diff --git a/AeroWizard/AeroWizard/AeroWizard.csproj b/AeroWizard/AeroWizard/AeroWizard.csproj new file mode 100644 index 0000000..bd694de --- /dev/null +++ b/AeroWizard/AeroWizard/AeroWizard.csproj @@ -0,0 +1,258 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {199C12C4-3EEF-4D08-BAC3-F2A62BCF969C} + Library + Properties + AeroWizard + AeroWizard + v4.0 + 512 + + + + + + + + + + + + + + + 3.5 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + + + + + true + full + false + + + bin\Debug\ + prompt + 4 + AllRules.ruleset + AnyCPU + false + + + pdbonly + true + TRACE; + bin\$(Configuration)\$(TargetFrameworkVersion) + prompt + 4 + bin\Release\$(TargetFrameworkVersion)\AeroWizard.XML + AllRules.ruleset + false + + + + + $(DefineConstants)$(TargetFrameworkVersion.Replace("v", "NET").Replace(".", "")), + $(DefineConstants.Remove($(DefineConstants.LastIndexOf(", NET"))))$(TargetFrameworkVersion.Replace("v", "NET").Replace(".", "")), + + $(DefineConstants.Remove($(DefineConstants.LastIndexOf(", ")))) + + + false + + + + + + + Resources\WizardHat.ico + + + + ..\..\packages\Newtonsoft.Json.12.0.1\lib\net40\Newtonsoft.Json.dll + True + + + + ..\..\packages\System.Data.SQLite.Core.1.0.110.0\lib\net40\System.Data.SQLite.dll + True + + + + + + + + + + Component + + + + + + + + + + + + + + + + + + + + + + + + Component + + + Component + + + + + Component + + + + Component + + + Component + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + Component + + + Component + + + WizardControl.cs + + + + Component + + + WizardPage.cs + + + StepWizardControl.cs + + + + WizardControl.cs + + + + + + + + + + + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + {ee86e933-d883-4b18-80eb-0fba55ec67c6} + ScoutBase.Core + + + + + + + + + + + + + + + this.Ver = Version.Parse(FileVersionInfo.GetVersionInfo(this.AssemblyPath).FileVersion).ToString(3); + + + + + + + + + + + Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}". + + + + \ No newline at end of file diff --git a/AeroWizard/AeroWizard/AeroWizard.sln b/AeroWizard/AeroWizard/AeroWizard.sln new file mode 100644 index 0000000..4615668 --- /dev/null +++ b/AeroWizard/AeroWizard/AeroWizard.sln @@ -0,0 +1,64 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AeroWizard", "AeroWizard.csproj", "{199C12C4-3EEF-4D08-BAC3-F2A62BCF969C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestWizard", "TestWizard\TestWizard.csproj", "{EAA3C12F-D499-476C-9500-524EAEE3373A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AeroWizardTemplates", "AeroWizardTemplates\AeroWizardTemplates.csproj", "{438228D8-C9EB-41DC-A7C9-9A0C3D8EEAAA}" + ProjectSection(ProjectDependencies) = postProject + {199C12C4-3EEF-4D08-BAC3-F2A62BCF969C} = {199C12C4-3EEF-4D08-BAC3-F2A62BCF969C} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AeroWizardItemTemplate", "AeroWizardItemTemplate\AeroWizardItemTemplate.csproj", "{BE2F5D91-AB68-432F-A94D-E68F05071324}" + ProjectSection(ProjectDependencies) = postProject + {199C12C4-3EEF-4D08-BAC3-F2A62BCF969C} = {199C12C4-3EEF-4D08-BAC3-F2A62BCF969C} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CustomWizardItemTemplate", "CustomWizardItemTemplate\CustomWizardItemTemplate.csproj", "{D8029003-7A3F-4840-B441-E15D6E2BAFC2}" + ProjectSection(ProjectDependencies) = postProject + {199C12C4-3EEF-4D08-BAC3-F2A62BCF969C} = {199C12C4-3EEF-4D08-BAC3-F2A62BCF969C} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wizard97ItemTemplate", "Wizard97ItemTemplate\Wizard97ItemTemplate.csproj", "{D127643F-69AA-4C4C-A624-7754289E798C}" + ProjectSection(ProjectDependencies) = postProject + {199C12C4-3EEF-4D08-BAC3-F2A62BCF969C} = {199C12C4-3EEF-4D08-BAC3-F2A62BCF969C} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {199C12C4-3EEF-4D08-BAC3-F2A62BCF969C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {199C12C4-3EEF-4D08-BAC3-F2A62BCF969C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {199C12C4-3EEF-4D08-BAC3-F2A62BCF969C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {199C12C4-3EEF-4D08-BAC3-F2A62BCF969C}.Release|Any CPU.Build.0 = Release|Any CPU + {EAA3C12F-D499-476C-9500-524EAEE3373A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EAA3C12F-D499-476C-9500-524EAEE3373A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EAA3C12F-D499-476C-9500-524EAEE3373A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EAA3C12F-D499-476C-9500-524EAEE3373A}.Release|Any CPU.Build.0 = Release|Any CPU + {438228D8-C9EB-41DC-A7C9-9A0C3D8EEAAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {438228D8-C9EB-41DC-A7C9-9A0C3D8EEAAA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {438228D8-C9EB-41DC-A7C9-9A0C3D8EEAAA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {438228D8-C9EB-41DC-A7C9-9A0C3D8EEAAA}.Release|Any CPU.Build.0 = Release|Any CPU + {BE2F5D91-AB68-432F-A94D-E68F05071324}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BE2F5D91-AB68-432F-A94D-E68F05071324}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BE2F5D91-AB68-432F-A94D-E68F05071324}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BE2F5D91-AB68-432F-A94D-E68F05071324}.Release|Any CPU.Build.0 = Release|Any CPU + {D8029003-7A3F-4840-B441-E15D6E2BAFC2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D8029003-7A3F-4840-B441-E15D6E2BAFC2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D8029003-7A3F-4840-B441-E15D6E2BAFC2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D8029003-7A3F-4840-B441-E15D6E2BAFC2}.Release|Any CPU.Build.0 = Release|Any CPU + {D127643F-69AA-4C4C-A624-7754289E798C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D127643F-69AA-4C4C-A624-7754289E798C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D127643F-69AA-4C4C-A624-7754289E798C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D127643F-69AA-4C4C-A624-7754289E798C}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/AeroWizard/AeroWizard/AeroWizardHelp/AeroWizardHelp.shfbproj b/AeroWizard/AeroWizard/AeroWizardHelp/AeroWizardHelp.shfbproj new file mode 100644 index 0000000..2822a14 --- /dev/null +++ b/AeroWizard/AeroWizard/AeroWizardHelp/AeroWizardHelp.shfbproj @@ -0,0 +1,142 @@ + + + + + Debug + AnyCPU + 2.0 + {d62d015e-12be-42d0-a62c-997aae54d654} + 2015.6.5.0 + + AeroWizardHelp + AeroWizardHelp + AeroWizardHelp + + .\Help\ + AeroWizard + en-US + + + + + + + + + + {@TokenFiles} + + + + + + {@HelpFormatOutputPaths} + + + + + + + + + + + + + + + + {@SyntaxFilters} + + + + + + + + + + + + The Microsoft.Win32.DesktopWindowManager namespace includes the DesktopWindowManager class and an extender control which allows the manipulation of the Desktop Window Manager and encapsulates API calls to that end. The DWM controls the glass effect on windows and painting on those affected surfaces. + The AeroWizard namespace provides the classes and components necessary for the creation of wizards. + + Extensions to the VisualStyles classes native to .NET that accomodate the AEROWIZARD tag. + + + + + + + + + OnlyWarningsAndErrors + MSHelpViewer + False + .NET Framework 4.0 + True + False + False + True + C#, Visual Basic, Managed C++ + Blank + Wizard .NET Library Help + True + VS2013 + False + Guid + Wizard .NET Library + &#169%3b 2015 Codeplex Community. All rights reserved. + http://aerowizard.codeplex.com/license + AboveNamespaces + VisualStudio12 + -1 + 100 + 100 + -1 + + + Msdn + 100 + VS + SAK + SAK + SAK + SAK + The Wizard project provides components allowing for the simple creation of Aero Wizards %28new wizard format in Vista and later%29 and other wizards which strictly follow Microsoft guidelines and use Visual Styles to pull specific settings from the system. + Attributes, InheritedMembers, InheritedFrameworkMembers, Protected, SealedProtected + + + + 2 + True + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AeroWizard/AeroWizard/AeroWizardItemTemplate/AeroWizardItemTemplate.Designer.cs b/AeroWizard/AeroWizard/AeroWizardItemTemplate/AeroWizardItemTemplate.Designer.cs new file mode 100644 index 0000000..f0bcba5 --- /dev/null +++ b/AeroWizard/AeroWizard/AeroWizardItemTemplate/AeroWizardItemTemplate.Designer.cs @@ -0,0 +1,69 @@ +namespace $rootnamespace$ +{ + partial class $safeitemname$ + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.wizardControl1 = new AeroWizard.WizardControl(); + this.wizardPage1 = new AeroWizard.WizardPage(); + ((System.ComponentModel.ISupportInitialize)(this.wizardControl1)).BeginInit(); + this.SuspendLayout(); + // + // wizardControl1 + // + this.wizardControl1.Location = new System.Drawing.Point(0, 0); + this.wizardControl1.Name = "wizardControl1"; + this.wizardControl1.Pages.Add(this.wizardPage1); + this.wizardControl1.Size = new System.Drawing.Size(574, 415); + this.wizardControl1.TabIndex = 0; + // + // wizardPage1 + // + this.wizardPage1.Name = "wizardPage1"; + this.wizardPage1.Size = new System.Drawing.Size(527, 260); + this.wizardPage1.TabIndex = 0; + this.wizardPage1.Text = "Page Title"; + // + // $safeitemname$ + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(574, 415); + this.Controls.Add(this.wizardControl1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.Name = "$safeitemname$"; + ((System.ComponentModel.ISupportInitialize)(this.wizardControl1)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private AeroWizard.WizardControl wizardControl1; + private AeroWizard.WizardPage wizardPage1; + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/AeroWizardItemTemplate/AeroWizardItemTemplate.cs b/AeroWizard/AeroWizard/AeroWizardItemTemplate/AeroWizardItemTemplate.cs new file mode 100644 index 0000000..93981e0 --- /dev/null +++ b/AeroWizard/AeroWizard/AeroWizardItemTemplate/AeroWizardItemTemplate.cs @@ -0,0 +1,13 @@ +using System; +using System.Windows.Forms; + +namespace $rootnamespace$ +{ + public partial class $safeitemname$ : Form + { + public $safeitemname$() + { + InitializeComponent(); + } + } +} diff --git a/AeroWizard/AeroWizard/AeroWizardItemTemplate/AeroWizardItemTemplate.csproj b/AeroWizard/AeroWizard/AeroWizardItemTemplate/AeroWizardItemTemplate.csproj new file mode 100644 index 0000000..1742c80 --- /dev/null +++ b/AeroWizard/AeroWizard/AeroWizardItemTemplate/AeroWizardItemTemplate.csproj @@ -0,0 +1,129 @@ + + + + 14.0 + 11.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + 12.0 + SAK + SAK + SAK + SAK + + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + Client + + + + Debug + AnyCPU + {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {BE2F5D91-AB68-432F-A94D-E68F05071324} + Library + Properties + AeroWizardItemTemplate + AeroWizardItemTemplate + v4.0 + 512 + false + false + false + false + false + false + false + false + false + false + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + AeroWizardItemTemplate.cs + + + + + + Windows Forms + Designer + + + + + AeroWizardItemTemplate.cs + + + + + + + + False + Microsoft .NET Framework 4.5 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + false + + + + + + + + + \ No newline at end of file diff --git a/AeroWizard/AeroWizard/AeroWizardItemTemplate/AeroWizardItemTemplate.resx b/AeroWizard/AeroWizard/AeroWizardItemTemplate/AeroWizardItemTemplate.resx new file mode 100644 index 0000000..7080a7d --- /dev/null +++ b/AeroWizard/AeroWizard/AeroWizardItemTemplate/AeroWizardItemTemplate.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/AeroWizard/AeroWizard/AeroWizardItemTemplate/AeroWizardItemTemplate.vstemplate b/AeroWizard/AeroWizard/AeroWizardItemTemplate/AeroWizardItemTemplate.vstemplate new file mode 100644 index 0000000..7ad3251 --- /dev/null +++ b/AeroWizard/AeroWizard/AeroWizardItemTemplate/AeroWizardItemTemplate.vstemplate @@ -0,0 +1,46 @@ + + + + AeroWizard.cs + AeroWizard Control + Windows Form hosting a WizardControl to create an Aero style wizard + CSharp + true + 1 + 2.0 + 100 + WizardControlTemplate.ico + __PreviewImage.PNG + + + + + System + + + System.Design + + + System.Drawing + + + System.Windows.Forms + + + AeroWizard + + + AeroWizardItemTemplate.cs + AeroWizardItemTemplate.Designer.cs + AeroWizardItemTemplate.resx + + + NuGet.VisualStudio.Interop, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + NuGet.VisualStudio.TemplateWizard + + + + + + + \ No newline at end of file diff --git a/AeroWizard/AeroWizard/AeroWizardItemTemplate/Properties/AssemblyInfo.cs b/AeroWizard/AeroWizard/AeroWizardItemTemplate/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..ad7b7d0 --- /dev/null +++ b/AeroWizard/AeroWizard/AeroWizardItemTemplate/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +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("AeroWizardItemTemplate")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("AeroWizardItemTemplate")] +[assembly: AssemblyCopyright("Copyright © 2013")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("b944454d-35f5-4278-a467-a2fdbb9ce699")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// 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("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/AeroWizard/AeroWizard/AeroWizardItemTemplate/WizardControlTemplate.ico b/AeroWizard/AeroWizard/AeroWizardItemTemplate/WizardControlTemplate.ico new file mode 100644 index 0000000..1137925 Binary files /dev/null and b/AeroWizard/AeroWizard/AeroWizardItemTemplate/WizardControlTemplate.ico differ diff --git a/AeroWizard/AeroWizard/AeroWizardItemTemplate/__PreviewImage.PNG b/AeroWizard/AeroWizard/AeroWizardItemTemplate/__PreviewImage.PNG new file mode 100644 index 0000000..6503e7b Binary files /dev/null and b/AeroWizard/AeroWizard/AeroWizardItemTemplate/__PreviewImage.PNG differ diff --git a/AeroWizard/AeroWizard/AeroWizardTemplates/AeroWizardTemplates.csproj b/AeroWizard/AeroWizard/AeroWizardTemplates/AeroWizardTemplates.csproj new file mode 100644 index 0000000..cedc6a4 --- /dev/null +++ b/AeroWizard/AeroWizard/AeroWizardTemplates/AeroWizardTemplates.csproj @@ -0,0 +1,151 @@ + + + + 14.0 + 11.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + 12.0 + SAK + SAK + SAK + SAK + + + Client + false + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + true + + + + Debug + AnyCPU + 2.0 + {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {438228D8-C9EB-41DC-A7C9-9A0C3D8EEAAA} + Library + Properties + AeroWizardTemplates + AeroWizardTemplates + v4.0 + false + false + false + false + false + false + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + False + + + + + + + Always + true + + + true + + + Designer + + + + + Always + true + + + Always + true + + + + + False + Microsoft .NET Framework 4.5 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + false + + + + + {BE2F5D91-AB68-432F-A94D-E68F05071324} + AeroWizardItemTemplate + ItemTemplatesA + false + TemplateProjectOutputGroup%3b + + + {D8029003-7A3F-4840-B441-E15D6E2BAFC2} + CustomWizardItemTemplate + ItemTemplatesB + false + TemplateProjectOutputGroup%3b + + + {d127643f-69aa-4c4c-a624-7754289e798c} + Wizard97ItemTemplate + ItemTemplatesC + false + TemplateProjectOutputGroup%3b + + + + + + + + + + + \ No newline at end of file diff --git a/AeroWizard/AeroWizard/AeroWizardTemplates/License.txt b/AeroWizard/AeroWizard/AeroWizardTemplates/License.txt new file mode 100644 index 0000000..e8e68ae --- /dev/null +++ b/AeroWizard/AeroWizard/AeroWizardTemplates/License.txt @@ -0,0 +1,9 @@ +The MIT License (MIT) + +Copyright (c) 2012-2014 David Hall + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/AeroWizard/AeroWizard/AeroWizardTemplates/Properties/AssemblyInfo.cs b/AeroWizard/AeroWizard/AeroWizardTemplates/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..015b608 --- /dev/null +++ b/AeroWizard/AeroWizard/AeroWizardTemplates/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +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("AeroWizardTemplates")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("AeroWizardTemplates")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// 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("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/AeroWizard/AeroWizard/AeroWizardTemplates/WizardControlTemplate.ico b/AeroWizard/AeroWizard/AeroWizardTemplates/WizardControlTemplate.ico new file mode 100644 index 0000000..06dd979 Binary files /dev/null and b/AeroWizard/AeroWizard/AeroWizardTemplates/WizardControlTemplate.ico differ diff --git a/AeroWizard/AeroWizard/AeroWizardTemplates/__PreviewImage.PNG b/AeroWizard/AeroWizard/AeroWizardTemplates/__PreviewImage.PNG new file mode 100644 index 0000000..6503e7b Binary files /dev/null and b/AeroWizard/AeroWizard/AeroWizardTemplates/__PreviewImage.PNG differ diff --git a/AeroWizard/AeroWizard/AeroWizardTemplates/source.extension.vsixmanifest b/AeroWizard/AeroWizard/AeroWizardTemplates/source.extension.vsixmanifest new file mode 100644 index 0000000..3fd900a --- /dev/null +++ b/AeroWizard/AeroWizard/AeroWizardTemplates/source.extension.vsixmanifest @@ -0,0 +1,28 @@ + + + + + Windows Forms Wizard Templates + Library for creating wizards for Windows Forms + https://aerowizard.codeplex.com/ + License.txt + http://aerowizard.codeplex.com/documentation + https://aerowizard.codeplex.com/SourceControl/list/changesets + WizardControlTemplate.ico + __PreviewImage.PNG + winforms;wizard;aero + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AeroWizard/AeroWizard/ControlExtension.cs b/AeroWizard/AeroWizard/ControlExtension.cs new file mode 100644 index 0000000..326ceb7 --- /dev/null +++ b/AeroWizard/AeroWizard/ControlExtension.cs @@ -0,0 +1,98 @@ + +namespace System.Windows.Forms +{ + static class ControlExtension + { + /// + /// Performs an action on a control after its handle has been created. If the control's handle has already been created, the action is executed immediately. + /// + /// This control. + /// The action to execute. + public static void CallWhenHandleValid(this Control ctrl, Action action) + { + if (ctrl.IsHandleCreated) + { + action(ctrl); + } + else + { + LayoutEventHandler handler = null; + handler = (sender, e) => + { + if (ctrl.IsHandleCreated) + { + ctrl.Layout -= handler; + action(ctrl); + } + }; + ctrl.Layout += handler; + } + } + + /// + /// Gets the control in the list of parents of type T. + /// + /// The based of the parent control to retrieve. + /// This control. + /// The parent control matching T or null if not found. + [Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] + public static T GetParent(this Control ctrl) where T : Control, new() + { + Control p = ctrl.Parent; + while (p != null & !(p is T)) + p = p.Parent; + return p as T; + } + + /// + /// Gets the top-most control in the list of parents of type T. + /// + /// The based of the parent control to retrieve. + /// This control. + /// The top-most parent control matching T or null if not found. + public static T GetTopMostParent(this Control ctrl) where T : Control, new() + { + var stack = new System.Collections.Generic.Stack(); + Control p = ctrl.Parent; + while (p != null) + { + stack.Push(p); + p = p.Parent; + } + while (stack.Count > 0) + if ((p = stack.Pop()) is T) + return p as T; + return null; + } + + /// + /// Gets the right to left property. + /// + /// This control. + /// Culture defined direction of text for this control. + public static RightToLeft GetRightToLeftProperty(this Control ctrl) + { + if (ctrl.RightToLeft == RightToLeft.Inherit) + return GetRightToLeftProperty(ctrl.Parent); + return ctrl.RightToLeft; + } + + /// + /// Determines whether this control is in design mode. + /// + /// This control. + /// true if in design mode; otherwise, false. + public static bool IsDesignMode(this Control ctrl) + { + Control p = ctrl; + while (p != null) + { + var site = p.Site; + if (site != null && site.DesignMode) + return true; + p = p.Parent; + } + return false; + } + } +} diff --git a/AeroWizard/AeroWizard/CustomWizardItemTemplate/CustomWizardItemTemplate.Designer.cs b/AeroWizard/AeroWizard/CustomWizardItemTemplate/CustomWizardItemTemplate.Designer.cs new file mode 100644 index 0000000..05c5ae2 --- /dev/null +++ b/AeroWizard/AeroWizard/CustomWizardItemTemplate/CustomWizardItemTemplate.Designer.cs @@ -0,0 +1,130 @@ +namespace $rootnamespace$ +{ + partial class $safeitemname$ + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.backBtn = new System.Windows.Forms.Button(); + this.cancelBtn = new System.Windows.Forms.Button(); + this.nextBtn = new System.Windows.Forms.Button(); + this.wizardPageContainer1 = new AeroWizard.WizardPageContainer(); + this.wizardPage1 = new AeroWizard.WizardPage(); + this.headingControl = new System.Windows.Forms.Label(); + ((System.ComponentModel.ISupportInitialize)(this.wizardPageContainer1)).BeginInit(); + this.wizardPageContainer1.SuspendLayout(); + this.SuspendLayout(); + // + // backBtn + // + this.backBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.backBtn.Location = new System.Drawing.Point(93, 204); + this.backBtn.Name = "backBtn"; + this.backBtn.Size = new System.Drawing.Size(75, 23); + this.backBtn.TabIndex = 1; + this.backBtn.Text = "&Back"; + // + // cancelBtn + // + this.cancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancelBtn.Location = new System.Drawing.Point(255, 204); + this.cancelBtn.Name = "cancelBtn"; + this.cancelBtn.Size = new System.Drawing.Size(75, 23); + this.cancelBtn.TabIndex = 1; + this.cancelBtn.Text = "&Cancel"; + // + // nextBtn + // + this.nextBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.nextBtn.Location = new System.Drawing.Point(174, 204); + this.nextBtn.Name = "nextBtn"; + this.nextBtn.Size = new System.Drawing.Size(75, 23); + this.nextBtn.TabIndex = 1; + this.nextBtn.Text = "&Next"; + // + // wizardPageContainer1 + // + this.wizardPageContainer1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.wizardPageContainer1.BackButton = this.backBtn; + this.wizardPageContainer1.CancelButton = this.cancelBtn; + this.wizardPageContainer1.Controls.Add(this.wizardPage1); + this.wizardPageContainer1.Location = new System.Drawing.Point(12, 25); + this.wizardPageContainer1.Name = "wizardPageContainer1"; + this.wizardPageContainer1.NextButton = this.nextBtn; + this.wizardPageContainer1.Pages.Add(this.wizardPage1); + this.wizardPageContainer1.Size = new System.Drawing.Size(318, 173); + this.wizardPageContainer1.TabIndex = 2; + this.wizardPageContainer1.Cancelling += new System.ComponentModel.CancelEventHandler(this.wizardPageContainer1_Cancelling); + this.wizardPageContainer1.Finished += new System.EventHandler(this.wizardPageContainer1_Finished); + this.wizardPageContainer1.SelectedPageChanged += new System.EventHandler(this.wizardPageContainer1_SelectedPageChanged); + // + // wizardPage1 + // + this.wizardPage1.Name = "wizardPage1"; + this.wizardPage1.Size = new System.Drawing.Size(318, 173); + this.wizardPage1.TabIndex = 0; + this.wizardPage1.Text = "Page 1"; + // + // headingControl + // + this.headingControl.AutoSize = true; + this.headingControl.Location = new System.Drawing.Point(9, 9); + this.headingControl.Name = "headingControl"; + this.headingControl.Size = new System.Drawing.Size(59, 13); + this.headingControl.TabIndex = 3; + this.headingControl.Text = ""; + // + // $safeitemname$ + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(342, 239); + this.Controls.Add(this.headingControl); + this.Controls.Add(this.wizardPageContainer1); + this.Controls.Add(this.cancelBtn); + this.Controls.Add(this.nextBtn); + this.Controls.Add(this.backBtn); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; + this.Name = "$safeitemname$"; + ((System.ComponentModel.ISupportInitialize)(this.wizardPageContainer1)).EndInit(); + this.wizardPageContainer1.ResumeLayout(false); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button backBtn; + private System.Windows.Forms.Button cancelBtn; + private System.Windows.Forms.Button nextBtn; + private AeroWizard.WizardPageContainer wizardPageContainer1; + private AeroWizard.WizardPage wizardPage1; + private System.Windows.Forms.Label headingControl; + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/CustomWizardItemTemplate/CustomWizardItemTemplate.cs b/AeroWizard/AeroWizard/CustomWizardItemTemplate/CustomWizardItemTemplate.cs new file mode 100644 index 0000000..fb95a5b --- /dev/null +++ b/AeroWizard/AeroWizard/CustomWizardItemTemplate/CustomWizardItemTemplate.cs @@ -0,0 +1,28 @@ +using System; +using System.Windows.Forms; + +namespace $rootnamespace$ +{ + public partial class $safeitemname$ : Form + { + public $safeitemname$() + { + InitializeComponent(); + } + + private void wizardPageContainer1_Cancelling(object sender, System.ComponentModel.CancelEventArgs e) + { + this.Close(); + } + + private void wizardPageContainer1_Finished(object sender, EventArgs e) + { + this.Close(); + } + + private void wizardPageContainer1_SelectedPageChanged(object sender, EventArgs e) + { + headingControl.Text = wizardPageContainer1.SelectedPage.Text; + } + } +} diff --git a/AeroWizard/AeroWizard/CustomWizardItemTemplate/CustomWizardItemTemplate.csproj b/AeroWizard/AeroWizard/CustomWizardItemTemplate/CustomWizardItemTemplate.csproj new file mode 100644 index 0000000..1edf669 --- /dev/null +++ b/AeroWizard/AeroWizard/CustomWizardItemTemplate/CustomWizardItemTemplate.csproj @@ -0,0 +1,129 @@ + + + + 14.0 + 11.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + 12.0 + SAK + SAK + SAK + SAK + + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + Client + + + + Debug + AnyCPU + {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {D8029003-7A3F-4840-B441-E15D6E2BAFC2} + Library + Properties + CustomWizardItemTemplate + CustomWizardItemTemplate + v4.0 + 512 + false + false + false + false + false + false + false + false + false + false + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + CustomWizardItemTemplate.cs + + + + + + Windows Forms + Designer + + + + + CustomWizardItemTemplate.cs + + + + + + + + False + Microsoft .NET Framework 4.5 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + false + + + + + + + + + \ No newline at end of file diff --git a/AeroWizard/AeroWizard/CustomWizardItemTemplate/CustomWizardItemTemplate.resx b/AeroWizard/AeroWizard/CustomWizardItemTemplate/CustomWizardItemTemplate.resx new file mode 100644 index 0000000..7080a7d --- /dev/null +++ b/AeroWizard/AeroWizard/CustomWizardItemTemplate/CustomWizardItemTemplate.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/AeroWizard/AeroWizard/CustomWizardItemTemplate/CustomWizardItemTemplate.vstemplate b/AeroWizard/AeroWizard/CustomWizardItemTemplate/CustomWizardItemTemplate.vstemplate new file mode 100644 index 0000000..5ef4d3e --- /dev/null +++ b/AeroWizard/AeroWizard/CustomWizardItemTemplate/CustomWizardItemTemplate.vstemplate @@ -0,0 +1,46 @@ + + + + CustomWizard.cs + Custom Wizard Control + Windows Form hosting controls that allow it to behave as a custom wizard. + CSharp + true + 1 + 2.0 + 100 + WizardControlTemplate.ico + __PreviewImage.PNG + + + + + System + + + System.Design + + + System.Drawing + + + System.Windows.Forms + + + AeroWizard + + + CustomWizardItemTemplate.cs + CustomWizardItemTemplate.Designer.cs + CustomWizardItemTemplate.resx + + + NuGet.VisualStudio.Interop, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + NuGet.VisualStudio.TemplateWizard + + + + + + + \ No newline at end of file diff --git a/AeroWizard/AeroWizard/CustomWizardItemTemplate/Properties/AssemblyInfo.cs b/AeroWizard/AeroWizard/CustomWizardItemTemplate/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..8a8e2ac --- /dev/null +++ b/AeroWizard/AeroWizard/CustomWizardItemTemplate/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +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("CustomWizardItemTemplate")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("CustomWizardItemTemplate")] +[assembly: AssemblyCopyright("Copyright © 2013")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("26d5a2d9-ef16-4d09-a4f5-1b401d26db14")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// 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("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/AeroWizard/AeroWizard/CustomWizardItemTemplate/WizardControlTemplate.ico b/AeroWizard/AeroWizard/CustomWizardItemTemplate/WizardControlTemplate.ico new file mode 100644 index 0000000..1137925 Binary files /dev/null and b/AeroWizard/AeroWizard/CustomWizardItemTemplate/WizardControlTemplate.ico differ diff --git a/AeroWizard/AeroWizard/CustomWizardItemTemplate/__PreviewImage.PNG b/AeroWizard/AeroWizard/CustomWizardItemTemplate/__PreviewImage.PNG new file mode 100644 index 0000000..ea998d3 Binary files /dev/null and b/AeroWizard/AeroWizard/CustomWizardItemTemplate/__PreviewImage.PNG differ diff --git a/AeroWizard/AeroWizard/EventedList.cs b/AeroWizard/AeroWizard/EventedList.cs new file mode 100644 index 0000000..521815e --- /dev/null +++ b/AeroWizard/AeroWizard/EventedList.cs @@ -0,0 +1,1410 @@ +using System.Collections.ObjectModel; +using System.ComponentModel; + +namespace System.Collections.Generic +{ + /// + /// A generic list that provides event for changes to the list. + /// + /// Type for the list. + [Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] + [Serializable] + public class EventedList : IList, IList + { + // Fields + private const int _defaultCapacity = 4; + + private static T[] _emptyArray = new T[0]; + + private T[] _items; + private int _size; + [NonSerialized] + private object _syncRoot; + private int _version; + + /// + /// Initializes a new instance of the class that is empty and has the default initial capacity. + /// + public EventedList() + { + _items = EventedList._emptyArray; + } + + /// + /// Initializes a new instance of the class that contains elements copied from the specified collection and has sufficient capacity to accommodate the number of elements copied. + /// + /// The collection whose elements are copied to the new list. + /// is null. + [Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] + public EventedList(IEnumerable collection) + { + if (collection == null) + { + throw new ArgumentNullException(nameof(collection)); + } + ICollection is2 = collection as ICollection; + if (is2 != null) + { + int count = is2.Count; + _items = new T[count]; + is2.CopyTo(_items, 0); + _size = count; + } + else + { + _size = 0; + _items = new T[4]; + using (IEnumerator enumerator = collection.GetEnumerator()) + { + while (enumerator.MoveNext()) + { + Add(enumerator.Current); + } + } + } + } + + /// + /// Initializes a new instance of the class that is empty and has the default initial capacity. + /// + /// The number of elements that the new list can initially store. + /// is less than 0. + public EventedList(int capacity) + { + if (capacity < 0) + { + throw new ArgumentOutOfRangeException(nameof(capacity)); + } + _items = new T[capacity]; + } + + /// + /// Occurs when an item has been added. + /// + public event EventHandler> ItemAdded; + + /// + /// Occurs when an item has changed. + /// + public event EventHandler> ItemChanged; + + /// + /// Occurs when an item has been deleted. + /// + public event EventHandler> ItemDeleted; + + /// + /// Occurs when the list has been reset. + /// + public event EventHandler> Reset; + + /// + /// Gets or sets the total number of elements the internal data structure can hold without resizing. + /// + /// + /// The number of elements that the can contain before resizing is required. + /// + /// Capacity is set to a value that is less than . + public int Capacity + { + get + { + return _items.Length; + } + set + { + if (value != _items.Length) + { + if (value < _size) + { + throw new ArgumentOutOfRangeException("value"); + } + if (value > 0) + { + T[] destinationArray = new T[value]; + if (_size > 0) + { + Array.Copy(_items, 0, destinationArray, 0, _size); + } + _items = destinationArray; + } + else + { + _items = EventedList._emptyArray; + } + } + } + } + + /// + /// Gets the number of elements contained in the . + /// + /// + /// The number of elements contained in the . + /// + public int Count => _size; + + /// + /// Gets a value indicating whether access to the is synchronized (thread safe). + /// + bool ICollection.IsSynchronized => false; + + /// + /// Gets an object that can be used to synchronize access to the . + /// + object ICollection.SyncRoot + { + get + { + if (_syncRoot == null) + { + System.Threading.Interlocked.CompareExchange(ref _syncRoot, new object(), null); + } + return _syncRoot; + } + } + + /// + /// Gets a value indicating whether the is read-only. + /// + bool ICollection.IsReadOnly => false; + + /// + /// Gets a value indicating whether the has a fixed size. + /// + bool IList.IsFixedSize => false; + + /// + /// Gets a value indicating whether the is read-only. + /// + bool IList.IsReadOnly => false; + + /// + /// Gets or sets the at the specified index. + /// + /// The index. + /// The element at the specified index. + object IList.this[int index] + { + get + { + return this[index]; + } + set + { + EventedList.VerifyValueType(value); + this[index] = (T)value; + } + } + + /// + /// Gets or sets the element at the specified index. + /// + /// The element at the specified index. + /// The zero-based index of the element to get or set. + /// is less than 0. + public T this[int index] + { + get + { + if (index >= _size) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + return _items[index]; + } + set + { + if (index >= _size) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + T oldValue = _items[index]; + _items[index] = value; + _version++; + OnItemChanged(index, oldValue, value); + } + } + + /// + /// Adds an item to the . + /// + /// The object to add to the . + /// The is read-only. + public void Add(T item) + { + if (_size == _items.Length) + { + EnsureCapacity(_size + 1); + } + _items[_size++] = item; + _version++; + OnItemAdded(_size, item); + } + + /// + /// Adds the range. + /// + /// The collection. + public void AddRange(IEnumerable collection) + { + InsertRange(_size, collection); + } + + /// + /// Returns a read-only wrapper for the current collection. + /// + /// A that acts as a read-only wrapper around the current . + public ReadOnlyCollection AsReadOnly() => new ReadOnlyCollection(this); + + /// + /// Searches the entire sorted for an element using the default comparer and returns the zero-based index of the element. + /// + /// The object to locate. The value can be null for reference types. + /// The zero-based index of in the sorted , if is found; otherwise, a negative number that is the bitwise complement of the index of the next element that is larger than item or, if there is no larger element, the bitwise complement of . + public int BinarySearch(T item) => BinarySearch(0, Count, item, null); + + /// + /// Searches the entire sorted for an element using the specified comparer and returns the zero-based index of the element. + /// + /// The object to locate. The value can be null for reference types. + /// The implementation to use when comparing elements, or null to use the default comparer . + /// The zero-based index of in the sorted , if is found; otherwise, a negative number that is the bitwise complement of the index of the next element that is larger than item or, if there is no larger element, the bitwise complement of . + public int BinarySearch(T item, IComparer comparer) => BinarySearch(0, Count, item, comparer); + + /// + /// Searches a range of elements in the sorted for an element using the specified comparer and returns the zero-based index of the element. + /// + /// The zero-based starting index of the range to search. + /// The length of the range to search. + /// The object to locate. The value can be null for reference types. + /// The implementation to use when comparing elements, or null to use the default comparer . + /// The zero-based index of in the sorted , if is found; otherwise, a negative number that is the bitwise complement of the index of the next element that is larger than item or, if there is no larger element, the bitwise complement of . + /// is less than 0. -or- is less than 0. + /// and do not denote a valid range in the . + public int BinarySearch(int index, int count, T item, IComparer comparer) + { + if ((index < 0) || (count < 0)) + { + throw new ArgumentOutOfRangeException((index < 0) ? "index" : "count"); + } + if ((_size - index) < count) + { + throw new ArgumentException($"{nameof(index)} and {nameof(count)} do not denote a valid range in the {nameof(EventedList)}."); + } + return Array.BinarySearch(_items, index, count, item, comparer); + } + + /// + /// Removes all items from the . + /// + /// The is read-only. + public void Clear() + { + Array.Clear(_items, 0, _size); + _size = 0; + _version++; + OnReset(); + } + + /// + /// Determines whether the contains a specific value. + /// + /// The object to locate in the . + /// + /// true if is found in the ; otherwise, false. + /// + public bool Contains(T item) + { + if (item == null) + { + for (int j = 0; j < _size; j++) + { + if (_items[j] == null) + { + return true; + } + } + return false; + } + EqualityComparer comparer = EqualityComparer.Default; + for (int i = 0; i < _size; i++) + { + if (comparer.Equals(_items[i], item)) + { + return true; + } + } + return false; + } + + /// + /// Converts the elements in the current to another type, and returns a list containing the converted elements. + /// + /// The type of the elements of the target array. + /// A delegate that converts each element from one type to another type. + /// A of the target type containing the converted elements from the current . + /// is null. + public EventedList ConvertAll(Converter converter) + { + if (converter == null) + { + throw new ArgumentNullException(nameof(converter)); + } + EventedList list = new EventedList(_size); + for (int i = 0; i < _size; i++) + { + list._items[i] = converter(_items[i]); + } + list._size = _size; + return list; + } + + /// + /// Copies the entire to a compatible one-dimensional array, starting at the beginning of the target array. + /// + /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. + public void CopyTo(T[] array) + { + CopyTo(array, 0); + } + + /// + /// Copies the elements of the to an , starting at a particular index. + /// + /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. + /// The zero-based index in at which copying begins. + /// is null. + /// is less than 0. + /// is multidimensional. -or- is equal to or greater than the length of .-or-The number of elements in the source is greater than the available space from to the end of the destination .-or-Type T cannot be cast automatically to the type of the destination . + public void CopyTo(T[] array, int arrayIndex) + { + Array.Copy(_items, 0, array, arrayIndex, _size); + } + + /// + /// Copies a range of elements from the to an , starting at a particular index. + /// + /// The zero-based index in the source at which copying begins. + /// The one-dimensional that is the destination of the elements copied from . The must have zero-based indexing. + /// The zero-based index in at which copying begins. + /// The number of elements to copy. + /// is null. + /// is less than 0. + /// is multidimensional. -or- is equal to or greater than the length of .-or-The number of elements in the source is greater than the available space from to the end of the destination .-or-Type T cannot be cast automatically to the type of the destination . + public void CopyTo(int index, T[] array, int arrayIndex, int count) + { + if ((_size - index) < count) + { + throw new ArgumentException($"{nameof(array)} is multidimensional. -or- {nameof(arrayIndex)} is equal to or greater than the length of {nameof(array)}. -or- The number of elements in the source list is greater than the available space from {nameof(arrayIndex)} to the end of the destination {nameof(array)}. -or- Type {nameof(T)} cannot be cast automatically to the type of the destination {nameof(array)}."); + } + Array.Copy(_items, index, array, arrayIndex, count); + } + + /// + /// Determines whether the contains elements that match the conditions defined by the specified predicate. + /// + /// The delegate that defines the conditions of the elements to search for. + /// true if the contains one or more elements that match the conditions defined by the specified predicate; otherwise, false. + public bool Exists(Predicate match) => (FindIndex(match) != -1); + + /// + /// Searches for an element that matches the conditions defined by the specified predicate, and returns the first occurrence within the entire . + /// + /// The delegate that defines the conditions of the elements to search for. + /// The first element that matches the conditions defined by the specified predicate, if found; otherwise, the default value for type . + public T Find(Predicate match) + { + if (match == null) + { + throw new ArgumentNullException(nameof(match)); + } + for (int i = 0; i < _size; i++) + { + if (match(_items[i])) + { + return _items[i]; + } + } + return default(T); + } + + /// + /// Retrieves all the elements that match the conditions defined by the specified predicate. + /// + /// The delegate that defines the conditions of the elements to search for. + /// A containing all the elements that match the conditions defined by the specified predicate, if found; otherwise, an empty . + public EventedList FindAll(Predicate match) + { + if (match == null) + { + throw new ArgumentNullException(nameof(match)); + } + EventedList list = new EventedList(); + for (int i = 0; i < _size; i++) + { + if (match(_items[i])) + { + list.Add(_items[i]); + } + } + return list; + } + + /// + /// Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the first occurrence within the entire . + /// + /// The delegate that defines the conditions of the elements to search for. + /// The zero-based index of the first occurrence of an element that matches the conditions defined by , if found; otherwise, 1. + /// is null. + public int FindIndex(Predicate match) => FindIndex(0, _size, match); + + /// + /// Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the first occurrence within the range of elements in the that extends from the specified index to the last element. + /// + /// The zero-based starting index of the search. + /// The delegate that defines the conditions of the elements to search for. + /// The zero-based index of the first occurrence of an element that matches the conditions defined by , if found; otherwise, 1. + /// + /// is outside the range of valid indexes for the . + /// + /// is null. + public int FindIndex(int startIndex, Predicate match) => FindIndex(startIndex, _size - startIndex, match); + + /// + /// Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the first occurrence within the range of elements in the that starts at the specified index and contains the specified number of elements. + /// + /// The zero-based starting index of the search. + /// The number of elements in the section to search. + /// The delegate that defines the conditions of the elements to search for. + /// The zero-based index of the first occurrence of an element that matches the conditions defined by , if found; otherwise, 1. + /// + /// is outside the range of valid indexes for the . + /// -or- + /// is less than 0. + /// -or- + /// and do not specify a valid section in the . + /// + /// is null. + public int FindIndex(int startIndex, int count, Predicate match) + { + if (startIndex > _size) + { + throw new ArgumentOutOfRangeException(nameof(startIndex)); + } + if ((count < 0) || (startIndex > (_size - count))) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + if (match == null) + { + throw new ArgumentNullException(nameof(match)); + } + int num = startIndex + count; + for (int i = startIndex; i < num; i++) + { + if (match(_items[i])) + { + return i; + } + } + return -1; + } + + /// + /// Searches for an element that matches the conditions defined by the specified predicate, and returns the last occurrence within the entire . + /// + /// The delegate that defines the conditions of the elements to search for. + /// The last element that matches the conditions defined by the specified predicate, if found; otherwise, the default value for type . + public T FindLast(Predicate match) + { + if (match == null) + { + throw new ArgumentNullException(nameof(match)); + } + for (int i = _size - 1; i >= 0; i--) + { + if (match(_items[i])) + { + return _items[i]; + } + } + return default(T); + } + + /// + /// Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the last occurrence within the entire . + /// + /// The delegate that defines the conditions of the elements to search for. + /// The zero-based index of the last occurrence of an element that matches the conditions defined by , if found; otherwise, 1. + /// is null. + public int FindLastIndex(Predicate match) => FindLastIndex(_size - 1, _size, match); + + /// + /// Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the last occurrence within the range of elements in the that extends from the specified index to the last element. + /// + /// The zero-based starting index of the search. + /// The delegate that defines the conditions of the elements to search for. + /// The zero-based index of the last occurrence of an element that matches the conditions defined by , if found; otherwise, 1. + /// + /// is outside the range of valid indexes for the . + /// + /// is null. + public int FindLastIndex(int startIndex, Predicate match) => FindLastIndex(startIndex, startIndex + 1, match); + + /// + /// Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the last occurrence within the range of elements in the that starts at the specified index and contains the specified number of elements. + /// + /// The zero-based starting index of the search. + /// The number of elements in the section to search. + /// The delegate that defines the conditions of the elements to search for. + /// The zero-based index of the last occurrence of an element that matches the conditions defined by , if found; otherwise, 1. + /// + /// is outside the range of valid indexes for the . + /// -or- + /// is less than 0. + /// -or- + /// and do not specify a valid section in the . + /// + /// is null. + public int FindLastIndex(int startIndex, int count, Predicate match) + { + if (match == null) + { + throw new ArgumentNullException(nameof(match)); + } + if (_size == 0) + { + if (startIndex != -1) + { + throw new ArgumentOutOfRangeException(nameof(startIndex)); + } + } + else if (startIndex >= _size) + { + throw new ArgumentOutOfRangeException(nameof(startIndex)); + } + if ((count < 0) || (((startIndex - count) + 1) < 0)) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + int num = startIndex - count; + for (int i = startIndex; i > num; i--) + { + if (match(_items[i])) + { + return i; + } + } + return -1; + } + + /// + /// Performs the specified action on each element of the . + /// + /// The delegate to perform on each element of the . + public void ForEach(Action action) + { + if (action == null) + { + throw new ArgumentNullException(nameof(action)); + } + for (int i = 0; i < _size; i++) + { + action(_items[i]); + } + } + + /// + /// Returns an enumerator that iterates through the . + /// + /// A for the . + public EventedList.Enumerator GetEnumerator() => new EventedList.Enumerator((EventedList)this); + + /// + /// Creates a shallow copy of a range of elements in the source . + /// + /// The zero-based index at which the range starts. + /// The number of elements in the range. + /// A shallow copy of a range of elements in the source . + public EventedList GetRange(int index, int count) + { + if ((index < 0) || (count < 0)) + { + throw new ArgumentOutOfRangeException((index < 0) ? nameof(index) : nameof(count)); + } + if ((_size - index) < count) + { + throw new ArgumentException($"{nameof(index)} and {nameof(count)} do not denote a valid range of elements in the {nameof(EventedList)}."); + } + EventedList list = new EventedList(count); + Array.Copy(_items, index, list._items, 0, count); + list._size = count; + return list; + } + + /// + /// Copies the elements of the ICollection to an Array, starting at a particular Array index. + /// + /// The array. + /// Index of the array. + /// Destination array cannot be null or multidimensional. + void ICollection.CopyTo(Array array, int arrayIndex) + { + if (array?.Rank != 1) + throw new ArgumentException("Destination array cannot be null or multidimensional.", nameof(array)); + try + { + Array.Copy(_items, 0, array, arrayIndex, _size); + } + catch (ArrayTypeMismatchException e) + { + throw new ArgumentException("Type mismatch", e); + } + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + IEnumerator IEnumerable.GetEnumerator() => new EventedList.Enumerator((EventedList)this); + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through the collection. + /// + IEnumerator IEnumerable.GetEnumerator() => new EventedList.Enumerator((EventedList)this); + + /// + /// Adds an item to the IList. + /// + /// The item. + /// + int IList.Add(object item) + { + EventedList.VerifyValueType(item); + Add((T)item); + return (Count - 1); + } + + /// + /// Determines whether the IList contains a specific value. + /// + /// The item. + /// + /// true if contains the specified item; otherwise, false. + /// + bool IList.Contains(object item) => (EventedList.IsCompatibleObject(item) && Contains((T)item)); + + /// + /// Determines the index of a specific item in the IList. + /// + /// The item. + /// + int IList.IndexOf(object item) + { + if (EventedList.IsCompatibleObject(item)) + { + return IndexOf((T)item); + } + return -1; + } + + /// + /// Inserts an item to the IList at the specified index. + /// + /// The index. + /// The item. + void IList.Insert(int index, object item) + { + EventedList.VerifyValueType(item); + Insert(index, (T)item); + } + + /// + /// Removes the first occurrence of a specific object from the IList. + /// + /// The item. + void IList.Remove(object item) + { + if (EventedList.IsCompatibleObject(item)) + { + Remove((T)item); + } + } + + /// + /// Searches for the specified object and returns the zero-based index of the first occurrence within the entire . + /// + /// The object to locate in the . The value can be null for reference types. + /// + /// The index of if found in the list; otherwise, -1. + /// + public int IndexOf(T item) => Array.IndexOf(_items, item, 0, _size); + + /// + /// Searches for the specified object and returns the zero-based index of the first occurrence within the range of elements in the that starts at the specified index. + /// + /// The object to locate in the . The value can be null for reference types. + /// The zero-based starting index of the search. 0 (zero) is valid in an empty list. + /// The zero-based of the first occurrence of within the range of elements in the that starts at , if found; otherwise, 1. + /// + /// is outside the range of valid indexes for the . + /// + public int IndexOf(T item, int index) + { + if (index > _size) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + return Array.IndexOf(_items, item, index, _size - index); + } + + /// + /// Searches for the specified object and returns the zero-based index of the first occurrence within the range of elements in the that starts at the specified index and contains the specified number of elements. + /// + /// The object to locate in the . The value can be null for reference types. + /// The zero-based starting index of the search. 0 (zero) is valid in an empty list. + /// The number of elements in the section to search. + /// The zero-based of the first occurrence of within the range of elements in the that starts at and contains number of elements, if found; otherwise, 1. + /// + /// is outside the range of valid indexes for the . + /// -or- + /// is less than 0. + /// -or- + /// and do not specify a valid section in the . + /// + public int IndexOf(T item, int index, int count) + { + if (index > _size) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + if ((count < 0) || (index > (_size - count))) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + return Array.IndexOf(_items, item, index, count); + } + + /// + /// Inserts an item to the at the specified index. + /// + /// The zero-based index at which should be inserted. + /// The object to insert into the . + /// is not a valid index in the . + /// The is read-only. + public void Insert(int index, T item) + { + if (index > _size) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + if (_size == _items.Length) + { + EnsureCapacity(_size + 1); + } + if (index < _size) + { + Array.Copy(_items, index, _items, index + 1, _size - index); + } + _items[index] = item; + _size++; + _version++; + OnItemAdded(index, item); + } + + /// + /// Inserts the elements of a collection into the at the specified index. + /// + /// The zero-based index at which the new elements should be inserted. + /// The collection whose elements should be inserted into the . The collection itself cannot be null, but it can contain elements that are null, if type is a reference type. + public void InsertRange(int index, IEnumerable collection) + { + if (collection == null) + { + throw new ArgumentNullException(nameof(collection)); + } + if (index > _size) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + ICollection is2 = collection as ICollection; + if (is2 != null) + { + int count = is2.Count; + if (count > 0) + { + EnsureCapacity(_size + count); + if (index < _size) + { + Array.Copy(_items, index, _items, index + count, _size - index); + } + if (this == is2) + { + Array.Copy(_items, 0, _items, index, index); + Array.Copy(_items, (int)(index + count), _items, (int)(index * 2), (int)(_size - index)); + } + else + { + T[] array = new T[count]; + is2.CopyTo(array, 0); + array.CopyTo(_items, index); + } + _size += count; + for (int i = index; i < index + count; i++) + OnItemAdded(i, _items[i]); + } + } + else + { + using (IEnumerator enumerator = collection.GetEnumerator()) + { + while (enumerator.MoveNext()) + { + Insert(index++, enumerator.Current); + } + } + } + _version++; + } + + /// + /// Searches for the specified object and returns the zero-based index of the last occurrence within the entire . + /// + /// The object to locate in the . The value can be null for reference types. + /// + /// The index of if found in the list; otherwise, -1. + /// + public int LastIndexOf(T item) => LastIndexOf(item, _size - 1, _size); + + /// + /// Searches for the specified object and returns the zero-based index of the last occurrence within the range of elements in the that starts at the specified index. + /// + /// The object to locate in the . The value can be null for reference types. + /// The zero-based starting index of the search. 0 (zero) is valid in an empty list. + /// The zero-based of the last occurrence of within the range of elements in the that starts at , if found; otherwise, 1. + /// + /// is outside the range of valid indexes for the . + /// + public int LastIndexOf(T item, int index) + { + if (index >= _size) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + return LastIndexOf(item, index, index + 1); + } + + /// + /// Searches for the specified object and returns the zero-based index of the last occurrence within the range of elements in the that starts at the specified index and contains the specified number of elements. + /// + /// The object to locate in the . The value can be null for reference types. + /// The zero-based starting index of the search. 0 (zero) is valid in an empty list. + /// The number of elements in the section to search. + /// The zero-based of the last occurrence of within the range of elements in the that starts at and contains number of elements, if found; otherwise, 1. + /// + /// is outside the range of valid indexes for the . + /// -or- + /// is less than 0. + /// -or- + /// and do not specify a valid section in the . + /// + public int LastIndexOf(T item, int index, int count) + { + if (_size == 0) + { + return -1; + } + if ((index < 0) || (count < 0)) + { + throw new ArgumentOutOfRangeException((index < 0) ? "index" : "count"); + } + if ((index >= _size) || (count > (index + 1))) + { + throw new ArgumentOutOfRangeException((index >= _size) ? "index" : "count"); + } + return Array.LastIndexOf(_items, item, index, count); + } + + /// + /// Removes the first occurrence of a specific object from the . + /// + /// The object to remove from the . + /// + /// true if was successfully removed from the ; otherwise, false. This method also returns false if is not found in the original . + /// + /// The is read-only. + public bool Remove(T item) + { + int index = IndexOf(item); + if (index >= 0) + { + RemoveAt(index); + return true; + } + return false; + } + + /// + /// Removes all the elements that match the conditions defined by the specified predicate. + /// + /// The delegate that defines the conditions of the elements to remove. + /// The number of elements removed from the . + public int RemoveAll(Predicate match) + { + if (match == null) + { + throw new ArgumentNullException(nameof(match)); + } + int index = 0; + while ((index < _size) && !match(_items[index])) + { + index++; + } + if (index >= _size) + { + return 0; + } + int num2 = index + 1; + while (num2 < _size) + { + while ((num2 < _size) && match(_items[num2])) + { + num2++; + } + if (num2 < _size) + { + T oldVal = _items[index + 1]; + _items[index++] = _items[num2++]; + OnItemDeleted(index, oldVal); + } + } + Array.Clear(_items, index, _size - index); + int num3 = _size - index; + _size = index; + _version++; + return num3; + } + + /// + /// Removes the item at the specified index. + /// + /// The zero-based index of the item to remove. + /// is not a valid index in the . + /// The is read-only. + public void RemoveAt(int index) + { + if (index >= _size) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } + _size--; + T oldVal = _items[index]; + if (index < _size) + { + Array.Copy(_items, index + 1, _items, index, _size - index); + } + _items[_size] = default(T); + _version++; + OnItemDeleted(index, oldVal); + } + + /// + /// Removes a range of elements from the . + /// + /// The zero-based starting index of the range of elements to remove. + /// The number of elements to remove. + public void RemoveRange(int index, int count) + { + if ((index < 0) || (count < 0)) + { + throw new ArgumentOutOfRangeException((index < 0) ? "index" : "count"); + } + if ((_size - index) < count) + { + throw new ArgumentException($"{nameof(index)} and {nameof(count)} do not denote a valid range of elements in the {nameof(EventedList)}."); + } + if (count > 0) + { + _size -= count; + T[] array = new T[count]; + Array.Copy(_items, index, array, 0, count); + if (index < _size) + { + Array.Copy(_items, index + count, _items, index, _size - index); + } + Array.Clear(_items, _size, count); + _version++; + for (int i = index; i < index + count; i++) + OnItemDeleted(i, array[i - index]); + } + } + + /// + /// Reverses the order of the elements in the entire . + /// + public void Reverse() + { + Reverse(0, Count); + } + + /// + /// Reverses the order of the elements in the specified range. + /// + /// The zero-based starting index of the range of elements to reverse. + /// The number of elements to reverse. + public void Reverse(int index, int count) + { + if ((index < 0) || (count < 0)) + { + throw new ArgumentOutOfRangeException((index < 0) ? "index" : "count"); + } + if ((_size - index) < count) + { + throw new ArgumentException($"{nameof(index)} and {nameof(count)} do not denote a valid range of elements in the {nameof(EventedList)}."); + } + Array.Reverse(_items, index, count); + _version++; + } + + /// + /// Sorts the elements in the entire using the default comparer. + /// + public void Sort() + { + Sort(0, Count, null); + } + + /// + /// Sorts the elements in the entire using the specified comparer. + /// + /// The implementation to use when comparing elements, or null to use the default comparer . + public void Sort(IComparer comparer) + { + Sort(0, Count, comparer); + } + + /// + /// Sorts the elements in a range of elements in using the specified comparer. + /// + /// The zero-based starting index of the range of elements to sort. + /// The number of elements to sort. + /// The implementation to use when comparing elements, or null to use the default comparer . + public void Sort(int index, int count, IComparer comparer) + { + if ((index < 0) || (count < 0)) + { + throw new ArgumentOutOfRangeException((index < 0) ? "index" : "count"); + } + if ((_size - index) < count) + { + throw new ArgumentException($"{nameof(index)} and {nameof(count)} do not denote a valid range of elements in the {nameof(EventedList)}."); + } + Array.Sort(_items, index, count, comparer); + _version++; + } + + /// + /// Copies the elements of the to a new array. + /// + /// An array containing copies of the elements of the . + public T[] ToArray() + { + T[] destinationArray = new T[_size]; + Array.Copy(_items, 0, destinationArray, 0, _size); + return destinationArray; + } + + /// + /// Sets the capacity to the actual number of elements in the , if that number is less than a threshold value. + /// + public void TrimExcess() + { + int num = (int)(_items.Length * 0.9); + if (_size < num) + { + Capacity = _size; + } + } + + /// + /// Determines whether every element in the matches the conditions defined by the specified predicate. + /// + /// The delegate that defines the conditions to check against the elements. + /// true if every element in the matches the conditions defined by the specified predicate; otherwise, false. If the list has no elements, the return value is true. + public bool TrueForAll(Predicate match) + { + if (match == null) + { + throw new ArgumentNullException(nameof(match)); + } + for (int i = 0; i < _size; i++) + { + if (!match(_items[i])) + { + return false; + } + } + return true; + } + + /// + /// Raises the event. + /// + /// The index of the added item. + /// The value of the added item. + protected virtual void OnItemAdded(int index, T value) + { + EventHandler> h = ItemAdded; + if (h != null) + h(this, new EventedList.ListChangedEventArgs(ListChangedType.ItemAdded, value, index)); + } + + /// + /// Raises the event. + /// + /// The index of the changed item. + /// The previous value of the changed item. + /// The new value of the changed item. + protected virtual void OnItemChanged(int index, T oldValue, T newValue) + { + EventHandler> h = ItemChanged; + if (h != null) + h(this, new EventedList.ListChangedEventArgs(ListChangedType.ItemChanged, newValue, index, oldValue)); + } + + /// + /// Raises the event. + /// + /// The index of the deleted item. + /// The value of the deleted item. + protected virtual void OnItemDeleted(int index, T value) + { + EventHandler> h = ItemDeleted; + if (h != null) + h(this, new EventedList.ListChangedEventArgs(ListChangedType.ItemDeleted, value, index)); + } + + /// + /// Raises the event. + /// + protected virtual void OnReset() + { + EventHandler> h = Reset; + if (h != null) + h(this, new EventedList.ListChangedEventArgs(ListChangedType.Reset)); + } + + /// + /// Determines whether the specified object is compatible with this list's item type. + /// + /// The value. + /// + /// true if the specified object is compatible with this list's item type; otherwise, false. + /// + private static bool IsCompatibleObject(object value) + { + if (!(value is T) && ((value != null) || typeof(T).IsValueType)) + { + return false; + } + return true; + } + + /// + /// Verifies the type of the value. + /// + /// The value. + private static void VerifyValueType(object value) + { + if (!EventedList.IsCompatibleObject(value)) + { + throw new ArgumentException("Incompatible type.", nameof(value)); + } + } + + /// + /// Ensures the capacity. + /// + /// The min. + private void EnsureCapacity(int min) + { + if (_items.Length < min) + { + int num = (_items.Length == 0) ? 4 : (_items.Length * 2); + if (num < min) + { + num = min; + } + Capacity = num; + } + } + + /// + /// Enumerates over the . + /// + [Serializable, + System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)] + public struct Enumerator : IEnumerator, IDisposable, IEnumerator + { + private EventedList list; + private int index; + private int version; + private T current; + + /// + /// Initializes a new instance of the struct. + /// + /// The list. + internal Enumerator(EventedList list) + { + this.list = list; + index = 0; + version = list._version; + current = default(T); + } + + /// + /// Gets the element at the current position of the enumerator. + /// + /// The current element. + public T Current => current; + + /// + /// Gets the element at the current position of the enumerator. + /// + /// The current element. + object IEnumerator.Current + { + get + { + if ((index == 0) || (index == (list._size + 1))) + { + throw new InvalidOperationException(); + } + return Current; + } + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + } + + /// + /// Sets the enumerator to its initial position, which is before the first element in the collection. + /// + /// The collection was modified after the enumerator was created. + void IEnumerator.Reset() + { + if (version != list._version) + { + throw new InvalidOperationException(); + } + index = 0; + current = default(T); + } + + /// + /// Advances the enumerator to the next element of the collection. + /// + /// + /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection. + /// + /// The collection was modified after the enumerator was created. + public bool MoveNext() + { + if (version != list._version) + { + throw new InvalidOperationException(); + } + if (index < list._size) + { + current = list._items[index]; + index++; + return true; + } + index = list._size + 1; + current = default(T); + return false; + } + } + + /// + /// An structure passed to events generated by an . + /// + /// +#pragma warning disable 693 + [Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")] + public class ListChangedEventArgs : EventArgs + { + /// + /// Initializes a new instance of the class. + /// + /// The type of change. + public ListChangedEventArgs(ListChangedType type) + { + ItemIndex = -1; + ListChangedType = type; + } + + /// + /// Initializes a new instance of the class. + /// + /// The type of change. + /// The item that has changed. + /// Index of the changed item. + public ListChangedEventArgs(ListChangedType type, T item, int itemIndex) + { + Item = item; + ItemIndex = itemIndex; + ListChangedType = type; + } + + /// + /// Initializes a new instance of the class. + /// + /// The type of change. + /// The item that has changed. + /// Index of the changed item. + /// The old item when an item has changed. + public ListChangedEventArgs(ListChangedType type, T item, int itemIndex, T oldItem) + : this(type, item, itemIndex) + { + OldItem = oldItem; + } + + /// + /// Gets the item that has changed. + /// + /// The item. + public T Item { get; } + + /// + /// Gets the index of the item. + /// + /// The index of the item. + public int ItemIndex { get; } + + /// + /// Gets the type of change for the list. + /// + /// The type of change for the list. + public ListChangedType ListChangedType { get; } + + /// + /// Gets the item's previous value. + /// + /// The old item. + public T OldItem { get; } + } +#pragma warning restore 693 + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/ExtensionAttributeFor.NET_2.0.cs b/AeroWizard/AeroWizard/ExtensionAttributeFor.NET_2.0.cs new file mode 100644 index 0000000..4757f1c --- /dev/null +++ b/AeroWizard/AeroWizard/ExtensionAttributeFor.NET_2.0.cs @@ -0,0 +1,9 @@ +namespace System.Runtime.CompilerServices +{ + /// + /// Attribute allowing extenders to be used with .NET Framework 2.0. + /// + internal sealed class ExtensionAttribute : Attribute + { + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/FixVSIX.ps1 b/AeroWizard/AeroWizard/FixVSIX.ps1 new file mode 100644 index 0000000..8996dd0 --- /dev/null +++ b/AeroWizard/AeroWizard/FixVSIX.ps1 @@ -0,0 +1,6 @@ +# Load vsix tools +. "C:\Users\dahall\Documents\Visual Studio 2010\Projects\VsixTools2.ps1" +# Set the version number of 'MyPackage' and fix the zip issue for uploading to the gallery. +$vsixPath = "C:\Users\dahall\Documents\Visual Studio 2010\Projects\AeroWizard\AeroWizardTemplates\bin\Release\AeroWizardTemplates.vsix" +Vsix-SetVersion -VsixPath $vsixPath -Version "2.0.3" +Vsix-FixInvalidMultipleFiles -VsixPath $vsixPath \ No newline at end of file diff --git a/AeroWizard/AeroWizard/GenericDesigner.cs b/AeroWizard/AeroWizard/GenericDesigner.cs new file mode 100644 index 0000000..4799caa --- /dev/null +++ b/AeroWizard/AeroWizard/GenericDesigner.cs @@ -0,0 +1,779 @@ +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Windows.Forms.Design; +using System.Windows.Forms.Design.Behavior; + +namespace System.ComponentModel.Design +{ + internal static class ComponentDesignerExtension + { + public const System.Reflection.BindingFlags AllInstBind = System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance; + + public static object EditValue(this ComponentDesigner designer, object objectToChange, string propName) + { + PropertyDescriptor prop = TypeDescriptor.GetProperties(objectToChange)[propName]; + EditorServiceContext context = new EditorServiceContext(designer, prop); + var editor = prop.GetEditor(typeof(System.Drawing.Design.UITypeEditor)) as System.Drawing.Design.UITypeEditor; + object curVal = prop.GetValue(objectToChange); + object newVal = editor.EditValue(context, context, curVal); + if (newVal != curVal) + try { prop.SetValue(objectToChange, newVal); } + catch (CheckoutException) { } + return newVal; + } + + public static List GetAllAttributedActionItems(this DesignerActionList actionList) + { + var fullAIList = new List(); + foreach (var mbr in actionList.GetType().GetMethods(AllInstBind)) + { + foreach (IActionGetItem attr in mbr.GetCustomAttributes(typeof(DesignerActionMethodAttribute), false)) + { + if (mbr.ReturnType == typeof(void) && mbr.GetParameters().Length == 0) + fullAIList.Add(attr.GetItem(actionList, mbr)); + else + throw new FormatException("DesignerActionMethodAttribute must be applied to a method returning void and having no parameters."); + } + } + foreach (var mbr in actionList.GetType().GetProperties(AllInstBind)) + { + foreach (IActionGetItem attr in mbr.GetCustomAttributes(typeof(DesignerActionPropertyAttribute), false)) + fullAIList.Add(attr.GetItem(actionList, mbr)); + } + fullAIList.Sort(CompareItems); + return fullAIList; + } + + public static DesignerVerbCollection GetAttributedVerbs(this ComponentDesigner designer) + { + var verbs = new DesignerVerbCollection(); + foreach (var m in designer.GetType().GetMethods(AllInstBind)) + { + foreach (DesignerVerbAttribute attr in m.GetCustomAttributes(typeof(DesignerVerbAttribute), true)) + { + verbs.Add(attr.GetDesignerVerb(designer, m)); + } + } + return verbs; + } + + public static DesignerActionItemCollection GetFilteredActionItems(this DesignerActionList actionList, List fullAIList) + { + DesignerActionItemCollection col = new DesignerActionItemCollection(); + fullAIList.ForEach(ai => { if (CheckCondition(actionList, ai)) { col.Add(ai); } }); + + // Add header items for displayed items + int i = 0; string cat = null; + while (i < col.Count) + { + string curCat = col[i].Category; + if (string.Compare(curCat, cat, true, Globalization.CultureInfo.CurrentCulture) != 0) + { + col.Insert(i++, new DesignerActionHeaderItem(curCat)); + cat = curCat; + } + i++; + } + + return col; + } + + public static IDictionary> GetRedirectedProperties(this ComponentDesigner d) + { + var ret = new Dictionary>(); + foreach (var prop in d.GetType().GetProperties(AllInstBind)) + { + foreach (RedirectedDesignerPropertyAttribute attr in prop.GetCustomAttributes(typeof(RedirectedDesignerPropertyAttribute), false)) + { + List attributes; + if (attr.ApplyOtherAttributes) + { + attributes = new List(Array.ConvertAll(prop.GetCustomAttributes(false), o => o as System.Attribute)); + attributes.RemoveAll(a => a is RedirectedDesignerPropertyAttribute); + } + else + attributes = new List(); + ret.Add(prop.Name, attributes); + } + } + return ret; + } + + public static void RedirectRegisteredProperties(this ComponentDesigner d, System.Collections.IDictionary properties, IDictionary> redirectedProps) + { + foreach (var propName in redirectedProps.Keys) + { + PropertyDescriptor oldPropertyDescriptor = (PropertyDescriptor)properties[propName]; + if (oldPropertyDescriptor != null) + { + List attributes = redirectedProps[propName]; + properties[propName] = TypeDescriptor.CreateProperty(d.GetType(), oldPropertyDescriptor, attributes.ToArray()); + } + } + } + + [Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "d")] + public static void RemoveProperties(this ComponentDesigner d, System.Collections.IDictionary properties, IEnumerable propertiesToRemove) + { + foreach (string p in propertiesToRemove) + if (properties.Contains(p)) + properties.Remove(p); + } + + public static void SetComponentProperty(this ComponentDesigner d, string propName, T value) + { + PropertyDescriptor propDesc = TypeDescriptor.GetProperties(d.Component)[propName]; + if (propDesc != null && propDesc.PropertyType == typeof(T) && !propDesc.IsReadOnly && propDesc.IsBrowsable) + propDesc.SetValue(d.Component, value); + } + + public static System.Windows.Forms.DialogResult ShowDialog(this ComponentDesigner designer, System.Windows.Forms.Form dialog) + { + EditorServiceContext context = new EditorServiceContext(designer); + return context.ShowDialog(dialog); + } + + private static bool CheckCondition(DesignerActionList actionList, DesignerActionItem ai) + { + if (ai.Properties["Condition"] != null) + { + var p = actionList.GetType().GetProperty((string)ai.Properties["Condition"], AllInstBind, null, typeof(bool), Type.EmptyTypes, null); + if (p != null) + return (bool)p.GetValue(actionList, null); + } + return true; + } + + private static int CompareItems(DesignerActionItem a, DesignerActionItem b) + { + int c = string.Compare(a.Category ?? string.Empty, b.Category ?? string.Empty, true, Globalization.CultureInfo.CurrentCulture); + if (c != 0) + return c; + c = (int)a.Properties["Order"] - (int)b.Properties["Order"]; + if (c != 0) + return c; + return string.Compare(a.DisplayName, b.DisplayName, true, Globalization.CultureInfo.CurrentCulture); + } + } + + [System.AttributeUsage(System.AttributeTargets.Property, Inherited = true, AllowMultiple = false)] + internal sealed class RedirectedDesignerPropertyAttribute : System.Attribute + { + public RedirectedDesignerPropertyAttribute() { ApplyOtherAttributes = true; } + + public bool ApplyOtherAttributes { get; set; } + } +} + +namespace System.Windows.Forms.Design +{ + internal interface IActionGetItem + { + string Category { get; } + + DesignerActionItem GetItem(DesignerActionList actions, Reflection.MemberInfo mbr); + } + + [System.AttributeUsage(System.AttributeTargets.Method, Inherited = true, AllowMultiple = false)] + internal sealed class DesignerActionMethodAttribute : System.Attribute, IActionGetItem + { + public DesignerActionMethodAttribute(string displayName, int displayOrder = 0) + { + DisplayName = displayName; + DisplayOrder = displayOrder; + } + + public bool AllowAssociate { get; set; } + + public string Category { get; set; } + + public string Condition { get; set; } + + public string Description { get; set; } + + public string DisplayName { get; } + + public int DisplayOrder { get; } + + public bool IncludeAsDesignerVerb { get; set; } + + DesignerActionItem IActionGetItem.GetItem(DesignerActionList actions, Reflection.MemberInfo mbr) + { + var ret = new DesignerActionMethodItem(actions, mbr.Name, DisplayName, Category, Description, IncludeAsDesignerVerb) + { AllowAssociate = AllowAssociate }; + if (!string.IsNullOrEmpty(Condition)) + ret.Properties.Add("Condition", Condition); + ret.Properties.Add("Order", DisplayOrder); + return ret; + } + } + + [System.AttributeUsage(System.AttributeTargets.Property, Inherited = true, AllowMultiple = false)] + internal sealed class DesignerActionPropertyAttribute : System.Attribute, IActionGetItem + { + public DesignerActionPropertyAttribute(string displayName, int displayOrder = 0) + { + DisplayName = displayName; + DisplayOrder = displayOrder; + } + + public bool AllowAssociate { get; set; } + + public string Category { get; set; } + + public string Condition { get; set; } + + public string Description { get; set; } + + public string DisplayName { get; } + + public int DisplayOrder { get; } + + DesignerActionItem IActionGetItem.GetItem(DesignerActionList actions, Reflection.MemberInfo mbr) + { + var ret = new DesignerActionPropertyItem(mbr.Name, DisplayName, Category, Description) + { AllowAssociate = AllowAssociate }; + if (!string.IsNullOrEmpty(Condition)) + ret.Properties.Add("Condition", Condition); + ret.Properties.Add("Order", DisplayOrder); + return ret; + } + } + + [System.AttributeUsage(System.AttributeTargets.Method, Inherited = true, AllowMultiple = false)] + internal sealed class DesignerVerbAttribute : System.Attribute + { + private CommandID cmdId; + private string menuText; + + public DesignerVerbAttribute(string menuText) + { + this.menuText = menuText; + } + + public DesignerVerbAttribute(string menuText, Guid commandMenuGroup, int commandId) + { + this.menuText = menuText; + cmdId = new CommandID(commandMenuGroup, commandId); + } + + internal DesignerVerb GetDesignerVerb(object obj, Reflection.MethodInfo mi) + { + EventHandler handler = (EventHandler)Delegate.CreateDelegate(typeof(EventHandler), obj, mi); + if (cmdId != null) + return new DesignerVerb(menuText, handler, cmdId); + return new DesignerVerb(menuText, handler); + } + } + + internal class EditorServiceContext : IWindowsFormsEditorService, ITypeDescriptorContext, IServiceProvider + { + private IComponentChangeService _componentChangeSvc; + private ComponentDesigner _designer; + private PropertyDescriptor _targetProperty; + + internal EditorServiceContext(ComponentDesigner designer) + { + _designer = designer; + } + + internal EditorServiceContext(ComponentDesigner designer, PropertyDescriptor prop) + { + _designer = designer; + _targetProperty = prop; + if (prop == null) + { + prop = TypeDescriptor.GetDefaultProperty(designer.Component); + if ((prop != null) && typeof(System.Collections.ICollection).IsAssignableFrom(prop.PropertyType)) + _targetProperty = prop; + } + } + + internal EditorServiceContext(ComponentDesigner designer, PropertyDescriptor prop, string newVerbText) + : this(designer, prop) + { + _designer.Verbs.Add(new DesignerVerb(newVerbText, new EventHandler(OnEditItems))); + } + + private T GetService() => (T)((IServiceProvider)this).GetService(typeof(T)); + + private void OnEditItems(object sender, EventArgs e) + { + object component = _targetProperty.GetValue(_designer.Component); + if (component != null) + { + CollectionEditor editor = TypeDescriptor.GetEditor(component, typeof(System.Drawing.Design.UITypeEditor)) as CollectionEditor; + if (editor != null) + editor.EditValue(this, this, component); + } + } + + void ITypeDescriptorContext.OnComponentChanged() + { + ChangeService.OnComponentChanged(_designer.Component, _targetProperty, null, null); + } + + bool ITypeDescriptorContext.OnComponentChanging() + { + try + { + ChangeService.OnComponentChanging(_designer.Component, _targetProperty); + } + catch (CheckoutException exception) + { + if (exception != CheckoutException.Canceled) + throw; + return false; + } + return true; + } + + object IServiceProvider.GetService(Type serviceType) + { + if ((serviceType == typeof(ITypeDescriptorContext)) || (serviceType == typeof(IWindowsFormsEditorService))) + return this; + if ((_designer.Component != null) && (_designer.Component.Site != null)) + return _designer.Component.Site.GetService(serviceType); + return null; + } + + void IWindowsFormsEditorService.CloseDropDown() + { + } + + void IWindowsFormsEditorService.DropDownControl(Control control) + { + } + + public DialogResult ShowDialog(Form dialog) + { + if (dialog == null) + throw new ArgumentNullException(nameof(dialog)); + IUIService service = GetService(); + if (service != null) + return service.ShowDialog(dialog); + return dialog.ShowDialog(_designer.Component as IWin32Window); + } + + private IComponentChangeService ChangeService + { + get + { + if (_componentChangeSvc == null) + _componentChangeSvc = GetService(); + return _componentChangeSvc; + } + } + + IContainer ITypeDescriptorContext.Container + { + get + { + if (_designer.Component.Site != null) + return _designer.Component.Site.Container; + return null; + } + } + + object ITypeDescriptorContext.Instance => _designer.Component; + + PropertyDescriptor ITypeDescriptorContext.PropertyDescriptor => _targetProperty; + } + + internal abstract class RichBehavior : Behavior.Behavior where D : ControlDesigner + { + public RichBehavior(D designer) + { + Designer = designer; + } + + public D Designer { get; } + } + + internal class RichComponentDesigner : ComponentDesigner + where C : Component + where A : _BaseDesignerActionList + { + private A actions; + private Adorner adorner; + private IDictionary> redirectedProps; + private DesignerVerbCollection verbs; + + public override DesignerActionListCollection ActionLists + { + get + { + if (actions == null) + actions = Activator.CreateInstance(typeof(A), this, Component) as A; + return new DesignerActionListCollection(new DesignerActionList[] { actions }); + } + } + + public BehaviorService BehaviorService { get; private set; } + + public IComponentChangeService ComponentChangeService { get; private set; } + + public new C Component => (C)base.Component; + + public virtual GlyphCollection Glyphs => Adorner.Glyphs; + + public ISelectionService SelectionService { get; private set; } + + public override DesignerVerbCollection Verbs + { + get + { + if (verbs == null) + verbs = this.GetAttributedVerbs(); + return verbs; + } + } + + internal Adorner Adorner + { + get + { + if (adorner == null) + { + adorner = new Adorner(); + BehaviorService.Adorners.Add(adorner); + } + return adorner; + } + } + + protected virtual IEnumerable PropertiesToRemove => new string[0]; + + public override void Initialize(IComponent component) + { + base.Initialize(component); + BehaviorService = GetService(); + SelectionService = GetService(); + if (SelectionService != null) + SelectionService.SelectionChanged += OnSelectionChanged; + ComponentChangeService = GetService(); + if (ComponentChangeService != null) + ComponentChangeService.ComponentChanged += OnComponentChanged; + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (BehaviorService != null & adorner != null) + BehaviorService.Adorners.Remove(adorner); + ISelectionService ss = SelectionService; + if (ss != null) + ss.SelectionChanged -= OnSelectionChanged; + IComponentChangeService cs = ComponentChangeService; + if (cs != null) + cs.ComponentChanged -= OnComponentChanged; + } + base.Dispose(disposing); + } + + protected virtual S GetService() where S : class => (S)GetService(typeof(S)); + + protected virtual void OnComponentChanged(object sender, ComponentChangedEventArgs e) + { + } + + protected virtual void OnSelectionChanged(object sender, EventArgs e) + { + } + + protected override void PreFilterProperties(System.Collections.IDictionary properties) + { + base.PreFilterProperties(properties); + + // RedirectRegisteredProperties + if (redirectedProps == null) + redirectedProps = this.GetRedirectedProperties(); + this.RedirectRegisteredProperties(properties, redirectedProps); + + // Remove properties + this.RemoveProperties(properties, PropertiesToRemove); + } + } + + internal class RichControlDesigner : ControlDesigner + where C : Control + where A : _BaseDesignerActionList + { + private A actions; + private Adorner adorner; + private IDictionary> redirectedProps; + private DesignerVerbCollection verbs; + + public override DesignerActionListCollection ActionLists + { + get + { + if (actions == null) + actions = Activator.CreateInstance(typeof(A), this, Component) as A; + return new DesignerActionListCollection(new DesignerActionList[] { actions }); + } + } + + public new BehaviorService BehaviorService => base.BehaviorService; + + public IComponentChangeService ComponentChangeService { get; private set; } + + public new C Control => (C)base.Control; + + public virtual GlyphCollection Glyphs => Adorner.Glyphs; + + public ISelectionService SelectionService { get; private set; } + + public override DesignerVerbCollection Verbs + { + get + { + if (verbs == null) + verbs = this.GetAttributedVerbs(); + return verbs; + } + } + + internal Adorner Adorner + { + get + { + if (adorner == null) + { + adorner = new Adorner(); + BehaviorService.Adorners.Add(adorner); + } + return adorner; + } + } + + protected virtual IEnumerable PropertiesToRemove => new string[0]; + + public override void Initialize(IComponent component) + { + base.Initialize(component); + SelectionService = GetService(); + if (SelectionService != null) + SelectionService.SelectionChanged += OnSelectionChanged; + ComponentChangeService = GetService(); + if (ComponentChangeService != null) + ComponentChangeService.ComponentChanged += OnComponentChanged; + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (BehaviorService != null) + BehaviorService.Adorners.Remove(adorner); + ISelectionService ss = SelectionService; + if (ss != null) + ss.SelectionChanged -= OnSelectionChanged; + IComponentChangeService cs = ComponentChangeService; + if (cs != null) + cs.ComponentChanged -= OnComponentChanged; + } + base.Dispose(disposing); + } + + protected virtual S GetService() where S : class => (S)GetService(typeof(S)); + + protected virtual void OnComponentChanged(object sender, ComponentChangedEventArgs e) + { + } + + protected virtual void OnSelectionChanged(object sender, EventArgs e) + { + } + + protected override void PreFilterProperties(System.Collections.IDictionary properties) + { + base.PreFilterProperties(properties); + + // RedirectRegisteredProperties + if (redirectedProps == null) + redirectedProps = this.GetRedirectedProperties(); + this.RedirectRegisteredProperties(properties, redirectedProps); + + // Remove properties + this.RemoveProperties(properties, PropertiesToRemove); + } + } + + internal abstract class _BaseDesignerActionList : DesignerActionList + { + private List fullAIList; + + public _BaseDesignerActionList(ComponentDesigner designer, IComponent component) + : base(component) + { + base.AutoShow = true; + ParentDesigner = designer; + } + + public ComponentDesigner ParentDesigner { get; } + + public override DesignerActionItemCollection GetSortedActionItems() + { + // Retrieve all attributed methods and properties + if (fullAIList == null) + fullAIList = this.GetAllAttributedActionItems(); + + // Filter for conditions and load + return this.GetFilteredActionItems(fullAIList); + } + + protected T GetComponentProperty(string propName) + { + var p = ComponentProp(propName, typeof(T)); + if (p != null) + return (T)p.GetValue(Component, null); + return default(T); + } + + protected void SetComponentProperty(string propName, T value) + { + var p = ComponentProp(propName, typeof(T)); + if (p != null) + p.SetValue(Component, value, null); + } + + private Reflection.PropertyInfo ComponentProp(string propName, Type retType) => Component.GetType().GetProperty(propName, ComponentDesignerExtension.AllInstBind, null, retType, Type.EmptyTypes, null); + } + + internal abstract class RichDesignerActionList : _BaseDesignerActionList where D : ComponentDesigner where C : Component + { + public RichDesignerActionList(D designer, C component) : base(designer, component) + { + ParentDesigner = designer; + } + + public new C Component => (C)base.Component; + + public new D ParentDesigner { get; } + } + + internal abstract class RichGlyph : Glyph, IDisposable where D : ControlDesigner + { + public RichGlyph(D designer, Behavior.Behavior behavior) + : base(behavior) + { + Designer = designer; + } + + public D Designer { get; } + + public virtual void Dispose() + { + } + + public void SetBehavior(RichBehavior b) { base.SetBehavior(b); } + } + + internal class RichParentControlDesigner : ParentControlDesigner + where C : Control + where A : _BaseDesignerActionList + { + private A actions; + private Adorner adorner; + private IDictionary> redirectedProps; + private DesignerVerbCollection verbs; + + public override DesignerActionListCollection ActionLists + { + get + { + if (actions == null) + actions = Activator.CreateInstance(typeof(A), this, Component) as A; + return new DesignerActionListCollection(new DesignerActionList[] { actions }); + } + } + + public new BehaviorService BehaviorService => base.BehaviorService; + + public IComponentChangeService ComponentChangeService { get; private set; } + + public new C Control => (C)base.Control; + + public virtual GlyphCollection Glyphs => Adorner.Glyphs; + + public ISelectionService SelectionService { get; private set; } + + public override DesignerVerbCollection Verbs + { + get + { + if (verbs == null) + verbs = this.GetAttributedVerbs(); + return verbs; + } + } + + internal Adorner Adorner + { + get + { + if (adorner == null) + { + adorner = new Adorner(); + BehaviorService.Adorners.Add(adorner); + } + return adorner; + } + } + + protected virtual IEnumerable PropertiesToRemove => new string[0]; + + public override void Initialize(IComponent component) + { + base.Initialize(component); + SelectionService = GetService(); + if (SelectionService != null) + SelectionService.SelectionChanged += OnSelectionChanged; + ComponentChangeService = GetService(); + if (ComponentChangeService != null) + ComponentChangeService.ComponentChanged += OnComponentChanged; + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (BehaviorService != null & adorner != null) + BehaviorService.Adorners.Remove(adorner); + ISelectionService ss = SelectionService; + if (ss != null) + ss.SelectionChanged -= OnSelectionChanged; + IComponentChangeService cs = ComponentChangeService; + if (cs != null) + cs.ComponentChanged -= OnComponentChanged; + } + base.Dispose(disposing); + } + + protected virtual S GetService() where S : class => (S)GetService(typeof(S)); + + protected virtual void OnComponentChanged(object sender, ComponentChangedEventArgs e) + { + } + + protected virtual void OnSelectionChanged(object sender, EventArgs e) + { + } + + protected override void PreFilterProperties(System.Collections.IDictionary properties) + { + base.PreFilterProperties(properties); + + // RedirectRegisteredProperties + if (redirectedProps == null) + redirectedProps = this.GetRedirectedProperties(); + this.RedirectRegisteredProperties(properties, redirectedProps); + + // Remove properties + this.RemoveProperties(properties, PropertiesToRemove); + } + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/GlassExtenderProvider.bmp b/AeroWizard/AeroWizard/GlassExtenderProvider.bmp new file mode 100644 index 0000000..c8aa486 Binary files /dev/null and b/AeroWizard/AeroWizard/GlassExtenderProvider.bmp differ diff --git a/AeroWizard/AeroWizard/GlassExtenderProvider.cs b/AeroWizard/AeroWizard/GlassExtenderProvider.cs new file mode 100644 index 0000000..8ba4723 --- /dev/null +++ b/AeroWizard/AeroWizard/GlassExtenderProvider.cs @@ -0,0 +1,277 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; +using System.Windows.Forms; + +namespace Vanara.Interop.DesktopWindowManager +{ + /// + /// GlassExtenderProvider extends a and provides glass margins. + /// + [ProvideProperty("GlassEnabled", typeof(Form))] + [ProvideProperty("GlassMarginMovesForm", typeof(Form))] + [ProvideProperty("GlassMargins", typeof(Form))] + [ToolboxItem(true), ToolboxBitmap(typeof(AeroWizard.WizardControl), "GlassExtenderProvider.bmp")] + [Description("Extender for a Form that adds Aero glass properties.")] + public class GlassExtenderProvider : Component, IExtenderProvider + { + private readonly Dictionary formProps = new Dictionary(); + + /// + /// Properties for each form that is extended. + /// + [SuppressMessage("ReSharper", "InconsistentNaming")] + private class GlassFormProperties + { + public Point FormMoveLastMousePos = Point.Empty; + public bool FormMoveTracking; + public bool GlassEnabled = true; + public Padding GlassMargins = Padding.Empty; + public bool GlassMarginMovesForm = true; + } + + /// + /// Gets whether glass should be extended into the client space. + /// + /// The to be extended. + /// true if the glass is enabled; otherwise false. + [DisplayName(@"GlassEnabled")] + [DefaultValue(true)] + [Category("Behavior")] + [Description("Indicates whether extending glass into the client area is enabled.")] + public bool GetGlassEnabled(Form form) + { + GlassFormProperties prop; + if (formProps.TryGetValue(form, out prop)) + return prop.GlassEnabled; + return true; + } + + /// + /// Gets a value indicating whether clicking and dragging within the top margin will move the form. + /// + /// The to be extended. + /// true if clicking and dragging on the top margin moves the form; otherwise, false. + [DisplayName(@"GlassMarginMovesForm")] + [DefaultValue(true)] + [Category("Behavior")] + [Description("Specifies if clicking and dragging within the margin will move the form. ")] + public bool GetGlassMarginMovesForm(Form form) + { + GlassFormProperties prop; + if (formProps.TryGetValue(form, out prop)) + return prop.GlassMarginMovesForm; + return true; + } + + /// + /// Gets the glass margins. + /// + /// The to be extended. + /// The margins where the glass will be extended. + [DefaultValue(typeof(Padding), "0")] + [DisplayName(@"GlassMargins")] + [Description("Specifies the interior glass margin of the form. Set to -1 for full window glass.")] + [Category("Layout")] + public Padding GetGlassMargins(Form form) + { + GlassFormProperties prop; + if (formProps.TryGetValue(form, out prop)) + return prop.GlassMargins; + return Padding.Empty; + } + + /// + /// Specifies whether this object can provide its extender properties to the specified object. + /// + /// The to receive the extender properties. + /// + /// true if this object can provide extender properties to the specified object; otherwise, false. + /// + bool IExtenderProvider.CanExtend(object form) => (form != this) && (form is Form); + + /// + /// Set whether the glass should be extended into the client space. + /// + /// The to be extended. + /// The enabled value. + public void SetGlassEnabled(Form form, bool value) + { + var prop = GetFormProperties(form); + prop.GlassEnabled = value; + GlassifyForm(form); + } + + /// + /// Sets a value indicating whether clicking and dragging within the margin will move the form. + /// + /// The to be extended. + /// true if clicking and dragging within the margin moves the form; otherwise, false. + public void SetGlassMarginMovesForm(Form form, bool value) + { + var prop = GetFormProperties(form); + prop.GlassMarginMovesForm = value; + } + + /// + /// Sets the glass margins. + /// + /// The to be extended. + /// The margins where the glass will be extended. + public void SetGlassMargins(Form form, Padding value) + { + if (form == null) + throw new ArgumentNullException(nameof(form)); + + var prop = GetFormProperties(form); + if (value == Padding.Empty) + { + prop.GlassMargins = Padding.Empty; + UnhookForm(form); + } + else + { + prop.GlassMargins = value; + form.Paint += form_Paint; + if (!form.IsDesignMode()) + { + form.MouseDown += form_MouseDown; + form.MouseMove += form_MouseMove; + form.MouseUp += form_MouseUp; + form.Resize += form_Resize; + form.Shown += form_Shown; + } + } + form.Invalidate(); + } + + /// + /// Releases the unmanaged resources used by the and optionally releases the managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected override void Dispose(bool disposing) + { + if (disposing) + { + foreach (var control in formProps.Keys) + { + var form = control as Form; + if (form != null && !form.IsDisposed) + UnhookForm(form); + } + } + base.Dispose(disposing); + } + + private void form_MouseDown(object sender, MouseEventArgs e) + { + if (e.Button != MouseButtons.Left) return; + var prop = GetFormProperties(sender as Form); + if (!prop.GlassMarginMovesForm) return; + prop.FormMoveTracking = true; + prop.FormMoveLastMousePos = ((Control)sender).PointToScreen(e.Location); + } + + private void form_MouseMove(object sender, MouseEventArgs e) + { + var form = sender as Form; + if (form == null) return; + var prop = GetFormProperties(form); + if (!prop.FormMoveTracking || GetNonGlassArea(form, prop).Contains(e.Location)) return; + var screen = form.PointToScreen(e.Location); + var diff = new Point(screen.X - prop.FormMoveLastMousePos.X, screen.Y - prop.FormMoveLastMousePos.Y); + var loc = form.Location; + loc.Offset(diff); + form.Location = loc; + prop.FormMoveLastMousePos = screen; + } + + private void form_MouseUp(object sender, MouseEventArgs e) + { + if (e.Button != MouseButtons.Left) return; + var prop = GetFormProperties(sender as Form); + prop.FormMoveTracking = false; + } + + private void form_Paint(object sender, PaintEventArgs e) + { + GlassifyForm(sender as Form, e.Graphics); + } + + private void form_Resize(object sender, EventArgs e) + { + var form = sender as Form; + if (form != null && (DesktopWindowManager.IsCompositionEnabled() && GetGlassEnabled(form)) || form.IsDesignMode()) + InvalidateNonGlassClientArea(form); + } + + private void form_Shown(object sender, EventArgs e) + { + GlassifyForm(sender as Form); + } + + private GlassFormProperties GetFormProperties(Form form) + { + GlassFormProperties prop; + if (!formProps.TryGetValue(form, out prop)) + formProps.Add(form, prop = new GlassFormProperties()); + return prop; + } + + private static Rectangle GetNonGlassArea(Form form, GlassFormProperties prop) + { + if (prop == null) + return form.ClientRectangle; + return new Rectangle(form.ClientRectangle.Left + prop.GlassMargins.Left, form.ClientRectangle.Top + prop.GlassMargins.Top, + form.ClientRectangle.Width - prop.GlassMargins.Horizontal, form.ClientRectangle.Height - prop.GlassMargins.Vertical); + } + + private void GlassifyForm(Form form, Graphics g = null) + { + if (!(DesktopWindowManager.IsCompositionEnabled() && GetGlassEnabled(form)) && !form.IsDesignMode()) + return; + + if (g == null) g = form.CreateGraphics(); + + GlassFormProperties prop; + if (!formProps.TryGetValue(form, out prop)) + return; + + // Paint the glass effect. + if (prop.GlassMargins == new Padding(-1)) + g.FillRectangle(Brushes.Black, form.ClientRectangle); + else + { + using (var r = new Region(form.ClientRectangle)) + { + r.Exclude(GetNonGlassArea(form, prop)); + g.FillRegion(Brushes.Black, r); + } + } + + if (!form.IsDesignMode()) + form.ExtendFrameIntoClientArea(prop.GlassMargins); + } + + private void InvalidateNonGlassClientArea(Form form) + { + var glassMargin = GetGlassMargins(form); + if (glassMargin == Padding.Empty) return; + var rect = new Rectangle(glassMargin.Left, glassMargin.Top, form.ClientRectangle.Width - glassMargin.Right, + form.ClientRectangle.Height - glassMargin.Bottom); + form.Invalidate(rect, false); + } + + private void UnhookForm(Form form) + { + form.MouseDown -= form_MouseDown; + form.MouseMove -= form_MouseMove; + form.MouseUp -= form_MouseUp; + form.Shown -= form_Shown; + form.Resize -= form_Resize; + form.Paint -= form_Paint; + } + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/GlobalSuppressions.cs b/AeroWizard/AeroWizard/GlobalSuppressions.cs new file mode 100644 index 0000000..80ca9a9 Binary files /dev/null and b/AeroWizard/AeroWizard/GlobalSuppressions.cs differ diff --git a/AeroWizard/AeroWizard/Native/BITMAPINFO.cs b/AeroWizard/AeroWizard/Native/BITMAPINFO.cs new file mode 100644 index 0000000..1e6f9b8 --- /dev/null +++ b/AeroWizard/AeroWizard/Native/BITMAPINFO.cs @@ -0,0 +1,436 @@ +using System; +using System.Runtime.InteropServices; + +namespace Vanara.Interop +{ + internal static partial class NativeMethods + { + /// The BITMAPINFO structure defines the dimensions and color information for a DIB. + /// + /// A DIB consists of two distinct parts: a BITMAPINFO structure describing the dimensions and colors of the + /// bitmap, and an array of bytes defining the pixels of the bitmap. The bits in the array are packed together, + /// but each scan line must be padded with zeros to end on a LONG data-type boundary. If the height of the bitmap + /// is positive, the bitmap is a bottom-up DIB and its origin is the lower-left corner. If the height is + /// negative, the bitmap is a top-down DIB and its origin is the upper left corner. + /// + /// A bitmap is packed when the bitmap array immediately follows the BITMAPINFO header. Packed bitmaps are + /// referenced by a single pointer. For packed bitmaps, the biClrUsed member must be set to an even number when + /// using the DIB_PAL_COLORS mode so that the DIB bitmap array starts on a DWORD boundary. + /// + /// Note + /// + /// The bmiColors member should not contain palette indexes if the bitmap is to be stored in a file or + /// transferred to another application. + /// + /// + /// Unless the application has exclusive use and control of the bitmap, the bitmap color table should contain + /// explicit RGB values. + /// + /// + [StructLayout(LayoutKind.Sequential)] + public struct BITMAPINFO + { + /// A BITMAPINFOHEADER structure that contains information about the dimensions of color format. + public BITMAPINFOHEADER bmiHeader; + + /// + /// The bmiColors member contains one of the following: + /// + /// + /// An array of RGBQUAD. The elements of the array that make up the color table. + /// + /// + /// + /// An array of 16-bit unsigned integers that specifies indexes into the currently realized logical palette. + /// This use of bmiColors is allowed for functions that use DIBs. When bmiColors elements contain indexes to + /// a realized logical palette, they must also call the following bitmap functions: CreateDIBitmap, + /// CreateDIBPatternBrush, CreateDIBSection (The iUsage parameter of CreateDIBSection must be set to DIB_PAL_COLORS.) + /// + /// + /// + /// + /// The number of entries in the array depends on the values of the biBitCount and biClrUsed members of the + /// BITMAPINFOHEADER structure. + /// + /// + /// The colors in the bmiColors table appear in order of importance. For more information, see the Remarks section. + /// + /// + [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 1, ArraySubType = UnmanagedType.Struct)] + public RGBQUAD[] bmiColors; + + /// + /// Initializes a new instance of the structure. + /// + /// The width. + /// The height. + /// The bit count. + public BITMAPINFO(int width, int height, ushort bitCount = 32) + : this() + { + bmiHeader.biSize = Marshal.SizeOf(typeof(BITMAPINFO)); + bmiHeader.biWidth = width; + bmiHeader.biHeight = height; + bmiHeader.biPlanes = 1; + bmiHeader.biBitCount = bitCount; + bmiHeader.biCompression = BitmapCompressionMode.BI_RGB; + bmiHeader.biSizeImage = 0; // (uint)width * (uint)height * bitCount / 8; + } + } + + /// The type of compression for a compressed bottom-up bitmap (top-down DIBs cannot be compressed). Used in . + public enum BitmapCompressionMode : uint + { + /// An uncompressed format. + BI_RGB = 0, + + /// A run-length encoded (RLE) format for bitmaps with 8 bpp. The compression format is a 2-byte format consisting of a count byte followed by a byte containing a color index. + BI_RLE8 = 1, + + /// An RLE format for bitmaps with 4 bpp. The compression format is a 2-byte format consisting of a count byte followed by two word-length color indexes. + BI_RLE4 = 2, + + /// Specifies that the bitmap is not compressed and that the color table consists of three DWORD color masks that specify the red, green, and blue components, respectively, of each pixel. This is valid when used with 16- and 32-bpp bitmaps. + BI_BITFIELDS = 3, + + /// Indicates that the image is a JPEG image. + BI_JPEG = 4, + + /// Indicates that the image is a PNG image. + BI_PNG = 5 + } + + /// The BITMAPINFOHEADER structure contains information about the dimensions and color format of a DIB. + [StructLayout(LayoutKind.Sequential)] + public struct BITMAPINFOHEADER + { + /// The number of bytes required by the structure. + public int biSize; + + /// + /// The width of the bitmap, in pixels. If biCompression is BI_JPEG or BI_PNG, the biWidth member specifies the width of the decompressed JPEG or PNG + /// image file, respectively. + /// + public int biWidth; + + /// + /// The height of the bitmap, in pixels. If biHeight is positive, the bitmap is a bottom-up DIB and its origin is the lower-left corner. If biHeight + /// is negative, the bitmap is a top-down DIB and its origin is the upper-left corner. + /// If biHeight is negative, indicating a top-down DIB, biCompression must be either BI_RGB or BI_BITFIELDS. Top-down DIBs cannot be compressed. + /// If biCompression is BI_JPEG or BI_PNG, the biHeight member specifies the height of the decompressed JPEG or PNG image file, respectively. + /// + public int biHeight; + + /// The number of planes for the target device. This value must be set to 1. + public ushort biPlanes; + + /// + /// The number of bits-per-pixel. The biBitCount member of the BITMAPINFOHEADER structure determines the number of bits that define each pixel and + /// the maximum number of colors in the bitmap. This member must be one of the following values. + /// + /// + /// Value + /// Meaning + /// + /// + /// 0 + /// The number of bits-per-pixel is specified or is implied by the JPEG or PNG format. + /// + /// + /// 1 + /// + /// The bitmap is monochrome, and the bmiColors member of BITMAPINFO contains two entries. Each bit in the bitmap array represents a pixel. If the + /// bit is clear, the pixel is displayed with the color of the first entry in the bmiColors table; if the bit is set, the pixel has the color of the + /// second entry in the table. + /// + /// + /// + /// 4 + /// + /// The bitmap has a maximum of 16 colors, and the bmiColors member of BITMAPINFO contains up to 16 entries. Each pixel in the bitmap is represented + /// by a 4-bit index into the color table. For example, if the first byte in the bitmap is 0x1F, the byte represents two pixels. The first pixel + /// contains the color in the second table entry, and the second pixel contains the color in the sixteenth table entry. + /// + /// + /// + /// 8 + /// + /// The bitmap has a maximum of 256 colors, and the bmiColors member of BITMAPINFO contains up to 256 entries. In this case, each byte in the array + /// represents a single pixel. + /// + /// + /// + /// 16 + /// + /// The bitmap has a maximum of 2^16 colors. If the biCompression member of the BITMAPINFOHEADER is BI_RGB, the bmiColors member of BITMAPINFO is + /// NULL. Each WORD in the bitmap array represents a single pixel. The relative intensities of red, green, and blue are represented with five bits + /// for each color component. The value for blue is in the least significant five bits, followed by five bits each for green and red. The most + /// significant bit is not used. The bmiColors color table is used for optimizing colors used on palette-based devices, and must contain the number + /// of entries specified by the biClrUsed member of the BITMAPINFOHEADER. + /// + /// If the biCompression member of the BITMAPINFOHEADER is BI_BITFIELDS, the bmiColors member contains three DWORD color masks that specify the red, + /// green, and blue components, respectively, of each pixel. Each WORD in the bitmap array represents a single pixel. + /// + /// + /// When the biCompression member is BI_BITFIELDS, bits set in each DWORD mask must be contiguous and should not overlap the bits of another mask. + /// All the bits in the pixel do not have to be used. + /// + /// + /// + /// + /// 24 + /// + /// The bitmap has a maximum of 2^24 colors, and the bmiColors member of BITMAPINFO is NULL. Each 3-byte triplet in the bitmap array represents the + /// relative intensities of blue, green, and red, respectively, for a pixel. The bmiColors color table is used for optimizing colors used on + /// palette-based devices, and must contain the number of entries specified by the biClrUsed member of the BITMAPINFOHEADER. + /// + /// + /// + /// 32 + /// + /// The bitmap has a maximum of 2^32 colors. If the biCompression member of the BITMAPINFOHEADER is BI_RGB, the bmiColors member of BITMAPINFO is + /// NULL. Each DWORD in the bitmap array represents the relative intensities of blue, green, and red for a pixel. The value for blue is in the least + /// significant 8 bits, followed by 8 bits each for green and red. The high byte in each DWORD is not used. The bmiColors color table is used for + /// optimizing colors used on palette-based devices, and must contain the number of entries specified by the biClrUsed member of the BITMAPINFOHEADER. + /// + /// If the biCompression member of the BITMAPINFOHEADER is BI_BITFIELDS, the bmiColors member contains three DWORD color masks that specify the red, + /// green, and blue components, respectively, of each pixel. Each DWORD in the bitmap array represents a single pixel. + /// + /// + /// When the biCompression member is BI_BITFIELDS, bits set in each DWORD mask must be contiguous and should not overlap the bits of another mask. + /// All the bits in the pixel do not need to be used. + /// + /// + /// + /// + /// + public ushort biBitCount; + + /// The type of compression for a compressed bottom-up bitmap (top-down DIBs cannot be compressed). + public BitmapCompressionMode biCompression; + + /// + /// The size, in bytes, of the image. This may be set to zero for BI_RGB bitmaps. If biCompression is BI_JPEG or BI_PNG, biSizeImage indicates the + /// size of the JPEG or PNG image buffer, respectively. + /// + public uint biSizeImage; + + /// + /// The horizontal resolution, in pixels-per-meter, of the target device for the bitmap. An application can use this value to select a bitmap from a + /// resource group that best matches the characteristics of the current device. + /// + public int biXPelsPerMeter; + + /// The vertical resolution, in pixels-per-meter, of the target device for the bitmap. + public int biYPelsPerMeter; + + /// + /// The number of color indexes in the color table that are actually used by the bitmap. If this value is zero, the bitmap uses the maximum number of + /// colors corresponding to the value of the biBitCount member for the compression mode specified by biCompression. + /// + /// If biClrUsed is nonzero and the biBitCount member is less than 16, the biClrUsed member specifies the actual number of colors the graphics engine + /// or device driver accesses. If biBitCount is 16 or greater, the biClrUsed member specifies the size of the color table used to optimize + /// performance of the system color palettes. If biBitCount equals 16 or 32, the optimal color palette starts immediately following the three DWORD masks. + /// + /// + /// When the bitmap array immediately follows the BITMAPINFO structure, it is a packed bitmap. Packed bitmaps are referenced by a single pointer. + /// Packed bitmaps require that the biClrUsed member must be either zero or the actual size of the color table. + /// + /// + public uint biClrUsed; + + /// The number of color indexes that are required for displaying the bitmap. If this value is zero, all colors are required. + public uint biClrImportant; + } + + /// The RGBQUAD structure describes a color consisting of relative intensities of red, green, and blue. + [StructLayout(LayoutKind.Sequential)] + public struct RGBQUAD + { + /// The intensity of blue in the color. + public byte rgbBlue; + /// The intensity of green in the color. + public byte rgbGreen; + /// The intensity of red in the color. + public byte rgbRed; + /// This member is reserved and must be zero. + public byte rgbReserved; + + /// Gets or sets the color associated with the structure. + /// The color. + public System.Drawing.Color Color + { + get { return System.Drawing.Color.FromArgb(rgbReserved, rgbRed, rgbGreen, rgbBlue); } + set { rgbReserved = value.A; rgbBlue = value.B; rgbGreen = value.G; rgbRed = value.R; } + } + } + + public enum DIBColorMode : int + { + /// The BITMAPINFO structure contains an array of literal RGB values. + DIB_RGB_COLORS = 0, + + /// + /// The bmiColors member of the BITMAPINFO structure is an array of 16-bit indexes into the logical palette of the device context specified by hdc. + /// + DIB_PAL_COLORS = 1 + } + + /// + /// The CreateDIBSection function creates a DIB that applications can write to directly. The function gives you a pointer to the location of the bitmap + /// bit values. You can supply a handle to a file-mapping object that the function will use to create the bitmap, or you can let the system allocate the + /// memory for the bitmap. + /// + /// + /// A handle to a device context. If the value of iUsage is DIB_PAL_COLORS, the function uses this device context's logical palette to initialize the DIB colors. + /// + /// A pointer to a BITMAPINFO structure that specifies various attributes of the DIB, including the bitmap dimensions and colors. + /// + /// The type of data contained in the bmiColors array member of the BITMAPINFO structure pointed to by pbmi (either logical palette indexes or literal + /// RGB values). + /// + /// A pointer to a variable that receives a pointer to the location of the DIB bit values. + /// + /// A handle to a file-mapping object that the function will use to create the DIB. This parameter can be NULL. + /// + /// If hSection is not NULL, it must be a handle to a file-mapping object created by calling the CreateFileMapping function with the PAGE_READWRITE or + /// PAGE_WRITECOPY flag. Read-only DIB sections are not supported. Handles created by other means will cause CreateDIBSection to fail. + /// + /// + /// If hSection is not NULL, the CreateDIBSection function locates the bitmap bit values at offset dwOffset in the file-mapping object referred to by + /// hSection. An application can later retrieve the hSection handle by calling the GetObject function with the HBITMAP returned by CreateDIBSection. + /// + /// + /// If hSection is NULL, the system allocates memory for the DIB. In this case, the CreateDIBSection function ignores the dwOffset parameter. An + /// application cannot later obtain a handle to this memory. The dshSection member of the DIBSECTION structure filled in by calling the GetObject + /// function will be NULL. + /// + /// + /// + /// The offset from the beginning of the file-mapping object referenced by hSection where storage for the bitmap bit values is to begin. This value is + /// ignored if hSection is NULL. The bitmap bit values are aligned on doubleword boundaries, so dwOffset must be a multiple of the size of a DWORD. + /// + /// + /// If the function succeeds, the return value is a handle to the newly created DIB, and *ppvBits points to the bitmap bit values. + /// If the function fails, the return value is NULL, and *ppvBits is NULL. + /// + /// + /// As noted above, if hSection is NULL, the system allocates memory for the DIB. The system closes the handle to that memory when you later delete the + /// DIB by calling the DeleteObject function. If hSection is not NULL, you must close the hSection memory handle yourself after calling DeleteObject to + /// delete the bitmap. + /// You cannot paste a DIB section from one application into another application. + /// + /// CreateDIBSection does not use the BITMAPINFOHEADER parameters biXPelsPerMeter or biYPelsPerMeter and will not provide resolution information in the + /// BITMAPINFO structure. + /// + /// + /// You need to guarantee that the GDI subsystem has completed any drawing to a bitmap created by CreateDIBSection before you draw to the bitmap + /// yourself. Access to the bitmap must be synchronized. Do this by calling the GdiFlush function. This applies to any use of the pointer to the bitmap + /// bit values, including passing the pointer in calls to functions such as SetDIBits. + /// + /// ICM: No color management is done. + /// + [DllImport(GDI32, ExactSpelling = true, SetLastError = true)] + public static extern IntPtr CreateDIBSection(SafeDCHandle hdc, ref BITMAPINFO pbmi, DIBColorMode iUsage, out IntPtr ppvBits, IntPtr hSection, int dwOffset); + + /// + /// The GetDIBits function retrieves the bits of the specified compatible bitmap and copies them into a buffer as a DIB using the specified format. + /// + /// A handle to the device context. + /// A handle to the bitmap. This must be a compatible bitmap (DDB). + /// The first scan line to retrieve. + /// The number of scan lines to retrieve. + /// + /// A pointer to a buffer to receive the bitmap data. If this parameter is NULL, the function passes the dimensions and format of the bitmap to the + /// BITMAPINFO structure pointed to by the lpbi parameter. + /// + /// A pointer to a BITMAPINFO structure that specifies the desired format for the DIB data. + /// The format of the bmiColors member of the BITMAPINFO structure. + /// + /// If the lpvBits parameter is non-NULL and the function succeeds, the return value is the number of scan lines copied from the bitmap. + /// If the lpvBits parameter is NULL and GetDIBits successfully fills the BITMAPINFO structure, the return value is nonzero. + /// If the function fails, the return value is zero. + /// + /// + /// If the requested format for the DIB matches its internal format, the RGB values for the bitmap are copied. If the requested format doesn't match the + /// internal format, a color table is synthesized. The following table describes the color table synthesized for each format. + /// + /// ValueMeaning + /// 1_BPPThe color table consists of a black and a white entry. + /// 4_BPPThe color table consists of a mix of colors identical to the standard VGA palette. + /// 8_BPPThe color table consists of a general mix of 256 colors defined by GDI. (Included in these 256 colors are the 20 colors found in the default logical palette.) + /// 24_BPPNo color table is returned. + /// + /// + /// If the lpvBits parameter is a valid pointer, the first six members of the BITMAPINFOHEADER structure must be initialized to specify the size and + /// format of the DIB. The scan lines must be aligned on a DWORD except for RLE compressed bitmaps. + /// + /// + /// A bottom-up DIB is specified by setting the height to a positive number, while a top-down DIB is specified by setting the height to a negative + /// number. The bitmap color table will be appended to the BITMAPINFO structure. + /// + /// + /// If lpvBits is NULL, GetDIBits examines the first member of the first structure pointed to by lpbi. This member must specify the size, in bytes, of a + /// BITMAPCOREHEADER or a BITMAPINFOHEADER structure. The function uses the specified size to determine how the remaining members should be initialized. + /// + /// + /// If lpvBits is NULL and the bit count member of BITMAPINFO is initialized to zero, GetDIBits fills in a BITMAPINFOHEADER structure or BITMAPCOREHEADER + /// without the color table. This technique can be used to query bitmap attributes. + /// + /// The bitmap identified by the hbmp parameter must not be selected into a device context when the application calls this function. + /// The origin for a bottom-up DIB is the lower-left corner of the bitmap; the origin for a top-down DIB is the upper-left corner. + /// + [DllImport(GDI32, ExactSpelling = true, SetLastError = true)] + public static extern int GetDIBits(SafeDCHandle hdc, IntPtr hbmp, int uStartScan, int cScanLines, ref byte[] lpvBits, ref BITMAPINFO lpbi, DIBColorMode uUsage); + + /// + /// The GetDIBits function retrieves the bits of the specified compatible bitmap and copies them into a buffer as a DIB using the specified format. + /// + /// A handle to the device context. + /// A handle to the bitmap. This must be a compatible bitmap (DDB). + /// The first scan line to retrieve. + /// The number of scan lines to retrieve. + /// + /// A pointer to a buffer to receive the bitmap data. If this parameter is NULL, the function passes the dimensions and format of the bitmap to the + /// BITMAPINFO structure pointed to by the lpbi parameter. + /// + /// A pointer to a BITMAPINFO structure that specifies the desired format for the DIB data. + /// The format of the bmiColors member of the BITMAPINFO structure. + /// + /// If the lpvBits parameter is non-NULL and the function succeeds, the return value is the number of scan lines copied from the bitmap. + /// If the lpvBits parameter is NULL and GetDIBits successfully fills the BITMAPINFO structure, the return value is nonzero. + /// If the function fails, the return value is zero. + /// + /// + /// If the requested format for the DIB matches its internal format, the RGB values for the bitmap are copied. If the requested format doesn't match the + /// internal format, a color table is synthesized. The following table describes the color table synthesized for each format. + /// + /// ValueMeaning + /// 1_BPPThe color table consists of a black and a white entry. + /// 4_BPPThe color table consists of a mix of colors identical to the standard VGA palette. + /// 8_BPPThe color table consists of a general mix of 256 colors defined by GDI. (Included in these 256 colors are the 20 colors found in the default logical palette.) + /// 24_BPPNo color table is returned. + /// + /// + /// If the lpvBits parameter is a valid pointer, the first six members of the BITMAPINFOHEADER structure must be initialized to specify the size and + /// format of the DIB. The scan lines must be aligned on a DWORD except for RLE compressed bitmaps. + /// + /// + /// A bottom-up DIB is specified by setting the height to a positive number, while a top-down DIB is specified by setting the height to a negative + /// number. The bitmap color table will be appended to the BITMAPINFO structure. + /// + /// + /// If lpvBits is NULL, GetDIBits examines the first member of the first structure pointed to by lpbi. This member must specify the size, in bytes, of a + /// BITMAPCOREHEADER or a BITMAPINFOHEADER structure. The function uses the specified size to determine how the remaining members should be initialized. + /// + /// + /// If lpvBits is NULL and the bit count member of BITMAPINFO is initialized to zero, GetDIBits fills in a BITMAPINFOHEADER structure or BITMAPCOREHEADER + /// without the color table. This technique can be used to query bitmap attributes. + /// + /// The bitmap identified by the hbmp parameter must not be selected into a device context when the application calls this function. + /// The origin for a bottom-up DIB is the lower-left corner of the bitmap; the origin for a top-down DIB is the upper-left corner. + /// + [DllImport(GDI32, ExactSpelling = true, SetLastError = true)] + public static extern int GetDIBits(SafeDCHandle hdc, IntPtr hbmp, int uStartScan, int cScanLines, IntPtr lpvBits, ref BITMAPINFO lpbi, DIBColorMode uUsage); + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/Native/ButtonExtension.cs b/AeroWizard/AeroWizard/Native/ButtonExtension.cs new file mode 100644 index 0000000..7e3f8e8 --- /dev/null +++ b/AeroWizard/AeroWizard/Native/ButtonExtension.cs @@ -0,0 +1,19 @@ + +namespace System.Windows.Forms +{ + internal static class TextBoxExtension + { + public static void SetElevationRequiredState(this ButtonBase btn, bool required = true) + { + if (System.Environment.OSVersion.Version.Major >= 6) + { + const uint BCM_SETSHIELD = 0x160C; //Elevated button + btn.FlatStyle = required ? FlatStyle.System : FlatStyle.Standard; + Vanara.Interop.NativeMethods.SendMessage(btn.Handle, BCM_SETSHIELD, IntPtr.Zero, required ? new IntPtr(1) : IntPtr.Zero); + btn.Invalidate(); + } + else + throw new PlatformNotSupportedException(); + } + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/Native/DWMAPI.cs b/AeroWizard/AeroWizard/Native/DWMAPI.cs new file mode 100644 index 0000000..05b560a --- /dev/null +++ b/AeroWizard/AeroWizard/Native/DWMAPI.cs @@ -0,0 +1,167 @@ +using System; +using System.Runtime.InteropServices; + +namespace Vanara.Interop +{ + internal static partial class NativeMethods + { + internal const string DWMAPI = "dwmapi.dll"; + + public enum DWMWINDOWATTRIBUTE : uint + { + NCRenderingEnabled = 1, + NCRenderingPolicy, + TransitionsForceDisabled, + AllowNCPaint, + CaptionButtonBounds, + NonClientRtlLayout, + ForceIconicRepresentation, + Flip3DPolicy, + ExtendedFrameBounds, + HasIconicBitmap, + DisallowPeek, + ExceludedFromPeek, + Cloak, + Cloaked, + FreezeRepresentation + } + + public enum BlurBehindFlags : int + { + Enable = 0x00000001, + BlurRegion = 0x00000002, + TransitionOnMaximized = 0x00000004 + } + + [StructLayout(LayoutKind.Sequential)] + public struct BlurBehind + { + BlurBehindFlags dwFlags; + int fEnable; + IntPtr hRgnBlur; + int fTransitionOnMaximized; + + public BlurBehind(bool enabled) + { + fEnable = enabled ? 1 : 0; + hRgnBlur = IntPtr.Zero; + fTransitionOnMaximized = 0; + dwFlags = BlurBehindFlags.Enable; + } + + public System.Drawing.Region Region => System.Drawing.Region.FromHrgn(hRgnBlur); + + public bool TransitionOnMaximized + { + get { return fTransitionOnMaximized > 0; } + set + { + fTransitionOnMaximized = value ? 1 : 0; + dwFlags |= BlurBehindFlags.TransitionOnMaximized; + } + } + + public void SetRegion(System.Drawing.Graphics graphics, System.Drawing.Region region) + { + hRgnBlur = region.GetHrgn(graphics); + dwFlags |= BlurBehindFlags.BlurRegion; + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct ColorizationParams + { + public uint Color1, Color2, Intensity, Unk1, Unk2, Unk3, Opaque; + } + + /// Margins structure for theme related functions. + [StructLayout(LayoutKind.Sequential)] + public struct Margins + { + public int Left; + public int Right; + public int Top; + public int Bottom; + + public static readonly Margins Empty = new Margins(0); + public static readonly Margins Infinite = new Margins(-1); + + public Margins(int left, int right, int top, int bottom) + { + Left = left; + Right = right; + Top = top; + Bottom = bottom; + } + + public Margins(int allMargins) + { + Left = Right = Top = Bottom = allMargins; + } + + public Margins(System.Windows.Forms.Padding padding) + : this(padding.Left, padding.Right, padding.Top, padding.Bottom) + { + } + + public static bool operator !=(Margins m1, Margins m2) => !m1.Equals(m2); + + public static bool operator ==(Margins m1, Margins m2) => m1.Equals(m2); + + public override bool Equals(object obj) + { + if (obj is Margins) + { + Margins m2 = (Margins)obj; + return Left == m2.Left && Right == m2.Right && Top == m2.Top && Bottom == m2.Bottom; + } + return base.Equals(obj); + } + + public override int GetHashCode() => (((Left ^ RotateLeft(Top, 8)) ^ RotateLeft(Right, 0x10)) ^ RotateLeft(Bottom, 0x18)); + + public override string ToString() => $"{{Left={Left},Right={Right},Top={Top},Bottom={Bottom}}}"; + + internal static int RotateLeft(int value, int nBits) + { + nBits = nBits % 0x20; + return ((value << nBits) | (value >> (0x20 - nBits))); + } + } + + [DllImport(DWMAPI, EntryPoint = "#127", PreserveSig = false)] + [System.Security.SecurityCritical] + public static extern void DwmGetColorizationParameters(ref ColorizationParams parameters); + + [DllImport(DWMAPI, EntryPoint = "#131", PreserveSig = false)] + [System.Security.SecurityCritical] + public static extern void DwmSetColorizationParameters(ref ColorizationParams parameters, uint unk); + + [DllImport(DWMAPI, ExactSpelling = true, PreserveSig = false)] + [System.Security.SecurityCritical] + public static extern void DwmEnableBlurBehindWindow(IntPtr hWnd, ref BlurBehind pBlurBehind); + + [DllImport(DWMAPI, ExactSpelling = true, PreserveSig = false)] + [System.Security.SecurityCritical] + public static extern void DwmEnableComposition(int compositionAction); + + [DllImport(DWMAPI, ExactSpelling = true, PreserveSig = false)] + [System.Security.SecurityCritical] + public static extern void DwmExtendFrameIntoClientArea(IntPtr hWnd, ref Margins pMarInset); + + //[DllImport(DWMAPI, ExactSpelling = true, PreserveSig = false)] + //public static extern void DwmGetColorizationColor(out uint ColorizationColor, [MarshalAs(UnmanagedType.Bool)]out bool ColorizationOpaqueBlend); + + [DllImport(DWMAPI, ExactSpelling = true, PreserveSig = false)] + [System.Security.SecurityCritical] + public static extern void DwmGetWindowAttribute(IntPtr hwnd, DWMWINDOWATTRIBUTE dwAttribute, IntPtr pvAttribute, int cbAttribute); + + [DllImport(DWMAPI, ExactSpelling = true, PreserveSig = false)] + [System.Security.SecurityCritical] + public static extern void DwmIsCompositionEnabled(ref int pfEnabled); + + [DllImport(DWMAPI, ExactSpelling = true, PreserveSig = false)] + [System.Security.SecurityCritical] + public static extern void DwmSetWindowAttribute(IntPtr hwnd, DWMWINDOWATTRIBUTE dwAttribute, [In] IntPtr pvAttribute, int cbAttribute); + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/Native/DesktopWindowManager.cs b/AeroWizard/AeroWizard/Native/DesktopWindowManager.cs new file mode 100644 index 0000000..360dca5 --- /dev/null +++ b/AeroWizard/AeroWizard/Native/DesktopWindowManager.cs @@ -0,0 +1,409 @@ +using System; +using System.ComponentModel; +using System.Drawing; +using System.Windows.Forms; + +namespace Vanara.Interop.DesktopWindowManager +{ + /// Main DWM class, provides glass sheet effect and blur behind. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1724:TypeNamesShouldNotMatchNamespaces")] + [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")] + [System.Security.SecuritySafeCritical] + public static class DesktopWindowManager + { + static object ColorizationColorChangedKey = new object(); + static object CompositionChangedKey = new object(); + static EventHandlerList eventHandlerList; + static object NonClientRenderingChangedKey = new object(); + //static object WindowMaximizedChangedKey = new object(); + static object[] keys = new object[] { CompositionChangedKey, NonClientRenderingChangedKey, ColorizationColorChangedKey/*, WindowMaximizedChangedKey*/ }; + static object _lock = new object(); + static MessageWindow _window; + + /// + /// Occurs when the colorization color has changed. + /// + public static event EventHandler ColorizationColorChanged + { + add { AddEventHandler(ColorizationColorChangedKey, value); } + remove { RemoveEventHandler(ColorizationColorChangedKey, value); } + } + + /// + /// Occurs when the desktop window composition has been enabled or disabled. + /// + public static event EventHandler CompositionChanged + { + add { AddEventHandler(CompositionChangedKey, value); } + remove { RemoveEventHandler(CompositionChangedKey, value); } + } + + /// + /// Occurs when the non-client area rendering policy has changed. + /// + public static event EventHandler NonClientRenderingChanged + { + add { AddEventHandler(NonClientRenderingChangedKey, value); } + remove { RemoveEventHandler(NonClientRenderingChangedKey, value); } + } + + /// + /// Gets or sets the current color used for Desktop Window Manager (DWM) glass composition. This value is based on the current color scheme and can be modified by the user. + /// + /// The color of the glass composition. + public static Color CompositionColor + { + get + { + if (!CompositionSupported) + return Color.Transparent; + int value = (int)Microsoft.Win32.Registry.CurrentUser.GetValue(@"Software\Microsoft\Windows\DWM\ColorizationColor", 0); + return Color.FromArgb(value); + } + set + { + if (!CompositionSupported) + return; + NativeMethods.ColorizationParams p = new NativeMethods.ColorizationParams(); + NativeMethods.DwmGetColorizationParameters(ref p); + p.Color1 = (uint)value.ToArgb(); + NativeMethods.DwmSetColorizationParameters(ref p, 1); + Microsoft.Win32.Registry.CurrentUser.SetValue(@"Software\Microsoft\Windows\DWM\ColorizationColor", value.ToArgb(), Microsoft.Win32.RegistryValueKind.DWord); + } + } + + /// + /// Gets or sets a value indicating whether composition (Windows Aero) is enabled. + /// + /// true if composition is enabled; otherwise, false. + public static bool CompositionEnabled + { + get { return IsCompositionEnabled(); } + set { if (CompositionSupported) EnableComposition(value); } + } + + /// + /// Gets or sets a value indicating whether composition (Windows Aero) is supported. + /// + /// true if composition is supported; otherwise, false. + public static bool CompositionSupported => System.Environment.OSVersion.Version.Major >= 6; + + /// + /// Gets or sets a value that indicates whether the is transparent. + /// + /// true if transparent; otherwise, false. + public static bool TransparencyEnabled + { + get + { + if (!CompositionSupported) + return false; + int value = (int)Microsoft.Win32.Registry.CurrentUser.GetValue(@"Software\Microsoft\Windows\DWM\ColorizationOpaqueBlend", 1); + return value == 0; + } + set + { + if (!CompositionSupported) + return; + NativeMethods.ColorizationParams p = new NativeMethods.ColorizationParams(); + NativeMethods.DwmGetColorizationParameters(ref p); + p.Opaque = value ? 0u : 1u; + NativeMethods.DwmSetColorizationParameters(ref p, 1); + Microsoft.Win32.Registry.CurrentUser.SetValue(@"Software\Microsoft\Windows\DWM\ColorizationOpaqueBlend", p.Opaque, Microsoft.Win32.RegistryValueKind.DWord); + } + } + + /*/// + /// Occurs when a Desktop Window Manager (DWM) composed window is maximized. + /// + public static event EventHandler WindowMaximizedChanged + { + add { AddEventHandler(WindowMaximizedChangedKey, value); } + remove { RemoveEventHandler(WindowMaximizedChangedKey, value); } + }*/ + + /// + /// Enable the Aero "Blur Behind" effect on the whole client area. Background must be black. + /// + /// The window. + /// true to enable blur behind for this window, false to disable it. + public static void EnableBlurBehind(this IWin32Window window, bool enabled) + { + EnableBlurBehind(window, null, null, enabled, false); + } + + /// + /// Enable the Aero "Blur Behind" effect on a specific region of a drawing area. Background must be black. + /// + /// The window. + /// The graphics area on which the region resides. + /// The region within the client area to apply the blur behind. + /// true to enable blur behind for this region, false to disable it. + /// true if the window's colorization should transition to match the maximized windows; otherwise, false. + public static void EnableBlurBehind(this IWin32Window window, System.Drawing.Graphics graphics, System.Drawing.Region region, bool enabled, bool transitionOnMaximized) + { + if (window == null) + throw new ArgumentNullException(nameof(window)); + NativeMethods.BlurBehind bb = new NativeMethods.BlurBehind(enabled); + if (graphics != null && region != null) + bb.SetRegion(graphics, region); + if (transitionOnMaximized) + bb.TransitionOnMaximized = true; + NativeMethods.DwmEnableBlurBehindWindow(window.Handle, ref bb); + } + + /// + /// Enables or disables Desktop Window Manager (DWM) composition. + /// + /// true to enable DWM composition; false to disable composition. + public static void EnableComposition(bool value) + { + NativeMethods.DwmEnableComposition(value ? 1 : 0); + } + + /// + /// Excludes the specified child control from the glass effect. + /// + /// The parent control. + /// The control to exclude. + /// Occurs if control is null. + /// Occurs if control is not a child control. + public static void ExcludeChildFromGlass(this Control parent, Control control) + { + if (parent == null) + throw new ArgumentNullException(nameof(parent)); + if (control == null) + throw new ArgumentNullException(nameof(control)); + if (!parent.Contains(control)) + throw new ArgumentException("Control must be a child control."); + + if (IsCompositionEnabled()) + { + System.Drawing.Rectangle clientScreen = parent.RectangleToScreen(parent.ClientRectangle); + System.Drawing.Rectangle controlScreen = control.RectangleToScreen(control.ClientRectangle); + + NativeMethods.Margins margins = new NativeMethods.Margins(controlScreen.Left - clientScreen.Left, controlScreen.Top - clientScreen.Top, + clientScreen.Right - controlScreen.Right, clientScreen.Bottom - controlScreen.Bottom); + + // Extend the Frame into client area + NativeMethods.DwmExtendFrameIntoClientArea(parent.Handle, ref margins); + } + } + + /// + /// Extends the window frame beyond the client area. + /// + /// The window. + /// The padding to use as the area into which the frame is extended. + public static void ExtendFrameIntoClientArea(this IWin32Window window, Padding padding) + { + if (window == null) + throw new ArgumentNullException(nameof(window)); + NativeMethods.Margins m = new NativeMethods.Margins(padding); + NativeMethods.DwmExtendFrameIntoClientArea(window.Handle, ref m); + } + + /// + /// Flags used by the SetWindowAttr method to specify the non-client area rendering policy. + /// + public enum NonClientRenderingPolicy + { + /// The non-client rendering area is rendered based on the window style. + UseWindowStyle, + /// The non-client area rendering is disabled; the window style is ignored. + Disabled, + /// The non-client area rendering is enabled; the window style is ignored. + Enabled + } + + /// + /// Flags used by the SetWindowAttr method to specify the Flip3D window policy. + /// + public enum Flip3DWindowPolicy + { + /// Use the window's style and visibility settings to determine whether to hide or include the window in Flip3D rendering. + Default, + /// Exclude the window from Flip3D and display it below the Flip3D rendering. + ExcludeBelow, + /// Exclude the window from Flip3D and display it above the Flip3D rendering. + ExcludeAbove + } + + /// + /// Use with GetWindowAttr and WindowAttribute.Cloaked. If the window is cloaked, provides one of the following values explaining why. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1714:FlagsEnumsShouldHavePluralNames")] + [Flags] + public enum CloakingSource + { + /// The window was cloaked by its owner application. + App = 0x01, + /// The window was cloaked by the Shell. + Shell = 0x02, + /// The cloak value was inherited from its owner window. + Inherited = 0x04 + } + + /// + /// Window attribute to get through the methods. + /// + public enum GetWindowAttr + { + /// No attribute. + None = 0, + /// Gets whether non-client rendering is enabled. The retrieved value is of type bool. True if non-client rendering is enabled; otherwise, False. + NonClientRenderingEnabled = 1, + /// Gets the bounds of the caption button area in the window-relative space. The retrieved value is of type RECT. + CaptionButtonBounds = 5, + /// Gets the extended frame bounds rectangle in screen space. The retrieved value is of type RECT. + ExtendedFrameBounds = 9, + /// Win8+. Get CloakingSource flags explaining why a window is cloaked. + Cloaked = 14 + } + + /// + /// Window attribute to set through the methods. + /// + public enum SetWindowAttr + { + /// No attribute. + None = 0, + /// Sets the non-client rendering policy. The retrieved value is from the NonClientRenderingPolicy enumeration. + NonClientRenderingPolicy = 2, + /// Enables or forcibly disables DWM transitions. The retrieved value is of type bool. True to disable transitions or False to enable transitions. + TransitionsForceDisabled = 3, + /// Enables content rendered in the non-client area to be visible on the frame drawn by DWM. The retrieved value is of type bool. True to enable content rendered in the non-client area to be visible on the frame; otherwise, False. + AllowNonClientPaint = 4, + /// Sets whether non-client content is right-to-left (RTL) mirrored. The retrieved value is of type bool. True if the non-client content is right-to-left (RTL) mirrored; otherwise False. + NonClientRtlLayout = 6, + /// Forces the window to display an iconic thumbnail or peek representation (a static bitmap), even if a live or snapshot representation of the window is available. This value normally is set during a window's creation and not changed throughout the window's lifetime. Some scenarios, however, might require the value to change over time. The retrieved value is of type bool. True to require a iconic thumbnail or peek representation; otherwise, False. + ForceIconicRepresentation = 7, + /// Sets how Flip3D treats the window. The pvAttribute parameter points to a value from the Flip3DWindowPolicy enumeration. + Flip3DPolicy = 8, + /// Win7+. Sets if the window will provide a bitmap for use by DWM as an iconic thumbnail or peek representation (a static bitmap) for the window. HasIconicBitmap can be specified with ForceIconicRepresentation. HasIconicBitmap normally is set during a window's creation and not changed throughout the window's lifetime. Some scenarios, however, might require the value to change over time. The retrieved value is of type bool. True to inform DWM that the window will provide an iconic thumbnail or peek representation; otherwise, False. + HasIconicBitmap = 10, + /// Win7+. Set true to not show peek preview for the window. The peek view shows a full-sized preview of the window when the mouse hovers over the window's thumbnail in the taskbar. If this attribute is set, hovering the mouse pointer over the window's thumbnail dismisses peek (in case another window in the group has a peek preview showing). The retrieved value is of type bool. True to prevent peek functionality or False to allow it. + DisallowPeek = 11, + /// Win7+. Sets value preventing a window from fading to a glass sheet when peek is invoked. The retrieved value is of type bool. True to prevent the window from fading during another window's peek or False for normal behavior. + ExcludedFromPeek = 12, + /// Win8+. Cloaks the window such that it is not visible to the user. The window is still composed by DWM. True to cloak or False to not cloak. + Cloak = 13, + /// Win8+. Freeze the window's thumbnail image with its current visuals. Do no further live updates on the thumbnail image to match the window's contents. + FreezeRepresentation = 15 + } + + /// + /// Gets the specified window attribute from the Desktop Window Manager (DWM). + /// + /// Return type. Must match the attribute. + /// The window. + /// The attribute. + /// Value of the windows attribute. + public static T GetWindowAttribute(this IWin32Window window, GetWindowAttr attribute) where T : struct + { + if (window == null) + throw new ArgumentNullException(nameof(window)); + using (var ptr = System.Runtime.InteropServices.SafeHGlobalHandle.AllocHGlobal()) + { + NativeMethods.DwmGetWindowAttribute(window.Handle, (NativeMethods.DWMWINDOWATTRIBUTE)attribute, ptr, ptr.Size); + return ptr.ToStructure(); + } + } + + /// + /// Sets the specified window attribute through the Desktop Window Manager (DWM). + /// + /// The window. + /// The attribute. + /// The value. + public static void SetWindowAttribute(this IWin32Window window, SetWindowAttr attribute, object value) + { + if (window == null) + throw new ArgumentNullException(nameof(window)); + if (value == null) + throw new ArgumentNullException(nameof(value)); + using (var ptr = new System.Runtime.InteropServices.SafeHGlobalHandle(value)) + NativeMethods.DwmSetWindowAttribute(window.Handle, (NativeMethods.DWMWINDOWATTRIBUTE)attribute, ptr, ptr.Size); + } + + /// + /// Indicates whether Desktop Window Manager (DWM) composition is enabled. + /// + /// true if is composition enabled; otherwise, false. + public static bool IsCompositionEnabled() + { + if (!CompositionSupported || !System.IO.File.Exists(System.IO.Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.System), NativeMethods.DWMAPI))) + return false; + int res = 0; + NativeMethods.DwmIsCompositionEnabled(ref res); + return res != 0; + } + + private static void AddEventHandler(object id, EventHandler value) + { + lock (_lock) + { + if (_window == null) + _window = new MessageWindow(); + if (eventHandlerList == null) + eventHandlerList = new EventHandlerList(); + eventHandlerList.AddHandler(id, value); + } + } + + private static void RemoveEventHandler(object id, EventHandler value) + { + lock (_lock) + { + if (eventHandlerList != null) + { + eventHandlerList.RemoveHandler(id, value); + } + } + } + + [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")] + [System.Security.SecuritySafeCritical] + private class MessageWindow : NativeWindow, IDisposable + { + const int WM_DWMCOLORIZATIONCOLORCHANGED = 0x0320; + const int WM_DWMCOMPOSITIONCHANGED = 0x031E; + const int WM_DWMNCRENDERINGCHANGED = 0x031F; + //const int WM_DWMWINDOWMAXIMIZEDCHANGE = 0x0321; + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] + public MessageWindow() + { + CreateParams cp = new CreateParams() { Style = 0, ExStyle = 0, ClassStyle = 0, Parent = IntPtr.Zero }; + cp.Caption = base.GetType().Name; + CreateHandle(cp); + } + + public void Dispose() + { + DestroyHandle(); + } + + [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")] + protected override void WndProc(ref Message m) + { + if (m.Msg >= WM_DWMCOMPOSITIONCHANGED && m.Msg <= WM_DWMCOLORIZATIONCOLORCHANGED) + ExecuteEvents(m.Msg - WM_DWMCOMPOSITIONCHANGED); + + base.WndProc(ref m); + } + + private void ExecuteEvents(int idx) + { + if (eventHandlerList != null) + { + lock (_lock) + { + try { ((EventHandler)eventHandlerList[keys[idx]]).Invoke(null, EventArgs.Empty); } + catch { }; + } + } + } + } + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/Native/GDI32.cs b/AeroWizard/AeroWizard/Native/GDI32.cs new file mode 100644 index 0000000..2cb99e5 --- /dev/null +++ b/AeroWizard/AeroWizard/Native/GDI32.cs @@ -0,0 +1,491 @@ +using System; +using System.Runtime.InteropServices; + +namespace Vanara.Interop +{ + internal static partial class NativeMethods + { + const string GDI32 = "gdi32.dll"; + + /// + /// The background mode used by the function. + /// + public enum BackgroundMode + { + /// Indicates that on return, the has failed. + ERROR = 0, + + /// Background remains untouched. + TRANSPARENT = 1, + + /// Background is filled with the current background color before the text, hatched brush, or pen is drawn. + OPAQUE = 2, + } + + /// + /// The DC layout used by the function. + /// + public enum DCLayout + { + /// Indicates that on return, the has failed. + GDI_ERROR = -1, + + /// Sets the default horizontal layout to be right to left. + LAYOUT_RTL = 1, + + /// Sets the default horizontal layout to be bottom to top. + LAYOUT_BTT = 2, + + /// Sets the default horizontal layout to be vertical before horizontal. + LAYOUT_VBH = 4, + + /// Disables any reflection during BitBlt and StretchBlt operations. + LAYOUT_BITMAPORIENTATIONPRESERVED = 8, + } + + /// + /// Defines how the color data for the source rectangle is to be combined with the color data for the destination rectangle to achieve the final color when using the function. + /// + public enum RasterOperationMode + { + /// Copies the source rectangle directly to the destination rectangle. + SRCCOPY = 0x00CC0020, + + /// Combines the colors of the source and destination rectangles by using the Boolean OR operator. + SRCPAINT = 0x00EE0086, + + /// Combines the colors of the source and destination rectangles by using the Boolean AND operator. + SRCAND = 0x008800C6, + + /// Combines the colors of the source and destination rectangles by using the Boolean XOR operator. + SRCINVERT = 0x00660046, + + /// Combines the inverted colors of the destination rectangle with the colors of the source rectangle by using the Boolean AND operator. + SRCERASE = 0x00440328, + + /// + NOTSRCCOPY = 0x00330008, + + /// Copies the inverted source rectangle to the destination. + NOTSRCERASE = 0x001100A6, + + /// Merges the colors of the source rectangle with the brush currently selected in hdcDest, by using the Boolean AND operator. + MERGECOPY = 0x00C000CA, + + /// Merges the colors of the inverted source rectangle with the colors of the destination rectangle by using the Boolean OR operator. + MERGEPAINT = 0x00BB0226, + + /// Copies the brush currently selected in hdcDest, into the destination bitmap. + PATCOPY = 0x00F00021, + + /// Combines the colors of the brush currently selected in hdcDest, with the colors of the inverted source rectangle by using the Boolean OR operator. The result of this operation is combined with the colors of the destination rectangle by using the Boolean OR operator. + PATPAINT = 0x00FB0A09, + + /// Combines the colors of the brush currently selected in hdcDest, with the colors of the destination rectangle by using the Boolean XOR operator. + PATINVERT = 0x005A0049, + + /// Inverts the destination rectangle. + DSTINVERT = 0x00550009, + + /// Fills the destination rectangle using the color associated with index 0 in the physical palette. (This color is black for the default physical palette.) + BLACKNESS = 0x00000042, + + /// Fills the destination rectangle using the color associated with index 1 in the physical palette. (This color is white for the default physical palette.) + WHITENESS = 0x00FF0062, + + /// Prevents the bitmap from being mirrored. + NOMIRRORBITMAP = -2147483648, + + /// Includes any windows that are layered on top of your window in the resulting image.By default, the image only contains your window.Note that this generally cannot be used for printing device contexts. + CAPTUREBLT = 0x40000000 + } + + /// The AlphaBlend function displays bitmaps that have transparent or semitransparent pixels. + /// A handle to the destination device context. + /// The x-coordinate, in logical units, of the upper-left corner of the destination rectangle. + /// The y-coordinate, in logical units, of the upper-left corner of the destination rectangle. + /// The width, in logical units, of the destination rectangle. + /// The height, in logical units, of the destination rectangle. + /// A handle to the source device context. + /// The x-coordinate, in logical units, of the upper-left corner of the source rectangle. + /// The y-coordinate, in logical units, of the upper-left corner of the source rectangle. + /// The width, in logical units, of the source rectangle. + /// The height, in logical units, of the source rectangle. + /// + /// The alpha-blending function for source and destination bitmaps, a global alpha value to be applied to the entire source bitmap, and format + /// information for the source bitmap. The source and destination blend functions are currently limited to AC_SRC_OVER. + /// + /// If the function succeeds, the return value is TRUE. If the function fails, the return value is FALSE. + /// + /// If the source rectangle and destination rectangle are not the same size, the source bitmap is stretched to match the destination rectangle. If the + /// SetStretchBltMode function is used, the iStretchMode value is automatically converted to COLORONCOLOR for this function (that is, BLACKONWHITE, + /// WHITEONBLACK, and HALFTONE are changed to COLORONCOLOR). + /// + /// The destination coordinates are transformed by using the transformation currently specified for the destination device context. The source + /// coordinates are transformed by using the transformation currently specified for the source device context. + /// + /// An error occurs (and the function returns FALSE) if the source device context identifies an enhanced metafile device context. + /// If destination and source bitmaps do not have the same color format, AlphaBlend converts the source bitmap to match the destination bitmap. + /// AlphaBlend does not support mirroring. If either the width or height of the source or destination is negative, this call will fail. + /// + /// When rendering to a printer, first call GetDeviceCaps with SHADEBLENDCAPS to determine if the printer supports blending with AlphaBlend. Note that, + /// for a display DC, all blending operations are supported and these flags represent whether the operations are accelerated. + /// + /// + /// If the source and destination are the same surface that is, they are both the screen or the same memory bitmap and the source and destination + /// rectangles overlap, an error occurs and the function returns FALSE. + /// + /// The source rectangle must lie completely within the source surface, otherwise an error occurs and the function returns FALSE. + /// AlphaBlend fails if the width or height of the source or destination is negative. + /// + /// The SourceConstantAlpha member of BLENDFUNCTION specifies an alpha transparency value to be used on the entire source bitmap. The SourceConstantAlpha + /// value is combined with any per-pixel alpha values. If SourceConstantAlpha is 0, it is assumed that the image is transparent. Set the + /// SourceConstantAlpha value to 255 (which indicates that the image is opaque) when you only want to use per-pixel alpha values. + /// + /// + [DllImport(GDI32, SetLastError = true, EntryPoint = "GdiAlphaBlend")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool AlphaBlend(SafeDCHandle hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest, SafeDCHandle hdcSrc, int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc, BLENDFUNCTION blendFunction); + + /// + /// The BitBlt function performs a bit-block transfer of the color data corresponding to a rectangle of pixels from the specified source device context + /// into a destination device context. + /// + /// A handle to the destination device context. + /// The x-coordinate, in logical units, of the upper-left corner of the destination rectangle. + /// The y-coordinate, in logical units, of the upper-left corner of the destination rectangle. + /// The width, in logical units, of the destination rectangle. + /// The height, in logical units, of the destination rectangle. + /// A handle to the source device context. + /// The x-coordinate, in logical units, of the upper-left corner of the source rectangle. + /// The y-coordinate, in logical units, of the upper-left corner of the source rectangle. + /// + /// A raster-operation code. These codes define how the color data for the source rectangle is to be combined with the color data for the destination + /// rectangle to achieve the final color. + /// + /// + /// If the function succeeds, the return value is nonzero. + /// If the function fails, the return value is zero. To get extended error information, call GetLastError. + /// + /// + /// BitBlt only does clipping on the destination DC. + /// + /// If a rotation or shear transformation is in effect in the source device context, BitBlt returns an error. If other transformations exist in the + /// source device context (and a matching transformation is not in effect in the destination device context), the rectangle in the destination device + /// context is stretched, compressed, or rotated, as necessary. + /// + /// + /// If the color formats of the source and destination device contexts do not match, the BitBlt function converts the source color format to match the + /// destination format. + /// + /// When an enhanced metafile is being recorded, an error occurs if the source device context identifies an enhanced-metafile device context. + /// + /// Not all devices support the BitBlt function. For more information, see the RC_BITBLT raster capability entry in the GetDeviceCaps function as well as + /// the following functions: MaskBlt, PlgBlt, and StretchBlt. + /// + /// + /// BitBlt returns an error if the source and destination device contexts represent different devices. To transfer data between DCs for different + /// devices, convert the memory bitmap to a DIB by calling GetDIBits. To display the DIB to the second device, call SetDIBits or StretchDIBits. + /// + /// ICM: No color management is performed when blits occur. + /// + [DllImport(GDI32, ExactSpelling = true, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool BitBlt(SafeDCHandle hdc, int nXDest, int nYDest, int nWidth, int nHeight, SafeDCHandle hdcSrc, int nXSrc, int nYSrc, RasterOperationMode dwRop); + + /// The CreateCompatibleDC function creates a memory device context (DC) compatible with the specified device. + /// + /// A handle to an existing DC. If this handle is NULL, the function creates a memory DC compatible with the application's current screen. + /// + /// + /// If the function succeeds, the return value is the handle to a memory DC. + /// If the function fails, the return value is NULL. + /// + /// + /// A memory DC exists only in memory. When the memory DC is created, its display surface is exactly one monochrome pixel wide and one monochrome pixel + /// high. Before an application can use a memory DC for drawing operations, it must select a bitmap of the correct width and height into the DC. To + /// select a bitmap into a DC, use the CreateCompatibleBitmap function, specifying the height, width, and color organization required. + /// + /// When a memory DC is created, all attributes are set to normal default values. The memory DC can be used as a normal DC. You can set the attributes; + /// obtain the current settings of its attributes; and select pens, brushes, and regions. + /// + /// + /// The CreateCompatibleDC function can only be used with devices that support raster operations. An application can determine whether a device supports + /// these operations by calling the GetDeviceCaps function. + /// + /// + /// When you no longer need the memory DC, call the DeleteDC function. We recommend that you call DeleteDC to delete the DC. However, you can also call + /// DeleteObject with the HDC to delete the DC. + /// + /// + /// If hdc is NULL, the thread that calls CreateCompatibleDC owns the HDC that is created. When this thread is destroyed, the HDC is no longer valid. + /// Thus, if you create the HDC and pass it to another thread, then exit the first thread, the second thread will not be able to use the HDC. + /// + /// + /// ICM: If the DC that is passed to this function is enabled for Image Color Management (ICM), the DC created by the function is ICM-enabled. The source + /// and destination color spaces are specified in the DC. + /// + /// + [DllImport(GDI32, ExactSpelling = true, SetLastError = true)] + public static extern IntPtr CreateCompatibleDC(IntPtr hDC); + + /// The DeleteDC function deletes the specified device context (DC). + /// A handle to the device context. + /// If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. + /// + /// An application must not delete a DC whose handle was obtained by calling the GetDC function. Instead, it must call the ReleaseDC function to free the DC. + /// + [DllImport(GDI32, ExactSpelling = true, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + [System.Security.SecurityCritical] + public static extern bool DeleteDC(IntPtr hdc); + + /// + /// The DeleteObject function deletes a logical pen, brush, font, bitmap, region, or palette, freeing all system resources associated with the object. + /// After the object is deleted, the specified handle is no longer valid. + /// + /// A handle to a logical pen, brush, font, bitmap, region, or palette. + /// + /// If the function succeeds, the return value is nonzero. If the specified handle is not valid or is currently selected into a DC, the return value is zero. + /// + /// + /// Do not delete a drawing object (pen or brush) while it is still selected into a DC. + /// When a pattern brush is deleted, the bitmap associated with the brush is not deleted. The bitmap must be deleted independently. + /// + [DllImport(GDI32, ExactSpelling = true, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool DeleteObject(IntPtr hObject); + + /// The GdiFlush function flushes the calling thread's current batch. + /// + /// If all functions in the current batch succeed, the return value is nonzero. + /// If not all functions in the current batch succeed, the return value is zero, indicating that at least one function returned an error. + /// + [DllImport(GDI32, ExactSpelling = true, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + [System.Security.SecurityCritical] + public static extern bool GdiFlush(); + + /// The GetObject function retrieves information for the specified graphics object. + /// + /// A handle to the graphics object of interest. This can be a handle to one of the following: a logical bitmap, + /// a brush, a font, a palette, a pen, or a device independent bitmap created by calling the CreateDIBSection function. + /// + /// The number of bytes of information to be written to the buffer. + /// + /// A pointer to a buffer that receives the information about the specified graphics object. If the parameter is NULL, the function return value is the number of bytes required to store the + /// information it writes to the buffer for the specified graphics object. + /// + /// + /// If the function succeeds, and is a valid pointer, the return value is the number + /// of bytes stored into the buffer. + /// + /// If the function succeeds, and is NULL, the return value is the number of bytes + /// required to hold the information the function would store into the buffer. + /// + /// If the function fails, the return value is zero. + /// + /// + /// The buffer pointed to by the parameter must be sufficiently large to receive the + /// information about the graphics object. Depending on the graphics object, the function uses a BITMAP, + /// DIBSECTION, EXTLOGPEN, LOGBRUSH, LOGFONT, or LOGPEN structure, or a count of table entries (for a logical palette). + /// + /// If is a handle to a bitmap created by calling CreateDIBSection, and the specified + /// buffer is large enough, the GetObject function returns a DIBSECTION structure. In addition, the bmBits member + /// of the BITMAP structure contained within the DIBSECTION will contain a pointer to the bitmap's bit values. + /// + /// + /// If is a handle to a bitmap created by any other means, GetObject returns only the + /// width, height, and color format information of the bitmap. You can obtain the bitmap's bit values by calling + /// the GetDIBits or GetBitmapBits function. + /// + /// + /// If is a handle to a logical palette, GetObject retrieves a 2-byte integer that + /// specifies the number of entries in the palette. The function does not retrieve the LOGPALETTE structure + /// defining the palette. To retrieve information about palette entries, an application can call the + /// GetPaletteEntries function. + /// + /// + /// If is a handle to a font, the LOGFONT that is returned is the LOGFONT used to + /// create the font. If Windows had to make some interpolation of the font because the precise LOGFONT could not + /// be represented, the interpolation will not be reflected in the LOGFONT. For example, if you ask for a + /// vertical version of a font that doesn't support vertical painting, the LOGFONT indicates the font is + /// vertical, but Windows will paint it horizontally. + /// + /// + [DllImport(GDI32, ExactSpelling = true, SetLastError = true)] + public static extern int GetObject(IntPtr hgdiobj, int cbBuffer, IntPtr lpvObject); + + /// The GetObject function retrieves information for the specified graphics object. + /// The output structure type. + /// + /// A handle to the graphics object of interest. This can be a handle to one of the following: a logical bitmap, a brush, a font, a palette, a pen, or a + /// device independent bitmap created by calling the CreateDIBSection function. + /// + /// The output structure holding the information for the graphics object. + public static T GetObject(IntPtr hgdiobj) where T : struct + { + var result = default(T); + var hGC = GCHandle.Alloc(result, GCHandleType.Pinned); + try + { + var ptr = hGC.AddrOfPinnedObject(); + var ret = GetObject(hgdiobj, Marshal.SizeOf(typeof(T)), ptr); + if (ret == 0 || ptr == IntPtr.Zero) + throw new System.ComponentModel.Win32Exception(); + + return (T)Marshal.PtrToStructure(ptr, typeof(T)); + } + finally + { + if (hGC.IsAllocated) + hGC.Free(); + } + } + + /// + /// The SelectObject function selects an object into the specified device context (DC). The new object replaces + /// the previous object of the same type. + /// + /// A handle to the DC. + /// + /// A handle to the object to be selected. The specified object must have been created by using one of the + /// following functions. + /// + /// + /// If the selected object is not a region and the function succeeds, the return value is a handle to the object + /// being replaced. If the selected object is a region and the function succeeds, the return value is one of the + /// following values. + /// + [DllImport(GDI32, ExactSpelling = true, SetLastError = true)] + public static extern IntPtr SelectObject(SafeDCHandle hDC, IntPtr hObject); + + /// + /// The SetBkMode function sets the background mix mode of the specified device context. The background mix mode + /// is used with text, hatched brushes, and pen styles that are not solid lines. + /// + /// A handle to the device context. + /// The background mode. + /// + /// If the function succeeds, the return value specifies the previous background mode. If the function fails, the + /// return value is zero. + /// + [DllImport(GDI32, ExactSpelling = true, SetLastError = true)] + public static extern BackgroundMode SetBkMode(SafeDCHandle hdc, BackgroundMode mode); + + /// The SetLayout function changes the layout of a device context (DC). + /// A handle to the DC. + /// The DC layout. + /// + /// If the function succeeds, it returns the previous layout of the DC. If the function fails, it returns GDI_ERROR. + /// + /// + /// The layout specifies the order in which text and graphics are revealed in a window or a device context. The + /// default is left to right. The SetLayout function changes this to be right to left, which is the standard in + /// Arabic and Hebrew cultures. + /// + [DllImport(GDI32, ExactSpelling = true, SetLastError = true)] + public static extern DCLayout SetLayout(SafeDCHandle hdc, DCLayout dwLayout); + + /// + /// The TransparentBlt function performs a bit-block transfer of the color data corresponding to a rectangle of + /// pixels from the specified source device context into a destination device context. + /// + /// A handle to the destination device context. + /// + /// The x-coordinate, in logical units, of the upper-left corner of the destination rectangle. + /// + /// + /// The y-coordinate, in logical units, of the upper-left corner of the destination rectangle. + /// + /// The width, in logical units, of the destination rectangle. + /// The height, in logical units, of the destination rectangle. + /// A handle to the source device context. + /// The x-coordinate, in logical units, of the upper-left corner of the source rectangle. + /// The y-coordinate, in logical units, of the upper-left corner of the source rectangle. + /// The width, in logical units, of the source rectangle. + /// The height, in logical units, of the source rectangle. + /// The RGB color in the source bitmap to treat as transparent. + /// + /// If the function succeeds, the return value is TRUE. If the function fails, the return value is FALSE. + /// + /// + /// The TransparentBlt function works with compatible bitmaps (DDBs). + /// + /// The TransparentBlt function supports all formats of source bitmaps. However, for 32 bpp bitmaps, it just + /// copies the alpha value over. Use AlphaBlend to specify 32 bits-per-pixel bitmaps with transparency. + /// + /// + /// If the source and destination rectangles are not the same size, the source bitmap is stretched to match the + /// destination rectangle. When the SetStretchBltMode function is used, the iStretchMode modes of BLACKONWHITE + /// and WHITEONBLACK are converted to COLORONCOLOR for the TransparentBlt function. + /// + /// + /// The destination device context specifies the transformation type for the destination coordinates. The source + /// device context specifies the transformation type for the source coordinates. + /// + /// + /// TransparentBlt does not mirror a bitmap if either the width or height, of either the source or destination, + /// is negative. + /// + /// + /// When used in a multiple monitor system, both hdcSrc and hdcDest must refer to the same device or the function + /// will fail. To transfer data between DCs for different devices, convert the memory bitmap to a DIB by calling + /// GetDIBits. To display the DIB to the second device, call SetDIBits or StretchDIBits. + /// + /// + [DllImport(GDI32, SetLastError = true, EntryPoint = "GdiTransparentBlt")] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool TransparentBlt(SafeDCHandle hdcDest, int xOriginDest, int yOriginDest, int wDest, int hDest, SafeDCHandle hdcSrc, int xOriginSrc, int yOriginSrc, int wSrc, int hSrc, int crTransparent); + + /// + /// The BLENDFUNCTION structure controls blending by specifying the blending functions for source and destination bitmaps. + /// + /// See information on how this function determines the resulting values on MSDN. + [StructLayout(LayoutKind.Sequential)] + public struct BLENDFUNCTION + { + /// + /// The source blend operation. Currently, the only source and destination blend operation that has been defined is AC_SRC_OVER. For details, see the + /// following Remarks section. + /// + public byte BlendOp; + + /// Must be zero. + public byte BlendFlags; + + /// + /// Specifies an alpha transparency value to be used on the entire source bitmap. The SourceConstantAlpha value is combined with any per-pixel alpha + /// values in the source bitmap. If you set SourceConstantAlpha to 0, it is assumed that your image is transparent. Set the SourceConstantAlpha value + /// to 255 (opaque) when you only want to use per-pixel alpha values. + /// + public byte SourceConstantAlpha; + + /// + /// This member controls the way the source and destination bitmaps are interpreted. AlphaFormat has the following value. + /// + /// AC_SRC_ALPHA This flag is set when the bitmap has an Alpha channel (that is, per-pixel alpha). Note that the APIs use premultiplied alpha, + /// which means that the red, green and blue channel values in the bitmap must be premultiplied with the alpha channel value. For example, if the + /// alpha channel value is x, the red, green and blue channels must be multiplied by x and divided by 0xff prior to the call. + /// + /// + public byte AlphaFormat; + + /// Initializes a new instance of the struct and sets the alpha value. + /// The alpha. + public BLENDFUNCTION(byte alpha) + { + // AC_SRC_OVER is the only possible value for BlendOp and it equals 0 + this.BlendOp = 0; + this.BlendFlags = 0; + this.SourceConstantAlpha = alpha; + + // AC_SRC_ALPHA is the only possible value for AlphaFormat and it equals 1 + this.AlphaFormat = 1; + } + + public bool IsEmpty => BlendOp == 0 && BlendFlags == 0 && AlphaFormat == 0 && SourceConstantAlpha == 0; + } + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/Native/InteropUtil.cs b/AeroWizard/AeroWizard/Native/InteropUtil.cs new file mode 100644 index 0000000..1fbdab7 --- /dev/null +++ b/AeroWizard/AeroWizard/Native/InteropUtil.cs @@ -0,0 +1,138 @@ + +using System.Collections.Generic; + +namespace System.Runtime.InteropServices +{ + internal static class InteropUtil + { + internal const int cbBuffer = 256; + + [Security.Permissions.SecurityPermission(Security.Permissions.SecurityAction.LinkDemand, Flags = Security.Permissions.SecurityPermissionFlag.UnmanagedCode)] + public static T ToStructure(this IntPtr ptr) => (T)Marshal.PtrToStructure(ptr, typeof(T)); + + public static IntPtr StructureToPtr(this T value) where T : struct + { + var ret = Marshal.AllocHGlobal(Marshal.SizeOf(value)); + Marshal.StructureToPtr(value, ret, false); + return ret; + } + + [Security.Permissions.SecurityPermission(Security.Permissions.SecurityAction.LinkDemand, Flags = Security.Permissions.SecurityPermissionFlag.UnmanagedCode)] + public static void AllocString(ref IntPtr ptr, ref uint size) + { + FreeString(ref ptr, ref size); + if (size == 0) size = cbBuffer; + ptr = Marshal.AllocHGlobal(cbBuffer); + } + + [Security.Permissions.SecurityPermission(Security.Permissions.SecurityAction.LinkDemand, Flags = Security.Permissions.SecurityPermissionFlag.UnmanagedCode)] + public static void FreeString(ref IntPtr ptr, ref uint size) + { + if (ptr == IntPtr.Zero) return; + Marshal.FreeHGlobal(ptr); + ptr = IntPtr.Zero; + size = 0; + } + + [Security.Permissions.SecurityPermission(Security.Permissions.SecurityAction.LinkDemand, Flags = Security.Permissions.SecurityPermissionFlag.UnmanagedCode)] + public static string GetString(IntPtr pString) => Marshal.PtrToStringUni(pString); + + [Security.Permissions.SecurityPermission(Security.Permissions.SecurityAction.LinkDemand, Flags = Security.Permissions.SecurityPermissionFlag.UnmanagedCode)] + public static bool SetString(ref IntPtr ptr, ref uint size, string value = null) + { + var s = GetString(ptr); + if (value == string.Empty) value = null; + if (string.CompareOrdinal(s, value) == 0) return false; + FreeString(ref ptr, ref size); + if (value == null) return true; + ptr = Marshal.StringToHGlobalUni(value); + size = (uint)value.Length + 1; + return true; + } + + /// + /// Converts an that points to a C-style array into a CLI array. + /// + /// Type of native structure used by the C-style array. + /// Output type for the CLI array. must be able to convert to . + /// The pointing to the native array. + /// The number of items in the native array. + /// Bytes to skip before reading the array. + /// An array of type containing the converted elements of the native array. + public static T[] ToArray(this IntPtr ptr, int count, int prefixBytes = 0) where TS : IConvertible + { + var ret = new T[count]; + var stSize = Marshal.SizeOf(typeof(TS)); + for (var i = 0; i < count; i++) + { + var val = ToStructure(Marshal.ReadIntPtr(ptr, prefixBytes + i * stSize)); + ret[i] = (T)Convert.ChangeType(val, typeof(T)); + } + return ret; + } + + /// + /// Converts an that points to a C-style array into a CLI array. + /// + /// Type of native structure used by the C-style array. + /// The pointing to the native array. + /// The number of items in the native array. + /// Bytes to skip before reading the array. + /// An array of type containing the elements of the native array. + public static T[] ToArray(this IntPtr ptr, int count, int prefixBytes = 0) + { + var ret = new T[count]; + var stSize = Marshal.SizeOf(typeof(T)); + for (var i = 0; i < count; i++) + ret[i] = ToStructure(Marshal.ReadIntPtr(ptr, prefixBytes + i * stSize)); + return ret; + } + + /// + /// Converts an that points to a C-style array into an . + /// + /// Type of native structure used by the C-style array. + /// The pointing to the native array. + /// The number of items in the native array. + /// Bytes to skip before reading the array. + /// An exposing the elements of the native array. + public static IEnumerable ToIEnum(this IntPtr ptr, int count, int prefixBytes = 0) + { + if (count == 0) yield break; + var stSize = Marshal.SizeOf(typeof(T)); + for (var i = 0; i < count; i++) + yield return ToStructure(Marshal.ReadIntPtr(ptr, prefixBytes + i * stSize)); + } + + /// + /// Converts an to a structure. If pointer has no value, null is returned. + /// + /// Type of the structure. + /// The that points to allocated memory holding a structure or . + /// The converted structure or null. + public static T? PtrToStructure(this IntPtr ptr) where T : struct => ptr != IntPtr.Zero ? ptr.ToStructure() : (T?)null; + + /// + /// Converts a structure or null value to an . If memory has not been allocated for the , it will be via a call to . + /// + /// Type of the structure. + /// The structure to convert. If this value is null, will be set to and memory will be released. + /// The that will point to allocated memory holding the structure or . + /// An optional predicate check to determine if the structure is non-essential and can be replaced with an empty pointer (null). + public static void StructureToPtr(T? value, ref IntPtr ptr, Predicate isEmpty = null) where T : struct + { + if (value == null || (isEmpty != null && isEmpty(value.Value))) + { + if (ptr == IntPtr.Zero) return; + Marshal.FreeHGlobal(ptr); + ptr = IntPtr.Zero; + } + else + { + if (ptr == IntPtr.Zero) + ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(T))); + Marshal.StructureToPtr(value, ptr, false); + } + } + } +} diff --git a/AeroWizard/AeroWizard/Native/LOGFONT.cs b/AeroWizard/AeroWizard/Native/LOGFONT.cs new file mode 100644 index 0000000..8029525 --- /dev/null +++ b/AeroWizard/AeroWizard/Native/LOGFONT.cs @@ -0,0 +1,236 @@ +using System.Drawing; +using System.Runtime.InteropServices; + +namespace Vanara.Interop +{ + internal static partial class NativeMethods + { + public enum LogFontCharSet : byte + { + ANSI_CHARSET = 0, + DEFAULT_CHARSET = 1, + SYMBOL_CHARSET = 2, + SHIFTJIS_CHARSET = 128, + HANGEUL_CHARSET = 129, + HANGUL_CHARSET = 129, + GB2312_CHARSET = 134, + CHINESEBIG5_CHARSET = 136, + OEM_CHARSET = 255, + JOHAB_CHARSET = 130, + HEBREW_CHARSET = 177, + ARABIC_CHARSET = 178, + GREEK_CHARSET = 161, + TURKISH_CHARSET = 162, + VIETNAMESE_CHARSET = 163, + THAI_CHARSET = 222, + EASTEUROPE_CHARSET = 238, + RUSSIAN_CHARSET = 204, + MAC_CHARSET = 77, + BALTIC_CHARSET = 186 + } + + public enum LogFontClippingPrecision : byte + { + CLIP_DEFAULT_PRECIS = 0, + CLIP_CHARACTER_PRECIS = 1, + CLIP_STROKE_PRECIS = 2, + CLIP_MASK = 0xf, + CLIP_LH_ANGLES = 1 << 4, + CLIP_TT_ALWAYS = 2 << 4, + CLIP_DFA_DISABLE = 4 << 4, + CLIP_EMBEDDED = 8 << 4 + } + + public enum LogFontFontFamily : byte + { + FF_DONTCARE = 0 << 4, + FF_ROMAN = 1 << 4, + FF_SWISS = 2 << 4, + FF_MODERN = 3 << 4, + FF_SCRIPT = 4 << 4, + FF_DECORATIVE = 5 << 4, + } + + public enum LogFontOutputPrecision : byte + { + OUT_DEFAULT_PRECIS = 0, + OUT_STRING_PRECIS = 1, + OUT_CHARACTER_PRECIS = 2, + OUT_STROKE_PRECIS = 3, + OUT_TT_PRECIS = 4, + OUT_DEVICE_PRECIS = 5, + OUT_RASTER_PRECIS = 6, + OUT_TT_ONLY_PRECIS = 7, + OUT_OUTLINE_PRECIS = 8, + OUT_SCREEN_OUTLINE_PRECIS = 9, + OUT_PS_ONLY_PRECIS = 10 + } + public enum LogFontOutputQuality : byte + { + DEFAULT_QUALITY = 0, + DRAFT_QUALITY = 1, + PROOF_QUALITY = 2, + NONANTIALIASED_QUALITY = 3, + ANTIALIASED_QUALITY = 4, + CLEARTYPE_QUALITY = 5, + CLEARTYPE_NATURAL_QUALITY = 6 + } + + public enum LogFontPitch : byte + { + DEFAULT_PITCH = 0, + FIXED_PITCH = 1, + VARIABLE_PITCH = 2 + } + /// The LOGFONT structure defines the attributes of a font. + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public struct LOGFONT + { + /// + /// The height, in logical units, of the font's character cell or character. The character height value (also known as the em height) is the + /// character cell height value minus the internal-leading value. The font mapper interprets the value specified in lfHeight in the following manner. + /// + /// + /// Value + /// Meaning + /// + /// + /// > 0 + /// The font mapper transforms this value into device units and matches it against the cell height of the available fonts. + /// + /// + /// 0 + /// The font mapper uses a default height value when it searches for a match. + /// + /// + /// < 0 + /// The font mapper transforms this value into device units and matches its absolute value against the character height of the available fonts. + /// + /// + /// For all height comparisons, the font mapper looks for the largest font that does not exceed the requested size. + /// This mapping occurs when the font is used for the first time. + /// For the MM_TEXT mapping mode, you can use the following formula to specify a height for a font with a specified point size: + /// + public int lfHeight; + + /// + /// The average width, in logical units, of characters in the font. If lfWidth is zero, the aspect ratio of the device is matched against the + /// digitization aspect ratio of the available fonts to find the closest match, determined by the absolute value of the difference. + /// + public int lfWidth; + + /// + /// The angle, in tenths of degrees, between the escapement vector and the x-axis of the device. The escapement vector is parallel to the base line + /// of a row of text. + /// + /// When the graphics mode is set to GM_ADVANCED, you can specify the escapement angle of the string independently of the orientation angle of the + /// string's characters. + /// + /// + /// When the graphics mode is set to GM_COMPATIBLE, lfEscapement specifies both the escapement and orientation. You should set lfEscapement and + /// lfOrientation to the same value. + /// + /// + public int lfEscapement; + + /// The angle, in tenths of degrees, between each character's base line and the x-axis of the device. + public int lfOrientation; + + /// + /// The weight of the font in the range 0 through 1000. For example, 400 is normal and 700 is bold. If this value is zero, a default weight is used. + /// + public int lfWeight; + + /// An italic font if set to TRUE. + public byte lfItalic; + + /// An underlined font if set to TRUE. + public byte lfUnderline; + + /// A strikeout font if set to TRUE. + public byte lfStrikeOut; + + /// The character set. + public LogFontCharSet lfCharSet; + + /// + /// The output precision. The output precision defines how closely the output must match the requested font's height, width, character orientation, + /// escapement, pitch, and font type. + /// + public LogFontOutputPrecision lfOutPrecision; + + /// The clipping precision. The clipping precision defines how to clip characters that are partially outside the clipping region. + public LogFontClippingPrecision lfClipPrecision; + + /// + /// The output quality. The output quality defines how carefully the graphics device interface (GDI) must attempt to match the logical-font + /// attributes to those of an actual physical font. + /// + public LogFontOutputQuality lfQuality; + + /// The pitch and family of the font. + public byte lfPitchAndFamily; + + /// + /// A null-terminated string that specifies the typeface name of the font. The length of this string must not exceed 32 TCHAR values, including the + /// terminating NULL. The EnumFontFamiliesEx function can be used to enumerate the typeface names of all currently available fonts. If lfFaceName is + /// an empty string, GDI uses the first font that matches the other specified attributes. + /// + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] + public string lfFaceName; + + public bool Italic + { + get { return lfItalic == 1; } + set { lfItalic = System.Convert.ToByte(value); } + } + + public bool Underline + { + get { return lfUnderline == 1; } + set { lfUnderline = System.Convert.ToByte(value); } + } + + public bool StrikeOut + { + get { return lfStrikeOut == 1; } + set { lfStrikeOut = System.Convert.ToByte(value); } + } + + public LogFontPitch Pitch + { + get { return (LogFontPitch)(lfPitchAndFamily & 0x0F); } + set { lfPitchAndFamily = (byte)((lfPitchAndFamily & 0xF0) | (byte)value); } + } + + public LogFontFontFamily FontFamily + { + get { return (LogFontFontFamily)(lfPitchAndFamily & 0xF0); } + set { lfPitchAndFamily = (byte)((lfPitchAndFamily & 0x0F) | (byte)value); } + } + + public static LOGFONT FromFont(Font font) + { + if (font == null) + throw new System.ArgumentNullException(nameof(font)); + var lf = default(LOGFONT); + font.ToLogFont(lf); + return lf; + } + + public Font ToFont() + { + try + { + return Font.FromLogFont(this); + } + catch + { + return new Font(lfFaceName, lfHeight, FontStyle.Regular, GraphicsUnit.Display); + } + } + + public override string ToString() => $"lfHeight={lfHeight}, lfWidth={lfWidth}, lfEscapement={lfEscapement}, lfOrientation={lfOrientation}, lfWeight={lfWeight}, lfItalic={lfItalic}, lfUnderline={lfUnderline}, lfStrikeOut={lfStrikeOut}, lfCharSet={lfCharSet}, lfOutPrecision={lfOutPrecision}, lfClipPrecision={lfClipPrecision}, lfQuality={lfQuality}, lfPitchAndFamily={lfPitchAndFamily}, lfFaceName={lfFaceName}"; + } + } +} diff --git a/AeroWizard/AeroWizard/Native/RECT.cs b/AeroWizard/AeroWizard/Native/RECT.cs new file mode 100644 index 0000000..2612a11 --- /dev/null +++ b/AeroWizard/AeroWizard/Native/RECT.cs @@ -0,0 +1,165 @@ +using System.Runtime.InteropServices; + +namespace Vanara.Interop +{ + internal static partial class NativeMethods + { + [StructLayout(LayoutKind.Sequential)] + public struct RECT + { + public int Left, Top, Right, Bottom; + + public RECT(int left, int top, int right, int bottom) + { + Left = left; + Top = top; + Right = right; + Bottom = bottom; + } + + public RECT(System.Drawing.Rectangle r) + : this(r.Left, r.Top, r.Right, r.Bottom) + { + } + + public int X + { + get { return Left; } + set { Right -= (Left - value); Left = value; } + } + + public int Y + { + get { return Top; } + set { Bottom -= (Top - value); Top = value; } + } + + public int Height + { + get { return Bottom - Top; } + set { Bottom = value + Top; } + } + + public int Width + { + get { return Right - Left; } + set { Right = value + Left; } + } + + public System.Drawing.Point Location + { + get { return new System.Drawing.Point(Left, Top); } + set { X = value.X; Y = value.Y; } + } + + public System.Drawing.Size Size + { + get { return new System.Drawing.Size(Width, Height); } + set { Width = value.Width; Height = value.Height; } + } + + public static implicit operator System.Drawing.Rectangle(RECT r) => new System.Drawing.Rectangle(r.Left, r.Top, r.Width, r.Height); + + public static implicit operator RECT(System.Drawing.Rectangle r) => new RECT(r); + + public static bool operator ==(RECT r1, RECT r2) => r1.Equals(r2); + + public static bool operator !=(RECT r1, RECT r2) => !r1.Equals(r2); + + public bool Equals(RECT r) => r.Left == Left && r.Top == Top && r.Right == Right && r.Bottom == Bottom; + + public override bool Equals(object obj) + { + if (obj is RECT) + return Equals((RECT)obj); + else if (obj is System.Drawing.Rectangle) + return Equals(new RECT((System.Drawing.Rectangle)obj)); + return false; + } + + public override int GetHashCode() => ((System.Drawing.Rectangle)this).GetHashCode(); + + public override string ToString() => $"{{Left={Left},Top={Top},Right={Right},Bottom={Bottom}}}"; + } + + [StructLayout(LayoutKind.Sequential)] + public class PRECT + { + public int Left, Top, Right, Bottom; + + public PRECT(int left, int top, int right, int bottom) + { + Left = left; + Top = top; + Right = right; + Bottom = bottom; + } + + public PRECT(System.Drawing.Rectangle r) + : this(r.Left, r.Top, r.Right, r.Bottom) + { + } + + public int X + { + get { return Left; } + set { Right -= (Left - value); Left = value; } + } + + public int Y + { + get { return Top; } + set { Bottom -= (Top - value); Top = value; } + } + + public int Height + { + get { return Bottom - Top; } + set { Bottom = value + Top; } + } + + public int Width + { + get { return Right - Left; } + set { Right = value + Left; } + } + + public System.Drawing.Point Location + { + get { return new System.Drawing.Point(Left, Top); } + set { X = value.X; Y = value.Y; } + } + + public System.Drawing.Size Size + { + get { return new System.Drawing.Size(Width, Height); } + set { Width = value.Width; Height = value.Height; } + } + + public static implicit operator System.Drawing.Rectangle(PRECT r) => new System.Drawing.Rectangle(r.Left, r.Top, r.Width, r.Height); + + public static implicit operator PRECT(System.Drawing.Rectangle? r) => r.HasValue ? new PRECT(r.Value) : null; + + public static implicit operator PRECT(System.Drawing.Rectangle r) => new PRECT(r); + + public static bool operator ==(PRECT r1, PRECT r2) => r1.Equals(r2); + + public static bool operator !=(PRECT r1, PRECT r2) => !r1.Equals(r2); + + public bool Equals(PRECT r) => r.Left == Left && r.Top == Top && r.Right == Right && r.Bottom == Bottom; + + public override bool Equals(object obj) + { + if (obj is PRECT) + return Equals((PRECT)obj); + else if (obj is System.Drawing.Rectangle) + return Equals(new PRECT((System.Drawing.Rectangle)obj)); + return false; + } + + public override int GetHashCode() => ((System.Drawing.Rectangle)this).GetHashCode(); + + public override string ToString() => $"{{Left={Left},Top={Top},Right={Right},Bottom={Bottom}}}"; + } + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/Native/SHELL32.cs b/AeroWizard/AeroWizard/Native/SHELL32.cs new file mode 100644 index 0000000..80aa8f7 --- /dev/null +++ b/AeroWizard/AeroWizard/Native/SHELL32.cs @@ -0,0 +1,286 @@ +using System; +using System.Runtime.InteropServices; +using System.Security; +using System.Text; + +namespace Vanara.Interop +{ + [System.Security.SuppressUnmanagedCodeSecurity] + internal static partial class NativeMethods + { + internal const string SHELL32 = "shell32.dll"; + + [Flags] + public enum KF_FLAG : uint + { + ALIAS_ONLY = 0x80000000, + CREATE = 0x8000, + DEFAULT = 0, + DEFAULT_PATH = 0x400, + DONT_UNEXPAND = 0x2000, + DONT_VERIFY = 0x4000, + INIT = 0x800, + NO_ALIAS = 0x1000, + NOT_PARENT_RELATIVE = 0x200, + SIMPLE_IDLIST = 0x100 + } + + public enum SHARD + { + APPIDINFO = 4, + APPIDINFOIDLIST = 5, + APPIDINFOLINK = 7, + LINK = 6, + PATHA = 2, + PATHW = 3, + PIDL = 1, + SHELLITEM = 8 + } + + [Flags] + public enum ShellExecuteMaskFlags : uint + { + SEE_MASK_DEFAULT = 0x00000000, + SEE_MASK_CLASSNAME = 0x00000001, + SEE_MASK_CLASSKEY = 0x00000003, + SEE_MASK_IDLIST = 0x00000004, + SEE_MASK_INVOKEIDLIST = 0x0000000c, // Note SEE_MASK_INVOKEIDLIST(0xC) implies SEE_MASK_IDLIST(0x04) + SEE_MASK_HOTKEY = 0x00000020, + SEE_MASK_NOCLOSEPROCESS = 0x00000040, + SEE_MASK_CONNECTNETDRV = 0x00000080, + SEE_MASK_NOASYNC = 0x00000100, + SEE_MASK_FLAG_DDEWAIT = SEE_MASK_NOASYNC, + SEE_MASK_DOENVSUBST = 0x00000200, + SEE_MASK_FLAG_NO_UI = 0x00000400, + SEE_MASK_UNICODE = 0x00004000, + SEE_MASK_NO_CONSOLE = 0x00008000, + SEE_MASK_ASYNCOK = 0x00100000, + SEE_MASK_HMONITOR = 0x00200000, + SEE_MASK_NOZONECHECKS = 0x00800000, + SEE_MASK_NOQUERYCLASSSTORE = 0x01000000, + SEE_MASK_WAITFORINPUTIDLE = 0x02000000, + SEE_MASK_FLAG_LOG_USAGE = 0x04000000, + } + + [Flags] + public enum SHGFI : int + { + /// get icon + Icon = 0x000000100, + + /// get display name + DisplayName = 0x000000200, + + /// get type name + TypeName = 0x000000400, + + /// get attributes + Attributes = 0x000000800, + + /// get icon location + IconLocation = 0x000001000, + + /// return executable type + ExeType = 0x000002000, + + /// get system icon index + SysIconIndex = 0x000004000, + + /// put a link overlay on icon + LinkOverlay = 0x000008000, + + /// show icon in selected state + Selected = 0x000010000, + + /// get only specified attributes + Attr_Specified = 0x000020000, + + /// get large icon + LargeIcon = 0x000000000, + + /// get small icon + SmallIcon = 0x000000001, + + /// get open icon + OpenIcon = 0x000000002, + + /// get shell size icon + ShellIconSize = 0x000000004, + + /// pszPath is a PIDL + PIDL = 0x000000008, + + /// use passed dwFileAttribute + UseFileAttributes = 0x000000010, + + /// apply the appropriate overlays + AddOverlays = 0x000000020, + + /// Get the index of the overlay in the upper 8 bits of the iIcon + OverlayIndex = 0x000000040, + } + + public enum ShowCommands : int + { + SW_HIDE = 0, + SW_SHOWNORMAL = 1, + SW_NORMAL = 1, + SW_SHOWMINIMIZED = 2, + SW_SHOWMAXIMIZED = 3, + SW_MAXIMIZE = 3, + SW_SHOWNOACTIVATE = 4, + SW_SHOW = 5, + SW_MINIMIZE = 6, + SW_SHOWMINNOACTIVE = 7, + SW_SHOWNA = 8, + SW_RESTORE = 9, + SW_SHOWDEFAULT = 10, + SW_FORCEMINIMIZE = 11, + SW_MAX = 11 + } + + [DllImport(SHELL32, CharSet = CharSet.Auto)] + public extern static int ExtractIconEx([MarshalAs(UnmanagedType.LPTStr)] string lpszFile, int nIconIndex, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4)] IntPtr[] phIconLarge, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4)] IntPtr[] phIconSmall, int nIcons); + + /// Retrieves the User Model AppID that has been explicitly set for the current process via + /// SetCurrentProcessExplicitAppUserModelID + [DllImport(SHELL32)] + public static extern HRESULT GetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] out string AppID); + + /// Sets the User Model AppID for the current process, enabling Windows to retrieve this ID + /// + [DllImport(SHELL32, PreserveSig = false)] + public static extern void SetCurrentProcessExplicitAppUserModelID([MarshalAs(UnmanagedType.LPWStr)] string AppID); + + // This overload is required. There's a cast in the Shell code that causes the wrong vtbl to be used if we let + // the marshaller convert the parameter to an IUnknown. + /// + /// Critical - elevates via a SUC. + /// + [DllImport(SHELL32, EntryPoint = "SHAddToRecentDocs")] + [SecurityCritical, SuppressUnmanagedCodeSecurity] + public static extern void SHAddToRecentDocs(SHARD uFlags, IShellLinkW pv); + + /// + /// Critical - elevates via a SUC. + /// + [DllImport(SHELL32, EntryPoint = "SHAddToRecentDocs")] + [SecurityCritical, SuppressUnmanagedCodeSecurity] + public static extern void SHAddToRecentDocs(SHARD uFlags, [MarshalAs(UnmanagedType.LPWStr)] string pv); + + // Vista only + /// + /// Critical - elevates via a SUC. + /// + [DllImport(SHELL32, CharSet = CharSet.Unicode, SetLastError = true, PreserveSig = false)] + [SecurityCritical, SuppressUnmanagedCodeSecurity] + public static extern void SHCreateItemFromParsingName([MarshalAs(UnmanagedType.LPWStr)] string pszPath, [MarshalAs(UnmanagedType.Interface)] System.Runtime.InteropServices.ComTypes.IBindCtx pbc, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid, [MarshalAs(UnmanagedType.Interface)] out IShellItem ppv); + + // Vista only + /// + /// Critical - elevates via a SUC. + /// + [DllImport(SHELL32, CharSet = CharSet.Unicode, SetLastError = true, PreserveSig = false)] + [SecurityCritical, SuppressUnmanagedCodeSecurity] + public static extern void SHCreateItemFromParsingName([MarshalAs(UnmanagedType.LPWStr)] string pszPath, [MarshalAs(UnmanagedType.Interface)] System.Runtime.InteropServices.ComTypes.IBindCtx pbc, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid, [MarshalAs(UnmanagedType.Interface)] out IShellItem2 ppv); + + [DllImport(SHELL32, CharSet = CharSet.Auto)] + public static extern bool ShellExecuteEx(ref SHELLEXECUTEINFO lpExecInfo); + + [DllImport(SHELL32, CharSet = CharSet.Auto, SetLastError = false)] + public static extern int SHGetFileInfo(string pszPath, System.IO.FileAttributes dwFileAttributes, ref SHFILEINFO psfi, uint cbFileInfo, SHGFI uFlags); + + [DllImport(SHELL32, CharSet = CharSet.Auto, SetLastError = false)] + public static extern int SHGetFileInfo(IntPtr itemIdList, System.IO.FileAttributes dwFileAttributes, ref SHFILEINFO psfi, uint cbFileInfo, SHGFI uFlags); + + // Vista only. Also inconsistently documented on MSDN. It was available in some versions of the SDK, and it + // mentioned on several pages, but isn't specifically documented. + /// + /// Critical - elevates via a SUC. + /// + [DllImport(SHELL32)] + [SecurityCritical, SuppressUnmanagedCodeSecurity] + public static extern HRESULT SHGetFolderPathEx([In] ref Guid rfid, KF_FLAG dwFlags, [In, Optional] IntPtr hToken, [Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszPath, uint cchPath); + + [DllImport(SHELL32, CharSet = CharSet.Unicode, PreserveSig = false, SetLastError = true)] + public static extern void SHGetIDListFromObject(IntPtr iUnknown, out IntPtr ppidl); + + // Note that the BROWSEINFO object's pszDisplayName only gives you the name of the folder. To get the actual + // folderToSelect, you need to parse the returned PIDL + [DllImport(SHELL32, CharSet = CharSet.Unicode)] + public static extern uint SHGetPathFromIDList(IntPtr pidl, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszPath); + + [DllImport(SHELL32, CharSet = CharSet.Unicode, SetLastError = false)] + public static extern int SHGetSpecialFolderLocation(IntPtr hwndOwner, int nFolder, out IntPtr ppidl); + + [DllImport(SHELL32, SetLastError = true)] + public static extern int SHOpenFolderAndSelectItems(IntPtr pidlFolder, uint cidl, [In, MarshalAs(UnmanagedType.LPArray)] IntPtr[] apidl, uint dwFlags); + + [DllImport(SHELL32, CharSet = CharSet.Unicode, SetLastError = false)] + public static extern int SHParseDisplayName([MarshalAs(UnmanagedType.LPWStr)] string pszName, IntPtr pbc, out IntPtr ppidl, uint sfgaoIn, out uint psfgaoOut); + + [StructLayout(LayoutKind.Sequential)] + public struct ITEMIDLIST + { + /// A list of item identifiers. + [MarshalAs(UnmanagedType.Struct)] + public SHITEMID mkid; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SHELLEXECUTEINFO + { + public int cbSize; + public ShellExecuteMaskFlags fMask; + public IntPtr hwnd; + + [MarshalAs(UnmanagedType.LPTStr)] + public string lpVerb; + + [MarshalAs(UnmanagedType.LPTStr)] + public string lpFile; + + [MarshalAs(UnmanagedType.LPTStr)] + public string lpParameters; + + [MarshalAs(UnmanagedType.LPTStr)] + public string lpDirectory; + + public ShowCommands nShow; + public IntPtr hInstApp; + public IntPtr lpIDList; + + [MarshalAs(UnmanagedType.LPTStr)] + public string lpClass; + + public IntPtr hkeyClass; + public uint dwHotKey; + public IntPtr hIcon; + public IntPtr hProcess; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public struct SHFILEINFO + { + public IntPtr hIcon; + public int iIcon; + public int dwAttributes; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] + public string szDisplayName; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)] + public string szTypeName; + } + + [StructLayout(LayoutKind.Sequential)] + public struct SHITEMID + { + /// The size of identifier, in bytes, including itself. + public ushort cb; + + /// A variable-length item identifier. + public byte[] abID; + } + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/Native/SIZE.cs b/AeroWizard/AeroWizard/Native/SIZE.cs new file mode 100644 index 0000000..eb09b7c --- /dev/null +++ b/AeroWizard/AeroWizard/Native/SIZE.cs @@ -0,0 +1,26 @@ +using System.Drawing; +using System.Runtime.InteropServices; + +namespace Vanara.Interop +{ + internal static partial class NativeMethods + { + [StructLayout(LayoutKind.Sequential)] + public struct SIZE + { + public int width; + public int height; + + public SIZE(int w, int h) + { + width = w; height = h; + } + + public Size ToSize() => this; + + public static implicit operator Size(SIZE s) => new Size(s.width, s.height); + + public static implicit operator SIZE(Size s) => new SIZE(s.Width, s.Height); + } + } +} diff --git a/AeroWizard/AeroWizard/Native/SafeDCHandles.cs b/AeroWizard/AeroWizard/Native/SafeDCHandles.cs new file mode 100644 index 0000000..c005568 --- /dev/null +++ b/AeroWizard/AeroWizard/Native/SafeDCHandles.cs @@ -0,0 +1,101 @@ +using System; +using System.Drawing; +using System.Runtime.InteropServices; + +namespace Vanara.Interop +{ + internal static partial class NativeMethods + { + /// + /// A SafeHandle to track DC handles. + /// + public class SafeDCHandle : SafeHandle + { + /// + /// A null handle. + /// + public static readonly SafeDCHandle Null = new SafeDCHandle(IntPtr.Zero); + + private readonly IDeviceContext idc; + /// + /// Initializes a new instance of the class. + /// + /// The handle to the DC. + /// + /// to have the native handle released when this safe handle is disposed or finalized; + /// otherwise. + /// + public SafeDCHandle(IntPtr hDC, bool ownsHandle = true) + : base(IntPtr.Zero, ownsHandle) + { + SetHandle(hDC); + } + + /// + /// Initializes a new instance of the class. + /// + /// An instance. + public SafeDCHandle(IDeviceContext dc) + : base(IntPtr.Zero, true) + { + if (dc == null) + { + throw new ArgumentNullException(nameof(dc)); + } + + idc = dc; + SetHandle(dc.GetHdc()); + } + + /// + public override bool IsInvalid => handle == IntPtr.Zero; + + public static SafeDCHandle ScreenCompatibleDCHandle => new SafeDCHandle(CreateCompatibleDC(IntPtr.Zero)); + + /// + /// Performs an implicit conversion from to . + /// + /// The instance. + /// + /// The result of the conversion. + /// + public static implicit operator SafeDCHandle(Graphics graphics) => new SafeDCHandle(graphics); + + public SafeDCHandle GetCompatibleDCHandle() => new SafeDCHandle(CreateCompatibleDC(handle)); + + /// + protected override bool ReleaseHandle() + { + if (idc != null) + { + idc.ReleaseHdc(); + return true; + } + + return DeleteDC(handle); + } + } + + public class SafeDCObjectHandle : SafeHandle + { + private readonly SafeDCHandle hDC; + private readonly IntPtr hOld; + + public SafeDCObjectHandle(SafeDCHandle hdc, IntPtr hObj): base(IntPtr.Zero, true) + { + if (hdc == null || hdc.IsInvalid) return; + hDC = hdc; + hOld = SelectObject(hdc, hObj); + SetHandle(hObj); + } + + public override bool IsInvalid => handle == IntPtr.Zero; + + protected override bool ReleaseHandle() + { + SelectObject(hDC, hOld); + return DeleteObject(handle); + } + } + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/Native/SafeHGlobalHandle.cs b/AeroWizard/AeroWizard/Native/SafeHGlobalHandle.cs new file mode 100644 index 0000000..d06d2cd --- /dev/null +++ b/AeroWizard/AeroWizard/Native/SafeHGlobalHandle.cs @@ -0,0 +1,205 @@ +using System.Collections.Generic; +using System.Diagnostics; + +namespace System.Runtime.InteropServices +{ + internal class SafeHGlobalHandle : SafeHandle + { + /// + /// Maintains reference to other SafeHGlobalHandle objects, the pointer + /// to which are referred to by this object. This is to ensure that such + /// objects being referred to wouldn't be unreferenced until this object + /// is active. + /// + List references; + + public SafeHGlobalHandle() : this(IntPtr.Zero, 0, false) { } + + public SafeHGlobalHandle(IntPtr handle, int size, bool ownsHandle = true) : + base(IntPtr.Zero, ownsHandle) + { + if (handle != IntPtr.Zero) + SetHandle(handle); + Size = size; + } + + public SafeHGlobalHandle(int size) : this() + { + if (size < 0) + throw new ArgumentOutOfRangeException(nameof(size), "The value of this argument must be non-negative"); + System.Runtime.CompilerServices.RuntimeHelpers.PrepareConstrainedRegions(); + SetHandle(Marshal.AllocHGlobal(size)); + Size = size; + } + + public SafeHGlobalHandle(object value) : this(Marshal.SizeOf(value)) + { + Marshal.StructureToPtr(value, handle, false); + } + + /// + /// Allocates from unmanaged memory to represent an array of pointers + /// and marshals the unmanaged pointers (IntPtr) to the native array + /// equivalent. + /// + /// Array of unmanaged pointers + /// SafeHGlobalHandle object to an native (unmanaged) array of pointers + public SafeHGlobalHandle(IntPtr[] values) : this(IntPtr.Size * values.Length) + { + Marshal.Copy(values, 0, handle, values.Length); + } + + /// + /// Allocates from unmanaged memory to represent a Unicode string (WSTR) + /// and marshal this to a native PWSTR. + /// + /// String + /// SafeHGlobalHandle object to an native (unmanaged) Unicode string + public SafeHGlobalHandle(string s) : this(s == null ? IntPtr.Zero : Marshal.StringToHGlobalUni(s), (s?.Length + 1) * 2 ?? 0) + { + } + + /* + /// + /// Initializes a new instance of the class. + /// + /// The secure string. + public SafeHGlobalHandle(Security.SecureString s) : + base(IntPtr.Zero, p => { Marshal.ZeroFreeGlobalAllocUnicode(p); return true; }, true) + { + if (s != null) + { + s.MakeReadOnly(); + SetHandle(Marshal.SecureStringToGlobalAllocUnicode(s)); + Size = s.Length; + } + } + */ + + /// + /// Allocates from unmanaged memory sufficient memory to hold an object of type T. + /// + /// Native type + /// SafeHGlobalHandle object to an native (unmanaged) memory block the size of T. + public static SafeHGlobalHandle AllocHGlobal() => new SafeHGlobalHandle(Marshal.SizeOf(typeof(T))); + + /// + /// Allocates from unmanaged memory to represent an array of structures + /// and marshals the structure elements to the native array of + /// structures. ONLY structures with attribute StructLayout of + /// LayoutKind.Sequential are supported. + /// + /// Native structure type + /// Collection of structure objects + /// SafeHGlobalHandle object to an native (unmanaged) array of structures + public static SafeHGlobalHandle AllocHGlobal(ICollection values) where T : struct + { + Debug.Assert(typeof(T).StructLayoutAttribute?.Value == LayoutKind.Sequential); + + return AllocHGlobal(0, values, values.Count); + } + + /// + /// Allocates from unmanaged memory to represent a structure with a + /// variable length array at the end and marshal these structure + /// elements. It is the callers responsibility to marshal what precedes + /// the trailing array into the unmanaged memory. ONLY structures with + /// attribute StructLayout of LayoutKind.Sequential are supported. + /// + /// Type of the trailing array of structures + /// Number of bytes preceding the trailing array of structures + /// Collection of structure objects + /// Number of items in . + /// SafeHGlobalHandle object to an native (unmanaged) structure with a trail array of structures + public static SafeHGlobalHandle AllocHGlobal(int prefixBytes, IEnumerable values, int count) where T : struct + { + Debug.Assert(typeof(T).StructLayoutAttribute?.Value == LayoutKind.Sequential); + + var result = new SafeHGlobalHandle(prefixBytes + Marshal.SizeOf(typeof(T)) * count); + var ptr = new IntPtr(result.handle.ToInt32() + prefixBytes); + foreach (var value in values) + { + Marshal.StructureToPtr(value, ptr, false); + ptr = new IntPtr(ptr.ToInt32() + Marshal.SizeOf(typeof(T))); + } + return result; + } + + /// + /// Allocates from unmanaged memory to hold a copy of a structure. + /// + /// Type of the structure. + /// The object. + /// SafeHGlobalHandle object to an native (unmanaged) structure + public static SafeHGlobalHandle AllocHGlobalStruct(T obj) where T : struct + { + Debug.Assert(typeof(T).StructLayoutAttribute?.Value == LayoutKind.Sequential); + + var result = new SafeHGlobalHandle(Marshal.SizeOf(typeof(T))); + Marshal.StructureToPtr(obj, result.handle, false); + return result; + } + + /// + /// Gets the size of the allocated memory block. + /// + /// + /// The sizeof the allocated memory block. + /// + public int Size { get; } + + /// + /// Allows to assign IntPtr to SafeHGlobalHandle + /// + public static implicit operator SafeHGlobalHandle(IntPtr ptr) => new SafeHGlobalHandle(ptr, 0, true); + + /// + /// Allows to use SafeHGlobalHandle as IntPtr + /// + public static implicit operator IntPtr(SafeHGlobalHandle h) => h.DangerousGetHandle(); + + /// + /// Adds reference to other SafeHGlobalHandle objects, the pointer to + /// which are referred to by this object. This is to ensure that such + /// objects being referred to wouldn't be unreferenced until this object + /// is active. + /// + /// For e.g. when this object is an array of pointers to other objects + /// + /// Collection of SafeHGlobalHandle objects referred to by this object. + public void AddSubReference(IEnumerable children) + { + if (references == null) + references = new List(); + references.AddRange(children); + } + + public T ToStructure() where T : struct + { + if (IsInvalid) + return default(T); + if (Size < Marshal.SizeOf(typeof(T))) + throw new InsufficientMemoryException("Requested structure is larger than the memory allocated."); + return handle.ToStructure(); + } + + public T[] ToArray(int count, int prefixBytes = 0) where T : struct + { + if (IsInvalid) + return null; + if (Size < Marshal.SizeOf(typeof(T)) * count + prefixBytes) + throw new InsufficientMemoryException("Requested array is larger than the memory allocated."); + Debug.Assert(typeof(T).StructLayoutAttribute?.Value == LayoutKind.Sequential); + return handle.ToArray(count, prefixBytes); + } + + protected override bool ReleaseHandle() + { + if (!IsInvalid) + Marshal.FreeHGlobal(handle); + return true; + } + + public override bool IsInvalid => handle == IntPtr.Zero; + } +} diff --git a/AeroWizard/AeroWizard/Native/ShObjIdl.cs b/AeroWizard/AeroWizard/Native/ShObjIdl.cs new file mode 100644 index 0000000..dea2b6a --- /dev/null +++ b/AeroWizard/AeroWizard/Native/ShObjIdl.cs @@ -0,0 +1,8185 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using System.Security; +using System.Text; +using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME; + +namespace Vanara.Interop +{ + [StructLayout(LayoutKind.Sequential, Pack = 4)] + public struct ShellItemPropertyKey : IComparable + { + private readonly Guid fmtid; + private readonly int pid; + + public ShellItemPropertyKey(Guid key, int id = 2) + { + fmtid = key; + pid = id; + } + + public Guid Key => fmtid; + + public int Id => pid; + + public override string ToString() => KnownShellItemPropertyKeys.ReverseLookup(this) ?? $"{fmtid:B} {pid}"; + + public override bool Equals(object obj) + { + if (obj != null && obj is ShellItemPropertyKey) + return Guid.Equals(fmtid, ((ShellItemPropertyKey)obj).fmtid) && pid == ((ShellItemPropertyKey)obj).pid; + return false; + } + + public override int GetHashCode() => new { fmtid, pid }.GetHashCode(); + + int IComparable.CompareTo(ShellItemPropertyKey other) + { + int ret = fmtid.GetHashCode() - other.fmtid.GetHashCode(); + if (ret == 0) + ret = pid - other.pid; + return ret; + } + } + + public static class KnownShellItemPropertyKeys + { + private static Dictionary revIndex = null; + + /// + /// Name: System.AcquisitionID -- PKEY_AcquisitionID + /// Description: Hash to determine acquisition session. + /// + /// Type: Int32 -- VT_I4 + /// FormatID: {65A98875-3C80-40AB-ABBC-EFDAF77DBEE2}, 100 + /// + public static ShellItemPropertyKey AcquisitionID => new ShellItemPropertyKey(new Guid("{65A98875-3C80-40AB-ABBC-EFDAF77DBEE2}"), 100); + + /// + /// Name: System.ApplicationName -- PKEY_ApplicationName + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) Legacy code may treat this as VT_LPSTR. + /// FormatID: (FMTID_SummaryInformation) {F29F85E0-4FF9-1068-AB91-08002B27B3D9}, 18 (PIDSI_APPNAME) + /// + public static ShellItemPropertyKey ApplicationName => new ShellItemPropertyKey(new Guid("{F29F85E0-4FF9-1068-AB91-08002B27B3D9}"), 18); + + /// + /// Name: System.Author -- PKEY_Author + /// Description: + /// + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) Legacy code may treat this as VT_LPSTR. + /// FormatID: (FMTID_SummaryInformation) {F29F85E0-4FF9-1068-AB91-08002B27B3D9}, 4 (PIDSI_AUTHOR) + /// + public static ShellItemPropertyKey Author => new ShellItemPropertyKey(new Guid("{F29F85E0-4FF9-1068-AB91-08002B27B3D9}"), 4); + + /// + /// Name: System.Capacity -- PKEY_Capacity + /// Description: The amount of total space in bytes. + /// + /// Type: UInt64 -- VT_UI8 + /// FormatID: (FMTID_Volume) {9B174B35-40FF-11D2-A27E-00C04FC30871}, 3 (PID_VOLUME_CAPACITY) (Filesystem Volume Properties) + /// + public static ShellItemPropertyKey Capacity => new ShellItemPropertyKey(new Guid("{9B174B35-40FF-11D2-A27E-00C04FC30871}"), 3); + + /// + /// Name: System.Category -- PKEY_Category + /// Description: Legacy code treats this as VT_LPSTR. + /// + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: (FMTID_DocumentSummaryInformation) {D5CDD502-2E9C-101B-9397-08002B2CF9AE}, 2 (PIDDSI_CATEGORY) + /// + public static ShellItemPropertyKey Category => new ShellItemPropertyKey(new Guid("{D5CDD502-2E9C-101B-9397-08002B2CF9AE}"), 2); + + /// + /// Name: System.Comment -- PKEY_Comment + /// Description: Comments. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) Legacy code may treat this as VT_LPSTR. + /// FormatID: (FMTID_SummaryInformation) {F29F85E0-4FF9-1068-AB91-08002B27B3D9}, 6 (PIDSI_COMMENTS) + /// + public static ShellItemPropertyKey Comment => new ShellItemPropertyKey(new Guid("{F29F85E0-4FF9-1068-AB91-08002B27B3D9}"), 6); + + /// + /// Name: System.Company -- PKEY_Company + /// Description: The company or publisher. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_DocumentSummaryInformation) {D5CDD502-2E9C-101B-9397-08002B2CF9AE}, 15 (PIDDSI_COMPANY) + /// + public static ShellItemPropertyKey Company => new ShellItemPropertyKey(new Guid("{D5CDD502-2E9C-101B-9397-08002B2CF9AE}"), 15); + + /// + /// Name: System.ComputerName -- PKEY_ComputerName + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_ShellDetails) {28636AA6-953D-11D2-B5D6-00C04FD918D0}, 5 (PID_COMPUTERNAME) + /// + public static ShellItemPropertyKey ComputerName => new ShellItemPropertyKey(new Guid("{28636AA6-953D-11D2-B5D6-00C04FD918D0}"), 5); + + /// + /// Name: System.ContainedItems -- PKEY_ContainedItems + /// Description: The list of type of items, this item contains. For example, this item contains urls, attachments etc. + ///This is represented as a vector array of GUIDs where each GUID represents certain type. + /// + /// Type: Multivalue Guid -- VT_VECTOR | VT_CLSID (For variants: VT_ARRAY | VT_CLSID) + /// FormatID: (FMTID_ShellDetails) {28636AA6-953D-11D2-B5D6-00C04FD918D0}, 29 + /// + public static ShellItemPropertyKey ContainedItems => new ShellItemPropertyKey(new Guid("{28636AA6-953D-11D2-B5D6-00C04FD918D0}"), 29); + + /// + /// Name: System.ContentStatus -- PKEY_ContentStatus + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_DocumentSummaryInformation) {D5CDD502-2E9C-101B-9397-08002B2CF9AE}, 27 + /// + public static ShellItemPropertyKey ContentStatus => new ShellItemPropertyKey(new Guid("{D5CDD502-2E9C-101B-9397-08002B2CF9AE}"), 27); + + /// + /// Name: System.ContentType -- PKEY_ContentType + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_DocumentSummaryInformation) {D5CDD502-2E9C-101B-9397-08002B2CF9AE}, 26 + /// + public static ShellItemPropertyKey ContentType => new ShellItemPropertyKey(new Guid("{D5CDD502-2E9C-101B-9397-08002B2CF9AE}"), 26); + + /// + /// Name: System.Copyright -- PKEY_Copyright + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (PSGUID_MEDIAFILESUMMARYINFORMATION) {64440492-4C8B-11D1-8B70-080036B11A03}, 11 (PIDMSI_COPYRIGHT) + /// + public static ShellItemPropertyKey Copyright => new ShellItemPropertyKey(new Guid("{64440492-4C8B-11D1-8B70-080036B11A03}"), 11); + + /// + /// Name: System.DateAccessed -- PKEY_DateAccessed + /// Description: The time of the last access to the item. The Indexing Service friendly name is 'access'. + /// + /// Type: DateTime -- VT_FILETIME (For variants: VT_DATE) + /// FormatID: (FMTID_Storage) {B725F130-47EF-101A-A5F1-02608C9EEBAC}, 16 (PID_STG_ACCESSTIME) + /// + public static ShellItemPropertyKey DateAccessed => new ShellItemPropertyKey(new Guid("{B725F130-47EF-101A-A5F1-02608C9EEBAC}"), 16); + + /// + /// Name: System.DateAcquired -- PKEY_DateAcquired + /// Description: The time the file entered the system via acquisition. This is not the same as System.DateImported. + ///Examples are when pictures are acquired from a camera, or when music is purchased online. + /// + /// Type: DateTime -- VT_FILETIME (For variants: VT_DATE) + /// FormatID: {2CBAA8F5-D81F-47CA-B17A-F8D822300131}, 100 + /// + public static ShellItemPropertyKey DateAcquired => new ShellItemPropertyKey(new Guid("{2CBAA8F5-D81F-47CA-B17A-F8D822300131}"), 100); + + /// + /// Name: System.DateArchived -- PKEY_DateArchived + /// Description: + /// Type: DateTime -- VT_FILETIME (For variants: VT_DATE) + /// FormatID: {43F8D7B7-A444-4F87-9383-52271C9B915C}, 100 + /// + public static ShellItemPropertyKey DateArchived => new ShellItemPropertyKey(new Guid("{43F8D7B7-A444-4F87-9383-52271C9B915C}"), 100); + + /// + /// Name: System.DateCompleted -- PKEY_DateCompleted + /// Description: + /// Type: DateTime -- VT_FILETIME (For variants: VT_DATE) + /// FormatID: {72FAB781-ACDA-43E5-B155-B2434F85E678}, 100 + /// + public static ShellItemPropertyKey DateCompleted => new ShellItemPropertyKey(new Guid("{72FAB781-ACDA-43E5-B155-B2434F85E678}"), 100); + + /// + /// Name: System.DateCreated -- PKEY_DateCreated + /// Description: The date and time the item was created. The Indexing Service friendly name is 'create'. + /// + /// Type: DateTime -- VT_FILETIME (For variants: VT_DATE) + /// FormatID: (FMTID_Storage) {B725F130-47EF-101A-A5F1-02608C9EEBAC}, 15 (PID_STG_CREATETIME) + /// + public static ShellItemPropertyKey DateCreated => new ShellItemPropertyKey(new Guid("{B725F130-47EF-101A-A5F1-02608C9EEBAC}"), 15); + + /// + /// Name: System.DateImported -- PKEY_DateImported + /// Description: The time the file is imported into a separate database. This is not the same as System.DateAcquired. (Eg, 2003:05:22 13:55:04) + /// + /// Type: DateTime -- VT_FILETIME (For variants: VT_DATE) + /// FormatID: (FMTID_ImageProperties) {14B81DA1-0135-4D31-96D9-6CBFC9671A99}, 18258 + /// + public static ShellItemPropertyKey DateImported => new ShellItemPropertyKey(new Guid("{14B81DA1-0135-4D31-96D9-6CBFC9671A99}"), 18258); + + /// + /// Name: System.DateModified -- PKEY_DateModified + /// Description: The date and time of the last write to the item. The Indexing Service friendly name is 'write'. + /// + /// Type: DateTime -- VT_FILETIME (For variants: VT_DATE) + /// FormatID: (FMTID_Storage) {B725F130-47EF-101A-A5F1-02608C9EEBAC}, 14 (PID_STG_WRITETIME) + /// + public static ShellItemPropertyKey DateModified => new ShellItemPropertyKey(new Guid("{B725F130-47EF-101A-A5F1-02608C9EEBAC}"), 14); + + /// + /// Name: System.DescriptionID -- PKEY_DescriptionID + /// Description: The contents of a SHDESCRIPTIONID structure as a buffer of bytes. + /// + /// Type: Buffer -- VT_VECTOR | VT_UI1 (For variants: VT_ARRAY | VT_UI1) + /// FormatID: (FMTID_ShellDetails) {28636AA6-953D-11D2-B5D6-00C04FD918D0}, 2 (PID_DESCRIPTIONID) + /// + public static ShellItemPropertyKey DescriptionID => new ShellItemPropertyKey(new Guid("{28636AA6-953D-11D2-B5D6-00C04FD918D0}"), 2); + + /// + /// Name: System.DueDate -- PKEY_DueDate + /// Description: + /// Type: DateTime -- VT_FILETIME (For variants: VT_DATE) + /// FormatID: {3F8472B5-E0AF-4DB2-8071-C53FE76AE7CE}, 100 + /// + public static ShellItemPropertyKey DueDate => new ShellItemPropertyKey(new Guid("{3F8472B5-E0AF-4DB2-8071-C53FE76AE7CE}"), 100); + + /// + /// Name: System.EndDate -- PKEY_EndDate + /// Description: + /// Type: DateTime -- VT_FILETIME (For variants: VT_DATE) + /// FormatID: {C75FAA05-96FD-49E7-9CB4-9F601082D553}, 100 + /// + public static ShellItemPropertyKey EndDate => new ShellItemPropertyKey(new Guid("{C75FAA05-96FD-49E7-9CB4-9F601082D553}"), 100); + + /// + /// Name: System.FileAllocationSize -- PKEY_FileAllocationSize + /// Description: + /// + /// Type: UInt64 -- VT_UI8 + /// FormatID: (FMTID_Storage) {B725F130-47EF-101A-A5F1-02608C9EEBAC}, 18 (PID_STG_ALLOCSIZE) + /// + public static ShellItemPropertyKey FileAllocationSize => new ShellItemPropertyKey(new Guid("{B725F130-47EF-101A-A5F1-02608C9EEBAC}"), 18); + + /// + /// Name: System.FileAttributes -- PKEY_FileAttributes + /// Description: This is the WIN32_FIND_DATA dwFileAttributes for the file-based item. + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: (FMTID_Storage) {B725F130-47EF-101A-A5F1-02608C9EEBAC}, 13 (PID_STG_ATTRIBUTES) + /// + public static ShellItemPropertyKey FileAttributes => new ShellItemPropertyKey(new Guid("{B725F130-47EF-101A-A5F1-02608C9EEBAC}"), 13); + + /// + /// Name: System.FileCount -- PKEY_FileCount + /// Description: + /// + /// Type: UInt64 -- VT_UI8 + /// FormatID: (FMTID_ShellDetails) {28636AA6-953D-11D2-B5D6-00C04FD918D0}, 12 + /// + public static ShellItemPropertyKey FileCount => new ShellItemPropertyKey(new Guid("{28636AA6-953D-11D2-B5D6-00C04FD918D0}"), 12); + + /// + /// Name: System.FileDescription -- PKEY_FileDescription + /// Description: This is a user-friendly description of the file. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (PSFMTID_VERSION) {0CEF7D53-FA64-11D1-A203-0000F81FEDEE}, 3 (PIDVSI_FileDescription) + /// + public static ShellItemPropertyKey FileDescription => new ShellItemPropertyKey(new Guid("{0CEF7D53-FA64-11D1-A203-0000F81FEDEE}"), 3); + + /// + /// Name: System.FileExtension -- PKEY_FileExtension + /// Description: This is the file extension of the file based item, including the leading period. + /// + ///If System.FileName is VT_EMPTY, then this property should be too. Otherwise, it should be derived + ///appropriately by the data source from System.FileName. If System.FileName does not have a file + ///extension, this value should be VT_EMPTY. + /// + ///To obtain the type of any item (including an item that is not a file), use System.ItemType. + /// + ///Example values: + /// + /// If the path is... The property value is... + /// ----------------- ------------------------ + /// "c:\foo\bar\hello.txt" ".txt" + /// "\\server\share\mydir\goodnews.doc" ".doc" + /// "\\server\share\numbers.xls" ".xls" + /// "\\server\share\folder" VT_EMPTY + /// "c:\foo\MyFolder" VT_EMPTY + /// [desktop] VT_EMPTY + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {E4F10A3C-49E6-405D-8288-A23BD4EEAA6C}, 100 + /// + public static ShellItemPropertyKey FileExtension => new ShellItemPropertyKey(new Guid("{E4F10A3C-49E6-405D-8288-A23BD4EEAA6C}"), 100); + + /// + /// Name: System.FileFRN -- PKEY_FileFRN + /// Description: This is the unique file ID, also known as the File Reference Number. For a given file, this is the same value + ///as is found in the structure variable FILE_ID_BOTH_DIR_INFO.FileId, via GetFileInformationByHandleEx(). + /// + /// Type: UInt64 -- VT_UI8 + /// FormatID: (FMTID_Storage) {B725F130-47EF-101A-A5F1-02608C9EEBAC}, 21 (PID_STG_FRN) + /// + public static ShellItemPropertyKey FileFRN => new ShellItemPropertyKey(new Guid("{B725F130-47EF-101A-A5F1-02608C9EEBAC}"), 21); + + /// + /// Name: System.FileName -- PKEY_FileName + /// Description: This is the file name (including extension) of the file. + /// + ///It is possible that the item might not exist on a filesystem (ie, it may not be opened + ///using CreateFile). Nonetheless, if the item is represented as a file from the logical sense + ///(and its name follows standard Win32 file-naming syntax), then the data source should emit this property. + /// + ///If an item is not a file, then the value for this property is VT_EMPTY. See + ///System.ItemNameDisplay. + /// + ///This has the same value as System.ParsingName for items that are provided by the Shell's file folder. + /// + ///Example values: + /// + /// If the path is... The property value is... + /// ----------------- ------------------------ + /// "c:\foo\bar\hello.txt" "hello.txt" + /// "\\server\share\mydir\goodnews.doc" "goodnews.doc" + /// "\\server\share\numbers.xls" "numbers.xls" + /// "c:\foo\MyFolder" "MyFolder" + /// (email message) VT_EMPTY + /// (song on portable device) "song.wma" + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {41CF5AE0-F75A-4806-BD87-59C7D9248EB9}, 100 + /// + public static ShellItemPropertyKey FileName => new ShellItemPropertyKey(new Guid("{41CF5AE0-F75A-4806-BD87-59C7D9248EB9}"), 100); + + /// + /// Name: System.FileOwner -- PKEY_FileOwner + /// Description: This is the owner of the file, according to the file system. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_Misc) {9B174B34-40FF-11D2-A27E-00C04FC30871}, 4 (PID_MISC_OWNER) + /// + public static ShellItemPropertyKey FileOwner => new ShellItemPropertyKey(new Guid("{9B174B34-40FF-11D2-A27E-00C04FC30871}"), 4); + + /// + /// Name: System.FileVersion -- PKEY_FileVersion + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (PSFMTID_VERSION) {0CEF7D53-FA64-11D1-A203-0000F81FEDEE}, 4 (PIDVSI_FileVersion) + /// + public static ShellItemPropertyKey FileVersion => new ShellItemPropertyKey(new Guid("{0CEF7D53-FA64-11D1-A203-0000F81FEDEE}"), 4); + + /// + /// Name: System.FindData -- PKEY_FindData + /// Description: WIN32_FIND_DATAW in buffer of bytes. + /// + /// Type: Buffer -- VT_VECTOR | VT_UI1 (For variants: VT_ARRAY | VT_UI1) + /// FormatID: (FMTID_ShellDetails) {28636AA6-953D-11D2-B5D6-00C04FD918D0}, 0 (PID_FINDDATA) + /// + public static ShellItemPropertyKey FindData => new ShellItemPropertyKey(new Guid("{28636AA6-953D-11D2-B5D6-00C04FD918D0}"), 0); + + /// + /// Name: System.FlagColor -- PKEY_FlagColor + /// Description: + /// + /// Type: UInt16 -- VT_UI2 + /// FormatID: {67DF94DE-0CA7-4D6F-B792-053A3E4F03CF}, 100 + /// + public static ShellItemPropertyKey FlagColor => new ShellItemPropertyKey(new Guid("{67DF94DE-0CA7-4D6F-B792-053A3E4F03CF}"), 100); + + /// + /// Name: System.FlagColorText -- PKEY_FlagColorText + /// Description: This is the user-friendly form of System.FlagColor. Not intended to be parsed + ///programmatically. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {45EAE747-8E2A-40AE-8CBF-CA52ABA6152A}, 100 + /// + public static ShellItemPropertyKey FlagColorText => new ShellItemPropertyKey(new Guid("{45EAE747-8E2A-40AE-8CBF-CA52ABA6152A}"), 100); + + /// + /// Name: System.FlagStatus -- PKEY_FlagStatus + /// Description: Status of Flag. Values: (0=none 1=white 2=Red). cdoPR_FLAG_STATUS + /// + /// Type: Int32 -- VT_I4 + /// FormatID: {E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}, 12 + /// + public static ShellItemPropertyKey FlagStatus => new ShellItemPropertyKey(new Guid("{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}"), 12); + + /// + /// Name: System.FlagStatusText -- PKEY_FlagStatusText + /// Description: This is the user-friendly form of System.FlagStatus. Not intended to be parsed + ///programmatically. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {DC54FD2E-189D-4871-AA01-08C2F57A4ABC}, 100 + /// + public static ShellItemPropertyKey FlagStatusText => new ShellItemPropertyKey(new Guid("{DC54FD2E-189D-4871-AA01-08C2F57A4ABC}"), 100); + + /// + /// Name: System.FreeSpace -- PKEY_FreeSpace + /// Description: The amount of free space in bytes. + /// + /// Type: UInt64 -- VT_UI8 + /// FormatID: (FMTID_Volume) {9B174B35-40FF-11D2-A27E-00C04FC30871}, 2 (PID_VOLUME_FREE) (Filesystem Volume Properties) + /// + public static ShellItemPropertyKey FreeSpace => new ShellItemPropertyKey(new Guid("{9B174B35-40FF-11D2-A27E-00C04FC30871}"), 2); + + /// + /// Name: System.FullText -- PKEY_FullText + /// Description: This PKEY is used to specify search terms that should be applied as broadly as possible, + ///across all valid properties for the data source(s) being searched. It should not be + ///emitted from a data source. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {1E3EE840-BC2B-476C-8237-2ACD1A839B22}, 6 + /// + public static ShellItemPropertyKey FullText => new ShellItemPropertyKey(new Guid("{1E3EE840-BC2B-476C-8237-2ACD1A839B22}"), 6); + + /// + /// Name: System.Identity -- PKEY_Identity + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {A26F4AFC-7346-4299-BE47-EB1AE613139F}, 100 + /// + public static ShellItemPropertyKey IdentityProperty => new ShellItemPropertyKey(new Guid("{A26F4AFC-7346-4299-BE47-EB1AE613139F}"), 100); + + /// + /// Name: System.ImageParsingName -- PKEY_ImageParsingName + /// Description: + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: {D7750EE0-C6A4-48EC-B53E-B87B52E6D073}, 100 + /// + public static ShellItemPropertyKey ImageParsingName => new ShellItemPropertyKey(new Guid("{D7750EE0-C6A4-48EC-B53E-B87B52E6D073}"), 100); + + /// + /// Name: System.Importance -- PKEY_Importance + /// Description: + /// Type: Int32 -- VT_I4 + /// FormatID: {E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}, 11 + /// + public static ShellItemPropertyKey Importance => new ShellItemPropertyKey(new Guid("{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}"), 11); + + /// + /// Name: System.ImportanceText -- PKEY_ImportanceText + /// Description: This is the user-friendly form of System.Importance. Not intended to be parsed + ///programmatically. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {A3B29791-7713-4E1D-BB40-17DB85F01831}, 100 + /// + public static ShellItemPropertyKey ImportanceText => new ShellItemPropertyKey(new Guid("{A3B29791-7713-4E1D-BB40-17DB85F01831}"), 100); + + /// + /// Name: System.InfoTipText -- PKEY_InfoTipText + /// Description: The text (with formatted property values) to show in the infotip. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {C9944A21-A406-48FE-8225-AEC7E24C211B}, 17 + /// + public static ShellItemPropertyKey InfoTipText => new ShellItemPropertyKey(new Guid("{C9944A21-A406-48FE-8225-AEC7E24C211B}"), 17); + + /// + /// Name: System.InternalName -- PKEY_InternalName + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (PSFMTID_VERSION) {0CEF7D53-FA64-11D1-A203-0000F81FEDEE}, 5 (PIDVSI_InternalName) + /// + public static ShellItemPropertyKey InternalName => new ShellItemPropertyKey(new Guid("{0CEF7D53-FA64-11D1-A203-0000F81FEDEE}"), 5); + + /// + /// Name: System.IsAttachment -- PKEY_IsAttachment + /// Description: Identifies if this item is an attachment. + /// + /// Type: Boolean -- VT_BOOL + /// FormatID: {F23F425C-71A1-4FA8-922F-678EA4A60408}, 100 + /// + public static ShellItemPropertyKey IsAttachment => new ShellItemPropertyKey(new Guid("{F23F425C-71A1-4FA8-922F-678EA4A60408}"), 100); + + /// + /// Name: System.IsDefaultNonOwnerSaveLocation -- PKEY_IsDefaultNonOwnerSaveLocation + /// Description: Identifies the default save location for a library for non-owners of the library + /// + /// Type: Boolean -- VT_BOOL + /// FormatID: {5D76B67F-9B3D-44BB-B6AE-25DA4F638A67}, 5 + /// + public static ShellItemPropertyKey IsDefaultNonOwnerSaveLocation => new ShellItemPropertyKey(new Guid("{5D76B67F-9B3D-44BB-B6AE-25DA4F638A67}"), 5); + + /// + /// Name: System.IsDefaultSaveLocation -- PKEY_IsDefaultSaveLocation + /// Description: Identifies the default save location for a library for the owner of the library + /// + /// Type: Boolean -- VT_BOOL + /// FormatID: {5D76B67F-9B3D-44BB-B6AE-25DA4F638A67}, 3 + /// + public static ShellItemPropertyKey IsDefaultSaveLocation => new ShellItemPropertyKey(new Guid("{5D76B67F-9B3D-44BB-B6AE-25DA4F638A67}"), 3); + + /// + /// Name: System.IsDeleted -- PKEY_IsDeleted + /// Description: + /// Type: Boolean -- VT_BOOL + /// FormatID: {5CDA5FC8-33EE-4FF3-9094-AE7BD8868C4D}, 100 + /// + public static ShellItemPropertyKey IsDeleted => new ShellItemPropertyKey(new Guid("{5CDA5FC8-33EE-4FF3-9094-AE7BD8868C4D}"), 100); + + /// + /// Name: System.IsEncrypted -- PKEY_IsEncrypted + /// Description: Is the item encrypted? + /// + /// Type: Boolean -- VT_BOOL + /// FormatID: {90E5E14E-648B-4826-B2AA-ACAF790E3513}, 10 + /// + public static ShellItemPropertyKey IsEncrypted => new ShellItemPropertyKey(new Guid("{90E5E14E-648B-4826-B2AA-ACAF790E3513}"), 10); + + /// + /// Name: System.IsFlagged -- PKEY_IsFlagged + /// Description: + /// Type: Boolean -- VT_BOOL + /// FormatID: {5DA84765-E3FF-4278-86B0-A27967FBDD03}, 100 + /// + public static ShellItemPropertyKey IsFlagged => new ShellItemPropertyKey(new Guid("{5DA84765-E3FF-4278-86B0-A27967FBDD03}"), 100); + + /// + /// Name: System.IsFlaggedComplete -- PKEY_IsFlaggedComplete + /// Description: + /// Type: Boolean -- VT_BOOL + /// FormatID: {A6F360D2-55F9-48DE-B909-620E090A647C}, 100 + /// + public static ShellItemPropertyKey IsFlaggedComplete => new ShellItemPropertyKey(new Guid("{A6F360D2-55F9-48DE-B909-620E090A647C}"), 100); + + /// + /// Name: System.IsIncomplete -- PKEY_IsIncomplete + /// Description: Identifies if the message was not completely received for some error condition. + /// + /// Type: Boolean -- VT_BOOL + /// FormatID: {346C8BD1-2E6A-4C45-89A4-61B78E8E700F}, 100 + /// + public static ShellItemPropertyKey IsIncomplete => new ShellItemPropertyKey(new Guid("{346C8BD1-2E6A-4C45-89A4-61B78E8E700F}"), 100); + + /// + /// Name: System.IsLocationSupported -- PKEY_IsLocationSupported + /// Description: A bool value to know if a location is supported (locally indexable, or remotely indexed). + /// + /// Type: Boolean -- VT_BOOL + /// FormatID: {5D76B67F-9B3D-44BB-B6AE-25DA4F638A67}, 8 + /// + public static ShellItemPropertyKey IsLocationSupported => new ShellItemPropertyKey(new Guid("{5D76B67F-9B3D-44BB-B6AE-25DA4F638A67}"), 8); + + /// + /// Name: System.IsPinnedToNameSpaceTree -- PKEY_IsPinnedToNameSpaceTree + /// Description: A bool value to know if a shell folder is pinned to the navigation pane + /// + /// Type: Boolean -- VT_BOOL + /// FormatID: {5D76B67F-9B3D-44BB-B6AE-25DA4F638A67}, 2 + /// + public static ShellItemPropertyKey IsPinnedToNameSpaceTree => new ShellItemPropertyKey(new Guid("{5D76B67F-9B3D-44BB-B6AE-25DA4F638A67}"), 2); + + /// + /// Name: System.IsRead -- PKEY_IsRead + /// Description: Has the item been read? + /// + /// Type: Boolean -- VT_BOOL + /// FormatID: {E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}, 10 + /// + public static ShellItemPropertyKey IsRead => new ShellItemPropertyKey(new Guid("{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}"), 10); + + /// + /// Name: System.IsSearchOnlyItem -- PKEY_IsSearchOnlyItem + /// Description: Identifies if a location or a library is search only + /// + /// Type: Boolean -- VT_BOOL + /// FormatID: {5D76B67F-9B3D-44BB-B6AE-25DA4F638A67}, 4 + /// + public static ShellItemPropertyKey IsSearchOnlyItem => new ShellItemPropertyKey(new Guid("{5D76B67F-9B3D-44BB-B6AE-25DA4F638A67}"), 4); + + /// + /// Name: System.IsSendToTarget -- PKEY_IsSendToTarget + /// Description: Provided by certain shell folders. Return TRUE if the folder is a valid Send To target. + /// + /// Type: Boolean -- VT_BOOL + /// FormatID: (FMTID_ShellDetails) {28636AA6-953D-11D2-B5D6-00C04FD918D0}, 33 + /// + public static ShellItemPropertyKey IsSendToTarget => new ShellItemPropertyKey(new Guid("{28636AA6-953D-11D2-B5D6-00C04FD918D0}"), 33); + + /// + /// Name: System.IsShared -- PKEY_IsShared + /// Description: Is this item shared? This only checks for ACLs that are not inherited. + /// + /// Type: Boolean -- VT_BOOL + /// FormatID: {EF884C5B-2BFE-41BB-AAE5-76EEDF4F9902}, 100 + /// + public static ShellItemPropertyKey IsShared => new ShellItemPropertyKey(new Guid("{EF884C5B-2BFE-41BB-AAE5-76EEDF4F9902}"), 100); + + /// + /// Name: System.ItemAuthors -- PKEY_ItemAuthors + /// Description: This is the generic list of authors associated with an item. + /// + ///For example, the artist name for a track is the item author. + /// + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: {D0A04F0A-462A-48A4-BB2F-3706E88DBD7D}, 100 + /// + public static ShellItemPropertyKey ItemAuthors => new ShellItemPropertyKey(new Guid("{D0A04F0A-462A-48A4-BB2F-3706E88DBD7D}"), 100); + + /// + /// Name: System.ItemClassType -- PKEY_ItemClassType + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {048658AD-2DB8-41A4-BBB6-AC1EF1207EB1}, 100 + /// + public static ShellItemPropertyKey ItemClassType => new ShellItemPropertyKey(new Guid("{048658AD-2DB8-41A4-BBB6-AC1EF1207EB1}"), 100); + + /// + /// Name: System.ItemDate -- PKEY_ItemDate + /// Description: This is the main date for an item. The date of interest. + /// + ///For example, for photos this maps to System.Photo.DateTaken. + /// + /// Type: DateTime -- VT_FILETIME (For variants: VT_DATE) + /// FormatID: {F7DB74B4-4287-4103-AFBA-F1B13DCD75CF}, 100 + /// + public static ShellItemPropertyKey ItemDate => new ShellItemPropertyKey(new Guid("{F7DB74B4-4287-4103-AFBA-F1B13DCD75CF}"), 100); + + /// + /// Name: System.ItemFolderNameDisplay -- PKEY_ItemFolderNameDisplay + /// Description: This is the user-friendly display name of the parent folder of an item. + /// + ///If System.ItemFolderPathDisplay is VT_EMPTY, then this property should be too. Otherwise, it + ///should be derived appropriately by the data source from System.ItemFolderPathDisplay. + /// + ///If the folder is a file folder, the value will be localized if a localized name is available. + /// + ///Example values: + /// + /// If the path is... The property value is... + /// ----------------- ------------------------ + /// "c:\foo\bar\hello.txt" "bar" + /// "\\server\share\mydir\goodnews.doc" "mydir" + /// "\\server\share\numbers.xls" "share" + /// "c:\foo\MyFolder" "foo" + /// "/Mailbox Account/Inbox/'Re: Hello!'" "Inbox" + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_Storage) {B725F130-47EF-101A-A5F1-02608C9EEBAC}, 2 (PID_STG_DIRECTORY) + /// + public static ShellItemPropertyKey ItemFolderNameDisplay => new ShellItemPropertyKey(new Guid("{B725F130-47EF-101A-A5F1-02608C9EEBAC}"), 2); + + /// + /// Name: System.ItemFolderPathDisplay -- PKEY_ItemFolderPathDisplay + /// Description: This is the user-friendly display path of the parent folder of an item. + /// + ///If System.ItemPathDisplay is VT_EMPTY, then this property should be too. Otherwise, it should + ///be derived appropriately by the data source from System.ItemPathDisplay. + /// + ///Example values: + /// + /// If the path is... The property value is... + /// ----------------- ------------------------ + /// "c:\foo\bar\hello.txt" "c:\foo\bar" + /// "\\server\share\mydir\goodnews.doc" "\\server\share\mydir" + /// "\\server\share\numbers.xls" "\\server\share" + /// "c:\foo\MyFolder" "c:\foo" + /// "/Mailbox Account/Inbox/'Re: Hello!'" "/Mailbox Account/Inbox" + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}, 6 + /// + public static ShellItemPropertyKey ItemFolderPathDisplay => new ShellItemPropertyKey(new Guid("{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}"), 6); + + /// + /// Name: System.ItemFolderPathDisplayNarrow -- PKEY_ItemFolderPathDisplayNarrow + /// Description: This is the user-friendly display path of the parent folder of an item. The format of the string + ///should be tailored such that the folder name comes first, to optimize for a narrow viewing column. + /// + ///If the folder is a file folder, the value includes localized names if they are present. + /// + ///If System.ItemFolderPathDisplay is VT_EMPTY, then this property should be too. Otherwise, it should + ///be derived appropriately by the data source from System.ItemFolderPathDisplay. + /// + ///Example values: + /// + /// If the path is... The property value is... + /// ----------------- ------------------------ + /// "c:\foo\bar\hello.txt" "bar (c:\foo)" + /// "\\server\share\mydir\goodnews.doc" "mydir (\\server\share)" + /// "\\server\share\numbers.xls" "share (\\server)" + /// "c:\foo\MyFolder" "foo (c:\)" + /// "/Mailbox Account/Inbox/'Re: Hello!'" "Inbox (/Mailbox Account)" + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {DABD30ED-0043-4789-A7F8-D013A4736622}, 100 + /// + public static ShellItemPropertyKey ItemFolderPathDisplayNarrow => new ShellItemPropertyKey(new Guid("{DABD30ED-0043-4789-A7F8-D013A4736622}"), 100); + + /// + /// Name: System.ItemName -- PKEY_ItemName + /// Description: This is the base-name of the System.ItemNameDisplay. + /// + ///If the item is a file this property + ///includes the extension in all cases, and will be localized if a localized name is available. + /// + ///If the item is a message, then the value of this property does not include the forwarding or + ///reply prefixes (see System.ItemNamePrefix). + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {6B8DA074-3B5C-43BC-886F-0A2CDCE00B6F}, 100 + /// + public static ShellItemPropertyKey ItemName => new ShellItemPropertyKey(new Guid("{6B8DA074-3B5C-43BC-886F-0A2CDCE00B6F}"), 100); + + /// + /// Name: System.ItemNameDisplay -- PKEY_ItemNameDisplay + /// Description: This is the display name in "most complete" form. This is the best effort unique representation + ///of the name of an item that makes sense for end users to read. It is the concatentation of + ///System.ItemNamePrefix and System.ItemName. + /// + ///If the item is a file this property + ///includes the extension in all cases, and will be localized if a localized name is available. + /// + ///There are acceptable cases when System.FileName is not VT_EMPTY, yet the value of this property + ///is completely different. Email messages are a key example. If the item is an email message, + ///the item name is likely the subject. In that case, the value must be the concatenation of the + ///System.ItemNamePrefix and System.ItemName. Since the value of System.ItemNamePrefix excludes + ///any trailing whitespace, the concatenation must include a whitespace when generating System.ItemNameDisplay. + /// + ///Note that this property is not guaranteed to be unique, but the idea is to promote the most likely + ///candidate that can be unique and also makes sense for end users. For example, for documents, you + ///might think about using System.Title as the System.ItemNameDisplay, but in practice the title of + ///the documents may not be useful or unique enough to be of value as the sole System.ItemNameDisplay. + ///Instead, providing the value of System.FileName as the value of System.ItemNameDisplay is a better + ///candidate. In Windows Mail, the emails are stored in the file system as .eml files and the + ///System.FileName for those files are not human-friendly as they contain GUIDs. In this example, + ///promoting System.Subject as System.ItemNameDisplay makes more sense. + /// + ///Compatibility notes: + /// + ///Shell folder implementations on Vista: use PKEY_ItemNameDisplay for the name column when + ///you want Explorer to call ISF::GetDisplayNameOf(SHGDN_NORMAL) to get the value of the name. Use + ///another PKEY (like PKEY_ItemName) when you want Explorer to call either the folder's property store or + ///ISF2::GetDetailsEx in order to get the value of the name. + /// + ///Shell folder implementations on XP: the first column needs to be the name column, and Explorer + ///will call ISF::GetDisplayNameOf to get the value of the name. The PKEY/SCID does not matter. + /// + ///Example values: + /// + /// File: "hello.txt" + /// Message: "Re: Let's talk about Tom's argyle socks!" + /// Device folder: "song.wma" + /// Folder: "Documents" + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_Storage) {B725F130-47EF-101A-A5F1-02608C9EEBAC}, 10 (PID_STG_NAME) + /// + public static ShellItemPropertyKey ItemNameDisplay => new ShellItemPropertyKey(new Guid("{B725F130-47EF-101A-A5F1-02608C9EEBAC}"), 10); + + /// + /// Name: System.ItemNamePrefix -- PKEY_ItemNamePrefix + /// Description: This is the prefix of an item, used for email messages. + ///where the subject begins with "Re:" which is the prefix. + /// + ///If the item is a file, then the value of this property is VT_EMPTY. + /// + ///If the item is a message, then the value of this property is the forwarding or reply + ///prefixes (including delimiting colon, but no whitespace), or VT_EMPTY if there is no prefix. + /// + ///Example values: + /// + ///System.ItemNamePrefix System.ItemName System.ItemNameDisplay + ///--------------------- ------------------- ---------------------- + ///VT_EMPTY "Great day" "Great day" + ///"Re:" "Great day" "Re: Great day" + ///"Fwd: " "Monthly budget" "Fwd: Monthly budget" + ///VT_EMPTY "accounts.xls" "accounts.xls" + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {D7313FF1-A77A-401C-8C99-3DBDD68ADD36}, 100 + /// + public static ShellItemPropertyKey ItemNamePrefix => new ShellItemPropertyKey(new Guid("{D7313FF1-A77A-401C-8C99-3DBDD68ADD36}"), 100); + + /// + /// Name: System.ItemParticipants -- PKEY_ItemParticipants + /// Description: This is the generic list of people associated with an item and who contributed + ///to the item. + /// + ///For example, this is the combination of people in the To list, Cc list and + ///sender of an email message. + /// + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: {D4D0AA16-9948-41A4-AA85-D97FF9646993}, 100 + /// + public static ShellItemPropertyKey ItemParticipants => new ShellItemPropertyKey(new Guid("{D4D0AA16-9948-41A4-AA85-D97FF9646993}"), 100); + + /// + /// Name: System.ItemPathDisplay -- PKEY_ItemPathDisplay + /// Description: This is the user-friendly display path to the item. + /// + ///If the item is a file or folder this property + ///includes the extension in all cases, and will be localized if a localized name is available. + /// + ///For other items,this is the user-friendly equivalent, assuming the item exists in hierarchical storage. + /// + ///Unlike System.ItemUrl, this property value does not include the URL scheme. + /// + ///To parse an item path, use System.ItemUrl or System.ParsingPath. To reference shell + ///namespace items using shell APIs, use System.ParsingPath. + /// + ///Example values: + /// + /// If the path is... The property value is... + /// ----------------- ------------------------ + /// "c:\foo\bar\hello.txt" "c:\foo\bar\hello.txt" + /// "\\server\share\mydir\goodnews.doc" "\\server\share\mydir\goodnews.doc" + /// "\\server\share\numbers.xls" "\\server\share\numbers.xls" + /// "c:\foo\MyFolder" "c:\foo\MyFolder" + /// "/Mailbox Account/Inbox/'Re: Hello!'" "/Mailbox Account/Inbox/'Re: Hello!'" + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}, 7 + /// + public static ShellItemPropertyKey ItemPathDisplay => new ShellItemPropertyKey(new Guid("{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}"), 7); + + /// + /// Name: System.ItemPathDisplayNarrow -- PKEY_ItemPathDisplayNarrow + /// Description: This is the user-friendly display path to the item. The format of the string should be + ///tailored such that the name comes first, to optimize for a narrow viewing column. + /// + ///If the item is a file, the value excludes the file extension, and includes localized names if they are present. + ///If the item is a message, the value includes the System.ItemNamePrefix. + /// + ///To parse an item path, use System.ItemUrl or System.ParsingPath. + /// + ///Example values: + /// + /// If the path is... The property value is... + /// ----------------- ------------------------ + /// "c:\foo\bar\hello.txt" "hello (c:\foo\bar)" + /// "\\server\share\mydir\goodnews.doc" "goodnews (\\server\share\mydir)" + /// "\\server\share\folder" "folder (\\server\share)" + /// "c:\foo\MyFolder" "MyFolder (c:\foo)" + /// "/Mailbox Account/Inbox/'Re: Hello!'" "Re: Hello! (/Mailbox Account/Inbox)" + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_ShellDetails) {28636AA6-953D-11D2-B5D6-00C04FD918D0}, 8 + /// + public static ShellItemPropertyKey ItemPathDisplayNarrow => new ShellItemPropertyKey(new Guid("{28636AA6-953D-11D2-B5D6-00C04FD918D0}"), 8); + + /// + /// Name: System.ItemType -- PKEY_ItemType + /// Description: This is the canonical type of the item and is intended to be programmatically + ///parsed. + /// + ///If there is no canonical type, the value is VT_EMPTY. + /// + ///If the item is a file (ie, System.FileName is not VT_EMPTY), the value is the same as + ///System.FileExtension. + /// + ///Use System.ItemTypeText when you want to display the type to end users in a view. (If + /// the item is a file, passing the System.ItemType value to PSFormatForDisplay will + /// result in the same value as System.ItemTypeText.) + /// + ///Example values: + /// + /// If the path is... The property value is... + /// ----------------- ------------------------ + /// "c:\foo\bar\hello.txt" ".txt" + /// "\\server\share\mydir\goodnews.doc" ".doc" + /// "\\server\share\folder" "Directory" + /// "c:\foo\MyFolder" "Directory" + /// [desktop] "Folder" + /// "/Mailbox Account/Inbox/'Re: Hello!'" "MAPI/IPM.Message" + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_ShellDetails) {28636AA6-953D-11D2-B5D6-00C04FD918D0}, 11 + /// + public static ShellItemPropertyKey ItemType => new ShellItemPropertyKey(new Guid("{28636AA6-953D-11D2-B5D6-00C04FD918D0}"), 11); + + /// + /// Name: System.ItemTypeText -- PKEY_ItemTypeText + /// Description: This is the user friendly type name of the item. This is not intended to be + ///programmatically parsed. + /// + ///If System.ItemType is VT_EMPTY, the value of this property is also VT_EMPTY. + /// + ///If the item is a file, the value of this property is the same as if you passed the + ///file's System.ItemType value to PSFormatForDisplay. + /// + ///This property should not be confused with System.Kind, where System.Kind is a high-level + ///user friendly kind name. For example, for a document, System.Kind = "Document" and + ///System.Item.Type = ".doc" and System.Item.TypeText = "Microsoft Word Document" + /// + ///Example values: + /// + /// If the path is... The property value is... + /// ----------------- ------------------------ + /// "c:\foo\bar\hello.txt" "Text File" + /// "\\server\share\mydir\goodnews.doc" "Microsoft Word Document" + /// "\\server\share\folder" "File Folder" + /// "c:\foo\MyFolder" "File Folder" + /// "/Mailbox Account/Inbox/'Re: Hello!'" "Outlook E-Mail Message" + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_Storage) {B725F130-47EF-101A-A5F1-02608C9EEBAC}, 4 (PID_STG_STORAGETYPE) + /// + public static ShellItemPropertyKey ItemTypeText => new ShellItemPropertyKey(new Guid("{B725F130-47EF-101A-A5F1-02608C9EEBAC}"), 4); + + /// + /// Name: System.ItemUrl -- PKEY_ItemUrl + /// Description: This always represents a well formed URL that points to the item. + /// + ///To reference shell namespace items using shell APIs, use System.ParsingPath. + /// + ///Example values: + /// + /// Files: "file:///c:/foo/bar/hello.txt" + /// "csc://{GUID}/..." + /// Messages: "mapi://..." + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_Query) {49691C90-7E17-101A-A91C-08002B2ECDA9}, 9 (DISPID_QUERY_VIRTUALPATH) + /// + public static ShellItemPropertyKey ItemUrl => new ShellItemPropertyKey(new Guid("{49691C90-7E17-101A-A91C-08002B2ECDA9}"), 9); + + /// + /// Name: System.Keywords -- PKEY_Keywords + /// Description: The keywords for the item. Also referred to as tags. + /// + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) Legacy code may treat this as VT_LPSTR. + /// FormatID: (FMTID_SummaryInformation) {F29F85E0-4FF9-1068-AB91-08002B27B3D9}, 5 (PIDSI_KEYWORDS) + /// + public static ShellItemPropertyKey Keywords => new ShellItemPropertyKey(new Guid("{F29F85E0-4FF9-1068-AB91-08002B27B3D9}"), 5); + + /// + /// Name: System.Kind -- PKEY_Kind + /// Description: System.Kind is used to map extensions to various .Search folders. + ///Extensions are mapped to Kinds at HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Explorer\KindMap + ///The list of kinds is not extensible. + /// + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: {1E3EE840-BC2B-476C-8237-2ACD1A839B22}, 3 + /// + public static ShellItemPropertyKey Kind => new ShellItemPropertyKey(new Guid("{1E3EE840-BC2B-476C-8237-2ACD1A839B22}"), 3); + + /// + /// Name: System.KindText -- PKEY_KindText + /// Description: This is the user-friendly form of System.Kind. Not intended to be parsed + ///programmatically. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {F04BEF95-C585-4197-A2B7-DF46FDC9EE6D}, 100 + /// + public static ShellItemPropertyKey KindText => new ShellItemPropertyKey(new Guid("{F04BEF95-C585-4197-A2B7-DF46FDC9EE6D}"), 100); + + /// + /// Name: System.Language -- PKEY_Language + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_DocumentSummaryInformation) {D5CDD502-2E9C-101B-9397-08002B2CF9AE}, 28 + /// + public static ShellItemPropertyKey Language => new ShellItemPropertyKey(new Guid("{D5CDD502-2E9C-101B-9397-08002B2CF9AE}"), 28); + + /// + /// Name: System.MileageInformation -- PKEY_MileageInformation + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {FDF84370-031A-4ADD-9E91-0D775F1C6605}, 100 + /// + public static ShellItemPropertyKey MileageInformation => new ShellItemPropertyKey(new Guid("{FDF84370-031A-4ADD-9E91-0D775F1C6605}"), 100); + + /// + /// Name: System.MIMEType -- PKEY_MIMEType + /// Description: The MIME type. Eg, for EML files: 'message/rfc822'. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {0B63E350-9CCC-11D0-BCDB-00805FCCCE04}, 5 + /// + public static ShellItemPropertyKey MIMEType => new ShellItemPropertyKey(new Guid("{0B63E350-9CCC-11D0-BCDB-00805FCCCE04}"), 5); + + /// + /// Name: System.NamespaceCLSID -- PKEY_NamespaceCLSID + /// Description: The CLSID of the name space extension for an item, the object that implements IShellFolder for this item + /// + /// Type: Guid -- VT_CLSID + /// FormatID: (FMTID_ShellDetails) {28636AA6-953D-11D2-B5D6-00C04FD918D0}, 6 + /// + public static ShellItemPropertyKey NamespaceCLSID => new ShellItemPropertyKey(new Guid("{28636AA6-953D-11D2-B5D6-00C04FD918D0}"), 6); + + /// + /// Name: System.Null -- PKEY_Null + /// Description: + /// Type: Null -- VT_NULL + /// FormatID: {00000000-0000-0000-0000-000000000000}, 0 + /// + public static ShellItemPropertyKey Null => new ShellItemPropertyKey(new Guid("{00000000-0000-0000-0000-000000000000}"), 0); + + /// + /// Name: System.OfflineAvailability -- PKEY_OfflineAvailability + /// Description: + /// Type: UInt32 -- VT_UI4 + /// FormatID: {A94688B6-7D9F-4570-A648-E3DFC0AB2B3F}, 100 + /// + public static ShellItemPropertyKey OfflineAvailability => new ShellItemPropertyKey(new Guid("{A94688B6-7D9F-4570-A648-E3DFC0AB2B3F}"), 100); + + /// + /// Name: System.OfflineStatus -- PKEY_OfflineStatus + /// Description: + /// Type: UInt32 -- VT_UI4 + /// FormatID: {6D24888F-4718-4BDA-AFED-EA0FB4386CD8}, 100 + /// + public static ShellItemPropertyKey OfflineStatus => new ShellItemPropertyKey(new Guid("{6D24888F-4718-4BDA-AFED-EA0FB4386CD8}"), 100); + + /// + /// Name: System.OriginalFileName -- PKEY_OriginalFileName + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (PSFMTID_VERSION) {0CEF7D53-FA64-11D1-A203-0000F81FEDEE}, 6 + /// + public static ShellItemPropertyKey OriginalFileName => new ShellItemPropertyKey(new Guid("{0CEF7D53-FA64-11D1-A203-0000F81FEDEE}"), 6); + + /// + /// Name: System.OwnerSID -- PKEY_OwnerSID + /// Description: SID of the user that owns the library. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {5D76B67F-9B3D-44BB-B6AE-25DA4F638A67}, 6 + /// + public static ShellItemPropertyKey OwnerSID => new ShellItemPropertyKey(new Guid("{5D76B67F-9B3D-44BB-B6AE-25DA4F638A67}"), 6); + + /// + /// Name: System.ParentalRating -- PKEY_ParentalRating + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (PSGUID_MEDIAFILESUMMARYINFORMATION) {64440492-4C8B-11D1-8B70-080036B11A03}, 21 (PIDMSI_PARENTAL_RATING) + /// + public static ShellItemPropertyKey ParentalRating => new ShellItemPropertyKey(new Guid("{64440492-4C8B-11D1-8B70-080036B11A03}"), 21); + + /// + /// Name: System.ParentalRatingReason -- PKEY_ParentalRatingReason + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {10984E0A-F9F2-4321-B7EF-BAF195AF4319}, 100 + /// + public static ShellItemPropertyKey ParentalRatingReason => new ShellItemPropertyKey(new Guid("{10984E0A-F9F2-4321-B7EF-BAF195AF4319}"), 100); + + /// + /// Name: System.ParentalRatingsOrganization -- PKEY_ParentalRatingsOrganization + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {A7FE0840-1344-46F0-8D37-52ED712A4BF9}, 100 + /// + public static ShellItemPropertyKey ParentalRatingsOrganization => new ShellItemPropertyKey(new Guid("{A7FE0840-1344-46F0-8D37-52ED712A4BF9}"), 100); + + /// + /// Name: System.ParsingBindContext -- PKEY_ParsingBindContext + /// Description: used to get the IBindCtx for an item for parsing + /// + /// Type: Any -- VT_NULL Legacy code may treat this as VT_UNKNOWN. + /// FormatID: {DFB9A04D-362F-4CA3-B30B-0254B17B5B84}, 100 + /// + public static ShellItemPropertyKey ParsingBindContext => new ShellItemPropertyKey(new Guid("{DFB9A04D-362F-4CA3-B30B-0254B17B5B84}"), 100); + + /// + /// Name: System.ParsingName -- PKEY_ParsingName + /// Description: The shell namespace name of an item relative to a parent folder. This name may be passed to + ///IShellFolder::ParseDisplayName() of the parent shell folder. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_ShellDetails) {28636AA6-953D-11D2-B5D6-00C04FD918D0}, 24 + /// + public static ShellItemPropertyKey ParsingName => new ShellItemPropertyKey(new Guid("{28636AA6-953D-11D2-B5D6-00C04FD918D0}"), 24); + + /// + /// Name: System.ParsingPath -- PKEY_ParsingPath + /// Description: This is the shell namespace path to the item. This path may be passed to + ///SHParseDisplayName to parse the path to the correct shell folder. + /// + ///If the item is a file, the value is identical to System.ItemPathDisplay. + /// + ///If the item cannot be accessed through the shell namespace, this value is VT_EMPTY. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_ShellDetails) {28636AA6-953D-11D2-B5D6-00C04FD918D0}, 30 + /// + public static ShellItemPropertyKey ParsingPath => new ShellItemPropertyKey(new Guid("{28636AA6-953D-11D2-B5D6-00C04FD918D0}"), 30); + + /// + /// Name: System.PerceivedType -- PKEY_PerceivedType + /// Description: The perceived type of a shell item, based upon its canonical type. + /// + /// Type: Int32 -- VT_I4 + /// FormatID: (FMTID_ShellDetails) {28636AA6-953D-11D2-B5D6-00C04FD918D0}, 9 + /// + public static ShellItemPropertyKey PerceivedType => new ShellItemPropertyKey(new Guid("{28636AA6-953D-11D2-B5D6-00C04FD918D0}"), 9); + + /// + /// Name: System.PercentFull -- PKEY_PercentFull + /// Description: The amount filled as a percentage, multiplied by 100 (ie, the valid range is 0 through 100). + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: (FMTID_Volume) {9B174B35-40FF-11D2-A27E-00C04FC30871}, 5 (Filesystem Volume Properties) + /// + public static ShellItemPropertyKey PercentFull => new ShellItemPropertyKey(new Guid("{9B174B35-40FF-11D2-A27E-00C04FC30871}"), 5); + + /// + /// Name: System.Priority -- PKEY_Priority + /// Description: + /// + /// Type: UInt16 -- VT_UI2 + /// FormatID: {9C1FCF74-2D97-41BA-B4AE-CB2E3661A6E4}, 5 + /// + public static ShellItemPropertyKey Priority => new ShellItemPropertyKey(new Guid("{9C1FCF74-2D97-41BA-B4AE-CB2E3661A6E4}"), 5); + + /// + /// Name: System.PriorityText -- PKEY_PriorityText + /// Description: This is the user-friendly form of System.Priority. Not intended to be parsed + ///programmatically. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {D98BE98B-B86B-4095-BF52-9D23B2E0A752}, 100 + /// + public static ShellItemPropertyKey PriorityText => new ShellItemPropertyKey(new Guid("{D98BE98B-B86B-4095-BF52-9D23B2E0A752}"), 100); + + /// + /// Name: System.Project -- PKEY_Project + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {39A7F922-477C-48DE-8BC8-B28441E342E3}, 100 + /// + public static ShellItemPropertyKey Project => new ShellItemPropertyKey(new Guid("{39A7F922-477C-48DE-8BC8-B28441E342E3}"), 100); + + /// + /// Name: System.ProviderItemID -- PKEY_ProviderItemID + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {F21D9941-81F0-471A-ADEE-4E74B49217ED}, 100 + /// + public static ShellItemPropertyKey ProviderItemID => new ShellItemPropertyKey(new Guid("{F21D9941-81F0-471A-ADEE-4E74B49217ED}"), 100); + + /// + /// Name: System.Rating -- PKEY_Rating + /// Description: Indicates the users preference rating of an item on a scale of 1-99 (1-12 = One Star, + ///13-37 = Two Stars, 38-62 = Three Stars, 63-87 = Four Stars, 88-99 = Five Stars). + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: (PSGUID_MEDIAFILESUMMARYINFORMATION) {64440492-4C8B-11D1-8B70-080036B11A03}, 9 (PIDMSI_RATING) + /// + public static ShellItemPropertyKey Rating => new ShellItemPropertyKey(new Guid("{64440492-4C8B-11D1-8B70-080036B11A03}"), 9); + + /// + /// Name: System.RatingText -- PKEY_RatingText + /// Description: This is the user-friendly form of System.Rating. Not intended to be parsed + ///programmatically. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {90197CA7-FD8F-4E8C-9DA3-B57E1E609295}, 100 + /// + public static ShellItemPropertyKey RatingText => new ShellItemPropertyKey(new Guid("{90197CA7-FD8F-4E8C-9DA3-B57E1E609295}"), 100); + + /// + /// Name: System.Sensitivity -- PKEY_Sensitivity + /// Description: + /// + /// Type: UInt16 -- VT_UI2 + /// FormatID: {F8D3F6AC-4874-42CB-BE59-AB454B30716A}, 100 + /// + public static ShellItemPropertyKey Sensitivity => new ShellItemPropertyKey(new Guid("{F8D3F6AC-4874-42CB-BE59-AB454B30716A}"), 100); + + /// + /// Name: System.SensitivityText -- PKEY_SensitivityText + /// Description: This is the user-friendly form of System.Sensitivity. Not intended to be parsed + ///programmatically. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {D0C7F054-3F72-4725-8527-129A577CB269}, 100 + /// + public static ShellItemPropertyKey SensitivityText => new ShellItemPropertyKey(new Guid("{D0C7F054-3F72-4725-8527-129A577CB269}"), 100); + + /// + /// Name: System.SFGAOFlags -- PKEY_SFGAOFlags + /// Description: IShellFolder::GetAttributesOf flags, with SFGAO_PKEYSFGAOMASK attributes masked out. + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: (FMTID_ShellDetails) {28636AA6-953D-11D2-B5D6-00C04FD918D0}, 25 + /// + public static ShellItemPropertyKey SFGAOFlags => new ShellItemPropertyKey(new Guid("{28636AA6-953D-11D2-B5D6-00C04FD918D0}"), 25); + + /// + /// Name: System.SharedWith -- PKEY_SharedWith + /// Description: Who is the item shared with? + /// + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: {EF884C5B-2BFE-41BB-AAE5-76EEDF4F9902}, 200 + /// + public static ShellItemPropertyKey SharedWith => new ShellItemPropertyKey(new Guid("{EF884C5B-2BFE-41BB-AAE5-76EEDF4F9902}"), 200); + + /// + /// Name: System.ShareUserRating -- PKEY_ShareUserRating + /// Description: + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: (PSGUID_MEDIAFILESUMMARYINFORMATION) {64440492-4C8B-11D1-8B70-080036B11A03}, 12 (PIDMSI_SHARE_USER_RATING) + /// + public static ShellItemPropertyKey ShareUserRating => new ShellItemPropertyKey(new Guid("{64440492-4C8B-11D1-8B70-080036B11A03}"), 12); + + /// + /// Name: System.SharingStatus -- PKEY_SharingStatus + /// Description: What is the item's sharing status (not shared, shared, everyone (homegroup or everyone), or private)? + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {EF884C5B-2BFE-41BB-AAE5-76EEDF4F9902}, 300 + /// + public static ShellItemPropertyKey SharingStatus => new ShellItemPropertyKey(new Guid("{EF884C5B-2BFE-41BB-AAE5-76EEDF4F9902}"), 300); + + /// + /// Name: System.SimpleRating -- PKEY_SimpleRating + /// Description: Indicates the users preference rating of an item on a scale of 0-5 (0=unrated, 1=One Star, 2=Two Stars, 3=Three Stars, + ///4=Four Stars, 5=Five Stars) + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {A09F084E-AD41-489F-8076-AA5BE3082BCA}, 100 + /// + public static ShellItemPropertyKey SimpleRating => new ShellItemPropertyKey(new Guid("{A09F084E-AD41-489F-8076-AA5BE3082BCA}"), 100); + + /// + /// Name: System.Size -- PKEY_Size + /// Description: + /// + /// Type: UInt64 -- VT_UI8 + /// FormatID: (FMTID_Storage) {B725F130-47EF-101A-A5F1-02608C9EEBAC}, 12 (PID_STG_SIZE) + /// + public static ShellItemPropertyKey Size => new ShellItemPropertyKey(new Guid("{B725F130-47EF-101A-A5F1-02608C9EEBAC}"), 12); + + /// + /// Name: System.SoftwareUsed -- PKEY_SoftwareUsed + /// Description: PropertyTagSoftwareUsed + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_ImageProperties) {14B81DA1-0135-4D31-96D9-6CBFC9671A99}, 305 + /// + public static ShellItemPropertyKey SoftwareUsed => new ShellItemPropertyKey(new Guid("{14B81DA1-0135-4D31-96D9-6CBFC9671A99}"), 305); + + /// + /// Name: System.SourceItem -- PKEY_SourceItem + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {668CDFA5-7A1B-4323-AE4B-E527393A1D81}, 100 + /// + public static ShellItemPropertyKey SourceItem => new ShellItemPropertyKey(new Guid("{668CDFA5-7A1B-4323-AE4B-E527393A1D81}"), 100); + + /// + /// Name: System.StartDate -- PKEY_StartDate + /// Description: + /// Type: DateTime -- VT_FILETIME (For variants: VT_DATE) + /// FormatID: {48FD6EC8-8A12-4CDF-A03E-4EC5A511EDDE}, 100 + /// + public static ShellItemPropertyKey StartDate => new ShellItemPropertyKey(new Guid("{48FD6EC8-8A12-4CDF-A03E-4EC5A511EDDE}"), 100); + + /// + /// Name: System.Status -- PKEY_Status + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_IntSite) {000214A1-0000-0000-C000-000000000046}, 9 + /// + public static ShellItemPropertyKey Status => new ShellItemPropertyKey(new Guid("{000214A1-0000-0000-C000-000000000046}"), 9); + + /// + /// Name: System.Subject -- PKEY_Subject + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_SummaryInformation) {F29F85E0-4FF9-1068-AB91-08002B27B3D9}, 3 (PIDSI_SUBJECT) + /// + public static ShellItemPropertyKey Subject => new ShellItemPropertyKey(new Guid("{F29F85E0-4FF9-1068-AB91-08002B27B3D9}"), 3); + + /// + /// Name: System.Thumbnail -- PKEY_Thumbnail + /// Description: A data that represents the thumbnail in VT_CF format. + /// + /// Type: Clipboard -- VT_CF + /// FormatID: (FMTID_SummaryInformation) {F29F85E0-4FF9-1068-AB91-08002B27B3D9}, 17 (PIDSI_THUMBNAIL) + /// + public static ShellItemPropertyKey Thumbnail => new ShellItemPropertyKey(new Guid("{F29F85E0-4FF9-1068-AB91-08002B27B3D9}"), 17); + + /// + /// Name: System.ThumbnailCacheId -- PKEY_ThumbnailCacheId + /// Description: Unique value that can be used as a key to cache thumbnails. The value changes when the name, volume, or data modified + ///of an item changes. + /// + /// Type: UInt64 -- VT_UI8 + /// FormatID: {446D16B1-8DAD-4870-A748-402EA43D788C}, 100 + /// + public static ShellItemPropertyKey ThumbnailCacheId => new ShellItemPropertyKey(new Guid("{446D16B1-8DAD-4870-A748-402EA43D788C}"), 100); + + /// + /// Name: System.ThumbnailStream -- PKEY_ThumbnailStream + /// Description: Data that represents the thumbnail in VT_STREAM format that GDI+/WindowsCodecs supports (jpg, png, etc). + /// + /// Type: Stream -- VT_STREAM + /// FormatID: (FMTID_SummaryInformation) {F29F85E0-4FF9-1068-AB91-08002B27B3D9}, 27 + /// + public static ShellItemPropertyKey ThumbnailStream => new ShellItemPropertyKey(new Guid("{F29F85E0-4FF9-1068-AB91-08002B27B3D9}"), 27); + + /// + /// Name: System.Title -- PKEY_Title + /// Description: Title of item. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) Legacy code may treat this as VT_LPSTR. + /// FormatID: (FMTID_SummaryInformation) {F29F85E0-4FF9-1068-AB91-08002B27B3D9}, 2 (PIDSI_TITLE) + /// + public static ShellItemPropertyKey Title => new ShellItemPropertyKey(new Guid("{F29F85E0-4FF9-1068-AB91-08002B27B3D9}"), 2); + + /// + /// Name: System.TotalFileSize -- PKEY_TotalFileSize + /// Description: + /// + /// Type: UInt64 -- VT_UI8 + /// FormatID: (FMTID_ShellDetails) {28636AA6-953D-11D2-B5D6-00C04FD918D0}, 14 + /// + public static ShellItemPropertyKey TotalFileSize => new ShellItemPropertyKey(new Guid("{28636AA6-953D-11D2-B5D6-00C04FD918D0}"), 14); + + /// + /// Name: System.Trademarks -- PKEY_Trademarks + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (PSFMTID_VERSION) {0CEF7D53-FA64-11D1-A203-0000F81FEDEE}, 9 (PIDVSI_Trademarks) + /// + public static ShellItemPropertyKey Trademarks => new ShellItemPropertyKey(new Guid("{0CEF7D53-FA64-11D1-A203-0000F81FEDEE}"), 9); + + internal static string ReverseLookup(ShellItemPropertyKey key) + { + if (revIndex == null) + { + revIndex = new Dictionary(); + AddMembersToIndex(typeof(KnownShellItemPropertyKeys)); + } + string ret = null; + revIndex.TryGetValue(key, out ret); + return ret; + } + + private static void AddMembersToIndex(Type type, int level = 0) + { + foreach (PropertyInfo pi in type.GetProperties(BindingFlags.Public | BindingFlags.Static)) + { + if (pi.PropertyType == typeof(ShellItemPropertyKey)) + { + Type pType = type; + StringBuilder name = new StringBuilder(pi.Name); + for (int i = 0; i < level; i++) + { + name.Insert(0, pType.Name + "."); + pType = pType.DeclaringType; + } + try { revIndex.Add((ShellItemPropertyKey)pi.GetValue(null, null), name.ToString()); } catch { } + } + } + foreach (Type ti in type.GetNestedTypes(BindingFlags.Public)) + AddMembersToIndex(ti, level + 1); + } + + /// AppUserModel Properties + public static class AppUserModel + { + /// + /// Name: System.AppUserModel.ExcludeFromShowInNewInstall -- PKEY_AppUserModel_ExcludeFromShowInNewInstall + /// Description: + /// Type: Boolean -- VT_BOOL + /// FormatID: {9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}, 8 + /// + public static ShellItemPropertyKey ExcludeFromShowInNewInstall => new ShellItemPropertyKey(new Guid("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}"), 8); + + /// + /// Name: System.AppUserModel.ID -- PKEY_AppUserModel_ID + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}, 5 + /// + public static ShellItemPropertyKey ID => new ShellItemPropertyKey(new Guid("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}"), 5); + + /// + /// Name: System.AppUserModel.IsDestListSeparator -- PKEY_AppUserModel_IsDestListSeparator + /// Description: + /// Type: Boolean -- VT_BOOL + /// FormatID: {9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}, 6 + /// + public static ShellItemPropertyKey IsDestListSeparator => new ShellItemPropertyKey(new Guid("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}"), 6); + + /// + /// Name: System.AppUserModel.PreventPinning -- PKEY_AppUserModel_PreventPinning + /// Description: + /// Type: Boolean -- VT_BOOL + /// FormatID: {9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}, 9 + /// + public static ShellItemPropertyKey PreventPinning => new ShellItemPropertyKey(new Guid("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}"), 9); + + /// + /// Name: System.AppUserModel.RelaunchCommand -- PKEY_AppUserModel_RelaunchCommand + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}, 2 + /// + public static ShellItemPropertyKey RelaunchCommand => new ShellItemPropertyKey(new Guid("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}"), 2); + + /// + /// Name: System.AppUserModel.RelaunchDisplayNameResource -- PKEY_AppUserModel_RelaunchDisplayNameResource + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}, 4 + /// + public static ShellItemPropertyKey RelaunchDisplayNameResource => new ShellItemPropertyKey(new Guid("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}"), 4); + + /// + /// Name: System.AppUserModel.RelaunchIconResource -- PKEY_AppUserModel_RelaunchIconResource + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}, 3 + /// + public static ShellItemPropertyKey RelaunchIconResource => new ShellItemPropertyKey(new Guid("{9F4C2855-9F79-4B39-A8D0-E1D42DE1D5F3}"), 3); + } + + /// Audio Properties + public static class Audio + { + /// + /// Name: System.Audio.ChannelCount -- PKEY_Audio_ChannelCount + /// Description: Indicates the channel count for the audio file. Values: 1 (mono), 2 (stereo). + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: (FMTID_AudioSummaryInformation) {64440490-4C8B-11D1-8B70-080036B11A03}, 7 (PIDASI_CHANNEL_COUNT) + /// + public static ShellItemPropertyKey ChannelCount => new ShellItemPropertyKey(new Guid("{64440490-4C8B-11D1-8B70-080036B11A03}"), 7); + + /// + /// Name: System.Audio.Compression -- PKEY_Audio_Compression + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_AudioSummaryInformation) {64440490-4C8B-11D1-8B70-080036B11A03}, 10 (PIDASI_COMPRESSION) + /// + public static ShellItemPropertyKey Compression => new ShellItemPropertyKey(new Guid("{64440490-4C8B-11D1-8B70-080036B11A03}"), 10); + + /// + /// Name: System.Audio.EncodingBitrate -- PKEY_Audio_EncodingBitrate + /// Description: Indicates the average data rate in Hz for the audio file in "bits per second". + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: (FMTID_AudioSummaryInformation) {64440490-4C8B-11D1-8B70-080036B11A03}, 4 (PIDASI_AVG_DATA_RATE) + /// + public static ShellItemPropertyKey EncodingBitrate => new ShellItemPropertyKey(new Guid("{64440490-4C8B-11D1-8B70-080036B11A03}"), 4); + + /// + /// Name: System.Audio.Format -- PKEY_Audio_Format + /// Description: Indicates the format of the audio file. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) Legacy code may treat this as VT_BSTR. + /// FormatID: (FMTID_AudioSummaryInformation) {64440490-4C8B-11D1-8B70-080036B11A03}, 2 (PIDASI_FORMAT) + /// + public static ShellItemPropertyKey Format => new ShellItemPropertyKey(new Guid("{64440490-4C8B-11D1-8B70-080036B11A03}"), 2); + + /// + /// Name: System.Audio.IsVariableBitRate -- PKEY_Audio_IsVariableBitRate + /// Description: + /// Type: Boolean -- VT_BOOL + /// FormatID: {E6822FEE-8C17-4D62-823C-8E9CFCBD1D5C}, 100 + /// + public static ShellItemPropertyKey IsVariableBitRate => new ShellItemPropertyKey(new Guid("{E6822FEE-8C17-4D62-823C-8E9CFCBD1D5C}"), 100); + + /// + /// Name: System.Audio.PeakValue -- PKEY_Audio_PeakValue + /// Description: + /// Type: UInt32 -- VT_UI4 + /// FormatID: {2579E5D0-1116-4084-BD9A-9B4F7CB4DF5E}, 100 + /// + public static ShellItemPropertyKey PeakValue => new ShellItemPropertyKey(new Guid("{2579E5D0-1116-4084-BD9A-9B4F7CB4DF5E}"), 100); + + /// + /// Name: System.Audio.SampleRate -- PKEY_Audio_SampleRate + /// Description: Indicates the audio sample rate for the audio file in "samples per second". + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: (FMTID_AudioSummaryInformation) {64440490-4C8B-11D1-8B70-080036B11A03}, 5 (PIDASI_SAMPLE_RATE) + /// + public static ShellItemPropertyKey SampleRate => new ShellItemPropertyKey(new Guid("{64440490-4C8B-11D1-8B70-080036B11A03}"), 5); + + /// + /// Name: System.Audio.SampleSize -- PKEY_Audio_SampleSize + /// Description: Indicates the audio sample size for the audio file in "bits per sample". + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: (FMTID_AudioSummaryInformation) {64440490-4C8B-11D1-8B70-080036B11A03}, 6 (PIDASI_SAMPLE_SIZE) + /// + public static ShellItemPropertyKey SampleSize => new ShellItemPropertyKey(new Guid("{64440490-4C8B-11D1-8B70-080036B11A03}"), 6); + + /// + /// Name: System.Audio.StreamName -- PKEY_Audio_StreamName + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_AudioSummaryInformation) {64440490-4C8B-11D1-8B70-080036B11A03}, 9 (PIDASI_STREAM_NAME) + /// + public static ShellItemPropertyKey StreamName => new ShellItemPropertyKey(new Guid("{64440490-4C8B-11D1-8B70-080036B11A03}"), 9); + + /// + /// Name: System.Audio.StreamNumber -- PKEY_Audio_StreamNumber + /// Description: + /// + /// Type: UInt16 -- VT_UI2 + /// FormatID: (FMTID_AudioSummaryInformation) {64440490-4C8B-11D1-8B70-080036B11A03}, 8 (PIDASI_STREAM_NUMBER) + /// + public static ShellItemPropertyKey StreamNumber => new ShellItemPropertyKey(new Guid("{64440490-4C8B-11D1-8B70-080036B11A03}"), 8); + } + + /// Calendar Properties + public static class Calendar + { + /// + /// Name: System.Calendar.Duration -- PKEY_Calendar_Duration + /// Description: The duration as specified in a string. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {293CA35A-09AA-4DD2-B180-1FE245728A52}, 100 + /// + public static ShellItemPropertyKey Duration => new ShellItemPropertyKey(new Guid("{293CA35A-09AA-4DD2-B180-1FE245728A52}"), 100); + + /// + /// Name: System.Calendar.IsOnline -- PKEY_Calendar_IsOnline + /// Description: Identifies if the event is an online event. + /// + /// Type: Boolean -- VT_BOOL + /// FormatID: {BFEE9149-E3E2-49A7-A862-C05988145CEC}, 100 + /// + public static ShellItemPropertyKey IsOnline => new ShellItemPropertyKey(new Guid("{BFEE9149-E3E2-49A7-A862-C05988145CEC}"), 100); + + /// + /// Name: System.Calendar.IsRecurring -- PKEY_Calendar_IsRecurring + /// Description: + /// Type: Boolean -- VT_BOOL + /// FormatID: {315B9C8D-80A9-4EF9-AE16-8E746DA51D70}, 100 + /// + public static ShellItemPropertyKey IsRecurring => new ShellItemPropertyKey(new Guid("{315B9C8D-80A9-4EF9-AE16-8E746DA51D70}"), 100); + + /// + /// Name: System.Calendar.Location -- PKEY_Calendar_Location + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {F6272D18-CECC-40B1-B26A-3911717AA7BD}, 100 + /// + public static ShellItemPropertyKey Location => new ShellItemPropertyKey(new Guid("{F6272D18-CECC-40B1-B26A-3911717AA7BD}"), 100); + + /// + /// Name: System.Calendar.OptionalAttendeeAddresses -- PKEY_Calendar_OptionalAttendeeAddresses + /// Description: + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: {D55BAE5A-3892-417A-A649-C6AC5AAAEAB3}, 100 + /// + public static ShellItemPropertyKey OptionalAttendeeAddresses => new ShellItemPropertyKey(new Guid("{D55BAE5A-3892-417A-A649-C6AC5AAAEAB3}"), 100); + + /// + /// Name: System.Calendar.OptionalAttendeeNames -- PKEY_Calendar_OptionalAttendeeNames + /// Description: + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: {09429607-582D-437F-84C3-DE93A2B24C3C}, 100 + /// + public static ShellItemPropertyKey OptionalAttendeeNames => new ShellItemPropertyKey(new Guid("{09429607-582D-437F-84C3-DE93A2B24C3C}"), 100); + + /// + /// Name: System.Calendar.OrganizerAddress -- PKEY_Calendar_OrganizerAddress + /// Description: Address of the organizer organizing the event. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {744C8242-4DF5-456C-AB9E-014EFB9021E3}, 100 + /// + public static ShellItemPropertyKey OrganizerAddress => new ShellItemPropertyKey(new Guid("{744C8242-4DF5-456C-AB9E-014EFB9021E3}"), 100); + + /// + /// Name: System.Calendar.OrganizerName -- PKEY_Calendar_OrganizerName + /// Description: Name of the organizer organizing the event. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {AAA660F9-9865-458E-B484-01BC7FE3973E}, 100 + /// + public static ShellItemPropertyKey OrganizerName => new ShellItemPropertyKey(new Guid("{AAA660F9-9865-458E-B484-01BC7FE3973E}"), 100); + + /// + /// Name: System.Calendar.ReminderTime -- PKEY_Calendar_ReminderTime + /// Description: + /// Type: DateTime -- VT_FILETIME (For variants: VT_DATE) + /// FormatID: {72FC5BA4-24F9-4011-9F3F-ADD27AFAD818}, 100 + /// + public static ShellItemPropertyKey ReminderTime => new ShellItemPropertyKey(new Guid("{72FC5BA4-24F9-4011-9F3F-ADD27AFAD818}"), 100); + + /// + /// Name: System.Calendar.RequiredAttendeeAddresses -- PKEY_Calendar_RequiredAttendeeAddresses + /// Description: + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: {0BA7D6C3-568D-4159-AB91-781A91FB71E5}, 100 + /// + public static ShellItemPropertyKey RequiredAttendeeAddresses => new ShellItemPropertyKey(new Guid("{0BA7D6C3-568D-4159-AB91-781A91FB71E5}"), 100); + + /// + /// Name: System.Calendar.RequiredAttendeeNames -- PKEY_Calendar_RequiredAttendeeNames + /// Description: + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: {B33AF30B-F552-4584-936C-CB93E5CDA29F}, 100 + /// + public static ShellItemPropertyKey RequiredAttendeeNames => new ShellItemPropertyKey(new Guid("{B33AF30B-F552-4584-936C-CB93E5CDA29F}"), 100); + + /// + /// Name: System.Calendar.Resources -- PKEY_Calendar_Resources + /// Description: + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: {00F58A38-C54B-4C40-8696-97235980EAE1}, 100 + /// + public static ShellItemPropertyKey Resources => new ShellItemPropertyKey(new Guid("{00F58A38-C54B-4C40-8696-97235980EAE1}"), 100); + + /// + /// Name: System.Calendar.ResponseStatus -- PKEY_Calendar_ResponseStatus + /// Description: This property stores the status of the user responses to meetings in her calendar. + /// + /// Type: UInt16 -- VT_UI2 + /// FormatID: {188C1F91-3C40-4132-9EC5-D8B03B72A8A2}, 100 + /// + public static ShellItemPropertyKey ResponseStatus => new ShellItemPropertyKey(new Guid("{188C1F91-3C40-4132-9EC5-D8B03B72A8A2}"), 100); + + /// + /// Name: System.Calendar.ShowTimeAs -- PKEY_Calendar_ShowTimeAs + /// Description: + /// + /// Type: UInt16 -- VT_UI2 + /// FormatID: {5BF396D4-5EB2-466F-BDE9-2FB3F2361D6E}, 100 + /// + public static ShellItemPropertyKey ShowTimeAs => new ShellItemPropertyKey(new Guid("{5BF396D4-5EB2-466F-BDE9-2FB3F2361D6E}"), 100); + + /// + /// Name: System.Calendar.ShowTimeAsText -- PKEY_Calendar_ShowTimeAsText + /// Description: This is the user-friendly form of System.Calendar.ShowTimeAs. Not intended to be parsed + ///programmatically. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {53DA57CF-62C0-45C4-81DE-7610BCEFD7F5}, 100 + /// + public static ShellItemPropertyKey ShowTimeAsText => new ShellItemPropertyKey(new Guid("{53DA57CF-62C0-45C4-81DE-7610BCEFD7F5}"), 100); + } + + /// Communication Properties + public static class Communication + { + /// + /// Name: System.Communication.AccountName -- PKEY_Communication_AccountName + /// Description: Account Name + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}, 9 + /// + public static ShellItemPropertyKey AccountName => new ShellItemPropertyKey(new Guid("{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}"), 9); + + /// + /// Name: System.Communication.DateItemExpires -- PKEY_Communication_DateItemExpires + /// Description: Date the item expires due to the retention policy. + /// + /// Type: DateTime -- VT_FILETIME (For variants: VT_DATE) + /// FormatID: {428040AC-A177-4C8A-9760-F6F761227F9A}, 100 + /// + public static ShellItemPropertyKey DateItemExpires => new ShellItemPropertyKey(new Guid("{428040AC-A177-4C8A-9760-F6F761227F9A}"), 100); + + /// + /// Name: System.Communication.FollowupIconIndex -- PKEY_Communication_FollowupIconIndex + /// Description: This is the icon index used on messages marked for followup. + /// + /// Type: Int32 -- VT_I4 + /// FormatID: {83A6347E-6FE4-4F40-BA9C-C4865240D1F4}, 100 + /// + public static ShellItemPropertyKey FollowupIconIndex => new ShellItemPropertyKey(new Guid("{83A6347E-6FE4-4F40-BA9C-C4865240D1F4}"), 100); + + /// + /// Name: System.Communication.HeaderItem -- PKEY_Communication_HeaderItem + /// Description: This property will be true if the item is a header item which means the item hasn't been fully downloaded. + /// + /// Type: Boolean -- VT_BOOL + /// FormatID: {C9C34F84-2241-4401-B607-BD20ED75AE7F}, 100 + /// + public static ShellItemPropertyKey HeaderItem => new ShellItemPropertyKey(new Guid("{C9C34F84-2241-4401-B607-BD20ED75AE7F}"), 100); + + /// + /// Name: System.Communication.PolicyTag -- PKEY_Communication_PolicyTag + /// Description: This a string used to identify the retention policy applied to the item. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {EC0B4191-AB0B-4C66-90B6-C6637CDEBBAB}, 100 + /// + public static ShellItemPropertyKey PolicyTag => new ShellItemPropertyKey(new Guid("{EC0B4191-AB0B-4C66-90B6-C6637CDEBBAB}"), 100); + + /// + /// Name: System.Communication.SecurityFlags -- PKEY_Communication_SecurityFlags + /// Description: Security flags associated with the item to know if the item is encrypted, signed or DRM enabled. + /// + /// Type: Int32 -- VT_I4 + /// FormatID: {8619A4B6-9F4D-4429-8C0F-B996CA59E335}, 100 + /// + public static ShellItemPropertyKey SecurityFlags => new ShellItemPropertyKey(new Guid("{8619A4B6-9F4D-4429-8C0F-B996CA59E335}"), 100); + + /// + /// Name: System.Communication.Suffix -- PKEY_Communication_Suffix + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {807B653A-9E91-43EF-8F97-11CE04EE20C5}, 100 + /// + public static ShellItemPropertyKey Suffix => new ShellItemPropertyKey(new Guid("{807B653A-9E91-43EF-8F97-11CE04EE20C5}"), 100); + + /// + /// Name: System.Communication.TaskStatus -- PKEY_Communication_TaskStatus + /// Description: + /// Type: UInt16 -- VT_UI2 + /// FormatID: {BE1A72C6-9A1D-46B7-AFE7-AFAF8CEF4999}, 100 + /// + public static ShellItemPropertyKey TaskStatus => new ShellItemPropertyKey(new Guid("{BE1A72C6-9A1D-46B7-AFE7-AFAF8CEF4999}"), 100); + + /// + /// Name: System.Communication.TaskStatusText -- PKEY_Communication_TaskStatusText + /// Description: This is the user-friendly form of System.Communication.TaskStatus. Not intended to be parsed + ///programmatically. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {A6744477-C237-475B-A075-54F34498292A}, 100 + /// + public static ShellItemPropertyKey TaskStatusText => new ShellItemPropertyKey(new Guid("{A6744477-C237-475B-A075-54F34498292A}"), 100); + } + + /// Computer Properties + public static class Computer + { + /// + /// Name: System.Computer.DecoratedFreeSpace -- PKEY_Computer_DecoratedFreeSpace + /// Description: Free space and total space: "%s free of %s" + /// + /// Type: Multivalue UInt64 -- VT_VECTOR | VT_UI8 (For variants: VT_ARRAY | VT_UI8) + /// FormatID: (FMTID_Volume) {9B174B35-40FF-11D2-A27E-00C04FC30871}, 7 (Filesystem Volume Properties) + /// + public static ShellItemPropertyKey DecoratedFreeSpace => new ShellItemPropertyKey(new Guid("{9B174B35-40FF-11D2-A27E-00C04FC30871}"), 7); + } + + /// Contact Properties + public static class Contact + { + /// + /// Name: System.Contact.Anniversary -- PKEY_Contact_Anniversary + /// Description: + /// Type: DateTime -- VT_FILETIME (For variants: VT_DATE) + /// FormatID: {9AD5BADB-CEA7-4470-A03D-B84E51B9949E}, 100 + /// + public static ShellItemPropertyKey Anniversary => new ShellItemPropertyKey(new Guid("{9AD5BADB-CEA7-4470-A03D-B84E51B9949E}"), 100); + + /// + /// Name: System.Contact.AssistantName -- PKEY_Contact_AssistantName + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {CD102C9C-5540-4A88-A6F6-64E4981C8CD1}, 100 + /// + public static ShellItemPropertyKey AssistantName => new ShellItemPropertyKey(new Guid("{CD102C9C-5540-4A88-A6F6-64E4981C8CD1}"), 100); + + /// + /// Name: System.Contact.AssistantTelephone -- PKEY_Contact_AssistantTelephone + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {9A93244D-A7AD-4FF8-9B99-45EE4CC09AF6}, 100 + /// + public static ShellItemPropertyKey AssistantTelephone => new ShellItemPropertyKey(new Guid("{9A93244D-A7AD-4FF8-9B99-45EE4CC09AF6}"), 100); + + /// + /// Name: System.Contact.Birthday -- PKEY_Contact_Birthday + /// Description: + /// Type: DateTime -- VT_FILETIME (For variants: VT_DATE) + /// FormatID: {176DC63C-2688-4E89-8143-A347800F25E9}, 47 + /// + public static ShellItemPropertyKey Birthday => new ShellItemPropertyKey(new Guid("{176DC63C-2688-4E89-8143-A347800F25E9}"), 47); + + /// + /// Name: System.Contact.BusinessAddress -- PKEY_Contact_BusinessAddress + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {730FB6DD-CF7C-426B-A03F-BD166CC9EE24}, 100 + /// + public static ShellItemPropertyKey BusinessAddress => new ShellItemPropertyKey(new Guid("{730FB6DD-CF7C-426B-A03F-BD166CC9EE24}"), 100); + + /// + /// Name: System.Contact.BusinessAddressCity -- PKEY_Contact_BusinessAddressCity + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {402B5934-EC5A-48C3-93E6-85E86A2D934E}, 100 + /// + public static ShellItemPropertyKey BusinessAddressCity => new ShellItemPropertyKey(new Guid("{402B5934-EC5A-48C3-93E6-85E86A2D934E}"), 100); + + /// + /// Name: System.Contact.BusinessAddressCountry -- PKEY_Contact_BusinessAddressCountry + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {B0B87314-FCF6-4FEB-8DFF-A50DA6AF561C}, 100 + /// + public static ShellItemPropertyKey BusinessAddressCountry => new ShellItemPropertyKey(new Guid("{B0B87314-FCF6-4FEB-8DFF-A50DA6AF561C}"), 100); + + /// + /// Name: System.Contact.BusinessAddressPostalCode -- PKEY_Contact_BusinessAddressPostalCode + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {E1D4A09E-D758-4CD1-B6EC-34A8B5A73F80}, 100 + /// + public static ShellItemPropertyKey BusinessAddressPostalCode => new ShellItemPropertyKey(new Guid("{E1D4A09E-D758-4CD1-B6EC-34A8B5A73F80}"), 100); + + /// + /// Name: System.Contact.BusinessAddressPostOfficeBox -- PKEY_Contact_BusinessAddressPostOfficeBox + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {BC4E71CE-17F9-48D5-BEE9-021DF0EA5409}, 100 + /// + public static ShellItemPropertyKey BusinessAddressPostOfficeBox => new ShellItemPropertyKey(new Guid("{BC4E71CE-17F9-48D5-BEE9-021DF0EA5409}"), 100); + + /// + /// Name: System.Contact.BusinessAddressState -- PKEY_Contact_BusinessAddressState + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {446F787F-10C4-41CB-A6C4-4D0343551597}, 100 + /// + public static ShellItemPropertyKey BusinessAddressState => new ShellItemPropertyKey(new Guid("{446F787F-10C4-41CB-A6C4-4D0343551597}"), 100); + + /// + /// Name: System.Contact.BusinessAddressStreet -- PKEY_Contact_BusinessAddressStreet + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {DDD1460F-C0BF-4553-8CE4-10433C908FB0}, 100 + /// + public static ShellItemPropertyKey BusinessAddressStreet => new ShellItemPropertyKey(new Guid("{DDD1460F-C0BF-4553-8CE4-10433C908FB0}"), 100); + + /// + /// Name: System.Contact.BusinessFaxNumber -- PKEY_Contact_BusinessFaxNumber + /// Description: Business fax number of the contact. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {91EFF6F3-2E27-42CA-933E-7C999FBE310B}, 100 + /// + public static ShellItemPropertyKey BusinessFaxNumber => new ShellItemPropertyKey(new Guid("{91EFF6F3-2E27-42CA-933E-7C999FBE310B}"), 100); + + /// + /// Name: System.Contact.BusinessHomePage -- PKEY_Contact_BusinessHomePage + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {56310920-2491-4919-99CE-EADB06FAFDB2}, 100 + /// + public static ShellItemPropertyKey BusinessHomePage => new ShellItemPropertyKey(new Guid("{56310920-2491-4919-99CE-EADB06FAFDB2}"), 100); + + /// + /// Name: System.Contact.BusinessTelephone -- PKEY_Contact_BusinessTelephone + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {6A15E5A0-0A1E-4CD7-BB8C-D2F1B0C929BC}, 100 + /// + public static ShellItemPropertyKey BusinessTelephone => new ShellItemPropertyKey(new Guid("{6A15E5A0-0A1E-4CD7-BB8C-D2F1B0C929BC}"), 100); + + /// + /// Name: System.Contact.CallbackTelephone -- PKEY_Contact_CallbackTelephone + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {BF53D1C3-49E0-4F7F-8567-5A821D8AC542}, 100 + /// + public static ShellItemPropertyKey CallbackTelephone => new ShellItemPropertyKey(new Guid("{BF53D1C3-49E0-4F7F-8567-5A821D8AC542}"), 100); + + /// + /// Name: System.Contact.CarTelephone -- PKEY_Contact_CarTelephone + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {8FDC6DEA-B929-412B-BA90-397A257465FE}, 100 + /// + public static ShellItemPropertyKey CarTelephone => new ShellItemPropertyKey(new Guid("{8FDC6DEA-B929-412B-BA90-397A257465FE}"), 100); + + /// + /// Name: System.Contact.Children -- PKEY_Contact_Children + /// Description: + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: {D4729704-8EF1-43EF-9024-2BD381187FD5}, 100 + /// + public static ShellItemPropertyKey Children => new ShellItemPropertyKey(new Guid("{D4729704-8EF1-43EF-9024-2BD381187FD5}"), 100); + + /// + /// Name: System.Contact.CompanyMainTelephone -- PKEY_Contact_CompanyMainTelephone + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {8589E481-6040-473D-B171-7FA89C2708ED}, 100 + /// + public static ShellItemPropertyKey CompanyMainTelephone => new ShellItemPropertyKey(new Guid("{8589E481-6040-473D-B171-7FA89C2708ED}"), 100); + + /// + /// Name: System.Contact.Department -- PKEY_Contact_Department + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {FC9F7306-FF8F-4D49-9FB6-3FFE5C0951EC}, 100 + /// + public static ShellItemPropertyKey Department => new ShellItemPropertyKey(new Guid("{FC9F7306-FF8F-4D49-9FB6-3FFE5C0951EC}"), 100); + + /// + /// Name: System.Contact.EmailAddress -- PKEY_Contact_EmailAddress + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {F8FA7FA3-D12B-4785-8A4E-691A94F7A3E7}, 100 + /// + public static ShellItemPropertyKey EmailAddress => new ShellItemPropertyKey(new Guid("{F8FA7FA3-D12B-4785-8A4E-691A94F7A3E7}"), 100); + + /// + /// Name: System.Contact.EmailAddress2 -- PKEY_Contact_EmailAddress2 + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {38965063-EDC8-4268-8491-B7723172CF29}, 100 + /// + public static ShellItemPropertyKey EmailAddress2 => new ShellItemPropertyKey(new Guid("{38965063-EDC8-4268-8491-B7723172CF29}"), 100); + + /// + /// Name: System.Contact.EmailAddress3 -- PKEY_Contact_EmailAddress3 + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {644D37B4-E1B3-4BAD-B099-7E7C04966ACA}, 100 + /// + public static ShellItemPropertyKey EmailAddress3 => new ShellItemPropertyKey(new Guid("{644D37B4-E1B3-4BAD-B099-7E7C04966ACA}"), 100); + + /// + /// Name: System.Contact.EmailAddresses -- PKEY_Contact_EmailAddresses + /// Description: + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: {84D8F337-981D-44B3-9615-C7596DBA17E3}, 100 + /// + public static ShellItemPropertyKey EmailAddresses => new ShellItemPropertyKey(new Guid("{84D8F337-981D-44B3-9615-C7596DBA17E3}"), 100); + + /// + /// Name: System.Contact.EmailName -- PKEY_Contact_EmailName + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {CC6F4F24-6083-4BD4-8754-674D0DE87AB8}, 100 + /// + public static ShellItemPropertyKey EmailName => new ShellItemPropertyKey(new Guid("{CC6F4F24-6083-4BD4-8754-674D0DE87AB8}"), 100); + + /// + /// Name: System.Contact.FileAsName -- PKEY_Contact_FileAsName + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {F1A24AA7-9CA7-40F6-89EC-97DEF9FFE8DB}, 100 + /// + public static ShellItemPropertyKey FileAsName => new ShellItemPropertyKey(new Guid("{F1A24AA7-9CA7-40F6-89EC-97DEF9FFE8DB}"), 100); + + /// + /// Name: System.Contact.FirstName -- PKEY_Contact_FirstName + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {14977844-6B49-4AAD-A714-A4513BF60460}, 100 + /// + public static ShellItemPropertyKey FirstName => new ShellItemPropertyKey(new Guid("{14977844-6B49-4AAD-A714-A4513BF60460}"), 100); + + /// + /// Name: System.Contact.FullName -- PKEY_Contact_FullName + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {635E9051-50A5-4BA2-B9DB-4ED056C77296}, 100 + /// + public static ShellItemPropertyKey FullName => new ShellItemPropertyKey(new Guid("{635E9051-50A5-4BA2-B9DB-4ED056C77296}"), 100); + + /// + /// Name: System.Contact.Gender -- PKEY_Contact_Gender + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {3C8CEE58-D4F0-4CF9-B756-4E5D24447BCD}, 100 + /// + public static ShellItemPropertyKey Gender => new ShellItemPropertyKey(new Guid("{3C8CEE58-D4F0-4CF9-B756-4E5D24447BCD}"), 100); + + /// + /// Name: System.Contact.GenderValue -- PKEY_Contact_GenderValue + /// Description: + /// Type: UInt16 -- VT_UI2 + /// FormatID: {3C8CEE58-D4F0-4CF9-B756-4E5D24447BCD}, 101 + /// + public static ShellItemPropertyKey GenderValue => new ShellItemPropertyKey(new Guid("{3C8CEE58-D4F0-4CF9-B756-4E5D24447BCD}"), 101); + + /// + /// Name: System.Contact.Hobbies -- PKEY_Contact_Hobbies + /// Description: + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: {5DC2253F-5E11-4ADF-9CFE-910DD01E3E70}, 100 + /// + public static ShellItemPropertyKey Hobbies => new ShellItemPropertyKey(new Guid("{5DC2253F-5E11-4ADF-9CFE-910DD01E3E70}"), 100); + + /// + /// Name: System.Contact.HomeAddress -- PKEY_Contact_HomeAddress + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {98F98354-617A-46B8-8560-5B1B64BF1F89}, 100 + /// + public static ShellItemPropertyKey HomeAddress => new ShellItemPropertyKey(new Guid("{98F98354-617A-46B8-8560-5B1B64BF1F89}"), 100); + + /// + /// Name: System.Contact.HomeAddressCity -- PKEY_Contact_HomeAddressCity + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {176DC63C-2688-4E89-8143-A347800F25E9}, 65 + /// + public static ShellItemPropertyKey HomeAddressCity => new ShellItemPropertyKey(new Guid("{176DC63C-2688-4E89-8143-A347800F25E9}"), 65); + + /// + /// Name: System.Contact.HomeAddressCountry -- PKEY_Contact_HomeAddressCountry + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {08A65AA1-F4C9-43DD-9DDF-A33D8E7EAD85}, 100 + /// + public static ShellItemPropertyKey HomeAddressCountry => new ShellItemPropertyKey(new Guid("{08A65AA1-F4C9-43DD-9DDF-A33D8E7EAD85}"), 100); + + /// + /// Name: System.Contact.HomeAddressPostalCode -- PKEY_Contact_HomeAddressPostalCode + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {8AFCC170-8A46-4B53-9EEE-90BAE7151E62}, 100 + /// + public static ShellItemPropertyKey HomeAddressPostalCode => new ShellItemPropertyKey(new Guid("{8AFCC170-8A46-4B53-9EEE-90BAE7151E62}"), 100); + + /// + /// Name: System.Contact.HomeAddressPostOfficeBox -- PKEY_Contact_HomeAddressPostOfficeBox + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {7B9F6399-0A3F-4B12-89BD-4ADC51C918AF}, 100 + /// + public static ShellItemPropertyKey HomeAddressPostOfficeBox => new ShellItemPropertyKey(new Guid("{7B9F6399-0A3F-4B12-89BD-4ADC51C918AF}"), 100); + + /// + /// Name: System.Contact.HomeAddressState -- PKEY_Contact_HomeAddressState + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {C89A23D0-7D6D-4EB8-87D4-776A82D493E5}, 100 + /// + public static ShellItemPropertyKey HomeAddressState => new ShellItemPropertyKey(new Guid("{C89A23D0-7D6D-4EB8-87D4-776A82D493E5}"), 100); + + /// + /// Name: System.Contact.HomeAddressStreet -- PKEY_Contact_HomeAddressStreet + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {0ADEF160-DB3F-4308-9A21-06237B16FA2A}, 100 + /// + public static ShellItemPropertyKey HomeAddressStreet => new ShellItemPropertyKey(new Guid("{0ADEF160-DB3F-4308-9A21-06237B16FA2A}"), 100); + + /// + /// Name: System.Contact.HomeFaxNumber -- PKEY_Contact_HomeFaxNumber + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {660E04D6-81AB-4977-A09F-82313113AB26}, 100 + /// + public static ShellItemPropertyKey HomeFaxNumber => new ShellItemPropertyKey(new Guid("{660E04D6-81AB-4977-A09F-82313113AB26}"), 100); + + /// + /// Name: System.Contact.HomeTelephone -- PKEY_Contact_HomeTelephone + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {176DC63C-2688-4E89-8143-A347800F25E9}, 20 + /// + public static ShellItemPropertyKey HomeTelephone => new ShellItemPropertyKey(new Guid("{176DC63C-2688-4E89-8143-A347800F25E9}"), 20); + + /// + /// Name: System.Contact.IMAddress -- PKEY_Contact_IMAddress + /// Description: + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: {D68DBD8A-3374-4B81-9972-3EC30682DB3D}, 100 + /// + public static ShellItemPropertyKey IMAddress => new ShellItemPropertyKey(new Guid("{D68DBD8A-3374-4B81-9972-3EC30682DB3D}"), 100); + + /// + /// Name: System.Contact.Initials -- PKEY_Contact_Initials + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {F3D8F40D-50CB-44A2-9718-40CB9119495D}, 100 + /// + public static ShellItemPropertyKey Initials => new ShellItemPropertyKey(new Guid("{F3D8F40D-50CB-44A2-9718-40CB9119495D}"), 100); + + /// + /// Name: System.Contact.JobTitle -- PKEY_Contact_JobTitle + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {176DC63C-2688-4E89-8143-A347800F25E9}, 6 + /// + public static ShellItemPropertyKey JobTitle => new ShellItemPropertyKey(new Guid("{176DC63C-2688-4E89-8143-A347800F25E9}"), 6); + + /// + /// Name: System.Contact.Label -- PKEY_Contact_Label + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {97B0AD89-DF49-49CC-834E-660974FD755B}, 100 + /// + public static ShellItemPropertyKey Label => new ShellItemPropertyKey(new Guid("{97B0AD89-DF49-49CC-834E-660974FD755B}"), 100); + + /// + /// Name: System.Contact.LastName -- PKEY_Contact_LastName + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {8F367200-C270-457C-B1D4-E07C5BCD90C7}, 100 + /// + public static ShellItemPropertyKey LastName => new ShellItemPropertyKey(new Guid("{8F367200-C270-457C-B1D4-E07C5BCD90C7}"), 100); + + /// + /// Name: System.Contact.MailingAddress -- PKEY_Contact_MailingAddress + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {C0AC206A-827E-4650-95AE-77E2BB74FCC9}, 100 + /// + public static ShellItemPropertyKey MailingAddress => new ShellItemPropertyKey(new Guid("{C0AC206A-827E-4650-95AE-77E2BB74FCC9}"), 100); + + /// + /// Name: System.Contact.MiddleName -- PKEY_Contact_MiddleName + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {176DC63C-2688-4E89-8143-A347800F25E9}, 71 + /// + public static ShellItemPropertyKey MiddleName => new ShellItemPropertyKey(new Guid("{176DC63C-2688-4E89-8143-A347800F25E9}"), 71); + + /// + /// Name: System.Contact.MobileTelephone -- PKEY_Contact_MobileTelephone + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {176DC63C-2688-4E89-8143-A347800F25E9}, 35 + /// + public static ShellItemPropertyKey MobileTelephone => new ShellItemPropertyKey(new Guid("{176DC63C-2688-4E89-8143-A347800F25E9}"), 35); + + /// + /// Name: System.Contact.NickName -- PKEY_Contact_NickName + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {176DC63C-2688-4E89-8143-A347800F25E9}, 74 + /// + public static ShellItemPropertyKey NickName => new ShellItemPropertyKey(new Guid("{176DC63C-2688-4E89-8143-A347800F25E9}"), 74); + + /// + /// Name: System.Contact.OfficeLocation -- PKEY_Contact_OfficeLocation + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {176DC63C-2688-4E89-8143-A347800F25E9}, 7 + /// + public static ShellItemPropertyKey OfficeLocation => new ShellItemPropertyKey(new Guid("{176DC63C-2688-4E89-8143-A347800F25E9}"), 7); + + /// + /// Name: System.Contact.OtherAddress -- PKEY_Contact_OtherAddress + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {508161FA-313B-43D5-83A1-C1ACCF68622C}, 100 + /// + public static ShellItemPropertyKey OtherAddress => new ShellItemPropertyKey(new Guid("{508161FA-313B-43D5-83A1-C1ACCF68622C}"), 100); + + /// + /// Name: System.Contact.OtherAddressCity -- PKEY_Contact_OtherAddressCity + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {6E682923-7F7B-4F0C-A337-CFCA296687BF}, 100 + /// + public static ShellItemPropertyKey OtherAddressCity => new ShellItemPropertyKey(new Guid("{6E682923-7F7B-4F0C-A337-CFCA296687BF}"), 100); + + /// + /// Name: System.Contact.OtherAddressCountry -- PKEY_Contact_OtherAddressCountry + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {8F167568-0AAE-4322-8ED9-6055B7B0E398}, 100 + /// + public static ShellItemPropertyKey OtherAddressCountry => new ShellItemPropertyKey(new Guid("{8F167568-0AAE-4322-8ED9-6055B7B0E398}"), 100); + + /// + /// Name: System.Contact.OtherAddressPostalCode -- PKEY_Contact_OtherAddressPostalCode + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {95C656C1-2ABF-4148-9ED3-9EC602E3B7CD}, 100 + /// + public static ShellItemPropertyKey OtherAddressPostalCode => new ShellItemPropertyKey(new Guid("{95C656C1-2ABF-4148-9ED3-9EC602E3B7CD}"), 100); + + /// + /// Name: System.Contact.OtherAddressPostOfficeBox -- PKEY_Contact_OtherAddressPostOfficeBox + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {8B26EA41-058F-43F6-AECC-4035681CE977}, 100 + /// + public static ShellItemPropertyKey OtherAddressPostOfficeBox => new ShellItemPropertyKey(new Guid("{8B26EA41-058F-43F6-AECC-4035681CE977}"), 100); + + /// + /// Name: System.Contact.OtherAddressState -- PKEY_Contact_OtherAddressState + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {71B377D6-E570-425F-A170-809FAE73E54E}, 100 + /// + public static ShellItemPropertyKey OtherAddressState => new ShellItemPropertyKey(new Guid("{71B377D6-E570-425F-A170-809FAE73E54E}"), 100); + + /// + /// Name: System.Contact.OtherAddressStreet -- PKEY_Contact_OtherAddressStreet + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {FF962609-B7D6-4999-862D-95180D529AEA}, 100 + /// + public static ShellItemPropertyKey OtherAddressStreet => new ShellItemPropertyKey(new Guid("{FF962609-B7D6-4999-862D-95180D529AEA}"), 100); + + /// + /// Name: System.Contact.PagerTelephone -- PKEY_Contact_PagerTelephone + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {D6304E01-F8F5-4F45-8B15-D024A6296789}, 100 + /// + public static ShellItemPropertyKey PagerTelephone => new ShellItemPropertyKey(new Guid("{D6304E01-F8F5-4F45-8B15-D024A6296789}"), 100); + + /// + /// Name: System.Contact.PersonalTitle -- PKEY_Contact_PersonalTitle + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {176DC63C-2688-4E89-8143-A347800F25E9}, 69 + /// + public static ShellItemPropertyKey PersonalTitle => new ShellItemPropertyKey(new Guid("{176DC63C-2688-4E89-8143-A347800F25E9}"), 69); + + /// + /// Name: System.Contact.PrimaryAddressCity -- PKEY_Contact_PrimaryAddressCity + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {C8EA94F0-A9E3-4969-A94B-9C62A95324E0}, 100 + /// + public static ShellItemPropertyKey PrimaryAddressCity => new ShellItemPropertyKey(new Guid("{C8EA94F0-A9E3-4969-A94B-9C62A95324E0}"), 100); + + /// + /// Name: System.Contact.PrimaryAddressCountry -- PKEY_Contact_PrimaryAddressCountry + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {E53D799D-0F3F-466E-B2FF-74634A3CB7A4}, 100 + /// + public static ShellItemPropertyKey PrimaryAddressCountry => new ShellItemPropertyKey(new Guid("{E53D799D-0F3F-466E-B2FF-74634A3CB7A4}"), 100); + + /// + /// Name: System.Contact.PrimaryAddressPostalCode -- PKEY_Contact_PrimaryAddressPostalCode + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {18BBD425-ECFD-46EF-B612-7B4A6034EDA0}, 100 + /// + public static ShellItemPropertyKey PrimaryAddressPostalCode => new ShellItemPropertyKey(new Guid("{18BBD425-ECFD-46EF-B612-7B4A6034EDA0}"), 100); + + /// + /// Name: System.Contact.PrimaryAddressPostOfficeBox -- PKEY_Contact_PrimaryAddressPostOfficeBox + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {DE5EF3C7-46E1-484E-9999-62C5308394C1}, 100 + /// + public static ShellItemPropertyKey PrimaryAddressPostOfficeBox => new ShellItemPropertyKey(new Guid("{DE5EF3C7-46E1-484E-9999-62C5308394C1}"), 100); + + /// + /// Name: System.Contact.PrimaryAddressState -- PKEY_Contact_PrimaryAddressState + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {F1176DFE-7138-4640-8B4C-AE375DC70A6D}, 100 + /// + public static ShellItemPropertyKey PrimaryAddressState => new ShellItemPropertyKey(new Guid("{F1176DFE-7138-4640-8B4C-AE375DC70A6D}"), 100); + + /// + /// Name: System.Contact.PrimaryAddressStreet -- PKEY_Contact_PrimaryAddressStreet + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {63C25B20-96BE-488F-8788-C09C407AD812}, 100 + /// + public static ShellItemPropertyKey PrimaryAddressStreet => new ShellItemPropertyKey(new Guid("{63C25B20-96BE-488F-8788-C09C407AD812}"), 100); + + /// + /// Name: System.Contact.PrimaryEmailAddress -- PKEY_Contact_PrimaryEmailAddress + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {176DC63C-2688-4E89-8143-A347800F25E9}, 48 + /// + public static ShellItemPropertyKey PrimaryEmailAddress => new ShellItemPropertyKey(new Guid("{176DC63C-2688-4E89-8143-A347800F25E9}"), 48); + + /// + /// Name: System.Contact.PrimaryTelephone -- PKEY_Contact_PrimaryTelephone + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {176DC63C-2688-4E89-8143-A347800F25E9}, 25 + /// + public static ShellItemPropertyKey PrimaryTelephone => new ShellItemPropertyKey(new Guid("{176DC63C-2688-4E89-8143-A347800F25E9}"), 25); + + /// + /// Name: System.Contact.Profession -- PKEY_Contact_Profession + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {7268AF55-1CE4-4F6E-A41F-B6E4EF10E4A9}, 100 + /// + public static ShellItemPropertyKey Profession => new ShellItemPropertyKey(new Guid("{7268AF55-1CE4-4F6E-A41F-B6E4EF10E4A9}"), 100); + + /// + /// Name: System.Contact.SpouseName -- PKEY_Contact_SpouseName + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {9D2408B6-3167-422B-82B0-F583B7A7CFE3}, 100 + /// + public static ShellItemPropertyKey SpouseName => new ShellItemPropertyKey(new Guid("{9D2408B6-3167-422B-82B0-F583B7A7CFE3}"), 100); + + /// + /// Name: System.Contact.Suffix -- PKEY_Contact_Suffix + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {176DC63C-2688-4E89-8143-A347800F25E9}, 73 + /// + public static ShellItemPropertyKey Suffix => new ShellItemPropertyKey(new Guid("{176DC63C-2688-4E89-8143-A347800F25E9}"), 73); + + /// + /// Name: System.Contact.TelexNumber -- PKEY_Contact_TelexNumber + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {C554493C-C1F7-40C1-A76C-EF8C0614003E}, 100 + /// + public static ShellItemPropertyKey TelexNumber => new ShellItemPropertyKey(new Guid("{C554493C-C1F7-40C1-A76C-EF8C0614003E}"), 100); + + /// + /// Name: System.Contact.TTYTDDTelephone -- PKEY_Contact_TTYTDDTelephone + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {AAF16BAC-2B55-45E6-9F6D-415EB94910DF}, 100 + /// + public static ShellItemPropertyKey TTYTDDTelephone => new ShellItemPropertyKey(new Guid("{AAF16BAC-2B55-45E6-9F6D-415EB94910DF}"), 100); + + /// + /// Name: System.Contact.WebPage -- PKEY_Contact_WebPage + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}, 18 + /// + public static ShellItemPropertyKey WebPage => new ShellItemPropertyKey(new Guid("{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}"), 18); + + /// JA Properties + public static class JA + { + /// + /// Name: System.Contact.JA.CompanyNamePhonetic -- PKEY_Contact_JA_CompanyNamePhonetic + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {897B3694-FE9E-43E6-8066-260F590C0100}, 2 + /// + public static ShellItemPropertyKey CompanyNamePhonetic => new ShellItemPropertyKey(new Guid("{897B3694-FE9E-43E6-8066-260F590C0100}"), 2); + + /// + /// Name: System.Contact.JA.FirstNamePhonetic -- PKEY_Contact_JA_FirstNamePhonetic + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {897B3694-FE9E-43E6-8066-260F590C0100}, 3 + /// + public static ShellItemPropertyKey FirstNamePhonetic => new ShellItemPropertyKey(new Guid("{897B3694-FE9E-43E6-8066-260F590C0100}"), 3); + + /// + /// Name: System.Contact.JA.LastNamePhonetic -- PKEY_Contact_JA_LastNamePhonetic + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {897B3694-FE9E-43E6-8066-260F590C0100}, 4 + /// + public static ShellItemPropertyKey LastNamePhonetic => new ShellItemPropertyKey(new Guid("{897B3694-FE9E-43E6-8066-260F590C0100}"), 4); + } + } + + /// Device Properties + public static class Device + { + /// + /// Name: System.Device.PrinterURL -- PKEY_Device_PrinterURL + /// Description: Printer information Printer URL. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {0B48F35A-BE6E-4F17-B108-3C4073D1669A}, 15 + /// + public static ShellItemPropertyKey PrinterURL => new ShellItemPropertyKey(new Guid("{0B48F35A-BE6E-4F17-B108-3C4073D1669A}"), 15); + } + + /// DeviceInterface Properties + public static class DeviceInterface + { + /// + /// Name: System.DeviceInterface.PrinterDriverDirectory -- PKEY_DeviceInterface_PrinterDriverDirectory + /// Description: Printer information Printer Driver Directory. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {847C66DE-B8D6-4AF9-ABC3-6F4F926BC039}, 14 + /// + public static ShellItemPropertyKey PrinterDriverDirectory => new ShellItemPropertyKey(new Guid("{847C66DE-B8D6-4AF9-ABC3-6F4F926BC039}"), 14); + + /// + /// Name: System.DeviceInterface.PrinterDriverName -- PKEY_DeviceInterface_PrinterDriverName + /// Description: Printer information Driver Name. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {AFC47170-14F5-498C-8F30-B0D19BE449C6}, 11 + /// + public static ShellItemPropertyKey PrinterDriverName => new ShellItemPropertyKey(new Guid("{AFC47170-14F5-498C-8F30-B0D19BE449C6}"), 11); + + /// + /// Name: System.DeviceInterface.PrinterName -- PKEY_DeviceInterface_PrinterName + /// Description: Printer information Printer Name. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {0A7B84EF-0C27-463F-84EF-06C5070001BE}, 10 + /// + public static ShellItemPropertyKey PrinterName => new ShellItemPropertyKey(new Guid("{0A7B84EF-0C27-463F-84EF-06C5070001BE}"), 10); + + /// + /// Name: System.DeviceInterface.PrinterPortName -- PKEY_DeviceInterface_PrinterPortName + /// Description: Printer information Port Name. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {EEC7B761-6F94-41B1-949F-C729720DD13C}, 12 + /// + public static ShellItemPropertyKey PrinterPortName => new ShellItemPropertyKey(new Guid("{EEC7B761-6F94-41B1-949F-C729720DD13C}"), 12); + } + + /// Devices Properties + public static class Devices + { + /// + /// Name: System.Devices.BatteryLife -- PKEY_Devices_BatteryLife + /// Description: Remaining battery life of the device as an integer between 0 and 100 percent. + /// + /// Type: Byte -- VT_UI1 + /// FormatID: {49CD1F76-5626-4B17-A4E8-18B4AA1A2213}, 10 + /// + public static ShellItemPropertyKey BatteryLife => new ShellItemPropertyKey(new Guid("{49CD1F76-5626-4B17-A4E8-18B4AA1A2213}"), 10); + + /// + /// Name: System.Devices.BatteryPlusCharging -- PKEY_Devices_BatteryPlusCharging + /// Description: Remaining battery life of the device as an integer between 0 and 100 percent and the device's charging state. + /// + /// Type: Byte -- VT_UI1 + /// FormatID: {49CD1F76-5626-4B17-A4E8-18B4AA1A2213}, 22 + /// + public static ShellItemPropertyKey BatteryPlusCharging => new ShellItemPropertyKey(new Guid("{49CD1F76-5626-4B17-A4E8-18B4AA1A2213}"), 22); + + /// + /// Name: System.Devices.BatteryPlusChargingText -- PKEY_Devices_BatteryPlusChargingText + /// Description: Remaining battery life of the device and the device's charging state as a string. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {49CD1F76-5626-4B17-A4E8-18B4AA1A2213}, 23 + /// + public static ShellItemPropertyKey BatteryPlusChargingText => new ShellItemPropertyKey(new Guid("{49CD1F76-5626-4B17-A4E8-18B4AA1A2213}"), 23); + + /// + /// Name: System.Devices.Category -- PKEY_Devices_Category_Desc_Singular + /// Description: Singular form of device category. + /// + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: {78C34FC8-104A-4ACA-9EA4-524D52996E57}, 91 + /// + public static ShellItemPropertyKey Category => new ShellItemPropertyKey(new Guid("{78C34FC8-104A-4ACA-9EA4-524D52996E57}"), 91); + + /// + /// Name: System.Devices.CategoryGroup -- PKEY_Devices_CategoryGroup_Desc + /// Description: Plural form of device category. + /// + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: {78C34FC8-104A-4ACA-9EA4-524D52996E57}, 94 + /// + public static ShellItemPropertyKey CategoryGroup => new ShellItemPropertyKey(new Guid("{78C34FC8-104A-4ACA-9EA4-524D52996E57}"), 94); + + /// + /// Name: System.Devices.CategoryPlural -- PKEY_Devices_Category_Desc_Plural + /// Description: Plural form of device category. + /// + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: {78C34FC8-104A-4ACA-9EA4-524D52996E57}, 92 + /// + public static ShellItemPropertyKey CategoryPlural => new ShellItemPropertyKey(new Guid("{78C34FC8-104A-4ACA-9EA4-524D52996E57}"), 92); + + /// + /// Name: System.Devices.ChargingState -- PKEY_Devices_ChargingState + /// Description: Boolean value representing if the device is currently charging. + /// + /// Type: Byte -- VT_UI1 + /// FormatID: {49CD1F76-5626-4B17-A4E8-18B4AA1A2213}, 11 + /// + public static ShellItemPropertyKey ChargingState => new ShellItemPropertyKey(new Guid("{49CD1F76-5626-4B17-A4E8-18B4AA1A2213}"), 11); + + /// + /// Name: System.Devices.Connected -- PKEY_Devices_IsConnected + /// Description: Device connection state. If VARIANT_TRUE, indicates the device is currently connected to the computer. + /// + /// Type: Boolean -- VT_BOOL + /// FormatID: {78C34FC8-104A-4ACA-9EA4-524D52996E57}, 55 + /// + public static ShellItemPropertyKey Connected => new ShellItemPropertyKey(new Guid("{78C34FC8-104A-4ACA-9EA4-524D52996E57}"), 55); + + /// + /// Name: System.Devices.ContainerId -- PKEY_Devices_ContainerId + /// Description: Device container ID. + /// + /// Type: Guid -- VT_CLSID + /// FormatID: {8C7ED206-3F8A-4827-B3AB-AE9E1FAEFC6C}, 2 + /// + public static ShellItemPropertyKey ContainerId => new ShellItemPropertyKey(new Guid("{8C7ED206-3F8A-4827-B3AB-AE9E1FAEFC6C}"), 2); + + /// + /// Name: System.Devices.DefaultTooltip -- PKEY_Devices_DefaultTooltip + /// Description: Tooltip for default state + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {880F70A2-6082-47AC-8AAB-A739D1A300C3}, 153 + /// + public static ShellItemPropertyKey DefaultTooltip => new ShellItemPropertyKey(new Guid("{880F70A2-6082-47AC-8AAB-A739D1A300C3}"), 153); + + /// + /// Name: System.Devices.DeviceDescription1 -- PKEY_Devices_DeviceDescription1 + /// Description: First line of descriptive text about the device. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {78C34FC8-104A-4ACA-9EA4-524D52996E57}, 81 + /// + public static ShellItemPropertyKey DeviceDescription1 => new ShellItemPropertyKey(new Guid("{78C34FC8-104A-4ACA-9EA4-524D52996E57}"), 81); + + /// + /// Name: System.Devices.DeviceDescription2 -- PKEY_Devices_DeviceDescription2 + /// Description: Second line of descriptive text about the device. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {78C34FC8-104A-4ACA-9EA4-524D52996E57}, 82 + /// + public static ShellItemPropertyKey DeviceDescription2 => new ShellItemPropertyKey(new Guid("{78C34FC8-104A-4ACA-9EA4-524D52996E57}"), 82); + + /// + /// Name: System.Devices.DiscoveryMethod -- PKEY_Devices_DiscoveryMethod + /// Description: Device discovery method. This indicates on what transport or physical connection the device is discovered. + /// + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: {78C34FC8-104A-4ACA-9EA4-524D52996E57}, 52 + /// + public static ShellItemPropertyKey DiscoveryMethod => new ShellItemPropertyKey(new Guid("{78C34FC8-104A-4ACA-9EA4-524D52996E57}"), 52); + + /// + /// Name: System.Devices.FriendlyName -- PKEY_Devices_FriendlyName + /// Description: Device friendly name. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {656A3BB3-ECC0-43FD-8477-4AE0404A96CD}, 12288 + /// + public static ShellItemPropertyKey FriendlyName => new ShellItemPropertyKey(new Guid("{656A3BB3-ECC0-43FD-8477-4AE0404A96CD}"), 12288); + + /// + /// Name: System.Devices.FunctionPaths -- PKEY_Devices_FunctionPaths + /// Description: Available functions for this device. + /// + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: {D08DD4C0-3A9E-462E-8290-7B636B2576B9}, 3 + /// + public static ShellItemPropertyKey FunctionPaths => new ShellItemPropertyKey(new Guid("{D08DD4C0-3A9E-462E-8290-7B636B2576B9}"), 3); + + /// + /// Name: System.Devices.InterfacePaths -- PKEY_Devices_InterfacePaths + /// Description: Available interfaces for this device. + /// + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: {D08DD4C0-3A9E-462E-8290-7B636B2576B9}, 2 + /// + public static ShellItemPropertyKey InterfacePaths => new ShellItemPropertyKey(new Guid("{D08DD4C0-3A9E-462E-8290-7B636B2576B9}"), 2); + + /// + /// Name: System.Devices.IsDefault -- PKEY_Devices_IsDefaultDevice + /// Description: If VARIANT_TRUE, the device is not working properly. + /// + /// Type: Boolean -- VT_BOOL + /// FormatID: {78C34FC8-104A-4ACA-9EA4-524D52996E57}, 86 + /// + public static ShellItemPropertyKey IsDefault => new ShellItemPropertyKey(new Guid("{78C34FC8-104A-4ACA-9EA4-524D52996E57}"), 86); + + /// + /// Name: System.Devices.IsNetworkConnected -- PKEY_Devices_IsNetworkDevice + /// Description: If VARIANT_TRUE, the device is not working properly. + /// + /// Type: Boolean -- VT_BOOL + /// FormatID: {78C34FC8-104A-4ACA-9EA4-524D52996E57}, 85 + /// + public static ShellItemPropertyKey IsNetworkConnected => new ShellItemPropertyKey(new Guid("{78C34FC8-104A-4ACA-9EA4-524D52996E57}"), 85); + + /// + /// Name: System.Devices.IsShared -- PKEY_Devices_IsSharedDevice + /// Description: If VARIANT_TRUE, the device is not working properly. + /// + /// Type: Boolean -- VT_BOOL + /// FormatID: {78C34FC8-104A-4ACA-9EA4-524D52996E57}, 84 + /// + public static ShellItemPropertyKey IsShared => new ShellItemPropertyKey(new Guid("{78C34FC8-104A-4ACA-9EA4-524D52996E57}"), 84); + + /// + /// Name: System.Devices.IsSoftwareInstalling -- PKEY_Devices_IsSoftwareInstalling + /// Description: If VARIANT_TRUE, the device installer is currently installing software. + /// + /// Type: Boolean -- VT_BOOL + /// FormatID: {83DA6326-97A6-4088-9453-A1923F573B29}, 9 + /// + public static ShellItemPropertyKey IsSoftwareInstalling => new ShellItemPropertyKey(new Guid("{83DA6326-97A6-4088-9453-A1923F573B29}"), 9); + + /// + /// Name: System.Devices.LaunchDeviceStageFromExplorer -- PKEY_Devices_LaunchDeviceStageFromExplorer + /// Description: Indicates whether to launch Device Stage or not + /// + /// Type: Boolean -- VT_BOOL + /// FormatID: {78C34FC8-104A-4ACA-9EA4-524D52996E57}, 77 + /// + public static ShellItemPropertyKey LaunchDeviceStageFromExplorer => new ShellItemPropertyKey(new Guid("{78C34FC8-104A-4ACA-9EA4-524D52996E57}"), 77); + + /// + /// Name: System.Devices.LocalMachine -- PKEY_Devices_IsLocalMachine + /// Description: If VARIANT_TRUE, the device in question is actually the computer. + /// + /// Type: Boolean -- VT_BOOL + /// FormatID: {78C34FC8-104A-4ACA-9EA4-524D52996E57}, 70 + /// + public static ShellItemPropertyKey LocalMachine => new ShellItemPropertyKey(new Guid("{78C34FC8-104A-4ACA-9EA4-524D52996E57}"), 70); + + /// + /// Name: System.Devices.Manufacturer -- PKEY_Devices_Manufacturer + /// Description: Device manufacturer. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {656A3BB3-ECC0-43FD-8477-4AE0404A96CD}, 8192 + /// + public static ShellItemPropertyKey Manufacturer => new ShellItemPropertyKey(new Guid("{656A3BB3-ECC0-43FD-8477-4AE0404A96CD}"), 8192); + + /// + /// Name: System.Devices.MissedCalls -- PKEY_Devices_MissedCalls + /// Description: Number of missed calls on the device. + /// + /// Type: Byte -- VT_UI1 + /// FormatID: {49CD1F76-5626-4B17-A4E8-18B4AA1A2213}, 5 + /// + public static ShellItemPropertyKey MissedCalls => new ShellItemPropertyKey(new Guid("{49CD1F76-5626-4B17-A4E8-18B4AA1A2213}"), 5); + + /// + /// Name: System.Devices.ModelName -- PKEY_Devices_ModelName + /// Description: Model name of the device. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {656A3BB3-ECC0-43FD-8477-4AE0404A96CD}, 8194 + /// + public static ShellItemPropertyKey ModelName => new ShellItemPropertyKey(new Guid("{656A3BB3-ECC0-43FD-8477-4AE0404A96CD}"), 8194); + + /// + /// Name: System.Devices.ModelNumber -- PKEY_Devices_ModelNumber + /// Description: Model number of the device. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {656A3BB3-ECC0-43FD-8477-4AE0404A96CD}, 8195 + /// + public static ShellItemPropertyKey ModelNumber => new ShellItemPropertyKey(new Guid("{656A3BB3-ECC0-43FD-8477-4AE0404A96CD}"), 8195); + + /// + /// Name: System.Devices.NetworkedTooltip -- PKEY_Devices_NetworkedTooltip + /// Description: Tooltip for connection state + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {880F70A2-6082-47AC-8AAB-A739D1A300C3}, 152 + /// + public static ShellItemPropertyKey NetworkedTooltip => new ShellItemPropertyKey(new Guid("{880F70A2-6082-47AC-8AAB-A739D1A300C3}"), 152); + + /// + /// Name: System.Devices.NetworkName -- PKEY_Devices_NetworkName + /// Description: Name of the device's network. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {49CD1F76-5626-4B17-A4E8-18B4AA1A2213}, 7 + /// + public static ShellItemPropertyKey NetworkName => new ShellItemPropertyKey(new Guid("{49CD1F76-5626-4B17-A4E8-18B4AA1A2213}"), 7); + + /// + /// Name: System.Devices.NetworkType -- PKEY_Devices_NetworkType + /// Description: String representing the type of the device's network. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {49CD1F76-5626-4B17-A4E8-18B4AA1A2213}, 8 + /// + public static ShellItemPropertyKey NetworkType => new ShellItemPropertyKey(new Guid("{49CD1F76-5626-4B17-A4E8-18B4AA1A2213}"), 8); + + /// + /// Name: System.Devices.NewPictures -- PKEY_Devices_NewPictures + /// Description: Number of new pictures on the device. + /// + /// Type: UInt16 -- VT_UI2 + /// FormatID: {49CD1F76-5626-4B17-A4E8-18B4AA1A2213}, 4 + /// + public static ShellItemPropertyKey NewPictures => new ShellItemPropertyKey(new Guid("{49CD1F76-5626-4B17-A4E8-18B4AA1A2213}"), 4); + + /// + /// Name: System.Devices.Notification -- PKEY_Devices_Notification + /// Description: Device Notification Property. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {06704B0C-E830-4C81-9178-91E4E95A80A0}, 3 + /// + public static ShellItemPropertyKey Notification => new ShellItemPropertyKey(new Guid("{06704B0C-E830-4C81-9178-91E4E95A80A0}"), 3); + + /// + /// Name: System.Devices.NotificationStore -- PKEY_Devices_NotificationStore + /// Description: Device Notification Store. + /// + /// Type: Object -- VT_UNKNOWN + /// FormatID: {06704B0C-E830-4C81-9178-91E4E95A80A0}, 2 + /// + public static ShellItemPropertyKey NotificationStore => new ShellItemPropertyKey(new Guid("{06704B0C-E830-4C81-9178-91E4E95A80A0}"), 2); + + /// + /// Name: System.Devices.NotWorkingProperly -- PKEY_Devices_IsNotWorkingProperly + /// Description: If VARIANT_TRUE, the device is not working properly. + /// + /// Type: Boolean -- VT_BOOL + /// FormatID: {78C34FC8-104A-4ACA-9EA4-524D52996E57}, 83 + /// + public static ShellItemPropertyKey NotWorkingProperly => new ShellItemPropertyKey(new Guid("{78C34FC8-104A-4ACA-9EA4-524D52996E57}"), 83); + + /// + /// Name: System.Devices.Paired -- PKEY_Devices_IsPaired + /// Description: Device paired state. If VARIANT_TRUE, indicates the device is not paired with the computer. + /// + /// Type: Boolean -- VT_BOOL + /// FormatID: {78C34FC8-104A-4ACA-9EA4-524D52996E57}, 56 + /// + public static ShellItemPropertyKey Paired => new ShellItemPropertyKey(new Guid("{78C34FC8-104A-4ACA-9EA4-524D52996E57}"), 56); + + /// + /// Name: System.Devices.PrimaryCategory -- PKEY_Devices_PrimaryCategory + /// Description: Primary category group for this device. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {D08DD4C0-3A9E-462E-8290-7B636B2576B9}, 10 + /// + public static ShellItemPropertyKey PrimaryCategory => new ShellItemPropertyKey(new Guid("{D08DD4C0-3A9E-462E-8290-7B636B2576B9}"), 10); + + /// + /// Name: System.Devices.Roaming -- PKEY_Devices_Roaming + /// Description: Status indicator used to indicate if the device is roaming. + /// + /// Type: Byte -- VT_UI1 + /// FormatID: {49CD1F76-5626-4B17-A4E8-18B4AA1A2213}, 9 + /// + public static ShellItemPropertyKey Roaming => new ShellItemPropertyKey(new Guid("{49CD1F76-5626-4B17-A4E8-18B4AA1A2213}"), 9); + + /// + /// Name: System.Devices.SafeRemovalRequired -- PKEY_Devices_SafeRemovalRequired + /// Description: Indicates if a device requires safe removal or not + /// + /// Type: Boolean -- VT_BOOL + /// FormatID: {AFD97640-86A3-4210-B67C-289C41AABE55}, 2 + /// + public static ShellItemPropertyKey SafeRemovalRequired => new ShellItemPropertyKey(new Guid("{AFD97640-86A3-4210-B67C-289C41AABE55}"), 2); + + /// + /// Name: System.Devices.SharedTooltip -- PKEY_Devices_SharedTooltip + /// Description: Tooltip for sharing state + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {880F70A2-6082-47AC-8AAB-A739D1A300C3}, 151 + /// + public static ShellItemPropertyKey SharedTooltip => new ShellItemPropertyKey(new Guid("{880F70A2-6082-47AC-8AAB-A739D1A300C3}"), 151); + + /// + /// Name: System.Devices.SignalStrength -- PKEY_Devices_SignalStrength + /// Description: Device signal strength. + /// + /// Type: Byte -- VT_UI1 + /// FormatID: {49CD1F76-5626-4B17-A4E8-18B4AA1A2213}, 2 + /// + public static ShellItemPropertyKey SignalStrength => new ShellItemPropertyKey(new Guid("{49CD1F76-5626-4B17-A4E8-18B4AA1A2213}"), 2); + + /// + /// Name: System.Devices.Status1 -- PKEY_Devices_Status1 + /// Description: 1st line of device status. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {D08DD4C0-3A9E-462E-8290-7B636B2576B9}, 257 + /// + public static ShellItemPropertyKey Status1 => new ShellItemPropertyKey(new Guid("{D08DD4C0-3A9E-462E-8290-7B636B2576B9}"), 257); + + /// + /// Name: System.Devices.Status2 -- PKEY_Devices_Status2 + /// Description: 2nd line of device status. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {D08DD4C0-3A9E-462E-8290-7B636B2576B9}, 258 + /// + public static ShellItemPropertyKey Status2 => new ShellItemPropertyKey(new Guid("{D08DD4C0-3A9E-462E-8290-7B636B2576B9}"), 258); + + /// + /// Name: System.Devices.StorageCapacity -- PKEY_Devices_StorageCapacity + /// Description: Total storage capacity of the device. + /// + /// Type: UInt64 -- VT_UI8 + /// FormatID: {49CD1F76-5626-4B17-A4E8-18B4AA1A2213}, 12 + /// + public static ShellItemPropertyKey StorageCapacity => new ShellItemPropertyKey(new Guid("{49CD1F76-5626-4B17-A4E8-18B4AA1A2213}"), 12); + + /// + /// Name: System.Devices.StorageFreeSpace -- PKEY_Devices_StorageFreeSpace + /// Description: Total free space of the storage of the device. + /// + /// Type: UInt64 -- VT_UI8 + /// FormatID: {49CD1F76-5626-4B17-A4E8-18B4AA1A2213}, 13 + /// + public static ShellItemPropertyKey StorageFreeSpace => new ShellItemPropertyKey(new Guid("{49CD1F76-5626-4B17-A4E8-18B4AA1A2213}"), 13); + + /// + /// Name: System.Devices.StorageFreeSpacePercent -- PKEY_Devices_StorageFreeSpacePercent + /// Description: Total free space of the storage of the device as a percentage. + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {49CD1F76-5626-4B17-A4E8-18B4AA1A2213}, 14 + /// + public static ShellItemPropertyKey StorageFreeSpacePercent => new ShellItemPropertyKey(new Guid("{49CD1F76-5626-4B17-A4E8-18B4AA1A2213}"), 14); + + /// + /// Name: System.Devices.TextMessages -- PKEY_Devices_TextMessages + /// Description: Number of unread text messages on the device. + /// + /// Type: Byte -- VT_UI1 + /// FormatID: {49CD1F76-5626-4B17-A4E8-18B4AA1A2213}, 3 + /// + public static ShellItemPropertyKey TextMessages => new ShellItemPropertyKey(new Guid("{49CD1F76-5626-4B17-A4E8-18B4AA1A2213}"), 3); + + /// + /// Name: System.Devices.Voicemail -- PKEY_Devices_Voicemail + /// Description: Status indicator used to indicate if the device has voicemail. + /// + /// Type: Byte -- VT_UI1 + /// FormatID: {49CD1F76-5626-4B17-A4E8-18B4AA1A2213}, 6 + /// + public static ShellItemPropertyKey Voicemail => new ShellItemPropertyKey(new Guid("{49CD1F76-5626-4B17-A4E8-18B4AA1A2213}"), 6); + + /// Notifications Properties + public static class Notifications + { + /// + /// Name: System.Devices.Notifications.LowBattery -- PKEY_Devices_Notification_LowBattery + /// Description: Device Low Battery Notification. + /// + /// Type: Byte -- VT_UI1 + /// FormatID: {C4C07F2B-8524-4E66-AE3A-A6235F103BEB}, 2 + /// + public static ShellItemPropertyKey LowBattery => new ShellItemPropertyKey(new Guid("{C4C07F2B-8524-4E66-AE3A-A6235F103BEB}"), 2); + + /// + /// Name: System.Devices.Notifications.MissedCall -- PKEY_Devices_Notification_MissedCall + /// Description: Device Missed Call Notification. + /// + /// Type: Byte -- VT_UI1 + /// FormatID: {6614EF48-4EFE-4424-9EDA-C79F404EDF3E}, 2 + /// + public static ShellItemPropertyKey MissedCall => new ShellItemPropertyKey(new Guid("{6614EF48-4EFE-4424-9EDA-C79F404EDF3E}"), 2); + + /// + /// Name: System.Devices.Notifications.NewMessage -- PKEY_Devices_Notification_NewMessage + /// Description: Device New Message Notification. + /// + /// Type: Byte -- VT_UI1 + /// FormatID: {2BE9260A-2012-4742-A555-F41B638B7DCB}, 2 + /// + public static ShellItemPropertyKey NewMessage => new ShellItemPropertyKey(new Guid("{2BE9260A-2012-4742-A555-F41B638B7DCB}"), 2); + + /// + /// Name: System.Devices.Notifications.NewVoicemail -- PKEY_Devices_Notification_NewVoicemail + /// Description: Device Voicemail Notification. + /// + /// Type: Byte -- VT_UI1 + /// FormatID: {59569556-0A08-4212-95B9-FAE2AD6413DB}, 2 + /// + public static ShellItemPropertyKey NewVoicemail => new ShellItemPropertyKey(new Guid("{59569556-0A08-4212-95B9-FAE2AD6413DB}"), 2); + + /// + /// Name: System.Devices.Notifications.StorageFull -- PKEY_Devices_Notification_StorageFull + /// Description: Device Storage Full Notification. + /// + /// Type: UInt64 -- VT_UI8 + /// FormatID: {A0E00EE1-F0C7-4D41-B8E7-26A7BD8D38B0}, 2 + /// + public static ShellItemPropertyKey StorageFull => new ShellItemPropertyKey(new Guid("{A0E00EE1-F0C7-4D41-B8E7-26A7BD8D38B0}"), 2); + + /// + /// Name: System.Devices.Notifications.StorageFullLinkText -- PKEY_Devices_Notification_StorageFullLinkText + /// Description: Link Text for the Device Storage Full Notification. + /// + /// Type: UInt64 -- VT_UI8 + /// FormatID: {A0E00EE1-F0C7-4D41-B8E7-26A7BD8D38B0}, 3 + /// + public static ShellItemPropertyKey StorageFullLinkText => new ShellItemPropertyKey(new Guid("{A0E00EE1-F0C7-4D41-B8E7-26A7BD8D38B0}"), 3); + } + } + + /// Document Properties + public static class Document + { + /// + /// Name: System.Document.ByteCount -- PKEY_Document_ByteCount + /// Description: + /// + /// Type: Int32 -- VT_I4 + /// FormatID: (FMTID_DocumentSummaryInformation) {D5CDD502-2E9C-101B-9397-08002B2CF9AE}, 4 (PIDDSI_BYTECOUNT) + /// + public static ShellItemPropertyKey ByteCount => new ShellItemPropertyKey(new Guid("{D5CDD502-2E9C-101B-9397-08002B2CF9AE}"), 4); + + /// + /// Name: System.Document.CharacterCount -- PKEY_Document_CharacterCount + /// Description: + /// + /// Type: Int32 -- VT_I4 + /// FormatID: (FMTID_SummaryInformation) {F29F85E0-4FF9-1068-AB91-08002B27B3D9}, 16 (PIDSI_CHARCOUNT) + /// + public static ShellItemPropertyKey CharacterCount => new ShellItemPropertyKey(new Guid("{F29F85E0-4FF9-1068-AB91-08002B27B3D9}"), 16); + + /// + /// Name: System.Document.ClientID -- PKEY_Document_ClientID + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {276D7BB0-5B34-4FB0-AA4B-158ED12A1809}, 100 + /// + public static ShellItemPropertyKey ClientID => new ShellItemPropertyKey(new Guid("{276D7BB0-5B34-4FB0-AA4B-158ED12A1809}"), 100); + + /// + /// Name: System.Document.Contributor -- PKEY_Document_Contributor + /// Description: + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: {F334115E-DA1B-4509-9B3D-119504DC7ABB}, 100 + /// + public static ShellItemPropertyKey Contributor => new ShellItemPropertyKey(new Guid("{F334115E-DA1B-4509-9B3D-119504DC7ABB}"), 100); + + /// + /// Name: System.Document.DateCreated -- PKEY_Document_DateCreated + /// Description: This property is stored in the document, not obtained from the file system. + /// + /// Type: DateTime -- VT_FILETIME (For variants: VT_DATE) + /// FormatID: (FMTID_SummaryInformation) {F29F85E0-4FF9-1068-AB91-08002B27B3D9}, 12 (PIDSI_CREATE_DTM) + /// + public static ShellItemPropertyKey DateCreated => new ShellItemPropertyKey(new Guid("{F29F85E0-4FF9-1068-AB91-08002B27B3D9}"), 12); + + /// + /// Name: System.Document.DatePrinted -- PKEY_Document_DatePrinted + /// Description: Legacy name: "DocLastPrinted". + /// + /// Type: DateTime -- VT_FILETIME (For variants: VT_DATE) + /// FormatID: (FMTID_SummaryInformation) {F29F85E0-4FF9-1068-AB91-08002B27B3D9}, 11 (PIDSI_LASTPRINTED) + /// + public static ShellItemPropertyKey DatePrinted => new ShellItemPropertyKey(new Guid("{F29F85E0-4FF9-1068-AB91-08002B27B3D9}"), 11); + + /// + /// Name: System.Document.DateSaved -- PKEY_Document_DateSaved + /// Description: Legacy name: "DocLastSavedTm". + /// + /// Type: DateTime -- VT_FILETIME (For variants: VT_DATE) + /// FormatID: (FMTID_SummaryInformation) {F29F85E0-4FF9-1068-AB91-08002B27B3D9}, 13 (PIDSI_LASTSAVE_DTM) + /// + public static ShellItemPropertyKey DateSaved => new ShellItemPropertyKey(new Guid("{F29F85E0-4FF9-1068-AB91-08002B27B3D9}"), 13); + + /// + /// Name: System.Document.Division -- PKEY_Document_Division + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {1E005EE6-BF27-428B-B01C-79676ACD2870}, 100 + /// + public static ShellItemPropertyKey Division => new ShellItemPropertyKey(new Guid("{1E005EE6-BF27-428B-B01C-79676ACD2870}"), 100); + + /// + /// Name: System.Document.DocumentID -- PKEY_Document_DocumentID + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {E08805C8-E395-40DF-80D2-54F0D6C43154}, 100 + /// + public static ShellItemPropertyKey DocumentID => new ShellItemPropertyKey(new Guid("{E08805C8-E395-40DF-80D2-54F0D6C43154}"), 100); + + /// + /// Name: System.Document.HiddenSlideCount -- PKEY_Document_HiddenSlideCount + /// Description: + /// + /// Type: Int32 -- VT_I4 + /// FormatID: (FMTID_DocumentSummaryInformation) {D5CDD502-2E9C-101B-9397-08002B2CF9AE}, 9 (PIDDSI_HIDDENCOUNT) + /// + public static ShellItemPropertyKey HiddenSlideCount => new ShellItemPropertyKey(new Guid("{D5CDD502-2E9C-101B-9397-08002B2CF9AE}"), 9); + + /// + /// Name: System.Document.LastAuthor -- PKEY_Document_LastAuthor + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_SummaryInformation) {F29F85E0-4FF9-1068-AB91-08002B27B3D9}, 8 (PIDSI_LASTAUTHOR) + /// + public static ShellItemPropertyKey LastAuthor => new ShellItemPropertyKey(new Guid("{F29F85E0-4FF9-1068-AB91-08002B27B3D9}"), 8); + + /// + /// Name: System.Document.LineCount -- PKEY_Document_LineCount + /// Description: + /// + /// Type: Int32 -- VT_I4 + /// FormatID: (FMTID_DocumentSummaryInformation) {D5CDD502-2E9C-101B-9397-08002B2CF9AE}, 5 (PIDDSI_LINECOUNT) + /// + public static ShellItemPropertyKey LineCount => new ShellItemPropertyKey(new Guid("{D5CDD502-2E9C-101B-9397-08002B2CF9AE}"), 5); + + /// + /// Name: System.Document.Manager -- PKEY_Document_Manager + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_DocumentSummaryInformation) {D5CDD502-2E9C-101B-9397-08002B2CF9AE}, 14 (PIDDSI_MANAGER) + /// + public static ShellItemPropertyKey Manager => new ShellItemPropertyKey(new Guid("{D5CDD502-2E9C-101B-9397-08002B2CF9AE}"), 14); + + /// + /// Name: System.Document.MultimediaClipCount -- PKEY_Document_MultimediaClipCount + /// Description: + /// + /// Type: Int32 -- VT_I4 + /// FormatID: (FMTID_DocumentSummaryInformation) {D5CDD502-2E9C-101B-9397-08002B2CF9AE}, 10 (PIDDSI_MMCLIPCOUNT) + /// + public static ShellItemPropertyKey MultimediaClipCount => new ShellItemPropertyKey(new Guid("{D5CDD502-2E9C-101B-9397-08002B2CF9AE}"), 10); + + /// + /// Name: System.Document.NoteCount -- PKEY_Document_NoteCount + /// Description: + /// + /// Type: Int32 -- VT_I4 + /// FormatID: (FMTID_DocumentSummaryInformation) {D5CDD502-2E9C-101B-9397-08002B2CF9AE}, 8 (PIDDSI_NOTECOUNT) + /// + public static ShellItemPropertyKey NoteCount => new ShellItemPropertyKey(new Guid("{D5CDD502-2E9C-101B-9397-08002B2CF9AE}"), 8); + + /// + /// Name: System.Document.PageCount -- PKEY_Document_PageCount + /// Description: + /// + /// Type: Int32 -- VT_I4 + /// FormatID: (FMTID_SummaryInformation) {F29F85E0-4FF9-1068-AB91-08002B27B3D9}, 14 (PIDSI_PAGECOUNT) + /// + public static ShellItemPropertyKey PageCount => new ShellItemPropertyKey(new Guid("{F29F85E0-4FF9-1068-AB91-08002B27B3D9}"), 14); + + /// + /// Name: System.Document.ParagraphCount -- PKEY_Document_ParagraphCount + /// Description: + /// + /// Type: Int32 -- VT_I4 + /// FormatID: (FMTID_DocumentSummaryInformation) {D5CDD502-2E9C-101B-9397-08002B2CF9AE}, 6 (PIDDSI_PARCOUNT) + /// + public static ShellItemPropertyKey ParagraphCount => new ShellItemPropertyKey(new Guid("{D5CDD502-2E9C-101B-9397-08002B2CF9AE}"), 6); + + /// + /// Name: System.Document.PresentationFormat -- PKEY_Document_PresentationFormat + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_DocumentSummaryInformation) {D5CDD502-2E9C-101B-9397-08002B2CF9AE}, 3 (PIDDSI_PRESFORMAT) + /// + public static ShellItemPropertyKey PresentationFormat => new ShellItemPropertyKey(new Guid("{D5CDD502-2E9C-101B-9397-08002B2CF9AE}"), 3); + + /// + /// Name: System.Document.RevisionNumber -- PKEY_Document_RevisionNumber + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_SummaryInformation) {F29F85E0-4FF9-1068-AB91-08002B27B3D9}, 9 (PIDSI_REVNUMBER) + /// + public static ShellItemPropertyKey RevisionNumber => new ShellItemPropertyKey(new Guid("{F29F85E0-4FF9-1068-AB91-08002B27B3D9}"), 9); + + /// + /// Name: System.Document.Security -- PKEY_Document_Security + /// Description: Access control information, from SummaryInfo propset + /// + /// Type: Int32 -- VT_I4 + /// FormatID: (FMTID_SummaryInformation) {F29F85E0-4FF9-1068-AB91-08002B27B3D9}, 19 + /// + public static ShellItemPropertyKey Security => new ShellItemPropertyKey(new Guid("{F29F85E0-4FF9-1068-AB91-08002B27B3D9}"), 19); + + /// + /// Name: System.Document.SlideCount -- PKEY_Document_SlideCount + /// Description: + /// + /// Type: Int32 -- VT_I4 + /// FormatID: (FMTID_DocumentSummaryInformation) {D5CDD502-2E9C-101B-9397-08002B2CF9AE}, 7 (PIDDSI_SLIDECOUNT) + /// + public static ShellItemPropertyKey SlideCount => new ShellItemPropertyKey(new Guid("{D5CDD502-2E9C-101B-9397-08002B2CF9AE}"), 7); + + /// + /// Name: System.Document.Template -- PKEY_Document_Template + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_SummaryInformation) {F29F85E0-4FF9-1068-AB91-08002B27B3D9}, 7 (PIDSI_TEMPLATE) + /// + public static ShellItemPropertyKey Template => new ShellItemPropertyKey(new Guid("{F29F85E0-4FF9-1068-AB91-08002B27B3D9}"), 7); + + /// + /// Name: System.Document.TotalEditingTime -- PKEY_Document_TotalEditingTime + /// Description: 100ns units, not milliseconds. VT_FILETIME for IPropertySetStorage handlers (legacy) + /// + /// Type: UInt64 -- VT_UI8 + /// FormatID: (FMTID_SummaryInformation) {F29F85E0-4FF9-1068-AB91-08002B27B3D9}, 10 (PIDSI_EDITTIME) + /// + public static ShellItemPropertyKey TotalEditingTime => new ShellItemPropertyKey(new Guid("{F29F85E0-4FF9-1068-AB91-08002B27B3D9}"), 10); + + /// + /// Name: System.Document.Version -- PKEY_Document_Version + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_DocumentSummaryInformation) {D5CDD502-2E9C-101B-9397-08002B2CF9AE}, 29 + /// + public static ShellItemPropertyKey Version => new ShellItemPropertyKey(new Guid("{D5CDD502-2E9C-101B-9397-08002B2CF9AE}"), 29); + + /// + /// Name: System.Document.WordCount -- PKEY_Document_WordCount + /// Description: + /// + /// Type: Int32 -- VT_I4 + /// FormatID: (FMTID_SummaryInformation) {F29F85E0-4FF9-1068-AB91-08002B27B3D9}, 15 (PIDSI_WORDCOUNT) + /// + public static ShellItemPropertyKey WordCount => new ShellItemPropertyKey(new Guid("{F29F85E0-4FF9-1068-AB91-08002B27B3D9}"), 15); + } + + /// DRM Properties + public static class DRM + { + /// + /// Name: System.DRM.DatePlayExpires -- PKEY_DRM_DatePlayExpires + /// Description: Indicates when play expires for digital rights management. + /// + /// Type: DateTime -- VT_FILETIME (For variants: VT_DATE) + /// FormatID: (FMTID_DRM) {AEAC19E4-89AE-4508-B9B7-BB867ABEE2ED}, 6 (PIDDRSI_PLAYEXPIRES) + /// + public static ShellItemPropertyKey DatePlayExpires => new ShellItemPropertyKey(new Guid("{AEAC19E4-89AE-4508-B9B7-BB867ABEE2ED}"), 6); + + /// + /// Name: System.DRM.DatePlayStarts -- PKEY_DRM_DatePlayStarts + /// Description: Indicates when play starts for digital rights management. + /// + /// Type: DateTime -- VT_FILETIME (For variants: VT_DATE) + /// FormatID: (FMTID_DRM) {AEAC19E4-89AE-4508-B9B7-BB867ABEE2ED}, 5 (PIDDRSI_PLAYSTARTS) + /// + public static ShellItemPropertyKey DatePlayStarts => new ShellItemPropertyKey(new Guid("{AEAC19E4-89AE-4508-B9B7-BB867ABEE2ED}"), 5); + + /// + /// Name: System.DRM.Description -- PKEY_DRM_Description + /// Description: Displays the description for digital rights management. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_DRM) {AEAC19E4-89AE-4508-B9B7-BB867ABEE2ED}, 3 (PIDDRSI_DESCRIPTION) + /// + public static ShellItemPropertyKey Description => new ShellItemPropertyKey(new Guid("{AEAC19E4-89AE-4508-B9B7-BB867ABEE2ED}"), 3); + + /// + /// Name: System.DRM.IsProtected -- PKEY_DRM_IsProtected + /// Description: + /// + /// Type: Boolean -- VT_BOOL + /// FormatID: (FMTID_DRM) {AEAC19E4-89AE-4508-B9B7-BB867ABEE2ED}, 2 (PIDDRSI_PROTECTED) + /// + public static ShellItemPropertyKey IsProtected => new ShellItemPropertyKey(new Guid("{AEAC19E4-89AE-4508-B9B7-BB867ABEE2ED}"), 2); + + /// + /// Name: System.DRM.PlayCount -- PKEY_DRM_PlayCount + /// Description: Indicates the play count for digital rights management. + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: (FMTID_DRM) {AEAC19E4-89AE-4508-B9B7-BB867ABEE2ED}, 4 (PIDDRSI_PLAYCOUNT) + /// + public static ShellItemPropertyKey PlayCount => new ShellItemPropertyKey(new Guid("{AEAC19E4-89AE-4508-B9B7-BB867ABEE2ED}"), 4); + } + + /// GPS Properties + public static class GPS + { + /// + /// Name: System.GPS.Altitude -- PKEY_GPS_Altitude + /// Description: Indicates the altitude based on the reference in PKEY_GPS_AltitudeRef. Calculated from PKEY_GPS_AltitudeNumerator and + ///PKEY_GPS_AltitudeDenominator + /// + /// Type: Double -- VT_R8 + /// FormatID: {827EDB4F-5B73-44A7-891D-FDFFABEA35CA}, 100 + /// + public static ShellItemPropertyKey Altitude => new ShellItemPropertyKey(new Guid("{827EDB4F-5B73-44A7-891D-FDFFABEA35CA}"), 100); + + /// + /// Name: System.GPS.AltitudeDenominator -- PKEY_GPS_AltitudeDenominator + /// Description: Denominator of PKEY_GPS_Altitude + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {78342DCB-E358-4145-AE9A-6BFE4E0F9F51}, 100 + /// + public static ShellItemPropertyKey AltitudeDenominator => new ShellItemPropertyKey(new Guid("{78342DCB-E358-4145-AE9A-6BFE4E0F9F51}"), 100); + + /// + /// Name: System.GPS.AltitudeNumerator -- PKEY_GPS_AltitudeNumerator + /// Description: Numerator of PKEY_GPS_Altitude + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {2DAD1EB7-816D-40D3-9EC3-C9773BE2AADE}, 100 + /// + public static ShellItemPropertyKey AltitudeNumerator => new ShellItemPropertyKey(new Guid("{2DAD1EB7-816D-40D3-9EC3-C9773BE2AADE}"), 100); + + /// + /// Name: System.GPS.AltitudeRef -- PKEY_GPS_AltitudeRef + /// Description: Indicates the reference for the altitude property. (eg: above sea level, below sea level, absolute value) + /// + /// Type: Byte -- VT_UI1 + /// FormatID: {46AC629D-75EA-4515-867F-6DC4321C5844}, 100 + /// + public static ShellItemPropertyKey AltitudeRef => new ShellItemPropertyKey(new Guid("{46AC629D-75EA-4515-867F-6DC4321C5844}"), 100); + + /// + /// Name: System.GPS.AreaInformation -- PKEY_GPS_AreaInformation + /// Description: Represents the name of the GPS area + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {972E333E-AC7E-49F1-8ADF-A70D07A9BCAB}, 100 + /// + public static ShellItemPropertyKey AreaInformation => new ShellItemPropertyKey(new Guid("{972E333E-AC7E-49F1-8ADF-A70D07A9BCAB}"), 100); + + /// + /// Name: System.GPS.Date -- PKEY_GPS_Date + /// Description: Date and time of the GPS record + /// + /// Type: DateTime -- VT_FILETIME (For variants: VT_DATE) + /// FormatID: {3602C812-0F3B-45F0-85AD-603468D69423}, 100 + /// + public static ShellItemPropertyKey Date => new ShellItemPropertyKey(new Guid("{3602C812-0F3B-45F0-85AD-603468D69423}"), 100); + + /// + /// Name: System.GPS.DestBearing -- PKEY_GPS_DestBearing + /// Description: Indicates the bearing to the destination point. Calculated from PKEY_GPS_DestBearingNumerator and + ///PKEY_GPS_DestBearingDenominator. + /// + /// Type: Double -- VT_R8 + /// FormatID: {C66D4B3C-E888-47CC-B99F-9DCA3EE34DEA}, 100 + /// + public static ShellItemPropertyKey DestBearing => new ShellItemPropertyKey(new Guid("{C66D4B3C-E888-47CC-B99F-9DCA3EE34DEA}"), 100); + + /// + /// Name: System.GPS.DestBearingDenominator -- PKEY_GPS_DestBearingDenominator + /// Description: Denominator of PKEY_GPS_DestBearing + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {7ABCF4F8-7C3F-4988-AC91-8D2C2E97ECA5}, 100 + /// + public static ShellItemPropertyKey DestBearingDenominator => new ShellItemPropertyKey(new Guid("{7ABCF4F8-7C3F-4988-AC91-8D2C2E97ECA5}"), 100); + + /// + /// Name: System.GPS.DestBearingNumerator -- PKEY_GPS_DestBearingNumerator + /// Description: Numerator of PKEY_GPS_DestBearing + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {BA3B1DA9-86EE-4B5D-A2A4-A271A429F0CF}, 100 + /// + public static ShellItemPropertyKey DestBearingNumerator => new ShellItemPropertyKey(new Guid("{BA3B1DA9-86EE-4B5D-A2A4-A271A429F0CF}"), 100); + + /// + /// Name: System.GPS.DestBearingRef -- PKEY_GPS_DestBearingRef + /// Description: Indicates the reference used for the giving the bearing to the destination point. (eg: true direction, magnetic direction) + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {9AB84393-2A0F-4B75-BB22-7279786977CB}, 100 + /// + public static ShellItemPropertyKey DestBearingRef => new ShellItemPropertyKey(new Guid("{9AB84393-2A0F-4B75-BB22-7279786977CB}"), 100); + + /// + /// Name: System.GPS.DestDistance -- PKEY_GPS_DestDistance + /// Description: Indicates the distance to the destination point. Calculated from PKEY_GPS_DestDistanceNumerator and + ///PKEY_GPS_DestDistanceDenominator. + /// + /// Type: Double -- VT_R8 + /// FormatID: {A93EAE04-6804-4F24-AC81-09B266452118}, 100 + /// + public static ShellItemPropertyKey DestDistance => new ShellItemPropertyKey(new Guid("{A93EAE04-6804-4F24-AC81-09B266452118}"), 100); + + /// + /// Name: System.GPS.DestDistanceDenominator -- PKEY_GPS_DestDistanceDenominator + /// Description: Denominator of PKEY_GPS_DestDistance + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {9BC2C99B-AC71-4127-9D1C-2596D0D7DCB7}, 100 + /// + public static ShellItemPropertyKey DestDistanceDenominator => new ShellItemPropertyKey(new Guid("{9BC2C99B-AC71-4127-9D1C-2596D0D7DCB7}"), 100); + + /// + /// Name: System.GPS.DestDistanceNumerator -- PKEY_GPS_DestDistanceNumerator + /// Description: Numerator of PKEY_GPS_DestDistance + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {2BDA47DA-08C6-4FE1-80BC-A72FC517C5D0}, 100 + /// + public static ShellItemPropertyKey DestDistanceNumerator => new ShellItemPropertyKey(new Guid("{2BDA47DA-08C6-4FE1-80BC-A72FC517C5D0}"), 100); + + /// + /// Name: System.GPS.DestDistanceRef -- PKEY_GPS_DestDistanceRef + /// Description: Indicates the unit used to express the distance to the destination. (eg: kilometers, miles, knots) + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {ED4DF2D3-8695-450B-856F-F5C1C53ACB66}, 100 + /// + public static ShellItemPropertyKey DestDistanceRef => new ShellItemPropertyKey(new Guid("{ED4DF2D3-8695-450B-856F-F5C1C53ACB66}"), 100); + + /// + /// Name: System.GPS.DestLatitude -- PKEY_GPS_DestLatitude + /// Description: Indicates the latitude of the destination point. This is an array of three values. Index 0 is the degrees, index 1 + ///is the minutes, index 2 is the seconds. Each is calculated from the values in PKEY_GPS_DestLatitudeNumerator and + ///PKEY_GPS_DestLatitudeDenominator. + /// + /// Type: Multivalue Double -- VT_VECTOR | VT_R8 (For variants: VT_ARRAY | VT_R8) + /// FormatID: {9D1D7CC5-5C39-451C-86B3-928E2D18CC47}, 100 + /// + public static ShellItemPropertyKey DestLatitude => new ShellItemPropertyKey(new Guid("{9D1D7CC5-5C39-451C-86B3-928E2D18CC47}"), 100); + + /// + /// Name: System.GPS.DestLatitudeDenominator -- PKEY_GPS_DestLatitudeDenominator + /// Description: Denominator of PKEY_GPS_DestLatitude + /// + /// Type: Multivalue UInt32 -- VT_VECTOR | VT_UI4 (For variants: VT_ARRAY | VT_UI4) + /// FormatID: {3A372292-7FCA-49A7-99D5-E47BB2D4E7AB}, 100 + /// + public static ShellItemPropertyKey DestLatitudeDenominator => new ShellItemPropertyKey(new Guid("{3A372292-7FCA-49A7-99D5-E47BB2D4E7AB}"), 100); + + /// + /// Name: System.GPS.DestLatitudeNumerator -- PKEY_GPS_DestLatitudeNumerator + /// Description: Numerator of PKEY_GPS_DestLatitude + /// + /// Type: Multivalue UInt32 -- VT_VECTOR | VT_UI4 (For variants: VT_ARRAY | VT_UI4) + /// FormatID: {ECF4B6F6-D5A6-433C-BB92-4076650FC890}, 100 + /// + public static ShellItemPropertyKey DestLatitudeNumerator => new ShellItemPropertyKey(new Guid("{ECF4B6F6-D5A6-433C-BB92-4076650FC890}"), 100); + + /// + /// Name: System.GPS.DestLatitudeRef -- PKEY_GPS_DestLatitudeRef + /// Description: Indicates whether the latitude destination point is north or south latitude + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {CEA820B9-CE61-4885-A128-005D9087C192}, 100 + /// + public static ShellItemPropertyKey DestLatitudeRef => new ShellItemPropertyKey(new Guid("{CEA820B9-CE61-4885-A128-005D9087C192}"), 100); + + /// + /// Name: System.GPS.DestLongitude -- PKEY_GPS_DestLongitude + /// Description: Indicates the latitude of the destination point. This is an array of three values. Index 0 is the degrees, index 1 + ///is the minutes, index 2 is the seconds. Each is calculated from the values in PKEY_GPS_DestLongitudeNumerator and + ///PKEY_GPS_DestLongitudeDenominator. + /// + /// Type: Multivalue Double -- VT_VECTOR | VT_R8 (For variants: VT_ARRAY | VT_R8) + /// FormatID: {47A96261-CB4C-4807-8AD3-40B9D9DBC6BC}, 100 + /// + public static ShellItemPropertyKey DestLongitude => new ShellItemPropertyKey(new Guid("{47A96261-CB4C-4807-8AD3-40B9D9DBC6BC}"), 100); + + /// + /// Name: System.GPS.DestLongitudeDenominator -- PKEY_GPS_DestLongitudeDenominator + /// Description: Denominator of PKEY_GPS_DestLongitude + /// + /// Type: Multivalue UInt32 -- VT_VECTOR | VT_UI4 (For variants: VT_ARRAY | VT_UI4) + /// FormatID: {425D69E5-48AD-4900-8D80-6EB6B8D0AC86}, 100 + /// + public static ShellItemPropertyKey DestLongitudeDenominator => new ShellItemPropertyKey(new Guid("{425D69E5-48AD-4900-8D80-6EB6B8D0AC86}"), 100); + + /// + /// Name: System.GPS.DestLongitudeNumerator -- PKEY_GPS_DestLongitudeNumerator + /// Description: Numerator of PKEY_GPS_DestLongitude + /// + /// Type: Multivalue UInt32 -- VT_VECTOR | VT_UI4 (For variants: VT_ARRAY | VT_UI4) + /// FormatID: {A3250282-FB6D-48D5-9A89-DBCACE75CCCF}, 100 + /// + public static ShellItemPropertyKey DestLongitudeNumerator => new ShellItemPropertyKey(new Guid("{A3250282-FB6D-48D5-9A89-DBCACE75CCCF}"), 100); + + /// + /// Name: System.GPS.DestLongitudeRef -- PKEY_GPS_DestLongitudeRef + /// Description: Indicates whether the longitude destination point is east or west longitude + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {182C1EA6-7C1C-4083-AB4B-AC6C9F4ED128}, 100 + /// + public static ShellItemPropertyKey DestLongitudeRef => new ShellItemPropertyKey(new Guid("{182C1EA6-7C1C-4083-AB4B-AC6C9F4ED128}"), 100); + + /// + /// Name: System.GPS.Differential -- PKEY_GPS_Differential + /// Description: Indicates whether differential correction was applied to the GPS receiver + /// + /// Type: UInt16 -- VT_UI2 + /// FormatID: {AAF4EE25-BD3B-4DD7-BFC4-47F77BB00F6D}, 100 + /// + public static ShellItemPropertyKey Differential => new ShellItemPropertyKey(new Guid("{AAF4EE25-BD3B-4DD7-BFC4-47F77BB00F6D}"), 100); + + /// + /// Name: System.GPS.DOP -- PKEY_GPS_DOP + /// Description: Indicates the GPS DOP (data degree of precision). Calculated from PKEY_GPS_DOPNumerator and PKEY_GPS_DOPDenominator + /// + /// Type: Double -- VT_R8 + /// FormatID: {0CF8FB02-1837-42F1-A697-A7017AA289B9}, 100 + /// + public static ShellItemPropertyKey DOP => new ShellItemPropertyKey(new Guid("{0CF8FB02-1837-42F1-A697-A7017AA289B9}"), 100); + + /// + /// Name: System.GPS.DOPDenominator -- PKEY_GPS_DOPDenominator + /// Description: Denominator of PKEY_GPS_DOP + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {A0BE94C5-50BA-487B-BD35-0654BE8881ED}, 100 + /// + public static ShellItemPropertyKey DOPDenominator => new ShellItemPropertyKey(new Guid("{A0BE94C5-50BA-487B-BD35-0654BE8881ED}"), 100); + + /// + /// Name: System.GPS.DOPNumerator -- PKEY_GPS_DOPNumerator + /// Description: Numerator of PKEY_GPS_DOP + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {47166B16-364F-4AA0-9F31-E2AB3DF449C3}, 100 + /// + public static ShellItemPropertyKey DOPNumerator => new ShellItemPropertyKey(new Guid("{47166B16-364F-4AA0-9F31-E2AB3DF449C3}"), 100); + + /// + /// Name: System.GPS.ImgDirection -- PKEY_GPS_ImgDirection + /// Description: Indicates direction of the image when it was captured. Calculated from PKEY_GPS_ImgDirectionNumerator and + ///PKEY_GPS_ImgDirectionDenominator. + /// + /// Type: Double -- VT_R8 + /// FormatID: {16473C91-D017-4ED9-BA4D-B6BAA55DBCF8}, 100 + /// + public static ShellItemPropertyKey ImgDirection => new ShellItemPropertyKey(new Guid("{16473C91-D017-4ED9-BA4D-B6BAA55DBCF8}"), 100); + + /// + /// Name: System.GPS.ImgDirectionDenominator -- PKEY_GPS_ImgDirectionDenominator + /// Description: Denominator of PKEY_GPS_ImgDirection + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {10B24595-41A2-4E20-93C2-5761C1395F32}, 100 + /// + public static ShellItemPropertyKey ImgDirectionDenominator => new ShellItemPropertyKey(new Guid("{10B24595-41A2-4E20-93C2-5761C1395F32}"), 100); + + /// + /// Name: System.GPS.ImgDirectionNumerator -- PKEY_GPS_ImgDirectionNumerator + /// Description: Numerator of PKEY_GPS_ImgDirection + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {DC5877C7-225F-45F7-BAC7-E81334B6130A}, 100 + /// + public static ShellItemPropertyKey ImgDirectionNumerator => new ShellItemPropertyKey(new Guid("{DC5877C7-225F-45F7-BAC7-E81334B6130A}"), 100); + + /// + /// Name: System.GPS.ImgDirectionRef -- PKEY_GPS_ImgDirectionRef + /// Description: Indicates reference for giving the direction of the image when it was captured. (eg: true direction, magnetic direction) + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {A4AAA5B7-1AD0-445F-811A-0F8F6E67F6B5}, 100 + /// + public static ShellItemPropertyKey ImgDirectionRef => new ShellItemPropertyKey(new Guid("{A4AAA5B7-1AD0-445F-811A-0F8F6E67F6B5}"), 100); + + /// + /// Name: System.GPS.Latitude -- PKEY_GPS_Latitude + /// Description: Indicates the latitude. This is an array of three values. Index 0 is the degrees, index 1 is the minutes, index 2 + ///is the seconds. Each is calculated from the values in PKEY_GPS_LatitudeNumerator and PKEY_GPS_LatitudeDenominator. + /// + /// Type: Multivalue Double -- VT_VECTOR | VT_R8 (For variants: VT_ARRAY | VT_R8) + /// FormatID: {8727CFFF-4868-4EC6-AD5B-81B98521D1AB}, 100 + /// + public static ShellItemPropertyKey Latitude => new ShellItemPropertyKey(new Guid("{8727CFFF-4868-4EC6-AD5B-81B98521D1AB}"), 100); + + /// + /// Name: System.GPS.LatitudeDenominator -- PKEY_GPS_LatitudeDenominator + /// Description: Denominator of PKEY_GPS_Latitude + /// + /// Type: Multivalue UInt32 -- VT_VECTOR | VT_UI4 (For variants: VT_ARRAY | VT_UI4) + /// FormatID: {16E634EE-2BFF-497B-BD8A-4341AD39EEB9}, 100 + /// + public static ShellItemPropertyKey LatitudeDenominator => new ShellItemPropertyKey(new Guid("{16E634EE-2BFF-497B-BD8A-4341AD39EEB9}"), 100); + + /// + /// Name: System.GPS.LatitudeNumerator -- PKEY_GPS_LatitudeNumerator + /// Description: Numerator of PKEY_GPS_Latitude + /// + /// Type: Multivalue UInt32 -- VT_VECTOR | VT_UI4 (For variants: VT_ARRAY | VT_UI4) + /// FormatID: {7DDAAAD1-CCC8-41AE-B750-B2CB8031AEA2}, 100 + /// + public static ShellItemPropertyKey LatitudeNumerator => new ShellItemPropertyKey(new Guid("{7DDAAAD1-CCC8-41AE-B750-B2CB8031AEA2}"), 100); + + /// + /// Name: System.GPS.LatitudeRef -- PKEY_GPS_LatitudeRef + /// Description: Indicates whether latitude is north or south latitude + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {029C0252-5B86-46C7-ACA0-2769FFC8E3D4}, 100 + /// + public static ShellItemPropertyKey LatitudeRef => new ShellItemPropertyKey(new Guid("{029C0252-5B86-46C7-ACA0-2769FFC8E3D4}"), 100); + + /// + /// Name: System.GPS.Longitude -- PKEY_GPS_Longitude + /// Description: Indicates the longitude. This is an array of three values. Index 0 is the degrees, index 1 is the minutes, index 2 + ///is the seconds. Each is calculated from the values in PKEY_GPS_LongitudeNumerator and PKEY_GPS_LongitudeDenominator. + /// + /// Type: Multivalue Double -- VT_VECTOR | VT_R8 (For variants: VT_ARRAY | VT_R8) + /// FormatID: {C4C4DBB2-B593-466B-BBDA-D03D27D5E43A}, 100 + /// + public static ShellItemPropertyKey Longitude => new ShellItemPropertyKey(new Guid("{C4C4DBB2-B593-466B-BBDA-D03D27D5E43A}"), 100); + + /// + /// Name: System.GPS.LongitudeDenominator -- PKEY_GPS_LongitudeDenominator + /// Description: Denominator of PKEY_GPS_Longitude + /// + /// Type: Multivalue UInt32 -- VT_VECTOR | VT_UI4 (For variants: VT_ARRAY | VT_UI4) + /// FormatID: {BE6E176C-4534-4D2C-ACE5-31DEDAC1606B}, 100 + /// + public static ShellItemPropertyKey LongitudeDenominator => new ShellItemPropertyKey(new Guid("{BE6E176C-4534-4D2C-ACE5-31DEDAC1606B}"), 100); + + /// + /// Name: System.GPS.LongitudeNumerator -- PKEY_GPS_LongitudeNumerator + /// Description: Numerator of PKEY_GPS_Longitude + /// + /// Type: Multivalue UInt32 -- VT_VECTOR | VT_UI4 (For variants: VT_ARRAY | VT_UI4) + /// FormatID: {02B0F689-A914-4E45-821D-1DDA452ED2C4}, 100 + /// + public static ShellItemPropertyKey LongitudeNumerator => new ShellItemPropertyKey(new Guid("{02B0F689-A914-4E45-821D-1DDA452ED2C4}"), 100); + + /// + /// Name: System.GPS.LongitudeRef -- PKEY_GPS_LongitudeRef + /// Description: Indicates whether longitude is east or west longitude + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {33DCF22B-28D5-464C-8035-1EE9EFD25278}, 100 + /// + public static ShellItemPropertyKey LongitudeRef => new ShellItemPropertyKey(new Guid("{33DCF22B-28D5-464C-8035-1EE9EFD25278}"), 100); + + /// + /// Name: System.GPS.MapDatum -- PKEY_GPS_MapDatum + /// Description: Indicates the geodetic survey data used by the GPS receiver + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {2CA2DAE6-EDDC-407D-BEF1-773942ABFA95}, 100 + /// + public static ShellItemPropertyKey MapDatum => new ShellItemPropertyKey(new Guid("{2CA2DAE6-EDDC-407D-BEF1-773942ABFA95}"), 100); + + /// + /// Name: System.GPS.MeasureMode -- PKEY_GPS_MeasureMode + /// Description: Indicates the GPS measurement mode. (eg: 2-dimensional, 3-dimensional) + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {A015ED5D-AAEA-4D58-8A86-3C586920EA0B}, 100 + /// + public static ShellItemPropertyKey MeasureMode => new ShellItemPropertyKey(new Guid("{A015ED5D-AAEA-4D58-8A86-3C586920EA0B}"), 100); + + /// + /// Name: System.GPS.ProcessingMethod -- PKEY_GPS_ProcessingMethod + /// Description: Indicates the name of the method used for location finding + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {59D49E61-840F-4AA9-A939-E2099B7F6399}, 100 + /// + public static ShellItemPropertyKey ProcessingMethod => new ShellItemPropertyKey(new Guid("{59D49E61-840F-4AA9-A939-E2099B7F6399}"), 100); + + /// + /// Name: System.GPS.Satellites -- PKEY_GPS_Satellites + /// Description: Indicates the GPS satellites used for measurements + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {467EE575-1F25-4557-AD4E-B8B58B0D9C15}, 100 + /// + public static ShellItemPropertyKey Satellites => new ShellItemPropertyKey(new Guid("{467EE575-1F25-4557-AD4E-B8B58B0D9C15}"), 100); + + /// + /// Name: System.GPS.Speed -- PKEY_GPS_Speed + /// Description: Indicates the speed of the GPS receiver movement. Calculated from PKEY_GPS_SpeedNumerator and + ///PKEY_GPS_SpeedDenominator. + /// + /// Type: Double -- VT_R8 + /// FormatID: {DA5D0862-6E76-4E1B-BABD-70021BD25494}, 100 + /// + public static ShellItemPropertyKey Speed => new ShellItemPropertyKey(new Guid("{DA5D0862-6E76-4E1B-BABD-70021BD25494}"), 100); + + /// + /// Name: System.GPS.SpeedDenominator -- PKEY_GPS_SpeedDenominator + /// Description: Denominator of PKEY_GPS_Speed + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {7D122D5A-AE5E-4335-8841-D71E7CE72F53}, 100 + /// + public static ShellItemPropertyKey SpeedDenominator => new ShellItemPropertyKey(new Guid("{7D122D5A-AE5E-4335-8841-D71E7CE72F53}"), 100); + + /// + /// Name: System.GPS.SpeedNumerator -- PKEY_GPS_SpeedNumerator + /// Description: Numerator of PKEY_GPS_Speed + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {ACC9CE3D-C213-4942-8B48-6D0820F21C6D}, 100 + /// + public static ShellItemPropertyKey SpeedNumerator => new ShellItemPropertyKey(new Guid("{ACC9CE3D-C213-4942-8B48-6D0820F21C6D}"), 100); + + /// + /// Name: System.GPS.SpeedRef -- PKEY_GPS_SpeedRef + /// Description: Indicates the unit used to express the speed of the GPS receiver movement. (eg: kilometers per hour, + ///miles per hour, knots). + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {ECF7F4C9-544F-4D6D-9D98-8AD79ADAF453}, 100 + /// + public static ShellItemPropertyKey SpeedRef => new ShellItemPropertyKey(new Guid("{ECF7F4C9-544F-4D6D-9D98-8AD79ADAF453}"), 100); + + /// + /// Name: System.GPS.Status -- PKEY_GPS_Status + /// Description: Indicates the status of the GPS receiver when the image was recorded. (eg: measurement in progress, + ///measurement interoperability). + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {125491F4-818F-46B2-91B5-D537753617B2}, 100 + /// + public static ShellItemPropertyKey Status => new ShellItemPropertyKey(new Guid("{125491F4-818F-46B2-91B5-D537753617B2}"), 100); + + /// + /// Name: System.GPS.Track -- PKEY_GPS_Track + /// Description: Indicates the direction of the GPS receiver movement. Calculated from PKEY_GPS_TrackNumerator and + ///PKEY_GPS_TrackDenominator. + /// + /// Type: Double -- VT_R8 + /// FormatID: {76C09943-7C33-49E3-9E7E-CDBA872CFADA}, 100 + /// + public static ShellItemPropertyKey Track => new ShellItemPropertyKey(new Guid("{76C09943-7C33-49E3-9E7E-CDBA872CFADA}"), 100); + + /// + /// Name: System.GPS.TrackDenominator -- PKEY_GPS_TrackDenominator + /// Description: Denominator of PKEY_GPS_Track + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {C8D1920C-01F6-40C0-AC86-2F3A4AD00770}, 100 + /// + public static ShellItemPropertyKey TrackDenominator => new ShellItemPropertyKey(new Guid("{C8D1920C-01F6-40C0-AC86-2F3A4AD00770}"), 100); + + /// + /// Name: System.GPS.TrackNumerator -- PKEY_GPS_TrackNumerator + /// Description: Numerator of PKEY_GPS_Track + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {702926F4-44A6-43E1-AE71-45627116893B}, 100 + /// + public static ShellItemPropertyKey TrackNumerator => new ShellItemPropertyKey(new Guid("{702926F4-44A6-43E1-AE71-45627116893B}"), 100); + + /// + /// Name: System.GPS.TrackRef -- PKEY_GPS_TrackRef + /// Description: Indicates reference for the direction of the GPS receiver movement. (eg: true direction, magnetic direction) + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {35DBE6FE-44C3-4400-AAAE-D2C799C407E8}, 100 + /// + public static ShellItemPropertyKey TrackRef => new ShellItemPropertyKey(new Guid("{35DBE6FE-44C3-4400-AAAE-D2C799C407E8}"), 100); + + /// + /// Name: System.GPS.VersionID -- PKEY_GPS_VersionID + /// Description: Indicates the version of the GPS information + /// + /// Type: Buffer -- VT_VECTOR | VT_UI1 (For variants: VT_ARRAY | VT_UI1) + /// FormatID: {22704DA4-C6B2-4A99-8E56-F16DF8C92599}, 100 + /// + public static ShellItemPropertyKey VersionID => new ShellItemPropertyKey(new Guid("{22704DA4-C6B2-4A99-8E56-F16DF8C92599}"), 100); + } + + /// Identity Properties + public static class Identity + { + /// + /// Name: System.Identity.Blob -- PKEY_Identity_Blob + /// Description: Blob used to import/export identities + /// + /// Type: Blob -- VT_BLOB + /// FormatID: {8C3B93A4-BAED-1A83-9A32-102EE313F6EB}, 100 + /// + public static ShellItemPropertyKey Blob => new ShellItemPropertyKey(new Guid("{8C3B93A4-BAED-1A83-9A32-102EE313F6EB}"), 100); + + /// + /// Name: System.Identity.DisplayName -- PKEY_Identity_DisplayName + /// Description: Display Name + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {7D683FC9-D155-45A8-BB1F-89D19BCB792F}, 100 + /// + public static ShellItemPropertyKey DisplayName => new ShellItemPropertyKey(new Guid("{7D683FC9-D155-45A8-BB1F-89D19BCB792F}"), 100); + + /// + /// Name: System.Identity.IsMeIdentity -- PKEY_Identity_IsMeIdentity + /// Description: Is it Me Identity + /// + /// Type: Boolean -- VT_BOOL + /// FormatID: {A4108708-09DF-4377-9DFC-6D99986D5A67}, 100 + /// + public static ShellItemPropertyKey IsMeIdentity => new ShellItemPropertyKey(new Guid("{A4108708-09DF-4377-9DFC-6D99986D5A67}"), 100); + + /// + /// Name: System.Identity.PrimaryEmailAddress -- PKEY_Identity_PrimaryEmailAddress + /// Description: Primary Email Address + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {FCC16823-BAED-4F24-9B32-A0982117F7FA}, 100 + /// + public static ShellItemPropertyKey PrimaryEmailAddress => new ShellItemPropertyKey(new Guid("{FCC16823-BAED-4F24-9B32-A0982117F7FA}"), 100); + + /// + /// Name: System.Identity.ProviderID -- PKEY_Identity_ProviderID + /// Description: Provider ID + /// + /// Type: Guid -- VT_CLSID + /// FormatID: {74A7DE49-FA11-4D3D-A006-DB7E08675916}, 100 + /// + public static ShellItemPropertyKey ProviderID => new ShellItemPropertyKey(new Guid("{74A7DE49-FA11-4D3D-A006-DB7E08675916}"), 100); + + /// + /// Name: System.Identity.UniqueID -- PKEY_Identity_UniqueID + /// Description: Unique ID + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {E55FC3B0-2B60-4220-918E-B21E8BF16016}, 100 + /// + public static ShellItemPropertyKey UniqueID => new ShellItemPropertyKey(new Guid("{E55FC3B0-2B60-4220-918E-B21E8BF16016}"), 100); + + /// + /// Name: System.Identity.UserName -- PKEY_Identity_UserName + /// Description: Identity User Name + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {C4322503-78CA-49C6-9ACC-A68E2AFD7B6B}, 100 + /// + public static ShellItemPropertyKey UserName => new ShellItemPropertyKey(new Guid("{C4322503-78CA-49C6-9ACC-A68E2AFD7B6B}"), 100); + } + + /// IdentityProvider Properties + public static class IdentityProvider + { + /// + /// Name: System.IdentityProvider.Name -- PKEY_IdentityProvider_Name + /// Description: Identity Provider Name + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {B96EFF7B-35CA-4A35-8607-29E3A54C46EA}, 100 + /// + public static ShellItemPropertyKey Name => new ShellItemPropertyKey(new Guid("{B96EFF7B-35CA-4A35-8607-29E3A54C46EA}"), 100); + + /// + /// Name: System.IdentityProvider.Picture -- PKEY_IdentityProvider_Picture + /// Description: Picture for the Identity Provider + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {2425166F-5642-4864-992F-98FD98F294C3}, 100 + /// + public static ShellItemPropertyKey Picture => new ShellItemPropertyKey(new Guid("{2425166F-5642-4864-992F-98FD98F294C3}"), 100); + } + + /// Image Properties + public static class Image + { + /// + /// Name: System.Image.BitDepth -- PKEY_Image_BitDepth + /// Description: + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: (PSGUID_IMAGESUMMARYINFORMATION) {6444048F-4C8B-11D1-8B70-080036B11A03}, 7 (PIDISI_BITDEPTH) + /// + public static ShellItemPropertyKey BitDepth => new ShellItemPropertyKey(new Guid("{6444048F-4C8B-11D1-8B70-080036B11A03}"), 7); + + /// + /// Name: System.Image.ColorSpace -- PKEY_Image_ColorSpace + /// Description: PropertyTagExifColorSpace + /// + /// Type: UInt16 -- VT_UI2 + /// FormatID: (FMTID_ImageProperties) {14B81DA1-0135-4D31-96D9-6CBFC9671A99}, 40961 + /// + public static ShellItemPropertyKey ColorSpace => new ShellItemPropertyKey(new Guid("{14B81DA1-0135-4D31-96D9-6CBFC9671A99}"), 40961); + + /// + /// Name: System.Image.CompressedBitsPerPixel -- PKEY_Image_CompressedBitsPerPixel + /// Description: Calculated from PKEY_Image_CompressedBitsPerPixelNumerator and PKEY_Image_CompressedBitsPerPixelDenominator. + /// + /// Type: Double -- VT_R8 + /// FormatID: {364B6FA9-37AB-482A-BE2B-AE02F60D4318}, 100 + /// + public static ShellItemPropertyKey CompressedBitsPerPixel => new ShellItemPropertyKey(new Guid("{364B6FA9-37AB-482A-BE2B-AE02F60D4318}"), 100); + + /// + /// Name: System.Image.CompressedBitsPerPixelDenominator -- PKEY_Image_CompressedBitsPerPixelDenominator + /// Description: Denominator of PKEY_Image_CompressedBitsPerPixel. + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {1F8844E1-24AD-4508-9DFD-5326A415CE02}, 100 + /// + public static ShellItemPropertyKey CompressedBitsPerPixelDenominator => new ShellItemPropertyKey(new Guid("{1F8844E1-24AD-4508-9DFD-5326A415CE02}"), 100); + + /// + /// Name: System.Image.CompressedBitsPerPixelNumerator -- PKEY_Image_CompressedBitsPerPixelNumerator + /// Description: Numerator of PKEY_Image_CompressedBitsPerPixel. + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {D21A7148-D32C-4624-8900-277210F79C0F}, 100 + /// + public static ShellItemPropertyKey CompressedBitsPerPixelNumerator => new ShellItemPropertyKey(new Guid("{D21A7148-D32C-4624-8900-277210F79C0F}"), 100); + + /// + /// Name: System.Image.Compression -- PKEY_Image_Compression + /// Description: Indicates the image compression level. PropertyTagCompression. + /// + /// Type: UInt16 -- VT_UI2 + /// FormatID: (FMTID_ImageProperties) {14B81DA1-0135-4D31-96D9-6CBFC9671A99}, 259 + /// + public static ShellItemPropertyKey Compression => new ShellItemPropertyKey(new Guid("{14B81DA1-0135-4D31-96D9-6CBFC9671A99}"), 259); + + /// + /// Name: System.Image.CompressionText -- PKEY_Image_CompressionText + /// Description: This is the user-friendly form of System.Image.Compression. Not intended to be parsed + ///programmatically. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {3F08E66F-2F44-4BB9-A682-AC35D2562322}, 100 + /// + public static ShellItemPropertyKey CompressionText => new ShellItemPropertyKey(new Guid("{3F08E66F-2F44-4BB9-A682-AC35D2562322}"), 100); + + /// + /// Name: System.Image.Dimensions -- PKEY_Image_Dimensions + /// Description: Indicates the dimensions of the image. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (PSGUID_IMAGESUMMARYINFORMATION) {6444048F-4C8B-11D1-8B70-080036B11A03}, 13 (PIDISI_DIMENSIONS) + /// + public static ShellItemPropertyKey Dimensions => new ShellItemPropertyKey(new Guid("{6444048F-4C8B-11D1-8B70-080036B11A03}"), 13); + + /// + /// Name: System.Image.HorizontalResolution -- PKEY_Image_HorizontalResolution + /// Description: + /// + /// Type: Double -- VT_R8 + /// FormatID: (PSGUID_IMAGESUMMARYINFORMATION) {6444048F-4C8B-11D1-8B70-080036B11A03}, 5 (PIDISI_RESOLUTIONX) + /// + public static ShellItemPropertyKey HorizontalResolution => new ShellItemPropertyKey(new Guid("{6444048F-4C8B-11D1-8B70-080036B11A03}"), 5); + + /// + /// Name: System.Image.HorizontalSize -- PKEY_Image_HorizontalSize + /// Description: + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: (PSGUID_IMAGESUMMARYINFORMATION) {6444048F-4C8B-11D1-8B70-080036B11A03}, 3 (PIDISI_CX) + /// + public static ShellItemPropertyKey HorizontalSize => new ShellItemPropertyKey(new Guid("{6444048F-4C8B-11D1-8B70-080036B11A03}"), 3); + + /// + /// Name: System.Image.ImageID -- PKEY_Image_ImageID + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {10DABE05-32AA-4C29-BF1A-63E2D220587F}, 100 + /// + public static ShellItemPropertyKey ImageID => new ShellItemPropertyKey(new Guid("{10DABE05-32AA-4C29-BF1A-63E2D220587F}"), 100); + + /// + /// Name: System.Image.ResolutionUnit -- PKEY_Image_ResolutionUnit + /// Description: + /// Type: Int16 -- VT_I2 + /// FormatID: {19B51FA6-1F92-4A5C-AB48-7DF0ABD67444}, 100 + /// + public static ShellItemPropertyKey ResolutionUnit => new ShellItemPropertyKey(new Guid("{19B51FA6-1F92-4A5C-AB48-7DF0ABD67444}"), 100); + + /// + /// Name: System.Image.VerticalResolution -- PKEY_Image_VerticalResolution + /// Description: + /// + /// Type: Double -- VT_R8 + /// FormatID: (PSGUID_IMAGESUMMARYINFORMATION) {6444048F-4C8B-11D1-8B70-080036B11A03}, 6 (PIDISI_RESOLUTIONY) + /// + public static ShellItemPropertyKey VerticalResolution => new ShellItemPropertyKey(new Guid("{6444048F-4C8B-11D1-8B70-080036B11A03}"), 6); + + /// + /// Name: System.Image.VerticalSize -- PKEY_Image_VerticalSize + /// Description: + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: (PSGUID_IMAGESUMMARYINFORMATION) {6444048F-4C8B-11D1-8B70-080036B11A03}, 4 (PIDISI_CY) + /// + public static ShellItemPropertyKey VerticalSize => new ShellItemPropertyKey(new Guid("{6444048F-4C8B-11D1-8B70-080036B11A03}"), 4); + } + + /// JA Properties + public static class JA + { + /// + /// Name: System.Contact.JA.CompanyNamePhonetic -- PKEY_Contact_JA_CompanyNamePhonetic + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {897B3694-FE9E-43E6-8066-260F590C0100}, 2 + /// + public static ShellItemPropertyKey CompanyNamePhonetic => new ShellItemPropertyKey(new Guid("{897B3694-FE9E-43E6-8066-260F590C0100}"), 2); + + /// + /// Name: System.Contact.JA.FirstNamePhonetic -- PKEY_Contact_JA_FirstNamePhonetic + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {897B3694-FE9E-43E6-8066-260F590C0100}, 3 + /// + public static ShellItemPropertyKey FirstNamePhonetic => new ShellItemPropertyKey(new Guid("{897B3694-FE9E-43E6-8066-260F590C0100}"), 3); + + /// + /// Name: System.Contact.JA.LastNamePhonetic -- PKEY_Contact_JA_LastNamePhonetic + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {897B3694-FE9E-43E6-8066-260F590C0100}, 4 + /// + public static ShellItemPropertyKey LastNamePhonetic => new ShellItemPropertyKey(new Guid("{897B3694-FE9E-43E6-8066-260F590C0100}"), 4); + } + + /// Journal Properties + public static class Journal + { + /// + /// Name: System.Journal.Contacts -- PKEY_Journal_Contacts + /// Description: + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: {DEA7C82C-1D89-4A66-9427-A4E3DEBABCB1}, 100 + /// + public static ShellItemPropertyKey Contacts => new ShellItemPropertyKey(new Guid("{DEA7C82C-1D89-4A66-9427-A4E3DEBABCB1}"), 100); + + /// + /// Name: System.Journal.EntryType -- PKEY_Journal_EntryType + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {95BEB1FC-326D-4644-B396-CD3ED90E6DDF}, 100 + /// + public static ShellItemPropertyKey EntryType => new ShellItemPropertyKey(new Guid("{95BEB1FC-326D-4644-B396-CD3ED90E6DDF}"), 100); + } + + /// LayoutPattern Properties + public static class LayoutPattern + { + /// + /// Name: System.LayoutPattern.ContentViewModeForBrowse -- PKEY_LayoutPattern_ContentViewModeForBrowse + /// Description: Specifies the layout pattern that the content view mode should apply for this item in the context of browsing. + ///Register the regvalue under the name of "ContentViewModeLayoutPatternForBrowse". + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {C9944A21-A406-48FE-8225-AEC7E24C211B}, 500 + /// + public static ShellItemPropertyKey ContentViewModeForBrowse => new ShellItemPropertyKey(new Guid("{C9944A21-A406-48FE-8225-AEC7E24C211B}"), 500); + + /// + /// Name: System.LayoutPattern.ContentViewModeForSearch -- PKEY_LayoutPattern_ContentViewModeForSearch + /// Description: Specifies the layout pattern that the content view mode should apply for this item in the context of searching. + ///Register the regvalue under the name of "ContentViewModeLayoutPatternForSearch". + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {C9944A21-A406-48FE-8225-AEC7E24C211B}, 501 + /// + public static ShellItemPropertyKey ContentViewModeForSearch => new ShellItemPropertyKey(new Guid("{C9944A21-A406-48FE-8225-AEC7E24C211B}"), 501); + } + + /// Link Properties + public static class Link + { + /// + /// Name: System.Link.Arguments -- PKEY_Link_Arguments + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {436F2667-14E2-4FEB-B30A-146C53B5B674}, 100 + /// + public static ShellItemPropertyKey Arguments => new ShellItemPropertyKey(new Guid("{436F2667-14E2-4FEB-B30A-146C53B5B674}"), 100); + + /// + /// Name: System.Link.Comment -- PKEY_Link_Comment + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (PSGUID_LINK) {B9B4B3FC-2B51-4A42-B5D8-324146AFCF25}, 5 + /// + public static ShellItemPropertyKey Comment => new ShellItemPropertyKey(new Guid("{B9B4B3FC-2B51-4A42-B5D8-324146AFCF25}"), 5); + + /// + /// Name: System.Link.DateVisited -- PKEY_Link_DateVisited + /// Description: + /// Type: DateTime -- VT_FILETIME (For variants: VT_DATE) + /// FormatID: {5CBF2787-48CF-4208-B90E-EE5E5D420294}, 23 (PKEYs relating to URLs. Used by IE History.) + /// + public static ShellItemPropertyKey DateVisited => new ShellItemPropertyKey(new Guid("{5CBF2787-48CF-4208-B90E-EE5E5D420294}"), 23); + + /// + /// Name: System.Link.Description -- PKEY_Link_Description + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {5CBF2787-48CF-4208-B90E-EE5E5D420294}, 21 (PKEYs relating to URLs. Used by IE History.) + /// + public static ShellItemPropertyKey Description => new ShellItemPropertyKey(new Guid("{5CBF2787-48CF-4208-B90E-EE5E5D420294}"), 21); + + /// + /// Name: System.Link.Status -- PKEY_Link_Status + /// Description: + /// + /// Type: Int32 -- VT_I4 + /// FormatID: (PSGUID_LINK) {B9B4B3FC-2B51-4A42-B5D8-324146AFCF25}, 3 (PID_LINK_TARGET_TYPE) + /// + public static ShellItemPropertyKey Status => new ShellItemPropertyKey(new Guid("{B9B4B3FC-2B51-4A42-B5D8-324146AFCF25}"), 3); + + /// + /// Name: System.Link.TargetExtension -- PKEY_Link_TargetExtension + /// Description: The file extension of the link target. See System.File.Extension + /// + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: {7A7D76F4-B630-4BD7-95FF-37CC51A975C9}, 2 + /// + public static ShellItemPropertyKey TargetExtension => new ShellItemPropertyKey(new Guid("{7A7D76F4-B630-4BD7-95FF-37CC51A975C9}"), 2); + + /// + /// Name: System.Link.TargetParsingPath -- PKEY_Link_TargetParsingPath + /// Description: This is the shell namespace path to the target of the link item. This path may be passed to + ///SHParseDisplayName to parse the path to the correct shell folder. + /// + ///If the target item is a file, the value is identical to System.ItemPathDisplay. + /// + ///If the target item cannot be accessed through the shell namespace, this value is VT_EMPTY. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (PSGUID_LINK) {B9B4B3FC-2B51-4A42-B5D8-324146AFCF25}, 2 (PID_LINK_TARGET) + /// + public static ShellItemPropertyKey TargetParsingPath => new ShellItemPropertyKey(new Guid("{B9B4B3FC-2B51-4A42-B5D8-324146AFCF25}"), 2); + + /// + /// Name: System.Link.TargetSFGAOFlags -- PKEY_Link_TargetSFGAOFlags + /// Description: IShellFolder::GetAttributesOf flags for the target of a link, with SFGAO_PKEYSFGAOMASK + ///attributes masked out. + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: (PSGUID_LINK) {B9B4B3FC-2B51-4A42-B5D8-324146AFCF25}, 8 + /// + public static ShellItemPropertyKey TargetSFGAOFlags => new ShellItemPropertyKey(new Guid("{B9B4B3FC-2B51-4A42-B5D8-324146AFCF25}"), 8); + + /// + /// Name: System.Link.TargetSFGAOFlagsStrings -- PKEY_Link_TargetSFGAOFlagsStrings + /// Description: Expresses the SFGAO flags of a link as string values and is used as a query optimization. See + ///PKEY_Shell_SFGAOFlagsStrings for possible values of this. + /// + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: {D6942081-D53B-443D-AD47-5E059D9CD27A}, 3 + /// + public static ShellItemPropertyKey TargetSFGAOFlagsStrings => new ShellItemPropertyKey(new Guid("{D6942081-D53B-443D-AD47-5E059D9CD27A}"), 3); + + /// + /// Name: System.Link.TargetUrl -- PKEY_Link_TargetUrl + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {5CBF2787-48CF-4208-B90E-EE5E5D420294}, 2 (PKEYs relating to URLs. Used by IE History.) + /// + public static ShellItemPropertyKey TargetUrl => new ShellItemPropertyKey(new Guid("{5CBF2787-48CF-4208-B90E-EE5E5D420294}"), 2); + } + + /// Media Properties + public static class Media + { + /// + /// Name: System.Media.AuthorUrl -- PKEY_Media_AuthorUrl + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (PSGUID_MEDIAFILESUMMARYINFORMATION) {64440492-4C8B-11D1-8B70-080036B11A03}, 32 (PIDMSI_AUTHOR_URL) + /// + public static ShellItemPropertyKey AuthorUrl => new ShellItemPropertyKey(new Guid("{64440492-4C8B-11D1-8B70-080036B11A03}"), 32); + + /// + /// Name: System.Media.AverageLevel -- PKEY_Media_AverageLevel + /// Description: + /// Type: UInt32 -- VT_UI4 + /// FormatID: {09EDD5B6-B301-43C5-9990-D00302EFFD46}, 100 + /// + public static ShellItemPropertyKey AverageLevel => new ShellItemPropertyKey(new Guid("{09EDD5B6-B301-43C5-9990-D00302EFFD46}"), 100); + + /// + /// Name: System.Media.ClassPrimaryID -- PKEY_Media_ClassPrimaryID + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (PSGUID_MEDIAFILESUMMARYINFORMATION) {64440492-4C8B-11D1-8B70-080036B11A03}, 13 (PIDMSI_CLASS_PRIMARY_ID) + /// + public static ShellItemPropertyKey ClassPrimaryID => new ShellItemPropertyKey(new Guid("{64440492-4C8B-11D1-8B70-080036B11A03}"), 13); + + /// + /// Name: System.Media.ClassSecondaryID -- PKEY_Media_ClassSecondaryID + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (PSGUID_MEDIAFILESUMMARYINFORMATION) {64440492-4C8B-11D1-8B70-080036B11A03}, 14 (PIDMSI_CLASS_SECONDARY_ID) + /// + public static ShellItemPropertyKey ClassSecondaryID => new ShellItemPropertyKey(new Guid("{64440492-4C8B-11D1-8B70-080036B11A03}"), 14); + + /// + /// Name: System.Media.CollectionGroupID -- PKEY_Media_CollectionGroupID + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (PSGUID_MEDIAFILESUMMARYINFORMATION) {64440492-4C8B-11D1-8B70-080036B11A03}, 24 (PIDMSI_COLLECTION_GROUP_ID) + /// + public static ShellItemPropertyKey CollectionGroupID => new ShellItemPropertyKey(new Guid("{64440492-4C8B-11D1-8B70-080036B11A03}"), 24); + + /// + /// Name: System.Media.CollectionID -- PKEY_Media_CollectionID + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (PSGUID_MEDIAFILESUMMARYINFORMATION) {64440492-4C8B-11D1-8B70-080036B11A03}, 25 (PIDMSI_COLLECTION_ID) + /// + public static ShellItemPropertyKey CollectionID => new ShellItemPropertyKey(new Guid("{64440492-4C8B-11D1-8B70-080036B11A03}"), 25); + + /// + /// Name: System.Media.ContentDistributor -- PKEY_Media_ContentDistributor + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (PSGUID_MEDIAFILESUMMARYINFORMATION) {64440492-4C8B-11D1-8B70-080036B11A03}, 18 (PIDMSI_CONTENTDISTRIBUTOR) + /// + public static ShellItemPropertyKey ContentDistributor => new ShellItemPropertyKey(new Guid("{64440492-4C8B-11D1-8B70-080036B11A03}"), 18); + + /// + /// Name: System.Media.ContentID -- PKEY_Media_ContentID + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (PSGUID_MEDIAFILESUMMARYINFORMATION) {64440492-4C8B-11D1-8B70-080036B11A03}, 26 (PIDMSI_CONTENT_ID) + /// + public static ShellItemPropertyKey ContentID => new ShellItemPropertyKey(new Guid("{64440492-4C8B-11D1-8B70-080036B11A03}"), 26); + + /// + /// Name: System.Media.CreatorApplication -- PKEY_Media_CreatorApplication + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (PSGUID_MEDIAFILESUMMARYINFORMATION) {64440492-4C8B-11D1-8B70-080036B11A03}, 27 (PIDMSI_TOOL_NAME) + /// + public static ShellItemPropertyKey CreatorApplication => new ShellItemPropertyKey(new Guid("{64440492-4C8B-11D1-8B70-080036B11A03}"), 27); + + /// + /// Name: System.Media.CreatorApplicationVersion -- PKEY_Media_CreatorApplicationVersion + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (PSGUID_MEDIAFILESUMMARYINFORMATION) {64440492-4C8B-11D1-8B70-080036B11A03}, 28 (PIDMSI_TOOL_VERSION) + /// + public static ShellItemPropertyKey CreatorApplicationVersion => new ShellItemPropertyKey(new Guid("{64440492-4C8B-11D1-8B70-080036B11A03}"), 28); + + /// + /// Name: System.Media.DateEncoded -- PKEY_Media_DateEncoded + /// Description: DateTime is in UTC (in the doc, not file system). + /// + /// Type: DateTime -- VT_FILETIME (For variants: VT_DATE) + /// FormatID: {2E4B640D-5019-46D8-8881-55414CC5CAA0}, 100 + /// + public static ShellItemPropertyKey DateEncoded => new ShellItemPropertyKey(new Guid("{2E4B640D-5019-46D8-8881-55414CC5CAA0}"), 100); + + /// + /// Name: System.Media.DateReleased -- PKEY_Media_DateReleased + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {DE41CC29-6971-4290-B472-F59F2E2F31E2}, 100 + /// + public static ShellItemPropertyKey DateReleased => new ShellItemPropertyKey(new Guid("{DE41CC29-6971-4290-B472-F59F2E2F31E2}"), 100); + + /// + /// Name: System.Media.Duration -- PKEY_Media_Duration + /// Description: 100ns units, not milliseconds + /// + /// Type: UInt64 -- VT_UI8 + /// FormatID: (FMTID_AudioSummaryInformation) {64440490-4C8B-11D1-8B70-080036B11A03}, 3 (PIDASI_TIMELENGTH) + /// + public static ShellItemPropertyKey Duration => new ShellItemPropertyKey(new Guid("{64440490-4C8B-11D1-8B70-080036B11A03}"), 3); + + /// + /// Name: System.Media.DVDID -- PKEY_Media_DVDID + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (PSGUID_MEDIAFILESUMMARYINFORMATION) {64440492-4C8B-11D1-8B70-080036B11A03}, 15 (PIDMSI_DVDID) + /// + public static ShellItemPropertyKey DVDID => new ShellItemPropertyKey(new Guid("{64440492-4C8B-11D1-8B70-080036B11A03}"), 15); + + /// + /// Name: System.Media.EncodedBy -- PKEY_Media_EncodedBy + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (PSGUID_MEDIAFILESUMMARYINFORMATION) {64440492-4C8B-11D1-8B70-080036B11A03}, 36 (PIDMSI_ENCODED_BY) + /// + public static ShellItemPropertyKey EncodedBy => new ShellItemPropertyKey(new Guid("{64440492-4C8B-11D1-8B70-080036B11A03}"), 36); + + /// + /// Name: System.Media.EncodingSettings -- PKEY_Media_EncodingSettings + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (PSGUID_MEDIAFILESUMMARYINFORMATION) {64440492-4C8B-11D1-8B70-080036B11A03}, 37 (PIDMSI_ENCODING_SETTINGS) + /// + public static ShellItemPropertyKey EncodingSettings => new ShellItemPropertyKey(new Guid("{64440492-4C8B-11D1-8B70-080036B11A03}"), 37); + + /// + /// Name: System.Media.FrameCount -- PKEY_Media_FrameCount + /// Description: Indicates the frame count for the image. + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: (PSGUID_IMAGESUMMARYINFORMATION) {6444048F-4C8B-11D1-8B70-080036B11A03}, 12 (PIDISI_FRAMECOUNT) + /// + public static ShellItemPropertyKey FrameCount => new ShellItemPropertyKey(new Guid("{6444048F-4C8B-11D1-8B70-080036B11A03}"), 12); + + /// + /// Name: System.Media.MCDI -- PKEY_Media_MCDI + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (PSGUID_MEDIAFILESUMMARYINFORMATION) {64440492-4C8B-11D1-8B70-080036B11A03}, 16 (PIDMSI_MCDI) + /// + public static ShellItemPropertyKey MCDI => new ShellItemPropertyKey(new Guid("{64440492-4C8B-11D1-8B70-080036B11A03}"), 16); + + /// + /// Name: System.Media.MetadataContentProvider -- PKEY_Media_MetadataContentProvider + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (PSGUID_MEDIAFILESUMMARYINFORMATION) {64440492-4C8B-11D1-8B70-080036B11A03}, 17 (PIDMSI_PROVIDER) + /// + public static ShellItemPropertyKey MetadataContentProvider => new ShellItemPropertyKey(new Guid("{64440492-4C8B-11D1-8B70-080036B11A03}"), 17); + + /// + /// Name: System.Media.Producer -- PKEY_Media_Producer + /// Description: + /// + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: (PSGUID_MEDIAFILESUMMARYINFORMATION) {64440492-4C8B-11D1-8B70-080036B11A03}, 22 (PIDMSI_PRODUCER) + /// + public static ShellItemPropertyKey Producer => new ShellItemPropertyKey(new Guid("{64440492-4C8B-11D1-8B70-080036B11A03}"), 22); + + /// + /// Name: System.Media.PromotionUrl -- PKEY_Media_PromotionUrl + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (PSGUID_MEDIAFILESUMMARYINFORMATION) {64440492-4C8B-11D1-8B70-080036B11A03}, 33 (PIDMSI_PROMOTION_URL) + /// + public static ShellItemPropertyKey PromotionUrl => new ShellItemPropertyKey(new Guid("{64440492-4C8B-11D1-8B70-080036B11A03}"), 33); + + /// + /// Name: System.Media.ProtectionType -- PKEY_Media_ProtectionType + /// Description: If media is protected, how is it protected? + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (PSGUID_MEDIAFILESUMMARYINFORMATION) {64440492-4C8B-11D1-8B70-080036B11A03}, 38 + /// + public static ShellItemPropertyKey ProtectionType => new ShellItemPropertyKey(new Guid("{64440492-4C8B-11D1-8B70-080036B11A03}"), 38); + + /// + /// Name: System.Media.ProviderRating -- PKEY_Media_ProviderRating + /// Description: Rating (0 - 99) supplied by metadata provider + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (PSGUID_MEDIAFILESUMMARYINFORMATION) {64440492-4C8B-11D1-8B70-080036B11A03}, 39 + /// + public static ShellItemPropertyKey ProviderRating => new ShellItemPropertyKey(new Guid("{64440492-4C8B-11D1-8B70-080036B11A03}"), 39); + + /// + /// Name: System.Media.ProviderStyle -- PKEY_Media_ProviderStyle + /// Description: Style of music or video, supplied by metadata provider + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (PSGUID_MEDIAFILESUMMARYINFORMATION) {64440492-4C8B-11D1-8B70-080036B11A03}, 40 + /// + public static ShellItemPropertyKey ProviderStyle => new ShellItemPropertyKey(new Guid("{64440492-4C8B-11D1-8B70-080036B11A03}"), 40); + + /// + /// Name: System.Media.Publisher -- PKEY_Media_Publisher + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (PSGUID_MEDIAFILESUMMARYINFORMATION) {64440492-4C8B-11D1-8B70-080036B11A03}, 30 (PIDMSI_PUBLISHER) + /// + public static ShellItemPropertyKey Publisher => new ShellItemPropertyKey(new Guid("{64440492-4C8B-11D1-8B70-080036B11A03}"), 30); + + /// + /// Name: System.Media.SubscriptionContentId -- PKEY_Media_SubscriptionContentId + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {9AEBAE7A-9644-487D-A92C-657585ED751A}, 100 + /// + public static ShellItemPropertyKey SubscriptionContentId => new ShellItemPropertyKey(new Guid("{9AEBAE7A-9644-487D-A92C-657585ED751A}"), 100); + + /// + /// Name: System.Media.SubTitle -- PKEY_Media_SubTitle + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_MUSIC) {56A3372E-CE9C-11D2-9F0E-006097C686F6}, 38 (PIDSI_MUSIC_SUB_TITLE) + /// + public static ShellItemPropertyKey SubTitle => new ShellItemPropertyKey(new Guid("{56A3372E-CE9C-11D2-9F0E-006097C686F6}"), 38); + + /// + /// Name: System.Media.UniqueFileIdentifier -- PKEY_Media_UniqueFileIdentifier + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (PSGUID_MEDIAFILESUMMARYINFORMATION) {64440492-4C8B-11D1-8B70-080036B11A03}, 35 (PIDMSI_UNIQUE_FILE_IDENTIFIER) + /// + public static ShellItemPropertyKey UniqueFileIdentifier => new ShellItemPropertyKey(new Guid("{64440492-4C8B-11D1-8B70-080036B11A03}"), 35); + + /// + /// Name: System.Media.UserNoAutoInfo -- PKEY_Media_UserNoAutoInfo + /// Description: If true, do NOT alter this file's metadata. Set by user. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (PSGUID_MEDIAFILESUMMARYINFORMATION) {64440492-4C8B-11D1-8B70-080036B11A03}, 41 + /// + public static ShellItemPropertyKey UserNoAutoInfo => new ShellItemPropertyKey(new Guid("{64440492-4C8B-11D1-8B70-080036B11A03}"), 41); + + /// + /// Name: System.Media.UserWebUrl -- PKEY_Media_UserWebUrl + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (PSGUID_MEDIAFILESUMMARYINFORMATION) {64440492-4C8B-11D1-8B70-080036B11A03}, 34 (PIDMSI_USER_WEB_URL) + /// + public static ShellItemPropertyKey UserWebUrl => new ShellItemPropertyKey(new Guid("{64440492-4C8B-11D1-8B70-080036B11A03}"), 34); + + /// + /// Name: System.Media.Writer -- PKEY_Media_Writer + /// Description: + /// + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: (PSGUID_MEDIAFILESUMMARYINFORMATION) {64440492-4C8B-11D1-8B70-080036B11A03}, 23 (PIDMSI_WRITER) + /// + public static ShellItemPropertyKey Writer => new ShellItemPropertyKey(new Guid("{64440492-4C8B-11D1-8B70-080036B11A03}"), 23); + + /// + /// Name: System.Media.Year -- PKEY_Media_Year + /// Description: + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: (FMTID_MUSIC) {56A3372E-CE9C-11D2-9F0E-006097C686F6}, 5 (PIDSI_MUSIC_YEAR) + /// + public static ShellItemPropertyKey Year => new ShellItemPropertyKey(new Guid("{56A3372E-CE9C-11D2-9F0E-006097C686F6}"), 5); + } + + /// Message Properties + public static class Message + { + /// + /// Name: System.Message.AttachmentContents -- PKEY_Message_AttachmentContents + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {3143BF7C-80A8-4854-8880-E2E40189BDD0}, 100 + /// + public static ShellItemPropertyKey AttachmentContents => new ShellItemPropertyKey(new Guid("{3143BF7C-80A8-4854-8880-E2E40189BDD0}"), 100); + + /// + /// Name: System.Message.AttachmentNames -- PKEY_Message_AttachmentNames + /// Description: The names of the attachments in a message + /// + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: {E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}, 21 + /// + public static ShellItemPropertyKey AttachmentNames => new ShellItemPropertyKey(new Guid("{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}"), 21); + + /// + /// Name: System.Message.BccAddress -- PKEY_Message_BccAddress + /// Description: Addresses in Bcc: field + /// + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: {E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}, 2 + /// + public static ShellItemPropertyKey BccAddress => new ShellItemPropertyKey(new Guid("{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}"), 2); + + /// + /// Name: System.Message.BccName -- PKEY_Message_BccName + /// Description: person names in Bcc: field + /// + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: {E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}, 3 + /// + public static ShellItemPropertyKey BccName => new ShellItemPropertyKey(new Guid("{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}"), 3); + + /// + /// Name: System.Message.CcAddress -- PKEY_Message_CcAddress + /// Description: Addresses in Cc: field + /// + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: {E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}, 4 + /// + public static ShellItemPropertyKey CcAddress => new ShellItemPropertyKey(new Guid("{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}"), 4); + + /// + /// Name: System.Message.CcName -- PKEY_Message_CcName + /// Description: person names in Cc: field + /// + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: {E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}, 5 + /// + public static ShellItemPropertyKey CcName => new ShellItemPropertyKey(new Guid("{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}"), 5); + + /// + /// Name: System.Message.ConversationID -- PKEY_Message_ConversationID + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {DC8F80BD-AF1E-4289-85B6-3DFC1B493992}, 100 + /// + public static ShellItemPropertyKey ConversationID => new ShellItemPropertyKey(new Guid("{DC8F80BD-AF1E-4289-85B6-3DFC1B493992}"), 100); + + /// + /// Name: System.Message.ConversationIndex -- PKEY_Message_ConversationIndex + /// Description: + /// + /// Type: Buffer -- VT_VECTOR | VT_UI1 (For variants: VT_ARRAY | VT_UI1) + /// FormatID: {DC8F80BD-AF1E-4289-85B6-3DFC1B493992}, 101 + /// + public static ShellItemPropertyKey ConversationIndex => new ShellItemPropertyKey(new Guid("{DC8F80BD-AF1E-4289-85B6-3DFC1B493992}"), 101); + + /// + /// Name: System.Message.DateReceived -- PKEY_Message_DateReceived + /// Description: Date and Time communication was received + /// + /// Type: DateTime -- VT_FILETIME (For variants: VT_DATE) + /// FormatID: {E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}, 20 + /// + public static ShellItemPropertyKey DateReceived => new ShellItemPropertyKey(new Guid("{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}"), 20); + + /// + /// Name: System.Message.DateSent -- PKEY_Message_DateSent + /// Description: Date and Time communication was sent + /// + /// Type: DateTime -- VT_FILETIME (For variants: VT_DATE) + /// FormatID: {E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}, 19 + /// + public static ShellItemPropertyKey DateSent => new ShellItemPropertyKey(new Guid("{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}"), 19); + + /// + /// Name: System.Message.Flags -- PKEY_Message_Flags + /// Description: These are flags associated with email messages to know if a read receipt is pending, etc. + ///The values stored here by Outlook are defined for PR_MESSAGE_FLAGS on MSDN. + /// + /// Type: Int32 -- VT_I4 + /// FormatID: {A82D9EE7-CA67-4312-965E-226BCEA85023}, 100 + /// + public static ShellItemPropertyKey Flags => new ShellItemPropertyKey(new Guid("{A82D9EE7-CA67-4312-965E-226BCEA85023}"), 100); + + /// + /// Name: System.Message.FromAddress -- PKEY_Message_FromAddress + /// Description: + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: {E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}, 13 + /// + public static ShellItemPropertyKey FromAddress => new ShellItemPropertyKey(new Guid("{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}"), 13); + + /// + /// Name: System.Message.FromName -- PKEY_Message_FromName + /// Description: Address in from field as person name + /// + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: {E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}, 14 + /// + public static ShellItemPropertyKey FromName => new ShellItemPropertyKey(new Guid("{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}"), 14); + + /// + /// Name: System.Message.HasAttachments -- PKEY_Message_HasAttachments + /// Description: + /// + /// Type: Boolean -- VT_BOOL + /// FormatID: {9C1FCF74-2D97-41BA-B4AE-CB2E3661A6E4}, 8 + /// + public static ShellItemPropertyKey HasAttachments => new ShellItemPropertyKey(new Guid("{9C1FCF74-2D97-41BA-B4AE-CB2E3661A6E4}"), 8); + + /// + /// Name: System.Message.IsFwdOrReply -- PKEY_Message_IsFwdOrReply + /// Description: + /// Type: Int32 -- VT_I4 + /// FormatID: {9A9BC088-4F6D-469E-9919-E705412040F9}, 100 + /// + public static ShellItemPropertyKey IsFwdOrReply => new ShellItemPropertyKey(new Guid("{9A9BC088-4F6D-469E-9919-E705412040F9}"), 100); + + /// + /// Name: System.Message.MessageClass -- PKEY_Message_MessageClass + /// Description: What type of outlook msg this is (meeting, task, mail, etc.) + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {CD9ED458-08CE-418F-A70E-F912C7BB9C5C}, 103 + /// + public static ShellItemPropertyKey MessageClass => new ShellItemPropertyKey(new Guid("{CD9ED458-08CE-418F-A70E-F912C7BB9C5C}"), 103); + + /// + /// Name: System.Message.ProofInProgress -- PKEY_Message_ProofInProgress + /// Description: This property will be true if the message junk email proofing is still in progress. + /// + /// Type: Boolean -- VT_BOOL + /// FormatID: {9098F33C-9A7D-48A8-8DE5-2E1227A64E91}, 100 + /// + public static ShellItemPropertyKey ProofInProgress => new ShellItemPropertyKey(new Guid("{9098F33C-9A7D-48A8-8DE5-2E1227A64E91}"), 100); + + /// + /// Name: System.Message.SenderAddress -- PKEY_Message_SenderAddress + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {0BE1C8E7-1981-4676-AE14-FDD78F05A6E7}, 100 + /// + public static ShellItemPropertyKey SenderAddress => new ShellItemPropertyKey(new Guid("{0BE1C8E7-1981-4676-AE14-FDD78F05A6E7}"), 100); + + /// + /// Name: System.Message.SenderName -- PKEY_Message_SenderName + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {0DA41CFA-D224-4A18-AE2F-596158DB4B3A}, 100 + /// + public static ShellItemPropertyKey SenderName => new ShellItemPropertyKey(new Guid("{0DA41CFA-D224-4A18-AE2F-596158DB4B3A}"), 100); + + /// + /// Name: System.Message.Store -- PKEY_Message_Store + /// Description: The store (aka protocol handler) FILE, MAIL, OUTLOOKEXPRESS + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}, 15 + /// + public static ShellItemPropertyKey Store => new ShellItemPropertyKey(new Guid("{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}"), 15); + + /// + /// Name: System.Message.ToAddress -- PKEY_Message_ToAddress + /// Description: Addresses in To: field + /// + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: {E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}, 16 + /// + public static ShellItemPropertyKey ToAddress => new ShellItemPropertyKey(new Guid("{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}"), 16); + + /// + /// Name: System.Message.ToDoFlags -- PKEY_Message_ToDoFlags + /// Description: Flags associated with a message flagged to know if it's still active, if it was custom flagged, etc. + /// + /// Type: Int32 -- VT_I4 + /// FormatID: {1F856A9F-6900-4ABA-9505-2D5F1B4D66CB}, 100 + /// + public static ShellItemPropertyKey ToDoFlags => new ShellItemPropertyKey(new Guid("{1F856A9F-6900-4ABA-9505-2D5F1B4D66CB}"), 100); + + /// + /// Name: System.Message.ToDoTitle -- PKEY_Message_ToDoTitle + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {BCCC8A3C-8CEF-42E5-9B1C-C69079398BC7}, 100 + /// + public static ShellItemPropertyKey ToDoTitle => new ShellItemPropertyKey(new Guid("{BCCC8A3C-8CEF-42E5-9B1C-C69079398BC7}"), 100); + + /// + /// Name: System.Message.ToName -- PKEY_Message_ToName + /// Description: Person names in To: field + /// + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: {E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}, 17 + /// + public static ShellItemPropertyKey ToName => new ShellItemPropertyKey(new Guid("{E3E0584C-B788-4A5A-BB20-7F5A44C9ACDD}"), 17); + } + + /// Music Properties + public static class Music + { + /// + /// Name: System.Music.AlbumArtist -- PKEY_Music_AlbumArtist + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_MUSIC) {56A3372E-CE9C-11D2-9F0E-006097C686F6}, 13 (PIDSI_MUSIC_ALBUM_ARTIST) + /// + public static ShellItemPropertyKey AlbumArtist => new ShellItemPropertyKey(new Guid("{56A3372E-CE9C-11D2-9F0E-006097C686F6}"), 13); + + /// + /// Name: System.Music.AlbumID -- PKEY_Music_AlbumID + /// Description: Concatenation of System.Music.AlbumArtist and System.Music.AlbumTitle, suitable for indexing and display. + ///Used to differentiate albums with the same title from different artists. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_MUSIC) {56A3372E-CE9C-11D2-9F0E-006097C686F6}, 100 + /// + public static ShellItemPropertyKey AlbumID => new ShellItemPropertyKey(new Guid("{56A3372E-CE9C-11D2-9F0E-006097C686F6}"), 100); + + /// + /// Name: System.Music.AlbumTitle -- PKEY_Music_AlbumTitle + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_MUSIC) {56A3372E-CE9C-11D2-9F0E-006097C686F6}, 4 (PIDSI_MUSIC_ALBUM) + /// + public static ShellItemPropertyKey AlbumTitle => new ShellItemPropertyKey(new Guid("{56A3372E-CE9C-11D2-9F0E-006097C686F6}"), 4); + + /// + /// Name: System.Music.Artist -- PKEY_Music_Artist + /// Description: + /// + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: (FMTID_MUSIC) {56A3372E-CE9C-11D2-9F0E-006097C686F6}, 2 (PIDSI_MUSIC_ARTIST) + /// + public static ShellItemPropertyKey Artist => new ShellItemPropertyKey(new Guid("{56A3372E-CE9C-11D2-9F0E-006097C686F6}"), 2); + + /// + /// Name: System.Music.BeatsPerMinute -- PKEY_Music_BeatsPerMinute + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_MUSIC) {56A3372E-CE9C-11D2-9F0E-006097C686F6}, 35 (PIDSI_MUSIC_BEATS_PER_MINUTE) + /// + public static ShellItemPropertyKey BeatsPerMinute => new ShellItemPropertyKey(new Guid("{56A3372E-CE9C-11D2-9F0E-006097C686F6}"), 35); + + /// + /// Name: System.Music.Composer -- PKEY_Music_Composer + /// Description: + /// + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: (PSGUID_MEDIAFILESUMMARYINFORMATION) {64440492-4C8B-11D1-8B70-080036B11A03}, 19 (PIDMSI_COMPOSER) + /// + public static ShellItemPropertyKey Composer => new ShellItemPropertyKey(new Guid("{64440492-4C8B-11D1-8B70-080036B11A03}"), 19); + + /// + /// Name: System.Music.Conductor -- PKEY_Music_Conductor + /// Description: + /// + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: (FMTID_MUSIC) {56A3372E-CE9C-11D2-9F0E-006097C686F6}, 36 (PIDSI_MUSIC_CONDUCTOR) + /// + public static ShellItemPropertyKey Conductor => new ShellItemPropertyKey(new Guid("{56A3372E-CE9C-11D2-9F0E-006097C686F6}"), 36); + + /// + /// Name: System.Music.ContentGroupDescription -- PKEY_Music_ContentGroupDescription + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_MUSIC) {56A3372E-CE9C-11D2-9F0E-006097C686F6}, 33 (PIDSI_MUSIC_CONTENT_GROUP_DESCRIPTION) + /// + public static ShellItemPropertyKey ContentGroupDescription => new ShellItemPropertyKey(new Guid("{56A3372E-CE9C-11D2-9F0E-006097C686F6}"), 33); + + /// + /// Name: System.Music.DisplayArtist -- PKEY_Music_DisplayArtist + /// Description: This property returns the best representation of Album Artist for a given music file + ///based upon AlbumArtist, ContributingArtist and compilation info. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {FD122953-FA93-4EF7-92C3-04C946B2F7C8}, 100 + /// + public static ShellItemPropertyKey DisplayArtist => new ShellItemPropertyKey(new Guid("{FD122953-FA93-4EF7-92C3-04C946B2F7C8}"), 100); + + /// + /// Name: System.Music.Genre -- PKEY_Music_Genre + /// Description: + /// + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: (FMTID_MUSIC) {56A3372E-CE9C-11D2-9F0E-006097C686F6}, 11 (PIDSI_MUSIC_GENRE) + /// + public static ShellItemPropertyKey Genre => new ShellItemPropertyKey(new Guid("{56A3372E-CE9C-11D2-9F0E-006097C686F6}"), 11); + + /// + /// Name: System.Music.InitialKey -- PKEY_Music_InitialKey + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_MUSIC) {56A3372E-CE9C-11D2-9F0E-006097C686F6}, 34 (PIDSI_MUSIC_INITIAL_KEY) + /// + public static ShellItemPropertyKey InitialKey => new ShellItemPropertyKey(new Guid("{56A3372E-CE9C-11D2-9F0E-006097C686F6}"), 34); + + /// + /// Name: System.Music.IsCompilation -- PKEY_Music_IsCompilation + /// Description: Indicates whether the file is part of a compilation. + /// + /// Type: Boolean -- VT_BOOL + /// FormatID: {C449D5CB-9EA4-4809-82E8-AF9D59DED6D1}, 100 + /// + public static ShellItemPropertyKey IsCompilation => new ShellItemPropertyKey(new Guid("{C449D5CB-9EA4-4809-82E8-AF9D59DED6D1}"), 100); + + /// + /// Name: System.Music.Lyrics -- PKEY_Music_Lyrics + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_MUSIC) {56A3372E-CE9C-11D2-9F0E-006097C686F6}, 12 (PIDSI_MUSIC_LYRICS) + /// + public static ShellItemPropertyKey Lyrics => new ShellItemPropertyKey(new Guid("{56A3372E-CE9C-11D2-9F0E-006097C686F6}"), 12); + + /// + /// Name: System.Music.Mood -- PKEY_Music_Mood + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_MUSIC) {56A3372E-CE9C-11D2-9F0E-006097C686F6}, 39 (PIDSI_MUSIC_MOOD) + /// + public static ShellItemPropertyKey Mood => new ShellItemPropertyKey(new Guid("{56A3372E-CE9C-11D2-9F0E-006097C686F6}"), 39); + + /// + /// Name: System.Music.PartOfSet -- PKEY_Music_PartOfSet + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_MUSIC) {56A3372E-CE9C-11D2-9F0E-006097C686F6}, 37 (PIDSI_MUSIC_PART_OF_SET) + /// + public static ShellItemPropertyKey PartOfSet => new ShellItemPropertyKey(new Guid("{56A3372E-CE9C-11D2-9F0E-006097C686F6}"), 37); + + /// + /// Name: System.Music.Period -- PKEY_Music_Period + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (PSGUID_MEDIAFILESUMMARYINFORMATION) {64440492-4C8B-11D1-8B70-080036B11A03}, 31 (PIDMSI_PERIOD) + /// + public static ShellItemPropertyKey Period => new ShellItemPropertyKey(new Guid("{64440492-4C8B-11D1-8B70-080036B11A03}"), 31); + + /// + /// Name: System.Music.SynchronizedLyrics -- PKEY_Music_SynchronizedLyrics + /// Description: + /// Type: Blob -- VT_BLOB + /// FormatID: {6B223B6A-162E-4AA9-B39F-05D678FC6D77}, 100 + /// + public static ShellItemPropertyKey SynchronizedLyrics => new ShellItemPropertyKey(new Guid("{6B223B6A-162E-4AA9-B39F-05D678FC6D77}"), 100); + + /// + /// Name: System.Music.TrackNumber -- PKEY_Music_TrackNumber + /// Description: + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: (FMTID_MUSIC) {56A3372E-CE9C-11D2-9F0E-006097C686F6}, 7 (PIDSI_MUSIC_TRACK) + /// + public static ShellItemPropertyKey TrackNumber => new ShellItemPropertyKey(new Guid("{56A3372E-CE9C-11D2-9F0E-006097C686F6}"), 7); + } + + /// Note Properties + public static class Note + { + /// + /// Name: System.Note.Color -- PKEY_Note_Color + /// Description: + /// Type: UInt16 -- VT_UI2 + /// FormatID: {4776CAFA-BCE4-4CB1-A23E-265E76D8EB11}, 100 + /// + public static ShellItemPropertyKey Color => new ShellItemPropertyKey(new Guid("{4776CAFA-BCE4-4CB1-A23E-265E76D8EB11}"), 100); + + /// + /// Name: System.Note.ColorText -- PKEY_Note_ColorText + /// Description: This is the user-friendly form of System.Note.Color. Not intended to be parsed + ///programmatically. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {46B4E8DE-CDB2-440D-885C-1658EB65B914}, 100 + /// + public static ShellItemPropertyKey ColorText => new ShellItemPropertyKey(new Guid("{46B4E8DE-CDB2-440D-885C-1658EB65B914}"), 100); + } + + /// Notifications Properties + public static class Notifications + { + /// + /// Name: System.Devices.Notifications.LowBattery -- PKEY_Devices_Notification_LowBattery + /// Description: Device Low Battery Notification. + /// + /// Type: Byte -- VT_UI1 + /// FormatID: {C4C07F2B-8524-4E66-AE3A-A6235F103BEB}, 2 + /// + public static ShellItemPropertyKey LowBattery => new ShellItemPropertyKey(new Guid("{C4C07F2B-8524-4E66-AE3A-A6235F103BEB}"), 2); + + /// + /// Name: System.Devices.Notifications.MissedCall -- PKEY_Devices_Notification_MissedCall + /// Description: Device Missed Call Notification. + /// + /// Type: Byte -- VT_UI1 + /// FormatID: {6614EF48-4EFE-4424-9EDA-C79F404EDF3E}, 2 + /// + public static ShellItemPropertyKey MissedCall => new ShellItemPropertyKey(new Guid("{6614EF48-4EFE-4424-9EDA-C79F404EDF3E}"), 2); + + /// + /// Name: System.Devices.Notifications.NewMessage -- PKEY_Devices_Notification_NewMessage + /// Description: Device New Message Notification. + /// + /// Type: Byte -- VT_UI1 + /// FormatID: {2BE9260A-2012-4742-A555-F41B638B7DCB}, 2 + /// + public static ShellItemPropertyKey NewMessage => new ShellItemPropertyKey(new Guid("{2BE9260A-2012-4742-A555-F41B638B7DCB}"), 2); + + /// + /// Name: System.Devices.Notifications.NewVoicemail -- PKEY_Devices_Notification_NewVoicemail + /// Description: Device Voicemail Notification. + /// + /// Type: Byte -- VT_UI1 + /// FormatID: {59569556-0A08-4212-95B9-FAE2AD6413DB}, 2 + /// + public static ShellItemPropertyKey NewVoicemail => new ShellItemPropertyKey(new Guid("{59569556-0A08-4212-95B9-FAE2AD6413DB}"), 2); + + /// + /// Name: System.Devices.Notifications.StorageFull -- PKEY_Devices_Notification_StorageFull + /// Description: Device Storage Full Notification. + /// + /// Type: UInt64 -- VT_UI8 + /// FormatID: {A0E00EE1-F0C7-4D41-B8E7-26A7BD8D38B0}, 2 + /// + public static ShellItemPropertyKey StorageFull => new ShellItemPropertyKey(new Guid("{A0E00EE1-F0C7-4D41-B8E7-26A7BD8D38B0}"), 2); + + /// + /// Name: System.Devices.Notifications.StorageFullLinkText -- PKEY_Devices_Notification_StorageFullLinkText + /// Description: Link Text for the Device Storage Full Notification. + /// + /// Type: UInt64 -- VT_UI8 + /// FormatID: {A0E00EE1-F0C7-4D41-B8E7-26A7BD8D38B0}, 3 + /// + public static ShellItemPropertyKey StorageFullLinkText => new ShellItemPropertyKey(new Guid("{A0E00EE1-F0C7-4D41-B8E7-26A7BD8D38B0}"), 3); + } + + /// Photo Properties + public static class Photo + { + /// + /// Name: System.Photo.Aperture -- PKEY_Photo_Aperture + /// Description: PropertyTagExifAperture. Calculated from PKEY_Photo_ApertureNumerator and PKEY_Photo_ApertureDenominator + /// + /// Type: Double -- VT_R8 + /// FormatID: (FMTID_ImageProperties) {14B81DA1-0135-4D31-96D9-6CBFC9671A99}, 37378 + /// + public static ShellItemPropertyKey Aperture => new ShellItemPropertyKey(new Guid("{14B81DA1-0135-4D31-96D9-6CBFC9671A99}"), 37378); + + /// + /// Name: System.Photo.ApertureDenominator -- PKEY_Photo_ApertureDenominator + /// Description: Denominator of PKEY_Photo_Aperture + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {E1A9A38B-6685-46BD-875E-570DC7AD7320}, 100 + /// + public static ShellItemPropertyKey ApertureDenominator => new ShellItemPropertyKey(new Guid("{E1A9A38B-6685-46BD-875E-570DC7AD7320}"), 100); + + /// + /// Name: System.Photo.ApertureNumerator -- PKEY_Photo_ApertureNumerator + /// Description: Numerator of PKEY_Photo_Aperture + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {0337ECEC-39FB-4581-A0BD-4C4CC51E9914}, 100 + /// + public static ShellItemPropertyKey ApertureNumerator => new ShellItemPropertyKey(new Guid("{0337ECEC-39FB-4581-A0BD-4C4CC51E9914}"), 100); + + /// + /// Name: System.Photo.Brightness -- PKEY_Photo_Brightness + /// Description: This is the brightness of the photo. + /// + ///Calculated from PKEY_Photo_BrightnessNumerator and PKEY_Photo_BrightnessDenominator. + /// + ///The units are "APEX", normally in the range of -99.99 to 99.99. If the numerator of + ///the recorded value is FFFFFFFF.H, "Unknown" should be indicated. + /// + /// Type: Double -- VT_R8 + /// FormatID: {1A701BF6-478C-4361-83AB-3701BB053C58}, 100 (PropertyTagExifBrightness) + /// + public static ShellItemPropertyKey Brightness => new ShellItemPropertyKey(new Guid("{1A701BF6-478C-4361-83AB-3701BB053C58}"), 100); + + /// + /// Name: System.Photo.BrightnessDenominator -- PKEY_Photo_BrightnessDenominator + /// Description: Denominator of PKEY_Photo_Brightness + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {6EBE6946-2321-440A-90F0-C043EFD32476}, 100 + /// + public static ShellItemPropertyKey BrightnessDenominator => new ShellItemPropertyKey(new Guid("{6EBE6946-2321-440A-90F0-C043EFD32476}"), 100); + + /// + /// Name: System.Photo.BrightnessNumerator -- PKEY_Photo_BrightnessNumerator + /// Description: Numerator of PKEY_Photo_Brightness + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {9E7D118F-B314-45A0-8CFB-D654B917C9E9}, 100 + /// + public static ShellItemPropertyKey BrightnessNumerator => new ShellItemPropertyKey(new Guid("{9E7D118F-B314-45A0-8CFB-D654B917C9E9}"), 100); + + /// + /// Name: System.Photo.CameraManufacturer -- PKEY_Photo_CameraManufacturer + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_ImageProperties) {14B81DA1-0135-4D31-96D9-6CBFC9671A99}, 271 (PropertyTagEquipMake) + /// + public static ShellItemPropertyKey CameraManufacturer => new ShellItemPropertyKey(new Guid("{14B81DA1-0135-4D31-96D9-6CBFC9671A99}"), 271); + + /// + /// Name: System.Photo.CameraModel -- PKEY_Photo_CameraModel + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_ImageProperties) {14B81DA1-0135-4D31-96D9-6CBFC9671A99}, 272 (PropertyTagEquipModel) + /// + public static ShellItemPropertyKey CameraModel => new ShellItemPropertyKey(new Guid("{14B81DA1-0135-4D31-96D9-6CBFC9671A99}"), 272); + + /// + /// Name: System.Photo.CameraSerialNumber -- PKEY_Photo_CameraSerialNumber + /// Description: Serial number of camera that produced this photo + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_ImageProperties) {14B81DA1-0135-4D31-96D9-6CBFC9671A99}, 273 + /// + public static ShellItemPropertyKey CameraSerialNumber => new ShellItemPropertyKey(new Guid("{14B81DA1-0135-4D31-96D9-6CBFC9671A99}"), 273); + + /// + /// Name: System.Photo.Contrast -- PKEY_Photo_Contrast + /// Description: This indicates the direction of contrast processing applied by the camera + ///when the image was shot. + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {2A785BA9-8D23-4DED-82E6-60A350C86A10}, 100 + /// + public static ShellItemPropertyKey Contrast => new ShellItemPropertyKey(new Guid("{2A785BA9-8D23-4DED-82E6-60A350C86A10}"), 100); + + /// + /// Name: System.Photo.ContrastText -- PKEY_Photo_ContrastText + /// Description: This is the user-friendly form of System.Photo.Contrast. Not intended to be parsed + ///programmatically. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {59DDE9F2-5253-40EA-9A8B-479E96C6249A}, 100 + /// + public static ShellItemPropertyKey ContrastText => new ShellItemPropertyKey(new Guid("{59DDE9F2-5253-40EA-9A8B-479E96C6249A}"), 100); + + /// + /// Name: System.Photo.DateTaken -- PKEY_Photo_DateTaken + /// Description: PropertyTagExifDTOrig + /// + /// Type: DateTime -- VT_FILETIME (For variants: VT_DATE) + /// FormatID: (FMTID_ImageProperties) {14B81DA1-0135-4D31-96D9-6CBFC9671A99}, 36867 + /// + public static ShellItemPropertyKey DateTaken => new ShellItemPropertyKey(new Guid("{14B81DA1-0135-4D31-96D9-6CBFC9671A99}"), 36867); + + /// + /// Name: System.Photo.DigitalZoom -- PKEY_Photo_DigitalZoom + /// Description: PropertyTagExifDigitalZoom. Calculated from PKEY_Photo_DigitalZoomNumerator and PKEY_Photo_DigitalZoomDenominator + /// + /// Type: Double -- VT_R8 + /// FormatID: {F85BF840-A925-4BC2-B0C4-8E36B598679E}, 100 + /// + public static ShellItemPropertyKey DigitalZoom => new ShellItemPropertyKey(new Guid("{F85BF840-A925-4BC2-B0C4-8E36B598679E}"), 100); + + /// + /// Name: System.Photo.DigitalZoomDenominator -- PKEY_Photo_DigitalZoomDenominator + /// Description: Denominator of PKEY_Photo_DigitalZoom + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {745BAF0E-E5C1-4CFB-8A1B-D031A0A52393}, 100 + /// + public static ShellItemPropertyKey DigitalZoomDenominator => new ShellItemPropertyKey(new Guid("{745BAF0E-E5C1-4CFB-8A1B-D031A0A52393}"), 100); + + /// + /// Name: System.Photo.DigitalZoomNumerator -- PKEY_Photo_DigitalZoomNumerator + /// Description: Numerator of PKEY_Photo_DigitalZoom + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {16CBB924-6500-473B-A5BE-F1599BCBE413}, 100 + /// + public static ShellItemPropertyKey DigitalZoomNumerator => new ShellItemPropertyKey(new Guid("{16CBB924-6500-473B-A5BE-F1599BCBE413}"), 100); + + /// + /// Name: System.Photo.Event -- PKEY_Photo_Event + /// Description: The event at which the photo was taken + /// + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: (FMTID_ImageProperties) {14B81DA1-0135-4D31-96D9-6CBFC9671A99}, 18248 + /// + public static ShellItemPropertyKey Event => new ShellItemPropertyKey(new Guid("{14B81DA1-0135-4D31-96D9-6CBFC9671A99}"), 18248); + + /// + /// Name: System.Photo.EXIFVersion -- PKEY_Photo_EXIFVersion + /// Description: The EXIF version. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {D35F743A-EB2E-47F2-A286-844132CB1427}, 100 + /// + public static ShellItemPropertyKey EXIFVersion => new ShellItemPropertyKey(new Guid("{D35F743A-EB2E-47F2-A286-844132CB1427}"), 100); + + /// + /// Name: System.Photo.ExposureBias -- PKEY_Photo_ExposureBias + /// Description: PropertyTagExifExposureBias. Calculated from PKEY_Photo_ExposureBiasNumerator and PKEY_Photo_ExposureBiasDenominator + /// + /// Type: Double -- VT_R8 + /// FormatID: (FMTID_ImageProperties) {14B81DA1-0135-4D31-96D9-6CBFC9671A99}, 37380 + /// + public static ShellItemPropertyKey ExposureBias => new ShellItemPropertyKey(new Guid("{14B81DA1-0135-4D31-96D9-6CBFC9671A99}"), 37380); + + /// + /// Name: System.Photo.ExposureBiasDenominator -- PKEY_Photo_ExposureBiasDenominator + /// Description: Denominator of PKEY_Photo_ExposureBias + /// + /// Type: Int32 -- VT_I4 + /// FormatID: {AB205E50-04B7-461C-A18C-2F233836E627}, 100 + /// + public static ShellItemPropertyKey ExposureBiasDenominator => new ShellItemPropertyKey(new Guid("{AB205E50-04B7-461C-A18C-2F233836E627}"), 100); + + /// + /// Name: System.Photo.ExposureBiasNumerator -- PKEY_Photo_ExposureBiasNumerator + /// Description: Numerator of PKEY_Photo_ExposureBias + /// + /// Type: Int32 -- VT_I4 + /// FormatID: {738BF284-1D87-420B-92CF-5834BF6EF9ED}, 100 + /// + public static ShellItemPropertyKey ExposureBiasNumerator => new ShellItemPropertyKey(new Guid("{738BF284-1D87-420B-92CF-5834BF6EF9ED}"), 100); + + /// + /// Name: System.Photo.ExposureIndex -- PKEY_Photo_ExposureIndex + /// Description: PropertyTagExifExposureIndex. Calculated from PKEY_Photo_ExposureIndexNumerator and PKEY_Photo_ExposureIndexDenominator + /// + /// Type: Double -- VT_R8 + /// FormatID: {967B5AF8-995A-46ED-9E11-35B3C5B9782D}, 100 + /// + public static ShellItemPropertyKey ExposureIndex => new ShellItemPropertyKey(new Guid("{967B5AF8-995A-46ED-9E11-35B3C5B9782D}"), 100); + + /// + /// Name: System.Photo.ExposureIndexDenominator -- PKEY_Photo_ExposureIndexDenominator + /// Description: Denominator of PKEY_Photo_ExposureIndex + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {93112F89-C28B-492F-8A9D-4BE2062CEE8A}, 100 + /// + public static ShellItemPropertyKey ExposureIndexDenominator => new ShellItemPropertyKey(new Guid("{93112F89-C28B-492F-8A9D-4BE2062CEE8A}"), 100); + + /// + /// Name: System.Photo.ExposureIndexNumerator -- PKEY_Photo_ExposureIndexNumerator + /// Description: Numerator of PKEY_Photo_ExposureIndex + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {CDEDCF30-8919-44DF-8F4C-4EB2FFDB8D89}, 100 + /// + public static ShellItemPropertyKey ExposureIndexNumerator => new ShellItemPropertyKey(new Guid("{CDEDCF30-8919-44DF-8F4C-4EB2FFDB8D89}"), 100); + + /// + /// Name: System.Photo.ExposureProgram -- PKEY_Photo_ExposureProgram + /// Description: + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: (FMTID_ImageProperties) {14B81DA1-0135-4D31-96D9-6CBFC9671A99}, 34850 (PropertyTagExifExposureProg) + /// + public static ShellItemPropertyKey ExposureProgram => new ShellItemPropertyKey(new Guid("{14B81DA1-0135-4D31-96D9-6CBFC9671A99}"), 34850); + + /// + /// Name: System.Photo.ExposureProgramText -- PKEY_Photo_ExposureProgramText + /// Description: This is the user-friendly form of System.Photo.ExposureProgram. Not intended to be parsed + ///programmatically. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {FEC690B7-5F30-4646-AE47-4CAAFBA884A3}, 100 + /// + public static ShellItemPropertyKey ExposureProgramText => new ShellItemPropertyKey(new Guid("{FEC690B7-5F30-4646-AE47-4CAAFBA884A3}"), 100); + + /// + /// Name: System.Photo.ExposureTime -- PKEY_Photo_ExposureTime + /// Description: PropertyTagExifExposureTime. Calculated from PKEY_Photo_ExposureTimeNumerator and PKEY_Photo_ExposureTimeDenominator + /// + /// Type: Double -- VT_R8 + /// FormatID: (FMTID_ImageProperties) {14B81DA1-0135-4D31-96D9-6CBFC9671A99}, 33434 + /// + public static ShellItemPropertyKey ExposureTime => new ShellItemPropertyKey(new Guid("{14B81DA1-0135-4D31-96D9-6CBFC9671A99}"), 33434); + + /// + /// Name: System.Photo.ExposureTimeDenominator -- PKEY_Photo_ExposureTimeDenominator + /// Description: Denominator of PKEY_Photo_ExposureTime + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {55E98597-AD16-42E0-B624-21599A199838}, 100 + /// + public static ShellItemPropertyKey ExposureTimeDenominator => new ShellItemPropertyKey(new Guid("{55E98597-AD16-42E0-B624-21599A199838}"), 100); + + /// + /// Name: System.Photo.ExposureTimeNumerator -- PKEY_Photo_ExposureTimeNumerator + /// Description: Numerator of PKEY_Photo_ExposureTime + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {257E44E2-9031-4323-AC38-85C552871B2E}, 100 + /// + public static ShellItemPropertyKey ExposureTimeNumerator => new ShellItemPropertyKey(new Guid("{257E44E2-9031-4323-AC38-85C552871B2E}"), 100); + + /// + /// Name: System.Photo.Flash -- PKEY_Photo_Flash + /// Description: PropertyTagExifFlash + /// + /// Type: Byte -- VT_UI1 + /// FormatID: (FMTID_ImageProperties) {14B81DA1-0135-4D31-96D9-6CBFC9671A99}, 37385 + /// + public static ShellItemPropertyKey Flash => new ShellItemPropertyKey(new Guid("{14B81DA1-0135-4D31-96D9-6CBFC9671A99}"), 37385); + + /// + /// Name: System.Photo.FlashEnergy -- PKEY_Photo_FlashEnergy + /// Description: PropertyTagExifFlashEnergy. Calculated from PKEY_Photo_FlashEnergyNumerator and PKEY_Photo_FlashEnergyDenominator + /// + /// Type: Double -- VT_R8 + /// FormatID: (FMTID_ImageProperties) {14B81DA1-0135-4D31-96D9-6CBFC9671A99}, 41483 + /// + public static ShellItemPropertyKey FlashEnergy => new ShellItemPropertyKey(new Guid("{14B81DA1-0135-4D31-96D9-6CBFC9671A99}"), 41483); + + /// + /// Name: System.Photo.FlashEnergyDenominator -- PKEY_Photo_FlashEnergyDenominator + /// Description: Denominator of PKEY_Photo_FlashEnergy + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {D7B61C70-6323-49CD-A5FC-C84277162C97}, 100 + /// + public static ShellItemPropertyKey FlashEnergyDenominator => new ShellItemPropertyKey(new Guid("{D7B61C70-6323-49CD-A5FC-C84277162C97}"), 100); + + /// + /// Name: System.Photo.FlashEnergyNumerator -- PKEY_Photo_FlashEnergyNumerator + /// Description: Numerator of PKEY_Photo_FlashEnergy + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {FCAD3D3D-0858-400F-AAA3-2F66CCE2A6BC}, 100 + /// + public static ShellItemPropertyKey FlashEnergyNumerator => new ShellItemPropertyKey(new Guid("{FCAD3D3D-0858-400F-AAA3-2F66CCE2A6BC}"), 100); + + /// + /// Name: System.Photo.FlashManufacturer -- PKEY_Photo_FlashManufacturer + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {AABAF6C9-E0C5-4719-8585-57B103E584FE}, 100 + /// + public static ShellItemPropertyKey FlashManufacturer => new ShellItemPropertyKey(new Guid("{AABAF6C9-E0C5-4719-8585-57B103E584FE}"), 100); + + /// + /// Name: System.Photo.FlashModel -- PKEY_Photo_FlashModel + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {FE83BB35-4D1A-42E2-916B-06F3E1AF719E}, 100 + /// + public static ShellItemPropertyKey FlashModel => new ShellItemPropertyKey(new Guid("{FE83BB35-4D1A-42E2-916B-06F3E1AF719E}"), 100); + + /// + /// Name: System.Photo.FlashText -- PKEY_Photo_FlashText + /// Description: This is the user-friendly form of System.Photo.Flash. Not intended to be parsed + ///programmatically. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {6B8B68F6-200B-47EA-8D25-D8050F57339F}, 100 + /// + public static ShellItemPropertyKey FlashText => new ShellItemPropertyKey(new Guid("{6B8B68F6-200B-47EA-8D25-D8050F57339F}"), 100); + + /// + /// Name: System.Photo.FNumber -- PKEY_Photo_FNumber + /// Description: PropertyTagExifFNumber. Calculated from PKEY_Photo_FNumberNumerator and PKEY_Photo_FNumberDenominator + /// + /// Type: Double -- VT_R8 + /// FormatID: (FMTID_ImageProperties) {14B81DA1-0135-4D31-96D9-6CBFC9671A99}, 33437 + /// + public static ShellItemPropertyKey FNumber => new ShellItemPropertyKey(new Guid("{14B81DA1-0135-4D31-96D9-6CBFC9671A99}"), 33437); + + /// + /// Name: System.Photo.FNumberDenominator -- PKEY_Photo_FNumberDenominator + /// Description: Denominator of PKEY_Photo_FNumber + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {E92A2496-223B-4463-A4E3-30EABBA79D80}, 100 + /// + public static ShellItemPropertyKey FNumberDenominator => new ShellItemPropertyKey(new Guid("{E92A2496-223B-4463-A4E3-30EABBA79D80}"), 100); + + /// + /// Name: System.Photo.FNumberNumerator -- PKEY_Photo_FNumberNumerator + /// Description: Numerator of PKEY_Photo_FNumber + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {1B97738A-FDFC-462F-9D93-1957E08BE90C}, 100 + /// + public static ShellItemPropertyKey FNumberNumerator => new ShellItemPropertyKey(new Guid("{1B97738A-FDFC-462F-9D93-1957E08BE90C}"), 100); + + /// + /// Name: System.Photo.FocalLength -- PKEY_Photo_FocalLength + /// Description: PropertyTagExifFocalLength. Calculated from PKEY_Photo_FocalLengthNumerator and PKEY_Photo_FocalLengthDenominator + /// + /// Type: Double -- VT_R8 + /// FormatID: (FMTID_ImageProperties) {14B81DA1-0135-4D31-96D9-6CBFC9671A99}, 37386 + /// + public static ShellItemPropertyKey FocalLength => new ShellItemPropertyKey(new Guid("{14B81DA1-0135-4D31-96D9-6CBFC9671A99}"), 37386); + + /// + /// Name: System.Photo.FocalLengthDenominator -- PKEY_Photo_FocalLengthDenominator + /// Description: Denominator of PKEY_Photo_FocalLength + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {305BC615-DCA1-44A5-9FD4-10C0BA79412E}, 100 + /// + public static ShellItemPropertyKey FocalLengthDenominator => new ShellItemPropertyKey(new Guid("{305BC615-DCA1-44A5-9FD4-10C0BA79412E}"), 100); + + /// + /// Name: System.Photo.FocalLengthInFilm -- PKEY_Photo_FocalLengthInFilm + /// Description: + /// Type: UInt16 -- VT_UI2 + /// FormatID: {A0E74609-B84D-4F49-B860-462BD9971F98}, 100 + /// + public static ShellItemPropertyKey FocalLengthInFilm => new ShellItemPropertyKey(new Guid("{A0E74609-B84D-4F49-B860-462BD9971F98}"), 100); + + /// + /// Name: System.Photo.FocalLengthNumerator -- PKEY_Photo_FocalLengthNumerator + /// Description: Numerator of PKEY_Photo_FocalLength + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {776B6B3B-1E3D-4B0C-9A0E-8FBAF2A8492A}, 100 + /// + public static ShellItemPropertyKey FocalLengthNumerator => new ShellItemPropertyKey(new Guid("{776B6B3B-1E3D-4B0C-9A0E-8FBAF2A8492A}"), 100); + + /// + /// Name: System.Photo.FocalPlaneXResolution -- PKEY_Photo_FocalPlaneXResolution + /// Description: PropertyTagExifFocalXRes. Calculated from PKEY_Photo_FocalPlaneXResolutionNumerator and + ///PKEY_Photo_FocalPlaneXResolutionDenominator. + /// + /// Type: Double -- VT_R8 + /// FormatID: {CFC08D97-C6F7-4484-89DD-EBEF4356FE76}, 100 + /// + public static ShellItemPropertyKey FocalPlaneXResolution => new ShellItemPropertyKey(new Guid("{CFC08D97-C6F7-4484-89DD-EBEF4356FE76}"), 100); + + /// + /// Name: System.Photo.FocalPlaneXResolutionDenominator -- PKEY_Photo_FocalPlaneXResolutionDenominator + /// Description: Denominator of PKEY_Photo_FocalPlaneXResolution + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {0933F3F5-4786-4F46-A8E8-D64DD37FA521}, 100 + /// + public static ShellItemPropertyKey FocalPlaneXResolutionDenominator => new ShellItemPropertyKey(new Guid("{0933F3F5-4786-4F46-A8E8-D64DD37FA521}"), 100); + + /// + /// Name: System.Photo.FocalPlaneXResolutionNumerator -- PKEY_Photo_FocalPlaneXResolutionNumerator + /// Description: Numerator of PKEY_Photo_FocalPlaneXResolution + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {DCCB10AF-B4E2-4B88-95F9-031B4D5AB490}, 100 + /// + public static ShellItemPropertyKey FocalPlaneXResolutionNumerator => new ShellItemPropertyKey(new Guid("{DCCB10AF-B4E2-4B88-95F9-031B4D5AB490}"), 100); + + /// + /// Name: System.Photo.FocalPlaneYResolution -- PKEY_Photo_FocalPlaneYResolution + /// Description: PropertyTagExifFocalYRes. Calculated from PKEY_Photo_FocalPlaneYResolutionNumerator and + ///PKEY_Photo_FocalPlaneYResolutionDenominator. + /// + /// Type: Double -- VT_R8 + /// FormatID: {4FFFE4D0-914F-4AC4-8D6F-C9C61DE169B1}, 100 + /// + public static ShellItemPropertyKey FocalPlaneYResolution => new ShellItemPropertyKey(new Guid("{4FFFE4D0-914F-4AC4-8D6F-C9C61DE169B1}"), 100); + + /// + /// Name: System.Photo.FocalPlaneYResolutionDenominator -- PKEY_Photo_FocalPlaneYResolutionDenominator + /// Description: Denominator of PKEY_Photo_FocalPlaneYResolution + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {1D6179A6-A876-4031-B013-3347B2B64DC8}, 100 + /// + public static ShellItemPropertyKey FocalPlaneYResolutionDenominator => new ShellItemPropertyKey(new Guid("{1D6179A6-A876-4031-B013-3347B2B64DC8}"), 100); + + /// + /// Name: System.Photo.FocalPlaneYResolutionNumerator -- PKEY_Photo_FocalPlaneYResolutionNumerator + /// Description: Numerator of PKEY_Photo_FocalPlaneYResolution + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {A2E541C5-4440-4BA8-867E-75CFC06828CD}, 100 + /// + public static ShellItemPropertyKey FocalPlaneYResolutionNumerator => new ShellItemPropertyKey(new Guid("{A2E541C5-4440-4BA8-867E-75CFC06828CD}"), 100); + + /// + /// Name: System.Photo.GainControl -- PKEY_Photo_GainControl + /// Description: This indicates the degree of overall image gain adjustment. + /// + ///Calculated from PKEY_Photo_GainControlNumerator and PKEY_Photo_GainControlDenominator. + /// + /// Type: Double -- VT_R8 + /// FormatID: {FA304789-00C7-4D80-904A-1E4DCC7265AA}, 100 (PropertyTagExifGainControl) + /// + public static ShellItemPropertyKey GainControl => new ShellItemPropertyKey(new Guid("{FA304789-00C7-4D80-904A-1E4DCC7265AA}"), 100); + + /// + /// Name: System.Photo.GainControlDenominator -- PKEY_Photo_GainControlDenominator + /// Description: Denominator of PKEY_Photo_GainControl + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {42864DFD-9DA4-4F77-BDED-4AAD7B256735}, 100 + /// + public static ShellItemPropertyKey GainControlDenominator => new ShellItemPropertyKey(new Guid("{42864DFD-9DA4-4F77-BDED-4AAD7B256735}"), 100); + + /// + /// Name: System.Photo.GainControlNumerator -- PKEY_Photo_GainControlNumerator + /// Description: Numerator of PKEY_Photo_GainControl + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {8E8ECF7C-B7B8-4EB8-A63F-0EE715C96F9E}, 100 + /// + public static ShellItemPropertyKey GainControlNumerator => new ShellItemPropertyKey(new Guid("{8E8ECF7C-B7B8-4EB8-A63F-0EE715C96F9E}"), 100); + + /// + /// Name: System.Photo.GainControlText -- PKEY_Photo_GainControlText + /// Description: This is the user-friendly form of System.Photo.GainControl. Not intended to be parsed + ///programmatically. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {C06238B2-0BF9-4279-A723-25856715CB9D}, 100 + /// + public static ShellItemPropertyKey GainControlText => new ShellItemPropertyKey(new Guid("{C06238B2-0BF9-4279-A723-25856715CB9D}"), 100); + + /// + /// Name: System.Photo.ISOSpeed -- PKEY_Photo_ISOSpeed + /// Description: PropertyTagExifISOSpeed + /// + /// Type: UInt16 -- VT_UI2 + /// FormatID: (FMTID_ImageProperties) {14B81DA1-0135-4D31-96D9-6CBFC9671A99}, 34855 + /// + public static ShellItemPropertyKey ISOSpeed => new ShellItemPropertyKey(new Guid("{14B81DA1-0135-4D31-96D9-6CBFC9671A99}"), 34855); + + /// + /// Name: System.Photo.LensManufacturer -- PKEY_Photo_LensManufacturer + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {E6DDCAF7-29C5-4F0A-9A68-D19412EC7090}, 100 + /// + public static ShellItemPropertyKey LensManufacturer => new ShellItemPropertyKey(new Guid("{E6DDCAF7-29C5-4F0A-9A68-D19412EC7090}"), 100); + + /// + /// Name: System.Photo.LensModel -- PKEY_Photo_LensModel + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {E1277516-2B5F-4869-89B1-2E585BD38B7A}, 100 + /// + public static ShellItemPropertyKey LensModel => new ShellItemPropertyKey(new Guid("{E1277516-2B5F-4869-89B1-2E585BD38B7A}"), 100); + + /// + /// Name: System.Photo.LightSource -- PKEY_Photo_LightSource + /// Description: PropertyTagExifLightSource + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: (FMTID_ImageProperties) {14B81DA1-0135-4D31-96D9-6CBFC9671A99}, 37384 + /// + public static ShellItemPropertyKey LightSource => new ShellItemPropertyKey(new Guid("{14B81DA1-0135-4D31-96D9-6CBFC9671A99}"), 37384); + + /// + /// Name: System.Photo.MakerNote -- PKEY_Photo_MakerNote + /// Description: + /// Type: Buffer -- VT_VECTOR | VT_UI1 (For variants: VT_ARRAY | VT_UI1) + /// FormatID: {FA303353-B659-4052-85E9-BCAC79549B84}, 100 + /// + public static ShellItemPropertyKey MakerNote => new ShellItemPropertyKey(new Guid("{FA303353-B659-4052-85E9-BCAC79549B84}"), 100); + + /// + /// Name: System.Photo.MakerNoteOffset -- PKEY_Photo_MakerNoteOffset + /// Description: + /// Type: UInt64 -- VT_UI8 + /// FormatID: {813F4124-34E6-4D17-AB3E-6B1F3C2247A1}, 100 + /// + public static ShellItemPropertyKey MakerNoteOffset => new ShellItemPropertyKey(new Guid("{813F4124-34E6-4D17-AB3E-6B1F3C2247A1}"), 100); + + /// + /// Name: System.Photo.MaxAperture -- PKEY_Photo_MaxAperture + /// Description: Calculated from PKEY_Photo_MaxApertureNumerator and PKEY_Photo_MaxApertureDenominator + /// + /// Type: Double -- VT_R8 + /// FormatID: {08F6D7C2-E3F2-44FC-AF1E-5AA5C81A2D3E}, 100 + /// + public static ShellItemPropertyKey MaxAperture => new ShellItemPropertyKey(new Guid("{08F6D7C2-E3F2-44FC-AF1E-5AA5C81A2D3E}"), 100); + + /// + /// Name: System.Photo.MaxApertureDenominator -- PKEY_Photo_MaxApertureDenominator + /// Description: Denominator of PKEY_Photo_MaxAperture + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {C77724D4-601F-46C5-9B89-C53F93BCEB77}, 100 + /// + public static ShellItemPropertyKey MaxApertureDenominator => new ShellItemPropertyKey(new Guid("{C77724D4-601F-46C5-9B89-C53F93BCEB77}"), 100); + + /// + /// Name: System.Photo.MaxApertureNumerator -- PKEY_Photo_MaxApertureNumerator + /// Description: Numerator of PKEY_Photo_MaxAperture + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {C107E191-A459-44C5-9AE6-B952AD4B906D}, 100 + /// + public static ShellItemPropertyKey MaxApertureNumerator => new ShellItemPropertyKey(new Guid("{C107E191-A459-44C5-9AE6-B952AD4B906D}"), 100); + + /// + /// Name: System.Photo.MeteringMode -- PKEY_Photo_MeteringMode + /// Description: PropertyTagExifMeteringMode + /// + /// Type: UInt16 -- VT_UI2 + /// FormatID: (FMTID_ImageProperties) {14B81DA1-0135-4D31-96D9-6CBFC9671A99}, 37383 + /// + public static ShellItemPropertyKey MeteringMode => new ShellItemPropertyKey(new Guid("{14B81DA1-0135-4D31-96D9-6CBFC9671A99}"), 37383); + + /// + /// Name: System.Photo.MeteringModeText -- PKEY_Photo_MeteringModeText + /// Description: This is the user-friendly form of System.Photo.MeteringMode. Not intended to be parsed + ///programmatically. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {F628FD8C-7BA8-465A-A65B-C5AA79263A9E}, 100 + /// + public static ShellItemPropertyKey MeteringModeText => new ShellItemPropertyKey(new Guid("{F628FD8C-7BA8-465A-A65B-C5AA79263A9E}"), 100); + + /// + /// Name: System.Photo.Orientation -- PKEY_Photo_Orientation + /// Description: This is the image orientation viewed in terms of rows and columns. + /// + /// Type: UInt16 -- VT_UI2 + /// FormatID: (FMTID_ImageProperties) {14B81DA1-0135-4D31-96D9-6CBFC9671A99}, 274 (PropertyTagOrientation) + /// + public static ShellItemPropertyKey Orientation => new ShellItemPropertyKey(new Guid("{14B81DA1-0135-4D31-96D9-6CBFC9671A99}"), 274); + + /// + /// Name: System.Photo.OrientationText -- PKEY_Photo_OrientationText + /// Description: This is the user-friendly form of System.Photo.Orientation. Not intended to be parsed + ///programmatically. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {A9EA193C-C511-498A-A06B-58E2776DCC28}, 100 + /// + public static ShellItemPropertyKey OrientationText => new ShellItemPropertyKey(new Guid("{A9EA193C-C511-498A-A06B-58E2776DCC28}"), 100); + + /// + /// Name: System.Photo.PeopleNames -- PKEY_Photo_PeopleNames + /// Description: The people tags on an image. + /// + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) Legacy code may treat this as VT_LPSTR. + /// FormatID: {E8309B6E-084C-49B4-B1FC-90A80331B638}, 100 + /// + public static ShellItemPropertyKey PeopleNames => new ShellItemPropertyKey(new Guid("{E8309B6E-084C-49B4-B1FC-90A80331B638}"), 100); + + /// + /// Name: System.Photo.PhotometricInterpretation -- PKEY_Photo_PhotometricInterpretation + /// Description: This is the pixel composition. In JPEG compressed data, a JPEG marker is used + ///instead of this property. + /// + /// Type: UInt16 -- VT_UI2 + /// FormatID: {341796F1-1DF9-4B1C-A564-91BDEFA43877}, 100 + /// + public static ShellItemPropertyKey PhotometricInterpretation => new ShellItemPropertyKey(new Guid("{341796F1-1DF9-4B1C-A564-91BDEFA43877}"), 100); + + /// + /// Name: System.Photo.PhotometricInterpretationText -- PKEY_Photo_PhotometricInterpretationText + /// Description: This is the user-friendly form of System.Photo.PhotometricInterpretation. Not intended to be parsed + ///programmatically. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {821437D6-9EAB-4765-A589-3B1CBBD22A61}, 100 + /// + public static ShellItemPropertyKey PhotometricInterpretationText => new ShellItemPropertyKey(new Guid("{821437D6-9EAB-4765-A589-3B1CBBD22A61}"), 100); + + /// + /// Name: System.Photo.ProgramMode -- PKEY_Photo_ProgramMode + /// Description: This is the class of the program used by the camera to set exposure when the + ///picture is taken. + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {6D217F6D-3F6A-4825-B470-5F03CA2FBE9B}, 100 + /// + public static ShellItemPropertyKey ProgramMode => new ShellItemPropertyKey(new Guid("{6D217F6D-3F6A-4825-B470-5F03CA2FBE9B}"), 100); + + /// + /// Name: System.Photo.ProgramModeText -- PKEY_Photo_ProgramModeText + /// Description: This is the user-friendly form of System.Photo.ProgramMode. Not intended to be parsed + ///programmatically. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {7FE3AA27-2648-42F3-89B0-454E5CB150C3}, 100 + /// + public static ShellItemPropertyKey ProgramModeText => new ShellItemPropertyKey(new Guid("{7FE3AA27-2648-42F3-89B0-454E5CB150C3}"), 100); + + /// + /// Name: System.Photo.RelatedSoundFile -- PKEY_Photo_RelatedSoundFile + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {318A6B45-087F-4DC2-B8CC-05359551FC9E}, 100 + /// + public static ShellItemPropertyKey RelatedSoundFile => new ShellItemPropertyKey(new Guid("{318A6B45-087F-4DC2-B8CC-05359551FC9E}"), 100); + + /// + /// Name: System.Photo.Saturation -- PKEY_Photo_Saturation + /// Description: This indicates the direction of saturation processing applied by the camera when + ///the image was shot. + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {49237325-A95A-4F67-B211-816B2D45D2E0}, 100 + /// + public static ShellItemPropertyKey Saturation => new ShellItemPropertyKey(new Guid("{49237325-A95A-4F67-B211-816B2D45D2E0}"), 100); + + /// + /// Name: System.Photo.SaturationText -- PKEY_Photo_SaturationText + /// Description: This is the user-friendly form of System.Photo.Saturation. Not intended to be parsed + ///programmatically. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {61478C08-B600-4A84-BBE4-E99C45F0A072}, 100 + /// + public static ShellItemPropertyKey SaturationText => new ShellItemPropertyKey(new Guid("{61478C08-B600-4A84-BBE4-E99C45F0A072}"), 100); + + /// + /// Name: System.Photo.Sharpness -- PKEY_Photo_Sharpness + /// Description: This indicates the direction of sharpness processing applied by the camera when + ///the image was shot. + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {FC6976DB-8349-4970-AE97-B3C5316A08F0}, 100 + /// + public static ShellItemPropertyKey Sharpness => new ShellItemPropertyKey(new Guid("{FC6976DB-8349-4970-AE97-B3C5316A08F0}"), 100); + + /// + /// Name: System.Photo.SharpnessText -- PKEY_Photo_SharpnessText + /// Description: This is the user-friendly form of System.Photo.Sharpness. Not intended to be parsed + ///programmatically. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {51EC3F47-DD50-421D-8769-334F50424B1E}, 100 + /// + public static ShellItemPropertyKey SharpnessText => new ShellItemPropertyKey(new Guid("{51EC3F47-DD50-421D-8769-334F50424B1E}"), 100); + + /// + /// Name: System.Photo.ShutterSpeed -- PKEY_Photo_ShutterSpeed + /// Description: PropertyTagExifShutterSpeed. Calculated from PKEY_Photo_ShutterSpeedNumerator and PKEY_Photo_ShutterSpeedDenominator + /// + /// Type: Double -- VT_R8 + /// FormatID: (FMTID_ImageProperties) {14B81DA1-0135-4D31-96D9-6CBFC9671A99}, 37377 + /// + public static ShellItemPropertyKey ShutterSpeed => new ShellItemPropertyKey(new Guid("{14B81DA1-0135-4D31-96D9-6CBFC9671A99}"), 37377); + + /// + /// Name: System.Photo.ShutterSpeedDenominator -- PKEY_Photo_ShutterSpeedDenominator + /// Description: Denominator of PKEY_Photo_ShutterSpeed + /// + /// Type: Int32 -- VT_I4 + /// FormatID: {E13D8975-81C7-4948-AE3F-37CAE11E8FF7}, 100 + /// + public static ShellItemPropertyKey ShutterSpeedDenominator => new ShellItemPropertyKey(new Guid("{E13D8975-81C7-4948-AE3F-37CAE11E8FF7}"), 100); + + /// + /// Name: System.Photo.ShutterSpeedNumerator -- PKEY_Photo_ShutterSpeedNumerator + /// Description: Numerator of PKEY_Photo_ShutterSpeed + /// + /// Type: Int32 -- VT_I4 + /// FormatID: {16EA4042-D6F4-4BCA-8349-7C78D30FB333}, 100 + /// + public static ShellItemPropertyKey ShutterSpeedNumerator => new ShellItemPropertyKey(new Guid("{16EA4042-D6F4-4BCA-8349-7C78D30FB333}"), 100); + + /// + /// Name: System.Photo.SubjectDistance -- PKEY_Photo_SubjectDistance + /// Description: PropertyTagExifSubjectDist. Calculated from PKEY_Photo_SubjectDistanceNumerator and PKEY_Photo_SubjectDistanceDenominator + /// + /// Type: Double -- VT_R8 + /// FormatID: (FMTID_ImageProperties) {14B81DA1-0135-4D31-96D9-6CBFC9671A99}, 37382 + /// + public static ShellItemPropertyKey SubjectDistance => new ShellItemPropertyKey(new Guid("{14B81DA1-0135-4D31-96D9-6CBFC9671A99}"), 37382); + + /// + /// Name: System.Photo.SubjectDistanceDenominator -- PKEY_Photo_SubjectDistanceDenominator + /// Description: Denominator of PKEY_Photo_SubjectDistance + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {0C840A88-B043-466D-9766-D4B26DA3FA77}, 100 + /// + public static ShellItemPropertyKey SubjectDistanceDenominator => new ShellItemPropertyKey(new Guid("{0C840A88-B043-466D-9766-D4B26DA3FA77}"), 100); + + /// + /// Name: System.Photo.SubjectDistanceNumerator -- PKEY_Photo_SubjectDistanceNumerator + /// Description: Numerator of PKEY_Photo_SubjectDistance + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {8AF4961C-F526-43E5-AA81-DB768219178D}, 100 + /// + public static ShellItemPropertyKey SubjectDistanceNumerator => new ShellItemPropertyKey(new Guid("{8AF4961C-F526-43E5-AA81-DB768219178D}"), 100); + + /// + /// Name: System.Photo.TagViewAggregate -- PKEY_Photo_TagViewAggregate + /// Description: A read-only aggregation of tag-like properties for use in building views. + /// + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) Legacy code may treat this as VT_LPSTR. + /// FormatID: {B812F15D-C2D8-4BBF-BACD-79744346113F}, 100 + /// + public static ShellItemPropertyKey TagViewAggregate => new ShellItemPropertyKey(new Guid("{B812F15D-C2D8-4BBF-BACD-79744346113F}"), 100); + + /// + /// Name: System.Photo.TranscodedForSync -- PKEY_Photo_TranscodedForSync + /// Description: + /// Type: Boolean -- VT_BOOL + /// FormatID: {9A8EBB75-6458-4E82-BACB-35C0095B03BB}, 100 + /// + public static ShellItemPropertyKey TranscodedForSync => new ShellItemPropertyKey(new Guid("{9A8EBB75-6458-4E82-BACB-35C0095B03BB}"), 100); + + /// + /// Name: System.Photo.WhiteBalance -- PKEY_Photo_WhiteBalance + /// Description: This indicates the white balance mode set when the image was shot. + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {EE3D3D8A-5381-4CFA-B13B-AAF66B5F4EC9}, 100 + /// + public static ShellItemPropertyKey WhiteBalance => new ShellItemPropertyKey(new Guid("{EE3D3D8A-5381-4CFA-B13B-AAF66B5F4EC9}"), 100); + + /// + /// Name: System.Photo.WhiteBalanceText -- PKEY_Photo_WhiteBalanceText + /// Description: This is the user-friendly form of System.Photo.WhiteBalance. Not intended to be parsed + ///programmatically. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {6336B95E-C7A7-426D-86FD-7AE3D39C84B4}, 100 + /// + public static ShellItemPropertyKey WhiteBalanceText => new ShellItemPropertyKey(new Guid("{6336B95E-C7A7-426D-86FD-7AE3D39C84B4}"), 100); + } + + /// PropGroup Properties + public static class PropGroup + { + /// + /// Name: System.PropGroup.Advanced -- PKEY_PropGroup_Advanced + /// Description: + /// Type: Null -- VT_NULL + /// FormatID: {900A403B-097B-4B95-8AE2-071FDAEEB118}, 100 + /// + public static ShellItemPropertyKey Advanced => new ShellItemPropertyKey(new Guid("{900A403B-097B-4B95-8AE2-071FDAEEB118}"), 100); + + /// + /// Name: System.PropGroup.Audio -- PKEY_PropGroup_Audio + /// Description: + /// Type: Null -- VT_NULL + /// FormatID: {2804D469-788F-48AA-8570-71B9C187E138}, 100 + /// + public static ShellItemPropertyKey Audio => new ShellItemPropertyKey(new Guid("{2804D469-788F-48AA-8570-71B9C187E138}"), 100); + + /// + /// Name: System.PropGroup.Calendar -- PKEY_PropGroup_Calendar + /// Description: + /// Type: Null -- VT_NULL + /// FormatID: {9973D2B5-BFD8-438A-BA94-5349B293181A}, 100 + /// + public static ShellItemPropertyKey Calendar => new ShellItemPropertyKey(new Guid("{9973D2B5-BFD8-438A-BA94-5349B293181A}"), 100); + + /// + /// Name: System.PropGroup.Camera -- PKEY_PropGroup_Camera + /// Description: + /// Type: Null -- VT_NULL + /// FormatID: {DE00DE32-547E-4981-AD4B-542F2E9007D8}, 100 + /// + public static ShellItemPropertyKey Camera => new ShellItemPropertyKey(new Guid("{DE00DE32-547E-4981-AD4B-542F2E9007D8}"), 100); + + /// + /// Name: System.PropGroup.Contact -- PKEY_PropGroup_Contact + /// Description: + /// Type: Null -- VT_NULL + /// FormatID: {DF975FD3-250A-4004-858F-34E29A3E37AA}, 100 + /// + public static ShellItemPropertyKey Contact => new ShellItemPropertyKey(new Guid("{DF975FD3-250A-4004-858F-34E29A3E37AA}"), 100); + + /// + /// Name: System.PropGroup.Content -- PKEY_PropGroup_Content + /// Description: + /// Type: Null -- VT_NULL + /// FormatID: {D0DAB0BA-368A-4050-A882-6C010FD19A4F}, 100 + /// + public static ShellItemPropertyKey Content => new ShellItemPropertyKey(new Guid("{D0DAB0BA-368A-4050-A882-6C010FD19A4F}"), 100); + + /// + /// Name: System.PropGroup.Description -- PKEY_PropGroup_Description + /// Description: + /// Type: Null -- VT_NULL + /// FormatID: {8969B275-9475-4E00-A887-FF93B8B41E44}, 100 + /// + public static ShellItemPropertyKey Description => new ShellItemPropertyKey(new Guid("{8969B275-9475-4E00-A887-FF93B8B41E44}"), 100); + + /// + /// Name: System.PropGroup.FileSystem -- PKEY_PropGroup_FileSystem + /// Description: + /// Type: Null -- VT_NULL + /// FormatID: {E3A7D2C1-80FC-4B40-8F34-30EA111BDC2E}, 100 + /// + public static ShellItemPropertyKey FileSystem => new ShellItemPropertyKey(new Guid("{E3A7D2C1-80FC-4B40-8F34-30EA111BDC2E}"), 100); + + /// + /// Name: System.PropGroup.General -- PKEY_PropGroup_General + /// Description: + /// Type: Null -- VT_NULL + /// FormatID: {CC301630-B192-4C22-B372-9F4C6D338E07}, 100 + /// + public static ShellItemPropertyKey General => new ShellItemPropertyKey(new Guid("{CC301630-B192-4C22-B372-9F4C6D338E07}"), 100); + + /// + /// Name: System.PropGroup.GPS -- PKEY_PropGroup_GPS + /// Description: + /// Type: Null -- VT_NULL + /// FormatID: {F3713ADA-90E3-4E11-AAE5-FDC17685B9BE}, 100 + /// + public static ShellItemPropertyKey GPS => new ShellItemPropertyKey(new Guid("{F3713ADA-90E3-4E11-AAE5-FDC17685B9BE}"), 100); + + /// + /// Name: System.PropGroup.Image -- PKEY_PropGroup_Image + /// Description: + /// Type: Null -- VT_NULL + /// FormatID: {E3690A87-0FA8-4A2A-9A9F-FCE8827055AC}, 100 + /// + public static ShellItemPropertyKey Image => new ShellItemPropertyKey(new Guid("{E3690A87-0FA8-4A2A-9A9F-FCE8827055AC}"), 100); + + /// + /// Name: System.PropGroup.Media -- PKEY_PropGroup_Media + /// Description: + /// Type: Null -- VT_NULL + /// FormatID: {61872CF7-6B5E-4B4B-AC2D-59DA84459248}, 100 + /// + public static ShellItemPropertyKey Media => new ShellItemPropertyKey(new Guid("{61872CF7-6B5E-4B4B-AC2D-59DA84459248}"), 100); + + /// + /// Name: System.PropGroup.MediaAdvanced -- PKEY_PropGroup_MediaAdvanced + /// Description: + /// Type: Null -- VT_NULL + /// FormatID: {8859A284-DE7E-4642-99BA-D431D044B1EC}, 100 + /// + public static ShellItemPropertyKey MediaAdvanced => new ShellItemPropertyKey(new Guid("{8859A284-DE7E-4642-99BA-D431D044B1EC}"), 100); + + /// + /// Name: System.PropGroup.Message -- PKEY_PropGroup_Message + /// Description: + /// Type: Null -- VT_NULL + /// FormatID: {7FD7259D-16B4-4135-9F97-7C96ECD2FA9E}, 100 + /// + public static ShellItemPropertyKey Message => new ShellItemPropertyKey(new Guid("{7FD7259D-16B4-4135-9F97-7C96ECD2FA9E}"), 100); + + /// + /// Name: System.PropGroup.Music -- PKEY_PropGroup_Music + /// Description: + /// Type: Null -- VT_NULL + /// FormatID: {68DD6094-7216-40F1-A029-43FE7127043F}, 100 + /// + public static ShellItemPropertyKey Music => new ShellItemPropertyKey(new Guid("{68DD6094-7216-40F1-A029-43FE7127043F}"), 100); + + /// + /// Name: System.PropGroup.Origin -- PKEY_PropGroup_Origin + /// Description: + /// Type: Null -- VT_NULL + /// FormatID: {2598D2FB-5569-4367-95DF-5CD3A177E1A5}, 100 + /// + public static ShellItemPropertyKey Origin => new ShellItemPropertyKey(new Guid("{2598D2FB-5569-4367-95DF-5CD3A177E1A5}"), 100); + + /// + /// Name: System.PropGroup.PhotoAdvanced -- PKEY_PropGroup_PhotoAdvanced + /// Description: + /// Type: Null -- VT_NULL + /// FormatID: {0CB2BF5A-9EE7-4A86-8222-F01E07FDADAF}, 100 + /// + public static ShellItemPropertyKey PhotoAdvanced => new ShellItemPropertyKey(new Guid("{0CB2BF5A-9EE7-4A86-8222-F01E07FDADAF}"), 100); + + /// + /// Name: System.PropGroup.RecordedTV -- PKEY_PropGroup_RecordedTV + /// Description: + /// Type: Null -- VT_NULL + /// FormatID: {E7B33238-6584-4170-A5C0-AC25EFD9DA56}, 100 + /// + public static ShellItemPropertyKey RecordedTV => new ShellItemPropertyKey(new Guid("{E7B33238-6584-4170-A5C0-AC25EFD9DA56}"), 100); + + /// + /// Name: System.PropGroup.Video -- PKEY_PropGroup_Video + /// Description: + /// Type: Null -- VT_NULL + /// FormatID: {BEBE0920-7671-4C54-A3EB-49FDDFC191EE}, 100 + /// + public static ShellItemPropertyKey Video => new ShellItemPropertyKey(new Guid("{BEBE0920-7671-4C54-A3EB-49FDDFC191EE}"), 100); + } + + /// PropList Properties + public static class PropList + { + /// + /// Name: System.PropList.ConflictPrompt -- PKEY_PropList_ConflictPrompt + /// Description: The list of properties to show in the file operation conflict resolution dialog. Properties with empty + ///values will not be displayed. Register under the regvalue of "ConflictPrompt". + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {C9944A21-A406-48FE-8225-AEC7E24C211B}, 11 + /// + public static ShellItemPropertyKey ConflictPrompt => new ShellItemPropertyKey(new Guid("{C9944A21-A406-48FE-8225-AEC7E24C211B}"), 11); + + /// + /// Name: System.PropList.ContentViewModeForBrowse -- PKEY_PropList_ContentViewModeForBrowse + /// Description: The list of properties to show in the content view mode of an item in the context of browsing. + ///Register the regvalue under the name of "ContentViewModeForBrowse". + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {C9944A21-A406-48FE-8225-AEC7E24C211B}, 13 + /// + public static ShellItemPropertyKey ContentViewModeForBrowse => new ShellItemPropertyKey(new Guid("{C9944A21-A406-48FE-8225-AEC7E24C211B}"), 13); + + /// + /// Name: System.PropList.ContentViewModeForSearch -- PKEY_PropList_ContentViewModeForSearch + /// Description: The list of properties to show in the content view mode of an item in the context of searching. + ///Register the regvalue under the name of "ContentViewModeForSearch". + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {C9944A21-A406-48FE-8225-AEC7E24C211B}, 14 + /// + public static ShellItemPropertyKey ContentViewModeForSearch => new ShellItemPropertyKey(new Guid("{C9944A21-A406-48FE-8225-AEC7E24C211B}"), 14); + + /// + /// Name: System.PropList.ExtendedTileInfo -- PKEY_PropList_ExtendedTileInfo + /// Description: The list of properties to show in the listview on extended tiles. Register under the regvalue of + ///"ExtendedTileInfo". + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {C9944A21-A406-48FE-8225-AEC7E24C211B}, 9 + /// + public static ShellItemPropertyKey ExtendedTileInfo => new ShellItemPropertyKey(new Guid("{C9944A21-A406-48FE-8225-AEC7E24C211B}"), 9); + + /// + /// Name: System.PropList.FileOperationPrompt -- PKEY_PropList_FileOperationPrompt + /// Description: The list of properties to show in the file operation confirmation dialog. Properties with empty values + ///will not be displayed. If this list is not specified, then the InfoTip property list is used instead. + ///Register under the regvalue of "FileOperationPrompt". + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {C9944A21-A406-48FE-8225-AEC7E24C211B}, 10 + /// + public static ShellItemPropertyKey FileOperationPrompt => new ShellItemPropertyKey(new Guid("{C9944A21-A406-48FE-8225-AEC7E24C211B}"), 10); + + /// + /// Name: System.PropList.FullDetails -- PKEY_PropList_FullDetails + /// Description: The list of all the properties to show in the details page. Property groups can be included in this list + ///in order to more easily organize the UI. Register under the regvalue of "FullDetails". + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {C9944A21-A406-48FE-8225-AEC7E24C211B}, 2 + /// + public static ShellItemPropertyKey FullDetails => new ShellItemPropertyKey(new Guid("{C9944A21-A406-48FE-8225-AEC7E24C211B}"), 2); + + /// + /// Name: System.PropList.InfoTip -- PKEY_PropList_InfoTip + /// Description: The list of properties to show in the infotip. Properties with empty values will not be displayed. Register + ///under the regvalue of "InfoTip". + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {C9944A21-A406-48FE-8225-AEC7E24C211B}, 4 (PID_PROPLIST_INFOTIP) + /// + public static ShellItemPropertyKey InfoTip => new ShellItemPropertyKey(new Guid("{C9944A21-A406-48FE-8225-AEC7E24C211B}"), 4); + + /// + /// Name: System.PropList.NonPersonal -- PKEY_PropList_NonPersonal + /// Description: The list of properties that are considered 'non-personal'. When told to remove all non-personal properties + ///from a given file, the system will leave these particular properties untouched. Register under the regvalue + ///of "NonPersonal". + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {49D1091F-082E-493F-B23F-D2308AA9668C}, 100 + /// + public static ShellItemPropertyKey NonPersonal => new ShellItemPropertyKey(new Guid("{49D1091F-082E-493F-B23F-D2308AA9668C}"), 100); + + /// + /// Name: System.PropList.PreviewDetails -- PKEY_PropList_PreviewDetails + /// Description: The list of properties to display in the preview pane. Register under the regvalue of "PreviewDetails". + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {C9944A21-A406-48FE-8225-AEC7E24C211B}, 8 + /// + public static ShellItemPropertyKey PreviewDetails => new ShellItemPropertyKey(new Guid("{C9944A21-A406-48FE-8225-AEC7E24C211B}"), 8); + + /// + /// Name: System.PropList.PreviewTitle -- PKEY_PropList_PreviewTitle + /// Description: The one or two properties to display in the preview pane title section. The optional second property is + ///displayed as a subtitle. Register under the regvalue of "PreviewTitle". + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {C9944A21-A406-48FE-8225-AEC7E24C211B}, 6 + /// + public static ShellItemPropertyKey PreviewTitle => new ShellItemPropertyKey(new Guid("{C9944A21-A406-48FE-8225-AEC7E24C211B}"), 6); + + /// + /// Name: System.PropList.QuickTip -- PKEY_PropList_QuickTip + /// Description: The list of properties to show in the infotip when the item is on a slow network. Properties with empty + ///values will not be displayed. Register under the regvalue of "QuickTip". + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {C9944A21-A406-48FE-8225-AEC7E24C211B}, 5 (PID_PROPLIST_QUICKTIP) + /// + public static ShellItemPropertyKey QuickTip => new ShellItemPropertyKey(new Guid("{C9944A21-A406-48FE-8225-AEC7E24C211B}"), 5); + + /// + /// Name: System.PropList.TileInfo -- PKEY_PropList_TileInfo + /// Description: The list of properties to show in the listview on tiles. Register under the regvalue of "TileInfo". + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {C9944A21-A406-48FE-8225-AEC7E24C211B}, 3 (PID_PROPLIST_TILEINFO) + /// + public static ShellItemPropertyKey TileInfo => new ShellItemPropertyKey(new Guid("{C9944A21-A406-48FE-8225-AEC7E24C211B}"), 3); + + /// + /// Name: System.PropList.XPDetailsPanel -- PKEY_PropList_XPDetailsPanel + /// Description: The list of properties to display in the XP webview details panel. Obsolete. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_WebView) {F2275480-F782-4291-BD94-F13693513AEC}, 0 (PID_DISPLAY_PROPERTIES) + /// + public static ShellItemPropertyKey XPDetailsPanel => new ShellItemPropertyKey(new Guid("{F2275480-F782-4291-BD94-F13693513AEC}"), 0); + } + + /// RecordedTV Properties + public static class RecordedTV + { + /// + /// Name: System.RecordedTV.ChannelNumber -- PKEY_RecordedTV_ChannelNumber + /// Description: Example: 42 + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {6D748DE2-8D38-4CC3-AC60-F009B057C557}, 7 + /// + public static ShellItemPropertyKey ChannelNumber => new ShellItemPropertyKey(new Guid("{6D748DE2-8D38-4CC3-AC60-F009B057C557}"), 7); + + /// + /// Name: System.RecordedTV.Credits -- PKEY_RecordedTV_Credits + /// Description: Example: "Don Messick/Frank Welker/Casey Kasem/Heather North/Nicole Jaffe;;;" + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {6D748DE2-8D38-4CC3-AC60-F009B057C557}, 4 + /// + public static ShellItemPropertyKey Credits => new ShellItemPropertyKey(new Guid("{6D748DE2-8D38-4CC3-AC60-F009B057C557}"), 4); + + /// + /// Name: System.RecordedTV.DateContentExpires -- PKEY_RecordedTV_DateContentExpires + /// Description: + /// Type: DateTime -- VT_FILETIME (For variants: VT_DATE) + /// FormatID: {6D748DE2-8D38-4CC3-AC60-F009B057C557}, 15 + /// + public static ShellItemPropertyKey DateContentExpires => new ShellItemPropertyKey(new Guid("{6D748DE2-8D38-4CC3-AC60-F009B057C557}"), 15); + + /// + /// Name: System.RecordedTV.EpisodeName -- PKEY_RecordedTV_EpisodeName + /// Description: Example: "Nowhere to Hyde" + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {6D748DE2-8D38-4CC3-AC60-F009B057C557}, 2 + /// + public static ShellItemPropertyKey EpisodeName => new ShellItemPropertyKey(new Guid("{6D748DE2-8D38-4CC3-AC60-F009B057C557}"), 2); + + /// + /// Name: System.RecordedTV.IsATSCContent -- PKEY_RecordedTV_IsATSCContent + /// Description: + /// Type: Boolean -- VT_BOOL + /// FormatID: {6D748DE2-8D38-4CC3-AC60-F009B057C557}, 16 + /// + public static ShellItemPropertyKey IsATSCContent => new ShellItemPropertyKey(new Guid("{6D748DE2-8D38-4CC3-AC60-F009B057C557}"), 16); + + /// + /// Name: System.RecordedTV.IsClosedCaptioningAvailable -- PKEY_RecordedTV_IsClosedCaptioningAvailable + /// Description: + /// Type: Boolean -- VT_BOOL + /// FormatID: {6D748DE2-8D38-4CC3-AC60-F009B057C557}, 12 + /// + public static ShellItemPropertyKey IsClosedCaptioningAvailable => new ShellItemPropertyKey(new Guid("{6D748DE2-8D38-4CC3-AC60-F009B057C557}"), 12); + + /// + /// Name: System.RecordedTV.IsDTVContent -- PKEY_RecordedTV_IsDTVContent + /// Description: + /// Type: Boolean -- VT_BOOL + /// FormatID: {6D748DE2-8D38-4CC3-AC60-F009B057C557}, 17 + /// + public static ShellItemPropertyKey IsDTVContent => new ShellItemPropertyKey(new Guid("{6D748DE2-8D38-4CC3-AC60-F009B057C557}"), 17); + + /// + /// Name: System.RecordedTV.IsHDContent -- PKEY_RecordedTV_IsHDContent + /// Description: + /// Type: Boolean -- VT_BOOL + /// FormatID: {6D748DE2-8D38-4CC3-AC60-F009B057C557}, 18 + /// + public static ShellItemPropertyKey IsHDContent => new ShellItemPropertyKey(new Guid("{6D748DE2-8D38-4CC3-AC60-F009B057C557}"), 18); + + /// + /// Name: System.RecordedTV.IsRepeatBroadcast -- PKEY_RecordedTV_IsRepeatBroadcast + /// Description: + /// Type: Boolean -- VT_BOOL + /// FormatID: {6D748DE2-8D38-4CC3-AC60-F009B057C557}, 13 + /// + public static ShellItemPropertyKey IsRepeatBroadcast => new ShellItemPropertyKey(new Guid("{6D748DE2-8D38-4CC3-AC60-F009B057C557}"), 13); + + /// + /// Name: System.RecordedTV.IsSAP -- PKEY_RecordedTV_IsSAP + /// Description: + /// Type: Boolean -- VT_BOOL + /// FormatID: {6D748DE2-8D38-4CC3-AC60-F009B057C557}, 14 + /// + public static ShellItemPropertyKey IsSAP => new ShellItemPropertyKey(new Guid("{6D748DE2-8D38-4CC3-AC60-F009B057C557}"), 14); + + /// + /// Name: System.RecordedTV.NetworkAffiliation -- PKEY_RecordedTV_NetworkAffiliation + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {2C53C813-FB63-4E22-A1AB-0B331CA1E273}, 100 + /// + public static ShellItemPropertyKey NetworkAffiliation => new ShellItemPropertyKey(new Guid("{2C53C813-FB63-4E22-A1AB-0B331CA1E273}"), 100); + + /// + /// Name: System.RecordedTV.OriginalBroadcastDate -- PKEY_RecordedTV_OriginalBroadcastDate + /// Description: + /// Type: DateTime -- VT_FILETIME (For variants: VT_DATE) + /// FormatID: {4684FE97-8765-4842-9C13-F006447B178C}, 100 + /// + public static ShellItemPropertyKey OriginalBroadcastDate => new ShellItemPropertyKey(new Guid("{4684FE97-8765-4842-9C13-F006447B178C}"), 100); + + /// + /// Name: System.RecordedTV.ProgramDescription -- PKEY_RecordedTV_ProgramDescription + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {6D748DE2-8D38-4CC3-AC60-F009B057C557}, 3 + /// + public static ShellItemPropertyKey ProgramDescription => new ShellItemPropertyKey(new Guid("{6D748DE2-8D38-4CC3-AC60-F009B057C557}"), 3); + + /// + /// Name: System.RecordedTV.RecordingTime -- PKEY_RecordedTV_RecordingTime + /// Description: + /// Type: DateTime -- VT_FILETIME (For variants: VT_DATE) + /// FormatID: {A5477F61-7A82-4ECA-9DDE-98B69B2479B3}, 100 + /// + public static ShellItemPropertyKey RecordingTime => new ShellItemPropertyKey(new Guid("{A5477F61-7A82-4ECA-9DDE-98B69B2479B3}"), 100); + + /// + /// Name: System.RecordedTV.StationCallSign -- PKEY_RecordedTV_StationCallSign + /// Description: Example: "TOONP" + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {6D748DE2-8D38-4CC3-AC60-F009B057C557}, 5 + /// + public static ShellItemPropertyKey StationCallSign => new ShellItemPropertyKey(new Guid("{6D748DE2-8D38-4CC3-AC60-F009B057C557}"), 5); + + /// + /// Name: System.RecordedTV.StationName -- PKEY_RecordedTV_StationName + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {1B5439E7-EBA1-4AF8-BDD7-7AF1D4549493}, 100 + /// + public static ShellItemPropertyKey StationName => new ShellItemPropertyKey(new Guid("{1B5439E7-EBA1-4AF8-BDD7-7AF1D4549493}"), 100); + } + + /// Search Properties + public static class Search + { + /// + /// Name: System.Search.AutoSummary -- PKEY_Search_AutoSummary + /// Description: General Summary of the document. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {560C36C0-503A-11CF-BAA1-00004C752A9A}, 2 + /// + public static ShellItemPropertyKey AutoSummary => new ShellItemPropertyKey(new Guid("{560C36C0-503A-11CF-BAA1-00004C752A9A}"), 2); + + /// + /// Name: System.Search.ContainerHash -- PKEY_Search_ContainerHash + /// Description: Hash code used to identify attachments to be deleted based on a common container url + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {BCEEE283-35DF-4D53-826A-F36A3EEFC6BE}, 100 + /// + public static ShellItemPropertyKey ContainerHash => new ShellItemPropertyKey(new Guid("{BCEEE283-35DF-4D53-826A-F36A3EEFC6BE}"), 100); + + /// + /// Name: System.Search.Contents -- PKEY_Search_Contents + /// Description: The contents of the item. This property is for query restrictions only; it cannot be retrieved in a + ///query result. The Indexing Service friendly name is 'contents'. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_Storage) {B725F130-47EF-101A-A5F1-02608C9EEBAC}, 19 (PID_STG_CONTENTS) + /// + public static ShellItemPropertyKey Contents => new ShellItemPropertyKey(new Guid("{B725F130-47EF-101A-A5F1-02608C9EEBAC}"), 19); + + /// + /// Name: System.Search.EntryID -- PKEY_Search_EntryID + /// Description: The entry ID for an item within a given catalog in the Windows Search Index. + ///This value may be recycled, and therefore is not considered unique over time. + /// + /// Type: Int32 -- VT_I4 + /// FormatID: (FMTID_Query) {49691C90-7E17-101A-A91C-08002B2ECDA9}, 5 (PROPID_QUERY_WORKID) + /// + public static ShellItemPropertyKey EntryID => new ShellItemPropertyKey(new Guid("{49691C90-7E17-101A-A91C-08002B2ECDA9}"), 5); + + /// + /// Name: System.Search.ExtendedProperties -- PKEY_Search_ExtendedProperties + /// Description: + /// Type: Blob -- VT_BLOB + /// FormatID: {7B03B546-FA4F-4A52-A2FE-03D5311E5865}, 100 + /// + public static ShellItemPropertyKey ExtendedProperties => new ShellItemPropertyKey(new Guid("{7B03B546-FA4F-4A52-A2FE-03D5311E5865}"), 100); + + /// + /// Name: System.Search.GatherTime -- PKEY_Search_GatherTime + /// Description: The Datetime that the Windows Search Gatherer process last pushed properties of this document to the Windows Search Gatherer Plugins. + /// + /// Type: DateTime -- VT_FILETIME (For variants: VT_DATE) + /// FormatID: {0B63E350-9CCC-11D0-BCDB-00805FCCCE04}, 8 + /// + public static ShellItemPropertyKey GatherTime => new ShellItemPropertyKey(new Guid("{0B63E350-9CCC-11D0-BCDB-00805FCCCE04}"), 8); + + /// + /// Name: System.Search.HitCount -- PKEY_Search_HitCount + /// Description: When using CONTAINS over the Windows Search Index, this is the number of matches of the term. + ///If there are multiple CONTAINS, an AND computes the min number of hits and an OR the max number of hits. + /// + /// Type: Int32 -- VT_I4 + /// FormatID: (FMTID_Query) {49691C90-7E17-101A-A91C-08002B2ECDA9}, 4 (PROPID_QUERY_HITCOUNT) + /// + public static ShellItemPropertyKey HitCount => new ShellItemPropertyKey(new Guid("{49691C90-7E17-101A-A91C-08002B2ECDA9}"), 4); + + /// + /// Name: System.Search.IsClosedDirectory -- PKEY_Search_IsClosedDirectory + /// Description: If this property is emitted with a value of TRUE, then it indicates that this URL's last modified time applies to all of it's children, and if this URL is deleted then all of it's children are deleted as well. For example, this would be emitted as TRUE when emitting the URL of an email so that all attachments are tied to the last modified time of that email. + /// + /// Type: Boolean -- VT_BOOL + /// FormatID: {0B63E343-9CCC-11D0-BCDB-00805FCCCE04}, 23 + /// + public static ShellItemPropertyKey IsClosedDirectory => new ShellItemPropertyKey(new Guid("{0B63E343-9CCC-11D0-BCDB-00805FCCCE04}"), 23); + + /// + /// Name: System.Search.IsFullyContained -- PKEY_Search_IsFullyContained + /// Description: Any child URL of a URL which has System.Search.IsClosedDirectory=TRUE must emit System.Search.IsFullyContained=TRUE. This ensures that the URL is not deleted at the end of a crawl because it hasn't been visited (which is the normal mechanism for detecting deletes). For example an email attachment would emit this property + /// + /// Type: Boolean -- VT_BOOL + /// FormatID: {0B63E343-9CCC-11D0-BCDB-00805FCCCE04}, 24 + /// + public static ShellItemPropertyKey IsFullyContained => new ShellItemPropertyKey(new Guid("{0B63E343-9CCC-11D0-BCDB-00805FCCCE04}"), 24); + + /// + /// Name: System.Search.QueryFocusedSummary -- PKEY_Search_QueryFocusedSummary + /// Description: Query Focused Summary of the document. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {560C36C0-503A-11CF-BAA1-00004C752A9A}, 3 + /// + public static ShellItemPropertyKey QueryFocusedSummary => new ShellItemPropertyKey(new Guid("{560C36C0-503A-11CF-BAA1-00004C752A9A}"), 3); + + /// + /// Name: System.Search.QueryFocusedSummaryWithFallback -- PKEY_Search_QueryFocusedSummaryWithFallback + /// Description: Query Focused Summary of the document, if none is available it returns the AutoSummary. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {560C36C0-503A-11CF-BAA1-00004C752A9A}, 4 + /// + public static ShellItemPropertyKey QueryFocusedSummaryWithFallback => new ShellItemPropertyKey(new Guid("{560C36C0-503A-11CF-BAA1-00004C752A9A}"), 4); + + /// + /// Name: System.Search.Rank -- PKEY_Search_Rank + /// Description: Relevance rank of row. Ranges from 0-1000. Larger numbers = better matches. Query-time only. + /// + /// Type: Int32 -- VT_I4 + /// FormatID: (FMTID_Query) {49691C90-7E17-101A-A91C-08002B2ECDA9}, 3 (PROPID_QUERY_RANK) + /// + public static ShellItemPropertyKey Rank => new ShellItemPropertyKey(new Guid("{49691C90-7E17-101A-A91C-08002B2ECDA9}"), 3); + + /// + /// Name: System.Search.Store -- PKEY_Search_Store + /// Description: The identifier for the protocol handler that produced this item. (E.g. MAPI, CSC, FILE etc.) + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {A06992B3-8CAF-4ED7-A547-B259E32AC9FC}, 100 + /// + public static ShellItemPropertyKey Store => new ShellItemPropertyKey(new Guid("{A06992B3-8CAF-4ED7-A547-B259E32AC9FC}"), 100); + + /// + /// Name: System.Search.UrlToIndex -- PKEY_Search_UrlToIndex + /// Description: This property should be emitted by a container IFilter for each child URL within the container. The children will eventually be crawled by the indexer if they are within scope. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {0B63E343-9CCC-11D0-BCDB-00805FCCCE04}, 2 + /// + public static ShellItemPropertyKey UrlToIndex => new ShellItemPropertyKey(new Guid("{0B63E343-9CCC-11D0-BCDB-00805FCCCE04}"), 2); + + /// + /// Name: System.Search.UrlToIndexWithModificationTime -- PKEY_Search_UrlToIndexWithModificationTime + /// Description: This property is the same as System.Search.UrlToIndex except that it includes the time the URL was last modified. This is an optimization for the indexer as it doesn't have to call back into the protocol handler to ask for this information to determine if the content needs to be indexed again. The property is a vector with two elements, a VT_LPWSTR with the URL and a VT_FILETIME for the last modified time. + /// + /// Type: Multivalue Any -- VT_VECTOR | VT_NULL (For variants: VT_ARRAY | VT_NULL) + /// FormatID: {0B63E343-9CCC-11D0-BCDB-00805FCCCE04}, 12 + /// + public static ShellItemPropertyKey UrlToIndexWithModificationTime => new ShellItemPropertyKey(new Guid("{0B63E343-9CCC-11D0-BCDB-00805FCCCE04}"), 12); + } + + /// Shell Properties + public static class Shell + { + /// + /// Name: System.Shell.OmitFromView -- PKEY_Shell_OmitFromView + /// Description: Set this to a string value of 'True' to omit this item from shell views + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {DE35258C-C695-4CBC-B982-38B0AD24CED0}, 2 + /// + public static ShellItemPropertyKey OmitFromView => new ShellItemPropertyKey(new Guid("{DE35258C-C695-4CBC-B982-38B0AD24CED0}"), 2); + + /// + /// Name: System.Shell.SFGAOFlagsStrings -- PKEY_Shell_SFGAOFlagsStrings + /// Description: Expresses the SFGAO flags as string values and is used as a query optimization. + /// + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: {D6942081-D53B-443D-AD47-5E059D9CD27A}, 2 + /// + public static ShellItemPropertyKey SFGAOFlagsStrings => new ShellItemPropertyKey(new Guid("{D6942081-D53B-443D-AD47-5E059D9CD27A}"), 2); + } + + /// Software Properties + public static class Software + { + /// + /// Name: System.Software.DateLastUsed -- PKEY_Software_DateLastUsed + /// Description: + /// + /// Type: DateTime -- VT_FILETIME (For variants: VT_DATE) + /// FormatID: {841E4F90-FF59-4D16-8947-E81BBFFAB36D}, 16 + /// + public static ShellItemPropertyKey DateLastUsed => new ShellItemPropertyKey(new Guid("{841E4F90-FF59-4D16-8947-E81BBFFAB36D}"), 16); + + /// + /// Name: System.Software.ProductName -- PKEY_Software_ProductName + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (PSFMTID_VERSION) {0CEF7D53-FA64-11D1-A203-0000F81FEDEE}, 7 + /// + public static ShellItemPropertyKey ProductName => new ShellItemPropertyKey(new Guid("{0CEF7D53-FA64-11D1-A203-0000F81FEDEE}"), 7); + } + + /// Sync Properties + public static class Sync + { + /// + /// Name: System.Sync.Comments -- PKEY_Sync_Comments + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {7BD5533E-AF15-44DB-B8C8-BD6624E1D032}, 13 + /// + public static ShellItemPropertyKey Comments => new ShellItemPropertyKey(new Guid("{7BD5533E-AF15-44DB-B8C8-BD6624E1D032}"), 13); + + /// + /// Name: System.Sync.ConflictDescription -- PKEY_Sync_ConflictDescription + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {CE50C159-2FB8-41FD-BE68-D3E042E274BC}, 4 + /// + public static ShellItemPropertyKey ConflictDescription => new ShellItemPropertyKey(new Guid("{CE50C159-2FB8-41FD-BE68-D3E042E274BC}"), 4); + + /// + /// Name: System.Sync.ConflictFirstLocation -- PKEY_Sync_ConflictFirstLocation + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {CE50C159-2FB8-41FD-BE68-D3E042E274BC}, 6 + /// + public static ShellItemPropertyKey ConflictFirstLocation => new ShellItemPropertyKey(new Guid("{CE50C159-2FB8-41FD-BE68-D3E042E274BC}"), 6); + + /// + /// Name: System.Sync.ConflictSecondLocation -- PKEY_Sync_ConflictSecondLocation + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {CE50C159-2FB8-41FD-BE68-D3E042E274BC}, 7 + /// + public static ShellItemPropertyKey ConflictSecondLocation => new ShellItemPropertyKey(new Guid("{CE50C159-2FB8-41FD-BE68-D3E042E274BC}"), 7); + + /// + /// Name: System.Sync.HandlerCollectionID -- PKEY_Sync_HandlerCollectionID + /// Description: + /// Type: Guid -- VT_CLSID + /// FormatID: {7BD5533E-AF15-44DB-B8C8-BD6624E1D032}, 2 + /// + public static ShellItemPropertyKey HandlerCollectionID => new ShellItemPropertyKey(new Guid("{7BD5533E-AF15-44DB-B8C8-BD6624E1D032}"), 2); + + /// + /// Name: System.Sync.HandlerID -- PKEY_Sync_HandlerID + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {7BD5533E-AF15-44DB-B8C8-BD6624E1D032}, 3 + /// + public static ShellItemPropertyKey HandlerID => new ShellItemPropertyKey(new Guid("{7BD5533E-AF15-44DB-B8C8-BD6624E1D032}"), 3); + + /// + /// Name: System.Sync.HandlerName -- PKEY_Sync_HandlerName + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {CE50C159-2FB8-41FD-BE68-D3E042E274BC}, 2 + /// + public static ShellItemPropertyKey HandlerName => new ShellItemPropertyKey(new Guid("{CE50C159-2FB8-41FD-BE68-D3E042E274BC}"), 2); + + /// + /// Name: System.Sync.HandlerType -- PKEY_Sync_HandlerType + /// Description: + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {7BD5533E-AF15-44DB-B8C8-BD6624E1D032}, 8 + /// + public static ShellItemPropertyKey HandlerType => new ShellItemPropertyKey(new Guid("{7BD5533E-AF15-44DB-B8C8-BD6624E1D032}"), 8); + + /// + /// Name: System.Sync.HandlerTypeLabel -- PKEY_Sync_HandlerTypeLabel + /// Description: + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {7BD5533E-AF15-44DB-B8C8-BD6624E1D032}, 9 + /// + public static ShellItemPropertyKey HandlerTypeLabel => new ShellItemPropertyKey(new Guid("{7BD5533E-AF15-44DB-B8C8-BD6624E1D032}"), 9); + + /// + /// Name: System.Sync.ItemID -- PKEY_Sync_ItemID + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {7BD5533E-AF15-44DB-B8C8-BD6624E1D032}, 6 + /// + public static ShellItemPropertyKey ItemID => new ShellItemPropertyKey(new Guid("{7BD5533E-AF15-44DB-B8C8-BD6624E1D032}"), 6); + + /// + /// Name: System.Sync.ItemName -- PKEY_Sync_ItemName + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {CE50C159-2FB8-41FD-BE68-D3E042E274BC}, 3 + /// + public static ShellItemPropertyKey ItemName => new ShellItemPropertyKey(new Guid("{CE50C159-2FB8-41FD-BE68-D3E042E274BC}"), 3); + + /// + /// Name: System.Sync.ProgressPercentage -- PKEY_Sync_ProgressPercentage + /// Description: An integer value between 0 and 100 representing the percentage completed. + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {7BD5533E-AF15-44DB-B8C8-BD6624E1D032}, 23 + /// + public static ShellItemPropertyKey ProgressPercentage => new ShellItemPropertyKey(new Guid("{7BD5533E-AF15-44DB-B8C8-BD6624E1D032}"), 23); + + /// + /// Name: System.Sync.State -- PKEY_Sync_State + /// Description: Sync state. + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: {7BD5533E-AF15-44DB-B8C8-BD6624E1D032}, 24 + /// + public static ShellItemPropertyKey State => new ShellItemPropertyKey(new Guid("{7BD5533E-AF15-44DB-B8C8-BD6624E1D032}"), 24); + + /// + /// Name: System.Sync.Status -- PKEY_Sync_Status + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {7BD5533E-AF15-44DB-B8C8-BD6624E1D032}, 10 + /// + public static ShellItemPropertyKey Status => new ShellItemPropertyKey(new Guid("{7BD5533E-AF15-44DB-B8C8-BD6624E1D032}"), 10); + } + + /// Task Properties + public static class Task + { + /// + /// Name: System.Task.BillingInformation -- PKEY_Task_BillingInformation + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {D37D52C6-261C-4303-82B3-08B926AC6F12}, 100 + /// + public static ShellItemPropertyKey BillingInformation => new ShellItemPropertyKey(new Guid("{D37D52C6-261C-4303-82B3-08B926AC6F12}"), 100); + + /// + /// Name: System.Task.CompletionStatus -- PKEY_Task_CompletionStatus + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {084D8A0A-E6D5-40DE-BF1F-C8820E7C877C}, 100 + /// + public static ShellItemPropertyKey CompletionStatus => new ShellItemPropertyKey(new Guid("{084D8A0A-E6D5-40DE-BF1F-C8820E7C877C}"), 100); + + /// + /// Name: System.Task.Owner -- PKEY_Task_Owner + /// Description: + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: {08C7CC5F-60F2-4494-AD75-55E3E0B5ADD0}, 100 + /// + public static ShellItemPropertyKey Owner => new ShellItemPropertyKey(new Guid("{08C7CC5F-60F2-4494-AD75-55E3E0B5ADD0}"), 100); + } + + /// Video Properties + public static class Video + { + /// + /// Name: System.Video.Compression -- PKEY_Video_Compression + /// Description: Indicates the level of compression for the video stream. "Compression". + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_VideoSummaryInformation) {64440491-4C8B-11D1-8B70-080036B11A03}, 10 (PIDVSI_COMPRESSION) + /// + public static ShellItemPropertyKey Compression => new ShellItemPropertyKey(new Guid("{64440491-4C8B-11D1-8B70-080036B11A03}"), 10); + + /// + /// Name: System.Video.Director -- PKEY_Video_Director + /// Description: + /// + /// Type: Multivalue String -- VT_VECTOR | VT_LPWSTR (For variants: VT_ARRAY | VT_BSTR) + /// FormatID: (PSGUID_MEDIAFILESUMMARYINFORMATION) {64440492-4C8B-11D1-8B70-080036B11A03}, 20 (PIDMSI_DIRECTOR) + /// + public static ShellItemPropertyKey Director => new ShellItemPropertyKey(new Guid("{64440492-4C8B-11D1-8B70-080036B11A03}"), 20); + + /// + /// Name: System.Video.EncodingBitrate -- PKEY_Video_EncodingBitrate + /// Description: Indicates the data rate in "bits per second" for the video stream. "DataRate". + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: (FMTID_VideoSummaryInformation) {64440491-4C8B-11D1-8B70-080036B11A03}, 8 (PIDVSI_DATA_RATE) + /// + public static ShellItemPropertyKey EncodingBitrate => new ShellItemPropertyKey(new Guid("{64440491-4C8B-11D1-8B70-080036B11A03}"), 8); + + /// + /// Name: System.Video.FourCC -- PKEY_Video_FourCC + /// Description: Indicates the 4CC for the video stream. + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: (FMTID_VideoSummaryInformation) {64440491-4C8B-11D1-8B70-080036B11A03}, 44 + /// + public static ShellItemPropertyKey FourCC => new ShellItemPropertyKey(new Guid("{64440491-4C8B-11D1-8B70-080036B11A03}"), 44); + + /// + /// Name: System.Video.FrameHeight -- PKEY_Video_FrameHeight + /// Description: Indicates the frame height for the video stream. + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: (FMTID_VideoSummaryInformation) {64440491-4C8B-11D1-8B70-080036B11A03}, 4 + /// + public static ShellItemPropertyKey FrameHeight => new ShellItemPropertyKey(new Guid("{64440491-4C8B-11D1-8B70-080036B11A03}"), 4); + + /// + /// Name: System.Video.FrameRate -- PKEY_Video_FrameRate + /// Description: Indicates the frame rate in "frames per millisecond" for the video stream. "FrameRate". + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: (FMTID_VideoSummaryInformation) {64440491-4C8B-11D1-8B70-080036B11A03}, 6 (PIDVSI_FRAME_RATE) + /// + public static ShellItemPropertyKey FrameRate => new ShellItemPropertyKey(new Guid("{64440491-4C8B-11D1-8B70-080036B11A03}"), 6); + + /// + /// Name: System.Video.FrameWidth -- PKEY_Video_FrameWidth + /// Description: Indicates the frame width for the video stream. + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: (FMTID_VideoSummaryInformation) {64440491-4C8B-11D1-8B70-080036B11A03}, 3 + /// + public static ShellItemPropertyKey FrameWidth => new ShellItemPropertyKey(new Guid("{64440491-4C8B-11D1-8B70-080036B11A03}"), 3); + + /// + /// Name: System.Video.HorizontalAspectRatio -- PKEY_Video_HorizontalAspectRatio + /// Description: Indicates the horizontal portion of the aspect ratio. The X portion of XX:YY, + ///like 16:9. + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: (FMTID_VideoSummaryInformation) {64440491-4C8B-11D1-8B70-080036B11A03}, 42 + /// + public static ShellItemPropertyKey HorizontalAspectRatio => new ShellItemPropertyKey(new Guid("{64440491-4C8B-11D1-8B70-080036B11A03}"), 42); + + /// + /// Name: System.Video.SampleSize -- PKEY_Video_SampleSize + /// Description: Indicates the sample size in bits for the video stream. "SampleSize". + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: (FMTID_VideoSummaryInformation) {64440491-4C8B-11D1-8B70-080036B11A03}, 9 (PIDVSI_SAMPLE_SIZE) + /// + public static ShellItemPropertyKey SampleSize => new ShellItemPropertyKey(new Guid("{64440491-4C8B-11D1-8B70-080036B11A03}"), 9); + + /// + /// Name: System.Video.StreamName -- PKEY_Video_StreamName + /// Description: Indicates the name for the video stream. "StreamName". + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_VideoSummaryInformation) {64440491-4C8B-11D1-8B70-080036B11A03}, 2 (PIDVSI_STREAM_NAME) + /// + public static ShellItemPropertyKey StreamName => new ShellItemPropertyKey(new Guid("{64440491-4C8B-11D1-8B70-080036B11A03}"), 2); + + /// + /// Name: System.Video.StreamNumber -- PKEY_Video_StreamNumber + /// Description: "Stream Number". + /// + /// Type: UInt16 -- VT_UI2 + /// FormatID: (FMTID_VideoSummaryInformation) {64440491-4C8B-11D1-8B70-080036B11A03}, 11 (PIDVSI_STREAM_NUMBER) + /// + public static ShellItemPropertyKey StreamNumber => new ShellItemPropertyKey(new Guid("{64440491-4C8B-11D1-8B70-080036B11A03}"), 11); + + /// + /// Name: System.Video.TotalBitrate -- PKEY_Video_TotalBitrate + /// Description: Indicates the total data rate in "bits per second" for all video and audio streams. + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: (FMTID_VideoSummaryInformation) {64440491-4C8B-11D1-8B70-080036B11A03}, 43 (PIDVSI_TOTAL_BITRATE) + /// + public static ShellItemPropertyKey TotalBitrate => new ShellItemPropertyKey(new Guid("{64440491-4C8B-11D1-8B70-080036B11A03}"), 43); + + /// + /// Name: System.Video.TranscodedForSync -- PKEY_Video_TranscodedForSync + /// Description: + /// Type: Boolean -- VT_BOOL + /// FormatID: (FMTID_VideoSummaryInformation) {64440491-4C8B-11D1-8B70-080036B11A03}, 46 + /// + public static ShellItemPropertyKey TranscodedForSync => new ShellItemPropertyKey(new Guid("{64440491-4C8B-11D1-8B70-080036B11A03}"), 46); + + /// + /// Name: System.Video.VerticalAspectRatio -- PKEY_Video_VerticalAspectRatio + /// Description: Indicates the vertical portion of the aspect ratio. The Y portion of + ///XX:YY, like 16:9. + /// + /// Type: UInt32 -- VT_UI4 + /// FormatID: (FMTID_VideoSummaryInformation) {64440491-4C8B-11D1-8B70-080036B11A03}, 45 + /// + public static ShellItemPropertyKey VerticalAspectRatio => new ShellItemPropertyKey(new Guid("{64440491-4C8B-11D1-8B70-080036B11A03}"), 45); + } + + /// Volume Properties + public static class Volume + { + /// + /// Name: System.Volume.FileSystem -- PKEY_Volume_FileSystem + /// Description: Indicates the filesystem of the volume. + /// + /// Type: String -- VT_LPWSTR (For variants: VT_BSTR) + /// FormatID: (FMTID_Volume) {9B174B35-40FF-11D2-A27E-00C04FC30871}, 4 (PID_VOLUME_FILESYSTEM) (Filesystem Volume Properties) + /// + public static ShellItemPropertyKey FileSystem => new ShellItemPropertyKey(new Guid("{9B174B35-40FF-11D2-A27E-00C04FC30871}"), 4); + + /// + /// Name: System.Volume.IsMappedDrive -- PKEY_Volume_IsMappedDrive + /// Description: + /// Type: Boolean -- VT_BOOL + /// FormatID: {149C0B69-2C2D-48FC-808F-D318D78C4636}, 2 + /// + public static ShellItemPropertyKey IsMappedDrive => new ShellItemPropertyKey(new Guid("{149C0B69-2C2D-48FC-808F-D318D78C4636}"), 2); + + /// + /// Name: System.Volume.IsRoot -- PKEY_Volume_IsRoot + /// Description: + /// + /// Type: Boolean -- VT_BOOL + /// FormatID: (FMTID_Volume) {9B174B35-40FF-11D2-A27E-00C04FC30871}, 10 (Filesystem Volume Properties) + /// + public static ShellItemPropertyKey IsRoot => new ShellItemPropertyKey(new Guid("{9B174B35-40FF-11D2-A27E-00C04FC30871}"), 10); + } + } + + internal static partial class NativeMethods + { + internal enum ADLT + { + RECENT, + FREQUENT + } + + internal enum Facility + { + Control = 10, + Dispatch = 2, + Ese = 0xe5e, + Itf = 4, + Null = 0, + Rpc = 1, + Storage = 3, + Win32 = 7, + Windows = 8 + } + + internal enum FDAP : uint + { + BOTTOM = 0, + TOP = 1 + } + + internal enum FDEOR + { + DEFAULT, + ACCEPT, + REFUSE + } + + internal enum FDESVR + { + DEFAULT, + ACCEPT, + REFUSE + } + + [Flags] + internal enum FOS : uint + { + ALLNONSTORAGEITEMS = 0x80, + ALLOWMULTISELECT = 0x200, + CREATEPROMPT = 0x2000, + DEFAULTNOMINIMODE = 0x20000000, + DONTADDTORECENT = 0x2000000, + FILEMUSTEXIST = 0x1000, + FORCEFILESYSTEM = 0x40, + FORCEPREVIEWPANEON = 0x40000000, + FORCESHOWHIDDEN = 0x10000000, + HIDEMRUPLACES = 0x20000, + HIDEPINNEDPLACES = 0x40000, + NOCHANGEDIR = 8, + NODEREFERENCELINKS = 0x100000, + NOREADONLYRETURN = 0x8000, + NOTESTFILECREATE = 0x10000, + NOVALIDATE = 0x100, + OVERWRITEPROMPT = 2, + PATHMUSTEXIST = 0x800, + PICKFOLDERS = 0x20, + SHAREAWARE = 0x4000, + STRICTFILETYPES = 4 + } + + [Flags] + internal enum GPS + { + BESTEFFORT = 0x40, + DEFAULT = 0, + DELAYCREATION = 0x20, + FASTPROPERTIESONLY = 8, + HANDLERPROPERTIESONLY = 1, + MASK_VALID = 0xff, + NO_OPLOCK = 0x80, + OPENSLOWITEM = 0x10, + READWRITE = 2, + TEMPORARY = 4 + } + + internal enum KDC + { + FREQUENT = 1, + RECENT = 2 + } + + [Flags] + internal enum SFGAO : uint + { + BROWSABLE = 0x8000000, + CANCOPY = 1, + CANDELETE = 0x20, + CANLINK = 4, + CANMONIKER = 0x400000, + CANMOVE = 2, + CANRENAME = 0x10, + CAPABILITYMASK = 0x177, + COMPRESSED = 0x4000000, + CONTENTSMASK = 0x80000000, + DISPLAYATTRMASK = 0xfc000, + DROPTARGET = 0x100, + ENCRYPTED = 0x2000, + FILESYSANCESTOR = 0x10000000, + FILESYSTEM = 0x40000000, + FOLDER = 0x20000000, + GHOSTED = 0x8000, + HASPROPSHEET = 0x40, + HASSTORAGE = 0x400000, + HASSUBFOLDER = 0x80000000, + HIDDEN = 0x80000, + ISSLOW = 0x4000, + LINK = 0x10000, + NEWCONTENT = 0x200000, + NONENUMERATED = 0x100000, + PKEYSFGAOMASK = 0x81044000, + READONLY = 0x40000, + REMOVABLE = 0x2000000, + SHARE = 0x20000, + STORAGE = 8, + STORAGEANCESTOR = 0x800000, + STORAGECAPMASK = 0x70c50008, + STREAM = 0x400000, + SYSTEM = 0x00001000, + VALIDATE = 0x1000000 + } + + [Flags] + internal enum SHCONTF + { + CHECKING_FOR_CHILDREN = 0x10, + ENABLE_ASYNC = 0x8000, + FASTITEMS = 0x2000, + FLATLIST = 0x4000, + FOLDERS = 0x20, + INCLUDEHIDDEN = 0x80, + INIT_ON_FIRST_NEXT = 0x100, + NAVIGATION_ENUM = 0x1000, + NETPRINTERSRCH = 0x200, + NONFOLDERS = 0x40, + SHAREABLE = 0x400, + STORAGE = 0x800 + } + + [Flags] + internal enum SHGDN + { + SHGDN_FORADDRESSBAR = 0x4000, + SHGDN_FOREDITING = 0x1000, + SHGDN_FORPARSING = 0x8000, + SHGDN_INFOLDER = 1, + SHGDN_NORMAL = 0 + } + + internal enum SIATTRIBFLAGS + { + AND = 1, + APPCOMPAT = 3, + OR = 2 + } + + [Flags] + internal enum SICHINT : uint + { + ALLFIELDS = 0x80000000, + CANONICAL = 0x10000000, + DISPLAY = 0, + TEST_FILESYSPATH_IF_NOT_EQUAL = 0x20000000 + } + + internal enum SIGDN : uint + { + DESKTOPABSOLUTEEDITING = 0x8004c000, + DESKTOPABSOLUTEPARSING = 0x80028000, + FILESYSPATH = 0x80058000, + NORMALDISPLAY = 0, + PARENTRELATIVE = 0x80080001, + PARENTRELATIVEEDITING = 0x80031001, + PARENTRELATIVEFORADDRESSBAR = 0x8007c001, + PARENTRELATIVEFORUI = 0x80094001, + PARENTRELATIVEPARSING = 0x80018001, + URL = 0x80068000, + } + + [Flags] + internal enum SLGP + { + RAWPATH = 4, + SHORTPATH = 1, + UNCPRIORITY = 2, + RELATIVEPRIORITY = 8 + } + + [Flags] + internal enum STPF + { + NONE = 0, + USEAPPPEEKALWAYS = 4, + USEAPPPEEKWHENACTIVE = 8, + USEAPPTHUMBNAILALWAYS = 1, + USEAPPTHUMBNAILWHENACTIVE = 2 + } + + [Flags] + internal enum TBPF + { + ERROR = 4, + INDETERMINATE = 1, + NOPROGRESS = 0, + NORMAL = 2, + PAUSED = 8 + } + + [Flags] + internal enum THB : uint + { + BITMAP = 1, + FLAGS = 8, + ICON = 2, + TOOLTIP = 4 + } + + [Flags] + internal enum THBF : uint + { + DISABLED = 1, + DISMISSONCLICK = 2, + ENABLED = 0, + HIDDEN = 8, + NOBACKGROUND = 4, + NONINTERACTIVE = 0x10 + } + + // Used to remove items from the automatic destination lists created when apps or the system call + // SHAddToRecentDocs to report usage of a document. + /// Critical: Suppresses unmanaged code security. + [SuppressUnmanagedCodeSecurity] + [ComImport, Guid("12337d35-94c6-48a0-bce7-6a9c69d4d600"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IApplicationDestinations + { + // Set the App User Model ID for the application removing destinations from its list. If an AppID is not + // provided via this method, the system will use a heuristically determined ID. This method must be called + // before RemoveDestination or RemoveAllDestinations. + void SetAppID([MarshalAs(UnmanagedType.LPWStr)] string pszAppID); + + // Remove an IShellItem or an IShellLink from the automatic destination list + void RemoveDestination([MarshalAs(UnmanagedType.IUnknown)] object punk); + + // Clear the frequent and recent destination lists for this application. + void RemoveAllDestinations(); + } + + /// + /// Allows an application to retrieve the most recent and frequent documents opened in that app, as reported via SHAddToRecentDocs + /// + /// Critical: Suppresses unmanaged code security. + [SuppressUnmanagedCodeSecurity] + [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("3c594f9f-9f30-47a1-979a-c9e83d3d0a06")] + internal interface IApplicationDocumentLists + { + /// + /// Set the App User Model ID for the application retrieving this list. If an AppID is not provided via this + /// method, the system will use a heuristically determined ID. This method must be called before GetList. + /// + /// App Id. + void SetAppID([MarshalAs(UnmanagedType.LPWStr)] string pszAppID); + + /// + /// Retrieve an IEnumObjects or IObjectArray for IShellItems and/or IShellLinks. Items may appear in both the + /// frequent and recent lists. + /// + /// Which of the known list types to retrieve + /// The number of items desired. + /// The interface Id that the return value should be queried for. + /// A COM object based on the IID passed for the riid parameter. + [return: MarshalAs(UnmanagedType.IUnknown)] + object GetList(ADLT listtype, uint cItemsDesired, [In] ref Guid riid); + } + + // Custom Destination List + /// Critical: Suppresses unmanaged code security. + [SuppressUnmanagedCodeSecurity] + [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("6332debf-87b5-4670-90c0-5e57b408a49e")] + internal interface ICustomDestinationList + { + void SetAppID([MarshalAs(UnmanagedType.LPWStr)] string pszAppID); + + // Retrieve IObjectArray of IShellItems or IShellLinks that represent removed destinations + [return: MarshalAs(UnmanagedType.Interface)] + object BeginList(out uint pcMaxSlots, [In] ref Guid riid); + + // PreserveSig because this will return custom errors when attempting to add unregistered ShellItems. Can't + // readily detect that case without just trying to append it. + [PreserveSig] + HRESULT AppendCategory([MarshalAs(UnmanagedType.LPWStr)] string pszCategory, IObjectArray poa); + + void AppendKnownCategory(KDC category); + + [PreserveSig] + HRESULT AddUserTasks(IObjectArray poa); + + void CommitList(); + + // Retrieve IObjectCollection of IShellItems + [return: MarshalAs(UnmanagedType.Interface)] + object GetRemovedDestinations([In] ref Guid riid); + + void DeleteList([MarshalAs(UnmanagedType.LPWStr)] string pszAppID); + + void AbortList(); + } + + /// Critical: Suppresses unmanaged code security. + [SuppressUnmanagedCodeSecurity] + [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214F2-0000-0000-C000-000000000046")] + internal interface IEnumIDList + { + [PreserveSig()] + HRESULT Next(uint celt, out IntPtr rgelt, out int pceltFetched); + + [PreserveSig()] + HRESULT Skip(uint celt); + + void Reset(); + + [return: MarshalAs(UnmanagedType.Interface)] + IEnumIDList Clone(); + } + + /// Critical: Suppresses unmanaged code security. + [SuppressUnmanagedCodeSecurity] + [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("42f85136-db7e-439c-85f1-e4075d135fc8")] + internal interface IFileDialog : IModalWindow + { + [PreserveSig] + new HRESULT Show(IntPtr parent); + + void SetFileTypes(uint cFileTypes, [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] COMDLG_FILTERSPEC[] rgFilterSpec); + + void SetFileTypeIndex(uint iFileType); + + uint GetFileTypeIndex(); + + uint Advise(IFileDialogEvents pfde); + + void Unadvise(uint dwCookie); + + void SetOptions(FOS fos); + + FOS GetOptions(); + + void SetDefaultFolder(IShellItem psi); + + void SetFolder(IShellItem psi); + + IShellItem GetFolder(); + + IShellItem GetCurrentSelection(); + + void SetFileName([MarshalAs(UnmanagedType.LPWStr)] string pszName); + + [return: MarshalAs(UnmanagedType.LPWStr)] + string GetFileName(); + + void SetTitle([MarshalAs(UnmanagedType.LPWStr)] string pszTitle); + + void SetOkButtonLabel([MarshalAs(UnmanagedType.LPWStr)] string pszText); + + void SetFileNameLabel([MarshalAs(UnmanagedType.LPWStr)] string pszLabel); + + IShellItem GetResult(); + + void AddPlace(IShellItem psi, FDAP alignment); + + void SetDefaultExtension([MarshalAs(UnmanagedType.LPWStr)] string pszDefaultExtension); + + void Close([MarshalAs(UnmanagedType.Error)] int hr); + + void SetClientGuid([In] ref Guid guid); + + void ClearClientData(); + + void SetFilter([MarshalAs(UnmanagedType.Interface)] object pFilter); + } + + /// Critical: Suppresses unmanaged code security. + [SuppressUnmanagedCodeSecurity] + [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("973510DB-7D7F-452B-8975-74A85828D354")] + internal interface IFileDialogEvents + { + [PreserveSig] + HRESULT OnFileOk(IFileDialog pfd); + + [PreserveSig] + HRESULT OnFolderChanging(IFileDialog pfd, IShellItem psiFolder); + + [PreserveSig] + HRESULT OnFolderChange(IFileDialog pfd); + + [PreserveSig] + HRESULT OnSelectionChange(IFileDialog pfd); + + [PreserveSig] + HRESULT OnShareViolation(IFileDialog pfd, IShellItem psi, out FDESVR pResponse); + + [PreserveSig] + HRESULT OnTypeChange(IFileDialog pfd); + + [PreserveSig] + HRESULT OnOverwrite(IFileDialog pfd, IShellItem psi, out FDEOR pResponse); + } + + /// Critical: Suppresses unmanaged code security. + [SuppressUnmanagedCodeSecurity] + [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("d57c7288-d4ad-4768-be02-9d969532d960")] + internal interface IFileOpenDialog : IFileDialog + { + [PreserveSig] + new HRESULT Show(IntPtr parent); + + new void SetFileTypes(uint cFileTypes, [In] COMDLG_FILTERSPEC[] rgFilterSpec); + + new void SetFileTypeIndex(uint iFileType); + + new uint GetFileTypeIndex(); + + new uint Advise(IFileDialogEvents pfde); + + new void Unadvise(uint dwCookie); + + new void SetOptions(FOS fos); + + new FOS GetOptions(); + + new void SetDefaultFolder(IShellItem psi); + + new void SetFolder(IShellItem psi); + + new IShellItem GetFolder(); + + new IShellItem GetCurrentSelection(); + + new void SetFileName([MarshalAs(UnmanagedType.LPWStr)] string pszName); + + [return: MarshalAs(UnmanagedType.LPWStr)] + new void GetFileName(); + + new void SetTitle([MarshalAs(UnmanagedType.LPWStr)] string pszTitle); + + new void SetOkButtonLabel([MarshalAs(UnmanagedType.LPWStr)] string pszText); + + new void SetFileNameLabel([MarshalAs(UnmanagedType.LPWStr)] string pszLabel); + + new IShellItem GetResult(); + + new void AddPlace(IShellItem psi, FDAP fdcp); + + new void SetDefaultExtension([MarshalAs(UnmanagedType.LPWStr)] string pszDefaultExtension); + + new void Close([MarshalAs(UnmanagedType.Error)] int hr); + + new void SetClientGuid([In] ref Guid guid); + + new void ClearClientData(); + + new void SetFilter([MarshalAs(UnmanagedType.Interface)] object pFilter); + + IShellItemArray GetResults(); + + IShellItemArray GetSelectedItems(); + } + + /// Critical: Suppresses unmanaged code security. + [SuppressUnmanagedCodeSecurity] + [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("84bccd23-5fde-4cdb-aea4-af64b83d78ab")] + internal interface IFileSaveDialog : IFileDialog + { + [PreserveSig] + new HRESULT Show(IntPtr parent); + + new void SetFileTypes(uint cFileTypes, [In] COMDLG_FILTERSPEC[] rgFilterSpec); + + new void SetFileTypeIndex(uint iFileType); + + new uint GetFileTypeIndex(); + + new uint Advise(IFileDialogEvents pfde); + + new void Unadvise(uint dwCookie); + + new void SetOptions(FOS fos); + + new FOS GetOptions(); + + new void SetDefaultFolder(IShellItem psi); + + new void SetFolder(IShellItem psi); + + new IShellItem GetFolder(); + + new IShellItem GetCurrentSelection(); + + new void SetFileName([MarshalAs(UnmanagedType.LPWStr)] string pszName); + + [return: MarshalAs(UnmanagedType.LPWStr)] + new void GetFileName(); + + new void SetTitle([MarshalAs(UnmanagedType.LPWStr)] string pszTitle); + + new void SetOkButtonLabel([MarshalAs(UnmanagedType.LPWStr)] string pszText); + + new void SetFileNameLabel([MarshalAs(UnmanagedType.LPWStr)] string pszLabel); + + new IShellItem GetResult(); + + new void AddPlace(IShellItem psi, FDAP fdcp); + + new void SetDefaultExtension([MarshalAs(UnmanagedType.LPWStr)] string pszDefaultExtension); + + new void Close([MarshalAs(UnmanagedType.Error)] int hr); + + new void SetClientGuid([In] ref Guid guid); + + new void ClearClientData(); + + new void SetFilter([MarshalAs(UnmanagedType.Interface)] object pFilter); + + void SetSaveAsItem(IShellItem psi); + + void SetProperties([In, MarshalAs(UnmanagedType.Interface)] object pStore); + + void SetCollectedProperties([In, MarshalAs(UnmanagedType.Interface)] object pList, [In] int fAppendDefault); + + [return: MarshalAs(UnmanagedType.Interface)] + object GetProperties(); + + void ApplyProperties(IShellItem psi, [MarshalAs(UnmanagedType.Interface)] object pStore, [In] ref IntPtr hwnd, [MarshalAs(UnmanagedType.Interface)] object pSink); + } + + /// Critical: Suppresses unmanaged code security. + [SuppressUnmanagedCodeSecurity] + [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("b4db1657-70d7-485e-8e3e-6fcb5a5c1802")] + internal interface IModalWindow + { + [PreserveSig] + HRESULT Show(IntPtr parent); + } + + /// Unknown Object Array + /// Critical: Suppresses unmanaged code security. + [SuppressUnmanagedCodeSecurity] + [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("92CA9DCD-5622-4bba-A805-5E9F541BD8C9")] + internal interface IObjectArray + { + uint GetCount(); + + [return: MarshalAs(UnmanagedType.IUnknown)] + object GetAt([In] uint uiIndex, [In] ref Guid riid); + } + + /// Critical: Suppresses unmanaged code security. + [SuppressUnmanagedCodeSecurity] + [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("92CA9DCD-5622-4bba-A805-5E9F541BD8C9")] + internal interface IObjectCollection : IObjectArray + { + new uint GetCount(); + + [return: MarshalAs(UnmanagedType.IUnknown)] + new object GetAt([In] uint uiIndex, [In] ref Guid riid); + + void AddObject([MarshalAs(UnmanagedType.IUnknown)] object punk); + + void AddFromArray(IObjectArray poaSource); + + void RemoveObjectAt(uint uiIndex); + + void Clear(); + } + + /// Provides access to the App User Model ID on objects supporting this value. + /// Critical: Suppresses unmanaged code security. + [SuppressUnmanagedCodeSecurity] + [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("36db0196-9665-46d1-9ba7-d3709eecf9ed")] + internal interface IObjectWithAppUserModelId + { + void SetAppID([MarshalAs(UnmanagedType.LPWStr)] string pszAppID); + + [return: MarshalAs(UnmanagedType.LPWStr)] + string GetAppID(); + }; + + /// Provides access to the ProgID associated with an object + /// Critical: Suppresses unmanaged code security. + [SuppressUnmanagedCodeSecurity] + [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("71e806fb-8dee-46fc-bf8c-7748a8a1ae13")] + internal interface IObjectWithProgId + { + void SetProgID([MarshalAs(UnmanagedType.LPWStr)] string pszProgID); + + [return: MarshalAs(UnmanagedType.LPWStr)] + string GetProgID(); + }; + + /// Critical: Suppresses unmanaged code security. + [SuppressUnmanagedCodeSecurity] + [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("886d8eeb-8cf2-4446-8d02-cdba1dbdcf99")] + internal interface IPropertyStore + { + uint GetCount(); + + ShellItemPropertyKey GetAt(uint iProp); + + void GetValue([In] ref ShellItemPropertyKey pkey, [Out] PROPVARIANT pv); + + void SetValue([In] ref ShellItemPropertyKey pkey, [In] PROPVARIANT pv); + + void Commit(); + } + + /// Critical: Suppresses unmanaged code security. + [SuppressUnmanagedCodeSecurity] + [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214E6-0000-0000-C000-000000000046")] + internal interface IShellFolder + { + void ParseDisplayName( + IntPtr hwnd, + IBindCtx pbc, + [MarshalAs(UnmanagedType.LPWStr)] string pszDisplayName, + [In, Out] ref int pchEaten, + out IntPtr ppidl, + [In, Out] ref uint pdwAttributes); + + IEnumIDList EnumObjects( + IntPtr hwnd, + SHCONTF grfFlags); + + // returns an instance of a sub-folder which is specified by the IDList (pidl). IShellFolder or derived interfaces + [return: MarshalAs(UnmanagedType.Interface)] + object BindToObject( + IntPtr pidl, + IBindCtx pbc, + [In] ref Guid riid); + + // produces the same result as BindToObject() + [return: MarshalAs(UnmanagedType.Interface)] + object BindToStorage(IntPtr pidl, IBindCtx pbc, [In] ref Guid riid); + + // compares two IDLists and returns the result. The shell explorer always passes 0 as lParam, which indicates + // 'sort by name'. It should return 0 (as CODE of the scode), if two id indicates the same object; negative + // value if pidl1 should be placed before pidl2; positive value if pidl2 should be placed before pidl1. use + // the macro ResultFromShort() to extract the result comparison it deals with the casting and type conversion + // issues for you + [PreserveSig] + HRESULT CompareIDs(IntPtr lParam, IntPtr pidl1, IntPtr pidl2); + + // creates a view object of the folder itself. The view object is a difference instance from the shell folder + // object. 'hwndOwner' can be used as the owner window of its dialog box or menu during the lifetime of the + // view object. This member function should always create a new instance which has only one reference count. + // The explorer may create more than one instances of view object from one shell folder object and treat them + // as separate instances. returns IShellView derived interface + [return: MarshalAs(UnmanagedType.Interface)] + object CreateViewObject(IntPtr hwndOwner, [In] ref Guid riid); + + // returns the attributes of specified objects in that folder. 'cidl' and 'apidl' specifies objects. 'apidl' + // contains only simple IDLists. The explorer initializes *prgfInOut with a set of flags to be evaluated. The + // shell folder may optimize the operation by not returning unspecified flags. + void GetAttributesOf( + uint cidl, + IntPtr apidl, + [In, Out] ref SFGAO rgfInOut); + + // creates a UI object to be used for specified objects. The shell explorer passes either IID_IDataObject + // (for transfer operation) or IID_IContextMenu (for context menu operation) as riid and many other interfaces + [return: MarshalAs(UnmanagedType.Interface)] + object GetUIObjectOf( + IntPtr hwndOwner, + uint cidl, + [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.SysInt, SizeParamIndex = 1)] IntPtr apidl, + [In] ref Guid riid, + [In, Out] ref uint rgfReserved); + + // returns the display name of the specified object. If the ID contains the display name (in the locale + // character set), it returns the offset to the name. Otherwise, it returns a pointer to the display name + // string (UNICODE), which is allocated by the task allocator, or fills in a buffer. use the helper APIS + // StrRetToStr() or StrRetToBuf() to deal with the different forms of the STRRET structure + void GetDisplayNameOf(IntPtr pidl, SHGDN uFlags, out IntPtr pName); + + // sets the display name of the specified object. If it changes the ID as well, it returns the new ID which + // is alocated by the task allocator. + void SetNameOf(IntPtr hwnd, + IntPtr pidl, + [MarshalAs(UnmanagedType.LPWStr)] string pszName, + SHGDN uFlags, + out IntPtr ppidlOut); + } + + /// Critical: Suppresses unmanaged code security. + [SuppressUnmanagedCodeSecurity] + [ + ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe"), + ] + internal interface IShellItem + { + [return: MarshalAs(UnmanagedType.Interface)] + object BindToHandler(IBindCtx pbc, [In] ref Guid bhid, [In] ref Guid riid); + + IShellItem GetParent(); + + [return: MarshalAs(UnmanagedType.LPWStr)] + string GetDisplayName(SIGDN sigdnName); + + uint GetAttributes(SFGAO sfgaoMask); + + int Compare(IShellItem psi, SICHINT hint); + } + + /// Shell Namespace helper 2 + /// Critical: Suppresses unmanaged code security. + [SuppressUnmanagedCodeSecurity] + [ + ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("7e9fb0d3-919f-4307-ab2e-9b1860310c93"), + ] + internal interface IShellItem2 : IShellItem + { + [return: MarshalAs(UnmanagedType.Interface)] + new object BindToHandler(IBindCtx pbc, [In] ref Guid bhid, [In] ref Guid riid); + + new IShellItem GetParent(); + + [return: MarshalAs(UnmanagedType.LPWStr)] + new string GetDisplayName(SIGDN sigdnName); + + new SFGAO GetAttributes(SFGAO sfgaoMask); + + new int Compare(IShellItem psi, SICHINT hint); + + [return: MarshalAs(UnmanagedType.Interface)] + object GetPropertyStore( + GPS flags, + [In] ref Guid riid); + + [return: MarshalAs(UnmanagedType.Interface)] + object GetPropertyStoreWithCreateObject( + GPS flags, + [MarshalAs(UnmanagedType.IUnknown)] object punkCreateObject, // factory for low-rights creation of type ICreateObject + [In] ref Guid riid); + + [return: MarshalAs(UnmanagedType.Interface)] + object GetPropertyStoreForKeys( + IntPtr rgKeys, + uint cKeys, + GPS flags, + [In] ref Guid riid); + + [return: MarshalAs(UnmanagedType.Interface)] + object GetPropertyDescriptionList( + IntPtr keyType, + [In] ref Guid riid); + + // Ensures any cached information in this item is up to date, or returns + // __HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) if the item does not exist. + void Update(IBindCtx pbc); + + void GetProperty([In] ref ShellItemPropertyKey key, ref PROPVARIANT pv); + + Guid GetCLSID([In] ref ShellItemPropertyKey key); + + FILETIME GetFileTime([In] ref ShellItemPropertyKey key); + + int GetInt32([In] ref ShellItemPropertyKey key); + + [return: MarshalAs(UnmanagedType.LPWStr)] + string GetString([In] ref ShellItemPropertyKey key); + + uint GetUInt32([In] ref ShellItemPropertyKey key); + + ulong GetUInt64([In] ref ShellItemPropertyKey key); + + [return: MarshalAs(UnmanagedType.Bool)] + bool GetBool([In] ref ShellItemPropertyKey key); + } + + /// Critical: Suppresses unmanaged code security. + [SuppressUnmanagedCodeSecurity] + [ + ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("B63EA76D-1F85-456F-A19C-48159EFA858B"), + ] + internal interface IShellItemArray + { + [return: MarshalAs(UnmanagedType.Interface)] + object BindToHandler(IBindCtx pbc, [In] ref Guid rbhid, [In] ref Guid riid); + + [return: MarshalAs(UnmanagedType.Interface)] + object GetPropertyStore(int flags, [In] ref Guid riid); + + [return: MarshalAs(UnmanagedType.Interface)] + object GetPropertyDescriptionList([In] ref ShellItemPropertyKey keyType, [In] ref Guid riid); + + uint GetAttributes(SIATTRIBFLAGS dwAttribFlags, uint sfgaoMask); + + uint GetCount(); + + IShellItem GetItemAt(uint dwIndex); + + [return: MarshalAs(UnmanagedType.Interface)] + object EnumItems(); + } + + /// Critical: Suppresses unmanaged code security. + [SuppressUnmanagedCodeSecurity] + [ + ComImport, + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown), + Guid("000214F9-0000-0000-C000-000000000046"), + ] + internal interface IShellLinkW + { + void GetPath([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, [In, Out] WIN32_FIND_DATA pfd, SLGP fFlags); + + IntPtr GetIDList(); + + void SetIDList(IntPtr pidl); + + void GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxName); + + void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName); + + void GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath); + + void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir); + + void GetArguments([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath); + + void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs); + + short GetHotKey(); + + void SetHotKey(short wHotKey); + + uint GetShowCmd(); + + void SetShowCmd(uint iShowCmd); + + void GetIconLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, int cchIconPath, out int piIcon); + + void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon); + + void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, uint dwReserved); + + void Resolve(IntPtr hwnd, uint fFlags); + + void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile); + } + + /// Critical: Suppresses unmanaged code security. + [SuppressUnmanagedCodeSecurity] + [ + ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("56FDF342-FD6D-11d0-958A-006097C9A090"), + ] + internal interface ITaskbarList + { + /// This function must be called first to validate use of other members. + void HrInit(); + + /// This function adds a tab for hwnd to the taskbar. The HWND for + /// which to add the tab. + void AddTab(IntPtr hwnd); + + /// This function deletes a tab for hwnd from the taskbar. The HWND + /// for which the tab is to be deleted. + void DeleteTab(IntPtr hwnd); + + /// This function activates the tab associated with hwnd on the taskbar. The HWND for which the tab is to be activated. + void ActivateTab(IntPtr hwnd); + + /// This function marks hwnd in the taskbar as the active tab. The + /// HWND to activate. + void SetActiveAlt(IntPtr hwnd); + } + + /// Critical: Suppresses unmanaged code security. + [SuppressUnmanagedCodeSecurity] + [ + ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("602D4995-B13A-429b-A66E-1935E44F4317"), + ] + internal interface ITaskbarList2 : ITaskbarList + { + new void HrInit(); + + new void AddTab(IntPtr hwnd); + + new void DeleteTab(IntPtr hwnd); + + new void ActivateTab(IntPtr hwnd); + + new void SetActiveAlt(IntPtr hwnd); + + /// Marks a window as full-screen. The handle of the window to be + /// marked. A Boolean value marking the desired full-screen status of the window. + /// Setting the value of fFullscreen to true, the Shell treats this window as a full-screen window, + /// and the taskbar is moved to the bottom of the z-order when this window is active. Setting the value of + /// fFullscreen to false removes the full-screen marking, but does not cause the Shell to treat the + /// window as though it were definitely not full-screen. With a false fFullscreen value, the Shell depends on + /// its automatic detection facility to specify how the window should be treated, possibly still flagging the + /// window as full-screen. + void MarkFullscreenWindow(IntPtr hwnd, [MarshalAs(UnmanagedType.Bool)] bool fFullscreen); + } + + /// Critical: Suppresses unmanaged code security. + /// + /// Methods on this interface are marked as PreserveSig because the implementation inconsistently surfaces errors + /// in Explorer. Many of these methods are implemented by posting messages to the desktop window, but if explorer + /// is not running or currently busy then we get back error codes that must be handled by the caller. + /// + [SuppressUnmanagedCodeSecurity] + [ + ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("ea1afb91-9e28-4b86-90e9-9e9f8a5eefaf"), + ] + internal interface ITaskbarList3 : ITaskbarList2 + { + new void HrInit(); + + new void AddTab(IntPtr hwnd); + + new void DeleteTab(IntPtr hwnd); + + new void ActivateTab(IntPtr hwnd); + + new void SetActiveAlt(IntPtr hwnd); + + new void MarkFullscreenWindow(IntPtr hwnd, [MarshalAs(UnmanagedType.Bool)] bool fFullscreen); + + [PreserveSig] + HRESULT SetProgressValue(IntPtr hwnd, ulong ullCompleted, ulong ullTotal); + + [PreserveSig] + HRESULT SetProgressState(IntPtr hwnd, TBPF tbpFlags); + + [PreserveSig] + HRESULT RegisterTab(IntPtr hwndTab, IntPtr hwndMDI); + + [PreserveSig] + HRESULT UnregisterTab(IntPtr hwndTab); + + [PreserveSig] + HRESULT SetTabOrder(IntPtr hwndTab, IntPtr hwndInsertBefore); + + [PreserveSig] + HRESULT SetTabActive(IntPtr hwndTab, IntPtr hwndMDI, uint dwReserved); + + [PreserveSig] + HRESULT ThumbBarAddButtons(IntPtr hwnd, uint cButtons, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] THUMBBUTTON[] pButtons); + + [PreserveSig] + HRESULT ThumbBarUpdateButtons(IntPtr hwnd, uint cButtons, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] THUMBBUTTON[] pButtons); + + [PreserveSig] + HRESULT ThumbBarSetImageList(IntPtr hwnd, IntPtr himl); + + [PreserveSig] + HRESULT SetOverlayIcon(IntPtr hwnd, IntPtr hIcon, [MarshalAs(UnmanagedType.LPWStr)] string pszDescription); + + [PreserveSig] + HRESULT SetThumbnailTooltip(IntPtr hwnd, [MarshalAs(UnmanagedType.LPWStr)] string pszTip); + + // Using RefRECT to making passing NULL possible. Removes clipping from the HWND. + [PreserveSig] + HRESULT SetThumbnailClip(IntPtr hwnd, ref RECT prcClip); + } + + /// Critical: Suppresses unmanaged code security. + [SuppressUnmanagedCodeSecurity] + [ + ComImport, + InterfaceType(ComInterfaceType.InterfaceIsIUnknown), + Guid("ea1afb91-9e28-4b86-90e9-9e9f8a5eefaf"), + ] + internal interface ITaskbarList4 : ITaskbarList3 + { + new void HrInit(); + + new void AddTab(IntPtr hwnd); + + new void DeleteTab(IntPtr hwnd); + + new void ActivateTab(IntPtr hwnd); + + new void SetActiveAlt(IntPtr hwnd); + + new void MarkFullscreenWindow(IntPtr hwnd, [MarshalAs(UnmanagedType.Bool)] bool fFullscreen); + + [PreserveSig] + new HRESULT SetProgressValue(IntPtr hwnd, ulong ullCompleted, ulong ullTotal); + + [PreserveSig] + new HRESULT SetProgressState(IntPtr hwnd, TBPF tbpFlags); + + [PreserveSig] + new HRESULT RegisterTab(IntPtr hwndTab, IntPtr hwndMDI); + + [PreserveSig] + new HRESULT UnregisterTab(IntPtr hwndTab); + + [PreserveSig] + new HRESULT SetTabOrder(IntPtr hwndTab, IntPtr hwndInsertBefore); + + [PreserveSig] + new HRESULT SetTabActive(IntPtr hwndTab, IntPtr hwndMDI, uint dwReserved); + + [PreserveSig] + new HRESULT ThumbBarAddButtons(IntPtr hwnd, uint cButtons, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] THUMBBUTTON[] pButtons); + + [PreserveSig] + new HRESULT ThumbBarUpdateButtons(IntPtr hwnd, uint cButtons, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] THUMBBUTTON[] pButtons); + + [PreserveSig] + new HRESULT ThumbBarSetImageList(IntPtr hwnd, IntPtr himl); + + [PreserveSig] + new HRESULT SetOverlayIcon(IntPtr hwnd, IntPtr hIcon, [MarshalAs(UnmanagedType.LPWStr)] string pszDescription); + + [PreserveSig] + new HRESULT SetThumbnailTooltip(IntPtr hwnd, [MarshalAs(UnmanagedType.LPWStr)] string pszTip); + + [PreserveSig] + new HRESULT SetThumbnailClip(IntPtr hwnd, ref RECT prcClip); + + [PreserveSig] + HRESULT SetTabProperties(IntPtr hwndTab, STPF stpFlags); + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + internal struct COMDLG_FILTERSPEC + { + [MarshalAs(UnmanagedType.LPWStr)] + public string pszName; + + [MarshalAs(UnmanagedType.LPWStr)] + public string pszSpec; + } + + [StructLayout(LayoutKind.Explicit)] + internal struct HRESULT + { + // Fields + [FieldOffset(0)] + private readonly uint _value; + + public static readonly HRESULT COR_E_OBJECTDISPOSED; + public static readonly HRESULT DESTS_E_NO_MATCHING_ASSOC_HANDLER; + public static readonly HRESULT DISP_E_BADINDEX; + public static readonly HRESULT DISP_E_BADPARAMCOUNT; + public static readonly HRESULT DISP_E_EXCEPTION; + public static readonly HRESULT DISP_E_MEMBERNOTFOUND; + public static readonly HRESULT DISP_E_OVERFLOW; + public static readonly HRESULT DISP_E_PARAMNOTOPTIONAL; + public static readonly HRESULT DISP_E_TYPEMISMATCH; + public static readonly HRESULT DISP_E_UNKNOWNNAME; + public static readonly HRESULT E_ABORT; + public static readonly HRESULT E_ACCESSDENIED; + public static readonly HRESULT E_FAIL; + public static readonly HRESULT E_INVALIDARG; + public static readonly HRESULT E_NOINTERFACE; + public static readonly HRESULT E_NOTIMPL; + public static readonly HRESULT E_OUTOFMEMORY; + public static readonly HRESULT E_POINTER; + public static readonly HRESULT E_UNEXPECTED; + public static readonly HRESULT S_FALSE; + public static readonly HRESULT S_OK; + public static readonly HRESULT SCRIPT_E_REPORTED; + public static readonly HRESULT STG_E_INVALIDFUNCTION; + public static readonly HRESULT WC_E_GREATERTHAN; + public static readonly HRESULT WC_E_SYNTAX; + + // Methods + static HRESULT() + { + S_OK = new HRESULT(0); + S_FALSE = new HRESULT(1); + E_NOTIMPL = new HRESULT(0x80004001); + E_NOINTERFACE = new HRESULT(0x80004002); + E_POINTER = new HRESULT(0x80004003); + E_ABORT = new HRESULT(0x80004004); + E_FAIL = new HRESULT(0x80004005); + E_UNEXPECTED = new HRESULT(0x8000ffff); + DISP_E_MEMBERNOTFOUND = new HRESULT(0x80020003); + DISP_E_TYPEMISMATCH = new HRESULT(0x80020005); + DISP_E_UNKNOWNNAME = new HRESULT(0x80020006); + DISP_E_EXCEPTION = new HRESULT(0x80020009); + DISP_E_OVERFLOW = new HRESULT(0x8002000a); + DISP_E_BADINDEX = new HRESULT(0x8002000b); + DISP_E_BADPARAMCOUNT = new HRESULT(0x8002000e); + DISP_E_PARAMNOTOPTIONAL = new HRESULT(0x8002000f); + SCRIPT_E_REPORTED = new HRESULT(0x80020101); + STG_E_INVALIDFUNCTION = new HRESULT(0x80030001); + DESTS_E_NO_MATCHING_ASSOC_HANDLER = new HRESULT(0x80040f03); + E_ACCESSDENIED = new HRESULT(0x80070005); + E_OUTOFMEMORY = new HRESULT(0x8007000e); + E_INVALIDARG = new HRESULT(0x80070057); + COR_E_OBJECTDISPOSED = new HRESULT(0x80131622); + WC_E_GREATERTHAN = new HRESULT(0xc00cee23); + WC_E_SYNTAX = new HRESULT(0xc00cee2d); + } + + public HRESULT(uint i) + { + _value = i; + } + + public override bool Equals(object obj) + { + try + { + return (((HRESULT)obj)._value == _value); + } + catch (InvalidCastException) + { + return false; + } + } + + public static int GetCode(int error) => (error & 0xffff); + + public Exception GetException() => GetException(null); + + [SecurityCritical, SecuritySafeCritical] + public Exception GetException(string message) + { + if (!Failed) + { + return null; + } + Exception exceptionForHR = Marshal.GetExceptionForHR((int)_value, new IntPtr(-1)); + if (exceptionForHR.GetType() == typeof(COMException)) + { + if (Facility == Facility.Win32) + { + if (string.IsNullOrEmpty(message)) + { + return new Win32Exception(Code); + } + return new Win32Exception(Code, message); + } + return new COMException(message ?? exceptionForHR.Message, (int)_value); + } + if (!string.IsNullOrEmpty(message)) + { + Type[] types = new Type[] { typeof(string) }; + ConstructorInfo constructor = exceptionForHR.GetType().GetConstructor(types); + if (null != constructor) + { + object[] parameters = new object[] { message }; + exceptionForHR = constructor.Invoke(parameters) as Exception; + } + } + return exceptionForHR; + } + + public static Facility GetFacility(int errorCode) => (((Facility)(errorCode >> 0x10)) & ((Facility)0x1fff)); + + public override int GetHashCode() => _value.GetHashCode(); + + public static HRESULT Make(bool severe, Facility facility, int code) => new HRESULT((uint)(((severe ? -2147483648 : 0) | (((int)facility) << 0x10)) | code)); + + public static bool operator ==(HRESULT hrLeft, HRESULT hrRight) => (hrLeft._value == hrRight._value); + + public static bool operator !=(HRESULT hrLeft, HRESULT hrRight) => !(hrLeft == hrRight); + + public void ThrowIfFailed() + { + ThrowIfFailed(null); + } + + [SecurityCritical, SecuritySafeCritical] + public void ThrowIfFailed(string message) + { + Exception exception = GetException(message); + if (exception != null) + { + throw exception; + } + } + + public override string ToString() + { + foreach (FieldInfo info in typeof(HRESULT).GetFields(BindingFlags.Public | BindingFlags.Static)) + { + if ((info.FieldType == typeof(HRESULT)) && (((HRESULT)info.GetValue(null)) == this)) + { + return info.Name; + } + } + if (Facility == Facility.Win32) + { + foreach (FieldInfo info2 in typeof(Win32Error).GetFields(BindingFlags.Public | BindingFlags.Static)) + { + if ((info2.FieldType == typeof(Win32Error)) && (((HRESULT)((Win32Error)info2.GetValue(null))) == this)) + { + return ("HRESULT_FROM_WIN32(" + info2.Name + ")"); + } + } + } + object[] args = new object[] { _value }; + return string.Format(CultureInfo.InvariantCulture, "0x{0:X8}", args); + } + + // Properties + public int Code => GetCode((int)_value); + + public Facility Facility => GetFacility((int)_value); + + public bool Failed => (_value < 0); + + public bool Succeeded => (_value >= 0); + } + + [StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Unicode)] + internal struct THUMBBUTTON + { + /// WPARAM value for a THUMBBUTTON being clicked. + public const int THBN_CLICKED = 0x1800; + + public THB dwMask; + public uint iId; + public uint iBitmap; + public IntPtr hIcon; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] + public string szTip; + + public THBF dwFlags; + + public static THUMBBUTTON Default = new THUMBBUTTON() { dwMask = THB.FLAGS, dwFlags = THBF.HIDDEN }; + } + + [StructLayout(LayoutKind.Explicit)] + internal struct Win32Error + { + // Fields + [FieldOffset(0)] + private readonly int _value; + + public static readonly Win32Error ERROR_ACCESS_DENIED; + public static readonly Win32Error ERROR_BAD_DEVICE; + public static readonly Win32Error ERROR_CANCELLED; + public static readonly Win32Error ERROR_FILE_NOT_FOUND; + public static readonly Win32Error ERROR_INSUFFICIENT_BUFFER; + public static readonly Win32Error ERROR_INVALID_DATATYPE; + public static readonly Win32Error ERROR_INVALID_FUNCTION; + public static readonly Win32Error ERROR_INVALID_HANDLE; + public static readonly Win32Error ERROR_INVALID_PARAMETER; + public static readonly Win32Error ERROR_INVALID_WINDOW_HANDLE; + public static readonly Win32Error ERROR_KEY_DELETED; + public static readonly Win32Error ERROR_NESTING_NOT_ALLOWED; + public static readonly Win32Error ERROR_NO_MATCH; + public static readonly Win32Error ERROR_NO_MORE_FILES; + public static readonly Win32Error ERROR_OUTOFMEMORY; + public static readonly Win32Error ERROR_PATH_NOT_FOUND; + public static readonly Win32Error ERROR_SHARING_VIOLATION; + public static readonly Win32Error ERROR_SUCCESS; + public static readonly Win32Error ERROR_TIMEOUT; + public static readonly Win32Error ERROR_TOO_MANY_OPEN_FILES; + + // Methods + static Win32Error() + { + ERROR_SUCCESS = new Win32Error(0); + ERROR_INVALID_FUNCTION = new Win32Error(1); + ERROR_FILE_NOT_FOUND = new Win32Error(2); + ERROR_PATH_NOT_FOUND = new Win32Error(3); + ERROR_TOO_MANY_OPEN_FILES = new Win32Error(4); + ERROR_ACCESS_DENIED = new Win32Error(5); + ERROR_INVALID_HANDLE = new Win32Error(6); + ERROR_OUTOFMEMORY = new Win32Error(14); + ERROR_NO_MORE_FILES = new Win32Error(0x12); + ERROR_SHARING_VIOLATION = new Win32Error(0x20); + ERROR_INVALID_PARAMETER = new Win32Error(0x57); + ERROR_INSUFFICIENT_BUFFER = new Win32Error(0x7a); + ERROR_NESTING_NOT_ALLOWED = new Win32Error(0xd7); + ERROR_KEY_DELETED = new Win32Error(0x3fa); + ERROR_NO_MATCH = new Win32Error(0x491); + ERROR_BAD_DEVICE = new Win32Error(0x4b0); + ERROR_CANCELLED = new Win32Error(0x4c7); + ERROR_INVALID_WINDOW_HANDLE = new Win32Error(0x578); + ERROR_TIMEOUT = new Win32Error(0x5b4); + ERROR_INVALID_DATATYPE = new Win32Error(0x70c); + } + + public Win32Error(int i) + { + _value = i; + } + + public override bool Equals(object obj) + { + try + { + return (((Win32Error)obj)._value == _value); + } + catch (InvalidCastException) + { + return false; + } + } + + public override int GetHashCode() => _value.GetHashCode(); + + [SecurityCritical] + public static Win32Error GetLastError() => new Win32Error(Marshal.GetLastWin32Error()); + + public static bool operator ==(Win32Error errLeft, Win32Error errRight) => (errLeft._value == errRight._value); + + public static explicit operator HRESULT(Win32Error error) + { + if (error._value <= 0) + { + return new HRESULT((uint)error._value); + } + return HRESULT.Make(true, Facility.Win32, error._value & 0xffff); + } + + public static bool operator !=(Win32Error errLeft, Win32Error errRight) => !(errLeft == errRight); + + public HRESULT ToHRESULT() => (HRESULT)this; + } + + [ComImport, Guid("77f10cf0-3db5-4966-b520-b7c54fd35ed6"), ClassInterface(ClassInterfaceType.None)] + public class CDestinationList { } + + [ComImport, Guid("2d3468c1-36a7-43b6-ac24-d3f02fd9607a"), ClassInterface(ClassInterfaceType.None)] + public class CEnumerableObjectCollection { } + + [ComImport, Guid("00021401-0000-0000-C000-000000000046"), ClassInterface(ClassInterfaceType.None)] + public class CShellLinkW { } + + [ComImport, Guid("56FDF344-FD6D-11d0-958A-006097C9A090"), ClassInterface(ClassInterfaceType.None)] + public class CTaskbarList { } + + /// Methods in this class will only work on Vista and above. + internal static class ShellUtil + { + /// + /// Critical: Resolves an opaque Guid into a path on the user's machine. Calls critical SHGetFolderPathEx + /// + [SecurityCritical] + public static string GetPathForKnownFolder(Guid knownFolder) + { + if (knownFolder == default(Guid)) + return null; + + var pathBuilder = new StringBuilder(260); + HRESULT hr = SHGetFolderPathEx(ref knownFolder, 0, IntPtr.Zero, pathBuilder, (uint)pathBuilder.Capacity); + return hr.Succeeded ? pathBuilder.ToString() : null; + } + + /// Critical: Resolves an opaque interface into a path on the user's machine. + [SecurityCritical] + public static string GetPathFromShellItem(IShellItem item) => item.GetDisplayName(SIGDN.DESKTOPABSOLUTEPARSING); + + /// Critical: Calls SHCreateItemFromParsingName + [SecurityCritical] + public static IShellItem2 GetShellItemForPath(string path) + { + if (string.IsNullOrEmpty(path)) + return null; + + IShellItem2 unk = null; + SHCreateItemFromParsingName(path, null, Marshal.GenerateGuidForType(typeof(IShellItem2)), out unk); + return unk; + } + } + + [StructLayout(LayoutKind.Sequential)] + internal class PROPVARIANT : IDisposable + { + private ushort vt; + private ushort wReserved1; + private ushort wReserved2; + private ushort wReserved3; + private IntPtr valueData; + private Int32 valueDataExt; + + public PROPVARIANT() + { + } + + public PROPVARIANT(object value) + { + Value = value; + } + + [SecurityCritical, SecuritySafeCritical] + public void Clear() + { + NativeMethods.PropVariantClear(this); + } + + [SecurityCritical, SecuritySafeCritical] + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + [SecurityCritical, SecuritySafeCritical] + private void Dispose(bool disposing) + { + Clear(); + } + + [SecurityCritical, SecuritySafeCritical] + ~PROPVARIANT() + { + Dispose(false); + } + + private byte[] ByteArray + { + get + { + byte[] ret = new byte[IntPtr.Size + sizeof(int)]; + if (IntPtr.Size == 4) + BitConverter.GetBytes(valueData.ToInt32()).CopyTo(ret, 0); + else if (IntPtr.Size == 8) + BitConverter.GetBytes(valueData.ToInt64()).CopyTo(ret, 0); + BitConverter.GetBytes(valueDataExt).CopyTo(ret, IntPtr.Size); + return ret; + } + } + + public T GetValue() => (T)Value; + + public object Value + { + get + { + switch ((VarEnum)vt) + { + case VarEnum.VT_EMPTY: + case VarEnum.VT_NULL: + return DBNull.Value; + + case VarEnum.VT_I2: + return BitConverter.ToInt16(ByteArray, 0); + + case VarEnum.VT_INT: + case VarEnum.VT_I4: + case VarEnum.VT_ERROR: + return BitConverter.ToInt32(ByteArray, 0); + + case VarEnum.VT_BSTR: + return Marshal.PtrToStringBSTR(valueData); + + case VarEnum.VT_DISPATCH: + case VarEnum.VT_UNKNOWN: + return valueData == IntPtr.Zero ? null : Marshal.GetObjectForIUnknown(valueData); + + case VarEnum.VT_BOOL: + return BitConverter.ToBoolean(ByteArray, 0); + + case VarEnum.VT_I1: + return BitConverter.ToChar(ByteArray, 0); + + case VarEnum.VT_UI1: + return ByteArray[0]; + + case VarEnum.VT_UI2: + return BitConverter.ToUInt16(ByteArray, 0); + + case VarEnum.VT_UINT: + case VarEnum.VT_UI4: + return BitConverter.ToUInt32(ByteArray, 0); + + case VarEnum.VT_I8: + return BitConverter.ToInt64(ByteArray, 0); + + case VarEnum.VT_UI8: + return BitConverter.ToUInt64(ByteArray, 0); + + case VarEnum.VT_VOID: + case VarEnum.VT_HRESULT: + case VarEnum.VT_PTR: + case VarEnum.VT_USERDEFINED: + return valueData; + + case VarEnum.VT_LPSTR: + return Marshal.PtrToStringAnsi(valueData); + + case VarEnum.VT_LPWSTR: + return Marshal.PtrToStringUni(valueData); + + case VarEnum.VT_R8: + return BitConverter.ToDouble(ByteArray, 0); + + case VarEnum.VT_DATE: + return DateTime.FromOADate(BitConverter.ToDouble(ByteArray, 0)); + + case VarEnum.VT_CY: + return decimal.FromOACurrency(BitConverter.ToInt64(ByteArray, 0)); + + case VarEnum.VT_DECIMAL: + return new decimal(new int[] { (int)valueData, valueDataExt, (wReserved3 << 16) | wReserved2, (wReserved1 << 16) }); + + case VarEnum.VT_R4: + return BitConverter.ToSingle(ByteArray, 0); + + case VarEnum.VT_FILETIME: + return DateTime.FromFileTime(BitConverter.ToInt64(ByteArray, 0)); + /*case VarEnum.VT_VARIANT: + case VarEnum.VT_CARRAY: + case VarEnum.VT_RECORD: + case VarEnum.VT_BLOB: + case VarEnum.VT_SAFEARRAY: + case VarEnum.VT_STREAM: + case VarEnum.VT_STORAGE: + case VarEnum.VT_STREAMED_OBJECT: + case VarEnum.VT_STORED_OBJECT: + case VarEnum.VT_BLOB_OBJECT: + case VarEnum.VT_CF: + case VarEnum.VT_CLSID: + case VarEnum.VT_VECTOR: + case VarEnum.VT_ARRAY: + case VarEnum.VT_BYREF:*/ + default: + break; + } + return null; + } + set + { + Clear(); + if (value == null || DBNull.Value.Equals(value)) + { + vt = (ushort)VarEnum.VT_NULL; + } + else if (value is bool) + { + vt = (ushort)VarEnum.VT_BOOL; + valueData = ((bool)value) ? (IntPtr)(-1) : (IntPtr)0; + } + else if (value is string) + { + vt = (ushort)VarEnum.VT_BSTR; + valueData = Marshal.StringToCoTaskMemUni(value.ToString()); + } + else if (value is byte) + { + vt = (ushort)VarEnum.VT_UI1; + valueData = (IntPtr)value; + } + else if (value is char) + { + vt = (ushort)VarEnum.VT_I1; + valueData = (IntPtr)value; + } + else if (value is double) + { + vt = (ushort)VarEnum.VT_R8; + valueData = (IntPtr)value; + } + else if (value is float) + { + vt = (ushort)VarEnum.VT_R4; + valueData = (IntPtr)value; + } + else if (value is decimal || value is float) + { + vt = (ushort)VarEnum.VT_DECIMAL; + int[] bits = decimal.GetBits((decimal)value); + valueData = (IntPtr)bits[0]; + valueDataExt = bits[1]; + wReserved3 = (ushort)(bits[2] >> 16); + wReserved2 = (ushort)(bits[2] & 0x0000FFFF); + wReserved1 = (ushort)(bits[3] >> 16); + } + else if (value is DateTime || value is FILETIME) + { + vt = (ushort)VarEnum.VT_FILETIME; + FILETIME ft; + if (value is FILETIME) + ft = (FILETIME)value; + else + { + long lft = ((DateTime)value).ToFileTime(); + ft = new FILETIME { dwHighDateTime = (int)(lft >> 32), dwLowDateTime = (int)(lft & 0xFFFFFFFF) }; + } + PROPVARIANT pv; + NativeMethods.InitPropVariantFromFileTime(ref ft, out pv); + valueData = pv.valueData; + valueDataExt = pv.valueDataExt; + } + else if (value is CurrencyWrapper) + { + vt = (ushort)VarEnum.VT_CY; + valueData = (IntPtr)decimal.ToOACurrency(((CurrencyWrapper)value).WrappedObject); + } + else if (value is ulong) + { + vt = (ushort)VarEnum.VT_UI8; + valueData = (IntPtr)value; + } + else if (value is long) + { + vt = (ushort)VarEnum.VT_I8; + valueData = (IntPtr)value; + } + else if (value is IntPtr) + { + vt = (ushort)VarEnum.VT_PTR; + valueData = (IntPtr)value; + } + else if (value is int) + { + vt = (ushort)VarEnum.VT_I4; + valueData = (IntPtr)value; + } + else if (value is uint) + { + vt = (ushort)VarEnum.VT_UI4; + valueData = (IntPtr)value; + } + else if (value is short) + { + vt = (ushort)VarEnum.VT_I2; + valueData = (IntPtr)value; + } + else if (value is ushort) + { + vt = (ushort)VarEnum.VT_UI2; + valueData = (IntPtr)value; + } + else if (Marshal.IsComObject(value)) + { + var id = Marshal.GetIDispatchForObject(value); + if (id != null) + { + vt = (ushort)VarEnum.VT_DISPATCH; + valueData = id; + } + else + { + vt = (ushort)VarEnum.VT_UNKNOWN; + valueData = Marshal.GetIUnknownForObject(value); + } + } + else + throw new InvalidCastException("This supplied type is not supported."); + } + } + + public VarEnum VarType => (VarEnum)vt; + + public override string ToString() => Value?.ToString() ?? base.ToString(); + + // Nested Types + private static class NativeMethods + { + // Methods + [SecurityCritical, SuppressUnmanagedCodeSecurity, DllImport("ole32.dll")] + internal static extern int PropVariantClear(PROPVARIANT pvar); + + [DllImport("propsys.dll", CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern uint InitPropVariantFromFileTime([In] ref System.Runtime.InteropServices.ComTypes.FILETIME pftIn, out PROPVARIANT ppropvar); + } + } + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/Native/TaskbarList.cs b/AeroWizard/AeroWizard/Native/TaskbarList.cs new file mode 100644 index 0000000..5f331c1 --- /dev/null +++ b/AeroWizard/AeroWizard/Native/TaskbarList.cs @@ -0,0 +1,193 @@ +// Requires ShlObjIdl.cs + +using System; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Windows.Forms; + +namespace Vanara.Interop +{ + [System.Security.SuppressUnmanagedCodeSecurity] + internal static partial class NativeMethods + { + public static class TaskbarList + { + static readonly Finalizer finalizer = new Finalizer(); + static ITaskbarList2 taskbar2; + static ITaskbarList4 taskbar4; + + static TaskbarList() + { + var tb = new CTaskbarList(); + taskbar2 = (ITaskbarList2)tb; + try { taskbar4 = (ITaskbarList4)tb; } catch { taskbar4 = null; } + taskbar2?.HrInit(); + } + + sealed class Finalizer + { + ~Finalizer() + { + if (taskbar2 != null) + Marshal.ReleaseComObject(taskbar2); + if (taskbar4 != null) + Marshal.ReleaseComObject(taskbar4); + } + } + + public static uint TaskbarButtonCreatedWinMsgId => RegisterWindowMessage("TaskbarButtonCreated"); + + public static void ActivateTaskbarItem(IWin32Window parent) + { + if (parent == null) + throw new ArgumentNullException(nameof(parent)); + taskbar2?.ActivateTab(parent.Handle); + } + + public static void MarkFullscreenWindow(IWin32Window parent, bool fullscreen) + { + if (parent == null) + throw new ArgumentNullException(nameof(parent)); + taskbar2?.MarkFullscreenWindow(parent.Handle, fullscreen); + } + + public static void SetActiveAlt(IWin32Window parent) + { + if (parent == null) + throw new ArgumentNullException(nameof(parent)); + taskbar2?.SetActiveAlt(parent.Handle); + } + + // Thumbnail Toolbars ============================================ + + public static void ThumbBarAddButtons(IWin32Window parent, THUMBBUTTON[] buttons) + { + Validate7OrLater(); + if (parent == null) + throw new ArgumentNullException(nameof(parent)); + if (buttons == null) + throw new ArgumentNullException(nameof(buttons)); + taskbar4?.ThumbBarAddButtons(parent.Handle, (uint)buttons.Length, buttons); + } + + public static void ThumbBarSetImageList(IWin32Window parent, ImageList imageList) + { + Validate7OrLater(); + if (parent == null) + throw new ArgumentNullException(nameof(parent)); + if (imageList == null) + throw new ArgumentNullException(nameof(imageList)); + taskbar4?.ThumbBarSetImageList(parent.Handle, imageList.Handle); + } + + public static void ThumbBarUpdateButtons(IWin32Window parent, THUMBBUTTON[] buttons) + { + Validate7OrLater(); + if (parent == null) + throw new ArgumentNullException(nameof(parent)); + if (buttons == null) + throw new ArgumentNullException(nameof(buttons)); + taskbar4?.ThumbBarUpdateButtons(parent.Handle, (uint)buttons.Length, buttons); + } + + // Overlays ============================================ + + public static void SetOverlayIcon(IWin32Window parent, Icon icon, string description) + { + Validate7OrLater(); + if (parent == null) + throw new ArgumentNullException(nameof(parent)); + taskbar4?.SetOverlayIcon(parent.Handle, icon == null ? IntPtr.Zero : icon.Handle, description); + } + + // Progress Bars ============================================ + + public static void SetProgressState(IWin32Window parent, TBPF status) + { + Validate7OrLater(); + if (parent == null) + throw new ArgumentNullException(nameof(parent)); + taskbar4?.SetProgressState(parent.Handle, status); + } + + public static void SetProgressValue(IWin32Window parent, ulong completed, ulong total) + { + Validate7OrLater(); + if (parent == null) + throw new ArgumentNullException(nameof(parent)); + taskbar4?.SetProgressValue(parent.Handle, completed, total); + } + + // Thumbnails ============================================ + + public static void RegisterTab(IWin32Window parent, IWin32Window childWindow) + { + Validate7OrLater(); + if (childWindow == null) + throw new ArgumentNullException(nameof(childWindow)); + if (parent == null) + throw new ArgumentNullException(nameof(parent)); + taskbar4?.RegisterTab(childWindow.Handle, parent.Handle); + } + + public static void SetTabActive(IWin32Window parent, IWin32Window childWindow) + { + Validate7OrLater(); + if (childWindow == null) + throw new ArgumentNullException(nameof(childWindow)); + if (parent == null) + throw new ArgumentNullException(nameof(parent)); + taskbar4?.SetTabActive(childWindow.Handle, parent.Handle, 0); + } + + public static void SetTabOrder(IWin32Window childWindow, IWin32Window insertBeforeChildWindow = null) + { + Validate7OrLater(); + if (childWindow == null) + throw new ArgumentNullException(nameof(childWindow)); + taskbar4?.SetTabOrder(childWindow.Handle, insertBeforeChildWindow == null ? IntPtr.Zero : insertBeforeChildWindow.Handle); + } + + public static void SetTabProperties(IWin32Window childWindow, STPF properties) + { + Validate7OrLater(); + if (childWindow == null) + throw new ArgumentNullException(nameof(childWindow)); + taskbar4?.SetTabProperties(childWindow.Handle, properties); + } + + public static void UnregisterTab(IWin32Window childWindow) + { + Validate7OrLater(); + if (childWindow == null) + throw new ArgumentNullException(nameof(childWindow)); + taskbar4?.UnregisterTab(childWindow.Handle); + } + + public static void SetThumbnailClip(IWin32Window parent, Rectangle windowClipRect) + { + Validate7OrLater(); + if (parent == null) + throw new ArgumentNullException(nameof(parent)); + RECT cr = windowClipRect; + taskbar4?.SetThumbnailClip(parent.Handle, ref cr); + } + + public static void SetThumbnailTooltip(IWin32Window parent, string tip) + { + Validate7OrLater(); + if (parent == null) + throw new ArgumentNullException(nameof(parent)); + taskbar4?.SetThumbnailTooltip(parent.Handle, tip); + } + + static readonly Version Win7Ver = new Version(6, 1); + + private static void Validate7OrLater() + { + if (Environment.OSVersion.Version < Win7Ver) + throw new InvalidOperationException("This method is only available on Windows 7 and later."); + } + } + } +} diff --git a/AeroWizard/AeroWizard/Native/USER32.cs b/AeroWizard/AeroWizard/Native/USER32.cs new file mode 100644 index 0000000..9b0a0c3 --- /dev/null +++ b/AeroWizard/AeroWizard/Native/USER32.cs @@ -0,0 +1,344 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +namespace Vanara.Interop +{ + internal static partial class NativeMethods + { + internal const string USER32 = "user32.dll"; + + [Flags] + public enum LoadImageOptions : uint + { + DEFAULTCOLOR = 0x00000000, + MONOCHROME = 0x00000001, + COLOR = 0x00000002, + COPYRETURNORG = 0x00000004, + COPYDELETEORG = 0x00000008, + LOADFROMFILE = 0x00000010, + LOADTRANSPARENT = 0x00000020, + DEFAULTSIZE = 0x00000040, + VGACOLOR = 0x00000080, + LOADMAP3DCOLORS = 0x00001000, + CREATEDIBSECTION = 0x00002000, + COPYFROMRESOURCE = 0x00004000, + SHARED = 0x00008000 + } + + public enum LoadImageType : uint + { + /// Loads a bitmap. + BITMAP = 0, + + /// Loads an icon. + ICON = 1, + + /// Loads a cursor. + CURSOR = 2, + + /// Loads an enhanced metafile. + IMAGE_ENHMETAFILE = 3 + } + + /// Window sizing and positioning flags. + [Flags] + public enum SetWindowPosFlags : uint + { + /// + /// If the calling thread and the thread that owns the window are attached to different input queues, the + /// system posts the request to the thread that owns the window. This prevents the calling thread from + /// blocking its execution while other threads process the request. + /// + /// SWP_ASYNCWINDOWPOS + AsynchronousWindowPosition = 0x4000, + + /// Prevents generation of the WM_SYNCPAINT message. + /// SWP_DEFERERASE + DeferErase = 0x2000, + + /// Draws a frame (defined in the window's class description) around the window. + /// SWP_DRAWFRAME + DrawFrame = 0x0020, + + /// + /// Applies new frame styles set using the SetWindowLong function. Sends a WM_NCCALCSIZE message to the + /// window, even if the window's size is not being changed. If this flag is not specified, WM_NCCALCSIZE is + /// sent only when the window's size is being changed. + /// + /// SWP_FRAMECHANGED + FrameChanged = 0x0020, + + /// Hides the window. + /// SWP_HIDEWINDOW + HideWindow = 0x0080, + + /// + /// Does not activate the window. If this flag is not set, the window is activated and moved to the top of + /// either the topmost or non-topmost group (depending on the setting of the hWndInsertAfter parameter). + /// + /// SWP_NOACTIVATE + DoNotActivate = 0x0010, + + /// + /// Discards the entire contents of the client area. If this flag is not specified, the valid contents of the + /// client area are saved and copied back into the client area after the window is sized or repositioned. + /// + /// SWP_NOCOPYBITS + DoNotCopyBits = 0x0100, + + /// Retains the current position (ignores X and Y parameters). + /// SWP_NOMOVE + IgnoreMove = 0x0002, + + /// Does not change the owner window's position in the Z order. + /// SWP_NOOWNERZORDER + DoNotChangeOwnerZOrder = 0x0200, + + /// + /// Does not redraw changes. If this flag is set, no repainting of any kind occurs. This applies to the + /// client area, the nonclient area (including the title bar and scroll bars), and any part of the parent + /// window uncovered as a result of the window being moved. When this flag is set, the application must + /// explicitly invalidate or redraw any parts of the window and parent window that need redrawing. + /// + /// SWP_NOREDRAW + DoNotRedraw = 0x0008, + + /// Same as the SWP_NOOWNERZORDER flag. + /// SWP_NOREPOSITION + DoNotReposition = 0x0200, + + /// Prevents the window from receiving the WM_WINDOWPOSCHANGING message. + /// SWP_NOSENDCHANGING + DoNotSendChangingEvent = 0x0400, + + /// Retains the current size (ignores the cx and cy parameters). + /// SWP_NOSIZE + IgnoreResize = 0x0001, + + /// Retains the current Z order (ignores the hWndInsertAfter parameter). + /// SWP_NOZORDER + IgnoreZOrder = 0x0004, + + /// Displays the window. + /// SWP_SHOWWINDOW + ShowWindow = 0x0040, + } + + /// Flags used for and methods to retrieve information about a window. + [Flags] + public enum WindowLongFlags : int + { + /// The extended window styles + /// GWL_EXSTYLE + ExtendedWindowStyles = -20, + /// The application instance handle + /// GWL_HINSTANCE + InstanceHandle = -6, + /// The parent window handle + /// GWL_HWNDPARENT + ParentWindowHandle = -8, + /// The window identifier + /// GWL_ID + WindowIdentifier = -12, + /// The window styles + /// GWL_STYLE + WindowStyles = -16, + /// The window user data + /// GWL_USERDATA + WindowUserData = -21, + /// The window procedure address or handle + /// GWL_WNDPROC + WindowProcedure = -4, + /// The dialog user data + /// DWLP_USER + DialogUserData = 0x8, + /// The dialog procedure message result + /// DWLP_MSGRESULT + DialogMessageResult = 0x0, + /// The dialog procedure address or handle + /// DWLP_DLGPROC + DialogProcedure = 0x4 + } + + [DllImport(USER32, CharSet = CharSet.Auto, ExactSpelling = true)] + [System.Security.SecurityCritical] + public static extern IntPtr ChildWindowFromPointEx(IntPtr hwndParent, ref System.Drawing.Point pt, System.Windows.Forms.GetChildAtPointSkip uFlags); + + [DllImport(USER32, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + [System.Security.SecurityCritical] + public static extern bool DestroyIcon(IntPtr hIcon); + + [DllImport(USER32, CharSet = CharSet.Unicode)] + public static extern int DrawText(IntPtr hDC, string lpString, int nCount, ref RECT lpRect, uint uFormat); + + [DllImport(USER32, ExactSpelling = true)] + [System.Security.SecurityCritical] + public static extern IntPtr GetActiveWindow(); + + [DllImport(USER32, CharSet = CharSet.Auto, ExactSpelling = true, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + [System.Security.SecurityCritical] + public static extern bool GetClientRect(IntPtr hWnd, [In, Out] ref NativeMethods.RECT rect); + + public static IntPtr GetWindowLong(IntPtr hWnd, int nIndex) + { + IntPtr ret = IntPtr.Zero; + if (IntPtr.Size == 4) + ret = (IntPtr)GetWindowLong32(hWnd, nIndex); + else + ret = GetWindowLongPtr(hWnd, nIndex); + if (ret == IntPtr.Zero) + throw new System.ComponentModel.Win32Exception(); + return ret; + } + + [DllImport(USER32, EntryPoint = "GetWindowLong", SetLastError = true)] + [SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", MessageId = "return", Justification = "This declaration is not used on 64-bit Windows.")] + [SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", MessageId = "2", Justification = "This declaration is not used on 64-bit Windows.")] + [System.Security.SecurityCritical] + public static extern int GetWindowLong32(IntPtr hWnd, int nIndex); + + [DllImport(USER32, EntryPoint = "GetWindowLongPtr", SetLastError = true)] + [SuppressMessage("Microsoft.Interoperability", "CA1400:PInvokeEntryPointsShouldExist", Justification = "Entry point does exist on 64-bit Windows.")] + [System.Security.SecurityCritical] + public static extern IntPtr GetWindowLongPtr(IntPtr hWnd, int nIndex); + + [DllImport(USER32, ExactSpelling = true, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + [System.Security.SecurityCritical] + public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); + + [DllImport(USER32, ExactSpelling = true)] + [return: MarshalAs(UnmanagedType.Bool)] + [System.Security.SecurityCritical] + public static extern bool InvalidateRect(IntPtr hWnd, [In] ref NativeMethods.RECT rect, [MarshalAs(UnmanagedType.Bool)] bool bErase); + + [DllImport(USER32, ExactSpelling = true)] + [return: MarshalAs(UnmanagedType.Bool)] + [System.Security.SecurityCritical] + public static extern bool InvalidateRect(IntPtr hWnd, IntPtr rect, [MarshalAs(UnmanagedType.Bool)] bool bErase); + + [DllImport(USER32, ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)] + [System.Security.SecurityCritical] + public static extern IntPtr LoadImage(IntPtr hinst, string lpszName, LoadImageType uType, int cxDesired, int cyDesired, LoadImageOptions fuLoad); + + [DllImport(USER32, ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)] + [System.Security.SecurityCritical] + public static extern IntPtr LoadImage(IntPtr hinst, [MarshalAs(UnmanagedType.SysInt)] int uID, LoadImageType uType, int cxDesired, int cyDesired, LoadImageOptions fuLoad); + + [DllImport(USER32, ExactSpelling = true, CharSet = CharSet.Auto)] + [System.Security.SecurityCritical] + public static extern int LoadString(IntPtr hInstance, uint uID, System.Text.StringBuilder lpBuffer, int nBufferMax); + + [DllImport(USER32, SetLastError = true, CharSet = CharSet.Auto)] + [System.Security.SecurityCritical] + public static extern uint RegisterWindowMessage(string lpString); + + [DllImport(USER32, CharSet = CharSet.Auto, ExactSpelling = true)] + [return: MarshalAs(UnmanagedType.Bool)] + [System.Security.SecurityCritical] + public static extern bool ScreenToClient(IntPtr hWnd, [In, Out] ref System.Drawing.Point lpPoint); + + [DllImport(USER32, CharSet = CharSet.Unicode, SetLastError = false)] + [System.Security.SecurityCritical] + public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam); + + public static IntPtr SendMessage(IntPtr hWnd, uint msg, int wParam = 0, int lParam = 0) => SendMessage(hWnd, msg, (IntPtr)wParam, (IntPtr)lParam); + + public static IntPtr SendMessage(IntPtr hWnd, uint msg, int wParam, string lParam) => SendMessage(hWnd, msg, (IntPtr)wParam, lParam); + + [DllImport(USER32, SetLastError = false)] + [System.Security.SecurityCritical] + public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, ref RECT rect); + + [DllImport(USER32, SetLastError = false)] + [System.Security.SecurityCritical] + public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, [In, MarshalAs(UnmanagedType.LPWStr)] string lParam); + + [DllImport(USER32, CharSet = CharSet.Auto, SetLastError = false)] + [System.Security.SecurityCritical] + public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, ref int wParam, [In, Out] System.Text.StringBuilder lParam); + + public static IntPtr SetWindowLong(IntPtr hWnd, Int32 nIndex, IntPtr dwNewLong) + { + IntPtr ret = IntPtr.Zero; + if (IntPtr.Size == 4) + ret = SetWindowLongPtr32(hWnd, nIndex, dwNewLong); + else + ret = SetWindowLongPtr64(hWnd, nIndex, dwNewLong); + if (ret == IntPtr.Zero) + throw new System.ComponentModel.Win32Exception(); + return ret; + } + + [DllImport(USER32, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + [System.Security.SecurityCritical] + public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, SetWindowPosFlags uFlags); + + [DllImport(USER32, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + [System.Security.SecurityCritical] + public static extern bool SetWindowText(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)] string lpString); + + [DllImport(USER32, SetLastError = true, EntryPoint = "SetWindowLong")] + [SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", MessageId = "return", Justification = "This declaration is not used on 64-bit Windows.")] + [SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", MessageId = "2", Justification = "This declaration is not used on 64-bit Windows.")] + private static extern IntPtr SetWindowLongPtr32(IntPtr hWnd, Int32 nIndex, IntPtr dwNewLong); + + [DllImport(USER32, SetLastError = true, EntryPoint = "SetWindowLongPtr")] + [SuppressMessage("Microsoft.Interoperability", "CA1400:PInvokeEntryPointsShouldExist", Justification = "Entry point does exist on 64-bit Windows.")] + private static extern IntPtr SetWindowLongPtr64(IntPtr hWnd, Int32 nIndex, IntPtr dwNewLong); + + [StructLayout(LayoutKind.Sequential)] + public struct NMHDR + { + public IntPtr hwndFrom; + public IntPtr idFrom; + public int code; + } + + [StructLayout(LayoutKind.Sequential)] + public struct WINDOWPOS + { + public IntPtr hwnd; + public IntPtr hwndInsertAfter; + public int x; + public int y; + public int cx; + public int cy; + public int flags; + } + + /// Special window handles + public static class SpecialWindowHandles + { + /// + /// Places the window at the bottom of the Z order. If the hWnd parameter identifies a topmost window, the + /// window loses its topmost status and is placed at the bottom of all other windows. + /// + /// HWND_BOTTOM + public static IntPtr HwndBottom = new IntPtr(1); + + /// + /// Places the window above all non-topmost windows (that is, behind all topmost windows). This flag has no + /// effect if the window is already a non-topmost window. + /// + /// HWND_NOTOPMOST + public static IntPtr HwndNoTopMost = new IntPtr(-2); + + /// Places the window at the top of the Z order. + /// HWND_TOP + public static IntPtr HwndTop = new IntPtr(0); + + /// + /// Places the window above all non-topmost windows. The window maintains its topmost position even when it + /// is deactivated. + /// + /// HWND_TOPMOST + public static IntPtr HwndTopMost = new IntPtr(-1); + } + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/Native/UXTHEME.cs b/AeroWizard/AeroWizard/Native/UXTHEME.cs new file mode 100644 index 0000000..ef43dff --- /dev/null +++ b/AeroWizard/AeroWizard/Native/UXTHEME.cs @@ -0,0 +1,602 @@ +// Requires Gdi\RECT.cs +using System; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Windows.Forms; +using System.Windows.Forms.VisualStyles; + +namespace Vanara.Interop +{ + internal static partial class NativeMethods + { + private const string UXTHEME = "uxtheme.dll"; + + public class SafeThemeHandle : SafeHandle + { + public SafeThemeHandle(IntPtr hTheme, bool ownsHandle = true) : base(IntPtr.Zero, ownsHandle) { SetHandle(hTheme); } + protected override bool ReleaseHandle() => CloseThemeData(handle) == 0; + public override bool IsInvalid => handle == IntPtr.Zero; + public static implicit operator SafeThemeHandle(VisualStyleRenderer r) => new SafeThemeHandle(r.Handle, false); + } + + [UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet = CharSet.Unicode)] + public delegate int DrawThemeTextCallback(SafeDCHandle hdc, string text, int textLen, ref RECT rc, int flags, IntPtr lParam); + + [Flags] + public enum DrawThemeParentBackgroundFlags + { + None = 0, + /// If set, hdc is assumed to be a window DC, not a client DC. + /// DTPB_WINDOWDC + WindowDC = 1, + /// If set, this function sends a WM_CTLCOLORSTATIC message to the parent and uses the brush if one is provided. Otherwise, it uses COLOR_BTNFACE. + /// DTPB_USECTLCOLORSTATIC + UseCtlColorStaticMsg = 2, + /// If set, this function returns S_OK without sending a WM_CTLCOLORSTATIC message if the parent actually painted on WM_ERASEBKGND. + /// DTPB_USEERASEBKGND + UseEraseBkgndMsg = 4 + } + + public enum DrawThemeTextSystemFonts + { + Caption = 801, + SmallCaption = 802, + Menu = 803, + Status = 804, + MessageBox = 805, + IconTitle = 806 + } + + public enum IntegerListProperty + { + TransitionDuration = 6000 + } + + public enum OpenThemeDataOptions + { + None = 0, + /// Forces drawn images from this theme to stretch to fit the rectangles specified by drawing functions. + /// OTD_FORCE_RECT_SIZING + ForceRectSizing = 1, + /// Allows theme elements to be drawn in the non-client area of the window. + /// OTD_NONCLIENT + NonClient = 2 + } + + public enum ThemePropertyOrigin + { + /// Property was found in the state section. + /// PO_STATE + State = 0, + /// Property was found in the part section. + /// PO_PART + Part = 1, + /// Property was found in the class section. + /// PO_CLASS + Class = 2, + /// Property was found in the list of global variables. + /// PO_GLOBAL + Global = 3, + /// Property was not found. + /// PO_NOTFOUND + NotFound = 4 + } + + public enum TextShadowType + { + /// No shadow will be drawn. + /// TST_NONE + None = 0, + /// The shadow will be drawn to appear detailed underneath text. + /// TST_SINGLE + Single = 1, + /// The shadow will be drawn to appear blurred underneath text. + /// TST_CONTINUOUS + Continuous = 2 + } + + public enum ThemeSize + { + /// Receives the minimum size of a visual style part. + /// TS_MIN + Min, + /// Receives the size of the visual style part that will best fit the available space. + /// TS_TRUE + True, + /// Receives the size that the theme manager uses to draw a part. + /// TS_DRAW + Draw + } + + [Flags] + public enum WindowThemeNonClientAttributes : int + { + /// Do Not Draw The Caption (Text) + NoDrawCaption = 0x00000001, + /// Do Not Draw the Icon + NoDrawIcon = 0x00000002, + /// Do Not Show the System Menu + NoSysMenu = 0x00000004, + /// Do Not Mirror the Question mark Symbol + NoMirrorHelp = 0x00000008 + } + + [Flags] + private enum DrawThemeTextOptionsMasks + { + TextColor = 1, + BorderColor = 2, + ShadowColor = 4, + ShadowType = 8, + ShadowOffset = 16, + BorderSize = 32, + FontProp = 64, + ColorProp = 128, + StateId = 256, + CalcRect = 512, + ApplyOverlay = 1024, + GlowSize = 2048, + Callback = 4096, + Composited = 8192 + } + + private enum WindowThemeAttributeType + { + NonClient = 1, + } + + [DllImport(UXTHEME, ExactSpelling = true)] + public static extern int CloseThemeData(IntPtr hTheme); + + [DllImport(UXTHEME, ExactSpelling = true)] + [System.Security.SecurityCritical] + public static extern int DrawThemeBackground(SafeThemeHandle hTheme, SafeDCHandle hdc, int iPartId, int iStateId, ref RECT pRect, PRECT pClipRect); + + [DllImport(UXTHEME, ExactSpelling = true)] + [System.Security.SecurityCritical] + public static extern int DrawThemeBackgroundEx(SafeThemeHandle hTheme, SafeDCHandle hdc, int iPartId, int iStateId, ref RECT pRect, DrawThemeBackgroundOptions opts); + + [DllImport(UXTHEME, ExactSpelling = true)] + [System.Security.SecurityCritical] + public static extern int DrawThemeIcon(SafeThemeHandle hTheme, SafeDCHandle hdc, int iPartId, int iStateId, ref RECT pRect, IntPtr himl, int iImageIndex); + + [DllImport(UXTHEME, ExactSpelling = true)] + [System.Security.SecurityCritical] + public static extern int DrawThemeParentBackground(IntPtr hwnd, SafeDCHandle hdc, PRECT pRect); + + [DllImport(UXTHEME, ExactSpelling = true)] + [System.Security.SecurityCritical] + public static extern int DrawThemeParentBackgroundEx(IntPtr hwnd, SafeDCHandle hdc, DrawThemeParentBackgroundFlags dwFlags, PRECT pRect); + + [DllImport(UXTHEME, ExactSpelling = true, CharSet = CharSet.Unicode)] + public static extern int DrawThemeText(SafeThemeHandle hTheme, SafeDCHandle hdc, int iPartId, int iStateId, string text, int textLength, TextFormatFlags textFlags, int textFlags2, ref RECT pRect); + + [DllImport(UXTHEME, ExactSpelling = true, CharSet = CharSet.Unicode)] + [System.Security.SecurityCritical] + public static extern int DrawThemeTextEx(SafeThemeHandle hTheme, SafeDCHandle hdc, int iPartId, int iStateId, string text, int iCharCount, TextFormatFlags dwFlags, ref RECT pRect, ref DrawThemeTextOptions pOptions); + + [DllImport(UXTHEME, ExactSpelling = true)] + public static extern int GetThemeBackgroundContentRect(SafeThemeHandle hTheme, SafeDCHandle hdc, int iPartId, int iStateId, ref RECT pBoundingRect, out RECT pContentRect); + + [DllImport(UXTHEME, ExactSpelling = true)] + public static extern int GetThemeBitmap(SafeThemeHandle hTheme, SafeDCHandle hdc, int iPartId, int iStateId, int iPropId, int dwFlags, out IntPtr phBitmap); + + [DllImport(UXTHEME, ExactSpelling = true)] + public static extern int GetThemeBool(SafeThemeHandle hTheme, int iPartId, int iStateId, int iPropId, [MarshalAs(UnmanagedType.Bool)] out bool pfVal); + + [DllImport(UXTHEME, ExactSpelling = true)] + public static extern int GetThemeColor(SafeThemeHandle hTheme, int iPartId, int iStateId, int iPropId, out int pColor); + + [DllImport(UXTHEME, ExactSpelling = true)] + public static extern int GetThemeEnumValue(SafeThemeHandle hTheme, int iPartId, int iStateId, int iPropId, out int piVal); + + [DllImport(UXTHEME, ExactSpelling = true, CharSet = CharSet.Unicode)] + public static extern int GetThemeFilename(SafeThemeHandle hTheme, int iPartId, int iStateId, int iPropId, ref System.Text.StringBuilder pszBuff, int buffLength); + + [DllImport(UXTHEME, ExactSpelling = true)] + public static extern int GetThemeInt(SafeThemeHandle hTheme, int iPartId, int iStateId, int iPropId, out int piVal); + + public static int[] GetThemeIntList(SafeThemeHandle hTheme, int partId, int stateId, int propId) + { + if (Environment.OSVersion.Version.Major < 6) + { + INTLIST_OLD l; + if (0 != GetThemeIntListPreVista(hTheme, partId, stateId, propId, out l)) + return null; + var outlist = new int[l.iValueCount]; + Array.Copy(l.iValues, outlist, l.iValueCount); + return outlist; + } + else + { + INTLIST l; + if (0 != GetThemeIntList(hTheme, partId, stateId, propId, out l)) + return null; + var outlist = new int[l.iValueCount]; + Array.Copy(l.iValues, outlist, l.iValueCount); + return outlist; + } + } + + [DllImport(UXTHEME, ExactSpelling = true)] + [System.Security.SecurityCritical] + public static extern int GetThemeMargins(SafeThemeHandle hTheme, SafeDCHandle hdc, int iPartId, int iStateId, int iPropId, IntPtr prc, out RECT pMargins); + + [DllImport(UXTHEME, ExactSpelling = true)] + public static extern int GetThemeMetric(SafeThemeHandle hTheme, SafeDCHandle hdc, int iPartId, int iStateId, int iPropId, out int piVal); + + [DllImport(UXTHEME, ExactSpelling = true)] + public static extern int GetThemePartSize(SafeThemeHandle hTheme, SafeDCHandle hdc, int part, int state, PRECT pRect, ThemeSize eSize, out Size size); + + [DllImport(UXTHEME, ExactSpelling = true)] + public static extern int GetThemePosition(SafeThemeHandle hTheme, int iPartId, int iStateId, int iPropId, out Point piVal); + + [DllImport(UXTHEME, ExactSpelling = true)] + public static extern int GetThemePropertyOrigin(SafeThemeHandle hTheme, int iPartId, int iStateId, int iPropId, out ThemePropertyOrigin pOrigin); + + [DllImport(UXTHEME, ExactSpelling = true)] + public static extern int GetThemeRect(SafeThemeHandle hTheme, int iPartId, int iStateId, int iPropId, out RECT pRect); + + [DllImport(UXTHEME, ExactSpelling = true)] + public static extern int GetThemeStream(SafeThemeHandle hTheme, int iPartId, int iStateId, int iPropId, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 5)] out byte[] pvStream, out int cbStream, IntPtr hInst); + + [DllImport(UXTHEME, ExactSpelling = true, CharSet = CharSet.Unicode)] + public static extern int GetThemeString(SafeThemeHandle hTheme, int iPartId, int iStateId, int iPropId, ref System.Text.StringBuilder themeString, int themeStringLength); + + [DllImport(UXTHEME, ExactSpelling = true)] + public static extern int GetThemeSysInt(SafeThemeHandle hTheme, int iIntID, out int piVal); + + [DllImport(UXTHEME, ExactSpelling = true, CharSet = CharSet.Unicode)] + public static extern int GetThemeTextExtent(SafeThemeHandle hTheme, SafeDCHandle hdc, int iPartId, int iStateId, string text, int textLength, TextFormatFlags textFlags, ref RECT boundingRect, out RECT extentRect); + + [DllImport(UXTHEME, ExactSpelling = true)] + [System.Security.SecurityCritical] + public static extern int GetThemeTransitionDuration(SafeThemeHandle hTheme, int iPartId, int iStateIdFrom, int iStateIdTo, int iPropId, out int pdwDuration); + + [DllImport(UXTHEME, ExactSpelling = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool IsThemeBackgroundPartiallyTransparent(SafeThemeHandle hTheme, int iPartId, int iStateId); + + [DllImport(UXTHEME, ExactSpelling = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool IsThemePartDefined(SafeThemeHandle hTheme, int iPartId, int iStateId); + + [DllImport(UXTHEME, ExactSpelling = true, CharSet = CharSet.Unicode)] + public static extern IntPtr OpenThemeData(IntPtr hWnd, string classList); + + [DllImport(UXTHEME, ExactSpelling = true, CharSet = CharSet.Unicode)] + public static extern IntPtr OpenThemeDataEx(IntPtr hWnd, string classList, OpenThemeDataOptions dwFlags); + + [DllImport(UXTHEME, ExactSpelling = true, CharSet = CharSet.Unicode)] + [System.Security.SecurityCritical] + public static extern int SetWindowTheme(IntPtr hWnd, string pszSubAppName, string pszSubIdList); + + public static int SetWindowThemeAttribute(IWin32Window wnd, WindowThemeNonClientAttributes ncAttrs, int ncAttrMasks = int.MaxValue) + { + var opt = new WTA_OPTIONS {Flags = ncAttrs, Mask = ncAttrMasks == int.MaxValue ? (int)ncAttrs : ncAttrMasks}; + return SetWindowThemeAttribute(wnd?.Handle ?? IntPtr.Zero, WindowThemeAttributeType.NonClient, ref opt, Marshal.SizeOf(opt)); + } + + [DllImport(UXTHEME, ExactSpelling = true)] + private static extern int GetThemeIntList(SafeThemeHandle hTheme, int iPartId, int iStateId, int iPropId, out INTLIST pIntList); + + [DllImport(UXTHEME, ExactSpelling = true, EntryPoint = "GetThemeIntList")] + private static extern int GetThemeIntListPreVista(SafeThemeHandle hTheme, int iPartId, int iStateId, int iPropId, out INTLIST_OLD pIntList); + + [DllImport(UXTHEME, ExactSpelling = true)] + [System.Security.SecurityCritical] + private static extern int SetWindowThemeAttribute(IntPtr hWnd, WindowThemeAttributeType wtype, ref WTA_OPTIONS attributes, int size); + + /// + /// Defines the options for the function. + /// + [StructLayout(LayoutKind.Sequential)] + public struct DrawThemeTextOptions + { + private int dwSize; + private DrawThemeTextOptionsMasks dwMasks; + private int crText; + private int crBorder; + private int crShadow; + private TextShadowType iTextShadowType; + private Point ptShadowOffset; + private int iBorderSize; + private int iFontPropId; + private int iColorPropId; + private int iStateId; + [MarshalAs(UnmanagedType.Bool)] + private bool fApplyOverlay; + private int iGlowSize; + [MarshalAs(UnmanagedType.FunctionPtr)] + private DrawThemeTextCallback pfnDrawTextCallback; + private IntPtr lParam; + + /// Initializes a new instance of the struct. + /// This value must be specified to initialize. + public DrawThemeTextOptions(bool init) : this() + { + dwSize = Marshal.SizeOf(typeof(DrawThemeTextOptions)); + } + + /// Gets or sets a value that specifies an alternate color property to use when drawing text. + /// The alternate color of the text. + public Color AlternateColor + { + get { return ColorTranslator.FromWin32(iColorPropId); } + set + { + iColorPropId = ColorTranslator.ToWin32(value); + dwMasks |= DrawThemeTextOptionsMasks.ColorProp; + } + } + + /// Gets or sets an alternate font property to use when drawing text. + /// The alternate font. + public DrawThemeTextSystemFonts AlternateFont + { + get { return (DrawThemeTextSystemFonts)iFontPropId; } + set + { + iFontPropId = (int)value; + dwMasks |= DrawThemeTextOptionsMasks.FontProp; + } + } + + /// + /// Gets or sets a value indicating whether to draw text with antialiased alpha. Use of this flag requires a + /// top-down DIB section. This flag works only if the HDC passed to function DrawThemeTextEx has a top-down + /// DIB section currently selected in it. For more information, see Device-Independent Bitmaps. + /// + /// true if antialiased alpha; otherwise, false. + public bool AntiAliasedAlpha + { + get { return (dwMasks & DrawThemeTextOptionsMasks.Composited) == DrawThemeTextOptionsMasks.Composited; } + set { SetFlag(DrawThemeTextOptionsMasks.Composited, value); } + } + + /// + /// Gets or sets a value indicating whether text will be drawn on top of the shadow and outline effects + /// (true) or if just the shadow and outline effects will be drawn (false). + /// + /// true if drawn on top; otherwise, false. + public bool ApplyOverlay + { + get { return fApplyOverlay; } + set + { + fApplyOverlay = value; + dwMasks |= DrawThemeTextOptionsMasks.ApplyOverlay; + } + } + + /// Gets or sets the color of the outline that will be drawn around the text. + /// The color of the border. + public Color BorderColor + { + get { return ColorTranslator.FromWin32(crBorder); } + set + { + crBorder = ColorTranslator.ToWin32(value); + dwMasks |= DrawThemeTextOptionsMasks.BorderColor; + } + } + + /// Gets or sets the radius of the outline that will be drawn around the text. + /// The size of the border. + public int BorderSize + { + get { return iBorderSize; } + set + { + iBorderSize = value; + dwMasks |= DrawThemeTextOptionsMasks.BorderSize; + } + } + + /// Gets or sets the callback function. + /// The callback function. + public DrawThemeTextCallback Callback + { + get { return pfnDrawTextCallback; } + set + { + pfnDrawTextCallback = value; + dwMasks |= DrawThemeTextOptionsMasks.Callback; + } + } + + /// Gets or sets the size of a glow that will be drawn on the background prior to any text being drawn. + /// The size of the glow. + public int GlowSize + { + get { return iGlowSize; } + set + { + iGlowSize = value; + dwMasks |= DrawThemeTextOptionsMasks.GlowSize; + } + } + + /// Gets or sets the parameter for callback back function specified by . + /// The parameter. + public IntPtr LParam + { + get { return lParam; } + set { lParam = value; } + } + + /// + /// Gets or sets a value indicating whether the pRect parameter of the function + /// that uses this structure will be used as both an in and an out parameter. After the function returns, the + /// pRect parameter will contain the rectangle that corresponds to the region calculated to be drawn. + /// + /// true if returning the calculated rectangle; otherwise, false. + public bool ReturnCalculatedRectangle + { + get { return (dwMasks & DrawThemeTextOptionsMasks.CalcRect) == DrawThemeTextOptionsMasks.CalcRect; } + set { SetFlag(DrawThemeTextOptionsMasks.CalcRect, value); } + } + + /// Gets or sets the color of the shadow drawn behind the text. + /// The color of the shadow. + public Color ShadowColor + { + get { return ColorTranslator.FromWin32(crShadow); } + set + { + crShadow = ColorTranslator.ToWin32(value); + dwMasks |= DrawThemeTextOptionsMasks.ShadowColor; + } + } + + /// Gets or sets the amount of offset, in logical coordinates, between the shadow and the text. + /// The shadow offset. + public Point ShadowOffset + { + get { return new Point(ptShadowOffset.X, ptShadowOffset.Y); } + set + { + ptShadowOffset = value; + dwMasks |= DrawThemeTextOptionsMasks.ShadowOffset; + } + } + + /// Gets or sets the type of the shadow that will be drawn behind the text. + /// The type of the shadow. + public TextShadowType ShadowType + { + get { return iTextShadowType; } + set + { + iTextShadowType = value; + dwMasks |= DrawThemeTextOptionsMasks.ShadowType; + } + } + + /// Gets or sets the color of the text that will be drawn. + /// The color of the text. + public Color TextColor + { + get { return ColorTranslator.FromWin32(crText); } + set + { + crText = ColorTranslator.ToWin32(value); + dwMasks |= DrawThemeTextOptionsMasks.TextColor; + } + } + + /// Gets an instance with default values set. + public static DrawThemeTextOptions Default => new DrawThemeTextOptions(true); + + private void SetFlag(DrawThemeTextOptionsMasks f, bool value) { if (value) dwMasks |= f; else dwMasks &= ~f; } + } + + /// + /// The Options of What Attributes to Add/Remove + /// + [StructLayout(LayoutKind.Sequential)] + private struct WTA_OPTIONS + { + public WindowThemeNonClientAttributes Flags; + public int Mask; + } + + [StructLayout(LayoutKind.Sequential)] + private struct INTLIST + { + public int iValueCount; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 402)] + public int[] iValues; + } + + [StructLayout(LayoutKind.Sequential)] + private struct INTLIST_OLD + { + public int iValueCount; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] + public int[] iValues; + } + + /// + /// Defines the options for the DrawThemeBackgroundEx function. + /// + [StructLayout(LayoutKind.Sequential)] + public class DrawThemeBackgroundOptions + { + private int dwSize; + private DrawThemeBackgroundFlags dwFlags; + private RECT rcClip; + + [Flags] + private enum DrawThemeBackgroundFlags + { + None = 0, + /// The ClipRectangle value is defined. + ClipRect = 1, + /// Deprecated. Draw transparent and alpha images as solid. + DrawSolid = 2, + /// Do not draw the border of the part (currently this value is only supported for bgtype=borderfill). + OmitBorder = 4, + /// Do not draw the content area of the part (currently this value is only supported for bgtype=borderfill). + OmitContent = 8, + /// Deprecated. + ComputingRegion = 16, + /// Assume the hdc is mirrored and flip images as appropriate (currently this value is only supported for bgtype=imagefile). + HasMirroredDC = 32, + /// Do not mirror the output; even in right-to-left (RTL) layout. + DoNotMirror = 64 + } + + /// + /// Initializes a new instance of the class. + /// + /// The rectangle to which drawing is clipped. + public DrawThemeBackgroundOptions(Rectangle? clipRect) + { + dwSize = Marshal.SizeOf(this); + ClipRectangle = clipRect; + } + + /// Gets or sets the bounding rectangle of the clip region. + /// The clip rectangle. + public Rectangle? ClipRectangle + { + get + { + Rectangle r = rcClip; + return r.IsEmpty ? (Rectangle?)null : r; + } + set + { + rcClip = value ?? default(RECT); + SetFlag(DrawThemeBackgroundFlags.ClipRect, value.HasValue); + } + } + + /// Gets or sets a value indicating whether omit drawing the border. + /// true if omit border; otherwise, false. + public bool OmitBorder { get { return GetFlag(DrawThemeBackgroundFlags.OmitBorder); } set { SetFlag(DrawThemeBackgroundFlags.OmitBorder, value); } } + + /// Gets or sets a value indicating whether omit drawing the content area of the part. + /// true if omit content area of the part; otherwise, false. + public bool OmitContent { get { return GetFlag(DrawThemeBackgroundFlags.OmitContent); } set { SetFlag(DrawThemeBackgroundFlags.OmitContent, value); } } + + /// Gets or sets a value indicating the hdc is mirrored and flip images as appropriate. + /// true if mirrored; otherwise, false. + public bool HasMirroredDC { get { return GetFlag(DrawThemeBackgroundFlags.HasMirroredDC); } set { SetFlag(DrawThemeBackgroundFlags.HasMirroredDC, value); } } + + /// Gets or sets a value indicating whether to mirror the output; even in right-to-left (RTL) layout. + /// true if not mirroring; otherwise, false. + public bool DoNotMirror { get { return GetFlag(DrawThemeBackgroundFlags.DoNotMirror); } set { SetFlag(DrawThemeBackgroundFlags.DoNotMirror, value); } } + + /// Performs an implicit conversion from to . + /// The clipping rectangle. + /// The result of the conversion. + public static implicit operator DrawThemeBackgroundOptions(Rectangle clipRectangle) => new DrawThemeBackgroundOptions(clipRectangle); + + private bool GetFlag(DrawThemeBackgroundFlags f) => (dwFlags & f) == f; + + private void SetFlag(DrawThemeBackgroundFlags f, bool value) { if (value) dwFlags |= f; else dwFlags &= ~f; } + } + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/Native/UXTHEME_GDI.cs b/AeroWizard/AeroWizard/Native/UXTHEME_GDI.cs new file mode 100644 index 0000000..18d76cf --- /dev/null +++ b/AeroWizard/AeroWizard/Native/UXTHEME_GDI.cs @@ -0,0 +1,16 @@ +// Requires UXTHEME\UXTHEME.cs +// Requires Gdi\LOGFONT.cs +using System; +using System.Runtime.InteropServices; + +namespace Vanara.Interop +{ + internal static partial class NativeMethods + { + [DllImport(UXTHEME, ExactSpelling = true, CharSet = CharSet.Unicode)] + public static extern int GetThemeFont(SafeThemeHandle hTheme, SafeDCHandle hdc, int iPartId, int iStateId, int iPropId, out LOGFONT pFont); + + [DllImport(UXTHEME, ExactSpelling = true, CharSet = CharSet.Unicode)] + public static extern int GetThemeSysFont(SafeThemeHandle hTheme, int iFontId, out LOGFONT pFont); + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/Native/VisualStylesRendererExtension.cs b/AeroWizard/AeroWizard/Native/VisualStylesRendererExtension.cs new file mode 100644 index 0000000..322cec1 --- /dev/null +++ b/AeroWizard/AeroWizard/Native/VisualStylesRendererExtension.cs @@ -0,0 +1,42 @@ +using System.Drawing; +using Vanara.Interop; + +namespace System.Windows.Forms.VisualStyles +{ + internal static partial class VisualStyleRendererExtension + { + public static Padding GetMargins2(this VisualStyleRenderer rnd, IDeviceContext dc = null, MarginProperty prop = MarginProperty.ContentMargins) + { + NativeMethods.RECT rc; + using (var hdc = new NativeMethods.SafeDCHandle(dc)) + NativeMethods.GetThemeMargins(rnd, hdc, rnd.Part, rnd.State, (int)prop, IntPtr.Zero, out rc); + return new Padding(rc.Left, rc.Top, rc.Right, rc.Bottom); + } + + public static int GetTransitionDuration(this VisualStyleRenderer rnd, int toState, int fromState = 0) + { + int dwDuration; + NativeMethods.GetThemeTransitionDuration(rnd, rnd.Part, fromState == 0 ? rnd.State : fromState, toState, (int)NativeMethods.IntegerListProperty.TransitionDuration, out dwDuration); + return dwDuration; + } + + /// + /// Sets the state of the . + /// + /// The instance. + /// The state. + public static void SetState(this VisualStyleRenderer rnd, int state) { rnd.SetParameters(rnd.Class, rnd.Part, state); } + + /// + /// Sets attributes to control how visual styles are applied to a specified window. + /// + /// The window. + /// The attributes to apply or disable. + /// if set to true enable the attribute, otherwise disable it. + public static void SetWindowThemeAttribute(this IWin32Window window, NativeMethods.WindowThemeNonClientAttributes attr, bool enable = true) + { + try { NativeMethods.SetWindowThemeAttribute(window, attr, enable ? (int)attr : 0); } + catch (EntryPointNotFoundException) { } + } + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/Native/VisualStylesRendererExtensionGDI.cs b/AeroWizard/AeroWizard/Native/VisualStylesRendererExtensionGDI.cs new file mode 100644 index 0000000..0b57540 --- /dev/null +++ b/AeroWizard/AeroWizard/Native/VisualStylesRendererExtensionGDI.cs @@ -0,0 +1,263 @@ +using System.CodeDom; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using Vanara.Interop; +using static Vanara.Interop.NativeMethods; + +namespace System.Windows.Forms.VisualStyles +{ + internal static partial class VisualStyleRendererExtension + { + private static readonly Dictionary bmpCache = new Dictionary(); + + private delegate void DrawWrapperMethod(SafeDCHandle hdc); + + /// + /// Draws the background image of the current visual style element within the specified bounding rectangle and optionally clipped to the specified clipping rectangle. + /// + /// The instance. + /// The used to draw the background image. + /// A in which the background image is drawn. + /// A that defines a clipping rectangle for the drawing operation. + /// If set to true flip the image for right to left layout. + public static void DrawBackground(this VisualStyleRenderer rnd, Graphics dc, Rectangle bounds, Rectangle? clipRectangle = null, bool rightToLeft = false) + { + /*var h = rnd.GetHashCode(); + Bitmap bmp; + if (!bmpCache.TryGetValue(h, out bmp)) + bmpCache.Add(h, bmp = GraphicsExtension.GetTransparentBitmap(GetBackgroundBitmap(rnd, Color.White), GetBackgroundBitmap(rnd, Color.Black))); + if (rightToLeft) + bmp.RotateFlip(RotateFlipType.RotateNoneFlipX); + if (clipRectangle != null) dc.SetClip(clipRectangle.Value); + using (var attr = new ImageAttributes()) + { + dc.CompositingMode = CompositingMode.SourceOver; + dc.CompositingQuality = CompositingQuality.HighQuality; + dc.InterpolationMode = InterpolationMode.HighQualityBicubic; + dc.PixelOffsetMode = PixelOffsetMode.HighQuality; + attr.SetWrapMode(WrapMode.TileFlipXY); + dc.DrawImage(bmp, bounds, 0, 0, bmp.Width, bmp.Height, GraphicsUnit.Pixel, attr); + }*/ + rnd.DrawBackground(dc, bounds, clipRectangle ?? bounds); + } + + /// + /// Draws the background image of the current visual style element onto a glass background within the specified bounding rectangle and optionally clipped to the specified clipping rectangle. + /// + /// The instance. + /// The used to draw the background image. + /// A in which the background image is drawn. + /// A that defines a clipping rectangle for the drawing operation. + /// If set to true flip the image for right to left layout. + public static void DrawGlassBackground(this VisualStyleRenderer rnd, IDeviceContext dc, Rectangle bounds, Rectangle? clipRectangle = null, bool rightToLeft = false) + { + DrawWrapper(dc, bounds, + memoryHdc => + { + var rBounds = new RECT(bounds); + //var opts = new DrawThemeBackgroundOptions(clipRectangle); + // Draw background + var oldLayout = DCLayout.GDI_ERROR; + if (rightToLeft) + if ((oldLayout = SetLayout(memoryHdc, DCLayout.LAYOUT_RTL)) == DCLayout.GDI_ERROR) + throw new NotSupportedException("Unable to change graphics layout to RTL."); + DrawThemeBackground(rnd, memoryHdc, rnd.Part, rnd.State, ref rBounds, clipRectangle); + if (oldLayout != DCLayout.GDI_ERROR) + SetLayout(memoryHdc, oldLayout); + } + ); + } + + /// + /// Draws the image from the specified within the specified bounds on a glass background. + /// + /// The instance. + /// The used to draw the image. + /// A in which the image is drawn. + /// An that contains the to draw. + /// The index of the within to draw. + public static void DrawGlassImage(this VisualStyleRenderer rnd, Graphics g, Rectangle bounds, ImageList imageList, int imageIndex) + { + DrawWrapper(g, bounds, + memoryHdc => + { + var rBounds = new RECT(bounds); + DrawThemeIcon(rnd, memoryHdc, rnd.Part, rnd.State, ref rBounds, imageList.Handle, imageIndex); + } + ); + } + + /// + /// Draws the specified image within the specified bounds on a glass background. + /// + /// The instance. + /// The used to draw the image. + /// A in which the image is drawn. + /// An that contains the to draw. + /// if set to true draws the image in a disabled state using the method. + public static void DrawGlassImage(this VisualStyleRenderer rnd, Graphics g, Rectangle bounds, Image image, bool disabled = false) + { + DrawWrapper(g, bounds, + memoryHdc => + { + using (var mg = Graphics.FromHdc(memoryHdc.DangerousGetHandle())) + { + if (disabled) + ControlPaint.DrawImageDisabled(mg, image, bounds.X, bounds.Y, Color.Transparent); + else + mg.DrawImage(image, bounds); + } + } + ); + } + + /// + /// Draws glowing text in the specified bounding rectangle with the option of overriding text color and applying other text formatting. + /// + /// The instance. + /// The used to draw the text. + /// A in which the text is drawn. + /// The text to draw. + /// Optional font override. + /// Optionally, the color to draw text in overriding the default color for the theme. + /// A bitwise combination of the values. + /// The size of the glow. + public static void DrawGlowingText(this VisualStyleRenderer rnd, IDeviceContext dc, Rectangle bounds, string text, Font font, Color? color, TextFormatFlags flags = TextFormatFlags.Default, int glowSize = 10) + { + DrawWrapper(dc, bounds, + memoryHdc => + { + // Create and select font + using (var fontHandle = new SafeDCObjectHandle(memoryHdc, font?.ToHfont() ?? IntPtr.Zero)) + { + // Draw glowing text + var dttOpts = new DrawThemeTextOptions(true) {GlowSize = glowSize, AntiAliasedAlpha = true}; + if (color != null) dttOpts.TextColor = color.Value; + var textBounds = new RECT(4, 0, bounds.Right - bounds.Left, bounds.Bottom - bounds.Top); + DrawThemeTextEx(rnd, memoryHdc, rnd.Part, rnd.State, text, text.Length, flags, ref textBounds, ref dttOpts); + } + } + ); + } + + /// + /// Draws text in the specified bounding rectangle with the option of applying other text formatting. + /// + /// The instance. + /// The used to draw the text. + /// A in which the text is drawn. + /// The text to draw. + /// A bitwise combination of the values. + /// The . + public static void DrawText(this VisualStyleRenderer rnd, IDeviceContext dc, ref Rectangle bounds, string text, TextFormatFlags flags, ref DrawThemeTextOptions options) + { + var rc = new RECT(bounds); + using (var hdc = new SafeDCHandle(dc)) + DrawThemeTextEx(rnd, hdc, rnd.Part, rnd.State, text, text.Length, flags, ref rc, ref options); + bounds = rc; + } + + /// + /// Gets the background image of the current visual style element within the specified background color. If is set, the resulting image will contain each of the state images side by side. + /// + /// The instance. + /// The background color. This color cannot have an alpha channel. + /// The optional list of states to render side by side. + /// The background image. + public static Bitmap GetBackgroundBitmap(this VisualStyleRenderer rnd, Color clr, int[] states = null) + { + const int wh = 200; + if (rnd == null) throw new ArgumentNullException(nameof(rnd)); + rnd.SetParameters(rnd.Class, rnd.Part, 0); + if (states == null) states = new[] { rnd.State }; + var i = states.Length; + + // Get image size + Size imgSz; + using (var sg = Graphics.FromHwnd(IntPtr.Zero)) + imgSz = rnd.GetPartSize(sg, new Rectangle(0, 0, wh, wh), ThemeSizeType.Draw); + if (imgSz.Width == 0 || imgSz.Height == 0) + imgSz = new Size(rnd.GetInteger(IntegerProperty.Width), rnd.GetInteger(IntegerProperty.Height)); + + var bounds = new Rectangle(0, 0, imgSz.Width * i, imgSz.Height); + + // Draw each background linearly down the bitmap + using (var memoryHdc = SafeDCHandle.ScreenCompatibleDCHandle) + { + // Create a device-independent bitmap and select it into our DC + var info = new BITMAPINFO(bounds.Width, -bounds.Height); + IntPtr ppv; + using (new SafeDCObjectHandle(memoryHdc, CreateDIBSection(SafeDCHandle.Null, ref info, DIBColorMode.DIB_RGB_COLORS, out ppv, IntPtr.Zero, 0))) + { + using (var memoryGraphics = Graphics.FromHdc(memoryHdc.DangerousGetHandle())) + { + // Setup graphics + memoryGraphics.CompositingMode = CompositingMode.SourceOver; + memoryGraphics.CompositingQuality = CompositingQuality.HighQuality; + memoryGraphics.SmoothingMode = SmoothingMode.HighQuality; + memoryGraphics.Clear(clr); + + // Draw each background linearly down the bitmap + var rect = new Rectangle(0, 0, imgSz.Width, imgSz.Height); + foreach (var state in states) + { + rnd.SetParameters(rnd.Class, rnd.Part, state); + rnd.DrawBackground(memoryGraphics, rect); + rect.X += imgSz.Width; + } + } + + // Copy DIB to Bitmap + var bmp = new Bitmap(bounds.Width, bounds.Height, PixelFormat.Format32bppArgb); + using (var primaryHdc = new SafeDCHandle(Graphics.FromImage(bmp))) + BitBlt(primaryHdc, bounds.Left, bounds.Top, bounds.Width, bounds.Height, memoryHdc, 0, 0, RasterOperationMode.SRCCOPY); + return bmp; + } + } + } + + /// + /// Returns the value of the specified font property for the current visual style element. + /// + /// The instance. + /// The used to draw the text. + /// A that contains the value of the property specified by the prop parameter for the current visual style element. + public static Font GetFont2(this VisualStyleRenderer rnd, IDeviceContext dc = null) + { + using (var hdc = new SafeDCHandle(dc)) + { + LOGFONT f; + var hres = GetThemeFont(rnd, hdc, rnd.Part, rnd.State, 210, out f); + if (hres != 0) + throw new System.ComponentModel.Win32Exception(hres); + return f.ToFont(); + } + } + + private static void DrawWrapper(IDeviceContext dc, Rectangle bounds, DrawWrapperMethod func) + { + using (var sdc = new SafeDCHandle(dc)) + { + // Create a memory DC so we can work off screen + using (var memoryHdc = sdc.GetCompatibleDCHandle()) + { + // Create a device-independent bitmap and select it into our DC + var info = new BITMAPINFO(bounds.Width, -bounds.Height); + IntPtr pBits; + using (new SafeDCObjectHandle(memoryHdc, CreateDIBSection(sdc, ref info, 0, out pBits, IntPtr.Zero, 0))) + { + // Call method + func(memoryHdc); + + // Copy to foreground + BitBlt(sdc, bounds.Left, bounds.Top, bounds.Width, bounds.Height, memoryHdc, 0, 0, RasterOperationMode.SRCCOPY); + } + } + } + } + + private static long GetHashCode(this VisualStyleRenderer r) => (long)r.Class.GetHashCode() << 32 | ((uint)r.Part << 16 | (ushort)r.State); + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/Native/WIN32_FIND_DATA.cs b/AeroWizard/AeroWizard/Native/WIN32_FIND_DATA.cs new file mode 100644 index 0000000..d9775e7 --- /dev/null +++ b/AeroWizard/AeroWizard/Native/WIN32_FIND_DATA.cs @@ -0,0 +1,28 @@ +using System; +using System.Runtime.InteropServices; +using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME; + +namespace Vanara.Interop +{ + internal static partial class NativeMethods + { + [Serializable, StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto), BestFitMapping(false)] + public class WIN32_FIND_DATA + { + public System.IO.FileAttributes dwFileAttributes; + public FILETIME ftCreationTime; + public FILETIME ftLastAccessTime; + public FILETIME ftLastWriteTime; + public int nFileSizeHigh; + public int nFileSizeLow; + public int dwReserved0; + public int dwReserved1; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] + public string cFileName; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] + public string cAlternateFileName; + } + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/Properties/AssemblyInfo.cs b/AeroWizard/AeroWizard/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..65971ea --- /dev/null +++ b/AeroWizard/AeroWizard/Properties/AssemblyInfo.cs @@ -0,0 +1,39 @@ +using System; +using System.Reflection; +using System.Resources; +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("AeroWizard")] +[assembly: AssemblyDescription("Library for easy creation of custom and Aero Wizards. Aero Wizard strictly follows Microsoft guidelines and uses Visual Styles to get visual theming.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("CodePlex Community")] +[assembly: AssemblyProduct("AeroWizard")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: CLSCompliant(true)] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("949bb556-287f-4184-99f2-f46321235322")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// 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("2.1.8.0")] +[assembly: AssemblyFileVersion("2.1.8.0")] +[assembly: NeutralResourcesLanguageAttribute("en-US")] diff --git a/AeroWizard/AeroWizard/Properties/Resources.Designer.cs b/AeroWizard/AeroWizard/Properties/Resources.Designer.cs new file mode 100644 index 0000000..5fcf68f --- /dev/null +++ b/AeroWizard/AeroWizard/Properties/Resources.Designer.cs @@ -0,0 +1,183 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.42000 +// +// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn +// der Code erneut generiert wird. +// +//------------------------------------------------------------------------------ + +namespace AeroWizard.Properties { + using System; + + + /// + /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw. + /// + // Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert + // -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert. + // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen + // mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AeroWizard.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle + /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap BackBtnStrip { + get { + object obj = ResourceManager.GetObject("BackBtnStrip", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap BackBtnStrip2 { + get { + object obj = ResourceManager.GetObject("BackBtnStrip2", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Returns to a previous page ähnelt. + /// + internal static string WizardBackButtonToolTip { + get { + return ResourceManager.GetString("WizardBackButtonToolTip", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die &Back ähnelt. + /// + internal static string WizardBackText { + get { + return ResourceManager.GetString("WizardBackText", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die &Cancel ähnelt. + /// + internal static string WizardCancelText { + get { + return ResourceManager.GetString("WizardCancelText", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Icon ähnlich wie (Symbol). + /// + internal static System.Drawing.Icon WizardControlIcon { + get { + object obj = ResourceManager.GetObject("WizardControlIcon", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die &Finish ähnelt. + /// + internal static string WizardFinishText { + get { + return ResourceManager.GetString("WizardFinishText", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Page Title ähnelt. + /// + internal static string WizardHeader { + get { + return ResourceManager.GetString("WizardHeader", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die &Help ähnelt. + /// + internal static string WizardHelpText { + get { + return ResourceManager.GetString("WizardHelpText", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die &Next ähnelt. + /// + internal static string WizardNextText { + get { + return ResourceManager.GetString("WizardNextText", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die No wizard pages have been added. ähnelt. + /// + internal static string WizardNoPagesNotice { + get { + return ResourceManager.GetString("WizardNoPagesNotice", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Help ähnelt. + /// + internal static string WizardPageDefaultHelpText { + get { + return ResourceManager.GetString("WizardPageDefaultHelpText", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die Wizard Title ähnelt. + /// + internal static string WizardTitle { + get { + return ResourceManager.GetString("WizardTitle", resourceCulture); + } + } + } +} diff --git a/AeroWizard/AeroWizard/Properties/Resources.de.resx b/AeroWizard/AeroWizard/Properties/Resources.de.resx new file mode 100644 index 0000000..8e74e74 --- /dev/null +++ b/AeroWizard/AeroWizard/Properties/Resources.de.resx @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Hilfe + + + &Zurück + + + &Abbrechen + + + &Fertig stellen + + + Seitentitel + + + &Hilfe + + + &Weiter + + + Assistententitel + + + Kehrt zurück zur vorherigen Seite! + + + Keine Assistentenseiten hinzugekommen! + + \ No newline at end of file diff --git a/AeroWizard/AeroWizard/Properties/Resources.es.resx b/AeroWizard/AeroWizard/Properties/Resources.es.resx new file mode 100644 index 0000000..109fad7 --- /dev/null +++ b/AeroWizard/AeroWizard/Properties/Resources.es.resx @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + &Cancelar + + + Vuelve a la página anterior + + + &Atrás + + + &Finalizar + + + Título de la página + + + A&yuda + + + &Siguiente + + + Se han añadido ninguna página del asistente. + + + Título del asistente + + + Ayuda + + \ No newline at end of file diff --git a/AeroWizard/AeroWizard/Properties/Resources.fr.resx b/AeroWizard/AeroWizard/Properties/Resources.fr.resx new file mode 100644 index 0000000..d3217bf --- /dev/null +++ b/AeroWizard/AeroWizard/Properties/Resources.fr.resx @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Retourne à une page précédente + + + &Précédent + + + &Annuler + + + &Terminer + + + Titre de la page + + + &Aide + + + &Suivant + + + Pas de pages de l'assistant ont été ajoutés. + + + Assistant Titre + + + Aide + + \ No newline at end of file diff --git a/AeroWizard/AeroWizard/Properties/Resources.resx b/AeroWizard/AeroWizard/Properties/Resources.resx new file mode 100644 index 0000000..698c366 --- /dev/null +++ b/AeroWizard/AeroWizard/Properties/Resources.resx @@ -0,0 +1,160 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\resources\backbtnstrip.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\BackBtnStrip2.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + Returns to a previous page + + + &Back + + + &Cancel + + + ..\Resources\WizardHat.ico;System.Drawing.Icon, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + &Finish + + + Page Title + + + &Help + + + &Next + + + No wizard pages have been added. + + + Wizard Title + + + Help + + \ No newline at end of file diff --git a/AeroWizard/AeroWizard/Resources/BackBtnStrip.png b/AeroWizard/AeroWizard/Resources/BackBtnStrip.png new file mode 100644 index 0000000..a07f4b9 Binary files /dev/null and b/AeroWizard/AeroWizard/Resources/BackBtnStrip.png differ diff --git a/AeroWizard/AeroWizard/Resources/BackBtnStrip2.png b/AeroWizard/AeroWizard/Resources/BackBtnStrip2.png new file mode 100644 index 0000000..bb97536 Binary files /dev/null and b/AeroWizard/AeroWizard/Resources/BackBtnStrip2.png differ diff --git a/AeroWizard/AeroWizard/Resources/WizardHat.ico b/AeroWizard/AeroWizard/Resources/WizardHat.ico new file mode 100644 index 0000000..aae4c51 Binary files /dev/null and b/AeroWizard/AeroWizard/Resources/WizardHat.ico differ diff --git a/AeroWizard/AeroWizard/StepList.cs b/AeroWizard/AeroWizard/StepList.cs new file mode 100644 index 0000000..eb88a43 --- /dev/null +++ b/AeroWizard/AeroWizard/StepList.cs @@ -0,0 +1,271 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Windows.Forms; + +namespace AeroWizard +{ + /// + /// Shows a list of all the pages in the WizardControl + /// + [ProvideProperty("StepText", typeof(WizardPage))] + [ProvideProperty("StepTextIndentLevel", typeof(WizardPage))] + internal class StepList : ScrollableControl, IExtenderProvider + { + private const TextFormatFlags defBulletTextFormat = TextFormatFlags.SingleLine | TextFormatFlags.VerticalCenter | TextFormatFlags.NoPadding; + private const TextFormatFlags defStringTextFormat = TextFormatFlags.EndEllipsis | TextFormatFlags.SingleLine | TextFormatFlags.VerticalCenter; + + private System.Drawing.Font boldFont; + private int bulletWidth; + private int defItemHeight; + private Dictionary indentLevels = new Dictionary(); + private WizardControl myParent; + private System.Drawing.Font ptrFont; + private Dictionary stepTexts = new Dictionary(); + + /// + /// Initializes a new instance of the class. + /// + public StepList() + { + } + + /// + /// Occurs when a visual aspect of an owner-drawn StepList changes. + /// + [Category("Appearance")] + public event EventHandler DrawItem; + + /// + /// Occurs when an owner-drawn StepList is created and the sizes of the list items are determined. + /// + [Category("Appearance")] + public event EventHandler MeasureItem; + + /// + /// Gets or sets a value indicating whether the StepList is drawn by the operating system or by code that you provide. + /// + /// + /// true if the StepList is drawn by code that you provide; otherwise, false. + /// + [DefaultValue(false), Category("Appearance"), Description("Indicates if controls is drawn by owner.")] + public bool OwnerDraw { get; set; } + + /// + /// Gets the default size of the control. + /// + /// The default of the control. + protected override Size DefaultSize => new Size(150, 200); + + /// + /// Gets the step text. + /// + /// The page. + /// Step text for the specified wizard page. + [DefaultValue((string)null), Category("Appearance"), Description("Alternate text to provide to the StepList. Default value comes the Text property of the WizardPage.")] + public string GetStepText(WizardPage page) + { + string value; + if (stepTexts.TryGetValue(page, out value)) + return value; + return page.Text; + } + + /// + /// Gets the step text indent level. + /// + /// The page. + /// Step text indent level for the specified wizard page. + [DefaultValue(0), Category("Appearance"), Description("Indentation level for text provided to the StepList.")] + public int GetStepTextIndentLevel(WizardPage page) + { + int value; + if (indentLevels.TryGetValue(page, out value)) + return value; + return 0; + } + + bool IExtenderProvider.CanExtend(object extendee) => (extendee is WizardPage); + + /// + /// Sets the step text. + /// + /// The page. + /// The value. + public void SetStepText(WizardPage page, string value) + { + if (string.IsNullOrEmpty(value) || value == page.Text) + stepTexts.Remove(page); + else + stepTexts[page] = value; + Refresh(); + } + + /// + /// Sets the step text indent level. + /// + /// The page. + /// The indent level. + public void SetStepTextIndentLevel(WizardPage page, int value) + { + if (value < 0) value = 0; + if (value == 0) + indentLevels.Remove(page); + else + indentLevels[page] = value; + Refresh(); + } + + /// + /// Raises the event. + /// + /// The instance containing the event data. + protected virtual void OnDrawItem(DrawStepListItemEventArgs e) + { + var h = DrawItem; + if (h != null) + h(this, e); + } + + /// + /// Raises the event. + /// + /// The instance containing the event data. + protected override void OnFontChanged(EventArgs e) + { + base.OnFontChanged(e); + using (Graphics g = CreateGraphics()) + { + defItemHeight = (int)Math.Ceiling(TextRenderer.MeasureText(g, "Wg", Font).Height * 1.2); + ptrFont = new Font("Marlett", Font.Size); + boldFont = new Font(Font, FontStyle.Bold); + bulletWidth = TextRenderer.MeasureText(g, "4", ptrFont, new Size(0, defItemHeight), defBulletTextFormat).Width; + } + } + + /// + /// Raises the event. + /// + /// The instance containing the event data. + protected virtual void OnMeasureItem(MeasureStepListItemEventArgs e) + { + var h = MeasureItem; + if (h != null) + h(this, e); + } + + /// + /// Raises the event. + /// + /// A that contains the event data. + protected override void OnPaint(PaintEventArgs e) + { + base.OnPaint(e); + if (myParent == null) return; + int y = Padding.Top; + WizardPageCollection pages = myParent.Pages; + bool hit = false; + for (int i = 0; i < pages.Count; i++) + { + var curPage = pages[i]; + if (!curPage.Suppress) + { + Size itemSize = new Size(Width - Padding.Horizontal, defItemHeight); + if (OwnerDraw) + { + var meArg = new MeasureStepListItemEventArgs(e.Graphics, Font, curPage, new Size(Width, defItemHeight)); + OnMeasureItem(meArg); + itemSize = meArg.ItemSize; + } + if (y + itemSize.Height > (Height - Padding.Bottom)) + break; + bool isSelected = myParent.SelectedPage == curPage; + if (isSelected) hit = true; + var eArg = new DrawStepListItemEventArgs(e.Graphics, Font, new Rectangle(new Point(Padding.Left, y), itemSize), curPage, isSelected, hit); + if (OwnerDraw) + OnDrawItem(eArg); + else + DefaultDrawItem(eArg); + y += itemSize.Height; + } + } + } + + /// + /// Raises the event. + /// + /// An that contains the event data. + protected override void OnParentChanged(EventArgs e) + { + base.OnParentChanged(e); + SetupControl(this.GetParent()); + } + + private static void Split(Rectangle rect, int xPos, out Rectangle r1, out Rectangle r2) + { + r1 = r2 = rect; + r1.Width = r2.X = xPos; + r2.Width = r2.Width - xPos; + } + + private void DefaultDrawItem(DrawStepListItemEventArgs e) + { + Color fc = ForeColor, bc = BackColor; + int level = GetStepTextIndentLevel(e.Item); + bool isRTL = RightToLeft == System.Windows.Forms.RightToLeft.Yes; + var tffrtl = isRTL ? TextFormatFlags.Right : 0; + Rectangle rect, prect; + if (isRTL) + { + Split(e.Bounds, Width - bulletWidth, out rect, out prect); + prect.X = e.Bounds.Width - (bulletWidth * (level + 1)); + rect.Width = Width - (bulletWidth * (level + 1)); + } + else + { + Split(e.Bounds, bulletWidth, out prect, out rect); + prect.X = bulletWidth * level; + rect.X = bulletWidth * (level + 1); + } + if (!e.Completed) + fc = SystemColors.GrayText; + using (Brush br = new SolidBrush(bc)) + e.Graphics.FillRectangle(br, Rectangle.Union(rect, prect)); + TextRenderer.DrawText(e.Graphics, e.Completed ? (isRTL ? "3" : "4") : "a", ptrFont, prect, fc, defBulletTextFormat | tffrtl); + TextRenderer.DrawText(e.Graphics, GetStepText(e.Item), e.Selected ? boldFont : Font, rect, fc, defStringTextFormat | tffrtl); + } + + private void pages_Changed(object sender, EventedList.ListChangedEventArgs e) + { + Refresh(); + } + + private void ResetStepText(WizardPage page) + { + SetStepText(page, null); + } + + private void SetupControl(WizardControl p) + { + if (myParent != null) + { + WizardPageCollection pages = myParent.Pages; + pages.ItemAdded -= pages_Changed; + pages.ItemChanged -= pages_Changed; + pages.ItemDeleted -= pages_Changed; + } + myParent = p; + if (myParent != null) + { + WizardPageCollection pages = myParent.Pages; + pages.ItemAdded += pages_Changed; + pages.ItemChanged += pages_Changed; + pages.ItemDeleted += pages_Changed; + } + Refresh(); + } + + private bool ShouldSerializeStepText(WizardPage page) => (GetStepText(page) != page.Text); + } +} diff --git a/AeroWizard/AeroWizard/StepWizardControl.bmp b/AeroWizard/AeroWizard/StepWizardControl.bmp new file mode 100644 index 0000000..09b1b6f Binary files /dev/null and b/AeroWizard/AeroWizard/StepWizardControl.bmp differ diff --git a/AeroWizard/AeroWizard/StepWizardControl.cs b/AeroWizard/AeroWizard/StepWizardControl.cs new file mode 100644 index 0000000..2d60ce3 --- /dev/null +++ b/AeroWizard/AeroWizard/StepWizardControl.cs @@ -0,0 +1,305 @@ +using System; +using System.ComponentModel; +using System.Drawing; +using System.Windows.Forms; + +namespace AeroWizard +{ + /// + /// Wizard control that shows a step summary on the left of the wizard page area. + /// + [ProvideProperty("StepText", typeof(WizardPage))] + [ProvideProperty("StepTextIndentLevel", typeof(WizardPage))] + [ToolboxItem(true), ToolboxBitmap(typeof(StepWizardControl), "StepWizardControl.bmp")] + public class StepWizardControl : WizardControl, IExtenderProvider + { + private StepList list; + private Splitter splitter; + + /// + /// Initializes a new instance of the class. + /// + public StepWizardControl() + { + var ds = RightToLeft == System.Windows.Forms.RightToLeft.Yes ? DockStyle.Right : DockStyle.Left; + pageContainer.Controls.Add(splitter = new Splitter() { Dock = ds, BorderStyle = BorderStyle.FixedSingle, Width = 1, Name = "splitter" }); + pageContainer.Controls.Add(list = new StepList() { Dock = ds, Name = "stepList" }); + list.DrawItem += list_DrawItem; + list.MeasureItem += list_MeasureItem; + Pages.Reset += Pages_Reset; + } + + /// + /// Occurs when a visual aspect of an owner-drawn StepList changes. + /// + [Category("Appearance")] + public event EventHandler DrawStepListItem; + + /// + /// Occurs when an owner-drawn StepList is created and the sizes of the list items are determined. + /// + [Category("Appearance")] + public event EventHandler MeasureStepListItem; + + /// + /// Gets or sets a value indicating whether the StepWizardControl step list is drawn by the operating system or by code that you provide. + /// + /// + /// true if the StepWizardControl step list is drawn by code that you provide; otherwise, false. + /// + [DefaultValue(false), Category("Appearance"), Description("Indicates if step list items are drawn by owner.")] + public bool OwnerDrawStepList + { + get { return list.OwnerDraw; } + set { list.OwnerDraw = value; } + } + + /// + /// Gets or sets the StepList font. + /// + /// + /// The StepList font. + /// + [Category("Appearance"), Description("Font for drawing StepList.")] + public Font StepListFont + { + get { return list.Font; } + set { list.Font = value; } + } + + /// + /// Gets or sets the width of the step list. + /// + /// + /// The width of the step list. + /// + [DefaultValue(150), Category("Appearance"), Description("Determines width of step list on left.")] + public int StepListWidth + { + get { return list.Width; } + set { list.Width = value; } + } + + /// + /// Gets the step text. + /// + /// The page. + /// Step text for the specified wizard page. + [DefaultValue((string)null), Category("Appearance"), Description("Alternate text to provide to the StepList. Default value comes the Text property of the WizardPage.")] + public string GetStepText(WizardPage page) => list.GetStepText(page); + + /// + /// Gets the step text indent level. + /// + /// The page. + /// Step text indent level for the specified wizard page. + [DefaultValue(0), Category("Appearance"), Description("Indentation level for text provided to the StepList.")] + public int GetStepTextIndentLevel(WizardPage page) => list.GetStepTextIndentLevel(page); + + /// + /// Specifies whether this object can provide its extender properties to the specified object. + /// + /// The to receive the extender properties. + /// + /// true if this object can provide extender properties to the specified object; otherwise, false. + /// + bool IExtenderProvider.CanExtend(object extendee) => (extendee is WizardPage); + + /// + /// Sets the step text. + /// + /// The page. + /// The value. + public void SetStepText(WizardPage page, string value) + { + list.SetStepText(page, value); + } + + /// + /// Sets the step text indent level. + /// + /// The page. + /// The indent level. + public void SetStepTextIndentLevel(WizardPage page, int value) + { + list.SetStepTextIndentLevel(page, value); + } + + /// + /// Raises the event. + /// + /// The instance containing the event data. + protected virtual void OnDrawStepListItem(DrawStepListItemEventArgs e) + { + var h = DrawStepListItem; + if (h != null) + h(this, e); + } + + /// + /// Raises the event. + /// + /// The instance containing the event data. + protected virtual void OnMeasureStepListItem(MeasureStepListItemEventArgs e) + { + var h = MeasureStepListItem; + if (h != null) + h(this, e); + } + + /// + /// Raises the event. + /// + /// An that contains the event data. + protected override void OnRightToLeftChanged(System.EventArgs e) + { + base.OnRightToLeftChanged(e); + var ds = RightToLeft == System.Windows.Forms.RightToLeft.Yes ? DockStyle.Right : DockStyle.Left; + if (pageContainer.Controls.Count > 1) + { + pageContainer.Controls["splitter"].Dock = ds; + pageContainer.Controls["stepList"].Dock = ds; + } + } + + private void list_DrawItem(object sender, DrawStepListItemEventArgs e) + { + OnDrawStepListItem(e); + } + + private void list_MeasureItem(object sender, MeasureStepListItemEventArgs e) + { + OnMeasureStepListItem(e); + } + + void Pages_Reset(object sender, System.Collections.Generic.EventedList.ListChangedEventArgs e) + { + pageContainer.Controls.Add(splitter); + pageContainer.Controls.Add(list); + } + + private void ResetStepListFont() + { + list.Font = Font; + } + + private void ResetStepText(WizardPage page) + { + SetStepText(page, null); + } + + private bool ShouldSerializeStepListFont() => Font != list.Font; + + private bool ShouldSerializeStepText(WizardPage page) => (GetStepText(page) != page.Text); + } + + /// + /// Provides data for the event. + /// + public class DrawStepListItemEventArgs : EventArgs + { + internal DrawStepListItemEventArgs(Graphics graphics, Font font, Rectangle itemRect, WizardPage page, bool isSelected, bool isCompleted) + { + Graphics = graphics; + Font = font; + Bounds = itemRect; + Item = page; + Selected = isSelected; + Completed = isCompleted; + } + + /// + /// Gets the size and location of the item to draw. + /// + /// + /// A rectangle that represents the bounds of the item to draw. + /// + public Rectangle Bounds { get; } + + /// + /// Gets a value indicating whether this step has already been completed. + /// + /// + /// true if completed; otherwise, false. + /// + public bool Completed { get; } + + /// + /// Gets the used to draw the item. + /// + /// + /// The used to draw the item. + /// + public Font Font { get; } + + /// + /// Gets the used to draw the item. + /// + /// + /// The used to draw the item. + /// + public Graphics Graphics { get; } + + /// + /// Gets the to which this item refers. + /// + /// + /// The to which this item refers. + /// + public WizardPage Item { get; } + + /// + /// Gets a value indicating whether this item is the one currently selected. + /// + /// + /// true if selected; otherwise, false. + /// + public bool Selected { get; } + } + + /// + /// Provides data for the event. + /// + public class MeasureStepListItemEventArgs : EventArgs + { + internal MeasureStepListItemEventArgs(Graphics graphics, Font font, WizardPage page, Size itemSize) + { + Graphics = graphics; + Font = font; + Item = page; + ItemSize = itemSize; + } + + /// + /// Gets the used to draw the item. + /// + /// + /// The used to draw the item. + /// + public Font Font { get; } + + /// + /// Gets the used to draw the item. + /// + /// + /// The used to draw the item. + /// + public Graphics Graphics { get; } + + /// + /// Gets the to which this item refers. + /// + /// + /// The to which this item refers. + /// + public WizardPage Item { get; } + + /// + /// Gets or sets the size of the item. + /// + /// + /// The size of the item. + /// + public Size ItemSize { get; set; } + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/StepWizardControl.resx b/AeroWizard/AeroWizard/StepWizardControl.resx new file mode 100644 index 0000000..7080a7d --- /dev/null +++ b/AeroWizard/AeroWizard/StepWizardControl.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/AeroWizard/AeroWizard/TestWizard/AeroWizardTemplate.Designer.cs b/AeroWizard/AeroWizard/TestWizard/AeroWizardTemplate.Designer.cs new file mode 100644 index 0000000..f82efc1 --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/AeroWizardTemplate.Designer.cs @@ -0,0 +1,71 @@ +using AeroWizard; + +namespace TestWizard +{ + partial class AeroWizardTemplate + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.wizardControl1 = new AeroWizard.WizardControl(); + this.wizardPage1 = new AeroWizard.WizardPage(); + ((System.ComponentModel.ISupportInitialize)(this.wizardControl1)).BeginInit(); + this.SuspendLayout(); + // + // wizardControl1 + // + this.wizardControl1.Location = new System.Drawing.Point(0, 0); + this.wizardControl1.Name = "wizardControl1"; + this.wizardControl1.Pages.Add(this.wizardPage1); + this.wizardControl1.Size = new System.Drawing.Size(574, 415); + this.wizardControl1.TabIndex = 0; + // + // wizardPage1 + // + this.wizardPage1.Name = "wizardPage1"; + this.wizardPage1.Size = new System.Drawing.Size(527, 260); + this.wizardPage1.TabIndex = 0; + this.wizardPage1.Text = "Page Title"; + // + // AeroWizardTemplate + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(574, 415); + this.Controls.Add(this.wizardControl1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.Name = "AeroWizardTemplate"; + ((System.ComponentModel.ISupportInitialize)(this.wizardControl1)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private AeroWizard.WizardControl wizardControl1; + private AeroWizard.WizardPage wizardPage1; + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/TestWizard/AeroWizardTemplate.cs b/AeroWizard/AeroWizard/TestWizard/AeroWizardTemplate.cs new file mode 100644 index 0000000..32a9a2d --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/AeroWizardTemplate.cs @@ -0,0 +1,12 @@ +using System.Windows.Forms; + +namespace TestWizard +{ + public partial class AeroWizardTemplate : Form + { + public AeroWizardTemplate() + { + InitializeComponent(); + } + } +} diff --git a/AeroWizard/AeroWizard/TestWizard/AeroWizardTemplate.resx b/AeroWizard/AeroWizard/TestWizard/AeroWizardTemplate.resx new file mode 100644 index 0000000..7080a7d --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/AeroWizardTemplate.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/AeroWizard/AeroWizard/TestWizard/CustomWizardTemplate.Designer.cs b/AeroWizard/AeroWizard/TestWizard/CustomWizardTemplate.Designer.cs new file mode 100644 index 0000000..c5dc7bd --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/CustomWizardTemplate.Designer.cs @@ -0,0 +1,132 @@ +using AeroWizard; + +namespace TestWizard +{ + partial class CustomWizardTemplate + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.backBtn = new System.Windows.Forms.Button(); + this.cancelBtn = new System.Windows.Forms.Button(); + this.nextBtn = new System.Windows.Forms.Button(); + this.wizardPageContainer1 = new AeroWizard.WizardPageContainer(); + this.wizardPage1 = new AeroWizard.WizardPage(); + this.headingControl = new System.Windows.Forms.Label(); + ((System.ComponentModel.ISupportInitialize)(this.wizardPageContainer1)).BeginInit(); + this.wizardPageContainer1.SuspendLayout(); + this.SuspendLayout(); + // + // backBtn + // + this.backBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.backBtn.Location = new System.Drawing.Point(93, 204); + this.backBtn.Name = "backBtn"; + this.backBtn.Size = new System.Drawing.Size(75, 23); + this.backBtn.TabIndex = 1; + this.backBtn.Text = "&Back"; + // + // cancelBtn + // + this.cancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancelBtn.Location = new System.Drawing.Point(255, 204); + this.cancelBtn.Name = "cancelBtn"; + this.cancelBtn.Size = new System.Drawing.Size(75, 23); + this.cancelBtn.TabIndex = 1; + this.cancelBtn.Text = "&Cancel"; + // + // nextBtn + // + this.nextBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.nextBtn.Location = new System.Drawing.Point(174, 204); + this.nextBtn.Name = "nextBtn"; + this.nextBtn.Size = new System.Drawing.Size(75, 23); + this.nextBtn.TabIndex = 1; + this.nextBtn.Text = "&Next"; + // + // wizardPageContainer1 + // + this.wizardPageContainer1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.wizardPageContainer1.BackButton = this.backBtn; + this.wizardPageContainer1.CancelButton = this.cancelBtn; + this.wizardPageContainer1.Controls.Add(this.wizardPage1); + this.wizardPageContainer1.Location = new System.Drawing.Point(12, 25); + this.wizardPageContainer1.Name = "wizardPageContainer1"; + this.wizardPageContainer1.NextButton = this.nextBtn; + this.wizardPageContainer1.Pages.Add(this.wizardPage1); + this.wizardPageContainer1.Size = new System.Drawing.Size(318, 173); + this.wizardPageContainer1.TabIndex = 2; + this.wizardPageContainer1.Cancelling += new System.ComponentModel.CancelEventHandler(this.wizardPageContainer1_Cancelling); + this.wizardPageContainer1.Finished += new System.EventHandler(this.wizardPageContainer1_Finished); + this.wizardPageContainer1.SelectedPageChanged += new System.EventHandler(this.wizardPageContainer1_SelectedPageChanged); + // + // wizardPage1 + // + this.wizardPage1.Name = "wizardPage1"; + this.wizardPage1.Size = new System.Drawing.Size(318, 173); + this.wizardPage1.TabIndex = 0; + this.wizardPage1.Text = "Page 1"; + // + // headingControl + // + this.headingControl.AutoSize = true; + this.headingControl.Location = new System.Drawing.Point(9, 9); + this.headingControl.Name = "headingControl"; + this.headingControl.Size = new System.Drawing.Size(59, 13); + this.headingControl.TabIndex = 3; + this.headingControl.Text = ""; + // + // CustomWizardTemplate + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(342, 239); + this.Controls.Add(this.headingControl); + this.Controls.Add(this.wizardPageContainer1); + this.Controls.Add(this.cancelBtn); + this.Controls.Add(this.nextBtn); + this.Controls.Add(this.backBtn); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; + this.Name = "CustomWizardTemplate"; + ((System.ComponentModel.ISupportInitialize)(this.wizardPageContainer1)).EndInit(); + this.wizardPageContainer1.ResumeLayout(false); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button backBtn; + private System.Windows.Forms.Button cancelBtn; + private System.Windows.Forms.Button nextBtn; + private AeroWizard.WizardPageContainer wizardPageContainer1; + private AeroWizard.WizardPage wizardPage1; + private System.Windows.Forms.Label headingControl; + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/TestWizard/CustomWizardTemplate.cs b/AeroWizard/AeroWizard/TestWizard/CustomWizardTemplate.cs new file mode 100644 index 0000000..0218887 --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/CustomWizardTemplate.cs @@ -0,0 +1,28 @@ +using System; +using System.Windows.Forms; + +namespace TestWizard +{ + public partial class CustomWizardTemplate : Form + { + public CustomWizardTemplate() + { + InitializeComponent(); + } + + private void wizardPageContainer1_Cancelling(object sender, System.ComponentModel.CancelEventArgs e) + { + Close(); + } + + private void wizardPageContainer1_Finished(object sender, EventArgs e) + { + Close(); + } + + private void wizardPageContainer1_SelectedPageChanged(object sender, EventArgs e) + { + headingControl.Text = wizardPageContainer1.SelectedPage.Text; + } + } +} diff --git a/AeroWizard/AeroWizard/TestWizard/CustomWizardTemplate.resx b/AeroWizard/AeroWizard/TestWizard/CustomWizardTemplate.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/CustomWizardTemplate.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/AeroWizard/AeroWizard/TestWizard/Main.Designer.cs b/AeroWizard/AeroWizard/TestWizard/Main.Designer.cs new file mode 100644 index 0000000..500a938 --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/Main.Designer.cs @@ -0,0 +1,201 @@ +namespace TestWizard +{ + partial class Main + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.appRenderVS = new System.Windows.Forms.CheckBox(); + this.compEnabledCheck = new System.Windows.Forms.CheckBox(); + this.vsEnabledByUser = new System.Windows.Forms.CheckBox(); + this.vsOnOS = new System.Windows.Forms.CheckBox(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.customBtn = new System.Windows.Forms.Button(); + this.stepBtn = new System.Windows.Forms.Button(); + this.wizBtn = new System.Windows.Forms.Button(); + this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); + this.oldButton = new System.Windows.Forms.Button(); + this.groupBox1.SuspendLayout(); + this.groupBox2.SuspendLayout(); + this.SuspendLayout(); + // + // appRenderVS + // + this.appRenderVS.AutoCheck = false; + this.appRenderVS.AutoSize = true; + this.appRenderVS.Location = new System.Drawing.Point(6, 42); + this.appRenderVS.Name = "appRenderVS"; + this.appRenderVS.Size = new System.Drawing.Size(94, 17); + this.appRenderVS.TabIndex = 0; + this.appRenderVS.Text = "AppRenderVS"; + this.toolTip1.SetToolTip(this.appRenderVS, "Indicates if Visual Styles are enabled for this application."); + this.appRenderVS.UseVisualStyleBackColor = true; + // + // compEnabledCheck + // + this.compEnabledCheck.AutoSize = true; + this.compEnabledCheck.Location = new System.Drawing.Point(6, 19); + this.compEnabledCheck.Name = "compEnabledCheck"; + this.compEnabledCheck.Size = new System.Drawing.Size(125, 17); + this.compEnabledCheck.TabIndex = 0; + this.compEnabledCheck.Text = "Composition Enabled"; + this.toolTip1.SetToolTip(this.compEnabledCheck, "Indicates if Desktop Window Composition (Aero) is enabled. For Windows Vista and " + + "Windows 7, this setting is user configurable."); + this.compEnabledCheck.UseVisualStyleBackColor = true; + this.compEnabledCheck.CheckedChanged += new System.EventHandler(this.compEnabledCheck_CheckedChanged); + // + // vsEnabledByUser + // + this.vsEnabledByUser.AutoCheck = false; + this.vsEnabledByUser.AutoSize = true; + this.vsEnabledByUser.Location = new System.Drawing.Point(149, 19); + this.vsEnabledByUser.Name = "vsEnabledByUser"; + this.vsEnabledByUser.Size = new System.Drawing.Size(113, 17); + this.vsEnabledByUser.TabIndex = 0; + this.vsEnabledByUser.Text = "VSEnabledByUser"; + this.toolTip1.SetToolTip(this.vsEnabledByUser, "Indicates if Visual Styles are enabled by the user."); + this.vsEnabledByUser.UseVisualStyleBackColor = true; + // + // vsOnOS + // + this.vsOnOS.AutoCheck = false; + this.vsOnOS.AutoSize = true; + this.vsOnOS.Location = new System.Drawing.Point(149, 42); + this.vsOnOS.Name = "vsOnOS"; + this.vsOnOS.Size = new System.Drawing.Size(67, 17); + this.vsOnOS.TabIndex = 0; + this.vsOnOS.Text = "VSonOS"; + this.toolTip1.SetToolTip(this.vsOnOS, "Indicates if Visual Styles are enabled by the Operating System."); + this.vsOnOS.UseVisualStyleBackColor = true; + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.compEnabledCheck); + this.groupBox1.Controls.Add(this.appRenderVS); + this.groupBox1.Controls.Add(this.vsOnOS); + this.groupBox1.Controls.Add(this.vsEnabledByUser); + this.groupBox1.Location = new System.Drawing.Point(12, 12); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(268, 67); + this.groupBox1.TabIndex = 2; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Environment"; + // + // groupBox2 + // + this.groupBox2.Controls.Add(this.oldButton); + this.groupBox2.Controls.Add(this.customBtn); + this.groupBox2.Controls.Add(this.stepBtn); + this.groupBox2.Controls.Add(this.wizBtn); + this.groupBox2.Location = new System.Drawing.Point(12, 86); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Size = new System.Drawing.Size(268, 82); + this.groupBox2.TabIndex = 3; + this.groupBox2.TabStop = false; + this.groupBox2.Text = "Wizard"; + // + // customBtn + // + this.customBtn.Location = new System.Drawing.Point(7, 49); + this.customBtn.Name = "customBtn"; + this.customBtn.Size = new System.Drawing.Size(107, 23); + this.customBtn.TabIndex = 0; + this.customBtn.Text = "Custom Wizard"; + this.toolTip1.SetToolTip(this.customBtn, "Launches a custom wizard."); + this.customBtn.UseVisualStyleBackColor = true; + this.customBtn.Click += new System.EventHandler(this.customBtn_Click); + // + // stepBtn + // + this.stepBtn.Location = new System.Drawing.Point(149, 20); + this.stepBtn.Name = "stepBtn"; + this.stepBtn.Size = new System.Drawing.Size(107, 23); + this.stepBtn.TabIndex = 0; + this.stepBtn.Text = "Step Wizard"; + this.toolTip1.SetToolTip(this.stepBtn, "Lauches a modification of the Aero Wizard that includes a checked step list on th" + + "e left."); + this.stepBtn.UseVisualStyleBackColor = true; + this.stepBtn.Click += new System.EventHandler(this.stepBtn_Click); + // + // wizBtn + // + this.wizBtn.Location = new System.Drawing.Point(7, 20); + this.wizBtn.Name = "wizBtn"; + this.wizBtn.Size = new System.Drawing.Size(107, 23); + this.wizBtn.TabIndex = 0; + this.wizBtn.Text = "Aero Wizard"; + this.toolTip1.SetToolTip(this.wizBtn, "Launches a wizard using the Aero Wizard styling defined for Operating Systems aft" + + "er Windows Vista."); + this.wizBtn.UseVisualStyleBackColor = true; + this.wizBtn.Click += new System.EventHandler(this.wizBtn_Click); + // + // oldButton + // + this.oldButton.Location = new System.Drawing.Point(149, 49); + this.oldButton.Name = "oldButton"; + this.oldButton.Size = new System.Drawing.Size(107, 23); + this.oldButton.TabIndex = 0; + this.oldButton.Text = "Old Wizard"; + this.oldButton.UseVisualStyleBackColor = true; + this.oldButton.Click += new System.EventHandler(this.oldButton_Click); + // + // Main + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(292, 180); + this.Controls.Add(this.groupBox2); + this.Controls.Add(this.groupBox1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.Name = "Main"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "Wizard Test"; + this.Load += new System.EventHandler(this.Main_Load); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.groupBox2.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.CheckBox appRenderVS; + private System.Windows.Forms.CheckBox compEnabledCheck; + private System.Windows.Forms.CheckBox vsEnabledByUser; + private System.Windows.Forms.CheckBox vsOnOS; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.GroupBox groupBox2; + private System.Windows.Forms.Button customBtn; + private System.Windows.Forms.Button stepBtn; + private System.Windows.Forms.Button wizBtn; + private System.Windows.Forms.ToolTip toolTip1; + private System.Windows.Forms.Button oldButton; + + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/TestWizard/Main.cs b/AeroWizard/AeroWizard/TestWizard/Main.cs new file mode 100644 index 0000000..2b4bc5a --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/Main.cs @@ -0,0 +1,61 @@ +using System; +using System.Windows.Forms; +using Vanara.Interop.DesktopWindowManager; + +namespace TestWizard +{ + public partial class Main : Form + { + public Main() + { + InitializeComponent(); + } + + private void Main_Load(object sender, EventArgs e) + { + compEnabledCheck.AutoCheck = Environment.OSVersion.Version < new Version(6, 2) && Environment.OSVersion.Version >= new Version(6, 0); + UpdateChecks(); + DesktopWindowManager.CompositionChanged += DesktopWindowManager_CompositionChanged; + //new MyWizard().ShowDialog(this); + //Close(); + } + + private void DesktopWindowManager_CompositionChanged(object sender, EventArgs e) + { + UpdateChecks(); + } + + private void UpdateChecks() + { + appRenderVS.Checked = Application.RenderWithVisualStyles; + compEnabledCheck.Checked = DesktopWindowManager.CompositionEnabled; + vsEnabledByUser.Checked = System.Windows.Forms.VisualStyles.VisualStyleInformation.IsEnabledByUser; + vsOnOS.Checked = System.Windows.Forms.VisualStyles.VisualStyleInformation.IsSupportedByOS; + } + + private void wizBtn_Click(object sender, EventArgs e) + { + new MyWizard().ShowDialog(this); + } + + private void stepBtn_Click(object sender, EventArgs e) + { + new MyStepWizard().ShowDialog(this); + } + + private void customBtn_Click(object sender, EventArgs e) + { + new TestWizBase().ShowDialog(this); + } + + private void compEnabledCheck_CheckedChanged(object sender, EventArgs e) + { + DesktopWindowManager.CompositionEnabled = compEnabledCheck.Checked; + } + + private void oldButton_Click(object sender, EventArgs e) + { + new OldStyleWizard().ShowDialog(this); + } + } +} diff --git a/AeroWizard/AeroWizard/TestWizard/Main.resx b/AeroWizard/AeroWizard/TestWizard/Main.resx new file mode 100644 index 0000000..9d9bc4d --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/Main.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/AeroWizard/AeroWizard/TestWizard/MyStepWizard.Designer.cs b/AeroWizard/AeroWizard/TestWizard/MyStepWizard.Designer.cs new file mode 100644 index 0000000..103e511 --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/MyStepWizard.Designer.cs @@ -0,0 +1,103 @@ +using AeroWizard; + +namespace TestWizard +{ + partial class MyStepWizard + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.wizardControl1 = new AeroWizard.StepWizardControl(); + this.wizardPage1 = new AeroWizard.WizardPage(); + this.wizardPage3 = new AeroWizard.WizardPage(); + this.wizardPage4 = new AeroWizard.WizardPage(); + this.wizardPage5 = new AeroWizard.WizardPage(); + ((System.ComponentModel.ISupportInitialize)(this.wizardControl1)).BeginInit(); + this.SuspendLayout(); + // + // wizardControl1 + // + this.wizardControl1.ClassicStyle = AeroWizard.WizardClassicStyle.Automatic; + this.wizardControl1.Location = new System.Drawing.Point(0, 0); + this.wizardControl1.Name = "wizardControl1"; + this.wizardControl1.Pages.Add(this.wizardPage1); + this.wizardControl1.Pages.Add(this.wizardPage3); + this.wizardControl1.Pages.Add(this.wizardPage4); + this.wizardControl1.Pages.Add(this.wizardPage5); + this.wizardControl1.Size = new System.Drawing.Size(768, 482); + this.wizardControl1.StepListFont = new System.Drawing.Font("Segoe UI", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.World); + this.wizardControl1.TabIndex = 0; + // + // wizardPage1 + // + this.wizardPage1.Name = "wizardPage1"; + this.wizardPage1.Size = new System.Drawing.Size(570, 330); + this.wizardPage1.TabIndex = 0; + this.wizardPage1.Text = "Page 1"; + // + // wizardPage3 + // + this.wizardPage3.Name = "wizardPage3"; + this.wizardPage3.Size = new System.Drawing.Size(570, 329); + this.wizardPage3.TabIndex = 2; + this.wizardPage3.Text = "Page 3"; + // + // wizardPage4 + // + this.wizardPage4.Name = "wizardPage4"; + this.wizardPage4.Size = new System.Drawing.Size(570, 329); + this.wizardPage4.TabIndex = 3; + this.wizardPage4.Text = "Page 4"; + // + // wizardPage5 + // + this.wizardPage5.Name = "wizardPage5"; + this.wizardPage5.Size = new System.Drawing.Size(571, 327); + this.wizardPage5.TabIndex = 4; + this.wizardPage5.Text = "Page 5"; + // + // MyStepWizard + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(768, 482); + this.Controls.Add(this.wizardControl1); + this.Name = "MyStepWizard"; + this.Text = "MyStepWizard"; + ((System.ComponentModel.ISupportInitialize)(this.wizardControl1)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private AeroWizard.StepWizardControl wizardControl1; + private AeroWizard.WizardPage wizardPage1; + private AeroWizard.WizardPage wizardPage3; + private AeroWizard.WizardPage wizardPage4; + private AeroWizard.WizardPage wizardPage5; + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/TestWizard/MyStepWizard.cs b/AeroWizard/AeroWizard/TestWizard/MyStepWizard.cs new file mode 100644 index 0000000..84e2266 --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/MyStepWizard.cs @@ -0,0 +1,27 @@ +using AeroWizard; + +using System.Windows.Forms; + +namespace TestWizard +{ + public partial class MyStepWizard : Form + { + System.Drawing.Font myFont = new System.Drawing.Font("Impact", 20); + + public MyStepWizard() + { + InitializeComponent(); + } + + private void wizardControl1_DrawStepListItem(object sender, AeroWizard.DrawStepListItemEventArgs e) + { + string pre = e.Selected ? "> " : e.Completed ? "- " : "+ "; + TextRenderer.DrawText(e.Graphics, pre + wizardControl1.GetStepText(e.Item), myFont, e.Bounds, ForeColor); + } + + private void wizardControl1_MeasureStepListItem(object sender, AeroWizard.MeasureStepListItemEventArgs e) + { + e.ItemSize = new System.Drawing.Size(e.ItemSize.Width, (int)(TextRenderer.MeasureText("Wg", myFont).Height * 1.2)); + } + } +} diff --git a/AeroWizard/AeroWizard/TestWizard/MyStepWizard.resx b/AeroWizard/AeroWizard/TestWizard/MyStepWizard.resx new file mode 100644 index 0000000..7080a7d --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/MyStepWizard.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/AeroWizard/AeroWizard/TestWizard/MyWizard.Designer.cs b/AeroWizard/AeroWizard/TestWizard/MyWizard.Designer.cs new file mode 100644 index 0000000..fcf3f89 --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/MyWizard.Designer.cs @@ -0,0 +1,249 @@ +using AeroWizard; + +namespace TestWizard +{ + partial class MyWizard + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.wizardControl1 = new AeroWizard.WizardControl(); + this.introPage = new AeroWizard.WizardPage(); + this.button2 = new System.Windows.Forms.Button(); + this.button1 = new System.Windows.Forms.Button(); + this.suppressedPage = new AeroWizard.WizardPage(); + this.label2 = new System.Windows.Forms.Label(); + this.questionPage = new AeroWizard.WizardPage(); + this.checkBox2 = new System.Windows.Forms.CheckBox(); + this.middlePage = new AeroWizard.WizardPage(); + this.checkBox1 = new System.Windows.Forms.CheckBox(); + this.linkLabel1 = new System.Windows.Forms.LinkLabel(); + this.endPage = new AeroWizard.WizardPage(); + this.label1 = new System.Windows.Forms.Label(); + this.progressBar1 = new System.Windows.Forms.ProgressBar(); + ((System.ComponentModel.ISupportInitialize)(this.wizardControl1)).BeginInit(); + this.introPage.SuspendLayout(); + this.suppressedPage.SuspendLayout(); + this.questionPage.SuspendLayout(); + this.middlePage.SuspendLayout(); + this.endPage.SuspendLayout(); + this.SuspendLayout(); + // + // wizardControl1 + // + this.wizardControl1.ClassicStyle = AeroWizard.WizardClassicStyle.Automatic; + this.wizardControl1.Location = new System.Drawing.Point(0, 0); + this.wizardControl1.Name = "wizardControl1"; + this.wizardControl1.Pages.Add(this.introPage); + this.wizardControl1.Pages.Add(this.suppressedPage); + this.wizardControl1.Pages.Add(this.questionPage); + this.wizardControl1.Pages.Add(this.middlePage); + this.wizardControl1.Pages.Add(this.endPage); + this.wizardControl1.ShowProgressInTaskbarIcon = true; + this.wizardControl1.Size = new System.Drawing.Size(574, 415); + this.wizardControl1.TabIndex = 0; + this.wizardControl1.Title = "Modify System"; + // + // introPage + // + this.introPage.AllowNext = false; + this.introPage.Controls.Add(this.button2); + this.introPage.Controls.Add(this.button1); + this.introPage.HelpText = "Do you hate this?"; + this.introPage.Name = "introPage"; + this.introPage.Size = new System.Drawing.Size(527, 263); + this.introPage.TabIndex = 0; + this.introPage.Text = "Choose an activity"; + this.introPage.HelpClicked += new System.EventHandler(this.introPage_HelpClicked); + this.introPage.Initialize += new System.EventHandler(this.introPage_Initialize); + // + // button2 + // + this.button2.Location = new System.Drawing.Point(3, 32); + this.button2.Name = "button2"; + this.button2.Size = new System.Drawing.Size(227, 23); + this.button2.TabIndex = 1; + this.button2.Text = "Trash everything"; + this.button2.UseVisualStyleBackColor = true; + this.button2.Click += new System.EventHandler(this.commandLink2_Click); + // + // button1 + // + this.button1.Location = new System.Drawing.Point(3, 3); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(227, 23); + this.button1.TabIndex = 0; + this.button1.Text = "Cleanup system"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.commandLink1_Click); + // + // suppressedPage + // + this.suppressedPage.Controls.Add(this.label2); + this.suppressedPage.Name = "suppressedPage"; + this.suppressedPage.Size = new System.Drawing.Size(527, 263); + this.suppressedPage.Suppress = true; + this.suppressedPage.TabIndex = 3; + this.suppressedPage.Text = "Suppressed"; + // + // label2 + // + this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(188, 113); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(139, 15); + this.label2.TabIndex = 0; + this.label2.Text = "You should never see me"; + // + // questionPage + // + this.questionPage.AllowNext = false; + this.questionPage.Controls.Add(this.checkBox2); + this.questionPage.Name = "questionPage"; + this.questionPage.Size = new System.Drawing.Size(552, 258); + this.questionPage.TabIndex = 4; + this.questionPage.Text = "Are you sure?"; + // + // checkBox2 + // + this.checkBox2.AutoSize = true; + this.checkBox2.Location = new System.Drawing.Point(3, 0); + this.checkBox2.Name = "checkBox2"; + this.checkBox2.Size = new System.Drawing.Size(44, 19); + this.checkBox2.TabIndex = 0; + this.checkBox2.Text = "Yes"; + this.checkBox2.UseVisualStyleBackColor = true; + this.checkBox2.CheckedChanged += new System.EventHandler(this.checkBox2_CheckedChanged); + // + // middlePage + // + this.middlePage.Controls.Add(this.checkBox1); + this.middlePage.Controls.Add(this.linkLabel1); + this.middlePage.IsFinishPage = true; + this.middlePage.Name = "middlePage"; + this.middlePage.ShowCancel = false; + this.middlePage.ShowNext = false; + this.middlePage.Size = new System.Drawing.Size(552, 258); + this.middlePage.TabIndex = 1; + this.middlePage.Text = "Launch System Cleanup"; + this.middlePage.Initialize += new System.EventHandler(this.middlePage_Initialize); + // + // checkBox1 + // + this.checkBox1.AutoSize = true; + this.checkBox1.Location = new System.Drawing.Point(3, 31); + this.checkBox1.Name = "checkBox1"; + this.checkBox1.Size = new System.Drawing.Size(233, 19); + this.checkBox1.TabIndex = 1; + this.checkBox1.Text = "Desktop Window Composition Enabled"; + this.checkBox1.UseVisualStyleBackColor = true; + this.checkBox1.CheckedChanged += new System.EventHandler(this.checkBox1_CheckedChanged); + // + // linkLabel1 + // + this.linkLabel1.AutoSize = true; + this.linkLabel1.Location = new System.Drawing.Point(0, 0); + this.linkLabel1.Name = "linkLabel1"; + this.linkLabel1.Size = new System.Drawing.Size(116, 15); + this.linkLabel1.TabIndex = 0; + this.linkLabel1.TabStop = true; + this.linkLabel1.Text = "Launch SysClean.exe"; + // + // endPage + // + this.endPage.Controls.Add(this.label1); + this.endPage.Controls.Add(this.progressBar1); + this.endPage.Name = "endPage"; + this.endPage.Size = new System.Drawing.Size(527, 259); + this.endPage.TabIndex = 2; + this.endPage.Text = "Bad Choice"; + this.endPage.Initialize += new System.EventHandler(this.endPage_Initialize); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(0, 0); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(185, 15); + this.label1.TabIndex = 1; + this.label1.Text = "Hosing your system. Please wait..."; + // + // progressBar1 + // + this.progressBar1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.progressBar1.Location = new System.Drawing.Point(3, 28); + this.progressBar1.Name = "progressBar1"; + this.progressBar1.Size = new System.Drawing.Size(370, 23); + this.progressBar1.Style = System.Windows.Forms.ProgressBarStyle.Marquee; + this.progressBar1.TabIndex = 0; + // + // MyWizard + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(574, 415); + this.Controls.Add(this.wizardControl1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.Name = "MyWizard"; + this.RightToLeftLayout = true; + ((System.ComponentModel.ISupportInitialize)(this.wizardControl1)).EndInit(); + this.introPage.ResumeLayout(false); + this.introPage.PerformLayout(); + this.suppressedPage.ResumeLayout(false); + this.suppressedPage.PerformLayout(); + this.questionPage.ResumeLayout(false); + this.questionPage.PerformLayout(); + this.middlePage.ResumeLayout(false); + this.middlePage.PerformLayout(); + this.endPage.ResumeLayout(false); + this.endPage.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private AeroWizard.WizardControl wizardControl1; + private AeroWizard.WizardPage introPage; + private AeroWizard.WizardPage middlePage; + private AeroWizard.WizardPage endPage; + private System.Windows.Forms.LinkLabel linkLabel1; + private System.Windows.Forms.ProgressBar progressBar1; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.CheckBox checkBox1; + private System.Windows.Forms.Button button2; + private System.Windows.Forms.Button button1; + private AeroWizard.WizardPage suppressedPage; + private System.Windows.Forms.Label label2; + private AeroWizard.WizardPage questionPage; + private System.Windows.Forms.CheckBox checkBox2; + } +} + diff --git a/AeroWizard/AeroWizard/TestWizard/MyWizard.cs b/AeroWizard/AeroWizard/TestWizard/MyWizard.cs new file mode 100644 index 0000000..5e70698 --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/MyWizard.cs @@ -0,0 +1,146 @@ +using Vanara.Interop.DesktopWindowManager; +using AeroWizard; +using System.Windows.Forms; + +namespace TestWizard +{ + public partial class MyWizard : Form + { + private readonly Button extraBtn; + private readonly System.Text.StringBuilder events = new System.Text.StringBuilder(1024); + + public MyWizard() + { + InitializeComponent(); + //this.wizardControl1.TitleIcon = null; + foreach (var i in wizardControl1.Pages) + i.Commit += new System.EventHandler(i_Commit); + wizardControl1.Finished += new System.EventHandler(wizardControl1_Finished); + extraBtn = new Button { Text = "Events", AutoSize = true, AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink, Anchor = AnchorStyles.Top | AnchorStyles.Right, Margin = Padding.Empty }; + wizardControl1.AddCommandControl(extraBtn); + extraBtn.Click += extraBtn_Click; + SystemColorsChanged += MyWizard_SystemColorsChanged; + StyleChanged += MyWizard_StyleChanged; + if (System.Environment.OSVersion.Version.Major >= 6) + { + DesktopWindowManager.ColorizationColorChanged += DesktopWindowManager_ColorizationColorChanged; + DesktopWindowManager.CompositionChanged += DesktopWindowManager_CompositionChanged; + } + Microsoft.Win32.SystemEvents.DisplaySettingsChanged += SystemEvents_DisplaySettingsChanged; + } + + void MyWizard_StyleChanged(object sender, System.EventArgs e) + { + bool ncre = false, clk = false; Padding cbb = Padding.Empty, efb = Padding.Empty; + try + { + ncre = this.GetWindowAttribute(DesktopWindowManager.GetWindowAttr.NonClientRenderingEnabled); + efb = this.GetWindowAttribute(DesktopWindowManager.GetWindowAttr.ExtendedFrameBounds); + cbb = this.GetWindowAttribute(DesktopWindowManager.GetWindowAttr.CaptionButtonBounds); + clk = System.Environment.OSVersion.Version.Minor >= 2 && this.GetWindowAttribute(DesktopWindowManager.GetWindowAttr.Cloaked); + } + catch { } + events.AppendFormat("{0:s}: Style (NCRend:{1}, Clk:{2}, CapBtn:{3}, ExtFrm:{4}\n", System.DateTime.Now, ncre, clk, cbb, efb); + } + + void MyWizard_SystemColorsChanged(object sender, System.EventArgs e) + { + bool ncre = this.GetWindowAttribute(DesktopWindowManager.GetWindowAttr.NonClientRenderingEnabled); + bool clk = System.Environment.OSVersion.Version.Minor >= 2 && this.GetWindowAttribute(DesktopWindowManager.GetWindowAttr.Cloaked); + Padding cbb = this.GetWindowAttribute(DesktopWindowManager.GetWindowAttr.CaptionButtonBounds); + Padding efb = this.GetWindowAttribute(DesktopWindowManager.GetWindowAttr.ExtendedFrameBounds); + events.AppendFormat("{0:s}: System colors (NCRend:{1}, Clk:{2}, CapBtn:{3}, ExtFrm:{4}\n", System.DateTime.Now, ncre, clk, cbb, efb); + } + + protected override void OnClosing(System.ComponentModel.CancelEventArgs e) + { + base.OnClosing(e); + if (System.Environment.OSVersion.Version.Major >= 6) + { + DesktopWindowManager.ColorizationColorChanged -= DesktopWindowManager_ColorizationColorChanged; + DesktopWindowManager.CompositionChanged -= DesktopWindowManager_CompositionChanged; + } + Microsoft.Win32.SystemEvents.DisplaySettingsChanged -= SystemEvents_DisplaySettingsChanged; + } + + void extraBtn_Click(object sender, System.EventArgs e) + { + MessageBox.Show(events.ToString()); + } + + void SystemEvents_DisplaySettingsChanged(object sender, System.EventArgs e) + { + events.AppendFormat("{0:s}: Display settings\n", System.DateTime.Now); + } + + void DesktopWindowManager_CompositionChanged(object sender, System.EventArgs e) + { + events.AppendFormat("{0:s}: Composition ({1})\n", System.DateTime.Now, DesktopWindowManager.IsCompositionEnabled() ? "On" : "Off"); + } + + void DesktopWindowManager_ColorizationColorChanged(object sender, System.EventArgs e) + { + events.AppendFormat("{0:s}: Colorization color (0x{1:x})\n", System.DateTime.Now, DesktopWindowManager.CompositionColor.ToArgb()); + } + + void wizardControl1_Finished(object sender, System.EventArgs e) + { + System.Diagnostics.Debug.WriteLine("--> Wizard finished."); + } + + void i_Commit(object sender, AeroWizard.WizardPageConfirmEventArgs e) + { + System.Diagnostics.Debug.WriteLine($"--> Page {e.Page.Name} committed."); + } + + private void commandLink1_Click(object sender, System.EventArgs e) + { + wizardControl1.NextPage(); + } + + private void commandLink2_Click(object sender, System.EventArgs e) + { + wizardControl1.NextPage(endPage); + } + + private void checkBox1_CheckedChanged(object sender, System.EventArgs e) + { + if (!initMiddle) + DesktopWindowManager.EnableComposition(checkBox1.Checked); + } + + private bool initMiddle = false; + + private void middlePage_Initialize(object sender, AeroWizard.WizardPageInitEventArgs e) + { + initMiddle = true; + if (!System.IO.File.Exists(System.IO.Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.System), "dwmapi.dll"))) + checkBox1.Enabled = false; + else + checkBox1.Checked = DesktopWindowManager.IsCompositionEnabled(); + wizardControl1.FinishButtonText = "Finish"; + initMiddle = false; + } + + private void checkBox2_CheckedChanged(object sender, System.EventArgs e) + { + wizardControl1.SelectedPage.AllowNext = checkBox2.Checked; + wizardControl1.SelectedPage.AllowBack = !checkBox2.Checked; + } + + private void endPage_Initialize(object sender, AeroWizard.WizardPageInitEventArgs e) + { + wizardControl1.FinishButtonText = "Sorry, but you are hosed."; + } + + private void introPage_HelpClicked(object sender, System.EventArgs e) + { + MessageBox.Show("Clicked help"); + } + + private void introPage_Initialize(object sender, AeroWizard.WizardPageInitEventArgs e) + { + //MessageBox.Show("Page initialized"); + } + } +} diff --git a/AeroWizard/AeroWizard/TestWizard/MyWizard.resx b/AeroWizard/AeroWizard/TestWizard/MyWizard.resx new file mode 100644 index 0000000..19dc0dd --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/MyWizard.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/AeroWizard/AeroWizard/TestWizard/Native/CommandLink.cs b/AeroWizard/AeroWizard/TestWizard/Native/CommandLink.cs new file mode 100644 index 0000000..7b6fc5e --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/Native/CommandLink.cs @@ -0,0 +1,68 @@ +// Requires USER32.cs +using Microsoft.Win32; +using System.ComponentModel; +using System.Drawing; + +namespace System.Windows.Forms +{ + /// + /// Implements a CommandLink button that can be used in WinForms user interfaces. + /// + [System.Drawing.ToolboxBitmap(typeof(Button))] + public class CommandLink : VistaButtonBase + { + private string note = null; + + /// + /// Initializes a new instance of the class. + /// + public CommandLink() + { + } + + /// + /// Gets or sets the note text for the button. + /// + /// + /// The note text. + /// + [Category("Appearance"), Browsable(true), DefaultValue((string)null)] + public string Note + { + get { return note; } + set + { + const uint BCM_SETNOTE = 0x1609; + note = value; + NativeMethods.SendMessage(Handle, BCM_SETNOTE, IntPtr.Zero, note); + Invalidate(); + } + } + + /// + /// Gets a System.Windows.Forms.CreateParams on the base class when creating a window. + /// + /// + /// The create parameters. + /// + protected override CreateParams CreateParams + { + get + { + const int BS_COMMANDLINK = 0xE; + var cp = base.CreateParams; + if (IsPlatformSupported) + cp.Style |= BS_COMMANDLINK; + return cp; + } + } + + /// + /// Gets the default size. + /// + /// + /// The default size. + /// + protected override Drawing.Size DefaultSize => new Drawing.Size(135, 60); + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/TestWizard/Native/RECT.cs b/AeroWizard/AeroWizard/TestWizard/Native/RECT.cs new file mode 100644 index 0000000..9c9e28d --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/Native/RECT.cs @@ -0,0 +1,85 @@ +using System.Runtime.InteropServices; + +namespace Microsoft.Win32 +{ + internal static partial class NativeMethods + { + [StructLayout(LayoutKind.Sequential)] + public struct RECT + { + public int Left, Top, Right, Bottom; + + public RECT(int left, int top, int right, int bottom) + { + Left = left; + Top = top; + Right = right; + Bottom = bottom; + } + + public RECT(System.Drawing.Rectangle r) + : this(r.Left, r.Top, r.Right, r.Bottom) + { + } + + public int X + { + get { return Left; } + set { Right -= (Left - value); Left = value; } + } + + public int Y + { + get { return Top; } + set { Bottom -= (Top - value); Top = value; } + } + + public int Height + { + get { return Bottom - Top; } + set { Bottom = value + Top; } + } + + public int Width + { + get { return Right - Left; } + set { Right = value + Left; } + } + + public System.Drawing.Point Location + { + get { return new System.Drawing.Point(Left, Top); } + set { X = value.X; Y = value.Y; } + } + + public System.Drawing.Size Size + { + get { return new System.Drawing.Size(Width, Height); } + set { Width = value.Width; Height = value.Height; } + } + + public static implicit operator System.Drawing.Rectangle(RECT r) => new System.Drawing.Rectangle(r.Left, r.Top, r.Width, r.Height); + + public static implicit operator RECT(System.Drawing.Rectangle r) => new RECT(r); + + public static bool operator ==(RECT r1, RECT r2) => r1.Equals(r2); + + public static bool operator !=(RECT r1, RECT r2) => !r1.Equals(r2); + + public bool Equals(RECT r) => r.Left == Left && r.Top == Top && r.Right == Right && r.Bottom == Bottom; + + public override bool Equals(object obj) + { + if (obj is RECT) + return Equals((RECT)obj); + else if (obj is System.Drawing.Rectangle) + return Equals(new RECT((System.Drawing.Rectangle)obj)); + return false; + } + + public override int GetHashCode() => ((System.Drawing.Rectangle)this).GetHashCode(); + + public override string ToString() => $"{{Left={Left},Top={Top},Right={Right},Bottom={Bottom}}}"; + } + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/TestWizard/Native/USER32.cs b/AeroWizard/AeroWizard/TestWizard/Native/USER32.cs new file mode 100644 index 0000000..dfd9544 --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/Native/USER32.cs @@ -0,0 +1,198 @@ +using System; +using System.Runtime.InteropServices; + +namespace Microsoft.Win32 +{ + internal static partial class NativeMethods + { + internal const string USER32 = "user32.dll"; + + [DllImport(USER32, CharSet = CharSet.Unicode, ExactSpelling = true)] + public static extern IntPtr GetActiveWindow(); + + [DllImport(USER32, CharSet = CharSet.Unicode, ExactSpelling = true)] + public static extern IntPtr ChildWindowFromPointEx(IntPtr hwndParent, System.Drawing.Point pt, System.Windows.Forms.GetChildAtPointSkip uFlags); + + [DllImport(USER32, CharSet = CharSet.Auto, ExactSpelling = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetClientRect(IntPtr hWnd, [In, Out] ref NativeMethods.RECT rect); + + public static IntPtr GetWindowLong(IntPtr hWnd, int nIndex) + { + if (IntPtr.Size == 4) + return GetWindowLong32(hWnd, nIndex); + return GetWindowLongPtr64(hWnd, nIndex); + } + + [DllImport(USER32, EntryPoint = "GetWindowLong", CharSet = CharSet.Auto)] + public static extern IntPtr GetWindowLong32(IntPtr hWnd, int nIndex); + + [DllImport(USER32, EntryPoint = "GetWindowLongPtr", CharSet = CharSet.Auto)] + public static extern IntPtr GetWindowLongPtr64(IntPtr hWnd, int nIndex); + + [DllImport(USER32, ExactSpelling = true, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); + + [DllImport(USER32, ExactSpelling = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool InvalidateRect(IntPtr hWnd, [In] ref NativeMethods.RECT rect, [MarshalAs(UnmanagedType.Bool)] bool bErase); + + [DllImport(USER32, ExactSpelling = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool InvalidateRect(IntPtr hWnd, IntPtr rect, [MarshalAs(UnmanagedType.Bool)] bool bErase); + + [DllImport(USER32, ExactSpelling = true, CharSet = CharSet.Auto)] + public static extern int LoadString(IntPtr hInstance, int uID, out IntPtr lpBuffer, int nBufferMax); + + [DllImport(USER32, CharSet = CharSet.Auto, ExactSpelling = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool ScreenToClient(IntPtr hWnd, [In, Out] ref System.Drawing.Point lpPoint); + + [DllImport(USER32, CharSet = CharSet.Unicode, SetLastError = false)] + public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); + + [DllImport(USER32, CharSet = CharSet.Unicode, SetLastError = false)] + public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, int wParam, IntPtr lParam); + + [DllImport(USER32, CharSet = CharSet.Unicode, SetLastError = false)] + public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, int wParam, int lParam); + + public static IntPtr SendMessage(IntPtr hWnd, uint Msg, int wParam = 0) => SendMessage(hWnd, Msg, wParam, 0); + + [DllImport(USER32, CharSet = CharSet.Unicode, SetLastError = false)] + public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, ref RECT rect); + + [DllImport(USER32, CharSet = CharSet.Unicode, SetLastError = false)] + public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, [In, MarshalAs(UnmanagedType.LPWStr)] string lParam); + + [DllImport(USER32, CharSet = CharSet.Unicode, SetLastError = false)] + public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, ref int wParam, [In, Out, MarshalAs(UnmanagedType.LPWStr)] System.Text.StringBuilder lParam); + + public static IntPtr SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong) + { + if (IntPtr.Size == 4) + return SetWindowLongPtr32(hWnd, nIndex, dwNewLong); + return SetWindowLongPtr64(hWnd, nIndex, dwNewLong); + } + + [DllImport(USER32, EntryPoint = "SetWindowLong", CharSet = CharSet.Auto)] + private static extern IntPtr SetWindowLongPtr32(IntPtr hWnd, int nIndex, IntPtr dwNewLong); + + [DllImport(USER32, EntryPoint = "SetWindowLongPtr", CharSet = CharSet.Auto)] + private static extern IntPtr SetWindowLongPtr64(IntPtr hWnd, int nIndex, IntPtr dwNewLong); + + /// + /// Special window handles + /// + public static class SpecialWindowHandles + { + /// Places the window at the top of the Z order. + /// HWND_TOP + public static IntPtr HwndTop = new IntPtr(0); + /// Places the window at the bottom of the Z order. If the hWnd parameter identifies a topmost window, the window loses its topmost status and is placed at the bottom of all other windows. + /// HWND_BOTTOM + public static IntPtr HwndBottom = new IntPtr(1); + /// Places the window above all non-topmost windows. The window maintains its topmost position even when it is deactivated. + /// HWND_TOPMOST + public static IntPtr HwndTopMost = new IntPtr(-1); + /// Places the window above all non-topmost windows (that is, behind all topmost windows). This flag has no effect if the window is already a non-topmost window. + /// HWND_NOTOPMOST + public static IntPtr HwndNoTopMost = new IntPtr(-2); + } + + /// + /// Window sizing and positioning flags. + /// + [Flags] + public enum SetWindowPosFlags : uint + { + /// If the calling thread and the thread that owns the window are attached to different input queues, + /// the system posts the request to the thread that owns the window. This prevents the calling thread from + /// blocking its execution while other threads process the request. + /// SWP_ASYNCWINDOWPOS + AsynchronousWindowPosition = 0x4000, + /// Prevents generation of the WM_SYNCPAINT message. + /// SWP_DEFERERASE + DeferErase = 0x2000, + /// Draws a frame (defined in the window's class description) around the window. + /// SWP_DRAWFRAME + DrawFrame = 0x0020, + /// Applies new frame styles set using the SetWindowLong function. Sends a WM_NCCALCSIZE message to + /// the window, even if the window's size is not being changed. If this flag is not specified, WM_NCCALCSIZE + /// is sent only when the window's size is being changed. + /// SWP_FRAMECHANGED + FrameChanged = 0x0020, + /// Hides the window. + /// SWP_HIDEWINDOW + HideWindow = 0x0080, + /// Does not activate the window. If this flag is not set, the window is activated and moved to the + /// top of either the topmost or non-topmost group (depending on the setting of the hWndInsertAfter + /// parameter). + /// SWP_NOACTIVATE + DoNotActivate = 0x0010, + /// Discards the entire contents of the client area. If this flag is not specified, the valid + /// contents of the client area are saved and copied back into the client area after the window is sized or + /// repositioned. + /// SWP_NOCOPYBITS + DoNotCopyBits = 0x0100, + /// Retains the current position (ignores X and Y parameters). + /// SWP_NOMOVE + IgnoreMove = 0x0002, + /// Does not change the owner window's position in the Z order. + /// SWP_NOOWNERZORDER + DoNotChangeOwnerZOrder = 0x0200, + /// Does not redraw changes. If this flag is set, no repainting of any kind occurs. This applies to + /// the client area, the nonclient area (including the title bar and scroll bars), and any part of the parent + /// window uncovered as a result of the window being moved. When this flag is set, the application must + /// explicitly invalidate or redraw any parts of the window and parent window that need redrawing. + /// SWP_NOREDRAW + DoNotRedraw = 0x0008, + /// Same as the SWP_NOOWNERZORDER flag. + /// SWP_NOREPOSITION + DoNotReposition = 0x0200, + /// Prevents the window from receiving the WM_WINDOWPOSCHANGING message. + /// SWP_NOSENDCHANGING + DoNotSendChangingEvent = 0x0400, + /// Retains the current size (ignores the cx and cy parameters). + /// SWP_NOSIZE + IgnoreResize = 0x0001, + /// Retains the current Z order (ignores the hWndInsertAfter parameter). + /// SWP_NOZORDER + IgnoreZOrder = 0x0004, + /// Displays the window. + /// SWP_SHOWWINDOW + ShowWindow = 0x0040, + } + + [DllImport(USER32, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, SetWindowPosFlags uFlags); + + [DllImport(USER32, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SetWindowText(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)] string lpString); + + [StructLayout(LayoutKind.Sequential)] + public struct WINDOWPOS + { + public IntPtr hwnd; + public IntPtr hwndInsertAfter; + public int x; + public int y; + public int cx; + public int cy; + public int flags; + } + + [StructLayout(LayoutKind.Sequential)] + public struct NMHDR + { + public IntPtr hwndFrom; + public IntPtr idFrom; + public int code; + + public override string ToString() => $"hwndFrom:{hwndFrom}; idFrom:{idFrom}; code:{code}"; + } + } +} diff --git a/AeroWizard/AeroWizard/TestWizard/Native/VistaButtonBase.cs b/AeroWizard/AeroWizard/TestWizard/Native/VistaButtonBase.cs new file mode 100644 index 0000000..f4b34f6 --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/Native/VistaButtonBase.cs @@ -0,0 +1,136 @@ +// Requires USER32.cs +using Microsoft.Win32; +using System.ComponentModel; +using System.Drawing; + +namespace System.Windows.Forms +{ + /// + /// Implements a CommandLink button that can be used in WinForms user interfaces. + /// + [System.Drawing.ToolboxBitmap(typeof(Button))] + public abstract class VistaButtonBase : Button + { + private Icon icon; + private bool showShield = false; + + /// + /// Initializes a new instance of the class. + /// + public VistaButtonBase() + { + base.FlatStyle = Forms.FlatStyle.System; + } + + /// + /// Gets or sets the flat style. + /// + /// + /// The flat style. + /// + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DefaultValue(typeof(Forms.FlatStyle), "System")] + public new Forms.FlatStyle FlatStyle + { + get { return base.FlatStyle; } + set { base.FlatStyle = value; } + } + + /// + /// Gets or sets the icon that is displayed on a button control. + /// + [Description("Gets or sets the icon that is displayed on a button control."), Category("Appearance"), DefaultValue(null)] + public Icon Icon + { + get { return icon; } + set + { + icon = value; + if (value != null) + Image = null; + ShowShield = false; + SetImage(); + } + } + + /// + /// Gets or sets the image that is displayed on a button control. + /// + /// + /// + /// + /// + /// + /// + [Description("Gets or sets the image that is displayed on a button control."), Category("Appearance"), DefaultValue(null)] + public new Image Image + { + get { return base.Image; } + set + { + base.Image = value; + if (value != null) + Icon = null; + ShowShield = false; + SetImage(); + } + } + + /// + /// Gets or sets a value indicating whether to display an elevated shield icon. + /// + /// + /// true if showing shield icon; otherwise, false. + /// + [Description("Gets or sets whether if the control should use an elevated shield icon."), Category("Appearance"), DefaultValue(false)] + public bool ShowShield + { + get { return showShield; } + set + { + if (showShield != value && IsHandleCreated) + { + showShield = value; + SetShield(value); + } + } + } + + internal static bool IsPlatformSupported => Environment.OSVersion.Version.Major >= 6; + + /// + /// Raises the event. + /// + /// An that contains the event data. + protected override void OnHandleCreated(EventArgs e) + { + base.OnHandleCreated(e); + SetShield(showShield); + SetImage(); + } + + /// + /// Refreshes the image displayed on the button + /// + private void SetImage() + { + if (IsHandleCreated) + { + IntPtr iconhandle = IntPtr.Zero; + if (Image != null) + iconhandle = new Bitmap(Image).GetHicon(); + else if (icon != null) + iconhandle = Icon.Handle; + + const int BM_SETIMAGE = 0xF7; + NativeMethods.SendMessage(Handle, BM_SETIMAGE, 1, iconhandle); + } + } + + private void SetShield(bool value) + { + const uint BCM_SETSHIELD = 0x160C; //Elevated button + NativeMethods.SendMessage(Handle, BCM_SETSHIELD, IntPtr.Zero, value ? new IntPtr(1) : IntPtr.Zero); + Invalidate(); + } + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/TestWizard/OldStyleWizard.Designer.cs b/AeroWizard/AeroWizard/TestWizard/OldStyleWizard.Designer.cs new file mode 100644 index 0000000..d214bcc --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/OldStyleWizard.Designer.cs @@ -0,0 +1,279 @@ +using AeroWizard; + +namespace TestWizard +{ + partial class OldStyleWizard + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.headerPanel = new System.Windows.Forms.Panel(); + this.headerImage = new System.Windows.Forms.PictureBox(); + this.subHeaderLabel = new System.Windows.Forms.Label(); + this.headerLabel = new System.Windows.Forms.Label(); + this.topDivider = new System.Windows.Forms.Label(); + this.bottomDivider = new System.Windows.Forms.Label(); + this.commandPanel = new System.Windows.Forms.Panel(); + this.backButton = new System.Windows.Forms.Button(); + this.nextButton = new System.Windows.Forms.Button(); + this.cancelButton = new System.Windows.Forms.Button(); + this.wizardPageContainer1 = new AeroWizard.WizardPageContainer(); + this.wizardPage2 = new AeroWizard.WizardPage(); + this.wizardPage3 = new AeroWizard.WizardPage(); + this.wizardPage1 = new AeroWizard.WizardPage(); + this.startEndPicture = new System.Windows.Forms.PictureBox(); + this.label1 = new System.Windows.Forms.Label(); + this.headerPanel.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.headerImage)).BeginInit(); + this.commandPanel.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.wizardPageContainer1)).BeginInit(); + this.wizardPageContainer1.SuspendLayout(); + this.wizardPage1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.startEndPicture)).BeginInit(); + this.SuspendLayout(); + // + // headerPanel + // + this.headerPanel.BackColor = System.Drawing.SystemColors.Window; + this.headerPanel.Controls.Add(this.headerImage); + this.headerPanel.Controls.Add(this.subHeaderLabel); + this.headerPanel.Controls.Add(this.headerLabel); + this.headerPanel.Dock = System.Windows.Forms.DockStyle.Top; + this.headerPanel.Location = new System.Drawing.Point(0, 0); + this.headerPanel.Name = "headerPanel"; + this.headerPanel.Size = new System.Drawing.Size(480, 57); + this.headerPanel.TabIndex = 2; + // + // headerImage + // + this.headerImage.Image = global::TestWizard.Properties.Resources.WizardHat_48; + this.headerImage.Location = new System.Drawing.Point(426, 4); + this.headerImage.Name = "headerImage"; + this.headerImage.Size = new System.Drawing.Size(49, 49); + this.headerImage.TabIndex = 1; + this.headerImage.TabStop = false; + // + // subHeaderLabel + // + this.subHeaderLabel.AutoSize = true; + this.subHeaderLabel.Location = new System.Drawing.Point(12, 31); + this.subHeaderLabel.Name = "subHeaderLabel"; + this.subHeaderLabel.Size = new System.Drawing.Size(74, 13); + this.subHeaderLabel.TabIndex = 0; + this.subHeaderLabel.Text = ""; + // + // headerLabel + // + this.headerLabel.AutoSize = true; + this.headerLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.headerLabel.Location = new System.Drawing.Point(12, 11); + this.headerLabel.Name = "headerLabel"; + this.headerLabel.Size = new System.Drawing.Size(62, 13); + this.headerLabel.TabIndex = 0; + this.headerLabel.Text = "
"; + // + // topDivider + // + this.topDivider.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.topDivider.Dock = System.Windows.Forms.DockStyle.Top; + this.topDivider.Location = new System.Drawing.Point(0, 57); + this.topDivider.Name = "topDivider"; + this.topDivider.Size = new System.Drawing.Size(480, 2); + this.topDivider.TabIndex = 3; + // + // bottomDivider + // + this.bottomDivider.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.bottomDivider.Dock = System.Windows.Forms.DockStyle.Bottom; + this.bottomDivider.Enabled = false; + this.bottomDivider.Location = new System.Drawing.Point(0, 313); + this.bottomDivider.Name = "bottomDivider"; + this.bottomDivider.Size = new System.Drawing.Size(480, 2); + this.bottomDivider.TabIndex = 4; + // + // commandPanel + // + this.commandPanel.Controls.Add(this.backButton); + this.commandPanel.Controls.Add(this.nextButton); + this.commandPanel.Controls.Add(this.cancelButton); + this.commandPanel.Dock = System.Windows.Forms.DockStyle.Bottom; + this.commandPanel.Location = new System.Drawing.Point(0, 315); + this.commandPanel.Name = "commandPanel"; + this.commandPanel.Size = new System.Drawing.Size(480, 40); + this.commandPanel.TabIndex = 5; + // + // backButton + // + this.backButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.backButton.Location = new System.Drawing.Point(171, 9); + this.backButton.Name = "backButton"; + this.backButton.Size = new System.Drawing.Size(97, 23); + this.backButton.TabIndex = 2; + this.backButton.Tag = AeroWizard.WizardCommandButtonState.Disabled; + this.backButton.Text = "< Back"; + this.backButton.UseVisualStyleBackColor = true; + // + // nextButton + // + this.nextButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.nextButton.Location = new System.Drawing.Point(270, 9); + this.nextButton.Name = "nextButton"; + this.nextButton.Size = new System.Drawing.Size(97, 23); + this.nextButton.TabIndex = 3; + this.nextButton.Tag = AeroWizard.WizardCommandButtonState.Enabled; + this.nextButton.Text = "Next >"; + this.nextButton.UseVisualStyleBackColor = true; + // + // cancelButton + // + this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(373, 9); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(97, 23); + this.cancelButton.TabIndex = 4; + this.cancelButton.Tag = AeroWizard.WizardCommandButtonState.Disabled; + this.cancelButton.Text = "Cancel"; + this.cancelButton.UseVisualStyleBackColor = true; + // + // wizardPageContainer1 + // + this.wizardPageContainer1.BackButton = this.backButton; + this.wizardPageContainer1.BackButtonText = "< Back"; + this.wizardPageContainer1.CancelButton = this.cancelButton; + this.wizardPageContainer1.CancelButtonText = "Cancel"; + this.wizardPageContainer1.Controls.Add(this.wizardPage1); + this.wizardPageContainer1.Controls.Add(this.wizardPage2); + this.wizardPageContainer1.Controls.Add(this.wizardPage3); + this.wizardPageContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.wizardPageContainer1.Location = new System.Drawing.Point(164, 59); + this.wizardPageContainer1.Name = "wizardPageContainer1"; + this.wizardPageContainer1.NextButton = this.nextButton; + this.wizardPageContainer1.Pages.Add(this.wizardPage1); + this.wizardPageContainer1.Pages.Add(this.wizardPage2); + this.wizardPageContainer1.Pages.Add(this.wizardPage3); + this.wizardPageContainer1.Size = new System.Drawing.Size(316, 254); + this.wizardPageContainer1.TabIndex = 0; + this.wizardPageContainer1.Finished += new System.EventHandler(this.wizardPageContainer1_Finished); + this.wizardPageContainer1.SelectedPageChanged += new System.EventHandler(this.wizardPageContainer1_SelectedPageChanged); + // + // wizardPage2 + // + this.wizardPage2.Name = "wizardPage2"; + this.wizardPage2.Size = new System.Drawing.Size(316, 254); + this.wizardPage2.TabIndex = 1; + this.wizardPage2.Tag = ""; + this.wizardPage2.Text = "Page 2 - Middle|This is the middle page"; + this.wizardPage2.Initialize += new System.EventHandler(this.wizardPage2_Initialize); + // + // wizardPage3 + // + this.wizardPage3.Name = "wizardPage3"; + this.wizardPage3.Size = new System.Drawing.Size(316, 254); + this.wizardPage3.TabIndex = 2; + this.wizardPage3.Tag = ""; + this.wizardPage3.Text = "Task Completed|You\'re all done!"; + // + // wizardPage1 + // + this.wizardPage1.Controls.Add(this.label1); + this.wizardPage1.Name = "wizardPage1"; + this.wizardPage1.Size = new System.Drawing.Size(316, 254); + this.wizardPage1.TabIndex = 0; + this.wizardPage1.Text = "Welcom"; + this.wizardPage1.Initialize += new System.EventHandler(this.wizardPage1_Initialize); + // + // startEndPicture + // + this.startEndPicture.BackColor = System.Drawing.Color.Navy; + this.startEndPicture.BackgroundImage = global::TestWizard.Properties.Resources.WizardHat_48; + this.startEndPicture.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Zoom; + this.startEndPicture.Dock = System.Windows.Forms.DockStyle.Left; + this.startEndPicture.Location = new System.Drawing.Point(0, 59); + this.startEndPicture.Name = "startEndPicture"; + this.startEndPicture.Size = new System.Drawing.Size(164, 254); + this.startEndPicture.TabIndex = 6; + this.startEndPicture.TabStop = false; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(20, 17); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(95, 13); + this.label1.TabIndex = 0; + this.label1.Text = ""; + // + // OldStyleWizard + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancelButton; + this.ClientSize = new System.Drawing.Size(480, 355); + this.Controls.Add(this.wizardPageContainer1); + this.Controls.Add(this.startEndPicture); + this.Controls.Add(this.bottomDivider); + this.Controls.Add(this.commandPanel); + this.Controls.Add(this.topDivider); + this.Controls.Add(this.headerPanel); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "OldStyleWizard"; + this.Text = "OldStyleWizard"; + this.headerPanel.ResumeLayout(false); + this.headerPanel.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.headerImage)).EndInit(); + this.commandPanel.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.wizardPageContainer1)).EndInit(); + this.wizardPageContainer1.ResumeLayout(false); + this.wizardPage1.ResumeLayout(false); + this.wizardPage1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.startEndPicture)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private AeroWizard.WizardPageContainer wizardPageContainer1; + private System.Windows.Forms.Panel headerPanel; + private System.Windows.Forms.Label topDivider; + private System.Windows.Forms.Label bottomDivider; + private System.Windows.Forms.Button backButton; + private System.Windows.Forms.Button cancelButton; + private AeroWizard.WizardPage wizardPage3; + private AeroWizard.WizardPage wizardPage2; + private AeroWizard.WizardPage wizardPage1; + private System.Windows.Forms.Button nextButton; + private System.Windows.Forms.Panel commandPanel; + private System.Windows.Forms.Label subHeaderLabel; + private System.Windows.Forms.Label headerLabel; + private System.Windows.Forms.PictureBox startEndPicture; + private System.Windows.Forms.PictureBox headerImage; + private System.Windows.Forms.Label label1; + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/TestWizard/OldStyleWizard.cs b/AeroWizard/AeroWizard/TestWizard/OldStyleWizard.cs new file mode 100644 index 0000000..c06d2a8 --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/OldStyleWizard.cs @@ -0,0 +1,41 @@ +using System; +using System.Windows.Forms; +using AeroWizard; + +namespace TestWizard +{ + public partial class OldStyleWizard : Form + { + public OldStyleWizard() + { + InitializeComponent(); + } + + private void wizardPageContainer1_Finished(object sender, EventArgs e) + { + Close(); + } + + private void wizardPageContainer1_SelectedPageChanged(object sender, EventArgs e) + { + string[] headers = new string[] { "" }; + if (wizardPageContainer1.SelectedPage.Text != null) + headers = wizardPageContainer1.SelectedPage.Text.Split('|'); + headerLabel.Text = headers[0]; + if (headers.Length == 2) + subHeaderLabel.Text = headers[1]; + } + + private void wizardPage1_Initialize(object sender, AeroWizard.WizardPageInitEventArgs e) + { + headerPanel.Visible = topDivider.Visible = false; + startEndPicture.Visible = true; + } + + private void wizardPage2_Initialize(object sender, AeroWizard.WizardPageInitEventArgs e) + { + headerPanel.Visible = topDivider.Visible = true; + startEndPicture.Visible = false; + } + } +} diff --git a/AeroWizard/AeroWizard/TestWizard/OldStyleWizard.resx b/AeroWizard/AeroWizard/TestWizard/OldStyleWizard.resx new file mode 100644 index 0000000..7080a7d --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/OldStyleWizard.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/AeroWizard/AeroWizard/TestWizard/Program.cs b/AeroWizard/AeroWizard/TestWizard/Program.cs new file mode 100644 index 0000000..851c42b --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/Program.cs @@ -0,0 +1,19 @@ +using System; +using System.Windows.Forms; + +namespace TestWizard +{ + static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new Main()); + } + } +} diff --git a/AeroWizard/AeroWizard/TestWizard/Properties/AssemblyInfo.cs b/AeroWizard/AeroWizard/TestWizard/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..e341a9a --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +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("TestWizard")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("CodePlex Community")] +[assembly: AssemblyProduct("TestWizard")] +[assembly: AssemblyCopyright("Copyright © 2013")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("1d4f4e11-cf96-43fa-afbf-07fba4c1bbe2")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// 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("1.0.*")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/AeroWizard/AeroWizard/TestWizard/Properties/Resources.Designer.cs b/AeroWizard/AeroWizard/TestWizard/Properties/Resources.Designer.cs new file mode 100644 index 0000000..210f173 --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/Properties/Resources.Designer.cs @@ -0,0 +1,73 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.42000 +// +// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn +// der Code erneut generiert wird. +// +//------------------------------------------------------------------------------ + +namespace TestWizard.Properties { + using System; + + + /// + /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw. + /// + // Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert + // -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert. + // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen + // mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("TestWizard.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle + /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap WizardHat_48 { + get { + object obj = ResourceManager.GetObject("WizardHat_48", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/AeroWizard/AeroWizard/TestWizard/Properties/Resources.resx b/AeroWizard/AeroWizard/TestWizard/Properties/Resources.resx new file mode 100644 index 0000000..59f7a17 --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/Properties/Resources.resx @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\WizardHat-48.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/AeroWizard/AeroWizard/TestWizard/Properties/Settings.Designer.cs b/AeroWizard/AeroWizard/TestWizard/Properties/Settings.Designer.cs new file mode 100644 index 0000000..44bc716 --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.42000 +// +// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn +// der Code erneut generiert wird. +// +//------------------------------------------------------------------------------ + +namespace TestWizard.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/AeroWizard/AeroWizard/TestWizard/Properties/Settings.settings b/AeroWizard/AeroWizard/TestWizard/Properties/Settings.settings new file mode 100644 index 0000000..3964565 --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/AeroWizard/AeroWizard/TestWizard/Resources/WizardHat-48.png b/AeroWizard/AeroWizard/TestWizard/Resources/WizardHat-48.png new file mode 100644 index 0000000..e5be2e6 Binary files /dev/null and b/AeroWizard/AeroWizard/TestWizard/Resources/WizardHat-48.png differ diff --git a/AeroWizard/AeroWizard/TestWizard/TestWizBase.Designer.cs b/AeroWizard/AeroWizard/TestWizard/TestWizBase.Designer.cs new file mode 100644 index 0000000..e8263b0 --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/TestWizBase.Designer.cs @@ -0,0 +1,285 @@ +using AeroWizard; + +namespace TestWizard +{ + partial class TestWizBase + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(TestWizBase)); + this.backBtn = new System.Windows.Forms.Button(); + this.cancelBtn = new System.Windows.Forms.Button(); + this.nextBtn = new System.Windows.Forms.Button(); + this.wizardPageContainer1 = new AeroWizard.WizardPageContainer(); + this.wizardPage1 = new AeroWizard.WizardPage(); + this.label1 = new System.Windows.Forms.Label(); + this.wizardPage2 = new AeroWizard.WizardPage(); + this.checkBox1 = new System.Windows.Forms.CheckBox(); + this.wizardPage3 = new AeroWizard.WizardPage(); + this.label5 = new System.Windows.Forms.Label(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.button1 = new System.Windows.Forms.Button(); + ((System.ComponentModel.ISupportInitialize)(this.wizardPageContainer1)).BeginInit(); + this.wizardPageContainer1.SuspendLayout(); + this.wizardPage1.SuspendLayout(); + this.wizardPage2.SuspendLayout(); + this.wizardPage3.SuspendLayout(); + this.SuspendLayout(); + // + // backBtn + // + this.backBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.backBtn.FlatAppearance.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224))))); + this.backBtn.FlatAppearance.MouseDownBackColor = System.Drawing.Color.Silver; + this.backBtn.FlatAppearance.MouseOverBackColor = System.Drawing.Color.DarkGray; + this.backBtn.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.backBtn.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224))))); + this.backBtn.Location = new System.Drawing.Point(141, 434); + this.backBtn.Name = "backBtn"; + this.backBtn.Size = new System.Drawing.Size(75, 23); + this.backBtn.TabIndex = 1; + this.backBtn.Tag = AeroWizard.WizardCommandButtonState.Disabled; + this.backBtn.Text = "&Back"; + this.backBtn.UseVisualStyleBackColor = false; + // + // cancelBtn + // + this.cancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancelBtn.FlatAppearance.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224))))); + this.cancelBtn.FlatAppearance.MouseDownBackColor = System.Drawing.Color.Silver; + this.cancelBtn.FlatAppearance.MouseOverBackColor = System.Drawing.Color.DarkGray; + this.cancelBtn.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.cancelBtn.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224))))); + this.cancelBtn.Location = new System.Drawing.Point(303, 434); + this.cancelBtn.Name = "cancelBtn"; + this.cancelBtn.Size = new System.Drawing.Size(75, 23); + this.cancelBtn.TabIndex = 1; + this.cancelBtn.Tag = AeroWizard.WizardCommandButtonState.Disabled; + this.cancelBtn.Text = "&Cancel"; + this.cancelBtn.UseVisualStyleBackColor = false; + this.cancelBtn.Click += new System.EventHandler(this.button1_Click); + // + // nextBtn + // + this.nextBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.nextBtn.FlatAppearance.BorderColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224))))); + this.nextBtn.FlatAppearance.MouseDownBackColor = System.Drawing.Color.Silver; + this.nextBtn.FlatAppearance.MouseOverBackColor = System.Drawing.Color.DarkGray; + this.nextBtn.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.nextBtn.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224))))); + this.nextBtn.Location = new System.Drawing.Point(222, 434); + this.nextBtn.Name = "nextBtn"; + this.nextBtn.Size = new System.Drawing.Size(75, 23); + this.nextBtn.TabIndex = 1; + this.nextBtn.Tag = AeroWizard.WizardCommandButtonState.Enabled; + this.nextBtn.Text = "&Next"; + this.nextBtn.UseVisualStyleBackColor = false; + // + // wizardPageContainer1 + // + this.wizardPageContainer1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.wizardPageContainer1.BackButton = this.backBtn; + this.wizardPageContainer1.CancelButton = this.cancelBtn; + this.wizardPageContainer1.Controls.Add(this.wizardPage1); + this.wizardPageContainer1.Controls.Add(this.wizardPage2); + this.wizardPageContainer1.Controls.Add(this.wizardPage3); + this.wizardPageContainer1.Location = new System.Drawing.Point(12, 93); + this.wizardPageContainer1.Name = "wizardPageContainer1"; + this.wizardPageContainer1.NextButton = this.nextBtn; + this.wizardPageContainer1.Pages.Add(this.wizardPage1); + this.wizardPageContainer1.Pages.Add(this.wizardPage2); + this.wizardPageContainer1.Pages.Add(this.wizardPage3); + this.wizardPageContainer1.ShowProgressInTaskbarIcon = true; + this.wizardPageContainer1.Size = new System.Drawing.Size(366, 335); + this.wizardPageContainer1.TabIndex = 2; + this.wizardPageContainer1.Finished += new System.EventHandler(this.wizardPageContainer1_Finished); + this.wizardPageContainer1.SelectedPageChanged += new System.EventHandler(this.wizardPageContainer1_SelectedPageChanged); + // + // wizardPage1 + // + this.wizardPage1.Controls.Add(this.label1); + this.wizardPage1.Name = "wizardPage1"; + this.wizardPage1.Size = new System.Drawing.Size(366, 335); + this.wizardPage1.TabIndex = 0; + this.wizardPage1.Text = "Page 1"; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(3, 9); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(280, 13); + this.label1.TabIndex = 0; + this.label1.Text = "This is another wizard form that is totally customized."; + // + // wizardPage2 + // + this.wizardPage2.AllowNext = false; + this.wizardPage2.Controls.Add(this.checkBox1); + this.wizardPage2.Name = "wizardPage2"; + this.wizardPage2.Size = new System.Drawing.Size(366, 335); + this.wizardPage2.TabIndex = 1; + this.wizardPage2.Text = "Page 2"; + // + // checkBox1 + // + this.checkBox1.AutoSize = true; + this.checkBox1.Location = new System.Drawing.Point(4, 12); + this.checkBox1.Name = "checkBox1"; + this.checkBox1.Size = new System.Drawing.Size(144, 17); + this.checkBox1.TabIndex = 0; + this.checkBox1.Text = "Click here to proceed..."; + this.checkBox1.UseVisualStyleBackColor = true; + this.checkBox1.CheckedChanged += new System.EventHandler(this.checkBox1_CheckedChanged); + // + // wizardPage3 + // + this.wizardPage3.Controls.Add(this.label5); + this.wizardPage3.Controls.Add(this.textBox1); + this.wizardPage3.Name = "wizardPage3"; + this.wizardPage3.Size = new System.Drawing.Size(366, 335); + this.wizardPage3.TabIndex = 2; + this.wizardPage3.Text = "Page 3"; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(4, 6); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(64, 13); + this.label5.TabIndex = 1; + this.label5.Text = "Your name:"; + // + // textBox1 + // + this.textBox1.Location = new System.Drawing.Point(74, 3); + this.textBox1.Name = "textBox1"; + this.textBox1.Size = new System.Drawing.Size(247, 22); + this.textBox1.TabIndex = 0; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Font = new System.Drawing.Font("Segoe UI", 15.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label2.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224))))); + this.label2.Location = new System.Drawing.Point(57, 15); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(189, 30); + this.label2.TabIndex = 3; + this.label2.Text = "My Cooler Wizard"; + // + // label3 + // + this.label3.Image = ((System.Drawing.Image)(resources.GetObject("label3.Image"))); + this.label3.ImageAlign = System.Drawing.ContentAlignment.TopLeft; + this.label3.Location = new System.Drawing.Point(9, 4); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(52, 55); + this.label3.TabIndex = 4; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Font = new System.Drawing.Font("Segoe UI", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label4.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224))))); + this.label4.Location = new System.Drawing.Point(12, 69); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(97, 21); + this.label4.TabIndex = 3; + this.label4.Text = ""; + // + // button1 + // + this.button1.BackColor = System.Drawing.Color.Crimson; + this.button1.FlatAppearance.BorderSize = 0; + this.button1.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.button1.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.button1.ForeColor = System.Drawing.Color.White; + this.button1.Location = new System.Drawing.Point(358, 0); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(27, 23); + this.button1.TabIndex = 5; + this.button1.Text = "X"; + this.button1.UseVisualStyleBackColor = false; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // TestWizBase + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.BackColor = System.Drawing.Color.DimGray; + this.ClientSize = new System.Drawing.Size(390, 469); + this.Controls.Add(this.button1); + this.Controls.Add(this.label3); + this.Controls.Add(this.label4); + this.Controls.Add(this.label2); + this.Controls.Add(this.wizardPageContainer1); + this.Controls.Add(this.cancelBtn); + this.Controls.Add(this.nextBtn); + this.Controls.Add(this.backBtn); + this.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224))))); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; + this.Name = "TestWizBase"; + this.Text = "TestWizBase"; + ((System.ComponentModel.ISupportInitialize)(this.wizardPageContainer1)).EndInit(); + this.wizardPageContainer1.ResumeLayout(false); + this.wizardPage1.ResumeLayout(false); + this.wizardPage1.PerformLayout(); + this.wizardPage2.ResumeLayout(false); + this.wizardPage2.PerformLayout(); + this.wizardPage3.ResumeLayout(false); + this.wizardPage3.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button backBtn; + private System.Windows.Forms.Button cancelBtn; + private System.Windows.Forms.Button nextBtn; + private AeroWizard.WizardPageContainer wizardPageContainer1; + private AeroWizard.WizardPage wizardPage3; + private AeroWizard.WizardPage wizardPage2; + private System.Windows.Forms.CheckBox checkBox1; + private AeroWizard.WizardPage wizardPage1; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.Button button1; + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/TestWizard/TestWizBase.cs b/AeroWizard/AeroWizard/TestWizard/TestWizBase.cs new file mode 100644 index 0000000..fb2c347 --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/TestWizBase.cs @@ -0,0 +1,33 @@ +using System; +using System.Windows.Forms; + +namespace TestWizard +{ + public partial class TestWizBase : Form + { + public TestWizBase() + { + InitializeComponent(); + } + + private void wizardPageContainer1_Finished(object sender, EventArgs e) + { + Close(); + } + + private void checkBox1_CheckedChanged(object sender, EventArgs e) + { + wizardPage2.AllowNext = checkBox1.Checked; + } + + private void wizardPageContainer1_SelectedPageChanged(object sender, EventArgs e) + { + label4.Text = wizardPageContainer1.SelectedPage.Text; + } + + private void button1_Click(object sender, EventArgs e) + { + Close(); + } + } +} diff --git a/AeroWizard/AeroWizard/TestWizard/TestWizBase.resx b/AeroWizard/AeroWizard/TestWizard/TestWizBase.resx new file mode 100644 index 0000000..3067e09 --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/TestWizBase.resx @@ -0,0 +1,172 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAABGdBTUEAALGPC/xhBQAAABl0RVh0U29m + dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAqESURBVGhD7Zn5U9NnHsdBA+FKuAmGHIQkhBAg4Uog + HIFcHAkJgmBFQFAoXhQELQpWFEVKPahitfZCqx1tdcZuO9aeS7fbnc7s7tTu7NF2pzPsMTvT6Xbq7F/w + 3s/zTUBgj+56NPzQz8wzgeT7zfd5Pc/neb8/z5OgH+PHCGzo/a/fF6NBwatBr1O+f1dGzFBjnWKv/y30 + QbxwhCjKEbQqhF0f7Xs7sOFNFUXi1kQJ7Jqo74OY5YkN4KsqsEqYwq7d5Hs7sNFnyUjCa8MWfHy6Er2l + Mf8JYtMqQTIiM6wIT68AT5zLrrvh+yjwcbvOkIKrQ+W4cdCMWm3kcojooFW8O5GaSmRV2AnChpA0C/xr + YUUEy+XbVq0I/Z4MHN+SDVfmknSaCRFpoSh145PzCuzqLYK6xIbgiHh2jdd3SeCDg8iTxeIxN4PIgVsn + YB2cC+YLkW004eYTifjqYjK+e0+FpFwHVifnLJ+pgAcHkUsQO10anOjKQaNeCIHMAHHZI9jd58Ln5xPw + 6aV0rN1Q7k+j4DnfrSsnOAiDNBbba9JxoluPLosEWd4erOvfjfaWUoRr7OBrqhGqqUEwP5rNwv/qIT9Y + +CFisK1ajeMEsdOlhblrFBJLC8IYQEYNB7AqVsEAVpSpzccMk83MNBl6nATRRRC1GkhM7iUAPKmJAdz2 + 3bJygnPcMI0DiQVu6AtMeNShwrEuA3qsMoizzDg2mMkBhKirELQ6lEHIfbeujJjliXQQ6l3QrH0MpX1T + MFps6LL5IA7Xi/DhZKIPIL0awVEiBtDnuzXwYWGyGa51QlTSDNP2Y6g79ias+56DzajEZqsSTxHEIU8y + pgbUqPRUzMvpinHluRBpIY2+G2rPDtgPXMLa0+/CNXkDxqYudJQL0VmRhkmCGKsX4+qTGiiNZgIIZhBs + 8Qc0NgWHxSA8ywWRuQmFjx5F3fE30fDMB6iduI6Ktq14a78AndYYbLIQxBYDhmqlSFYbERQSwQAC7spz + ITIjhAY3cuo3Y/dTe9Ew/T4aCaD68FUM9FZjfKsSOlMhKvLEaC9XYGKzAXvdMiTGCBlAQF15lI1+RLYb + ScXrcGivHe/NVKLx7CwaznwAx+hlKGu6Eal1kISSiSmtUCkUaCtV4GinAQe9EmjjueIuICU2y907sco8 + OKtMeHyrCZ8/m4A/XUnB2IWTBPA+bCMzkNs7EJHBAGqouaiUqIBUkoqNJakY79DjUL0U2oTAQPQFh8dA + V1KBd4Yi8ekxAb6gmufPV1Pw91tp+OajPOycHOZcOJwBaGupuXwyqrBAKlOhxZyKI5v0GGuQBwRiLlRe + BIHBg4ZmO24fF+KL5xPxl9ck+PYdJV6+1o+ygTNYQ7IaRgCKInJiP0Ao+UBIWiVBqLGhWI6xdj2OrEud + h/hB1gSnPBE5HiQUNWLvtlJ8diIaX76QhL9ek+LLmyZ4n74Fc+9JJJkaOIBPnxVj3y4TbGutPgByY56i + AsnJUjQWSnGoTY/xZgVMKbyHCsGs/wY/TgZ+ajEEuR6kVnfhhf2FOHRqBF3nXsMrl7vx3fsqeE7ehLFn + AvH5HrzcG4c/nIvH12+mIjG3agEgROUAL6UQgjAeGgqkONiag6PrlXAouDLjgUOM8qPXQGRcj8T8daT7 + bsSbGqFv28/pfeMzP8W6cx9ybeTieXLiN5DXeQixBhcSsm34eDIee3YUIM9uWwqgqARtPSHg81CfL8GB + jTmY2KCCI+3BQszEqksgreihRdkNga4GUQYvZI5OlA2e5dJlHUnnPACTUebC+tZhROcw9amC3Wlaugb8 + ACFpdgRRGULPuM0gvHkS7N+QjYkWNRxKDoKVG/fl1lMJOhvktu2QVW5FSlkn1TwOxNHo57SOoGb8Gue6 + 853nAGg2ao5eh655NwRZVdTxavAz3RxAqbMM8TnU8XkApR2rxQUIChXcoWfNMghPbgpGHsnCkxvT4VTy + OThq9wRhEYhpY+7sRap9BweRkLuWG30p6XvZ4DPc6LMRZx0ff/bgAkDV2FVo6nsRpXNSx2kW/AAHdmTj + xYPapQDSYvDk5bThT+DSRhjGu8NOPPY1Z+GYXQpzEndYcE8SOyu3bEZaVR8UDh9EdLYLsYUN0K3fg+oj + r2LD6Z+gb+oUxqafwJcko0PPnfW78CWoXD2IzGQA5AF+gMsH0jivmB6lzc62IuTbqDKVFHMgDMI/E14G + 0aEV4ZNIPvLDuRO9/3sLKhdKMqCuHYCyup8gHuMghFnVkFjbYe47Be/ULXSevoJfPMHHZ1PR+OqCCL+6 + Vobu86/COvwSUp2diKASm59Zi8xiC57vl+D3Z+Mxd3kNvn4jFf/4mQYHhooIoAihKqdvNlKoyKOBo6ZP + XxV8p8R3fHlP+wZLUqYF6a5BqGp2LUDEkapomwYpRa5wI81S5vpkEX53Jg5zr4jxzVvkwj/PQ/PoJKSV + rdwegZ/pwnRXEn55JAK/nY7ljln+dkOGj2YyfZ0mAL6S1Mm/qIOj1tzTiC8PS3x6CTTuPVAvghCbW5Df + Nc7pPMv9/qdP4dcTUVwdxFz4jWttqD/1Dsp2TXPHKmEZDIDSh5rObMFvpmLwxxd9hvftu0qkGSmF5GUI + lRWDT+uCQfDE+Qzgvjf9FnlxHTLqHke6ezelkh+CZsHQcRgeSh82+o+fHMf2My+h9dxNvH1lLS68OoD6 + p9/mXFhEFWoYSeg8AFsDrOR4/bCM1KgU27qNaGsv5lQoPKMWYSSv8xD0fJZG9xVyUaYJGZ4haJZBZDTs + g2XoJRrpdxcUaL71PH/d78JPIsFYTwDUKd1dgJPb5YjLXiSjlDIhKfm0VtxLIOj59w0QxI+Kmc3vmFgG + McBBaBuHYdo5DRftfZdA0N9sR5a/eQxx+XUIYx6gq1sAWG5kPBr9SI2T1KqOIFwI1/gg6PH3D0BhSSvz + Qte4/y6Eax6iH2r3IHLaD6Ny+CLqTtzkFrXPhV8nFx5BDC34MOYB/w6AqQ6NfJTGDmF2PaKyvAsQfN+5 + 0QM7+Lph3DIGrXevH2LPXQhOmfqgrhuEvv0ISgfPw37wCqwjF2mGBiCkMiKMeQClUCjrPKUIG/VQRRki + FCWIyaknyEYqNxogYBA6DwfBi5YwgAd29BgdGim8bSTlWZgJUiYmr8s9gjm2yjVADryHSo42CKnkiEi3 + cS1S44Ag3YqYrFokFLYgvuARxOWtR2xuEwch9EMIyfjomQ/8uIWDyKrfjtzW8SUQiz1C4djpKzms28gD + WOH3KFLKuyAu3Uwbm04kmzchqagNiaaNiyCaOYhofSN91oIokYw58UM5amFfOiXWl6J42xQyG0b+xSPS + nH4I2w7IFiC6F0F0kLT6IYxLIZIKGqjzctb5h35qbQmNEMxpnBtRtHUKWU0HFuRVWd23AMFVrwzCX4LP + QyRzEO0LECpaP2vynGAzTN/9gx65s+pwVl5UjdwNe2DqOQFD6xEyOgZBhd8CxNa7EGVbsIYglG5aJ95d + kJFRRiWmsB87AnKsMh9sm8mKrdkYWfodBqSsbIKmdgvyaEOTy9rGYSgsTZCbPWAp6O80222tmN/KFgcD + slBjozq6qLHOsvcfygL9MQITQUH/BJc6GSQWKR47AAAAAElFTkSuQmCC + + + \ No newline at end of file diff --git a/AeroWizard/AeroWizard/TestWizard/TestWizard.csproj b/AeroWizard/AeroWizard/TestWizard/TestWizard.csproj new file mode 100644 index 0000000..706572e --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/TestWizard.csproj @@ -0,0 +1,216 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {EAA3C12F-D499-476C-9500-524EAEE3373A} + WinExe + Properties + TestWizard + TestWizard + v4.5.2 + 512 + + + + + + + + + + + + + 3.5 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + AllRules.ruleset + false + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + AllRules.ruleset + false + + + + + + + + + + + + Form + + + AeroWizardTemplate.cs + + + Form + + + Main.cs + + + Form + + + MyStepWizard.cs + + + Form + + + MyWizard.cs + + + Component + + + + + Component + + + Form + + + OldStyleWizard.cs + + + + + Form + + + CustomWizardTemplate.cs + + + Form + + + Win10Wiz.cs + + + Form + + + TestWizBase.cs + + + AeroWizardTemplate.cs + + + Main.cs + + + MyStepWizard.cs + + + MyWizard.cs + + + OldStyleWizard.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + CustomWizardTemplate.cs + + + Win10Wiz.cs + + + TestWizBase.cs + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + + + + + + + {199c12c4-3eef-4d08-bac3-f2a62bcf969c} + AeroWizard + + + + + \ No newline at end of file diff --git a/AeroWizard/AeroWizard/TestWizard/Win10Wiz.Designer.cs b/AeroWizard/AeroWizard/TestWizard/Win10Wiz.Designer.cs new file mode 100644 index 0000000..f1a7574 --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/Win10Wiz.Designer.cs @@ -0,0 +1,316 @@ +using AeroWizard; + +namespace TestWizard +{ + partial class Win10Wiz + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.backBtn = new System.Windows.Forms.Button(); + this.cancelBtn = new System.Windows.Forms.Button(); + this.nextBtn = new System.Windows.Forms.Button(); + this.wizardPageContainer1 = new AeroWizard.WizardPageContainer(); + this.wizardPage2 = new AeroWizard.WizardPage(); + this.checkBox1 = new System.Windows.Forms.CheckBox(); + this.wizardPage3 = new AeroWizard.WizardPage(); + this.label5 = new System.Windows.Forms.Label(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.label4 = new System.Windows.Forms.Label(); + this.button1 = new System.Windows.Forms.Button(); + this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); + this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components); + this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem3 = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem4 = new System.Windows.Forms.ToolStripMenuItem(); + ((System.ComponentModel.ISupportInitialize)(this.wizardPageContainer1)).BeginInit(); + this.wizardPageContainer1.SuspendLayout(); + this.wizardPage2.SuspendLayout(); + this.wizardPage3.SuspendLayout(); + this.flowLayoutPanel1.SuspendLayout(); + this.contextMenuStrip1.SuspendLayout(); + this.SuspendLayout(); + // + // backBtn + // + this.backBtn.FlatAppearance.BorderSize = 0; + this.backBtn.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.backBtn.Font = new System.Drawing.Font("Segoe UI Symbol", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.backBtn.ForeColor = System.Drawing.Color.White; + this.backBtn.Location = new System.Drawing.Point(3, 3); + this.backBtn.Name = "backBtn"; + this.backBtn.Size = new System.Drawing.Size(47, 43); + this.backBtn.TabIndex = 1; + this.backBtn.Tag = AeroWizard.WizardCommandButtonState.Disabled; + this.backBtn.Text = "º"; + this.backBtn.TextAlign = System.Drawing.ContentAlignment.TopLeft; + this.backBtn.UseVisualStyleBackColor = false; + // + // cancelBtn + // + this.cancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancelBtn.AutoSize = true; + this.cancelBtn.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.cancelBtn.FlatAppearance.BorderColor = System.Drawing.Color.White; + this.cancelBtn.FlatAppearance.BorderSize = 2; + this.cancelBtn.FlatAppearance.MouseDownBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(63)))), ((int)(((byte)(116)))), ((int)(((byte)(188))))); + this.cancelBtn.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(31)))), ((int)(((byte)(57)))), ((int)(((byte)(92))))); + this.cancelBtn.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.cancelBtn.Font = new System.Drawing.Font("Segoe UI Semibold", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cancelBtn.ForeColor = System.Drawing.Color.White; + this.cancelBtn.Location = new System.Drawing.Point(661, 496); + this.cancelBtn.MinimumSize = new System.Drawing.Size(90, 0); + this.cancelBtn.Name = "cancelBtn"; + this.cancelBtn.Size = new System.Drawing.Size(90, 34); + this.cancelBtn.TabIndex = 1; + this.cancelBtn.Tag = AeroWizard.WizardCommandButtonState.Disabled; + this.cancelBtn.Text = "&Cancel"; + this.cancelBtn.UseVisualStyleBackColor = false; + this.cancelBtn.Click += new System.EventHandler(this.button1_Click); + // + // nextBtn + // + this.nextBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.nextBtn.AutoSize = true; + this.nextBtn.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.nextBtn.FlatAppearance.BorderColor = System.Drawing.Color.White; + this.nextBtn.FlatAppearance.BorderSize = 2; + this.nextBtn.FlatAppearance.MouseDownBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(63)))), ((int)(((byte)(116)))), ((int)(((byte)(188))))); + this.nextBtn.FlatAppearance.MouseOverBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(31)))), ((int)(((byte)(57)))), ((int)(((byte)(92))))); + this.nextBtn.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.nextBtn.Font = new System.Drawing.Font("Segoe UI Semibold", 11.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.nextBtn.ForeColor = System.Drawing.Color.White; + this.nextBtn.Location = new System.Drawing.Point(556, 496); + this.nextBtn.MinimumSize = new System.Drawing.Size(90, 0); + this.nextBtn.Name = "nextBtn"; + this.nextBtn.Size = new System.Drawing.Size(90, 34); + this.nextBtn.TabIndex = 1; + this.nextBtn.Tag = AeroWizard.WizardCommandButtonState.Enabled; + this.nextBtn.Text = "&Next"; + this.nextBtn.UseVisualStyleBackColor = false; + // + // wizardPageContainer1 + // + this.wizardPageContainer1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.wizardPageContainer1.BackButton = this.backBtn; + this.wizardPageContainer1.BackButtonText = "º"; + this.wizardPageContainer1.CancelButton = this.cancelBtn; + this.wizardPageContainer1.Controls.Add(this.wizardPage2); + this.wizardPageContainer1.Controls.Add(this.wizardPage3); + this.wizardPageContainer1.Location = new System.Drawing.Point(12, 93); + this.wizardPageContainer1.Name = "wizardPageContainer1"; + this.wizardPageContainer1.NextButton = this.nextBtn; + this.wizardPageContainer1.Pages.Add(this.wizardPage2); + this.wizardPageContainer1.Pages.Add(this.wizardPage3); + this.wizardPageContainer1.ShowProgressInTaskbarIcon = true; + this.wizardPageContainer1.Size = new System.Drawing.Size(739, 399); + this.wizardPageContainer1.TabIndex = 2; + this.wizardPageContainer1.Finished += new System.EventHandler(this.wizardPageContainer1_Finished); + this.wizardPageContainer1.SelectedPageChanged += new System.EventHandler(this.wizardPageContainer1_SelectedPageChanged); + // + // wizardPage2 + // + this.wizardPage2.AllowNext = false; + this.wizardPage2.Controls.Add(this.checkBox1); + this.wizardPage2.Name = "wizardPage2"; + this.wizardPage2.Size = new System.Drawing.Size(739, 399); + this.wizardPage2.TabIndex = 1; + this.wizardPage2.Text = "Page 2"; + // + // checkBox1 + // + this.checkBox1.AutoSize = true; + this.checkBox1.Location = new System.Drawing.Point(4, 12); + this.checkBox1.Name = "checkBox1"; + this.checkBox1.Size = new System.Drawing.Size(144, 17); + this.checkBox1.TabIndex = 0; + this.checkBox1.Text = "Click here to proceed..."; + this.checkBox1.UseVisualStyleBackColor = true; + this.checkBox1.CheckedChanged += new System.EventHandler(this.checkBox1_CheckedChanged); + // + // wizardPage3 + // + this.wizardPage3.Controls.Add(this.label5); + this.wizardPage3.Controls.Add(this.textBox1); + this.wizardPage3.Name = "wizardPage3"; + this.wizardPage3.Size = new System.Drawing.Size(366, 335); + this.wizardPage3.TabIndex = 2; + this.wizardPage3.Text = "Page 3"; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(4, 6); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(64, 13); + this.label5.TabIndex = 1; + this.label5.Text = "Your name:"; + // + // textBox1 + // + this.textBox1.Location = new System.Drawing.Point(74, 3); + this.textBox1.Name = "textBox1"; + this.textBox1.Size = new System.Drawing.Size(247, 22); + this.textBox1.TabIndex = 0; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Font = new System.Drawing.Font("Segoe UI", 20.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label4.ForeColor = System.Drawing.Color.White; + this.label4.Location = new System.Drawing.Point(56, 0); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(154, 37); + this.label4.TabIndex = 3; + this.label4.Text = ""; + // + // button1 + // + this.button1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.button1.BackColor = System.Drawing.Color.Crimson; + this.button1.FlatAppearance.BorderSize = 0; + this.button1.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.button1.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.button1.ForeColor = System.Drawing.Color.White; + this.button1.Location = new System.Drawing.Point(736, -1); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(27, 23); + this.button1.TabIndex = 5; + this.button1.Text = "X"; + this.button1.UseVisualStyleBackColor = false; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // flowLayoutPanel1 + // + this.flowLayoutPanel1.AutoSize = true; + this.flowLayoutPanel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.flowLayoutPanel1.Controls.Add(this.backBtn); + this.flowLayoutPanel1.Controls.Add(this.label4); + this.flowLayoutPanel1.Location = new System.Drawing.Point(13, 13); + this.flowLayoutPanel1.Name = "flowLayoutPanel1"; + this.flowLayoutPanel1.Size = new System.Drawing.Size(213, 49); + this.flowLayoutPanel1.TabIndex = 6; + // + // contextMenuStrip1 + // + this.contextMenuStrip1.Font = new System.Drawing.Font("Webdings", 11.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(2))); + this.contextMenuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.toolStripMenuItem1, + this.toolStripMenuItem2, + this.toolStripMenuItem3, + this.toolStripMenuItem4}); + this.contextMenuStrip1.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.Table; + this.contextMenuStrip1.Name = "contextMenuStrip1"; + this.contextMenuStrip1.ShowImageMargin = false; + this.contextMenuStrip1.Size = new System.Drawing.Size(69, 100); + // + // toolStripMenuItem1 + // + this.toolStripMenuItem1.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; + this.toolStripMenuItem1.Name = "toolStripMenuItem1"; + this.toolStripMenuItem1.Size = new System.Drawing.Size(68, 24); + this.toolStripMenuItem1.Text = "9"; + this.toolStripMenuItem1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // toolStripMenuItem2 + // + this.toolStripMenuItem2.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; + this.toolStripMenuItem2.Name = "toolStripMenuItem2"; + this.toolStripMenuItem2.Size = new System.Drawing.Size(68, 24); + this.toolStripMenuItem2.Text = "3"; + this.toolStripMenuItem2.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // toolStripMenuItem3 + // + this.toolStripMenuItem3.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; + this.toolStripMenuItem3.Name = "toolStripMenuItem3"; + this.toolStripMenuItem3.Size = new System.Drawing.Size(68, 24); + this.toolStripMenuItem3.Text = "4"; + this.toolStripMenuItem3.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // toolStripMenuItem4 + // + this.toolStripMenuItem4.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; + this.toolStripMenuItem4.Name = "toolStripMenuItem4"; + this.toolStripMenuItem4.Size = new System.Drawing.Size(68, 24); + this.toolStripMenuItem4.Text = ":"; + this.toolStripMenuItem4.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // Win10Wiz + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(44)))), ((int)(((byte)(81)))), ((int)(((byte)(131))))); + this.ClientSize = new System.Drawing.Size(763, 542); + this.Controls.Add(this.flowLayoutPanel1); + this.Controls.Add(this.button1); + this.Controls.Add(this.wizardPageContainer1); + this.Controls.Add(this.cancelBtn); + this.Controls.Add(this.nextBtn); + this.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.ForeColor = System.Drawing.Color.White; + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; + this.Name = "Win10Wiz"; + this.Text = "TestWizBase"; + ((System.ComponentModel.ISupportInitialize)(this.wizardPageContainer1)).EndInit(); + this.wizardPageContainer1.ResumeLayout(false); + this.wizardPage2.ResumeLayout(false); + this.wizardPage2.PerformLayout(); + this.wizardPage3.ResumeLayout(false); + this.wizardPage3.PerformLayout(); + this.flowLayoutPanel1.ResumeLayout(false); + this.flowLayoutPanel1.PerformLayout(); + this.contextMenuStrip1.ResumeLayout(false); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button backBtn; + private System.Windows.Forms.Button cancelBtn; + private System.Windows.Forms.Button nextBtn; + private AeroWizard.WizardPageContainer wizardPageContainer1; + private AeroWizard.WizardPage wizardPage3; + private AeroWizard.WizardPage wizardPage2; + private System.Windows.Forms.CheckBox checkBox1; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1; + private System.Windows.Forms.ContextMenuStrip contextMenuStrip1; + private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem1; + private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem2; + private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem3; + private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem4; + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/TestWizard/Win10Wiz.cs b/AeroWizard/AeroWizard/TestWizard/Win10Wiz.cs new file mode 100644 index 0000000..7a642b6 --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/Win10Wiz.cs @@ -0,0 +1,33 @@ +using System; +using System.Windows.Forms; + +namespace TestWizard +{ + public partial class Win10Wiz : Form + { + public Win10Wiz() + { + InitializeComponent(); + } + + private void wizardPageContainer1_Finished(object sender, EventArgs e) + { + Close(); + } + + private void checkBox1_CheckedChanged(object sender, EventArgs e) + { + wizardPage2.AllowNext = checkBox1.Checked; + } + + private void wizardPageContainer1_SelectedPageChanged(object sender, EventArgs e) + { + label4.Text = wizardPageContainer1.SelectedPage.Text; + } + + private void button1_Click(object sender, EventArgs e) + { + Close(); + } + } +} diff --git a/AeroWizard/AeroWizard/TestWizard/Win10Wiz.resx b/AeroWizard/AeroWizard/TestWizard/Win10Wiz.resx new file mode 100644 index 0000000..73b0127 --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/Win10Wiz.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/AeroWizard/AeroWizard/TestWizard/app.config b/AeroWizard/AeroWizard/TestWizard/app.config new file mode 100644 index 0000000..de82893 --- /dev/null +++ b/AeroWizard/AeroWizard/TestWizard/app.config @@ -0,0 +1,3 @@ + + + diff --git a/AeroWizard/AeroWizard/ThemeImageButton.cs b/AeroWizard/AeroWizard/ThemeImageButton.cs new file mode 100644 index 0000000..806f61d --- /dev/null +++ b/AeroWizard/AeroWizard/ThemeImageButton.cs @@ -0,0 +1,117 @@ +using System.ComponentModel; +using System.Drawing; +using System.Windows.Forms; +using System.Windows.Forms.VisualStyles; + +using Microsoft.Win32.DesktopWindowManager; + +namespace AeroWizard +{ + /// + /// Image button that can be displayed on glass. + /// + [ToolboxBitmap(typeof(Button))] + internal class XThemeImageButton : ThemeImageButton + { + private const string defaultText = ""; + + private Image imageStrip; + + /// + /// Initializes a new instance of the class. + /// + public XThemeImageButton() + { + StyleClass = "BUTTON"; + StylePart = 1; + Text = defaultText; + } + + /// + /// Gets or sets the compatible image strip used when visual style rendering is not available. + /// + /// The compatible image strip. + [DefaultValue(null), Category("Appearance")] + public Image CompatibleImageStrip + { + get { return imageStrip; } + set { base.SetImageListImageStrip(imageStrip = value, Orientation.Vertical); } + } + + /// + /// Gets or sets the style class. + /// + /// The style class. + [DefaultValue("BUTTON"), Category("Appearance")] + public string StyleClass { get; set; } + + /// + /// Gets or sets the style part. + /// + /// The style part. + [DefaultValue(1), Category("Appearance")] + public int StylePart { get; set; } + + /// + /// Gets or sets the text associated with this control. + /// + /// + /// The text associated with this control. + /// + [DefaultValue(defaultText), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), EditorBrowsable(EditorBrowsableState.Never)] + public override string Text + { + get { return base.Text; } + set { base.Text = value; } + } + + /// + /// Primary function for painting the button. This method should be overridden instead of OnPaint. + /// + /// The graphics. + /// The bounds. + protected override void PaintButton(Graphics graphics, Rectangle bounds) + { + if (Application.RenderWithVisualStyles) + { + try + { + VisualStyleRenderer rnd = new VisualStyleRenderer(StyleClass, StylePart, (int)ButtonState); + if (this.IsDesignMode() || !DesktopWindowManager.IsCompositionEnabled()) + { + rnd.DrawParentBackground(graphics, bounds, this); + rnd.DrawBackground(graphics, bounds); + } + else + { + rnd.DrawGlassBackground(graphics, bounds, bounds); + } + return; + } + catch { } + } + else + { + base.PaintButton(graphics, bounds); + /*Rectangle sr = this.ClientRectangle; + sr.Offset(0, sr.Height * ((int)ButtonState - 1)); + graphics.Clear(this.Parent.BackColor); + if (imageStrip != null) + { + Bitmap bmp = imageStrip.Clone(sr, imageStrip.PixelFormat); + if (this.IsDesignMode() || !DesktopWindowManager.IsCompositionEnabled()) + { + base.ImageList.Draw(graphics, bounds.X, bounds.Y, bounds.Width, bounds.Height, ((int)ButtonState - 1)); + } + else + { + VisualStyleRendererExtender.DrawGlassImage(null, graphics, bounds, bmp); + } + } + else + using (Brush br = new SolidBrush(this.BackColor)) + graphics.FillRectangle(br, sr);*/ + } + } + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/ThemedImageButton.bmp b/AeroWizard/AeroWizard/ThemedImageButton.bmp new file mode 100644 index 0000000..b852b1e Binary files /dev/null and b/AeroWizard/AeroWizard/ThemedImageButton.bmp differ diff --git a/AeroWizard/AeroWizard/ThemedImageButton.cs b/AeroWizard/AeroWizard/ThemedImageButton.cs new file mode 100644 index 0000000..37bad0b --- /dev/null +++ b/AeroWizard/AeroWizard/ThemedImageButton.cs @@ -0,0 +1,380 @@ +using System; +using System.ComponentModel; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Windows.Forms; +using System.Windows.Forms.VisualStyles; +using Vanara.Interop.DesktopWindowManager; + +namespace AeroWizard +{ + /// + /// A button that displays an image and no text. + /// + [ToolboxItem(true), ToolboxBitmap(typeof(ThemedImageButton), "ThemedImageButton.bmp")] + public class ThemedImageButton : ButtonBase + { + private const string defaultText = ""; + private const string defaultToolTip = "Returns to a previous page"; + + private ToolTip toolTip; + private VisualStyleRenderer rnd = null; + + /// + /// Initializes a new instance of the class. + /// + public ThemedImageButton() + { + SetStyle(ControlStyles.SupportsTransparentBackColor | + ControlStyles.OptimizedDoubleBuffer | + ControlStyles.AllPaintingInWmPaint | + ControlStyles.ResizeRedraw | + ControlStyles.UserPaint, true); + + ButtonState = PushButtonState.Normal; + toolTip = new ToolTip(); + toolTip.SetToolTip(this, defaultToolTip); + StyleClass = "BUTTON"; + StylePart = 1; + Text = defaultText; + } + + /// + /// Gets or sets the background color of the control. + /// + /// A value representing the background color. + public override Color BackColor + { + get { return OnGlass ? Color.Transparent : base.BackColor; } + set { base.BackColor = value; } + } + + /// + /// Gets or sets the image that is displayed on a button control. + /// + /// The displayed on the button control. The default value is null. + public new Image Image + { + get { return base.Image; } + set + { + if (value != null) + { + InitializeImageList(value.Size); + ImageList.Images.Add(value); + } + else + ImageList = null; + base.Image = value; + } + } + + /// + /// Gets or sets the style class. + /// + /// The style class. + [DefaultValue("BUTTON"), Category("Appearance")] + public string StyleClass { get; set; } + + /// + /// Gets or sets the style part. + /// + /// The style part. + [DefaultValue(1), Category("Appearance")] + public int StylePart { get; set; } + + /// + /// Gets or sets the text associated with this control. + /// + /// + /// The text associated with this control. + /// + [DefaultValue(defaultText), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), EditorBrowsable(EditorBrowsableState.Never)] + public override string Text + { + get { return base.Text; } + set { base.Text = value; } + } + + /// + /// Gets or sets the tool tip text. + /// + /// The tool tip text. + [DefaultValue(defaultToolTip), Category("Appearance")] + public string ToolTipText + { + get { return toolTip.GetToolTip(this); } + set { toolTip.SetToolTip(this, value); } + } + + /// + /// Gets or sets the state of the button. + /// + /// The state of the button. + protected PushButtonState ButtonState { get; set; } + + /// + /// Gets a value indicating whether on glass. + /// + /// true if on glass; otherwise, false. + private bool OnGlass => !this.IsDesignMode() && DesktopWindowManager.CompositionEnabled; + + /// + /// Retrieves the default size for the control. + /// + /// + /// + /// The default of the control. + /// + protected override Size DefaultSize => new Size(30, 30); + + /// + /// Retrieves the size of a rectangular area into which a control can be fitted. + /// + /// The custom-sized area for a control. + /// + /// An ordered pair of type representing the width and height of a rectangle. + /// + public override Size GetPreferredSize(Size proposedSize) => DefaultSize; + + /// + /// For button user use to simulate a click operate. + /// + public void PerformClicked() + { + base.OnClick(EventArgs.Empty); + } + + /// + /// Sets the image list images using an image strip. + /// + /// The image strip. + /// The orientation of the strip. + public void SetImageListImageStrip(Image imageStrip, Orientation orientation) + { + if (imageStrip == null) + ImageList = null; + else + { + Size imageSize = orientation == Orientation.Vertical ? new Size(imageStrip.Width, imageStrip.Height / 4) : new Size(imageStrip.Width / 4, imageStrip.Height); + InitializeImageList(imageSize); + using (Bitmap bmp = new Bitmap(imageStrip)) + { + for (Rectangle r = new Rectangle(Point.Empty, imageSize); r.Y < imageStrip.Height; r.Y += imageSize.Height) + ImageList.Images.Add(bmp.Clone(r, bmp.PixelFormat)); + } + } + } + + /// + /// Process Enabled property changed + /// + /// The instance containing the event data. + protected override void OnEnabledChanged(EventArgs e) + { + ButtonState = Enabled ? PushButtonState.Normal : PushButtonState.Disabled; + Invalidate(); + base.OnEnabledChanged(e); + } + + /// + /// Raises the event. + /// + /// The instance containing the event data. + protected override void OnGotFocus(EventArgs e) + { + Invalidate(); + base.OnGotFocus(e); + } + + /// + /// Raises the event. + /// + /// The instance containing the event data. + protected override void OnLostFocus(EventArgs e) + { + Invalidate(); + base.OnLostFocus(e); + } + + /// + /// Raises the event. + /// + /// A that contains the event data. + protected override void OnMouseDown(MouseEventArgs e) + { + if ((e.Button & MouseButtons.Left) != MouseButtons.Left) return; + ButtonState = PushButtonState.Pressed; + Invalidate(); + base.OnMouseDown(e); + } + + /// + /// Raises the event. + /// + /// An that contains the event data. + protected override void OnMouseEnter(EventArgs e) + { + ButtonState = PushButtonState.Hot; + Invalidate(); + base.OnMouseEnter(e); + } + + /// + /// Raises the event. + /// + /// An that contains the event data. + protected override void OnMouseLeave(EventArgs e) + { + ButtonState = Enabled ? PushButtonState.Normal : PushButtonState.Disabled; + Invalidate(); + base.OnMouseLeave(e); + } + + /// + /// Raises the event. + /// + /// A that contains the event data. + protected override void OnMouseUp(MouseEventArgs e) + { + if ((e.Button & MouseButtons.Left) != MouseButtons.Left) return; + ButtonState = Enabled ? PushButtonState.Hot : PushButtonState.Disabled; + Invalidate(); + base.OnMouseUp(e); + } + + /// + /// Raises the event. + /// + /// A that contains the event data. + protected override void OnPaint(PaintEventArgs e) + { + if (Visible) + { + Graphics g = e.Graphics; + g.SmoothingMode = SmoothingMode.HighQuality; + g.CompositingQuality = CompositingQuality.HighQuality; + g.InterpolationMode = InterpolationMode.HighQualityBicubic; + + PaintButton(e.Graphics, e.ClipRectangle); + } + } + + /// + /// Paints the background of the control. + /// + /// A that contains information about the control to paint. + protected override void OnPaintBackground(PaintEventArgs pevent) + { + base.OnPaintBackground(pevent); + } + + /// + /// Primary function for painting the button. This method should be overridden instead of OnPaint. + /// + /// The graphics. + /// The bounds. + protected virtual void PaintButton(Graphics graphics, Rectangle bounds) + { + System.Diagnostics.Debug.WriteLine($"PaintButton: desMode:{this.IsDesignMode()};vsEnabled:{Application.RenderWithVisualStyles};vsOnOS:{VisualStyleInformation.IsSupportedByOS};btnState:{ButtonState};enabled:{Enabled};imgCt:{(ImageList != null ? ImageList.Images.Count : 0)}"); + + if (InitializeRenderer()) + { + if (OnGlass) + { + rnd.DrawGlassBackground(graphics, bounds, bounds); + } + else + { + rnd.DrawParentBackground(graphics, bounds, this); + rnd.DrawBackground(graphics, bounds); + } + } + else + { + if (ImageList != null && ImageList.Images.Count > 0) + { + int idx = (int)ButtonState - 1; + if (ImageList.Images.Count == 1) + idx = 0; + else if (ImageList.Images.Count == 2) + idx = ButtonState == PushButtonState.Disabled ? 1 : 0; + else if (ImageList.Images.Count == 3) + idx = ButtonState == PushButtonState.Normal ? 0 : idx - 1; + bool forceDisabled = !Enabled && ImageList.Images.Count == 1; + if (OnGlass) + { + VisualStyleRendererExtension.DrawGlassImage(null, graphics, bounds, ImageList.Images[idx], forceDisabled); + } + else + { + if (!Application.RenderWithVisualStyles && VisualStyleInformation.IsSupportedByOS) + { + System.Drawing.Drawing2D.GraphicsContainer g = graphics.BeginContainer(); + Rectangle translateRect = bounds; + graphics.TranslateTransform(-bounds.Left, -bounds.Top); + PaintEventArgs pe = new PaintEventArgs(graphics, translateRect); + InvokePaintBackground(Parent, pe); + InvokePaint(Parent, pe); + graphics.ResetTransform(); + graphics.EndContainer(g); + } + else + graphics.Clear(Parent.BackColor); + if (forceDisabled) + ControlPaint.DrawImageDisabled(graphics, ImageList.Images[idx], 0, 0, Color.Transparent); + else + { + //base.ImageList.Draw(graphics, bounds.X, bounds.Y, bounds.Width, bounds.Height, idx); + //VisualStyleRendererExtender.DrawGlassImage(null, graphics, bounds, base.ImageList.Images[idx], forceDisabled); // Not 7 + graphics.DrawImage(ImageList.Images[idx], bounds, bounds, GraphicsUnit.Pixel); // Works on XP, not 7, with Parent.BackColor + } + } + } + /*else if (this.ImageList != null && this.ImageList.Images.Count > 1) + { + int idx = (int)ButtonState - 1; + if (this.ImageList.Images.Count == 2) + idx = ButtonState == PushButtonState.Disabled ? 1 : 0; + if (this.ImageList.Images.Count == 3) + idx = ButtonState == PushButtonState.Normal ? 0 : idx - 1; + if (rnd != null && !this.IsDesignMode() && DesktopWindowManager.IsCompositionEnabled()) + rnd.DrawGlassIcon(graphics, bounds, this.ImageList, idx); + else + this.ImageList.Draw(graphics, bounds.X, bounds.Y, bounds.Width, bounds.Height, idx); + }*/ + // No image so draw standard button + else + { + ButtonRenderer.DrawParentBackground(graphics, bounds, this); + ButtonRenderer.DrawButton(graphics, bounds, ButtonState); + } + } + + if (Focused) + ControlPaint.DrawFocusRectangle(graphics, bounds); + } + + private void InitializeImageList(Size imageSize) + { + ImageList = new ImageList() { ImageSize = imageSize, ColorDepth = ColorDepth.Depth32Bit, TransparentColor = Color.Transparent }; + } + + private bool InitializeRenderer() + { + if (Application.RenderWithVisualStyles) + { + try + { + if (rnd == null) + rnd = new VisualStyleRenderer(StyleClass, StylePart, (int)ButtonState); + else + rnd.SetParameters(StyleClass, StylePart, (int)ButtonState); + return true; + } + catch { } + } + return false; + } + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/ThemedLabel.bmp b/AeroWizard/AeroWizard/ThemedLabel.bmp new file mode 100644 index 0000000..6cc500b Binary files /dev/null and b/AeroWizard/AeroWizard/ThemedLabel.bmp differ diff --git a/AeroWizard/AeroWizard/ThemedLabel.cs b/AeroWizard/AeroWizard/ThemedLabel.cs new file mode 100644 index 0000000..332bfc6 --- /dev/null +++ b/AeroWizard/AeroWizard/ThemedLabel.cs @@ -0,0 +1,189 @@ +using System; +using System.ComponentModel; +using System.Drawing; +using System.Windows.Forms; +using System.Windows.Forms.VisualStyles; +using Vanara.Interop.DesktopWindowManager; + +namespace AeroWizard +{ + /// + /// A Label containing some text that will be drawn with glowing border on top of the Glass Sheet effect. + /// + //[Designer("AeroWizard.Design.ThemedLabelDesigner")] + [DefaultProperty("Text")] + [ToolboxItem(true), ToolboxBitmap(typeof(ThemedLabel), "ThemedLabel.bmp")] + public class ThemedLabel : Label + { + /// + /// Initializes a new instance of the class. + /// + public ThemedLabel() + { + SetStyle(ControlStyles.SupportsTransparentBackColor | + ControlStyles.OptimizedDoubleBuffer | + ControlStyles.AllPaintingInWmPaint | + ControlStyles.ResizeRedraw | + ControlStyles.UserPaint, true); + + BackColor = Color.Transparent; + } + + /// + /// Gets or sets the background color for the control. + /// + /// + /// + /// A that represents the background color of the control. The default is the value of the property. + /// + /// + /// + /// + [DefaultValue(typeof(Color), "Transparent")] + public override Color BackColor + { + get { return base.BackColor; } + set { base.BackColor = value; } + } + + /// + /// Gets or sets the image that is displayed on a . + /// + /// + /// + /// An displayed on the . The default is null. + /// + /// + /// + /// + /// + /// + /// + [DefaultValue((Image)null)] + public new Image Image + { + get { return base.Image; } + set + { + base.Image = value; + base.ImageIndex = -1; + ImageList = null; + } + } + + /// + /// Retrieves the size of a rectangular area into which a control can be fitted. + /// + /// The custom-sized area for a control. + /// + /// An ordered pair of type representing the width and height of a rectangle. + /// + public override Size GetPreferredSize(Size proposedSize) + { + Size sz = base.GetPreferredSize(proposedSize); + if (Text.Length > 0) + sz.Width += 10; + return sz; + } + + internal static Rectangle DeflateRect(Rectangle rect, Padding padding) + { + rect.X += padding.Left; + rect.Y += padding.Top; + rect.Width -= padding.Horizontal; + rect.Height -= padding.Vertical; + return rect; + } + + /// + /// Raises the Paint event. + /// + /// A that contains the event data. + protected override void OnPaint(PaintEventArgs e) + { + if (Visible) + { + VisualStyleRenderer vs = null; + if (Application.RenderWithVisualStyles || DesktopWindowManager.IsCompositionEnabled()) + { + vs = new VisualStyleRenderer(VisualStyleElement.Window.Caption.Active); + vs.DrawParentBackground(e.Graphics, base.ClientRectangle, this); + } + + // Draw image + Rectangle r = DeflateRect(base.ClientRectangle, base.Padding); + if (Image != null) + { + //Rectangle ir = CalcImageRenderBounds(this.Image, r, base.RtlTranslateAlignment(this.ImageAlign)); + if (ImageList != null && ImageIndex == 0) + { + if (vs != null && !this.IsDesignMode() && DesktopWindowManager.IsCompositionEnabled()) + vs.DrawGlassImage(e.Graphics, r, ImageList, ImageIndex); + else + ImageList.Draw(e.Graphics, r.X, r.Y, r.Width, r.Height, ImageIndex); + } + else + { + if (vs != null && !this.IsDesignMode() && DesktopWindowManager.IsCompositionEnabled()) + vs.DrawGlassImage(e.Graphics, r, Image); + else + e.Graphics.DrawImage(Image, r); + } + } + + // Draw text + if (Text.Length > 0) + { + if (this.IsDesignMode() || vs == null || !DesktopWindowManager.IsCompositionEnabled()) + { + Brush br = DesktopWindowManager.IsCompositionEnabled() ? SystemBrushes.ActiveCaptionText : SystemBrushes.ControlText; + StringFormat sf = new StringFormat(StringFormat.GenericDefault); + if (this.GetRightToLeftProperty() == System.Windows.Forms.RightToLeft.Yes) sf.FormatFlags |= StringFormatFlags.DirectionRightToLeft; + e.Graphics.DrawString(Text, Font, br, base.ClientRectangle, sf); + } + else + { + TextFormatFlags tff = CreateTextFormatFlags(base.RtlTranslateAlignment(TextAlign), AutoEllipsis, UseMnemonic); + vs.DrawGlowingText(e.Graphics, base.ClientRectangle, Text, Font, ForeColor, tff); + } + } + } + } + + /// + /// Processes Windows messages. + /// + /// The Windows to process. + protected override void WndProc(ref Message m) + { + const int WM_NCHITTEST = 0x84; + const int HTTRANSPARENT = -1; + + base.WndProc(ref m); + if (m.Msg == WM_NCHITTEST) + m.Result = new IntPtr(HTTRANSPARENT); + } + + private TextFormatFlags CreateTextFormatFlags(System.Drawing.ContentAlignment textAlign, bool showEllipsis, bool useMnemonic) + { + TextFormatFlags flags = TextFormatFlags.GlyphOverhangPadding | TextFormatFlags.SingleLine; + if ((textAlign & (System.Drawing.ContentAlignment.BottomRight | System.Drawing.ContentAlignment.BottomCenter | System.Drawing.ContentAlignment.BottomLeft)) != ((System.Drawing.ContentAlignment)0)) + flags |= TextFormatFlags.Bottom; + if ((textAlign & (System.Drawing.ContentAlignment.MiddleRight | System.Drawing.ContentAlignment.MiddleCenter | System.Drawing.ContentAlignment.MiddleLeft)) != ((System.Drawing.ContentAlignment)0)) + flags |= TextFormatFlags.VerticalCenter; + if ((textAlign & (System.Drawing.ContentAlignment.BottomRight | System.Drawing.ContentAlignment.MiddleRight | System.Drawing.ContentAlignment.TopRight)) != ((System.Drawing.ContentAlignment)0)) + flags |= TextFormatFlags.Right; + if ((textAlign & (System.Drawing.ContentAlignment.BottomCenter | System.Drawing.ContentAlignment.MiddleCenter | System.Drawing.ContentAlignment.TopCenter)) != ((System.Drawing.ContentAlignment)0)) + flags |= TextFormatFlags.HorizontalCenter; + if (showEllipsis) + flags |= TextFormatFlags.EndEllipsis; + if (this.GetRightToLeftProperty() == RightToLeft.Yes) + flags |= TextFormatFlags.RightToLeft; + if (!useMnemonic) + return (flags | TextFormatFlags.NoPrefix); + if (!ShowKeyboardCues) + flags |= TextFormatFlags.HidePrefix; + return flags; + } + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/ThemedTableLayoutPanel.bmp b/AeroWizard/AeroWizard/ThemedTableLayoutPanel.bmp new file mode 100644 index 0000000..5902e71 Binary files /dev/null and b/AeroWizard/AeroWizard/ThemedTableLayoutPanel.bmp differ diff --git a/AeroWizard/AeroWizard/ThemedTableLayoutPanel.cs b/AeroWizard/AeroWizard/ThemedTableLayoutPanel.cs new file mode 100644 index 0000000..05ef342 --- /dev/null +++ b/AeroWizard/AeroWizard/ThemedTableLayoutPanel.cs @@ -0,0 +1,136 @@ +using System.ComponentModel; +using System.Windows.Forms; +using System.Windows.Forms.VisualStyles; +using Vanara.Interop.DesktopWindowManager; + +namespace AeroWizard +{ + /// + /// A table layout panel that supports a glass overlay. + /// + [ToolboxItem(true), System.Drawing.ToolboxBitmap(typeof(ThemedTableLayoutPanel), "ThemedTableLayoutPanel.bmp")] + public class ThemedTableLayoutPanel : TableLayoutPanel + { + private VisualStyleRenderer rnd; + + /// + /// Initializes a new instance of the class. + /// + public ThemedTableLayoutPanel() + { + SetTheme(VisualStyleElement.Window.Dialog.Normal); + } + + /// + /// Clears the theme and defaults to TableLayoutPanel painting. + /// + public void ClearTheme() + { + rnd = null; + } + + /// + /// Sets the theme using a defined . + /// + /// The visual element. + public void SetTheme(VisualStyleElement element) + { + if (VisualStyleRenderer.IsSupported && VisualStyleRenderer.IsElementDefined(element)) + rnd = new VisualStyleRenderer(element); + else + rnd = null; + } + + /// + /// Sets the theme using theme class information. + /// + /// Name of the theme class. + /// The theme part. + /// The theme state. + public void SetTheme(string className, int part, int state) + { + if (VisualStyleRenderer.IsSupported) + { + try + { + rnd = new VisualStyleRenderer(className, part, state); + return; + } + catch { } + } + rnd = null; + } + + /// + /// Gets or sets a value indicating whether to watch getting and losing focus. + /// + /// + /// true if watching focus events; otherwise, false. + /// + [DefaultValue(false), Category("Behavior")] + public bool WatchFocus { get; set; } + + /// + /// Gets or sets a value indicating whether this table supports glass (can be enclosed in the glass margin). + /// + /// + /// true if supports glass; otherwise, false. + /// + [DefaultValue(false), Category("Appearance")] + public bool SupportGlass { get; set; } + + /// + /// Raises the event. + /// + /// An that contains the event data. + protected override void OnHandleCreated(System.EventArgs e) + { + base.OnHandleCreated(e); + AttachToFormEvents(); + } + + /// + /// Raises the event. + /// + /// A that contains the event data. + protected override void OnPaint(PaintEventArgs e) + { + if (!this.IsDesignMode() && SupportGlass && DesktopWindowManager.IsCompositionEnabled()) + try { e.Graphics.Clear(System.Drawing.Color.Black); } catch { } + else + { + if (this.IsDesignMode() || rnd == null || !Application.RenderWithVisualStyles) + try { e.Graphics.Clear(BackColor); } catch { } + else + rnd.DrawBackground(e.Graphics, ClientRectangle, e.ClipRectangle); + } + base.OnPaint(e); + } + + private void AttachToFormEvents() + { + Form pForm = FindForm(); + if (pForm != null && WatchFocus) + { + pForm.Activated += new System.EventHandler(Form_GotFocus); + pForm.Deactivate += new System.EventHandler(Form_LostFocus); + } + } + + private void Form_GotFocus(object sender, System.EventArgs e) + { + OnGotFocus(e); + if (rnd != null && Application.RenderWithVisualStyles) + rnd.SetParameters(rnd.Class, rnd.Part, 1); + Refresh(); + } + + private void Form_LostFocus(object sender, System.EventArgs e) + { + OnLostFocus(e); + if (rnd != null && Application.RenderWithVisualStyles) + rnd.SetParameters(rnd.Class, rnd.Part, 2); + Refresh(); + } + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/VisualStyleElementEx.cs b/AeroWizard/AeroWizard/VisualStyleElementEx.cs new file mode 100644 index 0000000..bbef3cb --- /dev/null +++ b/AeroWizard/AeroWizard/VisualStyleElementEx.cs @@ -0,0 +1,148 @@ +using System.Windows.Forms.VisualStyles; + +namespace AeroWizard.VisualStyles +{ + /// + /// Identifies a control or user interface (UI) element that is drawn with visual styles. + /// + public static class VisualStyleElementEx + { + /// + /// Contains classes that provide objects for navigation-related controls. This class cannot be inherited. + /// + public static class Navigation + { + private const string className = "NAVIGATION"; + + /// + /// Provides objects for the different states of the Back Button control. This class cannot be inherited. + /// + public static class BackButton + { + private const int part = 1; + + /// Gets a visual style element that represents a back button in the normal state. + /// A that represents a back button in the normal state. + public static VisualStyleElement Normal => VisualStyleElement.CreateElement(className, part, 1); + /// Gets a visual style element that represents a back button in the hot state. + /// A that represents a back button in the hot state. + public static VisualStyleElement Hot => VisualStyleElement.CreateElement(className, part, 2); + /// Gets a visual style element that represents a back button in the pressed state. + /// A that represents a back button in the pressed state. + public static VisualStyleElement Pressed => VisualStyleElement.CreateElement(className, part, 3); + /// Gets a visual style element that represents a back button in the disabled state. + /// A that represents a back button in the disabled state. + public static VisualStyleElement Disabled => VisualStyleElement.CreateElement(className, part, 4); + } + + /// + /// Provides objects for the different states of the Forward Button control. This class cannot be inherited. + /// + public static class ForwardButton + { + private const int part = 2; + + /// Gets a visual style element that represents a forward button in the normal state. + /// A that represents a forward button in the normal state. + public static VisualStyleElement Normal => VisualStyleElement.CreateElement(className, part, 1); + /// Gets a visual style element that represents a forward button in the hot state. + /// A that represents a forward button in the hot state. + public static VisualStyleElement Hot => VisualStyleElement.CreateElement(className, part, 2); + /// Gets a visual style element that represents a forward button in the pressed state. + /// A that represents a forward button in the pressed state. + public static VisualStyleElement Pressed => VisualStyleElement.CreateElement(className, part, 3); + /// Gets a visual style element that represents a forward button in the disabled state. + /// A that represents a forward button in the disabled state. + public static VisualStyleElement Disabled => VisualStyleElement.CreateElement(className, part, 4); + } + + /// + /// Provides objects for the different states of the Menu Button control. This class cannot be inherited. + /// + public static class MenuButton + { + private const int part = 3; + + /// Gets a visual style element that represents a menu button in the normal state. + /// A that represents a menu button in the normal state. + public static VisualStyleElement Normal => VisualStyleElement.CreateElement(className, part, 1); + /// Gets a visual style element that represents a menu button in the hot state. + /// A that represents a menu button in the hot state. + public static VisualStyleElement Hot => VisualStyleElement.CreateElement(className, part, 2); + /// Gets a visual style element that represents a menu button in the pressed state. + /// A that represents a menu button in the pressed state. + public static VisualStyleElement Pressed => VisualStyleElement.CreateElement(className, part, 3); + /// Gets a visual style element that represents a menu button in the disabled state. + /// A that represents a menu button in the disabled state. + public static VisualStyleElement Disabled => VisualStyleElement.CreateElement(className, part, 4); + } + } + + /// + /// Contains classes that provide objects for AeroWizard-related controls. This class cannot be inherited. + /// + public static class AeroWizard + { + private const string className = "AEROWIZARD"; + + /// + /// Provides a for the button of a wizard. This class cannot be inherited. + /// + public static class Button + { + /// Gets a visual style element that represents a button in a wizard. + /// A that represents a button in a wizard. + public static VisualStyleElement Normal => VisualStyleElement.CreateElement(className, 5, 0); + } + + /// + /// Provides a for the command area of a wizard. This class cannot be inherited. + /// + public static class CommandArea + { + /// Gets a visual style element that represents the command area of a wizard. + /// A that represents the command area of a wizard. + public static VisualStyleElement Normal => VisualStyleElement.CreateElement(className, 4, 0); + } + + /// + /// Provides a for the content area of a wizard. This class cannot be inherited. + /// + public static class ContentArea + { + /// Gets a visual style element that represents the content area of a wizard. + /// A that represents the content area of a wizard. + public static VisualStyleElement Normal => VisualStyleElement.CreateElement(className, 3, 0); + /// Gets a visual style element that represents the content area of a wizard without a margin. + /// A that represents the content area of a wizard without a margin. + public static VisualStyleElement NoMargin => VisualStyleElement.CreateElement(className, 3, 1); + } + + /// + /// Provides a for the header area of a wizard. This class cannot be inherited. + /// + public static class HeaderArea + { + /// Gets a visual style element that represents the header area of a wizard. + /// A that represents the header area of a wizard. + public static VisualStyleElement Normal => VisualStyleElement.CreateElement(className, 2, 0); + /// Gets a visual style element that represents the header area of a wizard without a margin. + /// A that represents the header area of a wizard without a margin. + public static VisualStyleElement NoMargin => VisualStyleElement.CreateElement(className, 2, 1); + } + + /// + /// Provides a for each state of the titlebar of a wizard. This class cannot be inherited. + /// + public static class TitleBar + { + /// Gets a visual style element that represents the titlebar of an active wizard. + /// A that represents the titlebar of an active wizard. + public static VisualStyleElement Active => VisualStyleElement.CreateElement(className, 1, 1); + /// Gets a visual style element that represents the titlebar of an inactive wizard. + /// A that represents the titlebar of an inactive wizard. + public static VisualStyleElement Inactive => VisualStyleElement.CreateElement(className, 1, 2); + } + } + } +} diff --git a/AeroWizard/AeroWizard/Wizard97ItemTemplate/OldStyleWizard.Designer.cs b/AeroWizard/AeroWizard/Wizard97ItemTemplate/OldStyleWizard.Designer.cs new file mode 100644 index 0000000..12f8e30 --- /dev/null +++ b/AeroWizard/AeroWizard/Wizard97ItemTemplate/OldStyleWizard.Designer.cs @@ -0,0 +1,257 @@ +namespace $rootnamespace$ +{ + partial class $safeitemname$ + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.headerPanel = new System.Windows.Forms.Panel(); + this.headerImage = new System.Windows.Forms.PictureBox(); + this.subHeaderLabel = new System.Windows.Forms.Label(); + this.headerLabel = new System.Windows.Forms.Label(); + this.topDivider = new System.Windows.Forms.Label(); + this.bottomDivider = new System.Windows.Forms.Label(); + this.commandPanel = new System.Windows.Forms.Panel(); + this.backButton = new System.Windows.Forms.Button(); + this.nextButton = new System.Windows.Forms.Button(); + this.cancelButton = new System.Windows.Forms.Button(); + this.wizardPageContainer1 = new AeroWizard.WizardPageContainer(); + this.wizardPage3 = new AeroWizard.WizardPage(); + this.wizardPage2 = new AeroWizard.WizardPage(); + this.wizardPage1 = new AeroWizard.WizardPage(); + this.startEndPicture = new System.Windows.Forms.PictureBox(); + this.headerPanel.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.headerImage)).BeginInit(); + this.commandPanel.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.wizardPageContainer1)).BeginInit(); + this.wizardPageContainer1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.startEndPicture)).BeginInit(); + this.SuspendLayout(); + // + // headerPanel + // + this.headerPanel.BackColor = System.Drawing.SystemColors.Window; + this.headerPanel.Controls.Add(this.headerImage); + this.headerPanel.Controls.Add(this.subHeaderLabel); + this.headerPanel.Controls.Add(this.headerLabel); + this.headerPanel.Dock = System.Windows.Forms.DockStyle.Top; + this.headerPanel.Location = new System.Drawing.Point(0, 0); + this.headerPanel.Name = "headerPanel"; + this.headerPanel.Size = new System.Drawing.Size(480, 57); + this.headerPanel.TabIndex = 2; + // + // headerImage + // + this.headerImage.Image = null; + this.headerImage.Location = new System.Drawing.Point(426, 4); + this.headerImage.Name = "headerImage"; + this.headerImage.Size = new System.Drawing.Size(49, 49); + this.headerImage.TabIndex = 1; + this.headerImage.TabStop = false; + // + // subHeaderLabel + // + this.subHeaderLabel.AutoSize = true; + this.subHeaderLabel.Location = new System.Drawing.Point(12, 31); + this.subHeaderLabel.Name = "subHeaderLabel"; + this.subHeaderLabel.Size = new System.Drawing.Size(74, 13); + this.subHeaderLabel.TabIndex = 0; + this.subHeaderLabel.Text = ""; + // + // headerLabel + // + this.headerLabel.AutoSize = true; + this.headerLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.headerLabel.Location = new System.Drawing.Point(12, 11); + this.headerLabel.Name = "headerLabel"; + this.headerLabel.Size = new System.Drawing.Size(62, 13); + this.headerLabel.TabIndex = 0; + this.headerLabel.Text = "
"; + // + // topDivider + // + this.topDivider.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.topDivider.Dock = System.Windows.Forms.DockStyle.Top; + this.topDivider.Location = new System.Drawing.Point(0, 57); + this.topDivider.Name = "topDivider"; + this.topDivider.Size = new System.Drawing.Size(480, 2); + this.topDivider.TabIndex = 3; + // + // bottomDivider + // + this.bottomDivider.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.bottomDivider.Dock = System.Windows.Forms.DockStyle.Bottom; + this.bottomDivider.Enabled = false; + this.bottomDivider.Location = new System.Drawing.Point(0, 313); + this.bottomDivider.Name = "bottomDivider"; + this.bottomDivider.Size = new System.Drawing.Size(480, 2); + this.bottomDivider.TabIndex = 4; + // + // commandPanel + // + this.commandPanel.Controls.Add(this.backButton); + this.commandPanel.Controls.Add(this.nextButton); + this.commandPanel.Controls.Add(this.cancelButton); + this.commandPanel.Dock = System.Windows.Forms.DockStyle.Bottom; + this.commandPanel.Location = new System.Drawing.Point(0, 315); + this.commandPanel.Name = "commandPanel"; + this.commandPanel.Size = new System.Drawing.Size(480, 40); + this.commandPanel.TabIndex = 5; + // + // backButton + // + this.backButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.backButton.Location = new System.Drawing.Point(171, 9); + this.backButton.Name = "backButton"; + this.backButton.Size = new System.Drawing.Size(97, 23); + this.backButton.TabIndex = 2; + this.backButton.Text = "< Back"; + this.backButton.UseVisualStyleBackColor = true; + // + // nextButton + // + this.nextButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.nextButton.Location = new System.Drawing.Point(270, 9); + this.nextButton.Name = "nextButton"; + this.nextButton.Size = new System.Drawing.Size(97, 23); + this.nextButton.TabIndex = 3; + this.nextButton.Text = "Next >"; + this.nextButton.UseVisualStyleBackColor = true; + // + // cancelButton + // + this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.cancelButton.Location = new System.Drawing.Point(373, 9); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(97, 23); + this.cancelButton.TabIndex = 4; + this.cancelButton.Text = "Cancel"; + this.cancelButton.UseVisualStyleBackColor = true; + // + // wizardPageContainer1 + // + this.wizardPageContainer1.BackButton = this.backButton; + this.wizardPageContainer1.BackButtonText = "< Back"; + this.wizardPageContainer1.CancelButton = this.cancelButton; + this.wizardPageContainer1.CancelButtonText = "Cancel"; + this.wizardPageContainer1.Controls.Add(this.wizardPage2); + this.wizardPageContainer1.Controls.Add(this.wizardPage3); + this.wizardPageContainer1.Controls.Add(this.wizardPage1); + this.wizardPageContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.wizardPageContainer1.Location = new System.Drawing.Point(164, 59); + this.wizardPageContainer1.Name = "wizardPageContainer1"; + this.wizardPageContainer1.NextButton = this.nextButton; + this.wizardPageContainer1.Pages.Add(this.wizardPage1); + this.wizardPageContainer1.Pages.Add(this.wizardPage2); + this.wizardPageContainer1.Pages.Add(this.wizardPage3); + this.wizardPageContainer1.Size = new System.Drawing.Size(316, 254); + this.wizardPageContainer1.TabIndex = 0; + this.wizardPageContainer1.Finished += new System.EventHandler(this.wizardPageContainer1_Finished); + this.wizardPageContainer1.SelectedPageChanged += new System.EventHandler(this.wizardPageContainer1_SelectedPageChanged); + // + // wizardPage3 + // + this.wizardPage3.Name = "wizardPage3"; + this.wizardPage3.Size = new System.Drawing.Size(316, 254); + this.wizardPage3.TabIndex = 2; + this.wizardPage3.Tag = ""; + this.wizardPage3.Text = "Task Completed|You\'re all done!"; + // + // wizardPage2 + // + this.wizardPage2.Name = "wizardPage2"; + this.wizardPage2.Size = new System.Drawing.Size(316, 254); + this.wizardPage2.TabIndex = 1; + this.wizardPage2.Tag = ""; + this.wizardPage2.Text = "Page 2 - Middle|This is the middle page"; + this.wizardPage2.Initialize += new System.EventHandler(this.wizardPage2_Initialize); + // + // wizardPage1 + // + this.wizardPage1.Name = "wizardPage1"; + this.wizardPage1.Size = new System.Drawing.Size(316, 254); + this.wizardPage1.TabIndex = 0; + this.wizardPage1.Text = "Welcom"; + this.wizardPage1.Initialize += new System.EventHandler(this.wizardPage1_Initialize); + // + // startEndPicture + // + this.startEndPicture.BackColor = System.Drawing.Color.Navy; + this.startEndPicture.Dock = System.Windows.Forms.DockStyle.Left; + this.startEndPicture.Location = new System.Drawing.Point(0, 59); + this.startEndPicture.Name = "startEndPicture"; + this.startEndPicture.Size = new System.Drawing.Size(164, 254); + this.startEndPicture.TabIndex = 6; + this.startEndPicture.TabStop = false; + // + // $safeitemname$ + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancelButton; + this.ClientSize = new System.Drawing.Size(480, 355); + this.Controls.Add(this.wizardPageContainer1); + this.Controls.Add(this.startEndPicture); + this.Controls.Add(this.bottomDivider); + this.Controls.Add(this.commandPanel); + this.Controls.Add(this.topDivider); + this.Controls.Add(this.headerPanel); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "$safeitemname$"; + this.Text = "$safeitemname$"; + this.headerPanel.ResumeLayout(false); + this.headerPanel.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.headerImage)).EndInit(); + this.commandPanel.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.wizardPageContainer1)).EndInit(); + this.wizardPageContainer1.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.startEndPicture)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private AeroWizard.WizardPageContainer wizardPageContainer1; + private System.Windows.Forms.Panel headerPanel; + private System.Windows.Forms.Label topDivider; + private System.Windows.Forms.Label bottomDivider; + private System.Windows.Forms.Button backButton; + private System.Windows.Forms.Button cancelButton; + private AeroWizard.WizardPage wizardPage3; + private AeroWizard.WizardPage wizardPage2; + private AeroWizard.WizardPage wizardPage1; + private System.Windows.Forms.Button nextButton; + private System.Windows.Forms.Panel commandPanel; + private System.Windows.Forms.Label subHeaderLabel; + private System.Windows.Forms.Label headerLabel; + private System.Windows.Forms.PictureBox startEndPicture; + private System.Windows.Forms.PictureBox headerImage; + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/Wizard97ItemTemplate/OldStyleWizard.cs b/AeroWizard/AeroWizard/Wizard97ItemTemplate/OldStyleWizard.cs new file mode 100644 index 0000000..219fea9 --- /dev/null +++ b/AeroWizard/AeroWizard/Wizard97ItemTemplate/OldStyleWizard.cs @@ -0,0 +1,40 @@ +using System; +using System.Windows.Forms; + +namespace $rootnamespace$ +{ + public partial class $safeitemname$ : Form + { + public $safeitemname$() + { + InitializeComponent(); + } + + private void wizardPageContainer1_Finished(object sender, EventArgs e) + { + Close(); + } + + private void wizardPageContainer1_SelectedPageChanged(object sender, EventArgs e) + { + string[] headers = new string[] { "" }; + if (wizardPageContainer1.SelectedPage.Text != null) + headers = wizardPageContainer1.SelectedPage.Text.Split('|'); + headerLabel.Text = headers[0]; + if (headers.Length == 2) + subHeaderLabel.Text = headers[1]; + } + + private void wizardPage1_Initialize(object sender, AeroWizard.WizardPageInitEventArgs e) + { + headerPanel.Visible = topDivider.Visible = false; + startEndPicture.Visible = true; + } + + private void wizardPage2_Initialize(object sender, AeroWizard.WizardPageInitEventArgs e) + { + headerPanel.Visible = topDivider.Visible = true; + startEndPicture.Visible = false; + } + } +} diff --git a/AeroWizard/AeroWizard/Wizard97ItemTemplate/OldStyleWizard.resx b/AeroWizard/AeroWizard/Wizard97ItemTemplate/OldStyleWizard.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/AeroWizard/AeroWizard/Wizard97ItemTemplate/OldStyleWizard.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/AeroWizard/AeroWizard/Wizard97ItemTemplate/Properties/AssemblyInfo.cs b/AeroWizard/AeroWizard/Wizard97ItemTemplate/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..80a8d54 --- /dev/null +++ b/AeroWizard/AeroWizard/Wizard97ItemTemplate/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +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("Wizard97ItemTemplate")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Hewlett-Packard Company")] +[assembly: AssemblyProduct("Wizard97ItemTemplate")] +[assembly: AssemblyCopyright("Copyright © Hewlett-Packard Company 2013")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("3f611b4b-5ae2-4b90-a68b-e0322e11a05d")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// 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("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/AeroWizard/AeroWizard/Wizard97ItemTemplate/Wizard97ItemTemplate.csproj b/AeroWizard/AeroWizard/Wizard97ItemTemplate/Wizard97ItemTemplate.csproj new file mode 100644 index 0000000..8ecc7e3 --- /dev/null +++ b/AeroWizard/AeroWizard/Wizard97ItemTemplate/Wizard97ItemTemplate.csproj @@ -0,0 +1,125 @@ + + + + 14.0 + 11.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + 12.0 + SAK + SAK + SAK + SAK + + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + Client + + + + Debug + AnyCPU + {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {D127643F-69AA-4C4C-A624-7754289E798C} + Library + Properties + Wizard97ItemTemplate + Wizard97ItemTemplate + v4.0 + 512 + false + false + false + false + false + false + false + false + false + false + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + OldStyleWizard.cs + + + + + + Windows Forms + Designer + + + + + + + OldStyleWizard.cs + + + + + False + Microsoft .NET Framework 4.5 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + false + + + + + + \ No newline at end of file diff --git a/AeroWizard/AeroWizard/Wizard97ItemTemplate/Wizard97ItemTemplate.vstemplate b/AeroWizard/AeroWizard/Wizard97ItemTemplate/Wizard97ItemTemplate.vstemplate new file mode 100644 index 0000000..b889a60 --- /dev/null +++ b/AeroWizard/AeroWizard/Wizard97ItemTemplate/Wizard97ItemTemplate.vstemplate @@ -0,0 +1,46 @@ + + + + Wizard97ItemTemplate.cs + Wizard97 Control + Windows Form hosting controls that allow it to behave as a Wizard97 look alike. + CSharp + true + 1 + 2.0 + 100 + __TemplateIcon.ico + __PreviewImage.PNG + + + + + System + + + System.Design + + + System.Drawing + + + System.Windows.Forms + + + AeroWizard + + + OldStyleWizard.cs + OldStyleWizard.Designer.cs + OldStyleWizard.resx + + + NuGet.VisualStudio.Interop, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + NuGet.VisualStudio.TemplateWizard + + + + + + + \ No newline at end of file diff --git a/AeroWizard/AeroWizard/Wizard97ItemTemplate/__PreviewImage.PNG b/AeroWizard/AeroWizard/Wizard97ItemTemplate/__PreviewImage.PNG new file mode 100644 index 0000000..f242ecb Binary files /dev/null and b/AeroWizard/AeroWizard/Wizard97ItemTemplate/__PreviewImage.PNG differ diff --git a/AeroWizard/AeroWizard/Wizard97ItemTemplate/__TemplateIcon.ico b/AeroWizard/AeroWizard/Wizard97ItemTemplate/__TemplateIcon.ico new file mode 100644 index 0000000..1137925 Binary files /dev/null and b/AeroWizard/AeroWizard/Wizard97ItemTemplate/__TemplateIcon.ico differ diff --git a/AeroWizard/AeroWizard/WizardControl.bmp b/AeroWizard/AeroWizard/WizardControl.bmp new file mode 100644 index 0000000..b5e9e6d Binary files /dev/null and b/AeroWizard/AeroWizard/WizardControl.bmp differ diff --git a/AeroWizard/AeroWizard/WizardControl.cs b/AeroWizard/AeroWizard/WizardControl.cs new file mode 100644 index 0000000..32088ef --- /dev/null +++ b/AeroWizard/AeroWizard/WizardControl.cs @@ -0,0 +1,832 @@ +using AeroWizard.VisualStyles; +using System; +using System.ComponentModel; +using System.Drawing; +using System.Windows.Forms; +using System.Windows.Forms.VisualStyles; +using Vanara.Interop; +using Vanara.Interop.DesktopWindowManager; +using ScoutBase.Core; + +namespace AeroWizard +{ + /// + /// Styles that can be applied to the body of a when on XP or earlier or when a Basic theme is applied. + /// + public enum WizardClassicStyle + { + /// Windows Vista style theme with large fonts and white background. + AeroStyle, + /// Windows XP style theme with control color background. + BasicStyle, + /// Use on Windows XP and for later versions. + Automatic + } + + /// + /// Control providing an "Aero Wizard" style interface. + /// + [Designer(typeof(Design.WizardControlDesigner))] + [ToolboxItem(true), ToolboxBitmap(typeof(WizardControl), "WizardControl.bmp")] + [Description("Creates an Aero Wizard interface.")] + [DefaultProperty("Pages"), DefaultEvent("SelectedPageChanged")] + public partial class WizardControl : +#if DEBUG + UserControl +#else + Control +#endif + , ISupportInitialize + { + private static readonly bool isMin6 = Environment.OSVersion.Version.Major >= 6; + + private WizardClassicStyle classicStyle = WizardClassicStyle.AeroStyle; + private Point formMoveLastMousePos; + private bool formMoveTracking; + private Form parentForm; + private bool themePropsSet; + private Icon titleImageIcon; + private bool titleImageIconSet; + + internal int contentCol = 1; + + /// + /// Initializes a new instance of the class. + /// + public WizardControl() + { + InitializeComponent(); + + OnRightToLeftChanged(EventArgs.Empty); + + // Get localized defaults for button text + ResetTitle(); + ResetTitleIcon(); + + // Connect to page add and remove events to track property changes + Pages.ItemAdded += Pages_ItemAdded; + Pages.ItemDeleted += Pages_ItemDeleted; + } + + /// + /// Occurs when the user clicks the Cancel button and allows for programmatic cancellation. + /// + [Category("Behavior"), Description("Occurs when the user clicks the Cancel button and allows for programmatic cancellation.")] + public event CancelEventHandler Cancelling; + + /// + /// Occurs when the user clicks the Next/Finish button and the page is set to or this is the last page in the collection. + /// + [Category("Behavior"), Description("Occurs when the user clicks the Next/Finish button on last page.")] + public event EventHandler Finished; + + /// + /// Occurs when the property has changed. + /// + [Category("Property Changed"), Description("Occurs when the SelectedPage property has changed.")] + public event EventHandler SelectedPageChanged; + + /// + /// Gets or sets the state of the back button. + /// + /// The state of the back button. + [Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public WizardCommandButtonState BackButtonState => pageContainer.BackButtonState; + + /// + /// Gets or sets the back button text. + /// + /// The back button text. + [Category("Wizard"), Localizable(true), Description("The back button text")] + public string BackButtonText + { + get { return pageContainer.BackButtonText; } + set { pageContainer.BackButtonText = value; Invalidate(); } + } + + /// + /// Gets the state of the cancel button. + /// + /// The state of the cancel button. + [Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public WizardCommandButtonState CancelButtonState => pageContainer.CancelButtonState; + + /// + /// Gets or sets the cancel button text. + /// + /// The cancel button text. + [Category("Wizard"), Localizable(true), Description("The cancel button text")] + public string CancelButtonText + { + get { return pageContainer.CancelButtonText; } + set { pageContainer.CancelButtonText = value; Refresh(); } + } + + /// + /// Gets or sets the style applied to the body of a when on XP or earlier or when a Basic theme is applied. + /// + /// A value which determines the style. + [Category("Wizard"), DefaultValue(typeof(WizardClassicStyle), "AeroStyle"), Description("The style used in Windows Classic mode or on Windows XP")] + public WizardClassicStyle ClassicStyle + { + get { return classicStyle; } + set { classicStyle = value; ConfigureStyles(); Invalidate(); } + } + + /// + /// Gets or sets the finish button text. + /// + /// The finish button text. + [Category("Wizard"), Localizable(true), Description("The finish button text")] + public string FinishButtonText + { + get { return pageContainer.FinishButtonText; } + set { pageContainer.FinishButtonText = value; Refresh(); } + } + + /// + /// Gets or sets the page header text. + /// + /// The page header text. + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public string HeaderText + { + get { return headerLabel.Text; } + set { headerLabel.Text = value; Refresh(); } + } + + /// + /// Gets or sets the shield icon on the next button. + /// + /// true if Next button should display a shield; otherwise, false. + /// Setting a UAF shield on a button only works on Vista and later versions of Windows. + [DefaultValue(false), Category("Wizard"), Description("Show a shield icon on the next button")] + public Boolean NextButtonShieldEnabled + { + get { return pageContainer.NextButtonShieldEnabled; } + set { pageContainer.NextButtonShieldEnabled = value; } + } + + /// + /// Gets the state of the next button. + /// + /// The state of the next button. + [Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public WizardCommandButtonState NextButtonState => pageContainer.NextButtonState; + + /// + /// Gets or sets the next button text. + /// + /// The next button text. + [Category("Wizard"), Localizable(true), Description("The next button text.")] + public string NextButtonText + { + get { return pageContainer.NextButtonText; } + set { pageContainer.NextButtonText = value; Refresh(); } + } + + /// + /// Gets the collection of wizard pages in this wizard control. + /// + /// The that contains the objects in this . + [Category("Wizard"), Description("Collection of wizard pages.")] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] + public WizardPageCollection Pages => pageContainer.Pages; + + /// + /// Gets how far the wizard has progressed, as a percentage. + /// + /// A value between 0 and 100. + [Browsable(false), Description("The percentage of the current page against all pages at run-time.")] + public short PercentComplete => pageContainer.PercentComplete; + + /// + /// Gets the currently selected wizard page. + /// + /// The selected wizard page. null if no page is active. + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public virtual WizardPage SelectedPage + { + get { return pageContainer.SelectedPage; } + internal set { pageContainer.SelectedPage = value; if (value != null) HeaderText = value.Text; } + } + + /// + /// Gets or sets a value indicating whether to show progress in form's taskbar icon. + /// + /// + /// This will only work on Windows 7 or later and the parent form must be showing its icon in the taskbar. No exception is thrown on failure. + /// + /// + /// true to show progress in taskbar icon; otherwise, false. + /// + [Category("Wizard"), DefaultValue(false), Description("Indicates whether to show progress in form's taskbar icon")] + public bool ShowProgressInTaskbarIcon + { + get { return pageContainer.ShowProgressInTaskbarIcon; } + set { pageContainer.ShowProgressInTaskbarIcon = value; } + } + + /// + /// Gets or sets a value indicating whether to suppress changing the parent form's icon to match the wizard's . + /// + /// true to not change the parent form's icon to match this wizard's icon; otherwise, false. + [Category("Wizard"), DefaultValue(false), Description("Indicates whether to suppress changing the parent form's icon to match the wizard's")] + public bool SuppressParentFormIconSync { get; set; } + + /// + /// Gets or sets a value indicating whether to suppress changing the parent form's caption to match the wizard's . + /// + /// true to not change the parent form's caption (Text) to match this wizard's title; otherwise, false. + [Category("Wizard"), DefaultValue(false), Description("Indicates whether to suppress changing the parent form's caption to match the wizard's")] + public bool SuppressParentFormCaptionSync { get; set; } + + /// + /// Gets or sets the title for the wizard. + /// + /// The title text. + [Category("Wizard"), Localizable(true), Description("Title for the wizard")] + public string Title + { + get { return title.Text; } + set { title.Text = value; Invalidate(); } + } + + /// + /// Gets or sets the optionally displayed icon next to the wizard title. + /// + /// The title icon. + [Category("Wizard"), Localizable(true), Description("Icon next to the wizard title")] + public Icon TitleIcon + { + get { return titleImageIcon; } + set + { + titleImageIcon = value; + titleImageList.Images.Clear(); + if (titleImageIcon != null) + { + // Resolve for different DPI settings and ensure that if icon is not a standard size, such as 20x20, + // that the larger one (24x24) is downsized and not the smaller up-sized. (thanks demidov) + titleImage.Size = titleImageList.ImageSize = SystemInformation.SmallIconSize; + titleImageList.Images.Add(new Icon(value, SystemInformation.SmallIconSize + new Size(1, 1))); + titleImage.ImageIndex = 0; + } + titleImageIconSet = true; + Invalidate(); + } + } + + internal int SelectedPageIndex => pageContainer.SelectedPageIndex; + + private bool UseAeroStyle => !SupportFunctions.IsMono && (classicStyle == WizardClassicStyle.AeroStyle || (classicStyle == WizardClassicStyle.Automatic && DesktopWindowManager.CompositionSupported && Application.RenderWithVisualStyles)); + + /// + /// Adds a new control to the command bar. + /// + /// This will cause your wizard to deviate from the Windows UI guidelines. All controls will display right to left in the order added and will cause + /// the command bar to remain visible as long as the control is visible. The developer must fully manage the state of this added control. + /// The control to add. + public void AddCommandControl(Control ctrl) + { + commandAreaButtonFlowLayout.Controls.Add(ctrl); + } + + /// + /// Signals the object that initialization is starting. + /// + public void BeginInit() + { + pageContainer.BeginInit(); + } + + /// + /// Signals the object that initialization is complete. + /// + public void EndInit() + { + pageContainer.EndInit(); + } + + /// + /// Advances to the specified page. + /// + /// The wizard page to go to next. + /// if set to true skip event. + /// When specifying a value for nextPage, it must already be in the Pages collection. + public virtual void NextPage(WizardPage nextPage = null, bool skipCommit = false) + { + pageContainer.NextPage(nextPage, skipCommit); + } + + /// + /// Overrides the theme fonts provided by the system. + /// + /// This is NOT recommended as it will cause the wizard to not match those provided by the system. This should be called only after the handle has been created or it will be overridden with the system theme values. + /// The title font. + /// The header font. + /// The command buttons font. + public virtual void OverrideThemeFonts(Font titleFont, Font headerFont, Font buttonFont) + { + title.Font = titleFont; + headerLabel.Font = headerFont; + foreach (Control ctrl in commandAreaButtonFlowLayout.Controls) + { + ctrl.Font = buttonFont; + } + } + + /// + /// Returns to the previous page. + /// + public virtual void PreviousPage() + { + pageContainer.PreviousPage(); + } + + /// + /// Restarts the wizard pages from the first page. + /// + public void RestartPages() + { + pageContainer.RestartPages(); + } + + /// + /// Gets the unthemed back button image. + /// + /// with the four state images stacked on top of each other. + protected virtual Bitmap GetUnthemedBackButtonImage() + { + if (Environment.OSVersion.Version >= new Version(6, 2)) + return Properties.Resources.BackBtnStrip2; + else + return Properties.Resources.BackBtnStrip; + } + + /// + /// Raises the event. + /// + protected virtual void OnCancelling() + { + var arg = new CancelEventArgs(true); + Cancelling?.Invoke(this, arg); + + if (arg.Cancel) + { + if (!this.IsDesignMode()) + CloseForm(DialogResult.Cancel); + } + } + + /// + /// Raises the event. + /// + /// A that contains the event data. + protected override void OnControlAdded(ControlEventArgs e) + { + base.OnControlAdded(e); + var page = e.Control as WizardPage; + if (page == null) return; + Controls.Remove(page); + Pages.Add(page); + } + + /// + /// Raises the event. + /// + protected virtual void OnFinished() + { + Finished?.Invoke(this, EventArgs.Empty); + + if (!this.IsDesignMode()) + CloseForm(DialogResult.OK); + } + + /// + /// Raises the event. + /// + /// An that contains the event data. + protected override void OnGotFocus(EventArgs e) + { + base.OnGotFocus(e); + pageContainer.Focus(); + } + + /// + /// Raises the event. + /// + /// An that contains the event data. + protected override void OnHandleCreated(EventArgs e) + { + System.Diagnostics.Debug.WriteLine("OnHandleCreated"); + base.OnHandleCreated(e); + SetLayout(); + AddSystemEvents(); + } + + /// + /// Raises the event. + /// + /// An that contains the event data. + protected override void OnHandleDestroyed(EventArgs e) + { + RemoveSystemEvents(); + base.OnHandleDestroyed(e); + } + + private void AddSystemEvents() + { + if (!SupportFunctions.IsMono && !this.IsDesignMode()) + { + if (DesktopWindowManager.CompositionSupported) + { + DesktopWindowManager.ColorizationColorChanged += DisplyColorOrCompositionChanged; + DesktopWindowManager.CompositionChanged += DisplyColorOrCompositionChanged; + } + Microsoft.Win32.SystemEvents.DisplaySettingsChanged += DisplyColorOrCompositionChanged; + SystemColorsChanged += DisplyColorOrCompositionChanged; + } + } + + private void RemoveSystemEvents() + { + if (!SupportFunctions.IsMono && !this.IsDesignMode()) + { + if (DesktopWindowManager.CompositionSupported) + { + DesktopWindowManager.CompositionChanged -= DisplyColorOrCompositionChanged; + DesktopWindowManager.ColorizationColorChanged -= DisplyColorOrCompositionChanged; + } + Microsoft.Win32.SystemEvents.DisplaySettingsChanged -= DisplyColorOrCompositionChanged; + SystemColorsChanged -= DisplyColorOrCompositionChanged; + } + } + + /// + /// Raises the event. + /// + /// An that contains the event data. + protected override void OnParentChanged(EventArgs e) + { + base.OnParentChanged(e); + if (parentForm != null) + parentForm.Load -= parentForm_Load; + parentForm = base.Parent as Form; // FindForm(); + Dock = DockStyle.Fill; + if (parentForm != null) + parentForm.Load += parentForm_Load; + } + + /// + /// Raises the event. + /// + /// An that contains the event data. + protected override void OnRightToLeftChanged(EventArgs e) + { + base.OnRightToLeftChanged(e); + var r2l = this.GetRightToLeftProperty() == RightToLeft.Yes; + var btnStrip = GetUnthemedBackButtonImage(); + if (r2l) btnStrip.RotateFlip(RotateFlipType.RotateNoneFlipX); +// backButton.SetImageListImageStrip(btnStrip, Orientation.Vertical); +// backButton.StylePart = r2l ? 2 : 1; + } + + /// + /// Raises the event. + /// + protected void OnSelectedPageChanged() + { + SelectedPageChanged?.Invoke(this, EventArgs.Empty); + } + + private void CloseForm(DialogResult dlgResult) + { + var form = FindForm(); + if (form != null) + { + if (form.Modal) + form.DialogResult = dlgResult; + else + form.Close(); + } + } + + private void ConfigureStyles() + { + if (!SupportFunctions.IsMono && Application.RenderWithVisualStyles) + { + titleBar.SetTheme(VisualStyleElementEx.AeroWizard.TitleBar.Active); + header.SetTheme(VisualStyleElementEx.AeroWizard.HeaderArea.Normal); + contentArea.SetTheme(VisualStyleElementEx.AeroWizard.ContentArea.Normal); + commandArea.SetTheme(VisualStyleElementEx.AeroWizard.CommandArea.Normal); + } + else + { + titleBar.ClearTheme(); + header.ClearTheme(); + contentArea.ClearTheme(); + commandArea.ClearTheme(); + titleBar.BackColor = SystemColors.Control; + } + + if (UseAeroStyle) + { + bodyPanel.BorderStyle = BorderStyle.None; + header.BackColor = contentArea.BackColor = SystemColors.Window; + if (!themePropsSet) + { + headerLabel.Font = new Font("Segoe UI", 12F, FontStyle.Regular, GraphicsUnit.Point, 0); + headerLabel.ForeColor = Color.FromArgb(19, 112, 171); + title.Font = Font; + } + } + else + { + bodyPanel.BorderStyle = BorderStyle.FixedSingle; + header.BackColor = contentArea.BackColor = SystemColors.Control; + headerLabel.Font = new Font("Segoe UI", 9F, FontStyle.Bold, GraphicsUnit.Point, 0); + headerLabel.ForeColor = SystemColors.ControlText; + title.Font = new Font(Font, FontStyle.Bold); + } + } + + private void ConfigureWindowFrame() + { +// System.Diagnostics.Debug.WriteLine($"ConfigureWindowFrame: compEnab={DesktopWindowManager.IsCompositionEnabled()},parentForm={(parentForm == null ? "null" : parentForm.Name)}"); + ConfigureStyles(); + if (!SupportFunctions.IsMono && DesktopWindowManager.IsCompositionEnabled()) + { + titleBar.BackColor = Color.Black; + try + { + if (!parentForm.GetWindowAttribute(DesktopWindowManager.GetWindowAttr.NonClientRenderingEnabled)) + parentForm.SetWindowAttribute(DesktopWindowManager.SetWindowAttr.NonClientRenderingPolicy, DesktopWindowManager.NonClientRenderingPolicy.Enabled); + parentForm.ExtendFrameIntoClientArea(new Padding(0)); + NativeMethods.SetWindowPos(this.Handle, IntPtr.Zero, this.Location.X, this.Location.Y, this.Width, this.Height, NativeMethods.SetWindowPosFlags.FrameChanged); + if (!SupportFunctions.IsMono) + parentForm?.ExtendFrameIntoClientArea(new Padding(0) {Top = titleBar.Visible ? titleBar.Height : 0}); + } + catch + { + titleBar.BackColor = commandArea.BackColor; + } + } + else + { + titleBar.BackColor = commandArea.BackColor; + } + + if (parentForm == null) return; + if (!SuppressParentFormCaptionSync) + parentForm.Text = Title; + if (!SuppressParentFormIconSync && titleImageIcon != null) + { + parentForm.Icon = TitleIcon; + parentForm.ShowIcon = true; + } + parentForm.CancelButton = cancelButton; + parentForm.AcceptButton = nextButton; + parentForm.AutoScaleMode = AutoScaleMode.Font; + if (!SupportFunctions.IsMono) + parentForm.SetWindowThemeAttribute(NativeMethods.WindowThemeNonClientAttributes.NoDrawCaption | NativeMethods.WindowThemeNonClientAttributes.NoDrawIcon | NativeMethods.WindowThemeNonClientAttributes.NoSysMenu); + parentForm.Invalidate(); + } + + private void contentArea_Paint(object sender, PaintEventArgs pe) + { + if (this.IsDesignMode() && Pages.Count == 0) + { + var noPagesText = Properties.Resources.WizardNoPagesNotice; + var r = GetContentAreaRectangle(false); + + r.Inflate(-2, -2); + //pe.Graphics.DrawRectangle(SystemPens.GrayText, r); + ControlPaint.DrawFocusRectangle(pe.Graphics, r); + + var textSize = pe.Graphics.MeasureString(noPagesText, Font); + r.Inflate((r.Width - (int)textSize.Width) / -2, (r.Height - (int)textSize.Height) / -2); + pe.Graphics.DrawString(noPagesText, Font, SystemBrushes.GrayText, r); + } + } + + private void DisplyColorOrCompositionChanged(object sender, EventArgs e) + { + SetLayout(); + ConfigureWindowFrame(); + parentForm?.Refresh(); + } + + /// + /// Gets the content area rectangle. + /// + /// if set to true rectangle is relative to parent. + /// Coordinates of content area. + private Rectangle GetContentAreaRectangle(bool parentRelative) + { + var cw = contentArea.GetColumnWidths(); + var ch = contentArea.GetRowHeights(); + var r = new Rectangle(cw[contentCol - 1], 0, cw[contentCol], ch[0]); + if (parentRelative) + r.Offset(contentArea.Location); + return r; + } + + private void pageContainer_ButtonStateChanged(object sender, EventArgs e) + { + var vis = false; + foreach (Control c in commandAreaButtonFlowLayout.Controls) + { + if (c.Visible || (c is ButtonBase && pageContainer.GetCmdButtonState(c as ButtonBase) != WizardCommandButtonState.Hidden)) + vis = true; + } + commandArea.Visible = vis; + } + + private void pageContainer_Cancelling(object sender, CancelEventArgs e) + { + OnCancelling(); + } + + private void pageContainer_Finished(object sender, EventArgs e) + { + OnFinished(); + } + + private void pageContainer_SelectedPageChanged(object sender, EventArgs e) + { + if (pageContainer.SelectedPage != null) + HeaderText = pageContainer.SelectedPage.Text; + OnSelectedPageChanged(); + } + + private void Pages_ItemAdded(object sender, System.Collections.Generic.EventedList.ListChangedEventArgs e) + { + e.Item.TextChanged += Page_TextChanged; + } + + private void Pages_ItemDeleted(object sender, System.Collections.Generic.EventedList.ListChangedEventArgs e) + { + e.Item.TextChanged -= Page_TextChanged; + } + + private void Page_TextChanged(object sender, EventArgs e) + { + HeaderText = ((WizardPage)sender).Text; + } + + private void parentForm_Load(object sender, EventArgs e) + { + ConfigureWindowFrame(); + } + + private void ResetBackButtonText() + { + pageContainer.ResetBackButtonText(); + } + + private void ResetCancelButtonText() + { + pageContainer.ResetCancelButtonText(); + } + + private void ResetFinishButtonText() + { + pageContainer.ResetFinishButtonText(); + } + + private void ResetNextButtonText() + { + pageContainer.ResetNextButtonText(); + } + + private void ResetTitle() + { + Title = Properties.Resources.WizardTitle; + } + + private void ResetTitleIcon() + { + TitleIcon = Properties.Resources.WizardControlIcon; + titleImageIconSet = false; + } + + private void SetLayout() + { + if (isMin6 && Application.RenderWithVisualStyles) + { + using (var g = CreateGraphics()) + { + // Back button + var theme = new VisualStyleRenderer(VisualStyleElementEx.Navigation.BackButton.Normal); + var bbSize = theme.GetPartSize(g, ThemeSizeType.Draw); + + // Title + theme.SetParameters(VisualStyleElementEx.AeroWizard.TitleBar.Active); + title.Font = theme.GetFont2(g); + titleBar.Height = Math.Max(theme.GetMargins2(g, MarginProperty.ContentMargins).Top, bbSize.Height + 2); + titleBar.ColumnStyles[0].Width = bbSize.Width + 4F; + titleBar.ColumnStyles[1].Width = titleImageIcon != null ? titleImageList.ImageSize.Width + 4F : 0; + backButton.Size = bbSize; + + // Header + theme.SetParameters(VisualStyleElementEx.AeroWizard.HeaderArea.Normal); + headerLabel.Font = theme.GetFont2(g); + headerLabel.Margin = theme.GetMargins2(g, MarginProperty.ContentMargins); + headerLabel.ForeColor = theme.GetColor(ColorProperty.TextColor); + + // Content + theme.SetParameters(VisualStyleElementEx.AeroWizard.ContentArea.Normal); + BackColor = theme.GetColor(ColorProperty.FillColor); + contentArea.Font = theme.GetFont2(g); + var cp = theme.GetMargins2(g, MarginProperty.ContentMargins); + contentArea.ColumnStyles[0].Width = cp.Left; + contentArea.RowStyles[1].Height = cp.Bottom; + + // Command + theme.SetParameters(VisualStyleElementEx.AeroWizard.CommandArea.Normal); + cp = theme.GetMargins2(g, MarginProperty.ContentMargins); + commandArea.RowStyles[0].Height = cp.Top; + commandArea.RowStyles[2].Height = cp.Bottom; + commandArea.ColumnStyles[1].Width = contentArea.ColumnStyles[contentCol + 1].Width = cp.Right; + commandAreaBorder.Height = 0; + theme.SetParameters(VisualStyleElementEx.AeroWizard.Button.Normal); + var btnHeight = theme.GetInteger(IntegerProperty.Height); + commandAreaButtonFlowLayout.MinimumSize = new Size(0, btnHeight); + var btnFont = theme.GetFont2(g); + foreach (Control ctrl in commandAreaButtonFlowLayout.Controls) + { + ctrl.Font = btnFont; + ctrl.Height = btnHeight; + //ctrl.MaximumSize = new Size(0, btnHeight); + } + + themePropsSet = true; + } + } + else + { + commandAreaBorder.Height = 1; + backButton.Size = new Size(GetUnthemedBackButtonImage().Width, GetUnthemedBackButtonImage().Height / 4); + BackColor = UseAeroStyle ? SystemColors.Window : SystemColors.Control; + } + } + + private bool ShouldSerializeBackButtonText() => pageContainer.ShouldSerializeBackButtonText(); + + private bool ShouldSerializeCancelButtonText() => pageContainer.ShouldSerializeCancelButtonText(); + + private bool ShouldSerializeFinishButtonText() => pageContainer.ShouldSerializeFinishButtonText(); + + private bool ShouldSerializeNextButtonText() => pageContainer.ShouldSerializeNextButtonText(); + + private bool ShouldSerializeTitle() => Title != Properties.Resources.WizardTitle; + + private bool ShouldSerializeTitleIcon() => titleImageIconSet; + + private void TitleBar_MouseDown(object sender, MouseEventArgs e) + { + if (e.Button == MouseButtons.Left) + { + var c = titleBar.GetChildAtPoint(e.Location); + if (c != backButton) + { + formMoveTracking = true; + formMoveLastMousePos = PointToScreen(e.Location); + } + } + + OnMouseDown(e); + } + + private void TitleBar_MouseMove(object sender, MouseEventArgs e) + { + if (formMoveTracking) + { + var screen = PointToScreen(e.Location); + + var diff = new Point(screen.X - formMoveLastMousePos.X, screen.Y - formMoveLastMousePos.Y); + + var loc = parentForm.Location; + loc.Offset(diff); + parentForm.Location = loc; + + formMoveLastMousePos = screen; + } + + OnMouseMove(e); + } + + private void TitleBar_MouseUp(object sender, MouseEventArgs e) + { + if (e.Button == MouseButtons.Left) + formMoveTracking = false; + + OnMouseUp(e); + } + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/WizardControl.designer.cs b/AeroWizard/AeroWizard/WizardControl.designer.cs new file mode 100644 index 0000000..11276c0 --- /dev/null +++ b/AeroWizard/AeroWizard/WizardControl.designer.cs @@ -0,0 +1,325 @@ +namespace AeroWizard +{ + partial class WizardControl + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.titleImageList = new System.Windows.Forms.ImageList(this.components); + this.commandAreaBorder = new System.Windows.Forms.Panel(); + this.bodyPanel = new System.Windows.Forms.Panel(); + this.contentArea = new AeroWizard.ThemedTableLayoutPanel(); + this.pageContainer = new AeroWizard.WizardPageContainer(); + this.backButton = new System.Windows.Forms.Button(); + this.cancelButton = new System.Windows.Forms.Button(); + this.nextButton = new System.Windows.Forms.Button(); + this.header = new AeroWizard.ThemedTableLayoutPanel(); + this.headerLabel = new System.Windows.Forms.Label(); + this.commandArea = new AeroWizard.ThemedTableLayoutPanel(); + this.commandAreaButtonFlowLayout = new System.Windows.Forms.FlowLayoutPanel(); + this.titleBar = new AeroWizard.ThemedTableLayoutPanel(); + this.title = new AeroWizard.ThemedLabel(); + this.titleImage = new AeroWizard.ThemedLabel(); + this.bodyPanel.SuspendLayout(); + this.contentArea.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pageContainer)).BeginInit(); + this.header.SuspendLayout(); + this.commandArea.SuspendLayout(); + this.commandAreaButtonFlowLayout.SuspendLayout(); + this.titleBar.SuspendLayout(); + this.SuspendLayout(); + // + // titleImageList + // + this.titleImageList.ColorDepth = System.Windows.Forms.ColorDepth.Depth32Bit; + this.titleImageList.ImageSize = new System.Drawing.Size(16, 16); + this.titleImageList.TransparentColor = System.Drawing.Color.Transparent; + // + // commandAreaBorder + // + this.commandAreaBorder.BackColor = System.Drawing.SystemColors.ControlLight; + this.commandAreaBorder.Dock = System.Windows.Forms.DockStyle.Bottom; + this.commandAreaBorder.Location = new System.Drawing.Point(0, 368); + this.commandAreaBorder.Margin = new System.Windows.Forms.Padding(0); + this.commandAreaBorder.Name = "commandAreaBorder"; + this.commandAreaBorder.Size = new System.Drawing.Size(609, 1); + this.commandAreaBorder.TabIndex = 2; + // + // bodyPanel + // + this.bodyPanel.Controls.Add(this.contentArea); + this.bodyPanel.Controls.Add(this.header); + this.bodyPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.bodyPanel.Location = new System.Drawing.Point(0, 32); + this.bodyPanel.Name = "bodyPanel"; + this.bodyPanel.Size = new System.Drawing.Size(609, 336); + this.bodyPanel.TabIndex = 1; + // + // contentArea + // + this.contentArea.BackColor = System.Drawing.SystemColors.Window; + this.contentArea.ColumnCount = 3; + this.contentArea.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 38F)); + this.contentArea.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.contentArea.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 19F)); + this.contentArea.Controls.Add(this.pageContainer, 1, 0); + this.contentArea.Dock = System.Windows.Forms.DockStyle.Fill; + this.contentArea.Location = new System.Drawing.Point(0, 59); + this.contentArea.Name = "contentArea"; + this.contentArea.RowCount = 2; + this.contentArea.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.contentArea.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 19F)); + this.contentArea.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F)); + this.contentArea.Size = new System.Drawing.Size(609, 277); + this.contentArea.TabIndex = 1; + this.contentArea.Paint += new System.Windows.Forms.PaintEventHandler(this.contentArea_Paint); + // + // pageContainer + // + this.pageContainer.BackButton = this.backButton; + this.pageContainer.BackButtonText = ""; + this.pageContainer.CancelButton = this.cancelButton; + this.pageContainer.Dock = System.Windows.Forms.DockStyle.Fill; + this.pageContainer.Location = new System.Drawing.Point(38, 0); + this.pageContainer.Margin = new System.Windows.Forms.Padding(0); + this.pageContainer.Name = "pageContainer"; + this.pageContainer.NextButton = this.nextButton; + this.pageContainer.Size = new System.Drawing.Size(552, 258); + this.pageContainer.TabIndex = 0; + this.pageContainer.ButtonStateChanged += new System.EventHandler(this.pageContainer_ButtonStateChanged); + this.pageContainer.Cancelling += new System.ComponentModel.CancelEventHandler(this.pageContainer_Cancelling); + this.pageContainer.Finished += new System.EventHandler(this.pageContainer_Finished); + this.pageContainer.SelectedPageChanged += new System.EventHandler(this.pageContainer_SelectedPageChanged); + // + // backButton + // + this.backButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.backButton.AutoSize = true; + this.backButton.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.backButton.Location = new System.Drawing.Point(366, 0); + this.backButton.Margin = new System.Windows.Forms.Padding(7, 0, 0, 0); + this.backButton.MinimumSize = new System.Drawing.Size(70, 15); + this.backButton.Name = "backButton"; + this.backButton.Size = new System.Drawing.Size(70, 25); + this.backButton.TabIndex = 1; + this.backButton.Tag = AeroWizard.WizardCommandButtonState.Disabled; + this.backButton.Text = "&Back"; + this.backButton.UseVisualStyleBackColor = true; + // + // cancelButton + // + this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.cancelButton.AutoSize = true; + this.cancelButton.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.cancelButton.Location = new System.Drawing.Point(520, 0); + this.cancelButton.Margin = new System.Windows.Forms.Padding(7, 0, 0, 0); + this.cancelButton.MinimumSize = new System.Drawing.Size(70, 15); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.Size = new System.Drawing.Size(70, 25); + this.cancelButton.TabIndex = 1; + this.cancelButton.Tag = AeroWizard.WizardCommandButtonState.Disabled; + this.cancelButton.Text = "&Cancel"; + this.cancelButton.UseVisualStyleBackColor = true; + // + // nextButton + // + this.nextButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.nextButton.AutoSize = true; + this.nextButton.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.nextButton.Location = new System.Drawing.Point(443, 0); + this.nextButton.Margin = new System.Windows.Forms.Padding(7, 0, 0, 0); + this.nextButton.MinimumSize = new System.Drawing.Size(70, 15); + this.nextButton.Name = "nextButton"; + this.nextButton.Size = new System.Drawing.Size(70, 23); + this.nextButton.TabIndex = 0; + this.nextButton.Text = "&Next"; + this.nextButton.UseVisualStyleBackColor = true; + // + // header + // + this.header.AutoSize = true; + this.header.BackColor = System.Drawing.SystemColors.Window; + this.header.ColumnCount = 1; + this.header.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.header.Controls.Add(this.headerLabel, 0, 0); + this.header.Dock = System.Windows.Forms.DockStyle.Top; + this.header.Location = new System.Drawing.Point(0, 0); + this.header.Name = "header"; + this.header.RowCount = 1; + this.header.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.header.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 59F)); + this.header.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 59F)); + this.header.Size = new System.Drawing.Size(609, 59); + this.header.TabIndex = 0; + // + // headerLabel + // + this.headerLabel.AutoSize = true; + this.headerLabel.BackColor = System.Drawing.Color.Transparent; + this.headerLabel.Font = new System.Drawing.Font("Segoe UI", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.headerLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(19)))), ((int)(((byte)(112)))), ((int)(((byte)(171))))); + this.headerLabel.Location = new System.Drawing.Point(38, 19); + this.headerLabel.Margin = new System.Windows.Forms.Padding(38, 19, 0, 19); + this.headerLabel.Name = "headerLabel"; + this.headerLabel.Size = new System.Drawing.Size(77, 21); + this.headerLabel.TabIndex = 0; + this.headerLabel.Text = "Page Title"; + // + // commandArea + // + this.commandArea.AutoSize = true; + this.commandArea.BackColor = System.Drawing.SystemColors.Control; + this.commandArea.ColumnCount = 2; + this.commandArea.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.commandArea.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 19F)); + this.commandArea.Controls.Add(this.commandAreaButtonFlowLayout, 0, 1); + this.commandArea.Dock = System.Windows.Forms.DockStyle.Bottom; + this.commandArea.Location = new System.Drawing.Point(0, 369); + this.commandArea.Name = "commandArea"; + this.commandArea.RowCount = 3; + this.commandArea.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 10F)); + this.commandArea.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.commandArea.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 10F)); + this.commandArea.Size = new System.Drawing.Size(609, 45); + this.commandArea.TabIndex = 3; + // + // commandAreaButtonFlowLayout + // + this.commandAreaButtonFlowLayout.AutoSize = true; + this.commandAreaButtonFlowLayout.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.commandAreaButtonFlowLayout.BackColor = System.Drawing.Color.Transparent; + this.commandAreaButtonFlowLayout.Controls.Add(this.cancelButton); + this.commandAreaButtonFlowLayout.Controls.Add(this.nextButton); + this.commandAreaButtonFlowLayout.Controls.Add(this.backButton); + this.commandAreaButtonFlowLayout.Dock = System.Windows.Forms.DockStyle.Fill; + this.commandAreaButtonFlowLayout.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft; + this.commandAreaButtonFlowLayout.Location = new System.Drawing.Point(0, 10); + this.commandAreaButtonFlowLayout.Margin = new System.Windows.Forms.Padding(0); + this.commandAreaButtonFlowLayout.MinimumSize = new System.Drawing.Size(0, 23); + this.commandAreaButtonFlowLayout.Name = "commandAreaButtonFlowLayout"; + this.commandAreaButtonFlowLayout.Size = new System.Drawing.Size(590, 25); + this.commandAreaButtonFlowLayout.TabIndex = 2; + this.commandAreaButtonFlowLayout.WrapContents = false; + // + // titleBar + // + this.titleBar.AutoSize = true; + this.titleBar.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.titleBar.BackColor = System.Drawing.SystemColors.ActiveCaption; + this.titleBar.ColumnCount = 3; + this.titleBar.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 32F)); + this.titleBar.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 23F)); + this.titleBar.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.titleBar.Controls.Add(this.title, 2, 0); + this.titleBar.Controls.Add(this.titleImage, 1, 0); + this.titleBar.Dock = System.Windows.Forms.DockStyle.Top; + this.titleBar.Location = new System.Drawing.Point(0, 0); + this.titleBar.Name = "titleBar"; + this.titleBar.RowCount = 1; + this.titleBar.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.titleBar.Size = new System.Drawing.Size(609, 32); + this.titleBar.SupportGlass = true; + this.titleBar.TabIndex = 0; + this.titleBar.WatchFocus = true; + this.titleBar.MouseDown += new System.Windows.Forms.MouseEventHandler(this.TitleBar_MouseDown); + this.titleBar.MouseMove += new System.Windows.Forms.MouseEventHandler(this.TitleBar_MouseMove); + this.titleBar.MouseUp += new System.Windows.Forms.MouseEventHandler(this.TitleBar_MouseUp); + // + // title + // + this.title.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.title.AutoSize = true; + this.title.ForeColor = System.Drawing.SystemColors.ActiveCaptionText; + this.title.Location = new System.Drawing.Point(55, 6); + this.title.Margin = new System.Windows.Forms.Padding(0); + this.title.Name = "title"; + this.title.Padding = new System.Windows.Forms.Padding(0, 2, 0, 2); + this.title.Size = new System.Drawing.Size(79, 19); + this.title.TabIndex = 2; + this.title.Text = "Wizard Title"; + this.title.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // titleImage + // + this.titleImage.Anchor = System.Windows.Forms.AnchorStyles.Left; + this.titleImage.AutoSize = true; + this.titleImage.ImageAlign = System.Drawing.ContentAlignment.TopLeft; + this.titleImage.ImageList = this.titleImageList; + this.titleImage.Location = new System.Drawing.Point(32, 8); + this.titleImage.Margin = new System.Windows.Forms.Padding(0, 0, 7, 0); + this.titleImage.MinimumSize = new System.Drawing.Size(16, 16); + this.titleImage.Name = "titleImage"; + this.titleImage.Size = new System.Drawing.Size(16, 16); + this.titleImage.TabIndex = 1; + // + // WizardControl + // + this.Controls.Add(this.bodyPanel); + this.Controls.Add(this.commandAreaBorder); + this.Controls.Add(this.commandArea); + this.Controls.Add(this.titleBar); + this.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.Name = "WizardControl"; + this.Size = new System.Drawing.Size(609, 414); + this.bodyPanel.ResumeLayout(false); + this.bodyPanel.PerformLayout(); + this.contentArea.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.pageContainer)).EndInit(); + this.header.ResumeLayout(false); + this.header.PerformLayout(); + this.commandArea.ResumeLayout(false); + this.commandArea.PerformLayout(); + this.commandAreaButtonFlowLayout.ResumeLayout(false); + this.commandAreaButtonFlowLayout.PerformLayout(); + this.titleBar.ResumeLayout(false); + this.titleBar.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private AeroWizard.ThemedTableLayoutPanel titleBar; + private AeroWizard.ThemedTableLayoutPanel header; + internal System.Windows.Forms.Label headerLabel; + private AeroWizard.ThemedTableLayoutPanel commandArea; + private System.Windows.Forms.Button cancelButton; + internal System.Windows.Forms.Button nextButton; + private AeroWizard.ThemedLabel title; + private AeroWizard.ThemedLabel titleImage; + // internal AeroWizard.ThemedImageButton backButton; + internal System.Windows.Forms.Button backButton; + private System.Windows.Forms.Panel commandAreaBorder; + private System.Windows.Forms.ImageList titleImageList; + private System.Windows.Forms.Panel bodyPanel; + private ThemedTableLayoutPanel contentArea; + internal AeroWizard.WizardPageContainer pageContainer; + private System.Windows.Forms.FlowLayoutPanel commandAreaButtonFlowLayout; + + } +} diff --git a/AeroWizard/AeroWizard/WizardControl.resx b/AeroWizard/AeroWizard/WizardControl.resx new file mode 100644 index 0000000..c890ce8 --- /dev/null +++ b/AeroWizard/AeroWizard/WizardControl.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/AeroWizard/AeroWizard/WizardControlDesigner.cs b/AeroWizard/AeroWizard/WizardControlDesigner.cs new file mode 100644 index 0000000..841db0e --- /dev/null +++ b/AeroWizard/AeroWizard/WizardControlDesigner.cs @@ -0,0 +1,367 @@ +using System; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Drawing; +using System.Drawing.Design; +using System.Windows.Forms; +using System.Windows.Forms.Design; + +namespace AeroWizard.Design +{ + [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")] + [EditorBrowsable(EditorBrowsableState.Never)] + internal class WizardControlDesigner : RichParentControlDesigner, IToolboxUser + { + private static string[] propsToRemove = new string[] { "Anchor", "AutoScrollOffset", "AutoSize", "BackColor", + "BackgroundImage", "BackgroundImageLayout", "ContextMenuStrip", "Cursor", "Dock", "Enabled", "Font", + "ForeColor", /*"Location",*/ "Margin", "MaximumSize", "MinimumSize", "Padding", /*"Size",*/ "TabStop", + "Text", "UseWaitCursor" }; + + private bool forwardOnDrag; + + public override System.Collections.ICollection AssociatedComponents => Control?.Pages ?? base.AssociatedComponents; + + public override SelectionRules SelectionRules => SelectionRules.Visible | SelectionRules.Locked; + + protected IDesignerHost DesignerHost => GetService(); + + protected override bool EnableDragRect => false; + + public override bool CanBeParentedTo(IDesigner parentDesigner) => parentDesigner?.Component is Form; + + public override bool CanParent(Control control) => control is WizardPage && !Control.Contains(control); + + public bool GetToolSupported(ToolboxItem tool) => tool.TypeName != typeof(AeroWizard.WizardControl).FullName && Control?.SelectedPage != null; + + public override void Initialize(IComponent component) + { + base.Initialize(component); + AutoResizeHandles = true; + var wc = Control; + if (wc == null) return; + wc.SelectedPageChanged += WizardControl_SelectedPageChanged; + //wc.GotFocus += WizardControl_OnGotFocus; + wc.ControlAdded += WizardControl_OnControlAdded; + } + + public override void InitializeNewComponent(System.Collections.IDictionary defaultValues) + { + base.InitializeNewComponent(defaultValues); + Control.Text = Properties.Resources.WizardTitle; + } + + public void ToolPicked(ToolboxItem tool) + { + if (tool.TypeName == "AeroWizard.WizardPage") + InsertPageIntoWizard(true); + if (Control?.SelectedPage != null) + AddControlToActivePage(tool.TypeName); + } + + internal void InsertPageIntoWizard(bool add) + { + var h = DesignerHost; + var wiz = Control; + DesignerTransaction dt = null; + try + { + dt = h.CreateTransaction("Insert Wizard Page"); + var page = (WizardPage)h.CreateComponent(typeof(WizardPage)); + MemberDescriptor member = TypeDescriptor.GetProperties(wiz)["Pages"]; + RaiseComponentChanging(member); + + //Add a new page to the collection + if (wiz.Pages.Count == 0 || add) + wiz.Pages.Add(page); + else + wiz.Pages.Insert(wiz.SelectedPageIndex, page); + + RaiseComponentChanged(member, null, null); + } + finally + { + dt?.Commit(); + } + RefreshDesigner(); + } + + internal void RefreshDesigner() + { + var das = GetService(); + das?.Refresh(Control); + } + + internal void RemovePageFromWizard(WizardPage page) + { + var h = DesignerHost; + var c = ComponentChangeService; + if (h == null || c == null) + throw new ArgumentException("Both IDesignerHost and IComponentChangeService arguments cannot be null."); + + if (Control == null || !Control.Pages.Contains(page)) + return; + + DesignerTransaction dt = null; + try + { + dt = h.CreateTransaction("Remove Wizard Page"); + + MemberDescriptor member = TypeDescriptor.GetProperties(Control)["Pages"]; + RaiseComponentChanging(member); + + if (page.Owner != null) + { + //c.OnComponentChanging(page.Owner, null); + page.Owner.Pages.Remove(page); + //c.OnComponentChanged(page.Owner, null, null, null); + h.DestroyComponent(page); + } + else + { + //c.OnComponentChanging(page, null); + page.Dispose(); + //c.OnComponentChanged(page, null, null, null); + } + RaiseComponentChanged(member, null, null); + } + finally + { + dt?.Commit(); + } + RefreshDesigner(); + } + + protected override IComponent[] CreateToolCore(ToolboxItem tool, int x, int y, int width, int height, bool hasLocation, bool hasSize) + { + var pageDes = GetSelectedWizardPageDesigner(); + if (pageDes != null) + InvokeCreateTool(pageDes, tool); + return null; + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + Control.SelectedPageChanged -= WizardControl_SelectedPageChanged; + var ss = SelectionService; + if (ss != null) + ss.SelectionChanged -= OnSelectionChanged; + } + + base.Dispose(disposing); + } + + protected override bool GetHitTest(Point point) + { + if (Control.nextButton.ClientRectangle.Contains(Control.nextButton.PointToClient(point))) + return true; + return Control.backButton.ClientRectangle.Contains(Control.backButton.PointToClient(point)); + } + + protected override void OnDragDrop(DragEventArgs de) + { + if (forwardOnDrag) + { + var wizPageDesigner = GetSelectedWizardPageDesigner(); + wizPageDesigner?.OnDragDropInternal(de); + } + else + { + base.OnDragDrop(de); + } + forwardOnDrag = false; + } + + protected override void OnDragEnter(DragEventArgs de) + { + forwardOnDrag = true; + var wizPageDesigner = GetSelectedWizardPageDesigner(); + wizPageDesigner?.OnDragEnterInternal(de); + } + + protected override void OnDragLeave(EventArgs e) + { + if (forwardOnDrag) + { + var wizPageDesigner = GetSelectedWizardPageDesigner(); + wizPageDesigner?.OnDragLeaveInternal(e); + } + else + { + base.OnDragLeave(e); + } + forwardOnDrag = false; + } + + protected override void OnDragOver(DragEventArgs de) + { + if (forwardOnDrag) + { + var control = Control; + var pt = control.PointToClient(new Point(de.X, de.Y)); + if (!control.DisplayRectangle.Contains(pt)) + { + de.Effect = DragDropEffects.None; + } + else + { + var wizPageDesigner = GetSelectedWizardPageDesigner(); + wizPageDesigner?.OnDragOverInternal(de); + } + } + else + { + base.OnDragOver(de); + } + } + + protected override void OnGiveFeedback(GiveFeedbackEventArgs e) + { + if (forwardOnDrag) + { + var wizPageDesigner = GetSelectedWizardPageDesigner(); + wizPageDesigner?.OnGiveFeedbackInternal(e); + } + else + { + base.OnGiveFeedback(e); + } + } + + private void AddControlToActivePage(string typeName) + { + var dt = DesignerHost?.CreateTransaction("Add Control"); + var comp = DesignerHost?.CreateComponent(Type.GetType(typeName, false)); + if (comp != null) + { + var c = GetService(); + c.OnComponentChanging(Control.SelectedPage, null); + Control.SelectedPage?.Container?.Add(comp); + c.OnComponentChanged(Control.SelectedPage, null, null, null); + } + dt?.Commit(); + } + + private void CheckStatus() + { + Verbs[1].Enabled = Control != null && Control.Pages.Count > 0; + Verbs[2].Enabled = Control?.SelectedPage != null; + } + + private WizardPageDesigner GetSelectedWizardPageDesigner() + { + if (Control.SelectedPage == null) return null; + return DesignerHost?.GetDesigner(Control.SelectedPage) as WizardPageDesigner; + } + + [DesignerVerb("Add Page")] + private void HandleAddPage(object sender, EventArgs e) + { + InsertPageIntoWizard(true); + OnSelectionChanged(sender, e); + } + + [DesignerVerb("Insert Page")] + private void HandleInsertPage(object sender, EventArgs e) + { + InsertPageIntoWizard(false); + } + + [DesignerVerb("Remove Page")] + private void HandleRemovePage(object sender, EventArgs e) + { + if (Control?.SelectedPage == null) return; + RemovePageFromWizard(Control.SelectedPage); + OnSelectionChanged(sender, e); + } + + protected override void OnComponentChanged(object sender, ComponentChangedEventArgs e) + { + CheckStatus(); + } + + protected override void OnSelectionChanged(object sender, EventArgs e) + { + if (!(SelectionService.PrimarySelection is WizardControl)) + { + var p = SelectionService.PrimarySelection as WizardPage; + if (p == null && SelectionService.PrimarySelection is Control) + p = ((Control)SelectionService.PrimarySelection).GetParent(); + if (p != null && Control.SelectedPage != p) + { + Control.SelectedPage = p; + } + } + + RefreshDesigner(); + } + + private void SelectComponent(Component p) + { + if (SelectionService == null) return; + SelectionService.SetSelectedComponents(new object[] { Control }, SelectionTypes.Primary); + if (p?.Site != null) + SelectionService.SetSelectedComponents(new object[] { p }); + RefreshDesigner(); + } + + /*private void WizardControl_OnGotFocus(object sender, EventArgs e) + { + IEventHandlerService service = (IEventHandlerService)this.GetService(typeof(IEventHandlerService)); + if (service != null) + { + Control focusWindow = service.FocusWindow; + if (focusWindow != null) + { + focusWindow.Focus(); + } + } + }*/ + + private void WizardControl_OnControlAdded(object sender, ControlEventArgs e) + { + /*if ((e.Control != null) && !e.Control.IsHandleCreated) + { + var handle = e.Control.Handle; + }*/ + } + + private void WizardControl_SelectedPageChanged(object sender, EventArgs e) + { + SelectComponent(Control.SelectedPage); + } + } + + internal class WizardControlDesignerActionList : RichDesignerActionList + { + public WizardControlDesignerActionList(WizardControlDesigner wizDesigner, WizardControl control) + : base(wizDesigner, control) + { + } + + [DesignerActionProperty("Go to page", 4, Condition = "HasPages")] + public WizardPage GoToPage + { + get { return Component.SelectedPage; } + set { if (value != null) Component.SelectedPage = value; } + } + + [DesignerActionProperty("Edit pages...")] + public WizardPageCollection Pages => Component?.Pages; + + private bool HasPages => Pages != null && Pages.Count > 0; + + [DesignerActionMethod("Add page", 1)] + private void AddPage() + { + ParentDesigner.InsertPageIntoWizard(true); + } + + [DesignerActionMethod("Insert page", 2, Condition = "HasPages")] + private void InsertPage() + { + ParentDesigner.InsertPageIntoWizard(false); + } + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/WizardPage.cs b/AeroWizard/AeroWizard/WizardPage.cs new file mode 100644 index 0000000..80cd34d --- /dev/null +++ b/AeroWizard/AeroWizard/WizardPage.cs @@ -0,0 +1,368 @@ +using System; +using System.ComponentModel; +using System.Windows.Forms; +using AeroWizard.Properties; + +namespace AeroWizard +{ + /// + /// Represents a single page in a . + /// + [Designer(typeof(Design.WizardPageDesigner)), DesignTimeVisible(true)] + [DefaultProperty("Text"), DefaultEvent("Commit")] + [ToolboxItem(false)] + public partial class WizardPage : Control + { + private bool allowCancel = true, allowNext = true, allowBack = true; + private bool showCancel = true, showNext = true, suppress; + private bool isFinishPage; + private string helpText; + + private LinkLabel helpLink; + + /// + /// Initializes a new instance of the class. + /// + public WizardPage() + { + InitializeComponent(); + Margin = Padding.Empty; + base.Text = Properties.Resources.WizardHeader; + } + + /// + /// Occurs when the user has clicked the Next/Finish button but before the page is changed. + /// + [Category("Wizard"), Description("Occurs when the user has clicked the Next/Finish button but before the page is changed")] + public event EventHandler Commit; + + /// + /// Occurs when is set and the user has clicked the link at bottom of the content area. + /// + [Category("Wizard"), Description("Occurs when the user has clicked the help link")] + public event EventHandler HelpClicked; + + /// + /// Occurs when this page is entered. + /// + [Category("Wizard"), Description("Occurs when this page is entered")] + public event EventHandler Initialize; + + /// + /// Occurs when the user has clicked the Back button but before the page is changed. + /// + [Category("Wizard"), Description("Occurs when the user has clicked the Back button")] + public event EventHandler Rollback; + + /// + /// Gets or sets a value indicating whether to enable the Back button. + /// + /// true if Back button is enabled; otherwise, false. + [DefaultValue(true), Category("Behavior"), Description("Indicates whether to enable the Back button")] + public virtual bool AllowBack + { + get { return allowBack; } + set + { + if (allowBack == value) return; + allowBack = value; + UpdateOwner(); + } + } + + /// + /// Gets or sets a value indicating whether to enable the Cancel button. + /// + /// true if Cancel button is enabled; otherwise, false. + [DefaultValue(true), Category("Behavior"), Description("Indicates whether to enable the Cancel button")] + public virtual bool AllowCancel + { + get { return allowCancel; } + set + { + if (allowCancel == value) return; + allowCancel = value; + UpdateOwner(); + } + } + + /// + /// Gets or sets a value indicating whether to enable the Next/Finish button. + /// + /// true if Next/Finish button is enabled; otherwise, false. + [DefaultValue(true), Category("Behavior"), Description("Indicates whether to enable the Next/Finish button")] + public virtual bool AllowNext + { + get { return allowNext; } + set + { + if (allowNext == value) return; + allowNext = value; + UpdateOwner(); + } + } + + /// + /// Gets or sets the help text. When value is not null, a help link will be displayed at the bottom left of the content area. When clicked, the method will call the event. + /// + /// + /// The help text to display. + /// + [DefaultValue(null), Category("Appearance"), Description("Help text to display on hyperlink at bottom left of content area.")] + public string HelpText + { + get { return helpText; } + set + { + if (helpLink == null) + { + helpLink = new LinkLabel() { AutoSize = true, Dock = DockStyle.Bottom, Text = Resources.WizardPageDefaultHelpText, Visible = false }; + helpLink.LinkClicked += helpLink_LinkClicked; + Controls.Add(helpLink); + } + helpText = value; + if (helpText == null) + { + helpLink.Visible = false; + } + else + { + helpLink.Text = helpText; + helpLink.Visible = true; + } + } + } + + /// + /// Gets or sets a value indicating whether this page is the last page in the sequence and should display the Finish text instead of the Next text on the Next/Finish button. + /// + /// true if this page is a finish page; otherwise, false. + [DefaultValue(false), Category("Behavior"), Description("Indicates whether this page is the last page")] + public virtual bool IsFinishPage + { + get { return isFinishPage; } + set + { + if (isFinishPage == value) return; + isFinishPage = value; + UpdateOwner(); + } + } + + /// + /// Gets or sets the next page that should be used when the user clicks the Next button or when the method is called. This is used to override the default behavior of going to the next page in the sequence defined within the collection. + /// + /// The wizard page to go to. + [DefaultValue(null), Category("Behavior"), + Description("Specify a page other than the next page in the Pages collection as the next page.")] + public virtual WizardPage NextPage { get; set; } + + /// + /// Gets the for this page. + /// + /// The for this page. + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public virtual WizardPageContainer Owner { get; internal set; } + + /// + /// Gets or sets a value indicating whether to show the Cancel button. If both and are false, then the bottom command area will not be shown. + /// + /// true if Cancel button should be shown; otherwise, false. + [DefaultValue(true), Category("Behavior"), Description("Indicates whether to show the Cancel button")] + public virtual bool ShowCancel + { + get { return showCancel; } + set + { + if (showCancel == value) return; + showCancel = value; + UpdateOwner(); + } + } + + /// + /// Gets or sets a value indicating whether to show the Next/Finish button. If both and are false, then the bottom command area will not be shown. + /// + /// true if Next/Finish button should be shown; otherwise, false. + [DefaultValue(true), Category("Behavior"), Description("Indicates whether to show the Next/Finish button")] + public virtual bool ShowNext + { + get { return showNext; } + set + { + if (showNext == value) return; + showNext = value; + UpdateOwner(); + } + } + + /// + /// Gets or sets the height and width of the control. + /// + /// + /// + /// The that represents the height and width of the control in pixels. + /// + [Browsable(false)] + public new System.Drawing.Size Size { get { return base.Size; } set { base.Size = value; } } + + /// + /// Gets or sets a value indicating whether this is suppressed and not shown in the normal flow. + /// + /// + /// true if suppressed; otherwise, false. + /// + [DefaultValue(false), Category("Behavior"), Description("Suppresses this page from viewing if selected as next.")] + public virtual bool Suppress + { + get { return suppress; } + set + { + if (suppress == value) return; + suppress = value; + UpdateOwner(); + } + } + + /// + /// Gets the required creation parameters when the control handle is created. + /// + /// A that contains the required creation parameters when the handle to the control is created. + protected override CreateParams CreateParams + { + get + { + var createParams = base.CreateParams; + var parent = FindForm(); + var parentRightToLeftLayout = parent != null && parent.RightToLeftLayout; + if ((RightToLeft == RightToLeft.Yes) && parentRightToLeftLayout) + { + createParams.ExStyle |= 0x500000; // WS_EX_LAYOUTRTL | WS_EX_NOINHERITLAYOUT + createParams.ExStyle &= ~0x7000; // WS_EX_RIGHT | WS_EX_RTLREADING | WS_EX_LEFTSCROLLBAR + } + return createParams; + } + } + + /// + /// Returns a that represents this wizard page. + /// + /// + /// A that represents this wizard page. + /// + public override string ToString() => $"{Name} (\"{Text}\")"; + + internal bool CommitPage() => OnCommit(); + + internal void InitializePage(WizardPage prevPage) + { + OnInitialize(prevPage); + } + + internal bool RollbackPage() => OnRollback(); + + /// + /// Raises the event. + /// + /// true if handler does not set the to true; otherwise, false. + protected virtual bool OnCommit() + { + var e = new WizardPageConfirmEventArgs(this); + Commit?.Invoke(this,e); + return !e.Cancel; + } + + /// + /// Raises the event. + /// + /// An that contains the event data. + protected override void OnGotFocus(EventArgs e) + { + base.OnGotFocus(e); + var firstChild = GetNextControl(this, true); + firstChild?.Focus(); + } + + /// + /// Raises the event. + /// + protected virtual void OnHelpClicked() + { + HelpClicked?.Invoke(this, EventArgs.Empty); + } + + /// + /// Raises the event. + /// + /// The page that was previously selected. + protected virtual void OnInitialize(WizardPage prevPage) + { + Initialize?.Invoke(this, new WizardPageInitEventArgs(this, prevPage)); + } + + /// + /// Raises the event. + /// + /// true if handler does not set the to true; otherwise, false. + protected virtual bool OnRollback() + { + var e = new WizardPageConfirmEventArgs(this); + Rollback?.Invoke(this, e); + return !e.Cancel; + } + + private void helpLink_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + OnHelpClicked(); + } + + private void UpdateOwner() + { + if (Owner != null && this == Owner.SelectedPage) + Owner.UpdateUIDependencies(); + } + } + + /// + /// Arguments supplied to the events. + /// + public class WizardPageConfirmEventArgs : EventArgs + { + internal WizardPageConfirmEventArgs(WizardPage page) + { + Cancel = false; + Page = page; + } + + /// + /// Gets or sets a value indicating whether this action is to be canceled or allowed. + /// + /// true if cancel; otherwise, false to allow. Default is false. + [DefaultValue(false)] + public bool Cancel { get; set; } + + /// + /// Gets the that has raised the event. + /// + /// The wizard page. + public WizardPage Page { get; } + } + + /// + /// Arguments supplied to the event. + /// + public class WizardPageInitEventArgs : WizardPageConfirmEventArgs + { + internal WizardPageInitEventArgs(WizardPage page, WizardPage prevPage) + : base(page) + { + PreviousPage = prevPage; + } + + /// + /// Gets the that was previously selected when the event was raised. + /// + /// The previous wizard page. + public WizardPage PreviousPage { get; } + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/WizardPage.designer.cs b/AeroWizard/AeroWizard/WizardPage.designer.cs new file mode 100644 index 0000000..50905e6 --- /dev/null +++ b/AeroWizard/AeroWizard/WizardPage.designer.cs @@ -0,0 +1,36 @@ +namespace AeroWizard +{ + partial class WizardPage + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + components = new System.ComponentModel.Container(); + } + + #endregion + } +} diff --git a/AeroWizard/AeroWizard/WizardPageCollection.cs b/AeroWizard/AeroWizard/WizardPageCollection.cs new file mode 100644 index 0000000..935183a --- /dev/null +++ b/AeroWizard/AeroWizard/WizardPageCollection.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; + +namespace AeroWizard +{ + /// + /// A collection of controls. + /// + public class WizardPageCollection : EventedList + { + /// + /// Initializes a new instance of the class. + /// + /// The that this collection belongs to. + internal WizardPageCollection(WizardPageContainer owner) + { + Owner = owner; + } + + /// + /// Gets the to which this collection belongs. + /// + /// The . + public WizardPageContainer Owner { get; } + } +} diff --git a/AeroWizard/AeroWizard/WizardPageContainer.cs b/AeroWizard/AeroWizard/WizardPageContainer.cs new file mode 100644 index 0000000..4f7abf1 --- /dev/null +++ b/AeroWizard/AeroWizard/WizardPageContainer.cs @@ -0,0 +1,755 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Windows.Forms; +using Vanara.Interop; + +namespace AeroWizard +{ + /// + /// Button state for buttons controlling the wizard. + /// + public enum WizardCommandButtonState + { + /// Button is enabled and can be clicked. + Enabled, + /// Button is disabled and cannot be clicked. + Disabled, + /// Button is hidden from the user. + Hidden + } + + /// + /// Control providing a collection of wizard style navigable pages. + /// + [Designer(typeof(Design.WizardBaseDesigner))] + [ToolboxItem(true), ToolboxBitmap(typeof(WizardControl), "WizardControl.bmp")] + [Description("Provides a container for wizard pages.")] + [DefaultProperty("Pages"), DefaultEvent("SelectedPageChanged")] + public class WizardPageContainer : ContainerControl, ISupportInitialize + { + private string finishBtnText; + private bool initialized; + private bool initializing; + private bool nextButtonShieldEnabled; + private string nextBtnText; + private readonly Stack pageHistory; + private Timer progressTimer; + private WizardPage selectedPage; + private bool showProgressInTaskbarIcon; + private NativeMethods.ITaskbarList4 taskbar; + private ButtonBase backButton, cancelButton, nextButton; + + /// + /// Initializes a new instance of the class. + /// + public WizardPageContainer() + { + pageHistory = new Stack(); + + Pages = new WizardPageCollection(this); + Pages.ItemAdded += Pages_AddItem; + Pages.ItemDeleted += Pages_RemoveItem; + Pages.Reset += Pages_Reset; + + OnRightToLeftChanged(EventArgs.Empty); + + // Get localized defaults for button text + ResetBackButtonText(); + ResetCancelButtonText(); + ResetFinishButtonText(); + ResetNextButtonText(); + } + + /// + /// Occurs when the button's state has changed. + /// + [Category("Property Changed"), Description("Occurs when any of the button's state has changed.")] + public event EventHandler ButtonStateChanged; + + /// + /// Occurs when the user clicks the Cancel button and allows for programmatic cancellation. + /// + [Category("Behavior"), Description("Occurs when the user clicks the Cancel button and allows for programmatic cancellation.")] + public event CancelEventHandler Cancelling; + + /// + /// Occurs when the user clicks the Next/Finish button and the page is set to or this is the last page in the collection. + /// + [Category("Behavior"), Description("Occurs when the user clicks the Next/Finish button on last page.")] + public event EventHandler Finished; + + /// + /// Occurs when the property has changed. + /// + [Category("Property Changed"), Description("Occurs when the SelectedPage property has changed.")] + public event EventHandler SelectedPageChanged; + + /// + /// Gets or sets the button assigned to control backing up through the pages. + /// + /// The back button control. + [Category("Wizard"), Description("Button used to command backward wizard flow.")] + public ButtonBase BackButton + { + get { return backButton; } + set + { + if (backButton != null) + backButton.Click -= backButton_Click; + if (value != null) + { + backButton = value; + backButton.Click += backButton_Click; + } + } + } + + /// + /// Gets or sets the state of the back button. + /// + /// The state of the back button. + [Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public WizardCommandButtonState BackButtonState + { + get { return GetCmdButtonState(BackButton); } + internal set { SetCmdButtonState(BackButton, value); } + } + + /// + /// Gets or sets the back button text. + /// + /// The cancel button text. + [Category("Wizard"), Localizable(true), Description("The back button text")] + public string BackButtonText + { + get { return GetCmdButtonText(BackButton); } + set { SetCmdButtonText(BackButton, value); } + } + + /// + /// Gets or sets the button assigned to canceling the page flow. + /// + /// The cancel button control. + [Category("Wizard"), Description("Button used to cancel wizard flow.")] + public ButtonBase CancelButton + { + get { return cancelButton; } + set + { + if (cancelButton != null) + cancelButton.Click -= cancelButton_Click; + if (value != null) + { + cancelButton = value; + cancelButton.Click += cancelButton_Click; + } + } + } + + /// + /// Gets the state of the cancel button. + /// + /// The state of the cancel button. + [Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public WizardCommandButtonState CancelButtonState + { + get { return GetCmdButtonState(CancelButton); } + internal set { SetCmdButtonState(CancelButton, value); } + } + + /// + /// Gets or sets the cancel button text. + /// + /// The cancel button text. + [Category("Wizard"), Localizable(true), Description("The cancel button text")] + public string CancelButtonText + { + get { return GetCmdButtonText(CancelButton); } + set { SetCmdButtonText(CancelButton, value); } + } + + /// + /// Gets or sets the finish button text. + /// + /// The finish button text. + [Category("Wizard"), Localizable(true), Description("The finish button text")] + public string FinishButtonText + { + get { return finishBtnText; } + set + { + finishBtnText = value; + if (selectedPage != null && selectedPage.IsFinishPage && !this.IsDesignMode()) + { + SetCmdButtonText(NextButton, value); + } + } + } + + /// + /// Gets or sets the button assigned to control moving forward through the pages. + /// + /// The next button control. + [Category("Wizard"), Description("Button used to command forward wizard flow.")] + public ButtonBase NextButton + { + get { return nextButton; } + set + { + if (nextButton != null) + nextButton.Click -= nextButton_Click; + if (value != null) + { + nextButton = value; + nextButton.Click += nextButton_Click; + } + } + } + + /// + /// Gets or sets the shield icon on the next button. + /// + /// true if Next button should display a shield; otherwise, false. + /// Setting a UAF shield on a button only works on Vista and later versions of Windows. + [DefaultValue(false), Category("Wizard"), Description("Show a shield icon on the next button")] + public Boolean NextButtonShieldEnabled + { + get { return nextButtonShieldEnabled; } + set + { + if (nextButtonShieldEnabled != value) + { + nextButtonShieldEnabled = value; + NextButton.SetElevationRequiredState(value); + } + } + } + + /// + /// Gets the state of the next button. + /// + /// The state of the next button. + [Browsable(false), + DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public WizardCommandButtonState NextButtonState + { + get { return GetCmdButtonState(NextButton); } + internal set { SetCmdButtonState(NextButton, value); } + } + + /// + /// Gets or sets the next button text. + /// + /// The next button text. + [Category("Wizard"), Localizable(true), Description("The next button text.")] + public string NextButtonText + { + get { return nextBtnText; } + set + { + nextBtnText = value; + if (!this.IsDesignMode() && (selectedPage == null || !selectedPage.IsFinishPage)) + { + SetCmdButtonText(NextButton, value); + } + } + } + + /// + /// Gets the collection of wizard pages in this wizard control. + /// + /// The that contains the objects in this . + [Category("Wizard"), Description("Collection of wizard pages.")] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] + public WizardPageCollection Pages { get; } + + /// + /// Gets how far the wizard has progressed, as a percentage. + /// + /// A value between 0 and 100. + [Browsable(false), Description("The percentage of the current page against all pages at run-time.")] + public virtual short PercentComplete + { + get + { + var pg = SelectedPage; + if (pg == null) + return 0; + if (IsLastPage(pg)) + return 100; + return Convert.ToInt16(Math.Ceiling(((double)Pages.IndexOf(SelectedPage) + 1) * 100f / Pages.Count)); + } + } + + /// + /// Gets the currently selected wizard page. + /// + /// The selected wizard page. null if no page is active. + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public virtual WizardPage SelectedPage + { + get + { + if (selectedPage == null || Pages.Count == 0) + return null; + return selectedPage; + } + internal set + { + if (value != null && !Pages.Contains(value)) + throw new ArgumentException("WizardPage is not in the Pages collection for the control."); + + System.Diagnostics.Debug.WriteLine($"SelectPage: New={(value == null ? "null" : value.Name)},Prev={(selectedPage == null ? "null" : selectedPage.Name)}"); + if (value != selectedPage) + { + var prev = selectedPage; + selectedPage?.Hide(); + selectedPage = value; + var idx = SelectedPageIndex; + if (!this.IsDesignMode()) + { + while (selectedPage != null && idx < Pages.Count - 1 && selectedPage.Suppress) + selectedPage = Pages[++idx]; + } + if (selectedPage != null) + { + //this.HeaderText = selectedPage.Text; + selectedPage.InitializePage(prev); + selectedPage.Dock = DockStyle.Fill; + selectedPage.PerformLayout(); + selectedPage.Show(); + selectedPage.BringToFront(); + selectedPage.Focus(); + } + UpdateUIDependencies(); + OnSelectedPageChanged(); + } + } + } + + /// + /// Gets or sets a value indicating whether to show progress in form's taskbar icon. + /// + /// + /// This will only work on Windows 7 or later and the parent form must be showing its icon in the taskbar. No exception is thrown on failure. + /// + /// + /// true to show progress in taskbar icon; otherwise, false. + /// + [Category("Wizard"), DefaultValue(false), Description("Indicates whether to show progress in form's taskbar icon")] + public bool ShowProgressInTaskbarIcon + { + get { return showProgressInTaskbarIcon; } + set + { + if (RunningOnWin7) + { + showProgressInTaskbarIcon = value; + UpdateTaskbarProgress(); + } + } + } + + /// + /// Gets a value indicating whether running on win7. + /// + /// true if [running on win7]; otherwise, false. + private static bool RunningOnWin7 => (Environment.OSVersion.Platform == PlatformID.Win32NT) && (Environment.OSVersion.Version.CompareTo(new Version(6, 1)) >= 0); + + /// + /// Gets the task bar interface for the current form. + /// + /// The task bar. + private NativeMethods.ITaskbarList4 TaskBar + { + get + { + if (taskbar == null) + { + taskbar = (NativeMethods.ITaskbarList4)new NativeMethods.CTaskbarList(); + taskbar.HrInit(); + if (ParentForm != null) taskbar.SetProgressState(ParentForm.Handle, NativeMethods.TBPF.NORMAL); + } + return taskbar; + } + } + + [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + internal bool DesignerSelected { get; set; } + + /// + /// Gets the index of the currently selected page. + /// + /// The index of the selected page. + internal int SelectedPageIndex + { + get + { + if (selectedPage == null) + return -1; + return Pages.IndexOf(selectedPage); + } + } + + /// + /// Signals the object that initialization is starting. + /// + public void BeginInit() + { + initializing = true; + } + + /// + /// Signals the object that initialization is complete. + /// + public void EndInit() + { + initializing = false; + } + + /// + /// Advances to the specified page. + /// + /// The wizard page to go to next. + /// if set to true skip event. + /// When specifying a value for nextPage, it must already be in the Pages collection. + public virtual void NextPage(WizardPage nextPage = null, bool skipCommit = false) + { + if (this.IsDesignMode()) + { + var idx = SelectedPageIndex; + if (idx < Pages.Count - 1) + SelectedPage = Pages[idx + 1]; + return; + } + + if (skipCommit || SelectedPage.CommitPage()) + { + if (nextPage != null) + { + if (!Pages.Contains(nextPage)) + throw new ArgumentException(@"When specifying a value for nextPage, it must already be in the Pages collection.", nameof(nextPage)); + pageHistory.Push(SelectedPage); + SelectedPage = nextPage; + } + else + { + var selNext = GetNextPage(SelectedPage); + + // Check for last page + if (SelectedPage.IsFinishPage || selNext == null) + { + OnFinished(); + return; + } + + // Set new SelectedPage value + pageHistory.Push(SelectedPage); + SelectedPage = selNext; + } + } + } + + /// + /// Returns to the previous page. + /// + public virtual void PreviousPage() + { + if (this.IsDesignMode()) + { + var idx = SelectedPageIndex; + if (idx > 0) + SelectedPage = Pages[idx - 1]; + return; + } + + if (SelectedPage.RollbackPage()) + SelectedPage = pageHistory.Pop(); + } + + /// + /// Restarts the wizard pages from the first page. + /// + public void RestartPages() + { + initialized = false; + InitialSetup(); + } + + /// + /// Raises the event. + /// + protected virtual void OnCancelling() + { + var arg = new CancelEventArgs(true); + Cancelling?.Invoke(this, arg); + } + + /// + /// Raises the event. + /// + protected virtual void OnFinished() + { + Finished?.Invoke(this, EventArgs.Empty); + } + + /// + /// Raises the event. + /// + /// An that contains the event data. + protected override void OnGotFocus(EventArgs e) + { + base.OnGotFocus(e); + selectedPage?.Focus(); + } + + /// + /// Raises the event. + /// + /// An that contains the event data. + protected override void OnHandleCreated(EventArgs e) + { + base.OnHandleCreated(e); + InitialSetup(); + } + + /// + /// Raises the event. + /// + protected void OnSelectedPageChanged() + { + SelectedPageChanged?.Invoke(this, EventArgs.Empty); + } + + /// + /// Updates the buttons and taskbar according to current sequence and history. + /// + protected internal void UpdateUIDependencies() + { + System.Diagnostics.Debug.WriteLine($"UpdBtn: hstCnt={pageHistory.Count},pgIdx={SelectedPageIndex}:{Pages.Count},isFin={selectedPage != null && selectedPage.IsFinishPage}"); + if (selectedPage == null) + { + CancelButtonState = this.IsDesignMode() ? WizardCommandButtonState.Disabled : WizardCommandButtonState.Enabled; + NextButtonState = BackButtonState = WizardCommandButtonState.Hidden; + } + else + { + if (this.IsDesignMode()) + { + CancelButtonState = WizardCommandButtonState.Disabled; + NextButtonState = SelectedPageIndex == Pages.Count - 1 ? WizardCommandButtonState.Disabled : WizardCommandButtonState.Enabled; + BackButtonState = SelectedPageIndex <= 0 ? WizardCommandButtonState.Disabled : WizardCommandButtonState.Enabled; + } + else + { + CancelButtonState = selectedPage.ShowCancel ? (selectedPage.AllowCancel && !this.IsDesignMode() ? WizardCommandButtonState.Enabled : WizardCommandButtonState.Disabled) : WizardCommandButtonState.Hidden; + NextButtonState = selectedPage.ShowNext ? (selectedPage.AllowNext ? WizardCommandButtonState.Enabled : WizardCommandButtonState.Disabled) : WizardCommandButtonState.Hidden; + if (selectedPage.IsFinishPage || IsLastPage(SelectedPage)) + SetCmdButtonText(NextButton, FinishButtonText); + else + SetCmdButtonText(NextButton, NextButtonText); + BackButtonState = pageHistory.Count == 0 || !selectedPage.AllowBack ? WizardCommandButtonState.Disabled : WizardCommandButtonState.Enabled; + + UpdateTaskbarProgress(); + } + } + if (Controls.ContainsKey("stepList")) + Controls["stepList"].Refresh(); + } + + private void backButton_Click(object sender, EventArgs e) + { + PreviousPage(); + } + + private void cancelButton_Click(object sender, EventArgs e) + { + OnCancelling(); + } + + internal WizardCommandButtonState GetCmdButtonState(ButtonBase btn) + { + if (btn?.Tag == null) + return WizardCommandButtonState.Hidden; + if (btn.Tag is WizardCommandButtonState) + return (WizardCommandButtonState)btn.Tag; + if (btn.Enabled) + return WizardCommandButtonState.Enabled; + if (!btn.Visible) + return WizardCommandButtonState.Hidden; + return WizardCommandButtonState.Disabled; + } + + private string GetCmdButtonText(ButtonBase btn) => btn == null ? string.Empty : btn.Text; + + private WizardPage GetNextPage(WizardPage page) + { + if (page == null || page.IsFinishPage) + return null; + + do + { + var pgIdx = Pages.IndexOf(page); + if (page.NextPage != null) + page = page.NextPage; + else if (pgIdx == Pages.Count - 1) + page = null; + else + page = Pages[pgIdx + 1]; + } while (page != null && page.Suppress); + + return page; + } + + private void InitialSetup() + { + if (!initialized) + { + pageHistory.Clear(); + selectedPage = null; + var firstPage = Pages.Find(p => !p.Suppress); + if (firstPage != null) SelectedPage = firstPage; + if (selectedPage == null) + UpdateUIDependencies(); + if (showProgressInTaskbarIcon) + { + progressTimer = new Timer() { Interval = 1000, Enabled = true }; + progressTimer.Tick += progressTimer_Tick; + } + initialized = true; + } + } + + private bool IsLastPage(WizardPage page) => GetNextPage(page) == null; + + private void nextButton_Click(object sender, EventArgs e) + { + NextPage(); + } + + private void Pages_AddItem(object sender, EventedList.ListChangedEventArgs e) + { + Pages_AddItemHandler(e.Item, !initializing); + } + + private void Pages_AddItemHandler(WizardPage item, bool selectAfterAdd) + { + System.Diagnostics.Debug.WriteLine($"AddPage: {(item == null ? "null" : item.Text)},sel={selectAfterAdd}"); + item.Owner = this; + item.Visible = false; + if (!Contains(item)) + Controls.Add(item); + if (selectAfterAdd) + SelectedPage = item; + } + + private void Pages_RemoveItem(object sender, EventedList.ListChangedEventArgs e) + { + Controls.Remove(e.Item); + if (e.Item == selectedPage && Pages.Count > 0) + SelectedPage = Pages[e.ItemIndex == Pages.Count ? e.ItemIndex - 1 : e.ItemIndex]; + else + UpdateUIDependencies(); + } + + private void Pages_Reset(object sender, EventedList.ListChangedEventArgs e) + { + var curPage = selectedPage; + SelectedPage = null; + Controls.Clear(); + foreach (var item in Pages) + Pages_AddItemHandler(item, false); + if (Pages.Count > 0) + SelectedPage = Pages.Contains(curPage) ? curPage : Pages[0]; + } + + private void progressTimer_Tick(object sender, EventArgs e) + { + if (ParentForm != null && ParentForm.Visible) + { + progressTimer.Enabled = false; + progressTimer = null; + UpdateTaskbarProgress(); + } + } + + private void UpdateTaskbarProgress() + { + if (showProgressInTaskbarIcon && selectedPage != null && Pages.Count > 0 && !this.IsDesignMode() && ParentForm != null && ParentForm.ShowInTaskbar) + TaskBar.SetProgressValue(ParentForm.Handle, Convert.ToUInt64(PercentComplete), 100ul); + } + + internal void ResetBackButtonText() + { + BackButtonText = Properties.Resources.WizardBackText; + } + + internal void ResetCancelButtonText() + { + CancelButtonText = Properties.Resources.WizardCancelText; + } + + internal void ResetFinishButtonText() + { + FinishButtonText = Properties.Resources.WizardFinishText; + } + + internal void ResetNextButtonText() + { + NextButtonText = Properties.Resources.WizardNextText; + } + + private void SetCmdButtonState(ButtonBase btn, WizardCommandButtonState value) + { + if (btn == null) + return; + + var prevVal = GetCmdButtonState(btn); + switch (value) + { + case WizardCommandButtonState.Disabled: + btn.Enabled = false; + btn.Visible = true; + break; + case WizardCommandButtonState.Hidden: + btn.Enabled = false; + if (btn != BackButton) + btn.Visible = false; + break; + case WizardCommandButtonState.Enabled: + btn.Enabled = true; + btn.Visible = true; + break; + default: + throw new ArgumentOutOfRangeException(nameof(value), value, null); + } + + if (prevVal != value) + { + btn.Tag = value; + ButtonStateChanged?.Invoke(btn, EventArgs.Empty); + } + + Invalidate(); + } + + private void SetCmdButtonText(ButtonBase btn, string text) + { + if (btn == null) return; + btn.Text = text; + Invalidate(); + } + + internal bool ShouldSerializeBackButtonText() => BackButtonText != Properties.Resources.WizardBackText; + + internal bool ShouldSerializeCancelButtonText() => CancelButtonText != Properties.Resources.WizardCancelText; + + internal bool ShouldSerializeFinishButtonText() => FinishButtonText != Properties.Resources.WizardFinishText; + + internal bool ShouldSerializeNextButtonText() => NextButtonText != Properties.Resources.WizardNextText; + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/WizardPageContainerDesigner.cs b/AeroWizard/AeroWizard/WizardPageContainerDesigner.cs new file mode 100644 index 0000000..e44108e --- /dev/null +++ b/AeroWizard/AeroWizard/WizardPageContainerDesigner.cs @@ -0,0 +1,383 @@ +using System; +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Drawing; +using System.Drawing.Design; +using System.Windows.Forms; +using System.Windows.Forms.Design; +using System.Windows.Forms.Design.Behavior; + +namespace AeroWizard.Design +{ + [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")] + [EditorBrowsable(EditorBrowsableState.Never)] + internal class WizardBaseDesigner : RichParentControlDesigner, IToolboxUser + { + private static readonly string[] propsToRemove = new string[] { "AutoScrollOffset", "AutoSize", "BackColor", + "BackgroundImage", "BackgroundImageLayout", "ContextMenuStrip", "Cursor", "Enabled", "Font", + "ForeColor", /*"Location",*/ "MaximumSize", "MinimumSize", "Padding", /*"Size",*/ "TabStop", + "Text", "UseWaitCursor" }; + + private bool forwardOnDrag; + + public override System.Collections.ICollection AssociatedComponents => Control.Pages; + + public override SelectionRules SelectionRules => SelectionRules.Visible | SelectionRules.AllSizeable | SelectionRules.Moveable; + + protected IDesignerHost DesignerHost => GetService(); + + protected override bool EnableDragRect => true; + + protected override System.Collections.Generic.IEnumerable PropertiesToRemove => propsToRemove; + + public override bool CanBeParentedTo(IDesigner parentDesigner) => parentDesigner?.Component is Control; + + public override bool CanParent(Control control) => control is WizardPage && !Control.Contains(control); + + public override void Initialize(IComponent component) + { + base.Initialize(component); + AutoResizeHandles = true; + //base.Glyphs.Add(new WizardPageContainerDesignerGlyph(this)); + if (Control == null) return; + Control.SelectedPageChanged += WizardPageContainer_SelectedPageChanged; + //this.Control.GotFocus += WizardPageContainer_OnGotFocus; + Control.ControlAdded += WizardPageContainer_OnControlAdded; + } + + public override void InitializeNewComponent(System.Collections.IDictionary defaultValues) + { + base.InitializeNewComponent(defaultValues); + Control.Text = Properties.Resources.WizardTitle; + } + + internal void InsertPageIntoWizard(bool add) + { + var h = DesignerHost; + var c = ComponentChangeService; + var wiz = Control; + if (h == null || c == null) + throw new ArgumentException("Both IDesignerHost and IComponentChangeService arguments cannot be null."); + + DesignerTransaction dt = null; + try + { + dt = h.CreateTransaction("Insert Wizard Page"); + var page = (WizardPage)h.CreateComponent(typeof(WizardPage)); + MemberDescriptor member = TypeDescriptor.GetProperties(wiz)["Pages"]; + RaiseComponentChanging(member); + + //Add a new page to the collection + if (wiz.Pages.Count == 0 || add) + wiz.Pages.Add(page); + else + wiz.Pages.Insert(wiz.SelectedPageIndex, page); + + RaiseComponentChanged(member, null, null); + } + finally + { + dt?.Commit(); + } + RefreshDesigner(); + } + + bool IToolboxUser.GetToolSupported(ToolboxItem tool) + { + if (tool.TypeName == typeof(WizardPage).FullName) + return false; + return Control?.SelectedPage != null; + } + + void IToolboxUser.ToolPicked(ToolboxItem tool) + { + if (tool.TypeName == typeof(WizardPage).FullName) + InsertPageIntoWizard(true); + if (Control?.SelectedPage != null) + AddControlToActivePage(tool.TypeName); + } + + internal void RefreshDesigner() + { + var das = GetService(); + das?.Refresh(Control); + } + + internal void RemovePageFromWizard(WizardPage page) + { + var h = DesignerHost; + var c = ComponentChangeService; + if (h == null || c == null) + throw new ArgumentException("Both IDesignerHost and IComponentChangeService arguments cannot be null."); + + if (Control == null || !Control.Pages.Contains(page)) + return; + + DesignerTransaction dt = null; + try + { + dt = h.CreateTransaction("Remove Wizard Page"); + + MemberDescriptor member = TypeDescriptor.GetProperties(Control)["Pages"]; + RaiseComponentChanging(member); + + if (page.Owner != null) + { + page.Owner.Pages.Remove(page); + h.DestroyComponent(page); + } + else + { + page.Dispose(); + } + RaiseComponentChanged(member, null, null); + } + finally + { + dt?.Commit(); + } + RefreshDesigner(); + } + + protected override IComponent[] CreateToolCore(ToolboxItem tool, int x, int y, int width, int height, bool hasLocation, bool hasSize) + { + var pageDes = GetSelectedWizardPageDesigner(); + if (pageDes != null) + InvokeCreateTool(pageDes, tool); + return null; + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (Control != null) + { + Control.SelectedPageChanged -= WizardPageContainer_SelectedPageChanged; + //this.Control.GotFocus -= WizardPageContainer_OnGotFocus; + Control.ControlAdded -= WizardPageContainer_OnControlAdded; + } + } + base.Dispose(disposing); + } + + protected override void OnComponentChanged(object sender, ComponentChangedEventArgs e) + { + CheckStatus(); + } + + protected override void OnDragDrop(DragEventArgs de) + { + if (forwardOnDrag) + { + var wizPageDesigner = GetSelectedWizardPageDesigner(); + wizPageDesigner?.OnDragDropInternal(de); + } + else + { + base.OnDragDrop(de); + } + forwardOnDrag = false; + } + + protected override void OnDragEnter(DragEventArgs de) + { + forwardOnDrag = true; + var wizPageDesigner = GetSelectedWizardPageDesigner(); + wizPageDesigner?.OnDragEnterInternal(de); + } + + protected override void OnDragLeave(EventArgs e) + { + if (forwardOnDrag) + { + var wizPageDesigner = GetSelectedWizardPageDesigner(); + wizPageDesigner?.OnDragLeaveInternal(e); + } + else + { + base.OnDragLeave(e); + } + forwardOnDrag = false; + } + + protected override void OnDragOver(DragEventArgs de) + { + if (forwardOnDrag) + { + var control = Control; + var pt = control.PointToClient(new Point(de.X, de.Y)); + if (!control.DisplayRectangle.Contains(pt)) + { + de.Effect = DragDropEffects.None; + } + else + { + var wizPageDesigner = GetSelectedWizardPageDesigner(); + wizPageDesigner?.OnDragOverInternal(de); + } + } + else + { + base.OnDragOver(de); + } + } + + protected override void OnGiveFeedback(GiveFeedbackEventArgs e) + { + if (forwardOnDrag) + { + var wizPageDesigner = GetSelectedWizardPageDesigner(); + wizPageDesigner?.OnGiveFeedbackInternal(e); + } + else + { + base.OnGiveFeedback(e); + } + } + + protected override void OnSelectionChanged(object sender, EventArgs e) + { + if (!(SelectionService.PrimarySelection is WizardPageContainer)) + { + var p = SelectionService.PrimarySelection as WizardPage; + if (p == null && SelectionService.PrimarySelection is Control) + p = ((Control)SelectionService.PrimarySelection).GetParent(); + if (p != null && Control.SelectedPage != p) + { + Control.SelectedPage = p; + } + } + + RefreshDesigner(); + } + + private void AddControlToActivePage(string typeName) + { + var c = GetService(); + + var dt = DesignerHost?.CreateTransaction("Add Control"); + var comp = DesignerHost?.CreateComponent(Type.GetType(typeName, false)); + if (comp != null) + { + c.OnComponentChanging(Control.SelectedPage, null); + Control.SelectedPage?.Container?.Add(comp); + c.OnComponentChanged(Control.SelectedPage, null, null, null); + } + dt?.Commit(); + } + + private void CheckStatus() + { + if (Verbs.Count < 3) return; + Verbs[1].Enabled = Control != null && Control.Pages.Count > 0; + Verbs[2].Enabled = Control?.SelectedPage != null; + } + + private WizardPageDesigner GetSelectedWizardPageDesigner() => Control.SelectedPage != null ? DesignerHost?.GetDesigner(Control.SelectedPage) as WizardPageDesigner : null; + + [DesignerVerb("Remove page")] + private void RemovePage(object sender, EventArgs e) + { + if (Control?.SelectedPage == null) return; + RemovePageFromWizard(Control.SelectedPage); + OnSelectionChanged(sender, e); + } + + private void SelectComponent(Component p) + { + if (SelectionService != null) + { + SelectionService.SetSelectedComponents(new object[] { Control }, SelectionTypes.Primary); + if (p?.Site != null) + SelectionService.SetSelectedComponents(new object[] { p }); + RefreshDesigner(); + } + } + + private void WizardPageContainer_OnControlAdded(object sender, ControlEventArgs e) + { + /*if ((e.Control != null) && !e.Control.IsHandleCreated) + { + var handle = e.Control.Handle; + }*/ + } + + private void WizardPageContainer_SelectedPageChanged(object sender, EventArgs e) + { + SelectComponent(Control.SelectedPage); + } + + private void WizFirstPage(object sender, EventArgs e) + { + if (Control != null && Control.Pages.Count > 0) + Control.SelectedPage = Control.Pages[0]; + } + + private void WizLastPage(object sender, EventArgs e) + { + if (Control != null && Control.Pages.Count > 0) + Control.SelectedPage = Control.Pages[Control.Pages.Count - 1]; + } + + private void WizNextPage(object sender, EventArgs e) + { + Control?.NextPage(); + } + + private void WizPrevPage(object sender, EventArgs e) + { + Control?.PreviousPage(); + } + + internal class ActionList : RichDesignerActionList + { + public ActionList(WizardBaseDesigner d, WizardPageContainer c) : base(d, c) + { + } + + [DesignerActionProperty("Go to page", 5)] + public WizardPage GoToPage + { + get { return Component.SelectedPage; } + set + { + if (value != null) + Component.SelectedPage = value; + } + } + + [DesignerActionProperty("Edit pages...")] + public WizardPageCollection Pages => Component?.Pages; + + internal bool HasPages => Pages != null && Pages.Count > 0; + + [DesignerActionMethod("Add page", 1, IncludeAsDesignerVerb = true)] + private void AddPage() + { + ParentDesigner.InsertPageIntoWizard(true); + ParentDesigner.OnSelectionChanged(this, EventArgs.Empty); + } + + [DesignerActionMethod("Insert page", 2, Condition = "HasPages", IncludeAsDesignerVerb = true)] + private void InsertPage() + { + ParentDesigner.InsertPageIntoWizard(false); + ParentDesigner.OnSelectionChanged(this, EventArgs.Empty); + } + + + [DesignerActionMethod("Next page", 3, Condition = "HasPages")] + private void NextPage() + { + Component.NextPage(); + } + + [DesignerActionMethod("Previous page", 4, Condition = "HasPages")] + private void PrevPage() + { + Component.PreviousPage(); + } + } + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/WizardPageDesigner.cs b/AeroWizard/AeroWizard/WizardPageDesigner.cs new file mode 100644 index 0000000..2763cf5 --- /dev/null +++ b/AeroWizard/AeroWizard/WizardPageDesigner.cs @@ -0,0 +1,266 @@ +using System; +using System.ComponentModel.Design; +using System.Drawing; +using System.Windows.Forms; +using System.Windows.Forms.Design; + +namespace AeroWizard.Design +{ + internal class WizardPageDesigner : RichParentControlDesigner + { + private static readonly string[] propsToRemove = new string[] { "Anchor", "AutoScrollOffset", "AutoSize", "BackColor", + "BackgroundImage", "BackgroundImageLayout", "ContextMenuStrip", "Cursor", "Dock", "Enabled", "Font", + "ForeColor", "Location", "Margin", "MaximumSize", "MinimumSize", "Padding", "TabStop", "UseWaitCursor", "Visible" }; + + public override SelectionRules SelectionRules => SelectionRules.Visible | SelectionRules.Locked; + + protected override bool EnableDragRect => false; + + public override bool CanBeParentedTo(IDesigner parentDesigner) => parentDesigner?.Component is WizardPageContainer; + + internal WizardBaseDesigner ContainerDesigner => GetService()?.GetDesigner(Control.Owner) as WizardBaseDesigner; + + public override void Initialize(System.ComponentModel.IComponent component) + { + base.Initialize(component); + //base.Glyphs.Add(new WizardPageDesignerGlyph(this)); + GetService()?.Remove(component); + } + + internal void OnDragDropInternal(DragEventArgs de) + { + OnDragDrop(de); + } + + internal void OnDragEnterInternal(DragEventArgs de) + { + OnDragEnter(de); + } + + internal void OnDragLeaveInternal(EventArgs e) + { + OnDragLeave(e); + } + + internal void OnDragOverInternal(DragEventArgs e) + { + OnDragOver(e); + } + + internal void OnGiveFeedbackInternal(GiveFeedbackEventArgs e) + { + OnGiveFeedback(e); + } + + protected override void OnPaintAdornments(PaintEventArgs pe) + { + var clientRectangle = Control.ClientRectangle; + clientRectangle.Width--; + clientRectangle.Height--; + ControlPaint.DrawFocusRectangle(pe.Graphics, clientRectangle); + base.OnPaintAdornments(pe); + } + + protected override System.Collections.Generic.IEnumerable PropertiesToRemove => propsToRemove; + + internal class WizardPageDesignerActionList : RichDesignerActionList + { + public WizardPageDesignerActionList(WizardPageDesigner pageDesigner, WizardPage page) + : base(pageDesigner, page) + { + } + + [DesignerActionProperty("Back Button Enabled", 0, Category = "Buttons", Description = "Enables the Back button when this page is shown.")] + public bool AllowBack + { + get { return Component.AllowBack; } + set { Component.AllowBack = value; } + } + + [DesignerActionProperty("Cancel Button Enabled", 1, Category = "Buttons", Description = "Enables the Cancel button when this page is shown.")] + public bool AllowCancel + { + get { return Component.AllowCancel; } + set { Component.AllowCancel = value; } + } + + [DesignerActionProperty("Next Button Enabled", 3, Category = "Buttons")] + public bool AllowNext + { + get { return Component.AllowNext; } + set { Component.AllowNext = value; } + } + + [DesignerActionProperty("Mark As Finish Page", 5, Category = "Behavior")] + public bool IsFinishPage + { + get { return Component.IsFinishPage; } + set { Component.IsFinishPage = value; } + } + + [DesignerActionProperty("Set Next Page", 6, Category = "Behavior")] + public WizardPage NextPage + { + get { return Component.NextPage; } + set { Component.NextPage = value; } + } + + [DesignerActionProperty("Cancel Button Visible", 2, Category = "Buttons")] + public bool ShowCancel + { + get { return Component.ShowCancel; } + set { Component.ShowCancel = value; } + } + + [DesignerActionProperty("Next Button Visible", 4, Category = "Behavior")] + public bool ShowNext + { + get { return Component.ShowNext; } + set { Component.ShowNext = value; } + } + } + } + + + internal class WizardPageDesignerBehavior : RichBehavior + { + public WizardPageDesignerBehavior(WizardPageDesigner designer) + : base(designer) + { + + } + + public override bool OnMouseDown(System.Windows.Forms.Design.Behavior.Glyph g, MouseButtons button, Point mouseLoc) + { + if (button == MouseButtons.Left) + { + switch (((WizardPageDesignerGlyph)g).LastHit) + { + case WizardPageDesignerGlyph.ClickState.FirstBtn: + Designer.Control.Owner.SelectedPage = Designer.Control.Owner.Pages[0]; + break; + case WizardPageDesignerGlyph.ClickState.PrevBtn: + Designer.Control.Owner.PreviousPage(); + break; + case WizardPageDesignerGlyph.ClickState.NextBtn: + Designer.Control.Owner.NextPage(); + break; + case WizardPageDesignerGlyph.ClickState.LastBtn: + Designer.Control.Owner.SelectedPage = Designer.Control.Owner.Pages[Designer.Control.Owner.Pages.Count - 1]; + break; + case WizardPageDesignerGlyph.ClickState.Control: + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + return base.OnMouseDown(g, button, mouseLoc); + } + } + + internal class WizardPageDesignerGlyph : RichGlyph + { + private const int btnCount = 4, btnSize = 16, navBoxWidth = btnSize*btnCount + (btnCount - 1)*2 + 4, navBoxHeight = btnSize + 4; + private Rectangle navBox; + + public WizardPageDesignerGlyph(WizardPageDesigner designer) : base(designer, new WizardPageDesignerBehavior(designer)) + { + Designer.SelectionService.SelectionChanged += selSvc_SelectionChanged; + Designer.Control.Move += control_Move; + Designer.Control.Resize += control_Move; + } + + internal enum ClickState + { + Control, + FirstBtn, + PrevBtn, + NextBtn, + LastBtn + } + + public override Rectangle Bounds => navBox; + + internal ClickState LastHit { get; set; } + + public override void Dispose() + { + Designer.SelectionService.SelectionChanged -= selSvc_SelectionChanged; + Designer.Control.Move -= control_Move; + Designer.Control.Resize -= control_Move; + base.Dispose(); + } + + public override Cursor GetHitTest(Point p) + { + var r1 = new Rectangle(navBox.X + 2, navBox.Y + 2, btnSize, btnSize); + for (var i = 0; i < btnCount; i++) + { + if (r1.Contains(p)) + { + LastHit = (ClickState)(i + 1); + return Cursors.Arrow; + } + r1.Offset(btnSize + 2, 0); + } + LastHit = ClickState.Control; + return null; + } + + public override void Paint(PaintEventArgs pe) + { + var isMin7 = Environment.OSVersion.Version >= new Version(6, 1); + var fn = isMin7 ? "Webdings" : "Arial Narrow"; + var btnText = isMin7 ? new[] {"9", "3", "4", ":"} : new[] {"«", "<", ">", "»"}; + using (var f = new Font(fn, btnSize - 2, isMin7 ? FontStyle.Regular : FontStyle.Bold, GraphicsUnit.Pixel)) + { + pe.Graphics.FillRectangle(SystemBrushes.Control, new Rectangle(navBox.X, navBox.Y, navBox.Width + 1, navBox.Height + 1)); + using (var pen = new Pen(SystemBrushes.ControlDark, 1f) {DashStyle = System.Drawing.Drawing2D.DashStyle.Dot}) + { + pe.Graphics.DrawRectangle(pen, navBox); + var r1 = new Rectangle(navBox.X + 2, navBox.Y + 2, btnSize, btnSize); + pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Solid; + var sf = new StringFormat() {Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center}; + for (var i = 0; i < btnCount; i++) + { + pe.Graphics.DrawRectangle(pen, r1); + r1.Offset(1, 1); + //TextRenderer.DrawText(pe.Graphics, btnText[i], f, r1, SystemColors.ControlDark, SystemColors.Window, TextFormatFlags.NoPadding | TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter); + pe.Graphics.DrawString(btnText[i], f, SystemBrushes.ControlDark, r1, sf); + r1.Offset(btnSize + 1, -1); + } + } + } + } + + private void control_Move(object sender, EventArgs e) + { + if (ReferenceEquals(Designer.SelectionService.PrimarySelection, Designer.Control)) + { + SetNavBoxes(); + Designer.Adorner.Invalidate(); + } + } + + private void selSvc_SelectionChanged(object sender, EventArgs e) + { + if (ReferenceEquals(Designer.SelectionService.PrimarySelection, Designer.Control)) + { + SetNavBoxes(); + Designer.Adorner.Enabled = true; + Designer.Control.Owner.DesignerSelected = true; + } + else if (Designer.Control.Owner.DesignerSelected) + { + Designer.Adorner.Enabled = false; + Designer.Control.Owner.DesignerSelected = false; + } + } + + private void SetNavBoxes() + { + var pt = Designer.BehaviorService.ControlToAdornerWindow(Designer.Control); + navBox = new Rectangle(pt.X + Designer.Control.Width - navBoxWidth - 17, pt.Y - navBoxHeight - 5, navBoxWidth, navBoxHeight); + } + } +} \ No newline at end of file diff --git a/AeroWizard/AeroWizard/packages.config b/AeroWizard/AeroWizard/packages.config new file mode 100644 index 0000000..0aee17e --- /dev/null +++ b/AeroWizard/AeroWizard/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/AirScout.AircraftPositions/AirScout.AircraftPositions.csproj b/AirScout.AircraftPositions/AirScout.AircraftPositions.csproj new file mode 100644 index 0000000..2a6bbfe --- /dev/null +++ b/AirScout.AircraftPositions/AirScout.AircraftPositions.csproj @@ -0,0 +1,100 @@ + + + + + Debug + AnyCPU + {BE467E28-C202-4D71-BB17-9086861AD75F} + Library + Properties + AirScout.AircraftPositions + AirScout.AircraftPositions + v4.0 + 512 + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Newtonsoft.Json.12.0.1\lib\net40\Newtonsoft.Json.dll + True + + + + + ..\packages\System.Data.SQLite.Core.1.0.110.0\lib\net40\System.Data.SQLite.dll + True + + + + + + + + + + + + + + + True + True + Settings.settings + + + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + {ee86e933-d883-4b18-80eb-0fba55ec67c6} + ScoutBase.Core + + + {610db007-5f74-4b5d-8b71-5e2c163a99b3} + ScoutBase.Propagation + + + {6056d3be-7002-4a6a-a9ea-6ff45122a3c7} + SQLiteDatabase + + + + + + + Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}". + + + + + \ No newline at end of file diff --git a/AirScout.AircraftPositions/AircraftPosition.cs b/AirScout.AircraftPositions/AircraftPosition.cs new file mode 100644 index 0000000..e2285ca --- /dev/null +++ b/AirScout.AircraftPositions/AircraftPosition.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections.Generic; +using System.Collections; +using System.Linq; +using System.Text; +using System.Threading; +using System.ComponentModel; +using System.Globalization; +using System.Net; +using System.IO; +using System.Data; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System.Data.SQLite; + +namespace AirScout.AircraftPositions +{ + + /// + /// Holds the aircraft information + /// + [System.ComponentModel.DesignerCategory("")] + public class AircraftPositionDesignator : SQLiteEntry + { + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // be sure to have a copy of these static members in each derived class !! + // update the tabale name to the table name according to the class + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + [JsonIgnore] + public static new readonly string TableName = "AircraftPositions"; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public string Hex { get; set; } + public string Call { get; set; } + public double Lat { get; set; } + public double Lon { get; set; } + public double Alt { get; set; } + public double Track { get; set; } + public double Speed { get; set; } + + public AircraftPositionDesignator() + { + Hex = ""; + Call = ""; + Lat = 0; + Lon = 0; + Alt = 0; + Track = 0; + Speed = 0; + LastUpdated = DateTime.MinValue; + } + + public AircraftPositionDesignator(AircraftPositionDesignator ap) + { + Hex = ap.Hex; + Call = ap.Call; + Lat = ap.Lat; + Lon = ap.Lon; + Alt = ap.Alt; + Track = ap.Track; + Speed = ap.Speed; + LastUpdated = ap.LastUpdated; + } + + public AircraftPositionDesignator(DataRow row) : this() + { + FillFromDataRow(row); + } + + public AircraftPositionDesignator(IDataRecord record) : this() + { + FillFromDataRecord(record); + } + + public AircraftPositionDesignator(string hex, DateTime lastupdated) : this(hex,"", 0,0,0,0,0, lastupdated) { } + public AircraftPositionDesignator(string hex, string call, double lat, double lon, double alt, double track, double speed, DateTime lastupdated) : this() + { + Hex = hex; + Call = call; + Lat = lat; + Lon = lon; + Alt = alt; + Track = track; + Speed = speed; + LastUpdated = lastupdated; + } + + } + + + + [System.ComponentModel.DesignerCategory("")] + public class DataTableAircraftPositions : DataTable + { + public DataTableAircraftPositions() + : base() + { + // set table name + TableName = "AircraftPositions"; + // create all specific columns + DataColumn Hex = this.Columns.Add("Hex", typeof(string)); + DataColumn Call = this.Columns.Add("Call", typeof(string)); + DataColumn Lat = this.Columns.Add("Lat", typeof(double)); + DataColumn Lon = this.Columns.Add("Lon", typeof(double)); + DataColumn Alt = this.Columns.Add("Alt", typeof(double)); + DataColumn Track = this.Columns.Add("Track", typeof(double)); + DataColumn Speed = this.Columns.Add("Speed", typeof(double)); + DataColumn LastUpdated = this.Columns.Add("LastUpdated", typeof(DateTime)); + // create primary key + DataColumn[] keys = new DataColumn[2]; + keys[0] = Hex; + keys[1] = LastUpdated; + this.PrimaryKey = keys; + } + + public DataTableAircraftPositions(List aps) + : this() + { + foreach (AircraftPositionDesignator ad in aps) + { + DataRow row = this.NewRow(); + row[0] = ad.Hex; + row[1] = ad.Hex; + row[2] = ad.Lat; + row[3] = ad.Lon; + row[4] = ad.Alt; + row[5] = ad.Track; + row[6] = ad.Speed; + row[7] = ad.LastUpdated; + this.Rows.Add(row); + } + } + + + } +} diff --git a/AirScout.AircraftPositions/AircraftPositionDatabase.cs b/AirScout.AircraftPositions/AircraftPositionDatabase.cs new file mode 100644 index 0000000..36b3af6 --- /dev/null +++ b/AirScout.AircraftPositions/AircraftPositionDatabase.cs @@ -0,0 +1,939 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.IO; +using System.Globalization; +using System.Reflection; +using System.Data; +using System.Diagnostics; +using ScoutBase.Core; +using ScoutBase.Propagation; +using System.Data.SQLite; +using System.ComponentModel; +using System.Windows.Forms; +using Newtonsoft.Json; + +namespace AirScout.AircraftPositions +{ + + public class AircraftPositionData + { + static AircraftPositionDatabase aircraftpositions = new AircraftPositionDatabase(); + public static AircraftPositionDatabase Database + { + get + { + return aircraftpositions; + } + } + + } + + /// + /// Holds the Aircraft position information in a database structure. + /// + public class AircraftPositionDatabase + { + System.Data.SQLite.SQLiteDatabase db; + + private string DBPath; + + private static LogWriter Log = LogWriter.Instance; + + public readonly int UserVersion = 1; + + public AircraftPositionDatabase() + { + db = OpenDatabase("aircraftpositions.db3"); + // set auto vacuum mode to "Full" to allow database to reduce size on disk + // requires a vacuum command to change database layout + AUTOVACUUMMODE mode = db.GetAutoVacuum(); + if (mode != AUTOVACUUMMODE.FULL) + { + if (MessageBox.Show("A major database layout change is necessary to run this version of AirScout. Older versions of AirScout are not compatible anymore and will cause errors. \n\nPress >OK< to start upgrade now (this will take some minutes). \nPress >Cancel< to leave.", "Database Upgrade of " + Path.GetFileName(db.DBLocation), MessageBoxButtons.OKCancel) == DialogResult.Cancel) + Environment.Exit(-1); // exit immediately + db.SetAutoVacuum(AUTOVACUUMMODE.FULL); + } + // create tables with schemas if not exist + if (!AircraftPositionTableExists()) + AircraftPositionCreateTable(); + } + + ~AircraftPositionDatabase() + { + CloseDatabase(db); + } + + private System.Data.SQLite.SQLiteDatabase OpenDatabase(string name) + { + System.Data.SQLite.SQLiteDatabase db = null; + try + { + // check if database path exists --> create if not + if (!Directory.Exists(DefaultDatabaseDirectory())) + Directory.CreateDirectory(DefaultDatabaseDirectory()); + // check if database is already on disk + DBPath = DefaultDatabaseDirectory(); + if (!File.Exists(Path.Combine(DBPath, name))) + { + // create one on disk + System.Data.SQLite.SQLiteDatabase dbn = new System.Data.SQLite.SQLiteDatabase(Path.Combine(DBPath, name)); + // open database + dbn.Open(); + // set user version + dbn.SetUserVerion(UserVersion); + // set auto vacuum mode to full + dbn.SetAutoVacuum(AUTOVACUUMMODE.FULL); + dbn.Close(); + } + // check for in-memory database --> open from disk, if not + if (Properties.Settings.Default.Database_InMemory) + db = System.Data.SQLite.SQLiteDatabase.CreateInMemoryDB(Path.Combine(DBPath, name)); + else + { + db = new System.Data.SQLite.SQLiteDatabase(Path.Combine(DBPath, name)); + db.Open(); + } + // get version info + int v = db.GetUserVersion(); + // do upgrade stuff here + } + catch (Exception ex) + { + Console.WriteLine("Error initilalizing database: " + ex.Message); + throw new TypeInitializationException(this.GetType().Name, ex); + } + return db; + } + + private void CloseDatabase(System.Data.SQLite.SQLiteDatabase db) + { + if (db == null) + return; + // save in-memory database to disk + if (db.IsInMemory) + db.BackupDatabase(db.DBLocation); + // else + // db.Close(); + } + + public void BackupDatabase() + { + if (db == null) + return; + // save in-memory database to disk + if (db.IsInMemory) + db.BackupDatabase(db.DiskFileName); + else + db.Close(); + } + + public bool IsInMemory() + { + return db.IsInMemory; + } + + public string DefaultDatabaseDirectory() + { + // create default database directory name + string dir = Properties.Settings.Default.Database_Directory; + if (!String.IsNullOrEmpty(dir)) + { + return dir; + } + // empty settings -_> create standard path + // collect entry assembly info + Assembly ass = Assembly.GetExecutingAssembly(); + string company = ""; + string product = ""; + object[] attribs; + attribs = ass.GetCustomAttributes(typeof(AssemblyCompanyAttribute), true); + if (attribs.Length > 0) + { + company = ((AssemblyCompanyAttribute)attribs[0]).Company; + } + attribs = ass.GetCustomAttributes(typeof(AssemblyProductAttribute), true); + if (attribs.Length > 0) + { + product = ((AssemblyProductAttribute)attribs[0]).Product; + } + // create database path + dir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + if (!String.IsNullOrEmpty(company)) + dir = Path.Combine(dir, company); + if (!String.IsNullOrEmpty(product)) + dir = Path.Combine(dir, product); + return Path.Combine(dir, "AircraftData"); + } + + public DATABASESTATUS GetDBStatus() + { + if (db != null) + return db.Status; + return DATABASESTATUS.UNDEFINED; + } + + public void SetDBStatus(DATABASESTATUS status) + { + if (db != null) + db.Status = status; + } + + public bool GetDBStatusBit(DATABASESTATUS statusbit) + { + if (db != null) + return (((int)db.Status) & ((int)statusbit)) > 0; + return false; + } + + public void SetDBStatusBit(DATABASESTATUS statusbit) + { + if (db != null) + db.Status |= statusbit; + } + + public void ResetDBStatusBit(DATABASESTATUS statusbit) + { + if (db != null) + db.Status &= ~statusbit; + } + + public void BeginTransaction() + { + if (db == null) + return; + db.BeginTransaction(); + } + + public void Commit() + { + if (db == null) + return; + db.Commit(); + } + + private DataTable Select(System.Data.SQLite.SQLiteDatabase db, string sql) + { + return db.Select(sql); + } + + public string GetDBLocation() + { + if (db == null) + return ""; + return db.DBLocation; + } + + public double GetDBSize() + { + if (db == null) + return 0; + return db.DBSize; + } + + private bool IsValid(object obj) + { + if (obj == null) + return false; + if (obj.GetType() == typeof(DBNull)) + return false; + return true; + } + + #region AircraftPositions + + public bool AircraftPositionTableExists(string tablename = "") + { + // check for table name is null or empty --> use default tablename from type instead + string tn = tablename; + if (String.IsNullOrEmpty(tn)) + tn = AircraftPositionDesignator.TableName; + return db.TableExists(tn); + } + + public void AircraftPositionCreateTable(string tablename = "") + { + lock (db.DBCommand) + { + // check for table name is null or empty --> use default tablename from type instead + string tn = tablename; + if (String.IsNullOrEmpty(tn)) + tn = AircraftPositionDesignator.TableName; + db.DBCommand.CommandText = "CREATE TABLE `" + tn + "`(Hex TEXT NOT NULL DEFAULT '', Call TEXT NOT NULL DEFAULT '', Lat DOUBLE, Lon DOUBLE, Alt DOUBLE, Track DOUBLE, Speed DOUBLE, LastUpdated INT32 NOT NULL DEFAULT 0, PRIMARY KEY (Hex, LastUpdated))"; + db.DBCommand.Parameters.Clear(); + db.Execute(db.DBCommand); + // create table indices + db.DBCommand.CommandText = "CREATE INDEX idx_" + tn + "_Hex ON `" + tn + "` (Hex)"; + db.DBCommand.Parameters.Clear(); + db.Execute(db.DBCommand); + db.DBCommand.CommandText = "CREATE INDEX idx_" + tn + "_Call ON `" + tn + "` (Call)"; + db.DBCommand.Parameters.Clear(); + db.Execute(db.DBCommand); + } + } + + public long AircraftPositionCount() + { + object count = db.ExecuteScalar("SELECT COUNT(*) FROM " + AircraftPositionDesignator.TableName); + if (IsValid(count)) + return (long)count; + return 0; + } + + public bool AircraftPositionExists(string hex, DateTime lastupdated) + { + AircraftPositionDesignator ap = new AircraftPositionDesignator(hex, lastupdated); + return AircraftPositionExists(ap); + } + + public bool AircraftPositionExists(AircraftPositionDesignator ap) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT EXISTS (SELECT LastUpdated FROM " + AircraftPositionDesignator.TableName + " WHERE Hex = @Hex AND LastUpdated = @LastUpdated"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(ap.AsString("Hex")); + db.DBCommand.Parameters.Add(ap.AsUNIXTime("LastUpdated")); + object result = db.DBCommand.ExecuteScalar(); + if (IsValid(result) && ((long)result > 0)) + return true; + } + return false; + } + + public AircraftPositionDesignator AircraftPositionFind(string hex, DateTime lastupdated) + { + AircraftPositionDesignator ad = new AircraftPositionDesignator(hex, lastupdated); + return AircraftPositionFind(ad); + } + + public AircraftPositionDesignator AircraftPositionFindByHex(string hex) + { + // returs entry by search string, latest entry if more than one entry found + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT * FROM " + AircraftPositionDesignator.TableName + " WHERE Hex = '" + hex + "' ORDER BY Lastupdated DESC LIMIT 1"; + db.DBCommand.Parameters.Clear(); + try + { + DataTable Result = db.Select(db.DBCommand); + if ((Result != null) && (Result.Rows.Count > 0)) + { + return new AircraftPositionDesignator(Result.Rows[0]); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + return null; + } + + public AircraftPositionDesignator AircraftPositionFindByCall(string call ) + { + // returs entry by search string, latest entry if more than one entry found + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT * FROM " + AircraftPositionDesignator.TableName + " WHERE Call = '" + call + "' ORDER BY Lastupdated DESC LIMIT 1"; + db.DBCommand.Parameters.Clear(); + try + { + DataTable Result = db.Select(db.DBCommand); + if ((Result != null) && (Result.Rows.Count > 0)) + { + return new AircraftPositionDesignator(Result.Rows[0]); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + return null; + } + + public AircraftPositionDesignator AircraftPositionFind(AircraftPositionDesignator ap) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT * FROM " + AircraftPositionDesignator.TableName + " WHERE Hex = @Hex AND LastUpdated = @LastUpdated"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(ap.AsString("Hex")); + db.DBCommand.Parameters.Add(ap.AsUNIXTime("LastUpdated")); + try + { + DataTable Result = db.Select(db.DBCommand); + if (IsValid(Result) && (Result.Rows.Count > 0)) + { + return new AircraftPositionDesignator(Result.Rows[0]); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + return null; + } + + public AircraftPositionDesignator AircraftPositionFindAt(long index) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT * FROM " + AircraftPositionDesignator.TableName + " LIMIT 1 OFFSET " + index.ToString(); + db.DBCommand.Parameters.Clear(); + try + { + DataTable Result = db.Select(db.DBCommand); + if ((Result != null) && (Result.Rows.Count > 0)) + { + return new AircraftPositionDesignator(Result.Rows[0]); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + return null; + } + + public int AircraftPositionInsert(AircraftPositionDesignator ap) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "INSERT INTO " + AircraftPositionDesignator.TableName + " (Hex, Call, Lat, Lon, Alt, Track, Speed, LastUpdated) VALUES (@Hex, @Call, @Lat, @Lon, @Alt, @Track, @Speed, @LastUpdated)"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(ap.AsString("Hex")); + db.DBCommand.Parameters.Add(ap.AsString("Call")); + db.DBCommand.Parameters.Add(ap.AsDouble("Lat")); + db.DBCommand.Parameters.Add(ap.AsDouble("Lon")); + db.DBCommand.Parameters.Add(ap.AsDouble("Alt")); + db.DBCommand.Parameters.Add(ap.AsDouble("Track")); + db.DBCommand.Parameters.Add(ap.AsDouble("Speed")); + db.DBCommand.Parameters.Add(ap.AsUNIXTime("LastUpdated")); + return db.ExecuteNonQuery(db.DBCommand); + } + } + + public int AircraftPositionDelete(string hex, DateTime lastupdated) + { + AircraftPositionDesignator ap = new AircraftPositionDesignator(hex, lastupdated); + return AircraftPositionDelete(ap); + } + + public int AircraftPositionDelete(AircraftPositionDesignator ap) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "DELETE FROM " + AircraftPositionDesignator.TableName + " WHERE Hex = @Hex AND LastUpdated = @LastUpdated"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(ap.AsString("Hex")); + db.DBCommand.Parameters.Add(ap.AsUNIXTime("LastUpdated")); + return db.ExecuteNonQuery(db.DBCommand); + } + } + + public int AircraftPositionDeleteAll() + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "DELETE FROM " + AircraftPositionDesignator.TableName; + db.DBCommand.Parameters.Clear(); + return db.ExecuteNonQuery(db.DBCommand); + } + } + + public int AircraftPositionDeleteFirst(int count) + { + // deletes the first n entries from data table sorted by rowid + lock (db.DBCommand) + { + db.DBCommand.CommandText = "DELETE FROM " + AircraftPositionDesignator.TableName + " WHERE rowid IN (SELECT rowid FROM AircraftPositions ORDER BY rowid ASC LIMIT " + count.ToString() + ")"; + db.DBCommand.Parameters.Clear(); + return db.ExecuteNonQuery(db.DBCommand); + } + } + + public int AircraftPositionBulkDeleteFirst(int count) + { + int i = 0; + try + { + lock (db) + { + db.BeginTransaction(); + // deletes the first n entries from data table sorted by rowid + lock (db.DBCommand) + { + db.DBCommand.CommandText = "DELETE FROM " + AircraftPositionDesignator.TableName + " WHERE rowid IN (SELECT rowid FROM AircraftPositions ORDER BY rowid ASC LIMIT " + count.ToString() + ")"; + db.DBCommand.Parameters.Clear(); + i = db.ExecuteNonQuery(db.DBCommand); + } + db.Commit(); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + return i; + } + + public int AircraftPositionDeleteOlderThan(DateTime olderthan) + { + // deletes all entries from data table older than xxx + AircraftPositionDesignator ap = new AircraftPositionDesignator(); + ap.LastUpdated = olderthan; + lock (db.DBCommand) + { + db.DBCommand.CommandText = "DELETE FROM " + AircraftPositionDesignator.TableName + " WHERE LastUpdated < @LastUpdated"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(ap.AsUNIXTime("LastUpdated")); + return db.ExecuteNonQuery(db.DBCommand); + } + } + + public int AircraftPositionBulkDeleteOlderThan(DateTime olderthan) + { + // deletes all entries from data table older than xxx + AircraftPositionDesignator ap = new AircraftPositionDesignator(); + ap.LastUpdated = olderthan; + int i = 0; + try + { + lock (db) + { + db.BeginTransaction(); + lock (db.DBCommand) + { + db.DBCommand.CommandText = "DELETE FROM " + AircraftPositionDesignator.TableName + " WHERE LastUpdated < @LastUpdated"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(ap.AsUNIXTime("LastUpdated")); + i = db.ExecuteNonQuery(db.DBCommand); + } + db.Commit(); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + return i; + } + + public int AircraftPositionUpdate(AircraftPositionDesignator ap) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "UPDATE " + AircraftPositionDesignator.TableName + " SET Hex = @Hex, Call = @Call, Lat = @Lat, Lon = @Lon, Alt = @Alt, Track = @Track, Speed = @Speed, LastUpdated = @LastUpdated WHERE Hex = @Hex AND LastUpdated = @LastUpdated"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(ap.AsString("Hex")); + db.DBCommand.Parameters.Add(ap.AsString("Call")); + db.DBCommand.Parameters.Add(ap.AsDouble("Lat")); + db.DBCommand.Parameters.Add(ap.AsDouble("Lon")); + db.DBCommand.Parameters.Add(ap.AsDouble("Alt")); + db.DBCommand.Parameters.Add(ap.AsDouble("Track")); + db.DBCommand.Parameters.Add(ap.AsDouble("Speed")); + db.DBCommand.Parameters.Add(ap.AsUNIXTime("LastUpdated")); + return db.ExecuteNonQuery(db.DBCommand); + } + } + + public int AircraftPositionBulkInsert(List aps) + { + int errors = 0; + try + { + lock (db) + { + db.BeginTransaction(); + foreach (AircraftPositionDesignator ap in aps) + { + try + { + AircraftPositionInsert(ap); + } + catch (Exception ex) + { + Log.WriteMessage("Error inserting aircraft position [" + ap.Hex + "]: " + ex.ToString()); + errors++; + } + } + db.Commit(); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + return -errors; + } + + public int AircraftPositionBulkDelete(List aps) + { + int errors = 0; + try + { + lock (db) + { + db.BeginTransaction(); + foreach (AircraftPositionDesignator ap in aps) + { + try + { + AircraftPositionDelete(ap); + } + catch (Exception ex) + { + Log.WriteMessage("Error deleting aircraft position[" + ap.Hex + "]: " + ex.ToString()); + errors++; + } + } + db.Commit(); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + return -errors; + } + + public int AircraftPositionBulkInsertOrUpdateIfNewer(List aps) + { + if (aps == null) + return 0; + int i = 0; + lock (db) + { + db.BeginTransaction(); + foreach (AircraftPositionDesignator ap in aps) + { + i = i + AircraftPositionInsertOrUpdateIfNewer(ap); + } + db.Commit(); + } + return i; + } + + public int AircraftPositionBulkInsertOrUpdateIfNewer(BackgroundWorker caller, List aps) + { + if (aps == null) + return 0; + int i = 0; + int count = aps.Count; + lock (db) + { + db.BeginTransaction(); + foreach (AircraftPositionDesignator ap in aps) + { + i = i + AircraftPositionInsertOrUpdateIfNewer(ap); + // abort calculation if called from background worker and cancellation pending + if (caller != null) + { + if (caller.WorkerSupportsCancellation && caller.CancellationPending) + { + db.Rollback(); + return -1; + } + if (caller.WorkerReportsProgress && (i % 1000 == 0)) + caller.ReportProgress(0, "Updating position " + i.ToString() + " of " + count.ToString()); + } + + } + db.Commit(); + } + return i; + } + + public int AircraftPositionInsertOrUpdateIfNewer(AircraftPositionDesignator ap) + { + AircraftPositionDesignator an = AircraftPositionFind(ap); + if (an == null) + { + int result = AircraftPositionInsert(ap); + // Console.WriteLine("Aircraft position inserted: " + ap.Hex + "," + ap.LastUpdated.ToString("yyyy-MM-dd HH:mm:ss") + ", " + result.ToString()); + return result; + } + if (ap.LastUpdated > an.LastUpdated) + return AircraftPositionUpdate(ap); + return 0; + } + + public DateTime AircraftPositionOldestEntry() + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT min(LastUpdated) FROM " + AircraftPositionDesignator.TableName; + db.DBCommand.Parameters.Clear(); + object result = db.DBCommand.ExecuteScalar(); + if (IsValid(result)) + { + DateTime dt = SupportFunctions.UNIXTimeToDateTime(System.Convert.ToInt32(result)); + return dt; + } + } + return DateTime.MinValue; + + } + + public DateTime AircraftPositionYoungestEntry() + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT max(LastUpdated) FROM " + AircraftPositionDesignator.TableName; + db.DBCommand.Parameters.Clear(); + object result = db.DBCommand.ExecuteScalar(); + if (IsValid(result)) + { + DateTime dt = SupportFunctions.UNIXTimeToDateTime(System.Convert.ToInt32(result)); + return dt; + } + } + return DateTime.MinValue; + + } + + public List AircraftPositionGetAllLastUpdated() + { + List l = new List(); + DataTable Result = db.Select("SELECT LastUpdated FROM " + AircraftPositionDesignator.TableName + " ORDER BY LastUpdated ASC"); + if (!IsValid(Result) || (Result.Rows.Count == 0)) + return l; + foreach (DataRow row in Result.Rows) + l.Add(SupportFunctions.UNIXTimeToDateTime((int)row[0])); + return l; + + } + + public List AircraftPositionGetAllHex() + { + List l = new List(); + DataTable Result = db.Select("SELECT Hex FROM " + AircraftPositionDesignator.TableName + " GROUP BY Hex"); + if (!IsValid(Result) || (Result.Rows.Count == 0)) + return l; + foreach (DataRow row in Result.Rows) + l.Add(row[0].ToString()); + return l; + + } + + public List AircraftPositionGetAllHex(DateTime from, DateTime to) + { + List l = new List(); + DataTable Result = db.Select("SELECT Hex FROM " + AircraftPositionDesignator.TableName + " WHERE LastUpdated >= " + SupportFunctions.DateTimeToUNIXTime(from).ToString() + " AND LastUpdated <= " + SupportFunctions.DateTimeToUNIXTime(to).ToString() + " GROUP BY Hex"); + if (!IsValid(Result) || (Result.Rows.Count == 0)) + return l; + foreach (DataRow row in Result.Rows) + l.Add(row[0].ToString()); + return l; + + } + + public List AircraftPositionGetAllCalls() + { + List l = new List(); + DataTable Result = db.Select("SELECT Call FROM " + AircraftPositionDesignator.TableName + " GROUP BY Call"); + if (!IsValid(Result) || (Result.Rows.Count == 0)) + return l; + foreach (DataRow row in Result.Rows) + l.Add(row[0].ToString()); + return l; + + } + + public List AircraftPositionGetAllCalls(DateTime from, DateTime to) + { + List l = new List(); + DataTable Result = db.Select("SELECT Call FROM " + AircraftPositionDesignator.TableName + " WHERE LastUpdated >= " + SupportFunctions.DateTimeToUNIXTime(from).ToString() + " AND LastUpdated <= " + SupportFunctions.DateTimeToUNIXTime(to).ToString() + " GROUP BY Call"); + if (!IsValid(Result) || (Result.Rows.Count == 0)) + return l; + foreach (DataRow row in Result.Rows) + l.Add(row[0].ToString()); + return l; + + } + + public List AircraftPositionGetAll() + { + List l = new List(); + DataTable Result = db.Select("SELECT * FROM " + AircraftPositionDesignator.TableName + " ORDER BY LastUpdated ASC"); + if (!IsValid(Result) || (Result.Rows.Count == 0)) + return l; + foreach (DataRow row in Result.Rows) + l.Add(new AircraftPositionDesignator(row)); + return l; + + } + + public List AircraftPositionGetAll(BackgroundWorker caller) + { + // gets all aircraftpositions from database + // supports abort calculation if called from background worker and cancellation requested + List l = new List(); + int i = 0; + SQLiteCommand cmd = new SQLiteCommand(db.DBConnection); + cmd.CommandText = "SELECT * FROM " + AircraftPositionDesignator.TableName + " ORDER BY LastUpdated ASC"; + SQLiteDataReader reader = cmd.ExecuteReader(); + while (reader.Read()) + { + AircraftPositionDesignator ap = new AircraftPositionDesignator((IDataRecord)reader); + l.Add(ap); + i++; + // abort calculation if called from background worker and cancellation pending + if (caller != null) + { + if (caller.WorkerSupportsCancellation && caller.CancellationPending) + return new List(); + if (caller.WorkerReportsProgress && (i % 1000 == 0)) + caller.ReportProgress(0, "Getting position " + i.ToString() + " of"); + } + } + reader.Close(); + return l; + } + + public List AircraftPositionGetAllByHex(string hex) + { + List l = new List(); + DataTable Result = db.Select("SELECT * FROM " + AircraftPositionDesignator.TableName + " WHERE Hex = '" + hex + "' ORDER BY LastUpdated ASC"); + if (!IsValid(Result) || (Result.Rows.Count == 0)) + return l; + foreach (DataRow row in Result.Rows) + l.Add(new AircraftPositionDesignator(row)); + return l; + + } + + public List AircraftPositionGetAllByHex(string hex, DateTime from, DateTime to) + { + List l = new List(); + DataTable Result = db.Select("SELECT * FROM " + AircraftPositionDesignator.TableName + " WHERE Hex = '" + hex + "' AND LastUpdated >= " + SupportFunctions.DateTimeToUNIXTime(from).ToString() + " AND LastUpdated <= " + SupportFunctions.DateTimeToUNIXTime(to).ToString() + " ORDER BY LastUpdated ASC"); + if (!IsValid(Result) || (Result.Rows.Count == 0)) + return l; + foreach (DataRow row in Result.Rows) + l.Add(new AircraftPositionDesignator(row)); + return l; + + } + + public List AircraftPositionGetAllByCall(string call) + { + List l = new List(); + DataTable Result = db.Select("SELECT * FROM " + AircraftPositionDesignator.TableName + " WHERE Call = '" + call + "' ORDER BY LastUpdated ASC"); + if (!IsValid(Result) || (Result.Rows.Count == 0)) + return l; + foreach (DataRow row in Result.Rows) + l.Add(new AircraftPositionDesignator(row)); + return l; + + } + + public List AircraftPositionGetAllByCall(string call, DateTime from, DateTime to) + { + List l = new List(); + DataTable Result = db.Select("SELECT * FROM " + AircraftPositionDesignator.TableName + " WHERE Call = '" + call + "' AND LastUpdated >= " + SupportFunctions.DateTimeToUNIXTime(from).ToString() + " AND LastUpdated <= " + SupportFunctions.DateTimeToUNIXTime(to).ToString() + " ORDER BY LastUpdated ASC"); + if (!IsValid(Result) || (Result.Rows.Count == 0)) + return l; + foreach (DataRow row in Result.Rows) + l.Add(new AircraftPositionDesignator(row)); + return l; + + } + + public List AircraftPositionGetAll(DateTime from, DateTime to) + { + List l = new List(); + DataTable Result = db.Select("SELECT * FROM " + AircraftPositionDesignator.TableName + " WHERE LastUpdated >= " + SupportFunctions.DateTimeToUNIXTime(from).ToString() + " AND LastUpdated <= " + SupportFunctions.DateTimeToUNIXTime(to).ToString() + " ORDER BY LastUpdated ASC"); + if (!IsValid(Result) || (Result.Rows.Count == 0)) + return l; + foreach (DataRow row in Result.Rows) + l.Add(new AircraftPositionDesignator(row)); + return l; + + } + + public List AircraftPositionGetAllLatest(DateTime from) + { + List l = new List(); + int i = SupportFunctions.DateTimeToUNIXTime(from); + DataTable Result = db.Select("SELECT Hex, Call, Lat, Lon, Alt, Track, Speed, max(Lastupdated) AS LastUpdated FROM " + AircraftPositionDesignator.TableName + " WHERE LastUpdated > " + i.ToString() + " GROUP BY Hex"); + if (!IsValid(Result) || (Result.Rows.Count == 0)) + return l; + foreach (DataRow row in Result.Rows) + l.Add(new AircraftPositionDesignator(row)); + return l; + + } + + + /// + /// Gets a list of aircraft positions at a time. + /// Querying the latest position entry per aircraft but not older than ttl back in history + /// and estimating the position at given time + /// The given time. + /// "Time To Live": discard positions which are older than ttl [min]. + /// + /// + public List AircraftPositionGetAllLatest(DateTime at, int ttl) + { + List l = new List(); + int to = SupportFunctions.DateTimeToUNIXTime(at); + int from = to - ttl * 60; + DataTable Result = db.Select("SELECT Hex, Call, Lat, Lon, Alt, Track, Speed, max(Lastupdated) AS LastUpdated FROM " + AircraftPositionDesignator.TableName + " WHERE LastUpdated >= " + from.ToString() + " AND LastUpdated <= " + to.ToString() + " GROUP BY Hex"); + if (!IsValid(Result) || (Result.Rows.Count == 0)) + return l; + foreach (DataRow row in Result.Rows) + { + AircraftPositionDesignator ap = new AircraftPositionDesignator(row); + //estimate new position + // change speed to km/h + double speed = ap.Speed * 1.852; + // calculate distance after timespan + double dist = speed * (at - ap.LastUpdated).TotalHours; + // estimate new position + LatLon.GPoint newpos = LatLon.DestinationPoint(ap.Lat, ap.Lon, ap.Track, dist); + ap.Lat = newpos.Lat; + ap.Lon = newpos.Lon; + l.Add(ap); + } + return l; + + } + + public List AircraftPositionFromJSON(string json) + { + if (String.IsNullOrEmpty(json)) + return null; + JsonSerializerSettings settings = new JsonSerializerSettings(); + settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; + settings.FloatFormatHandling = FloatFormatHandling.String; + settings.Formatting = Newtonsoft.Json.Formatting.Indented; + return JsonConvert.DeserializeObject>(json, settings); + } + + public string AircraftPositionToJSON() + { + List l = AircraftPositionGetAll(); + JsonSerializerSettings settings = new JsonSerializerSettings(); + settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; + settings.FloatFormatHandling = FloatFormatHandling.String; + settings.Formatting = Newtonsoft.Json.Formatting.Indented; + string json = JsonConvert.SerializeObject(l, settings); + return json; + } + + public DataTableAircraftPositions AircraftPositionToDataTable() + { + List ap = AircraftPositionGetAll(); + DataTableAircraftPositions dtl = new DataTableAircraftPositions(ap); + return dtl; + } + + #endregion + + } + +} diff --git a/AirScout.AircraftPositions/AircraftPositionDatabaseMaintainer.cs b/AirScout.AircraftPositions/AircraftPositionDatabaseMaintainer.cs new file mode 100644 index 0000000..817f7e2 --- /dev/null +++ b/AirScout.AircraftPositions/AircraftPositionDatabaseMaintainer.cs @@ -0,0 +1,142 @@ + +using System; +using System.ComponentModel; +using System.Windows.Forms; +using System.Net; +using System.IO; +using System.Threading; +using System.Diagnostics; +using System.Collections; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Globalization; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using ScoutBase; +using ScoutBase.Core; +using System.Data.SQLite; +using System.Text; + +namespace AirScout.AircraftPositions +{ + + #region AircraftPositionDatabaseMaintainer + + + public class AircraftPositionDatabaseMaintainerStartOptions + { + public string Name; + public double Database_MaxSize; + public long Database_MaxCount; + public int Database_MaxDaysLifetime; + } + + + // Background worker for aircraft position database maintainance + [DefaultPropertyAttribute("Name")] + public class AircraftPositionDatabaseMaintainer : BackgroundWorker + { + + AircraftPositionDatabaseMaintainerStartOptions StartOptions; + double DBLastSize = 0; + + public AircraftPositionDatabaseMaintainer() : base() + { + this.WorkerReportsProgress = true; + this.WorkerSupportsCancellation = true; + } + + protected override void OnDoWork(DoWorkEventArgs e) + { + StartOptions = (AircraftPositionDatabaseMaintainerStartOptions)e.Argument; + this.ReportProgress(0, StartOptions.Name + "started."); + // name the thread for debugging + if (String.IsNullOrEmpty(Thread.CurrentThread.Name)) + Thread.CurrentThread.Name = nameof(AircraftPositionDatabaseMaintainer); + // get maintenance interval + int interval = (int)Properties.Settings.Default.Database_BackgroundMaintenance_Period * 60; + do + { + try + { + this.ReportProgress(0, "Maintaining database..."); + try + { + // check database entry count and remove oldest entries + long count = AircraftPositionData.Database.AircraftPositionCount(); + if (this.CancellationPending) + break; + // check against limit + if (count > StartOptions.Database_MaxCount) + { + // remove oldest entries from database + this.ReportProgress(0, "Exceeding database entries count limit, removing entries..."); + AircraftPositionData.Database.AircraftPositionBulkDeleteFirst(1000); + } + } + catch (Exception ex) + { + this.ReportProgress(-1, ex.ToString()); + } + try + { + // check and reduce database size + // tricky, because deleting entries not necessarily reduces database size + // neeeds pragma auto_vacuum to be set to full + // delete an amount of entries from AircraftPositions table and let auto_vacuum do the rest + // keep last reported size in memory trigger deletion only if size is increasing again + double size = AircraftPositionData.Database.GetDBSize(); + // check against last reported size + if (size > DBLastSize) + { + // keep last reported size + DBLastSize = size; + if (DBLastSize > StartOptions.Database_MaxSize) + { + // remove first 1000 entries from database + this.ReportProgress(0, "Exceeding database size limit, removing entries..."); + AircraftPositionData.Database.AircraftPositionBulkDeleteFirst(1000); + } + } + } + catch (Exception ex) + { + this.ReportProgress(-1, ex.ToString()); + } + try + { + // check and remove oldest entries + DateTime olderthan = DateTime.UtcNow - new TimeSpan(StartOptions.Database_MaxDaysLifetime, 0, 0, 0); + // remove oldest entries from database + this.ReportProgress(0, "Cleaning up aircraft positions..."); + AircraftPositionData.Database.AircraftPositionBulkDeleteOlderThan(olderthan); + } + catch (Exception ex) + { + this.ReportProgress(-1, ex.ToString()); + } + // sleep + int i = 0; + while (!this.CancellationPending && (i < interval)) + { + Thread.Sleep(1000); + i++; + } + } + catch (Exception ex) + { + this.ReportProgress(-1, ex.ToString()); + } + } + while (!this.CancellationPending); + if (this.CancellationPending) + this.ReportProgress(0, "Cancelled."); + else + this.ReportProgress(0, "Finished."); + } + + #endregion + + } +} diff --git a/AirScout.AircraftPositions/Properties/AssemblyInfo.cs b/AirScout.AircraftPositions/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..6dc5abd --- /dev/null +++ b/AirScout.AircraftPositions/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Allgemeine Informationen über eine Assembly werden über die folgenden +// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, +// die einer Assembly zugeordnet sind. +[assembly: AssemblyTitle("AirScout.AircraftPositions")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("DL2ALF")] +[assembly: AssemblyProduct("AirScout")] +[assembly: AssemblyCopyright("Copyright © 2019 DL2ALF")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar +// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von +// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen. +[assembly: ComVisible(false)] + +// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird +[assembly: Guid("be467e28-c202-4d71-bb17-9086861ad75f")] + +// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: +// +// Hauptversion +// Nebenversion +// Buildnummer +// Revision +// +// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern +// übernehmen, indem Sie "*" eingeben: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.3.0.0")] +[assembly: AssemblyFileVersion("1.3.0.0")] diff --git a/AirScout.AircraftPositions/Properties/Settings.Designer.cs b/AirScout.AircraftPositions/Properties/Settings.Designer.cs new file mode 100644 index 0000000..76e0ee7 --- /dev/null +++ b/AirScout.AircraftPositions/Properties/Settings.Designer.cs @@ -0,0 +1,74 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.42000 +// +// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn +// der Code erneut generiert wird. +// +//------------------------------------------------------------------------------ + +namespace AirScout.AircraftPositions.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string Database_Directory { + get { + return ((string)(this["Database_Directory"])); + } + set { + this["Database_Directory"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Database_InMemory { + get { + return ((bool)(this["Database_InMemory"])); + } + set { + this["Database_InMemory"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string AIrcraftPositions_DataPath { + get { + return ((string)(this["AIrcraftPositions_DataPath"])); + } + set { + this["AIrcraftPositions_DataPath"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("60")] + public int Database_BackgroundMaintenance_Period { + get { + return ((int)(this["Database_BackgroundMaintenance_Period"])); + } + set { + this["Database_BackgroundMaintenance_Period"] = value; + } + } + } +} diff --git a/AirScout.AircraftPositions/Properties/Settings.settings b/AirScout.AircraftPositions/Properties/Settings.settings new file mode 100644 index 0000000..a9629b0 --- /dev/null +++ b/AirScout.AircraftPositions/Properties/Settings.settings @@ -0,0 +1,18 @@ + + + + + + + + + False + + + + + + 60 + + + \ No newline at end of file diff --git a/AirScout.AircraftPositions/app.config b/AirScout.AircraftPositions/app.config new file mode 100644 index 0000000..fb67c45 --- /dev/null +++ b/AirScout.AircraftPositions/app.config @@ -0,0 +1,24 @@ + + + + +
+ + + + + + + + + False + + + + + + 60 + + + + \ No newline at end of file diff --git a/AirScout.AircraftPositions/packages.config b/AirScout.AircraftPositions/packages.config new file mode 100644 index 0000000..0aee17e --- /dev/null +++ b/AirScout.AircraftPositions/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/AirScout.Aircrafts/AirScout.Aircrafts.csproj b/AirScout.Aircrafts/AirScout.Aircrafts.csproj new file mode 100644 index 0000000..d1150ee --- /dev/null +++ b/AirScout.Aircrafts/AirScout.Aircrafts.csproj @@ -0,0 +1,108 @@ + + + + + Debug + AnyCPU + {288A26EC-B690-41A2-84E5-61C9B7B74046} + Library + Properties + AirScout.Aircrafts + AirScout.Aircrafts + v4.0 + 512 + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Newtonsoft.Json.12.0.1\lib\net40\Newtonsoft.Json.dll + True + + + + + ..\packages\System.Data.SQLite.Core.1.0.110.0\lib\net40\System.Data.SQLite.dll + True + + + + + + + + + + + + + + + + + + + + + + True + True + Settings.settings + + + + + + {ee86e933-d883-4b18-80eb-0fba55ec67c6} + ScoutBase.Core + + + {610db007-5f74-4b5d-8b71-5e2c163a99b3} + ScoutBase.Propagation + + + {6056d3be-7002-4a6a-a9ea-6ff45122a3c7} + SQLiteDatabase + + + + + + + PublicSettingsSingleFileGenerator + Settings.Designer.cs + + + + + + + Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}". + + + + + \ No newline at end of file diff --git a/AirScout.Aircrafts/Aircraft.cs b/AirScout.Aircrafts/Aircraft.cs new file mode 100644 index 0000000..1ff8794 --- /dev/null +++ b/AirScout.Aircrafts/Aircraft.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using System.Collections; +using System.Linq; +using System.Text; +using System.Threading; +using System.ComponentModel; +using System.Globalization; +using System.Net; +using System.IO; +using System.Data; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System.Data.SQLite; + +namespace AirScout.Aircrafts +{ + /// + /// Holds the aircraft information + /// + [System.ComponentModel.DesignerCategory("")] + public class AircraftDesignator : SQLiteEntry + { + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // be sure to have a copy of these static members in each derived class !! + // update the tabale name to the table name according to the class + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + [JsonIgnore] + public static new readonly string TableName = "Aircrafts"; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public string Hex { get; set; } + public string Call { get; set; } + public string Reg { get; set; } + public string TypeCode { get; set; } + + public AircraftDesignator() + { + Hex = ""; + Call = ""; + Reg = ""; + TypeCode = ""; + LastUpdated = DateTime.MinValue; + } + + public AircraftDesignator(DataRow row) : this() + { + FillFromDataRow(row); + } + + public AircraftDesignator(IDataRecord record) : this() + { + FillFromDataRecord(record); + } + + + public AircraftDesignator(string hex) : this(hex, "", "", "", DateTime.UtcNow) { } + public AircraftDesignator(string hex, string call, string reg, string typecode) : this(hex, call, reg, typecode, DateTime.UtcNow) { } + public AircraftDesignator(string hex, string call, string reg, string typecode, DateTime lastupdated) : this() + { + Hex = hex; + Call = call; + Reg = reg; + TypeCode = typecode; + LastUpdated = lastupdated; + } + + } + + [System.ComponentModel.DesignerCategory("")] + public class DataTableAircrafts : DataTable + { + public DataTableAircrafts() + : base() + { + // set table name + TableName = "Aircrafts"; + // create all specific columns + DataColumn Hex = this.Columns.Add("Hex", typeof(string)); + DataColumn Call = this.Columns.Add("Call", typeof(string)); + DataColumn Reg = this.Columns.Add("Reg", typeof(string)); + DataColumn TypeCode = this.Columns.Add("TypeCode", typeof(string)); + DataColumn LastUpdated = this.Columns.Add("LastUpdated", typeof(string)); + // create primary key + DataColumn[] keys = new DataColumn[1]; + keys[0] = Hex; + this.PrimaryKey = keys; + } + + public DataTableAircrafts(List ads) + : this() + { + foreach (AircraftDesignator ad in ads) + { + DataRow row = this.NewRow(); + row[0] = ad.Hex; + row[1] = ad.Call; + row[2] = ad.Reg; + row[3] = ad.TypeCode; + row[4] = ad.LastUpdated; + this.Rows.Add(row); + } + } + + } +} diff --git a/AirScout.Aircrafts/AircraftDatabase.cs b/AirScout.Aircrafts/AircraftDatabase.cs new file mode 100644 index 0000000..023b497 --- /dev/null +++ b/AirScout.Aircrafts/AircraftDatabase.cs @@ -0,0 +1,2690 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.IO; +using System.Globalization; +using System.Reflection; +using System.Data; +using System.Diagnostics; +using ScoutBase.Core; +using ScoutBase.Propagation; +using System.Data.SQLite; +using Newtonsoft.Json; +using System.Windows.Forms; +using System.ComponentModel; + +namespace AirScout.Aircrafts +{ + + public class AircraftData + { + static AircraftDatabase aircrafts = new AircraftDatabase(); + public static AircraftDatabase Database + { + get + { + return aircrafts; + } + } + + } + + /// + /// Holds the Aircraft information in a database structure. + /// + public class AircraftDatabase + { + System.Data.SQLite.SQLiteDatabase db; + + private string DBPath; + + private static LogWriter Log = LogWriter.Instance; + + public readonly int UserVersion = 1; + + public long AircraftRegistrationMinLength { get; private set; } + public long AircraftRegistrationMaxLength { get; private set; } + + public long AircraftTypeIATAMinLength { get; private set; } + public long AircraftTypeIATAMaxLength { get; private set; } + + public long AircraftTypeICAOMinLength { get; private set; } + public long AircraftTypeICAOMaxLength { get; private set; } + + public AircraftDatabase() + { + db = OpenDatabase("aircrafts.db3"); + // set auto vacuum mode to "Full" to allow database to reduce size on disk + // requires a vacuum command to change database layout + AUTOVACUUMMODE mode = db.GetAutoVacuum(); + if (mode != AUTOVACUUMMODE.FULL) + { + if (MessageBox.Show("A major database layout change is necessary to run this version of AirScout. Older versions of AirScout are not compatible anymore and will cause errors. \n\nPress >OK< to start upgrade now (this will take some minutes). \nPress >Cancel< to leave.", "Database Upgrade of " + Path.GetFileName(db.DBLocation), MessageBoxButtons.OKCancel) == DialogResult.Cancel) + Environment.Exit(-1); // exit immediately + db.SetAutoVacuum(AUTOVACUUMMODE.FULL); + } + // create tables with schemas if not exist + if (!AircraftTableExists()) + AircraftCreateTable(); + if (!AircraftTypeTableExists()) + AircraftTypeCreateTable(); + if (!AirlineTableExists()) + AirlineCreateTable(); + if (!AirportTableExists()) + AirportCreateTable(); + if (!AircraftRegistrationTableExists()) + AircraftRegistrationCreateTable(); + // create views + PlaneInfoCreateView(); + + // get max/min lengths for AircraftRegistrations + // due to performance reasons the maintenance is only performed at startup and during insertion (without database query) + AircraftRegistrationMaxLength = AircraftRegistrationGetMaxLength(); + AircraftRegistrationMinLength = AircraftRegistrationGetMinLength(); + AircraftTypeIATAMaxLength = AircraftTypeIATAGetMaxLength(); + AircraftTypeIATAMinLength = AircraftTypeIATAGetMinLength(); + AircraftTypeICAOMaxLength = AircraftTypeICAOGetMaxLength(); + AircraftTypeICAOMinLength = AircraftTypeICAOGetMinLength(); + } + + ~AircraftDatabase() + { + CloseDatabase(db); + } + + private System.Data.SQLite.SQLiteDatabase OpenDatabase(string name) + { + System.Data.SQLite.SQLiteDatabase db = null; + try + { + // check if database path exists --> create if not + if (!Directory.Exists(DefaultDatabaseDirectory())) + Directory.CreateDirectory(DefaultDatabaseDirectory()); + // check if database is already on disk + DBPath = DefaultDatabaseDirectory(); + if (!File.Exists(Path.Combine(DBPath, name))) + { + // create one on disk + System.Data.SQLite.SQLiteDatabase dbn = new System.Data.SQLite.SQLiteDatabase(Path.Combine(DBPath, name)); + // open database + dbn.Open(); + // set user version + dbn.SetUserVerion(UserVersion); + // set auto vacuum mode to full + dbn.SetAutoVacuum(AUTOVACUUMMODE.FULL); + dbn.Close(); + } + // check for in-memory database --> open from disk, if not + if (Properties.Settings.Default.Database_InMemory) + db = System.Data.SQLite.SQLiteDatabase.CreateInMemoryDB(Path.Combine(DBPath, name)); + else + { + db = new System.Data.SQLite.SQLiteDatabase(Path.Combine(DBPath, name)); + db.Open(); + } + // get version info + int v = db.GetUserVersion(); + // do upgrade stuff here + } + catch (Exception ex) + { + Console.WriteLine("Error initilalizing database: " + ex.Message); + throw new TypeInitializationException(this.GetType().Name, ex); + } + return db; + } + + private void CloseDatabase(System.Data.SQLite.SQLiteDatabase db) + { + if (db == null) + return; + // save in-memory database to disk + if (db.IsInMemory) + db.BackupDatabase(db.DBLocation); + // else + // db.Close(); + } + + public void BackupDatabase() + { + if (db == null) + return; + // save in-memory database to disk + if (db.IsInMemory) + db.BackupDatabase(db.DiskFileName); + else + db.Close(); + } + + public bool IsInMemory() + { + return db.IsInMemory; + } + + public string DefaultDatabaseDirectory() + { + // create default database directory name + string dir = Properties.Settings.Default.Database_Directory; + if (!String.IsNullOrEmpty(dir)) + { + return dir; + } + // empty settings -_> create standard path + // collect entry assembly info + Assembly ass = Assembly.GetExecutingAssembly(); + string company = ""; + string product = ""; + object[] attribs; + attribs = ass.GetCustomAttributes(typeof(AssemblyCompanyAttribute), true); + if (attribs.Length > 0) + { + company = ((AssemblyCompanyAttribute)attribs[0]).Company; + } + attribs = ass.GetCustomAttributes(typeof(AssemblyProductAttribute), true); + if (attribs.Length > 0) + { + product = ((AssemblyProductAttribute)attribs[0]).Product; + } + // create database path + dir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + if (!String.IsNullOrEmpty(company)) + dir = Path.Combine(dir, company); + if (!String.IsNullOrEmpty(product)) + dir = Path.Combine(dir, product); + return Path.Combine(dir, "AircraftData"); + } + + public DATABASESTATUS GetDBStatus() + { + if (db != null) + return db.Status; + return DATABASESTATUS.UNDEFINED; + } + + public void SetDBStatus(DATABASESTATUS status) + { + if (db != null) + db.Status = status; + } + + public bool GetDBStatusBit(DATABASESTATUS statusbit) + { + if (db != null) + return (((int)db.Status) & ((int)statusbit)) > 0; + return false; + } + + public void SetDBStatusBit(DATABASESTATUS statusbit) + { + if (db != null) + db.Status |= statusbit; + } + + public void ResetDBStatusBit(DATABASESTATUS statusbit) + { + if (db != null) + db.Status &= ~statusbit; + } + + public void BeginTransaction() + { + if (db == null) + return; + db.BeginTransaction(); + } + + public void Commit() + { + if (db == null) + return; + db.Commit(); + } + + private DataTable Select(System.Data.SQLite.SQLiteDatabase db, string sql) + { + return db.Select(sql); + } + + public string GetDBLocation() + { + if (db == null) + return ""; + return db.DBLocation; + } + + public double GetDBSize() + { + if (db == null) + return 0; + return db.DBSize; + } + + private bool IsValid(object obj) + { + if (obj == null) + return false; + if (obj.GetType() == typeof(DBNull)) + return false; + return true; + } + + #region Aircrafts + + public bool AircraftTableExists(string tablename = "") + { + // check for table name is null or empty --> use default tablename from type instead + string tn = tablename; + if (String.IsNullOrEmpty(tn)) + tn = AircraftDesignator.TableName; + return db.TableExists(tn); + } + + public void AircraftCreateTable(string tablename = "") + { + lock (db.DBCommand) + { + // check for table name is null or empty --> use default tablename from type instead + string tn = tablename; + if (String.IsNullOrEmpty(tn)) + tn = AircraftDesignator.TableName; + db.DBCommand.CommandText = "CREATE TABLE `" + tn + "`(Hex TEXT UNIQUE NOT NULL DEFAULT '', Call TEXT NOT NULL DEFAULT '', Reg TEXT NOT NULL DEFAULT '', TypeCode TEXT, LastUpdated INT32, PRIMARY KEY (Hex))"; + db.DBCommand.Parameters.Clear(); + db.Execute(db.DBCommand); + // create table indices + db.DBCommand.CommandText = "CREATE INDEX idx_" + tn + "_Reg ON `" + tn + "` (Reg)"; + db.DBCommand.Parameters.Clear(); + db.Execute(db.DBCommand); + // create table indices + db.DBCommand.CommandText = "CREATE INDEX idx_" + tn + "_Call ON `" + tn + "` (Call)"; + db.DBCommand.Parameters.Clear(); + db.Execute(db.DBCommand); + } + } + + public long AircraftCount() + { + object count = db.ExecuteScalar("SELECT COUNT(*) FROM " + AircraftDesignator.TableName); + if (IsValid(count)) + return (long)count; + return 0; + } + + public long AircraftCountUnknownCall() + { + object count = db.ExecuteScalar("SELECT COUNT(*) FROM " + AircraftDesignator.TableName + " WHERE Call = '[unknown]'"); + if (IsValid(count)) + return (long)count; + return 0; + } + + public long AircraftCountUnknownHex() + { + object count = db.ExecuteScalar("SELECT COUNT(*) FROM " + AircraftDesignator.TableName + " WHERE Hex = '[unknown]'"); + if (IsValid(count)) + return (long)count; + return 0; + } + + public long AircraftCountUnknownType() + { + object count = db.ExecuteScalar("SELECT COUNT(*) FROM " + AircraftDesignator.TableName + " WHERE TypeCode = '[unknown]'"); + if (IsValid(count)) + return (long)count; + return 0; + } + + public bool AircraftExists(string hex) + { + AircraftDesignator ad = new AircraftDesignator(hex); + return AircraftExists(ad); + } + + public bool AircraftExists(AircraftDesignator ad) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT EXISTS (SELECT LastUpdated FROM " + AircraftDesignator.TableName + " WHERE Hex = @Hex"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(ad.AsString("Hex")); + object result = db.DBCommand.ExecuteScalar(); + if (IsValid(result) && ((long)result > 0)) + return true; + } + return false; + } + + public AircraftDesignator AircraftFindByHex(string hex) + { + AircraftDesignator ad = new AircraftDesignator(hex); + return AircraftFind(ad); + } + + public AircraftDesignator AircraftFindByReg(string reg) + { + // returs entry by search string, latest entry if more than one entry found + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT * FROM " + AircraftDesignator.TableName + " WHERE Reg = '" + reg + "' ORDER BY Lastupdated DESC LIMIT 1"; + db.DBCommand.Parameters.Clear(); + try + { + DataTable Result = db.Select(db.DBCommand); + if ((Result != null) && (Result.Rows.Count > 0)) + { + return new AircraftDesignator(Result.Rows[0]); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + return null; + } + + public AircraftDesignator AircraftFindByCall(string call) + { + // returs entry by search string, latest entry if more than one entry found + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT * FROM " + AircraftDesignator.TableName + " WHERE Call = '" + call + "' ORDER BY Lastupdated DESC LIMIT 1"; + db.DBCommand.Parameters.Clear(); + try + { + DataTable Result = db.Select(db.DBCommand); + if ((Result != null) && (Result.Rows.Count > 0)) + { + return new AircraftDesignator(Result.Rows[0]); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + return null; + } + + public AircraftDesignator AircraftFind(AircraftDesignator ad) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT * FROM " + AircraftDesignator.TableName + " WHERE Hex = @Hex"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(ad.AsString("Hex")); + try + { + DataTable Result = db.Select(db.DBCommand); + if ((Result != null) && (Result.Rows.Count > 0)) + { + return new AircraftDesignator(Result.Rows[0]); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + return null; + } + + public AircraftDesignator AircraftFindAt(long index) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT * FROM " + AircraftDesignator.TableName + " LIMIT 1 OFFSET " + index.ToString(); + db.DBCommand.Parameters.Clear(); + try + { + DataTable Result = db.Select(db.DBCommand); + if ((Result != null) && (Result.Rows.Count > 0)) + { + return new AircraftDesignator(Result.Rows[0]); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + return null; + } + + public DateTime AircraftFindlastUpdated(string hex) + { + AircraftDesignator ad = new AircraftDesignator(hex); + return AircraftFindLastUpdated(ad); + } + + public DateTime AircraftFindLastUpdated(AircraftDesignator ad) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT LastUpdated FROM " + AircraftDesignator.TableName + " WHERE Hex = @Hex"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(ad.AsString("Hex")); + object result = db.ExecuteScalar(db.DBCommand); + if (IsValid(result)) + return (SQLiteEntry.UNIXTimeToDateTime((int)result)); + } + return DateTime.MinValue; + } + + public int AircraftInsert(AircraftDesignator ad) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "INSERT INTO " + AircraftDesignator.TableName + " (Hex, Call, Reg, TypeCode, LastUpdated) VALUES (@Hex, @Call, @Reg, @TypeCode, @LastUpdated)"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(ad.AsString("Hex")); + db.DBCommand.Parameters.Add(ad.AsString("Call")); + db.DBCommand.Parameters.Add(ad.AsString("Reg")); + db.DBCommand.Parameters.Add(ad.AsString("TypeCode")); + db.DBCommand.Parameters.Add(ad.AsUNIXTime("LastUpdated")); + return db.ExecuteNonQuery(db.DBCommand); + } + } + + public int AircraftDelete(string hex) + { + AircraftDesignator ad = new AircraftDesignator(hex); + return AircraftDelete(ad); + } + + public int AircraftDelete(AircraftDesignator ad) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "DELETE FROM " + AircraftDesignator.TableName + " WHERE Hex = @Hex"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(ad.AsString("Hex")); + return db.ExecuteNonQuery(db.DBCommand); + } + } + + public int AircraftDeleteAll() + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "DELETE FROM " + AircraftDesignator.TableName; + db.DBCommand.Parameters.Clear(); + return db.ExecuteNonQuery(db.DBCommand); + } + } + + public int AircraftUpdate(AircraftDesignator ad) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "UPDATE " + AircraftDesignator.TableName + " SET Hex = @Hex, Call = @Call, Reg = @Reg, TypeCode = @TypeCode, LastUpdated = @LastUpdated WHERE Hex = @Hex"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(ad.AsString("Hex")); + db.DBCommand.Parameters.Add(ad.AsString("Call")); + db.DBCommand.Parameters.Add(ad.AsString("Reg")); + db.DBCommand.Parameters.Add(ad.AsString("TypeCode")); + db.DBCommand.Parameters.Add(ad.AsUNIXTime("LastUpdated")); + return db.ExecuteNonQuery(db.DBCommand); + } + } + + public int AircraftBulkInsert(List ads) + { + int errors = 0; + try + { + lock (db) + { + db.BeginTransaction(); + foreach (AircraftDesignator ad in ads) + { + try + { + AircraftInsert(ad); + } + catch (Exception ex) + { + Log.WriteMessage("Error inserting aircraft [" + ad.Hex + "]: " + ex.ToString()); + errors++; + } + } + db.Commit(); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + return -errors; + } + + public int AircraftBulkDelete(List ads) + { + int errors = 0; + try + { + lock (db) + { + db.BeginTransaction(); + foreach (AircraftDesignator ad in ads) + { + try + { + AircraftDelete(ad); + } + catch (Exception ex) + { + Log.WriteMessage("Error deleting aircraft [" + ad.Hex + "]: " + ex.ToString()); + errors++; + } + } + db.Commit(); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + return -errors; + } + + public int AircraftBulkInsertOrUpdateIfNewer(List ads) + { + if (ads == null) + return 0; + int i = 0; + try + { + lock (db) + { + db.BeginTransaction(); + foreach (AircraftDesignator ad in ads) + { + try + { + AircraftInsertOrUpdateIfNewer(ad); + i++; + } + catch (Exception ex) + { + + } + } + db.Commit(); + } + } + catch(Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + return i; + } + + public int AircraftInsertOrUpdateIfNewer(AircraftDesignator ad) + { + DateTime dt = AircraftFindLastUpdated(ad); + if (dt == DateTime.MinValue) + return AircraftInsert(ad); + if (dt < ad.LastUpdated) + return AircraftUpdate(ad); + return 0; + } + + public List AircraftGetAll() + { + List l = new List(); + DataTable Result = db.Select("SELECT * FROM " + AircraftDesignator.TableName); + if (!IsValid(Result) || (Result.Rows.Count == 0)) + return l; + foreach (DataRow row in Result.Rows) + l.Add(new AircraftDesignator(row)); + return l; + + } + + public List AircraftGetAll(BackgroundWorker caller) + { + // gets all aircrafts from database + // supports abort calculation if called from background worker and cancellation requested + List l = new List(); + int i = 0; + SQLiteCommand cmd = new SQLiteCommand(db.DBConnection); + cmd.CommandText = "SELECT * FROM " + AircraftDesignator.TableName; + SQLiteDataReader reader = cmd.ExecuteReader(); + while (reader.Read()) + { + AircraftDesignator ap = new AircraftDesignator((IDataRecord)reader); + l.Add(ap); + i++; + // abort calculation if called from background worker and cancellation pending + if (caller != null) + { + if (caller.WorkerSupportsCancellation && caller.CancellationPending) + return new List(); + if (caller.WorkerReportsProgress && (i % 1000 == 0)) + caller.ReportProgress(0, "Getting aircraft " + i.ToString() + " of"); + } + } + reader.Close(); + return l; + } + + public List AircraftFromJSON(string json) + { + if (String.IsNullOrEmpty(json)) + return null; + JsonSerializerSettings settings = new JsonSerializerSettings(); + settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; + settings.FloatFormatHandling = FloatFormatHandling.String; + settings.Formatting = Newtonsoft.Json.Formatting.Indented; + return JsonConvert.DeserializeObject>(json, settings); + } + + public string AircraftToJSON() + { + List l = AircraftGetAll(); + JsonSerializerSettings settings = new JsonSerializerSettings(); + settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; + settings.FloatFormatHandling = FloatFormatHandling.String; + settings.Formatting = Newtonsoft.Json.Formatting.Indented; + string json = JsonConvert.SerializeObject(l, settings); + return json; + } + + public DataTableAircrafts AircraftToDataTable() + { + List ads = AircraftGetAll(); + DataTableAircrafts dtl = new DataTableAircrafts(ads); + return dtl; + } + + #endregion + + #region AircraftTypes + + public bool AircraftTypeTableExists(string tablename = "") + { + // check for table name is null or empty --> use default tablename from type instead + string tn = tablename; + if (String.IsNullOrEmpty(tn)) + tn = AircraftTypeDesignator.TableName; + return db.TableExists(tn); + } + + public void AircraftTypeCreateTable(string tablename = "") + { + lock (db.DBCommand) + { + // check for table name is null or empty --> use default tablename from type instead + string tn = tablename; + if (String.IsNullOrEmpty(tn)) + tn = AircraftTypeDesignator.TableName; + db.DBCommand.CommandText = "CREATE TABLE `" + tn + "`(ICAO TEXT NOT NULL DEFAULT '', IATA TEXT NOT NULL DEFAULT '', Manufacturer TEXT, Model TEXT, Category INT32, LastUpdated INT32, PRIMARY KEY (ICAO, IATA))"; + db.DBCommand.Parameters.Clear(); + db.Execute(db.DBCommand); + // create table indices + db.DBCommand.CommandText = "CREATE INDEX idx_" + tn + "_ICAO ON `" + tn + "` (ICAO)"; + db.DBCommand.Parameters.Clear(); + db.Execute(db.DBCommand); + db.DBCommand.CommandText = "CREATE INDEX idx_" + tn + "_IATA ON `" + tn + "` (IATA)"; + db.DBCommand.Parameters.Clear(); + db.Execute(db.DBCommand); + } + } + + public long AircraftTypeCount() + { + object count = db.ExecuteScalar("SELECT COUNT(*) FROM " + AircraftTypeDesignator.TableName); + if (IsValid(count)) + return (long)count; + return 0; + } + + public bool AircraftTypeExists(string icao, string iata) + { + AircraftTypeDesignator td = new AircraftTypeDesignator(icao, iata); + return AircraftTypeExists(td); + } + + public bool AircraftTypeExists(AircraftTypeDesignator td) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT EXISTS (SELECT LastUpdated FROM " + AircraftTypeDesignator.TableName + " WHERE ICAO = @ICAO AND IATA = @IATA"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(td.AsString("ICAO")); + db.DBCommand.Parameters.Add(td.AsString("IATA")); + object result = db.DBCommand.ExecuteScalar(); + if (IsValid(result) && ((long)result > 0)) + return true; + } + return false; + } + + private long AircraftTypeIATAGetMaxLength() + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT max(length(IATA)) FROM " + AircraftTypeDesignator.TableName; + db.DBCommand.Parameters.Clear(); + object result = db.DBCommand.ExecuteScalar(); + if (IsValid(result)) + return (long)result; + } + return 0; + } + + private long AircraftTypeIATAGetMinLength() + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT min(length(IATA)) FROM " + AircraftTypeDesignator.TableName; + db.DBCommand.Parameters.Clear(); + object result = db.DBCommand.ExecuteScalar(); + if (IsValid(result)) + return (long)result; + } + return 0; + } + + private long AircraftTypeICAOGetMaxLength() + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT max(length(ICAO)) FROM " + AircraftTypeDesignator.TableName; + db.DBCommand.Parameters.Clear(); + object result = db.DBCommand.ExecuteScalar(); + if (IsValid(result)) + return (long)result; + } + return 0; + } + + private long AircraftTypeICAOGetMinLength() + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT min(length(ICAO)) FROM " + AircraftTypeDesignator.TableName; + db.DBCommand.Parameters.Clear(); + object result = db.DBCommand.ExecuteScalar(); + if (IsValid(result)) + return (long)result; + } + return 0; + } + + + public AircraftTypeDesignator AircraftTypeFindByIATA(string iata) + { + // returs entry by search string, latest entry if more than one entry found + if (String.IsNullOrEmpty(iata)) + return null; + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT * FROM " + AircraftTypeDesignator.TableName + " WHERE IATA = '" + iata + "' ORDER BY Lastupdated DESC LIMIT 1"; + db.DBCommand.Parameters.Clear(); + try + { + DataTable Result = db.Select(db.DBCommand); + if ((Result != null) && (Result.Rows.Count > 0)) + { + return new AircraftTypeDesignator(Result.Rows[0]); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + return null; + } + + public AircraftTypeDesignator AircraftTypeFindByICAO(string icao) + { + // returs entry by search string, latest entry if more than one entry found + if (String.IsNullOrEmpty(icao)) + return null; + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT * FROM " + AircraftTypeDesignator.TableName + " WHERE ICAO = '" + icao + "' ORDER BY Lastupdated DESC LIMIT 1"; + db.DBCommand.Parameters.Clear(); + try + { + DataTable Result = db.Select(db.DBCommand); + if ((Result != null) && (Result.Rows.Count > 0)) + { + return new AircraftTypeDesignator(Result.Rows[0]); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + return null; + } + + public AircraftTypeDesignator AircraftTypeFind(string icao, string iata) + { + AircraftTypeDesignator td = new AircraftTypeDesignator(icao, iata); + return AircraftTypeFind(td); + } + + public AircraftTypeDesignator AircraftTypeFind(AircraftTypeDesignator td) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT * FROM " + AircraftTypeDesignator.TableName + " WHERE ICAO = @ICAO AND IATA = @IATA"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(td.AsString("ICAO")); + db.DBCommand.Parameters.Add(td.AsString("IATA")); + try + { + DataTable Result = db.Select(db.DBCommand); + if ((Result != null) && (Result.Rows.Count > 0)) + { + return new AircraftTypeDesignator(Result.Rows[0]); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + return null; + } + + public AircraftTypeDesignator AircraftTypeFindAt(long index) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT * FROM " + AircraftTypeDesignator.TableName + " LIMIT 1 OFFSET " + index.ToString(); + db.DBCommand.Parameters.Clear(); + try + { + DataTable Result = db.Select(db.DBCommand); + if ((Result != null) && (Result.Rows.Count > 0)) + { + return new AircraftTypeDesignator(Result.Rows[0]); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + return null; + } + + public DateTime AircraftTypeFindlastUpdated(string icao, string iata) + { + AircraftTypeDesignator td = new AircraftTypeDesignator(icao, iata); + return AircraftTypeFindLastUpdated(td); + } + + public DateTime AircraftTypeFindLastUpdated(AircraftTypeDesignator td) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT LastUpdated FROM " + AircraftTypeDesignator.TableName + " WHERE ICAO = @ICAO AND IATA = @IATA"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(td.AsString("ICAO")); + db.DBCommand.Parameters.Add(td.AsString("IATA")); + object result = db.ExecuteScalar(db.DBCommand); + if (IsValid(result)) + return (SQLiteEntry.UNIXTimeToDateTime((int)result)); + } + return DateTime.MinValue; + } + + public int AircraftTypeInsert(AircraftTypeDesignator td) + { + // maintain min/max values + if (td.IATA.Length < AircraftTypeIATAMinLength) + AircraftTypeIATAMinLength = td.IATA.Length; + if (td.IATA.Length > AircraftTypeIATAMaxLength) + AircraftTypeIATAMaxLength = td.IATA.Length; + if (td.ICAO.Length < AircraftTypeICAOMinLength) + AircraftTypeICAOMinLength = td.ICAO.Length; + if (td.ICAO.Length > AircraftTypeICAOMaxLength) + AircraftTypeICAOMaxLength = td.ICAO.Length; + + lock (db.DBCommand) + { + db.DBCommand.CommandText = "INSERT INTO " + AircraftTypeDesignator.TableName + " (ICAO, IATA, Manufacturer, Model, Category, LastUpdated) VALUES (@ICAO, @IATA, @Manufacturer, @Model, @Category, @LastUpdated)"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(td.AsString("ICAO")); + db.DBCommand.Parameters.Add(td.AsString("IATA")); + db.DBCommand.Parameters.Add(td.AsString("Manufacturer")); + db.DBCommand.Parameters.Add(td.AsString("Model")); + db.DBCommand.Parameters.Add(td.AsInt32("Category")); + db.DBCommand.Parameters.Add(td.AsUNIXTime("LastUpdated")); + return db.ExecuteNonQuery(db.DBCommand); + } + } + + public int AircraftTypeDelete(string icao, string iata) + { + AircraftTypeDesignator td = new AircraftTypeDesignator(iata, icao); + return AircraftTypeDelete(td); + } + + public int AircraftTypeDelete(AircraftTypeDesignator td) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "DELETE FROM " + AircraftTypeDesignator.TableName + " WHERE ICAO = @ICAO AND IATA = @IATA"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(td.AsString("ICAO")); + db.DBCommand.Parameters.Add(td.AsString("IATA")); + return db.ExecuteNonQuery(db.DBCommand); + } + } + + public int AircraftTypeDeleteAll() + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "DELETE FROM " + AircraftTypeDesignator.TableName; + db.DBCommand.Parameters.Clear(); + return db.ExecuteNonQuery(db.DBCommand); + } + } + + public int AircraftTypeUpdate(AircraftTypeDesignator ad) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "UPDATE " + AircraftTypeDesignator.TableName + " SET ICAO = @ICAO, IATA = @IATA, Manufacturer = @Manufacturer, Model = @Model, Category = @Category, LastUpdated = @LastUpdated WHERE ICAO = @ICAO AND IATA = @IATA"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(ad.AsString("ICAO")); + db.DBCommand.Parameters.Add(ad.AsString("IATA")); + db.DBCommand.Parameters.Add(ad.AsString("Manufacturer")); + db.DBCommand.Parameters.Add(ad.AsString("Model")); + db.DBCommand.Parameters.Add(ad.AsInt32("Category")); + db.DBCommand.Parameters.Add(ad.AsUNIXTime("LastUpdated")); + return db.ExecuteNonQuery(db.DBCommand); + } + } + + public int AircraftTypeBulkInsert(List tds) + { + int errors = 0; + try + { + lock (db) + { + db.BeginTransaction(); + foreach (AircraftTypeDesignator td in tds) + { + try + { + AircraftTypeInsert(td); + } + catch (Exception ex) + { + Log.WriteMessage("Error inserting AircraftType [" + td.ICAO + ", " + td.IATA + "]: " + ex.ToString()); + errors++; + } + } + db.Commit(); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + return -errors; + } + + public int AircraftTypeBulkDelete(List ads) + { + int errors = 0; + try + { + lock (db) + { + db.BeginTransaction(); + foreach (AircraftTypeDesignator td in ads) + { + try + { + AircraftTypeDelete(td); + } + catch (Exception ex) + { + Log.WriteMessage("Error inserting AircraftType [" + td.ICAO + ", " + td.IATA + "]: " + ex.ToString()); + errors++; + } + } + db.Commit(); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + return -errors; + } + + public int AircraftTypeBulkInsertOrUpdateIfNewer(List ads) + { + if (ads == null) + return 0; + int i = 0; + lock (db) + { + db.BeginTransaction(); + foreach (AircraftTypeDesignator ad in ads) + { + AircraftTypeInsertOrUpdateIfNewer(ad); + i++; + } + db.Commit(); + } + return i; + } + + public int AircraftTypeInsertOrUpdateIfNewer(AircraftTypeDesignator ad) + { + DateTime dt = AircraftTypeFindLastUpdated(ad); + if (dt == DateTime.MinValue) + return AircraftTypeInsert(ad); + if (dt < ad.LastUpdated) + return AircraftTypeUpdate(ad); + return 0; + } + + public List AircraftTypeGetAll() + { + List l = new List(); + DataTable Result = db.Select("SELECT * FROM " + AircraftTypeDesignator.TableName); + if (!IsValid(Result) || (Result.Rows.Count == 0)) + return l; + foreach (DataRow row in Result.Rows) + l.Add(new AircraftTypeDesignator(row)); + return l; + + } + + public List AircraftTypeGetAll(BackgroundWorker caller) + { + // gets all Aircraft types from database + // supports abort calculation if called from background worker and cancellation requested + List l = new List(); + int i = 0; + SQLiteCommand cmd = new SQLiteCommand(db.DBConnection); + cmd.CommandText = "SELECT * FROM " + AircraftTypeDesignator.TableName; + SQLiteDataReader reader = cmd.ExecuteReader(); + while (reader.Read()) + { + AircraftTypeDesignator ap = new AircraftTypeDesignator((IDataRecord)reader); + l.Add(ap); + i++; + // abort calculation if called from background worker and cancellation pending + if (caller != null) + { + if (caller.WorkerSupportsCancellation && caller.CancellationPending) + return new List(); + if (caller.WorkerReportsProgress && (i % 1000 == 0)) + caller.ReportProgress(0, "Getting Aircraft type " + i.ToString() + " of"); + } + } + reader.Close(); + return l; + } + + public List AircraftTypeFromJSON(string json) + { + if (String.IsNullOrEmpty(json)) + return null; + JsonSerializerSettings settings = new JsonSerializerSettings(); + settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; + settings.FloatFormatHandling = FloatFormatHandling.String; + settings.Formatting = Newtonsoft.Json.Formatting.Indented; + return JsonConvert.DeserializeObject>(json, settings); + } + + public string AircraftTypeToJSON() + { + List l = AircraftTypeGetAll(); + JsonSerializerSettings settings = new JsonSerializerSettings(); + settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; + settings.FloatFormatHandling = FloatFormatHandling.String; + settings.Formatting = Newtonsoft.Json.Formatting.Indented; + string json = JsonConvert.SerializeObject(l, settings); + return json; + } + + public DataTableAircraftTypes AircraftTypeToDataTable() + { + List ads = AircraftTypeGetAll(); + DataTableAircraftTypes dtl = new DataTableAircraftTypes(ads); + return dtl; + } + + #endregion + + #region Airlines + + public bool AirlineTableExists(string tablename = "") + { + // check for table name is null or empty --> use default tablename from type instead + string tn = tablename; + if (String.IsNullOrEmpty(tn)) + tn = AirlineDesignator.TableName; + return db.TableExists(tn); + } + + public void AirlineCreateTable(string tablename = "") + { + lock (db.DBCommand) + { + // check for table name is null or empty --> use default tablename from type instead + string tn = tablename; + if (String.IsNullOrEmpty(tn)) + tn = AirlineDesignator.TableName; + db.DBCommand.CommandText = "CREATE TABLE `" + tn + "`(ICAO TEXT NOT NULL DEFAULT '', IATA TEXT NOT NULL DEFAULT '', Airline TEXT, Country TEXT, LastUpdated INT32, PRIMARY KEY (ICAO, IATA))"; + db.DBCommand.Parameters.Clear(); + db.Execute(db.DBCommand); + // create table indices + db.DBCommand.CommandText = "CREATE INDEX idx_" + tn + "_ICAO ON `" + tn + "` (ICAO)"; + db.DBCommand.Parameters.Clear(); + db.Execute(db.DBCommand); + db.DBCommand.CommandText = "CREATE INDEX idx_" + tn + "_IATA ON `" + tn + "` (IATA)"; + db.DBCommand.Parameters.Clear(); + db.Execute(db.DBCommand); + } + } + + public long AirlineCount() + { + object count = db.ExecuteScalar("SELECT COUNT(*) FROM " + AirlineDesignator.TableName); + if (IsValid(count)) + return (long)count; + return 0; + } + + public bool AirlineExists(string icao, string iata) + { + AirlineDesignator ld = new AirlineDesignator(icao, iata); + return AirlineExists(ld); + } + + public bool AirlineExists(AirlineDesignator ld) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT EXISTS (SELECT LastUpdated FROM " + AirlineDesignator.TableName + " WHERE ICAO = @ICAO AND IATA = @IATA"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(ld.AsString("ICAO")); + db.DBCommand.Parameters.Add(ld.AsString("IATA")); + object result = db.DBCommand.ExecuteScalar(); + if (IsValid(result) && ((long)result > 0)) + return true; + } + return false; + } + + public AirlineDesignator AirlineFindByICAO(string icao) + { + // returs entry by search string, latest entry if more than one entry found + if (String.IsNullOrEmpty(icao)) + return null; + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT * FROM " + AirlineDesignator.TableName + " WHERE ICAO = '" + icao + "' ORDER BY LastUpdated DESC LIMIT 1"; + db.DBCommand.Parameters.Clear(); + try + { + DataTable Result = db.Select(db.DBCommand); + if ((Result != null) && (Result.Rows.Count > 0)) + { + return new AirlineDesignator(Result.Rows[0]); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + return null; + } + + public AirlineDesignator AirlineFindByIATA(string iata) + { + // returs entry by search string, latest entry if more than one entry found + if (String.IsNullOrEmpty(iata)) + return null; + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT * FROM " + AirlineDesignator.TableName + " WHERE IATA = '" + iata + "' ORDER BY LastUpdated DESC LIMIT 1"; + db.DBCommand.Parameters.Clear(); + try + { + DataTable Result = db.Select(db.DBCommand); + if ((Result != null) && (Result.Rows.Count > 0)) + { + return new AirlineDesignator(Result.Rows[0]); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + return null; + } + + public AirlineDesignator AirlineFind(string icao, string iata) + { + AirlineDesignator ld = new AirlineDesignator(icao, iata); + return AirlineFind(ld); + } + + public AirlineDesignator AirlineFind(AirlineDesignator ld) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT * FROM " + AirlineDesignator.TableName + " WHERE ICAO = @ICAO AND IATA = @IATA"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(ld.AsString("ICAO")); + db.DBCommand.Parameters.Add(ld.AsString("IATA")); + try + { + DataTable Result = db.Select(db.DBCommand); + if ((Result != null) && (Result.Rows.Count > 0)) + { + return new AirlineDesignator(Result.Rows[0]); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + return null; + } + + public AirlineDesignator AirlineFindAt(long index) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT * FROM " + AirlineDesignator.TableName + " LIMIT 1 OFFSET " + index.ToString(); + db.DBCommand.Parameters.Clear(); + try + { + DataTable Result = db.Select(db.DBCommand); + if ((Result != null) && (Result.Rows.Count > 0)) + { + return new AirlineDesignator(Result.Rows[0]); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + return null; + } + + public DateTime AirlineFindlastUpdated(string icao, string iata) + { + AirlineDesignator ld = new AirlineDesignator(icao, iata); + return AirlineFindLastUpdated(ld); + } + + public DateTime AirlineFindLastUpdated(AirlineDesignator ld) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT LastUpdated FROM " + AirlineDesignator.TableName + " WHERE ICAO = @ICAO AND IATA = @IATA"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(ld.AsString("ICAO")); + db.DBCommand.Parameters.Add(ld.AsString("IATA")); + object result = db.ExecuteScalar(db.DBCommand); + if (IsValid(result)) + return (SQLiteEntry.UNIXTimeToDateTime((int)result)); + } + return DateTime.MinValue; + } + + public int AirlineInsert(AirlineDesignator ld) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "INSERT INTO " + AirlineDesignator.TableName + " (ICAO, IATA, Airline, Country, LastUpdated) VALUES (@ICAO, @IATA, @Airline, @Country, @LastUpdated)"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(ld.AsString("ICAO")); + db.DBCommand.Parameters.Add(ld.AsString("IATA")); + db.DBCommand.Parameters.Add(ld.AsString("Airline")); + db.DBCommand.Parameters.Add(ld.AsString("Country")); + db.DBCommand.Parameters.Add(ld.AsUNIXTime("LastUpdated")); + return db.ExecuteNonQuery(db.DBCommand); + } + } + + public int AirlineDelete(string icao, string iata) + { + AirlineDesignator ld = new AirlineDesignator(iata, icao); + return AirlineDelete(ld); + } + + public int AirlineDelete(AirlineDesignator ld) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "DELETE FROM " + AirlineDesignator.TableName + " WHERE ICAO = @ICAO AND IATA = @IATA"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(ld.AsString("ICAO")); + db.DBCommand.Parameters.Add(ld.AsString("IATA")); + return db.ExecuteNonQuery(db.DBCommand); + } + } + + public int AirlineDeleteAll() + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "DELETE FROM " + AirlineDesignator.TableName; + db.DBCommand.Parameters.Clear(); + return db.ExecuteNonQuery(db.DBCommand); + } + } + + public int AirlineUpdate(AirlineDesignator ld) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "UPDATE " + AirlineDesignator.TableName + " SET ICAO = @ICAO, IATA = @IATA, Airline = @Airline, Country = @Country, LastUpdated = @LastUpdated WHERE ICAO = @ICAO AND IATA = @IATA"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(ld.AsString("ICAO")); + db.DBCommand.Parameters.Add(ld.AsString("IATA")); + db.DBCommand.Parameters.Add(ld.AsString("Airline")); + db.DBCommand.Parameters.Add(ld.AsString("Country")); + db.DBCommand.Parameters.Add(ld.AsUNIXTime("LastUpdated")); + return db.ExecuteNonQuery(db.DBCommand); + } + } + + public int AirlineBulkInsert(List lds) + { + int errors = 0; + try + { + lock (db) + { + db.BeginTransaction(); + foreach (AirlineDesignator ld in lds) + { + try + { + AirlineInsert(ld); + } + catch (Exception ex) + { + Log.WriteMessage("Error inserting Airline [" + ld.ICAO + ", " + ld.IATA + "]: " + ex.ToString()); + errors++; + } + } + db.Commit(); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + return -errors; + } + + public int AirlineBulkDelete(List lds) + { + int errors = 0; + try + { + lock (db) + { + db.BeginTransaction(); + foreach (AirlineDesignator ld in lds) + { + try + { + AirlineDelete(ld); + } + catch (Exception ex) + { + Log.WriteMessage("Error inserting Airline [" + ld.ICAO + ", " + ld.IATA + "]: " + ex.ToString()); + errors++; + } + } + db.Commit(); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + return -errors; + } + + public int AirlineBulkInsertOrUpdateIfNewer(List lds) + { + if (lds == null) + return 0; + int i = 0; + lock (db) + { + db.BeginTransaction(); + foreach (AirlineDesignator ld in lds) + { + AirlineInsertOrUpdateIfNewer(ld); + i++; + } + db.Commit(); + } + return i; + } + + public int AirlineInsertOrUpdateIfNewer(AirlineDesignator ld) + { + DateTime dt = AirlineFindLastUpdated(ld); + if (dt == DateTime.MinValue) + return AirlineInsert(ld); + if (dt < ld.LastUpdated) + return AirlineUpdate(ld); + return 0; + } + + public List AirlineGetAll() + { + List l = new List(); + DataTable Result = db.Select("SELECT * FROM " + AirlineDesignator.TableName); + if (!IsValid(Result) || (Result.Rows.Count == 0)) + return l; + foreach (DataRow row in Result.Rows) + l.Add(new AirlineDesignator(row)); + return l; + + } + + public List AirlineGetAll(BackgroundWorker caller) + { + // gets all Airlines from database + // supports abort calculation if called from background worker and cancellation requested + List l = new List(); + int i = 0; + SQLiteCommand cmd = new SQLiteCommand(db.DBConnection); + cmd.CommandText = "SELECT * FROM " + AirlineDesignator.TableName; + SQLiteDataReader reader = cmd.ExecuteReader(); + while (reader.Read()) + { + AirlineDesignator ap = new AirlineDesignator((IDataRecord)reader); + l.Add(ap); + i++; + // abort calculation if called from background worker and cancellation pending + if (caller != null) + { + if (caller.WorkerSupportsCancellation && caller.CancellationPending) + return new List(); + if (caller.WorkerReportsProgress && (i % 1000 == 0)) + caller.ReportProgress(0, "Getting Airline " + i.ToString() + " of"); + } + } + reader.Close(); + return l; + } + + public List AirlineFromJSON(string json) + { + if (String.IsNullOrEmpty(json)) + return null; + JsonSerializerSettings settings = new JsonSerializerSettings(); + settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; + settings.FloatFormatHandling = FloatFormatHandling.String; + settings.Formatting = Newtonsoft.Json.Formatting.Indented; + return JsonConvert.DeserializeObject>(json, settings); + } + + public string AirlineToJSON() + { + List l = AirlineGetAll(); + JsonSerializerSettings settings = new JsonSerializerSettings(); + settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; + settings.FloatFormatHandling = FloatFormatHandling.String; + settings.Formatting = Newtonsoft.Json.Formatting.Indented; + string json = JsonConvert.SerializeObject(l, settings); + return json; + } + + public DataTableAirlines AirlineToDataTable() + { + List lds = AirlineGetAll(); + DataTableAirlines dtl = new DataTableAirlines(lds); + return dtl; + } + + #endregion + + #region Airports + + public bool AirportTableExists(string tablename = "") + { + // check for table name is null or empty --> use default tablename from type instead + string tn = tablename; + if (String.IsNullOrEmpty(tn)) + tn = AirportDesignator.TableName; + return db.TableExists(tn); + } + + public void AirportCreateTable(string tablename = "") + { + lock (db.DBCommand) + { + // check for table name is null or empty --> use default tablename from type instead + string tn = tablename; + if (String.IsNullOrEmpty(tn)) + tn = AirportDesignator.TableName; + db.DBCommand.CommandText = "CREATE TABLE `" + tn + "`(ICAO TEXT NOT NULL DEFAULT '', IATA TEXT NOT NULL DEFAULT '', Lat DOUBLE, Lon DOUBLE, Alt DOUBLE, Airport TEXT, Country TEXT, LastUpdated INT32, PRIMARY KEY (ICAO, IATA))"; + db.DBCommand.Parameters.Clear(); + db.Execute(db.DBCommand); + // create table indices + db.DBCommand.CommandText = "CREATE INDEX idx_" + tn + "_ICAO ON `" + tn + "` (ICAO)"; + db.DBCommand.Parameters.Clear(); + db.Execute(db.DBCommand); + db.DBCommand.CommandText = "CREATE INDEX idx_" + tn + "_IATA ON `" + tn + "` (IATA)"; + db.DBCommand.Parameters.Clear(); + db.Execute(db.DBCommand); + } + } + + public long AirportCount() + { + object count = db.ExecuteScalar("SELECT COUNT(*) FROM " + AirportDesignator.TableName); + if (IsValid(count)) + return (long)count; + return 0; + } + + public bool AirportExists(string icao, string iata) + { + AirportDesignator pd = new AirportDesignator(icao, iata); + return AirportExists(pd); + } + + public bool AirportExists(AirportDesignator pd) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT EXISTS (SELECT LastUpdated FROM " + AirportDesignator.TableName + " WHERE ICAO = @ICAO AND IATA = @IATA"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(pd.AsString("ICAO")); + db.DBCommand.Parameters.Add(pd.AsString("IATA")); + object result = db.DBCommand.ExecuteScalar(); + if (IsValid(result) && ((long)result > 0)) + return true; + } + return false; + } + + public AirportDesignator AirportFindByICAO(string icao) + { + // returs entry by search string, latest entry if more than one entry found + if (String.IsNullOrEmpty(icao)) + return null; + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT * FROM " + AirportDesignator.TableName + " WHERE ICAO = '" + icao + "' ORDER BY LastUpdated DESC LIMIT 1"; + db.DBCommand.Parameters.Clear(); + try + { + DataTable Result = db.Select(db.DBCommand); + if ((Result != null) && (Result.Rows.Count > 0)) + { + return new AirportDesignator(Result.Rows[0]); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + return null; + } + + public AirportDesignator AirportFindByIATA(string iata) + { + // returs entry by search string, latest entry if more than one entry found + if (String.IsNullOrEmpty(iata)) + return null; + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT * FROM " + AirportDesignator.TableName + " WHERE IATA = '" + iata + "' ORDER BY LastUpdated DESC LIMIT 1"; + db.DBCommand.Parameters.Clear(); + try + { + DataTable Result = db.Select(db.DBCommand); + if ((Result != null) && (Result.Rows.Count > 0)) + { + return new AirportDesignator(Result.Rows[0]); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + return null; + } + + public AirportDesignator AirportFind(string icao, string iata) + { + AirportDesignator pd = new AirportDesignator(icao, iata); + return AirportFind(pd); + } + + public AirportDesignator AirportFind(AirportDesignator pd) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT * FROM " + AirportDesignator.TableName + " WHERE ICAO = @ICAO AND IATA = @IATA"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(pd.AsString("ICAO")); + db.DBCommand.Parameters.Add(pd.AsString("IATA")); + try + { + DataTable Result = db.Select(db.DBCommand); + if ((Result != null) && (Result.Rows.Count > 0)) + { + return new AirportDesignator(Result.Rows[0]); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + return null; + } + + public AirportDesignator AirportFindAt(long index) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT * FROM " + AirportDesignator.TableName + " LIMIT 1 OFFSET " + index.ToString(); + db.DBCommand.Parameters.Clear(); + try + { + DataTable Result = db.Select(db.DBCommand); + if ((Result != null) && (Result.Rows.Count > 0)) + { + return new AirportDesignator(Result.Rows[0]); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + return null; + } + + public DateTime AirportFindlastUpdated(string icao, string iata) + { + AirportDesignator pd = new AirportDesignator(icao, iata); + return AirportFindLastUpdated(pd); + } + + public DateTime AirportFindLastUpdated(AirportDesignator pd) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT LastUpdated FROM " + AirportDesignator.TableName + " WHERE ICAO = @ICAO AND IATA = @IATA"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(pd.AsString("ICAO")); + db.DBCommand.Parameters.Add(pd.AsString("IATA")); + object result = db.ExecuteScalar(db.DBCommand); + if (IsValid(result)) + return (SQLiteEntry.UNIXTimeToDateTime((int)result)); + } + return DateTime.MinValue; + } + + public int AirportInsert(AirportDesignator pd) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "INSERT INTO " + AirportDesignator.TableName + " (ICAO, IATA, Lat, Lon, Alt, Airport, Country, LastUpdated) VALUES (@ICAO, @IATA, @Lat, @Lon, @Alt, @Airport, @Country, @LastUpdated)"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(pd.AsString("ICAO")); + db.DBCommand.Parameters.Add(pd.AsString("IATA")); + db.DBCommand.Parameters.Add(pd.AsDouble("Lat")); + db.DBCommand.Parameters.Add(pd.AsDouble("Lon")); + db.DBCommand.Parameters.Add(pd.AsDouble("Alt")); + db.DBCommand.Parameters.Add(pd.AsString("Airport")); + db.DBCommand.Parameters.Add(pd.AsString("Country")); + db.DBCommand.Parameters.Add(pd.AsUNIXTime("LastUpdated")); + return db.ExecuteNonQuery(db.DBCommand); + } + } + + public int AirportDelete(string icao, string iata) + { + AirportDesignator pd = new AirportDesignator(iata, icao); + return AirportDelete(pd); + } + + public int AirportDelete(AirportDesignator pd) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "DELETE FROM " + AirportDesignator.TableName + " WHERE ICAO = @ICAO AND IATA = @IATA"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(pd.AsString("ICAO")); + db.DBCommand.Parameters.Add(pd.AsString("IATA")); + return db.ExecuteNonQuery(db.DBCommand); + } + } + + public int AirportDeleteAll() + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "DELETE FROM " + AirportDesignator.TableName; + db.DBCommand.Parameters.Clear(); + return db.ExecuteNonQuery(db.DBCommand); + } + } + + public int AirportUpdate(AirportDesignator pd) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "UPDATE " + AirportDesignator.TableName + " SET ICAO = @ICAO, IATA = @IATA, Lat = @Lat, Lon = @Lon, Alt = @Alt, Airport = @Airport, Country = @Country, LastUpdated = @LastUpdated WHERE ICAO = @ICAO AND IATA = @IATA"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(pd.AsString("ICAO")); + db.DBCommand.Parameters.Add(pd.AsString("IATA")); + db.DBCommand.Parameters.Add(pd.AsDouble("Lat")); + db.DBCommand.Parameters.Add(pd.AsDouble("Lon")); + db.DBCommand.Parameters.Add(pd.AsDouble("Alt")); + db.DBCommand.Parameters.Add(pd.AsString("Airport")); + db.DBCommand.Parameters.Add(pd.AsString("Country")); + db.DBCommand.Parameters.Add(pd.AsUNIXTime("LastUpdated")); + return db.ExecuteNonQuery(db.DBCommand); + } + } + + public int AirportBulkInsert(List pds) + { + int errors = 0; + try + { + lock (db) + { + db.BeginTransaction(); + foreach (AirportDesignator pd in pds) + { + try + { + AirportInsert(pd); + } + catch (Exception ex) + { + Log.WriteMessage("Error inserting Airport [" + pd.ICAO + ", " + pd.IATA + "]: " + ex.ToString()); + errors++; + } + } + db.Commit(); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + return -errors; + } + + public int AirportBulkDelete(List pds) + { + int errors = 0; + try + { + lock (db) + { + db.BeginTransaction(); + foreach (AirportDesignator pd in pds) + { + try + { + AirportDelete(pd); + } + catch (Exception ex) + { + Log.WriteMessage("Error inserting Airport [" + pd.ICAO + ", " + pd.IATA + "]: " + ex.ToString()); + errors++; + } + } + db.Commit(); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + return -errors; + } + + public int AirportBulkInsertOrUpdateIfNewer(List pds) + { + if (pds == null) + return 0; + int i = 0; + lock (db) + { + db.BeginTransaction(); + foreach (AirportDesignator pd in pds) + { + AirportInsertOrUpdateIfNewer(pd); + i++; + } + db.Commit(); + } + return i; + } + + public int AirportInsertOrUpdateIfNewer(AirportDesignator pd) + { + DateTime dt = AirportFindLastUpdated(pd); + if (dt == DateTime.MinValue) + return AirportInsert(pd); + if (dt < pd.LastUpdated) + return AirportUpdate(pd); + return 0; + } + + public List AirportGetAll() + { + List l = new List(); + DataTable Result = db.Select("SELECT * FROM " + AirportDesignator.TableName); + if (!IsValid(Result) || (Result.Rows.Count == 0)) + return l; + foreach (DataRow row in Result.Rows) + l.Add(new AirportDesignator(row)); + return l; + + } + + public List AirportGetAll(BackgroundWorker caller) + { + // gets all Airports from database + // supports abort calculation if called from background worker and cancellation requested + List l = new List(); + int i = 0; + SQLiteCommand cmd = new SQLiteCommand(db.DBConnection); + cmd.CommandText = "SELECT * FROM " + AirportDesignator.TableName; + SQLiteDataReader reader = cmd.ExecuteReader(); + while (reader.Read()) + { + AirportDesignator ap = new AirportDesignator((IDataRecord)reader); + l.Add(ap); + i++; + // abort calculation if called from background worker and cancellation pending + if (caller != null) + { + if (caller.WorkerSupportsCancellation && caller.CancellationPending) + return new List(); + if (caller.WorkerSupportsCancellation && caller.WorkerReportsProgress && (i % 1000 == 0)) + caller.ReportProgress(0, "Getting Airport " + i.ToString() + " of"); + } + } + reader.Close(); + return l; + } + + public List AirportFromJSON(string json) + { + if (String.IsNullOrEmpty(json)) + return null; + JsonSerializerSettings settings = new JsonSerializerSettings(); + settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; + settings.FloatFormatHandling = FloatFormatHandling.String; + settings.Formatting = Newtonsoft.Json.Formatting.Indented; + return JsonConvert.DeserializeObject>(json, settings); + } + + public string AirportToJSON() + { + List l = AirportGetAll(); + JsonSerializerSettings settings = new JsonSerializerSettings(); + settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; + settings.FloatFormatHandling = FloatFormatHandling.String; + settings.Formatting = Newtonsoft.Json.Formatting.Indented; + string json = JsonConvert.SerializeObject(l, settings); + return json; + } + + public DataTableAirports AirportToDataTable() + { + List lds = AirportGetAll(); + DataTableAirports dtl = new DataTableAirports(lds); + return dtl; + } + + #endregion + + #region AircraftRegistrations + + public bool AircraftRegistrationTableExists(string tablename = "") + { + // check for table name is null or empty --> use default tablename from type instead + string tn = tablename; + if (String.IsNullOrEmpty(tn)) + tn = AircraftRegistrationDesignator.TableName; + return db.TableExists(tn); + } + + public void AircraftRegistrationCreateTable(string tablename = "") + { + lock (db.DBCommand) + { + // check for table name is null or empty --> use default tablename from type instead + string tn = tablename; + if (String.IsNullOrEmpty(tn)) + tn = AircraftRegistrationDesignator.TableName; + db.DBCommand.CommandText = "CREATE TABLE `" + tn + "`(Prefix TEXT NOT NULL DEFAULT '', Country TEXT, Remarks TEXT, LastUpdated INT32, PRIMARY KEY (Prefix))"; + db.DBCommand.Parameters.Clear(); + db.Execute(db.DBCommand); + } + } + + public long AircraftRegistrationCount() + { + object count = db.ExecuteScalar("SELECT COUNT(*) FROM " + AircraftRegistrationDesignator.TableName); + if (IsValid(count)) + return (long)count; + return 0; + } + + public bool AircraftRegistrationExists(string prefix) + { + AircraftRegistrationDesignator rd = new AircraftRegistrationDesignator(prefix); + return AircraftRegistrationExists(rd); + } + + public bool AircraftRegistrationExists(AircraftRegistrationDesignator rd) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT EXISTS (SELECT LastUpdated FROM " + AircraftRegistrationDesignator.TableName + " WHERE Prefix = @Prefix"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(rd.AsString("Prefix")); + object result = db.DBCommand.ExecuteScalar(); + if (IsValid(result) && ((long)result > 0)) + return true; + } + return false; + } + + private long AircraftRegistrationGetMaxLength() + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT max(length(Prefix)) FROM " + AircraftRegistrationDesignator.TableName; + db.DBCommand.Parameters.Clear(); + object result = db.DBCommand.ExecuteScalar(); + if (IsValid(result)) + return (long)result; + } + return 0; + } + + private long AircraftRegistrationGetMinLength() + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT min(length(Prefix)) FROM " + AircraftRegistrationDesignator.TableName; + db.DBCommand.Parameters.Clear(); + object result = db.DBCommand.ExecuteScalar(); + if (IsValid(result)) + return (long)result; + } + return 0; + } + + public AircraftRegistrationDesignator AircraftRegistrationFindByReg(string reg) + { + if (String.IsNullOrEmpty(reg)) + return null; + reg = reg.ToUpper().Trim(); + // check for US registration --> insert '-' after first letter + if (reg.StartsWith("N")) + { + if (Char.IsDigit(reg[1])) + reg = reg[0] + "-" + reg.Substring(1); + else + return null; + } + // return null if not a registration + if (reg.IndexOf("-") == 0) + return null; + // stop search at char before '-' should be clean prefix then + int stop = reg.IndexOf("-") - 1; + // start search two chars after '-' + int i = stop + 2; + while (i >= stop) + { + AircraftRegistrationDesignator rd = AircraftRegistrationFind(reg.Substring(0, i)); + if (rd != null) + return rd; + i--; + } + return null; + } + public AircraftRegistrationDesignator AircraftRegistrationFind(string prefix) + { + AircraftRegistrationDesignator rd = new AircraftRegistrationDesignator(prefix); + return AircraftRegistrationFind(rd); + } + + public AircraftRegistrationDesignator AircraftRegistrationFind(AircraftRegistrationDesignator rd) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT * FROM " + AircraftRegistrationDesignator.TableName + " WHERE Prefix = @Prefix"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(rd.AsString("Prefix")); + try + { + DataTable Result = db.Select(db.DBCommand); + if ((Result != null) && (Result.Rows.Count > 0)) + { + return new AircraftRegistrationDesignator(Result.Rows[0]); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + return null; + } + + public AircraftRegistrationDesignator AircraftRegistrationFindAt(long index) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT * FROM " + AircraftRegistrationDesignator.TableName + " LIMIT 1 OFFSET " + index.ToString(); + db.DBCommand.Parameters.Clear(); + try + { + DataTable Result = db.Select(db.DBCommand); + if ((Result != null) && (Result.Rows.Count > 0)) + { + return new AircraftRegistrationDesignator(Result.Rows[0]); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + return null; + } + + public DateTime AircraftRegistrationFindlastUpdated(string prefix) + { + AircraftRegistrationDesignator rd = new AircraftRegistrationDesignator(prefix); + return AircraftRegistrationFindLastUpdated(rd); + } + + public DateTime AircraftRegistrationFindLastUpdated(AircraftRegistrationDesignator rd) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT LastUpdated FROM " + AircraftRegistrationDesignator.TableName + " WHERE Prefix = @Prefix"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(rd.AsString("Prefix")); + object result = db.ExecuteScalar(db.DBCommand); + if (IsValid(result)) + return (SQLiteEntry.UNIXTimeToDateTime((int)result)); + } + return DateTime.MinValue; + } + + public int AircraftRegistrationInsert(AircraftRegistrationDesignator rd) + { + // maintain max/min lengths + if (rd.Prefix.Length < AircraftRegistrationMinLength) + AircraftRegistrationMinLength = rd.Prefix.Length; + if (rd.Prefix.Length > AircraftRegistrationMaxLength) + AircraftRegistrationMaxLength = rd.Prefix.Length; + lock (db.DBCommand) + { + db.DBCommand.CommandText = "INSERT INTO " + AircraftRegistrationDesignator.TableName + " (Prefix, Country, Remarks, LastUpdated) VALUES (@Prefix, @Country, @Remarks, @LastUpdated)"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(rd.AsString("Prefix")); + db.DBCommand.Parameters.Add(rd.AsString("Country")); + db.DBCommand.Parameters.Add(rd.AsString("Remarks")); + db.DBCommand.Parameters.Add(rd.AsUNIXTime("LastUpdated")); + return db.ExecuteNonQuery(db.DBCommand); + } + } + + public int AircraftRegistrationDelete(string prefix) + { + AircraftRegistrationDesignator rd = new AircraftRegistrationDesignator(prefix); + return AircraftRegistrationDelete(rd); + } + + public int AircraftRegistrationDelete(AircraftRegistrationDesignator rd) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "DELETE FROM " + AircraftRegistrationDesignator.TableName + " WHERE Prefix = @Prefix"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(rd.AsString("Prefix")); + return db.ExecuteNonQuery(db.DBCommand); + } + } + + public int AircraftRegistrationDeleteAll() + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "DELETE FROM " + AircraftRegistrationDesignator.TableName; + db.DBCommand.Parameters.Clear(); + return db.ExecuteNonQuery(db.DBCommand); + } + } + + public int AircraftRegistrationUpdate(AircraftRegistrationDesignator rd) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "UPDATE " + AircraftRegistrationDesignator.TableName + " SET Prefix = @Prefix, Country = @Country, Remarks = @Remarks, LastUpdated = @LastUpdated WHERE Prefix = @Prefix"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(rd.AsString("Prefix")); + db.DBCommand.Parameters.Add(rd.AsString("Country")); + db.DBCommand.Parameters.Add(rd.AsString("Remarks")); + db.DBCommand.Parameters.Add(rd.AsUNIXTime("LastUpdated")); + return db.ExecuteNonQuery(db.DBCommand); + } + } + + public int AircraftRegistrationBulkInsert(List rds) + { + int errors = 0; + try + { + lock (db) + { + db.BeginTransaction(); + foreach (AircraftRegistrationDesignator rd in rds) + { + try + { + AircraftRegistrationInsert(rd); + } + catch (Exception ex) + { + Log.WriteMessage("Error inserting AircraftRegistration [" + rd.Prefix + "]: " + ex.ToString()); + errors++; + } + } + db.Commit(); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + return -errors; + } + + public int AircraftRegistrationBulkDelete(List rds) + { + int errors = 0; + try + { + lock (db) + { + db.BeginTransaction(); + foreach (AircraftRegistrationDesignator rd in rds) + { + try + { + AircraftRegistrationDelete(rd); + } + catch (Exception ex) + { + Log.WriteMessage("Error deleting AircraftRegistration [" + rd.Prefix + "]: " + ex.ToString()); + errors++; + } + } + db.Commit(); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + return -errors; + } + + public int AircraftRegistrationBulkInsertOrUpdateIfNewer(List rds) + { + if (rds == null) + return 0; + int i = 0; + lock (db) + { + db.BeginTransaction(); + foreach (AircraftRegistrationDesignator rd in rds) + { + AircraftRegistrationInsertOrUpdateIfNewer(rd); + i++; + } + db.Commit(); + } + return i; + } + + public int AircraftRegistrationInsertOrUpdateIfNewer(AircraftRegistrationDesignator rd) + { + DateTime dt = AircraftRegistrationFindLastUpdated(rd); + if (dt == DateTime.MinValue) + return AircraftRegistrationInsert(rd); + if (dt < rd.LastUpdated) + return AircraftRegistrationUpdate(rd); + return 0; + } + + public List AircraftRegistrationGetAll() + { + List l = new List(); + DataTable Result = db.Select("SELECT * FROM " + AircraftRegistrationDesignator.TableName); + if (!IsValid(Result) || (Result.Rows.Count == 0)) + return l; + foreach (DataRow row in Result.Rows) + l.Add(new AircraftRegistrationDesignator(row)); + return l; + + } + + public List AircraftRegistrationGetAll(BackgroundWorker caller) + { + // gets all AircraftRegistrations from database + // supports abort calculation if called from background worker and cancellation requested + List l = new List(); + int i = 0; + SQLiteCommand cmd = new SQLiteCommand(db.DBConnection); + cmd.CommandText = "SELECT * FROM " + AircraftRegistrationDesignator.TableName; + SQLiteDataReader reader = cmd.ExecuteReader(); + while (reader.Read()) + { + AircraftRegistrationDesignator ap = new AircraftRegistrationDesignator((IDataRecord)reader); + l.Add(ap); + i++; + // abort calculation if called from background worker and cancellation pending + if (caller != null) + { + if (caller.WorkerSupportsCancellation && caller.CancellationPending) + return new List(); + if (caller.WorkerReportsProgress && (i % 1000 == 0)) + caller.ReportProgress(0, "Getting AircraftRegistration " + i.ToString() + " of"); + } + } + reader.Close(); + return l; + } + + public List AircraftRegistrationFromJSON(string json) + { + if (String.IsNullOrEmpty(json)) + return null; + JsonSerializerSettings settings = new JsonSerializerSettings(); + settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; + settings.FloatFormatHandling = FloatFormatHandling.String; + settings.Formatting = Newtonsoft.Json.Formatting.Indented; + return JsonConvert.DeserializeObject>(json, settings); + } + + public string AircraftRegistrationToJSON() + { + List l = AircraftRegistrationGetAll(); + JsonSerializerSettings settings = new JsonSerializerSettings(); + settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; + settings.FloatFormatHandling = FloatFormatHandling.String; + settings.Formatting = Newtonsoft.Json.Formatting.Indented; + string json = JsonConvert.SerializeObject(l, settings); + return json; + } + + public DataTableAircraftRegistrations AircraftRegistrationToDataTable() + { + List rds = AircraftRegistrationGetAll(); + DataTableAircraftRegistrations dtl = new DataTableAircraftRegistrations(rds); + return dtl; + } + + #endregion + + #region PlaneInfo + + public void PlaneInfoCreateView() + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "CREATE VIEW IF NOT EXISTS view_PlaneInfo AS SELECT AircraftPositions.LastUpdated, Call, Reg, AircraftPositions.Hex, Lat, Lon, Track, Alt, Speed, TypeCode, Manufacturer, Model, Category FROM AircraftPositions INNER JOIN Aircrafts ON AircraftPositions.Hex = Aircrafts.Hex INNER JOIN AircraftTypes ON AircraftTypes.ICAO = Aircrafts.TypeCode"; + db.DBCommand.Parameters.Clear(); + object result = db.DBCommand.ExecuteNonQuery(); + } + } + + public int PlaneInfoBulkInsertOrUpdateIfNewer(List planes) + { + if (planes == null) + return 0; + int i = 0; + lock (db) + { + db.BeginTransaction(); + foreach (PlaneInfo plane in planes) + { + try + { + // update aircraft information + if (PlaneInfo.Check_Hex(plane.Hex) && PlaneInfo.Check_Call(plane.Call) && PlaneInfo.Check_Reg(plane.Reg) && PlaneInfo.Check_Type(plane.Type)) + AircraftData.Database.AircraftInsertOrUpdateIfNewer(new AircraftDesignator(plane.Hex, plane.Call, plane.Reg, plane.Type, plane.Time)); + // update aircraft type information + if (!String.IsNullOrEmpty(plane.Type)) + { + AircraftTypeInsertOrUpdateIfNewer(new AircraftTypeDesignator("", plane.Type, plane.Manufacturer, plane.Model, plane.Category, DateTime.UtcNow)); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + return -1; + } + } + db.Commit(); + } + return i; + } + + public int PlaneInfoBulkInsertOrUpdateIfNewer(BackgroundWorker caller, List planes) + { + if (planes == null) + return 0; + int i = 0; + lock (db) + { + db.BeginTransaction(); + foreach (PlaneInfo plane in planes) + { + try + { + // update aircraft information + if (!String.IsNullOrEmpty(plane.Hex) && !String.IsNullOrEmpty(plane.Reg) && !String.IsNullOrEmpty(plane.Type)) + AircraftData.Database.AircraftInsertOrUpdateIfNewer(new AircraftDesignator(plane.Hex, plane.Call, plane.Reg, plane.Type, plane.Time)); + // update aircraft type information + if (!String.IsNullOrEmpty(plane.Type)) + { + AircraftTypeInsertOrUpdateIfNewer(new AircraftTypeDesignator("", plane.Type, plane.Manufacturer, plane.Model, plane.Category, DateTime.UtcNow)); + } + } + catch (Exception ex) + { + db.Rollback(); + Log.WriteMessage(ex.ToString()); + return -1; + } + // abort if called from background worker and cancellation pending + if (caller != null) + { + if (caller.WorkerSupportsCancellation && caller.CancellationPending) + { + db.Rollback(); + return -1; + } + } + + } + db.Commit(); + } + return i; + } + + public List PlaneInfoGetAll(DateTime newerthan) + { + List l = new List(); + int i = SupportFunctions.DateTimeToUNIXTime(newerthan); + // SELECT max(AircraftPositions.Lastupdated) AS LastUpdated, Call, Reg, AircraftPositions.Hex, Lat, Lon, Track, Alt, Speed, TypeCode, Manufacturer, Model, Category FROM AircraftPositions INNER JOIN Aircrafts ON AircraftPositions.Hex = Aircrafts.Hex INNER JOIN AircraftTypes ON AircraftTypes.ICAO = Aircrafts.TypeCode WHERE AircraftPositions.LastUpdated > 1500000 GROUP BY AircraftPositions.Hex + DataTable Result = db.Select("SELECT max(AircraftPositions.Lastupdated) AS LastUpdated, Call, Reg, AircraftPositions.Hex, Lat, Lon, Track, Alt, Speed, TypeCode, Manufacturer, Model, Category FROM AircraftPositions INNER JOIN Aircrafts ON AircraftPositions.Hex = Aircrafts.Hex INNER JOIN AircraftTypes ON AircraftTypes.ICAO = Aircrafts.TypeCode WHERE AircraftPositions.LastUpdated > " + i.ToString() + " GROUP BY AircraftPositions.Hex"); + if (!IsValid(Result) || (Result.Rows.Count == 0)) + return l; + foreach (DataRow row in Result.Rows) + { + PlaneInfo info = new PlaneInfo(row); + try + { + l.Add(info); + } + catch (Exception ex) + { + Log.WriteMessage("Error inserting PlaneInfo[" + info.ToString() + "]: " + ex.ToString()); + } + } + return l; + } + + /// + /// Gets a list of aircraft infos at a time. + /// Querying the latest position entry per aircraft but not older than ttl back in history + /// and estimating the position at given time + /// The given time. + /// "Time To Live": discard positions which are older than ttl [min]. + /// + /// + public List PlaneInfoGetAll(DateTime at, int ttl) + { + List l = new List(); + int to = SupportFunctions.DateTimeToUNIXTime(at); + int from = to - ttl * 60; + DataTable Result = db.Select("SELECT max(AircraftPositions.Lastupdated) AS LastUpdated, Call, Reg, AircraftPositions.Hex, Lat, Lon, Track, Alt, Speed, TypeCode, Manufacturer, Model, Category FROM AircraftPositions INNER JOIN Aircrafts ON AircraftPositions.Hex = Aircrafts.Hex INNER JOIN AircraftTypes ON AircraftTypes.ICAO = Aircrafts.TypeCode WHERE AircraftPositions.LastUpdated >= " + from.ToString() + " AND AircraftPositions.LastUpdated <= " + to.ToString() + " GROUP BY AircraftPositions.Hex"); +// DataTable Result = db.Select("SELECT max(Lastupdated) AS LastUpdated, Call, Reg, Hex, Lat, Lon, Track, Alt, Speed, TypeCode, Manufacturer, Model, Category FROM view_PlaneInfo WHERE LastUpdated > " + from.ToString() + " AND LastUpdated <= " + to.ToString() + " GROUP BY Hex"); + if (!IsValid(Result) || (Result.Rows.Count == 0)) + return l; + foreach (DataRow row in Result.Rows) + { + PlaneInfo info = new PlaneInfo(row); + //estimate new position + // change speed to km/h + double speed = info.Speed_kmh; + // calculate distance after timespan + double dist = speed * (at - info.Time).TotalHours; + // estimate new position + LatLon.GPoint newpos = LatLon.DestinationPoint(info.Lat, info.Lon, info.Track, dist); + info.Lat = newpos.Lat; + info.Lon = newpos.Lon; + info.Time = at; + l.Add(info); + } + return l; + + } + + // selects all planes from a list which are in range of the midpoint of a given propagation path + public List GetNearestPlanes(DateTime at, PropagationPathDesignator ppath, List planes, double maxradius, double maxdist, double maxalt) + { + List l = new List(); + // return empty list on empty list or null + if (planes == null) + return l; + if (planes.Count() == 0) + return l; + // adjust maxradius when automatic calculation is required + if (maxradius < 0) + maxradius = double.MaxValue; + if (maxradius == 0) + maxradius = ppath.Distance / 2; + // get midpoint value + double midlat = ppath.GetMidPoint().Lat; + double midlon = ppath.GetMidPoint().Lon; + try + { +// StreamWriter sw = new StreamWriter("GetNearestPlanes.csv"); +// sw.WriteLine("utc;hex;call;lat;lon;alt;speed;track;maxdist"); + // get intersection info for each plane in list + foreach (PlaneInfo info in planes) + { + PlaneInfo plane; + try + { + // skip if plane is out of range + double dist = LatLon.Distance(midlat, midlon, info.Lat, info.Lon); +/* + sw.WriteLine(info.Time.ToString() + ";" + + info.Hex + ";" + + info.Call + ";" + + info.Lat.ToString("") + ";" + + info.Lon.ToString("") + ";" + + info.Alt_m.ToString("") + ";" + + info.Speed_kmh.ToString("") + ";" + + info.Track.ToString("") + ";" + + maxdist.ToString("")); +*/ + if (dist > maxradius) + continue; + + // clone object + plane = new PlaneInfo(info.Time, info.Call, info.Reg, info.Hex, info.Lat, info.Lon, info.Track, info.Alt, info.Speed, info.Type, info.Manufacturer, info.Model, info.Category); + //estimate new position + // change speed to km/h + double speed = info.Speed_kmh; + // calculate distance after timespan + double di = speed * (at - info.Time).TotalHours; + // estimate new position + LatLon.GPoint newpos = LatLon.DestinationPoint(info.Lat, info.Lon, info.Track, di); + info.Lat = newpos.Lat; + info.Lon = newpos.Lon; + info.Time = at; + + + // calculate four possible intersections + // i1 --> plane heading + // i2 --> plane heading +90° + // i3 --> plane heading - 90° + // i4 --> opposite plane heading + // imin --> intpoint with shortest distance + + IntersectionPoint imin = null; + IntersectionPoint i1 = null; + IntersectionPoint i2 = null; + IntersectionPoint i3 = null; + IntersectionPoint i4 = null; + Stopwatch st = new Stopwatch(); + i1 = ppath.GetIntersectionPoint(plane.Lat, plane.Lon, plane.Track, 0); + i2 = ppath.GetIntersectionPoint(plane.Lat, plane.Lon, ppath.Bearing12 - 90, 0); + // calculate right opposite direction only if no left intersection was found + if (i2 == null) + i3 = ppath.GetIntersectionPoint(plane.Lat, plane.Lon, ppath.Bearing12 + 90, 0); + // calcalute opposite direction only if no forward intersection was found + if (i1 == null) + i4 = ppath.GetIntersectionPoint(plane.Lat, plane.Lon, plane.Track - 180, 0); + // find the minimum distance first + if (i1 != null) + imin = i1; + if ((i2 != null) && ((imin == null) || (i2.QRB < imin.QRB))) + imin = i2; + if ((i3 != null) && ((imin == null) || (i3.QRB < imin.QRB))) + imin = i3; + if ((i4 != null) && ((imin == null) || (i4.QRB < imin.QRB))) + imin = i4; + // check hot planes which are very near the path first + if ((imin != null) && (imin.QRB <= maxdist)) + { + // plane is near path + // use the minimum qrb info + plane.IntPoint = new LatLon.GPoint(imin.Lat, imin.Lon); + plane.IntQRB = imin.QRB; + plane.AltDiff = plane.Alt_m - imin.Min_H; + double c1 = LatLon.Bearing(plane.IntPoint.Lat, plane.IntPoint.Lon, ppath.Lat2, ppath.Lon2); + double c2 = plane.Track; + double ca = c1 - c2; + if (ca < 0) + ca = ca + 360; + if ((ca > 180) && (ca < 360)) + ca = 360 - ca; + // save in rad + plane.Angle = ca / 180.0 * Math.PI; + plane.Eps1 = Propagation.EpsilonFromHeights(ppath.h1, imin.Dist1, plane.Alt_m, ppath.Radius); + plane.Eps2 = Propagation.EpsilonFromHeights(ppath.h2, imin.Dist2, plane.Alt_m, ppath.Radius); + plane.Theta1 = Propagation.ThetaFromHeights(ppath.h1, imin.Dist1, plane.Alt_m, ppath.Radius); + plane.Theta2 = Propagation.ThetaFromHeights(ppath.h2, imin.Dist2, plane.Alt_m, ppath.Radius); + plane.Squint = Math.Abs(Propagation.ThetaFromHeights(ppath.h1, imin.Dist1, plane.Alt_m, ppath.Radius) - Propagation.ThetaFromHeights(ppath.h2, imin.Dist2, plane.Alt_m, ppath.Radius)); + if (plane.AltDiff > 0) + { + // plane is high enough + plane.Potential = 100; + } + else if (imin.Min_H <= maxalt) + { + // plane is not high enough yet but might be in the future + plane.Potential = 50; + } + else + { + // minimal needed altitude is higher than Planes_MaxAlt --> no way to reach + // plane is not interesting + plane.Potential = 0; + } + } + else + { + // plane is far from path --> check only intersection i1 = planes moves towards path + if ((i1 != null) && (i1.Min_H <= maxalt)) + { + plane.IntPoint = new LatLon.GPoint(i1.Lat, i1.Lon); + plane.IntQRB = i1.QRB; + plane.AltDiff = plane.Alt_m - i1.Min_H; + plane.Eps1 = Propagation.EpsilonFromHeights(ppath.h1, i1.Dist1, plane.Alt_m, ppath.Radius); + plane.Eps2 = Propagation.EpsilonFromHeights(ppath.h2, i1.Dist2, plane.Alt_m, ppath.Radius); + plane.Squint = Math.Abs(Propagation.ThetaFromHeights(ppath.h1, imin.Dist1, plane.Alt_m, ppath.Radius) - Propagation.ThetaFromHeights(ppath.h2, imin.Dist2, plane.Alt_m, ppath.Radius)); + if (plane.AltDiff > 0) + { + // plane wil cross path in a suitable altitude + plane.Potential = 75; + } + else + { + // plane wil cross path not in a suitable altitude + plane.Potential = 50; + } + } + else + { + // plane is not interesting + plane.Potential = 0; + } + } + + // add plane to list + l.Add(plane); + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + return l; + } + + #endregion + } + +} diff --git a/AirScout.Aircrafts/AircraftDatabaseUpdater.cs b/AirScout.Aircrafts/AircraftDatabaseUpdater.cs new file mode 100644 index 0000000..fcf8f6d --- /dev/null +++ b/AirScout.Aircrafts/AircraftDatabaseUpdater.cs @@ -0,0 +1,365 @@ + +using System; +using System.ComponentModel; +using System.Windows.Forms; +using System.Net; +using System.IO; +using System.Threading; +using System.Diagnostics; +using System.Collections; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Globalization; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using ScoutBase; +using ScoutBase.Core; +using System.Data.SQLite; +using System.Text; + +namespace AirScout.Aircrafts +{ + + #region AircraftDatabaseUpdater + + + public class AircraftDatabaseUpdaterStartOptions + { + public string Name; + public BACKGROUNDUPDATERSTARTOPTIONS Options; + } + + + // Background worker for aircraft database update + [DefaultPropertyAttribute("Name")] + public class AircraftDatabaseUpdater : BackgroundWorker + { + + AircraftDatabaseUpdaterStartOptions StartOptions; + string Password; + + public AircraftDatabaseUpdater() : base() + { + this.WorkerReportsProgress = true; + this.WorkerSupportsCancellation = true; + } + + private bool ReadAircraftsFromURL(string url, string filename) + { + try + { + AutoDecompressionWebClient cl = new AutoDecompressionWebClient(); + DOWNLOADFILESTATUS status = cl.DownloadFileIfNewer(url, filename, true, true, Password); + if (((status & DOWNLOADFILESTATUS.ERROR) > 0) && ((status & DOWNLOADFILESTATUS.ERROR) > 0)) + { + this.ReportProgress(-1, "Error while downloading and extracting " + filename); + return false; + } + else if (((status & DOWNLOADFILESTATUS.NEWER) > 0) || ((status & DOWNLOADFILESTATUS.NOTNEWER) > 0)) + { + string json = ""; + using (StreamReader sr = new StreamReader(filename)) + json = sr.ReadToEnd(); + List ads = AircraftData.Database.AircraftFromJSON(json); + // check for invalid entries + foreach (AircraftDesignator ad in ads) + { + if (String.IsNullOrEmpty(ad.Call)) + ad.Call = "[unknown]"; + if (String.IsNullOrEmpty(ad.Reg)) + ad.Reg = "[unknown]"; + if (String.IsNullOrEmpty(ad.TypeCode)) + ad.TypeCode = "[unknown]"; + } + // check for empty database + if (AircraftData.Database.AircraftCount() == 0) + { + // do bulk insert + AircraftData.Database.AircraftBulkInsert(ads); + } + else + { + // do bulk update + AircraftData.Database.AircraftBulkInsertOrUpdateIfNewer(ads); + } + return true; + } + } + catch (Exception ex) + { + // Error loading database + this.ReportProgress(-1, "[" + url + "]: " + ex.ToString()); + } + return false; + } + + private bool ReadAircraftTypesFromURL(string url, string filename) + { + try + { + AutoDecompressionWebClient cl = new AutoDecompressionWebClient(); + DOWNLOADFILESTATUS status = cl.DownloadFileIfNewer(url, filename, true, true, Password); + if (((status & DOWNLOADFILESTATUS.ERROR) > 0) && ((status & DOWNLOADFILESTATUS.ERROR) > 0)) + { + this.ReportProgress(-1, "Error while downloading and extracting " + filename); + return false; + } + else if (((status & DOWNLOADFILESTATUS.NEWER) > 0) || ((status & DOWNLOADFILESTATUS.NOTNEWER) > 0)) + { + string json = ""; + using (StreamReader sr = new StreamReader(filename)) + json = sr.ReadToEnd(); + List tds = AircraftData.Database.AircraftTypeFromJSON(json); + // check for empty database + if (AircraftData.Database.AircraftTypeCount() == 0) + { + // do bulk insert + AircraftData.Database.AircraftTypeBulkInsert(tds); + } + else + { + // do bulk update + AircraftData.Database.AircraftTypeBulkInsertOrUpdateIfNewer(tds); + } + return true; + } + } + catch (Exception ex) + { + // Error loading database + this.ReportProgress(-1, "[" + url + "]: " + ex.ToString()); + } + return false; + } + + private bool ReadAircraftRegistrationsFromURL(string url, string filename) + { + try + { + AutoDecompressionWebClient cl = new AutoDecompressionWebClient(); + DOWNLOADFILESTATUS status = cl.DownloadFileIfNewer(url, filename, true, true, Password); + if (((status & DOWNLOADFILESTATUS.ERROR) > 0) && ((status & DOWNLOADFILESTATUS.ERROR) > 0)) + { + this.ReportProgress(-1, "Error while downloading and extracting " + filename); + return false; + } + else if (((status & DOWNLOADFILESTATUS.NEWER) > 0) || ((status & DOWNLOADFILESTATUS.NOTNEWER) > 0)) + { + string json = ""; + using (StreamReader sr = new StreamReader(filename)) + json = sr.ReadToEnd(); + List rds = AircraftData.Database.AircraftRegistrationFromJSON(json); + // check for empty database + if (AircraftData.Database.AircraftRegistrationCount() == 0) + { + // do bulk insert + AircraftData.Database.AircraftRegistrationBulkInsert(rds); + } + else + { + // do bulk update + AircraftData.Database.AircraftRegistrationBulkInsertOrUpdateIfNewer(rds); + } + return true; + } + } + catch (Exception ex) + { + // Error loading database + this.ReportProgress(-1, "[" + url + "]: " + ex.ToString()); + } + return false; + } + + private bool ReadAirportsFromURL(string url, string filename) + { + try + { + AutoDecompressionWebClient cl = new AutoDecompressionWebClient(); + DOWNLOADFILESTATUS status = cl.DownloadFileIfNewer(url, filename, true, true, Password); + if (((status & DOWNLOADFILESTATUS.ERROR) > 0) && ((status & DOWNLOADFILESTATUS.ERROR) > 0)) + { + this.ReportProgress(-1, "Error while downloading and extracting " + filename); + return false; + } + else if (((status & DOWNLOADFILESTATUS.NEWER) > 0) || ((status & DOWNLOADFILESTATUS.NOTNEWER) > 0)) + { + string json = ""; + using (StreamReader sr = new StreamReader(filename)) + json = sr.ReadToEnd(); + List pds = AircraftData.Database.AirportFromJSON(json); + // check for empty database + if (AircraftData.Database.AirportCount() == 0) + { + // do bulk insert + AircraftData.Database.AirportBulkInsert(pds); + } + else + { + // do bulk update + AircraftData.Database.AirportBulkInsertOrUpdateIfNewer(pds); + } + return true; + } + } + catch (Exception ex) + { + // Error loading database + this.ReportProgress(-1, "[" + url + "]: " + ex.ToString()); + } + return false; + } + + private bool ReadAirlinesFromURL(string url, string filename) + { + try + { + AutoDecompressionWebClient cl = new AutoDecompressionWebClient(); + DOWNLOADFILESTATUS status = cl.DownloadFileIfNewer(url, filename, true, true, Password); + if (((status & DOWNLOADFILESTATUS.ERROR) > 0) && ((status & DOWNLOADFILESTATUS.ERROR) > 0)) + { + this.ReportProgress(-1, "Error while downloading and extracting " + filename); + return false; + } + else if (((status & DOWNLOADFILESTATUS.NEWER) > 0) || ((status & DOWNLOADFILESTATUS.NOTNEWER) > 0)) + { + string json = ""; + using (StreamReader sr = new StreamReader(filename)) + json = sr.ReadToEnd(); + List lds = AircraftData.Database.AirlineFromJSON(json); + // check for empty database + if (AircraftData.Database.AirlineCount() == 0) + { + // do bulk insert + AircraftData.Database.AirlineBulkInsert(lds); + } + else + { + // do bulk update + AircraftData.Database.AirlineBulkInsertOrUpdateIfNewer(lds); + } + return true; + } + } + catch (Exception ex) + { + // Error loading database + this.ReportProgress(-1, "[" + url + "]: " + ex.ToString()); + } + return false; + } + + protected override void OnDoWork(DoWorkEventArgs e) + { + StartOptions = (AircraftDatabaseUpdaterStartOptions)e.Argument; + this.ReportProgress(0, StartOptions.Name + "started."); + // name the thread for debugging + if (String.IsNullOrEmpty(Thread.CurrentThread.Name)) + Thread.CurrentThread.Name = nameof(AircraftDatabaseUpdater); + // get update interval + // get current AirScout password phrase from website and store it in settings + try + { + // get upload info + WebRequest myWebRequest = WebRequest.Create(Properties.Settings.Default.Aircrafts_PasswordURL); + WebResponse myWebResponse = myWebRequest.GetResponse(); + Stream ReceiveStream = myWebResponse.GetResponseStream(); + Encoding encode = System.Text.Encoding.GetEncoding("utf-8"); + StreamReader readStream = new StreamReader(ReceiveStream, encode); + Password = readStream.ReadToEnd(); + Password = ScoutBase.Core.Password.GetSFTPPassword(Password); + } + catch (Exception ex) + { + this.ReportProgress(-1, ex.ToString()); + } + int interval = (int)Properties.Settings.Default.Database_BackgroundUpdate_Period * 60; + do + { + try + { + int errors = 0; + // check if any kind of update is enabled + if ((StartOptions.Options == BACKGROUNDUPDATERSTARTOPTIONS.RUNONCE) || (StartOptions.Options == BACKGROUNDUPDATERSTARTOPTIONS.RUNPERIODICALLY)) + { + this.ReportProgress(0, "Updating database..."); + // get temp directory + string TmpDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), Application.CompanyName, Application.ProductName, "Tmp").TrimEnd(Path.DirectorySeparatorChar); + if (!Directory.Exists(TmpDirectory)) + Directory.CreateDirectory(TmpDirectory); + // reset database status + AircraftData.Database.SetDBStatus(DATABASESTATUS.UNDEFINED); + this.ReportProgress(1, AircraftData.Database.GetDBStatus()); + Stopwatch st = new Stopwatch(); + st.Start(); + AircraftData.Database.SetDBStatus(DATABASESTATUS.UPDATING); + this.ReportProgress(1, AircraftData.Database.GetDBStatus()); + // update aircraft database + this.ReportProgress(0, "Updating aircraft types from web database..."); + if (!ReadAircraftTypesFromURL(Properties.Settings.Default.Aircrafts_UpdateURL + "AircraftTypes.json", Path.Combine(TmpDirectory, "AircraftTypes.json"))) + errors++; + if (this.CancellationPending) + break; + this.ReportProgress(0, "Updating airports from web database..."); + if (!ReadAirportsFromURL(Properties.Settings.Default.Aircrafts_UpdateURL + "Airports.json", Path.Combine(TmpDirectory, "Airports.json"))) + errors++; + if (this.CancellationPending) + break; + this.ReportProgress(0, "Updating aircrafts from web database..."); + if (!ReadAircraftsFromURL(Properties.Settings.Default.Aircrafts_UpdateURL + "Aircrafts.json", Path.Combine(TmpDirectory, "Aircrafts.json"))) + errors++; + if (this.CancellationPending) + break; + this.ReportProgress(0, "Updating aircraft registrations from web database..."); + if (!ReadAircraftRegistrationsFromURL(Properties.Settings.Default.Aircrafts_UpdateURL + "AircraftRegistrations.json", Path.Combine(TmpDirectory, "AircraftRegistrations.json"))) + errors++; + if (this.CancellationPending) + break; + this.ReportProgress(0, "Updating airlines from web database..."); + if (!ReadAirlinesFromURL(Properties.Settings.Default.Aircrafts_UpdateURL + "Airlines.json", Path.Combine(TmpDirectory, "Airlines.json"))) + errors++; + + st.Stop(); + // display status + if (errors == 0) + { + AircraftData.Database.SetDBStatus(DATABASESTATUS.UPTODATE); + this.ReportProgress(1, AircraftData.Database.GetDBStatus()); + this.ReportProgress(0, " Aircraft database update completed: " + st.Elapsed.ToString(@"hh\:mm\:ss")); + } + else + { + AircraftData.Database.SetDBStatus(DATABASESTATUS.ERROR); + this.ReportProgress(1, AircraftData.Database.GetDBStatus()); + this.ReportProgress(0, " Aircraft database update completed with errors[" + errors.ToString() + "]: " + st.Elapsed.ToString(@"hh\:mm\:ss")); + } + + // sleep when running periodically + if (StartOptions.Options == BACKGROUNDUPDATERSTARTOPTIONS.RUNPERIODICALLY) + { + int i = 0; + while (!this.CancellationPending && (i < interval)) + { + Thread.Sleep(1000); + i++; + } + } + } + } + catch (Exception ex) + { + this.ReportProgress(-1, ex.ToString()); + } + } + while (!this.CancellationPending && (StartOptions.Options == BACKGROUNDUPDATERSTARTOPTIONS.RUNPERIODICALLY)); + if (this.CancellationPending) + this.ReportProgress(0, "Cancelled."); + else + this.ReportProgress(0, "Finished."); + } + + #endregion + + } +} diff --git a/AirScout.Aircrafts/AircraftRegistration.cs b/AirScout.Aircrafts/AircraftRegistration.cs new file mode 100644 index 0000000..c272330 --- /dev/null +++ b/AirScout.Aircrafts/AircraftRegistration.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.Collections; +using System.Linq; +using System.Text; +using System.Threading; +using System.ComponentModel; +using System.Globalization; +using System.Net; +using System.IO; +using System.Data; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System.Data.SQLite; + +namespace AirScout.Aircrafts +{ + /// + /// Holds the aircraft registration information + /// + [System.ComponentModel.DesignerCategory("")] + public class AircraftRegistrationDesignator : SQLiteEntry + { + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // be sure to have a copy of these static members in each derived class !! + // update the tabale name to the table name according to the class + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + [JsonIgnore] + public static new readonly string TableName = "AircraftRegistrations"; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public string Prefix { get; set; } + public string Country { get; set; } + public string Remarks { get; set; } + + public AircraftRegistrationDesignator() + { + Prefix = ""; + Country = ""; + Remarks = ""; + LastUpdated = DateTime.MinValue; + } + + public AircraftRegistrationDesignator(DataRow row) : this() + { + FillFromDataRow(row); + } + + public AircraftRegistrationDesignator(IDataRecord record) : this() + { + FillFromDataRecord(record); + } + + public AircraftRegistrationDesignator(string prefix) : this(prefix, "", "", DateTime.UtcNow) { } + public AircraftRegistrationDesignator(string prefix, string country, string remarks) : this(prefix, country, remarks, DateTime.UtcNow) { } + public AircraftRegistrationDesignator(string prefix, string country, string remarks, DateTime lastupdated) : this() + { + Prefix = prefix; + Country = country; + Remarks = remarks; + LastUpdated = lastupdated; + } + + } + + [System.ComponentModel.DesignerCategory("")] + public class DataTableAircraftRegistrations : DataTable + { + public DataTableAircraftRegistrations() + : base() + { + // set table name + TableName = "AircraftRegistrations"; + // create all specific columns + DataColumn Prefix = this.Columns.Add("Prefix", typeof(string)); + DataColumn Country = this.Columns.Add("Country", typeof(string)); + DataColumn Remarks = this.Columns.Add("Remarks", typeof(string)); + DataColumn LastUpdated = this.Columns.Add("LastUpdated", typeof(string)); + // create primary key + DataColumn[] keys = new DataColumn[1]; + keys[0] = Prefix; + this.PrimaryKey = keys; + } + + public DataTableAircraftRegistrations(List ads) + : this() + { + foreach (AircraftRegistrationDesignator ad in ads) + { + DataRow row = this.NewRow(); + row[0] = ad.Prefix; + row[1] = ad.Country; + row[2] = ad.Remarks; + row[3] = ad.LastUpdated; + this.Rows.Add(row); + } + } + + } +} diff --git a/AirScout.Aircrafts/AircraftType.cs b/AirScout.Aircrafts/AircraftType.cs new file mode 100644 index 0000000..5b9edd2 --- /dev/null +++ b/AirScout.Aircrafts/AircraftType.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using System.Collections; +using System.Linq; +using System.Text; +using System.Threading; +using System.ComponentModel; +using System.Globalization; +using System.Net; +using System.IO; +using System.Data; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System.Data.SQLite; + +namespace AirScout.Aircrafts +{ + /// + /// Holds the aircraft type information + /// + [System.ComponentModel.DesignerCategory("")] + public class AircraftTypeDesignator : SQLiteEntry + { + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // be sure to have a copy of these static members in each derived class !! + // update the tabale name to the table name according to the class + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + [JsonIgnore] + public static new readonly string TableName = "AircraftTypes"; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public string IATA { get; set; } + public string ICAO { get; set; } + public string Manufacturer { get; set; } + public string Model { get; set; } + public PLANECATEGORY Category {get; set;} + + public AircraftTypeDesignator() + { + LastUpdated = DateTime.MinValue; + } + + public AircraftTypeDesignator(DataRow row) : this() + { + FillFromDataRow(row); + } + + public AircraftTypeDesignator(IDataRecord record) : this() + { + FillFromDataRecord(record); + } + + public AircraftTypeDesignator(string iata, string icao) : this(iata, icao, "", "", PLANECATEGORY.NONE, DateTime.UtcNow) { } + public AircraftTypeDesignator(string iata, string icao, string manufacturer, string model, PLANECATEGORY cat) : this(iata, icao, manufacturer, model, cat, DateTime.UtcNow) { } + public AircraftTypeDesignator(string iata, string icao, string manufacturer, string model, PLANECATEGORY cat, DateTime lastupdated) : this() + { + IATA = iata; + ICAO = icao; + Manufacturer = manufacturer; + Model = model; + Category = cat; + LastUpdated = lastupdated; + } + + } + + [System.ComponentModel.DesignerCategory("")] + public class DataTableAircraftTypes : DataTable + { + public DataTableAircraftTypes() + : base() + { + // set table name + TableName = "AircraftTypes"; + // create all specific columns + DataColumn IATA = this.Columns.Add("IATA", typeof(string)); + DataColumn ICAO = this.Columns.Add("ICAO", typeof(string)); + DataColumn Manufacturer = this.Columns.Add("Manufacturer", typeof(string)); + DataColumn Model = this.Columns.Add("Model", typeof(string)); + DataColumn Category = this.Columns.Add("Category", typeof(PLANECATEGORY)); + DataColumn LastUpdated = this.Columns.Add("LastUpdated", typeof(string)); + // create primary key + DataColumn[] keys = new DataColumn[2]; + keys[0] = IATA; + keys[1] = ICAO; + this.PrimaryKey = keys; + } + + public DataTableAircraftTypes(List ads) + : this() + { + foreach (AircraftTypeDesignator ad in ads) + { + DataRow row = this.NewRow(); + row[0] = ad.IATA; + row[1] = ad.ICAO; + row[2] = ad.Manufacturer; + row[3] = ad.Model; + row[4] = ad.Category; + row[5] = ad.LastUpdated; + this.Rows.Add(row); + } + } + + } +} diff --git a/AirScout.Aircrafts/Airline.cs b/AirScout.Aircrafts/Airline.cs new file mode 100644 index 0000000..9cb2559 --- /dev/null +++ b/AirScout.Aircrafts/Airline.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using System.Collections; +using System.Linq; +using System.Text; +using System.Threading; +using System.ComponentModel; +using System.Globalization; +using System.Net; +using System.IO; +using System.Data; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System.Data.SQLite; + +namespace AirScout.Aircrafts +{ + /// + /// Holds the airline information + /// + [System.ComponentModel.DesignerCategory("")] + public class AirlineDesignator : SQLiteEntry + { + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // be sure to have a copy of these static members in each derived class !! + // update the tabale name to the table name according to the class + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + [JsonIgnore] + public static new readonly string TableName = "Airlines"; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public string ICAO { get; set; } + public string IATA { get; set; } + public string Airline { get; set; } + public string Country { get; set; } + + public AirlineDesignator() + { + ICAO = ""; + IATA = ""; + Airline = ""; + Country = ""; + LastUpdated = DateTime.MinValue; + } + + public AirlineDesignator(DataRow row) : this() + { + FillFromDataRow(row); + } + + public AirlineDesignator(IDataRecord record) : this() + { + FillFromDataRecord(record); + } + + public AirlineDesignator(string icao, string iata) : this(icao, iata,"", "", DateTime.UtcNow) { } + public AirlineDesignator(string icao, string iata, string airline, string country) : this(icao, iata, airline, country, DateTime.UtcNow) { } + public AirlineDesignator(string icao, string iata, string airline, string country, DateTime lastupdated) : this() + { + ICAO = icao; + IATA = iata; + Airline = airline; + Country = country; + LastUpdated = lastupdated; + } + + } + + [System.ComponentModel.DesignerCategory("")] + public class DataTableAirlines : DataTable + { + public DataTableAirlines() + : base() + { + // set table name + TableName = "Airlines"; + // create all specific columns + DataColumn IATA = this.Columns.Add("IATA", typeof(string)); + DataColumn ICAO = this.Columns.Add("ICAO", typeof(string)); + DataColumn Airline = this.Columns.Add("Airline", typeof(string)); + DataColumn Country = this.Columns.Add("Country", typeof(string)); + DataColumn LastUpdated = this.Columns.Add("LastUpdated", typeof(string)); + // create primary key + DataColumn[] keys = new DataColumn[2]; + keys[0] = IATA; + keys[1] = ICAO; + this.PrimaryKey = keys; + } + + public DataTableAirlines(List ads) + : this() + { + foreach (AirlineDesignator ad in ads) + { + DataRow row = this.NewRow(); + row[0] = ad.ICAO; + row[1] = ad.IATA; + row[2] = ad.Airline; + row[3] = ad.Country; + row[4] = ad.LastUpdated; + this.Rows.Add(row); + } + } + + } +} diff --git a/AirScout.Aircrafts/Airport.cs b/AirScout.Aircrafts/Airport.cs new file mode 100644 index 0000000..c9c04c7 --- /dev/null +++ b/AirScout.Aircrafts/Airport.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Generic; +using System.Collections; +using System.Linq; +using System.Text; +using System.Threading; +using System.ComponentModel; +using System.Globalization; +using System.Net; +using System.IO; +using System.Data; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System.Data.SQLite; + +namespace AirScout.Aircrafts +{ + /// + /// Holds the airport information + /// + [System.ComponentModel.DesignerCategory("")] + public class AirportDesignator : SQLiteEntry + { + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // be sure to have a copy of these static members in each derived class !! + // update the tabale name to the table name according to the class + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + [JsonIgnore] + public static new readonly string TableName = "Airports"; + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public string ICAO { get; set; } + public string IATA { get; set; } + public double Lat { get; set; } + public double Lon { get; set; } + public double Alt { get; set; } + public string Airport { get; set; } + public string Country { get; set; } + + public AirportDesignator() + { + ICAO = ""; + IATA = ""; + Lat = 0; + Lon = 0; + Alt = 0; + Airport = ""; + Country = ""; + LastUpdated = DateTime.MinValue; + } + + public AirportDesignator(DataRow row) : this() + { + FillFromDataRow(row); + } + + public AirportDesignator(IDataRecord record) : this() + { + FillFromDataRecord(record); + } + + public AirportDesignator(string icao, string iata) : this(icao, iata, 0,0,0, "", "", DateTime.UtcNow) { } + public AirportDesignator(string icao, string iata, double lat, double lon, double alt, string airport, string country) : this(icao, iata, lat, lon, alt, airport, country, DateTime.UtcNow) { } + public AirportDesignator(string icao, string iata, double lat, double lon, double alt, string airport, string country, DateTime lastupdated) : this() + { + ICAO = icao; + IATA = iata; + Lat = lat; + Lon = lon; + Alt = alt; + Airport = airport; + Country = country; + LastUpdated = lastupdated; + } + + } + + [System.ComponentModel.DesignerCategory("")] + public class DataTableAirports : DataTable + { + public DataTableAirports() + : base() + { + // set table name + TableName = "Airports"; + // create all specific columns + DataColumn IATA = this.Columns.Add("ICAO", typeof(string)); + DataColumn ICAO = this.Columns.Add("IATA", typeof(string)); + DataColumn Lat = this.Columns.Add("Lat", typeof(double)); + DataColumn Lon = this.Columns.Add("Lon", typeof(double)); + DataColumn Alt = this.Columns.Add("Alt", typeof(double)); + DataColumn Airport = this.Columns.Add("Airport", typeof(string)); + DataColumn Country = this.Columns.Add("Country", typeof(string)); + DataColumn LastUpdated = this.Columns.Add("LastUpdated", typeof(string)); + // create primary key + DataColumn[] keys = new DataColumn[2]; + keys[0] = IATA; + keys[1] = ICAO; + this.PrimaryKey = keys; + } + + public DataTableAirports(List pds) + : this() + { + foreach (AirportDesignator pd in pds) + { + DataRow row = this.NewRow(); + row[0] = pd.ICAO; + row[1] = pd.IATA; + row[2] = pd.Lat; + row[3] = pd.Lon; + row[4] = pd.Alt; + row[5] = pd.Airport; + row[6] = pd.Country; + row[7] = pd.LastUpdated; + this.Rows.Add(row); + } + } + + } +} diff --git a/AirScout.Aircrafts/PlaneCategory.cs b/AirScout.Aircrafts/PlaneCategory.cs new file mode 100644 index 0000000..d59e725 --- /dev/null +++ b/AirScout.Aircrafts/PlaneCategory.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ScoutBase.Core; +using System.Reflection; + +namespace AirScout.Aircrafts +{ + public enum PLANECATEGORY + { + [StringCustomAttribute("None")] + NONE = 0, + [StringCustomAttribute("Light")] + LIGHT = 1, + [StringCustomAttribute("Medium")] + MEDIUM = 2, + [StringCustomAttribute("Heavy")] + HEAVY = 3, + [StringCustomAttribute("Superheavy")] + SUPERHEAVY = 4, + } + + public static class PlaneCategories + { + public static string GetName(PLANECATEGORY cat) + { + return Enum.GetName(typeof(PLANECATEGORY), cat); + } + + public static string[] GetNames() + { + return Enum.GetNames(typeof(PLANECATEGORY)); + } + + public static string[] GetNamesExceptNone() + { + PLANECATEGORY[] bandsexceptnone = GetValuesExceptNone(); + string[] namesexceptnone = new string[bandsexceptnone.Length]; + for (int i = 0; i < bandsexceptnone.Length; i++) + namesexceptnone[i] = GetName(bandsexceptnone[i]); + return namesexceptnone; + } + + public static PLANECATEGORY[] GetValues() + { + return (PLANECATEGORY[])Enum.GetValues(typeof(PLANECATEGORY)); + } + + public static PLANECATEGORY[] GetValuesExceptNone() + { + PLANECATEGORY[] cats = (PLANECATEGORY[])Enum.GetValues(typeof(PLANECATEGORY)); + PLANECATEGORY[] catsexceptnone = new PLANECATEGORY[cats.Length - 1]; + int i = 0; + foreach (PLANECATEGORY cat in cats) + { + if (cat != PLANECATEGORY.NONE) + { + catsexceptnone[i] = cat; + i++; + } + } + return catsexceptnone; + } + + public static string GetStringValue(PLANECATEGORY cat) + { + string output = null; + FieldInfo fi = typeof(PLANECATEGORY).GetField(cat.ToString()); + StringCustomAttribute[] attrs = fi.GetCustomAttributes(typeof(StringCustomAttribute), false) as StringCustomAttribute[]; + if (attrs.Length > 0) + { + output = attrs[0].Value; + } + return output; + } + + public static string GetShortStringValue (PLANECATEGORY cat) + { + string output = null; + FieldInfo fi = typeof(PLANECATEGORY).GetField(cat.ToString()); + StringCustomAttribute[] attrs = fi.GetCustomAttributes(typeof(StringCustomAttribute), false) as StringCustomAttribute[]; + if (attrs.Length > 0) + { + output = attrs[0].Value.Substring(0, 1).ToUpper(); + } + return output; + } + + public static string[] GetStringValues() + { + List cats = new List(); + foreach (PLANECATEGORY cat in Enum.GetValues(typeof(PLANECATEGORY))) + { + cats.Add(GetStringValue(cat)); + } + if (cats.Count > 0) + return cats.ToArray(); + return null; + } + + public static string[] GetStringValuesExceptNone() + { + List cats = new List(); + foreach (PLANECATEGORY cat in Enum.GetValues(typeof(PLANECATEGORY))) + { + if (cat != PLANECATEGORY.NONE) + cats.Add(GetStringValue(cat)); + } + if (cats.Count > 0) + return cats.ToArray(); + return null; + } + + public static PLANECATEGORY ParseStringValue(string catstr) + { + foreach (PLANECATEGORY c in PlaneCategories.GetValuesExceptNone()) + { + if (PlaneCategories.GetStringValue(c) == catstr) + return c; + + } + return PLANECATEGORY.NONE; + } + + } +} diff --git a/AirScout.Aircrafts/PlaneInfo.cs b/AirScout.Aircrafts/PlaneInfo.cs new file mode 100644 index 0000000..c0e58a1 --- /dev/null +++ b/AirScout.Aircrafts/PlaneInfo.cs @@ -0,0 +1,398 @@ +using System; +using System.Collections.Generic; +using System.Collections; +using System.Linq; +using System.Text; +using System.Threading; +using System.ComponentModel; +using System.Globalization; +using System.Net; +using System.IO; +using System.Text.RegularExpressions; +using System.Data; +using ScoutBase.Core; +using System.Reflection; + +namespace AirScout.Aircrafts +{ + + // holds the complete aircraft info + public class PlaneInfo + { + public DateTime Time { get; set; } + public string Call { get; set; } + public string Reg { get; set; } + public string Hex { get; set; } + public double Lat { get; set; } + public double Lon { get; set; } + private double _Alt; + public double Alt + { + get + { + return _Alt; + } + set + { + _Alt = value; + _Alt_m = UnitConverter.ft_m(value); + } + } + private double _Alt_m; + public double Alt_m + { + get + { + return _Alt_m; + } + } + public double Track = 0; + private double _Speed; + public double Speed + { + get + { + return _Speed; + } + set + { + _Speed = value; + _Speed_kmh = UnitConverter.kts_kmh(value); + } + } + private double _Speed_kmh; + public double Speed_kmh + { + get + { + return _Speed_kmh; + } + } + public string Type { get; set; } + public string Manufacturer { get; set; } + public string Model { get; set; } + public PLANECATEGORY Category { get; set; } + + public LatLon.GPoint IntPoint = null; + public double IntQRB = double.MaxValue; + public double AltDiff = 0; + public int Potential = 0; + public double Eps1 = 0; + public double Eps2 = 0; + public double Theta1 = 0; + public double Theta2 = 0; + public double Angle = 0; + public double Squint = 0; + public double SignalStrength = double.MinValue; + public bool Ambiguous = false; + public string Comment = ""; + + public PlaneInfo() + { + Time = DateTime.UtcNow; + Call = ""; + Reg = ""; + Hex = ""; + Lat = 0; + Lon = 0; + Alt = 0; + Track = 0; + Speed = 0; + Type = ""; + Manufacturer = ""; + Model = ""; + Category = PLANECATEGORY.NONE; + } + + public PlaneInfo(DateTime time, string call, string reg, string hex, double lat, double lon, double track, double alt, double speed, string type, string manufacturer, string model, PLANECATEGORY category) + { + Time = time.ToUniversalTime(); + Call = call; + Reg = reg; + Hex = hex; + Lat = lat; + Lon = lon; + Alt = alt; + Track = track; + Speed = speed; + Type = type; + Manufacturer = manufacturer; + Model = model; + Category = category; + } + + // LEGACY!!! + public PlaneInfo (DataRow row) + { + Time = SupportFunctions.UNIXTimeToDateTime(System.Convert.ToInt32(row[0])); + Call = (string)row[1]; + Reg = (string)row[2]; + Hex = (string)row[3]; + Lat = (double)row[4]; + Lon = (double)row[5]; + Track = System.Convert.ToInt32(row[6]); + Alt = System.Convert.ToInt32(row[7]); + Speed = System.Convert.ToInt32(row[8]); + Type = (string)row[9]; + Manufacturer = (string)row[10]; + Model = (string)row[11]; + Category = (PLANECATEGORY)row[12]; + } + + public PlaneInfo (PlaneInfo plane) + { + this.Ambiguous = plane.Ambiguous; + this.Alt = plane.Alt; + this.AltDiff = plane.AltDiff; + this.Angle = plane.Angle; + this.Call = plane.Call; + this.Category = plane.Category; + this.Comment = plane.Comment; + this.Eps1 = plane.Eps1; + this.Eps2 = plane.Eps2; + this.Theta1 = plane.Theta1; + this.Theta2 = plane.Theta2; + this.Hex = plane.Hex; + this.IntPoint = plane.IntPoint; + this.IntQRB = plane.IntQRB; + this.Lat = plane.Lat; + this.Lon = plane.Lon; + this.Manufacturer = plane.Manufacturer; + this.Model = plane.Model; + this.Potential = plane.Potential; + this.Reg = plane.Reg; + this.SignalStrength = plane.SignalStrength; + this.Speed = plane.Speed; + this.Squint = plane.Squint; + this.Time = plane.Time; + this.Track = plane.Track; + this.Type = plane.Type; + + } + + public override string ToString() + { + string s = ""; + PropertyInfo[] properties = this.GetType().GetProperties(); + foreach (PropertyInfo p in properties) + { + if (p.PropertyType.Name.ToUpper() == "STRING") + { + string v = (string)p.GetValue(this, null); + if (v == null) + v = "[null]"; + else if (v.Length == 0) + v = "[empty]"; + s = s + p.Name + ": " + v + "\n"; + } + else if (p.PropertyType.Name.ToUpper() == "DATETIME") + { + DateTime dt = (DateTime)p.GetValue(this, null); + s = s + p.Name + ": " + dt.ToString("yyyy-MM-dd HH:mm:ss") + "\n"; + } + else + { + object o = p.GetValue(this, null); + if (o == null) + s = s + p.Name + ": " + "[null]" + "\n"; + else + s = s + p.Name + ": " + o.ToString() + "\n"; + } + } + return s; + } + + public static bool Check_Hex(string hex) + { + if (String.IsNullOrEmpty(hex)) + return false; + hex = hex.Replace("\"", String.Empty).ToUpper().Trim(); + // Hex must be a 6 character value + if (hex.Length != 6) + return false; + if (hex.ToCharArray().Any(c => !"0123456789ABCDEF".Contains(c))) + return false; + try + { + // try to convert to Hex value + long h = System.Convert.ToInt64(hex, 16); + // check boundaries + if ((h < 0) || (h > 16777215)) + return false; + } + catch (Exception ex) + { + Console.WriteLine("[" + System.Reflection.MethodBase.GetCurrentMethod().Name + "]" + ex.Message + ": " + hex); + return false; + } + return true; + } + + public static bool Check_Call(string call) + { + if (String.IsNullOrEmpty(call)) + return false; + if (String.IsNullOrWhiteSpace(call)) + return false; + if (call.Contains("[unknown]")) + return false; + call = call.Replace("\"", String.Empty).ToUpper().Trim(); + // check length + if (call.Length < 4) + return false; + if (call.Contains('-')) + return false; + string airline = ""; + // Type B callsign? + if (char.IsNumber(call[2])) + { + airline = call.Substring(0,2); + if (AircraftData.Database.AirlineFindByIATA(airline) == null) + return false; + } + // Type C callsign + if (!Char.IsLetter(call[0]) || !char.IsLetter(call[1])) + return false; + airline = call.Substring(0, 3); + if (AircraftData.Database.AirlineFindByICAO(airline) == null) + return false; + return true; + } + + public static bool Check_Reg(string reg) + { + if (String.IsNullOrEmpty(reg)) + return false; + if (String.IsNullOrWhiteSpace(reg)) + return false; + if (reg.Contains("[unknown]")) + return false; + reg = reg.Replace("\"", String.Empty).ToUpper().Trim(); + if (reg.Length < AircraftData.Database.AircraftRegistrationMinLength + 1) + return false; + if (!reg.Contains('-') && !reg.StartsWith("N")) + return false; + return true; + } + + public static bool Check_Lat(double lat) + { + if (lat < -90) + return false; + if (lat > 90) + return false; + return true; + } + + public static bool Check_Lon(double lon) + { + if (lon < -180) + return false; + if (lon > 180) + return false; + return true; + } + + public static bool Check_Alt(double alt) + { + if (alt < 0) + return false; + if (alt > 100000.0) + return false; + return true; + } + + public static bool Check_Track(double track) + { + if (track < 0) + return false; + if (track >= 360) + return false; + return true; + } + + + public static bool Check_Speed(double speed) + { + if (speed < 0) + return false; + if (speed > 800) + return false; + return true; + } + + public static bool Check_Type(string type) + { + if (String.IsNullOrEmpty(type)) + return false; + if (String.IsNullOrWhiteSpace(type)) + return false; + if (type.Contains("[unknown]")) + return false; + type = type.Replace("\"", String.Empty).ToUpper().Trim(); + // check for alphanumeric values only + if (!type.All(char.IsLetterOrDigit)) + return false; + if (type.Length < AircraftData.Database.AircraftTypeICAOMinLength) + return false; + return true; + } + + public static bool Check_Manufacturer(string manufacturer) + { + if (String.IsNullOrEmpty(manufacturer)) + return false; + return true; + } + + public static bool Check_Model(string model) + { + if (String.IsNullOrEmpty(model)) + return false; + return true; + } + + public static bool Check(PlaneInfo info) + { + // checks a PlaneInfo object + // returns TRUE if contains all mandantory data + // get a plane info converter for plausiblity check + PlaneInfoConverter C = new PlaneInfoConverter(); + // check hex + if (String.IsNullOrEmpty(C.To_Hex(info.Hex))) + return false; + // check call + if (String.IsNullOrEmpty(C.To_Call(info.Call, false))) + return false; + if (info.Time == null) + return false; + if (info.Time == DateTime.MinValue) + return false; + if (info.Time == DateTime.MaxValue) + return false; + if (info.Lat > 90) + return false; + if (info.Lat < -90) + return false; + if (info.Lon > 180) + return false; + if (info.Lon < -180) + return false; + if (info.Alt < 0) + return false; + if (info.Alt > 100000) + return false; + if (info.Track > 360) + return false; + if (info.Track < 0) + return false; + if (info.Speed < 0) + return false; + if (info.Speed > 5000) + return false; + return true; + } + + } + +} \ No newline at end of file diff --git a/AirScout.Aircrafts/PlaneInfoCache.cs b/AirScout.Aircrafts/PlaneInfoCache.cs new file mode 100644 index 0000000..fd5ecde --- /dev/null +++ b/AirScout.Aircrafts/PlaneInfoCache.cs @@ -0,0 +1,169 @@ +using ScoutBase.Core; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace AirScout.Aircrafts +{ + public class PlaneInfoCache : SortedDictionary + { + + public int InsertOrUpdateIfNewer (PlaneInfo plane) + { + int i = 0; + if (plane == null) + return i; + lock (this) + { + PlaneInfo oldplane = null; + if (!this.TryGetValue(plane.Hex, out oldplane)) + { + // add plane + this.Add(plane.Hex, plane); + i = 1; + } + else + { + // plane already in cache --> check time and update if newer + if (plane.Time > oldplane.Time) + { + oldplane.Alt = plane.Alt; + oldplane.AltDiff = plane.AltDiff; + oldplane.Angle = plane.Angle; + oldplane.Call = plane.Call; + oldplane.Category = plane.Category; + oldplane.Comment = plane.Comment; + oldplane.Eps1 = plane.Eps1; + oldplane.Eps2 = plane.Eps2; + oldplane.Theta1 = plane.Theta1; + oldplane.Theta2 = plane.Theta2; + oldplane.IntPoint = plane.IntPoint; + oldplane.IntQRB = plane.IntQRB; + oldplane.Lat = plane.Lat; + oldplane.Lon = plane.Lon; + oldplane.Manufacturer = plane.Manufacturer; + oldplane.Model = plane.Model; + oldplane.Potential = plane.Potential; + oldplane.Reg = plane.Reg; + oldplane.SignalStrength = plane.SignalStrength; + oldplane.Speed = plane.Speed; + oldplane.Squint = plane.Squint; + oldplane.Time = plane.Time; + oldplane.Track = plane.Track; + oldplane.Type = plane.Type; + i = 1; + } + } + } + return i; + } + + public int BulkInsertOrUpdateIfNewer (List planes) + { + int i = 0; + if (planes == null) + return i; + if (planes.Count == 0) + return i; + lock (this) + { + foreach (PlaneInfo plane in planes) + { + int j = InsertOrUpdateIfNewer(plane); + i = i + j; + } + } + return i; + } + + public int Delete (PlaneInfo plane) + { + int i = 0; + PlaneInfo oldplane = null; + if (this.TryGetValue(plane.Hex, out oldplane)) + { + this.Remove(plane.Hex); + i = 1; + } + return i; + } + + public int BulkDelete (List planes) + { + int i = 0; + if (planes == null) + return i; + if (planes.Count == 0) + return i; + lock (this) + { + foreach (PlaneInfo plane in planes) + { + int j = Delete(plane); + i = i + j; + } + } + return i; + } + + public PlaneInfo Get(string hex, DateTime at, int ttl) + { + PlaneInfo plane = null; + DateTime to = at; + DateTime from = to - new TimeSpan(0, ttl, 0); + // return null if not found + if (!this.TryGetValue(hex, out plane)) + return null; + // return null if not in timespan + if ((plane.Time < from) || (plane.Time > to)) + return null; + // create new plane info + PlaneInfo info = new PlaneInfo(plane); + // estimate new position + // change speed to km/h + double speed = info.Speed_kmh; + // calculate distance after timespan + double dist = speed * (at - info.Time).TotalHours; + // estimate new position + LatLon.GPoint newpos = LatLon.DestinationPoint(info.Lat, info.Lon, info.Track, dist); + info.Lat = newpos.Lat; + info.Lon = newpos.Lon; + info.Time = at; + return info; + } + + public List GetAll(DateTime at, int ttl) + { + List l = new List(); + DateTime to = at; + DateTime from = to - new TimeSpan(0, ttl, 0); + lock (this) + { + foreach (PlaneInfo plane in this.Values) + { + if ((plane.Time < from) || (plane.Time > to)) + continue; + // create new plane info + PlaneInfo info = new PlaneInfo(plane); + // estimate new position + // change speed to km/h + double speed = info.Speed_kmh; + // calculate distance after timespan + double dist = speed * (at - info.Time).TotalHours; + // estimate new position + LatLon.GPoint newpos = LatLon.DestinationPoint(info.Lat, info.Lon, info.Track, dist); + double d = LatLon.Distance(info.Lat, info.Lon, newpos.Lat, newpos.Lon); + Console.WriteLine(d); + if (d > 100) + Console.WriteLine("Error"); + info.Lat = newpos.Lat; + info.Lon = newpos.Lon; + info.Time = at; + l.Add(info); + } + } + return l; + } + } +} diff --git a/AirScout.Aircrafts/PlaneInfoConverter.cs b/AirScout.Aircrafts/PlaneInfoConverter.cs new file mode 100644 index 0000000..97ff1ef --- /dev/null +++ b/AirScout.Aircrafts/PlaneInfoConverter.cs @@ -0,0 +1,434 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Globalization; +using AirScout.Aircrafts; +using ScoutBase.Core; + +namespace AirScout.Aircrafts +{ + public class PlaneInfoConverter + { + + public PlaneInfoConverter() + { + } + + private bool IsHex(string s) + { + bool b; + try + { + b = !s.ToCharArray().Any(c => !"0123456789abcdefABCDEF".Contains(c)); + } + catch (Exception ex) + { + b = false; + } + return b; + } + + private bool IsInt(string s) + { + bool b; + try + { + b = !s.ToCharArray().Any(c => !"0123456789".Contains(c)); + } + catch (Exception ex) + { + b = false; + } + return b; + } + + private bool IsDouble(string s) + { + bool b; + try + { + // double should contain only following chars + b = !s.ToCharArray().Any(c => !"+-.,E0123456789".Contains(c)); + // double must contain a decimal separator + if (b) + { + b = s.Contains(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator); + } + } + catch (Exception ex) + { + b = false; + } + return b; + } + + private bool IsOct(string s) + { + bool b; + try + { + b = !s.ToCharArray().Any(c => !"01234567".Contains(c)); + } + catch (Exception ex) + { + b = false; + } + return b; + } + + + public DateTime To_UTC(string s) + { + if (String.IsNullOrEmpty(s)) + return DateTime.MinValue; + s = s.Replace("\"", String.Empty).Trim(); + // UTC must be a 10 character value + if (s.Length < 10) + return DateTime.MinValue; + // try to convert UNIX time first + if (IsInt(s)) + { + try + { + // try to convert to UTC timestamp + long l = System.Convert.ToInt64(s); + DateTime timestamp = new System.DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + timestamp = timestamp.AddSeconds(l); + return timestamp; + } + catch (Exception ex) + { + Console.WriteLine("[" + System.Reflection.MethodBase.GetCurrentMethod().Name + "]" + ex.Message + ": " + s); + } + } + // check for Standard DateTime notation + try + { + // try to convert to UTC timestamp + DateTime timestamp; + if (DateTime.TryParse(s, out timestamp)) + return timestamp; + } + catch (Exception ex) + { + Console.WriteLine("[" + System.Reflection.MethodBase.GetCurrentMethod().Name + "]" + ex.Message + ": " + s); + } + return DateTime.MinValue; + } + + public string To_Hex(string s) + { + if (String.IsNullOrEmpty(s)) + return null; + s = s.Replace("\"", String.Empty).ToUpper().Trim(); + // Hex must be a 6 character value + if (s.Length != 6) + return null; + if (!IsHex(s)) + return null; + try + { + // try to convert to Hex value + long hex = System.Convert.ToInt64(s, 16); + // check boundaries + if ((hex < 0) || (hex > 16777215)) + return null; + return s; + } + catch (Exception ex) + { + Console.WriteLine("[" + System.Reflection.MethodBase.GetCurrentMethod().Name + "]" + ex.Message + ": " + s); + } + return null; + } + + public string To_Reg(string s) + { + if (String.IsNullOrEmpty(s)) + return null; + s = s.Replace("\"", String.Empty).ToUpper().Trim(); + if (s.Length < AircraftData.Database.AircraftRegistrationMinLength + 1) + return null; + if (!s.Contains('-') && !s.StartsWith("N")) + return null; + try + { + // try to find the registration string in aircraft registration database + AircraftRegistrationDesignator reg = AircraftData.Database.AircraftRegistrationFindByReg(s); + if (reg != null) + return s; + } + catch (Exception ex) + { + Console.WriteLine("[" + System.Reflection.MethodBase.GetCurrentMethod().Name + "]" + ex.Message + ": " + s); + } + return null; + } + + public string To_Flight(string s) + { + if (String.IsNullOrEmpty(s)) + return null; + s = s.Replace("\"", String.Empty).ToUpper().Trim(); + if (s.Length < 3) + return null; + string iata = s.Substring(0, 2); + string flightnumber = s.Substring(2); + if (!IsInt(flightnumber)) + return null; + try + { + // try to find the string in IATA airline database + AirlineDesignator airline = AircraftData.Database.AirlineFindByIATA(iata); + if (airline != null) + return s; + } + catch (Exception ex) + { + Console.WriteLine("[" + System.Reflection.MethodBase.GetCurrentMethod().Name + "]" + ex.Message + ": " + s); + } + return null; + } + + public string To_Type(string s, bool checktype = true) + { + if (String.IsNullOrEmpty(s)) + return null; + s = s.Replace("\"", String.Empty).ToUpper().Trim(); + // check for alphanumeric values only + if (!s.All(char.IsLetterOrDigit)) + return null; + if (s.Length < AircraftData.Database.AircraftTypeICAOMinLength) + return null; + try + { + if (!checktype) + return s; + // try to find the string in aircraft registration database + AircraftTypeDesignator type = AircraftData.Database.AircraftTypeFindByICAO(s); + if (type != null) + return s; + } + catch (Exception ex) + { + Console.WriteLine("[" + System.Reflection.MethodBase.GetCurrentMethod().Name + "]" + ex.Message + ": " + s); + } + return null; + } + + public string To_Radar(string s) + { + if (String.IsNullOrEmpty(s)) + return null; + s = s.Replace("\"", String.Empty).ToUpper().Trim(); + if (s.Length < 3) + return null; + try + { + // Radars will have a letter in first char and "-" as second char + if (Char.IsLetter(s[0]) && (s[1] == '-')) + return s; + } + catch (Exception ex) + { + Console.WriteLine("[" + System.Reflection.MethodBase.GetCurrentMethod().Name + "]" + ex.Message + ": " + s); + } + return null; + } + + public string To_Route(string s) + { + if (String.IsNullOrEmpty(s)) + return null; + s = s.Replace("\"", String.Empty).ToUpper().Trim(); + // Routes will have a "-" between two IATA airport codes + if (s.Length < 9) + return null; + int index = s.IndexOf('-'); + if ((index < 3) || (index > s.Length - 3)) + return null; + string from = s.Substring(0, index - 1); + string to = s.Substring(index + 1); + try + { + if (AircraftData.Database.AirportFindByICAO(from) == null) + return null; + if (AircraftData.Database.AirportFindByICAO(to) == null) + return null; + return s; + } + catch (Exception ex) + { + Console.WriteLine("[" + System.Reflection.MethodBase.GetCurrentMethod().Name + "]" + ex.Message + ": " + s); + } + return null; + } + + public string To_Call(string s, bool checkairline = true) + { + if (String.IsNullOrEmpty(s)) + return null; + s = s.Replace("\"", String.Empty).ToUpper().Trim(); + // check length + if (s.Length < 4) + return null; + if (s.Contains('-')) + return null; + // check for numeric flight number + string icao = s.Substring(0, 3); + try + { + if (!checkairline) + return s; + // try to find the string in aircraft registration database + AirlineDesignator airline = AircraftData.Database.AirlineFindByICAO(icao); + if (airline != null) + return s; + } + catch (Exception ex) + { + Console.WriteLine("[" + System.Reflection.MethodBase.GetCurrentMethod().Name + "]" + ex.Message + ": " + s); + } + return null; + } + + public int To_Squawk(string s) + { + s = s.Replace("\"", String.Empty).ToUpper().Trim(); + // check length + if (s.Length != 4) + return int.MinValue; + if (!IsOct(s)) + return int.MinValue; + try + { + // check for octal number + // try to convert to Hex value + long oct = System.Convert.ToInt64(s, 8); + // check boundaries + if ((oct >= 0) && (oct <= 4095)) + return (int)oct; + } + catch (Exception ex) + { + Console.WriteLine("[" + System.Reflection.MethodBase.GetCurrentMethod().Name + "]" + ex.Message + ": " + s); + } + return int.MinValue; + } + + public double To_Lat(string s) + { + s = s.Replace("\"", String.Empty).Trim(); + if (s.Length < 3) + return double.MinValue; + // double Lon must contain a decimal separator + if (s.IndexOf(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator) < 0) + return double.MinValue; + try + { + // try to convert to double + double d = System.Convert.ToDouble(s); + // check bounds + if ((d >= -90.0) && (d <= 90.0)) + return d; + } + catch (Exception ex) + { + Console.WriteLine("[" + System.Reflection.MethodBase.GetCurrentMethod().Name + "]" + ex.Message + ": " + s); + } + return double.MinValue; + } + + + public double To_Lon(string s) + { + s = s.Replace("\"", String.Empty).Trim(); + if (s.Length < 3) + return double.MinValue; + // double Lon must contain a decimal separator + if (s.IndexOf(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator) < 0) + return double.MinValue; + try + { + // try to convert to double + double d = System.Convert.ToDouble(s); + // check bounds + if ((d >= -180.0) && (d <= 180.0)) + return d; + } + catch (Exception ex) + { + Console.WriteLine("[" + System.Reflection.MethodBase.GetCurrentMethod().Name + "]" + ex.Message + ": " + s); + } + return double.MinValue; + } + + + public int To_Alt(string s) + { + s = s.Replace("\"", String.Empty).Trim(); + if (s.Length > 5) + return int.MinValue; + if (!IsInt(s)) + return int.MinValue; + try + { + // try to convert to integer + long alt = System.Convert.ToInt64(s); + // check bounds + if ((alt >= 0) && (alt <= 100000)) + return (int)alt; + } + catch (Exception ex) + { + Console.WriteLine("[" + System.Reflection.MethodBase.GetCurrentMethod().Name + "]" + ex.Message + ": " + s); + } + return int.MinValue; + } + + public int To_Track(string s) + { + s = s.Replace("\"", String.Empty).Trim(); + if (s.Length > 3) + return int.MinValue; + if (!IsInt(s)) + return int.MinValue; + try + { + // try to convert to integer + long track = System.Convert.ToInt64(s); + // check bounds + if ((track >= 0) && (track < 360)) + return (int)track; + } + catch (Exception ex) + { + Console.WriteLine("[" + System.Reflection.MethodBase.GetCurrentMethod().Name + "]" + ex.Message + ": " + s); + } + return int.MinValue; + } + + public int To_Speed(string s) + { + s = s.Replace("\"", String.Empty).Trim(); + if (s.Length > 3) + return int.MinValue; + if (!IsInt(s)) + return int.MinValue; + try + { + // try to convert to integer + long speed = System.Convert.ToInt64(s); + // check bounds + if ((speed >= 0) && (speed <= 800)) + return (int)speed; + } + catch (Exception ex) + { + Console.WriteLine("[" + System.Reflection.MethodBase.GetCurrentMethod().Name + "]" + ex.Message + ": " + s); + } + return int.MinValue; + } + } +} diff --git a/AirScout.Aircrafts/Properties/AssemblyInfo.cs b/AirScout.Aircrafts/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..c38a19b --- /dev/null +++ b/AirScout.Aircrafts/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Allgemeine Informationen über eine Assembly werden über die folgenden +// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, +// die einer Assembly zugeordnet sind. +[assembly: AssemblyTitle("AirScout.Aircrafts")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("DL2ALF")] +[assembly: AssemblyProduct("AirScout")] +[assembly: AssemblyCopyright("Copyright © 2018 DL2ALF")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar +// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von +// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen. +[assembly: ComVisible(false)] + +// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird +[assembly: Guid("288a26ec-b690-41a2-84e5-61c9b7b74046")] + +// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: +// +// Hauptversion +// Nebenversion +// Buildnummer +// Revision +// +// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern +// übernehmen, indem Sie "*" eingeben: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.3.0.0")] +[assembly: AssemblyFileVersion("1.3.0.0")] diff --git a/AirScout.Aircrafts/Properties/Settings.Designer.cs b/AirScout.Aircrafts/Properties/Settings.Designer.cs new file mode 100644 index 0000000..fccce2a --- /dev/null +++ b/AirScout.Aircrafts/Properties/Settings.Designer.cs @@ -0,0 +1,98 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.42000 +// +// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn +// der Code erneut generiert wird. +// +//------------------------------------------------------------------------------ + +namespace AirScout.Aircrafts.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] + public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string Database_Directory { + get { + return ((string)(this["Database_Directory"])); + } + set { + this["Database_Directory"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Database_InMemory { + get { + return ((bool)(this["Database_InMemory"])); + } + set { + this["Database_InMemory"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string Aircrafts_DataPath { + get { + return ((string)(this["Aircrafts_DataPath"])); + } + set { + this["Aircrafts_DataPath"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://www.airscout.eu/downloads/AirScout/1/AircraftData/")] + public string Aircrafts_UpdateURL { + get { + return ((string)(this["Aircrafts_UpdateURL"])); + } + set { + this["Aircrafts_UpdateURL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("60")] + public int Database_BackgroundUpdate_Period { + get { + return ((int)(this["Database_BackgroundUpdate_Period"])); + } + set { + this["Database_BackgroundUpdate_Period"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://www.airscout.eu/downloads/airscout.pwd")] + public string Aircrafts_PasswordURL { + get { + return ((string)(this["Aircrafts_PasswordURL"])); + } + set { + this["Aircrafts_PasswordURL"] = value; + } + } + } +} diff --git a/AirScout.Aircrafts/Properties/Settings.settings b/AirScout.Aircrafts/Properties/Settings.settings new file mode 100644 index 0000000..e2d8590 --- /dev/null +++ b/AirScout.Aircrafts/Properties/Settings.settings @@ -0,0 +1,24 @@ + + + + + + + + + False + + + + + + http://www.airscout.eu/downloads/AirScout/1/AircraftData/ + + + 60 + + + http://www.airscout.eu/downloads/airscout.pwd + + + \ No newline at end of file diff --git a/AirScout.Aircrafts/app.config b/AirScout.Aircrafts/app.config new file mode 100644 index 0000000..28d6bbb --- /dev/null +++ b/AirScout.Aircrafts/app.config @@ -0,0 +1,30 @@ + + + + +
+ + + + + + + + + False + + + + + + http://www.airscout.eu/downloads/AirScout/1/AircraftData/ + + + 60 + + + http://www.airscout.eu/downloads/airscout.pwd + + + + \ No newline at end of file diff --git a/AirScout.Aircrafts/packages.config b/AirScout.Aircrafts/packages.config new file mode 100644 index 0000000..0aee17e --- /dev/null +++ b/AirScout.Aircrafts/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/AirScout.Core/AirScout.Core.csproj b/AirScout.Core/AirScout.Core.csproj new file mode 100644 index 0000000..f153113 --- /dev/null +++ b/AirScout.Core/AirScout.Core.csproj @@ -0,0 +1,86 @@ + + + + + Debug + AnyCPU + {41B66BE4-6086-4AE3-BE31-C81EE6B10154} + Library + Properties + AirScout.Core + AirScout.Core + v4.0 + 512 + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + ..\packages\System.Data.SQLite.Core.1.0.110.0\lib\net40\System.Data.SQLite.dll + True + + + + + + + + + + + + + True + True + Settings.settings + + + + + {ee86e933-d883-4b18-80eb-0fba55ec67c6} + ScoutBase.Core + + + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + + + Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}". + + + + + \ No newline at end of file diff --git a/AirScout.Core/BandSettings.cs b/AirScout.Core/BandSettings.cs new file mode 100644 index 0000000..5ccc565 --- /dev/null +++ b/AirScout.Core/BandSettings.cs @@ -0,0 +1,153 @@ +using ScoutBase.Core; +using System; +using System.Collections.Generic; +using System.Data; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; + +namespace AirScout.Core +{ + public class BandSetting + { + public double K_Factor; + public double F1_Clearance; + public double GroundClearance; + public double MaxDistance; + public double MaxSquint; + public double MaxElevation; + + public BandSetting() + { + K_Factor = 1.33; + F1_Clearance = 0.6; + GroundClearance = 0; + MaxDistance = 10; + MaxSquint = double.MaxValue; + MaxElevation = double.MaxValue; + } + } + + [Serializable] + public class BandSettings : DataTable + { + + public BandSettings() : base("BANDSETTINGS") + { + this.Columns.Add("BAND"); + this.Columns.Add("K-FACTOR", typeof(double)); + this.Columns.Add("F1-CLEARANCE", typeof(double)); + this.Columns.Add("GROUNDCLEARANCE", typeof(double)); + this.Columns.Add("MAXDISTANCE", typeof(double)); + this.Columns.Add("MAXSQUINT", typeof(double)); + this.Columns.Add("MAXELEVATION", typeof(double)); + DataColumn[] keys = new DataColumn[1]; + keys[0] = this.Columns["BAND"]; + this.PrimaryKey = keys; + } + + public BandSettings(bool generatedefault) : base("BANDSETTINGS") + { + this.Columns.Add("BAND"); + this.Columns.Add("K-FACTOR", typeof(double)); + this.Columns.Add("F1-CLEARANCE", typeof(double)); + this.Columns.Add("GROUNDCLEARANCE", typeof(double)); + this.Columns.Add("MAXDISTANCE", typeof(double)); + this.Columns.Add("MAXSQUINT", typeof(double)); + this.Columns.Add("MAXELEVATION", typeof(double)); + DataColumn[] keys = new DataColumn[1]; + keys[0] = this.Columns["BAND"]; + this.PrimaryKey = keys; + if (generatedefault) + GenerateDefault(); + } + + private void GenerateDefault() + { + if (this.Rows.Count > 0) + return; + // generate default rows + BAND[] bands = Bands.GetValuesExceptNoneAndAll(); + foreach (BAND band in bands) + { + if (band != BAND.BNONE) + { + DataRow row; + row = this.NewRow(); + row["BAND"] = Bands.GetStringValue(band); + row["GROUNDCLEARANCE"] = Properties.Settings.Default.Path_Default_Ground_Clearance; + row["MAXDISTANCE"] = Properties.Settings.Default.Path_Default_Max_Distance; + row["MAXSQUINT"] = Properties.Settings.Default.Path_Default_Max_Squint; + row["MAXELEVATION"] = Properties.Settings.Default.Path_Default_Max_Elevation; + // adjust values starting with V1.3.0.4 + switch (band) + { + case BAND.B50M: + { + row["K-FACTOR"] = 1.6; + row["F1-CLEARANCE"] = 0.1; + break; + } + case BAND.B70M: + { + row["K-FACTOR"] = 1.6; + row["F1-CLEARANCE"] = 0.1; + break; + } + case BAND.B144M: + { + row["K-FACTOR"] = 1.5; + row["F1-CLEARANCE"] = 0.2; + break; + } + case BAND.B432M: + { + row["K-FACTOR"] = 1.4; + row["F1-CLEARANCE"] = 0.4; + break; + } + default: + { + row["K-FACTOR"] = Properties.Settings.Default.Path_Default_K_Factor; + row["F1-CLEARANCE"] = Properties.Settings.Default.Path_Default_F1_Clearance; + break; + } + } + this.Rows.Add(row); + } + } + } + + public BandSetting this[BAND band] + { + get + { + DataRow row = this.Rows.Find(Bands.GetStringValue(band)); + if (row != null) + { + BandSetting setting = new BandSetting(); + try + { + // fill in the values from the bandsettings table + setting.K_Factor = (double)row["K-FACTOR"]; + setting.F1_Clearance = (double)row["F1-CLEARANCE"]; + setting.GroundClearance = (double)row["GROUNDCLEARANCE"]; + setting.MaxDistance = (double)row["MAXDISTANCE"]; + setting.MaxSquint = (double)row["MAXSQUINT"]; + setting.MaxElevation = (double)row["MAXELEVATION"]; + } + catch + { + } + return setting; + } + else + { + BandSetting setting = new BandSetting(); + return setting; + } + } + } + } +} diff --git a/AirScout.Core/Enums.cs b/AirScout.Core/Enums.cs new file mode 100644 index 0000000..ef0994e --- /dev/null +++ b/AirScout.Core/Enums.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace AirScout.Core +{ + public enum AIRSCOUTPATHMODE + { + NONE = 0, + SINGLE = 1, + MULTI = 2 + } + + public enum AIRSCOUTLIFEMODE + { + NONE = 0, + LIFE = 1, + HISTORY = 2 + } + + public enum AIRSCOUTPLAYMODE + { + NONE = 0, + PAUSE = 1, + FASTBACK = 2, + BACK = 3, + FORWARD = 4, + FASTFORWARD = 5 + } + +} diff --git a/AirScout.Core/Properties/AssemblyInfo.cs b/AirScout.Core/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..18b19b0 --- /dev/null +++ b/AirScout.Core/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Allgemeine Informationen über eine Assembly werden über die folgenden +// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, +// die einer Assembly zugeordnet sind. +[assembly: AssemblyTitle("AirScout.Core")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("AirScout.Core")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar +// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von +// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen. +[assembly: ComVisible(false)] + +// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird +[assembly: Guid("41b66be4-6086-4ae3-be31-c81ee6b10154")] + +// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: +// +// Hauptversion +// Nebenversion +// Buildnummer +// Revision +// +// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern +// übernehmen, indem Sie "*" eingeben: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/AirScout.Core/Properties/Settings.Designer.cs b/AirScout.Core/Properties/Settings.Designer.cs new file mode 100644 index 0000000..fab2572 --- /dev/null +++ b/AirScout.Core/Properties/Settings.Designer.cs @@ -0,0 +1,98 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.42000 +// +// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn +// der Code erneut generiert wird. +// +//------------------------------------------------------------------------------ + +namespace AirScout.Core.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public double Path_Default_Ground_Clearance { + get { + return ((double)(this["Path_Default_Ground_Clearance"])); + } + set { + this["Path_Default_Ground_Clearance"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0.6")] + public double Path_Default_F1_Clearance { + get { + return ((double)(this["Path_Default_F1_Clearance"])); + } + set { + this["Path_Default_F1_Clearance"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1.33")] + public double Path_Default_K_Factor { + get { + return ((double)(this["Path_Default_K_Factor"])); + } + set { + this["Path_Default_K_Factor"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("10")] + public double Path_Default_Max_Distance { + get { + return ((double)(this["Path_Default_Max_Distance"])); + } + set { + this["Path_Default_Max_Distance"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public double Path_Default_Max_Squint { + get { + return ((double)(this["Path_Default_Max_Squint"])); + } + set { + this["Path_Default_Max_Squint"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public double Path_Default_Max_Elevation { + get { + return ((double)(this["Path_Default_Max_Elevation"])); + } + set { + this["Path_Default_Max_Elevation"] = value; + } + } + } +} diff --git a/AirScout.Core/Properties/Settings.settings b/AirScout.Core/Properties/Settings.settings new file mode 100644 index 0000000..9f57ea8 --- /dev/null +++ b/AirScout.Core/Properties/Settings.settings @@ -0,0 +1,24 @@ + + + + + + 0 + + + 0.6 + + + 1.33 + + + 10 + + + 0 + + + 0 + + + \ No newline at end of file diff --git a/AirScout.Core/app.config b/AirScout.Core/app.config new file mode 100644 index 0000000..244123e --- /dev/null +++ b/AirScout.Core/app.config @@ -0,0 +1,30 @@ + + + + +
+ + + + + + 0 + + + 0.6 + + + 1.33 + + + 10 + + + 0 + + + 0 + + + + \ No newline at end of file diff --git a/AirScout.Core/packages.config b/AirScout.Core/packages.config new file mode 100644 index 0000000..8ce13a2 --- /dev/null +++ b/AirScout.Core/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/AirScout.PlaneFeeds/ADSB.cs b/AirScout.PlaneFeeds/ADSB.cs new file mode 100644 index 0000000..87f1b49 --- /dev/null +++ b/AirScout.PlaneFeeds/ADSB.cs @@ -0,0 +1,487 @@ +using System; +using System.Collections.Generic; +using System.Collections; +using System.Linq; +using System.Text; +using System.Threading; +using System.ComponentModel; +using System.Windows; +using System.Globalization; +using System.Net; +using System.Net.Sockets; +using System.IO; +using AirScout.Aircrafts; +using AirScout.PlaneFeeds.Generic; +using LibADSB; +using ScoutBase.Core; + +namespace AirScout.PlaneFeeds +{ + public class ADSBMessage + { + public string RawMessage = ""; + public DateTime TimeStamp = DateTime.UtcNow; + public int SignalStrength = 0; + } + + public class PlaneFeedSettings_ADSB + { + [Browsable(true)] + [DescriptionAttribute("Server address for raw ADS-B data.\nUse localhost for running on the same machine.")] + public virtual string Server + { + get + { + return Properties.Settings.Default.ADSB_Server; + } + set + { + Properties.Settings.Default.ADSB_Server = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(true)] + [DescriptionAttribute("Server port for raw ADS-B data.\nADSBSharp.exe: Port 47806\nRTL1090: Port 31001")] + public virtual int Port + { + get + { + return Properties.Settings.Default.ADSB_Port; + } + set + { + Properties.Settings.Default.ADSB_Port = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(true)] + [DescriptionAttribute("Interval for updating ADS-B data [sec].")] + public virtual int Interval + { + get + { + return Properties.Settings.Default.ADSB_Interval; + } + set + { + Properties.Settings.Default.ADSB_Interval = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(true)] + [DescriptionAttribute("Use binary data format for ADS-B data.\nTrue: Use binary format (ADS Beast with MLAT)\nFalse: Use ASCII format (AVR with/without MLAT)")] + public virtual bool Binary + { + get + { + return Properties.Settings.Default.ADSB_Binary; + } + set + { + Properties.Settings.Default.ADSB_Binary = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(true)] + [DescriptionAttribute("Report ADS-B messages and show in status line.")] + public virtual bool ReportMessages + { + get + { + return Properties.Settings.Default.ADSB_Report_Messages; + } + set + { + Properties.Settings.Default.ADSB_Report_Messages = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(true)] + [DescriptionAttribute("Marks locally received aircrafts by adding '@' to the call sign")] + public virtual bool MarkLocal + { + get + { + return Properties.Settings.Default.ADSB_MarkLocal; + } + set + { + Properties.Settings.Default.ADSB_MarkLocal = value; + Properties.Settings.Default.Save(); + } + } + } + + public class PlaneFeed_ADSB : PlaneFeed + { + [Browsable(false)] + public override string Name + { + get + { + return Properties.Settings.Default.ADSB_Name; ; + } + protected set + { + Properties.Settings.Default.ADSB_Name = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(false)] + public override string Disclaimer + { + get + { + return Properties.Settings.Default.ADSB_Disclaimer; + } + protected set + { + Properties.Settings.Default.ADSB_Disclaimer = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(false)] + public override string DisclaimerAccepted + { + get + { + return Properties.Settings.Default.ADSB_Disclaimer_Accepted; + } + set + { + Properties.Settings.Default.ADSB_Disclaimer_Accepted = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(false)] + public override string Info + { + get + { + return Properties.Settings.Default.ADSB_Info; + } + protected set + { + Properties.Settings.Default.ADSB_Info = value; + Properties.Settings.Default.Save(); + } + } + + public new PlaneFeedSettings_ADSB FeedSettings = new PlaneFeedSettings_ADSB(); + + public PlaneFeed_ADSB() + : base () + { + HasSettings = true; + } + + private ADSBMessage ReceiveBinaryMsg(Stream stream) + { + // read Mode-S beast binary input + string ADSB = null; + int signal_strength = 0; + long nanosec = 0; + long daysec = 0; + DateTime timestamp = DateTime.UtcNow; + byte[] buffer = new byte[23]; + // wait for escape character + DateTime start = DateTime.UtcNow; + DateTime stop = DateTime.Now; + do + { + stream.Read(buffer, 0, 1); + // System.Console.WriteLine(BitConverter.ToString(buffer,0,1)); + if (buffer[0] == 0x1A) + { + // read next character + stream.Read(buffer, 1, 1); + switch (buffer[1]) + { + case 0x31: + // do not decode + ADSB = null; + break; + case 0x32: + // 7 byte short frame + // read timestamp + stream.Read(buffer, 2, 6); + nanosec = ((buffer[4] & 0x3f) << 24) | + (buffer[5] << 16) | + (buffer[6] << 8) | + (buffer[7]); + daysec = (buffer[2] << 10) | + (buffer[3] << 2) | + (buffer[4] >> 6); + timestamp = DateTime.Today.AddSeconds(daysec); + timestamp = timestamp.AddMilliseconds(nanosec / 1000); + // plausibility check + if (Math.Abs((DateTime.Now - timestamp).Seconds) > 10) + { + // time difference > 10sec --> discard timestamp + timestamp = DateTime.UtcNow; + } + // read signal strength + stream.Read(buffer, 8, 1); + // plausibility check + if (Math.Abs((DateTime.Now - timestamp).Seconds) > 10) + { + // time difference > 10sec --> discard timestamp + timestamp = DateTime.UtcNow; + } + signal_strength = buffer[8]; + // read frame + stream.Read(buffer, 9, 7); + // convert to AVR string + ADSB = BitConverter.ToString((byte[])buffer, 9, 7).Replace("-", String.Empty); + ADSB = "*" + ADSB + ";"; + break; + case 0x33: + // 14 byte long frame + // read timestamp + stream.Read(buffer, 2, 6); + nanosec = ((buffer[4] & 0x3f) << 24) | + (buffer[5] << 16) | + (buffer[6] << 8) | + (buffer[7]); + + daysec = (buffer[2] << 10) | + (buffer[3] << 2) | + (buffer[4] >> 6); + timestamp = DateTime.Today.AddSeconds(daysec); + timestamp = timestamp.AddMilliseconds(nanosec / 1000); + // plausibility check + if (Math.Abs((DateTime.Now - timestamp).Seconds) > 10) + { + // time difference > 10sec --> discard timestamp + timestamp = DateTime.UtcNow; + } + // read signal strength + stream.Read(buffer, 8, 1); + signal_strength = buffer[8]; + // read frame + stream.Read(buffer, 9, 14); + // convert to AVR string + ADSB = BitConverter.ToString((byte[])buffer, 9, 14).Replace("-", String.Empty); + ADSB = "*" + ADSB + ";"; + break; + default: + // false decode + ADSB = null; + break; + } + } + // check for timeout 10sec + stop = DateTime.UtcNow; + if (stop - start > new TimeSpan(0, 0, 10)) + throw new TimeoutException(); + } + while ((ADSB == null) && !this.CancellationPending); + if (ADSB == null) + return null; + ADSBMessage msg = new ADSBMessage(); + msg.RawMessage = ADSB; + msg.TimeStamp = timestamp; + msg.SignalStrength = signal_strength; + return msg; + } + + private ADSBMessage ReceiveAVRMsg ( StreamReader sr) + { + ADSBMessage msg = new ADSBMessage(); + // read AVR format input + msg.RawMessage = sr.ReadLine(); + if (msg.RawMessage.StartsWith("*")) + { + // standard AVR message + // no timestamp in telegram --> set timestamp after reading + msg.TimeStamp = DateTime.UtcNow; + msg.SignalStrength = 0; + } + else if (msg.RawMessage.StartsWith("@")) + { + // extended AVR message wit MLAT + // convert into standard message format + // extract time string + string time = msg.RawMessage.Substring(1, 12); + // TODO: interprete the MLAT timestamp! + msg.TimeStamp = DateTime.UtcNow; + msg.RawMessage = "*" + msg.RawMessage.Remove(0, 13); + } + return msg; + } + + protected override void OnDoWork(DoWorkEventArgs e) + { + Log.WriteMessage("Started."); + PlaneFeedWorkEventArgs args = (PlaneFeedWorkEventArgs)e.Argument; + // set directories + AppDirectory = args.AppDirectory; + AppDataDirectory = args.AppDataDirectory; + LogDirectory = args.LogDirectory; + TmpDirectory = args.TmpDirectory; + DatabaseDirectory = args.DatabaseDirectory; + // set boundaries from arguments + MaxLat = args.MaxLat; + MinLon = args.MinLon; + MinLat = args.MinLat; + MaxLon = args.MaxLon; + MyLat = args.MyLat; + MyLon = args.MyLon; + DXLat = args.DXLat; + DXLon = args.DXLon; + MinAlt = args.MinAlt; + MaxAlt = args.MaxAlt; + + // check boundaries + if ((MaxLat <= MinLat) || (MaxLon <= MinLon)) + { + Status = STATUS.ERROR; + this.ReportProgress((int)PROGRESS.ERROR, "Area boundaries mismatch. Check your Covered Area parameters!"); + } + else + { + Thread.CurrentThread.Priority = ThreadPriority.Highest; + Status = STATUS.OK; + ADSBDecoder decoder = new ADSBDecoder(); + DateTime lastreported = DateTime.UtcNow; + StreamReader sr = null; + TcpClient client = null; + ADSBMessage msg = null; + // outer loop + do + { + try + { + // setup TCP listener + client = new TcpClient(); + client.Connect(Properties.Settings.Default.ADSB_Server, Properties.Settings.Default.ADSB_Port); + sr = new StreamReader(client.GetStream()); + // inner loop + // receive messages in a loop + do + { + if (FeedSettings.Binary) + msg = ReceiveBinaryMsg(sr.BaseStream); + else + msg = ReceiveAVRMsg(sr); + // decode the message + { + Console.Write(msg.RawMessage + " --> "); + // try to decode the message + string info = ""; + try + { + info = decoder.DecodeMessage(msg.RawMessage, msg.TimeStamp); + // report messages to main window if activated + if (FeedSettings.ReportMessages && (info.StartsWith("["))) + this.ReportProgress((int)PROGRESS.STATUS, info); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + Console.WriteLine(info); + } + DateTime stop = DateTime.UtcNow; + // check if update report is necessary + if ((DateTime.UtcNow - lastreported).TotalSeconds > FeedSettings.Interval) + { + lastreported = DateTime.UtcNow; + // time to report planes + ArrayList list = decoder.GetPlanes(); + if (list.Count > 0) + { + // convert to plane info list + List planes = new List(); + foreach (ADSBInfo info in list) + { + PlaneInfo planeinfo = new PlaneInfo(); + planeinfo.Time = info.Timestamp; + planeinfo.Hex = info.ICAO24; + // mark call with "@" if option is enabled + planeinfo.Call = (FeedSettings.MarkLocal) ? "@" + info.Call : info.Call; + planeinfo.Lat = info.Lat; + planeinfo.Lon = info.Lon; + planeinfo.Alt = info.Alt; + planeinfo.Speed = info.Speed; + planeinfo.Track = info.Heading; + planeinfo.Reg = "[unknown]"; + planeinfo.Type = "[unknown]"; + planeinfo.Manufacturer = "[unknown]"; + planeinfo.Model = "[unknown]"; + planeinfo.Category = PLANECATEGORY.NONE; + // try to get the registration and type + AircraftDesignator aircraft = AircraftData.Database.AircraftFindByHex(planeinfo.Hex); + if (aircraft != null) + { + planeinfo.Reg = aircraft.Reg; + planeinfo.Type = aircraft.TypeCode; + // try to get the type + AircraftTypeDesignator type = AircraftData.Database.AircraftTypeFindByICAO(planeinfo.Type); + if (planeinfo != null) + { + planeinfo.Manufacturer = type.Manufacturer; + planeinfo.Model = type.Model; + planeinfo.Category = type.Category; + } + } + planes.Add(planeinfo); + } + ReportProgress((int)PROGRESS.PLANES, planes); + AircraftData.Database.PlaneInfoBulkInsertOrUpdateIfNewer(planes); + string message = "[" + lastreported.ToString("HH:mm:ss") + "] " + + decoder.Count.ToString() + " Positions updated from local ADS-B receiver."; + this.ReportProgress((int)PROGRESS.STATUS, message); + } + } + } + while ((msg != null) && !CancellationPending); + } + catch (Exception ex) + { + Log.WriteMessage(ex.Message); + this.ReportProgress((int)PROGRESS.ERROR, "Error reading from TCP connection: " + ex.Message); + Thread.Sleep(10000); + } + finally + { + // try to close the stream and TCP client + try + { + if (sr != null) + sr.Close(); + } + catch + { + } + try + { + if (client != null) + client.Close(); + } + catch + { + } + } + } + while (!this.CancellationPending || (Status != STATUS.OK)); + } + this.ReportProgress((int)PROGRESS.FINISHED); + Log.WriteMessage("Finished."); + } + + public override Object GetFeedSettings() + { + return FeedSettings; + } + + } +} diff --git a/AirScout.PlaneFeeds/AirScout.PlaneFeeds.csproj b/AirScout.PlaneFeeds/AirScout.PlaneFeeds.csproj new file mode 100644 index 0000000..57edf91 --- /dev/null +++ b/AirScout.PlaneFeeds/AirScout.PlaneFeeds.csproj @@ -0,0 +1,138 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {EA78AD40-1505-406F-8049-744E58D93F54} + Library + Properties + AirScout.PlaneFeeds + PlaneFeeds + v4.0 + 512 + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + prompt + false + false + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + prompt + false + false + + + + ..\packages\Newtonsoft.Json.12.0.1\lib\net40\Newtonsoft.Json.dll + True + + + + + ..\packages\System.Data.SQLite.Core.1.0.110.0\lib\net40\System.Data.SQLite.dll + True + + + + + + + + + + + + + + + + + + + + + + True + True + Settings.settings + + + + + + + + + PublicSettingsSingleFileGenerator + Settings.Designer.cs + + + + + {be467e28-c202-4d71-bb17-9086861ad75f} + AirScout.AircraftPositions + + + {288a26ec-b690-41a2-84e5-61c9b7b74046} + AirScout.Aircrafts + + + {EE86E933-D883-4B18-80EB-0FBA55EC67C6} + ScoutBase.Core + + + {A5775994-404F-4B2E-9FF7-4537BBE17506} + LibADSB + + + {6056d3be-7002-4a6a-a9ea-6ff45122a3c7} + SQLiteDatabase + + + + + + + Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}". + + + + + \ No newline at end of file diff --git a/AirScout.PlaneFeeds/AirScout.cs b/AirScout.PlaneFeeds/AirScout.cs new file mode 100644 index 0000000..b2709ef --- /dev/null +++ b/AirScout.PlaneFeeds/AirScout.cs @@ -0,0 +1,265 @@ +using System; +using System.Collections.Generic; +using System.Collections; +using System.Linq; +using System.Text; +using System.Threading; +using System.ComponentModel; +using System.Windows; +using System.Globalization; +using System.Net; +using System.IO; +using AirScout.Aircrafts; +using AirScout.PlaneFeeds.Generic; +using ScoutBase.Core; + +namespace AirScout.PlaneFeeds +{ + + public class PlaneFeedSettings_AS : PlaneFeedSettings + { + [DescriptionAttribute("Base URL for website.")] + public virtual string URL + { + get + { + return Properties.Settings.Default.AS_URL; + } + set + { + Properties.Settings.Default.AS_URL = value; + Properties.Settings.Default.Save(); + } + } + + [DescriptionAttribute("Update interval for website request [seconds]")] + public virtual int Interval + { + get + { + return Properties.Settings.Default.AS_Interval; + } + set + { + Properties.Settings.Default.AS_Interval = value; + Properties.Settings.Default.Save(); + } + } + + } + + public class PlaneFeed_AS : PlaneFeed + { + [Browsable(false)] + public override string Name + { + get + { + return Properties.Settings.Default.AS_Name; ; + } + protected set + { + Properties.Settings.Default.AS_Name = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(false)] + public override string Disclaimer + { + get + { + return Properties.Settings.Default.AS_Disclaimer; + } + protected set + { + Properties.Settings.Default.AS_Disclaimer = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(false)] + public override string DisclaimerAccepted + { + get + { + return Properties.Settings.Default.AS_Disclaimer_Accepted; + } + set + { + Properties.Settings.Default.AS_Disclaimer_Accepted = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(false)] + public override string Info + { + get + { + return Properties.Settings.Default.AS_Info; + } + protected set + { + Properties.Settings.Default.AS_Info = value; + Properties.Settings.Default.Save(); + } + } + + public new PlaneFeedSettings_AS FeedSettings = new PlaneFeedSettings_AS(); + + public PlaneFeed_AS() + : base () + { + HasSettings = true; + } + + protected override void OnDoWork(DoWorkEventArgs e) + { + Log.WriteMessage("Started."); + PlaneFeedWorkEventArgs args = (PlaneFeedWorkEventArgs)e.Argument; + // set directories + AppDirectory = args.AppDirectory; + AppDataDirectory = args.AppDataDirectory; + LogDirectory = args.LogDirectory; + TmpDirectory = args.TmpDirectory; + DatabaseDirectory = args.DatabaseDirectory; + // set boundaries from arguments + MaxLat = args.MaxLat; + MinLon = args.MinLon; + MinLat = args.MinLat; + MaxLon = args.MaxLon; + MyLat = args.MyLat; + MyLon = args.MyLon; + DXLat = args.DXLat; + DXLon = args.DXLon; + MinAlt = args.MinAlt; + MaxAlt = args.MaxAlt; + + // check boundaries + if ((MaxLat <= MinLat) || (MaxLon <= MinLon)) + { + Status = STATUS.ERROR; + this.ReportProgress((int)PROGRESS.ERROR, "Area boundaries mismatch. Check your Covered Area parameters!"); + } + else + { + Status = STATUS.OK; + int interval = Properties.Settings.Default.AS_Interval * 1000; + // run loop + do + { + string json = ""; + DateTime start = DateTime.UtcNow; + // calculate url and get json + String url = Properties.Settings.Default.AS_URL; + try + { + HttpWebRequest webrequest = (HttpWebRequest)HttpWebRequest.Create(url); + webrequest.Referer = "AirScout"; + webrequest.UserAgent = "Mozilla/5.0 (X11; Linux x86_64; rv:17.0) Gecko/20130807 Firefox/17.0"; + HttpWebResponse webresponse = (HttpWebResponse)webrequest.GetResponse(); + using (StreamReader sr = new StreamReader(webresponse.GetResponseStream())) + { + json = sr.ReadToEnd(); + } + } + catch (Exception ex) + { + // do nothing + Log.WriteMessage(ex.Message); + this.ReportProgress((int)PROGRESS.ERROR, "Error loading file from url: " + ex.Message); + } + int count = 0; + List planes = new List(); + // analyze json string for planes data + if (json.Contains(']')) + { + json = json.Remove(0, json.IndexOf(":[") - 10); + json = json.Replace("\r", String.Empty); + json = json.Replace("\n", string.Empty); + // split plane positions + string[] items = json.Split(']'); + foreach (string item in items) + { + try + { + if (item.Length > 11) + { + string d = item.Substring(2).Replace(":", ",").Replace("\"", string.Empty).Replace("[", string.Empty); + string[] par = d.Split(','); + + // fill planeinfo with fields from JSON string + PlaneInfo info = new PlaneInfo(); + long l = System.Convert.ToInt64(par[11].ToString()); + DateTime timestamp = new System.DateTime(1970, 1, 1, 0, 0, 0, 0); + timestamp = timestamp.AddSeconds(l); + info.Time = timestamp; + try + { + // fill in the "must have" info + info.Call = par[17]; + NumberFormatInfo provider = new NumberFormatInfo(); + provider.NumberDecimalSeparator = "."; + provider.NumberGroupSeparator = ","; + provider.NumberGroupSizes = new int[] { 3 }; + info.Lat = System.Convert.ToDouble(par[2], provider); + info.Lon = System.Convert.ToDouble(par[3], provider); + info.Alt = System.Convert.ToInt32(par[5]); + info.Speed = System.Convert.ToInt32(par[6]); + info.Hex = par[1]; + try + { + if (par[4].Length > 0) + info.Track = System.Convert.ToInt32(par[4]); + } + catch + { + } + info.Reg = par[10].ToString(); + info.Type = par[9].ToString(); + if (AircraftData.Database.AirlineFindByICAO(info.Call) == null) + { + Console.WriteLine("Unkwon flight info: " + info.ToString()); + } + } + catch (Exception ex) + { + // discard data if any exception occured + Log.WriteMessage(ex.Message); + } + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.Message); + } + } + } + DateTime stop = DateTime.UtcNow; + ReportProgress((int)PROGRESS.PLANES, planes); + AircraftData.Database.PlaneInfoBulkInsertOrUpdateIfNewer(planes); + string msg = "[" + start.ToString("HH:mm:ss") + "] " + + count.ToString() + " Positions updated from " + url + ", " + + (stop - start).Milliseconds.ToString() + " ms."; + this.ReportProgress((int)PROGRESS.STATUS, msg); + int i = 0; + while (!CancellationPending && (i < interval)) + { + Thread.Sleep(1000); + i++; + } + } + while (!this.CancellationPending || (Status != STATUS.OK)); + } + this.ReportProgress((int)PROGRESS.FINISHED); + Log.WriteMessage("Finsihed."); + } + + public override Object GetFeedSettings() + { + return FeedSettings; + } + + } +} diff --git a/AirScout.PlaneFeeds/AutoJSON.cs b/AirScout.PlaneFeeds/AutoJSON.cs new file mode 100644 index 0000000..170be95 --- /dev/null +++ b/AirScout.PlaneFeeds/AutoJSON.cs @@ -0,0 +1,1440 @@ +using System; +using System.Collections.Generic; +using System.Collections; +using System.Linq; +using System.Text; +using System.Threading; +using System.ComponentModel; +using System.Windows; +using System.Globalization; +using System.Net; +using System.IO; +using System.Windows.Forms; +using System.Xml.Serialization; +using AirScout.Aircrafts; +using AirScout.PlaneFeeds.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using ScoutBase.Core; + +namespace AirScout.PlaneFeeds +{ + [Serializable] + public class PlanefeedSettings_AJ : PlaneFeedSettings + { + [CategoryAttribute("Web Feed")] + [DescriptionAttribute("Base URL for website.")] + public virtual string URL + { + get + { + return Properties.Settings.Default.AJ_URL; + } + set + { + // detect change in URL + if (Properties.Settings.Default.AJ_URL != value) + { + // reset array indices + ResetIndex(); + } + Properties.Settings.Default.AJ_URL = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Web Feed")] + [DescriptionAttribute("Update interval for website request [seconds]")] + public virtual int Interval + { + get + { + return Properties.Settings.Default.AJ_Interval; + } + set + { + Properties.Settings.Default.AJ_Interval = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Web Feed")] + [DescriptionAttribute("Save downloaded JSON to file")] + public virtual bool SaveToFile + { + get + { + return Properties.Settings.Default.AJ_SaveToFile; + } + set + { + Properties.Settings.Default.AJ_SaveToFile = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(true)] + [CategoryAttribute("Data Field Properties")] + [DescriptionAttribute("Try to find all Indices automatically")] + public virtual bool AutoIndex + { + get + { + return Properties.Settings.Default.AJ_AutoIndex; + } + set + { + // detect change from false to true + if (!Properties.Settings.Default.AJ_AutoIndex && value) + { + // reset array indices + ResetIndex(); + } + Properties.Settings.Default.AJ_AutoIndex = value; + Properties.Settings.Default.Save(); + + } + } + + [CategoryAttribute("Data Field Properties")] + [DescriptionAttribute("Index for UTC")] + [XmlElement("Index_UTC")] + public virtual int Index_UTC + { + get + { + return Properties.Settings.Default.AJ_Index_UTC; + } + set + { + Properties.Settings.Default.AJ_Index_UTC = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Data Field Properties")] + [DescriptionAttribute("Index for Hex")] + public virtual int Index_Hex + { + get + { + return Properties.Settings.Default.AJ_Index_Hex; + } + set + { + Properties.Settings.Default.AJ_Index_Hex = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Data Field Properties")] + [DescriptionAttribute("Index for Call")] + public virtual int Index_Call + { + get + { + return Properties.Settings.Default.AJ_Index_Call; + } + set + { + Properties.Settings.Default.AJ_Index_Call = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Data Field Properties")] + [DescriptionAttribute("Index for Latitude")] + public virtual int Index_Lat + { + get + { + return Properties.Settings.Default.AJ_Index_Lat; + } + set + { + Properties.Settings.Default.AJ_Index_Lat = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Data Field Properties")] + [DescriptionAttribute("Index for Longitde")] + public virtual int Index_Lon + { + get + { + return Properties.Settings.Default.AJ_Index_Lon; + } + set + { + Properties.Settings.Default.AJ_Index_Lon = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Data Field Properties")] + [DescriptionAttribute("Index for Altitude")] + public virtual int Index_Alt + { + get + { + return Properties.Settings.Default.AJ_Index_Alt; + } + set + { + Properties.Settings.Default.AJ_Index_Alt = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Data Field Properties")] + [DescriptionAttribute("Index for Speed")] + public virtual int Index_Speed + { + get + { + return Properties.Settings.Default.AJ_Index_Speed; + } + set + { + Properties.Settings.Default.AJ_Index_Speed = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Data Field Properties")] + [DescriptionAttribute("Index for Registration")] + public virtual int Index_Reg + { + get + { + return Properties.Settings.Default.AJ_Index_Reg; + } + set + { + Properties.Settings.Default.AJ_Index_Reg = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Data Field Properties")] + [DescriptionAttribute("Index for Flight Code")] + public virtual int Index_Flight + { + get + { + return Properties.Settings.Default.AJ_Index_Flight; + } + set + { + Properties.Settings.Default.AJ_Index_Flight = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Data Field Properties")] + [DescriptionAttribute("Index for Track")] + public virtual int Index_Track + { + get + { + return Properties.Settings.Default.AJ_Index_Track; + } + set + { + Properties.Settings.Default.AJ_Index_Track = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Data Field Properties")] + [DescriptionAttribute("Index for Type")] + public virtual int Index_Type + { + get + { + return Properties.Settings.Default.AJ_Index_Type; + } + set + { + Properties.Settings.Default.AJ_Index_Type = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Data Field Properties")] + [DescriptionAttribute("Index for Squawk")] + public virtual int Index_Squawk + { + get + { + return Properties.Settings.Default.AJ_Index_Squawk; + } + set + { + Properties.Settings.Default.AJ_Index_Squawk = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Data Field Properties")] + [DescriptionAttribute("Index for Radar")] + public virtual int Index_Radar + { + get + { + return Properties.Settings.Default.AJ_Index_Radar; + } + set + { + Properties.Settings.Default.AJ_Index_Radar = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Data Field Properties")] + [DescriptionAttribute("Index for Route")] + public virtual int Index_Route + { + get + { + return Properties.Settings.Default.AJ_Index_Route; + } + set + { + Properties.Settings.Default.AJ_Index_Route = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Data Field Properties")] + [DescriptionAttribute("Minimum Count of Elements per Plane Position Data Set")] + public virtual int Min_Elements + { + get + { + return Properties.Settings.Default.AJ_Min_Elements; + } + set + { + Properties.Settings.Default.AJ_Min_Elements = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Data Field Properties")] + [DescriptionAttribute("Minium Count of Plane Position Data Sets")] + public virtual int Min_Planes + { + get + { + return Properties.Settings.Default.AJ_Min_Planes; + } + set + { + Properties.Settings.Default.AJ_Min_Planes = value; + Properties.Settings.Default.Save(); + } + } + + private void ResetIndex() + { + Index_Alt = -1; + Index_Call = -1; + Index_Hex = -1; + Index_Lat = -1; + Index_Lon = -1; + Index_Reg = -1; + Index_Flight = -1; + Index_Speed = -1; + Index_Squawk = -1; + Index_Track = -1; + Index_Radar = -1; + Index_Route = -1; + Index_Type = -1; + Index_UTC = -1; + } + + } + + public class PlaneFeed_AJ : PlaneFeed + { + [Browsable(false)] + public override string Name + { + get + { + return Properties.Settings.Default.AJ_Name; ; + } + protected set + { + Properties.Settings.Default.AJ_Name = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(false)] + public override string Disclaimer + { + get + { + return Properties.Settings.Default.AJ_Disclaimer; + } + protected set + { + Properties.Settings.Default.AJ_Disclaimer = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(false)] + public override string DisclaimerAccepted + { + get + { + return Properties.Settings.Default.AJ_Disclaimer_Accepted; + } + set + { + Properties.Settings.Default.AJ_Disclaimer_Accepted = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(false)] + public override string Info + { + get + { + return Properties.Settings.Default.AJ_Info; + } + protected set + { + Properties.Settings.Default.AJ_Info = value; + Properties.Settings.Default.Save(); + } + } + + public new PlanefeedSettings_AJ FeedSettings = new PlanefeedSettings_AJ(); + + private Dictionary JProperties = new Dictionary(); + private Dictionary JArrays = new Dictionary(); + + private SortedList Maj = new SortedList(new DuplicateKeyComparer()); + + string[][] Values = new string[0][]; + string[][] Names = new string[0][]; + + string[][] Hexs = new string[0][]; + string[][] Regs = new string[0][]; + string[][] Flights = new string[0][]; + string[][] Calls = new string[0][]; + double[][] Lats = new double[0][]; + double[][] Lons = new double[0][]; + int[][] Alts = new int[0][]; + int[][] Speeds = new int[0][]; + int[][] Tracks = new int[0][]; + int[][] Squawks = new int[0][]; + string[][] Radars = new string[0][]; + string[][] Routes = new string[0][]; + string[][] Types = new string[0][]; + DateTime[][] UTCs = new DateTime[0][]; + + int[] Maj_Hexs = new int[0]; + int[] Maj_Regs = new int[0]; + int[] Maj_Flights = new int[0]; + int[] Maj_Lats = new int[0]; + int[] Maj_Calls = new int[0]; + int[] Maj_Lons = new int[0]; + int[] Maj_Alts = new int[0]; + int[] Maj_Speeds = new int[0]; + int[] Maj_Tracks = new int[0]; + int[] Maj_Squawks = new int[0]; + int[] Maj_Radars = new int[0]; + int[] Maj_Routes = new int[0]; + int[] Maj_Types = new int[0]; + int[] Maj_UTCs = new int[0]; + + double[] Min_Lats = new double[0]; + double[] Min_Lons = new double[0]; + int[] Min_Alts = new int[0]; + int[] Min_Speeds = new int[0]; + int[] Min_Tracks = new int[0]; + + double[] Max_Lats = new double[0]; + double[] Max_Lons = new double[0]; + int[] Max_Alts = new int[0]; + int[] Max_Speeds = new int[0]; + int[] Max_Tracks = new int[0]; + + int[] Min_Chars = new int[0]; + int[] Max_Chars = new int[0]; + + public PlaneFeed_AJ() + : base () + { + HasSettings = true; + CanImport = true; + CanExport = true; + } + + private class DuplicateKeyComparer + : IComparer where TKey : IComparable + { + public int Compare(TKey x, TKey y) + { + int result = y.CompareTo(x); + if (result == 0) + return -1; // Handle equality as beeing smaller + else + return result; + } + } + + private void ParseToken(JToken token) + { + foreach (var t in token.Children()) + { + try + { + if (t.GetType() == typeof(JProperty)) + { + string number = JProperties.Count.ToString("00000000"); + JProperties.Add(number, (JProperty)t); + } + else if (t.GetType() == typeof(JArray)) + { + // generate a unique key in case of no parent property is found + string number = JArrays.Count.ToString("00000000"); + // try to use parent's property name as a key + if (t.Parent.GetType() == typeof(JProperty)) + { + string pval = ((JProperty)t.Parent).Name; + JArray a; + if (!JArrays.TryGetValue(pval, out a)) + { + JArrays.Add(pval, (JArray)t); + } + else + { + // use number as unique key + JArrays.Add(number, (JArray)t); + } + } + } + } + catch (Exception ex) + { + // do nothing + } + ParseToken(t); + } + } + + private bool Is_Double(string s) + { + bool b; + try + { + // double should contain only following chars + b = !s.ToCharArray().Any(c => !"+-.,E0123456789".Contains(c)); + // double must contain a decimal separator + if (b) + { + b = s.Contains(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator); + } + } + catch (Exception ex) + { + b = false; + } + return b; + } + + private bool Is_Integer(string s) + { + bool b; + try + { + b = !s.ToCharArray().Any(c => !"0123456789abcdefABCDEF".Contains(c)); + } + catch (Exception ex) + { + b = false; + } + return b; + } + + private void SortMaj(int[] maj) + { + // add all majorities > 0 and sort it by value + // keep the original array index + Maj.Clear(); + for (int i = 0; i < maj.Length; i++) + { + if (maj[i] > 0) + Maj.Add(maj[i], i); + } + } + + private void ClearMajsByIndex(int index) + { + Maj_Hexs[index] = 0; + Maj_Regs[index] = 0; + Maj_Flights[index] = 0; + Maj_Lats[index] = 0; + Maj_Calls[index] = 0; + Maj_Lons[index] = 0; + Maj_Alts[index] = 0; + Maj_Speeds[index] = 0; + Maj_Tracks[index] = 0; + Maj_Squawks[index] = 0; + Maj_Radars[index] = 0; + Maj_Routes[index] = 0; + Maj_Types[index] = 0; + Maj_UTCs[index] = 0; + } + + private void InitializeArrays(int size_i, int size_j) + { + + Values = new string[size_i][]; + for (int i = 0; i < Values.Length; i++) + { + Values[i] = new string[size_j]; + for (int j = 0; j < Values[i].Length; j++) + { + Values[i][j] = ""; + } + } + Names = new string[size_i][]; + for (int i = 0; i < Names.Length; i++) + { + Names[i] = new string[size_j]; + for (int j = 0; j < Names[i].Length; j++) + { + Names[i][j] = ""; + } + } + + Hexs = new string[size_i][]; for (int i = 0; i < Values.Length; i++) { Hexs[i] = new string[size_j]; } + Regs = new string[size_i][]; for (int i = 0; i < Values.Length; i++) { Regs[i] = new string[size_j]; } + Flights = new string[size_i][]; for (int i = 0; i < Values.Length; i++) { Flights[i] = new string[size_j]; } + Calls = new string[size_i][]; for (int i = 0; i < Values.Length; i++) { Calls[i] = new string[size_j]; } + Lats = new double[size_i][]; for (int i = 0; i < Values.Length; i++) { Lats[i] = new double[size_j]; } + Lons = new double[size_i][]; for (int i = 0; i < Values.Length; i++) { Lons[i] = new double[size_j]; } + Alts = new int[size_i][]; for (int i = 0; i < Values.Length; i++) { Alts[i] = new int[size_j]; } + Speeds = new int[size_i][]; for (int i = 0; i < Values.Length; i++) { Speeds[i] = new int[size_j]; } + Tracks = new int[size_i][]; for (int i = 0; i < Values.Length; i++) { Tracks[i] = new int[size_j]; } + Squawks = new int[size_i][]; for (int i = 0; i < Values.Length; i++) { Squawks[i] = new int[size_j]; } + Radars = new string[size_i][]; for (int i = 0; i < Values.Length; i++) { Radars[i] = new string[size_j]; } + Routes = new string[size_i][]; for (int i = 0; i < Values.Length; i++) { Routes[i] = new string[size_j]; } + Types = new string[size_i][]; for (int i = 0; i < Values.Length; i++) { Types[i] = new string[size_j]; } + UTCs = new DateTime[size_i][]; for (int i = 0; i < Values.Length; i++) { UTCs[i] = new DateTime[size_j]; } + + Maj_Hexs = new int[size_j]; + Maj_Regs = new int[size_j]; + Maj_Flights = new int[size_j]; + Maj_Lats = new int[size_j]; + Maj_Calls = new int[size_j]; + Maj_Lons = new int[size_j]; + Maj_Alts = new int[size_j]; + Maj_Speeds = new int[size_j]; + Maj_Tracks = new int[size_j]; + Maj_Squawks = new int[size_j]; + Maj_Radars = new int[size_j]; + Maj_Routes = new int[size_j]; + Maj_Types = new int[size_j]; + Maj_UTCs = new int[size_j]; + + Min_Lats = new double[size_j]; + Min_Lons = new double[size_j]; + Min_Alts = new int[size_j]; + Min_Speeds = new int[size_j]; + Min_Tracks = new int[size_j]; + Max_Lats = new double[size_j]; + Max_Lons = new double[size_j]; + Max_Alts = new int[size_j]; + Max_Speeds = new int[size_j]; + Max_Tracks = new int[size_j]; + Min_Chars = new int[size_j]; + Max_Chars = new int[size_j]; + } + + private void ReadValuesFromArrays() + { + // analyze a random dataset + int i1 = new Random().Next(JArrays.Count - 1); + JArray a = JArrays.Values.ElementAt(i1); + int size_i = JArrays.Count; + int size_j = a.Children().Count() + 1; + // initialize arrays for values + InitializeArrays(size_i, size_j); + // copy strings to array + for (int i = 0; i < size_i; i++) + { + try + { + Values[i][0] = JArrays.Keys.ElementAt(i); + a = JArrays.Values.ElementAt(i); + for (int j = 0; j < a.Count; j++) + { + // check all values and try to find the majorities + string s = a[j].ToString(); + Values[i][j + 1] = s; + } + } + catch + { + } + } + try + { + using (StreamWriter sw = new StreamWriter(TmpDirectory + Path.DirectorySeparatorChar + "AutoJSON_Values.csv")) + { + for (int i = 0; i < Values.Length; i++) + { + for (int j = 0; j < Values[i].Length; j++) + { + sw.Write(Values[i][j] + ";"); + } + sw.WriteLine(); + } + } + } + catch (Exception ex) + { + Console.WriteLine("[AutoJSON] Error while writing file: " + ex.Message); + } + } + + private void ReadValuesFromProperties() + { + Dictionary properties = new Dictionary(); + foreach (JProperty p in JProperties.Values) + { + int count; + if (!properties.TryGetValue(p.Name, out count)) + { + properties.Add(p.Name, 1); + } + else + { + properties[p.Name]++; + } + } + // remove all properties with less count from list + for (int i = properties.Count - 1; i >= 0; i--) + { + if (properties.ElementAt(i).Value < FeedSettings.Min_Planes) + properties.Remove(properties.Keys.ElementAt(i)); + } + // find maximum and set ascending index + int max = 0; + for (int i = 0; i < properties.Keys.Count; i++ ) + { + if (properties.ElementAt(i).Value > max) + max = properties.ElementAt(i).Value; + properties[properties.ElementAt(i).Key] = i; + } + // initialize arrays + InitializeArrays(max, properties.Count); + int row = -1; + string parent = ""; + foreach (JProperty p in JProperties.Values) + { + if (p.Parent.Path != parent) + { + row++; + parent = p.Parent.Path; + } + int j; + if (properties.TryGetValue(p.Name, out j)) + Values[row][j] = p.Value.ToString(); + } + try + { + using (StreamWriter sw = new StreamWriter(TmpDirectory + Path.DirectorySeparatorChar + "AutoJSON_Values.csv")) + { + for (int i = 0; i < Values.Length; i++) + { + for (int j = 0; j < Values[i].Length; j++) + { + sw.Write(Values[i][j] + ";"); + } + sw.WriteLine(); + } + } + } + catch (Exception ex) + { + Console.WriteLine("[AutoJSON] Error while writing file: " + ex.Message); + } + } + + private bool IsIndexed() + { + // check for basic indices available + if (FeedSettings.Index_UTC < 0) + return false; + if (FeedSettings.Index_Hex < 0) + return false; + if (FeedSettings.Index_Call < 0) + return false; + if (FeedSettings.Index_Lat < 0) + return false; + if (FeedSettings.Index_Lon < 0) + return false; + if (FeedSettings.Index_Alt < 0) + return false; + if (FeedSettings.Index_Track < 0) + return false; + if (FeedSettings.Index_Speed < 0) + return false; + return true; + } + + private void AutoIndexArrays() + { + PlaneInfoConverter pic = new PlaneInfoConverter(); + + for (int i = 0; i < Values.Length; i++) + { + for (int j = 0; j < Values[i].Length; j++) + { + try + { + // check all values and try to find the majorities + string s = Values[i][j]; + if (String.IsNullOrEmpty(s)) + continue; + // store Min/Max string lenghts + if (s.Length < Min_Chars[j]) + Min_Chars[j] = s.Length; + if (s.Length > Max_Chars[j]) + Max_Chars[j] = s.Length; + // check for double values first + if (Is_Double(s)) + { + double lat = pic.To_Lat(s); + Lats[i][j] = lat; + if (lat != double.MinValue) + { + Maj_Lats[j]++; + if (lat < Min_Lats[j]) + Min_Lats[j] = lat; + if (lat > Max_Lats[j]) + Max_Lats[j] = lat; + } + double lon = pic.To_Lon(s); + Lons[i][j] = lon; + if (lon != double.MinValue) + { + Maj_Lons[j]++; + if (lon < Min_Lons[j]) + Min_Lons[j] = lon; + if (lon > Max_Lons[j]) + Max_Lons[j] = lon; + } + continue; + } + // check for integer + if (Is_Integer(s)) + { + DateTime utc = pic.To_UTC(s); + UTCs[i][j] = utc; + if (utc != DateTime.MinValue) + { + Maj_UTCs[j]++; + } + string hex = pic.To_Hex(s); + Hexs[i][j] = hex; + if (hex != null) + { + Maj_Hexs[j]++; + } + int alt = pic.To_Alt(s); + Alts[i][j] = alt; + if (alt != int.MinValue) + { + Maj_Alts[j]++; + if (alt < Min_Alts[j]) + Min_Alts[j] = alt; + if (alt > Max_Alts[j]) + Max_Alts[j] = alt; + } + int speed = pic.To_Speed(s); + Speeds[i][j] = speed; + if (speed != int.MinValue) + { + Maj_Speeds[j]++; + if (speed < Min_Speeds[j]) + Min_Speeds[j] = speed; + if (speed > Max_Speeds[j]) + Max_Speeds[j] = speed; + } + int track = pic.To_Track(s); + Tracks[i][j] = track; + if (track != int.MinValue) + { + Maj_Tracks[j]++; + if (track < Min_Tracks[j]) + Min_Tracks[j] = track; + if (track > Max_Tracks[j]) + Max_Tracks[j] = track; + } + int squawk = pic.To_Squawk(s); + Squawks[i][j] = squawk; + if (squawk != int.MinValue) + { + Maj_Squawks[j]++; + } + continue; + } + // the rest is string + string reg = pic.To_Reg(s); + Regs[i][j] = reg; + if (reg != null) + { + Maj_Regs[j]++; + } + string flight = pic.To_Flight(s); + Flights[i][j] = flight; + if (flight != null) + { + Maj_Flights[j]++; + } + string radar = pic.To_Radar(s); + Radars[i][j] = radar; + if (radar != null) + { + Maj_Radars[j]++; + } + string route = pic.To_Route(s); + Routes[i][j] = route; + if (route != null) + { + Maj_Routes[j]++; + } + string call = pic.To_Call(s); + Calls[i][j] = call; + if (call != null) + { + Maj_Calls[j]++; + } + string type = pic.To_Type(s); + Types[i][j] = type; + if (type != null) + { + Maj_Types[j]++; + } + } + catch (Exception ex) + { + Console.WriteLine("Values[" + i.ToString() + "][" + j.ToString() + "]=" + Values[i][j] + ":" + ex.Message); + } + } + } + // do the forensic work now + if (FeedSettings.Index_UTC < 0) + { + SortMaj(Maj_UTCs); + foreach (KeyValuePair maj in Maj) + { + // UTC must have >=10 chars + if (Max_Chars[maj.Value] >= 10) + { + FeedSettings.Index_UTC = maj.Value; + ClearMajsByIndex(maj.Value); + break; + } + } + } + if (FeedSettings.Index_Hex < 0) + { + SortMaj(Maj_Hexs); + foreach (KeyValuePair maj in Maj) + { + // Hex must have 6 chars +// if (Max_Chars[maj.Value] == 6) + { + FeedSettings.Index_Hex = maj.Value; + ClearMajsByIndex(maj.Value); + break; + } + } + } + if (FeedSettings.Index_Squawk < 0) + { + SortMaj(Maj_Squawks); + foreach (KeyValuePair maj in Maj) + { + // Squawk must have 4 chars + if (Max_Chars[maj.Value] == 4) + { + FeedSettings.Index_Squawk = maj.Value; + ClearMajsByIndex(maj.Value); + break; + } + } + } + if (FeedSettings.Index_Alt < 0) + { + SortMaj(Maj_Alts); + foreach (KeyValuePair maj in Maj) + { + // Alt will have > 3 chars and max. values > 10000 feet + if ((Max_Chars[maj.Value] > 3) && (Max_Alts[maj.Value] > 10000)) + { + FeedSettings.Index_Alt = maj.Value; + ClearMajsByIndex(maj.Value); + break; + } + } + } + if (FeedSettings.Index_Track < 0) + { + SortMaj(Maj_Tracks); + foreach (KeyValuePair maj in Maj) + { + // Track will have <= 3 chars and hopefully at last on plane between 180 and 360 + if ((Max_Chars[maj.Value] <= 3) && (Max_Tracks[maj.Value] > 180) && (Max_Tracks[maj.Value] < 360)) + { + FeedSettings.Index_Track = maj.Value; + ClearMajsByIndex(maj.Value); + break; + } + } + } + if (FeedSettings.Index_Speed < 0) + { + SortMaj(Maj_Speeds); + foreach (KeyValuePair maj in Maj) + { + // Speed will have <= 3 chars and max > 100 and < 800 + if ((Max_Chars[maj.Value] <= 3) && (Max_Speeds[maj.Value] > 100) && (Max_Speeds[maj.Value] <= 800)) + { + FeedSettings.Index_Speed = maj.Value; + ClearMajsByIndex(maj.Value); + break; + } + } + } + if (FeedSettings.Index_Lat < 0) + { + SortMaj(Maj_Lats); + foreach (KeyValuePair maj in Maj) + { + // Lat will have max >= -90 and <= 90 + if ((Min_Lats[maj.Value] >= -90) && (Max_Tracks[maj.Value] <= 90)) + { + FeedSettings.Index_Lat = maj.Value; + ClearMajsByIndex(maj.Value); + break; + } + } + } + if (FeedSettings.Index_Lon < 0) + { + SortMaj(Maj_Lons); + foreach (KeyValuePair maj in Maj) + { + // Lon will have max >= -180 and <= 180 + if ((Min_Lons[maj.Value] >= -180) && (Max_Tracks[maj.Value] <= 180)) + { + FeedSettings.Index_Lon = maj.Value; + ClearMajsByIndex(maj.Value); + break; + } + } + } + if (FeedSettings.Index_Type < 0) + { + SortMaj(Maj_Types); + foreach (KeyValuePair maj in Maj) + { + // type will have >= 2 chars + if (Max_Chars[maj.Value] >= 2) + { + FeedSettings.Index_Type = maj.Value; + ClearMajsByIndex(maj.Value); + break; + } + } + } + if (FeedSettings.Index_Flight < 0) + { + SortMaj(Maj_Flights); + foreach (KeyValuePair maj in Maj) + { + FeedSettings.Index_Flight = maj.Value; + ClearMajsByIndex(maj.Value); + break; + } + } + if (FeedSettings.Index_Reg < 0) + { + SortMaj(Maj_Regs); + foreach (KeyValuePair maj in Maj) + { + // Reg will have > 3 chars + if (Max_Chars[maj.Value] > 3) + { + FeedSettings.Index_Reg = maj.Value; + ClearMajsByIndex(maj.Value); + break; + } + } + } + if (FeedSettings.Index_Route < 0) + { + SortMaj(Maj_Routes); + foreach (KeyValuePair maj in Maj) + { + FeedSettings.Index_Route = maj.Value; + ClearMajsByIndex(maj.Value); + break; + } + } + if (FeedSettings.Index_Radar < 0) + { + SortMaj(Maj_Radars); + foreach (KeyValuePair maj in Maj) + { + // radar will have > 2 chars + if (Max_Chars[maj.Value] > 2) + { + FeedSettings.Index_Radar = maj.Value; + ClearMajsByIndex(maj.Value); + break; + } + } + } + if (FeedSettings.Index_Call < 0) + { + SortMaj(Maj_Calls); + foreach (KeyValuePair maj in Maj) + { + // reg will have > 3 chars + if (Max_Chars[maj.Value] > 3) + { + FeedSettings.Index_Call = maj.Value; + ClearMajsByIndex(maj.Value); + break; + } + } + } + } + + private List ReadPlanesFromArray() + { + List planes = new List(); + PlaneInfoConverter pic = new PlaneInfoConverter(); + for (int i = 0; i < Values.Length; i++) + { + PlaneInfo info = new PlaneInfo(); + + if (FeedSettings.Index_UTC >= 0) + { + DateTime utc = pic.To_UTC(Values[i][FeedSettings.Index_UTC]); + if (utc != DateTime.MinValue) + info.Time = utc; + } + if (FeedSettings.Index_Hex >= 0) + { + string hex = pic.To_Hex(Values[i][FeedSettings.Index_Hex]); + if (!String.IsNullOrEmpty(hex)) + info.Hex = hex; + } + if (FeedSettings.Index_Squawk >= 0) + { + int squawk = pic.To_Squawk(Values[i][FeedSettings.Index_Squawk]); +// if (squawk != int.MinValue) +// info.Squawk = squawk; + } + if (FeedSettings.Index_Alt >= 0) + { + int alt = pic.To_Alt(Values[i][FeedSettings.Index_Alt]); + if (alt != int.MinValue) + { + info.Alt = alt; +// info.Alt_m = (int)((double)info.Alt / 3.28084); + } + } + if (FeedSettings.Index_Track >= 0) + { + int track = pic.To_Track(Values[i][FeedSettings.Index_Track]); + if (track != int.MinValue) + info.Track = track; + } + if (FeedSettings.Index_Speed >= 0) + { + int speed = pic.To_Speed(Values[i][FeedSettings.Index_Speed]); + if (speed != int.MinValue) + info.Speed = speed; + } + if (FeedSettings.Index_Lat >= 0) + { + double lat = pic.To_Lat(Values[i][FeedSettings.Index_Lat]); + if (lat != double.MinValue) + info.Lat = lat; + } + if (FeedSettings.Index_Lon >= 0) + { + double lon = pic.To_Lon(Values[i][FeedSettings.Index_Lon]); + if (lon != double.MinValue) + info.Lon = lon; + } + if (FeedSettings.Index_Type >= 0) + { + string type = pic.To_Type(Values[i][FeedSettings.Index_Type]); + if (!String.IsNullOrEmpty(type)) + info.Type = type; + } + if (FeedSettings.Index_Reg >= 0) + { + string reg = pic.To_Reg(Values[i][FeedSettings.Index_Reg]); + if (!String.IsNullOrEmpty(reg)) + info.Reg = reg; + } + if (FeedSettings.Index_Call >= 0) + { + string call = pic.To_Call(Values[i][FeedSettings.Index_Call]); + if (!String.IsNullOrEmpty(call)) + info.Call = call; + } + // complete type info + AircraftTypeDesignator td = AircraftData.Database.AircraftTypeFindByICAO(info.Type); + if (td != null) + { + info.Manufacturer = td.Manufacturer; + info.Model = td.Model; + info.Category = td.Category; + } + else + { + info.Manufacturer = "[unknown]"; + info.Model = "[unknown]"; + info.Category = PLANECATEGORY.NONE; + } + if ((info.Call != null) && + (info.Lat >= MinLat) && + (info.Lat <= MaxLat) && + (info.Lon >= MinLon) && + (info.Lon <= MaxLon) && + (info.Alt_m >= MinAlt) && + (info.Alt_m <= MaxAlt)) + { + planes.Add(info); + } + } + return planes; + } + + protected override void OnDoWork(DoWorkEventArgs e) + { + Log.WriteMessage("Started."); + PlaneFeedWorkEventArgs args = (PlaneFeedWorkEventArgs)e.Argument; + // set directories + AppDirectory = args.AppDirectory; + AppDataDirectory = args.AppDataDirectory; + LogDirectory = args.LogDirectory; + TmpDirectory = args.TmpDirectory; + DatabaseDirectory = args.DatabaseDirectory; + // set boundaries from arguments + MaxLat = args.MaxLat; + MinLon = args.MinLon; + MinLat = args.MinLat; + MaxLon = args.MaxLon; + MyLat = args.MyLat; + MyLon = args.MyLon; + DXLat = args.DXLat; + DXLon = args.DXLon; + MinAlt = args.MinAlt; + MaxAlt = args.MaxAlt; + + // intialize variables + VC.AddVar("APPDIR", AppDirectory); + VC.AddVar("DATADIR", AppDataDirectory); + VC.AddVar("LOGDIR", LogDirectory); + VC.AddVar("DATABASEDIR", DatabaseDirectory); + VC.AddVar("MINLAT", MinLat); + VC.AddVar("MAXLAT", MaxLat); + VC.AddVar("MINLON", MinLon); + VC.AddVar("MAXLON", MaxLon); + VC.AddVar("MINALTM", MinAlt); + VC.AddVar("MAXALTM", MaxAlt); + VC.AddVar("MINALTFT", (int)UnitConverter.m_ft((double)MinAlt)); + VC.AddVar("MAXALTFT", (int)UnitConverter.m_ft((double)MaxAlt)); + + // check boundaries + if ((MaxLat <= MinLat) || (MaxLon <= MinLon)) + { + Status = STATUS.ERROR; + this.ReportProgress((int)PROGRESS.ERROR, "Area boundaries mismatch. Check your Covered Area parameters!"); + } + else + { + Status = STATUS.OK; + int count = 0; + int interval = Properties.Settings.Default.AJ_Interval; + // run loop + do + { + string json = ""; + DateTime start = DateTime.UtcNow; + // calculate url and get json + String url = VC.ReplaceAllVars(Properties.Settings.Default.AJ_URL); + try + { + HttpWebRequest webrequest = (HttpWebRequest)HttpWebRequest.Create(url); + webrequest.Referer = ""; + webrequest.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:37.0) Gecko/20100101 Firefox/37.0"; + webrequest.Accept = "application/json, text/javascript, */*;q=0.01"; + webrequest.AutomaticDecompression = System.Net.DecompressionMethods.Deflate | System.Net.DecompressionMethods.GZip; + HttpWebResponse webresponse = (HttpWebResponse)webrequest.GetResponse(); + using (StreamReader sr = new StreamReader(webresponse.GetResponseStream())) + { + json = sr.ReadToEnd(); + } + if (FeedSettings.SaveToFile) + { + try + { + using (StreamWriter sw = new StreamWriter(TmpDirectory + Path.DirectorySeparatorChar + "AutoJSON.json")) + { + sw.WriteLine(json); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.Message); + } + } + // deserialize JSON file + JObject o = (JObject)JsonConvert.DeserializeObject(json); + // clear collections + JArrays.Clear(); + JProperties.Clear(); + // parse all child tokens recursively --> can be either a property or an array + ParseToken(o); + // we've got all properties and arrays here + // assuming that plane info is stored in a structure with >10 elements + // check the child token count + try + { + var propertychildcounts = JProperties.Values.Select(x => x.Count) + .GroupBy(x => x) + .Select(x => new { ChildCount = x.Key, Count = x.Count() }) + .OrderByDescending(x => x) + .ToList(); + // single properties? + if (propertychildcounts.Count > 0) + { + ReadValuesFromProperties(); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.Message); + } + try + { + var arraychildcounts = JArrays.Values.Select(x => x.Count) + .GroupBy(x => x) + .Select(x => new { ChildCount = x.Key, Count = x.Count() }) + .OrderByDescending(x => x) + .ToList(); + // checking number of elements to determine the organisation of values + // arrays of values? + if ((arraychildcounts != null) && (arraychildcounts.Count > 0) && (arraychildcounts[0].ChildCount > FeedSettings.Min_Elements) && (arraychildcounts[0].Count > FeedSettings.Min_Planes)) + { + // assuming that we have data organized in arrays + // find the majority of element counts + // remove all arrays with different element count from list + for (int j = JArrays.Count - 1; j >= 0; j--) + { + if (JArrays.Values.ElementAt(j).Count != arraychildcounts[0].ChildCount) + JArrays.Remove(JArrays.Keys.ElementAt(j)); + } + // convert all properties into the values array of strings + ReadValuesFromArrays(); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.Message); + } + // auto-index if necessary + if (FeedSettings.AutoIndex && !IsIndexed()) + AutoIndexArrays(); + // read data + if (IsIndexed()) + { + List planes = ReadPlanesFromArray(); + if ((planes != null) && (planes.Count > 0)) + { + ReportProgress((int)PROGRESS.PLANES, planes); + AircraftData.Database.PlaneInfoBulkInsertOrUpdateIfNewer(planes); + count = planes.Count; + } + } + } + catch (Exception ex) + { + // report error + Log.WriteMessage(ex.Message); + ReportProgress(-1, ex.Message); + } + DateTime stop = DateTime.UtcNow; + string msg = "[" + start.ToString("HH:mm:ss") + "] " + + count.ToString() + " Positions updated from " + url + ", " + + (stop - start).Milliseconds.ToString() + " ms."; + this.ReportProgress((int)PROGRESS.STATUS, msg); + int i = 0; + while (!CancellationPending && (i < interval)) + { + Thread.Sleep(1000); + i++; + } + } + while (!this.CancellationPending || (Status != STATUS.OK)); + } + this.ReportProgress((int)PROGRESS.FINISHED); + Log.WriteMessage("Finished."); + } + + public override Object GetFeedSettings() + { + return FeedSettings; + } + + public override void Import() + { + OpenFileDialog Dlg = new OpenFileDialog(); + Dlg.FileName = "*.feed"; + Dlg.DefaultExt = "feed"; + Dlg.Filter = "Plane Feeds | .feed"; + Dlg.CheckFileExists = true; + if (Dlg.ShowDialog() == DialogResult.OK) + { + XmlSerializer s = new XmlSerializer(typeof(PlanefeedSettings_AJ)); + FeedSettings = (PlanefeedSettings_AJ)s.Deserialize(File.OpenRead(Dlg.FileName)); + } + } + + public override void Export() + { + SaveFileDialog Dlg = new SaveFileDialog(); + Dlg.DefaultExt = "feed"; + Dlg.Filter = "Plane Feeds | .feed"; + Dlg.OverwritePrompt = true; + if (Dlg.ShowDialog() == DialogResult.OK) + { + XmlSerializer s = new XmlSerializer(typeof(PlanefeedSettings_AJ)); + s.Serialize(File.Create(Dlg.FileName), FeedSettings); + } + + } + } +} diff --git a/AirScout.PlaneFeeds/FileJSON.cs b/AirScout.PlaneFeeds/FileJSON.cs new file mode 100644 index 0000000..ea85f54 --- /dev/null +++ b/AirScout.PlaneFeeds/FileJSON.cs @@ -0,0 +1,281 @@ +using System; +using System.Collections.Generic; +using System.Collections; +using System.Linq; +using System.Text; +using System.Threading; +using System.ComponentModel; +using System.Windows; +using System.Globalization; +using System.Net; +using System.IO; +using AirScout.Aircrafts; +using AirScout.PlaneFeeds.Generic; +using ScoutBase.Core; + +namespace AirScout.PlaneFeeds +{ + + public class PlaneFeedSettings_FJ : PlaneFeedSettings + { + [DescriptionAttribute("Path to JSON file.")] + public virtual string URL + { + get + { + return Properties.Settings.Default.FJ_Path; + } + set + { + Properties.Settings.Default.FJ_Path = value; + Properties.Settings.Default.Save(); + } + } + + [DescriptionAttribute("Update interval for file reading [seconds]")] + public virtual int Interval + { + get + { + return Properties.Settings.Default.FJ_Interval; + } + set + { + Properties.Settings.Default.FJ_Interval = value; + Properties.Settings.Default.Save(); + } + } + + } + + public class PlaneFeed_FJ : PlaneFeed + { + [Browsable(false)] + public override string Name + { + get + { + return Properties.Settings.Default.FJ_Name; ; + } + protected set + { + Properties.Settings.Default.FJ_Name = value; + Properties.Settings.Default.Save(); + } + } + + + [Browsable(false)] + public override string Disclaimer + { + get + { + return Properties.Settings.Default.FJ_Disclaimer; + } + protected set + { + Properties.Settings.Default.FJ_Disclaimer = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(false)] + public override string DisclaimerAccepted + { + get + { + return Properties.Settings.Default.FJ_Disclaimer_Accepted; + } + set + { + Properties.Settings.Default.FJ_Disclaimer_Accepted = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(false)] + public override string Info + { + get + { + return Properties.Settings.Default.FJ_Info; + } + protected set + { + Properties.Settings.Default.FJ_Info = value; + Properties.Settings.Default.Save(); + } + } + + public new PlaneFeedSettings_FJ FeedSettings = new PlaneFeedSettings_FJ(); + + public PlaneFeed_FJ() + : base () + { + HasSettings = true; + } + + protected override void OnDoWork(DoWorkEventArgs e) + { + Log.WriteMessage("Started."); + PlaneFeedWorkEventArgs args = (PlaneFeedWorkEventArgs)e.Argument; + // set directories + AppDirectory = args.AppDirectory; + AppDataDirectory = args.AppDataDirectory; + LogDirectory = args.LogDirectory; + TmpDirectory = args.TmpDirectory; + DatabaseDirectory = args.DatabaseDirectory; + // set boundaries from arguments + MaxLat = args.MaxLat; + MinLon = args.MinLon; + MinLat = args.MinLat; + MaxLon = args.MaxLon; + MyLat = args.MyLat; + MyLon = args.MyLon; + DXLat = args.DXLat; + DXLon = args.DXLon; + MinAlt = args.MinAlt; + MaxAlt = args.MaxAlt; + + // check boundaries + if ((MaxLat <= MinLat) || (MaxLon <= MinLon)) + { + Status = STATUS.ERROR; + this.ReportProgress((int)PROGRESS.ERROR, "Area boundaries mismatch. Check your Covered Area parameters!"); + } + else + { + Status = STATUS.OK; + int interval = Properties.Settings.Default.FJ_Interval; + // run loop + do + { + string json = ""; + DateTime start = DateTime.UtcNow; + // calculate url and get json + String path = Properties.Settings.Default.FJ_Path; + try + { + FileStream fs = new FileStream(path,FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + using (StreamReader sr = new StreamReader(fs)) + { + json = sr.ReadToEnd(); + } + } + catch (Exception ex) + { + // do nothing + Log.WriteMessage(ex.Message); + } + int count = 0; + List planes = new List(); + // analyze json string for planes data + if (json.Contains(']')) + { + json = json.Remove(0, json.IndexOf(":[") - 10); + json = json.Replace("\r", String.Empty); + json = json.Replace("\n", string.Empty); + // split plane positions + string[] items = json.Split(']'); + foreach (string item in items) + { + try + { + if (item.Length > 11) + { + string d = item.Substring(2).Replace(":", ",").Replace("\"", string.Empty).Replace("[", string.Empty); + string[] par = d.Split(','); + + // fill planeinfo with fields from JSON string + PlaneInfo info = new PlaneInfo(); + long l = System.Convert.ToInt64(par[11].ToString()); + DateTime timestamp = new System.DateTime(1970, 1, 1, 0, 0, 0, 0); + timestamp = timestamp.AddSeconds(l); + info.Time = timestamp; + try + { + // fill in the "must have" info + info.Call = par[17]; + NumberFormatInfo provider = new NumberFormatInfo(); + provider.NumberDecimalSeparator = "."; + provider.NumberGroupSeparator = ","; + provider.NumberGroupSizes = new int[] { 3 }; + info.Lat = System.Convert.ToDouble(par[2], provider); + info.Lon = System.Convert.ToDouble(par[3], provider); + info.Alt = System.Convert.ToInt32(par[5]); + info.Speed = System.Convert.ToInt32(par[6]); + info.Hex = par[1]; + try + { + if (par[4].Length > 0) + info.Track = System.Convert.ToInt32(par[4]); + } + catch + { + } + info.Reg = par[10].ToString(); + info.Type = par[9].ToString(); + AircraftTypeDesignator type = AircraftData.Database.AircraftTypeFindByICAO(info.Type); + if (info != null) + { + info.Manufacturer = type.Manufacturer; + info.Model = type.Model; + info.Category = type.Category; + } + else + { + info.Manufacturer = "[unknown]"; + info.Model = "[unknown]"; + info.Category = PLANECATEGORY.NONE; + } + if ((info.Lat >= MinLat) && + (info.Lat <= MaxLat) && + (info.Lon >= MinLon) && + (info.Lon <= MaxLon) && + (info.Alt_m >= MinAlt) && + (info.Alt_m <= MaxAlt)) + { + planes.Add(info); + count++; + } + } + catch (Exception ex) + { + // discard data if any exception occured + Log.WriteMessage(ex.Message); + } + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.Message); + } + } + } + DateTime stop = DateTime.UtcNow; + ReportProgress((int)PROGRESS.PLANES, planes); + AircraftData.Database.PlaneInfoBulkInsertOrUpdateIfNewer(planes); + string msg = "[" + start.ToString("HH:mm:ss") + "] " + + count.ToString() + " Positions updated from " + path + ", " + + (stop - start).Milliseconds.ToString() + " ms."; + this.ReportProgress((int)PROGRESS.STATUS, msg); + int i = 0; + while (!CancellationPending && (i < interval)) + { + Thread.Sleep(1000); + i++; + } + } + while (!this.CancellationPending || (Status != STATUS.OK)); + } + this.ReportProgress((int)PROGRESS.FINISHED); + Log.WriteMessage("Finished."); + } + + public override Object GetFeedSettings() + { + return FeedSettings; + } + + } +} diff --git a/AirScout.PlaneFeeds/Flightradar24.cs b/AirScout.PlaneFeeds/Flightradar24.cs new file mode 100644 index 0000000..04201b6 --- /dev/null +++ b/AirScout.PlaneFeeds/Flightradar24.cs @@ -0,0 +1,465 @@ +using System; +using System.Collections.Generic; +using System.Collections; +using System.Linq; +using System.Text; +using System.Threading; +using System.ComponentModel; +using System.Windows; +using System.Windows.Forms; +using System.Globalization; +using System.Net; +using System.IO; +using System.Diagnostics; +using System.Runtime.InteropServices; +using AirScout.Aircrafts; +using AirScout.PlaneFeeds.Generic; +using Newtonsoft.Json; +using System.Security.Cryptography; +using ScoutBase.Core; + +namespace AirScout.PlaneFeeds +{ + public class PlaneFeedSettings_FR : PlaneFeedSettings + { + [CategoryAttribute("Web Feed")] + [DescriptionAttribute("Base URL for website.")] + [Browsable(false)] + public virtual string URL + { + get + { + return "http://arn.data.fr24.com/zones/fcgi/feed.js"; + } + } + + [CategoryAttribute("Web Feed")] + [DescriptionAttribute("Update interval for website request [seconds]")] + public virtual int Interval + { + get + { + return Properties.Settings.Default.FR_Interval; + } + set + { + Properties.Settings.Default.FR_Interval = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Web Feed")] + [DescriptionAttribute("Save downloaded JSON to file")] + public virtual bool SaveToFile + { + get + { + return Properties.Settings.Default.FR_SaveToFile; + } + set + { + Properties.Settings.Default.FR_SaveToFile = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Login Data")] + [DescriptionAttribute("Base URL for login.")] + [Browsable(false)] + public virtual string Login_URL + { + get + { + return "https://www.flightradar24.com/premium/ws.php"; + } + } + + [CategoryAttribute("Login Data")] + [DescriptionAttribute("User name.")] + public virtual string Login_User + { + get + { + string user = ""; + // try to decrypt data + try + { + byte[] cipherBytes = Convert.FromBase64String(Properties.Settings.Default.FR_Login_User); + byte[] passwordBytes = ProtectedData.Unprotect(cipherBytes, null, DataProtectionScope.CurrentUser); + user = Encoding.Unicode.GetString(passwordBytes); + } + catch + { + // do nothing if failed + } + return user; + } + set + { + string user = ""; + // try to encrypt data + try + { + byte[] passwordBytes = Encoding.Unicode.GetBytes(value); + byte[] cipherBytes = ProtectedData.Protect(passwordBytes, null, DataProtectionScope.CurrentUser); + user = Convert.ToBase64String(cipherBytes); Properties.Settings.Default.Save(); + } + catch + { + } + Properties.Settings.Default.FR_Login_User = user; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Login Data")] + [DescriptionAttribute("Password.")] + public virtual string Login_Password + { + get + { + string password = ""; + // try to decrypt data + try + { + byte[] cipherBytes = Convert.FromBase64String(Properties.Settings.Default.FR_Login_Password); + byte[] passwordBytes = ProtectedData.Unprotect(cipherBytes, null, DataProtectionScope.CurrentUser); + password = Encoding.Unicode.GetString(passwordBytes); + } + catch + { + // do nothing if failed + } + return password; + } + set + { + string password = ""; + // try to encrypt data + try + { + byte[] passwordBytes = Encoding.Unicode.GetBytes(value); + byte[] cipherBytes = ProtectedData.Protect(passwordBytes, null, DataProtectionScope.CurrentUser); + password = Convert.ToBase64String(cipherBytes); Properties.Settings.Default.Save(); + } + catch + { + } + Properties.Settings.Default.FR_Login_Password = password; + Properties.Settings.Default.Save(); + } + } + + } + + public class PlaneFeed_FR : PlaneFeed + { + [Browsable(false)] + public override string Name + { + get + { + return Properties.Settings.Default.FR_Name; ; + } + protected set + { + Properties.Settings.Default.FR_Name = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(false)] + public override string Disclaimer + { + get + { + return Properties.Settings.Default.FR_Disclaimer; + } + protected set + { + Properties.Settings.Default.FR_Disclaimer = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(false)] + public override string DisclaimerAccepted + { + get + { + return Properties.Settings.Default.FR_Disclaimer_Accepted; + } + set + { + Properties.Settings.Default.FR_Disclaimer_Accepted = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(false)] + public override string Info + { + get + { + return Properties.Settings.Default.FR_Info; + } + protected set + { + Properties.Settings.Default.FR_Info = value; + Properties.Settings.Default.Save(); + } + } + + public new PlaneFeedSettings_FR FeedSettings = new PlaneFeedSettings_FR(); + + public PlaneFeed_FR() + : base() + { + HasSettings = true; + } + + protected override void OnDoWork(DoWorkEventArgs e) + { + PlaneFeedWorkEventArgs args = (PlaneFeedWorkEventArgs)e.Argument; + // set directories + AppDirectory = args.AppDirectory; + AppDataDirectory = args.AppDataDirectory; + LogDirectory = args.LogDirectory; + TmpDirectory = args.TmpDirectory; + DatabaseDirectory = args.DatabaseDirectory; + // set boundaries from arguments + MaxLat = args.MaxLat; + MinLon = args.MinLon; + MinLat = args.MinLat; + MaxLon = args.MaxLon; + MyLat = args.MyLat; + MyLon = args.MyLon; + DXLat = args.DXLat; + DXLon = args.DXLon; + MinAlt = args.MinAlt; + MaxAlt = args.MaxAlt; + + // check boundaries + if ((MaxLat <= MinLat) || (MaxLon <= MinLon)) + { + Status = STATUS.ERROR; + this.ReportProgress((int)PROGRESS.ERROR, "Area boundaries mismatch. Check your Covered Area parameters!"); + } + else + { + Status = STATUS.OK; + int interval = Properties.Settings.Default.FR_Interval; + // run loop + do + { + string login; + string json = ""; + DateTime start = DateTime.UtcNow; + // check for empty user data + if (String.IsNullOrEmpty(FeedSettings.Login_User) || String.IsNullOrEmpty(FeedSettings.Login_Password)) + { + this.ReportProgress((int)PROGRESS.STATUS, "[" + start.ToString("HH:mm:ss") + "] " + + "Login to Flightradar24 failed (Empty user or password). Unable to reed plane feed."); + Thread.Sleep(FeedSettings.Interval * 1000); + continue; + } + // initialize a new cookie container + CookieContainer cookies = new CookieContainer(); + // calculate url and get json + String url = FeedSettings.URL + "?bounds=" + + MaxLat.ToString(CultureInfo.InvariantCulture) + "," + + MinLat.ToString(CultureInfo.InvariantCulture) + "," + + MinLon.ToString(CultureInfo.InvariantCulture) + "," + + MaxLon.ToString(CultureInfo.InvariantCulture); + try + { + // check login status + string post_data = "email=" + FeedSettings.Login_User + + "&password=" + FeedSettings.Login_Password + + "&remember=false&type=web"; + // create a POST request + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(FeedSettings.Login_URL); + request.CookieContainer = cookies; + request.KeepAlive = false; + request.ProtocolVersion = HttpVersion.Version10; + request.Method = "POST"; + + // turn the request string into a byte stream + byte[] postBytes = Encoding.ASCII.GetBytes(post_data); + + // this is important - make sure you specify type this way + request.ContentType = "application/x-www-form-urlencoded"; + request.KeepAlive = true; + request.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:37.0) Gecko/20100101 Firefox/37.0"; + request.Accept = "*/*"; + request.Expect = ""; + request.ContentLength = postBytes.Length; + Stream requestStream = request.GetRequestStream(); + + // now send it + requestStream.Write(postBytes, 0, postBytes.Length); + requestStream.Close(); + + // grab te response + HttpWebResponse response = (HttpWebResponse)request.GetResponse(); + using (StreamReader sr = new StreamReader(response.GetResponseStream())) + { + login = sr.ReadToEnd(); + } + // check for success + if (!login.ToUpper().Contains("SUCCEEDED")) + { + this.ReportProgress((int)PROGRESS.STATUS, "[" + start.ToString("HH:mm:ss") + "] " + + "Login to Flightradar24 failed (Wrong user or password). Unable to reed plane feed."); + Thread.Sleep(FeedSettings.Interval * 1000); + continue; + } + // read data if login was successful + HttpWebRequest webrequest = (HttpWebRequest)HttpWebRequest.Create(url); + webrequest.Accept = "application/json, text/javascript, */*"; + webrequest.AutomaticDecompression = System.Net.DecompressionMethods.Deflate | System.Net.DecompressionMethods.GZip; + webrequest.CookieContainer = cookies; + webrequest.Referer = "http://www.flightradar24.com/"; + webrequest.UserAgent = "Mozilla/5.0 (X11; Linux x86_64; rv:17.0) Gecko/20130807 Firefox/17.0"; + HttpWebResponse webresponse = (HttpWebResponse)webrequest.GetResponse(); + // get the complete json string + using (StreamReader sr = new StreamReader(webresponse.GetResponseStream())) + { + json = sr.ReadToEnd(); + } + } + catch (Exception ex) + { + // do nothing + } + if (FeedSettings.SaveToFile) + { + try + { + using (StreamWriter sw = new StreamWriter(TmpDirectory + Path.DirectorySeparatorChar + "fr24.json")) + { + sw.WriteLine(json); + } + } + catch + { + } + } + int total = 0; + int count = 0; + int errors = 0; + List planes = new List(); + // analyze json string for planes data + if (json.Contains(']')) + { + // clean header + json = json.Remove(0, json.IndexOf(",") + 1); + json = json.Remove(0, json.IndexOf(",") + 1); + // split plane positions + string[] items = json.Split(']'); + string[] par = new string[0]; + foreach (string item in items) + { + try + { + if (item.Length > 11) + { + total++; + string d = item.Substring(2).Replace(":", ",").Replace("\"", string.Empty).Replace("[", string.Empty); + par = d.Split(','); + + // fill planeinfo with fields from JSON string + PlaneInfo info = new PlaneInfo(); + long l = System.Convert.ToInt64(par[11].ToString()); + DateTime timestamp = new System.DateTime(1970, 1, 1, 0, 0, 0, 0); + timestamp = timestamp.AddSeconds(l); + info.Time = timestamp; + try + { + // fill in the "must have" info + info.Call = par[17]; + NumberFormatInfo provider = new NumberFormatInfo(); + provider.NumberDecimalSeparator = "."; + provider.NumberGroupSeparator = ","; + provider.NumberGroupSizes = new int[] { 3 }; + info.Lat = double.Parse(par[2], NumberStyles.Float, provider); + info.Lon = double.Parse(par[3], NumberStyles.Float, provider); + info.Alt = double.Parse(par[5], NumberStyles.Float, provider); + info.Speed = double.Parse(par[6], NumberStyles.Float, provider); + info.Hex = par[1]; + try + { + if (par[4].Length > 0) + info.Track = System.Convert.ToInt32(par[4]); + } + catch + { + } + info.Reg = par[10].ToString(); + info.Type = par[9].ToString(); + AircraftTypeDesignator type = AircraftData.Database.AircraftTypeFindByICAO(info.Type); + if (type != null) + { + info.Manufacturer = type.Manufacturer; + info.Model = type.Model; + info.Category = type.Category; + } + else + { + info.Manufacturer = "[unknown]"; + info.Model = "[unknown]"; + info.Category = PLANECATEGORY.NONE; + } + if ((info.Lat >= MinLat) && + (info.Lat <= MaxLat) && + (info.Lon >= MinLon) && + (info.Lon <= MaxLon) && + (info.Alt_m >= MinAlt) && + (info.Alt_m <= MaxAlt)) + { + planes.Add(info); + count++; + } + } + catch (Exception ex) + { + // discard data if any exception occured + System.Console.WriteLine("Error in plane data: " + item); + errors++; + } + } + } + catch (Exception ex) + { + // discard data if any exception occured + System.Console.WriteLine("Error in plane data: " + item); + errors++; + } + } + } + DateTime stop = DateTime.UtcNow; + ReportProgress((int)PROGRESS.PLANES, planes); + AircraftData.Database.PlaneInfoBulkInsertOrUpdateIfNewer(planes); + string msg = "[" + start.ToString("HH:mm:ss") + "] " + + total.ToString() + " Positions updated from " + "www.fligthradar24.com" + ", " + + (stop - start).Milliseconds.ToString() + " ms. OK: " + count.ToString() + ", Errors: " + errors.ToString(); + this.ReportProgress((int)PROGRESS.STATUS, msg); + int i = 0; + while (!CancellationPending && (i < interval)) + { + Thread.Sleep(1000); + i++; + } + } + while (!this.CancellationPending || (Status != STATUS.OK)); + } + this.ReportProgress((int)PROGRESS.FINISHED); + } + + public override Object GetFeedSettings() + { + return FeedSettings; + } + + } +} diff --git a/AirScout.PlaneFeeds/Generic.cs b/AirScout.PlaneFeeds/Generic.cs new file mode 100644 index 0000000..f51b458 --- /dev/null +++ b/AirScout.PlaneFeeds/Generic.cs @@ -0,0 +1,337 @@ +using System; +using System.Collections.Generic; +using System.Collections; +using System.Linq; +using System.Text; +using System.Threading; +using System.ComponentModel; +using System.Windows; +using System.Windows.Forms; +using System.Globalization; +using System.Net; +using System.IO; +using System.Xml.Serialization; +using AirScout.Aircrafts; +using ScoutBase.Core; + +namespace AirScout.PlaneFeeds.Generic +{ + // enum for ReportProgress messages + public enum PROGRESS + { + ERROR = -1, + STATUS = 0, + PLANES = 1, + FINISHED = 100 + } + + // enum for PlaneFeed status + public enum STATUS + { + ERROR = -1, + OK = 0 + } + + // class for PlaneFeed property setting + public class PlaneFeedProperty + { + public string Info = ""; + public string Value = ""; + + public PlaneFeedProperty(string info, string value) + { + Info = info; + Value = value; + } + } + + public class PlaneFeedWorkEventArgs + { + public string AppDirectory = ""; + public string AppDataDirectory = ""; + public string LogDirectory = ""; + public string TmpDirectory = ""; + public string DatabaseDirectory = ""; + + public double MaxLat = 0; + public double MinLon = 0; + public double MinLat = 0; + public double MaxLon = 0; + + public int MinAlt = 0; + public int MaxAlt = 0; + + public double MyLat = 0; + public double MyLon = 0; + public double DXLat = 0; + public double DXLon = 0; + + public bool KeepHistory = false; + } + + [Serializable] + public class PlaneFeedSettings + { + } + + [DefaultPropertyAttribute("Name")] + public class PlaneFeed : BackgroundWorker + { + [Browsable(false)] + public virtual string Name + { + get + { + return Properties.Settings.Default.Generic_Name; + } + protected set + { + Properties.Settings.Default.Generic_Name = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(false)] + public virtual string Disclaimer + { + get + { + return Properties.Settings.Default.Generic_Disclaimer; + } + protected set + { + Properties.Settings.Default.Generic_Disclaimer = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(false)] + public virtual string DisclaimerAccepted + { + get + { + return Properties.Settings.Default.Generic_Disclaimer_Accepted; + } + set + { + Properties.Settings.Default.Generic_Disclaimer_Accepted = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(false)] + public virtual string Info + { + get + { + return Properties.Settings.Default.Generic_Info; + } + protected set + { + Properties.Settings.Default.Generic_Info = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(false)] + public bool HasSettings; + + [Browsable(false)] + public bool CanImport; + + [Browsable(false)] + public bool CanExport; + + public PlaneFeedSettings FeedSettings = new PlaneFeedSettings(); + + public STATUS Status; + + public string AppDirectory = ""; + public string AppDataDirectory = ""; + public string LogDirectory = ""; + public string TmpDirectory = ""; + public string DatabaseDirectory = ""; + + public double MaxLat = 60; + public double MinLon = -15; + public double MinLat = 30; + public double MaxLon = 35; + + public int MinAlt = 5000; + public int MaxAlt = 12200; + + public double MyLat; + public double MyLon; + public double DXLat; + public double DXLon; + + public bool KeepHistory = false; + + protected VarConverter VC; + + protected LogWriter Log = LogWriter.Instance; + + public PlaneFeed() + : base() + { + this.WorkerReportsProgress = true; + this.WorkerSupportsCancellation = true; + this.CanImport = false; + this.CanExport = false; + this.HasSettings = false; + // Initailize variables + VC = new VarConverter(); + + } + + protected override void OnDoWork(DoWorkEventArgs e) + { + Log.WriteMessage("Started."); + PlaneFeedWorkEventArgs args = (PlaneFeedWorkEventArgs)e.Argument; + // set parameters from arguments + + AppDirectory = args.AppDirectory; + AppDataDirectory = args.AppDataDirectory; + LogDirectory = args.LogDirectory; + TmpDirectory = args.TmpDirectory; + DatabaseDirectory = args.DatabaseDirectory; + MaxLat = args.MaxLat; + MinLon = args.MinLon; + MinLat = args.MinLat; + MaxLon = args.MaxLon; + MyLat = args.MyLat; + MyLon = args.MyLon; + DXLat = args.DXLat; + DXLon = args.DXLon; + MinAlt = args.MinAlt; + MaxAlt = args.MaxAlt; + + Status = STATUS.OK; + + // narrow args according to QSO partners + if (MyLat > DXLat) + { + MaxLat = MyLat; + MinLat = DXLat; + } + else + { + MaxLat = DXLat; + MinLat = MyLat; + } + if (MyLon > DXLon) + { + MaxLon = MyLon; + MinLon = DXLon; + } + else + { + MaxLon = DXLon; + MinLon = MyLon; + } + int interval = 60; + do + { + int count = 100; + DateTime start = DateTime.UtcNow; + List planes = new List(); + Random rnd = new Random(); + for (int ii = 0; ii < count; ii++) + { + PlaneInfo info = new PlaneInfo(); + info.Call = "RND" + ii.ToString("0000"); + info.Hex = ii.ToString("X4"); + info.Lat = rnd.NextDouble() * (MaxLat-MinLat) + MinLat; + info.Lon = rnd.NextDouble() * (MaxLon - MinLon) + MinLon; + info.Alt = rnd.Next((int)UnitConverter.m_ft(MinAlt), (int)UnitConverter.m_ft(MaxAlt)); + info.Speed = rnd.Next(200, 600); + info.Track = rnd.Next(0, 360); + int t = rnd.Next(0, 100); + info.Type = "C206"; + if (ii > 10) + info.Type = "A320"; + if (ii > 70) + info.Type = "B744"; + if(ii > 95) + info.Type = "A388"; + AircraftTypeDesignator type = AircraftData.Database.AircraftTypeFindByICAO(info.Type); + if (info != null) + { + info.Manufacturer = type.Manufacturer; + info.Model = type.Model; + info.Category = type.Category; + } + else + { + info.Manufacturer = "[unknown]"; + info.Model = "[unknown]"; + info.Category = PLANECATEGORY.NONE; + } + planes.Add(info); + } + ReportProgress((int)PROGRESS.PLANES, planes); + AircraftData.Database.PlaneInfoBulkInsertOrUpdateIfNewer(planes); + string msg = "[" + start.ToString("HH:mm:ss") + "] " + + count.ToString() + " Positions randomized."; + this.ReportProgress((int)PROGRESS.STATUS, msg); + int i = 0; + while (!CancellationPending && (i < interval)) + { + Thread.Sleep(1000); + i++; + } + } + while (!CancellationPending); + this.ReportProgress((int)PROGRESS.FINISHED); + Log.WriteMessage("Finished."); + } + + public virtual Object GetFeedSettings() + { + return FeedSettings; + } + + public virtual void Import() + { + + } + + public virtual void Export() + { + SaveFileDialog Dlg = new SaveFileDialog(); + Dlg.DefaultExt = "feed"; + Dlg.Filter = "Plane Feeds | .feed"; + Dlg.OverwritePrompt = true; + if (Dlg.ShowDialog() == DialogResult.OK) + { + XmlSerializer s = new XmlSerializer(typeof(PlaneFeed)); + s.Serialize(File.Create(Dlg.FileName), this); + } + + } + + public override string ToString() + { + return Name; + } + + } + + public class PlaneFeedEnumeration + { + public ArrayList EnumFeeds() + { + ArrayList feeds = new ArrayList(); + feeds.Add(new PlaneFeed()); + feeds.Add(new PlaneFeed_AS()); + feeds.Add(new PlaneFeed_PF()); + feeds.Add(new PlaneFeed_VR()); +// feeds.Add(new PlaneFeed_FR()); + feeds.Add(new PlaneFeed_AJ()); + feeds.Add(new PlaneFeed_FJ()); + feeds.Add(new PlaneFeed_ADSB()); + feeds.Add(new PlaneFeed_RTL()); + return feeds; + } + } +} diff --git a/AirScout.PlaneFeeds/Planefinder.cs b/AirScout.PlaneFeeds/Planefinder.cs new file mode 100644 index 0000000..c20da86 --- /dev/null +++ b/AirScout.PlaneFeeds/Planefinder.cs @@ -0,0 +1,332 @@ +using System; +using System.Collections.Generic; +using System.Collections; +using System.Linq; +using System.Text; +using System.Threading; +using System.ComponentModel; +using System.Windows; +using System.Globalization; +using System.Net; +using System.IO; +using AirScout.Aircrafts; +using AirScout.PlaneFeeds.Generic; +using Newtonsoft.Json; +using ScoutBase.Core; + +namespace AirScout.PlaneFeeds +{ + public class PlaneFeedSettings_PF : PlaneFeedSettings + { + [CategoryAttribute("Web Feed")] + [DescriptionAttribute("Base URL for website.")] + public virtual string URL + { + get + { + return Properties.Settings.Default.PF_URL; + } + set + { + Properties.Settings.Default.PF_URL = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Web Feed")] + [DescriptionAttribute("Update interval for website request [seconds]")] + public virtual int Interval + { + get + { + return Properties.Settings.Default.PF_Interval; + } + set + { + Properties.Settings.Default.PF_Interval = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Web Feed")] + [DescriptionAttribute("Save downloaded JSON to file")] + public virtual bool SaveToFile + { + get + { + return Properties.Settings.Default.PF_SaveToFile; + } + set + { + Properties.Settings.Default.PF_SaveToFile = value; + Properties.Settings.Default.Save(); + } + } + + } + + public class PlaneFeed_PF : PlaneFeed + { + [Browsable(false)] + public override string Name + { + get + { + return Properties.Settings.Default.PF_Name; ; + } + protected set + { + Properties.Settings.Default.PF_Name = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(false)] + public override string Disclaimer + { + get + { + return Properties.Settings.Default.PF_Disclaimer; + } + protected set + { + Properties.Settings.Default.PF_Disclaimer = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(false)] + public override string DisclaimerAccepted + { + get + { + return Properties.Settings.Default.PF_Disclaimer_Accepted; + } + set + { + Properties.Settings.Default.PF_Disclaimer_Accepted = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(false)] + public override string Info + { + get + { + return Properties.Settings.Default.PF_Info; + } + protected set + { + Properties.Settings.Default.PF_Info = value; + Properties.Settings.Default.Save(); + } + } + + public new PlaneFeedSettings_PF FeedSettings = new PlaneFeedSettings_PF(); + + public PlaneFeed_PF() + : base () + { + HasSettings = true; + } + + protected override void OnDoWork(DoWorkEventArgs e) + { + Log.WriteMessage("Started."); + PlaneFeedWorkEventArgs args = (PlaneFeedWorkEventArgs)e.Argument; + // set directories + AppDirectory = args.AppDirectory; + AppDataDirectory = args.AppDataDirectory; + LogDirectory = args.LogDirectory; + TmpDirectory = args.TmpDirectory; + DatabaseDirectory = args.DatabaseDirectory; + // set boundaries from arguments + MaxLat = args.MaxLat; + MinLon = args.MinLon; + MinLat = args.MinLat; + MaxLon = args.MaxLon; + MyLat = args.MyLat; + MyLon = args.MyLon; + DXLat = args.DXLat; + DXLon = args.DXLon; + MinAlt = args.MinAlt; + MaxAlt = args.MaxAlt; + + // intialize variables + VC.AddVar("APPDIR", AppDirectory); + VC.AddVar("DATADIR", AppDataDirectory); + VC.AddVar("LOGDIR", LogDirectory); + VC.AddVar("DATABASEDIR", DatabaseDirectory); + VC.AddVar("MINLAT", MinLat); + VC.AddVar("MAXLAT", MaxLat); + VC.AddVar("MINLON", MinLon); + VC.AddVar("MAXLON", MaxLon); + VC.AddVar("MINALTM", MinAlt); + VC.AddVar("MAXALTM", MaxAlt); + VC.AddVar("MINALTFT", (int)UnitConverter.m_ft((double)MinAlt)); + VC.AddVar("MAXALTFT", (int)UnitConverter.m_ft((double)MaxAlt)); + + + // check boundaries + if ((MaxLat <= MinLat) || (MaxLon <= MinLon)) + { + Status = STATUS.ERROR; + this.ReportProgress((int)PROGRESS.ERROR, "Area boundaries mismatch. Check your Covered Area parameters!"); + } + else + { + Status = STATUS.OK; + int interval = Properties.Settings.Default.PF_Interval; + // run loop + do + { + string json = ""; + DateTime start = DateTime.UtcNow; + String url = VC.ReplaceAllVars(Properties.Settings.Default.PF_URL); + try + { + HttpWebRequest webrequest = (HttpWebRequest)HttpWebRequest.Create(url); + webrequest.Referer = "http://planefinder.net/"; + webrequest.UserAgent = "Mozilla/5.0 (X11; Linux x86_64; rv:17.0) Gecko/20130807 Firefox/17.0"; + HttpWebResponse webresponse = (HttpWebResponse)webrequest.GetResponse(); + using (StreamReader sr = new StreamReader(webresponse.GetResponseStream())) + { + json = sr.ReadToEnd(); + } + } + catch + { + // do nothing + } + if (FeedSettings.SaveToFile) + { + try + { + using (StreamWriter sw = new StreamWriter(TmpDirectory + Path.DirectorySeparatorChar + "planefinder.json")) + { + sw.WriteLine(json); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.Message); + } + } + int total = 0; + int count = 0; + int errors = 0; + List planes = new List(); + // analyze json string for planes data + if (json.Contains(']')) + { + json = json.Remove(0, json.IndexOf(":{") + 2); + // split plane positions + string[] items = json.Split(']'); + string[] par = new string[0]; + foreach (string item in items) + { + try + { + if (item.Length > 11) + { + total++; + string d = item.Substring(2).Replace(":", ",").Replace("\"", string.Empty).Replace("[", string.Empty); + par = d.Split(','); + + // fill planeinfo with fields from JSON string + PlaneInfo info = new PlaneInfo(); + long l = System.Convert.ToInt64(par[10].ToString()); + DateTime timestamp = new System.DateTime(1970, 1, 1, 0, 0, 0, 0); + timestamp = timestamp.AddSeconds(l); + info.Time = timestamp; + try + { + // fill in the "must have" info + info.Call = par[4]; + NumberFormatInfo provider = new NumberFormatInfo(); + provider.NumberDecimalSeparator = "."; + provider.NumberGroupSeparator = ","; + provider.NumberGroupSizes = new int[] { 3 }; + info.Lat = double.Parse(par[5], NumberStyles.Float, provider); + info.Lon = double.Parse(par[6], NumberStyles.Float, provider); + info.Alt = double.Parse(par[7], NumberStyles.Float, provider); + info.Speed = double.Parse(par[9], NumberStyles.Float, provider); + info.Hex = par[0]; + try + { + if (par[8].Length > 0) + info.Track = System.Convert.ToInt32(par[8]); + } + catch + { + } + info.Reg = par[3].ToString(); + info.Type = par[1].ToString(); + AircraftTypeDesignator type = AircraftData.Database.AircraftTypeFindByICAO(info.Type); + if (type != null) + { + info.Manufacturer = type.Manufacturer; + info.Model = type.Model; + info.Category = type.Category; + } + else + { + info.Manufacturer = "[unknown]"; + info.Model = "[unknown]"; + info.Category = PLANECATEGORY.NONE; + } + if ((info.Lat >= MinLat) && + (info.Lat <= MaxLat) && + (info.Lon >= MinLon) && + (info.Lon <= MaxLon) && + (info.Alt_m >= MinAlt) && + (info.Alt_m <= MaxAlt)) + { + planes.Add(info); + count++; + } + } + catch (Exception ex) + { + // discard data if any exception occured + Log.WriteMessage(ex.Message); + errors++; + } + } + } + catch (Exception ex) + { + // discard data if any exception occured + Log.WriteMessage(ex.Message); + errors++; + } + } + } + DateTime stop = DateTime.UtcNow; + ReportProgress((int)PROGRESS.PLANES, planes); + AircraftData.Database.PlaneInfoBulkInsertOrUpdateIfNewer(planes); + string msg = "[" + start.ToString("HH:mm:ss") + "] " + + total.ToString() + " Positions updated from " + url + ", " + + (stop - start).Milliseconds.ToString() + " ms. OK: " + count.ToString() + ", Errors: " + errors.ToString() ; + this.ReportProgress((int)PROGRESS.STATUS, msg); + int i = 0; + while (!CancellationPending && (i < interval)) + { + Thread.Sleep(1000); + i++; + } + } + while (!this.CancellationPending || (Status != STATUS.OK)); + } + this.ReportProgress((int)PROGRESS.FINISHED); + Log.WriteMessage("Finished."); + } + + public override Object GetFeedSettings() + { + return FeedSettings; + } + + } +} diff --git a/AirScout.PlaneFeeds/Properties/AssemblyInfo.cs b/AirScout.PlaneFeeds/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..db12a7d --- /dev/null +++ b/AirScout.PlaneFeeds/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Allgemeine Informationen über eine Assembly werden über die folgenden +// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, +// die mit einer Assembly verknüpft sind. +[assembly: AssemblyTitle("PlaneFeeds")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("DL2ALF")] +[assembly: AssemblyProduct("AirScout")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar +// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von +// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest. +[assembly: ComVisible(false)] + +// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird +[assembly: Guid("a1baca6a-b0c1-41af-ae5a-4aa99dd7b40f")] + +// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: +// +// Hauptversion +// Nebenversion +// Buildnummer +// Revision +// +// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern +// übernehmen, indem Sie "*" eingeben: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.1")] +[assembly: AssemblyFileVersion("1.0.0.1")] diff --git a/AirScout.PlaneFeeds/Properties/Settings.Designer.cs b/AirScout.PlaneFeeds/Properties/Settings.Designer.cs new file mode 100644 index 0000000..2f77e2b --- /dev/null +++ b/AirScout.PlaneFeeds/Properties/Settings.Designer.cs @@ -0,0 +1,1377 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.42000 +// +// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn +// der Code erneut generiert wird. +// +//------------------------------------------------------------------------------ + +namespace AirScout.PlaneFeeds.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] + public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://droidapp.pinkfroot.com/APPAPIDROID/v7/planeUpdateFAA.php?routetype=IATA&FA" + + "A=1&bounds=%MAXLAT%,%MINLAT%,%MINLON%,%MAXLON%")] + public string PF_URL { + get { + return ((string)(this["PF_URL"])); + } + set { + this["PF_URL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("60")] + public int PF_Interval { + get { + return ((int)(this["PF_Interval"])); + } + set { + this["PF_Interval"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Generic Info.")] + public string Generic_Info { + get { + return ((string)(this["Generic_Info"])); + } + set { + this["Generic_Info"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Web feed from www.planefinder.net")] + public string PF_Info { + get { + return ((string)(this["PF_Info"])); + } + set { + this["PF_Info"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("[Generic] Dummy Plane Feed")] + public string Generic_Name { + get { + return ((string)(this["Generic_Name"])); + } + set { + this["Generic_Name"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("[WebFeed] www.planefinder.net")] + public string PF_Name { + get { + return ((string)(this["PF_Name"])); + } + set { + this["PF_Name"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute(@"This plane feed is being fetched from an Internet server via Deep Link +technology (see http://en.wikipedia.org/wiki/Deep_link). + +The use is not intended by the website owners and could be changed in URL and data format frequently and without further notice. +Furthermore, it might cause legal issues in some countries. + +By clicking on ""Accept"" you understand that you are + + DOING THAT ON YOUR OWN RISK + +The auhor of this software will not be responsible in any case.")] + public string PF_Disclaimer { + get { + return ((string)(this["PF_Disclaimer"])); + } + set { + this["PF_Disclaimer"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string PF_Disclaimer_Accepted { + get { + return ((string)(this["PF_Disclaimer_Accepted"])); + } + set { + this["PF_Disclaimer_Accepted"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string Generic_Disclaimer_Accepted { + get { + return ((string)(this["Generic_Disclaimer_Accepted"])); + } + set { + this["Generic_Disclaimer_Accepted"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute(@"This plane feed is a dummy plane feed generating random plane positions. + +USE THIS FEED FOR DEMONSTRATION PURPOSE ONLY! + +The feed will run without fetching any data from the Internet. +Please do not mix it with other feeds as confusion may occur with live data. +The call signs generated will look like ""RND0001"", ""RND0002"" etc.")] + public string Generic_Disclaimer { + get { + return ((string)(this["Generic_Disclaimer"])); + } + set { + this["Generic_Disclaimer"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://localhost:9880/planes.json")] + public string AS_URL { + get { + return ((string)(this["AS_URL"])); + } + set { + this["AS_URL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("60")] + public int AS_Interval { + get { + return ((int)(this["AS_Interval"])); + } + set { + this["AS_Interval"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("[WebFeed] AirScout Web Server")] + public string AS_Name { + get { + return ((string)(this["AS_Name"])); + } + set { + this["AS_Name"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string AS_Disclaimer { + get { + return ((string)(this["AS_Disclaimer"])); + } + set { + this["AS_Disclaimer"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string AS_Disclaimer_Accepted { + get { + return ((string)(this["AS_Disclaimer_Accepted"])); + } + set { + this["AS_Disclaimer_Accepted"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Web feed from another AirScout server.")] + public string AS_Info { + get { + return ((string)(this["AS_Info"])); + } + set { + this["AS_Info"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string FJ_Disclaimer { + get { + return ((string)(this["FJ_Disclaimer"])); + } + set { + this["FJ_Disclaimer"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string FJ_Disclaimer_Accepted { + get { + return ((string)(this["FJ_Disclaimer_Accepted"])); + } + set { + this["FJ_Disclaimer_Accepted"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("60")] + public int FJ_Interval { + get { + return ((int)(this["FJ_Interval"])); + } + set { + this["FJ_Interval"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Reads plane positions out of a JSON file.")] + public string FJ_Info { + get { + return ((string)(this["FJ_Info"])); + } + set { + this["FJ_Info"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("[FileFeed] JSON file")] + public string FJ_Name { + get { + return ((string)(this["FJ_Name"])); + } + set { + this["FJ_Name"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("planes.json")] + public string FJ_Path { + get { + return ((string)(this["FJ_Path"])); + } + set { + this["FJ_Path"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("[RawData] ADSBSharp data ")] + public string ADSB_Name { + get { + return ((string)(this["ADSB_Name"])); + } + set { + this["ADSB_Name"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Raw data feed from simple ADS-B receivers (DVB-T dongles).\r\nUse this feed thogeth" + + "er with ADSBSharp.\r\n\r\n\r\n")] + public string ADSB_Info { + get { + return ((string)(this["ADSB_Info"])); + } + set { + this["ADSB_Info"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string ADSB_Disclaimer { + get { + return ((string)(this["ADSB_Disclaimer"])); + } + set { + this["ADSB_Disclaimer"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string ADSB_Disclaimer_Accepted { + get { + return ((string)(this["ADSB_Disclaimer_Accepted"])); + } + set { + this["ADSB_Disclaimer_Accepted"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("localhost")] + public string ADSB_Server { + get { + return ((string)(this["ADSB_Server"])); + } + set { + this["ADSB_Server"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("47806")] + public int ADSB_Port { + get { + return ((int)(this["ADSB_Port"])); + } + set { + this["ADSB_Port"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("60")] + public int ADSB_Interval { + get { + return ((int)(this["ADSB_Interval"])); + } + set { + this["ADSB_Interval"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool ADSB_Binary { + get { + return ((bool)(this["ADSB_Binary"])); + } + set { + this["ADSB_Binary"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool ADSB_Report_Messages { + get { + return ((bool)(this["ADSB_Report_Messages"])); + } + set { + this["ADSB_Report_Messages"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool ADSB_MarkLocal { + get { + return ((bool)(this["ADSB_MarkLocal"])); + } + set { + this["ADSB_MarkLocal"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("[RawData] RTL1090 data ")] + public string RTL_Name { + get { + return ((string)(this["RTL_Name"])); + } + set { + this["RTL_Name"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Raw data feed from simple ADS-B receivers (DVB-T dongles).\r\nUse this feed togethe" + + "r with RTL1090.exe.\r\n\r\nFeed software must output raw data either binary or ASCII" + + " format via TCP.\r\n\r\n\r\n")] + public string RTL_Info { + get { + return ((string)(this["RTL_Info"])); + } + set { + this["RTL_Info"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string RTL_Disclaimer { + get { + return ((string)(this["RTL_Disclaimer"])); + } + set { + this["RTL_Disclaimer"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string RTL_Disclaimer_Accepted { + get { + return ((string)(this["RTL_Disclaimer_Accepted"])); + } + set { + this["RTL_Disclaimer_Accepted"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("localhost")] + public string RTL_Server { + get { + return ((string)(this["RTL_Server"])); + } + set { + this["RTL_Server"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("31001")] + public int RTL_Port { + get { + return ((int)(this["RTL_Port"])); + } + set { + this["RTL_Port"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("60")] + public int RTL_Interval { + get { + return ((int)(this["RTL_Interval"])); + } + set { + this["RTL_Interval"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool RTL_Binary { + get { + return ((bool)(this["RTL_Binary"])); + } + set { + this["RTL_Binary"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool RTL_Report_Messages { + get { + return ((bool)(this["RTL_Report_Messages"])); + } + set { + this["RTL_Report_Messages"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool RTL_MarkLocal { + get { + return ((bool)(this["RTL_MarkLocal"])); + } + set { + this["RTL_MarkLocal"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute(@"This plane feed is being fetched from an Internet server via Deep Link +technology (see http://en.wikipedia.org/wiki/Deep_link). + +The use is not intended by the website owners and could be changed in URL and data format frequently and without further notice. +Furthermore, it might cause legal issues in some countries. + +By clicking on ""Accept"" you understand that you are + + DOING THAT ON YOUR OWN RISK + +The auhor of this software will not be responsible in any case.")] + public string AJ_Disclaimer { + get { + return ((string)(this["AJ_Disclaimer"])); + } + set { + this["AJ_Disclaimer"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string AJ_Disclaimer_Accepted { + get { + return ((string)(this["AJ_Disclaimer_Accepted"])); + } + set { + this["AJ_Disclaimer_Accepted"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("JSON file from web feed with auto detection of content")] + public string AJ_Info { + get { + return ((string)(this["AJ_Info"])); + } + set { + this["AJ_Info"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("60")] + public int AJ_Interval { + get { + return ((int)(this["AJ_Interval"])); + } + set { + this["AJ_Interval"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("[WebFeed] Automatic JSON file detection")] + public string AJ_Name { + get { + return ((string)(this["AJ_Name"])); + } + set { + this["AJ_Name"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string AJ_URL { + get { + return ((string)(this["AJ_URL"])); + } + set { + this["AJ_URL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public int AJ_Index_UTC { + get { + return ((int)(this["AJ_Index_UTC"])); + } + set { + this["AJ_Index_UTC"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public int AJ_Index_Hex { + get { + return ((int)(this["AJ_Index_Hex"])); + } + set { + this["AJ_Index_Hex"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public int AJ_Index_Call { + get { + return ((int)(this["AJ_Index_Call"])); + } + set { + this["AJ_Index_Call"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public int AJ_Index_Lat { + get { + return ((int)(this["AJ_Index_Lat"])); + } + set { + this["AJ_Index_Lat"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public int AJ_Index_Lon { + get { + return ((int)(this["AJ_Index_Lon"])); + } + set { + this["AJ_Index_Lon"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public int AJ_Index_Alt { + get { + return ((int)(this["AJ_Index_Alt"])); + } + set { + this["AJ_Index_Alt"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public int AJ_Index_Speed { + get { + return ((int)(this["AJ_Index_Speed"])); + } + set { + this["AJ_Index_Speed"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public int AJ_Index_Reg { + get { + return ((int)(this["AJ_Index_Reg"])); + } + set { + this["AJ_Index_Reg"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public int AJ_Index_Track { + get { + return ((int)(this["AJ_Index_Track"])); + } + set { + this["AJ_Index_Track"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public int AJ_Index_Type { + get { + return ((int)(this["AJ_Index_Type"])); + } + set { + this["AJ_Index_Type"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("10")] + public int AJ_Min_Elements { + get { + return ((int)(this["AJ_Min_Elements"])); + } + set { + this["AJ_Min_Elements"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("50")] + public int AJ_Min_Planes { + get { + return ((int)(this["AJ_Min_Planes"])); + } + set { + this["AJ_Min_Planes"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public int AJ_Index_Squawk { + get { + return ((int)(this["AJ_Index_Squawk"])); + } + set { + this["AJ_Index_Squawk"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool AJ_AutoIndex { + get { + return ((bool)(this["AJ_AutoIndex"])); + } + set { + this["AJ_AutoIndex"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public int AJ_Index_Radar { + get { + return ((int)(this["AJ_Index_Radar"])); + } + set { + this["AJ_Index_Radar"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public int AJ_Index_Flight { + get { + return ((int)(this["AJ_Index_Flight"])); + } + set { + this["AJ_Index_Flight"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public int AJ_Index_Route { + get { + return ((int)(this["AJ_Index_Route"])); + } + set { + this["AJ_Index_Route"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute(@"This plane feed is being fetched from Flightradar24.com website +after checking your valid premium accout. +Even though AirScout is checking your account status online: +This service is not authorized by Fligthradar24 and is not guaranteed +to work under all circumstances.Paying for a FR24 premium account +does not automatically grants the right to access the data via AirScout. + +By clicking on ""Accept"" you understand that you are + + DOING THAT ON YOUR OWN RISK + +The auhor of this software will not be responsible in any case.")] + public string FR_Disclaimer { + get { + return ((string)(this["FR_Disclaimer"])); + } + set { + this["FR_Disclaimer"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string FR_Disclaimer_Accepted { + get { + return ((string)(this["FR_Disclaimer_Accepted"])); + } + set { + this["FR_Disclaimer_Accepted"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Web feed from www.flightradar24.com. You need a premim account to use this servic" + + "e.")] + public string FR_Info { + get { + return ((string)(this["FR_Info"])); + } + set { + this["FR_Info"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("60")] + public int FR_Interval { + get { + return ((int)(this["FR_Interval"])); + } + set { + this["FR_Interval"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("[WebFeed] www.flightradar24.com")] + public string FR_Name { + get { + return ((string)(this["FR_Name"])); + } + set { + this["FR_Name"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string FR_Login_User { + get { + return ((string)(this["FR_Login_User"])); + } + set { + this["FR_Login_User"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string FR_Login_Password { + get { + return ((string)(this["FR_Login_Password"])); + } + set { + this["FR_Login_Password"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Web feed from Virtual Radar Server")] + public string VR_Info { + get { + return ((string)(this["VR_Info"])); + } + set { + this["VR_Info"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("[WebFeed] Virtual Radar Server")] + public string VR_Name { + get { + return ((string)(this["VR_Name"])); + } + set { + this["VR_Name"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string VR_Disclaimer { + get { + return ((string)(this["VR_Disclaimer"])); + } + set { + this["VR_Disclaimer"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string VR_Disclaimer_Accepted { + get { + return ((string)(this["VR_Disclaimer_Accepted"])); + } + set { + this["VR_Disclaimer_Accepted"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("60")] + public int VR_Interval { + get { + return ((int)(this["VR_Interval"])); + } + set { + this["VR_Interval"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://public-api.adsbexchange.com/VirtualRadar/AircraftList.json?fNBnd=%MAXLAT%&" + + "fSBnd=%MINLAT%&fWBnd=%MINLON%&fEBnd=%MAXLON%&fAltL=%MINALTFT%&fAltU=%MAXALTFT%")] + public string VR_URL { + get { + return ((string)(this["VR_URL"])); + } + set { + this["VR_URL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool PF_SaveToFile { + get { + return ((bool)(this["PF_SaveToFile"])); + } + set { + this["PF_SaveToFile"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool FR_SaveToFile { + get { + return ((bool)(this["FR_SaveToFile"])); + } + set { + this["FR_SaveToFile"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool AJ_SaveToFile { + get { + return ((bool)(this["AJ_SaveToFile"])); + } + set { + this["AJ_SaveToFile"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool VR_SaveToFile { + get { + return ((bool)(this["VR_SaveToFile"])); + } + set { + this["VR_SaveToFile"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string WF_Disclaimer { + get { + return ((string)(this["WF_Disclaimer"])); + } + set { + this["WF_Disclaimer"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string WF_Disclaimer_Accepted { + get { + return ((string)(this["WF_Disclaimer_Accepted"])); + } + set { + this["WF_Disclaimer_Accepted"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Universal Web Feed")] + public string WF_Info { + get { + return ((string)(this["WF_Info"])); + } + set { + this["WF_Info"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("60")] + public int WF_Interval { + get { + return ((int)(this["WF_Interval"])); + } + set { + this["WF_Interval"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("[WebFeed] Universal Web Feed")] + public string WF_Name { + get { + return ((string)(this["WF_Name"])); + } + set { + this["WF_Name"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool WF_SaveToFile { + get { + return ((bool)(this["WF_SaveToFile"])); + } + set { + this["WF_SaveToFile"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string WF_URL { + get { + return ((string)(this["WF_URL"])); + } + set { + this["WF_URL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public int WF_Index_Call { + get { + return ((int)(this["WF_Index_Call"])); + } + set { + this["WF_Index_Call"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public int WF_Index_Alt { + get { + return ((int)(this["WF_Index_Alt"])); + } + set { + this["WF_Index_Alt"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public int WF_Index_Flight { + get { + return ((int)(this["WF_Index_Flight"])); + } + set { + this["WF_Index_Flight"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public int WF_Index_Hex { + get { + return ((int)(this["WF_Index_Hex"])); + } + set { + this["WF_Index_Hex"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public int WF_Index_Lat { + get { + return ((int)(this["WF_Index_Lat"])); + } + set { + this["WF_Index_Lat"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public int WF_Index_Lon { + get { + return ((int)(this["WF_Index_Lon"])); + } + set { + this["WF_Index_Lon"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public int WF_Index_Radar { + get { + return ((int)(this["WF_Index_Radar"])); + } + set { + this["WF_Index_Radar"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public int WF_Index_Reg { + get { + return ((int)(this["WF_Index_Reg"])); + } + set { + this["WF_Index_Reg"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public int WF_Index_Route { + get { + return ((int)(this["WF_Index_Route"])); + } + set { + this["WF_Index_Route"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public int WF_Index_Speed { + get { + return ((int)(this["WF_Index_Speed"])); + } + set { + this["WF_Index_Speed"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public int WF_Index_Squawk { + get { + return ((int)(this["WF_Index_Squawk"])); + } + set { + this["WF_Index_Squawk"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public int WF_Index_Track { + get { + return ((int)(this["WF_Index_Track"])); + } + set { + this["WF_Index_Track"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public int WF_Index_Type { + get { + return ((int)(this["WF_Index_Type"])); + } + set { + this["WF_Index_Type"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public int WF_Index_UTC { + get { + return ((int)(this["WF_Index_UTC"])); + } + set { + this["WF_Index_UTC"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public int WF_Min_Elements { + get { + return ((int)(this["WF_Min_Elements"])); + } + set { + this["WF_Min_Elements"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public int WF_Min_Planes { + get { + return ((int)(this["WF_Min_Planes"])); + } + set { + this["WF_Min_Planes"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool WF_AutoIndex { + get { + return ((bool)(this["WF_AutoIndex"])); + } + set { + this["WF_AutoIndex"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool VR_ExtendedPlausibilityCheck { + get { + return ((bool)(this["VR_ExtendedPlausibilityCheck"])); + } + set { + this["VR_ExtendedPlausibilityCheck"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("10")] + public double VR_EstimatedPosition_MaxError { + get { + return ((double)(this["VR_EstimatedPosition_MaxError"])); + } + set { + this["VR_EstimatedPosition_MaxError"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool VR_LogErrors { + get { + return ((bool)(this["VR_LogErrors"])); + } + set { + this["VR_LogErrors"] = value; + } + } + } +} diff --git a/AirScout.PlaneFeeds/Properties/Settings.settings b/AirScout.PlaneFeeds/Properties/Settings.settings new file mode 100644 index 0000000..00219c3 --- /dev/null +++ b/AirScout.PlaneFeeds/Properties/Settings.settings @@ -0,0 +1,380 @@ + + + + + + http://droidapp.pinkfroot.com/APPAPIDROID/v7/planeUpdateFAA.php?routetype=IATA&FAA=1&bounds=%MAXLAT%,%MINLAT%,%MINLON%,%MAXLON% + + + 60 + + + Generic Info. + + + Web feed from www.planefinder.net + + + [Generic] Dummy Plane Feed + + + [WebFeed] www.planefinder.net + + + This plane feed is being fetched from an Internet server via Deep Link +technology (see http://en.wikipedia.org/wiki/Deep_link). + +The use is not intended by the website owners and could be changed in URL and data format frequently and without further notice. +Furthermore, it might cause legal issues in some countries. + +By clicking on "Accept" you understand that you are + + DOING THAT ON YOUR OWN RISK + +The auhor of this software will not be responsible in any case. + + + + + + + + + This plane feed is a dummy plane feed generating random plane positions. + +USE THIS FEED FOR DEMONSTRATION PURPOSE ONLY! + +The feed will run without fetching any data from the Internet. +Please do not mix it with other feeds as confusion may occur with live data. +The call signs generated will look like "RND0001", "RND0002" etc. + + + http://localhost:9880/planes.json + + + 60 + + + [WebFeed] AirScout Web Server + + + + + + + + + Web feed from another AirScout server. + + + + + + + + + 60 + + + Reads plane positions out of a JSON file. + + + [FileFeed] JSON file + + + planes.json + + + [RawData] ADSBSharp data + + + Raw data feed from simple ADS-B receivers (DVB-T dongles). +Use this feed thogether with ADSBSharp. + + + + + + + + + + + + localhost + + + 47806 + + + 60 + + + False + + + True + + + True + + + [RawData] RTL1090 data + + + Raw data feed from simple ADS-B receivers (DVB-T dongles). +Use this feed together with RTL1090.exe. + +Feed software must output raw data either binary or ASCII format via TCP. + + + + + + + + + + + + localhost + + + 31001 + + + 60 + + + True + + + True + + + True + + + This plane feed is being fetched from an Internet server via Deep Link +technology (see http://en.wikipedia.org/wiki/Deep_link). + +The use is not intended by the website owners and could be changed in URL and data format frequently and without further notice. +Furthermore, it might cause legal issues in some countries. + +By clicking on "Accept" you understand that you are + + DOING THAT ON YOUR OWN RISK + +The auhor of this software will not be responsible in any case. + + + + + + JSON file from web feed with auto detection of content + + + 60 + + + [WebFeed] Automatic JSON file detection + + + + + + -1 + + + -1 + + + -1 + + + -1 + + + -1 + + + -1 + + + -1 + + + -1 + + + -1 + + + -1 + + + 10 + + + 50 + + + -1 + + + True + + + -1 + + + -1 + + + -1 + + + This plane feed is being fetched from Flightradar24.com website +after checking your valid premium accout. +Even though AirScout is checking your account status online: +This service is not authorized by Fligthradar24 and is not guaranteed +to work under all circumstances.Paying for a FR24 premium account +does not automatically grants the right to access the data via AirScout. + +By clicking on "Accept" you understand that you are + + DOING THAT ON YOUR OWN RISK + +The auhor of this software will not be responsible in any case. + + + + + + Web feed from www.flightradar24.com. You need a premim account to use this service. + + + 60 + + + [WebFeed] www.flightradar24.com + + + + + + + + + Web feed from Virtual Radar Server + + + [WebFeed] Virtual Radar Server + + + + + + + + + 60 + + + http://public-api.adsbexchange.com/VirtualRadar/AircraftList.json?fNBnd=%MAXLAT%&fSBnd=%MINLAT%&fWBnd=%MINLON%&fEBnd=%MAXLON%&fAltL=%MINALTFT%&fAltU=%MAXALTFT% + + + False + + + False + + + False + + + False + + + + + + + + + Universal Web Feed + + + 60 + + + [WebFeed] Universal Web Feed + + + False + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + False + + + True + + + 10 + + + False + + + \ No newline at end of file diff --git a/AirScout.PlaneFeeds/RTL1090.cs b/AirScout.PlaneFeeds/RTL1090.cs new file mode 100644 index 0000000..b3346be --- /dev/null +++ b/AirScout.PlaneFeeds/RTL1090.cs @@ -0,0 +1,489 @@ +using System; +using System.Collections.Generic; +using System.Collections; +using System.Linq; +using System.Text; +using System.Threading; +using System.ComponentModel; +using System.Windows; +using System.Globalization; +using System.Net; +using System.Net.Sockets; +using System.IO; +using AirScout.Aircrafts; +using AirScout.PlaneFeeds.Generic; +using LibADSB; +using ScoutBase.Core; + +namespace AirScout.PlaneFeeds +{ + public class RTLMessage + { + public string RawMessage = ""; + public DateTime TimeStamp = DateTime.UtcNow; + public int SignalStrength = 0; + } + + public class PlaneFeedSettings_RTL + { + [Browsable(true)] + [DescriptionAttribute("Server address for raw ADS-B data.\nUse localhost for running on the same machine.")] + public virtual string Server + { + get + { + return Properties.Settings.Default.RTL_Server; + } + set + { + Properties.Settings.Default.RTL_Server = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(true)] + [DescriptionAttribute("Server port for raw ADS-B data.\nRTLSharp.exe: Port 47806\nRTL1090: Port 31001")] + public virtual int Port + { + get + { + return Properties.Settings.Default.RTL_Port; + } + set + { + Properties.Settings.Default.RTL_Port = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(true)] + [DescriptionAttribute("Interval for updating ADS-B data [sec].")] + public virtual int Interval + { + get + { + return Properties.Settings.Default.RTL_Interval; + } + set + { + Properties.Settings.Default.RTL_Interval = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(true)] + [DescriptionAttribute("Use binary data format for ADS-B data.\nTrue: Use binary format (ADS Beast with MLAT)\nFalse: Use ASCII format (AVR with/without MLAT)")] + public virtual bool Binary + { + get + { + return Properties.Settings.Default.RTL_Binary; + } + set + { + Properties.Settings.Default.RTL_Binary = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(true)] + [DescriptionAttribute("Report ADS-B messages and show in status line.")] + public virtual bool ReportMessages + { + get + { + return Properties.Settings.Default.RTL_Report_Messages; + } + set + { + Properties.Settings.Default.RTL_Report_Messages = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(true)] + [DescriptionAttribute("Marks locally received aircrafts by adding '@' to the call sign")] + public virtual bool MarkLocal + { + get + { + return Properties.Settings.Default.RTL_MarkLocal; + } + set + { + Properties.Settings.Default.RTL_MarkLocal = value; + Properties.Settings.Default.Save(); + } + } + } + + public class PlaneFeed_RTL : PlaneFeed + { + [Browsable(false)] + public override string Name + { + get + { + return Properties.Settings.Default.RTL_Name; ; + } + protected set + { + Properties.Settings.Default.RTL_Name = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(false)] + public override string Disclaimer + { + get + { + return Properties.Settings.Default.RTL_Disclaimer; + } + protected set + { + Properties.Settings.Default.RTL_Disclaimer = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(false)] + public override string DisclaimerAccepted + { + get + { + return Properties.Settings.Default.RTL_Disclaimer_Accepted; + } + set + { + Properties.Settings.Default.RTL_Disclaimer_Accepted = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(false)] + public override string Info + { + get + { + return Properties.Settings.Default.RTL_Info; + } + protected set + { + Properties.Settings.Default.RTL_Info = value; + Properties.Settings.Default.Save(); + } + } + + public new PlaneFeedSettings_RTL FeedSettings = new PlaneFeedSettings_RTL(); + + public PlaneFeed_RTL() + : base () + { + HasSettings = true; + } + + private RTLMessage ReceiveBinaryMsg(Stream stream) + { + // read Mode-S beast binary input + string RTL = null; + int signal_strength = 0; + long nanosec = 0; + long daysec = 0; + DateTime timestamp = DateTime.UtcNow; + byte[] buffer = new byte[23]; + // wait for escape character + DateTime start = DateTime.UtcNow; + DateTime stop = DateTime.Now; + do + { + stream.Read(buffer,0,1); +// System.Console.WriteLine(BitConverter.ToString(buffer,0,1)); + if (buffer[0] == 0x1A) + { + // read next character + stream.Read(buffer,1,1); + switch (buffer[1]) + { + case 0x31: + // do not decode + RTL = null; + break; + case 0x32: + // 7 byte short frame + // read timestamp + stream.Read(buffer, 2, 6); + nanosec = ((buffer[4] & 0x3f) << 24) | + (buffer[5] << 16) | + (buffer[6] << 8) | + (buffer[7]); + daysec = (buffer[2] << 10) | + (buffer[3] << 2) | + (buffer[4] >> 6); + timestamp = DateTime.Today.AddSeconds(daysec); + timestamp = timestamp.AddMilliseconds(nanosec / 1000); + // plausibility check + if (Math.Abs((DateTime.Now - timestamp).Seconds) > 10) + { + // time difference > 10sec --> discard timestamp + timestamp = DateTime.UtcNow; + } + // read signal strength + stream.Read(buffer,8,1); + // plausibility check + if (Math.Abs((DateTime.Now - timestamp).Seconds) > 10) + { + // time difference > 10sec --> discard timestamp + timestamp = DateTime.UtcNow; + } + signal_strength = buffer[8]; + // read frame + stream.Read(buffer,9,7); + // convert to AVR string + RTL = BitConverter.ToString((byte[])buffer,9,7).Replace("-", String.Empty); + RTL = "*" + RTL + ";"; + break; + case 0x33: + // 14 byte long frame + // read timestamp + stream.Read(buffer, 2, 6); + nanosec = ((buffer[4] & 0x3f) << 24) | + (buffer[5] << 16) | + (buffer[6] << 8) | + (buffer[7]); + + daysec = (buffer[2] << 10) | + (buffer[3] << 2) | + (buffer[4] >> 6); + timestamp = DateTime.Today.AddSeconds(daysec); + timestamp = timestamp.AddMilliseconds(nanosec / 1000); + // plausibility check + if (Math.Abs((DateTime.Now - timestamp).Seconds) > 10) + { + // time difference > 10sec --> discard timestamp + timestamp = DateTime.UtcNow; + } + // read signal strength + stream.Read(buffer, 8, 1); + signal_strength = buffer[8]; + // read frame + stream.Read(buffer, 9, 14); + // convert to AVR string + RTL = BitConverter.ToString((byte[])buffer,9,14).Replace("-", String.Empty); + RTL = "*" + RTL + ";"; + break; + default: + // false decode + RTL = null; + break; + } + } + // check for timeout 10sec + stop = DateTime.UtcNow; + if (stop - start > new TimeSpan(0, 0, 10)) + throw new TimeoutException(); + } + while ((RTL == null) && !this.CancellationPending); + if (RTL == null) + return null; + RTLMessage msg = new RTLMessage(); + msg.RawMessage = RTL; + msg.TimeStamp = timestamp; + msg.SignalStrength = signal_strength; + return msg; + } + + private RTLMessage ReceiveAVRMsg ( StreamReader sr) + { + RTLMessage msg = new RTLMessage(); + // read AVR format input + msg.RawMessage = sr.ReadLine(); + if (msg.RawMessage.StartsWith("*")) + { + // standard AVR message + // no timestamp in telegram --> set timestamp after reading + msg.TimeStamp = DateTime.UtcNow; + msg.SignalStrength = 0; + } + else if (msg.RawMessage.StartsWith("@")) + { + // extended AVR message wit MLAT + // convert into standard message format + // extract time string + string time = msg.RawMessage.Substring(1, 12); + // TODO: interprete the MLAT timestamp! + msg.TimeStamp = DateTime.UtcNow; + msg.RawMessage = "*" + msg.RawMessage.Remove(0, 13); + } + return msg; + } + + protected override void OnDoWork(DoWorkEventArgs e) + { + Log.WriteMessage("Started."); + PlaneFeedWorkEventArgs args = (PlaneFeedWorkEventArgs)e.Argument; + // set directories + AppDirectory = args.AppDirectory; + AppDataDirectory = args.AppDataDirectory; + LogDirectory = args.LogDirectory; + TmpDirectory = args.TmpDirectory; + DatabaseDirectory = args.DatabaseDirectory; + // set boundaries from arguments + MaxLat = args.MaxLat; + MinLon = args.MinLon; + MinLat = args.MinLat; + MaxLon = args.MaxLon; + MyLat = args.MyLat; + MyLon = args.MyLon; + DXLat = args.DXLat; + DXLon = args.DXLon; + MinAlt = args.MinAlt; + MaxAlt = args.MaxAlt; + + // check boundaries + if ((MaxLat <= MinLat) || (MaxLon <= MinLon)) + { + Status = STATUS.ERROR; + this.ReportProgress((int)PROGRESS.ERROR, "Area boundaries mismatch. Check your Covered Area parameters!"); + } + else + { + Thread.CurrentThread.Priority = ThreadPriority.Highest; + Status = STATUS.OK; + ADSBDecoder decoder = new ADSBDecoder(); + DateTime lastreported = DateTime.UtcNow; + StreamReader sr = null; + TcpClient client = null; + RTLMessage msg = null; + // outer loop + do + { + try + { + // setup TCP listener + client = new TcpClient(); + string server = Properties.Settings.Default.RTL_Server; + client.Connect(server, Properties.Settings.Default.RTL_Port); + sr = new StreamReader(client.GetStream()); + // inner loop + // receive messages in a loop + do + { + if (FeedSettings.Binary) + msg = ReceiveBinaryMsg(sr.BaseStream); + else + msg = ReceiveAVRMsg(sr); + // decode the message + if (msg.RawMessage.StartsWith("*") && msg.RawMessage.EndsWith(";")) + { + // try to decode the message + string info = ""; + try + { + Console.Write(msg.RawMessage + " --> "); + info = decoder.DecodeMessage(msg.RawMessage, msg.TimeStamp); + // report messages to main window if activated + if (FeedSettings.ReportMessages && (info.StartsWith("["))) + this.ReportProgress((int)PROGRESS.STATUS, info); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + Console.WriteLine(info); + } + DateTime stop = DateTime.UtcNow; + // check if update report is necessary + if ((DateTime.UtcNow - lastreported).TotalSeconds > FeedSettings.Interval) + { + lastreported = DateTime.UtcNow; + // time to report planes + ArrayList list = decoder.GetPlanes(); + if (list.Count > 0) + { + // convert to plane info list + List planes = new List(); + foreach (ADSBInfo info in list) + { + PlaneInfo plane = new PlaneInfo(); + plane.Time = info.Timestamp; + plane.Hex = info.ICAO24; + // mark call with "@" if option is enabled + plane.Call = (FeedSettings.MarkLocal) ? "@" + info.Call : info.Call; + plane.Lat = info.Lat; + plane.Lon = info.Lon; + plane.Alt = info.Alt; + plane.Speed = info.Speed; + plane.Track = info.Heading; + plane.Reg = "[unknown]"; + plane.Type = "[unknown]"; + plane.Manufacturer = "[unknown]"; + plane.Model = "[unknown]"; + plane.Category = PLANECATEGORY.NONE; + // try to get the registration and type + AircraftDesignator aircraft = AircraftData.Database.AircraftFindByHex(plane.Hex); + if (aircraft != null) + { + plane.Reg = aircraft.Reg; + plane.Type = aircraft.TypeCode; + // try to get the type + AircraftTypeDesignator type = AircraftData.Database.AircraftTypeFindByICAO(plane.Type); + if (plane != null) + { + plane.Manufacturer = type.Manufacturer; + plane.Model = type.Model; + plane.Category = type.Category; + } + } + planes.Add(plane); + } + ReportProgress((int)PROGRESS.PLANES, planes); + AircraftData.Database.PlaneInfoBulkInsertOrUpdateIfNewer(planes); + string message = "[" + lastreported.ToString("HH:mm:ss") + "] " + + decoder.Count.ToString() + " Positions updated from local ADS-B receiver."; + this.ReportProgress((int)PROGRESS.STATUS, message); + } + } + } + while ((msg != null) && !CancellationPending); + } + catch (Exception ex) + { + Log.WriteMessage(ex.Message); + this.ReportProgress((int)PROGRESS.ERROR, "Error reading from TCP connection: " + ex.Message); + Thread.Sleep(10000); + } + finally + { + // try to close the stream and TCP client + try + { + if (sr != null) + sr.Close(); + } + catch + { + } + try + { + if (client != null) + client.Close(); + } + catch + { + } + } + } + while (!this.CancellationPending || (Status != STATUS.OK)); + } + this.ReportProgress((int)PROGRESS.FINISHED); + Log.WriteMessage("Finished."); + } + + public override Object GetFeedSettings() + { + return FeedSettings; + } + + } +} diff --git a/AirScout.PlaneFeeds/Settings.cs b/AirScout.PlaneFeeds/Settings.cs new file mode 100644 index 0000000..a8565db --- /dev/null +++ b/AirScout.PlaneFeeds/Settings.cs @@ -0,0 +1,30 @@ +namespace PlaneFeeds.Properties { + + + // Diese Klasse ermöglicht die Behandlung bestimmter Ereignisse der Einstellungsklasse: + // Das SettingChanging-Ereignis wird ausgelöst, bevor der Wert einer Einstellung geändert wird. + // Das PropertyChanged-Ereignis wird ausgelöst, nachdem der Wert einer Einstellung geändert wurde. + // Das SettingsLoaded-Ereignis wird ausgelöst, nachdem die Einstellungswerte geladen wurden. + // Das SettingsSaving-Ereignis wird ausgelöst, bevor die Einstellungswerte gespeichert werden. +// internal sealed partial class Settings + public partial class Settings + { + + public Settings() { + // // Heben Sie die Auskommentierung der unten angezeigten Zeilen auf, um Ereignishandler zum Speichern und Ändern von Einstellungen hinzuzufügen: + // + // this.SettingChanging += this.SettingChangingEventHandler; + // + // this.SettingsSaving += this.SettingsSavingEventHandler; + // + } + + private void SettingChangingEventHandler(object sender, System.Configuration.SettingChangingEventArgs e) { + // Fügen Sie hier Code zum Behandeln des SettingChangingEvent-Ereignisses hinzu. + } + + private void SettingsSavingEventHandler(object sender, System.ComponentModel.CancelEventArgs e) { + // Fügen Sie hier Code zum Behandeln des SettingsSaving-Ereignisses hinzu. + } + } +} diff --git a/AirScout.PlaneFeeds/VirtualRadarServer.cs b/AirScout.PlaneFeeds/VirtualRadarServer.cs new file mode 100644 index 0000000..036e176 --- /dev/null +++ b/AirScout.PlaneFeeds/VirtualRadarServer.cs @@ -0,0 +1,626 @@ +using System; +using System.Collections.Generic; +using System.Collections; +using System.Linq; +using System.Text; +using System.Threading; +using System.ComponentModel; +using System.Windows; +using System.Globalization; +using System.Net; +using System.IO; +using AirScout.Aircrafts; +using AirScout.PlaneFeeds.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using ScoutBase.Core; +using System.Diagnostics; + +namespace AirScout.PlaneFeeds +{ + public class PlaneFeedSettings_VR : PlaneFeedSettings + { + [CategoryAttribute("Web Feed")] + [DescriptionAttribute("Base URL for website.")] + public virtual string URL + { + get + { + return Properties.Settings.Default.VR_URL; + } + set + { + Properties.Settings.Default.VR_URL = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Web Feed")] + [DescriptionAttribute("Update interval for website request [seconds]")] + public virtual int Interval + { + get + { + return Properties.Settings.Default.VR_Interval; + } + set + { + Properties.Settings.Default.VR_Interval = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Web Feed")] + [DescriptionAttribute("Save downloaded JSON to file")] + public virtual bool SaveToFile + { + get + { + return Properties.Settings.Default.VR_SaveToFile; + } + set + { + Properties.Settings.Default.VR_SaveToFile = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Web Feed")] + [DescriptionAttribute("Extended plausibility check of received position messages")] + public virtual bool ExtendedPlausibilityCheck + { + get + { + return Properties.Settings.Default.VR_ExtendedPlausibilityCheck; + } + set + { + Properties.Settings.Default.VR_ExtendedPlausibilityCheck = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Web Feed")] + [DescriptionAttribute("Maximum error in [km] between estimated and reported position")] + public virtual double EstimatedPositionMaxError + { + get + { + return Properties.Settings.Default.VR_EstimatedPosition_MaxError; + } + set + { + Properties.Settings.Default.VR_EstimatedPosition_MaxError = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Web Feed")] + [DescriptionAttribute("Write errors to log file")] + public virtual bool LogErros + { + get + { + return Properties.Settings.Default.VR_LogErrors; + } + set + { + Properties.Settings.Default.VR_LogErrors = value; + Properties.Settings.Default.Save(); + } + } + + } + + public class PlaneFeed_VR : PlaneFeed + { + [Browsable(false)] + public override string Name + { + get + { + return Properties.Settings.Default.VR_Name; ; + } + protected set + { + Properties.Settings.Default.VR_Name = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(false)] + public override string Disclaimer + { + get + { + return Properties.Settings.Default.VR_Disclaimer; + } + protected set + { + Properties.Settings.Default.VR_Disclaimer = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(false)] + public override string DisclaimerAccepted + { + get + { + return Properties.Settings.Default.VR_Disclaimer_Accepted; + } + set + { + Properties.Settings.Default.VR_Disclaimer_Accepted = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(false)] + public override string Info + { + get + { + return Properties.Settings.Default.VR_Info; + } + protected set + { + Properties.Settings.Default.VR_Info = value; + Properties.Settings.Default.Save(); + } + } + + public new PlaneFeedSettings_VR FeedSettings = new PlaneFeedSettings_VR(); + + private PlaneInfoCache PlanePositions = new PlaneInfoCache(); + + public PlaneFeed_VR() + : base () + { + HasSettings = true; + } + + private string ReadPropertyString(JObject o, string propertyname) + { + if (o.Property(propertyname) == null) + return null; + return o.Property(propertyname).Value.Value(); + } + + private int ReadPropertyDoubleToInt(JObject o, string propertyname) + { + if (o.Property(propertyname) == null) + return int.MinValue; + double d = ReadPropertyDouble(o,propertyname); + if ((d != double.MinValue) && (d >= int.MinValue) && (d <= int.MaxValue)) + return (int)d; + return int.MinValue; + } + + private double ReadPropertyDouble(JObject o, string propertyname) + { + if (o.Property(propertyname) == null) + return double.MinValue; + return o.Property(propertyname).Value.Value(); + } + + private long ReadPropertyLong(JObject o, string propertyname) + { + if (o.Property(propertyname) == null) + return long.MinValue; + return o.Property(propertyname).Value.Value(); + } + + protected override void OnDoWork(DoWorkEventArgs e) + { + Log.WriteMessage("Started."); + PlaneFeedWorkEventArgs args = (PlaneFeedWorkEventArgs)e.Argument; + if (String.IsNullOrEmpty(Thread.CurrentThread.Name)) + Thread.CurrentThread.Name = this.GetType().Name; + + // set directories + AppDirectory = args.AppDirectory; + AppDataDirectory = args.AppDataDirectory; + LogDirectory = args.LogDirectory; + TmpDirectory = args.TmpDirectory; + DatabaseDirectory = args.DatabaseDirectory; + KeepHistory = args.KeepHistory; + + // set boundaries from arguments + MaxLat = args.MaxLat; + MinLon = args.MinLon; + MinLat = args.MinLat; + MaxLon = args.MaxLon; + MyLat = args.MyLat; + MyLon = args.MyLon; + DXLat = args.DXLat; + DXLon = args.DXLon; + MinAlt = args.MinAlt; + MaxAlt = args.MaxAlt; + + // intialize variables + VC.AddVar("APPDIR", AppDirectory); + VC.AddVar("DATADIR", AppDataDirectory); + VC.AddVar("LOGDIR", LogDirectory); + VC.AddVar("DATABASEDIR", DatabaseDirectory); + VC.AddVar("MINLAT", MinLat); + VC.AddVar("MAXLAT", MaxLat); + VC.AddVar("MINLON", MinLon); + VC.AddVar("MAXLON", MaxLon); + VC.AddVar("MINALTM", MinAlt); + VC.AddVar("MAXALTM", MaxAlt); + VC.AddVar("MINALTFT", (int)UnitConverter.m_ft((double)MinAlt)); + VC.AddVar("MAXALTFT", (int)UnitConverter.m_ft((double)MaxAlt)); + + // use PlaneInfoConverter for plausibility check + PlaneInfoConverter C = new PlaneInfoConverter(); + + // check boundaries + if ((MaxLat <= MinLat) || (MaxLon <= MinLon)) + { + Status = STATUS.ERROR; + this.ReportProgress((int)PROGRESS.ERROR, "Area boundaries mismatch. Check your Covered Area parameters!"); + } + else + { + Status = STATUS.OK; + int interval = Properties.Settings.Default.VR_Interval; + // run loop + do + { + string json = ""; + Stopwatch st = new Stopwatch(); + st.Start(); + // calculate url and get json + String url = VC.ReplaceAllVars(Properties.Settings.Default.VR_URL); + try + { + Console.WriteLine("Creating web request: " + url); + HttpWebRequest webrequest = (HttpWebRequest)HttpWebRequest.Create(url); + webrequest.Referer = "http://www.vrs-world.com/"; + webrequest.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:37.0) Gecko/20100101 Firefox/37.0"; + webrequest.Accept = "application/json, text/javascript, */*;q=0.01"; + webrequest.AutomaticDecompression = System.Net.DecompressionMethods.Deflate | System.Net.DecompressionMethods.GZip; + Console.WriteLine("Getting web response"); + HttpWebResponse webresponse = (HttpWebResponse)webrequest.GetResponse(); + Console.WriteLine("Reading stream"); + using (StreamReader sr = new StreamReader(webresponse.GetResponseStream())) + { + json = sr.ReadToEnd(); + } + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + Log.WriteMessage(ex.ToString()); + // do nothing + } + if (FeedSettings.SaveToFile) + { + try + { + using (StreamWriter sw = new StreamWriter(TmpDirectory + Path.DirectorySeparatorChar + "vrs" + DateTime.UtcNow.ToString("yyyy-MM-dd HH_mm_ss") + ".json")) + { + sw.WriteLine(json); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + // deserialize JSON file + int total = 0; + int count = 0; + int errors = 0; + try + { + JObject root = (JObject)JsonConvert.DeserializeObject(json); + // 2017-07-23: workaround for "jumping planes" due to incorrect time stamps + // try to get the server time to adjust the time stamps in plane positions + // --> compare server time with local time and calculate offset + // default offset is 0 + long toffset = 0; + try + { + // get local time of request in milliseconds + DateTime lt = DateTime.UtcNow; + long ltime = (long)(lt - new DateTime(1970, 1, 1)).TotalMilliseconds; + // get server time in milliseconds + long stime = ReadPropertyLong(root, "stm"); + DateTime sti = new System.DateTime(1970, 1, 1, 0, 0, 0, 0).AddMilliseconds(stime); + // calculate offset in milliseconds + toffset = ltime - stime; + // check value + string message = "Server timestamp difference (server <> local): " + sti.ToString("yyyy-MM-dd HH:mm:ss,fffZ") + " <> " + lt.ToString("yyyy-MM-dd HH:mm:ss,fffZ"); + // server time is more than 10.000 ms in the future --> keep offset for correction and log an error message + // else --> set offset to 0 to work with real timestamps of entries without correction + if (toffset <= -10000) + { + message += " --> timestamp correction applied!"; + Log.WriteMessage(message); + + } + else + { + // clear offset + toffset = 0; + } + } + catch + { + // do nothing if property is not found + } + // analyze json string for planes data + foreach (JProperty proot in root.Children()) + { + // get the planes position list + if (proot.Name == "acList") + { + List planes = new List(); + foreach (JArray a in proot.Children()) + { + foreach (JObject o in a.Values()) + { + PlaneInfo plane = new PlaneInfo(); + total++; + try + { + // get hex first + plane.Hex = ReadPropertyString(o, "Icao"); + // do basic check on hex --> is strictly needed as identifier + if (!PlaneInfo.Check_Hex(plane.Hex)) + { + if (Properties.Settings.Default.VR_LogErrors) + Log.WriteMessage("Incorrect aircraft data received [Hex]: " + plane.Hex); + errors++; + continue; + } + // get position and do basic check on lat/lon + plane.Lat = ReadPropertyDouble(o, "Lat"); + if (!PlaneInfo.Check_Lat(plane.Lat)) + { + if (Properties.Settings.Default.VR_LogErrors) + Log.WriteMessage("Incorrect aircraft data received [Lat]: " + plane.Lat.ToString("F8", CultureInfo.InvariantCulture)); + errors++; + continue; + } + plane.Lon = ReadPropertyDouble(o, "Long"); + if (!PlaneInfo.Check_Lon(plane.Lon)) + { + if (Properties.Settings.Default.VR_LogErrors) + Log.WriteMessage("Incorrect aircraft data received [Lon]: " + plane.Lon.ToString("F8", CultureInfo.InvariantCulture)); + errors++; + continue; + } + // get altitude + // 2017-07-23: take "GAlt" (corrected altitude by air pressure) rather than "Alt" + plane.Alt = ReadPropertyDoubleToInt(o, "GAlt"); + // do basic chekc on altitude + if (!PlaneInfo.Check_Alt(plane.Alt)) + { + // try to recover altitude from previuos messages + PlaneInfo info = null; + if (PlanePositions.TryGetValue(plane.Hex, out info)) + { + plane.Alt = info.Alt; + } + else + { + if (Properties.Settings.Default.VR_LogErrors) + Log.WriteMessage("Incorrect aircraft data received [Alt]: " + plane.Alt.ToString("F8", CultureInfo.InvariantCulture)); + errors++; + continue; + } + } + // continue if alt is out of bounds + if ((plane.Alt_m < MinAlt) || (plane.Alt_m > MaxAlt)) + { + continue; + } + // get callsign + plane.Call = ReadPropertyString(o, "Call"); + // do basic check --> try to recover from cache if check fails or set it to [unknown] + if (!PlaneInfo.Check_Call(plane.Call)) + { + PlaneInfo info = null; + if (PlanePositions.TryGetValue(plane.Hex, out info)) + { + plane.Call = info.Call; + } + else + plane.Call = "[unknown]"; + } + // still unknown --> try to recover last known call from database + if (!PlaneInfo.Check_Call(plane.Call)) + { + AircraftDesignator ad = AircraftData.Database.AircraftFindByHex(plane.Hex); + if (ad != null) + { + plane.Call = ad.Call; + } + else + plane.Call = "[unknown]"; + } + // get registration + plane.Reg = ReadPropertyString(o, "Reg"); + // do basic check --> try to recover from cache if check fails or set it to [unknown] + if (!PlaneInfo.Check_Reg(plane.Reg)) + { + PlaneInfo info = null; + if (PlanePositions.TryGetValue(plane.Hex, out info)) + { + plane.Reg = info.Reg; + } + else + plane.Reg = "[unknown]"; + } + // still unknown --> try to recover last known reg from database + if (!PlaneInfo.Check_Reg(plane.Reg)) + { + AircraftDesignator ad = AircraftData.Database.AircraftFindByHex(plane.Hex); + if (ad != null) + { + plane.Reg = ad.Reg; + } + else + plane.Reg = "[unknown]"; + } + // get track + plane.Track = ReadPropertyDoubleToInt(o, "Trak"); + // do basic check + if (!PlaneInfo.Check_Track(plane.Track)) + { + if (Properties.Settings.Default.VR_LogErrors) + Log.WriteMessage("Incorrect aircraft data received [Track]: " + plane.Track.ToString("F8", CultureInfo.InvariantCulture)); + errors++; + continue; + } + // get speed + plane.Speed = ReadPropertyDoubleToInt(o, "Spd"); + // do basic check + if (!PlaneInfo.Check_Speed(plane.Speed)) + { + // try to recover speed from previous messages + PlaneInfo info = null; + if (PlanePositions.TryGetValue(plane.Hex, out info)) + { + plane.Speed = info.Speed; + } + else + { + if (Properties.Settings.Default.VR_LogErrors) + Log.WriteMessage("Incorrect aircraft data received [Speed]: " + plane.Speed.ToString("F8", CultureInfo.InvariantCulture)); + errors++; + continue; + } + } + // get position timestamp + // CAUTION!! time is UNIX time in milliseconds + long l = ReadPropertyLong(o, "PosTime"); + if (l != long.MinValue) + { + // 2017-07-23: correct timestamp with offset + l = l + toffset; + DateTime timestamp = new System.DateTime(1970, 1, 1, 0, 0, 0, 0); + timestamp = timestamp.AddMilliseconds(l); + plane.Time = timestamp; + plane.Comment = toffset.ToString(); + } + else + { + if (Properties.Settings.Default.VR_LogErrors) + Log.WriteMessage("Incorrect aircraft data received [Time]: " + l.ToString()); + errors++; + continue; + } + // get type info + plane.Type = ReadPropertyString(o, "Type"); + if (!PlaneInfo.Check_Type(plane.Type)) + { + AircraftDesignator ad = AircraftData.Database.AircraftFindByHex(plane.Hex); + if (ad != null) + plane.Type = ad.TypeCode; + else + plane.Type = "[unknown]"; + } + // get extended plane type info + plane.Manufacturer = ReadPropertyString(o, "Man"); + plane.Model = ReadPropertyString(o, "Mdl"); + try + { + plane.Category = (PLANECATEGORY)ReadPropertyLong(o, "WTC"); + } + catch + { + plane.Category = PLANECATEGORY.NONE; + } + // keep the "SUPERHEAVY" category for A380-800 as not supported by VirtualRadarServer yet + if (plane.Type == "A388") + plane.Category = PLANECATEGORY.SUPERHEAVY; + // try to recover type info from database if check fails + if (!PlaneInfo.Check_Manufacturer(plane.Manufacturer) || !PlaneInfo.Check_Model(plane.Model)) + { + AircraftTypeDesignator td = AircraftData.Database.AircraftTypeFindByICAO(plane.Type); + if (td != null) + { + plane.Manufacturer = td.Manufacturer; + plane.Model = td.Model; + plane.Category = td.Category; + } + else + { + plane.Manufacturer = "[unknown]"; + plane.Model = "[unknown]"; + plane.Category = PLANECATEGORY.NONE; + } + } + // remove manufacturer if part of model description + if (plane.Model.StartsWith(plane.Manufacturer)) + plane.Model = plane.Model.Remove(0, plane.Manufacturer.Length).Trim(); + // check position against etimated position if possible + PlaneInfo oldplane = PlanePositions.Get(plane.Hex, plane.Time, 5); + double dist = 0; + if (Properties.Settings.Default.VR_ExtendedPlausibilityCheck && (oldplane != null) && ((dist = LatLon.Distance(oldplane.Lat, oldplane.Lon, plane.Lat, plane.Lon)) > Properties.Settings.Default.VR_EstimatedPosition_MaxError)) + { + // report error + if (Properties.Settings.Default.VR_LogErrors) + Log.WriteMessage("Incorrect aircraft position received [(" + oldplane.Lat.ToString("F8") + "," + oldplane.Lon.ToString("F8") + ")<" + dist.ToString("F0") + "km>(" + plane.Lat.ToString("F8") + "," + plane.Lon.ToString("F8") + ")]: " + plane.ToString()); + errors++; + continue; + } + if (plane.Speed < 0) + Console.WriteLine(""); + // all checks successfully done --> add plane to list + planes.Add(plane); + count++; + if (this.CancellationPending) + return; + } + catch (Exception ex) + { + Log.WriteMessage(ex.Message); + errors++; + } + } + if (this.CancellationPending) + return; + } + // update local cache + this.PlanePositions.BulkInsertOrUpdateIfNewer(planes); + // report planes to main program + this.ReportProgress((int)PROGRESS.PLANES, planes); + // update global database + AircraftData.Database.PlaneInfoBulkInsertOrUpdateIfNewer(this, planes); + st.Stop(); + string msg = "[" + DateTime.UtcNow.ToString("HH:mm:ss") + "] " + + total.ToString() + " Positions updated from " + url + ", " + + st.ElapsedMilliseconds.ToString() + " ms. OK: " + count.ToString() + ", Errors: " + errors.ToString(); + this.ReportProgress((int)PROGRESS.STATUS, msg); + } + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.Message); + } + int i = 0; + while (!CancellationPending && (i < interval)) + { + Thread.Sleep(1000); + i++; + } + } + while (!this.CancellationPending || (Status != STATUS.OK)); + } + this.ReportProgress((int)PROGRESS.FINISHED); + Log.WriteMessage("Finished."); + } + + public override Object GetFeedSettings() + { + return FeedSettings; + } + + } +} diff --git a/AirScout.PlaneFeeds/WebFeed.cs b/AirScout.PlaneFeeds/WebFeed.cs new file mode 100644 index 0000000..449a095 --- /dev/null +++ b/AirScout.PlaneFeeds/WebFeed.cs @@ -0,0 +1,1509 @@ +using System; +using System.Collections.Generic; +using System.Collections; +using System.Linq; +using System.Text; +using System.Threading; +using System.ComponentModel; +using System.Windows; +using System.Globalization; +using System.Net; +using System.IO; +using System.Data; +using System.Windows.Forms; +using System.Xml.Serialization; +using AirScout.Aircrafts; +using AirScout.PlaneFeeds.Generic; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using ScoutBase.Core; +using AirScout.AircraftPositions; +using static ScoutBase.Core.ZIP; + +namespace AirScout.PlaneFeeds +{ + [Serializable] + public class PlanefeedSettings_WF : PlaneFeedSettings + { + [CategoryAttribute("Web Feed")] + [DescriptionAttribute("Base URL for website.")] + public virtual string URL + { + get + { + return Properties.Settings.Default.WF_URL; + } + set + { + // detect change in URL + if (Properties.Settings.Default.WF_URL != value) + { + // reset array indices + ResetIndex(); + } + Properties.Settings.Default.WF_URL = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Web Feed")] + [DescriptionAttribute("Update interval for website request [seconds]")] + public virtual int Interval + { + get + { + return Properties.Settings.Default.WF_Interval; + } + set + { + Properties.Settings.Default.WF_Interval = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Web Feed")] + [DescriptionAttribute("Save downloaded JSON to file")] + public virtual bool SaveToFile + { + get + { + return Properties.Settings.Default.WF_SaveToFile; + } + set + { + Properties.Settings.Default.WF_SaveToFile = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(true)] + [CategoryAttribute("Data Field Properties")] + [DescriptionAttribute("Try to find all Indices automatically")] + public virtual bool AutoIndex + { + get + { + return Properties.Settings.Default.WF_AutoIndex; + } + set + { + // detect change from false to true + if (!Properties.Settings.Default.WF_AutoIndex && value) + { + // reset array indices + ResetIndex(); + } + Properties.Settings.Default.WF_AutoIndex = value; + Properties.Settings.Default.Save(); + + } + } + + [CategoryAttribute("Data Field Properties")] + [DescriptionAttribute("Index for UTC")] + [XmlElement("Index_UTC")] + public virtual int Index_UTC + { + get + { + return Properties.Settings.Default.WF_Index_UTC; + } + set + { + Properties.Settings.Default.WF_Index_UTC = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Data Field Properties")] + [DescriptionAttribute("Index for Hex")] + public virtual int Index_Hex + { + get + { + return Properties.Settings.Default.WF_Index_Hex; + } + set + { + Properties.Settings.Default.WF_Index_Hex = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Data Field Properties")] + [DescriptionAttribute("Index for Call")] + public virtual int Index_Call + { + get + { + return Properties.Settings.Default.WF_Index_Call; + } + set + { + Properties.Settings.Default.WF_Index_Call = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Data Field Properties")] + [DescriptionAttribute("Index for Latitude")] + public virtual int Index_Lat + { + get + { + return Properties.Settings.Default.WF_Index_Lat; + } + set + { + Properties.Settings.Default.WF_Index_Lat = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Data Field Properties")] + [DescriptionAttribute("Index for Longitde")] + public virtual int Index_Lon + { + get + { + return Properties.Settings.Default.WF_Index_Lon; + } + set + { + Properties.Settings.Default.WF_Index_Lon = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Data Field Properties")] + [DescriptionAttribute("Index for Altitude")] + public virtual int Index_Alt + { + get + { + return Properties.Settings.Default.WF_Index_Alt; + } + set + { + Properties.Settings.Default.WF_Index_Alt = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Data Field Properties")] + [DescriptionAttribute("Index for Speed")] + public virtual int Index_Speed + { + get + { + return Properties.Settings.Default.WF_Index_Speed; + } + set + { + Properties.Settings.Default.WF_Index_Speed = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Data Field Properties")] + [DescriptionAttribute("Index for Registration")] + public virtual int Index_Reg + { + get + { + return Properties.Settings.Default.WF_Index_Reg; + } + set + { + Properties.Settings.Default.WF_Index_Reg = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Data Field Properties")] + [DescriptionAttribute("Index for Flight Code")] + public virtual int Index_Flight + { + get + { + return Properties.Settings.Default.WF_Index_Flight; + } + set + { + Properties.Settings.Default.WF_Index_Flight = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Data Field Properties")] + [DescriptionAttribute("Index for Track")] + public virtual int Index_Track + { + get + { + return Properties.Settings.Default.WF_Index_Track; + } + set + { + Properties.Settings.Default.WF_Index_Track = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Data Field Properties")] + [DescriptionAttribute("Index for Type")] + public virtual int Index_Type + { + get + { + return Properties.Settings.Default.WF_Index_Type; + } + set + { + Properties.Settings.Default.WF_Index_Type = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Data Field Properties")] + [DescriptionAttribute("Index for Squawk")] + public virtual int Index_Squawk + { + get + { + return Properties.Settings.Default.WF_Index_Squawk; + } + set + { + Properties.Settings.Default.WF_Index_Squawk = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Data Field Properties")] + [DescriptionAttribute("Index for Radar")] + public virtual int Index_Radar + { + get + { + return Properties.Settings.Default.WF_Index_Radar; + } + set + { + Properties.Settings.Default.WF_Index_Radar = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Data Field Properties")] + [DescriptionAttribute("Index for Route")] + public virtual int Index_Route + { + get + { + return Properties.Settings.Default.WF_Index_Route; + } + set + { + Properties.Settings.Default.WF_Index_Route = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Data Field Properties")] + [DescriptionAttribute("Minimum Count of Elements per Plane Position Data Set")] + public virtual int Min_Elements + { + get + { + return Properties.Settings.Default.WF_Min_Elements; + } + set + { + Properties.Settings.Default.WF_Min_Elements = value; + Properties.Settings.Default.Save(); + } + } + + [CategoryAttribute("Data Field Properties")] + [DescriptionAttribute("Minium Count of Plane Position Data Sets")] + public virtual int Min_Planes + { + get + { + return Properties.Settings.Default.WF_Min_Planes; + } + set + { + Properties.Settings.Default.WF_Min_Planes = value; + Properties.Settings.Default.Save(); + } + } + + private void ResetIndex() + { + Index_Alt = -1; + Index_Call = -1; + Index_Hex = -1; + Index_Lat = -1; + Index_Lon = -1; + Index_Reg = -1; + Index_Flight = -1; + Index_Speed = -1; + Index_Squawk = -1; + Index_Track = -1; + Index_Radar = -1; + Index_Route = -1; + Index_Type = -1; + Index_UTC = -1; + } + + } + + public class PlaneFeed_WF : PlaneFeed + { + [Browsable(false)] + public override string Name + { + get + { + return Properties.Settings.Default.WF_Name; ; + } + protected set + { + Properties.Settings.Default.WF_Name = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(false)] + public override string Disclaimer + { + get + { + return Properties.Settings.Default.WF_Disclaimer; + } + protected set + { + Properties.Settings.Default.WF_Disclaimer = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(false)] + public override string DisclaimerAccepted + { + get + { + return Properties.Settings.Default.WF_Disclaimer_Accepted; + } + set + { + Properties.Settings.Default.WF_Disclaimer_Accepted = value; + Properties.Settings.Default.Save(); + } + } + + [Browsable(false)] + public override string Info + { + get + { + return Properties.Settings.Default.WF_Info; + } + protected set + { + Properties.Settings.Default.WF_Info = value; + Properties.Settings.Default.Save(); + } + } + + public new PlanefeedSettings_WF FeedSettings = new PlanefeedSettings_WF(); + + private Dictionary JProperties = new Dictionary(); + private Dictionary JArrays = new Dictionary(); + + private SortedList Maj = new SortedList(new DuplicateKeyComparer()); + + string[][] Values = new string[0][]; + string[][] Names = new string[0][]; + + string[][] Hexs = new string[0][]; + string[][] Regs = new string[0][]; + string[][] Flights = new string[0][]; + string[][] Calls = new string[0][]; + double[][] Lats = new double[0][]; + double[][] Lons = new double[0][]; + int[][] Alts = new int[0][]; + int[][] Speeds = new int[0][]; + int[][] Tracks = new int[0][]; + int[][] Squawks = new int[0][]; + string[][] Radars = new string[0][]; + string[][] Routes = new string[0][]; + string[][] Types = new string[0][]; + DateTime[][] UTCs = new DateTime[0][]; + + int[] Maj_Hexs = new int[0]; + int[] Maj_Regs = new int[0]; + int[] Maj_Flights = new int[0]; + int[] Maj_Lats = new int[0]; + int[] Maj_Calls = new int[0]; + int[] Maj_Lons = new int[0]; + int[] Maj_Alts = new int[0]; + int[] Maj_Speeds = new int[0]; + int[] Maj_Tracks = new int[0]; + int[] Maj_Squawks = new int[0]; + int[] Maj_Radars = new int[0]; + int[] Maj_Routes = new int[0]; + int[] Maj_Types = new int[0]; + int[] Maj_UTCs = new int[0]; + + double[] Min_Lats = new double[0]; + double[] Min_Lons = new double[0]; + int[] Min_Alts = new int[0]; + int[] Min_Speeds = new int[0]; + int[] Min_Tracks = new int[0]; + + double[] Max_Lats = new double[0]; + double[] Max_Lons = new double[0]; + int[] Max_Alts = new int[0]; + int[] Max_Speeds = new int[0]; + int[] Max_Tracks = new int[0]; + + int[] Min_Chars = new int[0]; + int[] Max_Chars = new int[0]; + + public PlaneFeed_WF() + : base () + { + HasSettings = true; + CanImport = true; + CanExport = true; + + } + + private class DuplicateKeyComparer + : IComparer where TKey : IComparable + { + public int Compare(TKey x, TKey y) + { + int result = y.CompareTo(x); + if (result == 0) + return -1; // Handle equality as beeing smaller + else + return result; + } + } + + private void ParseToken(JToken token) + { + foreach (var t in token.Children()) + { + try + { + if (t.GetType() == typeof(JProperty)) + { + string number = JProperties.Count.ToString("00000000"); + JProperties.Add(number, (JProperty)t); + } + else if (t.GetType() == typeof(JArray)) + { + // generate a unique key in case of no parent property is found + string number = JArrays.Count.ToString("00000000"); + // try to use parent's property name as a key + if (t.Parent.GetType() == typeof(JProperty)) + { + string pval = ((JProperty)t.Parent).Name; + JArray a; + if (!JArrays.TryGetValue(pval, out a)) + { + JArrays.Add(pval, (JArray)t); + } + else + { + // use number as unique key + JArrays.Add(number, (JArray)t); + } + } + } + } + catch (Exception ex) + { + // do nothing + } + ParseToken(t); + } + } + + private bool Is_Double(string s) + { + bool b; + try + { + // double should contain only following chars + b = !s.ToCharArray().Any(c => !"+-.,E0123456789".Contains(c)); + // double must contain a decimal separator + if (b) + { + b = s.Contains(CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator); + } + } + catch (Exception ex) + { + b = false; + } + return b; + } + + private bool Is_Integer(string s) + { + bool b; + try + { + b = !s.ToCharArray().Any(c => !"0123456789abcdefABCDEF".Contains(c)); + } + catch (Exception ex) + { + b = false; + } + return b; + } + + private void SortMaj(int[] maj) + { + // add all majorities > 0 and sort it by value + // keep the original array index + Maj.Clear(); + for (int i = 0; i < maj.Length; i++) + { + if (maj[i] > 0) + Maj.Add(maj[i], i); + } + } + + private void ClearMajsByIndex(int index) + { + Maj_Hexs[index] = 0; + Maj_Regs[index] = 0; + Maj_Flights[index] = 0; + Maj_Lats[index] = 0; + Maj_Calls[index] = 0; + Maj_Lons[index] = 0; + Maj_Alts[index] = 0; + Maj_Speeds[index] = 0; + Maj_Tracks[index] = 0; + Maj_Squawks[index] = 0; + Maj_Radars[index] = 0; + Maj_Routes[index] = 0; + Maj_Types[index] = 0; + Maj_UTCs[index] = 0; + } + + private void InitializeArrays(int size_i, int size_j) + { + + Values = new string[size_i][]; + for (int i = 0; i < Values.Length; i++) + { + Values[i] = new string[size_j]; + for (int j = 0; j < Values[i].Length; j++) + { + Values[i][j] = ""; + } + } + Names = new string[size_i][]; + for (int i = 0; i < Names.Length; i++) + { + Names[i] = new string[size_j]; + for (int j = 0; j < Names[i].Length; j++) + { + Names[i][j] = ""; + } + } + + Hexs = new string[size_i][]; for (int i = 0; i < Values.Length; i++) { Hexs[i] = new string[size_j]; } + Regs = new string[size_i][]; for (int i = 0; i < Values.Length; i++) { Regs[i] = new string[size_j]; } + Flights = new string[size_i][]; for (int i = 0; i < Values.Length; i++) { Flights[i] = new string[size_j]; } + Calls = new string[size_i][]; for (int i = 0; i < Values.Length; i++) { Calls[i] = new string[size_j]; } + Lats = new double[size_i][]; for (int i = 0; i < Values.Length; i++) { Lats[i] = new double[size_j]; } + Lons = new double[size_i][]; for (int i = 0; i < Values.Length; i++) { Lons[i] = new double[size_j]; } + Alts = new int[size_i][]; for (int i = 0; i < Values.Length; i++) { Alts[i] = new int[size_j]; } + Speeds = new int[size_i][]; for (int i = 0; i < Values.Length; i++) { Speeds[i] = new int[size_j]; } + Tracks = new int[size_i][]; for (int i = 0; i < Values.Length; i++) { Tracks[i] = new int[size_j]; } + Squawks = new int[size_i][]; for (int i = 0; i < Values.Length; i++) { Squawks[i] = new int[size_j]; } + Radars = new string[size_i][]; for (int i = 0; i < Values.Length; i++) { Radars[i] = new string[size_j]; } + Routes = new string[size_i][]; for (int i = 0; i < Values.Length; i++) { Routes[i] = new string[size_j]; } + Types = new string[size_i][]; for (int i = 0; i < Values.Length; i++) { Types[i] = new string[size_j]; } + UTCs = new DateTime[size_i][]; for (int i = 0; i < Values.Length; i++) { UTCs[i] = new DateTime[size_j]; } + + Maj_Hexs = new int[size_j]; + Maj_Regs = new int[size_j]; + Maj_Flights = new int[size_j]; + Maj_Lats = new int[size_j]; + Maj_Calls = new int[size_j]; + Maj_Lons = new int[size_j]; + Maj_Alts = new int[size_j]; + Maj_Speeds = new int[size_j]; + Maj_Tracks = new int[size_j]; + Maj_Squawks = new int[size_j]; + Maj_Radars = new int[size_j]; + Maj_Routes = new int[size_j]; + Maj_Types = new int[size_j]; + Maj_UTCs = new int[size_j]; + + Min_Lats = new double[size_j]; + Min_Lons = new double[size_j]; + Min_Alts = new int[size_j]; + Min_Speeds = new int[size_j]; + Min_Tracks = new int[size_j]; + Max_Lats = new double[size_j]; + Max_Lons = new double[size_j]; + Max_Alts = new int[size_j]; + Max_Speeds = new int[size_j]; + Max_Tracks = new int[size_j]; + Min_Chars = new int[size_j]; + Max_Chars = new int[size_j]; + } + + private void ReadValuesFromArrays() + { + // analyze a random dataset + int i1 = new Random().Next(JArrays.Count - 1); + JArray a = JArrays.Values.ElementAt(i1); + int size_i = JArrays.Count; + int size_j = a.Children().Count() + 1; + // initialize arrays for values + InitializeArrays(size_i, size_j); + // copy strings to array + for (int i = 0; i < size_i; i++) + { + try + { + Values[i][0] = JArrays.Keys.ElementAt(i); + a = JArrays.Values.ElementAt(i); + for (int j = 0; j < a.Count; j++) + { + // check all values and try to find the majorities + string s = a[j].ToString(); + Values[i][j + 1] = s; + } + } + catch + { + } + } + } + + private void ReadValuesFromProperties() + { + Dictionary properties = new Dictionary(); + foreach (JProperty p in JProperties.Values) + { + int count; + if (!properties.TryGetValue(p.Name, out count)) + { + properties.Add(p.Name, 1); + } + else + { + properties[p.Name]++; + } + } + // remove all properties with less count from list + for (int i = properties.Count - 1; i >= 0; i--) + { + if (properties.ElementAt(i).Value < FeedSettings.Min_Planes) + properties.Remove(properties.Keys.ElementAt(i)); + } + // find maximum and set ascending index + int max = 0; + for (int i = 0; i < properties.Keys.Count; i++ ) + { + if (properties.ElementAt(i).Value > max) + max = properties.ElementAt(i).Value; + properties[properties.ElementAt(i).Key] = i; + } + // initialize arrays + InitializeArrays(max, properties.Count); + int row = -1; + string parent = ""; + foreach (JProperty p in JProperties.Values) + { + if (p.Parent.Path != parent) + { + row++; + parent = p.Parent.Path; + } + int j; + if (properties.TryGetValue(p.Name, out j)) + Values[row][j] = p.Value.ToString(); + } + try + { + using (StreamWriter sw = new StreamWriter(TmpDirectory + Path.DirectorySeparatorChar + "AutoJSON_Values.csv")) + { + for (int i = 0; i < Values.Length; i++) + { + for (int j = 0; j < Values[i].Length; j++) + { + sw.Write(Values[i][j] + ";"); + } + sw.WriteLine(); + } + } + } + catch (Exception ex) + { + Console.WriteLine("[AutoJSON] Error while writing file: " + ex.Message); + } + } + + private bool IsIndexed() + { + // check for basic indices available + if (FeedSettings.Index_UTC < 0) + return false; + if (FeedSettings.Index_Hex < 0) + return false; + if (FeedSettings.Index_Call < 0) + return false; + if (FeedSettings.Index_Lat < 0) + return false; + if (FeedSettings.Index_Lon < 0) + return false; + if (FeedSettings.Index_Alt < 0) + return false; + if (FeedSettings.Index_Track < 0) + return false; + if (FeedSettings.Index_Speed < 0) + return false; + return true; + } + + private void AutoIndexArrays() + { + PlaneInfoConverter pic = new PlaneInfoConverter(); + + for (int i = 0; i < Values.Length; i++) + { + for (int j = 0; j < Values[i].Length; j++) + { + try + { + // check all values and try to find the majorities + string s = Values[i][j]; + if (String.IsNullOrEmpty(s)) + continue; + // store Min/Max string lenghts + if (s.Length < Min_Chars[j]) + Min_Chars[j] = s.Length; + if (s.Length > Max_Chars[j]) + Max_Chars[j] = s.Length; + // check for double values first + if (Is_Double(s)) + { + double lat = pic.To_Lat(s); + Lats[i][j] = lat; + if (lat != double.MinValue) + { + Maj_Lats[j]++; + if (lat < Min_Lats[j]) + Min_Lats[j] = lat; + if (lat > Max_Lats[j]) + Max_Lats[j] = lat; + } + double lon = pic.To_Lon(s); + Lons[i][j] = lon; + if (lon != double.MinValue) + { + Maj_Lons[j]++; + if (lon < Min_Lons[j]) + Min_Lons[j] = lon; + if (lon > Max_Lons[j]) + Max_Lons[j] = lon; + } + continue; + } + // check for integer + if (Is_Integer(s)) + { + DateTime utc = pic.To_UTC(s); + UTCs[i][j] = utc; + if (utc != DateTime.MinValue) + { + Maj_UTCs[j]++; + } + string hex = pic.To_Hex(s); + Hexs[i][j] = hex; + if (hex != null) + { + Maj_Hexs[j]++; + } + int alt = pic.To_Alt(s); + Alts[i][j] = alt; + if (alt != int.MinValue) + { + Maj_Alts[j]++; + if (alt < Min_Alts[j]) + Min_Alts[j] = alt; + if (alt > Max_Alts[j]) + Max_Alts[j] = alt; + } + int speed = pic.To_Speed(s); + Speeds[i][j] = speed; + if (speed != int.MinValue) + { + Maj_Speeds[j]++; + if (speed < Min_Speeds[j]) + Min_Speeds[j] = speed; + if (speed > Max_Speeds[j]) + Max_Speeds[j] = speed; + } + int track = pic.To_Track(s); + Tracks[i][j] = track; + if (track != int.MinValue) + { + Maj_Tracks[j]++; + if (track < Min_Tracks[j]) + Min_Tracks[j] = track; + if (track > Max_Tracks[j]) + Max_Tracks[j] = track; + } + int squawk = pic.To_Squawk(s); + Squawks[i][j] = squawk; + if (squawk != int.MinValue) + { + Maj_Squawks[j]++; + } + continue; + } + // the rest is string + string reg = pic.To_Reg(s); + Regs[i][j] = reg; + if (reg != null) + { + Maj_Regs[j]++; + } + string flight = pic.To_Flight(s); + Flights[i][j] = flight; + if (flight != null) + { + Maj_Flights[j]++; + } + string radar = pic.To_Radar(s); + Radars[i][j] = radar; + if (radar != null) + { + Maj_Radars[j]++; + } + string route = pic.To_Route(s); + Routes[i][j] = route; + if (route != null) + { + Maj_Routes[j]++; + } + string call = pic.To_Call(s); + Calls[i][j] = call; + if (call != null) + { + Maj_Calls[j]++; + } + string type = pic.To_Type(s); + Types[i][j] = type; + if (type != null) + { + Maj_Types[j]++; + } + } + catch (Exception ex) + { + Console.WriteLine("Values[" + i.ToString() + "][" + j.ToString() + "]=" + Values[i][j] + ":" + ex.Message); + } + } + } + // do the forensic work now + if (FeedSettings.Index_UTC < 0) + { + SortMaj(Maj_UTCs); + foreach (KeyValuePair maj in Maj) + { + // UTC must have >=10 chars + if (Max_Chars[maj.Value] >= 10) + { + FeedSettings.Index_UTC = maj.Value; + ClearMajsByIndex(maj.Value); + break; + } + } + } + if (FeedSettings.Index_Hex < 0) + { + SortMaj(Maj_Hexs); + foreach (KeyValuePair maj in Maj) + { + // Hex must have 6 chars + if (Max_Chars[maj.Value] == 6) + { + FeedSettings.Index_Hex = maj.Value; + ClearMajsByIndex(maj.Value); + break; + } + } + } + if (FeedSettings.Index_Squawk < 0) + { + SortMaj(Maj_Squawks); + foreach (KeyValuePair maj in Maj) + { + // Squawk must have 4 chars + if (Max_Chars[maj.Value] == 4) + { + FeedSettings.Index_Squawk = maj.Value; + ClearMajsByIndex(maj.Value); + break; + } + } + } + if (FeedSettings.Index_Alt < 0) + { + SortMaj(Maj_Alts); + foreach (KeyValuePair maj in Maj) + { + // Alt will have > 3 chars and max. values > 10000 feet + if ((Max_Chars[maj.Value] > 3) && (Max_Alts[maj.Value] > 10000)) + { + FeedSettings.Index_Alt = maj.Value; + ClearMajsByIndex(maj.Value); + break; + } + } + } + if (FeedSettings.Index_Track < 0) + { + SortMaj(Maj_Tracks); + foreach (KeyValuePair maj in Maj) + { + // Track will have <= 3 chars and hopefully at last on plane between 180 and 360 + if ((Max_Chars[maj.Value] <= 3) && (Max_Tracks[maj.Value] > 180) && (Max_Tracks[maj.Value] < 360)) + { + FeedSettings.Index_Track = maj.Value; + ClearMajsByIndex(maj.Value); + break; + } + } + } + if (FeedSettings.Index_Speed < 0) + { + SortMaj(Maj_Speeds); + foreach (KeyValuePair maj in Maj) + { + // Speed will have <= 3 chars and max > 100 and < 800 + if ((Max_Chars[maj.Value] <= 3) && (Max_Speeds[maj.Value] > 100) && (Max_Speeds[maj.Value] <= 800)) + { + FeedSettings.Index_Speed = maj.Value; + ClearMajsByIndex(maj.Value); + break; + } + } + } + if (FeedSettings.Index_Lat < 0) + { + SortMaj(Maj_Lats); + foreach (KeyValuePair maj in Maj) + { + // Lat will have max >= -90 and <= 90 + if ((Min_Lats[maj.Value] >= -90) && (Max_Tracks[maj.Value] <= 90)) + { + FeedSettings.Index_Lat = maj.Value; + ClearMajsByIndex(maj.Value); + break; + } + } + } + if (FeedSettings.Index_Lon < 0) + { + SortMaj(Maj_Lons); + foreach (KeyValuePair maj in Maj) + { + // Lon will have max >= -180 and <= 180 + if ((Min_Lons[maj.Value] >= -180) && (Max_Tracks[maj.Value] <= 180)) + { + FeedSettings.Index_Lon = maj.Value; + ClearMajsByIndex(maj.Value); + break; + } + } + } + if (FeedSettings.Index_Type < 0) + { + SortMaj(Maj_Types); + foreach (KeyValuePair maj in Maj) + { + // type will have >= 2 chars + if (Max_Chars[maj.Value] >= 2) + { + FeedSettings.Index_Type = maj.Value; + ClearMajsByIndex(maj.Value); + break; + } + } + } + if (FeedSettings.Index_Flight < 0) + { + SortMaj(Maj_Flights); + foreach (KeyValuePair maj in Maj) + { + FeedSettings.Index_Flight = maj.Value; + ClearMajsByIndex(maj.Value); + break; + } + } + if (FeedSettings.Index_Reg < 0) + { + SortMaj(Maj_Regs); + foreach (KeyValuePair maj in Maj) + { + // Reg will have > 3 chars + if (Max_Chars[maj.Value] > 3) + { + FeedSettings.Index_Reg = maj.Value; + ClearMajsByIndex(maj.Value); + break; + } + } + } + if (FeedSettings.Index_Route < 0) + { + SortMaj(Maj_Routes); + foreach (KeyValuePair maj in Maj) + { + FeedSettings.Index_Route = maj.Value; + ClearMajsByIndex(maj.Value); + break; + } + } + if (FeedSettings.Index_Radar < 0) + { + SortMaj(Maj_Radars); + foreach (KeyValuePair maj in Maj) + { + // radar will have > 2 chars + if (Max_Chars[maj.Value] > 2) + { + FeedSettings.Index_Radar = maj.Value; + ClearMajsByIndex(maj.Value); + break; + } + } + } + if (FeedSettings.Index_Call < 0) + { + SortMaj(Maj_Calls); + foreach (KeyValuePair maj in Maj) + { + // reg will have > 3 chars + if (Max_Chars[maj.Value] > 3) + { + FeedSettings.Index_Call = maj.Value; + ClearMajsByIndex(maj.Value); + break; + } + } + } + } + + private List ReadPlanesFromArray() + { + List l = new List(); + PlaneInfoConverter pic = new PlaneInfoConverter(); + for (int i = 0; i < Values.Length; i++) + { + PlaneInfo info = new PlaneInfo(); + + if (FeedSettings.Index_UTC >= 0) + { + DateTime utc = pic.To_UTC(Values[i][FeedSettings.Index_UTC]); + if (utc != DateTime.MinValue) + info.Time = utc; + } + if (FeedSettings.Index_Hex >= 0) + { + string hex = pic.To_Hex(Values[i][FeedSettings.Index_Hex]); + if (!String.IsNullOrEmpty(hex)) + info.Hex = hex; + } + if (FeedSettings.Index_Squawk >= 0) + { + int squawk = pic.To_Squawk(Values[i][FeedSettings.Index_Squawk]); +// if (squawk != int.MinValue) +// info.Squawk = squawk; + } + if (FeedSettings.Index_Alt >= 0) + { + int alt = pic.To_Alt(Values[i][FeedSettings.Index_Alt]); + if (alt != int.MinValue) + { + info.Alt = alt; + } + } + if (FeedSettings.Index_Track >= 0) + { + int track = pic.To_Track(Values[i][FeedSettings.Index_Track]); + if (track != int.MinValue) + info.Track = track; + } + if (FeedSettings.Index_Speed >= 0) + { + int speed = pic.To_Speed(Values[i][FeedSettings.Index_Speed]); + if (speed != int.MinValue) + info.Speed = speed; + } + if (FeedSettings.Index_Lat >= 0) + { + double lat = pic.To_Lat(Values[i][FeedSettings.Index_Lat]); + if (lat != double.MinValue) + info.Lat = lat; + } + if (FeedSettings.Index_Lon >= 0) + { + double lon = pic.To_Lon(Values[i][FeedSettings.Index_Lon]); + if (lon != double.MinValue) + info.Lon = lon; + } + if (FeedSettings.Index_Type >= 0) + { + string type = Values[i][FeedSettings.Index_Type]; + if (!String.IsNullOrEmpty(type)) + info.Type = type; + } + if (FeedSettings.Index_Reg >= 0) + { + string reg = Values[i][FeedSettings.Index_Reg]; + if (!String.IsNullOrEmpty(reg)) + info.Reg = reg; + } + if (FeedSettings.Index_Call >= 0) + { + string call = Values[i][FeedSettings.Index_Call]; + if (!String.IsNullOrEmpty(call)) + info.Call = call; + } + if (FeedSettings.Index_Radar >= 0) + { + string radar = Values[i][FeedSettings.Index_Radar]; +// if (!String.IsNullOrEmpty(radar)) +// info.Radar = radar; + } + // complete info + if (String.IsNullOrEmpty(info.Hex) && !String.IsNullOrEmpty(info.Reg)) + { + AircraftDesignator ad = AircraftData.Database.AircraftFindByReg(info.Reg); + if (ad != null) + info.Hex = ad.Hex; + } + // update aircraft database + if (!String.IsNullOrEmpty(info.Hex)) + AircraftData.Database.AircraftInsertOrUpdateIfNewer(new AircraftDesignator(info.Hex,info.Call, info.Reg, info.Type)); + // fill aircraft position table + if (!String.IsNullOrEmpty(info.Hex) && + !String.IsNullOrEmpty(info.Call) && + (info.Lat >= MinLat) && + (info.Lat <= MaxLat) && + (info.Lon >= MinLon) && + (info.Lon <= MaxLon) && + (info.Alt_m >= MinAlt) && + (info.Alt_m <= MaxAlt)) + { + l.Add(new AircraftPositionDesignator(info.Hex, info.Call, info.Lat, info.Lon, info.Alt, info.Track, info.Speed, info.Time)); + } + } + return l; + } + + private void ReadFromURL(string url, string filename) + { + int count = 0; + string json = ""; + DateTime start = DateTime.UtcNow; + try + { + AutoDecompressionWebClient cl = new AutoDecompressionWebClient(); + if (!cl.DownloadFile(url, filename, false, true)) + return; + Log.WriteMessage("Reading file from url successful. Try to deserialize JSON..."); + // check if file exists + if (!File.Exists(filename)) + return; + // deserialize JSON file + using (StreamReader sr = new StreamReader(File.OpenRead(filename))) + { + json = sr.ReadToEnd(); + } + JObject o = (JObject)JsonConvert.DeserializeObject(json); + // clear collections + JArrays.Clear(); + JProperties.Clear(); + // parse all child tokens recursively --> can be either a property or an array + ParseToken(o); + // we've got all properties and arrays here + // assuming that plane info is stored in a structure with >10 elements + // check the child token count + try + { + var propertychildcounts = JProperties.Values.Select(x => x.Count) + .GroupBy(x => x) + .Select(x => new { ChildCount = x.Key, Count = x.Count() }) + .OrderByDescending(x => x) + .ToList(); + // single properties? + if (propertychildcounts.Count > 0) + { + ReadValuesFromProperties(); + } + } + catch (Exception ex) + { + } + try + { + var arraychildcounts = JArrays.Values.Select(x => x.Count) + .GroupBy(x => x) + .Select(x => new { ChildCount = x.Key, Count = x.Count() }) + .OrderByDescending(x => x) + .ToList(); + // checking number of elements to determine the organisation of values + // arrays of values? + if ((arraychildcounts != null) && (arraychildcounts.Count > 0) && (arraychildcounts[0].ChildCount > FeedSettings.Min_Elements) && (arraychildcounts[0].Count > FeedSettings.Min_Planes)) + { + // assuming that we have data organized in arrays + // find the majority of element counts + // remove all arrays with different element count from list + for (int i = JArrays.Count - 1; i >= 0; i--) + { + if (JArrays.Values.ElementAt(i).Count != arraychildcounts[0].ChildCount) + JArrays.Remove(JArrays.Keys.ElementAt(i)); + } + // convert all properties into the values array of strings + ReadValuesFromArrays(); + } + } + catch (Exception ex) + { + } + // auto-index if necessary + if (FeedSettings.AutoIndex && !IsIndexed()) + AutoIndexArrays(); + // read data + if (IsIndexed()) + { + if (FeedSettings.SaveToFile) + { + try + { + using (StreamWriter sw = new StreamWriter(Path.Combine(TmpDirectory, "AutoJSON_Values.csv"))) + { + for (int i = 0; i < Values.Length; i++) + { + // write header + if (i == 0) + { + string[] a = new string[Values[i].Length]; + for (int j = 0; j < a.Length; j++) + a[j] = "[unknown]"; + if (FeedSettings.Index_UTC >= 0) + { + a[FeedSettings.Index_UTC] = "UTC"; + } + if (FeedSettings.Index_Hex >= 0) + { + a[FeedSettings.Index_Hex] = "Hex"; + } + if (FeedSettings.Index_Squawk >= 0) + { + a[FeedSettings.Index_Squawk] = "Squawk"; + } + if (FeedSettings.Index_Alt >= 0) + { + a[FeedSettings.Index_Alt] = "Alt"; + } + if (FeedSettings.Index_Track >= 0) + { + a[FeedSettings.Index_Track] = "Track"; + } + if (FeedSettings.Index_Speed >= 0) + { + a[FeedSettings.Index_Speed] = "Speed"; + } + if (FeedSettings.Index_Lat >= 0) + { + a[FeedSettings.Index_Lat] = "Lat"; + } + if (FeedSettings.Index_Lon >= 0) + { + a[FeedSettings.Index_Lon] = "Lon"; + } + if (FeedSettings.Index_Type >= 0) + { + a[FeedSettings.Index_Type] = "Type"; + } + if (FeedSettings.Index_Reg >= 0) + { + a[FeedSettings.Index_Reg] = "Reg"; + } + if (FeedSettings.Index_Call >= 0) + { + a[FeedSettings.Index_Call] = "Call"; + } + if (FeedSettings.Index_Radar >= 0) + { + a[FeedSettings.Index_Radar] = "Radar"; + } + for (int j = 0; j < Values[i].Length; j++) + { + if (j > 0) + sw.Write(";"); + sw.Write(a[j]); + } + sw.WriteLine(); + + } + // write values + for (int j = 0; j < Values[i].Length; j++) + { + sw.Write(Values[i][j] + ";"); + } + sw.WriteLine(); + } + } + } + catch (Exception ex) + { + Log.WriteMessage("[AutoJSON] Error while writing file: " + ex.Message); + } + } + List l = ReadPlanesFromArray(); + if ((l != null) && (l.Count > 0)) + { + + // ReportProgress((int)PROGRESS.PLANES, planes); + // AircraftData.Database.AircraftPositionBulkInsertOrUpdateIfNewer(l); + } + count = l.Count; + } + } + catch (Exception ex) + { + // report error + ReportProgress(-1, ex.Message); + Log.WriteMessage(ex.Message); + } + DateTime stop = DateTime.UtcNow; + string msg = "[" + start.ToString("HH:mm:ss") + "] " + + count.ToString() + " Positions updated, " + + (stop - start).Milliseconds.ToString() + " ms."; + this.ReportProgress((int)PROGRESS.STATUS, msg); + Log.WriteMessage(msg); + } + + protected override void OnDoWork(DoWorkEventArgs e) + { + Log.WriteMessage("Started."); + PlaneFeedWorkEventArgs args = (PlaneFeedWorkEventArgs)e.Argument; + // set parameters from arguments + AppDirectory = args.AppDirectory; + AppDataDirectory = args.AppDataDirectory; + LogDirectory = args.LogDirectory; + TmpDirectory = args.TmpDirectory; + DatabaseDirectory = args.DatabaseDirectory; + MaxLat = args.MaxLat; + MinLon = args.MinLon; + MinLat = args.MinLat; + MaxLon = args.MaxLon; + MyLat = args.MyLat; + MyLon = args.MyLon; + DXLat = args.DXLat; + DXLon = args.DXLon; + MinAlt = args.MinAlt; + MaxAlt = args.MaxAlt; + + // intialize variables + VC.AddVar("APPDIR", AppDirectory); + VC.AddVar("DATADIR", AppDataDirectory); + VC.AddVar("LOGDIR", LogDirectory); + VC.AddVar("DATABASEDIR", DatabaseDirectory); + VC.AddVar("MINLAT", MinLat); + VC.AddVar("MAXLAT", MaxLat); + VC.AddVar("MINLON", MinLon); + VC.AddVar("MAXLON", MaxLon); + VC.AddVar("MINALTM", MinAlt); + VC.AddVar("MAXALTM", MaxAlt); + VC.AddVar("MINALTFT", (int)UnitConverter.m_ft((double)MinAlt)); + VC.AddVar("MAXALTFT", (int)UnitConverter.m_ft((double)MaxAlt)); + + + // check boundaries + if ((MaxLat <= MinLat) || (MaxLon <= MinLon)) + { + Status = STATUS.ERROR; + this.ReportProgress((int)PROGRESS.ERROR, "Area boundaries mismatch. Check your Covered Area parameters!"); + Log.WriteMessage("Area boundaries mismatch. Check your Covered Area parameters!"); + } + else + { + Status = STATUS.OK; + int interval = Properties.Settings.Default.WF_Interval; + // run loop + do + { + // calculate url and get json + String url = VC.ReplaceAllVars(FeedSettings.URL); + ReadFromURL(url, Path.Combine(TmpDirectory, "AutoJSON.json")); + // sleep interval + // don't use Thread.Sleep() here as this is not interruptable + int i = 0; + while (!CancellationPending && (i < interval)) + { + Thread.Sleep(1000); + i++; + } + } + while (!this.CancellationPending || (Status != STATUS.OK)); + } + this.ReportProgress((int)PROGRESS.FINISHED); + Log.WriteMessage("Finished."); + } + + public override Object GetFeedSettings() + { + return FeedSettings; + } + + public override void Import() + { + OpenFileDialog Dlg = new OpenFileDialog(); + Dlg.FileName = "*.feed"; + Dlg.DefaultExt = "feed"; + Dlg.Filter = "Plane Feeds | .feed"; + Dlg.CheckFileExists = true; + if (Dlg.ShowDialog() == DialogResult.OK) + { + XmlSerializer s = new XmlSerializer(typeof(PlanefeedSettings_WF)); + FeedSettings = (PlanefeedSettings_WF)s.Deserialize(File.OpenRead(Dlg.FileName)); + } + } + + public override void Export() + { + SaveFileDialog Dlg = new SaveFileDialog(); + Dlg.DefaultExt = "feed"; + Dlg.Filter = "Plane Feeds | .feed"; + Dlg.OverwritePrompt = true; + if (Dlg.ShowDialog() == DialogResult.OK) + { + XmlSerializer s = new XmlSerializer(typeof(PlanefeedSettings_WF)); + s.Serialize(File.Create(Dlg.FileName), FeedSettings); + } + + } + } +} diff --git a/AirScout.PlaneFeeds/app.config b/AirScout.PlaneFeeds/app.config new file mode 100644 index 0000000..10bef44 --- /dev/null +++ b/AirScout.PlaneFeeds/app.config @@ -0,0 +1,420 @@ + + + + +
+
+ + + + + + http://droidapp.pinkfroot.com/APPAPIDROID/v7/planeUpdateFAA.php?routetype=IATA&FAA=1&bounds=%MAXLAT%,%MINLAT%,%MINLON%,%MAXLON% + + + 60 + + + Generic Info. + + + Web feed from www.planefinder.net + + + [Generic] Dummy Plane Feed + + + [WebFeed] www.planefinder.net + + + This plane feed is being fetched from an Internet server via Deep Link +technology (see http://en.wikipedia.org/wiki/Deep_link). + +The use is not intended by the website owners and could be changed in URL and data format frequently and without further notice. +Furthermore, it might cause legal issues in some countries. + +By clicking on "Accept" you understand that you are + + DOING THAT ON YOUR OWN RISK + +The auhor of this software will not be responsible in any case. + + + + + + + + + This plane feed is a dummy plane feed generating random plane positions. + +USE THIS FEED FOR DEMONSTRATION PURPOSE ONLY! + +The feed will run without fetching any data from the Internet. +Please do not mix it with other feeds as confusion may occur with live data. +The call signs generated will look like "RND0001", "RND0002" etc. + + + http://localhost:9880/planes.json + + + 60 + + + [WebFeed] AirScout Web Server + + + + + + + + + Web feed from another AirScout server. + + + + + + + + + 60 + + + Reads plane positions out of a JSON file. + + + [FileFeed] JSON file + + + planes.json + + + [RawData] ADSBSharp data + + + Raw data feed from simple ADS-B receivers (DVB-T dongles). +Use this feed thogether with ADSBSharp. + + + + + + + + + + + + localhost + + + 47806 + + + 60 + + + False + + + True + + + True + + + [RawData] RTL1090 data + + + Raw data feed from simple ADS-B receivers (DVB-T dongles). +Use this feed together with RTL1090.exe. + +Feed software must output raw data either binary or ASCII format via TCP. + + + + + + + + + + + + localhost + + + 31001 + + + 60 + + + True + + + True + + + True + + + This plane feed is being fetched from an Internet server via Deep Link +technology (see http://en.wikipedia.org/wiki/Deep_link). + +The use is not intended by the website owners and could be changed in URL and data format frequently and without further notice. +Furthermore, it might cause legal issues in some countries. + +By clicking on "Accept" you understand that you are + + DOING THAT ON YOUR OWN RISK + +The auhor of this software will not be responsible in any case. + + + + + + JSON file from web feed with auto detection of content + + + 60 + + + [WebFeed] Automatic JSON file detection + + + + + + -1 + + + -1 + + + -1 + + + -1 + + + -1 + + + -1 + + + -1 + + + -1 + + + -1 + + + -1 + + + 10 + + + 50 + + + -1 + + + True + + + -1 + + + -1 + + + -1 + + + This plane feed is being fetched from Flightradar24.com website +after checking your valid premium accout. +Even though AirScout is checking your account status online: +This service is not authorized by Fligthradar24 and is not guaranteed +to work under all circumstances.Paying for a FR24 premium account +does not automatically grants the right to access the data via AirScout. + +By clicking on "Accept" you understand that you are + + DOING THAT ON YOUR OWN RISK + +The auhor of this software will not be responsible in any case. + + + + + + Web feed from www.flightradar24.com. You need a premim account to use this service. + + + 60 + + + [WebFeed] www.flightradar24.com + + + + + + + + + Web feed from Virtual Radar Server + + + [WebFeed] Virtual Radar Server + + + + + + + + + 60 + + + http://public-api.adsbexchange.com/VirtualRadar/AircraftList.json?fNBnd=%MAXLAT%&fSBnd=%MINLAT%&fWBnd=%MINLON%&fEBnd=%MAXLON%&fAltL=%MINALTFT%&fAltU=%MAXALTFT% + + + False + + + False + + + False + + + False + + + + + + + + + Universal Web Feed + + + 60 + + + [WebFeed] Universal Web Feed + + + False + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + False + + + True + + + 10 + + + False + + + + + http://planefinder.net/endpoints/update.php + + + 60 + + + Generic Info. + + + Web feed from www.planefinder.net + + + [Generic] PlaneFeed + + + [WebFeed] Planefinder + + + This plane feed is being fetched from an Internet server via Deep Link +technology (see http://en.wikipedia.org/wiki/Deep_link). + +The use is not intended by the website owners and could be changed in URL and data format frequently and without further notice. +Furthermore, it might cause legal issues in some countries. + +By clicking on "Accept" you understand that you are + + DOING THAT ON YOUR OWN RISK + +The auhor of this software will not be responsible in any case. + + + + \ No newline at end of file diff --git a/AirScout.PlaneFeeds/packages.config b/AirScout.PlaneFeeds/packages.config new file mode 100644 index 0000000..0aee17e --- /dev/null +++ b/AirScout.PlaneFeeds/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/AirScout.Signals/AirScout.Signals.csproj b/AirScout.Signals/AirScout.Signals.csproj new file mode 100644 index 0000000..d46a263 --- /dev/null +++ b/AirScout.Signals/AirScout.Signals.csproj @@ -0,0 +1,95 @@ + + + + + Debug + AnyCPU + {3D34943B-CACA-426D-9FC0-661531668E87} + Library + Properties + AirScout.Signals + AirScout.Signals + v4.0 + 512 + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Newtonsoft.Json.12.0.1\lib\net40\Newtonsoft.Json.dll + True + + + + + ..\packages\System.Data.SQLite.Core.1.0.110.0\lib\net40\System.Data.SQLite.dll + True + + + + + + + + + + + + + + True + True + Settings.settings + + + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + {ee86e933-d883-4b18-80eb-0fba55ec67c6} + ScoutBase.Core + + + {6056d3be-7002-4a6a-a9ea-6ff45122a3c7} + SQLiteDatabase + + + + + + + Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}". + + + + + \ No newline at end of file diff --git a/AirScout.Signals/Properties/AssemblyInfo.cs b/AirScout.Signals/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..ab994e3 --- /dev/null +++ b/AirScout.Signals/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Allgemeine Informationen über eine Assembly werden über die folgenden +// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, +// die einer Assembly zugeordnet sind. +[assembly: AssemblyTitle("AirScout.Signals")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("DL2ALF")] +[assembly: AssemblyProduct("AirScout")] +[assembly: AssemblyCopyright("Copyright © 2019 DL2ALF")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar +// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von +// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen. +[assembly: ComVisible(false)] + +// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird +[assembly: Guid("3d34943b-caca-426d-9fc0-661531668e87")] + +// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: +// +// Hauptversion +// Nebenversion +// Buildnummer +// Revision +// +// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern +// übernehmen, indem Sie "*" eingeben: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.3.0.0")] +[assembly: AssemblyFileVersion("1.3.0.0")] diff --git a/AirScout.Signals/Properties/Settings.Designer.cs b/AirScout.Signals/Properties/Settings.Designer.cs new file mode 100644 index 0000000..bd2de93 --- /dev/null +++ b/AirScout.Signals/Properties/Settings.Designer.cs @@ -0,0 +1,50 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.42000 +// +// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn +// der Code erneut generiert wird. +// +//------------------------------------------------------------------------------ + +namespace AirScout.Signals.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string Database_Directory { + get { + return ((string)(this["Database_Directory"])); + } + set { + this["Database_Directory"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Database_InMemory { + get { + return ((bool)(this["Database_InMemory"])); + } + set { + this["Database_InMemory"] = value; + } + } + } +} diff --git a/AirScout.Signals/Properties/Settings.settings b/AirScout.Signals/Properties/Settings.settings new file mode 100644 index 0000000..c74cab7 --- /dev/null +++ b/AirScout.Signals/Properties/Settings.settings @@ -0,0 +1,12 @@ + + + + + + + + + False + + + \ No newline at end of file diff --git a/AirScout.Signals/SignalDatabase.cs b/AirScout.Signals/SignalDatabase.cs new file mode 100644 index 0000000..9bd8282 --- /dev/null +++ b/AirScout.Signals/SignalDatabase.cs @@ -0,0 +1,550 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.IO; +using System.Globalization; +using System.Reflection; +using System.Data; +using System.Diagnostics; +using ScoutBase.Core; +using System.Data.SQLite; +using Newtonsoft.Json; +using System.Windows.Forms; +using System.ComponentModel; + +namespace AirScout.Signals +{ + + public class SignalData + { + static SignalDatabase signals = new SignalDatabase(); + public static SignalDatabase Database + { + get + { + return signals; + } + } + + } + + /// + /// Holds the signal information in a database structure. + /// + public class SignalDatabase + { + System.Data.SQLite.SQLiteDatabase db; + + private string DBPath; + + private static LogWriter Log = LogWriter.Instance; + + public readonly int UserVersion = 1; + + public SignalDatabase() + { + db = OpenDatabase("signals.db3"); + // set auto vacuum mode to "Full" to allow database to reduce size on disk + // requires a vacuum command to change database layout + AUTOVACUUMMODE mode = db.GetAutoVacuum(); + if (mode != AUTOVACUUMMODE.FULL) + { + if (MessageBox.Show("A major database layout change is necessary to run this version of AirScout. Older versions of AirScout are not compatible anymore and will cause errors. \n\nPress >OK< to start upgrade now (this will take some minutes). \nPress >Cancel< to leave.", "Database Upgrade of " + Path.GetFileName(db.DBLocation), MessageBoxButtons.OKCancel) == DialogResult.Cancel) + Environment.Exit(-1); // exit immediately + db.SetAutoVacuum(AUTOVACUUMMODE.FULL); + } + // create tables with schemas if not exist + if (!SignalLevelTableExists()) + SignalLevelCreateTable(); + } + + ~SignalDatabase() + { + CloseDatabase(db); + } + + private System.Data.SQLite.SQLiteDatabase OpenDatabase(string name) + { + System.Data.SQLite.SQLiteDatabase db = null; + try + { + // check if database path exists --> create if not + if (!Directory.Exists(DefaultDatabaseDirectory())) + Directory.CreateDirectory(DefaultDatabaseDirectory()); + // check if database is already on disk + DBPath = DefaultDatabaseDirectory(); + if (!File.Exists(Path.Combine(DBPath, name))) + { + // create one on disk + System.Data.SQLite.SQLiteDatabase dbn = new System.Data.SQLite.SQLiteDatabase(Path.Combine(DBPath, name)); + // open database + dbn.Open(); + // set user version + dbn.SetUserVerion(UserVersion); + // set auto vacuum mode to full + dbn.SetAutoVacuum(AUTOVACUUMMODE.FULL); + dbn.Close(); + } + // check for in-memory database --> open from disk, if not + if (Properties.Settings.Default.Database_InMemory) + db = System.Data.SQLite.SQLiteDatabase.CreateInMemoryDB(Path.Combine(DBPath, name)); + else + { + db = new System.Data.SQLite.SQLiteDatabase(Path.Combine(DBPath, name)); + db.Open(); + } + // get version info + int v = db.GetUserVersion(); + // do upgrade stuff here + } + catch (Exception ex) + { + Console.WriteLine("Error initilalizing database: " + ex.Message); + throw new TypeInitializationException(this.GetType().Name, ex); + } + return db; + } + + private void CloseDatabase(System.Data.SQLite.SQLiteDatabase db) + { + if (db == null) + return; + // save in-memory database to disk + if (db.IsInMemory) + db.BackupDatabase(db.DBLocation); + // else + // db.Close(); + } + + public void BackupDatabase() + { + if (db == null) + return; + // save in-memory database to disk + if (db.IsInMemory) + db.BackupDatabase(db.DiskFileName); + else + db.Close(); + } + + public bool IsInMemory() + { + return db.IsInMemory; + } + + public string DefaultDatabaseDirectory() + { + // create default database directory name + string dir = Properties.Settings.Default.Database_Directory; + if (!String.IsNullOrEmpty(dir)) + { + return dir; + } + // empty settings -_> create standard path + // collect entry assembly info + Assembly ass = Assembly.GetExecutingAssembly(); + string company = ""; + string product = ""; + object[] attribs; + attribs = ass.GetCustomAttributes(typeof(AssemblyCompanyAttribute), true); + if (attribs.Length > 0) + { + company = ((AssemblyCompanyAttribute)attribs[0]).Company; + } + attribs = ass.GetCustomAttributes(typeof(AssemblyProductAttribute), true); + if (attribs.Length > 0) + { + product = ((AssemblyProductAttribute)attribs[0]).Product; + } + // create database path + dir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + if (!String.IsNullOrEmpty(company)) + dir = Path.Combine(dir, company); + if (!String.IsNullOrEmpty(product)) + dir = Path.Combine(dir, product); + return Path.Combine(dir, "AircraftData"); + } + + public DATABASESTATUS GetDBStatus() + { + if (db != null) + return db.Status; + return DATABASESTATUS.UNDEFINED; + } + + public void SetDBStatus(DATABASESTATUS status) + { + if (db != null) + db.Status = status; + } + + public bool GetDBStatusBit(DATABASESTATUS statusbit) + { + if (db != null) + return (((int)db.Status) & ((int)statusbit)) > 0; + return false; + } + + public void SetDBStatusBit(DATABASESTATUS statusbit) + { + if (db != null) + db.Status |= statusbit; + } + + public void ResetDBStatusBit(DATABASESTATUS statusbit) + { + if (db != null) + db.Status &= ~statusbit; + } + + public void BeginTransaction() + { + if (db == null) + return; + db.BeginTransaction(); + } + + public void Commit() + { + if (db == null) + return; + db.Commit(); + } + + private DataTable Select(System.Data.SQLite.SQLiteDatabase db, string sql) + { + return db.Select(sql); + } + + public string GetDBLocation() + { + if (db == null) + return ""; + return db.DBLocation; + } + + public double GetDBSize() + { + if (db == null) + return 0; + return db.DBSize; + } + + private bool IsValid(object obj) + { + if (obj == null) + return false; + if (obj.GetType() == typeof(DBNull)) + return false; + return true; + } + + #region SignalLevel + + public bool SignalLevelTableExists(string tablename = "") + { + // check for table name is null or empty --> use default tablename from type instead + string tn = tablename; + if (String.IsNullOrEmpty(tn)) + tn = SignalLevelDesignator.TableName; + return db.TableExists(tn); + } + + public void SignalLevelCreateTable(string tablename = "") + { + lock (db.DBCommand) + { + // check for table name is null or empty --> use default tablename from type instead + string tn = tablename; + if (String.IsNullOrEmpty(tn)) + tn = SignalLevelDesignator.TableName; + db.DBCommand.CommandText = "CREATE TABLE `" + tn + "`(Level DOUBLE, LastUpdated INT32, PRIMARY KEY (LastUpdated))"; + db.DBCommand.Parameters.Clear(); + db.Execute(db.DBCommand); + } + } + + public long SignalLevelCount() + { + object count = db.ExecuteScalar("SELECT COUNT(*) FROM " + SignalLevelDesignator.TableName); + if (IsValid(count)) + return (long)count; + return 0; + } + + public bool SignalLevelExists(DateTime lastupdated) + { + SignalLevelDesignator sd = new SignalLevelDesignator(lastupdated); + return SignalLevelExists(sd); + } + + public bool SignalLevelExists(SignalLevelDesignator sd) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT EXISTS (SELECT LastUpdated FROM " + SignalLevelDesignator.TableName + " WHERE LastUpdated = @LastUpdated"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(sd.AsUNIXTime("LastUpdated")); + object result = db.DBCommand.ExecuteScalar(); + if (IsValid(result) && ((long)result > 0)) + return true; + } + return false; + } + + public SignalLevelDesignator SignalLevelFind(DateTime lastupdated) + { + SignalLevelDesignator sd = new SignalLevelDesignator(lastupdated); + return SignalLevelFind(sd); + } + + public SignalLevelDesignator SignalLevelFind(SignalLevelDesignator sd) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT * FROM " + SignalLevelDesignator.TableName + " WHERE LastUpdated = @LastUpdated"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(sd.AsUNIXTime("LastUpdated")); + try + { + DataTable Result = db.Select(db.DBCommand); + if ((Result != null) && (Result.Rows.Count > 0)) + { + return new SignalLevelDesignator(Result.Rows[0]); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + return null; + } + + public SignalLevelDesignator SignalLevelFindAt(long index) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT * FROM " + SignalLevelDesignator.TableName + " LIMIT 1 OFFSET " + index.ToString(); + db.DBCommand.Parameters.Clear(); + try + { + DataTable Result = db.Select(db.DBCommand); + if ((Result != null) && (Result.Rows.Count > 0)) + { + return new SignalLevelDesignator(Result.Rows[0]); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + return null; + } + + public DateTime SignalLevelFindlastUpdated(DateTime lastupdated) + { + SignalLevelDesignator sd = new SignalLevelDesignator(lastupdated); + return SignalLevelFindLastUpdated(sd); + } + + public DateTime SignalLevelFindLastUpdated(SignalLevelDesignator sd) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "SELECT LastUpdated FROM " + SignalLevelDesignator.TableName + " WHERE LastUpdated = @LastUpdated"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(sd.AsUNIXTime("LastUpdated")); + object result = db.ExecuteScalar(db.DBCommand); + if (IsValid(result)) + return (SQLiteEntry.UNIXTimeToDateTime((int)result)); + } + return DateTime.MinValue; + } + + public int SignalLevelInsert(SignalLevelDesignator sd) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "INSERT INTO " + SignalLevelDesignator.TableName + " (Level, LastUpdated) VALUES (@Level, @LastUpdated)"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(sd.AsDouble("Level")); + db.DBCommand.Parameters.Add(sd.AsUNIXTime("LastUpdated")); + return db.ExecuteNonQuery(db.DBCommand); + } + } + + public int SignalLevelDelete(DateTime lastupdated) + { + SignalLevelDesignator sd = new SignalLevelDesignator(lastupdated); + return SignalLevelDelete(sd); + } + + public int SignalLevelDelete(SignalLevelDesignator sd) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "DELETE FROM " + SignalLevelDesignator.TableName + " WHERE LastUpdated = @LastUpdated"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(sd.AsUNIXTime("LastUpdated")); + return db.ExecuteNonQuery(db.DBCommand); + } + } + + public int SignalLevelDeleteAll() + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "DELETE FROM " + SignalLevelDesignator.TableName; + db.DBCommand.Parameters.Clear(); + return db.ExecuteNonQuery(db.DBCommand); + } + } + + public int SignalLevelUpdate(SignalLevelDesignator sd) + { + lock (db.DBCommand) + { + db.DBCommand.CommandText = "UPDATE " + SignalLevelDesignator.TableName + " SET Level = @Level, LastUpdated = @LastUpdated WHERE LastUpdated = @LastUpdated"; + db.DBCommand.Parameters.Clear(); + db.DBCommand.Parameters.Add(sd.AsDouble("Level")); + db.DBCommand.Parameters.Add(sd.AsUNIXTime("LastUpdated")); + return db.ExecuteNonQuery(db.DBCommand); + } + } + + public int SignalLevelBulkInsert(List sds) + { + int errors = 0; + try + { + lock (db) + { + db.BeginTransaction(); + foreach (SignalLevelDesignator sd in sds) + { + try + { + SignalLevelInsert(sd); + } + catch (Exception ex) + { + Log.WriteMessage("Error inserting SignalLevel at [" + sd.LastUpdated.ToString("yyyy-MM-dd HH:mm:ss") + "]: " + ex.ToString()); + errors++; + } + } + db.Commit(); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + return -errors; + } + + public int SignalLevelBulkDelete(List sds) + { + int errors = 0; + try + { + lock (db) + { + db.BeginTransaction(); + foreach (SignalLevelDesignator sd in sds) + { + try + { + SignalLevelDelete(sd); + } + catch (Exception ex) + { + Log.WriteMessage("Error deleting SignalLevel at [" + sd.LastUpdated.ToString("yyyy-MM-dd HH:mm:ss") + "]: " + ex.ToString()); + errors++; + } + } + db.Commit(); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + return -errors; + } + + public int SignalLevelBulkInsertOrUpdateIfNewer(List sds) + { + if (sds == null) + return 0; + int i = 0; + lock (db) + { + db.BeginTransaction(); + foreach (SignalLevelDesignator sd in sds) + { + SignalLevelInsertOrUpdateIfNewer(sd); + i++; + } + db.Commit(); + } + return i; + } + + public int SignalLevelInsertOrUpdateIfNewer(SignalLevelDesignator sd) + { + DateTime dt = SignalLevelFindLastUpdated(sd); + if (dt == DateTime.MinValue) + return SignalLevelInsert(sd); + if (dt < sd.LastUpdated) + return SignalLevelUpdate(sd); + return 0; + } + + public List SignalLevelGetAll() + { + List l = new List(); + DataTable Result = db.Select("SELECT * FROM " + SignalLevelDesignator.TableName); + if (!IsValid(Result) || (Result.Rows.Count == 0)) + return l; + foreach (DataRow row in Result.Rows) + l.Add(new SignalLevelDesignator(row)); + return l; + + } + + public List SignalLevelFromJSON(string json) + { + if (String.IsNullOrEmpty(json)) + return null; + JsonSerializerSettings settings = new JsonSerializerSettings(); + settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; + settings.FloatFormatHandling = FloatFormatHandling.String; + settings.Formatting = Newtonsoft.Json.Formatting.Indented; + return JsonConvert.DeserializeObject>(json, settings); + } + + public string SignalLevelToJSON() + { + List l = SignalLevelGetAll(); + JsonSerializerSettings settings = new JsonSerializerSettings(); + settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; + settings.FloatFormatHandling = FloatFormatHandling.String; + settings.Formatting = Newtonsoft.Json.Formatting.Indented; + string json = JsonConvert.SerializeObject(l, settings); + return json; + } + + public DataTableSignalLevels SignalLevelToDataTable() + { + List sds = SignalLevelGetAll(); + DataTableSignalLevels dtl = new DataTableSignalLevels(sds); + return dtl; + } + + #endregion + + } + +} diff --git a/AirScout.Signals/SignalLevel.cs b/AirScout.Signals/SignalLevel.cs new file mode 100644 index 0000000..dd8a8df --- /dev/null +++ b/AirScout.Signals/SignalLevel.cs @@ -0,0 +1,97 @@ +// AirScout.Aircrafts.SignalLevelDesignator +using System; +using System.Collections.Generic; +using System.Collections; +using System.Linq; +using System.Text; +using System.Threading; +using System.ComponentModel; +using System.Globalization; +using System.Net; +using System.IO; +using System.Data; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System.Data.SQLite; + +namespace AirScout.Signals +{ + [DesignerCategory("")] + public class SignalLevelDesignator : SQLiteEntry + { + [JsonIgnore] + public new static readonly string TableName = "SignalLevels"; + + public double Level + { + get; + set; + } + + public SignalLevelDesignator() + { + this.Level = -1.7976931348623157E+308; + base.LastUpdated = DateTime.MinValue; + } + + public SignalLevelDesignator(DataRow row) + : this() + { + base.FillFromDataRow(row); + } + + public SignalLevelDesignator(IDataRecord record) + : this() + { + base.FillFromDataRecord(record); + } + + public SignalLevelDesignator(DateTime lastupdated) + : this(-1.7976931348623157E+308, lastupdated) + { + } + + public SignalLevelDesignator(double level) + : this(level, DateTime.UtcNow) + { + } + + public SignalLevelDesignator(double level, DateTime lastupdated) + : this() + { + this.Level = level; + base.LastUpdated = lastupdated; + } + } + + [System.ComponentModel.DesignerCategory("")] + public class DataTableSignalLevels : DataTable + { + public DataTableSignalLevels() + : base() + { + // set table name + TableName = "SignalLevels"; + // create all specific columns + DataColumn Hex = this.Columns.Add("Level", typeof(double)); + DataColumn LastUpdated = this.Columns.Add("LastUpdated", typeof(string)); + // create primary key + DataColumn[] keys = new DataColumn[1]; + keys[0] = LastUpdated; + this.PrimaryKey = keys; + } + + public DataTableSignalLevels(List sds) + : this() + { + foreach (SignalLevelDesignator sd in sds) + { + DataRow row = this.NewRow(); + row[0] = sd.Level; + row[1] = sd.LastUpdated; + this.Rows.Add(row); + } + } + } +} + diff --git a/AirScout.Signals/app.config b/AirScout.Signals/app.config new file mode 100644 index 0000000..efbbc00 --- /dev/null +++ b/AirScout.Signals/app.config @@ -0,0 +1,18 @@ + + + + +
+ + + + + + + + + False + + + + \ No newline at end of file diff --git a/AirScout.Signals/packages.config b/AirScout.Signals/packages.config new file mode 100644 index 0000000..0aee17e --- /dev/null +++ b/AirScout.Signals/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/AirScout.sln b/AirScout.sln new file mode 100644 index 0000000..dd2c432 --- /dev/null +++ b/AirScout.sln @@ -0,0 +1,430 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AirScout", "AirScout\AirScout.csproj", "{17498590-2CFF-4D24-BFB8-549D14BD2545}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AirScout.PlaneFeeds", "AirScout.PlaneFeeds\AirScout.PlaneFeeds.csproj", "{EA78AD40-1505-406F-8049-744E58D93F54}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AirScoutPlaneServer", "AirScoutPlaneServer\AirScoutPlaneServer.csproj", "{5F5F4A33-9061-4F30-A08A-E68B4FB84D64}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AquaGauge", "AquaGauge\AquaGauge.csproj", "{0E5542E0-FC5D-4F67-950D-9F28C5D1225A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CubicSpline", "CubicSpline\CubicSpline\CubicSpline.csproj", "{CD637EDA-E0C3-4ABF-8E24-A5B94892311C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GMap.NET.WindowsForms", "GreatMaps\GMap.NET.WindowsForms\GMap.NET.WindowsForms.csproj", "{E06DEF77-F933-42FB-AFD7-DB2D0D8D6A98}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GMap.NET.Core", "GreatMaps\GMap.NET.Core\GMap.NET.Core.csproj", "{D0C39D9D-BED0-418B-9A5E-713176CAF40C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NDde", "NDDE\NDde\Source\NDde\NDde.csproj", "{D77772F9-3D3D-40BA-B95F-05C45878078F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orbit", "OrbitTools\OrbitTools\Orbit\Orbit.csproj", "{BAA5FE10-3E3A-4D5D-AB3D-4B50D6AC0321}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinTest", "WinTest\WinTest.csproj", "{7B815C51-6896-4989-BD1B-8D2D7A116AA3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LibADSB", "LibADSB\LibADSB.csproj", "{A5775994-404F-4B2E-9FF7-4537BBE17506}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScoutBase.Core", "ScoutBase\ScoutBase.Core\ScoutBase.Core.csproj", "{EE86E933-D883-4B18-80EB-0FBA55EC67C6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScoutBase.Elevation", "ScoutBase\ScoutBase.Elevation\ScoutBase.Elevation.csproj", "{009CABFD-726D-481F-972D-0A218E0AD9B9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AeroWizard", "AeroWizard\AeroWizard\AeroWizard.csproj", "{199C12C4-3EEF-4D08-BAC3-F2A62BCF969C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SerializableGenerics", "SerializableGenerics\SerializableGenerics.csproj", "{EF2EB181-4D5B-4418-A280-BFA1D53550BE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AirScoutDatabaseManager", "AirScoutDatabaseManager\AirScoutDatabaseManager.csproj", "{00062ABB-C482-4B06-BD1B-C49DE9ABA9E5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ElevationTileGenerator", "ElevationTileGenerator\ElevationTileGenerator.csproj", "{955CEA7D-37F6-4122-B4CA-B08C2B73969F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SQLiteDatabase", "SQLiteDatabase\SQLiteDatabase\SQLiteDatabase.csproj", "{6056D3BE-7002-4A6A-A9EA-6FF45122A3C7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HorizonGenerator", "HorizonGenerator\HorizonGenerator.csproj", "{ED1D9A86-30A9-4F0F-8FC8-E36B2EA01D0C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OxyPlot", "OxyPlot\Source\OxyPlot\OxyPlot.csproj", "{507DE008-21AC-469B-BC30-23B239832AF6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OxyPlot.WindowsForms", "OxyPlot\Source\OxyPlot.WindowsForms\OxyPlot.WindowsForms.csproj", "{8BC521AD-81CF-4E6C-8898-BE67527E7064}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScoutBase.Stations", "ScoutBase\ScoutBase.Stations\ScoutBase.Stations.csproj", "{358E87D7-849F-412A-B487-F7B7D585A7F9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ScoutBase.Propagation", "ScoutBase\ScoutBase.Propagation\ScoutBase.Propagation.csproj", "{610DB007-5F74-4B5D-8B71-5E2C163A99B3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Zip DLL", "DotNetZip\Zip\Zip DLL.csproj", "{D3B0AD67-44D8-4B3D-BED9-CE1FD6DE2C5A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AirScout.Aircrafts", "AirScout.Aircrafts\AirScout.Aircrafts.csproj", "{288A26EC-B690-41A2-84E5-61C9B7B74046}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CustomScrollBar", "CustomScrollBar\CustomScrollBar.csproj", "{80C7DC34-E1F6-4DA8-AFFB-904C06A3623B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orbit.Core", "OrbitTools\OrbitTools\Orbit.Core\Orbit.Core.csproj", "{99510ED5-99E0-405D-BCAC-9159A7426D61}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AirScoutViewClient", "AirScoutViewClient\AirScoutViewClient.csproj", "{930668AA-0DD9-42A9-889A-D319567F1473}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AirScout.Core", "AirScout.Core\AirScout.Core.csproj", "{41B66BE4-6086-4AE3-BE31-C81EE6B10154}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AirScout.AircraftPositions", "AirScout.AircraftPositions\AirScout.AircraftPositions.csproj", "{BE467E28-C202-4D71-BB17-9086861AD75F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AirScout.Signals", "AirScout.Signals\AirScout.Signals.csproj", "{3D34943B-CACA-426D-9FC0-661531668E87}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {17498590-2CFF-4D24-BFB8-549D14BD2545}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {17498590-2CFF-4D24-BFB8-549D14BD2545}.Debug|Any CPU.Build.0 = Debug|Any CPU + {17498590-2CFF-4D24-BFB8-549D14BD2545}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {17498590-2CFF-4D24-BFB8-549D14BD2545}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {17498590-2CFF-4D24-BFB8-549D14BD2545}.Debug|x86.ActiveCfg = Debug|x86 + {17498590-2CFF-4D24-BFB8-549D14BD2545}.Release|Any CPU.ActiveCfg = Release|Any CPU + {17498590-2CFF-4D24-BFB8-549D14BD2545}.Release|Any CPU.Build.0 = Release|Any CPU + {17498590-2CFF-4D24-BFB8-549D14BD2545}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {17498590-2CFF-4D24-BFB8-549D14BD2545}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {17498590-2CFF-4D24-BFB8-549D14BD2545}.Release|x86.ActiveCfg = Release|x86 + {17498590-2CFF-4D24-BFB8-549D14BD2545}.Release|x86.Build.0 = Release|x86 + {EA78AD40-1505-406F-8049-744E58D93F54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA78AD40-1505-406F-8049-744E58D93F54}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA78AD40-1505-406F-8049-744E58D93F54}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {EA78AD40-1505-406F-8049-744E58D93F54}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {EA78AD40-1505-406F-8049-744E58D93F54}.Debug|x86.ActiveCfg = Debug|x86 + {EA78AD40-1505-406F-8049-744E58D93F54}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA78AD40-1505-406F-8049-744E58D93F54}.Release|Any CPU.Build.0 = Release|Any CPU + {EA78AD40-1505-406F-8049-744E58D93F54}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {EA78AD40-1505-406F-8049-744E58D93F54}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {EA78AD40-1505-406F-8049-744E58D93F54}.Release|x86.ActiveCfg = Release|Any CPU + {5F5F4A33-9061-4F30-A08A-E68B4FB84D64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5F5F4A33-9061-4F30-A08A-E68B4FB84D64}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {5F5F4A33-9061-4F30-A08A-E68B4FB84D64}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {5F5F4A33-9061-4F30-A08A-E68B4FB84D64}.Debug|x86.ActiveCfg = Debug|x86 + {5F5F4A33-9061-4F30-A08A-E68B4FB84D64}.Release|Any CPU.ActiveCfg = Release|x86 + {5F5F4A33-9061-4F30-A08A-E68B4FB84D64}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {5F5F4A33-9061-4F30-A08A-E68B4FB84D64}.Release|Mixed Platforms.Build.0 = Release|x86 + {5F5F4A33-9061-4F30-A08A-E68B4FB84D64}.Release|x86.ActiveCfg = Release|x86 + {5F5F4A33-9061-4F30-A08A-E68B4FB84D64}.Release|x86.Build.0 = Release|x86 + {0E5542E0-FC5D-4F67-950D-9F28C5D1225A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0E5542E0-FC5D-4F67-950D-9F28C5D1225A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0E5542E0-FC5D-4F67-950D-9F28C5D1225A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {0E5542E0-FC5D-4F67-950D-9F28C5D1225A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {0E5542E0-FC5D-4F67-950D-9F28C5D1225A}.Debug|x86.ActiveCfg = Debug|Any CPU + {0E5542E0-FC5D-4F67-950D-9F28C5D1225A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0E5542E0-FC5D-4F67-950D-9F28C5D1225A}.Release|Any CPU.Build.0 = Release|Any CPU + {0E5542E0-FC5D-4F67-950D-9F28C5D1225A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {0E5542E0-FC5D-4F67-950D-9F28C5D1225A}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {0E5542E0-FC5D-4F67-950D-9F28C5D1225A}.Release|x86.ActiveCfg = Release|Any CPU + {CD637EDA-E0C3-4ABF-8E24-A5B94892311C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CD637EDA-E0C3-4ABF-8E24-A5B94892311C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CD637EDA-E0C3-4ABF-8E24-A5B94892311C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {CD637EDA-E0C3-4ABF-8E24-A5B94892311C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {CD637EDA-E0C3-4ABF-8E24-A5B94892311C}.Debug|x86.ActiveCfg = Debug|Any CPU + {CD637EDA-E0C3-4ABF-8E24-A5B94892311C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CD637EDA-E0C3-4ABF-8E24-A5B94892311C}.Release|Any CPU.Build.0 = Release|Any CPU + {CD637EDA-E0C3-4ABF-8E24-A5B94892311C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {CD637EDA-E0C3-4ABF-8E24-A5B94892311C}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {CD637EDA-E0C3-4ABF-8E24-A5B94892311C}.Release|x86.ActiveCfg = Release|Any CPU + {E06DEF77-F933-42FB-AFD7-DB2D0D8D6A98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E06DEF77-F933-42FB-AFD7-DB2D0D8D6A98}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E06DEF77-F933-42FB-AFD7-DB2D0D8D6A98}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {E06DEF77-F933-42FB-AFD7-DB2D0D8D6A98}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {E06DEF77-F933-42FB-AFD7-DB2D0D8D6A98}.Debug|x86.ActiveCfg = Debug|x86 + {E06DEF77-F933-42FB-AFD7-DB2D0D8D6A98}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E06DEF77-F933-42FB-AFD7-DB2D0D8D6A98}.Release|Any CPU.Build.0 = Release|Any CPU + {E06DEF77-F933-42FB-AFD7-DB2D0D8D6A98}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {E06DEF77-F933-42FB-AFD7-DB2D0D8D6A98}.Release|Mixed Platforms.Build.0 = Release|x86 + {E06DEF77-F933-42FB-AFD7-DB2D0D8D6A98}.Release|x86.ActiveCfg = Release|x86 + {E06DEF77-F933-42FB-AFD7-DB2D0D8D6A98}.Release|x86.Build.0 = Release|x86 + {D0C39D9D-BED0-418B-9A5E-713176CAF40C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D0C39D9D-BED0-418B-9A5E-713176CAF40C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D0C39D9D-BED0-418B-9A5E-713176CAF40C}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {D0C39D9D-BED0-418B-9A5E-713176CAF40C}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {D0C39D9D-BED0-418B-9A5E-713176CAF40C}.Debug|x86.ActiveCfg = Debug|x86 + {D0C39D9D-BED0-418B-9A5E-713176CAF40C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D0C39D9D-BED0-418B-9A5E-713176CAF40C}.Release|Any CPU.Build.0 = Release|Any CPU + {D0C39D9D-BED0-418B-9A5E-713176CAF40C}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {D0C39D9D-BED0-418B-9A5E-713176CAF40C}.Release|Mixed Platforms.Build.0 = Release|x86 + {D0C39D9D-BED0-418B-9A5E-713176CAF40C}.Release|x86.ActiveCfg = Release|x86 + {D0C39D9D-BED0-418B-9A5E-713176CAF40C}.Release|x86.Build.0 = Release|x86 + {D77772F9-3D3D-40BA-B95F-05C45878078F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D77772F9-3D3D-40BA-B95F-05C45878078F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D77772F9-3D3D-40BA-B95F-05C45878078F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {D77772F9-3D3D-40BA-B95F-05C45878078F}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {D77772F9-3D3D-40BA-B95F-05C45878078F}.Debug|x86.ActiveCfg = Debug|Any CPU + {D77772F9-3D3D-40BA-B95F-05C45878078F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D77772F9-3D3D-40BA-B95F-05C45878078F}.Release|Any CPU.Build.0 = Release|Any CPU + {D77772F9-3D3D-40BA-B95F-05C45878078F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {D77772F9-3D3D-40BA-B95F-05C45878078F}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {D77772F9-3D3D-40BA-B95F-05C45878078F}.Release|x86.ActiveCfg = Release|Any CPU + {BAA5FE10-3E3A-4D5D-AB3D-4B50D6AC0321}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAA5FE10-3E3A-4D5D-AB3D-4B50D6AC0321}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAA5FE10-3E3A-4D5D-AB3D-4B50D6AC0321}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {BAA5FE10-3E3A-4D5D-AB3D-4B50D6AC0321}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {BAA5FE10-3E3A-4D5D-AB3D-4B50D6AC0321}.Debug|x86.ActiveCfg = Debug|x86 + {BAA5FE10-3E3A-4D5D-AB3D-4B50D6AC0321}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAA5FE10-3E3A-4D5D-AB3D-4B50D6AC0321}.Release|Any CPU.Build.0 = Release|Any CPU + {BAA5FE10-3E3A-4D5D-AB3D-4B50D6AC0321}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {BAA5FE10-3E3A-4D5D-AB3D-4B50D6AC0321}.Release|Mixed Platforms.Build.0 = Release|x86 + {BAA5FE10-3E3A-4D5D-AB3D-4B50D6AC0321}.Release|x86.ActiveCfg = Release|x86 + {BAA5FE10-3E3A-4D5D-AB3D-4B50D6AC0321}.Release|x86.Build.0 = Release|x86 + {7B815C51-6896-4989-BD1B-8D2D7A116AA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7B815C51-6896-4989-BD1B-8D2D7A116AA3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7B815C51-6896-4989-BD1B-8D2D7A116AA3}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {7B815C51-6896-4989-BD1B-8D2D7A116AA3}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {7B815C51-6896-4989-BD1B-8D2D7A116AA3}.Debug|x86.ActiveCfg = Debug|Any CPU + {7B815C51-6896-4989-BD1B-8D2D7A116AA3}.Debug|x86.Build.0 = Debug|Any CPU + {7B815C51-6896-4989-BD1B-8D2D7A116AA3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7B815C51-6896-4989-BD1B-8D2D7A116AA3}.Release|Any CPU.Build.0 = Release|Any CPU + {7B815C51-6896-4989-BD1B-8D2D7A116AA3}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {7B815C51-6896-4989-BD1B-8D2D7A116AA3}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {7B815C51-6896-4989-BD1B-8D2D7A116AA3}.Release|x86.ActiveCfg = Release|Any CPU + {A5775994-404F-4B2E-9FF7-4537BBE17506}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A5775994-404F-4B2E-9FF7-4537BBE17506}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A5775994-404F-4B2E-9FF7-4537BBE17506}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {A5775994-404F-4B2E-9FF7-4537BBE17506}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {A5775994-404F-4B2E-9FF7-4537BBE17506}.Debug|x86.ActiveCfg = Debug|Any CPU + {A5775994-404F-4B2E-9FF7-4537BBE17506}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A5775994-404F-4B2E-9FF7-4537BBE17506}.Release|Any CPU.Build.0 = Release|Any CPU + {A5775994-404F-4B2E-9FF7-4537BBE17506}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {A5775994-404F-4B2E-9FF7-4537BBE17506}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {A5775994-404F-4B2E-9FF7-4537BBE17506}.Release|x86.ActiveCfg = Release|Any CPU + {EE86E933-D883-4B18-80EB-0FBA55EC67C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EE86E933-D883-4B18-80EB-0FBA55EC67C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EE86E933-D883-4B18-80EB-0FBA55EC67C6}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {EE86E933-D883-4B18-80EB-0FBA55EC67C6}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {EE86E933-D883-4B18-80EB-0FBA55EC67C6}.Debug|x86.ActiveCfg = Debug|Any CPU + {EE86E933-D883-4B18-80EB-0FBA55EC67C6}.Debug|x86.Build.0 = Debug|Any CPU + {EE86E933-D883-4B18-80EB-0FBA55EC67C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EE86E933-D883-4B18-80EB-0FBA55EC67C6}.Release|Any CPU.Build.0 = Release|Any CPU + {EE86E933-D883-4B18-80EB-0FBA55EC67C6}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {EE86E933-D883-4B18-80EB-0FBA55EC67C6}.Release|Mixed Platforms.Build.0 = Release|x86 + {EE86E933-D883-4B18-80EB-0FBA55EC67C6}.Release|x86.ActiveCfg = Release|x86 + {EE86E933-D883-4B18-80EB-0FBA55EC67C6}.Release|x86.Build.0 = Release|x86 + {009CABFD-726D-481F-972D-0A218E0AD9B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {009CABFD-726D-481F-972D-0A218E0AD9B9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {009CABFD-726D-481F-972D-0A218E0AD9B9}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {009CABFD-726D-481F-972D-0A218E0AD9B9}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {009CABFD-726D-481F-972D-0A218E0AD9B9}.Debug|x86.ActiveCfg = Debug|Any CPU + {009CABFD-726D-481F-972D-0A218E0AD9B9}.Debug|x86.Build.0 = Debug|Any CPU + {009CABFD-726D-481F-972D-0A218E0AD9B9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {009CABFD-726D-481F-972D-0A218E0AD9B9}.Release|Any CPU.Build.0 = Release|Any CPU + {009CABFD-726D-481F-972D-0A218E0AD9B9}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {009CABFD-726D-481F-972D-0A218E0AD9B9}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {009CABFD-726D-481F-972D-0A218E0AD9B9}.Release|x86.ActiveCfg = Release|Any CPU + {199C12C4-3EEF-4D08-BAC3-F2A62BCF969C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {199C12C4-3EEF-4D08-BAC3-F2A62BCF969C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {199C12C4-3EEF-4D08-BAC3-F2A62BCF969C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {199C12C4-3EEF-4D08-BAC3-F2A62BCF969C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {199C12C4-3EEF-4D08-BAC3-F2A62BCF969C}.Debug|x86.ActiveCfg = Debug|Any CPU + {199C12C4-3EEF-4D08-BAC3-F2A62BCF969C}.Debug|x86.Deploy.0 = Debug|Any CPU + {199C12C4-3EEF-4D08-BAC3-F2A62BCF969C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {199C12C4-3EEF-4D08-BAC3-F2A62BCF969C}.Release|Any CPU.Build.0 = Release|Any CPU + {199C12C4-3EEF-4D08-BAC3-F2A62BCF969C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {199C12C4-3EEF-4D08-BAC3-F2A62BCF969C}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {199C12C4-3EEF-4D08-BAC3-F2A62BCF969C}.Release|x86.ActiveCfg = Release|Any CPU + {199C12C4-3EEF-4D08-BAC3-F2A62BCF969C}.Release|x86.Build.0 = Release|Any CPU + {EF2EB181-4D5B-4418-A280-BFA1D53550BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EF2EB181-4D5B-4418-A280-BFA1D53550BE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EF2EB181-4D5B-4418-A280-BFA1D53550BE}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {EF2EB181-4D5B-4418-A280-BFA1D53550BE}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {EF2EB181-4D5B-4418-A280-BFA1D53550BE}.Debug|x86.ActiveCfg = Debug|Any CPU + {EF2EB181-4D5B-4418-A280-BFA1D53550BE}.Debug|x86.Build.0 = Debug|Any CPU + {EF2EB181-4D5B-4418-A280-BFA1D53550BE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EF2EB181-4D5B-4418-A280-BFA1D53550BE}.Release|Any CPU.Build.0 = Release|Any CPU + {EF2EB181-4D5B-4418-A280-BFA1D53550BE}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {EF2EB181-4D5B-4418-A280-BFA1D53550BE}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {EF2EB181-4D5B-4418-A280-BFA1D53550BE}.Release|x86.ActiveCfg = Release|Any CPU + {EF2EB181-4D5B-4418-A280-BFA1D53550BE}.Release|x86.Build.0 = Release|Any CPU + {00062ABB-C482-4B06-BD1B-C49DE9ABA9E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {00062ABB-C482-4B06-BD1B-C49DE9ABA9E5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {00062ABB-C482-4B06-BD1B-C49DE9ABA9E5}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {00062ABB-C482-4B06-BD1B-C49DE9ABA9E5}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {00062ABB-C482-4B06-BD1B-C49DE9ABA9E5}.Debug|x86.ActiveCfg = Debug|x86 + {00062ABB-C482-4B06-BD1B-C49DE9ABA9E5}.Release|Any CPU.ActiveCfg = Release|x86 + {00062ABB-C482-4B06-BD1B-C49DE9ABA9E5}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {00062ABB-C482-4B06-BD1B-C49DE9ABA9E5}.Release|Mixed Platforms.Build.0 = Release|x86 + {00062ABB-C482-4B06-BD1B-C49DE9ABA9E5}.Release|x86.ActiveCfg = Release|x86 + {00062ABB-C482-4B06-BD1B-C49DE9ABA9E5}.Release|x86.Build.0 = Release|x86 + {955CEA7D-37F6-4122-B4CA-B08C2B73969F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {955CEA7D-37F6-4122-B4CA-B08C2B73969F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {955CEA7D-37F6-4122-B4CA-B08C2B73969F}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {955CEA7D-37F6-4122-B4CA-B08C2B73969F}.Debug|x86.ActiveCfg = Debug|x86 + {955CEA7D-37F6-4122-B4CA-B08C2B73969F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {955CEA7D-37F6-4122-B4CA-B08C2B73969F}.Release|Any CPU.Build.0 = Release|Any CPU + {955CEA7D-37F6-4122-B4CA-B08C2B73969F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {955CEA7D-37F6-4122-B4CA-B08C2B73969F}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {955CEA7D-37F6-4122-B4CA-B08C2B73969F}.Release|x86.ActiveCfg = Release|Any CPU + {955CEA7D-37F6-4122-B4CA-B08C2B73969F}.Release|x86.Build.0 = Release|Any CPU + {6056D3BE-7002-4A6A-A9EA-6FF45122A3C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6056D3BE-7002-4A6A-A9EA-6FF45122A3C7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6056D3BE-7002-4A6A-A9EA-6FF45122A3C7}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {6056D3BE-7002-4A6A-A9EA-6FF45122A3C7}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {6056D3BE-7002-4A6A-A9EA-6FF45122A3C7}.Debug|x86.ActiveCfg = Debug|Any CPU + {6056D3BE-7002-4A6A-A9EA-6FF45122A3C7}.Debug|x86.Build.0 = Debug|Any CPU + {6056D3BE-7002-4A6A-A9EA-6FF45122A3C7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6056D3BE-7002-4A6A-A9EA-6FF45122A3C7}.Release|Any CPU.Build.0 = Release|Any CPU + {6056D3BE-7002-4A6A-A9EA-6FF45122A3C7}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {6056D3BE-7002-4A6A-A9EA-6FF45122A3C7}.Release|Mixed Platforms.Build.0 = Release|x86 + {6056D3BE-7002-4A6A-A9EA-6FF45122A3C7}.Release|x86.ActiveCfg = Release|x86 + {6056D3BE-7002-4A6A-A9EA-6FF45122A3C7}.Release|x86.Build.0 = Release|x86 + {ED1D9A86-30A9-4F0F-8FC8-E36B2EA01D0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ED1D9A86-30A9-4F0F-8FC8-E36B2EA01D0C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {ED1D9A86-30A9-4F0F-8FC8-E36B2EA01D0C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {ED1D9A86-30A9-4F0F-8FC8-E36B2EA01D0C}.Debug|x86.ActiveCfg = Debug|Any CPU + {ED1D9A86-30A9-4F0F-8FC8-E36B2EA01D0C}.Debug|x86.Build.0 = Debug|Any CPU + {ED1D9A86-30A9-4F0F-8FC8-E36B2EA01D0C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ED1D9A86-30A9-4F0F-8FC8-E36B2EA01D0C}.Release|Any CPU.Build.0 = Release|Any CPU + {ED1D9A86-30A9-4F0F-8FC8-E36B2EA01D0C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {ED1D9A86-30A9-4F0F-8FC8-E36B2EA01D0C}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {ED1D9A86-30A9-4F0F-8FC8-E36B2EA01D0C}.Release|x86.ActiveCfg = Release|Any CPU + {ED1D9A86-30A9-4F0F-8FC8-E36B2EA01D0C}.Release|x86.Build.0 = Release|Any CPU + {507DE008-21AC-469B-BC30-23B239832AF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {507DE008-21AC-469B-BC30-23B239832AF6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {507DE008-21AC-469B-BC30-23B239832AF6}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {507DE008-21AC-469B-BC30-23B239832AF6}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {507DE008-21AC-469B-BC30-23B239832AF6}.Debug|x86.ActiveCfg = Debug|Any CPU + {507DE008-21AC-469B-BC30-23B239832AF6}.Debug|x86.Build.0 = Debug|Any CPU + {507DE008-21AC-469B-BC30-23B239832AF6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {507DE008-21AC-469B-BC30-23B239832AF6}.Release|Any CPU.Build.0 = Release|Any CPU + {507DE008-21AC-469B-BC30-23B239832AF6}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {507DE008-21AC-469B-BC30-23B239832AF6}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {507DE008-21AC-469B-BC30-23B239832AF6}.Release|x86.ActiveCfg = Release|Any CPU + {507DE008-21AC-469B-BC30-23B239832AF6}.Release|x86.Build.0 = Release|Any CPU + {8BC521AD-81CF-4E6C-8898-BE67527E7064}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8BC521AD-81CF-4E6C-8898-BE67527E7064}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8BC521AD-81CF-4E6C-8898-BE67527E7064}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {8BC521AD-81CF-4E6C-8898-BE67527E7064}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {8BC521AD-81CF-4E6C-8898-BE67527E7064}.Debug|x86.ActiveCfg = Debug|Any CPU + {8BC521AD-81CF-4E6C-8898-BE67527E7064}.Debug|x86.Build.0 = Debug|Any CPU + {8BC521AD-81CF-4E6C-8898-BE67527E7064}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8BC521AD-81CF-4E6C-8898-BE67527E7064}.Release|Any CPU.Build.0 = Release|Any CPU + {8BC521AD-81CF-4E6C-8898-BE67527E7064}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {8BC521AD-81CF-4E6C-8898-BE67527E7064}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {8BC521AD-81CF-4E6C-8898-BE67527E7064}.Release|x86.ActiveCfg = Release|Any CPU + {8BC521AD-81CF-4E6C-8898-BE67527E7064}.Release|x86.Build.0 = Release|Any CPU + {358E87D7-849F-412A-B487-F7B7D585A7F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {358E87D7-849F-412A-B487-F7B7D585A7F9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {358E87D7-849F-412A-B487-F7B7D585A7F9}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {358E87D7-849F-412A-B487-F7B7D585A7F9}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {358E87D7-849F-412A-B487-F7B7D585A7F9}.Debug|x86.ActiveCfg = Debug|Any CPU + {358E87D7-849F-412A-B487-F7B7D585A7F9}.Debug|x86.Build.0 = Debug|Any CPU + {358E87D7-849F-412A-B487-F7B7D585A7F9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {358E87D7-849F-412A-B487-F7B7D585A7F9}.Release|Any CPU.Build.0 = Release|Any CPU + {358E87D7-849F-412A-B487-F7B7D585A7F9}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {358E87D7-849F-412A-B487-F7B7D585A7F9}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {358E87D7-849F-412A-B487-F7B7D585A7F9}.Release|x86.ActiveCfg = Release|Any CPU + {358E87D7-849F-412A-B487-F7B7D585A7F9}.Release|x86.Build.0 = Release|Any CPU + {610DB007-5F74-4B5D-8B71-5E2C163A99B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {610DB007-5F74-4B5D-8B71-5E2C163A99B3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {610DB007-5F74-4B5D-8B71-5E2C163A99B3}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {610DB007-5F74-4B5D-8B71-5E2C163A99B3}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {610DB007-5F74-4B5D-8B71-5E2C163A99B3}.Debug|x86.ActiveCfg = Debug|Any CPU + {610DB007-5F74-4B5D-8B71-5E2C163A99B3}.Debug|x86.Build.0 = Debug|Any CPU + {610DB007-5F74-4B5D-8B71-5E2C163A99B3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {610DB007-5F74-4B5D-8B71-5E2C163A99B3}.Release|Any CPU.Build.0 = Release|Any CPU + {610DB007-5F74-4B5D-8B71-5E2C163A99B3}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {610DB007-5F74-4B5D-8B71-5E2C163A99B3}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {610DB007-5F74-4B5D-8B71-5E2C163A99B3}.Release|x86.ActiveCfg = Release|Any CPU + {610DB007-5F74-4B5D-8B71-5E2C163A99B3}.Release|x86.Build.0 = Release|Any CPU + {D3B0AD67-44D8-4B3D-BED9-CE1FD6DE2C5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D3B0AD67-44D8-4B3D-BED9-CE1FD6DE2C5A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D3B0AD67-44D8-4B3D-BED9-CE1FD6DE2C5A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {D3B0AD67-44D8-4B3D-BED9-CE1FD6DE2C5A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {D3B0AD67-44D8-4B3D-BED9-CE1FD6DE2C5A}.Debug|x86.ActiveCfg = Debug|Any CPU + {D3B0AD67-44D8-4B3D-BED9-CE1FD6DE2C5A}.Debug|x86.Build.0 = Debug|Any CPU + {D3B0AD67-44D8-4B3D-BED9-CE1FD6DE2C5A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D3B0AD67-44D8-4B3D-BED9-CE1FD6DE2C5A}.Release|Any CPU.Build.0 = Release|Any CPU + {D3B0AD67-44D8-4B3D-BED9-CE1FD6DE2C5A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {D3B0AD67-44D8-4B3D-BED9-CE1FD6DE2C5A}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {D3B0AD67-44D8-4B3D-BED9-CE1FD6DE2C5A}.Release|x86.ActiveCfg = Release|Any CPU + {D3B0AD67-44D8-4B3D-BED9-CE1FD6DE2C5A}.Release|x86.Build.0 = Release|Any CPU + {288A26EC-B690-41A2-84E5-61C9B7B74046}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {288A26EC-B690-41A2-84E5-61C9B7B74046}.Debug|Any CPU.Build.0 = Debug|Any CPU + {288A26EC-B690-41A2-84E5-61C9B7B74046}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {288A26EC-B690-41A2-84E5-61C9B7B74046}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {288A26EC-B690-41A2-84E5-61C9B7B74046}.Debug|x86.ActiveCfg = Debug|Any CPU + {288A26EC-B690-41A2-84E5-61C9B7B74046}.Debug|x86.Build.0 = Debug|Any CPU + {288A26EC-B690-41A2-84E5-61C9B7B74046}.Release|Any CPU.ActiveCfg = Release|Any CPU + {288A26EC-B690-41A2-84E5-61C9B7B74046}.Release|Any CPU.Build.0 = Release|Any CPU + {288A26EC-B690-41A2-84E5-61C9B7B74046}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {288A26EC-B690-41A2-84E5-61C9B7B74046}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {288A26EC-B690-41A2-84E5-61C9B7B74046}.Release|x86.ActiveCfg = Release|Any CPU + {288A26EC-B690-41A2-84E5-61C9B7B74046}.Release|x86.Build.0 = Release|Any CPU + {80C7DC34-E1F6-4DA8-AFFB-904C06A3623B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {80C7DC34-E1F6-4DA8-AFFB-904C06A3623B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {80C7DC34-E1F6-4DA8-AFFB-904C06A3623B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {80C7DC34-E1F6-4DA8-AFFB-904C06A3623B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {80C7DC34-E1F6-4DA8-AFFB-904C06A3623B}.Debug|x86.ActiveCfg = Debug|Any CPU + {80C7DC34-E1F6-4DA8-AFFB-904C06A3623B}.Debug|x86.Build.0 = Debug|Any CPU + {80C7DC34-E1F6-4DA8-AFFB-904C06A3623B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {80C7DC34-E1F6-4DA8-AFFB-904C06A3623B}.Release|Any CPU.Build.0 = Release|Any CPU + {80C7DC34-E1F6-4DA8-AFFB-904C06A3623B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {80C7DC34-E1F6-4DA8-AFFB-904C06A3623B}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {80C7DC34-E1F6-4DA8-AFFB-904C06A3623B}.Release|x86.ActiveCfg = Release|Any CPU + {80C7DC34-E1F6-4DA8-AFFB-904C06A3623B}.Release|x86.Build.0 = Release|Any CPU + {99510ED5-99E0-405D-BCAC-9159A7426D61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {99510ED5-99E0-405D-BCAC-9159A7426D61}.Debug|Any CPU.Build.0 = Debug|Any CPU + {99510ED5-99E0-405D-BCAC-9159A7426D61}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {99510ED5-99E0-405D-BCAC-9159A7426D61}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {99510ED5-99E0-405D-BCAC-9159A7426D61}.Debug|x86.ActiveCfg = Debug|x86 + {99510ED5-99E0-405D-BCAC-9159A7426D61}.Debug|x86.Build.0 = Debug|x86 + {99510ED5-99E0-405D-BCAC-9159A7426D61}.Release|Any CPU.ActiveCfg = Release|Any CPU + {99510ED5-99E0-405D-BCAC-9159A7426D61}.Release|Any CPU.Build.0 = Release|Any CPU + {99510ED5-99E0-405D-BCAC-9159A7426D61}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {99510ED5-99E0-405D-BCAC-9159A7426D61}.Release|Mixed Platforms.Build.0 = Release|x86 + {99510ED5-99E0-405D-BCAC-9159A7426D61}.Release|x86.ActiveCfg = Release|x86 + {99510ED5-99E0-405D-BCAC-9159A7426D61}.Release|x86.Build.0 = Release|x86 + {930668AA-0DD9-42A9-889A-D319567F1473}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {930668AA-0DD9-42A9-889A-D319567F1473}.Debug|Any CPU.Build.0 = Debug|Any CPU + {930668AA-0DD9-42A9-889A-D319567F1473}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {930668AA-0DD9-42A9-889A-D319567F1473}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {930668AA-0DD9-42A9-889A-D319567F1473}.Debug|x86.ActiveCfg = Debug|Any CPU + {930668AA-0DD9-42A9-889A-D319567F1473}.Debug|x86.Build.0 = Debug|Any CPU + {930668AA-0DD9-42A9-889A-D319567F1473}.Release|Any CPU.ActiveCfg = Release|Any CPU + {930668AA-0DD9-42A9-889A-D319567F1473}.Release|Any CPU.Build.0 = Release|Any CPU + {930668AA-0DD9-42A9-889A-D319567F1473}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {930668AA-0DD9-42A9-889A-D319567F1473}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {930668AA-0DD9-42A9-889A-D319567F1473}.Release|x86.ActiveCfg = Release|Any CPU + {930668AA-0DD9-42A9-889A-D319567F1473}.Release|x86.Build.0 = Release|Any CPU + {41B66BE4-6086-4AE3-BE31-C81EE6B10154}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {41B66BE4-6086-4AE3-BE31-C81EE6B10154}.Debug|Any CPU.Build.0 = Debug|Any CPU + {41B66BE4-6086-4AE3-BE31-C81EE6B10154}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {41B66BE4-6086-4AE3-BE31-C81EE6B10154}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {41B66BE4-6086-4AE3-BE31-C81EE6B10154}.Debug|x86.ActiveCfg = Debug|Any CPU + {41B66BE4-6086-4AE3-BE31-C81EE6B10154}.Debug|x86.Build.0 = Debug|Any CPU + {41B66BE4-6086-4AE3-BE31-C81EE6B10154}.Release|Any CPU.ActiveCfg = Release|Any CPU + {41B66BE4-6086-4AE3-BE31-C81EE6B10154}.Release|Any CPU.Build.0 = Release|Any CPU + {41B66BE4-6086-4AE3-BE31-C81EE6B10154}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {41B66BE4-6086-4AE3-BE31-C81EE6B10154}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {41B66BE4-6086-4AE3-BE31-C81EE6B10154}.Release|x86.ActiveCfg = Release|Any CPU + {41B66BE4-6086-4AE3-BE31-C81EE6B10154}.Release|x86.Build.0 = Release|Any CPU + {BE467E28-C202-4D71-BB17-9086861AD75F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BE467E28-C202-4D71-BB17-9086861AD75F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BE467E28-C202-4D71-BB17-9086861AD75F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {BE467E28-C202-4D71-BB17-9086861AD75F}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {BE467E28-C202-4D71-BB17-9086861AD75F}.Debug|x86.ActiveCfg = Debug|Any CPU + {BE467E28-C202-4D71-BB17-9086861AD75F}.Debug|x86.Build.0 = Debug|Any CPU + {BE467E28-C202-4D71-BB17-9086861AD75F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BE467E28-C202-4D71-BB17-9086861AD75F}.Release|Any CPU.Build.0 = Release|Any CPU + {BE467E28-C202-4D71-BB17-9086861AD75F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {BE467E28-C202-4D71-BB17-9086861AD75F}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {BE467E28-C202-4D71-BB17-9086861AD75F}.Release|x86.ActiveCfg = Release|Any CPU + {BE467E28-C202-4D71-BB17-9086861AD75F}.Release|x86.Build.0 = Release|Any CPU + {3D34943B-CACA-426D-9FC0-661531668E87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3D34943B-CACA-426D-9FC0-661531668E87}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3D34943B-CACA-426D-9FC0-661531668E87}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {3D34943B-CACA-426D-9FC0-661531668E87}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {3D34943B-CACA-426D-9FC0-661531668E87}.Debug|x86.ActiveCfg = Debug|Any CPU + {3D34943B-CACA-426D-9FC0-661531668E87}.Debug|x86.Build.0 = Debug|Any CPU + {3D34943B-CACA-426D-9FC0-661531668E87}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3D34943B-CACA-426D-9FC0-661531668E87}.Release|Any CPU.Build.0 = Release|Any CPU + {3D34943B-CACA-426D-9FC0-661531668E87}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {3D34943B-CACA-426D-9FC0-661531668E87}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {3D34943B-CACA-426D-9FC0-661531668E87}.Release|x86.ActiveCfg = Release|Any CPU + {3D34943B-CACA-426D-9FC0-661531668E87}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/AirScout/AirScout.csproj b/AirScout/AirScout.csproj new file mode 100644 index 0000000..7447820 --- /dev/null +++ b/AirScout/AirScout.csproj @@ -0,0 +1,545 @@ + + + + Debug + x86 + 8.0.30703 + 2.0 + {17498590-2CFF-4D24-BFB8-549D14BD2545} + WinExe + Properties + AirScout + AirScout + v4.0 + + + 512 + false + + + publish\ + false + Disk + false + Foreground + 7 + Days + false + false + true + http://www.dl0gth.de/software/airscout + en-GB + AirScout + DL2ALF + 0 + 1.0.0.0 + false + true + true + + + x86 + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + false + + + x86 + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + 63A018CBEFF856D37BB8205182B79FA75AC368C1 + + + AirScout_TemporaryKey.pfx + + + false + + + false + + + AirScout.Program + + + Main.ico + + + true + bin\Debug\ + DEBUG;TRACE + full + AnyCPU + false + prompt + false + false + false + false + + + bin\Release\ + TRACE + true + pdbonly + AnyCPU + prompt + false + + + LocalIntranet + + + Properties\app.manifest + + + + ..\packages\Newtonsoft.Json.12.0.1\lib\net40\Newtonsoft.Json.dll + True + + + ..\packages\SSH.NET.2016.1.0\lib\net40\Renci.SshNet.dll + True + + + + + + + ..\packages\System.Data.SQLite.Core.1.0.110.0\lib\net40\System.Data.SQLite.dll + True + + + + + + + + + + + + + + + + + + Form + + + CleanupDlg.cs + + + Form + + + CrossingHistoryDlg.cs + + + Form + + + LocalObstructionsDlg.cs + + + Form + + + MapStationDlg.cs + + + Form + + + Form + + + ElevationCopyrightDlg.cs + + + Form + + + FirstRunWizard.cs + + + Form + + + LicenseDlg.cs + + + Form + + + PlaneFeedDisclaimerDlg.cs + + + Form + + + HistoryFromURLDlg.cs + + + Form + + + HorizonDlg.cs + + + Form + + + MapDlg.cs + + + Form + + + MapProviderDlg.cs + + + Form + + + PlaneFeedSettingsDlg.cs + + + + Form + + + OptionsDlg.cs + + + Form + + + PlaneHistoryDlg.cs + + + + + + Form + + + ReflectionDlg.cs + + + Form + + + ScoutBaseDatabaseMaintenanceDlg.cs + + + Form + + + SetTimeDlg.cs + + + + Form + + + Splash.cs + + + Form + + + TrafficDlg.cs + + + + Form + + + WatchlistDlg.cs + + + Form + + + PreserveNewest + + + + Always + + + + + Always + + + Always + + + Always + + + CleanupDlg.cs + + + CrossingHistoryDlg.cs + + + ElevationCopyrightDlg.cs + + + FirstRunWizard.cs + + + LicenseDlg.cs + + + LocalObstructionsDlg.cs + + + MapStationDlg.cs + + + PlaneFeedDisclaimerDlg.cs + + + HistoryFromURLDlg.cs + + + HorizonDlg.cs + + + MapDlg.cs + + + MapProviderDlg.cs + + + OptionsDlg.cs + + + PlaneFeedSettingsDlg.cs + + + PlaneHistoryDlg.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + ReflectionDlg.cs + + + ScoutBaseDatabaseMaintenanceDlg.cs + + + SetTimeDlg.cs + + + Splash.cs + + + TrafficDlg.cs + + + WatchlistDlg.cs + + + + Always + + + Always + + + Always + + + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + Designer + + + True + Settings.settings + True + + + + + {199c12c4-3eef-4d08-bac3-f2a62bcf969c} + AeroWizard + + + {be467e28-c202-4d71-bb17-9086861ad75f} + AirScout.AircraftPositions + + + {288a26ec-b690-41a2-84e5-61c9b7b74046} + AirScout.Aircrafts + + + {41b66be4-6086-4ae3-be31-c81ee6b10154} + AirScout.Core + + + {3d34943b-caca-426d-9fc0-661531668e87} + AirScout.Signals + + + {80c7dc34-e1f6-4da8-affb-904c06a3623b} + CustomScrollBar + + + {d3b0ad67-44d8-4b3d-bed9-ce1fd6de2c5a} + Zip DLL + + + {99510ed5-99e0-405d-bcac-9159a7426d61} + Orbit.Core + + + {baa5fe10-3e3a-4d5d-ab3d-4b50d6ac0321} + Orbit + + + {8bc521ad-81cf-4e6c-8898-be67527e7064} + OxyPlot.WindowsForms + + + {507de008-21ac-469b-bc30-23b239832af6} + OxyPlot + + + {EE86E933-D883-4B18-80EB-0FBA55EC67C6} + ScoutBase.Core + + + {009cabfd-726d-481f-972d-0a218e0ad9b9} + ScoutBase.Elevation + + + {610db007-5f74-4b5d-8b71-5e2c163a99b3} + ScoutBase.Propagation + + + {358e87d7-849f-412a-b487-f7b7d585a7f9} + ScoutBase.Stations + + + {6056D3BE-7002-4A6A-A9EA-6FF45122A3C7} + SQLiteDatabase + + + {EA78AD40-1505-406F-8049-744E58D93F54} + AirScout.PlaneFeeds + + + {0E5542E0-FC5D-4F67-950D-9F28C5D1225A} + AquaGauge + + + {CD637EDA-E0C3-4ABF-8E24-A5B94892311C} + CubicSpline + + + {D0C39D9D-BED0-418B-9A5E-713176CAF40C} + GMap.NET.Core + + + {E06DEF77-F933-42FB-AFD7-DB2D0D8D6A98} + GMap.NET.WindowsForms + + + {D77772F9-3D3D-40BA-B95F-05C45878078F} + NDde + + + {ef2eb181-4d5b-4418-a280-bfa1d53550be} + SerializableGenerics + + + {7B815C51-6896-4989-BD1B-8D2D7A116AA3} + WinTest + + + + + + + + + + + False + Microsoft .NET Framework 4 Client Profile %28x86 und x64%29 + true + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + false + + + False + Windows Installer 3.1 + true + + + + + PreserveNewest + + + + + + + + + + + + Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}". + + + + + \ No newline at end of file diff --git a/AirScout/AirScout_clean.cmd b/AirScout/AirScout_clean.cmd new file mode 100644 index 0000000..7583be4 --- /dev/null +++ b/AirScout/AirScout_clean.cmd @@ -0,0 +1 @@ +AirScout.exe /clean \ No newline at end of file diff --git a/AirScout/CleanupDlg.Designer.cs b/AirScout/CleanupDlg.Designer.cs new file mode 100644 index 0000000..8cbf265 --- /dev/null +++ b/AirScout/CleanupDlg.Designer.cs @@ -0,0 +1,192 @@ +namespace AirScout +{ + partial class CleanupDlg + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(CleanupDlg)); + this.label1 = new System.Windows.Forms.Label(); + this.cb_UserSettings = new System.Windows.Forms.CheckBox(); + this.cb_AirScout_Database = new System.Windows.Forms.CheckBox(); + this.cb_ScoutBase_Database = new System.Windows.Forms.CheckBox(); + this.cb_LogFiles = new System.Windows.Forms.CheckBox(); + this.cb_ElevationFiles = new System.Windows.Forms.CheckBox(); + this.cb_TmpFiles = new System.Windows.Forms.CheckBox(); + this.btn_OK = new System.Windows.Forms.Button(); + this.btn_Cancel = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(38, 22); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(501, 48); + this.label1.TabIndex = 0; + this.label1.Text = "This will cleanup your AirScout database and user settings.\r\nPlease choose which " + + "parts should be erased:"; + this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // cb_UserSettings + // + this.cb_UserSettings.AutoSize = true; + this.cb_UserSettings.Checked = true; + this.cb_UserSettings.CheckState = System.Windows.Forms.CheckState.Checked; + this.cb_UserSettings.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_UserSettings.Location = new System.Drawing.Point(42, 106); + this.cb_UserSettings.Name = "cb_UserSettings"; + this.cb_UserSettings.Size = new System.Drawing.Size(107, 20); + this.cb_UserSettings.TabIndex = 1; + this.cb_UserSettings.Text = "User Settings"; + this.cb_UserSettings.UseVisualStyleBackColor = true; + // + // cb_AirScout_Database + // + this.cb_AirScout_Database.AutoSize = true; + this.cb_AirScout_Database.Checked = true; + this.cb_AirScout_Database.CheckState = System.Windows.Forms.CheckState.Checked; + this.cb_AirScout_Database.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_AirScout_Database.Location = new System.Drawing.Point(42, 129); + this.cb_AirScout_Database.Name = "cb_AirScout_Database"; + this.cb_AirScout_Database.Size = new System.Drawing.Size(305, 20); + this.cb_AirScout_Database.TabIndex = 2; + this.cb_AirScout_Database.Text = "AirScout Database (Aircraft data and positions)"; + this.cb_AirScout_Database.UseVisualStyleBackColor = true; + // + // cb_ScoutBase_Database + // + this.cb_ScoutBase_Database.AutoSize = true; + this.cb_ScoutBase_Database.Checked = true; + this.cb_ScoutBase_Database.CheckState = System.Windows.Forms.CheckState.Checked; + this.cb_ScoutBase_Database.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_ScoutBase_Database.Location = new System.Drawing.Point(42, 152); + this.cb_ScoutBase_Database.Name = "cb_ScoutBase_Database"; + this.cb_ScoutBase_Database.Size = new System.Drawing.Size(400, 20); + this.cb_ScoutBase_Database.TabIndex = 3; + this.cb_ScoutBase_Database.Text = "ScoutBase Database (Elevation, propagation and station data)"; + this.cb_ScoutBase_Database.UseVisualStyleBackColor = true; + // + // cb_LogFiles + // + this.cb_LogFiles.AutoSize = true; + this.cb_LogFiles.Checked = true; + this.cb_LogFiles.CheckState = System.Windows.Forms.CheckState.Checked; + this.cb_LogFiles.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_LogFiles.Location = new System.Drawing.Point(42, 175); + this.cb_LogFiles.Name = "cb_LogFiles"; + this.cb_LogFiles.Size = new System.Drawing.Size(79, 20); + this.cb_LogFiles.TabIndex = 4; + this.cb_LogFiles.Text = "LogFiles"; + this.cb_LogFiles.UseVisualStyleBackColor = true; + // + // cb_ElevationFiles + // + this.cb_ElevationFiles.AutoSize = true; + this.cb_ElevationFiles.Checked = true; + this.cb_ElevationFiles.CheckState = System.Windows.Forms.CheckState.Checked; + this.cb_ElevationFiles.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_ElevationFiles.Location = new System.Drawing.Point(42, 198); + this.cb_ElevationFiles.Name = "cb_ElevationFiles"; + this.cb_ElevationFiles.Size = new System.Drawing.Size(204, 20); + this.cb_ElevationFiles.TabIndex = 5; + this.cb_ElevationFiles.Text = "Elevation Files (up to V1.2.0.7)"; + this.cb_ElevationFiles.UseVisualStyleBackColor = true; + // + // cb_TmpFiles + // + this.cb_TmpFiles.AutoSize = true; + this.cb_TmpFiles.Checked = true; + this.cb_TmpFiles.CheckState = System.Windows.Forms.CheckState.Checked; + this.cb_TmpFiles.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_TmpFiles.Location = new System.Drawing.Point(42, 224); + this.cb_TmpFiles.Name = "cb_TmpFiles"; + this.cb_TmpFiles.Size = new System.Drawing.Size(87, 20); + this.cb_TmpFiles.TabIndex = 6; + this.cb_TmpFiles.Text = "Tmp Files"; + this.cb_TmpFiles.UseVisualStyleBackColor = true; + // + // btn_OK + // + this.btn_OK.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_OK.Location = new System.Drawing.Point(152, 276); + this.btn_OK.Name = "btn_OK"; + this.btn_OK.Size = new System.Drawing.Size(114, 29); + this.btn_OK.TabIndex = 7; + this.btn_OK.Text = "Continue"; + this.btn_OK.UseVisualStyleBackColor = true; + this.btn_OK.Click += new System.EventHandler(this.btn_OK_Click); + // + // btn_Cancel + // + this.btn_Cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btn_Cancel.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Cancel.Location = new System.Drawing.Point(323, 276); + this.btn_Cancel.Name = "btn_Cancel"; + this.btn_Cancel.Size = new System.Drawing.Size(114, 29); + this.btn_Cancel.TabIndex = 8; + this.btn_Cancel.Text = "Cancel"; + this.btn_Cancel.UseVisualStyleBackColor = true; + this.btn_Cancel.Click += new System.EventHandler(this.btn_Cancel_Click); + // + // CleanupDlg + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.BackColor = System.Drawing.Color.MistyRose; + this.ClientSize = new System.Drawing.Size(624, 317); + this.Controls.Add(this.btn_Cancel); + this.Controls.Add(this.btn_OK); + this.Controls.Add(this.cb_TmpFiles); + this.Controls.Add(this.cb_ElevationFiles); + this.Controls.Add(this.cb_LogFiles); + this.Controls.Add(this.cb_ScoutBase_Database); + this.Controls.Add(this.cb_AirScout_Database); + this.Controls.Add(this.cb_UserSettings); + this.Controls.Add(this.label1); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.Name = "CleanupDlg"; + this.Text = "AirScout Cleanup"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.CleanupDlg_FormClosing); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label label1; + private System.Windows.Forms.CheckBox cb_UserSettings; + private System.Windows.Forms.CheckBox cb_AirScout_Database; + private System.Windows.Forms.CheckBox cb_ScoutBase_Database; + private System.Windows.Forms.CheckBox cb_LogFiles; + private System.Windows.Forms.CheckBox cb_ElevationFiles; + private System.Windows.Forms.CheckBox cb_TmpFiles; + private System.Windows.Forms.Button btn_OK; + private System.Windows.Forms.Button btn_Cancel; + } +} \ No newline at end of file diff --git a/AirScout/CleanupDlg.cs b/AirScout/CleanupDlg.cs new file mode 100644 index 0000000..907fee6 --- /dev/null +++ b/AirScout/CleanupDlg.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.IO; +using System.Threading.Tasks; +using System.Windows.Forms; +using ScoutBase.Stations; +using ScoutBase.Elevation; +using ScoutBase.Propagation; +using AirScout.Aircrafts; + +namespace AirScout +{ + public partial class CleanupDlg : Form + { + MapDlg ParentDlg; + + bool Success = false; + bool ButtonPressed = false; + + public CleanupDlg(MapDlg parentDlg) + { + ParentDlg = parentDlg; + InitializeComponent(); + cb_LogFiles.Text = cb_LogFiles.Text + " [" + ParentDlg.LogDirectory + "]"; + cb_ElevationFiles.Text = cb_ElevationFiles.Text + " [" + ParentDlg.ElevationDirectory + "]"; + cb_TmpFiles.Text = cb_TmpFiles.Text + " [" + ParentDlg.TmpDirectory + "]"; + this.DialogResult = DialogResult.Abort; + } + + private void CleanupDlg_FormClosing(object sender, FormClosingEventArgs e) + { + // chek if user pressed a button or closed the form other way + if (!ButtonPressed) + DialogResult = DialogResult.Abort; + } + + private void Cleanup() + { + // do cleanup here + Success = true; + if (cb_UserSettings.Checked) + { + // reset all properties + Properties.Settings.Default.Reset(); + AirScout.PlaneFeeds.Properties.Settings.Default.Reset(); + AirScout.Aircrafts.Properties.Settings.Default.Reset(); + ScoutBase.Elevation.Properties.Settings.Default.Reset(); + ScoutBase.Stations.Properties.Settings.Default.Reset(); + } + if (cb_AirScout_Database.Checked) + DeleteDirectory(AircraftData.Database.DefaultDatabaseDirectory()); + if (cb_ScoutBase_Database.Checked) + { + DeleteDirectory(StationData.Database.DefaultDatabaseDirectory()); + DeleteDirectory(ElevationData.Database.DefaultDatabaseDirectory()); + DeleteDirectory(PropagationData.Database.DefaultDatabaseDirectory()); + } + if (cb_LogFiles.Checked) + DeleteDirectory(ParentDlg.LogDirectory); + if (cb_ElevationFiles.Checked) + DeleteDirectory(ParentDlg.ElevationDirectory); + if (cb_TmpFiles.Checked) + DeleteDirectory(ParentDlg.TmpDirectory); + } + + private void DeleteDirectory(string target_dir) + { + // deletes directory and all its subdirectory recursively + try + { + if (!Directory.Exists(target_dir)) + return; + string[] files = Directory.GetFiles(target_dir); + string[] dirs = Directory.GetDirectories(target_dir); + + foreach (string file in files) + { + File.SetAttributes(file, FileAttributes.Normal); + File.Delete(file); + } + + foreach (string dir in dirs) + { + DeleteDirectory(dir); + } + + Directory.Delete(target_dir, true); + } + catch (Exception ex) + { + MessageBox.Show(ex.Message, target_dir); + Success = false; + } + + } + + private void btn_OK_Click(object sender, EventArgs e) + { + Cleanup(); + if (Success) + { + ButtonPressed = true; + this.DialogResult = DialogResult.OK; + this.Close(); + } + else + { + btn_OK.Text = "Retry"; + } + } + + private void btn_Cancel_Click(object sender, EventArgs e) + { + ButtonPressed = true; + this.DialogResult = DialogResult.Cancel; + this.Close(); + } + } +} diff --git a/AirScout/CleanupDlg.resx b/AirScout/CleanupDlg.resx new file mode 100644 index 0000000..0262d6e --- /dev/null +++ b/AirScout/CleanupDlg.resx @@ -0,0 +1,7330 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + AAABAAkAAAAAAAEAIAAoIAQAlgAAAICAAAABACAAKAgBAL4gBABgYAAAAQAgAKiUAADmKAUASEgAAAEA + IACIVAAAjr0FAEBAAAABACAAKEIAABYSBgAwMAAAAQAgAKglAAA+VAYAICAAAAEAIACoEAAA5nkGABgY + AAABACAAiAkAAI6KBgAQEAAAAQAgAGgEAAAWlAYAKAAAAAABAAAAAgAAAQAgAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRz + cgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBw + cABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJ + CQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9v + bwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFx + cQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGho + aAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNy + cgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1s + awBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFx + cABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBv + cABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNz + dABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNy + cwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1t + bABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxs + bQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9u + bgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBw + cABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4 + dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNy + cgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5t + bAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1t + bABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1s + bABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBw + bwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBw + cABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNz + cwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1t + bQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1s + bABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1t + bQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFw + cABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFx + cQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJx + cgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBw + cABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5u + bQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1t + bABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRz + cgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBw + cABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJ + CQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9v + bwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFx + cQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGho + aAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNy + cgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1s + awBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFx + cABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBv + cABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNz + dABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAAlIMAAJSDAACUg + wAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAAlIMAAJSDAACUg + wAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAAlIMAAJSDAACUg + wAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNy + cwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1t + bABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxs + bQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9u + bgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBw + cABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4 + dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNy + cgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5t + bAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1t + bABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1s + bABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBw + bwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBw + cABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNz + cwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1t + bQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1s + bABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1t + bQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFw + cABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFx + cQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJx + cgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBw + cABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5u + bQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1t + bABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRz + cgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBw + cABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJ + CQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9v + bwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFx + cQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGho + aAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNy + cgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1s + awBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFx + cABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBv + cABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNz + dABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNy + cwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1t + bABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxs + bQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9u + bgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBw + cABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4 + dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNy + cgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5t + bAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQAAABaAAAAlAAAALgAAAC/AAAAvgAAAL8AAAC+AAAApAAA + AIIAAABVAAAALQAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1t + bABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1s + bABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAGgAAAHIAAADHAAAA9gAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA9QAAANMAAACVAAAARgAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAD///8A////ACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBw + bwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBw + cABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAJAAAAdgAAAOMAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADIAAAAWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA////AP///wD///8AJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNz + cwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1t + bQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAyAAAAxQAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAACoAAAAGgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP///wD///8A////AP///wAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1s + bABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1t + bQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAABcAAAA7AAAAP8AAAD/AQMA/wUHEv8MDDD/ExVB/xUYUP8VGFD/EhQ//xET + Of8JCCn/BQcV/wUHBv8AAQD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAANEAAAAkAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////ACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFw + cABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFx + cQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAB4AAAA/wAAAP8EBgT/EhRO/x4dnf8lI8//Jh7r/ycg7f8oIfD/JyHv/ycg + 7f8nIOv/Jh7q/yUj1P8kIrf/HBqR/xUYVf8ICRj/AAEA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA0AAA + ABUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8AJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJx + cgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBw + cABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAACBAAAA/wECAP8SFEj/IyG4/ygh8v8mHvb/JRzu/yUe6f8lHej/JR3p/yUd + 6f8lHOr/Ixrs/yIa7v8hGPP/IRj4/yMa/P8kHPn/Ix7b/xoah/8KCyH/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAACjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5u + bQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1t + bABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAA + AAAAAAAAAAAAAAAAAAAAAACAAAAA/wcKEP8dHov/KCHw/yYd9P8lHej/JR3n/yUd5/8lHej/Ixvr/yEZ + 7f8gF+//Hxvo/yUi2v8tKsv/NDnA/zk+uP84Pbv/MzfB/ywn0/8oJOz/IR/d/xETYv8AAQD/AAAA/wAA + AP8AAAD/AAAA/gAAAEcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////ACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRz + cgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBw + cABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJ + CQAAAAAAAAAAAAAAAAAAAABrAAAA/w0PJf8kIrv/JyD3/yUd6v8lHef/JBzp/yIa7P8gGO7/Hxnq/yUi + 3P8wMMb/RUms/2Von/+Gi5z/qKml/7q7tP/Dxbz/w8S8/7q7s/+lpqP/g4ea/15ho/89QcP/HCF3/wAA + BP8AAAD/AAAA/wAAAP8AAADGAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP// + /wD///8AJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9v + bwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFx + cQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGho + aAAJCQkAAAAAAAAAAAAAAABbAAAA/A8SNP8mIdD/Jhz2/yMa6/8hGe3/Hxfu/yAd5P8qJ8//Oz+0/11g + nf+Bh5n/q6yn/9HSxv/s6+D////5//////////////////////////////////389//j49f/t7mw/3V6 + n/8qLkf/AAAA/wAAAP8AAAD/AAAA/wAAAEcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gACYe4AAmHuAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNy + cgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1s + awBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFx + cABoaGgACQkJAAAAAAAAAABFAAAA8g8TPf8kH97/IRj5/x4Z6v8kIdv/MTHE/0pOp/9wc5n/mZ2f/8bH + u//k5Nj//Pv1//////////////////////////////////////////////////////////////////// + ///t7ef/p6eo/0tLS/8NDQ3/AAAA/wAAAP8AAACmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBv + cABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNz + dABxcXAAaGhoAAkJCQAAAAAtAAAA5gkMNf8hINn/KSXd/zg7t/9YXZ7/gYaY/7Cxq//X2Mz/8vHo//// + /v////////////////////////////////////////////////////////////////////////////// + ///////////////////f3tr/goGA/xoaGf8AAAD/AAAA6gAAABYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wAlHt8AJR7fACUe3wAlHt8AJR7fACUe3wAlHt8AJR7fACUe + 3wAlHt8AJR7fACUe3wAlHt8AJR7fACUe3wB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNy + cwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1t + bABzc3QAcXFwAGhoaAAJCQkiAAAA1BcZH/9CSZH/aGqm/5CUm/+/wLX/4+PX//v69P////////////// + ///////////////////////////////////////////9/////f/+/PX/6+vp/9rf6v/S3PT/zdr9/8PO + +/++yPr/vsn6/8DL+//M2f3/4Ov//+/x8v+TkIb/GxoW/wAAAP8AAABTAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8AJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxs + bQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9u + bgBtbWwAc3N0AHFxcBRoaGhJS0tMz21tbf+ioaP/z87L/+zs4f////v///////////////////////// + ///////////////////////////+//j59f/a4vD/v8np/5im3f90i9v/V3Hd/z1a4f8wUeH/KU7p/yVN + 8f8cQu//GD3v/xk+7/8aQO//I0vw/y9V8f9aevr/l6vr/3BzdP8SEAf/AAAAfgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBw + cABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4 + dwBvbm4hbW1sWHNzdKCLjIvYrq6u/9jY2f/29vf///////////////////////////////////////// + ///////////+/+3y+v/F0vb/obHv/3CL6f9LaOf/MFPn/x1D5/8WPO//Fzzv/xk+8f8bQPL/HkLx/x9D + 8P8gQ+//IkXv/yJG8P8iRu//IkXw/yBE7/8eQe//GD3u/xxD9f8+XuP/Rk9w/wkHAIkJBwAACQcAAAkH + AAAJBwAACQcAAAkHAAAJBwAACQcAAAkHAAAJBwAACQcAAAkHAAAJBwAACQcAAAkHAAAJBwAACQcAAAkH + AAAJBwAACQcAAAkHAAAJBwAACQcAAAkHAAAJBwAACQcAAAkHAAAJBwAACQcAAAkHAAAJBwAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNy + cgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHACbWxsL25t + bGp4eHe0k5ST5Le3t//e3t//+Pj5///////////////////////////////////////////////9/+3y + 9//AzvT/kqjy/1597/89Xu7/Ikjs/xY87v8XPe7/GT/w/x5C8f8hRfH/I0fw/yNH8P8jRvD/I0fw/yNG + 7/8jR+//I0fv/yNH7/8jRu//I0bv/yNH7/8jR/D/I0fw/yNH7/8iRe//HkP3/yxP7f8/SnKbO13kACFE + 8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE + 8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1t + bABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxBm1tbT1tbW14fX19w5yb + nPDDw8T/5ubm//39/v/////////////////////////////////////////+////9v/d4u7/qrjs/2uG + 6/88Xez/IEbr/xY87v8YPe//G0Dv/yFE8P8jRu//I0fv/yNH8P8jR+//I0bv/yNG7/8jRvD/I0bw/yNH + 7/8jRvD/I0fv/yNG7/8jR+//I0fw/yNH8P8jR+//I0fw/yNH8P8jRu//I0fv/yNH7/8gRfT/LlHr8Dtd + 5DEhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE + 8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJh7gACYe + 4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gAHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBw + bwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcAttbWxGcHBwh4KCgsyjo6T4zc3P/+zs + 7f//////////////////////////////////////////////+//8+vD/2Nna/6Wv0/9geNj/MVPn/xo/ + 7P8YPe//HEHw/yFE8P8jR/D/I0fw/yNG7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH7/8jR/D/I0bw/yNH + 8P8jRu//I0bv/yNH8P8jR+//I0bv/yNH8P8jR/D/I0fv/yNH7/8jR+//I0bv/yNH8P8jR+//I0fv/yFG + 8f8hRfLNIUTyFSFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE + 8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE + 8gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wAmHt8AJh7fACYe3wAmHt8AJh7fACYe3wAmHt8AJh7fACYe3wAmHt8AJh7fACYe3wB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNz + cwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3ARbWxrTXFxcZaIiIjVqamq/NPT0//x8fL///////// + //////////////////////////////////////j/8/Lp/8rN1f+Un8r/XHLL/zFR2P8bQOf/GT/w/x5D + 8f8iRfD/I0fw/yNH8P8jR+//I0fw/yNH8P8jR+//I0bv/yNG7/8jR/D/I0bw/yNG7/8jRu//I0fw/yNG + 8P8jR+//I0fv/yNH8P8jR/D/I0fv/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fw/yNH7/8jR/D/I0jx/yNH + 8P8jR+//I0fv/yNH76IjR+8AJEfwACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8AJh7fACYe3wAmHt8AJh7fACYe3wAmHt8AJh7fACYe3wAmHt8AJh7fACYe3wAmHt8AdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1s + bABzc3MAcHBvAG1tbABzcnIAcHBwE2xsbVRzcnOdioqL3a+vsP/X19f/9vb2//////////////////// + ///////////////////////////2/+Xl4f+6v9H/gI7G/01mzv8pSdv/G0Dp/xk/8v8eQ/L/Ikbx/yNH + 8P8jR/D/I0fv/yNH8P8jR+//I0fv/yNH7/8jRu//I0fv/yNH7/8jRu//I0fv/yNG7/8jRu//I0bv/yNH + 7/8jR+//I0fw/yNH7/8jR/D/I0fv/yNH7/8jR/D/I0fv/yNH8P8jR+//I0bv/yNH8P8jSPD/I0fv/yRC + 7f8jR+//I0jw/yNH7/8jRu//I0fvciRH8AAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AB4W3wAeFt8AHhbfAB4W3wAeFt8AHhbfAB4W3wAeFt8AHhbfAB4W3wAeFt8AHhbfAHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFw + cABtbGwAc3NzAHBwbxJtbWxUc3JynIqKi9+xsbL/29vc//j4+f////////////////////////////// + ///////////8//399P/X2dv/prHV/2t+y/89WtT/IkTh/xk/7v8bQPL/IEXy/yJG8f8jR/D/I0bv/yNH + 7/8jR/D/I0fw/yNH8P8jR+//I0bv/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0fv/yNG7/8jR/D/I0fv/yNH + 7/8jR+//I0fv/yNH8P8jRvD/I0fv/yNH7/8jR/D/I0fw/yNG7/8jR+//I0fv/yNG7/8jSPD/JETu/yYq + 4/8mIOD/JS3k/yNF7/8jSfD/I0fv/yNH7/MkR/A7I0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8AUFDnAFBQ5wBQUOcAUFDnAFBQ5wBQUOcAUFDnAFBQ5wBQUOcAUFDnAFBQ + 5wB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJx + cgBxcHARbWxsVHNzc52JiorhsrGy/9zb3P/5+fn///////////////////////////////////////// + +v/19fH/x8ze/5Gf1f9ZcdH/MFDd/xxA5/8ZP/H/HUPy/yFF8f8jRvD/I0fw/yNH7/8jR+//I0bv/yNH + 8P8jR+//I0fw/yNH8P8jRvD/I0bv/yNH8P8jR/D/I0bv/yNH8P8jR/D/I0fw/yNH8P8jR+//I0fv/yNH + 7/8jR/D/I0fw/yNG7/8jR/D/I0bv/yNH7/8jR+//I0fv/yNG7/8jRu//I0fv/yNH7/8jR+//I0bv/yYp + 4/8mHN7/Jh7f/yYc3v8mJ+P/I0Ht/yNJ8P8jR+//I0fv0CNH7xMjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AMTM+gDEzPoAxMz6AMTM+gDEzPoAxMz6AMTM+gDEzPoAxMz6AMTM + +gDEzPoAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyBW5u + bUVycXKViYmJ4LKysv/c3Nz/+fn5//////////////////////////////////////////r/6+zw/7TB + 5f97jtr/RmPb/yVH5f8ZP+z/GkDy/x9E8v8iRvH/I0bv/yNH8P8jR+//I0fw/yNH7/8jR/D/I0fv/yNH + 7/8jRvD/I0bv/yNH8P8jR+//I0fv/yNH7/8jR+//I0bv/yNG7/8jR/D/I0fw/yNG7/8jR/D/I0fv/yNH + 7/8jR+//I0bv/yNG8P8kRu//I0fw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0nw/yU0 + 5/8mHN//Jh7f/yYf4P8lHuD/Jhzf/yYk4f8kQez/I0nw/yNG7/8jRu+aI0fwACNH8AAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A09L8ANPS/ADT0vwA09L8ANPS/ADT0vwA09L8ANPS + /ADT0vwA09L8AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vYH9/ + f8ypqKr/19fY//j4+f///////////////////////////////////////f76/9zi8P+ktOn/aIDh/zhY + 4v8eQ+j/GD7v/x1C8v8gRfH/Ikfw/yNH8P8jRu//I0fv/yNH7/8jR/D/I0bw/yNH7/8jR+//I0fv/yNH + 7/8jRu//I0bv/yNH8P8jR/D/I0fv/yNG7/8jR+//I0fw/yNG8P8jRu//I0bv/yNH7/8jR+//I0fw/yNG + 8P8jR+//I0fw/yNH8P8jRu//JEfw/yNH8P8jR/D/I0bv/yNH7/8jR+//I0bv/yNH7/8jR/D/I0jw/yRD + 7f8mIuD/Jh3f/yYf4P8mH+D/Jh/f/yYf4P8mHd//JiXh/yRB7P8jSPD/I0bv/yNH8FwjR/AAI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AM/P+wDPz/sAz8/7AM/P+wDPz/sAz8/7AM/P + +wDPz/sAz8/7AM/P+wB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQCc3Jyiays + rf/u7u/////////////////////////////////////+//n7/P/L1vL/kqbt/1Vz5v8tT+j/Gj/s/xg+ + 8P8eQ/H/IUXw/yNG7/8jR/D/I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNG7/8jRu//I0fw/yNH + 7/8jRu//I0bv/yNH7/8jR/D/I0fv/yNH8P8jR/D/I0fv/yNH8P8jR/D/I0fv/yNG7/8jRu//I0fw/yNG + 8P8jR+//I0fw/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fw/yNG7/8jR+//I0fv/yNH7/8jR/D/I0fw/yNJ + 8P8lNej/Jh3f/yYf4P8mH+D/Jh7g/yYe4P8mH9//Jh/g/yYd3/8mJOH/JELt/yNJ8P8jR/DjI0fwIiNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Az8/7AM/P+wDPz/sAz8/7AM/P + +wDPz/sAz8/7AM/P+wDPz/sAcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBw + bwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBw + bwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBw + bwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBw + bwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBw + bwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvPaOj + o////////////////////////////////v/x9fr/vszz/4OZ7f9HaOv/JEjs/xc97f8bQPH/IETx/yJG + 8P8jRu//I0bv/yNH7/8jRu//I0fw/yNH8P8jRu//I0fv/yNH7/8jR/D/I0fv/yNH7/8jRu//I0bv/yNH + 8P8jR+//I0bv/yNG7/8jR+//I0bv/yNH7/8jR+//I0fw/yNH7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNG + 7/8jRu//I0fv/yNH7/8jR+//I0bv/yNH8P8jR/D/I0bv/yNH8P8jRu//I0bv/yNH7/8jR+//I0bv/yNH + 8P8jR+//Jifj/yYd3/8mH+D/Jh7g/yYe4P8mH+D/Jh7f/yYf4P8mHuD/Jhzf/yUp4/8jR+//I0fv/yNH + 77AjRu8GI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM/P+gDPz/oAz8/6AM/P + +gDPz/oAz8/6AM/P+gDPz/oAz8/6AHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cDafn6D7/f3+////////////9/n6/7nG7P94kfD/PF7s/x5D7v8WPO7/HEDv/yFF7/8jR/D/I0bv/yNH + 8P8jR+//I0fw/yNG7/8jRu//I0fw/yNH8P8jR/D/I0bv/yNH7/8jRvD/I0fw/yNH7/8jR/D/I0bw/yNH + 8P8jR/D/I0fv/yNH7/8jR/D/I0fv/yNG7/8jR/D/I0fw/yNH7/8jR+//I0bv/yNH7/8jR/D/I0fw/yNG + 7/8jRu//I0bv/yNH7/8jRu//I0fv/yNG7/8jR/D/I0fv/yNG7/8jR/D/I0bw/yNG7/8jR+//I0fv/yNH + 8P8jSPD/JEHt/yYg4P8mHd//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mHN//JDTo/yNJ + 8P8jR+//I0fvTyNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Azs/6AM7P + +gDOz/oAzs/6AM7P+gDOz/oAzs/6AM7P+gBwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwjMLCw///////0d38/0Vn7f8aP+v/Fjzv/x1B7/8iRfD/I0bw/yNG7/8jR+//I0bv/yNH + 8P8jRu//I0bv/yNH8P8jR+//I0fw/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH + 8P8jR/D/I0bv/yNH7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH8P8jR+//I0fv/yNH7/8jR/D/I0fw/yNH + 7/8jRu//I0fv/yNH7/8jR/D/I0fv/yNG8P8jR/D/I0fv/yNH7/8jR/D/I0bv/yNH8P8jR/D/I0fv/yNH + 8P8jR/D/I0nx/yQ56v8mHd//Jh7f/yYf4P8mHuD/Jh/g/yYf4P8lHt//Jh7g/yYf4P8mH+D/Jh3f/yYk + 4f8kRe7/I0jw/yNH8GgjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM7P + +wDOz/sAzs/7AM7P+wDOz/sAzs/7AM7P+wDOz/sAJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNI8ABycnIAcnJyAHJycgBycnIAcnJyAHJy + cgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJy + cgBycnIAcnJyAHJychGCgHrLsbrW/zlf9/8VO+//Ikbw/yNH7/8jRu//I0fw/yNH8P8jRvD/I0bv/yNG + 7/8jR/D/I0bw/yNG7/8jR+//I0fv/yNH8P8jR/D/I0bv/yNG7/8jR/D/I0fw/yNH7/8jRu//I0fw/yNH + 7/8jR+//I0fw/yNG8P8jR+//I0fv/yNH7/8jR/D/I0bv/yNG8P8jR+//I0bv/yNG7/8jR+//I0fv/yNH + 7/8jR+//I0fw/yNH8P8jR+//I0fv/yNG7/8jRu//I0fv/yNH7/8jR/D/I0fw/yNG7/8jR/D/I0fv/yNH + 7/8jR+//I0fv/yNJ8P8lNOf/Jhzf/yYe3/8mH9//Jh/g/yYe4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mIOD/JD7s/yNJ8P8jRu9mI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8Az8/6AM/P+gDPz/oAz8/6AM/P+gDPz/oAz8/6ACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjSPAAI0bvACNJ8QAjSPAAcnJyAHJy + cgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJy + cgBycnIAcnJyAHJycgBycnIAeXdtI1RgmJcaQPX/JEfw/yNH7/8jRu//I0bw/yNH7/8jR+//I0fw/yNH + 8P8jRu//I0bw/yNH8P8jR+//I0fw/yNH7/8jR/D/I0fv/yNH7/8jR+//I0bw/yNH8P8jRu//I0fv/yNH + 7/8jR/D/I0fw/yNH8P8jR/D/I0bv/yNH7/8jR+//I0bv/yNH8P8jR/D/I0fw/yNG7/8jRu//I0fv/yNG + 7/8jR/D/I0fv/yNH7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH + 7/8jR+//I0fv/yNH8P8jSfD/JS/l/yYd3/8mHt//Jh7f/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7f/yYf + 4P8mH+D/Jhvf/yQ66v8jSvH/I0fvWyNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////ANPS/ADT0vwA09L8ANPS/ADT0vwA09L8ANPS/AAmIcAAJiHAACYhwAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0jwACNG7wAjSfEAI0jwACNI + 8AAjR/AAI0jwAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJy + cgBycnIAcnJyAHJycgBycnIAcnJyAHl3bQBUYJgAI0bwwSNG8P8jR/D/I0fw/yNH8P8jR+//I0fv/yNG + 7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fw/yNG7/8jR+//I0fv/yNG7/8jR/D/I0fv/yNH + 7/8jR+//I0fw/yNG7/8jRu//I0fv/yNG8P8jR/D/I0fv/yNH7/8jRu//I0fv/yNH8P8jR+//I0fw/yNH + 8P8jR+//I0fw/yNG7/8jRu//I0fv/yNH8P8jRvD/I0bv/yNH8P8jR+//I0fv/yNG8P8jR/D/I0fw/yNG + 7/8jRu//I0fv/yNH8P8jR/D/I0jw/yYr5P8mHd//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYd3/8kPOv/I0nx/yNH70kjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8AvsL5AL7C+QC+wvkAvsL5AL7C+QC+wvkAJiHAACYhwAAmIcAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNI8AAjRu8AI0nxACNI + 8AAjSPAAI0fwACNI8AAjSfAAI0XuACNI8ABycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJy + cgBycnIAcnJyAHJycgBycnIAcnJyAHJycgB5d20AI0fvACNH71MjR/D+I0fw/yNH8P8jR+//I0fw/yNH + 8P8jRu//I0fw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fw/yNH8P8jRu//I0fv/yNH + 7/8jR+//I0fw/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0fw/yNH7/8jR+//I0fv/yNG7/8jRvD/I0fv/yNH + 7/8jR+//I0fv/yNH7/8jR+//I0bv/yNG7/8jR+//I0bv/yNH7/8jR+//I0bv/yNG8P8jR/D/I0fv/yNG + 7/8jRu//I0bv/yNH7/8jR+//I0fw/yNH7/8mJuL/Jh3g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mIeD/JEHt/yNI8PEjRu8xI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AKGt9AChrfQAoa30AKGt9AChrfQAoa30ACYhwAAmIcAAJiHAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjSPAAI0bvACNJ + 8QAjSPAAI0jwACNH8AAjSPAAI0nwACNF7gAjSPAAI0rxACNJ8AAkR/AAJEfwACRH8AAkR/AAJEfwACRH + 8AAkR/AAJEfwACRH8AAkR/AAJEfwACRH8AAkR/AAJEfwACRH8AAkR/AHI0fwwSNH7/8jR+//I0fw/yNH + 8P8jR/D/I0fv/yNG7/8jRvD/I0bv/yNH7/8jR/D/I0fv/yNH7/8jR/D/I0fv/yNH7/8jR+//I0fv/yNH + 7/8jRu//I0bw/yNH8P8jRvD/I0fw/yNH8P8jR+//I0bv/yNG7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH + 7/8jR+//I0fv/yNG7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0bv/yNG7/8jR+//I0bv/yNG + 8P8jRu//I0bv/yNG7/8jRu//I0fv/yNH8P8kRO7/JiLh/yYd4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mHd//JiTh/yRF7v8jR/DYI0fwEiNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8Ac4ntAHOJ7QBzie0Ac4ntAHOJ7QAmIcAAJiHAACYh + wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0jwACNG + 7wAjSfEAI0jwACNI8AAjR/AAI0jwACNJ8AAjRe4AI0jwACNK8QAjSfAAI0fvACRH8AAkR/AAJEfwACRH + 8AAkR/AAJEfwACRH8AAkR/AAJEfwACRH8AAkR/AAJEfwACRH8AAkR/AAJEfwACNH8FMjR+/+I0fv/yNH + 8P8jR/D/I0fv/yNH7/8jR+//I0bv/yNG7/8jRu//I0fw/yNH7/8jR+//I0bv/yNH7/8jR/D/I0fw/yNH + 7/8jRu//I0bv/yNH8P8jR/D/I0bv/yNH8P8jRvD/I0fv/yNG7/8jR/D/I0fv/yNG8P8jRu//I0fw/yNH + 7/8jRu//I0bv/yNG8P8jR+//I0fv/yNG7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH8P8jRvD/I0fv/yNG + 7/8jRvD/I0fv/yNH8P8jRu//I0fv/yNH8P8jSPD/JEDs/yYg4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jhzf/yUp4/8jSfD/I0fwwCNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////ADU54wA1OeMANTnjADU54wA1OeMAJiHAACYh + wAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNI + 8AAjRu8AI0nxACNI8AAjSPAAI0fwACNI8AAjSfAAI0XuACNI8AAjSvEAI0nwACNH7wAjR/AAI0jwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AHI0fvwiNH + 7/8jR+//I0fw/yNH7/8jRu//I0fv/yNH7/8jRvD/I0fv/yNH7/8jR+//I0fv/yNH8P8jR/D/I0fw/yNH + 8P8jR+//I0bv/yNG7/8jR+//I0fv/yNH7/8jRvD/I0bv/yNH7/8jR/D/I0fw/yNG7/8jRu//I0fw/yNH + 8P8jRu//I0bv/yNG7/8jR/D/I0fv/yNH7/8jRu//I0bv/yNH8P8jR/D/I0fv/yNH8P8jR/D/I0fw/yNH + 7/8jRu//I0fw/yNH7/8jRu//I0fv/yNG7/8jRu//I0nw/yQ46f8mHd//Jh/g/yYe3/8mHuD/Jh7g/yYe + 4P8mH+D/Jh/g/yYe3/8mH+D/Jh7g/yYc3/8lL+X/I0nw/yNH8JAjR/AAI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AIxnfACMZ3wAjGd8AIxnfACYh + wAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjSPAAI0bvACNJ8QAjSPAAI0jwACNH8AAjSPAAI0nwACNF7gAjSPAAI0rxACNJ8AAjR+8AI0fwACNI + 8AAjSPAAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNG + 71IjR/D9I0fv/yNG7/8jR+//I0fw/yNH8P8jR/D/I0bv/yNH8P8jRvD/I0fv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNG7/8jR+//I0fw/yNH8P8jR+//I0fw/yNH + 8P8jR+//I0bv/yNH7/8jR+//I0fw/yNH8P8jRu//I0fv/yNG7/8jRu//I0fv/yNH7/8jR/D/I0bv/yNG + 7/8jRvD/I0fv/yNH7/8jRu//I0fv/yNH7/8jRu//I0bw/yNJ8P8lLuX/Jh3f/yYf4P8mH+D/Jh7g/yYe + 4P8mHuD/JR7f/yYf4P8mH+D/Jh/g/yYf4P8mHN//JTHm/yNJ8P8jR+9eI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ACUe3wAlHt8AJR7fACUe + 3wAmIcAAJiHAACYhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0jwACNG7wAjSfEAI0jwACNI8AAjR/AAI0jwACNJ8AAjRe4AI0jwACNK8QAjSfAAI0fvACNH + 8AAjSPAAI0jwACNF7gAjSO8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8FI0fvuCNH8P8jRvD/I0fv/yNH8P8jR/D/I0fv/yNH7/8jRvD/I0bw/yNG7/8jRu//I0fv/yNH + 7/8jRu//I0fw/yNH8P8jR/D/I0fw/yNG7/8jR+//I0fv/yNH8P8jR/D/I0fw/yNH8P8jR/D/I0fw/yNH + 8P8jRvD/I0bw/yNG8P8jR/D/I0fv/yNH8P8jR/D/I0bw/yNH8P8jR+//I0bw/yNG8P8jRvD/I0fw/yNH + 8P8jRvD/I0bw/yNH7/8kR/D/JEfw/yNG8P8jRu//I0fw/yNI8P8jRO7/JSPh/yYd3/8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yUe3/8mH9//Jh/g/yYe4P8mHuD/Jhzf/yUx5f8jSfD0I0bwMyNG8AAjRvAAI0bwACNG + 8AAjRvAAI0bwACNG8AAjRvAAI0bwACNG8AAjRvAAI0bwACNG8AAjRvAAI0bwACNG8AAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJh/gACYf + 4AAmH+AAJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNI8AAjRu8AI0nxACNI8AAjSPAAI0fwACNI8AAjSfAAI0XuACNI8AAjSvEAI0nwACNH + 7wAjR/AAI0jwACNI8AAjRe4AI0jvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNG70gjR/D8I0fw/yNG8P8jRu//I0bv/yNH8P8jRu//I0fv/yNH8P8jR/D/I0fw/yNH + 7/8jR+//I0bv/yNG8P8jRvD/I0bv/yNH8P8jRvD/I0fw/yNH7/8jR/D/I0bw/yRH8P8jRu//I0bv/yNH + 8P8jR+//I0bw/yNH7/8jR/D/I0bw/yNH8P8jR+//I0fw/yNH8P8jR/D/I0fv/yNH8P8jRvD/I0fw/yNH + 8P8jRvD/I0bw/yNH8P8jRu//I0bv/yNH8P8jRu//I0bv/yNG8P8jSfH/JTnp/yYd3/8mH+D/JR7f/yYf + 4P8lH9//JR7f/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh7f/yYd3/8lMeb/I0nwySNG7wcjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAABAAAAHAAAADsAAABJAAAAVgAAAGIAAABSAAAARQAAADQAAAAWAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ACYf + 4AAmH+AAJh/gACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjSPAAI0bvACNJ8QAjSPAAI0jwACNH8AAjSPAAI0nwACNF7gAjSPAAI0rxACNJ + 8AAjR+8AI0fwACNI8AAjSPAAI0XuACNI7wAlOOkAJD3rACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8DI0bvsyNH7/8jRvD/I0fv/yNH8P8jR/D/I0bw/yNH8P8jR/D/I0fw/yNH + 8P8jR/D/I0fw/yNH7/8jR/D/I0bw/yNH7/8jR+//I0fw/yNH8P8jRvD/I0bv/yNH8P8jRvD/I0fv/yNH + 7/8jR+//I0bw/yNH7/8jRu//I0fv/yNG7/8jRvD/I0bw/yNG7/8jR/D/I0fv/yNH8P8jR+//I0bv/yNH + 8P8jR/D/I0fv/yNH7/8jR+//JEfw/yNG8P8kR/D/I0fv/yNG7/8jR+//I0jw/yYq5P8mHd//Jh/g/yUe + 3/8mH+D/Jh/g/yYf4P8mH9//Jh7f/yYf3/8mHuD/Jh/g/yYf3/8mHd//JS3l/yNL8H0jRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAgAAADsAAACBAAAAvQAAAN4AAAD6AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD0AAAA2QAA + ALUAAABmAAAAHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wAlHt8AJR7fACUe3wAmIcAAJiHAACYhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0jwACNG7wAjSfEAI0jwACNI8AAjR/AAI0jwACNJ8AAjRe4AI0jwACNK + 8QAjSfAAI0fvACNH8AAjSPAAI0jwACNF7gAjSO8AJTjpACQ96wAkP+sAI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7zojRu/4I0fv/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fv/yNG + 7/8jRu//I0fv/yNH7/8jR/D/I0bw/yNG7/8jRu//I0fv/yNH8P8jR+//I0fv/yNG7/8jRu//I0fv/yNH + 8P8jR/D/I0fv/yNG8P8jR/D/I0fw/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0fv/yNG7/8jRu//I0bv/yNH + 7/8jR+//I0fv/yNH7/8jRu//I0fw/yRH7/8jR/D/JEfv/yNH7/8jRu//I0jw/yQ/7P8mIOD/Jh7g/yYe + 4P8mHuD/Jh7g/yYf4P8mHuD/Jh7f/yUd3/8lK+X/JS3l/yYf4P8mH+D/Jh7g/yYf4P8lLeVPJS3lACUt + 5QAlLeUAJS3lACUt5QAlLeUAJS3lACUt5QAlLeUAJS3lACUt5QAlLeUAJS3lACUt5QAlLeUAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAHAAAAXQAAAL4AAAD4AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAN8AAAB8AAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AB8V3wAfFd8AJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNI8AAjRu8AI0nxACNI8AAjSPAAI0fwACNI8AAjSfAAI0XuACNI + 8AAjSvEAI0nwACNH7wAjR/AAI0jwACNI8AAjRe4AI0jvACU46QAkPesAJD/rACNE7gAjR+8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0fwpiNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH + 8P8jR+//I0fw/yNH7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH7/8jR/D/I0fv/yNH8P8jRvD/I0bw/yNH + 7/8jRvD/I0fw/yNH7/8jR+//I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH7/8jR+//I0fw/yNH + 7/8jR/D/I0fv/yNH7/8jRu//I0bw/yNH8P8jRu//JEfw/yNH7/8jRu//I0bv/yNJ8f8lMOb/Jhzf/yYe + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYd3/8mIuD/JTXo/yU26f8mJeL/Jh7f/yYe3/8mH+DwJhzfLiYc + 3wAmHN8AJhzfACYc3wAmHN8AJhzfACYc3wAmHN8AJhzfACYc3wAmHN8AJhzfACYc3wAmHN8AJhzfAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAA3AAAAvwAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAM0AAABEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wBSY+cAUmPnACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjSPAAI0bvACNJ8QAjSPAAI0jwACNH8AAjSPAAI0nwACNF + 7gAjSPAAI0rxACNJ8AAjR+8AI0fwACNI8AAjSPAAI0XuACNI7wAlOOkAJD3rACQ/6wAjRO4AI0fvACNG + 7gAkRu4AI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8C4jRvDyI0fv/yNH7/8jR+//I0fv/yNH + 7/8jRu//I0fw/yNH8P8jR+//I0fv/yNH7/8jR/D/I0fw/yNH7/8jR+//I0bv/yNG7/8jR+//I0fw/yNG + 8P8jRu//I0fv/yNH8P8jRu//I0bv/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH7/8jRu//I0bv/yNG + 7/8jRvD/I0bw/yNH7/8jR/D/I0fw/yNH8P8jR/D/I0fv/yRH8P8jR+//I0bw/yNI8P8kRO7/JiLh/yYd + 3/8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHeD/Jinj/yU26f8mNun/Jink/yYd4P8mHuD/Jh/g0SYf + 4AgmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAB1AAAA8QAAAP8AAAD/AAAA/wABAP8BAgD/BQcJ/wwPG/8NEST/DREq/w0RJf8MEB3/BQYJ/wEC + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA9QAAAHIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AIaZ7wAmIcAAJiHAACYhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0jwACNG7wAjSfEAI0jwACNI8AAjR/AAI0jwACNJ + 8AAjRe4AI0jwACNK8QAjSfAAI0fvACNH8AAjSPAAI0jwACNF7gAjSO8AJTjpACQ96wAkP+sAI0TuACNH + 7wAjRu4AJEbuACNJ8QAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0bvjiNH7/8jR+//I0fw/yNH + 8P8jR+//I0fv/yNH8P8jR+//I0fv/yNH7/8jRu//I0bv/yNH7/8jR+//I0fw/yNH8P8jRu//I0bv/yNG + 8P8jRu//I0fv/yNH7/8jRu//I0bv/yNG7/8jR+//I0fv/yNH8P8jR+//I0bv/yNH8P8jRu//I0fw/yNH + 7/8jRu//I0fw/yNH8P8jRu//I0fw/yNH8P8jR+//I0bw/yNH8P8jR/D/I0fv/yNH7/8jSfD/JDbo/yYc + 3/8mHt//Jirk/yYi4f8mHt//Jh7g/yYe4P8mHuD/Jh7g/yUw5v8lNun/Jjbp/yUu5v8mHuD/Jh7g/yYf + 4JMmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + ABEAAACiAAAA/wAAAP8AAQD/BggL/w8SPv8aGnD/HyCo/yIdx/8mIdL/JiLX/ycj2v8mItf/JiLU/yId + xv8fIJr/Fxhk/wwOJ/8BAgD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAApQAAABUAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wDGyPkAJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNI8AAjRu8AI0nxACNI8AAjSPAAI0fwACNI + 8AAjSfAAI0XuACNI8AAjSvEAI0nwACNH7wAjR/AAI0jwACNI8AAjRe4AI0jvACU46QAkPesAJD/rACNE + 7gAjR+8AI0buACRG7gAjSfEAJDzqACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7xwjR+/iI0fv/yNH + 7/8jR+//I0fv/yNH7/8jRu//I0bv/yNH8P8jR/D/I0fw/yNH7/8jR+//I0bw/yNH8P8jR+//I0bv/yNH + 8P8jRu//I0fw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fw/yNH7/8jR/D/I0fw/yNH7/8jR/D/I0bv/yNH + 8P8jR/D/I0fv/yNH8P8jR/D/I0fv/yNG7/8jR/D/I0bv/yNH8P8jR/D/I0fv/yNG8P8jSPD/I0bv/yYl + 4v8mHN//JiLh/yU06f8mIuH/Jh7g/yYf4P8mH+D/Jh3g/yYj4f8lNen/JTXo/yU26P8lL+b/Jh7f/yYe + 3/YlH983JR/fACUf3wAlH98AJR/fACUf3wAlH98AJR/fACUf3wAlH98AJR/fACUf3wAlH98AJR/fACUf + 3wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AB0AAAC9AAAA/wABAP8MDiT/Ght1/yIfxf8nIuj/Jx/1/yYf8v8mHvD/JR3u/yUd7f8lHe3/JRzt/yUd + 7v8mHu//Jh7z/ycg9f8mIdz/HR6U/wsOK/8AAQD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADGAAAAIgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////ACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjSPAAI0bvACNJ8QAjSPAAI0jwACNH + 8AAjSPAAI0nwACNF7gAjSPAAI0rxACNJ8AAjR+8AI0fwACNI8AAjSPAAI0XuACNI7wAlOOkAJD3rACQ/ + 6wAjRO4AI0fvACNG7gAkRu4AI0nxACQ86gAkQe0AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvbSNG + 7/8jR+//I0jw/yNJ8f8jR+//I0fv/yNH7/8jR/D/I0fw/yNH8P8jRu//I0fv/yNG8P8jR/D/I0fv/yNG + 7/8jR/D/I0bv/yNH7/8jR/D/I0bv/yNH8P8jR/D/I0fw/yNH8P8jR/D/I0fw/yNH8P8jR+//I0bv/yNH + 7/8jR/D/I0fw/yNG7/8jRvD/I0fw/yNH7/8jR+//I0fw/yNH7/8jR/D/I0fw/yNH7/8jR/D/I0rx/yQ5 + 6f8mHd7/Jh/f/yUv5v8lNOj/JiHh/yYe3/8mH+D/Jh/g/yYd3/8mK+X/JTbp/yU16P8lNen/Jifj/yYd + 3/8mHuCnJR/fACUf3wAlH98AJR/fACUf3wAlH98AJR/fACUf3wAlH98AJR/fACUf3wAlH98AJR/fACUf + 3wAmH+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AB4AAADMAAAA/wgLFP8bHHv/JiPa/ycf9f8lHvD/JR3r/yUd6P8lHej/JR3n/yUd5/8lHej/JR3n/yUd + 6P8lHef/JR3n/yUd5/8lHuj/JR3s/yce9f8mIt7/Ghtz/wQHBP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + ANEAAAAiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wAmIcAAJiHAACYhwAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0jwACNG7wAjSfEAI0jwACNI + 8AAjR/AAI0jwACNJ8AAjRe4AI0jwACNK8QAjSfAAI0fvACNH8AAjSPAAI0jwACNF7gAjSO8AJTjpACQ9 + 6wAkP+sAI0TuACNH7wAjRu4AJEbuACNJ8QAkPOoAJEHtACRE7gAjRu8AI0fvACNH7wAjR+8AI0fvACNH + 7wcjRu/AI0jv/yRA7f8kOur/I0fv/yNI8P8jR/D/I0fw/yNH8P8jR+//I0fv/yNH7/8jR/D/I0fw/yNH + 8P8jR/D/JEbv/yNG7/8kRu//I0fv/yNH8P8jR/D/I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH + 8P8jR+//I0fv/yNH7/8jR+//I0bv/yNH7/8jR/D/I0fv/yNH8P8jR+//I0fv/yNH7/8jRvD/I0fv/yNI + 8P8lKeP/Jhvf/yYo4/8lN+r/JjLn/yYg4P8mHt//Jh/g/yYd4P8mIuD/JTPo/yU26P8lNun/Ji3m/yYe + 4P8mHuDzJh7gMSYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJh/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + ABwAAADMAAEA/xIVRP8lIsb/Jx/1/yUd7f8lHej/JR3o/yUd5/8lHej/JR3o/yUe5/8lHej/JR3n/yUe + 6P8lHef/JR3n/yUd5/8lHuj/JR3n/yUd6P8lHej/Jh7w/ycf+v8gIKr/CAkk/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAAuAAAAAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJiHAACYhwAAmIcAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNI8AAjRu8AI0nxACNI + 8AAjSPAAI0fwACNI8AAjSfAAI0XuACNI8AAjSvEAI0nwACNH7wAjR/AAI0jwACNI8AAjRe4AI0jvACU4 + 6QAkPesAJD/rACNE7gAjR+8AI0buACRG7gAjSfEAJDzqACRB7QAkRO4AI0bvACNJ8AAjR+8AI0fvACNH + 7wAjR+8AI0bvMiNK8fMlNOj/Jhvf/yUq4/8kQe3/I0nw/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fw/yNH + 7/8jR+//I0fw/yNH8P8jRu//I0bv/yNH7/8jR+//I0fw/yNH7/8jRu//I0fv/yNH8P8jRu//I0bw/yNH + 7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fw/yNH7/8jRu//I0fv/yNH8P8jRu//I0bw/yNJ + 8P8kPer/Jh3f/yYf4P8lM+j/JTfq/yYu5v8mH+D/Jh/g/yYe4P8mIOH/JjDn/yU16f8lNej/JTTo/yYi + 4f8mHuD/Jh7gkiYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmH+AAJh/gACYf4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + ABMAAADABAUA/xkbdf8nIun/Jh3x/yUd6P8lHef/JR3n/yUd6P8lHej/JR3n/yUe6P8lHef/JR3o/yUe + 6P8lHuj/JR7o/yUd6P8lHun/JR3s/yUc8P8mHvT/Jh/p/yQc3v8jHs3/Jia7/x4jbP8GCAD/AQIA/wAA + AP8AAAD/AAAA/wAAAPUAAAAlAAAAAAAAAAAjR/AAI0fwACNH8AAjR+8AI0fvACRH7wAkR+8AJEfvACRH + 7wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ACYhwAAmIcAAJiHAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjSPAAI0bvACNJ + 8QAjSPAAI0jwACNH8AAjSPAAI0nwACNF7gAjSPAAI0rxACNJ8AAjR+8AI0fwACNI8AAjSPAAI0XuACNI + 7wAlOOkAJD3rACQ/6wAjRO4AI0fvACNG7gAkRu4AI0nxACQ86gAkQe0AJETuACNG7wAjSfAAI0nxACNI + 8AAjR+8AI0fvACNG7wAjSfFhJTvq/yYe3/8mHN//JiHg/yU36f8jSPD/I0fv/yNH7/8jR/D/I0fw/yNG + 7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNG7/8jR+//I0fw/yNG8P8jR/D/I0fw/yNG7/8jRvD/I0bv/yNH + 8P8jR+//I0fv/yNH8P8jR/D/I0fv/yNG8P8jR+//I0fw/yNH8P8jRu//I0fv/yNH8P8jR/D/I0bv/yNI + 8P8jR+//JSfi/yYd3/8lLub/JTbp/yU26f8mKeP/Jh3g/yYd4P8mIeH/JjHn/yU26f8lNej/JTbp/yYr + 5f8mHt//Jh/g4yYf3xsmH98AJh/fACYf3wAmH98AJh/fACYf3wAmH98AJh/fACYf3wAmH98AJh/fACYe + 3wAmHt8AJh/gACYf4AAmH+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAYAAACpBAcJ/x4fkf8nH/b/JR3s/yUe5/8lHej/JR7n/yUd6P8lHuj/JR7o/yUd6P8lHuj/JR3n/yUd + 5/8lHun/JR3s/yUc8P8mHu7/JB7f/yQhyv8kJav/HRyS/yIpg/8hJXr/GBZ6/yAhgf8jJJD/ISKT/x4d + gf8WHnH/EiBO/woOGv8CBAm6AAAAACVL/gAjR+8AI0fwACNH8AAjR/AAI0fvACNH7wAkR+8AJEfvACRH + 7wAkR+8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wAmIcAAJiHAACYh + wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0jwACNG + 7wAjSfEAI0jwACNI8AAjR/AAI0jwACNJ8AAjRe4AI0jwBCNK8SEjSfBPI0fvYSNH8F4jSPBFI0jwKyNF + 7hEjSO8IJTjpACQ96wAkP+sAI0TuACNH7wAjRu4AJEbuACNJ8QAkPOoAJEHtACRE7gAjRu8AI0nwACNJ + 8QAjSPAAI0jxACNH7wAjRu8AI0nxACRA7GEmIOD1Jh7g/yYe4P8mHeD/JS7l/yNF7v8jSfH/I0fw/yNI + 8v8jR/H/I0fw/yNH7/8jRu//I0fv/yNH7/8jR+//I0fv/yNH8P8jRu//I0fv/yNH7/8jRu//I0bv/yNH + 8P8jR+//I0bv/yNH7/8jR/D/I0fw/yNH8P8jR/D/I0fw/yNH8P8jR/D/I0fw/yNH7/8jRvD/I0bv/yNG + 7/8jSvD/JDbo/yYd3/8lLeX/JTbp/yU16P8lNOj/JiLg/yYd4P8mHeD/Jibi/yY26f8lNen/JTbp/yYs + 5f8mHuD/Jh7g/yYf4GMmH98AJh/fACYf3wAmH98AJh/fACYf3wAmH98AJh/fACYf3wAmH98AJh/fACYf + 4AAmHt8AJh7fACYf4AAmH+AAJh/gACYf4AAmHt8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAB/BAcK/x8gm/8mH/f/JR3q/yUd6P8lHuj/JR3o/yUd6P8lHef/JR3o/yUe5/8lHej/JR3q/yUd + 7f8mHfH/JR3h/yQhzP8kJKv/IiOV/yIniv8fH5D/JSil/yMfvf8lH83/JyHb/ycf5/8mHun/Jh7p/yce + 6v8nHuz/Jybv/yZF8P8jReD/IEDN1yFC4GUlS/4SI0fvACNH8AAjR/AAI0fwACNH7wAjR+8AJEfvACRH + 7wAkR+8AJEfvAP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJiHAACYh + wAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNI + 8AAjRu8AI0nxACNI8AAjSPAAI0fwACNI8BAjSfBMI0XujyQ968slNOjzJS/l/yUr4/8kKuP/Iyjj/iMq + 4/YjLOXtJDLn1yU46bokPeucJD/rfSNE7mIjR+9FI0buJiRG7g4jSfEFJDzqACRB7QAkRO4AI0bvACNJ + 8AAjSfEAI0jwACNI8QAjQ+4AI0XuACNJ8QAkQOwAJh3fZSYe3/8mH+D/Jh7f/yYc3/8mJ+L/JD/t/yNI + 7f8iP9v/I0Xq/yRJ9P8jSfX/I0jx/yNH8P8jRvD/I0fw/yNH7/8jR/D/I0bv/yNH7/8jR+//I0fv/yNH + 7/8jR/D/I0bv/yNH8P8jR/D/I0fw/yNH8P8jR/D/I0fw/yNG7/8jR/D/I0fv/yNG8P8jR+//I0bv/yNG + 8P8jSfH/Iz/s/yYh4P8mLeX/JTbp/yU16f8lNun/Ji7m/yYe3/8mHuD/Jh3g/yYj4v8lNuj/JTTo/yYn + 4/8mHuD/Jh7g/yYe4LcmHuACJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJh7fACYe3wAmH+AAJh/gACYf4AAmH+AAJh7fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAABLBAYI+x8gnP8nIPb/JB3p/yUe6P8lHej/JR3n/yUd5/8lHef/JR3n/yUe6v8lHO7/JR3s/yQe + 2f8lJL7/IiGf/yMokv8jI5n/JSWz/yQeyv8mH9v/Jx/p/yYd5v8mHuT/Jh/j/yYf4v8mH+D/Jh7f/yUe + 3/8mH+D/Jh/g/yYc3/8lK+b/JEj0/yRJ9v8kSPP/I0fv2SNH72gjR/ACI0fwACNH8AAjR+8AI0fvACRH + 7wAkR+8AJEfvACRH7wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ACYh + wAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjSPAAI0bvACNJ8QAjSPADI0jwSCNH8J0kPuzjJTHm/yYl4f8mHuD/Ixfe/yAU3f8hGN3/JyDf/y0l + 4f8vJ+H/Lyfh/yUc3/8kGt//Jh/f/yYg4P8mIuH/JSjj/yUs5PYlMOfoJTbpyyQ86qwkQe2PJETuciNG + 71MjSfAzI0nxFiNI8AsjSPEBI0PuACNF7gAjSO8AI0rxACYd3wAmH+CDJh7g/yYf4P8mHt//Jh3g/yci + 5v8lNeb/GR6S/xYXgv8cKKn/IDrR/yNG7v8kSfb/I0n0/yNH8f8jRu//I0fv/yNH7/8jR+//I0fw/yNH + 7/8jR+//I0fv/yNH8P8jR/D/I0fw/yNH7/8jR/D/I0bv/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH + 7/8jSfD/I0Lt/yYm4/8lLuX/JTbp/yU16P8lNuj/JjTo/yYj4v8mHd//Jh/g/yYe4P8mH+D/JTDn/yYn + 4/8mHd//Jh7g/yYf4O4mHuApJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJh/gACYe3wAmHt8AJh/gACYf4AAmH+AAJh/gACYe3wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAbAwQA3R0dh/8oH/j/JR3o/yUe5/8kHef/JR7o/yUd6P8lHer/JRzu/yUe7P8kH9f/JCO3/yMm + nf8kJ5n/JCWq/yUgyf8mH9r/Jx/o/yYd5f8mHuL/Jh7h/yYf4P8mH+D/Jh7g/yYe4P8mHuD/Jh7g/yYf + 4P8mHt//Jh7f/yUf3/8mHuD/Jh7f/yQ96/8jSfD/I0bv/yNG7/8jR/D/I0fwuiNH8CkjR/AAI0fvACNH + 7wAkR+8AJEfvACRH7wAkR+8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wAmIcAAJiHAACYhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0jwACNG7wAjSfFQI0XvxiQ26f8lKOP/Jh7f/yYd3/8mHt//Jh/g/zc65P9UWun/bXjv/4GR + 8/+PnPb/laD4/5Sg9/9qdO7/Kifh/yMa3/8mHt//Jh7f/yYc3/8mHN//Jhzf/yYd3/8mH9//Jh/g/yUj + 4f8mKeP/JS7k+iUz5/AkOenXJD/suSND7pYjRe56I0jvWiNK8TgjSfEYJDbpCSYg4J0mHt/8Jxzi/yMb + zP8cFaH/JBzR/yQjz/8ZGo7/Ew9w/xUSef8YHY//Hi20/yI+2v8kSPL/JEn1/yNI8f8jR/D/I0fv/yNH + 7/8jR+//I0bv/yNH7/8jRu//I0bv/yNG7/8jR/D/I0fw/yNH7/8jR/D/I0fw/yNH8P8jR/D/I0fw/yNH + 8P8jSfH/I0Lt/yYk4f8mKOP/JTfp/yU16P8lNej/JTfp/yUo4/8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mHuBkJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJh/gACYf4AAmHt8AJh7fACYf4AAmH+AAJh/gACYf4AAmHt8AJh7fAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAohgbZf8oIPT/JRzp/yUe5/8lHej/JB7q/yYd8P8mHu//JB/Y/yQjtf8jJZ3/Iyab/yQl + sv8lIND/Jh/j/yYe6P8mH+P/Jh7h/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYc3/8lLuX/I0nw/yNH7/8jR/D/I0fw/yNH7/8jR+/iI0fwQiNH + 7wAjR+8AJEfvACRH7wAkR+8AJEfvAP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8AJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvASNI8EEjRu+yJDzq/yUm4v8mHN//Jh3f/yUe3/8mHt//Ixnf/zEz4v+Pn/b/orH6/3WA + 8P9YWuv/Y2/t/3aD8f+Ik/X/kaD2/3SE8P8wL+L/Ixvg/yYe4P8mHuD/Jh/g/yYf4P8mHuD/Jh/f/yYe + 4P8mHd//Jh3f/yYc3/8mHd//Jh7f/yYf4P8mIOD/JSbi/yUs5P8lMef7JDjp8iQ+69IjP+y4JDbp9CUy + 6v8hLcv/Fx+L/xcai/8cGpn/GBaE/xQQcv8UEHT/FA9y/xQPcf8VE3v/GiKa/x82yf8jRuz/JEr3/yNI + 8/8jR/H/I0fw/yNH8P8jR+//I0fw/yNG7/8jR+//I0fv/yNH7/8jR/D/I0fw/yNH7/8jR/D/I0fw/yNI + 8P8jSfD/JD3r/yYj4v8mG9//JS3k/yU26f8lNen/JTfp/yYs5f8mHt//Jh7g/yYf4P8mHuD/Jh7g/yYf + 4P8mHt//Jh/g/yYf4P8mH+CiJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7fACYe3wAmH+AAJh/gACYf4AAmH+AAJh7fACYe3wAAAAAAAAAAAAAA + AAAAAAAAAAAATg8TOv4nI+b/JR3t/yUe6v8mHu//Jh/0/yUg4P8gH63/HyGD/yImif8jI63/JSHS/yYf + 5f8mHeb/Jh7i/yYf4f8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/f/yYf4P8mH+D/Jh/g/yYf4P8mHeD/JiLh/yRD7v8jSO//I0fv/yNH7/8jR/D/I0fw/yNG + 7/MjR+9FI0fvACRH7wAkR+8AJEfvACRH7wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////ACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0jwACNI8AAjSPAAI0jwACNI + 8AAjSPAAI0jwACNI8E4kPev/JSXi/yYd3/8lHd//Jh7f/yYf4P8mH+D/Jh7f/yUd3/8mIeD/UVfp/1lk + 6/8mIeD/HBPe/yAa3/8kHd//KCHg/y8w4v86PeT/LCnh/yUd4P8mH+D/Jh7f/yYf3/8mH+D/Jh7g/yYf + 4P8mHt//JR7f/yYf4P8mH+D/Jh/g/yYf4P8mH9//JR7f/yYd4P8mHd//Jhzf/yYd3/8mHuD/JiDg/yUp + 4/8fJ+P/Hynn/yAx6P8gNeH/IDXR/x81yP8dMb7/Gymm/xgfkP8WGIP/FBJ3/xINbv8TDXD/GBqK/x4u + uP8iQOD/JEfw/yRK9v8kSfb/JEn2/yRJ9v8kSfX/I0n0/yNI8v8jR/D/I0fw/yNH8P8jR/H/I0jx/yNJ + 8v8kQ+7/JTHn/yYf4P8mHd//Jh3f/yYv5v8lN+n/JTbp/yUs5f8mHuD/Jh7f/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYe4P8mH+DPJh/gEyYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe3wAmHt8AJh/gACYf4AAmH+AAJh/gACYe3wAmHt8AAAAAAAAA + AAAAAAAAAAAADgYID9EkIsX/Jh35/yYe9P8mIOj/IR+6/xoccP8XG0r/HB50/yUhv/8mHuT/Jh7o/yYf + 4v8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHt//Jh/g/yYe4P8mHt//Jh/g/yYf + 4P8mHuD/Jh/f/yYe3/8mHt//Jh7g/yYf4P8mHt//Jh/g/yYd3/8kPOv/I0nx/yNG7/8jR+//I0bw/yNH + 8P8jRvD/I0fw6CNH7yokR+8AJEfvACRH7wAkR+8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wAmIcAAJiHAACYhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkQu4AJELuACRC + 7gAkQu4AJELuACRC7gAkQu4mJiDg2yYb3v8mH+D/Jh/g/yYf3/8mH9//Jh/g/yYf4P8mH+D/Jh7g/yAX + 3/8gGN//JR7g/yYf4P8mHt//JR3f/yQd3/8kG+D/Ixnf/yUd3/8mH+D/Jh/g/yYe3/8mHt//Jh/g/yYf + 4P8mHuD/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/x8V + 3v9ESeb/bnTu/1db6v9BReb/MS3k/ygo5f8gJOj/ISfq/yEt6v8hNej/Ijfd/yA0y/8dL7j/HCei/xcb + if8VE3n/FhV9/xkdkP8aJ6j/Hi+5/x8yv/8fMsD/HzLA/yA2yf8iQOD/JEjy/yRK9/8kSvT/I0bt/yNB + 5/8kM+T/JiLh/ycc4v8nHuX/Jx7j/yYg4v8mNOj/JTLn/yYn4/8mHuD/Jh7g/yYe4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+DpJh/gLyYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHt8AJh7fACYf4AAmH+AAJh/gACYf4AAmHt8AJh7fACYf + 4QAAAAAAAAAAAAECAXgdHZP/KCH9/yIfx/8bG4D/ExdH/xMXPP8bG3r/JCDG/ygf7P8mHub/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh7g/yYe4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mHN7/JTLn/yNJ8f8jR+//I0fv/yNG + 8P8jR+//I0fv/yNH7/8jR+/GJEfvCyRH7wAkR+8AJEfvAP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8AJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAkQu4AJELuACRC7gAkQu4AJELuACYc3yomH9+cJh/g8iYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYe + 4P8mH+D/Jh/g/yUf3/8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yQc + 3/84OuT/bXru/52q+f+aqPj/l6T4/46b9v99ivL/bHLu/1NZ6P8+QOT/MCvi/yUh4/8gHuf/ICbp/yEu + 6v8iNun/Ijja/x82yf8dLbD/GiOY/xcahv8WFHr/FA9x/xMOb/8UDnD/FRR8/xkim/8aJ6j/Giaj/xke + k/8YGIv/GROO/xsVlf8aFpn/HRmq/yMbx/8mIt//Jizq/yYg4/8mHd//Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYe3/8mHuD2Jh/gTCYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACNH + 7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7fACYe3wAmH+AAJh/gACYf4AAmH+AAJh7fACYe + 3wAmH+EAAAAAAAAAABsMDyXoICKV/xcaWv8VGkD/GRtr/yIfq/8nIeD/KB/u/yYe5v8mH+D/Jh7f/yYe + 4P8mHuD/Jh7g/yYf4P8mHuD/Jh/f/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mHt//Jh/g/yYf + 4P8mHuD/Jh7g/yYf4P8mH+D/Jh/f/yYe3/8mHuD/Jh/g/yYf4P8mH+D/Jhzf/yUr5P8jSPD/I0fv/yNH + 7/8jR+//I0fv/yNH8P8jR/D/I0fv/yRH8HojRu8AI0bvAP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////ACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACRC7gAkQu4AJELuACRC7gAmHN8AJh/fACYf4DEmH+CQJh/g6SYe4P8mH+D/Jh7g/yYe + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yUf3/8mH+D/Jh/g/yYf + 4P8mHuD/Jh7g/yYe4P8mH+D/Jh7f/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yUd + 4P8jHt//cIDw/56q+f+UoPf/lKD3/5Wh9/+Wovf/mqX4/5yo+f+Zp/j/mKT4/4yZ9f94hfH/aG/t/05V + 5/85OOP/LSni/yIg4/8fIej/ISvr/yE17P8iPOr/IjvY/x83yv8dLrL/GiSc/xcZh/8VEXX/Ewxs/xMM + bP8TDnD/ExBz/xQRdP8UEnX/ExJ0/xQSdP8UE3j/GRaM/yAZsv8lHtj/JyDm/ycf5P8mH+H/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/gaCYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAjRu8AI0nwACNI + 8AAjR+8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe3wAmHt8AJh/gACYf4AAmH+AAJh/gACYe + 3wAmHt8AJh/hACYg3gAICiGOFhlL/xsce/8iIKv/JiDe/ykh7/8mHur/Jh7h/yYe4P8mH+D/Jh/g/yYe + 3/8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7f/yYf4P8mHuD/Jh/g/yYc3/8lJ+L/I0bu/yNH + 8P8jR/D/I0fv/yNH7/8jR+//I0fw/yNH8P8jR/DrI0bvHSNG7wD///8A////AP///wz///8b////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wAmIcAAJiHAACYhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACRC7gAkQu4AJhzfACYf3wAmH+AAJh/gACYf4CQmHuB/Jh/f2iYe + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYe3/8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf + 4P8mH+D/Hxfe/2Rq7f+eq/n/lKD3/5Sg9/+UoPf/laD3/5Sg9/+UoPf/laD3/5Wh9/+Xo/j/m6b4/5yo + +P+ap/j/lqL3/4WV9P91f/D/XWPq/0RH5f80L+L/JyTh/x8f5P8hJ+v/ITHt/yM87/8jQOf/Ij3X/x84 + x/8dLK//GSGW/xcZh/8WE3r/FBBz/xQQdP8UEXX/FBJ3/xMRdf8TEXP/FxSE/x4arv8kHtL/Jh/j/ycg + 5v8mH+L/Jh/g/yYe4P8mH+D/Jh7geSYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACNJ8QAjSPAAI0bvACNJ + 8AAjSPAAI0fvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHt8AJh7fACYf4AAmH+AAJh/gACYf + 4AAmHt8OJh7fNCYf4XAmIN6uJiTP/Scg5v8oIOz/Jx7q/yYe4v8mH+D/Jh/g/yYf4P8lH9//Jh/f/yYf + 4P8mH9//Jh7g/yYe4P8mHuD/Jh7f/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf + 4P8mHt//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh7g/yYf3/8mHeD/JiTh/yNE + 7v8jSPD/I0fw/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fw/yNH74YUOu4A////AP///wD///9l////xf// + /wT///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8AJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJELuACYc3wAmH98AJh/gACYf4AAmH+AAJh7gACUe + 3xUmH+BpJh/gyCYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/yYe4P8mHt//Jh/f/yYf4P8mHt//Jh7f/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf + 4P8mHt//Jh/g/yAX3/9NTuj/k6L3/5uo+P+apvn/mKT4/5ei9/+VoPf/lKD3/5Sg9/+UoPf/lKD3/5Sg + 9/+VoPf/laD3/5ah9/+YpPj/m6f5/5uo+P+apvj/j531/36L8v9qce3/TlTn/zo44/8qJuD/IB/i/yAk + 6P8gLu3/Ijnw/yJA8P8jQ+b/IT7W/x80wf8cKan/GB6S/xcXgv8VEXj/ExB0/xQQcf8TEG//FRJ7/xoX + l/8hG7r/JR7Y/ycf5f8mH+H/Jh/gjiYe4AAmH+AAJh/gACYf4AAjSPAAI0fwACNJ8QAjSfEAI0jwACNG + 7wAjSfAAI0jwACNH7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7fACYe3wAmH+ANJh/gMCYf + 4GImH+CeJh7gziYf4PomH+D/Jh7h/yYe5v8mH+H/Jh7f/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf + 4P8mH9//Jh/f/yYf4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe + 3/8lHt//Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yUf3/8mHuD/Jh7g/yYf4P8mHuD/Jh7f/yYi + 4f8jQ+7/I0jw/yNH7/8jR/D/I0fw/yNG7/8jR+//I0fw/yNH7/8jR+/oFDruHf///wD///8G////w9LS + 08qpqasCqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrAKmp + qwCpqasAqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmHN8AJh/fACYf4AAmH+AAJh/gACYe + 4AAlHt8AJh/gACYe4AcmHuBUJh/gtiYf4PcmH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mHuD/Jh/g/yYe + 4P8mH+D/Jh7g/yYf4P8mHuD/Jh7f/yYe3/8mHuD/Jh/f/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh7f/yYe4P8mH+D/Ixzf/zU25P9UXur/c37w/4WT9P+Rn/b/m6j4/5qn+P+ap/j/mqb4/5ik + +P+Wovf/laD3/5Wg9/+UoPf/lKD3/5Sg9/+UoPf/laD3/5ei9/+apvj/m6j4/5uo+P+Uovf/gpD0/3F5 + 7/9YX+n/QUDk/zMu4v8nJuH/HyHk/x8q6v8gNe//ITzx/yJE8f8iQ+L/IDzS/x4yu/8aJaD/GBuN/xYV + ff8TD27/ExBu/xUSe/8eF6n7JhzeiiYf4AAmHuAAI0fvACNJ8AAjSPAAI0jwACNH8AAjSfEAI0nxACNI + 8AAjRu8AI0nwACNI8AAjR+8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AUJh/gNyYe32ImHt+fJh7gzCYf + 4PcmH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yUf + 4P8lH+D/Jh7f/yYe3/8mHuD/JR7g/yYf4P8mHuD/Jh/g/yYe4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/JR/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8lH9//JR7f/yUe4P8mH+D/Jh/g/yYe + 4P8lI+H/I0Tu/yNI8P8jR/D/I0fw/yNH7/8jR+//I0fv/yNH8P8jR/D/HkPv/zFS8U7///8A////lu3t + 7f8tLS6zqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrAKmp + qwCpqasAqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrAKmpqwAmIcAAJiHAACYhwAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAACQAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmH+AAJh/gACYf + 4AAmHuAAJR7fACYf4AAmHuAAJh7gACYf4AAmH+A8Jh/gnyYf4O0mH+D/Jh7g/yYe4P8mHuD/Jh/g/yYf + 4P8mHt//JR7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/f/yUf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh7f/yUe3/8hGd//Hhbe/yAZ3v8qKeH/Ojnk/0hL5/9YZOv/a3Xu/3yG + 8f+Ek/P/j532/5ml+P+bp/j/mqf4/5qn+P+Yo/j/laH3/5Wg9/+Un/f/lJ/3/5Sg9/+UoPf/laH3/5mk + +P+bp/j/mqj4/5ml+P+Nm/X/f4zy/3J37/9aYen/RUfm/zY04/8mKuL/HiXn/yEy7v8kQPP/JEf0/yRI + 7f8iQt3/HznL/xwrrf8XH5L/GiGcjiYc3gAjS/EAI0fvACNH7wAjSfAAI0jwACNI8AAjR/AAI0nxACNJ + 8QAjSPAAI0bvACNJ8AAjSPAAI0fvACYf4AAmH+AAJh/gACYf4AEmH+BxJh7g1SYe4PomHuD/Jh7g/yYe + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYe3/8mHt//Jh7g/yYe4P8mH+D/Jh7g/yYe4P8mH9//Jh/g/yYf + 4P8mHt//Jh/g/yYe4P8lHuD/Jh/g/yYe4P8mH+D/Jh7g/yYe4P8mH9//Jh/g/yYf4P8lHuD/Jh7g/yYf + 4P8mHuD/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf3/8mHt//Jh7g/yUf3/8lHt//Jh/g/yYf + 4P8mHd//JSXi/yNF7v8jSPD/I0fw/yNG8P8jR+//I0bw/yNH8P8jR+//I0fw/xM57/qOoPda////lf3+ + /v9hYWL/AAAAnwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYhwAAmIcAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAADgAAACFAAAAuAAAAMgAAADDAAAAogAAAFQAAAALAAAAAAAAAAAAAAAAAAAAAAAA + AAAmH+AAJh7gACUe3wAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8sJh7fiyYf4N8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yUe3/8mHuD/Jh/g/yYf3/8mHt//JR/g/yUe4P8lH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh7g/yYf4P8mH+D/JR/f/yUf4P8lHt//Jh7f/yYe4P8lHt//JBrg/yEY3/8fF97/IBje/yAY + 3/8iHd//Kivg/zc24/9DQ+b/UFno/2Vv7f95gvH/iJf0/5el9/+bp/n/m6f4/5ij+P+UoPf/lKD3/5Sg + 9/+Un/f/lJ/3/5Wg9/+VoPf/l6L4/5ql+P+cqPj/mqf4/5qm+P+Qnvb/f4rx/2Np6/86OeP/Hxbe/yMf + 4P8lK+X/JTbs/yU/8v8kRvT/JEr1/yNM9dkjTvO1I0vxgiNH70QjR+8TI0nwACNI8AAjSPAAI0fwACNJ + 8QAjSfEAI0jwACNG7wAjSfAAI0jwACNH7wAmH+AAJh/gACYf4AAmH+ACJh/gwyYe4P8mHuD/Jh7f/yYe + 4P8mH+D/Jh7f/yYf4P8mH+D/Jh7g/yYf4P8mHt//Jh/f/yYf4P8mHuD/Jh/g/yYe3/8mH+D/Jh/g/yUe + 3/8mH+D/Jh7g/yYf4P8lH+D/JR/g/yYe4P8lHt//Jh/g/yYf4P8mHuD/Jh/f/yYf3/8mH+D/JR7g/yUe + 3/8mH+D/Jh/g/yYe3/8mHuD/Jh/f/yYf4P8mHt//JR/g/yYf4P8mHt//JR/g/yUe4P8mH+D/JR/g/yYf + 4P8mH+D/Jhzf/yUp4/8jR/D/I0fv/yNH8P8jRvD/I0fw/yNH8P8jR/D/I0fw/x9C7/8gR/D2xtT90/// + //9+f4D/AAAA/wAAAJMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAJQAAAKMBAQD3BQcL/wECAf8AAAD/AAAA/wAAAP8AAAD/AAAAxwAAAE4AAAAAAAAAAAAA + AAAAAAAAAAAAACYe4AAlHt8AJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYe3wAmH+AcJh/gbiYf + 4MwmH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH9//Jh/f/yUf3/8mHt//Jh7g/yYe4P8mH+D/Jh7f/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yUf3/8mH+D/Jh/g/yYf3/8lH9//Jh/g/yYf4P8mH+D/Jh/g/yYe + 3/8mHuD/JR3g/yQb3/8hGN//IBff/yAX3v8fF97/IRvf/y4t4f9AQeX/WGLr/3eB8f+Mm/X/mqf4/5uo + +P+apfj/lqL4/5Sg9/+VoPf/laD3/5Sf9/+UoPf/laD3/5Wg9/+VoPf/l6L4/5qm+P+cqfj/lKL3/2Js + 7f8tKuD/Ihbe/ycd3/8mHd//JiLh/yUo5P8lMef/JDnp/yRB7P8jR+7/I0nw1SNJ8KcjSPBlI0jwNSNH + 8AojSfEAI0nxACNI8AAjRu8AI0nwACNI8AAjR+8AI0zxACYf4AAmH+AAJh/gACYf4DkmH+DgJR7f/yUf + 3/8mH+H/Jh/i/yYf4f8mH+D/Jh/f/yYf4P8mH9//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe + 4P8mH+D/Jh/f/yYf4P8mHuD/Jh/f/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/JR/g/yYe + 3/8lH9//Jh/g/yYf4P8mH+D/Jh/f/yYe4P8mHt//Jh7f/yUf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYc3v8lMuf/I0nx/yNH8P8jR+//I0fv/yNH8P8jRu//I0fv/yFE7/8YQO//mK/5//// + //+hoaHhAAAA/AAAAP8AAACHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYh + wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAATgAAAOIICh7/ERJX/xUWdP8JCS//AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD7AAAAhgAA + AAYAAAAAAAAAAAAAAAAAAAAAJR7fACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmHt8AJh/gACYf + 4AAmH98NJh7gWSYf4LkmH+D6Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe + 3/8lH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/JR/f/yYe4P8mH+D/Jh/f/yYe + 4P8mH+D/Jh/g/yYf4P8mHt//Jh7g/yYe3/8mHuD/JR7g/yUd4P8jGt//IBfe/x8W3v8hHN//MTDj/0dM + 5/9lbuz/fovy/5Cf9v+ap/j/m6j4/5yo+P+apvj/mqX4/5qm+P+apfj/maT4/5mk+P+Xovj/lqH4/5ij + +P+dqfj/i5j1/zk75P8iGt//Jh7g/yYd3/8mHN//Jh3e/yYc3/8mIeD/JSbi/yUv5f8lOen/JEDs/yNG + 7/UjSfHLI0nxnyNJ8WAjSPA0I0bvCCNJ8AAjSPAAI0fvACNM8QAlPOkAJh/gACYf4AAmH+AAJh/gJyUf + 37YmH+D/Jh/h/yYf2v8mH9//Jx/n/yYf4/8mHt//Jh7f/yYe3/8mHuD/Jh7g/yYf3/8mH+D/Jh/g/yYf + 4P8mH+D/Jh7f/yYe3/8lHt//JR7f/yYe4P8mHuD/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh7g/yUe + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf3/8lH9//Jh7f/yYe4P8mH+D/Jh/f/yYf4P8mHt//Jh7f/yYf + 4P8mH+D/Jh/g/yYe3/8mHt//JD7s/yNI8P8jR/D/I0bv/yNG7/8jR+//I0fw/yJG7/8WO+7/fpn3//// + ///c3NveFhYW4QAAAP8AAAD/AAAAcgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYh + wAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAWgMEBfQPEUD/FRZ6/xYThP8VFHz/Bgkd/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAACbAAAABwAAAAAAAAAAAAAAAAAAAAAAAAAAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh7fACYf + 4AAmH+AAJh/fACYe4AAmH+ACJh/gPiYe4KEmH+DuJh7g/yYe4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Ixnf/x4T + 3v8aEN3/GA7d/x8c3v8vLuL/QUfm/1dh6v9rce7/c3zw/3SD8P9zf/D/eYbx/4KQ8/+DkvP/ipn1/42b + 9f+Qnfb/nKf5/5Sj9/85PeT/Ihnf/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYc3/8mHN//Jhzf/yYg + 4P8lJeH/JS7l/yQ46f8kQO3/I0bv9CNI8MojSfCQI0jwSSNH7wUjTPEAJTzpACQ/7AAmH+AAJh/gACYf + 4AAmHuAFJyDjZh0ZpNoWFIH/GBWI/x8ar/8lHtX/JyDl/ycg5v8nH+X/Jh7i/yYe4f8mH+D/Jh/g/yYf + 3/8mHt//Jh/g/yYf4P8mHuD/Jh/g/yUe3/8mHt//Jh/f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHt//JR/f/yUf + 4P8mHuD/JR7f/yYf4P8mG9//JSzk/yNJ8P8jR/D/I0bv/yNH7/8jRu//I0bv/yNH7/8WPO7/W3z0//f7 + //////71UFBR1gAAAP0AAAD/AAAA/wAAAGYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYh + wAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAWQMGDfUSFFj/FhWC/xUTff8VE3n/FhR+/w8PU/8MECb/AgQA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAJAAAAAAAAAAAAAAAAAAAAAAAAAAACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYe + 3wAmH+AAJh/gACYf3wAmHuAAJh/gACYf4AAmHuAAJh7gKyYe4IYlHt/cJh7f/yUe3/8mH9//Jh/f/yYf + 4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh7g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yUf4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/y4u + 4v85OuT/QUDm/0hH5/9OVun/U13q/1pk6/9aY+v/WGPq/11r6/9XZ+r/SlXo/0BG5f8wNOL/JSPg/y0q + 4f80MuP/NjPj/z465f82M+P/JB3g/yYf4P8mHuD/Jh/g/yYf4P8lHt//Jh7g/yYf4P8mHuD/Jh7f/yYe + 3/8mHeD/Jhzf/yYc3v8mHN//JiDg/yYl4v8lLeX/JDjp/yRB7P8jR++4I0zxJiU86QAkP+wAI0nwACNH + 8AAmH+AAJh7gACcg4wAdFaEZHxmrgRoWk+IUEnL/FhOA/xsYnP8gG7r/Ix3O/yYf2P8mH+H/Jx/k/ycf + 5P8mH+P/Jx/k/ycf5f8nH+X/Jx/m/ycf5/8mH+f/Jx/l/ycf5f8nHuT/Jh/i/yYf4f8mH+D/Jh7g/yYf + 4P8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/f/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh7f/yYf3/8mHN//JiLh/yRD7f8jSPD/I0bv/yNG8P8jRu//I0fw/yRH7/8YPe//Qmby/+nv + /v/////+o6Ok5AAAAPUAAAD/AAAA/wAAAP8AAABfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAmIcAAJiHAACYhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAOQUIE/ASFGT/FhSC/xUTe/8UEnj/FBJ1/yAbx/8oIPb/JyLb/yAfpf8RFT7/AgMA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAAYQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYf4AAmH+AAJh/gACYe + 3wAmHt8AJh/gACYf4AAmH98AJh7gACYf4AAmH+AAJh7gACYe4AAmHuAAJR7fGCYf4GomH+DIJh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH9//Jh7f/yYf + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Ixvf/yws + 4v+BkvP/lqX3/5Wj9/+ap/j/m6f4/5qn+P+apvj/mqb4/5qm+P+Zpfj/maT4/5ql+P+SoPf/g47z/2Ns + 7f8zNOP/GxLe/yEY3/8gGd//Ihnf/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 3/8mH9//Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHd//Jhzf/yYc3/8mIOD/JiXi/yU16N8lPOlAJD/sACNJ + 8AAjR/AAI0fwACNH8AAjSfEAHRWhAB8ZqwAnH+McIBiwmRQSdv8UEnT/FBJ1/xUUff8XFIX/FxWM/xgW + j/8bF5j/Gxia/x0Zof8dGaT/Hhqs/x8asf8fGrf/IBu//yIdxP8jHcn/JB7P/yUf1v8mH+D/JyDk/ycg + 5v8mH+H/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh7f/yYe3/8mH+D/Jh/g/yYf + 3/8mH+D/JR/f/yYf3/8mHuD/Jh7f/yM86/8jSfD/I0fv/yNH7/8jR/D/I0fw/yRH8P8dQO//LFLx/8/c + /f//////5ubn/R0dHvcAAAD/AAAA/wAAAP8AAAD/AAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAHgcJF9sUFW3/FROB/xUTe/8UEnf/FBJ1/x0ZrP8lHuv/JR3q/yUd7f8mHvX/JyLq/x4f + i/8HCRH/AAAA/wAAAP8AAAD/AAAA/wAAAOYAAAAhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh/gACYf + 4AAmHt8AJh7fACYf4AAmH+AAJh/fACYe4AAmH+AAJh/gACYe4AAmHuAAJh7gACUe3wAmH+AAJh/gCSYe + 304mH+CwJh/g+yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yYe + 3/8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/JR/f/yUf3/8mHt//Jh/f/yYe3/8mHuD/Jh/g/yYe + 4P8iHd//TVPo/4+d9f+eq/n/l6P4/5Sg9/+VoPf/laD3/5Sg9/+UoPf/lKD3/5Wg9/+UoPf/lqH3/5ik + +P+cqfn/kJ72/1BY6f8eGN//JR3f/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/f/yYf3/8mH+D/Jh7f/yYd3/8mHd//JiPh6SQ/ + 7HEjSfBhI0fwTiNH8EojR/A4I0nxMSNK9CMjR+4VJD/vCSQn0wMXE399FBF0/BQQdf8UEHP/FA9w/xMP + b/8SD27/Eg5t/xMPbf8TEG7/ExBv/xIQcP8TEHH/ExFy/xQSc/8VE3f/FRN7/xYUgP8XFIT/FxWJ/xsY + mf8gG7v/Jh/h/yYf4/8mH+D/Jh7g/yYf4P8mH+D/Jh7f/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHt//Jh7g/yYf4P8lHt//Jh3f/yU46P8jSfD/I0fw/yNG7/8jR+//I0fw/yNH8P8gRO//G0Pv/7HF + +////////////25ub/8AAAD/AAAA/wAAAP8AAAD/AAAA/AAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAABAYIE7ITFGz/FRKA/xUTe/8UEnX/FhR+/yAavf8mHuz/JR7p/yUe6P8lHuj/JR7n/yUd + 7P8nH/b/IyK3/wsOIf8AAAD/AAAA/wAAAP8AAAD/AAAAhgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAmH+AAJh7fACYe3wAmH+AAJh/gACYf3wAmHuAAJh/gACYf4AAmHuAAJh7gACYe4AAlHt8AJh/gACYf + 4AAmHt8AJh/gACYe4DcmHuCXJh/g6iYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/JB3g/yUd + 4P8lHuD/JR7g/yYe4P8mHuD/Jh7g/yYe4P8mHuD/Jh7g/yYe3/8mH9//Jh/g/yUf3/8lHt//Jh/g/yYe + 3/8mHuD/JR3g/x4V3v8uLOP/Y23t/5Cd9f+bp/j/lqH3/5Sg9/+UoPf/laD3/5Wg9/+VoPf/lKD3/5Wg + 9/+UoPf/lJ/3/5ij+P+Zpvj/U1vp/x4X3v8lHt//Jh/g/yYf4P8mH9//Jh/g/yYe4P8mHuD/Jh7g/yYe + 4P8mHuD/Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYb + 3/8lLOX/I0nw/yNH8P8jR/D+I0bw9CNG7/AjR/DoI0jw4CNJ8dwjSPDTIT/ezR80w/gfMbz/HzG+/x4t + tf8cLLH/HCqt/xkmpP8aJaT/GiGZ/xofl/8ZG4//GBmK/xcWg/8WE37/FRF3/xURdv8UEHP/ExBy/xQS + dv8UE3X/FBN0/xsXmf8mH9v/Jx/l/yYf4f8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf + 4P8mH+D/Jh/g/yUe3/8mHuD/Jh3f/yQz5/8jSfH/I0fw/yNH7/8jRu//I0bv/yNH7/8jRu//GD3u/42m + +P///////////9TU1f8JCQn/AAAA/wAAAP8AAAD/AAAA/wAAAPAAAAAyAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAQEAAAMFCHcPEF3/GBiE/xgZf/8UEnP/FxWI/yIc0P8mHu//JR7p/yUd6P8lHef/JR7n/yUd + 5/8lHuf/JR3o/yYe9v8kIsf/DA8h/wAAAP8AAAD/AAAA/wAAAOAAAAAaAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAI0bvACNH7wAjRu8AI0fvACNG8AAmH98AJh7gACYf4AAmH+AAJh7gACYe4AAmHuAAJR7fACYf + 4AAmH+AAJh7fACYf4AAmHuAAJh7gACYe4CMmHuB7Jh/g1iYf4P8mH+D/Jh7g/yYf4P8mH+D/JyDg/ycf + 4P8lHuD/Ixzg/yAb4f8lH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf + 3/8mH+D/Ihvh/yIc4P8mHuD/Kize/yEf3v8zM+L/aHTt/5ak+P+Wovf/lKD3/5Sg9/+Un/f/lJ/3/5Wg + 9/+UoPf/lKD3/5Wg9/+UoPf/l6P4/5ak9/9ESuf/HhXe/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf + 3/8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mHt//Jh/g/yYf4P8mH+D/Jh/g/yUf + 3/8mHN//JSXh/yNH7/8jR/D/I0fv/yNH7/8jR/D/I0fw/yNH8P8jRu//I0fv/yRJ9P8kSvX/JEr2/yRJ + 9f8kSvb/JEn1/yRJ9v8kSvf/JEr2/yRH8P8kRu//I0Tq/yND6P8iQeT/Ij/g/yE62f8hNcz/Hy69/xoh + m/8VEnf/FBF2/xUTev8UEnT/GBaN/yQdzf8nIOX/Jx/m/ycg5f8nIOb/Jx/n/ycf5v8mH+T/Jh/h/yYe + 4P8mHuD/Jh/g/yYf4P8mHt//Jhve/yUw5v8jSfD/I0fv/yNH7/8jR+//I0fv/yNH7/8kR+//GD3v/1R2 + 9P/7/v////////////9jY2T/AAAA/wAAAP8AAAD/AAAA/wAAAP8aGhrngoOEI4KDhACCg4QAgoOEAIKD + hACCg4QAgoOEAIKDhACCg4QAgoOEAIKDhACCg4QAgoOEAIKDhACCg4QAgoOEAIKDhACCg4QAgoOEAIKD + hACCg4QAgoOEAIKDhACCg4QAJiHAACYhwAAmIcAAJiHAACYgwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAEBAEALDEP0ISGN/zg/nv8XF3f/GRWR/yQd3f8lHu7/JR3o/yUd5/8lHuj/JR7o/yUd + 5/8lHej/JR3n/yUd6P8lHef/Jx72/yMjtf8GCRD/AAAA/wAAAP8AAAD/AAAASwAAAAAAAAAAAAAAAAAA + AAAAAAAAI0bvACNG7wAjR+8AI0bvACNH7wAjRvAAI0fwACNH7wAjR+8AI0fwACNG7wAjR+8AI0fvACNH + 7wAjRu8AJh/gACYe3wAjR+8AJh7gACYe4AAmHuAAJh7gACYf4AsmG99XJhresCYc3+8mHd//Ihjh/z9E + 2/+tr8j/p6nK/5iezP+TnM7/TlrZ/yEZ4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7f/yYe + 3/8lHt//Ihvh/3F61P+co8v/rq3I/7i+xv9VXNj/GxHg/yAZ3/9RWun/l6T3/5ei9/+UoPf/lKD3/5Sg + 9/+VoPf/lKD3/5Sg9/+VoPf/lKD3/5Sg9/+ZpPj/jJr1/zg65P8eFd7/Jh7f/yYf3/8mHuD/Jh/g/yYf + 4P8mHt//Jh7g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh7f/yYf4P8mHuD/Jh/g/yYf + 4P8mHd//JiDg/yQ76v8jSPD/I0fv/yNH7/8jR+//I0bv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH + 8P8jR+//I0bv/yNH8P8jR+//I0fv/yNH7/8jR/H/I0fw/yNH8f8jSPL/I0jz/yRI8/8kSfP/I0n1/yRK + 9v8kSfP/HzjL/xgai/8VEHX/FBN5/xMSdP8WFHz/Gxic/x4Zq/8eGan/Hhms/x8Ztv8hG7//JB3N/yUf + 3f8nH+T/Jx/l/yYf4f8mHt//Jh3f/yUu5v8kR/D/JEfw/yNG7/8jR/D/I0bv/yNH8P8jR+//Ikbv/xtA + 7v+6yvv////////////d3d7/DAwM/wAAAP8AAAD/AAAA/wAAAP8AAAD/R0dJ3uHh4RPh4eEA4eHhAOHh + 4QDh4eEA4eHhAOHh4QDh4eEA4eHhAOHh4QDh4eEA4eHhAOHh4QDh4eEA4eHhAOHh4QDh4eEA4eHhAOHh + 4QDh4eEA4eHhAOHh4QDh4eEA4eHhACYhwAAmIcAAJiHAACYhwAAmIMAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAABcFBh7WHh6C/15lwP8iI4L/FxOY/yUe4/8lHuz/JR7n/yUe6P8lHef/JR3o/yUe + 6P8lHuj/JR3o/yUd5/8lHef/JR3n/yUd6P8nIPj/Ghx6/wAAAP8AAAD/AAAA/wAAAHAAAAAAAAAAAAAA + AAAAAAAAI0bvACNG7wAjRu8AI0fvACNG7wAjR+8AI0bwACNH8AAjR+8AI0fvACNH8AAjRu8AI0fvACNH + 7wAjR+8AI0bvACNH8AAjR/AAI0fvACNG7wAjR+8AI0jwACNI8AAkPOsKJTPnISUv5l0kNejCJS3l/yMc + 4P8xLdz/vb7F/8zMw//Pz8L/qrDJ/zIy3v8iGuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 3/8mH9//JR7g/x4X4f+Kk8//09LB/8nJw//S0sH/fH7R/xwW4v8kHOD/Hxnf/1BZ6f+XpPf/mKP3/5Sg + 9/+UoPf/lKD3/5Sg9/+UoPf/lJ/3/5Sg9/+UoPf/lJ/3/5ql+P+Jl/T/PD/k/x0W3v8gF97/Ixrf/yQc + 3/8lHeD/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mHt//Jh/f/yYf4P8lHt//Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mHN//JSPh/yM+7P8jSfD/I0bv/yNH8P8jR/D/I0bv/yNH7/8jR/D/I0bv/yNH7/8jR/D/I0fv/yNH + 7/8jR+//I0fw/yNG7/8jR/D/I0fv/yNH7/8jR/D/I0fw/yNH7/8jRu//I0fw/yNH7/8kRu//JEfv/yNH + 7/8jR+//I0fw/yRM+/8kRev/Gx+Y/xcUgf8ZFo3/FxWC/xQSdv8UEnX/FBJ1/xQSdf8UEnP/FRN3/xYU + fv8XFYj/HBie/yIcv/8mHuD/Jh7j/yUz6P8jSPD/I0jw/yNG8P8jRu//I0fw/yNH7/8jR/D/I0fv/xw/ + 7v9CZfL/9fn/////////////eXl6/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/2BgYdY4ODkJODg5ADg4 + OQA4ODkAODg5ADg4OQA4ODkAODg5ADg4OQA4ODkAODg5ADg4OQA4ODkAODg5ADg4OQA4ODkAODg5ADg4 + OQA4ODkAODg5ADg4OQA4ODkAODg5ADg4OQAmIcAAJiHAACYhwAAmIcAAJiDAACYgwAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAABAgSXEBJg/2xxz/88P5n/ExCQ/yUe5f8lHuz/JR3n/yUd5/8lHej/JR3o/yUd + 6P8lHej/JR7o/yUe6P8lHej/JR3o/yUd6P8lHej/JR3v/yYi2f8JCyD/AAAA/wAAAP8AAACMAAAAAAAA + AAAAAAAAAAAAACNG7wAjRu8AI0bvACNH7wAjRu8AI0fvACNG8AAjR/AAI0fvACNH7wAjR/AAI0bvACNH + 7wAjR+8AI0fvACNG7wAjR/AAI0fwACNH7wsjRu8xI0fvYyNI8IsjSPC5I0jw5CNK8fQjSfH/I0nw/yNI + 8P8iPu3/JTPm/56lzP/KycP/zMvD/4GJ0P8aFeL/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8bFOL/X2PW/83Mwv/ExMT/zcvC/5Wezf8jIeH/JR3g/yUd4P8gGd7/UFnp/5Si + 9/+cp/j/mKP4/5ik+P+YpPj/maX4/5ml+P+Xo/j/lqH3/5Wg9/+UoPf/maT4/5Cd9v9kbO3/Q0bm/zQ3 + 4/8tK+H/JyHg/yEc3/8hGN//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYe4P8mH+D/Jh/g/yYe + 4P8mG9//JSrj/yND7f8jSfD/I0fv/yNH7/8jR/D/I0fw/yNG7/8jR/D/I0fw/yNH7/8jR/D/I0bw/yNH + 7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNH8P8jRvD/JEbw/yNH + 8P8jR/D/I0nz/yRK9f8iQN//HjC7/xoflf8WFID/FhR//xURev8UEHT/FA9y/xQPcv8UEHP/Ew90/xQQ + df8UEnb/FBJ3/xQSdf8UEnT/Hhqt/yU04f8jR+7/JEn0/yRK9v8jSPP/I0fw/yNH7/8jRu//I0fv/yNH + 7/8VOu7/epT2////////////7O3t/xwcHP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP+oqKnS7+/vB+/v + 7wDv7+8A7+/vAO/v7wDv7+8A7+/vAO/v7wDv7+8A7+/vAO/v7wDv7+8A7+/vAO/v7wDv7+8A7+/vAO/v + 7wDv7+8A7+/vAO/v7wDv7+8A7+/vAO/v7wDv7+8AJiHAACYhwAAmIcAAJiHAACYgwAAmIMAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAABLAwUv/F1hwv9tcsX/EhCH/yMc3v8lHuv/JR3n/yUd5/8lHen/JR3o/yUe + 6P8lHej/JR3n/yUd5/8lHej/JR3n/yUd6P8lHej/JR3o/yUd6P8nH/b/GBpt/wAAAP8AAAD/AAAAmAAA + AAAAAAAAAAAAACNH8AAjRu8AI0bvACNG7wAjR+8AI0bvACNH7wAjRvAAI0fwACNH7wAjR+8AI0fwACNG + 7wAjR+8AI0fvCCNH7yMjRu9YI0fwhCNH8LQjR+/iI0fv9yNG7/8jR+//I0fv/yNH8P8jR/D/I0bw/yNG + 7/8jR/D/Ikfw/xxE8v+EltX/zsvB/8fHxP+4vMb/ODrc/yEX4f8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yUe + 3/8mHuD/Jh/g/yUf3/8mH+D/Hxbg/0FD3P+9w8X/x8bD/8fHw/+1ucb/MS3e/yIb4f8mH+D/Jh3f/yEZ + 3/9AQ+X/cX3w/4GP8/+BjvL/gY7y/3aE8f92hfD/g5Dz/5Gd9v+Ypfj/mqX4/5Wg9/+Woff/mqb4/5Wj + 9/+OnPb/jJj1/4OP8/9seO//QUXm/yId3/8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe + 4P8mHd//JTDm/yNG7/8jSPD/I0fw/yNH7/8jRu//I0fv/yNH8P8jRu//I0bv/yNH8P8jR/D/I0fw/yNG + 7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNG7/8jR+//I0fw/yNH8P8jR/D/I0fv/yNH8P8jR/D/I0fw/yNG + 8P8jR/H/JEr2/yI+3P8bJ6X/FhV//xMOcf8TD3H/FBF2/xcXgf8aIJb/HCem/xwssf8bLLP/Gymq/xwk + n/8ZH5b/FxmH/xUTev8UEHT/FBF2/xUTev8WGIL/GR2P/xsnpf8eMb//Ij/b/yNH8P8kSvb/I0jz/yNH + 8P8iRu//GT/v/6zA+////////////52dnf8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8ODg7/0dHS0v// + /wf///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////ACYhwAAmIcAAJiHAACYhwAAmIMAAJiDAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAALAAAHyTA0if+WnPD/KyyR/x0Wz/8mHu7/JR7o/yUe6/8lHuv/JR3m/yYd + 6v8lHer/JR3o/yUd5/8lHef/JR3o/yUd6P8lHuj/JR7n/yUd5/8lHef/Jh70/yAesv8DBAL/AAAA/wAA + AKgAAAAAAAAAACNG7wAjR/AAI0bvACNG7wAjRu8AI0fvACNG7wAjR+8AI0bwACNH8AAjR+8AI0fvECNH + 8D8jRu9xI0fvoCNH79UjR+/1I0fv/yNH7/8jRu//I0fw/yNH8P8jR+//I0fw/yNG7/8jR+//I0fv/yNG + 7/8jR/D/I0bv/yNG7/8ZP/L/a4Dc/8zLwv/ExMT/zczD/32E0v8bF+L/Jhzf/yYe4P8mH+D/Jh/g/yYf + 4P8lHt//Jh7g/yYe4P8lH9//Jh/g/yQb3/8nJuD/rK/I/8nJw//FxcT/xsfE/0dN2/8gGOH/Jh7f/yYf + 4P8mHuD/Ihnf/yMd3/8pJOH/KSTg/ykk3/8mIOD/JiHg/yoj4f80NOP/TFLo/3R/8f+Wo/f/mKT4/5Sg + 9/+VoPf/lqL3/5ei9/+Yo/j/nKf4/5Sk9/9FSeb/Ihnf/yYe4P8mH+D/Jh7f/yYf4P8mHuD/Jh/g/yYd + 4P8mIOD/JDjo/yNJ8P8jR/D/I0bw/yNG8P8jRu//I0bv/yNH7/8jRu//I0fv/yNH7/8jR/D/I0fw/yNG + 7/8jRu//I0fw/yNG7/8jR/D/I0fw/yNH8P8jRu//I0bv/yNH8P8jR/D/I0fw/yNH7/8jRvD/I0fv/yNH + 7/8kSfX/I0bs/x0ss/8WFX7/Ew5x/xQPcf8WFoH/GyWj/x82x/8iQuT/I0bv/yRI8/8kSvf/JEr4/yRJ + 9f8kSPH/I0bv/yJE6f8hO9P/HCuv/xgcjf8VEnn/FA9y/xMPc/8TD3P/FBB1/xUUff8ZHpP/HS+5/yJB + 4P8kSfT/IUb0/yhN8f/e5v3///////7///9BQUL/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/LCws//n5 + +dP///8H////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wAmIcAAJiHAACYhwAAmIcAAJiDAACYg + wAAmIMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAagkMQP+AhOH/Zmu9/xYRq/8mHe7/JR3n/yYd7P8jHdr/HBmm/xoW + kv8cGKH/Ix3Z/yYe6/8kHef/JB3n/yUe6P8lHef/JR7o/yUe6P8lHef/JR3n/yUd7/8mI9H/Cg8Z/wAA + AP8AAACsAAAAACZM/QAjRu8AI0fwACNG7wAjRu8AI0bvACNH7wAjRu8EI0fvGyNG8E4jR/CDI0fvtiNG + 7+cjR/D+I0fv/yNG7/8jRu//I0fw/yNG7/8jR+//I0bv/yNH8P8jR/D/I0fw/yNG7/8jRu//I0fv/yNH + 8P8jRu//I0fv/yNH8P8jR+//HEHx/09v5P/Fx8T/xcTD/8jGw/+7wMf/OFLn/x8p5/8mIeD/Jh3f/yYe + 4P8mHt//Jh/g/yYf4P8lHt//Jh7g/yYf4P8mHuD/Hhfh/4ePz//My8L/xMTE/8vNw/9pbNT/HRTh/yYe + 4P8mH+D/Jh/g/yYe4P8lHd//JB3f/yUd4P8lHeD/JR3g/yUd4P8kHeD/Ixrf/x8X3v8hHN//Qkfm/4iV + 9P+ZpPj/lKD3/5Sg9/+UoPf/lJ/3/5Sg9/+erPn/X2Xs/x8W3/8mHuD/Jh/g/yYe3/8lHt//Jh7g/yYc + 3/8mJuL/JD/s/yNJ8P8jR/D/I0fv/yNH8P8jRu//I0fv/yNH7/8jR/D/I0bv/yNH7/8jR+//I0fw/yNG + 7/8jRu//I0fv/yNH8P8jRu//I0fv/yNH8P8jR+//I0bw/yNH7/8jRu//I0fv/yNH7/8jRu//I0bv/yNH + 8P8kSvb/IT3Y/xkekf8TD3D/FBFz/xcZif8eLrf/IkHg/yRJ9P8kSvb/I0jy/yNH8P8jR/D/I0fw/yNH + 7/8jR/D/I0fw/yRH8P8kSPH/I0n0/yRJ9f8jRez/ITvV/x0ttf8YHpH/FhR8/xQPc/8UEHT/FBB1/xQQ + dP8WFH//GyWj/xg01f9JbfT/9fn+///////T09T/CQkK/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/01O + T//////B////A////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJiHAACYhwAAmIcAAJiHAACYg + wAAmIMAAJiDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAEQAACdU4PJP/lZrt/yQkj/8fGNn/Jh7r/yUd6/8kHd7/GBaL/xMS + cv8UEnX/ExJx/xkWlP8kHeb/JR7o/yUd5/8lHuj/JR7o/yUd5/8lHef/JR3o/yUd6P8lHer/JyHq/wsL + Of8AAAD/AAAAnwAAAAAmTP0AI0bvACNH8AAjRu8FI0bvIyNG71UjR++OI0bvxCNH7/IjRvD/I0fv/yNH + 7/8jRu//I0bv/yNH8P8jRvD/I0fv/yNH8P8jRvD/I0fv/yRH8P8jRu//I0fv/yNH8P8jR+//I0fv/yNH + 7/8jR+//I0fw/yNH8P8jR/D/I0bv/x9E8P81V+r/vr/F/8bGw//ExMT/zcvB/36T2P8YQfL/I0Lt/yUz + 6P8mJOL/JRzf/yYd3v8mHt//Jh/g/yYf4P8mHuD/Jh/g/xoU4f9jZdX/zc3C/8TExP/OzcL/iI/P/x4b + 4f8lHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8lH9//JR3f/xwT + 3f89QeT/k6D3/5ei+P+Un/f/lKD3/5Wg9/+apvj/jZv2/zc45P8jG9//Jh7f/yYf4P8mH+D/Jh3f/yYd + 3/8lLeX/I0Xu/yNJ8P8jR/D/I0bw/yNH7/8jRu//JEbw/yNH8P8jR+//I0fw/yNH7/8jRu//I0bv/yNH + 7/8jRu//I0fw/yNH8P8jR+//I0fw/yNH7/8jR/D/I0fw/yNG7/8jR+//I0bv/yNH8P8jR/D/I0bv/yNH + 8P8kSvb/IDXI/xYVf/8TD2//FxOB/x8htf8jROj/JEr3/yNI8/8jR/D/I0fv/yNH8P8jRu//I0bv/yNH + 7/8jR+//I0fw/yNH7/8jR+//I0fv/yNG8P8jR/D/I0jx/yNJ9P8kSfb/JEfv/yE92P8dLbX/GR6T/xYU + ff8UEHX/FBF2/xQQdP8GBXX/bHO/////////////oqKi/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP+Ghob/////t////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ACYhwAAmIcAAJiHAACYh + wAAmIMAAJiDAACYgwAAlIMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF8FBzf/cHXU/1tftP8QDZT/Jh7s/yUd6P8mHu3/Gxik/xQT + c/8VE3v/FBN6/xQSef8UEnf/IRvH/yYe7P8lHef/JR3n/yUd5/8lHef/JR7o/yUe5/8lHej/JR3o/ycf + 9f8RD07/AAAA/wEDCYInTf8AJkz9HiNG71MjR/CNI0fvxCNH7/UjRu//I0fv/yNH8P8jR+//I0fw/yNH + 8P8jR/D/I0fv/yNG7/8jR/D/I0fw/yNH8P8jR+//I0fw/yNH7/8kRvD/I0bv/yNH7/8jR+//I0fv/yNH + 7/8jR/D/I0fv/yNH7/8jRvD/I0fw/yNG7/8hRO//KEvu/6myy//Jx8L/xMTE/8nIw/+5vcf/Mlbr/x5E + 8f8jSfD/JEXu/yQ36f8lJ+P/Jh7f/yYc3/8mHt//Jh/g/yYf4P8eFeH/RUfb/8DFxf/GxcT/ysnD/6Wt + yv8qJd//JBzg/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH9//Jh/g/yYe4P8mH9//JR7f/yYe + 4P8mHuD/HhXe/1Ze6v+bqPj/mKT4/5mk+P+cqPj/hZPz/z9D5f8hGd//Jh/g/yYf4P8mHt//JR3e/yYf + 4P8lNuj/I0jw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fw/yNH8P8jR/D/I0fv/yNH7/8jRu//I0bv/yNH + 7/8jR+//I0fv/yNH8P8jR/D/I0fw/yNH8P8jR+//I0bv/yNH8P8jR+//I0fw/yNH8P8jR+//I0fv/yNH + 8P8jSff/HjPB/xURd/8TEHD/GheT/yUd0v8nH+f/JTPp/yNI8P8jR/D/I0bv/yNH7/8jR/D/I0bv/yNH + 8P8jR/D/I0bv/yNH8P8jR+//I0fw/yNH7/8jRu//I0fv/yNH8P8jR/D/I0fw/yNH8f8jSPT/JEr2/yNH + 7/8iPtv/Hi+4/xcZhv8UEHT/BQNv/36Etv///////////3x9fv8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8BAQH/srKy/////7H///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wAmIcAAJiHAACYh + wAAmIcAAJiDAACYgwAAmIMAAJSDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIBAga5Ghxv/3+E3/8nKIX/GRSs/yYe7/8mHuz/IhzU/xUT + fP8VE3n/EhB5/xQSev8VE3r/FBJ2/yAavv8mHu7/JR3n/yUd5/8lHej/JR7n/yUe6P8lHuf/JR3o/yUd + 6P8nH/X/FhVQ/wIDAP0YK3+mJ03/pyNH7+8jR+//I0fw/yNH7/8jR/D/I0bv/yNH7/8jR/D/I0fv/yNH + 7/8jR+//I0fw/yNH8P8jR/D/I0bv/yNH7/8jR/D/I0fv/yNH7/8jR+//I0bw/yNH7/8jR+//I0bv/yNH + 7/8jRu//I0bv/yNH7/8jR+//I0fv/yNG7/8jR+//IkXw/x9G8f+MndP/z8vB/6u1y/+8v8f/zszB/3aL + 2f8YPvL/I0fv/yNH8P8jSfD/I0fv/yQ96/8lLOX/Jh/g/yYc3/8mHt//JBrg/ykp4P+ussj/ycjD/8XF + xP/BwsT/Ojvc/yIZ4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/f/yYf4P8mHuD/Jh/g/yYf + 3/8mH9//Jh/g/yQc3/8hHN//WGHq/4iS9P9+ivL/WmPr/ywr4f8gGN//Jh7g/yYe4P8mHuD/Jhzf/yUm + 4v8kP+z/I0nw/yNH7/8jRvD/I0fw/yNG7/8jR+//I0fw/yNH8P8jRu//I0bv/yNH7/8jR+//I0bv/yNH + 7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH8P8jR+//I0fw/yNG7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH + 8P8kSvb/HjPB/xURdv8UEXL/Gxif/yYf3/8nH+X/Jh7f/yYc3/8lNef/I0jw/yNH8P8jR+//I0fv/yNH + 7/8jR/D/I0fw/yNH8P8jR+//I0bv/yNH8P8jR+//I0bv/yNG7/8jR/D/I0fv/yNH8P8jR+//I0fw/yNH + 7/8kR/H/I0j0/yRL+P8jQ+b/HCqt/wcGc/98gbP//////+Dl9v8mKln/AAAC/wAAAP8AAAD/AAAA/wAA + AP8AAAD/FRUW/+Dg4f////+U////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJiHAACYh + wAAmIcAAJiHAACYgwAAmIMAAJiDAACUgwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1AwUh9jQ1mv9lasX/Dw1x/yAavv8mHu7/Jh7t/xwY + ov8TEnP/FhR7/y41jf8aG3//FBJ5/xUTfP8iHNH/Jh7s/yUe6P8lHuf/JR3n/yUd5/8lHef/JR7o/yUd + 6P8lHOj/Jhzt/yErmf8fO6n+Jkr2/yNH8f8jR+//I0fv/yNG7/8jR+//I0fw/yNH8P8jRu//I0fv/yNH + 7/8jR/D/I0fv/yNH7/8jRvD/I0bv/yNH7/8jRu//I0fv/yNH8P8jR/D/I0fv/yNH8P8jR+//I0fv/yNH + 7/8jR+//I0fv/yNG7/8jR/D/I0fv/yNH7/8jRvD/I0fw/yNH8P8ZQPL/dYnZ/9LOwP+lsM3/kaLS/9HN + wf+3vMf/MVXq/x5C8f8jR/D/I0fw/yNH7/8jSPH/I0jw/yRA7P8lMOX/JiLh/yUc3/8fF+H/jJPP/8vL + wv/FxcT/x8rE/1Vc2P8fFuH/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh7f/yYf3/8mH+D/JR3f/yEa3v8mH+D/JB7f/yAY3/8jGt//Jh7g/yYd3/8mHuD/JiTh/yU1 + 6P8jRe//I0nw/yNH8P8jR+//I0bv/yNH8P8jR/D/I0bv/yNG7/8jRu//I0bv/yNH8P8jR+//I0bv/yNG + 7/8jR+//I0fv/yNH7/8jRu//I0fw/yNH8P8jR/D/I0fw/yNH8P8jR/D/I0fw/yNH7/8jR/D/I0fv/yNH + 7/8kSfb/IDfL/xUSd/8TEHD/HBmh/ycg5P8mH+P/Jh7g/yYf4P8mHuD/Jh3f/yQ46f8jSPD/I0fw/yNH + 7/8jR/D/I0fv/yNH8P8jR+//I0fv/yNH7/8jR+//I0bv/yNH7/8jR+//I0fw/yNH8P8jRu//I0fv/yNH + 7/8jR+//I0fw/yNG7/8jR+//I0j0/yRK+P8QKsr/e4DJ/9vc2/82O1H/AAAK/wAAAf8AAAD/AAAA/wAA + AP8AAAD/AAAA/z09Pv/+/v7/////dv///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ACYh + wAAmIcAAJiHAACYhwAAmIMAAJiDAACYgwAAlIMAAJiHAADQzxwAfF7wAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQcJRP9GR63/Sk+r/w0Kb/8hG8T/Jh/x/yMd + 2P8WFH//FBJ4/xQRef8jJYT/HB+A/xMRdf8ZFZH/JR7o/yUe6f8lHej/JR7o/yQe6P8kHef/JR7o/yUd + 5/8lGuf/JSLo/yQ47P8lSfb/JEr7/yNH8f8jR+//I0fw/yNH8P8jR/D/I0fv/yNH8P8jR/D/I0bv/yNG + 7/8jR+//I0bw/yNH7/8jRu//I0fv/yNH7/8jRu//I0bv/yNH8P8jR+//I0fw/yNH7/8jR+//I0fv/yNG + 7/8jR+//I0fv/yNH7/8jR+//I0fw/yNG7/8jRu//I0fw/yNH8P8jR+//G0Dx/1p14f/LysL/trvI/2GA + 3//AwsX/z8zB/3eM2f8YP/L/I0bv/yNG7/8jR/D/I0bw/yNH7/8jSfD/I0nw/yRE7f8lNOj/Gxrj/2pw + 0//NzcL/xMTE/87Owv93etL/HBXh/yYe4P8mH+D/Jh7f/yYe4P8lH+D/JR7f/yYf4P8mHuD/Jh/g/yYf + 4P8mHt//Jh/g/yYf4P8mHt//Jh7g/yYd4P8mHd//JRzf/yUb3/8mHN//JiDg/yYk4v8lL+b/JDzr/yRF + 7v8jSfD/I0fw/yNH8P8jR+//I0bv/yNH7/8jR/D/I0fw/yNH8P8jRu//I0bw/yNH7/8jR+//I0bw/yNG + 7/8jR+//I0fv/yNH8P8jRu//I0bv/yNG7/8jR/D/I0fv/yNH8P8jR+//I0fw/yNH8P8jR+//I0fw/yNG + 7/8kSfX/Ij/c/xYVgP8TEHD/GheX/yYf4P8mH+P/Jh/g/yYf4P8mHuD/Jh/g/yYd3/8lJOL/I0Tv/yNH + 8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH + 7/8jRvD/I0fw/yNH8P8jSPD/I0jw/yNI8P8jSfD/G0T7/1l05v8xMi3/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP98fH3//////////1v///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wAmIcAAJiHAACYhwAAmIcAAJiDAACYgwAAmIMAAJSDAACYhwAA0M8cAHxe8AAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQMFB8AMDGH/TlK0/zs7nf8NC2//IRvD/yYf + 9f8fGsH/FBN0/xQTev8VE3r/EhB5/xMRev8UE3X/IBu//yUe7v8lHuj/JR3n/yUd6P8lHuj/JR3o/yUc + 5/8lHOf/JS3q/yRE7/8jSvH/I0fw/yNG7/8jR+//I0fv/yNG7/8jR/D/I0fw/yNH7/8jR+//I0fv/yNG + 7/8jRu//I0fw/yNG7/8jR/D/I0fw/yNH8P8jR/D/I0bw/yNH7/8jR/D/I0fw/yNH8P8jRu//I0fv/yNH + 8P8jR+//I0fv/yNH8P8jRu//I0bv/yNH7/8jR/D/I0fv/yNG7/8jR/D/I0fv/x9D8P8+YOj/w8TE/8rI + w/9TcOP/iprU/9PPwP+2vMj/LlPs/x5C8f8jRu//I0fw/yNG8P8jR/D/I0fv/yFF8P8iSPH/I0rx/xs9 + 7/9JX+P/w8XF/8bFxP/MysL/lJ3N/yMf4P8lGt//Jh3f/yYd3/8mHN7/Jhzf/yYc3/8mHd//Jh3f/yYd + 3/8mHd//Jhzf/yUc3/8lHN7/Jh3e/yYg4P8mIuH/JSTi/yUq4/8lMeb/JTjq/yRA7P8jRu//I0nx/yNJ + 8f8jSPD/I0bv/yNH8P8jR/D/I0fw/yNH7/8jR+//I0fw/yNH7/8jR+//I0bv/yNG8P8jR/D/I0bv/yNG + 7/8jR/D/I0bv/yNG7/8jR/D/I0bv/yNH7/8jR/D/I0bw/yNH7/8jR+//I0bv/yNH7/8jR+//I0fv/yNG + 7/8jSPL/JEfu/xkelP8SD27/GBaJ/yYf2f8mH+T/Jh/f/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh3f/yQ9 + 6/8jSPD/I0fw/yNH8P8jR/D/I0bv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNG8P8jRu//I0fv/yNH + 7/8jR+//I0nw/yNJ8f8jR/D/I0Tt/yRB7f8kQO3/JD3r/yY99/8bJ3z/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/qamq//////T///8z////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8AJiHAACYhwAAmIcAAJiHAACYgwAAmIMAAJiDAACUgwAAmIcAANDPHAB8XvAAeF7wAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC4FBxj0EBBy/0JHqP8zM5X/Dw1x/yEc + x/8nHvX/HxnA/xQSdP8VE3r/FRN6/xUTe/8UEnb/GBWO/yUe5f8lHun/JR3n/yUd5/8lHuj/JRzn/yUb + 5/8lJOn/JDzt/yNK8P8jSPD/I0bv/yNH7/8jR/D/I0fv/yNH7/8jRu//I0fv/yNH8P8jR/D/I0fw/yNH + 8P8jR+//I0fv/yNH7/8jR+//I0bv/yNH7/8jR/D/I0fv/yNG7/8jRu//I0fv/yNH8P8jR/D/I0bv/yNG + 7/8jR/D/I0fw/yNH8P8jR/D/I0bv/yNG7/8jR/D/I0fw/yNG7/8jRu//I0fv/yNG7/8gRPD/LE7t/7K4 + yf/SzcD/boXc/0do5f/JycP/z8zB/22F3P8XPvP/I0fw/yNH8P8jRu//I0fv/yNH7/8hSO//HkPv/yNH + 8P8gRfD/LFPt/7S5yP/Ix8P/x8bD/7S5yP8uR+n/Ijfr/yQ26P8lM+f/JTHm/yUu5f8lK+T/JSzk/yUs + 5P8lLOP/JSzk/yUv5f8lMeb/JDbo/yQ76v8kP+v/JELt/yNG7/8jSPD/I0nw/yNJ8P8jSfH/I0fv/yNH + 8P8jR/D/I0bv/yNH7/8jR+//I0fv/yNG7/8jR/D/I0fv/yNH7/8jR+//I0fv/yNG7/8jRu//I0fw/yNH + 7/8jR/D/I0fv/yNG7/8jR+//I0fw/yNH7/8jR+//I0bv/yNH8P8jR+//I0fv/yNH8P8jR/D/I0bw/yNG + 7/8jR/D/JEr2/x4vuP8TD3D/FRR7/yMdx/8nIOf/Jh7f/yYf4P8mH9//Jh7g/yYf4P8mH+D/Jh/g/yYc + 3/8kPOr/I0nw/yNH7/8jRvD/I0bv/yNH8P8jR/D/I0fv/yNG7/8jR+//I0fv/yNH8P8jR/D/I0bv/yNH + 8P8jSfD/I0fv/yQ/7P8lMuf/JSnj/yYl4f8mH+D/Jh7g/yYd3/8pIOv/Fhhn/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/CwsM/8zMzv/////T////Cf///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////ACYhwAAmIcAAJiHAACYhwAAmIMAAJiDAACYgwAAlIMAAJiHAADQzxwAfF7wAHhe8AB0W + uwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbCQoo/xEPff88Q6H/LS6R/xAO + cv8gG8L/Jh7w/yQd2P8WFH//FBJ1/xUTe/8UEnb/FRN3/yIcx/8mHu3/JR3n/yUd5/8lHej/JRvn/yUf + 6P8kM+v/I0Xv/yNJ8P8jR/D/JEfw/yRG8P8jR+//I0bw/yNH8P8jR+//I0fv/yNH8P8jRu//I0fv/yNH + 7/8jR+//I0fv/yNH7/8jRvD/I0fv/yNH8P8jR+//I0fv/yNH7/8jR+//I0bv/yNH8P8jR+//I0fw/yNG + 8P8jR+//I0fw/yNG7/8jR/D/I0fw/yNH7/8jR+//Ikbv/yNG7/8jR+//I0fw/yNH7/8jRu//IUXv/yFI + 8P+XptD/08/A/5ak0f8bQ/L/m6nQ/9LOwP+yucr/K1Du/x5D8f8jRu//I0fw/yNH8P8dQe7/kKn4/1l5 + 9P8bQO//Ikbv/x5D8f+WpdH/y8jC/8XExP/DxMT/RWno/x1E8v8jSfD/I0nx/yNJ8P8jSPD/I0jv/yNI + 8P8jSfD/I0jv/yNJ8P8jSfH/I0nx/yNJ8f8jSfD/I0nw/yNI8P8jR+//I0fw/yNH8P8jR/D/I0fw/yNH + 7/8jR+//I0fw/yNG7/8jR+//I0fv/yNG7/8jRvD/I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//JEfw/yNG + 7/8jR/D/I0fw/yNG7/8jR+//I0fv/yNH8P8jRu//I0bv/yNH7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNG + 8P8jR/D/JEn0/yI/3v8WFX//ExBx/x4Zq/8nH+f/Jh/g/yYf4P8mH+D/JR7g/yYf4P8mH+D/Jh/g/yYc + 3/8lJOL/I0Xv/yNH8P8jR+//I0fv/yNG7/8jRvD/I0bv/yNH8P8jR+//I0fw/yNH8P8jR+//I0fv/yNH + 8P8jSfH/JD7r/yUp4/8mHuD/Jhvf/yYc3/8mHuD/Jh7g/yYe4P8mHt//KSDv/xoYhv8AAQD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/x4eH//w8PD/////hf///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wAmIcAAJiHAACYhwAAmIcAAJiDAACYgwAAmIMAAJSDAACYhwAA0M8cAHxe8AB4X + vAAdFrsAHBa6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkw4ROP8TEH7/MjiY/yYo + jP8RDnH/HRir/yYe7/8mHu3/IBq//xUUf/8TEnP/FhR+/x8au/8lHu3/JR3o/yUc6v8lG+j/JR3n/yQs + 6v8kQe7/I0rw/yNI8P8jRu//I0fv/yNH8P8jR+//I0bv/yNH7/8jR/D/I0fv/yNG7/8jR+//I0bv/yNH + 7/8jR+//I0fv/yNG7/8jR+//I0bv/yNG7/8jR+//I0fw/yNH7/8jR+//I0bv/yNH7/8jR+//I0fw/yNH + 7/8jRu//I0fw/yNG8P8jRu//I0fw/yNH8P8jR+//Ikbv/xxC7/8iRu//I0fv/yNH7/8jR+//HEDv/xY8 + 7/8ON/H/d4rV/9PPwP+0ucj/Iknw/1Zy4v/MysL/zszB/22E3P8XPvP/I0fv/yNG8P8hRfD/HUPv/9zj + /f+zwfr/Fzzu/yNG7/8ZP/L/b4fb/83Kwf/FxMT/y8rC/2V93v8ZPvL/I0fv/yNH7/8jR/D/I0fw/yNH + 7/8jRu//I0fv/yNH7/8jR+//I0fw/yNG8P8jR+//I0fw/yNH7/8jRu//I0fv/yNH7/8jRu//I0fv/yNH + 8P8jR+//I0bv/yNH7/8jR+//I0fv/yNG7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH8P8jR+//I0fv/yNH + 8P8jRu//I0bv/yNH7/8jRu//I0fw/yNH7/8jR+//I0bv/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0fw/yNH + 7/8jR+//I0fw/yRJ9f8cJ6f/Ew5u/xgVi/8mH93/Jx/j/yYf4P8mHt//Jh7g/yYf4P8mHt//Jh/g/yYe + 3/8mHt//JD7r/yNI7/8jR/D/I0fv/yNH7/8jR+//I0bv/yNG7/8jR+//I0fv/yNH8P8jR+//I0fv/yNI + 8P8jSO//JTTn/yYe3/8mG9//Jh7g/yYe3/8lHt//Jh/g/yYf4P8mH+D/Jh/g/ycf6P8lIr//BgcM/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP89PT///v7+8f///yr///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8AJiHAACYhwAAmIcAAJiHAACYgwAAmIMAAJiDAACUgwAAmIcAANDPHAB8X + vAAeF7wAHRa7ABwWugAeF7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALYPEUL/ExF//yMk + if8iJIf/EhBz/xkWkf8mHuf/JR3p/yYe7f8jHdn/IBrH/yQc2f8lHe7/JRzt/yUb6P8mH9z/JSvk/yQ9 + 7v8jSfD/I0jw/yNG7/8jR+//I0fw/yNH7/8jR/D/I0fv/yRH7/8jR/D/I0fv/yNG7/8jR+//I0fw/yNH + 8P8jRvD/I0bv/yNG8P8jRu//I0bv/yNH8P8jRu//I0fw/yNG8P8jRu//I0fv/yNG7/8kR+//IETv/xo/ + 7/8hRO//I0bv/yNH8P8kR/D/I0bv/yNG7/8jR+//I0fv/xxB7/+Amvf/QmHx/x5C7/8jR/D/Gj/v/0dq + 8/+ouvr/sb/7/6e14v/Ix8H/wcPF/0Jj5/8cQ/H/pK7N/9HNwP+yucn/K1Dt/x5C8f8jR+//HkLv/y5V + 8P/q8v7/tcH6/xc87v8jRvD/GT/y/1Bt5P/Gx8T/xcTE/87Lwf+DldX/HELx/yNG7/8jR+//I0fw/yNH + 7/8jR+//I0fw/yNG7/8jR+//I0fv/yNH7/8jRu//I0fv/yNG8P8jR/D/I0bv/yNH7/8jR+//I0bv/yNG + 8P8jR+//I0bv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNG8P8jR+//I0fv/yNG7/8jRvD/I0bv/yNG + 7/8jRu//I0fw/yNH8P8jR+//I0fv/yNH8P8jRvD/I0bv/yNH8P8jR+//I0fw/yJF7/8jRu//I0bv/yNG + 7/8jR+//I0fv/yNJ9P8iPtv/FRR9/xQRdP8hHL7/JyDn/yYe3/8mHuD/Jh7f/yYf3/8lH9//Jh7f/yYe + 4P8mHN//JDbo/yNK8P8jR+//I0fw/yNH8P8jRu//I0fw/yNH8P8jR+//I0bv/yNG7/8jR+//I0fw/yNH + 8P8jSfD/JTLm/yYc3/8lHt//Jh/g/yUf3/8mHt//JR7g/yYe3/8mHuD/Jh/g/yYf4P8lH+D/KCHp/xET + Q/8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/jo6P/////5n///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////ACYhwAAmIcAAJiHAACYhwAAmIMAAJiDAACYgwAAlIMAAJiHAADQz + xwAfF7wAHhe8AB0WuwAcFroAHhe8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABUAAADWDxJM/xUT + gf8YF37/Fxh9/xQSeP8WFH7/IxzY/yUd7P8kHef/JR3s/yYd8v8lHPD/JRzj/yUhxv8lMbj/JUDQ/yRI + 7P8jSfH/I0bv/yNH8P8jR+//I0fw/yNH8P8jR+//I0fw/yNH7/8jR+//I0fv/yNH8P8jRvD/I0fw/yNG + 7/8jRu//I0bw/yNG8P8jR/D/I0bw/yNG8P8jR/D/I0fv/yNG8P8jR/D/I0bw/yNH7/8jR+//IETv/zZZ + 8f9VdPP/KE3w/xg97/8jRu//I0fw/yNH8P8jR+//I0fv/x1B7v86XvH/5u/+/0Vj8v8dQe//HUHv/zle + 8f/j7P3////////////z8/D/wsLD/8zKwf9ie97/Djb1/1x44P/OzMH/zszB/2eA3f8XPvL/JEfw/xxA + 7/89YfL/3un+/1p59P8bQO//I0fw/x9D8P8xVev/uLzH/8fGw//Jx8P/o6/N/yVJ7/8iRfD/I0bw/yFE + 7/8iRe//I0fw/yNH7/8jRu//I0bv/yNH8P8jRvD/I0fv/yNH7/8jR+//I0fw/yNH8P8jRu//I0fv/yNH + 8P8jR/D/I0fw/yNH7/8jR/D/I0fw/yNG8P8jR+//I0fw/yNG7/8jR/D/I0fw/yNG8P8jR/D/I0bv/yNG + 7/8jRu//I0fv/yNH8P8jR/D/I0fv/yNH8P8jR/D/I0fv/yNG8P8jRvD/I0fw/yNG7/8kSfD/I0fv/yNH + 7/8jR/D/I0bv/yNH8P8kSvX/HSuv/xMObv8ZFpD/Jh/h/yYe4f8mHt//Jh/g/yYf4P8lHt//Jh7f/yYf + 4P8mHN//Jivk/yNI8P8jR/D/I0bw/yNH7/8jRu//I0bv/yNH8P8jR/D/I0bw/yNH7/8jR/D/I0fw/yNH + 8P8jSfD/JTfo/yYc3/8mHuD/Jh/g/yYf4P8lH9//Jh7g/yUf4P8mH+D/Jh7g/yYf4P8mH+D/JR7f/ygg + 7f8fH5X/AQIA/wAAAP8AAAD/AAAA/wAAAP8AAAD/JCQl//Hx8e////8n////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wAmIcAAJiHAACYhwAAmIcAAJiDAACYgwAAmIMAAJSDAACYh + wAA0M8cAHxe8AB4XvAAdFrsAHBa6AB4XvAAhGr0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsAAAA8g8S + S/8WFIL/FBJ6/xQSev8UEnr/FBJ1/yEbzv8mHu//JRzt/yUb5/8mH9b/JSnD/yU2s/8kQsb/I0no/yNJ + 8/8jR/H/I0fv/yNH7/8jRvD/I0bv/yNG7/8jRvD/I0fv/yNH8P8jR/D/JEfw/yRH7/8jRvD/I0fv/yNH + 7/8jRvD/I0bv/yNH7/8jRvD/I0fw/yNH7/8jR/D/I0fw/yNH7/8jR+//I0bw/yNH7/8jR/D/I0fw/x9D + 7/84XvH/i6j4/6zC+v9ig/X/GT7v/yJF7/8jRu//I0fw/yNH8P8VO+7/coz2/8/c/f8nTPD/IEPv/xY8 + 7/+Pp/j////////////////////5/8jIxv/LyMH/gpbW/xg+8/8nTe7/rLTL/9HNwP+qtMv/JEnu/xM4 + 8P8XPO//QmXy/8LW/P8tUvH/H0Pv/yNH7/8iRvD/H0Tw/5yoz//LycP/xsbD/7y/xv80V+v/H0Pw/yNF + 8P8pTvD/IUfw/yNH8P8jR+//I0bw/yNH8P8jR+//I0bv/yNG8P8jRvD/I0bv/yNH7/8jR/D/I0fv/yNG + 7/8jR/D/I0fv/yNG7/8jR+//JEfw/x9C7/8aPu//I0fv/yNH7/8jR/D/I0fv/yRH8P8kR/D/I0fv/yNH + 7/8jR+//I0bv/yJG7/8aP+//Gz/v/yJG7/8jR/D/I0bv/yNH8P8jR/D/I0fw/yNG8P8dQO//T3P0/zpg + 8v8gRO//I0bw/yNG7/8jSPL/I0To/xcZhv8UEXT/IRy//ycf5v8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mHuD/JiDg/yNA7P8jSPD/I0fv/yNH7/8jR/D/I0bw/yNG7/8jR+//I0fw/yNH8P8jR/D/I0bv/yNG + 7/8jSfD/JDzr/yYf3/8lHd//Jh/g/yYf4P8lHt//Jh7f/yYf3/8mH9//JR/f/yYf4f8mH+L/Jh/i/yYf + 4P8mHuP/JyPZ/wsNI/8AAAD/AAAA/wAAAP8AAAD/AAAA/0lJSf////+F////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8AJiHAACYhwAAmIcAAJiHAACYgwAAmIMAAJiDAACUg + wAAmIcAANDPHAB8XvAAeF7wAHRa7ABwWugAeF7wAIRq9AB8YvQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOQAA + AP0PEUH/FhOB/xUTe/8UEnv/FBJ3/xYThP8jGt//Jh3i/yYkyv8lMsH/JUDK/yRH3f8jSfD/I0f1/yNH + 8f8jRvD/I0fv/yNH8P8jR/D/I0fw/yNG7/8jRu//I0bv/yNH7/8jR/D/I0fw/yRH7/8kRu//I0bv/yNH + 8P8jR+//I0fw/yNH8P8jR/D/I0fv/yNH8P8jR+//I0bv/yNG7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH + 8P8kR/D/H0Pv/xg97/85XfL/obr5/3WT9/8YPe7/I0bv/yNG7/8jRvD/Fz7v/5y0+f+Bmvf/Fzzu/yJF + 7/8fRe//yNj8/9Hb/P9ujfb/dpP3/7TF+f/Hys//ysfA/6Wuzf8hRvD/Fz3y/2R+3v/QzcH/ysnB/26H + 3/9Scff/Kk/w/zZb8f/J3Pz/Nlrx/x5C7/8jR/D/I0bw/xpA8v94jtn/zcvB/8XFxP/Gx8P/Um/j/xs/ + 8f8bP+//d5X3/0tu8/8cQO//I0fv/yNH8P8hRfD/GT7v/xY77/8YPe//Fjzu/xo/7v8jR+//I0fw/yNH + 7/8jR+//I0fw/yNH7/8jR+//I0fv/x9C7/8yVfD/fJv3/ylN8P8hRe//I0fw/yNG7/8hQ+//I0fw/yNH + 8P8jR/D/I0fv/x9D7/8dQ+//SWvz/0Nm8v8bQe//IkXw/yNH7/8jR/D/I0fv/yBD7/8iRu//FTvu/4GY + 9v9oiPb/Gz/v/yNG8P8jR/D/JEn1/yA2x/8TEHH/GRaO/yYf4P8mH+H/Jh/g/yYf4P8mHuD/Jh/g/yYf + 3/8mHuD/Jhvf/yUy5v8jSvD/I0fw/yNH8P8jR/D/I0fw/yNH8P8jRu//I0bv/yNH8P8jR+//I0bv/yNH + 7/8jSfD/I0Dr/yYg4P8mHd//Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH9//Jh/h/ycf5v8nIOL/Jh/b/yYf + 2v8mH+L/KB/r/ygj1/8MDiP/AAAA/wAAAP8AAAD/AAAA/wAAAP9TVVfe/f7/D/3+/wD9/v8A/f7/AP3+ + /wD9/v8A/f7/AP3+/wD9/v8A/f7/AP3+/wD9/v8A/f7/AP3+/wD9/v8A/f7/AP3+/wD9/v8A/f7/AP3+ + /wD9/v8A/f7/AP3+/wD9/v8A/f7/AP3+/wD9/v8A/f7/ACYhwAAmIcAAJiHAACYhwAAmIMAAJiDAACYg + wAAlIMAAJiHAADQzxwAfF7wAHhe8AB0WuwAcFroAHhe8ACEavQAfGL0AIBm9AAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AE0AAAD/DhA3/xYTgv8VE3v/FRN7/xQRdf8aF4L/Ji22/yU6vf8kRdT/I0nt/yNI9P8jR/P/I0bw/yNG + 7/8jR+//I0bv/yNG7/8jR/D/I0fv/yNH7/8jR/D/I0fv/yNH7/8jRu//I0bw/yNH8P8jR/D/I0bw/yNG + 7/8jR/D/I0fv/yNH8P8jR/D/I0fw/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH8P8jR+//I0fw/yNH + 8P8jR+//I0bv/yNH8P8jR/D/HD/v/xxB7/+Pq/j/WHn1/xk97/8jR+//Ikbv/x1B7/+zxvv/R2fy/xxB + 7v8gRO//MFHw/77N/P8yV/H/Eznu/xY77v8WP/H/kJ/U/9HNwP+3vMf/M1js/xs/8v8sUO3/s7rJ/8nH + wP/Iysz////8/93l/f9oivX/zdz8/0Vk8v8bQO//I0fv/yNH7/8ZPvL/V3Pi/8jIw//FxcT/zszC/3OG + 2/8YP/L/GD3v/46l+P+Pp/j/ETfu/xs/7/8gRO//LlPw/1h49P+Jo/f/oLn5/4yl+P9HaPL/Fj3v/x9C + 7/8kR/D/I0bv/yNH7/8iRu//Fjzv/xc87v8ZP+7/orj5/+Hs/v8mS/D/IETv/yFF7/8vU/D/P2by/x9D + 7/8jRu//I0fw/yBE7/8jSe//orj5//H4///z+v//jKX4/xpA7/8iRu//Ikbv/yZK8P9CaPP/I0jw/xU6 + 7v+BmPb/b472/xs/7/8jRvD/I0fw/yRI8/8cJqP/Ew9t/x8btP8nIOf/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh7g/yUh4P8kQu3/I0nx/yNH8P8jR/D/I0fw/yNG7/8jRu//I0fw/yNH7/8jR+//I0bv/yNH + 8P8jSvH/Iz7r/yYi4P8mHd//Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mHt//Jh/k/ycg4v8gG7n/GReR/xQQ + hf8KB4D/DAmK/xIOsP8PEGj/AAAA/wAAAP8AAAD/AAAA/wAAAP8BAQD/IiSE7FZQ8S5WUPEAVlDxAFZQ + 8QBWUPEAVlDxAFZQ8QBWUPEAVlDxAFZQ8QBWUPEAVlDxAFZQ8QBWUPEAVlDxAFZQ8QBWUPEAVlDxAFZQ + 8QBWUPEAVlDxAFZQ8QBWUPEAVlDxAFZQ8QBWUPEAVlDxAFZQ8QAmIcAAJiHAACYhwAAmIcAAJiDAACYg + wAAmIMAAJSDAACYhwAA0M8cAHxe8AB4XvAAdFrsAHBa6AB4XvAAhGr0AHxi9ACAZvQAmH8AAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAABTAAAA/wgIJv8WFID/FRN7/xMQd/8WFoD/Iju0/yRI3v8jSfT/I0f0/yNH8P8jR+//I0fv/yNG + 7/8jR+//I0fw/yNH7/8jRvD/I0fw/yNH8P8jR+//I0fw/yNH7/8jR+//I0fw/yNH8P8jR+//I0bw/yNH + 8P8jR/D/I0fw/yNG7/8jR/D/I0bw/yNH8P8jR+//I0fw/yNH8P8jR/D/I0fv/yNG7/8jR/D/I0fw/yNH + 8P8jRu//I0fv/yNH8P8jR/D/I0fw/yRH8P8eQe//Kk/w/4em+P8tUfD/IETv/x5B7/85W/L/ssf6/ydO + 8P8gRO//IETv/zpa8f+Emfb/Fjzv/yNH7/8jR+//GD7y/2+E2//PzcH/x8fE/05q5P8ZP/L/GT/x/2yE + 2//PzcH/yMbB/7fB5P/S3f7/8fn//+zy/v9ScPP/GT7v/yNH8P8jR+//H0Pw/zRZ6v+6vsb/xsbD/8zJ + wv+SotL/H0Xx/xo+7/9hgfT/xdX8/zdd8f9NcPP/XIL1/3mX9/9+nfj/hqH4/4qk+P+wwvr/3un9/5+1 + +f8jS/D/HEDv/yRH7/8iRe//H0bv/1159P9Rb/P/IEXv/+fu/v+Rqfj/GkDv/yJG7/8gQ+//NFjx/1qC + 9f8gRO//I0bw/yNH8P8aP+//Y4T0/561+f9EY/L/c471/+rx/v9NbvL/Gj/v/yJG7/8hRe//fJ74/zZZ + 8f8UOe7/bov1/2iJ9f8bP+//I0bv/yNH8v8jROr/FxqG/xYTfv8lH9b/Jh/j/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYc3/8lMOX/I0nw/yNH7/8jR+//I0fv/yNG7/8jRu//I0fv/yNH8P8jR+//I0fv/yNH + 8P8jSfH/Izvq/yYg4P8mHN//Jh7g/yYe4P8mH+D/Jh/g/yYf4P8lH9//Jh/j/yYf3v8bGJr/FBN0/wkG + b/8XGXr/Wl+d/3mCrv9iaKX/JClM/wAAF/8AAAb/AAAA/wAAAP8AAAD/ExRH/yYg4f8dFeGtJh7gACYe + 4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJiHAACYhwAAmIcAAJiHAACYg + wAAmIMAAJiDAACUgwAAmIcAANDPHAB8XvAAeF7wAHRa7ABwWugAeF7wAIRq9AB8YvQAgGb0AJh/AACYf + vgAjSPAAI0jwACNH7wAjRu8AI0bvACNH7wAjR+8AI0bvACNH8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAaQAAAP8DBA7/FBNt/xQQef8UEHT/HzPB/yRK+/8jR/T/I0fv/yNH7/8jRu//I0fv/yNH + 7/8jR+//I0bv/yNH8P8jR+//I0fw/yNH8P8jR+//I0bv/yNH7/8jR/D/I0fv/yNG7/8jR/D/I0fw/yNH + 7/8kR/D/I0fw/yNH8P8jR+//I0fw/yNG8P8jRu//I0fw/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNG + 8P8jR+//I0bv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0bw/xk87v9UdfT/XoH1/xo+7/8NNO7/fZj3/5ux + +f8YPe7/I0bv/yBE7/83V/D/mK74/x9G8P8iRu//I0fv/x1B8f9Uc+L/yMjD/8/Mwf9shdv/GT/y/x5D + 8f8tU+z/tbzI/9HNwP+Wo8z/KVHx/3ST+P/M3/3/dZP1/xg97v8jRu//I0fv/yJF8P8iR+//pK7N/8nH + w//IxsP/r7bJ/ylN7v8bP/D/SGvz/9rn/v9dhPX/P2Py/zJW8v8fRO//Gz/v/xg97/8XPO7/Fjzv/z9i + 8f/A0Pv/xdT7/ytR8P8cQO//GD3v/4Gc9////////////6q++v+yxfr/Jkzw/yBE7/8jR+//IETw/zJW + 8f9qkPb/Ikbv/yJG7/8jR+//IkXv/1h89P8iRu//GD3v/w007v9/mvf/jKf4/xg97v8iRu//GUDv/6a9 + +v9nhfX/CzLu/4Wd9/9ggfT/G0Dv/yNH8P8jSPT/ITrS/xMSc/8bF5r/Jx/l/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYe3/8mH9//JD7s/yNJ8P8jR+//I0fv/yNH7/8jR/D/I0fw/yNH7/8jR+//I0fw/yNI + 8P8jSPD/JTXo/yYe4P8mHt//Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/i/yYg4P8bF5j/FBJz/wYE + cv89QZP/xMjY//b38P/39/D/9vbx/9vd3/+OlLz/Jy5w/wAAFP8AAQD/EhRF/ych1f8nH+f/Jh/f+SYe + 4DYmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYhwAAmIcAAJiHAACYh + wAAmIMAAJiDAACYgwAAlIMAAJiHAADQzxwAfF7wAHhe8AB0WuwAcFroAHhe8ACEavQAfGL0AIBm9ACYf + wAAmH74AI0jwACNI8AAjR+8AI0bvACNG7wAjR+8AI0fvACNG7wAjR/AAI0fwACNG7wAjR+8AI0bvACNH + 8AAAAAAAAAAAAAAAAHQAAAD/DBEg/xkghv8WFoT/HjC8/yNJ9f8jR/D/I0bv/yNH7/8jR+//I0bv/yNG + 7/8jR+//I0fv/yNG7/8jR+//I0fw/yNG7/8jR+//I0fv/yNG7/8jR/D/I0fv/yNH8P8jR/D/I0bv/yNG + 8P8jR+//I0fv/yNG7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fw/yNH + 7/8jR+//I0fw/yNH8P8jR/D/I0bw/yNH8P8jR/D/I0fw/yNH8P8hRe//JEjv/2uM9v8fRu//WHjz/+Lu + /v9SdPP/Gj/v/yNH7/8gRO//L1Hw/8LR/P8uUfH/H0Pv/yJG7/8fQ/D/OFrp/8PDxP/Ny8H/jp/T/xxB + 8f8iRvD/GkDx/3WL2f/Py8H/zMvC/1Rx4P8BK/D/gJv3/6S2+f8XPO7/I0fw/yNH8P8jRu//G0Ly/3+T + 1//NysH/xcXD/8DCxf8/Yuj/HEDx/yJI7/+zyPv/Tm/z/xY67v8hRO//Ikbw/yNH8P8jR/D/I0bw/yNG + 7/8ZPu7/HUXv/7DC+v/B0fv/HULv/ypQ8P+3yfv/eZf3/6m9+v//////gZv3/xI57v8jR+//I0fw/yBE + 8P8xVfH/f6D3/yRI7/8iRu//I0bv/yNH8P9PdPT/IEXv/yNH7/8fQu//NFny/5Kq+P8fQu//IkXv/xxB + 7/+Kp/j/rL/6/xI87v+uwvv/QmPy/x5C7/8jR/D/JEn3/x0vuf8TEG7/IBu5/ycf5v8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yUe4P8mHN//JSji/yNG7/8jR+//I0fv/yNH8P8jR+//I0fw/yNG8P8jR/D/I0fv/yNJ + 8f8kRe//JS3k/yYd3/8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/ycf5v8eGqz/FBJz/wkG + c/8/Q5P/3ODl//j28f/r6ur/6urq/+rq6v/w8O7/+Pfz/9fb5/9cY5v/CAxX/yEbyf8oIOz/Jh7f/yYc + 3/8mH+COJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmIcAAJiHAACYh + wAAmIcAAJiDAACYgwAAmIMAAJSDAACYhwAA0M8cAHxe8AB4XvAAdFrsAHBa6AB4XvAAhGr0AHxi9ACAZ + vQAmH8AAJh++ACNI8AAjSPAAI0fvACNG7wAjRu8AI0fvACNH7wAjRu8AI0fwACNH8AAjRu8AI0fvACNG + 7wAjR/AAI0bvACRK+QAGCh1sFCFW/yRF1/8kR/D/IkLl/yRI8/8jRvD/I0bv/yNH7/8jRu//I0fv/yNH + 7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fw/yNH + 8P8jRu//I0fw/yNH7/8jRu//I0bv/yNH7/8jR/D/I0fw/yNH7/8jR+//I0bv/yNH7/8jR+//I0fw/yNH + 8P8jR/D/I0fw/yNH7/8jR/D/I0fw/yNH7/8jR/D/I0jw/yNJ8f8jSvD/I0nw/xtB7/9KcvT/mbb6//z9 + //+uwPn/HEPv/yFG7/8jR+//IUfv/yNK8P/H1/3/X330/xM57v8bQe//Eznv/ydM7f+stsv/zsvB/6yz + yv8kSu//IUXw/xxA8f8+YOz/wcTH/87LwP+jr83/GkDv/0dn8/+zxvr/IUjw/yJF7/8jR+//I0fw/xk/ + 8v9cduD/yMnD/8XFw//KycL/X3jf/xg+8v8ZPu7/n7L5/2WE9f8YPe7/I0fv/yNH8P8jRu//I0fv/yNH + 8P8jR/D/JEfw/x1B7/8dRO//rcH6/4uj+P8+ZfP/T3b0/xE27v8YP+7/rsD6/8/c/P8jSO//IUTv/yNH + 7/8gRO//LlLx/6a9+v8rTvD/IUXv/yNG7/8cQO//XYH1/05x9P8TOO7/Gj7v/yhO8P+TqPf/IETv/yJG + 8P8kSO//SnHz/83a/P+pvfn/uMr7/yVK7/8hRe//I0fw/yRJ9P8aJqT/FRB3/yQez/8mH+P/Jh7f/yYf + 4P8mH+D/Jh/g/yYf4P8lHt//Jhze/yUy5/8jSfD/I0fw/yNH7/8jR/D/I0fv/yNH7/8jRvD/I0fw/yNJ + 8P8kPev/JiXh/yYd3v8mHuD/Jh/g/yYe3/8mHt//Jh7g/yYf4P8mH+D/Jh7g/ycf5P8kHdD/FhR+/wwJ + c/8mKYX/0dXe//b08P/q6er/6urq/+nq6v/p6en/6enp/+np6f/y8e3/8PHw/4aNu/8SE4f/IBjQ/ych + 5f8lJ+L/Jh7g2iYf4A4mH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJiHAACYh + wAAmIcAAJiHAACYgwAAmIMAAJiDAACUgwAAmIcAANDPHAB8XvAAeF7wAHRa7ABwWugAeF7wAIRq9AB8Y + vQAgGb0AJh/AACYfvgAjSPAAI0jwACNH7wAjRu8AI0bvACNH7wAjR+8AI0bvACNH8AAjR/AAI0bvACNH + 7wAjRu8AI0fwACNG7wAkSvkwIkLWxiVI8P8jSPf/I0jx/yRI8/8jR+//I0bv/yNG7/8jR/D/I0bv/yNH + 7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH + 7/8jR+//I0fw/yNH7/8jR/D/I0fw/yNG7/8jR+//I0fw/yNG7/8jR+//I0fv/yNG7/8jR+//I0bv/yNH + 8P8jR/D/I0fw/yNH8P8jSPD/I0nw/yNJ8f8jR/D/JETu/yQ/6/8kO+n/JDbp/yU05/8hKuX/Ljzm/87b + +v//////iJrx/xcf4v8kJ+P/Jirj/yQq5P8aJ+T/q7n1/9rk/f+uw/r/vNH8/3+Z9/8mRe3/i5jP/9LO + wf+8wMb/Pl/o/xxC8f8fRPD/L1j1/7vH2v/Jx8D/zMrC/1h04f8aQPL/pr77/zhc8v8fQu//I0fv/yNH + 8P8eQvH/PF/o/77Bxv/GxcP/zcvB/4CU1v8aQfL/GT3v/4ah+P9zjfb/Fjvu/yNH8P8jR/D/I0bv/yNH + 8P8jR+//I0fw/yNH8P8jR/D/HkLw/yFH8P+et/n/e5z3/2mL9v8WPO//Gz/v/zhc8f/y+P//XHz0/xo/ + 7/8jR/D/IUXv/yNJ7/+6zfv/Wnr0/xE37v8jRu//HkLv/zZZ8f+jvPr/WHjz/zFX8f+ww/r/l6/4/xc9 + 7v8iRu//Jkrv/ytU8P+Lovf//////4Oc9/8XPe7/I0bv/yNI8f8jRu3/GR6Q/xcThv8mH9//Jh7h/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYc3/8kO+v/I0nw/yNH7/8jRu//I0fv/yNH7/8jR/D/I0nx/yRH + 7/8lMub/JiDf/yYd3/8mH+D/JR7g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8nIOb/HRmj/xIQ + cv8LDHf/oKXG//j38f/p6er/6urq/+rq6v/p6en/6unp/+np6f/q6ur/6enq/+zs6//39/D/kpm5/xMS + jv8hH9z/I0jy/yUv5vwmHN5GJhzeACYc3gAmHN4AJhzeACYc3gAmHN4AJhzeACYc3gAmHN4AJhzeACYc + 3gAmHN4AJhzeACYc3gAmHN4AJhzeACYc3gAmHN4AJhzeACYc3gAmHN4AJhzeACYc3gAmHN4AJhzeACYh + wAAmIcAAJiHAACYhwAAmIMAAJiDAACYgwAAlIMAAJiHAADQzxwAfF7wAHhe8AB0WuwAcFroAHhe8ACEa + vQAfGL0AIBm9ACYfwAAmH74AI0jwACNI8AAjR+8AI0bvACNG7wAjR+8AI0fvACNG7wAjR/AAI0fwACNG + 7wAjR+8AI0bvACNH8AIjRu9zI0fw7SRJ9f8jR/H/I0bv/yNG7/8jRvD/I0fw/yNH7/8jR/D/I0fw/yNH + 7/8jRu//I0fv/yNH7/8jR+//I0bv/yNG7/8jR/D/I0fw/yNG8P8jR+//I0fv/yNH7/8jRu//I0bv/yNH + 8P8jR/D/I0fw/yNH8P8jR/D/I0bv/yNH8P8jR/D/I0fw/yNH7/8jRvD/I0fv/yNH8P8jR+//I0fw/yNH + 7/8jR+//I0nx/yNJ8P8jRu7/JD/r/yQ36f8lL+X/Jifj/yYi4f8mIOD/Jh3f/yYb3/8mHd//JRzf/yEZ + 3v95h+3/0Nj5//////98hOz/GRLd/yYc3/8mHd//GxLe/3eB7P//////oq7y/42S7//o7vz/xc36/4mR + 0//KycH/zMzC/1pg2P8bIOb/JS7l/x0t6f+suO7/09LH/8rIwP+hrM3/Fz3v/4ml+P9TcfP/G0Hv/yNJ + 8P8jSvD/IUjx/yVM8P+ossz/yMbD/8rIw/+grM7/Ikfw/xk+7/91k/f/fpT2/xQ67v8jR/D/I0fw/yJG + 7/8jR/D/I0fw/yNH8P8jRu//Ikbv/yJF7/8UOO//NFrx/6rB+v/H2P3/UHD0/xA27f8WO+7/ucv7/3mV + 9v8YPe//I0bw/yJF7/8fRu//nbf5/+Do/f9EaPP/Fj3u/xc97/8dQO//L1Px/6K7+v/5/P///////01w + 8/8aP+//I0bv/yZK8P85YPL/OVvx/8DR+/82WvH/H0Lv/yNG7/8jSPL/I0Po/xYYgf8bF5j/Jx/i/yYe + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYd3/8mIeD/JEHt/yNI8P8jRu//I0fv/yNH7/8jRu//I0nx/yQ/ + 7P8mKOP/Jhze/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf3/8mH9//Jh/g/yYf4P8mH+P/JR7W/xcV + gv8HBHD/Ulie//Lz7//s6+v/6enp/+rq6v/q6ur/6enp/+np6v/q6ur/6urq/+rq6v/p6en/7Ovr//X2 + 8P+PlLf/Fhaq/yND8/8jRe7/JSLgdSUi4AAlIuAAJSLgACUi4AAlIuAAJSLgACUi4AAlIuAAJSLgACUi + 4AAlIuAAJSLgACUi4AAlIuAAJSLgACUi4AAlIuAAJSLgACUi4AAlIuAAJSLgACUi4AAlIuAAJSLgACUi + 4AAmIcAAJiHAACYhwAAmIcAAJiDAACYgwAAmIMAAJSDAACYhwAA0M8cAHxe8AB4XvAAdFrsAHBa6AB4X + vAAhGr0AHxi9ACAZvQAmH8AAJh++ACNI8AAjSPAAI0fvACNG7wAjRu8AI0fvACNH7wAjRu8AI0fwACNH + 8AAjRu8AI0fvACNG7yUjR++3I0fv/yNH8P8jR/D/I0bv/yNH8P8jRu//I0bw/yNH8P8jR+//I0bw/yNG + 7/8jRu//I0bv/yNH7/8jRu//I0fv/yNG7/8jRu//I0fv/yNH7/8jRu//I0bv/yNH8P8jR+//I0fw/yNG + 7/8jR/D/I0fw/yNH8P8jR+//I0fw/yNH7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fw/yNI + 8P8jSfD/JETv/yQ76/8lMOX/Jibi/yYg4P8mHd//Jhzf/yYd4P8mHuD/Jh7g/yYe3/8mHuD/Jh7g/yYf + 3/8jGt//P0vl/z5G5P+1vvX//////1pf6P8cE97/Jh/g/yIZ3/8wMuH/6/D8/9fa+f8wNuP/JSXg/7e7 + 9//e5Of/v7/B/83Mwv92gNL/HRXh/yYc3/8bEN3/aW/t/+Pm2//CwsD/ysvE/0RM2P9dYOz/b4Hu/xwl + 4/8lMeX/JTTn/yQ26f8eNuv/hpXT/83Lwv/HxsP/ub3H/zJV6/8XO/D/Y4T1/3qQ9f8VO+7/JEnw/yNI + 7/8aQO7/HEDv/xxA7/8ZPe//FDvv/xg/7/8fRe//PGDy/5Sr+f+6z/z/d5j3/+Xw//+En/j/Q2jz/4mp + +f9QdvT/HUPw/yNJ8P8jSPD/HkTw/12E9v/t9///+fz//7HD+v9igfX/JEzx/xQ87/94mff/xtn9/3mW + 9/8cRfD/Iknw/yJJ8P8mTfH/OmTz/yBH8P8sU/H/IUXv/yNH7/8jR+//I0jy/yJA4f8UE3f/HRio/ycg + 5f8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8lHd//JSTh/yRE7v8jSPD/I0fv/yNH7/8jSPD/I0jx/yU2 + 6P8mIOD/Jh3f/yYe3/8mH+D/Jh7g/yYe3/8mHt//Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mH+D/Jx/m/yAb + uP8QDXL/Fxl9/8LG1//09O//6enp/+rq6v/q6ur/6urq/+np6f/q6ur/6urq/+np6f/p6en/6unp/+rp + 6v/s6+r/9PTt/25ys/8WL9n/I0v0/yQ66acjSPAAI0jwACNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI + 8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI + 8AAjSPAAJiHAACYhwAAmIcAAJiHAACYgwAAmIMAAJiDAACUgwAAmIcAANDPHAB8XvAAeF7wAHRa7ABwW + ugAeF7wAIRq9AB8YvQAgGb0AJh/AACYfvgAjSPAAI0jwACNH7wAjRu8AI0bvACNH7wAjR+8AI0bvACNH + 8AAjR/AAI0bvACNH708jR+/hI0fv/yNG7/8jR+//I0fv/yNG7/8jR/D/I0bw/yNG8P8jRu//I0fv/yNG + 7/8jRu//I0fv/yNG7/8jRu//I0fv/yNH8P8jRvD/I0bv/yNG7/8jR+//I0fv/yNG7/8jR/D/I0fv/yNG + 8P8jR+//I0bv/yNH7/8jR+//I0bw/yNH7/8jRu//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0nw/yNH + 7/8kPOr/JS/m/yYj4f8mHt//Jh3f/yYd4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH9//JR7f/yYf + 4P8mH+D/IBbf/0VI5P84PeP/HRzf/7/I9v/c5vv/Kynh/yIa3/8mHt//GxTe/4+Y8P//////4+r8/3J4 + 6/9NWun/197v/8nIw//KysL/naDM/yAZ4f8lHeD/JBvg/yYj4/+yu+r/1NPH/8rJwf+bn8v/i5jw/3uH + 7v8dE93/Jhzf/yYc3v8mHd//HBPh/2Zn1P/LzMP/xsXE/8PHxP9PVdr/FxPi/1Va6f96fO7/Fx3i/x8l + 4/9IUen/kqf2/0Ba6v9FYOr/XXnw/4Sb9/+ovfr/2OT+//f+///o7vv/XnDt/xQs6P9gcO7/t8H1/7LB + 9f9ne+//ITfq/yQ56f8kOen/JDnp/yE16f9MZ+//b4Lw//j9/////////////0xg7f8aKuf/JDXo/yY4 + 6P8dLOf/JDPo/yQ06P8lNOn/JDbp/yU66v8kPOv/IT3s/yNE7v8jR+//I0nw/yNK9P8hPNT/ExJ0/x4Z + sv8nIOf/Jh/f/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jhzf/yUn4/8jR+//I0jw/yNH8P8jSfD/I0Xu/yUu + 5f8mHd//Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH9//Jh7f/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 5P8bF5r/BgRt/21zrP/29/D/6urq/+rq6v/q6ur/6unp/+rq6v/q6ur/6urq/+rq6v/q6ur/6unp/+rp + 6f/q6ur/6enp/+/v7P/e3t7/O1DM/xxB8v8jSPDeI0jwFCNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI + 8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI + 8AAjSPAAI0jwACYhwAAmIcAAJiHAACYhwAAmIMAAJiDAACYgwAAlIMAAJiHAADQzxwAfF7wAHhe8AB0W + uwAcFroAHhe8ACEavQAfGL0AIBm9ACYfwAAmH74AI0jwACNI8AAjR+8AI0bvACNG7wAjR+8AI0fvACNG + 7wAjR/AAI0fwACNG73gjR+/9I0bv/yNG8P8jR/D/I0fw/yNG7/8jR/D/I0fw/yNH8P8jR/D/I0fv/yNH + 8P8jR+//I0fv/yNH7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH7/8jR/D/I0fv/yNH7/8jR+//I0fv/yNH + 7/8jRu//I0fv/yNG7/8jR/D/I0fw/yNH8P8jR/D/I0bv/yNH7/8jRu//Ikbx/yJH8v8iR/L/IT/x/yMy + 6v8lJeT/Jh3f/yYc3/8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/f/yYe4P8mH+D/Jh/g/yYf + 4P8lHt//Jh7f/yQb4P8vL+H/SFbl/x0T3/8vL+L/2eP6/3J57P8bE97/Jh7g/yEZ3/8xMuL/2N/6//// + ////////8vb+//P0+f/HyMj/yMfB/7K3x/8vMt//Ixnh/yYf4P8eFt7/Ojzl/6uz0v/JyMH/zMvD/46h + 4f98gu//IBff/yUe3/8mH+D/Jh/g/yAX4P9BRdv/v8PF/8bFxP/NzsL/b3HT/xUN4P9RSeb/d3Hr/xgO + 3f8cEt3/U1Pm//////////7/9/n9//H1/P/4+P3/2+H6/6218/9yeOv/MzTi/x8X3v8mHd//HRbf/yEd + 3/8mI9//IBnf/yYd3/8mHt//Jh7f/yYd3/8kG9//QUbk/yIe3/9WWuj/nqXx/5ih8f84MuL/Ihbe/yUb + 3v8kGd//Jhve/yYc3/8mHN7/Jhvf/yYd3/8mHt//Jh/g/yYg4P8mIuH/Jijj/yUy5/8lQPH/IDfJ/xQS + dP8gGrn/Jx/m/yYf3/8mH+D/Jh/g/yYe4P8mHt//Jh/g/yYc3v8lKOT/I0rx/yNI8P8jSfD/JD/s/yYn + 4/8mHd//Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe + 4v8lH9j/ExCC/xwefv/Q1eP/9/by/+np6f/q6ur/6unq/+rp6f/q6ur/6urq/+rq6v/q6ur/6urq/+np + 6f/q6er/6urq/+rq6v/q6er/9vPr/5up4/8ZPu7/I0fw/yNH71kjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAmIcAAJiHAACYhwAAmIcAAJiDAACYgwAAmIMAAJSDAACYhwAA0M8cAHxe8AB4X + vAAdFrsAHBa6AB4XvAAhGr0AHxi9ACAZvQAmH8AAJh++ACNI8AAjSPAAI0fvACNG7wAjRu8AI0fvACNH + 7wAjRu8AI0fwDSNH8KIjR/D/I0bv/yNG7/8jR/D/I0fw/yNH7/8jRu//I0bv/yNH7/8jR/D/I0bw/yNH + 7/8jR/D/I0fv/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fv/yNH8P8jR+//I0fw/yNH8P8jR/D/I0fw/yNH + 8P8jRu//I0fw/yNH7/8jR+//I0bv/yNH8P8jR/D/I0fv/yNH7/8iRvL/H0T2/yFG7/8lQOT/KDLX/y4y + xv8sKcr/JR3e/yUd4v8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mHuD/Jh/g/yYf3/8mH9//Jh/g/yYe + 4P8mHuD/JyDg/yYf3/8lHuD/KCDg/1Fb5/8lHt//GA/d/2Np6v+jpfP/HRXf/yYe4P8mH+D/HRXe/0RH + 5f/T2fn////////////n7/v/p6/N/8rIwf/Dx8X/Skna/x4V4v8mH+D/JiDg/xoR4P9FSdr/xMfF/8zL + wv+fpsz/h5Dv/yIc4P8lHuD/Jh/g/yYf4P8kHOD/KSXf/6ywyP/IyMP/y8rC/4yVzv8cFeD/Q0Dl/11g + 6P8cFN7/Ixvf/zAs4f9+gO3/wMj3//////++y/b/Mzbi/yIc3/8dGt//GxPe/yIa3/8mHuD/Jh7g/yYe + 4P8lHeD/JB3g/yYd4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yQd4P8mHuD/HhXf/xoW3v8aFt7/Ixvf/ycg + 4P8mH+D/Jh7g/yYf3/8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh7g/yYd3/8mHN//JyHl/yAj + vf8VE3X/IRu+/ycf5v8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8lHd//JSPh/yQ96/8kP+z/JTHm/yYg + 4P8mHeD/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mHt//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe + 3/8nH+T/IhzJ/wwKdf9BRY//sbOw/+Df3//v8O//6enp/+zs7P/t7e3/7u7u/+/v7//u7u7/7e3t/+rq + 6v/q6ur/6urq/+rp6v/p6un/6unq/+7t6v/a4Ov/NVnv/x5C8P8jR/CoI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAJiHAACYhwAAmIcAAJiHAACYgwAAmIMAAJiDAACUgwAAmIcAANDPHAB8X + vAAeF7wAHRa7ABwWugAeF7wAIRq9AB8YvQAgGb0AJh/AACYfvgAjSPAAI0jwACNH7wAjRu8AI0bvACNH + 7wAjR+8AI0bvHSNH78AjR/D/I0fw/yNG7/8jR+//I0fv/yNG7/8jRu//I0fw/yNH8P8jRu//I0bv/yNG + 7/8jR+//I0fv/yNH7/8jRu//I0fv/yNG8P8jR/D/I0fv/yNH7/8jRvD/I0fw/yNG7/8jR/D/I0fw/yNH + 8P8jRu//I0bv/yNH8P8jR+//I0fv/yNG7/8jR+//I0fv/yJH8f8fRff/JUjm/z9Ws/9baJX/b3GL/3x7 + if+HiJH/goSK/2Bljf8tLNH/JB3j/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mH9//Ixnf/zk84/87Q+P/Ihjf/yQb4P9EU+b/MjLi/yIZ3/8qJOD/c4Ds/yYi4P8lHd//Jh7g/yYf + 4P8eFt//Li7h/21x6/+Hhe7/S1Ho/4KGzv/OzsL/zc3C/2Vq1f8cFeL/JR7g/yYf4P8lHeD/HRrh/46U + zv/OzcL/ysrD/5iq4/8nJ+P/JBvf/yYf4P8mHuD/JR3g/yAc4f+Lk87/zczC/8jIw/+usMj/JCHf/0A9 + 5P9bXuj/HRTf/yYf4P8kHeD/Fw7d/x8c3/9rcer/4ej7/7a/9f8rKOH/HxXf/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mHuD/Jh7g/yYf4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8lHt//Jh7g/yYf4P8mH+D/Jh/f/yYe4P8mHuD/Jh/g/yYf4P8mH+D/JR7f/ycf + 5f8fGrP/FBNz/yEcv/8nH+b/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/JiDg/yYd + 3/8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yYe + 4P8mH+D/Jx/l/yAatf8UFHj/FBg6/wICAf9KSkv/7ezt//38/f/t7O3/3t7e/9jY2P/W1tb/2tra/+Pj + 4//09PT/9/f3//Hx8f/u7e3/6urq/+np6v/r6un/7u7q/1x47v8XPfD/I0fw4SNH8BQjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACYhwAAmIcAAJiHAACYhwAAmIMAAJiDAACYgwAAlIMAAJiHAADQz + xwAfF7wAHhe8AB0WuwAcFroAHhe8ACEavQAfGL0AIBm9ACYfwAAmH74AI0jwACNI8AAjR+8AI0bvACNG + 7wAjR+8AI0fvKCNG79QjR+//I0fv/yNG7/8jR+//I0bv/yNH8P8jR/D/I0bv/yNG8P8jR/D/I0fw/yNG + 7/8jR/D/I0fw/yNG7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH8P8jR+//I0fv/yNG7/8jR+//I0fv/yNG + 7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0jw/yFF9P8hRPH/OlK4/2txhv+Zl5b/sbC1/7i4 + wv+4ucX/urrI/76+zP+eoJf/QEKr/yIa5v8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYe + 4P8mH+D/Jh7g/yUc4P8rKOD/X27p/yQd4P8eE9//QEXk/zk64/8hF97/JSDg/0dR5v8kG+D/JR7f/yYe + 4P8mH+D/Jh/g/yMa4P8aEt7/Fw/e/xUL4P9nbdX/y83C/8zMwv+GjtD/Hhbh/yYe4P8mHt//Jh/f/x0V + 4v9JTdr/xsnE/8zKwf+vt8//LSzi/yMa3/8mH+D/Jh7f/yYe4P8cFeH/amzU/8zNwv/HxsP/vcHF/zo+ + 2/89OeX/XF/p/x0U3/8mH9//Jh/g/yYf4P8kG+D/GBDe/zc54v/Hz/j/vsj3/yYk4P8iGd//Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mHt//Jh/g/yYe4P8mHuD/Jh7f/yYe + 3/8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mHuD/Jh7g/yUe + 3/8nH+b/Hxuz/xUUeP8iHcf/Jx/l/yYe4P8mH+D/Jh7f/yYe4P8mH+D/Jh7f/yYe3/8lHt//JR/g/yYe + 3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/ycf5v8eGan/EhJh/wABBP8AAAD/AAAA/09QUP+Dg4T/TEtM/yIiI/8QEBD/DAwM/xgY + GP8yMjL/Y2Nj/42Njv/AwMD/4uHi//X09f/v7+//6urq//bz6f98kez/Fjzw/yNH7/ojR+9EI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAmIcAAJiHAACYhwAAmIcAAJiDAACYgwAAmIMAAJSDAACYh + wAA0M8cAHxe8AB4XvAAdFrsAHBa6AB4XvAAhGr0AHxi9ACAZvQAmH8AAJh++ACNI8AAjSPAAI0fvACNG + 7wAjRu8AI0fvOSNH7+MjR/D/I0fw/yNG7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jRu//I0fv/yNH + 8P8jR/D/I0fw/yNH7/8jRvD/I0fw/yNH7/8jR+//I0fv/yNH7/8jR/D/I0bv/yNH7/8jR+//I0bv/yNG + 7/8jR/D/I0fw/yNG7/8jR+//I0fv/yNG7/8jSO//I0nx/x9B9v8nPt7/UVuR/42Mif+zs7v/vL3N/7i5 + yf+3uMf/t7jH/7a4x/+7vMz/kpSU/zc5t/8iGuT/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh7g/yYf4P8mH+D/HxXf/1de5/9yfez/Ihvf/xwX3/8cFt7/HRje/11m6f9IUeb/Ihjf/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8gGOH/SlDZ/8jJw//My8P/p6rK/yQi4P8kHOD/Jh/f/yYf + 4P8kHN//IR3h/5WczP/OzcL/xcfE/01P2v8eFuH/Jh/g/yYf4P8mH+D/IBfh/0VL2//CxcT/xsXD/8vM + w/9cW9X/MC7l/0xQ5v8fFt//Jh7g/yYf4P8mH+D/Jh7g/yYf4P8eFN7/Hx3f/6659P+dp/H/HBbf/yUd + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/f/yYf4P8mH+D/Jh/g/yYe + 3/8mHt//Jh/g/yUf3/8lHt//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8mH+D/Jx/m/x8btP8VFHf/Ih3I/ycf5P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH9//Jh/g/yUe + 3/8mH+D/Jh/g/yYe4P8mHd//Jh7f/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe3/8nH+b/Gxii/w8SSP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/BgYG/ykpKv93dnf/z8/P/+zs7P/08ur/lqvs/xpA8P8jRu//I0fvbCNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AJiHAACYhwAAmIcAAJiHAACYgwAAmIMAAJiDAACUg + wAAmIcAANDPHAB8XvAAeF7wAHRa7ABwWugAeF7wAIRq9AB8YvQAgGb0AJh/AACYfvgAjSPAAI0jwACNH + 7wAjRu8AI0bvOSNH7+YjR+//I0fw/yNH8P8jR+//I0bv/yNH7/8jR+//I0fv/yNH7/8jR+//I0bw/yRH + 7/8kR+//I0bw/yNG7/8jR+//I0fw/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fw/yNH7/8jRu//I0bv/yNG + 8P8jRu//I0fw/yNH8P8jR+//I0fw/yNH7/8jSfD/I0Xv/yAz8f8vN8f/ZWeA/6Ggnf+7vMv/ubrK/7a3 + xv+4ucn/ubrJ/7i5yP+3uMj/trfA/25yhv8oJdb/JR3i/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yUd4P8fF9//bnns/6az8/+Hje//g4nu/7O79f+Kl+//JR/f/yUc + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Ihnh/zcz3f+8v8b/y8nD/7i+x/85O93/IBfg/yYf + 4P8mH9//Jh/f/xwT4f9QVNn/x8rD/8/Owf+Wnc3/Ih3g/yQd4P8mH+D/Jh/g/yQb4P8uLN7/s7bI/8fH + w//NzMP/eoHQ/y0u5P9HVOX/Ihjf/yYe4P8mH+D/Jh/g/yYf3/8mH9//Jh/g/yAX3/8kIuD/ws33/11i + 6P8dFN//Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/Jh/g/yYf4P8lH+D/JR7f/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh7f/yYf3/8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/ycf5f8eGq//FRR3/yIdyP8mH+T/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/JR7f/yYf + 4P8mH+D/Jh7f/yYc3/8jHOD/ISLi/yYj4f8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh7f/yYe + 3/8mHt//Jh/g/yYf4P8mH+H/Jx/i/xsXm/8PEkP/AAEA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/2trbP/s7Oz/8/Lr/6y76v8dQu//Ikbv/yNH + 8JAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACYhwAAmIcAAJiHAACYhwAAmIMAAJiDAACYg + wAAlIMAAJiHAADQzxwAfF7wAHhe8AB0WuwAcFroAHhe8ACEavQAfGL0AIBm9ACYfwAAmH74AI0jwACNI + 8AAjR+8AI0bvNSNG7+YjR+//I0fv/yNH7/8jR+//I0fv/yNG7/8jR+//I0fv/yNH7/8jRu//I0fv/yNH + 8P8jR+//I0fw/yNH8P8jRvD/I0fw/yNG7/8jR/D/I0fv/yNH7/8jR/D/I0fv/yNG7/8jR+//I0fv/yNH + 7/8jR/D/I0fv/yNH7/8jR+//I0fw/yNJ8f8jRu7/JDfq/yIh6f83N7X/dXd+/62tsf+8vc3/t7jH/7a3 + xv+5usr/pKWw/4SEif+ur7z/vL3O/6Kjof9JS53/IRvl/yYf4f8mH+D/Jh/g/yYe4P8mHt//Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe3/8mH+D/JB3f/xwT3v9EROT/g4nt/5Sh8P9pbOr/Ix3f/yMb + 4P8mH+D/Jh7g/yYf3/8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yQa4P8pKOD/oqzL/8zKwv/KzMP/UlLY/xwU + 4f8mH9//Jh7g/yYf4P8kHOD/Ix/h/56ky//NzML/yMrE/0tP2v8fFuH/Jh/g/yYf4P8lHeD/Ih7h/5CY + zv/MysL/ycnD/5mey/8yMuP/YHfq/yQc3/8mHuD/Jh/g/yYf4P8mH9//JR/f/yUe4P8mH+D/GA7e/2ds + 6v+iq/L/HRXe/yUd3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/f/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYe4P8mHuD/Jh7f/yYf4P8mHt//Jh7g/yYe + 3/8mH+D/Jh7f/yYf4P8nH+b/Hhqx/xUTdf8iHML/Jh/l/yYe4P8mH+D/Jh/g/yYe4P8mHt//JR/g/yUf + 3/8mH+D/Jh7f/yYf4P8gKOT/Mkrr/0Jk8/8kPev/JR3f/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf + 3/8mH+D/Jh/g/yYf4P8mHuD/Jh/h/yYf3v8aF5b/EBNO/wABAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/xQUFP/j4+P/7e3t//Hw6v/Gy+r/IUHu/yFG + 8P8jR/CzI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAmIcAAJiHAACYhwAAmIcAAJiDAACYg + wAAmIMAAJSDAACYhwAA0M8cAHxe8AB4XvAAdFrsAHBa6AB4XvAAhGr0AHxi9ACAZvQAmH8AAJh++ACNI + 8AAjSPAAI0fvGyNH79ojR+//I0fw/yNH7/8jR/D/I0fv/yNG7/8jR+//I0bv/yNG7/8jR/D/I0bv/yNH + 7/8jR/D/I0fv/yNG7/8jR+//I0bv/yNH8P8jRu//I0fw/yNH8P8jR+//I0fw/yNH8P8jRu//I0bv/yNG + 8P8jR+//I0fv/yNH7/8jR+//I0nw/yNG7/8lOOn/JCTj/yEX5v8+Pqr/gIJ//7S1vP+6u8z/trfG/7a4 + xv+2t8b/u7zM/4eIjv9cXFn/mZqk/72+zP9/goj/LS3H/yQb5P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf3/8mH+D/IBff/xkQ3v8YEt7/GxLe/yUd + 4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHeD/Hhri/46Tzv/PzsL/zc3D/3J7 + 0/8dFeL/Jh7f/yYe4P8mHuD/Jh/g/xsT4f9UWNj/yMrE/8/Owv+NlM3/IBvh/yUd4P8mH+D/Jh7g/xwV + 4v9ydNP/zs7C/8jIw/+ytcb/PEXg/52r8/8rJ+H/JBvg/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yIY + 3/83OOP/qbrz/yko4P8kG+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7f/yYe3/8mH+D/JR/g/yUe3/8mH+D/Jh7g/yYf + 4P8mH+D/Jh7f/yYe3/8mH+D/Jx/m/yAbtP8UE3P/IRu9/ycf5v8mH+D/Jh7g/yYf4P8mH9//Jh/g/yUe + 4P8mH+D/Jh3g/yYk4v8cOOv/OV7y/4yd9/+Pofj/LkHq/yIa3/8mHt//Jh/g/yYe4P8mH9//Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4f8mH+H/GheZ/w8TTv8AAQD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8EBAT/TU1O/2BgYP8QEBD/AAAA/wAAAP8MDAz/w8PE//Dw8P/w7+r/0NDn/yNA + 6P8hRvH/I0fvySNG7wQjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AJiHAACYhwAAmIcAAJiHAACYg + wAAmIMAAJiDAACUgwAAmIcAANDPHAB8XvAAeF7wAHRa7ABwWugAeF7wAIRq9AB8YvQAgGb0AJh/AACYf + vgAjSPAAI0jwBSNH764jR+//I0fv/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0fw/yNG8P8jR/D/I0fw/yNG + 7/8jRu//I0fv/yNG7/8jR/D/I0fv/yNG8P8jRvD/I0fv/yNH8P8jR/D/I0fw/yNG7/8jR/D/I0fv/yNH + 7/8jRvD/I0bv/yNH8P8jSfH/I0Xu/yQ36f8mJeH/JRrh/yIb4/9DRaH/hYiD/7i4w/+5usv/trfG/7a4 + x/+2uMf/trfG/7q7y/+fn6r/eXl8/62uvP+zs7j/XWGP/yQe3/8lHeH/Jh7g/yYe4P8mHt//Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH9//JR7f/yYe3/8mHt//Jh/f/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe3/8mH+D/Jh/g/x0V4v90d9P/zc/D/8zM + w/+Vmc3/Hxnh/yUe4P8mH+D/Jh7f/yYe4P8jG9//IyLg/6Oqyv/NzML/xMfE/0VI2/8fFuH/Jh/g/yYe + 4P8gF+H/TlTZ/8XIxP/HxsP/wMPE/1Ja2v/AzPn/Z27q/xUM3v8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8XDd3/U1bm/7S+9f8iIN//JRzg/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mHuD/Jh/g/yYe + 3/8lH9//Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mH9//Jh7g/yYe4P8mHuD/Jh7g/yYf + 4P8lHt//Jh/g/yYe3/8mHt//Jh7g/ycf5f8fG7P/FBJy/yAbvP8nIOb/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHt//Jh7f/yYc3/8eM+j/Ol/z/42c9/+dpvf/jJ33/zI55v8iGt//Jh/g/yYf4P8mH+D/Jh/f/yYf + 4P8kG9//Ixrf/yYf4P8mH+D/Jh/g/yYf4P8mH+D/JyDm/xsXof8PEkz/AAEA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wcHB/9FRUX/sLCw//T09P/5+fn/wsLC/zMzNf8AAAD/AAAA/3d3eP/19PX/8fDs/9fb + 6v8sUOn/IETx/yNH780jR+8FI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACYhwAAmIcAAJiHAACYh + wAAmIMAAJiDAACYgwAAlIMAAJiHAADQzxwAfF7wAHhe8AB0WuwAcFroAHhe8ACEavQAfGL0AIBm9ACYf + wAAmH74AI0v0ACNL9EkjRu//I0bv/yNH7/8jR/D/I0fw/yNG7/8jR+//I0fv/yNH7/8jR/D/I0fw/yNH + 7/8jR+//I0fv/yNH8P8jR+//I0bw/yNG8P8jRu//I0fv/yNH7/8jR+//I0fw/yNH8P8jR/D/I0fv/yNH + 7/8jR+//I0jw/yNJ8P8jRe7/JTbn/yYl4f8mHN7/JRvh/yIc5P9FSJ7/jY+H/7m5xv+4ucr/trfG/7a4 + x/+2t8f/trfH/7a3x/+2t8b/uLnI/7m6yf+8vc3/pael/0pIov8hGOf/Jh7g/yYf4P8lH9//JR/g/yYe + 3/8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/f/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/JR7f/yYf4P8fF+H/VFzY/8rL + w//LysP/rbPI/yos4P8jGuD/Jh/f/yUf3/8lHt//Jh/f/xsU4f9eZNf/yszD/8/Owv+Mk83/IBvh/yUd + 4P8mHuD/Ixvg/zEw3v+3ucb/x8bD/83Mwv9hadL/o6v2/+Xp+/89P+P/Fgze/x8X3/8iGd//IRjf/xwU + 3v8WD93/Mzbi/8rV+P+GjO//GxLe/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/JR7g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh7f/yYf4P8lH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/JR/f/yYe4P8mH+D/Jh/g/yYe4P8nH+X/IBu3/xMSb/8eGrT/JyDo/yYe4P8mH+D/Jh/g/yYe + 3/8mHuD/Jh/f/yYe4P8iGd7/N0vr/4yd9/+apPf/l6L3/4ia9/8vNOT/Ixrf/yYf4P8mH+D/Jh7f/yYf + 4P8kG9//ODvk/zo+5P8iGt//Jh/g/yYe4P8mH+D/Jh/g/ycg5v8dGan/DxBX/wAAAf8AAAD/AAAA/wAA + AP8AAAD/AAAA/ycnKP+5ubr/9fX1//b29v/r6+v/6urq//f29//j4uP/Xl5e/wAAAP+2trj//v3+//b1 + 8v/Jztn/L1Lj/x9D8v8jR+/MI0fvBSNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAmIcAAJiHAACYh + wAAmIcAAJiDAACYgwAAmIMAAJSDAACYhwAA0M8cAHxe8Bh4XvBsdFrsuHBa6OB4XvEshGr1VHxi9TSAZ + vTcmH8AmJh++DiomvwAhRvGUI0nz/yNI8f8jSPH/I0jx/yNH8P8jR/D/I0fw/yNH8P8jR/D/I0fv/yNG + 7/8jR/D/I0fw/yNH7/8jR+//I0fw/yNG7/8jR+//I0fv/yNG7/8jR+//I0fw/yNH7/8jR+//I0fw/yNH + 8P8jSfH/I0nw/yRD7v8kNOf/JiTh/yYc3/8mHd//JBzh/yEc5f9ER57/jZCJ/7q7yP+4ucn/trfG/7a3 + x/+2t8b/trfG/7a3xv+2t8b/trfG/7a3xv+2t8b/urvM/6eop/9MS6D/IRnm/yYe4P8mH+D/Jh/g/yYf + 4P8mH9//Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 3/8lHt//JR/g/yYf4P8mHuD/Jh/g/yYe4P8mHt//Jh7g/yYf4P8mH+D/Jh/g/yYe3/8mHuD/IBjh/z89 + 3P/ExcT/yMfD/7/ExP9CQNv/Hhbh/yYf3/8lH9//JR/f/yYf4P8jG+D/JiXg/6qvyf/Ny8L/w8fD/0RI + 2/8gFuH/Jh7g/yQd4P8kIOD/mKDM/8zLwv/MzMP/gonO/05W6P/8////5en8/3V77f83NuP/Li/h/zM0 + 4v9ISOX/g47u/+bt/P/H0vf/Jibf/yMa3/8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yUe3/8lHt//JR/g/yUe3/8mH+D/Jh7g/yYf + 4P8mH+D/Jh7g/yYf4P8mH9//Jh/g/yYf4P8mHuD/Jx/l/yIcv/8UE3L/Hhmq/ycf5f8mH+D/Jh/g/yYf + 4P8mH9//Jh7g/yYf4P8mH+D/Hxfe/1Ve6v+fq/j/lKD3/5mj9/+AlPb/KjDk/yQc3/8mH+D/Jh/g/yYe + 4P8mH+D/IBfe/1dd6f94iPH/Ih7f/yUe3/8mH+D/Jh/g/yYe4P8nH+X/IRy4/w8OXf8AAAD/AAAA/wAA + AP8AAAD/AAAA/xAQEP/CwsP//fz8/+vr6//p6en/6urq/+np6f/p6en/8fHx//T09P9oaGn/R0dI/8jI + yP9/fnv/foOO/ztd7v8fQ/H/I0fvzCNH7wUjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AJiHAACYh + wAAmIcAAJiHAACYgwAomIMAmJiDAPiUgwGEmIcCONDPHrUhO0cJVX9jcYWrd9WNs3vxjbd7/XGTb/1Nc + 1/9ARc37JiDA6yUfwM0qJr+1JjHN7h8u1P8eMdr/HTfh/x075f8ePur/IEPv/yFF8f8iR/H/I0ny/yNJ + 8v8jSPH/I0fw/yNH8P8jR+//I0fw/yNH7/8jRu//I0fw/yNH7/8jRu//I0bv/yNG7/8jR/D/I0jw/yNJ + 8f8jSfD/JEHs/yUy5v8mIuH/Jhzf/yYd3/8mH+D/JB3h/yEc5f9ESJ7/jY+J/7m6yP+4ucn/trfG/7a3 + xv+2t8f/trjH/7a3x/+2t8b/trfG/7a3xv+2t8b/trfG/7i5yf+ys7j/YWaN/yUg3f8lHeD/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh7f/yYf4P8mH9//Jh7g/yYe4P8lHt//Jh/g/yYe + 4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yIa + 4P8uLd7/qrPI/8vJw//OzcL/XmPW/xsV4f8mH9//Jh7g/yYf4P8mH+D/Jh/g/xsU4v9mbdX/zM3C/8/O + wv+GjM7/HRjh/yYd4P8mHuD/HRbh/3p70f/OzsL/ycnD/6uuyf8cHN7/fITt//7/////////7PP9/9bg + +v/i6vz///////////+8xvX/MDPh/x4V3v8mH+D/Jh7g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYe4P8mHuD/JR7f/yYf4P8mHuD/Jh/g/yYe + 4P8mHt//Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/ycf5P8iHMX/FBN1/x0Zn/8nH+P/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/f/yQc3/8lJOH/a4Hz/5ql+P+Yo/f/gpb2/yow5P8jG9//Jh/g/yYf + 4P8mH9//Jh7g/yAX3/9RVuj/l6f3/zs95P8hGN//Jh/g/yYe4P8mH+D/Jx/l/yMczP8SEmX/AwQF/wAA + AP8AAAD/AAAA/wAAAP96env/+vr6/+rq6v/q6en/6erq/+np6f/q6ur/6erq/+np6f/v7+//6Ojo/yEh + If8EBAT/AAAA/4uNnP8yUOj/IEXz/yNH780jR+8FI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACYh + wAAmIcAAJiHAbSYhwK0mIcDJJiDA6yYgwP0iHL7/NTPH/5Gf9f+dqfv/nqv8/6Ct/f+ir/3/oq/+/6Ct + /f+irv7/mab4/zY0x/8fGL3/d4Ho/4OO7P90euP/Ymja/1BY1P9FStH/O0DQ/zE70P8oN9P/ITLW/x4z + 3P8eOeT/HTvn/x0/6/8fRPD/IUbx/yNJ8v8jSPL/JEjx/yNH8P8kR+//JEfv/yNH7/8jSfD/I0nx/yNG + 7/8lO+v/JSvk/yYg4P8lHN7/JR7f/yYf4P8mHuD/JR3h/yEc5P9ER5//jY+J/7m6yP+4ucn/trfG/7a3 + xv+2t8f/trfH/7a3xv+2t8b/trfH/7a3x/+2t8b/trfG/7a3x/+2t8f/urvJ/4GDiP8sK8v/JBvi/yYe + 4P8mHuD/Jh/g/yYf4P8mH+D/JR7f/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYe3/8mH+D/Jh/g/yYf + 4P8mH+D/JR/g/yYe4P8mH+D/Jh/g/yUf4P8mHuD/Jh/g/yYe4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8kHOD/Ih/g/5edzP/OzML/zMzC/36G0f8dFuH/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8jGuD/Kirg/62y + yP/MysL/vsLF/zw+3P8hGOH/Jh/g/x8W4f9SWNj/x8nD/8jHw/+8wcb/Oz7c/xYO3/9aYej/vML2/+Xt + /P/5/v7/5/D8/8PJ9/9wd+r/IyDg/x4W3/8nH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/JR7f/yUf + 4P8mH+D/Jh7f/yYe3/8mH9//Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mHuD/JR/g/yYf4P8mH9//Jh/g/yYe + 4P8mHt//Jh7f/yUe3/8mH+D/Jh7g/yYe3/8mH+D/Jh/g/yYf4P8mH+P/JB3P/xQSeP8aFpL/Jh/i/yYf + 4f8lH9//JR7g/yYe4P8mHuD/Jh/g/yUe3/8mH+D/Ihjf/yg+6v+Fl/f/mqT3/5Oi9/82POX/IRje/yYf + 4P8mH+D/Jh/g/yYe4P8gF97/UVXo/5+t+f9gauz/Hxfe/yYe3/8mH+D/JR/f/yYe4/8lHtr/Fxd3/wUI + C/8AAAD/AAAA/wAAAP8jIyP/4eHh//Dw8P/p6en/6urp/+rq6v/q6ur/6urq/+rq6v/q6er/7u7u/9vb + 2/8ZGRr/AAAA/xQUD/+srL3/JkLe/yFH9P8jRu/HI0fvAyNH7wAjR+8AI0fvACNH7wAjR+8AJR7gACQd + 4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAmIMAAJiDAACYgwMcmIMD/JiHA/yYgwP8mIMD/Ixy+/y4sxf+MmfP/laD3/4CN7v92gen/cHfn/25z + 5f9weef/fYnr/4mY8P85N8j/HBW5/3eB5/+ms/7/oa79/6Gv/f+hrv3/oq78/5un+P+Pm/L/hZDt/3yD + 6P9tcuD/W2ba/0tY1/8/R9X/MT7U/yQ31v8eM9z/Hjnj/x4/6v8iR/H/JEny/yNJ8P8jR+//JD7s/yUx + 5v8lJeH/Jh3f/yYc3/8lHt//Jh/f/yYf3/8mH9//Jh7h/yIb5/9ERqL/jY+I/7q6x/+4ucn/trfG/7a4 + xv+2t8b/trfG/7a4x/+3uMb/trfH/7a3x/+2t8f/trfG/7a3x/+2t8f/trjG/7u8zP+XmZf/Oj2v/x8W + 6/8mHuH/Jh/g/yYf4P8mH+H/JR7j/yMe5v8jHeb/JB7l/yQe4/8mHuH/Jh7g/yYe4P8mH9//Jh7f/yYe + 4P8mHuD/Jh7g/yUf3/8mHuD/Jh/g/yYf3/8mH+D/Jh7g/yYe4P8mH+D/JR/f/yYe4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh7g/xwU4v9+gNH/zc7C/8zLwv+fo8r/IR7h/yUd4P8mHt//Jh7f/yYf4P8mH9//Jh/f/xoT + 4v9tdNP/zc3C/8/Pwv+CidD/Hhji/yUe4P8iGuD/Nzfc/77Axf/HxsP/ysvD/1pb1/8bEuL/HBLe/x4b + 3/81OOL/R0jl/zc64/8fHd//GRHe/yMb4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe3/8mHt//Jh7g/yUe + 3/8lHt//Jh/g/yYe3/8mH+D/Jh7g/yYe3/8mH9//JR/f/yYe4P8mH+D/Jh/g/yUe4P8mH9//Jh/g/yUf + 3/8mH+D/Jh/g/yYf3/8lHt//Jh7g/yYf4P8mH+D/Jh/g/yUe4P8mH+D/Jh/i/yQe2P8VFH//FxSF/yUf + 3f8mH+H/Jh/g/yYe4P8mH+D/Jh/g/yUf3/8mH+D/Jh/g/yYc3/8eJeT/bYX1/5uk9/+bpvj/aXTu/yAZ + 3v8lHeD/Jh7f/yYe4P8mHuD/IBjf/05T6P+cqfj/hJH0/ygk4P8kHN//Jh7g/yYe4P8mH+H/Jx/l/xsZ + l/8FBxb/AAAA/wAAAP8AAAD/iomK//n5+f/q6en/6urq/+rp6v/q6ur/6urq/+np6f/p6en/6urp//b2 + 9v+cnZ7/AAAA/wAAAP8vLin/rrPQ/x451v8iR/P/I0jvqyNH7wAjR+8AI0fvACUd4AAhGd8AHhXeACUe + 4AAkHeAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJiDAACYgwAAmIMDGJiHA/yYfwP8fGL3/Gxa9/x0Xvf8bFrz/OUHL/0NLzv86Qcn/Oz/H/0E+ + yf9PSc3/T03N/y4yyP8qKcn/JyLG/yMcxP8yMsz/UVnY/11m2/9mb93/cXjj/3d+5/97hen/go/t/4uZ + 8v+bqPr/oa79/6Gu/f+gq/v/mqT3/4uW7/+Aiuv/bnXh/1Rd1/88RNH/JTDQ/yQ64v8lNOj/Jibj/yYe + 4P8mHN//Jh3f/yUe3/8mHuD/JR7f/yYe4P8mH+D/Jh/g/yIb5v88Pqz/h4qF/7m6x/+4usn/trfG/7a3 + xv+2t8b/trfH/7a3x/+2t8b/t7jH/7e3x/+2t8b/trfG/7a3xv+2t8f/trfH/7a3x/+5usr/ra6z/11g + hv8pKcr/Ihrl/yId6v8iHuv/Ix7o/ykg2f8vIsv/MCLJ/y8iy/8qIdn/JB7m/yMe6v8iH+v/Ih7q/yMe + 5f8lHuH/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHt//Jh7f/yUe + 4P8mH+D/Jh/g/yYe4P8eFuH/YGnW/8vMw//JyMP/s7rH/zM03v8iGOD/Jh/g/yYe4P8mH+D/Jh7f/yYf + 4P8iGeD/MDHe/7K4x//LysP/vsLF/zw+3P8gGOD/JB3g/ygj4P+fp8v/y8nC/83Mwv92gdL/HRfi/yYe + 4P8lHd//IBff/x0U3v8fF97/JR3g/yYf4P8mHt//Jh7f/yYf4P8mHuD/JR7f/yUe3/8mHuD/JR7g/yYe + 4P8lH9//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8lHt//JR7f/yUe3/8lH+D/JR/f/yYf4P8mH+D/Jh7f/yUf + 3/8mHuD/Jh7f/yUe3/8mH9//Jh/f/yYe4P8mH+D/Jh7g/yYf3/8mHuD/Jh/g/yYe4f8mHtz/GBaK/xYU + fP8kHtH/Jh7j/yYf4P8mH+D/Jh/g/yYf4P8lHt//Jh/f/yYf4P8lHN//ICDh/2+C8/+bpfj/laD3/56r + +f9XX+r/IRrf/yUe3/8mH+D/Jh7g/yEY3/9DSeX/laP3/5il+P9ESOb/Hxfe/yYf3/8mHuD/JR7f/ycf + 5v8hHL//DA00/wAAAP8AAAD/Njc4/+jn6P/t7e3/6enp/+np6f/p6en/6urq/+rp6f/q6un/6unq/+vr + 6//z8/T/T09P/wAAAP8AAAD/V1ZQ/5Weyv8YNtb/I0n0/yQ/7KEgEt0AIhjfAB4W3gAlHeAAIRnfAB4V + 3gAlHuAAJB3gACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJRzgACYhwAAmIcAAJiHAiCUgwPYkHr3/REPK/2512v+HjOL/oqLr/6qs7v+vtO//ub3z/8PG + 9v/Nzvr/2dr+/9/f/v90euz/GhHc/yUe3/8mH9//Ixve/yAY2f8gGNf/IBjT/yAYzv8gGMv/Ih/I/ycm + x/8uLcf/OTnK/05V0/9vd+L/gpDs/5Kf9f+dqvv/n6z8/6Kv/v+irf3/nKj4/0pPz/8gGLv/Jh3S/yYd + 4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yIa5v8yNb7/fH9//7e3wv+5usr/trfH/7a3 + x/+2t8f/trfG/7a3x/+2t8f/trfG/7a3x/+2t8f/trfH/7a3x/+2t8f/trfH/7a3xv+2t8b/trfH/7q7 + zP+lpqv/f4KM/1ZanP85LbD/SCSC/1UnWP9dKT//Yik1/2IpM/9iKTX/Xig//1cmTv9QJ23/RCWP/zUh + tf8uIsz/JB/k/yIe6v8iHuz/Ih7p/yQe4v8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/f/yYe + 4P8mHuD/Jh/g/yYf4P8mH9//IBjh/0VG2v/JycP/xsXD/8jKxP9MTNn/HhXh/yYf4P8lH9//Jh/g/yYe + 4P8mH+D/Jh/g/xsU4f92fNL/zc7C/87Nwv9/htD/HRjh/yUe4P8dGOL/f4LR/87Owv/LysP/m5/M/yAZ + 4f8lHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yUe + 4P8mHt//Jh/g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/JR7g/yUe3/8mH+D/Jh/g/yUe3/8mH+D/Jh/g/yYe + 4P8mHt//Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/f/yYe3/8mH+H/Jx/i/xsX + mv8TEnP/IhzC/ycf5v8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7f/x8c4P9acPD/n6r4/5ql + +P+KmPX/WWHr/yIc4P8lHuD/Jh/g/yYf4P8hGN//REnm/5Wi9/+cp/j/anTu/yAY3v8mH+D/Jh/g/yYe + 3/8mH+P/JR7Y/xYXcP8BAwf/AAAA/5+foP/5+fn/6enp/+np6f/q6er/6unq/+nq6f/q6ur/6unq/+np + 6f/19PT/wsLD/wkJCf8AAAD/AAAA/42Nhf+AiMX/FDTe/yNK8/8kLubyIBLdfCIY3wkeFt4AJR3gACEZ + 3wAeFd4AJR7gACQd4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJRzgACUc4AAmIcAAJiHAACYhwAQgGc48Sk7hj8vP+cbc2//12Nj+/9jY/v/X1/7/1dX9/9XU + /f/T0/z/0dH8/8/P+//b2/3/lZby/xwU3/8mHuH/Jh/g/yYf4P8mHuH/Jh/h/yYe4f8mH+L/Jh/i/yUd + 4f8kHN//Ixvc/yEZ2P8gGNP/IBnN/ykozP83Ncv/RUvO/1Zf1v9sdOH/fIfq/5Wj9f9ncd7/Hxm6/yYf + z/8mH+H/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yQd4v8nJNj/ZmmE/6+vtf+6u8v/trfH/7a3 + x/+2t8b/trfG/7a3xv+2t8b/trfH/7a4xv+2uMb/t7fG/7a3x/+2t8f/trfG/7a3xv+4ucj/u7zM/7q8 + y/+0t8b/r7PB/6Kmrf97fXb/aT0w/2glFv9pKRr/aCgc/2coH/9nKB//Zygf/2gpHf9pKRr/aSka/2ko + Gf9oKSL/YSk0/1gmTP9OJ3P/QCSX/zUiuv8pINz/Ih7r/yIe7P8kHuT/JR7f/yYe3/8mH+D/Jh7g/yYe + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yIZ4f80Md3/tbvH/8nHxP/NzcL/aHHV/x0V4f8mH9//Jh/f/yYe + 4P8mH+D/Jh/g/yYf4P8hGOH/Nzfe/7q+xv/LysP/uL3G/zU33f8iGeH/HxXh/1xh1//Jy8P/ycjD/7S4 + x/8vMN7/Ixrg/yYe4P8mH+D/Jh/f/yYe4P8mHuD/Jh7f/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf + 4P8mHt//Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yUe + 4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/ycg + 5v8eGav/ExFv/x4Zr/8nH+b/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/JR/f/yYe3/8kHt//KETt/01u + 8/9IaPP/Kjfm/yEY3/8mHuD/Jh/g/yYe3/8mHuD/IRjf/0FG5v+Uoff/mKP4/4qX9f8sK+H/JBvg/yYf + 4P8lHuD/Jh/g/yYg5f8cGKX/GR1L/wAAAP9sbG3/9/f3/+nq6v/p6en/6unq/+rp6v/q6er/6enp/+np + 6f/v7+//8fHx/0VFRv8AAAD/AAAA/wMDAv+pqqn/UFqz/xk96v8iR/D/KC3k/0JA5f8vL+LCHhbeUSUd + 4AchGd8AHhXeACUe4AAkHeAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJRzgACUc4AAlHOAAJiHAACYhwAAmIcAAIBnOAEpO4QDY2v4J0ND7Ls/P+1/Pz/ucz8/7yM/P + +/HPz/v/0dD7/9XU/P/S0vz/2tn9/6is9f8hH9//JBzf/yYf3/8lHt//Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/h/yYf4f8mH+H/Jh/i/yYe4f8kG+D/Ihrb/yAZ0/8gGM//HxjL/yUizf81MtX/Mi7W/yQe + 1f8mH9//Jh/h/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8iG+b/Rkmg/5ydmv+7vM3/trfG/7a3 + xv+2t8b/trfH/7a3x/+2t8b/trfH/7a3x/+3uMf/trjH/7a3xv+4ucn/uLnJ/7e4x/+7vMz/tLfH/5uf + qf+BgIX/dWpr/21aWf9nSUX/ZjYy/2UoI/9mKCT/ZSgk/2YpJP9lKCT/ZSgk/2YpJP9mKST/Zigk/2Uo + JP9lKCT/Zigi/2cpHv9oKRv/aSoa/2kpGP9nKSX/XShB/08mcP86I6j/KiDY/yIf7P8iHun/JR7h/yYf + 4P8mH9//Jh/g/yYf4P8mH9//Jh/g/yYf4P8kHOD/JSTh/56ny//Ny8L/zMzC/4uPzv8dFeH/JR7g/yYf + 3/8mH+D/Jh/g/yYf4P8mH+D/Jh7g/xwV4v98gdL/zM3C/87Owv92ftL/HBbh/yIZ4P87Ptz/wMLF/8jH + w//Ex8X/S0vZ/x0V4f8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYe + 4P8lH9//Jh/f/yUf4P8mH9//Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/f/yYe3/8mHt//Jh/g/yYe + 4P8nH+X/IRy+/xQSc/8bGJr/JyDj/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/f/yYe3/8mHt//Jh3f/yI9 + 7f8dRPD/HUPw/yIt5f8mHd//Jh/g/yYf4P8mHt//Jh/g/yMa3/83POP/kp/2/5ai9/+Zpvj/TlPo/x8X + 3v8mH+D/Jh/g/yYf3/8nH+X/HhjF/yYphP9SVl7/hISC//Tz8//q6ur/6unp/+rp6v/q6ur/6enp/+3t + 7f/5+fn/8PDw/29vcP8AAAD/AAAA/wAAAP8iIh//ubvI/yAuqP8hR/P/Hz/u/z1E5v+bqPj/i5n1/1Ja + 6f8hG97GIRnfVx4V3gAlHuAAJB3gACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJRzgACUc4AAlHOAAJRzgACYhwAAmIcAAJiHAACAZzgBKTuEA2Nr+ANDQ+wDPz/sAz8/7AM/P + +wvQ0Psoy8z7Vrq/+JOxt/bHwsX599va/f+ip/T/IR7f/yQc3/8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4f8mH+H/Jh7h/yYe4v8lHOH/Ihrf/yMb + 4P8mH+L/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8kHOP/KijQ/3R3hf+3t8L/t7jI/7a3 + xv+2t8f/trfH/7a3x/+2t8b/trfH/7a3x/+2t8b/trfG/7a3xv+6u8v/srLA/6qruP+6vMv/mp+p/3dw + cv9lRUH/Yy4p/2MnI/9kJB//ZSQf/2UlIf9lKCT/ZSgk/2UoJP9lKCT/ZSgk/2YoJP9lKCT/ZSgk/2Yo + JP9lKCT/ZSgk/2UpJP9lKCT/Zikk/2YoJP9mKST/Zygh/2goHf9pKBn/aSkc/2AoPP9NJ3L/NyO1/yUf + 4/8iHuz/JB7k/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/x0X4v+KjM7/zs/C/8vKw/+orsj/Jyjg/yQb + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe3/8gF+H/PD7c/77Cxv/LysP/trzH/zQ13v8gFuH/KSbg/6Or + yv/LycL/zs3C/2Zr1f8bFeL/Jh/g/yYe3/8mH+D/Jh/g/yUf3/8mHuD/Jh/g/yYe4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/Jh7g/yYe3/8lHt//JR/g/yYe4P8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYe4P8mHt//Jh7g/yYf + 4P8mHuD/Jh/i/yQd0v8VE3v/FxWH/yYf3v8mH+H/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHt//Jh7g/yYd + 3/8kO+v/I0nw/yNJ8P8lMOX/Jhzf/yYe4P8mH+D/Jh/g/yYf4P8jGt//Nzzj/5Gf9v+Voff/mqX4/3SA + 8f8hHN7/Jh3g/yYf4P8mH+D/Jh/h/yYf4v8ODI//fISy//v79f/q6ur/6enp/+vr6//u7u7/9PT0//n5 + +f/n5+j/pKSl/z0+Pv8AAAD/AAAA/wAAAP8AAAD/enp1/36CtP8RJbf/JEz4/x856/9AROX/kqH3/6Gt + +f+eq/n/cHzv/y8w4v8eFd6jJR7gDyQd4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAmIcAAJiHAACYhwAAgGc4ASk7hANja/gDQ0PsAz8/7AM/P + +wDPz/sA0ND7AMvM+wC6v/gAdYrsCoWM7yxbaeleRlDmmSEX38gmHuD3Jhze/yYc3/8mHN//Jhzf/yYc + 3/8mHd//Jh/g/yYe4P8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/IRrm/0JFpv+cnZv/u7vL/7a3 + xv+2t8b/trfH/7a3xv+2t8b/trfG/7a3x/+2t8b/trfG/7a3xv+7vMz/qKm1/319gP+bnaj/h4uQ/2VM + Sf9jKiX/ZSQf/2UmIf9lJyP/Zigk/2UoJP9lKCT/ZSgk/2YoJP9mKST/ZSgk/2UoJP9lKCT/Zikk/2Yo + JP9mKCT/ZSgk/2YoJP9lKST/Zikk/2UpJP9lKCT/Zigk/2YoJP9mKST/Zikk/2YpI/9nKR7/aSkZ/2cp + I/9ZJ1D/QSWW/ysg1P8iH+z/Ix7n/yYe4P8mH9//Jh7g/yYf4P8eFeH/anLV/8zNwv/Hx8P/u8HG/zw7 + 3P8gF+H/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH9//JR7g/x0Y4f+FjND/zMzC/83Nwv91fdL/HBXi/x4Z + 4f+HjM//zs7C/8zLw/+Fjc//Hhbh/yYe4P8mH+D/Jh/g/yYf3/8mH+D/Jh7g/yYe4P8mHuD/Jh7g/yYf + 4P8mHuD/Jh/g/yUe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 3/8mH+D/Jh7g/yUf4P8mH+D/Jh7f/yUe3/8mHuD/JR7f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mHt//Jh7f/yYf4f8mH97/GRaN/xQSd/8jHcn/Jx/k/yYe3/8mH+D/Jh/g/yYf4P8mH+D/JR/g/yUf + 3/8mHd//JTjo/yNJ8P8jSfD/JTLm/yYc3v8mH+D/Jh/g/yYe4P8mH+D/Ixrf/zc85P+Rn/b/lqH3/5ah + 9/+ToPb/OTvk/yEY3v8mH+D/Jh/g/yYe3/8nH+X/HRbB/xsdfP/Hy9n/9vXv/+np6f/k5OT/3t3e/8LC + w/+FhYX/LS0u/wAAAP8AAAD/AAAA/wAAAP8AAAD/MjEu/8HF0f8lKIr/HTrb/yNL9f8kNej/JBrf/zU2 + 4/9ia+z/jJr1/6Kv+v+Rn/b/T1bp/yIb37skHeAYJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJiHAACYhwAAmIcAAIBnOAEpO4QDY2v4A0ND7AM/P + +wDPz/sAz8/7ANDQ+wDLzPsAur/4AHWK7ACFjO8AW2npAEZQ5gAjGd4IJDzrjyQ66f8lMOb/JSvk/yUt + 5v8kMef/JDPm/yYn4/8mHuD/Jh7f/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 3/8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH9//JR3g/yQd3/9hZYz/srK5/7i5 + yf+2t8b/trfG/7a3xv+2t8b/trfG/7a3xv+2t8b/trfG/7a3xv+7vMz/p6ey/3Rzdf+OkJj/fn2B/2M7 + N/9lJB//ZiYi/2YoJP9lKCT/Zigk/2YoJP9mKCT/Zigk/2YoJP9mKCT/Zikk/2YoJP9mKST/Zigk/2Yp + JP9mKCT/ZSgk/2UoJP9mKCT/Zigk/2YpJP9lKCT/ZSgk/2YoJP9mKCT/Zigk/2YpJP9lKCT/ZSkk/2Yp + JP9mKCL/aCkc/2kpG/9gKTr/SiZ9/zEhxv8jH+r/Ix7p/yYe4P8mH+D/Hhfh/09S2f/Ly8P/xcXE/8zN + wv9TV9f/HRXh/yYf3/8mHuD/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8fFuH/Q0Xb/8HFxf/KycP/tbrH/zEy + 3/8aD+H/YmjW/8rMw//KysP/qavJ/yUh4P8lHOD/Jh/g/yYf3/8mH9//Jh/g/yYf3/8mHuD/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/JR7g/yUe4P8mHuD/Jh7g/yYf4P8mHuD/Jh7g/yYe + 4P8mH9//Jh/g/yYe4P8mH9//Jh7f/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYe + 4P8mH+D/Jh7f/yYe3/8mH+H/JyDl/x0Zp/8SEW7/Hxqy/ycg5v8mH+D/Jh/g/yYf4P8lHuD/Jh/g/yYf + 3/8mH+D/Jhzf/yU26P8jSfD/I0nw/yRB7f8mIOD/Jh3f/yYe4P8mH+D/Jh/g/yMa3/83POT/kZ/2/5ah + 9/+UoPf/mqb4/2dw7f8fGN7/Jh7g/yYf4P8mH+D/Jh/g/ycg5P8JApX/Q0eQ/+3v7P/39vP/nZ6f/yws + LP8JCQr/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/FBQT/8XGwv9gY6P/Dhia/yRK9f8jSPD/JSnk/yYc + 3/8iGd//Hxnf/y4t4f9OVOj/doLw/5mp+P9mce3/JB7gvCUc4A8lHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACYhwAAmIcAAJiHAACAZzgBKTuEA2Nr+ANDQ + +wDPz/sAz8/7AM/P+wDQ0PsAy8z7ALq/+AB1iuwAhYzvAFtp6QBGUOYAIxneACNJ8K8jSfD/I0nw/yNJ + 8P8jSvD/JD3q/yYo4/8mH+D/Jh7g/yYe3/8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYe4P8mH+D/Jh7f/yYf + 4P8mH+D/Jh7g/yYe3/8mH9//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yUc4/8qKc7/fH+M/7m6 + xv+3uMb/trfG/7a3x/+2t8b/trfG/7a3xv+2t8b/trfH/7a3xv+6u8v/rK26/3Z2eP+RlJ3/fHp8/2M0 + MP9lJCD/Zigk/2YoJf9lKCT/ZSgk/2YoJP9mKST/Zikk/2YpJP9mKST/ZSgk/2UoJP9mKST/Zikk/2Uo + JP9lKCT/ZSgk/2UoJP9mKCT/Zigk/2UoJP9mKCT/Zigk/2UoJP9mKCT/Zikk/2YoJP9lKCT/Zigk/2Yo + JP9lKCT/Zigk/2UpJP9lKCP/Zygf/2kpGv9kKS3/TiZv/zMivf8jHuj/Ix7p/yAY4f87N9z/vsLG/8fG + w//NzcL/dX3S/xwU4v8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh3g/x4b4f+NlM3/zczC/8zN + wv9tcdX/Fw7i/0FC2//FxsP/ycjD/7q/xv87Pd3/IRfh/yYf4P8mH+D/Jh/g/yUf3/8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8lHt//Jh/g/yYf4P8mH+D/Jh7g/yYe + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh7g/yYe + 4P8mH9//Jh/g/yYf4P8mH+D/Jh/g/ycf5P8iHMT/FBJz/xoWlf8nIOT/Jh7g/yYf4P8mH+D/Jh/g/yYe + 3/8mHt//Jh/g/yYc3v8lMeb/I0nw/yNH7/8jSfD/JTTn/yYc3v8mHt//Jh/g/yYe4P8jGt//Nzzk/5Gf + 9v+Woff/lKD3/5ai+P+Rnfb/Njbj/yEY3v8mH+D/Jh/g/yYe3/8kG+P/QUXc/xQYg/9dYqL/8vPw//z6 + 9/+dnZ7/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/EhIR/7q7tf+NksD/Cwt4/yE82f8kSvT/JEHs/yYg + 4P8mHuD/Jh7g/yYe4P8jG9//IBff/yAa3v9AQ+X/k6T3/z9A5f8hGN9HIRjfACEY3wAhGN8AIRjfACEY + 3wAhGN8AIRjfACEY3wAhGN8AIRjfACEY3wAhGN8AIRjfACEY3wAmIcAAJiHAACYhwAAgGc4ASk7hANja + /gDQ0PsAz8/7AM/P+wDPz/sA0ND7AMvM+wC6v/gAdYrsAIWM7wAiSvAAIkrwACJK8AYjR/DQI0fv/yNJ + 8P8kQu3/JSzk/yYd4P8mHd//Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe + 4P8mH+D/Jh/g/yYf3/8mH9//Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8jGub/Nja4/5GU + mP+7vMv/trfG/7a3x/+2t8b/trfG/7a3x/+2t8f/trfG/7a3x/+5usr/r7G+/3R1d/+QkJn/hYWL/2M4 + NP9lIx//Zigk/2YoJP9lKCT/ZSgk/2YpJP9mKCT/Zikk/2YpJP9mKCT/Zigk/2YoJP9lKCT/Zigk/2Uo + JP9mKCT/Zigk/2UoJP9mKCT/ZSgk/2UoJP9mKST/Zigk/2YpJP9mKCT/Zigk/2YpJP9lKCT/ZSgk/2Uo + JP9mKCT/ZSgk/2YoJP9mKCT/ZSgk/2YoJP9mKCT/Zygg/2koGv9mKSf/Uidk/zQiuv8hG+r/Jino/6Wt + yv/LysL/zMzD/5mdzP8fG+H/JR3i/yYf4v8mH+H/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8fFuH/Rkna/8TG + xP/KycP/sbXI/ysp3/8pJd//q7LI/8rJw//Jy8P/V1jY/xwV4v8mH+D/Jh7f/yUf3/8mH+D/Jh7g/yYe + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh7f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHt//Jh/g/yYe3/8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/f/yYf4P8mH+D/Jh/g/yYf4P8mH+H/JR/Z/xcVhP8WE37/JR7V/yYf4/8mH9//Jh/g/yYe + 4P8mH+D/Jh/g/yYf4P8mHN//JSvk/yNJ8P8jR+//I0jw/yNH7/8mKeP/Jhzf/yYf4P8mH+D/Ixrf/zc8 + 5P+Rn/b/laH3/5Sg9/+UoPf/mqb4/2977/8hG9//JR3g/yYf4P8mHuD/GhLe/7K3+v/M0uf/Ki+L/15k + qP/o6/P/5+bg/xoZF/8AAAD/AAAA/wAAAP8AAAD/Hx4b/7m6tf+Wmsb/Dgxz/xwtuv8kSvX/I0nw/yU0 + 6P8mHd//Jh7g/yYf4P8mHuD/Jh/g/yYf4P8lHuD/Hhbe/0pO6P85OeX7IxvfNSMb3wAjG98AIxvfACMb + 3wAjG98AIxvfACMb3wAjG98AIxvfACMb3wAjG98AIxvfACMb3wAjG98AJiHAACYhwAAmIcAAIBnOAEpO + 4QDY2v4A0ND7AM/P+wDPz/sAz8/7ANDQ+wDLzPsAur/4AHWK7AAmHN8AJhzfACYc3wAlMOctJEDt7CNJ + 8P8lOur/JiLh/yYc3/8mHuD/JR/g/yYe4P8mHt//Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/JR/f/yYe4P8mHuD/Jh/g/yYe3/8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/IBjn/0A8 + rf+dn57/urvL/7a3x/+2t8f/trfH/7a3xv+2t8b/trfG/7a3xv+3uMj/trfG/3t7ff+EhIn/lpqi/2RE + QP9lIx//Zigk/2YpJP9mKCT/ZSgk/2YoJP9lKCT/Zigk/2UoJP9mKCT/Zigk/2UoJP9lKCT/Zikk/2Yp + JP9mKCT/ZSgk/2UoJP9mKCT/Zikk/2UpJP9lKCT/Zikk/2UpJf9mKST/Zigj/2YoI/9mKCL/Zigi/2Yo + Iv9mKCP/ZSgj/2YoI/9mKCP/Zikk/2UoJP9lKCT/Zikk/2YoJf9mKCT/Zygh/2kpGv9mKSX/USZk/y0d + u/+Sl9H/zc3D/8nIw/+utsj/LjDf/yEY1v8lHtX/Jh/f/ycf4v8nIOf/Jx/l/yYf4/8mHuL/JR3i/yId + 4v+Wnc3/zMzD/8vNwv9rcNT/FBDj/4yQzv/OzsH/zczC/3R+0/8cFuL/Jh7g/yYe3/8lH9//Jh7f/yYf + 4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/JR7f/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/f/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/ycg5f8dGab/ExFv/yAauf8nH+b/Jh/f/yYf + 4P8mH+D/JR/f/yYe4P8mH+D/Jh3f/yYl4v8jRe7/I0fv/yNH7/8jSPD/I0Lt/yYi4f8mHd//Jh/g/yMa + 3/83PeT/kp/2/5ah9/+UoPf/lKD3/5Wg9/+Ypfj/Ulvp/x4W3v8mHuD/Ixvf/yMl3//N0/j//////9jc + 5f8VGUX/LDJv/5Sayf8xND7/AAAA/wAAAP8DBAD/P0A7/6mrtf9vc6z/EA95/xosuP8kSfT/I0fw/yNH + 7/UmKOPVJh3f/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYe3/8hGN//JBzguCYf4AcmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYhwAAmIcAAJiHAACAZ + zgBKTuEA2Nr+ANDQ+wDPz/sAz8/7AM/P+wDQ0PsAJhzfACYc3wAmHN8AJhzfACYc3wAmHN8dJhvezSU5 + 6v8kPev/Jh7f/yYd4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yUf4P8mHuD/Jh/g/yYe4P8mHuD/Jh7g/yYf4P8lH9//JR7f/yYf4P8mH+D/Jh/g/yAa + 5v9ISqP/paal/7m6yv+2t8f/trfG/7a3x/+2t8f/trfG/7a3x/+2t8b/u7zM/5SUnP9ubW7/qKu4/3Rr + bP9jJyL/ZSYj/2YoJP9mKCT/Zigk/2UoJP9mKST/Zikk/2YoJP9mKCT/ZSgk/2UoJP9lKCT/ZSgk/2Yp + JP9mKCT/ZSgk/2UoJP9lKCT/Zikk/2YoJP9lKST/Zigj/2coHv9pJxj/aiYY/2knHf9nJyH/aCgh/2go + If9nJyH/aCcd/2gmHP9oJhz/aCYa/2olFf9qJRX/aSUX/2gmGf9pJhn/aCYa/2gnG/9oKBz/aCgb/2oo + FP9gHx3/kHeE/8zPy//Ix8P/yMnI/z4+qf8OC37/FhSA/xkWi/8dGJ7/Hxmz/yIcwv8kHc3/JR7X/yUe + 3P8eFt7/TlPX/8bJxf/KycP/r7TI/yYj4f9kaNn/y83D/8vLw/+Zncz/IRrm/yYe5v8nH+T/Jh/j/yYf + 4f8mH+H/Jh/h/yYe4P8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYf + 4P8mHuD/Jh/g/yUe3/8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8lHt//JR7f/yYf4P8mH+D/Jh7f/yYe + 3/8mHt//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8nH+T/Ix3M/xQTdv8aFpf/Jx/k/yYf + 4f8mH+D/Jh/g/yYf3/8mH+D/JR/f/yUd3/8mH+D/JD/s/yNI8P8jR+//I0bv/yNJ8P8kO+n/Jh7f/yYc + 3/8kGt7/MjDh/5Gc9v+Voff/lKD3/5Sg9/+UoPf/l6L4/5Cf9v88QOX/IRnf/x0U3v9ISOT/8/j9//// + ///m5uX/HR0Y/wAAAP8FCT3/GRp2/wsNKf8JChz/HBxO/1JWmv8uMpT/ERyd/x861f8kSfT/I0fx/yNI + 7/8jP+vEJiThECYe4FsmH+DDJh7g9SYf3/4mH+D/Jh7f/yYf4P8mHt//Jh7g4CYe3zAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmIcAAJiHAACYh + wAAgGc4ASk7hANja/gDQ0PsAz8/7AM/P+wDPz/sAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gmyYd + 3/8jQO3/IjDm/yUa3/8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf3/8lHt//Jh7g/yYe4P8mH+D/JR7f/yUe3/8mH+D/Jh7g/yYf + 4P8iHOX/TFKf/6mpq/+5usr/trfH/7a4x/+2t8f/trfH/7a3xv+2t8f/t7jI/7S1xP90dHb/kJCY/6Gm + sf9lRUH/ZSQf/2YoJP9mKST/Zigk/2YpJP9mKST/ZSgk/2YpJP9mKST/ZSgk/2YpJP9mKCT/Zigk/2Yo + JP9mKCT/Zikk/2YoJP9mKCT/ZSgk/2YpJP9mKSX/Zycf/2gnHv9fLEH/UDRu/0Q4kP9AO6L/PD2v/zw+ + sv86PLL/NTiy/zo3ov88OJ3/Pzqe/0I6lv9FOIf/RTiJ/0c1ff9MMW7/SzFw/08vY/9RK1j/UipY/1oq + Sf9dLUf/WyQz/39ZXf/Cx8z/wcPG/87QyP9VW5X/Cwhz/xUSdv8UEXP/FBBv/xMPbf8UEHP/FRB3/xUR + fP8WEoD/FxKI/xUViv+Xm7P/zczH/8zNx/9aXrX/MTal/8bIxP/LysX/sbXE/ygru/8dF73/Ix3G/yQe + 0P8lHtr/Jh/e/ycf4v8nH+b/Jx/m/ycf5P8mH+L/Jh/h/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe + 4P8lH9//Jh/g/yYf4P8mHuD/Jh7g/yYe3/8mH+D/Jh/g/yYe4P8mHuD/JR/f/yUf4P8mH+D/Jh/g/yYe + 3/8mH+D/Jh7g/yUe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mH+D/Jh/h/ycf4v8aF5b/FRN5/yUf + 0v8nH+T/Jh7g/yYf4P8mH+D/Jh/g/yUf3/8lH+D/Jhzf/yUy5/8jSfH/I0fw/yNG7/8jRu//I0jw/yQ5 + 6v8lMuf/Ijbo/yw96f+MmPb/lqL3/5Sg9/+UoPf/lKD3/5Sf9/+apvj/gY7z/yso4f8WDd7/gIft//// + ////////rq6w/wICA/8AAAD/AAEA/x8fif8lINf/IS7D/yA2z/8aMs7/Hj3i/yNI8v8kSPT/I0bv/yNH + 8P8jR+//JSnjeSYk4QAmHuAAJh/gAyYf4CImH+BBJR7fUCUe32QmH+BqJh7fYiYf3x4mHt8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJiHAACYh + wAAmIcAAIBnOAEpO4QDY2v4A0ND7ACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gCiYe + 4NElHN//KEHs/zJN7f8nI+D/Ihnf/yYf4P8mH+D/Jh/g/yYf3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mHt//Jh7g/yYf4P8mH+D/JR/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe + 4P8mHuD/Ix3j/1JYmf+srbH/ubrJ/7a4x/+2t8b/trfG/7a3xv+2t8b/trfG/7m6yv+np7P/a2tr/6ut + uv+MjJP/YjIt/2UlIP9mKST/Zigk/2YpJP9mKST/Zikk/2UoJP9mKST/Zikk/2YoJP9mKST/Zigk/2Yo + JP9mKST/Zigk/2YpJP9lKCT/Zigk/2YoJP9lKST/Zygg/2MrNP8+PKb/JkXq/yFI9/8fSP3/H0j7/yBJ + +P8eR/j/I035/09v+/9kgf//ZIH//22J//9wi///cIv//3CM//9sh///ZoD7/2aA+/9ngPf/Ynfz/1hw + 8/84Vuz/IkLp/yVE5v8rSuP/O1/p/0Jk6P9Pb+b/N1Ta/x42zP8gNsj/HzPB/x0xvf8cLbf/Gyqu/xsm + pP8ZIZn/FxyN/xUXgv8MC3n/SUyT/87NxP/W0sT/qKmx/yMme/+oqrj/0c/H/8PEw/83Oob/DAlz/xUT + e/8VE33/FxSD/xkWj/8bGJv/HRmq/yAbuP8jHcr/JR7Y/yYf3/8nH+T/Jx/m/yYf4/8mH+H/Jh/g/yYf + 4P8mH+D/JR7f/yYf4P8mH+D/Jh7g/yYf4P8mH9//Jh/g/yYf4P8mH+D/Jh7g/yYf3/8mHt//Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh7g/yYe4P8nH+X/IhzD/xMS + cv8cGKf/JyDm/yYf4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYc3/8lJeL/I0bu/yNH8P8jRu//I0bv/yNH + 8P8jSfD/I0jw/yJI8P8kSvD/eIz2/5mk9/+UoPf/lKD3/5Sg9/+UoPf/mKP4/46c9v8wL+L/HRnf/8HH + 9///////8vLy/zk6Ov8AAAD/AAAA/wEBAP8fKIb/JT/7/yRJ9v8jSvX/I0n0/yNI8v8jRvD/I0bv/yNH + 7/8jSfD/JDrp8CYb3iQmG94AJh7gACYf4AAmH+AAJh/gACUe3wAlHt8AJh/gACYe3wAmH98AJh7fACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYh + wAAmIcAAJiHAACAZzgBKTuEA2Nr+ACUe3wAlHt8AJR7fACUe3wAlHt8AJR7fACUe3wAlHt8AJR7fACUe + 3y4mH+DvIhnf/zY75P+On/f/eYTx/zEx4v8gF9//JBzg/yYe4P8mH+D/Jh/g/yYe3/8lHt//Jh7g/yYf + 4P8mH+D/Jh7f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/JR7g/yMd4v9XXZX/srO5/7i5yP+2t8b/trfH/7a3x/+2t8f/trfG/7a3xv+6u8r/lpaf/3Jy + c/+ytcT/fHR3/2IoI/9lJyL/ZSkk/2UoJP9mKST/Zigk/2YpJP9mKST/Zikk/2YpJP9lKCT/ZSgk/2Yo + JP9lKCT/Zikk/2YoJP9lKCT/ZSgk/2YoJP9mKCT/ZSgk/2kmGP9EOZP/HUr//yJH8/8jR/D/I0bv/yNH + 8P8jR/D/HULv/2B69P+fqff/nKX4/5ul9/+ZpPf/maP3/5mj9/+Zo/f/mqT4/5ql+P+apfj/mqX5/5um + +f+bpvr/kZ/5/0Vl9f8eRPP/IUbz/x5C8P8eQvH/HEDx/yFF8/8kSvX/JEn2/yRJ9v8kSvf/IUj4/x5E + 9f8dRPP/IEXw/yNG7f8jRuv/Hj7f/xw50/9Zctz/dozX/4ue1f87TK3/eYO4/8LFyf/Lzcr/W2CY/wkG + cf8UEHL/FBB0/xQQdP8TEHT/FBFz/xQRc/8UE3b/FRR7/xYVgv8aF5H/HBij/yAbt/8jHcz/JR7b/ycf + 4v8nIOb/Jx/k/yYf4v8mHuD/Jh/g/yYe4P8mHuD/Jh7g/yYf4P8mH9//Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf + 4f8aFpT/FRN5/yQe0v8mH+T/Jh/g/yYe3/8mHuD/Jh/f/yUe3/8mHuD/Jhze/yU66v8jSfH/I0bw/yNH + 7/8jR/D/I0fv/yNG7/8jRu//HULv/09s8v+apff/lZ/3/5Sg9/+UoPf/laD3/5uo+P9ha+3/GA/d/0VG + 5f/y+P3//////4uLjP8AAAD/AAAA/wAAAP8BAAD/HjKG/yRL/P8jR/D/I0fw/yNH8P8jR+//I0bv/yNG + 7/8jR/D/I0Xu/yUm4aImG94AJhveACYe4AAmH+AAJh/gACYf4AAlHt8AJR7fACYf4AAmHt8AJh/fACYe + 3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmIcAAJiHAACYhwAAgGc4AJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuBHJh/f/yYe4P8hGN7/RUnm/5Si9/+ToPb/Vl7p/ysn4f8gGN7/Ixrf/yQd4P8lHuD/JR7f/yYe + 3/8mH+D/Jh/g/yYe3/8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/f/yYf4P8mHuD/Jh7g/yYf + 4P8mH9//Jh/g/yUd4f8kHeD/XGGT/7Ozuf+4ucn/trfH/7a3x/+2t8b/trfG/7a3xv+2t8b/urvM/4yN + lP96en3/srbF/3JlZf9jJB//Zigk/2YpJP9lKST/Zikk/2YoJP9mKST/Zigk/2YpJP9mKST/Zigk/2Yo + JP9mKCT/Zigk/2UoJP9lKCT/Zikk/2YpJP9lKCT/ZSgk/2YoIv9pKB7/NUC6/x9J/P8jRu//I0fv/yNH + 7/8jRu//I0fw/x1C7/9qgfT/n6n3/5ei9/+Xovf/l6L3/5eh9/+Woff/lqH3/5ah9/+Woff/lqH3/5ah + 9/+Woff/lqH3/56n9/95jPb/H0Pv/yNG7/8jR/D/JEfw/yNH8P8jR/D/I0fv/yNG7/8jRvD/Ikbv/zFT + 8P9RbfP/Znz0/22E9f91ivf/d4z3/26H+P9ogfn/VG72/0lo9f9BY/T/OFrz/zBR7/86Xuv/SGrn/zZU + 2v8WLsT/GCy5/xklp/8YIJr/FhuN/xYWg/8WFH3/FRF2/xQPcv8UEHP/FBFy/xMRcf8UEnT/FRN6/xgV + h/8bF5v/Hhmv/yIcxv8lHtj/Jh/h/ycg5/8nH+T/Jh/i/yYe4P8mH+D/Jh7f/yYf4P8mH+D/Jh/f/yYe + 3/8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mHt//Jh/g/yYe + 3/8mHuT/JB3L/xQSdf8bGJv/Jh/k/yYf4f8mH+D/JR/f/yYf4P8mH9//Jh7g/yYc3/8lKOL/I0fv/yNI + 8P8jR+//I0fv/yNH8P8jR/D/I0bv/yFF7/8mSu//fpH2/5uk9/+UoPf/lKD3/5qm+P9+jfP/KSfg/xYL + 3f+Bie7//////+np6f8hISL/AAAA/wAAAP8AAAD/AAAA/xsrdP8kSvz/I0fw/yNH7/8jR+//I0bv/yNH + 8P8jR/D/I0nw/yQy5/cmHd8yJh3fACYd3wAmHd8AJh/gACYf4AAmH+AAJR7fACUe3wAmH+AAJh7fACYf + 3wAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJiHAACYhwAAmIcAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gXiYe3/8mH+D/Jh7g/x8X3v82N+P/fYny/6Gu+f+JlvT/W2Tr/zY35P8pJeH/Ih3f/x8X + 3v8gF97/IRjf/yIZ3/8iGt//Ixvf/yQc3/8kHOD/JBzg/yUd4P8lHeD/JR7g/yYe3/8mH9//Jh7g/yYe + 4P8mH+D/Jh7g/yYe4P8lHeH/JB3e/2NmkP+0tLz/t7jJ/7a3xv+2t8b/trfH/7a4x/+2t8f/trjH/7q7 + zP+IiI//goKI/66ywf9tWFf/ZCMf/2UoJP9mKST/Zigk/2YoJP9mKST/Zigk/2YpJP9mKCT/Zikk/2Uo + JP9lKCT/Zikk/2YpJP9lKST/ZSgk/2YoJP9mKCT/ZSgk/2UpJP9mKST/aScZ/zk2rv8dSf//I0jv/yNH + 8P8jR/D/I0fw/yNH8P8gRO//M1bx/3OI9f+JmPb/i5r2/4ua9v+Kmvb/jpz3/5Ge9/+Qnvf/kJ73/5Cd + 9/+Rnvf/kJ73/5Ce9/+Vovf/aYD1/x5D7/8jRu//I0fw/yRH8P8jR/D/I0fw/yNH8P8jR+//IETv/yxR + 8P+Kmvb/oKn4/56n+P+bpff/mqT3/5mk9/+bpff/nKb3/5ul9/+ZpPf/lqH3/5Sg+P+Mmvf/fI73/2uB + 9v9hePj/VnP7/0Rn+v81V/X/LE7y/yRI7/8cPeT/GzbU/xoxyf8aLbv/Giao/xkhmP8XGor/FxaB/xYT + ev8TEHL/Eg9u/xMQb/8UEXX/FhOB/xoWmP8fGq//IxzJ/yUf2v8nH+T/Jx/m/yYf4/8mH+H/Jh7g/yYf + 3/8mHt//Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh7f/yYe + 4P8mHuD/Jh7g/ycf5v8eGan/ExJv/yAat/8nIOn/Jh/g/yYf4P8mHuD/Jh/f/yYf4P8mH+D/Jhze/yQ3 + 6P8jSfD/I0bv/yNG7/8jR+//I0fv/yNG7/8jR/D/HkPv/zla8f+OnPf/m6T3/5ql+P+NmfT/Nzjj/x4T + 3v8iIuH/w8r4//////+HiIn/AAAA/wAAAP8AAAD/AAAA/wAAAP8YJ27/JEr9/yNH8P8jR+//I0fw/yNH + 8P8jR+//I0nw/yQ+6/8mH9+XJh3fACYd3wAmHd8AJh3fACYf4AAmH+AAJh/gACUe3wAlHt8AJh/gACYe + 3wAmH98AJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACUe3wAlHt8AJR7fACUe3wAlHt8AJR7fACUe3wAlHt8AJR7fACUe3wAlHt8AJR7fACUe + 3wAlHt8AJR7fACUe330mH+D/Jh/g/yYf4P8mH+D/IRjf/yUg3/9XX+r/kZ72/6Kv+f+Wo/f/gY/z/3B7 + 7/9kae3/VVzp/0pR5/8/RuX/ODfj/zMt4/8uK+H/Kyfg/ykm4P8mI+D/Ih/f/yIe3v8fF9//Hxbe/x8X + 3v8gGN//IBjf/yAY3/8gGN//IBfg/x4X4f9VW5X/srO5/7i5yP+2t8b/trfG/7a3x/+2t8b/trfH/7a3 + x/+6u8z/iYmQ/4qJkP+us8D/bFRT/2QkH/9lKCT/Zigk/2UpJP9lKCT/Zikk/2YoJP9mKCT/Zikk/2Yo + JP9mKCT/ZSgk/2YoJP9lKST/Zikk/2YoJP9lKCT/Zikk/2YpJP9mKST/Zikk/2goHP9WJ1f/KC7f/yA8 + 9v8jQe//I0Pu/yNF7v8jRu7/I0fv/yBF7/8fRe//J03w/y5V8f8tVPH/LFPx/zNZ8f85XPL/OVzy/zhc + 8v84XPL/OFzy/zhc8v84XPL/OVzx/ypQ8f8hR/D/I0nw/yNJ8P8jSfD/I0jw/yNI8P8jSPD/I0jw/yJG + 7/8kSPD/VG/z/4CS9v+Qnff/mqX3/5ul9/+Zo/f/mKL3/5ah9/+VoPf/laD3/5Wh9/+Woff/lqH3/5ij + 9/+bpfj/nab4/5ql9/+Xovf/lKD4/4iY9/94jfb/aoH3/1h1+f9Gafr/NFj5/yxQ9f8iSPL/HEDt/x07 + 3/8dNtH/GzHE/xsqsf8bI57/GB2P/xYXgf8VE3b/ExBu/xMQcv8XE4L/GhWZ/x8atP8jHM//Jh7c/ycf + 5f8mH+b/Jh7k/yYf4/8mHuH/Jh7g/yYf4P8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/f/yYe3/8mH+L/Jh/h/xoWlP8WFHr/IBu5/yYf4v8mH+L/Jh/g/yYe3/8mH+D/Jh/g/yYd + 3/8lIuH/I0Pt/yNI8P8jRu//I0bv/yNH8P8jR+//I0fw/yNH7/8dQu//Olrx/4GS9v+Kmvf/P1ft/x4s + 5v8cMej/RmXw//X7///t7Or/ISIi/wAAAP8AAAD/AAAA/wAAAP8AAAD/Dhxi/yVK/f8jR/D/I0fw/yNH + 7/8jR+//I0fw/yNF7/8lJuLeJh3fGCYd3wAmHd8AJh3fACYd3wAmH+AAJh/gACYf4AAlHt8AJR7fACYf + 4AAmHt8AJh/fACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+CaJh/g/yYe4P8mH+D/Jh/g/yYf4P8kHN//Hhbf/zIy4/9jbe3/jJn1/56r + +f+irvr/oq/6/5+t+f+dq/n/m6r5/5un+P+Wo/f/jZv2/4aV9P+AjvP/eojy/3OA8P9xfe//a2/u/2xv + 7v9ma+3/WmLr/1tj6/9aYer/Tlbp/01V6f9MVe3/YGif/6emrP+5usn/trfH/7a3x/+2t8f/trfG/7a3 + xv+2t8b/urvL/4+Pl/+DhIn/r7PB/21aWf9kIx7/ZSgk/2YoJP9mKST/Zigk/2YoJP9lKST/Zigk/2Yp + JP9mKCT/ZSgk/2UoJP9mKST/ZSgk/2UoJP9mKCT/ZSgk/2YoJP9lKCT/ZSgk/2YoJP9mKCT/aSga/1wm + Rf86Iqz/JCDn/yIi6v8kJuT/JSfi/yUp4/8lK+T/JCrj/yMr5f8iK+b/Iivm/yIr5v8hLOb/Hy/m/x8v + 5v8fL+b/Hy/m/x8w5v8fMOb/HzLn/x806P8iNun/JDjp/yQ46f8kOOj/JDjp/yQ86v8kPOr/JEDt/yND + 7v8jR+7/Ikjv/x1E8P8jSvD/N1ry/0xq8/9he/T/eIz1/4eW9v+Qnvf/maX3/5ul9/+ao/f/maP3/5ei + 9/+Woff/laD3/5Sf9/+UoPf/laD3/5Wg9/+Yovf/mqT3/5ym+P+apff/mKP3/5Sg9/+Glvb/dYn2/2Z9 + 9f9Sb/b/PV/3/y9U+P8mTfb/HkTy/x1C8P8ePuT/HjnT/x0zxf8dK6//GiOa/xYah/8WFnz/FhN5/xcT + hP8bFZr/IBmv/yIbxf8kHdX/Jh/d/ycf5f8nIOb/Jx/l/yce5P8nH+P/Jh/j/yYf4v8mHuH/Jh/h/yYe + 4P8mHuD/Jh7h/yYf4v8mH+P/Jx/m/yYf4f8fGrD/FBJ1/xQSdP8dGKH/Jh/g/yYf5P8mHuD/Jh7g/yYe + 4P8mH+D/Jhzf/yUs5P8jSPD/I0fw/yNG7/8jR/D/I0fv/yNG7/8jR+//I0fv/x5C7/8mS/H/K0/w/x5E + 7/8jSvD/Fj/v/4qk+f//////lpaX/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/w8cWP8mSvj/I0fx/yNH + 7/8jR+//I0jw/yNH8P8lLeX6JhzfTiYd3wAmHd8AJh3fACYd3wAmHd8AJh/gACYf4AAmH+AAJR7fACUe + 3wAmH+AAJh7fACYf3wAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAIBfeACAX3gAgF94AIBfeACAX3gAgF94AIBfeACAX3gAgF94AIBfeACAX + 3gAgF94AIBfeACAX3gAgF94AJh/gsCYf4P8mHuD/JBvg/yEZ3/8eF9//Hhbe/x0V3v8ZD97/HhXe/y8u + 4f9MT+j/WWTr/2Rw7f9lcO3/ZHDt/2Vy7f9xfe//cn7w/3yG8v+GkPT/jJn1/5Sj9/+hrfr/oq76/6Cs + +f+cqfn/nKn5/5uo+P+bqPj/mqj4/5mm+P+Zpvj/m6n//4GHuP+UlJn/urvM/7a3x/+2t8f/trfG/7a3 + xv+2t8b/trfG/7q7y/+XmKL/eHl7/7W4yP92bnD/YyYh/2UnIv9lKST/Zigk/2YoJP9lKCT/ZSkk/2Yo + JP9mKCT/Zigk/2YoJP9mKCT/Zikk/2YoJP9lKCT/Zigk/2YpJP9lKCT/ZSgk/2UpJP9mKST/Zigk/2Yo + JP9oKRv/aCkf/1UnXf83IrT/Jh7j/yIc6v8lHOL/Jh3f/yYc3/8mHN7/Jhzf/yYb3v8mHN//Jhzf/yYc + 3/8mHd//Jh3f/yYd3/8mHd//Jh3f/yYd3/8mHt//Jh3f/yYd3/8mHd//Jh7f/yYd3/8lHd//Jh7e/yYe + 3/8lIuH/JSfj/yUr5P8lMeb/Izfp/x866/8cPO3/HUHu/yBG8P8nTvH/OFzy/0dm8v9YdPT/boT1/4CQ + 9v+ImPb/kZ73/5qk9/+bpff/m6T3/5qk9/+Yovf/l6H3/5Wg9/+Un/f/lKD3/5Sg9/+VoPf/mKL3/5uk + 9/+cpvj/mqT3/5ij9/+Mm/f/fI/2/2yC9f9WcPT/PmD1/y9T9v8kS/j/HUT1/x5E8/8fQu//ID7f/x85 + z/8dMLn/Gyah/xgfjv8WGIL/GBWB/xYRiP8RDJj/GROn/yEbuv8jHMv/JB3T/yUf2P8mH9z/Jh/f/ycf + 5P8nH+b/Jx/m/ycf4v8mH93/JR7W/yAatv8ZFo3/FBN4/xUTe/8VE3r/ExJz/xsXmP8mHtz/Jh/l/yYf + 3/8mH+D/Jh/g/yYf3/8mHd//JDXo/yNJ8P8jR+//I0fv/yNH7/8jRu//I0fw/yNH7/8jR+//IUXv/yBF + 8P8jR/D/IETv/yNJ8P/O2/7/+Pf1/zMzNP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8QGkH/Jknt/yNH + 8v8jR+//I0fw/yNI8P8lMeb/JhzffCYc3wAmHd8AJh3fACYd3wAmHd8AJh3fACYf4AAmH+AAJh/gACUe + 3wAlHt8AJh/gACYe3wAmH98AJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACAX3gAgF94AIBfeACAX3gAgF94AIBfeACAX3gAgF94AIBfeACAX + 3gAgF94AIBfeACAX3gAgF94AIBfeAh8X378dFN7/HBbe/yYl4P83M+P/SEfm/1xk6f9xduv/eoDu/zM0 + 4v8hF9//Hxbf/x8Y3v8gGd7/IBne/yAZ3v8hGt//Ihvf/yIa3v8iGt//Ix7f/ysr4f82OOT/SEvn/2Zy + 7v+LmPX/m6j4/5qm+f+Zpfj/mKP4/5ii+P+Xovj/lqL4/5ij/f+Jktf/hIWL/7i5yP+2t8f/trfH/7q7 + y/+4ucn/trfH/7a3x/+6u8v/pqaz/29wcP+ys8L/kZSd/2I9OP9lIx7/Zigk/2YpJP9mKCT/ZSgk/2Uo + JP9mKCT/ZSgk/2UoJP9mKCT/ZSgk/2UoJP9mKCT/Zigk/2YoJP9lKCT/Zigk/2YoJP9lKST/Zikk/2Yo + JP9mKST/Zikl/2YpI/9pKRj/Zykg/1goVv87I6f/Jh/f/yIe7P8kHuX/Jh/g/yYf3/8mHt//Jh/g/yYf + 4P8mHuD/Jh7f/yYe3/8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh7g/yYe4P8mHt//Jh7f/yYe + 4P8mHuD/Jh3g/yYd3/8mHN//Jhze/yYd3/8mHd//JiLh/yYn4/8lK+T/IzHm/yA16f8cOez/HD7t/x5E + 7/8hSPD/KlHx/zlc8v9IZfL/VnP0/2mA9P98jfX/hpb2/46c9v+Yo/f/m6X3/5qk9/+apPf/maP3/5ii + 9/+Woff/laD3/5Wg9/+VoPf/l6H3/5qk9/+cpvj/mqT3/5ij+P+Mm/b/eo72/2Z99f9QbfP/Olry/ytQ + 9P8hSPb/HUT3/x9F9f8gRfL/IUHk/xo10P87TMD/ZG2q/zE4j/8UFX//ExB1/wwJd/8RDYH/GBOJ/xkW + kf8aF5r/Gxed/xsXnv8aF5f/GRaN/xcUgP8UEnX/FBJ1/xUTev8VE3v/FBN7/xQTev8TEnL/GheQ/yUe + 1v8nIOf/Jh7f/yYf4P8mH+D/Jh7g/yYe3/8kPOr/I0nw/yNG7/8jR+//I0fw/yNG8P8jRu//I0fv/yNH + 7/8jR+//I0fv/xo/7/9OcPP//f///7Szsf8BAAH/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/EBtD/yZK + 7v8jR/L/I0jw/yNI8P8lMeb/JhzfliYc3wAmHN8AJh3fACYd3wAmHd8AJh3fACYd3wAmH+AAJh/gACYf + 4AAlHt8AJR7fACYf4AAmHt8AJh/fACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AA9OeQAPTnkAD055AA9OeQAPTnkAD055AA9OeQAPTnkAD05 + 5AA9OeQAPTnkAD055AA9OeQAPTnkAD055AtDQ+XNZGzq/5GT8f+psPX/wsb5/9TU/P/V1/z/2dr8/9re + /f9OVeb/IBff/yYf4P8mHuD/Jh/g/yYf4P8mHt//Jh/g/yYe4P8mHuD/JR7g/yUe4P8kGt//Ihjf/x8W + 3v8eF97/MC7i/1Ja6f9xfPD/go7z/4eW9P+LmfX/j532/5Og9v+Zpvn/mqf4/3p9j/+rq7b/ubrK/7S1 + xP+UlJ3/q6u5/7i5yf+2t8b/t7jI/7O0w/9zdHX/m5ym/7i7y/95eXv/Yjcy/2UjHv9mKCP/Zikk/2Yo + JP9mKCT/Zigk/2UoJP9mKST/Zigk/2YoJP9mKST/Zikk/2YoJP9lKCT/Zigk/2YoJP9mKCT/Zikk/2Yp + JP9lKCT/Zikk/2YpJP9lKST/Zigl/2YoIv9pKBn/aSkb/1spR/9BJJT/KyDS/yIc6f8jG+X/JR3g/yYf + 4P8mH+D/Jh/g/yYe3/8mHt//Jh7f/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mHuD/Jh/g/yYf3/8mHt//Jh/g/yYe4P8mHuD/Jh/g/yYe4P8mHd//Jhzf/yYc3/8mHd//Jh7g/yYj + 4P8lKOP/JC7l/yIz6P8fOev/HDzt/xxB7v8dRfD/H0bw/yhO8f81WPH/QmDy/09s8/9he/T/dIf1/4KS + 9v+Kmvb/lKH3/5ul9/+apff/mqT3/5qk9/+Yovf/lqH3/5Sg9/+VoPf/l6L3/5uk9/+bpvf/mqX3/5ei + 9/+Hl/b/doj1/1958/9FZPP/M1Ty/yVM8/8QOvT/V3b6/+Tv//+dtfn/Gjzh/zNM0f9pdbn/QUud/xYa + hf8TEnX/Eg9t/xMPbf8TEHD/ExFz/xQSdv8UE3j/FRN7/xUTe/8VE3r/FRN7/xQTev8VE3v/FRJ6/xQS + dP8YFoj/JB7P/ycf5/8mHuD/Jh/g/yYf4P8mHd//JiLg/yNA7P8jSfD/I0fv/yNH8P8jRvD/I0bv/yNG + 7/8jR+//I0fw/yNG7/8XPO7/lq36//////9VVFX/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/xAY + N/8mSOj/I0n0/yNH8P8lMOb/Jh3flCYe4AUmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJR7fACUe3wAmH+AAJh7fACYf3wAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAx8v6AMfL+gDHy/oAx8v6AMfL+gDHy/oAx8v6AMfL + +gDHy/oAx8v6AMfL+gDHy/oAx8v6AMfL+gDHy/og0NL74dfZ/P/Z2vz/19f8/9PT/P/Q0Pv/0M/7/8/Q + +//W2Pz/aWzq/x0V3v8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mH9//Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/JR7f/yIa3/8fF9//IBnf/yUi4P8tLuH/MjTi/zg44/88POT/QkHm/1Ja8f9sdLH/k5SU/7y+ + zv+urr3/YmJg/4mKkP+6u8z/trfG/7a3xv+7vMz/k5Sb/3R0df+4ucj/srXE/3t7f/9jPjr/ZCUf/2Ul + IP9mJyP/Zigk/2YoJP9mKCT/Zikk/2YoJP9mKCT/Zigk/2UoJP9lKCT/ZSgk/2UoJP9mKST/Zigk/2Yo + JP9lKST/Zigk/2YoJP9mKCT/Zigk/2YpJP9mJyP/Zici/2UkIP9oJRf/aSQS/14nNf9ILIH/LSnI/x4Z + 4f8fF+H/JBvg/yYe3/8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7f/yYe4P8mH+D/Jh/g/yYe + 4P8mH9//Jh/g/yYf4P8mH9//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mHuD/Jh3f/yYc3/8mHd//Jh7f/yYi4f8mJ+P/JSzk/yQ15/8iOur/Hz7t/xxB7v8bQ/D/HUXw/x5F + 8P8jSfD/L1Pw/zta8f9JZvP/Wnb0/22C9f9+j/X/iJj2/5Kg9/+bpPf/mqT3/5ql9/+ao/f/l6L3/5Wg + 9/+Voff/maP3/5ul9/+bpff/mqX4/5Ge9/9/kfb/Y3r0/3SN9f/X4P7/r8P8/yBH8/85Yfn/0eH//563 + /P8hROn/IDrV/x4xu/8aJJ//GBmH/xUSd/8UEHT/FBF1/xQSef8UE3r/FRN7/xUTe/8VE3r/FRN7/xUS + e/8VEnv/FBN1/xYUgP8iHMX/Jx/n/yYf4P8mHuD/Jh/g/yYd3/8lI+H/JD/r/yNJ8P8jR/D/I0fw/yNH + 7/8jR+//I0fw/yNH8P8fQ+//Jkzv/9jk///d29b/DQ0O/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8PFSj/JUri/yRE8v8mK+T6JhzfeSYd3wAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmH+AAJh/gACUe3wAlHt8AJh/gACYe3wAmH98AJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gANLS+wDS0vsA0tL7ANLS+wDS0vsA0tL7ANLS + +wDS0vsA0tL7ANLS+wDS0vsA0tL7ANLS+wDS0vsA0tL7NdHR+/XPz/v/z8/7/8/P+//Pz/v/z8/6/87P + +//Oz/v/29z9/4WE7/8bFN7/Jh7g/yYe4P8mH+D/Jh/g/yUf3/8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/yYe3/8mHuD/Jh7g/yYe4P8lHN//Ixrf/yIZ3/8hGN7/IBje/x8V3v8YEd//XWnh/3+B + iP+ys7//tbbG/4SFiv+Xl6H/ubrK/7a3xv+2t8b/t7nI/7W2xv95eXz/fn6B/7i6yv+6vc3/jI+W/2hT + Uf9iMSz/Yygj/2QkH/9lJB//ZSQg/2UkIP9lJCD/ZSQg/2UlIP9lJSD/ZSUg/2YmIf9lJiH/ZiUh/2Yl + IP9lJSD/ZSUg/2UkIP9mJCD/ZSUg/2UkIP9kJB//ZCgk/2MtJ/9jNC//Y0A9/2pUUv92aGP/hX51/5CS + nf+Bh83/SU3b/yUg4P8cFOL/Ihrh/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh7f/yYe3/8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/f/yYe3/8mHt//Jh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh3f/yYc3v8mHN//Jh3f/yYi4P8mKOP/JS3l/yQ1 + 5/8jPev/IkHt/yBF7/8dRPD/HEPw/x1E8P8dQ/D/IUbw/yxQ8P85WfH/SGXy/1p18/9xhfX/gJH1/4yc + 9v+YpPj/m6X3/5ql9/+ZpPf/l6L3/5ag9/+Xoff/mqP3/5ei9/+rtPn/6uz+/93j/f95jfX/cYr1/9Xf + /f+Wrvn/HETy/xxD9f8eRfb/H0b1/yJD5/8gOtH/Hiyx/xkekv8WFH3/Ew9z/xMQdP8UEnj/FRN6/xUT + e/8VE3v/FRN7/xUTe/8TEnb/FRN7/yAbuf8mH+X/Jh/i/yYf3/8mH+D/Jhzf/yYg4P8lOOj/I0nw/yNI + 8P8jR/D/I0fv/yNG8P8jRu//Gj7v/1R19P//////hYSE/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/DhUq/yI23f8lIuXhJhzfUSYc3wAmHd8AJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmHt8AJh/fACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ADOz/oAzs/6AM7P+gDOz/oAzs/6AM7P + +gDOz/oAzs/6AM7P+gDOz/oAzs/6AM7P+gDOz/oAzs/6AM7P+jXPz/v1z8/7/8/P+//Pz/v/z8/7/8/P + +v/R0fv/19f8/+Xl//+MkPD/HBbe/yUe4P8mH+D/Jh/g/yYf4P8lH9//Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYe4P8mH+D/Jh7g/yYf3/8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8iGd//MC/h/4ya + /f+AhrX/j4+S/7u9zf+6u8v/uLrK/7a3xv+2t8b/trfH/7a3xv+5usr/r7C+/3N0dv9zc3X/oaKt/7q7 + y/+prbr/iImP/3dub/9vXl//aVBP/2VGQv9kPzv/Yzw4/2M2Mv9jNTD/YzUw/2MzL/9jMCv/Yy8q/2Ix + LP9jNDD/YzQw/2M0MP9jNjP/ZDw4/2VAPP9mSkf/a1lY/3Npaf9+eX3/jI6V/5ufqf+orbn/srXF/7q8 + zP+8vcr/wsPI/77By/+codL/Ymjb/zEx4v8iGuX/Jh7g/yYf3/8mHuD/Jh/g/yYe3/8mHuD/Jh7f/yUf + 3/8mH+D/Jh/g/yYf4P8mHuD/Jh7f/yYf4P8mH+D/Jh7g/yYf4P8lH9//Jh7f/yYf3/8lHt//Jh7g/yUe + 3/8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/JR/g/yUe4P8mH9//Jh7f/yYe4P8mHuD/Jhzf/yUc + 3v8mHN//Jh3f/yYj4f8lKeT/JTHm/yQ56v8kQOz/I0Xv/yJH8P8gRvD/HUTw/xxC8P8dQu//HULv/yNJ + 8P8xVPD/QF/y/1Zy8/9sgvX/gJH2/46c9/+Zo/j/nKX3/5ul9/+VoPf/oq34/+Xo/f/k5/3/oKn4/6ix + +P/u7/7/w8r7/3aJ9f9kfPP/R2by/zJU8f8iSPP/HUT2/x1F9v8gRO//Ij7c/x8xvP8ZIJf/FhR8/xQP + c/8UEHb/FRJ6/xUTev8UE3v/FRN7/xQSd/8TEnT/Hhqp/yYf4f8mH+T/Jh/g/yYf4P8mHeD/Jh3f/yUt + 5P8kQez/I0nw/yNJ8f8jR/D/I0bv/xY97v+ftv3/+/n0/zExMv8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/xIWKP82MtDKIBblKCYc3wAmHN8AJh3fACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh7fACYf3wAmHt8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AA0dH7ANHR+wDR0fsA0dH7ANHR + +wDR0fsA0dH7ANHR+wDR0fsA0dH7ANHR+wDR0fsA0dH7ANHR+wDR0fs0z8/79c/P+//Q0Pv/1dT8/9nZ + /P/Z2v3/y8/6/66z9f+Ei+//QEDk/yIa3/8mH+D/Jh/g/yUe3/8lHt//Jh/g/yYf4P8mHuD/Jh/g/yYf + 3/8mH9//Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8kHOD/Ihzf/3B7 + 7/+bp/v/kp3x/3Z4h/+pqbL/urzM/7a3x/+2t8b/trfH/7a3x/+2t8b/trfG/7q7y/+xssD/f36D/2Ni + Yf90dHX/k5Ob/7K0wv+2usr/rbG//6eruP+kqLP/mp2n/5ebpf+Qk5v/jpKa/4+Tmv+PkZn/iIaN/4aE + iv+Hh43/j5Ka/4+Smv+Pkpr/kJOc/5eapP+bnqn/pKm0/6ituf+wtMP/ubzM/7u8zf+9vs7/vr/Q/7y9 + zv+7vM3/uLrK/7Cxv/+oqLP/oqOk/5SVkv5vdITpSk2hwScj3scmHuH/Jh7g/yYe4P8mH+D/JR/g/yYe + 3/8lH9//Jh/g/yYf4P8mHuD/Jh7g/yYf3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8lH9//JR7f/yYf4P8mHuD/JR/f/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8lHt//Jh/f/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jhze/yYc3/8mHN//JR/g/yUl4v8lLOX/JTbo/yQ+7P8kRO7/I0jw/yNJ + 8P8hR/D/H0Xw/xxC7/8cQe//HEHv/yNI8P8zVPH/Q2Hx/1t18/93ivX/hZX2/6Cs+P/l5/7/5uj9/56o + +P+irfn/6ez9/8DH+/+WoPj/m6X3/5ql+P+Pnff/fY/2/2N89P9GZfL/MFPy/x9G9P8cRPb/IUXy/yI/ + 3P8dLbT/GBqJ/xQQdP8TEHT/FBJ5/xUTev8VE3v/FRN5/xQSdP8bF5n/JR7Y/ycf5v8mH+D/Jh/g/yYf + 4P8mHN//JiHg/yUw5v8kP+z/I0jv/x9G8P8rUvH/4O3//8PBvf8CAgP/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8hIiL/rK27hCAW5QAmHN8AJhzfACYd3wAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYe3wAmH98AJh7fACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAMvM+wDLzPsAy8z7AMvM + +wDLzPsAy8z7AMvM+wDLzPsAy8z7AMvM+wDLzPsAy8z7AMvM+wDLzPsAy8z7N9XV/PjZ2/3/1tf8/73D + +P+bn/P/aG/r/0FB5P8lJOD/GxTe/yEZ3v8nH+D/Jh/f/yYe4P8lHt//Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mH9//Jh/f/yYe3/8mHt//JR/g/yYe4P8lH9//JR7f/yYe4P8mHuD/Jh/f/yYe3/8lHt//Hhfe/1hi + 6/+ap/j/laD3/5ai/v+GjtD/fHt+/7Kzv/+6vMv/trfG/7a3xv+2t8b/trfG/7a3xv+2t8b/ubrK/7i5 + yf+amqT/ent+/3Bwcf+XmKL/u7zM/7m6yv+6u8v/urvL/7q7zP+6u8z/urzM/7q7zP+6vMz/urzM/7q8 + zf+6vc3/urzM/7q7zP+6u8v/urvM/7q8zP+7vMz/u7zN/76/z/++wND/vL7P/7u8zP+3uMj/rK27/6Wm + sf+XmKL/iYqQ/H19gONzc3W9ampqnmZnZm1mZmRCbmxhJmJoegQoJ9sJJR3iSSYe4KUmHuDsJh7g/yYf + 3/8mH+D/Jh/g/yYf4P8mHt//Jh7f/yUf3/8lH9//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yUe3/8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8lHt//JR7f/yYf + 3/8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yUe3/8mHd//Jhzf/yYc3/8mHt//JiTh/yUp + 4/8lNOf/JDzr/yND7f8jSPD/I0nw/yNJ8f8hRu//HkPv/x1C7/8cQe//HkPv/yZK8P9ObfP/0dv9/9bd + /f+Jmvb/qLL5/+3u/v+3v/n/k573/5Sf9/+VoPf/l6H3/5qk9/+bpff/mqX4/42b9v90iPX/T23y/ytO + 8f8bQvP/Ikn3/yNG6/8gNcb/GSCV/xQSeP8UD3P/FRJ4/xUTe/8UE3r/FBJ0/xcVhP8hHL//Jh/l/ycf + 5P8mH+D/Jh7g/yYe4P8mHN7/Jh/f/yUn4/8bKOX/WHDx//////9xcHD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/LCws/8HBvavGxsYAxsbGAMbGxgDGxsYAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmHt8AJh/fACYe3wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACJmvAAiZrwAIma + 8ACJmvAAiZrwAIma8ACJmvAAiZrwAIma8ACJmvAAiZrwAIma8ACJmvAAiZrwAIma8EejqvT/hYrw/1JW + 5/8yMuL/HRrf/xwU3v8fF9//JBzg/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yUf4P8lHuD/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8lH+D/Jh/f/yYe4P8mH+D/Jh/g/yYe3/8mH9//Hxfe/0hM + 5/+Vovf/lqL3/5Sg9/+UoPj/l6P+/4GIvv99fH7/r6+5/7y9zv+3uMj/trfG/7a3xv+2t8b/trfH/7a3 + x/+3uMf/u7zM/7m6yv+ztMP/t7fH/7a3xv+2t8b/trfH/7a3x/+2t8b/trfH/7a3x/+2t8f/trfH/7a3 + x/+2t8b/trfG/7a3xv+3uMj/ubrK/7u8zf+6u8z/urvL/7W2xf+rrLn/oqOu/5WVnv+FhYv7e3t+2XBw + crNqammVZ2dlYGdnZTpoaGYfaGdmBGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gKiYf + 4HwmHuDMJh/g/yYe4P8mH+D/Jh7f/yUf3/8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/JR7f/yYe + 4P8mHt//Jh7g/yYe4P8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/f/yYf + 4P8mHuD/JR7f/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh7g/yYd + 4P8mHd//Jhzf/yYe3/8mIuH/JSnk/yUz5/8kPOv/JETt/yNI8P8jSfH/I0nw/yNH7/8TOe7/U3T0/9Xh + /f+ds/n/HkXw/3KL9v/i6P7/jJ33/4ya9v+bpfj/m6X4/5ei9/+UoPf/lKD3/5Wg9/+Xovf/mqT3/5yl + +P+Kmff/VnDz/yJH7/8fRPL/JEr3/yRI8f8hO9T/GyWi/xYVfv8UD3L/FBF2/xUTe/8UEnb/FBN3/xwY + n/8kHtT/JyDn/yYf4/8mH+D/Jh/g/yYe4P8mHN//GhHd/56j9P/7+vP/Ly8w/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/zg4OP/Dw8TPxsbGDcbGxgDGxsYAxsbGAMbGxgDGxsYAxsbGAMbG + xgDGxsYAxsbGAMbGxgDGxsYAxsbGAMbGxgDGxsYAJR7fACUe3wAmH+AAJh7fACYf3wAmHt8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AALSzhAC0s + 4QAtLOEALSzhAC0s4QAtLOEALSzhAC0s4QAtLOEALSzhAC0s4QAtLOEALSzhAC0s4QAtLOFIJiTf/xwV + 3/8dFd7/IRnf/yYd4P8mH+D/Jh7g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/JR/g/yYf3/8mH9//Jh7g/yYe4P8mHuD/Ihrf/zY4 + 5PmNm/XsmKT48JSf9/qUoPf9lKD3/5Sg+P+Wo///g4vG/3Z3f/+cnKH/uLnJ/7u8zP+3uMj/trfG/7a3 + xv+2t8b/trfG/7a4x/+3uMf/t7nI/7a3xv+2t8b/trfG/7a3x/+2t8f/trfG/7a3xv+2t8f/trfH/7e4 + yP+5usr/u7zM/7q7y/+6usv/tLXF/6mquP+goKv/kZKZ/4SEifR4eHrRb29wrWlpaItmZmVTZ2ZmNGho + ZxdwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf + 4AAmH+AAJh7gDSYe30kmHt+gJh/g5iYf4P8mH9//Jh/g/yYf4P8mH9//Jh/g/yYf4P8mH+D/Jh7f/yYe + 3/8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYe + 3/8mH+D/Jh/g/yUe3/8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe + 3/8mH+D/Jh/g/yYf4P8mH+D/Jh3g/yYc3/8mHN//Jh7f/yYi4P8lKeP/JTPn/yQ97P8gQO3/Iknw/6a7 + +v/a5v7/Y4P1/w007v+Goff/ucv8/ytQ8P8wUfD/UW3z/3GF9f+Pnfb/m6b4/5qk9/+Woff/laD3/5Sg + 9/+VoPf/mKL3/52n+P92i/X/Kk7w/x9D7v8jR/H/JEn1/yRK9f8iQN7/HSux/xcZiP8UEHP/FBB2/xQS + ef8UEnT/FhSA/x4arv8mHtv/Jx/n/yYf4/8mH+D/Ihrf/yop4f/X4f7/1dPN/wUFBf8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP9MTE3/zc3N7sTExRzExMUAxMTFAMTExQDExMUAxMTFAMTE + xQDExMUAxMTFAMTExQDExMUAxMTFAMTExQDExMUAxMTFAMTExQAlHt8AJh/gACYe3wAmH98AJh7fACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACMb + 3wAjG98AIxvfACMb3wAjG98AIxvfACMb3wAjG98AIxvfACMb3wAjG98AIxvfACMb3wAjG98AIxvfSCQb + 3/8lHt//JB3g/yEY3/8cFN7/GhPe/xwW3v8dF97/Hxff/yYe4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf + 4P8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mHt//Jh7g/yYf4P8mH+D/Jh7f/yYf4P8mHuD/Jh/g/x4V + 3vNNT+hMnar4IpSg9yiUn/cxlKD3P5Wg91CUn/dslaD4hZej/5mNmOWsdXiTxH18fvOfoKr/trfG/7y8 + zf+6u8z/uLnI/7e3x/+3uMf/trfG/7a3x/+2t8f/trfH/7a3x/+3uMf/uLnJ/7q7y/+7vM3/urzM/7m6 + yv+1tsb/qqu4/6Chqv+RkZn/goKI8HZ2eMtub26maGhngGdnZU9nZ2YraGhnEW9vcABpaWgAZmZlAGdm + ZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4CAmHuBmJh7fuSYe3/kmHuD/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH9//Jh/f/yUf3/8lHt//Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh7f/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe3/8mHd//JR3e/yYc3/8mHt//GRjg/3KB + 7//b4v3/o7b5/x9E7v8oTvD/wdH8/5Ss+f8cQe//HkLv/x1B8P8fRO//MVPw/1Jv8/94jPX/laH3/5ym + +P+Zo/f/laD3/5Sg9/+Un/f/nqf3/3eM9f8jSO//IUXw/yNH7/8jR/D/I0n0/yRK9v8jROn/HzPC/xke + k/8UEXf/FBBz/xQRdv8UEnX/FxWH/x8asv8lHtn/Jh/h/x8W3/9OVef//////5OTkv8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/cHBx/8/PzvvDw8Q1w8PEAMPDxADDw8QAw8PEAMPD + xADDw8QAw8PEAMPDxADDw8QAw8PEAMPDxADDw8QAw8PEAMPDxADDw8QAw8PEACYf4AAmHt8AJh/fACYe + 3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAgFt8AIBbfACAW3wAgFt8AIBbfACAW3wAgFt8AIBbfACAW3wAgFt8AIBbfACAW3wAgFt8AIBbfACAW + 31obE97/HRbf/yUh4P84O+P/UU/n/2dp6v96hO7/h4/w/2hy6v8mIeD/JR7g/yYe4P8mHuD/Jh/g/yYf + 4P8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/f/yYe3/8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf + 4P8kHOCQTU/oAJ2q+ACUoPcAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QdmZmIwaWlnjnp7 + feSUlZz/paay/7Gywf+4usn/ubrK/7m7zP+6u8z/ubrK/7m6yv+5usr/t7jI/6+vvf+mp7P/n5+p/5CR + mf+Dg4j1dnZ41G5ub6hoaGh6Z2dlSWdnZidoaGgMbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZm + ZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd + 4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wImHuAzJh/ggiYe4M0mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYe3/8mH+D/Jh/g/yYf + 4P8mH+D/Jh7f/yYf4P8mH9//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7f/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/HhTf/z9C + 4//Hy/j/z9T7/0xR5v8QD9//Z3fu/9ji/f9fe/L/Gj/u/yNJ8P8jSfD/Ikfv/x9E7/8cQe//Ikfv/zhZ + 8f9fePP/g5T2/5ik9/+cpff/mKL3/5ah9/+dpvf/Smjy/x1B7/8jR/D/I0fv/yNH7/8jR/D/I0jz/yRK + 9/8jR+//ITrR/xslov8WFX7/FBBy/xMPb/8TEG7PHhqoVyUe5FQGANtjjJHxuf////9sbG3/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/CAgI/Kampv7Kysv/xMTEYMTExADExMQAxMTEAMTE + xADExMQAxMTEAMTExADExMQAxMTEAMTExADExMQAxMTEAMTExADExMQAxMTEAMTExADExMQAJh7fACYf + 3wAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAI0bvACNH7wAjR+8AI0bvACNG + 7wAjRu8AP0DkAD9A5AA/QOQAP0DkAD9A5AA/QOQAP0DkAD9A5AA/QOQAP0DkAD9A5AA/QOQAP0DkAD9A + 5AA/QOReZ2vr/4uT8f+ztfb/wcf5/9DT+//Z2v3/2Nj9/+Df/v+vtvb/KSbh/yMc4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYe + 4P8mH+DWJh/gFyYf4ACdqvgAlKD3AJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlp + ZwBoaGcdZ2dmW2xra5lzc3W8fX1/5IWGjPeOj5f9kZKa/omKkPmGhov2f3+D6nh4etFycnOzbGxtmWlp + Z3lnZ2ZJZ2dmJ2lpaBJubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlp + aABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn + 2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AMJh/gRiYf + 4J0mH+DiJh/g/yYf4P8mHuD/Jh7f/yYf4P8mHt//Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYe4P8mHuD/Jh7g/yYe4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH9//JBzg/x4Z + 3/+Wn/H/4OT9/4KL7/8YD93/Lyzh/7zD9/+7wvf/LzDi/yIk4v8lM+j/JD7r/yRF7v8jSe//I0nw/yJH + 8P8eQ+//HEHv/yZL8P9AYfH/Z3/0/4WW9v+Xovf/oKn3/1x38/8cQu//I0bv/yNH7/8jRvD/I0fw/yNG + 8P8jR+//I0fx/yRK9f8kSfT/Ij/d/xwssf8ZIpuoFBN4Dx4aqAAlHuQABgDbAOzr/ab+/v7/VFVV/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/woKCrCrqqvNysrL/8PDxIbDw8QAw8PEAMPD + xADDw8QAw8PEAMPDxADDw8QAw8PEAMPDxADDw8QAw8PEAMPDxADDw8QAw8PEAMPDxADDw8QAw8PEAMPD + xAAmH98AJh7fACYf4AAmH+AAI0fvACNH7wAjRvAAI0fwACNG7wAjR+8AI0fvACNG7wAjR+8AI0fvACNG + 7wAjRu8AI0bvALS99gC0vfYAtL32ALS99gC0vfYAtL32ALS99gC0vfYAtL32ALS99gC0vfYAtL32ALS9 + 9gC0vfYAtL32etjZ/f/Y2P3/1dX8/9PT/P/R0Pv/z8/7/8/P+//T0vv/wcT5/zMu4v8iGt//Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mHt//JR7f/yYf4P8mH+D/Jh7f/yYe3/8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf + 4P8mH+DzJh/gPSYf4AAmH+AAnar4AJSg9wCUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZm + YgBpaWcAaGhnAGdnZgBsa2sAampoBWloZxloaGYpaGhmP2dnZkRnZ2YuZ2dmJWhoZh1paWgQcnJzAGxs + bQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9v + cABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJo + egAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gGCYf4F4mH+CuJh7g8yYf3/8mH+D/Jh7f/yYf4P8mH9//Jh/f/yYf4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mHt//Jh/f/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8mHuD/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf3/8mH+D/Jh7g/yYf4P8mH+D/Ixvf/xcQ + 3f9mb+r/2uD8/6ev9P8nI+D/GBLd/4uT8f/e4/3/bnXs/x0U3v8mHd//Jhzf/yYf4P8mJOL/JS/l/yU5 + 6f8kQ+3/I0jw/yNJ8P8hRvD/HkLv/x1D7/8pTfD/PF3x/0lp8/8uUPD/IUXw/yNG7/8jR/D/I0bv/yNH + 8P8jR/D/I0fv/yNH7/8jR+//I0fx/yNJ8/8kSvb/JEjx5CJC5p8gQutFJEXvAc7Z/AL////T8vLy/zMz + M/8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP89PT1MyMjIm8XFxv/Dw8ShxMPEAMTD + xADEw8QAxMPEAMTDxADEw8QAxMPEAMTDxADEw8QAxMPEAMTDxADEw8QAxMPEAMTDxADEw8QAI0fvACNG + 7wAjR/AAI0fvACNH7wAkR/AAI0fwACNH7wAjR+8AI0bwACNH8AAjRu8AI0fvACNH7wAjRu8AI0fvACNH + 7wAjRu8AI0bvACNG7wDU0/wA1NP8ANTT/ADU0/wA1NP8ANTT/ADU0/wA1NP8ANTT/ADU0/wA1NP8ANTT + /ADU0/wA1NP8ANTT/H/Pz/v/z8/7/8/P+//Pz/v/z8/7/8/P+//Pz/v/0dD7/9HS+/9ESOX/IBjf/yYe + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mHt//Jh/g/yYe4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/gcSYf4AAmH+AAJh/gAJ2q+ACUoPcAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ + 0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJy + cwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGho + ZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5s + YQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4CkmH+B4Jh/gxSYf4PwmH+D/JR7f/yYe3/8mH+D/Jh/g/yYe + 4P8mH+D/Jh7g/yYf4P8mHuD/Jh7f/yYf3/8mHuD/Jh7f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/f/yYf4P8mH+D/Jh/g/yYf4P8mH9//Jh/g/yYf4P8kHOD/HBPe/yEe + 3/9ze+z/1tv7/6+39f8vLeH/FhHd/3qD7f/c4fz/oar0/ygm4P8jG9//Jh/g/yYf4P8mHuD/Jh7g/yYd + 3/8mHt//JiLg/yUq5P8lNun/JUHs/yNI7/8jSPD/IUfw/x5E7/8eQu//IUTv/yNH8P8jR+//I0fv/yNH + 7/8jR/D/I0bv/yNG7/8jR+//I0fv/yNG7/8jR+//I0fw/yNH8f8jSPL/I0jx/ho/8LxCYPFx/v//8OXk + 4/8YGBn/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADKZmZmBtfX137DwsP/xMTEycTD + xAnEw8QAxMPEAMTDxADEw8QAxMPEAMTDxADEw8QAxMPEAMTDxADEw8QAxMPEAMTDxADEw8QAxMPEACNH + 7wAjRu8AI0fwACNH7wAjR+8AJEfwACNH8AAjR+8AI0fvACNG8AAjR/AAI0bvACNH7wAjR+8AI0bvACNH + 7wAjR+8AI0bvACNG7wAjRu8AqbL2AKmy9gCpsvYAqbL2AKmy9gCpsvYAqbL2AKmy9gCpsvYAqbL2AKmy + 9gCpsvYAqbL2AKmy9gCpsvaT0dH7/8/P+//Pz/v/z8/7/9DP+//Q0fv/1tX8/9zb/f/R1fz/Q0fl/yAY + 3/8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mHt//Jh/g/yYf4P8lHt//Jh/g/yYf + 4P8mH+D/Jh/fmyYf4AAmH+AAJh/gACYf4ACdqvgAlKD3AJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y + 5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlp + aABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdn + ZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZm + ZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AYmH+A5Jh/gkCYf4NwmHuD/Jh/g/yYf + 4P8mH+D/Jh7g/yYd3/8mHuD/JR7g/yUd3/8lHd//Jh7g/yUd3/8mHuD/Jh7g/yYe3/8lHd//JR3f/yUd + 3/8lHd//JR7g/yUd3/8lHd//JR7f/yUd4P8kHOD/JB3g/yQd4P8kHOD/Ihng/x4U3/8bFN7/JiPg/1RZ + 6P+osPT/1tz8/46U8P8kIuD/NTXj/5qh8v/Z4Pz/n6j0/zIx4v8gGN//Jh/g/yYf4P8mH+D/Jh7g/yYe + 4P8mHuD/Jh7f/yYd4P8mHd//Jh3f/yYg4P8mKOP/JTTo/yRB7P8jR+//I0nw/yNH8P8jR/D/I0fv/yNG + 7/8jR+//I0fv/yNG7/8jR+//I0fw/yNH8P8jRu//I0fw/yNH7/8jR+//I0fv/yNH7/8cQe//QmTy//7/ + ///Pz83/BQUF/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAAY2ZmZgDW1tdkw8PD/8TE + xOrExMQXxMTEAMTExADExMQAxMTEAMTExADExMQAxMTEAMTExADExMQAxMTEAMTExADExMQAI0fvACNG + 7wAjR+8AI0bvACNH8AAjR+8AI0fvACRH8AAjR/AAI0fvACNH7wAjRvAAI0fwACNG7wAjR+8AI0fvACNG + 7wAjR+8AI0fvACNG7wAjRu8AI0bvAMPG+QDDxvkAw8b5AMPG+QDDxvkAw8b5AMPG+QDDxvkAw8b5AMPG + +QDDxvkAw8b5AMPG+QDDxvkAw8b5oNDQ+//Q0Pv/09L8/9nY/f/X2Pz/zc/7/6yy9v+EiO//VVnn/yci + 3/8lHd//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/JR7f/yYf4P8mH+D/Jh/f/yYe + 4P8mHuD/Jh7gySYe3xEmHt8AJh7fACYe3wAmHt8AJh7fAJSg9wCUn/cAlKD3AJWg9wCUn/cAlaD4AJej + /wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGho + ZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdn + ZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZn + ZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AQJh/gUiYf + 4KQmHuDqJh7g/yEa3/8dGN7/Hxrf/x8Z3/8fGd7/Hxrf/x8a3/8fGt//Hxne/x8a3/8fGt7/Hhne/yAa + 3/8iHd//Ix3g/yQe4P8jHt//JB7f/yMd3/8mIOD/KSPg/ygi4P8rJOH/Lynh/zs/4/9YWuj/gYbu/663 + 9f/ByPj/m6Py/1Rd5/9CR+T/hYzu/8fR+f+zvPb/YWjq/ycj4P8hGN//Jh/f/yYe4P8mHuD/Jh/g/yYe + 4P8mHuD/Jh/g/yUe3/8mHuD/Jh/g/yYf4P8mHeD/Jhzf/yYd3/8mH9//Jijj/yU06P8kQOv/I0fv/yNJ + 8P8jSPD/I0fv/yNG7/8jR+//I0fv/yNH8P8jR/D/I0bv/yNH7/8jR/D/I0fv/yNH7/8jRu//Gz/u/1R3 + 9P//////m5ub/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAAyAAAAArS0tIA0tLSQsTE + xPvExMT9xMTELcTExADExMQAxMTEAMTExADExMQAxMTEAMTExADExMQAxMTEAMTExAAjR+8AI0fvACNH + 7wAjRu8AI0fvACNG7wAjR/AAI0fvACNH7wAkR/AAI0fwACNH7wAjR+8AI0bwACNH8AAjRu8AI0fvACNH + 7wAjRu8AI0fvACNH7wAjRu8AI0bvACNG7wDHy/oAx8v6AMfL+gDHy/oAx8v6AMfL+gDHy/oAx8v6AMfL + +gDHy/oAx8v6AMfL+gDHy/oAx8v6AMfL+p/b2v3/0tX8/8DD+f+WnfL/bW/r/0JD5P8nI+D/HBff/x8W + 3/8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh7g/yYe4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g4CYe4CQmHt8AJh7fACYe3wAmHt8AJh7fACYe3wCUoPcAlJ/3AJSg9wCVoPcAlJ/3AJWg + +ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdn + ZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGho + ZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpq + agBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe + 3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJRzgHCYh4GVda+m5go3u+H6L7v9/jO7/fYnt/3uI7f98iO3/e4jt/3uI7f97iO3/fInt/3+L + 7v+Fku7/kp/y/46c8f+MmvD/jJrx/4yY8f+KlvH/lqDy/5qk8v+WoPL/naPz/6ip9f+VnvH/j57x/42W + 8P92e+z/ZXTq/3l/7f+hqvP/xs75/6u09f9iaOn/KSbh/x0V3/8kHOD/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYe3/8mH+D/Jh7g/yYd3/8mHd//JiDg/yUp + 4/8lNej/JEHs/yNH7/8jSfD/I0jw/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0fw/yNH7/8jRu//I0bv/xs/ + 7v9UdvT//////4mJif8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/QAAAFYAAAAAzs7MAM7O + zB7Ew8Tqw8PE/8PDxFbDw8QAw8PEAMPDxADDw8QAw8PEAMPDxADDw8QAI0fwACNH8AAjR+8AI0fvACNH + 7wAjR+8AI0bvACNH7wAjRu8AI0fwACNH7wAjR+8AJEfwACNH8AAjR+8AI0fvACNG8AAjR/AAI0bvACNH + 7wAjR+8AI0bvACNH7wAjR+8AI0bvACNG7wAjRu8AeoruAHqK7gB6iu4AeoruAHqK7gB6iu4AeoruAHqK + 7gB6iu4AeoruAHqK7gB6iu4AeoruAHqK7gB6iu6fhInv/1RZ5/8yLuH/IRzf/xwV3/8hGN//JBzg/yYe + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf + 4P8lH+D/Jh/g8SYf4EQmHuAAJh7fACYe3wAmHt8AJh7fACYe3wAmHt8AlKD3AJSf9wCUoPcAlaD3AJSf + 9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdn + ZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5v + bgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhn + ZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe + 4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/DBzeux7Ulfnyl9l6f1jaer/ZWvq/2hv6v9pb+v/aG7r/2tx + 6/96fu3/e3/t/3p+7f9tc+v/Zmzq/2ht6/9veuz/d4nt/3OF7P9jder/boHs/2x37P9tcuz/dnrs/4KK + 7/+JmfD/pa30/7a99/+krfX/fILu/0dL5v8lIuD/Hhbf/yQb4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYe + 3/8mHd//Jh3f/yYh4P8lKuT/JDjp/yRD7v8jSfH/I0nw/yNI7/8jRu//I0fw/yNH7/8jR/D/I0fw/yNH + 8P8bQO//VHb0//////+trKz/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wIDB7kAAAAAJEv/AMnI + wgDJyMIIxcTE18PDxP/ExMR+xMTEAMTExADExMQAxMTEAMTExAAjR+8AI0fwACNH8AAjR/AAI0fvACNH + 7wAjR+8AI0fvACNG7wAjR+8AI0bvACNH8AAjR+8AI0fvACRH8AAjR/AAI0fvACNH7wAjRvAAI0fwACNG + 7wAjR+8AI0fvACNG7wAjR+8AI0fvACNG7wAjRu8AI0bvACUh4AAlIeAAJSHgACUh4AAlIeAAJSHgACUh + 4AAlIeAAJSHgACUh4AAlIeAAJSHgACUh4AAlIeAAJSHgoR0X3/8eFN//Ixrf/yMc3/8jG9//IRnf/yAW + 3/8eFN7/IBff/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYe32ImH+AAJh7gACYe3wAmHt8AJh7fACYe3wAmHt8AJh7fAJSg9wCUn/cAlKD3AJWg + 9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdn + ZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGho + aABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGho + ZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf + 4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ASBgu48yMr5kqes9N2Sl/D/k5jx/5SZ + 8f+TmfH/kpfx/5GX8f+Rl/H/k5nx/5SZ8f+Di+//g43v/4SQ7/+Fke//h5Pv/4SQ7/97iO3/f43u/32B + 7f92dez/X2Pp/0dM5f8zMOL/JB/g/xwW3v8gGN//JR3f/yYe3/8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf + 3/8mH+D/Jh/g/yYf4P8mHuD/Jh3f/yYd3/8mIuH/JS7l/yQ86/8kRu//I0nw/yNH8P8jR+//I0fw/yNH + 7/8jR/D/Gz/v/1N28///////5+bl/xkZGv8AAAD/AAAA/wAAAP8AAAD/AAAA/wUGCf8gOrDqJUz/jCRL + /y0gQ/AAycjCAM/NwbHExMT/xMTEnsnHwQDJx8EAycfBAGqG9QAZPu8AI0fvACNH8AAjR/AAI0fwACNH + 7wAjR+8AI0fvACNH7wAjRu8AI0fvACNG7wAjR/AAI0fvACNH7wAkR/AAI0fwACNH7wAjR+8AI0bwACNH + 8AAjRu8AI0fvACNH7wAjRu8AI0fvACNH7wAjRu8AI0bvACNG7wAlHeAAJR3gACUd4AAlHeAAJR3gACUd + 4AAlHeAAJR3gACUd4AAlHeAAJR3gACUd4AAlHeAAJR3gACUd4JMiGd//HhXf/xwV3v8jIN//Lyrh/z5A + 5P9QV+f/Y2fq/1FX5/8lIOD/JR7f/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh7g/yYf4H4mHt8AJh/gACYe4AAmHt8AJh7fACYe3wAmHt8AJh7fACYe3wCUoPcAlJ/3AJSg + 9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGho + ZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdn + ZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdn + ZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe + 3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAMIBrfUCkj + 4J4pJODnKiTh/yok4f8pI+D/KSPg/yok4f8pJOH/JSDg/yQg4P8kH+D/JB/g/yQf3/8iHd//Hxrf/x8Z + 3/8cFd7/GxLe/x4V3v8hF9//Ixvg/yQd3/8lHt//Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mH9//Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yUf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mH9//Jh7g/yYf4P8mH+D/Jh7g/yYc3/8mHuD/JSbi/yU05/8jQe3/I0jw/yNI + 8P8jR/D/I0fv/xtA7/9TdvP//v////////9hYWL/AAAA/wAAAP8AAAD/AAAA/wAAAP8YJmn/Jkz+/yNH + 8f8jR+/0IEPwpyRH7zWts8uKzszB/8PDxL/Jx8EEycfBAMnHwQBqhvUAGT7vACNH7wAjR/AAI0fwACNH + 8AAjR+8AI0fvACNH7wAjR+8AI0bvACNH7wAjRu8AI0fwACNH7wAjR+8AJEfwACNH8AAjR+8AI0fvACNG + 8AAjR/AAI0bvACNH7wAjR+8AI0bvACNH7wAjR+8AI0bvACNG7wAjRu8AIxvfACMb3wAjG98AIxvfACMb + 3wAjG98AIxvfACMb3wAjG98AIxvfACMb3wAjG98AIxvfACMb3wAjG99qNzbj/1pg6P+Ehe//m6Tz/7q+ + +P/Nz/v/0NP7/9zd/f+os/X/JyTg/yQc4P8mH+D/Jh/g/yYe4P8mH9//Jh/g/yYf4P8mH+D/Jh7g/yYe + 4P8mH+D/Jh/g/yYf4JYmH+AAJh7fACYf4AAmHuAAJh7fACYe3wAmHt8AJh7fACYe3wAmHt8AlKD3AJSf + 9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGho + ZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdn + ZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdn + ZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe + 3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa + 3wApI+AAJB3gFiQd4F8kHd+vJB3f8CQc3/8kHeD/JBzg/yUd3/8lHuD/JR3g/yUd4P8lHeD/JR3f/yUe + 4P8lHuD/Jh7f/yYe4P8mH+D/Jh/g/yYf4P8mHt//Jh7g/yYf4P8mH+D/Jh7f/yYf4P8mHt//Jh7f/yYf + 4P8mH+D/Jh7f/yYf4P8mH+D/Jh7f/yYe4P8lH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7f/yYd3/8mHd//JiDg/yUr + 5P8kOur/I0Xv/yNJ8P8bQe//VHbz//z+////////tLS1/wAAAf8AAAD/AAAA/wAAAP8LDyD/JUbe/yRI + 9v8jR+//I0fw/yNH7/8bQPH3TGvl5LC3yv/IxsDnqbPPCxtE8wCjufoAaob1ABk+7wAjR+8AI0fwACNH + 8AAjR/AAI0fvACNH7wAjR+8AI0fvACNG7wAjR+8AI0bvACNH8AAjR+8AI0fvACRH8AAjR/AAI0fvACNH + 7wAjRvAAI0fwACNG7wAjR+8AI0fvACNG7wAjR+8AI0fvACNG7wAjRu8AI0bvAE5S5wBOUucATlLnAE5S + 5wBOUucATlLnAE5S5wBOUucATlLnAE5S5wBOUucATlLnAE5S5wBOUucATlLnPb7D+PvY2f3/2tv9/9nX + /f/T0/v/0dH7/9DQ+//V1Pz/ur74/y8r4f8jHOD/Jh7g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/Jh7g/yYf4LAmH+AJJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGlo + ZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGho + aABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpq + aQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S + 8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4CMmH+BwJh/gwSYe3/UmH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe + 3/8mH9//Jh/g/yYf4P8mHt//Jh7g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf3/8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf3/8mH9//Jh7g/yYf4P8lH9//Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe + 4P8mHd//Jh3f/yUl4f8lMuf/HDrr/0tu8//6/P////////Pz8/8xMTL/AAAA/wAAAP8DAgD/Hzad/yVL + //8jRu//I0fv/yNG7/8jRu//I0fv/xtA8f8uUu3/2tzk+5Su8mgbRPMKo7n6AGqG9QAZPu8AI0fvACNH + 8AAjR/AAI0fwACNH7wAjR+8AI0fvACNH7wAjRu8AI0fvACNG7wAjR/AAI0fvACNH7wAkR/AAI0fwACNH + 7wAjR+8AI0bwACNH8AAjRu8AI0fvACNH7wAjRu8AI0fvACNH7wAjRu8AI0bvACNG7wCHmPAAh5jwAIeY + 8ACHmPAAh5jwAIeY8ACHmPAAh5jwAIeY8ACHmPAAh5jwAIeY8ACHmPAAh5jwAIeY8Aq3vffN1NP8/8/P + +//Pz/v/z8/7/8/P+//Pz/v/0ND7/87Q+/8/QeT/IRnf/yYe4P8mHuD/Jh/g/yYe4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4MUmHt8TJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpq + aABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5u + bwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBw + cgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf + 4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK + +QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AImH+A1Jh7gfiYe4M8mHuD8Jh/g/yUf + 3/8mH+D/Jh/f/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHt//Jh7g/yUe4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/JR7f/yYf4P8mH+D/Jh7g/yYf4P8mH9//Jh7f/yYf + 4P8mH+D/Jh/f/yYf4P8mHeD/Jhzf/x8a3/9BR+f/9/j+////////////d3d4/wAAAP8AAAD/Ex9P/yVL + +f8jR/L/I0fw/yNG7/8jR+//I0bv/yNH7/8hRe//Ikfw/+zu///c5/7/LVLwyqO5+l5qhvUKGT7vACNH + 7wAjR/AAI0fwACNH8AAjR+8AI0fvACNH7wAjR+8AI0bvACNH7wAjRu8AI0fwACNH7wAjR+8AJEfwACNH + 8AAjR+8AI0fvACNG8AAjR/AAI0bvACNH7wAjR+8AI0bvACNH7wAjR+8AI0bvACNG7wAjRu8Ah5jwAIeY + 8ACHmPAAh5jwAIeY8ACHmPAAh5jwAIeY8ACHmPAAh5jwAIeY8ACHmPAAh5jwAIeY8ACHmPAAnarzgdXU + /P/Pz/v/z8/7/8/P+//Pz/v/0dD7/9bV/P/Y2v3/R07l/yAX3v8mHuD/Jh7f/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4MwmH+AeJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxr + awBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlp + aABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGho + ZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf + 4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC + 7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAGJh7gQyYf + 4JAmH+DYJh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYe4P8mH+D/Jh/g/yUe3/8lHuD/Jh/g/yYe + 3/8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yUe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yUe3/8lH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mHt//JR/f/yYf4P8mH+D/Jh/g/yYe4P8fFd//T1Xl//r9/v///////////769vv8AAAD/BgsV/yRE + 0/8jSPn/I0bv/yNG7/8jRu//I0fv/yNH7/8jR+//IETv/yZJ8P/r7f7/2eT+/zJV8f+4yPv/aYX1yRk+ + 710jR+8KI0fwACNH8AAjR/AAI0fvACNH7wAjR+8AI0fvACNG7wAjR+8AI0bvACNH8AAjR+8AI0fvACRH + 8AAjR/AAI0fvACNH7wAjRvAAI0fwACNG7wAjR+8AI0fvACNG7wAjR+8AI0fvACNG7wAjRu8AI0bvAISQ + 7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ + 7yXHyPrr1dT8/9LS/P/W1fz/2dn9/9PV+/+4vPf/goju/y0q4f8kG+D/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/Jh/g/yYf4NomH+AiJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdn + ZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdn + ZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdm + ZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ + 3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh7gECYf4FEmHuCdJh/g4CYf4P8mH+D/Jh7f/yYe3/8mH+D/Jh7g/yUf3/8lH9//Jh/f/yYe + 4P8mHt//Jh/g/yYf3/8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8lH9//Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8lHuD/JR/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7f/yUf4P8mH+D/Jh/g/yYf4P8mHuD/HhXf/1Vg5v/8//7////////////j4+P/GhkS/xco + h/8mTP//I0jw/yNH8P8jR+//I0fw/yNH8P8jR/D/I0fw/yFF8P8lSPD/7e/+/8TU/P8tUfH/v838/159 + 9P8aPu7/I0fvyCNH8FwjR/AEI0fwACNH7wAjR+8AI0fvACNH7wAjRu8AI0fvACNG7wAjR/AAI0fvACNH + 7wAkR/AAI0fwACNH7wAjR+8AI0bwACNH8AAjRu8AI0fvACNH7wAjRu8AI0fvACNH7wAjRu8AI0bvACNG + 7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ + 7wCEkO8Ag4nvmtLW+//HzPr/tLj3/4eN7/9TVOf/King/xwU3v8kHN//Jh7g/yYe4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh7g/yYf4NsmH+AoJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGho + ZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdn + ZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZm + ZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd + 4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN6 + 7AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4BgmH+BXJh/gqSYf3+YmHuD/Jh/g/yYe4P8mH+D/Jh/f/yYf + 3/8mH+D/Jh/g/yYe4P8lHt//Jh/g/yYe3/8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yUe + 3/8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe3/8lH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/x8X3/9IR+X/+fv+///////////////8/2Jl + jf8aFNX/JSnn/yQ56f8jRu7/I0nw/yNI8P8jR/D/I0bv/yNG8P8hRe//Jkrw/+3w/v+/0Pv/L1bx/7rL + /P9QcvT/HEDv/yNH7/8jR/D/I0fwvCNH8EwjR+8AI0fvACNH7wAjR+8AI0bvACNH7wAjRu8AI0fwACNH + 7wAjR+8AJEfwACNH8AAjR+8AI0fvACNG8AAjR/AAI0bvACNH7wAjR+8AI0bvACNH7wAjR+8AI0bvACNG + 7wAjRu8ALCrhACwq4QAsKuEALCrhACwq4QAsKuEALCrhACwq4QAsKuEALCrhACwq4QAsKuEALCrhACwq + 4QAsKuEALCrhACwq4TJHR+X2QUPk/ycm4P8cFN7/HBTe/yMb4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe4NgmHuAoJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlp + ZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlp + ZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlp + aABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn + 2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre + /ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AgJh7gXyYe4K4mH+DrJh/g/yYe + 3/8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYe4P8lHt//Jh/g/yUf + 3/8mH+D/Jh/g/yYe4P8mH+D/Jh7f/yYe4P8mH+D/Jh7g/yYf4P8mH9//JR/g/yYe4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe3/8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8iGuD/MS7i/9vj+v////////////// + //+QnPX/GBHg/yYc3v8mHd7/JSXh/yU06P8jQ+3/I0nw/yNI8P8jR+//HkLw/zBY8f/3+v//rsD6/zdf + 8v+6zPv/Qmfz/x5B7/8jR+//I0fv/yNH8P8jR+//I0fvryNH7zcjR+8AI0fvACNG7wAjR+8AI0bvACNH + 8AAjR+8AI0fvACRH8AAjR/AAI0fvACNH7wAjRvAAI0fwACNG7wAjR+8AI0fvACNG7wAjR+8AI0fvACNG + 7wAjRu8AI0bvACwq4QAsKuEALCrhACwq4QAsKuEALCrhACwq4QAsKuEALCrhACwq4QAsKuEALCrhACwq + 4QAsKuEALCrhACwq4QAsKuEAHhXfnh8W3v8kHOD/Jh7g/yYf4P8mH+D/Jh7f/yYe3/8mH+D/Jh/g/yYe + 4P8mHt//Jh7g/yUe38kmHuAgJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZm + YgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxs + bQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9v + cABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJo + egAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r + 6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gKSYf + 4GkmHuC3Jh7g7CYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHt//Jh7f/yYf3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8lHt//Jh7f/yYf3/8mH+D/Jh/g/yYe4P8mH+D/JR3g/xwW3v+epfH///////// + ////////aW/r/xkR3f8mHuD/Jh7g/yYd3/8mG97/JSHg/yUw5f8kQO3/I0nw/xM57/9lhPX//////5+x + +P9AZvP/vM38/zVZ8P8gQ+//I0fv/yNH7/8jR+//I0fw/yNH8P8jR/D1I0fvkCNH7x8jRu8AI0fvACNG + 7wAjR/AAI0fvACNH7wAkR/AAI0fwACNH7wAjR+8AI0bwACNH8AAjRu8AI0fvACNH7wAjRu8AI0fvACNH + 7wAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4BQmH+CuJh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4KomH+AYJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ + 0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJy + cwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGho + ZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5s + YQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh + 4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYe3yomH+BiJh/grCYf4N0mH+D/JR7f/yYe4P8mH+D/Jh/g/yYf3/8mH+D/Jh/g/yYe + 3/8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yUf3/8mH+D/Jh/f/yUe3/8mH9//Jh/g/yYe4P8mH9//JR7f/yUe + 3/8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8lHt//JR/g/yYf3/8fFt7/Pj/j/+bu + /P//////2OL6/ysr4P8iGd//JR/g/yUf3/8lH+D/JR/f/yUe3/8mHN//Jx/g/xkf4/86WO7/4+r+//// + //95k/f/U3Hz/77N/P8rTvD/IUXv/yNG7/8jRu//I0bv/yNH7/8jRvD/I0bv/yNH7/8jR+/gI0bvZSNH + 7wQjRu8AI0fwACNH7wAjR+8AJEfwACNH8AAjR+8AI0fvACNG8AAjR/AAI0bvACNH7wAjR+8AI0bvACNH + 7wAjR+8AI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gBiYe4GAmH+C7Jh/g6CYf4P4mH+D/Jh/g/yYf + 4P8mH+D1Jh7g0yYf4G8mH+AFJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y + 5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlp + aABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdn + ZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZm + ZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc + 4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AZJh/gRyUe34smHuDHJh/g9SYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/f/yYf3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH9//Jh/g/yUf + 4P8lHt//Jh/g/yYf4P8mHuD/Jh/g/yYf3/8mH9//Jh7g/yYe4P8mH+D/JR/f/yUf4P8mH+D/Jh/g/xwV + 3/9ZYOj/u7z1/1pf6P8cFN7/Jh/g/yUf3/8mHuD/Jh7f/yYf4P8mH+D/JiDf/xsR3v8yM+H/ztX4//// + ///u9P3/MFXw/4ae9/+uwvv/JUrw/yFF7/8jR+//I0bv/yNG8P8jR/D/I0fw/yNH7/8jR/D/I0fw/yNH + 7/8jR/CyI0bvLCNH8AAjR+8AI0fvACRH8AAjR/AAI0fvACNH7wAjRvAAI0fwACNG7wAjR+8AI0fvACNG + 7wAjR+8AI0fvACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gASYf4CUmHuBCJh/gRyYf + 4EcmH+BGJh/gNCYf4BImH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej + /wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGho + ZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdn + ZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZn + ZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gByYf4DcmH+B4Jh/gvCYf + 4PImH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yUe4P8mH+D/JR7f/yUf + 4P8mHuD/Jh/g/yYf4P8mHuD/Jh7f/yYf4P8mH9//Jh/f/yYe3/8mH9//Jh/g/yYf4P8mH9//Jh/g/yYf + 4P8mHt//Hhbf/xsU3v8eFt7/Jh/f/yYe3/8mHuD/Jh/g/yYf4P8mH+D/Jh/g/xkQ3v8xNeH/zdb4//// + ////////b3bq/0NP5//L1vv/l7D5/yBI8P8iR/D/I0fv/yNH7/8jR/D/I0fw/yNH8P8jR/D/I0bw/yNG + 8P8kRu//I0bw/yNG8OgjR/BpI0fvACNH7wAkR/AAI0fwACNH7wAjR+8AI0bwACNH8AAjRu8AI0fvACNH + 7wAjRu8AI0fvACNH7wAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg + +ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdn + ZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGho + ZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpq + agBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe + 3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd + 3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf + 4AMmHuAvJh7gaiYf4LUmH+DvJh/g/yYe4P8mHt//Jh/g/yYe4P8mHuD/Jh/f/yYf4P8mHt//Jh7g/yUe + 4P8lH9//Jh/f/yYf4P8mH+D/Jh/g/yUe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/f/yYf + 4P8mH+D/Jh/g/yYe4P8lHuD/JR/f/yYe3/8mH+D/Jh/g/yYf3/8mHuD/IBff/xQM3f8/ROT/z9n4//// + ///5/v7/eoDt/zA44v+3vfb/2978/3V+7f8bLef/I0Tu/yNK8P8jSPD/I0fw/yNH7/8jR+//I0bv/yNG + 8P8jR+//JEfw/yNG7/8jRu//I0fw/yNH76QjR+8bJEfwACNH8AAjR+8AI0fvACNG8AAjR/AAI0bvACNH + 7wAjR+8AI0bvACNH7wAjR+8AI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf + 9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdn + ZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5v + bgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhn + ZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe + 4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd + 4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh7gKyYf4GMmH+CuJh/f6iYf4P8mHuD/Jh7g/yYe3/8mHuD/Jh7f/yYf + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mHt//Jh7f/yQa3/8bE97/FxLe/zM14v+Jke//8PP9//// + ///h5vv/XWXo/zk95P+xuPb/2t38/6y29f8wL+H/Ixje/yYj4f8lM+f/I0Pu/yNK8f8jSfH/I0fv/yNH + 8P8jR+//I0fv/yNH7/8kRvD/I0fv/yNH7/8jR/D/I0fv1iRH8EgjR/AAI0fvACNH7wAjRvAAI0fwACNG + 7wAjR+8AI0fvACNG7wAjR+8AI0fvACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe + 3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg + 9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdn + ZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGho + aABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGho + ZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf + 4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd + 4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4CQmH+BhJh/gqyYf4OMmH+D/Jh7g/yUe + 3/8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mHt//Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/JR7g/yMa4P8eFd7/GBDe/xkR3v8kJOD/VFjo/56o8v/s8v3//////+Pn + +/+SmfD/RE3k/2Zu6v/ByPj/3N/8/7O89v87P+P/Hxff/yYf4P8mHd//Jh3f/yYh4P8lMeb/JELt/yNJ + 8f8jSfD/I0fv/yNG7/8jRu//JEbw/yNH8P8jR+//I0bv/yNH8P8jR+/3I0fwhiNH7wwjR+8AI0bwACNH + 8AAjRu8AI0fvACNH7wAjRu8AI0fvACNH7wAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf + 4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg + 9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGho + ZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdn + ZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdn + ZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe + 3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj + 4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AdJh/gVyYe + 4KUmH+DkJh/g/yUd3/8eFd7/Fw/e/xcP3v8XD97/Fw/e/xcP3v8XD97/Fg/d/xcP3v8XD97/Fw/e/xcP + 3v8XD97/FxDe/xgS3v8ZE97/HBXe/x8X3/8sLOH/Rknl/3Bz6/+krvP/2+D6//n+///8////ydL4/4uc + 7/9ncuv/d4Dt/7W79v/b4Pz/0tj7/6Kr9P88P+P/HRbf/yYf4P8mHt//Jh7g/yYf4P8mHuD/Jhzf/yYh + 4P8lL+b/JEHs/yNJ8f8jSfD/I0fw/yNG7/8jRu//I0fw/yNH7/8jR+//I0bv/yNG7/8jR/DCI0fvNCNG + 8AAjR/AAI0bvACNH7wAjR+8AI0bvACNH7wAjR+8AI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf + 9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGho + ZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdn + ZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdn + ZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe + 3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa + 3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe + 3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh/gHCUe31cmIuChS07m4m5r6v94c+z/enXs/3Vw6/91b+v/dG/r/3Nv6/9ybuv/cm7r/3Ju + 6/9ybuv/cm3r/3Fv6v99h+z/i5fv/56p8f+5vvX/ztX4/93p+//c5vr/z9r5/8nS+P+1w/b/pLHz/6Cs + 8/+0vPb/z9b6/9Xa+/+3wPf/h47v/1BW5/8lI+D/IBjf/yYf3/8mH9//Jh7f/yYf4P8mHuD/Jh7g/yYe + 4P8mHd//Jhzf/yYg4f8lLub/JEHt/yNK8f8jSPD/I0fv/yNH7/8jR+//I0fv/yNH8P8jR/D/I0bv/yNG + 7+8jRvBuI0fwASNG7wAjR+8AI0fvACNG7wAjR+8AI0fvACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGlo + ZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGho + aABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpq + aQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S + 8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf + 4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89Bn///9UzNb4nKe39N69zff/3Ob8//z////s9P7/2+f7/9zo + +//d6Pv/3en7/93p+//Y5fv/1uH6/9jj+//F0vj/tMP2/6Ox9P+ktPT/p7b0/7O+9/+xuvb/rbb1/6q0 + 9f+irfP/mJ7z/3l+7v9NUeb/LSrh/x0Y3v8eFt//JBzg/yYf4P8mH+D/Jh7f/yYe4P8mH+D/Jh7g/yYe + 3/8mH+D/Jh/g/yYf4P8mHuD/Jhzf/yYg4P8lL+X/JEDs/yNJ8P8jSPD/I0fw/yNG7/8jR/D/I0fw/yNH + 8P8jRu//I0bv/yNH8K0jRu8kI0fvACNH7wAjRu8AI0fvACNH7wAjRu8AI0bvACNG7wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpq + aABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5u + bwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBw + cgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf + 4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK + +QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYVusT4TZOb8pxqb+vfb3Ps/290 + 7P9vc+z/b3Ts/2907P9vdOz/b3Ts/2907P9vdOz/cHXs/3J27P9la+r/XWTp/11k6v9OVeb/Qj/k/zYx + 4/8sKeH/JSTg/x8a3/8dFN7/Hxjf/yMa3/8lHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh3f/yYg4P8lLuX/JEDs/yNJ8P8jSPD/I0fw/yNH + 7/8jRu//I0fv/yNH7/8jR+//I0fv4SNH71wjR+8AI0bvACNH7wAjR+8AI0bvACNG7wAjRu8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxr + awBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlp + aABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGho + ZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf + 4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC + 7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf + 4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf + 4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndEx0V + 30cdFd+SHRXf2B0V3/8dFd//HRXf/x0V3/8dFd//HRXf/x0W3/8dFd//Hxbf/x8X3v8fFt//IBff/yEZ + 3/8iGuD/Ixvg/yQc4P8lHuD/Jh7f/yYe4P8mH+D/Jh7g/yYf3/8mH+D/Jh/g/yYe3/8lHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYe3/8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mHt//Jhze/yYf3/8lLuX/JEDs/yNJ + 8P8jSfH/I0fw/yNG7/8jRvD/I0fv/yNH7/8jR/D/I0fvmSNG7xYjR+8AI0fvACNG7wAjRu8AI0bvACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdn + ZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdn + ZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdm + ZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ + 3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf + 4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ + 3QAdFd8AHRXfACYf4A4mH+BFJh7gkiYe39omHuD/Jh7f/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh7f/yYf + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYe3/8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mHt//JR7g/yYe + 4P8mH+D/JR7f/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/JR/g/yYf4P8mHd//Jh3f/yYg + 4P8lLuX/JEDs/yNJ8P8jSPD/I0bw/yNH8P8jR+//I0fw/yNH8P8jR+/RI0fvOyNH7wAjRu8AI0bvACNG + 7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGho + ZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdn + ZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZm + ZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd + 4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN6 + 7AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe + 3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb + 8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8NJh/gRiYf4JMmH+DYJh/g/SYe4P8mH+D/Jh/g/yYe + 3/8lHt//JR7f/yYe3/8lHt//Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yUe3/8mHt//Jh/g/yYf4P8mHt//Jh7g/yYf4P8mH+D/Jh7f/yUf4P8mHuD/Jh7g/yYf + 4P8mHd//Jh3f/yYg4P8lMOX/JELt/yNJ8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH8PIjR+9eI0bvAyNG + 7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlp + ZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlp + ZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlp + aABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn + 2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre + /ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE + +ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fCiYf4D0mH+CKJh7g0yYe + 4P0mHuD/Jh/f/yYe3/8mHt//Jh/f/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh/f/yYf4P8mH+D/Jh/g/yYe + 3/8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH9//Jh/g/yYe + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh3f/yYg4P8lMub/JELt/yNI8P8jR/D/I0fv/yNH7/8jR+//I0fv+iNG + 74gjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZm + YgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxs + bQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9v + cABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJo + egAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r + 6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY + 5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe + 4AcmHuA9Jh/giSYf39MmH+D9Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mHt//Jh7f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe3/8mHd//Jhze/yYi4P8lNOj/I0Xu/yNJ8P8jR/D/I0fw/yNG + 7/8jR/DEI0fwACNH8AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ + 0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJy + cwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGho + ZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5s + YQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh + 4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW + +ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf + 4AAmHuAAJh7gACYf4AAmHt8HJh7gPCYf4IAmH+DKJh/g+iYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7f/yYe3/8mH+D/Jh7g/yYf3/8mHt//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe3/8mHuD/Jh/g/yYe4P8mHd//Jhzf/yUm4/8lOur/I0jw/yNI + 8P8jRvD/I0fwviNH8AAjR/AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y + 5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlp + aABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdn + ZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZm + ZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc + 4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP// + /wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fBCYe4DUmH+B9Jh/gySYe4PcmH+D/Jh/g/yYe + 3/8mHt//Jh7g/yYf4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8lH9//JR7f/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHeD/Jh7f/yUt + 5f8kRe7/I0jw/yNH8IojR/AAI0fwACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej + /wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGho + ZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdn + ZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZn + ZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW8 + 9AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe + 3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AQlHt8zJR7fdSYe + 4MEmHt/0Jh7f/yYf4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mHt//Jh7g/yYe + 4P8mHuD/JR/f/yYf3/8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mHuD/Jh7g/yYe + 4P8mG9//JTXo/yNJ8M4jRu8sI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg + +ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdn + ZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGho + ZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpq + agBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe + 3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd + 3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi + 4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf + 4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe + 3wAmHt8CJh/gLyYf4G4mHt+5Jh/g7iYf4P8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7f/yYe + 4P8mH+D/Jh/g/yYf4P8lHt//JR/f/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jhzf/yUw5sEjSvAxI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf + 9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdn + ZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5v + bgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhn + ZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe + 4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd + 4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe + 3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf + 4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe + 3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4CYmH+BkJh7grCYf4OMmH+D/Jh/f/yYe4P8mH+D/Jh7f/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/JR/g/yUe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh7g/yYc36klMeYKI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe + 3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg + 9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdn + ZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGho + aABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGho + ZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf + 4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd + 4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf + 4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe + 3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf + 4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAWJh7gUSYf4IEmHuDAJh/g5SYf + 4P8mH+D/Jh7g/yYe4P8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P0mH+DjJh/gtyYe31YlHN4BJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf + 4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg + 9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGho + ZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdn + ZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdn + ZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe + 3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj + 4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe + 4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf + 4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gAiYe + 4BwmHuBRJh/geCYf4LMmHuDYJh/g8CYe3/8lH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4OUmH+DFJh/glCYf + 4GwmH+BIJh7gFyYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf + 9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGho + ZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdn + ZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdn + ZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe + 3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa + 3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe + 3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf + 4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe + 4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe + 4AAmHuAAJh7gACYf4AAmH+AZJR7fOyUe328mH+CYJh/guCYe374mH+C+Jh7gvyYf4JwmH+BWJh/gJSYf + 4AsmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGlo + ZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGho + aABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpq + aQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S + 8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf + 4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf + 4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe + 3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpq + aABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5u + bwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBw + cgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf + 4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK + +QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V + 3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf + 4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxr + awBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlp + aABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGho + ZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf + 4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC + 7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf + 4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf + 4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V + 3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe + 4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdn + ZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdn + ZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdm + ZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ + 3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf + 4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ + 3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe + 3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe + 4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG + 7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGho + ZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdn + ZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZm + ZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd + 4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN6 + 7AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe + 3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb + 8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf + 4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG + 7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlp + ZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlp + ZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlp + aABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn + 2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre + /ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE + +ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe + 3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG + 7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZm + YgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxs + bQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9v + cABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJo + egAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r + 6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY + 5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe + 4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe + 3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf + 4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK + 8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ + 0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJy + cwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGho + ZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5s + YQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh + 4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW + +ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf + 4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf + 4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf + 4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx + 5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y + 5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlp + aABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdn + ZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZm + ZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc + 4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP// + /wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf + 4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe + 3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc + 3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej + /wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGho + ZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdn + ZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZn + ZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW8 + 9AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe + 3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe + 3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe + 3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe + 3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg + +ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdn + ZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGho + ZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpq + agBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe + 3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd + 3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi + 4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf + 4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe + 3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf + 4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf + 4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf + 9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdn + ZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5v + bgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhn + ZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe + 4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd + 4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe + 3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf + 4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe + 3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe + 3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg + 9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdn + ZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGho + aABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGho + ZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf + 4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd + 4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf + 4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe + 3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf + 4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf + 4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg + 9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGho + ZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdn + ZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdn + ZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe + 3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj + 4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe + 4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf + 4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf + 9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGho + ZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdn + ZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdn + ZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe + 3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa + 3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe + 3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf + 4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe + 4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe + 4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGlo + ZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGho + aABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpq + aQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S + 8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf + 4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf + 4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe + 3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpq + aABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5u + bwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBw + cgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf + 4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK + +QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V + 3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf + 4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxr + awBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlp + aABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGho + ZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf + 4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC + 7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf + 4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf + 4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V + 3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe + 4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdn + ZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdn + ZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdm + ZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ + 3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf + 4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ + 3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe + 3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe + 4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG + 7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGho + ZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdn + ZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZm + ZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd + 4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN6 + 7AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe + 3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb + 8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf + 4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG + 7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlp + ZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlp + ZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlp + aABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn + 2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre + /ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE + +ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe + 3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG + 7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZm + YgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxs + bQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9v + cABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJo + egAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r + 6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY + 5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe + 4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe + 3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf + 4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK + 8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ + 0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJy + cwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGho + ZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5s + YQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh + 4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAhYaJAIWGiQCCjLsAMi81ADIx + VAAODgwAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW + +ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf + 4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf + 4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf + 4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx + 5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y + 5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlp + aABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdn + ZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZm + ZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc + 4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwAAcGHAAHBhwAEyeOAIWGiQCFhokAgoy7ADIv + NQAyMVQADg4MAB88vQAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP// + /wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf + 4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe + 3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc + 3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej + /wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGho + ZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdn + ZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZn + ZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3AAjHMEAIxzBACMcwQAHBhwABwYcABMnjgCFhokAhYaJAIKM + uwAyLzUAMjFUAA4ODAAfPL0ABg4uAAMDEQAAAAAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW8 + 9AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe + 3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe + 3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe + 3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe + 3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg + +ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdn + ZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGho + ZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpq + agBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe + 3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJRzgACYh4AAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEABwYcAAcGHAATJ44AhYaJAIWG + iQCCjLsAMi81ADIxVAAODgwAHzy9AAYOLgADAxEAAAAAAJmn2QCZp9kAJh7gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi + 4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf + 4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe + 3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf + 4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf + 4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf + 9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdn + ZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5v + bgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhn + ZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe + 4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBAAcGHAAHBhwAEyeOAIWG + iQCFhokAgoy7ADIvNQAyMVQADg4MAB88vQAGDi4AAwMRAAAAAACZp9kAmafZAJmn2QAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe + 3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf + 4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe + 3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe + 3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg + 9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdn + ZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGho + aABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGho + ZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf + 4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAHBhwABwYcABMn + jgCFhokAhYaJAIKMuwAyLzUAMjFUAA4ODAAfPL0ABg4uAAMDEQAAAAAAmafZAJmn2QCZp9kAmafZAJmn + 2QAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf + 4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe + 3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf + 4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf + 4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg + 9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGho + ZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdn + ZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdn + ZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe + 3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEABwYcAAcG + HAATJ44AhYaJAIWGiQCCjLsAMi81ADIxVAAODgwAHzy9AAYOLgADAxEAAAAAAJmn2QCZp9kAmafZAJmn + 2QCZp9kAmafZACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe + 4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf + 4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf + 9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGho + ZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdn + ZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdn + ZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe + 3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBAAcG + HAAHBhwAEyeOAIWGiQCFhokAgoy7ADIvNQAyMVQADg4MAB88vQAGDi4AAwMRAAAAAACZp9kAmafZAJmn + 2QCZp9kAmafZAJmn2QCZp9kAmafZACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe + 3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf + 4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe + 4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe + 4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGlo + ZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGho + aABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpq + aQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAHBhwABwYcABMnjgCFhokAhYaJAIKMuwAyLzUAMjFUAA4ODAAfPL0ABg4uAAMDEQAAAAAAmafZAJmn + 2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf + 4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf + 4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe + 3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpq + aABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5u + bwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBw + cgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf + 4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEABwYcAAcGHAATJ44AhYaJAIWGiQCCjLsAMi81ADIxVAAODgwAHzy9AAYOLgADAxEAAAAAAJmn + 2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAJh7gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V + 3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf + 4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxr + awBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlp + aABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGho + ZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf + 4AAmH+AAJh7gACYe3wAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBAAcGHAAHBhwAEyeOAIWGiQCFhokAgoy7ADIvNQAyMVQADg4MAB88vQAGDi4AAwMRAAAA + AACZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QAmHt8AJh/gACYf + 4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V + 3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe + 4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdn + ZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdn + ZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdm + ZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe + 4AAmH+AAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAHBhwABwYcABMnjgCFhokAhYaJAIKMuwAyLzUAMjFUAA4ODAAfPL0ABg4uAAMD + EQAAAAAAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn + 2QAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ + 3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe + 3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe + 4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG + 7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGho + ZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdn + ZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZm + ZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEABwYcAAcGHAATJ44AhYaJAIWGiQCCjLsAMi81ADIxVAAODgwAHzy9AAYO + LgADAxEAAAAAAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn + 2QCZp9kAmafZACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb + 8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf + 4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG + 7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlp + ZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlp + ZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlp + aABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBAAcGHAAHBhwAEyeOAIWGiQCFhokAgoy7ADIvNQAyMVQADg4MAB88 + vQAGDi4AAwMRAAAAAACZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn + 2QCZp9kAmafZAJmn2QCZp9kAmafZACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE + +ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe + 3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG + 7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZm + YgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxs + bQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9v + cABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAHBhwABwYcABMnjgCFhokAhYaJAIKMuwAyLzUAMjFUAA4O + DAAfPL0ABg4uAAMDEQAAAAAAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn + 2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY + 5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe + 4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe + 3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf + 4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK + 8AAjRu8AI0bvACNG7wCIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI////// + //////////////////////////iIiIiIiIiIj///////////////////////////////+IiIiIiIiIiP + ///////////////////////////////4iIiIiIiIiI////////////////////////////////iIiIiI + iIiIj///////////////////////////////+IiIiIiIiIiP///////////////////////////////4 + iIiIiIiIiI////////////////////////////////iIiIiIiIiIj/////////////////////////// + ////+IiIiIiIiIiP///////////////////////////////4iIiIiIiIiI////////////////////// + //////////iIiIiIiIiIj///////////////////////////////+IiIiIiIiIiP//////////////// + ///////////////4iIiIiIiIiI////////////////////////////////iIiIiIiIiIj/////////// + ////////////////////+IiIiIiIiIiP///////////////////////////////4iIiIiIiIiI////// + //////////////////////////iIiIiIiIiIj///////////////////////////////+IiIiIiIiIiP + ///////////////////////////////4iIiIiIiIiI////////////////////////////////iIiIiI + iIiIj///////////////////////////////+IiIiIiIiIiP///////////////////////////////4 + iIiIiIiIiI////////////////////////////////iIiIiIiIiIj/////////////////////////// + ////+IiIiIiIiIiP///////////////////////////////4iIiIiIiAiI////////////////////// + //////////iIiIiIAACIj/////////////////////////////////iIiIgAAIiP//////////////// + /////////////////4iIiAAAiI///////////////////////////////////4iIgACIj/////////// + ////////////////////////+IiIgIiP////////////////////////////////////+IiIiI////// + ////////////////////////////////iIiIj//////////////////////////////////////4iIiP + //////////////////////////////////d////4iI////////////////////////////////93d3d3 + //+Ij///////////////////////////////d0AHd3d//4iP//////////////////////////////dw + AAd3d3d/iI//////////////////////////////cAAAB3d3d3eIj/////////////////////////// + //cAAAAAR3d3d4iP////////////////////////////cAAAAAAAR3d3iI////////////////////// + //////cAAAAAAAAAd3eIj///////////////////////////cAAAAAAAAAAHd4iIj/////////////// + //////////wAAAAAAAAAAAB3iIiP////////////////////////9AAAAAAAAAAAAAeIiI////////// + //////////////9wAAAAAAAAAAAAB4iIiP///////////////////////wAAAAAAAAAAAAAAiIiI//// + ///////////////////3AAAAAAAAAAAAAACIiIj//////////////////////3MAAAAAAAAAAAAAAIiI + iI//////////////////////cAAAAAAAAAAAAAAAiIiIj/////////////////////cwAAAAAAAAAAAA + AACIiIiI////////////////////9wAAAAAAAAAAAAAAAIiIiIj///////////////////9wAAAAAAAA + AAAAAAAAiIiIiI///////////////////3AAAAAAAAAAAAAAAACIiIiIiI//////////////////cAAA + AAAAAAAAAAAAAIiIiIiIj/////////////////cAAAAAAAAAAAAAAAAAiIiIiIiI//////////////// + 9wAAAAAAAAAAAAAAAACIiIiIiIiP///////////////3AAAAAAAAAAAAAAAAAIiIiIiIiIj///////// + //////cAAAAAAAAAAAAAAAAAiIiIiIiIiI//////////////9wAAAAAAAAAAAAAAAACIiIiIiIiIj/// + //////////93AAAAAAAAAAAAAAAAAIiIiIiIiIiI/////////////3cAAAAAAAAAAAAAAAAAiIiIiIiI + iIiP////////////dwAAAAAAAAAAAAAAAACIiIiIiIiIiIj///////////93AAAAAAAAAAAAAAAAAIiI + iIiIiIiIiI///////////3cAAAAAAAAAAAAAAAAAiIiIiIiIiIiIiIiIiIiP////dwAAAAAAAAAAAAAA + AACIiIiIiIiIiIiIiIiIiIiIiP93AAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiIiIiIiAAAAAAAAAAA + AAAAAAAAiIiIiIiIiIiIiIiIiIiIiIiAAAAAAAAAAAAAAAAAAACIiIiIiIiIiIiIiIiIiIiIgAAAAAAA + AAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiIiIiIiIiIiIgAAA + AAAAAAAAAAAAAAAAAACIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiI + AAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAACIiIiIiIiIiIiI + iIiIAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiI + iIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAIiI + iIiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAA + AACIiIiIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIAAAAAACAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIgAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAiIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIgAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiI + iIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiI + iIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AACIiIiIiIiIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIiIiIgAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAIiIiIiIiIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiIAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIiAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIiIiIAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiI + iAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiI + iIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AACIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAiIiIiIiIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIiIiIAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAiIiIiIgAAAAAiIiIiIiIiAAAAAAAAAAAAAAA + AAAAAACIiIiIiIiAAACIiIiIiIiIAAAAAAAAAAAAAAAAAAAACIiIiIiIiIiIAIiIiIiIiIgAAAAAAAAA + AAAAAAAAAACIiIiIiIiIiIiIiIiIiIiIiAAAAAAAAAAAAAAAAAAACIiIiIiIiIiIiIiIiIiIiIiIAAAA + AAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiIiIiI + iAAAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiIiIiIiIAAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiI + iIiIiIgAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiIiIiIiIiAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiI + iIiIiIiIiIiIAAAAAAAAAAAAAAAAiIiIiIiIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAAAAAiIiIiIiIiI + iIiIiIiIiIiIiIiIiIAAAAAAAAAAAAAAiIiIiIiIiIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAAAiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIAAAAAAAAAAAAiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIAAAAAAAAAAiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIgAAAAAAA + AAiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIAAAAAAAAiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + AAAAAIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiCgAAACAAAAAAAEAAAEA + IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAAB7AAAAnwAAAJ8AAACZAAAAdQAAAEIAAAAJAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABfAAAAyAAA + APkAAAD/AAAA/wAAAP8AAAD2AAAA1AAAAIQAAAAbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAaAAAAswABAP8GBhj/Cwsr/wsLK/8HByD/BAQO/wABAP8AAAD/AAAA/wAA + AOQAAABIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJQABANcPDkb/Hhut/yUe + 3f8kHer/IRnr/xwU5f8ZFNH/FBGs/wkIYv8AAA3/AAAA/wAAAPQAAAAxAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAB4DBAjaGhiQ/yMb9v8eFff/GxTn/yUh1/9AP8n/Z2bD/3+Bx/9/gc7/aGbQ/zc4 + n/8BAx3/AAAA/wAAAMAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAAAUzRURr/8gGu7/Mi/O/1ta + wP+RksH/xcfP/+3u5P////b////+/////v////T/7u3p/5SUoP8aGRf/AAAA/wAAAD4AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAKSgoBwICBbY5O4r/cnLO/6qsxf/e39r////x/////////////////////P/09fb/5er3/97m + ///d4///4+r//8HG0P85Ny7/AAAAlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbm5tJH5+fmaPj4/Burq7/+/v7f////n///////// + ///4+v//1N35/6m48v99kuz/V3Hn/zxa5/8tT+v/J0vw/yJG8P8lSvD/QmX+/1pwzf8aHCG9AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1tbAJwb28yg4KDeqam + psLQz9D18/Pz///////////////9/93j9/+is/T/aIPx/z1e7/8iRu7/Fz3u/xY87/8ZPvD/HULx/yBE + 8P8hRO//IUbv/yFF7/8dQu//HkT6/y9My94zSZ0NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAbGxsB3Jycj6JiYmHr6+vz9nZ2v77+/r////////////+/ff/09fo/5ak3/9VcOP/J0rs/xY8 + 7v8XPe//HUHv/yFF7/8jRu//I0fv/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH8P8jSfD/Ikn2/yRJ + +Z8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsbGwIc3NzQYuLjI+0tLTY39/f/////f////////////Hy + 9P+/x+f/gpPd/0lk3v8kRuX/Fjzt/xo/8f8gRfD/I0fv/yNH8P8jR+//I0fv/yNG7/8jR+//I0fv/yNH + 8P8jR+//I0fv/yNG7/8jR+//I0bv/yU06P8kPuz/I0nw/yNH8GkAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABzcnI7jIyLj7S0 + tNni4eD////+////////////4+f2/6q36v9rgeP/OFfj/xxB6f8WPO//G0Hx/yFG8f8jR+//I0fv/yNH + 7/8jRu//I0fv/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH7/8jRu//I0fw/yNJ8P8lMeb/Jhre/yYf + 4P8kO+v/I0nw8yNG7y8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAB2dnUspaSlw97d3f////7////////////U3Pj/lafv/1dy6v8rTen/GD3t/xc9 + 8P8eQvH/Ikbw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH7/8jR+//I0fw/yNH + 8P8jR/D/I0bv/yNH7/8jSPD/JEHs/yYf4P8mHuD/Jh3f/yYf4P8kO+r/I0nwyyNH7woAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIuLjJj//////////8jS + +P+DmfP/SGfu/yJG7f8WPO7/Gj/w/yBE8P8jR/D/I0fv/yNH8P8jR+//I0bv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fv/yNH7/8jR/D/I0bv/yNH7/8jR+//I0fv/yNG7/8jRu//I0fv/yNJ8P8lM+f/Jhzf/yYf + 4P8mH+D/Jh3f/yYh4P8jQu3/I0jwgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAdnVzML/Aw/BohPn/GT7t/xY77v8bQO//IUXv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fw/yNH7/8jR+//I0bv/yNH7/8jR+//I0bv/yNH7/8jR+//I0fw/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fv/yNH7/8jR+//I0jw/yUr5P8mHN//Jh7g/yYf4P8mHuD/Jhzf/yUx5/8jSfC0AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW2WSNho/ + 8e0iRfH/I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH + 7/8jR+//I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0bv/yNH7/8jR/D/Jifj/yYd + 3/8mH+D/Jh/g/yYf4P8mHN//JS3l/yNJ8KUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIkXyhiNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH7/8jRu//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fv/yNG7/8jRu//I0fv/yNF7v8mI+H/Jh7g/yYf4P8mH+D/Jh/g/yYc3/8lM+f/I0nwhAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAjR/AZI0fv7SNH7/8jRu//I0bv/yNG7/8jR+//I0fv/yNH8P8jR+//I0bv/yNH7/8jRu//I0bv/yNH + 7/8jRu//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH7/8jSPD/JEHt/yYf + 4P8mHt//Jh/g/yYf4P8mHuD/Jhzf/yQ56v8jSfBYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjR++DI0fv/yNH7/8jR+//I0bv/yNG + 8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0bv/yNH7/8jRvD/I0bw/yNJ8P8kN+n/Jhze/yYf4P8mHuD/Jh7g/yYe4P8mHd//JDzr+yNI + 8CkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACNG7xUjR+/pI0fv/yNH8P8jRvD/I0fw/yNH8P8jR+//I0fw/yNH7/8jR/D/I0fv/yNH + 8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH8P8jR/D/I0jw/yUo + 5P8mHd//Jh/g/yYf4P8mHt//Jh7g/yYd3/8kOurMI0vxBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVAAAAVAAAAIoAAACkAAAAqAAA + AJgAAABrAAAAJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNH8HYjR+//I0fv/yNH + 7/8jRu//I0fw/yNH8P8jR+//I0fv/yNG7/8jRvD/I0bv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH + 8P8jRu//I0fv/yNH7/8jR/D/I0fv/yNI8P8kP+z/Jh7f/yYe4P8mH+D/Jh3f/yUp4/8mKuT/Jh3f/yYj + 4ZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAPAAAAhwAAAOYAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADzAAAAlAAAABIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAI0bvDSNH790jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNG + 7/8jRu//I0fv/yNH7/8jR+//I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNG8P8jR/D/I0nw/yUu + 5f8mHuD/JiDh/yYe4P8mHuD/JTHn/yUy5/8mH+D/Jh7gXgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMwAAANICAwP/Cwwx/xIRYv8YF3v/GRmA/xYU + cv8ODkX/BAUN/wAAAP8AAAD/AAAA0wAAADUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0fvWyNH + 7/8jSfD/I0jw/yNH7/8jR+//I0bv/yNH7/8jR+//I0bw/yNH8P8jR+//I0fv/yNH7/8jR/D/I0bv/yNH + 7/8jRvD/I0fw/yNH7/8jR+//I0fw/yNI8P8jQ+7/JiDg/yUs5f8mIuH/Jh3f/yYj4f8lNun/JTDm/yYe + 3+QlHt8SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AEYCAwPuExNf/yEdwP8mH+r/Jx/1/ycf9v8nH/b/Jx/2/ycg8f8jHs7/ExJj/wEBBP8AAAD/AAAA8AAA + AEkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjRu8BI0fvtSQ06P8kPOr/I0nx/yNI7/8jR+//I0fv/yNH + 7/8jRu//I0bv/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0rw/yUx + 5v8mJOL/JTPo/yYf4P8mHuD/JS/m/yU26f8mJOL/Jh3fbQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/CAkh8yAdsv8oIPb/Jh7x/yUd6f8lHef/JR3o/yUd + 7P8lHe3/JR3q/yYe6P8mIOL/GRtu/wMFAf8AAAD/AAAA5AAAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACNJ8REjRvAxIUTvLyFD7hgiR+8JAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAjSfAZJSzk0CYc3/8lM+f/I0jv/yNJ8/8kSfb/I0jy/yNH8P8jR+//I0fv/yNH8P8jR+//I0fv/yNG + 7/8jR/D/I0fv/yNH7/8jR/D/I0fv/yNI8P8jQe3/JSXi/yU06P8lLuX/Jhzf/yYr5f8lOOr/Jirk/yYd + 380mH+AHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJAoM + MOgkH9X/Jx72/yUd6P8lHen/JR3s/yUd6v8lHuD/JCDP/yMhvv8jIrP/IyGv/yIgsv8kIb3/Ih2y/xwn + n/8TJnLlFiyVKgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0nwGiNE7mYlOOmxIifk5iQm4v8wM+T9MjXl8CUq + 5N0kLubDJDXopSQ56YQkP+1jI0TuQyNH7ycjSfARI0nxAQAAAAAlMucNJhzfzyYa3v8mKuf/ITbU/x4y + vv8iQuT/JEn1/yRJ9f8jR/D/I0fv/yNH7/8jR+//I0fw/yNH7/8jR/D/I0fv/yNH7/8jSPD/I0bv/yUu + 5f8lMuf/JTbp/yYj4v8mHd//JSrl/yYp5P8mHd/8Jh7gOwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcJCijEJB/Z/yYe9P8lHe3/JR3q/yUf3f8kIcr/JCO9/yQi + uv8lIcP/JR/R/yYf3P8mHuP/Jx7m/yYe5v8nHuj/JyXt/yVI/f8lS/3xI0jyiSNH8AkAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0jwGyND + 7oElMObrJiHh/yIX3v9ISOb/cHnv/2lw7f+CivP/YGbr/yce3/8lG9//Jh3f/yYf4P8mIeH/Jibi+SUr + 5OklMefOJDfpsSQ964UlMuiWIiTl+hsbuv8cHKr/FhR8/xUUev8ZI5v/HzfK/yNG7v8kSvb/JEn1/yNJ + 9P8jSPT/I0fx/yNH8P8jSPH/I0ry/yNF7/8lKuT/JS7l/yU46v8mKuT/Jh3f/yYf4P8mH+D/Jh7g/yYe + 4IEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAUQeyMe + zP8nH/3/Ix7b/yIfvv8kIrf/JSK//yUhzf8mINz/Jh7j/yYe5P8mHuL/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHN//JTbp/yNJ8P8jR/D/I0fvvyNH7xIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkOuqJJiDg/yYc3/8mHuD/JBzf/zc15P86OeT/Ixzf/ysm + 4f80MuP/KSPg/yUe3/8mH+D/Jh7g/yYe4P8mHeD/Jh3f/yYc3/8mHN//Jh7g/yMf4f89Rej+PUfk/ykz + 2P8fKtD/GCG8/xUaof8VGZD/GCKb/x0wuP8fOMz/IDnP/yA60v8iQ+f/I0jx/yNE6v8kOOb/JiPj/yce + 5f8mMuv/Jirk/yYe4P8mHuD/Jh/g/yYe4P8mH+C2Jh/gAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQaGYzzIx7M/xwcj/8dHYv/Ix+6/yYf3P8mHuT/Jh7j/yYf + 4f8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYd3/8lK+T/I0jw/yNH7/8jR+//I0fvsiBE + 7wIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYv + 7A8mG95wJh/g1yYf4P8mH+D/Ixvf/yMb3/8mH+D/JR3g/yQb4P8lHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8iGd//PT7l/5Ge9v+Xo/j/ipT3/3mC9f9ka/P/TVTv/zhA6f8nL9r/HCXI/xci + uP8WIKX/FhyR/xcekP8YHo//FhaB/xcQgP8YEor/HRqp/yQh0v8nHuX/Jx/l/yYf4f8mHuD/Jh7g0iYe + 4BMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADA0spR8f + kv8fHZ7/Ix7E/yYf5P8nHub/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/Jh3f/yUk4v8jRu//I0fv/yNH7/8jR+//Gz/vYAAAAAD7/P8C////CgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmH+ALJh7fXiYf4MUmH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yEY3/9BQOb/l6X4/5yp + +f+eqvn/nqv5/56r+f+bqPj/lJ/3/4eR9f9ze/T/W2Lx/0NL7v8uOef/IC3a/xkoyv8XJbb/FSGg/xUb + i/8UFnj/FRJ4/xoUl/8hGr7/JR3a/yYc4N0mHN8iAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACYf4BYmH+BEJh7ggicg5MMmINn/Jh/h/ycf6f8mH+T/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/JiLh/yNE7v8jSPD/I0fv/yNH + 7/8YPe7ZAAAAAP///1Pc3NxoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAABAAA + AAAAAAAAAAAAAAAAAAAAAAAAJh7fASYe4EgmH+CwJh/g+yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh7f/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yUd3/83NuP/U1bp/2lw7v96hPH/h5P0/5Gd9v+Zpvj/nqz5/6Gt + +v+eq/n/maT4/42X9v97g/P/ZWzy/1Fa8f89Su7/Kzzo/x0w3P8bL8r/Gy2v/xgjlv8bH6LgJSzkJwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh/gJCYf4IcmHt+7Jh/g6SYf4P8mHuD/Jh7h/yYf + 4/8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYe4P8mIuH/I0Tu/yNI8P8jR+//I0fv/xM57vizwv9g19bO4xISEmIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAA4AgMFqAEBA9oAAADUAAAAiAAAABUAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAmHt80Jh/gmCYf4O0mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh7f/yIa + 3v8fF9//Hxff/yMc3/8pJOD/NDHj/0FC5f9VWen/b3jv/4iU9P+YpPj/nar5/56r+f+eq/n/nqr5/5mk + 9/+Pmfb/cnry/zo96/8lKer/JTHq/yQ55vAjQ+3BI0nwiSNJ8EgjSfEWAAAAAAAAAAAAAAAAAAAAAAAA + AAAmHd9DJh/g/yYf4v8nIOb/Jx/j/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/Jh/g/yYe3/8mH+D/Jh7f/yYe4P8mHuD/Jh/f/yYe4P8mH+D/Jh3f/yYm4v8jR+//I0fw/yRH + 8P8YPe7/S2z4+t/i5/EoJyT/AAAASgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAaAsMN/gTE2n/AwQK/wAA + AP8AAAD/AAAA2QAAAC8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh7gHyYe4H0mHuDdJh7g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/JR7g/yUc4P8jGt//IRjf/x8W + 3v8gGN7/JB7g/zg35P9WXOr/cHrw/4CK8/+AjfP/gY3z/4mU9f+Xo/j/jJj1/zAs4f8jGd//Jh3g/yYf + 4P8mJeL/JS7m/yQ46ekkQe27I0bvfiNI8DsAAAAAAAAAAAAAAAAmHeI+IxvOyR4Zrf8jHcz/Jx/k/ycf + 5/8nH+b/Jx/m/ycf5f8nH+X/Jh/k/ycf4/8mH+L/Jh/h/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/f/yYe + 4P8mHuD/Jh7g/yYf4P8mG9//JDTo/yNJ8P8jR+//HUHv/y5T8v/k7f/6W1lT7QAAAP8AAAA7AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAECBGIPEFD/FRR8/xgVk/8aGJT/DQ4+/wABAP8AAAD/AAAA3wAAABwAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACYe4A8mHt9iJR/fxiYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH9//JR3g/ygj4P9dZOv/bHHu/2x17/90fvD/eYXx/3aC + 8P9lbu3/QkXm/ywn4f8zLuP/JyDg/yYf4P8mH+D/Jh7g/yYd3/8mHN//Jhzf/yYf4P8mJeL/JS7l/yQ5 + 6qokQuwRAAAAAAAAAAAAAAAAHxyyVBgSh9MYE4z/HBam/x4Ysv8gGbf/IRq9/yEbw/8iG8r/Ix3Q/yUe + 1v8mH97/Jx/l/yYf4f8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jhzf/yUo4/8jR+//I0fw/yBE + 7/8dQ+//ydf//7a0qvcAAAD8AAAA/gAAADEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQ5Dw9X9xUTfP8YFY3/Ixza/ycf + +f8nIPD/GhmN/wMEB/8AAAD/AAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAmHd8BJh3fRiYe4K4mHuD5Jh/g/yYf4P8nH+D/IBnh/xwT4f8hGeH/Jh/g/yYf4P8mH+D/Jh/g/yYf + 3/8mHuD/HRXh/0dK6v+LmPb/oK75/5qm+P+YpPj/maT4/5qm+P+Uoff/U1jp/xwU3f8lHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYd3/8mHN//Jhve/yUr5NQjSfCnI0nxmCNJ8YUlRPVqHzDIkxki + l/sZIZT/Fx+P/xcdjP8XGoj/FxiE/xYVgP8WFH7/FhJ8/xYTg/8eGaz/Jx/h/ycf5v8nH+T/Jx/k/yYf + 4v8mH+D/Jh7g/yYc3/8mJOH/JEPu/yNI8P8jRu//GD7v/52x+//+/PL/ISEh/wAAAP8AAAD1AAAAJAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAERITTdcgIoj/GRac/yUd5P8mHuz/JR3n/yUd6f8oIPr/HRud/wIDBP8AAAD1AAAAHwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYh4SEmHuCAJh3f3yIZ + 4P9PS9j/a2rU/0pK2v8jHeD/Jh7g/yYf4P8mH+D/JR7g/yYf4P9XVtf/aWnS/zY13v9bYez/mKb4/5ai + 9/+UoPf/lKD3/5ei9/+Zp/j/TE/o/xwT3v8jGt//JB3f/yYd4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mG97/JSjj/yNH7/8jR+//I0fw/yNI8P8jSvT/JEn1/yRJ8/8jSPL/I0fx/yNF7P8jROn/IkLl/yE+ + 3f8gN8v/GCCV/xQRcv8aF5f/IRvC/yIcxP8jHMv/JR3X/ycf4/8nHeP/JiPi/yRB7f8jSfD/I0fv/xk+ + 7/9XdPP//////5aVlP8AAAD/AAAA/xkZGemOjo8VAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJCiWfQ0ai/yIfqv8kHOr/JR7s/yUd + 5/8lHej/JR3o/yUd6P8oIPn/EhJc/wAAAP8AAABEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACNJ8QkjRe4qI0TuTiQ+7HgkPOvKGivq/3N51P/e4L//ZmjV/xwU4f8mH+D/Jh/g/yYe + 4P8lHuD/IRrh/6Ckyv/c3b//WFfW/xUN4P9WXOr/mKX4/5up+P+ap/j/mqf4/52p+f+Wo/f/Vlrq/zIw + 4v8rJ+H/Ixzf/yQc4P8mH+D/Jh/g/yYe4P8mH+D/Jhzf/yUs5f8jRe7/I0jv/yNH7/8jR/D/I0fv/yNH + 8P8jR/D/I0fw/yNH8P8jR/D/I0jx/yNI8v8jR/L/JEr3/yVM/P8hPNf/GBeF/xUQdf8UEXH/FBFz/xQQ + c/8VEXr/GhSS/yMizv8kQe3/JEz3/yRK9f8jR/H/Fjzu/62+/P///vn/KCgp/wAAAP8AAAD/RERF4aen + pwsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAASklOk/89Pbz/IBjl/yYe7f8lHeb/JR3q/yUd5/8lHej/JR3n/yYe8v8gHLr/AgMD/wAA + AFYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjR+8dI0fwSSNH730jR++xI0fv3SNH7/sjSPD/I0jw/yNJ + 8P8bQ/P/T2vj/9HQwf+Ii8//HRfh/yYc3/8mHuD/Jh7g/yYf4P8cFOL/dHbS/9fYwP97fdH/HBTh/yEZ + 3/9BQeX/WFzq/1NW6f9TVun/Z27t/4yY9f+eq/n/kJ32/4uW9f9zffD/MCzi/yQc4P8mH+D/Jh3g/yYe + 3/8lM+f/I0jw/yNI8P8jR+//I0fv/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0bv/yNH8P8jR/D/I0fw/yRK + 9v8hP93/GSGZ/xUUff8ZIJn/HTC6/x84zf8fOM7/HzPD/xwrrv8XG4r/FRV9/xYZhv8aJKH/HzbI/yBC + 6/8nTfb/6vH//7u5tP8AAAD/AAAA/wAAAP+BgYHg////CwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUbHT/MYmbH/x8Y1P8lHev/Gxej/xgV + iP8hG8z/Jh7r/yUe6P8lHef/JR3r/yUf3/8FBRX/AQMKTgAAAAAlSv0CI0buJSNH71ojR++VI0fvyyNH + 7/IjR/D/I0fv/yNH7/8jR/D/I0fw/yNH7/8jR/D/I0fw/x9D8f83WOr/wMLF/8fIw/9BV+T/Hyjn/yYg + 4P8mHN//Jh3g/x4W4f9PTtj/0NHC/52fy/8hHOH/JR3g/yEZ3/8fFt//IBff/x8X3/8eF97/LSnh/3qF + 8f+eqvn/nan5/5qo+P86OOT/Ihrf/yYc3/8mIuH/JDzr/yNJ8P8jR+//I0bv/yNH8P8jR+//I0fv/yNH + 7/8jR+//I0fv/yNH8P8jRu//I0fv/yNH8P8kSvf/HjPB/xYTfP8aIaH/IT/b/yRJ8/8kSvf/JEn1/yRJ + 9f8kSvb/JEr3/yNH7/8gPNf/HCux/xcaiv8UEHX/Dw+A/0BQwf//////ZmVj/wAAAP8AAAD/AAAA/7Gx + sdj///8GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAABSkNGjv83NrL/IRnt/x8ZuP8UE3L/ExJw/xsXnv8mHu3/JR3o/yUd6P8lHen/Jh3p/wsJ + J/wVKod/Jkz/kCNH79AjR+/4I0fv/yNH7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNH8P8jR+//I0fv/yNH + 7/8jR+//IUXw/yZK7/+qssr/zMvC/4qb1P8ZQvP/I0Lt/yUy5/8mI+H/Ixjg/zMt3f+8vsX/urvF/zAt + 3v8jG+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8gGN7/MS7i/4CK8v+CjPP/Rkfn/yMZ3v8mHN//JSnj/yRD + 7v8jSfD/I0fv/yNH7/8jR+//I0fv/yNG7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH8P8jR/D/JEr2/xwv + t/8WEXz/IhnD/ycm6v8kRPL/I0jw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fx/yNJ9P8kSvf/I0jw/yE+ + 2/8PGpz/UlGZ//r8//8rLTj/AAAA/wAAAP8KCgr/39/fy////wIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBR2oQ0aj/x0apP8lHef/FhOH/xwe + ff8YGHb/HRew/yYe7v8lHej/JR3n/yUa5/8lJuf/Ij3K/iRJ8v8jR/D/I0fv/yNH7/8jRu//I0fv/yNH + 7/8jR+//I0bv/yNH8P8jR+//I0fv/yNG7/8jR+//I0fv/yNH7/8jRvD/HELx/5ai0P+jrs3/vcDG/z5e + 6P8dQ/H/I0nw/yNF7/8jNen/ISPj/56gy//Oz8L/SEba/yAV4P8mHd//Jh3f/yYd3/8mHeD/Jh3f/yYd + 3/8jGd//Jx7f/yYe3/8hG+D/JSvk/yQ56v8jR/D/I0jw/yNH7/8jR/D/I0bv/yNH7/8jR+//I0fv/yNG + 7/8jRu//I0fw/yNH8P8jR+//I0fv/yRK9/8eNML/FRF5/yMczf8nIOf/Jhzf/yUn4/8jRu//I0fw/yNH + 7/8jR/D/I0fv/yNG7/8jR+//I0fv/yNH8P8jSfL/I0z1/x5G9v9JYdr/VFVa/wAABP8AAAD/AAAA/zMz + NP/9/f2xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAEg4PQug5O53/Ghak/yMc2P8UE3X/ExJ2/xUTfv8jHdv/JR3q/yUb5/8lH+j/JDPr/yNH + 8P8kSff/I0fx/yNH7/8jR/D/I0fv/yNH7/8jR+//I0fv/yNH7/8jRu//I0fv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fw/yNH7/8YPvL/fY/X/5ek0P+dqM7/jZvT/xk/8v8jR+//I0jw/yFJ8f8ZPvH/eorX/9fV + v/9ocdf/GyLn/yUp4/8lJuL/JSbi/yYm4v8lKOP/JSzl/yUw5v8jNej/Izzr/yNE7v8jSPH/I0nw/yNH + 8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0bv/yNG7/8jR+//I0fv/yNH7/8jSPP/IkDf/xUV + e/8hGr7/Jx/n/yYf4P8mHuD/Jh3f/yRA7f8jSPD/I0fv/yNH7/8jR+//I0fv/yNH7/8jSfD/I0Tu/yQ5 + 6v8lMuf/JjDx/x4mrP8AAAD/AAAA/wAAAP8AAAD/ZGRk/////4IAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAERJX/ywvkv8ZFpv/JR3o/xkV + kv8UE3f/Hxm8/yYc7/8lHOf/JSvq/yNB7v8jSvD/I0fv/yNG7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH + 7/8jR+//I0fv/yNG7/8gRO//IUXv/yNH7/8jR+//I0fw/yBE7/8kR+//IETv/xM68f9bc9//r7TJ/1hz + 4f/FxsT/Olrq/xxB8f8sT/D/jaL3/xxB8v9Tb+L/1NC//4ub1P8bQ/L/I0jw/yNH7/8jR+//I0fv/yNI + 8P8jSfD/I0nw/yNJ8P8jSfD/I0jw/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0bv/yNG7/8jR+//I0fw/yRJ9f8ZI5r/GxWc/ycg5/8mH+D/Jh/g/yYc3/8mKOT/I0bv/yNH + 7/8jRu//I0fv/yNH7/8jR+//I0jw/yU26f8mIeH/Jhzf/yYc3/8nHeb/IRu7/wICBP8AAAD/AAAA/wAA + AP+Pj5D7////MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAABAG0SEmD/HByE/xYUg/8kHeP/JRvo/yMZ2f8mH93/JSvW/yQ96/8jSfD/I0jw/yNH + 7/8jR+//I0fw/yNG7/8jR+//I0fw/yNH7/8jR+//I0fw/yNG7/8jR+//Ikbv/zda8f8sT/D/HkLv/yNH + 7/8bP+//aIT1/zxd8f8kSfD/t8b8/97i8v/Cw8T/Olvo/6auzP+KmdT/EDfy/zlb8f+ouvn/HUHw/zVW + 6//DxMT/q7LL/yVJ7/8fQ/D/IUXv/yRH7/8jR+//Ikbv/yNG7/8jR/D/I0bv/yNH7/8jR+//IUXv/x9D + 7/8jR+//I0bv/yNG7/8jR+//Ikbv/x5C7/8iRvD/I0fv/yNG7/8jR+//Ikbv/yNG7/8kSfT/ID3W/xYT + f/8lHdb/Jh/j/yYf4P8mHd//JiHg/yRC7v8jSPD/I0bw/yNH7/8jR+//I0fw/yNK8f8lNOf/Jhvf/yYd + 3/8mH9//Jh7h/yYf4v8oIef/DQ4+/wAAAP8AAAD/Dg8O/9zc2akAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAkhESX/8UEn7/FRF8/yMb + 1P8mJd3/JTTW/yRB2P8jSer/I0nw/yNH7/8jRu//I0bv/yNH7/8jRu//I0fv/yNH7/8jR/D/I0fw/yNH + 7/8jR/D/I0bw/yNH7/8gRO//QGLy/3GO9v87XfH/HkLv/yJG7/+Lovf/I0bw/2qF9f/Z4v3/x9P4/9DP + xv9SbeH/Um3j/8XGw/9LaOz/Rmf0/3CN9v8YPe//IUbw/6auzP/FxcT/NFXp/zVY8/8vUvD/HUHv/yBE + 7/8nSvD/HkPv/xxB7/8kR+//Ikbw/xtA7/8rTvD/PV/y/yBE7/8iRu//I0fw/yJG7/8mSu//QmPy/yJG + 7/8jRu//IUXw/yRH7/9TdPP/I0fw/yRJ9/8bKKX/HBWi/ycf5/8mH+D/Jh/g/yYc3/8kN+n/I0nx/yNH + 8P8jR+//I0fv/yNH8P8jSvD/JDjp/yYd3/8mHuD/Jh/f/ycg5P8mH+L/IBjX/x8X6v8VE3b/AAAA/wAA + AP8sLCrz+fn/JgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAACjDw1Q/xQQev8aH4//JD3U/yNH5v8jSfD/I0jz/yNH8P8jRu//I0fv/yNH + 7/8jRu//I0fv/yNH7/8jR+//I0fw/yNH8P8jR+//I0fw/yNH7/8jR+//I0fv/yNH8P8dQe//Jknw/1+A + 9f8lSe//MVPw/22J9f8UOe7/ZYD0/y9S8f8ZQPD/q7PM/4OT1v8ZQPL/qbDI/9PV2v/F1P7/jKP3/xg9 + 7/8ZQPL/gpLW/9TRwP9NaOH/Tm/2/22K9f8wVfH/Vnf0/3+b9/+NpPj/S2vy/xk+7v8mSu//PFzx/3qT + 9v91j/b/Fzzu/zlf8f8mSvD/HULv/2WD9f+luPn/dI72/xY77v8/Y/L/LlHw/2eE9f8kSfH/IkXr/xga + jP8jG83/Jh/k/yYf4P8mHd//JSTh/yNG7/8jR+//I0fv/yNH7/8jR+//I0nw/yQ46f8mHt//Jh7g/yYf + 4P8nH+X/Ix3N/w8Mi/8uLpD/S0yv/xMVQP8AAAD/AAAA/xoZbPZFP+43AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK4PEkz/GR6Y/yJC + 5P8jSvX/I0fx/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH7/8jR+//I0fw/yNH + 8P8jR+//I0fv/yNH7/8jR+//I0fw/yNI8P8eRPD/M1ny/z5k8v9xjPb/UXP0/xU97/9lgvX/Kk7w/w00 + 8f+Il9P/qbHM/xU88/9het7/vsDD/1l37v+Mpfn/I0fv/xk/8v9cdeD/1tK//3KG2f8xVfT/hqH3/ytR + 8f8qTfD/I0fv/zVX8f+Qp/j/T27z/1Fw8//G0/z/t8f7/yRI8P8eQu//TnHz/ypO8P8oTPD/PWHy/xM4 + 7v9ph/X/IkXv/15+9f9nhfX/YoD0/yBG9P8gPNT/GRaT/yYf4v8mH+D/Jh/g/yYc3/8lM+f/I0nw/yNH + 7/8jR+//I0jw/yNI8P8lMuf/Jh3f/yYe4P8mHuD/Jh/j/yQe0/8KB3n/WVya/+Lk5P/x8+v/zc/Y/1db + ef8HCFX/JB3i/yAW4a0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAYMqkMFy2PyyNE4f8jRu//I0jy/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH + 7/8jR+//I0fw/yNH7/8jR+//I0bv/yNH7/8jR+//I0fv/yNH7/8jSPD/I0nw/yNJ8P8jR/D/I0Pt/yQ/ + 7P8aMun/f5T0/9fi/P8fNOj/GCvo/3GE8v+Mo/f/VXX3/22B3P+/wcX/K0zs/y1S8P/Jy8j/b4LV/11+ + +P82WvL/G0Lx/ztc6v/IyMP/mKTP/yJI8f9wi/b/H0Pv/yFF7/8jR/D/HULv/yFF8P9wjfb/Zoj1/xxC + 7/+csPj/QGHy/xY77v9qiPX/PV7y/xI47v9UdvT/RGXy/3iT9v8mSfD/NFnx/8bU+/9mgvX/GkH1/x0z + vf8dFqj/Jx/m/yYf4P8mHuD/Jh7f/yQ+7P8jSPD/I0fv/yNJ8P8kQe3/JSjj/yYc3/8mHt//Jh/g/yYf + 4P8nH+X/EQ2W/zw+iv/q6+v/8vHt/+rq6v/y8e//9fbz/4eJwv8ZGMb/JCzp8SYc3xoAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJEfzPiVK+9ElS/z/I0j0/yNH + 8P8jR+//I0fv/yNG7/8jR+//I0bv/yNH7/8jR+//I0fw/yNH7/8jR/D/I0fv/yNH8P8jR+//I0fv/yNH + 8P8jSfH/I0jw/yNC7v8kOOr/JS7l/yYm4v8mIeH/Jh7f/yAW3v9DROT/uMD1/4SF7f8WCtz/SEbk/9zi + +v+DiPD/qbDn/8jJwv9ITNv/Gx7n/6+z3f++wcT/Wm/r/0Na7v8dNev/JkHs/66zyf+4vMf/K03s/2WA + 9f8lSO//IUXv/xc87v8bQO//JErw/19+9f+owfv/a4j2/26O9v9LbvP/FDvv/1l89P/E1fz/Xnz1/ylQ + 8f+wxfv/gJn3/xg/7/8oUPH/Vnf0/zFW8f8iSff/Gyyv/yAYuf8nH+X/Jh/g/yYe3/8mIuH/I0Tu/yNI + 8P8jSfD/JDfp/yYg4P8mHd//Jh/g/yYf4P8mH+D/Jh/j/yIb0f8REHr/uLvQ//b18P/p6er/6unq/+rp + 6v/s6+v/9/fs/4CEw/8YN+X/JSzmSgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACNG73EjRu/5I0fw/yNH7/8jR/D/I0fv/yNH7/8jR+//I0fv/yNH7/8jRu//I0bv/yNH + 7/8jR/D/I0fv/yNH7/8jR/D/I0fv/yFF8/8dQ/X/HD7y/x4z7v8lKuX/JiHg/yYc3/8mHN//Jh7f/yYe + 4P8mH+D/JBrf/ywp4f8wMuL/oKby/0hF5f8WDt7/qKvz/9bZ+v/P1PP/1tfG/2lo0/8UCuD/UE/k/8jJ + xv+lrNf/S0vo/x8V3v8eF+H/iozO/87Pwv9CQ9v/VFTq/x8f4f+DjPD/q7r3/6Gw9v+suPf/mZ7x/zY8 + 5v9dYen/V17p/yMr5f8iKeT/LDjm/4mO7//Lz/j/NDvm/yUq5P8iJuT/JSnk/yYt5f8eKuX/IjPo/yVC + 8f8bJ6X/IRnD/ycf5P8mH+D/Jh3f/yYk4v8jSfD/I0Xv/yUu5v8mHd//Jh7g/yYf4P8mHuD/Jh/g/yYf + 4P8nH+b/Ew6v/2Nmpf/9/vf/6+vr/+7u7v/v7+//7Ozs/+rp6v/t7ev/7Ovm/0pk5P8cQvGRAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNH7wUjR/CaI0bv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH7/8jR+//I0fv/yJH8v8dQ/T/KkrZ/0lb + vv9bYLP/UU60/ykh2f8lHeH/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8vLeH/JyLg/zI14v85NeP/VVfn/xwT + 3v8sJ+H/pKXy/7O19P+2ucj/j5HN/x0X4v8YEeH/cHLS/8/Txv9ZXeP/HhXg/x0U4f9jY9X/1tfB/2Jh + 1f9BPub/JBvf/0M94/+mpvL/tr31/zIt4f8ZEd7/Ihnf/x8W3/8fFt//Jhzf/yYd3/8mHd//HRbf/yEc + 3/8mHd//JBvf/yYc3/8mHd//Jhzf/yYc3/8mHN//Jx/j/xsanP8iHMn/Jx/k/yYf4P8mHuD/JSHg/yUv + 5v8mJeL/Jhzf/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/ycf5f8WE5v/QUNX/8DAu//08/T/2tra/9fX + 1//n5+f/9vb2//b29v/59+z/lKbs/xc98N0jR/AKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAjR+8OI0fvsyNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH + 7/8jR+//I0fv/yNI7/8hRvT/Hj7p/1BhtP+anKn/uri8/7+/w/+oqqj/MS/G/yMb5P8mH+D/Jh/g/yYf + 4P8mH+D/Ixrf/0BD5P9EROX/Ly3h/zw85P82N+P/JBzg/yMc4P8bE97/FQ7g/5KTzP+ytcf/JiHg/yEY + 4f8vK97/wcTF/3+C0/8bE+H/IBjh/0JA2//NzsP/h4jP/zk55v8oIOD/IBjf/xcP3f9dXuj/g4nu/yMc + 3/8lHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/JR7g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuL/Gxeb/yMdzf8mH+P/Jh/g/yYf4P8mHuD/Jhzf/yUa3/8kG9//Jh7g/yYe4P8mH+D/Jh/g/yYe + 4P8mH+D/Jx/l/xcWfP8AAAD/FBQT/zk5Of8UFBT/EBAQ/ykpKf9ZWVn/mJiZ/+fm3v+6xO3/HUPv/SJG + 7zEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0bvDSNH77wjR+//I0fv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fw/yNH7/8jR+//I0fw/yNH7/8jR+//I0fv/yNI8P8jSvD/ID7y/yQy2f9tcaP/trW2/72+ + y/+kpbH/uLnC/4SGqP8jHtj/JR7h/yYf4P8mH+D/Jh/g/yYf4P8lHuD/JR7g/1lc6P99g+3/aW3q/yYf + 4P8lHuD/Jh/g/yYe4P8cFOL/c3PS/8nMw/85Ntz/Ihng/xsT4f95e9H/wMPE/zAs3v8gGOH/Kibf/7W3 + x/+oqcn/P0fl/ych4P8mHuD/Jh/g/xcP3f9hY+n/Vlfn/x8X3/8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf4v8bF5r/Ix3L/yYf4/8mH+D/Jh7g/yYe + 4P8jHN//KC/l/y476P8lHuD/Jh7g/yYf4P8mHuD/Jh7g/yYf4f8mH+L/FRRv/wABAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8BAQH/ysnE/9PY8f8mSO7/IUXwVwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNF + 7QEjR/CmI0fv/yNH7/8jR+//I0fw/yNH7/8jR+//I0fv/yNH7/8jRu//I0fv/yNH7/8jRu//I0fv/yNI + 8P8jSfD/JELt/yAr7P8pJs3/fn2f/7y9wP+5usr/t7jI/4eIjf+oqar/UFC2/x8Y5f8mH9//Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8lHt//IRnf/ych4P8iGt//JR3g/yYf4P8mH+D/Jh7g/x4W4f9VVNj/0tTC/1ZV + 2P8dFeH/Ihrg/zMw3f/FyMT/cnPS/xsU4v8fGOH/kpTN/7y9w/9/iuj/Pjzl/xoQ3v8jG9//GRDe/z49 + 4/9rcOr/HRXf/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8mHuD/Jx/j/xoXmP8iHMj/Jx/j/yYf4P8mHuD/JBvf/yk86v99lPf/YG/w/x8Y3/8mHuD/Jh/g/yMb + 4P8lHuD/Jh/h/ycf4/8VFHT/AAEA/wAAAP8AAAD/AAAA/0ZGR/+hoaH/RkZG/wEBAf/Ozcr/5uj3/y1O + 7P8gRPBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAB0WvA4fGbwhIhy+LiAavSglHL0QHjPdPh1D8P8fRPD/IEbx/yJH8f8iSPH/I0fw/yNH + 7/8jR+//I0fv/yNH7/8jR+//I0fw/yNJ8P8jSfD/JEHt/yUv5v8hG+X/KiPK/4WGoP++v8T/t7jI/7a3 + xv+2t8b/ubrJ/62vs/85NcH/Ihrl/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/JR7g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/IRng/zw63P/LzcP/eHnR/xsU4f8mH+D/GxTh/4GD0P++wcX/LSne/xoR + 4v9rbNT/y8zA/4mQ4P+8wfj/SETl/zEt4f9TUub/pKzy/0VE5f8gGN//Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8nH+T/GxeZ/yEbwv8nH+T/Jh/g/yYe + 4P8gGd//aXjx/6ay+v9YY+3/Hxjf/yYf4P8mHt//SU7n/ygj4P8lHuD/JyDm/xcUg/8AAAD/AAAA/wQE + BP+dnZ7/9vb2//r6+v/v7+//b29w/5uamP+vsr3/MFLq/yBE8G0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcA/JiDAeiEavaJISdHJeILp4X2H7PV7huv8cHnl+S4q + wuRTWtnmT17i/z1Q4P8zSOH/KUPk/yE/5/8dPun/HD/s/x1C7/8hR/H/I0nx/yNJ8P8jR/D/JD3r/yUs + 5f8mH+D/Ihjk/yoly/+Fh6L/vb/F/7e4yP+2t8b/trfG/7a3xv+4ucj/t7m8/05OtP8eFub/Jh/g/yUf + 4v8kHuT/JR7j/yUe4f8mHuD/Jh/g/yYf4P8mHuD/Jh7f/yYf4P8mH+D/Jh7g/yYf4P8kHOD/KiXf/7e5 + xv+cnsv/Hxnh/yYe4P8hGeH/OTfd/8nMw/9sbdT/FQ3i/0lH2v/Q0sL/bG7R/3p87//Z4Pr/1Nz6/8TJ + 9/9YWef/HBXe/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf + 4P8mHuD/Jh/g/ycf5f8bF5//Hxu2/ycf5f8mH+D/Jh7g/yIZ3v8+Ruj/l6j5/19o7f8dFd7/Jh/g/yIa + 3/9zffD/QEDl/yEZ3/8nIOn/HBic/wIDAv8AAAD/X19f//z8/P/s6+v/6enp//Hw8f/j4+P/EBAN/0ZH + VP83VvL/IUfyaQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYh + v84fGb3/GxW7/01R0/90gOb/aG7e/29y4P9eZd//LCjH/2Vr4f+Ci+v/g4rq/4CJ6P+Aiun/fonr/3OB + 6v9fbuf/R1nj/y1D4f8iPej/JTLo/yYl4v8mHd//Jhzf/yMb4/8nI9D/g4Wh/76/xf+3uMj/trfG/7a3 + xv+2t8f/trfG/7a3x/+9vsf/enyn/yMe2P8iHen/KB/b/y0gz/8rINP/Jh/g/yMe6v8hHuz/Ix7p/yQe + 5P8mHuD/Jh/f/yYf4P8mHuD/Jh/g/yUe4P8gGuH/nJ7L/7q8xv8sKN//Ixvg/yYf4P8cFeH/iYzO/7q8 + xv8oI9//LCje/72/xf+Ymcz/FA7f/zAs4f8+O+T/Ix7g/x0V3v8mH+D/Jh/g/yYe3/8lHuD/Jh/g/yYf + 4P8mHt//Jh/f/yYf4P8mH+D/JR/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jx/m/x0Zqv8dGaf/Jx/m/yYf + 4P8mH+D/JR3f/yEh4f+FlPb/lZ/2/zQy4/8jGt//IBnf/3R98P9nbu3/Hxbe/ycf5v8iHMP/AAAN/xQU + Ef/R0NH/8fHx/+np6v/p6en/9vb2/7CwsP8AAAD/ZGh9/y9P7/8gPe9XAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIBrDYF5e2dCoqu35s7Tx/7i58v/ExPb/0dD5/1BN + 4/8cFN3/IRrb/yMd2v8mH9j/KiXW/zMw1f9ISdv/ZGnh/3iA5v+Lle7/bnbf/yIbyP8mHOD/Jh3f/yYf + 4P8lHuH/IRvc/3Byo/+9vsL/t7jI/7a3xv+2t8f/trfG/7e4x/+3uMf/urzM/7q+z/+us7z/dXeT/1Es + Y/9eJjv/Yygw/2EoM/9bJ0X/USZk/0Mki/83IrD/LCDR/yMf6P8iHuv/JR7k/yYf4P8mH+D/Jh/g/xwV + 4f99ftH/z9HC/0NB2/8gGOD/Jh/g/yAY4f8/Pdz/zc/D/2Zn1P8WD+P/nJ7L/7i7xv8rJt//IRjg/yEZ + 3/8lHd//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8nH+X/IBu7/xoXlv8nH+T/Jh/g/yYf4P8mHt//Ih7g/1lw8f9qfPL/MzDi/yMb + 3/8gGN7/bHTv/4uX9f8oIuD/JR3h/yYe4/8KClH/Rkc///X09P/q6ur/6enp//Lx8f/39/f/RENE/wIC + AP9zeaH/H0Hr/zAz5tQpH99NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAmJz3A+Lh/ynU1P1f0tL8mcXI+tHT1vz6ZmXq/xwS3v8mHeD/JR3g/yUd4f8lHeH/JBvh/yEZ + 3/8fFtv/IRnX/ygi1/8wLNz/Jh/c/yYf4P8mH+D/Jh/g/yEZ5f9EQ7f/srO0/7m6yv+2t8b/trfH/7a3 + xv+4ucj/tbbF/7W4yP+fn6n/e2Zo/25JSP9rOjH/aCkb/2coHv9mKCH/Zygg/2goHf9pKBn/aikY/2gp + Hv9iKDD/VCZa/z8jmP8rINL/Ih7s/yMe5/8mH+D/HRXh/19f1v/W18H/YWHV/xwV4f8mH+D/Jh7g/x0W + 4f+TlM3/tLfH/x4Z4f9yc9L/ztDD/0I/2/8hGOH/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4/8jHc7/GBWL/yYf + 3P8mH+H/Jh/g/yYe3/8mHuD/H0Du/x016v8kGt7/Jh/g/yAY3v9kbO3/n635/0RF5v8gF9//JR3m/xwZ + rP+OkZv/8vHt//Ly8v/39/f/19fX/2RkZP8AAAD/MC8m/1xnuv8VNez/ZnDu/4uX9f9BQuXAIBjfMwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmKTzA2p3 + 6ikwM+JeIyjk4CUn4/8lKeT/JSXi/yYe4P8mHuD/Jh7g/yYf4f8mHuH/JR3h/yQc4P8mH+H/Jh/g/yYf + 4P8mHuH/IRrc/3t8qP++v8f/trfG/7a3xv+2t8b/uLnJ/7Kzwv+Qk5v/gHV5/2QxLf9hIBv/ZCIe/2Ul + If9lKCT/Zigk/2YoJP9mKCT/Zigk/2YoJP9lKCT/Zigj/2coIP9pKRr/aSkZ/2EoMv9MJXL/MiG//yMe + 6f8dF+n/RUPb/8/Rwv+Ehs//HBXh/yYe4P8mH+D/Hxfh/0dG2v/P0cL/W1rX/0VD2v/S1ML/YGDV/x0U + 4f8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYe4P8mHuD/Jh7f/yYe + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf3/8YFoz/IxzK/ycf4/8mHuD/Jh7f/yYe3/8kP+z/I0Ht/yYe + 3/8mHuD/IBje/2Nr7f+hrvr/cHjv/x8Y3v8mH+L/GxTU/1JTq//09O//sK+u/1VVVv8QEBH/AAAA/w0M + B/94eIn/Jz3H/yE68P8sJeD/Vlrq/3uG8f9cYuvtJh/gPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiSfHUI0fv/yQz5/8mIuH/Jh7g/yYf + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yQc4/8sJ83/nZ+t/7u8yf+2t8b/trfG/7e4 + x/+0tcT/jpGZ/3lsb/9jKCP/ZSQg/2UoJP9mKCT/Zikk/2YoJP9mKCT/ZSgk/2YoJP9mKCT/ZSgk/2Yo + I/9mKCL/Zigi/2YoIv9mKCP/Zygf/2opGP9mKST/UyZg/zIetv8uLOf/vL/K/6aoyf8kHuX/Jh7m/ycf + 5f8mHuP/Hxnj/5udzf+srsn/MjDe/8HDxP+Fh8//HBXi/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYe4P8mH+D/Jx/m/xwY + of8eGa3/Jx/m/yYf4P8mHuD/Jh3f/yQ66v8jS/H/JTDm/yYc3/8gGN7/Y2vt/5yo+P+Vovf/Pj7l/x8W + 3v8oIeX/ZWnJ/4mNu//Awcb/AwMA/wAAAP8TEgr/e3qA/0BGq/8bP+f/Ji/n/yQa3/8fFt7/Ihvf/1JW + 6f81MuOfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJCnkSyM96/glKOP/Jhzf/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yYf + 4P8mH+D/Ihrk/zczxP+qrLL/ubrJ/7a3xv+2t8b/ubrK/5KTm/+Fg4j/ZjEt/2UlIP9mKCT/Zigk/2Yo + JP9mKCT/ZSgk/2YoJP9mKCT/Zikk/2YoIv9qJxn/aScc/2coJP9lKCf/YSQk/2EjIf9iIx7/YiIc/2Qi + Gf9nIw//ZyMV/1QiUf+yqLH/yszI/zAssf8ZEqf/IRm9/yMbzf8dFNb/TUzT/9DSw/9ZWtb/mpvM/6qs + yf8kHuT/Jh7m/ycg5v8nH+X/Jx/k/yYf4v8mHuD/Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8nH+T/IhzG/xkWkv8nH+P/Jh/g/yYf4P8mHN//JTLn/yNK + 8P8jRu//JSjj/x8V3f9jaez/m6j4/5ml+P+BjPP/KCPg/zAs4f/8////e3yK/y4xV/8aGjz/DAwf/1FU + gv87R7b/GTnf/yRH8ugmIuGUJh7f5CYf4PslHd//Ixrf8yYf4EEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUd3wclHuDXLkLq/yYf3/8iGd//Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8hGeX/Pj2//6+xtv+4ucn/trfG/7i5 + yP+srbr/i4+W/4Brbv9hIBv/Zigk/2YoJP9mKST/Zigk/2YoJP9mKCT/Zigk/2YoJP9mKCL/ZSko/0s1 + fP83Prf/MEDJ/zNEzf9PWcr/WmHG/19jwf9fYLn/X1uv/2BXpv9USZv/QTOG/2hvuv+Hm9z/MkCs/xUf + mf8XIJj/FBuW/xIVkv8ND43/mZy1/6Ciuv92eKn/zMvD/ywpnP8XEZ7/Hhiu/yEbvv8jHc3/JR7b/ycf + 4/8nH+b/Jx/l/yYf4v8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+L/GheV/yIcyP8nH+T/Jh/g/yYd3/8lJuL/I0fv/yNI8P8jRO7/HTfr/1Zr8f+cp/f/lJ/3/5+s + +f9PVOn/YF/q/////f9BQDz/AAAA/x4nrP8kPeH/Hz/l/x5C8P8kS/T/JDHmoQAAAAAmH+AQJh/gKiYf + 4DkmH98lAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJh7gIyEa3/dKT+j/c33w/zk45P8gGN//Hxfe/yEY3/8iGt//Ixvf/yQc4P8lHeD/JR3g/yUe + 3/8mHuD/Jh7g/yEY5f9EQ7v/tLW6/7i5yP+2t8b/ubrK/6ChrP+RlZ3/dFFR/2MiHf9mKCT/Zigk/2Yp + JP9mKCT/Zigk/2UoJP9mKCT/Zigk/2kmGf9QMmj/HEv//x9J+v8bRPf/QGP4/56s//+erP//nav//52r + //+cq///nav//5ip//8/Y/7/Fj71/xpA8v8iSPX/I0j0/yxQ9P9DYfL/TWjt/0hi5f9TauT/a4Li/1Jo + 0f+El9n/MD+q/w0Wj/8QFIT/EBF8/xIQef8VEX//GBOO/xwWpP8gGb3/JB3S/yYf4P8nH+b/Jx/l/yYf + 4v8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf5f8hG8H/GheY/ycf5f8mH+H/Jh/g/yYd + 3/8kO+v/I0nw/yNI7/8gRvD/MVPx/5Cd9/+cpvj/kZz1/ywp4f+2uf//sbGn/wAAAP8BAAD/Hzu2/yRL + //8jSPL/I0jw/yNB7f4mIOA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmHuA8Jh7g/yEZ3/9GR+b/fory/3R9 + 8P9XW+r/R0fm/z085f81MeP/MCzi/ysn4f8mIuD/JB3f/yMb3/8iG9//GxTk/0JBuv+0trr/uLnI/7a3 + xv+6u8v/n6Cq/5OXoP9uREP/ZCQf/2UoJP9mKCT/Zikk/2YoJP9mKCT/Zigk/2UoJP9mKCT/aCgc/1Ys + Vf8iPfH/IEP4/yFE7/8pS+//Umzy/1138/9eePP/Y3z0/2N89P9jfPT/Z3/0/zla8v8fRe//I0nw/yNJ + 8P8eRfD/SGjz/5Gg+P+eqfn/oqz6/5yn+v+Snvr/jJv7/3yO+P93i/v/aH73/1Vu8P9CXOX/MUnX/yI5 + xf8YKrH/EyCd/xEZjv8TFYr/FxWR/xwWpP8hGb3/JBvS/yYd3v8nH+T/Jx7l/ycf5P8mH+P/Jh/i/yYf + 4f8mH+D/Jh/h/ycf6P8dGab/Hhqu/ycf5f8mH+H/Jh3f/yUm4v8jR+//I0fv/yNH7/8dQu//Smby/42d + 9/9HVOv/LTfp/+bq+f8zMi//AAAA/wAAAP8aNKr/JUr7/yNH8P8jSO//JSnjogAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACIa31ceFt//HBPe/xcP3f8jH+D/Vlrq/3qF8f9+ivL/eYbx/3uF8f96hPH/fYny/4SQ + 9P+FjvT/fYby/3iB8f9xe/X/cXrN/6mpsv+5usr/uLnJ/7m6yv+io6//lJii/3BLSv9kIhz/Zigk/2Yo + JP9mKCT/Zigk/2YoJP9lKCT/ZSgk/2UoJP9mKST/aCce/1gmT/82JbP/JCPn/yAj7f8eIOT/HiHi/x8i + 4/8eJOP/HiXj/x4l4/8eJuT/Iyrk/yUs5P8lLeX/JS/l/yQ06P8iOer/K0ft/z9b8P9TbvP/aID1/3uP + 9v+Km/f/lqP4/56o+P+iq/j/oqv5/56p+/+Wo/z/iZr9/3iN/P9kfPr/Tmr0/zhW6f8mQ9j/GjPE/xUo + s/8WIan/GR2o/x0brP8bFbb/HhbC/yAXzv8iGtf/Jh7e/yYe4v8mH9//JB3S/xwYpP8UEnT/HBik/yYf + 4v8mH+P/Jhzf/yUx5/8jSfD/I0fv/yNH7/8fRO//J0vw/xQ67v92k///trWu/wAAAP8AAAD/AAAA/xgw + mv8lS/z/I0nw/yUw5tkmHN8VAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAODPjalFQ5/90dO3/kpPx/5uf + 8/8xLeH/HRbe/yQe4P8lH+D/JyHg/yok4f8xLuL/REPm/2x07/+KlvX/kZ/2/5ek+v+Snen/mpuq/7e4 + xv+pqrf/uLnJ/6ytuv+RlJz/joSL/18lIP9lIx//Zicj/2YoJP9mKCT/ZSgk/2UoJP9lKCT/Zigk/2Yo + JP9mKCT/aSkb/2gnHf9XJFD/Oh2g/yQY1/8dFOL/Ixng/yYc3/8mHN//Jh3f/yYc3/8mHd//Jh3f/yYc + 3/8mHN//Jhzf/yYd3/8kHd//IR7h/x4i4v8eKOX/IjTo/ylA6/80UO//QmHx/1Rw8/9lfvX/dYr2/4WW + 9v+Sn/f/nKb4/6Kq+P+iq/n/m6b6/42c+v95jfv/YXv7/0hn9v8yU+z/HDzd/21/0v9CUrr/KjSl/ycp + lf8WE4r/FxKH/xcSg/8VEnn/FBJ1/xQTev8TEnP/Gxea/yYf3/8mH+T/Jh3f/yQ46v8jSvD/I0fv/yNH + 7/8hRe//GT/w/7rK/v9QTkX/AAAA/wAAAP8AAAD/GTCP/yVM/v8lMufhJhzfKwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAADIyfqJ1NX8/9ra/f/b2/3/4uP+/1RR5/8eFt7/JR7g/yUe4P8lHuD/JR3g/yQc + 3/8hGd7/IBjf/yci4P8uK+L/Mi/i/zY26P95frL/r7C1/4yMk/+3uMf/ubrK/5GRmf+fo67/j4aN/2c4 + NP9hJB7/YSId/2EhHP9iIRz/YiEb/2IhHP9iIRz/YiEb/2IhHP9hIhz/YiUf/2YsHv9rPC7/alRx/1xb + yf81MuP/IRrn/yQc4/8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe3/8mHt//Jh3f/yYd + 3/8mHN//JBzf/yId4P8gIOH/Hibk/x0u5/8gOer/JUTu/y9R8P88XvL/TWvz/1959P9zh/X/hpX2/5ah + 9/+gqvj/oar4/5ij+P9+kPn/ws3//4+l/v+Sqvz/aYXv/xMy1v8WK7r/FB+b/xUWg/8UEHX/FBBz/xQS + ef8TEnT/GRaQ/yUe1/8nHuX/Jh/f/yQ36f8jSfH/I0nw/x5D7/89YPr/wMXQ/wgHBf8AAAD/AAAA/wAA + AP8UJIP/Hif0wyAW3yMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANfW/JPY2Pz/2tr9/87Q + +/+0t/f/RkPl/yAY3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/JR3f/yQc4P8dE97/OTjo/4eS + 3/+bnKb/u7zK/7a3x/+3uMj/tLXE/4qKkf+Mj5X/lpih/5KLk/+HeH3/gW1w/3xmaP98ZGb/eF1e/3ph + Y/98ZWf/fmls/4Z1ef+QiZD/oaCr/6+ywP+5vMX/vL3A/6Cjuf9hY7HyLirO3CQd4v8mH+D/Jh/f/yYf + 4P8mH+D/Jh7f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh7g/yYd3/8mHN//Jhzf/yYd + 3/8lH+D/IyTi/yAq5f8eMun/HDrs/x5C7/8kSvD/MFXx/0Ni8v9Zc/T/c4b1/4WU9v/Gzfv/ys/7/8jO + +/+qtfr/cYb5/1l1+/8+X/f/J0jo/xgyy/8XIqL/FhV+/xQPc/8TEXH/FxWF/yMdyv8nH+f/Jh3g/yUr + 5P8kPuz/Fzzv/4Ca//+Eg3r/AAAA/wAAAP8AAAD/AAAA/0JCdNFdVeMIAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAArrT2mZ6g8/9sbev/QD7k/yQe4P8iG9//Jh/g/yYe4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/JBvf/y4r4f+EkPP/mab+/4eNw/+ioqn/u7zK/7i5yf+4ucj/t7jH/5qb + pf+goaz/vL/Q/7u/0P+6vs7/ub7O/7q/z/+6v9D/vMHS/73C0/+7v9D/trrJ/6yvvf+foKvtj4+WzYCA + hKFzc3Vzbm1pRnNzXR9GR6kJIhrnQCYf4KEmHuDtJh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mHuD/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHd//Jhzf/yYc3/8mH+D/JSPi/yQr + 5P8hMuj/Hjrs/xw/7v8eRfD/G0Lv/5it+f+QpPj/q7j5/6Kt+P+cpfj/o6v4/5ql+P+ImPj/Z4D7/zFV + 9/8dPeH/HS20/xcZh/8TD3D/FBF1/x4Zrf8mH9//Jx3l/yYe4P8eIeT/tb30/zc2Lf8AAAD/AAAA/wAA + AP8AAAD/enp01NbWzgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkIN+fFQ/d/xEJ + 3f8ZEt7/IRvf/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8fF979aG/ulZ2q + +YyUoPielKD8t4ePyMySk6Dnr7C8/7q7y/+7vMz/vr/Q/72+z/+6u8v/u7zM/7u8zP+4ucn/sbLB/6eo + s/+YmaPnioqQxHx9f5hxcXJnamppPGZmZRkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYe + 3xwmHuBsJh/fwiYe4P0mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mHeD/Jh3f/yYc3/8mHuD/JiPh/x8l5P87T+z/tMX7/ztc + 8f+Qqvn/M1fx/0Fg8v9pf/T/jJr2/56o+P+jq/j/kZ33/z1d9P8gR/f/I0Xr/x4zxP8YHpL/FBFw/xcS + g/8gGrr/IBfd/Tk26/6/wc//BwcE/wAAAP8AAAD/AAAA/wAAAP+Pj5D1zc3NHAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAADw646ljY+r/hYfv/6Ol9P+UmfL/KSTh/yQd4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yAX3p0AAAAAAAAAAAAAAAAAAAAAAAAAAHZ5mRVubWhlhYaKt5aX + n+Cgoav2oaKt95ycpvCTlJzaiIiOwHx8f5ZxcXJmaWlpNWZmZRMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh/gMyYf4IcmH+DZJh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh7g/yYe4P8kGt//HBPd/6Sn8/9rbev/X2Tr/4uX8/8ZL+n/Hj7t/xxD7/8qUPH/TGnz/3OH + 9f+Xovf/dIj1/x5D7/8jR/H/JEr2/yRI8v8gOtP/GiWf/xQWfqQeGsQjm5n8dLKzrP8AAAD/AAAA/wAA + AP8AAAD/CgoK77CwsPTJyck/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAxsn5uNna + /f/a2v3/3t79/9TV/P84NeP/Ihrf/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+DVIhvfEAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ2dlDWhoZyNpaWclZmdmGWZmZQkAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACYf4AkmH+BJJh/goiYf4OkmHuD/Jh/g/yYf4P8lHuD/JR7g/yUd + 4P8lHeD/JR7g/yUe4P8lHeD/JR3g/yUd4P8kHd//JBzg/yQc4P8iGt//HBTe/yMc3/98gO7/i4/w/0ND + 5P+xuPb/Pzvj/yEX3v8mIeH/JSrk/yMz6P8dO+z/H0Tv/zBU8f8yVfH/Ikbv/yNH7/8jR+//I0fw/yRJ + 9f8kSvX/IUPp1B9B627S3P+LkpGJ/wAAAP8AAAD/AAAA/wAAAP8rKyuHx8fIxcXExWoAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADNz/vH29v9/9bX/P/Cxfn/mJvy/zAt4f8jHN//Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g8iYe3zAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJh/gFyYf4GEmH+C4Jh/g9yMe3/8qJeD/KSTg/ygj4P8pJOD/KCPg/yol4f8tKOH/LCfg/y0n + 4f8wKuH/Mizi/zs44/9aWuj/jJPw/5CX8P9vdOv/l53y/09O5v8eF9//Jh/g/yYe4P8mHN//Jhzf/yYf + 4P8lJ+P/IjHn/yE97P8jSO//I0nw/yNI8P8jR+//I0fv/yNH7/8jR/H/Fj3w/6e6//9qZ17/AAAA/wAA + AP8AAAD/AAAA7TY0LRXOzs2rxMTElgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKKn + 9MuGh+//U1Ln/y0n4f8aE97/JBzg/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P4mH+BVAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnI+EnhY/veW92 + 68tocer/dHzs/3Z/7f97g+3/hY3v/3+I7v98iO7/g5Hv/4OQ7/+Hju//i5Xw/5Wg8v+PmPH/e4Hu/11e + 6P8oIuD/Hxff/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYd3/8mHN//Jh/g/yUo4/8lNej/I0Ht/yNJ + 8P8jSfD/I0fw/yNG7/8YPu//rsD//1pYUP8AAAD/AAAA/wAAAP8DCB6DAAAAAMzMy4LExMTAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHRffyBUO3f8jHt//Ozjj/0E/5f8mIOD/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/gdwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQz/kAZ+i8ziAge6NVVXn2lxc6f9cW+j/Wlno/1JU + 5/9TV+f/UFXn/0xP5v9LSOX/QD7k/zMt4v8iG9//Hhbf/yUd4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh3f/yYc3/8mIOD/JSrk/yQ56v8jRe7/I0nw/xc/8P+yxf//oZ+W/wAA + AP8AAAD/CAwf/yFD39QfRf1rhZPVbtDNv9kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABaWuimoKL0/7q8+P/P0Pv/u7/4/y4p4f8jHN//Jh7g/yYf4P8mH+D/Jh/g/yYf4JIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAVDd0JHxjfSB8X350fF9/mIBjf/yAX3/8gFt//IBff/x8X3/8hGd//JBzf/yYe + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mHN//Jh3f/yYj4f8lMOb/Fzbr/6i5/v/v7ub/EBAP/wAAAP8cNKL/JUv//x9D8P8mSu30o63Y8Ieb + 5S4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKCv9Fne3f3/19f8/9zc/f/S1fv/Ojjj/yEZ + 3/8mHuD/Jh/g/yYf4P8mH+CoJh/gAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYf + 4BQmHuBYJh7gryYf4PAmH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh3g/yYc3/8ZE97/oKXz//// + //9OTUT/AxJE/yVM+/8jR/D/I0fv/xI47/+Ln/n/m7D67XON9owgRO8gAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAZnDqDLS3997GyPn/nZ7z/2Jh6f8oIuD/JR3g/yYf4P8mH+D/Jh/gsiYf4AcAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh7gHyYf4GgmH+C6Jh7f9iYe + 3/8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/xkS3v+jpvL//////5ubpf8WIsL/JEP1/yNJ8P8jSfD/FTvv/4yf + 9/+Zrvn/dI72/x5C7+0jR/CHI0fwGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMi7hcDMw4v8fGN//HBTe/yUe + 4P8mH+D/Jh7g/yYe4KwmHuAJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmHuAlJh/gciYe4MImH+D4Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mH+D/GhLe/3Z3 + 7P//////v8P6/xwU4f8lIN//JS7l/yQ/7P8ROO7/prj5/5Sr+P9qh/X/HEDv/yNH7/8jR/DlI0fvcCNH + 7wgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAjG98FIhrfgyYe4OEmH+D6Jh/g+yYf4OomH+CGJh/gAwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACYe4CkmH+BrJh7fryYe4OgmH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8jHN//LCfh/7q99f9oaOr/HRXe/yYe3/8lHN//Fg7d/2Jt + 7P/v9v//eZL2/2uI9v8aP+//I0bv/yNH7/8jR+//I0fwyiNH7z8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh/gECYf + 4CkmH+AqJh/gGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh/gFiYf + 4FImHuCfJh/g4iYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8jHOD/IBnf/yIa3/8lHd//HRTe/xYP3v9mZ+n/9fX9/46T8P+irPT/XHHw/xs/7v8jSfH/I0fw/yNH + 8P8jR+//I0bv+yNH74MjR+8FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmHuAQJh/gSiYf4JgmH+DdJh/g/yYf + 4P8kG+D/Hhbf/x8X3/8eF9//Hhff/x8X3/8fF9//Hhbf/x0U3/8aEt7/HBTe/ykk4P9VVef/pKjy/9LX + +f+PlfD/o6n0/56i8v8mIN//JCPh/yQ36f8jRu//I0rw/yNH8P8jR+//I0fv/yNH778jR+8rAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACYf4AsmH+BEJBzgkzEt4tpQS+b/T0rm/1VQ5/9WUef/VE/n/1NP + 5v9VVef/YmXp/3d57P+ZofH/wsv3/9vl+//Bzfj/oKjz/6Gn8/9/g+7/KCTg/yIa3/8mHd//Jhzf/yYk + 4v8lNuj/I0Xv/yNJ8P8jR+//I0fv/yNH7+4jR/BlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAb3nrCtff+kGbqPKOtbr22KKo8/+fpvL/n6by/56m8v+ep/L/kpvx/4mU7/9/he7/am3r/2Bi + 6f9NS+b/KiXg/xwU3v8kHOD/Jh/g/yYf4P8mHuD/Jh3f/yYc3/8mJOL/JTXo/yNF7v8jSvD/I0jw/yNH + 7/8jR++oI0bvGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSUOcHGBDePCEa + 34khGt/VIRrf/yEa3/8iG9//IBnf/x4W3/8dFN//HRXf/yAX3/8kHeD/Jh7g/yYe4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYd4P8mHN//JiPh/yU16P8jRe7/I0nw/yNI8P8jR+/gI0fvTQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUe4AUlHuA7JR7ghyYe4NEmHuD/Jh7f/yYe + 3/8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHd//Jhzf/yYk + 4v8kN+n/I0bv/yNJ8P8jR+/+I0bvagAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAJh7gAyYe4DYmH+CEJh/gzSYf4P4mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh3f/yYc3/8mJeL/JDvq/yNI8P8jR/DIAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAmH+ABJh/gMiYf4H0mHt/IJh/g/CYe3/8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYd3/8mHN//JTLn/yNI8IgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmHt8tJh/gdCYf + 4L8mH+DzJh/g/yYf4P8mHuD/Jh7g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYd3/8lJ+ObI0nwDAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYe4CEmHuBWJh7gkiYf4MYmHuDsJh/g/yYf + 4P8mH+D/Jh/g5CYf4LwmH+COJhzfRwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAmHuAPJh/gNSUe32cmH+CVJh/gnyYf4JYmH+BaJh/gLCYe4AwAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////+Af///////////////////AD////// + /////////////AAf//////////////////gAD//////////////////wAAf/////////////////4AAH + /////////////////8AAB/////////////////8AAAf////////////////wAAAD//////////////// + gAAAA////////////////AAAAAH///////////////AAAAAA///////////////AAAAAAH////////// + ////wAAAAAB//////////////8AAAAAAf//////////////gAAAAAH//////////////8AAAAAB///// + //////////AAAAAAf//////////////4AAAAAH//////////////+AAAAAB//4B///////////wAAAAA + //4AH//////////8AAAAAP/8AA///////////gAAAAD/+AAH//////////4AAAAB//AAA////////+D/ + AAAAAf/gAAP///////8AAIAAAAP/wAAA///////8AAAAAAAH/8AAAH///////AAAAAAAB/+AAAA///// + //wAAAAAAA//gAAAJ///////AAAAAAAf+AAAACf/////58AAAAAAP8AAAAAH/////4H4AAAAAAfAAAAA + B/////8A/gAAAAAA4AAAAAf////+AH+AAAAAADgAAAAH/////AB/4AAAAAAAAAAAB/////gAP/wAAAAA + AAAAAAf////4AD/wAAAAAAAAAAAH////8AA/AAAAAAAAAAAAB////+AAIAAAAAAAAAAAAAf////gAAAA + AAAAAAAAAAAH////4AAAAAAAAAAAAAAAD////8AAAAAAAAAAAAAAAA/////AAAAAAAAAAAAAAAAP//// + wAAAAAAAAAAAAAAAH////8AAAAAAAAAAAAAAAB/////AAAAAAAAAAAAAAAAf////wAAAAAAAAAAAAAAA + H////4AAAAAAAAAAAAAAAA////8AAAAAAAAAAAAAAAAP///+AAAAAAAAAAAAAAAAD///+AAAAAAAAAAA + AAAAAAf///AAAAAAAAAAAAAAAAAH///gAAAAAAAAAAAAAAAAB///wAAAAAAAAAAAAAAAAAf/+AAAAAAA + AAAAAAAAAAAH/4AAAAAAAAAAAAAAAAAAB/+AAAAAAAAAAAAAAAAAAAf/gAAAAAAAAAAAAAAAAAAD/8AA + AAAAAAAAAAAAAAAAAP/8AAAAAAAAAAAAAAAAAAB//4AAAAAAAAAAAAAAAAAAf/8AAAAAAAAAAAAAAAAA + AH/+AAAAAAAAAAAAAAAAABD//gAAAAAAAAAAAAAAAAAf//4AAAAAAAAAAAAAAAAAP//+AAAAAAAAAAAA + AAAAAD///gAAAAAAAAAAAAAAAAB///4AAAAAAAAAAAAAAAAA///+AAAAAAAAAAAAAAAAAf///gAAAAAA + AAAAAAAAAAH///4AAAAAA/wAAAAAAAAB///+AAHwAH//gAAAAAAAAf///gAB/g///+AAAAAAAAH///4A + A//////8AAAAAAAB///+AAf//////4AAAAAACf///gAP///////gAAAAAAH///4AH////////AAAAAAA + ///+AB////////+AAAAAAD///gA/////////8AAAAAAP//8Af/////////4AAAAAA///AP////////// + wAAAAAH//8P///////////gAAAAAf///////////////AAAAAD///////////////+AAAAAf//////// + ///////8AAAAB////////////////4AAAAP////////////////wAAAB/////////////////gAAAf// + ///////////////AAAH//////////////////AAB//////////////////+AB/////////////////// + 4A////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////8oAAAAYAAAAMAA + AAABACAAAAAAAICUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcG + KAgGBSYWBgUkFwUEHxIEBBsEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAANDE8BBQUiVgAAAr8AAADrAAAA7gAAAOIAAAWyBgYYUhISKwQAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwbXxcDAwqwBwck/g8OTv8REFf/DQxI/ggI + Kf8CAgf/AAAA/gICArQiIiwSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBtGQwM + JcUaF5L/Ix3g/iQd6P8oIuP/NTHX/j08zv8xL7L/EBFG/gAAAv4LCw+ZAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABxcowJEREwvx8awv4qJdv+UE7G/ouLyf7Bwdb+5+fn/u/v7f7m5uf+srTM/jk6 + Q/4BAQH5IShNIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIuOoRAwMDSoaGqs/qmqy//i4uH//f37/v7+ + /v/19vr/3eL0/sPM8P+0wvn/rbr5/rC+7/9cYGz+DBAjdwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAur7QAq2xxRacn6xMoaKlm8bG + x9zp6en9+vr6/u/y+//J0/j/k6b0/mB77/88XOz/KUvt/iNG7v8iRu//IUXv/iJG7/8uUOn+KjqEmwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ6i + tBSSlJ5ar7G4qs/Pz+Xw8PD+/v7+/vDx8v67w+X+cIfo/jpa7f4iRu7+IUTv/iJG7/4iRu/+Ikbv/iJG + 7/4iRu/+Ikbv/iJG7/4iRu/+JEjv7CY/1DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAqa/MAZqftx+Ym6hhqamquNjY2PX6+vr/+vr7/trf8f+ir+f/XnXf/itM5v8gRO//Ikbv/iNG + 7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNF7v8kMuf+Izvq/yNG7dIkOdsUAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAJqiyAKSmLNHq6yxs9fX1+z19fX+8fT7/svT9f+Lnuz/S2fp/ipM + 6/8iRe7/Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//I0bv/iNG7/8jRu//Ikfv/iQ2 + 6P8lHt/+JR/g/yQ46f4jQuycJDXlAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIaMqyza2tv39/j8/r3J + 9/53jvH+PV3u/iNH7v4hRe/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG + 7/4iRu/+Ikbv/iJG7/4iRu7+I0bv/iUm4v4lHt/+JR7f/iUf4P4jPev8Iz3rSwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAIKOxweanamoP2Dy/h9D7/8iRu//Ikbv/iNG7/8jR+//Ikbv/iNG7/8jRu//Ikbv/iNG + 7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jR+//I0Pt/iUg4P8lHt/+JR7f/yYe + 3/8lK+T+I0LthQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABrf9gIJUjvzyNG7/8jRu//Ikbv/iNG + 7/8jR+//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG + 7/8jRu//Iz/s/iUf4P8lHt/+Jh7g/yYe3/8lKuT+I0HtcgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAMlPrWSJG7/wiRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG + 7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+JDrq/iUe3/4lHt/+JR7f/iUe3/4kMeb+IzfpRQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ0nrCCNG78cjRu//Ikbv/iNG7/8jRu//Ikbv/iNG + 7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//I0bv/iNG7/8jRu//JC7l/iUe + 3/8lHt/+JR7f/yYe3/8kNOf4JDHmFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAGBiQIBQUeFQQEGxIFBBwFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG + 708jRu/7Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG + 7/8jRu//I0bv/iNG7/8jRO7/JSHg/iUe3/8lHt/+JSPh/yUf4P8lK+TGAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAODFQCCQg4PQIBDZ4AAALXAAAA6QAAAOgAAALRAQELgQUE + IRkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACJF7gciRu+8Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG + 7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+I0bv/iNG7/4kN+n+JR/g/iUe3/4lHt/+JTHm/iUl + 4v4lH+CNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8MXQkEBByOAgIH+AoL + NP4REVj+ExNe/g0NQv4DBA7+AAAA/gAAAdgFBR88AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjQu0zI0bu+SNF + 7v8jRu//Ikbv/iNG7/8jRu//I0bv/iNG7/8jR+//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNF + 7/8lJuL/JSzl/iYe3/8lIuH+JTTo/yUk4fIlIuAxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAEA1jEgQEE7YVE3D+Ix3W/yUd6/8lHen+JRzp/yUd6v8kHt7+FBNu/wECCP8AAAD2BQciUAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAyO+YBMz3nCzI85wYAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAI0DshiUo4/wjPOv/Ikbv/iNG7/8jRu//I0bv/iNG7/8jRu//Ikbv/iNG + 7/8jR+//Ikbv/iNG7/8jRu//Ikbv/iM56f8lKeT/JSzl/iYg4P8lMOb+JS3l/yUf4JglJOEBAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVE4EGBwgksB4atf4lHej+JR3n/yUd5/8lHef+JB3j/yQf + 1v8jIMX+Ih60/xkZbv8NEUT+CA4utRcklwQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJzLmGSY6 + 6WomMOavIyvk1SYv5csmLuWwJzDlkCY153ElMOZMJDLnLCM36Q4kMeYBKDXnBicl4ZYlH+D/JDTo/h83 + zP8iQuX/I0fx/iNG7/8jRu//Ikbv/iNG7/8jR+//Ikbv/iNG7/8jRu//I0Lt/iUu5f8lNOj/JSPh/iUi + 4f8lLeX+Jh/g4SUj4RsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABYUgAELCjZ/IRzG/yQd + 6P8kHOj+JB7i/yQgyv8kIcL+JCDE/yQfzP8lHtj+Jh7g/yUe4f8lJOT+I0Xr8SFC5ZoeNssXAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACQt5BIkNuiNJCjj9iUe3/48POT+ZGzs/mdu7f5gZ+z+JyDf/iUd3/4lHt/+JR/f/iUk + 4folKuTiJDLmwiQ06KIkKeTtHiK9/hwgp/4WF4P+GSKc/h83y/4iRev+I0fw/iNH8P4iR+/+Ikbv/iNH + 8P4jQOz+JSfi/iU06P4lKuT+JR7f/iUe3/4lHt/6JSDgSgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAABEQYjseGrL2JB3e/iEevf4jIb3+JCHI/iUf3f4lHuH+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JDnp/iJG7/4iRu/eKkrmIwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB0luRklH9u6JR7f+CUe3/8nIOD+JyDg/yUd + 3/8mH+D+Jh7f/yYe3/8mHuD/JR7f/iYe4P8lHt//JR7f/icg4P9cY+v/aHHv/lBY6f85QeD/KDDP/h8n + u/8cKLP/HCuy/hsnqf8cLbX/HTPD/h0ouP8fHLv/JCHU/iUm4v8mHuD/JR7f/iYe3/wlIN+BAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBejBhMTWr8eHZ7+Hhyf/yQezP8lHt/+Jh7g/yYe + 3/8lHt/+Jh7f/yYe3/8lHt/+Jh7f/yYe3/8lHt/+JC7l/yNH7/8iRu/+I0fvwDhW5AsAAAAAZXnUAQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAZGp8EIh7IRCUe3q8lHt/5Jh7f/yYe4P8lHt/+JR7f/yYe4P8mHt//JR7f/iYe4P8mHuD/JR7f/iol + 4P+JlfT/laH3/pWh9/+UoPf/i5b0/nqD8f9ja+7/R1Hp/i033f8fKsv/HCi3/hkkoP8WGYf/FRN9/hoW + m/8iHMf/JR7f/iUg35skJd4GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJB7bASQd2RwkHdhYIx3NqCId + w/smHt/+JR7g/yUe3/8lHt/+Jh7f/yYe4P8lHt/+Jh7f/yUe3/8lHt/+Jh/g/yUe3/8lHt/+JSnj/yNG + 7/8iRu/+I0fv/SpL5VmJmN8rk53HOwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAgIMwkFBiItCQo7Jw4OWQMAAAAAAAAAAAAAAAAlINImJR7eliUe3+klHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4sKOH+SUrn/l9l6/5weO/+gIry/oyY9f6UoPf+lqL3/pSh + 9/6HkvP+bnXu/lhg7f5CUOz+KDng/h8vy/4cKrD+Hie5wyQt4B8jJ98BAAAAAAAAAAAAAAAAAAAAACQn + 3gslHt+SJR7f1iUe3/slHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+JCrk/iJG7/4iRu/+Ikbv/lJu8biwsrzOOkFhPQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQk5OwgIKsoEBRf1AAAA8gEBC64OC1QcAAAAAAAA + AAAAAAAAIxzSASUe3h4lHt95JR7f3CUe3/wlHt//JR7f/iUe3/8lHuD/JR7f/iUe3/8lHt//JR7f/iUd + 3/8lHt//JyDg/i0o4f89POT/W2Dr/nmD8f+Ll/X/kp32/pOf9v+Un/f/h5L0/kdI5v8lIeH/JSnk+iQz + 5+kkOum5Iz3rdiQ36TskMOYRAAAAACQr4QUlId+FJB7a9iQe2P8mHt/+Jh7g/yYe3/8lHt/+JR7f/yUe + 3/8lHt/+Jh7f/yUe3/8lHt/+Jh7g/yUe3/8lHt/+JR7f/yUe3/8lHt/+JDLn/yNG7/8iRu/+MFPw/663 + 2/IeHx/4LTNMLgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANDEg2DQ5K7RUT + fP8VFHv+Bwgg/wAAAP8BAQjODApODQAAAAAAAAAAAAAAAAAAAAAAAAAAJR7fECUf4F8lHt/JJR7f/iYe + 3/8mHuD/JR7f/iYe4P8mHt//JR7f/iYe4P8mHt//JR7f/iUe4P8mIOD/UVTo/l1h6/9sde7/d4Lw/m96 + 7/9SWOn/REPm/jc04/8lHt//JR7f/iYe3/8lHt/+JSHg/yUo4/wkMufgJDboaSQv4wMAAAAAICTFPRwY + o7obF6D+Hxq6/yEbxP8iHMn+Ix3Q/yQd1v8lHtv+Jh/h/yYe4P8lHt/+Jh7g/yYe4P8lHt/+JR7f/yUe + 3/8lIuH+I0Tu/yNG7/8iRu/+ssH3/lhYWfMAAAD+Jys9IgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAABYVah0PD1PdFROA/h8Zv/4kHen+JB7i/hMSZP4AAAL+AgIPigAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAkKOMBJSXiQCUg4KslHt/yJR7f/iYe3/4lHd/+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4kHd/+S03n/oKM8v6UoPf+lJ/2/pSg9/6Voff+V1zq/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/CM66ssiRu+3IkfwpCJA5pkcLLTsGyms/hkmpv4ZJJ/+GCCY/hgckP4XGIj+FhSE/h8a + uf4mHuD+Jh7h/iYe4f4lHt/+JR7f/iUh4P4jP+z+Ikbv/iJG7/6Oo/f+tbW2/gUFBf4CAgL6LjA5EwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBxzBhcYUK4mJ5P+IRvP/yUd5/8kHef+JR3n/yQd + 5P8PDk/+AAAA1A4RWgsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIzPnAiQy5xIkLeVJJSnj0jk4 + 3f+lpcn/UVPY/iUe4P8mHuD/JR7f/iQd3/9wcdP/m53K/jAt4P9udu//laD3/pSg9/+UoPf/kp32/k9R + 6P8nIeD/JR7f/iUe3/8lHt/+JR7f/yUe3/8lHt/+JSXi/yNB7P8iRu/+I0bv/yNG7/8jR/D+I0fw/yNH + 8P8jR+/+Ikbv/yNG7v8iROr+HCy0/xYUgf8ZFZH+GhaX/xwXpP8gGr3+JSLf/yQ97P8jRu/+I0bv/z1d + 8f/x9P3+Q0NE/wAAAP8gICHxTk9SBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADxA+YUlM + p/siHM7+JR3o/yUd6P8kHef+JR3n/yUd5/8gG77+AAAA5hMbfRMAAAAAAAAAAAAAAAAAAAAAI0PtFSND + 7UgjQ+6BI0XutCNG79wjR+/3Ikbv/ixL6/+3usf/ZmbU/iUd4P8mHuD/JR7f/iQe3/9lZdT/wsPE/jw4 + 3P8oIuD/WFzq/mVr7P9iaOz/eoPx/pKe9v+Ai/L/c3rv/kVG5v8lHt/+Jh7f/yYe4P8lKuP+I0Pu/yNG + 7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNF7f8dMLz+FxuN/xkinv8cLbb+HC64/xoo + qv8XGon+GBqQ/xsoq/8fOM/+Ikbu/3KL9f/U1NT+AgIC/wAAAP9YWFjuMzM0AgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAbGmILMzZu5DQxwf4kHOL+GBWQ/hoWnP4kHeX+JB3n/iQd5/4lHuf+BAUT5hgp + oxgcNb8lIEHfXSJG7pgiRu/SIkbv+yJG7/4iRu/+I0bv/iJG7/4iRu/+Ikbv/iRH7v6fqM3+sLXJ/ixB + 5/4lKeP+JR7f/iUe3/47ONv+yMjD/lta1v4lHt/+JR7f/iUe3/4lHt/+Ixze/kRF5v6Rnfb+lKD2/l5j + 6/4lHd/+JR/f/iQz5/4jRu/+I0bv/iNG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+IUDi/hke + lv4cIKz+IkPn/iNH8P4iRu/+Ikbv/iNH7/4jR/D+IT7c/hwstP4XHJD+ExaG/qSt4P6AgYH+AAAA/gAA + AP6Ojo7kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPEEBaP0Gd/yEbyf8cGK7+GRl9/xYU + hP8kHeL+JB3n/yQd5/8lHuv+FyZ+8iJE59EiRu/xI0bv/iNG7/8iRu/+I0bv/yNG7/8jR+//I0bv/iNG + 7/8jRu//Ikbv/iJG7/+FlNT/sLfJ/mR73f8iRe//Iz7s/iUt5f8lIeD/t7nG/oCBz/8kHOD/JR7f/iUe + 3/8mHt//JR7f/iUe3/9FReb/Rkfm/ici4P8lJuL+Iz3r/yNG7/8iRu/+I0bv/yNG7/8iRu7+I0bv/yNG + 7/8iRu/+I0bv/yNH7/8iQub+GBuR/yEbxf8mH+D+JDnp/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNH + 8P8iRu3+GzPJ/5CVwf8tLjr+AAAA/wUFBf/CwsPRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABoa + XQIKCzutNDWY/yAazP8XFIv+FRR6/xsXpP8kHej+JB3n/yQk6P8jPe3+I0fx/yNG7/8iRu/+I0bv/yNG + 7/8iRu/+I0bv/yNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iJG7/9mfN3/jp3S/qevy/8nS+7/Ikbv/iJH + 7/8gP+3/k53P/qKlyv8kJ+P/JSTh/iUi4f8lI+H/JSTh/iUp4/8kL+b/JDfp/iM/7P8jRu/+I0bv/yNG + 7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yJG7v8ZIpz+IBu//yYe4P8lHt/+JSbi/yNG + 7/8iRu/+I0bv/yNG7/8iRu/+I0fv/yNB7f8kOOn+JDTr/xgeWP8AAAD+AAAA/xYWF//p6eqoAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAABQVUxALC0LgJSeK/h8ZxP4dGLL+GRWW/iMc3f4kIef+IzXr/iNG + 7/4jRu/+I0bv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbu/iJG7/4iRe/+I0bv/iJG7/4pTe/+JUjv/ihL + 7/5cd+f+mKPP/oua0/5iet7+IkXv/muG9P46WvH+aH7c/rq8xv4vUuz+Ikfv/iJH7/4iR+/+Ikfv/iJH + 7/4iRu/+Ikbv/iJG7/4iRu7+Ikbv/iJG7/4iRu/+I0bv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/h41 + yP4bF5/+Jh7h/iUe3/4lHt/+JDPn/iJG7/4iRu/+Ikbv/iJG7/4jRe7+JCzk/iUd3/4lHt/+Jh7h/hMS + aP4AAAD+AAAA/jk5Ofufn6ZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsNNygNDUv3FhV7/xwX + rP8kHub+JCXZ/yQ11f8jQ+v+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/y5R + 8P9Sc/P/JEjv/iFF7/9wi/X/JUjv/per+P/k6fn/srfJ/khl5f+rssr/I0fv/nKO9f8rTvD/RWPl/sDC + xP9IZeX/KEvw/iNH7/8iRu//IUXv/iJG7/8jRu//Ikbv/iNG7/8mSe/+Ikbv/yNG7/8iRu/+Ikbv/yNH + 7/8iRu/+Ikbv/y5R8P8mSu/+I0bu/xkfmf8jHdH+Jh7f/yUe3/8lJ+P+I0bv/yNG7/8iRu/+I0bv/yNF + 7/8kLOT+JR7f/yUe3/8lHuD+JR7g/yActv8BAQT+AAAA/39/gcNTUm8IAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAsRQTsMDEP+FBJ6/x8psP8jPt7+I0br/yNG8P8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG + 7/8iRu/+I0fv/yNG7/8iRu/+I0bv/yJG7/8tUPD/T3Dz/iJF7/9lg/T/Ikbv/mmE9P82WPH/srfJ/jxb + 6P+UoND/sr3l/qS4+f8oTPD/K07s/re7x/9pftz/Wnn0/jda8f9Ia/P/c4/1/lx58/8hRe//Jkrv/jdY + 8P90j/X+JEjv/y5T8P8hRe/+Z4P0/4ad9/8kSO/+NVnx/0hn8v8xVPD+IT7c/xsZof8mH+H+Jh7g/yUf + 4P8jPOv+I0bv/yNG7/8iRu/+I0bv/yQu5f8lHt/+JR7f/yUe3f8bF6r+Kiig/yYmi/8AAAX+AQEG/0NB + pZkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0YV0kQGVj+Gyuy/iJG7/4iRu7+Ikbu/iJG + 7/4iRu/+Ikbv/iJG7/4iRu/+I0bv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iR+/+Nlnx/l18 + 9P5Zd/P+JEjv/mB99P4dQu/+kp/Q/lt03/5EYub+p6/L/mWD8/48XfH+IUXv/pmkz/6PnNH+Smrz/kxt + 8v4iRu/+HkLv/kdn8v5rhvT+ZoP0/rDA+f5BYvH+JUnv/khr8v4hRe/+N1vx/jpc8f47XPH+Y4H0/myI + 9f4pTfD+HTG//iEbw/4lHt/+JR7f/iUl4v4jRu7+Ikbv/iJG7/4jQ+3+JSnj/iUe3/4lHt/+Jh7g/hoW + nv5gYqL+6enp/ufn6P6Qk6z+Hx+H/iYf4O5GRrAhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHTnGHSBB + 264iRer+I0bv/yNG7/8iRu7+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG + 7/8jRO7+Izzr/yQ06P8kLeX/JSrj/qWu8/9gaOv/IiXi/p2n8/+NnvT/l6La/oKN0/8iOOv/v8PP/mZ8 + 4f9KaPH/IkTu/nKF2f+zuMj/NFfx/khn8v8iRu//Ikbv/iBE7/9FZvL/epf2/kVl8v9gffT+Ikbv/4Kc + 9v8wUvD+T3Dz/5Op+P8xVPD+PF/x/32V9v8hRe/+HCmv/yQd1f8lHt/+JR7f/yQv5v8iR+/+I0bv/yQ6 + 6v8lI+H+JR7f/yYe3/8lHt/+IhzM/zY3j//f3+T+6enp/+np6f/q6ur+r7HQ/ys41v49RNJhAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAhQ+M/Ikbv3yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu7+I0bv/yNG + 7/8iRu/+I0bv/yJG7/8iRfD+Izrr/yUr5f8lIeH+Jh7f/yYe3/8lHt//JB3f/jo+4/+QlO//Lijh/l9e + 6P+5vPX/xMnm/qOkyf8jHN//eXvd/qyvy/9YXuf/JSLh/kxO2v/FxsP/PUXi/kRL6P9baO3/f5Py/pOm + 9v+UofP/UFrq/mdx7P8zQej+IzHn/2Jw7f+7w/f+Ljvn/z1G6P8kL+b+JjXn/yQ36f8jQO7+GyOn/yUe + 3P8lHt/+JR3f/yQ16P8jRu/+JDDm/yUf4P8lHt/+Jh7f/yYe4P8lHuD+Gxeq/6Wnx//q6ur+6unp/+rq + 6v/p6en+6+rq/42Z3f8oSOqhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIkXuAiJG72kiRu/2Ikbv/iJG7/4iRu/+Ikbv/iJG + 7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+KErk/lBlwv5scbX+V1a3/iYf3v4lHt/+JR7f/iUe + 3/4lHd/+MC/h/jIy4v4uKeD+Q0Lk/iUe3/5oZ+n+iInm/rW2xv4xLN7+JR/f/p+hyv51edr+JR3f/jEs + 3f67vMX+XFzY/jk24/4xK+H+goTt/mlr6f4iGt/+JR7f/iUe3/4lHt/+JR7f/iUd3/4hGt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4mHuH+Gxmg/iUe3v4lHt/+JR7f/iUl4v4lJOH+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4mHuH+GhiD/l9gZf7Ly8v+sLCw/re3t/7Y2Nj+6urq/tfb6v4rTu/tXG/eCQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApRecCIkbufSNG + 7/gjRu//Ikbv/iNG7/8jRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yND7f84S87+kZWw/7O0 + wf+1tsX+f4Cw/yYf3v8lHt/+Jh7g/yYe4P8lHuD/Oznj/lZY5/9cYOj/Kybg/iUe3/8lHd//KiTe/rKz + x/9KSNn/JR7f/lZU1/+nqcn/JR7f/icg3/+eoMr/f4HR/jQz4v8mHuD/JB3f/lJS5v9LSuX/JR7f/iYe + 4P8lHt/+Jh7f/yYe3/8lHt/+JR7f/yUe3/8lHt/+Jh7f/yYe3/8mHuL+Gxih/yUe3v8lHt/+JR7f/yUe + 3/8lIOD+JiTh/yUe4P8lHt/+Jh7f/yYe3/8mHuH+EA9X/wEBAf8MDAz+AgIC/wUFBf8TExP+WVlZ/93f + 4/8/Xu7+VGvdLwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAsSutkI0bv/CNG7/8jRu//Ikbv/iNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG + 7/8jRO7+IzLo/0RGvv+io7X+trjH/6Okr/+foKn+SUe5/yUe4P8lHt/+Jh7g/yYe4P8lHt//JR3f/jEs + 4f8vKuH/JR7f/iYe3/8mHt//JB3g/p6fy/9qadT/JR7f/ikj3/+oqcn/UE7Y/iQd4P98fND/naDL/l5j + 6P8kHN//JR7f/iYf4P9gZej/JR3f/iYe4P8lHt/+Jh7f/yUe3/8lHt/+Jh7g/yYe3/8lHt/+JR7f/yUe + 3/8mHuH+Gxed/yUe3f8lHt/+JR7f/yUl4v9Xa/D+R1Pr/yUe3/8lHt/+JR3f/yYe4P8mH+H+Dw9X/wAA + AP8AAAD+BwcH/1lZWv9FRUX+Ly8v/+Tk5f9OaOz+T2bUSwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAFNR0QJUUtERW1vVKkhJz0ZGR9BbPT7OVDY91EEiQeniIUTt/iJG7/4iR/D+Ikbv/iJG + 7/4iRu/+Ikbu/iJG7/4iR+/+I0Lt/iQy5v4kIOD+S0m5/qqruf62t8b+tbbG/ra3xv6ytL3+NC/I/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JBzf/n5+0P6Qksz+Ixzf/iQe + 3/5bW9b+oaLJ/igh3/5SUNf+uLnF/pme7f54eOz+WVjn/oeL7f5PTuX+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4mHuH+Gxec/iUe2/4lHt/+JR7f/jk75f6UoPb+SlHp/iUe + 3/4kHN/+TFLn/iUe3/4mHuH+EhBn/gAAAP4NDQ3+x8fH/uvs7P7r6+v+hISE/pWVlv5GYd3+RlzNTgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQj/KIykkwcEjHb/lX2Tc9H2I6/t7g+r+aHDh/kdI + 0Ph2gen+aHTm/1lo5P9LXeT/OlHk/ixH5v8kROr+I0Tu/yM86/8kLeX+JSDg/yUe3/9HRbz+qqu6/7a3 + xv+2t8b+trfG/7a3xv+3uMf+UVC1/yUd4f8lHuD+Jh7f/yUe4P8lHuD/JR7g/iUe3/8mHt//JR7f/iUe + 3/8lHt//JR7f/l1c1v+0tsb/Ix3g/iYe3/8mId//rrHH/k5N2P8wLN3/w8TD/kxK2/90dev/io3u/klG + 5f8lHt//JR7f/iUe3/8lHt/+Jh7f/yUe3/8lHt/+JR7f/yUe3/8lHt/+JR7f/yUe3/8lHuD+Gxeg/yQd + 1f8lHt/+JR7f/yUg4P9zgfL+YWjs/yUe3/8jG9/+c33v/y8q4f8lHuD+GBaL/wEBA/94eHj+6+vr/+np + 6f/p6en+oaGh/yQkJf9CXN3+P1PLRQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARkLLGTUx + yryAgN/4m53p/qWo7P6zsvD+Pjvd/ich2f4xLdv+NzTa/j8/2f5TVt7+b3fl/n2H6v5sd+P+IyHS/iUe + 3/4lHt/+JR7g/jo4xv6lprb+trfG/rW2xv62t8b+trbG/ra3xv63uMj+mZyz/lJFlP5QJWP+VyZR/lIl + Xf5IJH3+OSKn/iwgzv4kHuL+JR7g/iUe3/4lHt/+JR7f/kM/2v7FxsP+My/d/iUe3/4lHt/+Z2bU/pqb + y/4jHeD+rK7I/mVl1P4kHd/+JBzf/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+HRms/iIbx/4lHt/+JR7f/iUe3/5UY+7+ZnPv/igi4P4kHN/+d4Dw/k5Q + 6P4lHt/+IRzC/hQVKP7Pz9D+6ejp/unp6f7s7Oz+RERE/kJDSv4yT97+LzLcnTtC2BUAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUlegUrKzwTbGz9Iu3u/fGSknl7yUd3/4lHt/+Jh7f/yYe + 4P8mHuD/JB3e/iMc2f8oItv+JR7e/yYe4P8lHt/+JyDc/4aHrv+2t8f+trfG/7a3xv+2t8b+ra27/6Ce + qP95XV/+aDo3/2UqJv9lKCP+ZSgk/2UoJP9lKCP/Zigg/mYoIv9cJ0D/RSSG/i8gxv8lHuH/JR7f/jAr + 3v+7vMX/VFPX/iUe3/8mHuD/LSfe/q6wx/9LSdn/goPP/o2Ozf8jHOD/JR7f/iUe3/8mHuD/JR7f/iUe + 4P8lHt/+Jh7f/yYe4P8lHt/+JR7f/yUe3/8lHt/+Jh7g/yYe3/8lHt/+IRvB/x4Zsf8mHuD+Jh/f/yUe + 3/8jOOn+IzPn/yYe3/8kHN/+cHrv/3mC8f8lHt/+JR7e/0ZGnv/i4uH+5+fn/8bGxv9gYGD+AwMC/1xf + g/8fPOL+Zm7t/2tz7u8zMuNyODbjAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABeY+YGPEPlPSM76vUkNOf+JSPh/yYe3/8mHuD/JR7f/iYe3/8lHt/+Jh7f/yYe4P8lHt/+ODTI/6yt + vP+1tsb+trfG/7a3xv+lprL+f3d7/2g2M/9lJyP+ZSgj/2UoJP9lKCP+ZSgk/2UoJP9lKCT/ZSgj/mUo + JP9lKCP/ZSgi/mIoLP9SJWD/MyG4/igh3v+kpcn/eXnQ/iQd4P8mHt//JR3g/m5v0/+Xmcz/WlnW/qyt + yP8nId//JR7f/iUe3/8mHuD/JR7f/iUe3/8lHt/+Jh7g/yYe4P8lHt/+JR7f/yUe3/8lHt/+Jh7g/yYe + 3/8lHt/+JB7W/xoXm/8mHuD+JR7f/yUe3/8kNej+Iz/s/yUf4P8kHN/+cHrv/5Gd9v89POT+JR7g/zAs + yP+kp8j+iIiI/wwMDP8BAQH+UFFZ/zhGs/8kOer+Jx/g/zs65P9fZuv7LyrhWQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjjlYiQ56fwlIeH+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+S0m+/rS1w/61tsb+tbbG/qusuf6IiI7+ZzQx/mUnI/5lKCP+ZSgj/mUo + I/5lKCP+ZScj/mUoI/5lKCP+Zycf/mYnIv5mJyH+Zicg/mcnHv5mJx/+Zici/lMlWP6Ui6/+mpvH/iAb + u/4iHMn+JR7Z/jIt3P62uMb+WFnX/ri6xv44NN7+JR7g/iUe4P4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+Jh/g/hsXn/4kHdb+JR7f/iUe3/4kLuX+Ikfv/iQz + 5/4kHN/+b3jv/pSg9/59h/L+Jh/f/oOE7f6Okaj+T1Jy/ggJFv44OVb+Pkmw/iFB5fslK+S5JR7f9SUe + 3/4nIeD6Mi7iPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4OOQFJiXi6TlB + 5/8lHt/+Jh7f/yUe3/8mHuD/JR7f/iYe3/8lHt/+Jh7g/yUe3/8lHt/+Vla7/7W3xP+1t8b+trfH/5SU + nf+Dc3f+ZSci/2UoJP9lKCP+ZSgk/2UoJP9lKCP+ZSgk/2UoJv9HNoX/L0HM/ixF2v9VZdz/ZXDX/mlx + 0P9pbMf/aWe8/k5NsP9PW8H/YXfX/h8wtv8bKq3/GSWn/hghn/9pcLT/goi3/qirvP9MTKL/GhaZ/h0Y + qP8fGrr/IhzL/iUe2f8mHuD+JR7g/yYe4P8lHt/+Jh7f/yUe3/8lHt/+JR7f/yUe3/8lHt/+Jh7f/yEb + xv8eGrP+Jh7g/yUe3/8lIeD+I0Xv/yNG7/8jQOz+X3Xy/5Sg9/+Woff+R0nn/8PF8/9XWFj+Bwoh/yI4 + 2/8gQef+I0bw/yM+6tssNcwOLzPYCyws4CEuK+EbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAA6OOQfJR7f/k1P5/9mbOz+PDvk/ywn4f8oIuD/Jh/f/iUe4P8lHt/+JR3f/yUd + 3/8kHeD+XFy5/7a3xf+1t8b+t7jH/5CRmf97XmD+ZSgj/2UoJP9lKCP+ZSgk/2UoJP9lKCP+ZSgk/2Eq + MP8nQuD/Ikbv/iZJ7/9zh/T/hZX1/oeW9v+Il/b/iJf2/m6D9f8hRe//Ikbw/iJH8P82V/H/dIf1/nyO + 9f94ivP/cYbw/muB7P9Tad7/M0bJ/iU1t/8cJ6T/GB6U/hcZj/8ZF5b+HRms/yEbxv8kHdj+JR7f/yYe + 4P8lHuD+Jh7g/yYe4P8lHt/+Jh7f/yUe3v8cGKL+JB3V/yUe3/8lHt/+JDbo/yNG7/8iRu/+MlPw/4mX + 9v96hvL+REXl/7e4wv8HBwf+Bgoc/yJE5P8iRu/+I0Xu/iUu3XIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLSOY6JR7f/iMb3/4xLeL+Z27t/nmE + 8f5yfu/+cHjv/m537/5veO/+am7u/mRq7P5fZez+c3jC/rS1xP61tsb+trfH/pOUnf57XmD+ZSgj/mUo + I/5lKCP+ZScj/mUoI/5lKCP+ZSgj/mUoJP5RKWH+LSrN/iQr5v4kLOT+Ji/l/icx5v4nM+b+JzTm/iY0 + 5v4kMub+JDLn/iQ26P4nQOv+Ql3w/lpz8/5xhvT+hpX2/pOf9v6Yovf+laD2/o2b9/6Bkvf+b4P1/ltx + 7/5CW+P+KULS/hwwvv4bJ7H+HSGu/h4bsv4fGcD+IxzS/iUe3P4mHuD+Jh7h/iUe3f4fGrT+FxSH/iMc + zv4lHuD+JSHg/iNB7f4iRu/+Ikbv/jBS8P4qS+7+gJX1/lRUVf4AAAD+AwYU/iFC3v4iR+/+JDLlwSQ0 + 3QwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABmZOpRc3Ps/5eY8v+vsvX+MCzh/yMc3/8jHN//JBzf/iYg4P8yMOL+XGHr/3R98P98hfH+gIjZ/6qr + uv+jpLD+t7jH/5mapP+VkZn+ZjIu/2UnI/9lJyP+ZSgk/2UoJP9lJyP+ZSgk/2UoJP9lKCT/ZSgl/lAk + Yv80IbT/JB3e/iUe3/8mHt//JR7f/iYe3/8mHuD/JR7f/iYe3/8mHd//JR7f/iUh4f8jJuP/Ii/m/ig8 + 6f81Tu3/SWLw/lt08/9tgvT/fY71/oqY9v+UoPb+lqH3/4mY9v9xhfX+VnD1/z1b8P9rgOL+QFTF/0ZP + rf8YHZf+FxWH/xUSfP8UEnn+FBN6/xYTgP8hG8T+Jh7g/yUm4v8jQ+3+I0bv/yNG7/8fRO7+qrPS/wwM + DP8AAAD+AwYN/yFB1v4kMt7LJTS9FQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC2tvZt0ND7/9DQ+v/P0fv+Qj7k/yUe3/8lHt//JR7f/iYe + 3/8lHt/+JR3f/yUe3/8mH9/+Pz7j/5KWs/+lprL+trfH/7CxwP+QkZj+lJCY/3hcXf9uRUT+ajw5/2k4 + Nf9oNjP+aTc0/2k5Nv9sPz3/c1BQ/oFqbf+SiZL/j5DF/lRT0/4qJNv4JR7f/iUe3/8lHt//JR7f/iYe + 3/8mHuD/JR7f/iUe3/8lHt//JR7f/iUe3/8lH9//JSHg/iQm4v8kLuX/JDrq/ilF7f8yUu/+Q2Hx/1ly + 8/9xhfT+hpX2/5Cc9v+1vvn+o7H5/6+9+v9FYe/+LUrh/x81xP8YIp7+FRWA/xQSef8VE33+Hxq4/yUe + 3/8lJeL+Iz7r/yNG7/9BYvL+fn5//wAAAP8AAAD+BAUK/ykuuKsjKocVAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACsr/V0paf0/nFx + 7P5FQuT+JyDf/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4oIt/+f4ny/o6X3v6io7H+trfH/ra3 + xv6xssD+k5Sc/q2uvP6ztcT+r7G//q2vvf6srbr+ra68/q+xv/6wssD+qqy5/p2eqOmNjpXCeniTjWhm + lGBAPb44KSLdeyUe39olHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR3f/iUf3/4lI+H+JSzk/iM16P4hP+z+JUjv/jFT8P6Emfb+lKX3/rrD+v6Woff+kJ32/n2O + 9f5bdPT+J0Xj/hwuuf4WGIf+FBJ5/hsXof4lHtr+JR/g/iQn4/6JlfH+KCgo/gAAAP4AAAD+CwsL/nV3 + h2MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABBPON8Ihvf/y8p4f83NOL+JR7g/yYe4P8mHuD/JR7f/iYe3/8lHt/+Jh7f/yUd + 3/tRU+eLhY7vb4yV7YOLktKZkJGew6qruPm1tsX+t7jI/7a3x/+2tsb+sbLB/6iotfeam6TbjIuSsoJ9 + gYZ5cXNTeGxyJHhrfgUAAAAAAAAAAAAAAAAAAAAAAAAAACoj2wUmHt9CJR7foiYe3+gmH+D/JR7f/iYe + 4P8mHuD/JR7f/iYe3/8mHuD/JR7f/iUe3/8mH+D/JR7f/iYe4P8lHt/+Jh7f/yYe4P8lHd/+JSHg/y01 + 5v+gr/b+UWvx/1589P8zVPD+XXbz/4OS9f+VoPb+hpb2/y1P8P8jRu7+HjXJ/xgfl/8WFIH+Hhm08CYg + 3tiqrNT6AAAA/wAAAP8AAAD+HBwc/qurrpAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACHiO+Ira/1/8LD+f/Bw/j+KCLg/yYe + 4P8mHt//JR7f/iYe3/8lHt/+Jh7g/ych4K0+OuAGAAAAAAAAAAAAAAAAl5q4B4qLljSKipJshoeMkoSF + ioyFhYpxiYiOTYaEiSePiZAOjoeNAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJR3fASUe3xklHt9fJR7fvSYe3/IlHt/+JR7f/iYe4P8mHuD/JR7f/iYe3/8mHuD/JR7f/iYe + 4P8lHt/+Jh7g/yYe3/8lHt/+JR/f/3h77P9eX+j+j5Tw/zQ25P8kMOb+Izzq/yZH7v89XPH+Ynrz/zZW + 8P8iRu/+I0bw/yJG7v8gPNj+HzK+jkVRwzivsLTvAAAA/wAAAP8AAAD+Ozs8ura2uK0AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAC0tfaW0ND7/s/P+/6ztfb+Kybg/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f1zAq4RoAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJR7eAiUe3yomH996JR7f1yQd + 3/0jHN/+Ixze/iMc3/4jHN/+Ix3f/iQd3/4kHt/+Jh/g/ism4P5IRuX+fYHt/nh97P6DiO7+VFPn/iUe + 3/4lHt/+JR7f/iUi4f4lLOT+Iznq/iJE7v4iR+/+Ikbv/iJG7/4iRu/+Ikfv/klo8uqKior9AAAA/gAA + AP4AAAD3SUtUOsXFxcV6gJoGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACHie+bZGPp/zUx4v8hGt/+Jh7g/yYe4P8mHuD/JR7f/iYe + 4P8lHt/uJh/fLgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAKyXgAkFA5Ddtc+uRfITt4HqC7f59he3/hY3v/n6H7v+Bj+7+gY7u/4SL + 7/9/h+7+dHrs/15g6f8pI+D+JR7g/yYf4P8lHt/+Jh7g/yYe4P8lHt/+Jh7f/yUg4P8lLOT+Izvr/yNF + 7v8iR+/+I0bv/01s8v+Dg4P+AAAA/wAAAP8PFzmsU16QB8HBwsB5hLcdAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLR+WNWFbo/3t7 + 7f+Hiu/+JR/f/yYe4P8mHt//JR7f/iYe4PIlHt9QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU1LmEUZD + 5E04NOKlMy7h6TIt4v0wLOH+Lyvh/y0o4P8pI+D+JR7f/yUe3/8lHt/+Jh7f/yYe3/8lHt/+JR7g/yYe + 4P8lHt/+Jh7f/yYe3/8lHt/+JR/g/yUk4f8kMuf+I0Ht/01s8v/R0dH+AgIC/wYLH/8hQtv4JEbov4yZ + 0tOBkM87AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACNk/BN09L7/s/P+/7P0Pv+LSng/iUe3/4lHt/+JR7f+iUe32gAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQkDjGjQv4WApIuC4JR7f+SUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR/g/kpP + 6P78/P3+MDAy/hkxnf4jRvD+Ikbu/jtb8P6UqPbSc4zxV1hz7AUAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB0dOwFn6Dz2ZOT8f9aWOf+Jh/g/yYe + 4P8mHuD7JR7fbSUe3wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAMCvhHCgi4G4mHt+/JR7f9SUe3/8lHt/+JR7f/yYe4P8lHt/+JR7f/yUe3/8lHt/+Jh7g/yUe + 3/8lHt/+Jh7f/yYe3/8lHt/+Jh7g/0lH5f/7/P7+gIK3/yMz5f8jQ+3+I0fv/z1d8f+Uqfj+bIf1/yRI + 79ExU/BUJ0rvBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAASUXlVyYf4OwlHt/+Jh7f/iYe3+4oIOBqJR7fAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlHt8FJR7fKiUe33YlHt/BJR7f7yYe + 4P0lHt/+JR7f/yUe3/8lHt/+JR7f/yUe3/8lHt/+Jh7g/yUe3/8lHt/+JR7f/y0n4P/Nz/f+dnfr/yQd + 3/8lIeD+Iy7m/3WL9P+Oo/f+YH30/yJG7/8iRu/7I0bvuCJE7jMiRO4BAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEpF5SMzLOFUMCnhVy8p4SoAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAlHt4CJR7fICUe314lHt+sJR7f8iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/41L+L+KCLf/iUe3/4iG9/+ZGXo/s3Q9/6NmfL+SmXw/iJH7/4iRu/+Ikbv/iNG + 7/AiRe56IkPtAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJR7fDyUe + 31MmHt+jJR7f5SUe3/4iG9/+Ihvf/yIa3/8iGt/+Ihvf/yIb3/8kHd/+My7h/15c6P+gpfH+tbv1/5GY + 8P+PlPD+Jh/f/yUp4/8jPOv+I0bv/yNG7/8iRu/+I0bvuyND7ScAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnIN8BKyTgFTMv4U+Bgu2ghoru45WW8PyOkO/+jZDv/5Ka + 8P+aovH+qbT0/6y09P+Xn/H+gIPu/1JS5/8pI+D+Jh7f/yYe3/8lHt/+JSjj/yM86/8jRu7+I0bv/yNG + 7+UiQ+1eIz/sBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAU1PmEkI/5E04NOKbNDDi6jQw4v40L+L+MCzh/ikj4P4kHt/+JBzf/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lKOL+Izzr/iNG7/4iRu/8IkXunyM96xMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASEXkCDk14kYxK+GWJR7f3iUe + 3/0lHt/+Jh7g/yYe3/8lHt/+Jh7f/yUe3/8lHt/+Jh7g/yUe3/8lHt/+Jh7f/yUo4/8jPOv+I0fv/yNG + 79IjOuoZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAPzvjEC4o4UMnIOCTJR7f3CUe3/klHt/+Jh7f/yYe3/8lHt/+Jh7g/yUe + 3/8lHt/+Jh7f/yYe3/8lH+D+JC3l/yNE7u8jO+omAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJR7eDCUe + 30AlHt+KJR7f2iUe3/0lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR3f9iQ16GsjNugCAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJR7fAiUe3yclHt9lJR7foyUe39slHt/uJh7g4iUf + 4KclIOByJSXiJQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJR7fAyUe3xAlHt8XJR7fEiUf3wMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////g//////////////+AP/////////////8 + AH/////////////4AH/////////////wAD/////////////gAD////////////4AAD////////////gA + AB///////////4AAAA///////////gAAAAf//////////gAAAAf//////////gAAAAf//////////wAA + AAf//////////4AAAAf//////////4AAAAf/w////////8AAAA/+AP///////8AAAA/8AH///////+AA + AA/4AD//////x/AAAA/wAB//////AAAAAB/gAA/////8AAAAAD/gAAf////8AAAAAH/AAAL////+AAAA + AH4AAAD////hwAAAAHgAAAD////A4AAAAAgAAAD///+AfAAAAAIAAAD///8AfwAAAAAAAAD///4AP4AA + AAAAAAD///4APAAAAAAAAAD///wAAAAAAAAAAAH///wAAAAAAAAAAAH///gAAAAAAAAAAAH///gAAAAA + AAAAAAH///gAAAAAAAAAAAH///gAAAAAAAAAAAP///gAAAAAAAAAAAH///AAAAAAAAAAAAH//+AAAAAA + AAAAAAH//4AAAAAAAAAAAAD//wAAAAAAAAAAAAD//wAAAAAAAAAAAAD/gAAAAAAAAAAAAAD/AAAAAAAA + AAAAAAD/AAAAAAAAAAAAAAB/wAAAAAAAAAAAAAAf+AAAAAAAAAAAAAAf/AAAAAAAAAAAAAAf+AAAAAAA + AAAAAAA/+AAAAAAAAAAAAAP/+AAAAAAAAAAAAAP/+AAAAAAAAAAAAAf/+AAAAAAAAAAAAA//+AAAAAAA + AAAAAB//+AAAAB8AAAAAAB//+AA4Af/AAAAAAB//+AB////4AAAAAA//+AD/////AAAAAA//+AH///// + 4AAAAA//+AP//////AAAAAP/+AP//////4AAAAD//Af//////+AAAAA//h////////wAAAAf//////// + ///AAAAP///////////wAAAD////////////AAAB////////////4AAA/////////////AAA//////// + /////4AA//////////////AD//////////////4P//////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + ////////KAAAAEgAAACQAAAAAQAgAAAAAABgVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAgHMRsDAxZxAwMVhQICEXEFBBspCAggAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcGmkCCAglbgYG + IfMMDED/Cgk4/gUFGv8AAAD7BgYKniAgKwoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFhYgwEdHT6FGRaV/SQd4v8vKt3/UU7R/2Bf + y/88PI//BwcT/hIUHYQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAADY3SW87OqX9eHfS/7i52f7p6ur//f38/vj5+//s7vf+pqmz/x4d + HO0hLWIMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvsHNAa+y + whaipbBWsLG2ps7Oz/nx8vX/3OL6/6e39v92jfD/S2fr/zJT7P8qTfD/LlHw/ztUw/8kNIUyAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKOmtxujpbBlvr/FtuDg4PD19fX+zNLs/4mb + 6P5HZOz/JEju/yFF7/4jRu//Ikbv/iNG7/8iRu/+I0bv/yJG7/4lRuirJDbGBAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAJ2jwRego7NovLy/vebn5/fq7fj+tcDw/3OI6f83Vuf/Ikbu/yJG7/8jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//I0Ht/yUj4f8kOOn+I0DocgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjZS2GtPT + 1efk6Pj/obH1/1137/8uUO3/IUXv/yJG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//JDDm/yYe3/8lH+D/Izrq9SM66SsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhpLIBIGNwaAkSO/+Ikbv/yJG + 7/4jRu//Ikbv/iNG7/8iRu/+I0bv/yJG7/4jRu//I0bv/yJG7/4jRu//Ikbv/iNG7/8iR+/+JSbi/yUe + 3/4lHt//JSfj/iM+62IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFhx5QwjR+/jI0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//JSHh/yYe4P8mHuD/JSrk/yM5 + 6kUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlSO1tI0bv/iJG7/4jRu//Ikbv/iNG7/8iRu/+I0bv/yJG + 7/4jRu//I0bv/yJG7/4jRu//I0bv/iNG7/8jP+z+JR7f/yUe3/4lHt//JC7l+yQw5hUAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGBSIBBAQXDgMDFAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAjRu4NI0bv2SNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8kMeb/JR7f/yUe3/8lJeL/JSXizgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAADQtRCgYFJHcBAQXPAAEC7gAAAOsAAAPDAwMXTAYGJAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAI0TuWyJH7/4jRu//Ikbv/iNG7/8iRu/+I0bv/yJG7/4jRu//I0bv/yJG7/4jRu//I0bv/iNE + 7v8lJuL+JSDg/yUj4f4lL+b/JSDghgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOC1YeBwckyBQS + dP8dGaz/IBu5/hsXof8ODUz+AAAB+wMDEokNEVADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAND7nBzM95wQAAAAAAAAAAAAAAAAAAAAAIzfpAiQ8 + 67YkNej+I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yQ26P8lLuX/JiDg/yUv + 5v8lJuLpJSPhGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABIQbRcPDlPVIx3Y/yUd6P8lHej/JB3l/yQe + 2P8jHsf/Fxds/wkMLf4OFlpFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJCrkBSY051gmL+WrMTrm3EBK6dQqL+S3Ji7lmCUr5HQkMudUJDXoMSc46CQmIuHGJCre/h4w + wv8gPtz+I0fv/yJG7/4jRu//I0bv/yJG7/4jRu//I0Ds/iUw5v8lK+T+JSDg/yUn4/4lIOBnAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAFxSDBBIQYrIkHeT/JB3k/iQg0f8kIMz/JSDK/iUf0v8lHt7+JR7h/yUp + 5P4iRezaHzvWUgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgJ8oEJC7ksiUf + 3/4nIeD/R0jn/z495f81M+P/JR7f/yUe3/8lHt/+JR/g/SUj4fgrL+TwO0TZ/ycuwf8bIaj/Gyas/x82 + yf8gOtT/ID7c/yFC5f8iNt//JSTg/yUt5f8lH+D/Jh7g/iUf36glI+ECAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAExFnXyAduP4gHa7/JB/F/yUf3v8mHuD/Jh7f/yYe3/8mHt//Jh7f/yUe3/8jQu3/I0bv/CpM + 61YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGxqlFyMe0XwlHt/lJh7f/yYe + 4P8lHt//JR7g/yUe3/8mHt//Jh7g/yUe3/9VWOn/lqL3/4+a9f9/ifP/aHLv/09Y5f8yO9L/Hie8/xok + qf8YHJD/FxWL/x4Zsv8kHtn/JR/fxSQl3xAAAAAAAAAAAAAAAAAAAAAAAAAAACQd1xwjHdNcHhqk4iQe + zf8mHuD/Jh7f/yYe3/8mHuD/Jh7f/yUe3/8mHuD/JR7f/yYe3/8jO+r/I0bv/yNH7+E/WtkToarVNwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAACAcvHwUFHVsICTY4EhFwAQAAAAAiHb8IJR7aYiUe380lHt/9JR7f/yUe + 3/4lHt//Jh7f/yUe3/4mIN//PDrk/lJV6f9kaez+dn/w/4qV9P6Wovf/jJj1/3V98P5fa+//P07m/iEt + zf8dKrr1IzLiWyMs4hUAAAAAAAAAAAAAAAAlI95RJR7f2SYe3/0lHt/+Jh7f/yUe3/4lHt//JR7f/iYe + 3/8lHt//JR7f/iUe3/8lHt/+JR7f/yUe3/4jPOv/Ikbv/iNH7/6Zpdi5QUZdawAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAMC0VLDg5Q7AgIKf8AAAD6BQQhigAAAAAAAAAAAAAAACUe3gYlHt9IJR7ftiUe3/kmHt//JR7f/yUe + 3/8lHt//JR7f/yYe3/8lHd//JR3f/yol4P9HSOf/anPu/3uG8f90ffD/dHzw/0FA5f8lH+D/JSPh/iUs + 5O4kNOi/JDjpfiQx5hYkK+EPIiDNkh8at/UkHdT/JR7e/yUe3/8lHuD/Jh7g/yYe4P8lHuD/JR7f/yUe + 3/8lHt//JR7f/yUi4f8jRO7/IkXv/4CW8f1CQkPzICMzXQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQTYDAREWTwGxel/yQe + 4/4YFYn/AgIJ/gYFJVQAAAAAAAAAAAAAAAAAAAAAAAAAACUl4iwlIeGYJh7g7iUe3/4lHt//JR7f/iYe + 4P8lHt/+JR7f/y0o4f51fvD/laH3/5Wh9/6Pm/b/VVnp/iUe3/8lHt/+Jh7f/yUe3/4mHt//JR7f/iUl + 4t0jP+uPIkHrfSAz0J4aJKL+GSOh/xogn/4aHZ3/Ghqa/hsXof8kHdb/Jh7g/iYe4P8lHt/+JSDg/yM9 + 6/4iRu//X3v0/pucnv4BAQH+IiMsTgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHRxyDSQmbs8gHLb/JB3n/yUd5/8lHej/FhSD/wID + EasAAAAAAAAAAAAAAAAAAAAAAAAAACM26AcjOOkhJDXoZSQv5uV1eNP/Z2jU/yUe4P8lHt//JR3f/4CB + z/9oZ9T/U1bp/5Ke9v+Tn/b/k572/1ZZ6f8vKuH/JyDg/yYe3/8lHt//JR7f/yQv5v8jRu//I0bv/yNG + 7/8jR+//I0fv/yNG7/8jRu//IkXr/xsmpv8XFIb/GRWR/xoXnP8gHMD/Iznn/yNG7/8lSe//1Nz5/ycn + J/8DAwP9UVFUOQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAHB1Qfzw7wP4iHNf+IBrE/yQd5/4lHef/JB3c/wUHHr0AAAAAHDjADSJF + 7TsiRe51I0XuriNG798jRu/8I0bv/yJG7/5ke93/nJ7L/iYo4v8lHt/+JR7f/25t0v6Sksz/JB3f/zQx + 4v44NuP/RETm/nuE8f+Tn/b+a3Pu/yUd3/4lIOD/JDfp/iNG7/8jRu//Ikbv/iNG7/8iRu/+I0bv/yJG + 7/4hP97/GiSi/hwttf8gPdn/IUHi/iA71f8cLLP+GCCX/xkkof49VNb/uLq9/gAAAP8aGhr9ZGVlMwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAcHGMNMDJ26SMdzv8ZF5D/FhWC/yQd4/8kHef/JR7q/xUkfNoiReu4Ikbu7iNG7/0jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/9FYub/ur7G/0tm5P8jPev/JSvk/0dF2v+xssf/KCLf/yUe3/8mHt//JR7f/y0o + 4f9hZez/NjPj/yUn4v8jP+z/I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yA+3P8bGqD/JB/Z/yM/ + 7P8jRu//I0bv/yNG7/8jRu//Ikbu/yA+2/9JVLX/YWJq/wAAAP9OTk/7b29vJAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEUFNKSqK/h8a + xv8VE3n+Gxeh/yQd5/4kJOj/Izzt/yNH8P4jRu//Ikbv/iNG7/8iRu/+I0bv/yJG7/4jRu//Ikbv/yJG + 7/4sTuz/mKTP/o+d0v8iRe/+K07w/ylI7P66vMb/OkTh/yQt5f4kLOT/JC3l/iQy5/8kOOn+I0Dt/yNG + 7/4jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8iRu/+IkTr/xodnf4lHtr/JR7f/iQu5f8jRu//Ikbv/iNG + 7/8jRu/+Izrq/yQx5v4hKbT/AQEB/gAAAP+GhobramptCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALDDOHGhqB/x8Zw/8gGsX/IyPX/yQ1 + 6f8jRe//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//J0rv/y1Q8P8jRu//QmLx/yhM7/+Infb/qrPR/32P + 1v9HZOX/YX30/zFS8f+dps7/WHHg/yJG7/8jRu//I0bv/yNG7/8jRu//Ikbv/yJG7/8jRu//I0bv/yJG + 7/8iRu//I0bv/yVI7/8iRu//HTG+/yEbxP8lHt//JSDg/yNB7f8jRu//I0bv/yNG7/8kLOT/JR7f/yUe + 3/8jHc7/AwMP/wMDA/+SkpmLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHCCOpFBJ7/yAouv8jPOH/I0Xq/yNG7/8jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//JEjv/0Vn8v8vUvD/WHbz/zdY8P9rhfT/oavU/0Vj5v+nsNP/k6n3/yZK + 7/90h9n/fI3X/01t8/8yVvH/WHfz/0Ji8f8iRu//MFLw/1Jx8/8rT/D/IUXv/1578/9HZvL/LVHw/0ho + 8v8hRfD/Gx+m/yUe3/8mHt//JDLn/yNG7/8jRu//I0bv/yQv5v8lHt//JR7e/x4ZtP8uLLX/BgYd/woK + GvxYV5c2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAABkupwEPG1i7HTC8/iJG7/8iRu7+I0bv/yJG7/4jRu//I0bv/yNG7/4jRu//Ikbv/iNG + 7/8iRu/+I0bv/yJE7v5Qb/L/Y3zy/ztY7/5NbPL/dIfZ/lFs4v+HltX+aoTp/zhZ8P5MaOT/oqvM/1Ny + 8/4qTvD/H0Pv/kxr8v9be/T+j6T3/z9f8f5EZvL/Ikbv/jdb8f9ObfL/Tm7y/miE9P8gQOP+Hxq2/yUe + 3/4lH+D/I0Lt/iNG7/8jQ+7/JSrk/iYe3/8mHuD+HRmo/5WWvv7s7Or/tbfG/jEvpv8vLdOXAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHz/ZCSFC + 4pIjRu/5I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8iRu//I0Xv/yQ76v8kLuX/JSXi/yUh + 4P9AQOT/iY7u/yol4P+rsfT/oqnj/3Z60/9cYOH/maHR/0VW6/8wQeX/t7rG/0Vc7f8uSOz/N1fv/0tq + 8v9+lfX/aIHz/0Nf8P9KZvD/j6P2/01o8P9TavD/K0ru/z1c8P8fOtL/IhzJ/yYe4P8lI+H/I0bv/yM7 + 6v8lIuH/JR7f/yYe3/8kHdn/VVah/+np6f/p6en/6enp/8fI3P8vReLcXmnLAgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiRu4cIkbuxCJG7/4jRu//Ikbv/iNG + 7/8iRu/+I0bv/yJG7/4jRu//Ikbv/yxM4v5RY8j/T1HC/iYe3/8lHt/+Jh7f/ycg3/4uLOH/PTzj/zs3 + 4/5VU+b/oqPr/piZy/8kHOD+jo/O/1xd3f4lHt//o6TJ/1ZV3P4vKeH/k5Tv/nV36/8pI+D+JB3f/yUe + 3/4nIOD/NzLi/iYe3/8lHd//JR7f/iUf4P8gIMD+Ix3R/yUe3/4lIeD/JC3l/iYf4P8mHt//JR7f/iYe + 3/8gGsH+YmR6/87Ozv6+vr7/2NjY/uvq6v92i+z8UGXhMgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAChG6SAjRu/YI0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG + 7/8jQu3/QlPL/6Cjuf+urrv/hoe3/yYf3/8mHt//Jh7g/yUd3/9HSOX/WVvn/ygi4P8lHuD/JyHf/6iq + yf8pI9//Qj/a/42Ozf8lHeD/fXzQ/3V41f8qJeD/JR7f/1VV5/8wK+H/Jh7g/yYe4P8mHt//Jh7f/yUe + 3/8lHuD/JR7f/yYe3/8gG73/Ix3T/yYe3/8lHt//KCbh/ykp4/8mHuD/Jh7f/yYe3/8eGq3/AQIF/wcH + B/8CAgL/Dw8P/3R0dP+bqez/R2HgZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAElJ + 0AlITNQQQk3dCSVI7sIjRu//I0bv/yJG7/4jRu//Ikbv/iNG7/8iR+/+I0Lt/yQv5v5QT7//ra6+/7a3 + xv6lprH/VlO//iUe3/8lHt/+Jh7g/yUe3/4lHd//Ixzf/yUe3/4mHt//JB3g/pucy/9APNv+JR7f/5GS + zP4+O9v/VlTX/6Ck0P5lZen/JyHg/ldX5/85NeL+Jh7f/yUe3/4lHt//JR7f/iUe3/8lHt//JR7f/iUe + 3/8gG73+IxzP/yUe3/4mIeD/cYLz/kNH5/8lHt//Lyzh/iUe3/8eGrL+AAEF/w4ODv6RkZH/sbCx/mpq + a/+eqdz/QFrYegAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEI+yjQrJsKoS03S0XF65exuduTzP0DN5V9t + 5vxRY+f/Q1nn/zJN5/8mRer/I0Xu/yM96/8kLeX/JSDg/09Nv/+vsL//trfG/7a3xv+2t8b/ZmW7/yUe + 4f8lHuD/JR7g/yUe4P8lHt//JR7f/yUe4P8lHt//JR7g/3180P9hX9X/JR7f/0hF2f+Jic7/MSzd/62v + x/9qaun/lprw/1lX5/8lHd//JR7f/yUe3/8lHt//JR7f/yUe3/8lHt//JR7f/yUe3/8hG8X/IRvD/yUe + 3/8lHt//Z3Lv/01R6P8lHt//W2Dq/ysl4P8iHMn/AwMM/4WFhf/q6ur/6enp/39/gP9EToT/OFHVdQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAERAyyptbNuupabs6bKz8P6JiOv+Jh/a/y8r3P41Mtv/RUXe/15i + 4v5ob+T/LCvU/iUe3/8lHt/+PzzI/6yuvv62t8b/trfG/7a3xv60tcT/lZOj/lY5c/9YJkr+WCZM/00l + a/4/I5T/MCDC/yUe4f4lHt//JR7f/l5d1f+FhM7+JR7f/yUe3/6Xmcv/Ojfc/6iqyP43Mtz/JB3f/iUe + 3/8lHt/+JR7f/yUe3/4lHt//JR7f/iUe3/8lHt//JR7f/iYe3/8kHdP+Hhmz/yUe3/4lHt//Pknp/ktU + 6v8lHt//YGXr/klK5/8lHt/+Hh5V/9bW1v7p6en/5OTk/icnJ/9OW6f/OEXlyDpB3TgAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAhoflCISJ6zFoa+lyJSnj6yUm4v8lH+D/Jh7g/yUe3/8lHt//JR7f/yYe + 4P8lHuD/hoe1/7a3xv+2t8b/s7TC/5OPl/9xSUj/ZSkl/2UoI/9lKCT/ZSgk/2UoI/9lKCP/ZSgj/1on + Rf9AI5L/KR/V/0A82/+nqcj/Jh/f/yYe3/9QTtj/hIXP/4WEz/9VU9f/JR7f/yUe3/8mHuD/JR7f/yUe + 3/8mHuD/Jh7f/yUe3/8lHt//Jh7g/yYe3/8lHt//HBim/yYe4P8lHt//JDDm/yQx5v8lHt//XWLr/3Z+ + 8P8lHt//My/B/9LT2/+Ghob/JCQk/zM0Ov8ySMr/Oz7m/2Jo7Po9POR6AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABJSuUSJT3r6iUl4v4lHt//Jh7g/yUe3/4lHt//JR7f/iUe3/8pI9f+pqe6/7W2 + xv61tsX/kZGZ/2o6OP5lKCP/ZSgj/mUoJP9lKCP+ZSgk/2UoIv5mJyH/Zici/2YnIf5mJx//YCcz/ksr + g/+4ucL+KyfH/yMd0f4nIN3/n6HK/25u0/56edH/JR7g/iUe4P8lHt/+Jh7f/yUe3/4lHt//JR7f/iUe + 3/8lHt//JR7f/iYe3/8mHuD+HRmt/yUe2v4lHt//JCvk/iND7f8lI+H/XGHr/pOe9v9EReb+X13m/4SG + ov4yM0X/ISI0/kJOrv8jPOfhJR7f7SUe3/4yLuK0ODbjAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAsKOF6Pkbo/ysl4P8lHt//JR7f/yYe3/8lHt//JR7f/yUe3/8uKtH/rq++/7a3xv+oqbX/g3V5/2Un + I/9lKCT/ZSgk/2UoJP9lKCT/ZCgn/zs8qf8pQ9z/V2ni/3N93v91fNb/dHbN/09VwP9Sadz/KD/J/x4y + vv8nNbf/U2C+/3R/wP+Gjb7/FxaX/xsXoP8eGbD/IRvF/yQd1/8lHuD/Jh7g/yUe3/8lHt//JR7f/yUe + 4P8mHt//Ix3Q/x8auv8lHt//JR/g/yNE7v8jRO7/SmTx/5Sg9/9xee//oaHr/zQ0Nf8XJIX/IULo/yNG + 7/4nNNliLjXVBiws4RoyL+IIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwKuGhLinh/11i + 6/9hZuz/U1bp/0lJ5/9BQeX/Ozjk/zYz4/87O9P/r7C//7a3xv+kpbH/gGpt/2UoI/9lKCT/ZSgk/2Uo + JP9lKCT/ZCgo/zI2vv8jO+v/PVLt/1Fk7/9UaPD/VGjw/z5Y7/8jQu3/I0Pu/0Be8f97jfX/i5r2/4qY + 9v+AkfT/bX/u/1ls4/9DVdL/LD2+/x0rr/8bIav/Hhyz/yEcxv8kHdj/JR7f/yYe4P8mHuD/Jh7g/x4Z + rv8iHMv/Jh7f/yQx5v8jRu//JUnv/2F48/86S+v/lJan/wAAAP8UJoD/I0fv/yQ46M8mM9gLAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbWei5dXTs/3d47P4sKOH/NDHi/zk2 + 4/5FROb/Zmzt/n+J8v+Eju/+oaO3/6ysuv6rrLn/jYeN/2YtKf5lJyP/ZSgj/mUoJP9lKCP+ZSgk/2Uo + Jf5QJGP/NCC1/yUd3v4lHd//JR7f/iYe3/8lHt/+JR7f/yUi4P4jJ+P/JzLm/zZG6v5IW+7/WnDx/muA + 9P99jvX+jJr2/4ya9v55i/b/YHfz/kVd5P82S9D/TFfE/jU6tP8bGqT+GRWU/xUTff4VE37/IBvA/iUf + 4P8jOun/Ikbv/iJG7/9jffL+OTk5/wAAAP4TJHD/Izvm4SY2yyYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAHV07APQ0PvPzc76/6iq9P8lHt//JR7f/yYe3/8lHt//JR7f/yUe + 3/8tKOH/iY/F/6ytuv+1t8b/m5ul/4uFi/+CZ2v/eFdY/3VRUf9zTk7/dlJS/3tcXf+JdHn/mJCa/4yN + wPpHRcXqJh7f8iUe3/4lHt//JR7f/yUe3/8lHt//JR7f/yUe3/8lHuD/JSDg/yQl4v8kLuX/Jzrq/zFM + 7v9DYPH/WXLz/3OG9P+NnPb/t8H6/5+u+P9PaO3/MEfU/xwqrv8WF4X/FBJ7/x4Zs/8lH9//JDPn/yJD + 7v+Aj8n/BAQE/wAAAP8jJ1vcJCmJHwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAI2N8ARkZOnVOzfj/yQd3/4mHt//JR7f/yUe3/4lHt//JR7f/iUe3/tude7Okp3y2Zaa + u+uxssD9t7fH/6usuv62t8b/t7jI/ra3x/+ur7z7oKGs4ZSSm7eGgoyHeHCIV2FamSlLRrYJMSvXGycg + 3nQlHt/QJR7f/SUe3/4lHt//Jh7g/yUe3/4mHuD/JR7f/iUe3/8lHt/+JR7f/yUe3/4lI+H/JSvk/iM1 + 6P9GYfD/gJf2/oGW9v94ivX+k5/2/36P9f43Vuz/HjTE/hcdkf8ZFpb/IxzO/i8r4f9vcX3+AAAA/wAA + AP5gYGHXR0pqAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIuL + 7wZ6eu3doKHz/4qL8P8lHuD/Jh7f/yUe3/8mHt//Jh7f/ykj4JtPTt4DcXLWBZCTyQqLjZw/k5Sdi5OT + nK2QkJegkpGZfYiGjFCGgYcgi4GIBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlHt4FJR7fNyUe + 35QlHt/kJR7f/iYe3/8mHuD/JR7f/yUe3/8mHt//Jh7g/yYe3/8mHt//JR7f/yok4P+Fie7/cHXs/zU/ + 5/8kOur/MU/v/1hx8v9bdPP/Ikbv/yJG7v8gO9T/HSuxt2Rq1W5MTEz/AAAA/wAAAP2KiovEZWZzFAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKB7gnMzfrntrf3/3d3 + 7P4lHt//Jh7g/yUe3/4lHt//Jh7fyzEs4Q4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlHt8NJh/fUCYf + 3641MuL0OTfi/jk34v87OeL+Pj3j/0A+5P5FQeX/U1Tn/oKI7v90eOz/U1Ln/iUe3/8lHt/+JSDg/yUq + 5P4jN+n/I0Tu/iNG7/8jRu//Ikbv/nKK8PMoKCj+AAAA/wMDBr+anKR8jZGoMQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIyM7wk9O+PnOjbj/z064/8lHt//Jh7g/yYe + 4P8lHuDjJR7fIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABKSOUWYmPpaF9h + 6MFeX+j3WVvo/1dd6P9VWef/VFXn/0ZD5f8oIeD/JR7f/yYe4P8lHt//Jh7g/yYe3/8mHt//JSDg/yUs + 5f8jO+r/I0Xv/3eP8/9AQED/AQIG/x82obV0gb97jZfDVgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAISE7gKkp/TAzM36/6ip9P4lHd//Jh7f/yUe3+4lHt83AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEpK5QFAPuMnMy7heycg + 4NQlHt/+JR7f/iUe3/8lHt//JR7f/iYe3/8lHt/+Jh7g/yUe3/4lHt//JR7f/iYe3/8lHt//JSTh/naA + 7/+Xl5f+ESBk/yNG7/4mSe79jJ/uxGqD7TQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAB9fe1ZfHvt/UM/5P8lHuD/JR7g7yUe3z8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADg04gErJeAyJh/fiSUe + 39olHt/8JR7f/yYe4P8lHt//JR7f/yYe3/8lHt//JR7f/yYe3/8lHt//Jh7g/25s6v/Z2uX/JCzY/yM9 + 6/8jR+//jaP3/1d18/wpTO+vKEvvLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAB0cuwFPDbjeicf4L8nIOCxLCXgNQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUe3wglHt84JR7fgiUe + 384mHt/6Jh7f/yUe3/8lHt//JR7f/yYe3/8lHt//JR7f/zAq4f94eOv/JR7f/yQe3/9udu3/m6r2/0pp + 8v8jRu//I0bv8yNF74YjRO4JAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlHt8mJR7fdCYe + 38MlHt/3Ixzf/iMb3/8jG9//Ixvf/iMc3/8oIuD+SUbl/4mL7v6qsPP/k5nx/ior4/8kOOn/I0Xu/iNG + 7/8jRu/LI0PtLgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgh3wItJ+AjY2PoboSI + 7sKDhO72gYPt/4WL7v+Nle//kJbw/3h87P9ZWOf/LCfh/yUe3/8mHt//JSTi/yQ36f8jRe7/I0bv8CND + 7W0jP+wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAO+MeMy3haycg + 4LwkHN/2JB3f/yUe3/4mHt//JR7f/iUe3/8lHt//JR7f/iUe3/8lJOH+JDfp/yNF7v4jRe6wIzzqFQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE9N5QFBPeMbLynhZiYf + 4LglHt/yJh7f/iYe3/8mHt//Jh7f/yUe3/8mHt//Jh7f/yUl4v8jPev+Iz7rVAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlHt8aJR7fXiUe + 364mHt/qJR7f/iUe3/8lHt/+Jh7g/yUe3/glJ+OWIzboCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlHt8IJR7fNSUe + 33UlHt+DJR/fSiUi4RQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////wAAAP///////////wAAAP///////////wAAAP// + /////////wAAAP///////////wAAAP///////////wAAAP///////////wAAAP///////////wAAAP// + /////////wAAAP///////////wAAAP/////wP////wAAAP/////AH////wAAAP////+AH////wAAAP// + //+AD////wAAAP////gAD////wAAAP///+AAB////wAAAP///wAAB////wAAAP///gAAA////wAAAP// + /gAAA////wAAAP///wAAA////wAAAP///4AAA/4//wAAAP///4AAB/gH/wAAAP///8AAB/AD/wAAAP// + 88AAB+AD/wAAAP//gAAAD8AB/wAAAP//AAAAD8AA/wAAAP//gAAAHwAAPwAAAP/8IAAAHAAAPwAAAP/4 + OAAAAAAAPwAAAP/wHwAAAAAAPwAAAP/gHwAAAAAAPwAAAP/gEAAAAAAAPwAAAP/AAAAAAAAAPwAAAP/A + AAAAAAAAPwAAAP/AAAAAAAAAfwAAAP/AAAAAAAAAfwAAAP+AAAAAAAAAfwAAAP8AAAAAAAAAPwAAAP4A + AAAAAAAAPwAAAPwAAAAAAAAAPwAAAOAAAAAAAAAAPwAAAAAAAAAAAAAAPwAAAAAAAAAAAAAAHwAAAMAA + AAAAAAAADwAAAPAAAAAAAAAABwAAAPAAAAAAAAAADwAAAPAAAAAAAAAAfwAAAPAAAAAAAAAA/wAAAOAA + AAAAAAAB/wAAAOAAAAAAAAAB/wAAAOAAAP4AAAAB/wAAAOAH///AAAAB/wAAAOAP///4AAAB/wAAAOAf + ///+AAAA/wAAAPA/////wAAAPwAAAPB/////+AAADwAAAP///////4AABwAAAP///////+AAAQAAAP// + //////4AAAAAAP////////+AAAAAAP/////////4AAAAAP//////////AwAAAP///////////wAAAP// + /////////wAAAP///////////wAAAP///////////wAAAP///////////wAAAP///////////wAAAP// + /////////wAAAP///////////wAAAP///////////wAAAP///////////wAAACgAAABAAAAAgAAAAAEA + IAAAAAAAAEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYF + JUwCAhClAgIOrQICDoUICCAmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJyZrCAkJJaoSEGr/GBSH/xQRev8LCkX/AAAC+RUVHFkAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAc3SPBBwbT7IkIM7/SkfR/4aF1/+2ttz/tbbd/2tskf8FBQXvJi5UDwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmp2pIHJzeaGjpMz/4+Pm//P1+//J0vb/nq3w/4WZ + 8/9/lPb/YXCv/xojUFUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtrnJAaOmszCztcGAysrNz/Dw7/3b3+//lKXw/1Fv + 8P8mSe7/IUXv/yNG7/8jRu//I0bv/yJG7/8oR9yjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACan7MxsLK9h83Nzdvx8fT/xMzx/4GV + 6v9AXuX/IUXu/yJG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8kMuf/JDjp/yM+5GEAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACsrrih6ev0/6+9 + 9/9rg/D/LlDs/yFF7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jQe3/Jh7f/yYe + 3/8kOerzIznpIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAho+7US5R8PsiRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//JDfp/yYe3/8lHt//JSbi/yM861YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqTe2iI0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//I0bv/yQx5/8mHt//Jh7f/yUq5P8jNug1AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJUftJSNG7/ojRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8lJuL/JR7f/yUe3/8lLeXyJCzlCQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJCDYDBQUfNgMDFFQDAxNBBAQcCAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAjRu+YI0fv/yNG7/8jRu//I0bv/yNG7/8jRu//I0fv/yNG7/8jRu//I0bv/yNG7/8jPuz/Jh/g/yYe + 3/8lLOX/JR/gvQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJCDtEBAQR3AoKNv8MCzz/BAQS/wAA + AOIFBB1GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAIz7rFyNC7e0jRO7/I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//JC7l/yUo4/8lI+H/JS/m/yUh4FgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKCTlbFhSC+iQd + 4P8lHen/JR3p/yUd5v8UE3D/AAAA+wgLM08AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAKDPmHSk05msoMeWUKTLleykx5VsmL+U4JDHmGCQz5wInM+ZBJSPh9yM75f8hQeL/I0fw/yNG + 7/8jRu//I0bv/yNG7/8jRu//Iz/s/yUw5v8lIuH/JSzl/yUi4cAlJOEBAAAAAAAAAAAAAAAAAAAAAAAA + AAARD108HRmy+SUd6P8kHuD/JCDP/yQgzP8kIMX/JB7J/yMiyP8eOca9HTTFJAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAJDHmiCUj4fswLOH/TlHo/1BS6P8mH9//Jh7f/yUg4P4lJeLwJCzl0Cov + 5OEmKsr/GR+k/xsnqf8fNsr/IUHj/yJE6v8jR+//JDnq/yUq5P8lKeT/Jh7f/yYe3+0lIeAhAAAAAAAA + AAAAAAAAAAAAAAAAAAAZFpQIGhiZ2yActv8jH8b/JSDT/yYe4P8mHt//Jh7f/yYe3/8mHt//Izvr/yNG + 7/AsTekyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsapx4kHtSQJh7g8iYe3/8mHuD/JR7f/yYe + 3/8lHt//Jh7g/yUe3/9nbe3/lKD3/4KM8/9qcvD/UVvm/zM8zP8eJbb/GiSm/xgZjf8aF5z/IhzI/yYe + 4PckI95CAAAAAAAAAAAAAAAAAAAAACQe3AMjHdIwHRmomiIeuv8lHt7/Jh7f/yYe3/8mHuD/JR7f/yYe + 3/8lHt//Jh7f/yQz5/8jRu//JEfuzmJ43RV+ib0bAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALCkEMBAQYYwYHKlgREGoEAAAAACId + xBElHt13Jh7g4iUe3/8lHt//JR7f/yYe3/8lHt//KCLg/0FA5f9UV+n/Zmvt/3yF8f+UoPb/jpr1/3iA + 8f9hbvD/OUbh/x8pxf8hL9KxIzLkMyQr5AMAAAAAAAAAACUh33YlHt/rJh7f/yYe3/8mHt//JR7f/yUe + 3/8mHt//JR7f/yUe3/8lHt//JR7f/yUe3/8kNOj/I0bv/ydL8P6SmLHHP0VjJgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAREF0YDQ1I1hIR + af8DAw7/AgISuw8MXQYAAAAAAAAAACUe3wYlHt9aJR7fyiYe3/8lHt//Jh7g/yUe3/8mHt//Jh7f/yUe + 3/8lHd//Skzn/2537/9+ivL/bXbu/19j7P8nIeD/JR7f/yUj4f8kLeXrJDTnrCQx5jAjLOAOISDEjR4Z + svgiHMj/Ix3Q/yQd1v8lHtz/Jh7g/yUe4P8mHt//JR7f/yUe3/8lIOD/I0Lt/yJG7/+bquT8FBQU9zA1 + SBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAcG3UEExRdwRoWof8kHeX/IhzU/wgILP8FBB9sAAAAAAAAAAAAAAAAAAAAAAAAAAAkKOM8JSDgqyUd + 3/k+Otv/Line/yUe3/8lHt//MCvd/1xf4/+HkfT/lJ/3/5Wg9/9WWer/JR7f/yYe3/8mHuD/Jh7f/yYe + 3/8lI+H0I0fv0SNG778fOdHgHTPC/xwwu/8cLLP/Gyep/xgXjf8hHMb/JB3W/yUe3/8lH+D/Izvr/yNG + 7/+Al/b/aWlp/wYGBvo8PkUKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAHh9ZeS4tuP8lHej/JR3n/yUd5/8fGr3/BQchowAAAAAAAAAAAAAAACNA + 7BYjP+xMI0HtgiNC7bMjP+zxkprP/0xK2f8mHuD/JR7f/1ZU1/+Ymcv/MCzi/2927/90e/D/iJL0/2tx + 7v9UVun/JyDg/yUe3/8lI+H/Iz/s/yNG7/8jRu//I0bv/yNG7/8jR+//I0bv/yA81/8ZIp7/GiOg/xkk + oP8YG5H/HCSw/x861P8hRe//z9Xq/wkJCf8wMDD0MjIzAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBxiEjk7jPMiG9j/FhSF/yEbz/8lHef/JR7o/w0T + SbEeOs9bIUTnmyNG79gjRu/9I0bv/yNG7/8jRu//I0bv/3KF2f+SnND/IzTo/yUk4f8wK97/ubvF/yYf + 3/8lHt//JR7f/yYf3/9xeO//fIXx/ykj4P8lKuT/I0Tu/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/x0v + vP8eHbT/IkTr/yNG7/8jRu//I0fw/yE+3P8cLLT/KTGe/56fpP8AAAD/Z2dn6wAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQRmkuLaH/HBiv/xcX + fP8iHNj/JB3n/yQu6/8jROf+I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/9UbuH/oqzN/0Be + 5/8jRu//ITjq/6uwyf88Pd3/JSLg/yUh4P8lI+H/JCjj/yQv5v8jPOv/I0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//I0bv/x40xf8gGrz/Jh7g/yQz5/8jRu//I0bv/yNG7/8jRu//Iz7s/yo92f8UFBb/AAAA/6Oj + o8wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAMDEGoHh6K/yEb0P8fGbz/JCjj/yM+7f8jR+//I0bv/yNG7/8jRu//I0bv/yJG7/8mSe//I0bv/zVW + 8P8mSu//fJLv/4CQ1v+JmNP/Ikfv/2N+9P+CkdX/X3ff/yJH7/8jR+//I0fv/yNH7/8jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//I0bv/yJF6/8bGqT/Jh7g/yUf3/8jPuv/I0bv/yNG7/8jQu3/JSTi/yUd + 3/8lHtv/AwMO/wICAv+RkJh0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAACAguzhUVfv8kMdz/I0Di/yNH7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG + 7/8iRu//PmDx/zdZ8f9TcvP/Q2Lx/3SM9f+SntL/eYvX/3+V7f9TcfP/W3Pg/4KS1f9EZfL/Nlrx/1h2 + 8/8pTO//J0rv/05t8v8pTe//IUXv/1p38/82WPD/KEzw/0Nk8v8eM8P/Ix3P/yYe3/8kL+b/I0bv/yNG + 7/8jQ+7/JSXi/yUe3/8hG8f/Kyi7/wkJLP8PDyT7WVmQFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGjCsAxIhcOAfOtT/I0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//I0fv/yNE7v9BX/D/aH/y/0hh7/9HZ/L/k5/Q/zJU6/+OndT/UnHz/zRV + 6v+or8v/U3P0/yJG7/8hRu//Xnv0/2aD9P9rhvT/QGLy/yRI7/84XPH/TGzy/2WC9P9FZfL/HSi2/yYe + 4P8lHt//I0Ds/yNH7/8jPuv/JSPh/yYe3/8iHMz/YmOk/+rq6v/Aws3/NzWv/zk6yG4AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIELhHCJE6cEjRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//I0bv/yJG7/8jPOv/JC7l/yUj4f8lHt//Ling/4CF7f8tKOD/sbX0/8PG + 2P87Otz/oKLT/15r5f8jLeX/r7LI/01c6v84TOv/Y3zy/3GH8/9ndu//SFzt/zJI6/+Vo/X/S1vs/zhJ + 6/8ySOv/JEHt/x4iuP8mHt//JR/g/yNG7/8kMuf/Jh7f/yYe3/8mHt//Kiio/+Dh5f/p6en/6enp/9HR + 3/8zTeO1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIkXuMyNG + 7+YjRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//Ikbv/01jyv+CiLz/U1HC/yYe4P8mHt//JR7f/zIw + 4f8yMeH/NTLi/0M+5P+Cg9r/YV7V/zg03P+Ul9D/JB3f/4qKzf9eX9v/KCHg/2lp6f88OeP/JR7g/yUe + 4P8lHt//JBzf/yUe3/8lHt//Jh7f/yYe4f8fGrT/Jh7f/yUe3/8lI+H/Jh7f/yYe3/8mHuD/Jh7h/xwb + Xf9+fn7/d3d3/5WVlf/W1dX/ZH3t915w4AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAMkznLSNG7+8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//JTjl/3J3t/+2t8X/oqKt/0ZD + xP8mHt//Jh7g/yUe3/8yLuH/T07m/yUe3/8mHt//Qj7b/4WEz/8lHd//iInO/zg03P9kYtT/iY/W/ygi + 4P8lHt//UFHm/yUe3/8mHuD/JR7f/yYe3/8lHt//JR7f/yUe3/8mHuH/Hhqz/yYe3/8lHt//O0To/zQ4 + 5f8mHt//JR7f/yYf4P8KCjn/AAAA/xAQEP85OTr/ZmZm/36R6/9XbdUuAAAAAAAAAAAAAAAAAAAAAAAA + AABIRcwRQ0DLR15h2W9fY9yPREXRhjtP48cpROb/IkLq/yFF7v8jR+//I0bv/yNE7v8kNej/JiTd/3t7 + tf+2t8b/trfG/7W2wv8zLsz/JR7f/yUe3/8mHt//JR7f/yUe3/8lHt//Jh7f/ykj3/+kpsn/Ixzf/z47 + 2/+EhM7/PTjb/56gzf+Vl/D/hojt/1VU5v8mHuD/JR7f/yUe3/8mHuD/JR7f/yUe3/8lHt//Jh7h/x4Z + r/8lHt//JR3f/3R/8f8/Qeb/JB3f/0hK5v8mHuH/DQxK/xgYGP/c3Nz/6urq/4WFhv9UZrn/S1/LMwAA + AAAAAAAAAAAAAAAAAAAAAAAAOzbIVFNR0POKjub/l5nq/z072P9MTOH/V1ni/11h4P9mcOP/Wmnl/yQq + 3v8lH+D/JR7f/3V1tv+2t8b/trfG/7a3xv+3uMf/cXK0/z8klf9GJIL/PiKZ/zAgwf8mHt//JR7g/yUe + 3/8kHOD/p6nJ/ysm3v8lHd//j5HN/zUx3f+qrMj/JB3g/yYg3/8lHt//JR7f/yUe3/8lHt//JR7f/yUe + 3/8lHt//JR7f/yUe4P8dGaz/Jh7h/yUe3/9JUOn/VFrq/yQc3/9ye+//JR7f/xcVhf+FhYb/6enp/+vq + 6v8+Pj7/S16//zc/1V0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCg+IJm5zsPZmd8X45OOPOJSHh/yUg + 4P8mHuD/JB3e/yYg3P8lHt//Jh7f/0E+xv+1tsT/trfG/7W2xf+dm6X/eFZX/2YxLf9lKCP/ZSgk/2Uo + I/9mKCH/YScx/0wlcP8xIb7/JR7h/4uLzv9KR9n/Jh7f/0ZD2v9/f9D/k5TM/zg03P8lHt//Jh7g/yUe + 3/8lHuD/Jh7f/yYe3/8lHt//JR7f/yYe3/8mHt//Hhmw/yUe3P8lHt//JC/m/yQt5f8lHd//fYfy/z89 + 5f8jHdL/rq/G/7S0tP9TU1P/LCww/y5F1P9aXur/T1PouDYz4w4AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAMUHpiCQv5v8lHuD/Jh7g/yYe3/8lHt//JR7f/yUe3/9qab//trfG/7a3xv+Wlp//azw5/2Uo + JP9lKCT/ZSgk/2UoJP9lKCP/Zich/2YoIv9mJyH/Zicg/1UlVP95bK3/bGvJ/yMczf8kHd3/lpfM/3h5 + 0f9cWtf/JR7g/yYe4P8mHt//JR7f/yYe4P8lHt//JR7f/yUe3/8mHt//Jh7f/yMczf8gG8D/JR7f/yUq + 5P8jQez/JR/g/3yG8f95gvH/JiDg/5WYwf9JS13/Ghsq/0FNrP8kNujeJR7f+i8q4f01MeI2AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAOTrkBy8y5PQ8OuT/JR3f/yUe3/8mHt//JR7f/yUe3/8lHt//d3e9/7a3 + xv+xscD/gXR3/2UoI/9lKCT/ZSgk/2UoJP9lKCP/SDV+/ypD2/9UaOT/d4Hg/3h/2f92eM7/QlLM/z5Y + 2f8eNcX/LT2//1Ffw/9wfsb/ZW25/xkZmv8bF6H/Hhmz/yIcy/8lHt7/Jh7g/yUe3/8lHt//JR7f/yYe + 3/8mHuD/Hhmw/yUe4P8lH+D/I0Tu/yND7v9qfvP/lKD3/2Rm6v92dnf/EBld/yFC6P8jRe/+KTPTNi0y + 2wstK+EUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE1M5yIlHd//QUDl/2Jo7P9cYuv/V1rq/1Za + 6v9RUuj/TE7o/4SGwP+2t8b/rK27/4Ftcf9lKCP/ZSgk/2UoJP9lKCT/ZSgk/04uav8nM97/MD/p/0BN + 6/9DUev/Q1Lr/yg86v8kOur/LEfs/2N48/97jfX/jZr2/4uZ9/96jPb/ZXnt/1Bi2/83SMX/ITC3/x0l + u/8gH7//IRvI/yQd2f8mHt//Jh7g/yAbv/8dGKz/Jh7g/yQu5f8jRu//K03v/0dg8P+JlOD/CgoK/w0a + VP8jR+//JDTkpAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5d+08mJny/7e5 + 9/8wK+H/JB3f/yQd3/8sJ+H/Tk/o/15i6/91ecn/pqaz/7a3xv+SkZn/bkE//2UnI/9lJyP/ZScj/2Un + I/9lJyP/Yigt/0sxhv8zLtn/JR3f/yYe3/8mHt//Jh7f/yYe3/8mH9//JCPh/yQr5P8uO+j/P1Hs/1Fo + 8f9ievP/don1/4iX9v+Ckvb/aH71/3OH7P9ic9L/LDu4/xkemf8UE3r/FBJ5/xsXn/8lHt7/JDXo/yNG + 7/8kSO//c3eF/wAAAP8NFkb/IzPVsyo8xQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAn6DzTKus9f96ee3/LCbg/yUe3/8mHt//JR7f/yUe3/8nIN//eIHu/5+iuP+2t8b/qqu4/5qa + pP+inqn/mZCa/5eMlP+YjZb/m5Od/52cp/ybnabdiomlrEhGu34nIN63JR7f+yUe3/8mHuD/JR7f/yUe + 3/8lHt//JR7f/yUe3/8lHt//JSPh/yQs5f8jNej/LEjt/0Nh8f9/k/b/sr35/5Og9/9vhPX/TWXp/x8y + vf8WGor/GBWQ/yQd0/8lKeT/W23v/ykoKP8AAAD/Ly876zI3cgMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAGJg6VRKRuX/YWDp/yUf4P8mHuD/Jh7g/yUe3/8mHt//PTvji3l+ + 50yKkNZjkpOil6Kireisrbv9p6i09ZucptaUkpqkiYSKcYB2ej9+cnoSAAAAAAAAAAAAAAAAAAAAACYf + 3R8lHt98Jh7f2SYe3/8mHt//Jh7f/yYe4P8lHt//Jh7g/yYe3/8mHt//Jh7f/yYe3/8kIeD/f4rx/2x/ + 8f8qSO7/UGvy/3uM9f91iPT/Ikbv/x83zf8ZIZz/IR+2soaG0NsAAAD/AAAA/1RUVfVkZnUTAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACjpPNi0dH7/8HC+P8pI+D/Jh7f/yUe + 3/8lHt//JyDgvTs34gMAAAAAAAAAAAAAAACcnasBk5ScDZaXnwUAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACUe3wElHt85JR7flyYe3+0kHd//JBzf/yQd3/8kHd//JR7f/yYf + 4P83MuL/aWzq/3V57P9WVef/Jh7g/yUm4v8jMuf/J0Tt/yJG7/8jRu//I0fw/yJE6teJkLDiAAAA/wAA + APtzdHiDio2dOQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeXntZkZD + 5f8vK+H/JR7f/yYe4P8mHt//JR7f3CYf3xQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtJ+AIVljnUnJ3 + 669pbur3bnPr/2lx6/9qcer/ZWjp/1hZ6P8wK+H/JR7f/yYe4P8lHuD/Jh7f/yYe3/8lJeL/JDTo/yND + 7f8hRe//jZa1/wAAAP8OGEjXaHWzUZKav2MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAH+B7j/Bwvj/zc/6/ykj4P8mHt//Jh7g6iUe3yYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAREPkEjUx4mYpI+DBJR7f/CYe3/8lHt//JR7f/yYe3/8lHt//JR7f/yYe + 4P8lHt//JR7f/yYe3/8lH9//Iynk/8vR9P8aHSr/IULe/yRH7vyGmurCaIHrKQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2duwCfHvt1FFO5v8mHt//JR7f6yUe3zIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwm4B8mH992JR7fzSYe + 3/0lHt//JR7f/yUe3/8mHt//JR7f/yUe3/8mHt//JR7f/yQd3//Fxvb/ZGfR/yQ16P8hQ+7/hZz3/1Fv + 8/sqTe+gJUfuHQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFhU5yIyLOGELCXgiS0m + 4CMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAlHt8iJR7fbCUe37smHt/5JR7f/yUe3/8lHt//JR7f/yYe3/8lHt//S0fl/zMu + 4f8kHd//b3Hr/6Cs9f9BX/D/I0bv/yNG7/MjRO5tI0TuAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJR7fEyYe32EmH9+0Lynh9zYv + 4v81LuL/NTDi/0VC5P9oaen/nKLx/6mw8/94e+z/JSDg/yQv5v8jQu3/I0bv/yNG77YjQe0ZAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAFdV5hBydetcaGnqr2Nj6fVbXOj/UVHm/0VD5f8sJuD/JR7f/yUe3/8mHt//JR/g/yQu + 5f8jQu3/I0bv6iNB7VEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABOTOUOPTnjVy0m4KwlHt/zJh7f/yYe + 3/8lHt//Jh7f/yYe3/8lHt//JR/g/yQv5v8jRO7+Iz7sVgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJyHfCyUe31IlHt+lJh7f7iYe3/8mHt//JR7f/yYe3/8mHuD/JSTi5iM76isAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUe3wYlHt85JR7feyUe36wlHt+XJSDgVCUm + 4hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////////////////////////////////// + /////////////////////////////////////////////////////////////wf////////8A/////// + //gB////////8AH///////8AAf///////AAA///////4AAB///////gAAH///////AAAf//////8AAB/ + B/////4AAP4D/////gAA/AH///8AAAD4AP///gAAAfAAf//+AAADwAAf//CAAAGAAB//4GAAAAAAH//A + fAAAAAAf/8BwAAAAAB//gAAAAAAAP/+AAAAAAAA//4AAAAAAAD//gAAAAAAAP/8AAAAAAAA//gAAAAAA + AD/8AAAAAAAAH/gAAAAAAAAfAAAAAAAAAB8AAAAAAAAAH4AAAAAAAAAH8AAAAAAAAAfgAAAAAAAAD+AA + AAAAAAB/4AAAAAAAAH/gAAAAAAAA/+AAAeAAAAD/4A4/+AAAAP/gH///AAAA/+A////gAAB/4H////wA + AB/w/////4AAB///////8AAD///////+AAH////////AAP////////gA/////////wH///////////// + //////////////////////////////////////////////////////////////////////////////// + //8oAAAAMAAAAGAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI1AAArxwAANvEAABnnAAAAkQAA + AAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABTghHYbwYlzf/5mV + 6f+opt//XFtp/wIBAHoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDgXwfnZuPZ6mo + r+bFyvj/vMj0/5Km9P94kPn/a4b+/ys7fMsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFxbwGIhn8qrKmecMjI + x8XFzOf2o7Lz/2eB9/8zVfL/G0Dv/xY77/8YPe//GT7x/yU/8/8nS+1ZAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeHZwCLm5 + uLa5w+/+jaD6/1Nw9v8mSfD/Fzzu/xk+7/8gRO//I0bv/yNG7/8jSPD/JDvr/yYc3/8kNOrrI0jyIwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGJ1z5YeRPf/Fjvv/xtA7/8hRfD/I0bv/yNG7/8jR+//I0fv/yNG7/8jSfD/JTLn/yYb + 3v8mI+H/I0bvUgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAB9C7hUiRe/jI0bv/yNH7/8jR+//I0bv/yNG7/8jRu//I0bv/yNG + 7/8jSfD/JSzl/yYa3v8lJ+P7I0nwKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAABcAAAAXAAAAAQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjRu94I0jv/yNI8P8jR+//I0bv/yNH + 7/8jRu//I0bv/yNH7/8jQu7/JiDh/yYj4f8lJuLWJS3lBAAAAAAAAAAAAAAAAAAAAAAAAAAqBQUbtAwL + QfIHByjyAAAAqAAAABoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACJF7wohQ+4KI0fvAQAAAAAjSvEFIz/swCM/ + 7f8jSPL/I0j0/yNH8f8jR/D/I0fw/yNJ8P8kN+n/JiTi/yUs5f8mIuF3AAAAAAAAAAAAAAAAAAAAAAIC + CDQWE4bqJB3f/ycg7P8lINv/FhR2/wUJHcULF08CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkRvUsIzLnsTU75uZARujgJyvkyiQw + 5qokOOp1JTHofiYn3PsZKLv/GDLM/xw95P8gQ+v/Ikbu/yQ87f8mL+r/JiLi/yYg4MgmHN8JAAAAAAAA + AAAAAAAAAAAADhgVhtQnIef/JiDY/yUf1f8mH9v/Jx7i/yUt4f8jSviwIUn/DwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKtAsKBzsuSoi + 4f4rJOD/Jx7f/yYc3/8lG9//JyDg/m947/50fun/Xmfa/0hU1P8sOsr/HSu8/xgZqf8fHcH/Jh7c2yYm + 5B0AAAAAAAAAACYe4QkmH+ElGhaVlyMexP8lH9b/Jh/g/yYe4f8mHuH/Jh3h/yYk5P8iRfD/I0fzlM/V + 8RPk4t0BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgIISAEB + AqUDAhI+AAAAACce6CklHN+TJh7f7yYe3/8mHt//JR3g/zQw5P9KSej/WVrt/3B28v+AifX/d4Lx/15p + 4/8rLsz/ISvcwyRB7k4jSvEeAAAAACUb25AlG9b9Jx7o/yYd5P8mHeP/Jh/l/yYf4f8mHuD/Jh3f/yYl + 4v8ZPvH/YXz2+UZGRIwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAFBRJLFxWH/BwYqf8GBh/5AAAANQAAAAAAAAAAJiLhDiYf4GgmHd/MKR/e/yki3v8gGN//HRPd/zk2 + 4f99h/L/l6X4/3R88v8pIeP/Hxbf/yYg4P8lI+L6JTTomCM86GUfKb7OHSOy/x4juP8gIbz/IBu9/yUb + 1v8mHN7/Jh/l/x837f9JbP//d3uI/wUEAJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAUGBRspKZHlJB3m/ygf+/8fGr3/AAAAewAAAAAlSvoDI0XvHyRB7UkiOuyQMkXm9ISH + 0P8jHOD/IRfg/39+z/9SUuD/cXjx/3yE8v9qcO7/S0vn/yUb3v8lH+D/JDrq/yNK8v8jSfL+I0ju/yNJ + 8P8fPtP/GSOa/xskrP8dIa//Hia7/xg43P+cq/D/IyEW/3V1dYIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAABkaPogtKc//GBSS/yQb3v8lG+L/FyeQuCNJ9ZkjR/DUI0fw+CNI + 8P8iR/D/H0bw/5ikz/9GWeL/FB3o/3d30f9YU9b/GQ7f/yAW3v9WVun/YGXs/yMn4/8jQe3/I0nw/yNG + 7/8jR+//JEn1/yA50f8fHrn/Iz3m/yNK8/8jR+z/Hz7X/yM4xf95fZn/AAAA/8DAwHcAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhgYYtUeGLb/Gxah/yUl6P8kPO7/JEn3/yNH + 8P8jRu//JEfv/yNH7/8kR+//K07y/4+e2v9ziNn/IUj0/1px4/94g9X/GCzq/yEy6P8dM+n/Gzjs/yFF + 7/8jSPD/H0Pv/yFF7/8iRvH/IUPh/x4dtf8nG+P/JDjs/yNJ8P8jSPD/JTfu/yYq8v8HBzf/FxcM/+/v + 7UoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFg8NVO4hKMz/JDrr/yNH + 7v8jSfD/I0fv/yNG7/8jR/D/Jkvw/zZb8v88X/L/VXX2/4uc5/90h9f/fJPs/0Ji7v+Hl9T/NFrz/z1h + 8v82WvL/NVfx/zdZ8f8hRu//Q2Py/zFU8f81WvX/HjHI/yQZ0/8mKOX/I0jw/yNJ8P8lMef/Ihjd/ysj + zf8XFlT/KShD3bGs/QkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFSqOQxw0 + uP0jSPH/I0ny/yNH7/8jR+//H0Ty/x1C8/8jRO//Ij3s/ytA6v9oefD/Sl7v/4GW5/9oeNr/laPe/zNU + 8f+DkdP/WXXw/zJV8f9QcfP/dpL2/0Vm8v9HavP/W3r0/1Bx8/9QcvP/GiK//yYa3/8kOur/I0fv/yUr + 5P8kGuL/KSS//8HC0v/g4dn/UVTD/hMR4zQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAkSfhnJUr9+SRJ9/8jR+//I0fv/yNI8P8cQfL/NlTg/0xb0/8oJt7/JR3g/ycf3/9KSeX/RkLl/6eo + 6f9ubNX/bW3Y/01O4P9sa9P/W13d/2Bm7f9weO3/Ojvl/ycq4/9OT+f/OTrl/ygp5P8mLd//IB/B/yce + 4v8iLeb/JCLi/yYc3/8fF9//R0aT/8bHu//Ix8L/0dbn/y1N7nMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAB9F8XUfRfD/IETw/yJG8P8jSPD/I0jx/xs37v9SYM3/ra+3/4yJsf8jHN7/Ixzj/ysl + 4f8+POP/JR7g/yoj3f94d9H/NC/c/3Bu0/9TT9f/foDY/yUc4v9KSOX/Jh7f/yUc3/8hGN7/Ixrf/yUc + 4P8lG9n/IRvA/yQc4v84OeX/NDLj/yQb4P8lHuL/CQk3/wYGAP8xMTD/mpmW/1Vz+aYAAAAAAAAAAAAA + AAAAAAAAIhy+TTQyx6ZhZdu6Q0TRsURX5PE8Vej/OFbt/ylJ7f8jPOz/HiXn/1dVxf+5uL//w8XJ/4SG + yP8gGuD/KB/a/yQd5P8fG+v/Ix3n/yEa4f97etH/KiTf/2lo1P9ZV9X/kZPU/3R17f9QTub/JBzf/yYe + 4P8mHt//Jh7g/yYe4P8lHtv/IBq6/yAX4P9XXuz/TlDp/zc05P8wK+r/AgBA/4GCef//////eHdz/zVP + 068AAAAAAAAAAAAAAAAAAAAAPTrOPrO086PCx/nSRkTj9i8q3v84M93/SUnf/zo53f8fFuH/QDrN/7W2 + vv+8vsv/rq+8/459hf9eMU7/XCU7/1cmT/9II33/NCC0/xwU4/9yctn/SEPb/zYw4P93d9P/fHvQ/ywl + 3/8jG+D/Jh7g/yYe4P8mHuD/Jh7f/yYe4P8mHuH/Hxq5/yQb3f8vNOb/MTfl/0xL5/9PUPD/HRih/9TV + 0f+3tq3/OTpA/zxL3vI/O+duJhvfBAAAAAAAAAAAAAAAAAAAAAAAAAAAIjXonR8f4v8hF9//IBff/yMa + 3/8dFeH/dXS//8LEyP+lprP/b0dG/2EfGf9nJx3/aCge/2gpH/9mKyP/ZSsp/00fWP9zYqb/ZmXL/xEJ + zP9kY8v/kJDJ/zcw1P8gFt3/JR3i/yYe4/8mHuL/Jh/g/yYe4P8nH+P/IRzE/yQc1P8jJ+X/HDHp/0xM + 5/+CivP/SUTs/4OEm/8PEB7/M0Sj/CYt6uY8OOT/LCXhMwAAAAAAAAAAAAAAAAAAAAAaEt4PKCji5jw7 + 5f80L+P/MCvi/y0n4v8jHN//hYa+/7/Cyv+IfYL/YSEc/2UoI/9mKCL/ZSgn/zE5u/9DWej/d37a/3x+ + y/9TYc7/L07d/zBI1P9lc9n/h5bb/0xVwv8oLLH/IiKz/xwawf8dF9D/IBjY/yAX3v8kG+P/JR7b/yIb + xP8nIOb/ID7t/zZV8P9+jPj/hYjl/zEzN/8aOc7/JD300iUe4BEjG98ZJh7fAwAAAAAAAAAAAAAAAAAA + AABMR+YoZWHq/Wpr6/9EReb/R0jm/1ha6v9ka/D/io7E/7i6wv+QipH/ZS0o/2AeGf9hIBr/YiEZ/1Ak + VP86L7n/MDPv/ywu7f8mKuj/ICTm/zE66/9FUe//VGPy/2Z49P9ug/P/bYPt/2R55P9TZ97/QlDU/0dM + zv8uMMn/FxW2/xIOg/8eFq3/Jijp/yFG8f8yWP7/ZW2Q/wAAAv8YMtrzHRrpPwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACytPY1pqf1/11Z6f8cE97/Ihrf/yMb3/8qJeT/dXvd/6+wvP+ztcP/mpOc/5aB + if+ReoD/jXZ8/453dPqOg4LcdHOdqi8qy5ojGuLpJh3f/yQb3/8hGN7/Hxjf/yEb3/8kJOL/KzPn/zdF + 7P9KXvL/V3D1/5+y+v+QpPT/XHDh/zlJv/8WHpL/HBWi/x8f2v9CVO//MTMz/wEBAf9rabtTAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABfYek8hIPv/1pX6P8gGN//Jh7g/yMb3/8yLuKRk5/9Lo2U + yU2NjI+cnJ6nxpaao62JjZN3foKFSnR3eSdsbm0IAAAAAAAAAAAiGuYVJh/gXCYf4LsmH+D3JRzg/yAY + 3v8hGN7/IBbe/x8U3v8bEt7/PEDm/3uH8P9HVu3/SmDz/2yE/P8xVfL/HDnN/xgivdlubc+5ERAH/xUV + FPzGxbtVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACVmPJGlpby/1BN5/8hGd//Jh7g/yUd + 4LwoIN8FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAkHeAiMCvhe09P5s1OTeb8TU3m/0xN5v9XWej/cXPr/0tI5f8iGd//Hxrg/yIl4/8lNun/JEPy/x9J + +/hxg8jxBgQA/ycuS53IyMdcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABXXOgyn6Dz/2xq + 6/8fF9//Jh/g1iUe4BcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKF7gFIROU3LijhijEt4t0xLOL/Jh/f/yEZ3/8mHuD/Jh7f/yYd + 3/8mHN//Jh/g/xsj5/+SmuD/MjhK/xk/5fCGmefBTmvuJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAYmDqrjw34/8jHN/TJh/gIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQd4AokHeBDJR7glyYe + 4OEmH+D+Jh7g/yYf4P8lHuD/JBzf/xkP3f+HhOv/XF7d/xIj6v+Bmfj/RGf09R5D75MjR+8TAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAHRbfCiIb3yomH+APAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACYf4AkmH+A4Jh/gjCYe4NMmHt/9Lifh/zIr4f84M+P/S0fo/4KB7P+hpvP/MkDp/yA/ + 7f8jSfDjI0jwSQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8X3wNgYuk3fYDtg1ZU589dX+j/aWvq/2Fh + 6f8zLeL/Ixnf/yYg4P8lMOb/I0Lt/yNJ8JAjR+8QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABoS + 3gUgGN8zHhXeex4W39AjG9/7Jh/f/yYe4P8mHN//JiDg/yQy5/8jRO6NAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACYf4AMmH+AqJh/geCYe4LgmH+DqJh7f3CYb36IkO+srAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA////////h/////////8AAP///////wAA////////AAD///////8AAP// + /////5yA////////AAD///4H///cRv///Af//wU////wB///AAD//wAD///i///+AAH//y8D//8AAf// + /////wAB8P+6Cf//gAHgf/////iAA8A/bwD/4AADgB/////gAAYAB2QA/4gAAgAP/v//BgAAAA+DAP4E + AAAAD+j//gAAAAAPHAD8AAAAAA////wAAAAADwAA/AAAAAAPm+/4AAAAAA8AAPAAAAAADwAsAAAAAAAP + AAAAAAAAAAMAAOAAAAAAAwAAwAAAAAADAADAAAAAAB8AAMAAAAAAPwADwAAwAAA/AAHAf/4AAD8AAMD/ + /4AAHwAB4f//8AAHAAHj///+AAMAAP/////AAAAI//////gAAAD//////wAAAP///////wAA//////// + AAD///////8AAP///////4gH////////AAD///////8EgP///////wAYKAAAACAAAABAAAAAAQAgAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFQAA + HIUAABOYAAAAPQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAUEUSaVVC1/3V0qf8sLDHPAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISC + eBWfnZVYqay1tJOf6v97kvn/Y3/8/0Zj5P8bLXlQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiG + eyqqr8W3i5zn8WJ89P84Wfb/HULy/xg+8P8bQvD/IDLr/yc19d0lTP4VAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAV2i2GTFT9egXPvL/GkDv/yBF8P8jR+//I0fw/yNJ8P8lKeT/JiDh/yNI7zUAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAIUXvXyNJ8P8jSPH/I0fw/yNH8P8jSPD/I0Tu/yYj4v8mJeLsJTTtDgAA + AAAAAAAAAAAAEAUEGo0GBR+uAAAAXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACZO/wUkRO03LEbsUSRB7DYjRu8aIjnquxw34P8dQer/IUfx/yNK9P8lOu7/Jibj/yYi + 4Y4AAAAAAAAAAAAAAAwWE4PNJSDf/yYg2v8YFof/DR97XgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAHS+/Fysm8NE1MOP/KCHg/yMf4fM4OOblX2fb/0pY1v8vRNj/HzDF/yAi + x/8mIuC2AAAAACYh4wEkHNIeFxSCnCYg3P8mIN7/Jh/g/ygf6v8bOPn+Wnn/Ue3s6wUAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAABQcGKJoDAQyTFRB9CyQb4U8mHOCzJhvf+Sce4P88OOj/TUvq/3R8 + 8f96he3/NjjW/x8k4L0kPexvJS/mLyMez8klHdb/JB3W/yYe3v8nHeP/Jh/j/yhG+v9baqfuCAcEIwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWFk+OJR/k/xsUpf8DBxVDAAAAACU67iEgMeqOSlHe+Dkz + 2/8xKNr/aWvg/3mA8/9hZO3/ODDj/yQa3v8kPu3qIkTl5CE+3P8fN8r/HCOs/yAhv/8ZJs7/ZH3p/zo4 + L/eGhocbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQYKIiYjoPodFbT/JSLn/x46ycskSvXWIknw/htD + 8v9MauX/coHZ/y9A5v9patT/GBTh/zxA5/89Sur/IDvs/yFH8P8iR/P/IkTn/yEjx/8jPun/Ikvu/x87 + 4v8xN3//QUAy7P///w8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAQhPHR6o/yM04v8kRvL/I0n2/yJH + 8P8pT/H/Mlfy/1578f+Cldn/WXnw/2yC3f80VvD/K0/w/zBV8f8oTvH/NFjy/zBX8/8gLcr/JSDe/yNH + 8f8kPu7/JSHo/w0KZ/9VU3GnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH0DUBRs1s6UiROb/JEvz/x9G + 8v8fQ+//JT7q/yIw6P9MV+v/a3jv/4SO3f9peOL/YnHd/1Fn6v9WcPP/WW3w/0Zf7/9OZPD/Plfp/yEg + yv8kLun/JDbq/x0V3/9oZMb/6Ond/15k28wTG+YEAAAAAAAAAAAAAAAAAAAAACMz2g0iS/i9IUj6/yNK + 8/8cQfL/OE/f/42Uvf88Ntb/Hxfp/zUy5v82MOP/Z2TY/1ZR1/9lYtX/Y2Db/0pG5/8oIeD/KCDg/yYe + 4f8iG9f/IRvK/y8u5/8tKOP/IBje/yUlSf9mZVv/mqTK/CFL9yIAAAAAAAAAADMuxlpzdN2+Rkjb2j5O + 5v88Uun/ITPp/zg51/+qqcL/vL/G/0k8tP80Ha3/LR/H/xsU5v9VU93/R0Lb/2lo1f95edv/Qj7n/yUe + 4P8lHuD/Jh/h/yUe2f8eF8T/QEHq/0VH6P82M+X/Ly1o/9jXxf9gZ4j/Kj3uRQAAAAAAAAAAa2nhC62x + +DEpMObXIhne/ykg3v8dEt7/iIfF/7i6vf96V1j/aSsf/2goHf9iLTT/Uyhi/2BRrP9FQ9H/VFLM/3Fv + zP8aEdj/Ihrf/yUd4f8mHuH/Jh/h/yMbyP8kJeT/Kjbn/2pu8f9vbd3/Y2Vn/yc2n/s6OOzXKSDgHAAA + AAAAAAAAJx/gDDg45OpCQOb/OTbk/z895P+eocr/nZaZ/14dFv9jHhP/ViU+/zQ/1f9dY+L/TVfW/yg/ + 3f9TY93/eYjd/0xXy/8+Rcv/MDPQ/yYk1v8hGtf/HBXA/yEayP8jPvL/XHf//2Fjj/8LIo3/Hin3dyEX + 3wsmHt8DAAAAAAAAAACKifAliIfw+jYx4/8sJuH/QkHp/4+Tz/6urrb/jHR5/4NgY/9/W1n/eGR/5UpJ + v8AgGuLZJR7i/ycj5P8rK+b/PUPs/0lW7v9NYO7/Y3nu/3mJ5P9BT8P/ICWc/x0cw/80SfD9IiYr/zI2 + fasAAAAAAAAAAAAAAAAAAAAAAAAAAHx+7i58e+7/KyTh/yEZ3/8uKOOQjZfzIoWHlleNkJR8g4eLWnV5 + eydydGsIAAAAACcg3wQlHuFMJR7gqygh4O8uJ+H/MCrh/zEs4v9gZOv/T1jt/0ZV8v87WPL/GDbX+0pT + xNAKCAD4mJWIawAAAAAAAAAAAAAAAAAAAAAAAAAAbHHrKYiH8P8tJuH/Ixzfuigh4QQAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUVDmFGFh6Wc/PeS+REPl+Tk04/8jGt//Ihnf/yMe + 4P8fKO3/Xm7W/yg1ZPNwhNyiW3XoFAAAAAAAAAAAAAAAAAAAAAA6OOMCSkXmlScg4KgkHuAOAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiGt8jIxzgdSYf + 4L8mIOD1Ixvf/xsQ3f9PSOT/TlHp/26B9/85WvLpH0bwbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACIa3xhEQeRlXl3ouUdF5fNoaOv/X1zo/yUh4f8kNOj/I0XvtiNI8CMAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHhbfFyEZ32MgGN+1Jh7g7iYc3/8mIuH/JD3rkgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYf4BQmHuBNJh/gjyYb + 3oMkNOgqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////////////////4f///+ + D///8A///8AH///AB///4AYf/gAMD/4AEAP4AAAD+EAAA/AAAAPwAAAH4AAAA8AAAAMAAAADAAAAAYAA + AAGAAAAPgAgAD4P/AAeH/+AD///8AP///4D////g/////////////////////ygAAAAYAAAAMAAAAAEA + IAAAAAAAYAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkI + KwwFBR5yAwMOWwYGCQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAk5ScHnR0psSFi+f+anbD/iIsYUwAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdHV9Arm9 + 0HWUod3LbYPs/TJT7v4iRu/+I0Pu/iQ048kfONQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADVU6KkjRu//I0bv/yNG7/8jRu/+Iznq/yUi + 4f4hN9sVAAAAAAAAAAABAQoBAQEGAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACM+6yQjQ+72I0bv/yNG7/8jRu/+JC7m/yUm4tAAAAAAAAAAABAOXlEWEoboDw5Y4goP + PTMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwjsxcrK+HNLSzi5yUk4sU6P+TeP07U/ig/ + 3P4fMc3+JCPc+CUl4TwAAAAAGxeeMSEcxPYlH9n+JR7e/iM35udEYOUqAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAEA9ZYgwLR60XFIsVJSDgbCUf4NkoIeD/Ojfk/2px7v9wevD+Jyja6yUp45UkLOFsISHK8yIe + yf4kHdj/JR7f/ypB6/5KU3u/AAAAAAAAAAAAAAAAAAAAAAAAAAAcHVgrIx+y+SMc2/4VJoxwI0LteyNB + 7bVdbdz8KSzi/2Ni1v9RUuj+U1Xp/yUq5P4jRO7/I0bv/yA31/4eMsb/HzLM/1RlyP4xMTG5AAAAAAAA + AAAAAAAAAAAAAAAAAAATFVl/HyTA/yM77f4jRu//I0fv/zJV8P5kfOb/ZHzl/1903v8wSe3+KEbt/ylM + 7/4uUfD/JUPh/yMg1f4jQ+7/JDLn/xkZiv5GRVmFAAAAAAAAAAAAAAAAAAAAACFC4hoePM/OIkbv/iJG + 7/4uS+T+JDHn/kJL6P5ocej+cXrZ/mNu3P5EV+v+Wmvu/kBS7P47TOv+LDfW/iUp5P4lLOX+MCvD/tHR + 1/5qc9KgAAAAAAAAAABAPskvTFLYUjNQ69slR+3/Iz7r/2Bq0P6YmL3/JR7g/y4p4f4mH9//WFXW/19d + 1f9nZt7+OTXi/yUe3/4lHt//IxzO/zU05P41MuP/GBSK/2tra/5mc7HQAAAAAAAAAAB8e+AzXWDonSwo + 3/40MN/+Qj3T/rCxv/6DZWn+YCk1/lkmSf5EI4f+XVfJ/lFO1v5XVNf+JR7f/iUe3/4lHt/+IxzN/igm + 4f48Quf+SUnT/o2Olv40Pa3zPDzjYAAAAAAAAAAASUjmiUNC5f48OeT/a2zV/6Ccpv5lKCT/ZSgk/z87 + sf5PVeH/MT/e/0JP2/9gbtv+SlTS/z5D1v4uMNf/JCDK/x8auv4oQez/ZXPU/xgoffsmN99EJiPHAwAA + AAAAAAAAi4rwnT864/4lHt//U1PfsqGitr+XkJndjoCGs4l+iIJSTr9ZJh/enCUe3/AlHt/+JSTh/zA2 + 5v5qd/D/W2/u/zdKy/4fKMT3OD147jU2TKEAAAAAAAAAAAAAAAAAAAAAjo7woEA65P4lHt+pNC/QAQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACUf2ApAPeNZSEfltEE/5PpBPuT+JR7f/iUn4/4kNej+RU14/Vlp + sqJjedgFAAAAAAAAAAAAAAAAZWPpNC0n4IIkHdsHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACch3RUlHt9mJR7ftSUe3/clHd//UE3i/2t47/4vTO7dI0XuSAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAExK + 4RBhYehcUE/msDUw4vUlH+D/JC3l/SM964AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQd2Q0lHt9QJR7feCQm + 4i8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP// + /wD/4f8A/8H/AP4A/wD/AM8A/wGHAPgBAwDwAAMA4AADAOAAAwDAAAMAAAADAAAAAQCAAAEAgAAHAIfg + AwCP/AEA//+AAP//8AD///8A////AP///wAoAAAAEAAAACAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMi81LDIxVJ4ODgw5AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACFhok9goy7m2B14O9Laf//Kj7KyB88 + vQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOFjkVh5G+f8aQfT/HkHw/yUo + 7PcjPOwUBg4uAgMDEUsAAAAVAAAAAAAAAAAAAAAAAAAAAAAAAAATJ44GKTf4jiIt6ZoyQeLoMk7j/yQ3 + 3f8lJueFIRi9BBYQfaAlHNj/FR+30Zmn2RwAAAAAAAAAAAAAAAAHBhwvEQpfvxkbmkAmKu2oNjPg/lpZ + 4/9eX+T/JirjviIy5bMjLNb/IB7Q/zNC5f9UW2mDAAAAAAAAAAAAAAAAExNbniMv6v8cQubiK1P072B4 + 5P9YaOD/O0zq/zJP7/8qUvH/IjTZ/yE87f8lLab/npusZAAAAAAjHMELIjrRQB5D4OouUe7/QlTk/ywx + 8P9gZeb/Z27b/1Ng6P88Rur/Mjjh/ygp3v8hIdv/dXGV/1Zm228AAAAAXl3cWDpB5+woL+j/hofF/4Bg + c/8/GXj/Rje7/11d1P9STtj/HhTd/x4T2f8nI9v/QUTm/21ulP8/SsK6IiXpCWVj6hNJRufxPDnl/5iV + tf57Sjv/Zkts70RFzOI0OuH/Rk3e/zxF3v9IT9//KCzD/zVG1v04QX3jOzfbLycc3wN+g+4jV1Po/ygi + 6JF9g7slenx3NXZ2ZxFDPq0FHxXnPz045Zs9O+XoR0jo/yw05/8sOcD9W2J+twAAAAAAAAAALirhBi0m + 4UcsJuoDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJyDgDSEZ304rI+GfRUDk5lle7f8oP+3cIUbvRgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAERC5QsiGt9IJRrenCUu + 5YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP//AAP//wAD/j/nFvgf5xb4A4AU4AEAAMABAADAAQAAAAEAAAAAAAAAAAAAAAMAAB+A + AAP/8HMf//8AAP//AAQ= + + + \ No newline at end of file diff --git a/AirScout/CrossingHistoryDlg.Designer.cs b/AirScout/CrossingHistoryDlg.Designer.cs new file mode 100644 index 0000000..eec794f --- /dev/null +++ b/AirScout/CrossingHistoryDlg.Designer.cs @@ -0,0 +1,232 @@ +namespace AirScout +{ + partial class CrossingHistoryDlg + { + /// + /// Erforderliche Designervariable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Verwendete Ressourcen bereinigen. + /// + /// True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Vom Windows Form-Designer generierter Code + + /// + /// Erforderliche Methode für die Designerunterstützung. + /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + System.Windows.Forms.DataVisualization.Charting.ChartArea chartArea1 = new System.Windows.Forms.DataVisualization.Charting.ChartArea(); + System.Windows.Forms.DataVisualization.Charting.Legend legend1 = new System.Windows.Forms.DataVisualization.Charting.Legend(); + System.Windows.Forms.DataVisualization.Charting.Series series1 = new System.Windows.Forms.DataVisualization.Charting.Series(); + this.bw_History = new System.ComponentModel.BackgroundWorker(); + this.ss_Main = new System.Windows.Forms.StatusStrip(); + this.tsl_Main = new System.Windows.Forms.ToolStripStatusLabel(); + this.panel1 = new System.Windows.Forms.Panel(); + this.btn_History_Export = new System.Windows.Forms.Button(); + this.btn_History_Cancel = new System.Windows.Forms.Button(); + this.btn_History_Calculate = new System.Windows.Forms.Button(); + this.tt_Crossing_History = new System.Windows.Forms.ToolTip(this.components); + this.ch_Crossing_History = new System.Windows.Forms.DataVisualization.Charting.Chart(); + this.cb_Analysis_CrossingHistory_WithSignlaLevel = new System.Windows.Forms.CheckBox(); + this.ud_Analysis_AmbiguousGap = new System.Windows.Forms.NumericUpDown(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.ss_Main.SuspendLayout(); + this.panel1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.ch_Crossing_History)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.ud_Analysis_AmbiguousGap)).BeginInit(); + this.SuspendLayout(); + // + // bw_History + // + this.bw_History.WorkerReportsProgress = true; + this.bw_History.WorkerSupportsCancellation = true; + this.bw_History.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bw_History_DoWork); + this.bw_History.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.bw_History_ProgressChanged); + this.bw_History.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.bw_History_RunWorkerCompleted); + // + // ss_Main + // + this.ss_Main.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.tsl_Main}); + this.ss_Main.Location = new System.Drawing.Point(0, 516); + this.ss_Main.Name = "ss_Main"; + this.ss_Main.Size = new System.Drawing.Size(893, 22); + this.ss_Main.TabIndex = 1; + this.ss_Main.Text = "statusStrip1"; + // + // tsl_Main + // + this.tsl_Main.Name = "tsl_Main"; + this.tsl_Main.Size = new System.Drawing.Size(135, 17); + this.tsl_Main.Text = "Press Calculate to start..."; + // + // panel1 + // + this.panel1.Controls.Add(this.label2); + this.panel1.Controls.Add(this.label1); + this.panel1.Controls.Add(this.ud_Analysis_AmbiguousGap); + this.panel1.Controls.Add(this.cb_Analysis_CrossingHistory_WithSignlaLevel); + this.panel1.Controls.Add(this.btn_History_Export); + this.panel1.Controls.Add(this.btn_History_Cancel); + this.panel1.Controls.Add(this.btn_History_Calculate); + this.panel1.Dock = System.Windows.Forms.DockStyle.Bottom; + this.panel1.Location = new System.Drawing.Point(0, 416); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(893, 100); + this.panel1.TabIndex = 2; + // + // btn_History_Export + // + this.btn_History_Export.Location = new System.Drawing.Point(697, 41); + this.btn_History_Export.Name = "btn_History_Export"; + this.btn_History_Export.Size = new System.Drawing.Size(92, 23); + this.btn_History_Export.TabIndex = 2; + this.btn_History_Export.Text = "Export to CSV"; + this.btn_History_Export.UseVisualStyleBackColor = true; + this.btn_History_Export.Click += new System.EventHandler(this.btn_History_Export_Click); + // + // btn_History_Cancel + // + this.btn_History_Cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btn_History_Cancel.Location = new System.Drawing.Point(795, 41); + this.btn_History_Cancel.Name = "btn_History_Cancel"; + this.btn_History_Cancel.Size = new System.Drawing.Size(75, 23); + this.btn_History_Cancel.TabIndex = 1; + this.btn_History_Cancel.Text = "Back"; + this.btn_History_Cancel.UseVisualStyleBackColor = true; + this.btn_History_Cancel.Click += new System.EventHandler(this.btn_History_Cancel_Click); + // + // btn_History_Calculate + // + this.btn_History_Calculate.Location = new System.Drawing.Point(616, 41); + this.btn_History_Calculate.Name = "btn_History_Calculate"; + this.btn_History_Calculate.Size = new System.Drawing.Size(75, 23); + this.btn_History_Calculate.TabIndex = 0; + this.btn_History_Calculate.Text = "Calculate"; + this.btn_History_Calculate.UseVisualStyleBackColor = true; + this.btn_History_Calculate.Click += new System.EventHandler(this.btn_History_Calculate_Click); + // + // tt_Crossing_History + // + this.tt_Crossing_History.IsBalloon = true; + this.tt_Crossing_History.OwnerDraw = true; + // + // ch_Crossing_History + // + chartArea1.AxisX.IsLabelAutoFit = false; + chartArea1.AxisX.LabelAutoFitMaxFontSize = 6; + chartArea1.AxisX.LabelStyle.Angle = -90; + chartArea1.AxisX.LabelStyle.Font = new System.Drawing.Font("Microsoft Sans Serif", 6F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + chartArea1.AxisY.LabelAutoFitMaxFontSize = 6; + chartArea1.AxisY2.Enabled = System.Windows.Forms.DataVisualization.Charting.AxisEnabled.True; + chartArea1.AxisY2.LabelAutoFitMaxFontSize = 6; + chartArea1.Name = "ChartArea1"; + this.ch_Crossing_History.ChartAreas.Add(chartArea1); + this.ch_Crossing_History.Dock = System.Windows.Forms.DockStyle.Fill; + legend1.Name = "Legend1"; + this.ch_Crossing_History.Legends.Add(legend1); + this.ch_Crossing_History.Location = new System.Drawing.Point(0, 0); + this.ch_Crossing_History.Name = "ch_Crossing_History"; + series1.ChartArea = "ChartArea1"; + series1.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + series1.Legend = "Legend1"; + series1.Name = "Series1"; + this.ch_Crossing_History.Series.Add(series1); + this.ch_Crossing_History.Size = new System.Drawing.Size(893, 416); + this.ch_Crossing_History.TabIndex = 5; + this.ch_Crossing_History.Text = "History"; + // + // cb_Analysis_CrossingHistory_WithSignlaLevel + // + this.cb_Analysis_CrossingHistory_WithSignlaLevel.AutoSize = true; + this.cb_Analysis_CrossingHistory_WithSignlaLevel.Checked = global::AirScout.Properties.Settings.Default.Analysis_CrossingHistory_WithSignalLevel; + this.cb_Analysis_CrossingHistory_WithSignlaLevel.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Analysis_CrossingHistory_WithSignalLevel", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Analysis_CrossingHistory_WithSignlaLevel.Location = new System.Drawing.Point(12, 21); + this.cb_Analysis_CrossingHistory_WithSignlaLevel.Name = "cb_Analysis_CrossingHistory_WithSignlaLevel"; + this.cb_Analysis_CrossingHistory_WithSignlaLevel.Size = new System.Drawing.Size(258, 17); + this.cb_Analysis_CrossingHistory_WithSignlaLevel.TabIndex = 3; + this.cb_Analysis_CrossingHistory_WithSignlaLevel.Text = "Consider crossings with recorded signal level only"; + this.cb_Analysis_CrossingHistory_WithSignlaLevel.UseVisualStyleBackColor = true; + // + // ud_Analysis_AmbiguousGap + // + this.ud_Analysis_AmbiguousGap.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScout.Properties.Settings.Default, "Analysis_CrossingHistory_AmbiguousGap", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.ud_Analysis_AmbiguousGap.Location = new System.Drawing.Point(318, 49); + this.ud_Analysis_AmbiguousGap.Name = "ud_Analysis_AmbiguousGap"; + this.ud_Analysis_AmbiguousGap.Size = new System.Drawing.Size(60, 20); + this.ud_Analysis_AmbiguousGap.TabIndex = 4; + this.ud_Analysis_AmbiguousGap.Value = global::AirScout.Properties.Settings.Default.Analysis_CrossingHistory_AmbiguousGap; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(9, 51); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(276, 13); + this.label1.TabIndex = 5; + this.label1.Text = "Timespan within two crossings considered as ambiguous:"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(393, 51); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(32, 13); + this.label2.TabIndex = 6; + this.label2.Text = "secs."; + // + // CrossingHistoryDlg + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btn_History_Cancel; + this.ClientSize = new System.Drawing.Size(893, 538); + this.Controls.Add(this.ch_Crossing_History); + this.Controls.Add(this.panel1); + this.Controls.Add(this.ss_Main); + this.Name = "CrossingHistoryDlg"; + this.Text = "CrossingHistoryDlg"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.CrossingHistoryDlg_FormClosing); + this.ss_Main.ResumeLayout(false); + this.ss_Main.PerformLayout(); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.ch_Crossing_History)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.ud_Analysis_AmbiguousGap)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.ComponentModel.BackgroundWorker bw_History; + private System.Windows.Forms.StatusStrip ss_Main; + private System.Windows.Forms.ToolStripStatusLabel tsl_Main; + private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.Button btn_History_Cancel; + private System.Windows.Forms.Button btn_History_Calculate; + private System.Windows.Forms.Button btn_History_Export; + private System.Windows.Forms.ToolTip tt_Crossing_History; + private System.Windows.Forms.DataVisualization.Charting.Chart ch_Crossing_History; + private System.Windows.Forms.CheckBox cb_Analysis_CrossingHistory_WithSignlaLevel; + private System.Windows.Forms.NumericUpDown ud_Analysis_AmbiguousGap; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label1; + } +} \ No newline at end of file diff --git a/AirScout/CrossingHistoryDlg.cs b/AirScout/CrossingHistoryDlg.cs new file mode 100644 index 0000000..bc24f14 --- /dev/null +++ b/AirScout/CrossingHistoryDlg.cs @@ -0,0 +1,366 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using System.Globalization; +using System.IO; +using AirScout.Core; +using AirScout.Aircrafts; +using ScoutBase.Core; +using ScoutBase.Stations; +using ScoutBase.Elevation; +using ScoutBase.Propagation; +using AirScout.AircraftPositions; +using AirScout.Signals; + +namespace AirScout +{ + public partial class CrossingHistoryDlg : Form + { + + DateTime From; + DateTime To; + int Stepwidth; + List AllPositions; + List NearestPositions = new List(); + + PropagationPathDesignator PPath; + + const int TOOLTIP_XOFFSET =3; + const int TOOLTIP_YOFFSET = 3; + + List Crossings = new List(); + + public CrossingHistoryDlg(DateTime from, DateTime to, int stepwidth, ref List alllpositions) + { + InitializeComponent(); + From = new DateTime(from.Year, from.Month, from.Day, 0, 0, 0, 0, from.Kind); + DateTime oldest = AircraftPositionData.Database.AircraftPositionOldestEntry(); + if (From < oldest) + From = oldest; + To = to; + // check bounds + if (From > To) + From = To; + Stepwidth = stepwidth; + AllPositions = alllpositions; + // set minimum stepwidth to 1sec + if (Stepwidth <= 0) + Stepwidth = 1; + this.Text = "Path Crossing History: " + Properties.Settings.Default.MyCall + " >>> " + Properties.Settings.Default.DXCall; + btn_History_Export.Enabled = false; + + } + + private void bw_History_DoWork(object sender, DoWorkEventArgs e) + { + // check that Stepwidth ist positive in any case + if (Properties.Settings.Default.Path_History_StepWidth <= 0) + Properties.Settings.Default.Path_History_StepWidth = 1; + bw_History.ReportProgress(0, "Calculating Path...."); + LocationDesignator mycall = StationData.Database.LocationFindOrCreate(Properties.Settings.Default.MyCall, MaidenheadLocator.LocFromLatLon(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, false, 3)); + QRVDesignator myqrv = StationData.Database.QRVFindOrCreateDefault(mycall.Call, mycall.Loc, Properties.Settings.Default.Band); + // set qrv defaults if zero + if (myqrv.AntennaHeight == 0) + myqrv.AntennaHeight = StationData.Database.QRVGetDefaultAntennaHeight(Properties.Settings.Default.Band); + if (myqrv.AntennaGain == 0) + myqrv.AntennaGain = StationData.Database.QRVGetDefaultAntennaGain(Properties.Settings.Default.Band); + if (myqrv.Power == 0) + myqrv.Power = StationData.Database.QRVGetDefaultPower(Properties.Settings.Default.Band); + if (Properties.Settings.Default.Path_BestCaseElevation) + { + if (!MaidenheadLocator.IsPrecise(mycall.Lat, mycall.Lon, 3)) + { + ElvMinMaxInfo maxinfo = ElevationData.Database.ElevationTileFindMinMaxInfo(mycall.Loc, Properties.Settings.Default.ElevationModel); + if (maxinfo != null) + { + mycall.Lat = maxinfo.MaxLat; + mycall.Lon = maxinfo.MaxLon; + } + } + } + LocationDesignator dxcall = StationData.Database.LocationFindOrCreate(Properties.Settings.Default.DXCall, MaidenheadLocator.LocFromLatLon(Properties.Settings.Default.DXLat, Properties.Settings.Default.DXLon, false, 3)); + QRVDesignator dxqrv = StationData.Database.QRVFindOrCreateDefault(dxcall.Call, dxcall.Loc, Properties.Settings.Default.Band); + // set qrv defaults if zero + if (dxqrv.AntennaHeight == 0) + dxqrv.AntennaHeight = StationData.Database.QRVGetDefaultAntennaHeight(Properties.Settings.Default.Band); + if (dxqrv.AntennaGain == 0) + dxqrv.AntennaGain = StationData.Database.QRVGetDefaultAntennaGain(Properties.Settings.Default.Band); + if (dxqrv.Power == 0) + dxqrv.Power = StationData.Database.QRVGetDefaultPower(Properties.Settings.Default.Band); + if (Properties.Settings.Default.Path_BestCaseElevation) + { + if (!MaidenheadLocator.IsPrecise(dxcall.Lat, dxcall.Lon, 3)) + { + ElvMinMaxInfo maxinfo = ElevationData.Database.ElevationTileFindMinMaxInfo(dxcall.Loc, Properties.Settings.Default.ElevationModel); + if (maxinfo != null) + { + dxcall.Lat = maxinfo.MaxLat; + dxcall.Lon = maxinfo.MaxLon; + } + } + } + + // find local obstruction, if any + LocalObstructionDesignator o = ElevationData.Database.LocalObstructionFind(mycall.Lat, mycall.Lon, Properties.Settings.Default.ElevationModel); + double mybearing = LatLon.Bearing(mycall.Lat, mycall.Lon, dxcall.Lat, dxcall.Lon); + double myobstr = (o != null) ? o.GetObstruction(myqrv.AntennaHeight, mybearing) : double.MinValue; + + // try to find propagation path in database or create new one and store + PPath = PropagationData.Database.PropagationPathFindOrCreateFromLatLon( + bw_History, + mycall.Lat, + mycall.Lon, + ElevationData.Database[mycall.Lat, mycall.Lon,Properties.Settings.Default.ElevationModel] + myqrv.AntennaHeight, + dxcall.Lat, + dxcall.Lon, + ElevationData.Database[dxcall.Lat, dxcall.Lon,Properties.Settings.Default.ElevationModel] + dxqrv.AntennaHeight, + Bands.ToGHz(Properties.Settings.Default.Band), + LatLon.Earth.Radius * Properties.Settings.Default.Path_Band_Settings[Properties.Settings.Default.Band].K_Factor, + Properties.Settings.Default.Path_Band_Settings[Properties.Settings.Default.Band].F1_Clearance, + ElevationData.Database.GetDefaultStepWidth(Properties.Settings.Default.ElevationModel), + Properties.Settings.Default.ElevationModel, + myobstr); + DateTime time = From; + lock (Crossings) + { + Crossings.Clear(); + } + lock(NearestPositions) + { + NearestPositions.Clear(); + } + // pre-select nearest positions only + bw_History.ReportProgress(0, "Pre-selecting nearest positions..."); + LatLon.GPoint midpoint = PPath.GetMidPoint(); + double maxdist = PPath.Distance / 2; + foreach (AircraftPositionDesignator ap in AllPositions) + { + if ((ap.LastUpdated >= From) && (ap.LastUpdated <= To) && (LatLon.Distance(ap.Lat, ap.Lon, midpoint.Lat, midpoint.Lon) <= maxdist)) + { + AircraftDesignator ac = null; + AircraftTypeDesignator at = null; + ac = AircraftData.Database.AircraftFindByHex(ap.Hex); + if (ac != null) + at = AircraftData.Database.AircraftTypeFindByICAO(ac.TypeCode); + PlaneInfo plane = new PlaneInfo(ap.LastUpdated, ap.Call, ((ac != null) && (!String.IsNullOrEmpty(ac.TypeCode)))? ac.Reg : "[unknown]", ap.Hex, ap.Lat, ap.Lon, ap.Track, ap.Alt, ap.Speed, (ac != null) && (!String.IsNullOrEmpty(ac.TypeCode)) ? ac.TypeCode : "[unkomwn]", ((at != null) && (!String.IsNullOrEmpty(at.Manufacturer))) ? at.Manufacturer : "[unknown]", ((at != null) && (!String.IsNullOrEmpty(at.Model))) ? at.Model : "[unknown]", (at != null) ? at.Category : PLANECATEGORY.NONE); + lock (NearestPositions) + { + NearestPositions.Add(plane); + } + if (NearestPositions.Count % 1000 == 0) + bw_History.ReportProgress(0, "Pre-selecting nearest positions..." + "[" + NearestPositions.Count.ToString() + "]"); + } + if (bw_History.CancellationPending) + break; + } + bw_History.ReportProgress(0, "Pre-selecting nearest positions finished, " + NearestPositions.Count.ToString() + " positions."); + // return if no positions left over + if (NearestPositions.Count == 0) + return; + int startindex = 0; + // set timeline to first reported position + time = NearestPositions[0].Time; + while ((!bw_History.CancellationPending) && (time <= To)) + { + if (Crossings.Count % 1000 == 0) + bw_History.ReportProgress(0, "Calculating at " + time.ToString("yyyy-MM-dd HH:mm:ss") + ", " + Crossings.Count.ToString() + " crossings so far."); + // calculate from timestamp + DateTime from = time.AddMinutes(-Properties.Settings.Default.Planes_Position_TTL); + // fill plane position cache + PlaneInfoCache ac = new PlaneInfoCache(); + int i = startindex; + startindex = -1; + while ((!bw_History.CancellationPending) && (i < NearestPositions.Count)) + { + // update ap in cache if relevant + if (NearestPositions[i].Time >= from) + { + // store first index as startindex for next iteration + if (startindex == -1) + startindex = i; + lock (ac) + { + ac.InsertOrUpdateIfNewer(NearestPositions[i]); + } + } + // stop if position is newer than current time + if (NearestPositions[i].Time > time) + break; + i++; + } + List allplanes = ac.GetAll(time, Properties.Settings.Default.Planes_Position_TTL); + // get nearest planes + List nearestplanes = AircraftData.Database.GetNearestPlanes(time, PPath, allplanes, Properties.Settings.Default.Planes_Filter_Max_Circumcircle, Properties.Settings.Default.Path_Band_Settings[Properties.Settings.Default.Band].MaxDistance, Properties.Settings.Default.Planes_MaxAlt); + if ((nearestplanes != null) && (nearestplanes.Count() > 0)) + { + // get all planes crossing the path + foreach (PlaneInfo plane in nearestplanes) + { + if (plane.IntQRB <= Properties.Settings.Default.Path_Band_Settings[Properties.Settings.Default.Band].MaxDistance) + { + // check if level value is available + SignalLevelDesignator ad = SignalData.Database.SignalLevelFind(plane.Time); + if (ad != null) + plane.SignalStrength = ad.Level; + else + plane.SignalStrength = double.MinValue; + lock (Crossings) + { + if (!Properties.Settings.Default.Analysis_CrossingHistory_WithSignalLevel || (ad != null)) + Crossings.Add(plane); + } + } + bw_History.ReportProgress(0, "Calculating at " + time.ToString("yyyy-MM-dd HH:mm:ss") + ", " + Crossings.Count.ToString() + " crossings so far."); + } + } + time = time.AddSeconds(Stepwidth); + } + bw_History.ReportProgress(100, "Calculation done."); + } + + private void bw_History_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + if (e.ProgressPercentage == 0) + { + tsl_Main.Text = (string)e.UserState; +// ss_Main.Refresh(); + } + else if (e.ProgressPercentage == 100) + { + tsl_Main.Text = (string)e.UserState; + // this.Refresh(); + ch_Crossing_History.Show(); + } + } + + private void bw_History_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + if (!bw_History.CancellationPending) + btn_History_Export.Enabled = true; + } + + private void btn_History_Calculate_Click(object sender, EventArgs e) + { + btn_History_Calculate.Enabled = false; + // drwaing charts + ch_Crossing_History.Series.Clear(); + ch_Crossing_History.Series.Add("Count_Lo"); + ch_Crossing_History.Series["Count_Lo"].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.StackedColumn; + ch_Crossing_History.Series["Count_Lo"].Color = Color.Blue; + ch_Crossing_History.Series["Count_Lo"].BorderWidth = 2; + ch_Crossing_History.Series["Count_Lo"].BackSecondaryColor = Color.Blue; + ch_Crossing_History.Series["Count_Lo"].XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.DateTime; + ch_Crossing_History.Series["Count_Lo"]["PointWidth"] = "1"; + ch_Crossing_History.Series.Add("Count_Hi"); + ch_Crossing_History.Series["Count_Hi"].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.StackedColumn; + ch_Crossing_History.Series["Count_Hi"].Color = Color.Red; + ch_Crossing_History.Series["Count_Hi"].BorderWidth = 2; + ch_Crossing_History.Series["Count_Hi"].BackSecondaryColor = Color.Red; + ch_Crossing_History.Series["Count_Hi"].XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.DateTime; + ch_Crossing_History.Series["Count_Hi"]["PointWidth"] = "1"; + + ch_Crossing_History.ChartAreas[0].AxisX.LabelStyle.Format = "yyyy-MM-dd\nHH:mm:ss"; + bw_History.RunWorkerAsync(); + } + + private void btn_History_Export_Click(object sender, EventArgs e) + { + if (PPath == null) + return; + if (Crossings == null) + return; + if (Crossings.Count() <= 0) + return; + tsl_Main.Text = "Checking ambiguous crossings..."; + int start = 0; + Dictionary p = new Dictionary(); + for (int i = 1; i < Crossings.Count; i++) + { +// if ((Crossings[i].Call == "DLH2LJ") && (Crossings[i].Time.Day == 26)) +// Console.WriteLine(""); + Crossings[i].Ambiguous = false; + PlaneInfo pl = null; + if (!p.TryGetValue(Crossings[i].Hex, out pl)) + p.Add(Crossings[i].Hex, Crossings[i]); + if (((Crossings[i].Time - Crossings[i - 1].Time).TotalSeconds > (double)Properties.Settings.Default.Analysis_CrossingHistory_AmbiguousGap) || (i >= Crossings.Count - 1)) + { + // gap detected + if (p.Count > 1) + { + // multiple planes detected --> mark all as ambiguous + for (int j = start; j <= i - 1; j++) + Crossings[j].Ambiguous = true; + } + // reset all and start new crossing + p.Clear(); + start = i; + } + } + // mark last plane as ambiguous in any case + Crossings[Crossings.Count - 1].Ambiguous = true; + CultureInfo uiculture = CultureInfo.CurrentUICulture; + SaveFileDialog Dlg = new SaveFileDialog(); + Dlg.AddExtension = true; + Dlg.DefaultExt = "csv"; + Dlg.FileName = "Path Crossing History_" + Properties.Settings.Default.MyCall + "_" + Properties.Settings.Default.DXCall + "_" + From.ToString("yyyyMMdd_HHmmss") + "_to_" + To.ToString("yyyyMMdd_HHmmss"); +// Dlg.InitialDirectory = Application.StartupPath; + Dlg.OverwritePrompt = true; + if (Dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) + { + using (StreamWriter sw = new StreamWriter(Dlg.FileName)) + { + sw.WriteLine("time[utc];call;lat[deg];lon[deg];type;category;potential;track[deg];alt[m];altdiff[m];dist[km];crossangle[deg];squint[deg];dist1[km];dist2[km];eps1[deg];eps2[deg];theta1[deg];theta2[deg];signal_strength[dB];ambiguous"); + foreach (PlaneInfo plane in Crossings) + { + double a1 = LatLon.Distance(PPath.Lat1, PPath.Lon1, plane.Lat, plane.Lon); + double a2 = LatLon.Distance(PPath.Lat2, PPath.Lon2, plane.Lat, plane.Lon); + double sl = plane.SignalStrength; + if (sl == double.MinValue) + sl = -255; + sw.WriteLine(plane.Time.ToString("yyyy-MM-dd HH:mm:ss") + ";" + + plane.Call + ";" + + plane.Lat.ToString("F8", uiculture) + ";" + + plane.Lon.ToString("F8", uiculture) + ";" + + plane.Type + ";" + + plane.Category.ToString() + ";" + + plane.Potential.ToString() + ";" + + plane.Track.ToString("F8", uiculture) + ";" + + plane.Alt_m.ToString("F8", uiculture) + ";" + + plane.AltDiff.ToString("F8", uiculture) + ";" + + plane.IntQRB.ToString("F8", uiculture) + ";" + + (plane.Angle / Math.PI * 180.0).ToString("F8", uiculture) + ";" + + (plane.Squint / Math.PI * 180.0).ToString("F8", uiculture) + ";" + + a1.ToString("F8", uiculture) + ";" + + a2.ToString("F8", uiculture) + ";" + + (plane.Eps1 / Math.PI * 180.0).ToString("F8", uiculture) + ";" + + (plane.Eps2 / Math.PI * 180.0).ToString("F8", uiculture) + ";" + + (plane.Theta1 / Math.PI * 180.0).ToString("F8", uiculture) + ";" + + (plane.Theta2 / Math.PI * 180.0).ToString("F8", uiculture) + ";" + + sl.ToString("F2", uiculture) + ";" + + plane.Ambiguous.ToString()); + } + } + tsl_Main.Text = "Export done."; + } + } + + private void CrossingHistoryDlg_FormClosing(object sender, FormClosingEventArgs e) + { + bw_History.CancelAsync(); + } + + private void btn_History_Cancel_Click(object sender, EventArgs e) + { + this.Close(); + } + + private void ch_Crossing_History_MouseMove(object sender, MouseEventArgs e) + { + } + } +} diff --git a/AirScout/CrossingHistoryDlg.resx b/AirScout/CrossingHistoryDlg.resx new file mode 100644 index 0000000..8055f99 --- /dev/null +++ b/AirScout/CrossingHistoryDlg.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 129, 17 + + + 252, 12 + + \ No newline at end of file diff --git a/AirScout/DatabaseStatus.cs b/AirScout/DatabaseStatus.cs new file mode 100644 index 0000000..f149260 --- /dev/null +++ b/AirScout/DatabaseStatus.cs @@ -0,0 +1,66 @@ +using System; +using System.Data.SQLite; +using System.Drawing; + +namespace AirScout +{ + + public class DatabaseStatus + { + public static Color GetDatabaseStatusColor(DATABASESTATUS status) + { + Color color = Color.Plum; + if ((status & DATABASESTATUS.ERROR) > 0) + color = Color.Red; + else if ((status & DATABASESTATUS.UPTODATE) > 0) + color = Color.Green; + else if ((status & DATABASESTATUS.COMPLETE) > 0) + color = Color.Blue; + else if ((status & DATABASESTATUS.UPDATING) > 0) + color = Color.Gold; + else if ((status & DATABASESTATUS.EMPTY) > 0) + color = Color.Black; + return color; + } + + public static string GetDatabaseStatusText(DATABASESTATUS status) + { + string s = ""; + if ((status & DATABASESTATUS.UNDEFINED) > 0) + s = "Database status is unknown."; + if ((status & DATABASESTATUS.ERROR) > 0) + { + if (s.Length > 0) + s = s + "\n"; + s = s + "Database has errors."; + } + if ((status & DATABASESTATUS.UPTODATE) > 0) + { + if (s.Length > 0) + s = s + "\n"; + s = s + "Database is up to date."; + } + if ((status & DATABASESTATUS.COMPLETE) > 0) + { + if (s.Length > 0) + s = s + "\n"; + s = s + "Database is complete."; + } + if ((status & DATABASESTATUS.UPDATING) > 0) + { + if (s.Length > 0) + s = s + "\n"; + s = s + "Database is updating."; + } + if ((status & DATABASESTATUS.EMPTY) > 0) + { + if (s.Length > 0) + s = s + "\n"; + s = s + "Database is empty."; + } + return s; + } + + } + +} \ No newline at end of file diff --git a/AirScout/ElevationCopyrightDlg.Designer.cs b/AirScout/ElevationCopyrightDlg.Designer.cs new file mode 100644 index 0000000..9a85656 --- /dev/null +++ b/AirScout/ElevationCopyrightDlg.Designer.cs @@ -0,0 +1,67 @@ +namespace AirScout +{ + partial class ElevationCopyrightDlg + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ElevationCopyrightDlg)); + this.rtb_Copyright = new System.Windows.Forms.RichTextBox(); + this.SuspendLayout(); + // + // rtb_Copyright + // + this.rtb_Copyright.BackColor = System.Drawing.Color.White; + this.rtb_Copyright.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.rtb_Copyright.Dock = System.Windows.Forms.DockStyle.Fill; + this.rtb_Copyright.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.rtb_Copyright.Location = new System.Drawing.Point(0, 0); + this.rtb_Copyright.Name = "rtb_Copyright"; + this.rtb_Copyright.ReadOnly = true; + this.rtb_Copyright.Size = new System.Drawing.Size(1077, 253); + this.rtb_Copyright.TabIndex = 0; + this.rtb_Copyright.Text = ""; + // + // ElevationCopyrightDlg + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(1077, 253); + this.Controls.Add(this.rtb_Copyright); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow; + this.Name = "ElevationCopyrightDlg"; + this.Text = resources.GetString("$this.Text"); + this.Load += new System.EventHandler(this.ElevationCopyrightDlg_Load); + this.Leave += new System.EventHandler(this.ElevationCopyrightDlg_Leave); + this.ResumeLayout(false); + + } + + #endregion + + public System.Windows.Forms.RichTextBox rtb_Copyright; + } +} \ No newline at end of file diff --git a/AirScout/ElevationCopyrightDlg.cs b/AirScout/ElevationCopyrightDlg.cs new file mode 100644 index 0000000..47b4fd3 --- /dev/null +++ b/AirScout/ElevationCopyrightDlg.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace AirScout +{ + public partial class ElevationCopyrightDlg : Form + { + public ElevationCopyrightDlg() + { + InitializeComponent(); + } + + private void ElevationCopyrightDlg_Leave(object sender, EventArgs e) + { + this.Close(); + } + + private void ElevationCopyrightDlg_Load(object sender, EventArgs e) + { + rtb_Copyright.SelectAll(); + rtb_Copyright.SelectionAlignment = HorizontalAlignment.Center; + rtb_Copyright.DeselectAll(); + } + } +} diff --git a/AirScout/ElevationCopyrightDlg.resx b/AirScout/ElevationCopyrightDlg.resx new file mode 100644 index 0000000..408d779 --- /dev/null +++ b/AirScout/ElevationCopyrightDlg.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + +Maidenhead locator system elvation tiles calculated by DL2ALF based on elevation data of the GLOBE project: + +GLOBE Task Team and others (Hastings, David A., Paula K. Dunbar, Gerald M. Elphingstone, Mark Bootz, Hiroshi Murakami, Hiroshi Maruyama, +Hiroshi Masaharu, Peter Holland, John Payne, Nevin A. Bryant, Thomas L. Logan, J.-P. Muller, Gunter Schreier, and John S. MacDonald), eds., 1999. + +The Global Land One-kilometer Base Elevation (GLOBE) Digital Elevation Model, +Version 1.0. National Oceanic and Atmospheric Administration, National Geophysical Data Center, 325 Broadway, Boulder, Colorado 80305-3328, U.S.A. + +Digital data base on the World Wide Web (URL: http://www.ngdc.noaa.gov/mgg/topo/globe.html) and CD-ROMs. + + \ No newline at end of file diff --git a/AirScout/FirstRunWizard.Designer.cs b/AirScout/FirstRunWizard.Designer.cs new file mode 100644 index 0000000..1164dd9 --- /dev/null +++ b/AirScout/FirstRunWizard.Designer.cs @@ -0,0 +1,1113 @@ +namespace AirScout +{ + partial class FirstRunWizard + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FirstRunWizard)); + this.sw_FirstRun = new AeroWizard.StepWizardControl(); + this.wp_TermsAndConditions = new AeroWizard.WizardPage(); + this.cb_TermsAndConditions = new System.Windows.Forms.CheckBox(); + this.rb_TermsAndConditions = new System.Windows.Forms.RichTextBox(); + this.wp_GeneralCoverage = new AeroWizard.WizardPage(); + this.label6 = new System.Windows.Forms.Label(); + this.label17 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.ud_MaxLat = new System.Windows.Forms.NumericUpDown(); + this.ud_MinLat = new System.Windows.Forms.NumericUpDown(); + this.ud_MaxLon = new System.Windows.Forms.NumericUpDown(); + this.label2 = new System.Windows.Forms.Label(); + this.label1 = new System.Windows.Forms.Label(); + this.ud_MinLon = new System.Windows.Forms.NumericUpDown(); + this.gm_Coverage = new GMap.NET.WindowsForms.GMapControl(); + this.wp_ElevationModel = new AeroWizard.WizardPage(); + this.richTextBox2 = new System.Windows.Forms.RichTextBox(); + this.cb_SRTM1 = new System.Windows.Forms.CheckBox(); + this.cb_SRTM3 = new System.Windows.Forms.CheckBox(); + this.cb_GLOBE = new System.Windows.Forms.CheckBox(); + this.wp_GLOBE = new AeroWizard.WizardPage(); + this.lbl_GLOBE_Status = new System.Windows.Forms.Label(); + this.label5 = new System.Windows.Forms.Label(); + this.cb_GLOBE_EnableCache = new System.Windows.Forms.CheckBox(); + this.gm_GLOBE = new GMap.NET.WindowsForms.GMapControl(); + this.wp_SRTM3 = new AeroWizard.WizardPage(); + this.lbl_SRTM3_Status = new System.Windows.Forms.Label(); + this.label21 = new System.Windows.Forms.Label(); + this.cb_SRTM3_EnableCache = new System.Windows.Forms.CheckBox(); + this.gm_SRTM3 = new GMap.NET.WindowsForms.GMapControl(); + this.wp_SRTM1 = new AeroWizard.WizardPage(); + this.lbl_SRTM1_Status = new System.Windows.Forms.Label(); + this.label7 = new System.Windows.Forms.Label(); + this.cb_SRTM1_EnableCache = new System.Windows.Forms.CheckBox(); + this.gm_SRTM1 = new GMap.NET.WindowsForms.GMapControl(); + this.wp_UserDetails = new AeroWizard.WizardPage(); + this.label19 = new System.Windows.Forms.Label(); + this.label18 = new System.Windows.Forms.Label(); + this.tb_Zoom = new System.Windows.Forms.TextBox(); + this.btn_Zoom_Out = new System.Windows.Forms.Button(); + this.btn_Zoom_In = new System.Windows.Forms.Button(); + this.btn_QRZ = new System.Windows.Forms.Button(); + this.label12 = new System.Windows.Forms.Label(); + this.label11 = new System.Windows.Forms.Label(); + this.label10 = new System.Windows.Forms.Label(); + this.label9 = new System.Windows.Forms.Label(); + this.label8 = new System.Windows.Forms.Label(); + this.gm_Callsign = new GMap.NET.WindowsForms.GMapControl(); + this.tb_Callsign = new ScoutBase.Core.CallsignTextBox(); + this.tb_Locator = new ScoutBase.Core.LocatorTextBox(); + this.tb_Longitude = new ScoutBase.Core.DoubleTextBox(); + this.tb_Latitude = new ScoutBase.Core.DoubleTextBox(); + this.wp_PlaneFeeds = new AeroWizard.WizardPage(); + this.label20 = new System.Windows.Forms.Label(); + this.label16 = new System.Windows.Forms.Label(); + this.label15 = new System.Windows.Forms.Label(); + this.label14 = new System.Windows.Forms.Label(); + this.cb_PlaneFeed3 = new System.Windows.Forms.ComboBox(); + this.cb_PlaneFeed2 = new System.Windows.Forms.ComboBox(); + this.cb_PlaneFeed1 = new System.Windows.Forms.ComboBox(); + this.label13 = new System.Windows.Forms.Label(); + this.wp_Finish = new AeroWizard.WizardPage(); + this.lbl_Finish = new System.Windows.Forms.Label(); + this.bw_GLOBE_MapUpdater = new System.ComponentModel.BackgroundWorker(); + this.bw_SRTM3_MapUpdater = new System.ComponentModel.BackgroundWorker(); + this.bw_SRTM1_MapUpdater = new System.ComponentModel.BackgroundWorker(); + this.ss_Main = new System.Windows.Forms.StatusStrip(); + this.tsl_Status = new System.Windows.Forms.ToolStripStatusLabel(); + ((System.ComponentModel.ISupportInitialize)(this.sw_FirstRun)).BeginInit(); + this.wp_TermsAndConditions.SuspendLayout(); + this.wp_GeneralCoverage.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.ud_MaxLat)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.ud_MinLat)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.ud_MaxLon)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.ud_MinLon)).BeginInit(); + this.wp_ElevationModel.SuspendLayout(); + this.wp_GLOBE.SuspendLayout(); + this.wp_SRTM3.SuspendLayout(); + this.wp_SRTM1.SuspendLayout(); + this.wp_UserDetails.SuspendLayout(); + this.wp_PlaneFeeds.SuspendLayout(); + this.wp_Finish.SuspendLayout(); + this.ss_Main.SuspendLayout(); + this.SuspendLayout(); + // + // sw_FirstRun + // + this.sw_FirstRun.BackColor = System.Drawing.Color.White; + this.sw_FirstRun.Dock = System.Windows.Forms.DockStyle.Fill; + this.sw_FirstRun.Font = new System.Drawing.Font("Arial", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.sw_FirstRun.Location = new System.Drawing.Point(0, 0); + this.sw_FirstRun.Name = "sw_FirstRun"; + this.sw_FirstRun.Pages.Add(this.wp_TermsAndConditions); + this.sw_FirstRun.Pages.Add(this.wp_GeneralCoverage); + this.sw_FirstRun.Pages.Add(this.wp_ElevationModel); + this.sw_FirstRun.Pages.Add(this.wp_GLOBE); + this.sw_FirstRun.Pages.Add(this.wp_SRTM3); + this.sw_FirstRun.Pages.Add(this.wp_SRTM1); + this.sw_FirstRun.Pages.Add(this.wp_UserDetails); + this.sw_FirstRun.Pages.Add(this.wp_PlaneFeeds); + this.sw_FirstRun.Pages.Add(this.wp_Finish); + this.sw_FirstRun.Size = new System.Drawing.Size(784, 539); + this.sw_FirstRun.StepListFont = new System.Drawing.Font("Segoe UI", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.World); + this.sw_FirstRun.TabIndex = 0; + this.sw_FirstRun.Text = "Welcome to AirScout"; + this.sw_FirstRun.Title = "Welcome to AirScout"; + this.sw_FirstRun.TitleIcon = ((System.Drawing.Icon)(resources.GetObject("sw_FirstRun.TitleIcon"))); + // + // wp_TermsAndConditions + // + this.wp_TermsAndConditions.AllowNext = false; + this.wp_TermsAndConditions.Controls.Add(this.cb_TermsAndConditions); + this.wp_TermsAndConditions.Controls.Add(this.rb_TermsAndConditions); + this.wp_TermsAndConditions.Name = "wp_TermsAndConditions"; + this.wp_TermsAndConditions.NextPage = this.wp_GeneralCoverage; + this.wp_TermsAndConditions.Size = new System.Drawing.Size(586, 398); + this.sw_FirstRun.SetStepText(this.wp_TermsAndConditions, "Agree to the Terms and Conditions"); + this.wp_TermsAndConditions.TabIndex = 2; + this.wp_TermsAndConditions.Text = "Agree to the Terms and Conditions"; + this.wp_TermsAndConditions.Enter += new System.EventHandler(this.wp_TermsAndConditions_Enter); + // + // cb_TermsAndConditions + // + this.cb_TermsAndConditions.AutoSize = true; + this.cb_TermsAndConditions.Location = new System.Drawing.Point(15, 373); + this.cb_TermsAndConditions.Name = "cb_TermsAndConditions"; + this.cb_TermsAndConditions.Size = new System.Drawing.Size(260, 19); + this.cb_TermsAndConditions.TabIndex = 1; + this.cb_TermsAndConditions.Text = "I agree with the terms and conditions above."; + this.cb_TermsAndConditions.UseVisualStyleBackColor = true; + this.cb_TermsAndConditions.CheckedChanged += new System.EventHandler(this.cb_TermsAndConditions_CheckedChanged); + // + // rb_TermsAndConditions + // + this.rb_TermsAndConditions.BackColor = System.Drawing.Color.White; + this.rb_TermsAndConditions.Dock = System.Windows.Forms.DockStyle.Top; + this.rb_TermsAndConditions.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.rb_TermsAndConditions.Location = new System.Drawing.Point(0, 0); + this.rb_TermsAndConditions.Name = "rb_TermsAndConditions"; + this.rb_TermsAndConditions.ReadOnly = true; + this.rb_TermsAndConditions.Size = new System.Drawing.Size(586, 343); + this.rb_TermsAndConditions.TabIndex = 0; + this.rb_TermsAndConditions.Text = resources.GetString("rb_TermsAndConditions.Text"); + // + // wp_GeneralCoverage + // + this.wp_GeneralCoverage.Controls.Add(this.label6); + this.wp_GeneralCoverage.Controls.Add(this.label17); + this.wp_GeneralCoverage.Controls.Add(this.label4); + this.wp_GeneralCoverage.Controls.Add(this.label3); + this.wp_GeneralCoverage.Controls.Add(this.ud_MaxLat); + this.wp_GeneralCoverage.Controls.Add(this.ud_MinLat); + this.wp_GeneralCoverage.Controls.Add(this.ud_MaxLon); + this.wp_GeneralCoverage.Controls.Add(this.label2); + this.wp_GeneralCoverage.Controls.Add(this.label1); + this.wp_GeneralCoverage.Controls.Add(this.ud_MinLon); + this.wp_GeneralCoverage.Controls.Add(this.gm_Coverage); + this.wp_GeneralCoverage.Name = "wp_GeneralCoverage"; + this.wp_GeneralCoverage.Size = new System.Drawing.Size(586, 398); + this.sw_FirstRun.SetStepText(this.wp_GeneralCoverage, "Select the Covered Area"); + this.wp_GeneralCoverage.TabIndex = 3; + this.wp_GeneralCoverage.Text = "Select the Covered Area"; + this.wp_GeneralCoverage.Enter += new System.EventHandler(this.wp_GeneralCoverage_Enter); + // + // label6 + // + this.label6.AutoSize = true; + this.label6.ForeColor = System.Drawing.Color.Red; + this.label6.Location = new System.Drawing.Point(14, 24); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(533, 30); + this.label6.TabIndex = 9; + this.label6.Text = "CAUTION: Select area of interest just as large to cover a range of 1000-2000km ar" + + "ound your qth.\r\nLarger areas together with SRTM3 and SRTM1 elevation data will r" + + "esult in extensive disk space usage!"; + // + // label17 + // + this.label17.AutoSize = true; + this.label17.Font = new System.Drawing.Font("Segoe UI Semibold", 9F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label17.Location = new System.Drawing.Point(14, 6); + this.label17.Name = "label17"; + this.label17.Size = new System.Drawing.Size(504, 15); + this.label17.TabIndex = 1; + this.label17.Text = "Select a covered area of your interest by entering lat/long values in the numeric" + + " boxes below."; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(14, 85); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(147, 15); + this.label4.TabIndex = 8; + this.label4.Text = "Max. Latitude (-90 ... + 90):"; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(14, 61); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(143, 15); + this.label3.TabIndex = 7; + this.label3.Text = "Min. Latitude (-90 ... +90):"; + // + // ud_MaxLat + // + this.ud_MaxLat.Location = new System.Drawing.Point(163, 83); + this.ud_MaxLat.Maximum = new decimal(new int[] { + 90, + 0, + 0, + 0}); + this.ud_MaxLat.Minimum = new decimal(new int[] { + 90, + 0, + 0, + -2147483648}); + this.ud_MaxLat.Name = "ud_MaxLat"; + this.ud_MaxLat.Size = new System.Drawing.Size(49, 23); + this.ud_MaxLat.TabIndex = 6; + this.ud_MaxLat.ValueChanged += new System.EventHandler(this.ud_MaxLat_ValueChanged); + // + // ud_MinLat + // + this.ud_MinLat.Location = new System.Drawing.Point(163, 59); + this.ud_MinLat.Maximum = new decimal(new int[] { + 90, + 0, + 0, + 0}); + this.ud_MinLat.Minimum = new decimal(new int[] { + 90, + 0, + 0, + -2147483648}); + this.ud_MinLat.Name = "ud_MinLat"; + this.ud_MinLat.Size = new System.Drawing.Size(49, 23); + this.ud_MinLat.TabIndex = 5; + this.ud_MinLat.ValueChanged += new System.EventHandler(this.ud_MinLat_ValueChanged); + // + // ud_MaxLon + // + this.ud_MaxLon.Location = new System.Drawing.Point(424, 83); + this.ud_MaxLon.Maximum = new decimal(new int[] { + 180, + 0, + 0, + 0}); + this.ud_MaxLon.Minimum = new decimal(new int[] { + 180, + 0, + 0, + -2147483648}); + this.ud_MaxLon.Name = "ud_MaxLon"; + this.ud_MaxLon.Size = new System.Drawing.Size(49, 23); + this.ud_MaxLon.TabIndex = 4; + this.ud_MaxLon.ValueChanged += new System.EventHandler(this.ud_MaxLon_ValueChanged); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(251, 85); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(167, 15); + this.label2.TabIndex = 3; + this.label2.Text = "Max. Longitude (-180 ... +180):"; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(251, 61); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(169, 15); + this.label1.TabIndex = 2; + this.label1.Text = "Min. Longitude (-180 ... +180):"; + // + // ud_MinLon + // + this.ud_MinLon.Location = new System.Drawing.Point(426, 59); + this.ud_MinLon.Maximum = new decimal(new int[] { + 180, + 0, + 0, + 0}); + this.ud_MinLon.Minimum = new decimal(new int[] { + 180, + 0, + 0, + -2147483648}); + this.ud_MinLon.Name = "ud_MinLon"; + this.ud_MinLon.Size = new System.Drawing.Size(47, 23); + this.ud_MinLon.TabIndex = 1; + this.ud_MinLon.ValueChanged += new System.EventHandler(this.ud_MinLon_ValueChanged); + // + // gm_Coverage + // + this.gm_Coverage.Bearing = 0F; + this.gm_Coverage.CanDragMap = true; + this.gm_Coverage.EmptyTileColor = System.Drawing.Color.Navy; + this.gm_Coverage.GrayScaleMode = false; + this.gm_Coverage.HelperLineOption = GMap.NET.WindowsForms.HelperLineOptions.DontShow; + this.gm_Coverage.LevelsKeepInMemmory = 5; + this.gm_Coverage.Location = new System.Drawing.Point(17, 108); + this.gm_Coverage.MarkersEnabled = true; + this.gm_Coverage.MaxZoom = 2; + this.gm_Coverage.MinZoom = 2; + this.gm_Coverage.MouseWheelZoomType = GMap.NET.MouseWheelZoomType.MousePositionAndCenter; + this.gm_Coverage.Name = "gm_Coverage"; + this.gm_Coverage.NegativeMode = false; + this.gm_Coverage.PolygonsEnabled = true; + this.gm_Coverage.RetryLoadTile = 0; + this.gm_Coverage.RoutesEnabled = true; + this.gm_Coverage.SelectedAreaFillColor = System.Drawing.Color.FromArgb(((int)(((byte)(33)))), ((int)(((byte)(65)))), ((int)(((byte)(105)))), ((int)(((byte)(225))))); + this.gm_Coverage.ShowTileGridLines = false; + this.gm_Coverage.Size = new System.Drawing.Size(456, 287); + this.gm_Coverage.TabIndex = 0; + this.gm_Coverage.Zoom = 0D; + this.gm_Coverage.Enter += new System.EventHandler(this.gm_Coverage_Enter); + // + // wp_ElevationModel + // + this.wp_ElevationModel.Controls.Add(this.richTextBox2); + this.wp_ElevationModel.Controls.Add(this.cb_SRTM1); + this.wp_ElevationModel.Controls.Add(this.cb_SRTM3); + this.wp_ElevationModel.Controls.Add(this.cb_GLOBE); + this.wp_ElevationModel.Name = "wp_ElevationModel"; + this.wp_ElevationModel.Size = new System.Drawing.Size(586, 398); + this.sw_FirstRun.SetStepText(this.wp_ElevationModel, "Elevation Model"); + this.wp_ElevationModel.TabIndex = 7; + this.wp_ElevationModel.Text = "Elevation Model"; + this.wp_ElevationModel.Enter += new System.EventHandler(this.wp_ElevationModel_Enter); + // + // richTextBox2 + // + this.richTextBox2.BackColor = System.Drawing.Color.White; + this.richTextBox2.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.richTextBox2.Location = new System.Drawing.Point(13, 13); + this.richTextBox2.Name = "richTextBox2"; + this.richTextBox2.ReadOnly = true; + this.richTextBox2.Size = new System.Drawing.Size(558, 212); + this.richTextBox2.TabIndex = 4; + this.richTextBox2.Text = resources.GetString("richTextBox2.Text"); + // + // cb_SRTM1 + // + this.cb_SRTM1.AutoSize = true; + this.cb_SRTM1.Font = new System.Drawing.Font("Segoe UI", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_SRTM1.Location = new System.Drawing.Point(85, 303); + this.cb_SRTM1.Name = "cb_SRTM1"; + this.cb_SRTM1.Size = new System.Drawing.Size(365, 25); + this.cb_SRTM1.TabIndex = 3; + this.cb_SRTM1.Text = "Use SRTM1 Elevation Model (not recommended)"; + this.cb_SRTM1.UseVisualStyleBackColor = true; + this.cb_SRTM1.CheckedChanged += new System.EventHandler(this.cb_SRTM1_CheckedChanged); + // + // cb_SRTM3 + // + this.cb_SRTM3.AutoSize = true; + this.cb_SRTM3.Font = new System.Drawing.Font("Segoe UI", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_SRTM3.Location = new System.Drawing.Point(85, 277); + this.cb_SRTM3.Name = "cb_SRTM3"; + this.cb_SRTM3.Size = new System.Drawing.Size(294, 25); + this.cb_SRTM3.TabIndex = 2; + this.cb_SRTM3.Text = "Use SRTM3 Elevation Model (optional)"; + this.cb_SRTM3.UseVisualStyleBackColor = true; + this.cb_SRTM3.CheckedChanged += new System.EventHandler(this.cb_SRTM3_CheckedChanged); + // + // cb_GLOBE + // + this.cb_GLOBE.AutoSize = true; + this.cb_GLOBE.Font = new System.Drawing.Font("Segoe UI", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_GLOBE.Location = new System.Drawing.Point(85, 252); + this.cb_GLOBE.Name = "cb_GLOBE"; + this.cb_GLOBE.Size = new System.Drawing.Size(397, 25); + this.cb_GLOBE.TabIndex = 1; + this.cb_GLOBE.Text = "Use GLOBE Elevation Model (strongly recommended)"; + this.cb_GLOBE.UseVisualStyleBackColor = true; + this.cb_GLOBE.CheckedChanged += new System.EventHandler(this.cb_GLOBE_CheckedChanged); + // + // wp_GLOBE + // + this.wp_GLOBE.Controls.Add(this.lbl_GLOBE_Status); + this.wp_GLOBE.Controls.Add(this.label5); + this.wp_GLOBE.Controls.Add(this.cb_GLOBE_EnableCache); + this.wp_GLOBE.Controls.Add(this.gm_GLOBE); + this.wp_GLOBE.Name = "wp_GLOBE"; + this.wp_GLOBE.Size = new System.Drawing.Size(586, 398); + this.sw_FirstRun.SetStepText(this.wp_GLOBE, "GLOBE"); + this.wp_GLOBE.TabIndex = 6; + this.wp_GLOBE.Text = "GLOBE"; + this.wp_GLOBE.Commit += new System.EventHandler(this.wp_GLOBE_Commit); + this.wp_GLOBE.Enter += new System.EventHandler(this.wp_GLOBE_Enter); + this.wp_GLOBE.Leave += new System.EventHandler(this.wp_GLOBE_Leave); + // + // lbl_GLOBE_Status + // + this.lbl_GLOBE_Status.AutoSize = true; + this.lbl_GLOBE_Status.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lbl_GLOBE_Status.Location = new System.Drawing.Point(4, 58); + this.lbl_GLOBE_Status.Name = "lbl_GLOBE_Status"; + this.lbl_GLOBE_Status.Size = new System.Drawing.Size(40, 15); + this.lbl_GLOBE_Status.TabIndex = 2; + this.lbl_GLOBE_Status.Text = "Status"; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Font = new System.Drawing.Font("Segoe UI Semibold", 12F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label5.ForeColor = System.Drawing.Color.SteelBlue; + this.label5.Location = new System.Drawing.Point(3, 6); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(371, 21); + this.label5.TabIndex = 2; + this.label5.Text = "(G)lobal (L)and (O)ne-km (B)ase (E)levation Project"; + // + // cb_GLOBE_EnableCache + // + this.cb_GLOBE_EnableCache.AutoSize = true; + this.cb_GLOBE_EnableCache.Checked = global::AirScout.Properties.Settings.Default.Elevation_GLOBE_EnableCache; + this.cb_GLOBE_EnableCache.CheckState = System.Windows.Forms.CheckState.Checked; + this.cb_GLOBE_EnableCache.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Elevation_GLOBE_EnableCache", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_GLOBE_EnableCache.Location = new System.Drawing.Point(7, 36); + this.cb_GLOBE_EnableCache.Name = "cb_GLOBE_EnableCache"; + this.cb_GLOBE_EnableCache.Size = new System.Drawing.Size(243, 19); + this.cb_GLOBE_EnableCache.TabIndex = 1; + this.cb_GLOBE_EnableCache.Text = "Keep downloaded elevation tiles in cache"; + this.cb_GLOBE_EnableCache.UseVisualStyleBackColor = true; + // + // gm_GLOBE + // + this.gm_GLOBE.Bearing = 0F; + this.gm_GLOBE.CanDragMap = true; + this.gm_GLOBE.EmptyTileColor = System.Drawing.Color.Navy; + this.gm_GLOBE.GrayScaleMode = false; + this.gm_GLOBE.HelperLineOption = GMap.NET.WindowsForms.HelperLineOptions.DontShow; + this.gm_GLOBE.LevelsKeepInMemmory = 5; + this.gm_GLOBE.Location = new System.Drawing.Point(0, 84); + this.gm_GLOBE.MarkersEnabled = true; + this.gm_GLOBE.MaxZoom = 2; + this.gm_GLOBE.MinZoom = 2; + this.gm_GLOBE.MouseWheelZoomType = GMap.NET.MouseWheelZoomType.MousePositionAndCenter; + this.gm_GLOBE.Name = "gm_GLOBE"; + this.gm_GLOBE.NegativeMode = false; + this.gm_GLOBE.PolygonsEnabled = true; + this.gm_GLOBE.RetryLoadTile = 0; + this.gm_GLOBE.RoutesEnabled = true; + this.gm_GLOBE.SelectedAreaFillColor = System.Drawing.Color.FromArgb(((int)(((byte)(33)))), ((int)(((byte)(65)))), ((int)(((byte)(105)))), ((int)(((byte)(225))))); + this.gm_GLOBE.ShowTileGridLines = false; + this.gm_GLOBE.Size = new System.Drawing.Size(480, 311); + this.gm_GLOBE.TabIndex = 0; + this.gm_GLOBE.Zoom = 0D; + // + // wp_SRTM3 + // + this.wp_SRTM3.Controls.Add(this.lbl_SRTM3_Status); + this.wp_SRTM3.Controls.Add(this.label21); + this.wp_SRTM3.Controls.Add(this.cb_SRTM3_EnableCache); + this.wp_SRTM3.Controls.Add(this.gm_SRTM3); + this.wp_SRTM3.Name = "wp_SRTM3"; + this.wp_SRTM3.Size = new System.Drawing.Size(586, 398); + this.sw_FirstRun.SetStepText(this.wp_SRTM3, "SRTM3"); + this.wp_SRTM3.TabIndex = 8; + this.wp_SRTM3.Text = "SRTM3"; + this.wp_SRTM3.Enter += new System.EventHandler(this.wp_SRTM3_Enter); + this.wp_SRTM3.Leave += new System.EventHandler(this.wp_SRTM3_Leave); + // + // lbl_SRTM3_Status + // + this.lbl_SRTM3_Status.AutoSize = true; + this.lbl_SRTM3_Status.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lbl_SRTM3_Status.Location = new System.Drawing.Point(4, 65); + this.lbl_SRTM3_Status.Name = "lbl_SRTM3_Status"; + this.lbl_SRTM3_Status.Size = new System.Drawing.Size(40, 15); + this.lbl_SRTM3_Status.TabIndex = 5; + this.lbl_SRTM3_Status.Text = "Status"; + // + // label21 + // + this.label21.AutoSize = true; + this.label21.Font = new System.Drawing.Font("Segoe UI Semibold", 12F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label21.ForeColor = System.Drawing.Color.SteelBlue; + this.label21.Location = new System.Drawing.Point(3, 13); + this.label21.Name = "label21"; + this.label21.Size = new System.Drawing.Size(358, 21); + this.label21.TabIndex = 4; + this.label21.Text = "(S)huttle (R)adar (T)opography (M)ission 3 arcsec"; + // + // cb_SRTM3_EnableCache + // + this.cb_SRTM3_EnableCache.AutoSize = true; + this.cb_SRTM3_EnableCache.Checked = global::AirScout.Properties.Settings.Default.Elevation_SRTM3_EnableCache; + this.cb_SRTM3_EnableCache.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Elevation_SRTM3_EnableCache", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_SRTM3_EnableCache.Location = new System.Drawing.Point(7, 43); + this.cb_SRTM3_EnableCache.Name = "cb_SRTM3_EnableCache"; + this.cb_SRTM3_EnableCache.Size = new System.Drawing.Size(243, 19); + this.cb_SRTM3_EnableCache.TabIndex = 3; + this.cb_SRTM3_EnableCache.Text = "Keep downloaded elevation tiles in cache"; + this.cb_SRTM3_EnableCache.UseVisualStyleBackColor = true; + // + // gm_SRTM3 + // + this.gm_SRTM3.Bearing = 0F; + this.gm_SRTM3.CanDragMap = true; + this.gm_SRTM3.EmptyTileColor = System.Drawing.Color.Navy; + this.gm_SRTM3.GrayScaleMode = false; + this.gm_SRTM3.HelperLineOption = GMap.NET.WindowsForms.HelperLineOptions.DontShow; + this.gm_SRTM3.LevelsKeepInMemmory = 5; + this.gm_SRTM3.Location = new System.Drawing.Point(3, 84); + this.gm_SRTM3.MarkersEnabled = true; + this.gm_SRTM3.MaxZoom = 2; + this.gm_SRTM3.MinZoom = 2; + this.gm_SRTM3.MouseWheelZoomType = GMap.NET.MouseWheelZoomType.MousePositionAndCenter; + this.gm_SRTM3.Name = "gm_SRTM3"; + this.gm_SRTM3.NegativeMode = false; + this.gm_SRTM3.PolygonsEnabled = true; + this.gm_SRTM3.RetryLoadTile = 0; + this.gm_SRTM3.RoutesEnabled = true; + this.gm_SRTM3.SelectedAreaFillColor = System.Drawing.Color.FromArgb(((int)(((byte)(33)))), ((int)(((byte)(65)))), ((int)(((byte)(105)))), ((int)(((byte)(225))))); + this.gm_SRTM3.ShowTileGridLines = false; + this.gm_SRTM3.Size = new System.Drawing.Size(480, 311); + this.gm_SRTM3.TabIndex = 0; + this.gm_SRTM3.Zoom = 0D; + // + // wp_SRTM1 + // + this.wp_SRTM1.Controls.Add(this.lbl_SRTM1_Status); + this.wp_SRTM1.Controls.Add(this.label7); + this.wp_SRTM1.Controls.Add(this.cb_SRTM1_EnableCache); + this.wp_SRTM1.Controls.Add(this.gm_SRTM1); + this.wp_SRTM1.Name = "wp_SRTM1"; + this.wp_SRTM1.Size = new System.Drawing.Size(586, 398); + this.sw_FirstRun.SetStepText(this.wp_SRTM1, "SRTM1"); + this.wp_SRTM1.TabIndex = 9; + this.wp_SRTM1.Text = "SRTM1"; + this.wp_SRTM1.Enter += new System.EventHandler(this.wp_SRTM1_Enter); + this.wp_SRTM1.Leave += new System.EventHandler(this.wp_SRTM1_Leave); + // + // lbl_SRTM1_Status + // + this.lbl_SRTM1_Status.AutoSize = true; + this.lbl_SRTM1_Status.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lbl_SRTM1_Status.Location = new System.Drawing.Point(4, 60); + this.lbl_SRTM1_Status.Name = "lbl_SRTM1_Status"; + this.lbl_SRTM1_Status.Size = new System.Drawing.Size(40, 15); + this.lbl_SRTM1_Status.TabIndex = 12; + this.lbl_SRTM1_Status.Text = "Status"; + // + // label7 + // + this.label7.AutoSize = true; + this.label7.Font = new System.Drawing.Font("Segoe UI Semibold", 12F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label7.ForeColor = System.Drawing.Color.SteelBlue; + this.label7.Location = new System.Drawing.Point(3, 9); + this.label7.Name = "label7"; + this.label7.Size = new System.Drawing.Size(357, 21); + this.label7.TabIndex = 11; + this.label7.Text = "(S)huttle (R)adar (T)opography (M)ission 1 arcsec"; + // + // cb_SRTM1_EnableCache + // + this.cb_SRTM1_EnableCache.AutoSize = true; + this.cb_SRTM1_EnableCache.Checked = global::AirScout.Properties.Settings.Default.Elevation_SRTM3_EnableCache; + this.cb_SRTM1_EnableCache.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Elevation_SRTM3_EnableCache", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_SRTM1_EnableCache.Location = new System.Drawing.Point(7, 38); + this.cb_SRTM1_EnableCache.Name = "cb_SRTM1_EnableCache"; + this.cb_SRTM1_EnableCache.Size = new System.Drawing.Size(243, 19); + this.cb_SRTM1_EnableCache.TabIndex = 10; + this.cb_SRTM1_EnableCache.Text = "Keep downloaded elevation tiles in cache"; + this.cb_SRTM1_EnableCache.UseVisualStyleBackColor = true; + // + // gm_SRTM1 + // + this.gm_SRTM1.Bearing = 0F; + this.gm_SRTM1.CanDragMap = true; + this.gm_SRTM1.EmptyTileColor = System.Drawing.Color.Navy; + this.gm_SRTM1.GrayScaleMode = false; + this.gm_SRTM1.HelperLineOption = GMap.NET.WindowsForms.HelperLineOptions.DontShow; + this.gm_SRTM1.LevelsKeepInMemmory = 5; + this.gm_SRTM1.Location = new System.Drawing.Point(3, 84); + this.gm_SRTM1.MarkersEnabled = true; + this.gm_SRTM1.MaxZoom = 2; + this.gm_SRTM1.MinZoom = 2; + this.gm_SRTM1.MouseWheelZoomType = GMap.NET.MouseWheelZoomType.MousePositionAndCenter; + this.gm_SRTM1.Name = "gm_SRTM1"; + this.gm_SRTM1.NegativeMode = false; + this.gm_SRTM1.PolygonsEnabled = true; + this.gm_SRTM1.RetryLoadTile = 0; + this.gm_SRTM1.RoutesEnabled = true; + this.gm_SRTM1.SelectedAreaFillColor = System.Drawing.Color.FromArgb(((int)(((byte)(33)))), ((int)(((byte)(65)))), ((int)(((byte)(105)))), ((int)(((byte)(225))))); + this.gm_SRTM1.ShowTileGridLines = false; + this.gm_SRTM1.Size = new System.Drawing.Size(480, 314); + this.gm_SRTM1.TabIndex = 9; + this.gm_SRTM1.Zoom = 0D; + // + // wp_UserDetails + // + this.wp_UserDetails.Controls.Add(this.label19); + this.wp_UserDetails.Controls.Add(this.label18); + this.wp_UserDetails.Controls.Add(this.tb_Zoom); + this.wp_UserDetails.Controls.Add(this.btn_Zoom_Out); + this.wp_UserDetails.Controls.Add(this.btn_Zoom_In); + this.wp_UserDetails.Controls.Add(this.btn_QRZ); + this.wp_UserDetails.Controls.Add(this.label12); + this.wp_UserDetails.Controls.Add(this.label11); + this.wp_UserDetails.Controls.Add(this.label10); + this.wp_UserDetails.Controls.Add(this.label9); + this.wp_UserDetails.Controls.Add(this.label8); + this.wp_UserDetails.Controls.Add(this.gm_Callsign); + this.wp_UserDetails.Controls.Add(this.tb_Callsign); + this.wp_UserDetails.Controls.Add(this.tb_Locator); + this.wp_UserDetails.Controls.Add(this.tb_Longitude); + this.wp_UserDetails.Controls.Add(this.tb_Latitude); + this.wp_UserDetails.Name = "wp_UserDetails"; + this.wp_UserDetails.Size = new System.Drawing.Size(586, 398); + this.sw_FirstRun.SetStepText(this.wp_UserDetails, "User Details"); + this.wp_UserDetails.TabIndex = 5; + this.wp_UserDetails.Text = "User Details"; + this.wp_UserDetails.Enter += new System.EventHandler(this.wp_UserDetails_Enter); + // + // label19 + // + this.label19.AutoSize = true; + this.label19.Location = new System.Drawing.Point(9, 6); + this.label19.Name = "label19"; + this.label19.Size = new System.Drawing.Size(530, 60); + this.label19.TabIndex = 28; + this.label19.Text = resources.GetString("label19.Text"); + // + // label18 + // + this.label18.AutoSize = true; + this.label18.Location = new System.Drawing.Point(342, 365); + this.label18.Name = "label18"; + this.label18.Size = new System.Drawing.Size(69, 15); + this.label18.TabIndex = 27; + this.label18.Text = "Map Zoom:"; + // + // tb_Zoom + // + this.tb_Zoom.BackColor = System.Drawing.Color.FloralWhite; + this.tb_Zoom.Location = new System.Drawing.Point(495, 361); + this.tb_Zoom.Name = "tb_Zoom"; + this.tb_Zoom.Size = new System.Drawing.Size(30, 23); + this.tb_Zoom.TabIndex = 26; + // + // btn_Zoom_Out + // + this.btn_Zoom_Out.Location = new System.Drawing.Point(533, 360); + this.btn_Zoom_Out.Name = "btn_Zoom_Out"; + this.btn_Zoom_Out.Size = new System.Drawing.Size(30, 25); + this.btn_Zoom_Out.TabIndex = 25; + this.btn_Zoom_Out.Text = "-"; + this.btn_Zoom_Out.UseVisualStyleBackColor = true; + this.btn_Zoom_Out.Click += new System.EventHandler(this.btn_Zoom_Out_Click); + // + // btn_Zoom_In + // + this.btn_Zoom_In.Location = new System.Drawing.Point(456, 360); + this.btn_Zoom_In.Name = "btn_Zoom_In"; + this.btn_Zoom_In.Size = new System.Drawing.Size(30, 25); + this.btn_Zoom_In.TabIndex = 24; + this.btn_Zoom_In.Text = "+"; + this.btn_Zoom_In.UseVisualStyleBackColor = true; + this.btn_Zoom_In.Click += new System.EventHandler(this.btn_Zoom_In_Click); + // + // btn_QRZ + // + this.btn_QRZ.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_QRZ.Location = new System.Drawing.Point(458, 216); + this.btn_QRZ.Name = "btn_QRZ"; + this.btn_QRZ.Size = new System.Drawing.Size(107, 26); + this.btn_QRZ.TabIndex = 17; + this.btn_QRZ.Text = "QRZ lookup"; + this.btn_QRZ.UseVisualStyleBackColor = true; + this.btn_QRZ.Click += new System.EventHandler(this.btn_QRZ_Click); + // + // label12 + // + this.label12.AutoSize = true; + this.label12.Location = new System.Drawing.Point(3, 6); + this.label12.Name = "label12"; + this.label12.Size = new System.Drawing.Size(0, 15); + this.label12.TabIndex = 1; + // + // label11 + // + this.label11.AutoSize = true; + this.label11.Location = new System.Drawing.Point(344, 120); + this.label11.Name = "label11"; + this.label11.Size = new System.Drawing.Size(50, 15); + this.label11.TabIndex = 8; + this.label11.Text = "Locator:"; + // + // label10 + // + this.label10.AutoSize = true; + this.label10.Location = new System.Drawing.Point(344, 177); + this.label10.Name = "label10"; + this.label10.Size = new System.Drawing.Size(64, 15); + this.label10.TabIndex = 6; + this.label10.Text = "Longitude:"; + // + // label9 + // + this.label9.AutoSize = true; + this.label9.Location = new System.Drawing.Point(344, 148); + this.label9.Name = "label9"; + this.label9.Size = new System.Drawing.Size(53, 15); + this.label9.TabIndex = 4; + this.label9.Text = "Latitude:"; + // + // label8 + // + this.label8.AutoSize = true; + this.label8.Location = new System.Drawing.Point(344, 93); + this.label8.Name = "label8"; + this.label8.Size = new System.Drawing.Size(52, 15); + this.label8.TabIndex = 2; + this.label8.Text = "Callsign:"; + // + // gm_Callsign + // + this.gm_Callsign.Bearing = 0F; + this.gm_Callsign.CanDragMap = true; + this.gm_Callsign.EmptyTileColor = System.Drawing.Color.Navy; + this.gm_Callsign.GrayScaleMode = false; + this.gm_Callsign.HelperLineOption = GMap.NET.WindowsForms.HelperLineOptions.DontShow; + this.gm_Callsign.LevelsKeepInMemmory = 5; + this.gm_Callsign.Location = new System.Drawing.Point(0, 84); + this.gm_Callsign.MarkersEnabled = true; + this.gm_Callsign.MaxZoom = 2; + this.gm_Callsign.MinZoom = 2; + this.gm_Callsign.MouseWheelZoomType = GMap.NET.MouseWheelZoomType.MousePositionAndCenter; + this.gm_Callsign.Name = "gm_Callsign"; + this.gm_Callsign.NegativeMode = false; + this.gm_Callsign.PolygonsEnabled = true; + this.gm_Callsign.RetryLoadTile = 0; + this.gm_Callsign.RoutesEnabled = true; + this.gm_Callsign.SelectedAreaFillColor = System.Drawing.Color.FromArgb(((int)(((byte)(33)))), ((int)(((byte)(65)))), ((int)(((byte)(105)))), ((int)(((byte)(225))))); + this.gm_Callsign.ShowTileGridLines = false; + this.gm_Callsign.Size = new System.Drawing.Size(320, 311); + this.gm_Callsign.TabIndex = 0; + this.gm_Callsign.Zoom = 0D; + this.gm_Callsign.OnMarkerEnter += new GMap.NET.WindowsForms.MarkerEnter(this.gm_Callsign_OnMarkerEnter); + this.gm_Callsign.OnMapZoomChanged += new GMap.NET.MapZoomChanged(this.gm_Callsign_OnMapZoomChanged); + this.gm_Callsign.MouseDown += new System.Windows.Forms.MouseEventHandler(this.gm_Callsign_MouseDown); + this.gm_Callsign.MouseMove += new System.Windows.Forms.MouseEventHandler(this.gm_Callsign_MouseMove); + this.gm_Callsign.MouseUp += new System.Windows.Forms.MouseEventHandler(this.gm_Callsign_MouseUp); + // + // tb_Callsign + // + this.tb_Callsign.BackColor = System.Drawing.SystemColors.Window; + this.tb_Callsign.CharacterCasing = System.Windows.Forms.CharacterCasing.Upper; + this.tb_Callsign.ErrorBackColor = System.Drawing.Color.Red; + this.tb_Callsign.ErrorForeColor = System.Drawing.Color.White; + this.tb_Callsign.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Callsign.ForeColor = System.Drawing.SystemColors.WindowText; + this.tb_Callsign.Location = new System.Drawing.Point(458, 90); + this.tb_Callsign.Name = "tb_Callsign"; + this.tb_Callsign.Size = new System.Drawing.Size(107, 21); + this.tb_Callsign.TabIndex = 1; + this.tb_Callsign.TextChanged += new System.EventHandler(this.tb_Callsign_TextChanged); + // + // tb_Locator + // + this.tb_Locator.DataBindings.Add(new System.Windows.Forms.Binding("SmallLettersForSubsquares", global::AirScout.Properties.Settings.Default, "Locator_SmallLettersForSubsquares", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Locator.ErrorBackColor = System.Drawing.Color.Red; + this.tb_Locator.ErrorForeColor = System.Drawing.Color.White; + this.tb_Locator.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Locator.Location = new System.Drawing.Point(458, 117); + this.tb_Locator.Name = "tb_Locator"; + this.tb_Locator.Size = new System.Drawing.Size(107, 21); + this.tb_Locator.SmallLettersForSubsquares = global::AirScout.Properties.Settings.Default.Locator_SmallLettersForSubsquares; + this.tb_Locator.TabIndex = 4; + this.tb_Locator.TextChanged += new System.EventHandler(this.tb_Locator_TextChanged); + // + // tb_Longitude + // + this.tb_Longitude.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Longitude.FormatSpecifier = "F8"; + this.tb_Longitude.Location = new System.Drawing.Point(458, 174); + this.tb_Longitude.MaxValue = 180D; + this.tb_Longitude.MinValue = -180D; + this.tb_Longitude.Name = "tb_Longitude"; + this.tb_Longitude.Size = new System.Drawing.Size(107, 21); + this.tb_Longitude.TabIndex = 3; + this.tb_Longitude.Text = "10.68327000"; + this.tb_Longitude.Value = 10.68327D; + this.tb_Longitude.TextChanged += new System.EventHandler(this.tb_Longitude_TextChanged); + // + // tb_Latitude + // + this.tb_Latitude.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Latitude.FormatSpecifier = "F8"; + this.tb_Latitude.Location = new System.Drawing.Point(458, 144); + this.tb_Latitude.MaxValue = 90D; + this.tb_Latitude.MinValue = -90D; + this.tb_Latitude.Name = "tb_Latitude"; + this.tb_Latitude.Size = new System.Drawing.Size(107, 21); + this.tb_Latitude.TabIndex = 2; + this.tb_Latitude.Text = "50.93706700"; + this.tb_Latitude.Value = 50.937067D; + this.tb_Latitude.TextChanged += new System.EventHandler(this.tb_Latitude_TextChanged); + // + // wp_PlaneFeeds + // + this.wp_PlaneFeeds.Controls.Add(this.label20); + this.wp_PlaneFeeds.Controls.Add(this.label16); + this.wp_PlaneFeeds.Controls.Add(this.label15); + this.wp_PlaneFeeds.Controls.Add(this.label14); + this.wp_PlaneFeeds.Controls.Add(this.cb_PlaneFeed3); + this.wp_PlaneFeeds.Controls.Add(this.cb_PlaneFeed2); + this.wp_PlaneFeeds.Controls.Add(this.cb_PlaneFeed1); + this.wp_PlaneFeeds.Controls.Add(this.label13); + this.wp_PlaneFeeds.Name = "wp_PlaneFeeds"; + this.wp_PlaneFeeds.Size = new System.Drawing.Size(586, 398); + this.sw_FirstRun.SetStepText(this.wp_PlaneFeeds, "Select a Plane Feed"); + this.wp_PlaneFeeds.TabIndex = 10; + this.wp_PlaneFeeds.Text = "Select a Plane Feed"; + this.wp_PlaneFeeds.Enter += new System.EventHandler(this.wp_PlaneFeeds_Enter); + // + // label20 + // + this.label20.AutoSize = true; + this.label20.Font = new System.Drawing.Font("Segoe UI", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label20.Location = new System.Drawing.Point(29, 34); + this.label20.Name = "label20"; + this.label20.Size = new System.Drawing.Size(521, 85); + this.label20.TabIndex = 8; + this.label20.Text = resources.GetString("label20.Text"); + this.label20.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // label16 + // + this.label16.AutoSize = true; + this.label16.Font = new System.Drawing.Font("Segoe UI", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label16.Location = new System.Drawing.Point(12, 242); + this.label16.Name = "label16"; + this.label16.Size = new System.Drawing.Size(101, 21); + this.label16.TabIndex = 7; + this.label16.Text = "Plane Feed 3:"; + // + // label15 + // + this.label15.AutoSize = true; + this.label15.Font = new System.Drawing.Font("Segoe UI", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label15.Location = new System.Drawing.Point(12, 213); + this.label15.Name = "label15"; + this.label15.Size = new System.Drawing.Size(101, 21); + this.label15.TabIndex = 6; + this.label15.Text = "Plane Feed 2:"; + // + // label14 + // + this.label14.AutoSize = true; + this.label14.Font = new System.Drawing.Font("Segoe UI", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label14.Location = new System.Drawing.Point(12, 184); + this.label14.Name = "label14"; + this.label14.Size = new System.Drawing.Size(101, 21); + this.label14.TabIndex = 1; + this.label14.Text = "Plane Feed 1:"; + // + // cb_PlaneFeed3 + // + this.cb_PlaneFeed3.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cb_PlaneFeed3.Font = new System.Drawing.Font("Courier New", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_PlaneFeed3.FormattingEnabled = true; + this.cb_PlaneFeed3.Location = new System.Drawing.Point(119, 242); + this.cb_PlaneFeed3.Name = "cb_PlaneFeed3"; + this.cb_PlaneFeed3.Size = new System.Drawing.Size(431, 26); + this.cb_PlaneFeed3.TabIndex = 5; + this.cb_PlaneFeed3.SelectedIndexChanged += new System.EventHandler(this.cb_PlaneFeed3_SelectedIndexChanged); + // + // cb_PlaneFeed2 + // + this.cb_PlaneFeed2.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cb_PlaneFeed2.Font = new System.Drawing.Font("Courier New", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_PlaneFeed2.FormattingEnabled = true; + this.cb_PlaneFeed2.Location = new System.Drawing.Point(119, 213); + this.cb_PlaneFeed2.Name = "cb_PlaneFeed2"; + this.cb_PlaneFeed2.Size = new System.Drawing.Size(431, 26); + this.cb_PlaneFeed2.TabIndex = 4; + this.cb_PlaneFeed2.SelectedIndexChanged += new System.EventHandler(this.cb_PlaneFeed2_SelectedIndexChanged); + // + // cb_PlaneFeed1 + // + this.cb_PlaneFeed1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cb_PlaneFeed1.Font = new System.Drawing.Font("Courier New", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_PlaneFeed1.FormattingEnabled = true; + this.cb_PlaneFeed1.Location = new System.Drawing.Point(119, 184); + this.cb_PlaneFeed1.Name = "cb_PlaneFeed1"; + this.cb_PlaneFeed1.Size = new System.Drawing.Size(431, 26); + this.cb_PlaneFeed1.TabIndex = 3; + this.cb_PlaneFeed1.SelectedIndexChanged += new System.EventHandler(this.cb_PlaneFeed1_SelectedIndexChanged); + // + // label13 + // + this.label13.AutoSize = true; + this.label13.Location = new System.Drawing.Point(3, 14); + this.label13.Name = "label13"; + this.label13.Size = new System.Drawing.Size(0, 15); + this.label13.TabIndex = 2; + // + // wp_Finish + // + this.wp_Finish.Controls.Add(this.lbl_Finish); + this.wp_Finish.IsFinishPage = true; + this.wp_Finish.Name = "wp_Finish"; + this.wp_Finish.Size = new System.Drawing.Size(586, 398); + this.sw_FirstRun.SetStepText(this.wp_Finish, "Finish"); + this.wp_Finish.TabIndex = 11; + this.wp_Finish.Text = "Finish"; + this.wp_Finish.Enter += new System.EventHandler(this.wp_Finish_Enter); + // + // lbl_Finish + // + this.lbl_Finish.Font = new System.Drawing.Font("Segoe UI", 15.75F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lbl_Finish.Location = new System.Drawing.Point(55, 67); + this.lbl_Finish.Name = "lbl_Finish"; + this.lbl_Finish.Size = new System.Drawing.Size(486, 271); + this.lbl_Finish.TabIndex = 1; + this.lbl_Finish.Text = "Finish statement."; + this.lbl_Finish.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // bw_GLOBE_MapUpdater + // + this.bw_GLOBE_MapUpdater.WorkerReportsProgress = true; + this.bw_GLOBE_MapUpdater.WorkerSupportsCancellation = true; + this.bw_GLOBE_MapUpdater.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bw_GLOBE_MapUpdater_DoWork); + this.bw_GLOBE_MapUpdater.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.bw_GLOBE_MapUpdater_ProgressChanged); + this.bw_GLOBE_MapUpdater.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.bw_GLOBE_MapUpdater_RunWorkerCompleted); + // + // bw_SRTM3_MapUpdater + // + this.bw_SRTM3_MapUpdater.WorkerReportsProgress = true; + this.bw_SRTM3_MapUpdater.WorkerSupportsCancellation = true; + this.bw_SRTM3_MapUpdater.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bw_SRTM3_MapUpdater_DoWork); + this.bw_SRTM3_MapUpdater.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.bw_SRTM3_MapUpdater_ProgressChanged); + this.bw_SRTM3_MapUpdater.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.bw_SRTM3_MapUpdater_RunWorkerCompleted); + // + // bw_SRTM1_MapUpdater + // + this.bw_SRTM1_MapUpdater.WorkerReportsProgress = true; + this.bw_SRTM1_MapUpdater.WorkerSupportsCancellation = true; + this.bw_SRTM1_MapUpdater.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bw_SRTM1_MapUpdater_DoWork); + this.bw_SRTM1_MapUpdater.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.bw_SRTM1_MapUpdater_ProgressChanged); + this.bw_SRTM1_MapUpdater.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.bw_SRTM1_MapUpdater_RunWorkerCompleted); + // + // ss_Main + // + this.ss_Main.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.tsl_Status}); + this.ss_Main.Location = new System.Drawing.Point(0, 539); + this.ss_Main.Name = "ss_Main"; + this.ss_Main.Size = new System.Drawing.Size(784, 22); + this.ss_Main.TabIndex = 1; + this.ss_Main.Text = "statusStrip1"; + // + // tsl_Status + // + this.tsl_Status.Name = "tsl_Status"; + this.tsl_Status.Size = new System.Drawing.Size(42, 17); + this.tsl_Status.Text = "Status."; + // + // FirstRunWizard + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(784, 561); + this.Controls.Add(this.sw_FirstRun); + this.Controls.Add(this.ss_Main); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.MaximizeBox = false; + this.Name = "FirstRunWizard"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.TopMost = true; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FirstRunWizard_FormClosing); + this.Load += new System.EventHandler(this.FirstRunWizard_Load); + ((System.ComponentModel.ISupportInitialize)(this.sw_FirstRun)).EndInit(); + this.wp_TermsAndConditions.ResumeLayout(false); + this.wp_TermsAndConditions.PerformLayout(); + this.wp_GeneralCoverage.ResumeLayout(false); + this.wp_GeneralCoverage.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.ud_MaxLat)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.ud_MinLat)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.ud_MaxLon)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.ud_MinLon)).EndInit(); + this.wp_ElevationModel.ResumeLayout(false); + this.wp_ElevationModel.PerformLayout(); + this.wp_GLOBE.ResumeLayout(false); + this.wp_GLOBE.PerformLayout(); + this.wp_SRTM3.ResumeLayout(false); + this.wp_SRTM3.PerformLayout(); + this.wp_SRTM1.ResumeLayout(false); + this.wp_SRTM1.PerformLayout(); + this.wp_UserDetails.ResumeLayout(false); + this.wp_UserDetails.PerformLayout(); + this.wp_PlaneFeeds.ResumeLayout(false); + this.wp_PlaneFeeds.PerformLayout(); + this.wp_Finish.ResumeLayout(false); + this.ss_Main.ResumeLayout(false); + this.ss_Main.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private AeroWizard.StepWizardControl sw_FirstRun; + private AeroWizard.WizardPage wp_TermsAndConditions; + private AeroWizard.WizardPage wp_ElevationModel; + private AeroWizard.WizardPage wp_SRTM3; + private AeroWizard.WizardPage wp_SRTM1; + private System.Windows.Forms.CheckBox cb_TermsAndConditions; + private System.Windows.Forms.RichTextBox rb_TermsAndConditions; + private AeroWizard.WizardPage wp_GeneralCoverage; + private AeroWizard.WizardPage wp_UserDetails; + private GMap.NET.WindowsForms.GMapControl gm_Coverage; + private System.Windows.Forms.NumericUpDown ud_MinLon; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.NumericUpDown ud_MaxLon; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.NumericUpDown ud_MaxLat; + private System.Windows.Forms.NumericUpDown ud_MinLat; + private AeroWizard.WizardPage wp_GLOBE; + private GMap.NET.WindowsForms.GMapControl gm_GLOBE; + private System.Windows.Forms.CheckBox cb_GLOBE; + private System.Windows.Forms.CheckBox cb_SRTM1; + private System.Windows.Forms.CheckBox cb_SRTM3; + private System.Windows.Forms.RichTextBox richTextBox2; + private System.ComponentModel.BackgroundWorker bw_GLOBE_MapUpdater; + private AeroWizard.WizardPage wp_PlaneFeeds; + private AeroWizard.WizardPage wp_Finish; + private System.ComponentModel.BackgroundWorker bw_SRTM3_MapUpdater; + private System.ComponentModel.BackgroundWorker bw_SRTM1_MapUpdater; + private GMap.NET.WindowsForms.GMapControl gm_SRTM3; + private GMap.NET.WindowsForms.GMapControl gm_SRTM1; + private GMap.NET.WindowsForms.GMapControl gm_Callsign; + private System.Windows.Forms.Label label8; + private System.Windows.Forms.Label label11; + private System.Windows.Forms.Label label10; + private System.Windows.Forms.Label label9; + private System.Windows.Forms.Label label12; + private ScoutBase.Core.DoubleTextBox tb_Longitude; + private ScoutBase.Core.DoubleTextBox tb_Latitude; + private ScoutBase.Core.LocatorTextBox tb_Locator; + private ScoutBase.Core.CallsignTextBox tb_Callsign; + private System.Windows.Forms.Label label13; + private System.Windows.Forms.ComboBox cb_PlaneFeed1; + private System.Windows.Forms.ComboBox cb_PlaneFeed3; + private System.Windows.Forms.ComboBox cb_PlaneFeed2; + private System.Windows.Forms.Label label16; + private System.Windows.Forms.Label label15; + private System.Windows.Forms.Label label14; + private System.Windows.Forms.Label label17; + private System.Windows.Forms.Label lbl_Finish; + private System.Windows.Forms.Button btn_QRZ; + private System.Windows.Forms.Label label18; + private System.Windows.Forms.TextBox tb_Zoom; + private System.Windows.Forms.Button btn_Zoom_Out; + private System.Windows.Forms.Button btn_Zoom_In; + private System.Windows.Forms.StatusStrip ss_Main; + private System.Windows.Forms.ToolStripStatusLabel tsl_Status; + private System.Windows.Forms.Label label19; + private System.Windows.Forms.Label label20; + private System.Windows.Forms.CheckBox cb_GLOBE_EnableCache; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.Label lbl_GLOBE_Status; + private System.Windows.Forms.Label lbl_SRTM3_Status; + private System.Windows.Forms.Label label21; + private System.Windows.Forms.CheckBox cb_SRTM3_EnableCache; + private System.Windows.Forms.Label lbl_SRTM1_Status; + private System.Windows.Forms.Label label7; + private System.Windows.Forms.CheckBox cb_SRTM1_EnableCache; + private System.Windows.Forms.Label label6; + } +} \ No newline at end of file diff --git a/AirScout/FirstRunWizard.cs b/AirScout/FirstRunWizard.cs new file mode 100644 index 0000000..90f0bc0 --- /dev/null +++ b/AirScout/FirstRunWizard.cs @@ -0,0 +1,1162 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using System.Globalization; +using System.Collections; +using System.IO; +using System.Net; +using Microsoft.Win32; +using GMap.NET; +using GMap.NET.MapProviders; +using GMap.NET.WindowsForms; +using GMap.NET.WindowsForms.Markers; +using ScoutBase; +using ScoutBase.Core; +using ScoutBase.Stations; +using ScoutBase.Elevation; +using AirScout.PlaneFeeds.Generic; + +namespace AirScout +{ + public partial class FirstRunWizard : Form + { + GMapOverlay Coveragepolygons = new GMapOverlay("Coveragepolygons"); + GMapOverlay GLOBEpolygons = new GMapOverlay("GLOBEpolygons"); + GMapOverlay SRTM3polygons = new GMapOverlay("SRTM3polygons"); + GMapOverlay SRTM1polygons = new GMapOverlay("SRTM1polygons"); + GMapOverlay Callsignpolygons = new GMapOverlay("Callsignpolygons"); + GMapOverlay Callsignspositions = new GMapOverlay("Callsignpositions"); + + GMarkerGoogle UserPos = new GMarkerGoogle(new PointLatLng(0.0, 0.0), GMarkerGoogleType.red_dot); + + private bool IsDraggingMarker = false; + + private LogWriter Log = LogWriter.Instance; + + public FirstRunWizard() + { + InitializeComponent(); + } + + private void FirstRunWizard_Load(object sender, EventArgs e) + { + Log.WriteMessage("Loading."); + // set initial settings for CoverageMap + GMap.NET.MapProviders.GMapProvider.UserAgent = "AirScout"; + gm_Coverage.MapProvider = GMapProviders.Find(Properties.Settings.Default.Map_Provider); + gm_Coverage.IgnoreMarkerOnMouseWheel = true; + gm_Coverage.MinZoom = 0; + gm_Coverage.MaxZoom = 20; + gm_Coverage.Zoom = 6; + gm_Coverage.DragButton = System.Windows.Forms.MouseButtons.Left; + gm_Coverage.CanDragMap = true; + gm_Coverage.ScalePen = new Pen(Color.Black, 3); + gm_Coverage.HelperLinePen = null; + gm_Coverage.SelectionPen = null; + gm_Coverage.MapScaleInfoEnabled = true; + gm_Coverage.Overlays.Add(Coveragepolygons); + + // set initial settings for GLOBEMap + GMap.NET.MapProviders.GMapProvider.UserAgent = "AirScout"; + gm_GLOBE.MapProvider = GMapProviders.Find(Properties.Settings.Default.Map_Provider); + gm_GLOBE.IgnoreMarkerOnMouseWheel = true; + gm_GLOBE.MinZoom = 0; + gm_GLOBE.MaxZoom = 20; + gm_GLOBE.Zoom = 1; + gm_GLOBE.DragButton = System.Windows.Forms.MouseButtons.Left; + gm_GLOBE.CanDragMap = true; + gm_GLOBE.ScalePen = new Pen(Color.Black, 3); + gm_GLOBE.HelperLinePen = null; + gm_GLOBE.SelectionPen = null; + gm_GLOBE.MapScaleInfoEnabled = true; + gm_GLOBE.Overlays.Add(GLOBEpolygons); + + // set initial settings for SRTM3Map + GMap.NET.MapProviders.GMapProvider.UserAgent = "AirScout"; + gm_SRTM3.MapProvider = GMapProviders.Find(Properties.Settings.Default.Map_Provider); + gm_SRTM3.IgnoreMarkerOnMouseWheel = true; + gm_SRTM3.MinZoom = 0; + gm_SRTM3.MaxZoom = 20; + gm_SRTM3.Zoom = 1; + gm_SRTM3.DragButton = System.Windows.Forms.MouseButtons.Left; + gm_SRTM3.CanDragMap = true; + gm_SRTM3.ScalePen = new Pen(Color.Black, 3); + gm_SRTM3.HelperLinePen = null; + gm_SRTM3.SelectionPen = null; + gm_SRTM3.MapScaleInfoEnabled = true; + gm_SRTM3.Overlays.Add(SRTM3polygons); + + // set initial settings for SRTM1Map + GMap.NET.MapProviders.GMapProvider.UserAgent = "AirScout"; + gm_SRTM1.MapProvider = GMapProviders.Find(Properties.Settings.Default.Map_Provider); + gm_SRTM1.IgnoreMarkerOnMouseWheel = true; + gm_SRTM1.MinZoom = 0; + gm_SRTM1.MaxZoom = 20; + gm_SRTM1.Zoom = 1; + gm_SRTM1.DragButton = System.Windows.Forms.MouseButtons.Left; + gm_SRTM1.CanDragMap = true; + gm_SRTM1.ScalePen = new Pen(Color.Black, 3); + gm_SRTM1.HelperLinePen = null; + gm_SRTM1.SelectionPen = null; + gm_SRTM1.MapScaleInfoEnabled = true; + gm_SRTM1.Overlays.Add(SRTM1polygons); + + // set initial settings for user details + GMap.NET.MapProviders.GMapProvider.UserAgent = "AirScout"; + gm_Callsign.MapProvider = GMapProviders.Find(Properties.Settings.Default.Map_Provider); + gm_Callsign.IgnoreMarkerOnMouseWheel = true; + gm_Callsign.MinZoom = 0; + gm_Callsign.MaxZoom = 20; + gm_Callsign.Zoom = 1; + gm_Callsign.DragButton = System.Windows.Forms.MouseButtons.Left; + gm_Callsign.CanDragMap = true; + gm_Callsign.ScalePen = new Pen(Color.Black, 3); + gm_Callsign.HelperLinePen = null; + gm_Callsign.SelectionPen = null; + gm_Callsign.MapScaleInfoEnabled = true; + gm_Callsign.Overlays.Add(Callsignpolygons); + gm_Callsign.Overlays.Add(Callsignspositions); + Callsignspositions.Markers.Add(UserPos); + Say(""); + Log.WriteMessage("Finished."); + } + + private void Say(string text) + { + if (tsl_Status.Text != text) + { + tsl_Status.Text = text; + ss_Main.Refresh(); + } + } + + private void wp_TermsAndConditions_Enter(object sender, EventArgs e) + { + // reset topmost flag + this.TopMost = false; + // load terms and conditions file + rb_TermsAndConditions.Text = ""; + try + { + using (StreamReader sr = new StreamReader(Path.Combine(Application.StartupPath, "TERMSANDCONDITIONS"))) + { + rb_TermsAndConditions.Text = sr.ReadToEnd(); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + if (rb_TermsAndConditions.Text.Length > 0) + cb_TermsAndConditions.Enabled = true; + else + cb_TermsAndConditions.Enabled = false; + } + + private void cb_TermsAndConditions_CheckedChanged(object sender, EventArgs e) + { + if (cb_TermsAndConditions.Checked) + wp_TermsAndConditions.AllowNext = true; + else + wp_TermsAndConditions.AllowNext = false; + } + + private void wizardControl1_SelectedPageChanged(object sender, EventArgs e) + { + + } + + #region wp_General + + private void wp_GeneralCoverage_Enter(object sender, EventArgs e) + { + UpdateCoverage(); + ud_MinLon.Value = (decimal)Properties.Settings.Default.MinLon; + ud_MaxLon.Value = (decimal)Properties.Settings.Default.MaxLon; + ud_MinLat.Value = (decimal)Properties.Settings.Default.MinLat; + ud_MaxLat.Value = (decimal)Properties.Settings.Default.MaxLat; + + } + + private void UpdateCoverage() + { + Coveragepolygons.Clear(); + // add tile to map polygons + List l = new List(); + l.Add(new PointLatLng(Properties.Settings.Default.MinLat, Properties.Settings.Default.MinLon)); + l.Add(new PointLatLng(Properties.Settings.Default.MinLat, Properties.Settings.Default.MaxLon)); + l.Add(new PointLatLng(Properties.Settings.Default.MaxLat, Properties.Settings.Default.MaxLon)); + l.Add(new PointLatLng(Properties.Settings.Default.MaxLat, Properties.Settings.Default.MinLon)); + GMapPolygon p = new GMapPolygon(l, "Coverage"); + p.Stroke = new Pen(Color.FromArgb(255, Color.Magenta), 3); + p.Fill = new SolidBrush(Color.FromArgb(0, Color.Magenta)); + Coveragepolygons.Polygons.Add(p); + // zoom the map + gm_Coverage.SetZoomToFitRect(RectLatLng.FromLTRB(Properties.Settings.Default.MinLon - 1, Properties.Settings.Default.MaxLat + 1, Properties.Settings.Default.MaxLon + 1, Properties.Settings.Default.MinLat - 1)); + } + + private void gm_Coverage_Enter(object sender, EventArgs e) + { + } + + private void ud_MinLon_ValueChanged(object sender, EventArgs e) + { + if ((double)ud_MinLon.Value <= Properties.Settings.Default.MaxLon) + { + Properties.Settings.Default.MinLon = (double)ud_MinLon.Value; + UpdateCoverage(); + } + } + + private void ud_MaxLon_ValueChanged(object sender, EventArgs e) + { + if ((double)ud_MaxLon.Value >= Properties.Settings.Default.MinLon) + { + Properties.Settings.Default.MaxLon = (double)ud_MaxLon.Value; + UpdateCoverage(); + } + } + + private void ud_MinLat_ValueChanged(object sender, EventArgs e) + { + if ((double)ud_MinLat.Value <= Properties.Settings.Default.MaxLat) + { + Properties.Settings.Default.MinLat = (double)ud_MinLat.Value; + UpdateCoverage(); + } + } + + private void ud_MaxLat_ValueChanged(object sender, EventArgs e) + { + if ((double)ud_MaxLat.Value >= Properties.Settings.Default.MinLat) + { + Properties.Settings.Default.MaxLat = (double)ud_MaxLat.Value; + UpdateCoverage(); + } + } + + #endregion + + private void UpdateElevationPages() + { + wp_GLOBE.Suppress = !cb_GLOBE.Checked; + wp_SRTM3.Suppress = !cb_SRTM3.Checked; + wp_SRTM1.Suppress = !cb_SRTM1.Checked; + wp_ElevationModel.AllowNext = cb_GLOBE.Checked || cb_SRTM3.Checked || cb_SRTM1.Checked; + this.Refresh(); + } + + private void cb_GLOBE_CheckedChanged(object sender, EventArgs e) + { + Properties.Settings.Default.Elevation_GLOBE_Enabled = cb_GLOBE.Checked; + UpdateElevationPages(); + } + + private void cb_SRTM3_CheckedChanged(object sender, EventArgs e) + { + Properties.Settings.Default.Elevation_SRTM3_Enabled = cb_SRTM3.Checked; + UpdateElevationPages(); + } + + private void cb_SRTM1_CheckedChanged(object sender, EventArgs e) + { + Properties.Settings.Default.Elevation_SRTM1_Enabled = cb_SRTM1.Checked; + UpdateElevationPages(); + } + + #region wp_ElevationModel + private void wp_ElevationModel_Enter(object sender, EventArgs e) + { + cb_GLOBE.Checked = Properties.Settings.Default.Elevation_GLOBE_Enabled; + cb_SRTM3.Checked = Properties.Settings.Default.Elevation_SRTM3_Enabled; + cb_SRTM1.Checked = Properties.Settings.Default.Elevation_SRTM1_Enabled; + UpdateElevationPages(); + } + #endregion + + #region wp_GLOBE + + private void bw_GLOBE_MapUpdater_DoWork(object sender, DoWorkEventArgs e) + { + bw_GLOBE_MapUpdater.ReportProgress(0, "Creating elevation tile catalogue (please wait)..."); + // get all locs needed for covered area + ElevationCatalogue availabletiles = ElevationData.Database.ElevationCatalogueCreateCheckBoundsAndLastModified(ELEVATIONMODEL.GLOBE, Properties.Settings.Default.MinLat, Properties.Settings.Default.MinLon, Properties.Settings.Default.MaxLat, Properties.Settings.Default.MaxLon); + bw_GLOBE_MapUpdater.ReportProgress(0, "Processing tiles..."); + // report tile count + bw_GLOBE_MapUpdater.ReportProgress(2, availabletiles.Files.Keys.Count); + int missing = 0; + int found = 0; + foreach (string tilename in availabletiles.Files.Keys) + { + if (ElevationData.Database.ElevationTileExists(tilename.Substring(0, 6), ELEVATIONMODEL.GLOBE)) + { + bw_GLOBE_MapUpdater.ReportProgress(1, tilename); + found++; + } + else + { + bw_GLOBE_MapUpdater.ReportProgress(-1, tilename); + missing++; + } + if (bw_GLOBE_MapUpdater.CancellationPending) + { + bw_GLOBE_MapUpdater.ReportProgress(0, "Processing cancelled..."); + return; + } + } + bw_GLOBE_MapUpdater.ReportProgress(0, found.ToString() + " tile(s) found, " + missing.ToString() + " more tile(s) available and missing."); + } + + private void bw_GLOBE_MapUpdater_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + if (e.ProgressPercentage == 2) + { + try + { + wp_GLOBE.AllowNext = true; + // get tile count = available tiles - tiles already in data base + long tilecount = (int)e.UserState - ElevationData.Database.ElevationTileCount(ELEVATIONMODEL.GLOBE); + if (tilecount < 0) + tilecount = 0; + // check available disk space and file system + // estimate disk space needed = tilecount * tilesize * 150% + long spaceneeded = (long)tilecount * (long)(250 * 3 / 2); + string rootdrive = Path.GetPathRoot(ElevationData.Database.DefaultDatabaseDirectory(ELEVATIONMODEL.GLOBE)); + if (SupportFunctions.GetDriveAvailableFreeSpace(rootdrive) < spaceneeded) + { + // show message box + MessageBox.Show("Not enough disk space for elevation database.\n\nAvailable: " + SupportFunctions.GetDriveAvailableFreeSpace(rootdrive) + " bytes.\nNeeded: " + spaceneeded + "bytes.\n\nUncheck this option or try to enlarge free space on disk.", "Not enough disk space"); + wp_GLOBE.AllowNext = false; + } + if (SupportFunctions.GetDriveMaxFileSize(rootdrive) < spaceneeded) + { + // show message box + MessageBox.Show("The elevation database will exceed maximum allowed filesize for this file system.\n\nAllowed: " + SupportFunctions.GetDriveMaxFileSize(rootdrive) + " bytes.\nNeeded: " + spaceneeded + "bytes.\n\nUncheck this option or change file system on disk.", "File size exceeded"); + wp_GLOBE.AllowNext = false; + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + else if (e.ProgressPercentage == 1) + { + // add a tile found in database to map polygons + double baselat; + double baselon; + MaidenheadLocator.LatLonFromLoc(((string)e.UserState).Substring(0, 6), PositionInRectangle.BottomLeft, out baselat, out baselon); + List l = new List(); + l.Add(new PointLatLng((decimal)baselat, (decimal)baselon)); + l.Add(new PointLatLng((decimal)(baselat + 1 / 24.0), (decimal)baselon)); + l.Add(new PointLatLng((decimal)(baselat + 1 / 24.0), (decimal)(baselon + 2 / 24.0))); + l.Add(new PointLatLng((decimal)baselat, (decimal)(baselon + 2 / 24.0))); + GMapPolygon p = new GMapPolygon(l, (string)e.UserState); + p.Stroke = new Pen(Color.FromArgb(50, Color.Green)); + p.Fill = new SolidBrush(Color.FromArgb(50, Color.Green)); + GLOBEpolygons.Polygons.Add(p); + } + else if (e.ProgressPercentage == -1) + { + // add missing tile to map polygons + double baselat; + double baselon; + MaidenheadLocator.LatLonFromLoc(((string)e.UserState).Substring(0, 6), PositionInRectangle.BottomLeft, out baselat, out baselon); + List l = new List(); + l.Add(new PointLatLng((decimal)baselat, (decimal)baselon)); + l.Add(new PointLatLng((decimal)(baselat + 1 / 24.0), (decimal)baselon)); + l.Add(new PointLatLng((decimal)(baselat + 1 / 24.0), (decimal)(baselon + 2 / 24.0))); + l.Add(new PointLatLng((decimal)baselat, (decimal)(baselon + 2 / 24.0))); + GMapPolygon p = new GMapPolygon(l, (string)e.UserState); + p.Stroke = new Pen(Color.FromArgb(50, Color.Red)); + p.Fill = new SolidBrush(Color.FromArgb(50, Color.Red)); + GLOBEpolygons.Polygons.Add(p); + } + else + { + lbl_GLOBE_Status.Text = (string)e.UserState; + } + } + + private void bw_GLOBE_MapUpdater_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + } + + private void wp_GLOBE_Enter(object sender, EventArgs e) + { + wp_GLOBE.AllowNext = false; + // clear map polygons + GLOBEpolygons.Clear(); + // add coverage to map polygons + List cl = new List(); + cl.Add(new PointLatLng(Properties.Settings.Default.MinLat, Properties.Settings.Default.MinLon)); + cl.Add(new PointLatLng(Properties.Settings.Default.MinLat, Properties.Settings.Default.MaxLon)); + cl.Add(new PointLatLng(Properties.Settings.Default.MaxLat, Properties.Settings.Default.MaxLon)); + cl.Add(new PointLatLng(Properties.Settings.Default.MaxLat, Properties.Settings.Default.MinLon)); + GMapPolygon c = new GMapPolygon(cl, "Coverage"); + c.Stroke = new Pen(Color.FromArgb(255, Color.Magenta), 3); + c.Fill = new SolidBrush(Color.FromArgb(0, Color.Magenta)); + GLOBEpolygons.Polygons.Add(c); + // zoom the map initally + gm_GLOBE.SetZoomToFitRect(RectLatLng.FromLTRB(Properties.Settings.Default.MinLon, Properties.Settings.Default.MaxLat, Properties.Settings.Default.MaxLon, Properties.Settings.Default.MinLat)); + // start map updater + if (!bw_GLOBE_MapUpdater.IsBusy) + bw_GLOBE_MapUpdater.RunWorkerAsync(); + // zoom the map + gm_GLOBE.SetZoomToFitRect(RectLatLng.FromLTRB(Properties.Settings.Default.MinLon - 1, Properties.Settings.Default.MaxLat + 1, Properties.Settings.Default.MaxLon + 1, Properties.Settings.Default.MinLat - 1)); + } + + private void wp_GLOBE_Leave(object sender, EventArgs e) + { + // stop map updater + bw_GLOBE_MapUpdater.CancelAsync(); + // clear map polygons + GLOBEpolygons.Clear(); + // do garbage collection + GC.Collect(); + lbl_GLOBE_Status.Text = ""; + } + + private void wp_GLOBE_Commit(object sender, AeroWizard.WizardPageConfirmEventArgs e) + { + + } + + #endregion + + #region wp_SRTM3 + + private void bw_SRTM3_MapUpdater_DoWork(object sender, DoWorkEventArgs e) + { + bw_SRTM3_MapUpdater.ReportProgress(0, "Creating elevation tile catalogue (please wait)..."); + // get all locs needed for covered area + ElevationCatalogue availabletiles = ElevationData.Database.ElevationCatalogueCreateCheckBoundsAndLastModified(ELEVATIONMODEL.SRTM3, Properties.Settings.Default.MinLat, Properties.Settings.Default.MinLon, Properties.Settings.Default.MaxLat, Properties.Settings.Default.MaxLon); + bw_SRTM3_MapUpdater.ReportProgress(0, "Processing tiles..."); + // report tile count + bw_SRTM3_MapUpdater.ReportProgress(2, availabletiles.Files.Keys.Count); + int missing = 0; + int found = 0; + foreach (string tilename in availabletiles.Files.Keys) + { + if (ElevationData.Database.ElevationTileExists(tilename.Substring(0, 6), ELEVATIONMODEL.SRTM3)) + { + bw_SRTM3_MapUpdater.ReportProgress(1, tilename); + found++; + } + else + { + bw_SRTM3_MapUpdater.ReportProgress(-1, tilename); + missing++; + } + if (bw_SRTM3_MapUpdater.CancellationPending) + { + bw_SRTM3_MapUpdater.ReportProgress(0, "Processing cancelled..."); + return; + } + } + bw_SRTM3_MapUpdater.ReportProgress(0, found.ToString() + " tile(s) found, " + missing.ToString() + " more tile(s) available and missing."); + } + + private void bw_SRTM3_MapUpdater_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + if (e.ProgressPercentage == 2) + { + try + { + wp_SRTM3.AllowNext = true; + // get tile count = available tiles - tiles already in data base + long tilecount = (int)e.UserState - ElevationData.Database.ElevationTileCount(ELEVATIONMODEL.SRTM3); + if (tilecount < 0) + tilecount = 0; + // check available disk space and file system + // estimate disk space needed = tilecount * tilesize * 150% + long spaceneeded = (long)tilecount * (long)(10000 * 3 / 2); + string rootdrive = Path.GetPathRoot(ElevationData.Database.DefaultDatabaseDirectory(ELEVATIONMODEL.SRTM3)); + if (SupportFunctions.GetDriveAvailableFreeSpace(rootdrive) < spaceneeded) + { + // show message box + MessageBox.Show("Not enough disk space for elevation database.\n\nAvailable: " + SupportFunctions.GetDriveAvailableFreeSpace(rootdrive) + " bytes.\nNeeded: " + spaceneeded + "bytes.\n\nUncheck this option or try to enlarge free space on disk.", "Not enough disk space"); + wp_SRTM3.AllowNext = false; + } + if (SupportFunctions.GetDriveMaxFileSize(rootdrive) < spaceneeded) + { + // show message box + MessageBox.Show("The elevation database will exceed maximum allowed filesize for this file system.\n\nAllowed: " + SupportFunctions.GetDriveMaxFileSize(rootdrive) + " bytes.\nNeeded: " + spaceneeded + "bytes.\n\nUncheck this option or change file system on disk.", "File size exceeded"); + wp_SRTM3.AllowNext = false; + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + else if (e.ProgressPercentage == 1) + { + // add a tile found in database to map polygons + double baselat; + double baselon; + MaidenheadLocator.LatLonFromLoc(((string)e.UserState).Substring(0, 6), PositionInRectangle.BottomLeft, out baselat, out baselon); + List l = new List(); + l.Add(new PointLatLng((decimal)baselat, (decimal)baselon)); + l.Add(new PointLatLng((decimal)(baselat + 1 / 24.0), (decimal)baselon)); + l.Add(new PointLatLng((decimal)(baselat + 1 / 24.0), (decimal)(baselon + 2 / 24.0))); + l.Add(new PointLatLng((decimal)baselat, (decimal)(baselon + 2 / 24.0))); + GMapPolygon p = new GMapPolygon(l, (string)e.UserState); + p.Stroke = new Pen(Color.FromArgb(50, Color.Green)); + p.Fill = new SolidBrush(Color.FromArgb(50, Color.Green)); + SRTM3polygons.Polygons.Add(p); + } + else if (e.ProgressPercentage == -1) + { + // add missing tile to map polygons + double baselat; + double baselon; + MaidenheadLocator.LatLonFromLoc(((string)e.UserState).Substring(0, 6), PositionInRectangle.BottomLeft, out baselat, out baselon); + List l = new List(); + l.Add(new PointLatLng((decimal)baselat, (decimal)baselon)); + l.Add(new PointLatLng((decimal)(baselat + 1 / 24.0), (decimal)baselon)); + l.Add(new PointLatLng((decimal)(baselat + 1 / 24.0), (decimal)(baselon + 2 / 24.0))); + l.Add(new PointLatLng((decimal)baselat, (decimal)(baselon + 2 / 24.0))); + GMapPolygon p = new GMapPolygon(l, (string)e.UserState); + p.Stroke = new Pen(Color.FromArgb(50, Color.Red)); + p.Fill = new SolidBrush(Color.FromArgb(50, Color.Red)); + SRTM3polygons.Polygons.Add(p); + } + else + { + lbl_SRTM3_Status.Text = (string)e.UserState; + } + } + + private void bw_SRTM3_MapUpdater_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + } + + private void wp_SRTM3_Enter(object sender, EventArgs e) + { + wp_SRTM3.AllowNext = false; + // clear map polygons + SRTM3polygons.Clear(); + // add coverage to map polygons + List cl = new List(); + cl.Add(new PointLatLng(Properties.Settings.Default.MinLat, Properties.Settings.Default.MinLon)); + cl.Add(new PointLatLng(Properties.Settings.Default.MinLat, Properties.Settings.Default.MaxLon)); + cl.Add(new PointLatLng(Properties.Settings.Default.MaxLat, Properties.Settings.Default.MaxLon)); + cl.Add(new PointLatLng(Properties.Settings.Default.MaxLat, Properties.Settings.Default.MinLon)); + GMapPolygon c = new GMapPolygon(cl, "Coverage"); + c.Stroke = new Pen(Color.FromArgb(255, Color.Magenta), 3); + c.Fill = new SolidBrush(Color.FromArgb(0, Color.Magenta)); + SRTM3polygons.Polygons.Add(c); + // zoom the map initally + gm_SRTM3.SetZoomToFitRect(RectLatLng.FromLTRB(Properties.Settings.Default.MinLon, Properties.Settings.Default.MaxLat, Properties.Settings.Default.MaxLon, Properties.Settings.Default.MinLat)); + // start map updater + if (!bw_SRTM3_MapUpdater.IsBusy) + bw_SRTM3_MapUpdater.RunWorkerAsync(); + // zoom the map + gm_SRTM3.SetZoomToFitRect(RectLatLng.FromLTRB(Properties.Settings.Default.MinLon - 1, Properties.Settings.Default.MaxLat + 1, Properties.Settings.Default.MaxLon + 1, Properties.Settings.Default.MinLat - 1)); + } + + private void wp_SRTM3_Leave(object sender, EventArgs e) + { + // stop map updater + bw_SRTM3_MapUpdater.CancelAsync(); + // clear map polygons + SRTM3polygons.Clear(); + // do garbage collection + GC.Collect(); + lbl_SRTM3_Status.Text = ""; + } + + #endregion + + #region wp_SRTM1 + + private void bw_SRTM1_MapUpdater_DoWork(object sender, DoWorkEventArgs e) + { + bw_SRTM1_MapUpdater.ReportProgress(0, "Creating elevation tile catalogue (please wait)..."); + // get all locs needed for covered area + ElevationCatalogue availabletiles = ElevationData.Database.ElevationCatalogueCreateCheckBoundsAndLastModified(ELEVATIONMODEL.SRTM1, Properties.Settings.Default.MinLat, Properties.Settings.Default.MinLon, Properties.Settings.Default.MaxLat, Properties.Settings.Default.MaxLon); + bw_SRTM1_MapUpdater.ReportProgress(0, "Processing tiles..."); + // report tile count + bw_SRTM1_MapUpdater.ReportProgress(2, availabletiles.Files.Keys.Count); + int missing = 0; + int found = 0; + foreach (string tilename in availabletiles.Files.Keys) + { + if (ElevationData.Database.ElevationTileExists(tilename.Substring(0, 6), ELEVATIONMODEL.SRTM1)) + { + bw_SRTM1_MapUpdater.ReportProgress(1, tilename); + found++; + } + else + { + bw_SRTM1_MapUpdater.ReportProgress(-1, tilename); + missing++; + } + if (bw_SRTM1_MapUpdater.CancellationPending) + { + bw_SRTM1_MapUpdater.ReportProgress(0, "Processing cancelled..."); + return; + } + } + bw_SRTM1_MapUpdater.ReportProgress(0, found.ToString() + " tile(s) found, " + missing.ToString() + " more tile(s) available and missing."); + } + + private void bw_SRTM1_MapUpdater_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + if (e.ProgressPercentage == 2) + { + try + { + wp_SRTM1.AllowNext = true; + // get tile count = available tiles - tiles already in data base + long tilecount = (int)e.UserState - ElevationData.Database.ElevationTileCount(ELEVATIONMODEL.SRTM1); + if (tilecount < 0) + tilecount = 0; + // check available disk space and file system + // estimate disk space needed = tilecount * tilesize * 150% + long spaceneeded = (long)tilecount * (long)(100000 * 3 / 2); + string rootdrive = Path.GetPathRoot(ElevationData.Database.DefaultDatabaseDirectory(ELEVATIONMODEL.SRTM1)); + if (SupportFunctions.GetDriveAvailableFreeSpace(rootdrive) < spaceneeded) + { + // show message box + MessageBox.Show("Not enough disk space for elevation database.\n\nAvailable: " + SupportFunctions.GetDriveAvailableFreeSpace(rootdrive) + " bytes.\nNeeded: " + spaceneeded + "bytes.\n\nUncheck this option or try to enlarge free space on disk.", "Not enough disk space"); + wp_SRTM1.AllowNext = false; + } + if (SupportFunctions.GetDriveMaxFileSize(rootdrive) < spaceneeded) + { + // show message box + MessageBox.Show("The elevation database will exceed maximum allowed filesize for this file system.\n\nAllowed: " + SupportFunctions.GetDriveMaxFileSize(rootdrive) + " bytes.\nNeeded: " + spaceneeded + "bytes.\n\nUncheck this option or change file system on disk.", "File size exceeded"); + wp_SRTM1.AllowNext = false; + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + else if (e.ProgressPercentage == 1) + { + // add a tile found in database to map polygons + double baselat; + double baselon; + MaidenheadLocator.LatLonFromLoc(((string)e.UserState).Substring(0, 6), PositionInRectangle.BottomLeft, out baselat, out baselon); + List l = new List(); + l.Add(new PointLatLng((decimal)baselat, (decimal)baselon)); + l.Add(new PointLatLng((decimal)(baselat + 1 / 24.0), (decimal)baselon)); + l.Add(new PointLatLng((decimal)(baselat + 1 / 24.0), (decimal)(baselon + 2 / 24.0))); + l.Add(new PointLatLng((decimal)baselat, (decimal)(baselon + 2 / 24.0))); + GMapPolygon p = new GMapPolygon(l, (string)e.UserState); + p.Stroke = new Pen(Color.FromArgb(50, Color.Green)); + p.Fill = new SolidBrush(Color.FromArgb(50, Color.Green)); + SRTM1polygons.Polygons.Add(p); + } + else if (e.ProgressPercentage == -1) + { + // add missing tile to map polygons + double baselat; + double baselon; + MaidenheadLocator.LatLonFromLoc(((string)e.UserState).Substring(0, 6), PositionInRectangle.BottomLeft, out baselat, out baselon); + List l = new List(); + l.Add(new PointLatLng((decimal)baselat, (decimal)baselon)); + l.Add(new PointLatLng((decimal)(baselat + 1 / 24.0), (decimal)baselon)); + l.Add(new PointLatLng((decimal)(baselat + 1 / 24.0), (decimal)(baselon + 2 / 24.0))); + l.Add(new PointLatLng((decimal)baselat, (decimal)(baselon + 2 / 24.0))); + GMapPolygon p = new GMapPolygon(l, (string)e.UserState); + p.Stroke = new Pen(Color.FromArgb(50, Color.Red)); + p.Fill = new SolidBrush(Color.FromArgb(50, Color.Red)); + SRTM1polygons.Polygons.Add(p); + } + else + { + lbl_SRTM1_Status.Text = (string)e.UserState; + } + } + + private void bw_SRTM1_MapUpdater_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + } + + private void wp_SRTM1_Enter(object sender, EventArgs e) + { + wp_SRTM1.AllowNext = false; + // clear map polygons + SRTM1polygons.Clear(); + // add coverage to map polygons + List cl = new List(); + cl.Add(new PointLatLng(Properties.Settings.Default.MinLat, Properties.Settings.Default.MinLon)); + cl.Add(new PointLatLng(Properties.Settings.Default.MinLat, Properties.Settings.Default.MaxLon)); + cl.Add(new PointLatLng(Properties.Settings.Default.MaxLat, Properties.Settings.Default.MaxLon)); + cl.Add(new PointLatLng(Properties.Settings.Default.MaxLat, Properties.Settings.Default.MinLon)); + GMapPolygon c = new GMapPolygon(cl, "Coverage"); + c.Stroke = new Pen(Color.FromArgb(255, Color.Magenta), 3); + c.Fill = new SolidBrush(Color.FromArgb(0, Color.Magenta)); + SRTM1polygons.Polygons.Add(c); + // zoom the map initally + gm_SRTM1.SetZoomToFitRect(RectLatLng.FromLTRB(Properties.Settings.Default.MinLon, Properties.Settings.Default.MaxLat, Properties.Settings.Default.MaxLon, Properties.Settings.Default.MinLat)); + // start map updater + if (!bw_SRTM1_MapUpdater.IsBusy) + bw_SRTM1_MapUpdater.RunWorkerAsync(); + // zoom the map + gm_SRTM1.SetZoomToFitRect(RectLatLng.FromLTRB(Properties.Settings.Default.MinLon - 1, Properties.Settings.Default.MaxLat + 1, Properties.Settings.Default.MaxLon + 1, Properties.Settings.Default.MinLat - 1)); + } + + private void wp_SRTM1_Leave(object sender, EventArgs e) + { + // stop map updater + bw_SRTM1_MapUpdater.CancelAsync(); + // clear map polygons + SRTM1polygons.Clear(); + // do garbage collection + GC.Collect(); + lbl_SRTM1_Status.Text = ""; + } + + #endregion + + #region wp_Finish + private void wp_Finish_Enter(object sender, EventArgs e) + { + lbl_Finish.Text = "All inputs are done successfully. You can start AirScout now.\n\nIn case of missing elevation data the download will start immediately.\n\nFull funtionality will not be reached until\nthe database is up to date."; + } + #endregion + + #region wp_UserDetails + + private void wp_UserDetails_Enter(object sender, EventArgs e) + { + // initially set textboxes + tb_Callsign.SilentText = Properties.Settings.Default.MyCall; + tb_Latitude.SilentValue = Properties.Settings.Default.MyLat; + tb_Longitude.SilentValue = Properties.Settings.Default.MyLon; + tb_Locator.SilentText = MaidenheadLocator.LocFromLatLon(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, Properties.Settings.Default.Locator_SmallLettersForSubsquares, (int)Properties.Settings.Default.Locator_MaxLength / 2, Properties.Settings.Default.Locator_AutoLength); + ValidateMyDetails(); + } + + private bool ValidateMyDetails() + { + // validates user details and sets position on map + // enables/disables next button + double mlat, mlon; + // colour Textbox if more precise lat/lon information is available + if (MaidenheadLocator.IsPrecise(tb_Latitude.Value, tb_Longitude.Value, 3)) + { + if (tb_Locator.BackColor != Color.PaleGreen) + tb_Locator.BackColor = Color.PaleGreen; + } + else + { + if (tb_Locator.BackColor != Color.FloralWhite) + tb_Locator.BackColor = Color.FloralWhite; + } + if (GeographicalPoint.Check(tb_Latitude.Value, tb_Longitude.Value)) + { + // update locator text if not focusd + if (!tb_Locator.Focused) + { + tb_Locator.SilentText = MaidenheadLocator.LocFromLatLon(tb_Latitude.Value, tb_Longitude.Value, Properties.Settings.Default.Locator_SmallLettersForSubsquares, (int)Properties.Settings.Default.Locator_MaxLength / 2, true); + } + // get locator polygon + Callsignpolygons.Clear(); + List l = new List(); + // add loc bounds to map polygons + MaidenheadLocator.LatLonFromLoc(tb_Locator.Text, PositionInRectangle.TopLeft, out mlat, out mlon); + l.Add(new PointLatLng(mlat, mlon)); + MaidenheadLocator.LatLonFromLoc(tb_Locator.Text, PositionInRectangle.TopRight, out mlat, out mlon); + l.Add(new PointLatLng(mlat, mlon)); + MaidenheadLocator.LatLonFromLoc(tb_Locator.Text, PositionInRectangle.BottomRight, out mlat, out mlon); + l.Add(new PointLatLng(mlat, mlon)); + MaidenheadLocator.LatLonFromLoc(tb_Locator.Text, PositionInRectangle.BottomLeft, out mlat, out mlon); + l.Add(new PointLatLng(mlat, mlon)); + GMapPolygon p = new GMapPolygon(l, tb_Locator.Text.ToString()); + p.Stroke = new Pen(Color.FromArgb(255, Color.Magenta), 3); + p.Fill = new SolidBrush(Color.FromArgb(0, Color.Magenta)); + Callsignpolygons.Polygons.Add(p); + // update user position + UserPos.Position = new PointLatLng(tb_Latitude.Value, tb_Longitude.Value); + // update map position + if (!IsDraggingMarker) + { + string loc = MaidenheadLocator.LocFromLatLon(tb_Latitude.Value, tb_Longitude.Value, Properties.Settings.Default.Locator_SmallLettersForSubsquares, (int)Properties.Settings.Default.Locator_MaxLength / 2, true); + MaidenheadLocator.LatLonFromLoc(loc, PositionInRectangle.MiddleMiddle, out mlat, out mlon); + gm_Callsign.Position = new PointLatLng(mlat, mlon); + // adjust map zoom level + int zoom = loc.Length; + switch (zoom) + { + case 6: gm_Callsign.Zoom = 12; + break; + case 8: gm_Callsign.Zoom = 15; + break; + case 10: gm_Callsign.Zoom = 17; + break; + } + } + } + // check all values and enable/disable next button + if (Callsign.Check(tb_Callsign.Text) && MaidenheadLocator.Check(tb_Locator.Text) && !double.IsNaN(tb_Latitude.Value) && !double.IsNaN(tb_Longitude.Value)) + { + // save settings + Properties.Settings.Default.MyCall = tb_Callsign.Text; + Properties.Settings.Default.MyLat = tb_Latitude.Value; + Properties.Settings.Default.MyLon = tb_Longitude.Value; + // StationData.Database.LocationInsertOrUpdateIfNewer(new LocationDesignator(tb_Callsign.Text, tb_Latitude.Value, tb_Longitude.Value, (MaidenheadLocator.IsPrecise(tb_Latitude.Value, tb_Longitude.Value, 3) ? GEOSOURCE.FROMUSER : GEOSOURCE.FROMLOC))); + wp_UserDetails.AllowNext = true; + return true; + } + else + { + wp_UserDetails.AllowNext = false; + return false; + } + } + + private void tb_Callsign_TextChanged(object sender, EventArgs e) + { + if (tb_Callsign.Focused && Callsign.Check(tb_Callsign.Text)) + { + LocationDesignator ld = StationData.Database.LocationFind(tb_Callsign.Text); + if (ld != null) + { + tb_Latitude.SilentValue = ld.Lat; + tb_Longitude.SilentValue = ld.Lon; + ValidateMyDetails(); + return; + } + else + { + tb_Latitude.SilentValue = double.NaN; + tb_Longitude.SilentValue = double.NaN; + tb_Locator.SilentText = ""; + } + } + ValidateMyDetails(); + } + + private void tb_Latitude_TextChanged(object sender, EventArgs e) + { + ValidateMyDetails(); + } + + private void tb_Longitude_TextChanged(object sender, EventArgs e) + { + ValidateMyDetails(); + } + + private void tb_Locator_TextChanged(object sender, EventArgs e) + { + // update lat/lon + double mlat, mlon; + if (tb_Locator.Focused) + { + // locator box is focused --> update lat/lon + if (MaidenheadLocator.Check(tb_Locator.Text) && tb_Locator.Text.Length >= 6) + { + MaidenheadLocator.LatLonFromLoc(tb_Locator.Text, PositionInRectangle.MiddleMiddle, out mlat, out mlon); + tb_Latitude.SilentValue = mlat; + tb_Longitude.SilentValue = mlon; + } + else + { + tb_Latitude.SilentValue = double.NaN; + tb_Longitude.SilentValue = double.NaN; + } + } + ValidateMyDetails(); + } + + private void gm_Callsign_MouseDown(object sender, MouseEventArgs e) + { + if (e.Button == MouseButtons.Left && (UserPos != null && UserPos.IsMouseOver)) + { + // dummy set user position to set mouse position exact to marker's location + UserPos.Position = UserPos.Position; + gm_Callsign.CanDragMap = false; + IsDraggingMarker = true; + } + } + + private void gm_Callsign_MouseMove(object sender, MouseEventArgs e) + { + if ((UserPos != null) && IsDraggingMarker) + { + if (Callsign.Check(tb_Callsign.Text)) + { + // get geographic coordinates of mouse pointer + PointLatLng p = gm_Callsign.FromLocalToLatLng(e.X, e.Y); + tb_Latitude.SilentValue = p.Lat; + tb_Longitude.SilentValue = p.Lng; + UserPos.ToolTipMode = MarkerTooltipMode.OnMouseOver; + UserPos.ToolTipText = tb_Callsign.Text; + Properties.Settings.Default.MyLat = p.Lat; + Properties.Settings.Default.MyLon = p.Lng; + ValidateMyDetails(); + } + else + { + UserPos.ToolTipMode = MarkerTooltipMode.OnMouseOver; + UserPos.ToolTipText = "Please enter a valid callsign first."; + } + } + } + + private void gm_Callsign_MouseUp(object sender, MouseEventArgs e) + { + if (IsDraggingMarker) + { + gm_Callsign.CanDragMap = true; + IsDraggingMarker = false; + } + } + + private void gm_Callsign_OnMarkerEnter(GMapMarker item) + { + } + + private void btn_QRZ_Click(object sender, EventArgs e) + { + // get an EN format provider + NumberFormatInfo provider = new NumberFormatInfo(); + provider.NumberDecimalSeparator = "."; + provider.NumberGroupSeparator = ","; + try + { + WebRequest myWebRequest = WebRequest.Create(Properties.Settings.Default.QRZ_URL_Database + Properties.Settings.Default.MyCall); + myWebRequest.Timeout = 10000; + WebResponse myWebResponse = myWebRequest.GetResponse(); + Stream ReceiveStream = myWebResponse.GetResponseStream(); + Encoding encode = System.Text.Encoding.GetEncoding("utf-8"); + StreamReader readStream = new StreamReader(ReceiveStream, encode); + string s = readStream.ReadToEnd(); + if (s.IndexOf("cs_lat") >= 0) + { + string loc; + double lat = 0; + double lon = 0; + try + { + s = s.Remove(0, s.IndexOf("cs_lat = \"") + 10); + lat = System.Convert.ToDouble(s.Substring(0, s.IndexOf("\n") - 2), provider); + s = s.Remove(0, s.IndexOf("cs_lon = \"") + 10); + lon = System.Convert.ToDouble(s.Substring(0, s.IndexOf("\n") - 2), provider); + } + catch + { + } + loc = MaidenheadLocator.LocFromLatLon(lat, lon, false, 3); + // check if loc is matching --> refine lat/lon + if ((tb_Locator.Text.Length >= 6) && (loc == MaidenheadLocator.Convert(tb_Locator.Text, false).Substring(0, 6))) + { + Properties.Settings.Default["MyLat"] = lat; + Properties.Settings.Default.MyLon = lon; + tb_Latitude.Value = lat; + tb_Longitude.Value = lon; + MessageBox.Show("Position update from QRZ.com was performed succesfully.", "QRZ.com"); + return; + } + } + } + catch + { + } + MessageBox.Show("Position query at QRZ.com failed or does not match with current locator. \nNo update was performed.", "QRZ.com"); + + } + + private void btn_Zoom_In_Click(object sender, EventArgs e) + { + if (gm_Callsign.Zoom < 20) + gm_Callsign.Zoom++; + } + + private void btn_Zoom_Out_Click(object sender, EventArgs e) + { + if (gm_Callsign.Zoom > 0) + gm_Callsign.Zoom--; + } + + private void gm_Callsign_OnMapZoomChanged() + { + // maintain zoom level + tb_Zoom.Text = gm_Callsign.Zoom.ToString(); + } + + #endregion + + #region wp_PlaneFeeds + private void ValidatePlaneFeeds() + { + if ((cb_PlaneFeed1.SelectedItem != null) && (cb_PlaneFeed2.SelectedItem != null) && (cb_PlaneFeed3.SelectedItem != null) && (cb_PlaneFeed1.SelectedItem.ToString() == "[none]") && (cb_PlaneFeed2.SelectedItem.ToString() == "[none]") && (cb_PlaneFeed3.SelectedItem.ToString() == "[none]")) + wp_PlaneFeeds.AllowNext = false; + else + wp_PlaneFeeds.AllowNext = true; + } + + private void wp_PlaneFeeds_Enter(object sender, EventArgs e) + { + // set initial settings for planes tab + cb_PlaneFeed1.DisplayMember = "Name"; + cb_PlaneFeed2.DisplayMember = "Name"; + cb_PlaneFeed3.DisplayMember = "Name"; + cb_PlaneFeed1.Items.Clear(); + cb_PlaneFeed2.Items.Clear(); + cb_PlaneFeed3.Items.Clear(); + cb_PlaneFeed1.Items.Add("[none]"); + cb_PlaneFeed2.Items.Add("[none]"); + cb_PlaneFeed3.Items.Add("[none]"); + ArrayList feeds = new PlaneFeedEnumeration().EnumFeeds(); + foreach (PlaneFeed feed in feeds) + { + cb_PlaneFeed1.Items.Add(feed); + cb_PlaneFeed2.Items.Add(feed); + cb_PlaneFeed3.Items.Add(feed); + } + cb_PlaneFeed1.SelectedIndex = cb_PlaneFeed1.FindStringExact(Properties.Settings.Default.Planes_PlaneFeed1); + cb_PlaneFeed2.SelectedIndex = cb_PlaneFeed1.FindStringExact(Properties.Settings.Default.Planes_PlaneFeed2); + cb_PlaneFeed3.SelectedIndex = cb_PlaneFeed1.FindStringExact(Properties.Settings.Default.Planes_PlaneFeed3); + ValidatePlaneFeeds(); + } + + private void cb_PlaneFeed1_SelectedIndexChanged(object sender, EventArgs e) + { + if ((cb_PlaneFeed1.SelectedItem == null) || (cb_PlaneFeed1.GetItemText(cb_PlaneFeed1.SelectedItem) == "[none]")) + { + Properties.Settings.Default.Planes_PlaneFeed1 = "[none]"; + ValidatePlaneFeeds(); + return; + } + PlaneFeed feed = (PlaneFeed)cb_PlaneFeed1.SelectedItem; + // show disclaimer if necessary + if (!String.IsNullOrEmpty(feed.Disclaimer) && (String.IsNullOrEmpty(feed.DisclaimerAccepted))) + { + PlaneFeedDisclaimerDlg Dlg = new PlaneFeedDisclaimerDlg(); + Dlg.tb_DisclaimerText.Text = feed.Disclaimer; + if (Dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) + { + // making a unique ID for confirmation + string ID = ""; + try + { + ID = (string)Registry.GetValue("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion", "ProductId", ""); + } + catch + { + ID = "Key not found!"; + } + ID = ID + "," + DateTime.UtcNow.ToString("u"); + ID = ID + "," + System.Security.Principal.WindowsIdentity.GetCurrent().Name; + feed.DisclaimerAccepted = ID; + } + else + { + cb_PlaneFeed1.SelectedItem = "[none]"; + } + } + Properties.Settings.Default.Planes_PlaneFeed1 = feed.Name; + ValidatePlaneFeeds(); + } + + private void cb_PlaneFeed2_SelectedIndexChanged(object sender, EventArgs e) + { + if ((cb_PlaneFeed2.SelectedItem == null) || (cb_PlaneFeed2.GetItemText(cb_PlaneFeed2.SelectedItem) == "[none]")) + { + Properties.Settings.Default.Planes_PlaneFeed2 = "[none]"; + ValidatePlaneFeeds(); + return; + } + PlaneFeed feed = (PlaneFeed)cb_PlaneFeed2.SelectedItem; + // show disclaimer if necessary + if (!String.IsNullOrEmpty(feed.Disclaimer) && (String.IsNullOrEmpty(feed.DisclaimerAccepted))) + { + PlaneFeedDisclaimerDlg Dlg = new PlaneFeedDisclaimerDlg(); + Dlg.tb_DisclaimerText.Text = feed.Disclaimer; + if (Dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) + { + // making a unique ID for confirmation + string ID = ""; + try + { + ID = (string)Registry.GetValue("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion", "ProductId", ""); + } + catch + { + ID = "Key not found!"; + } + ID = ID + "," + DateTime.UtcNow.ToString("u"); + ID = ID + "," + System.Security.Principal.WindowsIdentity.GetCurrent().Name; + feed.DisclaimerAccepted = ID; + } + } + Properties.Settings.Default.Planes_PlaneFeed2 = feed.Name; + ValidatePlaneFeeds(); + } + + private void cb_PlaneFeed3_SelectedIndexChanged(object sender, EventArgs e) + { + if ((cb_PlaneFeed3.SelectedItem == null) || (cb_PlaneFeed3.GetItemText(cb_PlaneFeed3.SelectedItem) == "[none]")) + { + Properties.Settings.Default.Planes_PlaneFeed3 = "[none]"; + ValidatePlaneFeeds(); + return; + } + PlaneFeed feed = (PlaneFeed)cb_PlaneFeed3.SelectedItem; + // show disclaimer if necessary + if (!String.IsNullOrEmpty(feed.Disclaimer) && (String.IsNullOrEmpty(feed.DisclaimerAccepted))) + { + PlaneFeedDisclaimerDlg Dlg = new PlaneFeedDisclaimerDlg(); + Dlg.tb_DisclaimerText.Text = feed.Disclaimer; + if (Dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) + { + // making a unique ID for confirmation + string ID = ""; + try + { + ID = (string)Registry.GetValue("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion", "ProductId", ""); + } + catch + { + ID = "Key not found!"; + } + ID = ID + "," + DateTime.UtcNow.ToString("u"); + ID = ID + "," + System.Security.Principal.WindowsIdentity.GetCurrent().Name; + feed.DisclaimerAccepted = ID; + } + } + Properties.Settings.Default.Planes_PlaneFeed3 = feed.Name; + ValidatePlaneFeeds(); + } + #endregion + + + private void FirstRunWizard_FormClosing(object sender, FormClosingEventArgs e) + { + lbl_Finish.Text = "Please wait for download of last tile is finished..."; + bw_GLOBE_MapUpdater.CancelAsync(); + bw_SRTM3_MapUpdater.CancelAsync(); + bw_SRTM1_MapUpdater.CancelAsync(); + while (bw_GLOBE_MapUpdater.IsBusy) + Application.DoEvents(); + while (bw_SRTM3_MapUpdater.IsBusy) + Application.DoEvents(); + while (bw_SRTM1_MapUpdater.IsBusy) + Application.DoEvents(); + } + } + + + + public enum TILEDOWNLOADSTATUS + { + ERROR = -1, + INIT = 0, + DOWNLOADING = 2, + DOWNLOADED = 3, + UPTODATE = 4 + } +} diff --git a/AirScout/FirstRunWizard.resx b/AirScout/FirstRunWizard.resx new file mode 100644 index 0000000..04653f7 --- /dev/null +++ b/AirScout/FirstRunWizard.resx @@ -0,0 +1,14614 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + +++ TERMS AND CONDITIONS +++ + +Copyright (c) 2018 by DL2ALF + +AirScout is free for personal ham radio related use. You may free distribute the package without changes. It has been developed using C# and Microsoft Visual Studio Express. It is released under the GNU Public Licence V3, the source code is available on request. It could not have been developed without the great help of Open Source software found on the Internet for almost all presentation and calculation tasks inside the software. You may use it "as it is" without any warranties. For further information see the disclaimer on the "Options/Info" tab. + ++++ PRIVACY POLICY +++ + +AirScout is retrieveing user information to maintain a station database. Therefore a minimum of personal data will be stored on the AirScout server. Most of them is publically available. Please support the community with details about your station and your QTH. + +The following user information is collected (as stored in your program settings): + + - your call sign + - your latitude + - your longitude + - your grid square + - your elevation + - your antenna height (per band + - your antenna gain (per band) + - your power output (per band) + - a timestamp (UTC) + +The data are uploaded either at program startup or on user action. The transfer of the collected data is encrypted via Secure FTP (SFTP) to the AirScout web server. +All information is kept confidental and is used only to maintain the global station database and for statistical purposes. +In case of any change an update is redistributed to all AirScout users. +No further user tracking, analysis or advertisement is performed. + ++++ LIABILITY DISCLAIMER +++ + +THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +You must agree to these terms and conditions to run AirScout. +To view complete license information please refer to "Options/Info". + + + Elevation models are essential for work with AirScout. They are needed for calculating radio horizons and propagation paths. +The software can handle the three most common models available for the public: + +GLOBE: 1km x 1km resolution, covers whole earth +SRTM3: 90m x 90m resolution, covers only -60° ... +60° +SRTM1: 30m x 30m resolution, covers only -60° ... +60° + +Elevation data are provided in files. AirScout is trying to download and stores them locally. If you have them already somewhere on your computer, you can select a user specific path. + +You must choose at least one elevation model to continue. + + + Update your user details here. Exact lat/long values are essential for calculation of the radio horizon. +Start with entering a valid callsign into the callsign box. +Use the lat/lon textboxes for numeric input or enter a valid Maidenhead Locator. +Drag the needle on the map for exact location. Use mouse wheel to zoom in and out. + + + Plane feeds are necessary to get aircraft positions in real time. +You can choose between web feeds, local feeds or dummy feed only for demonstration. +You can combine up to three different feeds. + +Please enter at least one plane feed to continue. + + + + + AAABAAkAAAAAAAEAIAAoIAQAlgAAAICAAAABACAAKAgBAL4gBABgYAAAAQAgAKiUAADmKAUASEgAAAEA + IACIVAAAjr0FAEBAAAABACAAKEIAABYSBgAwMAAAAQAgAKglAAA+VAYAICAAAAEAIACoEAAA5nkGABgY + AAABACAAiAkAAI6KBgAQEAAAAQAgAGgEAAAWlAYAKAAAAAABAAAAAgAAAQAgAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRz + cgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBw + cABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJ + CQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9v + bwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFx + cQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGho + aAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNy + cgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1s + awBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFx + cABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBv + cABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNz + dABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNy + cwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1t + bABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxs + bQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9u + bgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBw + cABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4 + dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNy + cgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5t + bAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1t + bABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1s + bABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBw + bwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBw + cABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNz + cwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1t + bQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1s + bABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1t + bQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFw + cABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFx + cQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJx + cgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBw + cABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5u + bQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1t + bABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRz + cgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBw + cABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJ + CQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9v + bwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFx + cQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGho + aAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNy + cgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1s + awBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFx + cABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBv + cABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNz + dABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAAlIMAAJSDAACUg + wAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAAlIMAAJSDAACUg + wAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAAlIMAAJSDAACUg + wAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNy + cwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1t + bABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxs + bQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9u + bgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBw + cABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4 + dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNy + cgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5t + bAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1t + bABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1s + bABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBw + bwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBw + cABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNz + cwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1t + bQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1s + bABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1t + bQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFw + cABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFx + cQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJx + cgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBw + cABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5u + bQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1t + bABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRz + cgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBw + cABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJ + CQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9v + bwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFx + cQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGho + aAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNy + cgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1s + awBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFx + cABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBv + cABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNz + dABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNy + cwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1t + bABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxs + bQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9u + bgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBw + cABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4 + dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNy + cgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5t + bAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQAAABaAAAAlAAAALgAAAC/AAAAvgAAAL8AAAC+AAAApAAA + AIIAAABVAAAALQAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1t + bABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1s + bABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAGgAAAHIAAADHAAAA9gAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA9QAAANMAAACVAAAARgAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAD///8A////ACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBw + bwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBw + cABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAJAAAAdgAAAOMAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADIAAAAWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA////AP///wD///8AJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNz + cwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1t + bQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAyAAAAxQAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAACoAAAAGgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP///wD///8A////AP///wAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1s + bABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1t + bQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAABcAAAA7AAAAP8AAAD/AQMA/wUHEv8MDDD/ExVB/xUYUP8VGFD/EhQ//xET + Of8JCCn/BQcV/wUHBv8AAQD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAANEAAAAkAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////ACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFw + cABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFx + cQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAB4AAAA/wAAAP8EBgT/EhRO/x4dnf8lI8//Jh7r/ycg7f8oIfD/JyHv/ycg + 7f8nIOv/Jh7q/yUj1P8kIrf/HBqR/xUYVf8ICRj/AAEA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA0AAA + ABUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8AJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJx + cgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBw + cABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAACBAAAA/wECAP8SFEj/IyG4/ygh8v8mHvb/JRzu/yUe6f8lHej/JR3p/yUd + 6f8lHOr/Ixrs/yIa7v8hGPP/IRj4/yMa/P8kHPn/Ix7b/xoah/8KCyH/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAACjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5u + bQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1t + bABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAA + AAAAAAAAAAAAAAAAAAAAAACAAAAA/wcKEP8dHov/KCHw/yYd9P8lHej/JR3n/yUd5/8lHej/Ixvr/yEZ + 7f8gF+//Hxvo/yUi2v8tKsv/NDnA/zk+uP84Pbv/MzfB/ywn0/8oJOz/IR/d/xETYv8AAQD/AAAA/wAA + AP8AAAD/AAAA/gAAAEcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////ACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRz + cgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBw + cABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJ + CQAAAAAAAAAAAAAAAAAAAABrAAAA/w0PJf8kIrv/JyD3/yUd6v8lHef/JBzp/yIa7P8gGO7/Hxnq/yUi + 3P8wMMb/RUms/2Von/+Gi5z/qKml/7q7tP/Dxbz/w8S8/7q7s/+lpqP/g4ea/15ho/89QcP/HCF3/wAA + BP8AAAD/AAAA/wAAAP8AAADGAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP// + /wD///8AJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9v + bwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFx + cQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGho + aAAJCQkAAAAAAAAAAAAAAABbAAAA/A8SNP8mIdD/Jhz2/yMa6/8hGe3/Hxfu/yAd5P8qJ8//Oz+0/11g + nf+Bh5n/q6yn/9HSxv/s6+D////5//////////////////////////////////389//j49f/t7mw/3V6 + n/8qLkf/AAAA/wAAAP8AAAD/AAAA/wAAAEcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gACYe4AAmHuAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNy + cgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1s + awBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFx + cABoaGgACQkJAAAAAAAAAABFAAAA8g8TPf8kH97/IRj5/x4Z6v8kIdv/MTHE/0pOp/9wc5n/mZ2f/8bH + u//k5Nj//Pv1//////////////////////////////////////////////////////////////////// + ///t7ef/p6eo/0tLS/8NDQ3/AAAA/wAAAP8AAACmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBv + cABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNz + dABxcXAAaGhoAAkJCQAAAAAtAAAA5gkMNf8hINn/KSXd/zg7t/9YXZ7/gYaY/7Cxq//X2Mz/8vHo//// + /v////////////////////////////////////////////////////////////////////////////// + ///////////////////f3tr/goGA/xoaGf8AAAD/AAAA6gAAABYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wAlHt8AJR7fACUe3wAlHt8AJR7fACUe3wAlHt8AJR7fACUe + 3wAlHt8AJR7fACUe3wAlHt8AJR7fACUe3wB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNy + cwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1t + bABzc3QAcXFwAGhoaAAJCQkiAAAA1BcZH/9CSZH/aGqm/5CUm/+/wLX/4+PX//v69P////////////// + ///////////////////////////////////////////9/////f/+/PX/6+vp/9rf6v/S3PT/zdr9/8PO + +/++yPr/vsn6/8DL+//M2f3/4Ov//+/x8v+TkIb/GxoW/wAAAP8AAABTAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8AJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxs + bQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9u + bgBtbWwAc3N0AHFxcBRoaGhJS0tMz21tbf+ioaP/z87L/+zs4f////v///////////////////////// + ///////////////////////////+//j59f/a4vD/v8np/5im3f90i9v/V3Hd/z1a4f8wUeH/KU7p/yVN + 8f8cQu//GD3v/xk+7/8aQO//I0vw/y9V8f9aevr/l6vr/3BzdP8SEAf/AAAAfgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBw + cABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4 + dwBvbm4hbW1sWHNzdKCLjIvYrq6u/9jY2f/29vf///////////////////////////////////////// + ///////////+/+3y+v/F0vb/obHv/3CL6f9LaOf/MFPn/x1D5/8WPO//Fzzv/xk+8f8bQPL/HkLx/x9D + 8P8gQ+//IkXv/yJG8P8iRu//IkXw/yBE7/8eQe//GD3u/xxD9f8+XuP/Rk9w/wkHAIkJBwAACQcAAAkH + AAAJBwAACQcAAAkHAAAJBwAACQcAAAkHAAAJBwAACQcAAAkHAAAJBwAACQcAAAkHAAAJBwAACQcAAAkH + AAAJBwAACQcAAAkHAAAJBwAACQcAAAkHAAAJBwAACQcAAAkHAAAJBwAACQcAAAkHAAAJBwAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNy + cgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHACbWxsL25t + bGp4eHe0k5ST5Le3t//e3t//+Pj5///////////////////////////////////////////////9/+3y + 9//AzvT/kqjy/1597/89Xu7/Ikjs/xY87v8XPe7/GT/w/x5C8f8hRfH/I0fw/yNH8P8jRvD/I0fw/yNG + 7/8jR+//I0fv/yNH7/8jRu//I0bv/yNH7/8jR/D/I0fw/yNH7/8iRe//HkP3/yxP7f8/SnKbO13kACFE + 8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE + 8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1t + bABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxBm1tbT1tbW14fX19w5yb + nPDDw8T/5ubm//39/v/////////////////////////////////////////+////9v/d4u7/qrjs/2uG + 6/88Xez/IEbr/xY87v8YPe//G0Dv/yFE8P8jRu//I0fv/yNH8P8jR+//I0bv/yNG7/8jRvD/I0bw/yNH + 7/8jRvD/I0fv/yNG7/8jR+//I0fw/yNH8P8jR+//I0fw/yNH8P8jRu//I0fv/yNH7/8gRfT/LlHr8Dtd + 5DEhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE + 8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJh7gACYe + 4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gAHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBw + bwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcAttbWxGcHBwh4KCgsyjo6T4zc3P/+zs + 7f//////////////////////////////////////////////+//8+vD/2Nna/6Wv0/9geNj/MVPn/xo/ + 7P8YPe//HEHw/yFE8P8jR/D/I0fw/yNG7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH7/8jR/D/I0bw/yNH + 8P8jRu//I0bv/yNH8P8jR+//I0bv/yNH8P8jR/D/I0fv/yNH7/8jR+//I0bv/yNH8P8jR+//I0fv/yFG + 8f8hRfLNIUTyFSFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE + 8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE + 8gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wAmHt8AJh7fACYe3wAmHt8AJh7fACYe3wAmHt8AJh7fACYe3wAmHt8AJh7fACYe3wB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNz + cwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3ARbWxrTXFxcZaIiIjVqamq/NPT0//x8fL///////// + //////////////////////////////////////j/8/Lp/8rN1f+Un8r/XHLL/zFR2P8bQOf/GT/w/x5D + 8f8iRfD/I0fw/yNH8P8jR+//I0fw/yNH8P8jR+//I0bv/yNG7/8jR/D/I0bw/yNG7/8jRu//I0fw/yNG + 8P8jR+//I0fv/yNH8P8jR/D/I0fv/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fw/yNH7/8jR/D/I0jx/yNH + 8P8jR+//I0fv/yNH76IjR+8AJEfwACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8AJh7fACYe3wAmHt8AJh7fACYe3wAmHt8AJh7fACYe3wAmHt8AJh7fACYe3wAmHt8AdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1s + bABzc3MAcHBvAG1tbABzcnIAcHBwE2xsbVRzcnOdioqL3a+vsP/X19f/9vb2//////////////////// + ///////////////////////////2/+Xl4f+6v9H/gI7G/01mzv8pSdv/G0Dp/xk/8v8eQ/L/Ikbx/yNH + 8P8jR/D/I0fv/yNH8P8jR+//I0fv/yNH7/8jRu//I0fv/yNH7/8jRu//I0fv/yNG7/8jRu//I0bv/yNH + 7/8jR+//I0fw/yNH7/8jR/D/I0fv/yNH7/8jR/D/I0fv/yNH8P8jR+//I0bv/yNH8P8jSPD/I0fv/yRC + 7f8jR+//I0jw/yNH7/8jRu//I0fvciRH8AAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AB4W3wAeFt8AHhbfAB4W3wAeFt8AHhbfAB4W3wAeFt8AHhbfAB4W3wAeFt8AHhbfAHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFw + cABtbGwAc3NzAHBwbxJtbWxUc3JynIqKi9+xsbL/29vc//j4+f////////////////////////////// + ///////////8//399P/X2dv/prHV/2t+y/89WtT/IkTh/xk/7v8bQPL/IEXy/yJG8f8jR/D/I0bv/yNH + 7/8jR/D/I0fw/yNH8P8jR+//I0bv/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0fv/yNG7/8jR/D/I0fv/yNH + 7/8jR+//I0fv/yNH8P8jRvD/I0fv/yNH7/8jR/D/I0fw/yNG7/8jR+//I0fv/yNG7/8jSPD/JETu/yYq + 4/8mIOD/JS3k/yNF7/8jSfD/I0fv/yNH7/MkR/A7I0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8AUFDnAFBQ5wBQUOcAUFDnAFBQ5wBQUOcAUFDnAFBQ5wBQUOcAUFDnAFBQ + 5wB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJx + cgBxcHARbWxsVHNzc52JiorhsrGy/9zb3P/5+fn///////////////////////////////////////// + +v/19fH/x8ze/5Gf1f9ZcdH/MFDd/xxA5/8ZP/H/HUPy/yFF8f8jRvD/I0fw/yNH7/8jR+//I0bv/yNH + 8P8jR+//I0fw/yNH8P8jRvD/I0bv/yNH8P8jR/D/I0bv/yNH8P8jR/D/I0fw/yNH8P8jR+//I0fv/yNH + 7/8jR/D/I0fw/yNG7/8jR/D/I0bv/yNH7/8jR+//I0fv/yNG7/8jRu//I0fv/yNH7/8jR+//I0bv/yYp + 4/8mHN7/Jh7f/yYc3v8mJ+P/I0Ht/yNJ8P8jR+//I0fv0CNH7xMjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AMTM+gDEzPoAxMz6AMTM+gDEzPoAxMz6AMTM+gDEzPoAxMz6AMTM + +gDEzPoAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyBW5u + bUVycXKViYmJ4LKysv/c3Nz/+fn5//////////////////////////////////////////r/6+zw/7TB + 5f97jtr/RmPb/yVH5f8ZP+z/GkDy/x9E8v8iRvH/I0bv/yNH8P8jR+//I0fw/yNH7/8jR/D/I0fv/yNH + 7/8jRvD/I0bv/yNH8P8jR+//I0fv/yNH7/8jR+//I0bv/yNG7/8jR/D/I0fw/yNG7/8jR/D/I0fv/yNH + 7/8jR+//I0bv/yNG8P8kRu//I0fw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0nw/yU0 + 5/8mHN//Jh7f/yYf4P8lHuD/Jhzf/yYk4f8kQez/I0nw/yNG7/8jRu+aI0fwACNH8AAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A09L8ANPS/ADT0vwA09L8ANPS/ADT0vwA09L8ANPS + /ADT0vwA09L8AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vYH9/ + f8ypqKr/19fY//j4+f///////////////////////////////////////f76/9zi8P+ktOn/aIDh/zhY + 4v8eQ+j/GD7v/x1C8v8gRfH/Ikfw/yNH8P8jRu//I0fv/yNH7/8jR/D/I0bw/yNH7/8jR+//I0fv/yNH + 7/8jRu//I0bv/yNH8P8jR/D/I0fv/yNG7/8jR+//I0fw/yNG8P8jRu//I0bv/yNH7/8jR+//I0fw/yNG + 8P8jR+//I0fw/yNH8P8jRu//JEfw/yNH8P8jR/D/I0bv/yNH7/8jR+//I0bv/yNH7/8jR/D/I0jw/yRD + 7f8mIuD/Jh3f/yYf4P8mH+D/Jh/f/yYf4P8mHd//JiXh/yRB7P8jSPD/I0bv/yNH8FwjR/AAI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AM/P+wDPz/sAz8/7AM/P+wDPz/sAz8/7AM/P + +wDPz/sAz8/7AM/P+wB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQCc3Jyiays + rf/u7u/////////////////////////////////////+//n7/P/L1vL/kqbt/1Vz5v8tT+j/Gj/s/xg+ + 8P8eQ/H/IUXw/yNG7/8jR/D/I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNG7/8jRu//I0fw/yNH + 7/8jRu//I0bv/yNH7/8jR/D/I0fv/yNH8P8jR/D/I0fv/yNH8P8jR/D/I0fv/yNG7/8jRu//I0fw/yNG + 8P8jR+//I0fw/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fw/yNG7/8jR+//I0fv/yNH7/8jR/D/I0fw/yNJ + 8P8lNej/Jh3f/yYf4P8mH+D/Jh7g/yYe4P8mH9//Jh/g/yYd3/8mJOH/JELt/yNJ8P8jR/DjI0fwIiNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Az8/7AM/P+wDPz/sAz8/7AM/P + +wDPz/sAz8/7AM/P+wDPz/sAcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBw + bwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBw + bwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBw + bwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBw + bwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBw + bwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvPaOj + o////////////////////////////////v/x9fr/vszz/4OZ7f9HaOv/JEjs/xc97f8bQPH/IETx/yJG + 8P8jRu//I0bv/yNH7/8jRu//I0fw/yNH8P8jRu//I0fv/yNH7/8jR/D/I0fv/yNH7/8jRu//I0bv/yNH + 8P8jR+//I0bv/yNG7/8jR+//I0bv/yNH7/8jR+//I0fw/yNH7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNG + 7/8jRu//I0fv/yNH7/8jR+//I0bv/yNH8P8jR/D/I0bv/yNH8P8jRu//I0bv/yNH7/8jR+//I0bv/yNH + 8P8jR+//Jifj/yYd3/8mH+D/Jh7g/yYe4P8mH+D/Jh7f/yYf4P8mHuD/Jhzf/yUp4/8jR+//I0fv/yNH + 77AjRu8GI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM/P+gDPz/oAz8/6AM/P + +gDPz/oAz8/6AM/P+gDPz/oAz8/6AHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cDafn6D7/f3+////////////9/n6/7nG7P94kfD/PF7s/x5D7v8WPO7/HEDv/yFF7/8jR/D/I0bv/yNH + 8P8jR+//I0fw/yNG7/8jRu//I0fw/yNH8P8jR/D/I0bv/yNH7/8jRvD/I0fw/yNH7/8jR/D/I0bw/yNH + 8P8jR/D/I0fv/yNH7/8jR/D/I0fv/yNG7/8jR/D/I0fw/yNH7/8jR+//I0bv/yNH7/8jR/D/I0fw/yNG + 7/8jRu//I0bv/yNH7/8jRu//I0fv/yNG7/8jR/D/I0fv/yNG7/8jR/D/I0bw/yNG7/8jR+//I0fv/yNH + 8P8jSPD/JEHt/yYg4P8mHd//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mHN//JDTo/yNJ + 8P8jR+//I0fvTyNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Azs/6AM7P + +gDOz/oAzs/6AM7P+gDOz/oAzs/6AM7P+gBwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwjMLCw///////0d38/0Vn7f8aP+v/Fjzv/x1B7/8iRfD/I0bw/yNG7/8jR+//I0bv/yNH + 8P8jRu//I0bv/yNH8P8jR+//I0fw/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH + 8P8jR/D/I0bv/yNH7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH8P8jR+//I0fv/yNH7/8jR/D/I0fw/yNH + 7/8jRu//I0fv/yNH7/8jR/D/I0fv/yNG8P8jR/D/I0fv/yNH7/8jR/D/I0bv/yNH8P8jR/D/I0fv/yNH + 8P8jR/D/I0nx/yQ56v8mHd//Jh7f/yYf4P8mHuD/Jh/g/yYf4P8lHt//Jh7g/yYf4P8mH+D/Jh3f/yYk + 4f8kRe7/I0jw/yNH8GgjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM7P + +wDOz/sAzs/7AM7P+wDOz/sAzs/7AM7P+wDOz/sAJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNI8ABycnIAcnJyAHJycgBycnIAcnJyAHJy + cgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJy + cgBycnIAcnJyAHJychGCgHrLsbrW/zlf9/8VO+//Ikbw/yNH7/8jRu//I0fw/yNH8P8jRvD/I0bv/yNG + 7/8jR/D/I0bw/yNG7/8jR+//I0fv/yNH8P8jR/D/I0bv/yNG7/8jR/D/I0fw/yNH7/8jRu//I0fw/yNH + 7/8jR+//I0fw/yNG8P8jR+//I0fv/yNH7/8jR/D/I0bv/yNG8P8jR+//I0bv/yNG7/8jR+//I0fv/yNH + 7/8jR+//I0fw/yNH8P8jR+//I0fv/yNG7/8jRu//I0fv/yNH7/8jR/D/I0fw/yNG7/8jR/D/I0fv/yNH + 7/8jR+//I0fv/yNJ8P8lNOf/Jhzf/yYe3/8mH9//Jh/g/yYe4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mIOD/JD7s/yNJ8P8jRu9mI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8Az8/6AM/P+gDPz/oAz8/6AM/P+gDPz/oAz8/6ACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjSPAAI0bvACNJ8QAjSPAAcnJyAHJy + cgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJy + cgBycnIAcnJyAHJycgBycnIAeXdtI1RgmJcaQPX/JEfw/yNH7/8jRu//I0bw/yNH7/8jR+//I0fw/yNH + 8P8jRu//I0bw/yNH8P8jR+//I0fw/yNH7/8jR/D/I0fv/yNH7/8jR+//I0bw/yNH8P8jRu//I0fv/yNH + 7/8jR/D/I0fw/yNH8P8jR/D/I0bv/yNH7/8jR+//I0bv/yNH8P8jR/D/I0fw/yNG7/8jRu//I0fv/yNG + 7/8jR/D/I0fv/yNH7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH + 7/8jR+//I0fv/yNH8P8jSfD/JS/l/yYd3/8mHt//Jh7f/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7f/yYf + 4P8mH+D/Jhvf/yQ66v8jSvH/I0fvWyNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////ANPS/ADT0vwA09L8ANPS/ADT0vwA09L8ANPS/AAmIcAAJiHAACYhwAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0jwACNG7wAjSfEAI0jwACNI + 8AAjR/AAI0jwAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJy + cgBycnIAcnJyAHJycgBycnIAcnJyAHl3bQBUYJgAI0bwwSNG8P8jR/D/I0fw/yNH8P8jR+//I0fv/yNG + 7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fw/yNG7/8jR+//I0fv/yNG7/8jR/D/I0fv/yNH + 7/8jR+//I0fw/yNG7/8jRu//I0fv/yNG8P8jR/D/I0fv/yNH7/8jRu//I0fv/yNH8P8jR+//I0fw/yNH + 8P8jR+//I0fw/yNG7/8jRu//I0fv/yNH8P8jRvD/I0bv/yNH8P8jR+//I0fv/yNG8P8jR/D/I0fw/yNG + 7/8jRu//I0fv/yNH8P8jR/D/I0jw/yYr5P8mHd//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYd3/8kPOv/I0nx/yNH70kjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8AvsL5AL7C+QC+wvkAvsL5AL7C+QC+wvkAJiHAACYhwAAmIcAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNI8AAjRu8AI0nxACNI + 8AAjSPAAI0fwACNI8AAjSfAAI0XuACNI8ABycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJy + cgBycnIAcnJyAHJycgBycnIAcnJyAHJycgB5d20AI0fvACNH71MjR/D+I0fw/yNH8P8jR+//I0fw/yNH + 8P8jRu//I0fw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fw/yNH8P8jRu//I0fv/yNH + 7/8jR+//I0fw/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0fw/yNH7/8jR+//I0fv/yNG7/8jRvD/I0fv/yNH + 7/8jR+//I0fv/yNH7/8jR+//I0bv/yNG7/8jR+//I0bv/yNH7/8jR+//I0bv/yNG8P8jR/D/I0fv/yNG + 7/8jRu//I0bv/yNH7/8jR+//I0fw/yNH7/8mJuL/Jh3g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mIeD/JEHt/yNI8PEjRu8xI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AKGt9AChrfQAoa30AKGt9AChrfQAoa30ACYhwAAmIcAAJiHAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjSPAAI0bvACNJ + 8QAjSPAAI0jwACNH8AAjSPAAI0nwACNF7gAjSPAAI0rxACNJ8AAkR/AAJEfwACRH8AAkR/AAJEfwACRH + 8AAkR/AAJEfwACRH8AAkR/AAJEfwACRH8AAkR/AAJEfwACRH8AAkR/AHI0fwwSNH7/8jR+//I0fw/yNH + 8P8jR/D/I0fv/yNG7/8jRvD/I0bv/yNH7/8jR/D/I0fv/yNH7/8jR/D/I0fv/yNH7/8jR+//I0fv/yNH + 7/8jRu//I0bw/yNH8P8jRvD/I0fw/yNH8P8jR+//I0bv/yNG7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH + 7/8jR+//I0fv/yNG7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0bv/yNG7/8jR+//I0bv/yNG + 8P8jRu//I0bv/yNG7/8jRu//I0fv/yNH8P8kRO7/JiLh/yYd4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mHd//JiTh/yRF7v8jR/DYI0fwEiNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8Ac4ntAHOJ7QBzie0Ac4ntAHOJ7QAmIcAAJiHAACYh + wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0jwACNG + 7wAjSfEAI0jwACNI8AAjR/AAI0jwACNJ8AAjRe4AI0jwACNK8QAjSfAAI0fvACRH8AAkR/AAJEfwACRH + 8AAkR/AAJEfwACRH8AAkR/AAJEfwACRH8AAkR/AAJEfwACRH8AAkR/AAJEfwACNH8FMjR+/+I0fv/yNH + 8P8jR/D/I0fv/yNH7/8jR+//I0bv/yNG7/8jRu//I0fw/yNH7/8jR+//I0bv/yNH7/8jR/D/I0fw/yNH + 7/8jRu//I0bv/yNH8P8jR/D/I0bv/yNH8P8jRvD/I0fv/yNG7/8jR/D/I0fv/yNG8P8jRu//I0fw/yNH + 7/8jRu//I0bv/yNG8P8jR+//I0fv/yNG7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH8P8jRvD/I0fv/yNG + 7/8jRvD/I0fv/yNH8P8jRu//I0fv/yNH8P8jSPD/JEDs/yYg4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jhzf/yUp4/8jSfD/I0fwwCNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////ADU54wA1OeMANTnjADU54wA1OeMAJiHAACYh + wAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNI + 8AAjRu8AI0nxACNI8AAjSPAAI0fwACNI8AAjSfAAI0XuACNI8AAjSvEAI0nwACNH7wAjR/AAI0jwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AHI0fvwiNH + 7/8jR+//I0fw/yNH7/8jRu//I0fv/yNH7/8jRvD/I0fv/yNH7/8jR+//I0fv/yNH8P8jR/D/I0fw/yNH + 8P8jR+//I0bv/yNG7/8jR+//I0fv/yNH7/8jRvD/I0bv/yNH7/8jR/D/I0fw/yNG7/8jRu//I0fw/yNH + 8P8jRu//I0bv/yNG7/8jR/D/I0fv/yNH7/8jRu//I0bv/yNH8P8jR/D/I0fv/yNH8P8jR/D/I0fw/yNH + 7/8jRu//I0fw/yNH7/8jRu//I0fv/yNG7/8jRu//I0nw/yQ46f8mHd//Jh/g/yYe3/8mHuD/Jh7g/yYe + 4P8mH+D/Jh/g/yYe3/8mH+D/Jh7g/yYc3/8lL+X/I0nw/yNH8JAjR/AAI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AIxnfACMZ3wAjGd8AIxnfACYh + wAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjSPAAI0bvACNJ8QAjSPAAI0jwACNH8AAjSPAAI0nwACNF7gAjSPAAI0rxACNJ8AAjR+8AI0fwACNI + 8AAjSPAAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNG + 71IjR/D9I0fv/yNG7/8jR+//I0fw/yNH8P8jR/D/I0bv/yNH8P8jRvD/I0fv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNG7/8jR+//I0fw/yNH8P8jR+//I0fw/yNH + 8P8jR+//I0bv/yNH7/8jR+//I0fw/yNH8P8jRu//I0fv/yNG7/8jRu//I0fv/yNH7/8jR/D/I0bv/yNG + 7/8jRvD/I0fv/yNH7/8jRu//I0fv/yNH7/8jRu//I0bw/yNJ8P8lLuX/Jh3f/yYf4P8mH+D/Jh7g/yYe + 4P8mHuD/JR7f/yYf4P8mH+D/Jh/g/yYf4P8mHN//JTHm/yNJ8P8jR+9eI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ACUe3wAlHt8AJR7fACUe + 3wAmIcAAJiHAACYhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0jwACNG7wAjSfEAI0jwACNI8AAjR/AAI0jwACNJ8AAjRe4AI0jwACNK8QAjSfAAI0fvACNH + 8AAjSPAAI0jwACNF7gAjSO8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8FI0fvuCNH8P8jRvD/I0fv/yNH8P8jR/D/I0fv/yNH7/8jRvD/I0bw/yNG7/8jRu//I0fv/yNH + 7/8jRu//I0fw/yNH8P8jR/D/I0fw/yNG7/8jR+//I0fv/yNH8P8jR/D/I0fw/yNH8P8jR/D/I0fw/yNH + 8P8jRvD/I0bw/yNG8P8jR/D/I0fv/yNH8P8jR/D/I0bw/yNH8P8jR+//I0bw/yNG8P8jRvD/I0fw/yNH + 8P8jRvD/I0bw/yNH7/8kR/D/JEfw/yNG8P8jRu//I0fw/yNI8P8jRO7/JSPh/yYd3/8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yUe3/8mH9//Jh/g/yYe4P8mHuD/Jhzf/yUx5f8jSfD0I0bwMyNG8AAjRvAAI0bwACNG + 8AAjRvAAI0bwACNG8AAjRvAAI0bwACNG8AAjRvAAI0bwACNG8AAjRvAAI0bwACNG8AAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJh/gACYf + 4AAmH+AAJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNI8AAjRu8AI0nxACNI8AAjSPAAI0fwACNI8AAjSfAAI0XuACNI8AAjSvEAI0nwACNH + 7wAjR/AAI0jwACNI8AAjRe4AI0jvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNG70gjR/D8I0fw/yNG8P8jRu//I0bv/yNH8P8jRu//I0fv/yNH8P8jR/D/I0fw/yNH + 7/8jR+//I0bv/yNG8P8jRvD/I0bv/yNH8P8jRvD/I0fw/yNH7/8jR/D/I0bw/yRH8P8jRu//I0bv/yNH + 8P8jR+//I0bw/yNH7/8jR/D/I0bw/yNH8P8jR+//I0fw/yNH8P8jR/D/I0fv/yNH8P8jRvD/I0fw/yNH + 8P8jRvD/I0bw/yNH8P8jRu//I0bv/yNH8P8jRu//I0bv/yNG8P8jSfH/JTnp/yYd3/8mH+D/JR7f/yYf + 4P8lH9//JR7f/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh7f/yYd3/8lMeb/I0nwySNG7wcjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAABAAAAHAAAADsAAABJAAAAVgAAAGIAAABSAAAARQAAADQAAAAWAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ACYf + 4AAmH+AAJh/gACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjSPAAI0bvACNJ8QAjSPAAI0jwACNH8AAjSPAAI0nwACNF7gAjSPAAI0rxACNJ + 8AAjR+8AI0fwACNI8AAjSPAAI0XuACNI7wAlOOkAJD3rACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8DI0bvsyNH7/8jRvD/I0fv/yNH8P8jR/D/I0bw/yNH8P8jR/D/I0fw/yNH + 8P8jR/D/I0fw/yNH7/8jR/D/I0bw/yNH7/8jR+//I0fw/yNH8P8jRvD/I0bv/yNH8P8jRvD/I0fv/yNH + 7/8jR+//I0bw/yNH7/8jRu//I0fv/yNG7/8jRvD/I0bw/yNG7/8jR/D/I0fv/yNH8P8jR+//I0bv/yNH + 8P8jR/D/I0fv/yNH7/8jR+//JEfw/yNG8P8kR/D/I0fv/yNG7/8jR+//I0jw/yYq5P8mHd//Jh/g/yUe + 3/8mH+D/Jh/g/yYf4P8mH9//Jh7f/yYf3/8mHuD/Jh/g/yYf3/8mHd//JS3l/yNL8H0jRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAgAAADsAAACBAAAAvQAAAN4AAAD6AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD0AAAA2QAA + ALUAAABmAAAAHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wAlHt8AJR7fACUe3wAmIcAAJiHAACYhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0jwACNG7wAjSfEAI0jwACNI8AAjR/AAI0jwACNJ8AAjRe4AI0jwACNK + 8QAjSfAAI0fvACNH8AAjSPAAI0jwACNF7gAjSO8AJTjpACQ96wAkP+sAI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7zojRu/4I0fv/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fv/yNG + 7/8jRu//I0fv/yNH7/8jR/D/I0bw/yNG7/8jRu//I0fv/yNH8P8jR+//I0fv/yNG7/8jRu//I0fv/yNH + 8P8jR/D/I0fv/yNG8P8jR/D/I0fw/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0fv/yNG7/8jRu//I0bv/yNH + 7/8jR+//I0fv/yNH7/8jRu//I0fw/yRH7/8jR/D/JEfv/yNH7/8jRu//I0jw/yQ/7P8mIOD/Jh7g/yYe + 4P8mHuD/Jh7g/yYf4P8mHuD/Jh7f/yUd3/8lK+X/JS3l/yYf4P8mH+D/Jh7g/yYf4P8lLeVPJS3lACUt + 5QAlLeUAJS3lACUt5QAlLeUAJS3lACUt5QAlLeUAJS3lACUt5QAlLeUAJS3lACUt5QAlLeUAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAHAAAAXQAAAL4AAAD4AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAN8AAAB8AAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AB8V3wAfFd8AJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNI8AAjRu8AI0nxACNI8AAjSPAAI0fwACNI8AAjSfAAI0XuACNI + 8AAjSvEAI0nwACNH7wAjR/AAI0jwACNI8AAjRe4AI0jvACU46QAkPesAJD/rACNE7gAjR+8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0fwpiNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH + 8P8jR+//I0fw/yNH7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH7/8jR/D/I0fv/yNH8P8jRvD/I0bw/yNH + 7/8jRvD/I0fw/yNH7/8jR+//I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH7/8jR+//I0fw/yNH + 7/8jR/D/I0fv/yNH7/8jRu//I0bw/yNH8P8jRu//JEfw/yNH7/8jRu//I0bv/yNJ8f8lMOb/Jhzf/yYe + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYd3/8mIuD/JTXo/yU26f8mJeL/Jh7f/yYe3/8mH+DwJhzfLiYc + 3wAmHN8AJhzfACYc3wAmHN8AJhzfACYc3wAmHN8AJhzfACYc3wAmHN8AJhzfACYc3wAmHN8AJhzfAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAA3AAAAvwAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAM0AAABEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wBSY+cAUmPnACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjSPAAI0bvACNJ8QAjSPAAI0jwACNH8AAjSPAAI0nwACNF + 7gAjSPAAI0rxACNJ8AAjR+8AI0fwACNI8AAjSPAAI0XuACNI7wAlOOkAJD3rACQ/6wAjRO4AI0fvACNG + 7gAkRu4AI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8C4jRvDyI0fv/yNH7/8jR+//I0fv/yNH + 7/8jRu//I0fw/yNH8P8jR+//I0fv/yNH7/8jR/D/I0fw/yNH7/8jR+//I0bv/yNG7/8jR+//I0fw/yNG + 8P8jRu//I0fv/yNH8P8jRu//I0bv/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH7/8jRu//I0bv/yNG + 7/8jRvD/I0bw/yNH7/8jR/D/I0fw/yNH8P8jR/D/I0fv/yRH8P8jR+//I0bw/yNI8P8kRO7/JiLh/yYd + 3/8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHeD/Jinj/yU26f8mNun/Jink/yYd4P8mHuD/Jh/g0SYf + 4AgmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAB1AAAA8QAAAP8AAAD/AAAA/wABAP8BAgD/BQcJ/wwPG/8NEST/DREq/w0RJf8MEB3/BQYJ/wEC + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA9QAAAHIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AIaZ7wAmIcAAJiHAACYhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0jwACNG7wAjSfEAI0jwACNI8AAjR/AAI0jwACNJ + 8AAjRe4AI0jwACNK8QAjSfAAI0fvACNH8AAjSPAAI0jwACNF7gAjSO8AJTjpACQ96wAkP+sAI0TuACNH + 7wAjRu4AJEbuACNJ8QAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0bvjiNH7/8jR+//I0fw/yNH + 8P8jR+//I0fv/yNH8P8jR+//I0fv/yNH7/8jRu//I0bv/yNH7/8jR+//I0fw/yNH8P8jRu//I0bv/yNG + 8P8jRu//I0fv/yNH7/8jRu//I0bv/yNG7/8jR+//I0fv/yNH8P8jR+//I0bv/yNH8P8jRu//I0fw/yNH + 7/8jRu//I0fw/yNH8P8jRu//I0fw/yNH8P8jR+//I0bw/yNH8P8jR/D/I0fv/yNH7/8jSfD/JDbo/yYc + 3/8mHt//Jirk/yYi4f8mHt//Jh7g/yYe4P8mHuD/Jh7g/yUw5v8lNun/Jjbp/yUu5v8mHuD/Jh7g/yYf + 4JMmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + ABEAAACiAAAA/wAAAP8AAQD/BggL/w8SPv8aGnD/HyCo/yIdx/8mIdL/JiLX/ycj2v8mItf/JiLU/yId + xv8fIJr/Fxhk/wwOJ/8BAgD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAApQAAABUAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wDGyPkAJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNI8AAjRu8AI0nxACNI8AAjSPAAI0fwACNI + 8AAjSfAAI0XuACNI8AAjSvEAI0nwACNH7wAjR/AAI0jwACNI8AAjRe4AI0jvACU46QAkPesAJD/rACNE + 7gAjR+8AI0buACRG7gAjSfEAJDzqACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7xwjR+/iI0fv/yNH + 7/8jR+//I0fv/yNH7/8jRu//I0bv/yNH8P8jR/D/I0fw/yNH7/8jR+//I0bw/yNH8P8jR+//I0bv/yNH + 8P8jRu//I0fw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fw/yNH7/8jR/D/I0fw/yNH7/8jR/D/I0bv/yNH + 8P8jR/D/I0fv/yNH8P8jR/D/I0fv/yNG7/8jR/D/I0bv/yNH8P8jR/D/I0fv/yNG8P8jSPD/I0bv/yYl + 4v8mHN//JiLh/yU06f8mIuH/Jh7g/yYf4P8mH+D/Jh3g/yYj4f8lNen/JTXo/yU26P8lL+b/Jh7f/yYe + 3/YlH983JR/fACUf3wAlH98AJR/fACUf3wAlH98AJR/fACUf3wAlH98AJR/fACUf3wAlH98AJR/fACUf + 3wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AB0AAAC9AAAA/wABAP8MDiT/Ght1/yIfxf8nIuj/Jx/1/yYf8v8mHvD/JR3u/yUd7f8lHe3/JRzt/yUd + 7v8mHu//Jh7z/ycg9f8mIdz/HR6U/wsOK/8AAQD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADGAAAAIgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////ACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjSPAAI0bvACNJ8QAjSPAAI0jwACNH + 8AAjSPAAI0nwACNF7gAjSPAAI0rxACNJ8AAjR+8AI0fwACNI8AAjSPAAI0XuACNI7wAlOOkAJD3rACQ/ + 6wAjRO4AI0fvACNG7gAkRu4AI0nxACQ86gAkQe0AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvbSNG + 7/8jR+//I0jw/yNJ8f8jR+//I0fv/yNH7/8jR/D/I0fw/yNH8P8jRu//I0fv/yNG8P8jR/D/I0fv/yNG + 7/8jR/D/I0bv/yNH7/8jR/D/I0bv/yNH8P8jR/D/I0fw/yNH8P8jR/D/I0fw/yNH8P8jR+//I0bv/yNH + 7/8jR/D/I0fw/yNG7/8jRvD/I0fw/yNH7/8jR+//I0fw/yNH7/8jR/D/I0fw/yNH7/8jR/D/I0rx/yQ5 + 6f8mHd7/Jh/f/yUv5v8lNOj/JiHh/yYe3/8mH+D/Jh/g/yYd3/8mK+X/JTbp/yU16P8lNen/Jifj/yYd + 3/8mHuCnJR/fACUf3wAlH98AJR/fACUf3wAlH98AJR/fACUf3wAlH98AJR/fACUf3wAlH98AJR/fACUf + 3wAmH+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AB4AAADMAAAA/wgLFP8bHHv/JiPa/ycf9f8lHvD/JR3r/yUd6P8lHej/JR3n/yUd5/8lHej/JR3n/yUd + 6P8lHef/JR3n/yUd5/8lHuj/JR3s/yce9f8mIt7/Ghtz/wQHBP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + ANEAAAAiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wAmIcAAJiHAACYhwAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0jwACNG7wAjSfEAI0jwACNI + 8AAjR/AAI0jwACNJ8AAjRe4AI0jwACNK8QAjSfAAI0fvACNH8AAjSPAAI0jwACNF7gAjSO8AJTjpACQ9 + 6wAkP+sAI0TuACNH7wAjRu4AJEbuACNJ8QAkPOoAJEHtACRE7gAjRu8AI0fvACNH7wAjR+8AI0fvACNH + 7wcjRu/AI0jv/yRA7f8kOur/I0fv/yNI8P8jR/D/I0fw/yNH8P8jR+//I0fv/yNH7/8jR/D/I0fw/yNH + 8P8jR/D/JEbv/yNG7/8kRu//I0fv/yNH8P8jR/D/I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH + 8P8jR+//I0fv/yNH7/8jR+//I0bv/yNH7/8jR/D/I0fv/yNH8P8jR+//I0fv/yNH7/8jRvD/I0fv/yNI + 8P8lKeP/Jhvf/yYo4/8lN+r/JjLn/yYg4P8mHt//Jh/g/yYd4P8mIuD/JTPo/yU26P8lNun/Ji3m/yYe + 4P8mHuDzJh7gMSYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJh/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + ABwAAADMAAEA/xIVRP8lIsb/Jx/1/yUd7f8lHej/JR3o/yUd5/8lHej/JR3o/yUe5/8lHej/JR3n/yUe + 6P8lHef/JR3n/yUd5/8lHuj/JR3n/yUd6P8lHej/Jh7w/ycf+v8gIKr/CAkk/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAAuAAAAAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJiHAACYhwAAmIcAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNI8AAjRu8AI0nxACNI + 8AAjSPAAI0fwACNI8AAjSfAAI0XuACNI8AAjSvEAI0nwACNH7wAjR/AAI0jwACNI8AAjRe4AI0jvACU4 + 6QAkPesAJD/rACNE7gAjR+8AI0buACRG7gAjSfEAJDzqACRB7QAkRO4AI0bvACNJ8AAjR+8AI0fvACNH + 7wAjR+8AI0bvMiNK8fMlNOj/Jhvf/yUq4/8kQe3/I0nw/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fw/yNH + 7/8jR+//I0fw/yNH8P8jRu//I0bv/yNH7/8jR+//I0fw/yNH7/8jRu//I0fv/yNH8P8jRu//I0bw/yNH + 7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fw/yNH7/8jRu//I0fv/yNH8P8jRu//I0bw/yNJ + 8P8kPer/Jh3f/yYf4P8lM+j/JTfq/yYu5v8mH+D/Jh/g/yYe4P8mIOH/JjDn/yU16f8lNej/JTTo/yYi + 4f8mHuD/Jh7gkiYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmH+AAJh/gACYf4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + ABMAAADABAUA/xkbdf8nIun/Jh3x/yUd6P8lHef/JR3n/yUd6P8lHej/JR3n/yUe6P8lHef/JR3o/yUe + 6P8lHuj/JR7o/yUd6P8lHun/JR3s/yUc8P8mHvT/Jh/p/yQc3v8jHs3/Jia7/x4jbP8GCAD/AQIA/wAA + AP8AAAD/AAAA/wAAAPUAAAAlAAAAAAAAAAAjR/AAI0fwACNH8AAjR+8AI0fvACRH7wAkR+8AJEfvACRH + 7wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ACYhwAAmIcAAJiHAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjSPAAI0bvACNJ + 8QAjSPAAI0jwACNH8AAjSPAAI0nwACNF7gAjSPAAI0rxACNJ8AAjR+8AI0fwACNI8AAjSPAAI0XuACNI + 7wAlOOkAJD3rACQ/6wAjRO4AI0fvACNG7gAkRu4AI0nxACQ86gAkQe0AJETuACNG7wAjSfAAI0nxACNI + 8AAjR+8AI0fvACNG7wAjSfFhJTvq/yYe3/8mHN//JiHg/yU36f8jSPD/I0fv/yNH7/8jR/D/I0fw/yNG + 7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNG7/8jR+//I0fw/yNG8P8jR/D/I0fw/yNG7/8jRvD/I0bv/yNH + 8P8jR+//I0fv/yNH8P8jR/D/I0fv/yNG8P8jR+//I0fw/yNH8P8jRu//I0fv/yNH8P8jR/D/I0bv/yNI + 8P8jR+//JSfi/yYd3/8lLub/JTbp/yU26f8mKeP/Jh3g/yYd4P8mIeH/JjHn/yU26f8lNej/JTbp/yYr + 5f8mHt//Jh/g4yYf3xsmH98AJh/fACYf3wAmH98AJh/fACYf3wAmH98AJh/fACYf3wAmH98AJh/fACYe + 3wAmHt8AJh/gACYf4AAmH+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAYAAACpBAcJ/x4fkf8nH/b/JR3s/yUe5/8lHej/JR7n/yUd6P8lHuj/JR7o/yUd6P8lHuj/JR3n/yUd + 5/8lHun/JR3s/yUc8P8mHu7/JB7f/yQhyv8kJav/HRyS/yIpg/8hJXr/GBZ6/yAhgf8jJJD/ISKT/x4d + gf8WHnH/EiBO/woOGv8CBAm6AAAAACVL/gAjR+8AI0fwACNH8AAjR/AAI0fvACNH7wAkR+8AJEfvACRH + 7wAkR+8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wAmIcAAJiHAACYh + wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0jwACNG + 7wAjSfEAI0jwACNI8AAjR/AAI0jwACNJ8AAjRe4AI0jwBCNK8SEjSfBPI0fvYSNH8F4jSPBFI0jwKyNF + 7hEjSO8IJTjpACQ96wAkP+sAI0TuACNH7wAjRu4AJEbuACNJ8QAkPOoAJEHtACRE7gAjRu8AI0nwACNJ + 8QAjSPAAI0jxACNH7wAjRu8AI0nxACRA7GEmIOD1Jh7g/yYe4P8mHeD/JS7l/yNF7v8jSfH/I0fw/yNI + 8v8jR/H/I0fw/yNH7/8jRu//I0fv/yNH7/8jR+//I0fv/yNH8P8jRu//I0fv/yNH7/8jRu//I0bv/yNH + 8P8jR+//I0bv/yNH7/8jR/D/I0fw/yNH8P8jR/D/I0fw/yNH8P8jR/D/I0fw/yNH7/8jRvD/I0bv/yNG + 7/8jSvD/JDbo/yYd3/8lLeX/JTbp/yU16P8lNOj/JiLg/yYd4P8mHeD/Jibi/yY26f8lNen/JTbp/yYs + 5f8mHuD/Jh7g/yYf4GMmH98AJh/fACYf3wAmH98AJh/fACYf3wAmH98AJh/fACYf3wAmH98AJh/fACYf + 4AAmHt8AJh7fACYf4AAmH+AAJh/gACYf4AAmHt8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAB/BAcK/x8gm/8mH/f/JR3q/yUd6P8lHuj/JR3o/yUd6P8lHef/JR3o/yUe5/8lHej/JR3q/yUd + 7f8mHfH/JR3h/yQhzP8kJKv/IiOV/yIniv8fH5D/JSil/yMfvf8lH83/JyHb/ycf5/8mHun/Jh7p/yce + 6v8nHuz/Jybv/yZF8P8jReD/IEDN1yFC4GUlS/4SI0fvACNH8AAjR/AAI0fwACNH7wAjR+8AJEfvACRH + 7wAkR+8AJEfvAP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJiHAACYh + wAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNI + 8AAjRu8AI0nxACNI8AAjSPAAI0fwACNI8BAjSfBMI0XujyQ968slNOjzJS/l/yUr4/8kKuP/Iyjj/iMq + 4/YjLOXtJDLn1yU46bokPeucJD/rfSNE7mIjR+9FI0buJiRG7g4jSfEFJDzqACRB7QAkRO4AI0bvACNJ + 8AAjSfEAI0jwACNI8QAjQ+4AI0XuACNJ8QAkQOwAJh3fZSYe3/8mH+D/Jh7f/yYc3/8mJ+L/JD/t/yNI + 7f8iP9v/I0Xq/yRJ9P8jSfX/I0jx/yNH8P8jRvD/I0fw/yNH7/8jR/D/I0bv/yNH7/8jR+//I0fv/yNH + 7/8jR/D/I0bv/yNH8P8jR/D/I0fw/yNH8P8jR/D/I0fw/yNG7/8jR/D/I0fv/yNG8P8jR+//I0bv/yNG + 8P8jSfH/Iz/s/yYh4P8mLeX/JTbp/yU16f8lNun/Ji7m/yYe3/8mHuD/Jh3g/yYj4v8lNuj/JTTo/yYn + 4/8mHuD/Jh7g/yYe4LcmHuACJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJh7fACYe3wAmH+AAJh/gACYf4AAmH+AAJh7fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAABLBAYI+x8gnP8nIPb/JB3p/yUe6P8lHej/JR3n/yUd5/8lHef/JR3n/yUe6v8lHO7/JR3s/yQe + 2f8lJL7/IiGf/yMokv8jI5n/JSWz/yQeyv8mH9v/Jx/p/yYd5v8mHuT/Jh/j/yYf4v8mH+D/Jh7f/yUe + 3/8mH+D/Jh/g/yYc3/8lK+b/JEj0/yRJ9v8kSPP/I0fv2SNH72gjR/ACI0fwACNH8AAjR+8AI0fvACRH + 7wAkR+8AJEfvACRH7wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ACYh + wAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjSPAAI0bvACNJ8QAjSPADI0jwSCNH8J0kPuzjJTHm/yYl4f8mHuD/Ixfe/yAU3f8hGN3/JyDf/y0l + 4f8vJ+H/Lyfh/yUc3/8kGt//Jh/f/yYg4P8mIuH/JSjj/yUs5PYlMOfoJTbpyyQ86qwkQe2PJETuciNG + 71MjSfAzI0nxFiNI8AsjSPEBI0PuACNF7gAjSO8AI0rxACYd3wAmH+CDJh7g/yYf4P8mHt//Jh3g/yci + 5v8lNeb/GR6S/xYXgv8cKKn/IDrR/yNG7v8kSfb/I0n0/yNH8f8jRu//I0fv/yNH7/8jR+//I0fw/yNH + 7/8jR+//I0fv/yNH8P8jR/D/I0fw/yNH7/8jR/D/I0bv/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH + 7/8jSfD/I0Lt/yYm4/8lLuX/JTbp/yU16P8lNuj/JjTo/yYj4v8mHd//Jh/g/yYe4P8mH+D/JTDn/yYn + 4/8mHd//Jh7g/yYf4O4mHuApJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJh/gACYe3wAmHt8AJh/gACYf4AAmH+AAJh/gACYe3wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAbAwQA3R0dh/8oH/j/JR3o/yUe5/8kHef/JR7o/yUd6P8lHer/JRzu/yUe7P8kH9f/JCO3/yMm + nf8kJ5n/JCWq/yUgyf8mH9r/Jx/o/yYd5f8mHuL/Jh7h/yYf4P8mH+D/Jh7g/yYe4P8mHuD/Jh7g/yYf + 4P8mHt//Jh7f/yUf3/8mHuD/Jh7f/yQ96/8jSfD/I0bv/yNG7/8jR/D/I0fwuiNH8CkjR/AAI0fvACNH + 7wAkR+8AJEfvACRH7wAkR+8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wAmIcAAJiHAACYhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0jwACNG7wAjSfFQI0XvxiQ26f8lKOP/Jh7f/yYd3/8mHt//Jh/g/zc65P9UWun/bXjv/4GR + 8/+PnPb/laD4/5Sg9/9qdO7/Kifh/yMa3/8mHt//Jh7f/yYc3/8mHN//Jhzf/yYd3/8mH9//Jh/g/yUj + 4f8mKeP/JS7k+iUz5/AkOenXJD/suSND7pYjRe56I0jvWiNK8TgjSfEYJDbpCSYg4J0mHt/8Jxzi/yMb + zP8cFaH/JBzR/yQjz/8ZGo7/Ew9w/xUSef8YHY//Hi20/yI+2v8kSPL/JEn1/yNI8f8jR/D/I0fv/yNH + 7/8jR+//I0bv/yNH7/8jRu//I0bv/yNG7/8jR/D/I0fw/yNH7/8jR/D/I0fw/yNH8P8jR/D/I0fw/yNH + 8P8jSfH/I0Lt/yYk4f8mKOP/JTfp/yU16P8lNej/JTfp/yUo4/8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mHuBkJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJh/gACYf4AAmHt8AJh7fACYf4AAmH+AAJh/gACYf4AAmHt8AJh7fAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAohgbZf8oIPT/JRzp/yUe5/8lHej/JB7q/yYd8P8mHu//JB/Y/yQjtf8jJZ3/Iyab/yQl + sv8lIND/Jh/j/yYe6P8mH+P/Jh7h/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYc3/8lLuX/I0nw/yNH7/8jR/D/I0fw/yNH7/8jR+/iI0fwQiNH + 7wAjR+8AJEfvACRH7wAkR+8AJEfvAP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8AJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvASNI8EEjRu+yJDzq/yUm4v8mHN//Jh3f/yUe3/8mHt//Ixnf/zEz4v+Pn/b/orH6/3WA + 8P9YWuv/Y2/t/3aD8f+Ik/X/kaD2/3SE8P8wL+L/Ixvg/yYe4P8mHuD/Jh/g/yYf4P8mHuD/Jh/f/yYe + 4P8mHd//Jh3f/yYc3/8mHd//Jh7f/yYf4P8mIOD/JSbi/yUs5P8lMef7JDjp8iQ+69IjP+y4JDbp9CUy + 6v8hLcv/Fx+L/xcai/8cGpn/GBaE/xQQcv8UEHT/FA9y/xQPcf8VE3v/GiKa/x82yf8jRuz/JEr3/yNI + 8/8jR/H/I0fw/yNH8P8jR+//I0fw/yNG7/8jR+//I0fv/yNH7/8jR/D/I0fw/yNH7/8jR/D/I0fw/yNI + 8P8jSfD/JD3r/yYj4v8mG9//JS3k/yU26f8lNen/JTfp/yYs5f8mHt//Jh7g/yYf4P8mHuD/Jh7g/yYf + 4P8mHt//Jh/g/yYf4P8mH+CiJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7fACYe3wAmH+AAJh/gACYf4AAmH+AAJh7fACYe3wAAAAAAAAAAAAAA + AAAAAAAAAAAATg8TOv4nI+b/JR3t/yUe6v8mHu//Jh/0/yUg4P8gH63/HyGD/yImif8jI63/JSHS/yYf + 5f8mHeb/Jh7i/yYf4f8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/f/yYf4P8mH+D/Jh/g/yYf4P8mHeD/JiLh/yRD7v8jSO//I0fv/yNH7/8jR/D/I0fw/yNG + 7/MjR+9FI0fvACRH7wAkR+8AJEfvACRH7wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////ACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0jwACNI8AAjSPAAI0jwACNI + 8AAjSPAAI0jwACNI8E4kPev/JSXi/yYd3/8lHd//Jh7f/yYf4P8mH+D/Jh7f/yUd3/8mIeD/UVfp/1lk + 6/8mIeD/HBPe/yAa3/8kHd//KCHg/y8w4v86PeT/LCnh/yUd4P8mH+D/Jh7f/yYf3/8mH+D/Jh7g/yYf + 4P8mHt//JR7f/yYf4P8mH+D/Jh/g/yYf4P8mH9//JR7f/yYd4P8mHd//Jhzf/yYd3/8mHuD/JiDg/yUp + 4/8fJ+P/Hynn/yAx6P8gNeH/IDXR/x81yP8dMb7/Gymm/xgfkP8WGIP/FBJ3/xINbv8TDXD/GBqK/x4u + uP8iQOD/JEfw/yRK9v8kSfb/JEn2/yRJ9v8kSfX/I0n0/yNI8v8jR/D/I0fw/yNH8P8jR/H/I0jx/yNJ + 8v8kQ+7/JTHn/yYf4P8mHd//Jh3f/yYv5v8lN+n/JTbp/yUs5f8mHuD/Jh7f/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYe4P8mH+DPJh/gEyYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe3wAmHt8AJh/gACYf4AAmH+AAJh/gACYe3wAmHt8AAAAAAAAA + AAAAAAAAAAAADgYID9EkIsX/Jh35/yYe9P8mIOj/IR+6/xoccP8XG0r/HB50/yUhv/8mHuT/Jh7o/yYf + 4v8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHt//Jh/g/yYe4P8mHt//Jh/g/yYf + 4P8mHuD/Jh/f/yYe3/8mHt//Jh7g/yYf4P8mHt//Jh/g/yYd3/8kPOv/I0nx/yNG7/8jR+//I0bw/yNH + 8P8jRvD/I0fw6CNH7yokR+8AJEfvACRH7wAkR+8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wAmIcAAJiHAACYhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkQu4AJELuACRC + 7gAkQu4AJELuACRC7gAkQu4mJiDg2yYb3v8mH+D/Jh/g/yYf3/8mH9//Jh/g/yYf4P8mH+D/Jh7g/yAX + 3/8gGN//JR7g/yYf4P8mHt//JR3f/yQd3/8kG+D/Ixnf/yUd3/8mH+D/Jh/g/yYe3/8mHt//Jh/g/yYf + 4P8mHuD/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/x8V + 3v9ESeb/bnTu/1db6v9BReb/MS3k/ygo5f8gJOj/ISfq/yEt6v8hNej/Ijfd/yA0y/8dL7j/HCei/xcb + if8VE3n/FhV9/xkdkP8aJ6j/Hi+5/x8yv/8fMsD/HzLA/yA2yf8iQOD/JEjy/yRK9/8kSvT/I0bt/yNB + 5/8kM+T/JiLh/ycc4v8nHuX/Jx7j/yYg4v8mNOj/JTLn/yYn4/8mHuD/Jh7g/yYe4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+DpJh/gLyYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHt8AJh7fACYf4AAmH+AAJh/gACYf4AAmHt8AJh7fACYf + 4QAAAAAAAAAAAAECAXgdHZP/KCH9/yIfx/8bG4D/ExdH/xMXPP8bG3r/JCDG/ygf7P8mHub/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh7g/yYe4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mHN7/JTLn/yNJ8f8jR+//I0fv/yNG + 8P8jR+//I0fv/yNH7/8jR+/GJEfvCyRH7wAkR+8AJEfvAP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8AJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAkQu4AJELuACRC7gAkQu4AJELuACYc3yomH9+cJh/g8iYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYe + 4P8mH+D/Jh/g/yUf3/8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yQc + 3/84OuT/bXru/52q+f+aqPj/l6T4/46b9v99ivL/bHLu/1NZ6P8+QOT/MCvi/yUh4/8gHuf/ICbp/yEu + 6v8iNun/Ijja/x82yf8dLbD/GiOY/xcahv8WFHr/FA9x/xMOb/8UDnD/FRR8/xkim/8aJ6j/Giaj/xke + k/8YGIv/GROO/xsVlf8aFpn/HRmq/yMbx/8mIt//Jizq/yYg4/8mHd//Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYe3/8mHuD2Jh/gTCYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACNH + 7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7fACYe3wAmH+AAJh/gACYf4AAmH+AAJh7fACYe + 3wAmH+EAAAAAAAAAABsMDyXoICKV/xcaWv8VGkD/GRtr/yIfq/8nIeD/KB/u/yYe5v8mH+D/Jh7f/yYe + 4P8mHuD/Jh7g/yYf4P8mHuD/Jh/f/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mHt//Jh/g/yYf + 4P8mHuD/Jh7g/yYf4P8mH+D/Jh/f/yYe3/8mHuD/Jh/g/yYf4P8mH+D/Jhzf/yUr5P8jSPD/I0fv/yNH + 7/8jR+//I0fv/yNH8P8jR/D/I0fv/yRH8HojRu8AI0bvAP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////ACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACRC7gAkQu4AJELuACRC7gAmHN8AJh/fACYf4DEmH+CQJh/g6SYe4P8mH+D/Jh7g/yYe + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yUf3/8mH+D/Jh/g/yYf + 4P8mHuD/Jh7g/yYe4P8mH+D/Jh7f/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yUd + 4P8jHt//cIDw/56q+f+UoPf/lKD3/5Wh9/+Wovf/mqX4/5yo+f+Zp/j/mKT4/4yZ9f94hfH/aG/t/05V + 5/85OOP/LSni/yIg4/8fIej/ISvr/yE17P8iPOr/IjvY/x83yv8dLrL/GiSc/xcZh/8VEXX/Ewxs/xMM + bP8TDnD/ExBz/xQRdP8UEnX/ExJ0/xQSdP8UE3j/GRaM/yAZsv8lHtj/JyDm/ycf5P8mH+H/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/gaCYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAjRu8AI0nwACNI + 8AAjR+8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe3wAmHt8AJh/gACYf4AAmH+AAJh/gACYe + 3wAmHt8AJh/hACYg3gAICiGOFhlL/xsce/8iIKv/JiDe/ykh7/8mHur/Jh7h/yYe4P8mH+D/Jh/g/yYe + 3/8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7f/yYf4P8mHuD/Jh/g/yYc3/8lJ+L/I0bu/yNH + 8P8jR/D/I0fv/yNH7/8jR+//I0fw/yNH8P8jR/DrI0bvHSNG7wD///8A////AP///wz///8b////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wAmIcAAJiHAACYhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACRC7gAkQu4AJhzfACYf3wAmH+AAJh/gACYf4CQmHuB/Jh/f2iYe + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYe3/8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf + 4P8mH+D/Hxfe/2Rq7f+eq/n/lKD3/5Sg9/+UoPf/laD3/5Sg9/+UoPf/laD3/5Wh9/+Xo/j/m6b4/5yo + +P+ap/j/lqL3/4WV9P91f/D/XWPq/0RH5f80L+L/JyTh/x8f5P8hJ+v/ITHt/yM87/8jQOf/Ij3X/x84 + x/8dLK//GSGW/xcZh/8WE3r/FBBz/xQQdP8UEXX/FBJ3/xMRdf8TEXP/FxSE/x4arv8kHtL/Jh/j/ycg + 5v8mH+L/Jh/g/yYe4P8mH+D/Jh7geSYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACNJ8QAjSPAAI0bvACNJ + 8AAjSPAAI0fvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHt8AJh7fACYf4AAmH+AAJh/gACYf + 4AAmHt8OJh7fNCYf4XAmIN6uJiTP/Scg5v8oIOz/Jx7q/yYe4v8mH+D/Jh/g/yYf4P8lH9//Jh/f/yYf + 4P8mH9//Jh7g/yYe4P8mHuD/Jh7f/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf + 4P8mHt//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh7g/yYf3/8mHeD/JiTh/yNE + 7v8jSPD/I0fw/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fw/yNH74YUOu4A////AP///wD///9l////xf// + /wT///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8AJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJELuACYc3wAmH98AJh/gACYf4AAmH+AAJh7gACUe + 3xUmH+BpJh/gyCYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/yYe4P8mHt//Jh/f/yYf4P8mHt//Jh7f/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf + 4P8mHt//Jh/g/yAX3/9NTuj/k6L3/5uo+P+apvn/mKT4/5ei9/+VoPf/lKD3/5Sg9/+UoPf/lKD3/5Sg + 9/+VoPf/laD3/5ah9/+YpPj/m6f5/5uo+P+apvj/j531/36L8v9qce3/TlTn/zo44/8qJuD/IB/i/yAk + 6P8gLu3/Ijnw/yJA8P8jQ+b/IT7W/x80wf8cKan/GB6S/xcXgv8VEXj/ExB0/xQQcf8TEG//FRJ7/xoX + l/8hG7r/JR7Y/ycf5f8mH+H/Jh/gjiYe4AAmH+AAJh/gACYf4AAjSPAAI0fwACNJ8QAjSfEAI0jwACNG + 7wAjSfAAI0jwACNH7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7fACYe3wAmH+ANJh/gMCYf + 4GImH+CeJh7gziYf4PomH+D/Jh7h/yYe5v8mH+H/Jh7f/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf + 4P8mH9//Jh/f/yYf4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe + 3/8lHt//Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yUf3/8mHuD/Jh7g/yYf4P8mHuD/Jh7f/yYi + 4f8jQ+7/I0jw/yNH7/8jR/D/I0fw/yNG7/8jR+//I0fw/yNH7/8jR+/oFDruHf///wD///8G////w9LS + 08qpqasCqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrAKmp + qwCpqasAqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmHN8AJh/fACYf4AAmH+AAJh/gACYe + 4AAlHt8AJh/gACYe4AcmHuBUJh/gtiYf4PcmH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mHuD/Jh/g/yYe + 4P8mH+D/Jh7g/yYf4P8mHuD/Jh7f/yYe3/8mHuD/Jh/f/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh7f/yYe4P8mH+D/Ixzf/zU25P9UXur/c37w/4WT9P+Rn/b/m6j4/5qn+P+ap/j/mqb4/5ik + +P+Wovf/laD3/5Wg9/+UoPf/lKD3/5Sg9/+UoPf/laD3/5ei9/+apvj/m6j4/5uo+P+Uovf/gpD0/3F5 + 7/9YX+n/QUDk/zMu4v8nJuH/HyHk/x8q6v8gNe//ITzx/yJE8f8iQ+L/IDzS/x4yu/8aJaD/GBuN/xYV + ff8TD27/ExBu/xUSe/8eF6n7JhzeiiYf4AAmHuAAI0fvACNJ8AAjSPAAI0jwACNH8AAjSfEAI0nxACNI + 8AAjRu8AI0nwACNI8AAjR+8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AUJh/gNyYe32ImHt+fJh7gzCYf + 4PcmH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yUf + 4P8lH+D/Jh7f/yYe3/8mHuD/JR7g/yYf4P8mHuD/Jh/g/yYe4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/JR/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8lH9//JR7f/yUe4P8mH+D/Jh/g/yYe + 4P8lI+H/I0Tu/yNI8P8jR/D/I0fw/yNH7/8jR+//I0fv/yNH8P8jR/D/HkPv/zFS8U7///8A////lu3t + 7f8tLS6zqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrAKmp + qwCpqasAqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrAKmpqwAmIcAAJiHAACYhwAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAACQAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmH+AAJh/gACYf + 4AAmHuAAJR7fACYf4AAmHuAAJh7gACYf4AAmH+A8Jh/gnyYf4O0mH+D/Jh7g/yYe4P8mHuD/Jh/g/yYf + 4P8mHt//JR7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/f/yUf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh7f/yUe3/8hGd//Hhbe/yAZ3v8qKeH/Ojnk/0hL5/9YZOv/a3Xu/3yG + 8f+Ek/P/j532/5ml+P+bp/j/mqf4/5qn+P+Yo/j/laH3/5Wg9/+Un/f/lJ/3/5Sg9/+UoPf/laH3/5mk + +P+bp/j/mqj4/5ml+P+Nm/X/f4zy/3J37/9aYen/RUfm/zY04/8mKuL/HiXn/yEy7v8kQPP/JEf0/yRI + 7f8iQt3/HznL/xwrrf8XH5L/GiGcjiYc3gAjS/EAI0fvACNH7wAjSfAAI0jwACNI8AAjR/AAI0nxACNJ + 8QAjSPAAI0bvACNJ8AAjSPAAI0fvACYf4AAmH+AAJh/gACYf4AEmH+BxJh7g1SYe4PomHuD/Jh7g/yYe + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYe3/8mHt//Jh7g/yYe4P8mH+D/Jh7g/yYe4P8mH9//Jh/g/yYf + 4P8mHt//Jh/g/yYe4P8lHuD/Jh/g/yYe4P8mH+D/Jh7g/yYe4P8mH9//Jh/g/yYf4P8lHuD/Jh7g/yYf + 4P8mHuD/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf3/8mHt//Jh7g/yUf3/8lHt//Jh/g/yYf + 4P8mHd//JSXi/yNF7v8jSPD/I0fw/yNG8P8jR+//I0bw/yNH8P8jR+//I0fw/xM57/qOoPda////lf3+ + /v9hYWL/AAAAnwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYhwAAmIcAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAADgAAACFAAAAuAAAAMgAAADDAAAAogAAAFQAAAALAAAAAAAAAAAAAAAAAAAAAAAA + AAAmH+AAJh7gACUe3wAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8sJh7fiyYf4N8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yUe3/8mHuD/Jh/g/yYf3/8mHt//JR/g/yUe4P8lH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh7g/yYf4P8mH+D/JR/f/yUf4P8lHt//Jh7f/yYe4P8lHt//JBrg/yEY3/8fF97/IBje/yAY + 3/8iHd//Kivg/zc24/9DQ+b/UFno/2Vv7f95gvH/iJf0/5el9/+bp/n/m6f4/5ij+P+UoPf/lKD3/5Sg + 9/+Un/f/lJ/3/5Wg9/+VoPf/l6L4/5ql+P+cqPj/mqf4/5qm+P+Qnvb/f4rx/2Np6/86OeP/Hxbe/yMf + 4P8lK+X/JTbs/yU/8v8kRvT/JEr1/yNM9dkjTvO1I0vxgiNH70QjR+8TI0nwACNI8AAjSPAAI0fwACNJ + 8QAjSfEAI0jwACNG7wAjSfAAI0jwACNH7wAmH+AAJh/gACYf4AAmH+ACJh/gwyYe4P8mHuD/Jh7f/yYe + 4P8mH+D/Jh7f/yYf4P8mH+D/Jh7g/yYf4P8mHt//Jh/f/yYf4P8mHuD/Jh/g/yYe3/8mH+D/Jh/g/yUe + 3/8mH+D/Jh7g/yYf4P8lH+D/JR/g/yYe4P8lHt//Jh/g/yYf4P8mHuD/Jh/f/yYf3/8mH+D/JR7g/yUe + 3/8mH+D/Jh/g/yYe3/8mHuD/Jh/f/yYf4P8mHt//JR/g/yYf4P8mHt//JR/g/yUe4P8mH+D/JR/g/yYf + 4P8mH+D/Jhzf/yUp4/8jR/D/I0fv/yNH8P8jRvD/I0fw/yNH8P8jR/D/I0fw/x9C7/8gR/D2xtT90/// + //9+f4D/AAAA/wAAAJMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAJQAAAKMBAQD3BQcL/wECAf8AAAD/AAAA/wAAAP8AAAD/AAAAxwAAAE4AAAAAAAAAAAAA + AAAAAAAAAAAAACYe4AAlHt8AJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYe3wAmH+AcJh/gbiYf + 4MwmH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH9//Jh/f/yUf3/8mHt//Jh7g/yYe4P8mH+D/Jh7f/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yUf3/8mH+D/Jh/g/yYf3/8lH9//Jh/g/yYf4P8mH+D/Jh/g/yYe + 3/8mHuD/JR3g/yQb3/8hGN//IBff/yAX3v8fF97/IRvf/y4t4f9AQeX/WGLr/3eB8f+Mm/X/mqf4/5uo + +P+apfj/lqL4/5Sg9/+VoPf/laD3/5Sf9/+UoPf/laD3/5Wg9/+VoPf/l6L4/5qm+P+cqfj/lKL3/2Js + 7f8tKuD/Ihbe/ycd3/8mHd//JiLh/yUo5P8lMef/JDnp/yRB7P8jR+7/I0nw1SNJ8KcjSPBlI0jwNSNH + 8AojSfEAI0nxACNI8AAjRu8AI0nwACNI8AAjR+8AI0zxACYf4AAmH+AAJh/gACYf4DkmH+DgJR7f/yUf + 3/8mH+H/Jh/i/yYf4f8mH+D/Jh/f/yYf4P8mH9//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe + 4P8mH+D/Jh/f/yYf4P8mHuD/Jh/f/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/JR/g/yYe + 3/8lH9//Jh/g/yYf4P8mH+D/Jh/f/yYe4P8mHt//Jh7f/yUf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYc3v8lMuf/I0nx/yNH8P8jR+//I0fv/yNH8P8jRu//I0fv/yFE7/8YQO//mK/5//// + //+hoaHhAAAA/AAAAP8AAACHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYh + wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAATgAAAOIICh7/ERJX/xUWdP8JCS//AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD7AAAAhgAA + AAYAAAAAAAAAAAAAAAAAAAAAJR7fACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmHt8AJh/gACYf + 4AAmH98NJh7gWSYf4LkmH+D6Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe + 3/8lH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/JR/f/yYe4P8mH+D/Jh/f/yYe + 4P8mH+D/Jh/g/yYf4P8mHt//Jh7g/yYe3/8mHuD/JR7g/yUd4P8jGt//IBfe/x8W3v8hHN//MTDj/0dM + 5/9lbuz/fovy/5Cf9v+ap/j/m6j4/5yo+P+apvj/mqX4/5qm+P+apfj/maT4/5mk+P+Xovj/lqH4/5ij + +P+dqfj/i5j1/zk75P8iGt//Jh7g/yYd3/8mHN//Jh3e/yYc3/8mIeD/JSbi/yUv5f8lOen/JEDs/yNG + 7/UjSfHLI0nxnyNJ8WAjSPA0I0bvCCNJ8AAjSPAAI0fvACNM8QAlPOkAJh/gACYf4AAmH+AAJh/gJyUf + 37YmH+D/Jh/h/yYf2v8mH9//Jx/n/yYf4/8mHt//Jh7f/yYe3/8mHuD/Jh7g/yYf3/8mH+D/Jh/g/yYf + 4P8mH+D/Jh7f/yYe3/8lHt//JR7f/yYe4P8mHuD/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh7g/yUe + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf3/8lH9//Jh7f/yYe4P8mH+D/Jh/f/yYf4P8mHt//Jh7f/yYf + 4P8mH+D/Jh/g/yYe3/8mHt//JD7s/yNI8P8jR/D/I0bv/yNG7/8jR+//I0fw/yJG7/8WO+7/fpn3//// + ///c3NveFhYW4QAAAP8AAAD/AAAAcgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYh + wAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAWgMEBfQPEUD/FRZ6/xYThP8VFHz/Bgkd/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAACbAAAABwAAAAAAAAAAAAAAAAAAAAAAAAAAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh7fACYf + 4AAmH+AAJh/fACYe4AAmH+ACJh/gPiYe4KEmH+DuJh7g/yYe4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Ixnf/x4T + 3v8aEN3/GA7d/x8c3v8vLuL/QUfm/1dh6v9rce7/c3zw/3SD8P9zf/D/eYbx/4KQ8/+DkvP/ipn1/42b + 9f+Qnfb/nKf5/5Sj9/85PeT/Ihnf/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYc3/8mHN//Jhzf/yYg + 4P8lJeH/JS7l/yQ46f8kQO3/I0bv9CNI8MojSfCQI0jwSSNH7wUjTPEAJTzpACQ/7AAmH+AAJh/gACYf + 4AAmHuAFJyDjZh0ZpNoWFIH/GBWI/x8ar/8lHtX/JyDl/ycg5v8nH+X/Jh7i/yYe4f8mH+D/Jh/g/yYf + 3/8mHt//Jh/g/yYf4P8mHuD/Jh/g/yUe3/8mHt//Jh/f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHt//JR/f/yUf + 4P8mHuD/JR7f/yYf4P8mG9//JSzk/yNJ8P8jR/D/I0bv/yNH7/8jRu//I0bv/yNH7/8WPO7/W3z0//f7 + //////71UFBR1gAAAP0AAAD/AAAA/wAAAGYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYh + wAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAWQMGDfUSFFj/FhWC/xUTff8VE3n/FhR+/w8PU/8MECb/AgQA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAJAAAAAAAAAAAAAAAAAAAAAAAAAAACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYe + 3wAmH+AAJh/gACYf3wAmHuAAJh/gACYf4AAmHuAAJh7gKyYe4IYlHt/cJh7f/yUe3/8mH9//Jh/f/yYf + 4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh7g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yUf4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/y4u + 4v85OuT/QUDm/0hH5/9OVun/U13q/1pk6/9aY+v/WGPq/11r6/9XZ+r/SlXo/0BG5f8wNOL/JSPg/y0q + 4f80MuP/NjPj/z465f82M+P/JB3g/yYf4P8mHuD/Jh/g/yYf4P8lHt//Jh7g/yYf4P8mHuD/Jh7f/yYe + 3/8mHeD/Jhzf/yYc3v8mHN//JiDg/yYl4v8lLeX/JDjp/yRB7P8jR++4I0zxJiU86QAkP+wAI0nwACNH + 8AAmH+AAJh7gACcg4wAdFaEZHxmrgRoWk+IUEnL/FhOA/xsYnP8gG7r/Ix3O/yYf2P8mH+H/Jx/k/ycf + 5P8mH+P/Jx/k/ycf5f8nH+X/Jx/m/ycf5/8mH+f/Jx/l/ycf5f8nHuT/Jh/i/yYf4f8mH+D/Jh7g/yYf + 4P8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/f/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh7f/yYf3/8mHN//JiLh/yRD7f8jSPD/I0bv/yNG8P8jRu//I0fw/yRH7/8YPe//Qmby/+nv + /v/////+o6Ok5AAAAPUAAAD/AAAA/wAAAP8AAABfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAmIcAAJiHAACYhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAOQUIE/ASFGT/FhSC/xUTe/8UEnj/FBJ1/yAbx/8oIPb/JyLb/yAfpf8RFT7/AgMA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAAYQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYf4AAmH+AAJh/gACYe + 3wAmHt8AJh/gACYf4AAmH98AJh7gACYf4AAmH+AAJh7gACYe4AAmHuAAJR7fGCYf4GomH+DIJh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH9//Jh7f/yYf + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Ixvf/yws + 4v+BkvP/lqX3/5Wj9/+ap/j/m6f4/5qn+P+apvj/mqb4/5qm+P+Zpfj/maT4/5ql+P+SoPf/g47z/2Ns + 7f8zNOP/GxLe/yEY3/8gGd//Ihnf/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 3/8mH9//Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHd//Jhzf/yYc3/8mIOD/JiXi/yU16N8lPOlAJD/sACNJ + 8AAjR/AAI0fwACNH8AAjSfEAHRWhAB8ZqwAnH+McIBiwmRQSdv8UEnT/FBJ1/xUUff8XFIX/FxWM/xgW + j/8bF5j/Gxia/x0Zof8dGaT/Hhqs/x8asf8fGrf/IBu//yIdxP8jHcn/JB7P/yUf1v8mH+D/JyDk/ycg + 5v8mH+H/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh7f/yYe3/8mH+D/Jh/g/yYf + 3/8mH+D/JR/f/yYf3/8mHuD/Jh7f/yM86/8jSfD/I0fv/yNH7/8jR/D/I0fw/yRH8P8dQO//LFLx/8/c + /f//////5ubn/R0dHvcAAAD/AAAA/wAAAP8AAAD/AAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAHgcJF9sUFW3/FROB/xUTe/8UEnf/FBJ1/x0ZrP8lHuv/JR3q/yUd7f8mHvX/JyLq/x4f + i/8HCRH/AAAA/wAAAP8AAAD/AAAA/wAAAOYAAAAhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh/gACYf + 4AAmHt8AJh7fACYf4AAmH+AAJh/fACYe4AAmH+AAJh/gACYe4AAmHuAAJh7gACUe3wAmH+AAJh/gCSYe + 304mH+CwJh/g+yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yYe + 3/8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/JR/f/yUf3/8mHt//Jh/f/yYe3/8mHuD/Jh/g/yYe + 4P8iHd//TVPo/4+d9f+eq/n/l6P4/5Sg9/+VoPf/laD3/5Sg9/+UoPf/lKD3/5Wg9/+UoPf/lqH3/5ik + +P+cqfn/kJ72/1BY6f8eGN//JR3f/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/f/yYf3/8mH+D/Jh7f/yYd3/8mHd//JiPh6SQ/ + 7HEjSfBhI0fwTiNH8EojR/A4I0nxMSNK9CMjR+4VJD/vCSQn0wMXE399FBF0/BQQdf8UEHP/FA9w/xMP + b/8SD27/Eg5t/xMPbf8TEG7/ExBv/xIQcP8TEHH/ExFy/xQSc/8VE3f/FRN7/xYUgP8XFIT/FxWJ/xsY + mf8gG7v/Jh/h/yYf4/8mH+D/Jh7g/yYf4P8mH+D/Jh7f/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHt//Jh7g/yYf4P8lHt//Jh3f/yU46P8jSfD/I0fw/yNG7/8jR+//I0fw/yNH8P8gRO//G0Pv/7HF + +////////////25ub/8AAAD/AAAA/wAAAP8AAAD/AAAA/AAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAABAYIE7ITFGz/FRKA/xUTe/8UEnX/FhR+/yAavf8mHuz/JR7p/yUe6P8lHuj/JR7n/yUd + 7P8nH/b/IyK3/wsOIf8AAAD/AAAA/wAAAP8AAAD/AAAAhgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAmH+AAJh7fACYe3wAmH+AAJh/gACYf3wAmHuAAJh/gACYf4AAmHuAAJh7gACYe4AAlHt8AJh/gACYf + 4AAmHt8AJh/gACYe4DcmHuCXJh/g6iYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/JB3g/yUd + 4P8lHuD/JR7g/yYe4P8mHuD/Jh7g/yYe4P8mHuD/Jh7g/yYe3/8mH9//Jh/g/yUf3/8lHt//Jh/g/yYe + 3/8mHuD/JR3g/x4V3v8uLOP/Y23t/5Cd9f+bp/j/lqH3/5Sg9/+UoPf/laD3/5Wg9/+VoPf/lKD3/5Wg + 9/+UoPf/lJ/3/5ij+P+Zpvj/U1vp/x4X3v8lHt//Jh/g/yYf4P8mH9//Jh/g/yYe4P8mHuD/Jh7g/yYe + 4P8mHuD/Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYb + 3/8lLOX/I0nw/yNH8P8jR/D+I0bw9CNG7/AjR/DoI0jw4CNJ8dwjSPDTIT/ezR80w/gfMbz/HzG+/x4t + tf8cLLH/HCqt/xkmpP8aJaT/GiGZ/xofl/8ZG4//GBmK/xcWg/8WE37/FRF3/xURdv8UEHP/ExBy/xQS + dv8UE3X/FBN0/xsXmf8mH9v/Jx/l/yYf4f8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf + 4P8mH+D/Jh/g/yUe3/8mHuD/Jh3f/yQz5/8jSfH/I0fw/yNH7/8jRu//I0bv/yNH7/8jRu//GD3u/42m + +P///////////9TU1f8JCQn/AAAA/wAAAP8AAAD/AAAA/wAAAPAAAAAyAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAQEAAAMFCHcPEF3/GBiE/xgZf/8UEnP/FxWI/yIc0P8mHu//JR7p/yUd6P8lHef/JR7n/yUd + 5/8lHuf/JR3o/yYe9v8kIsf/DA8h/wAAAP8AAAD/AAAA/wAAAOAAAAAaAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAI0bvACNH7wAjRu8AI0fvACNG8AAmH98AJh7gACYf4AAmH+AAJh7gACYe4AAmHuAAJR7fACYf + 4AAmH+AAJh7fACYf4AAmHuAAJh7gACYe4CMmHuB7Jh/g1iYf4P8mH+D/Jh7g/yYf4P8mH+D/JyDg/ycf + 4P8lHuD/Ixzg/yAb4f8lH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf + 3/8mH+D/Ihvh/yIc4P8mHuD/Kize/yEf3v8zM+L/aHTt/5ak+P+Wovf/lKD3/5Sg9/+Un/f/lJ/3/5Wg + 9/+UoPf/lKD3/5Wg9/+UoPf/l6P4/5ak9/9ESuf/HhXe/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf + 3/8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mHt//Jh/g/yYf4P8mH+D/Jh/g/yUf + 3/8mHN//JSXh/yNH7/8jR/D/I0fv/yNH7/8jR/D/I0fw/yNH8P8jRu//I0fv/yRJ9P8kSvX/JEr2/yRJ + 9f8kSvb/JEn1/yRJ9v8kSvf/JEr2/yRH8P8kRu//I0Tq/yND6P8iQeT/Ij/g/yE62f8hNcz/Hy69/xoh + m/8VEnf/FBF2/xUTev8UEnT/GBaN/yQdzf8nIOX/Jx/m/ycg5f8nIOb/Jx/n/ycf5v8mH+T/Jh/h/yYe + 4P8mHuD/Jh/g/yYf4P8mHt//Jhve/yUw5v8jSfD/I0fv/yNH7/8jR+//I0fv/yNH7/8kR+//GD3v/1R2 + 9P/7/v////////////9jY2T/AAAA/wAAAP8AAAD/AAAA/wAAAP8aGhrngoOEI4KDhACCg4QAgoOEAIKD + hACCg4QAgoOEAIKDhACCg4QAgoOEAIKDhACCg4QAgoOEAIKDhACCg4QAgoOEAIKDhACCg4QAgoOEAIKD + hACCg4QAgoOEAIKDhACCg4QAJiHAACYhwAAmIcAAJiHAACYgwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAEBAEALDEP0ISGN/zg/nv8XF3f/GRWR/yQd3f8lHu7/JR3o/yUd5/8lHuj/JR7o/yUd + 5/8lHej/JR3n/yUd6P8lHef/Jx72/yMjtf8GCRD/AAAA/wAAAP8AAAD/AAAASwAAAAAAAAAAAAAAAAAA + AAAAAAAAI0bvACNG7wAjR+8AI0bvACNH7wAjRvAAI0fwACNH7wAjR+8AI0fwACNG7wAjR+8AI0fvACNH + 7wAjRu8AJh/gACYe3wAjR+8AJh7gACYe4AAmHuAAJh7gACYf4AsmG99XJhresCYc3+8mHd//Ihjh/z9E + 2/+tr8j/p6nK/5iezP+TnM7/TlrZ/yEZ4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7f/yYe + 3/8lHt//Ihvh/3F61P+co8v/rq3I/7i+xv9VXNj/GxHg/yAZ3/9RWun/l6T3/5ei9/+UoPf/lKD3/5Sg + 9/+VoPf/lKD3/5Sg9/+VoPf/lKD3/5Sg9/+ZpPj/jJr1/zg65P8eFd7/Jh7f/yYf3/8mHuD/Jh/g/yYf + 4P8mHt//Jh7g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh7f/yYf4P8mHuD/Jh/g/yYf + 4P8mHd//JiDg/yQ76v8jSPD/I0fv/yNH7/8jR+//I0bv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH + 8P8jR+//I0bv/yNH8P8jR+//I0fv/yNH7/8jR/H/I0fw/yNH8f8jSPL/I0jz/yRI8/8kSfP/I0n1/yRK + 9v8kSfP/HzjL/xgai/8VEHX/FBN5/xMSdP8WFHz/Gxic/x4Zq/8eGan/Hhms/x8Ztv8hG7//JB3N/yUf + 3f8nH+T/Jx/l/yYf4f8mHt//Jh3f/yUu5v8kR/D/JEfw/yNG7/8jR/D/I0bv/yNH8P8jR+//Ikbv/xtA + 7v+6yvv////////////d3d7/DAwM/wAAAP8AAAD/AAAA/wAAAP8AAAD/R0dJ3uHh4RPh4eEA4eHhAOHh + 4QDh4eEA4eHhAOHh4QDh4eEA4eHhAOHh4QDh4eEA4eHhAOHh4QDh4eEA4eHhAOHh4QDh4eEA4eHhAOHh + 4QDh4eEA4eHhAOHh4QDh4eEA4eHhACYhwAAmIcAAJiHAACYhwAAmIMAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAABcFBh7WHh6C/15lwP8iI4L/FxOY/yUe4/8lHuz/JR7n/yUe6P8lHef/JR3o/yUe + 6P8lHuj/JR3o/yUd5/8lHef/JR3n/yUd6P8nIPj/Ghx6/wAAAP8AAAD/AAAA/wAAAHAAAAAAAAAAAAAA + AAAAAAAAI0bvACNG7wAjRu8AI0fvACNG7wAjR+8AI0bwACNH8AAjR+8AI0fvACNH8AAjRu8AI0fvACNH + 7wAjR+8AI0bvACNH8AAjR/AAI0fvACNG7wAjR+8AI0jwACNI8AAkPOsKJTPnISUv5l0kNejCJS3l/yMc + 4P8xLdz/vb7F/8zMw//Pz8L/qrDJ/zIy3v8iGuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 3/8mH9//JR7g/x4X4f+Kk8//09LB/8nJw//S0sH/fH7R/xwW4v8kHOD/Hxnf/1BZ6f+XpPf/mKP3/5Sg + 9/+UoPf/lKD3/5Sg9/+UoPf/lJ/3/5Sg9/+UoPf/lJ/3/5ql+P+Jl/T/PD/k/x0W3v8gF97/Ixrf/yQc + 3/8lHeD/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mHt//Jh/f/yYf4P8lHt//Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mHN//JSPh/yM+7P8jSfD/I0bv/yNH8P8jR/D/I0bv/yNH7/8jR/D/I0bv/yNH7/8jR/D/I0fv/yNH + 7/8jR+//I0fw/yNG7/8jR/D/I0fv/yNH7/8jR/D/I0fw/yNH7/8jRu//I0fw/yNH7/8kRu//JEfv/yNH + 7/8jR+//I0fw/yRM+/8kRev/Gx+Y/xcUgf8ZFo3/FxWC/xQSdv8UEnX/FBJ1/xQSdf8UEnP/FRN3/xYU + fv8XFYj/HBie/yIcv/8mHuD/Jh7j/yUz6P8jSPD/I0jw/yNG8P8jRu//I0fw/yNH7/8jR/D/I0fv/xw/ + 7v9CZfL/9fn/////////////eXl6/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/2BgYdY4ODkJODg5ADg4 + OQA4ODkAODg5ADg4OQA4ODkAODg5ADg4OQA4ODkAODg5ADg4OQA4ODkAODg5ADg4OQA4ODkAODg5ADg4 + OQA4ODkAODg5ADg4OQA4ODkAODg5ADg4OQAmIcAAJiHAACYhwAAmIcAAJiDAACYgwAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAABAgSXEBJg/2xxz/88P5n/ExCQ/yUe5f8lHuz/JR3n/yUd5/8lHej/JR3o/yUd + 6P8lHej/JR7o/yUe6P8lHej/JR3o/yUd6P8lHej/JR3v/yYi2f8JCyD/AAAA/wAAAP8AAACMAAAAAAAA + AAAAAAAAAAAAACNG7wAjRu8AI0bvACNH7wAjRu8AI0fvACNG8AAjR/AAI0fvACNH7wAjR/AAI0bvACNH + 7wAjR+8AI0fvACNG7wAjR/AAI0fwACNH7wsjRu8xI0fvYyNI8IsjSPC5I0jw5CNK8fQjSfH/I0nw/yNI + 8P8iPu3/JTPm/56lzP/KycP/zMvD/4GJ0P8aFeL/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8bFOL/X2PW/83Mwv/ExMT/zcvC/5Wezf8jIeH/JR3g/yUd4P8gGd7/UFnp/5Si + 9/+cp/j/mKP4/5ik+P+YpPj/maX4/5ml+P+Xo/j/lqH3/5Wg9/+UoPf/maT4/5Cd9v9kbO3/Q0bm/zQ3 + 4/8tK+H/JyHg/yEc3/8hGN//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYe4P8mH+D/Jh/g/yYe + 4P8mG9//JSrj/yND7f8jSfD/I0fv/yNH7/8jR/D/I0fw/yNG7/8jR/D/I0fw/yNH7/8jR/D/I0bw/yNH + 7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNH8P8jRvD/JEbw/yNH + 8P8jR/D/I0nz/yRK9f8iQN//HjC7/xoflf8WFID/FhR//xURev8UEHT/FA9y/xQPcv8UEHP/Ew90/xQQ + df8UEnb/FBJ3/xQSdf8UEnT/Hhqt/yU04f8jR+7/JEn0/yRK9v8jSPP/I0fw/yNH7/8jRu//I0fv/yNH + 7/8VOu7/epT2////////////7O3t/xwcHP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP+oqKnS7+/vB+/v + 7wDv7+8A7+/vAO/v7wDv7+8A7+/vAO/v7wDv7+8A7+/vAO/v7wDv7+8A7+/vAO/v7wDv7+8A7+/vAO/v + 7wDv7+8A7+/vAO/v7wDv7+8A7+/vAO/v7wDv7+8AJiHAACYhwAAmIcAAJiHAACYgwAAmIMAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAABLAwUv/F1hwv9tcsX/EhCH/yMc3v8lHuv/JR3n/yUd5/8lHen/JR3o/yUe + 6P8lHej/JR3n/yUd5/8lHej/JR3n/yUd6P8lHej/JR3o/yUd6P8nH/b/GBpt/wAAAP8AAAD/AAAAmAAA + AAAAAAAAAAAAACNH8AAjRu8AI0bvACNG7wAjR+8AI0bvACNH7wAjRvAAI0fwACNH7wAjR+8AI0fwACNG + 7wAjR+8AI0fvCCNH7yMjRu9YI0fwhCNH8LQjR+/iI0fv9yNG7/8jR+//I0fv/yNH8P8jR/D/I0bw/yNG + 7/8jR/D/Ikfw/xxE8v+EltX/zsvB/8fHxP+4vMb/ODrc/yEX4f8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yUe + 3/8mHuD/Jh/g/yUf3/8mH+D/Hxbg/0FD3P+9w8X/x8bD/8fHw/+1ucb/MS3e/yIb4f8mH+D/Jh3f/yEZ + 3/9AQ+X/cX3w/4GP8/+BjvL/gY7y/3aE8f92hfD/g5Dz/5Gd9v+Ypfj/mqX4/5Wg9/+Woff/mqb4/5Wj + 9/+OnPb/jJj1/4OP8/9seO//QUXm/yId3/8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe + 4P8mHd//JTDm/yNG7/8jSPD/I0fw/yNH7/8jRu//I0fv/yNH8P8jRu//I0bv/yNH8P8jR/D/I0fw/yNG + 7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNG7/8jR+//I0fw/yNH8P8jR/D/I0fv/yNH8P8jR/D/I0fw/yNG + 8P8jR/H/JEr2/yI+3P8bJ6X/FhV//xMOcf8TD3H/FBF2/xcXgf8aIJb/HCem/xwssf8bLLP/Gymq/xwk + n/8ZH5b/FxmH/xUTev8UEHT/FBF2/xUTev8WGIL/GR2P/xsnpf8eMb//Ij/b/yNH8P8kSvb/I0jz/yNH + 8P8iRu//GT/v/6zA+////////////52dnf8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8ODg7/0dHS0v// + /wf///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////ACYhwAAmIcAAJiHAACYhwAAmIMAAJiDAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAALAAAHyTA0if+WnPD/KyyR/x0Wz/8mHu7/JR7o/yUe6/8lHuv/JR3m/yYd + 6v8lHer/JR3o/yUd5/8lHef/JR3o/yUd6P8lHuj/JR7n/yUd5/8lHef/Jh70/yAesv8DBAL/AAAA/wAA + AKgAAAAAAAAAACNG7wAjR/AAI0bvACNG7wAjRu8AI0fvACNG7wAjR+8AI0bwACNH8AAjR+8AI0fvECNH + 8D8jRu9xI0fvoCNH79UjR+/1I0fv/yNH7/8jRu//I0fw/yNH8P8jR+//I0fw/yNG7/8jR+//I0fv/yNG + 7/8jR/D/I0bv/yNG7/8ZP/L/a4Dc/8zLwv/ExMT/zczD/32E0v8bF+L/Jhzf/yYe4P8mH+D/Jh/g/yYf + 4P8lHt//Jh7g/yYe4P8lH9//Jh/g/yQb3/8nJuD/rK/I/8nJw//FxcT/xsfE/0dN2/8gGOH/Jh7f/yYf + 4P8mHuD/Ihnf/yMd3/8pJOH/KSTg/ykk3/8mIOD/JiHg/yoj4f80NOP/TFLo/3R/8f+Wo/f/mKT4/5Sg + 9/+VoPf/lqL3/5ei9/+Yo/j/nKf4/5Sk9/9FSeb/Ihnf/yYe4P8mH+D/Jh7f/yYf4P8mHuD/Jh/g/yYd + 4P8mIOD/JDjo/yNJ8P8jR/D/I0bw/yNG8P8jRu//I0bv/yNH7/8jRu//I0fv/yNH7/8jR/D/I0fw/yNG + 7/8jRu//I0fw/yNG7/8jR/D/I0fw/yNH8P8jRu//I0bv/yNH8P8jR/D/I0fw/yNH7/8jRvD/I0fv/yNH + 7/8kSfX/I0bs/x0ss/8WFX7/Ew5x/xQPcf8WFoH/GyWj/x82x/8iQuT/I0bv/yRI8/8kSvf/JEr4/yRJ + 9f8kSPH/I0bv/yJE6f8hO9P/HCuv/xgcjf8VEnn/FA9y/xMPc/8TD3P/FBB1/xUUff8ZHpP/HS+5/yJB + 4P8kSfT/IUb0/yhN8f/e5v3///////7///9BQUL/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/LCws//n5 + +dP///8H////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wAmIcAAJiHAACYhwAAmIcAAJiDAACYg + wAAmIMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAagkMQP+AhOH/Zmu9/xYRq/8mHe7/JR3n/yYd7P8jHdr/HBmm/xoW + kv8cGKH/Ix3Z/yYe6/8kHef/JB3n/yUe6P8lHef/JR7o/yUe6P8lHef/JR3n/yUd7/8mI9H/Cg8Z/wAA + AP8AAACsAAAAACZM/QAjRu8AI0fwACNG7wAjRu8AI0bvACNH7wAjRu8EI0fvGyNG8E4jR/CDI0fvtiNG + 7+cjR/D+I0fv/yNG7/8jRu//I0fw/yNG7/8jR+//I0bv/yNH8P8jR/D/I0fw/yNG7/8jRu//I0fv/yNH + 8P8jRu//I0fv/yNH8P8jR+//HEHx/09v5P/Fx8T/xcTD/8jGw/+7wMf/OFLn/x8p5/8mIeD/Jh3f/yYe + 4P8mHt//Jh/g/yYf4P8lHt//Jh7g/yYf4P8mHuD/Hhfh/4ePz//My8L/xMTE/8vNw/9pbNT/HRTh/yYe + 4P8mH+D/Jh/g/yYe4P8lHd//JB3f/yUd4P8lHeD/JR3g/yUd4P8kHeD/Ixrf/x8X3v8hHN//Qkfm/4iV + 9P+ZpPj/lKD3/5Sg9/+UoPf/lJ/3/5Sg9/+erPn/X2Xs/x8W3/8mHuD/Jh/g/yYe3/8lHt//Jh7g/yYc + 3/8mJuL/JD/s/yNJ8P8jR/D/I0fv/yNH8P8jRu//I0fv/yNH7/8jR/D/I0bv/yNH7/8jR+//I0fw/yNG + 7/8jRu//I0fv/yNH8P8jRu//I0fv/yNH8P8jR+//I0bw/yNH7/8jRu//I0fv/yNH7/8jRu//I0bv/yNH + 8P8kSvb/IT3Y/xkekf8TD3D/FBFz/xcZif8eLrf/IkHg/yRJ9P8kSvb/I0jy/yNH8P8jR/D/I0fw/yNH + 7/8jR/D/I0fw/yRH8P8kSPH/I0n0/yRJ9f8jRez/ITvV/x0ttf8YHpH/FhR8/xQPc/8UEHT/FBB1/xQQ + dP8WFH//GyWj/xg01f9JbfT/9fn+///////T09T/CQkK/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/01O + T//////B////A////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJiHAACYhwAAmIcAAJiHAACYg + wAAmIMAAJiDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAEQAACdU4PJP/lZrt/yQkj/8fGNn/Jh7r/yUd6/8kHd7/GBaL/xMS + cv8UEnX/ExJx/xkWlP8kHeb/JR7o/yUd5/8lHuj/JR7o/yUd5/8lHef/JR3o/yUd6P8lHer/JyHq/wsL + Of8AAAD/AAAAnwAAAAAmTP0AI0bvACNH8AAjRu8FI0bvIyNG71UjR++OI0bvxCNH7/IjRvD/I0fv/yNH + 7/8jRu//I0bv/yNH8P8jRvD/I0fv/yNH8P8jRvD/I0fv/yRH8P8jRu//I0fv/yNH8P8jR+//I0fv/yNH + 7/8jR+//I0fw/yNH8P8jR/D/I0bv/x9E8P81V+r/vr/F/8bGw//ExMT/zcvB/36T2P8YQfL/I0Lt/yUz + 6P8mJOL/JRzf/yYd3v8mHt//Jh/g/yYf4P8mHuD/Jh/g/xoU4f9jZdX/zc3C/8TExP/OzcL/iI/P/x4b + 4f8lHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8lH9//JR3f/xwT + 3f89QeT/k6D3/5ei+P+Un/f/lKD3/5Wg9/+apvj/jZv2/zc45P8jG9//Jh7f/yYf4P8mH+D/Jh3f/yYd + 3/8lLeX/I0Xu/yNJ8P8jR/D/I0bw/yNH7/8jRu//JEbw/yNH8P8jR+//I0fw/yNH7/8jRu//I0bv/yNH + 7/8jRu//I0fw/yNH8P8jR+//I0fw/yNH7/8jR/D/I0fw/yNG7/8jR+//I0bv/yNH8P8jR/D/I0bv/yNH + 8P8kSvb/IDXI/xYVf/8TD2//FxOB/x8htf8jROj/JEr3/yNI8/8jR/D/I0fv/yNH8P8jRu//I0bv/yNH + 7/8jR+//I0fw/yNH7/8jR+//I0fv/yNG8P8jR/D/I0jx/yNJ9P8kSfb/JEfv/yE92P8dLbX/GR6T/xYU + ff8UEHX/FBF2/xQQdP8GBXX/bHO/////////////oqKi/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP+Ghob/////t////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ACYhwAAmIcAAJiHAACYh + wAAmIMAAJiDAACYgwAAlIMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF8FBzf/cHXU/1tftP8QDZT/Jh7s/yUd6P8mHu3/Gxik/xQT + c/8VE3v/FBN6/xQSef8UEnf/IRvH/yYe7P8lHef/JR3n/yUd5/8lHef/JR7o/yUe5/8lHej/JR3o/ycf + 9f8RD07/AAAA/wEDCYInTf8AJkz9HiNG71MjR/CNI0fvxCNH7/UjRu//I0fv/yNH8P8jR+//I0fw/yNH + 8P8jR/D/I0fv/yNG7/8jR/D/I0fw/yNH8P8jR+//I0fw/yNH7/8kRvD/I0bv/yNH7/8jR+//I0fv/yNH + 7/8jR/D/I0fv/yNH7/8jRvD/I0fw/yNG7/8hRO//KEvu/6myy//Jx8L/xMTE/8nIw/+5vcf/Mlbr/x5E + 8f8jSfD/JEXu/yQ36f8lJ+P/Jh7f/yYc3/8mHt//Jh/g/yYf4P8eFeH/RUfb/8DFxf/GxcT/ysnD/6Wt + yv8qJd//JBzg/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH9//Jh/g/yYe4P8mH9//JR7f/yYe + 4P8mHuD/HhXe/1Ze6v+bqPj/mKT4/5mk+P+cqPj/hZPz/z9D5f8hGd//Jh/g/yYf4P8mHt//JR3e/yYf + 4P8lNuj/I0jw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fw/yNH8P8jR/D/I0fv/yNH7/8jRu//I0bv/yNH + 7/8jR+//I0fv/yNH8P8jR/D/I0fw/yNH8P8jR+//I0bv/yNH8P8jR+//I0fw/yNH8P8jR+//I0fv/yNH + 8P8jSff/HjPB/xURd/8TEHD/GheT/yUd0v8nH+f/JTPp/yNI8P8jR/D/I0bv/yNH7/8jR/D/I0bv/yNH + 8P8jR/D/I0bv/yNH8P8jR+//I0fw/yNH7/8jRu//I0fv/yNH8P8jR/D/I0fw/yNH8f8jSPT/JEr2/yNH + 7/8iPtv/Hi+4/xcZhv8UEHT/BQNv/36Etv///////////3x9fv8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8BAQH/srKy/////7H///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wAmIcAAJiHAACYh + wAAmIcAAJiDAACYgwAAmIMAAJSDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIBAga5Ghxv/3+E3/8nKIX/GRSs/yYe7/8mHuz/IhzU/xUT + fP8VE3n/EhB5/xQSev8VE3r/FBJ2/yAavv8mHu7/JR3n/yUd5/8lHej/JR7n/yUe6P8lHuf/JR3o/yUd + 6P8nH/X/FhVQ/wIDAP0YK3+mJ03/pyNH7+8jR+//I0fw/yNH7/8jR/D/I0bv/yNH7/8jR/D/I0fv/yNH + 7/8jR+//I0fw/yNH8P8jR/D/I0bv/yNH7/8jR/D/I0fv/yNH7/8jR+//I0bw/yNH7/8jR+//I0bv/yNH + 7/8jRu//I0bv/yNH7/8jR+//I0fv/yNG7/8jR+//IkXw/x9G8f+MndP/z8vB/6u1y/+8v8f/zszB/3aL + 2f8YPvL/I0fv/yNH8P8jSfD/I0fv/yQ96/8lLOX/Jh/g/yYc3/8mHt//JBrg/ykp4P+ussj/ycjD/8XF + xP/BwsT/Ojvc/yIZ4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/f/yYf4P8mHuD/Jh/g/yYf + 3/8mH9//Jh/g/yQc3/8hHN//WGHq/4iS9P9+ivL/WmPr/ywr4f8gGN//Jh7g/yYe4P8mHuD/Jhzf/yUm + 4v8kP+z/I0nw/yNH7/8jRvD/I0fw/yNG7/8jR+//I0fw/yNH8P8jRu//I0bv/yNH7/8jR+//I0bv/yNH + 7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH8P8jR+//I0fw/yNG7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH + 8P8kSvb/HjPB/xURdv8UEXL/Gxif/yYf3/8nH+X/Jh7f/yYc3/8lNef/I0jw/yNH8P8jR+//I0fv/yNH + 7/8jR/D/I0fw/yNH8P8jR+//I0bv/yNH8P8jR+//I0bv/yNG7/8jR/D/I0fv/yNH8P8jR+//I0fw/yNH + 7/8kR/H/I0j0/yRL+P8jQ+b/HCqt/wcGc/98gbP//////+Dl9v8mKln/AAAC/wAAAP8AAAD/AAAA/wAA + AP8AAAD/FRUW/+Dg4f////+U////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJiHAACYh + wAAmIcAAJiHAACYgwAAmIMAAJiDAACUgwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1AwUh9jQ1mv9lasX/Dw1x/yAavv8mHu7/Jh7t/xwY + ov8TEnP/FhR7/y41jf8aG3//FBJ5/xUTfP8iHNH/Jh7s/yUe6P8lHuf/JR3n/yUd5/8lHef/JR7o/yUd + 6P8lHOj/Jhzt/yErmf8fO6n+Jkr2/yNH8f8jR+//I0fv/yNG7/8jR+//I0fw/yNH8P8jRu//I0fv/yNH + 7/8jR/D/I0fv/yNH7/8jRvD/I0bv/yNH7/8jRu//I0fv/yNH8P8jR/D/I0fv/yNH8P8jR+//I0fv/yNH + 7/8jR+//I0fv/yNG7/8jR/D/I0fv/yNH7/8jRvD/I0fw/yNH8P8ZQPL/dYnZ/9LOwP+lsM3/kaLS/9HN + wf+3vMf/MVXq/x5C8f8jR/D/I0fw/yNH7/8jSPH/I0jw/yRA7P8lMOX/JiLh/yUc3/8fF+H/jJPP/8vL + wv/FxcT/x8rE/1Vc2P8fFuH/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh7f/yYf3/8mH+D/JR3f/yEa3v8mH+D/JB7f/yAY3/8jGt//Jh7g/yYd3/8mHuD/JiTh/yU1 + 6P8jRe//I0nw/yNH8P8jR+//I0bv/yNH8P8jR/D/I0bv/yNG7/8jRu//I0bv/yNH8P8jR+//I0bv/yNG + 7/8jR+//I0fv/yNH7/8jRu//I0fw/yNH8P8jR/D/I0fw/yNH8P8jR/D/I0fw/yNH7/8jR/D/I0fv/yNH + 7/8kSfb/IDfL/xUSd/8TEHD/HBmh/ycg5P8mH+P/Jh7g/yYf4P8mHuD/Jh3f/yQ46f8jSPD/I0fw/yNH + 7/8jR/D/I0fv/yNH8P8jR+//I0fv/yNH7/8jR+//I0bv/yNH7/8jR+//I0fw/yNH8P8jRu//I0fv/yNH + 7/8jR+//I0fw/yNG7/8jR+//I0j0/yRK+P8QKsr/e4DJ/9vc2/82O1H/AAAK/wAAAf8AAAD/AAAA/wAA + AP8AAAD/AAAA/z09Pv/+/v7/////dv///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ACYh + wAAmIcAAJiHAACYhwAAmIMAAJiDAACYgwAAlIMAAJiHAADQzxwAfF7wAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQcJRP9GR63/Sk+r/w0Kb/8hG8T/Jh/x/yMd + 2P8WFH//FBJ4/xQRef8jJYT/HB+A/xMRdf8ZFZH/JR7o/yUe6f8lHej/JR7o/yQe6P8kHef/JR7o/yUd + 5/8lGuf/JSLo/yQ47P8lSfb/JEr7/yNH8f8jR+//I0fw/yNH8P8jR/D/I0fv/yNH8P8jR/D/I0bv/yNG + 7/8jR+//I0bw/yNH7/8jRu//I0fv/yNH7/8jRu//I0bv/yNH8P8jR+//I0fw/yNH7/8jR+//I0fv/yNG + 7/8jR+//I0fv/yNH7/8jR+//I0fw/yNG7/8jRu//I0fw/yNH8P8jR+//G0Dx/1p14f/LysL/trvI/2GA + 3//AwsX/z8zB/3eM2f8YP/L/I0bv/yNG7/8jR/D/I0bw/yNH7/8jSfD/I0nw/yRE7f8lNOj/Gxrj/2pw + 0//NzcL/xMTE/87Owv93etL/HBXh/yYe4P8mH+D/Jh7f/yYe4P8lH+D/JR7f/yYf4P8mHuD/Jh/g/yYf + 4P8mHt//Jh/g/yYf4P8mHt//Jh7g/yYd4P8mHd//JRzf/yUb3/8mHN//JiDg/yYk4v8lL+b/JDzr/yRF + 7v8jSfD/I0fw/yNH8P8jR+//I0bv/yNH7/8jR/D/I0fw/yNH8P8jRu//I0bw/yNH7/8jR+//I0bw/yNG + 7/8jR+//I0fv/yNH8P8jRu//I0bv/yNG7/8jR/D/I0fv/yNH8P8jR+//I0fw/yNH8P8jR+//I0fw/yNG + 7/8kSfX/Ij/c/xYVgP8TEHD/GheX/yYf4P8mH+P/Jh/g/yYf4P8mHuD/Jh/g/yYd3/8lJOL/I0Tv/yNH + 8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH + 7/8jRvD/I0fw/yNH8P8jSPD/I0jw/yNI8P8jSfD/G0T7/1l05v8xMi3/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP98fH3//////////1v///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wAmIcAAJiHAACYhwAAmIcAAJiDAACYgwAAmIMAAJSDAACYhwAA0M8cAHxe8AAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQMFB8AMDGH/TlK0/zs7nf8NC2//IRvD/yYf + 9f8fGsH/FBN0/xQTev8VE3r/EhB5/xMRev8UE3X/IBu//yUe7v8lHuj/JR3n/yUd6P8lHuj/JR3o/yUc + 5/8lHOf/JS3q/yRE7/8jSvH/I0fw/yNG7/8jR+//I0fv/yNG7/8jR/D/I0fw/yNH7/8jR+//I0fv/yNG + 7/8jRu//I0fw/yNG7/8jR/D/I0fw/yNH8P8jR/D/I0bw/yNH7/8jR/D/I0fw/yNH8P8jRu//I0fv/yNH + 8P8jR+//I0fv/yNH8P8jRu//I0bv/yNH7/8jR/D/I0fv/yNG7/8jR/D/I0fv/x9D8P8+YOj/w8TE/8rI + w/9TcOP/iprU/9PPwP+2vMj/LlPs/x5C8f8jRu//I0fw/yNG8P8jR/D/I0fv/yFF8P8iSPH/I0rx/xs9 + 7/9JX+P/w8XF/8bFxP/MysL/lJ3N/yMf4P8lGt//Jh3f/yYd3/8mHN7/Jhzf/yYc3/8mHd//Jh3f/yYd + 3/8mHd//Jhzf/yUc3/8lHN7/Jh3e/yYg4P8mIuH/JSTi/yUq4/8lMeb/JTjq/yRA7P8jRu//I0nx/yNJ + 8f8jSPD/I0bv/yNH8P8jR/D/I0fw/yNH7/8jR+//I0fw/yNH7/8jR+//I0bv/yNG8P8jR/D/I0bv/yNG + 7/8jR/D/I0bv/yNG7/8jR/D/I0bv/yNH7/8jR/D/I0bw/yNH7/8jR+//I0bv/yNH7/8jR+//I0fv/yNG + 7/8jSPL/JEfu/xkelP8SD27/GBaJ/yYf2f8mH+T/Jh/f/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh3f/yQ9 + 6/8jSPD/I0fw/yNH8P8jR/D/I0bv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNG8P8jRu//I0fv/yNH + 7/8jR+//I0nw/yNJ8f8jR/D/I0Tt/yRB7f8kQO3/JD3r/yY99/8bJ3z/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/qamq//////T///8z////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8AJiHAACYhwAAmIcAAJiHAACYgwAAmIMAAJiDAACUgwAAmIcAANDPHAB8XvAAeF7wAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC4FBxj0EBBy/0JHqP8zM5X/Dw1x/yEc + x/8nHvX/HxnA/xQSdP8VE3r/FRN6/xUTe/8UEnb/GBWO/yUe5f8lHun/JR3n/yUd5/8lHuj/JRzn/yUb + 5/8lJOn/JDzt/yNK8P8jSPD/I0bv/yNH7/8jR/D/I0fv/yNH7/8jRu//I0fv/yNH8P8jR/D/I0fw/yNH + 8P8jR+//I0fv/yNH7/8jR+//I0bv/yNH7/8jR/D/I0fv/yNG7/8jRu//I0fv/yNH8P8jR/D/I0bv/yNG + 7/8jR/D/I0fw/yNH8P8jR/D/I0bv/yNG7/8jR/D/I0fw/yNG7/8jRu//I0fv/yNG7/8gRPD/LE7t/7K4 + yf/SzcD/boXc/0do5f/JycP/z8zB/22F3P8XPvP/I0fw/yNH8P8jRu//I0fv/yNH7/8hSO//HkPv/yNH + 8P8gRfD/LFPt/7S5yP/Ix8P/x8bD/7S5yP8uR+n/Ijfr/yQ26P8lM+f/JTHm/yUu5f8lK+T/JSzk/yUs + 5P8lLOP/JSzk/yUv5f8lMeb/JDbo/yQ76v8kP+v/JELt/yNG7/8jSPD/I0nw/yNJ8P8jSfH/I0fv/yNH + 8P8jR/D/I0bv/yNH7/8jR+//I0fv/yNG7/8jR/D/I0fv/yNH7/8jR+//I0fv/yNG7/8jRu//I0fw/yNH + 7/8jR/D/I0fv/yNG7/8jR+//I0fw/yNH7/8jR+//I0bv/yNH8P8jR+//I0fv/yNH8P8jR/D/I0bw/yNG + 7/8jR/D/JEr2/x4vuP8TD3D/FRR7/yMdx/8nIOf/Jh7f/yYf4P8mH9//Jh7g/yYf4P8mH+D/Jh/g/yYc + 3/8kPOr/I0nw/yNH7/8jRvD/I0bv/yNH8P8jR/D/I0fv/yNG7/8jR+//I0fv/yNH8P8jR/D/I0bv/yNH + 8P8jSfD/I0fv/yQ/7P8lMuf/JSnj/yYl4f8mH+D/Jh7g/yYd3/8pIOv/Fhhn/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/CwsM/8zMzv/////T////Cf///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////ACYhwAAmIcAAJiHAACYhwAAmIMAAJiDAACYgwAAlIMAAJiHAADQzxwAfF7wAHhe8AB0W + uwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbCQoo/xEPff88Q6H/LS6R/xAO + cv8gG8L/Jh7w/yQd2P8WFH//FBJ1/xUTe/8UEnb/FRN3/yIcx/8mHu3/JR3n/yUd5/8lHej/JRvn/yUf + 6P8kM+v/I0Xv/yNJ8P8jR/D/JEfw/yRG8P8jR+//I0bw/yNH8P8jR+//I0fv/yNH8P8jRu//I0fv/yNH + 7/8jR+//I0fv/yNH7/8jRvD/I0fv/yNH8P8jR+//I0fv/yNH7/8jR+//I0bv/yNH8P8jR+//I0fw/yNG + 8P8jR+//I0fw/yNG7/8jR/D/I0fw/yNH7/8jR+//Ikbv/yNG7/8jR+//I0fw/yNH7/8jRu//IUXv/yFI + 8P+XptD/08/A/5ak0f8bQ/L/m6nQ/9LOwP+yucr/K1Du/x5D8f8jRu//I0fw/yNH8P8dQe7/kKn4/1l5 + 9P8bQO//Ikbv/x5D8f+WpdH/y8jC/8XExP/DxMT/RWno/x1E8v8jSfD/I0nx/yNJ8P8jSPD/I0jv/yNI + 8P8jSfD/I0jv/yNJ8P8jSfH/I0nx/yNJ8f8jSfD/I0nw/yNI8P8jR+//I0fw/yNH8P8jR/D/I0fw/yNH + 7/8jR+//I0fw/yNG7/8jR+//I0fv/yNG7/8jRvD/I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//JEfw/yNG + 7/8jR/D/I0fw/yNG7/8jR+//I0fv/yNH8P8jRu//I0bv/yNH7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNG + 8P8jR/D/JEn0/yI/3v8WFX//ExBx/x4Zq/8nH+f/Jh/g/yYf4P8mH+D/JR7g/yYf4P8mH+D/Jh/g/yYc + 3/8lJOL/I0Xv/yNH8P8jR+//I0fv/yNG7/8jRvD/I0bv/yNH8P8jR+//I0fw/yNH8P8jR+//I0fv/yNH + 8P8jSfH/JD7r/yUp4/8mHuD/Jhvf/yYc3/8mHuD/Jh7g/yYe4P8mHt//KSDv/xoYhv8AAQD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/x4eH//w8PD/////hf///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wAmIcAAJiHAACYhwAAmIcAAJiDAACYgwAAmIMAAJSDAACYhwAA0M8cAHxe8AB4X + vAAdFrsAHBa6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkw4ROP8TEH7/MjiY/yYo + jP8RDnH/HRir/yYe7/8mHu3/IBq//xUUf/8TEnP/FhR+/x8au/8lHu3/JR3o/yUc6v8lG+j/JR3n/yQs + 6v8kQe7/I0rw/yNI8P8jRu//I0fv/yNH8P8jR+//I0bv/yNH7/8jR/D/I0fv/yNG7/8jR+//I0bv/yNH + 7/8jR+//I0fv/yNG7/8jR+//I0bv/yNG7/8jR+//I0fw/yNH7/8jR+//I0bv/yNH7/8jR+//I0fw/yNH + 7/8jRu//I0fw/yNG8P8jRu//I0fw/yNH8P8jR+//Ikbv/xxC7/8iRu//I0fv/yNH7/8jR+//HEDv/xY8 + 7/8ON/H/d4rV/9PPwP+0ucj/Iknw/1Zy4v/MysL/zszB/22E3P8XPvP/I0fv/yNG8P8hRfD/HUPv/9zj + /f+zwfr/Fzzu/yNG7/8ZP/L/b4fb/83Kwf/FxMT/y8rC/2V93v8ZPvL/I0fv/yNH7/8jR/D/I0fw/yNH + 7/8jRu//I0fv/yNH7/8jR+//I0fw/yNG8P8jR+//I0fw/yNH7/8jRu//I0fv/yNH7/8jRu//I0fv/yNH + 8P8jR+//I0bv/yNH7/8jR+//I0fv/yNG7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH8P8jR+//I0fv/yNH + 8P8jRu//I0bv/yNH7/8jRu//I0fw/yNH7/8jR+//I0bv/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0fw/yNH + 7/8jR+//I0fw/yRJ9f8cJ6f/Ew5u/xgVi/8mH93/Jx/j/yYf4P8mHt//Jh7g/yYf4P8mHt//Jh/g/yYe + 3/8mHt//JD7r/yNI7/8jR/D/I0fv/yNH7/8jR+//I0bv/yNG7/8jR+//I0fv/yNH8P8jR+//I0fv/yNI + 8P8jSO//JTTn/yYe3/8mG9//Jh7g/yYe3/8lHt//Jh/g/yYf4P8mH+D/Jh/g/ycf6P8lIr//BgcM/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP89PT///v7+8f///yr///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8AJiHAACYhwAAmIcAAJiHAACYgwAAmIMAAJiDAACUgwAAmIcAANDPHAB8X + vAAeF7wAHRa7ABwWugAeF7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALYPEUL/ExF//yMk + if8iJIf/EhBz/xkWkf8mHuf/JR3p/yYe7f8jHdn/IBrH/yQc2f8lHe7/JRzt/yUb6P8mH9z/JSvk/yQ9 + 7v8jSfD/I0jw/yNG7/8jR+//I0fw/yNH7/8jR/D/I0fv/yRH7/8jR/D/I0fv/yNG7/8jR+//I0fw/yNH + 8P8jRvD/I0bv/yNG8P8jRu//I0bv/yNH8P8jRu//I0fw/yNG8P8jRu//I0fv/yNG7/8kR+//IETv/xo/ + 7/8hRO//I0bv/yNH8P8kR/D/I0bv/yNG7/8jR+//I0fv/xxB7/+Amvf/QmHx/x5C7/8jR/D/Gj/v/0dq + 8/+ouvr/sb/7/6e14v/Ix8H/wcPF/0Jj5/8cQ/H/pK7N/9HNwP+yucn/K1Dt/x5C8f8jR+//HkLv/y5V + 8P/q8v7/tcH6/xc87v8jRvD/GT/y/1Bt5P/Gx8T/xcTE/87Lwf+DldX/HELx/yNG7/8jR+//I0fw/yNH + 7/8jR+//I0fw/yNG7/8jR+//I0fv/yNH7/8jRu//I0fv/yNG8P8jR/D/I0bv/yNH7/8jR+//I0bv/yNG + 8P8jR+//I0bv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNG8P8jR+//I0fv/yNG7/8jRvD/I0bv/yNG + 7/8jRu//I0fw/yNH8P8jR+//I0fv/yNH8P8jRvD/I0bv/yNH8P8jR+//I0fw/yJF7/8jRu//I0bv/yNG + 7/8jR+//I0fv/yNJ9P8iPtv/FRR9/xQRdP8hHL7/JyDn/yYe3/8mHuD/Jh7f/yYf3/8lH9//Jh7f/yYe + 4P8mHN//JDbo/yNK8P8jR+//I0fw/yNH8P8jRu//I0fw/yNH8P8jR+//I0bv/yNG7/8jR+//I0fw/yNH + 8P8jSfD/JTLm/yYc3/8lHt//Jh/g/yUf3/8mHt//JR7g/yYe3/8mHuD/Jh/g/yYf4P8lH+D/KCHp/xET + Q/8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/jo6P/////5n///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////ACYhwAAmIcAAJiHAACYhwAAmIMAAJiDAACYgwAAlIMAAJiHAADQz + xwAfF7wAHhe8AB0WuwAcFroAHhe8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABUAAADWDxJM/xUT + gf8YF37/Fxh9/xQSeP8WFH7/IxzY/yUd7P8kHef/JR3s/yYd8v8lHPD/JRzj/yUhxv8lMbj/JUDQ/yRI + 7P8jSfH/I0bv/yNH8P8jR+//I0fw/yNH8P8jR+//I0fw/yNH7/8jR+//I0fv/yNH8P8jRvD/I0fw/yNG + 7/8jRu//I0bw/yNG8P8jR/D/I0bw/yNG8P8jR/D/I0fv/yNG8P8jR/D/I0bw/yNH7/8jR+//IETv/zZZ + 8f9VdPP/KE3w/xg97/8jRu//I0fw/yNH8P8jR+//I0fv/x1B7v86XvH/5u/+/0Vj8v8dQe//HUHv/zle + 8f/j7P3////////////z8/D/wsLD/8zKwf9ie97/Djb1/1x44P/OzMH/zszB/2eA3f8XPvL/JEfw/xxA + 7/89YfL/3un+/1p59P8bQO//I0fw/x9D8P8xVev/uLzH/8fGw//Jx8P/o6/N/yVJ7/8iRfD/I0bw/yFE + 7/8iRe//I0fw/yNH7/8jRu//I0bv/yNH8P8jRvD/I0fv/yNH7/8jR+//I0fw/yNH8P8jRu//I0fv/yNH + 8P8jR/D/I0fw/yNH7/8jR/D/I0fw/yNG8P8jR+//I0fw/yNG7/8jR/D/I0fw/yNG8P8jR/D/I0bv/yNG + 7/8jRu//I0fv/yNH8P8jR/D/I0fv/yNH8P8jR/D/I0fv/yNG8P8jRvD/I0fw/yNG7/8kSfD/I0fv/yNH + 7/8jR/D/I0bv/yNH8P8kSvX/HSuv/xMObv8ZFpD/Jh/h/yYe4f8mHt//Jh/g/yYf4P8lHt//Jh7f/yYf + 4P8mHN//Jivk/yNI8P8jR/D/I0bw/yNH7/8jRu//I0bv/yNH8P8jR/D/I0bw/yNH7/8jR/D/I0fw/yNH + 8P8jSfD/JTfo/yYc3/8mHuD/Jh/g/yYf4P8lH9//Jh7g/yUf4P8mH+D/Jh7g/yYf4P8mH+D/JR7f/ygg + 7f8fH5X/AQIA/wAAAP8AAAD/AAAA/wAAAP8AAAD/JCQl//Hx8e////8n////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wAmIcAAJiHAACYhwAAmIcAAJiDAACYgwAAmIMAAJSDAACYh + wAA0M8cAHxe8AB4XvAAdFrsAHBa6AB4XvAAhGr0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsAAAA8g8S + S/8WFIL/FBJ6/xQSev8UEnr/FBJ1/yEbzv8mHu//JRzt/yUb5/8mH9b/JSnD/yU2s/8kQsb/I0no/yNJ + 8/8jR/H/I0fv/yNH7/8jRvD/I0bv/yNG7/8jRvD/I0fv/yNH8P8jR/D/JEfw/yRH7/8jRvD/I0fv/yNH + 7/8jRvD/I0bv/yNH7/8jRvD/I0fw/yNH7/8jR/D/I0fw/yNH7/8jR+//I0bw/yNH7/8jR/D/I0fw/x9D + 7/84XvH/i6j4/6zC+v9ig/X/GT7v/yJF7/8jRu//I0fw/yNH8P8VO+7/coz2/8/c/f8nTPD/IEPv/xY8 + 7/+Pp/j////////////////////5/8jIxv/LyMH/gpbW/xg+8/8nTe7/rLTL/9HNwP+qtMv/JEnu/xM4 + 8P8XPO//QmXy/8LW/P8tUvH/H0Pv/yNH7/8iRvD/H0Tw/5yoz//LycP/xsbD/7y/xv80V+v/H0Pw/yNF + 8P8pTvD/IUfw/yNH8P8jR+//I0bw/yNH8P8jR+//I0bv/yNG8P8jRvD/I0bv/yNH7/8jR/D/I0fv/yNG + 7/8jR/D/I0fv/yNG7/8jR+//JEfw/x9C7/8aPu//I0fv/yNH7/8jR/D/I0fv/yRH8P8kR/D/I0fv/yNH + 7/8jR+//I0bv/yJG7/8aP+//Gz/v/yJG7/8jR/D/I0bv/yNH8P8jR/D/I0fw/yNG8P8dQO//T3P0/zpg + 8v8gRO//I0bw/yNG7/8jSPL/I0To/xcZhv8UEXT/IRy//ycf5v8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mHuD/JiDg/yNA7P8jSPD/I0fv/yNH7/8jR/D/I0bw/yNG7/8jR+//I0fw/yNH8P8jR/D/I0bv/yNG + 7/8jSfD/JDzr/yYf3/8lHd//Jh/g/yYf4P8lHt//Jh7f/yYf3/8mH9//JR/f/yYf4f8mH+L/Jh/i/yYf + 4P8mHuP/JyPZ/wsNI/8AAAD/AAAA/wAAAP8AAAD/AAAA/0lJSf////+F////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8AJiHAACYhwAAmIcAAJiHAACYgwAAmIMAAJiDAACUg + wAAmIcAANDPHAB8XvAAeF7wAHRa7ABwWugAeF7wAIRq9AB8YvQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOQAA + AP0PEUH/FhOB/xUTe/8UEnv/FBJ3/xYThP8jGt//Jh3i/yYkyv8lMsH/JUDK/yRH3f8jSfD/I0f1/yNH + 8f8jRvD/I0fv/yNH8P8jR/D/I0fw/yNG7/8jRu//I0bv/yNH7/8jR/D/I0fw/yRH7/8kRu//I0bv/yNH + 8P8jR+//I0fw/yNH8P8jR/D/I0fv/yNH8P8jR+//I0bv/yNG7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH + 8P8kR/D/H0Pv/xg97/85XfL/obr5/3WT9/8YPe7/I0bv/yNG7/8jRvD/Fz7v/5y0+f+Bmvf/Fzzu/yJF + 7/8fRe//yNj8/9Hb/P9ujfb/dpP3/7TF+f/Hys//ysfA/6Wuzf8hRvD/Fz3y/2R+3v/QzcH/ysnB/26H + 3/9Scff/Kk/w/zZb8f/J3Pz/Nlrx/x5C7/8jR/D/I0bw/xpA8v94jtn/zcvB/8XFxP/Gx8P/Um/j/xs/ + 8f8bP+//d5X3/0tu8/8cQO//I0fv/yNH8P8hRfD/GT7v/xY77/8YPe//Fjzu/xo/7v8jR+//I0fw/yNH + 7/8jR+//I0fw/yNH7/8jR+//I0fv/x9C7/8yVfD/fJv3/ylN8P8hRe//I0fw/yNG7/8hQ+//I0fw/yNH + 8P8jR/D/I0fv/x9D7/8dQ+//SWvz/0Nm8v8bQe//IkXw/yNH7/8jR/D/I0fv/yBD7/8iRu//FTvu/4GY + 9v9oiPb/Gz/v/yNG8P8jR/D/JEn1/yA2x/8TEHH/GRaO/yYf4P8mH+H/Jh/g/yYf4P8mHuD/Jh/g/yYf + 3/8mHuD/Jhvf/yUy5v8jSvD/I0fw/yNH8P8jR/D/I0fw/yNH8P8jRu//I0bv/yNH8P8jR+//I0bv/yNH + 7/8jSfD/I0Dr/yYg4P8mHd//Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH9//Jh/h/ycf5v8nIOL/Jh/b/yYf + 2v8mH+L/KB/r/ygj1/8MDiP/AAAA/wAAAP8AAAD/AAAA/wAAAP9TVVfe/f7/D/3+/wD9/v8A/f7/AP3+ + /wD9/v8A/f7/AP3+/wD9/v8A/f7/AP3+/wD9/v8A/f7/AP3+/wD9/v8A/f7/AP3+/wD9/v8A/f7/AP3+ + /wD9/v8A/f7/AP3+/wD9/v8A/f7/AP3+/wD9/v8A/f7/ACYhwAAmIcAAJiHAACYhwAAmIMAAJiDAACYg + wAAlIMAAJiHAADQzxwAfF7wAHhe8AB0WuwAcFroAHhe8ACEavQAfGL0AIBm9AAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AE0AAAD/DhA3/xYTgv8VE3v/FRN7/xQRdf8aF4L/Ji22/yU6vf8kRdT/I0nt/yNI9P8jR/P/I0bw/yNG + 7/8jR+//I0bv/yNG7/8jR/D/I0fv/yNH7/8jR/D/I0fv/yNH7/8jRu//I0bw/yNH8P8jR/D/I0bw/yNG + 7/8jR/D/I0fv/yNH8P8jR/D/I0fw/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH8P8jR+//I0fw/yNH + 8P8jR+//I0bv/yNH8P8jR/D/HD/v/xxB7/+Pq/j/WHn1/xk97/8jR+//Ikbv/x1B7/+zxvv/R2fy/xxB + 7v8gRO//MFHw/77N/P8yV/H/Eznu/xY77v8WP/H/kJ/U/9HNwP+3vMf/M1js/xs/8v8sUO3/s7rJ/8nH + wP/Iysz////8/93l/f9oivX/zdz8/0Vk8v8bQO//I0fv/yNH7/8ZPvL/V3Pi/8jIw//FxcT/zszC/3OG + 2/8YP/L/GD3v/46l+P+Pp/j/ETfu/xs/7/8gRO//LlPw/1h49P+Jo/f/oLn5/4yl+P9HaPL/Fj3v/x9C + 7/8kR/D/I0bv/yNH7/8iRu//Fjzv/xc87v8ZP+7/orj5/+Hs/v8mS/D/IETv/yFF7/8vU/D/P2by/x9D + 7/8jRu//I0fw/yBE7/8jSe//orj5//H4///z+v//jKX4/xpA7/8iRu//Ikbv/yZK8P9CaPP/I0jw/xU6 + 7v+BmPb/b472/xs/7/8jRvD/I0fw/yRI8/8cJqP/Ew9t/x8btP8nIOf/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh7g/yUh4P8kQu3/I0nx/yNH8P8jR/D/I0fw/yNG7/8jRu//I0fw/yNH7/8jR+//I0bv/yNH + 8P8jSvH/Iz7r/yYi4P8mHd//Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mHt//Jh/k/ycg4v8gG7n/GReR/xQQ + hf8KB4D/DAmK/xIOsP8PEGj/AAAA/wAAAP8AAAD/AAAA/wAAAP8BAQD/IiSE7FZQ8S5WUPEAVlDxAFZQ + 8QBWUPEAVlDxAFZQ8QBWUPEAVlDxAFZQ8QBWUPEAVlDxAFZQ8QBWUPEAVlDxAFZQ8QBWUPEAVlDxAFZQ + 8QBWUPEAVlDxAFZQ8QBWUPEAVlDxAFZQ8QBWUPEAVlDxAFZQ8QAmIcAAJiHAACYhwAAmIcAAJiDAACYg + wAAmIMAAJSDAACYhwAA0M8cAHxe8AB4XvAAdFrsAHBa6AB4XvAAhGr0AHxi9ACAZvQAmH8AAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAABTAAAA/wgIJv8WFID/FRN7/xMQd/8WFoD/Iju0/yRI3v8jSfT/I0f0/yNH8P8jR+//I0fv/yNG + 7/8jR+//I0fw/yNH7/8jRvD/I0fw/yNH8P8jR+//I0fw/yNH7/8jR+//I0fw/yNH8P8jR+//I0bw/yNH + 8P8jR/D/I0fw/yNG7/8jR/D/I0bw/yNH8P8jR+//I0fw/yNH8P8jR/D/I0fv/yNG7/8jR/D/I0fw/yNH + 8P8jRu//I0fv/yNH8P8jR/D/I0fw/yRH8P8eQe//Kk/w/4em+P8tUfD/IETv/x5B7/85W/L/ssf6/ydO + 8P8gRO//IETv/zpa8f+Emfb/Fjzv/yNH7/8jR+//GD7y/2+E2//PzcH/x8fE/05q5P8ZP/L/GT/x/2yE + 2//PzcH/yMbB/7fB5P/S3f7/8fn//+zy/v9ScPP/GT7v/yNH8P8jR+//H0Pw/zRZ6v+6vsb/xsbD/8zJ + wv+SotL/H0Xx/xo+7/9hgfT/xdX8/zdd8f9NcPP/XIL1/3mX9/9+nfj/hqH4/4qk+P+wwvr/3un9/5+1 + +f8jS/D/HEDv/yRH7/8iRe//H0bv/1159P9Rb/P/IEXv/+fu/v+Rqfj/GkDv/yJG7/8gQ+//NFjx/1qC + 9f8gRO//I0bw/yNH8P8aP+//Y4T0/561+f9EY/L/c471/+rx/v9NbvL/Gj/v/yJG7/8hRe//fJ74/zZZ + 8f8UOe7/bov1/2iJ9f8bP+//I0bv/yNH8v8jROr/FxqG/xYTfv8lH9b/Jh/j/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYc3/8lMOX/I0nw/yNH7/8jR+//I0fv/yNG7/8jRu//I0fv/yNH8P8jR+//I0fv/yNH + 8P8jSfH/Izvq/yYg4P8mHN//Jh7g/yYe4P8mH+D/Jh/g/yYf4P8lH9//Jh/j/yYf3v8bGJr/FBN0/wkG + b/8XGXr/Wl+d/3mCrv9iaKX/JClM/wAAF/8AAAb/AAAA/wAAAP8AAAD/ExRH/yYg4f8dFeGtJh7gACYe + 4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJiHAACYhwAAmIcAAJiHAACYg + wAAmIMAAJiDAACUgwAAmIcAANDPHAB8XvAAeF7wAHRa7ABwWugAeF7wAIRq9AB8YvQAgGb0AJh/AACYf + vgAjSPAAI0jwACNH7wAjRu8AI0bvACNH7wAjR+8AI0bvACNH8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAaQAAAP8DBA7/FBNt/xQQef8UEHT/HzPB/yRK+/8jR/T/I0fv/yNH7/8jRu//I0fv/yNH + 7/8jR+//I0bv/yNH8P8jR+//I0fw/yNH8P8jR+//I0bv/yNH7/8jR/D/I0fv/yNG7/8jR/D/I0fw/yNH + 7/8kR/D/I0fw/yNH8P8jR+//I0fw/yNG8P8jRu//I0fw/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNG + 8P8jR+//I0bv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0bw/xk87v9UdfT/XoH1/xo+7/8NNO7/fZj3/5ux + +f8YPe7/I0bv/yBE7/83V/D/mK74/x9G8P8iRu//I0fv/x1B8f9Uc+L/yMjD/8/Mwf9shdv/GT/y/x5D + 8f8tU+z/tbzI/9HNwP+Wo8z/KVHx/3ST+P/M3/3/dZP1/xg97v8jRu//I0fv/yJF8P8iR+//pK7N/8nH + w//IxsP/r7bJ/ylN7v8bP/D/SGvz/9rn/v9dhPX/P2Py/zJW8v8fRO//Gz/v/xg97/8XPO7/Fjzv/z9i + 8f/A0Pv/xdT7/ytR8P8cQO//GD3v/4Gc9////////////6q++v+yxfr/Jkzw/yBE7/8jR+//IETw/zJW + 8f9qkPb/Ikbv/yJG7/8jR+//IkXv/1h89P8iRu//GD3v/w007v9/mvf/jKf4/xg97v8iRu//GUDv/6a9 + +v9nhfX/CzLu/4Wd9/9ggfT/G0Dv/yNH8P8jSPT/ITrS/xMSc/8bF5r/Jx/l/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYe3/8mH9//JD7s/yNJ8P8jR+//I0fv/yNH7/8jR/D/I0fw/yNH7/8jR+//I0fw/yNI + 8P8jSPD/JTXo/yYe4P8mHt//Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/i/yYg4P8bF5j/FBJz/wYE + cv89QZP/xMjY//b38P/39/D/9vbx/9vd3/+OlLz/Jy5w/wAAFP8AAQD/EhRF/ych1f8nH+f/Jh/f+SYe + 4DYmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYhwAAmIcAAJiHAACYh + wAAmIMAAJiDAACYgwAAlIMAAJiHAADQzxwAfF7wAHhe8AB0WuwAcFroAHhe8ACEavQAfGL0AIBm9ACYf + wAAmH74AI0jwACNI8AAjR+8AI0bvACNG7wAjR+8AI0fvACNG7wAjR/AAI0fwACNG7wAjR+8AI0bvACNH + 8AAAAAAAAAAAAAAAAHQAAAD/DBEg/xkghv8WFoT/HjC8/yNJ9f8jR/D/I0bv/yNH7/8jR+//I0bv/yNG + 7/8jR+//I0fv/yNG7/8jR+//I0fw/yNG7/8jR+//I0fv/yNG7/8jR/D/I0fv/yNH8P8jR/D/I0bv/yNG + 8P8jR+//I0fv/yNG7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fw/yNH + 7/8jR+//I0fw/yNH8P8jR/D/I0bw/yNH8P8jR/D/I0fw/yNH8P8hRe//JEjv/2uM9v8fRu//WHjz/+Lu + /v9SdPP/Gj/v/yNH7/8gRO//L1Hw/8LR/P8uUfH/H0Pv/yJG7/8fQ/D/OFrp/8PDxP/Ny8H/jp/T/xxB + 8f8iRvD/GkDx/3WL2f/Py8H/zMvC/1Rx4P8BK/D/gJv3/6S2+f8XPO7/I0fw/yNH8P8jRu//G0Ly/3+T + 1//NysH/xcXD/8DCxf8/Yuj/HEDx/yJI7/+zyPv/Tm/z/xY67v8hRO//Ikbw/yNH8P8jR/D/I0bw/yNG + 7/8ZPu7/HUXv/7DC+v/B0fv/HULv/ypQ8P+3yfv/eZf3/6m9+v//////gZv3/xI57v8jR+//I0fw/yBE + 8P8xVfH/f6D3/yRI7/8iRu//I0bv/yNH8P9PdPT/IEXv/yNH7/8fQu//NFny/5Kq+P8fQu//IkXv/xxB + 7/+Kp/j/rL/6/xI87v+uwvv/QmPy/x5C7/8jR/D/JEn3/x0vuf8TEG7/IBu5/ycf5v8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yUe4P8mHN//JSji/yNG7/8jR+//I0fv/yNH8P8jR+//I0fw/yNG8P8jR/D/I0fv/yNJ + 8f8kRe//JS3k/yYd3/8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/ycf5v8eGqz/FBJz/wkG + c/8/Q5P/3ODl//j28f/r6ur/6urq/+rq6v/w8O7/+Pfz/9fb5/9cY5v/CAxX/yEbyf8oIOz/Jh7f/yYc + 3/8mH+COJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmIcAAJiHAACYh + wAAmIcAAJiDAACYgwAAmIMAAJSDAACYhwAA0M8cAHxe8AB4XvAAdFrsAHBa6AB4XvAAhGr0AHxi9ACAZ + vQAmH8AAJh++ACNI8AAjSPAAI0fvACNG7wAjRu8AI0fvACNH7wAjRu8AI0fwACNH8AAjRu8AI0fvACNG + 7wAjR/AAI0bvACRK+QAGCh1sFCFW/yRF1/8kR/D/IkLl/yRI8/8jRvD/I0bv/yNH7/8jRu//I0fv/yNH + 7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fw/yNH + 8P8jRu//I0fw/yNH7/8jRu//I0bv/yNH7/8jR/D/I0fw/yNH7/8jR+//I0bv/yNH7/8jR+//I0fw/yNH + 8P8jR/D/I0fw/yNH7/8jR/D/I0fw/yNH7/8jR/D/I0jw/yNJ8f8jSvD/I0nw/xtB7/9KcvT/mbb6//z9 + //+uwPn/HEPv/yFG7/8jR+//IUfv/yNK8P/H1/3/X330/xM57v8bQe//Eznv/ydM7f+stsv/zsvB/6yz + yv8kSu//IUXw/xxA8f8+YOz/wcTH/87LwP+jr83/GkDv/0dn8/+zxvr/IUjw/yJF7/8jR+//I0fw/xk/ + 8v9cduD/yMnD/8XFw//KycL/X3jf/xg+8v8ZPu7/n7L5/2WE9f8YPe7/I0fv/yNH8P8jRu//I0fv/yNH + 8P8jR/D/JEfw/x1B7/8dRO//rcH6/4uj+P8+ZfP/T3b0/xE27v8YP+7/rsD6/8/c/P8jSO//IUTv/yNH + 7/8gRO//LlLx/6a9+v8rTvD/IUXv/yNG7/8cQO//XYH1/05x9P8TOO7/Gj7v/yhO8P+TqPf/IETv/yJG + 8P8kSO//SnHz/83a/P+pvfn/uMr7/yVK7/8hRe//I0fw/yRJ9P8aJqT/FRB3/yQez/8mH+P/Jh7f/yYf + 4P8mH+D/Jh/g/yYf4P8lHt//Jhze/yUy5/8jSfD/I0fw/yNH7/8jR/D/I0fv/yNH7/8jRvD/I0fw/yNJ + 8P8kPev/JiXh/yYd3v8mHuD/Jh/g/yYe3/8mHt//Jh7g/yYf4P8mH+D/Jh7g/ycf5P8kHdD/FhR+/wwJ + c/8mKYX/0dXe//b08P/q6er/6urq/+nq6v/p6en/6enp/+np6f/y8e3/8PHw/4aNu/8SE4f/IBjQ/ych + 5f8lJ+L/Jh7g2iYf4A4mH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJiHAACYh + wAAmIcAAJiHAACYgwAAmIMAAJiDAACUgwAAmIcAANDPHAB8XvAAeF7wAHRa7ABwWugAeF7wAIRq9AB8Y + vQAgGb0AJh/AACYfvgAjSPAAI0jwACNH7wAjRu8AI0bvACNH7wAjR+8AI0bvACNH8AAjR/AAI0bvACNH + 7wAjRu8AI0fwACNG7wAkSvkwIkLWxiVI8P8jSPf/I0jx/yRI8/8jR+//I0bv/yNG7/8jR/D/I0bv/yNH + 7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH + 7/8jR+//I0fw/yNH7/8jR/D/I0fw/yNG7/8jR+//I0fw/yNG7/8jR+//I0fv/yNG7/8jR+//I0bv/yNH + 8P8jR/D/I0fw/yNH8P8jSPD/I0nw/yNJ8f8jR/D/JETu/yQ/6/8kO+n/JDbp/yU05/8hKuX/Ljzm/87b + +v//////iJrx/xcf4v8kJ+P/Jirj/yQq5P8aJ+T/q7n1/9rk/f+uw/r/vNH8/3+Z9/8mRe3/i5jP/9LO + wf+8wMb/Pl/o/xxC8f8fRPD/L1j1/7vH2v/Jx8D/zMrC/1h04f8aQPL/pr77/zhc8v8fQu//I0fv/yNH + 8P8eQvH/PF/o/77Bxv/GxcP/zcvB/4CU1v8aQfL/GT3v/4ah+P9zjfb/Fjvu/yNH8P8jR/D/I0bv/yNH + 8P8jR+//I0fw/yNH8P8jR/D/HkLw/yFH8P+et/n/e5z3/2mL9v8WPO//Gz/v/zhc8f/y+P//XHz0/xo/ + 7/8jR/D/IUXv/yNJ7/+6zfv/Wnr0/xE37v8jRu//HkLv/zZZ8f+jvPr/WHjz/zFX8f+ww/r/l6/4/xc9 + 7v8iRu//Jkrv/ytU8P+Lovf//////4Oc9/8XPe7/I0bv/yNI8f8jRu3/GR6Q/xcThv8mH9//Jh7h/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYc3/8kO+v/I0nw/yNH7/8jRu//I0fv/yNH7/8jR/D/I0nx/yRH + 7/8lMub/JiDf/yYd3/8mH+D/JR7g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8nIOb/HRmj/xIQ + cv8LDHf/oKXG//j38f/p6er/6urq/+rq6v/p6en/6unp/+np6f/q6ur/6enq/+zs6//39/D/kpm5/xMS + jv8hH9z/I0jy/yUv5vwmHN5GJhzeACYc3gAmHN4AJhzeACYc3gAmHN4AJhzeACYc3gAmHN4AJhzeACYc + 3gAmHN4AJhzeACYc3gAmHN4AJhzeACYc3gAmHN4AJhzeACYc3gAmHN4AJhzeACYc3gAmHN4AJhzeACYh + wAAmIcAAJiHAACYhwAAmIMAAJiDAACYgwAAlIMAAJiHAADQzxwAfF7wAHhe8AB0WuwAcFroAHhe8ACEa + vQAfGL0AIBm9ACYfwAAmH74AI0jwACNI8AAjR+8AI0bvACNG7wAjR+8AI0fvACNG7wAjR/AAI0fwACNG + 7wAjR+8AI0bvACNH8AIjRu9zI0fw7SRJ9f8jR/H/I0bv/yNG7/8jRvD/I0fw/yNH7/8jR/D/I0fw/yNH + 7/8jRu//I0fv/yNH7/8jR+//I0bv/yNG7/8jR/D/I0fw/yNG8P8jR+//I0fv/yNH7/8jRu//I0bv/yNH + 8P8jR/D/I0fw/yNH8P8jR/D/I0bv/yNH8P8jR/D/I0fw/yNH7/8jRvD/I0fv/yNH8P8jR+//I0fw/yNH + 7/8jR+//I0nx/yNJ8P8jRu7/JD/r/yQ36f8lL+X/Jifj/yYi4f8mIOD/Jh3f/yYb3/8mHd//JRzf/yEZ + 3v95h+3/0Nj5//////98hOz/GRLd/yYc3/8mHd//GxLe/3eB7P//////oq7y/42S7//o7vz/xc36/4mR + 0//KycH/zMzC/1pg2P8bIOb/JS7l/x0t6f+suO7/09LH/8rIwP+hrM3/Fz3v/4ml+P9TcfP/G0Hv/yNJ + 8P8jSvD/IUjx/yVM8P+ossz/yMbD/8rIw/+grM7/Ikfw/xk+7/91k/f/fpT2/xQ67v8jR/D/I0fw/yJG + 7/8jR/D/I0fw/yNH8P8jRu//Ikbv/yJF7/8UOO//NFrx/6rB+v/H2P3/UHD0/xA27f8WO+7/ucv7/3mV + 9v8YPe//I0bw/yJF7/8fRu//nbf5/+Do/f9EaPP/Fj3u/xc97/8dQO//L1Px/6K7+v/5/P///////01w + 8/8aP+//I0bv/yZK8P85YPL/OVvx/8DR+/82WvH/H0Lv/yNG7/8jSPL/I0Po/xYYgf8bF5j/Jx/i/yYe + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYd3/8mIeD/JEHt/yNI8P8jRu//I0fv/yNH7/8jRu//I0nx/yQ/ + 7P8mKOP/Jhze/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf3/8mH9//Jh/g/yYf4P8mH+P/JR7W/xcV + gv8HBHD/Ulie//Lz7//s6+v/6enp/+rq6v/q6ur/6enp/+np6v/q6ur/6urq/+rq6v/p6en/7Ovr//X2 + 8P+PlLf/Fhaq/yND8/8jRe7/JSLgdSUi4AAlIuAAJSLgACUi4AAlIuAAJSLgACUi4AAlIuAAJSLgACUi + 4AAlIuAAJSLgACUi4AAlIuAAJSLgACUi4AAlIuAAJSLgACUi4AAlIuAAJSLgACUi4AAlIuAAJSLgACUi + 4AAmIcAAJiHAACYhwAAmIcAAJiDAACYgwAAmIMAAJSDAACYhwAA0M8cAHxe8AB4XvAAdFrsAHBa6AB4X + vAAhGr0AHxi9ACAZvQAmH8AAJh++ACNI8AAjSPAAI0fvACNG7wAjRu8AI0fvACNH7wAjRu8AI0fwACNH + 8AAjRu8AI0fvACNG7yUjR++3I0fv/yNH8P8jR/D/I0bv/yNH8P8jRu//I0bw/yNH8P8jR+//I0bw/yNG + 7/8jRu//I0bv/yNH7/8jRu//I0fv/yNG7/8jRu//I0fv/yNH7/8jRu//I0bv/yNH8P8jR+//I0fw/yNG + 7/8jR/D/I0fw/yNH8P8jR+//I0fw/yNH7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fw/yNI + 8P8jSfD/JETv/yQ76/8lMOX/Jibi/yYg4P8mHd//Jhzf/yYd4P8mHuD/Jh7g/yYe3/8mHuD/Jh7g/yYf + 3/8jGt//P0vl/z5G5P+1vvX//////1pf6P8cE97/Jh/g/yIZ3/8wMuH/6/D8/9fa+f8wNuP/JSXg/7e7 + 9//e5Of/v7/B/83Mwv92gNL/HRXh/yYc3/8bEN3/aW/t/+Pm2//CwsD/ysvE/0RM2P9dYOz/b4Hu/xwl + 4/8lMeX/JTTn/yQ26f8eNuv/hpXT/83Lwv/HxsP/ub3H/zJV6/8XO/D/Y4T1/3qQ9f8VO+7/JEnw/yNI + 7/8aQO7/HEDv/xxA7/8ZPe//FDvv/xg/7/8fRe//PGDy/5Sr+f+6z/z/d5j3/+Xw//+En/j/Q2jz/4mp + +f9QdvT/HUPw/yNJ8P8jSPD/HkTw/12E9v/t9///+fz//7HD+v9igfX/JEzx/xQ87/94mff/xtn9/3mW + 9/8cRfD/Iknw/yJJ8P8mTfH/OmTz/yBH8P8sU/H/IUXv/yNH7/8jR+//I0jy/yJA4f8UE3f/HRio/ycg + 5f8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8lHd//JSTh/yRE7v8jSPD/I0fv/yNH7/8jSPD/I0jx/yU2 + 6P8mIOD/Jh3f/yYe3/8mH+D/Jh7g/yYe3/8mHt//Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mH+D/Jx/m/yAb + uP8QDXL/Fxl9/8LG1//09O//6enp/+rq6v/q6ur/6urq/+np6f/q6ur/6urq/+np6f/p6en/6unp/+rp + 6v/s6+r/9PTt/25ys/8WL9n/I0v0/yQ66acjSPAAI0jwACNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI + 8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI + 8AAjSPAAJiHAACYhwAAmIcAAJiHAACYgwAAmIMAAJiDAACUgwAAmIcAANDPHAB8XvAAeF7wAHRa7ABwW + ugAeF7wAIRq9AB8YvQAgGb0AJh/AACYfvgAjSPAAI0jwACNH7wAjRu8AI0bvACNH7wAjR+8AI0bvACNH + 8AAjR/AAI0bvACNH708jR+/hI0fv/yNG7/8jR+//I0fv/yNG7/8jR/D/I0bw/yNG8P8jRu//I0fv/yNG + 7/8jRu//I0fv/yNG7/8jRu//I0fv/yNH8P8jRvD/I0bv/yNG7/8jR+//I0fv/yNG7/8jR/D/I0fv/yNG + 8P8jR+//I0bv/yNH7/8jR+//I0bw/yNH7/8jRu//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0nw/yNH + 7/8kPOr/JS/m/yYj4f8mHt//Jh3f/yYd4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH9//JR7f/yYf + 4P8mH+D/IBbf/0VI5P84PeP/HRzf/7/I9v/c5vv/Kynh/yIa3/8mHt//GxTe/4+Y8P//////4+r8/3J4 + 6/9NWun/197v/8nIw//KysL/naDM/yAZ4f8lHeD/JBvg/yYj4/+yu+r/1NPH/8rJwf+bn8v/i5jw/3uH + 7v8dE93/Jhzf/yYc3v8mHd//HBPh/2Zn1P/LzMP/xsXE/8PHxP9PVdr/FxPi/1Va6f96fO7/Fx3i/x8l + 4/9IUen/kqf2/0Ba6v9FYOr/XXnw/4Sb9/+ovfr/2OT+//f+///o7vv/XnDt/xQs6P9gcO7/t8H1/7LB + 9f9ne+//ITfq/yQ56f8kOen/JDnp/yE16f9MZ+//b4Lw//j9/////////////0xg7f8aKuf/JDXo/yY4 + 6P8dLOf/JDPo/yQ06P8lNOn/JDbp/yU66v8kPOv/IT3s/yNE7v8jR+//I0nw/yNK9P8hPNT/ExJ0/x4Z + sv8nIOf/Jh/f/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jhzf/yUn4/8jR+//I0jw/yNH8P8jSfD/I0Xu/yUu + 5f8mHd//Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH9//Jh7f/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 5P8bF5r/BgRt/21zrP/29/D/6urq/+rq6v/q6ur/6unp/+rq6v/q6ur/6urq/+rq6v/q6ur/6unp/+rp + 6f/q6ur/6enp/+/v7P/e3t7/O1DM/xxB8v8jSPDeI0jwFCNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI + 8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI + 8AAjSPAAI0jwACYhwAAmIcAAJiHAACYhwAAmIMAAJiDAACYgwAAlIMAAJiHAADQzxwAfF7wAHhe8AB0W + uwAcFroAHhe8ACEavQAfGL0AIBm9ACYfwAAmH74AI0jwACNI8AAjR+8AI0bvACNG7wAjR+8AI0fvACNG + 7wAjR/AAI0fwACNG73gjR+/9I0bv/yNG8P8jR/D/I0fw/yNG7/8jR/D/I0fw/yNH8P8jR/D/I0fv/yNH + 8P8jR+//I0fv/yNH7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH7/8jR/D/I0fv/yNH7/8jR+//I0fv/yNH + 7/8jRu//I0fv/yNG7/8jR/D/I0fw/yNH8P8jR/D/I0bv/yNH7/8jRu//Ikbx/yJH8v8iR/L/IT/x/yMy + 6v8lJeT/Jh3f/yYc3/8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/f/yYe4P8mH+D/Jh/g/yYf + 4P8lHt//Jh7f/yQb4P8vL+H/SFbl/x0T3/8vL+L/2eP6/3J57P8bE97/Jh7g/yEZ3/8xMuL/2N/6//// + ////////8vb+//P0+f/HyMj/yMfB/7K3x/8vMt//Ixnh/yYf4P8eFt7/Ojzl/6uz0v/JyMH/zMvD/46h + 4f98gu//IBff/yUe3/8mH+D/Jh/g/yAX4P9BRdv/v8PF/8bFxP/NzsL/b3HT/xUN4P9RSeb/d3Hr/xgO + 3f8cEt3/U1Pm//////////7/9/n9//H1/P/4+P3/2+H6/6218/9yeOv/MzTi/x8X3v8mHd//HRbf/yEd + 3/8mI9//IBnf/yYd3/8mHt//Jh7f/yYd3/8kG9//QUbk/yIe3/9WWuj/nqXx/5ih8f84MuL/Ihbe/yUb + 3v8kGd//Jhve/yYc3/8mHN7/Jhvf/yYd3/8mHt//Jh/g/yYg4P8mIuH/Jijj/yUy5/8lQPH/IDfJ/xQS + dP8gGrn/Jx/m/yYf3/8mH+D/Jh/g/yYe4P8mHt//Jh/g/yYc3v8lKOT/I0rx/yNI8P8jSfD/JD/s/yYn + 4/8mHd//Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe + 4v8lH9j/ExCC/xwefv/Q1eP/9/by/+np6f/q6ur/6unq/+rp6f/q6ur/6urq/+rq6v/q6ur/6urq/+np + 6f/q6er/6urq/+rq6v/q6er/9vPr/5up4/8ZPu7/I0fw/yNH71kjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAmIcAAJiHAACYhwAAmIcAAJiDAACYgwAAmIMAAJSDAACYhwAA0M8cAHxe8AB4X + vAAdFrsAHBa6AB4XvAAhGr0AHxi9ACAZvQAmH8AAJh++ACNI8AAjSPAAI0fvACNG7wAjRu8AI0fvACNH + 7wAjRu8AI0fwDSNH8KIjR/D/I0bv/yNG7/8jR/D/I0fw/yNH7/8jRu//I0bv/yNH7/8jR/D/I0bw/yNH + 7/8jR/D/I0fv/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fv/yNH8P8jR+//I0fw/yNH8P8jR/D/I0fw/yNH + 8P8jRu//I0fw/yNH7/8jR+//I0bv/yNH8P8jR/D/I0fv/yNH7/8iRvL/H0T2/yFG7/8lQOT/KDLX/y4y + xv8sKcr/JR3e/yUd4v8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mHuD/Jh/g/yYf3/8mH9//Jh/g/yYe + 4P8mHuD/JyDg/yYf3/8lHuD/KCDg/1Fb5/8lHt//GA/d/2Np6v+jpfP/HRXf/yYe4P8mH+D/HRXe/0RH + 5f/T2fn////////////n7/v/p6/N/8rIwf/Dx8X/Skna/x4V4v8mH+D/JiDg/xoR4P9FSdr/xMfF/8zL + wv+fpsz/h5Dv/yIc4P8lHuD/Jh/g/yYf4P8kHOD/KSXf/6ywyP/IyMP/y8rC/4yVzv8cFeD/Q0Dl/11g + 6P8cFN7/Ixvf/zAs4f9+gO3/wMj3//////++y/b/Mzbi/yIc3/8dGt//GxPe/yIa3/8mHuD/Jh7g/yYe + 4P8lHeD/JB3g/yYd4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yQd4P8mHuD/HhXf/xoW3v8aFt7/Ixvf/ycg + 4P8mH+D/Jh7g/yYf3/8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh7g/yYd3/8mHN//JyHl/yAj + vf8VE3X/IRu+/ycf5v8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8lHd//JSPh/yQ96/8kP+z/JTHm/yYg + 4P8mHeD/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mHt//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe + 3/8nH+T/IhzJ/wwKdf9BRY//sbOw/+Df3//v8O//6enp/+zs7P/t7e3/7u7u/+/v7//u7u7/7e3t/+rq + 6v/q6ur/6urq/+rp6v/p6un/6unq/+7t6v/a4Ov/NVnv/x5C8P8jR/CoI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAJiHAACYhwAAmIcAAJiHAACYgwAAmIMAAJiDAACUgwAAmIcAANDPHAB8X + vAAeF7wAHRa7ABwWugAeF7wAIRq9AB8YvQAgGb0AJh/AACYfvgAjSPAAI0jwACNH7wAjRu8AI0bvACNH + 7wAjR+8AI0bvHSNH78AjR/D/I0fw/yNG7/8jR+//I0fv/yNG7/8jRu//I0fw/yNH8P8jRu//I0bv/yNG + 7/8jR+//I0fv/yNH7/8jRu//I0fv/yNG8P8jR/D/I0fv/yNH7/8jRvD/I0fw/yNG7/8jR/D/I0fw/yNH + 8P8jRu//I0bv/yNH8P8jR+//I0fv/yNG7/8jR+//I0fv/yJH8f8fRff/JUjm/z9Ws/9baJX/b3GL/3x7 + if+HiJH/goSK/2Bljf8tLNH/JB3j/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mH9//Ixnf/zk84/87Q+P/Ihjf/yQb4P9EU+b/MjLi/yIZ3/8qJOD/c4Ds/yYi4P8lHd//Jh7g/yYf + 4P8eFt//Li7h/21x6/+Hhe7/S1Ho/4KGzv/OzsL/zc3C/2Vq1f8cFeL/JR7g/yYf4P8lHeD/HRrh/46U + zv/OzcL/ysrD/5iq4/8nJ+P/JBvf/yYf4P8mHuD/JR3g/yAc4f+Lk87/zczC/8jIw/+usMj/JCHf/0A9 + 5P9bXuj/HRTf/yYf4P8kHeD/Fw7d/x8c3/9rcer/4ej7/7a/9f8rKOH/HxXf/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mHuD/Jh7g/yYf4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8lHt//Jh7g/yYf4P8mH+D/Jh/f/yYe4P8mHuD/Jh/g/yYf4P8mH+D/JR7f/ycf + 5f8fGrP/FBNz/yEcv/8nH+b/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/JiDg/yYd + 3/8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yYe + 4P8mH+D/Jx/l/yAatf8UFHj/FBg6/wICAf9KSkv/7ezt//38/f/t7O3/3t7e/9jY2P/W1tb/2tra/+Pj + 4//09PT/9/f3//Hx8f/u7e3/6urq/+np6v/r6un/7u7q/1x47v8XPfD/I0fw4SNH8BQjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACYhwAAmIcAAJiHAACYhwAAmIMAAJiDAACYgwAAlIMAAJiHAADQz + xwAfF7wAHhe8AB0WuwAcFroAHhe8ACEavQAfGL0AIBm9ACYfwAAmH74AI0jwACNI8AAjR+8AI0bvACNG + 7wAjR+8AI0fvKCNG79QjR+//I0fv/yNG7/8jR+//I0bv/yNH8P8jR/D/I0bv/yNG8P8jR/D/I0fw/yNG + 7/8jR/D/I0fw/yNG7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH8P8jR+//I0fv/yNG7/8jR+//I0fv/yNG + 7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0jw/yFF9P8hRPH/OlK4/2txhv+Zl5b/sbC1/7i4 + wv+4ucX/urrI/76+zP+eoJf/QEKr/yIa5v8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYe + 4P8mH+D/Jh7g/yUc4P8rKOD/X27p/yQd4P8eE9//QEXk/zk64/8hF97/JSDg/0dR5v8kG+D/JR7f/yYe + 4P8mH+D/Jh/g/yMa4P8aEt7/Fw/e/xUL4P9nbdX/y83C/8zMwv+GjtD/Hhbh/yYe4P8mHt//Jh/f/x0V + 4v9JTdr/xsnE/8zKwf+vt8//LSzi/yMa3/8mH+D/Jh7f/yYe4P8cFeH/amzU/8zNwv/HxsP/vcHF/zo+ + 2/89OeX/XF/p/x0U3/8mH9//Jh/g/yYf4P8kG+D/GBDe/zc54v/Hz/j/vsj3/yYk4P8iGd//Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mHt//Jh/g/yYe4P8mHuD/Jh7f/yYe + 3/8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mHuD/Jh7g/yUe + 3/8nH+b/Hxuz/xUUeP8iHcf/Jx/l/yYe4P8mH+D/Jh7f/yYe4P8mH+D/Jh7f/yYe3/8lHt//JR/g/yYe + 3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/ycf5v8eGan/EhJh/wABBP8AAAD/AAAA/09QUP+Dg4T/TEtM/yIiI/8QEBD/DAwM/xgY + GP8yMjL/Y2Nj/42Njv/AwMD/4uHi//X09f/v7+//6urq//bz6f98kez/Fjzw/yNH7/ojR+9EI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAmIcAAJiHAACYhwAAmIcAAJiDAACYgwAAmIMAAJSDAACYh + wAA0M8cAHxe8AB4XvAAdFrsAHBa6AB4XvAAhGr0AHxi9ACAZvQAmH8AAJh++ACNI8AAjSPAAI0fvACNG + 7wAjRu8AI0fvOSNH7+MjR/D/I0fw/yNG7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jRu//I0fv/yNH + 8P8jR/D/I0fw/yNH7/8jRvD/I0fw/yNH7/8jR+//I0fv/yNH7/8jR/D/I0bv/yNH7/8jR+//I0bv/yNG + 7/8jR/D/I0fw/yNG7/8jR+//I0fv/yNG7/8jSO//I0nx/x9B9v8nPt7/UVuR/42Mif+zs7v/vL3N/7i5 + yf+3uMf/t7jH/7a4x/+7vMz/kpSU/zc5t/8iGuT/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh7g/yYf4P8mH+D/HxXf/1de5/9yfez/Ihvf/xwX3/8cFt7/HRje/11m6f9IUeb/Ihjf/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8gGOH/SlDZ/8jJw//My8P/p6rK/yQi4P8kHOD/Jh/f/yYf + 4P8kHN//IR3h/5WczP/OzcL/xcfE/01P2v8eFuH/Jh/g/yYf4P8mH+D/IBfh/0VL2//CxcT/xsXD/8vM + w/9cW9X/MC7l/0xQ5v8fFt//Jh7g/yYf4P8mH+D/Jh7g/yYf4P8eFN7/Hx3f/6659P+dp/H/HBbf/yUd + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/f/yYf4P8mH+D/Jh/g/yYe + 3/8mHt//Jh/g/yUf3/8lHt//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8mH+D/Jx/m/x8btP8VFHf/Ih3I/ycf5P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH9//Jh/g/yUe + 3/8mH+D/Jh/g/yYe4P8mHd//Jh7f/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe3/8nH+b/Gxii/w8SSP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/BgYG/ykpKv93dnf/z8/P/+zs7P/08ur/lqvs/xpA8P8jRu//I0fvbCNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AJiHAACYhwAAmIcAAJiHAACYgwAAmIMAAJiDAACUg + wAAmIcAANDPHAB8XvAAeF7wAHRa7ABwWugAeF7wAIRq9AB8YvQAgGb0AJh/AACYfvgAjSPAAI0jwACNH + 7wAjRu8AI0bvOSNH7+YjR+//I0fw/yNH8P8jR+//I0bv/yNH7/8jR+//I0fv/yNH7/8jR+//I0bw/yRH + 7/8kR+//I0bw/yNG7/8jR+//I0fw/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fw/yNH7/8jRu//I0bv/yNG + 8P8jRu//I0fw/yNH8P8jR+//I0fw/yNH7/8jSfD/I0Xv/yAz8f8vN8f/ZWeA/6Ggnf+7vMv/ubrK/7a3 + xv+4ucn/ubrJ/7i5yP+3uMj/trfA/25yhv8oJdb/JR3i/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yUd4P8fF9//bnns/6az8/+Hje//g4nu/7O79f+Kl+//JR/f/yUc + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Ihnh/zcz3f+8v8b/y8nD/7i+x/85O93/IBfg/yYf + 4P8mH9//Jh/f/xwT4f9QVNn/x8rD/8/Owf+Wnc3/Ih3g/yQd4P8mH+D/Jh/g/yQb4P8uLN7/s7bI/8fH + w//NzMP/eoHQ/y0u5P9HVOX/Ihjf/yYe4P8mH+D/Jh/g/yYf3/8mH9//Jh/g/yAX3/8kIuD/ws33/11i + 6P8dFN//Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/Jh/g/yYf4P8lH+D/JR7f/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh7f/yYf3/8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/ycf5f8eGq//FRR3/yIdyP8mH+T/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/JR7f/yYf + 4P8mH+D/Jh7f/yYc3/8jHOD/ISLi/yYj4f8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh7f/yYe + 3/8mHt//Jh/g/yYf4P8mH+H/Jx/i/xsXm/8PEkP/AAEA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/2trbP/s7Oz/8/Lr/6y76v8dQu//Ikbv/yNH + 8JAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACYhwAAmIcAAJiHAACYhwAAmIMAAJiDAACYg + wAAlIMAAJiHAADQzxwAfF7wAHhe8AB0WuwAcFroAHhe8ACEavQAfGL0AIBm9ACYfwAAmH74AI0jwACNI + 8AAjR+8AI0bvNSNG7+YjR+//I0fv/yNH7/8jR+//I0fv/yNG7/8jR+//I0fv/yNH7/8jRu//I0fv/yNH + 8P8jR+//I0fw/yNH8P8jRvD/I0fw/yNG7/8jR/D/I0fv/yNH7/8jR/D/I0fv/yNG7/8jR+//I0fv/yNH + 7/8jR/D/I0fv/yNH7/8jR+//I0fw/yNJ8f8jRu7/JDfq/yIh6f83N7X/dXd+/62tsf+8vc3/t7jH/7a3 + xv+5usr/pKWw/4SEif+ur7z/vL3O/6Kjof9JS53/IRvl/yYf4f8mH+D/Jh/g/yYe4P8mHt//Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe3/8mH+D/JB3f/xwT3v9EROT/g4nt/5Sh8P9pbOr/Ix3f/yMb + 4P8mH+D/Jh7g/yYf3/8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yQa4P8pKOD/oqzL/8zKwv/KzMP/UlLY/xwU + 4f8mH9//Jh7g/yYf4P8kHOD/Ix/h/56ky//NzML/yMrE/0tP2v8fFuH/Jh/g/yYf4P8lHeD/Ih7h/5CY + zv/MysL/ycnD/5mey/8yMuP/YHfq/yQc3/8mHuD/Jh/g/yYf4P8mH9//JR/f/yUe4P8mH+D/GA7e/2ds + 6v+iq/L/HRXe/yUd3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/f/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYe4P8mHuD/Jh7f/yYf4P8mHt//Jh7g/yYe + 3/8mH+D/Jh7f/yYf4P8nH+b/Hhqx/xUTdf8iHML/Jh/l/yYe4P8mH+D/Jh/g/yYe4P8mHt//JR/g/yUf + 3/8mH+D/Jh7f/yYf4P8gKOT/Mkrr/0Jk8/8kPev/JR3f/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf + 3/8mH+D/Jh/g/yYf4P8mHuD/Jh/h/yYf3v8aF5b/EBNO/wABAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/xQUFP/j4+P/7e3t//Hw6v/Gy+r/IUHu/yFG + 8P8jR/CzI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAmIcAAJiHAACYhwAAmIcAAJiDAACYg + wAAmIMAAJSDAACYhwAA0M8cAHxe8AB4XvAAdFrsAHBa6AB4XvAAhGr0AHxi9ACAZvQAmH8AAJh++ACNI + 8AAjSPAAI0fvGyNH79ojR+//I0fw/yNH7/8jR/D/I0fv/yNG7/8jR+//I0bv/yNG7/8jR/D/I0bv/yNH + 7/8jR/D/I0fv/yNG7/8jR+//I0bv/yNH8P8jRu//I0fw/yNH8P8jR+//I0fw/yNH8P8jRu//I0bv/yNG + 8P8jR+//I0fv/yNH7/8jR+//I0nw/yNG7/8lOOn/JCTj/yEX5v8+Pqr/gIJ//7S1vP+6u8z/trfG/7a4 + xv+2t8b/u7zM/4eIjv9cXFn/mZqk/72+zP9/goj/LS3H/yQb5P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf3/8mH+D/IBff/xkQ3v8YEt7/GxLe/yUd + 4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHeD/Hhri/46Tzv/PzsL/zc3D/3J7 + 0/8dFeL/Jh7f/yYe4P8mHuD/Jh/g/xsT4f9UWNj/yMrE/8/Owv+NlM3/IBvh/yUd4P8mH+D/Jh7g/xwV + 4v9ydNP/zs7C/8jIw/+ytcb/PEXg/52r8/8rJ+H/JBvg/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yIY + 3/83OOP/qbrz/yko4P8kG+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7f/yYe3/8mH+D/JR/g/yUe3/8mH+D/Jh7g/yYf + 4P8mH+D/Jh7f/yYe3/8mH+D/Jx/m/yAbtP8UE3P/IRu9/ycf5v8mH+D/Jh7g/yYf4P8mH9//Jh/g/yUe + 4P8mH+D/Jh3g/yYk4v8cOOv/OV7y/4yd9/+Pofj/LkHq/yIa3/8mHt//Jh/g/yYe4P8mH9//Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4f8mH+H/GheZ/w8TTv8AAQD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8EBAT/TU1O/2BgYP8QEBD/AAAA/wAAAP8MDAz/w8PE//Dw8P/w7+r/0NDn/yNA + 6P8hRvH/I0fvySNG7wQjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AJiHAACYhwAAmIcAAJiHAACYg + wAAmIMAAJiDAACUgwAAmIcAANDPHAB8XvAAeF7wAHRa7ABwWugAeF7wAIRq9AB8YvQAgGb0AJh/AACYf + vgAjSPAAI0jwBSNH764jR+//I0fv/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0fw/yNG8P8jR/D/I0fw/yNG + 7/8jRu//I0fv/yNG7/8jR/D/I0fv/yNG8P8jRvD/I0fv/yNH8P8jR/D/I0fw/yNG7/8jR/D/I0fv/yNH + 7/8jRvD/I0bv/yNH8P8jSfH/I0Xu/yQ36f8mJeH/JRrh/yIb4/9DRaH/hYiD/7i4w/+5usv/trfG/7a4 + x/+2uMf/trfG/7q7y/+fn6r/eXl8/62uvP+zs7j/XWGP/yQe3/8lHeH/Jh7g/yYe4P8mHt//Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH9//JR7f/yYe3/8mHt//Jh/f/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe3/8mH+D/Jh/g/x0V4v90d9P/zc/D/8zM + w/+Vmc3/Hxnh/yUe4P8mH+D/Jh7f/yYe4P8jG9//IyLg/6Oqyv/NzML/xMfE/0VI2/8fFuH/Jh/g/yYe + 4P8gF+H/TlTZ/8XIxP/HxsP/wMPE/1Ja2v/AzPn/Z27q/xUM3v8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8XDd3/U1bm/7S+9f8iIN//JRzg/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mHuD/Jh/g/yYe + 3/8lH9//Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mH9//Jh7g/yYe4P8mHuD/Jh7g/yYf + 4P8lHt//Jh/g/yYe3/8mHt//Jh7g/ycf5f8fG7P/FBJy/yAbvP8nIOb/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHt//Jh7f/yYc3/8eM+j/Ol/z/42c9/+dpvf/jJ33/zI55v8iGt//Jh/g/yYf4P8mH+D/Jh/f/yYf + 4P8kG9//Ixrf/yYf4P8mH+D/Jh/g/yYf4P8mH+D/JyDm/xsXof8PEkz/AAEA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wcHB/9FRUX/sLCw//T09P/5+fn/wsLC/zMzNf8AAAD/AAAA/3d3eP/19PX/8fDs/9fb + 6v8sUOn/IETx/yNH780jR+8FI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACYhwAAmIcAAJiHAACYh + wAAmIMAAJiDAACYgwAAlIMAAJiHAADQzxwAfF7wAHhe8AB0WuwAcFroAHhe8ACEavQAfGL0AIBm9ACYf + wAAmH74AI0v0ACNL9EkjRu//I0bv/yNH7/8jR/D/I0fw/yNG7/8jR+//I0fv/yNH7/8jR/D/I0fw/yNH + 7/8jR+//I0fv/yNH8P8jR+//I0bw/yNG8P8jRu//I0fv/yNH7/8jR+//I0fw/yNH8P8jR/D/I0fv/yNH + 7/8jR+//I0jw/yNJ8P8jRe7/JTbn/yYl4f8mHN7/JRvh/yIc5P9FSJ7/jY+H/7m5xv+4ucr/trfG/7a4 + x/+2t8f/trfH/7a3x/+2t8b/uLnI/7m6yf+8vc3/pael/0pIov8hGOf/Jh7g/yYf4P8lH9//JR/g/yYe + 3/8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/f/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/JR7f/yYf4P8fF+H/VFzY/8rL + w//LysP/rbPI/yos4P8jGuD/Jh/f/yUf3/8lHt//Jh/f/xsU4f9eZNf/yszD/8/Owv+Mk83/IBvh/yUd + 4P8mHuD/Ixvg/zEw3v+3ucb/x8bD/83Mwv9hadL/o6v2/+Xp+/89P+P/Fgze/x8X3/8iGd//IRjf/xwU + 3v8WD93/Mzbi/8rV+P+GjO//GxLe/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/JR7g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh7f/yYf4P8lH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/JR/f/yYe4P8mH+D/Jh/g/yYe4P8nH+X/IBu3/xMSb/8eGrT/JyDo/yYe4P8mH+D/Jh/g/yYe + 3/8mHuD/Jh/f/yYe4P8iGd7/N0vr/4yd9/+apPf/l6L3/4ia9/8vNOT/Ixrf/yYf4P8mH+D/Jh7f/yYf + 4P8kG9//ODvk/zo+5P8iGt//Jh/g/yYe4P8mH+D/Jh/g/ycg5v8dGan/DxBX/wAAAf8AAAD/AAAA/wAA + AP8AAAD/AAAA/ycnKP+5ubr/9fX1//b29v/r6+v/6urq//f29//j4uP/Xl5e/wAAAP+2trj//v3+//b1 + 8v/Jztn/L1Lj/x9D8v8jR+/MI0fvBSNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAmIcAAJiHAACYh + wAAmIcAAJiDAACYgwAAmIMAAJSDAACYhwAA0M8cAHxe8Bh4XvBsdFrsuHBa6OB4XvEshGr1VHxi9TSAZ + vTcmH8AmJh++DiomvwAhRvGUI0nz/yNI8f8jSPH/I0jx/yNH8P8jR/D/I0fw/yNH8P8jR/D/I0fv/yNG + 7/8jR/D/I0fw/yNH7/8jR+//I0fw/yNG7/8jR+//I0fv/yNG7/8jR+//I0fw/yNH7/8jR+//I0fw/yNH + 8P8jSfH/I0nw/yRD7v8kNOf/JiTh/yYc3/8mHd//JBzh/yEc5f9ER57/jZCJ/7q7yP+4ucn/trfG/7a3 + x/+2t8b/trfG/7a3xv+2t8b/trfG/7a3xv+2t8b/urvM/6eop/9MS6D/IRnm/yYe4P8mH+D/Jh/g/yYf + 4P8mH9//Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 3/8lHt//JR/g/yYf4P8mHuD/Jh/g/yYe4P8mHt//Jh7g/yYf4P8mH+D/Jh/g/yYe3/8mHuD/IBjh/z89 + 3P/ExcT/yMfD/7/ExP9CQNv/Hhbh/yYf3/8lH9//JR/f/yYf4P8jG+D/JiXg/6qvyf/Ny8L/w8fD/0RI + 2/8gFuH/Jh7g/yQd4P8kIOD/mKDM/8zLwv/MzMP/gonO/05W6P/8////5en8/3V77f83NuP/Li/h/zM0 + 4v9ISOX/g47u/+bt/P/H0vf/Jibf/yMa3/8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yUe3/8lHt//JR/g/yUe3/8mH+D/Jh7g/yYf + 4P8mH+D/Jh7g/yYf4P8mH9//Jh/g/yYf4P8mHuD/Jx/l/yIcv/8UE3L/Hhmq/ycf5f8mH+D/Jh/g/yYf + 4P8mH9//Jh7g/yYf4P8mH+D/Hxfe/1Ve6v+fq/j/lKD3/5mj9/+AlPb/KjDk/yQc3/8mH+D/Jh/g/yYe + 4P8mH+D/IBfe/1dd6f94iPH/Ih7f/yUe3/8mH+D/Jh/g/yYe4P8nH+X/IRy4/w8OXf8AAAD/AAAA/wAA + AP8AAAD/AAAA/xAQEP/CwsP//fz8/+vr6//p6en/6urq/+np6f/p6en/8fHx//T09P9oaGn/R0dI/8jI + yP9/fnv/foOO/ztd7v8fQ/H/I0fvzCNH7wUjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AJiHAACYh + wAAmIcAAJiHAACYgwAomIMAmJiDAPiUgwGEmIcCONDPHrUhO0cJVX9jcYWrd9WNs3vxjbd7/XGTb/1Nc + 1/9ARc37JiDA6yUfwM0qJr+1JjHN7h8u1P8eMdr/HTfh/x075f8ePur/IEPv/yFF8f8iR/H/I0ny/yNJ + 8v8jSPH/I0fw/yNH8P8jR+//I0fw/yNH7/8jRu//I0fw/yNH7/8jRu//I0bv/yNG7/8jR/D/I0jw/yNJ + 8f8jSfD/JEHs/yUy5v8mIuH/Jhzf/yYd3/8mH+D/JB3h/yEc5f9ESJ7/jY+J/7m6yP+4ucn/trfG/7a3 + xv+2t8f/trjH/7a3x/+2t8b/trfG/7a3xv+2t8b/trfG/7i5yf+ys7j/YWaN/yUg3f8lHeD/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh7f/yYf4P8mH9//Jh7g/yYe4P8lHt//Jh/g/yYe + 4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yIa + 4P8uLd7/qrPI/8vJw//OzcL/XmPW/xsV4f8mH9//Jh7g/yYf4P8mH+D/Jh/g/xsU4v9mbdX/zM3C/8/O + wv+GjM7/HRjh/yYd4P8mHuD/HRbh/3p70f/OzsL/ycnD/6uuyf8cHN7/fITt//7/////////7PP9/9bg + +v/i6vz///////////+8xvX/MDPh/x4V3v8mH+D/Jh7g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYe4P8mHuD/JR7f/yYf4P8mHuD/Jh/g/yYe + 4P8mHt//Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/ycf5P8iHMX/FBN1/x0Zn/8nH+P/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/f/yQc3/8lJOH/a4Hz/5ql+P+Yo/f/gpb2/yow5P8jG9//Jh/g/yYf + 4P8mH9//Jh7g/yAX3/9RVuj/l6f3/zs95P8hGN//Jh/g/yYe4P8mH+D/Jx/l/yMczP8SEmX/AwQF/wAA + AP8AAAD/AAAA/wAAAP96env/+vr6/+rq6v/q6en/6erq/+np6f/q6ur/6erq/+np6f/v7+//6Ojo/yEh + If8EBAT/AAAA/4uNnP8yUOj/IEXz/yNH780jR+8FI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACYh + wAAmIcAAJiHAbSYhwK0mIcDJJiDA6yYgwP0iHL7/NTPH/5Gf9f+dqfv/nqv8/6Ct/f+ir/3/oq/+/6Ct + /f+irv7/mab4/zY0x/8fGL3/d4Ho/4OO7P90euP/Ymja/1BY1P9FStH/O0DQ/zE70P8oN9P/ITLW/x4z + 3P8eOeT/HTvn/x0/6/8fRPD/IUbx/yNJ8v8jSPL/JEjx/yNH8P8kR+//JEfv/yNH7/8jSfD/I0nx/yNG + 7/8lO+v/JSvk/yYg4P8lHN7/JR7f/yYf4P8mHuD/JR3h/yEc5P9ER5//jY+J/7m6yP+4ucn/trfG/7a3 + xv+2t8f/trfH/7a3xv+2t8b/trfH/7a3x/+2t8b/trfG/7a3x/+2t8f/urvJ/4GDiP8sK8v/JBvi/yYe + 4P8mHuD/Jh/g/yYf4P8mH+D/JR7f/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYe3/8mH+D/Jh/g/yYf + 4P8mH+D/JR/g/yYe4P8mH+D/Jh/g/yUf4P8mHuD/Jh/g/yYe4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8kHOD/Ih/g/5edzP/OzML/zMzC/36G0f8dFuH/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8jGuD/Kirg/62y + yP/MysL/vsLF/zw+3P8hGOH/Jh/g/x8W4f9SWNj/x8nD/8jHw/+8wcb/Oz7c/xYO3/9aYej/vML2/+Xt + /P/5/v7/5/D8/8PJ9/9wd+r/IyDg/x4W3/8nH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/JR7f/yUf + 4P8mH+D/Jh7f/yYe3/8mH9//Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mHuD/JR/g/yYf4P8mH9//Jh/g/yYe + 4P8mHt//Jh7f/yUe3/8mH+D/Jh7g/yYe3/8mH+D/Jh/g/yYf4P8mH+P/JB3P/xQSeP8aFpL/Jh/i/yYf + 4f8lH9//JR7g/yYe4P8mHuD/Jh/g/yUe3/8mH+D/Ihjf/yg+6v+Fl/f/mqT3/5Oi9/82POX/IRje/yYf + 4P8mH+D/Jh/g/yYe4P8gF97/UVXo/5+t+f9gauz/Hxfe/yYe3/8mH+D/JR/f/yYe4/8lHtr/Fxd3/wUI + C/8AAAD/AAAA/wAAAP8jIyP/4eHh//Dw8P/p6en/6urp/+rq6v/q6ur/6urq/+rq6v/q6er/7u7u/9vb + 2/8ZGRr/AAAA/xQUD/+srL3/JkLe/yFH9P8jRu/HI0fvAyNH7wAjR+8AI0fvACNH7wAjR+8AJR7gACQd + 4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAmIMAAJiDAACYgwMcmIMD/JiHA/yYgwP8mIMD/Ixy+/y4sxf+MmfP/laD3/4CN7v92gen/cHfn/25z + 5f9weef/fYnr/4mY8P85N8j/HBW5/3eB5/+ms/7/oa79/6Gv/f+hrv3/oq78/5un+P+Pm/L/hZDt/3yD + 6P9tcuD/W2ba/0tY1/8/R9X/MT7U/yQ31v8eM9z/Hjnj/x4/6v8iR/H/JEny/yNJ8P8jR+//JD7s/yUx + 5v8lJeH/Jh3f/yYc3/8lHt//Jh/f/yYf3/8mH9//Jh7h/yIb5/9ERqL/jY+I/7q6x/+4ucn/trfG/7a4 + xv+2t8b/trfG/7a4x/+3uMb/trfH/7a3x/+2t8f/trfG/7a3x/+2t8f/trjG/7u8zP+XmZf/Oj2v/x8W + 6/8mHuH/Jh/g/yYf4P8mH+H/JR7j/yMe5v8jHeb/JB7l/yQe4/8mHuH/Jh7g/yYe4P8mH9//Jh7f/yYe + 4P8mHuD/Jh7g/yUf3/8mHuD/Jh/g/yYf3/8mH+D/Jh7g/yYe4P8mH+D/JR/f/yYe4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh7g/xwU4v9+gNH/zc7C/8zLwv+fo8r/IR7h/yUd4P8mHt//Jh7f/yYf4P8mH9//Jh/f/xoT + 4v9tdNP/zc3C/8/Pwv+CidD/Hhji/yUe4P8iGuD/Nzfc/77Axf/HxsP/ysvD/1pb1/8bEuL/HBLe/x4b + 3/81OOL/R0jl/zc64/8fHd//GRHe/yMb4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe3/8mHt//Jh7g/yUe + 3/8lHt//Jh/g/yYe3/8mH+D/Jh7g/yYe3/8mH9//JR/f/yYe4P8mH+D/Jh/g/yUe4P8mH9//Jh/g/yUf + 3/8mH+D/Jh/g/yYf3/8lHt//Jh7g/yYf4P8mH+D/Jh/g/yUe4P8mH+D/Jh/i/yQe2P8VFH//FxSF/yUf + 3f8mH+H/Jh/g/yYe4P8mH+D/Jh/g/yUf3/8mH+D/Jh/g/yYc3/8eJeT/bYX1/5uk9/+bpvj/aXTu/yAZ + 3v8lHeD/Jh7f/yYe4P8mHuD/IBjf/05T6P+cqfj/hJH0/ygk4P8kHN//Jh7g/yYe4P8mH+H/Jx/l/xsZ + l/8FBxb/AAAA/wAAAP8AAAD/iomK//n5+f/q6en/6urq/+rp6v/q6ur/6urq/+np6f/p6en/6urp//b2 + 9v+cnZ7/AAAA/wAAAP8vLin/rrPQ/x451v8iR/P/I0jvqyNH7wAjR+8AI0fvACUd4AAhGd8AHhXeACUe + 4AAkHeAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJiDAACYgwAAmIMDGJiHA/yYfwP8fGL3/Gxa9/x0Xvf8bFrz/OUHL/0NLzv86Qcn/Oz/H/0E+ + yf9PSc3/T03N/y4yyP8qKcn/JyLG/yMcxP8yMsz/UVnY/11m2/9mb93/cXjj/3d+5/97hen/go/t/4uZ + 8v+bqPr/oa79/6Gu/f+gq/v/mqT3/4uW7/+Aiuv/bnXh/1Rd1/88RNH/JTDQ/yQ64v8lNOj/Jibj/yYe + 4P8mHN//Jh3f/yUe3/8mHuD/JR7f/yYe4P8mH+D/Jh/g/yIb5v88Pqz/h4qF/7m6x/+4usn/trfG/7a3 + xv+2t8b/trfH/7a3x/+2t8b/t7jH/7e3x/+2t8b/trfG/7a3xv+2t8f/trfH/7a3x/+5usr/ra6z/11g + hv8pKcr/Ihrl/yId6v8iHuv/Ix7o/ykg2f8vIsv/MCLJ/y8iy/8qIdn/JB7m/yMe6v8iH+v/Ih7q/yMe + 5f8lHuH/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHt//Jh7f/yUe + 4P8mH+D/Jh/g/yYe4P8eFuH/YGnW/8vMw//JyMP/s7rH/zM03v8iGOD/Jh/g/yYe4P8mH+D/Jh7f/yYf + 4P8iGeD/MDHe/7K4x//LysP/vsLF/zw+3P8gGOD/JB3g/ygj4P+fp8v/y8nC/83Mwv92gdL/HRfi/yYe + 4P8lHd//IBff/x0U3v8fF97/JR3g/yYf4P8mHt//Jh7f/yYf4P8mHuD/JR7f/yUe3/8mHuD/JR7g/yYe + 4P8lH9//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8lHt//JR7f/yUe3/8lH+D/JR/f/yYf4P8mH+D/Jh7f/yUf + 3/8mHuD/Jh7f/yUe3/8mH9//Jh/f/yYe4P8mH+D/Jh7g/yYf3/8mHuD/Jh/g/yYe4f8mHtz/GBaK/xYU + fP8kHtH/Jh7j/yYf4P8mH+D/Jh/g/yYf4P8lHt//Jh/f/yYf4P8lHN//ICDh/2+C8/+bpfj/laD3/56r + +f9XX+r/IRrf/yUe3/8mH+D/Jh7g/yEY3/9DSeX/laP3/5il+P9ESOb/Hxfe/yYf3/8mHuD/JR7f/ycf + 5v8hHL//DA00/wAAAP8AAAD/Njc4/+jn6P/t7e3/6enp/+np6f/p6en/6urq/+rp6f/q6un/6unq/+vr + 6//z8/T/T09P/wAAAP8AAAD/V1ZQ/5Weyv8YNtb/I0n0/yQ/7KEgEt0AIhjfAB4W3gAlHeAAIRnfAB4V + 3gAlHuAAJB3gACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJRzgACYhwAAmIcAAJiHAiCUgwPYkHr3/REPK/2512v+HjOL/oqLr/6qs7v+vtO//ub3z/8PG + 9v/Nzvr/2dr+/9/f/v90euz/GhHc/yUe3/8mH9//Ixve/yAY2f8gGNf/IBjT/yAYzv8gGMv/Ih/I/ycm + x/8uLcf/OTnK/05V0/9vd+L/gpDs/5Kf9f+dqvv/n6z8/6Kv/v+irf3/nKj4/0pPz/8gGLv/Jh3S/yYd + 4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yIa5v8yNb7/fH9//7e3wv+5usr/trfH/7a3 + x/+2t8f/trfG/7a3x/+2t8f/trfG/7a3x/+2t8f/trfH/7a3x/+2t8f/trfH/7a3xv+2t8b/trfH/7q7 + zP+lpqv/f4KM/1ZanP85LbD/SCSC/1UnWP9dKT//Yik1/2IpM/9iKTX/Xig//1cmTv9QJ23/RCWP/zUh + tf8uIsz/JB/k/yIe6v8iHuz/Ih7p/yQe4v8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/f/yYe + 4P8mHuD/Jh/g/yYf4P8mH9//IBjh/0VG2v/JycP/xsXD/8jKxP9MTNn/HhXh/yYf4P8lH9//Jh/g/yYe + 4P8mH+D/Jh/g/xsU4f92fNL/zc7C/87Nwv9/htD/HRjh/yUe4P8dGOL/f4LR/87Owv/LysP/m5/M/yAZ + 4f8lHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yUe + 4P8mHt//Jh/g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/JR7g/yUe3/8mH+D/Jh/g/yUe3/8mH+D/Jh/g/yYe + 4P8mHt//Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/f/yYe3/8mH+H/Jx/i/xsX + mv8TEnP/IhzC/ycf5v8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7f/x8c4P9acPD/n6r4/5ql + +P+KmPX/WWHr/yIc4P8lHuD/Jh/g/yYf4P8hGN//REnm/5Wi9/+cp/j/anTu/yAY3v8mH+D/Jh/g/yYe + 3/8mH+P/JR7Y/xYXcP8BAwf/AAAA/5+foP/5+fn/6enp/+np6f/q6er/6unq/+nq6f/q6ur/6unq/+np + 6f/19PT/wsLD/wkJCf8AAAD/AAAA/42Nhf+AiMX/FDTe/yNK8/8kLubyIBLdfCIY3wkeFt4AJR3gACEZ + 3wAeFd4AJR7gACQd4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJRzgACUc4AAmIcAAJiHAACYhwAQgGc48Sk7hj8vP+cbc2//12Nj+/9jY/v/X1/7/1dX9/9XU + /f/T0/z/0dH8/8/P+//b2/3/lZby/xwU3/8mHuH/Jh/g/yYf4P8mHuH/Jh/h/yYe4f8mH+L/Jh/i/yUd + 4f8kHN//Ixvc/yEZ2P8gGNP/IBnN/ykozP83Ncv/RUvO/1Zf1v9sdOH/fIfq/5Wj9f9ncd7/Hxm6/yYf + z/8mH+H/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yQd4v8nJNj/ZmmE/6+vtf+6u8v/trfH/7a3 + x/+2t8b/trfG/7a3xv+2t8b/trfH/7a4xv+2uMb/t7fG/7a3x/+2t8f/trfG/7a3xv+4ucj/u7zM/7q8 + y/+0t8b/r7PB/6Kmrf97fXb/aT0w/2glFv9pKRr/aCgc/2coH/9nKB//Zygf/2gpHf9pKRr/aSka/2ko + Gf9oKSL/YSk0/1gmTP9OJ3P/QCSX/zUiuv8pINz/Ih7r/yIe7P8kHuT/JR7f/yYe3/8mH+D/Jh7g/yYe + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yIZ4f80Md3/tbvH/8nHxP/NzcL/aHHV/x0V4f8mH9//Jh/f/yYe + 4P8mH+D/Jh/g/yYf4P8hGOH/Nzfe/7q+xv/LysP/uL3G/zU33f8iGeH/HxXh/1xh1//Jy8P/ycjD/7S4 + x/8vMN7/Ixrg/yYe4P8mH+D/Jh/f/yYe4P8mHuD/Jh7f/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf + 4P8mHt//Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yUe + 4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/ycg + 5v8eGav/ExFv/x4Zr/8nH+b/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/JR/f/yYe3/8kHt//KETt/01u + 8/9IaPP/Kjfm/yEY3/8mHuD/Jh/g/yYe3/8mHuD/IRjf/0FG5v+Uoff/mKP4/4qX9f8sK+H/JBvg/yYf + 4P8lHuD/Jh/g/yYg5f8cGKX/GR1L/wAAAP9sbG3/9/f3/+nq6v/p6en/6unq/+rp6v/q6er/6enp/+np + 6f/v7+//8fHx/0VFRv8AAAD/AAAA/wMDAv+pqqn/UFqz/xk96v8iR/D/KC3k/0JA5f8vL+LCHhbeUSUd + 4AchGd8AHhXeACUe4AAkHeAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJRzgACUc4AAlHOAAJiHAACYhwAAmIcAAIBnOAEpO4QDY2v4J0ND7Ls/P+1/Pz/ucz8/7yM/P + +/HPz/v/0dD7/9XU/P/S0vz/2tn9/6is9f8hH9//JBzf/yYf3/8lHt//Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/h/yYf4f8mH+H/Jh/i/yYe4f8kG+D/Ihrb/yAZ0/8gGM//HxjL/yUizf81MtX/Mi7W/yQe + 1f8mH9//Jh/h/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8iG+b/Rkmg/5ydmv+7vM3/trfG/7a3 + xv+2t8b/trfH/7a3x/+2t8b/trfH/7a3x/+3uMf/trjH/7a3xv+4ucn/uLnJ/7e4x/+7vMz/tLfH/5uf + qf+BgIX/dWpr/21aWf9nSUX/ZjYy/2UoI/9mKCT/ZSgk/2YpJP9lKCT/ZSgk/2YpJP9mKST/Zigk/2Uo + JP9lKCT/Zigi/2cpHv9oKRv/aSoa/2kpGP9nKSX/XShB/08mcP86I6j/KiDY/yIf7P8iHun/JR7h/yYf + 4P8mH9//Jh/g/yYf4P8mH9//Jh/g/yYf4P8kHOD/JSTh/56ny//Ny8L/zMzC/4uPzv8dFeH/JR7g/yYf + 3/8mH+D/Jh/g/yYf4P8mH+D/Jh7g/xwV4v98gdL/zM3C/87Owv92ftL/HBbh/yIZ4P87Ptz/wMLF/8jH + w//Ex8X/S0vZ/x0V4f8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYe + 4P8lH9//Jh/f/yUf4P8mH9//Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/f/yYe3/8mHt//Jh/g/yYe + 4P8nH+X/IRy+/xQSc/8bGJr/JyDj/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/f/yYe3/8mHt//Jh3f/yI9 + 7f8dRPD/HUPw/yIt5f8mHd//Jh/g/yYf4P8mHt//Jh/g/yMa3/83POP/kp/2/5ai9/+Zpvj/TlPo/x8X + 3v8mH+D/Jh/g/yYf3/8nH+X/HhjF/yYphP9SVl7/hISC//Tz8//q6ur/6unp/+rp6v/q6ur/6enp/+3t + 7f/5+fn/8PDw/29vcP8AAAD/AAAA/wAAAP8iIh//ubvI/yAuqP8hR/P/Hz/u/z1E5v+bqPj/i5n1/1Ja + 6f8hG97GIRnfVx4V3gAlHuAAJB3gACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJRzgACUc4AAlHOAAJRzgACYhwAAmIcAAJiHAACAZzgBKTuEA2Nr+ANDQ+wDPz/sAz8/7AM/P + +wvQ0Psoy8z7Vrq/+JOxt/bHwsX599va/f+ip/T/IR7f/yQc3/8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4f8mH+H/Jh7h/yYe4v8lHOH/Ihrf/yMb + 4P8mH+L/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8kHOP/KijQ/3R3hf+3t8L/t7jI/7a3 + xv+2t8f/trfH/7a3x/+2t8b/trfH/7a3x/+2t8b/trfG/7a3xv+6u8v/srLA/6qruP+6vMv/mp+p/3dw + cv9lRUH/Yy4p/2MnI/9kJB//ZSQf/2UlIf9lKCT/ZSgk/2UoJP9lKCT/ZSgk/2YoJP9lKCT/ZSgk/2Yo + JP9lKCT/ZSgk/2UpJP9lKCT/Zikk/2YoJP9mKST/Zygh/2goHf9pKBn/aSkc/2AoPP9NJ3L/NyO1/yUf + 4/8iHuz/JB7k/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/x0X4v+KjM7/zs/C/8vKw/+orsj/Jyjg/yQb + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe3/8gF+H/PD7c/77Cxv/LysP/trzH/zQ13v8gFuH/KSbg/6Or + yv/LycL/zs3C/2Zr1f8bFeL/Jh/g/yYe3/8mH+D/Jh/g/yUf3/8mHuD/Jh/g/yYe4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/Jh7g/yYe3/8lHt//JR/g/yYe4P8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYe4P8mHt//Jh7g/yYf + 4P8mHuD/Jh/i/yQd0v8VE3v/FxWH/yYf3v8mH+H/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHt//Jh7g/yYd + 3/8kO+v/I0nw/yNJ8P8lMOX/Jhzf/yYe4P8mH+D/Jh/g/yYf4P8jGt//Nzzj/5Gf9v+Voff/mqX4/3SA + 8f8hHN7/Jh3g/yYf4P8mH+D/Jh/h/yYf4v8ODI//fISy//v79f/q6ur/6enp/+vr6//u7u7/9PT0//n5 + +f/n5+j/pKSl/z0+Pv8AAAD/AAAA/wAAAP8AAAD/enp1/36CtP8RJbf/JEz4/x856/9AROX/kqH3/6Gt + +f+eq/n/cHzv/y8w4v8eFd6jJR7gDyQd4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAmIcAAJiHAACYhwAAgGc4ASk7hANja/gDQ0PsAz8/7AM/P + +wDPz/sA0ND7AMvM+wC6v/gAdYrsCoWM7yxbaeleRlDmmSEX38gmHuD3Jhze/yYc3/8mHN//Jhzf/yYc + 3/8mHd//Jh/g/yYe4P8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/IRrm/0JFpv+cnZv/u7vL/7a3 + xv+2t8b/trfH/7a3xv+2t8b/trfG/7a3x/+2t8b/trfG/7a3xv+7vMz/qKm1/319gP+bnaj/h4uQ/2VM + Sf9jKiX/ZSQf/2UmIf9lJyP/Zigk/2UoJP9lKCT/ZSgk/2YoJP9mKST/ZSgk/2UoJP9lKCT/Zikk/2Yo + JP9mKCT/ZSgk/2YoJP9lKST/Zikk/2UpJP9lKCT/Zigk/2YoJP9mKST/Zikk/2YpI/9nKR7/aSkZ/2cp + I/9ZJ1D/QSWW/ysg1P8iH+z/Ix7n/yYe4P8mH9//Jh7g/yYf4P8eFeH/anLV/8zNwv/Hx8P/u8HG/zw7 + 3P8gF+H/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH9//JR7g/x0Y4f+FjND/zMzC/83Nwv91fdL/HBXi/x4Z + 4f+HjM//zs7C/8zLw/+Fjc//Hhbh/yYe4P8mH+D/Jh/g/yYf3/8mH+D/Jh7g/yYe4P8mHuD/Jh7g/yYf + 4P8mHuD/Jh/g/yUe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 3/8mH+D/Jh7g/yUf4P8mH+D/Jh7f/yUe3/8mHuD/JR7f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mHt//Jh7f/yYf4f8mH97/GRaN/xQSd/8jHcn/Jx/k/yYe3/8mH+D/Jh/g/yYf4P8mH+D/JR/g/yUf + 3/8mHd//JTjo/yNJ8P8jSfD/JTLm/yYc3v8mH+D/Jh/g/yYe4P8mH+D/Ixrf/zc85P+Rn/b/lqH3/5ah + 9/+ToPb/OTvk/yEY3v8mH+D/Jh/g/yYe3/8nH+X/HRbB/xsdfP/Hy9n/9vXv/+np6f/k5OT/3t3e/8LC + w/+FhYX/LS0u/wAAAP8AAAD/AAAA/wAAAP8AAAD/MjEu/8HF0f8lKIr/HTrb/yNL9f8kNej/JBrf/zU2 + 4/9ia+z/jJr1/6Kv+v+Rn/b/T1bp/yIb37skHeAYJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJiHAACYhwAAmIcAAIBnOAEpO4QDY2v4A0ND7AM/P + +wDPz/sAz8/7ANDQ+wDLzPsAur/4AHWK7ACFjO8AW2npAEZQ5gAjGd4IJDzrjyQ66f8lMOb/JSvk/yUt + 5v8kMef/JDPm/yYn4/8mHuD/Jh7f/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 3/8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH9//JR3g/yQd3/9hZYz/srK5/7i5 + yf+2t8b/trfG/7a3xv+2t8b/trfG/7a3xv+2t8b/trfG/7a3xv+7vMz/p6ey/3Rzdf+OkJj/fn2B/2M7 + N/9lJB//ZiYi/2YoJP9lKCT/Zigk/2YoJP9mKCT/Zigk/2YoJP9mKCT/Zikk/2YoJP9mKST/Zigk/2Yp + JP9mKCT/ZSgk/2UoJP9mKCT/Zigk/2YpJP9lKCT/ZSgk/2YoJP9mKCT/Zigk/2YpJP9lKCT/ZSkk/2Yp + JP9mKCL/aCkc/2kpG/9gKTr/SiZ9/zEhxv8jH+r/Ix7p/yYe4P8mH+D/Hhfh/09S2f/Ly8P/xcXE/8zN + wv9TV9f/HRXh/yYf3/8mHuD/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8fFuH/Q0Xb/8HFxf/KycP/tbrH/zEy + 3/8aD+H/YmjW/8rMw//KysP/qavJ/yUh4P8lHOD/Jh/g/yYf3/8mH9//Jh/g/yYf3/8mHuD/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/JR7g/yUe4P8mHuD/Jh7g/yYf4P8mHuD/Jh7g/yYe + 4P8mH9//Jh/g/yYe4P8mH9//Jh7f/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYe + 4P8mH+D/Jh7f/yYe3/8mH+H/JyDl/x0Zp/8SEW7/Hxqy/ycg5v8mH+D/Jh/g/yYf4P8lHuD/Jh/g/yYf + 3/8mH+D/Jhzf/yU26P8jSfD/I0nw/yRB7f8mIOD/Jh3f/yYe4P8mH+D/Jh/g/yMa3/83POT/kZ/2/5ah + 9/+UoPf/mqb4/2dw7f8fGN7/Jh7g/yYf4P8mH+D/Jh/g/ycg5P8JApX/Q0eQ/+3v7P/39vP/nZ6f/yws + LP8JCQr/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/FBQT/8XGwv9gY6P/Dhia/yRK9f8jSPD/JSnk/yYc + 3/8iGd//Hxnf/y4t4f9OVOj/doLw/5mp+P9mce3/JB7gvCUc4A8lHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACYhwAAmIcAAJiHAACAZzgBKTuEA2Nr+ANDQ + +wDPz/sAz8/7AM/P+wDQ0PsAy8z7ALq/+AB1iuwAhYzvAFtp6QBGUOYAIxneACNJ8K8jSfD/I0nw/yNJ + 8P8jSvD/JD3q/yYo4/8mH+D/Jh7g/yYe3/8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYe4P8mH+D/Jh7f/yYf + 4P8mH+D/Jh7g/yYe3/8mH9//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yUc4/8qKc7/fH+M/7m6 + xv+3uMb/trfG/7a3x/+2t8b/trfG/7a3xv+2t8b/trfH/7a3xv+6u8v/rK26/3Z2eP+RlJ3/fHp8/2M0 + MP9lJCD/Zigk/2YoJf9lKCT/ZSgk/2YoJP9mKST/Zikk/2YpJP9mKST/ZSgk/2UoJP9mKST/Zikk/2Uo + JP9lKCT/ZSgk/2UoJP9mKCT/Zigk/2UoJP9mKCT/Zigk/2UoJP9mKCT/Zikk/2YoJP9lKCT/Zigk/2Yo + JP9lKCT/Zigk/2UpJP9lKCP/Zygf/2kpGv9kKS3/TiZv/zMivf8jHuj/Ix7p/yAY4f87N9z/vsLG/8fG + w//NzcL/dX3S/xwU4v8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh3g/x4b4f+NlM3/zczC/8zN + wv9tcdX/Fw7i/0FC2//FxsP/ycjD/7q/xv87Pd3/IRfh/yYf4P8mH+D/Jh/g/yUf3/8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8lHt//Jh/g/yYf4P8mH+D/Jh7g/yYe + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh7g/yYe + 4P8mH9//Jh/g/yYf4P8mH+D/Jh/g/ycf5P8iHMT/FBJz/xoWlf8nIOT/Jh7g/yYf4P8mH+D/Jh/g/yYe + 3/8mHt//Jh/g/yYc3v8lMeb/I0nw/yNH7/8jSfD/JTTn/yYc3v8mHt//Jh/g/yYe4P8jGt//Nzzk/5Gf + 9v+Woff/lKD3/5ai+P+Rnfb/Njbj/yEY3v8mH+D/Jh/g/yYe3/8kG+P/QUXc/xQYg/9dYqL/8vPw//z6 + 9/+dnZ7/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/EhIR/7q7tf+NksD/Cwt4/yE82f8kSvT/JEHs/yYg + 4P8mHuD/Jh7g/yYe4P8jG9//IBff/yAa3v9AQ+X/k6T3/z9A5f8hGN9HIRjfACEY3wAhGN8AIRjfACEY + 3wAhGN8AIRjfACEY3wAhGN8AIRjfACEY3wAhGN8AIRjfACEY3wAmIcAAJiHAACYhwAAgGc4ASk7hANja + /gDQ0PsAz8/7AM/P+wDPz/sA0ND7AMvM+wC6v/gAdYrsAIWM7wAiSvAAIkrwACJK8AYjR/DQI0fv/yNJ + 8P8kQu3/JSzk/yYd4P8mHd//Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe + 4P8mH+D/Jh/g/yYf3/8mH9//Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8jGub/Nja4/5GU + mP+7vMv/trfG/7a3x/+2t8b/trfG/7a3x/+2t8f/trfG/7a3x/+5usr/r7G+/3R1d/+QkJn/hYWL/2M4 + NP9lIx//Zigk/2YoJP9lKCT/ZSgk/2YpJP9mKCT/Zikk/2YpJP9mKCT/Zigk/2YoJP9lKCT/Zigk/2Uo + JP9mKCT/Zigk/2UoJP9mKCT/ZSgk/2UoJP9mKST/Zigk/2YpJP9mKCT/Zigk/2YpJP9lKCT/ZSgk/2Uo + JP9mKCT/ZSgk/2YoJP9mKCT/ZSgk/2YoJP9mKCT/Zygg/2koGv9mKSf/Uidk/zQiuv8hG+r/Jino/6Wt + yv/LysL/zMzD/5mdzP8fG+H/JR3i/yYf4v8mH+H/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8fFuH/Rkna/8TG + xP/KycP/sbXI/ysp3/8pJd//q7LI/8rJw//Jy8P/V1jY/xwV4v8mH+D/Jh7f/yUf3/8mH+D/Jh7g/yYe + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh7f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHt//Jh/g/yYe3/8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/f/yYf4P8mH+D/Jh/g/yYf4P8mH+H/JR/Z/xcVhP8WE37/JR7V/yYf4/8mH9//Jh/g/yYe + 4P8mH+D/Jh/g/yYf4P8mHN//JSvk/yNJ8P8jR+//I0jw/yNH7/8mKeP/Jhzf/yYf4P8mH+D/Ixrf/zc8 + 5P+Rn/b/laH3/5Sg9/+UoPf/mqb4/2977/8hG9//JR3g/yYf4P8mHuD/GhLe/7K3+v/M0uf/Ki+L/15k + qP/o6/P/5+bg/xoZF/8AAAD/AAAA/wAAAP8AAAD/Hx4b/7m6tf+Wmsb/Dgxz/xwtuv8kSvX/I0nw/yU0 + 6P8mHd//Jh7g/yYf4P8mHuD/Jh/g/yYf4P8lHuD/Hhbe/0pO6P85OeX7IxvfNSMb3wAjG98AIxvfACMb + 3wAjG98AIxvfACMb3wAjG98AIxvfACMb3wAjG98AIxvfACMb3wAjG98AJiHAACYhwAAmIcAAIBnOAEpO + 4QDY2v4A0ND7AM/P+wDPz/sAz8/7ANDQ+wDLzPsAur/4AHWK7AAmHN8AJhzfACYc3wAlMOctJEDt7CNJ + 8P8lOur/JiLh/yYc3/8mHuD/JR/g/yYe4P8mHt//Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/JR/f/yYe4P8mHuD/Jh/g/yYe3/8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/IBjn/0A8 + rf+dn57/urvL/7a3x/+2t8f/trfH/7a3xv+2t8b/trfG/7a3xv+3uMj/trfG/3t7ff+EhIn/lpqi/2RE + QP9lIx//Zigk/2YpJP9mKCT/ZSgk/2YoJP9lKCT/Zigk/2UoJP9mKCT/Zigk/2UoJP9lKCT/Zikk/2Yp + JP9mKCT/ZSgk/2UoJP9mKCT/Zikk/2UpJP9lKCT/Zikk/2UpJf9mKST/Zigj/2YoI/9mKCL/Zigi/2Yo + Iv9mKCP/ZSgj/2YoI/9mKCP/Zikk/2UoJP9lKCT/Zikk/2YoJf9mKCT/Zygh/2kpGv9mKSX/USZk/y0d + u/+Sl9H/zc3D/8nIw/+utsj/LjDf/yEY1v8lHtX/Jh/f/ycf4v8nIOf/Jx/l/yYf4/8mHuL/JR3i/yId + 4v+Wnc3/zMzD/8vNwv9rcNT/FBDj/4yQzv/OzsH/zczC/3R+0/8cFuL/Jh7g/yYe3/8lH9//Jh7f/yYf + 4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/JR7f/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/f/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/ycg5f8dGab/ExFv/yAauf8nH+b/Jh/f/yYf + 4P8mH+D/JR/f/yYe4P8mH+D/Jh3f/yYl4v8jRe7/I0fv/yNH7/8jSPD/I0Lt/yYi4f8mHd//Jh/g/yMa + 3/83PeT/kp/2/5ah9/+UoPf/lKD3/5Wg9/+Ypfj/Ulvp/x4W3v8mHuD/Ixvf/yMl3//N0/j//////9jc + 5f8VGUX/LDJv/5Sayf8xND7/AAAA/wAAAP8DBAD/P0A7/6mrtf9vc6z/EA95/xosuP8kSfT/I0fw/yNH + 7/UmKOPVJh3f/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYe3/8hGN//JBzguCYf4AcmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYhwAAmIcAAJiHAACAZ + zgBKTuEA2Nr+ANDQ+wDPz/sAz8/7AM/P+wDQ0PsAJhzfACYc3wAmHN8AJhzfACYc3wAmHN8dJhvezSU5 + 6v8kPev/Jh7f/yYd4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yUf4P8mHuD/Jh/g/yYe4P8mHuD/Jh7g/yYf4P8lH9//JR7f/yYf4P8mH+D/Jh/g/yAa + 5v9ISqP/paal/7m6yv+2t8f/trfG/7a3x/+2t8f/trfG/7a3x/+2t8b/u7zM/5SUnP9ubW7/qKu4/3Rr + bP9jJyL/ZSYj/2YoJP9mKCT/Zigk/2UoJP9mKST/Zikk/2YoJP9mKCT/ZSgk/2UoJP9lKCT/ZSgk/2Yp + JP9mKCT/ZSgk/2UoJP9lKCT/Zikk/2YoJP9lKST/Zigj/2coHv9pJxj/aiYY/2knHf9nJyH/aCgh/2go + If9nJyH/aCcd/2gmHP9oJhz/aCYa/2olFf9qJRX/aSUX/2gmGf9pJhn/aCYa/2gnG/9oKBz/aCgb/2oo + FP9gHx3/kHeE/8zPy//Ix8P/yMnI/z4+qf8OC37/FhSA/xkWi/8dGJ7/Hxmz/yIcwv8kHc3/JR7X/yUe + 3P8eFt7/TlPX/8bJxf/KycP/r7TI/yYj4f9kaNn/y83D/8vLw/+Zncz/IRrm/yYe5v8nH+T/Jh/j/yYf + 4f8mH+H/Jh/h/yYe4P8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYf + 4P8mHuD/Jh/g/yUe3/8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8lHt//JR7f/yYf4P8mH+D/Jh7f/yYe + 3/8mHt//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8nH+T/Ix3M/xQTdv8aFpf/Jx/k/yYf + 4f8mH+D/Jh/g/yYf3/8mH+D/JR/f/yUd3/8mH+D/JD/s/yNI8P8jR+//I0bv/yNJ8P8kO+n/Jh7f/yYc + 3/8kGt7/MjDh/5Gc9v+Voff/lKD3/5Sg9/+UoPf/l6L4/5Cf9v88QOX/IRnf/x0U3v9ISOT/8/j9//// + ///m5uX/HR0Y/wAAAP8FCT3/GRp2/wsNKf8JChz/HBxO/1JWmv8uMpT/ERyd/x861f8kSfT/I0fx/yNI + 7/8jP+vEJiThECYe4FsmH+DDJh7g9SYf3/4mH+D/Jh7f/yYf4P8mHt//Jh7g4CYe3zAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmIcAAJiHAACYh + wAAgGc4ASk7hANja/gDQ0PsAz8/7AM/P+wDPz/sAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gmyYd + 3/8jQO3/IjDm/yUa3/8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf3/8lHt//Jh7g/yYe4P8mH+D/JR7f/yUe3/8mH+D/Jh7g/yYf + 4P8iHOX/TFKf/6mpq/+5usr/trfH/7a4x/+2t8f/trfH/7a3xv+2t8f/t7jI/7S1xP90dHb/kJCY/6Gm + sf9lRUH/ZSQf/2YoJP9mKST/Zigk/2YpJP9mKST/ZSgk/2YpJP9mKST/ZSgk/2YpJP9mKCT/Zigk/2Yo + JP9mKCT/Zikk/2YoJP9mKCT/ZSgk/2YpJP9mKSX/Zycf/2gnHv9fLEH/UDRu/0Q4kP9AO6L/PD2v/zw+ + sv86PLL/NTiy/zo3ov88OJ3/Pzqe/0I6lv9FOIf/RTiJ/0c1ff9MMW7/SzFw/08vY/9RK1j/UipY/1oq + Sf9dLUf/WyQz/39ZXf/Cx8z/wcPG/87QyP9VW5X/Cwhz/xUSdv8UEXP/FBBv/xMPbf8UEHP/FRB3/xUR + fP8WEoD/FxKI/xUViv+Xm7P/zczH/8zNx/9aXrX/MTal/8bIxP/LysX/sbXE/ygru/8dF73/Ix3G/yQe + 0P8lHtr/Jh/e/ycf4v8nH+b/Jx/m/ycf5P8mH+L/Jh/h/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe + 4P8lH9//Jh/g/yYf4P8mHuD/Jh7g/yYe3/8mH+D/Jh/g/yYe4P8mHuD/JR/f/yUf4P8mH+D/Jh/g/yYe + 3/8mH+D/Jh7g/yUe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mH+D/Jh/h/ycf4v8aF5b/FRN5/yUf + 0v8nH+T/Jh7g/yYf4P8mH+D/Jh/g/yUf3/8lH+D/Jhzf/yUy5/8jSfH/I0fw/yNG7/8jRu//I0jw/yQ5 + 6v8lMuf/Ijbo/yw96f+MmPb/lqL3/5Sg9/+UoPf/lKD3/5Sf9/+apvj/gY7z/yso4f8WDd7/gIft//// + ////////rq6w/wICA/8AAAD/AAEA/x8fif8lINf/IS7D/yA2z/8aMs7/Hj3i/yNI8v8kSPT/I0bv/yNH + 8P8jR+//JSnjeSYk4QAmHuAAJh/gAyYf4CImH+BBJR7fUCUe32QmH+BqJh7fYiYf3x4mHt8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJiHAACYh + wAAmIcAAIBnOAEpO4QDY2v4A0ND7ACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gCiYe + 4NElHN//KEHs/zJN7f8nI+D/Ihnf/yYf4P8mH+D/Jh/g/yYf3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mHt//Jh7g/yYf4P8mH+D/JR/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe + 4P8mHuD/Ix3j/1JYmf+srbH/ubrJ/7a4x/+2t8b/trfG/7a3xv+2t8b/trfG/7m6yv+np7P/a2tr/6ut + uv+MjJP/YjIt/2UlIP9mKST/Zigk/2YpJP9mKST/Zikk/2UoJP9mKST/Zikk/2YoJP9mKST/Zigk/2Yo + JP9mKST/Zigk/2YpJP9lKCT/Zigk/2YoJP9lKST/Zygg/2MrNP8+PKb/JkXq/yFI9/8fSP3/H0j7/yBJ + +P8eR/j/I035/09v+/9kgf//ZIH//22J//9wi///cIv//3CM//9sh///ZoD7/2aA+/9ngPf/Ynfz/1hw + 8/84Vuz/IkLp/yVE5v8rSuP/O1/p/0Jk6P9Pb+b/N1Ta/x42zP8gNsj/HzPB/x0xvf8cLbf/Gyqu/xsm + pP8ZIZn/FxyN/xUXgv8MC3n/SUyT/87NxP/W0sT/qKmx/yMme/+oqrj/0c/H/8PEw/83Oob/DAlz/xUT + e/8VE33/FxSD/xkWj/8bGJv/HRmq/yAbuP8jHcr/JR7Y/yYf3/8nH+T/Jx/m/yYf4/8mH+H/Jh/g/yYf + 4P8mH+D/JR7f/yYf4P8mH+D/Jh7g/yYf4P8mH9//Jh/g/yYf4P8mH+D/Jh7g/yYf3/8mHt//Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh7g/yYe4P8nH+X/IhzD/xMS + cv8cGKf/JyDm/yYf4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYc3/8lJeL/I0bu/yNH8P8jRu//I0bv/yNH + 8P8jSfD/I0jw/yJI8P8kSvD/eIz2/5mk9/+UoPf/lKD3/5Sg9/+UoPf/mKP4/46c9v8wL+L/HRnf/8HH + 9///////8vLy/zk6Ov8AAAD/AAAA/wEBAP8fKIb/JT/7/yRJ9v8jSvX/I0n0/yNI8v8jRvD/I0bv/yNH + 7/8jSfD/JDrp8CYb3iQmG94AJh7gACYf4AAmH+AAJh/gACUe3wAlHt8AJh/gACYe3wAmH98AJh7fACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYh + wAAmIcAAJiHAACAZzgBKTuEA2Nr+ACUe3wAlHt8AJR7fACUe3wAlHt8AJR7fACUe3wAlHt8AJR7fACUe + 3y4mH+DvIhnf/zY75P+On/f/eYTx/zEx4v8gF9//JBzg/yYe4P8mH+D/Jh/g/yYe3/8lHt//Jh7g/yYf + 4P8mH+D/Jh7f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/JR7g/yMd4v9XXZX/srO5/7i5yP+2t8b/trfH/7a3x/+2t8f/trfG/7a3xv+6u8r/lpaf/3Jy + c/+ytcT/fHR3/2IoI/9lJyL/ZSkk/2UoJP9mKST/Zigk/2YpJP9mKST/Zikk/2YpJP9lKCT/ZSgk/2Yo + JP9lKCT/Zikk/2YoJP9lKCT/ZSgk/2YoJP9mKCT/ZSgk/2kmGP9EOZP/HUr//yJH8/8jR/D/I0bv/yNH + 8P8jR/D/HULv/2B69P+fqff/nKX4/5ul9/+ZpPf/maP3/5mj9/+Zo/f/mqT4/5ql+P+apfj/mqX5/5um + +f+bpvr/kZ/5/0Vl9f8eRPP/IUbz/x5C8P8eQvH/HEDx/yFF8/8kSvX/JEn2/yRJ9v8kSvf/IUj4/x5E + 9f8dRPP/IEXw/yNG7f8jRuv/Hj7f/xw50/9Zctz/dozX/4ue1f87TK3/eYO4/8LFyf/Lzcr/W2CY/wkG + cf8UEHL/FBB0/xQQdP8TEHT/FBFz/xQRc/8UE3b/FRR7/xYVgv8aF5H/HBij/yAbt/8jHcz/JR7b/ycf + 4v8nIOb/Jx/k/yYf4v8mHuD/Jh/g/yYe4P8mHuD/Jh7g/yYf4P8mH9//Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf + 4f8aFpT/FRN5/yQe0v8mH+T/Jh/g/yYe3/8mHuD/Jh/f/yUe3/8mHuD/Jhze/yU66v8jSfH/I0bw/yNH + 7/8jR/D/I0fv/yNG7/8jRu//HULv/09s8v+apff/lZ/3/5Sg9/+UoPf/laD3/5uo+P9ha+3/GA/d/0VG + 5f/y+P3//////4uLjP8AAAD/AAAA/wAAAP8BAAD/HjKG/yRL/P8jR/D/I0fw/yNH8P8jR+//I0bv/yNG + 7/8jR/D/I0Xu/yUm4aImG94AJhveACYe4AAmH+AAJh/gACYf4AAlHt8AJR7fACYf4AAmHt8AJh/fACYe + 3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmIcAAJiHAACYhwAAgGc4AJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuBHJh/f/yYe4P8hGN7/RUnm/5Si9/+ToPb/Vl7p/ysn4f8gGN7/Ixrf/yQd4P8lHuD/JR7f/yYe + 3/8mH+D/Jh/g/yYe3/8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/f/yYf4P8mHuD/Jh7g/yYf + 4P8mH9//Jh/g/yUd4f8kHeD/XGGT/7Ozuf+4ucn/trfH/7a3x/+2t8b/trfG/7a3xv+2t8b/urvM/4yN + lP96en3/srbF/3JlZf9jJB//Zigk/2YpJP9lKST/Zikk/2YoJP9mKST/Zigk/2YpJP9mKST/Zigk/2Yo + JP9mKCT/Zigk/2UoJP9lKCT/Zikk/2YpJP9lKCT/ZSgk/2YoIv9pKB7/NUC6/x9J/P8jRu//I0fv/yNH + 7/8jRu//I0fw/x1C7/9qgfT/n6n3/5ei9/+Xovf/l6L3/5eh9/+Woff/lqH3/5ah9/+Woff/lqH3/5ah + 9/+Woff/lqH3/56n9/95jPb/H0Pv/yNG7/8jR/D/JEfw/yNH8P8jR/D/I0fv/yNG7/8jRvD/Ikbv/zFT + 8P9RbfP/Znz0/22E9f91ivf/d4z3/26H+P9ogfn/VG72/0lo9f9BY/T/OFrz/zBR7/86Xuv/SGrn/zZU + 2v8WLsT/GCy5/xklp/8YIJr/FhuN/xYWg/8WFH3/FRF2/xQPcv8UEHP/FBFy/xMRcf8UEnT/FRN6/xgV + h/8bF5v/Hhmv/yIcxv8lHtj/Jh/h/ycg5/8nH+T/Jh/i/yYe4P8mH+D/Jh7f/yYf4P8mH+D/Jh/f/yYe + 3/8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mHt//Jh/g/yYe + 3/8mHuT/JB3L/xQSdf8bGJv/Jh/k/yYf4f8mH+D/JR/f/yYf4P8mH9//Jh7g/yYc3/8lKOL/I0fv/yNI + 8P8jR+//I0fv/yNH8P8jR/D/I0bv/yFF7/8mSu//fpH2/5uk9/+UoPf/lKD3/5qm+P9+jfP/KSfg/xYL + 3f+Bie7//////+np6f8hISL/AAAA/wAAAP8AAAD/AAAA/xsrdP8kSvz/I0fw/yNH7/8jR+//I0bv/yNH + 8P8jR/D/I0nw/yQy5/cmHd8yJh3fACYd3wAmHd8AJh/gACYf4AAmH+AAJR7fACUe3wAmH+AAJh7fACYf + 3wAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJiHAACYhwAAmIcAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gXiYe3/8mH+D/Jh7g/x8X3v82N+P/fYny/6Gu+f+JlvT/W2Tr/zY35P8pJeH/Ih3f/x8X + 3v8gF97/IRjf/yIZ3/8iGt//Ixvf/yQc3/8kHOD/JBzg/yUd4P8lHeD/JR7g/yYe3/8mH9//Jh7g/yYe + 4P8mH+D/Jh7g/yYe4P8lHeH/JB3e/2NmkP+0tLz/t7jJ/7a3xv+2t8b/trfH/7a4x/+2t8f/trjH/7q7 + zP+IiI//goKI/66ywf9tWFf/ZCMf/2UoJP9mKST/Zigk/2YoJP9mKST/Zigk/2YpJP9mKCT/Zikk/2Uo + JP9lKCT/Zikk/2YpJP9lKST/ZSgk/2YoJP9mKCT/ZSgk/2UpJP9mKST/aScZ/zk2rv8dSf//I0jv/yNH + 8P8jR/D/I0fw/yNH8P8gRO//M1bx/3OI9f+JmPb/i5r2/4ua9v+Kmvb/jpz3/5Ge9/+Qnvf/kJ73/5Cd + 9/+Rnvf/kJ73/5Ce9/+Vovf/aYD1/x5D7/8jRu//I0fw/yRH8P8jR/D/I0fw/yNH8P8jR+//IETv/yxR + 8P+Kmvb/oKn4/56n+P+bpff/mqT3/5mk9/+bpff/nKb3/5ul9/+ZpPf/lqH3/5Sg+P+Mmvf/fI73/2uB + 9v9hePj/VnP7/0Rn+v81V/X/LE7y/yRI7/8cPeT/GzbU/xoxyf8aLbv/Giao/xkhmP8XGor/FxaB/xYT + ev8TEHL/Eg9u/xMQb/8UEXX/FhOB/xoWmP8fGq//IxzJ/yUf2v8nH+T/Jx/m/yYf4/8mH+H/Jh7g/yYf + 3/8mHt//Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh7f/yYe + 4P8mHuD/Jh7g/ycf5v8eGan/ExJv/yAat/8nIOn/Jh/g/yYf4P8mHuD/Jh/f/yYf4P8mH+D/Jhze/yQ3 + 6P8jSfD/I0bv/yNG7/8jR+//I0fv/yNG7/8jR/D/HkPv/zla8f+OnPf/m6T3/5ql+P+NmfT/Nzjj/x4T + 3v8iIuH/w8r4//////+HiIn/AAAA/wAAAP8AAAD/AAAA/wAAAP8YJ27/JEr9/yNH8P8jR+//I0fw/yNH + 8P8jR+//I0nw/yQ+6/8mH9+XJh3fACYd3wAmHd8AJh3fACYf4AAmH+AAJh/gACUe3wAlHt8AJh/gACYe + 3wAmH98AJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACUe3wAlHt8AJR7fACUe3wAlHt8AJR7fACUe3wAlHt8AJR7fACUe3wAlHt8AJR7fACUe + 3wAlHt8AJR7fACUe330mH+D/Jh/g/yYf4P8mH+D/IRjf/yUg3/9XX+r/kZ72/6Kv+f+Wo/f/gY/z/3B7 + 7/9kae3/VVzp/0pR5/8/RuX/ODfj/zMt4/8uK+H/Kyfg/ykm4P8mI+D/Ih/f/yIe3v8fF9//Hxbe/x8X + 3v8gGN//IBjf/yAY3/8gGN//IBfg/x4X4f9VW5X/srO5/7i5yP+2t8b/trfG/7a3x/+2t8b/trfH/7a3 + x/+6u8z/iYmQ/4qJkP+us8D/bFRT/2QkH/9lKCT/Zigk/2UpJP9lKCT/Zikk/2YoJP9mKCT/Zikk/2Yo + JP9mKCT/ZSgk/2YoJP9lKST/Zikk/2YoJP9lKCT/Zikk/2YpJP9mKST/Zikk/2goHP9WJ1f/KC7f/yA8 + 9v8jQe//I0Pu/yNF7v8jRu7/I0fv/yBF7/8fRe//J03w/y5V8f8tVPH/LFPx/zNZ8f85XPL/OVzy/zhc + 8v84XPL/OFzy/zhc8v84XPL/OVzx/ypQ8f8hR/D/I0nw/yNJ8P8jSfD/I0jw/yNI8P8jSPD/I0jw/yJG + 7/8kSPD/VG/z/4CS9v+Qnff/mqX3/5ul9/+Zo/f/mKL3/5ah9/+VoPf/laD3/5Wh9/+Woff/lqH3/5ij + 9/+bpfj/nab4/5ql9/+Xovf/lKD4/4iY9/94jfb/aoH3/1h1+f9Gafr/NFj5/yxQ9f8iSPL/HEDt/x07 + 3/8dNtH/GzHE/xsqsf8bI57/GB2P/xYXgf8VE3b/ExBu/xMQcv8XE4L/GhWZ/x8atP8jHM//Jh7c/ycf + 5f8mH+b/Jh7k/yYf4/8mHuH/Jh7g/yYf4P8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/f/yYe3/8mH+L/Jh/h/xoWlP8WFHr/IBu5/yYf4v8mH+L/Jh/g/yYe3/8mH+D/Jh/g/yYd + 3/8lIuH/I0Pt/yNI8P8jRu//I0bv/yNH8P8jR+//I0fw/yNH7/8dQu//Olrx/4GS9v+Kmvf/P1ft/x4s + 5v8cMej/RmXw//X7///t7Or/ISIi/wAAAP8AAAD/AAAA/wAAAP8AAAD/Dhxi/yVK/f8jR/D/I0fw/yNH + 7/8jR+//I0fw/yNF7/8lJuLeJh3fGCYd3wAmHd8AJh3fACYd3wAmH+AAJh/gACYf4AAlHt8AJR7fACYf + 4AAmHt8AJh/fACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+CaJh/g/yYe4P8mH+D/Jh/g/yYf4P8kHN//Hhbf/zIy4/9jbe3/jJn1/56r + +f+irvr/oq/6/5+t+f+dq/n/m6r5/5un+P+Wo/f/jZv2/4aV9P+AjvP/eojy/3OA8P9xfe//a2/u/2xv + 7v9ma+3/WmLr/1tj6/9aYer/Tlbp/01V6f9MVe3/YGif/6emrP+5usn/trfH/7a3x/+2t8f/trfG/7a3 + xv+2t8b/urvL/4+Pl/+DhIn/r7PB/21aWf9kIx7/ZSgk/2YoJP9mKST/Zigk/2YoJP9lKST/Zigk/2Yp + JP9mKCT/ZSgk/2UoJP9mKST/ZSgk/2UoJP9mKCT/ZSgk/2YoJP9lKCT/ZSgk/2YoJP9mKCT/aSga/1wm + Rf86Iqz/JCDn/yIi6v8kJuT/JSfi/yUp4/8lK+T/JCrj/yMr5f8iK+b/Iivm/yIr5v8hLOb/Hy/m/x8v + 5v8fL+b/Hy/m/x8w5v8fMOb/HzLn/x806P8iNun/JDjp/yQ46f8kOOj/JDjp/yQ86v8kPOr/JEDt/yND + 7v8jR+7/Ikjv/x1E8P8jSvD/N1ry/0xq8/9he/T/eIz1/4eW9v+Qnvf/maX3/5ul9/+ao/f/maP3/5ei + 9/+Woff/laD3/5Sf9/+UoPf/laD3/5Wg9/+Yovf/mqT3/5ym+P+apff/mKP3/5Sg9/+Glvb/dYn2/2Z9 + 9f9Sb/b/PV/3/y9U+P8mTfb/HkTy/x1C8P8ePuT/HjnT/x0zxf8dK6//GiOa/xYah/8WFnz/FhN5/xcT + hP8bFZr/IBmv/yIbxf8kHdX/Jh/d/ycf5f8nIOb/Jx/l/yce5P8nH+P/Jh/j/yYf4v8mHuH/Jh/h/yYe + 4P8mHuD/Jh7h/yYf4v8mH+P/Jx/m/yYf4f8fGrD/FBJ1/xQSdP8dGKH/Jh/g/yYf5P8mHuD/Jh7g/yYe + 4P8mH+D/Jhzf/yUs5P8jSPD/I0fw/yNG7/8jR/D/I0fv/yNG7/8jR+//I0fv/x5C7/8mS/H/K0/w/x5E + 7/8jSvD/Fj/v/4qk+f//////lpaX/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/w8cWP8mSvj/I0fx/yNH + 7/8jR+//I0jw/yNH8P8lLeX6JhzfTiYd3wAmHd8AJh3fACYd3wAmHd8AJh/gACYf4AAmH+AAJR7fACUe + 3wAmH+AAJh7fACYf3wAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAIBfeACAX3gAgF94AIBfeACAX3gAgF94AIBfeACAX3gAgF94AIBfeACAX + 3gAgF94AIBfeACAX3gAgF94AJh/gsCYf4P8mHuD/JBvg/yEZ3/8eF9//Hhbe/x0V3v8ZD97/HhXe/y8u + 4f9MT+j/WWTr/2Rw7f9lcO3/ZHDt/2Vy7f9xfe//cn7w/3yG8v+GkPT/jJn1/5Sj9/+hrfr/oq76/6Cs + +f+cqfn/nKn5/5uo+P+bqPj/mqj4/5mm+P+Zpvj/m6n//4GHuP+UlJn/urvM/7a3x/+2t8f/trfG/7a3 + xv+2t8b/trfG/7q7y/+XmKL/eHl7/7W4yP92bnD/YyYh/2UnIv9lKST/Zigk/2YoJP9lKCT/ZSkk/2Yo + JP9mKCT/Zigk/2YoJP9mKCT/Zikk/2YoJP9lKCT/Zigk/2YpJP9lKCT/ZSgk/2UpJP9mKST/Zigk/2Yo + JP9oKRv/aCkf/1UnXf83IrT/Jh7j/yIc6v8lHOL/Jh3f/yYc3/8mHN7/Jhzf/yYb3v8mHN//Jhzf/yYc + 3/8mHd//Jh3f/yYd3/8mHd//Jh3f/yYd3/8mHt//Jh3f/yYd3/8mHd//Jh7f/yYd3/8lHd//Jh7e/yYe + 3/8lIuH/JSfj/yUr5P8lMeb/Izfp/x866/8cPO3/HUHu/yBG8P8nTvH/OFzy/0dm8v9YdPT/boT1/4CQ + 9v+ImPb/kZ73/5qk9/+bpff/m6T3/5qk9/+Yovf/l6H3/5Wg9/+Un/f/lKD3/5Sg9/+VoPf/mKL3/5uk + 9/+cpvj/mqT3/5ij9/+Mm/f/fI/2/2yC9f9WcPT/PmD1/y9T9v8kS/j/HUT1/x5E8/8fQu//ID7f/x85 + z/8dMLn/Gyah/xgfjv8WGIL/GBWB/xYRiP8RDJj/GROn/yEbuv8jHMv/JB3T/yUf2P8mH9z/Jh/f/ycf + 5P8nH+b/Jx/m/ycf4v8mH93/JR7W/yAatv8ZFo3/FBN4/xUTe/8VE3r/ExJz/xsXmP8mHtz/Jh/l/yYf + 3/8mH+D/Jh/g/yYf3/8mHd//JDXo/yNJ8P8jR+//I0fv/yNH7/8jRu//I0fw/yNH7/8jR+//IUXv/yBF + 8P8jR/D/IETv/yNJ8P/O2/7/+Pf1/zMzNP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8QGkH/Jknt/yNH + 8v8jR+//I0fw/yNI8P8lMeb/JhzffCYc3wAmHd8AJh3fACYd3wAmHd8AJh3fACYf4AAmH+AAJh/gACUe + 3wAlHt8AJh/gACYe3wAmH98AJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACAX3gAgF94AIBfeACAX3gAgF94AIBfeACAX3gAgF94AIBfeACAX + 3gAgF94AIBfeACAX3gAgF94AIBfeAh8X378dFN7/HBbe/yYl4P83M+P/SEfm/1xk6f9xduv/eoDu/zM0 + 4v8hF9//Hxbf/x8Y3v8gGd7/IBne/yAZ3v8hGt//Ihvf/yIa3v8iGt//Ix7f/ysr4f82OOT/SEvn/2Zy + 7v+LmPX/m6j4/5qm+f+Zpfj/mKP4/5ii+P+Xovj/lqL4/5ij/f+Jktf/hIWL/7i5yP+2t8f/trfH/7q7 + y/+4ucn/trfH/7a3x/+6u8v/pqaz/29wcP+ys8L/kZSd/2I9OP9lIx7/Zigk/2YpJP9mKCT/ZSgk/2Uo + JP9mKCT/ZSgk/2UoJP9mKCT/ZSgk/2UoJP9mKCT/Zigk/2YoJP9lKCT/Zigk/2YoJP9lKST/Zikk/2Yo + JP9mKST/Zikl/2YpI/9pKRj/Zykg/1goVv87I6f/Jh/f/yIe7P8kHuX/Jh/g/yYf3/8mHt//Jh/g/yYf + 4P8mHuD/Jh7f/yYe3/8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh7g/yYe4P8mHt//Jh7f/yYe + 4P8mHuD/Jh3g/yYd3/8mHN//Jhze/yYd3/8mHd//JiLh/yYn4/8lK+T/IzHm/yA16f8cOez/HD7t/x5E + 7/8hSPD/KlHx/zlc8v9IZfL/VnP0/2mA9P98jfX/hpb2/46c9v+Yo/f/m6X3/5qk9/+apPf/maP3/5ii + 9/+Woff/laD3/5Wg9/+VoPf/l6H3/5qk9/+cpvj/mqT3/5ij+P+Mm/b/eo72/2Z99f9QbfP/Olry/ytQ + 9P8hSPb/HUT3/x9F9f8gRfL/IUHk/xo10P87TMD/ZG2q/zE4j/8UFX//ExB1/wwJd/8RDYH/GBOJ/xkW + kf8aF5r/Gxed/xsXnv8aF5f/GRaN/xcUgP8UEnX/FBJ1/xUTev8VE3v/FBN7/xQTev8TEnL/GheQ/yUe + 1v8nIOf/Jh7f/yYf4P8mH+D/Jh7g/yYe3/8kPOr/I0nw/yNG7/8jR+//I0fw/yNG8P8jRu//I0fv/yNH + 7/8jR+//I0fv/xo/7/9OcPP//f///7Szsf8BAAH/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/EBtD/yZK + 7v8jR/L/I0jw/yNI8P8lMeb/JhzfliYc3wAmHN8AJh3fACYd3wAmHd8AJh3fACYd3wAmH+AAJh/gACYf + 4AAlHt8AJR7fACYf4AAmHt8AJh/fACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AA9OeQAPTnkAD055AA9OeQAPTnkAD055AA9OeQAPTnkAD05 + 5AA9OeQAPTnkAD055AA9OeQAPTnkAD055AtDQ+XNZGzq/5GT8f+psPX/wsb5/9TU/P/V1/z/2dr8/9re + /f9OVeb/IBff/yYf4P8mHuD/Jh/g/yYf4P8mHt//Jh/g/yYe4P8mHuD/JR7g/yUe4P8kGt//Ihjf/x8W + 3v8eF97/MC7i/1Ja6f9xfPD/go7z/4eW9P+LmfX/j532/5Og9v+Zpvn/mqf4/3p9j/+rq7b/ubrK/7S1 + xP+UlJ3/q6u5/7i5yf+2t8b/t7jI/7O0w/9zdHX/m5ym/7i7y/95eXv/Yjcy/2UjHv9mKCP/Zikk/2Yo + JP9mKCT/Zigk/2UoJP9mKST/Zigk/2YoJP9mKST/Zikk/2YoJP9lKCT/Zigk/2YoJP9mKCT/Zikk/2Yp + JP9lKCT/Zikk/2YpJP9lKST/Zigl/2YoIv9pKBn/aSkb/1spR/9BJJT/KyDS/yIc6f8jG+X/JR3g/yYf + 4P8mH+D/Jh/g/yYe3/8mHt//Jh7f/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mHuD/Jh/g/yYf3/8mHt//Jh/g/yYe4P8mHuD/Jh/g/yYe4P8mHd//Jhzf/yYc3/8mHd//Jh7g/yYj + 4P8lKOP/JC7l/yIz6P8fOev/HDzt/xxB7v8dRfD/H0bw/yhO8f81WPH/QmDy/09s8/9he/T/dIf1/4KS + 9v+Kmvb/lKH3/5ul9/+apff/mqT3/5qk9/+Yovf/lqH3/5Sg9/+VoPf/l6L3/5uk9/+bpvf/mqX3/5ei + 9/+Hl/b/doj1/1958/9FZPP/M1Ty/yVM8/8QOvT/V3b6/+Tv//+dtfn/Gjzh/zNM0f9pdbn/QUud/xYa + hf8TEnX/Eg9t/xMPbf8TEHD/ExFz/xQSdv8UE3j/FRN7/xUTe/8VE3r/FRN7/xQTev8VE3v/FRJ6/xQS + dP8YFoj/JB7P/ycf5/8mHuD/Jh/g/yYf4P8mHd//JiLg/yNA7P8jSfD/I0fv/yNH8P8jRvD/I0bv/yNG + 7/8jR+//I0fw/yNG7/8XPO7/lq36//////9VVFX/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/xAY + N/8mSOj/I0n0/yNH8P8lMOb/Jh3flCYe4AUmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJR7fACUe3wAmH+AAJh7fACYf3wAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAx8v6AMfL+gDHy/oAx8v6AMfL+gDHy/oAx8v6AMfL + +gDHy/oAx8v6AMfL+gDHy/oAx8v6AMfL+gDHy/og0NL74dfZ/P/Z2vz/19f8/9PT/P/Q0Pv/0M/7/8/Q + +//W2Pz/aWzq/x0V3v8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mH9//Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/JR7f/yIa3/8fF9//IBnf/yUi4P8tLuH/MjTi/zg44/88POT/QkHm/1Ja8f9sdLH/k5SU/7y+ + zv+urr3/YmJg/4mKkP+6u8z/trfG/7a3xv+7vMz/k5Sb/3R0df+4ucj/srXE/3t7f/9jPjr/ZCUf/2Ul + IP9mJyP/Zigk/2YoJP9mKCT/Zikk/2YoJP9mKCT/Zigk/2UoJP9lKCT/ZSgk/2UoJP9mKST/Zigk/2Yo + JP9lKST/Zigk/2YoJP9mKCT/Zigk/2YpJP9mJyP/Zici/2UkIP9oJRf/aSQS/14nNf9ILIH/LSnI/x4Z + 4f8fF+H/JBvg/yYe3/8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7f/yYe4P8mH+D/Jh/g/yYe + 4P8mH9//Jh/g/yYf4P8mH9//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mHuD/Jh3f/yYc3/8mHd//Jh7f/yYi4f8mJ+P/JSzk/yQ15/8iOur/Hz7t/xxB7v8bQ/D/HUXw/x5F + 8P8jSfD/L1Pw/zta8f9JZvP/Wnb0/22C9f9+j/X/iJj2/5Kg9/+bpPf/mqT3/5ql9/+ao/f/l6L3/5Wg + 9/+Voff/maP3/5ul9/+bpff/mqX4/5Ge9/9/kfb/Y3r0/3SN9f/X4P7/r8P8/yBH8/85Yfn/0eH//563 + /P8hROn/IDrV/x4xu/8aJJ//GBmH/xUSd/8UEHT/FBF1/xQSef8UE3r/FRN7/xUTe/8VE3r/FRN7/xUS + e/8VEnv/FBN1/xYUgP8iHMX/Jx/n/yYf4P8mHuD/Jh/g/yYd3/8lI+H/JD/r/yNJ8P8jR/D/I0fw/yNH + 7/8jR+//I0fw/yNH8P8fQ+//Jkzv/9jk///d29b/DQ0O/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8PFSj/JUri/yRE8v8mK+T6JhzfeSYd3wAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmH+AAJh/gACUe3wAlHt8AJh/gACYe3wAmH98AJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gANLS+wDS0vsA0tL7ANLS+wDS0vsA0tL7ANLS + +wDS0vsA0tL7ANLS+wDS0vsA0tL7ANLS+wDS0vsA0tL7NdHR+/XPz/v/z8/7/8/P+//Pz/v/z8/6/87P + +//Oz/v/29z9/4WE7/8bFN7/Jh7g/yYe4P8mH+D/Jh/g/yUf3/8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/yYe3/8mHuD/Jh7g/yYe4P8lHN//Ixrf/yIZ3/8hGN7/IBje/x8V3v8YEd//XWnh/3+B + iP+ys7//tbbG/4SFiv+Xl6H/ubrK/7a3xv+2t8b/t7nI/7W2xv95eXz/fn6B/7i6yv+6vc3/jI+W/2hT + Uf9iMSz/Yygj/2QkH/9lJB//ZSQg/2UkIP9lJCD/ZSQg/2UlIP9lJSD/ZSUg/2YmIf9lJiH/ZiUh/2Yl + IP9lJSD/ZSUg/2UkIP9mJCD/ZSUg/2UkIP9kJB//ZCgk/2MtJ/9jNC//Y0A9/2pUUv92aGP/hX51/5CS + nf+Bh83/SU3b/yUg4P8cFOL/Ihrh/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh7f/yYe3/8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/f/yYe3/8mHt//Jh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh3f/yYc3v8mHN//Jh3f/yYi4P8mKOP/JS3l/yQ1 + 5/8jPev/IkHt/yBF7/8dRPD/HEPw/x1E8P8dQ/D/IUbw/yxQ8P85WfH/SGXy/1p18/9xhfX/gJH1/4yc + 9v+YpPj/m6X3/5ql9/+ZpPf/l6L3/5ag9/+Xoff/mqP3/5ei9/+rtPn/6uz+/93j/f95jfX/cYr1/9Xf + /f+Wrvn/HETy/xxD9f8eRfb/H0b1/yJD5/8gOtH/Hiyx/xkekv8WFH3/Ew9z/xMQdP8UEnj/FRN6/xUT + e/8VE3v/FRN7/xUTe/8TEnb/FRN7/yAbuf8mH+X/Jh/i/yYf3/8mH+D/Jhzf/yYg4P8lOOj/I0nw/yNI + 8P8jR/D/I0fv/yNG8P8jRu//Gj7v/1R19P//////hYSE/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/DhUq/yI23f8lIuXhJhzfUSYc3wAmHd8AJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmHt8AJh/fACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ADOz/oAzs/6AM7P+gDOz/oAzs/6AM7P + +gDOz/oAzs/6AM7P+gDOz/oAzs/6AM7P+gDOz/oAzs/6AM7P+jXPz/v1z8/7/8/P+//Pz/v/z8/7/8/P + +v/R0fv/19f8/+Xl//+MkPD/HBbe/yUe4P8mH+D/Jh/g/yYf4P8lH9//Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYe4P8mH+D/Jh7g/yYf3/8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8iGd//MC/h/4ya + /f+AhrX/j4+S/7u9zf+6u8v/uLrK/7a3xv+2t8b/trfH/7a3xv+5usr/r7C+/3N0dv9zc3X/oaKt/7q7 + y/+prbr/iImP/3dub/9vXl//aVBP/2VGQv9kPzv/Yzw4/2M2Mv9jNTD/YzUw/2MzL/9jMCv/Yy8q/2Ix + LP9jNDD/YzQw/2M0MP9jNjP/ZDw4/2VAPP9mSkf/a1lY/3Npaf9+eX3/jI6V/5ufqf+orbn/srXF/7q8 + zP+8vcr/wsPI/77By/+codL/Ymjb/zEx4v8iGuX/Jh7g/yYf3/8mHuD/Jh/g/yYe3/8mHuD/Jh7f/yUf + 3/8mH+D/Jh/g/yYf4P8mHuD/Jh7f/yYf4P8mH+D/Jh7g/yYf4P8lH9//Jh7f/yYf3/8lHt//Jh7g/yUe + 3/8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/JR/g/yUe4P8mH9//Jh7f/yYe4P8mHuD/Jhzf/yUc + 3v8mHN//Jh3f/yYj4f8lKeT/JTHm/yQ56v8kQOz/I0Xv/yJH8P8gRvD/HUTw/xxC8P8dQu//HULv/yNJ + 8P8xVPD/QF/y/1Zy8/9sgvX/gJH2/46c9/+Zo/j/nKX3/5ul9/+VoPf/oq34/+Xo/f/k5/3/oKn4/6ix + +P/u7/7/w8r7/3aJ9f9kfPP/R2by/zJU8f8iSPP/HUT2/x1F9v8gRO//Ij7c/x8xvP8ZIJf/FhR8/xQP + c/8UEHb/FRJ6/xUTev8UE3v/FRN7/xQSd/8TEnT/Hhqp/yYf4f8mH+T/Jh/g/yYf4P8mHeD/Jh3f/yUt + 5P8kQez/I0nw/yNJ8f8jR/D/I0bv/xY97v+ftv3/+/n0/zExMv8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/xIWKP82MtDKIBblKCYc3wAmHN8AJh3fACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh7fACYf3wAmHt8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AA0dH7ANHR+wDR0fsA0dH7ANHR + +wDR0fsA0dH7ANHR+wDR0fsA0dH7ANHR+wDR0fsA0dH7ANHR+wDR0fs0z8/79c/P+//Q0Pv/1dT8/9nZ + /P/Z2v3/y8/6/66z9f+Ei+//QEDk/yIa3/8mH+D/Jh/g/yUe3/8lHt//Jh/g/yYf4P8mHuD/Jh/g/yYf + 3/8mH9//Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8kHOD/Ihzf/3B7 + 7/+bp/v/kp3x/3Z4h/+pqbL/urzM/7a3x/+2t8b/trfH/7a3x/+2t8b/trfG/7q7y/+xssD/f36D/2Ni + Yf90dHX/k5Ob/7K0wv+2usr/rbG//6eruP+kqLP/mp2n/5ebpf+Qk5v/jpKa/4+Tmv+PkZn/iIaN/4aE + iv+Hh43/j5Ka/4+Smv+Pkpr/kJOc/5eapP+bnqn/pKm0/6ituf+wtMP/ubzM/7u8zf+9vs7/vr/Q/7y9 + zv+7vM3/uLrK/7Cxv/+oqLP/oqOk/5SVkv5vdITpSk2hwScj3scmHuH/Jh7g/yYe4P8mH+D/JR/g/yYe + 3/8lH9//Jh/g/yYf4P8mHuD/Jh7g/yYf3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8lH9//JR7f/yYf4P8mHuD/JR/f/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8lHt//Jh/f/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jhze/yYc3/8mHN//JR/g/yUl4v8lLOX/JTbo/yQ+7P8kRO7/I0jw/yNJ + 8P8hR/D/H0Xw/xxC7/8cQe//HEHv/yNI8P8zVPH/Q2Hx/1t18/93ivX/hZX2/6Cs+P/l5/7/5uj9/56o + +P+irfn/6ez9/8DH+/+WoPj/m6X3/5ql+P+Pnff/fY/2/2N89P9GZfL/MFPy/x9G9P8cRPb/IUXy/yI/ + 3P8dLbT/GBqJ/xQQdP8TEHT/FBJ5/xUTev8VE3v/FRN5/xQSdP8bF5n/JR7Y/ycf5v8mH+D/Jh/g/yYf + 4P8mHN//JiHg/yUw5v8kP+z/I0jv/x9G8P8rUvH/4O3//8PBvf8CAgP/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8hIiL/rK27hCAW5QAmHN8AJhzfACYd3wAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYe3wAmH98AJh7fACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAMvM+wDLzPsAy8z7AMvM + +wDLzPsAy8z7AMvM+wDLzPsAy8z7AMvM+wDLzPsAy8z7AMvM+wDLzPsAy8z7N9XV/PjZ2/3/1tf8/73D + +P+bn/P/aG/r/0FB5P8lJOD/GxTe/yEZ3v8nH+D/Jh/f/yYe4P8lHt//Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mH9//Jh/f/yYe3/8mHt//JR/g/yYe4P8lH9//JR7f/yYe4P8mHuD/Jh/f/yYe3/8lHt//Hhfe/1hi + 6/+ap/j/laD3/5ai/v+GjtD/fHt+/7Kzv/+6vMv/trfG/7a3xv+2t8b/trfG/7a3xv+2t8b/ubrK/7i5 + yf+amqT/ent+/3Bwcf+XmKL/u7zM/7m6yv+6u8v/urvL/7q7zP+6u8z/urzM/7q7zP+6vMz/urzM/7q8 + zf+6vc3/urzM/7q7zP+6u8v/urvM/7q8zP+7vMz/u7zN/76/z/++wND/vL7P/7u8zP+3uMj/rK27/6Wm + sf+XmKL/iYqQ/H19gONzc3W9ampqnmZnZm1mZmRCbmxhJmJoegQoJ9sJJR3iSSYe4KUmHuDsJh7g/yYf + 3/8mH+D/Jh/g/yYf4P8mHt//Jh7f/yUf3/8lH9//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yUe3/8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8lHt//JR7f/yYf + 3/8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yUe3/8mHd//Jhzf/yYc3/8mHt//JiTh/yUp + 4/8lNOf/JDzr/yND7f8jSPD/I0nw/yNJ8f8hRu//HkPv/x1C7/8cQe//HkPv/yZK8P9ObfP/0dv9/9bd + /f+Jmvb/qLL5/+3u/v+3v/n/k573/5Sf9/+VoPf/l6H3/5qk9/+bpff/mqX4/42b9v90iPX/T23y/ytO + 8f8bQvP/Ikn3/yNG6/8gNcb/GSCV/xQSeP8UD3P/FRJ4/xUTe/8UE3r/FBJ0/xcVhP8hHL//Jh/l/ycf + 5P8mH+D/Jh7g/yYe4P8mHN7/Jh/f/yUn4/8bKOX/WHDx//////9xcHD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/LCws/8HBvavGxsYAxsbGAMbGxgDGxsYAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmHt8AJh/fACYe3wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACJmvAAiZrwAIma + 8ACJmvAAiZrwAIma8ACJmvAAiZrwAIma8ACJmvAAiZrwAIma8ACJmvAAiZrwAIma8EejqvT/hYrw/1JW + 5/8yMuL/HRrf/xwU3v8fF9//JBzg/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yUf4P8lHuD/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8lH+D/Jh/f/yYe4P8mH+D/Jh/g/yYe3/8mH9//Hxfe/0hM + 5/+Vovf/lqL3/5Sg9/+UoPj/l6P+/4GIvv99fH7/r6+5/7y9zv+3uMj/trfG/7a3xv+2t8b/trfH/7a3 + x/+3uMf/u7zM/7m6yv+ztMP/t7fH/7a3xv+2t8b/trfH/7a3x/+2t8b/trfH/7a3x/+2t8f/trfH/7a3 + x/+2t8b/trfG/7a3xv+3uMj/ubrK/7u8zf+6u8z/urvL/7W2xf+rrLn/oqOu/5WVnv+FhYv7e3t+2XBw + crNqammVZ2dlYGdnZTpoaGYfaGdmBGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gKiYf + 4HwmHuDMJh/g/yYe4P8mH+D/Jh7f/yUf3/8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/JR7f/yYe + 4P8mHt//Jh7g/yYe4P8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/f/yYf + 4P8mHuD/JR7f/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh7g/yYd + 4P8mHd//Jhzf/yYe3/8mIuH/JSnk/yUz5/8kPOv/JETt/yNI8P8jSfH/I0nw/yNH7/8TOe7/U3T0/9Xh + /f+ds/n/HkXw/3KL9v/i6P7/jJ33/4ya9v+bpfj/m6X4/5ei9/+UoPf/lKD3/5Wg9/+Xovf/mqT3/5yl + +P+Kmff/VnDz/yJH7/8fRPL/JEr3/yRI8f8hO9T/GyWi/xYVfv8UD3L/FBF2/xUTe/8UEnb/FBN3/xwY + n/8kHtT/JyDn/yYf4/8mH+D/Jh/g/yYe4P8mHN//GhHd/56j9P/7+vP/Ly8w/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/zg4OP/Dw8TPxsbGDcbGxgDGxsYAxsbGAMbGxgDGxsYAxsbGAMbG + xgDGxsYAxsbGAMbGxgDGxsYAxsbGAMbGxgDGxsYAJR7fACUe3wAmH+AAJh7fACYf3wAmHt8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AALSzhAC0s + 4QAtLOEALSzhAC0s4QAtLOEALSzhAC0s4QAtLOEALSzhAC0s4QAtLOEALSzhAC0s4QAtLOFIJiTf/xwV + 3/8dFd7/IRnf/yYd4P8mH+D/Jh7g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/JR/g/yYf3/8mH9//Jh7g/yYe4P8mHuD/Ihrf/zY4 + 5PmNm/XsmKT48JSf9/qUoPf9lKD3/5Sg+P+Wo///g4vG/3Z3f/+cnKH/uLnJ/7u8zP+3uMj/trfG/7a3 + xv+2t8b/trfG/7a4x/+3uMf/t7nI/7a3xv+2t8b/trfG/7a3x/+2t8f/trfG/7a3xv+2t8f/trfH/7e4 + yP+5usr/u7zM/7q7y/+6usv/tLXF/6mquP+goKv/kZKZ/4SEifR4eHrRb29wrWlpaItmZmVTZ2ZmNGho + ZxdwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf + 4AAmH+AAJh7gDSYe30kmHt+gJh/g5iYf4P8mH9//Jh/g/yYf4P8mH9//Jh/g/yYf4P8mH+D/Jh7f/yYe + 3/8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYe + 3/8mH+D/Jh/g/yUe3/8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe + 3/8mH+D/Jh/g/yYf4P8mH+D/Jh3g/yYc3/8mHN//Jh7f/yYi4P8lKeP/JTPn/yQ97P8gQO3/Iknw/6a7 + +v/a5v7/Y4P1/w007v+Goff/ucv8/ytQ8P8wUfD/UW3z/3GF9f+Pnfb/m6b4/5qk9/+Woff/laD3/5Sg + 9/+VoPf/mKL3/52n+P92i/X/Kk7w/x9D7v8jR/H/JEn1/yRK9f8iQN7/HSux/xcZiP8UEHP/FBB2/xQS + ef8UEnT/FhSA/x4arv8mHtv/Jx/n/yYf4/8mH+D/Ihrf/yop4f/X4f7/1dPN/wUFBf8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP9MTE3/zc3N7sTExRzExMUAxMTFAMTExQDExMUAxMTFAMTE + xQDExMUAxMTFAMTExQDExMUAxMTFAMTExQDExMUAxMTFAMTExQAlHt8AJh/gACYe3wAmH98AJh7fACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACMb + 3wAjG98AIxvfACMb3wAjG98AIxvfACMb3wAjG98AIxvfACMb3wAjG98AIxvfACMb3wAjG98AIxvfSCQb + 3/8lHt//JB3g/yEY3/8cFN7/GhPe/xwW3v8dF97/Hxff/yYe4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf + 4P8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mHt//Jh7g/yYf4P8mH+D/Jh7f/yYf4P8mHuD/Jh/g/x4V + 3vNNT+hMnar4IpSg9yiUn/cxlKD3P5Wg91CUn/dslaD4hZej/5mNmOWsdXiTxH18fvOfoKr/trfG/7y8 + zf+6u8z/uLnI/7e3x/+3uMf/trfG/7a3x/+2t8f/trfH/7a3x/+3uMf/uLnJ/7q7y/+7vM3/urzM/7m6 + yv+1tsb/qqu4/6Chqv+RkZn/goKI8HZ2eMtub26maGhngGdnZU9nZ2YraGhnEW9vcABpaWgAZmZlAGdm + ZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4CAmHuBmJh7fuSYe3/kmHuD/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH9//Jh/f/yUf3/8lHt//Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh7f/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe3/8mHd//JR3e/yYc3/8mHt//GRjg/3KB + 7//b4v3/o7b5/x9E7v8oTvD/wdH8/5Ss+f8cQe//HkLv/x1B8P8fRO//MVPw/1Jv8/94jPX/laH3/5ym + +P+Zo/f/laD3/5Sg9/+Un/f/nqf3/3eM9f8jSO//IUXw/yNH7/8jR/D/I0n0/yRK9v8jROn/HzPC/xke + k/8UEXf/FBBz/xQRdv8UEnX/FxWH/x8asv8lHtn/Jh/h/x8W3/9OVef//////5OTkv8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/cHBx/8/PzvvDw8Q1w8PEAMPDxADDw8QAw8PEAMPD + xADDw8QAw8PEAMPDxADDw8QAw8PEAMPDxADDw8QAw8PEAMPDxADDw8QAw8PEACYf4AAmHt8AJh/fACYe + 3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAgFt8AIBbfACAW3wAgFt8AIBbfACAW3wAgFt8AIBbfACAW3wAgFt8AIBbfACAW3wAgFt8AIBbfACAW + 31obE97/HRbf/yUh4P84O+P/UU/n/2dp6v96hO7/h4/w/2hy6v8mIeD/JR7g/yYe4P8mHuD/Jh/g/yYf + 4P8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/f/yYe3/8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf + 4P8kHOCQTU/oAJ2q+ACUoPcAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QdmZmIwaWlnjnp7 + feSUlZz/paay/7Gywf+4usn/ubrK/7m7zP+6u8z/ubrK/7m6yv+5usr/t7jI/6+vvf+mp7P/n5+p/5CR + mf+Dg4j1dnZ41G5ub6hoaGh6Z2dlSWdnZidoaGgMbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZm + ZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd + 4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wImHuAzJh/ggiYe4M0mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYe3/8mH+D/Jh/g/yYf + 4P8mH+D/Jh7f/yYf4P8mH9//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7f/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/HhTf/z9C + 4//Hy/j/z9T7/0xR5v8QD9//Z3fu/9ji/f9fe/L/Gj/u/yNJ8P8jSfD/Ikfv/x9E7/8cQe//Ikfv/zhZ + 8f9fePP/g5T2/5ik9/+cpff/mKL3/5ah9/+dpvf/Smjy/x1B7/8jR/D/I0fv/yNH7/8jR/D/I0jz/yRK + 9/8jR+//ITrR/xslov8WFX7/FBBy/xMPb/8TEG7PHhqoVyUe5FQGANtjjJHxuf////9sbG3/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/CAgI/Kampv7Kysv/xMTEYMTExADExMQAxMTEAMTE + xADExMQAxMTEAMTExADExMQAxMTEAMTExADExMQAxMTEAMTExADExMQAxMTEAMTExADExMQAJh7fACYf + 3wAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAI0bvACNH7wAjR+8AI0bvACNG + 7wAjRu8AP0DkAD9A5AA/QOQAP0DkAD9A5AA/QOQAP0DkAD9A5AA/QOQAP0DkAD9A5AA/QOQAP0DkAD9A + 5AA/QOReZ2vr/4uT8f+ztfb/wcf5/9DT+//Z2v3/2Nj9/+Df/v+vtvb/KSbh/yMc4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYe + 4P8mH+DWJh/gFyYf4ACdqvgAlKD3AJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlp + ZwBoaGcdZ2dmW2xra5lzc3W8fX1/5IWGjPeOj5f9kZKa/omKkPmGhov2f3+D6nh4etFycnOzbGxtmWlp + Z3lnZ2ZJZ2dmJ2lpaBJubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlp + aABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn + 2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AMJh/gRiYf + 4J0mH+DiJh/g/yYf4P8mHuD/Jh7f/yYf4P8mHt//Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYe4P8mHuD/Jh7g/yYe4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH9//JBzg/x4Z + 3/+Wn/H/4OT9/4KL7/8YD93/Lyzh/7zD9/+7wvf/LzDi/yIk4v8lM+j/JD7r/yRF7v8jSe//I0nw/yJH + 8P8eQ+//HEHv/yZL8P9AYfH/Z3/0/4WW9v+Xovf/oKn3/1x38/8cQu//I0bv/yNH7/8jRvD/I0fw/yNG + 8P8jR+//I0fx/yRK9f8kSfT/Ij/d/xwssf8ZIpuoFBN4Dx4aqAAlHuQABgDbAOzr/ab+/v7/VFVV/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/woKCrCrqqvNysrL/8PDxIbDw8QAw8PEAMPD + xADDw8QAw8PEAMPDxADDw8QAw8PEAMPDxADDw8QAw8PEAMPDxADDw8QAw8PEAMPDxADDw8QAw8PEAMPD + xAAmH98AJh7fACYf4AAmH+AAI0fvACNH7wAjRvAAI0fwACNG7wAjR+8AI0fvACNG7wAjR+8AI0fvACNG + 7wAjRu8AI0bvALS99gC0vfYAtL32ALS99gC0vfYAtL32ALS99gC0vfYAtL32ALS99gC0vfYAtL32ALS9 + 9gC0vfYAtL32etjZ/f/Y2P3/1dX8/9PT/P/R0Pv/z8/7/8/P+//T0vv/wcT5/zMu4v8iGt//Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mHt//JR7f/yYf4P8mH+D/Jh7f/yYe3/8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf + 4P8mH+DzJh/gPSYf4AAmH+AAnar4AJSg9wCUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZm + YgBpaWcAaGhnAGdnZgBsa2sAampoBWloZxloaGYpaGhmP2dnZkRnZ2YuZ2dmJWhoZh1paWgQcnJzAGxs + bQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9v + cABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJo + egAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gGCYf4F4mH+CuJh7g8yYf3/8mH+D/Jh7f/yYf4P8mH9//Jh/f/yYf4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mHt//Jh/f/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8mHuD/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf3/8mH+D/Jh7g/yYf4P8mH+D/Ixvf/xcQ + 3f9mb+r/2uD8/6ev9P8nI+D/GBLd/4uT8f/e4/3/bnXs/x0U3v8mHd//Jhzf/yYf4P8mJOL/JS/l/yU5 + 6f8kQ+3/I0jw/yNJ8P8hRvD/HkLv/x1D7/8pTfD/PF3x/0lp8/8uUPD/IUXw/yNG7/8jR/D/I0bv/yNH + 8P8jR/D/I0fv/yNH7/8jR+//I0fx/yNJ8/8kSvb/JEjx5CJC5p8gQutFJEXvAc7Z/AL////T8vLy/zMz + M/8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP89PT1MyMjIm8XFxv/Dw8ShxMPEAMTD + xADEw8QAxMPEAMTDxADEw8QAxMPEAMTDxADEw8QAxMPEAMTDxADEw8QAxMPEAMTDxADEw8QAI0fvACNG + 7wAjR/AAI0fvACNH7wAkR/AAI0fwACNH7wAjR+8AI0bwACNH8AAjRu8AI0fvACNH7wAjRu8AI0fvACNH + 7wAjRu8AI0bvACNG7wDU0/wA1NP8ANTT/ADU0/wA1NP8ANTT/ADU0/wA1NP8ANTT/ADU0/wA1NP8ANTT + /ADU0/wA1NP8ANTT/H/Pz/v/z8/7/8/P+//Pz/v/z8/7/8/P+//Pz/v/0dD7/9HS+/9ESOX/IBjf/yYe + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mHt//Jh/g/yYe4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/gcSYf4AAmH+AAJh/gAJ2q+ACUoPcAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ + 0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJy + cwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGho + ZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5s + YQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4CkmH+B4Jh/gxSYf4PwmH+D/JR7f/yYe3/8mH+D/Jh/g/yYe + 4P8mH+D/Jh7g/yYf4P8mHuD/Jh7f/yYf3/8mHuD/Jh7f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/f/yYf4P8mH+D/Jh/g/yYf4P8mH9//Jh/g/yYf4P8kHOD/HBPe/yEe + 3/9ze+z/1tv7/6+39f8vLeH/FhHd/3qD7f/c4fz/oar0/ygm4P8jG9//Jh/g/yYf4P8mHuD/Jh7g/yYd + 3/8mHt//JiLg/yUq5P8lNun/JUHs/yNI7/8jSPD/IUfw/x5E7/8eQu//IUTv/yNH8P8jR+//I0fv/yNH + 7/8jR/D/I0bv/yNG7/8jR+//I0fv/yNG7/8jR+//I0fw/yNH8f8jSPL/I0jx/ho/8LxCYPFx/v//8OXk + 4/8YGBn/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADKZmZmBtfX137DwsP/xMTEycTD + xAnEw8QAxMPEAMTDxADEw8QAxMPEAMTDxADEw8QAxMPEAMTDxADEw8QAxMPEAMTDxADEw8QAxMPEACNH + 7wAjRu8AI0fwACNH7wAjR+8AJEfwACNH8AAjR+8AI0fvACNG8AAjR/AAI0bvACNH7wAjR+8AI0bvACNH + 7wAjR+8AI0bvACNG7wAjRu8AqbL2AKmy9gCpsvYAqbL2AKmy9gCpsvYAqbL2AKmy9gCpsvYAqbL2AKmy + 9gCpsvYAqbL2AKmy9gCpsvaT0dH7/8/P+//Pz/v/z8/7/9DP+//Q0fv/1tX8/9zb/f/R1fz/Q0fl/yAY + 3/8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mHt//Jh/g/yYf4P8lHt//Jh/g/yYf + 4P8mH+D/Jh/fmyYf4AAmH+AAJh/gACYf4ACdqvgAlKD3AJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y + 5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlp + aABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdn + ZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZm + ZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AYmH+A5Jh/gkCYf4NwmHuD/Jh/g/yYf + 4P8mH+D/Jh7g/yYd3/8mHuD/JR7g/yUd3/8lHd//Jh7g/yUd3/8mHuD/Jh7g/yYe3/8lHd//JR3f/yUd + 3/8lHd//JR7g/yUd3/8lHd//JR7f/yUd4P8kHOD/JB3g/yQd4P8kHOD/Ihng/x4U3/8bFN7/JiPg/1RZ + 6P+osPT/1tz8/46U8P8kIuD/NTXj/5qh8v/Z4Pz/n6j0/zIx4v8gGN//Jh/g/yYf4P8mH+D/Jh7g/yYe + 4P8mHuD/Jh7f/yYd4P8mHd//Jh3f/yYg4P8mKOP/JTTo/yRB7P8jR+//I0nw/yNH8P8jR/D/I0fv/yNG + 7/8jR+//I0fv/yNG7/8jR+//I0fw/yNH8P8jRu//I0fw/yNH7/8jR+//I0fv/yNH7/8cQe//QmTy//7/ + ///Pz83/BQUF/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAAY2ZmZgDW1tdkw8PD/8TE + xOrExMQXxMTEAMTExADExMQAxMTEAMTExADExMQAxMTEAMTExADExMQAxMTEAMTExADExMQAI0fvACNG + 7wAjR+8AI0bvACNH8AAjR+8AI0fvACRH8AAjR/AAI0fvACNH7wAjRvAAI0fwACNG7wAjR+8AI0fvACNG + 7wAjR+8AI0fvACNG7wAjRu8AI0bvAMPG+QDDxvkAw8b5AMPG+QDDxvkAw8b5AMPG+QDDxvkAw8b5AMPG + +QDDxvkAw8b5AMPG+QDDxvkAw8b5oNDQ+//Q0Pv/09L8/9nY/f/X2Pz/zc/7/6yy9v+EiO//VVnn/yci + 3/8lHd//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/JR7f/yYf4P8mH+D/Jh/f/yYe + 4P8mHuD/Jh7gySYe3xEmHt8AJh7fACYe3wAmHt8AJh7fAJSg9wCUn/cAlKD3AJWg9wCUn/cAlaD4AJej + /wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGho + ZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdn + ZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZn + ZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AQJh/gUiYf + 4KQmHuDqJh7g/yEa3/8dGN7/Hxrf/x8Z3/8fGd7/Hxrf/x8a3/8fGt//Hxne/x8a3/8fGt7/Hhne/yAa + 3/8iHd//Ix3g/yQe4P8jHt//JB7f/yMd3/8mIOD/KSPg/ygi4P8rJOH/Lynh/zs/4/9YWuj/gYbu/663 + 9f/ByPj/m6Py/1Rd5/9CR+T/hYzu/8fR+f+zvPb/YWjq/ycj4P8hGN//Jh/f/yYe4P8mHuD/Jh/g/yYe + 4P8mHuD/Jh/g/yUe3/8mHuD/Jh/g/yYf4P8mHeD/Jhzf/yYd3/8mH9//Jijj/yU06P8kQOv/I0fv/yNJ + 8P8jSPD/I0fv/yNG7/8jR+//I0fv/yNH8P8jR/D/I0bv/yNH7/8jR/D/I0fv/yNH7/8jRu//Gz/u/1R3 + 9P//////m5ub/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAAyAAAAArS0tIA0tLSQsTE + xPvExMT9xMTELcTExADExMQAxMTEAMTExADExMQAxMTEAMTExADExMQAxMTEAMTExAAjR+8AI0fvACNH + 7wAjRu8AI0fvACNG7wAjR/AAI0fvACNH7wAkR/AAI0fwACNH7wAjR+8AI0bwACNH8AAjRu8AI0fvACNH + 7wAjRu8AI0fvACNH7wAjRu8AI0bvACNG7wDHy/oAx8v6AMfL+gDHy/oAx8v6AMfL+gDHy/oAx8v6AMfL + +gDHy/oAx8v6AMfL+gDHy/oAx8v6AMfL+p/b2v3/0tX8/8DD+f+WnfL/bW/r/0JD5P8nI+D/HBff/x8W + 3/8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh7g/yYe4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g4CYe4CQmHt8AJh7fACYe3wAmHt8AJh7fACYe3wCUoPcAlJ/3AJSg9wCVoPcAlJ/3AJWg + +ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdn + ZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGho + ZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpq + agBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe + 3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJRzgHCYh4GVda+m5go3u+H6L7v9/jO7/fYnt/3uI7f98iO3/e4jt/3uI7f97iO3/fInt/3+L + 7v+Fku7/kp/y/46c8f+MmvD/jJrx/4yY8f+KlvH/lqDy/5qk8v+WoPL/naPz/6ip9f+VnvH/j57x/42W + 8P92e+z/ZXTq/3l/7f+hqvP/xs75/6u09f9iaOn/KSbh/x0V3/8kHOD/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYe3/8mH+D/Jh7g/yYd3/8mHd//JiDg/yUp + 4/8lNej/JEHs/yNH7/8jSfD/I0jw/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0fw/yNH7/8jRu//I0bv/xs/ + 7v9UdvT//////4mJif8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/QAAAFYAAAAAzs7MAM7O + zB7Ew8Tqw8PE/8PDxFbDw8QAw8PEAMPDxADDw8QAw8PEAMPDxADDw8QAI0fwACNH8AAjR+8AI0fvACNH + 7wAjR+8AI0bvACNH7wAjRu8AI0fwACNH7wAjR+8AJEfwACNH8AAjR+8AI0fvACNG8AAjR/AAI0bvACNH + 7wAjR+8AI0bvACNH7wAjR+8AI0bvACNG7wAjRu8AeoruAHqK7gB6iu4AeoruAHqK7gB6iu4AeoruAHqK + 7gB6iu4AeoruAHqK7gB6iu4AeoruAHqK7gB6iu6fhInv/1RZ5/8yLuH/IRzf/xwV3/8hGN//JBzg/yYe + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf + 4P8lH+D/Jh/g8SYf4EQmHuAAJh7fACYe3wAmHt8AJh7fACYe3wAmHt8AlKD3AJSf9wCUoPcAlaD3AJSf + 9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdn + ZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5v + bgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhn + ZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe + 4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/DBzeux7Ulfnyl9l6f1jaer/ZWvq/2hv6v9pb+v/aG7r/2tx + 6/96fu3/e3/t/3p+7f9tc+v/Zmzq/2ht6/9veuz/d4nt/3OF7P9jder/boHs/2x37P9tcuz/dnrs/4KK + 7/+JmfD/pa30/7a99/+krfX/fILu/0dL5v8lIuD/Hhbf/yQb4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYe + 3/8mHd//Jh3f/yYh4P8lKuT/JDjp/yRD7v8jSfH/I0nw/yNI7/8jRu//I0fw/yNH7/8jR/D/I0fw/yNH + 8P8bQO//VHb0//////+trKz/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wIDB7kAAAAAJEv/AMnI + wgDJyMIIxcTE18PDxP/ExMR+xMTEAMTExADExMQAxMTEAMTExAAjR+8AI0fwACNH8AAjR/AAI0fvACNH + 7wAjR+8AI0fvACNG7wAjR+8AI0bvACNH8AAjR+8AI0fvACRH8AAjR/AAI0fvACNH7wAjRvAAI0fwACNG + 7wAjR+8AI0fvACNG7wAjR+8AI0fvACNG7wAjRu8AI0bvACUh4AAlIeAAJSHgACUh4AAlIeAAJSHgACUh + 4AAlIeAAJSHgACUh4AAlIeAAJSHgACUh4AAlIeAAJSHgoR0X3/8eFN//Ixrf/yMc3/8jG9//IRnf/yAW + 3/8eFN7/IBff/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYe32ImH+AAJh7gACYe3wAmHt8AJh7fACYe3wAmHt8AJh7fAJSg9wCUn/cAlKD3AJWg + 9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdn + ZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGho + aABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGho + ZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf + 4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ASBgu48yMr5kqes9N2Sl/D/k5jx/5SZ + 8f+TmfH/kpfx/5GX8f+Rl/H/k5nx/5SZ8f+Di+//g43v/4SQ7/+Fke//h5Pv/4SQ7/97iO3/f43u/32B + 7f92dez/X2Pp/0dM5f8zMOL/JB/g/xwW3v8gGN//JR3f/yYe3/8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf + 3/8mH+D/Jh/g/yYf4P8mHuD/Jh3f/yYd3/8mIuH/JS7l/yQ86/8kRu//I0nw/yNH8P8jR+//I0fw/yNH + 7/8jR/D/Gz/v/1N28///////5+bl/xkZGv8AAAD/AAAA/wAAAP8AAAD/AAAA/wUGCf8gOrDqJUz/jCRL + /y0gQ/AAycjCAM/NwbHExMT/xMTEnsnHwQDJx8EAycfBAGqG9QAZPu8AI0fvACNH8AAjR/AAI0fwACNH + 7wAjR+8AI0fvACNH7wAjRu8AI0fvACNG7wAjR/AAI0fvACNH7wAkR/AAI0fwACNH7wAjR+8AI0bwACNH + 8AAjRu8AI0fvACNH7wAjRu8AI0fvACNH7wAjRu8AI0bvACNG7wAlHeAAJR3gACUd4AAlHeAAJR3gACUd + 4AAlHeAAJR3gACUd4AAlHeAAJR3gACUd4AAlHeAAJR3gACUd4JMiGd//HhXf/xwV3v8jIN//Lyrh/z5A + 5P9QV+f/Y2fq/1FX5/8lIOD/JR7f/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh7g/yYf4H4mHt8AJh/gACYe4AAmHt8AJh7fACYe3wAmHt8AJh7fACYe3wCUoPcAlJ/3AJSg + 9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGho + ZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdn + ZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdn + ZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe + 3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAMIBrfUCkj + 4J4pJODnKiTh/yok4f8pI+D/KSPg/yok4f8pJOH/JSDg/yQg4P8kH+D/JB/g/yQf3/8iHd//Hxrf/x8Z + 3/8cFd7/GxLe/x4V3v8hF9//Ixvg/yQd3/8lHt//Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mH9//Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yUf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mH9//Jh7g/yYf4P8mH+D/Jh7g/yYc3/8mHuD/JSbi/yU05/8jQe3/I0jw/yNI + 8P8jR/D/I0fv/xtA7/9TdvP//v////////9hYWL/AAAA/wAAAP8AAAD/AAAA/wAAAP8YJmn/Jkz+/yNH + 8f8jR+/0IEPwpyRH7zWts8uKzszB/8PDxL/Jx8EEycfBAMnHwQBqhvUAGT7vACNH7wAjR/AAI0fwACNH + 8AAjR+8AI0fvACNH7wAjR+8AI0bvACNH7wAjRu8AI0fwACNH7wAjR+8AJEfwACNH8AAjR+8AI0fvACNG + 8AAjR/AAI0bvACNH7wAjR+8AI0bvACNH7wAjR+8AI0bvACNG7wAjRu8AIxvfACMb3wAjG98AIxvfACMb + 3wAjG98AIxvfACMb3wAjG98AIxvfACMb3wAjG98AIxvfACMb3wAjG99qNzbj/1pg6P+Ehe//m6Tz/7q+ + +P/Nz/v/0NP7/9zd/f+os/X/JyTg/yQc4P8mH+D/Jh/g/yYe4P8mH9//Jh/g/yYf4P8mH+D/Jh7g/yYe + 4P8mH+D/Jh/g/yYf4JYmH+AAJh7fACYf4AAmHuAAJh7fACYe3wAmHt8AJh7fACYe3wAmHt8AlKD3AJSf + 9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGho + ZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdn + ZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdn + ZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe + 3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa + 3wApI+AAJB3gFiQd4F8kHd+vJB3f8CQc3/8kHeD/JBzg/yUd3/8lHuD/JR3g/yUd4P8lHeD/JR3f/yUe + 4P8lHuD/Jh7f/yYe4P8mH+D/Jh/g/yYf4P8mHt//Jh7g/yYf4P8mH+D/Jh7f/yYf4P8mHt//Jh7f/yYf + 4P8mH+D/Jh7f/yYf4P8mH+D/Jh7f/yYe4P8lH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7f/yYd3/8mHd//JiDg/yUr + 5P8kOur/I0Xv/yNJ8P8bQe//VHbz//z+////////tLS1/wAAAf8AAAD/AAAA/wAAAP8LDyD/JUbe/yRI + 9v8jR+//I0fw/yNH7/8bQPH3TGvl5LC3yv/IxsDnqbPPCxtE8wCjufoAaob1ABk+7wAjR+8AI0fwACNH + 8AAjR/AAI0fvACNH7wAjR+8AI0fvACNG7wAjR+8AI0bvACNH8AAjR+8AI0fvACRH8AAjR/AAI0fvACNH + 7wAjRvAAI0fwACNG7wAjR+8AI0fvACNG7wAjR+8AI0fvACNG7wAjRu8AI0bvAE5S5wBOUucATlLnAE5S + 5wBOUucATlLnAE5S5wBOUucATlLnAE5S5wBOUucATlLnAE5S5wBOUucATlLnPb7D+PvY2f3/2tv9/9nX + /f/T0/v/0dH7/9DQ+//V1Pz/ur74/y8r4f8jHOD/Jh7g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/Jh7g/yYf4LAmH+AJJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGlo + ZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGho + aABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpq + aQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S + 8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4CMmH+BwJh/gwSYe3/UmH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe + 3/8mH9//Jh/g/yYf4P8mHt//Jh7g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf3/8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf3/8mH9//Jh7g/yYf4P8lH9//Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe + 4P8mHd//Jh3f/yUl4f8lMuf/HDrr/0tu8//6/P////////Pz8/8xMTL/AAAA/wAAAP8DAgD/Hzad/yVL + //8jRu//I0fv/yNG7/8jRu//I0fv/xtA8f8uUu3/2tzk+5Su8mgbRPMKo7n6AGqG9QAZPu8AI0fvACNH + 8AAjR/AAI0fwACNH7wAjR+8AI0fvACNH7wAjRu8AI0fvACNG7wAjR/AAI0fvACNH7wAkR/AAI0fwACNH + 7wAjR+8AI0bwACNH8AAjRu8AI0fvACNH7wAjRu8AI0fvACNH7wAjRu8AI0bvACNG7wCHmPAAh5jwAIeY + 8ACHmPAAh5jwAIeY8ACHmPAAh5jwAIeY8ACHmPAAh5jwAIeY8ACHmPAAh5jwAIeY8Aq3vffN1NP8/8/P + +//Pz/v/z8/7/8/P+//Pz/v/0ND7/87Q+/8/QeT/IRnf/yYe4P8mHuD/Jh/g/yYe4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4MUmHt8TJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpq + aABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5u + bwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBw + cgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf + 4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK + +QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AImH+A1Jh7gfiYe4M8mHuD8Jh/g/yUf + 3/8mH+D/Jh/f/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHt//Jh7g/yUe4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/JR7f/yYf4P8mH+D/Jh7g/yYf4P8mH9//Jh7f/yYf + 4P8mH+D/Jh/f/yYf4P8mHeD/Jhzf/x8a3/9BR+f/9/j+////////////d3d4/wAAAP8AAAD/Ex9P/yVL + +f8jR/L/I0fw/yNG7/8jR+//I0bv/yNH7/8hRe//Ikfw/+zu///c5/7/LVLwyqO5+l5qhvUKGT7vACNH + 7wAjR/AAI0fwACNH8AAjR+8AI0fvACNH7wAjR+8AI0bvACNH7wAjRu8AI0fwACNH7wAjR+8AJEfwACNH + 8AAjR+8AI0fvACNG8AAjR/AAI0bvACNH7wAjR+8AI0bvACNH7wAjR+8AI0bvACNG7wAjRu8Ah5jwAIeY + 8ACHmPAAh5jwAIeY8ACHmPAAh5jwAIeY8ACHmPAAh5jwAIeY8ACHmPAAh5jwAIeY8ACHmPAAnarzgdXU + /P/Pz/v/z8/7/8/P+//Pz/v/0dD7/9bV/P/Y2v3/R07l/yAX3v8mHuD/Jh7f/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4MwmH+AeJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxr + awBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlp + aABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGho + ZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf + 4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC + 7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAGJh7gQyYf + 4JAmH+DYJh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYe4P8mH+D/Jh/g/yUe3/8lHuD/Jh/g/yYe + 3/8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yUe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yUe3/8lH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mHt//JR/f/yYf4P8mH+D/Jh/g/yYe4P8fFd//T1Xl//r9/v///////////769vv8AAAD/BgsV/yRE + 0/8jSPn/I0bv/yNG7/8jRu//I0fv/yNH7/8jR+//IETv/yZJ8P/r7f7/2eT+/zJV8f+4yPv/aYX1yRk+ + 710jR+8KI0fwACNH8AAjR/AAI0fvACNH7wAjR+8AI0fvACNG7wAjR+8AI0bvACNH8AAjR+8AI0fvACRH + 8AAjR/AAI0fvACNH7wAjRvAAI0fwACNG7wAjR+8AI0fvACNG7wAjR+8AI0fvACNG7wAjRu8AI0bvAISQ + 7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ + 7yXHyPrr1dT8/9LS/P/W1fz/2dn9/9PV+/+4vPf/goju/y0q4f8kG+D/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/Jh/g/yYf4NomH+AiJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdn + ZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdn + ZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdm + ZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ + 3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh7gECYf4FEmHuCdJh/g4CYf4P8mH+D/Jh7f/yYe3/8mH+D/Jh7g/yUf3/8lH9//Jh/f/yYe + 4P8mHt//Jh/g/yYf3/8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8lH9//Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8lHuD/JR/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7f/yUf4P8mH+D/Jh/g/yYf4P8mHuD/HhXf/1Vg5v/8//7////////////j4+P/GhkS/xco + h/8mTP//I0jw/yNH8P8jR+//I0fw/yNH8P8jR/D/I0fw/yFF8P8lSPD/7e/+/8TU/P8tUfH/v838/159 + 9P8aPu7/I0fvyCNH8FwjR/AEI0fwACNH7wAjR+8AI0fvACNH7wAjRu8AI0fvACNG7wAjR/AAI0fvACNH + 7wAkR/AAI0fwACNH7wAjR+8AI0bwACNH8AAjRu8AI0fvACNH7wAjRu8AI0fvACNH7wAjRu8AI0bvACNG + 7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ + 7wCEkO8Ag4nvmtLW+//HzPr/tLj3/4eN7/9TVOf/King/xwU3v8kHN//Jh7g/yYe4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh7g/yYf4NsmH+AoJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGho + ZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdn + ZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZm + ZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd + 4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN6 + 7AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4BgmH+BXJh/gqSYf3+YmHuD/Jh/g/yYe4P8mH+D/Jh/f/yYf + 3/8mH+D/Jh/g/yYe4P8lHt//Jh/g/yYe3/8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yUe + 3/8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe3/8lH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/x8X3/9IR+X/+fv+///////////////8/2Jl + jf8aFNX/JSnn/yQ56f8jRu7/I0nw/yNI8P8jR/D/I0bv/yNG8P8hRe//Jkrw/+3w/v+/0Pv/L1bx/7rL + /P9QcvT/HEDv/yNH7/8jR/D/I0fwvCNH8EwjR+8AI0fvACNH7wAjR+8AI0bvACNH7wAjRu8AI0fwACNH + 7wAjR+8AJEfwACNH8AAjR+8AI0fvACNG8AAjR/AAI0bvACNH7wAjR+8AI0bvACNH7wAjR+8AI0bvACNG + 7wAjRu8ALCrhACwq4QAsKuEALCrhACwq4QAsKuEALCrhACwq4QAsKuEALCrhACwq4QAsKuEALCrhACwq + 4QAsKuEALCrhACwq4TJHR+X2QUPk/ycm4P8cFN7/HBTe/yMb4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe4NgmHuAoJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlp + ZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlp + ZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlp + aABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn + 2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre + /ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AgJh7gXyYe4K4mH+DrJh/g/yYe + 3/8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYe4P8lHt//Jh/g/yUf + 3/8mH+D/Jh/g/yYe4P8mH+D/Jh7f/yYe4P8mH+D/Jh7g/yYf4P8mH9//JR/g/yYe4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe3/8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8iGuD/MS7i/9vj+v////////////// + //+QnPX/GBHg/yYc3v8mHd7/JSXh/yU06P8jQ+3/I0nw/yNI8P8jR+//HkLw/zBY8f/3+v//rsD6/zdf + 8v+6zPv/Qmfz/x5B7/8jR+//I0fv/yNH8P8jR+//I0fvryNH7zcjR+8AI0fvACNG7wAjR+8AI0bvACNH + 8AAjR+8AI0fvACRH8AAjR/AAI0fvACNH7wAjRvAAI0fwACNG7wAjR+8AI0fvACNG7wAjR+8AI0fvACNG + 7wAjRu8AI0bvACwq4QAsKuEALCrhACwq4QAsKuEALCrhACwq4QAsKuEALCrhACwq4QAsKuEALCrhACwq + 4QAsKuEALCrhACwq4QAsKuEAHhXfnh8W3v8kHOD/Jh7g/yYf4P8mH+D/Jh7f/yYe3/8mH+D/Jh/g/yYe + 4P8mHt//Jh7g/yUe38kmHuAgJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZm + YgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxs + bQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9v + cABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJo + egAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r + 6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gKSYf + 4GkmHuC3Jh7g7CYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHt//Jh7f/yYf3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8lHt//Jh7f/yYf3/8mH+D/Jh/g/yYe4P8mH+D/JR3g/xwW3v+epfH///////// + ////////aW/r/xkR3f8mHuD/Jh7g/yYd3/8mG97/JSHg/yUw5f8kQO3/I0nw/xM57/9lhPX//////5+x + +P9AZvP/vM38/zVZ8P8gQ+//I0fv/yNH7/8jR+//I0fw/yNH8P8jR/D1I0fvkCNH7x8jRu8AI0fvACNG + 7wAjR/AAI0fvACNH7wAkR/AAI0fwACNH7wAjR+8AI0bwACNH8AAjRu8AI0fvACNH7wAjRu8AI0fvACNH + 7wAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4BQmH+CuJh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4KomH+AYJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ + 0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJy + cwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGho + ZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5s + YQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh + 4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYe3yomH+BiJh/grCYf4N0mH+D/JR7f/yYe4P8mH+D/Jh/g/yYf3/8mH+D/Jh/g/yYe + 3/8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yUf3/8mH+D/Jh/f/yUe3/8mH9//Jh/g/yYe4P8mH9//JR7f/yUe + 3/8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8lHt//JR/g/yYf3/8fFt7/Pj/j/+bu + /P//////2OL6/ysr4P8iGd//JR/g/yUf3/8lH+D/JR/f/yUe3/8mHN//Jx/g/xkf4/86WO7/4+r+//// + //95k/f/U3Hz/77N/P8rTvD/IUXv/yNG7/8jRu//I0bv/yNH7/8jRvD/I0bv/yNH7/8jR+/gI0bvZSNH + 7wQjRu8AI0fwACNH7wAjR+8AJEfwACNH8AAjR+8AI0fvACNG8AAjR/AAI0bvACNH7wAjR+8AI0bvACNH + 7wAjR+8AI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gBiYe4GAmH+C7Jh/g6CYf4P4mH+D/Jh/g/yYf + 4P8mH+D1Jh7g0yYf4G8mH+AFJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y + 5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlp + aABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdn + ZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZm + ZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc + 4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AZJh/gRyUe34smHuDHJh/g9SYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/f/yYf3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH9//Jh/g/yUf + 4P8lHt//Jh/g/yYf4P8mHuD/Jh/g/yYf3/8mH9//Jh7g/yYe4P8mH+D/JR/f/yUf4P8mH+D/Jh/g/xwV + 3/9ZYOj/u7z1/1pf6P8cFN7/Jh/g/yUf3/8mHuD/Jh7f/yYf4P8mH+D/JiDf/xsR3v8yM+H/ztX4//// + ///u9P3/MFXw/4ae9/+uwvv/JUrw/yFF7/8jR+//I0bv/yNG8P8jR/D/I0fw/yNH7/8jR/D/I0fw/yNH + 7/8jR/CyI0bvLCNH8AAjR+8AI0fvACRH8AAjR/AAI0fvACNH7wAjRvAAI0fwACNG7wAjR+8AI0fvACNG + 7wAjR+8AI0fvACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gASYf4CUmHuBCJh/gRyYf + 4EcmH+BGJh/gNCYf4BImH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej + /wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGho + ZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdn + ZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZn + ZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gByYf4DcmH+B4Jh/gvCYf + 4PImH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yUe4P8mH+D/JR7f/yUf + 4P8mHuD/Jh/g/yYf4P8mHuD/Jh7f/yYf4P8mH9//Jh/f/yYe3/8mH9//Jh/g/yYf4P8mH9//Jh/g/yYf + 4P8mHt//Hhbf/xsU3v8eFt7/Jh/f/yYe3/8mHuD/Jh/g/yYf4P8mH+D/Jh/g/xkQ3v8xNeH/zdb4//// + ////////b3bq/0NP5//L1vv/l7D5/yBI8P8iR/D/I0fv/yNH7/8jR/D/I0fw/yNH8P8jR/D/I0bw/yNG + 8P8kRu//I0bw/yNG8OgjR/BpI0fvACNH7wAkR/AAI0fwACNH7wAjR+8AI0bwACNH8AAjRu8AI0fvACNH + 7wAjRu8AI0fvACNH7wAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg + +ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdn + ZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGho + ZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpq + agBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe + 3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd + 3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf + 4AMmHuAvJh7gaiYf4LUmH+DvJh/g/yYe4P8mHt//Jh/g/yYe4P8mHuD/Jh/f/yYf4P8mHt//Jh7g/yUe + 4P8lH9//Jh/f/yYf4P8mH+D/Jh/g/yUe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/f/yYf + 4P8mH+D/Jh/g/yYe4P8lHuD/JR/f/yYe3/8mH+D/Jh/g/yYf3/8mHuD/IBff/xQM3f8/ROT/z9n4//// + ///5/v7/eoDt/zA44v+3vfb/2978/3V+7f8bLef/I0Tu/yNK8P8jSPD/I0fw/yNH7/8jR+//I0bv/yNG + 8P8jR+//JEfw/yNG7/8jRu//I0fw/yNH76QjR+8bJEfwACNH8AAjR+8AI0fvACNG8AAjR/AAI0bvACNH + 7wAjR+8AI0bvACNH7wAjR+8AI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf + 9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdn + ZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5v + bgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhn + ZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe + 4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd + 4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh7gKyYf4GMmH+CuJh/f6iYf4P8mHuD/Jh7g/yYe3/8mHuD/Jh7f/yYf + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mHt//Jh7f/yQa3/8bE97/FxLe/zM14v+Jke//8PP9//// + ///h5vv/XWXo/zk95P+xuPb/2t38/6y29f8wL+H/Ixje/yYj4f8lM+f/I0Pu/yNK8f8jSfH/I0fv/yNH + 8P8jR+//I0fv/yNH7/8kRvD/I0fv/yNH7/8jR/D/I0fv1iRH8EgjR/AAI0fvACNH7wAjRvAAI0fwACNG + 7wAjR+8AI0fvACNG7wAjR+8AI0fvACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe + 3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg + 9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdn + ZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGho + aABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGho + ZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf + 4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd + 4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4CQmH+BhJh/gqyYf4OMmH+D/Jh7g/yUe + 3/8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mHt//Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/JR7g/yMa4P8eFd7/GBDe/xkR3v8kJOD/VFjo/56o8v/s8v3//////+Pn + +/+SmfD/RE3k/2Zu6v/ByPj/3N/8/7O89v87P+P/Hxff/yYf4P8mHd//Jh3f/yYh4P8lMeb/JELt/yNJ + 8f8jSfD/I0fv/yNG7/8jRu//JEbw/yNH8P8jR+//I0bv/yNH8P8jR+/3I0fwhiNH7wwjR+8AI0bwACNH + 8AAjRu8AI0fvACNH7wAjRu8AI0fvACNH7wAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf + 4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg + 9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGho + ZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdn + ZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdn + ZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe + 3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj + 4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AdJh/gVyYe + 4KUmH+DkJh/g/yUd3/8eFd7/Fw/e/xcP3v8XD97/Fw/e/xcP3v8XD97/Fg/d/xcP3v8XD97/Fw/e/xcP + 3v8XD97/FxDe/xgS3v8ZE97/HBXe/x8X3/8sLOH/Rknl/3Bz6/+krvP/2+D6//n+///8////ydL4/4uc + 7/9ncuv/d4Dt/7W79v/b4Pz/0tj7/6Kr9P88P+P/HRbf/yYf4P8mHt//Jh7g/yYf4P8mHuD/Jhzf/yYh + 4P8lL+b/JEHs/yNJ8f8jSfD/I0fw/yNG7/8jRu//I0fw/yNH7/8jR+//I0bv/yNG7/8jR/DCI0fvNCNG + 8AAjR/AAI0bvACNH7wAjR+8AI0bvACNH7wAjR+8AI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf + 9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGho + ZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdn + ZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdn + ZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe + 3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa + 3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe + 3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh/gHCUe31cmIuChS07m4m5r6v94c+z/enXs/3Vw6/91b+v/dG/r/3Nv6/9ybuv/cm7r/3Ju + 6/9ybuv/cm3r/3Fv6v99h+z/i5fv/56p8f+5vvX/ztX4/93p+//c5vr/z9r5/8nS+P+1w/b/pLHz/6Cs + 8/+0vPb/z9b6/9Xa+/+3wPf/h47v/1BW5/8lI+D/IBjf/yYf3/8mH9//Jh7f/yYf4P8mHuD/Jh7g/yYe + 4P8mHd//Jhzf/yYg4f8lLub/JEHt/yNK8f8jSPD/I0fv/yNH7/8jR+//I0fv/yNH8P8jR/D/I0bv/yNG + 7+8jRvBuI0fwASNG7wAjR+8AI0fvACNG7wAjR+8AI0fvACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGlo + ZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGho + aABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpq + aQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S + 8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf + 4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89Bn///9UzNb4nKe39N69zff/3Ob8//z////s9P7/2+f7/9zo + +//d6Pv/3en7/93p+//Y5fv/1uH6/9jj+//F0vj/tMP2/6Ox9P+ktPT/p7b0/7O+9/+xuvb/rbb1/6q0 + 9f+irfP/mJ7z/3l+7v9NUeb/LSrh/x0Y3v8eFt//JBzg/yYf4P8mH+D/Jh7f/yYe4P8mH+D/Jh7g/yYe + 3/8mH+D/Jh/g/yYf4P8mHuD/Jhzf/yYg4P8lL+X/JEDs/yNJ8P8jSPD/I0fw/yNG7/8jR/D/I0fw/yNH + 8P8jRu//I0bv/yNH8K0jRu8kI0fvACNH7wAjRu8AI0fvACNH7wAjRu8AI0bvACNG7wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpq + aABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5u + bwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBw + cgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf + 4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK + +QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYVusT4TZOb8pxqb+vfb3Ps/290 + 7P9vc+z/b3Ts/2907P9vdOz/b3Ts/2907P9vdOz/cHXs/3J27P9la+r/XWTp/11k6v9OVeb/Qj/k/zYx + 4/8sKeH/JSTg/x8a3/8dFN7/Hxjf/yMa3/8lHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh3f/yYg4P8lLuX/JEDs/yNJ8P8jSPD/I0fw/yNH + 7/8jRu//I0fv/yNH7/8jR+//I0fv4SNH71wjR+8AI0bvACNH7wAjR+8AI0bvACNG7wAjRu8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxr + awBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlp + aABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGho + ZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf + 4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC + 7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf + 4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf + 4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndEx0V + 30cdFd+SHRXf2B0V3/8dFd//HRXf/x0V3/8dFd//HRXf/x0W3/8dFd//Hxbf/x8X3v8fFt//IBff/yEZ + 3/8iGuD/Ixvg/yQc4P8lHuD/Jh7f/yYe4P8mH+D/Jh7g/yYf3/8mH+D/Jh/g/yYe3/8lHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYe3/8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mHt//Jhze/yYf3/8lLuX/JEDs/yNJ + 8P8jSfH/I0fw/yNG7/8jRvD/I0fv/yNH7/8jR/D/I0fvmSNG7xYjR+8AI0fvACNG7wAjRu8AI0bvACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdn + ZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdn + ZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdm + ZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ + 3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf + 4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ + 3QAdFd8AHRXfACYf4A4mH+BFJh7gkiYe39omHuD/Jh7f/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh7f/yYf + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYe3/8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mHt//JR7g/yYe + 4P8mH+D/JR7f/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/JR/g/yYf4P8mHd//Jh3f/yYg + 4P8lLuX/JEDs/yNJ8P8jSPD/I0bw/yNH8P8jR+//I0fw/yNH8P8jR+/RI0fvOyNH7wAjRu8AI0bvACNG + 7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGho + ZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdn + ZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZm + ZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd + 4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN6 + 7AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe + 3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb + 8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8NJh/gRiYf4JMmH+DYJh/g/SYe4P8mH+D/Jh/g/yYe + 3/8lHt//JR7f/yYe3/8lHt//Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yUe3/8mHt//Jh/g/yYf4P8mHt//Jh7g/yYf4P8mH+D/Jh7f/yUf4P8mHuD/Jh7g/yYf + 4P8mHd//Jh3f/yYg4P8lMOX/JELt/yNJ8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH8PIjR+9eI0bvAyNG + 7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlp + ZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlp + ZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlp + aABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn + 2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre + /ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE + +ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fCiYf4D0mH+CKJh7g0yYe + 4P0mHuD/Jh/f/yYe3/8mHt//Jh/f/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh/f/yYf4P8mH+D/Jh/g/yYe + 3/8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH9//Jh/g/yYe + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh3f/yYg4P8lMub/JELt/yNI8P8jR/D/I0fv/yNH7/8jR+//I0fv+iNG + 74gjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZm + YgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxs + bQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9v + cABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJo + egAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r + 6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY + 5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe + 4AcmHuA9Jh/giSYf39MmH+D9Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mHt//Jh7f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe3/8mHd//Jhze/yYi4P8lNOj/I0Xu/yNJ8P8jR/D/I0fw/yNG + 7/8jR/DEI0fwACNH8AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ + 0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJy + cwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGho + ZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5s + YQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh + 4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW + +ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf + 4AAmHuAAJh7gACYf4AAmHt8HJh7gPCYf4IAmH+DKJh/g+iYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7f/yYe3/8mH+D/Jh7g/yYf3/8mHt//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe3/8mHuD/Jh/g/yYe4P8mHd//Jhzf/yUm4/8lOur/I0jw/yNI + 8P8jRvD/I0fwviNH8AAjR/AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y + 5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlp + aABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdn + ZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZm + ZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc + 4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP// + /wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fBCYe4DUmH+B9Jh/gySYe4PcmH+D/Jh/g/yYe + 3/8mHt//Jh7g/yYf4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8lH9//JR7f/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHeD/Jh7f/yUt + 5f8kRe7/I0jw/yNH8IojR/AAI0fwACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej + /wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGho + ZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdn + ZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZn + ZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW8 + 9AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe + 3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AQlHt8zJR7fdSYe + 4MEmHt/0Jh7f/yYf4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mHt//Jh7g/yYe + 4P8mHuD/JR/f/yYf3/8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mHuD/Jh7g/yYe + 4P8mG9//JTXo/yNJ8M4jRu8sI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg + +ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdn + ZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGho + ZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpq + agBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe + 3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd + 3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi + 4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf + 4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe + 3wAmHt8CJh/gLyYf4G4mHt+5Jh/g7iYf4P8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7f/yYe + 4P8mH+D/Jh/g/yYf4P8lHt//JR/f/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jhzf/yUw5sEjSvAxI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf + 9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdn + ZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5v + bgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhn + ZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe + 4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd + 4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe + 3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf + 4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe + 3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4CYmH+BkJh7grCYf4OMmH+D/Jh/f/yYe4P8mH+D/Jh7f/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/JR/g/yUe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh7g/yYc36klMeYKI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe + 3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg + 9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdn + ZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGho + aABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGho + ZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf + 4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd + 4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf + 4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe + 3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf + 4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAWJh7gUSYf4IEmHuDAJh/g5SYf + 4P8mH+D/Jh7g/yYe4P8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P0mH+DjJh/gtyYe31YlHN4BJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf + 4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg + 9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGho + ZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdn + ZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdn + ZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe + 3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj + 4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe + 4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf + 4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gAiYe + 4BwmHuBRJh/geCYf4LMmHuDYJh/g8CYe3/8lH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4OUmH+DFJh/glCYf + 4GwmH+BIJh7gFyYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf + 9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGho + ZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdn + ZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdn + ZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe + 3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa + 3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe + 3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf + 4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe + 4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe + 4AAmHuAAJh7gACYf4AAmH+AZJR7fOyUe328mH+CYJh/guCYe374mH+C+Jh7gvyYf4JwmH+BWJh/gJSYf + 4AsmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGlo + ZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGho + aABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpq + aQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S + 8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf + 4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf + 4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe + 3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpq + aABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5u + bwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBw + cgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf + 4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK + +QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V + 3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf + 4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxr + awBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlp + aABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGho + ZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf + 4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC + 7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf + 4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf + 4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V + 3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe + 4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdn + ZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdn + ZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdm + ZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ + 3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf + 4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ + 3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe + 3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe + 4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG + 7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGho + ZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdn + ZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZm + ZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd + 4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN6 + 7AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe + 3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb + 8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf + 4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG + 7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlp + ZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlp + ZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlp + aABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn + 2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre + /ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE + +ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe + 3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG + 7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZm + YgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxs + bQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9v + cABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJo + egAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r + 6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY + 5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe + 4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe + 3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf + 4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK + 8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ + 0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJy + cwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGho + ZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5s + YQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh + 4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW + +ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf + 4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf + 4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf + 4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx + 5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y + 5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlp + aABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdn + ZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZm + ZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc + 4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP// + /wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf + 4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe + 3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc + 3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej + /wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGho + ZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdn + ZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZn + ZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW8 + 9AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe + 3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe + 3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe + 3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe + 3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg + +ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdn + ZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGho + ZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpq + agBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe + 3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd + 3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi + 4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf + 4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe + 3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf + 4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf + 4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf + 9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdn + ZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5v + bgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhn + ZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe + 4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd + 4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe + 3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf + 4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe + 3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe + 3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg + 9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdn + ZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGho + aABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGho + ZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf + 4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd + 4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf + 4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe + 3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf + 4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf + 4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg + 9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGho + ZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdn + ZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdn + ZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe + 3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj + 4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe + 4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf + 4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf + 9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGho + ZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdn + ZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdn + ZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe + 3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa + 3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe + 3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf + 4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe + 4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe + 4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGlo + ZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGho + aABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpq + aQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S + 8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf + 4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf + 4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe + 3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpq + aABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5u + bwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBw + cgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf + 4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK + +QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V + 3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf + 4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxr + awBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlp + aABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGho + ZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf + 4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC + 7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf + 4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf + 4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V + 3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe + 4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdn + ZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdn + ZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdm + ZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ + 3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf + 4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ + 3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe + 3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe + 4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG + 7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGho + ZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdn + ZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZm + ZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd + 4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN6 + 7AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe + 3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb + 8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf + 4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG + 7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlp + ZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlp + ZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlp + aABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn + 2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre + /ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE + +ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe + 3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG + 7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZm + YgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxs + bQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9v + cABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJo + egAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r + 6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY + 5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe + 4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe + 3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf + 4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK + 8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ + 0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJy + cwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGho + ZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5s + YQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh + 4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAhYaJAIWGiQCCjLsAMi81ADIx + VAAODgwAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW + +ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf + 4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf + 4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf + 4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx + 5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y + 5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlp + aABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdn + ZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZm + ZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc + 4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwAAcGHAAHBhwAEyeOAIWGiQCFhokAgoy7ADIv + NQAyMVQADg4MAB88vQAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP// + /wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf + 4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe + 3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc + 3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej + /wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGho + ZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdn + ZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZn + ZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3AAjHMEAIxzBACMcwQAHBhwABwYcABMnjgCFhokAhYaJAIKM + uwAyLzUAMjFUAA4ODAAfPL0ABg4uAAMDEQAAAAAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW8 + 9AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe + 3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe + 3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe + 3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe + 3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg + +ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdn + ZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGho + ZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpq + agBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe + 3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJRzgACYh4AAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEABwYcAAcGHAATJ44AhYaJAIWG + iQCCjLsAMi81ADIxVAAODgwAHzy9AAYOLgADAxEAAAAAAJmn2QCZp9kAJh7gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi + 4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf + 4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe + 3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf + 4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf + 4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf + 9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdn + ZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5v + bgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhn + ZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe + 4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBAAcGHAAHBhwAEyeOAIWG + iQCFhokAgoy7ADIvNQAyMVQADg4MAB88vQAGDi4AAwMRAAAAAACZp9kAmafZAJmn2QAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe + 3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf + 4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe + 3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe + 3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg + 9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdn + ZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGho + aABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGho + ZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf + 4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAHBhwABwYcABMn + jgCFhokAhYaJAIKMuwAyLzUAMjFUAA4ODAAfPL0ABg4uAAMDEQAAAAAAmafZAJmn2QCZp9kAmafZAJmn + 2QAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf + 4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe + 3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf + 4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf + 4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg + 9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGho + ZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdn + ZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdn + ZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe + 3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEABwYcAAcG + HAATJ44AhYaJAIWGiQCCjLsAMi81ADIxVAAODgwAHzy9AAYOLgADAxEAAAAAAJmn2QCZp9kAmafZAJmn + 2QCZp9kAmafZACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe + 4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf + 4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf + 9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGho + ZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdn + ZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdn + ZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe + 3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBAAcG + HAAHBhwAEyeOAIWGiQCFhokAgoy7ADIvNQAyMVQADg4MAB88vQAGDi4AAwMRAAAAAACZp9kAmafZAJmn + 2QCZp9kAmafZAJmn2QCZp9kAmafZACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe + 3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf + 4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe + 4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe + 4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGlo + ZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGho + aABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpq + aQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAHBhwABwYcABMnjgCFhokAhYaJAIKMuwAyLzUAMjFUAA4ODAAfPL0ABg4uAAMDEQAAAAAAmafZAJmn + 2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf + 4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf + 4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe + 3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpq + aABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5u + bwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBw + cgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf + 4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEABwYcAAcGHAATJ44AhYaJAIWGiQCCjLsAMi81ADIxVAAODgwAHzy9AAYOLgADAxEAAAAAAJmn + 2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAJh7gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V + 3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf + 4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxr + awBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlp + aABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGho + ZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf + 4AAmH+AAJh7gACYe3wAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBAAcGHAAHBhwAEyeOAIWGiQCFhokAgoy7ADIvNQAyMVQADg4MAB88vQAGDi4AAwMRAAAA + AACZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QAmHt8AJh/gACYf + 4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V + 3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe + 4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdn + ZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdn + ZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdm + ZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe + 4AAmH+AAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAHBhwABwYcABMnjgCFhokAhYaJAIKMuwAyLzUAMjFUAA4ODAAfPL0ABg4uAAMD + EQAAAAAAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn + 2QAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ + 3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe + 3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe + 4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG + 7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGho + ZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdn + ZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZm + ZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEABwYcAAcGHAATJ44AhYaJAIWGiQCCjLsAMi81ADIxVAAODgwAHzy9AAYO + LgADAxEAAAAAAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn + 2QCZp9kAmafZACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb + 8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf + 4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG + 7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlp + ZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlp + ZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlp + aABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBAAcGHAAHBhwAEyeOAIWGiQCFhokAgoy7ADIvNQAyMVQADg4MAB88 + vQAGDi4AAwMRAAAAAACZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn + 2QCZp9kAmafZAJmn2QCZp9kAmafZACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE + +ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe + 3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG + 7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZm + YgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxs + bQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9v + cABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAHBhwABwYcABMnjgCFhokAhYaJAIKMuwAyLzUAMjFUAA4O + DAAfPL0ABg4uAAMDEQAAAAAAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn + 2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY + 5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe + 4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe + 3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf + 4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK + 8AAjRu8AI0bvACNG7wCIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI////// + //////////////////////////iIiIiIiIiIj///////////////////////////////+IiIiIiIiIiP + ///////////////////////////////4iIiIiIiIiI////////////////////////////////iIiIiI + iIiIj///////////////////////////////+IiIiIiIiIiP///////////////////////////////4 + iIiIiIiIiI////////////////////////////////iIiIiIiIiIj/////////////////////////// + ////+IiIiIiIiIiP///////////////////////////////4iIiIiIiIiI////////////////////// + //////////iIiIiIiIiIj///////////////////////////////+IiIiIiIiIiP//////////////// + ///////////////4iIiIiIiIiI////////////////////////////////iIiIiIiIiIj/////////// + ////////////////////+IiIiIiIiIiP///////////////////////////////4iIiIiIiIiI////// + //////////////////////////iIiIiIiIiIj///////////////////////////////+IiIiIiIiIiP + ///////////////////////////////4iIiIiIiIiI////////////////////////////////iIiIiI + iIiIj///////////////////////////////+IiIiIiIiIiP///////////////////////////////4 + iIiIiIiIiI////////////////////////////////iIiIiIiIiIj/////////////////////////// + ////+IiIiIiIiIiP///////////////////////////////4iIiIiIiAiI////////////////////// + //////////iIiIiIAACIj/////////////////////////////////iIiIgAAIiP//////////////// + /////////////////4iIiAAAiI///////////////////////////////////4iIgACIj/////////// + ////////////////////////+IiIgIiP////////////////////////////////////+IiIiI////// + ////////////////////////////////iIiIj//////////////////////////////////////4iIiP + //////////////////////////////////d////4iI////////////////////////////////93d3d3 + //+Ij///////////////////////////////d0AHd3d//4iP//////////////////////////////dw + AAd3d3d/iI//////////////////////////////cAAAB3d3d3eIj/////////////////////////// + //cAAAAAR3d3d4iP////////////////////////////cAAAAAAAR3d3iI////////////////////// + //////cAAAAAAAAAd3eIj///////////////////////////cAAAAAAAAAAHd4iIj/////////////// + //////////wAAAAAAAAAAAB3iIiP////////////////////////9AAAAAAAAAAAAAeIiI////////// + //////////////9wAAAAAAAAAAAAB4iIiP///////////////////////wAAAAAAAAAAAAAAiIiI//// + ///////////////////3AAAAAAAAAAAAAACIiIj//////////////////////3MAAAAAAAAAAAAAAIiI + iI//////////////////////cAAAAAAAAAAAAAAAiIiIj/////////////////////cwAAAAAAAAAAAA + AACIiIiI////////////////////9wAAAAAAAAAAAAAAAIiIiIj///////////////////9wAAAAAAAA + AAAAAAAAiIiIiI///////////////////3AAAAAAAAAAAAAAAACIiIiIiI//////////////////cAAA + AAAAAAAAAAAAAIiIiIiIj/////////////////cAAAAAAAAAAAAAAAAAiIiIiIiI//////////////// + 9wAAAAAAAAAAAAAAAACIiIiIiIiP///////////////3AAAAAAAAAAAAAAAAAIiIiIiIiIj///////// + //////cAAAAAAAAAAAAAAAAAiIiIiIiIiI//////////////9wAAAAAAAAAAAAAAAACIiIiIiIiIj/// + //////////93AAAAAAAAAAAAAAAAAIiIiIiIiIiI/////////////3cAAAAAAAAAAAAAAAAAiIiIiIiI + iIiP////////////dwAAAAAAAAAAAAAAAACIiIiIiIiIiIj///////////93AAAAAAAAAAAAAAAAAIiI + iIiIiIiIiI///////////3cAAAAAAAAAAAAAAAAAiIiIiIiIiIiIiIiIiIiP////dwAAAAAAAAAAAAAA + AACIiIiIiIiIiIiIiIiIiIiIiP93AAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiIiIiIiAAAAAAAAAAA + AAAAAAAAiIiIiIiIiIiIiIiIiIiIiIiAAAAAAAAAAAAAAAAAAACIiIiIiIiIiIiIiIiIiIiIgAAAAAAA + AAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiIiIiIiIiIiIgAAA + AAAAAAAAAAAAAAAAAACIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiI + AAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAACIiIiIiIiIiIiI + iIiIAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiI + iIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAIiI + iIiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAA + AACIiIiIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIAAAAAACAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIgAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAiIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIgAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiI + iIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiI + iIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AACIiIiIiIiIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIiIiIgAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAIiIiIiIiIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiIAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIiAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIiIiIAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiI + iAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiI + iIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AACIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAiIiIiIiIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIiIiIAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAiIiIiIgAAAAAiIiIiIiIiAAAAAAAAAAAAAAA + AAAAAACIiIiIiIiAAACIiIiIiIiIAAAAAAAAAAAAAAAAAAAACIiIiIiIiIiIAIiIiIiIiIgAAAAAAAAA + AAAAAAAAAACIiIiIiIiIiIiIiIiIiIiIiAAAAAAAAAAAAAAAAAAACIiIiIiIiIiIiIiIiIiIiIiIAAAA + AAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiIiIiI + iAAAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiIiIiIiIAAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiI + iIiIiIgAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiIiIiIiIiAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiI + iIiIiIiIiIiIAAAAAAAAAAAAAAAAiIiIiIiIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAAAAAiIiIiIiIiI + iIiIiIiIiIiIiIiIiIAAAAAAAAAAAAAAiIiIiIiIiIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAAAiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIAAAAAAAAAAAAiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIAAAAAAAAAAiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIgAAAAAAA + AAiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIAAAAAAAAiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + AAAAAIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiCgAAACAAAAAAAEAAAEA + IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAAB7AAAAnwAAAJ8AAACZAAAAdQAAAEIAAAAJAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABfAAAAyAAA + APkAAAD/AAAA/wAAAP8AAAD2AAAA1AAAAIQAAAAbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAaAAAAswABAP8GBhj/Cwsr/wsLK/8HByD/BAQO/wABAP8AAAD/AAAA/wAA + AOQAAABIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJQABANcPDkb/Hhut/yUe + 3f8kHer/IRnr/xwU5f8ZFNH/FBGs/wkIYv8AAA3/AAAA/wAAAPQAAAAxAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAB4DBAjaGhiQ/yMb9v8eFff/GxTn/yUh1/9AP8n/Z2bD/3+Bx/9/gc7/aGbQ/zc4 + n/8BAx3/AAAA/wAAAMAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAAAUzRURr/8gGu7/Mi/O/1ta + wP+RksH/xcfP/+3u5P////b////+/////v////T/7u3p/5SUoP8aGRf/AAAA/wAAAD4AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAKSgoBwICBbY5O4r/cnLO/6qsxf/e39r////x/////////////////////P/09fb/5er3/97m + ///d4///4+r//8HG0P85Ny7/AAAAlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbm5tJH5+fmaPj4/Burq7/+/v7f////n///////// + ///4+v//1N35/6m48v99kuz/V3Hn/zxa5/8tT+v/J0vw/yJG8P8lSvD/QmX+/1pwzf8aHCG9AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1tbAJwb28yg4KDeqam + psLQz9D18/Pz///////////////9/93j9/+is/T/aIPx/z1e7/8iRu7/Fz3u/xY87/8ZPvD/HULx/yBE + 8P8hRO//IUbv/yFF7/8dQu//HkT6/y9My94zSZ0NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAbGxsB3Jycj6JiYmHr6+vz9nZ2v77+/r////////////+/ff/09fo/5ak3/9VcOP/J0rs/xY8 + 7v8XPe//HUHv/yFF7/8jRu//I0fv/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH8P8jSfD/Ikn2/yRJ + +Z8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsbGwIc3NzQYuLjI+0tLTY39/f/////f////////////Hy + 9P+/x+f/gpPd/0lk3v8kRuX/Fjzt/xo/8f8gRfD/I0fv/yNH8P8jR+//I0fv/yNG7/8jR+//I0fv/yNH + 8P8jR+//I0fv/yNG7/8jR+//I0bv/yU06P8kPuz/I0nw/yNH8GkAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABzcnI7jIyLj7S0 + tNni4eD////+////////////4+f2/6q36v9rgeP/OFfj/xxB6f8WPO//G0Hx/yFG8f8jR+//I0fv/yNH + 7/8jRu//I0fv/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH7/8jRu//I0fw/yNJ8P8lMeb/Jhre/yYf + 4P8kO+v/I0nw8yNG7y8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAB2dnUspaSlw97d3f////7////////////U3Pj/lafv/1dy6v8rTen/GD3t/xc9 + 8P8eQvH/Ikbw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH7/8jR+//I0fw/yNH + 8P8jR/D/I0bv/yNH7/8jSPD/JEHs/yYf4P8mHuD/Jh3f/yYf4P8kO+r/I0nwyyNH7woAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIuLjJj//////////8jS + +P+DmfP/SGfu/yJG7f8WPO7/Gj/w/yBE8P8jR/D/I0fv/yNH8P8jR+//I0bv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fv/yNH7/8jR/D/I0bv/yNH7/8jR+//I0fv/yNG7/8jRu//I0fv/yNJ8P8lM+f/Jhzf/yYf + 4P8mH+D/Jh3f/yYh4P8jQu3/I0jwgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAdnVzML/Aw/BohPn/GT7t/xY77v8bQO//IUXv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fw/yNH7/8jR+//I0bv/yNH7/8jR+//I0bv/yNH7/8jR+//I0fw/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fv/yNH7/8jR+//I0jw/yUr5P8mHN//Jh7g/yYf4P8mHuD/Jhzf/yUx5/8jSfC0AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW2WSNho/ + 8e0iRfH/I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH + 7/8jR+//I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0bv/yNH7/8jR/D/Jifj/yYd + 3/8mH+D/Jh/g/yYf4P8mHN//JS3l/yNJ8KUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIkXyhiNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH7/8jRu//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fv/yNG7/8jRu//I0fv/yNF7v8mI+H/Jh7g/yYf4P8mH+D/Jh/g/yYc3/8lM+f/I0nwhAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAjR/AZI0fv7SNH7/8jRu//I0bv/yNG7/8jR+//I0fv/yNH8P8jR+//I0bv/yNH7/8jRu//I0bv/yNH + 7/8jRu//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH7/8jSPD/JEHt/yYf + 4P8mHt//Jh/g/yYf4P8mHuD/Jhzf/yQ56v8jSfBYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjR++DI0fv/yNH7/8jR+//I0bv/yNG + 8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0bv/yNH7/8jRvD/I0bw/yNJ8P8kN+n/Jhze/yYf4P8mHuD/Jh7g/yYe4P8mHd//JDzr+yNI + 8CkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACNG7xUjR+/pI0fv/yNH8P8jRvD/I0fw/yNH8P8jR+//I0fw/yNH7/8jR/D/I0fv/yNH + 8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH8P8jR/D/I0jw/yUo + 5P8mHd//Jh/g/yYf4P8mHt//Jh7g/yYd3/8kOurMI0vxBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVAAAAVAAAAIoAAACkAAAAqAAA + AJgAAABrAAAAJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNH8HYjR+//I0fv/yNH + 7/8jRu//I0fw/yNH8P8jR+//I0fv/yNG7/8jRvD/I0bv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH + 8P8jRu//I0fv/yNH7/8jR/D/I0fv/yNI8P8kP+z/Jh7f/yYe4P8mH+D/Jh3f/yUp4/8mKuT/Jh3f/yYj + 4ZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAPAAAAhwAAAOYAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADzAAAAlAAAABIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAI0bvDSNH790jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNG + 7/8jRu//I0fv/yNH7/8jR+//I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNG8P8jR/D/I0nw/yUu + 5f8mHuD/JiDh/yYe4P8mHuD/JTHn/yUy5/8mH+D/Jh7gXgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMwAAANICAwP/Cwwx/xIRYv8YF3v/GRmA/xYU + cv8ODkX/BAUN/wAAAP8AAAD/AAAA0wAAADUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0fvWyNH + 7/8jSfD/I0jw/yNH7/8jR+//I0bv/yNH7/8jR+//I0bw/yNH8P8jR+//I0fv/yNH7/8jR/D/I0bv/yNH + 7/8jRvD/I0fw/yNH7/8jR+//I0fw/yNI8P8jQ+7/JiDg/yUs5f8mIuH/Jh3f/yYj4f8lNun/JTDm/yYe + 3+QlHt8SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AEYCAwPuExNf/yEdwP8mH+r/Jx/1/ycf9v8nH/b/Jx/2/ycg8f8jHs7/ExJj/wEBBP8AAAD/AAAA8AAA + AEkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjRu8BI0fvtSQ06P8kPOr/I0nx/yNI7/8jR+//I0fv/yNH + 7/8jRu//I0bv/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0rw/yUx + 5v8mJOL/JTPo/yYf4P8mHuD/JS/m/yU26f8mJOL/Jh3fbQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/CAkh8yAdsv8oIPb/Jh7x/yUd6f8lHef/JR3o/yUd + 7P8lHe3/JR3q/yYe6P8mIOL/GRtu/wMFAf8AAAD/AAAA5AAAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACNJ8REjRvAxIUTvLyFD7hgiR+8JAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAjSfAZJSzk0CYc3/8lM+f/I0jv/yNJ8/8kSfb/I0jy/yNH8P8jR+//I0fv/yNH8P8jR+//I0fv/yNG + 7/8jR/D/I0fv/yNH7/8jR/D/I0fv/yNI8P8jQe3/JSXi/yU06P8lLuX/Jhzf/yYr5f8lOOr/Jirk/yYd + 380mH+AHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJAoM + MOgkH9X/Jx72/yUd6P8lHen/JR3s/yUd6v8lHuD/JCDP/yMhvv8jIrP/IyGv/yIgsv8kIb3/Ih2y/xwn + n/8TJnLlFiyVKgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0nwGiNE7mYlOOmxIifk5iQm4v8wM+T9MjXl8CUq + 5N0kLubDJDXopSQ56YQkP+1jI0TuQyNH7ycjSfARI0nxAQAAAAAlMucNJhzfzyYa3v8mKuf/ITbU/x4y + vv8iQuT/JEn1/yRJ9f8jR/D/I0fv/yNH7/8jR+//I0fw/yNH7/8jR/D/I0fv/yNH7/8jSPD/I0bv/yUu + 5f8lMuf/JTbp/yYj4v8mHd//JSrl/yYp5P8mHd/8Jh7gOwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcJCijEJB/Z/yYe9P8lHe3/JR3q/yUf3f8kIcr/JCO9/yQi + uv8lIcP/JR/R/yYf3P8mHuP/Jx7m/yYe5v8nHuj/JyXt/yVI/f8lS/3xI0jyiSNH8AkAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0jwGyND + 7oElMObrJiHh/yIX3v9ISOb/cHnv/2lw7f+CivP/YGbr/yce3/8lG9//Jh3f/yYf4P8mIeH/Jibi+SUr + 5OklMefOJDfpsSQ964UlMuiWIiTl+hsbuv8cHKr/FhR8/xUUev8ZI5v/HzfK/yNG7v8kSvb/JEn1/yNJ + 9P8jSPT/I0fx/yNH8P8jSPH/I0ry/yNF7/8lKuT/JS7l/yU46v8mKuT/Jh3f/yYf4P8mH+D/Jh7g/yYe + 4IEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAUQeyMe + zP8nH/3/Ix7b/yIfvv8kIrf/JSK//yUhzf8mINz/Jh7j/yYe5P8mHuL/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHN//JTbp/yNJ8P8jR/D/I0fvvyNH7xIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkOuqJJiDg/yYc3/8mHuD/JBzf/zc15P86OeT/Ixzf/ysm + 4f80MuP/KSPg/yUe3/8mH+D/Jh7g/yYe4P8mHeD/Jh3f/yYc3/8mHN//Jh7g/yMf4f89Rej+PUfk/ykz + 2P8fKtD/GCG8/xUaof8VGZD/GCKb/x0wuP8fOMz/IDnP/yA60v8iQ+f/I0jx/yNE6v8kOOb/JiPj/yce + 5f8mMuv/Jirk/yYe4P8mHuD/Jh/g/yYe4P8mH+C2Jh/gAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQaGYzzIx7M/xwcj/8dHYv/Ix+6/yYf3P8mHuT/Jh7j/yYf + 4f8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYd3/8lK+T/I0jw/yNH7/8jR+//I0fvsiBE + 7wIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYv + 7A8mG95wJh/g1yYf4P8mH+D/Ixvf/yMb3/8mH+D/JR3g/yQb4P8lHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8iGd//PT7l/5Ge9v+Xo/j/ipT3/3mC9f9ka/P/TVTv/zhA6f8nL9r/HCXI/xci + uP8WIKX/FhyR/xcekP8YHo//FhaB/xcQgP8YEor/HRqp/yQh0v8nHuX/Jx/l/yYf4f8mHuD/Jh7g0iYe + 4BMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADA0spR8f + kv8fHZ7/Ix7E/yYf5P8nHub/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/Jh3f/yUk4v8jRu//I0fv/yNH7/8jR+//Gz/vYAAAAAD7/P8C////CgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmH+ALJh7fXiYf4MUmH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yEY3/9BQOb/l6X4/5yp + +f+eqvn/nqv5/56r+f+bqPj/lJ/3/4eR9f9ze/T/W2Lx/0NL7v8uOef/IC3a/xkoyv8XJbb/FSGg/xUb + i/8UFnj/FRJ4/xoUl/8hGr7/JR3a/yYc4N0mHN8iAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACYf4BYmH+BEJh7ggicg5MMmINn/Jh/h/ycf6f8mH+T/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/JiLh/yNE7v8jSPD/I0fv/yNH + 7/8YPe7ZAAAAAP///1Pc3NxoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAABAAA + AAAAAAAAAAAAAAAAAAAAAAAAJh7fASYe4EgmH+CwJh/g+yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh7f/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yUd3/83NuP/U1bp/2lw7v96hPH/h5P0/5Gd9v+Zpvj/nqz5/6Gt + +v+eq/n/maT4/42X9v97g/P/ZWzy/1Fa8f89Su7/Kzzo/x0w3P8bL8r/Gy2v/xgjlv8bH6LgJSzkJwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh/gJCYf4IcmHt+7Jh/g6SYf4P8mHuD/Jh7h/yYf + 4/8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYe4P8mIuH/I0Tu/yNI8P8jR+//I0fv/xM57vizwv9g19bO4xISEmIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAA4AgMFqAEBA9oAAADUAAAAiAAAABUAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAmHt80Jh/gmCYf4O0mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh7f/yIa + 3v8fF9//Hxff/yMc3/8pJOD/NDHj/0FC5f9VWen/b3jv/4iU9P+YpPj/nar5/56r+f+eq/n/nqr5/5mk + 9/+Pmfb/cnry/zo96/8lKer/JTHq/yQ55vAjQ+3BI0nwiSNJ8EgjSfEWAAAAAAAAAAAAAAAAAAAAAAAA + AAAmHd9DJh/g/yYf4v8nIOb/Jx/j/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/Jh/g/yYe3/8mH+D/Jh7f/yYe4P8mHuD/Jh/f/yYe4P8mH+D/Jh3f/yYm4v8jR+//I0fw/yRH + 8P8YPe7/S2z4+t/i5/EoJyT/AAAASgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAaAsMN/gTE2n/AwQK/wAA + AP8AAAD/AAAA2QAAAC8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh7gHyYe4H0mHuDdJh7g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/JR7g/yUc4P8jGt//IRjf/x8W + 3v8gGN7/JB7g/zg35P9WXOr/cHrw/4CK8/+AjfP/gY3z/4mU9f+Xo/j/jJj1/zAs4f8jGd//Jh3g/yYf + 4P8mJeL/JS7m/yQ46ekkQe27I0bvfiNI8DsAAAAAAAAAAAAAAAAmHeI+IxvOyR4Zrf8jHcz/Jx/k/ycf + 5/8nH+b/Jx/m/ycf5f8nH+X/Jh/k/ycf4/8mH+L/Jh/h/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/f/yYe + 4P8mHuD/Jh7g/yYf4P8mG9//JDTo/yNJ8P8jR+//HUHv/y5T8v/k7f/6W1lT7QAAAP8AAAA7AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAECBGIPEFD/FRR8/xgVk/8aGJT/DQ4+/wABAP8AAAD/AAAA3wAAABwAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACYe4A8mHt9iJR/fxiYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH9//JR3g/ygj4P9dZOv/bHHu/2x17/90fvD/eYXx/3aC + 8P9lbu3/QkXm/ywn4f8zLuP/JyDg/yYf4P8mH+D/Jh7g/yYd3/8mHN//Jhzf/yYf4P8mJeL/JS7l/yQ5 + 6qokQuwRAAAAAAAAAAAAAAAAHxyyVBgSh9MYE4z/HBam/x4Ysv8gGbf/IRq9/yEbw/8iG8r/Ix3Q/yUe + 1v8mH97/Jx/l/yYf4f8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jhzf/yUo4/8jR+//I0fw/yBE + 7/8dQ+//ydf//7a0qvcAAAD8AAAA/gAAADEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQ5Dw9X9xUTfP8YFY3/Ixza/ycf + +f8nIPD/GhmN/wMEB/8AAAD/AAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAmHd8BJh3fRiYe4K4mHuD5Jh/g/yYf4P8nH+D/IBnh/xwT4f8hGeH/Jh/g/yYf4P8mH+D/Jh/g/yYf + 3/8mHuD/HRXh/0dK6v+LmPb/oK75/5qm+P+YpPj/maT4/5qm+P+Uoff/U1jp/xwU3f8lHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYd3/8mHN//Jhve/yUr5NQjSfCnI0nxmCNJ8YUlRPVqHzDIkxki + l/sZIZT/Fx+P/xcdjP8XGoj/FxiE/xYVgP8WFH7/FhJ8/xYTg/8eGaz/Jx/h/ycf5v8nH+T/Jx/k/yYf + 4v8mH+D/Jh7g/yYc3/8mJOH/JEPu/yNI8P8jRu//GD7v/52x+//+/PL/ISEh/wAAAP8AAAD1AAAAJAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAERITTdcgIoj/GRac/yUd5P8mHuz/JR3n/yUd6f8oIPr/HRud/wIDBP8AAAD1AAAAHwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYh4SEmHuCAJh3f3yIZ + 4P9PS9j/a2rU/0pK2v8jHeD/Jh7g/yYf4P8mH+D/JR7g/yYf4P9XVtf/aWnS/zY13v9bYez/mKb4/5ai + 9/+UoPf/lKD3/5ei9/+Zp/j/TE/o/xwT3v8jGt//JB3f/yYd4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mG97/JSjj/yNH7/8jR+//I0fw/yNI8P8jSvT/JEn1/yRJ8/8jSPL/I0fx/yNF7P8jROn/IkLl/yE+ + 3f8gN8v/GCCV/xQRcv8aF5f/IRvC/yIcxP8jHMv/JR3X/ycf4/8nHeP/JiPi/yRB7f8jSfD/I0fv/xk+ + 7/9XdPP//////5aVlP8AAAD/AAAA/xkZGemOjo8VAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJCiWfQ0ai/yIfqv8kHOr/JR7s/yUd + 5/8lHej/JR3o/yUd6P8oIPn/EhJc/wAAAP8AAABEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACNJ8QkjRe4qI0TuTiQ+7HgkPOvKGivq/3N51P/e4L//ZmjV/xwU4f8mH+D/Jh/g/yYe + 4P8lHuD/IRrh/6Ckyv/c3b//WFfW/xUN4P9WXOr/mKX4/5up+P+ap/j/mqf4/52p+f+Wo/f/Vlrq/zIw + 4v8rJ+H/Ixzf/yQc4P8mH+D/Jh/g/yYe4P8mH+D/Jhzf/yUs5f8jRe7/I0jv/yNH7/8jR/D/I0fv/yNH + 8P8jR/D/I0fw/yNH8P8jR/D/I0jx/yNI8v8jR/L/JEr3/yVM/P8hPNf/GBeF/xUQdf8UEXH/FBFz/xQQ + c/8VEXr/GhSS/yMizv8kQe3/JEz3/yRK9f8jR/H/Fjzu/62+/P///vn/KCgp/wAAAP8AAAD/RERF4aen + pwsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAASklOk/89Pbz/IBjl/yYe7f8lHeb/JR3q/yUd5/8lHej/JR3n/yYe8v8gHLr/AgMD/wAA + AFYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjR+8dI0fwSSNH730jR++xI0fv3SNH7/sjSPD/I0jw/yNJ + 8P8bQ/P/T2vj/9HQwf+Ii8//HRfh/yYc3/8mHuD/Jh7g/yYf4P8cFOL/dHbS/9fYwP97fdH/HBTh/yEZ + 3/9BQeX/WFzq/1NW6f9TVun/Z27t/4yY9f+eq/n/kJ32/4uW9f9zffD/MCzi/yQc4P8mH+D/Jh3g/yYe + 3/8lM+f/I0jw/yNI8P8jR+//I0fv/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0bv/yNH8P8jR/D/I0fw/yRK + 9v8hP93/GSGZ/xUUff8ZIJn/HTC6/x84zf8fOM7/HzPD/xwrrv8XG4r/FRV9/xYZhv8aJKH/HzbI/yBC + 6/8nTfb/6vH//7u5tP8AAAD/AAAA/wAAAP+BgYHg////CwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUbHT/MYmbH/x8Y1P8lHev/Gxej/xgV + iP8hG8z/Jh7r/yUe6P8lHef/JR3r/yUf3/8FBRX/AQMKTgAAAAAlSv0CI0buJSNH71ojR++VI0fvyyNH + 7/IjR/D/I0fv/yNH7/8jR/D/I0fw/yNH7/8jR/D/I0fw/x9D8f83WOr/wMLF/8fIw/9BV+T/Hyjn/yYg + 4P8mHN//Jh3g/x4W4f9PTtj/0NHC/52fy/8hHOH/JR3g/yEZ3/8fFt//IBff/x8X3/8eF97/LSnh/3qF + 8f+eqvn/nan5/5qo+P86OOT/Ihrf/yYc3/8mIuH/JDzr/yNJ8P8jR+//I0bv/yNH8P8jR+//I0fv/yNH + 7/8jR+//I0fv/yNH8P8jRu//I0fv/yNH8P8kSvf/HjPB/xYTfP8aIaH/IT/b/yRJ8/8kSvf/JEn1/yRJ + 9f8kSvb/JEr3/yNH7/8gPNf/HCux/xcaiv8UEHX/Dw+A/0BQwf//////ZmVj/wAAAP8AAAD/AAAA/7Gx + sdj///8GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAABSkNGjv83NrL/IRnt/x8ZuP8UE3L/ExJw/xsXnv8mHu3/JR3o/yUd6P8lHen/Jh3p/wsJ + J/wVKod/Jkz/kCNH79AjR+/4I0fv/yNH7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNH8P8jR+//I0fv/yNH + 7/8jR+//IUXw/yZK7/+qssr/zMvC/4qb1P8ZQvP/I0Lt/yUy5/8mI+H/Ixjg/zMt3f+8vsX/urvF/zAt + 3v8jG+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8gGN7/MS7i/4CK8v+CjPP/Rkfn/yMZ3v8mHN//JSnj/yRD + 7v8jSfD/I0fv/yNH7/8jR+//I0fv/yNG7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH8P8jR/D/JEr2/xwv + t/8WEXz/IhnD/ycm6v8kRPL/I0jw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fx/yNJ9P8kSvf/I0jw/yE+ + 2/8PGpz/UlGZ//r8//8rLTj/AAAA/wAAAP8KCgr/39/fy////wIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBR2oQ0aj/x0apP8lHef/FhOH/xwe + ff8YGHb/HRew/yYe7v8lHej/JR3n/yUa5/8lJuf/Ij3K/iRJ8v8jR/D/I0fv/yNH7/8jRu//I0fv/yNH + 7/8jR+//I0bv/yNH8P8jR+//I0fv/yNG7/8jR+//I0fv/yNH7/8jRvD/HELx/5ai0P+jrs3/vcDG/z5e + 6P8dQ/H/I0nw/yNF7/8jNen/ISPj/56gy//Oz8L/SEba/yAV4P8mHd//Jh3f/yYd3/8mHeD/Jh3f/yYd + 3/8jGd//Jx7f/yYe3/8hG+D/JSvk/yQ56v8jR/D/I0jw/yNH7/8jR/D/I0bv/yNH7/8jR+//I0fv/yNG + 7/8jRu//I0fw/yNH8P8jR+//I0fv/yRK9/8eNML/FRF5/yMczf8nIOf/Jhzf/yUn4/8jRu//I0fw/yNH + 7/8jR/D/I0fv/yNG7/8jR+//I0fv/yNH8P8jSfL/I0z1/x5G9v9JYdr/VFVa/wAABP8AAAD/AAAA/zMz + NP/9/f2xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAEg4PQug5O53/Ghak/yMc2P8UE3X/ExJ2/xUTfv8jHdv/JR3q/yUb5/8lH+j/JDPr/yNH + 8P8kSff/I0fx/yNH7/8jR/D/I0fv/yNH7/8jR+//I0fv/yNH7/8jRu//I0fv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fw/yNH7/8YPvL/fY/X/5ek0P+dqM7/jZvT/xk/8v8jR+//I0jw/yFJ8f8ZPvH/eorX/9fV + v/9ocdf/GyLn/yUp4/8lJuL/JSbi/yYm4v8lKOP/JSzl/yUw5v8jNej/Izzr/yNE7v8jSPH/I0nw/yNH + 8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0bv/yNG7/8jR+//I0fv/yNH7/8jSPP/IkDf/xUV + e/8hGr7/Jx/n/yYf4P8mHuD/Jh3f/yRA7f8jSPD/I0fv/yNH7/8jR+//I0fv/yNH7/8jSfD/I0Tu/yQ5 + 6v8lMuf/JjDx/x4mrP8AAAD/AAAA/wAAAP8AAAD/ZGRk/////4IAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAERJX/ywvkv8ZFpv/JR3o/xkV + kv8UE3f/Hxm8/yYc7/8lHOf/JSvq/yNB7v8jSvD/I0fv/yNG7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH + 7/8jR+//I0fv/yNG7/8gRO//IUXv/yNH7/8jR+//I0fw/yBE7/8kR+//IETv/xM68f9bc9//r7TJ/1hz + 4f/FxsT/Olrq/xxB8f8sT/D/jaL3/xxB8v9Tb+L/1NC//4ub1P8bQ/L/I0jw/yNH7/8jR+//I0fv/yNI + 8P8jSfD/I0nw/yNJ8P8jSfD/I0jw/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0bv/yNG7/8jR+//I0fw/yRJ9f8ZI5r/GxWc/ycg5/8mH+D/Jh/g/yYc3/8mKOT/I0bv/yNH + 7/8jRu//I0fv/yNH7/8jR+//I0jw/yU26f8mIeH/Jhzf/yYc3/8nHeb/IRu7/wICBP8AAAD/AAAA/wAA + AP+Pj5D7////MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAABAG0SEmD/HByE/xYUg/8kHeP/JRvo/yMZ2f8mH93/JSvW/yQ96/8jSfD/I0jw/yNH + 7/8jR+//I0fw/yNG7/8jR+//I0fw/yNH7/8jR+//I0fw/yNG7/8jR+//Ikbv/zda8f8sT/D/HkLv/yNH + 7/8bP+//aIT1/zxd8f8kSfD/t8b8/97i8v/Cw8T/Olvo/6auzP+KmdT/EDfy/zlb8f+ouvn/HUHw/zVW + 6//DxMT/q7LL/yVJ7/8fQ/D/IUXv/yRH7/8jR+//Ikbv/yNG7/8jR/D/I0bv/yNH7/8jR+//IUXv/x9D + 7/8jR+//I0bv/yNG7/8jR+//Ikbv/x5C7/8iRvD/I0fv/yNG7/8jR+//Ikbv/yNG7/8kSfT/ID3W/xYT + f/8lHdb/Jh/j/yYf4P8mHd//JiHg/yRC7v8jSPD/I0bw/yNH7/8jR+//I0fw/yNK8f8lNOf/Jhvf/yYd + 3/8mH9//Jh7h/yYf4v8oIef/DQ4+/wAAAP8AAAD/Dg8O/9zc2akAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAkhESX/8UEn7/FRF8/yMb + 1P8mJd3/JTTW/yRB2P8jSer/I0nw/yNH7/8jRu//I0bv/yNH7/8jRu//I0fv/yNH7/8jR/D/I0fw/yNH + 7/8jR/D/I0bw/yNH7/8gRO//QGLy/3GO9v87XfH/HkLv/yJG7/+Lovf/I0bw/2qF9f/Z4v3/x9P4/9DP + xv9SbeH/Um3j/8XGw/9LaOz/Rmf0/3CN9v8YPe//IUbw/6auzP/FxcT/NFXp/zVY8/8vUvD/HUHv/yBE + 7/8nSvD/HkPv/xxB7/8kR+//Ikbw/xtA7/8rTvD/PV/y/yBE7/8iRu//I0fw/yJG7/8mSu//QmPy/yJG + 7/8jRu//IUXw/yRH7/9TdPP/I0fw/yRJ9/8bKKX/HBWi/ycf5/8mH+D/Jh/g/yYc3/8kN+n/I0nx/yNH + 8P8jR+//I0fv/yNH8P8jSvD/JDjp/yYd3/8mHuD/Jh/f/ycg5P8mH+L/IBjX/x8X6v8VE3b/AAAA/wAA + AP8sLCrz+fn/JgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAACjDw1Q/xQQev8aH4//JD3U/yNH5v8jSfD/I0jz/yNH8P8jRu//I0fv/yNH + 7/8jRu//I0fv/yNH7/8jR+//I0fw/yNH8P8jR+//I0fw/yNH7/8jR+//I0fv/yNH8P8dQe//Jknw/1+A + 9f8lSe//MVPw/22J9f8UOe7/ZYD0/y9S8f8ZQPD/q7PM/4OT1v8ZQPL/qbDI/9PV2v/F1P7/jKP3/xg9 + 7/8ZQPL/gpLW/9TRwP9NaOH/Tm/2/22K9f8wVfH/Vnf0/3+b9/+NpPj/S2vy/xk+7v8mSu//PFzx/3qT + 9v91j/b/Fzzu/zlf8f8mSvD/HULv/2WD9f+luPn/dI72/xY77v8/Y/L/LlHw/2eE9f8kSfH/IkXr/xga + jP8jG83/Jh/k/yYf4P8mHd//JSTh/yNG7/8jR+//I0fv/yNH7/8jR+//I0nw/yQ46f8mHt//Jh7g/yYf + 4P8nH+X/Ix3N/w8Mi/8uLpD/S0yv/xMVQP8AAAD/AAAA/xoZbPZFP+43AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK4PEkz/GR6Y/yJC + 5P8jSvX/I0fx/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH7/8jR+//I0fw/yNH + 8P8jR+//I0fv/yNH7/8jR+//I0fw/yNI8P8eRPD/M1ny/z5k8v9xjPb/UXP0/xU97/9lgvX/Kk7w/w00 + 8f+Il9P/qbHM/xU88/9het7/vsDD/1l37v+Mpfn/I0fv/xk/8v9cdeD/1tK//3KG2f8xVfT/hqH3/ytR + 8f8qTfD/I0fv/zVX8f+Qp/j/T27z/1Fw8//G0/z/t8f7/yRI8P8eQu//TnHz/ypO8P8oTPD/PWHy/xM4 + 7v9ph/X/IkXv/15+9f9nhfX/YoD0/yBG9P8gPNT/GRaT/yYf4v8mH+D/Jh/g/yYc3/8lM+f/I0nw/yNH + 7/8jR+//I0jw/yNI8P8lMuf/Jh3f/yYe4P8mHuD/Jh/j/yQe0/8KB3n/WVya/+Lk5P/x8+v/zc/Y/1db + ef8HCFX/JB3i/yAW4a0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAYMqkMFy2PyyNE4f8jRu//I0jy/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH + 7/8jR+//I0fw/yNH7/8jR+//I0bv/yNH7/8jR+//I0fv/yNH7/8jSPD/I0nw/yNJ8P8jR/D/I0Pt/yQ/ + 7P8aMun/f5T0/9fi/P8fNOj/GCvo/3GE8v+Mo/f/VXX3/22B3P+/wcX/K0zs/y1S8P/Jy8j/b4LV/11+ + +P82WvL/G0Lx/ztc6v/IyMP/mKTP/yJI8f9wi/b/H0Pv/yFF7/8jR/D/HULv/yFF8P9wjfb/Zoj1/xxC + 7/+csPj/QGHy/xY77v9qiPX/PV7y/xI47v9UdvT/RGXy/3iT9v8mSfD/NFnx/8bU+/9mgvX/GkH1/x0z + vf8dFqj/Jx/m/yYf4P8mHuD/Jh7f/yQ+7P8jSPD/I0fv/yNJ8P8kQe3/JSjj/yYc3/8mHt//Jh/g/yYf + 4P8nH+X/EQ2W/zw+iv/q6+v/8vHt/+rq6v/y8e//9fbz/4eJwv8ZGMb/JCzp8SYc3xoAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJEfzPiVK+9ElS/z/I0j0/yNH + 8P8jR+//I0fv/yNG7/8jR+//I0bv/yNH7/8jR+//I0fw/yNH7/8jR/D/I0fv/yNH8P8jR+//I0fv/yNH + 8P8jSfH/I0jw/yNC7v8kOOr/JS7l/yYm4v8mIeH/Jh7f/yAW3v9DROT/uMD1/4SF7f8WCtz/SEbk/9zi + +v+DiPD/qbDn/8jJwv9ITNv/Gx7n/6+z3f++wcT/Wm/r/0Na7v8dNev/JkHs/66zyf+4vMf/K03s/2WA + 9f8lSO//IUXv/xc87v8bQO//JErw/19+9f+owfv/a4j2/26O9v9LbvP/FDvv/1l89P/E1fz/Xnz1/ylQ + 8f+wxfv/gJn3/xg/7/8oUPH/Vnf0/zFW8f8iSff/Gyyv/yAYuf8nH+X/Jh/g/yYe3/8mIuH/I0Tu/yNI + 8P8jSfD/JDfp/yYg4P8mHd//Jh/g/yYf4P8mH+D/Jh/j/yIb0f8REHr/uLvQ//b18P/p6er/6unq/+rp + 6v/s6+v/9/fs/4CEw/8YN+X/JSzmSgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACNG73EjRu/5I0fw/yNH7/8jR/D/I0fv/yNH7/8jR+//I0fv/yNH7/8jRu//I0bv/yNH + 7/8jR/D/I0fv/yNH7/8jR/D/I0fv/yFF8/8dQ/X/HD7y/x4z7v8lKuX/JiHg/yYc3/8mHN//Jh7f/yYe + 4P8mH+D/JBrf/ywp4f8wMuL/oKby/0hF5f8WDt7/qKvz/9bZ+v/P1PP/1tfG/2lo0/8UCuD/UE/k/8jJ + xv+lrNf/S0vo/x8V3v8eF+H/iozO/87Pwv9CQ9v/VFTq/x8f4f+DjPD/q7r3/6Gw9v+suPf/mZ7x/zY8 + 5v9dYen/V17p/yMr5f8iKeT/LDjm/4mO7//Lz/j/NDvm/yUq5P8iJuT/JSnk/yYt5f8eKuX/IjPo/yVC + 8f8bJ6X/IRnD/ycf5P8mH+D/Jh3f/yYk4v8jSfD/I0Xv/yUu5v8mHd//Jh7g/yYf4P8mHuD/Jh/g/yYf + 4P8nH+b/Ew6v/2Nmpf/9/vf/6+vr/+7u7v/v7+//7Ozs/+rp6v/t7ev/7Ovm/0pk5P8cQvGRAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNH7wUjR/CaI0bv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH7/8jR+//I0fv/yJH8v8dQ/T/KkrZ/0lb + vv9bYLP/UU60/ykh2f8lHeH/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8vLeH/JyLg/zI14v85NeP/VVfn/xwT + 3v8sJ+H/pKXy/7O19P+2ucj/j5HN/x0X4v8YEeH/cHLS/8/Txv9ZXeP/HhXg/x0U4f9jY9X/1tfB/2Jh + 1f9BPub/JBvf/0M94/+mpvL/tr31/zIt4f8ZEd7/Ihnf/x8W3/8fFt//Jhzf/yYd3/8mHd//HRbf/yEc + 3/8mHd//JBvf/yYc3/8mHd//Jhzf/yYc3/8mHN//Jx/j/xsanP8iHMn/Jx/k/yYf4P8mHuD/JSHg/yUv + 5v8mJeL/Jhzf/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/ycf5f8WE5v/QUNX/8DAu//08/T/2tra/9fX + 1//n5+f/9vb2//b29v/59+z/lKbs/xc98N0jR/AKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAjR+8OI0fvsyNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH + 7/8jR+//I0fv/yNI7/8hRvT/Hj7p/1BhtP+anKn/uri8/7+/w/+oqqj/MS/G/yMb5P8mH+D/Jh/g/yYf + 4P8mH+D/Ixrf/0BD5P9EROX/Ly3h/zw85P82N+P/JBzg/yMc4P8bE97/FQ7g/5KTzP+ytcf/JiHg/yEY + 4f8vK97/wcTF/3+C0/8bE+H/IBjh/0JA2//NzsP/h4jP/zk55v8oIOD/IBjf/xcP3f9dXuj/g4nu/yMc + 3/8lHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/JR7g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuL/Gxeb/yMdzf8mH+P/Jh/g/yYf4P8mHuD/Jhzf/yUa3/8kG9//Jh7g/yYe4P8mH+D/Jh/g/yYe + 4P8mH+D/Jx/l/xcWfP8AAAD/FBQT/zk5Of8UFBT/EBAQ/ykpKf9ZWVn/mJiZ/+fm3v+6xO3/HUPv/SJG + 7zEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0bvDSNH77wjR+//I0fv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fw/yNH7/8jR+//I0fw/yNH7/8jR+//I0fv/yNI8P8jSvD/ID7y/yQy2f9tcaP/trW2/72+ + y/+kpbH/uLnC/4SGqP8jHtj/JR7h/yYf4P8mH+D/Jh/g/yYf4P8lHuD/JR7g/1lc6P99g+3/aW3q/yYf + 4P8lHuD/Jh/g/yYe4P8cFOL/c3PS/8nMw/85Ntz/Ihng/xsT4f95e9H/wMPE/zAs3v8gGOH/Kibf/7W3 + x/+oqcn/P0fl/ych4P8mHuD/Jh/g/xcP3f9hY+n/Vlfn/x8X3/8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf4v8bF5r/Ix3L/yYf4/8mH+D/Jh7g/yYe + 4P8jHN//KC/l/y476P8lHuD/Jh7g/yYf4P8mHuD/Jh7g/yYf4f8mH+L/FRRv/wABAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8BAQH/ysnE/9PY8f8mSO7/IUXwVwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNF + 7QEjR/CmI0fv/yNH7/8jR+//I0fw/yNH7/8jR+//I0fv/yNH7/8jRu//I0fv/yNH7/8jRu//I0fv/yNI + 8P8jSfD/JELt/yAr7P8pJs3/fn2f/7y9wP+5usr/t7jI/4eIjf+oqar/UFC2/x8Y5f8mH9//Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8lHt//IRnf/ych4P8iGt//JR3g/yYf4P8mH+D/Jh7g/x4W4f9VVNj/0tTC/1ZV + 2P8dFeH/Ihrg/zMw3f/FyMT/cnPS/xsU4v8fGOH/kpTN/7y9w/9/iuj/Pjzl/xoQ3v8jG9//GRDe/z49 + 4/9rcOr/HRXf/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8mHuD/Jx/j/xoXmP8iHMj/Jx/j/yYf4P8mHuD/JBvf/yk86v99lPf/YG/w/x8Y3/8mHuD/Jh/g/yMb + 4P8lHuD/Jh/h/ycf4/8VFHT/AAEA/wAAAP8AAAD/AAAA/0ZGR/+hoaH/RkZG/wEBAf/Ozcr/5uj3/y1O + 7P8gRPBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAB0WvA4fGbwhIhy+LiAavSglHL0QHjPdPh1D8P8fRPD/IEbx/yJH8f8iSPH/I0fw/yNH + 7/8jR+//I0fv/yNH7/8jR+//I0fw/yNJ8P8jSfD/JEHt/yUv5v8hG+X/KiPK/4WGoP++v8T/t7jI/7a3 + xv+2t8b/ubrJ/62vs/85NcH/Ihrl/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/JR7g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/IRng/zw63P/LzcP/eHnR/xsU4f8mH+D/GxTh/4GD0P++wcX/LSne/xoR + 4v9rbNT/y8zA/4mQ4P+8wfj/SETl/zEt4f9TUub/pKzy/0VE5f8gGN//Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8nH+T/GxeZ/yEbwv8nH+T/Jh/g/yYe + 4P8gGd//aXjx/6ay+v9YY+3/Hxjf/yYf4P8mHt//SU7n/ygj4P8lHuD/JyDm/xcUg/8AAAD/AAAA/wQE + BP+dnZ7/9vb2//r6+v/v7+//b29w/5uamP+vsr3/MFLq/yBE8G0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcA/JiDAeiEavaJISdHJeILp4X2H7PV7huv8cHnl+S4q + wuRTWtnmT17i/z1Q4P8zSOH/KUPk/yE/5/8dPun/HD/s/x1C7/8hR/H/I0nx/yNJ8P8jR/D/JD3r/yUs + 5f8mH+D/Ihjk/yoly/+Fh6L/vb/F/7e4yP+2t8b/trfG/7a3xv+4ucj/t7m8/05OtP8eFub/Jh/g/yUf + 4v8kHuT/JR7j/yUe4f8mHuD/Jh/g/yYf4P8mHuD/Jh7f/yYf4P8mH+D/Jh7g/yYf4P8kHOD/KiXf/7e5 + xv+cnsv/Hxnh/yYe4P8hGeH/OTfd/8nMw/9sbdT/FQ3i/0lH2v/Q0sL/bG7R/3p87//Z4Pr/1Nz6/8TJ + 9/9YWef/HBXe/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf + 4P8mHuD/Jh/g/ycf5f8bF5//Hxu2/ycf5f8mH+D/Jh7g/yIZ3v8+Ruj/l6j5/19o7f8dFd7/Jh/g/yIa + 3/9zffD/QEDl/yEZ3/8nIOn/HBic/wIDAv8AAAD/X19f//z8/P/s6+v/6enp//Hw8f/j4+P/EBAN/0ZH + VP83VvL/IUfyaQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYh + v84fGb3/GxW7/01R0/90gOb/aG7e/29y4P9eZd//LCjH/2Vr4f+Ci+v/g4rq/4CJ6P+Aiun/fonr/3OB + 6v9fbuf/R1nj/y1D4f8iPej/JTLo/yYl4v8mHd//Jhzf/yMb4/8nI9D/g4Wh/76/xf+3uMj/trfG/7a3 + xv+2t8f/trfG/7a3x/+9vsf/enyn/yMe2P8iHen/KB/b/y0gz/8rINP/Jh/g/yMe6v8hHuz/Ix7p/yQe + 5P8mHuD/Jh/f/yYf4P8mHuD/Jh/g/yUe4P8gGuH/nJ7L/7q8xv8sKN//Ixvg/yYf4P8cFeH/iYzO/7q8 + xv8oI9//LCje/72/xf+Ymcz/FA7f/zAs4f8+O+T/Ix7g/x0V3v8mH+D/Jh/g/yYe3/8lHuD/Jh/g/yYf + 4P8mHt//Jh/f/yYf4P8mH+D/JR/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jx/m/x0Zqv8dGaf/Jx/m/yYf + 4P8mH+D/JR3f/yEh4f+FlPb/lZ/2/zQy4/8jGt//IBnf/3R98P9nbu3/Hxbe/ycf5v8iHMP/AAAN/xQU + Ef/R0NH/8fHx/+np6v/p6en/9vb2/7CwsP8AAAD/ZGh9/y9P7/8gPe9XAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIBrDYF5e2dCoqu35s7Tx/7i58v/ExPb/0dD5/1BN + 4/8cFN3/IRrb/yMd2v8mH9j/KiXW/zMw1f9ISdv/ZGnh/3iA5v+Lle7/bnbf/yIbyP8mHOD/Jh3f/yYf + 4P8lHuH/IRvc/3Byo/+9vsL/t7jI/7a3xv+2t8f/trfG/7e4x/+3uMf/urzM/7q+z/+us7z/dXeT/1Es + Y/9eJjv/Yygw/2EoM/9bJ0X/USZk/0Mki/83IrD/LCDR/yMf6P8iHuv/JR7k/yYf4P8mH+D/Jh/g/xwV + 4f99ftH/z9HC/0NB2/8gGOD/Jh/g/yAY4f8/Pdz/zc/D/2Zn1P8WD+P/nJ7L/7i7xv8rJt//IRjg/yEZ + 3/8lHd//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8nH+X/IBu7/xoXlv8nH+T/Jh/g/yYf4P8mHt//Ih7g/1lw8f9qfPL/MzDi/yMb + 3/8gGN7/bHTv/4uX9f8oIuD/JR3h/yYe4/8KClH/Rkc///X09P/q6ur/6enp//Lx8f/39/f/RENE/wIC + AP9zeaH/H0Hr/zAz5tQpH99NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAmJz3A+Lh/ynU1P1f0tL8mcXI+tHT1vz6ZmXq/xwS3v8mHeD/JR3g/yUd4f8lHeH/JBvh/yEZ + 3/8fFtv/IRnX/ygi1/8wLNz/Jh/c/yYf4P8mH+D/Jh/g/yEZ5f9EQ7f/srO0/7m6yv+2t8b/trfH/7a3 + xv+4ucj/tbbF/7W4yP+fn6n/e2Zo/25JSP9rOjH/aCkb/2coHv9mKCH/Zygg/2goHf9pKBn/aikY/2gp + Hv9iKDD/VCZa/z8jmP8rINL/Ih7s/yMe5/8mH+D/HRXh/19f1v/W18H/YWHV/xwV4f8mH+D/Jh7g/x0W + 4f+TlM3/tLfH/x4Z4f9yc9L/ztDD/0I/2/8hGOH/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4/8jHc7/GBWL/yYf + 3P8mH+H/Jh/g/yYe3/8mHuD/H0Du/x016v8kGt7/Jh/g/yAY3v9kbO3/n635/0RF5v8gF9//JR3m/xwZ + rP+OkZv/8vHt//Ly8v/39/f/19fX/2RkZP8AAAD/MC8m/1xnuv8VNez/ZnDu/4uX9f9BQuXAIBjfMwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmKTzA2p3 + 6ikwM+JeIyjk4CUn4/8lKeT/JSXi/yYe4P8mHuD/Jh7g/yYf4f8mHuH/JR3h/yQc4P8mH+H/Jh/g/yYf + 4P8mHuH/IRrc/3t8qP++v8f/trfG/7a3xv+2t8b/uLnJ/7Kzwv+Qk5v/gHV5/2QxLf9hIBv/ZCIe/2Ul + If9lKCT/Zigk/2YoJP9mKCT/Zigk/2YoJP9lKCT/Zigj/2coIP9pKRr/aSkZ/2EoMv9MJXL/MiG//yMe + 6f8dF+n/RUPb/8/Rwv+Ehs//HBXh/yYe4P8mH+D/Hxfh/0dG2v/P0cL/W1rX/0VD2v/S1ML/YGDV/x0U + 4f8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYe4P8mHuD/Jh7f/yYe + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf3/8YFoz/IxzK/ycf4/8mHuD/Jh7f/yYe3/8kP+z/I0Ht/yYe + 3/8mHuD/IBje/2Nr7f+hrvr/cHjv/x8Y3v8mH+L/GxTU/1JTq//09O//sK+u/1VVVv8QEBH/AAAA/w0M + B/94eIn/Jz3H/yE68P8sJeD/Vlrq/3uG8f9cYuvtJh/gPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiSfHUI0fv/yQz5/8mIuH/Jh7g/yYf + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yQc4/8sJ83/nZ+t/7u8yf+2t8b/trfG/7e4 + x/+0tcT/jpGZ/3lsb/9jKCP/ZSQg/2UoJP9mKCT/Zikk/2YoJP9mKCT/ZSgk/2YoJP9mKCT/ZSgk/2Yo + I/9mKCL/Zigi/2YoIv9mKCP/Zygf/2opGP9mKST/UyZg/zIetv8uLOf/vL/K/6aoyf8kHuX/Jh7m/ycf + 5f8mHuP/Hxnj/5udzf+srsn/MjDe/8HDxP+Fh8//HBXi/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYe4P8mH+D/Jx/m/xwY + of8eGa3/Jx/m/yYf4P8mHuD/Jh3f/yQ66v8jS/H/JTDm/yYc3/8gGN7/Y2vt/5yo+P+Vovf/Pj7l/x8W + 3v8oIeX/ZWnJ/4mNu//Awcb/AwMA/wAAAP8TEgr/e3qA/0BGq/8bP+f/Ji/n/yQa3/8fFt7/Ihvf/1JW + 6f81MuOfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJCnkSyM96/glKOP/Jhzf/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yYf + 4P8mH+D/Ihrk/zczxP+qrLL/ubrJ/7a3xv+2t8b/ubrK/5KTm/+Fg4j/ZjEt/2UlIP9mKCT/Zigk/2Yo + JP9mKCT/ZSgk/2YoJP9mKCT/Zikk/2YoIv9qJxn/aScc/2coJP9lKCf/YSQk/2EjIf9iIx7/YiIc/2Qi + Gf9nIw//ZyMV/1QiUf+yqLH/yszI/zAssf8ZEqf/IRm9/yMbzf8dFNb/TUzT/9DSw/9ZWtb/mpvM/6qs + yf8kHuT/Jh7m/ycg5v8nH+X/Jx/k/yYf4v8mHuD/Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8nH+T/IhzG/xkWkv8nH+P/Jh/g/yYf4P8mHN//JTLn/yNK + 8P8jRu//JSjj/x8V3f9jaez/m6j4/5ml+P+BjPP/KCPg/zAs4f/8////e3yK/y4xV/8aGjz/DAwf/1FU + gv87R7b/GTnf/yRH8ugmIuGUJh7f5CYf4PslHd//Ixrf8yYf4EEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUd3wclHuDXLkLq/yYf3/8iGd//Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8hGeX/Pj2//6+xtv+4ucn/trfG/7i5 + yP+srbr/i4+W/4Brbv9hIBv/Zigk/2YoJP9mKST/Zigk/2YoJP9mKCT/Zigk/2YoJP9mKCL/ZSko/0s1 + fP83Prf/MEDJ/zNEzf9PWcr/WmHG/19jwf9fYLn/X1uv/2BXpv9USZv/QTOG/2hvuv+Hm9z/MkCs/xUf + mf8XIJj/FBuW/xIVkv8ND43/mZy1/6Ciuv92eKn/zMvD/ywpnP8XEZ7/Hhiu/yEbvv8jHc3/JR7b/ycf + 4/8nH+b/Jx/l/yYf4v8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+L/GheV/yIcyP8nH+T/Jh/g/yYd3/8lJuL/I0fv/yNI8P8jRO7/HTfr/1Zr8f+cp/f/lJ/3/5+s + +f9PVOn/YF/q/////f9BQDz/AAAA/x4nrP8kPeH/Hz/l/x5C8P8kS/T/JDHmoQAAAAAmH+AQJh/gKiYf + 4DkmH98lAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJh7gIyEa3/dKT+j/c33w/zk45P8gGN//Hxfe/yEY3/8iGt//Ixvf/yQc4P8lHeD/JR3g/yUe + 3/8mHuD/Jh7g/yEY5f9EQ7v/tLW6/7i5yP+2t8b/ubrK/6ChrP+RlZ3/dFFR/2MiHf9mKCT/Zigk/2Yp + JP9mKCT/Zigk/2UoJP9mKCT/Zigk/2kmGf9QMmj/HEv//x9J+v8bRPf/QGP4/56s//+erP//nav//52r + //+cq///nav//5ip//8/Y/7/Fj71/xpA8v8iSPX/I0j0/yxQ9P9DYfL/TWjt/0hi5f9TauT/a4Li/1Jo + 0f+El9n/MD+q/w0Wj/8QFIT/EBF8/xIQef8VEX//GBOO/xwWpP8gGb3/JB3S/yYf4P8nH+b/Jx/l/yYf + 4v8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf5f8hG8H/GheY/ycf5f8mH+H/Jh/g/yYd + 3/8kO+v/I0nw/yNI7/8gRvD/MVPx/5Cd9/+cpvj/kZz1/ywp4f+2uf//sbGn/wAAAP8BAAD/Hzu2/yRL + //8jSPL/I0jw/yNB7f4mIOA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmHuA8Jh7g/yEZ3/9GR+b/fory/3R9 + 8P9XW+r/R0fm/z085f81MeP/MCzi/ysn4f8mIuD/JB3f/yMb3/8iG9//GxTk/0JBuv+0trr/uLnI/7a3 + xv+6u8v/n6Cq/5OXoP9uREP/ZCQf/2UoJP9mKCT/Zikk/2YoJP9mKCT/Zigk/2UoJP9mKCT/aCgc/1Ys + Vf8iPfH/IEP4/yFE7/8pS+//Umzy/1138/9eePP/Y3z0/2N89P9jfPT/Z3/0/zla8v8fRe//I0nw/yNJ + 8P8eRfD/SGjz/5Gg+P+eqfn/oqz6/5yn+v+Snvr/jJv7/3yO+P93i/v/aH73/1Vu8P9CXOX/MUnX/yI5 + xf8YKrH/EyCd/xEZjv8TFYr/FxWR/xwWpP8hGb3/JBvS/yYd3v8nH+T/Jx7l/ycf5P8mH+P/Jh/i/yYf + 4f8mH+D/Jh/h/ycf6P8dGab/Hhqu/ycf5f8mH+H/Jh3f/yUm4v8jR+//I0fv/yNH7/8dQu//Smby/42d + 9/9HVOv/LTfp/+bq+f8zMi//AAAA/wAAAP8aNKr/JUr7/yNH8P8jSO//JSnjogAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACIa31ceFt//HBPe/xcP3f8jH+D/Vlrq/3qF8f9+ivL/eYbx/3uF8f96hPH/fYny/4SQ + 9P+FjvT/fYby/3iB8f9xe/X/cXrN/6mpsv+5usr/uLnJ/7m6yv+io6//lJii/3BLSv9kIhz/Zigk/2Yo + JP9mKCT/Zigk/2YoJP9lKCT/ZSgk/2UoJP9mKST/aCce/1gmT/82JbP/JCPn/yAj7f8eIOT/HiHi/x8i + 4/8eJOP/HiXj/x4l4/8eJuT/Iyrk/yUs5P8lLeX/JS/l/yQ06P8iOer/K0ft/z9b8P9TbvP/aID1/3uP + 9v+Km/f/lqP4/56o+P+iq/j/oqv5/56p+/+Wo/z/iZr9/3iN/P9kfPr/Tmr0/zhW6f8mQ9j/GjPE/xUo + s/8WIan/GR2o/x0brP8bFbb/HhbC/yAXzv8iGtf/Jh7e/yYe4v8mH9//JB3S/xwYpP8UEnT/HBik/yYf + 4v8mH+P/Jhzf/yUx5/8jSfD/I0fv/yNH7/8fRO//J0vw/xQ67v92k///trWu/wAAAP8AAAD/AAAA/xgw + mv8lS/z/I0nw/yUw5tkmHN8VAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAODPjalFQ5/90dO3/kpPx/5uf + 8/8xLeH/HRbe/yQe4P8lH+D/JyHg/yok4f8xLuL/REPm/2x07/+KlvX/kZ/2/5ek+v+Snen/mpuq/7e4 + xv+pqrf/uLnJ/6ytuv+RlJz/joSL/18lIP9lIx//Zicj/2YoJP9mKCT/ZSgk/2UoJP9lKCT/Zigk/2Yo + JP9mKCT/aSkb/2gnHf9XJFD/Oh2g/yQY1/8dFOL/Ixng/yYc3/8mHN//Jh3f/yYc3/8mHd//Jh3f/yYc + 3/8mHN//Jhzf/yYd3/8kHd//IR7h/x4i4v8eKOX/IjTo/ylA6/80UO//QmHx/1Rw8/9lfvX/dYr2/4WW + 9v+Sn/f/nKb4/6Kq+P+iq/n/m6b6/42c+v95jfv/YXv7/0hn9v8yU+z/HDzd/21/0v9CUrr/KjSl/ycp + lf8WE4r/FxKH/xcSg/8VEnn/FBJ1/xQTev8TEnP/Gxea/yYf3/8mH+T/Jh3f/yQ46v8jSvD/I0fv/yNH + 7/8hRe//GT/w/7rK/v9QTkX/AAAA/wAAAP8AAAD/GTCP/yVM/v8lMufhJhzfKwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAADIyfqJ1NX8/9ra/f/b2/3/4uP+/1RR5/8eFt7/JR7g/yUe4P8lHuD/JR3g/yQc + 3/8hGd7/IBjf/yci4P8uK+L/Mi/i/zY26P95frL/r7C1/4yMk/+3uMf/ubrK/5GRmf+fo67/j4aN/2c4 + NP9hJB7/YSId/2EhHP9iIRz/YiEb/2IhHP9iIRz/YiEb/2IhHP9hIhz/YiUf/2YsHv9rPC7/alRx/1xb + yf81MuP/IRrn/yQc4/8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe3/8mHt//Jh3f/yYd + 3/8mHN//JBzf/yId4P8gIOH/Hibk/x0u5/8gOer/JUTu/y9R8P88XvL/TWvz/1959P9zh/X/hpX2/5ah + 9/+gqvj/oar4/5ij+P9+kPn/ws3//4+l/v+Sqvz/aYXv/xMy1v8WK7r/FB+b/xUWg/8UEHX/FBBz/xQS + ef8TEnT/GRaQ/yUe1/8nHuX/Jh/f/yQ36f8jSfH/I0nw/x5D7/89YPr/wMXQ/wgHBf8AAAD/AAAA/wAA + AP8UJIP/Hif0wyAW3yMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANfW/JPY2Pz/2tr9/87Q + +/+0t/f/RkPl/yAY3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/JR3f/yQc4P8dE97/OTjo/4eS + 3/+bnKb/u7zK/7a3x/+3uMj/tLXE/4qKkf+Mj5X/lpih/5KLk/+HeH3/gW1w/3xmaP98ZGb/eF1e/3ph + Y/98ZWf/fmls/4Z1ef+QiZD/oaCr/6+ywP+5vMX/vL3A/6Cjuf9hY7HyLirO3CQd4v8mH+D/Jh/f/yYf + 4P8mH+D/Jh7f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh7g/yYd3/8mHN//Jhzf/yYd + 3/8lH+D/IyTi/yAq5f8eMun/HDrs/x5C7/8kSvD/MFXx/0Ni8v9Zc/T/c4b1/4WU9v/Gzfv/ys/7/8jO + +/+qtfr/cYb5/1l1+/8+X/f/J0jo/xgyy/8XIqL/FhV+/xQPc/8TEXH/FxWF/yMdyv8nH+f/Jh3g/yUr + 5P8kPuz/Fzzv/4Ca//+Eg3r/AAAA/wAAAP8AAAD/AAAA/0JCdNFdVeMIAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAArrT2mZ6g8/9sbev/QD7k/yQe4P8iG9//Jh/g/yYe4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/JBvf/y4r4f+EkPP/mab+/4eNw/+ioqn/u7zK/7i5yf+4ucj/t7jH/5qb + pf+goaz/vL/Q/7u/0P+6vs7/ub7O/7q/z/+6v9D/vMHS/73C0/+7v9D/trrJ/6yvvf+foKvtj4+WzYCA + hKFzc3Vzbm1pRnNzXR9GR6kJIhrnQCYf4KEmHuDtJh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mHuD/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHd//Jhzf/yYc3/8mH+D/JSPi/yQr + 5P8hMuj/Hjrs/xw/7v8eRfD/G0Lv/5it+f+QpPj/q7j5/6Kt+P+cpfj/o6v4/5ql+P+ImPj/Z4D7/zFV + 9/8dPeH/HS20/xcZh/8TD3D/FBF1/x4Zrf8mH9//Jx3l/yYe4P8eIeT/tb30/zc2Lf8AAAD/AAAA/wAA + AP8AAAD/enp01NbWzgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkIN+fFQ/d/xEJ + 3f8ZEt7/IRvf/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8fF979aG/ulZ2q + +YyUoPielKD8t4ePyMySk6Dnr7C8/7q7y/+7vMz/vr/Q/72+z/+6u8v/u7zM/7u8zP+4ucn/sbLB/6eo + s/+YmaPnioqQxHx9f5hxcXJnamppPGZmZRkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYe + 3xwmHuBsJh/fwiYe4P0mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mHeD/Jh3f/yYc3/8mHuD/JiPh/x8l5P87T+z/tMX7/ztc + 8f+Qqvn/M1fx/0Fg8v9pf/T/jJr2/56o+P+jq/j/kZ33/z1d9P8gR/f/I0Xr/x4zxP8YHpL/FBFw/xcS + g/8gGrr/IBfd/Tk26/6/wc//BwcE/wAAAP8AAAD/AAAA/wAAAP+Pj5D1zc3NHAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAADw646ljY+r/hYfv/6Ol9P+UmfL/KSTh/yQd4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yAX3p0AAAAAAAAAAAAAAAAAAAAAAAAAAHZ5mRVubWhlhYaKt5aX + n+Cgoav2oaKt95ycpvCTlJzaiIiOwHx8f5ZxcXJmaWlpNWZmZRMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh/gMyYf4IcmH+DZJh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh7g/yYe4P8kGt//HBPd/6Sn8/9rbev/X2Tr/4uX8/8ZL+n/Hj7t/xxD7/8qUPH/TGnz/3OH + 9f+Xovf/dIj1/x5D7/8jR/H/JEr2/yRI8v8gOtP/GiWf/xQWfqQeGsQjm5n8dLKzrP8AAAD/AAAA/wAA + AP8AAAD/CgoK77CwsPTJyck/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAxsn5uNna + /f/a2v3/3t79/9TV/P84NeP/Ihrf/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+DVIhvfEAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ2dlDWhoZyNpaWclZmdmGWZmZQkAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACYf4AkmH+BJJh/goiYf4OkmHuD/Jh/g/yYf4P8lHuD/JR7g/yUd + 4P8lHeD/JR7g/yUe4P8lHeD/JR3g/yUd4P8kHd//JBzg/yQc4P8iGt//HBTe/yMc3/98gO7/i4/w/0ND + 5P+xuPb/Pzvj/yEX3v8mIeH/JSrk/yMz6P8dO+z/H0Tv/zBU8f8yVfH/Ikbv/yNH7/8jR+//I0fw/yRJ + 9f8kSvX/IUPp1B9B627S3P+LkpGJ/wAAAP8AAAD/AAAA/wAAAP8rKyuHx8fIxcXExWoAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADNz/vH29v9/9bX/P/Cxfn/mJvy/zAt4f8jHN//Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g8iYe3zAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJh/gFyYf4GEmH+C4Jh/g9yMe3/8qJeD/KSTg/ygj4P8pJOD/KCPg/yol4f8tKOH/LCfg/y0n + 4f8wKuH/Mizi/zs44/9aWuj/jJPw/5CX8P9vdOv/l53y/09O5v8eF9//Jh/g/yYe4P8mHN//Jhzf/yYf + 4P8lJ+P/IjHn/yE97P8jSO//I0nw/yNI8P8jR+//I0fv/yNH7/8jR/H/Fj3w/6e6//9qZ17/AAAA/wAA + AP8AAAD/AAAA7TY0LRXOzs2rxMTElgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKKn + 9MuGh+//U1Ln/y0n4f8aE97/JBzg/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P4mH+BVAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnI+EnhY/veW92 + 68tocer/dHzs/3Z/7f97g+3/hY3v/3+I7v98iO7/g5Hv/4OQ7/+Hju//i5Xw/5Wg8v+PmPH/e4Hu/11e + 6P8oIuD/Hxff/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYd3/8mHN//Jh/g/yUo4/8lNej/I0Ht/yNJ + 8P8jSfD/I0fw/yNG7/8YPu//rsD//1pYUP8AAAD/AAAA/wAAAP8DCB6DAAAAAMzMy4LExMTAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHRffyBUO3f8jHt//Ozjj/0E/5f8mIOD/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/gdwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQz/kAZ+i8ziAge6NVVXn2lxc6f9cW+j/Wlno/1JU + 5/9TV+f/UFXn/0xP5v9LSOX/QD7k/zMt4v8iG9//Hhbf/yUd4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh3f/yYc3/8mIOD/JSrk/yQ56v8jRe7/I0nw/xc/8P+yxf//oZ+W/wAA + AP8AAAD/CAwf/yFD39QfRf1rhZPVbtDNv9kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABaWuimoKL0/7q8+P/P0Pv/u7/4/y4p4f8jHN//Jh7g/yYf4P8mH+D/Jh/g/yYf4JIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAVDd0JHxjfSB8X350fF9/mIBjf/yAX3/8gFt//IBff/x8X3/8hGd//JBzf/yYe + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mHN//Jh3f/yYj4f8lMOb/Fzbr/6i5/v/v7ub/EBAP/wAAAP8cNKL/JUv//x9D8P8mSu30o63Y8Ieb + 5S4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKCv9Fne3f3/19f8/9zc/f/S1fv/Ojjj/yEZ + 3/8mHuD/Jh/g/yYf4P8mH+CoJh/gAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYf + 4BQmHuBYJh7gryYf4PAmH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh3g/yYc3/8ZE97/oKXz//// + //9OTUT/AxJE/yVM+/8jR/D/I0fv/xI47/+Ln/n/m7D67XON9owgRO8gAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAZnDqDLS3997GyPn/nZ7z/2Jh6f8oIuD/JR3g/yYf4P8mH+D/Jh/gsiYf4AcAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh7gHyYf4GgmH+C6Jh7f9iYe + 3/8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/xkS3v+jpvL//////5ubpf8WIsL/JEP1/yNJ8P8jSfD/FTvv/4yf + 9/+Zrvn/dI72/x5C7+0jR/CHI0fwGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMi7hcDMw4v8fGN//HBTe/yUe + 4P8mH+D/Jh7g/yYe4KwmHuAJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmHuAlJh/gciYe4MImH+D4Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mH+D/GhLe/3Z3 + 7P//////v8P6/xwU4f8lIN//JS7l/yQ/7P8ROO7/prj5/5Sr+P9qh/X/HEDv/yNH7/8jR/DlI0fvcCNH + 7wgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAjG98FIhrfgyYe4OEmH+D6Jh/g+yYf4OomH+CGJh/gAwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACYe4CkmH+BrJh7fryYe4OgmH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8jHN//LCfh/7q99f9oaOr/HRXe/yYe3/8lHN//Fg7d/2Jt + 7P/v9v//eZL2/2uI9v8aP+//I0bv/yNH7/8jR+//I0fwyiNH7z8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh/gECYf + 4CkmH+AqJh/gGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh/gFiYf + 4FImHuCfJh/g4iYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8jHOD/IBnf/yIa3/8lHd//HRTe/xYP3v9mZ+n/9fX9/46T8P+irPT/XHHw/xs/7v8jSfH/I0fw/yNH + 8P8jR+//I0bv+yNH74MjR+8FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmHuAQJh/gSiYf4JgmH+DdJh/g/yYf + 4P8kG+D/Hhbf/x8X3/8eF9//Hhff/x8X3/8fF9//Hhbf/x0U3/8aEt7/HBTe/ykk4P9VVef/pKjy/9LX + +f+PlfD/o6n0/56i8v8mIN//JCPh/yQ36f8jRu//I0rw/yNH8P8jR+//I0fv/yNH778jR+8rAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACYf4AsmH+BEJBzgkzEt4tpQS+b/T0rm/1VQ5/9WUef/VE/n/1NP + 5v9VVef/YmXp/3d57P+ZofH/wsv3/9vl+//Bzfj/oKjz/6Gn8/9/g+7/KCTg/yIa3/8mHd//Jhzf/yYk + 4v8lNuj/I0Xv/yNJ8P8jR+//I0fv/yNH7+4jR/BlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAb3nrCtff+kGbqPKOtbr22KKo8/+fpvL/n6by/56m8v+ep/L/kpvx/4mU7/9/he7/am3r/2Bi + 6f9NS+b/KiXg/xwU3v8kHOD/Jh/g/yYf4P8mHuD/Jh3f/yYc3/8mJOL/JTXo/yNF7v8jSvD/I0jw/yNH + 7/8jR++oI0bvGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSUOcHGBDePCEa + 34khGt/VIRrf/yEa3/8iG9//IBnf/x4W3/8dFN//HRXf/yAX3/8kHeD/Jh7g/yYe4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYd4P8mHN//JiPh/yU16P8jRe7/I0nw/yNI8P8jR+/gI0fvTQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUe4AUlHuA7JR7ghyYe4NEmHuD/Jh7f/yYe + 3/8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHd//Jhzf/yYk + 4v8kN+n/I0bv/yNJ8P8jR+/+I0bvagAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAJh7gAyYe4DYmH+CEJh/gzSYf4P4mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh3f/yYc3/8mJeL/JDvq/yNI8P8jR/DIAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAmH+ABJh/gMiYf4H0mHt/IJh/g/CYe3/8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYd3/8mHN//JTLn/yNI8IgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmHt8tJh/gdCYf + 4L8mH+DzJh/g/yYf4P8mHuD/Jh7g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYd3/8lJ+ObI0nwDAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYe4CEmHuBWJh7gkiYf4MYmHuDsJh/g/yYf + 4P8mH+D/Jh/g5CYf4LwmH+COJhzfRwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAmHuAPJh/gNSUe32cmH+CVJh/gnyYf4JYmH+BaJh/gLCYe4AwAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////+Af///////////////////AD////// + /////////////AAf//////////////////gAD//////////////////wAAf/////////////////4AAH + /////////////////8AAB/////////////////8AAAf////////////////wAAAD//////////////// + gAAAA////////////////AAAAAH///////////////AAAAAA///////////////AAAAAAH////////// + ////wAAAAAB//////////////8AAAAAAf//////////////gAAAAAH//////////////8AAAAAB///// + //////////AAAAAAf//////////////4AAAAAH//////////////+AAAAAB//4B///////////wAAAAA + //4AH//////////8AAAAAP/8AA///////////gAAAAD/+AAH//////////4AAAAB//AAA////////+D/ + AAAAAf/gAAP///////8AAIAAAAP/wAAA///////8AAAAAAAH/8AAAH///////AAAAAAAB/+AAAA///// + //wAAAAAAA//gAAAJ///////AAAAAAAf+AAAACf/////58AAAAAAP8AAAAAH/////4H4AAAAAAfAAAAA + B/////8A/gAAAAAA4AAAAAf////+AH+AAAAAADgAAAAH/////AB/4AAAAAAAAAAAB/////gAP/wAAAAA + AAAAAAf////4AD/wAAAAAAAAAAAH////8AA/AAAAAAAAAAAAB////+AAIAAAAAAAAAAAAAf////gAAAA + AAAAAAAAAAAH////4AAAAAAAAAAAAAAAD////8AAAAAAAAAAAAAAAA/////AAAAAAAAAAAAAAAAP//// + wAAAAAAAAAAAAAAAH////8AAAAAAAAAAAAAAAB/////AAAAAAAAAAAAAAAAf////wAAAAAAAAAAAAAAA + H////4AAAAAAAAAAAAAAAA////8AAAAAAAAAAAAAAAAP///+AAAAAAAAAAAAAAAAD///+AAAAAAAAAAA + AAAAAAf///AAAAAAAAAAAAAAAAAH///gAAAAAAAAAAAAAAAAB///wAAAAAAAAAAAAAAAAAf/+AAAAAAA + AAAAAAAAAAAH/4AAAAAAAAAAAAAAAAAAB/+AAAAAAAAAAAAAAAAAAAf/gAAAAAAAAAAAAAAAAAAD/8AA + AAAAAAAAAAAAAAAAAP/8AAAAAAAAAAAAAAAAAAB//4AAAAAAAAAAAAAAAAAAf/8AAAAAAAAAAAAAAAAA + AH/+AAAAAAAAAAAAAAAAABD//gAAAAAAAAAAAAAAAAAf//4AAAAAAAAAAAAAAAAAP//+AAAAAAAAAAAA + AAAAAD///gAAAAAAAAAAAAAAAAB///4AAAAAAAAAAAAAAAAA///+AAAAAAAAAAAAAAAAAf///gAAAAAA + AAAAAAAAAAH///4AAAAAA/wAAAAAAAAB///+AAHwAH//gAAAAAAAAf///gAB/g///+AAAAAAAAH///4A + A//////8AAAAAAAB///+AAf//////4AAAAAACf///gAP///////gAAAAAAH///4AH////////AAAAAAA + ///+AB////////+AAAAAAD///gA/////////8AAAAAAP//8Af/////////4AAAAAA///AP////////// + wAAAAAH//8P///////////gAAAAAf///////////////AAAAAD///////////////+AAAAAf//////// + ///////8AAAAB////////////////4AAAAP////////////////wAAAB/////////////////gAAAf// + ///////////////AAAH//////////////////AAB//////////////////+AB/////////////////// + 4A////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////8oAAAAYAAAAMAA + AAABACAAAAAAAICUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcG + KAgGBSYWBgUkFwUEHxIEBBsEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAANDE8BBQUiVgAAAr8AAADrAAAA7gAAAOIAAAWyBgYYUhISKwQAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwbXxcDAwqwBwck/g8OTv8REFf/DQxI/ggI + Kf8CAgf/AAAA/gICArQiIiwSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBtGQwM + JcUaF5L/Ix3g/iQd6P8oIuP/NTHX/j08zv8xL7L/EBFG/gAAAv4LCw+ZAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABxcowJEREwvx8awv4qJdv+UE7G/ouLyf7Bwdb+5+fn/u/v7f7m5uf+srTM/jk6 + Q/4BAQH5IShNIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIuOoRAwMDSoaGqs/qmqy//i4uH//f37/v7+ + /v/19vr/3eL0/sPM8P+0wvn/rbr5/rC+7/9cYGz+DBAjdwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAur7QAq2xxRacn6xMoaKlm8bG + x9zp6en9+vr6/u/y+//J0/j/k6b0/mB77/88XOz/KUvt/iNG7v8iRu//IUXv/iJG7/8uUOn+KjqEmwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ6i + tBSSlJ5ar7G4qs/Pz+Xw8PD+/v7+/vDx8v67w+X+cIfo/jpa7f4iRu7+IUTv/iJG7/4iRu/+Ikbv/iJG + 7/4iRu/+Ikbv/iJG7/4iRu/+JEjv7CY/1DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAqa/MAZqftx+Ym6hhqamquNjY2PX6+vr/+vr7/trf8f+ir+f/XnXf/itM5v8gRO//Ikbv/iNG + 7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNF7v8kMuf+Izvq/yNG7dIkOdsUAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAJqiyAKSmLNHq6yxs9fX1+z19fX+8fT7/svT9f+Lnuz/S2fp/ipM + 6/8iRe7/Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//I0bv/iNG7/8jRu//Ikfv/iQ2 + 6P8lHt/+JR/g/yQ46f4jQuycJDXlAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIaMqyza2tv39/j8/r3J + 9/53jvH+PV3u/iNH7v4hRe/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG + 7/4iRu/+Ikbv/iJG7/4iRu7+I0bv/iUm4v4lHt/+JR7f/iUf4P4jPev8Iz3rSwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAIKOxweanamoP2Dy/h9D7/8iRu//Ikbv/iNG7/8jR+//Ikbv/iNG7/8jRu//Ikbv/iNG + 7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jR+//I0Pt/iUg4P8lHt/+JR7f/yYe + 3/8lK+T+I0LthQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABrf9gIJUjvzyNG7/8jRu//Ikbv/iNG + 7/8jR+//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG + 7/8jRu//Iz/s/iUf4P8lHt/+Jh7g/yYe3/8lKuT+I0HtcgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAMlPrWSJG7/wiRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG + 7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+JDrq/iUe3/4lHt/+JR7f/iUe3/4kMeb+IzfpRQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ0nrCCNG78cjRu//Ikbv/iNG7/8jRu//Ikbv/iNG + 7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//I0bv/iNG7/8jRu//JC7l/iUe + 3/8lHt/+JR7f/yYe3/8kNOf4JDHmFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAGBiQIBQUeFQQEGxIFBBwFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG + 708jRu/7Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG + 7/8jRu//I0bv/iNG7/8jRO7/JSHg/iUe3/8lHt/+JSPh/yUf4P8lK+TGAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAODFQCCQg4PQIBDZ4AAALXAAAA6QAAAOgAAALRAQELgQUE + IRkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACJF7gciRu+8Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG + 7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+I0bv/iNG7/4kN+n+JR/g/iUe3/4lHt/+JTHm/iUl + 4v4lH+CNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8MXQkEBByOAgIH+AoL + NP4REVj+ExNe/g0NQv4DBA7+AAAA/gAAAdgFBR88AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjQu0zI0bu+SNF + 7v8jRu//Ikbv/iNG7/8jRu//I0bv/iNG7/8jR+//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNF + 7/8lJuL/JSzl/iYe3/8lIuH+JTTo/yUk4fIlIuAxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAEA1jEgQEE7YVE3D+Ix3W/yUd6/8lHen+JRzp/yUd6v8kHt7+FBNu/wECCP8AAAD2BQciUAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAyO+YBMz3nCzI85wYAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAI0DshiUo4/wjPOv/Ikbv/iNG7/8jRu//I0bv/iNG7/8jRu//Ikbv/iNG + 7/8jR+//Ikbv/iNG7/8jRu//Ikbv/iM56f8lKeT/JSzl/iYg4P8lMOb+JS3l/yUf4JglJOEBAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVE4EGBwgksB4atf4lHej+JR3n/yUd5/8lHef+JB3j/yQf + 1v8jIMX+Ih60/xkZbv8NEUT+CA4utRcklwQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJzLmGSY6 + 6WomMOavIyvk1SYv5csmLuWwJzDlkCY153ElMOZMJDLnLCM36Q4kMeYBKDXnBicl4ZYlH+D/JDTo/h83 + zP8iQuX/I0fx/iNG7/8jRu//Ikbv/iNG7/8jR+//Ikbv/iNG7/8jRu//I0Lt/iUu5f8lNOj/JSPh/iUi + 4f8lLeX+Jh/g4SUj4RsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABYUgAELCjZ/IRzG/yQd + 6P8kHOj+JB7i/yQgyv8kIcL+JCDE/yQfzP8lHtj+Jh7g/yUe4f8lJOT+I0Xr8SFC5ZoeNssXAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACQt5BIkNuiNJCjj9iUe3/48POT+ZGzs/mdu7f5gZ+z+JyDf/iUd3/4lHt/+JR/f/iUk + 4folKuTiJDLmwiQ06KIkKeTtHiK9/hwgp/4WF4P+GSKc/h83y/4iRev+I0fw/iNH8P4iR+/+Ikbv/iNH + 8P4jQOz+JSfi/iU06P4lKuT+JR7f/iUe3/4lHt/6JSDgSgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAABEQYjseGrL2JB3e/iEevf4jIb3+JCHI/iUf3f4lHuH+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JDnp/iJG7/4iRu/eKkrmIwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB0luRklH9u6JR7f+CUe3/8nIOD+JyDg/yUd + 3/8mH+D+Jh7f/yYe3/8mHuD/JR7f/iYe4P8lHt//JR7f/icg4P9cY+v/aHHv/lBY6f85QeD/KDDP/h8n + u/8cKLP/HCuy/hsnqf8cLbX/HTPD/h0ouP8fHLv/JCHU/iUm4v8mHuD/JR7f/iYe3/wlIN+BAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBejBhMTWr8eHZ7+Hhyf/yQezP8lHt/+Jh7g/yYe + 3/8lHt/+Jh7f/yYe3/8lHt/+Jh7f/yYe3/8lHt/+JC7l/yNH7/8iRu/+I0fvwDhW5AsAAAAAZXnUAQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAZGp8EIh7IRCUe3q8lHt/5Jh7f/yYe4P8lHt/+JR7f/yYe4P8mHt//JR7f/iYe4P8mHuD/JR7f/iol + 4P+JlfT/laH3/pWh9/+UoPf/i5b0/nqD8f9ja+7/R1Hp/i033f8fKsv/HCi3/hkkoP8WGYf/FRN9/hoW + m/8iHMf/JR7f/iUg35skJd4GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJB7bASQd2RwkHdhYIx3NqCId + w/smHt/+JR7g/yUe3/8lHt/+Jh7f/yYe4P8lHt/+Jh7f/yUe3/8lHt/+Jh/g/yUe3/8lHt/+JSnj/yNG + 7/8iRu/+I0fv/SpL5VmJmN8rk53HOwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAgIMwkFBiItCQo7Jw4OWQMAAAAAAAAAAAAAAAAlINImJR7eliUe3+klHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4sKOH+SUrn/l9l6/5weO/+gIry/oyY9f6UoPf+lqL3/pSh + 9/6HkvP+bnXu/lhg7f5CUOz+KDng/h8vy/4cKrD+Hie5wyQt4B8jJ98BAAAAAAAAAAAAAAAAAAAAACQn + 3gslHt+SJR7f1iUe3/slHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+JCrk/iJG7/4iRu/+Ikbv/lJu8biwsrzOOkFhPQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQk5OwgIKsoEBRf1AAAA8gEBC64OC1QcAAAAAAAA + AAAAAAAAIxzSASUe3h4lHt95JR7f3CUe3/wlHt//JR7f/iUe3/8lHuD/JR7f/iUe3/8lHt//JR7f/iUd + 3/8lHt//JyDg/i0o4f89POT/W2Dr/nmD8f+Ll/X/kp32/pOf9v+Un/f/h5L0/kdI5v8lIeH/JSnk+iQz + 5+kkOum5Iz3rdiQ36TskMOYRAAAAACQr4QUlId+FJB7a9iQe2P8mHt/+Jh7g/yYe3/8lHt/+JR7f/yUe + 3/8lHt/+Jh7f/yUe3/8lHt/+Jh7g/yUe3/8lHt/+JR7f/yUe3/8lHt/+JDLn/yNG7/8iRu/+MFPw/663 + 2/IeHx/4LTNMLgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANDEg2DQ5K7RUT + fP8VFHv+Bwgg/wAAAP8BAQjODApODQAAAAAAAAAAAAAAAAAAAAAAAAAAJR7fECUf4F8lHt/JJR7f/iYe + 3/8mHuD/JR7f/iYe4P8mHt//JR7f/iYe4P8mHt//JR7f/iUe4P8mIOD/UVTo/l1h6/9sde7/d4Lw/m96 + 7/9SWOn/REPm/jc04/8lHt//JR7f/iYe3/8lHt/+JSHg/yUo4/wkMufgJDboaSQv4wMAAAAAICTFPRwY + o7obF6D+Hxq6/yEbxP8iHMn+Ix3Q/yQd1v8lHtv+Jh/h/yYe4P8lHt/+Jh7g/yYe4P8lHt/+JR7f/yUe + 3/8lIuH+I0Tu/yNG7/8iRu/+ssH3/lhYWfMAAAD+Jys9IgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAABYVah0PD1PdFROA/h8Zv/4kHen+JB7i/hMSZP4AAAL+AgIPigAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAkKOMBJSXiQCUg4KslHt/yJR7f/iYe3/4lHd/+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4kHd/+S03n/oKM8v6UoPf+lJ/2/pSg9/6Voff+V1zq/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/CM66ssiRu+3IkfwpCJA5pkcLLTsGyms/hkmpv4ZJJ/+GCCY/hgckP4XGIj+FhSE/h8a + uf4mHuD+Jh7h/iYe4f4lHt/+JR7f/iUh4P4jP+z+Ikbv/iJG7/6Oo/f+tbW2/gUFBf4CAgL6LjA5EwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBxzBhcYUK4mJ5P+IRvP/yUd5/8kHef+JR3n/yQd + 5P8PDk/+AAAA1A4RWgsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIzPnAiQy5xIkLeVJJSnj0jk4 + 3f+lpcn/UVPY/iUe4P8mHuD/JR7f/iQd3/9wcdP/m53K/jAt4P9udu//laD3/pSg9/+UoPf/kp32/k9R + 6P8nIeD/JR7f/iUe3/8lHt/+JR7f/yUe3/8lHt/+JSXi/yNB7P8iRu/+I0bv/yNG7/8jR/D+I0fw/yNH + 8P8jR+/+Ikbv/yNG7v8iROr+HCy0/xYUgf8ZFZH+GhaX/xwXpP8gGr3+JSLf/yQ97P8jRu/+I0bv/z1d + 8f/x9P3+Q0NE/wAAAP8gICHxTk9SBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADxA+YUlM + p/siHM7+JR3o/yUd6P8kHef+JR3n/yUd5/8gG77+AAAA5hMbfRMAAAAAAAAAAAAAAAAAAAAAI0PtFSND + 7UgjQ+6BI0XutCNG79wjR+/3Ikbv/ixL6/+3usf/ZmbU/iUd4P8mHuD/JR7f/iQe3/9lZdT/wsPE/jw4 + 3P8oIuD/WFzq/mVr7P9iaOz/eoPx/pKe9v+Ai/L/c3rv/kVG5v8lHt/+Jh7f/yYe4P8lKuP+I0Pu/yNG + 7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNF7f8dMLz+FxuN/xkinv8cLbb+HC64/xoo + qv8XGon+GBqQ/xsoq/8fOM/+Ikbu/3KL9f/U1NT+AgIC/wAAAP9YWFjuMzM0AgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAbGmILMzZu5DQxwf4kHOL+GBWQ/hoWnP4kHeX+JB3n/iQd5/4lHuf+BAUT5hgp + oxgcNb8lIEHfXSJG7pgiRu/SIkbv+yJG7/4iRu/+I0bv/iJG7/4iRu/+Ikbv/iRH7v6fqM3+sLXJ/ixB + 5/4lKeP+JR7f/iUe3/47ONv+yMjD/lta1v4lHt/+JR7f/iUe3/4lHt/+Ixze/kRF5v6Rnfb+lKD2/l5j + 6/4lHd/+JR/f/iQz5/4jRu/+I0bv/iNG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+IUDi/hke + lv4cIKz+IkPn/iNH8P4iRu/+Ikbv/iNH7/4jR/D+IT7c/hwstP4XHJD+ExaG/qSt4P6AgYH+AAAA/gAA + AP6Ojo7kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPEEBaP0Gd/yEbyf8cGK7+GRl9/xYU + hP8kHeL+JB3n/yQd5/8lHuv+FyZ+8iJE59EiRu/xI0bv/iNG7/8iRu/+I0bv/yNG7/8jR+//I0bv/iNG + 7/8jRu//Ikbv/iJG7/+FlNT/sLfJ/mR73f8iRe//Iz7s/iUt5f8lIeD/t7nG/oCBz/8kHOD/JR7f/iUe + 3/8mHt//JR7f/iUe3/9FReb/Rkfm/ici4P8lJuL+Iz3r/yNG7/8iRu/+I0bv/yNG7/8iRu7+I0bv/yNG + 7/8iRu/+I0bv/yNH7/8iQub+GBuR/yEbxf8mH+D+JDnp/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNH + 8P8iRu3+GzPJ/5CVwf8tLjr+AAAA/wUFBf/CwsPRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABoa + XQIKCzutNDWY/yAazP8XFIv+FRR6/xsXpP8kHej+JB3n/yQk6P8jPe3+I0fx/yNG7/8iRu/+I0bv/yNG + 7/8iRu/+I0bv/yNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iJG7/9mfN3/jp3S/qevy/8nS+7/Ikbv/iJH + 7/8gP+3/k53P/qKlyv8kJ+P/JSTh/iUi4f8lI+H/JSTh/iUp4/8kL+b/JDfp/iM/7P8jRu/+I0bv/yNG + 7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yJG7v8ZIpz+IBu//yYe4P8lHt/+JSbi/yNG + 7/8iRu/+I0bv/yNG7/8iRu/+I0fv/yNB7f8kOOn+JDTr/xgeWP8AAAD+AAAA/xYWF//p6eqoAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAABQVUxALC0LgJSeK/h8ZxP4dGLL+GRWW/iMc3f4kIef+IzXr/iNG + 7/4jRu/+I0bv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbu/iJG7/4iRe/+I0bv/iJG7/4pTe/+JUjv/ihL + 7/5cd+f+mKPP/oua0/5iet7+IkXv/muG9P46WvH+aH7c/rq8xv4vUuz+Ikfv/iJH7/4iR+/+Ikfv/iJH + 7/4iRu/+Ikbv/iJG7/4iRu7+Ikbv/iJG7/4iRu/+I0bv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/h41 + yP4bF5/+Jh7h/iUe3/4lHt/+JDPn/iJG7/4iRu/+Ikbv/iJG7/4jRe7+JCzk/iUd3/4lHt/+Jh7h/hMS + aP4AAAD+AAAA/jk5Ofufn6ZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsNNygNDUv3FhV7/xwX + rP8kHub+JCXZ/yQ11f8jQ+v+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/y5R + 8P9Sc/P/JEjv/iFF7/9wi/X/JUjv/per+P/k6fn/srfJ/khl5f+rssr/I0fv/nKO9f8rTvD/RWPl/sDC + xP9IZeX/KEvw/iNH7/8iRu//IUXv/iJG7/8jRu//Ikbv/iNG7/8mSe/+Ikbv/yNG7/8iRu/+Ikbv/yNH + 7/8iRu/+Ikbv/y5R8P8mSu/+I0bu/xkfmf8jHdH+Jh7f/yUe3/8lJ+P+I0bv/yNG7/8iRu/+I0bv/yNF + 7/8kLOT+JR7f/yUe3/8lHuD+JR7g/yActv8BAQT+AAAA/39/gcNTUm8IAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAsRQTsMDEP+FBJ6/x8psP8jPt7+I0br/yNG8P8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG + 7/8iRu/+I0fv/yNG7/8iRu/+I0bv/yJG7/8tUPD/T3Dz/iJF7/9lg/T/Ikbv/mmE9P82WPH/srfJ/jxb + 6P+UoND/sr3l/qS4+f8oTPD/K07s/re7x/9pftz/Wnn0/jda8f9Ia/P/c4/1/lx58/8hRe//Jkrv/jdY + 8P90j/X+JEjv/y5T8P8hRe/+Z4P0/4ad9/8kSO/+NVnx/0hn8v8xVPD+IT7c/xsZof8mH+H+Jh7g/yUf + 4P8jPOv+I0bv/yNG7/8iRu/+I0bv/yQu5f8lHt/+JR7f/yUe3f8bF6r+Kiig/yYmi/8AAAX+AQEG/0NB + pZkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0YV0kQGVj+Gyuy/iJG7/4iRu7+Ikbu/iJG + 7/4iRu/+Ikbv/iJG7/4iRu/+I0bv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iR+/+Nlnx/l18 + 9P5Zd/P+JEjv/mB99P4dQu/+kp/Q/lt03/5EYub+p6/L/mWD8/48XfH+IUXv/pmkz/6PnNH+Smrz/kxt + 8v4iRu/+HkLv/kdn8v5rhvT+ZoP0/rDA+f5BYvH+JUnv/khr8v4hRe/+N1vx/jpc8f47XPH+Y4H0/myI + 9f4pTfD+HTG//iEbw/4lHt/+JR7f/iUl4v4jRu7+Ikbv/iJG7/4jQ+3+JSnj/iUe3/4lHt/+Jh7g/hoW + nv5gYqL+6enp/ufn6P6Qk6z+Hx+H/iYf4O5GRrAhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHTnGHSBB + 264iRer+I0bv/yNG7/8iRu7+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG + 7/8jRO7+Izzr/yQ06P8kLeX/JSrj/qWu8/9gaOv/IiXi/p2n8/+NnvT/l6La/oKN0/8iOOv/v8PP/mZ8 + 4f9KaPH/IkTu/nKF2f+zuMj/NFfx/khn8v8iRu//Ikbv/iBE7/9FZvL/epf2/kVl8v9gffT+Ikbv/4Kc + 9v8wUvD+T3Dz/5Op+P8xVPD+PF/x/32V9v8hRe/+HCmv/yQd1f8lHt/+JR7f/yQv5v8iR+/+I0bv/yQ6 + 6v8lI+H+JR7f/yYe3/8lHt/+IhzM/zY3j//f3+T+6enp/+np6f/q6ur+r7HQ/ys41v49RNJhAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAhQ+M/Ikbv3yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu7+I0bv/yNG + 7/8iRu/+I0bv/yJG7/8iRfD+Izrr/yUr5f8lIeH+Jh7f/yYe3/8lHt//JB3f/jo+4/+QlO//Lijh/l9e + 6P+5vPX/xMnm/qOkyf8jHN//eXvd/qyvy/9YXuf/JSLh/kxO2v/FxsP/PUXi/kRL6P9baO3/f5Py/pOm + 9v+UofP/UFrq/mdx7P8zQej+IzHn/2Jw7f+7w/f+Ljvn/z1G6P8kL+b+JjXn/yQ36f8jQO7+GyOn/yUe + 3P8lHt/+JR3f/yQ16P8jRu/+JDDm/yUf4P8lHt/+Jh7f/yYe4P8lHuD+Gxeq/6Wnx//q6ur+6unp/+rq + 6v/p6en+6+rq/42Z3f8oSOqhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIkXuAiJG72kiRu/2Ikbv/iJG7/4iRu/+Ikbv/iJG + 7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+KErk/lBlwv5scbX+V1a3/iYf3v4lHt/+JR7f/iUe + 3/4lHd/+MC/h/jIy4v4uKeD+Q0Lk/iUe3/5oZ+n+iInm/rW2xv4xLN7+JR/f/p+hyv51edr+JR3f/jEs + 3f67vMX+XFzY/jk24/4xK+H+goTt/mlr6f4iGt/+JR7f/iUe3/4lHt/+JR7f/iUd3/4hGt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4mHuH+Gxmg/iUe3v4lHt/+JR7f/iUl4v4lJOH+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4mHuH+GhiD/l9gZf7Ly8v+sLCw/re3t/7Y2Nj+6urq/tfb6v4rTu/tXG/eCQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApRecCIkbufSNG + 7/gjRu//Ikbv/iNG7/8jRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yND7f84S87+kZWw/7O0 + wf+1tsX+f4Cw/yYf3v8lHt/+Jh7g/yYe4P8lHuD/Oznj/lZY5/9cYOj/Kybg/iUe3/8lHd//KiTe/rKz + x/9KSNn/JR7f/lZU1/+nqcn/JR7f/icg3/+eoMr/f4HR/jQz4v8mHuD/JB3f/lJS5v9LSuX/JR7f/iYe + 4P8lHt/+Jh7f/yYe3/8lHt/+JR7f/yUe3/8lHt/+Jh7f/yYe3/8mHuL+Gxih/yUe3v8lHt/+JR7f/yUe + 3/8lIOD+JiTh/yUe4P8lHt/+Jh7f/yYe3/8mHuH+EA9X/wEBAf8MDAz+AgIC/wUFBf8TExP+WVlZ/93f + 4/8/Xu7+VGvdLwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAsSutkI0bv/CNG7/8jRu//Ikbv/iNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG + 7/8jRO7+IzLo/0RGvv+io7X+trjH/6Okr/+foKn+SUe5/yUe4P8lHt/+Jh7g/yYe4P8lHt//JR3f/jEs + 4f8vKuH/JR7f/iYe3/8mHt//JB3g/p6fy/9qadT/JR7f/ikj3/+oqcn/UE7Y/iQd4P98fND/naDL/l5j + 6P8kHN//JR7f/iYf4P9gZej/JR3f/iYe4P8lHt/+Jh7f/yUe3/8lHt/+Jh7g/yYe3/8lHt/+JR7f/yUe + 3/8mHuH+Gxed/yUe3f8lHt/+JR7f/yUl4v9Xa/D+R1Pr/yUe3/8lHt/+JR3f/yYe4P8mH+H+Dw9X/wAA + AP8AAAD+BwcH/1lZWv9FRUX+Ly8v/+Tk5f9OaOz+T2bUSwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAFNR0QJUUtERW1vVKkhJz0ZGR9BbPT7OVDY91EEiQeniIUTt/iJG7/4iR/D+Ikbv/iJG + 7/4iRu/+Ikbu/iJG7/4iR+/+I0Lt/iQy5v4kIOD+S0m5/qqruf62t8b+tbbG/ra3xv6ytL3+NC/I/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JBzf/n5+0P6Qksz+Ixzf/iQe + 3/5bW9b+oaLJ/igh3/5SUNf+uLnF/pme7f54eOz+WVjn/oeL7f5PTuX+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4mHuH+Gxec/iUe2/4lHt/+JR7f/jk75f6UoPb+SlHp/iUe + 3/4kHN/+TFLn/iUe3/4mHuH+EhBn/gAAAP4NDQ3+x8fH/uvs7P7r6+v+hISE/pWVlv5GYd3+RlzNTgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQj/KIykkwcEjHb/lX2Tc9H2I6/t7g+r+aHDh/kdI + 0Ph2gen+aHTm/1lo5P9LXeT/OlHk/ixH5v8kROr+I0Tu/yM86/8kLeX+JSDg/yUe3/9HRbz+qqu6/7a3 + xv+2t8b+trfG/7a3xv+3uMf+UVC1/yUd4f8lHuD+Jh7f/yUe4P8lHuD/JR7g/iUe3/8mHt//JR7f/iUe + 3/8lHt//JR7f/l1c1v+0tsb/Ix3g/iYe3/8mId//rrHH/k5N2P8wLN3/w8TD/kxK2/90dev/io3u/klG + 5f8lHt//JR7f/iUe3/8lHt/+Jh7f/yUe3/8lHt/+JR7f/yUe3/8lHt/+JR7f/yUe3/8lHuD+Gxeg/yQd + 1f8lHt/+JR7f/yUg4P9zgfL+YWjs/yUe3/8jG9/+c33v/y8q4f8lHuD+GBaL/wEBA/94eHj+6+vr/+np + 6f/p6en+oaGh/yQkJf9CXN3+P1PLRQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARkLLGTUx + yryAgN/4m53p/qWo7P6zsvD+Pjvd/ich2f4xLdv+NzTa/j8/2f5TVt7+b3fl/n2H6v5sd+P+IyHS/iUe + 3/4lHt/+JR7g/jo4xv6lprb+trfG/rW2xv62t8b+trbG/ra3xv63uMj+mZyz/lJFlP5QJWP+VyZR/lIl + Xf5IJH3+OSKn/iwgzv4kHuL+JR7g/iUe3/4lHt/+JR7f/kM/2v7FxsP+My/d/iUe3/4lHt/+Z2bU/pqb + y/4jHeD+rK7I/mVl1P4kHd/+JBzf/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+HRms/iIbx/4lHt/+JR7f/iUe3/5UY+7+ZnPv/igi4P4kHN/+d4Dw/k5Q + 6P4lHt/+IRzC/hQVKP7Pz9D+6ejp/unp6f7s7Oz+RERE/kJDSv4yT97+LzLcnTtC2BUAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUlegUrKzwTbGz9Iu3u/fGSknl7yUd3/4lHt/+Jh7f/yYe + 4P8mHuD/JB3e/iMc2f8oItv+JR7e/yYe4P8lHt/+JyDc/4aHrv+2t8f+trfG/7a3xv+2t8b+ra27/6Ce + qP95XV/+aDo3/2UqJv9lKCP+ZSgk/2UoJP9lKCP/Zigg/mYoIv9cJ0D/RSSG/i8gxv8lHuH/JR7f/jAr + 3v+7vMX/VFPX/iUe3/8mHuD/LSfe/q6wx/9LSdn/goPP/o2Ozf8jHOD/JR7f/iUe3/8mHuD/JR7f/iUe + 4P8lHt/+Jh7f/yYe4P8lHt/+JR7f/yUe3/8lHt/+Jh7g/yYe3/8lHt/+IRvB/x4Zsf8mHuD+Jh/f/yUe + 3/8jOOn+IzPn/yYe3/8kHN/+cHrv/3mC8f8lHt/+JR7e/0ZGnv/i4uH+5+fn/8bGxv9gYGD+AwMC/1xf + g/8fPOL+Zm7t/2tz7u8zMuNyODbjAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABeY+YGPEPlPSM76vUkNOf+JSPh/yYe3/8mHuD/JR7f/iYe3/8lHt/+Jh7f/yYe4P8lHt/+ODTI/6yt + vP+1tsb+trfG/7a3xv+lprL+f3d7/2g2M/9lJyP+ZSgj/2UoJP9lKCP+ZSgk/2UoJP9lKCT/ZSgj/mUo + JP9lKCP/ZSgi/mIoLP9SJWD/MyG4/igh3v+kpcn/eXnQ/iQd4P8mHt//JR3g/m5v0/+Xmcz/WlnW/qyt + yP8nId//JR7f/iUe3/8mHuD/JR7f/iUe3/8lHt/+Jh7g/yYe4P8lHt/+JR7f/yUe3/8lHt/+Jh7g/yYe + 3/8lHt/+JB7W/xoXm/8mHuD+JR7f/yUe3/8kNej+Iz/s/yUf4P8kHN/+cHrv/5Gd9v89POT+JR7g/zAs + yP+kp8j+iIiI/wwMDP8BAQH+UFFZ/zhGs/8kOer+Jx/g/zs65P9fZuv7LyrhWQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjjlYiQ56fwlIeH+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+S0m+/rS1w/61tsb+tbbG/qusuf6IiI7+ZzQx/mUnI/5lKCP+ZSgj/mUo + I/5lKCP+ZScj/mUoI/5lKCP+Zycf/mYnIv5mJyH+Zicg/mcnHv5mJx/+Zici/lMlWP6Ui6/+mpvH/iAb + u/4iHMn+JR7Z/jIt3P62uMb+WFnX/ri6xv44NN7+JR7g/iUe4P4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+Jh/g/hsXn/4kHdb+JR7f/iUe3/4kLuX+Ikfv/iQz + 5/4kHN/+b3jv/pSg9/59h/L+Jh/f/oOE7f6Okaj+T1Jy/ggJFv44OVb+Pkmw/iFB5fslK+S5JR7f9SUe + 3/4nIeD6Mi7iPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4OOQFJiXi6TlB + 5/8lHt/+Jh7f/yUe3/8mHuD/JR7f/iYe3/8lHt/+Jh7g/yUe3/8lHt/+Vla7/7W3xP+1t8b+trfH/5SU + nf+Dc3f+ZSci/2UoJP9lKCP+ZSgk/2UoJP9lKCP+ZSgk/2UoJv9HNoX/L0HM/ixF2v9VZdz/ZXDX/mlx + 0P9pbMf/aWe8/k5NsP9PW8H/YXfX/h8wtv8bKq3/GSWn/hghn/9pcLT/goi3/qirvP9MTKL/GhaZ/h0Y + qP8fGrr/IhzL/iUe2f8mHuD+JR7g/yYe4P8lHt/+Jh7f/yUe3/8lHt/+JR7f/yUe3/8lHt/+Jh7f/yEb + xv8eGrP+Jh7g/yUe3/8lIeD+I0Xv/yNG7/8jQOz+X3Xy/5Sg9/+Woff+R0nn/8PF8/9XWFj+Bwoh/yI4 + 2/8gQef+I0bw/yM+6tssNcwOLzPYCyws4CEuK+EbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAA6OOQfJR7f/k1P5/9mbOz+PDvk/ywn4f8oIuD/Jh/f/iUe4P8lHt/+JR3f/yUd + 3/8kHeD+XFy5/7a3xf+1t8b+t7jH/5CRmf97XmD+ZSgj/2UoJP9lKCP+ZSgk/2UoJP9lKCP+ZSgk/2Eq + MP8nQuD/Ikbv/iZJ7/9zh/T/hZX1/oeW9v+Il/b/iJf2/m6D9f8hRe//Ikbw/iJH8P82V/H/dIf1/nyO + 9f94ivP/cYbw/muB7P9Tad7/M0bJ/iU1t/8cJ6T/GB6U/hcZj/8ZF5b+HRms/yEbxv8kHdj+JR7f/yYe + 4P8lHuD+Jh7g/yYe4P8lHt/+Jh7f/yUe3v8cGKL+JB3V/yUe3/8lHt/+JDbo/yNG7/8iRu/+MlPw/4mX + 9v96hvL+REXl/7e4wv8HBwf+Bgoc/yJE5P8iRu/+I0Xu/iUu3XIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLSOY6JR7f/iMb3/4xLeL+Z27t/nmE + 8f5yfu/+cHjv/m537/5veO/+am7u/mRq7P5fZez+c3jC/rS1xP61tsb+trfH/pOUnf57XmD+ZSgj/mUo + I/5lKCP+ZScj/mUoI/5lKCP+ZSgj/mUoJP5RKWH+LSrN/iQr5v4kLOT+Ji/l/icx5v4nM+b+JzTm/iY0 + 5v4kMub+JDLn/iQ26P4nQOv+Ql3w/lpz8/5xhvT+hpX2/pOf9v6Yovf+laD2/o2b9/6Bkvf+b4P1/ltx + 7/5CW+P+KULS/hwwvv4bJ7H+HSGu/h4bsv4fGcD+IxzS/iUe3P4mHuD+Jh7h/iUe3f4fGrT+FxSH/iMc + zv4lHuD+JSHg/iNB7f4iRu/+Ikbv/jBS8P4qS+7+gJX1/lRUVf4AAAD+AwYU/iFC3v4iR+/+JDLlwSQ0 + 3QwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABmZOpRc3Ps/5eY8v+vsvX+MCzh/yMc3/8jHN//JBzf/iYg4P8yMOL+XGHr/3R98P98hfH+gIjZ/6qr + uv+jpLD+t7jH/5mapP+VkZn+ZjIu/2UnI/9lJyP+ZSgk/2UoJP9lJyP+ZSgk/2UoJP9lKCT/ZSgl/lAk + Yv80IbT/JB3e/iUe3/8mHt//JR7f/iYe3/8mHuD/JR7f/iYe3/8mHd//JR7f/iUh4f8jJuP/Ii/m/ig8 + 6f81Tu3/SWLw/lt08/9tgvT/fY71/oqY9v+UoPb+lqH3/4mY9v9xhfX+VnD1/z1b8P9rgOL+QFTF/0ZP + rf8YHZf+FxWH/xUSfP8UEnn+FBN6/xYTgP8hG8T+Jh7g/yUm4v8jQ+3+I0bv/yNG7/8fRO7+qrPS/wwM + DP8AAAD+AwYN/yFB1v4kMt7LJTS9FQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC2tvZt0ND7/9DQ+v/P0fv+Qj7k/yUe3/8lHt//JR7f/iYe + 3/8lHt/+JR3f/yUe3/8mH9/+Pz7j/5KWs/+lprL+trfH/7CxwP+QkZj+lJCY/3hcXf9uRUT+ajw5/2k4 + Nf9oNjP+aTc0/2k5Nv9sPz3/c1BQ/oFqbf+SiZL/j5DF/lRT0/4qJNv4JR7f/iUe3/8lHt//JR7f/iYe + 3/8mHuD/JR7f/iUe3/8lHt//JR7f/iUe3/8lH9//JSHg/iQm4v8kLuX/JDrq/ilF7f8yUu/+Q2Hx/1ly + 8/9xhfT+hpX2/5Cc9v+1vvn+o7H5/6+9+v9FYe/+LUrh/x81xP8YIp7+FRWA/xQSef8VE33+Hxq4/yUe + 3/8lJeL+Iz7r/yNG7/9BYvL+fn5//wAAAP8AAAD+BAUK/ykuuKsjKocVAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACsr/V0paf0/nFx + 7P5FQuT+JyDf/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4oIt/+f4ny/o6X3v6io7H+trfH/ra3 + xv6xssD+k5Sc/q2uvP6ztcT+r7G//q2vvf6srbr+ra68/q+xv/6wssD+qqy5/p2eqOmNjpXCeniTjWhm + lGBAPb44KSLdeyUe39olHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR3f/iUf3/4lI+H+JSzk/iM16P4hP+z+JUjv/jFT8P6Emfb+lKX3/rrD+v6Woff+kJ32/n2O + 9f5bdPT+J0Xj/hwuuf4WGIf+FBJ5/hsXof4lHtr+JR/g/iQn4/6JlfH+KCgo/gAAAP4AAAD+CwsL/nV3 + h2MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABBPON8Ihvf/y8p4f83NOL+JR7g/yYe4P8mHuD/JR7f/iYe3/8lHt/+Jh7f/yUd + 3/tRU+eLhY7vb4yV7YOLktKZkJGew6qruPm1tsX+t7jI/7a3x/+2tsb+sbLB/6iotfeam6TbjIuSsoJ9 + gYZ5cXNTeGxyJHhrfgUAAAAAAAAAAAAAAAAAAAAAAAAAACoj2wUmHt9CJR7foiYe3+gmH+D/JR7f/iYe + 4P8mHuD/JR7f/iYe3/8mHuD/JR7f/iUe3/8mH+D/JR7f/iYe4P8lHt/+Jh7f/yYe4P8lHd/+JSHg/y01 + 5v+gr/b+UWvx/1589P8zVPD+XXbz/4OS9f+VoPb+hpb2/y1P8P8jRu7+HjXJ/xgfl/8WFIH+Hhm08CYg + 3tiqrNT6AAAA/wAAAP8AAAD+HBwc/qurrpAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACHiO+Ira/1/8LD+f/Bw/j+KCLg/yYe + 4P8mHt//JR7f/iYe3/8lHt/+Jh7g/ych4K0+OuAGAAAAAAAAAAAAAAAAl5q4B4qLljSKipJshoeMkoSF + ioyFhYpxiYiOTYaEiSePiZAOjoeNAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJR3fASUe3xklHt9fJR7fvSYe3/IlHt/+JR7f/iYe4P8mHuD/JR7f/iYe3/8mHuD/JR7f/iYe + 4P8lHt/+Jh7g/yYe3/8lHt/+JR/f/3h77P9eX+j+j5Tw/zQ25P8kMOb+Izzq/yZH7v89XPH+Ynrz/zZW + 8P8iRu/+I0bw/yJG7v8gPNj+HzK+jkVRwzivsLTvAAAA/wAAAP8AAAD+Ozs8ura2uK0AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAC0tfaW0ND7/s/P+/6ztfb+Kybg/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f1zAq4RoAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJR7eAiUe3yomH996JR7f1yQd + 3/0jHN/+Ixze/iMc3/4jHN/+Ix3f/iQd3/4kHt/+Jh/g/ism4P5IRuX+fYHt/nh97P6DiO7+VFPn/iUe + 3/4lHt/+JR7f/iUi4f4lLOT+Iznq/iJE7v4iR+/+Ikbv/iJG7/4iRu/+Ikfv/klo8uqKior9AAAA/gAA + AP4AAAD3SUtUOsXFxcV6gJoGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACHie+bZGPp/zUx4v8hGt/+Jh7g/yYe4P8mHuD/JR7f/iYe + 4P8lHt/uJh/fLgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAKyXgAkFA5Ddtc+uRfITt4HqC7f59he3/hY3v/n6H7v+Bj+7+gY7u/4SL + 7/9/h+7+dHrs/15g6f8pI+D+JR7g/yYf4P8lHt/+Jh7g/yYe4P8lHt/+Jh7f/yUg4P8lLOT+Izvr/yNF + 7v8iR+/+I0bv/01s8v+Dg4P+AAAA/wAAAP8PFzmsU16QB8HBwsB5hLcdAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLR+WNWFbo/3t7 + 7f+Hiu/+JR/f/yYe4P8mHt//JR7f/iYe4PIlHt9QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU1LmEUZD + 5E04NOKlMy7h6TIt4v0wLOH+Lyvh/y0o4P8pI+D+JR7f/yUe3/8lHt/+Jh7f/yYe3/8lHt/+JR7g/yYe + 4P8lHt/+Jh7f/yYe3/8lHt/+JR/g/yUk4f8kMuf+I0Ht/01s8v/R0dH+AgIC/wYLH/8hQtv4JEbov4yZ + 0tOBkM87AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACNk/BN09L7/s/P+/7P0Pv+LSng/iUe3/4lHt/+JR7f+iUe32gAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQkDjGjQv4WApIuC4JR7f+SUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR/g/kpP + 6P78/P3+MDAy/hkxnf4jRvD+Ikbu/jtb8P6UqPbSc4zxV1hz7AUAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB0dOwFn6Dz2ZOT8f9aWOf+Jh/g/yYe + 4P8mHuD7JR7fbSUe3wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAMCvhHCgi4G4mHt+/JR7f9SUe3/8lHt/+JR7f/yYe4P8lHt/+JR7f/yUe3/8lHt/+Jh7g/yUe + 3/8lHt/+Jh7f/yYe3/8lHt/+Jh7g/0lH5f/7/P7+gIK3/yMz5f8jQ+3+I0fv/z1d8f+Uqfj+bIf1/yRI + 79ExU/BUJ0rvBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAASUXlVyYf4OwlHt/+Jh7f/iYe3+4oIOBqJR7fAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlHt8FJR7fKiUe33YlHt/BJR7f7yYe + 4P0lHt/+JR7f/yUe3/8lHt/+JR7f/yUe3/8lHt/+Jh7g/yUe3/8lHt/+JR7f/y0n4P/Nz/f+dnfr/yQd + 3/8lIeD+Iy7m/3WL9P+Oo/f+YH30/yJG7/8iRu/7I0bvuCJE7jMiRO4BAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEpF5SMzLOFUMCnhVy8p4SoAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAlHt4CJR7fICUe314lHt+sJR7f8iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/41L+L+KCLf/iUe3/4iG9/+ZGXo/s3Q9/6NmfL+SmXw/iJH7/4iRu/+Ikbv/iNG + 7/AiRe56IkPtAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJR7fDyUe + 31MmHt+jJR7f5SUe3/4iG9/+Ihvf/yIa3/8iGt/+Ihvf/yIb3/8kHd/+My7h/15c6P+gpfH+tbv1/5GY + 8P+PlPD+Jh/f/yUp4/8jPOv+I0bv/yNG7/8iRu/+I0bvuyND7ScAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnIN8BKyTgFTMv4U+Bgu2ghoru45WW8PyOkO/+jZDv/5Ka + 8P+aovH+qbT0/6y09P+Xn/H+gIPu/1JS5/8pI+D+Jh7f/yYe3/8lHt/+JSjj/yM86/8jRu7+I0bv/yNG + 7+UiQ+1eIz/sBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAU1PmEkI/5E04NOKbNDDi6jQw4v40L+L+MCzh/ikj4P4kHt/+JBzf/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lKOL+Izzr/iNG7/4iRu/8IkXunyM96xMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASEXkCDk14kYxK+GWJR7f3iUe + 3/0lHt/+Jh7g/yYe3/8lHt/+Jh7f/yUe3/8lHt/+Jh7g/yUe3/8lHt/+Jh7f/yUo4/8jPOv+I0fv/yNG + 79IjOuoZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAPzvjEC4o4UMnIOCTJR7f3CUe3/klHt/+Jh7f/yYe3/8lHt/+Jh7g/yUe + 3/8lHt/+Jh7f/yYe3/8lH+D+JC3l/yNE7u8jO+omAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJR7eDCUe + 30AlHt+KJR7f2iUe3/0lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR3f9iQ16GsjNugCAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJR7fAiUe3yclHt9lJR7foyUe39slHt/uJh7g4iUf + 4KclIOByJSXiJQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJR7fAyUe3xAlHt8XJR7fEiUf3wMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////g//////////////+AP/////////////8 + AH/////////////4AH/////////////wAD/////////////gAD////////////4AAD////////////gA + AB///////////4AAAA///////////gAAAAf//////////gAAAAf//////////gAAAAf//////////wAA + AAf//////////4AAAAf//////////4AAAAf/w////////8AAAA/+AP///////8AAAA/8AH///////+AA + AA/4AD//////x/AAAA/wAB//////AAAAAB/gAA/////8AAAAAD/gAAf////8AAAAAH/AAAL////+AAAA + AH4AAAD////hwAAAAHgAAAD////A4AAAAAgAAAD///+AfAAAAAIAAAD///8AfwAAAAAAAAD///4AP4AA + AAAAAAD///4APAAAAAAAAAD///wAAAAAAAAAAAH///wAAAAAAAAAAAH///gAAAAAAAAAAAH///gAAAAA + AAAAAAH///gAAAAAAAAAAAH///gAAAAAAAAAAAP///gAAAAAAAAAAAH///AAAAAAAAAAAAH//+AAAAAA + AAAAAAH//4AAAAAAAAAAAAD//wAAAAAAAAAAAAD//wAAAAAAAAAAAAD/gAAAAAAAAAAAAAD/AAAAAAAA + AAAAAAD/AAAAAAAAAAAAAAB/wAAAAAAAAAAAAAAf+AAAAAAAAAAAAAAf/AAAAAAAAAAAAAAf+AAAAAAA + AAAAAAA/+AAAAAAAAAAAAAP/+AAAAAAAAAAAAAP/+AAAAAAAAAAAAAf/+AAAAAAAAAAAAA//+AAAAAAA + AAAAAB//+AAAAB8AAAAAAB//+AA4Af/AAAAAAB//+AB////4AAAAAA//+AD/////AAAAAA//+AH///// + 4AAAAA//+AP//////AAAAAP/+AP//////4AAAAD//Af//////+AAAAA//h////////wAAAAf//////// + ///AAAAP///////////wAAAD////////////AAAB////////////4AAA/////////////AAA//////// + /////4AA//////////////AD//////////////4P//////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + ////////KAAAAEgAAACQAAAAAQAgAAAAAABgVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAgHMRsDAxZxAwMVhQICEXEFBBspCAggAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcGmkCCAglbgYG + IfMMDED/Cgk4/gUFGv8AAAD7BgYKniAgKwoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFhYgwEdHT6FGRaV/SQd4v8vKt3/UU7R/2Bf + y/88PI//BwcT/hIUHYQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAADY3SW87OqX9eHfS/7i52f7p6ur//f38/vj5+//s7vf+pqmz/x4d + HO0hLWIMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvsHNAa+y + whaipbBWsLG2ps7Oz/nx8vX/3OL6/6e39v92jfD/S2fr/zJT7P8qTfD/LlHw/ztUw/8kNIUyAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKOmtxujpbBlvr/FtuDg4PD19fX+zNLs/4mb + 6P5HZOz/JEju/yFF7/4jRu//Ikbv/iNG7/8iRu/+I0bv/yJG7/4lRuirJDbGBAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAJ2jwRego7NovLy/vebn5/fq7fj+tcDw/3OI6f83Vuf/Ikbu/yJG7/8jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//I0Ht/yUj4f8kOOn+I0DocgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjZS2GtPT + 1efk6Pj/obH1/1137/8uUO3/IUXv/yJG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//JDDm/yYe3/8lH+D/Izrq9SM66SsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhpLIBIGNwaAkSO/+Ikbv/yJG + 7/4jRu//Ikbv/iNG7/8iRu/+I0bv/yJG7/4jRu//I0bv/yJG7/4jRu//Ikbv/iNG7/8iR+/+JSbi/yUe + 3/4lHt//JSfj/iM+62IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFhx5QwjR+/jI0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//JSHh/yYe4P8mHuD/JSrk/yM5 + 6kUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlSO1tI0bv/iJG7/4jRu//Ikbv/iNG7/8iRu/+I0bv/yJG + 7/4jRu//I0bv/yJG7/4jRu//I0bv/iNG7/8jP+z+JR7f/yUe3/4lHt//JC7l+yQw5hUAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGBSIBBAQXDgMDFAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAjRu4NI0bv2SNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8kMeb/JR7f/yUe3/8lJeL/JSXizgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAADQtRCgYFJHcBAQXPAAEC7gAAAOsAAAPDAwMXTAYGJAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAI0TuWyJH7/4jRu//Ikbv/iNG7/8iRu/+I0bv/yJG7/4jRu//I0bv/yJG7/4jRu//I0bv/iNE + 7v8lJuL+JSDg/yUj4f4lL+b/JSDghgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOC1YeBwckyBQS + dP8dGaz/IBu5/hsXof8ODUz+AAAB+wMDEokNEVADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAND7nBzM95wQAAAAAAAAAAAAAAAAAAAAAIzfpAiQ8 + 67YkNej+I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yQ26P8lLuX/JiDg/yUv + 5v8lJuLpJSPhGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABIQbRcPDlPVIx3Y/yUd6P8lHej/JB3l/yQe + 2P8jHsf/Fxds/wkMLf4OFlpFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJCrkBSY051gmL+WrMTrm3EBK6dQqL+S3Ji7lmCUr5HQkMudUJDXoMSc46CQmIuHGJCre/h4w + wv8gPtz+I0fv/yJG7/4jRu//I0bv/yJG7/4jRu//I0Ds/iUw5v8lK+T+JSDg/yUn4/4lIOBnAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAFxSDBBIQYrIkHeT/JB3k/iQg0f8kIMz/JSDK/iUf0v8lHt7+JR7h/yUp + 5P4iRezaHzvWUgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgJ8oEJC7ksiUf + 3/4nIeD/R0jn/z495f81M+P/JR7f/yUe3/8lHt/+JR/g/SUj4fgrL+TwO0TZ/ycuwf8bIaj/Gyas/x82 + yf8gOtT/ID7c/yFC5f8iNt//JSTg/yUt5f8lH+D/Jh7g/iUf36glI+ECAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAExFnXyAduP4gHa7/JB/F/yUf3v8mHuD/Jh7f/yYe3/8mHt//Jh7f/yUe3/8jQu3/I0bv/CpM + 61YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGxqlFyMe0XwlHt/lJh7f/yYe + 4P8lHt//JR7g/yUe3/8mHt//Jh7g/yUe3/9VWOn/lqL3/4+a9f9/ifP/aHLv/09Y5f8yO9L/Hie8/xok + qf8YHJD/FxWL/x4Zsv8kHtn/JR/fxSQl3xAAAAAAAAAAAAAAAAAAAAAAAAAAACQd1xwjHdNcHhqk4iQe + zf8mHuD/Jh7f/yYe3/8mHuD/Jh7f/yUe3/8mHuD/JR7f/yYe3/8jO+r/I0bv/yNH7+E/WtkToarVNwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAACAcvHwUFHVsICTY4EhFwAQAAAAAiHb8IJR7aYiUe380lHt/9JR7f/yUe + 3/4lHt//Jh7f/yUe3/4mIN//PDrk/lJV6f9kaez+dn/w/4qV9P6Wovf/jJj1/3V98P5fa+//P07m/iEt + zf8dKrr1IzLiWyMs4hUAAAAAAAAAAAAAAAAlI95RJR7f2SYe3/0lHt/+Jh7f/yUe3/4lHt//JR7f/iYe + 3/8lHt//JR7f/iUe3/8lHt/+JR7f/yUe3/4jPOv/Ikbv/iNH7/6Zpdi5QUZdawAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAMC0VLDg5Q7AgIKf8AAAD6BQQhigAAAAAAAAAAAAAAACUe3gYlHt9IJR7ftiUe3/kmHt//JR7f/yUe + 3/8lHt//JR7f/yYe3/8lHd//JR3f/yol4P9HSOf/anPu/3uG8f90ffD/dHzw/0FA5f8lH+D/JSPh/iUs + 5O4kNOi/JDjpfiQx5hYkK+EPIiDNkh8at/UkHdT/JR7e/yUe3/8lHuD/Jh7g/yYe4P8lHuD/JR7f/yUe + 3/8lHt//JR7f/yUi4f8jRO7/IkXv/4CW8f1CQkPzICMzXQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQTYDAREWTwGxel/yQe + 4/4YFYn/AgIJ/gYFJVQAAAAAAAAAAAAAAAAAAAAAAAAAACUl4iwlIeGYJh7g7iUe3/4lHt//JR7f/iYe + 4P8lHt/+JR7f/y0o4f51fvD/laH3/5Wh9/6Pm/b/VVnp/iUe3/8lHt/+Jh7f/yUe3/4mHt//JR7f/iUl + 4t0jP+uPIkHrfSAz0J4aJKL+GSOh/xogn/4aHZ3/Ghqa/hsXof8kHdb/Jh7g/iYe4P8lHt/+JSDg/yM9 + 6/4iRu//X3v0/pucnv4BAQH+IiMsTgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHRxyDSQmbs8gHLb/JB3n/yUd5/8lHej/FhSD/wID + EasAAAAAAAAAAAAAAAAAAAAAAAAAACM26AcjOOkhJDXoZSQv5uV1eNP/Z2jU/yUe4P8lHt//JR3f/4CB + z/9oZ9T/U1bp/5Ke9v+Tn/b/k572/1ZZ6f8vKuH/JyDg/yYe3/8lHt//JR7f/yQv5v8jRu//I0bv/yNG + 7/8jR+//I0fv/yNG7/8jRu//IkXr/xsmpv8XFIb/GRWR/xoXnP8gHMD/Iznn/yNG7/8lSe//1Nz5/ycn + J/8DAwP9UVFUOQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAHB1Qfzw7wP4iHNf+IBrE/yQd5/4lHef/JB3c/wUHHr0AAAAAHDjADSJF + 7TsiRe51I0XuriNG798jRu/8I0bv/yJG7/5ke93/nJ7L/iYo4v8lHt/+JR7f/25t0v6Sksz/JB3f/zQx + 4v44NuP/RETm/nuE8f+Tn/b+a3Pu/yUd3/4lIOD/JDfp/iNG7/8jRu//Ikbv/iNG7/8iRu/+I0bv/yJG + 7/4hP97/GiSi/hwttf8gPdn/IUHi/iA71f8cLLP+GCCX/xkkof49VNb/uLq9/gAAAP8aGhr9ZGVlMwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAcHGMNMDJ26SMdzv8ZF5D/FhWC/yQd4/8kHef/JR7q/xUkfNoiReu4Ikbu7iNG7/0jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/9FYub/ur7G/0tm5P8jPev/JSvk/0dF2v+xssf/KCLf/yUe3/8mHt//JR7f/y0o + 4f9hZez/NjPj/yUn4v8jP+z/I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yA+3P8bGqD/JB/Z/yM/ + 7P8jRu//I0bv/yNG7/8jRu//Ikbu/yA+2/9JVLX/YWJq/wAAAP9OTk/7b29vJAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEUFNKSqK/h8a + xv8VE3n+Gxeh/yQd5/4kJOj/Izzt/yNH8P4jRu//Ikbv/iNG7/8iRu/+I0bv/yJG7/4jRu//Ikbv/yJG + 7/4sTuz/mKTP/o+d0v8iRe/+K07w/ylI7P66vMb/OkTh/yQt5f4kLOT/JC3l/iQy5/8kOOn+I0Dt/yNG + 7/4jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8iRu/+IkTr/xodnf4lHtr/JR7f/iQu5f8jRu//Ikbv/iNG + 7/8jRu/+Izrq/yQx5v4hKbT/AQEB/gAAAP+GhobramptCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALDDOHGhqB/x8Zw/8gGsX/IyPX/yQ1 + 6f8jRe//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//J0rv/y1Q8P8jRu//QmLx/yhM7/+Infb/qrPR/32P + 1v9HZOX/YX30/zFS8f+dps7/WHHg/yJG7/8jRu//I0bv/yNG7/8jRu//Ikbv/yJG7/8jRu//I0bv/yJG + 7/8iRu//I0bv/yVI7/8iRu//HTG+/yEbxP8lHt//JSDg/yNB7f8jRu//I0bv/yNG7/8kLOT/JR7f/yUe + 3/8jHc7/AwMP/wMDA/+SkpmLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHCCOpFBJ7/yAouv8jPOH/I0Xq/yNG7/8jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//JEjv/0Vn8v8vUvD/WHbz/zdY8P9rhfT/oavU/0Vj5v+nsNP/k6n3/yZK + 7/90h9n/fI3X/01t8/8yVvH/WHfz/0Ji8f8iRu//MFLw/1Jx8/8rT/D/IUXv/1578/9HZvL/LVHw/0ho + 8v8hRfD/Gx+m/yUe3/8mHt//JDLn/yNG7/8jRu//I0bv/yQv5v8lHt//JR7e/x4ZtP8uLLX/BgYd/woK + GvxYV5c2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAABkupwEPG1i7HTC8/iJG7/8iRu7+I0bv/yJG7/4jRu//I0bv/yNG7/4jRu//Ikbv/iNG + 7/8iRu/+I0bv/yJE7v5Qb/L/Y3zy/ztY7/5NbPL/dIfZ/lFs4v+HltX+aoTp/zhZ8P5MaOT/oqvM/1Ny + 8/4qTvD/H0Pv/kxr8v9be/T+j6T3/z9f8f5EZvL/Ikbv/jdb8f9ObfL/Tm7y/miE9P8gQOP+Hxq2/yUe + 3/4lH+D/I0Lt/iNG7/8jQ+7/JSrk/iYe3/8mHuD+HRmo/5WWvv7s7Or/tbfG/jEvpv8vLdOXAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHz/ZCSFC + 4pIjRu/5I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8iRu//I0Xv/yQ76v8kLuX/JSXi/yUh + 4P9AQOT/iY7u/yol4P+rsfT/oqnj/3Z60/9cYOH/maHR/0VW6/8wQeX/t7rG/0Vc7f8uSOz/N1fv/0tq + 8v9+lfX/aIHz/0Nf8P9KZvD/j6P2/01o8P9TavD/K0ru/z1c8P8fOtL/IhzJ/yYe4P8lI+H/I0bv/yM7 + 6v8lIuH/JR7f/yYe3/8kHdn/VVah/+np6f/p6en/6enp/8fI3P8vReLcXmnLAgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiRu4cIkbuxCJG7/4jRu//Ikbv/iNG + 7/8iRu/+I0bv/yJG7/4jRu//Ikbv/yxM4v5RY8j/T1HC/iYe3/8lHt/+Jh7f/ycg3/4uLOH/PTzj/zs3 + 4/5VU+b/oqPr/piZy/8kHOD+jo/O/1xd3f4lHt//o6TJ/1ZV3P4vKeH/k5Tv/nV36/8pI+D+JB3f/yUe + 3/4nIOD/NzLi/iYe3/8lHd//JR7f/iUf4P8gIMD+Ix3R/yUe3/4lIeD/JC3l/iYf4P8mHt//JR7f/iYe + 3/8gGsH+YmR6/87Ozv6+vr7/2NjY/uvq6v92i+z8UGXhMgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAChG6SAjRu/YI0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG + 7/8jQu3/QlPL/6Cjuf+urrv/hoe3/yYf3/8mHt//Jh7g/yUd3/9HSOX/WVvn/ygi4P8lHuD/JyHf/6iq + yf8pI9//Qj/a/42Ozf8lHeD/fXzQ/3V41f8qJeD/JR7f/1VV5/8wK+H/Jh7g/yYe4P8mHt//Jh7f/yUe + 3/8lHuD/JR7f/yYe3/8gG73/Ix3T/yYe3/8lHt//KCbh/ykp4/8mHuD/Jh7f/yYe3/8eGq3/AQIF/wcH + B/8CAgL/Dw8P/3R0dP+bqez/R2HgZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAElJ + 0AlITNQQQk3dCSVI7sIjRu//I0bv/yJG7/4jRu//Ikbv/iNG7/8iR+/+I0Lt/yQv5v5QT7//ra6+/7a3 + xv6lprH/VlO//iUe3/8lHt/+Jh7g/yUe3/4lHd//Ixzf/yUe3/4mHt//JB3g/pucy/9APNv+JR7f/5GS + zP4+O9v/VlTX/6Ck0P5lZen/JyHg/ldX5/85NeL+Jh7f/yUe3/4lHt//JR7f/iUe3/8lHt//JR7f/iUe + 3/8gG73+IxzP/yUe3/4mIeD/cYLz/kNH5/8lHt//Lyzh/iUe3/8eGrL+AAEF/w4ODv6RkZH/sbCx/mpq + a/+eqdz/QFrYegAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEI+yjQrJsKoS03S0XF65exuduTzP0DN5V9t + 5vxRY+f/Q1nn/zJN5/8mRer/I0Xu/yM96/8kLeX/JSDg/09Nv/+vsL//trfG/7a3xv+2t8b/ZmW7/yUe + 4f8lHuD/JR7g/yUe4P8lHt//JR7f/yUe4P8lHt//JR7g/3180P9hX9X/JR7f/0hF2f+Jic7/MSzd/62v + x/9qaun/lprw/1lX5/8lHd//JR7f/yUe3/8lHt//JR7f/yUe3/8lHt//JR7f/yUe3/8hG8X/IRvD/yUe + 3/8lHt//Z3Lv/01R6P8lHt//W2Dq/ysl4P8iHMn/AwMM/4WFhf/q6ur/6enp/39/gP9EToT/OFHVdQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAERAyyptbNuupabs6bKz8P6JiOv+Jh/a/y8r3P41Mtv/RUXe/15i + 4v5ob+T/LCvU/iUe3/8lHt/+PzzI/6yuvv62t8b/trfG/7a3xv60tcT/lZOj/lY5c/9YJkr+WCZM/00l + a/4/I5T/MCDC/yUe4f4lHt//JR7f/l5d1f+FhM7+JR7f/yUe3/6Xmcv/Ojfc/6iqyP43Mtz/JB3f/iUe + 3/8lHt/+JR7f/yUe3/4lHt//JR7f/iUe3/8lHt//JR7f/iYe3/8kHdP+Hhmz/yUe3/4lHt//Pknp/ktU + 6v8lHt//YGXr/klK5/8lHt/+Hh5V/9bW1v7p6en/5OTk/icnJ/9OW6f/OEXlyDpB3TgAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAhoflCISJ6zFoa+lyJSnj6yUm4v8lH+D/Jh7g/yUe3/8lHt//JR7f/yYe + 4P8lHuD/hoe1/7a3xv+2t8b/s7TC/5OPl/9xSUj/ZSkl/2UoI/9lKCT/ZSgk/2UoI/9lKCP/ZSgj/1on + Rf9AI5L/KR/V/0A82/+nqcj/Jh/f/yYe3/9QTtj/hIXP/4WEz/9VU9f/JR7f/yUe3/8mHuD/JR7f/yUe + 3/8mHuD/Jh7f/yUe3/8lHt//Jh7g/yYe3/8lHt//HBim/yYe4P8lHt//JDDm/yQx5v8lHt//XWLr/3Z+ + 8P8lHt//My/B/9LT2/+Ghob/JCQk/zM0Ov8ySMr/Oz7m/2Jo7Po9POR6AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABJSuUSJT3r6iUl4v4lHt//Jh7g/yUe3/4lHt//JR7f/iUe3/8pI9f+pqe6/7W2 + xv61tsX/kZGZ/2o6OP5lKCP/ZSgj/mUoJP9lKCP+ZSgk/2UoIv5mJyH/Zici/2YnIf5mJx//YCcz/ksr + g/+4ucL+KyfH/yMd0f4nIN3/n6HK/25u0/56edH/JR7g/iUe4P8lHt/+Jh7f/yUe3/4lHt//JR7f/iUe + 3/8lHt//JR7f/iYe3/8mHuD+HRmt/yUe2v4lHt//JCvk/iND7f8lI+H/XGHr/pOe9v9EReb+X13m/4SG + ov4yM0X/ISI0/kJOrv8jPOfhJR7f7SUe3/4yLuK0ODbjAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAsKOF6Pkbo/ysl4P8lHt//JR7f/yYe3/8lHt//JR7f/yUe3/8uKtH/rq++/7a3xv+oqbX/g3V5/2Un + I/9lKCT/ZSgk/2UoJP9lKCT/ZCgn/zs8qf8pQ9z/V2ni/3N93v91fNb/dHbN/09VwP9Sadz/KD/J/x4y + vv8nNbf/U2C+/3R/wP+Gjb7/FxaX/xsXoP8eGbD/IRvF/yQd1/8lHuD/Jh7g/yUe3/8lHt//JR7f/yUe + 4P8mHt//Ix3Q/x8auv8lHt//JR/g/yNE7v8jRO7/SmTx/5Sg9/9xee//oaHr/zQ0Nf8XJIX/IULo/yNG + 7/4nNNliLjXVBiws4RoyL+IIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwKuGhLinh/11i + 6/9hZuz/U1bp/0lJ5/9BQeX/Ozjk/zYz4/87O9P/r7C//7a3xv+kpbH/gGpt/2UoI/9lKCT/ZSgk/2Uo + JP9lKCT/ZCgo/zI2vv8jO+v/PVLt/1Fk7/9UaPD/VGjw/z5Y7/8jQu3/I0Pu/0Be8f97jfX/i5r2/4qY + 9v+AkfT/bX/u/1ls4/9DVdL/LD2+/x0rr/8bIav/Hhyz/yEcxv8kHdj/JR7f/yYe4P8mHuD/Jh7g/x4Z + rv8iHMv/Jh7f/yQx5v8jRu//JUnv/2F48/86S+v/lJan/wAAAP8UJoD/I0fv/yQ46M8mM9gLAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbWei5dXTs/3d47P4sKOH/NDHi/zk2 + 4/5FROb/Zmzt/n+J8v+Eju/+oaO3/6ysuv6rrLn/jYeN/2YtKf5lJyP/ZSgj/mUoJP9lKCP+ZSgk/2Uo + Jf5QJGP/NCC1/yUd3v4lHd//JR7f/iYe3/8lHt/+JR7f/yUi4P4jJ+P/JzLm/zZG6v5IW+7/WnDx/muA + 9P99jvX+jJr2/4ya9v55i/b/YHfz/kVd5P82S9D/TFfE/jU6tP8bGqT+GRWU/xUTff4VE37/IBvA/iUf + 4P8jOun/Ikbv/iJG7/9jffL+OTk5/wAAAP4TJHD/Izvm4SY2yyYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAHV07APQ0PvPzc76/6iq9P8lHt//JR7f/yYe3/8lHt//JR7f/yUe + 3/8tKOH/iY/F/6ytuv+1t8b/m5ul/4uFi/+CZ2v/eFdY/3VRUf9zTk7/dlJS/3tcXf+JdHn/mJCa/4yN + wPpHRcXqJh7f8iUe3/4lHt//JR7f/yUe3/8lHt//JR7f/yUe3/8lHuD/JSDg/yQl4v8kLuX/Jzrq/zFM + 7v9DYPH/WXLz/3OG9P+NnPb/t8H6/5+u+P9PaO3/MEfU/xwqrv8WF4X/FBJ7/x4Zs/8lH9//JDPn/yJD + 7v+Aj8n/BAQE/wAAAP8jJ1vcJCmJHwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAI2N8ARkZOnVOzfj/yQd3/4mHt//JR7f/yUe3/4lHt//JR7f/iUe3/tude7Okp3y2Zaa + u+uxssD9t7fH/6usuv62t8b/t7jI/ra3x/+ur7z7oKGs4ZSSm7eGgoyHeHCIV2FamSlLRrYJMSvXGycg + 3nQlHt/QJR7f/SUe3/4lHt//Jh7g/yUe3/4mHuD/JR7f/iUe3/8lHt/+JR7f/yUe3/4lI+H/JSvk/iM1 + 6P9GYfD/gJf2/oGW9v94ivX+k5/2/36P9f43Vuz/HjTE/hcdkf8ZFpb/IxzO/i8r4f9vcX3+AAAA/wAA + AP5gYGHXR0pqAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIuL + 7wZ6eu3doKHz/4qL8P8lHuD/Jh7f/yUe3/8mHt//Jh7f/ykj4JtPTt4DcXLWBZCTyQqLjZw/k5Sdi5OT + nK2QkJegkpGZfYiGjFCGgYcgi4GIBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlHt4FJR7fNyUe + 35QlHt/kJR7f/iYe3/8mHuD/JR7f/yUe3/8mHt//Jh7g/yYe3/8mHt//JR7f/yok4P+Fie7/cHXs/zU/ + 5/8kOur/MU/v/1hx8v9bdPP/Ikbv/yJG7v8gO9T/HSuxt2Rq1W5MTEz/AAAA/wAAAP2KiovEZWZzFAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKB7gnMzfrntrf3/3d3 + 7P4lHt//Jh7g/yUe3/4lHt//Jh7fyzEs4Q4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlHt8NJh/fUCYf + 3641MuL0OTfi/jk34v87OeL+Pj3j/0A+5P5FQeX/U1Tn/oKI7v90eOz/U1Ln/iUe3/8lHt/+JSDg/yUq + 5P4jN+n/I0Tu/iNG7/8jRu//Ikbv/nKK8PMoKCj+AAAA/wMDBr+anKR8jZGoMQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIyM7wk9O+PnOjbj/z064/8lHt//Jh7g/yYe + 4P8lHuDjJR7fIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABKSOUWYmPpaF9h + 6MFeX+j3WVvo/1dd6P9VWef/VFXn/0ZD5f8oIeD/JR7f/yYe4P8lHt//Jh7g/yYe3/8mHt//JSDg/yUs + 5f8jO+r/I0Xv/3eP8/9AQED/AQIG/x82obV0gb97jZfDVgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAISE7gKkp/TAzM36/6ip9P4lHd//Jh7f/yUe3+4lHt83AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEpK5QFAPuMnMy7heycg + 4NQlHt/+JR7f/iUe3/8lHt//JR7f/iYe3/8lHt/+Jh7g/yUe3/4lHt//JR7f/iYe3/8lHt//JSTh/naA + 7/+Xl5f+ESBk/yNG7/4mSe79jJ/uxGqD7TQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAB9fe1ZfHvt/UM/5P8lHuD/JR7g7yUe3z8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADg04gErJeAyJh/fiSUe + 39olHt/8JR7f/yYe4P8lHt//JR7f/yYe3/8lHt//JR7f/yYe3/8lHt//Jh7g/25s6v/Z2uX/JCzY/yM9 + 6/8jR+//jaP3/1d18/wpTO+vKEvvLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAB0cuwFPDbjeicf4L8nIOCxLCXgNQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUe3wglHt84JR7fgiUe + 384mHt/6Jh7f/yUe3/8lHt//JR7f/yYe3/8lHt//JR7f/zAq4f94eOv/JR7f/yQe3/9udu3/m6r2/0pp + 8v8jRu//I0bv8yNF74YjRO4JAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlHt8mJR7fdCYe + 38MlHt/3Ixzf/iMb3/8jG9//Ixvf/iMc3/8oIuD+SUbl/4mL7v6qsPP/k5nx/ior4/8kOOn/I0Xu/iNG + 7/8jRu/LI0PtLgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgh3wItJ+AjY2PoboSI + 7sKDhO72gYPt/4WL7v+Nle//kJbw/3h87P9ZWOf/LCfh/yUe3/8mHt//JSTi/yQ36f8jRe7/I0bv8CND + 7W0jP+wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAO+MeMy3haycg + 4LwkHN/2JB3f/yUe3/4mHt//JR7f/iUe3/8lHt//JR7f/iUe3/8lJOH+JDfp/yNF7v4jRe6wIzzqFQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE9N5QFBPeMbLynhZiYf + 4LglHt/yJh7f/iYe3/8mHt//Jh7f/yUe3/8mHt//Jh7f/yUl4v8jPev+Iz7rVAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlHt8aJR7fXiUe + 364mHt/qJR7f/iUe3/8lHt/+Jh7g/yUe3/glJ+OWIzboCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlHt8IJR7fNSUe + 33UlHt+DJR/fSiUi4RQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////wAAAP///////////wAAAP///////////wAAAP// + /////////wAAAP///////////wAAAP///////////wAAAP///////////wAAAP///////////wAAAP// + /////////wAAAP///////////wAAAP/////wP////wAAAP/////AH////wAAAP////+AH////wAAAP// + //+AD////wAAAP////gAD////wAAAP///+AAB////wAAAP///wAAB////wAAAP///gAAA////wAAAP// + /gAAA////wAAAP///wAAA////wAAAP///4AAA/4//wAAAP///4AAB/gH/wAAAP///8AAB/AD/wAAAP// + 88AAB+AD/wAAAP//gAAAD8AB/wAAAP//AAAAD8AA/wAAAP//gAAAHwAAPwAAAP/8IAAAHAAAPwAAAP/4 + OAAAAAAAPwAAAP/wHwAAAAAAPwAAAP/gHwAAAAAAPwAAAP/gEAAAAAAAPwAAAP/AAAAAAAAAPwAAAP/A + AAAAAAAAPwAAAP/AAAAAAAAAfwAAAP/AAAAAAAAAfwAAAP+AAAAAAAAAfwAAAP8AAAAAAAAAPwAAAP4A + AAAAAAAAPwAAAPwAAAAAAAAAPwAAAOAAAAAAAAAAPwAAAAAAAAAAAAAAPwAAAAAAAAAAAAAAHwAAAMAA + AAAAAAAADwAAAPAAAAAAAAAABwAAAPAAAAAAAAAADwAAAPAAAAAAAAAAfwAAAPAAAAAAAAAA/wAAAOAA + AAAAAAAB/wAAAOAAAAAAAAAB/wAAAOAAAP4AAAAB/wAAAOAH///AAAAB/wAAAOAP///4AAAB/wAAAOAf + ///+AAAA/wAAAPA/////wAAAPwAAAPB/////+AAADwAAAP///////4AABwAAAP///////+AAAQAAAP// + //////4AAAAAAP////////+AAAAAAP/////////4AAAAAP//////////AwAAAP///////////wAAAP// + /////////wAAAP///////////wAAAP///////////wAAAP///////////wAAAP///////////wAAAP// + /////////wAAAP///////////wAAAP///////////wAAAP///////////wAAACgAAABAAAAAgAAAAAEA + IAAAAAAAAEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYF + JUwCAhClAgIOrQICDoUICCAmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJyZrCAkJJaoSEGr/GBSH/xQRev8LCkX/AAAC+RUVHFkAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAc3SPBBwbT7IkIM7/SkfR/4aF1/+2ttz/tbbd/2tskf8FBQXvJi5UDwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmp2pIHJzeaGjpMz/4+Pm//P1+//J0vb/nq3w/4WZ + 8/9/lPb/YXCv/xojUFUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtrnJAaOmszCztcGAysrNz/Dw7/3b3+//lKXw/1Fv + 8P8mSe7/IUXv/yNG7/8jRu//I0bv/yJG7/8oR9yjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACan7MxsLK9h83Nzdvx8fT/xMzx/4GV + 6v9AXuX/IUXu/yJG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8kMuf/JDjp/yM+5GEAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACsrrih6ev0/6+9 + 9/9rg/D/LlDs/yFF7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jQe3/Jh7f/yYe + 3/8kOerzIznpIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAho+7US5R8PsiRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//JDfp/yYe3/8lHt//JSbi/yM861YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqTe2iI0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//I0bv/yQx5/8mHt//Jh7f/yUq5P8jNug1AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJUftJSNG7/ojRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8lJuL/JR7f/yUe3/8lLeXyJCzlCQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJCDYDBQUfNgMDFFQDAxNBBAQcCAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAjRu+YI0fv/yNG7/8jRu//I0bv/yNG7/8jRu//I0fv/yNG7/8jRu//I0bv/yNG7/8jPuz/Jh/g/yYe + 3/8lLOX/JR/gvQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJCDtEBAQR3AoKNv8MCzz/BAQS/wAA + AOIFBB1GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAIz7rFyNC7e0jRO7/I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//JC7l/yUo4/8lI+H/JS/m/yUh4FgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKCTlbFhSC+iQd + 4P8lHen/JR3p/yUd5v8UE3D/AAAA+wgLM08AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAKDPmHSk05msoMeWUKTLleykx5VsmL+U4JDHmGCQz5wInM+ZBJSPh9yM75f8hQeL/I0fw/yNG + 7/8jRu//I0bv/yNG7/8jRu//Iz/s/yUw5v8lIuH/JSzl/yUi4cAlJOEBAAAAAAAAAAAAAAAAAAAAAAAA + AAARD108HRmy+SUd6P8kHuD/JCDP/yQgzP8kIMX/JB7J/yMiyP8eOca9HTTFJAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAJDHmiCUj4fswLOH/TlHo/1BS6P8mH9//Jh7f/yUg4P4lJeLwJCzl0Cov + 5OEmKsr/GR+k/xsnqf8fNsr/IUHj/yJE6v8jR+//JDnq/yUq5P8lKeT/Jh7f/yYe3+0lIeAhAAAAAAAA + AAAAAAAAAAAAAAAAAAAZFpQIGhiZ2yActv8jH8b/JSDT/yYe4P8mHt//Jh7f/yYe3/8mHt//Izvr/yNG + 7/AsTekyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsapx4kHtSQJh7g8iYe3/8mHuD/JR7f/yYe + 3/8lHt//Jh7g/yUe3/9nbe3/lKD3/4KM8/9qcvD/UVvm/zM8zP8eJbb/GiSm/xgZjf8aF5z/IhzI/yYe + 4PckI95CAAAAAAAAAAAAAAAAAAAAACQe3AMjHdIwHRmomiIeuv8lHt7/Jh7f/yYe3/8mHuD/JR7f/yYe + 3/8lHt//Jh7f/yQz5/8jRu//JEfuzmJ43RV+ib0bAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALCkEMBAQYYwYHKlgREGoEAAAAACId + xBElHt13Jh7g4iUe3/8lHt//JR7f/yYe3/8lHt//KCLg/0FA5f9UV+n/Zmvt/3yF8f+UoPb/jpr1/3iA + 8f9hbvD/OUbh/x8pxf8hL9KxIzLkMyQr5AMAAAAAAAAAACUh33YlHt/rJh7f/yYe3/8mHt//JR7f/yUe + 3/8mHt//JR7f/yUe3/8lHt//JR7f/yUe3/8kNOj/I0bv/ydL8P6SmLHHP0VjJgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAREF0YDQ1I1hIR + af8DAw7/AgISuw8MXQYAAAAAAAAAACUe3wYlHt9aJR7fyiYe3/8lHt//Jh7g/yUe3/8mHt//Jh7f/yUe + 3/8lHd//Skzn/2537/9+ivL/bXbu/19j7P8nIeD/JR7f/yUj4f8kLeXrJDTnrCQx5jAjLOAOISDEjR4Z + svgiHMj/Ix3Q/yQd1v8lHtz/Jh7g/yUe4P8mHt//JR7f/yUe3/8lIOD/I0Lt/yJG7/+bquT8FBQU9zA1 + SBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAcG3UEExRdwRoWof8kHeX/IhzU/wgILP8FBB9sAAAAAAAAAAAAAAAAAAAAAAAAAAAkKOM8JSDgqyUd + 3/k+Otv/Line/yUe3/8lHt//MCvd/1xf4/+HkfT/lJ/3/5Wg9/9WWer/JR7f/yYe3/8mHuD/Jh7f/yYe + 3/8lI+H0I0fv0SNG778fOdHgHTPC/xwwu/8cLLP/Gyep/xgXjf8hHMb/JB3W/yUe3/8lH+D/Izvr/yNG + 7/+Al/b/aWlp/wYGBvo8PkUKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAHh9ZeS4tuP8lHej/JR3n/yUd5/8fGr3/BQchowAAAAAAAAAAAAAAACNA + 7BYjP+xMI0HtgiNC7bMjP+zxkprP/0xK2f8mHuD/JR7f/1ZU1/+Ymcv/MCzi/2927/90e/D/iJL0/2tx + 7v9UVun/JyDg/yUe3/8lI+H/Iz/s/yNG7/8jRu//I0bv/yNG7/8jR+//I0bv/yA81/8ZIp7/GiOg/xkk + oP8YG5H/HCSw/x861P8hRe//z9Xq/wkJCf8wMDD0MjIzAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBxiEjk7jPMiG9j/FhSF/yEbz/8lHef/JR7o/w0T + SbEeOs9bIUTnmyNG79gjRu/9I0bv/yNG7/8jRu//I0bv/3KF2f+SnND/IzTo/yUk4f8wK97/ubvF/yYf + 3/8lHt//JR7f/yYf3/9xeO//fIXx/ykj4P8lKuT/I0Tu/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/x0v + vP8eHbT/IkTr/yNG7/8jRu//I0fw/yE+3P8cLLT/KTGe/56fpP8AAAD/Z2dn6wAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQRmkuLaH/HBiv/xcX + fP8iHNj/JB3n/yQu6/8jROf+I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/9UbuH/oqzN/0Be + 5/8jRu//ITjq/6uwyf88Pd3/JSLg/yUh4P8lI+H/JCjj/yQv5v8jPOv/I0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//I0bv/x40xf8gGrz/Jh7g/yQz5/8jRu//I0bv/yNG7/8jRu//Iz7s/yo92f8UFBb/AAAA/6Oj + o8wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAMDEGoHh6K/yEb0P8fGbz/JCjj/yM+7f8jR+//I0bv/yNG7/8jRu//I0bv/yJG7/8mSe//I0bv/zVW + 8P8mSu//fJLv/4CQ1v+JmNP/Ikfv/2N+9P+CkdX/X3ff/yJH7/8jR+//I0fv/yNH7/8jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//I0bv/yJF6/8bGqT/Jh7g/yUf3/8jPuv/I0bv/yNG7/8jQu3/JSTi/yUd + 3/8lHtv/AwMO/wICAv+RkJh0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAACAguzhUVfv8kMdz/I0Di/yNH7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG + 7/8iRu//PmDx/zdZ8f9TcvP/Q2Lx/3SM9f+SntL/eYvX/3+V7f9TcfP/W3Pg/4KS1f9EZfL/Nlrx/1h2 + 8/8pTO//J0rv/05t8v8pTe//IUXv/1p38/82WPD/KEzw/0Nk8v8eM8P/Ix3P/yYe3/8kL+b/I0bv/yNG + 7/8jQ+7/JSXi/yUe3/8hG8f/Kyi7/wkJLP8PDyT7WVmQFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGjCsAxIhcOAfOtT/I0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//I0fv/yNE7v9BX/D/aH/y/0hh7/9HZ/L/k5/Q/zJU6/+OndT/UnHz/zRV + 6v+or8v/U3P0/yJG7/8hRu//Xnv0/2aD9P9rhvT/QGLy/yRI7/84XPH/TGzy/2WC9P9FZfL/HSi2/yYe + 4P8lHt//I0Ds/yNH7/8jPuv/JSPh/yYe3/8iHMz/YmOk/+rq6v/Aws3/NzWv/zk6yG4AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIELhHCJE6cEjRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//I0bv/yJG7/8jPOv/JC7l/yUj4f8lHt//Ling/4CF7f8tKOD/sbX0/8PG + 2P87Otz/oKLT/15r5f8jLeX/r7LI/01c6v84TOv/Y3zy/3GH8/9ndu//SFzt/zJI6/+Vo/X/S1vs/zhJ + 6/8ySOv/JEHt/x4iuP8mHt//JR/g/yNG7/8kMuf/Jh7f/yYe3/8mHt//Kiio/+Dh5f/p6en/6enp/9HR + 3/8zTeO1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIkXuMyNG + 7+YjRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//Ikbv/01jyv+CiLz/U1HC/yYe4P8mHt//JR7f/zIw + 4f8yMeH/NTLi/0M+5P+Cg9r/YV7V/zg03P+Ul9D/JB3f/4qKzf9eX9v/KCHg/2lp6f88OeP/JR7g/yUe + 4P8lHt//JBzf/yUe3/8lHt//Jh7f/yYe4f8fGrT/Jh7f/yUe3/8lI+H/Jh7f/yYe3/8mHuD/Jh7h/xwb + Xf9+fn7/d3d3/5WVlf/W1dX/ZH3t915w4AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAMkznLSNG7+8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//JTjl/3J3t/+2t8X/oqKt/0ZD + xP8mHt//Jh7g/yUe3/8yLuH/T07m/yUe3/8mHt//Qj7b/4WEz/8lHd//iInO/zg03P9kYtT/iY/W/ygi + 4P8lHt//UFHm/yUe3/8mHuD/JR7f/yYe3/8lHt//JR7f/yUe3/8mHuH/Hhqz/yYe3/8lHt//O0To/zQ4 + 5f8mHt//JR7f/yYf4P8KCjn/AAAA/xAQEP85OTr/ZmZm/36R6/9XbdUuAAAAAAAAAAAAAAAAAAAAAAAA + AABIRcwRQ0DLR15h2W9fY9yPREXRhjtP48cpROb/IkLq/yFF7v8jR+//I0bv/yNE7v8kNej/JiTd/3t7 + tf+2t8b/trfG/7W2wv8zLsz/JR7f/yUe3/8mHt//JR7f/yUe3/8lHt//Jh7f/ykj3/+kpsn/Ixzf/z47 + 2/+EhM7/PTjb/56gzf+Vl/D/hojt/1VU5v8mHuD/JR7f/yUe3/8mHuD/JR7f/yUe3/8lHt//Jh7h/x4Z + r/8lHt//JR3f/3R/8f8/Qeb/JB3f/0hK5v8mHuH/DQxK/xgYGP/c3Nz/6urq/4WFhv9UZrn/S1/LMwAA + AAAAAAAAAAAAAAAAAAAAAAAAOzbIVFNR0POKjub/l5nq/z072P9MTOH/V1ni/11h4P9mcOP/Wmnl/yQq + 3v8lH+D/JR7f/3V1tv+2t8b/trfG/7a3xv+3uMf/cXK0/z8klf9GJIL/PiKZ/zAgwf8mHt//JR7g/yUe + 3/8kHOD/p6nJ/ysm3v8lHd//j5HN/zUx3f+qrMj/JB3g/yYg3/8lHt//JR7f/yUe3/8lHt//JR7f/yUe + 3/8lHt//JR7f/yUe4P8dGaz/Jh7h/yUe3/9JUOn/VFrq/yQc3/9ye+//JR7f/xcVhf+FhYb/6enp/+vq + 6v8+Pj7/S16//zc/1V0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCg+IJm5zsPZmd8X45OOPOJSHh/yUg + 4P8mHuD/JB3e/yYg3P8lHt//Jh7f/0E+xv+1tsT/trfG/7W2xf+dm6X/eFZX/2YxLf9lKCP/ZSgk/2Uo + I/9mKCH/YScx/0wlcP8xIb7/JR7h/4uLzv9KR9n/Jh7f/0ZD2v9/f9D/k5TM/zg03P8lHt//Jh7g/yUe + 3/8lHuD/Jh7f/yYe3/8lHt//JR7f/yYe3/8mHt//Hhmw/yUe3P8lHt//JC/m/yQt5f8lHd//fYfy/z89 + 5f8jHdL/rq/G/7S0tP9TU1P/LCww/y5F1P9aXur/T1PouDYz4w4AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAMUHpiCQv5v8lHuD/Jh7g/yYe3/8lHt//JR7f/yUe3/9qab//trfG/7a3xv+Wlp//azw5/2Uo + JP9lKCT/ZSgk/2UoJP9lKCP/Zich/2YoIv9mJyH/Zicg/1UlVP95bK3/bGvJ/yMczf8kHd3/lpfM/3h5 + 0f9cWtf/JR7g/yYe4P8mHt//JR7f/yYe4P8lHt//JR7f/yUe3/8mHt//Jh7f/yMczf8gG8D/JR7f/yUq + 5P8jQez/JR/g/3yG8f95gvH/JiDg/5WYwf9JS13/Ghsq/0FNrP8kNujeJR7f+i8q4f01MeI2AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAOTrkBy8y5PQ8OuT/JR3f/yUe3/8mHt//JR7f/yUe3/8lHt//d3e9/7a3 + xv+xscD/gXR3/2UoI/9lKCT/ZSgk/2UoJP9lKCP/SDV+/ypD2/9UaOT/d4Hg/3h/2f92eM7/QlLM/z5Y + 2f8eNcX/LT2//1Ffw/9wfsb/ZW25/xkZmv8bF6H/Hhmz/yIcy/8lHt7/Jh7g/yUe3/8lHt//JR7f/yYe + 3/8mHuD/Hhmw/yUe4P8lH+D/I0Tu/yND7v9qfvP/lKD3/2Rm6v92dnf/EBld/yFC6P8jRe/+KTPTNi0y + 2wstK+EUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE1M5yIlHd//QUDl/2Jo7P9cYuv/V1rq/1Za + 6v9RUuj/TE7o/4SGwP+2t8b/rK27/4Ftcf9lKCP/ZSgk/2UoJP9lKCT/ZSgk/04uav8nM97/MD/p/0BN + 6/9DUev/Q1Lr/yg86v8kOur/LEfs/2N48/97jfX/jZr2/4uZ9/96jPb/ZXnt/1Bi2/83SMX/ITC3/x0l + u/8gH7//IRvI/yQd2f8mHt//Jh7g/yAbv/8dGKz/Jh7g/yQu5f8jRu//K03v/0dg8P+JlOD/CgoK/w0a + VP8jR+//JDTkpAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5d+08mJny/7e5 + 9/8wK+H/JB3f/yQd3/8sJ+H/Tk/o/15i6/91ecn/pqaz/7a3xv+SkZn/bkE//2UnI/9lJyP/ZScj/2Un + I/9lJyP/Yigt/0sxhv8zLtn/JR3f/yYe3/8mHt//Jh7f/yYe3/8mH9//JCPh/yQr5P8uO+j/P1Hs/1Fo + 8f9ievP/don1/4iX9v+Ckvb/aH71/3OH7P9ic9L/LDu4/xkemf8UE3r/FBJ5/xsXn/8lHt7/JDXo/yNG + 7/8kSO//c3eF/wAAAP8NFkb/IzPVsyo8xQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAn6DzTKus9f96ee3/LCbg/yUe3/8mHt//JR7f/yUe3/8nIN//eIHu/5+iuP+2t8b/qqu4/5qa + pP+inqn/mZCa/5eMlP+YjZb/m5Od/52cp/ybnabdiomlrEhGu34nIN63JR7f+yUe3/8mHuD/JR7f/yUe + 3/8lHt//JR7f/yUe3/8lHt//JSPh/yQs5f8jNej/LEjt/0Nh8f9/k/b/sr35/5Og9/9vhPX/TWXp/x8y + vf8WGor/GBWQ/yQd0/8lKeT/W23v/ykoKP8AAAD/Ly876zI3cgMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAGJg6VRKRuX/YWDp/yUf4P8mHuD/Jh7g/yUe3/8mHt//PTvji3l+ + 50yKkNZjkpOil6Kireisrbv9p6i09ZucptaUkpqkiYSKcYB2ej9+cnoSAAAAAAAAAAAAAAAAAAAAACYf + 3R8lHt98Jh7f2SYe3/8mHt//Jh7f/yYe4P8lHt//Jh7g/yYe3/8mHt//Jh7f/yYe3/8kIeD/f4rx/2x/ + 8f8qSO7/UGvy/3uM9f91iPT/Ikbv/x83zf8ZIZz/IR+2soaG0NsAAAD/AAAA/1RUVfVkZnUTAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACjpPNi0dH7/8HC+P8pI+D/Jh7f/yUe + 3/8lHt//JyDgvTs34gMAAAAAAAAAAAAAAACcnasBk5ScDZaXnwUAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACUe3wElHt85JR7flyYe3+0kHd//JBzf/yQd3/8kHd//JR7f/yYf + 4P83MuL/aWzq/3V57P9WVef/Jh7g/yUm4v8jMuf/J0Tt/yJG7/8jRu//I0fw/yJE6teJkLDiAAAA/wAA + APtzdHiDio2dOQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeXntZkZD + 5f8vK+H/JR7f/yYe4P8mHt//JR7f3CYf3xQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtJ+AIVljnUnJ3 + 669pbur3bnPr/2lx6/9qcer/ZWjp/1hZ6P8wK+H/JR7f/yYe4P8lHuD/Jh7f/yYe3/8lJeL/JDTo/yND + 7f8hRe//jZa1/wAAAP8OGEjXaHWzUZKav2MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAH+B7j/Bwvj/zc/6/ykj4P8mHt//Jh7g6iUe3yYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAREPkEjUx4mYpI+DBJR7f/CYe3/8lHt//JR7f/yYe3/8lHt//JR7f/yYe + 4P8lHt//JR7f/yYe3/8lH9//Iynk/8vR9P8aHSr/IULe/yRH7vyGmurCaIHrKQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2duwCfHvt1FFO5v8mHt//JR7f6yUe3zIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwm4B8mH992JR7fzSYe + 3/0lHt//JR7f/yUe3/8mHt//JR7f/yUe3/8mHt//JR7f/yQd3//Fxvb/ZGfR/yQ16P8hQ+7/hZz3/1Fv + 8/sqTe+gJUfuHQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFhU5yIyLOGELCXgiS0m + 4CMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAlHt8iJR7fbCUe37smHt/5JR7f/yUe3/8lHt//JR7f/yYe3/8lHt//S0fl/zMu + 4f8kHd//b3Hr/6Cs9f9BX/D/I0bv/yNG7/MjRO5tI0TuAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJR7fEyYe32EmH9+0Lynh9zYv + 4v81LuL/NTDi/0VC5P9oaen/nKLx/6mw8/94e+z/JSDg/yQv5v8jQu3/I0bv/yNG77YjQe0ZAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAFdV5hBydetcaGnqr2Nj6fVbXOj/UVHm/0VD5f8sJuD/JR7f/yUe3/8mHt//JR/g/yQu + 5f8jQu3/I0bv6iNB7VEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABOTOUOPTnjVy0m4KwlHt/zJh7f/yYe + 3/8lHt//Jh7f/yYe3/8lHt//JR/g/yQv5v8jRO7+Iz7sVgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJyHfCyUe31IlHt+lJh7f7iYe3/8mHt//JR7f/yYe3/8mHuD/JSTi5iM76isAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUe3wYlHt85JR7feyUe36wlHt+XJSDgVCUm + 4hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////////////////////////////////// + /////////////////////////////////////////////////////////////wf////////8A/////// + //gB////////8AH///////8AAf///////AAA///////4AAB///////gAAH///////AAAf//////8AAB/ + B/////4AAP4D/////gAA/AH///8AAAD4AP///gAAAfAAf//+AAADwAAf//CAAAGAAB//4GAAAAAAH//A + fAAAAAAf/8BwAAAAAB//gAAAAAAAP/+AAAAAAAA//4AAAAAAAD//gAAAAAAAP/8AAAAAAAA//gAAAAAA + AD/8AAAAAAAAH/gAAAAAAAAfAAAAAAAAAB8AAAAAAAAAH4AAAAAAAAAH8AAAAAAAAAfgAAAAAAAAD+AA + AAAAAAB/4AAAAAAAAH/gAAAAAAAA/+AAAeAAAAD/4A4/+AAAAP/gH///AAAA/+A////gAAB/4H////wA + AB/w/////4AAB///////8AAD///////+AAH////////AAP////////gA/////////wH///////////// + //////////////////////////////////////////////////////////////////////////////// + //8oAAAAMAAAAGAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI1AAArxwAANvEAABnnAAAAkQAA + AAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABTghHYbwYlzf/5mV + 6f+opt//XFtp/wIBAHoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDgXwfnZuPZ6mo + r+bFyvj/vMj0/5Km9P94kPn/a4b+/ys7fMsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFxbwGIhn8qrKmecMjI + x8XFzOf2o7Lz/2eB9/8zVfL/G0Dv/xY77/8YPe//GT7x/yU/8/8nS+1ZAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeHZwCLm5 + uLa5w+/+jaD6/1Nw9v8mSfD/Fzzu/xk+7/8gRO//I0bv/yNG7/8jSPD/JDvr/yYc3/8kNOrrI0jyIwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGJ1z5YeRPf/Fjvv/xtA7/8hRfD/I0bv/yNG7/8jR+//I0fv/yNG7/8jSfD/JTLn/yYb + 3v8mI+H/I0bvUgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAB9C7hUiRe/jI0bv/yNH7/8jR+//I0bv/yNG7/8jRu//I0bv/yNG + 7/8jSfD/JSzl/yYa3v8lJ+P7I0nwKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAABcAAAAXAAAAAQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjRu94I0jv/yNI8P8jR+//I0bv/yNH + 7/8jRu//I0bv/yNH7/8jQu7/JiDh/yYj4f8lJuLWJS3lBAAAAAAAAAAAAAAAAAAAAAAAAAAqBQUbtAwL + QfIHByjyAAAAqAAAABoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACJF7wohQ+4KI0fvAQAAAAAjSvEFIz/swCM/ + 7f8jSPL/I0j0/yNH8f8jR/D/I0fw/yNJ8P8kN+n/JiTi/yUs5f8mIuF3AAAAAAAAAAAAAAAAAAAAAAIC + CDQWE4bqJB3f/ycg7P8lINv/FhR2/wUJHcULF08CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkRvUsIzLnsTU75uZARujgJyvkyiQw + 5qokOOp1JTHofiYn3PsZKLv/GDLM/xw95P8gQ+v/Ikbu/yQ87f8mL+r/JiLi/yYg4MgmHN8JAAAAAAAA + AAAAAAAAAAAADhgVhtQnIef/JiDY/yUf1f8mH9v/Jx7i/yUt4f8jSviwIUn/DwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKtAsKBzsuSoi + 4f4rJOD/Jx7f/yYc3/8lG9//JyDg/m947/50fun/Xmfa/0hU1P8sOsr/HSu8/xgZqf8fHcH/Jh7c2yYm + 5B0AAAAAAAAAACYe4QkmH+ElGhaVlyMexP8lH9b/Jh/g/yYe4f8mHuH/Jh3h/yYk5P8iRfD/I0fzlM/V + 8RPk4t0BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgIISAEB + AqUDAhI+AAAAACce6CklHN+TJh7f7yYe3/8mHt//JR3g/zQw5P9KSej/WVrt/3B28v+AifX/d4Lx/15p + 4/8rLsz/ISvcwyRB7k4jSvEeAAAAACUb25AlG9b9Jx7o/yYd5P8mHeP/Jh/l/yYf4f8mHuD/Jh3f/yYl + 4v8ZPvH/YXz2+UZGRIwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAFBRJLFxWH/BwYqf8GBh/5AAAANQAAAAAAAAAAJiLhDiYf4GgmHd/MKR/e/yki3v8gGN//HRPd/zk2 + 4f99h/L/l6X4/3R88v8pIeP/Hxbf/yYg4P8lI+L6JTTomCM86GUfKb7OHSOy/x4juP8gIbz/IBu9/yUb + 1v8mHN7/Jh/l/x837f9JbP//d3uI/wUEAJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAUGBRspKZHlJB3m/ygf+/8fGr3/AAAAewAAAAAlSvoDI0XvHyRB7UkiOuyQMkXm9ISH + 0P8jHOD/IRfg/39+z/9SUuD/cXjx/3yE8v9qcO7/S0vn/yUb3v8lH+D/JDrq/yNK8v8jSfL+I0ju/yNJ + 8P8fPtP/GSOa/xskrP8dIa//Hia7/xg43P+cq/D/IyEW/3V1dYIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAABkaPogtKc//GBSS/yQb3v8lG+L/FyeQuCNJ9ZkjR/DUI0fw+CNI + 8P8iR/D/H0bw/5ikz/9GWeL/FB3o/3d30f9YU9b/GQ7f/yAW3v9WVun/YGXs/yMn4/8jQe3/I0nw/yNG + 7/8jR+//JEn1/yA50f8fHrn/Iz3m/yNK8/8jR+z/Hz7X/yM4xf95fZn/AAAA/8DAwHcAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhgYYtUeGLb/Gxah/yUl6P8kPO7/JEn3/yNH + 8P8jRu//JEfv/yNH7/8kR+//K07y/4+e2v9ziNn/IUj0/1px4/94g9X/GCzq/yEy6P8dM+n/Gzjs/yFF + 7/8jSPD/H0Pv/yFF7/8iRvH/IUPh/x4dtf8nG+P/JDjs/yNJ8P8jSPD/JTfu/yYq8v8HBzf/FxcM/+/v + 7UoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFg8NVO4hKMz/JDrr/yNH + 7v8jSfD/I0fv/yNG7/8jR/D/Jkvw/zZb8v88X/L/VXX2/4uc5/90h9f/fJPs/0Ji7v+Hl9T/NFrz/z1h + 8v82WvL/NVfx/zdZ8f8hRu//Q2Py/zFU8f81WvX/HjHI/yQZ0/8mKOX/I0jw/yNJ8P8lMef/Ihjd/ysj + zf8XFlT/KShD3bGs/QkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFSqOQxw0 + uP0jSPH/I0ny/yNH7/8jR+//H0Ty/x1C8/8jRO//Ij3s/ytA6v9oefD/Sl7v/4GW5/9oeNr/laPe/zNU + 8f+DkdP/WXXw/zJV8f9QcfP/dpL2/0Vm8v9HavP/W3r0/1Bx8/9QcvP/GiK//yYa3/8kOur/I0fv/yUr + 5P8kGuL/KSS//8HC0v/g4dn/UVTD/hMR4zQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAkSfhnJUr9+SRJ9/8jR+//I0fv/yNI8P8cQfL/NlTg/0xb0/8oJt7/JR3g/ycf3/9KSeX/RkLl/6eo + 6f9ubNX/bW3Y/01O4P9sa9P/W13d/2Bm7f9weO3/Ojvl/ycq4/9OT+f/OTrl/ygp5P8mLd//IB/B/yce + 4v8iLeb/JCLi/yYc3/8fF9//R0aT/8bHu//Ix8L/0dbn/y1N7nMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAB9F8XUfRfD/IETw/yJG8P8jSPD/I0jx/xs37v9SYM3/ra+3/4yJsf8jHN7/Ixzj/ysl + 4f8+POP/JR7g/yoj3f94d9H/NC/c/3Bu0/9TT9f/foDY/yUc4v9KSOX/Jh7f/yUc3/8hGN7/Ixrf/yUc + 4P8lG9n/IRvA/yQc4v84OeX/NDLj/yQb4P8lHuL/CQk3/wYGAP8xMTD/mpmW/1Vz+aYAAAAAAAAAAAAA + AAAAAAAAIhy+TTQyx6ZhZdu6Q0TRsURX5PE8Vej/OFbt/ylJ7f8jPOz/HiXn/1dVxf+5uL//w8XJ/4SG + yP8gGuD/KB/a/yQd5P8fG+v/Ix3n/yEa4f97etH/KiTf/2lo1P9ZV9X/kZPU/3R17f9QTub/JBzf/yYe + 4P8mHt//Jh7g/yYe4P8lHtv/IBq6/yAX4P9XXuz/TlDp/zc05P8wK+r/AgBA/4GCef//////eHdz/zVP + 068AAAAAAAAAAAAAAAAAAAAAPTrOPrO086PCx/nSRkTj9i8q3v84M93/SUnf/zo53f8fFuH/QDrN/7W2 + vv+8vsv/rq+8/459hf9eMU7/XCU7/1cmT/9II33/NCC0/xwU4/9yctn/SEPb/zYw4P93d9P/fHvQ/ywl + 3/8jG+D/Jh7g/yYe4P8mHuD/Jh7f/yYe4P8mHuH/Hxq5/yQb3f8vNOb/MTfl/0xL5/9PUPD/HRih/9TV + 0f+3tq3/OTpA/zxL3vI/O+duJhvfBAAAAAAAAAAAAAAAAAAAAAAAAAAAIjXonR8f4v8hF9//IBff/yMa + 3/8dFeH/dXS//8LEyP+lprP/b0dG/2EfGf9nJx3/aCge/2gpH/9mKyP/ZSsp/00fWP9zYqb/ZmXL/xEJ + zP9kY8v/kJDJ/zcw1P8gFt3/JR3i/yYe4/8mHuL/Jh/g/yYe4P8nH+P/IRzE/yQc1P8jJ+X/HDHp/0xM + 5/+CivP/SUTs/4OEm/8PEB7/M0Sj/CYt6uY8OOT/LCXhMwAAAAAAAAAAAAAAAAAAAAAaEt4PKCji5jw7 + 5f80L+P/MCvi/y0n4v8jHN//hYa+/7/Cyv+IfYL/YSEc/2UoI/9mKCL/ZSgn/zE5u/9DWej/d37a/3x+ + y/9TYc7/L07d/zBI1P9lc9n/h5bb/0xVwv8oLLH/IiKz/xwawf8dF9D/IBjY/yAX3v8kG+P/JR7b/yIb + xP8nIOb/ID7t/zZV8P9+jPj/hYjl/zEzN/8aOc7/JD300iUe4BEjG98ZJh7fAwAAAAAAAAAAAAAAAAAA + AABMR+YoZWHq/Wpr6/9EReb/R0jm/1ha6v9ka/D/io7E/7i6wv+QipH/ZS0o/2AeGf9hIBr/YiEZ/1Ak + VP86L7n/MDPv/ywu7f8mKuj/ICTm/zE66/9FUe//VGPy/2Z49P9ug/P/bYPt/2R55P9TZ97/QlDU/0dM + zv8uMMn/FxW2/xIOg/8eFq3/Jijp/yFG8f8yWP7/ZW2Q/wAAAv8YMtrzHRrpPwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACytPY1pqf1/11Z6f8cE97/Ihrf/yMb3/8qJeT/dXvd/6+wvP+ztcP/mpOc/5aB + if+ReoD/jXZ8/453dPqOg4LcdHOdqi8qy5ojGuLpJh3f/yQb3/8hGN7/Hxjf/yEb3/8kJOL/KzPn/zdF + 7P9KXvL/V3D1/5+y+v+QpPT/XHDh/zlJv/8WHpL/HBWi/x8f2v9CVO//MTMz/wEBAf9rabtTAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABfYek8hIPv/1pX6P8gGN//Jh7g/yMb3/8yLuKRk5/9Lo2U + yU2NjI+cnJ6nxpaao62JjZN3foKFSnR3eSdsbm0IAAAAAAAAAAAiGuYVJh/gXCYf4LsmH+D3JRzg/yAY + 3v8hGN7/IBbe/x8U3v8bEt7/PEDm/3uH8P9HVu3/SmDz/2yE/P8xVfL/HDnN/xgivdlubc+5ERAH/xUV + FPzGxbtVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACVmPJGlpby/1BN5/8hGd//Jh7g/yUd + 4LwoIN8FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAkHeAiMCvhe09P5s1OTeb8TU3m/0xN5v9XWej/cXPr/0tI5f8iGd//Hxrg/yIl4/8lNun/JEPy/x9J + +/hxg8jxBgQA/ycuS53IyMdcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABXXOgyn6Dz/2xq + 6/8fF9//Jh/g1iUe4BcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKF7gFIROU3LijhijEt4t0xLOL/Jh/f/yEZ3/8mHuD/Jh7f/yYd + 3/8mHN//Jh/g/xsj5/+SmuD/MjhK/xk/5fCGmefBTmvuJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAYmDqrjw34/8jHN/TJh/gIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQd4AokHeBDJR7glyYe + 4OEmH+D+Jh7g/yYf4P8lHuD/JBzf/xkP3f+HhOv/XF7d/xIj6v+Bmfj/RGf09R5D75MjR+8TAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAHRbfCiIb3yomH+APAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACYf4AkmH+A4Jh/gjCYe4NMmHt/9Lifh/zIr4f84M+P/S0fo/4KB7P+hpvP/MkDp/yA/ + 7f8jSfDjI0jwSQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8X3wNgYuk3fYDtg1ZU589dX+j/aWvq/2Fh + 6f8zLeL/Ixnf/yYg4P8lMOb/I0Lt/yNJ8JAjR+8QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABoS + 3gUgGN8zHhXeex4W39AjG9/7Jh/f/yYe4P8mHN//JiDg/yQy5/8jRO6NAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACYf4AMmH+AqJh/geCYe4LgmH+DqJh7f3CYb36IkO+srAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA////////h/////////8AAP///////wAA////////AAD///////8AAP// + /////5yA////////AAD///4H///cRv///Af//wU////wB///AAD//wAD///i///+AAH//y8D//8AAf// + /////wAB8P+6Cf//gAHgf/////iAA8A/bwD/4AADgB/////gAAYAB2QA/4gAAgAP/v//BgAAAA+DAP4E + AAAAD+j//gAAAAAPHAD8AAAAAA////wAAAAADwAA/AAAAAAPm+/4AAAAAA8AAPAAAAAADwAsAAAAAAAP + AAAAAAAAAAMAAOAAAAAAAwAAwAAAAAADAADAAAAAAB8AAMAAAAAAPwADwAAwAAA/AAHAf/4AAD8AAMD/ + /4AAHwAB4f//8AAHAAHj///+AAMAAP/////AAAAI//////gAAAD//////wAAAP///////wAA//////// + AAD///////8AAP///////4gH////////AAD///////8EgP///////wAYKAAAACAAAABAAAAAAQAgAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFQAA + HIUAABOYAAAAPQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAUEUSaVVC1/3V0qf8sLDHPAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISC + eBWfnZVYqay1tJOf6v97kvn/Y3/8/0Zj5P8bLXlQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiG + eyqqr8W3i5zn8WJ89P84Wfb/HULy/xg+8P8bQvD/IDLr/yc19d0lTP4VAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAV2i2GTFT9egXPvL/GkDv/yBF8P8jR+//I0fw/yNJ8P8lKeT/JiDh/yNI7zUAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAIUXvXyNJ8P8jSPH/I0fw/yNH8P8jSPD/I0Tu/yYj4v8mJeLsJTTtDgAA + AAAAAAAAAAAAEAUEGo0GBR+uAAAAXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACZO/wUkRO03LEbsUSRB7DYjRu8aIjnquxw34P8dQer/IUfx/yNK9P8lOu7/Jibj/yYi + 4Y4AAAAAAAAAAAAAAAwWE4PNJSDf/yYg2v8YFof/DR97XgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAHS+/Fysm8NE1MOP/KCHg/yMf4fM4OOblX2fb/0pY1v8vRNj/HzDF/yAi + x/8mIuC2AAAAACYh4wEkHNIeFxSCnCYg3P8mIN7/Jh/g/ygf6v8bOPn+Wnn/Ue3s6wUAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAABQcGKJoDAQyTFRB9CyQb4U8mHOCzJhvf+Sce4P88OOj/TUvq/3R8 + 8f96he3/NjjW/x8k4L0kPexvJS/mLyMez8klHdb/JB3W/yYe3v8nHeP/Jh/j/yhG+v9baqfuCAcEIwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWFk+OJR/k/xsUpf8DBxVDAAAAACU67iEgMeqOSlHe+Dkz + 2/8xKNr/aWvg/3mA8/9hZO3/ODDj/yQa3v8kPu3qIkTl5CE+3P8fN8r/HCOs/yAhv/8ZJs7/ZH3p/zo4 + L/eGhocbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQYKIiYjoPodFbT/JSLn/x46ycskSvXWIknw/htD + 8v9MauX/coHZ/y9A5v9patT/GBTh/zxA5/89Sur/IDvs/yFH8P8iR/P/IkTn/yEjx/8jPun/Ikvu/x87 + 4v8xN3//QUAy7P///w8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAQhPHR6o/yM04v8kRvL/I0n2/yJH + 8P8pT/H/Mlfy/1578f+Cldn/WXnw/2yC3f80VvD/K0/w/zBV8f8oTvH/NFjy/zBX8/8gLcr/JSDe/yNH + 8f8kPu7/JSHo/w0KZ/9VU3GnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH0DUBRs1s6UiROb/JEvz/x9G + 8v8fQ+//JT7q/yIw6P9MV+v/a3jv/4SO3f9peOL/YnHd/1Fn6v9WcPP/WW3w/0Zf7/9OZPD/Plfp/yEg + yv8kLun/JDbq/x0V3/9oZMb/6Ond/15k28wTG+YEAAAAAAAAAAAAAAAAAAAAACMz2g0iS/i9IUj6/yNK + 8/8cQfL/OE/f/42Uvf88Ntb/Hxfp/zUy5v82MOP/Z2TY/1ZR1/9lYtX/Y2Db/0pG5/8oIeD/KCDg/yYe + 4f8iG9f/IRvK/y8u5/8tKOP/IBje/yUlSf9mZVv/mqTK/CFL9yIAAAAAAAAAADMuxlpzdN2+Rkjb2j5O + 5v88Uun/ITPp/zg51/+qqcL/vL/G/0k8tP80Ha3/LR/H/xsU5v9VU93/R0Lb/2lo1f95edv/Qj7n/yUe + 4P8lHuD/Jh/h/yUe2f8eF8T/QEHq/0VH6P82M+X/Ly1o/9jXxf9gZ4j/Kj3uRQAAAAAAAAAAa2nhC62x + +DEpMObXIhne/ykg3v8dEt7/iIfF/7i6vf96V1j/aSsf/2goHf9iLTT/Uyhi/2BRrP9FQ9H/VFLM/3Fv + zP8aEdj/Ihrf/yUd4f8mHuH/Jh/h/yMbyP8kJeT/Kjbn/2pu8f9vbd3/Y2Vn/yc2n/s6OOzXKSDgHAAA + AAAAAAAAJx/gDDg45OpCQOb/OTbk/z895P+eocr/nZaZ/14dFv9jHhP/ViU+/zQ/1f9dY+L/TVfW/yg/ + 3f9TY93/eYjd/0xXy/8+Rcv/MDPQ/yYk1v8hGtf/HBXA/yEayP8jPvL/XHf//2Fjj/8LIo3/Hin3dyEX + 3wsmHt8DAAAAAAAAAACKifAliIfw+jYx4/8sJuH/QkHp/4+Tz/6urrb/jHR5/4NgY/9/W1n/eGR/5UpJ + v8AgGuLZJR7i/ycj5P8rK+b/PUPs/0lW7v9NYO7/Y3nu/3mJ5P9BT8P/ICWc/x0cw/80SfD9IiYr/zI2 + fasAAAAAAAAAAAAAAAAAAAAAAAAAAHx+7i58e+7/KyTh/yEZ3/8uKOOQjZfzIoWHlleNkJR8g4eLWnV5 + eydydGsIAAAAACcg3wQlHuFMJR7gqygh4O8uJ+H/MCrh/zEs4v9gZOv/T1jt/0ZV8v87WPL/GDbX+0pT + xNAKCAD4mJWIawAAAAAAAAAAAAAAAAAAAAAAAAAAbHHrKYiH8P8tJuH/Ixzfuigh4QQAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUVDmFGFh6Wc/PeS+REPl+Tk04/8jGt//Ihnf/yMe + 4P8fKO3/Xm7W/yg1ZPNwhNyiW3XoFAAAAAAAAAAAAAAAAAAAAAA6OOMCSkXmlScg4KgkHuAOAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiGt8jIxzgdSYf + 4L8mIOD1Ixvf/xsQ3f9PSOT/TlHp/26B9/85WvLpH0bwbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACIa3xhEQeRlXl3ouUdF5fNoaOv/X1zo/yUh4f8kNOj/I0XvtiNI8CMAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHhbfFyEZ32MgGN+1Jh7g7iYc3/8mIuH/JD3rkgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYf4BQmHuBNJh/gjyYb + 3oMkNOgqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////////////////4f///+ + D///8A///8AH///AB///4AYf/gAMD/4AEAP4AAAD+EAAA/AAAAPwAAAH4AAAA8AAAAMAAAADAAAAAYAA + AAGAAAAPgAgAD4P/AAeH/+AD///8AP///4D////g/////////////////////ygAAAAYAAAAMAAAAAEA + IAAAAAAAYAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkI + KwwFBR5yAwMOWwYGCQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAk5ScHnR0psSFi+f+anbD/iIsYUwAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdHV9Arm9 + 0HWUod3LbYPs/TJT7v4iRu/+I0Pu/iQ048kfONQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADVU6KkjRu//I0bv/yNG7/8jRu/+Iznq/yUi + 4f4hN9sVAAAAAAAAAAABAQoBAQEGAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACM+6yQjQ+72I0bv/yNG7/8jRu/+JC7m/yUm4tAAAAAAAAAAABAOXlEWEoboDw5Y4goP + PTMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwjsxcrK+HNLSzi5yUk4sU6P+TeP07U/ig/ + 3P4fMc3+JCPc+CUl4TwAAAAAGxeeMSEcxPYlH9n+JR7e/iM35udEYOUqAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAEA9ZYgwLR60XFIsVJSDgbCUf4NkoIeD/Ojfk/2px7v9wevD+Jyja6yUp45UkLOFsISHK8yIe + yf4kHdj/JR7f/ypB6/5KU3u/AAAAAAAAAAAAAAAAAAAAAAAAAAAcHVgrIx+y+SMc2/4VJoxwI0LteyNB + 7bVdbdz8KSzi/2Ni1v9RUuj+U1Xp/yUq5P4jRO7/I0bv/yA31/4eMsb/HzLM/1RlyP4xMTG5AAAAAAAA + AAAAAAAAAAAAAAAAAAATFVl/HyTA/yM77f4jRu//I0fv/zJV8P5kfOb/ZHzl/1903v8wSe3+KEbt/ylM + 7/4uUfD/JUPh/yMg1f4jQ+7/JDLn/xkZiv5GRVmFAAAAAAAAAAAAAAAAAAAAACFC4hoePM/OIkbv/iJG + 7/4uS+T+JDHn/kJL6P5ocej+cXrZ/mNu3P5EV+v+Wmvu/kBS7P47TOv+LDfW/iUp5P4lLOX+MCvD/tHR + 1/5qc9KgAAAAAAAAAABAPskvTFLYUjNQ69slR+3/Iz7r/2Bq0P6YmL3/JR7g/y4p4f4mH9//WFXW/19d + 1f9nZt7+OTXi/yUe3/4lHt//IxzO/zU05P41MuP/GBSK/2tra/5mc7HQAAAAAAAAAAB8e+AzXWDonSwo + 3/40MN/+Qj3T/rCxv/6DZWn+YCk1/lkmSf5EI4f+XVfJ/lFO1v5XVNf+JR7f/iUe3/4lHt/+IxzN/igm + 4f48Quf+SUnT/o2Olv40Pa3zPDzjYAAAAAAAAAAASUjmiUNC5f48OeT/a2zV/6Ccpv5lKCT/ZSgk/z87 + sf5PVeH/MT/e/0JP2/9gbtv+SlTS/z5D1v4uMNf/JCDK/x8auv4oQez/ZXPU/xgoffsmN99EJiPHAwAA + AAAAAAAAi4rwnT864/4lHt//U1PfsqGitr+XkJndjoCGs4l+iIJSTr9ZJh/enCUe3/AlHt/+JSTh/zA2 + 5v5qd/D/W2/u/zdKy/4fKMT3OD147jU2TKEAAAAAAAAAAAAAAAAAAAAAjo7woEA65P4lHt+pNC/QAQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACUf2ApAPeNZSEfltEE/5PpBPuT+JR7f/iUn4/4kNej+RU14/Vlp + sqJjedgFAAAAAAAAAAAAAAAAZWPpNC0n4IIkHdsHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACch3RUlHt9mJR7ftSUe3/clHd//UE3i/2t47/4vTO7dI0XuSAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAExK + 4RBhYehcUE/msDUw4vUlH+D/JC3l/SM964AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQd2Q0lHt9QJR7feCQm + 4i8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP// + /wD/4f8A/8H/AP4A/wD/AM8A/wGHAPgBAwDwAAMA4AADAOAAAwDAAAMAAAADAAAAAQCAAAEAgAAHAIfg + AwCP/AEA//+AAP//8AD///8A////AP///wAoAAAAEAAAACAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMi81LDIxVJ4ODgw5AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACFhok9goy7m2B14O9Laf//Kj7KyB88 + vQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOFjkVh5G+f8aQfT/HkHw/yUo + 7PcjPOwUBg4uAgMDEUsAAAAVAAAAAAAAAAAAAAAAAAAAAAAAAAATJ44GKTf4jiIt6ZoyQeLoMk7j/yQ3 + 3f8lJueFIRi9BBYQfaAlHNj/FR+30Zmn2RwAAAAAAAAAAAAAAAAHBhwvEQpfvxkbmkAmKu2oNjPg/lpZ + 4/9eX+T/JirjviIy5bMjLNb/IB7Q/zNC5f9UW2mDAAAAAAAAAAAAAAAAExNbniMv6v8cQubiK1P072B4 + 5P9YaOD/O0zq/zJP7/8qUvH/IjTZ/yE87f8lLab/npusZAAAAAAjHMELIjrRQB5D4OouUe7/QlTk/ywx + 8P9gZeb/Z27b/1Ng6P88Rur/Mjjh/ygp3v8hIdv/dXGV/1Zm228AAAAAXl3cWDpB5+woL+j/hofF/4Bg + c/8/GXj/Rje7/11d1P9STtj/HhTd/x4T2f8nI9v/QUTm/21ulP8/SsK6IiXpCWVj6hNJRufxPDnl/5iV + tf57Sjv/Zkts70RFzOI0OuH/Rk3e/zxF3v9IT9//KCzD/zVG1v04QX3jOzfbLycc3wN+g+4jV1Po/ygi + 6JF9g7slenx3NXZ2ZxFDPq0FHxXnPz045Zs9O+XoR0jo/yw05/8sOcD9W2J+twAAAAAAAAAALirhBi0m + 4UcsJuoDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJyDgDSEZ304rI+GfRUDk5lle7f8oP+3cIUbvRgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAERC5QsiGt9IJRrenCUu + 5YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP//AAP//wAD/j/nFvgf5xb4A4AU4AEAAMABAADAAQAAAAEAAAAAAAAAAAAAAAMAAB+A + AAP/8HMf//8AAP//AAQ= + + + + 172, 17 + + + 326, 17 + + + 17, 17 + + + 481, 17 + + + 50 + + + + AAABAAkAAAAAAAEAIAAoIAQAlgAAAICAAAABACAAKAgBAL4gBABgYAAAAQAgAKiUAADmKAUASEgAAAEA + IACIVAAAjr0FAEBAAAABACAAKEIAABYSBgAwMAAAAQAgAKglAAA+VAYAICAAAAEAIACoEAAA5nkGABgY + AAABACAAiAkAAI6KBgAQEAAAAQAgAGgEAAAWlAYAKAAAAAABAAAAAgAAAQAgAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRz + cgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBw + cABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJ + CQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9v + bwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFx + cQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGho + aAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNy + cgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1s + awBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFx + cABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBv + cABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNz + dABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNy + cwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1t + bABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxs + bQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9u + bgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBw + cABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4 + dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNy + cgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5t + bAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1t + bABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1s + bABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBw + bwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBw + cABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNz + cwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1t + bQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1s + bABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1t + bQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFw + cABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFx + cQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJx + cgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBw + cABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5u + bQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1t + bABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRz + cgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBw + cABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJ + CQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9v + bwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFx + cQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGho + aAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNy + cgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1s + awBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFx + cABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBv + cABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNz + dABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAAlIMAAJSDAACUg + wAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAAlIMAAJSDAACUg + wAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAAlIMAAJSDAACUg + wAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNy + cwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1t + bABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxs + bQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9u + bgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBw + cABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4 + dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNy + cgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5t + bAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1t + bABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1s + bABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBw + bwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBw + cABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNz + cwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1t + bQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1s + bABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1t + bQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFw + cABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFx + cQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJx + cgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBw + cABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5u + bQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1t + bABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRz + cgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBw + cABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJ + CQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9v + bwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFx + cQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGho + aAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNy + cgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1s + awBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFx + cABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBv + cABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNz + dABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNy + cwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1t + bABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxs + bQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9u + bgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBw + cABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4 + dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNy + cgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5t + bAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQAAABaAAAAlAAAALgAAAC/AAAAvgAAAL8AAAC+AAAApAAA + AIIAAABVAAAALQAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1t + bABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1s + bABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAGgAAAHIAAADHAAAA9gAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA9QAAANMAAACVAAAARgAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAD///8A////ACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBw + bwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBw + cABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAJAAAAdgAAAOMAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADIAAAAWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA////AP///wD///8AJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNz + cwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1t + bQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAyAAAAxQAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAACoAAAAGgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP///wD///8A////AP///wAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1s + bABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1t + bQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAABcAAAA7AAAAP8AAAD/AQMA/wUHEv8MDDD/ExVB/xUYUP8VGFD/EhQ//xET + Of8JCCn/BQcV/wUHBv8AAQD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAANEAAAAkAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////ACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFw + cABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFx + cQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAB4AAAA/wAAAP8EBgT/EhRO/x4dnf8lI8//Jh7r/ycg7f8oIfD/JyHv/ycg + 7f8nIOv/Jh7q/yUj1P8kIrf/HBqR/xUYVf8ICRj/AAEA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA0AAA + ABUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8AJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJx + cgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBw + cABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAACBAAAA/wECAP8SFEj/IyG4/ygh8v8mHvb/JRzu/yUe6f8lHej/JR3p/yUd + 6f8lHOr/Ixrs/yIa7v8hGPP/IRj4/yMa/P8kHPn/Ix7b/xoah/8KCyH/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAACjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5u + bQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1t + bABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAA + AAAAAAAAAAAAAAAAAAAAAACAAAAA/wcKEP8dHov/KCHw/yYd9P8lHej/JR3n/yUd5/8lHej/Ixvr/yEZ + 7f8gF+//Hxvo/yUi2v8tKsv/NDnA/zk+uP84Pbv/MzfB/ywn0/8oJOz/IR/d/xETYv8AAQD/AAAA/wAA + AP8AAAD/AAAA/gAAAEcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////ACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRz + cgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBw + cABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJ + CQAAAAAAAAAAAAAAAAAAAABrAAAA/w0PJf8kIrv/JyD3/yUd6v8lHef/JBzp/yIa7P8gGO7/Hxnq/yUi + 3P8wMMb/RUms/2Von/+Gi5z/qKml/7q7tP/Dxbz/w8S8/7q7s/+lpqP/g4ea/15ho/89QcP/HCF3/wAA + BP8AAAD/AAAA/wAAAP8AAADGAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP// + /wD///8AJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9v + bwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFx + cQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGho + aAAJCQkAAAAAAAAAAAAAAABbAAAA/A8SNP8mIdD/Jhz2/yMa6/8hGe3/Hxfu/yAd5P8qJ8//Oz+0/11g + nf+Bh5n/q6yn/9HSxv/s6+D////5//////////////////////////////////389//j49f/t7mw/3V6 + n/8qLkf/AAAA/wAAAP8AAAD/AAAA/wAAAEcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gACYe4AAmHuAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNy + cgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1s + awBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFx + cABoaGgACQkJAAAAAAAAAABFAAAA8g8TPf8kH97/IRj5/x4Z6v8kIdv/MTHE/0pOp/9wc5n/mZ2f/8bH + u//k5Nj//Pv1//////////////////////////////////////////////////////////////////// + ///t7ef/p6eo/0tLS/8NDQ3/AAAA/wAAAP8AAACmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBv + cABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNz + dABxcXAAaGhoAAkJCQAAAAAtAAAA5gkMNf8hINn/KSXd/zg7t/9YXZ7/gYaY/7Cxq//X2Mz/8vHo//// + /v////////////////////////////////////////////////////////////////////////////// + ///////////////////f3tr/goGA/xoaGf8AAAD/AAAA6gAAABYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wAlHt8AJR7fACUe3wAlHt8AJR7fACUe3wAlHt8AJR7fACUe + 3wAlHt8AJR7fACUe3wAlHt8AJR7fACUe3wB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNy + cwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1t + bABzc3QAcXFwAGhoaAAJCQkiAAAA1BcZH/9CSZH/aGqm/5CUm/+/wLX/4+PX//v69P////////////// + ///////////////////////////////////////////9/////f/+/PX/6+vp/9rf6v/S3PT/zdr9/8PO + +/++yPr/vsn6/8DL+//M2f3/4Ov//+/x8v+TkIb/GxoW/wAAAP8AAABTAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8AJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxs + bQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9u + bgBtbWwAc3N0AHFxcBRoaGhJS0tMz21tbf+ioaP/z87L/+zs4f////v///////////////////////// + ///////////////////////////+//j59f/a4vD/v8np/5im3f90i9v/V3Hd/z1a4f8wUeH/KU7p/yVN + 8f8cQu//GD3v/xk+7/8aQO//I0vw/y9V8f9aevr/l6vr/3BzdP8SEAf/AAAAfgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBw + cABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4 + dwBvbm4hbW1sWHNzdKCLjIvYrq6u/9jY2f/29vf///////////////////////////////////////// + ///////////+/+3y+v/F0vb/obHv/3CL6f9LaOf/MFPn/x1D5/8WPO//Fzzv/xk+8f8bQPL/HkLx/x9D + 8P8gQ+//IkXv/yJG8P8iRu//IkXw/yBE7/8eQe//GD3u/xxD9f8+XuP/Rk9w/wkHAIkJBwAACQcAAAkH + AAAJBwAACQcAAAkHAAAJBwAACQcAAAkHAAAJBwAACQcAAAkHAAAJBwAACQcAAAkHAAAJBwAACQcAAAkH + AAAJBwAACQcAAAkHAAAJBwAACQcAAAkHAAAJBwAACQcAAAkHAAAJBwAACQcAAAkHAAAJBwAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNy + cgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHACbWxsL25t + bGp4eHe0k5ST5Le3t//e3t//+Pj5///////////////////////////////////////////////9/+3y + 9//AzvT/kqjy/1597/89Xu7/Ikjs/xY87v8XPe7/GT/w/x5C8f8hRfH/I0fw/yNH8P8jRvD/I0fw/yNG + 7/8jR+//I0fv/yNH7/8jRu//I0bv/yNH7/8jR/D/I0fw/yNH7/8iRe//HkP3/yxP7f8/SnKbO13kACFE + 8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE + 8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1t + bABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxBm1tbT1tbW14fX19w5yb + nPDDw8T/5ubm//39/v/////////////////////////////////////////+////9v/d4u7/qrjs/2uG + 6/88Xez/IEbr/xY87v8YPe//G0Dv/yFE8P8jRu//I0fv/yNH8P8jR+//I0bv/yNG7/8jRvD/I0bw/yNH + 7/8jRvD/I0fv/yNG7/8jR+//I0fw/yNH8P8jR+//I0fw/yNH8P8jRu//I0fv/yNH7/8gRfT/LlHr8Dtd + 5DEhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE + 8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJh7gACYe + 4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gAHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBw + bwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcAttbWxGcHBwh4KCgsyjo6T4zc3P/+zs + 7f//////////////////////////////////////////////+//8+vD/2Nna/6Wv0/9geNj/MVPn/xo/ + 7P8YPe//HEHw/yFE8P8jR/D/I0fw/yNG7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH7/8jR/D/I0bw/yNH + 8P8jRu//I0bv/yNH8P8jR+//I0bv/yNH8P8jR/D/I0fv/yNH7/8jR+//I0bv/yNH8P8jR+//I0fv/yFG + 8f8hRfLNIUTyFSFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE + 8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE + 8gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wAmHt8AJh7fACYe3wAmHt8AJh7fACYe3wAmHt8AJh7fACYe3wAmHt8AJh7fACYe3wB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNz + cwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3ARbWxrTXFxcZaIiIjVqamq/NPT0//x8fL///////// + //////////////////////////////////////j/8/Lp/8rN1f+Un8r/XHLL/zFR2P8bQOf/GT/w/x5D + 8f8iRfD/I0fw/yNH8P8jR+//I0fw/yNH8P8jR+//I0bv/yNG7/8jR/D/I0bw/yNG7/8jRu//I0fw/yNG + 8P8jR+//I0fv/yNH8P8jR/D/I0fv/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fw/yNH7/8jR/D/I0jx/yNH + 8P8jR+//I0fv/yNH76IjR+8AJEfwACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8AJh7fACYe3wAmHt8AJh7fACYe3wAmHt8AJh7fACYe3wAmHt8AJh7fACYe3wAmHt8AdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1s + bABzc3MAcHBvAG1tbABzcnIAcHBwE2xsbVRzcnOdioqL3a+vsP/X19f/9vb2//////////////////// + ///////////////////////////2/+Xl4f+6v9H/gI7G/01mzv8pSdv/G0Dp/xk/8v8eQ/L/Ikbx/yNH + 8P8jR/D/I0fv/yNH8P8jR+//I0fv/yNH7/8jRu//I0fv/yNH7/8jRu//I0fv/yNG7/8jRu//I0bv/yNH + 7/8jR+//I0fw/yNH7/8jR/D/I0fv/yNH7/8jR/D/I0fv/yNH8P8jR+//I0bv/yNH8P8jSPD/I0fv/yRC + 7f8jR+//I0jw/yNH7/8jRu//I0fvciRH8AAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AB4W3wAeFt8AHhbfAB4W3wAeFt8AHhbfAB4W3wAeFt8AHhbfAB4W3wAeFt8AHhbfAHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFw + cABtbGwAc3NzAHBwbxJtbWxUc3JynIqKi9+xsbL/29vc//j4+f////////////////////////////// + ///////////8//399P/X2dv/prHV/2t+y/89WtT/IkTh/xk/7v8bQPL/IEXy/yJG8f8jR/D/I0bv/yNH + 7/8jR/D/I0fw/yNH8P8jR+//I0bv/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0fv/yNG7/8jR/D/I0fv/yNH + 7/8jR+//I0fv/yNH8P8jRvD/I0fv/yNH7/8jR/D/I0fw/yNG7/8jR+//I0fv/yNG7/8jSPD/JETu/yYq + 4/8mIOD/JS3k/yNF7/8jSfD/I0fv/yNH7/MkR/A7I0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8AUFDnAFBQ5wBQUOcAUFDnAFBQ5wBQUOcAUFDnAFBQ5wBQUOcAUFDnAFBQ + 5wB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJx + cgBxcHARbWxsVHNzc52JiorhsrGy/9zb3P/5+fn///////////////////////////////////////// + +v/19fH/x8ze/5Gf1f9ZcdH/MFDd/xxA5/8ZP/H/HUPy/yFF8f8jRvD/I0fw/yNH7/8jR+//I0bv/yNH + 8P8jR+//I0fw/yNH8P8jRvD/I0bv/yNH8P8jR/D/I0bv/yNH8P8jR/D/I0fw/yNH8P8jR+//I0fv/yNH + 7/8jR/D/I0fw/yNG7/8jR/D/I0bv/yNH7/8jR+//I0fv/yNG7/8jRu//I0fv/yNH7/8jR+//I0bv/yYp + 4/8mHN7/Jh7f/yYc3v8mJ+P/I0Ht/yNJ8P8jR+//I0fv0CNH7xMjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AMTM+gDEzPoAxMz6AMTM+gDEzPoAxMz6AMTM+gDEzPoAxMz6AMTM + +gDEzPoAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyBW5u + bUVycXKViYmJ4LKysv/c3Nz/+fn5//////////////////////////////////////////r/6+zw/7TB + 5f97jtr/RmPb/yVH5f8ZP+z/GkDy/x9E8v8iRvH/I0bv/yNH8P8jR+//I0fw/yNH7/8jR/D/I0fv/yNH + 7/8jRvD/I0bv/yNH8P8jR+//I0fv/yNH7/8jR+//I0bv/yNG7/8jR/D/I0fw/yNG7/8jR/D/I0fv/yNH + 7/8jR+//I0bv/yNG8P8kRu//I0fw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0nw/yU0 + 5/8mHN//Jh7f/yYf4P8lHuD/Jhzf/yYk4f8kQez/I0nw/yNG7/8jRu+aI0fwACNH8AAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A09L8ANPS/ADT0vwA09L8ANPS/ADT0vwA09L8ANPS + /ADT0vwA09L8AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vYH9/ + f8ypqKr/19fY//j4+f///////////////////////////////////////f76/9zi8P+ktOn/aIDh/zhY + 4v8eQ+j/GD7v/x1C8v8gRfH/Ikfw/yNH8P8jRu//I0fv/yNH7/8jR/D/I0bw/yNH7/8jR+//I0fv/yNH + 7/8jRu//I0bv/yNH8P8jR/D/I0fv/yNG7/8jR+//I0fw/yNG8P8jRu//I0bv/yNH7/8jR+//I0fw/yNG + 8P8jR+//I0fw/yNH8P8jRu//JEfw/yNH8P8jR/D/I0bv/yNH7/8jR+//I0bv/yNH7/8jR/D/I0jw/yRD + 7f8mIuD/Jh3f/yYf4P8mH+D/Jh/f/yYf4P8mHd//JiXh/yRB7P8jSPD/I0bv/yNH8FwjR/AAI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AM/P+wDPz/sAz8/7AM/P+wDPz/sAz8/7AM/P + +wDPz/sAz8/7AM/P+wB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQCc3Jyiays + rf/u7u/////////////////////////////////////+//n7/P/L1vL/kqbt/1Vz5v8tT+j/Gj/s/xg+ + 8P8eQ/H/IUXw/yNG7/8jR/D/I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNG7/8jRu//I0fw/yNH + 7/8jRu//I0bv/yNH7/8jR/D/I0fv/yNH8P8jR/D/I0fv/yNH8P8jR/D/I0fv/yNG7/8jRu//I0fw/yNG + 8P8jR+//I0fw/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fw/yNG7/8jR+//I0fv/yNH7/8jR/D/I0fw/yNJ + 8P8lNej/Jh3f/yYf4P8mH+D/Jh7g/yYe4P8mH9//Jh/g/yYd3/8mJOH/JELt/yNJ8P8jR/DjI0fwIiNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Az8/7AM/P+wDPz/sAz8/7AM/P + +wDPz/sAz8/7AM/P+wDPz/sAcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBw + bwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBw + bwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBw + bwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBw + bwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBw + bwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvPaOj + o////////////////////////////////v/x9fr/vszz/4OZ7f9HaOv/JEjs/xc97f8bQPH/IETx/yJG + 8P8jRu//I0bv/yNH7/8jRu//I0fw/yNH8P8jRu//I0fv/yNH7/8jR/D/I0fv/yNH7/8jRu//I0bv/yNH + 8P8jR+//I0bv/yNG7/8jR+//I0bv/yNH7/8jR+//I0fw/yNH7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNG + 7/8jRu//I0fv/yNH7/8jR+//I0bv/yNH8P8jR/D/I0bv/yNH8P8jRu//I0bv/yNH7/8jR+//I0bv/yNH + 8P8jR+//Jifj/yYd3/8mH+D/Jh7g/yYe4P8mH+D/Jh7f/yYf4P8mHuD/Jhzf/yUp4/8jR+//I0fv/yNH + 77AjRu8GI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM/P+gDPz/oAz8/6AM/P + +gDPz/oAz8/6AM/P+gDPz/oAz8/6AHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cDafn6D7/f3+////////////9/n6/7nG7P94kfD/PF7s/x5D7v8WPO7/HEDv/yFF7/8jR/D/I0bv/yNH + 8P8jR+//I0fw/yNG7/8jRu//I0fw/yNH8P8jR/D/I0bv/yNH7/8jRvD/I0fw/yNH7/8jR/D/I0bw/yNH + 8P8jR/D/I0fv/yNH7/8jR/D/I0fv/yNG7/8jR/D/I0fw/yNH7/8jR+//I0bv/yNH7/8jR/D/I0fw/yNG + 7/8jRu//I0bv/yNH7/8jRu//I0fv/yNG7/8jR/D/I0fv/yNG7/8jR/D/I0bw/yNG7/8jR+//I0fv/yNH + 8P8jSPD/JEHt/yYg4P8mHd//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mHN//JDTo/yNJ + 8P8jR+//I0fvTyNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Azs/6AM7P + +gDOz/oAzs/6AM7P+gDOz/oAzs/6AM7P+gBwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwjMLCw///////0d38/0Vn7f8aP+v/Fjzv/x1B7/8iRfD/I0bw/yNG7/8jR+//I0bv/yNH + 8P8jRu//I0bv/yNH8P8jR+//I0fw/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH + 8P8jR/D/I0bv/yNH7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH8P8jR+//I0fv/yNH7/8jR/D/I0fw/yNH + 7/8jRu//I0fv/yNH7/8jR/D/I0fv/yNG8P8jR/D/I0fv/yNH7/8jR/D/I0bv/yNH8P8jR/D/I0fv/yNH + 8P8jR/D/I0nx/yQ56v8mHd//Jh7f/yYf4P8mHuD/Jh/g/yYf4P8lHt//Jh7g/yYf4P8mH+D/Jh3f/yYk + 4f8kRe7/I0jw/yNH8GgjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM7P + +wDOz/sAzs/7AM7P+wDOz/sAzs/7AM7P+wDOz/sAJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNI8ABycnIAcnJyAHJycgBycnIAcnJyAHJy + cgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJy + cgBycnIAcnJyAHJychGCgHrLsbrW/zlf9/8VO+//Ikbw/yNH7/8jRu//I0fw/yNH8P8jRvD/I0bv/yNG + 7/8jR/D/I0bw/yNG7/8jR+//I0fv/yNH8P8jR/D/I0bv/yNG7/8jR/D/I0fw/yNH7/8jRu//I0fw/yNH + 7/8jR+//I0fw/yNG8P8jR+//I0fv/yNH7/8jR/D/I0bv/yNG8P8jR+//I0bv/yNG7/8jR+//I0fv/yNH + 7/8jR+//I0fw/yNH8P8jR+//I0fv/yNG7/8jRu//I0fv/yNH7/8jR/D/I0fw/yNG7/8jR/D/I0fv/yNH + 7/8jR+//I0fv/yNJ8P8lNOf/Jhzf/yYe3/8mH9//Jh/g/yYe4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mIOD/JD7s/yNJ8P8jRu9mI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8Az8/6AM/P+gDPz/oAz8/6AM/P+gDPz/oAz8/6ACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjSPAAI0bvACNJ8QAjSPAAcnJyAHJy + cgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJy + cgBycnIAcnJyAHJycgBycnIAeXdtI1RgmJcaQPX/JEfw/yNH7/8jRu//I0bw/yNH7/8jR+//I0fw/yNH + 8P8jRu//I0bw/yNH8P8jR+//I0fw/yNH7/8jR/D/I0fv/yNH7/8jR+//I0bw/yNH8P8jRu//I0fv/yNH + 7/8jR/D/I0fw/yNH8P8jR/D/I0bv/yNH7/8jR+//I0bv/yNH8P8jR/D/I0fw/yNG7/8jRu//I0fv/yNG + 7/8jR/D/I0fv/yNH7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH + 7/8jR+//I0fv/yNH8P8jSfD/JS/l/yYd3/8mHt//Jh7f/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7f/yYf + 4P8mH+D/Jhvf/yQ66v8jSvH/I0fvWyNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////ANPS/ADT0vwA09L8ANPS/ADT0vwA09L8ANPS/AAmIcAAJiHAACYhwAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0jwACNG7wAjSfEAI0jwACNI + 8AAjR/AAI0jwAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJy + cgBycnIAcnJyAHJycgBycnIAcnJyAHl3bQBUYJgAI0bwwSNG8P8jR/D/I0fw/yNH8P8jR+//I0fv/yNG + 7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fw/yNG7/8jR+//I0fv/yNG7/8jR/D/I0fv/yNH + 7/8jR+//I0fw/yNG7/8jRu//I0fv/yNG8P8jR/D/I0fv/yNH7/8jRu//I0fv/yNH8P8jR+//I0fw/yNH + 8P8jR+//I0fw/yNG7/8jRu//I0fv/yNH8P8jRvD/I0bv/yNH8P8jR+//I0fv/yNG8P8jR/D/I0fw/yNG + 7/8jRu//I0fv/yNH8P8jR/D/I0jw/yYr5P8mHd//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYd3/8kPOv/I0nx/yNH70kjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8AvsL5AL7C+QC+wvkAvsL5AL7C+QC+wvkAJiHAACYhwAAmIcAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNI8AAjRu8AI0nxACNI + 8AAjSPAAI0fwACNI8AAjSfAAI0XuACNI8ABycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJy + cgBycnIAcnJyAHJycgBycnIAcnJyAHJycgB5d20AI0fvACNH71MjR/D+I0fw/yNH8P8jR+//I0fw/yNH + 8P8jRu//I0fw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fw/yNH8P8jRu//I0fv/yNH + 7/8jR+//I0fw/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0fw/yNH7/8jR+//I0fv/yNG7/8jRvD/I0fv/yNH + 7/8jR+//I0fv/yNH7/8jR+//I0bv/yNG7/8jR+//I0bv/yNH7/8jR+//I0bv/yNG8P8jR/D/I0fv/yNG + 7/8jRu//I0bv/yNH7/8jR+//I0fw/yNH7/8mJuL/Jh3g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mIeD/JEHt/yNI8PEjRu8xI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AKGt9AChrfQAoa30AKGt9AChrfQAoa30ACYhwAAmIcAAJiHAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjSPAAI0bvACNJ + 8QAjSPAAI0jwACNH8AAjSPAAI0nwACNF7gAjSPAAI0rxACNJ8AAkR/AAJEfwACRH8AAkR/AAJEfwACRH + 8AAkR/AAJEfwACRH8AAkR/AAJEfwACRH8AAkR/AAJEfwACRH8AAkR/AHI0fwwSNH7/8jR+//I0fw/yNH + 8P8jR/D/I0fv/yNG7/8jRvD/I0bv/yNH7/8jR/D/I0fv/yNH7/8jR/D/I0fv/yNH7/8jR+//I0fv/yNH + 7/8jRu//I0bw/yNH8P8jRvD/I0fw/yNH8P8jR+//I0bv/yNG7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH + 7/8jR+//I0fv/yNG7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0bv/yNG7/8jR+//I0bv/yNG + 8P8jRu//I0bv/yNG7/8jRu//I0fv/yNH8P8kRO7/JiLh/yYd4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mHd//JiTh/yRF7v8jR/DYI0fwEiNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8Ac4ntAHOJ7QBzie0Ac4ntAHOJ7QAmIcAAJiHAACYh + wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0jwACNG + 7wAjSfEAI0jwACNI8AAjR/AAI0jwACNJ8AAjRe4AI0jwACNK8QAjSfAAI0fvACRH8AAkR/AAJEfwACRH + 8AAkR/AAJEfwACRH8AAkR/AAJEfwACRH8AAkR/AAJEfwACRH8AAkR/AAJEfwACNH8FMjR+/+I0fv/yNH + 8P8jR/D/I0fv/yNH7/8jR+//I0bv/yNG7/8jRu//I0fw/yNH7/8jR+//I0bv/yNH7/8jR/D/I0fw/yNH + 7/8jRu//I0bv/yNH8P8jR/D/I0bv/yNH8P8jRvD/I0fv/yNG7/8jR/D/I0fv/yNG8P8jRu//I0fw/yNH + 7/8jRu//I0bv/yNG8P8jR+//I0fv/yNG7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH8P8jRvD/I0fv/yNG + 7/8jRvD/I0fv/yNH8P8jRu//I0fv/yNH8P8jSPD/JEDs/yYg4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jhzf/yUp4/8jSfD/I0fwwCNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////ADU54wA1OeMANTnjADU54wA1OeMAJiHAACYh + wAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNI + 8AAjRu8AI0nxACNI8AAjSPAAI0fwACNI8AAjSfAAI0XuACNI8AAjSvEAI0nwACNH7wAjR/AAI0jwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AHI0fvwiNH + 7/8jR+//I0fw/yNH7/8jRu//I0fv/yNH7/8jRvD/I0fv/yNH7/8jR+//I0fv/yNH8P8jR/D/I0fw/yNH + 8P8jR+//I0bv/yNG7/8jR+//I0fv/yNH7/8jRvD/I0bv/yNH7/8jR/D/I0fw/yNG7/8jRu//I0fw/yNH + 8P8jRu//I0bv/yNG7/8jR/D/I0fv/yNH7/8jRu//I0bv/yNH8P8jR/D/I0fv/yNH8P8jR/D/I0fw/yNH + 7/8jRu//I0fw/yNH7/8jRu//I0fv/yNG7/8jRu//I0nw/yQ46f8mHd//Jh/g/yYe3/8mHuD/Jh7g/yYe + 4P8mH+D/Jh/g/yYe3/8mH+D/Jh7g/yYc3/8lL+X/I0nw/yNH8JAjR/AAI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AIxnfACMZ3wAjGd8AIxnfACYh + wAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjSPAAI0bvACNJ8QAjSPAAI0jwACNH8AAjSPAAI0nwACNF7gAjSPAAI0rxACNJ8AAjR+8AI0fwACNI + 8AAjSPAAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNG + 71IjR/D9I0fv/yNG7/8jR+//I0fw/yNH8P8jR/D/I0bv/yNH8P8jRvD/I0fv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNG7/8jR+//I0fw/yNH8P8jR+//I0fw/yNH + 8P8jR+//I0bv/yNH7/8jR+//I0fw/yNH8P8jRu//I0fv/yNG7/8jRu//I0fv/yNH7/8jR/D/I0bv/yNG + 7/8jRvD/I0fv/yNH7/8jRu//I0fv/yNH7/8jRu//I0bw/yNJ8P8lLuX/Jh3f/yYf4P8mH+D/Jh7g/yYe + 4P8mHuD/JR7f/yYf4P8mH+D/Jh/g/yYf4P8mHN//JTHm/yNJ8P8jR+9eI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ACUe3wAlHt8AJR7fACUe + 3wAmIcAAJiHAACYhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0jwACNG7wAjSfEAI0jwACNI8AAjR/AAI0jwACNJ8AAjRe4AI0jwACNK8QAjSfAAI0fvACNH + 8AAjSPAAI0jwACNF7gAjSO8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8FI0fvuCNH8P8jRvD/I0fv/yNH8P8jR/D/I0fv/yNH7/8jRvD/I0bw/yNG7/8jRu//I0fv/yNH + 7/8jRu//I0fw/yNH8P8jR/D/I0fw/yNG7/8jR+//I0fv/yNH8P8jR/D/I0fw/yNH8P8jR/D/I0fw/yNH + 8P8jRvD/I0bw/yNG8P8jR/D/I0fv/yNH8P8jR/D/I0bw/yNH8P8jR+//I0bw/yNG8P8jRvD/I0fw/yNH + 8P8jRvD/I0bw/yNH7/8kR/D/JEfw/yNG8P8jRu//I0fw/yNI8P8jRO7/JSPh/yYd3/8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yUe3/8mH9//Jh/g/yYe4P8mHuD/Jhzf/yUx5f8jSfD0I0bwMyNG8AAjRvAAI0bwACNG + 8AAjRvAAI0bwACNG8AAjRvAAI0bwACNG8AAjRvAAI0bwACNG8AAjRvAAI0bwACNG8AAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJh/gACYf + 4AAmH+AAJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNI8AAjRu8AI0nxACNI8AAjSPAAI0fwACNI8AAjSfAAI0XuACNI8AAjSvEAI0nwACNH + 7wAjR/AAI0jwACNI8AAjRe4AI0jvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNG70gjR/D8I0fw/yNG8P8jRu//I0bv/yNH8P8jRu//I0fv/yNH8P8jR/D/I0fw/yNH + 7/8jR+//I0bv/yNG8P8jRvD/I0bv/yNH8P8jRvD/I0fw/yNH7/8jR/D/I0bw/yRH8P8jRu//I0bv/yNH + 8P8jR+//I0bw/yNH7/8jR/D/I0bw/yNH8P8jR+//I0fw/yNH8P8jR/D/I0fv/yNH8P8jRvD/I0fw/yNH + 8P8jRvD/I0bw/yNH8P8jRu//I0bv/yNH8P8jRu//I0bv/yNG8P8jSfH/JTnp/yYd3/8mH+D/JR7f/yYf + 4P8lH9//JR7f/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh7f/yYd3/8lMeb/I0nwySNG7wcjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAABAAAAHAAAADsAAABJAAAAVgAAAGIAAABSAAAARQAAADQAAAAWAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ACYf + 4AAmH+AAJh/gACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjSPAAI0bvACNJ8QAjSPAAI0jwACNH8AAjSPAAI0nwACNF7gAjSPAAI0rxACNJ + 8AAjR+8AI0fwACNI8AAjSPAAI0XuACNI7wAlOOkAJD3rACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8DI0bvsyNH7/8jRvD/I0fv/yNH8P8jR/D/I0bw/yNH8P8jR/D/I0fw/yNH + 8P8jR/D/I0fw/yNH7/8jR/D/I0bw/yNH7/8jR+//I0fw/yNH8P8jRvD/I0bv/yNH8P8jRvD/I0fv/yNH + 7/8jR+//I0bw/yNH7/8jRu//I0fv/yNG7/8jRvD/I0bw/yNG7/8jR/D/I0fv/yNH8P8jR+//I0bv/yNH + 8P8jR/D/I0fv/yNH7/8jR+//JEfw/yNG8P8kR/D/I0fv/yNG7/8jR+//I0jw/yYq5P8mHd//Jh/g/yUe + 3/8mH+D/Jh/g/yYf4P8mH9//Jh7f/yYf3/8mHuD/Jh/g/yYf3/8mHd//JS3l/yNL8H0jRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAgAAADsAAACBAAAAvQAAAN4AAAD6AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD0AAAA2QAA + ALUAAABmAAAAHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wAlHt8AJR7fACUe3wAmIcAAJiHAACYhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0jwACNG7wAjSfEAI0jwACNI8AAjR/AAI0jwACNJ8AAjRe4AI0jwACNK + 8QAjSfAAI0fvACNH8AAjSPAAI0jwACNF7gAjSO8AJTjpACQ96wAkP+sAI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7zojRu/4I0fv/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fv/yNG + 7/8jRu//I0fv/yNH7/8jR/D/I0bw/yNG7/8jRu//I0fv/yNH8P8jR+//I0fv/yNG7/8jRu//I0fv/yNH + 8P8jR/D/I0fv/yNG8P8jR/D/I0fw/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0fv/yNG7/8jRu//I0bv/yNH + 7/8jR+//I0fv/yNH7/8jRu//I0fw/yRH7/8jR/D/JEfv/yNH7/8jRu//I0jw/yQ/7P8mIOD/Jh7g/yYe + 4P8mHuD/Jh7g/yYf4P8mHuD/Jh7f/yUd3/8lK+X/JS3l/yYf4P8mH+D/Jh7g/yYf4P8lLeVPJS3lACUt + 5QAlLeUAJS3lACUt5QAlLeUAJS3lACUt5QAlLeUAJS3lACUt5QAlLeUAJS3lACUt5QAlLeUAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAHAAAAXQAAAL4AAAD4AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAN8AAAB8AAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AB8V3wAfFd8AJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNI8AAjRu8AI0nxACNI8AAjSPAAI0fwACNI8AAjSfAAI0XuACNI + 8AAjSvEAI0nwACNH7wAjR/AAI0jwACNI8AAjRe4AI0jvACU46QAkPesAJD/rACNE7gAjR+8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0fwpiNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH + 8P8jR+//I0fw/yNH7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH7/8jR/D/I0fv/yNH8P8jRvD/I0bw/yNH + 7/8jRvD/I0fw/yNH7/8jR+//I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH7/8jR+//I0fw/yNH + 7/8jR/D/I0fv/yNH7/8jRu//I0bw/yNH8P8jRu//JEfw/yNH7/8jRu//I0bv/yNJ8f8lMOb/Jhzf/yYe + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYd3/8mIuD/JTXo/yU26f8mJeL/Jh7f/yYe3/8mH+DwJhzfLiYc + 3wAmHN8AJhzfACYc3wAmHN8AJhzfACYc3wAmHN8AJhzfACYc3wAmHN8AJhzfACYc3wAmHN8AJhzfAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAA3AAAAvwAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAM0AAABEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wBSY+cAUmPnACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjSPAAI0bvACNJ8QAjSPAAI0jwACNH8AAjSPAAI0nwACNF + 7gAjSPAAI0rxACNJ8AAjR+8AI0fwACNI8AAjSPAAI0XuACNI7wAlOOkAJD3rACQ/6wAjRO4AI0fvACNG + 7gAkRu4AI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8C4jRvDyI0fv/yNH7/8jR+//I0fv/yNH + 7/8jRu//I0fw/yNH8P8jR+//I0fv/yNH7/8jR/D/I0fw/yNH7/8jR+//I0bv/yNG7/8jR+//I0fw/yNG + 8P8jRu//I0fv/yNH8P8jRu//I0bv/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH7/8jRu//I0bv/yNG + 7/8jRvD/I0bw/yNH7/8jR/D/I0fw/yNH8P8jR/D/I0fv/yRH8P8jR+//I0bw/yNI8P8kRO7/JiLh/yYd + 3/8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHeD/Jinj/yU26f8mNun/Jink/yYd4P8mHuD/Jh/g0SYf + 4AgmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAB1AAAA8QAAAP8AAAD/AAAA/wABAP8BAgD/BQcJ/wwPG/8NEST/DREq/w0RJf8MEB3/BQYJ/wEC + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA9QAAAHIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AIaZ7wAmIcAAJiHAACYhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0jwACNG7wAjSfEAI0jwACNI8AAjR/AAI0jwACNJ + 8AAjRe4AI0jwACNK8QAjSfAAI0fvACNH8AAjSPAAI0jwACNF7gAjSO8AJTjpACQ96wAkP+sAI0TuACNH + 7wAjRu4AJEbuACNJ8QAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0bvjiNH7/8jR+//I0fw/yNH + 8P8jR+//I0fv/yNH8P8jR+//I0fv/yNH7/8jRu//I0bv/yNH7/8jR+//I0fw/yNH8P8jRu//I0bv/yNG + 8P8jRu//I0fv/yNH7/8jRu//I0bv/yNG7/8jR+//I0fv/yNH8P8jR+//I0bv/yNH8P8jRu//I0fw/yNH + 7/8jRu//I0fw/yNH8P8jRu//I0fw/yNH8P8jR+//I0bw/yNH8P8jR/D/I0fv/yNH7/8jSfD/JDbo/yYc + 3/8mHt//Jirk/yYi4f8mHt//Jh7g/yYe4P8mHuD/Jh7g/yUw5v8lNun/Jjbp/yUu5v8mHuD/Jh7g/yYf + 4JMmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + ABEAAACiAAAA/wAAAP8AAQD/BggL/w8SPv8aGnD/HyCo/yIdx/8mIdL/JiLX/ycj2v8mItf/JiLU/yId + xv8fIJr/Fxhk/wwOJ/8BAgD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAApQAAABUAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wDGyPkAJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNI8AAjRu8AI0nxACNI8AAjSPAAI0fwACNI + 8AAjSfAAI0XuACNI8AAjSvEAI0nwACNH7wAjR/AAI0jwACNI8AAjRe4AI0jvACU46QAkPesAJD/rACNE + 7gAjR+8AI0buACRG7gAjSfEAJDzqACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7xwjR+/iI0fv/yNH + 7/8jR+//I0fv/yNH7/8jRu//I0bv/yNH8P8jR/D/I0fw/yNH7/8jR+//I0bw/yNH8P8jR+//I0bv/yNH + 8P8jRu//I0fw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fw/yNH7/8jR/D/I0fw/yNH7/8jR/D/I0bv/yNH + 8P8jR/D/I0fv/yNH8P8jR/D/I0fv/yNG7/8jR/D/I0bv/yNH8P8jR/D/I0fv/yNG8P8jSPD/I0bv/yYl + 4v8mHN//JiLh/yU06f8mIuH/Jh7g/yYf4P8mH+D/Jh3g/yYj4f8lNen/JTXo/yU26P8lL+b/Jh7f/yYe + 3/YlH983JR/fACUf3wAlH98AJR/fACUf3wAlH98AJR/fACUf3wAlH98AJR/fACUf3wAlH98AJR/fACUf + 3wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AB0AAAC9AAAA/wABAP8MDiT/Ght1/yIfxf8nIuj/Jx/1/yYf8v8mHvD/JR3u/yUd7f8lHe3/JRzt/yUd + 7v8mHu//Jh7z/ycg9f8mIdz/HR6U/wsOK/8AAQD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADGAAAAIgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////ACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjSPAAI0bvACNJ8QAjSPAAI0jwACNH + 8AAjSPAAI0nwACNF7gAjSPAAI0rxACNJ8AAjR+8AI0fwACNI8AAjSPAAI0XuACNI7wAlOOkAJD3rACQ/ + 6wAjRO4AI0fvACNG7gAkRu4AI0nxACQ86gAkQe0AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvbSNG + 7/8jR+//I0jw/yNJ8f8jR+//I0fv/yNH7/8jR/D/I0fw/yNH8P8jRu//I0fv/yNG8P8jR/D/I0fv/yNG + 7/8jR/D/I0bv/yNH7/8jR/D/I0bv/yNH8P8jR/D/I0fw/yNH8P8jR/D/I0fw/yNH8P8jR+//I0bv/yNH + 7/8jR/D/I0fw/yNG7/8jRvD/I0fw/yNH7/8jR+//I0fw/yNH7/8jR/D/I0fw/yNH7/8jR/D/I0rx/yQ5 + 6f8mHd7/Jh/f/yUv5v8lNOj/JiHh/yYe3/8mH+D/Jh/g/yYd3/8mK+X/JTbp/yU16P8lNen/Jifj/yYd + 3/8mHuCnJR/fACUf3wAlH98AJR/fACUf3wAlH98AJR/fACUf3wAlH98AJR/fACUf3wAlH98AJR/fACUf + 3wAmH+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AB4AAADMAAAA/wgLFP8bHHv/JiPa/ycf9f8lHvD/JR3r/yUd6P8lHej/JR3n/yUd5/8lHej/JR3n/yUd + 6P8lHef/JR3n/yUd5/8lHuj/JR3s/yce9f8mIt7/Ghtz/wQHBP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + ANEAAAAiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wAmIcAAJiHAACYhwAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0jwACNG7wAjSfEAI0jwACNI + 8AAjR/AAI0jwACNJ8AAjRe4AI0jwACNK8QAjSfAAI0fvACNH8AAjSPAAI0jwACNF7gAjSO8AJTjpACQ9 + 6wAkP+sAI0TuACNH7wAjRu4AJEbuACNJ8QAkPOoAJEHtACRE7gAjRu8AI0fvACNH7wAjR+8AI0fvACNH + 7wcjRu/AI0jv/yRA7f8kOur/I0fv/yNI8P8jR/D/I0fw/yNH8P8jR+//I0fv/yNH7/8jR/D/I0fw/yNH + 8P8jR/D/JEbv/yNG7/8kRu//I0fv/yNH8P8jR/D/I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH + 8P8jR+//I0fv/yNH7/8jR+//I0bv/yNH7/8jR/D/I0fv/yNH8P8jR+//I0fv/yNH7/8jRvD/I0fv/yNI + 8P8lKeP/Jhvf/yYo4/8lN+r/JjLn/yYg4P8mHt//Jh/g/yYd4P8mIuD/JTPo/yU26P8lNun/Ji3m/yYe + 4P8mHuDzJh7gMSYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJh/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + ABwAAADMAAEA/xIVRP8lIsb/Jx/1/yUd7f8lHej/JR3o/yUd5/8lHej/JR3o/yUe5/8lHej/JR3n/yUe + 6P8lHef/JR3n/yUd5/8lHuj/JR3n/yUd6P8lHej/Jh7w/ycf+v8gIKr/CAkk/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAAuAAAAAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJiHAACYhwAAmIcAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNI8AAjRu8AI0nxACNI + 8AAjSPAAI0fwACNI8AAjSfAAI0XuACNI8AAjSvEAI0nwACNH7wAjR/AAI0jwACNI8AAjRe4AI0jvACU4 + 6QAkPesAJD/rACNE7gAjR+8AI0buACRG7gAjSfEAJDzqACRB7QAkRO4AI0bvACNJ8AAjR+8AI0fvACNH + 7wAjR+8AI0bvMiNK8fMlNOj/Jhvf/yUq4/8kQe3/I0nw/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fw/yNH + 7/8jR+//I0fw/yNH8P8jRu//I0bv/yNH7/8jR+//I0fw/yNH7/8jRu//I0fv/yNH8P8jRu//I0bw/yNH + 7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fw/yNH7/8jRu//I0fv/yNH8P8jRu//I0bw/yNJ + 8P8kPer/Jh3f/yYf4P8lM+j/JTfq/yYu5v8mH+D/Jh/g/yYe4P8mIOH/JjDn/yU16f8lNej/JTTo/yYi + 4f8mHuD/Jh7gkiYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmH+AAJh/gACYf4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + ABMAAADABAUA/xkbdf8nIun/Jh3x/yUd6P8lHef/JR3n/yUd6P8lHej/JR3n/yUe6P8lHef/JR3o/yUe + 6P8lHuj/JR7o/yUd6P8lHun/JR3s/yUc8P8mHvT/Jh/p/yQc3v8jHs3/Jia7/x4jbP8GCAD/AQIA/wAA + AP8AAAD/AAAA/wAAAPUAAAAlAAAAAAAAAAAjR/AAI0fwACNH8AAjR+8AI0fvACRH7wAkR+8AJEfvACRH + 7wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ACYhwAAmIcAAJiHAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjSPAAI0bvACNJ + 8QAjSPAAI0jwACNH8AAjSPAAI0nwACNF7gAjSPAAI0rxACNJ8AAjR+8AI0fwACNI8AAjSPAAI0XuACNI + 7wAlOOkAJD3rACQ/6wAjRO4AI0fvACNG7gAkRu4AI0nxACQ86gAkQe0AJETuACNG7wAjSfAAI0nxACNI + 8AAjR+8AI0fvACNG7wAjSfFhJTvq/yYe3/8mHN//JiHg/yU36f8jSPD/I0fv/yNH7/8jR/D/I0fw/yNG + 7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNG7/8jR+//I0fw/yNG8P8jR/D/I0fw/yNG7/8jRvD/I0bv/yNH + 8P8jR+//I0fv/yNH8P8jR/D/I0fv/yNG8P8jR+//I0fw/yNH8P8jRu//I0fv/yNH8P8jR/D/I0bv/yNI + 8P8jR+//JSfi/yYd3/8lLub/JTbp/yU26f8mKeP/Jh3g/yYd4P8mIeH/JjHn/yU26f8lNej/JTbp/yYr + 5f8mHt//Jh/g4yYf3xsmH98AJh/fACYf3wAmH98AJh/fACYf3wAmH98AJh/fACYf3wAmH98AJh/fACYe + 3wAmHt8AJh/gACYf4AAmH+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAYAAACpBAcJ/x4fkf8nH/b/JR3s/yUe5/8lHej/JR7n/yUd6P8lHuj/JR7o/yUd6P8lHuj/JR3n/yUd + 5/8lHun/JR3s/yUc8P8mHu7/JB7f/yQhyv8kJav/HRyS/yIpg/8hJXr/GBZ6/yAhgf8jJJD/ISKT/x4d + gf8WHnH/EiBO/woOGv8CBAm6AAAAACVL/gAjR+8AI0fwACNH8AAjR/AAI0fvACNH7wAkR+8AJEfvACRH + 7wAkR+8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wAmIcAAJiHAACYh + wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0jwACNG + 7wAjSfEAI0jwACNI8AAjR/AAI0jwACNJ8AAjRe4AI0jwBCNK8SEjSfBPI0fvYSNH8F4jSPBFI0jwKyNF + 7hEjSO8IJTjpACQ96wAkP+sAI0TuACNH7wAjRu4AJEbuACNJ8QAkPOoAJEHtACRE7gAjRu8AI0nwACNJ + 8QAjSPAAI0jxACNH7wAjRu8AI0nxACRA7GEmIOD1Jh7g/yYe4P8mHeD/JS7l/yNF7v8jSfH/I0fw/yNI + 8v8jR/H/I0fw/yNH7/8jRu//I0fv/yNH7/8jR+//I0fv/yNH8P8jRu//I0fv/yNH7/8jRu//I0bv/yNH + 8P8jR+//I0bv/yNH7/8jR/D/I0fw/yNH8P8jR/D/I0fw/yNH8P8jR/D/I0fw/yNH7/8jRvD/I0bv/yNG + 7/8jSvD/JDbo/yYd3/8lLeX/JTbp/yU16P8lNOj/JiLg/yYd4P8mHeD/Jibi/yY26f8lNen/JTbp/yYs + 5f8mHuD/Jh7g/yYf4GMmH98AJh/fACYf3wAmH98AJh/fACYf3wAmH98AJh/fACYf3wAmH98AJh/fACYf + 4AAmHt8AJh7fACYf4AAmH+AAJh/gACYf4AAmHt8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAB/BAcK/x8gm/8mH/f/JR3q/yUd6P8lHuj/JR3o/yUd6P8lHef/JR3o/yUe5/8lHej/JR3q/yUd + 7f8mHfH/JR3h/yQhzP8kJKv/IiOV/yIniv8fH5D/JSil/yMfvf8lH83/JyHb/ycf5/8mHun/Jh7p/yce + 6v8nHuz/Jybv/yZF8P8jReD/IEDN1yFC4GUlS/4SI0fvACNH8AAjR/AAI0fwACNH7wAjR+8AJEfvACRH + 7wAkR+8AJEfvAP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJiHAACYh + wAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNI + 8AAjRu8AI0nxACNI8AAjSPAAI0fwACNI8BAjSfBMI0XujyQ968slNOjzJS/l/yUr4/8kKuP/Iyjj/iMq + 4/YjLOXtJDLn1yU46bokPeucJD/rfSNE7mIjR+9FI0buJiRG7g4jSfEFJDzqACRB7QAkRO4AI0bvACNJ + 8AAjSfEAI0jwACNI8QAjQ+4AI0XuACNJ8QAkQOwAJh3fZSYe3/8mH+D/Jh7f/yYc3/8mJ+L/JD/t/yNI + 7f8iP9v/I0Xq/yRJ9P8jSfX/I0jx/yNH8P8jRvD/I0fw/yNH7/8jR/D/I0bv/yNH7/8jR+//I0fv/yNH + 7/8jR/D/I0bv/yNH8P8jR/D/I0fw/yNH8P8jR/D/I0fw/yNG7/8jR/D/I0fv/yNG8P8jR+//I0bv/yNG + 8P8jSfH/Iz/s/yYh4P8mLeX/JTbp/yU16f8lNun/Ji7m/yYe3/8mHuD/Jh3g/yYj4v8lNuj/JTTo/yYn + 4/8mHuD/Jh7g/yYe4LcmHuACJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJh7fACYe3wAmH+AAJh/gACYf4AAmH+AAJh7fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAABLBAYI+x8gnP8nIPb/JB3p/yUe6P8lHej/JR3n/yUd5/8lHef/JR3n/yUe6v8lHO7/JR3s/yQe + 2f8lJL7/IiGf/yMokv8jI5n/JSWz/yQeyv8mH9v/Jx/p/yYd5v8mHuT/Jh/j/yYf4v8mH+D/Jh7f/yUe + 3/8mH+D/Jh/g/yYc3/8lK+b/JEj0/yRJ9v8kSPP/I0fv2SNH72gjR/ACI0fwACNH8AAjR+8AI0fvACRH + 7wAkR+8AJEfvACRH7wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ACYh + wAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjSPAAI0bvACNJ8QAjSPADI0jwSCNH8J0kPuzjJTHm/yYl4f8mHuD/Ixfe/yAU3f8hGN3/JyDf/y0l + 4f8vJ+H/Lyfh/yUc3/8kGt//Jh/f/yYg4P8mIuH/JSjj/yUs5PYlMOfoJTbpyyQ86qwkQe2PJETuciNG + 71MjSfAzI0nxFiNI8AsjSPEBI0PuACNF7gAjSO8AI0rxACYd3wAmH+CDJh7g/yYf4P8mHt//Jh3g/yci + 5v8lNeb/GR6S/xYXgv8cKKn/IDrR/yNG7v8kSfb/I0n0/yNH8f8jRu//I0fv/yNH7/8jR+//I0fw/yNH + 7/8jR+//I0fv/yNH8P8jR/D/I0fw/yNH7/8jR/D/I0bv/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH + 7/8jSfD/I0Lt/yYm4/8lLuX/JTbp/yU16P8lNuj/JjTo/yYj4v8mHd//Jh/g/yYe4P8mH+D/JTDn/yYn + 4/8mHd//Jh7g/yYf4O4mHuApJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJh/gACYe3wAmHt8AJh/gACYf4AAmH+AAJh/gACYe3wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAbAwQA3R0dh/8oH/j/JR3o/yUe5/8kHef/JR7o/yUd6P8lHer/JRzu/yUe7P8kH9f/JCO3/yMm + nf8kJ5n/JCWq/yUgyf8mH9r/Jx/o/yYd5f8mHuL/Jh7h/yYf4P8mH+D/Jh7g/yYe4P8mHuD/Jh7g/yYf + 4P8mHt//Jh7f/yUf3/8mHuD/Jh7f/yQ96/8jSfD/I0bv/yNG7/8jR/D/I0fwuiNH8CkjR/AAI0fvACNH + 7wAkR+8AJEfvACRH7wAkR+8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wAmIcAAJiHAACYhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0jwACNG7wAjSfFQI0XvxiQ26f8lKOP/Jh7f/yYd3/8mHt//Jh/g/zc65P9UWun/bXjv/4GR + 8/+PnPb/laD4/5Sg9/9qdO7/Kifh/yMa3/8mHt//Jh7f/yYc3/8mHN//Jhzf/yYd3/8mH9//Jh/g/yUj + 4f8mKeP/JS7k+iUz5/AkOenXJD/suSND7pYjRe56I0jvWiNK8TgjSfEYJDbpCSYg4J0mHt/8Jxzi/yMb + zP8cFaH/JBzR/yQjz/8ZGo7/Ew9w/xUSef8YHY//Hi20/yI+2v8kSPL/JEn1/yNI8f8jR/D/I0fv/yNH + 7/8jR+//I0bv/yNH7/8jRu//I0bv/yNG7/8jR/D/I0fw/yNH7/8jR/D/I0fw/yNH8P8jR/D/I0fw/yNH + 8P8jSfH/I0Lt/yYk4f8mKOP/JTfp/yU16P8lNej/JTfp/yUo4/8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mHuBkJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJh/gACYf4AAmHt8AJh7fACYf4AAmH+AAJh/gACYf4AAmHt8AJh7fAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAohgbZf8oIPT/JRzp/yUe5/8lHej/JB7q/yYd8P8mHu//JB/Y/yQjtf8jJZ3/Iyab/yQl + sv8lIND/Jh/j/yYe6P8mH+P/Jh7h/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYc3/8lLuX/I0nw/yNH7/8jR/D/I0fw/yNH7/8jR+/iI0fwQiNH + 7wAjR+8AJEfvACRH7wAkR+8AJEfvAP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8AJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvASNI8EEjRu+yJDzq/yUm4v8mHN//Jh3f/yUe3/8mHt//Ixnf/zEz4v+Pn/b/orH6/3WA + 8P9YWuv/Y2/t/3aD8f+Ik/X/kaD2/3SE8P8wL+L/Ixvg/yYe4P8mHuD/Jh/g/yYf4P8mHuD/Jh/f/yYe + 4P8mHd//Jh3f/yYc3/8mHd//Jh7f/yYf4P8mIOD/JSbi/yUs5P8lMef7JDjp8iQ+69IjP+y4JDbp9CUy + 6v8hLcv/Fx+L/xcai/8cGpn/GBaE/xQQcv8UEHT/FA9y/xQPcf8VE3v/GiKa/x82yf8jRuz/JEr3/yNI + 8/8jR/H/I0fw/yNH8P8jR+//I0fw/yNG7/8jR+//I0fv/yNH7/8jR/D/I0fw/yNH7/8jR/D/I0fw/yNI + 8P8jSfD/JD3r/yYj4v8mG9//JS3k/yU26f8lNen/JTfp/yYs5f8mHt//Jh7g/yYf4P8mHuD/Jh7g/yYf + 4P8mHt//Jh/g/yYf4P8mH+CiJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7fACYe3wAmH+AAJh/gACYf4AAmH+AAJh7fACYe3wAAAAAAAAAAAAAA + AAAAAAAAAAAATg8TOv4nI+b/JR3t/yUe6v8mHu//Jh/0/yUg4P8gH63/HyGD/yImif8jI63/JSHS/yYf + 5f8mHeb/Jh7i/yYf4f8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/f/yYf4P8mH+D/Jh/g/yYf4P8mHeD/JiLh/yRD7v8jSO//I0fv/yNH7/8jR/D/I0fw/yNG + 7/MjR+9FI0fvACRH7wAkR+8AJEfvACRH7wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////ACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0jwACNI8AAjSPAAI0jwACNI + 8AAjSPAAI0jwACNI8E4kPev/JSXi/yYd3/8lHd//Jh7f/yYf4P8mH+D/Jh7f/yUd3/8mIeD/UVfp/1lk + 6/8mIeD/HBPe/yAa3/8kHd//KCHg/y8w4v86PeT/LCnh/yUd4P8mH+D/Jh7f/yYf3/8mH+D/Jh7g/yYf + 4P8mHt//JR7f/yYf4P8mH+D/Jh/g/yYf4P8mH9//JR7f/yYd4P8mHd//Jhzf/yYd3/8mHuD/JiDg/yUp + 4/8fJ+P/Hynn/yAx6P8gNeH/IDXR/x81yP8dMb7/Gymm/xgfkP8WGIP/FBJ3/xINbv8TDXD/GBqK/x4u + uP8iQOD/JEfw/yRK9v8kSfb/JEn2/yRJ9v8kSfX/I0n0/yNI8v8jR/D/I0fw/yNH8P8jR/H/I0jx/yNJ + 8v8kQ+7/JTHn/yYf4P8mHd//Jh3f/yYv5v8lN+n/JTbp/yUs5f8mHuD/Jh7f/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYe4P8mH+DPJh/gEyYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe3wAmHt8AJh/gACYf4AAmH+AAJh/gACYe3wAmHt8AAAAAAAAA + AAAAAAAAAAAADgYID9EkIsX/Jh35/yYe9P8mIOj/IR+6/xoccP8XG0r/HB50/yUhv/8mHuT/Jh7o/yYf + 4v8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHt//Jh/g/yYe4P8mHt//Jh/g/yYf + 4P8mHuD/Jh/f/yYe3/8mHt//Jh7g/yYf4P8mHt//Jh/g/yYd3/8kPOv/I0nx/yNG7/8jR+//I0bw/yNH + 8P8jRvD/I0fw6CNH7yokR+8AJEfvACRH7wAkR+8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wAmIcAAJiHAACYhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkQu4AJELuACRC + 7gAkQu4AJELuACRC7gAkQu4mJiDg2yYb3v8mH+D/Jh/g/yYf3/8mH9//Jh/g/yYf4P8mH+D/Jh7g/yAX + 3/8gGN//JR7g/yYf4P8mHt//JR3f/yQd3/8kG+D/Ixnf/yUd3/8mH+D/Jh/g/yYe3/8mHt//Jh/g/yYf + 4P8mHuD/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/x8V + 3v9ESeb/bnTu/1db6v9BReb/MS3k/ygo5f8gJOj/ISfq/yEt6v8hNej/Ijfd/yA0y/8dL7j/HCei/xcb + if8VE3n/FhV9/xkdkP8aJ6j/Hi+5/x8yv/8fMsD/HzLA/yA2yf8iQOD/JEjy/yRK9/8kSvT/I0bt/yNB + 5/8kM+T/JiLh/ycc4v8nHuX/Jx7j/yYg4v8mNOj/JTLn/yYn4/8mHuD/Jh7g/yYe4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+DpJh/gLyYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHt8AJh7fACYf4AAmH+AAJh/gACYf4AAmHt8AJh7fACYf + 4QAAAAAAAAAAAAECAXgdHZP/KCH9/yIfx/8bG4D/ExdH/xMXPP8bG3r/JCDG/ygf7P8mHub/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh7g/yYe4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mHN7/JTLn/yNJ8f8jR+//I0fv/yNG + 8P8jR+//I0fv/yNH7/8jR+/GJEfvCyRH7wAkR+8AJEfvAP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8AJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAkQu4AJELuACRC7gAkQu4AJELuACYc3yomH9+cJh/g8iYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYe + 4P8mH+D/Jh/g/yUf3/8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yQc + 3/84OuT/bXru/52q+f+aqPj/l6T4/46b9v99ivL/bHLu/1NZ6P8+QOT/MCvi/yUh4/8gHuf/ICbp/yEu + 6v8iNun/Ijja/x82yf8dLbD/GiOY/xcahv8WFHr/FA9x/xMOb/8UDnD/FRR8/xkim/8aJ6j/Giaj/xke + k/8YGIv/GROO/xsVlf8aFpn/HRmq/yMbx/8mIt//Jizq/yYg4/8mHd//Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYe3/8mHuD2Jh/gTCYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACNH + 7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7fACYe3wAmH+AAJh/gACYf4AAmH+AAJh7fACYe + 3wAmH+EAAAAAAAAAABsMDyXoICKV/xcaWv8VGkD/GRtr/yIfq/8nIeD/KB/u/yYe5v8mH+D/Jh7f/yYe + 4P8mHuD/Jh7g/yYf4P8mHuD/Jh/f/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mHt//Jh/g/yYf + 4P8mHuD/Jh7g/yYf4P8mH+D/Jh/f/yYe3/8mHuD/Jh/g/yYf4P8mH+D/Jhzf/yUr5P8jSPD/I0fv/yNH + 7/8jR+//I0fv/yNH8P8jR/D/I0fv/yRH8HojRu8AI0bvAP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////ACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACRC7gAkQu4AJELuACRC7gAmHN8AJh/fACYf4DEmH+CQJh/g6SYe4P8mH+D/Jh7g/yYe + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yUf3/8mH+D/Jh/g/yYf + 4P8mHuD/Jh7g/yYe4P8mH+D/Jh7f/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yUd + 4P8jHt//cIDw/56q+f+UoPf/lKD3/5Wh9/+Wovf/mqX4/5yo+f+Zp/j/mKT4/4yZ9f94hfH/aG/t/05V + 5/85OOP/LSni/yIg4/8fIej/ISvr/yE17P8iPOr/IjvY/x83yv8dLrL/GiSc/xcZh/8VEXX/Ewxs/xMM + bP8TDnD/ExBz/xQRdP8UEnX/ExJ0/xQSdP8UE3j/GRaM/yAZsv8lHtj/JyDm/ycf5P8mH+H/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/gaCYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAjRu8AI0nwACNI + 8AAjR+8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe3wAmHt8AJh/gACYf4AAmH+AAJh/gACYe + 3wAmHt8AJh/hACYg3gAICiGOFhlL/xsce/8iIKv/JiDe/ykh7/8mHur/Jh7h/yYe4P8mH+D/Jh/g/yYe + 3/8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7f/yYf4P8mHuD/Jh/g/yYc3/8lJ+L/I0bu/yNH + 8P8jR/D/I0fv/yNH7/8jR+//I0fw/yNH8P8jR/DrI0bvHSNG7wD///8A////AP///wz///8b////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wAmIcAAJiHAACYhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACRC7gAkQu4AJhzfACYf3wAmH+AAJh/gACYf4CQmHuB/Jh/f2iYe + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYe3/8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf + 4P8mH+D/Hxfe/2Rq7f+eq/n/lKD3/5Sg9/+UoPf/laD3/5Sg9/+UoPf/laD3/5Wh9/+Xo/j/m6b4/5yo + +P+ap/j/lqL3/4WV9P91f/D/XWPq/0RH5f80L+L/JyTh/x8f5P8hJ+v/ITHt/yM87/8jQOf/Ij3X/x84 + x/8dLK//GSGW/xcZh/8WE3r/FBBz/xQQdP8UEXX/FBJ3/xMRdf8TEXP/FxSE/x4arv8kHtL/Jh/j/ycg + 5v8mH+L/Jh/g/yYe4P8mH+D/Jh7geSYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACNJ8QAjSPAAI0bvACNJ + 8AAjSPAAI0fvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHt8AJh7fACYf4AAmH+AAJh/gACYf + 4AAmHt8OJh7fNCYf4XAmIN6uJiTP/Scg5v8oIOz/Jx7q/yYe4v8mH+D/Jh/g/yYf4P8lH9//Jh/f/yYf + 4P8mH9//Jh7g/yYe4P8mHuD/Jh7f/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf + 4P8mHt//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh7g/yYf3/8mHeD/JiTh/yNE + 7v8jSPD/I0fw/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fw/yNH74YUOu4A////AP///wD///9l////xf// + /wT///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8AJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJELuACYc3wAmH98AJh/gACYf4AAmH+AAJh7gACUe + 3xUmH+BpJh/gyCYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/yYe4P8mHt//Jh/f/yYf4P8mHt//Jh7f/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf + 4P8mHt//Jh/g/yAX3/9NTuj/k6L3/5uo+P+apvn/mKT4/5ei9/+VoPf/lKD3/5Sg9/+UoPf/lKD3/5Sg + 9/+VoPf/laD3/5ah9/+YpPj/m6f5/5uo+P+apvj/j531/36L8v9qce3/TlTn/zo44/8qJuD/IB/i/yAk + 6P8gLu3/Ijnw/yJA8P8jQ+b/IT7W/x80wf8cKan/GB6S/xcXgv8VEXj/ExB0/xQQcf8TEG//FRJ7/xoX + l/8hG7r/JR7Y/ycf5f8mH+H/Jh/gjiYe4AAmH+AAJh/gACYf4AAjSPAAI0fwACNJ8QAjSfEAI0jwACNG + 7wAjSfAAI0jwACNH7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7fACYe3wAmH+ANJh/gMCYf + 4GImH+CeJh7gziYf4PomH+D/Jh7h/yYe5v8mH+H/Jh7f/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf + 4P8mH9//Jh/f/yYf4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe + 3/8lHt//Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yUf3/8mHuD/Jh7g/yYf4P8mHuD/Jh7f/yYi + 4f8jQ+7/I0jw/yNH7/8jR/D/I0fw/yNG7/8jR+//I0fw/yNH7/8jR+/oFDruHf///wD///8G////w9LS + 08qpqasCqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrAKmp + qwCpqasAqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmHN8AJh/fACYf4AAmH+AAJh/gACYe + 4AAlHt8AJh/gACYe4AcmHuBUJh/gtiYf4PcmH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mHuD/Jh/g/yYe + 4P8mH+D/Jh7g/yYf4P8mHuD/Jh7f/yYe3/8mHuD/Jh/f/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh7f/yYe4P8mH+D/Ixzf/zU25P9UXur/c37w/4WT9P+Rn/b/m6j4/5qn+P+ap/j/mqb4/5ik + +P+Wovf/laD3/5Wg9/+UoPf/lKD3/5Sg9/+UoPf/laD3/5ei9/+apvj/m6j4/5uo+P+Uovf/gpD0/3F5 + 7/9YX+n/QUDk/zMu4v8nJuH/HyHk/x8q6v8gNe//ITzx/yJE8f8iQ+L/IDzS/x4yu/8aJaD/GBuN/xYV + ff8TD27/ExBu/xUSe/8eF6n7JhzeiiYf4AAmHuAAI0fvACNJ8AAjSPAAI0jwACNH8AAjSfEAI0nxACNI + 8AAjRu8AI0nwACNI8AAjR+8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AUJh/gNyYe32ImHt+fJh7gzCYf + 4PcmH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yUf + 4P8lH+D/Jh7f/yYe3/8mHuD/JR7g/yYf4P8mHuD/Jh/g/yYe4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/JR/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8lH9//JR7f/yUe4P8mH+D/Jh/g/yYe + 4P8lI+H/I0Tu/yNI8P8jR/D/I0fw/yNH7/8jR+//I0fv/yNH8P8jR/D/HkPv/zFS8U7///8A////lu3t + 7f8tLS6zqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrAKmp + qwCpqasAqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrAKmpqwAmIcAAJiHAACYhwAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAACQAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmH+AAJh/gACYf + 4AAmHuAAJR7fACYf4AAmHuAAJh7gACYf4AAmH+A8Jh/gnyYf4O0mH+D/Jh7g/yYe4P8mHuD/Jh/g/yYf + 4P8mHt//JR7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/f/yUf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh7f/yUe3/8hGd//Hhbe/yAZ3v8qKeH/Ojnk/0hL5/9YZOv/a3Xu/3yG + 8f+Ek/P/j532/5ml+P+bp/j/mqf4/5qn+P+Yo/j/laH3/5Wg9/+Un/f/lJ/3/5Sg9/+UoPf/laH3/5mk + +P+bp/j/mqj4/5ml+P+Nm/X/f4zy/3J37/9aYen/RUfm/zY04/8mKuL/HiXn/yEy7v8kQPP/JEf0/yRI + 7f8iQt3/HznL/xwrrf8XH5L/GiGcjiYc3gAjS/EAI0fvACNH7wAjSfAAI0jwACNI8AAjR/AAI0nxACNJ + 8QAjSPAAI0bvACNJ8AAjSPAAI0fvACYf4AAmH+AAJh/gACYf4AEmH+BxJh7g1SYe4PomHuD/Jh7g/yYe + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYe3/8mHt//Jh7g/yYe4P8mH+D/Jh7g/yYe4P8mH9//Jh/g/yYf + 4P8mHt//Jh/g/yYe4P8lHuD/Jh/g/yYe4P8mH+D/Jh7g/yYe4P8mH9//Jh/g/yYf4P8lHuD/Jh7g/yYf + 4P8mHuD/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf3/8mHt//Jh7g/yUf3/8lHt//Jh/g/yYf + 4P8mHd//JSXi/yNF7v8jSPD/I0fw/yNG8P8jR+//I0bw/yNH8P8jR+//I0fw/xM57/qOoPda////lf3+ + /v9hYWL/AAAAnwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYhwAAmIcAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAADgAAACFAAAAuAAAAMgAAADDAAAAogAAAFQAAAALAAAAAAAAAAAAAAAAAAAAAAAA + AAAmH+AAJh7gACUe3wAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8sJh7fiyYf4N8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yUe3/8mHuD/Jh/g/yYf3/8mHt//JR/g/yUe4P8lH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh7g/yYf4P8mH+D/JR/f/yUf4P8lHt//Jh7f/yYe4P8lHt//JBrg/yEY3/8fF97/IBje/yAY + 3/8iHd//Kivg/zc24/9DQ+b/UFno/2Vv7f95gvH/iJf0/5el9/+bp/n/m6f4/5ij+P+UoPf/lKD3/5Sg + 9/+Un/f/lJ/3/5Wg9/+VoPf/l6L4/5ql+P+cqPj/mqf4/5qm+P+Qnvb/f4rx/2Np6/86OeP/Hxbe/yMf + 4P8lK+X/JTbs/yU/8v8kRvT/JEr1/yNM9dkjTvO1I0vxgiNH70QjR+8TI0nwACNI8AAjSPAAI0fwACNJ + 8QAjSfEAI0jwACNG7wAjSfAAI0jwACNH7wAmH+AAJh/gACYf4AAmH+ACJh/gwyYe4P8mHuD/Jh7f/yYe + 4P8mH+D/Jh7f/yYf4P8mH+D/Jh7g/yYf4P8mHt//Jh/f/yYf4P8mHuD/Jh/g/yYe3/8mH+D/Jh/g/yUe + 3/8mH+D/Jh7g/yYf4P8lH+D/JR/g/yYe4P8lHt//Jh/g/yYf4P8mHuD/Jh/f/yYf3/8mH+D/JR7g/yUe + 3/8mH+D/Jh/g/yYe3/8mHuD/Jh/f/yYf4P8mHt//JR/g/yYf4P8mHt//JR/g/yUe4P8mH+D/JR/g/yYf + 4P8mH+D/Jhzf/yUp4/8jR/D/I0fv/yNH8P8jRvD/I0fw/yNH8P8jR/D/I0fw/x9C7/8gR/D2xtT90/// + //9+f4D/AAAA/wAAAJMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAJQAAAKMBAQD3BQcL/wECAf8AAAD/AAAA/wAAAP8AAAD/AAAAxwAAAE4AAAAAAAAAAAAA + AAAAAAAAAAAAACYe4AAlHt8AJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYe3wAmH+AcJh/gbiYf + 4MwmH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH9//Jh/f/yUf3/8mHt//Jh7g/yYe4P8mH+D/Jh7f/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yUf3/8mH+D/Jh/g/yYf3/8lH9//Jh/g/yYf4P8mH+D/Jh/g/yYe + 3/8mHuD/JR3g/yQb3/8hGN//IBff/yAX3v8fF97/IRvf/y4t4f9AQeX/WGLr/3eB8f+Mm/X/mqf4/5uo + +P+apfj/lqL4/5Sg9/+VoPf/laD3/5Sf9/+UoPf/laD3/5Wg9/+VoPf/l6L4/5qm+P+cqfj/lKL3/2Js + 7f8tKuD/Ihbe/ycd3/8mHd//JiLh/yUo5P8lMef/JDnp/yRB7P8jR+7/I0nw1SNJ8KcjSPBlI0jwNSNH + 8AojSfEAI0nxACNI8AAjRu8AI0nwACNI8AAjR+8AI0zxACYf4AAmH+AAJh/gACYf4DkmH+DgJR7f/yUf + 3/8mH+H/Jh/i/yYf4f8mH+D/Jh/f/yYf4P8mH9//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe + 4P8mH+D/Jh/f/yYf4P8mHuD/Jh/f/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/JR/g/yYe + 3/8lH9//Jh/g/yYf4P8mH+D/Jh/f/yYe4P8mHt//Jh7f/yUf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYc3v8lMuf/I0nx/yNH8P8jR+//I0fv/yNH8P8jRu//I0fv/yFE7/8YQO//mK/5//// + //+hoaHhAAAA/AAAAP8AAACHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYh + wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAATgAAAOIICh7/ERJX/xUWdP8JCS//AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD7AAAAhgAA + AAYAAAAAAAAAAAAAAAAAAAAAJR7fACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmHt8AJh/gACYf + 4AAmH98NJh7gWSYf4LkmH+D6Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe + 3/8lH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/JR/f/yYe4P8mH+D/Jh/f/yYe + 4P8mH+D/Jh/g/yYf4P8mHt//Jh7g/yYe3/8mHuD/JR7g/yUd4P8jGt//IBfe/x8W3v8hHN//MTDj/0dM + 5/9lbuz/fovy/5Cf9v+ap/j/m6j4/5yo+P+apvj/mqX4/5qm+P+apfj/maT4/5mk+P+Xovj/lqH4/5ij + +P+dqfj/i5j1/zk75P8iGt//Jh7g/yYd3/8mHN//Jh3e/yYc3/8mIeD/JSbi/yUv5f8lOen/JEDs/yNG + 7/UjSfHLI0nxnyNJ8WAjSPA0I0bvCCNJ8AAjSPAAI0fvACNM8QAlPOkAJh/gACYf4AAmH+AAJh/gJyUf + 37YmH+D/Jh/h/yYf2v8mH9//Jx/n/yYf4/8mHt//Jh7f/yYe3/8mHuD/Jh7g/yYf3/8mH+D/Jh/g/yYf + 4P8mH+D/Jh7f/yYe3/8lHt//JR7f/yYe4P8mHuD/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh7g/yUe + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf3/8lH9//Jh7f/yYe4P8mH+D/Jh/f/yYf4P8mHt//Jh7f/yYf + 4P8mH+D/Jh/g/yYe3/8mHt//JD7s/yNI8P8jR/D/I0bv/yNG7/8jR+//I0fw/yJG7/8WO+7/fpn3//// + ///c3NveFhYW4QAAAP8AAAD/AAAAcgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYh + wAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAWgMEBfQPEUD/FRZ6/xYThP8VFHz/Bgkd/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAACbAAAABwAAAAAAAAAAAAAAAAAAAAAAAAAAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh7fACYf + 4AAmH+AAJh/fACYe4AAmH+ACJh/gPiYe4KEmH+DuJh7g/yYe4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Ixnf/x4T + 3v8aEN3/GA7d/x8c3v8vLuL/QUfm/1dh6v9rce7/c3zw/3SD8P9zf/D/eYbx/4KQ8/+DkvP/ipn1/42b + 9f+Qnfb/nKf5/5Sj9/85PeT/Ihnf/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYc3/8mHN//Jhzf/yYg + 4P8lJeH/JS7l/yQ46f8kQO3/I0bv9CNI8MojSfCQI0jwSSNH7wUjTPEAJTzpACQ/7AAmH+AAJh/gACYf + 4AAmHuAFJyDjZh0ZpNoWFIH/GBWI/x8ar/8lHtX/JyDl/ycg5v8nH+X/Jh7i/yYe4f8mH+D/Jh/g/yYf + 3/8mHt//Jh/g/yYf4P8mHuD/Jh/g/yUe3/8mHt//Jh/f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHt//JR/f/yUf + 4P8mHuD/JR7f/yYf4P8mG9//JSzk/yNJ8P8jR/D/I0bv/yNH7/8jRu//I0bv/yNH7/8WPO7/W3z0//f7 + //////71UFBR1gAAAP0AAAD/AAAA/wAAAGYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYh + wAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAWQMGDfUSFFj/FhWC/xUTff8VE3n/FhR+/w8PU/8MECb/AgQA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAJAAAAAAAAAAAAAAAAAAAAAAAAAAACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYe + 3wAmH+AAJh/gACYf3wAmHuAAJh/gACYf4AAmHuAAJh7gKyYe4IYlHt/cJh7f/yUe3/8mH9//Jh/f/yYf + 4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh7g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yUf4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/y4u + 4v85OuT/QUDm/0hH5/9OVun/U13q/1pk6/9aY+v/WGPq/11r6/9XZ+r/SlXo/0BG5f8wNOL/JSPg/y0q + 4f80MuP/NjPj/z465f82M+P/JB3g/yYf4P8mHuD/Jh/g/yYf4P8lHt//Jh7g/yYf4P8mHuD/Jh7f/yYe + 3/8mHeD/Jhzf/yYc3v8mHN//JiDg/yYl4v8lLeX/JDjp/yRB7P8jR++4I0zxJiU86QAkP+wAI0nwACNH + 8AAmH+AAJh7gACcg4wAdFaEZHxmrgRoWk+IUEnL/FhOA/xsYnP8gG7r/Ix3O/yYf2P8mH+H/Jx/k/ycf + 5P8mH+P/Jx/k/ycf5f8nH+X/Jx/m/ycf5/8mH+f/Jx/l/ycf5f8nHuT/Jh/i/yYf4f8mH+D/Jh7g/yYf + 4P8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/f/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh7f/yYf3/8mHN//JiLh/yRD7f8jSPD/I0bv/yNG8P8jRu//I0fw/yRH7/8YPe//Qmby/+nv + /v/////+o6Ok5AAAAPUAAAD/AAAA/wAAAP8AAABfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAmIcAAJiHAACYhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAOQUIE/ASFGT/FhSC/xUTe/8UEnj/FBJ1/yAbx/8oIPb/JyLb/yAfpf8RFT7/AgMA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAAYQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYf4AAmH+AAJh/gACYe + 3wAmHt8AJh/gACYf4AAmH98AJh7gACYf4AAmH+AAJh7gACYe4AAmHuAAJR7fGCYf4GomH+DIJh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH9//Jh7f/yYf + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Ixvf/yws + 4v+BkvP/lqX3/5Wj9/+ap/j/m6f4/5qn+P+apvj/mqb4/5qm+P+Zpfj/maT4/5ql+P+SoPf/g47z/2Ns + 7f8zNOP/GxLe/yEY3/8gGd//Ihnf/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 3/8mH9//Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHd//Jhzf/yYc3/8mIOD/JiXi/yU16N8lPOlAJD/sACNJ + 8AAjR/AAI0fwACNH8AAjSfEAHRWhAB8ZqwAnH+McIBiwmRQSdv8UEnT/FBJ1/xUUff8XFIX/FxWM/xgW + j/8bF5j/Gxia/x0Zof8dGaT/Hhqs/x8asf8fGrf/IBu//yIdxP8jHcn/JB7P/yUf1v8mH+D/JyDk/ycg + 5v8mH+H/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh7f/yYe3/8mH+D/Jh/g/yYf + 3/8mH+D/JR/f/yYf3/8mHuD/Jh7f/yM86/8jSfD/I0fv/yNH7/8jR/D/I0fw/yRH8P8dQO//LFLx/8/c + /f//////5ubn/R0dHvcAAAD/AAAA/wAAAP8AAAD/AAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAHgcJF9sUFW3/FROB/xUTe/8UEnf/FBJ1/x0ZrP8lHuv/JR3q/yUd7f8mHvX/JyLq/x4f + i/8HCRH/AAAA/wAAAP8AAAD/AAAA/wAAAOYAAAAhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh/gACYf + 4AAmHt8AJh7fACYf4AAmH+AAJh/fACYe4AAmH+AAJh/gACYe4AAmHuAAJh7gACUe3wAmH+AAJh/gCSYe + 304mH+CwJh/g+yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yYe + 3/8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/JR/f/yUf3/8mHt//Jh/f/yYe3/8mHuD/Jh/g/yYe + 4P8iHd//TVPo/4+d9f+eq/n/l6P4/5Sg9/+VoPf/laD3/5Sg9/+UoPf/lKD3/5Wg9/+UoPf/lqH3/5ik + +P+cqfn/kJ72/1BY6f8eGN//JR3f/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/f/yYf3/8mH+D/Jh7f/yYd3/8mHd//JiPh6SQ/ + 7HEjSfBhI0fwTiNH8EojR/A4I0nxMSNK9CMjR+4VJD/vCSQn0wMXE399FBF0/BQQdf8UEHP/FA9w/xMP + b/8SD27/Eg5t/xMPbf8TEG7/ExBv/xIQcP8TEHH/ExFy/xQSc/8VE3f/FRN7/xYUgP8XFIT/FxWJ/xsY + mf8gG7v/Jh/h/yYf4/8mH+D/Jh7g/yYf4P8mH+D/Jh7f/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHt//Jh7g/yYf4P8lHt//Jh3f/yU46P8jSfD/I0fw/yNG7/8jR+//I0fw/yNH8P8gRO//G0Pv/7HF + +////////////25ub/8AAAD/AAAA/wAAAP8AAAD/AAAA/AAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAABAYIE7ITFGz/FRKA/xUTe/8UEnX/FhR+/yAavf8mHuz/JR7p/yUe6P8lHuj/JR7n/yUd + 7P8nH/b/IyK3/wsOIf8AAAD/AAAA/wAAAP8AAAD/AAAAhgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAmH+AAJh7fACYe3wAmH+AAJh/gACYf3wAmHuAAJh/gACYf4AAmHuAAJh7gACYe4AAlHt8AJh/gACYf + 4AAmHt8AJh/gACYe4DcmHuCXJh/g6iYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/JB3g/yUd + 4P8lHuD/JR7g/yYe4P8mHuD/Jh7g/yYe4P8mHuD/Jh7g/yYe3/8mH9//Jh/g/yUf3/8lHt//Jh/g/yYe + 3/8mHuD/JR3g/x4V3v8uLOP/Y23t/5Cd9f+bp/j/lqH3/5Sg9/+UoPf/laD3/5Wg9/+VoPf/lKD3/5Wg + 9/+UoPf/lJ/3/5ij+P+Zpvj/U1vp/x4X3v8lHt//Jh/g/yYf4P8mH9//Jh/g/yYe4P8mHuD/Jh7g/yYe + 4P8mHuD/Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYb + 3/8lLOX/I0nw/yNH8P8jR/D+I0bw9CNG7/AjR/DoI0jw4CNJ8dwjSPDTIT/ezR80w/gfMbz/HzG+/x4t + tf8cLLH/HCqt/xkmpP8aJaT/GiGZ/xofl/8ZG4//GBmK/xcWg/8WE37/FRF3/xURdv8UEHP/ExBy/xQS + dv8UE3X/FBN0/xsXmf8mH9v/Jx/l/yYf4f8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf + 4P8mH+D/Jh/g/yUe3/8mHuD/Jh3f/yQz5/8jSfH/I0fw/yNH7/8jRu//I0bv/yNH7/8jRu//GD3u/42m + +P///////////9TU1f8JCQn/AAAA/wAAAP8AAAD/AAAA/wAAAPAAAAAyAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAQEAAAMFCHcPEF3/GBiE/xgZf/8UEnP/FxWI/yIc0P8mHu//JR7p/yUd6P8lHef/JR7n/yUd + 5/8lHuf/JR3o/yYe9v8kIsf/DA8h/wAAAP8AAAD/AAAA/wAAAOAAAAAaAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAI0bvACNH7wAjRu8AI0fvACNG8AAmH98AJh7gACYf4AAmH+AAJh7gACYe4AAmHuAAJR7fACYf + 4AAmH+AAJh7fACYf4AAmHuAAJh7gACYe4CMmHuB7Jh/g1iYf4P8mH+D/Jh7g/yYf4P8mH+D/JyDg/ycf + 4P8lHuD/Ixzg/yAb4f8lH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf + 3/8mH+D/Ihvh/yIc4P8mHuD/Kize/yEf3v8zM+L/aHTt/5ak+P+Wovf/lKD3/5Sg9/+Un/f/lJ/3/5Wg + 9/+UoPf/lKD3/5Wg9/+UoPf/l6P4/5ak9/9ESuf/HhXe/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf + 3/8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mHt//Jh/g/yYf4P8mH+D/Jh/g/yUf + 3/8mHN//JSXh/yNH7/8jR/D/I0fv/yNH7/8jR/D/I0fw/yNH8P8jRu//I0fv/yRJ9P8kSvX/JEr2/yRJ + 9f8kSvb/JEn1/yRJ9v8kSvf/JEr2/yRH8P8kRu//I0Tq/yND6P8iQeT/Ij/g/yE62f8hNcz/Hy69/xoh + m/8VEnf/FBF2/xUTev8UEnT/GBaN/yQdzf8nIOX/Jx/m/ycg5f8nIOb/Jx/n/ycf5v8mH+T/Jh/h/yYe + 4P8mHuD/Jh/g/yYf4P8mHt//Jhve/yUw5v8jSfD/I0fv/yNH7/8jR+//I0fv/yNH7/8kR+//GD3v/1R2 + 9P/7/v////////////9jY2T/AAAA/wAAAP8AAAD/AAAA/wAAAP8aGhrngoOEI4KDhACCg4QAgoOEAIKD + hACCg4QAgoOEAIKDhACCg4QAgoOEAIKDhACCg4QAgoOEAIKDhACCg4QAgoOEAIKDhACCg4QAgoOEAIKD + hACCg4QAgoOEAIKDhACCg4QAJiHAACYhwAAmIcAAJiHAACYgwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAEBAEALDEP0ISGN/zg/nv8XF3f/GRWR/yQd3f8lHu7/JR3o/yUd5/8lHuj/JR7o/yUd + 5/8lHej/JR3n/yUd6P8lHef/Jx72/yMjtf8GCRD/AAAA/wAAAP8AAAD/AAAASwAAAAAAAAAAAAAAAAAA + AAAAAAAAI0bvACNG7wAjR+8AI0bvACNH7wAjRvAAI0fwACNH7wAjR+8AI0fwACNG7wAjR+8AI0fvACNH + 7wAjRu8AJh/gACYe3wAjR+8AJh7gACYe4AAmHuAAJh7gACYf4AsmG99XJhresCYc3+8mHd//Ihjh/z9E + 2/+tr8j/p6nK/5iezP+TnM7/TlrZ/yEZ4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7f/yYe + 3/8lHt//Ihvh/3F61P+co8v/rq3I/7i+xv9VXNj/GxHg/yAZ3/9RWun/l6T3/5ei9/+UoPf/lKD3/5Sg + 9/+VoPf/lKD3/5Sg9/+VoPf/lKD3/5Sg9/+ZpPj/jJr1/zg65P8eFd7/Jh7f/yYf3/8mHuD/Jh/g/yYf + 4P8mHt//Jh7g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh7f/yYf4P8mHuD/Jh/g/yYf + 4P8mHd//JiDg/yQ76v8jSPD/I0fv/yNH7/8jR+//I0bv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH + 8P8jR+//I0bv/yNH8P8jR+//I0fv/yNH7/8jR/H/I0fw/yNH8f8jSPL/I0jz/yRI8/8kSfP/I0n1/yRK + 9v8kSfP/HzjL/xgai/8VEHX/FBN5/xMSdP8WFHz/Gxic/x4Zq/8eGan/Hhms/x8Ztv8hG7//JB3N/yUf + 3f8nH+T/Jx/l/yYf4f8mHt//Jh3f/yUu5v8kR/D/JEfw/yNG7/8jR/D/I0bv/yNH8P8jR+//Ikbv/xtA + 7v+6yvv////////////d3d7/DAwM/wAAAP8AAAD/AAAA/wAAAP8AAAD/R0dJ3uHh4RPh4eEA4eHhAOHh + 4QDh4eEA4eHhAOHh4QDh4eEA4eHhAOHh4QDh4eEA4eHhAOHh4QDh4eEA4eHhAOHh4QDh4eEA4eHhAOHh + 4QDh4eEA4eHhAOHh4QDh4eEA4eHhACYhwAAmIcAAJiHAACYhwAAmIMAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAABcFBh7WHh6C/15lwP8iI4L/FxOY/yUe4/8lHuz/JR7n/yUe6P8lHef/JR3o/yUe + 6P8lHuj/JR3o/yUd5/8lHef/JR3n/yUd6P8nIPj/Ghx6/wAAAP8AAAD/AAAA/wAAAHAAAAAAAAAAAAAA + AAAAAAAAI0bvACNG7wAjRu8AI0fvACNG7wAjR+8AI0bwACNH8AAjR+8AI0fvACNH8AAjRu8AI0fvACNH + 7wAjR+8AI0bvACNH8AAjR/AAI0fvACNG7wAjR+8AI0jwACNI8AAkPOsKJTPnISUv5l0kNejCJS3l/yMc + 4P8xLdz/vb7F/8zMw//Pz8L/qrDJ/zIy3v8iGuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 3/8mH9//JR7g/x4X4f+Kk8//09LB/8nJw//S0sH/fH7R/xwW4v8kHOD/Hxnf/1BZ6f+XpPf/mKP3/5Sg + 9/+UoPf/lKD3/5Sg9/+UoPf/lJ/3/5Sg9/+UoPf/lJ/3/5ql+P+Jl/T/PD/k/x0W3v8gF97/Ixrf/yQc + 3/8lHeD/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mHt//Jh/f/yYf4P8lHt//Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mHN//JSPh/yM+7P8jSfD/I0bv/yNH8P8jR/D/I0bv/yNH7/8jR/D/I0bv/yNH7/8jR/D/I0fv/yNH + 7/8jR+//I0fw/yNG7/8jR/D/I0fv/yNH7/8jR/D/I0fw/yNH7/8jRu//I0fw/yNH7/8kRu//JEfv/yNH + 7/8jR+//I0fw/yRM+/8kRev/Gx+Y/xcUgf8ZFo3/FxWC/xQSdv8UEnX/FBJ1/xQSdf8UEnP/FRN3/xYU + fv8XFYj/HBie/yIcv/8mHuD/Jh7j/yUz6P8jSPD/I0jw/yNG8P8jRu//I0fw/yNH7/8jR/D/I0fv/xw/ + 7v9CZfL/9fn/////////////eXl6/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/2BgYdY4ODkJODg5ADg4 + OQA4ODkAODg5ADg4OQA4ODkAODg5ADg4OQA4ODkAODg5ADg4OQA4ODkAODg5ADg4OQA4ODkAODg5ADg4 + OQA4ODkAODg5ADg4OQA4ODkAODg5ADg4OQAmIcAAJiHAACYhwAAmIcAAJiDAACYgwAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAABAgSXEBJg/2xxz/88P5n/ExCQ/yUe5f8lHuz/JR3n/yUd5/8lHej/JR3o/yUd + 6P8lHej/JR7o/yUe6P8lHej/JR3o/yUd6P8lHej/JR3v/yYi2f8JCyD/AAAA/wAAAP8AAACMAAAAAAAA + AAAAAAAAAAAAACNG7wAjRu8AI0bvACNH7wAjRu8AI0fvACNG8AAjR/AAI0fvACNH7wAjR/AAI0bvACNH + 7wAjR+8AI0fvACNG7wAjR/AAI0fwACNH7wsjRu8xI0fvYyNI8IsjSPC5I0jw5CNK8fQjSfH/I0nw/yNI + 8P8iPu3/JTPm/56lzP/KycP/zMvD/4GJ0P8aFeL/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8bFOL/X2PW/83Mwv/ExMT/zcvC/5Wezf8jIeH/JR3g/yUd4P8gGd7/UFnp/5Si + 9/+cp/j/mKP4/5ik+P+YpPj/maX4/5ml+P+Xo/j/lqH3/5Wg9/+UoPf/maT4/5Cd9v9kbO3/Q0bm/zQ3 + 4/8tK+H/JyHg/yEc3/8hGN//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYe4P8mH+D/Jh/g/yYe + 4P8mG9//JSrj/yND7f8jSfD/I0fv/yNH7/8jR/D/I0fw/yNG7/8jR/D/I0fw/yNH7/8jR/D/I0bw/yNH + 7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNH8P8jRvD/JEbw/yNH + 8P8jR/D/I0nz/yRK9f8iQN//HjC7/xoflf8WFID/FhR//xURev8UEHT/FA9y/xQPcv8UEHP/Ew90/xQQ + df8UEnb/FBJ3/xQSdf8UEnT/Hhqt/yU04f8jR+7/JEn0/yRK9v8jSPP/I0fw/yNH7/8jRu//I0fv/yNH + 7/8VOu7/epT2////////////7O3t/xwcHP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP+oqKnS7+/vB+/v + 7wDv7+8A7+/vAO/v7wDv7+8A7+/vAO/v7wDv7+8A7+/vAO/v7wDv7+8A7+/vAO/v7wDv7+8A7+/vAO/v + 7wDv7+8A7+/vAO/v7wDv7+8A7+/vAO/v7wDv7+8AJiHAACYhwAAmIcAAJiHAACYgwAAmIMAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAABLAwUv/F1hwv9tcsX/EhCH/yMc3v8lHuv/JR3n/yUd5/8lHen/JR3o/yUe + 6P8lHej/JR3n/yUd5/8lHej/JR3n/yUd6P8lHej/JR3o/yUd6P8nH/b/GBpt/wAAAP8AAAD/AAAAmAAA + AAAAAAAAAAAAACNH8AAjRu8AI0bvACNG7wAjR+8AI0bvACNH7wAjRvAAI0fwACNH7wAjR+8AI0fwACNG + 7wAjR+8AI0fvCCNH7yMjRu9YI0fwhCNH8LQjR+/iI0fv9yNG7/8jR+//I0fv/yNH8P8jR/D/I0bw/yNG + 7/8jR/D/Ikfw/xxE8v+EltX/zsvB/8fHxP+4vMb/ODrc/yEX4f8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yUe + 3/8mHuD/Jh/g/yUf3/8mH+D/Hxbg/0FD3P+9w8X/x8bD/8fHw/+1ucb/MS3e/yIb4f8mH+D/Jh3f/yEZ + 3/9AQ+X/cX3w/4GP8/+BjvL/gY7y/3aE8f92hfD/g5Dz/5Gd9v+Ypfj/mqX4/5Wg9/+Woff/mqb4/5Wj + 9/+OnPb/jJj1/4OP8/9seO//QUXm/yId3/8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe + 4P8mHd//JTDm/yNG7/8jSPD/I0fw/yNH7/8jRu//I0fv/yNH8P8jRu//I0bv/yNH8P8jR/D/I0fw/yNG + 7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNG7/8jR+//I0fw/yNH8P8jR/D/I0fv/yNH8P8jR/D/I0fw/yNG + 8P8jR/H/JEr2/yI+3P8bJ6X/FhV//xMOcf8TD3H/FBF2/xcXgf8aIJb/HCem/xwssf8bLLP/Gymq/xwk + n/8ZH5b/FxmH/xUTev8UEHT/FBF2/xUTev8WGIL/GR2P/xsnpf8eMb//Ij/b/yNH8P8kSvb/I0jz/yNH + 8P8iRu//GT/v/6zA+////////////52dnf8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8ODg7/0dHS0v// + /wf///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////ACYhwAAmIcAAJiHAACYhwAAmIMAAJiDAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAALAAAHyTA0if+WnPD/KyyR/x0Wz/8mHu7/JR7o/yUe6/8lHuv/JR3m/yYd + 6v8lHer/JR3o/yUd5/8lHef/JR3o/yUd6P8lHuj/JR7n/yUd5/8lHef/Jh70/yAesv8DBAL/AAAA/wAA + AKgAAAAAAAAAACNG7wAjR/AAI0bvACNG7wAjRu8AI0fvACNG7wAjR+8AI0bwACNH8AAjR+8AI0fvECNH + 8D8jRu9xI0fvoCNH79UjR+/1I0fv/yNH7/8jRu//I0fw/yNH8P8jR+//I0fw/yNG7/8jR+//I0fv/yNG + 7/8jR/D/I0bv/yNG7/8ZP/L/a4Dc/8zLwv/ExMT/zczD/32E0v8bF+L/Jhzf/yYe4P8mH+D/Jh/g/yYf + 4P8lHt//Jh7g/yYe4P8lH9//Jh/g/yQb3/8nJuD/rK/I/8nJw//FxcT/xsfE/0dN2/8gGOH/Jh7f/yYf + 4P8mHuD/Ihnf/yMd3/8pJOH/KSTg/ykk3/8mIOD/JiHg/yoj4f80NOP/TFLo/3R/8f+Wo/f/mKT4/5Sg + 9/+VoPf/lqL3/5ei9/+Yo/j/nKf4/5Sk9/9FSeb/Ihnf/yYe4P8mH+D/Jh7f/yYf4P8mHuD/Jh/g/yYd + 4P8mIOD/JDjo/yNJ8P8jR/D/I0bw/yNG8P8jRu//I0bv/yNH7/8jRu//I0fv/yNH7/8jR/D/I0fw/yNG + 7/8jRu//I0fw/yNG7/8jR/D/I0fw/yNH8P8jRu//I0bv/yNH8P8jR/D/I0fw/yNH7/8jRvD/I0fv/yNH + 7/8kSfX/I0bs/x0ss/8WFX7/Ew5x/xQPcf8WFoH/GyWj/x82x/8iQuT/I0bv/yRI8/8kSvf/JEr4/yRJ + 9f8kSPH/I0bv/yJE6f8hO9P/HCuv/xgcjf8VEnn/FA9y/xMPc/8TD3P/FBB1/xUUff8ZHpP/HS+5/yJB + 4P8kSfT/IUb0/yhN8f/e5v3///////7///9BQUL/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/LCws//n5 + +dP///8H////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wAmIcAAJiHAACYhwAAmIcAAJiDAACYg + wAAmIMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAagkMQP+AhOH/Zmu9/xYRq/8mHe7/JR3n/yYd7P8jHdr/HBmm/xoW + kv8cGKH/Ix3Z/yYe6/8kHef/JB3n/yUe6P8lHef/JR7o/yUe6P8lHef/JR3n/yUd7/8mI9H/Cg8Z/wAA + AP8AAACsAAAAACZM/QAjRu8AI0fwACNG7wAjRu8AI0bvACNH7wAjRu8EI0fvGyNG8E4jR/CDI0fvtiNG + 7+cjR/D+I0fv/yNG7/8jRu//I0fw/yNG7/8jR+//I0bv/yNH8P8jR/D/I0fw/yNG7/8jRu//I0fv/yNH + 8P8jRu//I0fv/yNH8P8jR+//HEHx/09v5P/Fx8T/xcTD/8jGw/+7wMf/OFLn/x8p5/8mIeD/Jh3f/yYe + 4P8mHt//Jh/g/yYf4P8lHt//Jh7g/yYf4P8mHuD/Hhfh/4ePz//My8L/xMTE/8vNw/9pbNT/HRTh/yYe + 4P8mH+D/Jh/g/yYe4P8lHd//JB3f/yUd4P8lHeD/JR3g/yUd4P8kHeD/Ixrf/x8X3v8hHN//Qkfm/4iV + 9P+ZpPj/lKD3/5Sg9/+UoPf/lJ/3/5Sg9/+erPn/X2Xs/x8W3/8mHuD/Jh/g/yYe3/8lHt//Jh7g/yYc + 3/8mJuL/JD/s/yNJ8P8jR/D/I0fv/yNH8P8jRu//I0fv/yNH7/8jR/D/I0bv/yNH7/8jR+//I0fw/yNG + 7/8jRu//I0fv/yNH8P8jRu//I0fv/yNH8P8jR+//I0bw/yNH7/8jRu//I0fv/yNH7/8jRu//I0bv/yNH + 8P8kSvb/IT3Y/xkekf8TD3D/FBFz/xcZif8eLrf/IkHg/yRJ9P8kSvb/I0jy/yNH8P8jR/D/I0fw/yNH + 7/8jR/D/I0fw/yRH8P8kSPH/I0n0/yRJ9f8jRez/ITvV/x0ttf8YHpH/FhR8/xQPc/8UEHT/FBB1/xQQ + dP8WFH//GyWj/xg01f9JbfT/9fn+///////T09T/CQkK/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/01O + T//////B////A////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJiHAACYhwAAmIcAAJiHAACYg + wAAmIMAAJiDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAEQAACdU4PJP/lZrt/yQkj/8fGNn/Jh7r/yUd6/8kHd7/GBaL/xMS + cv8UEnX/ExJx/xkWlP8kHeb/JR7o/yUd5/8lHuj/JR7o/yUd5/8lHef/JR3o/yUd6P8lHer/JyHq/wsL + Of8AAAD/AAAAnwAAAAAmTP0AI0bvACNH8AAjRu8FI0bvIyNG71UjR++OI0bvxCNH7/IjRvD/I0fv/yNH + 7/8jRu//I0bv/yNH8P8jRvD/I0fv/yNH8P8jRvD/I0fv/yRH8P8jRu//I0fv/yNH8P8jR+//I0fv/yNH + 7/8jR+//I0fw/yNH8P8jR/D/I0bv/x9E8P81V+r/vr/F/8bGw//ExMT/zcvB/36T2P8YQfL/I0Lt/yUz + 6P8mJOL/JRzf/yYd3v8mHt//Jh/g/yYf4P8mHuD/Jh/g/xoU4f9jZdX/zc3C/8TExP/OzcL/iI/P/x4b + 4f8lHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8lH9//JR3f/xwT + 3f89QeT/k6D3/5ei+P+Un/f/lKD3/5Wg9/+apvj/jZv2/zc45P8jG9//Jh7f/yYf4P8mH+D/Jh3f/yYd + 3/8lLeX/I0Xu/yNJ8P8jR/D/I0bw/yNH7/8jRu//JEbw/yNH8P8jR+//I0fw/yNH7/8jRu//I0bv/yNH + 7/8jRu//I0fw/yNH8P8jR+//I0fw/yNH7/8jR/D/I0fw/yNG7/8jR+//I0bv/yNH8P8jR/D/I0bv/yNH + 8P8kSvb/IDXI/xYVf/8TD2//FxOB/x8htf8jROj/JEr3/yNI8/8jR/D/I0fv/yNH8P8jRu//I0bv/yNH + 7/8jR+//I0fw/yNH7/8jR+//I0fv/yNG8P8jR/D/I0jx/yNJ9P8kSfb/JEfv/yE92P8dLbX/GR6T/xYU + ff8UEHX/FBF2/xQQdP8GBXX/bHO/////////////oqKi/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP+Ghob/////t////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ACYhwAAmIcAAJiHAACYh + wAAmIMAAJiDAACYgwAAlIMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF8FBzf/cHXU/1tftP8QDZT/Jh7s/yUd6P8mHu3/Gxik/xQT + c/8VE3v/FBN6/xQSef8UEnf/IRvH/yYe7P8lHef/JR3n/yUd5/8lHef/JR7o/yUe5/8lHej/JR3o/ycf + 9f8RD07/AAAA/wEDCYInTf8AJkz9HiNG71MjR/CNI0fvxCNH7/UjRu//I0fv/yNH8P8jR+//I0fw/yNH + 8P8jR/D/I0fv/yNG7/8jR/D/I0fw/yNH8P8jR+//I0fw/yNH7/8kRvD/I0bv/yNH7/8jR+//I0fv/yNH + 7/8jR/D/I0fv/yNH7/8jRvD/I0fw/yNG7/8hRO//KEvu/6myy//Jx8L/xMTE/8nIw/+5vcf/Mlbr/x5E + 8f8jSfD/JEXu/yQ36f8lJ+P/Jh7f/yYc3/8mHt//Jh/g/yYf4P8eFeH/RUfb/8DFxf/GxcT/ysnD/6Wt + yv8qJd//JBzg/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH9//Jh/g/yYe4P8mH9//JR7f/yYe + 4P8mHuD/HhXe/1Ze6v+bqPj/mKT4/5mk+P+cqPj/hZPz/z9D5f8hGd//Jh/g/yYf4P8mHt//JR3e/yYf + 4P8lNuj/I0jw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fw/yNH8P8jR/D/I0fv/yNH7/8jRu//I0bv/yNH + 7/8jR+//I0fv/yNH8P8jR/D/I0fw/yNH8P8jR+//I0bv/yNH8P8jR+//I0fw/yNH8P8jR+//I0fv/yNH + 8P8jSff/HjPB/xURd/8TEHD/GheT/yUd0v8nH+f/JTPp/yNI8P8jR/D/I0bv/yNH7/8jR/D/I0bv/yNH + 8P8jR/D/I0bv/yNH8P8jR+//I0fw/yNH7/8jRu//I0fv/yNH8P8jR/D/I0fw/yNH8f8jSPT/JEr2/yNH + 7/8iPtv/Hi+4/xcZhv8UEHT/BQNv/36Etv///////////3x9fv8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8BAQH/srKy/////7H///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wAmIcAAJiHAACYh + wAAmIcAAJiDAACYgwAAmIMAAJSDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIBAga5Ghxv/3+E3/8nKIX/GRSs/yYe7/8mHuz/IhzU/xUT + fP8VE3n/EhB5/xQSev8VE3r/FBJ2/yAavv8mHu7/JR3n/yUd5/8lHej/JR7n/yUe6P8lHuf/JR3o/yUd + 6P8nH/X/FhVQ/wIDAP0YK3+mJ03/pyNH7+8jR+//I0fw/yNH7/8jR/D/I0bv/yNH7/8jR/D/I0fv/yNH + 7/8jR+//I0fw/yNH8P8jR/D/I0bv/yNH7/8jR/D/I0fv/yNH7/8jR+//I0bw/yNH7/8jR+//I0bv/yNH + 7/8jRu//I0bv/yNH7/8jR+//I0fv/yNG7/8jR+//IkXw/x9G8f+MndP/z8vB/6u1y/+8v8f/zszB/3aL + 2f8YPvL/I0fv/yNH8P8jSfD/I0fv/yQ96/8lLOX/Jh/g/yYc3/8mHt//JBrg/ykp4P+ussj/ycjD/8XF + xP/BwsT/Ojvc/yIZ4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/f/yYf4P8mHuD/Jh/g/yYf + 3/8mH9//Jh/g/yQc3/8hHN//WGHq/4iS9P9+ivL/WmPr/ywr4f8gGN//Jh7g/yYe4P8mHuD/Jhzf/yUm + 4v8kP+z/I0nw/yNH7/8jRvD/I0fw/yNG7/8jR+//I0fw/yNH8P8jRu//I0bv/yNH7/8jR+//I0bv/yNH + 7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH8P8jR+//I0fw/yNG7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH + 8P8kSvb/HjPB/xURdv8UEXL/Gxif/yYf3/8nH+X/Jh7f/yYc3/8lNef/I0jw/yNH8P8jR+//I0fv/yNH + 7/8jR/D/I0fw/yNH8P8jR+//I0bv/yNH8P8jR+//I0bv/yNG7/8jR/D/I0fv/yNH8P8jR+//I0fw/yNH + 7/8kR/H/I0j0/yRL+P8jQ+b/HCqt/wcGc/98gbP//////+Dl9v8mKln/AAAC/wAAAP8AAAD/AAAA/wAA + AP8AAAD/FRUW/+Dg4f////+U////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJiHAACYh + wAAmIcAAJiHAACYgwAAmIMAAJiDAACUgwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1AwUh9jQ1mv9lasX/Dw1x/yAavv8mHu7/Jh7t/xwY + ov8TEnP/FhR7/y41jf8aG3//FBJ5/xUTfP8iHNH/Jh7s/yUe6P8lHuf/JR3n/yUd5/8lHef/JR7o/yUd + 6P8lHOj/Jhzt/yErmf8fO6n+Jkr2/yNH8f8jR+//I0fv/yNG7/8jR+//I0fw/yNH8P8jRu//I0fv/yNH + 7/8jR/D/I0fv/yNH7/8jRvD/I0bv/yNH7/8jRu//I0fv/yNH8P8jR/D/I0fv/yNH8P8jR+//I0fv/yNH + 7/8jR+//I0fv/yNG7/8jR/D/I0fv/yNH7/8jRvD/I0fw/yNH8P8ZQPL/dYnZ/9LOwP+lsM3/kaLS/9HN + wf+3vMf/MVXq/x5C8f8jR/D/I0fw/yNH7/8jSPH/I0jw/yRA7P8lMOX/JiLh/yUc3/8fF+H/jJPP/8vL + wv/FxcT/x8rE/1Vc2P8fFuH/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh7f/yYf3/8mH+D/JR3f/yEa3v8mH+D/JB7f/yAY3/8jGt//Jh7g/yYd3/8mHuD/JiTh/yU1 + 6P8jRe//I0nw/yNH8P8jR+//I0bv/yNH8P8jR/D/I0bv/yNG7/8jRu//I0bv/yNH8P8jR+//I0bv/yNG + 7/8jR+//I0fv/yNH7/8jRu//I0fw/yNH8P8jR/D/I0fw/yNH8P8jR/D/I0fw/yNH7/8jR/D/I0fv/yNH + 7/8kSfb/IDfL/xUSd/8TEHD/HBmh/ycg5P8mH+P/Jh7g/yYf4P8mHuD/Jh3f/yQ46f8jSPD/I0fw/yNH + 7/8jR/D/I0fv/yNH8P8jR+//I0fv/yNH7/8jR+//I0bv/yNH7/8jR+//I0fw/yNH8P8jRu//I0fv/yNH + 7/8jR+//I0fw/yNG7/8jR+//I0j0/yRK+P8QKsr/e4DJ/9vc2/82O1H/AAAK/wAAAf8AAAD/AAAA/wAA + AP8AAAD/AAAA/z09Pv/+/v7/////dv///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ACYh + wAAmIcAAJiHAACYhwAAmIMAAJiDAACYgwAAlIMAAJiHAADQzxwAfF7wAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQcJRP9GR63/Sk+r/w0Kb/8hG8T/Jh/x/yMd + 2P8WFH//FBJ4/xQRef8jJYT/HB+A/xMRdf8ZFZH/JR7o/yUe6f8lHej/JR7o/yQe6P8kHef/JR7o/yUd + 5/8lGuf/JSLo/yQ47P8lSfb/JEr7/yNH8f8jR+//I0fw/yNH8P8jR/D/I0fv/yNH8P8jR/D/I0bv/yNG + 7/8jR+//I0bw/yNH7/8jRu//I0fv/yNH7/8jRu//I0bv/yNH8P8jR+//I0fw/yNH7/8jR+//I0fv/yNG + 7/8jR+//I0fv/yNH7/8jR+//I0fw/yNG7/8jRu//I0fw/yNH8P8jR+//G0Dx/1p14f/LysL/trvI/2GA + 3//AwsX/z8zB/3eM2f8YP/L/I0bv/yNG7/8jR/D/I0bw/yNH7/8jSfD/I0nw/yRE7f8lNOj/Gxrj/2pw + 0//NzcL/xMTE/87Owv93etL/HBXh/yYe4P8mH+D/Jh7f/yYe4P8lH+D/JR7f/yYf4P8mHuD/Jh/g/yYf + 4P8mHt//Jh/g/yYf4P8mHt//Jh7g/yYd4P8mHd//JRzf/yUb3/8mHN//JiDg/yYk4v8lL+b/JDzr/yRF + 7v8jSfD/I0fw/yNH8P8jR+//I0bv/yNH7/8jR/D/I0fw/yNH8P8jRu//I0bw/yNH7/8jR+//I0bw/yNG + 7/8jR+//I0fv/yNH8P8jRu//I0bv/yNG7/8jR/D/I0fv/yNH8P8jR+//I0fw/yNH8P8jR+//I0fw/yNG + 7/8kSfX/Ij/c/xYVgP8TEHD/GheX/yYf4P8mH+P/Jh/g/yYf4P8mHuD/Jh/g/yYd3/8lJOL/I0Tv/yNH + 8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH + 7/8jRvD/I0fw/yNH8P8jSPD/I0jw/yNI8P8jSfD/G0T7/1l05v8xMi3/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP98fH3//////////1v///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wAmIcAAJiHAACYhwAAmIcAAJiDAACYgwAAmIMAAJSDAACYhwAA0M8cAHxe8AAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQMFB8AMDGH/TlK0/zs7nf8NC2//IRvD/yYf + 9f8fGsH/FBN0/xQTev8VE3r/EhB5/xMRev8UE3X/IBu//yUe7v8lHuj/JR3n/yUd6P8lHuj/JR3o/yUc + 5/8lHOf/JS3q/yRE7/8jSvH/I0fw/yNG7/8jR+//I0fv/yNG7/8jR/D/I0fw/yNH7/8jR+//I0fv/yNG + 7/8jRu//I0fw/yNG7/8jR/D/I0fw/yNH8P8jR/D/I0bw/yNH7/8jR/D/I0fw/yNH8P8jRu//I0fv/yNH + 8P8jR+//I0fv/yNH8P8jRu//I0bv/yNH7/8jR/D/I0fv/yNG7/8jR/D/I0fv/x9D8P8+YOj/w8TE/8rI + w/9TcOP/iprU/9PPwP+2vMj/LlPs/x5C8f8jRu//I0fw/yNG8P8jR/D/I0fv/yFF8P8iSPH/I0rx/xs9 + 7/9JX+P/w8XF/8bFxP/MysL/lJ3N/yMf4P8lGt//Jh3f/yYd3/8mHN7/Jhzf/yYc3/8mHd//Jh3f/yYd + 3/8mHd//Jhzf/yUc3/8lHN7/Jh3e/yYg4P8mIuH/JSTi/yUq4/8lMeb/JTjq/yRA7P8jRu//I0nx/yNJ + 8f8jSPD/I0bv/yNH8P8jR/D/I0fw/yNH7/8jR+//I0fw/yNH7/8jR+//I0bv/yNG8P8jR/D/I0bv/yNG + 7/8jR/D/I0bv/yNG7/8jR/D/I0bv/yNH7/8jR/D/I0bw/yNH7/8jR+//I0bv/yNH7/8jR+//I0fv/yNG + 7/8jSPL/JEfu/xkelP8SD27/GBaJ/yYf2f8mH+T/Jh/f/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh3f/yQ9 + 6/8jSPD/I0fw/yNH8P8jR/D/I0bv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNG8P8jRu//I0fv/yNH + 7/8jR+//I0nw/yNJ8f8jR/D/I0Tt/yRB7f8kQO3/JD3r/yY99/8bJ3z/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/qamq//////T///8z////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8AJiHAACYhwAAmIcAAJiHAACYgwAAmIMAAJiDAACUgwAAmIcAANDPHAB8XvAAeF7wAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC4FBxj0EBBy/0JHqP8zM5X/Dw1x/yEc + x/8nHvX/HxnA/xQSdP8VE3r/FRN6/xUTe/8UEnb/GBWO/yUe5f8lHun/JR3n/yUd5/8lHuj/JRzn/yUb + 5/8lJOn/JDzt/yNK8P8jSPD/I0bv/yNH7/8jR/D/I0fv/yNH7/8jRu//I0fv/yNH8P8jR/D/I0fw/yNH + 8P8jR+//I0fv/yNH7/8jR+//I0bv/yNH7/8jR/D/I0fv/yNG7/8jRu//I0fv/yNH8P8jR/D/I0bv/yNG + 7/8jR/D/I0fw/yNH8P8jR/D/I0bv/yNG7/8jR/D/I0fw/yNG7/8jRu//I0fv/yNG7/8gRPD/LE7t/7K4 + yf/SzcD/boXc/0do5f/JycP/z8zB/22F3P8XPvP/I0fw/yNH8P8jRu//I0fv/yNH7/8hSO//HkPv/yNH + 8P8gRfD/LFPt/7S5yP/Ix8P/x8bD/7S5yP8uR+n/Ijfr/yQ26P8lM+f/JTHm/yUu5f8lK+T/JSzk/yUs + 5P8lLOP/JSzk/yUv5f8lMeb/JDbo/yQ76v8kP+v/JELt/yNG7/8jSPD/I0nw/yNJ8P8jSfH/I0fv/yNH + 8P8jR/D/I0bv/yNH7/8jR+//I0fv/yNG7/8jR/D/I0fv/yNH7/8jR+//I0fv/yNG7/8jRu//I0fw/yNH + 7/8jR/D/I0fv/yNG7/8jR+//I0fw/yNH7/8jR+//I0bv/yNH8P8jR+//I0fv/yNH8P8jR/D/I0bw/yNG + 7/8jR/D/JEr2/x4vuP8TD3D/FRR7/yMdx/8nIOf/Jh7f/yYf4P8mH9//Jh7g/yYf4P8mH+D/Jh/g/yYc + 3/8kPOr/I0nw/yNH7/8jRvD/I0bv/yNH8P8jR/D/I0fv/yNG7/8jR+//I0fv/yNH8P8jR/D/I0bv/yNH + 8P8jSfD/I0fv/yQ/7P8lMuf/JSnj/yYl4f8mH+D/Jh7g/yYd3/8pIOv/Fhhn/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/CwsM/8zMzv/////T////Cf///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////ACYhwAAmIcAAJiHAACYhwAAmIMAAJiDAACYgwAAlIMAAJiHAADQzxwAfF7wAHhe8AB0W + uwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbCQoo/xEPff88Q6H/LS6R/xAO + cv8gG8L/Jh7w/yQd2P8WFH//FBJ1/xUTe/8UEnb/FRN3/yIcx/8mHu3/JR3n/yUd5/8lHej/JRvn/yUf + 6P8kM+v/I0Xv/yNJ8P8jR/D/JEfw/yRG8P8jR+//I0bw/yNH8P8jR+//I0fv/yNH8P8jRu//I0fv/yNH + 7/8jR+//I0fv/yNH7/8jRvD/I0fv/yNH8P8jR+//I0fv/yNH7/8jR+//I0bv/yNH8P8jR+//I0fw/yNG + 8P8jR+//I0fw/yNG7/8jR/D/I0fw/yNH7/8jR+//Ikbv/yNG7/8jR+//I0fw/yNH7/8jRu//IUXv/yFI + 8P+XptD/08/A/5ak0f8bQ/L/m6nQ/9LOwP+yucr/K1Du/x5D8f8jRu//I0fw/yNH8P8dQe7/kKn4/1l5 + 9P8bQO//Ikbv/x5D8f+WpdH/y8jC/8XExP/DxMT/RWno/x1E8v8jSfD/I0nx/yNJ8P8jSPD/I0jv/yNI + 8P8jSfD/I0jv/yNJ8P8jSfH/I0nx/yNJ8f8jSfD/I0nw/yNI8P8jR+//I0fw/yNH8P8jR/D/I0fw/yNH + 7/8jR+//I0fw/yNG7/8jR+//I0fv/yNG7/8jRvD/I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//JEfw/yNG + 7/8jR/D/I0fw/yNG7/8jR+//I0fv/yNH8P8jRu//I0bv/yNH7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNG + 8P8jR/D/JEn0/yI/3v8WFX//ExBx/x4Zq/8nH+f/Jh/g/yYf4P8mH+D/JR7g/yYf4P8mH+D/Jh/g/yYc + 3/8lJOL/I0Xv/yNH8P8jR+//I0fv/yNG7/8jRvD/I0bv/yNH8P8jR+//I0fw/yNH8P8jR+//I0fv/yNH + 8P8jSfH/JD7r/yUp4/8mHuD/Jhvf/yYc3/8mHuD/Jh7g/yYe4P8mHt//KSDv/xoYhv8AAQD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/x4eH//w8PD/////hf///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wAmIcAAJiHAACYhwAAmIcAAJiDAACYgwAAmIMAAJSDAACYhwAA0M8cAHxe8AB4X + vAAdFrsAHBa6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkw4ROP8TEH7/MjiY/yYo + jP8RDnH/HRir/yYe7/8mHu3/IBq//xUUf/8TEnP/FhR+/x8au/8lHu3/JR3o/yUc6v8lG+j/JR3n/yQs + 6v8kQe7/I0rw/yNI8P8jRu//I0fv/yNH8P8jR+//I0bv/yNH7/8jR/D/I0fv/yNG7/8jR+//I0bv/yNH + 7/8jR+//I0fv/yNG7/8jR+//I0bv/yNG7/8jR+//I0fw/yNH7/8jR+//I0bv/yNH7/8jR+//I0fw/yNH + 7/8jRu//I0fw/yNG8P8jRu//I0fw/yNH8P8jR+//Ikbv/xxC7/8iRu//I0fv/yNH7/8jR+//HEDv/xY8 + 7/8ON/H/d4rV/9PPwP+0ucj/Iknw/1Zy4v/MysL/zszB/22E3P8XPvP/I0fv/yNG8P8hRfD/HUPv/9zj + /f+zwfr/Fzzu/yNG7/8ZP/L/b4fb/83Kwf/FxMT/y8rC/2V93v8ZPvL/I0fv/yNH7/8jR/D/I0fw/yNH + 7/8jRu//I0fv/yNH7/8jR+//I0fw/yNG8P8jR+//I0fw/yNH7/8jRu//I0fv/yNH7/8jRu//I0fv/yNH + 8P8jR+//I0bv/yNH7/8jR+//I0fv/yNG7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH8P8jR+//I0fv/yNH + 8P8jRu//I0bv/yNH7/8jRu//I0fw/yNH7/8jR+//I0bv/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0fw/yNH + 7/8jR+//I0fw/yRJ9f8cJ6f/Ew5u/xgVi/8mH93/Jx/j/yYf4P8mHt//Jh7g/yYf4P8mHt//Jh/g/yYe + 3/8mHt//JD7r/yNI7/8jR/D/I0fv/yNH7/8jR+//I0bv/yNG7/8jR+//I0fv/yNH8P8jR+//I0fv/yNI + 8P8jSO//JTTn/yYe3/8mG9//Jh7g/yYe3/8lHt//Jh/g/yYf4P8mH+D/Jh/g/ycf6P8lIr//BgcM/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP89PT///v7+8f///yr///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8AJiHAACYhwAAmIcAAJiHAACYgwAAmIMAAJiDAACUgwAAmIcAANDPHAB8X + vAAeF7wAHRa7ABwWugAeF7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALYPEUL/ExF//yMk + if8iJIf/EhBz/xkWkf8mHuf/JR3p/yYe7f8jHdn/IBrH/yQc2f8lHe7/JRzt/yUb6P8mH9z/JSvk/yQ9 + 7v8jSfD/I0jw/yNG7/8jR+//I0fw/yNH7/8jR/D/I0fv/yRH7/8jR/D/I0fv/yNG7/8jR+//I0fw/yNH + 8P8jRvD/I0bv/yNG8P8jRu//I0bv/yNH8P8jRu//I0fw/yNG8P8jRu//I0fv/yNG7/8kR+//IETv/xo/ + 7/8hRO//I0bv/yNH8P8kR/D/I0bv/yNG7/8jR+//I0fv/xxB7/+Amvf/QmHx/x5C7/8jR/D/Gj/v/0dq + 8/+ouvr/sb/7/6e14v/Ix8H/wcPF/0Jj5/8cQ/H/pK7N/9HNwP+yucn/K1Dt/x5C8f8jR+//HkLv/y5V + 8P/q8v7/tcH6/xc87v8jRvD/GT/y/1Bt5P/Gx8T/xcTE/87Lwf+DldX/HELx/yNG7/8jR+//I0fw/yNH + 7/8jR+//I0fw/yNG7/8jR+//I0fv/yNH7/8jRu//I0fv/yNG8P8jR/D/I0bv/yNH7/8jR+//I0bv/yNG + 8P8jR+//I0bv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNG8P8jR+//I0fv/yNG7/8jRvD/I0bv/yNG + 7/8jRu//I0fw/yNH8P8jR+//I0fv/yNH8P8jRvD/I0bv/yNH8P8jR+//I0fw/yJF7/8jRu//I0bv/yNG + 7/8jR+//I0fv/yNJ9P8iPtv/FRR9/xQRdP8hHL7/JyDn/yYe3/8mHuD/Jh7f/yYf3/8lH9//Jh7f/yYe + 4P8mHN//JDbo/yNK8P8jR+//I0fw/yNH8P8jRu//I0fw/yNH8P8jR+//I0bv/yNG7/8jR+//I0fw/yNH + 8P8jSfD/JTLm/yYc3/8lHt//Jh/g/yUf3/8mHt//JR7g/yYe3/8mHuD/Jh/g/yYf4P8lH+D/KCHp/xET + Q/8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/jo6P/////5n///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////ACYhwAAmIcAAJiHAACYhwAAmIMAAJiDAACYgwAAlIMAAJiHAADQz + xwAfF7wAHhe8AB0WuwAcFroAHhe8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABUAAADWDxJM/xUT + gf8YF37/Fxh9/xQSeP8WFH7/IxzY/yUd7P8kHef/JR3s/yYd8v8lHPD/JRzj/yUhxv8lMbj/JUDQ/yRI + 7P8jSfH/I0bv/yNH8P8jR+//I0fw/yNH8P8jR+//I0fw/yNH7/8jR+//I0fv/yNH8P8jRvD/I0fw/yNG + 7/8jRu//I0bw/yNG8P8jR/D/I0bw/yNG8P8jR/D/I0fv/yNG8P8jR/D/I0bw/yNH7/8jR+//IETv/zZZ + 8f9VdPP/KE3w/xg97/8jRu//I0fw/yNH8P8jR+//I0fv/x1B7v86XvH/5u/+/0Vj8v8dQe//HUHv/zle + 8f/j7P3////////////z8/D/wsLD/8zKwf9ie97/Djb1/1x44P/OzMH/zszB/2eA3f8XPvL/JEfw/xxA + 7/89YfL/3un+/1p59P8bQO//I0fw/x9D8P8xVev/uLzH/8fGw//Jx8P/o6/N/yVJ7/8iRfD/I0bw/yFE + 7/8iRe//I0fw/yNH7/8jRu//I0bv/yNH8P8jRvD/I0fv/yNH7/8jR+//I0fw/yNH8P8jRu//I0fv/yNH + 8P8jR/D/I0fw/yNH7/8jR/D/I0fw/yNG8P8jR+//I0fw/yNG7/8jR/D/I0fw/yNG8P8jR/D/I0bv/yNG + 7/8jRu//I0fv/yNH8P8jR/D/I0fv/yNH8P8jR/D/I0fv/yNG8P8jRvD/I0fw/yNG7/8kSfD/I0fv/yNH + 7/8jR/D/I0bv/yNH8P8kSvX/HSuv/xMObv8ZFpD/Jh/h/yYe4f8mHt//Jh/g/yYf4P8lHt//Jh7f/yYf + 4P8mHN//Jivk/yNI8P8jR/D/I0bw/yNH7/8jRu//I0bv/yNH8P8jR/D/I0bw/yNH7/8jR/D/I0fw/yNH + 8P8jSfD/JTfo/yYc3/8mHuD/Jh/g/yYf4P8lH9//Jh7g/yUf4P8mH+D/Jh7g/yYf4P8mH+D/JR7f/ygg + 7f8fH5X/AQIA/wAAAP8AAAD/AAAA/wAAAP8AAAD/JCQl//Hx8e////8n////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wAmIcAAJiHAACYhwAAmIcAAJiDAACYgwAAmIMAAJSDAACYh + wAA0M8cAHxe8AB4XvAAdFrsAHBa6AB4XvAAhGr0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsAAAA8g8S + S/8WFIL/FBJ6/xQSev8UEnr/FBJ1/yEbzv8mHu//JRzt/yUb5/8mH9b/JSnD/yU2s/8kQsb/I0no/yNJ + 8/8jR/H/I0fv/yNH7/8jRvD/I0bv/yNG7/8jRvD/I0fv/yNH8P8jR/D/JEfw/yRH7/8jRvD/I0fv/yNH + 7/8jRvD/I0bv/yNH7/8jRvD/I0fw/yNH7/8jR/D/I0fw/yNH7/8jR+//I0bw/yNH7/8jR/D/I0fw/x9D + 7/84XvH/i6j4/6zC+v9ig/X/GT7v/yJF7/8jRu//I0fw/yNH8P8VO+7/coz2/8/c/f8nTPD/IEPv/xY8 + 7/+Pp/j////////////////////5/8jIxv/LyMH/gpbW/xg+8/8nTe7/rLTL/9HNwP+qtMv/JEnu/xM4 + 8P8XPO//QmXy/8LW/P8tUvH/H0Pv/yNH7/8iRvD/H0Tw/5yoz//LycP/xsbD/7y/xv80V+v/H0Pw/yNF + 8P8pTvD/IUfw/yNH8P8jR+//I0bw/yNH8P8jR+//I0bv/yNG8P8jRvD/I0bv/yNH7/8jR/D/I0fv/yNG + 7/8jR/D/I0fv/yNG7/8jR+//JEfw/x9C7/8aPu//I0fv/yNH7/8jR/D/I0fv/yRH8P8kR/D/I0fv/yNH + 7/8jR+//I0bv/yJG7/8aP+//Gz/v/yJG7/8jR/D/I0bv/yNH8P8jR/D/I0fw/yNG8P8dQO//T3P0/zpg + 8v8gRO//I0bw/yNG7/8jSPL/I0To/xcZhv8UEXT/IRy//ycf5v8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mHuD/JiDg/yNA7P8jSPD/I0fv/yNH7/8jR/D/I0bw/yNG7/8jR+//I0fw/yNH8P8jR/D/I0bv/yNG + 7/8jSfD/JDzr/yYf3/8lHd//Jh/g/yYf4P8lHt//Jh7f/yYf3/8mH9//JR/f/yYf4f8mH+L/Jh/i/yYf + 4P8mHuP/JyPZ/wsNI/8AAAD/AAAA/wAAAP8AAAD/AAAA/0lJSf////+F////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8AJiHAACYhwAAmIcAAJiHAACYgwAAmIMAAJiDAACUg + wAAmIcAANDPHAB8XvAAeF7wAHRa7ABwWugAeF7wAIRq9AB8YvQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOQAA + AP0PEUH/FhOB/xUTe/8UEnv/FBJ3/xYThP8jGt//Jh3i/yYkyv8lMsH/JUDK/yRH3f8jSfD/I0f1/yNH + 8f8jRvD/I0fv/yNH8P8jR/D/I0fw/yNG7/8jRu//I0bv/yNH7/8jR/D/I0fw/yRH7/8kRu//I0bv/yNH + 8P8jR+//I0fw/yNH8P8jR/D/I0fv/yNH8P8jR+//I0bv/yNG7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH + 8P8kR/D/H0Pv/xg97/85XfL/obr5/3WT9/8YPe7/I0bv/yNG7/8jRvD/Fz7v/5y0+f+Bmvf/Fzzu/yJF + 7/8fRe//yNj8/9Hb/P9ujfb/dpP3/7TF+f/Hys//ysfA/6Wuzf8hRvD/Fz3y/2R+3v/QzcH/ysnB/26H + 3/9Scff/Kk/w/zZb8f/J3Pz/Nlrx/x5C7/8jR/D/I0bw/xpA8v94jtn/zcvB/8XFxP/Gx8P/Um/j/xs/ + 8f8bP+//d5X3/0tu8/8cQO//I0fv/yNH8P8hRfD/GT7v/xY77/8YPe//Fjzu/xo/7v8jR+//I0fw/yNH + 7/8jR+//I0fw/yNH7/8jR+//I0fv/x9C7/8yVfD/fJv3/ylN8P8hRe//I0fw/yNG7/8hQ+//I0fw/yNH + 8P8jR/D/I0fv/x9D7/8dQ+//SWvz/0Nm8v8bQe//IkXw/yNH7/8jR/D/I0fv/yBD7/8iRu//FTvu/4GY + 9v9oiPb/Gz/v/yNG8P8jR/D/JEn1/yA2x/8TEHH/GRaO/yYf4P8mH+H/Jh/g/yYf4P8mHuD/Jh/g/yYf + 3/8mHuD/Jhvf/yUy5v8jSvD/I0fw/yNH8P8jR/D/I0fw/yNH8P8jRu//I0bv/yNH8P8jR+//I0bv/yNH + 7/8jSfD/I0Dr/yYg4P8mHd//Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH9//Jh/h/ycf5v8nIOL/Jh/b/yYf + 2v8mH+L/KB/r/ygj1/8MDiP/AAAA/wAAAP8AAAD/AAAA/wAAAP9TVVfe/f7/D/3+/wD9/v8A/f7/AP3+ + /wD9/v8A/f7/AP3+/wD9/v8A/f7/AP3+/wD9/v8A/f7/AP3+/wD9/v8A/f7/AP3+/wD9/v8A/f7/AP3+ + /wD9/v8A/f7/AP3+/wD9/v8A/f7/AP3+/wD9/v8A/f7/ACYhwAAmIcAAJiHAACYhwAAmIMAAJiDAACYg + wAAlIMAAJiHAADQzxwAfF7wAHhe8AB0WuwAcFroAHhe8ACEavQAfGL0AIBm9AAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AE0AAAD/DhA3/xYTgv8VE3v/FRN7/xQRdf8aF4L/Ji22/yU6vf8kRdT/I0nt/yNI9P8jR/P/I0bw/yNG + 7/8jR+//I0bv/yNG7/8jR/D/I0fv/yNH7/8jR/D/I0fv/yNH7/8jRu//I0bw/yNH8P8jR/D/I0bw/yNG + 7/8jR/D/I0fv/yNH8P8jR/D/I0fw/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH8P8jR+//I0fw/yNH + 8P8jR+//I0bv/yNH8P8jR/D/HD/v/xxB7/+Pq/j/WHn1/xk97/8jR+//Ikbv/x1B7/+zxvv/R2fy/xxB + 7v8gRO//MFHw/77N/P8yV/H/Eznu/xY77v8WP/H/kJ/U/9HNwP+3vMf/M1js/xs/8v8sUO3/s7rJ/8nH + wP/Iysz////8/93l/f9oivX/zdz8/0Vk8v8bQO//I0fv/yNH7/8ZPvL/V3Pi/8jIw//FxcT/zszC/3OG + 2/8YP/L/GD3v/46l+P+Pp/j/ETfu/xs/7/8gRO//LlPw/1h49P+Jo/f/oLn5/4yl+P9HaPL/Fj3v/x9C + 7/8kR/D/I0bv/yNH7/8iRu//Fjzv/xc87v8ZP+7/orj5/+Hs/v8mS/D/IETv/yFF7/8vU/D/P2by/x9D + 7/8jRu//I0fw/yBE7/8jSe//orj5//H4///z+v//jKX4/xpA7/8iRu//Ikbv/yZK8P9CaPP/I0jw/xU6 + 7v+BmPb/b472/xs/7/8jRvD/I0fw/yRI8/8cJqP/Ew9t/x8btP8nIOf/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh7g/yUh4P8kQu3/I0nx/yNH8P8jR/D/I0fw/yNG7/8jRu//I0fw/yNH7/8jR+//I0bv/yNH + 8P8jSvH/Iz7r/yYi4P8mHd//Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mHt//Jh/k/ycg4v8gG7n/GReR/xQQ + hf8KB4D/DAmK/xIOsP8PEGj/AAAA/wAAAP8AAAD/AAAA/wAAAP8BAQD/IiSE7FZQ8S5WUPEAVlDxAFZQ + 8QBWUPEAVlDxAFZQ8QBWUPEAVlDxAFZQ8QBWUPEAVlDxAFZQ8QBWUPEAVlDxAFZQ8QBWUPEAVlDxAFZQ + 8QBWUPEAVlDxAFZQ8QBWUPEAVlDxAFZQ8QBWUPEAVlDxAFZQ8QAmIcAAJiHAACYhwAAmIcAAJiDAACYg + wAAmIMAAJSDAACYhwAA0M8cAHxe8AB4XvAAdFrsAHBa6AB4XvAAhGr0AHxi9ACAZvQAmH8AAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAABTAAAA/wgIJv8WFID/FRN7/xMQd/8WFoD/Iju0/yRI3v8jSfT/I0f0/yNH8P8jR+//I0fv/yNG + 7/8jR+//I0fw/yNH7/8jRvD/I0fw/yNH8P8jR+//I0fw/yNH7/8jR+//I0fw/yNH8P8jR+//I0bw/yNH + 8P8jR/D/I0fw/yNG7/8jR/D/I0bw/yNH8P8jR+//I0fw/yNH8P8jR/D/I0fv/yNG7/8jR/D/I0fw/yNH + 8P8jRu//I0fv/yNH8P8jR/D/I0fw/yRH8P8eQe//Kk/w/4em+P8tUfD/IETv/x5B7/85W/L/ssf6/ydO + 8P8gRO//IETv/zpa8f+Emfb/Fjzv/yNH7/8jR+//GD7y/2+E2//PzcH/x8fE/05q5P8ZP/L/GT/x/2yE + 2//PzcH/yMbB/7fB5P/S3f7/8fn//+zy/v9ScPP/GT7v/yNH8P8jR+//H0Pw/zRZ6v+6vsb/xsbD/8zJ + wv+SotL/H0Xx/xo+7/9hgfT/xdX8/zdd8f9NcPP/XIL1/3mX9/9+nfj/hqH4/4qk+P+wwvr/3un9/5+1 + +f8jS/D/HEDv/yRH7/8iRe//H0bv/1159P9Rb/P/IEXv/+fu/v+Rqfj/GkDv/yJG7/8gQ+//NFjx/1qC + 9f8gRO//I0bw/yNH8P8aP+//Y4T0/561+f9EY/L/c471/+rx/v9NbvL/Gj/v/yJG7/8hRe//fJ74/zZZ + 8f8UOe7/bov1/2iJ9f8bP+//I0bv/yNH8v8jROr/FxqG/xYTfv8lH9b/Jh/j/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYc3/8lMOX/I0nw/yNH7/8jR+//I0fv/yNG7/8jRu//I0fv/yNH8P8jR+//I0fv/yNH + 8P8jSfH/Izvq/yYg4P8mHN//Jh7g/yYe4P8mH+D/Jh/g/yYf4P8lH9//Jh/j/yYf3v8bGJr/FBN0/wkG + b/8XGXr/Wl+d/3mCrv9iaKX/JClM/wAAF/8AAAb/AAAA/wAAAP8AAAD/ExRH/yYg4f8dFeGtJh7gACYe + 4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJiHAACYhwAAmIcAAJiHAACYg + wAAmIMAAJiDAACUgwAAmIcAANDPHAB8XvAAeF7wAHRa7ABwWugAeF7wAIRq9AB8YvQAgGb0AJh/AACYf + vgAjSPAAI0jwACNH7wAjRu8AI0bvACNH7wAjR+8AI0bvACNH8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAaQAAAP8DBA7/FBNt/xQQef8UEHT/HzPB/yRK+/8jR/T/I0fv/yNH7/8jRu//I0fv/yNH + 7/8jR+//I0bv/yNH8P8jR+//I0fw/yNH8P8jR+//I0bv/yNH7/8jR/D/I0fv/yNG7/8jR/D/I0fw/yNH + 7/8kR/D/I0fw/yNH8P8jR+//I0fw/yNG8P8jRu//I0fw/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNG + 8P8jR+//I0bv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0bw/xk87v9UdfT/XoH1/xo+7/8NNO7/fZj3/5ux + +f8YPe7/I0bv/yBE7/83V/D/mK74/x9G8P8iRu//I0fv/x1B8f9Uc+L/yMjD/8/Mwf9shdv/GT/y/x5D + 8f8tU+z/tbzI/9HNwP+Wo8z/KVHx/3ST+P/M3/3/dZP1/xg97v8jRu//I0fv/yJF8P8iR+//pK7N/8nH + w//IxsP/r7bJ/ylN7v8bP/D/SGvz/9rn/v9dhPX/P2Py/zJW8v8fRO//Gz/v/xg97/8XPO7/Fjzv/z9i + 8f/A0Pv/xdT7/ytR8P8cQO//GD3v/4Gc9////////////6q++v+yxfr/Jkzw/yBE7/8jR+//IETw/zJW + 8f9qkPb/Ikbv/yJG7/8jR+//IkXv/1h89P8iRu//GD3v/w007v9/mvf/jKf4/xg97v8iRu//GUDv/6a9 + +v9nhfX/CzLu/4Wd9/9ggfT/G0Dv/yNH8P8jSPT/ITrS/xMSc/8bF5r/Jx/l/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYe3/8mH9//JD7s/yNJ8P8jR+//I0fv/yNH7/8jR/D/I0fw/yNH7/8jR+//I0fw/yNI + 8P8jSPD/JTXo/yYe4P8mHt//Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/i/yYg4P8bF5j/FBJz/wYE + cv89QZP/xMjY//b38P/39/D/9vbx/9vd3/+OlLz/Jy5w/wAAFP8AAQD/EhRF/ych1f8nH+f/Jh/f+SYe + 4DYmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYhwAAmIcAAJiHAACYh + wAAmIMAAJiDAACYgwAAlIMAAJiHAADQzxwAfF7wAHhe8AB0WuwAcFroAHhe8ACEavQAfGL0AIBm9ACYf + wAAmH74AI0jwACNI8AAjR+8AI0bvACNG7wAjR+8AI0fvACNG7wAjR/AAI0fwACNG7wAjR+8AI0bvACNH + 8AAAAAAAAAAAAAAAAHQAAAD/DBEg/xkghv8WFoT/HjC8/yNJ9f8jR/D/I0bv/yNH7/8jR+//I0bv/yNG + 7/8jR+//I0fv/yNG7/8jR+//I0fw/yNG7/8jR+//I0fv/yNG7/8jR/D/I0fv/yNH8P8jR/D/I0bv/yNG + 8P8jR+//I0fv/yNG7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fw/yNH + 7/8jR+//I0fw/yNH8P8jR/D/I0bw/yNH8P8jR/D/I0fw/yNH8P8hRe//JEjv/2uM9v8fRu//WHjz/+Lu + /v9SdPP/Gj/v/yNH7/8gRO//L1Hw/8LR/P8uUfH/H0Pv/yJG7/8fQ/D/OFrp/8PDxP/Ny8H/jp/T/xxB + 8f8iRvD/GkDx/3WL2f/Py8H/zMvC/1Rx4P8BK/D/gJv3/6S2+f8XPO7/I0fw/yNH8P8jRu//G0Ly/3+T + 1//NysH/xcXD/8DCxf8/Yuj/HEDx/yJI7/+zyPv/Tm/z/xY67v8hRO//Ikbw/yNH8P8jR/D/I0bw/yNG + 7/8ZPu7/HUXv/7DC+v/B0fv/HULv/ypQ8P+3yfv/eZf3/6m9+v//////gZv3/xI57v8jR+//I0fw/yBE + 8P8xVfH/f6D3/yRI7/8iRu//I0bv/yNH8P9PdPT/IEXv/yNH7/8fQu//NFny/5Kq+P8fQu//IkXv/xxB + 7/+Kp/j/rL/6/xI87v+uwvv/QmPy/x5C7/8jR/D/JEn3/x0vuf8TEG7/IBu5/ycf5v8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yUe4P8mHN//JSji/yNG7/8jR+//I0fv/yNH8P8jR+//I0fw/yNG8P8jR/D/I0fv/yNJ + 8f8kRe//JS3k/yYd3/8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/ycf5v8eGqz/FBJz/wkG + c/8/Q5P/3ODl//j28f/r6ur/6urq/+rq6v/w8O7/+Pfz/9fb5/9cY5v/CAxX/yEbyf8oIOz/Jh7f/yYc + 3/8mH+COJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmIcAAJiHAACYh + wAAmIcAAJiDAACYgwAAmIMAAJSDAACYhwAA0M8cAHxe8AB4XvAAdFrsAHBa6AB4XvAAhGr0AHxi9ACAZ + vQAmH8AAJh++ACNI8AAjSPAAI0fvACNG7wAjRu8AI0fvACNH7wAjRu8AI0fwACNH8AAjRu8AI0fvACNG + 7wAjR/AAI0bvACRK+QAGCh1sFCFW/yRF1/8kR/D/IkLl/yRI8/8jRvD/I0bv/yNH7/8jRu//I0fv/yNH + 7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fw/yNH + 8P8jRu//I0fw/yNH7/8jRu//I0bv/yNH7/8jR/D/I0fw/yNH7/8jR+//I0bv/yNH7/8jR+//I0fw/yNH + 8P8jR/D/I0fw/yNH7/8jR/D/I0fw/yNH7/8jR/D/I0jw/yNJ8f8jSvD/I0nw/xtB7/9KcvT/mbb6//z9 + //+uwPn/HEPv/yFG7/8jR+//IUfv/yNK8P/H1/3/X330/xM57v8bQe//Eznv/ydM7f+stsv/zsvB/6yz + yv8kSu//IUXw/xxA8f8+YOz/wcTH/87LwP+jr83/GkDv/0dn8/+zxvr/IUjw/yJF7/8jR+//I0fw/xk/ + 8v9cduD/yMnD/8XFw//KycL/X3jf/xg+8v8ZPu7/n7L5/2WE9f8YPe7/I0fv/yNH8P8jRu//I0fv/yNH + 8P8jR/D/JEfw/x1B7/8dRO//rcH6/4uj+P8+ZfP/T3b0/xE27v8YP+7/rsD6/8/c/P8jSO//IUTv/yNH + 7/8gRO//LlLx/6a9+v8rTvD/IUXv/yNG7/8cQO//XYH1/05x9P8TOO7/Gj7v/yhO8P+TqPf/IETv/yJG + 8P8kSO//SnHz/83a/P+pvfn/uMr7/yVK7/8hRe//I0fw/yRJ9P8aJqT/FRB3/yQez/8mH+P/Jh7f/yYf + 4P8mH+D/Jh/g/yYf4P8lHt//Jhze/yUy5/8jSfD/I0fw/yNH7/8jR/D/I0fv/yNH7/8jRvD/I0fw/yNJ + 8P8kPev/JiXh/yYd3v8mHuD/Jh/g/yYe3/8mHt//Jh7g/yYf4P8mH+D/Jh7g/ycf5P8kHdD/FhR+/wwJ + c/8mKYX/0dXe//b08P/q6er/6urq/+nq6v/p6en/6enp/+np6f/y8e3/8PHw/4aNu/8SE4f/IBjQ/ych + 5f8lJ+L/Jh7g2iYf4A4mH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJiHAACYh + wAAmIcAAJiHAACYgwAAmIMAAJiDAACUgwAAmIcAANDPHAB8XvAAeF7wAHRa7ABwWugAeF7wAIRq9AB8Y + vQAgGb0AJh/AACYfvgAjSPAAI0jwACNH7wAjRu8AI0bvACNH7wAjR+8AI0bvACNH8AAjR/AAI0bvACNH + 7wAjRu8AI0fwACNG7wAkSvkwIkLWxiVI8P8jSPf/I0jx/yRI8/8jR+//I0bv/yNG7/8jR/D/I0bv/yNH + 7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH + 7/8jR+//I0fw/yNH7/8jR/D/I0fw/yNG7/8jR+//I0fw/yNG7/8jR+//I0fv/yNG7/8jR+//I0bv/yNH + 8P8jR/D/I0fw/yNH8P8jSPD/I0nw/yNJ8f8jR/D/JETu/yQ/6/8kO+n/JDbp/yU05/8hKuX/Ljzm/87b + +v//////iJrx/xcf4v8kJ+P/Jirj/yQq5P8aJ+T/q7n1/9rk/f+uw/r/vNH8/3+Z9/8mRe3/i5jP/9LO + wf+8wMb/Pl/o/xxC8f8fRPD/L1j1/7vH2v/Jx8D/zMrC/1h04f8aQPL/pr77/zhc8v8fQu//I0fv/yNH + 8P8eQvH/PF/o/77Bxv/GxcP/zcvB/4CU1v8aQfL/GT3v/4ah+P9zjfb/Fjvu/yNH8P8jR/D/I0bv/yNH + 8P8jR+//I0fw/yNH8P8jR/D/HkLw/yFH8P+et/n/e5z3/2mL9v8WPO//Gz/v/zhc8f/y+P//XHz0/xo/ + 7/8jR/D/IUXv/yNJ7/+6zfv/Wnr0/xE37v8jRu//HkLv/zZZ8f+jvPr/WHjz/zFX8f+ww/r/l6/4/xc9 + 7v8iRu//Jkrv/ytU8P+Lovf//////4Oc9/8XPe7/I0bv/yNI8f8jRu3/GR6Q/xcThv8mH9//Jh7h/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYc3/8kO+v/I0nw/yNH7/8jRu//I0fv/yNH7/8jR/D/I0nx/yRH + 7/8lMub/JiDf/yYd3/8mH+D/JR7g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8nIOb/HRmj/xIQ + cv8LDHf/oKXG//j38f/p6er/6urq/+rq6v/p6en/6unp/+np6f/q6ur/6enq/+zs6//39/D/kpm5/xMS + jv8hH9z/I0jy/yUv5vwmHN5GJhzeACYc3gAmHN4AJhzeACYc3gAmHN4AJhzeACYc3gAmHN4AJhzeACYc + 3gAmHN4AJhzeACYc3gAmHN4AJhzeACYc3gAmHN4AJhzeACYc3gAmHN4AJhzeACYc3gAmHN4AJhzeACYh + wAAmIcAAJiHAACYhwAAmIMAAJiDAACYgwAAlIMAAJiHAADQzxwAfF7wAHhe8AB0WuwAcFroAHhe8ACEa + vQAfGL0AIBm9ACYfwAAmH74AI0jwACNI8AAjR+8AI0bvACNG7wAjR+8AI0fvACNG7wAjR/AAI0fwACNG + 7wAjR+8AI0bvACNH8AIjRu9zI0fw7SRJ9f8jR/H/I0bv/yNG7/8jRvD/I0fw/yNH7/8jR/D/I0fw/yNH + 7/8jRu//I0fv/yNH7/8jR+//I0bv/yNG7/8jR/D/I0fw/yNG8P8jR+//I0fv/yNH7/8jRu//I0bv/yNH + 8P8jR/D/I0fw/yNH8P8jR/D/I0bv/yNH8P8jR/D/I0fw/yNH7/8jRvD/I0fv/yNH8P8jR+//I0fw/yNH + 7/8jR+//I0nx/yNJ8P8jRu7/JD/r/yQ36f8lL+X/Jifj/yYi4f8mIOD/Jh3f/yYb3/8mHd//JRzf/yEZ + 3v95h+3/0Nj5//////98hOz/GRLd/yYc3/8mHd//GxLe/3eB7P//////oq7y/42S7//o7vz/xc36/4mR + 0//KycH/zMzC/1pg2P8bIOb/JS7l/x0t6f+suO7/09LH/8rIwP+hrM3/Fz3v/4ml+P9TcfP/G0Hv/yNJ + 8P8jSvD/IUjx/yVM8P+ossz/yMbD/8rIw/+grM7/Ikfw/xk+7/91k/f/fpT2/xQ67v8jR/D/I0fw/yJG + 7/8jR/D/I0fw/yNH8P8jRu//Ikbv/yJF7/8UOO//NFrx/6rB+v/H2P3/UHD0/xA27f8WO+7/ucv7/3mV + 9v8YPe//I0bw/yJF7/8fRu//nbf5/+Do/f9EaPP/Fj3u/xc97/8dQO//L1Px/6K7+v/5/P///////01w + 8/8aP+//I0bv/yZK8P85YPL/OVvx/8DR+/82WvH/H0Lv/yNG7/8jSPL/I0Po/xYYgf8bF5j/Jx/i/yYe + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYd3/8mIeD/JEHt/yNI8P8jRu//I0fv/yNH7/8jRu//I0nx/yQ/ + 7P8mKOP/Jhze/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf3/8mH9//Jh/g/yYf4P8mH+P/JR7W/xcV + gv8HBHD/Ulie//Lz7//s6+v/6enp/+rq6v/q6ur/6enp/+np6v/q6ur/6urq/+rq6v/p6en/7Ovr//X2 + 8P+PlLf/Fhaq/yND8/8jRe7/JSLgdSUi4AAlIuAAJSLgACUi4AAlIuAAJSLgACUi4AAlIuAAJSLgACUi + 4AAlIuAAJSLgACUi4AAlIuAAJSLgACUi4AAlIuAAJSLgACUi4AAlIuAAJSLgACUi4AAlIuAAJSLgACUi + 4AAmIcAAJiHAACYhwAAmIcAAJiDAACYgwAAmIMAAJSDAACYhwAA0M8cAHxe8AB4XvAAdFrsAHBa6AB4X + vAAhGr0AHxi9ACAZvQAmH8AAJh++ACNI8AAjSPAAI0fvACNG7wAjRu8AI0fvACNH7wAjRu8AI0fwACNH + 8AAjRu8AI0fvACNG7yUjR++3I0fv/yNH8P8jR/D/I0bv/yNH8P8jRu//I0bw/yNH8P8jR+//I0bw/yNG + 7/8jRu//I0bv/yNH7/8jRu//I0fv/yNG7/8jRu//I0fv/yNH7/8jRu//I0bv/yNH8P8jR+//I0fw/yNG + 7/8jR/D/I0fw/yNH8P8jR+//I0fw/yNH7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fw/yNI + 8P8jSfD/JETv/yQ76/8lMOX/Jibi/yYg4P8mHd//Jhzf/yYd4P8mHuD/Jh7g/yYe3/8mHuD/Jh7g/yYf + 3/8jGt//P0vl/z5G5P+1vvX//////1pf6P8cE97/Jh/g/yIZ3/8wMuH/6/D8/9fa+f8wNuP/JSXg/7e7 + 9//e5Of/v7/B/83Mwv92gNL/HRXh/yYc3/8bEN3/aW/t/+Pm2//CwsD/ysvE/0RM2P9dYOz/b4Hu/xwl + 4/8lMeX/JTTn/yQ26f8eNuv/hpXT/83Lwv/HxsP/ub3H/zJV6/8XO/D/Y4T1/3qQ9f8VO+7/JEnw/yNI + 7/8aQO7/HEDv/xxA7/8ZPe//FDvv/xg/7/8fRe//PGDy/5Sr+f+6z/z/d5j3/+Xw//+En/j/Q2jz/4mp + +f9QdvT/HUPw/yNJ8P8jSPD/HkTw/12E9v/t9///+fz//7HD+v9igfX/JEzx/xQ87/94mff/xtn9/3mW + 9/8cRfD/Iknw/yJJ8P8mTfH/OmTz/yBH8P8sU/H/IUXv/yNH7/8jR+//I0jy/yJA4f8UE3f/HRio/ycg + 5f8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8lHd//JSTh/yRE7v8jSPD/I0fv/yNH7/8jSPD/I0jx/yU2 + 6P8mIOD/Jh3f/yYe3/8mH+D/Jh7g/yYe3/8mHt//Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mH+D/Jx/m/yAb + uP8QDXL/Fxl9/8LG1//09O//6enp/+rq6v/q6ur/6urq/+np6f/q6ur/6urq/+np6f/p6en/6unp/+rp + 6v/s6+r/9PTt/25ys/8WL9n/I0v0/yQ66acjSPAAI0jwACNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI + 8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI + 8AAjSPAAJiHAACYhwAAmIcAAJiHAACYgwAAmIMAAJiDAACUgwAAmIcAANDPHAB8XvAAeF7wAHRa7ABwW + ugAeF7wAIRq9AB8YvQAgGb0AJh/AACYfvgAjSPAAI0jwACNH7wAjRu8AI0bvACNH7wAjR+8AI0bvACNH + 8AAjR/AAI0bvACNH708jR+/hI0fv/yNG7/8jR+//I0fv/yNG7/8jR/D/I0bw/yNG8P8jRu//I0fv/yNG + 7/8jRu//I0fv/yNG7/8jRu//I0fv/yNH8P8jRvD/I0bv/yNG7/8jR+//I0fv/yNG7/8jR/D/I0fv/yNG + 8P8jR+//I0bv/yNH7/8jR+//I0bw/yNH7/8jRu//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0nw/yNH + 7/8kPOr/JS/m/yYj4f8mHt//Jh3f/yYd4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH9//JR7f/yYf + 4P8mH+D/IBbf/0VI5P84PeP/HRzf/7/I9v/c5vv/Kynh/yIa3/8mHt//GxTe/4+Y8P//////4+r8/3J4 + 6/9NWun/197v/8nIw//KysL/naDM/yAZ4f8lHeD/JBvg/yYj4/+yu+r/1NPH/8rJwf+bn8v/i5jw/3uH + 7v8dE93/Jhzf/yYc3v8mHd//HBPh/2Zn1P/LzMP/xsXE/8PHxP9PVdr/FxPi/1Va6f96fO7/Fx3i/x8l + 4/9IUen/kqf2/0Ba6v9FYOr/XXnw/4Sb9/+ovfr/2OT+//f+///o7vv/XnDt/xQs6P9gcO7/t8H1/7LB + 9f9ne+//ITfq/yQ56f8kOen/JDnp/yE16f9MZ+//b4Lw//j9/////////////0xg7f8aKuf/JDXo/yY4 + 6P8dLOf/JDPo/yQ06P8lNOn/JDbp/yU66v8kPOv/IT3s/yNE7v8jR+//I0nw/yNK9P8hPNT/ExJ0/x4Z + sv8nIOf/Jh/f/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jhzf/yUn4/8jR+//I0jw/yNH8P8jSfD/I0Xu/yUu + 5f8mHd//Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH9//Jh7f/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 5P8bF5r/BgRt/21zrP/29/D/6urq/+rq6v/q6ur/6unp/+rq6v/q6ur/6urq/+rq6v/q6ur/6unp/+rp + 6f/q6ur/6enp/+/v7P/e3t7/O1DM/xxB8v8jSPDeI0jwFCNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI + 8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI + 8AAjSPAAI0jwACYhwAAmIcAAJiHAACYhwAAmIMAAJiDAACYgwAAlIMAAJiHAADQzxwAfF7wAHhe8AB0W + uwAcFroAHhe8ACEavQAfGL0AIBm9ACYfwAAmH74AI0jwACNI8AAjR+8AI0bvACNG7wAjR+8AI0fvACNG + 7wAjR/AAI0fwACNG73gjR+/9I0bv/yNG8P8jR/D/I0fw/yNG7/8jR/D/I0fw/yNH8P8jR/D/I0fv/yNH + 8P8jR+//I0fv/yNH7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH7/8jR/D/I0fv/yNH7/8jR+//I0fv/yNH + 7/8jRu//I0fv/yNG7/8jR/D/I0fw/yNH8P8jR/D/I0bv/yNH7/8jRu//Ikbx/yJH8v8iR/L/IT/x/yMy + 6v8lJeT/Jh3f/yYc3/8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/f/yYe4P8mH+D/Jh/g/yYf + 4P8lHt//Jh7f/yQb4P8vL+H/SFbl/x0T3/8vL+L/2eP6/3J57P8bE97/Jh7g/yEZ3/8xMuL/2N/6//// + ////////8vb+//P0+f/HyMj/yMfB/7K3x/8vMt//Ixnh/yYf4P8eFt7/Ojzl/6uz0v/JyMH/zMvD/46h + 4f98gu//IBff/yUe3/8mH+D/Jh/g/yAX4P9BRdv/v8PF/8bFxP/NzsL/b3HT/xUN4P9RSeb/d3Hr/xgO + 3f8cEt3/U1Pm//////////7/9/n9//H1/P/4+P3/2+H6/6218/9yeOv/MzTi/x8X3v8mHd//HRbf/yEd + 3/8mI9//IBnf/yYd3/8mHt//Jh7f/yYd3/8kG9//QUbk/yIe3/9WWuj/nqXx/5ih8f84MuL/Ihbe/yUb + 3v8kGd//Jhve/yYc3/8mHN7/Jhvf/yYd3/8mHt//Jh/g/yYg4P8mIuH/Jijj/yUy5/8lQPH/IDfJ/xQS + dP8gGrn/Jx/m/yYf3/8mH+D/Jh/g/yYe4P8mHt//Jh/g/yYc3v8lKOT/I0rx/yNI8P8jSfD/JD/s/yYn + 4/8mHd//Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe + 4v8lH9j/ExCC/xwefv/Q1eP/9/by/+np6f/q6ur/6unq/+rp6f/q6ur/6urq/+rq6v/q6ur/6urq/+np + 6f/q6er/6urq/+rq6v/q6er/9vPr/5up4/8ZPu7/I0fw/yNH71kjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAmIcAAJiHAACYhwAAmIcAAJiDAACYgwAAmIMAAJSDAACYhwAA0M8cAHxe8AB4X + vAAdFrsAHBa6AB4XvAAhGr0AHxi9ACAZvQAmH8AAJh++ACNI8AAjSPAAI0fvACNG7wAjRu8AI0fvACNH + 7wAjRu8AI0fwDSNH8KIjR/D/I0bv/yNG7/8jR/D/I0fw/yNH7/8jRu//I0bv/yNH7/8jR/D/I0bw/yNH + 7/8jR/D/I0fv/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fv/yNH8P8jR+//I0fw/yNH8P8jR/D/I0fw/yNH + 8P8jRu//I0fw/yNH7/8jR+//I0bv/yNH8P8jR/D/I0fv/yNH7/8iRvL/H0T2/yFG7/8lQOT/KDLX/y4y + xv8sKcr/JR3e/yUd4v8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mHuD/Jh/g/yYf3/8mH9//Jh/g/yYe + 4P8mHuD/JyDg/yYf3/8lHuD/KCDg/1Fb5/8lHt//GA/d/2Np6v+jpfP/HRXf/yYe4P8mH+D/HRXe/0RH + 5f/T2fn////////////n7/v/p6/N/8rIwf/Dx8X/Skna/x4V4v8mH+D/JiDg/xoR4P9FSdr/xMfF/8zL + wv+fpsz/h5Dv/yIc4P8lHuD/Jh/g/yYf4P8kHOD/KSXf/6ywyP/IyMP/y8rC/4yVzv8cFeD/Q0Dl/11g + 6P8cFN7/Ixvf/zAs4f9+gO3/wMj3//////++y/b/Mzbi/yIc3/8dGt//GxPe/yIa3/8mHuD/Jh7g/yYe + 4P8lHeD/JB3g/yYd4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yQd4P8mHuD/HhXf/xoW3v8aFt7/Ixvf/ycg + 4P8mH+D/Jh7g/yYf3/8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh7g/yYd3/8mHN//JyHl/yAj + vf8VE3X/IRu+/ycf5v8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8lHd//JSPh/yQ96/8kP+z/JTHm/yYg + 4P8mHeD/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mHt//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe + 3/8nH+T/IhzJ/wwKdf9BRY//sbOw/+Df3//v8O//6enp/+zs7P/t7e3/7u7u/+/v7//u7u7/7e3t/+rq + 6v/q6ur/6urq/+rp6v/p6un/6unq/+7t6v/a4Ov/NVnv/x5C8P8jR/CoI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAJiHAACYhwAAmIcAAJiHAACYgwAAmIMAAJiDAACUgwAAmIcAANDPHAB8X + vAAeF7wAHRa7ABwWugAeF7wAIRq9AB8YvQAgGb0AJh/AACYfvgAjSPAAI0jwACNH7wAjRu8AI0bvACNH + 7wAjR+8AI0bvHSNH78AjR/D/I0fw/yNG7/8jR+//I0fv/yNG7/8jRu//I0fw/yNH8P8jRu//I0bv/yNG + 7/8jR+//I0fv/yNH7/8jRu//I0fv/yNG8P8jR/D/I0fv/yNH7/8jRvD/I0fw/yNG7/8jR/D/I0fw/yNH + 8P8jRu//I0bv/yNH8P8jR+//I0fv/yNG7/8jR+//I0fv/yJH8f8fRff/JUjm/z9Ws/9baJX/b3GL/3x7 + if+HiJH/goSK/2Bljf8tLNH/JB3j/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mH9//Ixnf/zk84/87Q+P/Ihjf/yQb4P9EU+b/MjLi/yIZ3/8qJOD/c4Ds/yYi4P8lHd//Jh7g/yYf + 4P8eFt//Li7h/21x6/+Hhe7/S1Ho/4KGzv/OzsL/zc3C/2Vq1f8cFeL/JR7g/yYf4P8lHeD/HRrh/46U + zv/OzcL/ysrD/5iq4/8nJ+P/JBvf/yYf4P8mHuD/JR3g/yAc4f+Lk87/zczC/8jIw/+usMj/JCHf/0A9 + 5P9bXuj/HRTf/yYf4P8kHeD/Fw7d/x8c3/9rcer/4ej7/7a/9f8rKOH/HxXf/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mHuD/Jh7g/yYf4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8lHt//Jh7g/yYf4P8mH+D/Jh/f/yYe4P8mHuD/Jh/g/yYf4P8mH+D/JR7f/ycf + 5f8fGrP/FBNz/yEcv/8nH+b/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/JiDg/yYd + 3/8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yYe + 4P8mH+D/Jx/l/yAatf8UFHj/FBg6/wICAf9KSkv/7ezt//38/f/t7O3/3t7e/9jY2P/W1tb/2tra/+Pj + 4//09PT/9/f3//Hx8f/u7e3/6urq/+np6v/r6un/7u7q/1x47v8XPfD/I0fw4SNH8BQjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACYhwAAmIcAAJiHAACYhwAAmIMAAJiDAACYgwAAlIMAAJiHAADQz + xwAfF7wAHhe8AB0WuwAcFroAHhe8ACEavQAfGL0AIBm9ACYfwAAmH74AI0jwACNI8AAjR+8AI0bvACNG + 7wAjR+8AI0fvKCNG79QjR+//I0fv/yNG7/8jR+//I0bv/yNH8P8jR/D/I0bv/yNG8P8jR/D/I0fw/yNG + 7/8jR/D/I0fw/yNG7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH8P8jR+//I0fv/yNG7/8jR+//I0fv/yNG + 7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0jw/yFF9P8hRPH/OlK4/2txhv+Zl5b/sbC1/7i4 + wv+4ucX/urrI/76+zP+eoJf/QEKr/yIa5v8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYe + 4P8mH+D/Jh7g/yUc4P8rKOD/X27p/yQd4P8eE9//QEXk/zk64/8hF97/JSDg/0dR5v8kG+D/JR7f/yYe + 4P8mH+D/Jh/g/yMa4P8aEt7/Fw/e/xUL4P9nbdX/y83C/8zMwv+GjtD/Hhbh/yYe4P8mHt//Jh/f/x0V + 4v9JTdr/xsnE/8zKwf+vt8//LSzi/yMa3/8mH+D/Jh7f/yYe4P8cFeH/amzU/8zNwv/HxsP/vcHF/zo+ + 2/89OeX/XF/p/x0U3/8mH9//Jh/g/yYf4P8kG+D/GBDe/zc54v/Hz/j/vsj3/yYk4P8iGd//Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mHt//Jh/g/yYe4P8mHuD/Jh7f/yYe + 3/8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mHuD/Jh7g/yUe + 3/8nH+b/Hxuz/xUUeP8iHcf/Jx/l/yYe4P8mH+D/Jh7f/yYe4P8mH+D/Jh7f/yYe3/8lHt//JR/g/yYe + 3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/ycf5v8eGan/EhJh/wABBP8AAAD/AAAA/09QUP+Dg4T/TEtM/yIiI/8QEBD/DAwM/xgY + GP8yMjL/Y2Nj/42Njv/AwMD/4uHi//X09f/v7+//6urq//bz6f98kez/Fjzw/yNH7/ojR+9EI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAmIcAAJiHAACYhwAAmIcAAJiDAACYgwAAmIMAAJSDAACYh + wAA0M8cAHxe8AB4XvAAdFrsAHBa6AB4XvAAhGr0AHxi9ACAZvQAmH8AAJh++ACNI8AAjSPAAI0fvACNG + 7wAjRu8AI0fvOSNH7+MjR/D/I0fw/yNG7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jRu//I0fv/yNH + 8P8jR/D/I0fw/yNH7/8jRvD/I0fw/yNH7/8jR+//I0fv/yNH7/8jR/D/I0bv/yNH7/8jR+//I0bv/yNG + 7/8jR/D/I0fw/yNG7/8jR+//I0fv/yNG7/8jSO//I0nx/x9B9v8nPt7/UVuR/42Mif+zs7v/vL3N/7i5 + yf+3uMf/t7jH/7a4x/+7vMz/kpSU/zc5t/8iGuT/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh7g/yYf4P8mH+D/HxXf/1de5/9yfez/Ihvf/xwX3/8cFt7/HRje/11m6f9IUeb/Ihjf/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8gGOH/SlDZ/8jJw//My8P/p6rK/yQi4P8kHOD/Jh/f/yYf + 4P8kHN//IR3h/5WczP/OzcL/xcfE/01P2v8eFuH/Jh/g/yYf4P8mH+D/IBfh/0VL2//CxcT/xsXD/8vM + w/9cW9X/MC7l/0xQ5v8fFt//Jh7g/yYf4P8mH+D/Jh7g/yYf4P8eFN7/Hx3f/6659P+dp/H/HBbf/yUd + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/f/yYf4P8mH+D/Jh/g/yYe + 3/8mHt//Jh/g/yUf3/8lHt//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8mH+D/Jx/m/x8btP8VFHf/Ih3I/ycf5P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH9//Jh/g/yUe + 3/8mH+D/Jh/g/yYe4P8mHd//Jh7f/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe3/8nH+b/Gxii/w8SSP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/BgYG/ykpKv93dnf/z8/P/+zs7P/08ur/lqvs/xpA8P8jRu//I0fvbCNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AJiHAACYhwAAmIcAAJiHAACYgwAAmIMAAJiDAACUg + wAAmIcAANDPHAB8XvAAeF7wAHRa7ABwWugAeF7wAIRq9AB8YvQAgGb0AJh/AACYfvgAjSPAAI0jwACNH + 7wAjRu8AI0bvOSNH7+YjR+//I0fw/yNH8P8jR+//I0bv/yNH7/8jR+//I0fv/yNH7/8jR+//I0bw/yRH + 7/8kR+//I0bw/yNG7/8jR+//I0fw/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fw/yNH7/8jRu//I0bv/yNG + 8P8jRu//I0fw/yNH8P8jR+//I0fw/yNH7/8jSfD/I0Xv/yAz8f8vN8f/ZWeA/6Ggnf+7vMv/ubrK/7a3 + xv+4ucn/ubrJ/7i5yP+3uMj/trfA/25yhv8oJdb/JR3i/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yUd4P8fF9//bnns/6az8/+Hje//g4nu/7O79f+Kl+//JR/f/yUc + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Ihnh/zcz3f+8v8b/y8nD/7i+x/85O93/IBfg/yYf + 4P8mH9//Jh/f/xwT4f9QVNn/x8rD/8/Owf+Wnc3/Ih3g/yQd4P8mH+D/Jh/g/yQb4P8uLN7/s7bI/8fH + w//NzMP/eoHQ/y0u5P9HVOX/Ihjf/yYe4P8mH+D/Jh/g/yYf3/8mH9//Jh/g/yAX3/8kIuD/ws33/11i + 6P8dFN//Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/Jh/g/yYf4P8lH+D/JR7f/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh7f/yYf3/8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/ycf5f8eGq//FRR3/yIdyP8mH+T/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/JR7f/yYf + 4P8mH+D/Jh7f/yYc3/8jHOD/ISLi/yYj4f8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh7f/yYe + 3/8mHt//Jh/g/yYf4P8mH+H/Jx/i/xsXm/8PEkP/AAEA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/2trbP/s7Oz/8/Lr/6y76v8dQu//Ikbv/yNH + 8JAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACYhwAAmIcAAJiHAACYhwAAmIMAAJiDAACYg + wAAlIMAAJiHAADQzxwAfF7wAHhe8AB0WuwAcFroAHhe8ACEavQAfGL0AIBm9ACYfwAAmH74AI0jwACNI + 8AAjR+8AI0bvNSNG7+YjR+//I0fv/yNH7/8jR+//I0fv/yNG7/8jR+//I0fv/yNH7/8jRu//I0fv/yNH + 8P8jR+//I0fw/yNH8P8jRvD/I0fw/yNG7/8jR/D/I0fv/yNH7/8jR/D/I0fv/yNG7/8jR+//I0fv/yNH + 7/8jR/D/I0fv/yNH7/8jR+//I0fw/yNJ8f8jRu7/JDfq/yIh6f83N7X/dXd+/62tsf+8vc3/t7jH/7a3 + xv+5usr/pKWw/4SEif+ur7z/vL3O/6Kjof9JS53/IRvl/yYf4f8mH+D/Jh/g/yYe4P8mHt//Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe3/8mH+D/JB3f/xwT3v9EROT/g4nt/5Sh8P9pbOr/Ix3f/yMb + 4P8mH+D/Jh7g/yYf3/8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yQa4P8pKOD/oqzL/8zKwv/KzMP/UlLY/xwU + 4f8mH9//Jh7g/yYf4P8kHOD/Ix/h/56ky//NzML/yMrE/0tP2v8fFuH/Jh/g/yYf4P8lHeD/Ih7h/5CY + zv/MysL/ycnD/5mey/8yMuP/YHfq/yQc3/8mHuD/Jh/g/yYf4P8mH9//JR/f/yUe4P8mH+D/GA7e/2ds + 6v+iq/L/HRXe/yUd3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/f/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYe4P8mHuD/Jh7f/yYf4P8mHt//Jh7g/yYe + 3/8mH+D/Jh7f/yYf4P8nH+b/Hhqx/xUTdf8iHML/Jh/l/yYe4P8mH+D/Jh/g/yYe4P8mHt//JR/g/yUf + 3/8mH+D/Jh7f/yYf4P8gKOT/Mkrr/0Jk8/8kPev/JR3f/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf + 3/8mH+D/Jh/g/yYf4P8mHuD/Jh/h/yYf3v8aF5b/EBNO/wABAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/xQUFP/j4+P/7e3t//Hw6v/Gy+r/IUHu/yFG + 8P8jR/CzI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAmIcAAJiHAACYhwAAmIcAAJiDAACYg + wAAmIMAAJSDAACYhwAA0M8cAHxe8AB4XvAAdFrsAHBa6AB4XvAAhGr0AHxi9ACAZvQAmH8AAJh++ACNI + 8AAjSPAAI0fvGyNH79ojR+//I0fw/yNH7/8jR/D/I0fv/yNG7/8jR+//I0bv/yNG7/8jR/D/I0bv/yNH + 7/8jR/D/I0fv/yNG7/8jR+//I0bv/yNH8P8jRu//I0fw/yNH8P8jR+//I0fw/yNH8P8jRu//I0bv/yNG + 8P8jR+//I0fv/yNH7/8jR+//I0nw/yNG7/8lOOn/JCTj/yEX5v8+Pqr/gIJ//7S1vP+6u8z/trfG/7a4 + xv+2t8b/u7zM/4eIjv9cXFn/mZqk/72+zP9/goj/LS3H/yQb5P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf3/8mH+D/IBff/xkQ3v8YEt7/GxLe/yUd + 4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHeD/Hhri/46Tzv/PzsL/zc3D/3J7 + 0/8dFeL/Jh7f/yYe4P8mHuD/Jh/g/xsT4f9UWNj/yMrE/8/Owv+NlM3/IBvh/yUd4P8mH+D/Jh7g/xwV + 4v9ydNP/zs7C/8jIw/+ytcb/PEXg/52r8/8rJ+H/JBvg/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yIY + 3/83OOP/qbrz/yko4P8kG+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7f/yYe3/8mH+D/JR/g/yUe3/8mH+D/Jh7g/yYf + 4P8mH+D/Jh7f/yYe3/8mH+D/Jx/m/yAbtP8UE3P/IRu9/ycf5v8mH+D/Jh7g/yYf4P8mH9//Jh/g/yUe + 4P8mH+D/Jh3g/yYk4v8cOOv/OV7y/4yd9/+Pofj/LkHq/yIa3/8mHt//Jh/g/yYe4P8mH9//Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4f8mH+H/GheZ/w8TTv8AAQD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8EBAT/TU1O/2BgYP8QEBD/AAAA/wAAAP8MDAz/w8PE//Dw8P/w7+r/0NDn/yNA + 6P8hRvH/I0fvySNG7wQjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AJiHAACYhwAAmIcAAJiHAACYg + wAAmIMAAJiDAACUgwAAmIcAANDPHAB8XvAAeF7wAHRa7ABwWugAeF7wAIRq9AB8YvQAgGb0AJh/AACYf + vgAjSPAAI0jwBSNH764jR+//I0fv/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0fw/yNG8P8jR/D/I0fw/yNG + 7/8jRu//I0fv/yNG7/8jR/D/I0fv/yNG8P8jRvD/I0fv/yNH8P8jR/D/I0fw/yNG7/8jR/D/I0fv/yNH + 7/8jRvD/I0bv/yNH8P8jSfH/I0Xu/yQ36f8mJeH/JRrh/yIb4/9DRaH/hYiD/7i4w/+5usv/trfG/7a4 + x/+2uMf/trfG/7q7y/+fn6r/eXl8/62uvP+zs7j/XWGP/yQe3/8lHeH/Jh7g/yYe4P8mHt//Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH9//JR7f/yYe3/8mHt//Jh/f/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe3/8mH+D/Jh/g/x0V4v90d9P/zc/D/8zM + w/+Vmc3/Hxnh/yUe4P8mH+D/Jh7f/yYe4P8jG9//IyLg/6Oqyv/NzML/xMfE/0VI2/8fFuH/Jh/g/yYe + 4P8gF+H/TlTZ/8XIxP/HxsP/wMPE/1Ja2v/AzPn/Z27q/xUM3v8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8XDd3/U1bm/7S+9f8iIN//JRzg/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mHuD/Jh/g/yYe + 3/8lH9//Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mH9//Jh7g/yYe4P8mHuD/Jh7g/yYf + 4P8lHt//Jh/g/yYe3/8mHt//Jh7g/ycf5f8fG7P/FBJy/yAbvP8nIOb/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHt//Jh7f/yYc3/8eM+j/Ol/z/42c9/+dpvf/jJ33/zI55v8iGt//Jh/g/yYf4P8mH+D/Jh/f/yYf + 4P8kG9//Ixrf/yYf4P8mH+D/Jh/g/yYf4P8mH+D/JyDm/xsXof8PEkz/AAEA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wcHB/9FRUX/sLCw//T09P/5+fn/wsLC/zMzNf8AAAD/AAAA/3d3eP/19PX/8fDs/9fb + 6v8sUOn/IETx/yNH780jR+8FI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACYhwAAmIcAAJiHAACYh + wAAmIMAAJiDAACYgwAAlIMAAJiHAADQzxwAfF7wAHhe8AB0WuwAcFroAHhe8ACEavQAfGL0AIBm9ACYf + wAAmH74AI0v0ACNL9EkjRu//I0bv/yNH7/8jR/D/I0fw/yNG7/8jR+//I0fv/yNH7/8jR/D/I0fw/yNH + 7/8jR+//I0fv/yNH8P8jR+//I0bw/yNG8P8jRu//I0fv/yNH7/8jR+//I0fw/yNH8P8jR/D/I0fv/yNH + 7/8jR+//I0jw/yNJ8P8jRe7/JTbn/yYl4f8mHN7/JRvh/yIc5P9FSJ7/jY+H/7m5xv+4ucr/trfG/7a4 + x/+2t8f/trfH/7a3x/+2t8b/uLnI/7m6yf+8vc3/pael/0pIov8hGOf/Jh7g/yYf4P8lH9//JR/g/yYe + 3/8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/f/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/JR7f/yYf4P8fF+H/VFzY/8rL + w//LysP/rbPI/yos4P8jGuD/Jh/f/yUf3/8lHt//Jh/f/xsU4f9eZNf/yszD/8/Owv+Mk83/IBvh/yUd + 4P8mHuD/Ixvg/zEw3v+3ucb/x8bD/83Mwv9hadL/o6v2/+Xp+/89P+P/Fgze/x8X3/8iGd//IRjf/xwU + 3v8WD93/Mzbi/8rV+P+GjO//GxLe/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/JR7g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh7f/yYf4P8lH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/JR/f/yYe4P8mH+D/Jh/g/yYe4P8nH+X/IBu3/xMSb/8eGrT/JyDo/yYe4P8mH+D/Jh/g/yYe + 3/8mHuD/Jh/f/yYe4P8iGd7/N0vr/4yd9/+apPf/l6L3/4ia9/8vNOT/Ixrf/yYf4P8mH+D/Jh7f/yYf + 4P8kG9//ODvk/zo+5P8iGt//Jh/g/yYe4P8mH+D/Jh/g/ycg5v8dGan/DxBX/wAAAf8AAAD/AAAA/wAA + AP8AAAD/AAAA/ycnKP+5ubr/9fX1//b29v/r6+v/6urq//f29//j4uP/Xl5e/wAAAP+2trj//v3+//b1 + 8v/Jztn/L1Lj/x9D8v8jR+/MI0fvBSNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAmIcAAJiHAACYh + wAAmIcAAJiDAACYgwAAmIMAAJSDAACYhwAA0M8cAHxe8Bh4XvBsdFrsuHBa6OB4XvEshGr1VHxi9TSAZ + vTcmH8AmJh++DiomvwAhRvGUI0nz/yNI8f8jSPH/I0jx/yNH8P8jR/D/I0fw/yNH8P8jR/D/I0fv/yNG + 7/8jR/D/I0fw/yNH7/8jR+//I0fw/yNG7/8jR+//I0fv/yNG7/8jR+//I0fw/yNH7/8jR+//I0fw/yNH + 8P8jSfH/I0nw/yRD7v8kNOf/JiTh/yYc3/8mHd//JBzh/yEc5f9ER57/jZCJ/7q7yP+4ucn/trfG/7a3 + x/+2t8b/trfG/7a3xv+2t8b/trfG/7a3xv+2t8b/urvM/6eop/9MS6D/IRnm/yYe4P8mH+D/Jh/g/yYf + 4P8mH9//Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 3/8lHt//JR/g/yYf4P8mHuD/Jh/g/yYe4P8mHt//Jh7g/yYf4P8mH+D/Jh/g/yYe3/8mHuD/IBjh/z89 + 3P/ExcT/yMfD/7/ExP9CQNv/Hhbh/yYf3/8lH9//JR/f/yYf4P8jG+D/JiXg/6qvyf/Ny8L/w8fD/0RI + 2/8gFuH/Jh7g/yQd4P8kIOD/mKDM/8zLwv/MzMP/gonO/05W6P/8////5en8/3V77f83NuP/Li/h/zM0 + 4v9ISOX/g47u/+bt/P/H0vf/Jibf/yMa3/8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yUe3/8lHt//JR/g/yUe3/8mH+D/Jh7g/yYf + 4P8mH+D/Jh7g/yYf4P8mH9//Jh/g/yYf4P8mHuD/Jx/l/yIcv/8UE3L/Hhmq/ycf5f8mH+D/Jh/g/yYf + 4P8mH9//Jh7g/yYf4P8mH+D/Hxfe/1Ve6v+fq/j/lKD3/5mj9/+AlPb/KjDk/yQc3/8mH+D/Jh/g/yYe + 4P8mH+D/IBfe/1dd6f94iPH/Ih7f/yUe3/8mH+D/Jh/g/yYe4P8nH+X/IRy4/w8OXf8AAAD/AAAA/wAA + AP8AAAD/AAAA/xAQEP/CwsP//fz8/+vr6//p6en/6urq/+np6f/p6en/8fHx//T09P9oaGn/R0dI/8jI + yP9/fnv/foOO/ztd7v8fQ/H/I0fvzCNH7wUjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AJiHAACYh + wAAmIcAAJiHAACYgwAomIMAmJiDAPiUgwGEmIcCONDPHrUhO0cJVX9jcYWrd9WNs3vxjbd7/XGTb/1Nc + 1/9ARc37JiDA6yUfwM0qJr+1JjHN7h8u1P8eMdr/HTfh/x075f8ePur/IEPv/yFF8f8iR/H/I0ny/yNJ + 8v8jSPH/I0fw/yNH8P8jR+//I0fw/yNH7/8jRu//I0fw/yNH7/8jRu//I0bv/yNG7/8jR/D/I0jw/yNJ + 8f8jSfD/JEHs/yUy5v8mIuH/Jhzf/yYd3/8mH+D/JB3h/yEc5f9ESJ7/jY+J/7m6yP+4ucn/trfG/7a3 + xv+2t8f/trjH/7a3x/+2t8b/trfG/7a3xv+2t8b/trfG/7i5yf+ys7j/YWaN/yUg3f8lHeD/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh7f/yYf4P8mH9//Jh7g/yYe4P8lHt//Jh/g/yYe + 4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yIa + 4P8uLd7/qrPI/8vJw//OzcL/XmPW/xsV4f8mH9//Jh7g/yYf4P8mH+D/Jh/g/xsU4v9mbdX/zM3C/8/O + wv+GjM7/HRjh/yYd4P8mHuD/HRbh/3p70f/OzsL/ycnD/6uuyf8cHN7/fITt//7/////////7PP9/9bg + +v/i6vz///////////+8xvX/MDPh/x4V3v8mH+D/Jh7g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYe4P8mHuD/JR7f/yYf4P8mHuD/Jh/g/yYe + 4P8mHt//Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/ycf5P8iHMX/FBN1/x0Zn/8nH+P/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/f/yQc3/8lJOH/a4Hz/5ql+P+Yo/f/gpb2/yow5P8jG9//Jh/g/yYf + 4P8mH9//Jh7g/yAX3/9RVuj/l6f3/zs95P8hGN//Jh/g/yYe4P8mH+D/Jx/l/yMczP8SEmX/AwQF/wAA + AP8AAAD/AAAA/wAAAP96env/+vr6/+rq6v/q6en/6erq/+np6f/q6ur/6erq/+np6f/v7+//6Ojo/yEh + If8EBAT/AAAA/4uNnP8yUOj/IEXz/yNH780jR+8FI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACYh + wAAmIcAAJiHAbSYhwK0mIcDJJiDA6yYgwP0iHL7/NTPH/5Gf9f+dqfv/nqv8/6Ct/f+ir/3/oq/+/6Ct + /f+irv7/mab4/zY0x/8fGL3/d4Ho/4OO7P90euP/Ymja/1BY1P9FStH/O0DQ/zE70P8oN9P/ITLW/x4z + 3P8eOeT/HTvn/x0/6/8fRPD/IUbx/yNJ8v8jSPL/JEjx/yNH8P8kR+//JEfv/yNH7/8jSfD/I0nx/yNG + 7/8lO+v/JSvk/yYg4P8lHN7/JR7f/yYf4P8mHuD/JR3h/yEc5P9ER5//jY+J/7m6yP+4ucn/trfG/7a3 + xv+2t8f/trfH/7a3xv+2t8b/trfH/7a3x/+2t8b/trfG/7a3x/+2t8f/urvJ/4GDiP8sK8v/JBvi/yYe + 4P8mHuD/Jh/g/yYf4P8mH+D/JR7f/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYe3/8mH+D/Jh/g/yYf + 4P8mH+D/JR/g/yYe4P8mH+D/Jh/g/yUf4P8mHuD/Jh/g/yYe4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8kHOD/Ih/g/5edzP/OzML/zMzC/36G0f8dFuH/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8jGuD/Kirg/62y + yP/MysL/vsLF/zw+3P8hGOH/Jh/g/x8W4f9SWNj/x8nD/8jHw/+8wcb/Oz7c/xYO3/9aYej/vML2/+Xt + /P/5/v7/5/D8/8PJ9/9wd+r/IyDg/x4W3/8nH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/JR7f/yUf + 4P8mH+D/Jh7f/yYe3/8mH9//Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mHuD/JR/g/yYf4P8mH9//Jh/g/yYe + 4P8mHt//Jh7f/yUe3/8mH+D/Jh7g/yYe3/8mH+D/Jh/g/yYf4P8mH+P/JB3P/xQSeP8aFpL/Jh/i/yYf + 4f8lH9//JR7g/yYe4P8mHuD/Jh/g/yUe3/8mH+D/Ihjf/yg+6v+Fl/f/mqT3/5Oi9/82POX/IRje/yYf + 4P8mH+D/Jh/g/yYe4P8gF97/UVXo/5+t+f9gauz/Hxfe/yYe3/8mH+D/JR/f/yYe4/8lHtr/Fxd3/wUI + C/8AAAD/AAAA/wAAAP8jIyP/4eHh//Dw8P/p6en/6urp/+rq6v/q6ur/6urq/+rq6v/q6er/7u7u/9vb + 2/8ZGRr/AAAA/xQUD/+srL3/JkLe/yFH9P8jRu/HI0fvAyNH7wAjR+8AI0fvACNH7wAjR+8AJR7gACQd + 4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAmIMAAJiDAACYgwMcmIMD/JiHA/yYgwP8mIMD/Ixy+/y4sxf+MmfP/laD3/4CN7v92gen/cHfn/25z + 5f9weef/fYnr/4mY8P85N8j/HBW5/3eB5/+ms/7/oa79/6Gv/f+hrv3/oq78/5un+P+Pm/L/hZDt/3yD + 6P9tcuD/W2ba/0tY1/8/R9X/MT7U/yQ31v8eM9z/Hjnj/x4/6v8iR/H/JEny/yNJ8P8jR+//JD7s/yUx + 5v8lJeH/Jh3f/yYc3/8lHt//Jh/f/yYf3/8mH9//Jh7h/yIb5/9ERqL/jY+I/7q6x/+4ucn/trfG/7a4 + xv+2t8b/trfG/7a4x/+3uMb/trfH/7a3x/+2t8f/trfG/7a3x/+2t8f/trjG/7u8zP+XmZf/Oj2v/x8W + 6/8mHuH/Jh/g/yYf4P8mH+H/JR7j/yMe5v8jHeb/JB7l/yQe4/8mHuH/Jh7g/yYe4P8mH9//Jh7f/yYe + 4P8mHuD/Jh7g/yUf3/8mHuD/Jh/g/yYf3/8mH+D/Jh7g/yYe4P8mH+D/JR/f/yYe4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh7g/xwU4v9+gNH/zc7C/8zLwv+fo8r/IR7h/yUd4P8mHt//Jh7f/yYf4P8mH9//Jh/f/xoT + 4v9tdNP/zc3C/8/Pwv+CidD/Hhji/yUe4P8iGuD/Nzfc/77Axf/HxsP/ysvD/1pb1/8bEuL/HBLe/x4b + 3/81OOL/R0jl/zc64/8fHd//GRHe/yMb4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe3/8mHt//Jh7g/yUe + 3/8lHt//Jh/g/yYe3/8mH+D/Jh7g/yYe3/8mH9//JR/f/yYe4P8mH+D/Jh/g/yUe4P8mH9//Jh/g/yUf + 3/8mH+D/Jh/g/yYf3/8lHt//Jh7g/yYf4P8mH+D/Jh/g/yUe4P8mH+D/Jh/i/yQe2P8VFH//FxSF/yUf + 3f8mH+H/Jh/g/yYe4P8mH+D/Jh/g/yUf3/8mH+D/Jh/g/yYc3/8eJeT/bYX1/5uk9/+bpvj/aXTu/yAZ + 3v8lHeD/Jh7f/yYe4P8mHuD/IBjf/05T6P+cqfj/hJH0/ygk4P8kHN//Jh7g/yYe4P8mH+H/Jx/l/xsZ + l/8FBxb/AAAA/wAAAP8AAAD/iomK//n5+f/q6en/6urq/+rp6v/q6ur/6urq/+np6f/p6en/6urp//b2 + 9v+cnZ7/AAAA/wAAAP8vLin/rrPQ/x451v8iR/P/I0jvqyNH7wAjR+8AI0fvACUd4AAhGd8AHhXeACUe + 4AAkHeAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJiDAACYgwAAmIMDGJiHA/yYfwP8fGL3/Gxa9/x0Xvf8bFrz/OUHL/0NLzv86Qcn/Oz/H/0E+ + yf9PSc3/T03N/y4yyP8qKcn/JyLG/yMcxP8yMsz/UVnY/11m2/9mb93/cXjj/3d+5/97hen/go/t/4uZ + 8v+bqPr/oa79/6Gu/f+gq/v/mqT3/4uW7/+Aiuv/bnXh/1Rd1/88RNH/JTDQ/yQ64v8lNOj/Jibj/yYe + 4P8mHN//Jh3f/yUe3/8mHuD/JR7f/yYe4P8mH+D/Jh/g/yIb5v88Pqz/h4qF/7m6x/+4usn/trfG/7a3 + xv+2t8b/trfH/7a3x/+2t8b/t7jH/7e3x/+2t8b/trfG/7a3xv+2t8f/trfH/7a3x/+5usr/ra6z/11g + hv8pKcr/Ihrl/yId6v8iHuv/Ix7o/ykg2f8vIsv/MCLJ/y8iy/8qIdn/JB7m/yMe6v8iH+v/Ih7q/yMe + 5f8lHuH/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHt//Jh7f/yUe + 4P8mH+D/Jh/g/yYe4P8eFuH/YGnW/8vMw//JyMP/s7rH/zM03v8iGOD/Jh/g/yYe4P8mH+D/Jh7f/yYf + 4P8iGeD/MDHe/7K4x//LysP/vsLF/zw+3P8gGOD/JB3g/ygj4P+fp8v/y8nC/83Mwv92gdL/HRfi/yYe + 4P8lHd//IBff/x0U3v8fF97/JR3g/yYf4P8mHt//Jh7f/yYf4P8mHuD/JR7f/yUe3/8mHuD/JR7g/yYe + 4P8lH9//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8lHt//JR7f/yUe3/8lH+D/JR/f/yYf4P8mH+D/Jh7f/yUf + 3/8mHuD/Jh7f/yUe3/8mH9//Jh/f/yYe4P8mH+D/Jh7g/yYf3/8mHuD/Jh/g/yYe4f8mHtz/GBaK/xYU + fP8kHtH/Jh7j/yYf4P8mH+D/Jh/g/yYf4P8lHt//Jh/f/yYf4P8lHN//ICDh/2+C8/+bpfj/laD3/56r + +f9XX+r/IRrf/yUe3/8mH+D/Jh7g/yEY3/9DSeX/laP3/5il+P9ESOb/Hxfe/yYf3/8mHuD/JR7f/ycf + 5v8hHL//DA00/wAAAP8AAAD/Njc4/+jn6P/t7e3/6enp/+np6f/p6en/6urq/+rp6f/q6un/6unq/+vr + 6//z8/T/T09P/wAAAP8AAAD/V1ZQ/5Weyv8YNtb/I0n0/yQ/7KEgEt0AIhjfAB4W3gAlHeAAIRnfAB4V + 3gAlHuAAJB3gACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJRzgACYhwAAmIcAAJiHAiCUgwPYkHr3/REPK/2512v+HjOL/oqLr/6qs7v+vtO//ub3z/8PG + 9v/Nzvr/2dr+/9/f/v90euz/GhHc/yUe3/8mH9//Ixve/yAY2f8gGNf/IBjT/yAYzv8gGMv/Ih/I/ycm + x/8uLcf/OTnK/05V0/9vd+L/gpDs/5Kf9f+dqvv/n6z8/6Kv/v+irf3/nKj4/0pPz/8gGLv/Jh3S/yYd + 4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yIa5v8yNb7/fH9//7e3wv+5usr/trfH/7a3 + x/+2t8f/trfG/7a3x/+2t8f/trfG/7a3x/+2t8f/trfH/7a3x/+2t8f/trfH/7a3xv+2t8b/trfH/7q7 + zP+lpqv/f4KM/1ZanP85LbD/SCSC/1UnWP9dKT//Yik1/2IpM/9iKTX/Xig//1cmTv9QJ23/RCWP/zUh + tf8uIsz/JB/k/yIe6v8iHuz/Ih7p/yQe4v8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/f/yYe + 4P8mHuD/Jh/g/yYf4P8mH9//IBjh/0VG2v/JycP/xsXD/8jKxP9MTNn/HhXh/yYf4P8lH9//Jh/g/yYe + 4P8mH+D/Jh/g/xsU4f92fNL/zc7C/87Nwv9/htD/HRjh/yUe4P8dGOL/f4LR/87Owv/LysP/m5/M/yAZ + 4f8lHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yUe + 4P8mHt//Jh/g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/JR7g/yUe3/8mH+D/Jh/g/yUe3/8mH+D/Jh/g/yYe + 4P8mHt//Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/f/yYe3/8mH+H/Jx/i/xsX + mv8TEnP/IhzC/ycf5v8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7f/x8c4P9acPD/n6r4/5ql + +P+KmPX/WWHr/yIc4P8lHuD/Jh/g/yYf4P8hGN//REnm/5Wi9/+cp/j/anTu/yAY3v8mH+D/Jh/g/yYe + 3/8mH+P/JR7Y/xYXcP8BAwf/AAAA/5+foP/5+fn/6enp/+np6f/q6er/6unq/+nq6f/q6ur/6unq/+np + 6f/19PT/wsLD/wkJCf8AAAD/AAAA/42Nhf+AiMX/FDTe/yNK8/8kLubyIBLdfCIY3wkeFt4AJR3gACEZ + 3wAeFd4AJR7gACQd4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJRzgACUc4AAmIcAAJiHAACYhwAQgGc48Sk7hj8vP+cbc2//12Nj+/9jY/v/X1/7/1dX9/9XU + /f/T0/z/0dH8/8/P+//b2/3/lZby/xwU3/8mHuH/Jh/g/yYf4P8mHuH/Jh/h/yYe4f8mH+L/Jh/i/yUd + 4f8kHN//Ixvc/yEZ2P8gGNP/IBnN/ykozP83Ncv/RUvO/1Zf1v9sdOH/fIfq/5Wj9f9ncd7/Hxm6/yYf + z/8mH+H/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yQd4v8nJNj/ZmmE/6+vtf+6u8v/trfH/7a3 + x/+2t8b/trfG/7a3xv+2t8b/trfH/7a4xv+2uMb/t7fG/7a3x/+2t8f/trfG/7a3xv+4ucj/u7zM/7q8 + y/+0t8b/r7PB/6Kmrf97fXb/aT0w/2glFv9pKRr/aCgc/2coH/9nKB//Zygf/2gpHf9pKRr/aSka/2ko + Gf9oKSL/YSk0/1gmTP9OJ3P/QCSX/zUiuv8pINz/Ih7r/yIe7P8kHuT/JR7f/yYe3/8mH+D/Jh7g/yYe + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yIZ4f80Md3/tbvH/8nHxP/NzcL/aHHV/x0V4f8mH9//Jh/f/yYe + 4P8mH+D/Jh/g/yYf4P8hGOH/Nzfe/7q+xv/LysP/uL3G/zU33f8iGeH/HxXh/1xh1//Jy8P/ycjD/7S4 + x/8vMN7/Ixrg/yYe4P8mH+D/Jh/f/yYe4P8mHuD/Jh7f/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf + 4P8mHt//Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yUe + 4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/ycg + 5v8eGav/ExFv/x4Zr/8nH+b/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/JR/f/yYe3/8kHt//KETt/01u + 8/9IaPP/Kjfm/yEY3/8mHuD/Jh/g/yYe3/8mHuD/IRjf/0FG5v+Uoff/mKP4/4qX9f8sK+H/JBvg/yYf + 4P8lHuD/Jh/g/yYg5f8cGKX/GR1L/wAAAP9sbG3/9/f3/+nq6v/p6en/6unq/+rp6v/q6er/6enp/+np + 6f/v7+//8fHx/0VFRv8AAAD/AAAA/wMDAv+pqqn/UFqz/xk96v8iR/D/KC3k/0JA5f8vL+LCHhbeUSUd + 4AchGd8AHhXeACUe4AAkHeAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJRzgACUc4AAlHOAAJiHAACYhwAAmIcAAIBnOAEpO4QDY2v4J0ND7Ls/P+1/Pz/ucz8/7yM/P + +/HPz/v/0dD7/9XU/P/S0vz/2tn9/6is9f8hH9//JBzf/yYf3/8lHt//Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/h/yYf4f8mH+H/Jh/i/yYe4f8kG+D/Ihrb/yAZ0/8gGM//HxjL/yUizf81MtX/Mi7W/yQe + 1f8mH9//Jh/h/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8iG+b/Rkmg/5ydmv+7vM3/trfG/7a3 + xv+2t8b/trfH/7a3x/+2t8b/trfH/7a3x/+3uMf/trjH/7a3xv+4ucn/uLnJ/7e4x/+7vMz/tLfH/5uf + qf+BgIX/dWpr/21aWf9nSUX/ZjYy/2UoI/9mKCT/ZSgk/2YpJP9lKCT/ZSgk/2YpJP9mKST/Zigk/2Uo + JP9lKCT/Zigi/2cpHv9oKRv/aSoa/2kpGP9nKSX/XShB/08mcP86I6j/KiDY/yIf7P8iHun/JR7h/yYf + 4P8mH9//Jh/g/yYf4P8mH9//Jh/g/yYf4P8kHOD/JSTh/56ny//Ny8L/zMzC/4uPzv8dFeH/JR7g/yYf + 3/8mH+D/Jh/g/yYf4P8mH+D/Jh7g/xwV4v98gdL/zM3C/87Owv92ftL/HBbh/yIZ4P87Ptz/wMLF/8jH + w//Ex8X/S0vZ/x0V4f8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYe + 4P8lH9//Jh/f/yUf4P8mH9//Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/f/yYe3/8mHt//Jh/g/yYe + 4P8nH+X/IRy+/xQSc/8bGJr/JyDj/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/f/yYe3/8mHt//Jh3f/yI9 + 7f8dRPD/HUPw/yIt5f8mHd//Jh/g/yYf4P8mHt//Jh/g/yMa3/83POP/kp/2/5ai9/+Zpvj/TlPo/x8X + 3v8mH+D/Jh/g/yYf3/8nH+X/HhjF/yYphP9SVl7/hISC//Tz8//q6ur/6unp/+rp6v/q6ur/6enp/+3t + 7f/5+fn/8PDw/29vcP8AAAD/AAAA/wAAAP8iIh//ubvI/yAuqP8hR/P/Hz/u/z1E5v+bqPj/i5n1/1Ja + 6f8hG97GIRnfVx4V3gAlHuAAJB3gACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJRzgACUc4AAlHOAAJRzgACYhwAAmIcAAJiHAACAZzgBKTuEA2Nr+ANDQ+wDPz/sAz8/7AM/P + +wvQ0Psoy8z7Vrq/+JOxt/bHwsX599va/f+ip/T/IR7f/yQc3/8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4f8mH+H/Jh7h/yYe4v8lHOH/Ihrf/yMb + 4P8mH+L/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8kHOP/KijQ/3R3hf+3t8L/t7jI/7a3 + xv+2t8f/trfH/7a3x/+2t8b/trfH/7a3x/+2t8b/trfG/7a3xv+6u8v/srLA/6qruP+6vMv/mp+p/3dw + cv9lRUH/Yy4p/2MnI/9kJB//ZSQf/2UlIf9lKCT/ZSgk/2UoJP9lKCT/ZSgk/2YoJP9lKCT/ZSgk/2Yo + JP9lKCT/ZSgk/2UpJP9lKCT/Zikk/2YoJP9mKST/Zygh/2goHf9pKBn/aSkc/2AoPP9NJ3L/NyO1/yUf + 4/8iHuz/JB7k/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/x0X4v+KjM7/zs/C/8vKw/+orsj/Jyjg/yQb + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe3/8gF+H/PD7c/77Cxv/LysP/trzH/zQ13v8gFuH/KSbg/6Or + yv/LycL/zs3C/2Zr1f8bFeL/Jh/g/yYe3/8mH+D/Jh/g/yUf3/8mHuD/Jh/g/yYe4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/Jh7g/yYe3/8lHt//JR/g/yYe4P8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYe4P8mHt//Jh7g/yYf + 4P8mHuD/Jh/i/yQd0v8VE3v/FxWH/yYf3v8mH+H/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHt//Jh7g/yYd + 3/8kO+v/I0nw/yNJ8P8lMOX/Jhzf/yYe4P8mH+D/Jh/g/yYf4P8jGt//Nzzj/5Gf9v+Voff/mqX4/3SA + 8f8hHN7/Jh3g/yYf4P8mH+D/Jh/h/yYf4v8ODI//fISy//v79f/q6ur/6enp/+vr6//u7u7/9PT0//n5 + +f/n5+j/pKSl/z0+Pv8AAAD/AAAA/wAAAP8AAAD/enp1/36CtP8RJbf/JEz4/x856/9AROX/kqH3/6Gt + +f+eq/n/cHzv/y8w4v8eFd6jJR7gDyQd4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAmIcAAJiHAACYhwAAgGc4ASk7hANja/gDQ0PsAz8/7AM/P + +wDPz/sA0ND7AMvM+wC6v/gAdYrsCoWM7yxbaeleRlDmmSEX38gmHuD3Jhze/yYc3/8mHN//Jhzf/yYc + 3/8mHd//Jh/g/yYe4P8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/IRrm/0JFpv+cnZv/u7vL/7a3 + xv+2t8b/trfH/7a3xv+2t8b/trfG/7a3x/+2t8b/trfG/7a3xv+7vMz/qKm1/319gP+bnaj/h4uQ/2VM + Sf9jKiX/ZSQf/2UmIf9lJyP/Zigk/2UoJP9lKCT/ZSgk/2YoJP9mKST/ZSgk/2UoJP9lKCT/Zikk/2Yo + JP9mKCT/ZSgk/2YoJP9lKST/Zikk/2UpJP9lKCT/Zigk/2YoJP9mKST/Zikk/2YpI/9nKR7/aSkZ/2cp + I/9ZJ1D/QSWW/ysg1P8iH+z/Ix7n/yYe4P8mH9//Jh7g/yYf4P8eFeH/anLV/8zNwv/Hx8P/u8HG/zw7 + 3P8gF+H/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH9//JR7g/x0Y4f+FjND/zMzC/83Nwv91fdL/HBXi/x4Z + 4f+HjM//zs7C/8zLw/+Fjc//Hhbh/yYe4P8mH+D/Jh/g/yYf3/8mH+D/Jh7g/yYe4P8mHuD/Jh7g/yYf + 4P8mHuD/Jh/g/yUe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 3/8mH+D/Jh7g/yUf4P8mH+D/Jh7f/yUe3/8mHuD/JR7f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mHt//Jh7f/yYf4f8mH97/GRaN/xQSd/8jHcn/Jx/k/yYe3/8mH+D/Jh/g/yYf4P8mH+D/JR/g/yUf + 3/8mHd//JTjo/yNJ8P8jSfD/JTLm/yYc3v8mH+D/Jh/g/yYe4P8mH+D/Ixrf/zc85P+Rn/b/lqH3/5ah + 9/+ToPb/OTvk/yEY3v8mH+D/Jh/g/yYe3/8nH+X/HRbB/xsdfP/Hy9n/9vXv/+np6f/k5OT/3t3e/8LC + w/+FhYX/LS0u/wAAAP8AAAD/AAAA/wAAAP8AAAD/MjEu/8HF0f8lKIr/HTrb/yNL9f8kNej/JBrf/zU2 + 4/9ia+z/jJr1/6Kv+v+Rn/b/T1bp/yIb37skHeAYJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJiHAACYhwAAmIcAAIBnOAEpO4QDY2v4A0ND7AM/P + +wDPz/sAz8/7ANDQ+wDLzPsAur/4AHWK7ACFjO8AW2npAEZQ5gAjGd4IJDzrjyQ66f8lMOb/JSvk/yUt + 5v8kMef/JDPm/yYn4/8mHuD/Jh7f/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 3/8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH9//JR3g/yQd3/9hZYz/srK5/7i5 + yf+2t8b/trfG/7a3xv+2t8b/trfG/7a3xv+2t8b/trfG/7a3xv+7vMz/p6ey/3Rzdf+OkJj/fn2B/2M7 + N/9lJB//ZiYi/2YoJP9lKCT/Zigk/2YoJP9mKCT/Zigk/2YoJP9mKCT/Zikk/2YoJP9mKST/Zigk/2Yp + JP9mKCT/ZSgk/2UoJP9mKCT/Zigk/2YpJP9lKCT/ZSgk/2YoJP9mKCT/Zigk/2YpJP9lKCT/ZSkk/2Yp + JP9mKCL/aCkc/2kpG/9gKTr/SiZ9/zEhxv8jH+r/Ix7p/yYe4P8mH+D/Hhfh/09S2f/Ly8P/xcXE/8zN + wv9TV9f/HRXh/yYf3/8mHuD/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8fFuH/Q0Xb/8HFxf/KycP/tbrH/zEy + 3/8aD+H/YmjW/8rMw//KysP/qavJ/yUh4P8lHOD/Jh/g/yYf3/8mH9//Jh/g/yYf3/8mHuD/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/JR7g/yUe4P8mHuD/Jh7g/yYf4P8mHuD/Jh7g/yYe + 4P8mH9//Jh/g/yYe4P8mH9//Jh7f/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYe + 4P8mH+D/Jh7f/yYe3/8mH+H/JyDl/x0Zp/8SEW7/Hxqy/ycg5v8mH+D/Jh/g/yYf4P8lHuD/Jh/g/yYf + 3/8mH+D/Jhzf/yU26P8jSfD/I0nw/yRB7f8mIOD/Jh3f/yYe4P8mH+D/Jh/g/yMa3/83POT/kZ/2/5ah + 9/+UoPf/mqb4/2dw7f8fGN7/Jh7g/yYf4P8mH+D/Jh/g/ycg5P8JApX/Q0eQ/+3v7P/39vP/nZ6f/yws + LP8JCQr/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/FBQT/8XGwv9gY6P/Dhia/yRK9f8jSPD/JSnk/yYc + 3/8iGd//Hxnf/y4t4f9OVOj/doLw/5mp+P9mce3/JB7gvCUc4A8lHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACYhwAAmIcAAJiHAACAZzgBKTuEA2Nr+ANDQ + +wDPz/sAz8/7AM/P+wDQ0PsAy8z7ALq/+AB1iuwAhYzvAFtp6QBGUOYAIxneACNJ8K8jSfD/I0nw/yNJ + 8P8jSvD/JD3q/yYo4/8mH+D/Jh7g/yYe3/8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYe4P8mH+D/Jh7f/yYf + 4P8mH+D/Jh7g/yYe3/8mH9//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yUc4/8qKc7/fH+M/7m6 + xv+3uMb/trfG/7a3x/+2t8b/trfG/7a3xv+2t8b/trfH/7a3xv+6u8v/rK26/3Z2eP+RlJ3/fHp8/2M0 + MP9lJCD/Zigk/2YoJf9lKCT/ZSgk/2YoJP9mKST/Zikk/2YpJP9mKST/ZSgk/2UoJP9mKST/Zikk/2Uo + JP9lKCT/ZSgk/2UoJP9mKCT/Zigk/2UoJP9mKCT/Zigk/2UoJP9mKCT/Zikk/2YoJP9lKCT/Zigk/2Yo + JP9lKCT/Zigk/2UpJP9lKCP/Zygf/2kpGv9kKS3/TiZv/zMivf8jHuj/Ix7p/yAY4f87N9z/vsLG/8fG + w//NzcL/dX3S/xwU4v8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh3g/x4b4f+NlM3/zczC/8zN + wv9tcdX/Fw7i/0FC2//FxsP/ycjD/7q/xv87Pd3/IRfh/yYf4P8mH+D/Jh/g/yUf3/8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8lHt//Jh/g/yYf4P8mH+D/Jh7g/yYe + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh7g/yYe + 4P8mH9//Jh/g/yYf4P8mH+D/Jh/g/ycf5P8iHMT/FBJz/xoWlf8nIOT/Jh7g/yYf4P8mH+D/Jh/g/yYe + 3/8mHt//Jh/g/yYc3v8lMeb/I0nw/yNH7/8jSfD/JTTn/yYc3v8mHt//Jh/g/yYe4P8jGt//Nzzk/5Gf + 9v+Woff/lKD3/5ai+P+Rnfb/Njbj/yEY3v8mH+D/Jh/g/yYe3/8kG+P/QUXc/xQYg/9dYqL/8vPw//z6 + 9/+dnZ7/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/EhIR/7q7tf+NksD/Cwt4/yE82f8kSvT/JEHs/yYg + 4P8mHuD/Jh7g/yYe4P8jG9//IBff/yAa3v9AQ+X/k6T3/z9A5f8hGN9HIRjfACEY3wAhGN8AIRjfACEY + 3wAhGN8AIRjfACEY3wAhGN8AIRjfACEY3wAhGN8AIRjfACEY3wAmIcAAJiHAACYhwAAgGc4ASk7hANja + /gDQ0PsAz8/7AM/P+wDPz/sA0ND7AMvM+wC6v/gAdYrsAIWM7wAiSvAAIkrwACJK8AYjR/DQI0fv/yNJ + 8P8kQu3/JSzk/yYd4P8mHd//Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe + 4P8mH+D/Jh/g/yYf3/8mH9//Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8jGub/Nja4/5GU + mP+7vMv/trfG/7a3x/+2t8b/trfG/7a3x/+2t8f/trfG/7a3x/+5usr/r7G+/3R1d/+QkJn/hYWL/2M4 + NP9lIx//Zigk/2YoJP9lKCT/ZSgk/2YpJP9mKCT/Zikk/2YpJP9mKCT/Zigk/2YoJP9lKCT/Zigk/2Uo + JP9mKCT/Zigk/2UoJP9mKCT/ZSgk/2UoJP9mKST/Zigk/2YpJP9mKCT/Zigk/2YpJP9lKCT/ZSgk/2Uo + JP9mKCT/ZSgk/2YoJP9mKCT/ZSgk/2YoJP9mKCT/Zygg/2koGv9mKSf/Uidk/zQiuv8hG+r/Jino/6Wt + yv/LysL/zMzD/5mdzP8fG+H/JR3i/yYf4v8mH+H/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8fFuH/Rkna/8TG + xP/KycP/sbXI/ysp3/8pJd//q7LI/8rJw//Jy8P/V1jY/xwV4v8mH+D/Jh7f/yUf3/8mH+D/Jh7g/yYe + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh7f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHt//Jh/g/yYe3/8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/f/yYf4P8mH+D/Jh/g/yYf4P8mH+H/JR/Z/xcVhP8WE37/JR7V/yYf4/8mH9//Jh/g/yYe + 4P8mH+D/Jh/g/yYf4P8mHN//JSvk/yNJ8P8jR+//I0jw/yNH7/8mKeP/Jhzf/yYf4P8mH+D/Ixrf/zc8 + 5P+Rn/b/laH3/5Sg9/+UoPf/mqb4/2977/8hG9//JR3g/yYf4P8mHuD/GhLe/7K3+v/M0uf/Ki+L/15k + qP/o6/P/5+bg/xoZF/8AAAD/AAAA/wAAAP8AAAD/Hx4b/7m6tf+Wmsb/Dgxz/xwtuv8kSvX/I0nw/yU0 + 6P8mHd//Jh7g/yYf4P8mHuD/Jh/g/yYf4P8lHuD/Hhbe/0pO6P85OeX7IxvfNSMb3wAjG98AIxvfACMb + 3wAjG98AIxvfACMb3wAjG98AIxvfACMb3wAjG98AIxvfACMb3wAjG98AJiHAACYhwAAmIcAAIBnOAEpO + 4QDY2v4A0ND7AM/P+wDPz/sAz8/7ANDQ+wDLzPsAur/4AHWK7AAmHN8AJhzfACYc3wAlMOctJEDt7CNJ + 8P8lOur/JiLh/yYc3/8mHuD/JR/g/yYe4P8mHt//Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/JR/f/yYe4P8mHuD/Jh/g/yYe3/8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/IBjn/0A8 + rf+dn57/urvL/7a3x/+2t8f/trfH/7a3xv+2t8b/trfG/7a3xv+3uMj/trfG/3t7ff+EhIn/lpqi/2RE + QP9lIx//Zigk/2YpJP9mKCT/ZSgk/2YoJP9lKCT/Zigk/2UoJP9mKCT/Zigk/2UoJP9lKCT/Zikk/2Yp + JP9mKCT/ZSgk/2UoJP9mKCT/Zikk/2UpJP9lKCT/Zikk/2UpJf9mKST/Zigj/2YoI/9mKCL/Zigi/2Yo + Iv9mKCP/ZSgj/2YoI/9mKCP/Zikk/2UoJP9lKCT/Zikk/2YoJf9mKCT/Zygh/2kpGv9mKSX/USZk/y0d + u/+Sl9H/zc3D/8nIw/+utsj/LjDf/yEY1v8lHtX/Jh/f/ycf4v8nIOf/Jx/l/yYf4/8mHuL/JR3i/yId + 4v+Wnc3/zMzD/8vNwv9rcNT/FBDj/4yQzv/OzsH/zczC/3R+0/8cFuL/Jh7g/yYe3/8lH9//Jh7f/yYf + 4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/JR7f/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/f/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/ycg5f8dGab/ExFv/yAauf8nH+b/Jh/f/yYf + 4P8mH+D/JR/f/yYe4P8mH+D/Jh3f/yYl4v8jRe7/I0fv/yNH7/8jSPD/I0Lt/yYi4f8mHd//Jh/g/yMa + 3/83PeT/kp/2/5ah9/+UoPf/lKD3/5Wg9/+Ypfj/Ulvp/x4W3v8mHuD/Ixvf/yMl3//N0/j//////9jc + 5f8VGUX/LDJv/5Sayf8xND7/AAAA/wAAAP8DBAD/P0A7/6mrtf9vc6z/EA95/xosuP8kSfT/I0fw/yNH + 7/UmKOPVJh3f/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYe3/8hGN//JBzguCYf4AcmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYhwAAmIcAAJiHAACAZ + zgBKTuEA2Nr+ANDQ+wDPz/sAz8/7AM/P+wDQ0PsAJhzfACYc3wAmHN8AJhzfACYc3wAmHN8dJhvezSU5 + 6v8kPev/Jh7f/yYd4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yUf4P8mHuD/Jh/g/yYe4P8mHuD/Jh7g/yYf4P8lH9//JR7f/yYf4P8mH+D/Jh/g/yAa + 5v9ISqP/paal/7m6yv+2t8f/trfG/7a3x/+2t8f/trfG/7a3x/+2t8b/u7zM/5SUnP9ubW7/qKu4/3Rr + bP9jJyL/ZSYj/2YoJP9mKCT/Zigk/2UoJP9mKST/Zikk/2YoJP9mKCT/ZSgk/2UoJP9lKCT/ZSgk/2Yp + JP9mKCT/ZSgk/2UoJP9lKCT/Zikk/2YoJP9lKST/Zigj/2coHv9pJxj/aiYY/2knHf9nJyH/aCgh/2go + If9nJyH/aCcd/2gmHP9oJhz/aCYa/2olFf9qJRX/aSUX/2gmGf9pJhn/aCYa/2gnG/9oKBz/aCgb/2oo + FP9gHx3/kHeE/8zPy//Ix8P/yMnI/z4+qf8OC37/FhSA/xkWi/8dGJ7/Hxmz/yIcwv8kHc3/JR7X/yUe + 3P8eFt7/TlPX/8bJxf/KycP/r7TI/yYj4f9kaNn/y83D/8vLw/+Zncz/IRrm/yYe5v8nH+T/Jh/j/yYf + 4f8mH+H/Jh/h/yYe4P8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYf + 4P8mHuD/Jh/g/yUe3/8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8lHt//JR7f/yYf4P8mH+D/Jh7f/yYe + 3/8mHt//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8nH+T/Ix3M/xQTdv8aFpf/Jx/k/yYf + 4f8mH+D/Jh/g/yYf3/8mH+D/JR/f/yUd3/8mH+D/JD/s/yNI8P8jR+//I0bv/yNJ8P8kO+n/Jh7f/yYc + 3/8kGt7/MjDh/5Gc9v+Voff/lKD3/5Sg9/+UoPf/l6L4/5Cf9v88QOX/IRnf/x0U3v9ISOT/8/j9//// + ///m5uX/HR0Y/wAAAP8FCT3/GRp2/wsNKf8JChz/HBxO/1JWmv8uMpT/ERyd/x861f8kSfT/I0fx/yNI + 7/8jP+vEJiThECYe4FsmH+DDJh7g9SYf3/4mH+D/Jh7f/yYf4P8mHt//Jh7g4CYe3zAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmIcAAJiHAACYh + wAAgGc4ASk7hANja/gDQ0PsAz8/7AM/P+wDPz/sAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gmyYd + 3/8jQO3/IjDm/yUa3/8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf3/8lHt//Jh7g/yYe4P8mH+D/JR7f/yUe3/8mH+D/Jh7g/yYf + 4P8iHOX/TFKf/6mpq/+5usr/trfH/7a4x/+2t8f/trfH/7a3xv+2t8f/t7jI/7S1xP90dHb/kJCY/6Gm + sf9lRUH/ZSQf/2YoJP9mKST/Zigk/2YpJP9mKST/ZSgk/2YpJP9mKST/ZSgk/2YpJP9mKCT/Zigk/2Yo + JP9mKCT/Zikk/2YoJP9mKCT/ZSgk/2YpJP9mKSX/Zycf/2gnHv9fLEH/UDRu/0Q4kP9AO6L/PD2v/zw+ + sv86PLL/NTiy/zo3ov88OJ3/Pzqe/0I6lv9FOIf/RTiJ/0c1ff9MMW7/SzFw/08vY/9RK1j/UipY/1oq + Sf9dLUf/WyQz/39ZXf/Cx8z/wcPG/87QyP9VW5X/Cwhz/xUSdv8UEXP/FBBv/xMPbf8UEHP/FRB3/xUR + fP8WEoD/FxKI/xUViv+Xm7P/zczH/8zNx/9aXrX/MTal/8bIxP/LysX/sbXE/ygru/8dF73/Ix3G/yQe + 0P8lHtr/Jh/e/ycf4v8nH+b/Jx/m/ycf5P8mH+L/Jh/h/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe + 4P8lH9//Jh/g/yYf4P8mHuD/Jh7g/yYe3/8mH+D/Jh/g/yYe4P8mHuD/JR/f/yUf4P8mH+D/Jh/g/yYe + 3/8mH+D/Jh7g/yUe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mH+D/Jh/h/ycf4v8aF5b/FRN5/yUf + 0v8nH+T/Jh7g/yYf4P8mH+D/Jh/g/yUf3/8lH+D/Jhzf/yUy5/8jSfH/I0fw/yNG7/8jRu//I0jw/yQ5 + 6v8lMuf/Ijbo/yw96f+MmPb/lqL3/5Sg9/+UoPf/lKD3/5Sf9/+apvj/gY7z/yso4f8WDd7/gIft//// + ////////rq6w/wICA/8AAAD/AAEA/x8fif8lINf/IS7D/yA2z/8aMs7/Hj3i/yNI8v8kSPT/I0bv/yNH + 8P8jR+//JSnjeSYk4QAmHuAAJh/gAyYf4CImH+BBJR7fUCUe32QmH+BqJh7fYiYf3x4mHt8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJiHAACYh + wAAmIcAAIBnOAEpO4QDY2v4A0ND7ACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gCiYe + 4NElHN//KEHs/zJN7f8nI+D/Ihnf/yYf4P8mH+D/Jh/g/yYf3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mHt//Jh7g/yYf4P8mH+D/JR/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe + 4P8mHuD/Ix3j/1JYmf+srbH/ubrJ/7a4x/+2t8b/trfG/7a3xv+2t8b/trfG/7m6yv+np7P/a2tr/6ut + uv+MjJP/YjIt/2UlIP9mKST/Zigk/2YpJP9mKST/Zikk/2UoJP9mKST/Zikk/2YoJP9mKST/Zigk/2Yo + JP9mKST/Zigk/2YpJP9lKCT/Zigk/2YoJP9lKST/Zygg/2MrNP8+PKb/JkXq/yFI9/8fSP3/H0j7/yBJ + +P8eR/j/I035/09v+/9kgf//ZIH//22J//9wi///cIv//3CM//9sh///ZoD7/2aA+/9ngPf/Ynfz/1hw + 8/84Vuz/IkLp/yVE5v8rSuP/O1/p/0Jk6P9Pb+b/N1Ta/x42zP8gNsj/HzPB/x0xvf8cLbf/Gyqu/xsm + pP8ZIZn/FxyN/xUXgv8MC3n/SUyT/87NxP/W0sT/qKmx/yMme/+oqrj/0c/H/8PEw/83Oob/DAlz/xUT + e/8VE33/FxSD/xkWj/8bGJv/HRmq/yAbuP8jHcr/JR7Y/yYf3/8nH+T/Jx/m/yYf4/8mH+H/Jh/g/yYf + 4P8mH+D/JR7f/yYf4P8mH+D/Jh7g/yYf4P8mH9//Jh/g/yYf4P8mH+D/Jh7g/yYf3/8mHt//Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh7g/yYe4P8nH+X/IhzD/xMS + cv8cGKf/JyDm/yYf4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYc3/8lJeL/I0bu/yNH8P8jRu//I0bv/yNH + 8P8jSfD/I0jw/yJI8P8kSvD/eIz2/5mk9/+UoPf/lKD3/5Sg9/+UoPf/mKP4/46c9v8wL+L/HRnf/8HH + 9///////8vLy/zk6Ov8AAAD/AAAA/wEBAP8fKIb/JT/7/yRJ9v8jSvX/I0n0/yNI8v8jRvD/I0bv/yNH + 7/8jSfD/JDrp8CYb3iQmG94AJh7gACYf4AAmH+AAJh/gACUe3wAlHt8AJh/gACYe3wAmH98AJh7fACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYh + wAAmIcAAJiHAACAZzgBKTuEA2Nr+ACUe3wAlHt8AJR7fACUe3wAlHt8AJR7fACUe3wAlHt8AJR7fACUe + 3y4mH+DvIhnf/zY75P+On/f/eYTx/zEx4v8gF9//JBzg/yYe4P8mH+D/Jh/g/yYe3/8lHt//Jh7g/yYf + 4P8mH+D/Jh7f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/JR7g/yMd4v9XXZX/srO5/7i5yP+2t8b/trfH/7a3x/+2t8f/trfG/7a3xv+6u8r/lpaf/3Jy + c/+ytcT/fHR3/2IoI/9lJyL/ZSkk/2UoJP9mKST/Zigk/2YpJP9mKST/Zikk/2YpJP9lKCT/ZSgk/2Yo + JP9lKCT/Zikk/2YoJP9lKCT/ZSgk/2YoJP9mKCT/ZSgk/2kmGP9EOZP/HUr//yJH8/8jR/D/I0bv/yNH + 8P8jR/D/HULv/2B69P+fqff/nKX4/5ul9/+ZpPf/maP3/5mj9/+Zo/f/mqT4/5ql+P+apfj/mqX5/5um + +f+bpvr/kZ/5/0Vl9f8eRPP/IUbz/x5C8P8eQvH/HEDx/yFF8/8kSvX/JEn2/yRJ9v8kSvf/IUj4/x5E + 9f8dRPP/IEXw/yNG7f8jRuv/Hj7f/xw50/9Zctz/dozX/4ue1f87TK3/eYO4/8LFyf/Lzcr/W2CY/wkG + cf8UEHL/FBB0/xQQdP8TEHT/FBFz/xQRc/8UE3b/FRR7/xYVgv8aF5H/HBij/yAbt/8jHcz/JR7b/ycf + 4v8nIOb/Jx/k/yYf4v8mHuD/Jh/g/yYe4P8mHuD/Jh7g/yYf4P8mH9//Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf + 4f8aFpT/FRN5/yQe0v8mH+T/Jh/g/yYe3/8mHuD/Jh/f/yUe3/8mHuD/Jhze/yU66v8jSfH/I0bw/yNH + 7/8jR/D/I0fv/yNG7/8jRu//HULv/09s8v+apff/lZ/3/5Sg9/+UoPf/laD3/5uo+P9ha+3/GA/d/0VG + 5f/y+P3//////4uLjP8AAAD/AAAA/wAAAP8BAAD/HjKG/yRL/P8jR/D/I0fw/yNH8P8jR+//I0bv/yNG + 7/8jR/D/I0Xu/yUm4aImG94AJhveACYe4AAmH+AAJh/gACYf4AAlHt8AJR7fACYf4AAmHt8AJh/fACYe + 3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmIcAAJiHAACYhwAAgGc4AJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuBHJh/f/yYe4P8hGN7/RUnm/5Si9/+ToPb/Vl7p/ysn4f8gGN7/Ixrf/yQd4P8lHuD/JR7f/yYe + 3/8mH+D/Jh/g/yYe3/8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/f/yYf4P8mHuD/Jh7g/yYf + 4P8mH9//Jh/g/yUd4f8kHeD/XGGT/7Ozuf+4ucn/trfH/7a3x/+2t8b/trfG/7a3xv+2t8b/urvM/4yN + lP96en3/srbF/3JlZf9jJB//Zigk/2YpJP9lKST/Zikk/2YoJP9mKST/Zigk/2YpJP9mKST/Zigk/2Yo + JP9mKCT/Zigk/2UoJP9lKCT/Zikk/2YpJP9lKCT/ZSgk/2YoIv9pKB7/NUC6/x9J/P8jRu//I0fv/yNH + 7/8jRu//I0fw/x1C7/9qgfT/n6n3/5ei9/+Xovf/l6L3/5eh9/+Woff/lqH3/5ah9/+Woff/lqH3/5ah + 9/+Woff/lqH3/56n9/95jPb/H0Pv/yNG7/8jR/D/JEfw/yNH8P8jR/D/I0fv/yNG7/8jRvD/Ikbv/zFT + 8P9RbfP/Znz0/22E9f91ivf/d4z3/26H+P9ogfn/VG72/0lo9f9BY/T/OFrz/zBR7/86Xuv/SGrn/zZU + 2v8WLsT/GCy5/xklp/8YIJr/FhuN/xYWg/8WFH3/FRF2/xQPcv8UEHP/FBFy/xMRcf8UEnT/FRN6/xgV + h/8bF5v/Hhmv/yIcxv8lHtj/Jh/h/ycg5/8nH+T/Jh/i/yYe4P8mH+D/Jh7f/yYf4P8mH+D/Jh/f/yYe + 3/8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mHt//Jh/g/yYe + 3/8mHuT/JB3L/xQSdf8bGJv/Jh/k/yYf4f8mH+D/JR/f/yYf4P8mH9//Jh7g/yYc3/8lKOL/I0fv/yNI + 8P8jR+//I0fv/yNH8P8jR/D/I0bv/yFF7/8mSu//fpH2/5uk9/+UoPf/lKD3/5qm+P9+jfP/KSfg/xYL + 3f+Bie7//////+np6f8hISL/AAAA/wAAAP8AAAD/AAAA/xsrdP8kSvz/I0fw/yNH7/8jR+//I0bv/yNH + 8P8jR/D/I0nw/yQy5/cmHd8yJh3fACYd3wAmHd8AJh/gACYf4AAmH+AAJR7fACUe3wAmH+AAJh7fACYf + 3wAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJiHAACYhwAAmIcAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gXiYe3/8mH+D/Jh7g/x8X3v82N+P/fYny/6Gu+f+JlvT/W2Tr/zY35P8pJeH/Ih3f/x8X + 3v8gF97/IRjf/yIZ3/8iGt//Ixvf/yQc3/8kHOD/JBzg/yUd4P8lHeD/JR7g/yYe3/8mH9//Jh7g/yYe + 4P8mH+D/Jh7g/yYe4P8lHeH/JB3e/2NmkP+0tLz/t7jJ/7a3xv+2t8b/trfH/7a4x/+2t8f/trjH/7q7 + zP+IiI//goKI/66ywf9tWFf/ZCMf/2UoJP9mKST/Zigk/2YoJP9mKST/Zigk/2YpJP9mKCT/Zikk/2Uo + JP9lKCT/Zikk/2YpJP9lKST/ZSgk/2YoJP9mKCT/ZSgk/2UpJP9mKST/aScZ/zk2rv8dSf//I0jv/yNH + 8P8jR/D/I0fw/yNH8P8gRO//M1bx/3OI9f+JmPb/i5r2/4ua9v+Kmvb/jpz3/5Ge9/+Qnvf/kJ73/5Cd + 9/+Rnvf/kJ73/5Ce9/+Vovf/aYD1/x5D7/8jRu//I0fw/yRH8P8jR/D/I0fw/yNH8P8jR+//IETv/yxR + 8P+Kmvb/oKn4/56n+P+bpff/mqT3/5mk9/+bpff/nKb3/5ul9/+ZpPf/lqH3/5Sg+P+Mmvf/fI73/2uB + 9v9hePj/VnP7/0Rn+v81V/X/LE7y/yRI7/8cPeT/GzbU/xoxyf8aLbv/Giao/xkhmP8XGor/FxaB/xYT + ev8TEHL/Eg9u/xMQb/8UEXX/FhOB/xoWmP8fGq//IxzJ/yUf2v8nH+T/Jx/m/yYf4/8mH+H/Jh7g/yYf + 3/8mHt//Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh7f/yYe + 4P8mHuD/Jh7g/ycf5v8eGan/ExJv/yAat/8nIOn/Jh/g/yYf4P8mHuD/Jh/f/yYf4P8mH+D/Jhze/yQ3 + 6P8jSfD/I0bv/yNG7/8jR+//I0fv/yNG7/8jR/D/HkPv/zla8f+OnPf/m6T3/5ql+P+NmfT/Nzjj/x4T + 3v8iIuH/w8r4//////+HiIn/AAAA/wAAAP8AAAD/AAAA/wAAAP8YJ27/JEr9/yNH8P8jR+//I0fw/yNH + 8P8jR+//I0nw/yQ+6/8mH9+XJh3fACYd3wAmHd8AJh3fACYf4AAmH+AAJh/gACUe3wAlHt8AJh/gACYe + 3wAmH98AJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACUe3wAlHt8AJR7fACUe3wAlHt8AJR7fACUe3wAlHt8AJR7fACUe3wAlHt8AJR7fACUe + 3wAlHt8AJR7fACUe330mH+D/Jh/g/yYf4P8mH+D/IRjf/yUg3/9XX+r/kZ72/6Kv+f+Wo/f/gY/z/3B7 + 7/9kae3/VVzp/0pR5/8/RuX/ODfj/zMt4/8uK+H/Kyfg/ykm4P8mI+D/Ih/f/yIe3v8fF9//Hxbe/x8X + 3v8gGN//IBjf/yAY3/8gGN//IBfg/x4X4f9VW5X/srO5/7i5yP+2t8b/trfG/7a3x/+2t8b/trfH/7a3 + x/+6u8z/iYmQ/4qJkP+us8D/bFRT/2QkH/9lKCT/Zigk/2UpJP9lKCT/Zikk/2YoJP9mKCT/Zikk/2Yo + JP9mKCT/ZSgk/2YoJP9lKST/Zikk/2YoJP9lKCT/Zikk/2YpJP9mKST/Zikk/2goHP9WJ1f/KC7f/yA8 + 9v8jQe//I0Pu/yNF7v8jRu7/I0fv/yBF7/8fRe//J03w/y5V8f8tVPH/LFPx/zNZ8f85XPL/OVzy/zhc + 8v84XPL/OFzy/zhc8v84XPL/OVzx/ypQ8f8hR/D/I0nw/yNJ8P8jSfD/I0jw/yNI8P8jSPD/I0jw/yJG + 7/8kSPD/VG/z/4CS9v+Qnff/mqX3/5ul9/+Zo/f/mKL3/5ah9/+VoPf/laD3/5Wh9/+Woff/lqH3/5ij + 9/+bpfj/nab4/5ql9/+Xovf/lKD4/4iY9/94jfb/aoH3/1h1+f9Gafr/NFj5/yxQ9f8iSPL/HEDt/x07 + 3/8dNtH/GzHE/xsqsf8bI57/GB2P/xYXgf8VE3b/ExBu/xMQcv8XE4L/GhWZ/x8atP8jHM//Jh7c/ycf + 5f8mH+b/Jh7k/yYf4/8mHuH/Jh7g/yYf4P8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/f/yYe3/8mH+L/Jh/h/xoWlP8WFHr/IBu5/yYf4v8mH+L/Jh/g/yYe3/8mH+D/Jh/g/yYd + 3/8lIuH/I0Pt/yNI8P8jRu//I0bv/yNH8P8jR+//I0fw/yNH7/8dQu//Olrx/4GS9v+Kmvf/P1ft/x4s + 5v8cMej/RmXw//X7///t7Or/ISIi/wAAAP8AAAD/AAAA/wAAAP8AAAD/Dhxi/yVK/f8jR/D/I0fw/yNH + 7/8jR+//I0fw/yNF7/8lJuLeJh3fGCYd3wAmHd8AJh3fACYd3wAmH+AAJh/gACYf4AAlHt8AJR7fACYf + 4AAmHt8AJh/fACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+CaJh/g/yYe4P8mH+D/Jh/g/yYf4P8kHN//Hhbf/zIy4/9jbe3/jJn1/56r + +f+irvr/oq/6/5+t+f+dq/n/m6r5/5un+P+Wo/f/jZv2/4aV9P+AjvP/eojy/3OA8P9xfe//a2/u/2xv + 7v9ma+3/WmLr/1tj6/9aYer/Tlbp/01V6f9MVe3/YGif/6emrP+5usn/trfH/7a3x/+2t8f/trfG/7a3 + xv+2t8b/urvL/4+Pl/+DhIn/r7PB/21aWf9kIx7/ZSgk/2YoJP9mKST/Zigk/2YoJP9lKST/Zigk/2Yp + JP9mKCT/ZSgk/2UoJP9mKST/ZSgk/2UoJP9mKCT/ZSgk/2YoJP9lKCT/ZSgk/2YoJP9mKCT/aSga/1wm + Rf86Iqz/JCDn/yIi6v8kJuT/JSfi/yUp4/8lK+T/JCrj/yMr5f8iK+b/Iivm/yIr5v8hLOb/Hy/m/x8v + 5v8fL+b/Hy/m/x8w5v8fMOb/HzLn/x806P8iNun/JDjp/yQ46f8kOOj/JDjp/yQ86v8kPOr/JEDt/yND + 7v8jR+7/Ikjv/x1E8P8jSvD/N1ry/0xq8/9he/T/eIz1/4eW9v+Qnvf/maX3/5ul9/+ao/f/maP3/5ei + 9/+Woff/laD3/5Sf9/+UoPf/laD3/5Wg9/+Yovf/mqT3/5ym+P+apff/mKP3/5Sg9/+Glvb/dYn2/2Z9 + 9f9Sb/b/PV/3/y9U+P8mTfb/HkTy/x1C8P8ePuT/HjnT/x0zxf8dK6//GiOa/xYah/8WFnz/FhN5/xcT + hP8bFZr/IBmv/yIbxf8kHdX/Jh/d/ycf5f8nIOb/Jx/l/yce5P8nH+P/Jh/j/yYf4v8mHuH/Jh/h/yYe + 4P8mHuD/Jh7h/yYf4v8mH+P/Jx/m/yYf4f8fGrD/FBJ1/xQSdP8dGKH/Jh/g/yYf5P8mHuD/Jh7g/yYe + 4P8mH+D/Jhzf/yUs5P8jSPD/I0fw/yNG7/8jR/D/I0fv/yNG7/8jR+//I0fv/x5C7/8mS/H/K0/w/x5E + 7/8jSvD/Fj/v/4qk+f//////lpaX/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/w8cWP8mSvj/I0fx/yNH + 7/8jR+//I0jw/yNH8P8lLeX6JhzfTiYd3wAmHd8AJh3fACYd3wAmHd8AJh/gACYf4AAmH+AAJR7fACUe + 3wAmH+AAJh7fACYf3wAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAIBfeACAX3gAgF94AIBfeACAX3gAgF94AIBfeACAX3gAgF94AIBfeACAX + 3gAgF94AIBfeACAX3gAgF94AJh/gsCYf4P8mHuD/JBvg/yEZ3/8eF9//Hhbe/x0V3v8ZD97/HhXe/y8u + 4f9MT+j/WWTr/2Rw7f9lcO3/ZHDt/2Vy7f9xfe//cn7w/3yG8v+GkPT/jJn1/5Sj9/+hrfr/oq76/6Cs + +f+cqfn/nKn5/5uo+P+bqPj/mqj4/5mm+P+Zpvj/m6n//4GHuP+UlJn/urvM/7a3x/+2t8f/trfG/7a3 + xv+2t8b/trfG/7q7y/+XmKL/eHl7/7W4yP92bnD/YyYh/2UnIv9lKST/Zigk/2YoJP9lKCT/ZSkk/2Yo + JP9mKCT/Zigk/2YoJP9mKCT/Zikk/2YoJP9lKCT/Zigk/2YpJP9lKCT/ZSgk/2UpJP9mKST/Zigk/2Yo + JP9oKRv/aCkf/1UnXf83IrT/Jh7j/yIc6v8lHOL/Jh3f/yYc3/8mHN7/Jhzf/yYb3v8mHN//Jhzf/yYc + 3/8mHd//Jh3f/yYd3/8mHd//Jh3f/yYd3/8mHt//Jh3f/yYd3/8mHd//Jh7f/yYd3/8lHd//Jh7e/yYe + 3/8lIuH/JSfj/yUr5P8lMeb/Izfp/x866/8cPO3/HUHu/yBG8P8nTvH/OFzy/0dm8v9YdPT/boT1/4CQ + 9v+ImPb/kZ73/5qk9/+bpff/m6T3/5qk9/+Yovf/l6H3/5Wg9/+Un/f/lKD3/5Sg9/+VoPf/mKL3/5uk + 9/+cpvj/mqT3/5ij9/+Mm/f/fI/2/2yC9f9WcPT/PmD1/y9T9v8kS/j/HUT1/x5E8/8fQu//ID7f/x85 + z/8dMLn/Gyah/xgfjv8WGIL/GBWB/xYRiP8RDJj/GROn/yEbuv8jHMv/JB3T/yUf2P8mH9z/Jh/f/ycf + 5P8nH+b/Jx/m/ycf4v8mH93/JR7W/yAatv8ZFo3/FBN4/xUTe/8VE3r/ExJz/xsXmP8mHtz/Jh/l/yYf + 3/8mH+D/Jh/g/yYf3/8mHd//JDXo/yNJ8P8jR+//I0fv/yNH7/8jRu//I0fw/yNH7/8jR+//IUXv/yBF + 8P8jR/D/IETv/yNJ8P/O2/7/+Pf1/zMzNP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8QGkH/Jknt/yNH + 8v8jR+//I0fw/yNI8P8lMeb/JhzffCYc3wAmHd8AJh3fACYd3wAmHd8AJh3fACYf4AAmH+AAJh/gACUe + 3wAlHt8AJh/gACYe3wAmH98AJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACAX3gAgF94AIBfeACAX3gAgF94AIBfeACAX3gAgF94AIBfeACAX + 3gAgF94AIBfeACAX3gAgF94AIBfeAh8X378dFN7/HBbe/yYl4P83M+P/SEfm/1xk6f9xduv/eoDu/zM0 + 4v8hF9//Hxbf/x8Y3v8gGd7/IBne/yAZ3v8hGt//Ihvf/yIa3v8iGt//Ix7f/ysr4f82OOT/SEvn/2Zy + 7v+LmPX/m6j4/5qm+f+Zpfj/mKP4/5ii+P+Xovj/lqL4/5ij/f+Jktf/hIWL/7i5yP+2t8f/trfH/7q7 + y/+4ucn/trfH/7a3x/+6u8v/pqaz/29wcP+ys8L/kZSd/2I9OP9lIx7/Zigk/2YpJP9mKCT/ZSgk/2Uo + JP9mKCT/ZSgk/2UoJP9mKCT/ZSgk/2UoJP9mKCT/Zigk/2YoJP9lKCT/Zigk/2YoJP9lKST/Zikk/2Yo + JP9mKST/Zikl/2YpI/9pKRj/Zykg/1goVv87I6f/Jh/f/yIe7P8kHuX/Jh/g/yYf3/8mHt//Jh/g/yYf + 4P8mHuD/Jh7f/yYe3/8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh7g/yYe4P8mHt//Jh7f/yYe + 4P8mHuD/Jh3g/yYd3/8mHN//Jhze/yYd3/8mHd//JiLh/yYn4/8lK+T/IzHm/yA16f8cOez/HD7t/x5E + 7/8hSPD/KlHx/zlc8v9IZfL/VnP0/2mA9P98jfX/hpb2/46c9v+Yo/f/m6X3/5qk9/+apPf/maP3/5ii + 9/+Woff/laD3/5Wg9/+VoPf/l6H3/5qk9/+cpvj/mqT3/5ij+P+Mm/b/eo72/2Z99f9QbfP/Olry/ytQ + 9P8hSPb/HUT3/x9F9f8gRfL/IUHk/xo10P87TMD/ZG2q/zE4j/8UFX//ExB1/wwJd/8RDYH/GBOJ/xkW + kf8aF5r/Gxed/xsXnv8aF5f/GRaN/xcUgP8UEnX/FBJ1/xUTev8VE3v/FBN7/xQTev8TEnL/GheQ/yUe + 1v8nIOf/Jh7f/yYf4P8mH+D/Jh7g/yYe3/8kPOr/I0nw/yNG7/8jR+//I0fw/yNG8P8jRu//I0fv/yNH + 7/8jR+//I0fv/xo/7/9OcPP//f///7Szsf8BAAH/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/EBtD/yZK + 7v8jR/L/I0jw/yNI8P8lMeb/JhzfliYc3wAmHN8AJh3fACYd3wAmHd8AJh3fACYd3wAmH+AAJh/gACYf + 4AAlHt8AJR7fACYf4AAmHt8AJh/fACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AA9OeQAPTnkAD055AA9OeQAPTnkAD055AA9OeQAPTnkAD05 + 5AA9OeQAPTnkAD055AA9OeQAPTnkAD055AtDQ+XNZGzq/5GT8f+psPX/wsb5/9TU/P/V1/z/2dr8/9re + /f9OVeb/IBff/yYf4P8mHuD/Jh/g/yYf4P8mHt//Jh/g/yYe4P8mHuD/JR7g/yUe4P8kGt//Ihjf/x8W + 3v8eF97/MC7i/1Ja6f9xfPD/go7z/4eW9P+LmfX/j532/5Og9v+Zpvn/mqf4/3p9j/+rq7b/ubrK/7S1 + xP+UlJ3/q6u5/7i5yf+2t8b/t7jI/7O0w/9zdHX/m5ym/7i7y/95eXv/Yjcy/2UjHv9mKCP/Zikk/2Yo + JP9mKCT/Zigk/2UoJP9mKST/Zigk/2YoJP9mKST/Zikk/2YoJP9lKCT/Zigk/2YoJP9mKCT/Zikk/2Yp + JP9lKCT/Zikk/2YpJP9lKST/Zigl/2YoIv9pKBn/aSkb/1spR/9BJJT/KyDS/yIc6f8jG+X/JR3g/yYf + 4P8mH+D/Jh/g/yYe3/8mHt//Jh7f/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mHuD/Jh/g/yYf3/8mHt//Jh/g/yYe4P8mHuD/Jh/g/yYe4P8mHd//Jhzf/yYc3/8mHd//Jh7g/yYj + 4P8lKOP/JC7l/yIz6P8fOev/HDzt/xxB7v8dRfD/H0bw/yhO8f81WPH/QmDy/09s8/9he/T/dIf1/4KS + 9v+Kmvb/lKH3/5ul9/+apff/mqT3/5qk9/+Yovf/lqH3/5Sg9/+VoPf/l6L3/5uk9/+bpvf/mqX3/5ei + 9/+Hl/b/doj1/1958/9FZPP/M1Ty/yVM8/8QOvT/V3b6/+Tv//+dtfn/Gjzh/zNM0f9pdbn/QUud/xYa + hf8TEnX/Eg9t/xMPbf8TEHD/ExFz/xQSdv8UE3j/FRN7/xUTe/8VE3r/FRN7/xQTev8VE3v/FRJ6/xQS + dP8YFoj/JB7P/ycf5/8mHuD/Jh/g/yYf4P8mHd//JiLg/yNA7P8jSfD/I0fv/yNH8P8jRvD/I0bv/yNG + 7/8jR+//I0fw/yNG7/8XPO7/lq36//////9VVFX/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/xAY + N/8mSOj/I0n0/yNH8P8lMOb/Jh3flCYe4AUmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJR7fACUe3wAmH+AAJh7fACYf3wAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAx8v6AMfL+gDHy/oAx8v6AMfL+gDHy/oAx8v6AMfL + +gDHy/oAx8v6AMfL+gDHy/oAx8v6AMfL+gDHy/og0NL74dfZ/P/Z2vz/19f8/9PT/P/Q0Pv/0M/7/8/Q + +//W2Pz/aWzq/x0V3v8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mH9//Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/JR7f/yIa3/8fF9//IBnf/yUi4P8tLuH/MjTi/zg44/88POT/QkHm/1Ja8f9sdLH/k5SU/7y+ + zv+urr3/YmJg/4mKkP+6u8z/trfG/7a3xv+7vMz/k5Sb/3R0df+4ucj/srXE/3t7f/9jPjr/ZCUf/2Ul + IP9mJyP/Zigk/2YoJP9mKCT/Zikk/2YoJP9mKCT/Zigk/2UoJP9lKCT/ZSgk/2UoJP9mKST/Zigk/2Yo + JP9lKST/Zigk/2YoJP9mKCT/Zigk/2YpJP9mJyP/Zici/2UkIP9oJRf/aSQS/14nNf9ILIH/LSnI/x4Z + 4f8fF+H/JBvg/yYe3/8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7f/yYe4P8mH+D/Jh/g/yYe + 4P8mH9//Jh/g/yYf4P8mH9//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mHuD/Jh3f/yYc3/8mHd//Jh7f/yYi4f8mJ+P/JSzk/yQ15/8iOur/Hz7t/xxB7v8bQ/D/HUXw/x5F + 8P8jSfD/L1Pw/zta8f9JZvP/Wnb0/22C9f9+j/X/iJj2/5Kg9/+bpPf/mqT3/5ql9/+ao/f/l6L3/5Wg + 9/+Voff/maP3/5ul9/+bpff/mqX4/5Ge9/9/kfb/Y3r0/3SN9f/X4P7/r8P8/yBH8/85Yfn/0eH//563 + /P8hROn/IDrV/x4xu/8aJJ//GBmH/xUSd/8UEHT/FBF1/xQSef8UE3r/FRN7/xUTe/8VE3r/FRN7/xUS + e/8VEnv/FBN1/xYUgP8iHMX/Jx/n/yYf4P8mHuD/Jh/g/yYd3/8lI+H/JD/r/yNJ8P8jR/D/I0fw/yNH + 7/8jR+//I0fw/yNH8P8fQ+//Jkzv/9jk///d29b/DQ0O/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8PFSj/JUri/yRE8v8mK+T6JhzfeSYd3wAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmH+AAJh/gACUe3wAlHt8AJh/gACYe3wAmH98AJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gANLS+wDS0vsA0tL7ANLS+wDS0vsA0tL7ANLS + +wDS0vsA0tL7ANLS+wDS0vsA0tL7ANLS+wDS0vsA0tL7NdHR+/XPz/v/z8/7/8/P+//Pz/v/z8/6/87P + +//Oz/v/29z9/4WE7/8bFN7/Jh7g/yYe4P8mH+D/Jh/g/yUf3/8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/yYe3/8mHuD/Jh7g/yYe4P8lHN//Ixrf/yIZ3/8hGN7/IBje/x8V3v8YEd//XWnh/3+B + iP+ys7//tbbG/4SFiv+Xl6H/ubrK/7a3xv+2t8b/t7nI/7W2xv95eXz/fn6B/7i6yv+6vc3/jI+W/2hT + Uf9iMSz/Yygj/2QkH/9lJB//ZSQg/2UkIP9lJCD/ZSQg/2UlIP9lJSD/ZSUg/2YmIf9lJiH/ZiUh/2Yl + IP9lJSD/ZSUg/2UkIP9mJCD/ZSUg/2UkIP9kJB//ZCgk/2MtJ/9jNC//Y0A9/2pUUv92aGP/hX51/5CS + nf+Bh83/SU3b/yUg4P8cFOL/Ihrh/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh7f/yYe3/8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/f/yYe3/8mHt//Jh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh3f/yYc3v8mHN//Jh3f/yYi4P8mKOP/JS3l/yQ1 + 5/8jPev/IkHt/yBF7/8dRPD/HEPw/x1E8P8dQ/D/IUbw/yxQ8P85WfH/SGXy/1p18/9xhfX/gJH1/4yc + 9v+YpPj/m6X3/5ql9/+ZpPf/l6L3/5ag9/+Xoff/mqP3/5ei9/+rtPn/6uz+/93j/f95jfX/cYr1/9Xf + /f+Wrvn/HETy/xxD9f8eRfb/H0b1/yJD5/8gOtH/Hiyx/xkekv8WFH3/Ew9z/xMQdP8UEnj/FRN6/xUT + e/8VE3v/FRN7/xUTe/8TEnb/FRN7/yAbuf8mH+X/Jh/i/yYf3/8mH+D/Jhzf/yYg4P8lOOj/I0nw/yNI + 8P8jR/D/I0fv/yNG8P8jRu//Gj7v/1R19P//////hYSE/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/DhUq/yI23f8lIuXhJhzfUSYc3wAmHd8AJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmHt8AJh/fACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ADOz/oAzs/6AM7P+gDOz/oAzs/6AM7P + +gDOz/oAzs/6AM7P+gDOz/oAzs/6AM7P+gDOz/oAzs/6AM7P+jXPz/v1z8/7/8/P+//Pz/v/z8/7/8/P + +v/R0fv/19f8/+Xl//+MkPD/HBbe/yUe4P8mH+D/Jh/g/yYf4P8lH9//Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYe4P8mH+D/Jh7g/yYf3/8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8iGd//MC/h/4ya + /f+AhrX/j4+S/7u9zf+6u8v/uLrK/7a3xv+2t8b/trfH/7a3xv+5usr/r7C+/3N0dv9zc3X/oaKt/7q7 + y/+prbr/iImP/3dub/9vXl//aVBP/2VGQv9kPzv/Yzw4/2M2Mv9jNTD/YzUw/2MzL/9jMCv/Yy8q/2Ix + LP9jNDD/YzQw/2M0MP9jNjP/ZDw4/2VAPP9mSkf/a1lY/3Npaf9+eX3/jI6V/5ufqf+orbn/srXF/7q8 + zP+8vcr/wsPI/77By/+codL/Ymjb/zEx4v8iGuX/Jh7g/yYf3/8mHuD/Jh/g/yYe3/8mHuD/Jh7f/yUf + 3/8mH+D/Jh/g/yYf4P8mHuD/Jh7f/yYf4P8mH+D/Jh7g/yYf4P8lH9//Jh7f/yYf3/8lHt//Jh7g/yUe + 3/8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/JR/g/yUe4P8mH9//Jh7f/yYe4P8mHuD/Jhzf/yUc + 3v8mHN//Jh3f/yYj4f8lKeT/JTHm/yQ56v8kQOz/I0Xv/yJH8P8gRvD/HUTw/xxC8P8dQu//HULv/yNJ + 8P8xVPD/QF/y/1Zy8/9sgvX/gJH2/46c9/+Zo/j/nKX3/5ul9/+VoPf/oq34/+Xo/f/k5/3/oKn4/6ix + +P/u7/7/w8r7/3aJ9f9kfPP/R2by/zJU8f8iSPP/HUT2/x1F9v8gRO//Ij7c/x8xvP8ZIJf/FhR8/xQP + c/8UEHb/FRJ6/xUTev8UE3v/FRN7/xQSd/8TEnT/Hhqp/yYf4f8mH+T/Jh/g/yYf4P8mHeD/Jh3f/yUt + 5P8kQez/I0nw/yNJ8f8jR/D/I0bv/xY97v+ftv3/+/n0/zExMv8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/xIWKP82MtDKIBblKCYc3wAmHN8AJh3fACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh7fACYf3wAmHt8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AA0dH7ANHR+wDR0fsA0dH7ANHR + +wDR0fsA0dH7ANHR+wDR0fsA0dH7ANHR+wDR0fsA0dH7ANHR+wDR0fs0z8/79c/P+//Q0Pv/1dT8/9nZ + /P/Z2v3/y8/6/66z9f+Ei+//QEDk/yIa3/8mH+D/Jh/g/yUe3/8lHt//Jh/g/yYf4P8mHuD/Jh/g/yYf + 3/8mH9//Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8kHOD/Ihzf/3B7 + 7/+bp/v/kp3x/3Z4h/+pqbL/urzM/7a3x/+2t8b/trfH/7a3x/+2t8b/trfG/7q7y/+xssD/f36D/2Ni + Yf90dHX/k5Ob/7K0wv+2usr/rbG//6eruP+kqLP/mp2n/5ebpf+Qk5v/jpKa/4+Tmv+PkZn/iIaN/4aE + iv+Hh43/j5Ka/4+Smv+Pkpr/kJOc/5eapP+bnqn/pKm0/6ituf+wtMP/ubzM/7u8zf+9vs7/vr/Q/7y9 + zv+7vM3/uLrK/7Cxv/+oqLP/oqOk/5SVkv5vdITpSk2hwScj3scmHuH/Jh7g/yYe4P8mH+D/JR/g/yYe + 3/8lH9//Jh/g/yYf4P8mHuD/Jh7g/yYf3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8lH9//JR7f/yYf4P8mHuD/JR/f/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8lHt//Jh/f/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jhze/yYc3/8mHN//JR/g/yUl4v8lLOX/JTbo/yQ+7P8kRO7/I0jw/yNJ + 8P8hR/D/H0Xw/xxC7/8cQe//HEHv/yNI8P8zVPH/Q2Hx/1t18/93ivX/hZX2/6Cs+P/l5/7/5uj9/56o + +P+irfn/6ez9/8DH+/+WoPj/m6X3/5ql+P+Pnff/fY/2/2N89P9GZfL/MFPy/x9G9P8cRPb/IUXy/yI/ + 3P8dLbT/GBqJ/xQQdP8TEHT/FBJ5/xUTev8VE3v/FRN5/xQSdP8bF5n/JR7Y/ycf5v8mH+D/Jh/g/yYf + 4P8mHN//JiHg/yUw5v8kP+z/I0jv/x9G8P8rUvH/4O3//8PBvf8CAgP/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8hIiL/rK27hCAW5QAmHN8AJhzfACYd3wAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYe3wAmH98AJh7fACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAMvM+wDLzPsAy8z7AMvM + +wDLzPsAy8z7AMvM+wDLzPsAy8z7AMvM+wDLzPsAy8z7AMvM+wDLzPsAy8z7N9XV/PjZ2/3/1tf8/73D + +P+bn/P/aG/r/0FB5P8lJOD/GxTe/yEZ3v8nH+D/Jh/f/yYe4P8lHt//Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mH9//Jh/f/yYe3/8mHt//JR/g/yYe4P8lH9//JR7f/yYe4P8mHuD/Jh/f/yYe3/8lHt//Hhfe/1hi + 6/+ap/j/laD3/5ai/v+GjtD/fHt+/7Kzv/+6vMv/trfG/7a3xv+2t8b/trfG/7a3xv+2t8b/ubrK/7i5 + yf+amqT/ent+/3Bwcf+XmKL/u7zM/7m6yv+6u8v/urvL/7q7zP+6u8z/urzM/7q7zP+6vMz/urzM/7q8 + zf+6vc3/urzM/7q7zP+6u8v/urvM/7q8zP+7vMz/u7zN/76/z/++wND/vL7P/7u8zP+3uMj/rK27/6Wm + sf+XmKL/iYqQ/H19gONzc3W9ampqnmZnZm1mZmRCbmxhJmJoegQoJ9sJJR3iSSYe4KUmHuDsJh7g/yYf + 3/8mH+D/Jh/g/yYf4P8mHt//Jh7f/yUf3/8lH9//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yUe3/8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8lHt//JR7f/yYf + 3/8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yUe3/8mHd//Jhzf/yYc3/8mHt//JiTh/yUp + 4/8lNOf/JDzr/yND7f8jSPD/I0nw/yNJ8f8hRu//HkPv/x1C7/8cQe//HkPv/yZK8P9ObfP/0dv9/9bd + /f+Jmvb/qLL5/+3u/v+3v/n/k573/5Sf9/+VoPf/l6H3/5qk9/+bpff/mqX4/42b9v90iPX/T23y/ytO + 8f8bQvP/Ikn3/yNG6/8gNcb/GSCV/xQSeP8UD3P/FRJ4/xUTe/8UE3r/FBJ0/xcVhP8hHL//Jh/l/ycf + 5P8mH+D/Jh7g/yYe4P8mHN7/Jh/f/yUn4/8bKOX/WHDx//////9xcHD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/LCws/8HBvavGxsYAxsbGAMbGxgDGxsYAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmHt8AJh/fACYe3wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACJmvAAiZrwAIma + 8ACJmvAAiZrwAIma8ACJmvAAiZrwAIma8ACJmvAAiZrwAIma8ACJmvAAiZrwAIma8EejqvT/hYrw/1JW + 5/8yMuL/HRrf/xwU3v8fF9//JBzg/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yUf4P8lHuD/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8lH+D/Jh/f/yYe4P8mH+D/Jh/g/yYe3/8mH9//Hxfe/0hM + 5/+Vovf/lqL3/5Sg9/+UoPj/l6P+/4GIvv99fH7/r6+5/7y9zv+3uMj/trfG/7a3xv+2t8b/trfH/7a3 + x/+3uMf/u7zM/7m6yv+ztMP/t7fH/7a3xv+2t8b/trfH/7a3x/+2t8b/trfH/7a3x/+2t8f/trfH/7a3 + x/+2t8b/trfG/7a3xv+3uMj/ubrK/7u8zf+6u8z/urvL/7W2xf+rrLn/oqOu/5WVnv+FhYv7e3t+2XBw + crNqammVZ2dlYGdnZTpoaGYfaGdmBGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gKiYf + 4HwmHuDMJh/g/yYe4P8mH+D/Jh7f/yUf3/8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/JR7f/yYe + 4P8mHt//Jh7g/yYe4P8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/f/yYf + 4P8mHuD/JR7f/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh7g/yYd + 4P8mHd//Jhzf/yYe3/8mIuH/JSnk/yUz5/8kPOv/JETt/yNI8P8jSfH/I0nw/yNH7/8TOe7/U3T0/9Xh + /f+ds/n/HkXw/3KL9v/i6P7/jJ33/4ya9v+bpfj/m6X4/5ei9/+UoPf/lKD3/5Wg9/+Xovf/mqT3/5yl + +P+Kmff/VnDz/yJH7/8fRPL/JEr3/yRI8f8hO9T/GyWi/xYVfv8UD3L/FBF2/xUTe/8UEnb/FBN3/xwY + n/8kHtT/JyDn/yYf4/8mH+D/Jh/g/yYe4P8mHN//GhHd/56j9P/7+vP/Ly8w/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/zg4OP/Dw8TPxsbGDcbGxgDGxsYAxsbGAMbGxgDGxsYAxsbGAMbG + xgDGxsYAxsbGAMbGxgDGxsYAxsbGAMbGxgDGxsYAJR7fACUe3wAmH+AAJh7fACYf3wAmHt8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AALSzhAC0s + 4QAtLOEALSzhAC0s4QAtLOEALSzhAC0s4QAtLOEALSzhAC0s4QAtLOEALSzhAC0s4QAtLOFIJiTf/xwV + 3/8dFd7/IRnf/yYd4P8mH+D/Jh7g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/JR/g/yYf3/8mH9//Jh7g/yYe4P8mHuD/Ihrf/zY4 + 5PmNm/XsmKT48JSf9/qUoPf9lKD3/5Sg+P+Wo///g4vG/3Z3f/+cnKH/uLnJ/7u8zP+3uMj/trfG/7a3 + xv+2t8b/trfG/7a4x/+3uMf/t7nI/7a3xv+2t8b/trfG/7a3x/+2t8f/trfG/7a3xv+2t8f/trfH/7e4 + yP+5usr/u7zM/7q7y/+6usv/tLXF/6mquP+goKv/kZKZ/4SEifR4eHrRb29wrWlpaItmZmVTZ2ZmNGho + ZxdwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf + 4AAmH+AAJh7gDSYe30kmHt+gJh/g5iYf4P8mH9//Jh/g/yYf4P8mH9//Jh/g/yYf4P8mH+D/Jh7f/yYe + 3/8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYe + 3/8mH+D/Jh/g/yUe3/8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe + 3/8mH+D/Jh/g/yYf4P8mH+D/Jh3g/yYc3/8mHN//Jh7f/yYi4P8lKeP/JTPn/yQ97P8gQO3/Iknw/6a7 + +v/a5v7/Y4P1/w007v+Goff/ucv8/ytQ8P8wUfD/UW3z/3GF9f+Pnfb/m6b4/5qk9/+Woff/laD3/5Sg + 9/+VoPf/mKL3/52n+P92i/X/Kk7w/x9D7v8jR/H/JEn1/yRK9f8iQN7/HSux/xcZiP8UEHP/FBB2/xQS + ef8UEnT/FhSA/x4arv8mHtv/Jx/n/yYf4/8mH+D/Ihrf/yop4f/X4f7/1dPN/wUFBf8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP9MTE3/zc3N7sTExRzExMUAxMTFAMTExQDExMUAxMTFAMTE + xQDExMUAxMTFAMTExQDExMUAxMTFAMTExQDExMUAxMTFAMTExQAlHt8AJh/gACYe3wAmH98AJh7fACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACMb + 3wAjG98AIxvfACMb3wAjG98AIxvfACMb3wAjG98AIxvfACMb3wAjG98AIxvfACMb3wAjG98AIxvfSCQb + 3/8lHt//JB3g/yEY3/8cFN7/GhPe/xwW3v8dF97/Hxff/yYe4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf + 4P8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mHt//Jh7g/yYf4P8mH+D/Jh7f/yYf4P8mHuD/Jh/g/x4V + 3vNNT+hMnar4IpSg9yiUn/cxlKD3P5Wg91CUn/dslaD4hZej/5mNmOWsdXiTxH18fvOfoKr/trfG/7y8 + zf+6u8z/uLnI/7e3x/+3uMf/trfG/7a3x/+2t8f/trfH/7a3x/+3uMf/uLnJ/7q7y/+7vM3/urzM/7m6 + yv+1tsb/qqu4/6Chqv+RkZn/goKI8HZ2eMtub26maGhngGdnZU9nZ2YraGhnEW9vcABpaWgAZmZlAGdm + ZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4CAmHuBmJh7fuSYe3/kmHuD/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH9//Jh/f/yUf3/8lHt//Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh7f/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe3/8mHd//JR3e/yYc3/8mHt//GRjg/3KB + 7//b4v3/o7b5/x9E7v8oTvD/wdH8/5Ss+f8cQe//HkLv/x1B8P8fRO//MVPw/1Jv8/94jPX/laH3/5ym + +P+Zo/f/laD3/5Sg9/+Un/f/nqf3/3eM9f8jSO//IUXw/yNH7/8jR/D/I0n0/yRK9v8jROn/HzPC/xke + k/8UEXf/FBBz/xQRdv8UEnX/FxWH/x8asv8lHtn/Jh/h/x8W3/9OVef//////5OTkv8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/cHBx/8/PzvvDw8Q1w8PEAMPDxADDw8QAw8PEAMPD + xADDw8QAw8PEAMPDxADDw8QAw8PEAMPDxADDw8QAw8PEAMPDxADDw8QAw8PEACYf4AAmHt8AJh/fACYe + 3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAgFt8AIBbfACAW3wAgFt8AIBbfACAW3wAgFt8AIBbfACAW3wAgFt8AIBbfACAW3wAgFt8AIBbfACAW + 31obE97/HRbf/yUh4P84O+P/UU/n/2dp6v96hO7/h4/w/2hy6v8mIeD/JR7g/yYe4P8mHuD/Jh/g/yYf + 4P8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/f/yYe3/8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf + 4P8kHOCQTU/oAJ2q+ACUoPcAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QdmZmIwaWlnjnp7 + feSUlZz/paay/7Gywf+4usn/ubrK/7m7zP+6u8z/ubrK/7m6yv+5usr/t7jI/6+vvf+mp7P/n5+p/5CR + mf+Dg4j1dnZ41G5ub6hoaGh6Z2dlSWdnZidoaGgMbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZm + ZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd + 4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wImHuAzJh/ggiYe4M0mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYe3/8mH+D/Jh/g/yYf + 4P8mH+D/Jh7f/yYf4P8mH9//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7f/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/HhTf/z9C + 4//Hy/j/z9T7/0xR5v8QD9//Z3fu/9ji/f9fe/L/Gj/u/yNJ8P8jSfD/Ikfv/x9E7/8cQe//Ikfv/zhZ + 8f9fePP/g5T2/5ik9/+cpff/mKL3/5ah9/+dpvf/Smjy/x1B7/8jR/D/I0fv/yNH7/8jR/D/I0jz/yRK + 9/8jR+//ITrR/xslov8WFX7/FBBy/xMPb/8TEG7PHhqoVyUe5FQGANtjjJHxuf////9sbG3/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/CAgI/Kampv7Kysv/xMTEYMTExADExMQAxMTEAMTE + xADExMQAxMTEAMTExADExMQAxMTEAMTExADExMQAxMTEAMTExADExMQAxMTEAMTExADExMQAJh7fACYf + 3wAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAI0bvACNH7wAjR+8AI0bvACNG + 7wAjRu8AP0DkAD9A5AA/QOQAP0DkAD9A5AA/QOQAP0DkAD9A5AA/QOQAP0DkAD9A5AA/QOQAP0DkAD9A + 5AA/QOReZ2vr/4uT8f+ztfb/wcf5/9DT+//Z2v3/2Nj9/+Df/v+vtvb/KSbh/yMc4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYe + 4P8mH+DWJh/gFyYf4ACdqvgAlKD3AJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlp + ZwBoaGcdZ2dmW2xra5lzc3W8fX1/5IWGjPeOj5f9kZKa/omKkPmGhov2f3+D6nh4etFycnOzbGxtmWlp + Z3lnZ2ZJZ2dmJ2lpaBJubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlp + aABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn + 2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AMJh/gRiYf + 4J0mH+DiJh/g/yYf4P8mHuD/Jh7f/yYf4P8mHt//Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYe4P8mHuD/Jh7g/yYe4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH9//JBzg/x4Z + 3/+Wn/H/4OT9/4KL7/8YD93/Lyzh/7zD9/+7wvf/LzDi/yIk4v8lM+j/JD7r/yRF7v8jSe//I0nw/yJH + 8P8eQ+//HEHv/yZL8P9AYfH/Z3/0/4WW9v+Xovf/oKn3/1x38/8cQu//I0bv/yNH7/8jRvD/I0fw/yNG + 8P8jR+//I0fx/yRK9f8kSfT/Ij/d/xwssf8ZIpuoFBN4Dx4aqAAlHuQABgDbAOzr/ab+/v7/VFVV/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/woKCrCrqqvNysrL/8PDxIbDw8QAw8PEAMPD + xADDw8QAw8PEAMPDxADDw8QAw8PEAMPDxADDw8QAw8PEAMPDxADDw8QAw8PEAMPDxADDw8QAw8PEAMPD + xAAmH98AJh7fACYf4AAmH+AAI0fvACNH7wAjRvAAI0fwACNG7wAjR+8AI0fvACNG7wAjR+8AI0fvACNG + 7wAjRu8AI0bvALS99gC0vfYAtL32ALS99gC0vfYAtL32ALS99gC0vfYAtL32ALS99gC0vfYAtL32ALS9 + 9gC0vfYAtL32etjZ/f/Y2P3/1dX8/9PT/P/R0Pv/z8/7/8/P+//T0vv/wcT5/zMu4v8iGt//Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mHt//JR7f/yYf4P8mH+D/Jh7f/yYe3/8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf + 4P8mH+DzJh/gPSYf4AAmH+AAnar4AJSg9wCUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZm + YgBpaWcAaGhnAGdnZgBsa2sAampoBWloZxloaGYpaGhmP2dnZkRnZ2YuZ2dmJWhoZh1paWgQcnJzAGxs + bQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9v + cABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJo + egAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gGCYf4F4mH+CuJh7g8yYf3/8mH+D/Jh7f/yYf4P8mH9//Jh/f/yYf4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mHt//Jh/f/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8mHuD/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf3/8mH+D/Jh7g/yYf4P8mH+D/Ixvf/xcQ + 3f9mb+r/2uD8/6ev9P8nI+D/GBLd/4uT8f/e4/3/bnXs/x0U3v8mHd//Jhzf/yYf4P8mJOL/JS/l/yU5 + 6f8kQ+3/I0jw/yNJ8P8hRvD/HkLv/x1D7/8pTfD/PF3x/0lp8/8uUPD/IUXw/yNG7/8jR/D/I0bv/yNH + 8P8jR/D/I0fv/yNH7/8jR+//I0fx/yNJ8/8kSvb/JEjx5CJC5p8gQutFJEXvAc7Z/AL////T8vLy/zMz + M/8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP89PT1MyMjIm8XFxv/Dw8ShxMPEAMTD + xADEw8QAxMPEAMTDxADEw8QAxMPEAMTDxADEw8QAxMPEAMTDxADEw8QAxMPEAMTDxADEw8QAI0fvACNG + 7wAjR/AAI0fvACNH7wAkR/AAI0fwACNH7wAjR+8AI0bwACNH8AAjRu8AI0fvACNH7wAjRu8AI0fvACNH + 7wAjRu8AI0bvACNG7wDU0/wA1NP8ANTT/ADU0/wA1NP8ANTT/ADU0/wA1NP8ANTT/ADU0/wA1NP8ANTT + /ADU0/wA1NP8ANTT/H/Pz/v/z8/7/8/P+//Pz/v/z8/7/8/P+//Pz/v/0dD7/9HS+/9ESOX/IBjf/yYe + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mHt//Jh/g/yYe4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/gcSYf4AAmH+AAJh/gAJ2q+ACUoPcAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ + 0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJy + cwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGho + ZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5s + YQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4CkmH+B4Jh/gxSYf4PwmH+D/JR7f/yYe3/8mH+D/Jh/g/yYe + 4P8mH+D/Jh7g/yYf4P8mHuD/Jh7f/yYf3/8mHuD/Jh7f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/f/yYf4P8mH+D/Jh/g/yYf4P8mH9//Jh/g/yYf4P8kHOD/HBPe/yEe + 3/9ze+z/1tv7/6+39f8vLeH/FhHd/3qD7f/c4fz/oar0/ygm4P8jG9//Jh/g/yYf4P8mHuD/Jh7g/yYd + 3/8mHt//JiLg/yUq5P8lNun/JUHs/yNI7/8jSPD/IUfw/x5E7/8eQu//IUTv/yNH8P8jR+//I0fv/yNH + 7/8jR/D/I0bv/yNG7/8jR+//I0fv/yNG7/8jR+//I0fw/yNH8f8jSPL/I0jx/ho/8LxCYPFx/v//8OXk + 4/8YGBn/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADKZmZmBtfX137DwsP/xMTEycTD + xAnEw8QAxMPEAMTDxADEw8QAxMPEAMTDxADEw8QAxMPEAMTDxADEw8QAxMPEAMTDxADEw8QAxMPEACNH + 7wAjRu8AI0fwACNH7wAjR+8AJEfwACNH8AAjR+8AI0fvACNG8AAjR/AAI0bvACNH7wAjR+8AI0bvACNH + 7wAjR+8AI0bvACNG7wAjRu8AqbL2AKmy9gCpsvYAqbL2AKmy9gCpsvYAqbL2AKmy9gCpsvYAqbL2AKmy + 9gCpsvYAqbL2AKmy9gCpsvaT0dH7/8/P+//Pz/v/z8/7/9DP+//Q0fv/1tX8/9zb/f/R1fz/Q0fl/yAY + 3/8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mHt//Jh/g/yYf4P8lHt//Jh/g/yYf + 4P8mH+D/Jh/fmyYf4AAmH+AAJh/gACYf4ACdqvgAlKD3AJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y + 5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlp + aABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdn + ZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZm + ZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AYmH+A5Jh/gkCYf4NwmHuD/Jh/g/yYf + 4P8mH+D/Jh7g/yYd3/8mHuD/JR7g/yUd3/8lHd//Jh7g/yUd3/8mHuD/Jh7g/yYe3/8lHd//JR3f/yUd + 3/8lHd//JR7g/yUd3/8lHd//JR7f/yUd4P8kHOD/JB3g/yQd4P8kHOD/Ihng/x4U3/8bFN7/JiPg/1RZ + 6P+osPT/1tz8/46U8P8kIuD/NTXj/5qh8v/Z4Pz/n6j0/zIx4v8gGN//Jh/g/yYf4P8mH+D/Jh7g/yYe + 4P8mHuD/Jh7f/yYd4P8mHd//Jh3f/yYg4P8mKOP/JTTo/yRB7P8jR+//I0nw/yNH8P8jR/D/I0fv/yNG + 7/8jR+//I0fv/yNG7/8jR+//I0fw/yNH8P8jRu//I0fw/yNH7/8jR+//I0fv/yNH7/8cQe//QmTy//7/ + ///Pz83/BQUF/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAAY2ZmZgDW1tdkw8PD/8TE + xOrExMQXxMTEAMTExADExMQAxMTEAMTExADExMQAxMTEAMTExADExMQAxMTEAMTExADExMQAI0fvACNG + 7wAjR+8AI0bvACNH8AAjR+8AI0fvACRH8AAjR/AAI0fvACNH7wAjRvAAI0fwACNG7wAjR+8AI0fvACNG + 7wAjR+8AI0fvACNG7wAjRu8AI0bvAMPG+QDDxvkAw8b5AMPG+QDDxvkAw8b5AMPG+QDDxvkAw8b5AMPG + +QDDxvkAw8b5AMPG+QDDxvkAw8b5oNDQ+//Q0Pv/09L8/9nY/f/X2Pz/zc/7/6yy9v+EiO//VVnn/yci + 3/8lHd//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/JR7f/yYf4P8mH+D/Jh/f/yYe + 4P8mHuD/Jh7gySYe3xEmHt8AJh7fACYe3wAmHt8AJh7fAJSg9wCUn/cAlKD3AJWg9wCUn/cAlaD4AJej + /wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGho + ZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdn + ZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZn + ZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AQJh/gUiYf + 4KQmHuDqJh7g/yEa3/8dGN7/Hxrf/x8Z3/8fGd7/Hxrf/x8a3/8fGt//Hxne/x8a3/8fGt7/Hhne/yAa + 3/8iHd//Ix3g/yQe4P8jHt//JB7f/yMd3/8mIOD/KSPg/ygi4P8rJOH/Lynh/zs/4/9YWuj/gYbu/663 + 9f/ByPj/m6Py/1Rd5/9CR+T/hYzu/8fR+f+zvPb/YWjq/ycj4P8hGN//Jh/f/yYe4P8mHuD/Jh/g/yYe + 4P8mHuD/Jh/g/yUe3/8mHuD/Jh/g/yYf4P8mHeD/Jhzf/yYd3/8mH9//Jijj/yU06P8kQOv/I0fv/yNJ + 8P8jSPD/I0fv/yNG7/8jR+//I0fv/yNH8P8jR/D/I0bv/yNH7/8jR/D/I0fv/yNH7/8jRu//Gz/u/1R3 + 9P//////m5ub/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAAyAAAAArS0tIA0tLSQsTE + xPvExMT9xMTELcTExADExMQAxMTEAMTExADExMQAxMTEAMTExADExMQAxMTEAMTExAAjR+8AI0fvACNH + 7wAjRu8AI0fvACNG7wAjR/AAI0fvACNH7wAkR/AAI0fwACNH7wAjR+8AI0bwACNH8AAjRu8AI0fvACNH + 7wAjRu8AI0fvACNH7wAjRu8AI0bvACNG7wDHy/oAx8v6AMfL+gDHy/oAx8v6AMfL+gDHy/oAx8v6AMfL + +gDHy/oAx8v6AMfL+gDHy/oAx8v6AMfL+p/b2v3/0tX8/8DD+f+WnfL/bW/r/0JD5P8nI+D/HBff/x8W + 3/8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh7g/yYe4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g4CYe4CQmHt8AJh7fACYe3wAmHt8AJh7fACYe3wCUoPcAlJ/3AJSg9wCVoPcAlJ/3AJWg + +ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdn + ZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGho + ZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpq + agBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe + 3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJRzgHCYh4GVda+m5go3u+H6L7v9/jO7/fYnt/3uI7f98iO3/e4jt/3uI7f97iO3/fInt/3+L + 7v+Fku7/kp/y/46c8f+MmvD/jJrx/4yY8f+KlvH/lqDy/5qk8v+WoPL/naPz/6ip9f+VnvH/j57x/42W + 8P92e+z/ZXTq/3l/7f+hqvP/xs75/6u09f9iaOn/KSbh/x0V3/8kHOD/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYe3/8mH+D/Jh7g/yYd3/8mHd//JiDg/yUp + 4/8lNej/JEHs/yNH7/8jSfD/I0jw/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0fw/yNH7/8jRu//I0bv/xs/ + 7v9UdvT//////4mJif8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/QAAAFYAAAAAzs7MAM7O + zB7Ew8Tqw8PE/8PDxFbDw8QAw8PEAMPDxADDw8QAw8PEAMPDxADDw8QAI0fwACNH8AAjR+8AI0fvACNH + 7wAjR+8AI0bvACNH7wAjRu8AI0fwACNH7wAjR+8AJEfwACNH8AAjR+8AI0fvACNG8AAjR/AAI0bvACNH + 7wAjR+8AI0bvACNH7wAjR+8AI0bvACNG7wAjRu8AeoruAHqK7gB6iu4AeoruAHqK7gB6iu4AeoruAHqK + 7gB6iu4AeoruAHqK7gB6iu4AeoruAHqK7gB6iu6fhInv/1RZ5/8yLuH/IRzf/xwV3/8hGN//JBzg/yYe + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf + 4P8lH+D/Jh/g8SYf4EQmHuAAJh7fACYe3wAmHt8AJh7fACYe3wAmHt8AlKD3AJSf9wCUoPcAlaD3AJSf + 9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdn + ZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5v + bgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhn + ZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe + 4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/DBzeux7Ulfnyl9l6f1jaer/ZWvq/2hv6v9pb+v/aG7r/2tx + 6/96fu3/e3/t/3p+7f9tc+v/Zmzq/2ht6/9veuz/d4nt/3OF7P9jder/boHs/2x37P9tcuz/dnrs/4KK + 7/+JmfD/pa30/7a99/+krfX/fILu/0dL5v8lIuD/Hhbf/yQb4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYe + 3/8mHd//Jh3f/yYh4P8lKuT/JDjp/yRD7v8jSfH/I0nw/yNI7/8jRu//I0fw/yNH7/8jR/D/I0fw/yNH + 8P8bQO//VHb0//////+trKz/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wIDB7kAAAAAJEv/AMnI + wgDJyMIIxcTE18PDxP/ExMR+xMTEAMTExADExMQAxMTEAMTExAAjR+8AI0fwACNH8AAjR/AAI0fvACNH + 7wAjR+8AI0fvACNG7wAjR+8AI0bvACNH8AAjR+8AI0fvACRH8AAjR/AAI0fvACNH7wAjRvAAI0fwACNG + 7wAjR+8AI0fvACNG7wAjR+8AI0fvACNG7wAjRu8AI0bvACUh4AAlIeAAJSHgACUh4AAlIeAAJSHgACUh + 4AAlIeAAJSHgACUh4AAlIeAAJSHgACUh4AAlIeAAJSHgoR0X3/8eFN//Ixrf/yMc3/8jG9//IRnf/yAW + 3/8eFN7/IBff/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYe32ImH+AAJh7gACYe3wAmHt8AJh7fACYe3wAmHt8AJh7fAJSg9wCUn/cAlKD3AJWg + 9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdn + ZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGho + aABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGho + ZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf + 4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ASBgu48yMr5kqes9N2Sl/D/k5jx/5SZ + 8f+TmfH/kpfx/5GX8f+Rl/H/k5nx/5SZ8f+Di+//g43v/4SQ7/+Fke//h5Pv/4SQ7/97iO3/f43u/32B + 7f92dez/X2Pp/0dM5f8zMOL/JB/g/xwW3v8gGN//JR3f/yYe3/8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf + 3/8mH+D/Jh/g/yYf4P8mHuD/Jh3f/yYd3/8mIuH/JS7l/yQ86/8kRu//I0nw/yNH8P8jR+//I0fw/yNH + 7/8jR/D/Gz/v/1N28///////5+bl/xkZGv8AAAD/AAAA/wAAAP8AAAD/AAAA/wUGCf8gOrDqJUz/jCRL + /y0gQ/AAycjCAM/NwbHExMT/xMTEnsnHwQDJx8EAycfBAGqG9QAZPu8AI0fvACNH8AAjR/AAI0fwACNH + 7wAjR+8AI0fvACNH7wAjRu8AI0fvACNG7wAjR/AAI0fvACNH7wAkR/AAI0fwACNH7wAjR+8AI0bwACNH + 8AAjRu8AI0fvACNH7wAjRu8AI0fvACNH7wAjRu8AI0bvACNG7wAlHeAAJR3gACUd4AAlHeAAJR3gACUd + 4AAlHeAAJR3gACUd4AAlHeAAJR3gACUd4AAlHeAAJR3gACUd4JMiGd//HhXf/xwV3v8jIN//Lyrh/z5A + 5P9QV+f/Y2fq/1FX5/8lIOD/JR7f/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh7g/yYf4H4mHt8AJh/gACYe4AAmHt8AJh7fACYe3wAmHt8AJh7fACYe3wCUoPcAlJ/3AJSg + 9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGho + ZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdn + ZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdn + ZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe + 3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAMIBrfUCkj + 4J4pJODnKiTh/yok4f8pI+D/KSPg/yok4f8pJOH/JSDg/yQg4P8kH+D/JB/g/yQf3/8iHd//Hxrf/x8Z + 3/8cFd7/GxLe/x4V3v8hF9//Ixvg/yQd3/8lHt//Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mH9//Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yUf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mH9//Jh7g/yYf4P8mH+D/Jh7g/yYc3/8mHuD/JSbi/yU05/8jQe3/I0jw/yNI + 8P8jR/D/I0fv/xtA7/9TdvP//v////////9hYWL/AAAA/wAAAP8AAAD/AAAA/wAAAP8YJmn/Jkz+/yNH + 8f8jR+/0IEPwpyRH7zWts8uKzszB/8PDxL/Jx8EEycfBAMnHwQBqhvUAGT7vACNH7wAjR/AAI0fwACNH + 8AAjR+8AI0fvACNH7wAjR+8AI0bvACNH7wAjRu8AI0fwACNH7wAjR+8AJEfwACNH8AAjR+8AI0fvACNG + 8AAjR/AAI0bvACNH7wAjR+8AI0bvACNH7wAjR+8AI0bvACNG7wAjRu8AIxvfACMb3wAjG98AIxvfACMb + 3wAjG98AIxvfACMb3wAjG98AIxvfACMb3wAjG98AIxvfACMb3wAjG99qNzbj/1pg6P+Ehe//m6Tz/7q+ + +P/Nz/v/0NP7/9zd/f+os/X/JyTg/yQc4P8mH+D/Jh/g/yYe4P8mH9//Jh/g/yYf4P8mH+D/Jh7g/yYe + 4P8mH+D/Jh/g/yYf4JYmH+AAJh7fACYf4AAmHuAAJh7fACYe3wAmHt8AJh7fACYe3wAmHt8AlKD3AJSf + 9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGho + ZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdn + ZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdn + ZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe + 3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa + 3wApI+AAJB3gFiQd4F8kHd+vJB3f8CQc3/8kHeD/JBzg/yUd3/8lHuD/JR3g/yUd4P8lHeD/JR3f/yUe + 4P8lHuD/Jh7f/yYe4P8mH+D/Jh/g/yYf4P8mHt//Jh7g/yYf4P8mH+D/Jh7f/yYf4P8mHt//Jh7f/yYf + 4P8mH+D/Jh7f/yYf4P8mH+D/Jh7f/yYe4P8lH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7f/yYd3/8mHd//JiDg/yUr + 5P8kOur/I0Xv/yNJ8P8bQe//VHbz//z+////////tLS1/wAAAf8AAAD/AAAA/wAAAP8LDyD/JUbe/yRI + 9v8jR+//I0fw/yNH7/8bQPH3TGvl5LC3yv/IxsDnqbPPCxtE8wCjufoAaob1ABk+7wAjR+8AI0fwACNH + 8AAjR/AAI0fvACNH7wAjR+8AI0fvACNG7wAjR+8AI0bvACNH8AAjR+8AI0fvACRH8AAjR/AAI0fvACNH + 7wAjRvAAI0fwACNG7wAjR+8AI0fvACNG7wAjR+8AI0fvACNG7wAjRu8AI0bvAE5S5wBOUucATlLnAE5S + 5wBOUucATlLnAE5S5wBOUucATlLnAE5S5wBOUucATlLnAE5S5wBOUucATlLnPb7D+PvY2f3/2tv9/9nX + /f/T0/v/0dH7/9DQ+//V1Pz/ur74/y8r4f8jHOD/Jh7g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/Jh7g/yYf4LAmH+AJJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGlo + ZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGho + aABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpq + aQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S + 8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4CMmH+BwJh/gwSYe3/UmH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe + 3/8mH9//Jh/g/yYf4P8mHt//Jh7g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf3/8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf3/8mH9//Jh7g/yYf4P8lH9//Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe + 4P8mHd//Jh3f/yUl4f8lMuf/HDrr/0tu8//6/P////////Pz8/8xMTL/AAAA/wAAAP8DAgD/Hzad/yVL + //8jRu//I0fv/yNG7/8jRu//I0fv/xtA8f8uUu3/2tzk+5Su8mgbRPMKo7n6AGqG9QAZPu8AI0fvACNH + 8AAjR/AAI0fwACNH7wAjR+8AI0fvACNH7wAjRu8AI0fvACNG7wAjR/AAI0fvACNH7wAkR/AAI0fwACNH + 7wAjR+8AI0bwACNH8AAjRu8AI0fvACNH7wAjRu8AI0fvACNH7wAjRu8AI0bvACNG7wCHmPAAh5jwAIeY + 8ACHmPAAh5jwAIeY8ACHmPAAh5jwAIeY8ACHmPAAh5jwAIeY8ACHmPAAh5jwAIeY8Aq3vffN1NP8/8/P + +//Pz/v/z8/7/8/P+//Pz/v/0ND7/87Q+/8/QeT/IRnf/yYe4P8mHuD/Jh/g/yYe4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4MUmHt8TJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpq + aABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5u + bwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBw + cgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf + 4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK + +QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AImH+A1Jh7gfiYe4M8mHuD8Jh/g/yUf + 3/8mH+D/Jh/f/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHt//Jh7g/yUe4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/JR7f/yYf4P8mH+D/Jh7g/yYf4P8mH9//Jh7f/yYf + 4P8mH+D/Jh/f/yYf4P8mHeD/Jhzf/x8a3/9BR+f/9/j+////////////d3d4/wAAAP8AAAD/Ex9P/yVL + +f8jR/L/I0fw/yNG7/8jR+//I0bv/yNH7/8hRe//Ikfw/+zu///c5/7/LVLwyqO5+l5qhvUKGT7vACNH + 7wAjR/AAI0fwACNH8AAjR+8AI0fvACNH7wAjR+8AI0bvACNH7wAjRu8AI0fwACNH7wAjR+8AJEfwACNH + 8AAjR+8AI0fvACNG8AAjR/AAI0bvACNH7wAjR+8AI0bvACNH7wAjR+8AI0bvACNG7wAjRu8Ah5jwAIeY + 8ACHmPAAh5jwAIeY8ACHmPAAh5jwAIeY8ACHmPAAh5jwAIeY8ACHmPAAh5jwAIeY8ACHmPAAnarzgdXU + /P/Pz/v/z8/7/8/P+//Pz/v/0dD7/9bV/P/Y2v3/R07l/yAX3v8mHuD/Jh7f/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4MwmH+AeJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxr + awBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlp + aABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGho + ZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf + 4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC + 7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAGJh7gQyYf + 4JAmH+DYJh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYe4P8mH+D/Jh/g/yUe3/8lHuD/Jh/g/yYe + 3/8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yUe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yUe3/8lH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mHt//JR/f/yYf4P8mH+D/Jh/g/yYe4P8fFd//T1Xl//r9/v///////////769vv8AAAD/BgsV/yRE + 0/8jSPn/I0bv/yNG7/8jRu//I0fv/yNH7/8jR+//IETv/yZJ8P/r7f7/2eT+/zJV8f+4yPv/aYX1yRk+ + 710jR+8KI0fwACNH8AAjR/AAI0fvACNH7wAjR+8AI0fvACNG7wAjR+8AI0bvACNH8AAjR+8AI0fvACRH + 8AAjR/AAI0fvACNH7wAjRvAAI0fwACNG7wAjR+8AI0fvACNG7wAjR+8AI0fvACNG7wAjRu8AI0bvAISQ + 7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ + 7yXHyPrr1dT8/9LS/P/W1fz/2dn9/9PV+/+4vPf/goju/y0q4f8kG+D/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/Jh/g/yYf4NomH+AiJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdn + ZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdn + ZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdm + ZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ + 3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh7gECYf4FEmHuCdJh/g4CYf4P8mH+D/Jh7f/yYe3/8mH+D/Jh7g/yUf3/8lH9//Jh/f/yYe + 4P8mHt//Jh/g/yYf3/8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8lH9//Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8lHuD/JR/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7f/yUf4P8mH+D/Jh/g/yYf4P8mHuD/HhXf/1Vg5v/8//7////////////j4+P/GhkS/xco + h/8mTP//I0jw/yNH8P8jR+//I0fw/yNH8P8jR/D/I0fw/yFF8P8lSPD/7e/+/8TU/P8tUfH/v838/159 + 9P8aPu7/I0fvyCNH8FwjR/AEI0fwACNH7wAjR+8AI0fvACNH7wAjRu8AI0fvACNG7wAjR/AAI0fvACNH + 7wAkR/AAI0fwACNH7wAjR+8AI0bwACNH8AAjRu8AI0fvACNH7wAjRu8AI0fvACNH7wAjRu8AI0bvACNG + 7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ + 7wCEkO8Ag4nvmtLW+//HzPr/tLj3/4eN7/9TVOf/King/xwU3v8kHN//Jh7g/yYe4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh7g/yYf4NsmH+AoJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGho + ZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdn + ZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZm + ZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd + 4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN6 + 7AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4BgmH+BXJh/gqSYf3+YmHuD/Jh/g/yYe4P8mH+D/Jh/f/yYf + 3/8mH+D/Jh/g/yYe4P8lHt//Jh/g/yYe3/8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yUe + 3/8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe3/8lH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/x8X3/9IR+X/+fv+///////////////8/2Jl + jf8aFNX/JSnn/yQ56f8jRu7/I0nw/yNI8P8jR/D/I0bv/yNG8P8hRe//Jkrw/+3w/v+/0Pv/L1bx/7rL + /P9QcvT/HEDv/yNH7/8jR/D/I0fwvCNH8EwjR+8AI0fvACNH7wAjR+8AI0bvACNH7wAjRu8AI0fwACNH + 7wAjR+8AJEfwACNH8AAjR+8AI0fvACNG8AAjR/AAI0bvACNH7wAjR+8AI0bvACNH7wAjR+8AI0bvACNG + 7wAjRu8ALCrhACwq4QAsKuEALCrhACwq4QAsKuEALCrhACwq4QAsKuEALCrhACwq4QAsKuEALCrhACwq + 4QAsKuEALCrhACwq4TJHR+X2QUPk/ycm4P8cFN7/HBTe/yMb4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe4NgmHuAoJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlp + ZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlp + ZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlp + aABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn + 2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre + /ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AgJh7gXyYe4K4mH+DrJh/g/yYe + 3/8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYe4P8lHt//Jh/g/yUf + 3/8mH+D/Jh/g/yYe4P8mH+D/Jh7f/yYe4P8mH+D/Jh7g/yYf4P8mH9//JR/g/yYe4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe3/8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8iGuD/MS7i/9vj+v////////////// + //+QnPX/GBHg/yYc3v8mHd7/JSXh/yU06P8jQ+3/I0nw/yNI8P8jR+//HkLw/zBY8f/3+v//rsD6/zdf + 8v+6zPv/Qmfz/x5B7/8jR+//I0fv/yNH8P8jR+//I0fvryNH7zcjR+8AI0fvACNG7wAjR+8AI0bvACNH + 8AAjR+8AI0fvACRH8AAjR/AAI0fvACNH7wAjRvAAI0fwACNG7wAjR+8AI0fvACNG7wAjR+8AI0fvACNG + 7wAjRu8AI0bvACwq4QAsKuEALCrhACwq4QAsKuEALCrhACwq4QAsKuEALCrhACwq4QAsKuEALCrhACwq + 4QAsKuEALCrhACwq4QAsKuEAHhXfnh8W3v8kHOD/Jh7g/yYf4P8mH+D/Jh7f/yYe3/8mH+D/Jh/g/yYe + 4P8mHt//Jh7g/yUe38kmHuAgJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZm + YgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxs + bQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9v + cABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJo + egAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r + 6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gKSYf + 4GkmHuC3Jh7g7CYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHt//Jh7f/yYf3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8lHt//Jh7f/yYf3/8mH+D/Jh/g/yYe4P8mH+D/JR3g/xwW3v+epfH///////// + ////////aW/r/xkR3f8mHuD/Jh7g/yYd3/8mG97/JSHg/yUw5f8kQO3/I0nw/xM57/9lhPX//////5+x + +P9AZvP/vM38/zVZ8P8gQ+//I0fv/yNH7/8jR+//I0fw/yNH8P8jR/D1I0fvkCNH7x8jRu8AI0fvACNG + 7wAjR/AAI0fvACNH7wAkR/AAI0fwACNH7wAjR+8AI0bwACNH8AAjRu8AI0fvACNH7wAjRu8AI0fvACNH + 7wAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4BQmH+CuJh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4KomH+AYJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ + 0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJy + cwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGho + ZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5s + YQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh + 4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYe3yomH+BiJh/grCYf4N0mH+D/JR7f/yYe4P8mH+D/Jh/g/yYf3/8mH+D/Jh/g/yYe + 3/8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yUf3/8mH+D/Jh/f/yUe3/8mH9//Jh/g/yYe4P8mH9//JR7f/yUe + 3/8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8lHt//JR/g/yYf3/8fFt7/Pj/j/+bu + /P//////2OL6/ysr4P8iGd//JR/g/yUf3/8lH+D/JR/f/yUe3/8mHN//Jx/g/xkf4/86WO7/4+r+//// + //95k/f/U3Hz/77N/P8rTvD/IUXv/yNG7/8jRu//I0bv/yNH7/8jRvD/I0bv/yNH7/8jR+/gI0bvZSNH + 7wQjRu8AI0fwACNH7wAjR+8AJEfwACNH8AAjR+8AI0fvACNG8AAjR/AAI0bvACNH7wAjR+8AI0bvACNH + 7wAjR+8AI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gBiYe4GAmH+C7Jh/g6CYf4P4mH+D/Jh/g/yYf + 4P8mH+D1Jh7g0yYf4G8mH+AFJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y + 5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlp + aABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdn + ZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZm + ZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc + 4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AZJh/gRyUe34smHuDHJh/g9SYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/f/yYf3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH9//Jh/g/yUf + 4P8lHt//Jh/g/yYf4P8mHuD/Jh/g/yYf3/8mH9//Jh7g/yYe4P8mH+D/JR/f/yUf4P8mH+D/Jh/g/xwV + 3/9ZYOj/u7z1/1pf6P8cFN7/Jh/g/yUf3/8mHuD/Jh7f/yYf4P8mH+D/JiDf/xsR3v8yM+H/ztX4//// + ///u9P3/MFXw/4ae9/+uwvv/JUrw/yFF7/8jR+//I0bv/yNG8P8jR/D/I0fw/yNH7/8jR/D/I0fw/yNH + 7/8jR/CyI0bvLCNH8AAjR+8AI0fvACRH8AAjR/AAI0fvACNH7wAjRvAAI0fwACNG7wAjR+8AI0fvACNG + 7wAjR+8AI0fvACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gASYf4CUmHuBCJh/gRyYf + 4EcmH+BGJh/gNCYf4BImH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej + /wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGho + ZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdn + ZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZn + ZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gByYf4DcmH+B4Jh/gvCYf + 4PImH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yUe4P8mH+D/JR7f/yUf + 4P8mHuD/Jh/g/yYf4P8mHuD/Jh7f/yYf4P8mH9//Jh/f/yYe3/8mH9//Jh/g/yYf4P8mH9//Jh/g/yYf + 4P8mHt//Hhbf/xsU3v8eFt7/Jh/f/yYe3/8mHuD/Jh/g/yYf4P8mH+D/Jh/g/xkQ3v8xNeH/zdb4//// + ////////b3bq/0NP5//L1vv/l7D5/yBI8P8iR/D/I0fv/yNH7/8jR/D/I0fw/yNH8P8jR/D/I0bw/yNG + 8P8kRu//I0bw/yNG8OgjR/BpI0fvACNH7wAkR/AAI0fwACNH7wAjR+8AI0bwACNH8AAjRu8AI0fvACNH + 7wAjRu8AI0fvACNH7wAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg + +ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdn + ZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGho + ZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpq + agBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe + 3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd + 3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf + 4AMmHuAvJh7gaiYf4LUmH+DvJh/g/yYe4P8mHt//Jh/g/yYe4P8mHuD/Jh/f/yYf4P8mHt//Jh7g/yUe + 4P8lH9//Jh/f/yYf4P8mH+D/Jh/g/yUe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/f/yYf + 4P8mH+D/Jh/g/yYe4P8lHuD/JR/f/yYe3/8mH+D/Jh/g/yYf3/8mHuD/IBff/xQM3f8/ROT/z9n4//// + ///5/v7/eoDt/zA44v+3vfb/2978/3V+7f8bLef/I0Tu/yNK8P8jSPD/I0fw/yNH7/8jR+//I0bv/yNG + 8P8jR+//JEfw/yNG7/8jRu//I0fw/yNH76QjR+8bJEfwACNH8AAjR+8AI0fvACNG8AAjR/AAI0bvACNH + 7wAjR+8AI0bvACNH7wAjR+8AI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf + 9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdn + ZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5v + bgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhn + ZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe + 4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd + 4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh7gKyYf4GMmH+CuJh/f6iYf4P8mHuD/Jh7g/yYe3/8mHuD/Jh7f/yYf + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mHt//Jh7f/yQa3/8bE97/FxLe/zM14v+Jke//8PP9//// + ///h5vv/XWXo/zk95P+xuPb/2t38/6y29f8wL+H/Ixje/yYj4f8lM+f/I0Pu/yNK8f8jSfH/I0fv/yNH + 8P8jR+//I0fv/yNH7/8kRvD/I0fv/yNH7/8jR/D/I0fv1iRH8EgjR/AAI0fvACNH7wAjRvAAI0fwACNG + 7wAjR+8AI0fvACNG7wAjR+8AI0fvACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe + 3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg + 9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdn + ZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGho + aABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGho + ZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf + 4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd + 4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4CQmH+BhJh/gqyYf4OMmH+D/Jh7g/yUe + 3/8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mHt//Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/JR7g/yMa4P8eFd7/GBDe/xkR3v8kJOD/VFjo/56o8v/s8v3//////+Pn + +/+SmfD/RE3k/2Zu6v/ByPj/3N/8/7O89v87P+P/Hxff/yYf4P8mHd//Jh3f/yYh4P8lMeb/JELt/yNJ + 8f8jSfD/I0fv/yNG7/8jRu//JEbw/yNH8P8jR+//I0bv/yNH8P8jR+/3I0fwhiNH7wwjR+8AI0bwACNH + 8AAjRu8AI0fvACNH7wAjRu8AI0fvACNH7wAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf + 4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg + 9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGho + ZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdn + ZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdn + ZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe + 3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj + 4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AdJh/gVyYe + 4KUmH+DkJh/g/yUd3/8eFd7/Fw/e/xcP3v8XD97/Fw/e/xcP3v8XD97/Fg/d/xcP3v8XD97/Fw/e/xcP + 3v8XD97/FxDe/xgS3v8ZE97/HBXe/x8X3/8sLOH/Rknl/3Bz6/+krvP/2+D6//n+///8////ydL4/4uc + 7/9ncuv/d4Dt/7W79v/b4Pz/0tj7/6Kr9P88P+P/HRbf/yYf4P8mHt//Jh7g/yYf4P8mHuD/Jhzf/yYh + 4P8lL+b/JEHs/yNJ8f8jSfD/I0fw/yNG7/8jRu//I0fw/yNH7/8jR+//I0bv/yNG7/8jR/DCI0fvNCNG + 8AAjR/AAI0bvACNH7wAjR+8AI0bvACNH7wAjR+8AI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf + 9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGho + ZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdn + ZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdn + ZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe + 3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa + 3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe + 3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh/gHCUe31cmIuChS07m4m5r6v94c+z/enXs/3Vw6/91b+v/dG/r/3Nv6/9ybuv/cm7r/3Ju + 6/9ybuv/cm3r/3Fv6v99h+z/i5fv/56p8f+5vvX/ztX4/93p+//c5vr/z9r5/8nS+P+1w/b/pLHz/6Cs + 8/+0vPb/z9b6/9Xa+/+3wPf/h47v/1BW5/8lI+D/IBjf/yYf3/8mH9//Jh7f/yYf4P8mHuD/Jh7g/yYe + 4P8mHd//Jhzf/yYg4f8lLub/JEHt/yNK8f8jSPD/I0fv/yNH7/8jR+//I0fv/yNH8P8jR/D/I0bv/yNG + 7+8jRvBuI0fwASNG7wAjR+8AI0fvACNG7wAjR+8AI0fvACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGlo + ZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGho + aABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpq + aQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S + 8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf + 4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89Bn///9UzNb4nKe39N69zff/3Ob8//z////s9P7/2+f7/9zo + +//d6Pv/3en7/93p+//Y5fv/1uH6/9jj+//F0vj/tMP2/6Ox9P+ktPT/p7b0/7O+9/+xuvb/rbb1/6q0 + 9f+irfP/mJ7z/3l+7v9NUeb/LSrh/x0Y3v8eFt//JBzg/yYf4P8mH+D/Jh7f/yYe4P8mH+D/Jh7g/yYe + 3/8mH+D/Jh/g/yYf4P8mHuD/Jhzf/yYg4P8lL+X/JEDs/yNJ8P8jSPD/I0fw/yNG7/8jR/D/I0fw/yNH + 8P8jRu//I0bv/yNH8K0jRu8kI0fvACNH7wAjRu8AI0fvACNH7wAjRu8AI0bvACNG7wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpq + aABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5u + bwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBw + cgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf + 4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK + +QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYVusT4TZOb8pxqb+vfb3Ps/290 + 7P9vc+z/b3Ts/2907P9vdOz/b3Ts/2907P9vdOz/cHXs/3J27P9la+r/XWTp/11k6v9OVeb/Qj/k/zYx + 4/8sKeH/JSTg/x8a3/8dFN7/Hxjf/yMa3/8lHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh3f/yYg4P8lLuX/JEDs/yNJ8P8jSPD/I0fw/yNH + 7/8jRu//I0fv/yNH7/8jR+//I0fv4SNH71wjR+8AI0bvACNH7wAjR+8AI0bvACNG7wAjRu8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxr + awBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlp + aABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGho + ZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf + 4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC + 7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf + 4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf + 4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndEx0V + 30cdFd+SHRXf2B0V3/8dFd//HRXf/x0V3/8dFd//HRXf/x0W3/8dFd//Hxbf/x8X3v8fFt//IBff/yEZ + 3/8iGuD/Ixvg/yQc4P8lHuD/Jh7f/yYe4P8mH+D/Jh7g/yYf3/8mH+D/Jh/g/yYe3/8lHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYe3/8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mHt//Jhze/yYf3/8lLuX/JEDs/yNJ + 8P8jSfH/I0fw/yNG7/8jRvD/I0fv/yNH7/8jR/D/I0fvmSNG7xYjR+8AI0fvACNG7wAjRu8AI0bvACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdn + ZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdn + ZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdm + ZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ + 3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf + 4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ + 3QAdFd8AHRXfACYf4A4mH+BFJh7gkiYe39omHuD/Jh7f/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh7f/yYf + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYe3/8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mHt//JR7g/yYe + 4P8mH+D/JR7f/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/JR/g/yYf4P8mHd//Jh3f/yYg + 4P8lLuX/JEDs/yNJ8P8jSPD/I0bw/yNH8P8jR+//I0fw/yNH8P8jR+/RI0fvOyNH7wAjRu8AI0bvACNG + 7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGho + ZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdn + ZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZm + ZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd + 4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN6 + 7AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe + 3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb + 8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8NJh/gRiYf4JMmH+DYJh/g/SYe4P8mH+D/Jh/g/yYe + 3/8lHt//JR7f/yYe3/8lHt//Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yUe3/8mHt//Jh/g/yYf4P8mHt//Jh7g/yYf4P8mH+D/Jh7f/yUf4P8mHuD/Jh7g/yYf + 4P8mHd//Jh3f/yYg4P8lMOX/JELt/yNJ8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH8PIjR+9eI0bvAyNG + 7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlp + ZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlp + ZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlp + aABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn + 2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre + /ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE + +ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fCiYf4D0mH+CKJh7g0yYe + 4P0mHuD/Jh/f/yYe3/8mHt//Jh/f/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh/f/yYf4P8mH+D/Jh/g/yYe + 3/8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH9//Jh/g/yYe + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh3f/yYg4P8lMub/JELt/yNI8P8jR/D/I0fv/yNH7/8jR+//I0fv+iNG + 74gjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZm + YgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxs + bQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9v + cABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJo + egAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r + 6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY + 5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe + 4AcmHuA9Jh/giSYf39MmH+D9Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mHt//Jh7f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe3/8mHd//Jhze/yYi4P8lNOj/I0Xu/yNJ8P8jR/D/I0fw/yNG + 7/8jR/DEI0fwACNH8AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ + 0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJy + cwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGho + ZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5s + YQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh + 4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW + +ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf + 4AAmHuAAJh7gACYf4AAmHt8HJh7gPCYf4IAmH+DKJh/g+iYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7f/yYe3/8mH+D/Jh7g/yYf3/8mHt//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe3/8mHuD/Jh/g/yYe4P8mHd//Jhzf/yUm4/8lOur/I0jw/yNI + 8P8jRvD/I0fwviNH8AAjR/AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y + 5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlp + aABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdn + ZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZm + ZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc + 4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP// + /wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fBCYe4DUmH+B9Jh/gySYe4PcmH+D/Jh/g/yYe + 3/8mHt//Jh7g/yYf4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8lH9//JR7f/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHeD/Jh7f/yUt + 5f8kRe7/I0jw/yNH8IojR/AAI0fwACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej + /wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGho + ZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdn + ZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZn + ZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW8 + 9AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe + 3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AQlHt8zJR7fdSYe + 4MEmHt/0Jh7f/yYf4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mHt//Jh7g/yYe + 4P8mHuD/JR/f/yYf3/8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mHuD/Jh7g/yYe + 4P8mG9//JTXo/yNJ8M4jRu8sI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg + +ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdn + ZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGho + ZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpq + agBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe + 3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd + 3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi + 4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf + 4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe + 3wAmHt8CJh/gLyYf4G4mHt+5Jh/g7iYf4P8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7f/yYe + 4P8mH+D/Jh/g/yYf4P8lHt//JR/f/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jhzf/yUw5sEjSvAxI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf + 9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdn + ZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5v + bgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhn + ZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe + 4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd + 4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe + 3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf + 4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe + 3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4CYmH+BkJh7grCYf4OMmH+D/Jh/f/yYe4P8mH+D/Jh7f/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/JR/g/yUe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh7g/yYc36klMeYKI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe + 3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg + 9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdn + ZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGho + aABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGho + ZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf + 4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd + 4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf + 4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe + 3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf + 4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAWJh7gUSYf4IEmHuDAJh/g5SYf + 4P8mH+D/Jh7g/yYe4P8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P0mH+DjJh/gtyYe31YlHN4BJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf + 4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg + 9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGho + ZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdn + ZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdn + ZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe + 3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj + 4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe + 4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf + 4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gAiYe + 4BwmHuBRJh/geCYf4LMmHuDYJh/g8CYe3/8lH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4OUmH+DFJh/glCYf + 4GwmH+BIJh7gFyYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf + 9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGho + ZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdn + ZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdn + ZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe + 3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa + 3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe + 3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf + 4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe + 4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe + 4AAmHuAAJh7gACYf4AAmH+AZJR7fOyUe328mH+CYJh/guCYe374mH+C+Jh7gvyYf4JwmH+BWJh/gJSYf + 4AsmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGlo + ZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGho + aABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpq + aQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S + 8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf + 4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf + 4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe + 3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpq + aABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5u + bwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBw + cgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf + 4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK + +QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V + 3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf + 4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxr + awBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlp + aABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGho + ZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf + 4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC + 7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf + 4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf + 4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V + 3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe + 4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdn + ZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdn + ZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdm + ZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ + 3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf + 4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ + 3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe + 3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe + 4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG + 7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGho + ZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdn + ZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZm + ZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd + 4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN6 + 7AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe + 3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb + 8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf + 4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG + 7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlp + ZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlp + ZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlp + aABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn + 2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre + /ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE + +ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe + 3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG + 7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZm + YgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxs + bQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9v + cABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJo + egAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r + 6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY + 5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe + 4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe + 3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf + 4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK + 8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ + 0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJy + cwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGho + ZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5s + YQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh + 4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW + +ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf + 4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf + 4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf + 4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx + 5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y + 5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlp + aABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdn + ZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZm + ZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc + 4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP// + /wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf + 4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe + 3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc + 3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej + /wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGho + ZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdn + ZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZn + ZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW8 + 9AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe + 3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe + 3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe + 3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe + 3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg + +ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdn + ZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGho + ZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpq + agBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe + 3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd + 3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi + 4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf + 4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe + 3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf + 4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf + 4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf + 9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdn + ZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5v + bgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhn + ZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe + 4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd + 4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe + 3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf + 4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe + 3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe + 3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg + 9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdn + ZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGho + aABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGho + ZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf + 4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd + 4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf + 4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe + 3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf + 4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf + 4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg + 9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGho + ZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdn + ZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdn + ZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe + 3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj + 4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe + 4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf + 4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf + 9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGho + ZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdn + ZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdn + ZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe + 3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa + 3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe + 3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf + 4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe + 4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe + 4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGlo + ZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGho + aABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpq + aQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S + 8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf + 4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf + 4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe + 3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpq + aABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5u + bwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBw + cgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf + 4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK + +QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V + 3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf + 4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxr + awBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlp + aABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGho + ZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf + 4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC + 7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf + 4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf + 4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V + 3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe + 4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdn + ZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdn + ZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdm + ZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ + 3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf + 4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ + 3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe + 3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe + 4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG + 7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGho + ZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdn + ZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZm + ZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd + 4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN6 + 7AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe + 3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb + 8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf + 4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG + 7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlp + ZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlp + ZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlp + aABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn + 2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre + /ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE + +ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe + 3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG + 7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZm + YgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxs + bQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9v + cABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJo + egAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r + 6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY + 5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe + 4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe + 3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf + 4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK + 8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ + 0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJy + cwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGho + ZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5s + YQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh + 4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAhYaJAIWGiQCCjLsAMi81ADIx + VAAODgwAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW + +ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf + 4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf + 4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf + 4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx + 5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y + 5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlp + aABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdn + ZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZm + ZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc + 4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwAAcGHAAHBhwAEyeOAIWGiQCFhokAgoy7ADIv + NQAyMVQADg4MAB88vQAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP// + /wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf + 4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe + 3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc + 3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej + /wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGho + ZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdn + ZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZn + ZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3AAjHMEAIxzBACMcwQAHBhwABwYcABMnjgCFhokAhYaJAIKM + uwAyLzUAMjFUAA4ODAAfPL0ABg4uAAMDEQAAAAAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW8 + 9AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe + 3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe + 3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe + 3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe + 3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg + +ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdn + ZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGho + ZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpq + agBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe + 3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJRzgACYh4AAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEABwYcAAcGHAATJ44AhYaJAIWG + iQCCjLsAMi81ADIxVAAODgwAHzy9AAYOLgADAxEAAAAAAJmn2QCZp9kAJh7gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi + 4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf + 4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe + 3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf + 4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf + 4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf + 9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdn + ZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5v + bgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhn + ZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe + 4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBAAcGHAAHBhwAEyeOAIWG + iQCFhokAgoy7ADIvNQAyMVQADg4MAB88vQAGDi4AAwMRAAAAAACZp9kAmafZAJmn2QAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe + 3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf + 4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe + 3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe + 3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg + 9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdn + ZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGho + aABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGho + ZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf + 4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAHBhwABwYcABMn + jgCFhokAhYaJAIKMuwAyLzUAMjFUAA4ODAAfPL0ABg4uAAMDEQAAAAAAmafZAJmn2QCZp9kAmafZAJmn + 2QAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf + 4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe + 3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf + 4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf + 4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg + 9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGho + ZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdn + ZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdn + ZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe + 3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEABwYcAAcG + HAATJ44AhYaJAIWGiQCCjLsAMi81ADIxVAAODgwAHzy9AAYOLgADAxEAAAAAAJmn2QCZp9kAmafZAJmn + 2QCZp9kAmafZACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe + 4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf + 4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf + 9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGho + ZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdn + ZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdn + ZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe + 3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBAAcG + HAAHBhwAEyeOAIWGiQCFhokAgoy7ADIvNQAyMVQADg4MAB88vQAGDi4AAwMRAAAAAACZp9kAmafZAJmn + 2QCZp9kAmafZAJmn2QCZp9kAmafZACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe + 3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf + 4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe + 4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe + 4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGlo + ZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGho + aABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpq + aQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAHBhwABwYcABMnjgCFhokAhYaJAIKMuwAyLzUAMjFUAA4ODAAfPL0ABg4uAAMDEQAAAAAAmafZAJmn + 2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf + 4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf + 4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe + 3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpq + aABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5u + bwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBw + cgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf + 4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEABwYcAAcGHAATJ44AhYaJAIWGiQCCjLsAMi81ADIxVAAODgwAHzy9AAYOLgADAxEAAAAAAJmn + 2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAJh7gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V + 3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf + 4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxr + awBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlp + aABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGho + ZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf + 4AAmH+AAJh7gACYe3wAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBAAcGHAAHBhwAEyeOAIWGiQCFhokAgoy7ADIvNQAyMVQADg4MAB88vQAGDi4AAwMRAAAA + AACZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QAmHt8AJh/gACYf + 4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V + 3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe + 4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdn + ZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdn + ZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdm + ZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe + 4AAmH+AAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAHBhwABwYcABMnjgCFhokAhYaJAIKMuwAyLzUAMjFUAA4ODAAfPL0ABg4uAAMD + EQAAAAAAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn + 2QAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ + 3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe + 3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe + 4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG + 7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGho + ZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdn + ZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZm + ZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEABwYcAAcGHAATJ44AhYaJAIWGiQCCjLsAMi81ADIxVAAODgwAHzy9AAYO + LgADAxEAAAAAAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn + 2QCZp9kAmafZACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb + 8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf + 4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG + 7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlp + ZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlp + ZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlp + aABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBAAcGHAAHBhwAEyeOAIWGiQCFhokAgoy7ADIvNQAyMVQADg4MAB88 + vQAGDi4AAwMRAAAAAACZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn + 2QCZp9kAmafZAJmn2QCZp9kAmafZACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE + +ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe + 3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG + 7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZm + YgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxs + bQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9v + cABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAHBhwABwYcABMnjgCFhokAhYaJAIKMuwAyLzUAMjFUAA4O + DAAfPL0ABg4uAAMDEQAAAAAAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn + 2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY + 5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe + 4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe + 3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf + 4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK + 8AAjRu8AI0bvACNG7wCIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI////// + //////////////////////////iIiIiIiIiIj///////////////////////////////+IiIiIiIiIiP + ///////////////////////////////4iIiIiIiIiI////////////////////////////////iIiIiI + iIiIj///////////////////////////////+IiIiIiIiIiP///////////////////////////////4 + iIiIiIiIiI////////////////////////////////iIiIiIiIiIj/////////////////////////// + ////+IiIiIiIiIiP///////////////////////////////4iIiIiIiIiI////////////////////// + //////////iIiIiIiIiIj///////////////////////////////+IiIiIiIiIiP//////////////// + ///////////////4iIiIiIiIiI////////////////////////////////iIiIiIiIiIj/////////// + ////////////////////+IiIiIiIiIiP///////////////////////////////4iIiIiIiIiI////// + //////////////////////////iIiIiIiIiIj///////////////////////////////+IiIiIiIiIiP + ///////////////////////////////4iIiIiIiIiI////////////////////////////////iIiIiI + iIiIj///////////////////////////////+IiIiIiIiIiP///////////////////////////////4 + iIiIiIiIiI////////////////////////////////iIiIiIiIiIj/////////////////////////// + ////+IiIiIiIiIiP///////////////////////////////4iIiIiIiAiI////////////////////// + //////////iIiIiIAACIj/////////////////////////////////iIiIgAAIiP//////////////// + /////////////////4iIiAAAiI///////////////////////////////////4iIgACIj/////////// + ////////////////////////+IiIgIiP////////////////////////////////////+IiIiI////// + ////////////////////////////////iIiIj//////////////////////////////////////4iIiP + //////////////////////////////////d////4iI////////////////////////////////93d3d3 + //+Ij///////////////////////////////d0AHd3d//4iP//////////////////////////////dw + AAd3d3d/iI//////////////////////////////cAAAB3d3d3eIj/////////////////////////// + //cAAAAAR3d3d4iP////////////////////////////cAAAAAAAR3d3iI////////////////////// + //////cAAAAAAAAAd3eIj///////////////////////////cAAAAAAAAAAHd4iIj/////////////// + //////////wAAAAAAAAAAAB3iIiP////////////////////////9AAAAAAAAAAAAAeIiI////////// + //////////////9wAAAAAAAAAAAAB4iIiP///////////////////////wAAAAAAAAAAAAAAiIiI//// + ///////////////////3AAAAAAAAAAAAAACIiIj//////////////////////3MAAAAAAAAAAAAAAIiI + iI//////////////////////cAAAAAAAAAAAAAAAiIiIj/////////////////////cwAAAAAAAAAAAA + AACIiIiI////////////////////9wAAAAAAAAAAAAAAAIiIiIj///////////////////9wAAAAAAAA + AAAAAAAAiIiIiI///////////////////3AAAAAAAAAAAAAAAACIiIiIiI//////////////////cAAA + AAAAAAAAAAAAAIiIiIiIj/////////////////cAAAAAAAAAAAAAAAAAiIiIiIiI//////////////// + 9wAAAAAAAAAAAAAAAACIiIiIiIiP///////////////3AAAAAAAAAAAAAAAAAIiIiIiIiIj///////// + //////cAAAAAAAAAAAAAAAAAiIiIiIiIiI//////////////9wAAAAAAAAAAAAAAAACIiIiIiIiIj/// + //////////93AAAAAAAAAAAAAAAAAIiIiIiIiIiI/////////////3cAAAAAAAAAAAAAAAAAiIiIiIiI + iIiP////////////dwAAAAAAAAAAAAAAAACIiIiIiIiIiIj///////////93AAAAAAAAAAAAAAAAAIiI + iIiIiIiIiI///////////3cAAAAAAAAAAAAAAAAAiIiIiIiIiIiIiIiIiIiP////dwAAAAAAAAAAAAAA + AACIiIiIiIiIiIiIiIiIiIiIiP93AAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiIiIiIiAAAAAAAAAAA + AAAAAAAAiIiIiIiIiIiIiIiIiIiIiIiAAAAAAAAAAAAAAAAAAACIiIiIiIiIiIiIiIiIiIiIgAAAAAAA + AAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiIiIiIiIiIiIgAAA + AAAAAAAAAAAAAAAAAACIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiI + AAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAACIiIiIiIiIiIiI + iIiIAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiI + iIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAIiI + iIiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAA + AACIiIiIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIAAAAAACAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIgAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAiIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIgAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiI + iIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiI + iIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AACIiIiIiIiIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIiIiIgAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAIiIiIiIiIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiIAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIiAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIiIiIAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiI + iAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiI + iIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AACIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAiIiIiIiIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIiIiIAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAiIiIiIgAAAAAiIiIiIiIiAAAAAAAAAAAAAAA + AAAAAACIiIiIiIiAAACIiIiIiIiIAAAAAAAAAAAAAAAAAAAACIiIiIiIiIiIAIiIiIiIiIgAAAAAAAAA + AAAAAAAAAACIiIiIiIiIiIiIiIiIiIiIiAAAAAAAAAAAAAAAAAAACIiIiIiIiIiIiIiIiIiIiIiIAAAA + AAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiIiIiI + iAAAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiIiIiIiIAAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiI + iIiIiIgAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiIiIiIiIiAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiI + iIiIiIiIiIiIAAAAAAAAAAAAAAAAiIiIiIiIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAAAAAiIiIiIiIiI + iIiIiIiIiIiIiIiIiIAAAAAAAAAAAAAAiIiIiIiIiIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAAAiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIAAAAAAAAAAAAiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIAAAAAAAAAAiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIgAAAAAAA + AAiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIAAAAAAAAiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + AAAAAIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiCgAAACAAAAAAAEAAAEA + IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAAB7AAAAnwAAAJ8AAACZAAAAdQAAAEIAAAAJAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABfAAAAyAAA + APkAAAD/AAAA/wAAAP8AAAD2AAAA1AAAAIQAAAAbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAaAAAAswABAP8GBhj/Cwsr/wsLK/8HByD/BAQO/wABAP8AAAD/AAAA/wAA + AOQAAABIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJQABANcPDkb/Hhut/yUe + 3f8kHer/IRnr/xwU5f8ZFNH/FBGs/wkIYv8AAA3/AAAA/wAAAPQAAAAxAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAB4DBAjaGhiQ/yMb9v8eFff/GxTn/yUh1/9AP8n/Z2bD/3+Bx/9/gc7/aGbQ/zc4 + n/8BAx3/AAAA/wAAAMAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAAAUzRURr/8gGu7/Mi/O/1ta + wP+RksH/xcfP/+3u5P////b////+/////v////T/7u3p/5SUoP8aGRf/AAAA/wAAAD4AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAKSgoBwICBbY5O4r/cnLO/6qsxf/e39r////x/////////////////////P/09fb/5er3/97m + ///d4///4+r//8HG0P85Ny7/AAAAlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbm5tJH5+fmaPj4/Burq7/+/v7f////n///////// + ///4+v//1N35/6m48v99kuz/V3Hn/zxa5/8tT+v/J0vw/yJG8P8lSvD/QmX+/1pwzf8aHCG9AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1tbAJwb28yg4KDeqam + psLQz9D18/Pz///////////////9/93j9/+is/T/aIPx/z1e7/8iRu7/Fz3u/xY87/8ZPvD/HULx/yBE + 8P8hRO//IUbv/yFF7/8dQu//HkT6/y9My94zSZ0NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAbGxsB3Jycj6JiYmHr6+vz9nZ2v77+/r////////////+/ff/09fo/5ak3/9VcOP/J0rs/xY8 + 7v8XPe//HUHv/yFF7/8jRu//I0fv/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH8P8jSfD/Ikn2/yRJ + +Z8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsbGwIc3NzQYuLjI+0tLTY39/f/////f////////////Hy + 9P+/x+f/gpPd/0lk3v8kRuX/Fjzt/xo/8f8gRfD/I0fv/yNH8P8jR+//I0fv/yNG7/8jR+//I0fv/yNH + 8P8jR+//I0fv/yNG7/8jR+//I0bv/yU06P8kPuz/I0nw/yNH8GkAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABzcnI7jIyLj7S0 + tNni4eD////+////////////4+f2/6q36v9rgeP/OFfj/xxB6f8WPO//G0Hx/yFG8f8jR+//I0fv/yNH + 7/8jRu//I0fv/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH7/8jRu//I0fw/yNJ8P8lMeb/Jhre/yYf + 4P8kO+v/I0nw8yNG7y8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAB2dnUspaSlw97d3f////7////////////U3Pj/lafv/1dy6v8rTen/GD3t/xc9 + 8P8eQvH/Ikbw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH7/8jR+//I0fw/yNH + 8P8jR/D/I0bv/yNH7/8jSPD/JEHs/yYf4P8mHuD/Jh3f/yYf4P8kO+r/I0nwyyNH7woAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIuLjJj//////////8jS + +P+DmfP/SGfu/yJG7f8WPO7/Gj/w/yBE8P8jR/D/I0fv/yNH8P8jR+//I0bv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fv/yNH7/8jR/D/I0bv/yNH7/8jR+//I0fv/yNG7/8jRu//I0fv/yNJ8P8lM+f/Jhzf/yYf + 4P8mH+D/Jh3f/yYh4P8jQu3/I0jwgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAdnVzML/Aw/BohPn/GT7t/xY77v8bQO//IUXv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fw/yNH7/8jR+//I0bv/yNH7/8jR+//I0bv/yNH7/8jR+//I0fw/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fv/yNH7/8jR+//I0jw/yUr5P8mHN//Jh7g/yYf4P8mHuD/Jhzf/yUx5/8jSfC0AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW2WSNho/ + 8e0iRfH/I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH + 7/8jR+//I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0bv/yNH7/8jR/D/Jifj/yYd + 3/8mH+D/Jh/g/yYf4P8mHN//JS3l/yNJ8KUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIkXyhiNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH7/8jRu//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fv/yNG7/8jRu//I0fv/yNF7v8mI+H/Jh7g/yYf4P8mH+D/Jh/g/yYc3/8lM+f/I0nwhAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAjR/AZI0fv7SNH7/8jRu//I0bv/yNG7/8jR+//I0fv/yNH8P8jR+//I0bv/yNH7/8jRu//I0bv/yNH + 7/8jRu//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH7/8jSPD/JEHt/yYf + 4P8mHt//Jh/g/yYf4P8mHuD/Jhzf/yQ56v8jSfBYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjR++DI0fv/yNH7/8jR+//I0bv/yNG + 8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0bv/yNH7/8jRvD/I0bw/yNJ8P8kN+n/Jhze/yYf4P8mHuD/Jh7g/yYe4P8mHd//JDzr+yNI + 8CkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACNG7xUjR+/pI0fv/yNH8P8jRvD/I0fw/yNH8P8jR+//I0fw/yNH7/8jR/D/I0fv/yNH + 8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH8P8jR/D/I0jw/yUo + 5P8mHd//Jh/g/yYf4P8mHt//Jh7g/yYd3/8kOurMI0vxBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVAAAAVAAAAIoAAACkAAAAqAAA + AJgAAABrAAAAJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNH8HYjR+//I0fv/yNH + 7/8jRu//I0fw/yNH8P8jR+//I0fv/yNG7/8jRvD/I0bv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH + 8P8jRu//I0fv/yNH7/8jR/D/I0fv/yNI8P8kP+z/Jh7f/yYe4P8mH+D/Jh3f/yUp4/8mKuT/Jh3f/yYj + 4ZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAPAAAAhwAAAOYAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADzAAAAlAAAABIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAI0bvDSNH790jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNG + 7/8jRu//I0fv/yNH7/8jR+//I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNG8P8jR/D/I0nw/yUu + 5f8mHuD/JiDh/yYe4P8mHuD/JTHn/yUy5/8mH+D/Jh7gXgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMwAAANICAwP/Cwwx/xIRYv8YF3v/GRmA/xYU + cv8ODkX/BAUN/wAAAP8AAAD/AAAA0wAAADUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0fvWyNH + 7/8jSfD/I0jw/yNH7/8jR+//I0bv/yNH7/8jR+//I0bw/yNH8P8jR+//I0fv/yNH7/8jR/D/I0bv/yNH + 7/8jRvD/I0fw/yNH7/8jR+//I0fw/yNI8P8jQ+7/JiDg/yUs5f8mIuH/Jh3f/yYj4f8lNun/JTDm/yYe + 3+QlHt8SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AEYCAwPuExNf/yEdwP8mH+r/Jx/1/ycf9v8nH/b/Jx/2/ycg8f8jHs7/ExJj/wEBBP8AAAD/AAAA8AAA + AEkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjRu8BI0fvtSQ06P8kPOr/I0nx/yNI7/8jR+//I0fv/yNH + 7/8jRu//I0bv/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0rw/yUx + 5v8mJOL/JTPo/yYf4P8mHuD/JS/m/yU26f8mJOL/Jh3fbQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/CAkh8yAdsv8oIPb/Jh7x/yUd6f8lHef/JR3o/yUd + 7P8lHe3/JR3q/yYe6P8mIOL/GRtu/wMFAf8AAAD/AAAA5AAAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACNJ8REjRvAxIUTvLyFD7hgiR+8JAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAjSfAZJSzk0CYc3/8lM+f/I0jv/yNJ8/8kSfb/I0jy/yNH8P8jR+//I0fv/yNH8P8jR+//I0fv/yNG + 7/8jR/D/I0fv/yNH7/8jR/D/I0fv/yNI8P8jQe3/JSXi/yU06P8lLuX/Jhzf/yYr5f8lOOr/Jirk/yYd + 380mH+AHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJAoM + MOgkH9X/Jx72/yUd6P8lHen/JR3s/yUd6v8lHuD/JCDP/yMhvv8jIrP/IyGv/yIgsv8kIb3/Ih2y/xwn + n/8TJnLlFiyVKgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0nwGiNE7mYlOOmxIifk5iQm4v8wM+T9MjXl8CUq + 5N0kLubDJDXopSQ56YQkP+1jI0TuQyNH7ycjSfARI0nxAQAAAAAlMucNJhzfzyYa3v8mKuf/ITbU/x4y + vv8iQuT/JEn1/yRJ9f8jR/D/I0fv/yNH7/8jR+//I0fw/yNH7/8jR/D/I0fv/yNH7/8jSPD/I0bv/yUu + 5f8lMuf/JTbp/yYj4v8mHd//JSrl/yYp5P8mHd/8Jh7gOwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcJCijEJB/Z/yYe9P8lHe3/JR3q/yUf3f8kIcr/JCO9/yQi + uv8lIcP/JR/R/yYf3P8mHuP/Jx7m/yYe5v8nHuj/JyXt/yVI/f8lS/3xI0jyiSNH8AkAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0jwGyND + 7oElMObrJiHh/yIX3v9ISOb/cHnv/2lw7f+CivP/YGbr/yce3/8lG9//Jh3f/yYf4P8mIeH/Jibi+SUr + 5OklMefOJDfpsSQ964UlMuiWIiTl+hsbuv8cHKr/FhR8/xUUev8ZI5v/HzfK/yNG7v8kSvb/JEn1/yNJ + 9P8jSPT/I0fx/yNH8P8jSPH/I0ry/yNF7/8lKuT/JS7l/yU46v8mKuT/Jh3f/yYf4P8mH+D/Jh7g/yYe + 4IEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAUQeyMe + zP8nH/3/Ix7b/yIfvv8kIrf/JSK//yUhzf8mINz/Jh7j/yYe5P8mHuL/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHN//JTbp/yNJ8P8jR/D/I0fvvyNH7xIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkOuqJJiDg/yYc3/8mHuD/JBzf/zc15P86OeT/Ixzf/ysm + 4f80MuP/KSPg/yUe3/8mH+D/Jh7g/yYe4P8mHeD/Jh3f/yYc3/8mHN//Jh7g/yMf4f89Rej+PUfk/ykz + 2P8fKtD/GCG8/xUaof8VGZD/GCKb/x0wuP8fOMz/IDnP/yA60v8iQ+f/I0jx/yNE6v8kOOb/JiPj/yce + 5f8mMuv/Jirk/yYe4P8mHuD/Jh/g/yYe4P8mH+C2Jh/gAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQaGYzzIx7M/xwcj/8dHYv/Ix+6/yYf3P8mHuT/Jh7j/yYf + 4f8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYd3/8lK+T/I0jw/yNH7/8jR+//I0fvsiBE + 7wIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYv + 7A8mG95wJh/g1yYf4P8mH+D/Ixvf/yMb3/8mH+D/JR3g/yQb4P8lHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8iGd//PT7l/5Ge9v+Xo/j/ipT3/3mC9f9ka/P/TVTv/zhA6f8nL9r/HCXI/xci + uP8WIKX/FhyR/xcekP8YHo//FhaB/xcQgP8YEor/HRqp/yQh0v8nHuX/Jx/l/yYf4f8mHuD/Jh7g0iYe + 4BMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADA0spR8f + kv8fHZ7/Ix7E/yYf5P8nHub/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/Jh3f/yUk4v8jRu//I0fv/yNH7/8jR+//Gz/vYAAAAAD7/P8C////CgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmH+ALJh7fXiYf4MUmH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yEY3/9BQOb/l6X4/5yp + +f+eqvn/nqv5/56r+f+bqPj/lJ/3/4eR9f9ze/T/W2Lx/0NL7v8uOef/IC3a/xkoyv8XJbb/FSGg/xUb + i/8UFnj/FRJ4/xoUl/8hGr7/JR3a/yYc4N0mHN8iAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACYf4BYmH+BEJh7ggicg5MMmINn/Jh/h/ycf6f8mH+T/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/JiLh/yNE7v8jSPD/I0fv/yNH + 7/8YPe7ZAAAAAP///1Pc3NxoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAABAAA + AAAAAAAAAAAAAAAAAAAAAAAAJh7fASYe4EgmH+CwJh/g+yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh7f/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yUd3/83NuP/U1bp/2lw7v96hPH/h5P0/5Gd9v+Zpvj/nqz5/6Gt + +v+eq/n/maT4/42X9v97g/P/ZWzy/1Fa8f89Su7/Kzzo/x0w3P8bL8r/Gy2v/xgjlv8bH6LgJSzkJwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh/gJCYf4IcmHt+7Jh/g6SYf4P8mHuD/Jh7h/yYf + 4/8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYe4P8mIuH/I0Tu/yNI8P8jR+//I0fv/xM57vizwv9g19bO4xISEmIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAA4AgMFqAEBA9oAAADUAAAAiAAAABUAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAmHt80Jh/gmCYf4O0mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh7f/yIa + 3v8fF9//Hxff/yMc3/8pJOD/NDHj/0FC5f9VWen/b3jv/4iU9P+YpPj/nar5/56r+f+eq/n/nqr5/5mk + 9/+Pmfb/cnry/zo96/8lKer/JTHq/yQ55vAjQ+3BI0nwiSNJ8EgjSfEWAAAAAAAAAAAAAAAAAAAAAAAA + AAAmHd9DJh/g/yYf4v8nIOb/Jx/j/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/Jh/g/yYe3/8mH+D/Jh7f/yYe4P8mHuD/Jh/f/yYe4P8mH+D/Jh3f/yYm4v8jR+//I0fw/yRH + 8P8YPe7/S2z4+t/i5/EoJyT/AAAASgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAaAsMN/gTE2n/AwQK/wAA + AP8AAAD/AAAA2QAAAC8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh7gHyYe4H0mHuDdJh7g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/JR7g/yUc4P8jGt//IRjf/x8W + 3v8gGN7/JB7g/zg35P9WXOr/cHrw/4CK8/+AjfP/gY3z/4mU9f+Xo/j/jJj1/zAs4f8jGd//Jh3g/yYf + 4P8mJeL/JS7m/yQ46ekkQe27I0bvfiNI8DsAAAAAAAAAAAAAAAAmHeI+IxvOyR4Zrf8jHcz/Jx/k/ycf + 5/8nH+b/Jx/m/ycf5f8nH+X/Jh/k/ycf4/8mH+L/Jh/h/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/f/yYe + 4P8mHuD/Jh7g/yYf4P8mG9//JDTo/yNJ8P8jR+//HUHv/y5T8v/k7f/6W1lT7QAAAP8AAAA7AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAECBGIPEFD/FRR8/xgVk/8aGJT/DQ4+/wABAP8AAAD/AAAA3wAAABwAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACYe4A8mHt9iJR/fxiYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH9//JR3g/ygj4P9dZOv/bHHu/2x17/90fvD/eYXx/3aC + 8P9lbu3/QkXm/ywn4f8zLuP/JyDg/yYf4P8mH+D/Jh7g/yYd3/8mHN//Jhzf/yYf4P8mJeL/JS7l/yQ5 + 6qokQuwRAAAAAAAAAAAAAAAAHxyyVBgSh9MYE4z/HBam/x4Ysv8gGbf/IRq9/yEbw/8iG8r/Ix3Q/yUe + 1v8mH97/Jx/l/yYf4f8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jhzf/yUo4/8jR+//I0fw/yBE + 7/8dQ+//ydf//7a0qvcAAAD8AAAA/gAAADEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQ5Dw9X9xUTfP8YFY3/Ixza/ycf + +f8nIPD/GhmN/wMEB/8AAAD/AAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAmHd8BJh3fRiYe4K4mHuD5Jh/g/yYf4P8nH+D/IBnh/xwT4f8hGeH/Jh/g/yYf4P8mH+D/Jh/g/yYf + 3/8mHuD/HRXh/0dK6v+LmPb/oK75/5qm+P+YpPj/maT4/5qm+P+Uoff/U1jp/xwU3f8lHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYd3/8mHN//Jhve/yUr5NQjSfCnI0nxmCNJ8YUlRPVqHzDIkxki + l/sZIZT/Fx+P/xcdjP8XGoj/FxiE/xYVgP8WFH7/FhJ8/xYTg/8eGaz/Jx/h/ycf5v8nH+T/Jx/k/yYf + 4v8mH+D/Jh7g/yYc3/8mJOH/JEPu/yNI8P8jRu//GD7v/52x+//+/PL/ISEh/wAAAP8AAAD1AAAAJAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAERITTdcgIoj/GRac/yUd5P8mHuz/JR3n/yUd6f8oIPr/HRud/wIDBP8AAAD1AAAAHwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYh4SEmHuCAJh3f3yIZ + 4P9PS9j/a2rU/0pK2v8jHeD/Jh7g/yYf4P8mH+D/JR7g/yYf4P9XVtf/aWnS/zY13v9bYez/mKb4/5ai + 9/+UoPf/lKD3/5ei9/+Zp/j/TE/o/xwT3v8jGt//JB3f/yYd4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mG97/JSjj/yNH7/8jR+//I0fw/yNI8P8jSvT/JEn1/yRJ8/8jSPL/I0fx/yNF7P8jROn/IkLl/yE+ + 3f8gN8v/GCCV/xQRcv8aF5f/IRvC/yIcxP8jHMv/JR3X/ycf4/8nHeP/JiPi/yRB7f8jSfD/I0fv/xk+ + 7/9XdPP//////5aVlP8AAAD/AAAA/xkZGemOjo8VAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJCiWfQ0ai/yIfqv8kHOr/JR7s/yUd + 5/8lHej/JR3o/yUd6P8oIPn/EhJc/wAAAP8AAABEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACNJ8QkjRe4qI0TuTiQ+7HgkPOvKGivq/3N51P/e4L//ZmjV/xwU4f8mH+D/Jh/g/yYe + 4P8lHuD/IRrh/6Ckyv/c3b//WFfW/xUN4P9WXOr/mKX4/5up+P+ap/j/mqf4/52p+f+Wo/f/Vlrq/zIw + 4v8rJ+H/Ixzf/yQc4P8mH+D/Jh/g/yYe4P8mH+D/Jhzf/yUs5f8jRe7/I0jv/yNH7/8jR/D/I0fv/yNH + 8P8jR/D/I0fw/yNH8P8jR/D/I0jx/yNI8v8jR/L/JEr3/yVM/P8hPNf/GBeF/xUQdf8UEXH/FBFz/xQQ + c/8VEXr/GhSS/yMizv8kQe3/JEz3/yRK9f8jR/H/Fjzu/62+/P///vn/KCgp/wAAAP8AAAD/RERF4aen + pwsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAASklOk/89Pbz/IBjl/yYe7f8lHeb/JR3q/yUd5/8lHej/JR3n/yYe8v8gHLr/AgMD/wAA + AFYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjR+8dI0fwSSNH730jR++xI0fv3SNH7/sjSPD/I0jw/yNJ + 8P8bQ/P/T2vj/9HQwf+Ii8//HRfh/yYc3/8mHuD/Jh7g/yYf4P8cFOL/dHbS/9fYwP97fdH/HBTh/yEZ + 3/9BQeX/WFzq/1NW6f9TVun/Z27t/4yY9f+eq/n/kJ32/4uW9f9zffD/MCzi/yQc4P8mH+D/Jh3g/yYe + 3/8lM+f/I0jw/yNI8P8jR+//I0fv/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0bv/yNH8P8jR/D/I0fw/yRK + 9v8hP93/GSGZ/xUUff8ZIJn/HTC6/x84zf8fOM7/HzPD/xwrrv8XG4r/FRV9/xYZhv8aJKH/HzbI/yBC + 6/8nTfb/6vH//7u5tP8AAAD/AAAA/wAAAP+BgYHg////CwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUbHT/MYmbH/x8Y1P8lHev/Gxej/xgV + iP8hG8z/Jh7r/yUe6P8lHef/JR3r/yUf3/8FBRX/AQMKTgAAAAAlSv0CI0buJSNH71ojR++VI0fvyyNH + 7/IjR/D/I0fv/yNH7/8jR/D/I0fw/yNH7/8jR/D/I0fw/x9D8f83WOr/wMLF/8fIw/9BV+T/Hyjn/yYg + 4P8mHN//Jh3g/x4W4f9PTtj/0NHC/52fy/8hHOH/JR3g/yEZ3/8fFt//IBff/x8X3/8eF97/LSnh/3qF + 8f+eqvn/nan5/5qo+P86OOT/Ihrf/yYc3/8mIuH/JDzr/yNJ8P8jR+//I0bv/yNH8P8jR+//I0fv/yNH + 7/8jR+//I0fv/yNH8P8jRu//I0fv/yNH8P8kSvf/HjPB/xYTfP8aIaH/IT/b/yRJ8/8kSvf/JEn1/yRJ + 9f8kSvb/JEr3/yNH7/8gPNf/HCux/xcaiv8UEHX/Dw+A/0BQwf//////ZmVj/wAAAP8AAAD/AAAA/7Gx + sdj///8GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAABSkNGjv83NrL/IRnt/x8ZuP8UE3L/ExJw/xsXnv8mHu3/JR3o/yUd6P8lHen/Jh3p/wsJ + J/wVKod/Jkz/kCNH79AjR+/4I0fv/yNH7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNH8P8jR+//I0fv/yNH + 7/8jR+//IUXw/yZK7/+qssr/zMvC/4qb1P8ZQvP/I0Lt/yUy5/8mI+H/Ixjg/zMt3f+8vsX/urvF/zAt + 3v8jG+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8gGN7/MS7i/4CK8v+CjPP/Rkfn/yMZ3v8mHN//JSnj/yRD + 7v8jSfD/I0fv/yNH7/8jR+//I0fv/yNG7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH8P8jR/D/JEr2/xwv + t/8WEXz/IhnD/ycm6v8kRPL/I0jw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fx/yNJ9P8kSvf/I0jw/yE+ + 2/8PGpz/UlGZ//r8//8rLTj/AAAA/wAAAP8KCgr/39/fy////wIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBR2oQ0aj/x0apP8lHef/FhOH/xwe + ff8YGHb/HRew/yYe7v8lHej/JR3n/yUa5/8lJuf/Ij3K/iRJ8v8jR/D/I0fv/yNH7/8jRu//I0fv/yNH + 7/8jR+//I0bv/yNH8P8jR+//I0fv/yNG7/8jR+//I0fv/yNH7/8jRvD/HELx/5ai0P+jrs3/vcDG/z5e + 6P8dQ/H/I0nw/yNF7/8jNen/ISPj/56gy//Oz8L/SEba/yAV4P8mHd//Jh3f/yYd3/8mHeD/Jh3f/yYd + 3/8jGd//Jx7f/yYe3/8hG+D/JSvk/yQ56v8jR/D/I0jw/yNH7/8jR/D/I0bv/yNH7/8jR+//I0fv/yNG + 7/8jRu//I0fw/yNH8P8jR+//I0fv/yRK9/8eNML/FRF5/yMczf8nIOf/Jhzf/yUn4/8jRu//I0fw/yNH + 7/8jR/D/I0fv/yNG7/8jR+//I0fv/yNH8P8jSfL/I0z1/x5G9v9JYdr/VFVa/wAABP8AAAD/AAAA/zMz + NP/9/f2xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAEg4PQug5O53/Ghak/yMc2P8UE3X/ExJ2/xUTfv8jHdv/JR3q/yUb5/8lH+j/JDPr/yNH + 8P8kSff/I0fx/yNH7/8jR/D/I0fv/yNH7/8jR+//I0fv/yNH7/8jRu//I0fv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fw/yNH7/8YPvL/fY/X/5ek0P+dqM7/jZvT/xk/8v8jR+//I0jw/yFJ8f8ZPvH/eorX/9fV + v/9ocdf/GyLn/yUp4/8lJuL/JSbi/yYm4v8lKOP/JSzl/yUw5v8jNej/Izzr/yNE7v8jSPH/I0nw/yNH + 8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0bv/yNG7/8jR+//I0fv/yNH7/8jSPP/IkDf/xUV + e/8hGr7/Jx/n/yYf4P8mHuD/Jh3f/yRA7f8jSPD/I0fv/yNH7/8jR+//I0fv/yNH7/8jSfD/I0Tu/yQ5 + 6v8lMuf/JjDx/x4mrP8AAAD/AAAA/wAAAP8AAAD/ZGRk/////4IAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAERJX/ywvkv8ZFpv/JR3o/xkV + kv8UE3f/Hxm8/yYc7/8lHOf/JSvq/yNB7v8jSvD/I0fv/yNG7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH + 7/8jR+//I0fv/yNG7/8gRO//IUXv/yNH7/8jR+//I0fw/yBE7/8kR+//IETv/xM68f9bc9//r7TJ/1hz + 4f/FxsT/Olrq/xxB8f8sT/D/jaL3/xxB8v9Tb+L/1NC//4ub1P8bQ/L/I0jw/yNH7/8jR+//I0fv/yNI + 8P8jSfD/I0nw/yNJ8P8jSfD/I0jw/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0bv/yNG7/8jR+//I0fw/yRJ9f8ZI5r/GxWc/ycg5/8mH+D/Jh/g/yYc3/8mKOT/I0bv/yNH + 7/8jRu//I0fv/yNH7/8jR+//I0jw/yU26f8mIeH/Jhzf/yYc3/8nHeb/IRu7/wICBP8AAAD/AAAA/wAA + AP+Pj5D7////MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAABAG0SEmD/HByE/xYUg/8kHeP/JRvo/yMZ2f8mH93/JSvW/yQ96/8jSfD/I0jw/yNH + 7/8jR+//I0fw/yNG7/8jR+//I0fw/yNH7/8jR+//I0fw/yNG7/8jR+//Ikbv/zda8f8sT/D/HkLv/yNH + 7/8bP+//aIT1/zxd8f8kSfD/t8b8/97i8v/Cw8T/Olvo/6auzP+KmdT/EDfy/zlb8f+ouvn/HUHw/zVW + 6//DxMT/q7LL/yVJ7/8fQ/D/IUXv/yRH7/8jR+//Ikbv/yNG7/8jR/D/I0bv/yNH7/8jR+//IUXv/x9D + 7/8jR+//I0bv/yNG7/8jR+//Ikbv/x5C7/8iRvD/I0fv/yNG7/8jR+//Ikbv/yNG7/8kSfT/ID3W/xYT + f/8lHdb/Jh/j/yYf4P8mHd//JiHg/yRC7v8jSPD/I0bw/yNH7/8jR+//I0fw/yNK8f8lNOf/Jhvf/yYd + 3/8mH9//Jh7h/yYf4v8oIef/DQ4+/wAAAP8AAAD/Dg8O/9zc2akAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAkhESX/8UEn7/FRF8/yMb + 1P8mJd3/JTTW/yRB2P8jSer/I0nw/yNH7/8jRu//I0bv/yNH7/8jRu//I0fv/yNH7/8jR/D/I0fw/yNH + 7/8jR/D/I0bw/yNH7/8gRO//QGLy/3GO9v87XfH/HkLv/yJG7/+Lovf/I0bw/2qF9f/Z4v3/x9P4/9DP + xv9SbeH/Um3j/8XGw/9LaOz/Rmf0/3CN9v8YPe//IUbw/6auzP/FxcT/NFXp/zVY8/8vUvD/HUHv/yBE + 7/8nSvD/HkPv/xxB7/8kR+//Ikbw/xtA7/8rTvD/PV/y/yBE7/8iRu//I0fw/yJG7/8mSu//QmPy/yJG + 7/8jRu//IUXw/yRH7/9TdPP/I0fw/yRJ9/8bKKX/HBWi/ycf5/8mH+D/Jh/g/yYc3/8kN+n/I0nx/yNH + 8P8jR+//I0fv/yNH8P8jSvD/JDjp/yYd3/8mHuD/Jh/f/ycg5P8mH+L/IBjX/x8X6v8VE3b/AAAA/wAA + AP8sLCrz+fn/JgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAACjDw1Q/xQQev8aH4//JD3U/yNH5v8jSfD/I0jz/yNH8P8jRu//I0fv/yNH + 7/8jRu//I0fv/yNH7/8jR+//I0fw/yNH8P8jR+//I0fw/yNH7/8jR+//I0fv/yNH8P8dQe//Jknw/1+A + 9f8lSe//MVPw/22J9f8UOe7/ZYD0/y9S8f8ZQPD/q7PM/4OT1v8ZQPL/qbDI/9PV2v/F1P7/jKP3/xg9 + 7/8ZQPL/gpLW/9TRwP9NaOH/Tm/2/22K9f8wVfH/Vnf0/3+b9/+NpPj/S2vy/xk+7v8mSu//PFzx/3qT + 9v91j/b/Fzzu/zlf8f8mSvD/HULv/2WD9f+luPn/dI72/xY77v8/Y/L/LlHw/2eE9f8kSfH/IkXr/xga + jP8jG83/Jh/k/yYf4P8mHd//JSTh/yNG7/8jR+//I0fv/yNH7/8jR+//I0nw/yQ46f8mHt//Jh7g/yYf + 4P8nH+X/Ix3N/w8Mi/8uLpD/S0yv/xMVQP8AAAD/AAAA/xoZbPZFP+43AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK4PEkz/GR6Y/yJC + 5P8jSvX/I0fx/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH7/8jR+//I0fw/yNH + 8P8jR+//I0fv/yNH7/8jR+//I0fw/yNI8P8eRPD/M1ny/z5k8v9xjPb/UXP0/xU97/9lgvX/Kk7w/w00 + 8f+Il9P/qbHM/xU88/9het7/vsDD/1l37v+Mpfn/I0fv/xk/8v9cdeD/1tK//3KG2f8xVfT/hqH3/ytR + 8f8qTfD/I0fv/zVX8f+Qp/j/T27z/1Fw8//G0/z/t8f7/yRI8P8eQu//TnHz/ypO8P8oTPD/PWHy/xM4 + 7v9ph/X/IkXv/15+9f9nhfX/YoD0/yBG9P8gPNT/GRaT/yYf4v8mH+D/Jh/g/yYc3/8lM+f/I0nw/yNH + 7/8jR+//I0jw/yNI8P8lMuf/Jh3f/yYe4P8mHuD/Jh/j/yQe0/8KB3n/WVya/+Lk5P/x8+v/zc/Y/1db + ef8HCFX/JB3i/yAW4a0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAYMqkMFy2PyyNE4f8jRu//I0jy/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH + 7/8jR+//I0fw/yNH7/8jR+//I0bv/yNH7/8jR+//I0fv/yNH7/8jSPD/I0nw/yNJ8P8jR/D/I0Pt/yQ/ + 7P8aMun/f5T0/9fi/P8fNOj/GCvo/3GE8v+Mo/f/VXX3/22B3P+/wcX/K0zs/y1S8P/Jy8j/b4LV/11+ + +P82WvL/G0Lx/ztc6v/IyMP/mKTP/yJI8f9wi/b/H0Pv/yFF7/8jR/D/HULv/yFF8P9wjfb/Zoj1/xxC + 7/+csPj/QGHy/xY77v9qiPX/PV7y/xI47v9UdvT/RGXy/3iT9v8mSfD/NFnx/8bU+/9mgvX/GkH1/x0z + vf8dFqj/Jx/m/yYf4P8mHuD/Jh7f/yQ+7P8jSPD/I0fv/yNJ8P8kQe3/JSjj/yYc3/8mHt//Jh/g/yYf + 4P8nH+X/EQ2W/zw+iv/q6+v/8vHt/+rq6v/y8e//9fbz/4eJwv8ZGMb/JCzp8SYc3xoAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJEfzPiVK+9ElS/z/I0j0/yNH + 8P8jR+//I0fv/yNG7/8jR+//I0bv/yNH7/8jR+//I0fw/yNH7/8jR/D/I0fv/yNH8P8jR+//I0fv/yNH + 8P8jSfH/I0jw/yNC7v8kOOr/JS7l/yYm4v8mIeH/Jh7f/yAW3v9DROT/uMD1/4SF7f8WCtz/SEbk/9zi + +v+DiPD/qbDn/8jJwv9ITNv/Gx7n/6+z3f++wcT/Wm/r/0Na7v8dNev/JkHs/66zyf+4vMf/K03s/2WA + 9f8lSO//IUXv/xc87v8bQO//JErw/19+9f+owfv/a4j2/26O9v9LbvP/FDvv/1l89P/E1fz/Xnz1/ylQ + 8f+wxfv/gJn3/xg/7/8oUPH/Vnf0/zFW8f8iSff/Gyyv/yAYuf8nH+X/Jh/g/yYe3/8mIuH/I0Tu/yNI + 8P8jSfD/JDfp/yYg4P8mHd//Jh/g/yYf4P8mH+D/Jh/j/yIb0f8REHr/uLvQ//b18P/p6er/6unq/+rp + 6v/s6+v/9/fs/4CEw/8YN+X/JSzmSgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACNG73EjRu/5I0fw/yNH7/8jR/D/I0fv/yNH7/8jR+//I0fv/yNH7/8jRu//I0bv/yNH + 7/8jR/D/I0fv/yNH7/8jR/D/I0fv/yFF8/8dQ/X/HD7y/x4z7v8lKuX/JiHg/yYc3/8mHN//Jh7f/yYe + 4P8mH+D/JBrf/ywp4f8wMuL/oKby/0hF5f8WDt7/qKvz/9bZ+v/P1PP/1tfG/2lo0/8UCuD/UE/k/8jJ + xv+lrNf/S0vo/x8V3v8eF+H/iozO/87Pwv9CQ9v/VFTq/x8f4f+DjPD/q7r3/6Gw9v+suPf/mZ7x/zY8 + 5v9dYen/V17p/yMr5f8iKeT/LDjm/4mO7//Lz/j/NDvm/yUq5P8iJuT/JSnk/yYt5f8eKuX/IjPo/yVC + 8f8bJ6X/IRnD/ycf5P8mH+D/Jh3f/yYk4v8jSfD/I0Xv/yUu5v8mHd//Jh7g/yYf4P8mHuD/Jh/g/yYf + 4P8nH+b/Ew6v/2Nmpf/9/vf/6+vr/+7u7v/v7+//7Ozs/+rp6v/t7ev/7Ovm/0pk5P8cQvGRAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNH7wUjR/CaI0bv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH7/8jR+//I0fv/yJH8v8dQ/T/KkrZ/0lb + vv9bYLP/UU60/ykh2f8lHeH/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8vLeH/JyLg/zI14v85NeP/VVfn/xwT + 3v8sJ+H/pKXy/7O19P+2ucj/j5HN/x0X4v8YEeH/cHLS/8/Txv9ZXeP/HhXg/x0U4f9jY9X/1tfB/2Jh + 1f9BPub/JBvf/0M94/+mpvL/tr31/zIt4f8ZEd7/Ihnf/x8W3/8fFt//Jhzf/yYd3/8mHd//HRbf/yEc + 3/8mHd//JBvf/yYc3/8mHd//Jhzf/yYc3/8mHN//Jx/j/xsanP8iHMn/Jx/k/yYf4P8mHuD/JSHg/yUv + 5v8mJeL/Jhzf/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/ycf5f8WE5v/QUNX/8DAu//08/T/2tra/9fX + 1//n5+f/9vb2//b29v/59+z/lKbs/xc98N0jR/AKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAjR+8OI0fvsyNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH + 7/8jR+//I0fv/yNI7/8hRvT/Hj7p/1BhtP+anKn/uri8/7+/w/+oqqj/MS/G/yMb5P8mH+D/Jh/g/yYf + 4P8mH+D/Ixrf/0BD5P9EROX/Ly3h/zw85P82N+P/JBzg/yMc4P8bE97/FQ7g/5KTzP+ytcf/JiHg/yEY + 4f8vK97/wcTF/3+C0/8bE+H/IBjh/0JA2//NzsP/h4jP/zk55v8oIOD/IBjf/xcP3f9dXuj/g4nu/yMc + 3/8lHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/JR7g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuL/Gxeb/yMdzf8mH+P/Jh/g/yYf4P8mHuD/Jhzf/yUa3/8kG9//Jh7g/yYe4P8mH+D/Jh/g/yYe + 4P8mH+D/Jx/l/xcWfP8AAAD/FBQT/zk5Of8UFBT/EBAQ/ykpKf9ZWVn/mJiZ/+fm3v+6xO3/HUPv/SJG + 7zEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0bvDSNH77wjR+//I0fv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fw/yNH7/8jR+//I0fw/yNH7/8jR+//I0fv/yNI8P8jSvD/ID7y/yQy2f9tcaP/trW2/72+ + y/+kpbH/uLnC/4SGqP8jHtj/JR7h/yYf4P8mH+D/Jh/g/yYf4P8lHuD/JR7g/1lc6P99g+3/aW3q/yYf + 4P8lHuD/Jh/g/yYe4P8cFOL/c3PS/8nMw/85Ntz/Ihng/xsT4f95e9H/wMPE/zAs3v8gGOH/Kibf/7W3 + x/+oqcn/P0fl/ych4P8mHuD/Jh/g/xcP3f9hY+n/Vlfn/x8X3/8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf4v8bF5r/Ix3L/yYf4/8mH+D/Jh7g/yYe + 4P8jHN//KC/l/y476P8lHuD/Jh7g/yYf4P8mHuD/Jh7g/yYf4f8mH+L/FRRv/wABAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8BAQH/ysnE/9PY8f8mSO7/IUXwVwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNF + 7QEjR/CmI0fv/yNH7/8jR+//I0fw/yNH7/8jR+//I0fv/yNH7/8jRu//I0fv/yNH7/8jRu//I0fv/yNI + 8P8jSfD/JELt/yAr7P8pJs3/fn2f/7y9wP+5usr/t7jI/4eIjf+oqar/UFC2/x8Y5f8mH9//Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8lHt//IRnf/ych4P8iGt//JR3g/yYf4P8mH+D/Jh7g/x4W4f9VVNj/0tTC/1ZV + 2P8dFeH/Ihrg/zMw3f/FyMT/cnPS/xsU4v8fGOH/kpTN/7y9w/9/iuj/Pjzl/xoQ3v8jG9//GRDe/z49 + 4/9rcOr/HRXf/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8mHuD/Jx/j/xoXmP8iHMj/Jx/j/yYf4P8mHuD/JBvf/yk86v99lPf/YG/w/x8Y3/8mHuD/Jh/g/yMb + 4P8lHuD/Jh/h/ycf4/8VFHT/AAEA/wAAAP8AAAD/AAAA/0ZGR/+hoaH/RkZG/wEBAf/Ozcr/5uj3/y1O + 7P8gRPBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAB0WvA4fGbwhIhy+LiAavSglHL0QHjPdPh1D8P8fRPD/IEbx/yJH8f8iSPH/I0fw/yNH + 7/8jR+//I0fv/yNH7/8jR+//I0fw/yNJ8P8jSfD/JEHt/yUv5v8hG+X/KiPK/4WGoP++v8T/t7jI/7a3 + xv+2t8b/ubrJ/62vs/85NcH/Ihrl/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/JR7g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/IRng/zw63P/LzcP/eHnR/xsU4f8mH+D/GxTh/4GD0P++wcX/LSne/xoR + 4v9rbNT/y8zA/4mQ4P+8wfj/SETl/zEt4f9TUub/pKzy/0VE5f8gGN//Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8nH+T/GxeZ/yEbwv8nH+T/Jh/g/yYe + 4P8gGd//aXjx/6ay+v9YY+3/Hxjf/yYf4P8mHt//SU7n/ygj4P8lHuD/JyDm/xcUg/8AAAD/AAAA/wQE + BP+dnZ7/9vb2//r6+v/v7+//b29w/5uamP+vsr3/MFLq/yBE8G0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcA/JiDAeiEavaJISdHJeILp4X2H7PV7huv8cHnl+S4q + wuRTWtnmT17i/z1Q4P8zSOH/KUPk/yE/5/8dPun/HD/s/x1C7/8hR/H/I0nx/yNJ8P8jR/D/JD3r/yUs + 5f8mH+D/Ihjk/yoly/+Fh6L/vb/F/7e4yP+2t8b/trfG/7a3xv+4ucj/t7m8/05OtP8eFub/Jh/g/yUf + 4v8kHuT/JR7j/yUe4f8mHuD/Jh/g/yYf4P8mHuD/Jh7f/yYf4P8mH+D/Jh7g/yYf4P8kHOD/KiXf/7e5 + xv+cnsv/Hxnh/yYe4P8hGeH/OTfd/8nMw/9sbdT/FQ3i/0lH2v/Q0sL/bG7R/3p87//Z4Pr/1Nz6/8TJ + 9/9YWef/HBXe/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf + 4P8mHuD/Jh/g/ycf5f8bF5//Hxu2/ycf5f8mH+D/Jh7g/yIZ3v8+Ruj/l6j5/19o7f8dFd7/Jh/g/yIa + 3/9zffD/QEDl/yEZ3/8nIOn/HBic/wIDAv8AAAD/X19f//z8/P/s6+v/6enp//Hw8f/j4+P/EBAN/0ZH + VP83VvL/IUfyaQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYh + v84fGb3/GxW7/01R0/90gOb/aG7e/29y4P9eZd//LCjH/2Vr4f+Ci+v/g4rq/4CJ6P+Aiun/fonr/3OB + 6v9fbuf/R1nj/y1D4f8iPej/JTLo/yYl4v8mHd//Jhzf/yMb4/8nI9D/g4Wh/76/xf+3uMj/trfG/7a3 + xv+2t8f/trfG/7a3x/+9vsf/enyn/yMe2P8iHen/KB/b/y0gz/8rINP/Jh/g/yMe6v8hHuz/Ix7p/yQe + 5P8mHuD/Jh/f/yYf4P8mHuD/Jh/g/yUe4P8gGuH/nJ7L/7q8xv8sKN//Ixvg/yYf4P8cFeH/iYzO/7q8 + xv8oI9//LCje/72/xf+Ymcz/FA7f/zAs4f8+O+T/Ix7g/x0V3v8mH+D/Jh/g/yYe3/8lHuD/Jh/g/yYf + 4P8mHt//Jh/f/yYf4P8mH+D/JR/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jx/m/x0Zqv8dGaf/Jx/m/yYf + 4P8mH+D/JR3f/yEh4f+FlPb/lZ/2/zQy4/8jGt//IBnf/3R98P9nbu3/Hxbe/ycf5v8iHMP/AAAN/xQU + Ef/R0NH/8fHx/+np6v/p6en/9vb2/7CwsP8AAAD/ZGh9/y9P7/8gPe9XAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIBrDYF5e2dCoqu35s7Tx/7i58v/ExPb/0dD5/1BN + 4/8cFN3/IRrb/yMd2v8mH9j/KiXW/zMw1f9ISdv/ZGnh/3iA5v+Lle7/bnbf/yIbyP8mHOD/Jh3f/yYf + 4P8lHuH/IRvc/3Byo/+9vsL/t7jI/7a3xv+2t8f/trfG/7e4x/+3uMf/urzM/7q+z/+us7z/dXeT/1Es + Y/9eJjv/Yygw/2EoM/9bJ0X/USZk/0Mki/83IrD/LCDR/yMf6P8iHuv/JR7k/yYf4P8mH+D/Jh/g/xwV + 4f99ftH/z9HC/0NB2/8gGOD/Jh/g/yAY4f8/Pdz/zc/D/2Zn1P8WD+P/nJ7L/7i7xv8rJt//IRjg/yEZ + 3/8lHd//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8nH+X/IBu7/xoXlv8nH+T/Jh/g/yYf4P8mHt//Ih7g/1lw8f9qfPL/MzDi/yMb + 3/8gGN7/bHTv/4uX9f8oIuD/JR3h/yYe4/8KClH/Rkc///X09P/q6ur/6enp//Lx8f/39/f/RENE/wIC + AP9zeaH/H0Hr/zAz5tQpH99NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAmJz3A+Lh/ynU1P1f0tL8mcXI+tHT1vz6ZmXq/xwS3v8mHeD/JR3g/yUd4f8lHeH/JBvh/yEZ + 3/8fFtv/IRnX/ygi1/8wLNz/Jh/c/yYf4P8mH+D/Jh/g/yEZ5f9EQ7f/srO0/7m6yv+2t8b/trfH/7a3 + xv+4ucj/tbbF/7W4yP+fn6n/e2Zo/25JSP9rOjH/aCkb/2coHv9mKCH/Zygg/2goHf9pKBn/aikY/2gp + Hv9iKDD/VCZa/z8jmP8rINL/Ih7s/yMe5/8mH+D/HRXh/19f1v/W18H/YWHV/xwV4f8mH+D/Jh7g/x0W + 4f+TlM3/tLfH/x4Z4f9yc9L/ztDD/0I/2/8hGOH/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4/8jHc7/GBWL/yYf + 3P8mH+H/Jh/g/yYe3/8mHuD/H0Du/x016v8kGt7/Jh/g/yAY3v9kbO3/n635/0RF5v8gF9//JR3m/xwZ + rP+OkZv/8vHt//Ly8v/39/f/19fX/2RkZP8AAAD/MC8m/1xnuv8VNez/ZnDu/4uX9f9BQuXAIBjfMwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmKTzA2p3 + 6ikwM+JeIyjk4CUn4/8lKeT/JSXi/yYe4P8mHuD/Jh7g/yYf4f8mHuH/JR3h/yQc4P8mH+H/Jh/g/yYf + 4P8mHuH/IRrc/3t8qP++v8f/trfG/7a3xv+2t8b/uLnJ/7Kzwv+Qk5v/gHV5/2QxLf9hIBv/ZCIe/2Ul + If9lKCT/Zigk/2YoJP9mKCT/Zigk/2YoJP9lKCT/Zigj/2coIP9pKRr/aSkZ/2EoMv9MJXL/MiG//yMe + 6f8dF+n/RUPb/8/Rwv+Ehs//HBXh/yYe4P8mH+D/Hxfh/0dG2v/P0cL/W1rX/0VD2v/S1ML/YGDV/x0U + 4f8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYe4P8mHuD/Jh7f/yYe + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf3/8YFoz/IxzK/ycf4/8mHuD/Jh7f/yYe3/8kP+z/I0Ht/yYe + 3/8mHuD/IBje/2Nr7f+hrvr/cHjv/x8Y3v8mH+L/GxTU/1JTq//09O//sK+u/1VVVv8QEBH/AAAA/w0M + B/94eIn/Jz3H/yE68P8sJeD/Vlrq/3uG8f9cYuvtJh/gPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiSfHUI0fv/yQz5/8mIuH/Jh7g/yYf + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yQc4/8sJ83/nZ+t/7u8yf+2t8b/trfG/7e4 + x/+0tcT/jpGZ/3lsb/9jKCP/ZSQg/2UoJP9mKCT/Zikk/2YoJP9mKCT/ZSgk/2YoJP9mKCT/ZSgk/2Yo + I/9mKCL/Zigi/2YoIv9mKCP/Zygf/2opGP9mKST/UyZg/zIetv8uLOf/vL/K/6aoyf8kHuX/Jh7m/ycf + 5f8mHuP/Hxnj/5udzf+srsn/MjDe/8HDxP+Fh8//HBXi/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYe4P8mH+D/Jx/m/xwY + of8eGa3/Jx/m/yYf4P8mHuD/Jh3f/yQ66v8jS/H/JTDm/yYc3/8gGN7/Y2vt/5yo+P+Vovf/Pj7l/x8W + 3v8oIeX/ZWnJ/4mNu//Awcb/AwMA/wAAAP8TEgr/e3qA/0BGq/8bP+f/Ji/n/yQa3/8fFt7/Ihvf/1JW + 6f81MuOfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJCnkSyM96/glKOP/Jhzf/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yYf + 4P8mH+D/Ihrk/zczxP+qrLL/ubrJ/7a3xv+2t8b/ubrK/5KTm/+Fg4j/ZjEt/2UlIP9mKCT/Zigk/2Yo + JP9mKCT/ZSgk/2YoJP9mKCT/Zikk/2YoIv9qJxn/aScc/2coJP9lKCf/YSQk/2EjIf9iIx7/YiIc/2Qi + Gf9nIw//ZyMV/1QiUf+yqLH/yszI/zAssf8ZEqf/IRm9/yMbzf8dFNb/TUzT/9DSw/9ZWtb/mpvM/6qs + yf8kHuT/Jh7m/ycg5v8nH+X/Jx/k/yYf4v8mHuD/Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8nH+T/IhzG/xkWkv8nH+P/Jh/g/yYf4P8mHN//JTLn/yNK + 8P8jRu//JSjj/x8V3f9jaez/m6j4/5ml+P+BjPP/KCPg/zAs4f/8////e3yK/y4xV/8aGjz/DAwf/1FU + gv87R7b/GTnf/yRH8ugmIuGUJh7f5CYf4PslHd//Ixrf8yYf4EEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUd3wclHuDXLkLq/yYf3/8iGd//Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8hGeX/Pj2//6+xtv+4ucn/trfG/7i5 + yP+srbr/i4+W/4Brbv9hIBv/Zigk/2YoJP9mKST/Zigk/2YoJP9mKCT/Zigk/2YoJP9mKCL/ZSko/0s1 + fP83Prf/MEDJ/zNEzf9PWcr/WmHG/19jwf9fYLn/X1uv/2BXpv9USZv/QTOG/2hvuv+Hm9z/MkCs/xUf + mf8XIJj/FBuW/xIVkv8ND43/mZy1/6Ciuv92eKn/zMvD/ywpnP8XEZ7/Hhiu/yEbvv8jHc3/JR7b/ycf + 4/8nH+b/Jx/l/yYf4v8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+L/GheV/yIcyP8nH+T/Jh/g/yYd3/8lJuL/I0fv/yNI8P8jRO7/HTfr/1Zr8f+cp/f/lJ/3/5+s + +f9PVOn/YF/q/////f9BQDz/AAAA/x4nrP8kPeH/Hz/l/x5C8P8kS/T/JDHmoQAAAAAmH+AQJh/gKiYf + 4DkmH98lAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJh7gIyEa3/dKT+j/c33w/zk45P8gGN//Hxfe/yEY3/8iGt//Ixvf/yQc4P8lHeD/JR3g/yUe + 3/8mHuD/Jh7g/yEY5f9EQ7v/tLW6/7i5yP+2t8b/ubrK/6ChrP+RlZ3/dFFR/2MiHf9mKCT/Zigk/2Yp + JP9mKCT/Zigk/2UoJP9mKCT/Zigk/2kmGf9QMmj/HEv//x9J+v8bRPf/QGP4/56s//+erP//nav//52r + //+cq///nav//5ip//8/Y/7/Fj71/xpA8v8iSPX/I0j0/yxQ9P9DYfL/TWjt/0hi5f9TauT/a4Li/1Jo + 0f+El9n/MD+q/w0Wj/8QFIT/EBF8/xIQef8VEX//GBOO/xwWpP8gGb3/JB3S/yYf4P8nH+b/Jx/l/yYf + 4v8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf5f8hG8H/GheY/ycf5f8mH+H/Jh/g/yYd + 3/8kO+v/I0nw/yNI7/8gRvD/MVPx/5Cd9/+cpvj/kZz1/ywp4f+2uf//sbGn/wAAAP8BAAD/Hzu2/yRL + //8jSPL/I0jw/yNB7f4mIOA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmHuA8Jh7g/yEZ3/9GR+b/fory/3R9 + 8P9XW+r/R0fm/z085f81MeP/MCzi/ysn4f8mIuD/JB3f/yMb3/8iG9//GxTk/0JBuv+0trr/uLnI/7a3 + xv+6u8v/n6Cq/5OXoP9uREP/ZCQf/2UoJP9mKCT/Zikk/2YoJP9mKCT/Zigk/2UoJP9mKCT/aCgc/1Ys + Vf8iPfH/IEP4/yFE7/8pS+//Umzy/1138/9eePP/Y3z0/2N89P9jfPT/Z3/0/zla8v8fRe//I0nw/yNJ + 8P8eRfD/SGjz/5Gg+P+eqfn/oqz6/5yn+v+Snvr/jJv7/3yO+P93i/v/aH73/1Vu8P9CXOX/MUnX/yI5 + xf8YKrH/EyCd/xEZjv8TFYr/FxWR/xwWpP8hGb3/JBvS/yYd3v8nH+T/Jx7l/ycf5P8mH+P/Jh/i/yYf + 4f8mH+D/Jh/h/ycf6P8dGab/Hhqu/ycf5f8mH+H/Jh3f/yUm4v8jR+//I0fv/yNH7/8dQu//Smby/42d + 9/9HVOv/LTfp/+bq+f8zMi//AAAA/wAAAP8aNKr/JUr7/yNH8P8jSO//JSnjogAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACIa31ceFt//HBPe/xcP3f8jH+D/Vlrq/3qF8f9+ivL/eYbx/3uF8f96hPH/fYny/4SQ + 9P+FjvT/fYby/3iB8f9xe/X/cXrN/6mpsv+5usr/uLnJ/7m6yv+io6//lJii/3BLSv9kIhz/Zigk/2Yo + JP9mKCT/Zigk/2YoJP9lKCT/ZSgk/2UoJP9mKST/aCce/1gmT/82JbP/JCPn/yAj7f8eIOT/HiHi/x8i + 4/8eJOP/HiXj/x4l4/8eJuT/Iyrk/yUs5P8lLeX/JS/l/yQ06P8iOer/K0ft/z9b8P9TbvP/aID1/3uP + 9v+Km/f/lqP4/56o+P+iq/j/oqv5/56p+/+Wo/z/iZr9/3iN/P9kfPr/Tmr0/zhW6f8mQ9j/GjPE/xUo + s/8WIan/GR2o/x0brP8bFbb/HhbC/yAXzv8iGtf/Jh7e/yYe4v8mH9//JB3S/xwYpP8UEnT/HBik/yYf + 4v8mH+P/Jhzf/yUx5/8jSfD/I0fv/yNH7/8fRO//J0vw/xQ67v92k///trWu/wAAAP8AAAD/AAAA/xgw + mv8lS/z/I0nw/yUw5tkmHN8VAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAODPjalFQ5/90dO3/kpPx/5uf + 8/8xLeH/HRbe/yQe4P8lH+D/JyHg/yok4f8xLuL/REPm/2x07/+KlvX/kZ/2/5ek+v+Snen/mpuq/7e4 + xv+pqrf/uLnJ/6ytuv+RlJz/joSL/18lIP9lIx//Zicj/2YoJP9mKCT/ZSgk/2UoJP9lKCT/Zigk/2Yo + JP9mKCT/aSkb/2gnHf9XJFD/Oh2g/yQY1/8dFOL/Ixng/yYc3/8mHN//Jh3f/yYc3/8mHd//Jh3f/yYc + 3/8mHN//Jhzf/yYd3/8kHd//IR7h/x4i4v8eKOX/IjTo/ylA6/80UO//QmHx/1Rw8/9lfvX/dYr2/4WW + 9v+Sn/f/nKb4/6Kq+P+iq/n/m6b6/42c+v95jfv/YXv7/0hn9v8yU+z/HDzd/21/0v9CUrr/KjSl/ycp + lf8WE4r/FxKH/xcSg/8VEnn/FBJ1/xQTev8TEnP/Gxea/yYf3/8mH+T/Jh3f/yQ46v8jSvD/I0fv/yNH + 7/8hRe//GT/w/7rK/v9QTkX/AAAA/wAAAP8AAAD/GTCP/yVM/v8lMufhJhzfKwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAADIyfqJ1NX8/9ra/f/b2/3/4uP+/1RR5/8eFt7/JR7g/yUe4P8lHuD/JR3g/yQc + 3/8hGd7/IBjf/yci4P8uK+L/Mi/i/zY26P95frL/r7C1/4yMk/+3uMf/ubrK/5GRmf+fo67/j4aN/2c4 + NP9hJB7/YSId/2EhHP9iIRz/YiEb/2IhHP9iIRz/YiEb/2IhHP9hIhz/YiUf/2YsHv9rPC7/alRx/1xb + yf81MuP/IRrn/yQc4/8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe3/8mHt//Jh3f/yYd + 3/8mHN//JBzf/yId4P8gIOH/Hibk/x0u5/8gOer/JUTu/y9R8P88XvL/TWvz/1959P9zh/X/hpX2/5ah + 9/+gqvj/oar4/5ij+P9+kPn/ws3//4+l/v+Sqvz/aYXv/xMy1v8WK7r/FB+b/xUWg/8UEHX/FBBz/xQS + ef8TEnT/GRaQ/yUe1/8nHuX/Jh/f/yQ36f8jSfH/I0nw/x5D7/89YPr/wMXQ/wgHBf8AAAD/AAAA/wAA + AP8UJIP/Hif0wyAW3yMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANfW/JPY2Pz/2tr9/87Q + +/+0t/f/RkPl/yAY3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/JR3f/yQc4P8dE97/OTjo/4eS + 3/+bnKb/u7zK/7a3x/+3uMj/tLXE/4qKkf+Mj5X/lpih/5KLk/+HeH3/gW1w/3xmaP98ZGb/eF1e/3ph + Y/98ZWf/fmls/4Z1ef+QiZD/oaCr/6+ywP+5vMX/vL3A/6Cjuf9hY7HyLirO3CQd4v8mH+D/Jh/f/yYf + 4P8mH+D/Jh7f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh7g/yYd3/8mHN//Jhzf/yYd + 3/8lH+D/IyTi/yAq5f8eMun/HDrs/x5C7/8kSvD/MFXx/0Ni8v9Zc/T/c4b1/4WU9v/Gzfv/ys/7/8jO + +/+qtfr/cYb5/1l1+/8+X/f/J0jo/xgyy/8XIqL/FhV+/xQPc/8TEXH/FxWF/yMdyv8nH+f/Jh3g/yUr + 5P8kPuz/Fzzv/4Ca//+Eg3r/AAAA/wAAAP8AAAD/AAAA/0JCdNFdVeMIAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAArrT2mZ6g8/9sbev/QD7k/yQe4P8iG9//Jh/g/yYe4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/JBvf/y4r4f+EkPP/mab+/4eNw/+ioqn/u7zK/7i5yf+4ucj/t7jH/5qb + pf+goaz/vL/Q/7u/0P+6vs7/ub7O/7q/z/+6v9D/vMHS/73C0/+7v9D/trrJ/6yvvf+foKvtj4+WzYCA + hKFzc3Vzbm1pRnNzXR9GR6kJIhrnQCYf4KEmHuDtJh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mHuD/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHd//Jhzf/yYc3/8mH+D/JSPi/yQr + 5P8hMuj/Hjrs/xw/7v8eRfD/G0Lv/5it+f+QpPj/q7j5/6Kt+P+cpfj/o6v4/5ql+P+ImPj/Z4D7/zFV + 9/8dPeH/HS20/xcZh/8TD3D/FBF1/x4Zrf8mH9//Jx3l/yYe4P8eIeT/tb30/zc2Lf8AAAD/AAAA/wAA + AP8AAAD/enp01NbWzgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkIN+fFQ/d/xEJ + 3f8ZEt7/IRvf/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8fF979aG/ulZ2q + +YyUoPielKD8t4ePyMySk6Dnr7C8/7q7y/+7vMz/vr/Q/72+z/+6u8v/u7zM/7u8zP+4ucn/sbLB/6eo + s/+YmaPnioqQxHx9f5hxcXJnamppPGZmZRkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYe + 3xwmHuBsJh/fwiYe4P0mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mHeD/Jh3f/yYc3/8mHuD/JiPh/x8l5P87T+z/tMX7/ztc + 8f+Qqvn/M1fx/0Fg8v9pf/T/jJr2/56o+P+jq/j/kZ33/z1d9P8gR/f/I0Xr/x4zxP8YHpL/FBFw/xcS + g/8gGrr/IBfd/Tk26/6/wc//BwcE/wAAAP8AAAD/AAAA/wAAAP+Pj5D1zc3NHAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAADw646ljY+r/hYfv/6Ol9P+UmfL/KSTh/yQd4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yAX3p0AAAAAAAAAAAAAAAAAAAAAAAAAAHZ5mRVubWhlhYaKt5aX + n+Cgoav2oaKt95ycpvCTlJzaiIiOwHx8f5ZxcXJmaWlpNWZmZRMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh/gMyYf4IcmH+DZJh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh7g/yYe4P8kGt//HBPd/6Sn8/9rbev/X2Tr/4uX8/8ZL+n/Hj7t/xxD7/8qUPH/TGnz/3OH + 9f+Xovf/dIj1/x5D7/8jR/H/JEr2/yRI8v8gOtP/GiWf/xQWfqQeGsQjm5n8dLKzrP8AAAD/AAAA/wAA + AP8AAAD/CgoK77CwsPTJyck/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAxsn5uNna + /f/a2v3/3t79/9TV/P84NeP/Ihrf/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+DVIhvfEAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ2dlDWhoZyNpaWclZmdmGWZmZQkAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACYf4AkmH+BJJh/goiYf4OkmHuD/Jh/g/yYf4P8lHuD/JR7g/yUd + 4P8lHeD/JR7g/yUe4P8lHeD/JR3g/yUd4P8kHd//JBzg/yQc4P8iGt//HBTe/yMc3/98gO7/i4/w/0ND + 5P+xuPb/Pzvj/yEX3v8mIeH/JSrk/yMz6P8dO+z/H0Tv/zBU8f8yVfH/Ikbv/yNH7/8jR+//I0fw/yRJ + 9f8kSvX/IUPp1B9B627S3P+LkpGJ/wAAAP8AAAD/AAAA/wAAAP8rKyuHx8fIxcXExWoAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADNz/vH29v9/9bX/P/Cxfn/mJvy/zAt4f8jHN//Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g8iYe3zAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJh/gFyYf4GEmH+C4Jh/g9yMe3/8qJeD/KSTg/ygj4P8pJOD/KCPg/yol4f8tKOH/LCfg/y0n + 4f8wKuH/Mizi/zs44/9aWuj/jJPw/5CX8P9vdOv/l53y/09O5v8eF9//Jh/g/yYe4P8mHN//Jhzf/yYf + 4P8lJ+P/IjHn/yE97P8jSO//I0nw/yNI8P8jR+//I0fv/yNH7/8jR/H/Fj3w/6e6//9qZ17/AAAA/wAA + AP8AAAD/AAAA7TY0LRXOzs2rxMTElgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKKn + 9MuGh+//U1Ln/y0n4f8aE97/JBzg/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P4mH+BVAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnI+EnhY/veW92 + 68tocer/dHzs/3Z/7f97g+3/hY3v/3+I7v98iO7/g5Hv/4OQ7/+Hju//i5Xw/5Wg8v+PmPH/e4Hu/11e + 6P8oIuD/Hxff/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYd3/8mHN//Jh/g/yUo4/8lNej/I0Ht/yNJ + 8P8jSfD/I0fw/yNG7/8YPu//rsD//1pYUP8AAAD/AAAA/wAAAP8DCB6DAAAAAMzMy4LExMTAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHRffyBUO3f8jHt//Ozjj/0E/5f8mIOD/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/gdwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQz/kAZ+i8ziAge6NVVXn2lxc6f9cW+j/Wlno/1JU + 5/9TV+f/UFXn/0xP5v9LSOX/QD7k/zMt4v8iG9//Hhbf/yUd4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh3f/yYc3/8mIOD/JSrk/yQ56v8jRe7/I0nw/xc/8P+yxf//oZ+W/wAA + AP8AAAD/CAwf/yFD39QfRf1rhZPVbtDNv9kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABaWuimoKL0/7q8+P/P0Pv/u7/4/y4p4f8jHN//Jh7g/yYf4P8mH+D/Jh/g/yYf4JIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAVDd0JHxjfSB8X350fF9/mIBjf/yAX3/8gFt//IBff/x8X3/8hGd//JBzf/yYe + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mHN//Jh3f/yYj4f8lMOb/Fzbr/6i5/v/v7ub/EBAP/wAAAP8cNKL/JUv//x9D8P8mSu30o63Y8Ieb + 5S4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKCv9Fne3f3/19f8/9zc/f/S1fv/Ojjj/yEZ + 3/8mHuD/Jh/g/yYf4P8mH+CoJh/gAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYf + 4BQmHuBYJh7gryYf4PAmH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh3g/yYc3/8ZE97/oKXz//// + //9OTUT/AxJE/yVM+/8jR/D/I0fv/xI47/+Ln/n/m7D67XON9owgRO8gAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAZnDqDLS3997GyPn/nZ7z/2Jh6f8oIuD/JR3g/yYf4P8mH+D/Jh/gsiYf4AcAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh7gHyYf4GgmH+C6Jh7f9iYe + 3/8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/xkS3v+jpvL//////5ubpf8WIsL/JEP1/yNJ8P8jSfD/FTvv/4yf + 9/+Zrvn/dI72/x5C7+0jR/CHI0fwGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMi7hcDMw4v8fGN//HBTe/yUe + 4P8mH+D/Jh7g/yYe4KwmHuAJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmHuAlJh/gciYe4MImH+D4Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mH+D/GhLe/3Z3 + 7P//////v8P6/xwU4f8lIN//JS7l/yQ/7P8ROO7/prj5/5Sr+P9qh/X/HEDv/yNH7/8jR/DlI0fvcCNH + 7wgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAjG98FIhrfgyYe4OEmH+D6Jh/g+yYf4OomH+CGJh/gAwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACYe4CkmH+BrJh7fryYe4OgmH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8jHN//LCfh/7q99f9oaOr/HRXe/yYe3/8lHN//Fg7d/2Jt + 7P/v9v//eZL2/2uI9v8aP+//I0bv/yNH7/8jR+//I0fwyiNH7z8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh/gECYf + 4CkmH+AqJh/gGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh/gFiYf + 4FImHuCfJh/g4iYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8jHOD/IBnf/yIa3/8lHd//HRTe/xYP3v9mZ+n/9fX9/46T8P+irPT/XHHw/xs/7v8jSfH/I0fw/yNH + 8P8jR+//I0bv+yNH74MjR+8FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmHuAQJh/gSiYf4JgmH+DdJh/g/yYf + 4P8kG+D/Hhbf/x8X3/8eF9//Hhff/x8X3/8fF9//Hhbf/x0U3/8aEt7/HBTe/ykk4P9VVef/pKjy/9LX + +f+PlfD/o6n0/56i8v8mIN//JCPh/yQ36f8jRu//I0rw/yNH8P8jR+//I0fv/yNH778jR+8rAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACYf4AsmH+BEJBzgkzEt4tpQS+b/T0rm/1VQ5/9WUef/VE/n/1NP + 5v9VVef/YmXp/3d57P+ZofH/wsv3/9vl+//Bzfj/oKjz/6Gn8/9/g+7/KCTg/yIa3/8mHd//Jhzf/yYk + 4v8lNuj/I0Xv/yNJ8P8jR+//I0fv/yNH7+4jR/BlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAb3nrCtff+kGbqPKOtbr22KKo8/+fpvL/n6by/56m8v+ep/L/kpvx/4mU7/9/he7/am3r/2Bi + 6f9NS+b/KiXg/xwU3v8kHOD/Jh/g/yYf4P8mHuD/Jh3f/yYc3/8mJOL/JTXo/yNF7v8jSvD/I0jw/yNH + 7/8jR++oI0bvGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSUOcHGBDePCEa + 34khGt/VIRrf/yEa3/8iG9//IBnf/x4W3/8dFN//HRXf/yAX3/8kHeD/Jh7g/yYe4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYd4P8mHN//JiPh/yU16P8jRe7/I0nw/yNI8P8jR+/gI0fvTQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUe4AUlHuA7JR7ghyYe4NEmHuD/Jh7f/yYe + 3/8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHd//Jhzf/yYk + 4v8kN+n/I0bv/yNJ8P8jR+/+I0bvagAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAJh7gAyYe4DYmH+CEJh/gzSYf4P4mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh3f/yYc3/8mJeL/JDvq/yNI8P8jR/DIAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAmH+ABJh/gMiYf4H0mHt/IJh/g/CYe3/8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYd3/8mHN//JTLn/yNI8IgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmHt8tJh/gdCYf + 4L8mH+DzJh/g/yYf4P8mHuD/Jh7g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYd3/8lJ+ObI0nwDAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYe4CEmHuBWJh7gkiYf4MYmHuDsJh/g/yYf + 4P8mH+D/Jh/g5CYf4LwmH+COJhzfRwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAmHuAPJh/gNSUe32cmH+CVJh/gnyYf4JYmH+BaJh/gLCYe4AwAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////+Af///////////////////AD////// + /////////////AAf//////////////////gAD//////////////////wAAf/////////////////4AAH + /////////////////8AAB/////////////////8AAAf////////////////wAAAD//////////////// + gAAAA////////////////AAAAAH///////////////AAAAAA///////////////AAAAAAH////////// + ////wAAAAAB//////////////8AAAAAAf//////////////gAAAAAH//////////////8AAAAAB///// + //////////AAAAAAf//////////////4AAAAAH//////////////+AAAAAB//4B///////////wAAAAA + //4AH//////////8AAAAAP/8AA///////////gAAAAD/+AAH//////////4AAAAB//AAA////////+D/ + AAAAAf/gAAP///////8AAIAAAAP/wAAA///////8AAAAAAAH/8AAAH///////AAAAAAAB/+AAAA///// + //wAAAAAAA//gAAAJ///////AAAAAAAf+AAAACf/////58AAAAAAP8AAAAAH/////4H4AAAAAAfAAAAA + B/////8A/gAAAAAA4AAAAAf////+AH+AAAAAADgAAAAH/////AB/4AAAAAAAAAAAB/////gAP/wAAAAA + AAAAAAf////4AD/wAAAAAAAAAAAH////8AA/AAAAAAAAAAAAB////+AAIAAAAAAAAAAAAAf////gAAAA + AAAAAAAAAAAH////4AAAAAAAAAAAAAAAD////8AAAAAAAAAAAAAAAA/////AAAAAAAAAAAAAAAAP//// + wAAAAAAAAAAAAAAAH////8AAAAAAAAAAAAAAAB/////AAAAAAAAAAAAAAAAf////wAAAAAAAAAAAAAAA + H////4AAAAAAAAAAAAAAAA////8AAAAAAAAAAAAAAAAP///+AAAAAAAAAAAAAAAAD///+AAAAAAAAAAA + AAAAAAf///AAAAAAAAAAAAAAAAAH///gAAAAAAAAAAAAAAAAB///wAAAAAAAAAAAAAAAAAf/+AAAAAAA + AAAAAAAAAAAH/4AAAAAAAAAAAAAAAAAAB/+AAAAAAAAAAAAAAAAAAAf/gAAAAAAAAAAAAAAAAAAD/8AA + AAAAAAAAAAAAAAAAAP/8AAAAAAAAAAAAAAAAAAB//4AAAAAAAAAAAAAAAAAAf/8AAAAAAAAAAAAAAAAA + AH/+AAAAAAAAAAAAAAAAABD//gAAAAAAAAAAAAAAAAAf//4AAAAAAAAAAAAAAAAAP//+AAAAAAAAAAAA + AAAAAD///gAAAAAAAAAAAAAAAAB///4AAAAAAAAAAAAAAAAA///+AAAAAAAAAAAAAAAAAf///gAAAAAA + AAAAAAAAAAH///4AAAAAA/wAAAAAAAAB///+AAHwAH//gAAAAAAAAf///gAB/g///+AAAAAAAAH///4A + A//////8AAAAAAAB///+AAf//////4AAAAAACf///gAP///////gAAAAAAH///4AH////////AAAAAAA + ///+AB////////+AAAAAAD///gA/////////8AAAAAAP//8Af/////////4AAAAAA///AP////////// + wAAAAAH//8P///////////gAAAAAf///////////////AAAAAD///////////////+AAAAAf//////// + ///////8AAAAB////////////////4AAAAP////////////////wAAAB/////////////////gAAAf// + ///////////////AAAH//////////////////AAB//////////////////+AB/////////////////// + 4A////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////8oAAAAYAAAAMAA + AAABACAAAAAAAICUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcG + KAgGBSYWBgUkFwUEHxIEBBsEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAANDE8BBQUiVgAAAr8AAADrAAAA7gAAAOIAAAWyBgYYUhISKwQAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwbXxcDAwqwBwck/g8OTv8REFf/DQxI/ggI + Kf8CAgf/AAAA/gICArQiIiwSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBtGQwM + JcUaF5L/Ix3g/iQd6P8oIuP/NTHX/j08zv8xL7L/EBFG/gAAAv4LCw+ZAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABxcowJEREwvx8awv4qJdv+UE7G/ouLyf7Bwdb+5+fn/u/v7f7m5uf+srTM/jk6 + Q/4BAQH5IShNIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIuOoRAwMDSoaGqs/qmqy//i4uH//f37/v7+ + /v/19vr/3eL0/sPM8P+0wvn/rbr5/rC+7/9cYGz+DBAjdwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAur7QAq2xxRacn6xMoaKlm8bG + x9zp6en9+vr6/u/y+//J0/j/k6b0/mB77/88XOz/KUvt/iNG7v8iRu//IUXv/iJG7/8uUOn+KjqEmwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ6i + tBSSlJ5ar7G4qs/Pz+Xw8PD+/v7+/vDx8v67w+X+cIfo/jpa7f4iRu7+IUTv/iJG7/4iRu/+Ikbv/iJG + 7/4iRu/+Ikbv/iJG7/4iRu/+JEjv7CY/1DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAqa/MAZqftx+Ym6hhqamquNjY2PX6+vr/+vr7/trf8f+ir+f/XnXf/itM5v8gRO//Ikbv/iNG + 7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNF7v8kMuf+Izvq/yNG7dIkOdsUAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAJqiyAKSmLNHq6yxs9fX1+z19fX+8fT7/svT9f+Lnuz/S2fp/ipM + 6/8iRe7/Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//I0bv/iNG7/8jRu//Ikfv/iQ2 + 6P8lHt/+JR/g/yQ46f4jQuycJDXlAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIaMqyza2tv39/j8/r3J + 9/53jvH+PV3u/iNH7v4hRe/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG + 7/4iRu/+Ikbv/iJG7/4iRu7+I0bv/iUm4v4lHt/+JR7f/iUf4P4jPev8Iz3rSwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAIKOxweanamoP2Dy/h9D7/8iRu//Ikbv/iNG7/8jR+//Ikbv/iNG7/8jRu//Ikbv/iNG + 7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jR+//I0Pt/iUg4P8lHt/+JR7f/yYe + 3/8lK+T+I0LthQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABrf9gIJUjvzyNG7/8jRu//Ikbv/iNG + 7/8jR+//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG + 7/8jRu//Iz/s/iUf4P8lHt/+Jh7g/yYe3/8lKuT+I0HtcgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAMlPrWSJG7/wiRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG + 7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+JDrq/iUe3/4lHt/+JR7f/iUe3/4kMeb+IzfpRQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ0nrCCNG78cjRu//Ikbv/iNG7/8jRu//Ikbv/iNG + 7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//I0bv/iNG7/8jRu//JC7l/iUe + 3/8lHt/+JR7f/yYe3/8kNOf4JDHmFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAGBiQIBQUeFQQEGxIFBBwFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG + 708jRu/7Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG + 7/8jRu//I0bv/iNG7/8jRO7/JSHg/iUe3/8lHt/+JSPh/yUf4P8lK+TGAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAODFQCCQg4PQIBDZ4AAALXAAAA6QAAAOgAAALRAQELgQUE + IRkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACJF7gciRu+8Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG + 7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+I0bv/iNG7/4kN+n+JR/g/iUe3/4lHt/+JTHm/iUl + 4v4lH+CNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8MXQkEBByOAgIH+AoL + NP4REVj+ExNe/g0NQv4DBA7+AAAA/gAAAdgFBR88AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjQu0zI0bu+SNF + 7v8jRu//Ikbv/iNG7/8jRu//I0bv/iNG7/8jR+//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNF + 7/8lJuL/JSzl/iYe3/8lIuH+JTTo/yUk4fIlIuAxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAEA1jEgQEE7YVE3D+Ix3W/yUd6/8lHen+JRzp/yUd6v8kHt7+FBNu/wECCP8AAAD2BQciUAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAyO+YBMz3nCzI85wYAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAI0DshiUo4/wjPOv/Ikbv/iNG7/8jRu//I0bv/iNG7/8jRu//Ikbv/iNG + 7/8jR+//Ikbv/iNG7/8jRu//Ikbv/iM56f8lKeT/JSzl/iYg4P8lMOb+JS3l/yUf4JglJOEBAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVE4EGBwgksB4atf4lHej+JR3n/yUd5/8lHef+JB3j/yQf + 1v8jIMX+Ih60/xkZbv8NEUT+CA4utRcklwQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJzLmGSY6 + 6WomMOavIyvk1SYv5csmLuWwJzDlkCY153ElMOZMJDLnLCM36Q4kMeYBKDXnBicl4ZYlH+D/JDTo/h83 + zP8iQuX/I0fx/iNG7/8jRu//Ikbv/iNG7/8jR+//Ikbv/iNG7/8jRu//I0Lt/iUu5f8lNOj/JSPh/iUi + 4f8lLeX+Jh/g4SUj4RsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABYUgAELCjZ/IRzG/yQd + 6P8kHOj+JB7i/yQgyv8kIcL+JCDE/yQfzP8lHtj+Jh7g/yUe4f8lJOT+I0Xr8SFC5ZoeNssXAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACQt5BIkNuiNJCjj9iUe3/48POT+ZGzs/mdu7f5gZ+z+JyDf/iUd3/4lHt/+JR/f/iUk + 4folKuTiJDLmwiQ06KIkKeTtHiK9/hwgp/4WF4P+GSKc/h83y/4iRev+I0fw/iNH8P4iR+/+Ikbv/iNH + 8P4jQOz+JSfi/iU06P4lKuT+JR7f/iUe3/4lHt/6JSDgSgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAABEQYjseGrL2JB3e/iEevf4jIb3+JCHI/iUf3f4lHuH+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JDnp/iJG7/4iRu/eKkrmIwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB0luRklH9u6JR7f+CUe3/8nIOD+JyDg/yUd + 3/8mH+D+Jh7f/yYe3/8mHuD/JR7f/iYe4P8lHt//JR7f/icg4P9cY+v/aHHv/lBY6f85QeD/KDDP/h8n + u/8cKLP/HCuy/hsnqf8cLbX/HTPD/h0ouP8fHLv/JCHU/iUm4v8mHuD/JR7f/iYe3/wlIN+BAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBejBhMTWr8eHZ7+Hhyf/yQezP8lHt/+Jh7g/yYe + 3/8lHt/+Jh7f/yYe3/8lHt/+Jh7f/yYe3/8lHt/+JC7l/yNH7/8iRu/+I0fvwDhW5AsAAAAAZXnUAQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAZGp8EIh7IRCUe3q8lHt/5Jh7f/yYe4P8lHt/+JR7f/yYe4P8mHt//JR7f/iYe4P8mHuD/JR7f/iol + 4P+JlfT/laH3/pWh9/+UoPf/i5b0/nqD8f9ja+7/R1Hp/i033f8fKsv/HCi3/hkkoP8WGYf/FRN9/hoW + m/8iHMf/JR7f/iUg35skJd4GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJB7bASQd2RwkHdhYIx3NqCId + w/smHt/+JR7g/yUe3/8lHt/+Jh7f/yYe4P8lHt/+Jh7f/yUe3/8lHt/+Jh/g/yUe3/8lHt/+JSnj/yNG + 7/8iRu/+I0fv/SpL5VmJmN8rk53HOwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAgIMwkFBiItCQo7Jw4OWQMAAAAAAAAAAAAAAAAlINImJR7eliUe3+klHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4sKOH+SUrn/l9l6/5weO/+gIry/oyY9f6UoPf+lqL3/pSh + 9/6HkvP+bnXu/lhg7f5CUOz+KDng/h8vy/4cKrD+Hie5wyQt4B8jJ98BAAAAAAAAAAAAAAAAAAAAACQn + 3gslHt+SJR7f1iUe3/slHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+JCrk/iJG7/4iRu/+Ikbv/lJu8biwsrzOOkFhPQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQk5OwgIKsoEBRf1AAAA8gEBC64OC1QcAAAAAAAA + AAAAAAAAIxzSASUe3h4lHt95JR7f3CUe3/wlHt//JR7f/iUe3/8lHuD/JR7f/iUe3/8lHt//JR7f/iUd + 3/8lHt//JyDg/i0o4f89POT/W2Dr/nmD8f+Ll/X/kp32/pOf9v+Un/f/h5L0/kdI5v8lIeH/JSnk+iQz + 5+kkOum5Iz3rdiQ36TskMOYRAAAAACQr4QUlId+FJB7a9iQe2P8mHt/+Jh7g/yYe3/8lHt/+JR7f/yUe + 3/8lHt/+Jh7f/yUe3/8lHt/+Jh7g/yUe3/8lHt/+JR7f/yUe3/8lHt/+JDLn/yNG7/8iRu/+MFPw/663 + 2/IeHx/4LTNMLgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANDEg2DQ5K7RUT + fP8VFHv+Bwgg/wAAAP8BAQjODApODQAAAAAAAAAAAAAAAAAAAAAAAAAAJR7fECUf4F8lHt/JJR7f/iYe + 3/8mHuD/JR7f/iYe4P8mHt//JR7f/iYe4P8mHt//JR7f/iUe4P8mIOD/UVTo/l1h6/9sde7/d4Lw/m96 + 7/9SWOn/REPm/jc04/8lHt//JR7f/iYe3/8lHt/+JSHg/yUo4/wkMufgJDboaSQv4wMAAAAAICTFPRwY + o7obF6D+Hxq6/yEbxP8iHMn+Ix3Q/yQd1v8lHtv+Jh/h/yYe4P8lHt/+Jh7g/yYe4P8lHt/+JR7f/yUe + 3/8lIuH+I0Tu/yNG7/8iRu/+ssH3/lhYWfMAAAD+Jys9IgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAABYVah0PD1PdFROA/h8Zv/4kHen+JB7i/hMSZP4AAAL+AgIPigAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAkKOMBJSXiQCUg4KslHt/yJR7f/iYe3/4lHd/+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4kHd/+S03n/oKM8v6UoPf+lJ/2/pSg9/6Voff+V1zq/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/CM66ssiRu+3IkfwpCJA5pkcLLTsGyms/hkmpv4ZJJ/+GCCY/hgckP4XGIj+FhSE/h8a + uf4mHuD+Jh7h/iYe4f4lHt/+JR7f/iUh4P4jP+z+Ikbv/iJG7/6Oo/f+tbW2/gUFBf4CAgL6LjA5EwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBxzBhcYUK4mJ5P+IRvP/yUd5/8kHef+JR3n/yQd + 5P8PDk/+AAAA1A4RWgsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIzPnAiQy5xIkLeVJJSnj0jk4 + 3f+lpcn/UVPY/iUe4P8mHuD/JR7f/iQd3/9wcdP/m53K/jAt4P9udu//laD3/pSg9/+UoPf/kp32/k9R + 6P8nIeD/JR7f/iUe3/8lHt/+JR7f/yUe3/8lHt/+JSXi/yNB7P8iRu/+I0bv/yNG7/8jR/D+I0fw/yNH + 8P8jR+/+Ikbv/yNG7v8iROr+HCy0/xYUgf8ZFZH+GhaX/xwXpP8gGr3+JSLf/yQ97P8jRu/+I0bv/z1d + 8f/x9P3+Q0NE/wAAAP8gICHxTk9SBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADxA+YUlM + p/siHM7+JR3o/yUd6P8kHef+JR3n/yUd5/8gG77+AAAA5hMbfRMAAAAAAAAAAAAAAAAAAAAAI0PtFSND + 7UgjQ+6BI0XutCNG79wjR+/3Ikbv/ixL6/+3usf/ZmbU/iUd4P8mHuD/JR7f/iQe3/9lZdT/wsPE/jw4 + 3P8oIuD/WFzq/mVr7P9iaOz/eoPx/pKe9v+Ai/L/c3rv/kVG5v8lHt/+Jh7f/yYe4P8lKuP+I0Pu/yNG + 7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNF7f8dMLz+FxuN/xkinv8cLbb+HC64/xoo + qv8XGon+GBqQ/xsoq/8fOM/+Ikbu/3KL9f/U1NT+AgIC/wAAAP9YWFjuMzM0AgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAbGmILMzZu5DQxwf4kHOL+GBWQ/hoWnP4kHeX+JB3n/iQd5/4lHuf+BAUT5hgp + oxgcNb8lIEHfXSJG7pgiRu/SIkbv+yJG7/4iRu/+I0bv/iJG7/4iRu/+Ikbv/iRH7v6fqM3+sLXJ/ixB + 5/4lKeP+JR7f/iUe3/47ONv+yMjD/lta1v4lHt/+JR7f/iUe3/4lHt/+Ixze/kRF5v6Rnfb+lKD2/l5j + 6/4lHd/+JR/f/iQz5/4jRu/+I0bv/iNG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+IUDi/hke + lv4cIKz+IkPn/iNH8P4iRu/+Ikbv/iNH7/4jR/D+IT7c/hwstP4XHJD+ExaG/qSt4P6AgYH+AAAA/gAA + AP6Ojo7kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPEEBaP0Gd/yEbyf8cGK7+GRl9/xYU + hP8kHeL+JB3n/yQd5/8lHuv+FyZ+8iJE59EiRu/xI0bv/iNG7/8iRu/+I0bv/yNG7/8jR+//I0bv/iNG + 7/8jRu//Ikbv/iJG7/+FlNT/sLfJ/mR73f8iRe//Iz7s/iUt5f8lIeD/t7nG/oCBz/8kHOD/JR7f/iUe + 3/8mHt//JR7f/iUe3/9FReb/Rkfm/ici4P8lJuL+Iz3r/yNG7/8iRu/+I0bv/yNG7/8iRu7+I0bv/yNG + 7/8iRu/+I0bv/yNH7/8iQub+GBuR/yEbxf8mH+D+JDnp/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNH + 8P8iRu3+GzPJ/5CVwf8tLjr+AAAA/wUFBf/CwsPRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABoa + XQIKCzutNDWY/yAazP8XFIv+FRR6/xsXpP8kHej+JB3n/yQk6P8jPe3+I0fx/yNG7/8iRu/+I0bv/yNG + 7/8iRu/+I0bv/yNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iJG7/9mfN3/jp3S/qevy/8nS+7/Ikbv/iJH + 7/8gP+3/k53P/qKlyv8kJ+P/JSTh/iUi4f8lI+H/JSTh/iUp4/8kL+b/JDfp/iM/7P8jRu/+I0bv/yNG + 7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yJG7v8ZIpz+IBu//yYe4P8lHt/+JSbi/yNG + 7/8iRu/+I0bv/yNG7/8iRu/+I0fv/yNB7f8kOOn+JDTr/xgeWP8AAAD+AAAA/xYWF//p6eqoAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAABQVUxALC0LgJSeK/h8ZxP4dGLL+GRWW/iMc3f4kIef+IzXr/iNG + 7/4jRu/+I0bv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbu/iJG7/4iRe/+I0bv/iJG7/4pTe/+JUjv/ihL + 7/5cd+f+mKPP/oua0/5iet7+IkXv/muG9P46WvH+aH7c/rq8xv4vUuz+Ikfv/iJH7/4iR+/+Ikfv/iJH + 7/4iRu/+Ikbv/iJG7/4iRu7+Ikbv/iJG7/4iRu/+I0bv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/h41 + yP4bF5/+Jh7h/iUe3/4lHt/+JDPn/iJG7/4iRu/+Ikbv/iJG7/4jRe7+JCzk/iUd3/4lHt/+Jh7h/hMS + aP4AAAD+AAAA/jk5Ofufn6ZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsNNygNDUv3FhV7/xwX + rP8kHub+JCXZ/yQ11f8jQ+v+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/y5R + 8P9Sc/P/JEjv/iFF7/9wi/X/JUjv/per+P/k6fn/srfJ/khl5f+rssr/I0fv/nKO9f8rTvD/RWPl/sDC + xP9IZeX/KEvw/iNH7/8iRu//IUXv/iJG7/8jRu//Ikbv/iNG7/8mSe/+Ikbv/yNG7/8iRu/+Ikbv/yNH + 7/8iRu/+Ikbv/y5R8P8mSu/+I0bu/xkfmf8jHdH+Jh7f/yUe3/8lJ+P+I0bv/yNG7/8iRu/+I0bv/yNF + 7/8kLOT+JR7f/yUe3/8lHuD+JR7g/yActv8BAQT+AAAA/39/gcNTUm8IAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAsRQTsMDEP+FBJ6/x8psP8jPt7+I0br/yNG8P8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG + 7/8iRu/+I0fv/yNG7/8iRu/+I0bv/yJG7/8tUPD/T3Dz/iJF7/9lg/T/Ikbv/mmE9P82WPH/srfJ/jxb + 6P+UoND/sr3l/qS4+f8oTPD/K07s/re7x/9pftz/Wnn0/jda8f9Ia/P/c4/1/lx58/8hRe//Jkrv/jdY + 8P90j/X+JEjv/y5T8P8hRe/+Z4P0/4ad9/8kSO/+NVnx/0hn8v8xVPD+IT7c/xsZof8mH+H+Jh7g/yUf + 4P8jPOv+I0bv/yNG7/8iRu/+I0bv/yQu5f8lHt/+JR7f/yUe3f8bF6r+Kiig/yYmi/8AAAX+AQEG/0NB + pZkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0YV0kQGVj+Gyuy/iJG7/4iRu7+Ikbu/iJG + 7/4iRu/+Ikbv/iJG7/4iRu/+I0bv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iR+/+Nlnx/l18 + 9P5Zd/P+JEjv/mB99P4dQu/+kp/Q/lt03/5EYub+p6/L/mWD8/48XfH+IUXv/pmkz/6PnNH+Smrz/kxt + 8v4iRu/+HkLv/kdn8v5rhvT+ZoP0/rDA+f5BYvH+JUnv/khr8v4hRe/+N1vx/jpc8f47XPH+Y4H0/myI + 9f4pTfD+HTG//iEbw/4lHt/+JR7f/iUl4v4jRu7+Ikbv/iJG7/4jQ+3+JSnj/iUe3/4lHt/+Jh7g/hoW + nv5gYqL+6enp/ufn6P6Qk6z+Hx+H/iYf4O5GRrAhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHTnGHSBB + 264iRer+I0bv/yNG7/8iRu7+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG + 7/8jRO7+Izzr/yQ06P8kLeX/JSrj/qWu8/9gaOv/IiXi/p2n8/+NnvT/l6La/oKN0/8iOOv/v8PP/mZ8 + 4f9KaPH/IkTu/nKF2f+zuMj/NFfx/khn8v8iRu//Ikbv/iBE7/9FZvL/epf2/kVl8v9gffT+Ikbv/4Kc + 9v8wUvD+T3Dz/5Op+P8xVPD+PF/x/32V9v8hRe/+HCmv/yQd1f8lHt/+JR7f/yQv5v8iR+/+I0bv/yQ6 + 6v8lI+H+JR7f/yYe3/8lHt/+IhzM/zY3j//f3+T+6enp/+np6f/q6ur+r7HQ/ys41v49RNJhAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAhQ+M/Ikbv3yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu7+I0bv/yNG + 7/8iRu/+I0bv/yJG7/8iRfD+Izrr/yUr5f8lIeH+Jh7f/yYe3/8lHt//JB3f/jo+4/+QlO//Lijh/l9e + 6P+5vPX/xMnm/qOkyf8jHN//eXvd/qyvy/9YXuf/JSLh/kxO2v/FxsP/PUXi/kRL6P9baO3/f5Py/pOm + 9v+UofP/UFrq/mdx7P8zQej+IzHn/2Jw7f+7w/f+Ljvn/z1G6P8kL+b+JjXn/yQ36f8jQO7+GyOn/yUe + 3P8lHt/+JR3f/yQ16P8jRu/+JDDm/yUf4P8lHt/+Jh7f/yYe4P8lHuD+Gxeq/6Wnx//q6ur+6unp/+rq + 6v/p6en+6+rq/42Z3f8oSOqhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIkXuAiJG72kiRu/2Ikbv/iJG7/4iRu/+Ikbv/iJG + 7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+KErk/lBlwv5scbX+V1a3/iYf3v4lHt/+JR7f/iUe + 3/4lHd/+MC/h/jIy4v4uKeD+Q0Lk/iUe3/5oZ+n+iInm/rW2xv4xLN7+JR/f/p+hyv51edr+JR3f/jEs + 3f67vMX+XFzY/jk24/4xK+H+goTt/mlr6f4iGt/+JR7f/iUe3/4lHt/+JR7f/iUd3/4hGt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4mHuH+Gxmg/iUe3v4lHt/+JR7f/iUl4v4lJOH+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4mHuH+GhiD/l9gZf7Ly8v+sLCw/re3t/7Y2Nj+6urq/tfb6v4rTu/tXG/eCQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApRecCIkbufSNG + 7/gjRu//Ikbv/iNG7/8jRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yND7f84S87+kZWw/7O0 + wf+1tsX+f4Cw/yYf3v8lHt/+Jh7g/yYe4P8lHuD/Oznj/lZY5/9cYOj/Kybg/iUe3/8lHd//KiTe/rKz + x/9KSNn/JR7f/lZU1/+nqcn/JR7f/icg3/+eoMr/f4HR/jQz4v8mHuD/JB3f/lJS5v9LSuX/JR7f/iYe + 4P8lHt/+Jh7f/yYe3/8lHt/+JR7f/yUe3/8lHt/+Jh7f/yYe3/8mHuL+Gxih/yUe3v8lHt/+JR7f/yUe + 3/8lIOD+JiTh/yUe4P8lHt/+Jh7f/yYe3/8mHuH+EA9X/wEBAf8MDAz+AgIC/wUFBf8TExP+WVlZ/93f + 4/8/Xu7+VGvdLwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAsSutkI0bv/CNG7/8jRu//Ikbv/iNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG + 7/8jRO7+IzLo/0RGvv+io7X+trjH/6Okr/+foKn+SUe5/yUe4P8lHt/+Jh7g/yYe4P8lHt//JR3f/jEs + 4f8vKuH/JR7f/iYe3/8mHt//JB3g/p6fy/9qadT/JR7f/ikj3/+oqcn/UE7Y/iQd4P98fND/naDL/l5j + 6P8kHN//JR7f/iYf4P9gZej/JR3f/iYe4P8lHt/+Jh7f/yUe3/8lHt/+Jh7g/yYe3/8lHt/+JR7f/yUe + 3/8mHuH+Gxed/yUe3f8lHt/+JR7f/yUl4v9Xa/D+R1Pr/yUe3/8lHt/+JR3f/yYe4P8mH+H+Dw9X/wAA + AP8AAAD+BwcH/1lZWv9FRUX+Ly8v/+Tk5f9OaOz+T2bUSwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAFNR0QJUUtERW1vVKkhJz0ZGR9BbPT7OVDY91EEiQeniIUTt/iJG7/4iR/D+Ikbv/iJG + 7/4iRu/+Ikbu/iJG7/4iR+/+I0Lt/iQy5v4kIOD+S0m5/qqruf62t8b+tbbG/ra3xv6ytL3+NC/I/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JBzf/n5+0P6Qksz+Ixzf/iQe + 3/5bW9b+oaLJ/igh3/5SUNf+uLnF/pme7f54eOz+WVjn/oeL7f5PTuX+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4mHuH+Gxec/iUe2/4lHt/+JR7f/jk75f6UoPb+SlHp/iUe + 3/4kHN/+TFLn/iUe3/4mHuH+EhBn/gAAAP4NDQ3+x8fH/uvs7P7r6+v+hISE/pWVlv5GYd3+RlzNTgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQj/KIykkwcEjHb/lX2Tc9H2I6/t7g+r+aHDh/kdI + 0Ph2gen+aHTm/1lo5P9LXeT/OlHk/ixH5v8kROr+I0Tu/yM86/8kLeX+JSDg/yUe3/9HRbz+qqu6/7a3 + xv+2t8b+trfG/7a3xv+3uMf+UVC1/yUd4f8lHuD+Jh7f/yUe4P8lHuD/JR7g/iUe3/8mHt//JR7f/iUe + 3/8lHt//JR7f/l1c1v+0tsb/Ix3g/iYe3/8mId//rrHH/k5N2P8wLN3/w8TD/kxK2/90dev/io3u/klG + 5f8lHt//JR7f/iUe3/8lHt/+Jh7f/yUe3/8lHt/+JR7f/yUe3/8lHt/+JR7f/yUe3/8lHuD+Gxeg/yQd + 1f8lHt/+JR7f/yUg4P9zgfL+YWjs/yUe3/8jG9/+c33v/y8q4f8lHuD+GBaL/wEBA/94eHj+6+vr/+np + 6f/p6en+oaGh/yQkJf9CXN3+P1PLRQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARkLLGTUx + yryAgN/4m53p/qWo7P6zsvD+Pjvd/ich2f4xLdv+NzTa/j8/2f5TVt7+b3fl/n2H6v5sd+P+IyHS/iUe + 3/4lHt/+JR7g/jo4xv6lprb+trfG/rW2xv62t8b+trbG/ra3xv63uMj+mZyz/lJFlP5QJWP+VyZR/lIl + Xf5IJH3+OSKn/iwgzv4kHuL+JR7g/iUe3/4lHt/+JR7f/kM/2v7FxsP+My/d/iUe3/4lHt/+Z2bU/pqb + y/4jHeD+rK7I/mVl1P4kHd/+JBzf/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+HRms/iIbx/4lHt/+JR7f/iUe3/5UY+7+ZnPv/igi4P4kHN/+d4Dw/k5Q + 6P4lHt/+IRzC/hQVKP7Pz9D+6ejp/unp6f7s7Oz+RERE/kJDSv4yT97+LzLcnTtC2BUAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUlegUrKzwTbGz9Iu3u/fGSknl7yUd3/4lHt/+Jh7f/yYe + 4P8mHuD/JB3e/iMc2f8oItv+JR7e/yYe4P8lHt/+JyDc/4aHrv+2t8f+trfG/7a3xv+2t8b+ra27/6Ce + qP95XV/+aDo3/2UqJv9lKCP+ZSgk/2UoJP9lKCP/Zigg/mYoIv9cJ0D/RSSG/i8gxv8lHuH/JR7f/jAr + 3v+7vMX/VFPX/iUe3/8mHuD/LSfe/q6wx/9LSdn/goPP/o2Ozf8jHOD/JR7f/iUe3/8mHuD/JR7f/iUe + 4P8lHt/+Jh7f/yYe4P8lHt/+JR7f/yUe3/8lHt/+Jh7g/yYe3/8lHt/+IRvB/x4Zsf8mHuD+Jh/f/yUe + 3/8jOOn+IzPn/yYe3/8kHN/+cHrv/3mC8f8lHt/+JR7e/0ZGnv/i4uH+5+fn/8bGxv9gYGD+AwMC/1xf + g/8fPOL+Zm7t/2tz7u8zMuNyODbjAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABeY+YGPEPlPSM76vUkNOf+JSPh/yYe3/8mHuD/JR7f/iYe3/8lHt/+Jh7f/yYe4P8lHt/+ODTI/6yt + vP+1tsb+trfG/7a3xv+lprL+f3d7/2g2M/9lJyP+ZSgj/2UoJP9lKCP+ZSgk/2UoJP9lKCT/ZSgj/mUo + JP9lKCP/ZSgi/mIoLP9SJWD/MyG4/igh3v+kpcn/eXnQ/iQd4P8mHt//JR3g/m5v0/+Xmcz/WlnW/qyt + yP8nId//JR7f/iUe3/8mHuD/JR7f/iUe3/8lHt/+Jh7g/yYe4P8lHt/+JR7f/yUe3/8lHt/+Jh7g/yYe + 3/8lHt/+JB7W/xoXm/8mHuD+JR7f/yUe3/8kNej+Iz/s/yUf4P8kHN/+cHrv/5Gd9v89POT+JR7g/zAs + yP+kp8j+iIiI/wwMDP8BAQH+UFFZ/zhGs/8kOer+Jx/g/zs65P9fZuv7LyrhWQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjjlYiQ56fwlIeH+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+S0m+/rS1w/61tsb+tbbG/qusuf6IiI7+ZzQx/mUnI/5lKCP+ZSgj/mUo + I/5lKCP+ZScj/mUoI/5lKCP+Zycf/mYnIv5mJyH+Zicg/mcnHv5mJx/+Zici/lMlWP6Ui6/+mpvH/iAb + u/4iHMn+JR7Z/jIt3P62uMb+WFnX/ri6xv44NN7+JR7g/iUe4P4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+Jh/g/hsXn/4kHdb+JR7f/iUe3/4kLuX+Ikfv/iQz + 5/4kHN/+b3jv/pSg9/59h/L+Jh/f/oOE7f6Okaj+T1Jy/ggJFv44OVb+Pkmw/iFB5fslK+S5JR7f9SUe + 3/4nIeD6Mi7iPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4OOQFJiXi6TlB + 5/8lHt/+Jh7f/yUe3/8mHuD/JR7f/iYe3/8lHt/+Jh7g/yUe3/8lHt/+Vla7/7W3xP+1t8b+trfH/5SU + nf+Dc3f+ZSci/2UoJP9lKCP+ZSgk/2UoJP9lKCP+ZSgk/2UoJv9HNoX/L0HM/ixF2v9VZdz/ZXDX/mlx + 0P9pbMf/aWe8/k5NsP9PW8H/YXfX/h8wtv8bKq3/GSWn/hghn/9pcLT/goi3/qirvP9MTKL/GhaZ/h0Y + qP8fGrr/IhzL/iUe2f8mHuD+JR7g/yYe4P8lHt/+Jh7f/yUe3/8lHt/+JR7f/yUe3/8lHt/+Jh7f/yEb + xv8eGrP+Jh7g/yUe3/8lIeD+I0Xv/yNG7/8jQOz+X3Xy/5Sg9/+Woff+R0nn/8PF8/9XWFj+Bwoh/yI4 + 2/8gQef+I0bw/yM+6tssNcwOLzPYCyws4CEuK+EbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAA6OOQfJR7f/k1P5/9mbOz+PDvk/ywn4f8oIuD/Jh/f/iUe4P8lHt/+JR3f/yUd + 3/8kHeD+XFy5/7a3xf+1t8b+t7jH/5CRmf97XmD+ZSgj/2UoJP9lKCP+ZSgk/2UoJP9lKCP+ZSgk/2Eq + MP8nQuD/Ikbv/iZJ7/9zh/T/hZX1/oeW9v+Il/b/iJf2/m6D9f8hRe//Ikbw/iJH8P82V/H/dIf1/nyO + 9f94ivP/cYbw/muB7P9Tad7/M0bJ/iU1t/8cJ6T/GB6U/hcZj/8ZF5b+HRms/yEbxv8kHdj+JR7f/yYe + 4P8lHuD+Jh7g/yYe4P8lHt/+Jh7f/yUe3v8cGKL+JB3V/yUe3/8lHt/+JDbo/yNG7/8iRu/+MlPw/4mX + 9v96hvL+REXl/7e4wv8HBwf+Bgoc/yJE5P8iRu/+I0Xu/iUu3XIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLSOY6JR7f/iMb3/4xLeL+Z27t/nmE + 8f5yfu/+cHjv/m537/5veO/+am7u/mRq7P5fZez+c3jC/rS1xP61tsb+trfH/pOUnf57XmD+ZSgj/mUo + I/5lKCP+ZScj/mUoI/5lKCP+ZSgj/mUoJP5RKWH+LSrN/iQr5v4kLOT+Ji/l/icx5v4nM+b+JzTm/iY0 + 5v4kMub+JDLn/iQ26P4nQOv+Ql3w/lpz8/5xhvT+hpX2/pOf9v6Yovf+laD2/o2b9/6Bkvf+b4P1/ltx + 7/5CW+P+KULS/hwwvv4bJ7H+HSGu/h4bsv4fGcD+IxzS/iUe3P4mHuD+Jh7h/iUe3f4fGrT+FxSH/iMc + zv4lHuD+JSHg/iNB7f4iRu/+Ikbv/jBS8P4qS+7+gJX1/lRUVf4AAAD+AwYU/iFC3v4iR+/+JDLlwSQ0 + 3QwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABmZOpRc3Ps/5eY8v+vsvX+MCzh/yMc3/8jHN//JBzf/iYg4P8yMOL+XGHr/3R98P98hfH+gIjZ/6qr + uv+jpLD+t7jH/5mapP+VkZn+ZjIu/2UnI/9lJyP+ZSgk/2UoJP9lJyP+ZSgk/2UoJP9lKCT/ZSgl/lAk + Yv80IbT/JB3e/iUe3/8mHt//JR7f/iYe3/8mHuD/JR7f/iYe3/8mHd//JR7f/iUh4f8jJuP/Ii/m/ig8 + 6f81Tu3/SWLw/lt08/9tgvT/fY71/oqY9v+UoPb+lqH3/4mY9v9xhfX+VnD1/z1b8P9rgOL+QFTF/0ZP + rf8YHZf+FxWH/xUSfP8UEnn+FBN6/xYTgP8hG8T+Jh7g/yUm4v8jQ+3+I0bv/yNG7/8fRO7+qrPS/wwM + DP8AAAD+AwYN/yFB1v4kMt7LJTS9FQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC2tvZt0ND7/9DQ+v/P0fv+Qj7k/yUe3/8lHt//JR7f/iYe + 3/8lHt/+JR3f/yUe3/8mH9/+Pz7j/5KWs/+lprL+trfH/7CxwP+QkZj+lJCY/3hcXf9uRUT+ajw5/2k4 + Nf9oNjP+aTc0/2k5Nv9sPz3/c1BQ/oFqbf+SiZL/j5DF/lRT0/4qJNv4JR7f/iUe3/8lHt//JR7f/iYe + 3/8mHuD/JR7f/iUe3/8lHt//JR7f/iUe3/8lH9//JSHg/iQm4v8kLuX/JDrq/ilF7f8yUu/+Q2Hx/1ly + 8/9xhfT+hpX2/5Cc9v+1vvn+o7H5/6+9+v9FYe/+LUrh/x81xP8YIp7+FRWA/xQSef8VE33+Hxq4/yUe + 3/8lJeL+Iz7r/yNG7/9BYvL+fn5//wAAAP8AAAD+BAUK/ykuuKsjKocVAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACsr/V0paf0/nFx + 7P5FQuT+JyDf/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4oIt/+f4ny/o6X3v6io7H+trfH/ra3 + xv6xssD+k5Sc/q2uvP6ztcT+r7G//q2vvf6srbr+ra68/q+xv/6wssD+qqy5/p2eqOmNjpXCeniTjWhm + lGBAPb44KSLdeyUe39olHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR3f/iUf3/4lI+H+JSzk/iM16P4hP+z+JUjv/jFT8P6Emfb+lKX3/rrD+v6Woff+kJ32/n2O + 9f5bdPT+J0Xj/hwuuf4WGIf+FBJ5/hsXof4lHtr+JR/g/iQn4/6JlfH+KCgo/gAAAP4AAAD+CwsL/nV3 + h2MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABBPON8Ihvf/y8p4f83NOL+JR7g/yYe4P8mHuD/JR7f/iYe3/8lHt/+Jh7f/yUd + 3/tRU+eLhY7vb4yV7YOLktKZkJGew6qruPm1tsX+t7jI/7a3x/+2tsb+sbLB/6iotfeam6TbjIuSsoJ9 + gYZ5cXNTeGxyJHhrfgUAAAAAAAAAAAAAAAAAAAAAAAAAACoj2wUmHt9CJR7foiYe3+gmH+D/JR7f/iYe + 4P8mHuD/JR7f/iYe3/8mHuD/JR7f/iUe3/8mH+D/JR7f/iYe4P8lHt/+Jh7f/yYe4P8lHd/+JSHg/y01 + 5v+gr/b+UWvx/1589P8zVPD+XXbz/4OS9f+VoPb+hpb2/y1P8P8jRu7+HjXJ/xgfl/8WFIH+Hhm08CYg + 3tiqrNT6AAAA/wAAAP8AAAD+HBwc/qurrpAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACHiO+Ira/1/8LD+f/Bw/j+KCLg/yYe + 4P8mHt//JR7f/iYe3/8lHt/+Jh7g/ych4K0+OuAGAAAAAAAAAAAAAAAAl5q4B4qLljSKipJshoeMkoSF + ioyFhYpxiYiOTYaEiSePiZAOjoeNAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJR3fASUe3xklHt9fJR7fvSYe3/IlHt/+JR7f/iYe4P8mHuD/JR7f/iYe3/8mHuD/JR7f/iYe + 4P8lHt/+Jh7g/yYe3/8lHt/+JR/f/3h77P9eX+j+j5Tw/zQ25P8kMOb+Izzq/yZH7v89XPH+Ynrz/zZW + 8P8iRu/+I0bw/yJG7v8gPNj+HzK+jkVRwzivsLTvAAAA/wAAAP8AAAD+Ozs8ura2uK0AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAC0tfaW0ND7/s/P+/6ztfb+Kybg/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f1zAq4RoAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJR7eAiUe3yomH996JR7f1yQd + 3/0jHN/+Ixze/iMc3/4jHN/+Ix3f/iQd3/4kHt/+Jh/g/ism4P5IRuX+fYHt/nh97P6DiO7+VFPn/iUe + 3/4lHt/+JR7f/iUi4f4lLOT+Iznq/iJE7v4iR+/+Ikbv/iJG7/4iRu/+Ikfv/klo8uqKior9AAAA/gAA + AP4AAAD3SUtUOsXFxcV6gJoGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACHie+bZGPp/zUx4v8hGt/+Jh7g/yYe4P8mHuD/JR7f/iYe + 4P8lHt/uJh/fLgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAKyXgAkFA5Ddtc+uRfITt4HqC7f59he3/hY3v/n6H7v+Bj+7+gY7u/4SL + 7/9/h+7+dHrs/15g6f8pI+D+JR7g/yYf4P8lHt/+Jh7g/yYe4P8lHt/+Jh7f/yUg4P8lLOT+Izvr/yNF + 7v8iR+/+I0bv/01s8v+Dg4P+AAAA/wAAAP8PFzmsU16QB8HBwsB5hLcdAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLR+WNWFbo/3t7 + 7f+Hiu/+JR/f/yYe4P8mHt//JR7f/iYe4PIlHt9QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU1LmEUZD + 5E04NOKlMy7h6TIt4v0wLOH+Lyvh/y0o4P8pI+D+JR7f/yUe3/8lHt/+Jh7f/yYe3/8lHt/+JR7g/yYe + 4P8lHt/+Jh7f/yYe3/8lHt/+JR/g/yUk4f8kMuf+I0Ht/01s8v/R0dH+AgIC/wYLH/8hQtv4JEbov4yZ + 0tOBkM87AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACNk/BN09L7/s/P+/7P0Pv+LSng/iUe3/4lHt/+JR7f+iUe32gAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQkDjGjQv4WApIuC4JR7f+SUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR/g/kpP + 6P78/P3+MDAy/hkxnf4jRvD+Ikbu/jtb8P6UqPbSc4zxV1hz7AUAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB0dOwFn6Dz2ZOT8f9aWOf+Jh/g/yYe + 4P8mHuD7JR7fbSUe3wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAMCvhHCgi4G4mHt+/JR7f9SUe3/8lHt/+JR7f/yYe4P8lHt/+JR7f/yUe3/8lHt/+Jh7g/yUe + 3/8lHt/+Jh7f/yYe3/8lHt/+Jh7g/0lH5f/7/P7+gIK3/yMz5f8jQ+3+I0fv/z1d8f+Uqfj+bIf1/yRI + 79ExU/BUJ0rvBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAASUXlVyYf4OwlHt/+Jh7f/iYe3+4oIOBqJR7fAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlHt8FJR7fKiUe33YlHt/BJR7f7yYe + 4P0lHt/+JR7f/yUe3/8lHt/+JR7f/yUe3/8lHt/+Jh7g/yUe3/8lHt/+JR7f/y0n4P/Nz/f+dnfr/yQd + 3/8lIeD+Iy7m/3WL9P+Oo/f+YH30/yJG7/8iRu/7I0bvuCJE7jMiRO4BAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEpF5SMzLOFUMCnhVy8p4SoAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAlHt4CJR7fICUe314lHt+sJR7f8iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/41L+L+KCLf/iUe3/4iG9/+ZGXo/s3Q9/6NmfL+SmXw/iJH7/4iRu/+Ikbv/iNG + 7/AiRe56IkPtAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJR7fDyUe + 31MmHt+jJR7f5SUe3/4iG9/+Ihvf/yIa3/8iGt/+Ihvf/yIb3/8kHd/+My7h/15c6P+gpfH+tbv1/5GY + 8P+PlPD+Jh/f/yUp4/8jPOv+I0bv/yNG7/8iRu/+I0bvuyND7ScAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnIN8BKyTgFTMv4U+Bgu2ghoru45WW8PyOkO/+jZDv/5Ka + 8P+aovH+qbT0/6y09P+Xn/H+gIPu/1JS5/8pI+D+Jh7f/yYe3/8lHt/+JSjj/yM86/8jRu7+I0bv/yNG + 7+UiQ+1eIz/sBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAU1PmEkI/5E04NOKbNDDi6jQw4v40L+L+MCzh/ikj4P4kHt/+JBzf/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lKOL+Izzr/iNG7/4iRu/8IkXunyM96xMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASEXkCDk14kYxK+GWJR7f3iUe + 3/0lHt/+Jh7g/yYe3/8lHt/+Jh7f/yUe3/8lHt/+Jh7g/yUe3/8lHt/+Jh7f/yUo4/8jPOv+I0fv/yNG + 79IjOuoZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAPzvjEC4o4UMnIOCTJR7f3CUe3/klHt/+Jh7f/yYe3/8lHt/+Jh7g/yUe + 3/8lHt/+Jh7f/yYe3/8lH+D+JC3l/yNE7u8jO+omAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJR7eDCUe + 30AlHt+KJR7f2iUe3/0lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR3f9iQ16GsjNugCAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJR7fAiUe3yclHt9lJR7foyUe39slHt/uJh7g4iUf + 4KclIOByJSXiJQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJR7fAyUe3xAlHt8XJR7fEiUf3wMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////g//////////////+AP/////////////8 + AH/////////////4AH/////////////wAD/////////////gAD////////////4AAD////////////gA + AB///////////4AAAA///////////gAAAAf//////////gAAAAf//////////gAAAAf//////////wAA + AAf//////////4AAAAf//////////4AAAAf/w////////8AAAA/+AP///////8AAAA/8AH///////+AA + AA/4AD//////x/AAAA/wAB//////AAAAAB/gAA/////8AAAAAD/gAAf////8AAAAAH/AAAL////+AAAA + AH4AAAD////hwAAAAHgAAAD////A4AAAAAgAAAD///+AfAAAAAIAAAD///8AfwAAAAAAAAD///4AP4AA + AAAAAAD///4APAAAAAAAAAD///wAAAAAAAAAAAH///wAAAAAAAAAAAH///gAAAAAAAAAAAH///gAAAAA + AAAAAAH///gAAAAAAAAAAAH///gAAAAAAAAAAAP///gAAAAAAAAAAAH///AAAAAAAAAAAAH//+AAAAAA + AAAAAAH//4AAAAAAAAAAAAD//wAAAAAAAAAAAAD//wAAAAAAAAAAAAD/gAAAAAAAAAAAAAD/AAAAAAAA + AAAAAAD/AAAAAAAAAAAAAAB/wAAAAAAAAAAAAAAf+AAAAAAAAAAAAAAf/AAAAAAAAAAAAAAf+AAAAAAA + AAAAAAA/+AAAAAAAAAAAAAP/+AAAAAAAAAAAAAP/+AAAAAAAAAAAAAf/+AAAAAAAAAAAAA//+AAAAAAA + AAAAAB//+AAAAB8AAAAAAB//+AA4Af/AAAAAAB//+AB////4AAAAAA//+AD/////AAAAAA//+AH///// + 4AAAAA//+AP//////AAAAAP/+AP//////4AAAAD//Af//////+AAAAA//h////////wAAAAf//////// + ///AAAAP///////////wAAAD////////////AAAB////////////4AAA/////////////AAA//////// + /////4AA//////////////AD//////////////4P//////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + ////////KAAAAEgAAACQAAAAAQAgAAAAAABgVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAgHMRsDAxZxAwMVhQICEXEFBBspCAggAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcGmkCCAglbgYG + IfMMDED/Cgk4/gUFGv8AAAD7BgYKniAgKwoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFhYgwEdHT6FGRaV/SQd4v8vKt3/UU7R/2Bf + y/88PI//BwcT/hIUHYQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAADY3SW87OqX9eHfS/7i52f7p6ur//f38/vj5+//s7vf+pqmz/x4d + HO0hLWIMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvsHNAa+y + whaipbBWsLG2ps7Oz/nx8vX/3OL6/6e39v92jfD/S2fr/zJT7P8qTfD/LlHw/ztUw/8kNIUyAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKOmtxujpbBlvr/FtuDg4PD19fX+zNLs/4mb + 6P5HZOz/JEju/yFF7/4jRu//Ikbv/iNG7/8iRu/+I0bv/yJG7/4lRuirJDbGBAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAJ2jwRego7NovLy/vebn5/fq7fj+tcDw/3OI6f83Vuf/Ikbu/yJG7/8jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//I0Ht/yUj4f8kOOn+I0DocgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjZS2GtPT + 1efk6Pj/obH1/1137/8uUO3/IUXv/yJG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//JDDm/yYe3/8lH+D/Izrq9SM66SsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhpLIBIGNwaAkSO/+Ikbv/yJG + 7/4jRu//Ikbv/iNG7/8iRu/+I0bv/yJG7/4jRu//I0bv/yJG7/4jRu//Ikbv/iNG7/8iR+/+JSbi/yUe + 3/4lHt//JSfj/iM+62IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFhx5QwjR+/jI0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//JSHh/yYe4P8mHuD/JSrk/yM5 + 6kUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlSO1tI0bv/iJG7/4jRu//Ikbv/iNG7/8iRu/+I0bv/yJG + 7/4jRu//I0bv/yJG7/4jRu//I0bv/iNG7/8jP+z+JR7f/yUe3/4lHt//JC7l+yQw5hUAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGBSIBBAQXDgMDFAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAjRu4NI0bv2SNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8kMeb/JR7f/yUe3/8lJeL/JSXizgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAADQtRCgYFJHcBAQXPAAEC7gAAAOsAAAPDAwMXTAYGJAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAI0TuWyJH7/4jRu//Ikbv/iNG7/8iRu/+I0bv/yJG7/4jRu//I0bv/yJG7/4jRu//I0bv/iNE + 7v8lJuL+JSDg/yUj4f4lL+b/JSDghgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOC1YeBwckyBQS + dP8dGaz/IBu5/hsXof8ODUz+AAAB+wMDEokNEVADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAND7nBzM95wQAAAAAAAAAAAAAAAAAAAAAIzfpAiQ8 + 67YkNej+I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yQ26P8lLuX/JiDg/yUv + 5v8lJuLpJSPhGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABIQbRcPDlPVIx3Y/yUd6P8lHej/JB3l/yQe + 2P8jHsf/Fxds/wkMLf4OFlpFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJCrkBSY051gmL+WrMTrm3EBK6dQqL+S3Ji7lmCUr5HQkMudUJDXoMSc46CQmIuHGJCre/h4w + wv8gPtz+I0fv/yJG7/4jRu//I0bv/yJG7/4jRu//I0Ds/iUw5v8lK+T+JSDg/yUn4/4lIOBnAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAFxSDBBIQYrIkHeT/JB3k/iQg0f8kIMz/JSDK/iUf0v8lHt7+JR7h/yUp + 5P4iRezaHzvWUgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgJ8oEJC7ksiUf + 3/4nIeD/R0jn/z495f81M+P/JR7f/yUe3/8lHt/+JR/g/SUj4fgrL+TwO0TZ/ycuwf8bIaj/Gyas/x82 + yf8gOtT/ID7c/yFC5f8iNt//JSTg/yUt5f8lH+D/Jh7g/iUf36glI+ECAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAExFnXyAduP4gHa7/JB/F/yUf3v8mHuD/Jh7f/yYe3/8mHt//Jh7f/yUe3/8jQu3/I0bv/CpM + 61YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGxqlFyMe0XwlHt/lJh7f/yYe + 4P8lHt//JR7g/yUe3/8mHt//Jh7g/yUe3/9VWOn/lqL3/4+a9f9/ifP/aHLv/09Y5f8yO9L/Hie8/xok + qf8YHJD/FxWL/x4Zsv8kHtn/JR/fxSQl3xAAAAAAAAAAAAAAAAAAAAAAAAAAACQd1xwjHdNcHhqk4iQe + zf8mHuD/Jh7f/yYe3/8mHuD/Jh7f/yUe3/8mHuD/JR7f/yYe3/8jO+r/I0bv/yNH7+E/WtkToarVNwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAACAcvHwUFHVsICTY4EhFwAQAAAAAiHb8IJR7aYiUe380lHt/9JR7f/yUe + 3/4lHt//Jh7f/yUe3/4mIN//PDrk/lJV6f9kaez+dn/w/4qV9P6Wovf/jJj1/3V98P5fa+//P07m/iEt + zf8dKrr1IzLiWyMs4hUAAAAAAAAAAAAAAAAlI95RJR7f2SYe3/0lHt/+Jh7f/yUe3/4lHt//JR7f/iYe + 3/8lHt//JR7f/iUe3/8lHt/+JR7f/yUe3/4jPOv/Ikbv/iNH7/6Zpdi5QUZdawAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAMC0VLDg5Q7AgIKf8AAAD6BQQhigAAAAAAAAAAAAAAACUe3gYlHt9IJR7ftiUe3/kmHt//JR7f/yUe + 3/8lHt//JR7f/yYe3/8lHd//JR3f/yol4P9HSOf/anPu/3uG8f90ffD/dHzw/0FA5f8lH+D/JSPh/iUs + 5O4kNOi/JDjpfiQx5hYkK+EPIiDNkh8at/UkHdT/JR7e/yUe3/8lHuD/Jh7g/yYe4P8lHuD/JR7f/yUe + 3/8lHt//JR7f/yUi4f8jRO7/IkXv/4CW8f1CQkPzICMzXQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQTYDAREWTwGxel/yQe + 4/4YFYn/AgIJ/gYFJVQAAAAAAAAAAAAAAAAAAAAAAAAAACUl4iwlIeGYJh7g7iUe3/4lHt//JR7f/iYe + 4P8lHt/+JR7f/y0o4f51fvD/laH3/5Wh9/6Pm/b/VVnp/iUe3/8lHt/+Jh7f/yUe3/4mHt//JR7f/iUl + 4t0jP+uPIkHrfSAz0J4aJKL+GSOh/xogn/4aHZ3/Ghqa/hsXof8kHdb/Jh7g/iYe4P8lHt/+JSDg/yM9 + 6/4iRu//X3v0/pucnv4BAQH+IiMsTgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHRxyDSQmbs8gHLb/JB3n/yUd5/8lHej/FhSD/wID + EasAAAAAAAAAAAAAAAAAAAAAAAAAACM26AcjOOkhJDXoZSQv5uV1eNP/Z2jU/yUe4P8lHt//JR3f/4CB + z/9oZ9T/U1bp/5Ke9v+Tn/b/k572/1ZZ6f8vKuH/JyDg/yYe3/8lHt//JR7f/yQv5v8jRu//I0bv/yNG + 7/8jR+//I0fv/yNG7/8jRu//IkXr/xsmpv8XFIb/GRWR/xoXnP8gHMD/Iznn/yNG7/8lSe//1Nz5/ycn + J/8DAwP9UVFUOQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAHB1Qfzw7wP4iHNf+IBrE/yQd5/4lHef/JB3c/wUHHr0AAAAAHDjADSJF + 7TsiRe51I0XuriNG798jRu/8I0bv/yJG7/5ke93/nJ7L/iYo4v8lHt/+JR7f/25t0v6Sksz/JB3f/zQx + 4v44NuP/RETm/nuE8f+Tn/b+a3Pu/yUd3/4lIOD/JDfp/iNG7/8jRu//Ikbv/iNG7/8iRu/+I0bv/yJG + 7/4hP97/GiSi/hwttf8gPdn/IUHi/iA71f8cLLP+GCCX/xkkof49VNb/uLq9/gAAAP8aGhr9ZGVlMwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAcHGMNMDJ26SMdzv8ZF5D/FhWC/yQd4/8kHef/JR7q/xUkfNoiReu4Ikbu7iNG7/0jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/9FYub/ur7G/0tm5P8jPev/JSvk/0dF2v+xssf/KCLf/yUe3/8mHt//JR7f/y0o + 4f9hZez/NjPj/yUn4v8jP+z/I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yA+3P8bGqD/JB/Z/yM/ + 7P8jRu//I0bv/yNG7/8jRu//Ikbu/yA+2/9JVLX/YWJq/wAAAP9OTk/7b29vJAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEUFNKSqK/h8a + xv8VE3n+Gxeh/yQd5/4kJOj/Izzt/yNH8P4jRu//Ikbv/iNG7/8iRu/+I0bv/yJG7/4jRu//Ikbv/yJG + 7/4sTuz/mKTP/o+d0v8iRe/+K07w/ylI7P66vMb/OkTh/yQt5f4kLOT/JC3l/iQy5/8kOOn+I0Dt/yNG + 7/4jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8iRu/+IkTr/xodnf4lHtr/JR7f/iQu5f8jRu//Ikbv/iNG + 7/8jRu/+Izrq/yQx5v4hKbT/AQEB/gAAAP+GhobramptCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALDDOHGhqB/x8Zw/8gGsX/IyPX/yQ1 + 6f8jRe//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//J0rv/y1Q8P8jRu//QmLx/yhM7/+Infb/qrPR/32P + 1v9HZOX/YX30/zFS8f+dps7/WHHg/yJG7/8jRu//I0bv/yNG7/8jRu//Ikbv/yJG7/8jRu//I0bv/yJG + 7/8iRu//I0bv/yVI7/8iRu//HTG+/yEbxP8lHt//JSDg/yNB7f8jRu//I0bv/yNG7/8kLOT/JR7f/yUe + 3/8jHc7/AwMP/wMDA/+SkpmLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHCCOpFBJ7/yAouv8jPOH/I0Xq/yNG7/8jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//JEjv/0Vn8v8vUvD/WHbz/zdY8P9rhfT/oavU/0Vj5v+nsNP/k6n3/yZK + 7/90h9n/fI3X/01t8/8yVvH/WHfz/0Ji8f8iRu//MFLw/1Jx8/8rT/D/IUXv/1578/9HZvL/LVHw/0ho + 8v8hRfD/Gx+m/yUe3/8mHt//JDLn/yNG7/8jRu//I0bv/yQv5v8lHt//JR7e/x4ZtP8uLLX/BgYd/woK + GvxYV5c2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAABkupwEPG1i7HTC8/iJG7/8iRu7+I0bv/yJG7/4jRu//I0bv/yNG7/4jRu//Ikbv/iNG + 7/8iRu/+I0bv/yJE7v5Qb/L/Y3zy/ztY7/5NbPL/dIfZ/lFs4v+HltX+aoTp/zhZ8P5MaOT/oqvM/1Ny + 8/4qTvD/H0Pv/kxr8v9be/T+j6T3/z9f8f5EZvL/Ikbv/jdb8f9ObfL/Tm7y/miE9P8gQOP+Hxq2/yUe + 3/4lH+D/I0Lt/iNG7/8jQ+7/JSrk/iYe3/8mHuD+HRmo/5WWvv7s7Or/tbfG/jEvpv8vLdOXAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHz/ZCSFC + 4pIjRu/5I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8iRu//I0Xv/yQ76v8kLuX/JSXi/yUh + 4P9AQOT/iY7u/yol4P+rsfT/oqnj/3Z60/9cYOH/maHR/0VW6/8wQeX/t7rG/0Vc7f8uSOz/N1fv/0tq + 8v9+lfX/aIHz/0Nf8P9KZvD/j6P2/01o8P9TavD/K0ru/z1c8P8fOtL/IhzJ/yYe4P8lI+H/I0bv/yM7 + 6v8lIuH/JR7f/yYe3/8kHdn/VVah/+np6f/p6en/6enp/8fI3P8vReLcXmnLAgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiRu4cIkbuxCJG7/4jRu//Ikbv/iNG + 7/8iRu/+I0bv/yJG7/4jRu//Ikbv/yxM4v5RY8j/T1HC/iYe3/8lHt/+Jh7f/ycg3/4uLOH/PTzj/zs3 + 4/5VU+b/oqPr/piZy/8kHOD+jo/O/1xd3f4lHt//o6TJ/1ZV3P4vKeH/k5Tv/nV36/8pI+D+JB3f/yUe + 3/4nIOD/NzLi/iYe3/8lHd//JR7f/iUf4P8gIMD+Ix3R/yUe3/4lIeD/JC3l/iYf4P8mHt//JR7f/iYe + 3/8gGsH+YmR6/87Ozv6+vr7/2NjY/uvq6v92i+z8UGXhMgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAChG6SAjRu/YI0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG + 7/8jQu3/QlPL/6Cjuf+urrv/hoe3/yYf3/8mHt//Jh7g/yUd3/9HSOX/WVvn/ygi4P8lHuD/JyHf/6iq + yf8pI9//Qj/a/42Ozf8lHeD/fXzQ/3V41f8qJeD/JR7f/1VV5/8wK+H/Jh7g/yYe4P8mHt//Jh7f/yUe + 3/8lHuD/JR7f/yYe3/8gG73/Ix3T/yYe3/8lHt//KCbh/ykp4/8mHuD/Jh7f/yYe3/8eGq3/AQIF/wcH + B/8CAgL/Dw8P/3R0dP+bqez/R2HgZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAElJ + 0AlITNQQQk3dCSVI7sIjRu//I0bv/yJG7/4jRu//Ikbv/iNG7/8iR+/+I0Lt/yQv5v5QT7//ra6+/7a3 + xv6lprH/VlO//iUe3/8lHt/+Jh7g/yUe3/4lHd//Ixzf/yUe3/4mHt//JB3g/pucy/9APNv+JR7f/5GS + zP4+O9v/VlTX/6Ck0P5lZen/JyHg/ldX5/85NeL+Jh7f/yUe3/4lHt//JR7f/iUe3/8lHt//JR7f/iUe + 3/8gG73+IxzP/yUe3/4mIeD/cYLz/kNH5/8lHt//Lyzh/iUe3/8eGrL+AAEF/w4ODv6RkZH/sbCx/mpq + a/+eqdz/QFrYegAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEI+yjQrJsKoS03S0XF65exuduTzP0DN5V9t + 5vxRY+f/Q1nn/zJN5/8mRer/I0Xu/yM96/8kLeX/JSDg/09Nv/+vsL//trfG/7a3xv+2t8b/ZmW7/yUe + 4f8lHuD/JR7g/yUe4P8lHt//JR7f/yUe4P8lHt//JR7g/3180P9hX9X/JR7f/0hF2f+Jic7/MSzd/62v + x/9qaun/lprw/1lX5/8lHd//JR7f/yUe3/8lHt//JR7f/yUe3/8lHt//JR7f/yUe3/8hG8X/IRvD/yUe + 3/8lHt//Z3Lv/01R6P8lHt//W2Dq/ysl4P8iHMn/AwMM/4WFhf/q6ur/6enp/39/gP9EToT/OFHVdQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAERAyyptbNuupabs6bKz8P6JiOv+Jh/a/y8r3P41Mtv/RUXe/15i + 4v5ob+T/LCvU/iUe3/8lHt/+PzzI/6yuvv62t8b/trfG/7a3xv60tcT/lZOj/lY5c/9YJkr+WCZM/00l + a/4/I5T/MCDC/yUe4f4lHt//JR7f/l5d1f+FhM7+JR7f/yUe3/6Xmcv/Ojfc/6iqyP43Mtz/JB3f/iUe + 3/8lHt/+JR7f/yUe3/4lHt//JR7f/iUe3/8lHt//JR7f/iYe3/8kHdP+Hhmz/yUe3/4lHt//Pknp/ktU + 6v8lHt//YGXr/klK5/8lHt/+Hh5V/9bW1v7p6en/5OTk/icnJ/9OW6f/OEXlyDpB3TgAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAhoflCISJ6zFoa+lyJSnj6yUm4v8lH+D/Jh7g/yUe3/8lHt//JR7f/yYe + 4P8lHuD/hoe1/7a3xv+2t8b/s7TC/5OPl/9xSUj/ZSkl/2UoI/9lKCT/ZSgk/2UoI/9lKCP/ZSgj/1on + Rf9AI5L/KR/V/0A82/+nqcj/Jh/f/yYe3/9QTtj/hIXP/4WEz/9VU9f/JR7f/yUe3/8mHuD/JR7f/yUe + 3/8mHuD/Jh7f/yUe3/8lHt//Jh7g/yYe3/8lHt//HBim/yYe4P8lHt//JDDm/yQx5v8lHt//XWLr/3Z+ + 8P8lHt//My/B/9LT2/+Ghob/JCQk/zM0Ov8ySMr/Oz7m/2Jo7Po9POR6AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABJSuUSJT3r6iUl4v4lHt//Jh7g/yUe3/4lHt//JR7f/iUe3/8pI9f+pqe6/7W2 + xv61tsX/kZGZ/2o6OP5lKCP/ZSgj/mUoJP9lKCP+ZSgk/2UoIv5mJyH/Zici/2YnIf5mJx//YCcz/ksr + g/+4ucL+KyfH/yMd0f4nIN3/n6HK/25u0/56edH/JR7g/iUe4P8lHt/+Jh7f/yUe3/4lHt//JR7f/iUe + 3/8lHt//JR7f/iYe3/8mHuD+HRmt/yUe2v4lHt//JCvk/iND7f8lI+H/XGHr/pOe9v9EReb+X13m/4SG + ov4yM0X/ISI0/kJOrv8jPOfhJR7f7SUe3/4yLuK0ODbjAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAsKOF6Pkbo/ysl4P8lHt//JR7f/yYe3/8lHt//JR7f/yUe3/8uKtH/rq++/7a3xv+oqbX/g3V5/2Un + I/9lKCT/ZSgk/2UoJP9lKCT/ZCgn/zs8qf8pQ9z/V2ni/3N93v91fNb/dHbN/09VwP9Sadz/KD/J/x4y + vv8nNbf/U2C+/3R/wP+Gjb7/FxaX/xsXoP8eGbD/IRvF/yQd1/8lHuD/Jh7g/yUe3/8lHt//JR7f/yUe + 4P8mHt//Ix3Q/x8auv8lHt//JR/g/yNE7v8jRO7/SmTx/5Sg9/9xee//oaHr/zQ0Nf8XJIX/IULo/yNG + 7/4nNNliLjXVBiws4RoyL+IIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwKuGhLinh/11i + 6/9hZuz/U1bp/0lJ5/9BQeX/Ozjk/zYz4/87O9P/r7C//7a3xv+kpbH/gGpt/2UoI/9lKCT/ZSgk/2Uo + JP9lKCT/ZCgo/zI2vv8jO+v/PVLt/1Fk7/9UaPD/VGjw/z5Y7/8jQu3/I0Pu/0Be8f97jfX/i5r2/4qY + 9v+AkfT/bX/u/1ls4/9DVdL/LD2+/x0rr/8bIav/Hhyz/yEcxv8kHdj/JR7f/yYe4P8mHuD/Jh7g/x4Z + rv8iHMv/Jh7f/yQx5v8jRu//JUnv/2F48/86S+v/lJan/wAAAP8UJoD/I0fv/yQ46M8mM9gLAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbWei5dXTs/3d47P4sKOH/NDHi/zk2 + 4/5FROb/Zmzt/n+J8v+Eju/+oaO3/6ysuv6rrLn/jYeN/2YtKf5lJyP/ZSgj/mUoJP9lKCP+ZSgk/2Uo + Jf5QJGP/NCC1/yUd3v4lHd//JR7f/iYe3/8lHt/+JR7f/yUi4P4jJ+P/JzLm/zZG6v5IW+7/WnDx/muA + 9P99jvX+jJr2/4ya9v55i/b/YHfz/kVd5P82S9D/TFfE/jU6tP8bGqT+GRWU/xUTff4VE37/IBvA/iUf + 4P8jOun/Ikbv/iJG7/9jffL+OTk5/wAAAP4TJHD/Izvm4SY2yyYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAHV07APQ0PvPzc76/6iq9P8lHt//JR7f/yYe3/8lHt//JR7f/yUe + 3/8tKOH/iY/F/6ytuv+1t8b/m5ul/4uFi/+CZ2v/eFdY/3VRUf9zTk7/dlJS/3tcXf+JdHn/mJCa/4yN + wPpHRcXqJh7f8iUe3/4lHt//JR7f/yUe3/8lHt//JR7f/yUe3/8lHuD/JSDg/yQl4v8kLuX/Jzrq/zFM + 7v9DYPH/WXLz/3OG9P+NnPb/t8H6/5+u+P9PaO3/MEfU/xwqrv8WF4X/FBJ7/x4Zs/8lH9//JDPn/yJD + 7v+Aj8n/BAQE/wAAAP8jJ1vcJCmJHwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAI2N8ARkZOnVOzfj/yQd3/4mHt//JR7f/yUe3/4lHt//JR7f/iUe3/tude7Okp3y2Zaa + u+uxssD9t7fH/6usuv62t8b/t7jI/ra3x/+ur7z7oKGs4ZSSm7eGgoyHeHCIV2FamSlLRrYJMSvXGycg + 3nQlHt/QJR7f/SUe3/4lHt//Jh7g/yUe3/4mHuD/JR7f/iUe3/8lHt/+JR7f/yUe3/4lI+H/JSvk/iM1 + 6P9GYfD/gJf2/oGW9v94ivX+k5/2/36P9f43Vuz/HjTE/hcdkf8ZFpb/IxzO/i8r4f9vcX3+AAAA/wAA + AP5gYGHXR0pqAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIuL + 7wZ6eu3doKHz/4qL8P8lHuD/Jh7f/yUe3/8mHt//Jh7f/ykj4JtPTt4DcXLWBZCTyQqLjZw/k5Sdi5OT + nK2QkJegkpGZfYiGjFCGgYcgi4GIBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlHt4FJR7fNyUe + 35QlHt/kJR7f/iYe3/8mHuD/JR7f/yUe3/8mHt//Jh7g/yYe3/8mHt//JR7f/yok4P+Fie7/cHXs/zU/ + 5/8kOur/MU/v/1hx8v9bdPP/Ikbv/yJG7v8gO9T/HSuxt2Rq1W5MTEz/AAAA/wAAAP2KiovEZWZzFAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKB7gnMzfrntrf3/3d3 + 7P4lHt//Jh7g/yUe3/4lHt//Jh7fyzEs4Q4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlHt8NJh/fUCYf + 3641MuL0OTfi/jk34v87OeL+Pj3j/0A+5P5FQeX/U1Tn/oKI7v90eOz/U1Ln/iUe3/8lHt/+JSDg/yUq + 5P4jN+n/I0Tu/iNG7/8jRu//Ikbv/nKK8PMoKCj+AAAA/wMDBr+anKR8jZGoMQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIyM7wk9O+PnOjbj/z064/8lHt//Jh7g/yYe + 4P8lHuDjJR7fIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABKSOUWYmPpaF9h + 6MFeX+j3WVvo/1dd6P9VWef/VFXn/0ZD5f8oIeD/JR7f/yYe4P8lHt//Jh7g/yYe3/8mHt//JSDg/yUs + 5f8jO+r/I0Xv/3eP8/9AQED/AQIG/x82obV0gb97jZfDVgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAISE7gKkp/TAzM36/6ip9P4lHd//Jh7f/yUe3+4lHt83AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEpK5QFAPuMnMy7heycg + 4NQlHt/+JR7f/iUe3/8lHt//JR7f/iYe3/8lHt/+Jh7g/yUe3/4lHt//JR7f/iYe3/8lHt//JSTh/naA + 7/+Xl5f+ESBk/yNG7/4mSe79jJ/uxGqD7TQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAB9fe1ZfHvt/UM/5P8lHuD/JR7g7yUe3z8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADg04gErJeAyJh/fiSUe + 39olHt/8JR7f/yYe4P8lHt//JR7f/yYe3/8lHt//JR7f/yYe3/8lHt//Jh7g/25s6v/Z2uX/JCzY/yM9 + 6/8jR+//jaP3/1d18/wpTO+vKEvvLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAB0cuwFPDbjeicf4L8nIOCxLCXgNQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUe3wglHt84JR7fgiUe + 384mHt/6Jh7f/yUe3/8lHt//JR7f/yYe3/8lHt//JR7f/zAq4f94eOv/JR7f/yQe3/9udu3/m6r2/0pp + 8v8jRu//I0bv8yNF74YjRO4JAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlHt8mJR7fdCYe + 38MlHt/3Ixzf/iMb3/8jG9//Ixvf/iMc3/8oIuD+SUbl/4mL7v6qsPP/k5nx/ior4/8kOOn/I0Xu/iNG + 7/8jRu/LI0PtLgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgh3wItJ+AjY2PoboSI + 7sKDhO72gYPt/4WL7v+Nle//kJbw/3h87P9ZWOf/LCfh/yUe3/8mHt//JSTi/yQ36f8jRe7/I0bv8CND + 7W0jP+wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAO+MeMy3haycg + 4LwkHN/2JB3f/yUe3/4mHt//JR7f/iUe3/8lHt//JR7f/iUe3/8lJOH+JDfp/yNF7v4jRe6wIzzqFQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE9N5QFBPeMbLynhZiYf + 4LglHt/yJh7f/iYe3/8mHt//Jh7f/yUe3/8mHt//Jh7f/yUl4v8jPev+Iz7rVAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlHt8aJR7fXiUe + 364mHt/qJR7f/iUe3/8lHt/+Jh7g/yUe3/glJ+OWIzboCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlHt8IJR7fNSUe + 33UlHt+DJR/fSiUi4RQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////wAAAP///////////wAAAP///////////wAAAP// + /////////wAAAP///////////wAAAP///////////wAAAP///////////wAAAP///////////wAAAP// + /////////wAAAP///////////wAAAP/////wP////wAAAP/////AH////wAAAP////+AH////wAAAP// + //+AD////wAAAP////gAD////wAAAP///+AAB////wAAAP///wAAB////wAAAP///gAAA////wAAAP// + /gAAA////wAAAP///wAAA////wAAAP///4AAA/4//wAAAP///4AAB/gH/wAAAP///8AAB/AD/wAAAP// + 88AAB+AD/wAAAP//gAAAD8AB/wAAAP//AAAAD8AA/wAAAP//gAAAHwAAPwAAAP/8IAAAHAAAPwAAAP/4 + OAAAAAAAPwAAAP/wHwAAAAAAPwAAAP/gHwAAAAAAPwAAAP/gEAAAAAAAPwAAAP/AAAAAAAAAPwAAAP/A + AAAAAAAAPwAAAP/AAAAAAAAAfwAAAP/AAAAAAAAAfwAAAP+AAAAAAAAAfwAAAP8AAAAAAAAAPwAAAP4A + AAAAAAAAPwAAAPwAAAAAAAAAPwAAAOAAAAAAAAAAPwAAAAAAAAAAAAAAPwAAAAAAAAAAAAAAHwAAAMAA + AAAAAAAADwAAAPAAAAAAAAAABwAAAPAAAAAAAAAADwAAAPAAAAAAAAAAfwAAAPAAAAAAAAAA/wAAAOAA + AAAAAAAB/wAAAOAAAAAAAAAB/wAAAOAAAP4AAAAB/wAAAOAH///AAAAB/wAAAOAP///4AAAB/wAAAOAf + ///+AAAA/wAAAPA/////wAAAPwAAAPB/////+AAADwAAAP///////4AABwAAAP///////+AAAQAAAP// + //////4AAAAAAP////////+AAAAAAP/////////4AAAAAP//////////AwAAAP///////////wAAAP// + /////////wAAAP///////////wAAAP///////////wAAAP///////////wAAAP///////////wAAAP// + /////////wAAAP///////////wAAAP///////////wAAAP///////////wAAACgAAABAAAAAgAAAAAEA + IAAAAAAAAEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYF + JUwCAhClAgIOrQICDoUICCAmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJyZrCAkJJaoSEGr/GBSH/xQRev8LCkX/AAAC+RUVHFkAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAc3SPBBwbT7IkIM7/SkfR/4aF1/+2ttz/tbbd/2tskf8FBQXvJi5UDwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmp2pIHJzeaGjpMz/4+Pm//P1+//J0vb/nq3w/4WZ + 8/9/lPb/YXCv/xojUFUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtrnJAaOmszCztcGAysrNz/Dw7/3b3+//lKXw/1Fv + 8P8mSe7/IUXv/yNG7/8jRu//I0bv/yJG7/8oR9yjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACan7MxsLK9h83Nzdvx8fT/xMzx/4GV + 6v9AXuX/IUXu/yJG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8kMuf/JDjp/yM+5GEAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACsrrih6ev0/6+9 + 9/9rg/D/LlDs/yFF7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jQe3/Jh7f/yYe + 3/8kOerzIznpIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAho+7US5R8PsiRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//JDfp/yYe3/8lHt//JSbi/yM861YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqTe2iI0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//I0bv/yQx5/8mHt//Jh7f/yUq5P8jNug1AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJUftJSNG7/ojRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8lJuL/JR7f/yUe3/8lLeXyJCzlCQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJCDYDBQUfNgMDFFQDAxNBBAQcCAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAjRu+YI0fv/yNG7/8jRu//I0bv/yNG7/8jRu//I0fv/yNG7/8jRu//I0bv/yNG7/8jPuz/Jh/g/yYe + 3/8lLOX/JR/gvQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJCDtEBAQR3AoKNv8MCzz/BAQS/wAA + AOIFBB1GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAIz7rFyNC7e0jRO7/I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//JC7l/yUo4/8lI+H/JS/m/yUh4FgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKCTlbFhSC+iQd + 4P8lHen/JR3p/yUd5v8UE3D/AAAA+wgLM08AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAKDPmHSk05msoMeWUKTLleykx5VsmL+U4JDHmGCQz5wInM+ZBJSPh9yM75f8hQeL/I0fw/yNG + 7/8jRu//I0bv/yNG7/8jRu//Iz/s/yUw5v8lIuH/JSzl/yUi4cAlJOEBAAAAAAAAAAAAAAAAAAAAAAAA + AAARD108HRmy+SUd6P8kHuD/JCDP/yQgzP8kIMX/JB7J/yMiyP8eOca9HTTFJAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAJDHmiCUj4fswLOH/TlHo/1BS6P8mH9//Jh7f/yUg4P4lJeLwJCzl0Cov + 5OEmKsr/GR+k/xsnqf8fNsr/IUHj/yJE6v8jR+//JDnq/yUq5P8lKeT/Jh7f/yYe3+0lIeAhAAAAAAAA + AAAAAAAAAAAAAAAAAAAZFpQIGhiZ2yActv8jH8b/JSDT/yYe4P8mHt//Jh7f/yYe3/8mHt//Izvr/yNG + 7/AsTekyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsapx4kHtSQJh7g8iYe3/8mHuD/JR7f/yYe + 3/8lHt//Jh7g/yUe3/9nbe3/lKD3/4KM8/9qcvD/UVvm/zM8zP8eJbb/GiSm/xgZjf8aF5z/IhzI/yYe + 4PckI95CAAAAAAAAAAAAAAAAAAAAACQe3AMjHdIwHRmomiIeuv8lHt7/Jh7f/yYe3/8mHuD/JR7f/yYe + 3/8lHt//Jh7f/yQz5/8jRu//JEfuzmJ43RV+ib0bAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALCkEMBAQYYwYHKlgREGoEAAAAACId + xBElHt13Jh7g4iUe3/8lHt//JR7f/yYe3/8lHt//KCLg/0FA5f9UV+n/Zmvt/3yF8f+UoPb/jpr1/3iA + 8f9hbvD/OUbh/x8pxf8hL9KxIzLkMyQr5AMAAAAAAAAAACUh33YlHt/rJh7f/yYe3/8mHt//JR7f/yUe + 3/8mHt//JR7f/yUe3/8lHt//JR7f/yUe3/8kNOj/I0bv/ydL8P6SmLHHP0VjJgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAREF0YDQ1I1hIR + af8DAw7/AgISuw8MXQYAAAAAAAAAACUe3wYlHt9aJR7fyiYe3/8lHt//Jh7g/yUe3/8mHt//Jh7f/yUe + 3/8lHd//Skzn/2537/9+ivL/bXbu/19j7P8nIeD/JR7f/yUj4f8kLeXrJDTnrCQx5jAjLOAOISDEjR4Z + svgiHMj/Ix3Q/yQd1v8lHtz/Jh7g/yUe4P8mHt//JR7f/yUe3/8lIOD/I0Lt/yJG7/+bquT8FBQU9zA1 + SBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAcG3UEExRdwRoWof8kHeX/IhzU/wgILP8FBB9sAAAAAAAAAAAAAAAAAAAAAAAAAAAkKOM8JSDgqyUd + 3/k+Otv/Line/yUe3/8lHt//MCvd/1xf4/+HkfT/lJ/3/5Wg9/9WWer/JR7f/yYe3/8mHuD/Jh7f/yYe + 3/8lI+H0I0fv0SNG778fOdHgHTPC/xwwu/8cLLP/Gyep/xgXjf8hHMb/JB3W/yUe3/8lH+D/Izvr/yNG + 7/+Al/b/aWlp/wYGBvo8PkUKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAHh9ZeS4tuP8lHej/JR3n/yUd5/8fGr3/BQchowAAAAAAAAAAAAAAACNA + 7BYjP+xMI0HtgiNC7bMjP+zxkprP/0xK2f8mHuD/JR7f/1ZU1/+Ymcv/MCzi/2927/90e/D/iJL0/2tx + 7v9UVun/JyDg/yUe3/8lI+H/Iz/s/yNG7/8jRu//I0bv/yNG7/8jR+//I0bv/yA81/8ZIp7/GiOg/xkk + oP8YG5H/HCSw/x861P8hRe//z9Xq/wkJCf8wMDD0MjIzAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBxiEjk7jPMiG9j/FhSF/yEbz/8lHef/JR7o/w0T + SbEeOs9bIUTnmyNG79gjRu/9I0bv/yNG7/8jRu//I0bv/3KF2f+SnND/IzTo/yUk4f8wK97/ubvF/yYf + 3/8lHt//JR7f/yYf3/9xeO//fIXx/ykj4P8lKuT/I0Tu/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/x0v + vP8eHbT/IkTr/yNG7/8jRu//I0fw/yE+3P8cLLT/KTGe/56fpP8AAAD/Z2dn6wAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQRmkuLaH/HBiv/xcX + fP8iHNj/JB3n/yQu6/8jROf+I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/9UbuH/oqzN/0Be + 5/8jRu//ITjq/6uwyf88Pd3/JSLg/yUh4P8lI+H/JCjj/yQv5v8jPOv/I0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//I0bv/x40xf8gGrz/Jh7g/yQz5/8jRu//I0bv/yNG7/8jRu//Iz7s/yo92f8UFBb/AAAA/6Oj + o8wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAMDEGoHh6K/yEb0P8fGbz/JCjj/yM+7f8jR+//I0bv/yNG7/8jRu//I0bv/yJG7/8mSe//I0bv/zVW + 8P8mSu//fJLv/4CQ1v+JmNP/Ikfv/2N+9P+CkdX/X3ff/yJH7/8jR+//I0fv/yNH7/8jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//I0bv/yJF6/8bGqT/Jh7g/yUf3/8jPuv/I0bv/yNG7/8jQu3/JSTi/yUd + 3/8lHtv/AwMO/wICAv+RkJh0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAACAguzhUVfv8kMdz/I0Di/yNH7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG + 7/8iRu//PmDx/zdZ8f9TcvP/Q2Lx/3SM9f+SntL/eYvX/3+V7f9TcfP/W3Pg/4KS1f9EZfL/Nlrx/1h2 + 8/8pTO//J0rv/05t8v8pTe//IUXv/1p38/82WPD/KEzw/0Nk8v8eM8P/Ix3P/yYe3/8kL+b/I0bv/yNG + 7/8jQ+7/JSXi/yUe3/8hG8f/Kyi7/wkJLP8PDyT7WVmQFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGjCsAxIhcOAfOtT/I0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//I0fv/yNE7v9BX/D/aH/y/0hh7/9HZ/L/k5/Q/zJU6/+OndT/UnHz/zRV + 6v+or8v/U3P0/yJG7/8hRu//Xnv0/2aD9P9rhvT/QGLy/yRI7/84XPH/TGzy/2WC9P9FZfL/HSi2/yYe + 4P8lHt//I0Ds/yNH7/8jPuv/JSPh/yYe3/8iHMz/YmOk/+rq6v/Aws3/NzWv/zk6yG4AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIELhHCJE6cEjRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//I0bv/yJG7/8jPOv/JC7l/yUj4f8lHt//Ling/4CF7f8tKOD/sbX0/8PG + 2P87Otz/oKLT/15r5f8jLeX/r7LI/01c6v84TOv/Y3zy/3GH8/9ndu//SFzt/zJI6/+Vo/X/S1vs/zhJ + 6/8ySOv/JEHt/x4iuP8mHt//JR/g/yNG7/8kMuf/Jh7f/yYe3/8mHt//Kiio/+Dh5f/p6en/6enp/9HR + 3/8zTeO1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIkXuMyNG + 7+YjRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//Ikbv/01jyv+CiLz/U1HC/yYe4P8mHt//JR7f/zIw + 4f8yMeH/NTLi/0M+5P+Cg9r/YV7V/zg03P+Ul9D/JB3f/4qKzf9eX9v/KCHg/2lp6f88OeP/JR7g/yUe + 4P8lHt//JBzf/yUe3/8lHt//Jh7f/yYe4f8fGrT/Jh7f/yUe3/8lI+H/Jh7f/yYe3/8mHuD/Jh7h/xwb + Xf9+fn7/d3d3/5WVlf/W1dX/ZH3t915w4AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAMkznLSNG7+8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//JTjl/3J3t/+2t8X/oqKt/0ZD + xP8mHt//Jh7g/yUe3/8yLuH/T07m/yUe3/8mHt//Qj7b/4WEz/8lHd//iInO/zg03P9kYtT/iY/W/ygi + 4P8lHt//UFHm/yUe3/8mHuD/JR7f/yYe3/8lHt//JR7f/yUe3/8mHuH/Hhqz/yYe3/8lHt//O0To/zQ4 + 5f8mHt//JR7f/yYf4P8KCjn/AAAA/xAQEP85OTr/ZmZm/36R6/9XbdUuAAAAAAAAAAAAAAAAAAAAAAAA + AABIRcwRQ0DLR15h2W9fY9yPREXRhjtP48cpROb/IkLq/yFF7v8jR+//I0bv/yNE7v8kNej/JiTd/3t7 + tf+2t8b/trfG/7W2wv8zLsz/JR7f/yUe3/8mHt//JR7f/yUe3/8lHt//Jh7f/ykj3/+kpsn/Ixzf/z47 + 2/+EhM7/PTjb/56gzf+Vl/D/hojt/1VU5v8mHuD/JR7f/yUe3/8mHuD/JR7f/yUe3/8lHt//Jh7h/x4Z + r/8lHt//JR3f/3R/8f8/Qeb/JB3f/0hK5v8mHuH/DQxK/xgYGP/c3Nz/6urq/4WFhv9UZrn/S1/LMwAA + AAAAAAAAAAAAAAAAAAAAAAAAOzbIVFNR0POKjub/l5nq/z072P9MTOH/V1ni/11h4P9mcOP/Wmnl/yQq + 3v8lH+D/JR7f/3V1tv+2t8b/trfG/7a3xv+3uMf/cXK0/z8klf9GJIL/PiKZ/zAgwf8mHt//JR7g/yUe + 3/8kHOD/p6nJ/ysm3v8lHd//j5HN/zUx3f+qrMj/JB3g/yYg3/8lHt//JR7f/yUe3/8lHt//JR7f/yUe + 3/8lHt//JR7f/yUe4P8dGaz/Jh7h/yUe3/9JUOn/VFrq/yQc3/9ye+//JR7f/xcVhf+FhYb/6enp/+vq + 6v8+Pj7/S16//zc/1V0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCg+IJm5zsPZmd8X45OOPOJSHh/yUg + 4P8mHuD/JB3e/yYg3P8lHt//Jh7f/0E+xv+1tsT/trfG/7W2xf+dm6X/eFZX/2YxLf9lKCP/ZSgk/2Uo + I/9mKCH/YScx/0wlcP8xIb7/JR7h/4uLzv9KR9n/Jh7f/0ZD2v9/f9D/k5TM/zg03P8lHt//Jh7g/yUe + 3/8lHuD/Jh7f/yYe3/8lHt//JR7f/yYe3/8mHt//Hhmw/yUe3P8lHt//JC/m/yQt5f8lHd//fYfy/z89 + 5f8jHdL/rq/G/7S0tP9TU1P/LCww/y5F1P9aXur/T1PouDYz4w4AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAMUHpiCQv5v8lHuD/Jh7g/yYe3/8lHt//JR7f/yUe3/9qab//trfG/7a3xv+Wlp//azw5/2Uo + JP9lKCT/ZSgk/2UoJP9lKCP/Zich/2YoIv9mJyH/Zicg/1UlVP95bK3/bGvJ/yMczf8kHd3/lpfM/3h5 + 0f9cWtf/JR7g/yYe4P8mHt//JR7f/yYe4P8lHt//JR7f/yUe3/8mHt//Jh7f/yMczf8gG8D/JR7f/yUq + 5P8jQez/JR/g/3yG8f95gvH/JiDg/5WYwf9JS13/Ghsq/0FNrP8kNujeJR7f+i8q4f01MeI2AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAOTrkBy8y5PQ8OuT/JR3f/yUe3/8mHt//JR7f/yUe3/8lHt//d3e9/7a3 + xv+xscD/gXR3/2UoI/9lKCT/ZSgk/2UoJP9lKCP/SDV+/ypD2/9UaOT/d4Hg/3h/2f92eM7/QlLM/z5Y + 2f8eNcX/LT2//1Ffw/9wfsb/ZW25/xkZmv8bF6H/Hhmz/yIcy/8lHt7/Jh7g/yUe3/8lHt//JR7f/yYe + 3/8mHuD/Hhmw/yUe4P8lH+D/I0Tu/yND7v9qfvP/lKD3/2Rm6v92dnf/EBld/yFC6P8jRe/+KTPTNi0y + 2wstK+EUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE1M5yIlHd//QUDl/2Jo7P9cYuv/V1rq/1Za + 6v9RUuj/TE7o/4SGwP+2t8b/rK27/4Ftcf9lKCP/ZSgk/2UoJP9lKCT/ZSgk/04uav8nM97/MD/p/0BN + 6/9DUev/Q1Lr/yg86v8kOur/LEfs/2N48/97jfX/jZr2/4uZ9/96jPb/ZXnt/1Bi2/83SMX/ITC3/x0l + u/8gH7//IRvI/yQd2f8mHt//Jh7g/yAbv/8dGKz/Jh7g/yQu5f8jRu//K03v/0dg8P+JlOD/CgoK/w0a + VP8jR+//JDTkpAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5d+08mJny/7e5 + 9/8wK+H/JB3f/yQd3/8sJ+H/Tk/o/15i6/91ecn/pqaz/7a3xv+SkZn/bkE//2UnI/9lJyP/ZScj/2Un + I/9lJyP/Yigt/0sxhv8zLtn/JR3f/yYe3/8mHt//Jh7f/yYe3/8mH9//JCPh/yQr5P8uO+j/P1Hs/1Fo + 8f9ievP/don1/4iX9v+Ckvb/aH71/3OH7P9ic9L/LDu4/xkemf8UE3r/FBJ5/xsXn/8lHt7/JDXo/yNG + 7/8kSO//c3eF/wAAAP8NFkb/IzPVsyo8xQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAn6DzTKus9f96ee3/LCbg/yUe3/8mHt//JR7f/yUe3/8nIN//eIHu/5+iuP+2t8b/qqu4/5qa + pP+inqn/mZCa/5eMlP+YjZb/m5Od/52cp/ybnabdiomlrEhGu34nIN63JR7f+yUe3/8mHuD/JR7f/yUe + 3/8lHt//JR7f/yUe3/8lHt//JSPh/yQs5f8jNej/LEjt/0Nh8f9/k/b/sr35/5Og9/9vhPX/TWXp/x8y + vf8WGor/GBWQ/yQd0/8lKeT/W23v/ykoKP8AAAD/Ly876zI3cgMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAGJg6VRKRuX/YWDp/yUf4P8mHuD/Jh7g/yUe3/8mHt//PTvji3l+ + 50yKkNZjkpOil6Kireisrbv9p6i09ZucptaUkpqkiYSKcYB2ej9+cnoSAAAAAAAAAAAAAAAAAAAAACYf + 3R8lHt98Jh7f2SYe3/8mHt//Jh7f/yYe4P8lHt//Jh7g/yYe3/8mHt//Jh7f/yYe3/8kIeD/f4rx/2x/ + 8f8qSO7/UGvy/3uM9f91iPT/Ikbv/x83zf8ZIZz/IR+2soaG0NsAAAD/AAAA/1RUVfVkZnUTAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACjpPNi0dH7/8HC+P8pI+D/Jh7f/yUe + 3/8lHt//JyDgvTs34gMAAAAAAAAAAAAAAACcnasBk5ScDZaXnwUAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACUe3wElHt85JR7flyYe3+0kHd//JBzf/yQd3/8kHd//JR7f/yYf + 4P83MuL/aWzq/3V57P9WVef/Jh7g/yUm4v8jMuf/J0Tt/yJG7/8jRu//I0fw/yJE6teJkLDiAAAA/wAA + APtzdHiDio2dOQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeXntZkZD + 5f8vK+H/JR7f/yYe4P8mHt//JR7f3CYf3xQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtJ+AIVljnUnJ3 + 669pbur3bnPr/2lx6/9qcer/ZWjp/1hZ6P8wK+H/JR7f/yYe4P8lHuD/Jh7f/yYe3/8lJeL/JDTo/yND + 7f8hRe//jZa1/wAAAP8OGEjXaHWzUZKav2MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAH+B7j/Bwvj/zc/6/ykj4P8mHt//Jh7g6iUe3yYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAREPkEjUx4mYpI+DBJR7f/CYe3/8lHt//JR7f/yYe3/8lHt//JR7f/yYe + 4P8lHt//JR7f/yYe3/8lH9//Iynk/8vR9P8aHSr/IULe/yRH7vyGmurCaIHrKQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2duwCfHvt1FFO5v8mHt//JR7f6yUe3zIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwm4B8mH992JR7fzSYe + 3/0lHt//JR7f/yUe3/8mHt//JR7f/yUe3/8mHt//JR7f/yQd3//Fxvb/ZGfR/yQ16P8hQ+7/hZz3/1Fv + 8/sqTe+gJUfuHQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFhU5yIyLOGELCXgiS0m + 4CMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAlHt8iJR7fbCUe37smHt/5JR7f/yUe3/8lHt//JR7f/yYe3/8lHt//S0fl/zMu + 4f8kHd//b3Hr/6Cs9f9BX/D/I0bv/yNG7/MjRO5tI0TuAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJR7fEyYe32EmH9+0Lynh9zYv + 4v81LuL/NTDi/0VC5P9oaen/nKLx/6mw8/94e+z/JSDg/yQv5v8jQu3/I0bv/yNG77YjQe0ZAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAFdV5hBydetcaGnqr2Nj6fVbXOj/UVHm/0VD5f8sJuD/JR7f/yUe3/8mHt//JR/g/yQu + 5f8jQu3/I0bv6iNB7VEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABOTOUOPTnjVy0m4KwlHt/zJh7f/yYe + 3/8lHt//Jh7f/yYe3/8lHt//JR/g/yQv5v8jRO7+Iz7sVgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJyHfCyUe31IlHt+lJh7f7iYe3/8mHt//JR7f/yYe3/8mHuD/JSTi5iM76isAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUe3wYlHt85JR7feyUe36wlHt+XJSDgVCUm + 4hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////////////////////////////////// + /////////////////////////////////////////////////////////////wf////////8A/////// + //gB////////8AH///////8AAf///////AAA///////4AAB///////gAAH///////AAAf//////8AAB/ + B/////4AAP4D/////gAA/AH///8AAAD4AP///gAAAfAAf//+AAADwAAf//CAAAGAAB//4GAAAAAAH//A + fAAAAAAf/8BwAAAAAB//gAAAAAAAP/+AAAAAAAA//4AAAAAAAD//gAAAAAAAP/8AAAAAAAA//gAAAAAA + AD/8AAAAAAAAH/gAAAAAAAAfAAAAAAAAAB8AAAAAAAAAH4AAAAAAAAAH8AAAAAAAAAfgAAAAAAAAD+AA + AAAAAAB/4AAAAAAAAH/gAAAAAAAA/+AAAeAAAAD/4A4/+AAAAP/gH///AAAA/+A////gAAB/4H////wA + AB/w/////4AAB///////8AAD///////+AAH////////AAP////////gA/////////wH///////////// + //////////////////////////////////////////////////////////////////////////////// + //8oAAAAMAAAAGAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI1AAArxwAANvEAABnnAAAAkQAA + AAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABTghHYbwYlzf/5mV + 6f+opt//XFtp/wIBAHoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDgXwfnZuPZ6mo + r+bFyvj/vMj0/5Km9P94kPn/a4b+/ys7fMsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFxbwGIhn8qrKmecMjI + x8XFzOf2o7Lz/2eB9/8zVfL/G0Dv/xY77/8YPe//GT7x/yU/8/8nS+1ZAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeHZwCLm5 + uLa5w+/+jaD6/1Nw9v8mSfD/Fzzu/xk+7/8gRO//I0bv/yNG7/8jSPD/JDvr/yYc3/8kNOrrI0jyIwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGJ1z5YeRPf/Fjvv/xtA7/8hRfD/I0bv/yNG7/8jR+//I0fv/yNG7/8jSfD/JTLn/yYb + 3v8mI+H/I0bvUgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAB9C7hUiRe/jI0bv/yNH7/8jR+//I0bv/yNG7/8jRu//I0bv/yNG + 7/8jSfD/JSzl/yYa3v8lJ+P7I0nwKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAABcAAAAXAAAAAQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjRu94I0jv/yNI8P8jR+//I0bv/yNH + 7/8jRu//I0bv/yNH7/8jQu7/JiDh/yYj4f8lJuLWJS3lBAAAAAAAAAAAAAAAAAAAAAAAAAAqBQUbtAwL + QfIHByjyAAAAqAAAABoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACJF7wohQ+4KI0fvAQAAAAAjSvEFIz/swCM/ + 7f8jSPL/I0j0/yNH8f8jR/D/I0fw/yNJ8P8kN+n/JiTi/yUs5f8mIuF3AAAAAAAAAAAAAAAAAAAAAAIC + CDQWE4bqJB3f/ycg7P8lINv/FhR2/wUJHcULF08CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkRvUsIzLnsTU75uZARujgJyvkyiQw + 5qokOOp1JTHofiYn3PsZKLv/GDLM/xw95P8gQ+v/Ikbu/yQ87f8mL+r/JiLi/yYg4MgmHN8JAAAAAAAA + AAAAAAAAAAAADhgVhtQnIef/JiDY/yUf1f8mH9v/Jx7i/yUt4f8jSviwIUn/DwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKtAsKBzsuSoi + 4f4rJOD/Jx7f/yYc3/8lG9//JyDg/m947/50fun/Xmfa/0hU1P8sOsr/HSu8/xgZqf8fHcH/Jh7c2yYm + 5B0AAAAAAAAAACYe4QkmH+ElGhaVlyMexP8lH9b/Jh/g/yYe4f8mHuH/Jh3h/yYk5P8iRfD/I0fzlM/V + 8RPk4t0BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgIISAEB + AqUDAhI+AAAAACce6CklHN+TJh7f7yYe3/8mHt//JR3g/zQw5P9KSej/WVrt/3B28v+AifX/d4Lx/15p + 4/8rLsz/ISvcwyRB7k4jSvEeAAAAACUb25AlG9b9Jx7o/yYd5P8mHeP/Jh/l/yYf4f8mHuD/Jh3f/yYl + 4v8ZPvH/YXz2+UZGRIwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAFBRJLFxWH/BwYqf8GBh/5AAAANQAAAAAAAAAAJiLhDiYf4GgmHd/MKR/e/yki3v8gGN//HRPd/zk2 + 4f99h/L/l6X4/3R88v8pIeP/Hxbf/yYg4P8lI+L6JTTomCM86GUfKb7OHSOy/x4juP8gIbz/IBu9/yUb + 1v8mHN7/Jh/l/x837f9JbP//d3uI/wUEAJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAUGBRspKZHlJB3m/ygf+/8fGr3/AAAAewAAAAAlSvoDI0XvHyRB7UkiOuyQMkXm9ISH + 0P8jHOD/IRfg/39+z/9SUuD/cXjx/3yE8v9qcO7/S0vn/yUb3v8lH+D/JDrq/yNK8v8jSfL+I0ju/yNJ + 8P8fPtP/GSOa/xskrP8dIa//Hia7/xg43P+cq/D/IyEW/3V1dYIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAABkaPogtKc//GBSS/yQb3v8lG+L/FyeQuCNJ9ZkjR/DUI0fw+CNI + 8P8iR/D/H0bw/5ikz/9GWeL/FB3o/3d30f9YU9b/GQ7f/yAW3v9WVun/YGXs/yMn4/8jQe3/I0nw/yNG + 7/8jR+//JEn1/yA50f8fHrn/Iz3m/yNK8/8jR+z/Hz7X/yM4xf95fZn/AAAA/8DAwHcAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhgYYtUeGLb/Gxah/yUl6P8kPO7/JEn3/yNH + 8P8jRu//JEfv/yNH7/8kR+//K07y/4+e2v9ziNn/IUj0/1px4/94g9X/GCzq/yEy6P8dM+n/Gzjs/yFF + 7/8jSPD/H0Pv/yFF7/8iRvH/IUPh/x4dtf8nG+P/JDjs/yNJ8P8jSPD/JTfu/yYq8v8HBzf/FxcM/+/v + 7UoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFg8NVO4hKMz/JDrr/yNH + 7v8jSfD/I0fv/yNG7/8jR/D/Jkvw/zZb8v88X/L/VXX2/4uc5/90h9f/fJPs/0Ji7v+Hl9T/NFrz/z1h + 8v82WvL/NVfx/zdZ8f8hRu//Q2Py/zFU8f81WvX/HjHI/yQZ0/8mKOX/I0jw/yNJ8P8lMef/Ihjd/ysj + zf8XFlT/KShD3bGs/QkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFSqOQxw0 + uP0jSPH/I0ny/yNH7/8jR+//H0Ty/x1C8/8jRO//Ij3s/ytA6v9oefD/Sl7v/4GW5/9oeNr/laPe/zNU + 8f+DkdP/WXXw/zJV8f9QcfP/dpL2/0Vm8v9HavP/W3r0/1Bx8/9QcvP/GiK//yYa3/8kOur/I0fv/yUr + 5P8kGuL/KSS//8HC0v/g4dn/UVTD/hMR4zQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAkSfhnJUr9+SRJ9/8jR+//I0fv/yNI8P8cQfL/NlTg/0xb0/8oJt7/JR3g/ycf3/9KSeX/RkLl/6eo + 6f9ubNX/bW3Y/01O4P9sa9P/W13d/2Bm7f9weO3/Ojvl/ycq4/9OT+f/OTrl/ygp5P8mLd//IB/B/yce + 4v8iLeb/JCLi/yYc3/8fF9//R0aT/8bHu//Ix8L/0dbn/y1N7nMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAB9F8XUfRfD/IETw/yJG8P8jSPD/I0jx/xs37v9SYM3/ra+3/4yJsf8jHN7/Ixzj/ysl + 4f8+POP/JR7g/yoj3f94d9H/NC/c/3Bu0/9TT9f/foDY/yUc4v9KSOX/Jh7f/yUc3/8hGN7/Ixrf/yUc + 4P8lG9n/IRvA/yQc4v84OeX/NDLj/yQb4P8lHuL/CQk3/wYGAP8xMTD/mpmW/1Vz+aYAAAAAAAAAAAAA + AAAAAAAAIhy+TTQyx6ZhZdu6Q0TRsURX5PE8Vej/OFbt/ylJ7f8jPOz/HiXn/1dVxf+5uL//w8XJ/4SG + yP8gGuD/KB/a/yQd5P8fG+v/Ix3n/yEa4f97etH/KiTf/2lo1P9ZV9X/kZPU/3R17f9QTub/JBzf/yYe + 4P8mHt//Jh7g/yYe4P8lHtv/IBq6/yAX4P9XXuz/TlDp/zc05P8wK+r/AgBA/4GCef//////eHdz/zVP + 068AAAAAAAAAAAAAAAAAAAAAPTrOPrO086PCx/nSRkTj9i8q3v84M93/SUnf/zo53f8fFuH/QDrN/7W2 + vv+8vsv/rq+8/459hf9eMU7/XCU7/1cmT/9II33/NCC0/xwU4/9yctn/SEPb/zYw4P93d9P/fHvQ/ywl + 3/8jG+D/Jh7g/yYe4P8mHuD/Jh7f/yYe4P8mHuH/Hxq5/yQb3f8vNOb/MTfl/0xL5/9PUPD/HRih/9TV + 0f+3tq3/OTpA/zxL3vI/O+duJhvfBAAAAAAAAAAAAAAAAAAAAAAAAAAAIjXonR8f4v8hF9//IBff/yMa + 3/8dFeH/dXS//8LEyP+lprP/b0dG/2EfGf9nJx3/aCge/2gpH/9mKyP/ZSsp/00fWP9zYqb/ZmXL/xEJ + zP9kY8v/kJDJ/zcw1P8gFt3/JR3i/yYe4/8mHuL/Jh/g/yYe4P8nH+P/IRzE/yQc1P8jJ+X/HDHp/0xM + 5/+CivP/SUTs/4OEm/8PEB7/M0Sj/CYt6uY8OOT/LCXhMwAAAAAAAAAAAAAAAAAAAAAaEt4PKCji5jw7 + 5f80L+P/MCvi/y0n4v8jHN//hYa+/7/Cyv+IfYL/YSEc/2UoI/9mKCL/ZSgn/zE5u/9DWej/d37a/3x+ + y/9TYc7/L07d/zBI1P9lc9n/h5bb/0xVwv8oLLH/IiKz/xwawf8dF9D/IBjY/yAX3v8kG+P/JR7b/yIb + xP8nIOb/ID7t/zZV8P9+jPj/hYjl/zEzN/8aOc7/JD300iUe4BEjG98ZJh7fAwAAAAAAAAAAAAAAAAAA + AABMR+YoZWHq/Wpr6/9EReb/R0jm/1ha6v9ka/D/io7E/7i6wv+QipH/ZS0o/2AeGf9hIBr/YiEZ/1Ak + VP86L7n/MDPv/ywu7f8mKuj/ICTm/zE66/9FUe//VGPy/2Z49P9ug/P/bYPt/2R55P9TZ97/QlDU/0dM + zv8uMMn/FxW2/xIOg/8eFq3/Jijp/yFG8f8yWP7/ZW2Q/wAAAv8YMtrzHRrpPwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACytPY1pqf1/11Z6f8cE97/Ihrf/yMb3/8qJeT/dXvd/6+wvP+ztcP/mpOc/5aB + if+ReoD/jXZ8/453dPqOg4LcdHOdqi8qy5ojGuLpJh3f/yQb3/8hGN7/Hxjf/yEb3/8kJOL/KzPn/zdF + 7P9KXvL/V3D1/5+y+v+QpPT/XHDh/zlJv/8WHpL/HBWi/x8f2v9CVO//MTMz/wEBAf9rabtTAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABfYek8hIPv/1pX6P8gGN//Jh7g/yMb3/8yLuKRk5/9Lo2U + yU2NjI+cnJ6nxpaao62JjZN3foKFSnR3eSdsbm0IAAAAAAAAAAAiGuYVJh/gXCYf4LsmH+D3JRzg/yAY + 3v8hGN7/IBbe/x8U3v8bEt7/PEDm/3uH8P9HVu3/SmDz/2yE/P8xVfL/HDnN/xgivdlubc+5ERAH/xUV + FPzGxbtVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACVmPJGlpby/1BN5/8hGd//Jh7g/yUd + 4LwoIN8FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAkHeAiMCvhe09P5s1OTeb8TU3m/0xN5v9XWej/cXPr/0tI5f8iGd//Hxrg/yIl4/8lNun/JEPy/x9J + +/hxg8jxBgQA/ycuS53IyMdcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABXXOgyn6Dz/2xq + 6/8fF9//Jh/g1iUe4BcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKF7gFIROU3LijhijEt4t0xLOL/Jh/f/yEZ3/8mHuD/Jh7f/yYd + 3/8mHN//Jh/g/xsj5/+SmuD/MjhK/xk/5fCGmefBTmvuJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAYmDqrjw34/8jHN/TJh/gIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQd4AokHeBDJR7glyYe + 4OEmH+D+Jh7g/yYf4P8lHuD/JBzf/xkP3f+HhOv/XF7d/xIj6v+Bmfj/RGf09R5D75MjR+8TAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAHRbfCiIb3yomH+APAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACYf4AkmH+A4Jh/gjCYe4NMmHt/9Lifh/zIr4f84M+P/S0fo/4KB7P+hpvP/MkDp/yA/ + 7f8jSfDjI0jwSQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8X3wNgYuk3fYDtg1ZU589dX+j/aWvq/2Fh + 6f8zLeL/Ixnf/yYg4P8lMOb/I0Lt/yNJ8JAjR+8QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABoS + 3gUgGN8zHhXeex4W39AjG9/7Jh/f/yYe4P8mHN//JiDg/yQy5/8jRO6NAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACYf4AMmH+AqJh/geCYe4LgmH+DqJh7f3CYb36IkO+srAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA////////h/////////8AAP///////wAA////////AAD///////8AAP// + /////5yA////////AAD///4H///cRv///Af//wU////wB///AAD//wAD///i///+AAH//y8D//8AAf// + /////wAB8P+6Cf//gAHgf/////iAA8A/bwD/4AADgB/////gAAYAB2QA/4gAAgAP/v//BgAAAA+DAP4E + AAAAD+j//gAAAAAPHAD8AAAAAA////wAAAAADwAA/AAAAAAPm+/4AAAAAA8AAPAAAAAADwAsAAAAAAAP + AAAAAAAAAAMAAOAAAAAAAwAAwAAAAAADAADAAAAAAB8AAMAAAAAAPwADwAAwAAA/AAHAf/4AAD8AAMD/ + /4AAHwAB4f//8AAHAAHj///+AAMAAP/////AAAAI//////gAAAD//////wAAAP///////wAA//////// + AAD///////8AAP///////4gH////////AAD///////8EgP///////wAYKAAAACAAAABAAAAAAQAgAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFQAA + HIUAABOYAAAAPQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAUEUSaVVC1/3V0qf8sLDHPAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISC + eBWfnZVYqay1tJOf6v97kvn/Y3/8/0Zj5P8bLXlQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiG + eyqqr8W3i5zn8WJ89P84Wfb/HULy/xg+8P8bQvD/IDLr/yc19d0lTP4VAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAV2i2GTFT9egXPvL/GkDv/yBF8P8jR+//I0fw/yNJ8P8lKeT/JiDh/yNI7zUAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAIUXvXyNJ8P8jSPH/I0fw/yNH8P8jSPD/I0Tu/yYj4v8mJeLsJTTtDgAA + AAAAAAAAAAAAEAUEGo0GBR+uAAAAXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACZO/wUkRO03LEbsUSRB7DYjRu8aIjnquxw34P8dQer/IUfx/yNK9P8lOu7/Jibj/yYi + 4Y4AAAAAAAAAAAAAAAwWE4PNJSDf/yYg2v8YFof/DR97XgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAHS+/Fysm8NE1MOP/KCHg/yMf4fM4OOblX2fb/0pY1v8vRNj/HzDF/yAi + x/8mIuC2AAAAACYh4wEkHNIeFxSCnCYg3P8mIN7/Jh/g/ygf6v8bOPn+Wnn/Ue3s6wUAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAABQcGKJoDAQyTFRB9CyQb4U8mHOCzJhvf+Sce4P88OOj/TUvq/3R8 + 8f96he3/NjjW/x8k4L0kPexvJS/mLyMez8klHdb/JB3W/yYe3v8nHeP/Jh/j/yhG+v9baqfuCAcEIwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWFk+OJR/k/xsUpf8DBxVDAAAAACU67iEgMeqOSlHe+Dkz + 2/8xKNr/aWvg/3mA8/9hZO3/ODDj/yQa3v8kPu3qIkTl5CE+3P8fN8r/HCOs/yAhv/8ZJs7/ZH3p/zo4 + L/eGhocbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQYKIiYjoPodFbT/JSLn/x46ycskSvXWIknw/htD + 8v9MauX/coHZ/y9A5v9patT/GBTh/zxA5/89Sur/IDvs/yFH8P8iR/P/IkTn/yEjx/8jPun/Ikvu/x87 + 4v8xN3//QUAy7P///w8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAQhPHR6o/yM04v8kRvL/I0n2/yJH + 8P8pT/H/Mlfy/1578f+Cldn/WXnw/2yC3f80VvD/K0/w/zBV8f8oTvH/NFjy/zBX8/8gLcr/JSDe/yNH + 8f8kPu7/JSHo/w0KZ/9VU3GnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH0DUBRs1s6UiROb/JEvz/x9G + 8v8fQ+//JT7q/yIw6P9MV+v/a3jv/4SO3f9peOL/YnHd/1Fn6v9WcPP/WW3w/0Zf7/9OZPD/Plfp/yEg + yv8kLun/JDbq/x0V3/9oZMb/6Ond/15k28wTG+YEAAAAAAAAAAAAAAAAAAAAACMz2g0iS/i9IUj6/yNK + 8/8cQfL/OE/f/42Uvf88Ntb/Hxfp/zUy5v82MOP/Z2TY/1ZR1/9lYtX/Y2Db/0pG5/8oIeD/KCDg/yYe + 4f8iG9f/IRvK/y8u5/8tKOP/IBje/yUlSf9mZVv/mqTK/CFL9yIAAAAAAAAAADMuxlpzdN2+Rkjb2j5O + 5v88Uun/ITPp/zg51/+qqcL/vL/G/0k8tP80Ha3/LR/H/xsU5v9VU93/R0Lb/2lo1f95edv/Qj7n/yUe + 4P8lHuD/Jh/h/yUe2f8eF8T/QEHq/0VH6P82M+X/Ly1o/9jXxf9gZ4j/Kj3uRQAAAAAAAAAAa2nhC62x + +DEpMObXIhne/ykg3v8dEt7/iIfF/7i6vf96V1j/aSsf/2goHf9iLTT/Uyhi/2BRrP9FQ9H/VFLM/3Fv + zP8aEdj/Ihrf/yUd4f8mHuH/Jh/h/yMbyP8kJeT/Kjbn/2pu8f9vbd3/Y2Vn/yc2n/s6OOzXKSDgHAAA + AAAAAAAAJx/gDDg45OpCQOb/OTbk/z895P+eocr/nZaZ/14dFv9jHhP/ViU+/zQ/1f9dY+L/TVfW/yg/ + 3f9TY93/eYjd/0xXy/8+Rcv/MDPQ/yYk1v8hGtf/HBXA/yEayP8jPvL/XHf//2Fjj/8LIo3/Hin3dyEX + 3wsmHt8DAAAAAAAAAACKifAliIfw+jYx4/8sJuH/QkHp/4+Tz/6urrb/jHR5/4NgY/9/W1n/eGR/5UpJ + v8AgGuLZJR7i/ycj5P8rK+b/PUPs/0lW7v9NYO7/Y3nu/3mJ5P9BT8P/ICWc/x0cw/80SfD9IiYr/zI2 + fasAAAAAAAAAAAAAAAAAAAAAAAAAAHx+7i58e+7/KyTh/yEZ3/8uKOOQjZfzIoWHlleNkJR8g4eLWnV5 + eydydGsIAAAAACcg3wQlHuFMJR7gqygh4O8uJ+H/MCrh/zEs4v9gZOv/T1jt/0ZV8v87WPL/GDbX+0pT + xNAKCAD4mJWIawAAAAAAAAAAAAAAAAAAAAAAAAAAbHHrKYiH8P8tJuH/Ixzfuigh4QQAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUVDmFGFh6Wc/PeS+REPl+Tk04/8jGt//Ihnf/yMe + 4P8fKO3/Xm7W/yg1ZPNwhNyiW3XoFAAAAAAAAAAAAAAAAAAAAAA6OOMCSkXmlScg4KgkHuAOAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiGt8jIxzgdSYf + 4L8mIOD1Ixvf/xsQ3f9PSOT/TlHp/26B9/85WvLpH0bwbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACIa3xhEQeRlXl3ouUdF5fNoaOv/X1zo/yUh4f8kNOj/I0XvtiNI8CMAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHhbfFyEZ32MgGN+1Jh7g7iYc3/8mIuH/JD3rkgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYf4BQmHuBNJh/gjyYb + 3oMkNOgqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////////////////4f///+ + D///8A///8AH///AB///4AYf/gAMD/4AEAP4AAAD+EAAA/AAAAPwAAAH4AAAA8AAAAMAAAADAAAAAYAA + AAGAAAAPgAgAD4P/AAeH/+AD///8AP///4D////g/////////////////////ygAAAAYAAAAMAAAAAEA + IAAAAAAAYAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkI + KwwFBR5yAwMOWwYGCQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAk5ScHnR0psSFi+f+anbD/iIsYUwAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdHV9Arm9 + 0HWUod3LbYPs/TJT7v4iRu/+I0Pu/iQ048kfONQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADVU6KkjRu//I0bv/yNG7/8jRu/+Iznq/yUi + 4f4hN9sVAAAAAAAAAAABAQoBAQEGAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACM+6yQjQ+72I0bv/yNG7/8jRu/+JC7m/yUm4tAAAAAAAAAAABAOXlEWEoboDw5Y4goP + PTMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwjsxcrK+HNLSzi5yUk4sU6P+TeP07U/ig/ + 3P4fMc3+JCPc+CUl4TwAAAAAGxeeMSEcxPYlH9n+JR7e/iM35udEYOUqAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAEA9ZYgwLR60XFIsVJSDgbCUf4NkoIeD/Ojfk/2px7v9wevD+Jyja6yUp45UkLOFsISHK8yIe + yf4kHdj/JR7f/ypB6/5KU3u/AAAAAAAAAAAAAAAAAAAAAAAAAAAcHVgrIx+y+SMc2/4VJoxwI0LteyNB + 7bVdbdz8KSzi/2Ni1v9RUuj+U1Xp/yUq5P4jRO7/I0bv/yA31/4eMsb/HzLM/1RlyP4xMTG5AAAAAAAA + AAAAAAAAAAAAAAAAAAATFVl/HyTA/yM77f4jRu//I0fv/zJV8P5kfOb/ZHzl/1903v8wSe3+KEbt/ylM + 7/4uUfD/JUPh/yMg1f4jQ+7/JDLn/xkZiv5GRVmFAAAAAAAAAAAAAAAAAAAAACFC4hoePM/OIkbv/iJG + 7/4uS+T+JDHn/kJL6P5ocej+cXrZ/mNu3P5EV+v+Wmvu/kBS7P47TOv+LDfW/iUp5P4lLOX+MCvD/tHR + 1/5qc9KgAAAAAAAAAABAPskvTFLYUjNQ69slR+3/Iz7r/2Bq0P6YmL3/JR7g/y4p4f4mH9//WFXW/19d + 1f9nZt7+OTXi/yUe3/4lHt//IxzO/zU05P41MuP/GBSK/2tra/5mc7HQAAAAAAAAAAB8e+AzXWDonSwo + 3/40MN/+Qj3T/rCxv/6DZWn+YCk1/lkmSf5EI4f+XVfJ/lFO1v5XVNf+JR7f/iUe3/4lHt/+IxzN/igm + 4f48Quf+SUnT/o2Olv40Pa3zPDzjYAAAAAAAAAAASUjmiUNC5f48OeT/a2zV/6Ccpv5lKCT/ZSgk/z87 + sf5PVeH/MT/e/0JP2/9gbtv+SlTS/z5D1v4uMNf/JCDK/x8auv4oQez/ZXPU/xgoffsmN99EJiPHAwAA + AAAAAAAAi4rwnT864/4lHt//U1PfsqGitr+XkJndjoCGs4l+iIJSTr9ZJh/enCUe3/AlHt/+JSTh/zA2 + 5v5qd/D/W2/u/zdKy/4fKMT3OD147jU2TKEAAAAAAAAAAAAAAAAAAAAAjo7woEA65P4lHt+pNC/QAQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACUf2ApAPeNZSEfltEE/5PpBPuT+JR7f/iUn4/4kNej+RU14/Vlp + sqJjedgFAAAAAAAAAAAAAAAAZWPpNC0n4IIkHdsHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACch3RUlHt9mJR7ftSUe3/clHd//UE3i/2t47/4vTO7dI0XuSAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAExK + 4RBhYehcUE/msDUw4vUlH+D/JC3l/SM964AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQd2Q0lHt9QJR7feCQm + 4i8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP// + /wD/4f8A/8H/AP4A/wD/AM8A/wGHAPgBAwDwAAMA4AADAOAAAwDAAAMAAAADAAAAAQCAAAEAgAAHAIfg + AwCP/AEA//+AAP//8AD///8A////AP///wAoAAAAEAAAACAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMi81LDIxVJ4ODgw5AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACFhok9goy7m2B14O9Laf//Kj7KyB88 + vQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOFjkVh5G+f8aQfT/HkHw/yUo + 7PcjPOwUBg4uAgMDEUsAAAAVAAAAAAAAAAAAAAAAAAAAAAAAAAATJ44GKTf4jiIt6ZoyQeLoMk7j/yQ3 + 3f8lJueFIRi9BBYQfaAlHNj/FR+30Zmn2RwAAAAAAAAAAAAAAAAHBhwvEQpfvxkbmkAmKu2oNjPg/lpZ + 4/9eX+T/JirjviIy5bMjLNb/IB7Q/zNC5f9UW2mDAAAAAAAAAAAAAAAAExNbniMv6v8cQubiK1P072B4 + 5P9YaOD/O0zq/zJP7/8qUvH/IjTZ/yE87f8lLab/npusZAAAAAAjHMELIjrRQB5D4OouUe7/QlTk/ywx + 8P9gZeb/Z27b/1Ng6P88Rur/Mjjh/ygp3v8hIdv/dXGV/1Zm228AAAAAXl3cWDpB5+woL+j/hofF/4Bg + c/8/GXj/Rje7/11d1P9STtj/HhTd/x4T2f8nI9v/QUTm/21ulP8/SsK6IiXpCWVj6hNJRufxPDnl/5iV + tf57Sjv/Zkts70RFzOI0OuH/Rk3e/zxF3v9IT9//KCzD/zVG1v04QX3jOzfbLycc3wN+g+4jV1Po/ygi + 6JF9g7slenx3NXZ2ZxFDPq0FHxXnPz045Zs9O+XoR0jo/yw05/8sOcD9W2J+twAAAAAAAAAALirhBi0m + 4UcsJuoDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJyDgDSEZ304rI+GfRUDk5lle7f8oP+3cIUbvRgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAERC5QsiGt9IJRrenCUu + 5YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP//AAP//wAD/j/nFvgf5xb4A4AU4AEAAMABAADAAQAAAAEAAAAAAAAAAAAAAAMAAB+A + AAP/8HMf//8AAP//AAQ= + + + \ No newline at end of file diff --git a/AirScout/HistoryFromURLDlg.Designer.cs b/AirScout/HistoryFromURLDlg.Designer.cs new file mode 100644 index 0000000..fc77c6d --- /dev/null +++ b/AirScout/HistoryFromURLDlg.Designer.cs @@ -0,0 +1,231 @@ +namespace AirScout +{ + partial class HistoryFromURLDlg + { + /// + /// Erforderliche Designervariable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Verwendete Ressourcen bereinigen. + /// + /// True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Vom Windows Form-Designer generierter Code + + /// + /// Erforderliche Methode für die Designerunterstützung. + /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.btn_History_Start = new System.Windows.Forms.Button(); + this.label3 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.label5 = new System.Windows.Forms.Label(); + this.tb_History_Select = new System.Windows.Forms.Button(); + this.btn_History_Cancel = new System.Windows.Forms.Button(); + this.label6 = new System.Windows.Forms.Label(); + this.ud_History_Stepwidth = new System.Windows.Forms.NumericUpDown(); + this.tb_History_Directory = new System.Windows.Forms.TextBox(); + this.dtp_History_Date = new System.Windows.Forms.DateTimePicker(); + this.tb_History_URL = new System.Windows.Forms.TextBox(); + ((System.ComponentModel.ISupportInitialize)(this.ud_History_Stepwidth)).BeginInit(); + this.SuspendLayout(); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(28, 128); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(81, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Download from:"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(28, 183); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(33, 13); + this.label2.TabIndex = 3; + this.label2.Text = "Date:"; + // + // btn_History_Start + // + this.btn_History_Start.DialogResult = System.Windows.Forms.DialogResult.OK; + this.btn_History_Start.Location = new System.Drawing.Point(183, 245); + this.btn_History_Start.Name = "btn_History_Start"; + this.btn_History_Start.Size = new System.Drawing.Size(106, 23); + this.btn_History_Start.TabIndex = 7; + this.btn_History_Start.Text = "Start"; + this.btn_History_Start.UseVisualStyleBackColor = true; + // + // label3 + // + this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label3.Location = new System.Drawing.Point(12, 9); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(523, 52); + this.label3.TabIndex = 8; + this.label3.Text = "Use this dialog to download one day of plane positions history and convert it to " + + "AirScout\'s database import file format."; + this.label3.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // label4 + // + this.label4.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label4.ForeColor = System.Drawing.Color.Red; + this.label4.Location = new System.Drawing.Point(12, 61); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(523, 52); + this.label4.TabIndex = 9; + this.label4.Text = "CAUTION: This will download a large file (abt. 8GB) and will use abt. 50GB of tem" + + "porary disk space!"; + this.label4.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(28, 154); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(52, 13); + this.label5.TabIndex = 10; + this.label5.Text = "Directory:"; + // + // tb_History_Select + // + this.tb_History_Select.Location = new System.Drawing.Point(429, 149); + this.tb_History_Select.Name = "tb_History_Select"; + this.tb_History_Select.Size = new System.Drawing.Size(106, 23); + this.tb_History_Select.TabIndex = 12; + this.tb_History_Select.Text = "Select"; + this.tb_History_Select.UseVisualStyleBackColor = true; + this.tb_History_Select.Click += new System.EventHandler(this.tb_History_Select_Click); + // + // btn_History_Cancel + // + this.btn_History_Cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btn_History_Cancel.Location = new System.Drawing.Point(317, 245); + this.btn_History_Cancel.Name = "btn_History_Cancel"; + this.btn_History_Cancel.Size = new System.Drawing.Size(106, 23); + this.btn_History_Cancel.TabIndex = 13; + this.btn_History_Cancel.Text = "Cancel"; + this.btn_History_Cancel.UseVisualStyleBackColor = true; + // + // label6 + // + this.label6.AutoSize = true; + this.label6.Location = new System.Drawing.Point(28, 207); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(33, 13); + this.label6.TabIndex = 14; + this.label6.Text = "Date:"; + // + // ud_History_Stepwidth + // + this.ud_History_Stepwidth.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScout.Properties.Settings.Default, "Analysis_History_Stepwidth", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.ud_History_Stepwidth.Location = new System.Drawing.Point(115, 207); + this.ud_History_Stepwidth.Maximum = new decimal(new int[] { + 60, + 0, + 0, + 0}); + this.ud_History_Stepwidth.Minimum = new decimal(new int[] { + 1, + 0, + 0, + 0}); + this.ud_History_Stepwidth.Name = "ud_History_Stepwidth"; + this.ud_History_Stepwidth.Size = new System.Drawing.Size(100, 20); + this.ud_History_Stepwidth.TabIndex = 15; + this.ud_History_Stepwidth.Value = global::AirScout.Properties.Settings.Default.Analysis_History_Stepwidth; + // + // tb_History_Directory + // + this.tb_History_Directory.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::AirScout.Properties.Settings.Default, "Analysis_History_Directory", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_History_Directory.Location = new System.Drawing.Point(115, 151); + this.tb_History_Directory.Name = "tb_History_Directory"; + this.tb_History_Directory.ReadOnly = true; + this.tb_History_Directory.Size = new System.Drawing.Size(308, 20); + this.tb_History_Directory.TabIndex = 11; + this.tb_History_Directory.Tag = ""; + this.tb_History_Directory.Text = global::AirScout.Properties.Settings.Default.Analysis_History_Directory; + // + // dtp_History_Date + // + this.dtp_History_Date.CustomFormat = ""; + this.dtp_History_Date.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScout.Properties.Settings.Default, "Analysis_History_Date", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.dtp_History_Date.Format = System.Windows.Forms.DateTimePickerFormat.Short; + this.dtp_History_Date.Location = new System.Drawing.Point(115, 177); + this.dtp_History_Date.Name = "dtp_History_Date"; + this.dtp_History_Date.Size = new System.Drawing.Size(100, 20); + this.dtp_History_Date.TabIndex = 2; + this.dtp_History_Date.Value = global::AirScout.Properties.Settings.Default.Analysis_History_Date; + // + // tb_History_URL + // + this.tb_History_URL.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::AirScout.Properties.Settings.Default, "Analysis_History_URL", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_History_URL.Location = new System.Drawing.Point(115, 125); + this.tb_History_URL.Name = "tb_History_URL"; + this.tb_History_URL.Size = new System.Drawing.Size(420, 20); + this.tb_History_URL.TabIndex = 1; + this.tb_History_URL.Tag = "Planes_History_URL"; + this.tb_History_URL.Text = global::AirScout.Properties.Settings.Default.Analysis_History_URL; + // + // HistoryFromURLDlg + // + this.AcceptButton = this.btn_History_Start; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btn_History_Cancel; + this.ClientSize = new System.Drawing.Size(577, 280); + this.Controls.Add(this.ud_History_Stepwidth); + this.Controls.Add(this.label6); + this.Controls.Add(this.btn_History_Cancel); + this.Controls.Add(this.tb_History_Select); + this.Controls.Add(this.tb_History_Directory); + this.Controls.Add(this.label5); + this.Controls.Add(this.label4); + this.Controls.Add(this.label3); + this.Controls.Add(this.btn_History_Start); + this.Controls.Add(this.label2); + this.Controls.Add(this.dtp_History_Date); + this.Controls.Add(this.tb_History_URL); + this.Controls.Add(this.label1); + this.Name = "HistoryFromURLDlg"; + this.Text = "Get Planes History From URL"; + ((System.ComponentModel.ISupportInitialize)(this.ud_History_Stepwidth)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Button btn_History_Start; + public System.Windows.Forms.TextBox tb_History_URL; + public System.Windows.Forms.DateTimePicker dtp_History_Date; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label label4; + public System.Windows.Forms.TextBox tb_History_Directory; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.Button tb_History_Select; + private System.Windows.Forms.Button btn_History_Cancel; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.NumericUpDown ud_History_Stepwidth; + } +} \ No newline at end of file diff --git a/AirScout/HistoryFromURLDlg.cs b/AirScout/HistoryFromURLDlg.cs new file mode 100644 index 0000000..9138574 --- /dev/null +++ b/AirScout/HistoryFromURLDlg.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Net; +using System.IO; +using System.IO.Compression; +using System.Windows.Forms; + +namespace AirScout +{ + public partial class HistoryFromURLDlg : Form + { + + public HistoryFromURLDlg() + { + InitializeComponent(); + } + + private void tb_History_Select_Click(object sender, EventArgs e) + { + FolderBrowserDialog Dlg = new FolderBrowserDialog(); + Dlg.SelectedPath = Properties.Settings.Default.Analysis_History_Directory; + if (Dlg.ShowDialog() == DialogResult.OK) + { + Properties.Settings.Default.Analysis_History_Directory = Dlg.SelectedPath; + } + } + } +} diff --git a/AirScout/HistoryFromURLDlg.resx b/AirScout/HistoryFromURLDlg.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/AirScout/HistoryFromURLDlg.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/AirScout/HorizonDlg.Designer.cs b/AirScout/HorizonDlg.Designer.cs new file mode 100644 index 0000000..4409270 --- /dev/null +++ b/AirScout/HorizonDlg.Designer.cs @@ -0,0 +1,527 @@ +namespace AirScout +{ + partial class HorizonDlg + { + /// + /// Erforderliche Designervariable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Verwendete Ressourcen bereinigen. + /// + /// True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Vom Windows Form-Designer generierter Code + + /// + /// Erforderliche Methode für die Designerunterstützung. + /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.tb_Horizon_Lat = new System.Windows.Forms.TextBox(); + this.tb_Horizon_Lon = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.tb_Horizon_Elevation = new System.Windows.Forms.TextBox(); + this.label3 = new System.Windows.Forms.Label(); + this.tb_Horizon_K_Factor = new System.Windows.Forms.TextBox(); + this.label4 = new System.Windows.Forms.Label(); + this.tb_Horizon_QRG = new System.Windows.Forms.TextBox(); + this.label5 = new System.Windows.Forms.Label(); + this.tb_Horizon_F1_Clearance = new System.Windows.Forms.TextBox(); + this.label6 = new System.Windows.Forms.Label(); + this.tb_Horizon_ElevationModel = new System.Windows.Forms.TextBox(); + this.label7 = new System.Windows.Forms.Label(); + this.gb_Parameter = new System.Windows.Forms.GroupBox(); + this.label8 = new System.Windows.Forms.Label(); + this.tb_Horizon_Height = new System.Windows.Forms.TextBox(); + this.ss_Main = new System.Windows.Forms.StatusStrip(); + this.tsl_Main = new System.Windows.Forms.ToolStripStatusLabel(); + this.toolStripStatusLabel1 = new System.Windows.Forms.ToolStripStatusLabel(); + this.bw_Horizon_Calculate = new System.ComponentModel.BackgroundWorker(); + this.pa_Buttons = new System.Windows.Forms.Panel(); + this.btn_Horizon_Cancel = new System.Windows.Forms.Button(); + this.btn_Horizon_Export = new System.Windows.Forms.Button(); + this.btn_Horizon_Calculate = new System.Windows.Forms.Button(); + this.gb_Distance = new System.Windows.Forms.GroupBox(); + this.gb_Options = new System.Windows.Forms.GroupBox(); + this.panel2 = new System.Windows.Forms.Panel(); + this.rb_Horizon_Plot_Map = new System.Windows.Forms.RadioButton(); + this.rb_Horizon_Plot_Polar = new System.Windows.Forms.RadioButton(); + this.rb_Horizon_Plot_Cartesian = new System.Windows.Forms.RadioButton(); + this.gb_Map = new System.Windows.Forms.GroupBox(); + this.gm_Horizon = new GMap.NET.WindowsForms.GMapControl(); + this.gb_Elevation = new System.Windows.Forms.GroupBox(); + this.gb_Parameter.SuspendLayout(); + this.ss_Main.SuspendLayout(); + this.pa_Buttons.SuspendLayout(); + this.gb_Options.SuspendLayout(); + this.panel2.SuspendLayout(); + this.gb_Map.SuspendLayout(); + this.SuspendLayout(); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(6, 16); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(48, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Latitude:"; + // + // tb_Horizon_Lat + // + this.tb_Horizon_Lat.Enabled = false; + this.tb_Horizon_Lat.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Horizon_Lat.Location = new System.Drawing.Point(100, 16); + this.tb_Horizon_Lat.Name = "tb_Horizon_Lat"; + this.tb_Horizon_Lat.ReadOnly = true; + this.tb_Horizon_Lat.Size = new System.Drawing.Size(100, 20); + this.tb_Horizon_Lat.TabIndex = 1; + // + // tb_Horizon_Lon + // + this.tb_Horizon_Lon.Enabled = false; + this.tb_Horizon_Lon.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Horizon_Lon.Location = new System.Drawing.Point(100, 42); + this.tb_Horizon_Lon.Name = "tb_Horizon_Lon"; + this.tb_Horizon_Lon.ReadOnly = true; + this.tb_Horizon_Lon.Size = new System.Drawing.Size(100, 20); + this.tb_Horizon_Lon.TabIndex = 3; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label2.Location = new System.Drawing.Point(6, 42); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(59, 13); + this.label2.TabIndex = 2; + this.label2.Text = "Longiitude:"; + // + // tb_Horizon_Elevation + // + this.tb_Horizon_Elevation.Enabled = false; + this.tb_Horizon_Elevation.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Horizon_Elevation.Location = new System.Drawing.Point(100, 69); + this.tb_Horizon_Elevation.Name = "tb_Horizon_Elevation"; + this.tb_Horizon_Elevation.ReadOnly = true; + this.tb_Horizon_Elevation.Size = new System.Drawing.Size(100, 20); + this.tb_Horizon_Elevation.TabIndex = 5; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label3.Location = new System.Drawing.Point(6, 72); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(54, 13); + this.label3.TabIndex = 4; + this.label3.Text = "Elevation:"; + // + // tb_Horizon_K_Factor + // + this.tb_Horizon_K_Factor.Enabled = false; + this.tb_Horizon_K_Factor.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Horizon_K_Factor.Location = new System.Drawing.Point(379, 16); + this.tb_Horizon_K_Factor.Name = "tb_Horizon_K_Factor"; + this.tb_Horizon_K_Factor.ReadOnly = true; + this.tb_Horizon_K_Factor.Size = new System.Drawing.Size(100, 20); + this.tb_Horizon_K_Factor.TabIndex = 7; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label4.Location = new System.Drawing.Point(224, 16); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(50, 13); + this.label4.TabIndex = 6; + this.label4.Text = "K-Factor:"; + // + // tb_Horizon_QRG + // + this.tb_Horizon_QRG.Enabled = false; + this.tb_Horizon_QRG.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Horizon_QRG.Location = new System.Drawing.Point(379, 42); + this.tb_Horizon_QRG.Name = "tb_Horizon_QRG"; + this.tb_Horizon_QRG.ReadOnly = true; + this.tb_Horizon_QRG.Size = new System.Drawing.Size(100, 20); + this.tb_Horizon_QRG.TabIndex = 9; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label5.Location = new System.Drawing.Point(224, 42); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(34, 13); + this.label5.TabIndex = 8; + this.label5.Text = "QRG:"; + // + // tb_Horizon_F1_Clearance + // + this.tb_Horizon_F1_Clearance.Enabled = false; + this.tb_Horizon_F1_Clearance.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Horizon_F1_Clearance.Location = new System.Drawing.Point(379, 68); + this.tb_Horizon_F1_Clearance.Name = "tb_Horizon_F1_Clearance"; + this.tb_Horizon_F1_Clearance.ReadOnly = true; + this.tb_Horizon_F1_Clearance.Size = new System.Drawing.Size(100, 20); + this.tb_Horizon_F1_Clearance.TabIndex = 11; + // + // label6 + // + this.label6.AutoSize = true; + this.label6.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label6.Location = new System.Drawing.Point(224, 68); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(138, 13); + this.label6.TabIndex = 10; + this.label6.Text = "Fresnel Zone F1-Clearance:"; + // + // tb_Horizon_ElevationModel + // + this.tb_Horizon_ElevationModel.Enabled = false; + this.tb_Horizon_ElevationModel.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Horizon_ElevationModel.Location = new System.Drawing.Point(379, 94); + this.tb_Horizon_ElevationModel.Name = "tb_Horizon_ElevationModel"; + this.tb_Horizon_ElevationModel.ReadOnly = true; + this.tb_Horizon_ElevationModel.Size = new System.Drawing.Size(100, 20); + this.tb_Horizon_ElevationModel.TabIndex = 13; + // + // label7 + // + this.label7.AutoSize = true; + this.label7.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label7.Location = new System.Drawing.Point(224, 97); + this.label7.Name = "label7"; + this.label7.Size = new System.Drawing.Size(83, 13); + this.label7.TabIndex = 12; + this.label7.Text = "Elevation Model"; + // + // gb_Parameter + // + this.gb_Parameter.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.gb_Parameter.Controls.Add(this.label8); + this.gb_Parameter.Controls.Add(this.tb_Horizon_Height); + this.gb_Parameter.Controls.Add(this.tb_Horizon_Lat); + this.gb_Parameter.Controls.Add(this.tb_Horizon_ElevationModel); + this.gb_Parameter.Controls.Add(this.label1); + this.gb_Parameter.Controls.Add(this.label7); + this.gb_Parameter.Controls.Add(this.label2); + this.gb_Parameter.Controls.Add(this.tb_Horizon_F1_Clearance); + this.gb_Parameter.Controls.Add(this.tb_Horizon_Lon); + this.gb_Parameter.Controls.Add(this.label6); + this.gb_Parameter.Controls.Add(this.label3); + this.gb_Parameter.Controls.Add(this.tb_Horizon_QRG); + this.gb_Parameter.Controls.Add(this.tb_Horizon_Elevation); + this.gb_Parameter.Controls.Add(this.label5); + this.gb_Parameter.Controls.Add(this.label4); + this.gb_Parameter.Controls.Add(this.tb_Horizon_K_Factor); + this.gb_Parameter.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.gb_Parameter.Location = new System.Drawing.Point(12, 12); + this.gb_Parameter.Name = "gb_Parameter"; + this.gb_Parameter.Size = new System.Drawing.Size(499, 126); + this.gb_Parameter.TabIndex = 14; + this.gb_Parameter.TabStop = false; + this.gb_Parameter.Text = "Parameters"; + // + // label8 + // + this.label8.AutoSize = true; + this.label8.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label8.Location = new System.Drawing.Point(6, 97); + this.label8.Name = "label8"; + this.label8.Size = new System.Drawing.Size(84, 13); + this.label8.TabIndex = 14; + this.label8.Text = "Antenna Height:"; + // + // tb_Horizon_Height + // + this.tb_Horizon_Height.Enabled = false; + this.tb_Horizon_Height.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Horizon_Height.Location = new System.Drawing.Point(100, 94); + this.tb_Horizon_Height.Name = "tb_Horizon_Height"; + this.tb_Horizon_Height.ReadOnly = true; + this.tb_Horizon_Height.Size = new System.Drawing.Size(100, 20); + this.tb_Horizon_Height.TabIndex = 15; + // + // ss_Main + // + this.ss_Main.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.tsl_Main, + this.toolStripStatusLabel1}); + this.ss_Main.Location = new System.Drawing.Point(0, 540); + this.ss_Main.Name = "ss_Main"; + this.ss_Main.Size = new System.Drawing.Size(784, 22); + this.ss_Main.TabIndex = 21; + this.ss_Main.Text = "statusStrip1"; + // + // tsl_Main + // + this.tsl_Main.Name = "tsl_Main"; + this.tsl_Main.Size = new System.Drawing.Size(197, 17); + this.tsl_Main.Text = "Press Calculate to begin calculation."; + // + // toolStripStatusLabel1 + // + this.toolStripStatusLabel1.Name = "toolStripStatusLabel1"; + this.toolStripStatusLabel1.Size = new System.Drawing.Size(0, 17); + // + // bw_Horizon_Calculate + // + this.bw_Horizon_Calculate.WorkerReportsProgress = true; + this.bw_Horizon_Calculate.WorkerSupportsCancellation = true; + this.bw_Horizon_Calculate.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bw_Horizon_Calculate_DoWork); + this.bw_Horizon_Calculate.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.bw_Horizon_Calculate_ProgressChanged); + this.bw_Horizon_Calculate.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.bw_Horizon_Calculate_RunWorkerCompleted); + // + // pa_Buttons + // + this.pa_Buttons.Controls.Add(this.btn_Horizon_Cancel); + this.pa_Buttons.Controls.Add(this.btn_Horizon_Export); + this.pa_Buttons.Controls.Add(this.btn_Horizon_Calculate); + this.pa_Buttons.Dock = System.Windows.Forms.DockStyle.Bottom; + this.pa_Buttons.Location = new System.Drawing.Point(0, 490); + this.pa_Buttons.Name = "pa_Buttons"; + this.pa_Buttons.Size = new System.Drawing.Size(784, 50); + this.pa_Buttons.TabIndex = 25; + // + // btn_Horizon_Cancel + // + this.btn_Horizon_Cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.btn_Horizon_Cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btn_Horizon_Cancel.Location = new System.Drawing.Point(698, 15); + this.btn_Horizon_Cancel.Name = "btn_Horizon_Cancel"; + this.btn_Horizon_Cancel.Size = new System.Drawing.Size(75, 23); + this.btn_Horizon_Cancel.TabIndex = 27; + this.btn_Horizon_Cancel.Text = "Back"; + this.btn_Horizon_Cancel.UseVisualStyleBackColor = true; + this.btn_Horizon_Cancel.Click += new System.EventHandler(this.btn_Horizon_Cancel_Click); + // + // btn_Horizon_Export + // + this.btn_Horizon_Export.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.btn_Horizon_Export.Location = new System.Drawing.Point(588, 15); + this.btn_Horizon_Export.Name = "btn_Horizon_Export"; + this.btn_Horizon_Export.Size = new System.Drawing.Size(104, 23); + this.btn_Horizon_Export.TabIndex = 26; + this.btn_Horizon_Export.Text = "Export to CSV"; + this.btn_Horizon_Export.UseVisualStyleBackColor = true; + this.btn_Horizon_Export.Click += new System.EventHandler(this.btn_Horizon_Export_Click); + // + // btn_Horizon_Calculate + // + this.btn_Horizon_Calculate.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.btn_Horizon_Calculate.Location = new System.Drawing.Point(507, 15); + this.btn_Horizon_Calculate.Name = "btn_Horizon_Calculate"; + this.btn_Horizon_Calculate.Size = new System.Drawing.Size(75, 23); + this.btn_Horizon_Calculate.TabIndex = 25; + this.btn_Horizon_Calculate.Text = "Calculate"; + this.btn_Horizon_Calculate.UseVisualStyleBackColor = true; + this.btn_Horizon_Calculate.Click += new System.EventHandler(this.btn_Horizon_Calculate_Click); + // + // gb_Distance + // + this.gb_Distance.Anchor = System.Windows.Forms.AnchorStyles.None; + this.gb_Distance.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.gb_Distance.Location = new System.Drawing.Point(397, 144); + this.gb_Distance.Name = "gb_Distance"; + this.gb_Distance.Size = new System.Drawing.Size(375, 340); + this.gb_Distance.TabIndex = 27; + this.gb_Distance.TabStop = false; + this.gb_Distance.Text = "Horizon Distance versus True North [km]"; + // + // gb_Options + // + this.gb_Options.Controls.Add(this.panel2); + this.gb_Options.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.gb_Options.Location = new System.Drawing.Point(517, 12); + this.gb_Options.Name = "gb_Options"; + this.gb_Options.Size = new System.Drawing.Size(251, 125); + this.gb_Options.TabIndex = 28; + this.gb_Options.TabStop = false; + this.gb_Options.Text = "Plot Options"; + // + // panel2 + // + this.panel2.Controls.Add(this.rb_Horizon_Plot_Map); + this.panel2.Controls.Add(this.rb_Horizon_Plot_Polar); + this.panel2.Controls.Add(this.rb_Horizon_Plot_Cartesian); + this.panel2.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel2.Location = new System.Drawing.Point(3, 16); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(245, 106); + this.panel2.TabIndex = 2; + // + // rb_Horizon_Plot_Map + // + this.rb_Horizon_Plot_Map.AutoSize = true; + this.rb_Horizon_Plot_Map.Checked = global::AirScout.Properties.Settings.Default.Horizon_Plot_Map; + this.rb_Horizon_Plot_Map.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Horizon_Plot_Map", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.rb_Horizon_Plot_Map.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.rb_Horizon_Plot_Map.Location = new System.Drawing.Point(90, 68); + this.rb_Horizon_Plot_Map.Name = "rb_Horizon_Plot_Map"; + this.rb_Horizon_Plot_Map.Size = new System.Drawing.Size(46, 17); + this.rb_Horizon_Plot_Map.TabIndex = 4; + this.rb_Horizon_Plot_Map.Text = "Map"; + this.rb_Horizon_Plot_Map.UseVisualStyleBackColor = true; + this.rb_Horizon_Plot_Map.Click += new System.EventHandler(this.rb_Horizon_Plot_Map_Click); + // + // rb_Horizon_Plot_Polar + // + this.rb_Horizon_Plot_Polar.AutoSize = true; + this.rb_Horizon_Plot_Polar.Checked = global::AirScout.Properties.Settings.Default.Horizon_Plot_Polar; + this.rb_Horizon_Plot_Polar.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Horizon_Plot_Polar", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.rb_Horizon_Plot_Polar.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.rb_Horizon_Plot_Polar.Location = new System.Drawing.Point(90, 22); + this.rb_Horizon_Plot_Polar.Name = "rb_Horizon_Plot_Polar"; + this.rb_Horizon_Plot_Polar.Size = new System.Drawing.Size(49, 17); + this.rb_Horizon_Plot_Polar.TabIndex = 3; + this.rb_Horizon_Plot_Polar.Text = "Polar"; + this.rb_Horizon_Plot_Polar.UseVisualStyleBackColor = true; + this.rb_Horizon_Plot_Polar.Click += new System.EventHandler(this.rb_Horizon_Plot_Polar_Click); + // + // rb_Horizon_Plot_Cartesian + // + this.rb_Horizon_Plot_Cartesian.AutoSize = true; + this.rb_Horizon_Plot_Cartesian.Checked = global::AirScout.Properties.Settings.Default.Horizon_Plot_Cartesian; + this.rb_Horizon_Plot_Cartesian.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Horizon_Plot_Cartesian", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.rb_Horizon_Plot_Cartesian.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.rb_Horizon_Plot_Cartesian.Location = new System.Drawing.Point(90, 45); + this.rb_Horizon_Plot_Cartesian.Name = "rb_Horizon_Plot_Cartesian"; + this.rb_Horizon_Plot_Cartesian.Size = new System.Drawing.Size(69, 17); + this.rb_Horizon_Plot_Cartesian.TabIndex = 2; + this.rb_Horizon_Plot_Cartesian.Text = "Cartesian"; + this.rb_Horizon_Plot_Cartesian.UseVisualStyleBackColor = true; + this.rb_Horizon_Plot_Cartesian.Click += new System.EventHandler(this.rb_Horizon_Plot_Cartesian_Click); + // + // gb_Map + // + this.gb_Map.Controls.Add(this.gm_Horizon); + this.gb_Map.Location = new System.Drawing.Point(12, 144); + this.gb_Map.Name = "gb_Map"; + this.gb_Map.Size = new System.Drawing.Size(282, 244); + this.gb_Map.TabIndex = 30; + this.gb_Map.TabStop = false; + this.gb_Map.Text = "Map"; + this.gb_Map.Visible = false; + // + // gm_Horizon + // + this.gm_Horizon.Bearing = 0F; + this.gm_Horizon.CanDragMap = true; + this.gm_Horizon.Dock = System.Windows.Forms.DockStyle.Fill; + this.gm_Horizon.EmptyTileColor = System.Drawing.Color.Navy; + this.gm_Horizon.GrayScaleMode = false; + this.gm_Horizon.HelperLineOption = GMap.NET.WindowsForms.HelperLineOptions.DontShow; + this.gm_Horizon.LevelsKeepInMemmory = 5; + this.gm_Horizon.Location = new System.Drawing.Point(3, 16); + this.gm_Horizon.MarkersEnabled = true; + this.gm_Horizon.MaxZoom = 2; + this.gm_Horizon.MinZoom = 2; + this.gm_Horizon.MouseWheelZoomType = GMap.NET.MouseWheelZoomType.MousePositionAndCenter; + this.gm_Horizon.Name = "gm_Horizon"; + this.gm_Horizon.NegativeMode = false; + this.gm_Horizon.PolygonsEnabled = true; + this.gm_Horizon.RetryLoadTile = 0; + this.gm_Horizon.RoutesEnabled = true; + this.gm_Horizon.SelectedAreaFillColor = System.Drawing.Color.FromArgb(((int)(((byte)(33)))), ((int)(((byte)(65)))), ((int)(((byte)(105)))), ((int)(((byte)(225))))); + this.gm_Horizon.ShowTileGridLines = false; + this.gm_Horizon.Size = new System.Drawing.Size(276, 225); + this.gm_Horizon.TabIndex = 4; + this.gm_Horizon.Zoom = 0D; + this.gm_Horizon.MouseDown += new System.Windows.Forms.MouseEventHandler(this.gm_Horizon_MouseDown); + this.gm_Horizon.MouseUp += new System.Windows.Forms.MouseEventHandler(this.gm_Horizon_MouseUp); + // + // gb_Elevation + // + this.gb_Elevation.Anchor = System.Windows.Forms.AnchorStyles.None; + this.gb_Elevation.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.gb_Elevation.Location = new System.Drawing.Point(75, 138); + this.gb_Elevation.Name = "gb_Elevation"; + this.gb_Elevation.Size = new System.Drawing.Size(372, 340); + this.gb_Elevation.TabIndex = 31; + this.gb_Elevation.TabStop = false; + this.gb_Elevation.Text = "Minimum Elevation Angle versus True North [deg]"; + // + // HorizonDlg + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(784, 562); + this.Controls.Add(this.gb_Map); + this.Controls.Add(this.gb_Options); + this.Controls.Add(this.gb_Elevation); + this.Controls.Add(this.gb_Distance); + this.Controls.Add(this.pa_Buttons); + this.Controls.Add(this.ss_Main); + this.Controls.Add(this.gb_Parameter); + this.Name = "HorizonDlg"; + this.Text = "Radio Horizon"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.HorizonDlg_FormClosing); + this.SizeChanged += new System.EventHandler(this.HorizonDlg_SizeChanged); + this.gb_Parameter.ResumeLayout(false); + this.gb_Parameter.PerformLayout(); + this.ss_Main.ResumeLayout(false); + this.ss_Main.PerformLayout(); + this.pa_Buttons.ResumeLayout(false); + this.gb_Options.ResumeLayout(false); + this.panel2.ResumeLayout(false); + this.panel2.PerformLayout(); + this.gb_Map.ResumeLayout(false); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox tb_Horizon_Lat; + private System.Windows.Forms.TextBox tb_Horizon_Lon; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TextBox tb_Horizon_Elevation; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.TextBox tb_Horizon_K_Factor; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.TextBox tb_Horizon_QRG; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.TextBox tb_Horizon_F1_Clearance; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.TextBox tb_Horizon_ElevationModel; + private System.Windows.Forms.Label label7; + private System.Windows.Forms.GroupBox gb_Parameter; + private System.Windows.Forms.Label label8; + private System.Windows.Forms.TextBox tb_Horizon_Height; + private System.Windows.Forms.StatusStrip ss_Main; + private System.Windows.Forms.ToolStripStatusLabel tsl_Main; + private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabel1; + private System.ComponentModel.BackgroundWorker bw_Horizon_Calculate; + private System.Windows.Forms.Panel pa_Buttons; + private System.Windows.Forms.Button btn_Horizon_Cancel; + private System.Windows.Forms.Button btn_Horizon_Export; + private System.Windows.Forms.Button btn_Horizon_Calculate; + private System.Windows.Forms.GroupBox gb_Distance; + private System.Windows.Forms.GroupBox gb_Options; + private System.Windows.Forms.Panel panel2; + private System.Windows.Forms.RadioButton rb_Horizon_Plot_Polar; + private System.Windows.Forms.RadioButton rb_Horizon_Plot_Cartesian; + private System.Windows.Forms.RadioButton rb_Horizon_Plot_Map; + private System.Windows.Forms.GroupBox gb_Map; + private GMap.NET.WindowsForms.GMapControl gm_Horizon; + private System.Windows.Forms.GroupBox gb_Elevation; + } +} \ No newline at end of file diff --git a/AirScout/HorizonDlg.cs b/AirScout/HorizonDlg.cs new file mode 100644 index 0000000..13c816d --- /dev/null +++ b/AirScout/HorizonDlg.cs @@ -0,0 +1,562 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Globalization; +using System.Threading; +using System.Diagnostics; +using ScoutBase.Core; +using ScoutBase.Stations; +using ScoutBase.Elevation; +using ScoutBase.Propagation; +using GMap.NET; +using GMap.NET.MapProviders; +using GMap.NET.WindowsForms; +using GMap.NET.WindowsForms.Markers; +using OxyPlot; +using OxyPlot.WindowsForms; +using OxyPlot.Series; +using OxyPlot.Axes; + + +namespace AirScout +{ + public partial class HorizonDlg : Form + { + + new LocationDesignator Location; + QRVDesignator QRV; + LocalObstructionDesignator LocalObstruction; + short Elevation; + double AntennaHeight; + + PropagationHorizonDesignator Horizon; + double CalculationDistance = 500; + + GMapOverlay horizons = new GMapOverlay("Horizons"); + GMapRoute horizon = new GMapRoute("Horizon"); + + ToolTip TT; + IWin32Window OwnerWin; + + // charting + // horizon chart + PlotView pv_Elevation_Polar = new PlotView(); + PlotModel pm_Elevation_Polar = new PlotModel(); + AngleAxis Elevation_Polar_X = new AngleAxis(); + MagnitudeAxis Elevation_Polar_Y = new MagnitudeAxis(); + LineSeries Elevation_Polar_Series = new LineSeries(); + PlotView pv_Elevation_Cartesian = new PlotView(); + PlotModel pm_Elevation_Cartesian = new PlotModel(); + LinearAxis Elevation_Cartesian_X = new LinearAxis(); + LinearAxis Elevation_Cartesian_Y = new LinearAxis(); + LineSeries Elevation_Cartesian_Series = new LineSeries(); + + // horizon distance chart + PlotView pv_Distance_Polar = new PlotView(); + PlotModel pm_Distance_Polar = new PlotModel(); + AngleAxis Distance_Polar_X = new AngleAxis(); + MagnitudeAxis Distance_Polar_Y = new MagnitudeAxis(); + LineSeries Distance_Polar_Series = new LineSeries(); + PlotView pv_Distance_Cartesian = new PlotView(); + PlotModel pm_Distance_Cartesian = new PlotModel(); + LinearAxis Distance_Cartesian_X = new LinearAxis(); + LinearAxis Distance_Cartesian_Y = new LinearAxis(); + LineSeries Distance_Cartesian_Series = new LineSeries(); + + double Map_Left; + double Map_Right; + double Map_Top; + double Map_Bottom; + + public HorizonDlg(string call, double lat, double lon, LocalObstructionDesignator localobstruction) + { + InitializeComponent(); + Location = StationData.Database.LocationFindOrCreate(call, MaidenheadLocator.LocFromLatLon(lat, lon, false, 3)); + QRV = StationData.Database.QRVFindOrCreateDefault(call, MaidenheadLocator.LocFromLatLon(lat, lon, false, 3), Properties.Settings.Default.Band); + LocalObstruction = localobstruction; + Elevation = ElevationData.Database[Location.Lat, Location.Lon, Properties.Settings.Default.ElevationModel]; + AntennaHeight = (QRV.AntennaHeight != 0) ? QRV.AntennaHeight : StationData.Database.QRVGetDefaultAntennaHeight(Properties.Settings.Default.Band); + NumberFormatInfo provider = new NumberFormatInfo(); + provider.NumberDecimalSeparator = "."; + provider.NumberGroupSeparator = ","; + tb_Horizon_Lat.Text = Location.Lat.ToString("F8",provider); + tb_Horizon_Lon.Text = Location.Lon.ToString("F8",provider); + tb_Horizon_Elevation.Text = ElevationData.Database[Location.Lat, Location.Lon, Properties.Settings.Default.ElevationModel].ToString("F0",provider); + tb_Horizon_Height.Text = AntennaHeight.ToString("F0",provider); + tb_Horizon_K_Factor.Text = Properties.Settings.Default.Path_Band_Settings[Properties.Settings.Default.Band].K_Factor.ToString("F2",provider); + tb_Horizon_QRG.Text = Bands.GetStringValue(Properties.Settings.Default.Band); + tb_Horizon_F1_Clearance.Text = Properties.Settings.Default.Path_Band_Settings[Properties.Settings.Default.Band].F1_Clearance.ToString("F2",provider); + tb_Horizon_ElevationModel.Text = Properties.Settings.Default.ElevationModel.ToString(); + // setting User Agent to fix Open Street Map issue 2016-09-20 + GMap.NET.MapProviders.GMapProvider.UserAgent = "AirScout"; + // set initial settings for main map + gm_Horizon.MapProvider = GMapProviders.Find(Properties.Settings.Default.Map_Provider); + gm_Horizon.IgnoreMarkerOnMouseWheel = true; + gm_Horizon.MinZoom = 0; + gm_Horizon.MaxZoom = 20; + gm_Horizon.Zoom = 8; + gm_Horizon.DragButton = System.Windows.Forms.MouseButtons.Left; + gm_Horizon.CanDragMap = true; + gm_Horizon.ScalePen = new Pen(Color.Black, 3); + gm_Horizon.MapScaleInfoEnabled = true; + gm_Horizon.Overlays.Add(horizons); + GMarkerGoogle gm = new GMarkerGoogle(new PointLatLng(Location.Lat, Location.Lon), GMarkerGoogleType.red_dot); + gm.ToolTipText = Location.Call; + horizons.Markers.Add(gm); + horizon.Stroke = new Pen(Color.Red, 3); + horizons.Routes.Add(horizon); + this.Text = "Radio Horizon of " + Location.Call; + // initialize charts + InitializeCharts(); + // activate Polar if nothing checked + if (!Properties.Settings.Default.Horizon_Plot_Polar && !Properties.Settings.Default.Horizon_Plot_Cartesian && !Properties.Settings.Default.Horizon_Plot_Map) + Properties.Settings.Default.Horizon_Plot_Polar = true; + // show according child windows + UpdateCharts(); + // create ToolTip on this window + TT = new ToolTip(); + OwnerWin = gm_Horizon; + HorizonDlg_SizeChanged(this, null); + // set map bounds + Map_Left = Location.Lon; + Map_Right = Location.Lon; + Map_Top = Location.Lat; + Map_Bottom = Location.Lat; + } + + private ELEVATIONMODEL GetElevationModel() + { + if (Properties.Settings.Default.Elevation_SRTM1_Enabled) + return ELEVATIONMODEL.SRTM1; + if (Properties.Settings.Default.Elevation_SRTM3_Enabled) + return ELEVATIONMODEL.SRTM3; + if (Properties.Settings.Default.Elevation_GLOBE_Enabled) + return ELEVATIONMODEL.GLOBE; + return ELEVATIONMODEL.NONE; + } + + + private void InitializeCharts() + { + // elevation polar chart + pm_Elevation_Polar.PlotType = PlotType.Polar; + pm_Elevation_Polar.Title = String.Empty; + pm_Elevation_Polar.DefaultFontSize = 6F; + pv_Elevation_Polar.BackColor = Color.White; + pv_Elevation_Polar.Dock = DockStyle.Fill; + pv_Elevation_Polar.Model = pm_Elevation_Polar; + // add axes + pm_Elevation_Polar.Axes.Clear(); + // add x-axis + Elevation_Polar_X.MajorGridlineStyle = LineStyle.Solid; + Elevation_Polar_X.MinorGridlineStyle = LineStyle.Dot; + Elevation_Polar_X.Angle = 90; + Elevation_Polar_X.MajorStep = 30; + Elevation_Polar_X.MinorStep = 10; + Elevation_Polar_X.Minimum = 0; + Elevation_Polar_X.Maximum = 360; + Elevation_Polar_X.StartPosition = 1; + Elevation_Polar_X.EndPosition = 0; + Elevation_Polar_X.StartAngle = 360 + 90; + Elevation_Polar_X.EndAngle = 90; + pm_Elevation_Polar.Axes.Add(Elevation_Polar_X); + // add y-axis + Elevation_Polar_Y.MajorGridlineStyle = LineStyle.Solid; + Elevation_Polar_Y.MinorGridlineStyle = LineStyle.Dot; + Elevation_Polar_Y.Angle = 0; + Elevation_Polar_Y.Position = AxisPosition.Right; + pm_Elevation_Polar.Axes.Add(Elevation_Polar_Y); + // add series + pm_Elevation_Polar.Series.Clear(); + Elevation_Polar_Series.StrokeThickness = 2; + Elevation_Polar_Series.LineStyle = LineStyle.Solid; + Elevation_Polar_Series.Color = OxyColors.Blue; + pm_Elevation_Polar.Series.Add(Elevation_Polar_Series); + gb_Elevation.Controls.Add(pv_Elevation_Polar); + + // elevation cartesian chart + pv_Elevation_Cartesian.BackColor = Color.White; + pv_Elevation_Cartesian.Dock = DockStyle.Fill; + pv_Elevation_Cartesian.Model = pm_Elevation_Cartesian; + pm_Elevation_Cartesian.Title = String.Empty; + pm_Elevation_Cartesian.DefaultFontSize = 6F; + // add axes + pm_Elevation_Cartesian.Axes.Clear(); + // add x-axis + Elevation_Cartesian_X.MajorGridlineStyle = LineStyle.Solid; + Elevation_Cartesian_X.MinorGridlineStyle = LineStyle.Dot; + Elevation_Cartesian_X.MajorStep = 30; + Elevation_Cartesian_X.MinorStep = 10; + Elevation_Cartesian_X.Minimum = 0; + Elevation_Cartesian_X.Maximum = 360; + Elevation_Cartesian_X.Position = AxisPosition.Bottom; + pm_Elevation_Cartesian.Axes.Add(Elevation_Cartesian_X); + // add y-axis + Elevation_Cartesian_Y.MajorGridlineStyle = LineStyle.Solid; + Elevation_Cartesian_Y.MinorGridlineStyle = LineStyle.Dot; + Elevation_Cartesian_Y.Position = AxisPosition.Left; + pm_Elevation_Cartesian.Axes.Add(Elevation_Cartesian_Y); + // add series + pm_Elevation_Cartesian.Series.Clear(); + Elevation_Cartesian_Series.StrokeThickness = 2; + Elevation_Cartesian_Series.LineStyle = LineStyle.Solid; + Elevation_Cartesian_Series.Color = OxyColors.Blue; + pm_Elevation_Cartesian.Series.Add(Elevation_Cartesian_Series); + gb_Elevation.Controls.Add(pv_Elevation_Cartesian); + + // distance polar chart + pm_Distance_Polar.PlotType = PlotType.Polar; + pm_Distance_Polar.Title = String.Empty; + pm_Distance_Polar.DefaultFontSize = 6F; + pv_Distance_Polar.BackColor = Color.White; + pv_Distance_Polar.Dock = DockStyle.Fill; + pv_Distance_Polar.Model = pm_Distance_Polar; + // add axes + pm_Distance_Polar.Axes.Clear(); + // add x-axis + Distance_Polar_X.MajorGridlineStyle = LineStyle.Solid; + Distance_Polar_X.MinorGridlineStyle = LineStyle.Dot; + Distance_Polar_X.Angle = 90; + Distance_Polar_X.MajorStep = 30; + Distance_Polar_X.MinorStep = 10; + Distance_Polar_X.Minimum = 0; + Distance_Polar_X.Maximum = 360; + Distance_Polar_X.StartPosition = 1; + Distance_Polar_X.EndPosition = 0; + Distance_Polar_X.StartAngle = 360 + 90; + Distance_Polar_X.EndAngle = 90; + pm_Distance_Polar.Axes.Add(Distance_Polar_X); + // add y-axis + Distance_Polar_Y.MajorGridlineStyle = LineStyle.Solid; + Distance_Polar_Y.MinorGridlineStyle = LineStyle.Dot; + Distance_Polar_Y.Angle = 0; + Distance_Polar_Y.Position = AxisPosition.Right; + pm_Distance_Polar.Axes.Add(Distance_Polar_Y); + // add series + pm_Distance_Polar.Series.Clear(); + Distance_Polar_Series.StrokeThickness = 2; + Distance_Polar_Series.LineStyle = LineStyle.Solid; + Distance_Polar_Series.Color = OxyColors.Red; + pm_Distance_Polar.Series.Add(Distance_Polar_Series); + gb_Distance.Controls.Add(pv_Distance_Polar); + + // distance cartesian chart + pv_Distance_Cartesian.BackColor = Color.White; + pv_Distance_Cartesian.Dock = DockStyle.Fill; + pv_Distance_Cartesian.Model = pm_Distance_Cartesian; + pm_Distance_Cartesian.Title = String.Empty; + pm_Distance_Cartesian.DefaultFontSize = 6F; + // add axes + pm_Distance_Cartesian.Axes.Clear(); + // add x-axis + Distance_Cartesian_X.MajorGridlineStyle = LineStyle.Solid; + Distance_Cartesian_X.MinorGridlineStyle = LineStyle.Dot; + Distance_Cartesian_X.MajorStep = 30; + Distance_Cartesian_X.MinorStep = 10; + Distance_Cartesian_X.Minimum = 0; + Distance_Cartesian_X.Maximum = 360; + Distance_Cartesian_X.Position = AxisPosition.Bottom; + pm_Distance_Cartesian.Axes.Add(Distance_Cartesian_X); + // add y-axis + Distance_Cartesian_Y.MajorGridlineStyle = LineStyle.Solid; + Distance_Cartesian_Y.MinorGridlineStyle = LineStyle.Dot; + Distance_Cartesian_Y.Position = AxisPosition.Left; + pm_Distance_Cartesian.Axes.Add(Distance_Cartesian_Y); + // add series + pm_Distance_Cartesian.Series.Clear(); + Distance_Cartesian_Series.StrokeThickness = 2; + Distance_Cartesian_Series.LineStyle = LineStyle.Solid; + Distance_Cartesian_Series.Color = OxyColors.Red; + pm_Distance_Cartesian.Series.Add(Distance_Cartesian_Series); + gb_Distance.Controls.Add(pv_Distance_Cartesian); + + } + + private void btn_Horizon_Calculate_Click(object sender, EventArgs e) + { + // disable buttons + btn_Horizon_Calculate.Enabled = false; + btn_Horizon_Export.Enabled = false; + // let the background worker do the rest + bw_Horizon_Calculate.RunWorkerAsync(); + } + + private void btn_Horizon_Export_Click(object sender, EventArgs e) + { + try + { + SaveFileDialog Dlg = new SaveFileDialog(); + Dlg.CheckPathExists = true; + Dlg.FileName = Location.Call.ToUpper() + "_Horizon"; + if (Properties.Settings.Default.Elevation_SRTM1_Enabled) + Dlg.FileName = Dlg.FileName + "_SRTM1"; + if (Properties.Settings.Default.Elevation_SRTM3_Enabled) + Dlg.FileName = Dlg.FileName + "_SRTM3"; + if (Properties.Settings.Default.Elevation_GLOBE_Enabled) + Dlg.FileName = Dlg.FileName + "_GLOBE"; + else + Dlg.FileName = Dlg.FileName + "_NONE"; + Dlg.FileName = Dlg.FileName + "_" + ElevationData.Database.GetDefaultStepWidth(GetElevationModel()).ToString("F0") + "m"; + Dlg.FileName = Dlg.FileName + ".csv"; + Dlg.DefaultExt = ".csv"; + if (Dlg.ShowDialog() == DialogResult.OK) + { + using (StreamWriter sw = new StreamWriter(Dlg.FileName)) + { + sw.WriteLine("Bearing[deg];Distance[km];Eps_Min[deg];Elevation[m]"); + for (int i = 0; i < 360; i++) + { + sw.WriteLine(i.ToString() + ";" + + (Horizon.Horizon[i].Epsmin / Math.PI * 180).ToString("F8") + ";" + + Horizon.Horizon[i].Dist.ToString("F8") + ";" + + Horizon.Horizon[i].Elv.ToString("F8")); + } + } + } + } + catch + { + // do nothing, if export is going wrong + } + } + + private void btn_Horizon_Cancel_Click(object sender, EventArgs e) + { + this.Close(); + } + + private void HorizonDlg_SizeChanged(object sender, EventArgs e) + { + int width = this.Width - 50; + gb_Elevation.Top = gb_Parameter.Height + 16; + gb_Elevation.Left = 10; + gb_Elevation.Width = width / 2; + gb_Elevation.Height = this.Height - gb_Parameter.Height - pa_Buttons.Height - 70; + gb_Distance.Top = gb_Parameter.Height + 16; + gb_Distance.Left = gb_Elevation.Width + 30; + gb_Distance.Width = width / 2; + gb_Distance.Height = this.Height - gb_Parameter.Height - pa_Buttons.Height - 70; + gb_Map.Left = 10; + gb_Map.Top = gb_Parameter.Height + 16; + gb_Map.Width = gb_Elevation.Width + gb_Distance.Width + 15; + gb_Map.Height = this.Height - gb_Parameter.Height - pa_Buttons.Height - 70; + } + + private void ClearAllDiagrams() + { + Elevation_Cartesian_Series.Points.Clear(); + Elevation_Polar_Series.Points.Clear(); + Distance_Cartesian_Series.Points.Clear(); + Distance_Cartesian_Series.Points.Clear(); + horizon.Clear(); + } + + private void DrawHorizonPoint(int azimuth, HorizonPoint hp) + { + Elevation_Polar_Series.Points.Add(new DataPoint(hp.Epsmin / Math.PI * 180.0, azimuth)); + pm_Elevation_Polar.InvalidatePlot(true); + Elevation_Cartesian_Series.Points.Add(new DataPoint(azimuth, hp.Epsmin / Math.PI * 180.0)); + pm_Elevation_Cartesian.InvalidatePlot(true); + Distance_Polar_Series.Points.Add(new DataPoint(hp.Dist, azimuth)); + pm_Distance_Polar.InvalidatePlot(true); + Distance_Cartesian_Series.Points.Add(new DataPoint(azimuth, hp.Dist)); + pm_Distance_Cartesian.InvalidatePlot(true); + LatLon.GPoint gp = LatLon.DestinationPoint(Location.Lat, Location.Lon, azimuth, hp.Dist); + PointLatLng p = new PointLatLng(gp.Lat, gp.Lon); + horizon.Points.Add(p); + if (p.Lng < Map_Left) + Map_Left = p.Lng; + if (p.Lng > Map_Right) + Map_Right = p.Lng; + if (p.Lat < Map_Bottom) + Map_Bottom = p.Lat; + if (p.Lat > Map_Top) + Map_Top = p.Lat; + // toggle visible to redraw + horizon.IsVisible = false; + horizon.IsVisible = true; + gm_Horizon.SetZoomToFitRect(RectLatLng.FromLTRB(Map_Left, Map_Top, Map_Right, Map_Bottom)); + } + + private void bw_Horizon_Calculate_DoWork(object sender, DoWorkEventArgs e) + { + // name the thread for debugging + if (String.IsNullOrEmpty(Thread.CurrentThread.Name)) + Thread.CurrentThread.Name = Name + "_" + this.GetType().Name; + try + { + Stopwatch st = new Stopwatch(); + st.Start(); + // clear all diagrams + ClearAllDiagrams(); + PropagationHorizonDesignator hd = PropagationData.Database.PropagationHorizonFindOrCreate( + bw_Horizon_Calculate, + Location.Lat, + Location.Lon, + Elevation + AntennaHeight, + CalculationDistance, + Bands.ToGHz(Properties.Settings.Default.Band), + LatLon.Earth.Radius * Properties.Settings.Default.Path_Band_Settings[Properties.Settings.Default.Band].K_Factor, + Properties.Settings.Default.Path_Band_Settings[Properties.Settings.Default.Band].F1_Clearance, + ElevationData.Database.GetDefaultStepWidth(Properties.Settings.Default.ElevationModel), + Properties.Settings.Default.ElevationModel, LocalObstruction); + st.Stop(); + bw_Horizon_Calculate.ReportProgress(-1, "Calculation of radio horizon of " + Location.Call + " finished, " + st.ElapsedMilliseconds + "ms."); + // report complete horizon to main thread + bw_Horizon_Calculate.ReportProgress(360, hd); + } + catch (Exception ex) + { + bw_Horizon_Calculate.ReportProgress(-1, ex.ToString()); + } + + } + + private void bw_Horizon_Calculate_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + if (e.ProgressPercentage < 0) + { + tsl_Main.Text = (string)e.UserState; + } + else if ((e.ProgressPercentage >= 0) && (e.ProgressPercentage <= 359)) + { + // draw single horizon point when new calculation is running + HorizonPoint hp = (HorizonPoint)e.UserState; + DrawHorizonPoint(e.ProgressPercentage, hp); + } + else if (e.ProgressPercentage == 360) + { + // store complete horizon + Horizon = (PropagationHorizonDesignator)e.UserState; + } + } + + private void bw_Horizon_Calculate_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + // clear all drawing and draw again when finished + if (Horizon != null) + { + // clear all diagrams and maps + ClearAllDiagrams(); + // draw all points + for (int j = 0; j < 360; j++) + { + DrawHorizonPoint(j, Horizon.Horizon[j]); + } + // draw first point again to close the circle + DrawHorizonPoint(0, Horizon.Horizon[0]); + } + // enable radio buttons + btn_Horizon_Calculate.Enabled = true; + btn_Horizon_Export.Enabled = true; + } + + private void HorizonDlg_FormClosing(object sender, FormClosingEventArgs e) + { + bw_Horizon_Calculate.CancelAsync(); + } + + private void rb_Horizon_Plot_Cartesian_Click(object sender, EventArgs e) + { + rb_Horizon_Plot_Cartesian.Checked = true; + rb_Horizon_Plot_Polar.Checked = false; + rb_Horizon_Plot_Map.Checked = false; + UpdateCharts(); + } + + private void rb_Horizon_Plot_Polar_Click(object sender, EventArgs e) + { + rb_Horizon_Plot_Polar.Checked = true; + rb_Horizon_Plot_Cartesian.Checked = false; + rb_Horizon_Plot_Map.Checked = false; + UpdateCharts(); + } + + private void rb_Horizon_Plot_Map_Click(object sender, EventArgs e) + { + rb_Horizon_Plot_Map.Checked = true; + rb_Horizon_Plot_Cartesian.Checked = false; + rb_Horizon_Plot_Polar.Checked = false; + UpdateCharts(); + } + + private void UpdateCharts() + { + try + { + gb_Elevation.BringToFront(); + if (rb_Horizon_Plot_Polar.Checked) + { + gb_Distance.Visible = true; + gb_Elevation.Visible = true; + gb_Map.Visible = false; + pv_Elevation_Polar.Visible = true; + pv_Elevation_Cartesian.Visible = false; + pv_Distance_Polar.Visible = true; + pv_Distance_Cartesian.Visible = false; + } + else if (rb_Horizon_Plot_Cartesian.Checked) + { + gb_Distance.Visible = true; + gb_Elevation.Visible = true; + gb_Map.Visible = false; + pv_Elevation_Polar.Visible = false; + pv_Elevation_Cartesian.Visible = true; + pv_Distance_Polar.Visible = false; + pv_Distance_Cartesian.Visible = true; + } + else if (rb_Horizon_Plot_Map.Checked) + { + gb_Distance.Visible = false; + gb_Elevation.Visible = false; + gb_Map.Visible = true; + gm_Horizon.Position = new PointLatLng(Location.Lat, Location.Lon); + } + } + catch ( Exception ex) + { + MapDlg.Log.WriteMessage(ex.ToString()); + } + } + + private void gm_Horizon_MouseDown(object sender, MouseEventArgs e) + { + // get geographic coordinates of mouse pointer + PointLatLng p = gm_Horizon.FromLocalToLatLng(e.X, e.Y); + Point loc = e.Location; + loc.X += gm_Horizon.Cursor.Size.Width; + loc.Y += gm_Horizon.Cursor.Size.Height; + int elv = ElevationData.Database.ElvMissingFlag; + // try to get elevation data from distinct elevation model + // start with detailed one + if (Properties.Settings.Default.Elevation_SRTM1_Enabled && (elv == ElevationData.Database.ElvMissingFlag)) + elv = ElevationData.Database[p.Lat, p.Lng, ELEVATIONMODEL.SRTM1, false]; + if (Properties.Settings.Default.Elevation_SRTM3_Enabled && (elv == ElevationData.Database.ElvMissingFlag)) + elv = ElevationData.Database[p.Lat, p.Lng, ELEVATIONMODEL.SRTM3, false]; + if (Properties.Settings.Default.Elevation_GLOBE_Enabled && (elv == ElevationData.Database.ElvMissingFlag)) + elv = ElevationData.Database[p.Lat, p.Lng, ELEVATIONMODEL.GLOBE, false]; + // set it to zero if still invalid + if (elv == ElevationData.Database.ElvMissingFlag) + elv = 0; + double bearing = LatLon.Bearing(Location.Lat, Location.Lon, p.Lat, p.Lng); + double dist = LatLon.Distance(Location.Lat, Location.Lon, p.Lat, p.Lng); + TT.Show("Lat: " + p.Lat.ToString("F8", CultureInfo.InvariantCulture) + + "°\nLon: " + p.Lng.ToString("F8", CultureInfo.InvariantCulture) + + "°\nElv: " + elv.ToString() + + "m\n\nQRB: " + dist.ToString("F2", CultureInfo.InvariantCulture) + + "km\nQTF: " + bearing.ToString("F0",CultureInfo.InvariantCulture) + + "°", OwnerWin, loc); + } + + private void gm_Horizon_MouseUp(object sender, MouseEventArgs e) + { + TT.Hide(OwnerWin); + } + } +} diff --git a/AirScout/HorizonDlg.resx b/AirScout/HorizonDlg.resx new file mode 100644 index 0000000..225e4de --- /dev/null +++ b/AirScout/HorizonDlg.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 133, 17 + + \ No newline at end of file diff --git a/AirScout/Icons/Earth.jpg b/AirScout/Icons/Earth.jpg new file mode 100644 index 0000000..97d076a Binary files /dev/null and b/AirScout/Icons/Earth.jpg differ diff --git a/AirScout/Icons/ISS.png b/AirScout/Icons/ISS.png new file mode 100644 index 0000000..94dcd33 Binary files /dev/null and b/AirScout/Icons/ISS.png differ diff --git a/AirScout/Icons/Intro.jpg b/AirScout/Icons/Intro.jpg new file mode 100644 index 0000000..cb5f31e Binary files /dev/null and b/AirScout/Icons/Intro.jpg differ diff --git a/AirScout/Icons/Intro.mix b/AirScout/Icons/Intro.mix new file mode 100644 index 0000000..24f4739 Binary files /dev/null and b/AirScout/Icons/Intro.mix differ diff --git a/AirScout/Icons/Intro.png b/AirScout/Icons/Intro.png new file mode 100644 index 0000000..1dffee9 Binary files /dev/null and b/AirScout/Icons/Intro.png differ diff --git a/AirScout/Icons/LED.png b/AirScout/Icons/LED.png new file mode 100644 index 0000000..a2bb008 Binary files /dev/null and b/AirScout/Icons/LED.png differ diff --git a/AirScout/Icons/Main.ico b/AirScout/Icons/Main.ico new file mode 100644 index 0000000..64008f7 Binary files /dev/null and b/AirScout/Icons/Main.ico differ diff --git a/AirScout/Icons/Main.png b/AirScout/Icons/Main.png new file mode 100644 index 0000000..b142314 Binary files /dev/null and b/AirScout/Icons/Main.png differ diff --git a/AirScout/Icons/Map.png b/AirScout/Icons/Map.png new file mode 100644 index 0000000..5c73965 Binary files /dev/null and b/AirScout/Icons/Map.png differ diff --git a/AirScout/Icons/Map2.png b/AirScout/Icons/Map2.png new file mode 100644 index 0000000..8bd4304 Binary files /dev/null and b/AirScout/Icons/Map2.png differ diff --git a/AirScout/Icons/PauseHS.png b/AirScout/Icons/PauseHS.png new file mode 100644 index 0000000..7de96fd Binary files /dev/null and b/AirScout/Icons/PauseHS.png differ diff --git a/AirScout/Icons/PlayHS.png b/AirScout/Icons/PlayHS.png new file mode 100644 index 0000000..1c95447 Binary files /dev/null and b/AirScout/Icons/PlayHS.png differ diff --git a/AirScout/Icons/PlayHS_ori.png b/AirScout/Icons/PlayHS_ori.png new file mode 100644 index 0000000..96b050f Binary files /dev/null and b/AirScout/Icons/PlayHS_ori.png differ diff --git a/AirScout/Icons/RecordHS.png b/AirScout/Icons/RecordHS.png new file mode 100644 index 0000000..d3e7ba4 Binary files /dev/null and b/AirScout/Icons/RecordHS.png differ diff --git a/AirScout/Icons/back.png b/AirScout/Icons/back.png new file mode 100644 index 0000000..1224172 Binary files /dev/null and b/AirScout/Icons/back.png differ diff --git a/AirScout/Icons/donate.html b/AirScout/Icons/donate.html new file mode 100644 index 0000000..13d4894 --- /dev/null +++ b/AirScout/Icons/donate.html @@ -0,0 +1,9 @@ +
+ + + + +
+ diff --git a/AirScout/Icons/donate.png b/AirScout/Icons/donate.png new file mode 100644 index 0000000..5b1fe9e Binary files /dev/null and b/AirScout/Icons/donate.png differ diff --git a/AirScout/Icons/fastforward.png b/AirScout/Icons/fastforward.png new file mode 100644 index 0000000..d000452 Binary files /dev/null and b/AirScout/Icons/fastforward.png differ diff --git a/AirScout/Icons/forward.png b/AirScout/Icons/forward.png new file mode 100644 index 0000000..08e1fe6 Binary files /dev/null and b/AirScout/Icons/forward.png differ diff --git a/AirScout/Icons/pause.png b/AirScout/Icons/pause.png new file mode 100644 index 0000000..bb2dd12 Binary files /dev/null and b/AirScout/Icons/pause.png differ diff --git a/AirScout/Icons/plane.png b/AirScout/Icons/plane.png new file mode 100644 index 0000000..06b49fe Binary files /dev/null and b/AirScout/Icons/plane.png differ diff --git a/AirScout/Icons/plane001.png b/AirScout/Icons/plane001.png new file mode 100644 index 0000000..783e098 Binary files /dev/null and b/AirScout/Icons/plane001.png differ diff --git a/AirScout/Icons/plane002.png b/AirScout/Icons/plane002.png new file mode 100644 index 0000000..bb01677 Binary files /dev/null and b/AirScout/Icons/plane002.png differ diff --git a/AirScout/Icons/plane003.png b/AirScout/Icons/plane003.png new file mode 100644 index 0000000..a933083 Binary files /dev/null and b/AirScout/Icons/plane003.png differ diff --git a/AirScout/Icons/plane004.png b/AirScout/Icons/plane004.png new file mode 100644 index 0000000..8564f2f Binary files /dev/null and b/AirScout/Icons/plane004.png differ diff --git a/AirScout/Icons/plane005.png b/AirScout/Icons/plane005.png new file mode 100644 index 0000000..2e77fad Binary files /dev/null and b/AirScout/Icons/plane005.png differ diff --git a/AirScout/Icons/plane006.png b/AirScout/Icons/plane006.png new file mode 100644 index 0000000..7c56422 Binary files /dev/null and b/AirScout/Icons/plane006.png differ diff --git a/AirScout/Icons/plane_grren.png b/AirScout/Icons/plane_grren.png new file mode 100644 index 0000000..7cef154 Binary files /dev/null and b/AirScout/Icons/plane_grren.png differ diff --git a/AirScout/Icons/question mark.png b/AirScout/Icons/question mark.png new file mode 100644 index 0000000..8d4e1f0 Binary files /dev/null and b/AirScout/Icons/question mark.png differ diff --git a/AirScout/Icons/rewind.png b/AirScout/Icons/rewind.png new file mode 100644 index 0000000..d00d217 Binary files /dev/null and b/AirScout/Icons/rewind.png differ diff --git a/AirScout/LICENSE b/AirScout/LICENSE new file mode 100644 index 0000000..167ff25 --- /dev/null +++ b/AirScout/LICENSE @@ -0,0 +1,2524 @@ +******************************************************************************** +* AirScout AirCraft Scatter Prediction * +* * +* General Licence Information: 2018-08-03 * +******************************************************************************** + +Copyright (c) 2018 by DL2ALF +http://www.dl2alf.de + +******************************************************************************** +General Information +******************************************************************************** + +Writing a complex software is simply not possible to do "from the scratch". +Therefor I as the author of AirScout do rely on a lot of other open source +software provided by other people allover the world. Without the help of +these generous contributors I would never finish the work. + +I am not a skilled software engineer or a lawyer. For me, dealing with all the +different kinds of open source licences is a mess. It took me a long time to +figure out what is a combined/derived work and which licenses are compatible +to each other and I am still learning. I try my best to list below all the +licenses of any library used for the project. If you are the license holder +and feel something is wrong please tell me. It is not my intention to violate +any copyright or license. + +This is a compilation of different licences used in this project: + +- Gnu General Public License, Version 3.0 +- The MIT License (MIT) +- The Code Project Open License (CPOL) 1.02 +- Microsoft Public License (Ms-PL) +- other personal/non-commercial or otherwise specific licenses + +This license information file consists of >1500 lines. This is a good +reflection of my situation as a programmer of free software. + + +******************************************************************************** +Main Licence Information +(AirScout.exe) +******************************************************************************** + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + +******************************************************************************** +Licence Information for Great Maps .NET +(GMAP.NET.*.dll) +******************************************************************************** + +http://greatmaps.codeplex.com/ + +The MIT License (MIT) + +Copyright (c) 2008-2011 Universe, + +WARNING: This software can access some map providers and may viotile their +Terms of Service, you use it at your own risk, nothing is forcing you to +accept this ;} Source itself is legal! + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +******************************************************************************** +Licence Information for Cubic Spline +(Cubicspline.dll) +******************************************************************************** + +http://www.codeproject.com/Articles/560163/Csharp-Cubic-Spline-Interpolation + +Author: Ryan Seghers +Copyright (C) 2013 Ryan Seghers + +The Code Project Open License (CPOL) 1.02 + +Preamble + +This License governs Your use of the Work. This License is intended to allow +developers to use the Source Code and Executable Files provided as part of +the Work in any application in any form. + +The main points subject to the terms of the License are: + + Source Code and Executable Files can be used in commercial applications; + Source Code and Executable Files can be redistributed; and + Source Code can be modified to create derivative works. + No claim of suitability, guarantee, or any warranty whatsoever is + provided. + The software is provided "as-is". The Article(s) accompanying the Work + may not be distributed or republished without the Author's consent + +This License is entered between You, the individual or other entity reading +or otherwise making use of the Work licensed pursuant to this License and the +individual or other entity which offers the Work under the terms of this +License ("Author"). + +License + +THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CODE PROJECT +OPEN LICENSE ("LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER +APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE +OR COPYRIGHT LAW IS PROHIBITED. BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED +HEREIN, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. +THE AUTHOR GRANTS YOU THE RIGHTS CONTAINED HEREIN IN CONSIDERATION OF YOUR +ACCEPTANCE OF SUCH TERMS AND CONDITIONS. IF YOU DO NOT AGREE TO ACCEPT AND BE +BOUND BY THE TERMS OF THIS LICENSE, YOU CANNOT MAKE ANY USE OF THE WORK. + + 1. Definitions + a) "Articles" means, collectively, all articles written by Author + which describes how the Source Code and Executable Files for the Work + may be used by a user. + b) "Author" means the individual or entity that offers the Work under + the terms of this License. + c) "Derivative Work" means a work based upon the Work or upon the Work + and other pre-existing works. + d) "Executable Files" refer to the executables, binary files, + configuration and any required data files included in the Work. + e) "Publisher" means the provider of the website, magazine, CD-ROM, + DVD or other medium from or by which the Work is obtained by You. + f) "Source Code" refers to the collection of source code and + configuration files used to create the Executable Files. + g) "Standard Version" refers to such a Work if it has not been + modified, or has been modified in accordance with the consent of the + Author, such consent being in the full discretion of the Author. + h) "Work" refers to the collection of files distributed by the + Publisher, including the Source Code, Executable Files, binaries, data + files, documentation, whitepapers and the Articles. + i) "You" is you, an individual or entity wishing to use the Work and + exercise your rights under this License. + + 2. Fair Use/Fair Use Rights. + Nothing in this License is intended to reduce, limit, or restrict any + rights arising from fair use, fair dealing, first sale or other + limitations on the exclusive rights of the copyright owner under copyright + law or other applicable laws. + + 3. License Grant. + Subject to the terms and conditions of this License, the Author hereby + grants You a worldwide, royalty-free, non-exclusive, perpetual (for the + duration of the applicable copyright) license to exercise the rights in + the Work as stated below: + + a) You may use the standard version of the Source Code or Executable + Files in Your own applications. + b) You may apply bug fixes, portability fixes and other modifications + obtained from the Public Domain or from the Author. A Work modified in + such a way shall still be considered the standard version and will be + subject to this License. + c) You may otherwise modify Your copy of this Work (excluding the + Articles) in any way to create a Derivative Work, provided that You + insert a prominent notice in each changed file stating how, when and + where You changed that file. + d) You may distribute the standard version of the Executable Files and + Source Code or Derivative Work in aggregate with other (possibly + commercial) programs as part of a larger (possibly commercial) + software distribution. + e) The Articles discussing the Work published in any form by the + author may not be distributed or republished without the Author's + consent. The author retains copyright to any such Articles. You may + use the Executable Files and Source Code pursuant to this License but + you may not repost or republish or otherwise distribute or make + available the Articles, without the prior written consent of the + Author. + + Any subroutines or modules supplied by You and linked into the Source Code + or Executable Files of this Work shall not be considered part of this Work + and will not be subject to the terms of this License. + + 4. Patent License. + Subject to the terms and conditions of this License, each Author hereby + grants to You a perpetual, worldwide, non-exclusive, no-charge, + royalty-free, irrevocable (except as stated in this section) patent + license to make, have made, use, import, and otherwise transfer the Work. + + 5. Restrictions. The license granted in Section 3 above is expressly made + subject to and limited by the following restrictions: + + a) You agree not to remove any of the original copyright, patent, + trademark, and attribution notices and associated disclaimers that may + appear in the Source Code or Executable Files. + b) You agree not to advertise or in any way imply that this Work is a + product of Your own. + c) The name of the Author may not be used to endorse or promote products + derived from the Work without the prior written consent of the Author. + d) You agree not to sell, lease, or rent any part of the Work. This + does not restrict you from including the Work or any part of the Work + inside a larger software distribution that itself is being sold. The + Work by itself, though, cannot be sold, leased or rented. + e) You may distribute the Executable Files and Source Code only under + the terms of this License, and You must include a copy of, or the + Uniform Resource Identifier for, this License with every copy of the + Executable Files or Source Code You distribute and ensure that anyone + receiving such Executable Files and Source Code agrees that the terms + of this License apply to such Executable Files and/or Source Code. + You may not offer or impose any terms on the Work that alter or + restrict the terms of this License or the recipients' exercise of the + rights granted hereunder. You may not sublicense the Work. You must + keep intact all notices that refer to this License and to the + disclaimer of warranties. You may not distribute the Executable Files + or Source Code with any technological measures that control access or + use of the Work in a manner inconsistent with the terms of this + License. + f) You agree not to use the Work for illegal, immoral or improper + purposes, or on pages containing illegal, immoral or improper material. + The Work is subject to applicable export laws. You agree to comply with + all such laws and regulations that may apply to the Work + after Your receipt of the Work. + + 6. Representations, Warranties and Disclaimer. + THIS WORK IS PROVIDED "AS IS", "WHERE IS" AND "AS AVAILABLE", WITHOUT + ANY EXPRESS OR IMPLIED WARRANTIES OR CONDITIONS OR GUARANTEES. YOU, THE + USER, ASSUME ALL RISK IN ITS USE, INCLUDING COPYRIGHT INFRINGEMENT, + PATENT INFRINGEMENT, SUITABILITY, ETC. AUTHOR EXPRESSLY DISCLAIMS ALL + EXPRESS, IMPLIED OR STATUTORY WARRANTIES OR CONDITIONS, INCLUDING + WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF MERCHANTABILITY, + MERCHANTABLE QUALITY OR FITNESS FOR A PARTICULAR PURPOSE, OR ANY + WARRANTY OF TITLE OR NON-INFRINGEMENT, OR THAT THE WORK (OR ANY PORTION + THEREOF) IS CORRECT, USEFUL, BUG-FREE OR FREE OF VIRUSES. YOU MUST PASS + THIS DISCLAIMER ON WHENEVER YOU DISTRIBUTE THE WORK OR DERIVATIVE WORKS. + + 7. Indemnity. + You agree to defend, indemnify and hold harmless the Author and the + Publisher from and against any claims, suits, losses, damages, liabilities, + costs, and expenses (including reasonable legal or attorneys fees) + resulting from or relating to any use of the Work by You. + + 8. Limitation on Liability. + EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL THE + AUTHOR OR THE PUBLISHER BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY + SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING + OUT OF THIS LICENSE OR THE USE OF THE WORK OR OTHERWISE, EVEN IF THE + AUTHOR OR THE PUBLISHER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + DAMAGES. + + 9. Termination. + + a) This License and the rights granted hereunder will terminate + automatically upon any breach by You of any term of this License. + Individuals or entities who have received Derivative Works from You + under this License, however, will not have their licenses terminated + provided such individuals or entities remain in full compliance with + those licenses. Sections 1, 2, 6, 7, 8, 9, 10 and 11 will survive any + termination of this License. + b) If You bring a copyright, trademark, patent or any other + infringement claim against any contributor over infringements You claim + are made by the Work, your License from such contributor to the Work + ends automatically. + c) Subject to the above terms and conditions, this License is perpetual + (for the duration of the applicable copyright in the Work). + Notwithstanding the above, the Author reserves the right to release the + Work under different license terms or to stop distributing the Work at + any time; provided, however that any such election will not serve to + withdraw this License (or any other license that has been, or is + required to be, granted under the terms of this License), and this + License will continue in full force and effect unless terminated as + stated above. + + 10. Publisher. + The parties hereby confirm that the Publisher shall not, under any + circumstances, be responsible for and shall not have any liability in + respect of the subject matter of this License. The Publisher makes no + warranty whatsoever in connection with the Work and shall not be liable + to You or any party on any legal theory for any damages whatsoever, + including without limitation any general, special, incidental or + consequential damages arising in connection to this license. The Publisher + reserves the right to cease making the Work available to You at any time + without notice. + + 11. Miscellaneous + a) This License shall be governed by the laws of the location of the + head office of the Author or if the Author is an individual, the laws + of location of the principal place of residence of the Author. + b) If any provision of this License is invalid or unenforceable under + applicable law, it shall not affect the validity or enforceability of + the remainder of the terms of this License, and without further action + by the parties to this License, such provision shall be reformed to the + minimum extent necessary to make such provision valid and enforceable. + c) No term or provision of this License shall be deemed waived and no + breach consented to unless such waiver or consent shall be in writing + and signed by the party to be charged with such waiver or consent. + d) This License constitutes the entire agreement between the parties + with respect to the Work licensed herein. There are no understandings, + agreements or representations with respect to the Work not specified + herein. The Author shall not be bound by any additional provisions + that may appear in any communication from You. This License may not be + modified without the mutual written agreement of the Author and You. + + +************************************************************************************** +Licence Information for Renci SSH .NET +(Renci.SshNet.dll) +************************************************************************************** + +https://sshnet.codeplex.com/ + +New BSD License (BSD) + +Copyright (c) 2010, RENCI +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +* Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +* Neither the name of RENCI nor the names of its contributors may be used to +endorse or promote products derived from this software without specific prior +written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +************************************************************************************** +Licence Information for Orbit Tools +(Zeptomoby.OrbitTools.*.dll) +************************************************************************************** + +http://www.zeptomoby.com/satellites/ + +The OrbitTools Libraries +NORAD SGP4/SDP4 Implementations in C++ and C# by Michael F. Henry + +Provided here are implementations of NORAD algorithms for determining satellite +location and velocity in earth orbit. The algorithms come from the December, +1980 NORAD document "Space Track Report No. 3". The two orbital models +implemented here are: SGP4, for "near-earth" objects, and SDP4 for "deep +space" objects. These two models are widely used in satellite tracking software +and can produce very accurate results when used with current NORAD two-line +element data. + +Public and Standard Editions + +In 2002, I created a modern, object-oriented C++ version of the original 1980's +era NORAD FORTRAN IV SGP4/SDP4 implementations. In 2003, I ported this C++ +implementation to C#. These two implementations represent the Public Edition +of my software, and are available free of charge for non-commercial use. +Commercial users must purchase a software license agreement, for which they +receive the Standard Edition of the software. The Standard Edition includes +all the features of the Public Edition, plus WGS-84 support, and ECF coordinate +support. + +Professional Edition and Track Library + +In 2009, I integrated the changes recommended in "Revisiting Space Track Report +No. 3" (D. Valledo, et al.) into my C# implementation, and did the same for my +C++ implementation in 2010. These two implementations of the software +represent the Professional Edition, and are available only to customers who +purchase a license agreement. In 2012, I developed the Track Library as an +extension of the Professional Edition. The Track Library provides +implementations of common tasks performed by satellite tracking software, +including satellite pass predictions. Information about the Track Library can +be found here. + +More information about the the Public, Standard, and Professional Editions of +the OrbitTools libraries can be found here. + +For excellent information on the underlying physics of orbits, visible +satellite observations, current NORAD TLE data, and other related material, +see www.celestrak.com which is maintained by Dr. T. S. Kelso. + +Michael F. Henry +mfh@zeptomoby.com +April, 2014 + +************************************************************************************** +Licence Information for Ionic ZIP Tools +(ionic.dll) +************************************************************************************** + +http://dotnetzip.codeplex.com/ + +Microsoft Public License (Ms-PL) + +This license governs use of the accompanying software. If you use the software, +you accept this license. If you do not accept the license, do not use the +software. + +1. Definitions + +The terms "reproduce," "reproduction," "derivative works," and "distribution" +have the same meaning here as under U.S. copyright law. + +A "contribution" is the original software, or any additions or changes to the +software. +A "contributor" is any person that distributes its contribution under this +license. +"Licensed patents" are a contributor's patent claims that read directly on its +contribution. + +2. Grant of Rights + +(A) Copyright Grant- Subject to the terms of this license, including the +license conditions and limitations in section 3, each contributor grants you a +non-exclusive, worldwide, royalty-free copyright license to reproduce its +contribution, prepare derivative works of its contribution, and distribute its +contribution or any derivative works that you create. + +(B) Patent Grant- Subject to the terms of this license, including the license +conditions and limitations in section 3, each contributor grants you a +non-exclusive, worldwide, royalty-free license under its licensed patents to +make, have made, use, sell, offer for sale, import, and/or otherwise dispose +of its contribution in the software or derivative works of the contribution +in the software. + +3. Conditions and Limitations + +(A) No Trademark License- This license does not grant you rights to use any +contributors' name, logo, or trademarks. +(B) If you bring a patent claim against any contributor over patents that you +claim are infringed by the software, your patent license from such contributor +to the software ends automatically. +(C) If you distribute any portion of the software, you must retain all +copyright, patent, trademark, and attribution notices that are present in the +software. +(D) If you distribute any portion of the software in source code form, you may +do so only under this license by including a complete copy of this license with +your distribution. If you distribute any portion of the software in compiled or +object code form, you may only do so under a license that complies with this +license. +(E) The software is licensed "as-is." You bear the risk of using it. The +contributors give no express warranties, guarantees or conditions. You may have +additional consumer rights under your local laws which this license cannot +change. To the extent permitted under your local laws, the contributors exclude +the implied warranties of merchantability, fitness for a particular purpose and +non-infringement. + + +************************************************************************************** +Licence Information for LibASDB +(LibASDB.dll) +************************************************************************************** + +This library is ported and modified from the openskynetwork java-adsb library +by DL2ALF. See https://github.com/openskynetwork/java-adsb for more details. + +You may consider yourself whether this is a derived work or not. +Therefor the C# code is under GPL V3.0 to avoid any license issues. + + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + +************************************************************************************** +Licence Information for JSON +(LibASDB.dll) +************************************************************************************** + +The MIT License (MIT) + +Copyright (c) 2007 James Newton-King + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +************************************************************************************** +Licence Information for HtmlAgilityPack +(HtmlAgilityPack.dll) +************************************************************************************** + +https://htmlagilitypack.codeplex.com/ + +Microsoft Public License (Ms-PL) + +This license governs use of the accompanying software. If you use the software, +you accept this license. If you do not accept the license, do not use the +software. + +1. Definitions + +The terms "reproduce," "reproduction," "derivative works," and "distribution" +have the same meaning here as under U.S. copyright law. + +A "contribution" is the original software, or any additions or changes to the +software. +A "contributor" is any person that distributes its contribution under this +license. +"Licensed patents" are a contributor's patent claims that read directly on its +contribution. + +2. Grant of Rights + +(A) Copyright Grant- Subject to the terms of this license, including the +license conditions and limitations in section 3, each contributor grants you a +non-exclusive, worldwide, royalty-free copyright license to reproduce its +contribution, prepare derivative works of its contribution, and distribute its +contribution or any derivative works that you create. + +(B) Patent Grant- Subject to the terms of this license, including the license +conditions and limitations in section 3, each contributor grants you a +non-exclusive, worldwide, royalty-free license under its licensed patents to +make, have made, use, sell, offer for sale, import, and/or otherwise dispose +of its contribution in the software or derivative works of the contribution +in the software. + +3. Conditions and Limitations + +(A) No Trademark License- This license does not grant you rights to use any +contributors' name, logo, or trademarks. +(B) If you bring a patent claim against any contributor over patents that you +claim are infringed by the software, your patent license from such contributor +to the software ends automatically. +(C) If you distribute any portion of the software, you must retain all +copyright, patent, trademark, and attribution notices that are present in the +software. +(D) If you distribute any portion of the software in source code form, you may +do so only under this license by including a complete copy of this license with +your distribution. If you distribute any portion of the software in compiled or +object code form, you may only do so under a license that complies with this +license. +(E) The software is licensed "as-is." You bear the risk of using it. The +contributors give no express warranties, guarantees or conditions. You may have +additional consumer rights under your local laws which this license cannot +change. To the extent permitted under your local laws, the contributors exclude +the implied warranties of merchantability, fitness for a particular purpose and +non-infringement. + +************************************************************************************** +Licence Information for SQLite database +(System.Data.SQlite.dll, System.Data.SQliteEF6.dll, System.Data.SQlite.Linq.dll) +************************************************************************************** + +http://www.sqlite.org + +SQLite Is Public Domain + +All of the code and documentation in SQLite has been dedicated to the public domain +by the authors. All code authors, and representatives of the companies they work for, +have signed affidavits dedicating their contributions to the public domain and +originals of those signed affidavits are stored in a firesafe at the main offices +of Hwaci. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute the +original SQLite code, either in source code form or as a compiled binary, for any +purpose, commercial or non-commercial, and by any means. + +The previous paragraph applies to the deliverable code and documentation in +SQLite - those parts of the SQLite library that you actually bundle and ship with a +larger application. Some scripts used as part of the build process (for example +the "configure" scripts generated by autoconf) might fall under other open-source +licenses. Nothing from these build scripts ever reaches the final deliverable SQLite +library, however, and so the licenses associated with those scripts should not be a +factor in assessing your rights to copy and use the SQLite library. + +All of the deliverable code in SQLite has been written from scratch. No code has been +taken from other projects or from the open internet. Every line of code can be +traced back to its original author, and all of those authors have public domain +dedications on file. So the SQLite code base is clean and is uncontaminated with +licensed code from other projects. + + +******************************************************************************** +Licence Information for Wizard.NET Library +(AeroWizard.dll) +******************************************************************************** + +https://aerowizard.codeplex.com/ + +The MIT License (MIT) + +Copyright (c) 2013 David Hall + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************************** +Licence Information for Serializable Generics +(SerializableGenerics.dll) +******************************************************************************** + + Copyright (c) 2009, Eduardo Sanchez-Ros + http://eduardosanchezros.blogspot.de/2011/03/serializing-generics_606.html + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + +******************************************************************************** +Licence Information for OxyPlot +(OxyPlot.dll, OxyPlotWindowsForms.dll) +******************************************************************************** + +The MIT License (MIT) + +Copyright (c) 2014 OxyPlot contributors + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + + * * * + + 2015-02-08 + diff --git a/AirScout/LicenseDlg.Designer.cs b/AirScout/LicenseDlg.Designer.cs new file mode 100644 index 0000000..c215f6f --- /dev/null +++ b/AirScout/LicenseDlg.Designer.cs @@ -0,0 +1,74 @@ +namespace AirScout +{ + partial class LicenseDlg + { + /// + /// Erforderliche Designervariable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Verwendete Ressourcen bereinigen. + /// + /// True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Vom Windows Form-Designer generierter Code + + /// + /// Erforderliche Methode für die Designerunterstützung. + /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden. + /// + private void InitializeComponent() + { + this.rtb_License = new System.Windows.Forms.RichTextBox(); + this.btn_Options_License_OK = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // rtb_License + // + this.rtb_License.BackColor = System.Drawing.Color.White; + this.rtb_License.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.rtb_License.Location = new System.Drawing.Point(12, 12); + this.rtb_License.Name = "rtb_License"; + this.rtb_License.ReadOnly = true; + this.rtb_License.Size = new System.Drawing.Size(599, 659); + this.rtb_License.TabIndex = 0; + this.rtb_License.Text = ""; + // + // btn_Options_License_OK + // + this.btn_Options_License_OK.DialogResult = System.Windows.Forms.DialogResult.OK; + this.btn_Options_License_OK.Location = new System.Drawing.Point(266, 687); + this.btn_Options_License_OK.Name = "btn_Options_License_OK"; + this.btn_Options_License_OK.Size = new System.Drawing.Size(75, 23); + this.btn_Options_License_OK.TabIndex = 1; + this.btn_Options_License_OK.Text = "Close"; + this.btn_Options_License_OK.UseVisualStyleBackColor = true; + // + // LicenseDlg + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(623, 722); + this.Controls.Add(this.btn_Options_License_OK); + this.Controls.Add(this.rtb_License); + this.Name = "LicenseDlg"; + this.Text = "LicenseDlg"; + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.RichTextBox rtb_License; + private System.Windows.Forms.Button btn_Options_License_OK; + } +} \ No newline at end of file diff --git a/AirScout/LicenseDlg.cs b/AirScout/LicenseDlg.cs new file mode 100644 index 0000000..7b77e8c --- /dev/null +++ b/AirScout/LicenseDlg.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using System.IO; +using ScoutBase.Core; + +namespace AirScout +{ + public partial class LicenseDlg : Form + { + + private LogWriter Log = LogWriter.Instance; + + public LicenseDlg() + { + InitializeComponent(); + try + { + using (StreamReader sr = new StreamReader(Application.StartupPath + "\\license")) + { + rtb_License.Text = sr.ReadToEnd(); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + } +} diff --git a/AirScout/LicenseDlg.resx b/AirScout/LicenseDlg.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/AirScout/LicenseDlg.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/AirScout/LocalObstructionsDlg.Designer.cs b/AirScout/LocalObstructionsDlg.Designer.cs new file mode 100644 index 0000000..28eda97 --- /dev/null +++ b/AirScout/LocalObstructionsDlg.Designer.cs @@ -0,0 +1,153 @@ +namespace AirScout +{ + partial class LocalObstructionsDlg + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.dgv_Main = new System.Windows.Forms.DataGridView(); + this.btn_Cancel = new System.Windows.Forms.Button(); + this.btn_OK = new System.Windows.Forms.Button(); + this.btn_ClearAll = new System.Windows.Forms.Button(); + this.dc_Bearing = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.dc_Distance = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.dc_Height = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.label1 = new System.Windows.Forms.Label(); + ((System.ComponentModel.ISupportInitialize)(this.dgv_Main)).BeginInit(); + this.SuspendLayout(); + // + // dgv_Main + // + this.dgv_Main.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.dgv_Main.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { + this.dc_Bearing, + this.dc_Distance, + this.dc_Height}); + this.dgv_Main.Dock = System.Windows.Forms.DockStyle.Top; + this.dgv_Main.Location = new System.Drawing.Point(0, 0); + this.dgv_Main.Name = "dgv_Main"; + this.dgv_Main.Size = new System.Drawing.Size(325, 468); + this.dgv_Main.TabIndex = 0; + // + // btn_Cancel + // + this.btn_Cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btn_Cancel.Location = new System.Drawing.Point(151, 576); + this.btn_Cancel.Name = "btn_Cancel"; + this.btn_Cancel.Size = new System.Drawing.Size(75, 23); + this.btn_Cancel.TabIndex = 1; + this.btn_Cancel.Text = "Cancel"; + this.btn_Cancel.UseVisualStyleBackColor = true; + // + // btn_OK + // + this.btn_OK.DialogResult = System.Windows.Forms.DialogResult.OK; + this.btn_OK.Location = new System.Drawing.Point(232, 576); + this.btn_OK.Name = "btn_OK"; + this.btn_OK.Size = new System.Drawing.Size(75, 23); + this.btn_OK.TabIndex = 2; + this.btn_OK.Text = "OK"; + this.btn_OK.UseVisualStyleBackColor = true; + // + // btn_ClearAll + // + this.btn_ClearAll.DialogResult = System.Windows.Forms.DialogResult.OK; + this.btn_ClearAll.Location = new System.Drawing.Point(12, 576); + this.btn_ClearAll.Name = "btn_ClearAll"; + this.btn_ClearAll.Size = new System.Drawing.Size(111, 23); + this.btn_ClearAll.TabIndex = 3; + this.btn_ClearAll.Text = "Clear All"; + this.btn_ClearAll.UseVisualStyleBackColor = true; + this.btn_ClearAll.Click += new System.EventHandler(this.btn_ClearAll_Click); + // + // dc_Bearing + // + this.dc_Bearing.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.ColumnHeader; + this.dc_Bearing.DataPropertyName = "Bearing"; + this.dc_Bearing.HeaderText = "Bearing [deg]"; + this.dc_Bearing.Name = "dc_Bearing"; + this.dc_Bearing.ReadOnly = true; + this.dc_Bearing.Width = 95; + // + // dc_Distance + // + this.dc_Distance.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.ColumnHeader; + this.dc_Distance.DataPropertyName = "Distance"; + this.dc_Distance.HeaderText = "Distance [m]"; + this.dc_Distance.Name = "dc_Distance"; + this.dc_Distance.Width = 91; + // + // dc_Height + // + this.dc_Height.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.ColumnHeader; + this.dc_Height.DataPropertyName = "Height"; + this.dc_Height.HeaderText = "Height [m]"; + this.dc_Height.Name = "dc_Height"; + this.dc_Height.Width = 80; + // + // label1 + // + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(12, 484); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(295, 75); + this.label1.TabIndex = 4; + this.label1.Text = "Fill in your local obstructions here. You can set obe obstruction per degree azim" + + "uth. Please enter the distance to obstruction and the obstructions\' height above" + + " ground."; + this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // LocalObstructionsDlg + // + this.AcceptButton = this.btn_OK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btn_Cancel; + this.ClientSize = new System.Drawing.Size(325, 621); + this.Controls.Add(this.label1); + this.Controls.Add(this.btn_ClearAll); + this.Controls.Add(this.btn_OK); + this.Controls.Add(this.btn_Cancel); + this.Controls.Add(this.dgv_Main); + this.Name = "LocalObstructionsDlg"; + this.Text = "LocalObstructions at"; + ((System.ComponentModel.ISupportInitialize)(this.dgv_Main)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.DataGridView dgv_Main; + private System.Windows.Forms.Button btn_Cancel; + private System.Windows.Forms.Button btn_OK; + private System.Windows.Forms.Button btn_ClearAll; + private System.Windows.Forms.DataGridViewTextBoxColumn dc_Bearing; + private System.Windows.Forms.DataGridViewTextBoxColumn dc_Distance; + private System.Windows.Forms.DataGridViewTextBoxColumn dc_Height; + private System.Windows.Forms.Label label1; + } +} \ No newline at end of file diff --git a/AirScout/LocalObstructionsDlg.cs b/AirScout/LocalObstructionsDlg.cs new file mode 100644 index 0000000..0dd9d74 --- /dev/null +++ b/AirScout/LocalObstructionsDlg.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using ScoutBase.Elevation; + +namespace AirScout +{ + public partial class LocalObstructionsDlg : Form + { + public DataTableLocalObstructions LocalObstructions; + + public LocalObstructionsDlg(LocalObstructionDesignator obstr) + { + InitializeComponent(); + LocalObstructions = new DataTableLocalObstructions(obstr); + dgv_Main.DataSource = LocalObstructions; + } + + private void btn_ClearAll_Click(object sender, EventArgs e) + { + foreach (DataRow row in LocalObstructions.Rows) + { + row["Distance"] = 0; + row["Height"] = 0; + } + } + } +} diff --git a/AirScout/LocalObstructionsDlg.resx b/AirScout/LocalObstructionsDlg.resx new file mode 100644 index 0000000..dab3e6c --- /dev/null +++ b/AirScout/LocalObstructionsDlg.resx @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/AirScout/Main.ico b/AirScout/Main.ico new file mode 100644 index 0000000..64008f7 Binary files /dev/null and b/AirScout/Main.ico differ diff --git a/AirScout/MapDlg.Designer.cs b/AirScout/MapDlg.Designer.cs new file mode 100644 index 0000000..353e97e --- /dev/null +++ b/AirScout/MapDlg.Designer.cs @@ -0,0 +1,2140 @@ +namespace AirScout +{ + partial class MapDlg + { + /// + /// Erforderliche Designervariable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Verwendete Ressourcen bereinigen. + /// + /// True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Vom Windows Form-Designer generierter Code + + /// + /// Erforderliche Methode für die Designerunterstützung. + /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MapDlg)); + ScoutBase.Core.LatLon.GPoint gPoint5 = new ScoutBase.Core.LatLon.GPoint(); + ScoutBase.Core.LatLon.GPoint gPoint6 = new ScoutBase.Core.LatLon.GPoint(); + this.il_Main = new System.Windows.Forms.ImageList(this.components); + this.ti_Progress = new System.Windows.Forms.Timer(this.components); + this.sc_Map = new System.Windows.Forms.SplitContainer(); + this.gb_Map = new System.Windows.Forms.GroupBox(); + this.ag_Azimuth = new AquaControls.AquaGauge(); + this.ag_Elevation = new AquaControls.AquaGauge(); + this.gm_Main = new GMap.NET.WindowsForms.GMapControl(); + this.tc_Main = new System.Windows.Forms.TabControl(); + this.tp_Elevation = new System.Windows.Forms.TabPage(); + this.tp_Spectrum = new System.Windows.Forms.TabPage(); + this.gb_Spectrum_NearestInfo = new System.Windows.Forms.GroupBox(); + this.lbl_Nearest_Dist = new System.Windows.Forms.Label(); + this.label22 = new System.Windows.Forms.Label(); + this.lbl_Nearest_Call = new System.Windows.Forms.Label(); + this.label21 = new System.Windows.Forms.Label(); + this.lbl_Nearest_Angle = new System.Windows.Forms.Label(); + this.label7 = new System.Windows.Forms.Label(); + this.lbl_Nearest_Alt = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.lbl_Nearest_Cat = new System.Windows.Forms.Label(); + this.label5 = new System.Windows.Forms.Label(); + this.lbl_Nearest_Type = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.gb_Spectrum_Status = new System.Windows.Forms.GroupBox(); + this.tb_Spectrum_Status = new System.Windows.Forms.TextBox(); + this.gb_NearestPlaneMap = new System.Windows.Forms.GroupBox(); + this.gm_Nearest = new GMap.NET.WindowsForms.GMapControl(); + this.gb_Spectrum = new System.Windows.Forms.GroupBox(); + this.tp_Analysis = new System.Windows.Forms.TabPage(); + this.panel2 = new System.Windows.Forms.Panel(); + this.gb_Analysis_Player = new System.Windows.Forms.GroupBox(); + this.dtp_Analysis_MaxValue = new System.Windows.Forms.DateTimePicker(); + this.dtp_Analysis_MinValue = new System.Windows.Forms.DateTimePicker(); + this.sb_Analysis_Play = new CustomScrollBar.ScrollBarEx(); + this.gb_Analysis_Controls = new System.Windows.Forms.GroupBox(); + this.btn_Analysis_FastForward = new System.Windows.Forms.Button(); + this.btn_Analysis_Forward = new System.Windows.Forms.Button(); + this.btn_Analysis_Pause = new System.Windows.Forms.Button(); + this.btn_Analysis_Back = new System.Windows.Forms.Button(); + this.btn_Analysis_Rewind = new System.Windows.Forms.Button(); + this.panel1 = new System.Windows.Forms.Panel(); + this.gb_Analysis_Functions = new System.Windows.Forms.GroupBox(); + this.btn_Analysis_Plane_History = new System.Windows.Forms.Button(); + this.btn_Analysis_CrossingHistory = new System.Windows.Forms.Button(); + this.btn_Analysis_Path_SaveToFile = new System.Windows.Forms.Button(); + this.btn_Analysis_Planes_ShowTraffic = new System.Windows.Forms.Button(); + this.gb_Analysis_Database = new System.Windows.Forms.GroupBox(); + this.tb_Analysis_Status = new System.Windows.Forms.TextBox(); + this.btn_Analysis_Planes_History = new System.Windows.Forms.Button(); + this.btn_Analysis_Planes_Clear = new System.Windows.Forms.Button(); + this.btn_Analysis_Planes_Save = new System.Windows.Forms.Button(); + this.btn_Analysis_Planes_Load = new System.Windows.Forms.Button(); + this.gb_Analysis_Mode = new System.Windows.Forms.GroupBox(); + this.btn_Analysis_OFF = new System.Windows.Forms.Button(); + this.btn_Analysis_ON = new System.Windows.Forms.Button(); + this.gb_Analysis_Time = new System.Windows.Forms.GroupBox(); + this.label15 = new System.Windows.Forms.Label(); + this.label14 = new System.Windows.Forms.Label(); + this.tb_Analysis_Stepwidth = new System.Windows.Forms.TextBox(); + this.tb_Analysis_Time = new System.Windows.Forms.TextBox(); + this.tp_News = new System.Windows.Forms.TabPage(); + this.ss_Main = new System.Windows.Forms.StatusStrip(); + this.tsl_Status = new System.Windows.Forms.ToolStripStatusLabel(); + this.tsl_Dummy = new System.Windows.Forms.ToolStripStatusLabel(); + this.tsl_Calculations = new System.Windows.Forms.ToolStripStatusLabel(); + this.tsl_Database = new System.Windows.Forms.ToolStripStatusLabel(); + this.tsl_Database_LED_Aircraft = new System.Windows.Forms.ToolStripStatusLabel(); + this.tsl_Database_LED_Stations = new System.Windows.Forms.ToolStripStatusLabel(); + this.tsl_Database_LED_GLOBE = new System.Windows.Forms.ToolStripStatusLabel(); + this.tsl_Database_LED_SRTM3 = new System.Windows.Forms.ToolStripStatusLabel(); + this.tsl_Database_LED_SRTM1 = new System.Windows.Forms.ToolStripStatusLabel(); + this.tt_Main = new System.Windows.Forms.ToolTip(this.components); + this.btn_Map_PlayPause = new System.Windows.Forms.Button(); + this.btn_Map_Save = new System.Windows.Forms.Button(); + this.btn_Options = new System.Windows.Forms.Button(); + this.cb_Band = new System.Windows.Forms.ComboBox(); + this.tb_QTF = new System.Windows.Forms.TextBox(); + this.tb_QRB = new System.Windows.Forms.TextBox(); + this.gb_Map_Alarms = new System.Windows.Forms.GroupBox(); + this.cb_Alarms_Activate = new System.Windows.Forms.CheckBox(); + this.gb_Map_Zoom = new System.Windows.Forms.GroupBox(); + this.pa_Map_Zoom = new System.Windows.Forms.Panel(); + this.cb_AutoCenter = new System.Windows.Forms.CheckBox(); + this.tb_Zoom = new System.Windows.Forms.TextBox(); + this.btn_Zoom_Out = new System.Windows.Forms.Button(); + this.btn_Zoom_In = new System.Windows.Forms.Button(); + this.gb_Map_Filter = new System.Windows.Forms.GroupBox(); + this.pa_Planes_Filter = new System.Windows.Forms.Panel(); + this.tb_Planes_Filter_Min_Alt = new ScoutBase.Core.Int32TextBox(); + this.tb_Planes_Filter_Max_Circumcircle = new ScoutBase.Core.Int32TextBox(); + this.label12 = new System.Windows.Forms.Label(); + this.label13 = new System.Windows.Forms.Label(); + this.cb_Planes_Filter_Min_Cat = new System.Windows.Forms.ComboBox(); + this.label11 = new System.Windows.Forms.Label(); + this.label10 = new System.Windows.Forms.Label(); + this.label9 = new System.Windows.Forms.Label(); + this.il_Sat = new System.Windows.Forms.ImageList(this.components); + this.bw_WinTestReceive = new System.ComponentModel.BackgroundWorker(); + this.il_Planes_M = new System.Windows.Forms.ImageList(this.components); + this.bw_SpecLab_Receive = new System.ComponentModel.BackgroundWorker(); + this.sc_Main = new System.Windows.Forms.SplitContainer(); + this.tc_Control = new System.Windows.Forms.TabControl(); + this.tp_Control_Single = new System.Windows.Forms.TabPage(); + this.gb_Map_Info = new System.Windows.Forms.GroupBox(); + this.cb_DXCall = new ScoutBase.Core.CallsignComboBox(); + this.cb_DXLoc = new ScoutBase.Core.LocatorComboBox(); + this.cb_MyLoc = new ScoutBase.Core.LocatorComboBox(); + this.cb_MyCall = new ScoutBase.Core.CallsignComboBox(); + this.label6 = new System.Windows.Forms.Label(); + this.label16 = new System.Windows.Forms.Label(); + this.label17 = new System.Windows.Forms.Label(); + this.label18 = new System.Windows.Forms.Label(); + this.label19 = new System.Windows.Forms.Label(); + this.label20 = new System.Windows.Forms.Label(); + this.tp_Control_Multi = new System.Windows.Forms.TabPage(); + this.lv_Control_Watchlist = new System.Windows.Forms.ListView(); + this.ch_Control_Watchlist_Call = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.ch_Control_Watchlist_Loc = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.btn_Control_Manage_Watchlist = new System.Windows.Forms.Button(); + this.tp_Control_Options = new System.Windows.Forms.TabPage(); + this.pa_CommonInfo = new System.Windows.Forms.Panel(); + this.label1 = new System.Windows.Forms.Label(); + this.tb_UTC = new System.Windows.Forms.TextBox(); + this.label8 = new System.Windows.Forms.Label(); + this.gb_Map_Buttons = new System.Windows.Forms.GroupBox(); + this.bw_Track = new System.ComponentModel.BackgroundWorker(); + this.il_Planes_H = new System.Windows.Forms.ImageList(this.components); + this.il_Planes_L = new System.Windows.Forms.ImageList(this.components); + this.il_Planes_S = new System.Windows.Forms.ImageList(this.components); + this.bw_Webserver = new System.ComponentModel.BackgroundWorker(); + this.bw_JSONWriter = new System.ComponentModel.BackgroundWorker(); + this.bw_NewsFeed = new System.ComponentModel.BackgroundWorker(); + this.il_Airports = new System.Windows.Forms.ImageList(this.components); + this.bw_HistoryDownloader = new System.ComponentModel.BackgroundWorker(); + this.ti_Startup = new System.Windows.Forms.Timer(this.components); + this.ti_ShowLegends = new System.Windows.Forms.Timer(this.components); + this.tt_Control_Watchlist = new System.Windows.Forms.ToolTip(this.components); + this.bw_Analysis_DataGetter = new System.ComponentModel.BackgroundWorker(); + this.bw_Analysis_FileSaver = new System.ComponentModel.BackgroundWorker(); + this.bw_Analysis_FileLoader = new System.ComponentModel.BackgroundWorker(); + this.bw_AirportMapper = new System.ComponentModel.BackgroundWorker(); + ((System.ComponentModel.ISupportInitialize)(this.sc_Map)).BeginInit(); + this.sc_Map.Panel1.SuspendLayout(); + this.sc_Map.Panel2.SuspendLayout(); + this.sc_Map.SuspendLayout(); + this.gb_Map.SuspendLayout(); + this.tc_Main.SuspendLayout(); + this.tp_Spectrum.SuspendLayout(); + this.gb_Spectrum_NearestInfo.SuspendLayout(); + this.gb_Spectrum_Status.SuspendLayout(); + this.gb_NearestPlaneMap.SuspendLayout(); + this.tp_Analysis.SuspendLayout(); + this.panel2.SuspendLayout(); + this.gb_Analysis_Player.SuspendLayout(); + this.gb_Analysis_Controls.SuspendLayout(); + this.panel1.SuspendLayout(); + this.gb_Analysis_Functions.SuspendLayout(); + this.gb_Analysis_Database.SuspendLayout(); + this.gb_Analysis_Mode.SuspendLayout(); + this.gb_Analysis_Time.SuspendLayout(); + this.ss_Main.SuspendLayout(); + this.gb_Map_Alarms.SuspendLayout(); + this.gb_Map_Zoom.SuspendLayout(); + this.pa_Map_Zoom.SuspendLayout(); + this.gb_Map_Filter.SuspendLayout(); + this.pa_Planes_Filter.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.sc_Main)).BeginInit(); + this.sc_Main.Panel1.SuspendLayout(); + this.sc_Main.Panel2.SuspendLayout(); + this.sc_Main.SuspendLayout(); + this.tc_Control.SuspendLayout(); + this.tp_Control_Single.SuspendLayout(); + this.gb_Map_Info.SuspendLayout(); + this.tp_Control_Multi.SuspendLayout(); + this.tp_Control_Options.SuspendLayout(); + this.pa_CommonInfo.SuspendLayout(); + this.gb_Map_Buttons.SuspendLayout(); + this.SuspendLayout(); + // + // il_Main + // + this.il_Main.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("il_Main.ImageStream"))); + this.il_Main.TransparentColor = System.Drawing.Color.Transparent; + this.il_Main.Images.SetKeyName(0, "PauseHS.png"); + this.il_Main.Images.SetKeyName(1, "PlayHS.png"); + this.il_Main.Images.SetKeyName(2, "RecordHS.png"); + // + // ti_Progress + // + this.ti_Progress.Enabled = true; + this.ti_Progress.Interval = 1000; + this.ti_Progress.Tick += new System.EventHandler(this.ti_Progress_Tick); + // + // sc_Map + // + this.sc_Map.Dock = System.Windows.Forms.DockStyle.Fill; + this.sc_Map.Location = new System.Drawing.Point(0, 0); + this.sc_Map.Name = "sc_Map"; + this.sc_Map.Orientation = System.Windows.Forms.Orientation.Horizontal; + // + // sc_Map.Panel1 + // + this.sc_Map.Panel1.Controls.Add(this.gb_Map); + // + // sc_Map.Panel2 + // + this.sc_Map.Panel2.BackColor = System.Drawing.SystemColors.Control; + this.sc_Map.Panel2.Controls.Add(this.tc_Main); + this.sc_Map.Size = new System.Drawing.Size(852, 706); + this.sc_Map.SplitterDistance = 476; + this.sc_Map.SplitterWidth = 5; + this.sc_Map.TabIndex = 20; + this.sc_Map.SplitterMoved += new System.Windows.Forms.SplitterEventHandler(this.sc_Map_SplitterMoved); + // + // gb_Map + // + this.gb_Map.BackColor = System.Drawing.SystemColors.Control; + this.gb_Map.Controls.Add(this.ag_Azimuth); + this.gb_Map.Controls.Add(this.ag_Elevation); + this.gb_Map.Controls.Add(this.gm_Main); + this.gb_Map.Dock = System.Windows.Forms.DockStyle.Fill; + this.gb_Map.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.gb_Map.Location = new System.Drawing.Point(0, 0); + this.gb_Map.Name = "gb_Map"; + this.gb_Map.Size = new System.Drawing.Size(852, 476); + this.gb_Map.TabIndex = 13; + this.gb_Map.TabStop = false; + this.gb_Map.Text = "Map"; + // + // ag_Azimuth + // + this.ag_Azimuth.BackColor = System.Drawing.Color.Transparent; + this.ag_Azimuth.DialColor = System.Drawing.Color.SlateGray; + this.ag_Azimuth.DialText = "Azimuth"; + this.ag_Azimuth.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.ag_Azimuth.ForeColor = System.Drawing.Color.Black; + this.ag_Azimuth.Glossiness = 50F; + this.ag_Azimuth.Location = new System.Drawing.Point(493, 305); + this.ag_Azimuth.MaxValue = 360F; + this.ag_Azimuth.MinValue = 0F; + this.ag_Azimuth.Name = "ag_Azimuth"; + this.ag_Azimuth.NoOfDivisions = 12; + this.ag_Azimuth.RecommendedValue = 0F; + this.ag_Azimuth.Size = new System.Drawing.Size(175, 175); + this.ag_Azimuth.TabIndex = 25; + this.ag_Azimuth.ThresholdPercent = 0F; + this.ag_Azimuth.Value = 0F; + this.ag_Azimuth.Visible = false; + // + // ag_Elevation + // + this.ag_Elevation.BackColor = System.Drawing.Color.Transparent; + this.ag_Elevation.DialColor = System.Drawing.Color.SlateGray; + this.ag_Elevation.DialText = "Elevation"; + this.ag_Elevation.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.ag_Elevation.ForeColor = System.Drawing.Color.Black; + this.ag_Elevation.Glossiness = 50F; + this.ag_Elevation.Location = new System.Drawing.Point(674, 306); + this.ag_Elevation.MaxValue = 90F; + this.ag_Elevation.MinValue = 0F; + this.ag_Elevation.Name = "ag_Elevation"; + this.ag_Elevation.NoOfDivisions = 4; + this.ag_Elevation.RecommendedValue = 0F; + this.ag_Elevation.Size = new System.Drawing.Size(175, 175); + this.ag_Elevation.TabIndex = 26; + this.ag_Elevation.ThresholdPercent = 0F; + this.ag_Elevation.Value = 0F; + this.ag_Elevation.Visible = false; + // + // gm_Main + // + this.gm_Main.Bearing = 0F; + this.gm_Main.CanDragMap = true; + this.gm_Main.Dock = System.Windows.Forms.DockStyle.Fill; + this.gm_Main.EmptyTileColor = System.Drawing.Color.Navy; + this.gm_Main.GrayScaleMode = false; + this.gm_Main.HelperLineOption = GMap.NET.WindowsForms.HelperLineOptions.DontShow; + this.gm_Main.LevelsKeepInMemmory = 5; + this.gm_Main.Location = new System.Drawing.Point(3, 16); + this.gm_Main.MarkersEnabled = true; + this.gm_Main.MaxZoom = 2; + this.gm_Main.MinZoom = 2; + this.gm_Main.MouseWheelZoomType = GMap.NET.MouseWheelZoomType.MousePositionAndCenter; + this.gm_Main.Name = "gm_Main"; + this.gm_Main.NegativeMode = false; + this.gm_Main.PolygonsEnabled = true; + this.gm_Main.RetryLoadTile = 0; + this.gm_Main.RoutesEnabled = true; + this.gm_Main.SelectedAreaFillColor = System.Drawing.Color.FromArgb(((int)(((byte)(33)))), ((int)(((byte)(65)))), ((int)(((byte)(105)))), ((int)(((byte)(225))))); + this.gm_Main.ShowTileGridLines = false; + this.gm_Main.Size = new System.Drawing.Size(846, 457); + this.gm_Main.TabIndex = 3; + this.gm_Main.Zoom = 0D; + this.gm_Main.OnMarkerClick += new GMap.NET.WindowsForms.MarkerClick(this.gm_Main_OnMarkerClick); + this.gm_Main.OnMarkerEnter += new GMap.NET.WindowsForms.MarkerEnter(this.gm_Main_OnMarkerEnter); + this.gm_Main.OnMarkerLeave += new GMap.NET.WindowsForms.MarkerLeave(this.gm_Main_OnMarkerLeave); + this.gm_Main.OnMapZoomChanged += new GMap.NET.MapZoomChanged(this.gm_Main_OnMapZoomChanged); + this.gm_Main.Load += new System.EventHandler(this.gm_Main_Load); + this.gm_Main.SizeChanged += new System.EventHandler(this.gm_Main_SizeChanged); + this.gm_Main.Paint += new System.Windows.Forms.PaintEventHandler(this.gm_Main_Paint); + this.gm_Main.MouseDown += new System.Windows.Forms.MouseEventHandler(this.gm_Main_MouseDown); + this.gm_Main.MouseMove += new System.Windows.Forms.MouseEventHandler(this.gm_Main_MouseMove); + this.gm_Main.MouseUp += new System.Windows.Forms.MouseEventHandler(this.gm_Main_MouseUp); + // + // tc_Main + // + this.tc_Main.Controls.Add(this.tp_Elevation); + this.tc_Main.Controls.Add(this.tp_Spectrum); + this.tc_Main.Controls.Add(this.tp_Analysis); + this.tc_Main.Controls.Add(this.tp_News); + this.tc_Main.Dock = System.Windows.Forms.DockStyle.Fill; + this.tc_Main.DrawMode = System.Windows.Forms.TabDrawMode.OwnerDrawFixed; + this.tc_Main.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tc_Main.Location = new System.Drawing.Point(0, 0); + this.tc_Main.MinimumSize = new System.Drawing.Size(0, 200); + this.tc_Main.Name = "tc_Main"; + this.tc_Main.SelectedIndex = 0; + this.tc_Main.Size = new System.Drawing.Size(852, 225); + this.tc_Main.TabIndex = 0; + this.tc_Main.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.tc_Main_DrawItem); + this.tc_Main.SelectedIndexChanged += new System.EventHandler(this.tc_Main_SelectedIndexChanged); + // + // tp_Elevation + // + this.tp_Elevation.BackColor = System.Drawing.SystemColors.Control; + this.tp_Elevation.Location = new System.Drawing.Point(4, 22); + this.tp_Elevation.Name = "tp_Elevation"; + this.tp_Elevation.Padding = new System.Windows.Forms.Padding(3); + this.tp_Elevation.Size = new System.Drawing.Size(844, 199); + this.tp_Elevation.TabIndex = 0; + this.tp_Elevation.Text = "Path Profile"; + this.tp_Elevation.Enter += new System.EventHandler(this.tp_Elevation_Enter); + this.tp_Elevation.Resize += new System.EventHandler(this.tp_Elevation_Resize); + // + // tp_Spectrum + // + this.tp_Spectrum.BackColor = System.Drawing.SystemColors.Control; + this.tp_Spectrum.Controls.Add(this.gb_Spectrum_NearestInfo); + this.tp_Spectrum.Controls.Add(this.gb_Spectrum_Status); + this.tp_Spectrum.Controls.Add(this.gb_NearestPlaneMap); + this.tp_Spectrum.Controls.Add(this.gb_Spectrum); + this.tp_Spectrum.Location = new System.Drawing.Point(4, 22); + this.tp_Spectrum.Name = "tp_Spectrum"; + this.tp_Spectrum.Padding = new System.Windows.Forms.Padding(3); + this.tp_Spectrum.Size = new System.Drawing.Size(844, 199); + this.tp_Spectrum.TabIndex = 1; + this.tp_Spectrum.Text = "Spectrum"; + this.tp_Spectrum.Enter += new System.EventHandler(this.tp_Spectrum_Enter); + this.tp_Spectrum.Resize += new System.EventHandler(this.tp_Spectrum_Resize); + // + // gb_Spectrum_NearestInfo + // + this.gb_Spectrum_NearestInfo.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Right))); + this.gb_Spectrum_NearestInfo.Controls.Add(this.lbl_Nearest_Dist); + this.gb_Spectrum_NearestInfo.Controls.Add(this.label22); + this.gb_Spectrum_NearestInfo.Controls.Add(this.lbl_Nearest_Call); + this.gb_Spectrum_NearestInfo.Controls.Add(this.label21); + this.gb_Spectrum_NearestInfo.Controls.Add(this.lbl_Nearest_Angle); + this.gb_Spectrum_NearestInfo.Controls.Add(this.label7); + this.gb_Spectrum_NearestInfo.Controls.Add(this.lbl_Nearest_Alt); + this.gb_Spectrum_NearestInfo.Controls.Add(this.label4); + this.gb_Spectrum_NearestInfo.Controls.Add(this.lbl_Nearest_Cat); + this.gb_Spectrum_NearestInfo.Controls.Add(this.label5); + this.gb_Spectrum_NearestInfo.Controls.Add(this.lbl_Nearest_Type); + this.gb_Spectrum_NearestInfo.Controls.Add(this.label2); + this.gb_Spectrum_NearestInfo.Location = new System.Drawing.Point(489, 54); + this.gb_Spectrum_NearestInfo.Name = "gb_Spectrum_NearestInfo"; + this.gb_Spectrum_NearestInfo.Size = new System.Drawing.Size(175, 136); + this.gb_Spectrum_NearestInfo.TabIndex = 8; + this.gb_Spectrum_NearestInfo.TabStop = false; + this.gb_Spectrum_NearestInfo.Text = "Nearest Plane Info"; + // + // lbl_Nearest_Dist + // + this.lbl_Nearest_Dist.AutoSize = true; + this.lbl_Nearest_Dist.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lbl_Nearest_Dist.Location = new System.Drawing.Point(82, 114); + this.lbl_Nearest_Dist.Name = "lbl_Nearest_Dist"; + this.lbl_Nearest_Dist.Size = new System.Drawing.Size(14, 14); + this.lbl_Nearest_Dist.TabIndex = 11; + this.lbl_Nearest_Dist.Text = "0"; + // + // label22 + // + this.label22.AutoSize = true; + this.label22.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label22.Location = new System.Drawing.Point(6, 114); + this.label22.Name = "label22"; + this.label22.Size = new System.Drawing.Size(52, 13); + this.label22.TabIndex = 10; + this.label22.Text = "Distance:"; + // + // lbl_Nearest_Call + // + this.lbl_Nearest_Call.AutoSize = true; + this.lbl_Nearest_Call.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lbl_Nearest_Call.Location = new System.Drawing.Point(82, 26); + this.lbl_Nearest_Call.Name = "lbl_Nearest_Call"; + this.lbl_Nearest_Call.Size = new System.Drawing.Size(14, 14); + this.lbl_Nearest_Call.TabIndex = 9; + this.lbl_Nearest_Call.Text = "0"; + // + // label21 + // + this.label21.AutoSize = true; + this.label21.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label21.Location = new System.Drawing.Point(6, 26); + this.label21.Name = "label21"; + this.label21.Size = new System.Drawing.Size(27, 13); + this.label21.TabIndex = 8; + this.label21.Text = "Call:"; + // + // lbl_Nearest_Angle + // + this.lbl_Nearest_Angle.AutoSize = true; + this.lbl_Nearest_Angle.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lbl_Nearest_Angle.Location = new System.Drawing.Point(82, 97); + this.lbl_Nearest_Angle.Name = "lbl_Nearest_Angle"; + this.lbl_Nearest_Angle.Size = new System.Drawing.Size(14, 14); + this.lbl_Nearest_Angle.TabIndex = 7; + this.lbl_Nearest_Angle.Text = "0"; + // + // label7 + // + this.label7.AutoSize = true; + this.label7.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label7.Location = new System.Drawing.Point(6, 97); + this.label7.Name = "label7"; + this.label7.Size = new System.Drawing.Size(37, 13); + this.label7.TabIndex = 6; + this.label7.Text = "Angle:"; + // + // lbl_Nearest_Alt + // + this.lbl_Nearest_Alt.AutoSize = true; + this.lbl_Nearest_Alt.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lbl_Nearest_Alt.Location = new System.Drawing.Point(82, 79); + this.lbl_Nearest_Alt.Name = "lbl_Nearest_Alt"; + this.lbl_Nearest_Alt.Size = new System.Drawing.Size(14, 14); + this.lbl_Nearest_Alt.TabIndex = 5; + this.lbl_Nearest_Alt.Text = "0"; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label4.Location = new System.Drawing.Point(6, 79); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(45, 13); + this.label4.TabIndex = 4; + this.label4.Text = "Altitude:"; + // + // lbl_Nearest_Cat + // + this.lbl_Nearest_Cat.AutoSize = true; + this.lbl_Nearest_Cat.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lbl_Nearest_Cat.Location = new System.Drawing.Point(82, 60); + this.lbl_Nearest_Cat.Name = "lbl_Nearest_Cat"; + this.lbl_Nearest_Cat.Size = new System.Drawing.Size(14, 14); + this.lbl_Nearest_Cat.TabIndex = 3; + this.lbl_Nearest_Cat.Text = "0"; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label5.Location = new System.Drawing.Point(6, 60); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(52, 13); + this.label5.TabIndex = 2; + this.label5.Text = "Category:"; + // + // lbl_Nearest_Type + // + this.lbl_Nearest_Type.AutoSize = true; + this.lbl_Nearest_Type.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lbl_Nearest_Type.Location = new System.Drawing.Point(82, 44); + this.lbl_Nearest_Type.Name = "lbl_Nearest_Type"; + this.lbl_Nearest_Type.Size = new System.Drawing.Size(14, 14); + this.lbl_Nearest_Type.TabIndex = 1; + this.lbl_Nearest_Type.Text = "0"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label2.Location = new System.Drawing.Point(6, 44); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(34, 13); + this.label2.TabIndex = 0; + this.label2.Text = "Type:"; + // + // gb_Spectrum_Status + // + this.gb_Spectrum_Status.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.gb_Spectrum_Status.Controls.Add(this.tb_Spectrum_Status); + this.gb_Spectrum_Status.Location = new System.Drawing.Point(8, 6); + this.gb_Spectrum_Status.Name = "gb_Spectrum_Status"; + this.gb_Spectrum_Status.Size = new System.Drawing.Size(830, 40); + this.gb_Spectrum_Status.TabIndex = 7; + this.gb_Spectrum_Status.TabStop = false; + this.gb_Spectrum_Status.Text = "Status"; + // + // tb_Spectrum_Status + // + this.tb_Spectrum_Status.BackColor = System.Drawing.SystemColors.Control; + this.tb_Spectrum_Status.Location = new System.Drawing.Point(6, 14); + this.tb_Spectrum_Status.Name = "tb_Spectrum_Status"; + this.tb_Spectrum_Status.Size = new System.Drawing.Size(129, 20); + this.tb_Spectrum_Status.TabIndex = 4; + // + // gb_NearestPlaneMap + // + this.gb_NearestPlaneMap.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Right))); + this.gb_NearestPlaneMap.Controls.Add(this.gm_Nearest); + this.gb_NearestPlaneMap.Location = new System.Drawing.Point(670, 54); + this.gb_NearestPlaneMap.Name = "gb_NearestPlaneMap"; + this.gb_NearestPlaneMap.Size = new System.Drawing.Size(168, 139); + this.gb_NearestPlaneMap.TabIndex = 6; + this.gb_NearestPlaneMap.TabStop = false; + this.gb_NearestPlaneMap.Text = "Nearest Plane Map"; + // + // gm_Nearest + // + this.gm_Nearest.Bearing = 0F; + this.gm_Nearest.CanDragMap = false; + this.gm_Nearest.Dock = System.Windows.Forms.DockStyle.Fill; + this.gm_Nearest.EmptyTileColor = System.Drawing.Color.Navy; + this.gm_Nearest.GrayScaleMode = false; + this.gm_Nearest.HelperLineOption = GMap.NET.WindowsForms.HelperLineOptions.DontShow; + this.gm_Nearest.LevelsKeepInMemmory = 5; + this.gm_Nearest.Location = new System.Drawing.Point(3, 16); + this.gm_Nearest.MarkersEnabled = true; + this.gm_Nearest.MaxZoom = 2; + this.gm_Nearest.MinZoom = 2; + this.gm_Nearest.MouseWheelZoomType = GMap.NET.MouseWheelZoomType.MousePositionAndCenter; + this.gm_Nearest.Name = "gm_Nearest"; + this.gm_Nearest.NegativeMode = false; + this.gm_Nearest.PolygonsEnabled = true; + this.gm_Nearest.RetryLoadTile = 0; + this.gm_Nearest.RoutesEnabled = true; + this.gm_Nearest.SelectedAreaFillColor = System.Drawing.Color.FromArgb(((int)(((byte)(33)))), ((int)(((byte)(65)))), ((int)(((byte)(105)))), ((int)(((byte)(225))))); + this.gm_Nearest.ShowTileGridLines = false; + this.gm_Nearest.Size = new System.Drawing.Size(162, 120); + this.gm_Nearest.TabIndex = 0; + this.gm_Nearest.Zoom = 0D; + // + // gb_Spectrum + // + this.gb_Spectrum.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.gb_Spectrum.Location = new System.Drawing.Point(8, 54); + this.gb_Spectrum.Name = "gb_Spectrum"; + this.gb_Spectrum.Size = new System.Drawing.Size(475, 139); + this.gb_Spectrum.TabIndex = 5; + this.gb_Spectrum.TabStop = false; + this.gb_Spectrum.Text = "Spectrum"; + // + // tp_Analysis + // + this.tp_Analysis.BackColor = System.Drawing.SystemColors.Control; + this.tp_Analysis.Controls.Add(this.panel2); + this.tp_Analysis.Controls.Add(this.panel1); + this.tp_Analysis.Location = new System.Drawing.Point(4, 22); + this.tp_Analysis.Name = "tp_Analysis"; + this.tp_Analysis.Size = new System.Drawing.Size(844, 199); + this.tp_Analysis.TabIndex = 3; + this.tp_Analysis.Text = "Analysis"; + this.tp_Analysis.Enter += new System.EventHandler(this.tp_Analysis_Enter); + this.tp_Analysis.Leave += new System.EventHandler(this.tp_Analysis_Leave); + // + // panel2 + // + this.panel2.Controls.Add(this.gb_Analysis_Player); + this.panel2.Controls.Add(this.gb_Analysis_Controls); + this.panel2.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel2.Location = new System.Drawing.Point(0, 137); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(844, 62); + this.panel2.TabIndex = 1; + // + // gb_Analysis_Player + // + this.gb_Analysis_Player.Controls.Add(this.dtp_Analysis_MaxValue); + this.gb_Analysis_Player.Controls.Add(this.dtp_Analysis_MinValue); + this.gb_Analysis_Player.Controls.Add(this.sb_Analysis_Play); + this.gb_Analysis_Player.Dock = System.Windows.Forms.DockStyle.Fill; + this.gb_Analysis_Player.Location = new System.Drawing.Point(0, 0); + this.gb_Analysis_Player.MinimumSize = new System.Drawing.Size(565, 58); + this.gb_Analysis_Player.Name = "gb_Analysis_Player"; + this.gb_Analysis_Player.Size = new System.Drawing.Size(591, 62); + this.gb_Analysis_Player.TabIndex = 4; + this.gb_Analysis_Player.TabStop = false; + this.gb_Analysis_Player.Text = "Player"; + this.gb_Analysis_Player.SizeChanged += new System.EventHandler(this.gb_Analysis_Player_SizeChanged); + // + // dtp_Analysis_MaxValue + // + this.dtp_Analysis_MaxValue.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.dtp_Analysis_MaxValue.CustomFormat = "yyyy-MM-dd HH:mm"; + this.dtp_Analysis_MaxValue.Enabled = false; + this.dtp_Analysis_MaxValue.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.dtp_Analysis_MaxValue.Format = System.Windows.Forms.DateTimePickerFormat.Custom; + this.dtp_Analysis_MaxValue.Location = new System.Drawing.Point(463, 26); + this.dtp_Analysis_MaxValue.Name = "dtp_Analysis_MaxValue"; + this.dtp_Analysis_MaxValue.Size = new System.Drawing.Size(122, 20); + this.dtp_Analysis_MaxValue.TabIndex = 25; + this.dtp_Analysis_MaxValue.ValueChanged += new System.EventHandler(this.dtp_Analysis_MaxValue_ValueChanged); + // + // dtp_Analysis_MinValue + // + this.dtp_Analysis_MinValue.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.dtp_Analysis_MinValue.CustomFormat = "yyyy-MM-dd HH:mm"; + this.dtp_Analysis_MinValue.Enabled = false; + this.dtp_Analysis_MinValue.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.dtp_Analysis_MinValue.Format = System.Windows.Forms.DateTimePickerFormat.Custom; + this.dtp_Analysis_MinValue.Location = new System.Drawing.Point(6, 26); + this.dtp_Analysis_MinValue.Name = "dtp_Analysis_MinValue"; + this.dtp_Analysis_MinValue.Size = new System.Drawing.Size(123, 20); + this.dtp_Analysis_MinValue.TabIndex = 24; + this.dtp_Analysis_MinValue.ValueChanged += new System.EventHandler(this.dtp_Analysis_MinValue_ValueChanged); + // + // sb_Analysis_Play + // + this.sb_Analysis_Play.Anchor = System.Windows.Forms.AnchorStyles.Bottom; + this.sb_Analysis_Play.Location = new System.Drawing.Point(146, 26); + this.sb_Analysis_Play.Maximum = 1000000; + this.sb_Analysis_Play.Name = "sb_Analysis_Play"; + this.sb_Analysis_Play.Orientation = CustomScrollBar.ScrollBarOrientation.Horizontal; + this.sb_Analysis_Play.Size = new System.Drawing.Size(289, 19); + this.sb_Analysis_Play.TabIndex = 22; + this.sb_Analysis_Play.Scroll += new System.Windows.Forms.ScrollEventHandler(this.sb_Analysis_Play_Scroll); + this.sb_Analysis_Play.SizeChanged += new System.EventHandler(this.sb_Analysis_Play_SizeChanged); + // + // gb_Analysis_Controls + // + this.gb_Analysis_Controls.Controls.Add(this.btn_Analysis_FastForward); + this.gb_Analysis_Controls.Controls.Add(this.btn_Analysis_Forward); + this.gb_Analysis_Controls.Controls.Add(this.btn_Analysis_Pause); + this.gb_Analysis_Controls.Controls.Add(this.btn_Analysis_Back); + this.gb_Analysis_Controls.Controls.Add(this.btn_Analysis_Rewind); + this.gb_Analysis_Controls.Dock = System.Windows.Forms.DockStyle.Right; + this.gb_Analysis_Controls.Location = new System.Drawing.Point(591, 0); + this.gb_Analysis_Controls.Name = "gb_Analysis_Controls"; + this.gb_Analysis_Controls.Size = new System.Drawing.Size(253, 62); + this.gb_Analysis_Controls.TabIndex = 2; + this.gb_Analysis_Controls.TabStop = false; + this.gb_Analysis_Controls.Text = "Controls"; + // + // btn_Analysis_FastForward + // + this.btn_Analysis_FastForward.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.btn_Analysis_FastForward.Enabled = false; + this.btn_Analysis_FastForward.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Analysis_FastForward.Image = ((System.Drawing.Image)(resources.GetObject("btn_Analysis_FastForward.Image"))); + this.btn_Analysis_FastForward.Location = new System.Drawing.Point(202, 19); + this.btn_Analysis_FastForward.Margin = new System.Windows.Forms.Padding(0); + this.btn_Analysis_FastForward.Name = "btn_Analysis_FastForward"; + this.btn_Analysis_FastForward.Size = new System.Drawing.Size(47, 29); + this.btn_Analysis_FastForward.TabIndex = 20; + this.btn_Analysis_FastForward.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.tt_Main.SetToolTip(this.btn_Analysis_FastForward, "Click here to speed up playing by 10 sec"); + this.btn_Analysis_FastForward.UseVisualStyleBackColor = true; + this.btn_Analysis_FastForward.Click += new System.EventHandler(this.btn_Analysis_FastForward_Click); + // + // btn_Analysis_Forward + // + this.btn_Analysis_Forward.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.btn_Analysis_Forward.Enabled = false; + this.btn_Analysis_Forward.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Analysis_Forward.Image = ((System.Drawing.Image)(resources.GetObject("btn_Analysis_Forward.Image"))); + this.btn_Analysis_Forward.Location = new System.Drawing.Point(153, 19); + this.btn_Analysis_Forward.Margin = new System.Windows.Forms.Padding(0); + this.btn_Analysis_Forward.Name = "btn_Analysis_Forward"; + this.btn_Analysis_Forward.Size = new System.Drawing.Size(47, 29); + this.btn_Analysis_Forward.TabIndex = 19; + this.btn_Analysis_Forward.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.tt_Main.SetToolTip(this.btn_Analysis_Forward, "Click here to speed up playing by 1 sec"); + this.btn_Analysis_Forward.UseVisualStyleBackColor = true; + this.btn_Analysis_Forward.Click += new System.EventHandler(this.btn_Analysis_Forward_Click); + // + // btn_Analysis_Pause + // + this.btn_Analysis_Pause.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.btn_Analysis_Pause.Enabled = false; + this.btn_Analysis_Pause.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Analysis_Pause.Image = ((System.Drawing.Image)(resources.GetObject("btn_Analysis_Pause.Image"))); + this.btn_Analysis_Pause.Location = new System.Drawing.Point(104, 19); + this.btn_Analysis_Pause.Margin = new System.Windows.Forms.Padding(0); + this.btn_Analysis_Pause.Name = "btn_Analysis_Pause"; + this.btn_Analysis_Pause.Size = new System.Drawing.Size(47, 29); + this.btn_Analysis_Pause.TabIndex = 18; + this.btn_Analysis_Pause.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.tt_Main.SetToolTip(this.btn_Analysis_Pause, "Click here to stop playing"); + this.btn_Analysis_Pause.UseVisualStyleBackColor = true; + this.btn_Analysis_Pause.Click += new System.EventHandler(this.btn_Analysis_Pause_Click); + // + // btn_Analysis_Back + // + this.btn_Analysis_Back.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.btn_Analysis_Back.Enabled = false; + this.btn_Analysis_Back.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Analysis_Back.Image = ((System.Drawing.Image)(resources.GetObject("btn_Analysis_Back.Image"))); + this.btn_Analysis_Back.Location = new System.Drawing.Point(55, 19); + this.btn_Analysis_Back.Margin = new System.Windows.Forms.Padding(0); + this.btn_Analysis_Back.Name = "btn_Analysis_Back"; + this.btn_Analysis_Back.Size = new System.Drawing.Size(47, 29); + this.btn_Analysis_Back.TabIndex = 17; + this.btn_Analysis_Back.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.tt_Main.SetToolTip(this.btn_Analysis_Back, "Click here to slow down playing by 1 sec"); + this.btn_Analysis_Back.UseVisualStyleBackColor = true; + this.btn_Analysis_Back.Click += new System.EventHandler(this.btn_Analysis_Back_Click); + // + // btn_Analysis_Rewind + // + this.btn_Analysis_Rewind.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.btn_Analysis_Rewind.Enabled = false; + this.btn_Analysis_Rewind.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Analysis_Rewind.Image = ((System.Drawing.Image)(resources.GetObject("btn_Analysis_Rewind.Image"))); + this.btn_Analysis_Rewind.Location = new System.Drawing.Point(6, 19); + this.btn_Analysis_Rewind.Margin = new System.Windows.Forms.Padding(0); + this.btn_Analysis_Rewind.Name = "btn_Analysis_Rewind"; + this.btn_Analysis_Rewind.Size = new System.Drawing.Size(47, 29); + this.btn_Analysis_Rewind.TabIndex = 16; + this.btn_Analysis_Rewind.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.tt_Main.SetToolTip(this.btn_Analysis_Rewind, "Click here to slow down playing by 10 sec"); + this.btn_Analysis_Rewind.UseVisualStyleBackColor = true; + this.btn_Analysis_Rewind.Click += new System.EventHandler(this.btn_Analysis_Rewind_Click); + // + // panel1 + // + this.panel1.Controls.Add(this.gb_Analysis_Functions); + this.panel1.Controls.Add(this.gb_Analysis_Database); + this.panel1.Controls.Add(this.gb_Analysis_Mode); + this.panel1.Controls.Add(this.gb_Analysis_Time); + this.panel1.Dock = System.Windows.Forms.DockStyle.Top; + this.panel1.Location = new System.Drawing.Point(0, 0); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(844, 137); + this.panel1.TabIndex = 0; + // + // gb_Analysis_Functions + // + this.gb_Analysis_Functions.Controls.Add(this.btn_Analysis_Plane_History); + this.gb_Analysis_Functions.Controls.Add(this.btn_Analysis_CrossingHistory); + this.gb_Analysis_Functions.Controls.Add(this.btn_Analysis_Path_SaveToFile); + this.gb_Analysis_Functions.Controls.Add(this.btn_Analysis_Planes_ShowTraffic); + this.gb_Analysis_Functions.Dock = System.Windows.Forms.DockStyle.Fill; + this.gb_Analysis_Functions.Location = new System.Drawing.Point(358, 0); + this.gb_Analysis_Functions.Name = "gb_Analysis_Functions"; + this.gb_Analysis_Functions.Size = new System.Drawing.Size(233, 137); + this.gb_Analysis_Functions.TabIndex = 5; + this.gb_Analysis_Functions.TabStop = false; + this.gb_Analysis_Functions.Text = "Functions"; + // + // btn_Analysis_Plane_History + // + this.btn_Analysis_Plane_History.Enabled = false; + this.btn_Analysis_Plane_History.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Analysis_Plane_History.Location = new System.Drawing.Point(6, 108); + this.btn_Analysis_Plane_History.Name = "btn_Analysis_Plane_History"; + this.btn_Analysis_Plane_History.Size = new System.Drawing.Size(113, 23); + this.btn_Analysis_Plane_History.TabIndex = 40; + this.btn_Analysis_Plane_History.Text = "Export Plane History"; + this.btn_Analysis_Plane_History.UseVisualStyleBackColor = true; + this.btn_Analysis_Plane_History.Click += new System.EventHandler(this.btn_Analysis_Plane_History_Click); + // + // btn_Analysis_CrossingHistory + // + this.btn_Analysis_CrossingHistory.Enabled = false; + this.btn_Analysis_CrossingHistory.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Analysis_CrossingHistory.Location = new System.Drawing.Point(6, 79); + this.btn_Analysis_CrossingHistory.Name = "btn_Analysis_CrossingHistory"; + this.btn_Analysis_CrossingHistory.Size = new System.Drawing.Size(113, 23); + this.btn_Analysis_CrossingHistory.TabIndex = 39; + this.btn_Analysis_CrossingHistory.Text = "Crossing History"; + this.btn_Analysis_CrossingHistory.UseVisualStyleBackColor = true; + this.btn_Analysis_CrossingHistory.Click += new System.EventHandler(this.btn_Analysis_CrossingHistory_Click); + // + // btn_Analysis_Path_SaveToFile + // + this.btn_Analysis_Path_SaveToFile.Enabled = false; + this.btn_Analysis_Path_SaveToFile.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Analysis_Path_SaveToFile.Location = new System.Drawing.Point(6, 50); + this.btn_Analysis_Path_SaveToFile.Name = "btn_Analysis_Path_SaveToFile"; + this.btn_Analysis_Path_SaveToFile.Size = new System.Drawing.Size(113, 23); + this.btn_Analysis_Path_SaveToFile.TabIndex = 38; + this.btn_Analysis_Path_SaveToFile.Text = "Export Path"; + this.btn_Analysis_Path_SaveToFile.UseVisualStyleBackColor = true; + this.btn_Analysis_Path_SaveToFile.Click += new System.EventHandler(this.btn_Analysis_Path_SaveToFile_Click); + // + // btn_Analysis_Planes_ShowTraffic + // + this.btn_Analysis_Planes_ShowTraffic.Enabled = false; + this.btn_Analysis_Planes_ShowTraffic.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Analysis_Planes_ShowTraffic.Location = new System.Drawing.Point(6, 21); + this.btn_Analysis_Planes_ShowTraffic.Name = "btn_Analysis_Planes_ShowTraffic"; + this.btn_Analysis_Planes_ShowTraffic.Size = new System.Drawing.Size(113, 23); + this.btn_Analysis_Planes_ShowTraffic.TabIndex = 37; + this.btn_Analysis_Planes_ShowTraffic.Text = "Show All Traffic"; + this.btn_Analysis_Planes_ShowTraffic.UseVisualStyleBackColor = true; + this.btn_Analysis_Planes_ShowTraffic.Click += new System.EventHandler(this.btn_Analysis_Planes_ShowTraffic_Click); + // + // gb_Analysis_Database + // + this.gb_Analysis_Database.Controls.Add(this.tb_Analysis_Status); + this.gb_Analysis_Database.Controls.Add(this.btn_Analysis_Planes_History); + this.gb_Analysis_Database.Controls.Add(this.btn_Analysis_Planes_Clear); + this.gb_Analysis_Database.Controls.Add(this.btn_Analysis_Planes_Save); + this.gb_Analysis_Database.Controls.Add(this.btn_Analysis_Planes_Load); + this.gb_Analysis_Database.Dock = System.Windows.Forms.DockStyle.Left; + this.gb_Analysis_Database.Location = new System.Drawing.Point(78, 0); + this.gb_Analysis_Database.Name = "gb_Analysis_Database"; + this.gb_Analysis_Database.Size = new System.Drawing.Size(280, 137); + this.gb_Analysis_Database.TabIndex = 4; + this.gb_Analysis_Database.TabStop = false; + this.gb_Analysis_Database.Text = "Database"; + // + // tb_Analysis_Status + // + this.tb_Analysis_Status.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Analysis_Status.Location = new System.Drawing.Point(8, 111); + this.tb_Analysis_Status.Name = "tb_Analysis_Status"; + this.tb_Analysis_Status.ReadOnly = true; + this.tb_Analysis_Status.Size = new System.Drawing.Size(262, 20); + this.tb_Analysis_Status.TabIndex = 38; + // + // btn_Analysis_Planes_History + // + this.btn_Analysis_Planes_History.Enabled = false; + this.btn_Analysis_Planes_History.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Analysis_Planes_History.Location = new System.Drawing.Point(142, 21); + this.btn_Analysis_Planes_History.Name = "btn_Analysis_Planes_History"; + this.btn_Analysis_Planes_History.Size = new System.Drawing.Size(128, 23); + this.btn_Analysis_Planes_History.TabIndex = 36; + this.btn_Analysis_Planes_History.Text = "Load History from URL"; + this.btn_Analysis_Planes_History.UseVisualStyleBackColor = true; + this.btn_Analysis_Planes_History.Click += new System.EventHandler(this.btn_Analysis_Planes_History_Click); + // + // btn_Analysis_Planes_Clear + // + this.btn_Analysis_Planes_Clear.Enabled = false; + this.btn_Analysis_Planes_Clear.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Analysis_Planes_Clear.Location = new System.Drawing.Point(8, 79); + this.btn_Analysis_Planes_Clear.Name = "btn_Analysis_Planes_Clear"; + this.btn_Analysis_Planes_Clear.Size = new System.Drawing.Size(128, 23); + this.btn_Analysis_Planes_Clear.TabIndex = 34; + this.btn_Analysis_Planes_Clear.Text = "Clear Positions"; + this.btn_Analysis_Planes_Clear.UseVisualStyleBackColor = true; + this.btn_Analysis_Planes_Clear.Click += new System.EventHandler(this.btn_Analysis_Planes_Clear_Click); + // + // btn_Analysis_Planes_Save + // + this.btn_Analysis_Planes_Save.Enabled = false; + this.btn_Analysis_Planes_Save.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Analysis_Planes_Save.Location = new System.Drawing.Point(8, 50); + this.btn_Analysis_Planes_Save.Name = "btn_Analysis_Planes_Save"; + this.btn_Analysis_Planes_Save.Size = new System.Drawing.Size(128, 23); + this.btn_Analysis_Planes_Save.TabIndex = 33; + this.btn_Analysis_Planes_Save.Text = "Save Positions to File"; + this.btn_Analysis_Planes_Save.UseVisualStyleBackColor = true; + this.btn_Analysis_Planes_Save.Click += new System.EventHandler(this.btn_Analysis_Planes_Save_Click); + // + // btn_Analysis_Planes_Load + // + this.btn_Analysis_Planes_Load.Enabled = false; + this.btn_Analysis_Planes_Load.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Analysis_Planes_Load.Location = new System.Drawing.Point(8, 21); + this.btn_Analysis_Planes_Load.Name = "btn_Analysis_Planes_Load"; + this.btn_Analysis_Planes_Load.Size = new System.Drawing.Size(128, 23); + this.btn_Analysis_Planes_Load.TabIndex = 32; + this.btn_Analysis_Planes_Load.Text = "Load Positions from File"; + this.btn_Analysis_Planes_Load.UseVisualStyleBackColor = true; + this.btn_Analysis_Planes_Load.Click += new System.EventHandler(this.btn_Analysis_Planes_Load_Click); + // + // gb_Analysis_Mode + // + this.gb_Analysis_Mode.Controls.Add(this.btn_Analysis_OFF); + this.gb_Analysis_Mode.Controls.Add(this.btn_Analysis_ON); + this.gb_Analysis_Mode.Dock = System.Windows.Forms.DockStyle.Left; + this.gb_Analysis_Mode.Location = new System.Drawing.Point(0, 0); + this.gb_Analysis_Mode.Name = "gb_Analysis_Mode"; + this.gb_Analysis_Mode.Size = new System.Drawing.Size(78, 137); + this.gb_Analysis_Mode.TabIndex = 1; + this.gb_Analysis_Mode.TabStop = false; + this.gb_Analysis_Mode.Text = "Analysis"; + // + // btn_Analysis_OFF + // + this.btn_Analysis_OFF.BackColor = System.Drawing.Color.LightCoral; + this.btn_Analysis_OFF.Enabled = false; + this.btn_Analysis_OFF.Location = new System.Drawing.Point(8, 76); + this.btn_Analysis_OFF.Name = "btn_Analysis_OFF"; + this.btn_Analysis_OFF.Size = new System.Drawing.Size(64, 52); + this.btn_Analysis_OFF.TabIndex = 1; + this.btn_Analysis_OFF.Text = "OFF"; + this.btn_Analysis_OFF.UseVisualStyleBackColor = false; + this.btn_Analysis_OFF.Click += new System.EventHandler(this.btn_Analysis_OFF_Click); + // + // btn_Analysis_ON + // + this.btn_Analysis_ON.BackColor = System.Drawing.Color.YellowGreen; + this.btn_Analysis_ON.Enabled = false; + this.btn_Analysis_ON.Location = new System.Drawing.Point(8, 20); + this.btn_Analysis_ON.Name = "btn_Analysis_ON"; + this.btn_Analysis_ON.Size = new System.Drawing.Size(64, 52); + this.btn_Analysis_ON.TabIndex = 0; + this.btn_Analysis_ON.Text = "ON"; + this.btn_Analysis_ON.UseVisualStyleBackColor = false; + this.btn_Analysis_ON.Click += new System.EventHandler(this.btn_Analysis_ON_Click); + // + // gb_Analysis_Time + // + this.gb_Analysis_Time.Controls.Add(this.label15); + this.gb_Analysis_Time.Controls.Add(this.label14); + this.gb_Analysis_Time.Controls.Add(this.tb_Analysis_Stepwidth); + this.gb_Analysis_Time.Controls.Add(this.tb_Analysis_Time); + this.gb_Analysis_Time.Dock = System.Windows.Forms.DockStyle.Right; + this.gb_Analysis_Time.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.gb_Analysis_Time.Location = new System.Drawing.Point(591, 0); + this.gb_Analysis_Time.Name = "gb_Analysis_Time"; + this.gb_Analysis_Time.Size = new System.Drawing.Size(253, 137); + this.gb_Analysis_Time.TabIndex = 3; + this.gb_Analysis_Time.TabStop = false; + this.gb_Analysis_Time.Text = "Time"; + // + // label15 + // + this.label15.AutoSize = true; + this.label15.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label15.Location = new System.Drawing.Point(210, 85); + this.label15.Name = "label15"; + this.label15.Size = new System.Drawing.Size(27, 13); + this.label15.TabIndex = 3; + this.label15.Text = "sec."; + // + // label14 + // + this.label14.AutoSize = true; + this.label14.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label14.Location = new System.Drawing.Point(7, 84); + this.label14.Name = "label14"; + this.label14.Size = new System.Drawing.Size(57, 13); + this.label14.TabIndex = 2; + this.label14.Text = "Stepwidth:"; + // + // tb_Analysis_Stepwidth + // + this.tb_Analysis_Stepwidth.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Analysis_Stepwidth.Location = new System.Drawing.Point(84, 82); + this.tb_Analysis_Stepwidth.Name = "tb_Analysis_Stepwidth"; + this.tb_Analysis_Stepwidth.ReadOnly = true; + this.tb_Analysis_Stepwidth.Size = new System.Drawing.Size(96, 20); + this.tb_Analysis_Stepwidth.TabIndex = 1; + this.tb_Analysis_Stepwidth.Text = "0"; + // + // tb_Analysis_Time + // + this.tb_Analysis_Time.BackColor = System.Drawing.Color.Black; + this.tb_Analysis_Time.Font = new System.Drawing.Font("Arial", 18F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Analysis_Time.ForeColor = System.Drawing.Color.Chartreuse; + this.tb_Analysis_Time.Location = new System.Drawing.Point(6, 27); + this.tb_Analysis_Time.Name = "tb_Analysis_Time"; + this.tb_Analysis_Time.ReadOnly = true; + this.tb_Analysis_Time.Size = new System.Drawing.Size(242, 35); + this.tb_Analysis_Time.TabIndex = 0; + this.tb_Analysis_Time.Text = "0000-00-00 00:00:00"; + this.tb_Analysis_Time.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; + // + // tp_News + // + this.tp_News.Location = new System.Drawing.Point(4, 22); + this.tp_News.Name = "tp_News"; + this.tp_News.Size = new System.Drawing.Size(844, 199); + this.tp_News.TabIndex = 2; + this.tp_News.Text = "Latest News"; + this.tp_News.UseVisualStyleBackColor = true; + this.tp_News.Enter += new System.EventHandler(this.tp_News_Enter); + // + // ss_Main + // + this.ss_Main.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.tsl_Status, + this.tsl_Dummy, + this.tsl_Calculations, + this.tsl_Database, + this.tsl_Database_LED_Aircraft, + this.tsl_Database_LED_Stations, + this.tsl_Database_LED_GLOBE, + this.tsl_Database_LED_SRTM3, + this.tsl_Database_LED_SRTM1}); + this.ss_Main.Location = new System.Drawing.Point(0, 706); + this.ss_Main.Name = "ss_Main"; + this.ss_Main.ShowItemToolTips = true; + this.ss_Main.Size = new System.Drawing.Size(1008, 24); + this.ss_Main.TabIndex = 22; + this.ss_Main.Text = "ss_Main"; + // + // tsl_Status + // + this.tsl_Status.BorderSides = ((System.Windows.Forms.ToolStripStatusLabelBorderSides)((((System.Windows.Forms.ToolStripStatusLabelBorderSides.Left | System.Windows.Forms.ToolStripStatusLabelBorderSides.Top) + | System.Windows.Forms.ToolStripStatusLabelBorderSides.Right) + | System.Windows.Forms.ToolStripStatusLabelBorderSides.Bottom))); + this.tsl_Status.BorderStyle = System.Windows.Forms.Border3DStyle.SunkenInner; + this.tsl_Status.Name = "tsl_Status"; + this.tsl_Status.Size = new System.Drawing.Size(742, 19); + this.tsl_Status.Spring = true; + this.tsl_Status.Text = "No Messages."; + this.tsl_Status.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // tsl_Dummy + // + this.tsl_Dummy.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; + this.tsl_Dummy.Name = "tsl_Dummy"; + this.tsl_Dummy.Size = new System.Drawing.Size(0, 19); + this.tsl_Dummy.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // tsl_Calculations + // + this.tsl_Calculations.BorderSides = ((System.Windows.Forms.ToolStripStatusLabelBorderSides)((((System.Windows.Forms.ToolStripStatusLabelBorderSides.Left | System.Windows.Forms.ToolStripStatusLabelBorderSides.Top) + | System.Windows.Forms.ToolStripStatusLabelBorderSides.Right) + | System.Windows.Forms.ToolStripStatusLabelBorderSides.Bottom))); + this.tsl_Calculations.BorderStyle = System.Windows.Forms.Border3DStyle.SunkenInner; + this.tsl_Calculations.Name = "tsl_Calculations"; + this.tsl_Calculations.Size = new System.Drawing.Size(79, 19); + this.tsl_Calculations.Text = "Calculations."; + // + // tsl_Database + // + this.tsl_Database.BorderSides = ((System.Windows.Forms.ToolStripStatusLabelBorderSides)((((System.Windows.Forms.ToolStripStatusLabelBorderSides.Left | System.Windows.Forms.ToolStripStatusLabelBorderSides.Top) + | System.Windows.Forms.ToolStripStatusLabelBorderSides.Right) + | System.Windows.Forms.ToolStripStatusLabelBorderSides.Bottom))); + this.tsl_Database.BorderStyle = System.Windows.Forms.Border3DStyle.SunkenInner; + this.tsl_Database.Name = "tsl_Database"; + this.tsl_Database.Size = new System.Drawing.Size(96, 19); + this.tsl_Database.Text = "Database status."; + // + // tsl_Database_LED_Aircraft + // + this.tsl_Database_LED_Aircraft.AutoSize = false; + this.tsl_Database_LED_Aircraft.BackColor = System.Drawing.Color.Plum; + this.tsl_Database_LED_Aircraft.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.None; + this.tsl_Database_LED_Aircraft.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tsl_Database_LED_Aircraft.Image = ((System.Drawing.Image)(resources.GetObject("tsl_Database_LED_Aircraft.Image"))); + this.tsl_Database_LED_Aircraft.Margin = new System.Windows.Forms.Padding(7, 5, 1, 5); + this.tsl_Database_LED_Aircraft.Name = "tsl_Database_LED_Aircraft"; + this.tsl_Database_LED_Aircraft.Size = new System.Drawing.Size(12, 14); + this.tsl_Database_LED_Aircraft.Text = "Aircraft database status LED"; + this.tsl_Database_LED_Aircraft.ToolTipText = "Aircraft database status LED"; + // + // tsl_Database_LED_Stations + // + this.tsl_Database_LED_Stations.AutoSize = false; + this.tsl_Database_LED_Stations.BackColor = System.Drawing.Color.Plum; + this.tsl_Database_LED_Stations.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.None; + this.tsl_Database_LED_Stations.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tsl_Database_LED_Stations.Image = ((System.Drawing.Image)(resources.GetObject("tsl_Database_LED_Stations.Image"))); + this.tsl_Database_LED_Stations.Margin = new System.Windows.Forms.Padding(1, 5, 1, 5); + this.tsl_Database_LED_Stations.Name = "tsl_Database_LED_Stations"; + this.tsl_Database_LED_Stations.Size = new System.Drawing.Size(12, 14); + this.tsl_Database_LED_Stations.Text = " Station database status LED"; + this.tsl_Database_LED_Stations.ToolTipText = "Station database status LED"; + // + // tsl_Database_LED_GLOBE + // + this.tsl_Database_LED_GLOBE.AutoSize = false; + this.tsl_Database_LED_GLOBE.BackColor = System.Drawing.Color.Plum; + this.tsl_Database_LED_GLOBE.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.None; + this.tsl_Database_LED_GLOBE.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tsl_Database_LED_GLOBE.Image = ((System.Drawing.Image)(resources.GetObject("tsl_Database_LED_GLOBE.Image"))); + this.tsl_Database_LED_GLOBE.Margin = new System.Windows.Forms.Padding(1, 5, 1, 5); + this.tsl_Database_LED_GLOBE.Name = "tsl_Database_LED_GLOBE"; + this.tsl_Database_LED_GLOBE.Size = new System.Drawing.Size(12, 14); + this.tsl_Database_LED_GLOBE.Text = " GLOBE database status LED"; + this.tsl_Database_LED_GLOBE.ToolTipText = "GLOBE database status LED"; + // + // tsl_Database_LED_SRTM3 + // + this.tsl_Database_LED_SRTM3.AutoSize = false; + this.tsl_Database_LED_SRTM3.BackColor = System.Drawing.Color.Plum; + this.tsl_Database_LED_SRTM3.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.None; + this.tsl_Database_LED_SRTM3.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tsl_Database_LED_SRTM3.Image = ((System.Drawing.Image)(resources.GetObject("tsl_Database_LED_SRTM3.Image"))); + this.tsl_Database_LED_SRTM3.Margin = new System.Windows.Forms.Padding(1, 5, 1, 5); + this.tsl_Database_LED_SRTM3.Name = "tsl_Database_LED_SRTM3"; + this.tsl_Database_LED_SRTM3.Size = new System.Drawing.Size(12, 14); + this.tsl_Database_LED_SRTM3.Text = " SRTM3 database status LED"; + this.tsl_Database_LED_SRTM3.ToolTipText = "SRTM3 database status LED"; + // + // tsl_Database_LED_SRTM1 + // + this.tsl_Database_LED_SRTM1.AutoSize = false; + this.tsl_Database_LED_SRTM1.BackColor = System.Drawing.Color.Plum; + this.tsl_Database_LED_SRTM1.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.None; + this.tsl_Database_LED_SRTM1.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tsl_Database_LED_SRTM1.Image = ((System.Drawing.Image)(resources.GetObject("tsl_Database_LED_SRTM1.Image"))); + this.tsl_Database_LED_SRTM1.Margin = new System.Windows.Forms.Padding(1, 5, 1, 5); + this.tsl_Database_LED_SRTM1.Name = "tsl_Database_LED_SRTM1"; + this.tsl_Database_LED_SRTM1.Size = new System.Drawing.Size(12, 14); + this.tsl_Database_LED_SRTM1.Text = " SRTM3 database status LED"; + this.tsl_Database_LED_SRTM1.ToolTipText = "SRTM1 database status LED"; + // + // btn_Map_PlayPause + // + this.btn_Map_PlayPause.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.btn_Map_PlayPause.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Map_PlayPause.ImageIndex = 1; + this.btn_Map_PlayPause.ImageList = this.il_Main; + this.btn_Map_PlayPause.Location = new System.Drawing.Point(20, 88); + this.btn_Map_PlayPause.Name = "btn_Map_PlayPause"; + this.btn_Map_PlayPause.Size = new System.Drawing.Size(114, 29); + this.btn_Map_PlayPause.TabIndex = 15; + this.btn_Map_PlayPause.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.tt_Main.SetToolTip(this.btn_Map_PlayPause, "Press button to toggle betwwen Play and Pause.\r\nIf playing, aircafts are shown on" + + " the map in real time.\r\nIf paused, calls and options can be set."); + this.btn_Map_PlayPause.UseVisualStyleBackColor = true; + this.btn_Map_PlayPause.Click += new System.EventHandler(this.btn_Map_PlayPause_Click); + // + // btn_Map_Save + // + this.btn_Map_Save.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.btn_Map_Save.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Map_Save.Location = new System.Drawing.Point(19, 53); + this.btn_Map_Save.Name = "btn_Map_Save"; + this.btn_Map_Save.Size = new System.Drawing.Size(114, 29); + this.btn_Map_Save.TabIndex = 14; + this.btn_Map_Save.Text = "&Save"; + this.tt_Main.SetToolTip(this.btn_Map_Save, "Press button to save a hordcopy of the current main window.\r\nThe file is saved in" + + " the program\'s temp directory."); + this.btn_Map_Save.UseVisualStyleBackColor = true; + this.btn_Map_Save.Click += new System.EventHandler(this.btn_Map_Save_Click); + // + // btn_Options + // + this.btn_Options.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.btn_Options.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Options.Location = new System.Drawing.Point(19, 18); + this.btn_Options.Name = "btn_Options"; + this.btn_Options.Size = new System.Drawing.Size(114, 29); + this.btn_Options.TabIndex = 16; + this.btn_Options.Text = "&Options"; + this.tt_Main.SetToolTip(this.btn_Options, "Press button to change program options."); + this.btn_Options.UseVisualStyleBackColor = true; + this.btn_Options.Click += new System.EventHandler(this.btn_Options_Click); + // + // cb_Band + // + this.cb_Band.AllowDrop = true; + this.cb_Band.BackColor = System.Drawing.Color.FloralWhite; + this.cb_Band.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cb_Band.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Band.FormattingEnabled = true; + this.cb_Band.Location = new System.Drawing.Point(8, 60); + this.cb_Band.Name = "cb_Band"; + this.cb_Band.Size = new System.Drawing.Size(130, 24); + this.cb_Band.TabIndex = 16; + this.tt_Main.SetToolTip(this.cb_Band, "Select the current band here.\r\nThis is used to calculate the Fresnel Zone F1 clea" + + "rance."); + this.cb_Band.SelectedIndexChanged += new System.EventHandler(this.cb_Band_SelectedIndexChanged); + // + // tb_QTF + // + this.tb_QTF.BackColor = System.Drawing.Color.FloralWhite; + this.tb_QTF.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_QTF.Location = new System.Drawing.Point(3, 242); + this.tb_QTF.Name = "tb_QTF"; + this.tb_QTF.ReadOnly = true; + this.tb_QTF.Size = new System.Drawing.Size(133, 22); + this.tb_QTF.TabIndex = 11; + this.tt_Main.SetToolTip(this.tb_QTF, "The bearing to your QSO partner."); + // + // tb_QRB + // + this.tb_QRB.BackColor = System.Drawing.Color.FloralWhite; + this.tb_QRB.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_QRB.Location = new System.Drawing.Point(3, 198); + this.tb_QRB.Name = "tb_QRB"; + this.tb_QRB.ReadOnly = true; + this.tb_QRB.Size = new System.Drawing.Size(133, 22); + this.tb_QRB.TabIndex = 9; + this.tt_Main.SetToolTip(this.tb_QRB, "The distance between both QSO partners."); + // + // gb_Map_Alarms + // + this.gb_Map_Alarms.Controls.Add(this.cb_Alarms_Activate); + this.gb_Map_Alarms.Dock = System.Windows.Forms.DockStyle.Top; + this.gb_Map_Alarms.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.gb_Map_Alarms.Location = new System.Drawing.Point(0, 110); + this.gb_Map_Alarms.Name = "gb_Map_Alarms"; + this.gb_Map_Alarms.Size = new System.Drawing.Size(143, 47); + this.gb_Map_Alarms.TabIndex = 62; + this.gb_Map_Alarms.TabStop = false; + this.gb_Map_Alarms.Text = "Alarms"; + this.tt_Main.SetToolTip(this.gb_Map_Alarms, "Left Click on Title to show/hide box"); + // + // cb_Alarms_Activate + // + this.cb_Alarms_Activate.AutoSize = true; + this.cb_Alarms_Activate.Checked = global::AirScout.Properties.Settings.Default.Alarm_Activate; + this.cb_Alarms_Activate.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Alarm_Activate", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Alarms_Activate.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Alarms_Activate.Location = new System.Drawing.Point(14, 19); + this.cb_Alarms_Activate.Name = "cb_Alarms_Activate"; + this.cb_Alarms_Activate.Size = new System.Drawing.Size(99, 17); + this.cb_Alarms_Activate.TabIndex = 0; + this.cb_Alarms_Activate.Text = "Activate Alarms"; + this.cb_Alarms_Activate.UseVisualStyleBackColor = true; + this.cb_Alarms_Activate.CheckedChanged += new System.EventHandler(this.cb_Alarms_Activate_CheckedChanged); + // + // gb_Map_Zoom + // + this.gb_Map_Zoom.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.gb_Map_Zoom.Controls.Add(this.pa_Map_Zoom); + this.gb_Map_Zoom.Dock = System.Windows.Forms.DockStyle.Bottom; + this.gb_Map_Zoom.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.gb_Map_Zoom.Location = new System.Drawing.Point(0, 504); + this.gb_Map_Zoom.Name = "gb_Map_Zoom"; + this.gb_Map_Zoom.Size = new System.Drawing.Size(152, 79); + this.gb_Map_Zoom.TabIndex = 60; + this.gb_Map_Zoom.TabStop = false; + this.gb_Map_Zoom.Text = "Map Zoom"; + this.tt_Main.SetToolTip(this.gb_Map_Zoom, "Left Click on Title to show/hide box"); + // + // pa_Map_Zoom + // + this.pa_Map_Zoom.Controls.Add(this.cb_AutoCenter); + this.pa_Map_Zoom.Controls.Add(this.tb_Zoom); + this.pa_Map_Zoom.Controls.Add(this.btn_Zoom_Out); + this.pa_Map_Zoom.Controls.Add(this.btn_Zoom_In); + this.pa_Map_Zoom.Location = new System.Drawing.Point(3, 16); + this.pa_Map_Zoom.Name = "pa_Map_Zoom"; + this.pa_Map_Zoom.Size = new System.Drawing.Size(146, 60); + this.pa_Map_Zoom.TabIndex = 0; + // + // cb_AutoCenter + // + this.cb_AutoCenter.AutoSize = true; + this.cb_AutoCenter.Checked = global::AirScout.Properties.Settings.Default.Map_AutoCenter; + this.cb_AutoCenter.CheckState = System.Windows.Forms.CheckState.Checked; + this.cb_AutoCenter.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Map_AutoCenter", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_AutoCenter.Location = new System.Drawing.Point(20, 37); + this.cb_AutoCenter.Name = "cb_AutoCenter"; + this.cb_AutoCenter.Size = new System.Drawing.Size(93, 17); + this.cb_AutoCenter.TabIndex = 24; + this.cb_AutoCenter.Text = "Auto Center"; + this.cb_AutoCenter.UseVisualStyleBackColor = true; + // + // tb_Zoom + // + this.tb_Zoom.BackColor = System.Drawing.Color.FloralWhite; + this.tb_Zoom.Location = new System.Drawing.Point(53, 9); + this.tb_Zoom.Name = "tb_Zoom"; + this.tb_Zoom.Size = new System.Drawing.Size(41, 20); + this.tb_Zoom.TabIndex = 23; + // + // btn_Zoom_Out + // + this.btn_Zoom_Out.Location = new System.Drawing.Point(100, 8); + this.btn_Zoom_Out.Name = "btn_Zoom_Out"; + this.btn_Zoom_Out.Size = new System.Drawing.Size(30, 23); + this.btn_Zoom_Out.TabIndex = 22; + this.btn_Zoom_Out.Text = "-"; + this.btn_Zoom_Out.UseVisualStyleBackColor = true; + this.btn_Zoom_Out.Click += new System.EventHandler(this.btn_Zoom_Out_Click); + // + // btn_Zoom_In + // + this.btn_Zoom_In.Location = new System.Drawing.Point(17, 8); + this.btn_Zoom_In.Name = "btn_Zoom_In"; + this.btn_Zoom_In.Size = new System.Drawing.Size(30, 23); + this.btn_Zoom_In.TabIndex = 21; + this.btn_Zoom_In.Text = "+"; + this.btn_Zoom_In.UseVisualStyleBackColor = true; + this.btn_Zoom_In.Click += new System.EventHandler(this.btn_Zoom_In_Click); + // + // gb_Map_Filter + // + this.gb_Map_Filter.Controls.Add(this.pa_Planes_Filter); + this.gb_Map_Filter.Dock = System.Windows.Forms.DockStyle.Top; + this.gb_Map_Filter.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.gb_Map_Filter.Location = new System.Drawing.Point(0, 0); + this.gb_Map_Filter.Name = "gb_Map_Filter"; + this.gb_Map_Filter.Size = new System.Drawing.Size(143, 110); + this.gb_Map_Filter.TabIndex = 61; + this.gb_Map_Filter.TabStop = false; + this.gb_Map_Filter.Text = "Planes Filter"; + this.tt_Main.SetToolTip(this.gb_Map_Filter, "Left Click on Title to show/hide box"); + // + // pa_Planes_Filter + // + this.pa_Planes_Filter.Controls.Add(this.tb_Planes_Filter_Min_Alt); + this.pa_Planes_Filter.Controls.Add(this.tb_Planes_Filter_Max_Circumcircle); + this.pa_Planes_Filter.Controls.Add(this.label12); + this.pa_Planes_Filter.Controls.Add(this.label13); + this.pa_Planes_Filter.Controls.Add(this.cb_Planes_Filter_Min_Cat); + this.pa_Planes_Filter.Controls.Add(this.label11); + this.pa_Planes_Filter.Controls.Add(this.label10); + this.pa_Planes_Filter.Controls.Add(this.label9); + this.pa_Planes_Filter.Location = new System.Drawing.Point(3, 16); + this.pa_Planes_Filter.Name = "pa_Planes_Filter"; + this.pa_Planes_Filter.Size = new System.Drawing.Size(146, 91); + this.pa_Planes_Filter.TabIndex = 0; + // + // tb_Planes_Filter_Min_Alt + // + this.tb_Planes_Filter_Min_Alt.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScout.Properties.Settings.Default, "Planes_Filter_Min_Alt", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Planes_Filter_Min_Alt.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Planes_Filter_Min_Alt.FormatSpecifier = "F0"; + this.tb_Planes_Filter_Min_Alt.Location = new System.Drawing.Point(59, 34); + this.tb_Planes_Filter_Min_Alt.MaxValue = 12000; + this.tb_Planes_Filter_Min_Alt.MinValue = 0; + this.tb_Planes_Filter_Min_Alt.Name = "tb_Planes_Filter_Min_Alt"; + this.tb_Planes_Filter_Min_Alt.Size = new System.Drawing.Size(57, 22); + this.tb_Planes_Filter_Min_Alt.TabIndex = 33; + this.tb_Planes_Filter_Min_Alt.Text = "0"; + this.tt_Main.SetToolTip(this.tb_Planes_Filter_Min_Alt, "Set the minimum aircraft altitude here."); + this.tb_Planes_Filter_Min_Alt.Value = global::AirScout.Properties.Settings.Default.Planes_Filter_Min_Alt; + // + // tb_Planes_Filter_Max_Circumcircle + // + this.tb_Planes_Filter_Max_Circumcircle.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScout.Properties.Settings.Default, "Planes_Filter_Max_Circumcircle", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Planes_Filter_Max_Circumcircle.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Planes_Filter_Max_Circumcircle.FormatSpecifier = "F0"; + this.tb_Planes_Filter_Max_Circumcircle.Location = new System.Drawing.Point(59, 8); + this.tb_Planes_Filter_Max_Circumcircle.MaxValue = 1000; + this.tb_Planes_Filter_Max_Circumcircle.MinValue = -1; + this.tb_Planes_Filter_Max_Circumcircle.Name = "tb_Planes_Filter_Max_Circumcircle"; + this.tb_Planes_Filter_Max_Circumcircle.Size = new System.Drawing.Size(57, 22); + this.tb_Planes_Filter_Max_Circumcircle.TabIndex = 32; + this.tb_Planes_Filter_Max_Circumcircle.Text = "0"; + this.tt_Main.SetToolTip(this.tb_Planes_Filter_Max_Circumcircle, "Set the maximum path midpoint circumcircle here."); + this.tb_Planes_Filter_Max_Circumcircle.Value = global::AirScout.Properties.Settings.Default.Planes_Filter_Max_Circumcircle; + // + // label12 + // + this.label12.AutoSize = true; + this.label12.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label12.Location = new System.Drawing.Point(121, 11); + this.label12.Name = "label12"; + this.label12.Size = new System.Drawing.Size(21, 13); + this.label12.TabIndex = 31; + this.label12.Text = "km"; + // + // label13 + // + this.label13.AutoSize = true; + this.label13.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label13.Location = new System.Drawing.Point(5, 11); + this.label13.Name = "label13"; + this.label13.Size = new System.Drawing.Size(57, 13); + this.label13.TabIndex = 30; + this.label13.Text = "Max. Circ.:"; + // + // cb_Planes_Filter_Min_Cat + // + this.cb_Planes_Filter_Min_Cat.AllowDrop = true; + this.cb_Planes_Filter_Min_Cat.BackColor = System.Drawing.Color.FloralWhite; + this.cb_Planes_Filter_Min_Cat.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cb_Planes_Filter_Min_Cat.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Planes_Filter_Min_Cat.FormattingEnabled = true; + this.cb_Planes_Filter_Min_Cat.Items.AddRange(new object[] { + "Light", + "Medium", + "Heavy", + "Super"}); + this.cb_Planes_Filter_Min_Cat.Location = new System.Drawing.Point(59, 60); + this.cb_Planes_Filter_Min_Cat.Name = "cb_Planes_Filter_Min_Cat"; + this.cb_Planes_Filter_Min_Cat.Size = new System.Drawing.Size(77, 24); + this.cb_Planes_Filter_Min_Cat.TabIndex = 28; + this.tt_Main.SetToolTip(this.cb_Planes_Filter_Min_Cat, "Set the minimum category (wake) here. \r\nSo far there are 4 categories:\r\n\r\n(L)ight" + + "\r\n(M)edium\r\n(H)eavy\r\n(Super)\r\n\r\nPlanes with lower categories than selected are n" + + "ot shown on the map."); + this.cb_Planes_Filter_Min_Cat.SelectedIndexChanged += new System.EventHandler(this.cb_Planes_Filter_Min_Category_SelectedIndexChanged); + // + // label11 + // + this.label11.AutoSize = true; + this.label11.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label11.Location = new System.Drawing.Point(4, 64); + this.label11.Name = "label11"; + this.label11.Size = new System.Drawing.Size(49, 13); + this.label11.TabIndex = 27; + this.label11.Text = "Min. Cat:"; + // + // label10 + // + this.label10.AutoSize = true; + this.label10.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label10.Location = new System.Drawing.Point(119, 37); + this.label10.Name = "label10"; + this.label10.Size = new System.Drawing.Size(15, 13); + this.label10.TabIndex = 26; + this.label10.Text = "m"; + // + // label9 + // + this.label9.AutoSize = true; + this.label9.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label9.Location = new System.Drawing.Point(3, 37); + this.label9.Name = "label9"; + this.label9.Size = new System.Drawing.Size(48, 13); + this.label9.TabIndex = 25; + this.label9.Text = "Min. Alt.:"; + // + // il_Sat + // + this.il_Sat.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("il_Sat.ImageStream"))); + this.il_Sat.TransparentColor = System.Drawing.Color.Transparent; + this.il_Sat.Images.SetKeyName(0, "ISS.png"); + // + // bw_WinTestReceive + // + this.bw_WinTestReceive.WorkerReportsProgress = true; + this.bw_WinTestReceive.WorkerSupportsCancellation = true; + this.bw_WinTestReceive.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bw_WinTestReceive_DoWork); + this.bw_WinTestReceive.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.bw_WinTestReceive_ProgressChanged); + this.bw_WinTestReceive.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.bw_WinTestReceive_RunWorkerCompleted); + // + // il_Planes_M + // + this.il_Planes_M.ColorDepth = System.Windows.Forms.ColorDepth.Depth24Bit; + this.il_Planes_M.ImageSize = new System.Drawing.Size(24, 24); + this.il_Planes_M.TransparentColor = System.Drawing.Color.Transparent; + // + // bw_SpecLab_Receive + // + this.bw_SpecLab_Receive.WorkerReportsProgress = true; + this.bw_SpecLab_Receive.WorkerSupportsCancellation = true; + this.bw_SpecLab_Receive.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bw_SpecLab_Receive_DoWork); + this.bw_SpecLab_Receive.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.bw_SpecLab_Receive_ProgressChanged); + this.bw_SpecLab_Receive.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.bw_SpecLab_Receive_RunWorkerCompleted); + // + // sc_Main + // + this.sc_Main.BackColor = System.Drawing.Color.LightGray; + this.sc_Main.Dock = System.Windows.Forms.DockStyle.Fill; + this.sc_Main.Location = new System.Drawing.Point(0, 0); + this.sc_Main.Name = "sc_Main"; + // + // sc_Main.Panel1 + // + this.sc_Main.Panel1.Controls.Add(this.sc_Map); + // + // sc_Main.Panel2 + // + this.sc_Main.Panel2.BackColor = System.Drawing.SystemColors.Control; + this.sc_Main.Panel2.Controls.Add(this.tc_Control); + this.sc_Main.Panel2.Controls.Add(this.gb_Map_Zoom); + this.sc_Main.Panel2.Controls.Add(this.pa_CommonInfo); + this.sc_Main.Panel2.Controls.Add(this.gb_Map_Buttons); + this.sc_Main.Panel2MinSize = 152; + this.sc_Main.Size = new System.Drawing.Size(1008, 706); + this.sc_Main.SplitterDistance = 852; + this.sc_Main.SplitterWidth = 5; + this.sc_Main.TabIndex = 24; + this.sc_Main.SplitterMoved += new System.Windows.Forms.SplitterEventHandler(this.sc_Main_SplitterMoved); + // + // tc_Control + // + this.tc_Control.Controls.Add(this.tp_Control_Single); + this.tc_Control.Controls.Add(this.tp_Control_Multi); + this.tc_Control.Controls.Add(this.tp_Control_Options); + this.tc_Control.Dock = System.Windows.Forms.DockStyle.Fill; + this.tc_Control.DrawMode = System.Windows.Forms.TabDrawMode.OwnerDrawFixed; + this.tc_Control.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tc_Control.Location = new System.Drawing.Point(0, 100); + this.tc_Control.Name = "tc_Control"; + this.tc_Control.SelectedIndex = 0; + this.tc_Control.Size = new System.Drawing.Size(152, 404); + this.tc_Control.TabIndex = 60; + this.tc_Control.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.tc_Control_DrawItem); + // + // tp_Control_Single + // + this.tp_Control_Single.BackColor = System.Drawing.SystemColors.Control; + this.tp_Control_Single.Controls.Add(this.gb_Map_Info); + this.tp_Control_Single.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tp_Control_Single.Location = new System.Drawing.Point(4, 22); + this.tp_Control_Single.Name = "tp_Control_Single"; + this.tp_Control_Single.Padding = new System.Windows.Forms.Padding(3); + this.tp_Control_Single.Size = new System.Drawing.Size(144, 378); + this.tp_Control_Single.TabIndex = 0; + this.tp_Control_Single.Text = "Single"; + this.tp_Control_Single.Enter += new System.EventHandler(this.tp_Control_Single_Enter); + // + // gb_Map_Info + // + this.gb_Map_Info.Controls.Add(this.cb_DXCall); + this.gb_Map_Info.Controls.Add(this.cb_DXLoc); + this.gb_Map_Info.Controls.Add(this.cb_MyLoc); + this.gb_Map_Info.Controls.Add(this.cb_MyCall); + this.gb_Map_Info.Controls.Add(this.tb_QTF); + this.gb_Map_Info.Controls.Add(this.label6); + this.gb_Map_Info.Controls.Add(this.tb_QRB); + this.gb_Map_Info.Controls.Add(this.label16); + this.gb_Map_Info.Controls.Add(this.label17); + this.gb_Map_Info.Controls.Add(this.label18); + this.gb_Map_Info.Controls.Add(this.label19); + this.gb_Map_Info.Controls.Add(this.label20); + this.gb_Map_Info.Dock = System.Windows.Forms.DockStyle.Top; + this.gb_Map_Info.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.gb_Map_Info.Location = new System.Drawing.Point(3, 3); + this.gb_Map_Info.Name = "gb_Map_Info"; + this.gb_Map_Info.Size = new System.Drawing.Size(138, 275); + this.gb_Map_Info.TabIndex = 58; + this.gb_Map_Info.TabStop = false; + this.gb_Map_Info.Text = "Info"; + // + // cb_DXCall + // + this.cb_DXCall.BackColor = System.Drawing.SystemColors.Window; + this.cb_DXCall.CharacterCasing = System.Windows.Forms.CharacterCasing.Upper; + this.cb_DXCall.ErrorBackColor = System.Drawing.Color.Red; + this.cb_DXCall.ErrorForeColor = System.Drawing.Color.White; + this.cb_DXCall.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_DXCall.ForeColor = System.Drawing.SystemColors.WindowText; + this.cb_DXCall.FormattingEnabled = true; + this.cb_DXCall.Location = new System.Drawing.Point(3, 113); + this.cb_DXCall.Name = "cb_DXCall"; + this.cb_DXCall.Size = new System.Drawing.Size(133, 24); + this.cb_DXCall.Sorted = true; + this.cb_DXCall.TabIndex = 24; + this.cb_DXCall.DropDown += new System.EventHandler(this.cb_DXCall_DropDown); + this.cb_DXCall.SelectedIndexChanged += new System.EventHandler(this.cb_DXCall_SelectedIndexChanged); + this.cb_DXCall.TextChanged += new System.EventHandler(this.cb_DXCall_TextChanged); + // + // cb_DXLoc + // + this.cb_DXLoc.AutoLength = false; + this.cb_DXLoc.BackColor = System.Drawing.SystemColors.Window; + this.cb_DXLoc.CharacterCasing = System.Windows.Forms.CharacterCasing.Normal; + this.cb_DXLoc.DataBindings.Add(new System.Windows.Forms.Binding("SmallLettersForSubsquares", global::AirScout.Properties.Settings.Default, "Locator_SmallLettersForSubsquares", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_DXLoc.ErrorBackColor = System.Drawing.Color.Red; + this.cb_DXLoc.ErrorForeColor = System.Drawing.Color.White; + this.cb_DXLoc.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_DXLoc.ForeColor = System.Drawing.SystemColors.WindowText; + this.cb_DXLoc.FormattingEnabled = true; + this.cb_DXLoc.GeoLocation = gPoint5; + this.cb_DXLoc.Location = new System.Drawing.Point(3, 154); + this.cb_DXLoc.Name = "cb_DXLoc"; + this.cb_DXLoc.Precision = 3; + this.cb_DXLoc.SilentItemChange = true; + this.cb_DXLoc.Size = new System.Drawing.Size(133, 24); + this.cb_DXLoc.SmallLettersForSubsquares = global::AirScout.Properties.Settings.Default.Locator_SmallLettersForSubsquares; + this.cb_DXLoc.TabIndex = 23; + this.cb_DXLoc.DropDown += new System.EventHandler(this.cb_DXLoc_DropDown); + this.cb_DXLoc.SelectedIndexChanged += new System.EventHandler(this.cb_DXLoc_SelectedIndexChanged); + this.cb_DXLoc.SelectionChangeCommitted += new System.EventHandler(this.cb_DXLoc_SelectionChangeCommittedWithNoUpdate); + this.cb_DXLoc.TextChanged += new System.EventHandler(this.cb_DXLoc_TextChanged); + // + // cb_MyLoc + // + this.cb_MyLoc.AutoLength = false; + this.cb_MyLoc.BackColor = System.Drawing.SystemColors.Window; + this.cb_MyLoc.CharacterCasing = System.Windows.Forms.CharacterCasing.Normal; + this.cb_MyLoc.DataBindings.Add(new System.Windows.Forms.Binding("SmallLettersForSubsquares", global::AirScout.Properties.Settings.Default, "Locator_SmallLettersForSubsquares", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_MyLoc.ErrorBackColor = System.Drawing.Color.Red; + this.cb_MyLoc.ErrorForeColor = System.Drawing.Color.White; + this.cb_MyLoc.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_MyLoc.ForeColor = System.Drawing.SystemColors.WindowText; + this.cb_MyLoc.FormattingEnabled = true; + this.cb_MyLoc.GeoLocation = gPoint6; + this.cb_MyLoc.Location = new System.Drawing.Point(3, 71); + this.cb_MyLoc.Name = "cb_MyLoc"; + this.cb_MyLoc.Precision = 3; + this.cb_MyLoc.SilentItemChange = true; + this.cb_MyLoc.Size = new System.Drawing.Size(133, 24); + this.cb_MyLoc.SmallLettersForSubsquares = global::AirScout.Properties.Settings.Default.Locator_SmallLettersForSubsquares; + this.cb_MyLoc.TabIndex = 22; + this.cb_MyLoc.DropDown += new System.EventHandler(this.cb_MyLoc_DropDown); + this.cb_MyLoc.SelectedIndexChanged += new System.EventHandler(this.cb_MyLoc_SelectedIndexChanged); + this.cb_MyLoc.SelectionChangeCommitted += new System.EventHandler(this.cb_MyLoc_SelectionChangeCommittedWithNoUpdate); + this.cb_MyLoc.TextChanged += new System.EventHandler(this.cb_MyLoc_TextChanged); + // + // cb_MyCall + // + this.cb_MyCall.BackColor = System.Drawing.SystemColors.Window; + this.cb_MyCall.CharacterCasing = System.Windows.Forms.CharacterCasing.Upper; + this.cb_MyCall.ErrorBackColor = System.Drawing.Color.Red; + this.cb_MyCall.ErrorForeColor = System.Drawing.Color.White; + this.cb_MyCall.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_MyCall.ForeColor = System.Drawing.SystemColors.WindowText; + this.cb_MyCall.FormattingEnabled = true; + this.cb_MyCall.Location = new System.Drawing.Point(3, 34); + this.cb_MyCall.Name = "cb_MyCall"; + this.cb_MyCall.Size = new System.Drawing.Size(133, 24); + this.cb_MyCall.Sorted = true; + this.cb_MyCall.TabIndex = 21; + this.cb_MyCall.DropDown += new System.EventHandler(this.cb_MyCall_DropDown); + this.cb_MyCall.SelectedIndexChanged += new System.EventHandler(this.cb_MyCall_SelectedIndexChanged); + this.cb_MyCall.TextChanged += new System.EventHandler(this.cb_MyCall_TextChanged); + // + // label6 + // + this.label6.AutoSize = true; + this.label6.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label6.Location = new System.Drawing.Point(3, 226); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(28, 13); + this.label6.TabIndex = 10; + this.label6.Text = "QTF"; + // + // label16 + // + this.label16.AutoSize = true; + this.label16.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label16.Location = new System.Drawing.Point(2, 181); + this.label16.Name = "label16"; + this.label16.Size = new System.Drawing.Size(30, 13); + this.label16.TabIndex = 8; + this.label16.Text = "QRB"; + // + // label17 + // + this.label17.AutoSize = true; + this.label17.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label17.Location = new System.Drawing.Point(3, 139); + this.label17.Name = "label17"; + this.label17.Size = new System.Drawing.Size(43, 13); + this.label17.TabIndex = 6; + this.label17.Text = "DX Loc"; + // + // label18 + // + this.label18.AutoSize = true; + this.label18.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label18.Location = new System.Drawing.Point(2, 97); + this.label18.Name = "label18"; + this.label18.Size = new System.Drawing.Size(42, 13); + this.label18.TabIndex = 4; + this.label18.Text = "DX Call"; + // + // label19 + // + this.label19.AutoSize = true; + this.label19.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label19.Location = new System.Drawing.Point(2, 58); + this.label19.Name = "label19"; + this.label19.Size = new System.Drawing.Size(42, 13); + this.label19.TabIndex = 2; + this.label19.Text = "My Loc"; + // + // label20 + // + this.label20.AutoSize = true; + this.label20.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label20.Location = new System.Drawing.Point(3, 18); + this.label20.Name = "label20"; + this.label20.Size = new System.Drawing.Size(41, 13); + this.label20.TabIndex = 0; + this.label20.Text = "My Call"; + // + // tp_Control_Multi + // + this.tp_Control_Multi.BackColor = System.Drawing.SystemColors.Control; + this.tp_Control_Multi.Controls.Add(this.lv_Control_Watchlist); + this.tp_Control_Multi.Controls.Add(this.btn_Control_Manage_Watchlist); + this.tp_Control_Multi.Location = new System.Drawing.Point(4, 22); + this.tp_Control_Multi.Name = "tp_Control_Multi"; + this.tp_Control_Multi.Padding = new System.Windows.Forms.Padding(3); + this.tp_Control_Multi.Size = new System.Drawing.Size(143, 378); + this.tp_Control_Multi.TabIndex = 1; + this.tp_Control_Multi.Text = "Multi"; + this.tp_Control_Multi.Enter += new System.EventHandler(this.tp_Control_Multi_Enter); + // + // lv_Control_Watchlist + // + this.lv_Control_Watchlist.CheckBoxes = true; + this.lv_Control_Watchlist.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.ch_Control_Watchlist_Call, + this.ch_Control_Watchlist_Loc}); + this.lv_Control_Watchlist.Dock = System.Windows.Forms.DockStyle.Fill; + this.lv_Control_Watchlist.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lv_Control_Watchlist.FullRowSelect = true; + this.lv_Control_Watchlist.GridLines = true; + this.lv_Control_Watchlist.Location = new System.Drawing.Point(3, 3); + this.lv_Control_Watchlist.Name = "lv_Control_Watchlist"; + this.lv_Control_Watchlist.OwnerDraw = true; + this.lv_Control_Watchlist.Size = new System.Drawing.Size(137, 349); + this.lv_Control_Watchlist.Sorting = System.Windows.Forms.SortOrder.Ascending; + this.lv_Control_Watchlist.TabIndex = 1; + this.tt_Control_Watchlist.SetToolTip(this.lv_Control_Watchlist, "Watchlist"); + this.lv_Control_Watchlist.UseCompatibleStateImageBehavior = false; + this.lv_Control_Watchlist.View = System.Windows.Forms.View.Details; + this.lv_Control_Watchlist.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.lv_Control_Watchlist_ColumnClick); + this.lv_Control_Watchlist.ColumnWidthChanged += new System.Windows.Forms.ColumnWidthChangedEventHandler(this.lv_Control_Watchlist_ColumnWidthChanged); + this.lv_Control_Watchlist.DrawColumnHeader += new System.Windows.Forms.DrawListViewColumnHeaderEventHandler(this.lv_Control_Watchlist_DrawColumnHeader); + this.lv_Control_Watchlist.DrawItem += new System.Windows.Forms.DrawListViewItemEventHandler(this.lv_Control_Watchlist_DrawItem); + this.lv_Control_Watchlist.DrawSubItem += new System.Windows.Forms.DrawListViewSubItemEventHandler(this.lv_Control_Watchlist_DrawSubItem); + this.lv_Control_Watchlist.ItemChecked += new System.Windows.Forms.ItemCheckedEventHandler(this.lv_Control_Watchlist_ItemChecked); + this.lv_Control_Watchlist.ItemMouseHover += new System.Windows.Forms.ListViewItemMouseHoverEventHandler(this.lv_Control_Watchlist_ItemMouseHover); + this.lv_Control_Watchlist.MouseMove += new System.Windows.Forms.MouseEventHandler(this.lv_Control_Watchlist_MouseMove); + this.lv_Control_Watchlist.Resize += new System.EventHandler(this.lv_Control_Watchlist_Resize); + // + // ch_Control_Watchlist_Call + // + this.ch_Control_Watchlist_Call.Text = " Call"; + this.ch_Control_Watchlist_Call.Width = 86; + // + // ch_Control_Watchlist_Loc + // + this.ch_Control_Watchlist_Loc.Text = "Loc"; + this.ch_Control_Watchlist_Loc.Width = 47; + // + // btn_Control_Manage_Watchlist + // + this.btn_Control_Manage_Watchlist.Dock = System.Windows.Forms.DockStyle.Bottom; + this.btn_Control_Manage_Watchlist.Location = new System.Drawing.Point(3, 352); + this.btn_Control_Manage_Watchlist.Name = "btn_Control_Manage_Watchlist"; + this.btn_Control_Manage_Watchlist.Size = new System.Drawing.Size(137, 23); + this.btn_Control_Manage_Watchlist.TabIndex = 0; + this.btn_Control_Manage_Watchlist.Text = "Manage Watchlist"; + this.btn_Control_Manage_Watchlist.UseVisualStyleBackColor = true; + this.btn_Control_Manage_Watchlist.Click += new System.EventHandler(this.btn_Control_Manage_Watchlist_Click); + // + // tp_Control_Options + // + this.tp_Control_Options.BackColor = System.Drawing.SystemColors.Control; + this.tp_Control_Options.Controls.Add(this.gb_Map_Alarms); + this.tp_Control_Options.Controls.Add(this.gb_Map_Filter); + this.tp_Control_Options.Location = new System.Drawing.Point(4, 22); + this.tp_Control_Options.Name = "tp_Control_Options"; + this.tp_Control_Options.Size = new System.Drawing.Size(143, 378); + this.tp_Control_Options.TabIndex = 2; + this.tp_Control_Options.Text = "Opt"; + this.tp_Control_Options.Enter += new System.EventHandler(this.tp_Control_Options_Enter); + // + // pa_CommonInfo + // + this.pa_CommonInfo.Controls.Add(this.label1); + this.pa_CommonInfo.Controls.Add(this.tb_UTC); + this.pa_CommonInfo.Controls.Add(this.label8); + this.pa_CommonInfo.Controls.Add(this.cb_Band); + this.pa_CommonInfo.Dock = System.Windows.Forms.DockStyle.Top; + this.pa_CommonInfo.Location = new System.Drawing.Point(0, 0); + this.pa_CommonInfo.Name = "pa_CommonInfo"; + this.pa_CommonInfo.Size = new System.Drawing.Size(152, 100); + this.pa_CommonInfo.TabIndex = 59; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(4, 3); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(29, 13); + this.label1.TabIndex = 17; + this.label1.Text = "UTC"; + // + // tb_UTC + // + this.tb_UTC.BackColor = System.Drawing.Color.LightSalmon; + this.tb_UTC.Font = new System.Drawing.Font("Arial", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_UTC.ForeColor = System.Drawing.Color.White; + this.tb_UTC.Location = new System.Drawing.Point(4, 19); + this.tb_UTC.Name = "tb_UTC"; + this.tb_UTC.ReadOnly = true; + this.tb_UTC.Size = new System.Drawing.Size(133, 22); + this.tb_UTC.TabIndex = 13; + // + // label8 + // + this.label8.AutoSize = true; + this.label8.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label8.Location = new System.Drawing.Point(4, 44); + this.label8.Name = "label8"; + this.label8.Size = new System.Drawing.Size(32, 13); + this.label8.TabIndex = 14; + this.label8.Text = "Band"; + // + // gb_Map_Buttons + // + this.gb_Map_Buttons.BackColor = System.Drawing.SystemColors.Control; + this.gb_Map_Buttons.Controls.Add(this.btn_Map_PlayPause); + this.gb_Map_Buttons.Controls.Add(this.btn_Map_Save); + this.gb_Map_Buttons.Controls.Add(this.btn_Options); + this.gb_Map_Buttons.Dock = System.Windows.Forms.DockStyle.Bottom; + this.gb_Map_Buttons.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.gb_Map_Buttons.Location = new System.Drawing.Point(0, 583); + this.gb_Map_Buttons.Name = "gb_Map_Buttons"; + this.gb_Map_Buttons.Size = new System.Drawing.Size(152, 123); + this.gb_Map_Buttons.TabIndex = 57; + this.gb_Map_Buttons.TabStop = false; + this.gb_Map_Buttons.Text = "Control"; + // + // bw_Track + // + this.bw_Track.WorkerReportsProgress = true; + this.bw_Track.WorkerSupportsCancellation = true; + this.bw_Track.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bw_Track_DoWork); + this.bw_Track.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.bw_Track_ProgressChanged); + this.bw_Track.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.bw_Track_RunWorkerCompleted); + // + // il_Planes_H + // + this.il_Planes_H.ColorDepth = System.Windows.Forms.ColorDepth.Depth24Bit; + this.il_Planes_H.ImageSize = new System.Drawing.Size(36, 36); + this.il_Planes_H.TransparentColor = System.Drawing.Color.Transparent; + // + // il_Planes_L + // + this.il_Planes_L.ColorDepth = System.Windows.Forms.ColorDepth.Depth24Bit; + this.il_Planes_L.ImageSize = new System.Drawing.Size(16, 16); + this.il_Planes_L.TransparentColor = System.Drawing.Color.Transparent; + // + // il_Planes_S + // + this.il_Planes_S.ColorDepth = System.Windows.Forms.ColorDepth.Depth24Bit; + this.il_Planes_S.ImageSize = new System.Drawing.Size(48, 48); + this.il_Planes_S.TransparentColor = System.Drawing.Color.Transparent; + // + // bw_Webserver + // + this.bw_Webserver.WorkerReportsProgress = true; + this.bw_Webserver.WorkerSupportsCancellation = true; + this.bw_Webserver.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bw_Webserver_DoWork); + this.bw_Webserver.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.bw_Webserver_ProgressChanged); + // + // bw_JSONWriter + // + this.bw_JSONWriter.WorkerReportsProgress = true; + this.bw_JSONWriter.WorkerSupportsCancellation = true; + this.bw_JSONWriter.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bw_JSONWriter_DoWork); + // + // bw_NewsFeed + // + this.bw_NewsFeed.WorkerReportsProgress = true; + this.bw_NewsFeed.WorkerSupportsCancellation = true; + this.bw_NewsFeed.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bw_NewsFeed_DoWork); + this.bw_NewsFeed.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.bw_NewsFeed_ProgressChanged); + this.bw_NewsFeed.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.bw_NewsFeed_RunWorkerCompleted); + // + // il_Airports + // + this.il_Airports.ColorDepth = System.Windows.Forms.ColorDepth.Depth24Bit; + this.il_Airports.ImageSize = new System.Drawing.Size(16, 16); + this.il_Airports.TransparentColor = System.Drawing.Color.Transparent; + // + // bw_HistoryDownloader + // + this.bw_HistoryDownloader.WorkerReportsProgress = true; + this.bw_HistoryDownloader.WorkerSupportsCancellation = true; + this.bw_HistoryDownloader.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bw_HistoryDownloader_DoWork); + this.bw_HistoryDownloader.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.bw_HistoryDownloader_ProgressChanged); + this.bw_HistoryDownloader.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.bw_HistoryDownloader_RunWorkerCompleted); + // + // ti_Startup + // + this.ti_Startup.Interval = 5000; + this.ti_Startup.Tick += new System.EventHandler(this.ti_Startup_Tick); + // + // ti_ShowLegends + // + this.ti_ShowLegends.Interval = 5000; + this.ti_ShowLegends.Tick += new System.EventHandler(this.ti_ShowLegends_Tick); + // + // bw_Analysis_DataGetter + // + this.bw_Analysis_DataGetter.WorkerReportsProgress = true; + this.bw_Analysis_DataGetter.WorkerSupportsCancellation = true; + this.bw_Analysis_DataGetter.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bw_Analysis_DataGetter_DoWork); + this.bw_Analysis_DataGetter.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.bw_Analysis_DataGetter_ProgressChanged); + this.bw_Analysis_DataGetter.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.bw_Analysis_DataGetter_RunWorkerCompleted); + // + // bw_Analysis_FileSaver + // + this.bw_Analysis_FileSaver.WorkerReportsProgress = true; + this.bw_Analysis_FileSaver.WorkerSupportsCancellation = true; + this.bw_Analysis_FileSaver.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bw_Analysis_FileSaver_DoWork); + this.bw_Analysis_FileSaver.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.bw_Analysis_FileSaver_ProgressChanged); + this.bw_Analysis_FileSaver.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.bw_Analysis_FileSaver_RunWorkerCompleted); + // + // bw_Analysis_FileLoader + // + this.bw_Analysis_FileLoader.WorkerReportsProgress = true; + this.bw_Analysis_FileLoader.WorkerSupportsCancellation = true; + this.bw_Analysis_FileLoader.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bw_Analysis_FileLoader_DoWork); + this.bw_Analysis_FileLoader.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.bw_Analysis_FileLoader_ProgressChanged); + this.bw_Analysis_FileLoader.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.bw_Analysis_FileLoader_RunWorkerCompleted); + // + // bw_AirportMapper + // + this.bw_AirportMapper.WorkerReportsProgress = true; + this.bw_AirportMapper.WorkerSupportsCancellation = true; + this.bw_AirportMapper.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bw_AirportMapper_DoWork); + this.bw_AirportMapper.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.bw_AirportMapper_ProgressChanged); + this.bw_AirportMapper.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.bw_AirportMapper_RunWorkerCompleted); + // + // MapDlg + // + this.AcceptButton = this.btn_Map_PlayPause; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(1008, 730); + this.Controls.Add(this.sc_Main); + this.Controls.Add(this.ss_Main); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.MinimumSize = new System.Drawing.Size(800, 600); + this.Name = "MapDlg"; + this.Text = "AirScout - Aircraft Scatter Prediction (c) 2013 by DL2ALF"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MapDlg_FormClosing); + this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.MapDlg_FormClosed); + this.Load += new System.EventHandler(this.MapDlg_Load); + this.SizeChanged += new System.EventHandler(this.MapDlg_SizeChanged); + this.Resize += new System.EventHandler(this.MapDlg_Resize); + this.sc_Map.Panel1.ResumeLayout(false); + this.sc_Map.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.sc_Map)).EndInit(); + this.sc_Map.ResumeLayout(false); + this.gb_Map.ResumeLayout(false); + this.tc_Main.ResumeLayout(false); + this.tp_Spectrum.ResumeLayout(false); + this.gb_Spectrum_NearestInfo.ResumeLayout(false); + this.gb_Spectrum_NearestInfo.PerformLayout(); + this.gb_Spectrum_Status.ResumeLayout(false); + this.gb_Spectrum_Status.PerformLayout(); + this.gb_NearestPlaneMap.ResumeLayout(false); + this.tp_Analysis.ResumeLayout(false); + this.panel2.ResumeLayout(false); + this.gb_Analysis_Player.ResumeLayout(false); + this.gb_Analysis_Controls.ResumeLayout(false); + this.panel1.ResumeLayout(false); + this.gb_Analysis_Functions.ResumeLayout(false); + this.gb_Analysis_Database.ResumeLayout(false); + this.gb_Analysis_Database.PerformLayout(); + this.gb_Analysis_Mode.ResumeLayout(false); + this.gb_Analysis_Time.ResumeLayout(false); + this.gb_Analysis_Time.PerformLayout(); + this.ss_Main.ResumeLayout(false); + this.ss_Main.PerformLayout(); + this.gb_Map_Alarms.ResumeLayout(false); + this.gb_Map_Alarms.PerformLayout(); + this.gb_Map_Zoom.ResumeLayout(false); + this.pa_Map_Zoom.ResumeLayout(false); + this.pa_Map_Zoom.PerformLayout(); + this.gb_Map_Filter.ResumeLayout(false); + this.pa_Planes_Filter.ResumeLayout(false); + this.pa_Planes_Filter.PerformLayout(); + this.sc_Main.Panel1.ResumeLayout(false); + this.sc_Main.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.sc_Main)).EndInit(); + this.sc_Main.ResumeLayout(false); + this.tc_Control.ResumeLayout(false); + this.tp_Control_Single.ResumeLayout(false); + this.gb_Map_Info.ResumeLayout(false); + this.gb_Map_Info.PerformLayout(); + this.tp_Control_Multi.ResumeLayout(false); + this.tp_Control_Options.ResumeLayout(false); + this.pa_CommonInfo.ResumeLayout(false); + this.pa_CommonInfo.PerformLayout(); + this.gb_Map_Buttons.ResumeLayout(false); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Timer ti_Progress; + private System.Windows.Forms.ImageList il_Main; + private System.Windows.Forms.SplitContainer sc_Map; + private System.Windows.Forms.GroupBox gb_Map; + private System.Windows.Forms.StatusStrip ss_Main; + private System.Windows.Forms.ToolStripStatusLabel tsl_Status; + private System.Windows.Forms.ToolTip tt_Main; + private System.Windows.Forms.ImageList il_Sat; + private System.ComponentModel.BackgroundWorker bw_WinTestReceive; + public System.Windows.Forms.ImageList il_Planes_M; + private System.ComponentModel.BackgroundWorker bw_SpecLab_Receive; + private System.Windows.Forms.TabControl tc_Main; + private System.Windows.Forms.TabPage tp_Elevation; + private System.Windows.Forms.TabPage tp_Spectrum; + private System.Windows.Forms.TextBox tb_Spectrum_Status; + private System.Windows.Forms.SplitContainer sc_Main; + private System.Windows.Forms.GroupBox gb_Map_Buttons; + private System.Windows.Forms.Button btn_Map_PlayPause; + private System.Windows.Forms.Button btn_Map_Save; + private System.Windows.Forms.Button btn_Options; + private GMap.NET.WindowsForms.GMapControl gm_Main; + private AquaControls.AquaGauge ag_Azimuth; + private AquaControls.AquaGauge ag_Elevation; + private System.ComponentModel.BackgroundWorker bw_Track; + public System.Windows.Forms.ImageList il_Planes_H; + public System.Windows.Forms.ImageList il_Planes_L; + public System.Windows.Forms.ImageList il_Planes_S; + private System.ComponentModel.BackgroundWorker bw_Webserver; + private System.ComponentModel.BackgroundWorker bw_JSONWriter; + private System.ComponentModel.BackgroundWorker bw_NewsFeed; + private System.Windows.Forms.TabPage tp_News; + public System.Windows.Forms.ImageList il_Airports; + private System.Windows.Forms.ToolStripStatusLabel tsl_Dummy; + private System.Windows.Forms.ToolStripStatusLabel tsl_Database; + private System.Windows.Forms.ToolStripStatusLabel tsl_Database_LED_SRTM1; + private System.Windows.Forms.TabPage tp_Analysis; + private System.Windows.Forms.Panel panel2; + private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.GroupBox gb_Analysis_Time; + private System.ComponentModel.BackgroundWorker bw_HistoryDownloader; + private System.Windows.Forms.GroupBox gb_Analysis_Player; + private System.Windows.Forms.GroupBox gb_Analysis_Controls; + private System.Windows.Forms.Button btn_Analysis_FastForward; + private System.Windows.Forms.Button btn_Analysis_Forward; + private System.Windows.Forms.Button btn_Analysis_Pause; + private System.Windows.Forms.Button btn_Analysis_Back; + private System.Windows.Forms.Button btn_Analysis_Rewind; + private System.Windows.Forms.Label label15; + private System.Windows.Forms.Label label14; + private System.Windows.Forms.TextBox tb_Analysis_Stepwidth; + private System.Windows.Forms.TextBox tb_Analysis_Time; + private System.Windows.Forms.ToolStripStatusLabel tsl_Calculations; + private System.Windows.Forms.ToolStripStatusLabel tsl_Database_LED_GLOBE; + private System.Windows.Forms.ToolStripStatusLabel tsl_Database_LED_SRTM3; + private System.Windows.Forms.Timer ti_Startup; + private System.Windows.Forms.Timer ti_ShowLegends; + private System.Windows.Forms.ToolStripStatusLabel tsl_Database_LED_Stations; + private System.Windows.Forms.ComboBox cb_Band; + private System.Windows.Forms.Label label8; + private System.Windows.Forms.TextBox tb_UTC; + private System.Windows.Forms.ToolTip tt_Control_Watchlist; + private System.Windows.Forms.ToolStripStatusLabel tsl_Database_LED_Aircraft; + private System.Windows.Forms.TabControl tc_Control; + private System.Windows.Forms.TabPage tp_Control_Single; + private System.Windows.Forms.GroupBox gb_Map_Info; + private ScoutBase.Core.CallsignComboBox cb_DXCall; + private ScoutBase.Core.LocatorComboBox cb_DXLoc; + private ScoutBase.Core.LocatorComboBox cb_MyLoc; + private ScoutBase.Core.CallsignComboBox cb_MyCall; + private System.Windows.Forms.TextBox tb_QTF; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.TextBox tb_QRB; + private System.Windows.Forms.Label label16; + private System.Windows.Forms.Label label17; + private System.Windows.Forms.Label label18; + private System.Windows.Forms.Label label19; + private System.Windows.Forms.Label label20; + private System.Windows.Forms.TabPage tp_Control_Multi; + private System.Windows.Forms.ListView lv_Control_Watchlist; + private System.Windows.Forms.ColumnHeader ch_Control_Watchlist_Call; + private System.Windows.Forms.ColumnHeader ch_Control_Watchlist_Loc; + private System.Windows.Forms.Button btn_Control_Manage_Watchlist; + private System.Windows.Forms.TabPage tp_Control_Options; + private System.Windows.Forms.GroupBox gb_Map_Alarms; + private System.Windows.Forms.CheckBox cb_Alarms_Activate; + private System.Windows.Forms.GroupBox gb_Map_Zoom; + private System.Windows.Forms.Panel pa_Map_Zoom; + private System.Windows.Forms.CheckBox cb_AutoCenter; + private System.Windows.Forms.TextBox tb_Zoom; + private System.Windows.Forms.Button btn_Zoom_Out; + private System.Windows.Forms.Button btn_Zoom_In; + private System.Windows.Forms.GroupBox gb_Map_Filter; + private System.Windows.Forms.Panel pa_Planes_Filter; + private ScoutBase.Core.Int32TextBox tb_Planes_Filter_Min_Alt; + private ScoutBase.Core.Int32TextBox tb_Planes_Filter_Max_Circumcircle; + private System.Windows.Forms.Label label12; + private System.Windows.Forms.Label label13; + private System.Windows.Forms.ComboBox cb_Planes_Filter_Min_Cat; + private System.Windows.Forms.Label label11; + private System.Windows.Forms.Label label10; + private System.Windows.Forms.Label label9; + private System.Windows.Forms.Panel pa_CommonInfo; + private System.Windows.Forms.Label label1; + private CustomScrollBar.ScrollBarEx sb_Analysis_Play; + private System.Windows.Forms.DateTimePicker dtp_Analysis_MaxValue; + private System.Windows.Forms.DateTimePicker dtp_Analysis_MinValue; + private System.Windows.Forms.GroupBox gb_Analysis_Functions; + private System.Windows.Forms.Button btn_Analysis_CrossingHistory; + private System.Windows.Forms.Button btn_Analysis_Path_SaveToFile; + private System.Windows.Forms.Button btn_Analysis_Planes_ShowTraffic; + private System.Windows.Forms.GroupBox gb_Analysis_Database; + private System.Windows.Forms.TextBox tb_Analysis_Status; + private System.Windows.Forms.Button btn_Analysis_Planes_History; + private System.Windows.Forms.Button btn_Analysis_Planes_Clear; + private System.Windows.Forms.Button btn_Analysis_Planes_Save; + private System.Windows.Forms.Button btn_Analysis_Planes_Load; + private System.Windows.Forms.GroupBox gb_Analysis_Mode; + private System.Windows.Forms.Button btn_Analysis_OFF; + private System.Windows.Forms.Button btn_Analysis_ON; + private System.Windows.Forms.GroupBox gb_NearestPlaneMap; + private System.Windows.Forms.GroupBox gb_Spectrum; + private System.Windows.Forms.GroupBox gb_Spectrum_Status; + private GMap.NET.WindowsForms.GMapControl gm_Nearest; + private System.Windows.Forms.GroupBox gb_Spectrum_NearestInfo; + private System.Windows.Forms.Label lbl_Nearest_Type; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label lbl_Nearest_Cat; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.Label lbl_Nearest_Angle; + private System.Windows.Forms.Label label7; + private System.Windows.Forms.Label lbl_Nearest_Alt; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Label lbl_Nearest_Call; + private System.Windows.Forms.Label label21; + private System.Windows.Forms.Label lbl_Nearest_Dist; + private System.Windows.Forms.Label label22; + private System.Windows.Forms.Button btn_Analysis_Plane_History; + private System.ComponentModel.BackgroundWorker bw_Analysis_DataGetter; + private System.ComponentModel.BackgroundWorker bw_Analysis_FileSaver; + private System.ComponentModel.BackgroundWorker bw_Analysis_FileLoader; + private System.ComponentModel.BackgroundWorker bw_AirportMapper; + } +} + diff --git a/AirScout/MapDlg.cs b/AirScout/MapDlg.cs new file mode 100644 index 0000000..cab2acf --- /dev/null +++ b/AirScout/MapDlg.cs @@ -0,0 +1,7101 @@ + +// AirScout Aircraft Scatter Prediction +// Copyright (C) DL2ALF +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Forms; +using System.Windows.Forms.VisualStyles; +using GMap.NET; +using GMap.NET.MapProviders; +using GMap.NET.WindowsForms; +using GMap.NET.WindowsForms.Markers; +using GMap.NET.WindowsForms.ToolTips; +using System.IO; +using System.IO.Ports; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using Zeptomoby.OrbitTools; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Reflection; +using System.Configuration; +using WinTest; +using Renci.SshNet; +using System.Diagnostics; +using AquaControls; +using AirScout.Core; +using AirScout.PlaneFeeds.Generic; +using AirScout.Aircrafts; +using NDde; +using NDde.Server; +using NDde.Client; +using ScoutBase; +using ScoutBase.Core; +using ScoutBase.Elevation; +using ScoutBase.Stations; +using ScoutBase.Propagation; +using SerializableGenerics; +using Ionic.Zip; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using SQLiteDatabase; +using System.Xml; +using System.Xml.Serialization; +using System.Security.Cryptography; +using OxyPlot; +using OxyPlot.WindowsForms; +using OxyPlot.Series; +using OxyPlot.Axes; +using System.Data.SQLite; +using AirScout.AircraftPositions; +using AirScout.Signals; + +namespace AirScout +{ + + + public partial class MapDlg : Form + { + + [CategoryAttribute("Directories")] + [DescriptionAttribute("Application Directory")] + public string AppDirectory + { + get + { + return Application.StartupPath.TrimEnd(Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar); + } + } + + [CategoryAttribute("Directories")] + [DescriptionAttribute("Local Application Data Directory")] + public string AppDataDirectory + { + get + { + return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), Application.CompanyName, Application.ProductName).TrimEnd(Path.DirectorySeparatorChar); + } + } + + [CategoryAttribute("Directories")] + [DescriptionAttribute("Logfile Directory")] + public string LogDirectory + { + get + { + // get Property + string logdir = Properties.Settings.Default.Log_Directory; + // replace Windows/Linux directory spearator chars + logdir = logdir.Replace('\\', Path.DirectorySeparatorChar); + logdir = logdir.Replace('/', Path.DirectorySeparatorChar); + // set to default value if empty + if (String.IsNullOrEmpty(logdir)) + logdir = "Log"; + // replace variables, if any + logdir = VC.ReplaceAllVars(logdir); + // remove directory separator chars at begin and end + logdir = logdir.TrimStart(Path.DirectorySeparatorChar); + logdir = logdir.TrimEnd(Path.DirectorySeparatorChar); + // fully qualify path + if (!logdir.Contains(Path.VolumeSeparatorChar)) + logdir = Path.Combine(AppDataDirectory, logdir); + return logdir; + } + } + + [CategoryAttribute("Directories")] + [DescriptionAttribute("Tempfile Directory")] + public string TmpDirectory + { + get + { + // get Property + string tmpdir = Properties.Settings.Default.Tmp_Directory; + // replace Windows/Linux directory spearator chars + tmpdir = tmpdir.Replace('\\', Path.DirectorySeparatorChar); + tmpdir = tmpdir.Replace('/', Path.DirectorySeparatorChar); + // set to default value if empty + if (String.IsNullOrEmpty(tmpdir)) + tmpdir = "Tmp"; + // replace variables, if any + tmpdir = VC.ReplaceAllVars(tmpdir); + // remove directory separator chars at begin and end + tmpdir = tmpdir.TrimStart(Path.DirectorySeparatorChar); + tmpdir = tmpdir.TrimEnd(Path.DirectorySeparatorChar); + // fully qualify path + if (!tmpdir.Contains(Path.VolumeSeparatorChar)) + tmpdir = Path.Combine(AppDataDirectory, tmpdir); + return tmpdir; + } + } + + [CategoryAttribute("Directories")] + [DescriptionAttribute("Database Directory")] + public string DatabaseDirectory + { + get + { + // get Property + string databasedir = Properties.Settings.Default.Database_Directory; + // replace Windows/Linux directory spearator chars + databasedir = databasedir.Replace('\\', Path.DirectorySeparatorChar); + databasedir = databasedir.Replace('/', Path.DirectorySeparatorChar); + // set to default value if empty + if (String.IsNullOrEmpty(databasedir)) + databasedir = "Database"; + // replace variables, if any + databasedir = VC.ReplaceAllVars(databasedir); + // remove directory separator chars at begin and end + databasedir = databasedir.TrimStart(Path.DirectorySeparatorChar); + databasedir = databasedir.TrimEnd(Path.DirectorySeparatorChar); + // fully qualify path + if (!databasedir.Contains(Path.VolumeSeparatorChar)) + databasedir = Path.Combine(AppDataDirectory, databasedir); + return databasedir; + } + } + + [CategoryAttribute("Directories")] + [DescriptionAttribute("Icon Directory")] + public string IconDirectory + { + get + { + // get Property + string icondir = Properties.Settings.Default.Icon_Directory; + // replace Windows/Linux directory spearator chars + icondir = icondir.Replace('\\', Path.DirectorySeparatorChar); + icondir = icondir.Replace('/', Path.DirectorySeparatorChar); + // set to default value if empty + if (String.IsNullOrEmpty(icondir)) + icondir = "Icon"; + // replace variables, if any + icondir = VC.ReplaceAllVars(icondir); + // remove directory separator chars at begin and end + icondir = icondir.TrimStart(Path.DirectorySeparatorChar); + icondir = icondir.TrimEnd(Path.DirectorySeparatorChar); + // fully qualify path + if (!icondir.Contains(Path.VolumeSeparatorChar)) + icondir = Path.Combine(AppDataDirectory, icondir); + return icondir; + } + } + + [CategoryAttribute("Directories")] + [DescriptionAttribute("Elevation Directory")] + public string ElevationDirectory + { + get + { + // get Property + string elevationdir = Properties.Settings.Default.Elevation_Directory; + // replace Windows/Linux directory spearator chars + elevationdir = elevationdir.Replace('\\', Path.DirectorySeparatorChar); + elevationdir = elevationdir.Replace('/', Path.DirectorySeparatorChar); + // set to default value if empty + if (String.IsNullOrEmpty(elevationdir)) + elevationdir = "elevation"; + // replace variables, if any + elevationdir = VC.ReplaceAllVars(elevationdir); + // remove directory separator chars at begin and end + elevationdir = elevationdir.TrimStart(Path.DirectorySeparatorChar); + elevationdir = elevationdir.TrimEnd(Path.DirectorySeparatorChar); + // fully qualify path + if (!elevationdir.Contains(Path.VolumeSeparatorChar)) + elevationdir = Path.Combine(AppDataDirectory, elevationdir); + return elevationdir; + } + } + + public static LogWriter Log; + + public VarConverter VC = new VarConverter(); + + private int cntdn = 0; + + DateTime CurrentTime = DateTime.UtcNow; + + public bool ForceClose = false; + + + // FlightRadar + PlaneInfoCache Planes = new PlaneInfoCache(); + SortedList ActivePlanes = new SortedList(); + private DateTime History_OldestEntry = DateTime.MinValue; + private DateTime History_YoungestEntry = DateTime.MinValue; + private List SelectedPlanes = new List(); + + // Path + public List ElevationPaths = new List(); + public List PropagationPaths = new List(); + + // Map + // Overlays + GMapOverlay gmo_Routes = new GMapOverlay("Routes"); + GMapOverlay gmo_PropagationPaths = new GMapOverlay("PropagationPaths"); + GMapOverlay gmo_Objects = new GMapOverlay("Objects"); + GMapOverlay gmo_Planes = new GMapOverlay("Planes"); + GMapOverlay gmo_Airports = new GMapOverlay("Airports"); + GMapOverlay gmo_Callsigns = new GMapOverlay("Callsigns"); + GMapOverlay gmo_NearestPaths = new GMapOverlay("PropagationPaths"); + GMapOverlay gmo_NearestPlanes = new GMapOverlay("Planes"); + + // Routes + GMapRoute gmr_FullPath; + GMapRoute gmr_VisiblePpath; + GMapRoute gmr_NearestFull; + GMapRoute gmr_NearestVisible; + + // Markers + GMapMarker gmm_MyLoc; + GMapMarker gmm_DXLoc; + GMapMarker gmm_CurrentMarker; + bool isDraggingMarker; + + // Bitmap indices + private int bmindex_darkorange; + private int bmindex_lightgreen; + private int bmindex_red; + private int bmindex_gray; + private int bmindex_magenta; + + // Tooltip font + public Font ToolTipFont; + + // right control boxes + int gb_Map_Info_Width = 0; + + int gb_Map_Info_MaximizedHeight = 0; + int gb_Map_Zoom_MaximizedHeight = 0; + int gb_Map_Filter_MaximizedHeigth = 0; + int gb_Map_Alarms_MaximizedHeight = 0; + int gb_Map_Info_MinimizedHeight = 0; + int gb_Map_Zoom_MinimizedHeight = 0; + int gb_Map_Filter_MinimizedHeigth = 0; + int gb_Map_Alarms_MinimizedHeight = 0; + + int CurrentMapSplitterDistance = 0; + + // Plane feeds + public ArrayList PlaneFeeds; + public PlaneFeed bw_PlaneFeed1; + public PlaneFeed bw_PlaneFeed2; + public PlaneFeed bw_PlaneFeed3; + + // Startup + private bool FirstRun = true; + private bool CleanRun = false; + + private Splash SplashDlg = new Splash(); + + // Background workers + public ElevationDatabaseUpdater bw_GLOBEUpdater = new ElevationDatabaseUpdater(); + public ElevationDatabaseUpdater bw_SRTM3Updater = new ElevationDatabaseUpdater(); + public ElevationDatabaseUpdater bw_SRTM1Updater = new ElevationDatabaseUpdater(); + + public StationDatabaseUpdater bw_StationDatabaseUpdater = new StationDatabaseUpdater(); + public AircraftDatabaseUpdater bw_AircraftDatabaseUpdater = new AircraftDatabaseUpdater(); + + public AircraftPositionDatabaseMaintainer bw_AircraftDatabaseMaintainer = new AircraftPositionDatabaseMaintainer(); + + public PathCalculator bw_GLOBEPathCalculator = new PathCalculator(ELEVATIONMODEL.GLOBE); + public PathCalculator bw_SRTM3PathCalculator = new PathCalculator(ELEVATIONMODEL.SRTM3); + public PathCalculator bw_SRTM1PathCalculator = new PathCalculator(ELEVATIONMODEL.SRTM1); + + // Modes + AIRSCOUTPATHMODE PathMode = AIRSCOUTPATHMODE.NONE; + AIRSCOUTLIFEMODE LifeMode = AIRSCOUTLIFEMODE.NONE; + AIRSCOUTPLAYMODE PlayMode = AIRSCOUTPLAYMODE.NONE; + + private int Time_Offline_Interval = 0; + + // charting + + // path chart + PlotModel pm_Path = new PlotModel(); + PlotView pv_Path = new PlotView(); + LinearAxis Path_X = new LinearAxis(); + LinearAxis Path_Y = new LinearAxis(); + AreaSeries Path_Elevation = new AreaSeries(); + LineSeries Min_H1 = new LineSeries(); + LineSeries Min_H2 = new LineSeries(); + LineSeries Max_H = new LineSeries(); + AreaSeries Min_H = new AreaSeries(); + LineSeries Planes_Hi = new LineSeries(); + LineSeries Planes_Lo = new LineSeries(); + + // elevation chart + PlotModel pm_Elevation = new PlotModel(); + PlotView pv_Elevation = new PlotView(); + LinearAxis Elevation_X = new LinearAxis(); + LinearAxis Elevation_Y = new LinearAxis(); + AreaSeries Elevation = new AreaSeries(); + LineSeries LOS = new LineSeries(); + + // spectrum chart + PlotModel pm_Spectrum = new PlotModel(); + PlotView pv_Spectrum = new PlotView(); + LinearAxis Spectrum_X = new LinearAxis(); + LinearAxis Spectrum_Y = new LinearAxis(); + LineSeries Spectrum = new LineSeries(); + AreaSeries SpectrumRecord = new AreaSeries(); + int SpectrumPointsCount = 0; + int SpectrumMaxPoints = 600; + + // webbrowser + private System.Windows.Forms.WebBrowser wb_News = null; + + CultureInfo LocalCulture; + + // Watchlist control + private bool WatchlistUpdating = false; + private bool WatchlistAllCheckedChanging = false; + bool WatchlistAllChecked = false; + CheckBoxState WatchlistAllCheckedState = CheckBoxState.UncheckedNormal; + System.Drawing.Point WatchlistOldMousePos = new System.Drawing.Point(0, 0); + + // Analysis + private List AllLastUpdated = new List(); + private List AllPositions = new List(); + private long AircraftPositionsCount = 0; + + // nearest plane + private PlaneInfo NearestPlane = null; + + // Airports + private List Airports = new List(); + + #region Startup & Initialization + + public MapDlg() + { + // save current local LocalCulture + LocalCulture = Application.CurrentCulture; + + // force culture invariant language for GUI + Application.CurrentCulture = CultureInfo.InvariantCulture; + + InitializeComponent(); + + // do basic initialization + this.Text = "AirScout - Aircraft Scatter Prediction V" + Application.ProductVersion + " (c) 2013-2018 DL2ALF"; + + // create a new renderer wich is clipping the status text on overflow + ss_Main.Renderer = new ClippingToolStripRenderer(); + + // initialize settings + InitializeSettings(); + // initialize charting + InitializeCharts(); + // Initilialize Webbrowser + InitializeWebbrowser(); + + // check for command line args + string[] args = Environment.GetCommandLineArgs(); + if ((args != null) && (args.Count() > 1)) + { + // try to clean installation & database + if ((args[1].ToUpper() == "/CLEAN") || + (args[1].ToUpper() == "-CLEAN")) + { + CleanupDlg Dlg = new CleanupDlg(this); + DialogResult result = Dlg.ShowDialog(); + // exit immediately if user closes the form without pressing a button + if (result == DialogResult.Abort) + System.Environment.Exit(-1); + if (result == DialogResult.OK) + CleanRun = true; + } + } + // set elevation database update event handler + bw_GLOBEUpdater.ProgressChanged += new ProgressChangedEventHandler(bw_ElevationDatabaseUpdater_ProgressChanged); + bw_SRTM3Updater.ProgressChanged += new ProgressChangedEventHandler(bw_ElevationDatabaseUpdater_ProgressChanged); + bw_SRTM1Updater.ProgressChanged += new ProgressChangedEventHandler(bw_ElevationDatabaseUpdater_ProgressChanged); + + // set station database updater event handler + bw_StationDatabaseUpdater.ProgressChanged += new ProgressChangedEventHandler(bw_StationDatabaseUpdater_ProgressChanged); + bw_StationDatabaseUpdater.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_StationDatabaseUpdater_RunWorkerCompleted); + + // set aircraft database updater event handler + bw_AircraftDatabaseUpdater.ProgressChanged += new ProgressChangedEventHandler(bw_AircraftDatabaseUpdater_ProgressChanged); + bw_AircraftDatabaseUpdater.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_AircraftDatabaseUpdater_RunWorkerCompleted); + + // set aircraft database maintainer event handler + bw_AircraftDatabaseMaintainer.ProgressChanged += new ProgressChangedEventHandler(bw_AircraftDatabaseMaintainer_ProgressChanged); + bw_AircraftDatabaseMaintainer.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_AircraftDatabaseMaintainer_RunWorkerCompleted); + + // set elevation path calculator event handler + bw_GLOBEPathCalculator.ProgressChanged += new ProgressChangedEventHandler(bw_ElevationPathCalculator_ProgressChanged); + bw_SRTM3PathCalculator.ProgressChanged += new ProgressChangedEventHandler(bw_ElevationPathCalculator_ProgressChanged); + bw_SRTM1PathCalculator.ProgressChanged += new ProgressChangedEventHandler(bw_ElevationPathCalculator_ProgressChanged); + // save FirstRun property before trying to upgrade user settings + FirstRun = Properties.Settings.Default.FirstRun; + } + + public void CheckDirectories() + { + // check if directories exist + if (!Directory.Exists(TmpDirectory)) + Directory.CreateDirectory(TmpDirectory); + if (!Directory.Exists(LogDirectory)) + Directory.CreateDirectory(LogDirectory); + if (!Directory.Exists(IconDirectory)) + Directory.CreateDirectory(IconDirectory); + if (!Directory.Exists(DatabaseDirectory)) + Directory.CreateDirectory(DatabaseDirectory); + if (!Directory.Exists(ElevationDirectory)) + Directory.CreateDirectory(ElevationDirectory); + } + + /// + /// Returns the default value of a property + /// + /// The property name. + /// The property value. + public static dynamic GetPropertyDefaultValue(string propertyname) + { + string p = (string)Properties.Settings.Default.Properties[propertyname].DefaultValue; + Type t = Properties.Settings.Default.Properties[propertyname].PropertyType; + return Convert.ChangeType(p, t); + } + + private void InitializeSettings() + { + // check for invalid settings + // check if band is BNONE --> set to 1.2G + if (Properties.Settings.Default.Band == BAND.BNONE) + Properties.Settings.Default.Band = BAND.B1_2G; + // chekc if band settings are NULL --> set to default + if (Properties.Settings.Default.Path_Band_Settings == null) + Properties.Settings.Default.Path_Band_Settings = new BandSettings(true); + // check if watchlist in null --> create new + if (Properties.Settings.Default.Watchlist == null) + Properties.Settings.Default.Watchlist = new Watchlist(); + string location = StationData.Database.GetDBLocation(); + // check calls + if (String.IsNullOrEmpty(Properties.Settings.Default.MyCall)) + Properties.Settings.Default.MyCall = GetPropertyDefaultValue(nameof(Properties.Settings.Default.MyCall)); + if (String.IsNullOrEmpty(Properties.Settings.Default.DXCall)) + Properties.Settings.Default.DXCall = GetPropertyDefaultValue(nameof(Properties.Settings.Default.DXCall)); + // check lat/lon + if (!GeographicalPoint.Check(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon)) + { + Properties.Settings.Default.MyLat = GetPropertyDefaultValue(nameof(Properties.Settings.Default.MyLat)); + Properties.Settings.Default.MyLon = GetPropertyDefaultValue(nameof(Properties.Settings.Default.MyLon)); + } + if (!GeographicalPoint.Check(Properties.Settings.Default.DXLat, Properties.Settings.Default.DXLon)) + { + Properties.Settings.Default.DXLat = GetPropertyDefaultValue(nameof(Properties.Settings.Default.DXLat)); + Properties.Settings.Default.DXLon = GetPropertyDefaultValue(nameof(Properties.Settings.Default.DXLon)); + } + // set current elevation model + SetElevationModel(); + // set antenna height to 10m be default + Properties.Settings.Default.MyHeight = 10; + Properties.Settings.Default.DXHeight = 10; + } + + private void CheckSettings() + { + Log.WriteMessage("Checking properties..."); + // check for empty MyCalls list + if (Properties.Settings.Default.MyCalls == null) + { + Properties.Settings.Default.MyCalls = new List(); + Properties.Settings.Default.MyCalls.Add("DL2ALF"); + } + if (Properties.Settings.Default.MyCalls.Count == 0) + Properties.Settings.Default.MyCalls.Add("DL2ALF"); + // list all properties in log + foreach (SettingsPropertyValue p in Properties.Settings.Default.PropertyValues) + { + if ((p != null) && (p.Name != null)) + { + string value = p.PropertyValue != null ? p.PropertyValue.ToString() : "[null]"; + Log.WriteMessage("Checking property " + p.Name + " = " + value); + } + } + /* + // check URLs for trailing separator + if (!Properties.Settings.Default.Elevation_GLOBE_URL.EndsWith("/")) + Properties.Settings.Default.Elevation_GLOBE_URL = Properties.Settings.Default.Elevation_GLOBE_URL + "/"; + if (!Properties.Settings.Default.Elevation_SRTM3_URL.EndsWith("/")) + Properties.Settings.Default.Elevation_SRTM3_URL = Properties.Settings.Default.Elevation_SRTM3_URL + "/"; + if (!Properties.Settings.Default.Elevation_SRTM1_URL.EndsWith("/")) + Properties.Settings.Default.Elevation_SRTM1_URL = Properties.Settings.Default.Elevation_SRTM1_URL + "/"; + if (!Properties.Settings.Default.StationDatabase_Update_URL.EndsWith("/")) + Properties.Settings.Default.StationDatabase_Update_URL = Properties.Settings.Default.StationDatabase_Update_URL + "/"; + */ + Log.WriteMessage("Checking properties finished..."); + } + + private void CheckInternet() + { + // check internet connectivity + try + { + using (var client = new WebClient()) + { + string url = Properties.Settings.Default.Connectivity_URL; + using (var stream = client.OpenRead(url)) + { + } + } + } + catch + { + MessageBox.Show("Could not find an internet connection.\nThis is required on first run.\nAirScout will close.", "Internet connectivity"); + this.Close(); + } + } + + private void InitializeLogfile() + { + // set directories and formats for logfile + ScoutBase.Core.Properties.Settings.Default.LogWriter_Directory = LogDirectory; + ScoutBase.Core.Properties.Settings.Default.LogWriter_FileFormat = "AirScout_{0:yyyy_MM_dd}.log"; + ScoutBase.Core.Properties.Settings.Default.LogWriter_MessageFormat = "{0:u} {1}"; + // gets an instance of a LogWriter + Log = LogWriter.Instance; + Log.WriteMessage("-------------------------------------------------------------------------------------"); + Log.WriteMessage(Application.ProductName + " is starting up.", 0, false); + Log.WriteMessage("-------------------------------------------------------------------------------------"); + Log.WriteMessage("Operating system : " + Environment.OSVersion.Platform); + Log.WriteMessage("Application directory : " + AppDirectory); + Log.WriteMessage("Application data directory: " + AppDataDirectory); + Log.WriteMessage("Log directory : " + LogDirectory); + Log.WriteMessage("Temp directory : " + TmpDirectory); + Log.WriteMessage("Database directory : " + DatabaseDirectory); + Log.WriteMessage("Elevation directory : " + ElevationDirectory); + Log.WriteMessage("Application settings file : " + AppDomain.CurrentDomain.SetupInformation.ConfigurationFile); + Log.WriteMessage("User settings file : " + GetUserSettingsPath()); + Log.WriteMessage("Disk space available : " + SupportFunctions.GetDriveAvailableFreeSpace(Path.GetPathRoot(AppDataDirectory)) / 1024 / 1024 + " MB"); + Log.WriteMessage("-------------------------------------------------------------------------------------"); + } + + private void InitializeDatabase() + { + Log.WriteMessage("Started."); + SetElevationModel(); + // reset database status + Properties.Settings.Default.Elevation_GLOBE_DatabaseStatus = DATABASESTATUS.UNDEFINED; + Properties.Settings.Default.Elevation_SRTM3_DatabaseStatus = DATABASESTATUS.UNDEFINED; + Properties.Settings.Default.Elevation_SRTM1_DatabaseStatus = DATABASESTATUS.UNDEFINED; + // set nearfield suppression + PropagationData.Database.NearFieldSuppression = Properties.Settings.Default.Path_NearFieldSuppression; + Log.WriteMessage("Finished."); + } + + private void MapDlg_Load(object sender, EventArgs e) + { + try + { + // Show splash screen + SplashDlg.Show(); + // bring window to front + SplashDlg.BringToFront(); + Application.DoEvents(); + // Check directories, complete it and create, if not exist + SplashDlg.Status("Checking directories..."); + CheckDirectories(); + // start a log, specify format of logfile and entries + SplashDlg.Status("Initializing logfile..."); + InitializeLogfile(); + // Check properties + SplashDlg.Status("Checking settings..."); + CheckSettings(); + // get AirScout password phrase + InitializePassword(); + // Initialize database + SplashDlg.Status("Initializing database..."); + InitializeDatabase(); + // initialize icons + SplashDlg.Status("Creating icons..."); + InitializeIcons(); + ToolTipFont = CreateFontFromString(Properties.Settings.Default.Map_ToolTipFont); + SplashDlg.Status("Loading Map..."); + + // initialize map + // setting User Agent to fix Open Street Map issue 2016-09-20 + GMap.NET.MapProviders.GMapProvider.UserAgent = "AirScout"; + // set initial settings for main map + gm_Main.MapProvider = GMapProviders.Find(Properties.Settings.Default.Map_Provider); + gm_Main.IgnoreMarkerOnMouseWheel = true; + gm_Main.MinZoom = 0; + gm_Main.MaxZoom = 20; + gm_Main.Zoom = 6; + gm_Main.DragButton = System.Windows.Forms.MouseButtons.Left; + gm_Main.CanDragMap = true; + gm_Main.ScalePen = new Pen(Color.Black, 3); + gm_Main.MapScaleInfoEnabled = true; + gm_Main.Overlays.Add(gmo_Airports); + gm_Main.Overlays.Add(gmo_Callsigns); + gm_Main.Overlays.Add(gmo_PropagationPaths); + gm_Main.Overlays.Add(gmo_Routes); + gm_Main.Overlays.Add(gmo_Objects); + gm_Main.Overlays.Add(gmo_Planes); + + // setting User Agent to fix Open Street Map issue 2016-09-20 + GMap.NET.MapProviders.GMapProvider.UserAgent = "AirScout"; + // set initial settings for main map + gm_Nearest.MapProvider = GMapProviders.Find(Properties.Settings.Default.Map_Provider); + gm_Nearest.IgnoreMarkerOnMouseWheel = true; + gm_Nearest.MinZoom = 0; + gm_Nearest.MaxZoom = 20; + gm_Nearest.Zoom = 6; + gm_Nearest.DragButton = System.Windows.Forms.MouseButtons.Left; + gm_Nearest.CanDragMap = true; + gm_Nearest.ScalePen = new Pen(Color.Black, 3); + gm_Nearest.MapScaleInfoEnabled = true; + gm_Nearest.Overlays.Add(gmo_NearestPaths); + gm_Nearest.Overlays.Add(gmo_NearestPlanes); + gm_Nearest.Position = new PointLatLng(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon); + + cntdn = Properties.Settings.Default.Planes_Update; + btn_Map_PlayPause.Select(); + + // intially fill dialog box elements and set band + string[] bands = Bands.GetStringValuesExceptNoneAndAll(); + foreach (string b in bands) + cb_Band.Items.Add(b); + BAND band = Properties.Settings.Default.Band; + cb_Band.SelectedItem = Bands.GetStringValue(band); + string[] cats = PlaneCategories.GetStringValues(); + foreach (string cat in cats) + cb_Planes_Filter_Min_Cat.Items.Add(cat); + // initialize gauge controls + ag_Azimuth.fromAngle = -90; + ag_Azimuth.toAngle = 270; + + ag_Elevation.fromAngle = 180; + ag_Elevation.toAngle = 270; + + // install additional mouse events + gb_Map_Info.MouseClick += new MouseEventHandler(this.gb_Map_Info_MouseClick); + gb_Map_Zoom.MouseClick += new MouseEventHandler(this.gb_Map_Zoom_MouseClick); + gb_Map_Filter.MouseClick += new MouseEventHandler(this.gb_Map_Filter_MouseClick); + gb_Map_Alarms.MouseClick += new MouseEventHandler(this.gb_Map_Alarms_MouseClick); + + // get installed plane feeds + PlaneFeeds = new PlaneFeedEnumeration().EnumFeeds(); + + // set planefeed event handler + foreach (PlaneFeed feed in PlaneFeeds) + { + feed.ProgressChanged += new ProgressChangedEventHandler(bw_PlaneFeed_ProgressChanged); + } + + // select feeds + foreach (PlaneFeed feed in PlaneFeeds) + { + if (Properties.Settings.Default.Planes_PlaneFeed1 == feed.Name) + bw_PlaneFeed1 = feed; + if (Properties.Settings.Default.Planes_PlaneFeed2 == feed.Name) + bw_PlaneFeed2 = feed; + if (Properties.Settings.Default.Planes_PlaneFeed3 == feed.Name) + bw_PlaneFeed3 = feed; + } + + // update image list sizes + il_Planes_L.ImageSize = new System.Drawing.Size(Properties.Settings.Default.Planes_IconSize_L, Properties.Settings.Default.Planes_IconSize_L); + il_Planes_M.ImageSize = new System.Drawing.Size(Properties.Settings.Default.Planes_IconSize_M, Properties.Settings.Default.Planes_IconSize_M); + il_Planes_H.ImageSize = new System.Drawing.Size(Properties.Settings.Default.Planes_IconSize_H, Properties.Settings.Default.Planes_IconSize_H); + il_Planes_S.ImageSize = new System.Drawing.Size(Properties.Settings.Default.Planes_IconSize_S, Properties.Settings.Default.Planes_IconSize_S); + + // try to upgrade user settings from prevoius version on first run + try + { + if (FirstRun) + { + Log.WriteMessage("Preparing for first run."); + // try to ugrade settings when not started with /CLEAN option + if (!CleanRun) + { + // Mono hack to assure that default values were initilaized + if (SupportFunctions.IsMono) + { + Properties.Settings.Default.Reset(); + SaveUserSettings(); + Properties.Settings.Default.Reload(); + } + Log.WriteMessage("Upgrading settings."); + Properties.Settings.Default.Upgrade(); + // handle skip to version 1.3.0.0 + if (String.IsNullOrEmpty(Properties.Settings.Default.Version) || (String.Compare(Properties.Settings.Default.Version, "1.3.0.0") < 0)) + { + /* + // reset elevation data url to new default values + Properties.Settings.Default.Elevation_GLOBE_URL = GetPropertyDefaultValue(nameof(Properties.Settings.Default.Elevation_GLOBE_URL)); + Properties.Settings.Default.Elevation_SRTM3_URL = GetPropertyDefaultValue(nameof(Properties.Settings.Default.Elevation_SRTM3_URL)); + Properties.Settings.Default.Elevation_SRTM1_URL = GetPropertyDefaultValue(nameof(Properties.Settings.Default.Elevation_SRTM1_URL)); + // reset elevation data path to default values + Properties.Settings.Default.Elevation_GLOBE_DataPath = GetPropertyDefaultValue(nameof(Properties.Settings.Default.Elevation_GLOBE_DataPath)); + Properties.Settings.Default.Elevation_SRTM3_DataPath = GetPropertyDefaultValue(nameof(Properties.Settings.Default.Elevation_SRTM3_DataPath)); + Properties.Settings.Default.Elevation_SRTM1_DataPath = GetPropertyDefaultValue(nameof(Properties.Settings.Default.Elevation_SRTM1_DataPath)); + // reset stations data url to its default value + Properties.Settings.Default.StationDatabase_Update_URL = GetPropertyDefaultValue(nameof(Properties.Settings.Default.StationDatabase_Update_URL)); + */ + + Properties.Settings.Default.Version = Application.ProductVersion; + + SaveUserSettings(); + } + AirScout.PlaneFeeds.Properties.Settings.Default.Upgrade(); + } + CheckDirectories(); + CheckSettings(); + // get passphrase again + InitializePassword(); + // reset topmost state + SplashDlg.TopMost = false; + // must have internet connection on FirstRun + CheckInternet(); + /* + // run database updater once for basic information + bw_DatabaseUpdater.RunWorkerAsync(UPDATERSTARTOPTIONS.FIRSTRUN); + // wait till finished + while (bw_DatabaseUpdater.IsBusy) + Application.DoEvents(); + */ + SplashDlg.Close(); + // show FirstRunWizard + try + { + FirstRunWizard Dlg = new FirstRunWizard(); + if (Dlg.ShowDialog() != System.Windows.Forms.DialogResult.OK) + { + Log.WriteMessage("Aborting FirstRunWizard."); + // flush the log and exit immediately + Log.FlushLog(); + System.Environment.Exit(-1); + } + else + { + // reset FirstRun property + Properties.Settings.Default.FirstRun = false; + Properties.Settings.Default.FirstRun = Properties.Settings.Default.FirstRun; + // set privacy statements (for legacy) + Properties.Settings.Default.First_Agree = true; + Properties.Settings.Default.First_Disagree = false; + Properties.Settings.Default.First_Privacy = false; + + // save settings + SaveUserSettings(); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.Message); + // flush the log and exit immediately + Log.FlushLog(); + System.Environment.Exit(-1); + } + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.Message); + } + // refresh the Layout + OnSizeChanged(null); + // get initial width of boxes + gb_Map_Info_Width = gb_Map_Info.Width; + + // get initial height of boxes + gb_Map_Info_MinimizedHeight = gb_Map_Info.Height - gb_Map_Info.DisplayRectangle.Height; + gb_Map_Zoom_MinimizedHeight = gb_Map_Zoom.Height - gb_Map_Zoom.DisplayRectangle.Height; + gb_Map_Filter_MinimizedHeigth = gb_Map_Filter.Height - gb_Map_Filter.DisplayRectangle.Height; + gb_Map_Alarms_MinimizedHeight = gb_Map_Alarms.Height - gb_Map_Alarms.DisplayRectangle.Height; + gb_Map_Info_MaximizedHeight = gb_Map_Info.Height; + gb_Map_Zoom_MaximizedHeight = gb_Map_Zoom.Height; + gb_Map_Filter_MaximizedHeigth = gb_Map_Filter.Height; + gb_Map_Alarms_MaximizedHeight = gb_Map_Alarms.Height; + + // correct splitter values if default + if (Properties.Settings.Default.MainSplitter_Distance <= 0) + Properties.Settings.Default.MainSplitter_Distance = this.Width - gb_Map_Info_Width; + if (Properties.Settings.Default.MapSplitter_Distance <= 0) + Properties.Settings.Default.MapSplitter_Distance = this.Height - 300; + + // check directories and settings for missing values + CheckDirectories(); + CheckSettings(); + + // select plane feeds again + foreach (PlaneFeed feed in PlaneFeeds) + { + if (Properties.Settings.Default.Planes_PlaneFeed1 == feed.Name) + bw_PlaneFeed1 = feed; + if (Properties.Settings.Default.Planes_PlaneFeed2 == feed.Name) + bw_PlaneFeed2 = feed; + if (Properties.Settings.Default.Planes_PlaneFeed3 == feed.Name) + bw_PlaneFeed3 = feed; + } + + // start permanent background workers + StartAllBackgroundWorkers(); + + // start main timer + ti_Progress.Start(); + + FirstRun = false; + + // check if a vaild feed is on + if ((bw_PlaneFeed1 == null) && (bw_PlaneFeed2 == null) && (bw_PlaneFeed3 == null)) + MessageBox.Show("Plane Feeds are disabled. \n\nYou can use the software anyway, but you will never see a plane on the map. \nIn order to get valid plane positions do the following:\n\n1. Go to the \"Options/Plane\" tab and activate at least one plane feed\n2. Go to the \"Options/General\" tab and adjust \"Planes Positions Range\" to a suitable area around your location", "Plane Feeds Disabled"); + + // invalidate tracking values + Properties.Settings.Default.Track_SetAz = double.NaN; + Properties.Settings.Default.Track_SetEl = double.NaN; + + // set online mode by default + Properties.Settings.Default.Time_Mode_Online = true; + + // install OnIdle event handler + // must be here at first! + Application.Idle += new EventHandler(OnIdle); + // move map to MyLoc + if (!double.IsNaN(Properties.Settings.Default.MyLat) && !double.IsNaN(Properties.Settings.Default.MyLon)) + gm_Main.Position = new PointLatLng(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon); + // update status + UpdateStatus(); + // show airports on map + UpdateAirports(); + // show watchlist on map + UpdateWatchlistInMap(); + // set special event handlers on locator combo boxes + // set callsign history and locs + cb_MyCall_TextChanged(this, null); + // set MyLoc and DXLoc combobox properties + cb_MyLoc.DisplayMember = nameof(LocatorDropDownItem.Locator); + cb_MyLoc.ValueMember = nameof(LocatorDropDownItem.GeoLocation); + cb_MyLoc.Precision = (int)Properties.Settings.Default.Locator_MaxLength / 2; + cb_MyLoc.SmallLettersForSubsquares = Properties.Settings.Default.Locator_SmallLettersForSubsquares; + cb_MyLoc.AutoLength = Properties.Settings.Default.Locator_AutoLength; + cb_DXLoc.DisplayMember = nameof(LocatorDropDownItem.Locator); + cb_DXLoc.ValueMember = nameof(LocatorDropDownItem.GeoLocation); + cb_DXLoc.Precision = (int)Properties.Settings.Default.Locator_MaxLength / 2; + cb_DXLoc.SmallLettersForSubsquares = Properties.Settings.Default.Locator_SmallLettersForSubsquares; + cb_DXLoc.AutoLength = Properties.Settings.Default.Locator_AutoLength; + // populate watchlist + RefreshWatchlistView(); + // set players bounds + sb_Analysis_Play.Minimum = 0; + sb_Analysis_Play.Maximum = int.MaxValue; + // set path mode to single + PathMode = AIRSCOUTPATHMODE.SINGLE; + // set life mode to life + LifeMode = AIRSCOUTLIFEMODE.LIFE; + // set play mode to pause + PlayMode = AIRSCOUTPLAYMODE.PAUSE; + + // maintain background calculations thread wait + Properties.Settings.Default.Background_Calculations_ThreadWait = 0; + Log.WriteMessage("Finished."); + // start timer to finish startup + ti_Startup.Start(); + } + catch (Exception ex) + { + // close the application in case of any exception + Log.WriteMessage(ex.ToString()); + // close the splash window + if (SplashDlg != null) + SplashDlg.Close(); + MessageBox.Show("An error occured during startup: " + ex.ToString() + "\n\nPress >OK< to close the application.", "AirScout", MessageBoxButtons.OK); + this.Close(); + } + } + + private void FinishStartup() + { + // finish startup + // close splash window + // set window layout + if (SplashDlg != null) + SplashDlg.Close(); + // restore window size, state and location + try + { + if (!SupportFunctions.IsMono) + { + this.Size = Properties.Settings.Default.General_WindowSize; + this.Location = Properties.Settings.Default.General_WindowLocation; + this.WindowState = Properties.Settings.Default.General_WindowState; + // set splitter positions + sc_Map.SplitterDistance = Properties.Settings.Default.MapSplitter_Distance; + sc_Main.SplitterDistance = Properties.Settings.Default.MainSplitter_Distance; + CurrentMapSplitterDistance = Properties.Settings.Default.MapSplitter_Distance; + // hide latest news tab + tp_News.Hide(); + } + else + { + // ignore window settings under Linux/Mono + // start always maximized + this.WindowState = FormWindowState.Maximized; + sc_Map.SplitterDistance = this.Height - 220; + sc_Main.SplitterDistance = this.Width - gb_Map_Info_Width; + + } + } + catch (Exception ex) + { + // do nothing if failed + Log.WriteMessage(ex.Message); + } + // make main window visible + this.Visible = true; + // set focus to map + this.btn_Map_Save.Focus(); + // Linux/Mono compatibility + // simulate splitter click + this.sc_Map_SplitterMoved(this, null); + this.sc_Main_SplitterMoved(this, null); + } + + private void StartAllBackgroundWorkers() + { + // start all background workers + // check if the thread is not NULL and not activated + Say("Starting background threads..."); + if ((bw_AirportMapper != null) && !bw_AirportMapper.IsBusy) + bw_AirportMapper.RunWorkerAsync(); + if ((bw_JSONWriter != null) && !bw_JSONWriter.IsBusy) + bw_JSONWriter.RunWorkerAsync(); + if ((bw_NewsFeed != null) && !bw_NewsFeed.IsBusy) + bw_NewsFeed.RunWorkerAsync(); + if ((bw_AircraftDatabaseMaintainer != null) && (!bw_AircraftDatabaseMaintainer.IsBusy)) + { + AircraftPositionDatabaseMaintainerStartOptions startoptions = new AircraftPositionDatabaseMaintainerStartOptions(); + startoptions.Name = "Aircrafts"; + startoptions.Database_MaxCount = (long)Properties.Settings.Default.AircraftDatabase_MaxCount; + startoptions.Database_MaxSize = (double)Properties.Settings.Default.AircraftDatabase_MaxSize; + startoptions.Database_MaxDaysLifetime = (int)Properties.Settings.Default.AircraftDatabase_MaxDaysLifetime; + bw_AircraftDatabaseMaintainer.RunWorkerAsync(startoptions); + } + if (Properties.Settings.Default.Background_Update_OnStartup) + { + if ((bw_StationDatabaseUpdater != null) && !bw_StationDatabaseUpdater.IsBusy) + { + StationDatabaseUpdaterStartOptions startoptions = new StationDatabaseUpdaterStartOptions(); + startoptions.Name = "Stations"; + startoptions.Options = BACKGROUNDUPDATERSTARTOPTIONS.RUNONCE; + bw_StationDatabaseUpdater.RunWorkerAsync(startoptions); + } + if ((bw_AircraftDatabaseUpdater != null) && !bw_AircraftDatabaseUpdater.IsBusy) + { + AircraftDatabaseUpdaterStartOptions startoptions = new AircraftDatabaseUpdaterStartOptions(); + startoptions.Name = "Aircrafts"; + startoptions.Options = BACKGROUNDUPDATERSTARTOPTIONS.RUNONCE; + bw_AircraftDatabaseUpdater.RunWorkerAsync(startoptions); + } + if (Properties.Settings.Default.Elevation_GLOBE_Enabled && (bw_GLOBEUpdater != null) && !bw_GLOBEUpdater.IsBusy) + { + ElevationDatabaseUpdaterStartOptions startoptions = new ElevationDatabaseUpdaterStartOptions(); + startoptions.Name = "GLOBE"; + startoptions.Options = BACKGROUNDUPDATERSTARTOPTIONS.RUNONCE; + startoptions.Model = ELEVATIONMODEL.GLOBE; + startoptions.MinLat = Properties.Settings.Default.MinLat; + startoptions.MinLon = Properties.Settings.Default.MinLon; + startoptions.MaxLat = Properties.Settings.Default.MaxLat; + startoptions.MaxLon = Properties.Settings.Default.MaxLon; + startoptions.FileCacheEnabled = Properties.Settings.Default.Elevation_GLOBE_EnableCache; + bw_GLOBEUpdater.RunWorkerAsync(startoptions); + } + if (Properties.Settings.Default.Elevation_SRTM3_Enabled && (bw_SRTM3Updater != null) && !bw_SRTM3Updater.IsBusy) + { + ElevationDatabaseUpdaterStartOptions startoptions = new ElevationDatabaseUpdaterStartOptions(); + startoptions.Name = "SRTM3"; + startoptions.Options = BACKGROUNDUPDATERSTARTOPTIONS.RUNONCE; + startoptions.Model = ELEVATIONMODEL.SRTM3; + startoptions.MinLat = Properties.Settings.Default.MinLat; + startoptions.MinLon = Properties.Settings.Default.MinLon; + startoptions.MaxLat = Properties.Settings.Default.MaxLat; + startoptions.MaxLon = Properties.Settings.Default.MaxLon; + startoptions.FileCacheEnabled = Properties.Settings.Default.Elevation_SRTM3_EnableCache; + bw_SRTM3Updater.RunWorkerAsync(startoptions); + } + if (Properties.Settings.Default.Elevation_SRTM1_Enabled && (bw_SRTM1Updater != null) && !bw_SRTM1Updater.IsBusy) + { + ElevationDatabaseUpdaterStartOptions startoptions = new ElevationDatabaseUpdaterStartOptions(); + startoptions.Name = "SRTM1"; + startoptions.Options = BACKGROUNDUPDATERSTARTOPTIONS.RUNONCE; + startoptions.Model = ELEVATIONMODEL.SRTM1; + startoptions.MinLat = Properties.Settings.Default.MinLat; + startoptions.MinLon = Properties.Settings.Default.MinLon; + startoptions.MaxLat = Properties.Settings.Default.MaxLat; + startoptions.MaxLon = Properties.Settings.Default.MaxLon; + startoptions.FileCacheEnabled = Properties.Settings.Default.Elevation_SRTM1_EnableCache; + bw_SRTM1Updater.RunWorkerAsync(startoptions); + } + if (Properties.Settings.Default.Elevation_GLOBE_Enabled && (bw_GLOBEPathCalculator != null) && !bw_GLOBEPathCalculator.IsBusy) + bw_GLOBEPathCalculator.RunWorkerAsync(BACKGROUNDUPDATERSTARTOPTIONS.RUNONCE); + if (Properties.Settings.Default.Elevation_SRTM3_Enabled && (bw_SRTM3PathCalculator != null) && !bw_SRTM3PathCalculator.IsBusy) + bw_SRTM3PathCalculator.RunWorkerAsync(BACKGROUNDUPDATERSTARTOPTIONS.RUNONCE); + if (Properties.Settings.Default.Elevation_SRTM1_Enabled && (bw_SRTM1PathCalculator != null) && !bw_SRTM1PathCalculator.IsBusy) + bw_SRTM1PathCalculator.RunWorkerAsync(BACKGROUNDUPDATERSTARTOPTIONS.RUNONCE); + } + else if (Properties.Settings.Default.Background_Update_Periodically) + { + if ((bw_StationDatabaseUpdater != null) && !bw_StationDatabaseUpdater.IsBusy) + { + StationDatabaseUpdaterStartOptions startoptions = new StationDatabaseUpdaterStartOptions(); + startoptions.Name = "Stations"; + startoptions.Options = BACKGROUNDUPDATERSTARTOPTIONS.RUNPERIODICALLY; + bw_StationDatabaseUpdater.RunWorkerAsync(startoptions); + } + if ((bw_AircraftDatabaseUpdater != null) && !bw_AircraftDatabaseUpdater.IsBusy) + { + AircraftDatabaseUpdaterStartOptions startoptions = new AircraftDatabaseUpdaterStartOptions(); + startoptions.Name = "Aircrafts"; + startoptions.Options = BACKGROUNDUPDATERSTARTOPTIONS.RUNPERIODICALLY; + bw_AircraftDatabaseUpdater.RunWorkerAsync(startoptions); + } + if (Properties.Settings.Default.Elevation_GLOBE_Enabled && (bw_GLOBEUpdater != null) && !bw_GLOBEUpdater.IsBusy) + { + ElevationDatabaseUpdaterStartOptions startoptions = new ElevationDatabaseUpdaterStartOptions(); + startoptions.Name = "GLOBE"; + startoptions.Options = BACKGROUNDUPDATERSTARTOPTIONS.RUNPERIODICALLY; + startoptions.Model = ELEVATIONMODEL.GLOBE; + startoptions.MinLat = Properties.Settings.Default.MinLat; + startoptions.MinLon = Properties.Settings.Default.MinLon; + startoptions.MaxLat = Properties.Settings.Default.MaxLat; + startoptions.MaxLon = Properties.Settings.Default.MaxLon; + startoptions.FileCacheEnabled = Properties.Settings.Default.Elevation_GLOBE_EnableCache; + bw_GLOBEUpdater.RunWorkerAsync(startoptions); + } + if (Properties.Settings.Default.Elevation_SRTM3_Enabled && (bw_SRTM3Updater != null) && !bw_SRTM3Updater.IsBusy) + { + ElevationDatabaseUpdaterStartOptions startoptions = new ElevationDatabaseUpdaterStartOptions(); + startoptions.Name = "SRTM3"; + startoptions.Options = BACKGROUNDUPDATERSTARTOPTIONS.RUNPERIODICALLY; + startoptions.Model = ELEVATIONMODEL.SRTM3; + startoptions.MinLat = Properties.Settings.Default.MinLat; + startoptions.MinLon = Properties.Settings.Default.MinLon; + startoptions.MaxLat = Properties.Settings.Default.MaxLat; + startoptions.MaxLon = Properties.Settings.Default.MaxLon; + startoptions.FileCacheEnabled = Properties.Settings.Default.Elevation_SRTM3_EnableCache; + bw_SRTM3Updater.RunWorkerAsync(startoptions); + } + if (Properties.Settings.Default.Elevation_SRTM1_Enabled && (bw_SRTM1Updater != null) && !bw_SRTM1Updater.IsBusy) + { + ElevationDatabaseUpdaterStartOptions startoptions = new ElevationDatabaseUpdaterStartOptions(); + startoptions.Name = "SRTM1"; + startoptions.Options = BACKGROUNDUPDATERSTARTOPTIONS.RUNPERIODICALLY; + startoptions.Model = ELEVATIONMODEL.SRTM1; + startoptions.MinLat = Properties.Settings.Default.MinLat; + startoptions.MinLon = Properties.Settings.Default.MinLon; + startoptions.MaxLat = Properties.Settings.Default.MaxLat; + startoptions.MaxLon = Properties.Settings.Default.MaxLon; + startoptions.FileCacheEnabled = Properties.Settings.Default.Elevation_SRTM1_EnableCache; + bw_SRTM1Updater.RunWorkerAsync(startoptions); + } + if (Properties.Settings.Default.Elevation_GLOBE_Enabled && (bw_GLOBEPathCalculator != null) && !bw_GLOBEPathCalculator.IsBusy) + bw_GLOBEPathCalculator.RunWorkerAsync(BACKGROUNDUPDATERSTARTOPTIONS.RUNPERIODICALLY); + if (Properties.Settings.Default.Elevation_SRTM3_Enabled && (bw_SRTM3PathCalculator != null) && !bw_SRTM3PathCalculator.IsBusy) + bw_SRTM3PathCalculator.RunWorkerAsync(BACKGROUNDUPDATERSTARTOPTIONS.RUNPERIODICALLY); + if (Properties.Settings.Default.Elevation_SRTM1_Enabled && (bw_SRTM1PathCalculator != null) && !bw_SRTM1PathCalculator.IsBusy) + bw_SRTM1PathCalculator.RunWorkerAsync(BACKGROUNDUPDATERSTARTOPTIONS.RUNPERIODICALLY); + } + if ((bw_PlaneFeed1 != null) && (!bw_PlaneFeed1.IsBusy)) + { + PlaneFeedWorkEventArgs feedargs = new PlaneFeedWorkEventArgs(); + feedargs.AppDirectory = AppDirectory; + feedargs.AppDataDirectory = AppDataDirectory; + feedargs.LogDirectory = LogDirectory; + feedargs.TmpDirectory = TmpDirectory; + feedargs.DatabaseDirectory = DatabaseDirectory; + feedargs.MaxLat = Properties.Settings.Default.MaxLat; + feedargs.MinLon = Properties.Settings.Default.MinLon; + feedargs.MinLat = Properties.Settings.Default.MinLat; + feedargs.MaxLon = Properties.Settings.Default.MaxLon; + feedargs.MinAlt = Properties.Settings.Default.Planes_MinAlt; + feedargs.MaxAlt = Properties.Settings.Default.Planes_MaxAlt; + feedargs.MyLat = Properties.Settings.Default.MyLat; + feedargs.MyLon = Properties.Settings.Default.MyLon; + feedargs.DXLat = Properties.Settings.Default.DXLat; + feedargs.DXLon = Properties.Settings.Default.DXLon; + feedargs.KeepHistory = Properties.Settings.Default.Planes_KeepHistory; + bw_PlaneFeed1.RunWorkerAsync(feedargs); + } + if ((bw_PlaneFeed2 != null) && (!bw_PlaneFeed2.IsBusy)) + { + PlaneFeedWorkEventArgs feedargs = new PlaneFeedWorkEventArgs(); + feedargs.AppDirectory = AppDirectory; + feedargs.AppDataDirectory = AppDataDirectory; + feedargs.LogDirectory = LogDirectory; + feedargs.TmpDirectory = TmpDirectory; + feedargs.DatabaseDirectory = DatabaseDirectory; + feedargs.MaxLat = Properties.Settings.Default.MaxLat; + feedargs.MinLon = Properties.Settings.Default.MinLon; + feedargs.MinLat = Properties.Settings.Default.MinLat; + feedargs.MaxLon = Properties.Settings.Default.MaxLon; + feedargs.MinAlt = Properties.Settings.Default.Planes_MinAlt; + feedargs.MaxAlt = Properties.Settings.Default.Planes_MaxAlt; + feedargs.MyLat = Properties.Settings.Default.MyLat; + feedargs.MyLon = Properties.Settings.Default.MyLon; + feedargs.DXLat = Properties.Settings.Default.DXLat; + feedargs.DXLon = Properties.Settings.Default.DXLon; + feedargs.KeepHistory = Properties.Settings.Default.Planes_KeepHistory; + bw_PlaneFeed2.RunWorkerAsync(feedargs); + } + if ((bw_PlaneFeed3 != null) && (!bw_PlaneFeed3.IsBusy)) + { + PlaneFeedWorkEventArgs feedargs = new PlaneFeedWorkEventArgs(); + feedargs.AppDirectory = AppDirectory; + feedargs.AppDataDirectory = AppDataDirectory; + feedargs.LogDirectory = LogDirectory; + feedargs.TmpDirectory = TmpDirectory; + feedargs.DatabaseDirectory = DatabaseDirectory; + feedargs.MaxLat = Properties.Settings.Default.MaxLat; + feedargs.MinLon = Properties.Settings.Default.MinLon; + feedargs.MinLat = Properties.Settings.Default.MinLat; + feedargs.MaxLon = Properties.Settings.Default.MaxLon; + feedargs.MinAlt = Properties.Settings.Default.Planes_MinAlt; + feedargs.MaxAlt = Properties.Settings.Default.Planes_MaxAlt; + feedargs.MyLat = Properties.Settings.Default.MyLat; + feedargs.MyLon = Properties.Settings.Default.MyLon; + feedargs.DXLat = Properties.Settings.Default.DXLat; + feedargs.DXLon = Properties.Settings.Default.DXLon; + feedargs.KeepHistory = Properties.Settings.Default.Planes_KeepHistory; + bw_PlaneFeed3.RunWorkerAsync(feedargs); + } + if (Properties.Settings.Default.Server_Activate) + { + if ((bw_WinTestReceive != null) && (!bw_WinTestReceive.IsBusy)) + bw_WinTestReceive.RunWorkerAsync(); + if ((bw_Webserver != null) && (!bw_Webserver.IsBusy)) + bw_Webserver.RunWorkerAsync(); + } + if (Properties.Settings.Default.SpecLab_Enabled) + { + if ((bw_SpecLab_Receive != null) && (!bw_SpecLab_Receive.IsBusy)) + bw_SpecLab_Receive.RunWorkerAsync(); + } + if (Properties.Settings.Default.Track_Activate) + { + if ((bw_Track != null) && (!bw_Track.IsBusy)) + bw_Track.RunWorkerAsync(); + } + Say("Background threads started."); + } + + private void StopBackgroundworker(BackgroundWorker worker, string name, int count, int total) + { + if (worker == null) + return; + if (!worker.IsBusy) + return; + worker.CancelAsync(); + // waiting for background threads to finish + Stopwatch st = new Stopwatch(); + st.Start(); + Say("Stopping background thread " + count.ToString() + " of " + total.ToString() + " [" + name + "]..."); + while ((worker != null) && worker.IsBusy) + Application.DoEvents(); + st.Stop(); + Log.WriteMessage("Stopping " + name + ", " + st.ElapsedMilliseconds.ToString() + " ms."); + } + + private void StopAllBackgroundWorkers() + { + Say("Stopping background threads..."); + // cancel permanent background workers, wait for finish + int bcount = 13; + int i = 1; + // cancel all threads + StopBackgroundworker(bw_WinTestReceive, nameof(bw_WinTestReceive), i, bcount); i++; + StopBackgroundworker(bw_SpecLab_Receive, nameof(bw_SpecLab_Receive), i, bcount); i++; + StopBackgroundworker(bw_Track, nameof(bw_Track), i, bcount); i++; + StopBackgroundworker(bw_JSONWriter, nameof(bw_JSONWriter), i, bcount); i++; + StopBackgroundworker(bw_NewsFeed, nameof(bw_NewsFeed), i, bcount); i++; + StopBackgroundworker(bw_PlaneFeed1, nameof(bw_PlaneFeed1), i, bcount); i++; + StopBackgroundworker(bw_PlaneFeed2, nameof(bw_PlaneFeed2), i, bcount); i++; + StopBackgroundworker(bw_PlaneFeed3, nameof(bw_PlaneFeed3), i, bcount); i++; + StopBackgroundworker(bw_GLOBEPathCalculator, nameof(bw_GLOBEPathCalculator), i, bcount); i++; + StopBackgroundworker(bw_SRTM3PathCalculator, nameof(bw_SRTM3PathCalculator), i, bcount); i++; + StopBackgroundworker(bw_SRTM1PathCalculator, nameof(bw_SRTM1PathCalculator), i, bcount); i++; + StopBackgroundworker(bw_AircraftDatabaseUpdater, nameof(bw_AircraftDatabaseUpdater), i, bcount); i++; + StopBackgroundworker(bw_StationDatabaseUpdater, nameof(bw_StationDatabaseUpdater), i, bcount); i++; + Say("Background threads stopped."); + } + + private void CancelAllBackgroundWorkers() + { + // cancel all background workers, don't wait for finish + if (bw_AirportMapper != null) + bw_AirportMapper.CancelAsync(); + if (bw_PlaneFeed1 != null) + bw_PlaneFeed1.CancelAsync(); + if (bw_PlaneFeed2 != null) + bw_PlaneFeed2.CancelAsync(); + if (bw_PlaneFeed3 != null) + bw_PlaneFeed3.CancelAsync(); + if (bw_WinTestReceive != null) + bw_WinTestReceive.CancelAsync(); + if (bw_SpecLab_Receive != null) + bw_SpecLab_Receive.CancelAsync(); + if (bw_Track != null) + bw_Track.CancelAsync(); + if (bw_JSONWriter != null) + bw_JSONWriter.CancelAsync(); + if (bw_Webserver != null) + bw_Webserver.CancelAsync(); + if (bw_NewsFeed != null) + bw_NewsFeed.CancelAsync(); + if (bw_HistoryDownloader != null) + bw_HistoryDownloader.CancelAsync(); + if (bw_StationDatabaseUpdater != null) + bw_StationDatabaseUpdater.CancelAsync(); + if (bw_AircraftDatabaseMaintainer != null) + bw_AircraftDatabaseMaintainer.CancelAsync(); + if (bw_AircraftDatabaseUpdater != null) + bw_AircraftDatabaseUpdater.CancelAsync(); + if (bw_GLOBEPathCalculator != null) + bw_GLOBEUpdater.CancelAsync(); + if (bw_SRTM3Updater != null) + bw_SRTM3Updater.CancelAsync(); + if (bw_SRTM1Updater != null) + bw_SRTM1Updater.CancelAsync(); + if (bw_GLOBEPathCalculator != null) + bw_GLOBEPathCalculator.CancelAsync(); + if (bw_SRTM3PathCalculator != null) + bw_SRTM3PathCalculator.CancelAsync(); + if (bw_SRTM1PathCalculator != null) + bw_SRTM1PathCalculator.CancelAsync(); + } + + private Bitmap CreatePlaneIcon(Color color) + { + // get the basic icon + Bitmap bm = new Bitmap(AppDirectory + Path.DirectorySeparatorChar + Properties.Settings.Default.Planes_IconFileName); + // read the content and change color of each pixel + for (int j = 0; j < bm.Width; j++) + { + for (int k = 0; k < bm.Height; k++) + { + // get the color of each pixel + Color c = bm.GetPixel(j, k); + // check if not transparent + if (c.A > 0) + { + // change color + bm.SetPixel(j, k, Color.FromArgb(c.A, color.R, color.G, color.B)); + } + } + } + return bm; + } + + public static Color ColorFromHSV(double hue, double saturation, double value) + { + int hi = Convert.ToInt32(Math.Floor(hue / 60)) % 6; + double f = hue / 60 - Math.Floor(hue / 60); + + value = value * 255; + int v = Convert.ToInt32(value); + int p = Convert.ToInt32(value * (1 - saturation)); + int q = Convert.ToInt32(value * (1 - f * saturation)); + int t = Convert.ToInt32(value * (1 - (1 - f) * saturation)); + + if (hi == 0) + return Color.FromArgb(255, v, t, p); + else if (hi == 1) + return Color.FromArgb(255, q, v, p); + else if (hi == 2) + return Color.FromArgb(255, p, v, t); + else if (hi == 3) + return Color.FromArgb(255, p, q, v); + else if (hi == 4) + return Color.FromArgb(255, t, p, v); + else + return Color.FromArgb(255, v, p, q); + } + + public Color GetColor(double power) + { + double H = power * 0.3; // Hue (note 0.4 = Green, see huge chart below) + double S = 0.95; // Saturation + double B = 0.95; // Brightness + + return ColorFromHSV((float)H * 360, (float)S, (float)B); + } + + private Bitmap CreateAirportIcon(int alpha) + { + // get the basic icon + Bitmap bm = new Bitmap(AppDirectory + Path.DirectorySeparatorChar + Properties.Settings.Default.Airports_IconFileName); + // read the content and change opacity of each pixel + for (int j = 0; j < bm.Width; j++) + { + for (int k = 0; k < bm.Height; k++) + { + // get the color of each pixel + Color c = bm.GetPixel(j, k); + // check if not transparent + if (c.A > 0) + { + // change color + bm.SetPixel(j, k, Color.FromArgb(alpha, c.R, c.G, c.B)); + } + } + } + return bm; + } + + private void InitializeIcons() + { + // create extra icons regular size + Log.WriteMessage("Started."); + try + { + // now generate 0% - 100% colored planes + for (int i = 0; i <= 100; i++) + { + Bitmap bm = CreatePlaneIcon(GetColor(1.0f - (float)i / 100.0f)); + il_Planes_L.Images.Add(bm); + il_Planes_M.Images.Add(bm); + il_Planes_H.Images.Add(bm); + il_Planes_S.Images.Add(bm); + } + il_Planes_L.Images.Add(CreatePlaneIcon(Color.Gray)); + il_Planes_M.Images.Add(CreatePlaneIcon(Color.Gray)); + il_Planes_H.Images.Add(CreatePlaneIcon(Color.Gray)); + il_Planes_S.Images.Add(CreatePlaneIcon(Color.Gray)); + bmindex_gray = il_Planes_M.Images.Count - 1; + il_Planes_L.Images.Add(CreatePlaneIcon(Color.LightGreen)); + il_Planes_M.Images.Add(CreatePlaneIcon(Color.LightGreen)); + il_Planes_H.Images.Add(CreatePlaneIcon(Color.LightGreen)); + il_Planes_S.Images.Add(CreatePlaneIcon(Color.LightGreen)); + bmindex_lightgreen = il_Planes_M.Images.Count - 1; + il_Planes_L.Images.Add(CreatePlaneIcon(Color.DarkOrange)); + il_Planes_M.Images.Add(CreatePlaneIcon(Color.DarkOrange)); + il_Planes_H.Images.Add(CreatePlaneIcon(Color.DarkOrange)); + il_Planes_S.Images.Add(CreatePlaneIcon(Color.DarkOrange)); + bmindex_darkorange = il_Planes_M.Images.Count - 1; + il_Planes_L.Images.Add(CreatePlaneIcon(Color.Red)); + il_Planes_M.Images.Add(CreatePlaneIcon(Color.Red)); + il_Planes_H.Images.Add(CreatePlaneIcon(Color.Red)); + il_Planes_S.Images.Add(CreatePlaneIcon(Color.Red)); + bmindex_red = il_Planes_M.Images.Count - 1; + il_Planes_L.Images.Add(CreatePlaneIcon(Color.Magenta)); + il_Planes_M.Images.Add(CreatePlaneIcon(Color.Magenta)); + il_Planes_H.Images.Add(CreatePlaneIcon(Color.Magenta)); + il_Planes_S.Images.Add(CreatePlaneIcon(Color.Magenta)); + bmindex_magenta = il_Planes_M.Images.Count - 1; + il_Airports.Images.Add(CreateAirportIcon(255)); + } + catch (Exception ex) + { + Log.WriteMessage(ex.Message); + } + Log.WriteMessage("Finished."); + } + + private void InitializeCharts() + { + // propagation path chart + pm_Path.Title = String.Empty; + pm_Path.DefaultFontSize = 6F; + pm_Path.IsLegendVisible = false; + pv_Path.BackColor = Color.White; + pv_Path.Model = pm_Path; + // add axes + pm_Path.Axes.Clear(); + // add X-axis + Path_X.IsZoomEnabled = false; + Path_X.Maximum = 1000; + Path_X.Minimum = 0; + Path_X.MajorGridlineStyle = LineStyle.Solid; + Path_X.MinorGridlineStyle = LineStyle.Dot; + Path_X.Position = AxisPosition.Bottom; + this.pm_Path.Axes.Add(Path_X); + // add Y-axis + Path_Y.IsZoomEnabled = false; + Path_Y.Maximum = 20000; + Path_Y.Minimum = 0; + Path_Y.MajorGridlineStyle = LineStyle.Solid; + Path_Y.MinorGridlineStyle = LineStyle.Dot; + Path_Y.Position = AxisPosition.Left; + this.pm_Path.Axes.Add(Path_Y); + // add series + pm_Path.Series.Clear(); + Path_Elevation.Title = "Elevation"; + Min_H1.Title = "Min_H1"; + Min_H1.StrokeThickness = 2; + Min_H1.LineStyle = LineStyle.Solid; + Min_H1.Color = OxyColors.Red; + Min_H2.Title = "Min_H2"; + Min_H2.StrokeThickness = 2; + Min_H2.LineStyle = LineStyle.Solid; + Min_H2.Color = OxyColors.Gold; + Max_H.Title = "Max_H"; + Max_H.StrokeThickness = 2; + Max_H.LineStyle = LineStyle.Dot; + Max_H.Color = OxyColors.DarkBlue; + Min_H.Title = "Min_H"; + Min_H.StrokeThickness = 0; + Min_H.LineStyle = LineStyle.Solid; + Min_H.Color = OxyColors.Magenta.ChangeSaturation(0.4); + Planes_Hi.Title = "Planes_Hi"; + Planes_Hi.Color = OxyColors.Transparent; + Planes_Hi.MarkerType = MarkerType.Square; + Planes_Hi.MarkerFill = OxyColors.Magenta; + Planes_Lo.Title = "Planes_Lo"; + Planes_Lo.Color = OxyColors.Transparent; + Planes_Lo.MarkerType = MarkerType.Square; + Planes_Lo.MarkerFill = OxyColors.Gray; + pm_Path.Series.Add(Path_Elevation); + pm_Path.Series.Add(Min_H1); + pm_Path.Series.Add(Min_H2); + pm_Path.Series.Add(Max_H); + pm_Path.Series.Add(Min_H); + pm_Path.Series.Add(Planes_Hi); + pm_Path.Series.Add(Planes_Lo); + // add legend + pm_Path.LegendTitle = ""; + pm_Path.LegendPosition = LegendPosition.TopRight; + pm_Path.LegendBackground = OxyColors.White; + pm_Path.LegendBorder = OxyColors.Black; + pm_Path.LegendBorderThickness = 1; + // add control + this.tp_Elevation.Controls.Add(pv_Path); + pv_Path.Paint += new PaintEventHandler(pv_Path_Paint); + + // zoomed elevation chart + pm_Elevation.Title = String.Empty; + pm_Elevation.DefaultFontSize = 6F; + pm_Elevation.IsLegendVisible = false; + pv_Elevation.BackColor = Color.White; + pv_Elevation.Model = pm_Elevation; + // add series + pm_Elevation.Series.Clear(); + Elevation.Title = "Elevation"; + LOS.Title = "LOS"; + LOS.StrokeThickness = 2; + LOS.Color = OxyColors.Black; + pm_Elevation.Series.Add(Elevation); + pm_Elevation.Series.Add(LOS); + // create axes + pm_Elevation.Axes.Clear(); + // add X-axis + Elevation_X.IsZoomEnabled = false; + Elevation_X.Maximum = 1000; + Elevation_X.Minimum = 0; + Elevation_X.MajorGridlineStyle = LineStyle.Solid; + Elevation_X.MinorGridlineStyle = LineStyle.Dot; + Elevation_X.Position = AxisPosition.Bottom; + this.pm_Elevation.Axes.Add(Elevation_X); + // add Y-axis + Elevation_Y.IsZoomEnabled = false; + // auto size maximum + // Elevation_Y.Maximum = maxelv, + Elevation_Y.Minimum = 0; + Elevation_Y.MajorGridlineStyle = LineStyle.Solid; + Elevation_Y.MinorGridlineStyle = LineStyle.Dot; + Elevation_Y.Position = AxisPosition.Left; + this.pm_Elevation.Axes.Add(Elevation_Y); + // add legend + pm_Elevation.LegendTitle = ""; + pm_Elevation.LegendPosition = LegendPosition.TopRight; + pm_Elevation.LegendBackground = OxyColors.White; + pm_Elevation.LegendBorder = OxyColors.Black; + pm_Elevation.LegendBorderThickness = 1; + // add control + this.tp_Elevation.Controls.Add(pv_Elevation); + + // spectrum chart + pm_Spectrum.Title = String.Empty; + pm_Spectrum.DefaultFontSize = 6F; + pv_Spectrum.BackColor = Color.White; + pv_Spectrum.Model = pm_Spectrum; + // add Spectrum series + pm_Spectrum.Series.Clear(); + // create axes + pm_Spectrum.Axes.Clear(); + // add X-axis + Spectrum_X.IsZoomEnabled = false; + Spectrum_X.Maximum = SpectrumMaxPoints; + Spectrum_X.Minimum = 0; + Spectrum_X.MajorGridlineStyle = LineStyle.Solid; + Spectrum_X.MinorGridlineStyle = LineStyle.Dot; + Spectrum_X.Position = AxisPosition.Bottom; + this.pm_Spectrum.Axes.Add(Spectrum_X); + // add Y-axis + Spectrum_Y.IsZoomEnabled = false; + Spectrum_Y.Maximum = 0; + Spectrum_Y.Minimum = -120; + Spectrum_Y.MajorGridlineStyle = LineStyle.Solid; + Spectrum_Y.MinorGridlineStyle = LineStyle.Dot; + Spectrum_Y.Position = AxisPosition.Left; + this.pm_Spectrum.Axes.Add(Spectrum_Y); + // add series + SpectrumRecord.Color = OxyColors.Magenta; + pm_Spectrum.Series.Add(SpectrumRecord); + Spectrum.InterpolationAlgorithm = OxyPlot.InterpolationAlgorithms.CanonicalSpline; + Spectrum.StrokeThickness = 3; + Spectrum.Color = OxyColors.Goldenrod; + pm_Spectrum.Series.Add(Spectrum); + // add control + this.pv_Spectrum.Dock = DockStyle.Fill; + this.gb_Spectrum.Controls.Add(pv_Spectrum); + return; + + } + + private void InitializeWebbrowser() + { + // do not initialize webbrowser --> not working on all Linux systems + if (SupportFunctions.IsMono) + return; + // iniitialize webbrowser on Windows + this.wb_News = new System.Windows.Forms.WebBrowser(); + // + // wb_News + // + this.wb_News.DataBindings.Add(new System.Windows.Forms.Binding("Url", global::AirScout.Properties.Settings.Default, "News_URL", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.wb_News.Dock = System.Windows.Forms.DockStyle.Fill; + this.wb_News.Location = new System.Drawing.Point(0, 0); + this.wb_News.MinimumSize = new System.Drawing.Size(20, 20); + this.wb_News.Name = "wb_News"; + this.wb_News.Size = new System.Drawing.Size(844, 197); + this.wb_News.TabIndex = 0; + this.wb_News.Url = global::AirScout.Properties.Settings.Default.News_URL; + this.tp_News.Controls.Add(this.wb_News); + } + + private void InitializePassword() + { + // get current AirScout password phrase from website and store it in settings + try + { + // get upload info + WebRequest myWebRequest = WebRequest.Create(Properties.Settings.Default.SFTP_PwdURL); + WebResponse myWebResponse = myWebRequest.GetResponse(); + Stream ReceiveStream = myWebResponse.GetResponseStream(); + Encoding encode = System.Text.Encoding.GetEncoding("utf-8"); + StreamReader readStream = new StreamReader(ReceiveStream, encode); + string s = readStream.ReadToEnd(); + Properties.Settings.Default.Password = s; + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + + private void UpdateAirports() + { + if (!Properties.Settings.Default.Airports_Activate) + return; + if ((Airports == null) || (Airports.Count == 0)) + return; + foreach (AirportDesignator airport in Airports) + { + try + { + GMarkerGoogle gm = new GMarkerGoogle(new PointLatLng(airport.Lat, airport.Lon), ToolTipFont, RotateImageByAngle(il_Airports.Images[0], 0)); + gm.ToolTipText = airport.Airport + "\n" + + airport.IATA + "/" + airport.ICAO; + gm.ToolTipMode = MarkerTooltipMode.OnMouseOver; + gm.Tag = airport.IATA + "," + airport.ICAO; + gmo_Airports.Markers.Add(gm); + } + catch (Exception ex) + { + Log.WriteMessage(ex.Message); + } + } + } + + private void ti_Startup_Tick(object sender, EventArgs e) + { + FinishStartup(); + ti_Startup.Stop(); + } + + + #endregion + + #region User Settings + + private string GetUserSettingsPath() + { + if (!SupportFunctions.IsMono) + return ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal).FilePath; + // try to build a path to user specific settings under Linux/Mono + string usersettingspath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + usersettingspath = Path.Combine(usersettingspath, Application.CompanyName, AppDomain.CurrentDomain.FriendlyName); + usersettingspath += "_Url_"; + Assembly assembly = Assembly.GetEntryAssembly(); + if (assembly == null) + { + assembly = Assembly.GetCallingAssembly(); + } + byte[] pkt = assembly.GetName().GetPublicKeyToken(); + byte[] hash = SHA1.Create().ComputeHash((pkt != null && pkt.Length > 0) ? pkt : Encoding.UTF8.GetBytes(assembly.EscapedCodeBase)); + StringBuilder evidence_string = new StringBuilder(); + byte[] array = hash; + for (int i = 0; i < array.Length; i++) + { + byte b = array[i]; + evidence_string.AppendFormat("{0:x2}", b); + } + usersettingspath += evidence_string.ToString(); + if (!Directory.Exists(usersettingspath)) + { + Directory.CreateDirectory(usersettingspath); + } + usersettingspath = Path.Combine(usersettingspath, "user.config"); + return usersettingspath; + } + + private void SaveUserSettings() + { + Log.WriteMessage("Saving configuration..."); + Properties.Settings.Default.Save(); + if (!SupportFunctions.IsMono) + return; + Console.WriteLine("Creating XML document..."); + XmlDocument doc = new XmlDocument(); + XmlDeclaration xmlDeclaration = doc.CreateXmlDeclaration("1.0", "UTF-8", null); + XmlElement root = doc.DocumentElement; + doc.InsertBefore(xmlDeclaration, root); + XmlElement configuration = doc.CreateElement(string.Empty, "configuration", string.Empty); + doc.AppendChild(configuration); + XmlElement configsections = doc.CreateElement(string.Empty, "configSections", string.Empty); + configuration.AppendChild(configsections); + XmlElement usersettingsgroup = doc.CreateElement(string.Empty, "sectionGroup", string.Empty); + XmlAttribute usersettingsname = doc.CreateAttribute(string.Empty, "name", string.Empty); + usersettingsname.Value = "userSettings"; + usersettingsgroup.Attributes.Append(usersettingsname); + XmlElement usersection = doc.CreateElement(string.Empty, "section", string.Empty); + XmlAttribute sectionname = doc.CreateAttribute(string.Empty, "name", string.Empty); + sectionname.Value = "AirScout.PlaneFeeds.Properties.Settings"; + usersection.Attributes.Append(sectionname); + XmlAttribute sectiontype = doc.CreateAttribute(string.Empty, "type", string.Empty); + sectiontype.Value = "System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"; + usersection.Attributes.Append(sectiontype); + XmlAttribute sectionallowexedefinition = doc.CreateAttribute(string.Empty, "allowExeDefinition", string.Empty); + sectionallowexedefinition.Value = "MachineToLocalUser"; + usersection.Attributes.Append(sectionallowexedefinition); + XmlAttribute sectionrequirepermission = doc.CreateAttribute(string.Empty, "requirePermission", string.Empty); + sectionrequirepermission.Value = "false"; + usersection.Attributes.Append(sectionrequirepermission); + usersettingsgroup.AppendChild(usersection); + configsections.AppendChild(usersettingsgroup); + XmlElement usersettings = doc.CreateElement(string.Empty, "userSettings", string.Empty); + configuration.AppendChild(usersettings); + Console.WriteLine("Writing user settings..."); + // append AirScout.PlaneFeeds properties + Console.WriteLine("Appending AirScout.PlaneFeeds.Properties.Settings.Default node..."); + XmlElement planefeedproperties = doc.CreateElement(string.Empty, AirScout.PlaneFeeds.Properties.Settings.Default.ToString(), string.Empty); + usersettings.AppendChild(planefeedproperties); + foreach (SettingsPropertyValue p in AirScout.PlaneFeeds.Properties.Settings.Default.PropertyValues) + { + if ((p != null) && (p.Name != null) && (p.PropertyValue != null) && !p.UsingDefaultValue) + { + // Console.WriteLine("Appending " + p.Name + " = " + p.PropertyValue.ToString()); + XmlElement setting = doc.CreateElement(string.Empty, "setting", string.Empty); + XmlAttribute name = doc.CreateAttribute(string.Empty, "name", string.Empty); + name.Value = p.Name.ToString(); + setting.Attributes.Append(name); + XmlAttribute serializeas = doc.CreateAttribute(string.Empty, "serializeAs", string.Empty); + serializeas.Value = p.Property.SerializeAs.ToString(); + setting.Attributes.Append(serializeas); + XmlElement value = doc.CreateElement(string.Empty, "value", string.Empty); + if (p.PropertyValue != null && p.Property.SerializeAs == SettingsSerializeAs.String) + { + XmlText text = doc.CreateTextNode(p.SerializedValue.ToString()); + value.AppendChild(text); + } + else + { + if (p.PropertyValue != null && p.Property.SerializeAs == SettingsSerializeAs.Xml) + { + MemoryStream ms = new MemoryStream(); + XmlWriter writer = XmlWriter.Create(ms, new XmlWriterSettings + { + NewLineOnAttributes = true, + OmitXmlDeclaration = true + }); + XmlSerializer serializer = new XmlSerializer(p.PropertyValue.GetType()); + serializer.Serialize(writer, p.PropertyValue); + byte[] text2 = new byte[ms.ToArray().Length - 3]; + Array.Copy(ms.ToArray(), 3, text2, 0, text2.Length); + XmlText xml = doc.CreateTextNode(Encoding.UTF8.GetString(text2.ToArray())); + value.AppendChild(xml); + value.InnerXml = WebUtility.HtmlDecode(value.InnerXml); + } + } + setting.AppendChild(value); + planefeedproperties.AppendChild(setting); + } + } + // append AirScout properties + Console.WriteLine("Appending AirScout.Properties.Settings.Default node..."); + XmlElement properties = doc.CreateElement(string.Empty, Properties.Settings.Default.ToString(), string.Empty); + usersettings.AppendChild(properties); + foreach (SettingsPropertyValue p in Properties.Settings.Default.PropertyValues) + { + if ((p != null) && (p.Name != null) && (p.PropertyValue != null) && !p.UsingDefaultValue) + { + // Console.WriteLine("Appending " + p.Name + " = " + p.PropertyValue.ToString(); + XmlElement setting = doc.CreateElement(string.Empty, "setting", string.Empty); + XmlAttribute name = doc.CreateAttribute(string.Empty, "name", string.Empty); + name.Value = p.Name.ToString(); + setting.Attributes.Append(name); + XmlAttribute serializeas = doc.CreateAttribute(string.Empty, "serializeAs", string.Empty); + serializeas.Value = p.Property.SerializeAs.ToString(); + setting.Attributes.Append(serializeas); + XmlElement value = doc.CreateElement(string.Empty, "value", string.Empty); + if (p.PropertyValue != null && p.Property.SerializeAs == SettingsSerializeAs.String) + { + XmlText text = doc.CreateTextNode(p.SerializedValue.ToString()); + value.AppendChild(text); + } + else + { + if (p.PropertyValue != null && p.Property.SerializeAs == SettingsSerializeAs.Xml) + { + MemoryStream ms = new MemoryStream(); + XmlWriter writer = XmlWriter.Create(ms, new XmlWriterSettings + { + NewLineOnAttributes = true, + OmitXmlDeclaration = true + }); + XmlSerializer serializer = new XmlSerializer(p.PropertyValue.GetType()); + serializer.Serialize(writer, p.PropertyValue); + byte[] text2 = new byte[ms.ToArray().Length - 3]; + Array.Copy(ms.ToArray(), 3, text2, 0, text2.Length); + XmlText xml = doc.CreateTextNode(Encoding.UTF8.GetString(text2.ToArray())); + value.AppendChild(xml); + value.InnerXml = WebUtility.HtmlDecode(value.InnerXml); + } + } + setting.AppendChild(value); + properties.AppendChild(setting); + } + } + doc.Save(GetUserSettingsPath()); + } + + #endregion + + #region Idle + + private void OnIdle(object sender, EventArgs args) + { + // close window if disagreed + if (ForceClose) + { + Application.Exit(); + } + /* + // show/hide group boxes + if (Properties.Settings.Default.Map_ShowInfoBox) + { + gb_Map_Info.ClientSize = new System.Drawing.Size(gb_Map_Info.ClientSize.Width, gb_Map_Info_MaximizedHeight); + if (gb_Map_Info.Text != "˄ Info") + gb_Map_Info.Text = "˄ Info"; + } + else + { + gb_Map_Info.ClientSize = new System.Drawing.Size(gb_Map_Info.ClientSize.Width, gb_Map_Info_MinimizedHeight); + if (gb_Map_Info.Text != "˅ Info") + gb_Map_Info.Text = "˅ Info"; + } + if (Properties.Settings.Default.Map_ShowZoomBox) + { + gb_Map_Zoom.ClientSize = new System.Drawing.Size(gb_Map_Zoom.ClientSize.Width, gb_Map_Zoom_MaximizedHeight); + if (gb_Map_Zoom.Text != "˄ Map Zoom") + gb_Map_Zoom.Text = "˄ Map Zoom"; + } + else + { + gb_Map_Zoom.ClientSize = new System.Drawing.Size(gb_Map_Zoom.ClientSize.Width, gb_Map_Zoom_MinimizedHeight); + if (gb_Map_Zoom.Text != "˅ Map Zoom") + gb_Map_Zoom.Text = "˅ Map Zoom"; + } + if (Properties.Settings.Default.Map_ShowFilterBox) + { + gb_Map_Filter.ClientSize = new System.Drawing.Size(gb_Map_Filter.ClientSize.Width, gb_Map_Filter_MaximizedHeigth); + if (gb_Map_Filter.Text != "˄ Planes Filter") + gb_Map_Filter.Text = "˄ Planes Filter"; + } + else + { + gb_Map_Filter.ClientSize = new System.Drawing.Size(gb_Map_Filter.ClientSize.Width, gb_Map_Filter_MinimizedHeigth); + if (gb_Map_Filter.Text != "˅ Planes Filter") + gb_Map_Filter.Text = "˅ Planes Filter"; + } + if (Properties.Settings.Default.Map_ShowAlarmBox) + { + gb_Map_Alarms.ClientSize = new System.Drawing.Size(gb_Map_Alarms.ClientSize.Width, gb_Map_Alarms_MaximizedHeight); + if (gb_Map_Alarms.Text != "˄ Alarms") + gb_Map_Alarms.Text = "˄ Alarms"; + } + else + { + gb_Map_Alarms.ClientSize = new System.Drawing.Size(gb_Map_Alarms.ClientSize.Width, gb_Map_Alarms_MinimizedHeight); + if (gb_Map_Alarms.Text != "˅ Alarms") + gb_Map_Alarms.Text = "˅ Alarms"; + } + // show/hide panes + gmo_Airports.IsVisibile = Properties.Settings.Default.Airports_Activate; + gmo_Callsigns.IsVisibile = Properties.Settings.Default.Watchlist_Activated; + + pa_Map_Boxes.ClientSize = new System.Drawing.Size(pa_Map_Boxes.ClientSize.Width, ClientSize.Height - ss_Main.Height); + */ + // enable/disable watchlist button + if (btn_Control_Manage_Watchlist.Enabled == Properties.Settings.Default.Watchlist_SyncWithKST) + btn_Control_Manage_Watchlist.Enabled = !Properties.Settings.Default.Watchlist_SyncWithKST; + } + + #endregion + + #region Closing Down + + private void MapDlg_FormClosing(object sender, FormClosingEventArgs e) + { + Log.WriteMessage(Application.ProductName + " is closing."); + // flush the Log for the first time to save all messages + Log.FlushLog(); + //save window size, state and location + Properties.Settings.Default.General_WindowLocation = this.Location; + if (this.WindowState == FormWindowState.Normal) + Properties.Settings.Default.General_WindowSize = this.Size; + else + Properties.Settings.Default.General_WindowSize = this.RestoreBounds.Size; + Properties.Settings.Default.General_WindowState = this.WindowState; + Say("Waiting for background threads to close..."); + // close background threads, save database and settings + try + { + // cancel background workers + CancelAllBackgroundWorkers(); + // save splitter positions + Properties.Settings.Default.MainSplitter_Distance = sc_Main.SplitterDistance; + Properties.Settings.Default.MapSplitter_Distance = CurrentMapSplitterDistance; + // invalidate tracking values + Properties.Settings.Default.Track_SetAz = double.NaN; + Properties.Settings.Default.Track_SetEl = double.NaN; + } + catch (Exception ex) + { + Log.WriteMessage(ex.Message); + } + finally + { + // save InMemory databases if any + if (StationData.Database.IsInMemory()) + { + Stopwatch st = new Stopwatch(); + st.Start(); + SayDatabase("Saving station database..."); + StationData.Database.BackupDatabase(); + st.Stop(); + Log.WriteMessage("Station database saved, " + st.ElapsedMilliseconds.ToString() + " ms."); + } + if (PropagationData.Database.IsInMemory(ELEVATIONMODEL.GLOBE)) + { + Stopwatch st = new Stopwatch(); + st.Start(); + SayDatabase("Saving GLOBE database..."); + PropagationData.Database.BackupDatabase(ELEVATIONMODEL.GLOBE); + st.Stop(); + Log.WriteMessage("Propagation database GLOBE saved, " + st.ElapsedMilliseconds.ToString() + " ms."); + } + if (PropagationData.Database.IsInMemory(ELEVATIONMODEL.SRTM3)) + { + Stopwatch st = new Stopwatch(); + st.Start(); + SayDatabase("Saving SRTM3 database..."); + PropagationData.Database.BackupDatabase(ELEVATIONMODEL.SRTM3); + st.Stop(); + Log.WriteMessage("Propagation database SRTM3 saved, " + st.ElapsedMilliseconds.ToString() + " ms."); + } + if (PropagationData.Database.IsInMemory(ELEVATIONMODEL.SRTM1)) + { + Stopwatch st = new Stopwatch(); + st.Start(); + SayDatabase("Saving SRTM1 database..."); + PropagationData.Database.BackupDatabase(ELEVATIONMODEL.SRTM1); + st.Stop(); + Log.WriteMessage("Propagation database SRTM1 saved, " + st.ElapsedMilliseconds.ToString() + " ms."); + } + if (AircraftData.Database.IsInMemory()) + { + Stopwatch st = new Stopwatch(); + st.Start(); + SayDatabase("Saving aircraft database..."); + AircraftData.Database.BackupDatabase(); + st.Stop(); + Log.WriteMessage("Aircraft database saved, " + st.ElapsedMilliseconds.ToString() + " ms."); + } + // save properties to file + SaveUserSettings(); + // flush the Log again in case of any exception to save all messages + Log.FlushLog(); + } + } + + private void MapDlg_FormClosed(object sender, FormClosedEventArgs e) + { + Log.WriteMessage(Application.ProductName + " is closed."); + // flush the Log for the first time to save all messages + Log.FlushLog(); + } + + #endregion + + + #region Service Functions + + private void Say(string text) + { + if (String.Compare(tsl_Status.Text, text) == 0) + return; + tsl_Status.Text = text; + } + + private void SayDatabase(string text) + { + if (String.Compare(tsl_Database.Text, text) == 0) + return; + tsl_Database.Text = text; + } + + private void SayCalculations(string text) + { + if (String.Compare(tsl_Calculations.Text, text) == 0) + return; + tsl_Calculations.Text = text; + } + + private void SayAnalysis(string text) + { + if (String.Compare(tb_Analysis_Status.Text, text) == 0) + return; + tb_Analysis_Status.Text = text; + tb_Analysis_Status.Refresh(); + } + + private void UpdateStatus() + { + // upddate TextBoxes + tb_UTC.Text = CurrentTime.ToString("yyyy-MM-dd HH:mm:ss"); + if (Properties.Settings.Default.Time_Mode_Online) + tb_UTC.BackColor = Color.LightSalmon; + else + tb_UTC.BackColor = Color.Plum; + string call = Properties.Settings.Default.MyCall; + cb_MyCall.SilentText = Properties.Settings.Default.MyCall; + cb_MyLoc.SilentText = MaidenheadLocator.LocFromLatLon(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, + Properties.Settings.Default.Locator_SmallLettersForSubsquares, + (int)Properties.Settings.Default.Locator_MaxLength / 2, + Properties.Settings.Default.Locator_AutoLength); + cb_DXCall.Text = Properties.Settings.Default.DXCall; + cb_DXLoc.SilentText = MaidenheadLocator.LocFromLatLon(Properties.Settings.Default.DXLat, + Properties.Settings.Default.DXLon, + Properties.Settings.Default.Locator_SmallLettersForSubsquares, + (int)Properties.Settings.Default.Locator_MaxLength / 2, + Properties.Settings.Default.Locator_AutoLength); + if (MaidenheadLocator.Check(cb_MyLoc.Text) && MaidenheadLocator.Check(cb_DXLoc.Text)) + { + tb_QTF.Text = Math.Round(LatLon.Bearing(Properties.Settings.Default.MyLat, + Properties.Settings.Default.MyLon, + Properties.Settings.Default.DXLat, + Properties.Settings.Default.DXLon)).ToString("F0"); + tb_QRB.Text = Math.Round(LatLon.Distance(Properties.Settings.Default.MyLat, + Properties.Settings.Default.MyLon, + Properties.Settings.Default.DXLat, + Properties.Settings.Default.DXLon)).ToString("F0"); + } + else + { + tb_QRB.Text = "0"; + tb_QTF.Text = "0"; + } + // colour Textbox if more precise lat/lon information is available + if (MaidenheadLocator.IsPrecise(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, 3)) + { + cb_MyLoc.BackColor = Color.PaleGreen; + } + else + { + cb_MyLoc.BackColor = Color.FloralWhite; + } + // colour Textbox if more precise lat/lon information is available + if (MaidenheadLocator.IsPrecise(Properties.Settings.Default.DXLat, Properties.Settings.Default.DXLon, 3)) + { + cb_DXLoc.BackColor = Color.PaleGreen; + } + else + { + cb_DXLoc.BackColor = Color.FloralWhite; + } + cb_Band.SelectedItem = Bands.GetStringValue(Properties.Settings.Default.Band); + } + + private void Alarm(string msg) + { + if (Properties.Settings.Default.Alarm_Activate) + { + gb_Map_Alarms.BackColor = Color.Plum; + if (Properties.Settings.Default.Alarm_BringWindowToFront) + { + // try different methods to bring the window to front under WinXP and Win7 + this.TopMost = true; + SetForegroundWindow(this.Handle); + // restore window size, state and location + try + { + this.WindowState = Properties.Settings.Default.General_WindowState; + this.Size = Properties.Settings.Default.General_WindowSize; + this.Location = Properties.Settings.Default.General_WindowLocation; + } + catch (Exception ex) + { + // do nothing if failed + Log.WriteMessage(ex.Message); + } + this.BringToFront(); + this.Activate(); + this.TopMost = false; + } + if (Properties.Settings.Default.Alarm_PlaySound) + System.Media.SystemSounds.Beep.Play(); + } + } + + private void MapSave() + { + Log.WriteMessage("Started."); + try + { + Bitmap bmp = new Bitmap(this.Width, this.Height); + this.DrawToBitmap(bmp, new System.Drawing.Rectangle(new System.Drawing.Point(0, 0), this.Size)); + EncoderParameters encoderParameters = new EncoderParameters(1); + encoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L); + ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders(); + foreach (ImageCodecInfo codec in codecs) + { + if (codec.FormatID == System.Drawing.Imaging.ImageFormat.Jpeg.Guid) + { + bmp.Save(TmpDirectory + Path.DirectorySeparatorChar + Properties.Settings.Default.Band + "_" + Properties.Settings.Default.MyCall.Replace("/", "_") + "_" + Properties.Settings.Default.DXCall.Replace("/", "_") + "_" + CurrentTime.ToString("yyyyMMdd") + "_" + CurrentTime.ToString("HHmmss") + ".jpg", codec, encoderParameters); + } + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.Message); + } + Log.WriteMessage("Finished."); + } + + + private void cb_Alarms_Activate_CheckedChanged(object sender, EventArgs e) + { + if (!cb_Alarms_Activate.Checked) + gb_Map_Alarms.BackColor = SystemColors.Control; + } + + private void tb_UTC_MouseDoubleClick(object sender, MouseEventArgs e) + { + if (PlayMode != AIRSCOUTPLAYMODE.PAUSE) + return; + SetTimeDlg Dlg = new SetTimeDlg(); + Dlg.cb_Time_Online.Checked = Properties.Settings.Default.Time_Mode_Online; + Dlg.dtp_SetTimeDlg_Start.Value = Properties.Settings.Default.Time_Offline; + if (Dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) + { + Properties.Settings.Default.Time_Offline = Dlg.dtp_SetTimeDlg_Start.Value; + Properties.Settings.Default.Time_Mode_Online = Dlg.cb_Time_Online.Checked; + UpdateStatus(); + } + } + + private void UpdateLocation(string call, double lat, double lon, GEOSOURCE source) + { + // update callsign database with new lat/lon info + if (Callsign.Check(call)) + StationData.Database.LocationInsertOrUpdateIfNewer(new LocationDesignator(call, lat, lon, source)); + } + + public LocationDesignator LocationFindOrCreate(string call, string loc) + { + // check all parameters + if (!Callsign.Check(call)) + return null; + if (!MaidenheadLocator.Check(loc)) + return null; + // get location info + LocationDesignator ld = StationData.Database.LocationFindOrCreate(call, loc); + // get elevation + ld.Elevation = GetElevation(ld.Lat, ld.Lon); + ld.BestCaseElevation = false; + // modify location in case of best case elevation is selected --> but do not store in database or settings! + if (Properties.Settings.Default.Path_BestCaseElevation) + { + if (!MaidenheadLocator.IsPrecise(ld.Lat, ld.Lon, 3)) + { + ElvMinMaxInfo maxinfo = GetMinMaxElevationLoc(ld.Loc); + if (maxinfo != null) + { + ld.Lat = maxinfo.MaxLat; + ld.Lon = maxinfo.MaxLon; + ld.Elevation = maxinfo.MaxElv; + ld.BestCaseElevation = true; + } + } + } + return ld; + } + + public LocationDesignator LocationFindOrUpdateOrCreate(string call, double lat, double lon) + { + // check all parameters + if (!Callsign.Check(call)) + return null; + if (!GeographicalPoint.Check(lat, lon)) + return null; + // get location info + LocationDesignator ld = StationData.Database.LocationFindOrUpdateOrCreate(call, lat, lon); + // get elevation + ld.Elevation = GetElevation(ld.Lat, ld.Lon); + ld.BestCaseElevation = false; + // modify location in case of best case elevation is selected --> but do not store in database or settings! + if (Properties.Settings.Default.Path_BestCaseElevation) + { + if (!MaidenheadLocator.IsPrecise(ld.Lat, ld.Lon, 3)) + { + ElvMinMaxInfo maxinfo = GetMinMaxElevationLoc(ld.Loc); + if (maxinfo != null) + { + ld.Lat = maxinfo.MaxLat; + ld.Lon = maxinfo.MaxLon; + ld.Elevation = maxinfo.MaxElv; + ld.BestCaseElevation = true; + } + } + } + return ld; + } + + public LocationDesignator LocationFind(string call, string loc = "") + { + // check all parameters + if (!Callsign.Check(call)) + return null; + if (!String.IsNullOrEmpty(loc) && !MaidenheadLocator.Check(loc)) + return null; + // get location info + LocationDesignator ld = (String.IsNullOrEmpty(loc)) ? StationData.Database.LocationFind(call) : StationData.Database.LocationFind(call, loc); + // return null if not found + if (ld == null) + return null; + // get elevation + ld.Elevation = GetElevation(ld.Lat, ld.Lon); + ld.BestCaseElevation = false; + // modify location in case of best case elevation is selected --> but do not store in database or settings! + if (Properties.Settings.Default.Path_BestCaseElevation) + { + if (!MaidenheadLocator.IsPrecise(ld.Lat, ld.Lon, 3)) + { + ElvMinMaxInfo maxinfo = GetMinMaxElevationLoc(ld.Loc); + if (maxinfo != null) + { + ld.Lat = maxinfo.MaxLat; + ld.Lon = maxinfo.MaxLon; + ld.Elevation = maxinfo.MaxElv; + ld.BestCaseElevation = true; + } + } + } + return ld; + } + + public short GetElevation(string loc) + { + return GetElevation(MaidenheadLocator.LatFromLoc(loc), MaidenheadLocator.LonFromLoc(loc)); + } + + public short GetElevation(double lat, double lon) + { + if (!GeographicalPoint.Check(lat, lon)) + return 0; + short elv = ElevationData.Database.ElvMissingFlag; + // try to get elevation data from distinct elevation model + // start with detailed one + if (Properties.Settings.Default.Elevation_SRTM1_Enabled && (elv == ElevationData.Database.ElvMissingFlag)) + elv = ElevationData.Database[lat, lon, ELEVATIONMODEL.SRTM1, false]; + if (Properties.Settings.Default.Elevation_SRTM3_Enabled && (elv == ElevationData.Database.ElvMissingFlag)) + elv = ElevationData.Database[lat, lon, ELEVATIONMODEL.SRTM3, false]; + if (Properties.Settings.Default.Elevation_GLOBE_Enabled && (elv == ElevationData.Database.ElvMissingFlag)) + elv = ElevationData.Database[lat, lon, ELEVATIONMODEL.GLOBE, false]; + // set it to zero if still invalid + if (elv <= ElevationData.Database.TileMissingFlag) + elv = 0; + return elv; + } + + public ElvMinMaxInfo GetMinMaxElevationLoc(string loc) + { + ElvMinMaxInfo elv = new ElvMinMaxInfo(); + // try to get elevation data from distinct elevation model + // start with detailed one + if (Properties.Settings.Default.Elevation_SRTM1_Enabled && (elv.MaxElv == ElevationData.Database.ElvMissingFlag)) + { + ElvMinMaxInfo info = ElevationData.Database.GetMaxElvLoc(loc, ELEVATIONMODEL.SRTM1, false); + if (info != null) + { + elv.MaxLat = info.MaxLat; + elv.MaxLon = info.MaxLon; + elv.MaxElv = info.MaxElv; + elv.MinLat = info.MinLat; + elv.MinLon = info.MinLon; + elv.MinElv = info.MinElv; + } + } + if (Properties.Settings.Default.Elevation_SRTM3_Enabled && (elv.MaxElv == ElevationData.Database.ElvMissingFlag)) + { + ElvMinMaxInfo info = ElevationData.Database.GetMaxElvLoc(loc, ELEVATIONMODEL.SRTM3, false); + if (info != null) + { + elv.MaxLat = info.MaxLat; + elv.MaxLon = info.MaxLon; + elv.MaxElv = info.MaxElv; + elv.MinLat = info.MinLat; + elv.MinLon = info.MinLon; + elv.MinElv = info.MinElv; + } + } + if (Properties.Settings.Default.Elevation_GLOBE_Enabled && (elv.MaxElv == ElevationData.Database.ElvMissingFlag)) + { + ElvMinMaxInfo info = ElevationData.Database.GetMaxElvLoc(loc, ELEVATIONMODEL.GLOBE, false); + if (info != null) + { + elv.MaxLat = info.MaxLat; + elv.MaxLon = info.MaxLon; + elv.MaxElv = info.MaxElv; + elv.MinLat = info.MinLat; + elv.MinLon = info.MinLon; + elv.MinElv = info.MinElv; + } + } + // set it to zero if still invalid + if (elv.MaxElv == ElevationData.Database.ElvMissingFlag) + elv.MaxElv = 0; + if (elv.MinElv == ElevationData.Database.ElvMissingFlag) + elv.MinElv = 0; + + return elv; + } + + public void SetElevationModel() + { + if (Properties.Settings.Default.Elevation_SRTM1_Enabled) + Properties.Settings.Default.ElevationModel = ELEVATIONMODEL.SRTM1; + else if (Properties.Settings.Default.Elevation_SRTM3_Enabled) + Properties.Settings.Default.ElevationModel = ELEVATIONMODEL.SRTM3; + else if (Properties.Settings.Default.Elevation_GLOBE_Enabled) + Properties.Settings.Default.ElevationModel = ELEVATIONMODEL.GLOBE; + else + Properties.Settings.Default.ElevationModel = ELEVATIONMODEL.NONE; + } + + public static Font CreateFontFromString(string font) + { + try + { + string[] a = Properties.Settings.Default.Map_ToolTipFont.Split(';'); + string fontfamily = a[0].Trim(); + float emsize = 0; + float.TryParse(a[1].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out emsize); + FontStyle fontstyle = 0; + // check if any additional font style is given + if (a.Length > 2) + { + if (a[2].ToUpper().IndexOf("BOLD") >= 0) + fontstyle = fontstyle | FontStyle.Bold; + if (a[2].ToUpper().IndexOf("ITALIC") >= 0) + fontstyle = fontstyle | FontStyle.Italic; + if (a[2].ToUpper().IndexOf("UNDERLINE") >= 0) + fontstyle = fontstyle | FontStyle.Underline; + if (a[2].ToUpper().IndexOf("STRIKEOUT") >= 0) + fontstyle = fontstyle | FontStyle.Strikeout; + } + else + { + fontstyle = FontStyle.Regular; + } + return new Font(fontfamily, emsize, fontstyle, GraphicsUnit.Point); + } + catch + { + + } + return null; + } + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommands nCmdShow); + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool SetForegroundWindow(IntPtr hWnd); + + enum ShowWindowCommands : int + { + /// + /// Hides the window and activates another window. + /// + Hide = 0, + /// + /// Activates and displays a window. If the window is minimized or + /// maximized, the system restores it to its original size and position. + /// An application should specify this flag when displaying the window + /// for the first time. + /// + Normal = 1, + /// + /// Activates the window and displays it as a minimized window. + /// + ShowMinimized = 2, + /// + /// Maximizes the specified window. + /// + Maximize = 3, // is this the right value? + /// + /// Activates the window and displays it as a maximized window. + /// + ShowMaximized = 3, + /// + /// Displays a window in its most recent size and position. This value + /// is similar to , except + /// the window is not activated. + /// + ShowNoActivate = 4, + /// + /// Activates the window and displays it in its current size and position. + /// + Show = 5, + /// + /// Minimizes the specified window and activates the next top-level + /// window in the Z order. + /// + Minimize = 6, + /// + /// Displays the window as a minimized window. This value is similar to + /// , except the + /// window is not activated. + /// + ShowMinNoActive = 7, + /// + /// Displays the window in its current size and position. This value is + /// similar to , except the + /// window is not activated. + /// + ShowNA = 8, + /// + /// Activates and displays the window. If the window is minimized or + /// maximized, the system restores it to its original size and position. + /// An application should specify this flag when restoring a minimized window. + /// + Restore = 9, + /// + /// Sets the show state based on the SW_* value specified in the + /// STARTUPINFO structure passed to the CreateProcess function by the + /// program that started the application. + /// + ShowDefault = 10, + /// + /// Windows 2000/XP: Minimizes a window, even if the thread + /// that owns the window is not responding. This flag should only be + /// used when minimizing windows from a different thread. + /// + ForceMinimize = 11 + } + + private void ShowOptionsDlg() + { + // stop background threads + Say("Waiting for background threads to close...."); + StopAllBackgroundWorkers(); + // save current settings + SaveUserSettings(); + // show options dialog + OptionsDlg Dlg = new OptionsDlg(this); + Say("Options"); + if (Dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) + { + // clear paths cache assuming that new options were set + ElevationPaths.Clear(); + PropagationPaths.Clear(); + // update station infos + UpdateLocation(Properties.Settings.Default.MyCall, + Properties.Settings.Default.MyLat, + Properties.Settings.Default.MyLon, + MaidenheadLocator.IsPrecise(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, 3) ? GEOSOURCE.FROMUSER : GEOSOURCE.FROMLOC); + UpdateLocation(Properties.Settings.Default.DXCall, + Properties.Settings.Default.DXLat, + Properties.Settings.Default.DXLon, + MaidenheadLocator.IsPrecise(Properties.Settings.Default.DXLat, Properties.Settings.Default.DXLon, 3) ? GEOSOURCE.FROMUSER : GEOSOURCE.FROMLOC); + + // update map provider + gm_Main.MapProvider = GMapProviders.Find(Properties.Settings.Default.Map_Provider); + + // update ToolTipFont + ToolTipFont = CreateFontFromString(Properties.Settings.Default.Map_ToolTipFont); + + // update planefeeds + bw_PlaneFeed1 = null; + bw_PlaneFeed2 = null; + bw_PlaneFeed3 = null; + foreach (PlaneFeed feed in PlaneFeeds) + { + if (Properties.Settings.Default.Planes_PlaneFeed1 == feed.Name) + bw_PlaneFeed1 = feed; + if (Properties.Settings.Default.Planes_PlaneFeed2 == feed.Name) + bw_PlaneFeed2 = feed; + if (Properties.Settings.Default.Planes_PlaneFeed3 == feed.Name) + bw_PlaneFeed3 = feed; + } + // update timer interval + ti_Progress.Interval = Properties.Settings.Default.Map_Update * 1000; + // update background update intervals + ScoutBase.Elevation.Properties.Settings.Default.Datatbase_BackgroundUpdate_Period = (int)Properties.Settings.Default.Background_Update_Period; + ScoutBase.Stations.Properties.Settings.Default.Database_BackgroundUpdate_Period = (int)Properties.Settings.Default.Background_Update_Period; + AirScout.Aircrafts.Properties.Settings.Default.Database_BackgroundUpdate_Period = (int)Properties.Settings.Default.Background_Update_Period; + // update database path path and elevation model + InitializeDatabase(); + } + else + { + // nothing was changed + Properties.Settings.Default.Reload(); + } + // start permanent background workers + StartAllBackgroundWorkers(); + // update status window + UpdateStatus(); + UpdateAirports(); + UpdateWatchlistInMap(); + RefreshWatchlistView(); + + } + + #endregion + + #region Play & Pause + + private void Play() + { + PlayMode = AIRSCOUTPLAYMODE.FORWARD; + // switch tab control according to path mode + if (PathMode == AIRSCOUTPATHMODE.SINGLE) + tc_Control.SelectedTab = tp_Control_Single; + else if (PathMode == AIRSCOUTPATHMODE.MULTI) + tc_Control.SelectedTab = tp_Control_Multi; + // update tab control + tc_Control.Refresh(); + // update all current paths + UpdatePaths(); + // clear spectrum + try + { + Spectrum.Points.Clear(); + SpectrumPointsCount = 0; + Spectrum_X.Reset(); + SpectrumRecord.Points.Clear(); + } + catch (Exception ex) + { + Log.WriteMessage(ex.Message); + } + // change button image + btn_Map_PlayPause.Image = il_Main.Images[0]; + // disable controls + cb_Band.Enabled = false; + btn_Options.Enabled = false; + cb_MyCall.Enabled = false; + cb_MyLoc.Enabled = false; + cb_DXCall.Enabled = false; + cb_DXLoc.Enabled = false; + tc_Control.Enabled = false; + pa_Planes_Filter.Enabled = false; + gb_Analysis_Controls.Enabled = false; + gb_Analysis_Database.Enabled = false; + gb_Analysis_Player.Enabled = false; + tc_Main.Enabled = false; + //referesh main window + this.Refresh(); + } + + private void Pause() + { + PlayMode = AIRSCOUTPLAYMODE.PAUSE; + // change button image + btn_Map_PlayPause.Image = il_Main.Images[1]; + // update tab control + tc_Control.Refresh(); + // enable controls + cb_Band.Enabled = true; + btn_Options.Enabled = true; + cb_MyCall.Enabled = true; + cb_MyLoc.Enabled = true; + cb_DXCall.Enabled = true; + cb_DXLoc.Enabled = true; + tc_Control.Enabled = true; + pa_Planes_Filter.Enabled = true; + gb_Analysis_Controls.Enabled = true; + gb_Analysis_Database.Enabled = true; + gb_Analysis_Player.Enabled = true; + tc_Main.Enabled = true; + // stop tracking + Properties.Settings.Default.Track_SetAz = double.NaN; + Properties.Settings.Default.Track_SetEl = double.NaN; + //referesh main window + this.Refresh(); + } + + #endregion + + #region Paths + + private double GetMinH(double max_alt, double H1, double H2) + { + double max = Math.Max(H1, H2); + if (max <= max_alt) + return max; + return max_alt; + } + + private void ClearAllPathsInMap() + { + gmo_PropagationPaths.Clear(); + } + + private void DrawPath(PropagationPathDesignator ppath) + { + // draws a propagation path to map + PropagationPoint[] ppoints = new PropagationPoint[0]; + try + { + // get infopoints for map + ppoints = ppath.GetInfoPoints(); + // calculate midpoint + ScoutBase.Core.LatLon.GPoint midpoint = LatLon.MidPoint(ppath.Lat1, ppath.Lon1, ppath.Lat2, ppath.Lon2); + GMapMarker gmmid = new GMarkerGoogle(new PointLatLng(midpoint.Lat, midpoint.Lon), ToolTipFont, Properties.Settings.Default.Map_SmallMarkers ? GMarkerGoogleType.blue_small : GMarkerGoogleType.blue_dot); + gmmid.ToolTipText = ppath.Location1.Call + " <> " + ppath.Location2.Call; + gmmid.ToolTipMode = MarkerTooltipMode.OnMouseOver; + gmo_Objects.Markers.Add(gmmid); + // calculate dx end + gmm_DXLoc = new GMarkerGoogle(new PointLatLng(ppath.Lat2, ppath.Lon2), ToolTipFont, Properties.Settings.Default.Map_SmallMarkers ? GMarkerGoogleType.yellow_small : GMarkerGoogleType.yellow_dot); + gmm_DXLoc.ToolTipText = ppath.Location2.Call + "\n" + + ppath.Location2.Lat.ToString("F8", CultureInfo.InvariantCulture) + "\n" + + ppath.Location2.Lon.ToString("F8", CultureInfo.InvariantCulture) + "\n" + + ppath.Location2.Loc + "\n" + + GetElevation(ppath.Location2.Lat, ppath.Location2.Lon).ToString("F0") + "m"; + if (Properties.Settings.Default.Track_Activate) + gmm_DXLoc.ToolTipText += "\nRight+Click to Turn Antenna"; + gmm_DXLoc.ToolTipMode = MarkerTooltipMode.OnMouseOver; + gmm_DXLoc.Tag = ppath.Location2.Call; + gmo_Objects.Markers.Add(gmm_DXLoc); + // set three small points for hot path, if one + if (!Properties.Settings.Default.Map_SmallMarkers) + { + int i1 = -1; + int i3 = -1; + for (int i = 0; i < ppoints.Length; i++) + { + if (Math.Max(ppoints[i].H1, ppoints[i].H2) < Properties.Settings.Default.Planes_MaxAlt) + { + if (i1 == -1) + i1 = i; + else i3 = i; + + } + } + if ((i1 >= 0) && (i3 >= 0)) + { + GMapMarker gmi1 = new GMarkerGoogle(new PointLatLng(ppoints[i1].Lat, ppoints[i1].Lon), GMarkerGoogleType.red_small); + gmo_Objects.Markers.Add(gmi1); + LatLon.GPoint gp = LatLon.MidPoint(ppoints[i1].Lat, ppoints[i1].Lon, ppoints[i3].Lat, ppoints[i3].Lon); + GMapMarker gmi2 = new GMarkerGoogle(new PointLatLng(gp.Lat, gp.Lon), GMarkerGoogleType.blue_small); + gmo_Objects.Markers.Add(gmi2); + GMapMarker gmi3 = new GMarkerGoogle(new PointLatLng(ppoints[i3].Lat, ppoints[i3].Lon), GMarkerGoogleType.yellow_small); + gmo_Objects.Markers.Add(gmi3); + } + } + // draw propagation path according to path status + // valid: black + // invalid: red + gmr_FullPath = new GMapRoute("fullpath"); + gmr_FullPath.Stroke = (ppath.Valid) ? new Pen(Color.Black, 3) : new Pen(Color.Red, 3); + gmo_PropagationPaths.Routes.Add(gmr_FullPath); + gmr_NearestFull = new GMapRoute("fullpath"); + gmr_NearestFull.Stroke = (ppath.Valid) ? new Pen(Color.Black, 3) : new Pen(Color.Red, 3); + gmo_NearestPaths.Routes.Add(gmr_NearestFull); + foreach (PropagationPoint ppoint in ppoints) + { + gmr_FullPath.Points.Add(new PointLatLng(ppoint.Lat, ppoint.Lon)); + gmr_NearestFull.Points.Add(new PointLatLng(ppoint.Lat, ppoint.Lon)); + } + // draw mutual visible path + gmr_VisiblePpath = new GMapRoute("visiblepath"); + gmr_VisiblePpath.Stroke = new Pen(Color.Magenta, 3); + gmr_NearestVisible = new GMapRoute("visiblepath"); + gmr_NearestVisible.Stroke = new Pen(Color.Magenta, 3); + for (int i = 0; i < ppoints.Length; i++) + { + if ((Math.Max(ppoints[i].H1, ppoints[i].H2) > 0) && (Math.Max(ppoints[i].H1, ppoints[i].H2) < Properties.Settings.Default.Planes_MaxAlt)) + { + PointLatLng p = new PointLatLng(ppoints[i].Lat, ppoints[i].Lon); + gmr_VisiblePpath.Points.Add(p); + gmr_NearestVisible.Points.Add(p); + } + } + gmo_PropagationPaths.Routes.Add(gmr_VisiblePpath); + gmo_NearestPaths.Routes.Add(gmr_NearestVisible); + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + + private void UpdatePaths() + { + // updates all current path to calculate + + Log.WriteMessage("UpdatePath started."); + Stopwatch st = new Stopwatch(); + st.Start(); + + // check if there are a valid home settings + if (!Callsign.Check(Properties.Settings.Default.MyCall) || + !GeographicalPoint.Check(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon)) + return; + + // OK valid, lets continue + + // slow down background calculations + Properties.Settings.Default.Background_Calculations_ThreadWait = 1000; + + //clear map overlays + gmo_PropagationPaths.Clear(); + gmo_NearestPaths.Clear(); + gmo_Objects.Clear(); + + // clear all planes and tooltips + gmo_Planes.Clear(); + + // clear paths + ElevationPaths.Clear(); + PropagationPaths.Clear(); + + // clear charts + ClearCharts(); + + // put call on MyCalls last recent collection if not already in + if (Properties.Settings.Default.MyCalls.IndexOf(Properties.Settings.Default.MyCall) < 0) + { + Properties.Settings.Default.MyCalls.Insert(0, Properties.Settings.Default.MyCall); + } + + // keep the MyCalls list small + while (Properties.Settings.Default.MyCalls.Count > 10) + { + Properties.Settings.Default.MyCalls.RemoveAt(Properties.Settings.Default.MyCalls.Count - 1); + } + + + // check and update station database + LocationDesignator myloc = LocationFindOrUpdateOrCreate(Properties.Settings.Default.MyCall, Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon); + Properties.Settings.Default.MyElevation = myloc.Elevation; + + // get qrv info or create default + QRVDesignator myqrv = StationData.Database.QRVFindOrCreateDefault(myloc.Call, myloc.Loc, Properties.Settings.Default.Band); + // set qrv defaults if zero + if (myqrv.AntennaHeight == 0) + myqrv.AntennaHeight = StationData.Database.QRVGetDefaultAntennaHeight(Properties.Settings.Default.Band); + if (myqrv.AntennaGain == 0) + myqrv.AntennaGain = StationData.Database.QRVGetDefaultAntennaGain(Properties.Settings.Default.Band); + if (myqrv.Power == 0) + myqrv.Power = StationData.Database.QRVGetDefaultPower(Properties.Settings.Default.Band); + // draw my end on the map + gmm_MyLoc = new GMarkerGoogle(new PointLatLng(myloc.Lat, myloc.Lon), ToolTipFont, Properties.Settings.Default.Map_SmallMarkers ? GMarkerGoogleType.red_small : GMarkerGoogleType.red_dot); + gmm_MyLoc.ToolTipText = myloc.Call + "\n" + + myloc.Lat.ToString("F8", CultureInfo.InvariantCulture) + "\n" + + myloc.Lon.ToString("F8", CultureInfo.InvariantCulture) + "\n" + + myloc.Loc + "\n" + + GetElevation(myloc.Lat, myloc.Lon).ToString("F0") + "m"; + gmm_MyLoc.ToolTipMode = MarkerTooltipMode.OnMouseOver; + gmm_MyLoc.Tag = myloc.Call; + gmo_Objects.Markers.Add(gmm_MyLoc); + + // do single path mode + if (PathMode == AIRSCOUTPATHMODE.SINGLE) + { + // check if there are a valid DX settings + if (!Callsign.Check(Properties.Settings.Default.DXCall) || + !GeographicalPoint.Check(Properties.Settings.Default.DXLat, Properties.Settings.Default.DXLon)) + return; + + // OK valid, lets continue + // check and update station database + LocationDesignator dxloc = LocationFindOrUpdateOrCreate(Properties.Settings.Default.DXCall, Properties.Settings.Default.DXLat, Properties.Settings.Default.DXLon); + Properties.Settings.Default.DXElevation = dxloc.Elevation; + + // get qrv info or create default + QRVDesignator dxqrv = StationData.Database.QRVFindOrCreateDefault(dxloc.Call, dxloc.Loc, Properties.Settings.Default.Band); + // set qrv defaults if zero + if (dxqrv.AntennaHeight == 0) + dxqrv.AntennaHeight = StationData.Database.QRVGetDefaultAntennaHeight(Properties.Settings.Default.Band); + if (dxqrv.AntennaGain == 0) + dxqrv.AntennaGain = StationData.Database.QRVGetDefaultAntennaGain(Properties.Settings.Default.Band); + if (dxqrv.Power == 0) + dxqrv.Power = StationData.Database.QRVGetDefaultPower(Properties.Settings.Default.Band); + + // find local obstruction, if any + LocalObstructionDesignator o = ElevationData.Database.LocalObstructionFind(myloc.Lat, myloc.Lon, Properties.Settings.Default.ElevationModel); + double mybearing = LatLon.Bearing(myloc.Lat, myloc.Lon, dxloc.Lat, dxloc.Lon); + double myobstr = (o != null) ? o.GetObstruction(myqrv.AntennaHeight, mybearing) : double.MinValue; + + // try to find elevation path in database or create new one and store + ElevationPathDesignator epath = ElevationData.Database.ElevationPathFindOrCreateFromLatLon( + null, + myloc.Lat, + myloc.Lon, + dxloc.Lat, + dxloc.Lon, + ElevationData.Database.GetDefaultStepWidth(Properties.Settings.Default.ElevationModel), + Properties.Settings.Default.ElevationModel); + // add additional info to ppath + epath.Location1 = myloc; + epath.Location2 = dxloc; + epath.QRV1 = myqrv; + epath.QRV2 = dxqrv; + + // try to find propagation path in database or create new one and store + PropagationPathDesignator ppath = PropagationData.Database.PropagationPathFindOrCreateFromLatLon( + null, + myloc.Lat, + myloc.Lon, + GetElevation(myloc.Lat, myloc.Lon) + myqrv.AntennaHeight, + dxloc.Lat, + dxloc.Lon, + GetElevation(dxloc.Lat, dxloc.Lon) + dxqrv.AntennaHeight, + Bands.ToGHz(Properties.Settings.Default.Band), + LatLon.Earth.Radius * Properties.Settings.Default.Path_Band_Settings[Properties.Settings.Default.Band].K_Factor, + Properties.Settings.Default.Path_Band_Settings[Properties.Settings.Default.Band].F1_Clearance, + ElevationData.Database.GetDefaultStepWidth(Properties.Settings.Default.ElevationModel), + Properties.Settings.Default.ElevationModel, + myobstr); + + // add additional info to ppath + ppath.Location1 = myloc; + ppath.Location2 = dxloc; + ppath.QRV1 = myqrv; + ppath.QRV2 = dxqrv; + + // add single path to paths list + ElevationPaths.Add(epath); + PropagationPaths.Add(ppath); + // put DXCall on the watchlist if not already in + if (Properties.Settings.Default.Watchlist.IndexOf(Properties.Settings.Default.DXCall, MaidenheadLocator.LocFromLatLon(Properties.Settings.Default.DXLat, Properties.Settings.Default.DXLon, false, 3)) < 0) + { + Properties.Settings.Default.Watchlist.Insert(0, new WatchlistItem(Properties.Settings.Default.DXCall, MaidenheadLocator.LocFromLatLon(Properties.Settings.Default.DXLat, Properties.Settings.Default.DXLon, false, 3), ppath.Distance > Properties.Settings.Default.Path_MaxLength)); + } + // keep the watchlist small + while (Properties.Settings.Default.Watchlist.Count() > Properties.Settings.Default.Watchlist_MaxCount) + { + Properties.Settings.Default.Watchlist.RemoveAt(Properties.Settings.Default.Watchlist.Count() - 1); + } + + } + else if (PathMode == AIRSCOUTPATHMODE.MULTI) + { + // iterate through watchlist and add selected + foreach (ListViewItem item in lv_Control_Watchlist.Items) + { + // use only selected items + if (!item.Checked) + continue; + string call = item.Text; + string loc = item.SubItems[1].Text; + + // check if call & loc are valid + if (!Callsign.Check(call) || !MaidenheadLocator.Check(loc)) + continue; + + // check and update station database + LocationDesignator dxloc = LocationFindOrCreate(call, loc); + + // get qrv info or create default + QRVDesignator dxqrv = StationData.Database.QRVFindOrCreateDefault(dxloc.Call, dxloc.Loc, Properties.Settings.Default.Band); + // set qrv defaults if zero + if (dxqrv.AntennaHeight == 0) + dxqrv.AntennaHeight = StationData.Database.QRVGetDefaultAntennaHeight(Properties.Settings.Default.Band); + if (dxqrv.AntennaGain == 0) + dxqrv.AntennaGain = StationData.Database.QRVGetDefaultAntennaGain(Properties.Settings.Default.Band); + if (dxqrv.Power == 0) + dxqrv.Power = StationData.Database.QRVGetDefaultPower(Properties.Settings.Default.Band); + + // find local obstruction, if any + LocalObstructionDesignator o = ElevationData.Database.LocalObstructionFind(myloc.Lat, myloc.Lon, Properties.Settings.Default.ElevationModel); + double mybearing = LatLon.Bearing(myloc.Lat, myloc.Lon, dxloc.Lat, dxloc.Lon); + double myobstr = (o != null) ? o.GetObstruction(myqrv.AntennaHeight, mybearing) : double.MinValue; + + // try to find elevation path in database or create new one and store + ElevationPathDesignator epath = ElevationData.Database.ElevationPathFindOrCreateFromLatLon( + null, + myloc.Lat, + myloc.Lon, + dxloc.Lat, + dxloc.Lon, + ElevationData.Database.GetDefaultStepWidth(Properties.Settings.Default.ElevationModel), + Properties.Settings.Default.ElevationModel); + // try to find propagation path in database or create new one and store + PropagationPathDesignator ppath = PropagationData.Database.PropagationPathFindOrCreateFromLatLon( + null, + myloc.Lat, + myloc.Lon, + GetElevation(myloc.Lat, myloc.Lon) + myqrv.AntennaHeight, + dxloc.Lat, + dxloc.Lon, + GetElevation(dxloc.Lat, dxloc.Lon) + dxqrv.AntennaHeight, + Bands.ToGHz(Properties.Settings.Default.Band), + LatLon.Earth.Radius * Properties.Settings.Default.Path_Band_Settings[Properties.Settings.Default.Band].K_Factor, + Properties.Settings.Default.Path_Band_Settings[Properties.Settings.Default.Band].F1_Clearance, + ElevationData.Database.GetDefaultStepWidth(Properties.Settings.Default.ElevationModel), + Properties.Settings.Default.ElevationModel, + myobstr); + // add additional info to ppath + ppath.Location1 = myloc; + ppath.Location2 = dxloc; + ppath.QRV1 = myqrv; + ppath.QRV2 = dxqrv; + // add path to paths list + + ElevationPaths.Add(epath); + PropagationPaths.Add(ppath); + } + } + + // calculate the area to show in map + // initially set to my location + double minlat = myloc.Lat; + double minlon = myloc.Lon; + double maxlat = myloc.Lat; + double maxlon = myloc.Lon; + + double centerlat = myloc.Lat; + double centerlon = myloc.Lon; + + // now do the drawing + foreach (PropagationPathDesignator ppath in PropagationPaths) + { + DrawPath(ppath); + + // maintain Min/Max values + minlat = Math.Min(minlat, ppath.Lat2); + minlon = Math.Min(minlon, ppath.Lon2); + maxlat = Math.Max(maxlat, ppath.Lat2); + maxlon = Math.Max(maxlon, ppath.Lon2); + } + + // show diagram when in SINGLE mode + if (PathMode == AIRSCOUTPATHMODE.SINGLE) + { + // both Elevationpaths & PropagationPaths should contain only one entry + if ((ElevationPaths.Count > 0) && (PropagationPaths.Count > 0)) + UpdateCharts(ElevationPaths[ElevationPaths.Count - 1], PropagationPaths[PropagationPaths.Count - 1]); + } + + // calculate center + centerlat = LatLon.MidPoint(minlat, minlon, maxlat, maxlon).Lat; + centerlon = LatLon.MidPoint(minlat, minlon, maxlat, maxlon).Lon; + + // ensure that whole path is visible and optionally centered + gm_Main.SetZoomToFitRect(RectLatLng.FromLTRB(minlon, maxlat, maxlon, minlat)); + if (Properties.Settings.Default.Map_AutoCenter) + gm_Main.Position = new PointLatLng(centerlat, centerlon); + + // clear all selections + SelectedPlanes.Clear(); + + // update watchlist locations in map + UpdateWatchlistInMap(); + + // update status window + UpdateStatus(); + + // invalidate tracking + Properties.Settings.Default.Track_SetAz = double.NaN; + Properties.Settings.Default.Track_SetEl = double.NaN; + + // speed up background calculations + Properties.Settings.Default.Background_Calculations_ThreadWait = 0; + + st.Stop(); + Log.WriteMessage("UpdatePath finished, " + st.ElapsedMilliseconds.ToString() + "ms."); + } + + #endregion + + #region Charts + + private void UpdateCharts(ElevationPathDesignator epath, PropagationPathDesignator ppath) + { + // updates the diagram area + short[] epoints = new short[0]; + PropagationPoint[] ppoints = new PropagationPoint[0]; + try + { + ClearCharts(); + // adjust diagram axes + Path_X.Maximum = ppath.Distance; + Elevation_X.Maximum = epath.Distance; + // get infopoints for charting + epoints = epath.GetInfoPoints(); + ppoints = ppath.GetInfoPoints(); + // calculate epsilon for LOS + double eps_los = Propagation.EpsilonFromHeights(GetElevation(ppath.Lat1, ppath.Lon1) + ppath.QRV1.AntennaHeight, ppath.Distance, GetElevation(ppath.Lat2, ppath.Lon2) + ppath.QRV2.AntennaHeight, LatLon.Earth.Radius); + // fill chart + short maxelv = short.MinValue; + double myelev = GetElevation(ppath.Lat1, ppath.Lon1); + for (int i = 0; i < epoints.Length; i++) + { + Path_Elevation.Points.Add(new OxyPlot.DataPoint(i, epoints[i])); + Min_H1.Points.Add(new OxyPlot.DataPoint(i, ppoints[i].H1)); + Min_H2.Points.Add(new OxyPlot.DataPoint(i, ppoints[i].H2)); + Max_H.Points.Add(new OxyPlot.DataPoint(i, Properties.Settings.Default.Planes_MaxAlt)); + Min_H.Points.Add(new OxyPlot.DataPoint(i, Properties.Settings.Default.Planes_MaxAlt)); + Min_H.Points2.Add(new OxyPlot.DataPoint(i, GetMinH(Properties.Settings.Default.Planes_MaxAlt, Min_H1.Points[i].Y, Min_H2.Points[i].Y))); + LOS.Points.Add(new OxyPlot.DataPoint(i, Propagation.HeightFromEpsilon(myelev + ppath.QRV1.AntennaHeight, i, eps_los, LatLon.Earth.Radius))); + Elevation.Points.Add(new OxyPlot.DataPoint(i, epoints[i])); + if (maxelv < epoints[i]) + maxelv = epoints[i]; + } + // adjust Y-axis --> max elv + 10% + Elevation_Y.Maximum = maxelv + maxelv * 0.1; + // invalidate plots + pm_Path.InvalidatePlot(true); + pm_Elevation.InvalidatePlot(true); + // show path legends + Charts_ShowLegends(5000); + // refresh path info + tp_Elevation.Text = "Pathinfo "; + if (Properties.Settings.Default.ElevationModel == ELEVATIONMODEL.SRTM1) + tp_Elevation.Text = tp_Elevation.Text + "[SRTM1]"; + else if (Properties.Settings.Default.ElevationModel == ELEVATIONMODEL.SRTM3) + tp_Elevation.Text = tp_Elevation.Text + "[SRTM3]"; + else if (Properties.Settings.Default.ElevationModel == ELEVATIONMODEL.GLOBE) + tp_Elevation.Text = tp_Elevation.Text + "[GLOBE]"; + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + + private void ClearCharts() + { + try + { + // clear all points + Path_Elevation.Points.Clear(); + Min_H1.Points.Clear(); + Min_H2.Points.Clear(); + Max_H.Points.Clear(); + Min_H.Points.Clear(); + Min_H.Points2.Clear(); + Planes_Hi.Points.Clear(); + Planes_Lo.Points.Clear(); + Elevation.Points.Clear(); + LOS.Points.Clear(); + // update view + pm_Path.InvalidatePlot(true); + pm_Elevation.InvalidatePlot(true); + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + + private void Charts_ShowLegends(int ms) + { + // enable path legends for some seconds + pm_Path.IsLegendVisible = true; + pm_Path.InvalidatePlot(true); + pm_Elevation.IsLegendVisible = true; + pm_Elevation.InvalidatePlot(true); + ti_ShowLegends.Interval = ms; + ti_ShowLegends.Start(); + } + + private void ti_ShowLegends_Tick(object sender, EventArgs e) + { + pm_Path.IsLegendVisible = false; + pm_Path.InvalidatePlot(true); + pm_Elevation.IsLegendVisible = false; + pm_Elevation.InvalidatePlot(true); + ti_ShowLegends.Enabled = false; + } + + private void pv_Path_Paint(object sender, PaintEventArgs e) + { + // draw calsign s on chart + string mycall = Properties.Settings.Default.MyCall; + string dxcall = Properties.Settings.Default.DXCall; + int top = pv_Path.Bottom - pv_Path.Height / 2; + int left = pv_Path.Left + 50; + int right = pv_Path.Width - 38; + Font font = new Font(FontFamily.GenericSansSerif, 8, FontStyle.Bold); + Graphics g = e.Graphics; + using (StringFormat format = new StringFormat(StringFormatFlags.DirectionVertical)) + { + // measure text to emulate TextAlign.Middle + int mywidth = (int)g.MeasureString(mycall, font).Width; + int dxwidth = (int)g.MeasureString(dxcall, font).Width; + using (SolidBrush brush = new SolidBrush(Color.Black)) + { + if (pv_Path.Height - 50 > mywidth) + g.DrawString(mycall, font, brush, left, top - mywidth / 2, format); + if (pv_Path.Height - 50 > dxwidth) + g.DrawString(dxcall, font, brush, right, top - dxwidth / 2, format); + /* + if (pv_Path.Height > 2 * mywidth + 50) + g.DrawString(mycall, font, brush, left, top, format); + if (pv_Path.Height > 2 * dxwdith + 50) + g.DrawString(dxcall, font, brush, right, top, format); + */ + } + } + } + + #endregion + + #region Planes + + private static Bitmap RotateImageByAngle(System.Drawing.Image oldBitmap, float angle) + { + var newBitmap = new Bitmap(oldBitmap.Width, oldBitmap.Height); + var graphics = Graphics.FromImage(newBitmap); + graphics.TranslateTransform((float)oldBitmap.Width / 2, (float)oldBitmap.Height / 2); + graphics.RotateTransform(angle); + graphics.TranslateTransform(-(float)oldBitmap.Width / 2, -(float)oldBitmap.Height / 2); + graphics.DrawImage(oldBitmap, new System.Drawing.Point(0, 0)); + return newBitmap; + } + + private GMarkerGoogle CreatePlaneSimple(PlaneInfo info, bool selected) + { + // return on empty info + if (info == null) + return null; + // show flight info only + // get bitmap according to category + Bitmap bm; + int bmindex = bmindex_gray; + Brush brush = new SolidBrush(Color.FromArgb(180, Color.White)); + if (info.Potential == 100) + { + bmindex = bmindex_magenta; + brush = new SolidBrush(Color.FromArgb(150, Color.Plum)); + } + else if (info.Potential == 75) + { + bmindex = bmindex_red; + brush = new SolidBrush(Color.FromArgb(150, Color.Red)); + } + else if (info.Potential == 50) + { + bmindex = bmindex_darkorange; + brush = new SolidBrush(Color.FromArgb(150, Color.DarkOrange)); + } + if (info.Category == PLANECATEGORY.SUPERHEAVY) + bm = new Bitmap(il_Planes_S.Images[bmindex]); + else if (info.Category == PLANECATEGORY.HEAVY) + bm = new Bitmap(il_Planes_H.Images[bmindex]); + else if (info.Category == PLANECATEGORY.MEDIUM) + bm = new Bitmap(il_Planes_M.Images[bmindex]); + else if (info.Category == PLANECATEGORY.LIGHT) + bm = new Bitmap(il_Planes_L.Images[bmindex]); + else + bm = new Bitmap(il_Planes_M.Images[bmindex]); + GMarkerGoogle m = new GMarkerGoogle(new PointLatLng(info.Lat, info.Lon), ToolTipFont, RotateImageByAngle(bm, (float)info.Track)); + m.Tag = info.Hex; + string lat = ""; + if (info.Lat >= 0) + lat = Math.Abs(info.Lat).ToString("00.00") + "°N"; + else + lat = Math.Abs(info.Lat).ToString("00.00") + "°S"; + string lon = ""; + if (info.Lon >= 0) + lon = Math.Abs(info.Lon).ToString("000.00") + "°E"; + else + lon = Math.Abs(info.Lon).ToString("000.00") + "°W"; + m.ToolTipText = info.Call + "\n--------------------"; + if (Properties.Settings.Default.InfoWin_Position) + m.ToolTipText += "\nPos: " + lat + " , " + lon; + if (Properties.Settings.Default.InfoWin_Alt) + { + if (Properties.Settings.Default.InfoWin_Metric) + m.ToolTipText += "\nAlt: " + (int)info.Alt_m + "m"; + else + m.ToolTipText += "\nAlt: " + (int)info.Alt + "ft"; + } + if (Properties.Settings.Default.InfoWin_Track) + m.ToolTipText += "\nTrack: " + (int)info.Track + "°"; + if (Properties.Settings.Default.InfoWin_Type) + m.ToolTipText += "\nType: " + info.Manufacturer + " " + info.Model + " [" + PlaneCategories.GetShortStringValue(info.Category) + "]"; + // set tooltip on if hot + if (selected) + m.ToolTipMode = MarkerTooltipMode.Always; + else + m.ToolTipMode = MarkerTooltipMode.OnMouseOver; + if (m.ToolTip != null) + m.ToolTip.Fill = brush; + return m; + } + + private GMarkerGoogle CreatePlaneDetailed(PlaneInfo info, bool selected) + { + // return on empty info + if (info == null) + return null; + // get bitmap according to category + Bitmap bm; + int bmindex = bmindex_gray; + Brush brush = new SolidBrush(Color.FromArgb(180, Color.White)); + if (info.Potential == 100) + { + bmindex = bmindex_magenta; + brush = new SolidBrush(Color.FromArgb(150, Color.Plum)); + } + else if (info.Potential == 75) + { + bmindex = bmindex_red; + brush = new SolidBrush(Color.FromArgb(150, Color.Red)); + } + else if (info.Potential == 50) + { + bmindex = bmindex_darkorange; + brush = new SolidBrush(Color.FromArgb(150, Color.DarkOrange)); + } + if (info.Category == PLANECATEGORY.SUPERHEAVY) + bm = new Bitmap(il_Planes_S.Images[bmindex]); + else if (info.Category == PLANECATEGORY.HEAVY) + bm = new Bitmap(il_Planes_H.Images[bmindex]); + else if (info.Category == PLANECATEGORY.MEDIUM) + bm = new Bitmap(il_Planes_M.Images[bmindex]); + else if (info.Category == PLANECATEGORY.LIGHT) + bm = new Bitmap(il_Planes_L.Images[bmindex]); + else + bm = new Bitmap(il_Planes_M.Images[bmindex]); + GMarkerGoogle m = new GMarkerGoogle(new PointLatLng(info.Lat, info.Lon), ToolTipFont, RotateImageByAngle(bm, (float)info.Track)); + m.Tag = info.Hex; + string lat = ""; + if (info.Lat >= 0) + lat = Math.Abs(info.Lat).ToString("00.00") + "°N"; + else + lat = Math.Abs(info.Lat).ToString("00.00") + "°S"; + string lon = ""; + if (info.Lon >= 0) + lon = Math.Abs(info.Lon).ToString("000.00") + "°E"; + else + lon = Math.Abs(info.Lon).ToString("000.00") + "°W"; + int mins = 0; + if (info.Speed > 0) + mins = (int)(info.IntQRB / UnitConverter.kts_kmh(info.Speed) * 60.0); + // fill tooltip texts + m.ToolTipText = info.Call + "\n--------------------"; + if (Properties.Settings.Default.InfoWin_Position) + m.ToolTipText += "\nPos: " + lat + " , " + lon; + if (Properties.Settings.Default.InfoWin_Alt) + { + if (Properties.Settings.Default.InfoWin_Metric) + m.ToolTipText += "\nAlt: " + (int)info.Alt_m + "m [" + info.AltDiff.ToString("+#;-#;0") + "m]"; + else + m.ToolTipText += "\nAlt: " + (int)info.Alt + "ft [" + UnitConverter.m_ft(info.AltDiff).ToString("+#;-#;0") + "ft]"; + } + if (Properties.Settings.Default.InfoWin_Track) + m.ToolTipText += "\nTrack: " + info.Track + "°"; + if (Properties.Settings.Default.InfoWin_Speed) + { + if (Properties.Settings.Default.InfoWin_Metric) + m.ToolTipText += "\nSpeed: " + info.Speed_kmh.ToString("F0") + "km/h"; + else + m.ToolTipText += "\nSpeed: " + info.Speed.ToString("F0") + "kts"; + } + if (Properties.Settings.Default.InfoWin_Type) + m.ToolTipText += "\nType: " + info.Manufacturer + " " + info.Model + " [" + PlaneCategories.GetShortStringValue(info.Category) + "]"; + if (Properties.Settings.Default.InfoWin_Dist) + { + if (Properties.Settings.Default.InfoWin_Metric) + m.ToolTipText += "\nDist: " + info.IntQRB.ToString("F0") + "km"; + else + m.ToolTipText += "\nDist: " + UnitConverter.km_mi(info.IntQRB).ToString("F0") + "mi"; + } + if (Properties.Settings.Default.InfoWin_Time) + m.ToolTipText += "\nTime: " + (CurrentTime + new TimeSpan(0, mins, 0)).ToString("HH:mm") + " [ " + mins.ToString("") + "min]"; + if (Properties.Settings.Default.InfoWin_Angle) + m.ToolTipText += "\nAngle: " + (info.Angle / Math.PI * 180.0).ToString("F0") + "°"; + if (Properties.Settings.Default.InfoWin_Epsilon) + m.ToolTipText += "\nEps: " + (info.Eps1 / Math.PI * 180.0).ToString("00.00") + "° <> " + (info.Eps2 / Math.PI * 180.0).ToString("00.00") + "°"; + if (Properties.Settings.Default.InfoWin_Squint) + m.ToolTipText += "\nSquint: " + (info.Squint / Math.PI * 180).ToString("00.00") + "°"; + if (selected) + { + m.ToolTipMode = MarkerTooltipMode.Always; + } + else + { + m.ToolTipMode = MarkerTooltipMode.OnMouseOver; + } + if (m.ToolTip != null) + m.ToolTip.Fill = brush; + return m; + } + + private void DrawPlanes() + { + bool alarm = false; + bool isselected = false; + string alarm_msg = ""; + + List pathplanes = new List(); + + // check if any plane is on list --> return empty list + if ((ActivePlanes == null) || (ActivePlanes.Count == 0)) + return; + // draw all planes + foreach (PlaneInfo plane in ActivePlanes.Values) + { + try + { + // show planes if it meets filter criteria + if ((plane.Alt_m >= Properties.Settings.Default.Planes_MinAlt) && (plane.Category >= Properties.Settings.Default.Planes_Filter_Min_Category)) + { + // check selected state + isselected = SelectedPlanes.IndexOf(plane.Hex) >= 0; + // now, show plane according to potential + switch (plane.Potential) + { + case 100: + gmo_Planes.Markers.Add(CreatePlaneDetailed(plane, isselected)); + // set alarm + if (plane.IntQRB < Properties.Settings.Default.Alarm_Distance) + { + alarm = true; + alarm_msg = plane.Call; + } + break; + case 75: + gmo_Planes.Markers.Add(CreatePlaneDetailed(plane, isselected)); + // set alarm + if (plane.IntQRB < Properties.Settings.Default.Alarm_Distance) + { + alarm = true; + alarm_msg = plane.Call; + } + break; + case 50: + gmo_Planes.Markers.Add(CreatePlaneDetailed(plane, isselected)); + break; + default: + gmo_Planes.Markers.Add(CreatePlaneSimple(plane, isselected)); + break; + } + + // count the planes drawed and update caption + gb_Map.Text = "Map [" + gmo_Planes.Markers.Count.ToString() + " plane(s)]"; + // if selected: draw the thin path to crossing point if one + if (isselected) + { + if (plane.IntPoint != null) + { + GMapRoute intpath = new GMapRoute(plane.Call); + intpath.Stroke = new Pen(Color.Black, 1); + intpath.Points.Add(new PointLatLng(plane.Lat, plane.Lon)); + intpath.Points.Add(new PointLatLng(plane.IntPoint.Lat, plane.IntPoint.Lon)); + gmo_Routes.Routes.Add(intpath); + // Console.WriteLine(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + ";" + info.IntQRB.ToString("F3")); + } + // track plane, if enabled + if (Properties.Settings.Default.Track_Activate) + { + double az = LatLon.Bearing(Properties.Settings.Default.MyLat, + Properties.Settings.Default.MyLon, + plane.Lat, + plane.Lon); + // calculate elevation + // TODO: adjust K-Factor to settings + double h = (GetElevation(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon) + Properties.Settings.Default.MyHeight) / 1000; + double H = plane.Alt_m / 1000.0; + double dist = LatLon.Distance(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, plane.Lat, plane.Lon); + double el = Propagation.EpsilonFromHeights(h, dist, H, (LatLon.Earth.Radius * Properties.Settings.Default.Path_Band_Settings[Properties.Settings.Default.Band].K_Factor) / Math.PI * 180); + // set tracking values + Properties.Settings.Default.Track_SetAz = az; + Properties.Settings.Default.Track_SetEl = el; + } + } + + // show planes on chart if in sigle path mode + if (PathMode == AIRSCOUTPATHMODE.SINGLE) + { + if ((plane.IntPoint != null) && (plane.IntQRB <= Properties.Settings.Default.Path_Band_Settings[Properties.Settings.Default.Band].MaxDistance)) + { + // calculate distance from mylat/mylon + double dist = LatLon.Distance(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, plane.IntPoint.Lat, plane.IntPoint.Lon); + // add new data points + if (plane.AltDiff > 0) + { + Planes_Hi.Points.Add(new DataPoint(dist, plane.Alt_m)); + } + else + { + Planes_Lo.Points.Add(new DataPoint(dist, plane.Alt_m)); + } + } + } + } + + // invalidate chart + pm_Path.InvalidatePlot(true); + + // set alarm if one + if (alarm) + Alarm(alarm_msg); + // stop tracking if selected object is lost for any reason + if (!isselected) + { + Properties.Settings.Default.Track_SetAz = double.NaN; + Properties.Settings.Default.Track_SetEl = double.NaN; + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.Message); + } + } + + } + + private void UpdatePlanes() + { + // get current time + + // update status + UpdateStatus(); + + // check for filter settings + // and color filter box + int planes_filter_minalt = 0; + planes_filter_minalt = Properties.Settings.Default.Planes_Filter_Min_Alt; + if (planes_filter_minalt < 0) + planes_filter_minalt = 0; + if ((planes_filter_minalt != 0) || (Properties.Settings.Default.Planes_Filter_Min_Category > PLANECATEGORY.LIGHT)) + { + pa_Planes_Filter.BackColor = Color.Plum; + } + else + { + pa_Planes_Filter.BackColor = SystemColors.Control; + } + + Stopwatch st = new Stopwatch(); + st.Start(); + List allplanes = Planes.GetAll(CurrentTime, Properties.Settings.Default.Planes_Position_TTL); + // TODO: maintain selected status + st.Stop(); + // Log.WriteMessage("Getting plane positions from database: " + allplanes.Count().ToString() + " plane(s), " + st.ElapsedMilliseconds.ToString() + " ms."); + // clear active planes + ActivePlanes.Clear(); + + foreach (PropagationPathDesignator ppath in PropagationPaths) + { + st.Reset(); + st.Start(); + NearestPlane = null; + // get nearest planes per path + List nearestplanes = AircraftData.Database.GetNearestPlanes(DateTime.UtcNow, ppath, allplanes, Properties.Settings.Default.Planes_Filter_Max_Circumcircle, Properties.Settings.Default.Path_Band_Settings[Properties.Settings.Default.Band].MaxDistance, Properties.Settings.Default.Planes_MaxAlt); + + foreach (PlaneInfo plane in nearestplanes) + { + // add or update plane in active planes list + PlaneInfo activeplane; + if (ActivePlanes.TryGetValue(plane.Hex, out activeplane)) + { + // plane found --> update if necessary + bool update = false; + // plane has higher potential + if (plane.Potential > activeplane.Potential) + update = true; + // plane has same potetial but is nearer to path + else if ((plane.Potential == activeplane.Potential) && (plane.IntQRB < activeplane.IntQRB)) + update = true; + // update if necessary + if (update) + { + ActivePlanes.Remove(activeplane.Hex); + ActivePlanes.Add(plane.Hex, plane); + } + } + else + { + // add plane to list if not foound + ActivePlanes.Add(plane.Hex, plane); + } + // maintain nearest plane info + // check if plane is within MaxDistance + if (plane.IntQRB < Properties.Settings.Default.Path_Band_Settings[Properties.Settings.Default.Band].MaxDistance) + { + if (NearestPlane == null) + { + // set first nearest plane info anyway + NearestPlane = plane; + } + // use higher potential anyway + if (plane.Potential > NearestPlane.Potential) + { + NearestPlane = plane; + } + else if (plane.Potential == NearestPlane.Potential) + { + // use nearer plane if same potential + if (plane.IntQRB < NearestPlane.IntQRB) + NearestPlane = plane; + } + } + } + st.Stop(); + // Log.WriteMessage("Get nearest planes: " + ActivePlanes.Count.ToString() + " plane(s), " + st.ElapsedMilliseconds.ToString() + " ms."); + } + st.Start(); + // clear planes overlay in map + gmo_Planes.Clear(); + // clear all routes except paths + gmo_Routes.Clear(); + + // clear data points in chart + Planes_Hi.Points.Clear(); + Planes_Lo.Points.Clear(); + + // draw planes + DrawPlanes(); + st.Stop(); + // Log.WriteMessage("Drawing planes: " + ActivePlanes.Count.ToString() + " plane(s), " + st.ElapsedMilliseconds.ToString() + " ms."); + + } + + #endregion + + #region Watchlist + + private void UpdateWatchlistInMap() + { + // show callsigns from watchlist + gmo_Callsigns.Clear(); + if (!Properties.Settings.Default.Watchlist_Activated) + return; + // create new watchlist if null + if (Properties.Settings.Default.Watchlist == null) + Properties.Settings.Default.Watchlist = new Watchlist(); + foreach (WatchlistItem item in Properties.Settings.Default.Watchlist) + { + // nasty!! Should never be null! + if (item == null) + continue; + LocationDesignator dxloc = LocationFindOrCreate(item.Call, item.Loc); + GMarkerGoogle gm = new GMarkerGoogle(new PointLatLng(dxloc.Lat, dxloc.Lon), ToolTipFont, (dxloc.Source == GEOSOURCE.FROMUSER) ? GMarkerGoogleType.green_small : GMarkerGoogleType.white_small); + gm.ToolTipText = dxloc.Call; + gm.ToolTipMode = MarkerTooltipMode.OnMouseOver; + gm.Tag = dxloc.Call; + gmo_Callsigns.Markers.Add(gm); + } + } + + private void RefreshWatchlistView() + { + // set watchlistupdating flag + WatchlistUpdating = true; + // update listview + lv_Control_Watchlist.BeginUpdate(); + lv_Control_Watchlist.Items.Clear(); + foreach (WatchlistItem item in Properties.Settings.Default.Watchlist) + { + // nasty!! Should never be null! + if (item == null) + continue; + LocationDesignator dxcall = StationData.Database.LocationFindOrCreate(item.Call, item.Loc); + ListViewItem lvi = new ListViewItem(item.Call); + lvi.Name = "Call"; + ListViewItem.ListViewSubItem lsi = new ListViewItem.ListViewSubItem(lvi, item.Loc); + lsi.Name = "Loc"; + lvi.SubItems.Add(lsi); + lv_Control_Watchlist.Items.Add(lvi); + if (item.Checked) + lvi.Checked = true; + if (item.Selected) + lvi.Selected = true; + // tag item as "Out of Range" + if (item.OutOfRange) + { + lvi.Tag = "OOR"; + lvi.ForeColor = Color.LightGray; + } + else + { + lvi.BackColor = (dxcall.Source == GEOSOURCE.FROMUSER) ? Color.PaleGreen : Color.White; + } + } + lv_Control_Watchlist.Sort(); + lv_Control_Watchlist.EndUpdate(); + // reset watchlistupdating flag + WatchlistUpdating = false; + } + + #endregion + + #region User Interface + + private void MapDlg_SizeChanged(object sender, EventArgs e) + { + try + { + // ensure that the Info panel is fully visible + // set the splitter according to Info panel width + int dist = sc_Main.Width - gb_Map_Info_Width - 5; + if ((dist > 0) && (dist < sc_Main.Width)) + sc_Main.SplitterDistance = dist; + // set the infowin height according to window size +// int height = sc_Main.Height - gb_CommonInfo.Height - gb_Map_Buttons.Height - 5; +// tc_ControlPanel.Height = height; + } + catch + { + // do nothing if window is minimized or otherwise invisible + } + } + + private void MapDlg_Resize(object sender, EventArgs e) + { + } + + private void ti_Progress_Tick(object sender, EventArgs e) + { + if (LifeMode == AIRSCOUTLIFEMODE.LIFE) + { + if (PlayMode == AIRSCOUTPLAYMODE.FORWARD) + { + // update current time + if (Properties.Settings.Default.Time_Mode_Online) + { + CurrentTime = DateTime.UtcNow; + UpdatePlanes(); + } + } + } + else if (LifeMode == AIRSCOUTLIFEMODE.HISTORY) + { + if (PlayMode != AIRSCOUTPLAYMODE.PAUSE) + { + Properties.Settings.Default.Time_Offline = Properties.Settings.Default.Time_Offline.AddSeconds(Time_Offline_Interval); + if (Properties.Settings.Default.Time_Offline < dtp_Analysis_MinValue.Value) + Properties.Settings.Default.Time_Offline = dtp_Analysis_MinValue.Value; + if (Properties.Settings.Default.Time_Offline > dtp_Analysis_MaxValue.Value) + Properties.Settings.Default.Time_Offline = dtp_Analysis_MaxValue.Value; + CurrentTime = Properties.Settings.Default.Time_Offline; + tb_Analysis_Time.Text = CurrentTime.ToString("yyyy-MM-hh HH:mm:ss"); + double span = (CurrentTime - dtp_Analysis_MinValue.Value).TotalSeconds; + if ((span > sb_Analysis_Play.Minimum) && (span < sb_Analysis_Play.Maximum)) + sb_Analysis_Play.Value = (int)span; + UpdatePlanes(); + } + } + } + + private void sc_Main_SplitterMoved(object sender, SplitterEventArgs e) + { + } + + private void sc_Map_SplitterMoved(object sender, SplitterEventArgs e) + { + } + + + + #region Map + + private void gm_Main_OnMapZoomChanged() + { + double midlat = LatLon.MidPoint(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, Properties.Settings.Default.DXLat, Properties.Settings.Default.DXLon).Lat; + double midlon = LatLon.MidPoint(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, Properties.Settings.Default.DXLat, Properties.Settings.Default.DXLon).Lon; + if ((PlayMode == AIRSCOUTPLAYMODE.FORWARD) && Properties.Settings.Default.Map_AutoCenter) + gm_Main.Position = new PointLatLng(midlat, midlon); + tb_Zoom.Text = gm_Main.Zoom.ToString(); + } + + private void gm_Main_OnMarkerClick(GMapMarker item, MouseEventArgs e) + { + try + { + if (e.Button == System.Windows.Forms.MouseButtons.Left) + { + // check if callsign clicked + if (item.Overlay == gmo_Callsigns) + { + string call = (string)item.Tag; + if (Callsign.Check(call) && (call != Properties.Settings.Default.MyCall)) + { + if (PathMode == AIRSCOUTPATHMODE.MULTI) + { + // search call on watchlist + int index = Properties.Settings.Default.Watchlist.IndexOf(call); + if (index >= 0) + { + // toggle checked state + Properties.Settings.Default.Watchlist.ElementAt(index).Checked = !Properties.Settings.Default.Watchlist.ElementAt(index).Checked; + // refresh watchlist view + RefreshWatchlistView(); + // update paths if running + if (PlayMode == AIRSCOUTPLAYMODE.FORWARD) + UpdatePaths(); + } + } + } + } + // check if plane clicked + else if (item.Overlay == gmo_Planes) + { + // keep the selected state + int selectedindex = SelectedPlanes.IndexOf((string)item.Tag); + // clear all other selections if tracking is enabled + if (Properties.Settings.Default.Track_Activate) + { + SelectedPlanes.Clear(); + } + // toogle selection of the selected plane + if (selectedindex >= 0) + { + // remove item from selected planes list + SelectedPlanes.RemoveAt(selectedindex); + // invalidate tracking + Properties.Settings.Default.Track_SetAz = double.NaN; + Properties.Settings.Default.Track_SetEl = double.NaN; + } + else + { + SelectedPlanes.Add((string)item.Tag); + } + } + // check if call clicked + else if (item.Overlay == gmo_Callsigns) + { + LocationDesignator ld = StationData.Database.LocationFind(item.Tag.ToString()); + if (ld != null) + { + if (PlayMode != AIRSCOUTPLAYMODE.PAUSE) + this.Pause(); + Properties.Settings.Default.DXCall = item.Tag.ToString(); + Properties.Settings.Default.DXLat = ld.Lat; + Properties.Settings.Default.DXLon = ld.Lon; + UpdateStatus(); + this.Play(); + } + } + } + if (e.Button == System.Windows.Forms.MouseButtons.Right) + { + // new for tracking antenna + if ((string)item.Tag == Properties.Settings.Default.DXCall) + { + // Right click on DXCall needle --> set antenna position + // get antenna direction + double qtf = LatLon.Bearing(Properties.Settings.Default.MyLat, + Properties.Settings.Default.MyLon, + Properties.Settings.Default.DXLat, + Properties.Settings.Default.DXLon); + // store new QTF in properties + lock (Properties.Settings.Default) + { + Properties.Settings.Default.Track_SetAz = qtf; + Properties.Settings.Default.Track_SetEl = 0; + } + } + } + } + catch (Exception ex) + { + // do nothing if item not found in planes list + Log.WriteMessage(ex.Message); + } + } + + private void gm_Main_OnMarkerEnter(GMapMarker item) + { + if (((item == gmm_MyLoc) || (item == gmm_DXLoc)) && !isDraggingMarker) + gmm_CurrentMarker = item; + } + + private void gm_Main_OnMarkerLeave(GMapMarker item) + { + + } + + private void gm_Main_MouseDown(object sender, MouseEventArgs e) + { + if (e.Button == MouseButtons.Left && gmm_CurrentMarker != null && gmm_CurrentMarker.IsMouseOver) + isDraggingMarker = true; + } + + private void gm_Main_MouseMove(object sender, MouseEventArgs e) + { + } + + private void gm_Main_MouseUp(object sender, MouseEventArgs e) + { + if (isDraggingMarker) + { + isDraggingMarker = false; + } + } + + private void gm_Main_Paint(object sender, PaintEventArgs e) + { + // paint gauges on top of the map if enabled + if (Properties.Settings.Default.Track_Activate && + (Properties.Settings.Default.Track_SetAz >= 0) && + (Properties.Settings.Default.Track_SetAz <= 360) && + (Properties.Settings.Default.Track_SetEl >= 0) && + (Properties.Settings.Default.Track_SetEl <= 90)) + { + // update gauges + ag_Azimuth.Value = (float)Properties.Settings.Default.Track_SetAz; + ag_Elevation.Value = (float)Properties.Settings.Default.Track_SetEl; + // draw elements + int ofsX = ag_Azimuth.Left; + int ofsY = ag_Azimuth.Top; + ag_Azimuth.DrawDialText(e.Graphics, ofsX, ofsY); + ag_Azimuth.DisplayNumber(e.Graphics, ofsX, ofsY); + ag_Azimuth.DrawCalibration(e.Graphics, ofsX, ofsY); + ag_Azimuth.DrawCenterPoint(e.Graphics, ofsX, ofsY); + ag_Azimuth.DrawPointer(e.Graphics, ofsX, ofsY); + + ofsX = ag_Elevation.Left; + ofsY = ag_Elevation.Top; + ag_Elevation.DrawDialText(e.Graphics, ofsX, ofsY); + ag_Elevation.DisplayNumber(e.Graphics, ofsX, ofsY); + ag_Elevation.DrawCalibration(e.Graphics, ofsX, ofsY); + ag_Elevation.DrawCenterPoint(e.Graphics, ofsX, ofsY); + ag_Elevation.DrawPointer(e.Graphics, ofsX, ofsY); + } + } + + private void gm_Main_SizeChanged(object sender, EventArgs e) + { + // adjust position of gauges + ag_Azimuth.Left = gm_Main.Right - ag_Azimuth.Width - ag_Elevation.Width - 40; + ag_Azimuth.Top = gm_Main.Bottom - ag_Azimuth.Height - 20; + + ag_Elevation.Left = gm_Main.Right - ag_Elevation.Width - 20; + ag_Elevation.Top = gm_Main.Bottom - ag_Elevation.Height - 20; + } + + #endregion + + #region Right Controls + + private void cb_Band_SelectedIndexChanged(object sender, EventArgs e) + { + if (cb_Band.SelectedItem != null) + Properties.Settings.Default.Band = Bands.ParseStringValue((string)cb_Band.SelectedItem); + else Properties.Settings.Default.Band = BAND.BNONE; + SaveUserSettings(); + Properties.Settings.Default.Reload(); + } + + #region Tab Control Control Panel + + private void tc_Control_DrawItem(object sender, DrawItemEventArgs e) + { + // This event is called once for each tab button in your tab control + // First paint the background with a color based on the current tab + // e.Index is the index of the tab in the TabPages collection. + switch (e.Index) + { + case 0: + e.Graphics.FillRectangle(new SolidBrush(SystemColors.Control), e.Bounds); + break; + case 1: + e.Graphics.FillRectangle(new SolidBrush(SystemColors.Control), e.Bounds); + break; + case 2: + e.Graphics.FillRectangle(new SolidBrush((Properties.Settings.Default.Planes_Filter_Min_Category > PLANECATEGORY.LIGHT) ? Color.Plum : SystemColors.Control), e.Bounds); + break; + default: + break; + } + + // Then draw the current tab button text + Rectangle paddedBounds = e.Bounds; + paddedBounds.Inflate(-2, -2); + e.Graphics.DrawString(tc_Control.TabPages[e.Index].Text, tc_Control.Font, (tc_Control.Enabled) ? SystemBrushes.ControlText : SystemBrushes.GrayText, paddedBounds); + } + + + #region Single Tab + + private void tp_Control_Single_Enter(object sender, EventArgs e) + { + if (PathMode != AIRSCOUTPATHMODE.SINGLE) + { + PathMode = AIRSCOUTPATHMODE.SINGLE; + // restore splitter distance + sc_Map.SplitterDistance = CurrentMapSplitterDistance; + } + } + + private void gb_Map_Info_MouseClick(object sender, MouseEventArgs e) + { + // get font size in pixels + double pixels; + using (Graphics g = this.CreateGraphics()) + { + pixels = gb_Map_Info.Font.SizeInPoints * g.DpiX / 72; + } + if ((e.Y <= pixels) && (e.Button == MouseButtons.Left)) + { + Properties.Settings.Default.Map_ShowInfoBox = !Properties.Settings.Default.Map_ShowInfoBox; + } + } + + + private void cb_MyCall_TextChanged(object sender, EventArgs e) + { + Console.WriteLine("cb_MyCall_TextChanged: " + cb_MyCall.Text); + int i = cb_MyCall.SelectionStart; + Properties.Settings.Default.MyCall = cb_MyCall.Text; + // clear locator entries + cb_MyLoc.Text = ""; + cb_MyLoc.Items.Clear(); + Properties.Settings.Default.MyLat = double.NaN; + Properties.Settings.Default.MyLon = double.NaN; + Properties.Settings.Default.MyElevation = 0; + if (Callsign.Check(cb_MyCall.Text)) + { + // select last recent loc + LocationDesignator ld = StationData.Database.LocationFind(cb_MyCall.Text); + if (ld != null) + { + Properties.Settings.Default.MyLat = ld.Lat; + Properties.Settings.Default.MyLon = ld.Lon; + Properties.Settings.Default.MyElevation = GetElevation(ld.Lat, ld.Lon); + } + } + UpdateStatus(); + cb_MyCall.Select(i, 0); + } + + private void cb_MyCall_SelectedIndexChanged(object sender, EventArgs e) + { + if (cb_MyCall.SelectedItem != null) + { + cb_MyCall.Text = cb_MyCall.SelectedItem.ToString(); + LocationDesignator ld = StationData.Database.LocationFind(cb_MyCall.Text); + if (Callsign.Check(cb_MyCall.Text) && (ld != null)) + { + // update Settings + cb_MyLoc.Text = MaidenheadLocator.LocFromLatLon(ld.Lat, ld.Lon, Properties.Settings.Default.Locator_SmallLettersForSubsquares, (int)Properties.Settings.Default.Locator_MaxLength / 2, Properties.Settings.Default.Locator_AutoLength); + Properties.Settings.Default.MyCall = cb_MyCall.Text; + Properties.Settings.Default.MyLat = ld.Lat; + Properties.Settings.Default.MyLon = ld.Lon; + Properties.Settings.Default.MyElevation = GetElevation(ld.Lat, ld.Lon); + Properties.Settings.Default.MyHeight = 10; + } + else + { + Properties.Settings.Default.MyLat = double.NaN; + Properties.Settings.Default.MyLon = double.NaN; + Properties.Settings.Default.MyElevation = 0; + Properties.Settings.Default.MyHeight = 0; + } + UpdateStatus(); + } + } + + private void cb_MyCall_DropDown(object sender, EventArgs e) + { + // poulate from MyCalls last recent collection + // populate drop down list + cb_MyCall.Items.Clear(); + foreach (string call in Properties.Settings.Default.MyCalls) + { + cb_MyCall.Items.Add(call); + } + } + + private void cb_MyLoc_TextChanged(object sender, EventArgs e) + { + if (cb_MyLoc.Focused) + { + if (MaidenheadLocator.Check(cb_MyLoc.Text) && (cb_MyLoc.Text.Length >= 6)) + { + Properties.Settings.Default.MyLat = cb_MyLoc.GeoLocation.Lat; + Properties.Settings.Default.MyLon = cb_MyLoc.GeoLocation.Lon; + Properties.Settings.Default.MyElevation = GetElevation(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon); + Properties.Settings.Default.MyHeight = 10; + // colour Textbox if more precise lat/lon information is available + if (MaidenheadLocator.IsPrecise(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, 3)) + { + cb_MyLoc.BackColor = Color.PaleGreen; + } + UpdateStatus(); + } + else + { + cb_MyLoc.BackColor = Color.FloralWhite; + } + } + } + + private void cb_MyLoc_DropDown(object sender, EventArgs e) + { + // fill MyLoc combo box with all known locators + cb_MyLoc.BackColor = Color.FloralWhite; + cb_MyLoc.SilentText = ""; + cb_MyLoc.Items.Clear(); + List lds = StationData.Database.LocationFindAll(cb_MyCall.Text); + if (lds != null) + { + // fill item list of locator box + foreach (LocationDesignator ld in lds) + { + LocatorDropDownItem ldd = new LocatorDropDownItem(MaidenheadLocator.LocFromLatLon(ld.Lat, ld.Lon, Properties.Settings.Default.Locator_SmallLettersForSubsquares, (int)Properties.Settings.Default.Locator_MaxLength / 2, Properties.Settings.Default.Locator_AutoLength), new LatLon.GPoint(ld.Lat, ld.Lon)); + cb_MyLoc.Items.Add(ldd); + } + } + } + + private void cb_MyLoc_SelectedIndexChanged(object sender, EventArgs e) + { + LocatorDropDownItem ldd = (LocatorDropDownItem)cb_MyLoc.SelectedItem; + if (ldd != null) + { + // update properties + cb_MyLoc.Precision = (int)Properties.Settings.Default.Locator_MaxLength / 2; + cb_MyLoc.SmallLettersForSubsquares = Properties.Settings.Default.Locator_SmallLettersForSubsquares; + cb_MyLoc.AutoLength = Properties.Settings.Default.Locator_AutoLength; + // set geolocation instead of text + cb_MyLoc.GeoLocation = ldd.GeoLocation; + } + } + + private void cb_MyLoc_SelectionChangeCommittedWithNoUpdate(object sender, EventArgs e) + { + + } + + private void cb_DXCall_TextChanged(object sender, EventArgs e) + { + int i = cb_DXCall.SelectionStart; + Properties.Settings.Default.DXCall = cb_DXCall.Text; + // clear locator entries + cb_DXLoc.Text = ""; + cb_DXLoc.Items.Clear(); + Properties.Settings.Default.DXLat = double.NaN; + Properties.Settings.Default.DXLon = double.NaN; + Properties.Settings.Default.DXElevation = 0; + if (Callsign.Check(cb_DXCall.Text)) + { + // select last recent loc + LocationDesignator ld = StationData.Database.LocationFind(cb_DXCall.Text); + if (ld != null) + { + Properties.Settings.Default.DXLat = ld.Lat; + Properties.Settings.Default.DXLon = ld.Lon; + Properties.Settings.Default.DXElevation = GetElevation(ld.Lat, ld.Lon); + } + } + UpdateStatus(); + cb_DXCall.Select(i, 0); + } + + private void cb_DXCall_SelectedIndexChanged(object sender, EventArgs e) + { + if (cb_DXCall.SelectedItem != null) + { + cb_DXCall.Text = cb_DXCall.SelectedItem.ToString(); + LocationDesignator ld = StationData.Database.LocationFind(cb_DXCall.Text); + if (Callsign.Check(cb_DXCall.Text) && (ld != null)) + { + // update Settings + cb_DXLoc.Text = MaidenheadLocator.LocFromLatLon(ld.Lat, ld.Lon, Properties.Settings.Default.Locator_SmallLettersForSubsquares, (int)Properties.Settings.Default.Locator_MaxLength / 2, Properties.Settings.Default.Locator_AutoLength); + Properties.Settings.Default.DXCall = cb_DXCall.Text; + Properties.Settings.Default.DXLat = ld.Lat; + Properties.Settings.Default.DXLon = ld.Lon; + Properties.Settings.Default.DXElevation = GetElevation(ld.Lat, ld.Lon); + Properties.Settings.Default.DXHeight = 10; + } + else + { + Properties.Settings.Default.DXLat = double.NaN; + Properties.Settings.Default.DXLon = double.NaN; + Properties.Settings.Default.DXElevation = 0; + Properties.Settings.Default.DXHeight = 0; + } + UpdateStatus(); + } + } + + private void cb_DXCall_DropDown(object sender, EventArgs e) + { + // populate from watchlist + // return on empty watchlist + if (Properties.Settings.Default.Watchlist == null) + return; + // populate drop down list + List dxcalls = new List(); + foreach (WatchlistItem item in Properties.Settings.Default.Watchlist) + { + // nasty!! Should never be null! + if (item == null) + continue; + dxcalls.Add(item.Call); + } + dxcalls.Sort(); + cb_DXCall.Items.Clear(); + cb_DXCall.Items.AddRange(dxcalls.ToArray()); + } + + + private void cb_DXLoc_TextChanged(object sender, EventArgs e) + { + if (cb_DXLoc.Focused) + { + if (MaidenheadLocator.Check(cb_DXLoc.Text) && (cb_DXLoc.Text.Length >= 6)) + { + Properties.Settings.Default.DXLat = cb_DXLoc.GeoLocation.Lat; + Properties.Settings.Default.DXLon = cb_DXLoc.GeoLocation.Lon; + Properties.Settings.Default.DXElevation = GetElevation(Properties.Settings.Default.DXLat, Properties.Settings.Default.DXLon); + Properties.Settings.Default.DXHeight = 10; + // colour Textbox if more precise lat/lon information is available + if (MaidenheadLocator.IsPrecise(Properties.Settings.Default.DXLat, Properties.Settings.Default.DXLon, 3)) + { + cb_DXLoc.BackColor = Color.PaleGreen; + } + UpdateStatus(); + } + else + { + cb_DXLoc.BackColor = Color.FloralWhite; + } + } + } + + private void cb_DXLoc_DropDown(object sender, EventArgs e) + { + // fill DXLoc combo box with all known locators + cb_DXLoc.BackColor = Color.FloralWhite; + cb_DXLoc.SilentText = ""; + cb_DXLoc.Items.Clear(); + List lds = StationData.Database.LocationFindAll(cb_DXCall.Text); + if (lds != null) + { + // fill item list of locator box + foreach (LocationDesignator ld in lds) + { + LocatorDropDownItem ldd = new LocatorDropDownItem(MaidenheadLocator.LocFromLatLon(ld.Lat, ld.Lon, Properties.Settings.Default.Locator_SmallLettersForSubsquares, (int)Properties.Settings.Default.Locator_MaxLength / 2, Properties.Settings.Default.Locator_AutoLength), new LatLon.GPoint(ld.Lat, ld.Lon)); + cb_DXLoc.Items.Add(ldd); + } + } + } + + private void cb_DXLoc_SelectedIndexChanged(object sender, EventArgs e) + { + LocatorDropDownItem ldd = (LocatorDropDownItem)cb_DXLoc.SelectedItem; + if (ldd != null) + { + // update properties + cb_DXLoc.Precision = (int)Properties.Settings.Default.Locator_MaxLength / 2; + cb_DXLoc.SmallLettersForSubsquares = Properties.Settings.Default.Locator_SmallLettersForSubsquares; + cb_DXLoc.AutoLength = Properties.Settings.Default.Locator_AutoLength; + // set geolocation instead of text + cb_DXLoc.GeoLocation = ldd.GeoLocation; + } + } + + private void cb_DXLoc_SelectionChangeCommittedWithNoUpdate(object sender, EventArgs e) + { + } + + #endregion + + #region Tab Multi + + private void tp_Control_Multi_Enter(object sender, EventArgs e) + { + if (PathMode != AIRSCOUTPATHMODE.MULTI) + { + CurrentMapSplitterDistance = sc_Map.SplitterDistance; + PathMode = AIRSCOUTPATHMODE.MULTI; + sc_Map.SplitterDistance = gm_Main.Height + tc_Main.Height; + } + } + + private void lv_Control_Watchlist_Resize(object sender, EventArgs e) + { + // list view resized + // resize locator column to fit the client size + lv_Control_Watchlist.Columns[1].Width = lv_Control_Watchlist.ClientSize.Width - lv_Control_Watchlist.Columns[0].Width; + } + + private void lv_Control_Watchlist_ColumnWidthChanged(object sender, ColumnWidthChangedEventArgs e) + { + // adjust width of Locator column to list view width + if (e.ColumnIndex == 0) + { + // call sign column changed + // resize locator column to fit the client size + lv_Control_Watchlist.Columns[1].Width = lv_Control_Watchlist.ClientSize.Width - lv_Control_Watchlist.Columns[0].Width; + } + } + + private void lv_Control_Watchlist_ItemChecked(object sender, ItemCheckedEventArgs e) + { + // ignore event while populating list view + if (WatchlistUpdating) + return; + // sync watchlist with list view + ListViewItem lvi = e.Item; + if (lvi == null) + return; + // search item in watchlist + int index = Properties.Settings.Default.Watchlist.IndexOf(lvi.Text, lvi.SubItems[1].Text); + if (index >= 0) + Properties.Settings.Default.Watchlist[index].Checked = lvi.Checked; + if (WatchlistAllCheckedChanging) + return; + // maintain AllChecked checkbox + foreach (ListViewItem item in lv_Control_Watchlist.Items) + { + // stop on first different checked state + if ((item != null) && (item.Checked != lvi.Checked)) + { + WatchlistAllCheckedState = CheckBoxState.MixedNormal; + lv_Control_Watchlist.AutoResizeColumn(0, ColumnHeaderAutoResizeStyle.HeaderSize); + return; + } + } + // all items in the same state + WatchlistAllCheckedState = (lvi.Checked) ? CheckBoxState.CheckedNormal : CheckBoxState.UncheckedNormal; + lv_Control_Watchlist.AutoResizeColumn(0, ColumnHeaderAutoResizeStyle.HeaderSize); + } + + private void lv_Control_Watchlist_ColumnClick(object sender, ColumnClickEventArgs e) + { + WatchlistAllCheckedChanging = true; + if (!WatchlistAllChecked) + { + WatchlistAllChecked = true; + WatchlistAllCheckedState = CheckBoxState.CheckedPressed; + foreach (ListViewItem item in lv_Control_Watchlist.Items) + { + // check all items within range + if ((item.Tag == null) || (item.Tag.ToString() != "OOR")) + item.Checked = true; + } + + Invalidate(); + } + else + { + WatchlistAllChecked = false; + WatchlistAllCheckedState = CheckBoxState.UncheckedNormal; + Invalidate(); + + foreach (ListViewItem item in lv_Control_Watchlist.Items) + { + item.Checked = false; + } + } + WatchlistAllCheckedChanging = false; + } + + private void lv_Control_Watchlist_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e) + { + TextFormatFlags flags = TextFormatFlags.LeftAndRightPadding; + e.DrawBackground(); + CheckBoxRenderer.DrawCheckBox(e.Graphics, new System.Drawing.Point(ClientRectangle.Location.X + 4, ClientRectangle.Location.Y), WatchlistAllCheckedState); + e.DrawText(flags); + } + + private void lv_Control_Watchlist_DrawItem(object sender, DrawListViewItemEventArgs e) + { + e.DrawDefault = true; + } + + private void lv_Control_Watchlist_DrawSubItem(object sender, DrawListViewSubItemEventArgs e) + { + e.DrawDefault = true; + } + + private void btn_Control_Manage_Watchlist_Click(object sender, EventArgs e) + { + WatchlistDlg Dlg = new WatchlistDlg(); + if (Dlg.ShowDialog() == DialogResult.OK) + { + // sync watchlist + foreach (WatchlistItem item in Properties.Settings.Default.Watchlist) + { + // nasty!! Should never be null! + if (item == null) + continue; + item.Remove = true; + } + foreach (ListViewItem lvi in Dlg.lv_Watchlist_Selected.Items) + { + // search item in watchlist + int index = Properties.Settings.Default.Watchlist.IndexOf(lvi.Text); + // reset remove flag if found, create and add new entry if not + if (index >= 0) + Properties.Settings.Default.Watchlist[index].Remove = false; + else + { + // try to find last recent locator from database and add to watchlist + LocationDesignator dxcall = StationData.Database.LocationFindLastRecent(lvi.Text); + if (dxcall != null) + { + double qrb = LatLon.Distance(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, dxcall.Lat, dxcall.Lon); + Properties.Settings.Default.Watchlist.Add(new WatchlistItem(dxcall.Call, dxcall.Loc, qrb > Properties.Settings.Default.Path_MaxLength)); + } + } + } + // remove the rest of items + Properties.Settings.Default.Watchlist.RemoveAll(item => item.Remove); + // refersh watchlist view + RefreshWatchlistView(); + } + } + + private void ShowToolTip(ToolTip tt, string text, Control control, System.Drawing.Point p, int ms = 5000) + { + if (String.IsNullOrEmpty(text)) + return; + // int BorderWidth = this.Width – this.ClientSize.Width)/2; + // int TitlebarHeight = this.Height – this.ClientSize.Height – 2 * BorderWidth; + int BorderWith = (this.Width - this.ClientSize.Width) / 2; + int TitleBarHeight = this.Height - this.ClientSize.Height - 2 * BorderWith; + p = control.PointToScreen(p); + p = this.PointToClient(p); + p.X = p.X + BorderWith; + p.Y = p.Y + TitleBarHeight + Cursor.Size.Height; + tt.Show(text, this, p, ms); + } + + private void lv_Control_Watchlist_ItemMouseHover(object sender, ListViewItemMouseHoverEventArgs e) + { + } + + private void lv_Control_Watchlist_MouseMove(object sender, MouseEventArgs e) + { + try + { + System.Drawing.Point p = new System.Drawing.Point(e.X, e.Y); + ListViewHitTestInfo info = lv_Control_Watchlist.HitTest(p); + if ((WatchlistOldMousePos != p) && (info != null) && (info.SubItem != null)) + { + WatchlistOldMousePos = p; + // check whether the column name is one of the following + if (info.SubItem.Name == "Call") + { + string dxcall = info.Item.SubItems[0].Text; + string dxloc = info.Item.SubItems[1].Text; + LocationDesignator ld = StationData.Database.LocationFind(dxcall, dxloc); + // location found --> show Tooltip with details + if (ld != null) + { + string qrb = LatLon.Distance(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, ld.Lat, ld.Lon).ToString("F0", CultureInfo.InvariantCulture); + string qtf = LatLon.Bearing(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, ld.Lat, ld.Lon).ToString("F0", CultureInfo.InvariantCulture); + ShowToolTip(tt_Control_Watchlist, dxcall + "\n" + dxloc + "\n" + qrb + " km\n" + qtf + "°", lv_Control_Watchlist, p); + } + } + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + + #endregion + + #region Tab Options + + private void tp_Control_Options_Enter(object sender, EventArgs e) + { + // set plane filter + try + { + cb_Planes_Filter_Min_Cat.SelectedItem = PlaneCategories.GetStringValue(Properties.Settings.Default.Planes_Filter_Min_Category); + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + + private void gb_Map_Zoom_MouseClick(object sender, MouseEventArgs e) + { + // get font size in pixels + double pixels; + using (Graphics g = this.CreateGraphics()) + { + pixels = gb_Map_Zoom.Font.SizeInPoints * g.DpiX / 72; + } + if ((e.Y <= pixels) && (e.Button == MouseButtons.Left)) + { + Properties.Settings.Default.Map_ShowZoomBox = !Properties.Settings.Default.Map_ShowZoomBox; + } + } + + private void gb_Map_Filter_MouseClick(object sender, MouseEventArgs e) + { + // get font size in pixels + double pixels; + using (Graphics g = this.CreateGraphics()) + { + pixels = gb_Map_Filter.Font.SizeInPoints * g.DpiX / 72; + } + if ((e.Y <= pixels) && (e.Button == MouseButtons.Left)) + { + Properties.Settings.Default.Map_ShowFilterBox = !Properties.Settings.Default.Map_ShowFilterBox; + } + } + + private void gb_Map_Alarms_MouseClick(object sender, MouseEventArgs e) + { + // get font size in pixels + double pixels; + using (Graphics g = this.CreateGraphics()) + { + pixels = gb_Map_Alarms.Font.SizeInPoints * g.DpiX / 72; + } + if ((e.Y <= pixels) && (e.Button == MouseButtons.Left)) + { + Properties.Settings.Default.Map_ShowAlarmBox = !Properties.Settings.Default.Map_ShowAlarmBox; + } + } + + private void btn_Zoom_In_Click(object sender, EventArgs e) + { + if (gm_Main.Zoom < 20) + gm_Main.Zoom++; + } + + private void btn_Zoom_Out_Click(object sender, EventArgs e) + { + if (gm_Main.Zoom > 0) + gm_Main.Zoom--; + } + + private void cb_Planes_Filter_Min_Category_SelectedIndexChanged(object sender, EventArgs e) + { + try + { + Properties.Settings.Default.Planes_Filter_Min_Category = PlaneCategories.ParseStringValue((string)cb_Planes_Filter_Min_Cat.SelectedItem); + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + + + #endregion + + #endregion + + #region Buttons + + private void btn_Options_Click(object sender, EventArgs e) + { + ShowOptionsDlg(); + } + + private void btn_Map_Save_Click(object sender, EventArgs e) + { + MapSave(); + } + + private void btn_Map_PlayPause_Click(object sender, EventArgs e) + { + if (PlayMode != AIRSCOUTPLAYMODE.FORWARD) + { + Play(); + } + else + { + Pause(); + } + } + + + #endregion + + #endregion + + #region Main Tab Control + + private void tc_Main_SelectedIndexChanged(object sender, EventArgs e) + { + // restore splitter distance + if (tc_Main.SelectedTab != tp_News) + sc_Map.SplitterDistance = CurrentMapSplitterDistance; + } + + private void tc_Main_DrawItem(object sender, DrawItemEventArgs e) + { + // This event is called once for each tab button in your tab control + // First paint the background with a color based on the current tab + // e.Index is the index of the tab in the TabPages collection. + // fill background + e.Graphics.FillRectangle(new SolidBrush(SystemColors.Control), e.Bounds); + // Then draw the current tab button text + Rectangle paddedBounds = e.Bounds; + paddedBounds.Inflate(-2, -2); + e.Graphics.DrawString(tc_Main.TabPages[e.Index].Text, tc_Main.Font, (tc_Main.Enabled) ? SystemBrushes.ControlText : SystemBrushes.GrayText, paddedBounds); + + } + + #region Tab Page News + + private void tp_News_Enter(object sender, EventArgs e) + { + CurrentMapSplitterDistance = sc_Map.SplitterDistance; + // adjust splitter to maximum if not running under Linux/Mono + if (!SupportFunctions.IsMono) + sc_Map.SplitterDistance = 200; + } + + #endregion + + #region Tab Page Spectrum + + private void tp_Spectrum_Enter(object sender, EventArgs e) + { + // adjust splitter back to normal + sc_Map.SplitterDistance = CurrentMapSplitterDistance; + } + + private void tp_Spectrum_Resize(object sender, EventArgs e) + { + // adjust group boxes + // get quadratical nearest plane box + gb_NearestPlaneMap.Width = gb_NearestPlaneMap.Height; + gb_NearestPlaneMap.Left = tp_Spectrum.Width - gb_NearestPlaneMap.Width - 5; + // adjust plane info box + gb_Spectrum_NearestInfo.Left = tp_Spectrum.Width - gb_NearestPlaneMap.Width - gb_Spectrum_NearestInfo.Width - 10; + // adjust spectrum box + gb_Spectrum.Width = tp_Spectrum.Width - gb_NearestPlaneMap.Width - gb_Spectrum_NearestInfo.Width - 30; + tb_Spectrum_Status.Left = 5; + tb_Spectrum_Status.Width = pv_Spectrum.Width - 10; + } + + #endregion + + #region Tab Page Elevation + + private void tp_Elevation_Enter(object sender, EventArgs e) + { + // adjust splitter back to normal + sc_Map.SplitterDistance = CurrentMapSplitterDistance; + } + + private void tp_Elevation_Resize(object sender, EventArgs e) + { + // adjust charts + pv_Path.Location = new System.Drawing.Point(0, 0); + pv_Path.Width = tp_Elevation.Width; + pv_Path.Height = tp_Elevation.Height / 2; + pv_Elevation.Location = new System.Drawing.Point(0, tp_Elevation.Height / 2); + pv_Elevation.Width = tp_Elevation.Width; + pv_Elevation.Height = tp_Elevation.Height / 2; + } + + + #endregion + + #region Tab Page Analysis + + private void UpdatePlayer() + { + // set players bounds + sb_Analysis_Play.Minimum = 0; + sb_Analysis_Play.Maximum = (int)(dtp_Analysis_MaxValue.Value - dtp_Analysis_MinValue.Value).TotalSeconds; + sb_Analysis_Play.Value = 0; + } + + private void tp_Analysis_Enter(object sender, EventArgs e) + { + if (LifeMode == AIRSCOUTLIFEMODE.LIFE) + { + btn_Analysis_ON.Enabled = true; + btn_Analysis_ON.BackColor = Color.YellowGreen; + btn_Analysis_OFF.Enabled = false; + btn_Analysis_OFF.BackColor = Color.Gray; + } + else + { + btn_Analysis_ON.Enabled = false; + btn_Analysis_ON.BackColor = Color.Gray; + btn_Analysis_OFF.Enabled = true; + btn_Analysis_OFF.BackColor = Color.LightCoral; + } + SayAnalysis("Press to start analysis."); + } + + private void tp_Analysis_Leave(object sender, EventArgs e) + { + } + + private void btn_Analysis_ON_Click(object sender, EventArgs e) + { + SayAnalysis("Getting database properties, please wait..."); + // go into offline mode + btn_Analysis_ON.Enabled = false; + btn_Analysis_ON.BackColor = Color.Gray; + btn_Analysis_OFF.Enabled = true; + btn_Analysis_OFF.BackColor = Color.LightCoral; + btn_Map_PlayPause.Enabled = false; + LifeMode = AIRSCOUTLIFEMODE.HISTORY; + PlayMode = AIRSCOUTPLAYMODE.PAUSE; + if (!bw_Analysis_DataGetter.IsBusy) + bw_Analysis_DataGetter.RunWorkerAsync(); + } + + private void btn_Analysis_OFF_Click(object sender, EventArgs e) + { + if (bw_Analysis_FileSaver.IsBusy) + bw_Analysis_FileSaver.CancelAsync(); + if (bw_Analysis_FileLoader.IsBusy) + bw_Analysis_FileLoader.CancelAsync(); + if (bw_Analysis_DataGetter.IsBusy) + { + SayAnalysis("Cancelling background thread, please wait..."); + bw_Analysis_DataGetter.CancelAsync(); + } + else + { + btn_Analysis_ON.Enabled = true; + btn_Analysis_ON.BackColor = Color.YellowGreen; + } + lock (AllPositions) + { + AllPositions.Clear(); + } + GC.Collect(); + // go into online mode + btn_Analysis_OFF.Enabled = false; + btn_Analysis_OFF.BackColor = Color.Gray; + btn_Analysis_Planes_Load.Enabled = false; + btn_Analysis_Planes_Save.Enabled = false; + btn_Analysis_Planes_Clear.Enabled = false; + btn_Analysis_Planes_History.Enabled = false; + btn_Analysis_Planes_ShowTraffic.Enabled = false; + btn_Analysis_Path_SaveToFile.Enabled = false; + btn_Analysis_CrossingHistory.Enabled = false; + btn_Analysis_Plane_History.Enabled = false; + btn_Analysis_Rewind.Enabled = false; + btn_Analysis_Back.Enabled = false; + btn_Analysis_Pause.Enabled = false; + btn_Analysis_Forward.Enabled = false; + btn_Analysis_FastForward.Enabled = false; + sb_Analysis_Play.Enabled = false; + dtp_Analysis_MinValue.Enabled = false; + dtp_Analysis_MaxValue.Enabled = false; + btn_Map_PlayPause.Enabled = true; + LifeMode = AIRSCOUTLIFEMODE.LIFE; + PlayMode = AIRSCOUTPLAYMODE.PAUSE; + UpdateStatus(); + btn_Map_PlayPause.Focus(); + } + + private void btn_Analysis_Planes_Save_Click(object sender, EventArgs e) + { + try + { + SaveFileDialog Dlg = new SaveFileDialog(); + Dlg.AddExtension = true; + Dlg.Filter = "Java Script Object Notation File|*.json|Comma Separated Values|*.csv"; + Dlg.DefaultExt = "json"; + Dlg.FileName = "Plane Positions " + dtp_Analysis_MinValue.Value.ToString("yyyy_MM_dd_HH_mm_ss") + " to " + dtp_Analysis_MaxValue.Value.ToString("yyyy_MM_dd_HH_mm_ss"); + Dlg.InitialDirectory = TmpDirectory; + Dlg.OverwritePrompt = true; + if (Dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) + { + if (!bw_Analysis_FileSaver.IsBusy) + { + btn_Analysis_Planes_Save.Enabled = false; + bw_Analysis_FileSaver.RunWorkerAsync(Dlg.FileName); + } + } + } + catch (Exception ex) + { + Say(ex.Message); + Log.WriteMessage(ex.ToString()); + } + } + + private void btn_Analysis_Planes_Load_Click(object sender, EventArgs e) + { + try + { + OpenFileDialog Dlg = new OpenFileDialog(); + Dlg.Filter = "Java Script Object Notation File|*.json|Comma Separated Values|*.csv"; + Dlg.DefaultExt = "json"; + Dlg.CheckFileExists = true; + Dlg.CheckPathExists = true; + Dlg.Multiselect = false; + Dlg.InitialDirectory = TmpDirectory; + if (Dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) + { + if (!bw_Analysis_FileLoader.IsBusy) + { + btn_Analysis_Planes_Load.Enabled = false; + bw_Analysis_FileLoader.RunWorkerAsync(Dlg.FileName); + } + } + } + catch (Exception ex) + { + Say(ex.Message); + Log.WriteMessage(ex.ToString()); + } + } + + private void btn_Analysis_Planes_Clear_Click(object sender, EventArgs e) + { + AircraftPositionData.Database.AircraftPositionDeleteAll(); + } + + private void btn_Analysis_Planes_History_Click(object sender, EventArgs e) + { + HistoryFromURLDlg Dlg = new HistoryFromURLDlg(); + if (Dlg.ShowDialog() == DialogResult.OK) + { + btn_Analysis_Planes_History.Enabled = false; + bw_HistoryDownloader.RunWorkerAsync(Properties.Settings.Default.Analysis_History_Date); + } + } + + private void btn_Analysis_Planes_ShowTraffic_Click(object sender, EventArgs e) + { + TrafficDlg Dlg = new TrafficDlg(); + Dlg.ShowDialog(); + } + + private void btn_Analysis_Path_SaveToFile_Click(object sender, EventArgs e) + { + SaveFileDialog Dlg = new SaveFileDialog(); + Dlg.CheckPathExists = true; + Dlg.AddExtension = true; + Dlg.DefaultExt = "csv"; + Dlg.Filter = "Comma Separated Values *.csv |csv"; + Dlg.FileName = "Path Information " + Properties.Settings.Default.MyCall.Replace("/", "_") + " to " + Properties.Settings.Default.DXCall.Replace("/", "_"); + Dlg.InitialDirectory = TmpDirectory; + Dlg.OverwritePrompt = true; + if (Dlg.ShowDialog() == DialogResult.OK) + { + try + { + // calculate propagation path + + // check and update station database + LocationDesignator myloc = LocationFindOrUpdateOrCreate(Properties.Settings.Default.MyCall, Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon); + Properties.Settings.Default.MyElevation = myloc.Elevation; + + // get qrv info or create default + QRVDesignator myqrv = StationData.Database.QRVFindOrCreateDefault(myloc.Call, myloc.Loc, Properties.Settings.Default.Band); + // set qrv defaults if zero + if (myqrv.AntennaHeight == 0) + myqrv.AntennaHeight = StationData.Database.QRVGetDefaultAntennaHeight(Properties.Settings.Default.Band); + if (myqrv.AntennaGain == 0) + myqrv.AntennaGain = StationData.Database.QRVGetDefaultAntennaGain(Properties.Settings.Default.Band); + if (myqrv.Power == 0) + myqrv.Power = StationData.Database.QRVGetDefaultPower(Properties.Settings.Default.Band); + // check if there are a valid DX settings + if (!Callsign.Check(Properties.Settings.Default.DXCall) || + !GeographicalPoint.Check(Properties.Settings.Default.DXLat, Properties.Settings.Default.DXLon)) + return; + + // OK valid, lets continue + // check and update station database + LocationDesignator dxloc = LocationFindOrUpdateOrCreate(Properties.Settings.Default.DXCall, Properties.Settings.Default.DXLat, Properties.Settings.Default.DXLon); + Properties.Settings.Default.DXElevation = dxloc.Elevation; + // get qrv info or create default + QRVDesignator dxqrv = StationData.Database.QRVFindOrCreateDefault(dxloc.Call, dxloc.Loc, Properties.Settings.Default.Band); + // set qrv defaults if zero + if (dxqrv.AntennaHeight == 0) + dxqrv.AntennaHeight = StationData.Database.QRVGetDefaultAntennaHeight(Properties.Settings.Default.Band); + if (dxqrv.AntennaGain == 0) + dxqrv.AntennaGain = StationData.Database.QRVGetDefaultAntennaGain(Properties.Settings.Default.Band); + if (dxqrv.Power == 0) + dxqrv.Power = StationData.Database.QRVGetDefaultPower(Properties.Settings.Default.Band); + + // find local obstruction, if any + LocalObstructionDesignator o = ElevationData.Database.LocalObstructionFind(myloc.Lat, myloc.Lon, Properties.Settings.Default.ElevationModel); + double mybearing = LatLon.Bearing(myloc.Lat, myloc.Lon, dxloc.Lat, dxloc.Lon); + double myobstr = (o != null) ? o.GetObstruction(myqrv.AntennaHeight, mybearing) : double.MinValue; + + // try to find elevation path in database or create new one and store + ElevationPathDesignator epath = ElevationData.Database.ElevationPathFindOrCreateFromLatLon( + null, + myloc.Lat, + myloc.Lon, + dxloc.Lat, + dxloc.Lon, + ElevationData.Database.GetDefaultStepWidth(Properties.Settings.Default.ElevationModel), + Properties.Settings.Default.ElevationModel); + // add additional info to ppath + epath.Location1 = myloc; + epath.Location2 = dxloc; + epath.QRV1 = myqrv; + epath.QRV2 = dxqrv; + + // try to find propagation path in database or create new one and store + PropagationPathDesignator ppath = PropagationData.Database.PropagationPathFindOrCreateFromLatLon( + null, + myloc.Lat, + myloc.Lon, + GetElevation(myloc.Lat, myloc.Lon) + myqrv.AntennaHeight, + dxloc.Lat, + dxloc.Lon, + GetElevation(dxloc.Lat, dxloc.Lon) + dxqrv.AntennaHeight, + Bands.ToGHz(Properties.Settings.Default.Band), + LatLon.Earth.Radius * Properties.Settings.Default.Path_Band_Settings[Properties.Settings.Default.Band].K_Factor, + Properties.Settings.Default.Path_Band_Settings[Properties.Settings.Default.Band].F1_Clearance, + ElevationData.Database.GetDefaultStepWidth(Properties.Settings.Default.ElevationModel), + Properties.Settings.Default.ElevationModel, + myobstr); + + // add additional info to ppath + ppath.Location1 = myloc; + ppath.Location2 = dxloc; + ppath.QRV1 = myqrv; + ppath.QRV2 = dxqrv; + using (StreamWriter sw = new StreamWriter(Dlg.FileName)) + { + sw.WriteLine("Distance[km];Lat[deg];Lon[deg];Elevation[m]; Min_h1[m]; Min_h2[m]; Min_h[m]; Max_h[m]; F1[m];eps1[deg];eps2[deg];eps1_min[deg];eps2_min[deg];ElevationModel"); + for (int i = 0; i < epath.Count; i++) + { + double dist = (double)i * epath.StepWidth / 1000.0; + PropagationPoint p = ppath.GetInfoPoint(dist); + sw.WriteLine(dist.ToString() + ";" + + p.Lat.ToString() + ";" + + p.Lon.ToString() + ";" + + epath.Path[i].ToString() + ";" + + p.H1.ToString() + ";" + + p.H2.ToString() + ";" + + Math.Max(p.H1, p.H2).ToString() + ";" + + Properties.Settings.Default.Planes_MaxAlt.ToString() + ";" + + p.F1.ToString() + ";" + + (Propagation.EpsilonFromHeights(ppath.h1, dist, epath.Path[i],ppath.Radius) / Math.PI * 180.0).ToString() + ";" + + (Propagation.EpsilonFromHeights(ppath.h2, ppath.Distance-dist, epath.Path[i], ppath.Radius) / Math.PI * 180.0).ToString() + ";" + + (ppath.Eps1_Min / Math.PI * 180.0).ToString() + ";" + + (ppath.Eps2_Min / Math.PI * 180.0).ToString()); + } + } + } + catch (Exception ex) + { + Log.WriteMessage("Error while saving path to file [" + Dlg.FileName + "]:" + ex.ToString()); + } + } + } + + private void btn_Analysis_CrossingHistory_Click(object sender, EventArgs e) + { + if (dtp_Analysis_MaxValue.Value <= dtp_Analysis_MinValue.Value) + return; + if (Time_Offline_Interval <= 0) + Time_Offline_Interval = 1; + if ((AllPositions == null) || (AllPositions.Count == 0)) + return; + CrossingHistoryDlg Dlg = new CrossingHistoryDlg(dtp_Analysis_MinValue.Value, dtp_Analysis_MaxValue.Value, Time_Offline_Interval, ref AllPositions); + Dlg.ShowDialog(); + } + + private void sb_Analysis_Play_SizeChanged(object sender, EventArgs e) + { + // handle change of scrollbar + // set scrollbar markers + // reduce them to one marker per pixel + // get initial scrollbar width and build array + int[] sb = new int[sb_Analysis_Play.Width]; + double stepwidth = (double)sb.Length / (sb_Analysis_Play.Maximum - sb_Analysis_Play.Minimum); + foreach (DateTime dt in AllLastUpdated) + { + int index = (int)((dt - dtp_Analysis_MinValue.Value).TotalSeconds * stepwidth); + int i = (int)(dt - dtp_Analysis_MinValue.Value).TotalSeconds; + if ((i > sb_Analysis_Play.Minimum) && (i < sb_Analysis_Play.Maximum)) + sb[index] = i; + } + sb_Analysis_Play.BackgroundMarkers = sb.ToList(); + } + + private void gb_Analysis_Player_SizeChanged(object sender, EventArgs e) + { + // handle change of size --> arrange elements + sb_Analysis_Play.Left = dtp_Analysis_MinValue.Right + 10; + sb_Analysis_Play.Width = dtp_Analysis_MaxValue.Left - dtp_Analysis_MinValue.Width - 20; + sb_Analysis_Play.Height = dtp_Analysis_MinValue.Height; + } + + private void btn_Analysis_Rewind_Click(object sender, EventArgs e) + { + PlayMode = AIRSCOUTPLAYMODE.FASTBACK; + Time_Offline_Interval -= 10; + if (Time_Offline_Interval < 1) + Time_Offline_Interval = 1; + tb_Analysis_Stepwidth.Text = Time_Offline_Interval.ToString(); + } + + private void btn_Analysis_Back_Click(object sender, EventArgs e) + { + PlayMode = AIRSCOUTPLAYMODE.BACK; + Time_Offline_Interval -= 1; + if (Time_Offline_Interval < 1) + Time_Offline_Interval = 1; + tb_Analysis_Stepwidth.Text = Time_Offline_Interval.ToString(); + } + + private void btn_Analysis_Pause_Click(object sender, EventArgs e) + { + PlayMode = AIRSCOUTPLAYMODE.PAUSE; + Time_Offline_Interval = 0; + tb_Analysis_Stepwidth.Text = Time_Offline_Interval.ToString(); + } + + private void btn_Analysis_Forward_Click(object sender, EventArgs e) + { + PlayMode = AIRSCOUTPLAYMODE.FORWARD; + Time_Offline_Interval += 1; + if (Time_Offline_Interval > 3600) + Time_Offline_Interval = 3600; + tb_Analysis_Stepwidth.Text = Time_Offline_Interval.ToString(); + } + + private void btn_Analysis_FastForward_Click(object sender, EventArgs e) + { + PlayMode = AIRSCOUTPLAYMODE.FASTFORWARD; + Time_Offline_Interval += 10; + if (Time_Offline_Interval > 3600) + Time_Offline_Interval = 3600; + tb_Analysis_Stepwidth.Text = Time_Offline_Interval.ToString(); + } + + private void sb_Analysis_Play_Scroll(object sender, ScrollEventArgs e) + { +// PlayMode = AIRSCOUTPLAYMODE.PAUSE; +// Time_Offline_Interval = 0; + tb_Analysis_Stepwidth.Text = Time_Offline_Interval.ToString(); + Properties.Settings.Default.Time_Offline = dtp_Analysis_MinValue.Value.AddSeconds(sb_Analysis_Play.Value); + if (Properties.Settings.Default.Time_Offline < dtp_Analysis_MinValue.Value) + Properties.Settings.Default.Time_Offline = dtp_Analysis_MinValue.Value; + if (Properties.Settings.Default.Time_Offline > dtp_Analysis_MaxValue.Value) + Properties.Settings.Default.Time_Offline = dtp_Analysis_MaxValue.Value; + CurrentTime = Properties.Settings.Default.Time_Offline; + tb_Analysis_Time.Text = CurrentTime.ToString("yyyy-MM-hh HH:mm:ss"); + UpdatePlanes(); + + } + + private void dtp_Analysis_MinValue_ValueChanged(object sender, EventArgs e) + { + // check bounds + if (dtp_Analysis_MinValue.Value < History_OldestEntry) + dtp_Analysis_MinValue.Value = History_OldestEntry; + if (dtp_Analysis_MinValue.Value > History_YoungestEntry) + dtp_Analysis_MinValue.Value = History_YoungestEntry; + UpdatePlayer(); + } + + private void dtp_Analysis_MaxValue_ValueChanged(object sender, EventArgs e) + { + // check bounds + if (dtp_Analysis_MinValue.Value < History_OldestEntry) + dtp_Analysis_MinValue.Value = History_OldestEntry; + if (dtp_Analysis_MinValue.Value > History_YoungestEntry) + dtp_Analysis_MinValue.Value = History_YoungestEntry; + UpdatePlayer(); + } + + private void btn_Analysis_Plane_History_Click(object sender, EventArgs e) + { + PlaneHistoryDlg Dlg = new PlaneHistoryDlg(dtp_Analysis_MinValue.Value, dtp_Analysis_MaxValue.Value, TmpDirectory); + Dlg.ShowDialog(); + } + + #endregion + + #endregion + + #endregion + + #region Background Workers + + #region PlaneFeed + + private void bw_PlaneFeed_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + if (e.ProgressPercentage == (int)PROGRESS.STATUS) + { + if (e.UserState == null) + return; + Say((string)e.UserState); + } + else if (e.ProgressPercentage == (int)PROGRESS.PLANES) + { + List planes = (List)e.UserState; + Planes.BulkInsertOrUpdateIfNewer(planes); + } + } + + #endregion + + #region WinTestReceive + + // suppress console output when in debug mode, needs "Just my code" set in Tools\Options\Debugging + [System.Diagnostics.DebuggerNonUserCode] + private void bw_WinTestReceive_DoWork(object sender, DoWorkEventArgs e) + { + // Background thread for receiving Win-Test messages + // listens to the UDP broadcasts + // use the ReportProgress method to + // return status code and received message + // status values are: <0 = error orrcured (not supported yet) + // 0 = function calls OK, but no bytes received + // >0 = function calls OK, number of bytes received + Log.WriteMessage("Started."); + if (Thread.CurrentThread.Name == null) + Thread.CurrentThread.Name = "bw_WinTestReceive"; + // Get own IP addresses + string hostname; + IPAddress[] hostaddresses = new IPAddress[256]; + try + { + hostname = Dns.GetHostName(); + hostaddresses = Dns.GetHostAddresses(hostname); + } + catch (Exception ex) + { + Log.WriteMessage(ex.Message); + } + // initialize UDP socket + IPEndPoint ep = new IPEndPoint(IPAddress.Any, Properties.Settings.Default.Server_Port); + UdpClient u = new UdpClient(); + u.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1); + u.Client.ReceiveTimeout = 1000; + try + { + u.Client.Bind(ep); + } + catch (Exception ex) + { + Log.WriteMessage(ex.Message); + } + + // receive Win-Test messages in a loop + while (!bw_WinTestReceive.CancellationPending) + { + try + { + // receive bytes in blocking mode + // handle the receive timeout and cancellation + try + { + byte[] data = u.Receive(ref ep); + wtMessage Msg = new wtMessage(data); + // check if message is directed to server + if (Msg.Dst == Properties.Settings.Default.Server_Name) + DispatchWinTestMsg(Msg); + } + catch (SocketException ex) + { + // do nothing + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.Message); + } + } + Log.WriteMessage("Finished."); + } + + private void ASShowPath(wtMessage msg) + { + // a show path message received + try + { + Stopwatch st = new Stopwatch(); + st.Start(); + if (PlayMode != AIRSCOUTPLAYMODE.PAUSE) + Pause(); + // set single mode + tc_Control.SelectedTab = tp_Control_Single; + PathMode = AIRSCOUTPATHMODE.SINGLE; + string[] a = msg.Data.Split(','); + string qrgstr = a[0].Replace("\"", ""); + string mycallstr = a[1].Replace("\"", ""); + string myloc = a[2].Replace("\"", "").Substring(0, 6); + double mylat = MaidenheadLocator.LatFromLoc(myloc); + double mylon = MaidenheadLocator.LonFromLoc(myloc); + string dxcallstr = a[3].Replace("\"", ""); + string dxloc = a[4].Replace("\"", "").Substring(0, 6); + double dxlat = MaidenheadLocator.LatFromLoc(dxloc); + double dxlon = MaidenheadLocator.LonFromLoc(dxloc); + if (Callsign.Check(mycallstr) && + Callsign.Check(dxcallstr) && + MaidenheadLocator.Check(myloc) && + MaidenheadLocator.Check(dxloc)) + { + Properties.Settings.Default.MyCall = mycallstr; + Properties.Settings.Default.MyLat = mylat; + Properties.Settings.Default.MyLon = mylon; + Properties.Settings.Default.MyHeight = 10; + Properties.Settings.Default.DXCall = dxcallstr; + Properties.Settings.Default.DXLat = dxlat; + Properties.Settings.Default.DXLon = dxlon; + Properties.Settings.Default.DXHeight = 10; + Properties.Settings.Default.MyElevation = GetElevation(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon); + LocationDesignator info = StationData.Database.LocationFind(mycallstr); + if ((info != null) && (MaidenheadLocator.LocFromLatLon(info.Lat, info.Lon, false, 3) == myloc)) + { + // loc is matching with database --> use detailed info then + Properties.Settings.Default.MyLat = info.Lat; + Properties.Settings.Default.MyLon = info.Lon; + Properties.Settings.Default.MyElevation = GetElevation(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon); + } + else + { + // update database if not found or locator does not match + UpdateLocation(mycallstr, mylat, mylon, GEOSOURCE.FROMLOC); + } + info = StationData.Database.LocationFind(dxcallstr); + if ((info != null) && (MaidenheadLocator.LocFromLatLon(info.Lat, info.Lon, false, 3) == dxloc)) + { + // loc is matching with database --> use detailed info then + Properties.Settings.Default.DXLat = info.Lat; + Properties.Settings.Default.DXLon = info.Lon; + Properties.Settings.Default.DXElevation = GetElevation(Properties.Settings.Default.DXLat, Properties.Settings.Default.DXLon); + } + else + { + // update database if not found or locator does not match + UpdateLocation(dxcallstr, dxlat, dxlon, GEOSOURCE.FROMLOC); + } + if (qrgstr == "500000") + Properties.Settings.Default.Band = BAND.B50M; + if (qrgstr == "700000") + Properties.Settings.Default.Band = BAND.B70M; + if (qrgstr == "1440000") + Properties.Settings.Default.Band = BAND.B144M; + if (qrgstr == "4320000") + Properties.Settings.Default.Band = BAND.B432M; + if (qrgstr == "12960000") + Properties.Settings.Default.Band = BAND.B1_2G; + if (qrgstr == "23200000") + Properties.Settings.Default.Band = BAND.B2_3G; + if (qrgstr == "34000000") + Properties.Settings.Default.Band = BAND.B3_4G; + if (qrgstr == "57600000") + Properties.Settings.Default.Band = BAND.B5_7G; + if (qrgstr == "103680000") + Properties.Settings.Default.Band = BAND.B10G; + if (qrgstr == "240480000") + Properties.Settings.Default.Band = BAND.B24G; + if (qrgstr == "470880000") + Properties.Settings.Default.Band = BAND.B47G; + if (qrgstr == "760320000") + Properties.Settings.Default.Band = BAND.B76G; + UpdateStatus(); + Play(); + // try different methods to bring the window to front under WinXP and Win7 + bool max = this.WindowState == FormWindowState.Maximized; + // try to restore window from minimized and normal state --> normal state + this.TopMost = true; + SetForegroundWindow(this.Handle); + ShowWindow(this.Handle, ShowWindowCommands.ShowNoActivate); + this.BringToFront(); + this.TopMost = false; + // maximize it if it was maximized before + if (max) + this.WindowState = FormWindowState.Maximized; + // set antenna direction + double az = LatLon.Bearing(Properties.Settings.Default.MyLat, + Properties.Settings.Default.MyLon, + Properties.Settings.Default.DXLat, + Properties.Settings.Default.DXLon); + // set tracking values + Properties.Settings.Default.Track_SetAz = az; + Properties.Settings.Default.Track_SetEl = 0; + } + st.Stop(); + Log.WriteMessage("Processing ASSHOWPATH[" + mycallstr + "," + dxcallstr + "]: " + st.ElapsedMilliseconds.ToString() + " ms."); + } + catch (Exception ex) + { + Log.WriteMessage(ex.Message); + } + + } + + private void ASSetPath(wtMessage msg) + { + try + { + Stopwatch st = new Stopwatch(); + st.Start(); + string[] a = msg.Data.Split(','); + int qrg = 1296; + int.TryParse(a[0].Replace("\"", ""), out qrg); + qrg = qrg / 10000; + BAND band = BAND.BNONE; + try + { + band = (BAND)qrg; + } + catch + { + // do nothing + } + string mycallstr = a[1].Replace("\"", ""); + string mylocstr = a[2].Replace("\"", "").Substring(0, 6); + string dxcallstr = a[3].Replace("\"", ""); + string dxlocstr = a[4].Replace("\"", "").Substring(0, 6); + int count = 0; + // return on failure + if (!Callsign.Check(mycallstr)) + return; + if (!Callsign.Check(dxcallstr)) + return; + if (!MaidenheadLocator.Check(mylocstr)) + return; + if (!MaidenheadLocator.Check(dxlocstr)) + return; + // get my location info --> if loc is matching, use precise information automatically + LocationDesignator myloc = StationData.Database.LocationFindOrCreate(mycallstr, mylocstr); + // get my QRV info + QRVDesignator myqrv = StationData.Database.QRVFindOrCreateDefault(mycallstr, mylocstr, band); + // set qrv defaults if zero + if (myqrv.AntennaHeight == 0) + myqrv.AntennaHeight = StationData.Database.QRVGetDefaultAntennaHeight(Properties.Settings.Default.Band); + if (myqrv.AntennaGain == 0) + myqrv.AntennaGain = StationData.Database.QRVGetDefaultAntennaGain(Properties.Settings.Default.Band); + if (myqrv.Power == 0) + myqrv.Power = StationData.Database.QRVGetDefaultPower(Properties.Settings.Default.Band); + // get dx location info --> if loc is matching, use precise information automatically + LocationDesignator dxloc = LocationFindOrCreate(dxcallstr, dxlocstr); + // get dx QRV info + QRVDesignator dxqrv = StationData.Database.QRVFindOrCreateDefault(dxcallstr, dxlocstr, band); + // set qrv defaults if zero + if (dxqrv.AntennaHeight == 0) + dxqrv.AntennaHeight = StationData.Database.QRVGetDefaultAntennaHeight(Properties.Settings.Default.Band); + if (dxqrv.AntennaGain == 0) + dxqrv.AntennaGain = StationData.Database.QRVGetDefaultAntennaGain(Properties.Settings.Default.Band); + if (dxqrv.Power == 0) + dxqrv.Power = StationData.Database.QRVGetDefaultPower(Properties.Settings.Default.Band); + // find local obstruction, if any + LocalObstructionDesignator o = ElevationData.Database.LocalObstructionFind(myloc.Lat, myloc.Lon, Properties.Settings.Default.ElevationModel); + double mybearing = LatLon.Bearing(myloc.Lat, myloc.Lon, dxloc.Lat, dxloc.Lon); + double myobstr = (o != null) ? o.GetObstruction(myqrv.AntennaHeight, mybearing) : double.MinValue; + + // try to find propagation path in database or create new one and store + PropagationPathDesignator ppath = PropagationData.Database.PropagationPathFindOrCreateFromLatLon( + null, + myloc.Lat, + myloc.Lon, + GetElevation(myloc.Lat, myloc.Lon) + myqrv.AntennaHeight, + dxloc.Lat, + dxloc.Lon, + GetElevation(dxloc.Lat, dxloc.Lon) + dxqrv.AntennaHeight, + Bands.ToGHz(Properties.Settings.Default.Band), + LatLon.Earth.Radius * Properties.Settings.Default.Path_Band_Settings[Properties.Settings.Default.Band].K_Factor, + Properties.Settings.Default.Path_Band_Settings[Properties.Settings.Default.Band].F1_Clearance, + ElevationData.Database.GetDefaultStepWidth(Properties.Settings.Default.ElevationModel), + Properties.Settings.Default.ElevationModel, + myobstr); + List allplanes = Planes.GetAll(DateTime.UtcNow, Properties.Settings.Default.Planes_Position_TTL); + List nearestplanes = AircraftData.Database.GetNearestPlanes(DateTime.UtcNow, ppath, allplanes, Properties.Settings.Default.Planes_Filter_Max_Circumcircle, Properties.Settings.Default.Path_Band_Settings[band].MaxDistance, Properties.Settings.Default.Planes_MaxAlt); + count = 0; + string planes = ""; + wtMessage SendMsg = new wtMessage(WTMESSAGES.ASNEAREST, + Properties.Settings.Default.Server_Name, + msg.Src, + DateTime.UtcNow.ToString("u") + "," + + mycallstr + "," + + mylocstr + "," + + dxcallstr + "," + + dxlocstr + ","); + if ((nearestplanes != null) && (nearestplanes.Count() > 0)) + { + foreach (PlaneInfo planeinfo in nearestplanes) + { + if ((planeinfo.IntPoint != null) && (planeinfo.Potential > 0)) + { + int mins = 0; + if (planeinfo.Speed > 0) + mins = (int)(planeinfo.IntQRB / (double)planeinfo.Speed / 1.852 * 60.0); + planes = planes + planeinfo.Call + "," + planeinfo.Category + "," + ((int)planeinfo.IntQRB).ToString() + "," + planeinfo.Potential.ToString() + "," + mins.ToString() + ","; + count++; + } + } + } + planes = planes.TrimEnd(','); + SendMsg.Data = SendMsg.Data + count.ToString() + "," + planes; + SendMsg.Data = SendMsg.Data.TrimEnd(','); + SendMsg.HasChecksum = true; + // send message + UdpClient client = new UdpClient(); + client.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1); + client.Client.ReceiveTimeout = 10000; // 10s Receive timeout + IPEndPoint groupEp = new IPEndPoint(IPAddress.Broadcast, Properties.Settings.Default.Server_Port); + client.Connect(groupEp); + byte[] b = SendMsg.ToBytes(); + client.Send(b, b.Length); + client.Close(); + st.Stop(); + Log.WriteMessage("Processing ASSETPATH[" + mycallstr + "," + dxcallstr + "]: " + count.ToString() + " Plane(s), " + st.ElapsedMilliseconds.ToString() + " ms."); + } + catch (Exception ex) + { + Log.WriteMessage(ex.Message); + } + } + + private void ASSWatchlist (wtMessage msg) + { + if (!Properties.Settings.Default.Watchlist_SyncWithKST) + return; + // maintain watchlist + try + { + Stopwatch st = new Stopwatch(); + st.Start(); + // mark all watchlist items to remove + foreach (WatchlistItem item in Properties.Settings.Default.Watchlist) + { + // nasty!! Should never be null! + if (item == null) + continue; + item.Remove = true; + } + // split message + string[] a = msg.Data.Split(','); + // ignore band string so far + string qrgstr = a[0]; + // iterate through calls + for (int i = 1; i < a.Length - 1; i += 2) + { + // get call + string dxcallstr = a[i].Trim().ToUpper(); + // get loc + string dxlocstr = a[i + 1].Trim().ToUpper(); + // skip when invalid + if (!Callsign.Check(dxcallstr)) + continue; + if (!MaidenheadLocator.Check(dxlocstr)) + continue; + // skip own callsign + if (dxcallstr == Callsign.Cut(Properties.Settings.Default.MyCall)) + continue; + // find or create call & loc combination in station database + LocationDesignator dxcall = StationData.Database.LocationFindOrCreate(dxcallstr, dxlocstr); + double qrb = LatLon.Distance(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, dxcall.Lat, dxcall.Lon); + // add to watchlist + int index = Properties.Settings.Default.Watchlist.IndexOf(dxcallstr, dxlocstr); + // reset remove flag if item found, add to watchlist if not + if (index >= 0) + { + Properties.Settings.Default.Watchlist[index].Remove = false; + Properties.Settings.Default.Watchlist[index].OutOfRange = qrb > Properties.Settings.Default.Path_MaxLength; + } + else + { + Properties.Settings.Default.Watchlist.Add(new WatchlistItem(dxcallstr, dxlocstr, qrb > Properties.Settings.Default.Path_MaxLength)); + } + } + + // remove all items from watchlist which are not logged in anymore + Properties.Settings.Default.Watchlist.RemoveAll(item => item.Remove); + + // update watchlist in map + UpdateWatchlistInMap(); + + // update ListView control + RefreshWatchlistView(); + st.Stop(); + Log.WriteMessage("Processing ASWATCHLIST: " + Properties.Settings.Default.Watchlist.Count.ToString() + " call(s), " + st.ElapsedMilliseconds.ToString() + " ms."); + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + + private void DispatchWinTestMsg(wtMessage msg) + { + // a Win-Test message was received by the background thread + if (msg.Msg == WTMESSAGES.ASSHOWPATH) + { + // a show path message received + // dispatch it to main thread + bw_WinTestReceive.ReportProgress(1, msg); + } + else if (msg.Msg == WTMESSAGES.ASSETPATH) + { + // a path calculation message received + // keep it in the background thread and calculate + ASSetPath(msg); + } + else if (msg.Msg == WTMESSAGES.ASWATCHLIST) + { + // set users list on watchlist + // dispatch it to main thread + bw_WinTestReceive.ReportProgress(1, msg); + } + } + + private void bw_WinTestReceive_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + // a Win-Test message was received by the background thread + NumberFormatInfo provider = new NumberFormatInfo(); + provider.NumberDecimalSeparator = "."; + provider.NumberGroupSeparator = ","; + provider.NumberGroupSizes = new int[] { 3 }; + + wtMessage msg = (wtMessage)e.UserState; + if (msg.Msg == WTMESSAGES.ASSHOWPATH) + ASShowPath(msg); + else if (msg.Msg == WTMESSAGES.ASWATCHLIST) + ASSWatchlist(msg); + } + + private void bw_WinTestReceive_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + } + + #endregion + + # region SpecLabReceive + + // suppress console output when in debug mode, needs "Just my code" set in Tools\Options\Debugging + // [System.Diagnostics.DebuggerNonUserCode] + private void bw_SpecLab_Receive_DoWork(object sender, DoWorkEventArgs e) + { + // name the thread for debugging + if (String.IsNullOrEmpty(Thread.CurrentThread.Name)) + Thread.CurrentThread.Name = "bw_SpecLabReceive"; + Log.WriteMessage("Started."); + // get the update interval + int interval = System.Convert.ToInt32(Properties.Settings.Default.SpecLab_Update) * 1000; + // check for minimum + if (interval < 1000) + interval = 1000; + bw_SpecLab_Receive.ReportProgress(0); + int maxcount = 60; + List ads = new List(); + while (!bw_SpecLab_Receive.CancellationPending) + { + Thread.Sleep(interval); + // do nothing when not in play mode + if ((LifeMode != AIRSCOUTLIFEMODE.LIFE) || (PlayMode != AIRSCOUTPLAYMODE.FORWARD)) + continue; + try + { + // get boundary frequencies + int f1 = Properties.Settings.Default.SpecLab_F1; + if (f1 < 0) + f1 = 0; + if (f1 > 3000) + f1 = 3000; + int f2 = Properties.Settings.Default.SpecLab_F2; + if (f2 < 1) + f2 = 1; + if (f2 > 3000) + f2 = 3000; + if (f1 >= f2) + f1 = f2 - 1; + // get the url + string url = Properties.Settings.Default.SpecLab_URL; + // get the filename + string filename = Properties.Settings.Default.SpecLab_FileName; + if (!url.EndsWith("/")) + url = url + "/"; + url = url + filename + "?f1=" + f1.ToString() + "?f2=" + f2.ToString(); + string msg = ""; + bw_SpecLab_Receive.ReportProgress(1, "Trying to connect to Spectrum Lab..."); + WebRequest myWebRequest = WebRequest.Create(url); + WebResponse myWebResponse = myWebRequest.GetResponse(); + Stream ReceiveStream = myWebResponse.GetResponseStream(); + Encoding encode = System.Text.Encoding.GetEncoding("utf-8"); + StreamReader readStream = new StreamReader(ReceiveStream, encode); + string json = readStream.ReadToEnd(); + if (!json.StartsWith("[")) + throw (new FormatException("Format Exception: Not a JSON file.")); + // split the JSON string + // a[1] contains the header + // h contains alle header items + // d contains all data items + string[] a = json.Split('['); + a[1] = a[1].Replace(":", ","); + string[] h = a[1].Split(','); + a[2] = a[2].Replace("\r\n", ""); + a[2] = a[2].Remove(a[2].IndexOf("]") - 1); + a[2] = a[2].Replace(",", ";"); + a[2] = a[2].Replace(".", ","); + double[] d = System.Array.ConvertAll(a[2].Split(';'), new Converter(Double.Parse)); + // get the time stamp + long l = (long)System.Convert.ToDouble(h[1], CultureInfo.InvariantCulture); + DateTime utc = new System.DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + utc = utc.AddSeconds(l); + // get number of bins + int count = System.Convert.ToInt32(h[13]); + msg = "FFT-Data received: " + count.ToString() + "bins. F1=" + f1.ToString() + "Hz, F2=" + f2.ToString() + "Hz"; + bw_SpecLab_Receive.ReportProgress(1, msg); + double max = d.Max(); + // collect and save maximum if in play mode and NearestPlane != null + if ((PlayMode == AIRSCOUTPLAYMODE.FORWARD) && (NearestPlane != null)) + ads.Add(new SignalLevelDesignator(max, utc)); + // bulk save maximum to database if maxcount is reached + if (ads.Count > maxcount) + { + SignalData.Database.SignalLevelBulkInsertOrUpdateIfNewer(ads); + ads.Clear(); + } + // report maximum + bw_SpecLab_Receive.ReportProgress(100, max); + } + catch (WebException ex) + { + // do nothing + } + catch (SocketException ex) + { + // do nothing + } + catch (Exception ex) + { + Log.WriteMessage(ex.Message); + bw_SpecLab_Receive.ReportProgress(-1, ex.Message); + } + } + Log.WriteMessage("Finished."); + } + + private void bw_SpecLab_Receive_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + try + { + if (e.ProgressPercentage < 0) + { + // an error occured + tb_Spectrum_Status.ForeColor = Color.White; + tb_Spectrum_Status.BackColor = Color.Red; + tb_Spectrum_Status.Text = (string)e.UserState; + } + if (e.ProgressPercentage == 0) + { + // a init message occured + Spectrum.Points.Clear(); + SpectrumPointsCount = 0; + Spectrum_X.Reset(); + SpectrumRecord.Points.Clear(); + } + if (e.ProgressPercentage == 1) + { + // a status message occured + tb_Spectrum_Status.ForeColor = Color.Black; + tb_Spectrum_Status.BackColor = SystemColors.Control; + tb_Spectrum_Status.Text = (string)e.UserState; + } + if (e.ProgressPercentage == 100) + { + // FFT - data received + if ((LifeMode == AIRSCOUTLIFEMODE.LIFE) && (PlayMode == AIRSCOUTPLAYMODE.FORWARD)) + { + // draw data + double max = (double)e.UserState; + if (Spectrum.Points.Count >= SpectrumMaxPoints) + { + double pan = -(Spectrum_X.ScreenMax.X - Spectrum_X.ScreenMin.X) / (double)SpectrumMaxPoints; + Spectrum_X.Pan(pan); + Spectrum.Points.RemoveAt(0); + SpectrumRecord.Points.RemoveAt(0); + } + // add background area + SpectrumRecord.Fill = OxyColor.FromArgb(20, 255, 0, 255); + double on, off; + if (Spectrum_Y.Minimum < 0) + { + on = -1000; + off = 1000; + } + else + { + on = 1000; + off = -1000; + } + if (NearestPlane == null) + SpectrumRecord.Points.Add(new DataPoint(SpectrumPointsCount, off)); + else + SpectrumRecord.Points.Add(new DataPoint(SpectrumPointsCount, on)); + // add signal level point + Spectrum.Points.Add(new DataPoint(SpectrumPointsCount, max)); + SpectrumPointsCount++; + // autoscale Y axis + double y_min = double.MaxValue; + double y_max = double.MinValue; + foreach (DataPoint p in Spectrum.Points) + { + if (p.Y > y_max) + y_max = p.Y; + if (p.Y < y_min) + y_min = p.Y; + } + // enlarge scaling Y-axis by 10% in both directions + double y_diff = (y_max - y_min) * 0.1; + Spectrum_Y.Minimum = y_min - y_diff; + Spectrum_Y.Maximum = y_max + y_diff; + pm_Spectrum.InvalidatePlot(true); + } + } + // maintain nearest plane map + if (NearestPlane != null) + { + lbl_Nearest_Call.Text = NearestPlane.Call; + lbl_Nearest_Type.Text = NearestPlane.Type; + lbl_Nearest_Cat.Text = PlaneCategories.GetStringValue(NearestPlane.Category); + lbl_Nearest_Alt.Text = NearestPlane.Alt_m.ToString("F0") + "m"; + lbl_Nearest_Angle.Text = (NearestPlane.Angle / Math.PI * 180.0).ToString("F0") + "°"; + lbl_Nearest_Dist.Text = NearestPlane.IntQRB.ToString("F0") + "km"; + gmo_NearestPlanes.Clear(); + gmo_NearestPlanes.Markers.Add(CreatePlaneDetailed(NearestPlane, false)); + gm_Nearest.Position = new PointLatLng(NearestPlane.IntPoint.Lat, NearestPlane.IntPoint.Lon); + gm_Nearest.Zoom = 10; + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.Message); + } + } + + private void bw_SpecLab_Receive_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + + } + + # endregion + + #region Track + + private bool Track_DDE_HRD(DdeClient client, double az, double el) + { + // send Az/EL vie DDE to Ham Radio Deluxe Rotor Control (HRDRotator.exe) + // no position feedback expected + if (client == null) + throw new NullReferenceException("[DDE]: Client not initialized."); + if (!client.IsConnected) + throw new InvalidOperationException("[DDE]: Client not connected."); + + byte[] data; + if ((az >= 0) && (az <= 360)) + { + // send azimuth data + data = Encoding.ASCII.GetBytes("SET-AZ:" + az.ToString("F1")); + client.TryPoke("PositionData", data, 1, 10000); + } + if ((el >= 0) && (el <= 90)) + { + // send elevation data + data = Encoding.ASCII.GetBytes("SET-EL:" + el.ToString("F1")); + client.TryPoke("PositionData", data, 1, 10000); + } + return true; + } + + private bool Track_UDP_WinTest(UdpClient client, IPEndPoint ip, double az, double el) + { + // send UDP broadcast like Win-Test + // no position feedback expected + if ((client == null) || (ip == null)) + if (client == null) + throw new NullReferenceException("[UDP]: Client and/or IP endpoint not initialized."); + wtMessage msg = new wtMessage(WTMESSAGES.SETAZIMUTH, Properties.Settings.Default.Server_Name, "", "AUTO", " 00 " + az.ToString("000")); + byte[] bytes = msg.ToBytes(); + client.Send(bytes, bytes.Length, ip); + return true; + } + + private bool Track_UDP_AirScout(UdpClient client, IPEndPoint ip, double az, double el) + { + // send UDP broadcast like Win-Test + // no position feedback expected + if ((client == null) || (ip == null)) + throw new NullReferenceException("[UDP]: Client and/or IP endpoint not initialized."); + wtMessage msg; + msg = new wtMessage(WTMESSAGES.SETAZIMUTH, Properties.Settings.Default.Server_Name, "", "AUTO", " 00 " + az.ToString("000")); + msg = new wtMessage(WTMESSAGES.SETELEVATION, Properties.Settings.Default.Server_Name, "", "AUTO", " 00 " + el.ToString("000")); + byte[] bytes = msg.ToBytes(); + client.Send(bytes, bytes.Length, ip); + return true; + } + + private string Serial_SendCommand(SerialPort sp, string command, bool waitanswer) + { + // sends a command via serial port (and optional wait fo answer) + if ((sp == null) || (!sp.IsOpen)) + return ""; + string s = ""; + { + sp.WriteLine(command); + if (waitanswer) + { + s = sp.ReadLine(); + s = s.Replace("\n", ""); + } + } + return s; + } + + + private bool Track_SER__GS_232A_AZ(SerialPort sp, double az) + { + // send Az value via serial port (GS-232A protocol) + // communictaion test --> get azimuth value + if (sp == null) + throw new NullReferenceException("[Serial]: Port not initialized."); + if (!sp.IsOpen) + throw new InvalidOperationException("[Serial]: Port not open."); + // communictaion test --> get azimuth value + string s = Serial_SendCommand(sp, "C", true); + if (!s.StartsWith("+0")) + throw new FormatException("[Serial]: Wrong serial data format."); + try + { + double result = System.Convert.ToDouble(s.Substring(2, 3), CultureInfo.InvariantCulture); + } + catch + { + throw new FormatException("[Serial]: Wrong serial data format."); + } + // set azimuth value --> no feedback + Serial_SendCommand(sp, "M" + az.ToString("000"), false); + return true; + } + + private bool Track_SER__GS_232A_AZEL(SerialPort sp, double az, double el) + { + // send Az/El value via serial port (GS-232A protocol) + if (sp == null) + throw new NullReferenceException("[Serial]: Port not initialized."); + if (!sp.IsOpen) + throw new InvalidOperationException("[Serial]: Port not open."); + // communictaion test --> get azimuth and elevation value + string s = Serial_SendCommand(sp, "C2", true); + if (!s.StartsWith("+0")) + throw new FormatException("[Serial]: Wrong serial data format."); + try + { + double result = System.Convert.ToDouble(s.Substring(2, 3), CultureInfo.InvariantCulture); + } + catch + { + throw new FormatException("[Serial]: Wrong serial data format."); + } + // set azimuth value --> no feedback + Serial_SendCommand(sp, "W" + az.ToString("000") + " " + el.ToString("000"), false); + return true; + } + + private void Track_File_Native(double az, double el) + { + // writes a file with Az/El values in a file (native) + // Syntax: , + using (StreamWriter sw = new StreamWriter(TmpDirectory + Path.DirectorySeparatorChar + "azel.dat")) + { + sw.WriteLine(az.ToString("F1", CultureInfo.InvariantCulture) + "," + el.ToString("F1", CultureInfo.InvariantCulture)); + } + } + + private void Track_File_WSJT(double az, double el) + { + // writes a file with Az/El values in a file (WSJT) + // the info is filled in the "Source" line (originally intended to follow a radio source + using (StreamWriter sw = new StreamWriter(TmpDirectory + Path.DirectorySeparatorChar + "azel.dat")) + { + string utc = DateTime.UtcNow.ToString("HH:mm:ss"); + sw.WriteLine(utc + ",0,0,Moon"); + sw.WriteLine(utc + ",0,0,Sun"); + sw.WriteLine(utc + "," + az.ToString("F1", CultureInfo.InvariantCulture) + "," + el.ToString("F1", CultureInfo.InvariantCulture) + ",Source"); + sw.WriteLine("0, 0, 0, 0, 0,Doppler, R"); + } + } + + private void bw_Track_DoWork(object sender, DoWorkEventArgs e) + { + Log.WriteMessage("Started."); + // name the thread for debugging + if (String.IsNullOrEmpty(Thread.CurrentThread.Name)) + Thread.CurrentThread.Name = "bw_Track"; + // clients and ports + DdeClient ddeclient = null; + UdpClient udpclient = null; + IPEndPoint udpip = null; + SerialPort serialport = null; + // error counters + int ddeerr = 0; + int udperr = 0; + int serialerr = 0; + int maxerr = 10; + // outer loop + do + { + // intializations + if (Properties.Settings.Default.Track_DDE_HRD) + { + ddeclient = new DdeClient("HRDRotator", "Position"); + int result = ddeclient.TryConnect(); + + } + if (Properties.Settings.Default.Track_UDP_WinTest) + { + udpclient = new UdpClient(); + udpip = new IPEndPoint(IPAddress.Broadcast, Properties.Settings.Default.Track_UDP_WinTest_Port); + } + else if (Properties.Settings.Default.Track_UDP_AirScout) + { + udpclient = new UdpClient(); + udpip = new IPEndPoint(IPAddress.Broadcast, Properties.Settings.Default.Track_UDP_AirScout_Port); + } + if ((Properties.Settings.Default.Track_Serial_GS232_AZ) || (Properties.Settings.Default.Track_Serial_GS232_AZEL)) + { + serialport = new SerialPort(Properties.Settings.Default.Track_Serial_Port, + Properties.Settings.Default.Track_Serial_Baudrate, + Parity.None, + 8, + StopBits.One); + serialport.Handshake = System.IO.Ports.Handshake.None; + serialport.NewLine = "\r"; + serialport.Encoding = Encoding.ASCII; + serialport.ReadTimeout = 1000; + serialport.WriteTimeout = 1000; + serialport.Open(); + } + // inner loop + while (Properties.Settings.Default.Track_Activate && !bw_Track.CancellationPending) + { + try + { + // tracking + double az = Properties.Settings.Default.Track_SetAz; + double el = Properties.Settings.Default.Track_SetEl; + if (!double.IsNaN(az) && !double.IsNaN(el)) + { + if (Properties.Settings.Default.Track_DDE_HRD) + Track_DDE_HRD(ddeclient, az, el); + if (Properties.Settings.Default.Track_UDP_WinTest) + Track_UDP_WinTest(udpclient, udpip, az, el); + if (Properties.Settings.Default.Track_UDP_AirScout) + Track_UDP_WinTest(udpclient, udpip, az, el); + if (Properties.Settings.Default.Track_Serial_GS232_AZ) + Track_SER__GS_232A_AZ(serialport, az); + if (Properties.Settings.Default.Track_Serial_GS232_AZEL) + Track_SER__GS_232A_AZEL(serialport, az, el); + if (Properties.Settings.Default.Track_File_Native) + Track_File_Native(az, el); + if (Properties.Settings.Default.Track_File_WSJT) + Track_File_WSJT(az, el); + } + else + { + // no tracking! + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.Message); + //report error + bw_Track.ReportProgress(-1, ex.Message); + // increment error counters and switch off in case of subsequent errors + if (ex.Message.StartsWith("[DDE]:")) + { + ddeerr++; + if (ddeerr > maxerr) + { + // switch off DDE + Properties.Settings.Default.Track_DDE_None = true; + Properties.Settings.Default.Track_DDE_HRD = false; + bw_Track.ReportProgress(-1, "Tracking via DDE disabled."); + } + } + if (ex.Message.StartsWith("[UDP]:")) + { + udperr++; + if (udperr > maxerr) + { + // switch off UDP + Properties.Settings.Default.Track_UDP_None = true; + Properties.Settings.Default.Track_UDP_WinTest = false; + Properties.Settings.Default.Track_UDP_AirScout = false; + bw_Track.ReportProgress(-1, "Tracking via UDP disabled."); + } + } + if (ex.Message.StartsWith("[Serial]:")) + { + serialerr++; + if (serialerr > maxerr) + { + // switch off Serial + Properties.Settings.Default.Track_Serial_None = true; + Properties.Settings.Default.Track_Serial_GS232_AZ = false; + Properties.Settings.Default.Track_Serial_GS232_AZEL = false; + bw_Track.ReportProgress(-1, "Tracking via Serial disabled."); + } + if (ex.Message.StartsWith("[Serial]:")) + serialerr++; + } + // leave inner loop + break; + } + Thread.Sleep(1000); + } + Thread.Sleep(1000); + // try to close all connections + try + { + if ((ddeclient != null) && (ddeclient.IsConnected)) + ddeclient.Disconnect(); + if (udpclient != null) + udpclient.Close(); + if ((serialport != null) && (serialport.IsOpen)) + serialport.Close(); + } + catch (Exception ex) + { + Log.WriteMessage(ex.Message); + } + } + while (!bw_Track.CancellationPending); + Log.WriteMessage("Finished."); + } + + private void bw_Track_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + if (e.ProgressPercentage < 0) + { + // report Error + tsl_Status.Text = (string)e.UserState; + } + if (e.ProgressPercentage == 1) + { + // report Azimuth + ag_Azimuth.Value = (float)(double)e.UserState; + } + if (e.ProgressPercentage == 2) + { + // report Elevation + ag_Elevation.Value = (float)(double)e.UserState; + } + } + + private void bw_Track_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + + } + + #endregion + + #region JSONWriter + + private void bw_JSONWriter_DoWork(object sender, DoWorkEventArgs e) + { + Log.WriteMessage("Started."); + int interval = 60; + // name the thread for debugging + if (String.IsNullOrEmpty(Thread.CurrentThread.Name)) + Thread.CurrentThread.Name = "bw_JSONWriter"; + while (!bw_JSONWriter.CancellationPending) + { + // get planes each minute + List list = Planes.GetAll(DateTime.UtcNow, Properties.Settings.Default.Planes_Position_TTL); + // write json file + try + { + using (StreamWriter sw = new StreamWriter(TmpDirectory + Path.DirectorySeparatorChar + "planes.json")) + { + int major = Assembly.GetExecutingAssembly().GetName().Version.Major; + sw.Write("{\"full_count\":" + list.Count().ToString() + ",\"version\":" + major.ToString()); + int i = 0; + foreach (PlaneInfo info in list) + { + string index = "\"" + i.ToString("x8") + "\""; + string hex = "\"" + info.Hex + "\""; + string lat = info.Lat.ToString("F4", CultureInfo.InvariantCulture); + string lon = info.Lon.ToString("F4", CultureInfo.InvariantCulture); + string track = info.Track.ToString(); + string alt = info.Alt.ToString(); + string speed = info.Speed.ToString(); + string squawk = "\"" + "" + "\""; + string radar = "\"" + "" + "\""; + string type = "\"" + info.Type + "\""; + string reg = "\"" + info.Reg + "\""; + DateTime sTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + string time = ((long)(info.Time - sTime).TotalSeconds).ToString(); + string dep = "\"\""; + string dest = "\"\""; + string flight = "\"\""; + string dummy1 = "0"; + string dummy2 = "0"; + string call = "\"" + info.Call + "\""; + string dummy3 = "0"; + sw.WriteLine("," + index + ":[" + + hex + "," + + lat + "," + + lon + "," + + track + "," + + alt + "," + + speed + "," + + squawk + "," + + radar + "," + + type + "," + + reg + "," + + time + "," + + dep + "," + + dest + "," + + flight + "," + + dummy1 + "," + + dummy2 + "," + + call + "," + + dummy3 + + "]"); + } + sw.WriteLine("}"); + i++; + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.Message); + // do nothing + } + int ii = 0; + while (!bw_JSONWriter.CancellationPending && (ii < interval)) + { + Thread.Sleep(1000); + ii++; + } + } + Log.WriteMessage("Finished."); + } + + + #endregion + + #region NewsFeed + + private void bw_NewsFeed_DoWork(object sender, DoWorkEventArgs e) + { + Log.WriteMessage("Started."); + // name the thread for debugging + if (String.IsNullOrEmpty(Thread.CurrentThread.Name)) + Thread.CurrentThread.Name = "bw_NewsFeed"; + Uri uri = Properties.Settings.Default.News_URL; + int interval = Properties.Settings.Default.News_Interval; + while (!bw_NewsFeed.CancellationPending) + { + try + { + // get the last modified time of the website + AutoDecompressionWebClient cl = new AutoDecompressionWebClient(); + DateTime dt = cl.GetWebCreationTimeUtc(Properties.Settings.Default.News_URL); + Log.WriteMessage("Checking news page: " + dt.ToString("yyyy-MM-dd HH:mm:ss") + "<> " + Properties.Settings.Default.News_LastUpdate.ToString("yyyy-MM-dd HH:mm:ss")); + Console.WriteLine("Checking news page: " + dt.ToString("yyyy-MM-dd HH:mm:ss") + "<> " + Properties.Settings.Default.News_LastUpdate.ToString("yyyy-MM-dd HH:mm:ss")); + // report latest news if updated + if (dt > Properties.Settings.Default.News_LastUpdate) + { + // report news to main window + bw_NewsFeed.ReportProgress(1, dt); + } + } + catch (Exception ex) + { + // report error + Log.WriteMessage(ex.Message); + bw_NewsFeed.ReportProgress(-1, DateTime.UtcNow.ToString("[" + "HH:mm:ss") + "] Error while reading the website " + uri.ToString() + ": " + ex.Message); + } + int i = 0; + while (!bw_NewsFeed.CancellationPending && (i < interval)) + { + Thread.Sleep(1000); + i++; + } + } + Log.WriteMessage("Finished."); + } + + private void bw_NewsFeed_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + if (e.ProgressPercentage < 0) + { + // report error + Say((string)e.UserState); + } + else + { + // report website changes + DateTime dt = (DateTime)e.UserState; + if (!SupportFunctions.IsMono) + { + if (MessageBox.Show("There are news on the website. Latest update: " + dt.ToString() + "\n Do you want to read it now?", "Website News", MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.Yes) + { + try + { + if (wb_News != null) + wb_News.Refresh(); + tc_Main.SelectedTab = tp_News; + // save time to settings + Properties.Settings.Default.News_LastUpdate = dt; + } + catch (Exception ex) + { + // do nothing if wb_News fails to refresh + } + } + } + else + { + if (MessageBox.Show("There are news on the website. Latest update: " + dt.ToString() + "\n Do you want to read it now?\n\n Under Linux/Mono open web browser of your choice and goto: \n" + Properties.Settings.Default.News_URL + "\n\n", "Website News", MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.Yes) + { + try + { + // save time to settings + Properties.Settings.Default.News_LastUpdate = dt; + } + catch (Exception ex) + { + // do nothing if wb_News fails to refresh + } + } + + } + } + } + + private void bw_NewsFeed_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + + } + + #endregion + + #region HistoryDownloader + + private void HistoryDownloader_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) + { + // get byte in GB + double bytesin = double.Parse(e.BytesReceived.ToString()) / 1024.0 / 1024.0 / 1024.0; + double totalbytes = double.Parse(e.TotalBytesToReceive.ToString()) / 1024.0 / 1024.0 / 1024.0; + double percentage = bytesin / totalbytes * 100; + try + { + if (bw_HistoryDownloader.IsBusy) + bw_HistoryDownloader.ReportProgress(1, "Downloaded " + bytesin.ToString("F2") + " GB of " + totalbytes.ToString("F2") + " GB (" + percentage.ToString("F2") + "%)."); + } + catch + { + + } + } + + private string ReadPropertyString(JObject o, string propertyname) + { + if (o.Property(propertyname) == null) + return null; + return o.Property(propertyname).Value.Value(); + } + + private int ReadPropertyDoubleToInt(JObject o, string propertyname) + { + if (o.Property(propertyname) == null) + return int.MinValue; + double d = ReadPropertyDouble(o, propertyname); + if ((d != double.MinValue) && (d >= int.MinValue) && (d <= int.MaxValue)) + return (int)d; + return int.MinValue; + } + + private double ReadPropertyDouble(JObject o, string propertyname) + { + if (o.Property(propertyname) == null) + return double.MinValue; + return o.Property(propertyname).Value.Value(); + } + + private long ReadPropertyLong(JObject o, string propertyname) + { + if (o.Property(propertyname) == null) + return long.MinValue; + return o.Property(propertyname).Value.Value(); + } + + private void HistoryDownloader_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e) + { + try + { + if (bw_HistoryDownloader.IsBusy) + bw_HistoryDownloader.ReportProgress(1, "Reading " + Properties.Settings.Default.Analysis_History_ZIPFileName); + // unzip the file + if (File.Exists(Properties.Settings.Default.Analysis_History_ZIPFileName)) + { + string filename = Properties.Settings.Default.Analysis_History_ZIPFileName; + // unzips a zip file content to the same directory + string downloaddir = Path.GetDirectoryName(Properties.Settings.Default.Analysis_History_ZIPFileName); + // set path to calling assembly's path if not otherwise specified + if (String.IsNullOrEmpty(downloaddir)) + downloaddir = Assembly.GetCallingAssembly().Location; + // open the zip file + using (ZipFile zip = new ZipFile(filename)) + { + zip.ZipErrorAction = ZipErrorAction.Skip; + // here, we extract every entry, but we could extract conditionally + // based on entry name, size, date, checkbox status, etc. + foreach (ZipEntry ze in zip) + { + if (bw_HistoryDownloader.IsBusy) + bw_HistoryDownloader.ReportProgress(1, "Extracting " + ze.FileName); + try + { + /* + ze.Extract(downloaddir, ExtractExistingFileAction.OverwriteSilently); + string fname = Path.Combine(downloaddir, ze.FileName); + File.SetLastWriteTime(fname, ze.LastModified); + */ + } + catch (Exception ex) + { + if (bw_HistoryDownloader.IsBusy) + bw_HistoryDownloader.ReportProgress(-1, ex.Message); + } + } + } + // create csv to load into database + using (StreamWriter sw = new StreamWriter(Properties.Settings.Default.Analysis_History_ZIPFileName.ToLower().Replace(".zip", ".csv"))) + { + sw.WriteLine("time;call;reg;hex;lat;lon;track;alt;speed;squawk;radar;type"); + // read all files + for (int i = 0; i < 1440; i += (int)Properties.Settings.Default.Analysis_History_Stepwidth) + { + // calculate filename + int hours = i / 60; + int minutes = i % 60; + string fname = Path.Combine(Properties.Settings.Default.Analysis_History_Directory, Properties.Settings.Default.Analysis_History_Date.ToString("yyyy-MM-dd") + "-" + hours.ToString("00") + minutes.ToString("00") + "Z.json"); + if (bw_HistoryDownloader.IsBusy) + bw_HistoryDownloader.ReportProgress(1, "Processing " + fname); + if (File.Exists(fname)) + { + string json = ""; + using (StreamReader sr = new StreamReader(fname)) + json = sr.ReadToEnd(); + // analyze json string for planes data + JObject root = (JObject)JsonConvert.DeserializeObject(json); + foreach (JProperty proot in root.Children()) + { + // get the planes position list + if (proot.Name == "acList") + { + foreach (JArray a in proot.Children()) + { + foreach (JObject o in a.Values()) + { + PlaneInfo info = new PlaneInfo(); + try + { + info.Call = ReadPropertyString(o, "Call"); + info.Lat = ReadPropertyDouble(o, "Lat"); + info.Lon = ReadPropertyDouble(o, "Long"); + info.Track = ReadPropertyDoubleToInt(o, "Trak"); + // 2017-07-23: take "GAlt" (corrected altitude by air pressure) rather than "Alt" + info.Alt = ReadPropertyDoubleToInt(o, "GAlt"); + // info.Alt = ReadPropertyDoubleToInt(o, "Alt"); + info.Speed = ReadPropertyDoubleToInt(o, "Spd"); + info.Reg = ReadPropertyString(o, "Reg"); + try + { + string squawk = ReadPropertyString(o, "Sqk"); + } + catch + { + } + info.Hex = ReadPropertyString(o, "Icao"); + info.Type = ReadPropertyString(o, "Type"); + // complete type info + AircraftTypeDesignator td = AircraftData.Database.AircraftTypeFindByICAO(info.Type); + if (td != null) + { + info.Manufacturer = td.Manufacturer; + info.Model = td.Model; + info.Category = td.Category; + } + else + { + info.Manufacturer = "[unknown]"; + info.Model = "[unknown]"; + info.Category = PLANECATEGORY.NONE; + } + // CAUTION!! time is UNIX time in milliseconds + long l = ReadPropertyLong(o, "PosTime"); + if (l != long.MinValue) + { + DateTime timestamp = new System.DateTime(1970, 1, 1, 0, 0, 0, 0); + timestamp = timestamp.AddMilliseconds(l); + info.Time = timestamp; + } + else + { + info.Time = DateTime.MinValue; + } + if (PlaneInfo.Check(info) && + (info.Alt_m >= Properties.Settings.Default.Planes_MinAlt) && + (info.Alt_m <= Properties.Settings.Default.Planes_MaxAlt) && + (info.Lat >= Properties.Settings.Default.MinLat) && + (info.Lat <= Properties.Settings.Default.MaxLat) && + (info.Lon >= Properties.Settings.Default.MinLon) && + (info.Lon <= Properties.Settings.Default.MaxLon)) + { + sw.WriteLine(info.Time.ToString("u") + ";" + + info.Call + ";" + + info.Reg + ";" + + info.Hex + ";" + + info.Lat.ToString("F8", CultureInfo.InvariantCulture) + ";" + + info.Lon.ToString("F8", CultureInfo.InvariantCulture) + ";" + + info.Track.ToString() + ";" + + info.Alt.ToString() + ";" + + info.Speed.ToString() + ";" + + "" + ";" + + "" + ";" + + info.Type); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.Message); + } + } + } + } + } + } + } + } + } + } + catch (Exception ex) + { + if (bw_HistoryDownloader.IsBusy) + bw_HistoryDownloader.ReportProgress(-1, ex.Message); + } + // job is done + // cancel backgroundworker + bw_HistoryDownloader.CancelAsync(); + } + + private void bw_HistoryDownloader_DoWork(object sender, DoWorkEventArgs e) + { + // name the thread for debugging + if (String.IsNullOrEmpty(Thread.CurrentThread.Name)) + Thread.CurrentThread.Name = "bw_HistoryDownloader"; + try + { + DateTime date = (DateTime)e.Argument; + // check history directory first + if (!Directory.Exists(Properties.Settings.Default.Analysis_History_Directory)) + Properties.Settings.Default.Analysis_History_Directory = TmpDirectory; + // check free disk space + System.IO.DriveInfo drive = new System.IO.DriveInfo(Properties.Settings.Default.Analysis_History_Directory); + System.IO.DriveInfo a = new System.IO.DriveInfo(drive.Name); + if (a.AvailableFreeSpace < 50.0 * 1024.0 * 1024.0 * 1024.0) + throw new ArgumentException("Not enough disk space to run this operation."); + string url = Properties.Settings.Default.Analysis_History_URL; + Properties.Settings.Default.Analysis_History_ZIPFileName = Path.Combine(Properties.Settings.Default.Analysis_History_Directory, date.ToString("yyyy-MM-dd") + ".zip"); + if (!File.Exists(Properties.Settings.Default.Analysis_History_ZIPFileName)) + { + // file not found --> donwload it from url + // complete url with "/" and date + if (!url.EndsWith("/")) + url = url + "/"; + url = url + date.ToString("yyyy-MM-dd") + ".zip"; + // create web client for download + WebClient client = new WebClient(); + // register asynchronous file download event handler + client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(HistoryDownloader_DownloadProgressChanged); + client.DownloadFileCompleted += new AsyncCompletedEventHandler(HistoryDownloader_DownloadFileCompleted); + client.DownloadFileAsync(new Uri(url), Properties.Settings.Default.Analysis_History_ZIPFileName); + // remain here in a loop until job is finished + // cancellation will be initiated after download and unzip is complet + } + else + { // call download completed handler directly + HistoryDownloader_DownloadFileCompleted(this, null); + } + } + catch (Exception ex) + { + bw_HistoryDownloader.ReportProgress(-1, ex.Message); + } + } + + private void bw_HistoryDownloader_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + string s = (string)e.UserState; + if (String.IsNullOrEmpty(s)) + return; + tb_Analysis_Status.Text = s; + } + + private void bw_HistoryDownloader_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + btn_Analysis_Planes_History.Enabled = true; + } + + #endregion + + + #region ElevationDatabaseUpdater + + private void bw_ElevationDatabaseUpdater_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + try + { + if (e.ProgressPercentage < 0) + { + // error message received + string msg = (string)e.UserState; + Log.WriteMessage(msg); + } + else if (e.ProgressPercentage == 0) + { + // status message received + string msg = (string)e.UserState; + // redirect output to splash screen on first run + if (FirstRun && SplashDlg != null) + SplashDlg.Status("Preparing database for first run: " + msg + " (please wait)", Color.Yellow); + else + { + SayDatabase(msg); + Log.WriteMessage(msg); + } + } + else if (e.ProgressPercentage == 1) + { + // database status update message received + if (sender == this.bw_GLOBEUpdater) + { + Properties.Settings.Default.Elevation_GLOBE_DatabaseStatus = (DATABASESTATUS)e.UserState; + Color color = DatabaseStatus.GetDatabaseStatusColor(Properties.Settings.Default.Elevation_GLOBE_DatabaseStatus); + if (tsl_Database_LED_GLOBE.BackColor != color) + tsl_Database_LED_GLOBE.BackColor = color; + string text = "GLOBE Database Status\n\n" + DatabaseStatus.GetDatabaseStatusText(Properties.Settings.Default.Elevation_GLOBE_DatabaseStatus); + if (tsl_Database_LED_GLOBE.ToolTipText != text) + tsl_Database_LED_GLOBE.ToolTipText = text; + } + else if (sender == this.bw_SRTM3Updater) + { + Properties.Settings.Default.Elevation_SRTM3_DatabaseStatus = (DATABASESTATUS)e.UserState; + Color color = DatabaseStatus.GetDatabaseStatusColor(Properties.Settings.Default.Elevation_SRTM3_DatabaseStatus); + if (tsl_Database_LED_SRTM3.BackColor != color) + tsl_Database_LED_SRTM3.BackColor = color; + string text = "SRTM3 Database Status\n\n" + DatabaseStatus.GetDatabaseStatusText(Properties.Settings.Default.Elevation_SRTM3_DatabaseStatus); + if (tsl_Database_LED_SRTM3.ToolTipText != text) + tsl_Database_LED_SRTM3.ToolTipText = text; + } + else if (sender == this.bw_SRTM1Updater) + { + Properties.Settings.Default.Elevation_SRTM1_DatabaseStatus = (DATABASESTATUS)e.UserState; + Color color = DatabaseStatus.GetDatabaseStatusColor(Properties.Settings.Default.Elevation_SRTM1_DatabaseStatus); + if (tsl_Database_LED_SRTM1.BackColor != color) + tsl_Database_LED_SRTM1.BackColor = color; + string text = "SRTM1 Database Status\n\n" + DatabaseStatus.GetDatabaseStatusText(Properties.Settings.Default.Elevation_SRTM1_DatabaseStatus); + if (tsl_Database_LED_SRTM1.ToolTipText != text) + tsl_Database_LED_SRTM1.ToolTipText = text; + } + } + ss_Main.Update(); + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + + + #endregion + + #region ElevationPathCalculator + + private void bw_ElevationPathCalculator_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + if (e.ProgressPercentage == 0) + SayCalculations((string)e.UserState); + } + + + #endregion + + #region StationDatabaseUpdater + + private void bw_StationDatabaseUpdater_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + try + { + if (e.ProgressPercentage < 0) + { + // error message received + string msg = (string)e.UserState; + Log.WriteMessage(msg); + } + else if (e.ProgressPercentage == 0) + { + // status message received + string msg = (string)e.UserState; + Log.WriteMessage(msg); + // redirect output to splash screen on first run + if (FirstRun && SplashDlg != null) + SplashDlg.Status("Preparing database for first run: " + msg + " (please wait)", Color.Yellow); + else + { + SayDatabase(msg); + } + } + else if (e.ProgressPercentage == 1) + { + Properties.Settings.Default.StationsDatabase_Status = (DATABASESTATUS)e.UserState; + Color color = DatabaseStatus.GetDatabaseStatusColor(Properties.Settings.Default.StationsDatabase_Status); + if (tsl_Database_LED_Stations.BackColor != color) + tsl_Database_LED_Stations.BackColor = color; + string text = "Stations Database Status\n\n" + DatabaseStatus.GetDatabaseStatusText(Properties.Settings.Default.StationsDatabase_Status); + if (tsl_Database_LED_Stations.ToolTipText != text) + tsl_Database_LED_Stations.ToolTipText = text; + } + ss_Main.Update(); + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + + private void bw_StationDatabaseUpdater_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + // refresh display + if (!this.IsDisposed) + { + UpdateAirports(); + gm_Main.Refresh(); + } + } + + #endregion + + #region AircraftDatabaseUpdater + + private void bw_AircraftDatabaseUpdater_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + try + { + if (e.ProgressPercentage < 0) + { + // error message received + string msg = (string)e.UserState; + Log.WriteMessage(msg); + } + else if (e.ProgressPercentage == 0) + { + // status message received + string msg = (string)e.UserState; + Log.WriteMessage(msg); + // redirect output to splash screen on first run + if (FirstRun && SplashDlg != null) + SplashDlg.Status("Preparing database for first run: " + msg + " (please wait)", Color.Yellow); + else + { + SayDatabase(msg); + } + } + else if (e.ProgressPercentage == 1) + { + Properties.Settings.Default.AircraftDatabase_Status = (DATABASESTATUS)e.UserState; + Color color = DatabaseStatus.GetDatabaseStatusColor(Properties.Settings.Default.AircraftDatabase_Status); + if (tsl_Database_LED_Aircraft.BackColor != color) + { + tsl_Database_LED_Aircraft.BackColor = color; + } + string text = "Aircraft Database Status\n\n" + DatabaseStatus.GetDatabaseStatusText(Properties.Settings.Default.AircraftDatabase_Status); + if (tsl_Database_LED_Aircraft.ToolTipText != text) + tsl_Database_LED_Aircraft.ToolTipText = text; + } + ss_Main.Update(); + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + + private void bw_AircraftDatabaseUpdater_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + // refresh data tables and display + if (!this.IsDisposed) + { + // refresh all dictionnariees + // ScoutData.Database.UpdateFromDataTables(false); + // refresh airports and map + UpdateAirports(); + gm_Main.Refresh(); + } + } + + #endregion + + #region AircraftDatabaseMaintainer + + private void bw_AircraftDatabaseMaintainer_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + try + { + if (e.ProgressPercentage < 0) + { + // error message received + string msg = (string)e.UserState; + Log.WriteMessage(msg); + } + else if (e.ProgressPercentage == 0) + { + // status message received + string msg = (string)e.UserState; + Log.WriteMessage(msg); + // redirect output to splash screen on first run + if (FirstRun && SplashDlg != null) + SplashDlg.Status("Preparing database for first run: " + msg + " (please wait)", Color.Yellow); + else + { + SayDatabase(msg); + } + } + ss_Main.Update(); + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + + private void bw_AircraftDatabaseMaintainer_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + } + + #endregion + + #endregion + + + #region Analysis_DataGetter + + private void bw_Analysis_DataGetter_DoWork(object sender, DoWorkEventArgs e) + { + // name the thread for debugging + if (String.IsNullOrEmpty(Thread.CurrentThread.Name)) + Thread.CurrentThread.Name = "bw_Analysis_DataGetter"; + Stopwatch st = new Stopwatch(); + st.Start(); + bw_Analysis_DataGetter.ReportProgress(0, "Getting timespan of all positions in database..."); + // calculate min/max values for timespan + History_OldestEntry = AircraftPositionData.Database.AircraftPositionOldestEntry(); + if (bw_Analysis_DataGetter.CancellationPending) + { + e.Cancel = true; + return; + } + History_YoungestEntry = AircraftPositionData.Database.AircraftPositionYoungestEntry(); + if (bw_Analysis_DataGetter.CancellationPending) + { + e.Cancel = true; + return; + } + bw_Analysis_DataGetter.ReportProgress(0, "Getting positions..."); + AircraftPositionsCount = AircraftPositionData.Database.AircraftPositionCount(); + // get all aircraft positions into cache + lock (AllPositions) + { + AllPositions.Clear(); + } + lock (AllPositions) + { + // get all positions from database, can be interrupted + AllPositions = AircraftPositionData.Database.AircraftPositionGetAll(this.bw_Analysis_DataGetter); + if (bw_Analysis_DataGetter.CancellationPending) + { + e.Cancel = true; + return; + } + } + st.Stop(); + bw_Analysis_DataGetter.ReportProgress(0, "Getting positions finished, " + AllPositions.Count.ToString() + " positions, " + st.ElapsedMilliseconds.ToString() + " ms."); + } + + private void bw_Analysis_DataGetter_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + if (e.ProgressPercentage == 0) + { + string msg = (string)e.UserState; + // NASTY!! Add the total count of positions after the "of" in the message + // total count is calculated once in DoWork + if (msg.EndsWith("of)")) + msg = msg + " " + AircraftPositionsCount.ToString(); + SayAnalysis(msg); + } + } + + private void bw_Analysis_DataGetter_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + // check if cancelled + // return to default state + if (e.Cancelled) + { + SayAnalysis("Cancelled."); + btn_Analysis_ON.Enabled = true; + btn_Analysis_ON.BackColor = Color.YellowGreen; + return; + } + else + SayAnalysis("Ready."); + // nothing found in database --> show message box and do not enter analysis mode + if ((History_YoungestEntry == DateTime.MinValue) || (History_OldestEntry == DateTime.MinValue)) + { + MessageBox.Show("Nothing found for analysis. Please let AirScout run in PLAY mode for a while to collect some data.", "AirScout Analysis", MessageBoxButtons.OK); + return; + } + // set scroll bar bounds + dtp_Analysis_MinValue.Value = History_OldestEntry; + dtp_Analysis_MaxValue.Value = History_YoungestEntry; + // enable buttons + btn_Analysis_Planes_Load.Enabled = true; + btn_Analysis_Planes_Save.Enabled = true; + btn_Analysis_Planes_Clear.Enabled = true; + btn_Analysis_Planes_History.Enabled = true; + btn_Analysis_Planes_ShowTraffic.Enabled = true; + btn_Analysis_Path_SaveToFile.Enabled = true; + btn_Analysis_CrossingHistory.Enabled = true; + btn_Analysis_Plane_History.Enabled = true; + btn_Analysis_Rewind.Enabled = true; + btn_Analysis_Back.Enabled = true; + btn_Analysis_Pause.Enabled = true; + btn_Analysis_Forward.Enabled = true; + btn_Analysis_FastForward.Enabled = true; + sb_Analysis_Play.Enabled = true; + dtp_Analysis_MinValue.Enabled = true; + dtp_Analysis_MaxValue.Enabled = true; + UpdatePlayer(); + // set time to oldest entry + Properties.Settings.Default.Time_Offline = History_OldestEntry; + gb_Analysis_Player_SizeChanged(this, null); + UpdatePaths(); + UpdateStatus(); + btn_Analysis_Planes_Load.Focus(); + } + + #endregion + + #region Analysis_FileSaver + + + private void Analysis_Planes_Save_JSON(string filename) + { + int saved = 0; + using (StreamWriter sw = new StreamWriter(filename)) + { + sw.WriteLine("["); + for (int i = 0; i < AllPositions.Count; i++) + { + if (AllPositions[i].LastUpdated < dtp_Analysis_MinValue.Value) + continue; + if (AllPositions[i].LastUpdated > dtp_Analysis_MaxValue.Value) + continue; + string json = AllPositions[i].ToJSON(); + sw.Write(json); + if (i < AllPositions.Count - 1) + { + sw.WriteLine(","); + } + else + sw.WriteLine(); + saved++; + if (saved % 1000 == 0) + bw_Analysis_FileSaver.ReportProgress(0, "Saving position " + saved.ToString() + "..."); + if (bw_Analysis_FileSaver.CancellationPending) + return; + } + sw.WriteLine("]"); + } + } + + private void Analysis_Planes_Save_CSV(string filename) + { + int saved = 0; + using (StreamWriter sw = new StreamWriter(filename)) + { + sw.WriteLine("time[utc];hex;call;lat[deg];lon[deg];alt[ft];track[deg];speed[kts]"); + foreach (AircraftPositionDesignator ap in AllPositions) + { + if (ap.LastUpdated < dtp_Analysis_MinValue.Value) + continue; + if (ap.LastUpdated > dtp_Analysis_MaxValue.Value) + continue; + sw.WriteLine(ap.LastUpdated.ToString("yyyy-MM-dd HH:mm:ssZ") + ";" + + ap.Hex + ";" + + ap.Call + ";" + + ap.Lat.ToString("F8", CultureInfo.InvariantCulture) + ";" + + ap.Lon.ToString("F8", CultureInfo.InvariantCulture) + ";" + + ap.Alt.ToString("F8", CultureInfo.InvariantCulture) + ";" + + ap.Track.ToString("F8", CultureInfo.InvariantCulture) + ";" + + ap.Speed.ToString("F8", CultureInfo.InvariantCulture) + ); + saved++; + if (saved % 1000 == 0) + bw_Analysis_FileSaver.ReportProgress(0, "Saving position " + saved.ToString() + "..."); + if (bw_Analysis_FileSaver.CancellationPending) + return; + } + } + } + + private void bw_Analysis_FileSaver_DoWork(object sender, DoWorkEventArgs e) + { + string filename = (string)e.Argument; + // name the thread for debugging + if (String.IsNullOrEmpty(Thread.CurrentThread.Name)) + Thread.CurrentThread.Name = "bw_Analysis_FileSaver"; + try + { + if (filename.ToLower().EndsWith(".json")) + Analysis_Planes_Save_JSON(filename); + else if (filename.ToLower().EndsWith(".csv")) + Analysis_Planes_Save_CSV(filename); + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + bw_Analysis_FileSaver.ReportProgress(-1, ex.Message); + } + if (bw_Analysis_FileSaver.CancellationPending) + e.Cancel = true; + } + + private void bw_Analysis_FileSaver_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + if (e.ProgressPercentage <= 0) + SayAnalysis((string)e.UserState); + } + + private void bw_Analysis_FileSaver_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + if (e.Cancelled) + SayAnalysis("Cancelled."); + else + { + btn_Analysis_Planes_Save.Enabled = true; + SayAnalysis("Ready."); + } + } + + #endregion + + #region Analysis_FileLoader + + private void Analysis_Planes_Load_JSON(string filename) + { + JsonSerializerSettings settings = new JsonSerializerSettings(); + settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; + settings.FloatFormatHandling = FloatFormatHandling.String; + settings.Formatting = Newtonsoft.Json.Formatting.Indented; + settings.Culture = CultureInfo.InvariantCulture; + List aps = new List(); + bw_Analysis_FileLoader.ReportProgress(0, "Opening file..."); + using (StreamReader sr = new StreamReader(File.Open(filename, FileMode.Open))) + { + // check for starting bracket of array + char c = (char)sr.Read(); + if (c != '[') + return; + // read 1000 positions and update database + int count = 0; + int updated = 0; + while (!sr.EndOfStream) + { + aps.Clear(); + int j = 0; + while ((j < 1000) && !sr.EndOfStream) + { + char nextChar; + StringBuilder line = new StringBuilder(); + while ((j < 1000) && sr.Peek() > 0) + { + nextChar = (char)sr.Read(); + line.Append(nextChar); + if (line[0] != '{') + line.Clear(); + if (nextChar == '}') + { + AircraftPositionDesignator ap = JsonConvert.DeserializeObject(line.ToString(), settings); + line.Clear(); + aps.Add(ap); + j++; + } + } + if (bw_Analysis_FileLoader.CancellationPending) + return; + } + count = count + j; + updated = updated + AircraftPositionData.Database.AircraftPositionBulkInsertOrUpdateIfNewer(aps); + bw_Analysis_FileLoader.ReportProgress(0, "Updating position " + count.ToString() + ", " + updated.ToString() + " updated so far..."); + } + + } + } + + private void Analysis_Planes_Load_CSV(string filename) + { + List aps = new List(); + bw_Analysis_FileLoader.ReportProgress(0, "Opening file..."); + using (StreamReader sr = new StreamReader(File.Open(filename, FileMode.Open))) + { + // read header + string header = sr.ReadLine(); + // split header + string[] a = header.Split(';'); + // remove unit brackets and lower all + for (int i = 0; i < a.Length; i++) + { + a[i] = a[i].ToLower(); + if (a[i].IndexOf('[') >= 0) + a[i] = a[i].Substring(0, a[i].IndexOf('[')); + } + int lastupdated_index = Array.IndexOf(a, "time"); + int hex_index = Array.IndexOf(a, "hex"); + int call_index = Array.IndexOf(a, "call"); + int lat_index = Array.IndexOf(a, "lat"); + int lon_index = Array.IndexOf(a, "lon"); + int alt_index = Array.IndexOf(a, "alt"); + int track_index = Array.IndexOf(a, "track"); + int speed_index = Array.IndexOf(a, "speed"); + bw_Analysis_FileLoader.ReportProgress(0, "Creating position list..."); + // read 1000 positions and update database + int count = 0; + int updated = 0; + while (!sr.EndOfStream) + { + aps.Clear(); + int j = 0; + while ((j < 1000) && !sr.EndOfStream) + { + string s = sr.ReadLine(); + if (!s.Contains(";")) + continue; + a = s.Split(';'); + DateTime lastupdated = System.Convert.ToDateTime(a[lastupdated_index]).ToUniversalTime(); + string hex = a[hex_index].ToUpper(); + string call = a[call_index].ToUpper(); + double lat = System.Convert.ToDouble(a[lat_index], CultureInfo.InvariantCulture); + double lon = System.Convert.ToDouble(a[lon_index], CultureInfo.InvariantCulture); + double alt = System.Convert.ToDouble(a[alt_index], CultureInfo.InvariantCulture); + double track = System.Convert.ToDouble(a[track_index], CultureInfo.InvariantCulture); + double speed = System.Convert.ToDouble(a[speed_index], CultureInfo.InvariantCulture); + AircraftPositionDesignator ap = new AircraftPositionDesignator(hex, call, lat, lon, alt, track, speed, lastupdated); + aps.Add(ap); + j++; + if (bw_Analysis_FileLoader.CancellationPending) + return; + } + count = count + j; + updated = updated + AircraftPositionData.Database.AircraftPositionBulkInsertOrUpdateIfNewer(aps); + bw_Analysis_FileLoader.ReportProgress(0, "Updating position " + count.ToString() + ", " + updated.ToString() + " updated so far..."); + } + + } + } + + private void bw_Analysis_FileLoader_DoWork(object sender, DoWorkEventArgs e) + { + string filename = (string)e.Argument; + // name the thread for debugging + if (String.IsNullOrEmpty(Thread.CurrentThread.Name)) + Thread.CurrentThread.Name = "bw_Analysis_FileLoader"; + try + { + if (filename.ToLower().EndsWith(".json")) + Analysis_Planes_Load_JSON(filename); + else if (filename.ToLower().EndsWith(".csv")) + Analysis_Planes_Load_CSV(filename); + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + bw_Analysis_FileLoader.ReportProgress(-1, ex.Message); + } + if (bw_Analysis_FileLoader.CancellationPending) + e.Cancel = true; + } + + private void bw_Analysis_FileLoader_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + if (e.ProgressPercentage <= 0) + SayAnalysis((string)e.UserState); + } + + private void bw_Analysis_FileLoader_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + if (e.Cancelled) + SayAnalysis("Cancelled."); + else + { + btn_Analysis_Planes_Load.Enabled = true; + SayAnalysis("Ready."); + } + } + + #endregion + + + #region AirportMapper + + private void bw_AirportMapper_DoWork(object sender, DoWorkEventArgs e) + { + bw_AirportMapper.ReportProgress(0, "Getting airports from database..."); + Stopwatch st = new Stopwatch(); + st.Start(); + // clear airports if any + gmo_Airports.Clear(); + // fill the airports layer of maps + // return if switched off + if (!Properties.Settings.Default.Airports_Activate) + return; + List airports = new List(); + airports = AircraftData.Database.AirportGetAll(bw_AirportMapper); + if (airports != null) + bw_AirportMapper.ReportProgress(100, airports); + st.Stop(); + bw_AirportMapper.ReportProgress(0, airports.Count.ToString() + " airports updated, " + st.ElapsedMilliseconds.ToString() + " ms."); + } + + private void bw_AirportMapper_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + if (e.ProgressPercentage <= 0) + { + string msg = (string)e.UserState; + Say(msg); + Log.WriteMessage(msg); + } + else if (e.ProgressPercentage == 100) + { + lock (Airports) + { + Airports = (List)e.UserState; + } + UpdateAirports(); + } + } + + private void bw_AirportMapper_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + + } + + #endregion + + private void gm_Main_Load(object sender, EventArgs e) + { + + } + + } + + + public class ClippingToolStripRenderer : ToolStripSystemRenderer + { + protected override void OnRenderItemText(ToolStripItemTextRenderEventArgs e) + { + ToolStripStatusLabel label = e.Item as ToolStripStatusLabel; + + if (label != null) + { + TextRenderer.DrawText(e.Graphics, label.Text, + label.Font, e.TextRectangle, + label.ForeColor, + TextFormatFlags.EndEllipsis); + } + else + { + base.OnRenderItemText(e); + } + } + } + + public class LocatorDropDownItem + { + public string Locator { get; set; } + public LatLon.GPoint GeoLocation { get; set; } + + public LocatorDropDownItem(string locator, LatLon.GPoint geolocation) + { + Locator = locator; + GeoLocation = geolocation; + } + } + +} diff --git a/AirScout/MapDlg.resx b/AirScout/MapDlg.resx new file mode 100644 index 0000000..29ba4f0 --- /dev/null +++ b/AirScout/MapDlg.resx @@ -0,0 +1,7674 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 340, 17 + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAACc + DQAAAk1TRnQBSQFMAgEBAwEAAeABCgHgAQoBIAEAASABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo + AwABgAMAASADAAEBAQABCAYAARAYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB3AHA + AQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEAAykBAANVAQADTQEAA0IBAAM5 + AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8BAAHWAucBAAGQAakBrQIAAf8BMwMA + AWYDAAGZAwABzAIAATMDAAIzAgABMwFmAgABMwGZAgABMwHMAgABMwH/AgABZgMAAWYBMwIAAmYCAAFm + AZkCAAFmAcwCAAFmAf8CAAGZAwABmQEzAgABmQFmAgACmQIAAZkBzAIAAZkB/wIAAcwDAAHMATMCAAHM + AWYCAAHMAZkCAALMAgABzAH/AgAB/wFmAgAB/wGZAgAB/wHMAQABMwH/AgAB/wEAATMBAAEzAQABZgEA + ATMBAAGZAQABMwEAAcwBAAEzAQAB/wEAAf8BMwIAAzMBAAIzAWYBAAIzAZkBAAIzAcwBAAIzAf8BAAEz + AWYCAAEzAWYBMwEAATMCZgEAATMBZgGZAQABMwFmAcwBAAEzAWYB/wEAATMBmQIAATMBmQEzAQABMwGZ + AWYBAAEzApkBAAEzAZkBzAEAATMBmQH/AQABMwHMAgABMwHMATMBAAEzAcwBZgEAATMBzAGZAQABMwLM + AQABMwHMAf8BAAEzAf8BMwEAATMB/wFmAQABMwH/AZkBAAEzAf8BzAEAATMC/wEAAWYDAAFmAQABMwEA + AWYBAAFmAQABZgEAAZkBAAFmAQABzAEAAWYBAAH/AQABZgEzAgABZgIzAQABZgEzAWYBAAFmATMBmQEA + AWYBMwHMAQABZgEzAf8BAAJmAgACZgEzAQADZgEAAmYBmQEAAmYBzAEAAWYBmQIAAWYBmQEzAQABZgGZ + AWYBAAFmApkBAAFmAZkBzAEAAWYBmQH/AQABZgHMAgABZgHMATMBAAFmAcwBmQEAAWYCzAEAAWYBzAH/ + AQABZgH/AgABZgH/ATMBAAFmAf8BmQEAAWYB/wHMAQABzAEAAf8BAAH/AQABzAEAApkCAAGZATMBmQEA + AZkBAAGZAQABmQEAAcwBAAGZAwABmQIzAQABmQEAAWYBAAGZATMBzAEAAZkBAAH/AQABmQFmAgABmQFm + ATMBAAGZATMBZgEAAZkBZgGZAQABmQFmAcwBAAGZATMB/wEAApkBMwEAApkBZgEAA5kBAAKZAcwBAAKZ + Af8BAAGZAcwCAAGZAcwBMwEAAWYBzAFmAQABmQHMAZkBAAGZAswBAAGZAcwB/wEAAZkB/wIAAZkB/wEz + AQABmQHMAWYBAAGZAf8BmQEAAZkB/wHMAQABmQL/AQABzAMAAZkBAAEzAQABzAEAAWYBAAHMAQABmQEA + AcwBAAHMAQABmQEzAgABzAIzAQABzAEzAWYBAAHMATMBmQEAAcwBMwHMAQABzAEzAf8BAAHMAWYCAAHM + AWYBMwEAAZkCZgEAAcwBZgGZAQABzAFmAcwBAAGZAWYB/wEAAcwBmQIAAcwBmQEzAQABzAGZAWYBAAHM + ApkBAAHMAZkBzAEAAcwBmQH/AQACzAIAAswBMwEAAswBZgEAAswBmQEAA8wBAALMAf8BAAHMAf8CAAHM + Af8BMwEAAZkB/wFmAQABzAH/AZkBAAHMAf8BzAEAAcwC/wEAAcwBAAEzAQAB/wEAAWYBAAH/AQABmQEA + AcwBMwIAAf8CMwEAAf8BMwFmAQAB/wEzAZkBAAH/ATMBzAEAAf8BMwH/AQAB/wFmAgAB/wFmATMBAAHM + AmYBAAH/AWYBmQEAAf8BZgHMAQABzAFmAf8BAAH/AZkCAAH/AZkBMwEAAf8BmQFmAQAB/wKZAQAB/wGZ + AcwBAAH/AZkB/wEAAf8BzAIAAf8BzAEzAQAB/wHMAWYBAAH/AcwBmQEAAf8CzAEAAf8BzAH/AQAC/wEz + AQABzAH/AWYBAAL/AZkBAAL/AcwBAAJmAf8BAAFmAf8BZgEAAWYC/wEAAf8CZgEAAf8BZgH/AQAC/wFm + AQABIQEAAaUBAANfAQADdwEAA4YBAAOWAQADywEAA7IBAAPXAQAD3QEAA+MBAAPqAQAD8QEAA/gBAAHw + AfsB/wEAAaQCoAEAA4ADAAH/AgAB/wMAAv8BAAH/AwAB/wEAAf8BAAL/AgAD//8A/wAtAAL0fgABKAH0 + WwAB9AHxAbwDBwH0AQAB/wHxAbwDBwHzFAACJwL0WQABGQG0Aa0DpgG8AQAB8wG0Aa0DpgEHFAACJwEo + AfRZAAEZAtUBzwGLAaYBvAEAAfMC1QHPAYsBpgEHFAADKAEnAvQZAAL/AfQB8wHxAfACvAHxAfMB9AL/ + MQAB8QG0AdwBCQHPAaYBvAEAAfMBtAHcAQkBzwGmAQcUAAIoAicBKAH0GQAB/wH0AfABBwF0AW4BSwJu + Ae8BvAHzAf8xAAHxAbQCCQG0AaYBvAEAAfMBtAIJAbQBpgEHFAADSQEoAicC9BUAAv8B8wG8AZMBbwFM + BEUBbgHtAQcB8QL/LwAB8QG0AgkBtAGmAbwBAAHzAbQCCQG0AaYBBxQABEkCJwEoAfQVAAH/AfQBvAF0 + AUwERgIlASQBRQFuAQcB8wH/LwAB8QHVAdwBCQG0AaYBvAEAAfMB1QHcAQkBtAGmAQcUAAJJAU8CSQIo + AScC9BMAAfQBGwGTBkwCRgElASQBRQHtAbwB9C8AAfEB1QEJAd0BtAGmAbwBAAHzAdUBCQHdAbQBpgEH + FAACSQJPAkkCJwEoAfQTAAH0AQcBdAhMAUYBJQEkAW4B7wHzLwAB8QHVAQkB3QG0AaYBvAEAAfMB1QEJ + Ad0BtAGmAQcUAAJJAXICTwJJAigBJwL0EQAB8gGTAm8DdQLjA0wBRgElAUUBbgHxLwABGQHVAQkB3QG0 + AaYBvAEAAfMB1QEJAd0BtAGmAQcUAAJJAnICTwJJAicBKAH0EQABGgF0AW8BFgV1AeMCTAFGASUBRQFu + AbwvAAEZAdUB3QEZAbQBhgG8AQAB8wHVAd0BGQG0AYYBBxQAAk8BlwJyAk8CSQMoAvQPAAEaAm8BdQSU + AnUB4wJMAUYBRQFLAbwvAAEZAdYCGQG0AYYBvAEAAfMB1gIZAbQBhgEJFAACTwKXAnICTwJJAigC9A8A + ARoCdAKUApoClAF1AeMCTAFGAUUBbgHwLwABGQHWAhkBtQGGAbwBAAHzAdYCGQG1AYYBCRQAAk8DlwNy + A0kB9BEAAfIBkwF0AZQEmgGUAnUCTAFGAUwBdAHxLwABGQHWAhkBtQGGAbwBAAHzAdYCGQG1AYYBvBQA + Ak8ElwFyA0kC9BEAAfMBGgGTAXQBmgG9ApoBlAJ1AkwBRgFvAQcB8y8AARkB1gIZAbUBhgG8AQAB8wHW + AhkBtQGGAbwUAAJQA5cBCANJAfQTAAH0ARsBkwF0AXUCmgKUAnUDTAGTAfAB9C8AARkB3AIZAbUBiwG8 + AQAB8wHcAhkBtQGLAbwUAAJQApcBCANJAvQTAAH/AfQBGgGTAnQClAF1ARYBbwJMAXQBvAH0Af8vAAHz + AdwBCQHdAdYBzwHdAQAB8wHcAQkB3QHWAc8B8BQAAnIBlwEIA0kB9BUAAv8B8wEaApMCdANvAXQBkwG8 + AfMC/y8AAfMD3AHVAbQB8gEAAfMD3AHVAbQB8RQAAnIBCANJAvQXAAH/AfQBGwEaAZMBdAFvAXQBkwEH + ARsB9AH/MQAB9AHzAxkB8QH0AQAB/wHzAxkB8QH0FAACcgNJAfQZAAL/AfQB8wHyAxoB8gL0Av9UAAJy + AkkC9HoAAnIBSQH0fAACcgL0fAABcgH0fgAC9P8AVQABQgFNAT4HAAE+AwABKAMAAYADAAEgAwABAQEA + AQEGAAECFgAD/wEADP8EAAz/BAAM/wQADP8EAAX/Ac8G/wQABf8Bzwb/BAAB/gECAQMC/wHDBv8EAAH+ + AQIBAwL/AcMG/wQAAf4BAgEDAv8BwAP/AYABAwH/BAAB/gECAQMC/wHAA/8BgAEDAf8EAAH+AQIBAwL/ + AcABPwH/Af4CAAH/BAAB/gECAQMC/wHAAT8B/wH+AgAB/wQAAf4BAgEDAv8BwAEPAf8B/gIAAf8EAAH+ + AQIBAwL/AcABDwH/Af4CAAH/BAAB/gECAQMC/wHAAQMB/wH+AgAB/wQAAf4BAgEDAv8BwAEDAf8B/gIA + Af8EAAH+AQIBAwL/AcABAAH/Af4CAAH/BAAB/gECAQMC/wHAAQAB/wH+AgAB/wQAAf4BAgEDAv8BwAED + Af8B/gIAAf8EAAH+AQIBAwL/AcABAwH/Af4CAAH/BAAB/gECAQMC/wHAAQ8B/wH+AgAB/wQAAf4BAgED + Av8BwAEPAf8B/gIAAf8EAAH+AQIBAwL/AcABPwH/Af4CAAH/BAAB/gECAQMC/wHAAT8C/wGAAQMB/wQA + Af4BAgEDAv8BwAP/AYABAwH/BAAF/wHABv8EAAX/AcMG/wQABf8Bwwb/BAAF/wHPBv8EAAX/Ac8G/wQA + DP8EAAz/BAAL + + + + 131, 17 + + + 651, 17 + + + + + iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + awAADmsBVP4NBgAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAHbSURBVGhD7Zg/ + S8NAGIejIAo6OCiiVnqNRRQXxclV3QUXQcTRr+DaUXAQWpOYSXBx1dk/30Ad3ZwUpA7VXFJEqfEuvi1t + c4kpmEuU94HfcO9d7+4Z7npEQRAEQRAEQRAkCGJUiOK6XdAMhBy5fVnTGYVm+lA1WlB1epUvVjNQEpIv + 0uGcTl9U3d6AUrrgImyDLkuFaHQdyj5AhI9jsY5ntfIAdKWDJpHGJkf2nvqhu0GrCI99nz20FqE7efwi + LBq9yxrOPAzx8It4+eC/VwpuNwxLDqHId96bNxkg4oXo9DJjVMe9CZMiRKSe84mSMxYmAnkmB9YqTCuf + CCI85VzpdVNQF0R8xmInogjPp6AmDjtjRLPnYAk5dCDSad6Ibu1IuwhiFKnHO2OwXHzELmLQCyk3Wowi + Dst2lHfcr9CBSE1QC8ptVqMzsIQcooiwR+WDqlXXRH1tqbFH5W6+6PbC9PL4UcSgp1OmNRThD/GRSazA + tPIJFrHtnGZvwbDQJwrLGe+HockgElENek1MaxqGeIhFWmUTpU2kxtsLptsD3Q0EIjfSD3QYdRF+oCcN + ugRlH00iyR3oMLgIkzgh+5VBKAnhIt7tdWAvQyld/JuPDwiCIAiCIAjyR1GUL4wxdzcMzFiBAAAAAElF + TkSuQmCC + + + + + iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + awAADmsBVP4NBgAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAEfSURBVGhD7dg9 + a8JwFMXhbAVbbZ2EDqJpoIPfy6WDn6DgWHAQhEQy9ONZROsLjXt6lHtBRXxLSO4fzgN3sGg8P+ikR0RE + RERElwTx+q0Tzp7kpbvaUfKB+/OjzVczWtflz+6RkFTO3aCjkIOg1nD1Im+zD6NPhei5E4Sx50L07Adh + 5DUhegs/TPp+vHyWj9uBcbeE6P2aC8Koe0L07ARhTJYQvV1QMFrU5LHFw4g8QvTmpQXhy/MMWbbHyWcp + /2r48jxCflrhptcYTB/lscXDiCwhu4DXOK3I48qDMfeE2AlQGHVLyMRcgMK4a0ImuG4wSh/kY/Zg4LkQ + +wEKQ0+FuBOgMHg/xL0AJSHuBqjtjw/v3/OqvCQiIiIiooJ43j8cRZAeVK3X2QAAAABJRU5ErkJggg== + + + + + iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + awAADmsBVP4NBgAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAACGSURBVGhD7dix + CcMwEEBRjeJ0WcqQPvMkIdt5Btm9E4E6XZHiMMS8B6p0J/xbFwAA4BfTu14vz/U+nNc295FBu4t22lt9 + 5HjTo96+H7GPpy59ZNDuop32Vh85npBgR0gGIcGOkAxCgh0hGYQEO0IyCAl2hGQQEuwIyXCekLP8fAAA + gD9Sygf13QPKE81S/AAAAABJRU5ErkJggg== + + + + + iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAACH + DwAAjA8AAP1SAACBQAAAfXkAAOmLAAA85QAAGcxzPIV3AAAKOWlDQ1BQaG90b3Nob3AgSUNDIHByb2Zp + bGUAAEjHnZZ3VFTXFofPvXd6oc0wAlKG3rvAANJ7k15FYZgZYCgDDjM0sSGiAhFFRJoiSFDEgNFQJFZE + sRAUVLAHJAgoMRhFVCxvRtaLrqy89/Ly++Osb+2z97n77L3PWhcAkqcvl5cGSwGQyhPwgzyc6RGRUXTs + AIABHmCAKQBMVka6X7B7CBDJy82FniFyAl8EAfB6WLwCcNPQM4BOB/+fpFnpfIHomAARm7M5GSwRF4g4 + JUuQLrbPipgalyxmGCVmvihBEcuJOWGRDT77LLKjmNmpPLaIxTmns1PZYu4V8bZMIUfEiK+ICzO5nCwR + 3xKxRoowlSviN+LYVA4zAwAUSWwXcFiJIjYRMYkfEuQi4uUA4EgJX3HcVyzgZAvEl3JJS8/hcxMSBXQd + li7d1NqaQffkZKVwBALDACYrmcln013SUtOZvBwAFu/8WTLi2tJFRbY0tba0NDQzMv2qUP91829K3NtF + ehn4uWcQrf+L7a/80hoAYMyJarPziy2uCoDOLQDI3fti0zgAgKSobx3Xv7oPTTwviQJBuo2xcVZWlhGX + wzISF/QP/U+Hv6GvvmckPu6P8tBdOfFMYYqALq4bKy0lTcinZ6QzWRy64Z+H+B8H/nUeBkGceA6fwxNF + hImmjMtLELWbx+YKuGk8Opf3n5r4D8P+pMW5FonS+BFQY4yA1HUqQH7tBygKESDR+8Vd/6NvvvgwIH55 + 4SqTi3P/7zf9Z8Gl4iWDm/A5ziUohM4S8jMX98TPEqABAUgCKpAHykAd6ABDYAasgC1wBG7AG/iDEBAJ + VgMWSASpgA+yQB7YBApBMdgJ9oBqUAcaQTNoBcdBJzgFzoNL4Bq4AW6D+2AUTIBnYBa8BgsQBGEhMkSB + 5CEVSBPSh8wgBmQPuUG+UBAUCcVCCRAPEkJ50GaoGCqDqqF6qBn6HjoJnYeuQIPQXWgMmoZ+h97BCEyC + qbASrAUbwwzYCfaBQ+BVcAK8Bs6FC+AdcCXcAB+FO+Dz8DX4NjwKP4PnEIAQERqiihgiDMQF8UeikHiE + j6xHipAKpAFpRbqRPuQmMorMIG9RGBQFRUcZomxRnqhQFAu1BrUeVYKqRh1GdaB6UTdRY6hZ1Ec0Ga2I + 1kfboL3QEegEdBa6EF2BbkK3oy+ib6Mn0K8xGAwNo42xwnhiIjFJmLWYEsw+TBvmHGYQM46Zw2Kx8lh9 + rB3WH8vECrCF2CrsUexZ7BB2AvsGR8Sp4Mxw7rgoHA+Xj6vAHcGdwQ3hJnELeCm8Jt4G749n43PwpfhG + fDf+On4Cv0CQJmgT7AghhCTCJkIloZVwkfCA8JJIJKoRrYmBRC5xI7GSeIx4mThGfEuSIemRXEjRJCFp + B+kQ6RzpLuklmUzWIjuSo8gC8g5yM/kC+RH5jQRFwkjCS4ItsUGiRqJDYkjiuSReUlPSSXK1ZK5kheQJ + yeuSM1J4KS0pFymm1HqpGqmTUiNSc9IUaVNpf+lU6RLpI9JXpKdksDJaMm4ybJkCmYMyF2TGKQhFneJC + YVE2UxopFykTVAxVm+pFTaIWU7+jDlBnZWVkl8mGyWbL1sielh2lITQtmhcthVZKO04bpr1borTEaQln + yfYlrUuGlszLLZVzlOPIFcm1yd2WeydPl3eTT5bfJd8p/1ABpaCnEKiQpbBf4aLCzFLqUtulrKVFS48v + vacIK+opBimuVTyo2K84p6Ss5KGUrlSldEFpRpmm7KicpFyufEZ5WoWiYq/CVSlXOavylC5Ld6Kn0Cvp + vfRZVUVVT1Whar3qgOqCmrZaqFq+WpvaQ3WCOkM9Xr1cvUd9VkNFw08jT6NF454mXpOhmai5V7NPc15L + Wytca6tWp9aUtpy2l3audov2Ax2yjoPOGp0GnVu6GF2GbrLuPt0berCehV6iXo3edX1Y31Kfq79Pf9AA + bWBtwDNoMBgxJBk6GWYathiOGdGMfI3yjTqNnhtrGEcZ7zLuM/5oYmGSYtJoct9UxtTbNN+02/R3Mz0z + llmN2S1zsrm7+QbzLvMXy/SXcZbtX3bHgmLhZ7HVosfig6WVJd+y1XLaSsMq1qrWaoRBZQQwShiXrdHW + ztYbrE9Zv7WxtBHYHLf5zdbQNtn2iO3Ucu3lnOWNy8ft1OyYdvV2o/Z0+1j7A/ajDqoOTIcGh8eO6o5s + xybHSSddpySno07PnU2c+c7tzvMuNi7rXM65Iq4erkWuA24ybqFu1W6P3NXcE9xb3Gc9LDzWepzzRHv6 + eO7yHPFS8mJ5NXvNelt5r/Pu9SH5BPtU+zz21fPl+3b7wX7efrv9HqzQXMFb0ekP/L38d/s/DNAOWBPw + YyAmMCCwJvBJkGlQXlBfMCU4JvhI8OsQ55DSkPuhOqHC0J4wybDosOaw+XDX8LLw0QjjiHUR1yIVIrmR + XVHYqLCopqi5lW4r96yciLaILoweXqW9KnvVldUKq1NWn46RjGHGnIhFx4bHHol9z/RnNjDn4rziauNm + WS6svaxnbEd2OXuaY8cp40zG28WXxU8l2CXsTphOdEisSJzhunCruS+SPJPqkuaT/ZMPJX9KCU9pS8Wl + xqae5Mnwknm9acpp2WmD6frphemja2zW7Fkzy/fhN2VAGasyugRU0c9Uv1BHuEU4lmmfWZP5Jiss60S2 + dDYvuz9HL2d7zmSue+63a1FrWWt78lTzNuWNrXNaV78eWh+3vmeD+oaCDRMbPTYe3kTYlLzpp3yT/LL8 + V5vDN3cXKBVsLBjf4rGlpVCikF84stV2a9021DbutoHt5turtn8sYhddLTYprih+X8IqufqN6TeV33za + Eb9joNSydP9OzE7ezuFdDrsOl0mX5ZaN7/bb3VFOLy8qf7UnZs+VimUVdXsJe4V7Ryt9K7uqNKp2Vr2v + Tqy+XeNc01arWLu9dn4fe9/Qfsf9rXVKdcV17w5wD9yp96jvaNBqqDiIOZh58EljWGPft4xvm5sUmoqb + PhziHRo9HHS4t9mqufmI4pHSFrhF2DJ9NProje9cv+tqNWytb6O1FR8Dx4THnn4f+/3wcZ/jPScYJ1p/ + 0Pyhtp3SXtQBdeR0zHYmdo52RXYNnvQ+2dNt293+o9GPh06pnqo5LXu69AzhTMGZT2dzz86dSz83cz7h + /HhPTM/9CxEXbvUG9g5c9Ll4+ZL7pQt9Tn1nL9tdPnXF5srJq4yrndcsr3X0W/S3/2TxU/uA5UDHdavr + XTesb3QPLh88M+QwdP6m681Lt7xuXbu94vbgcOjwnZHokdE77DtTd1PuvriXeW/h/sYH6AdFD6UeVjxS + fNTws+7PbaOWo6fHXMf6Hwc/vj/OGn/2S8Yv7ycKnpCfVEyqTDZPmU2dmnafvvF05dOJZ+nPFmYKf5X+ + tfa5zvMffnP8rX82YnbiBf/Fp99LXsq/PPRq2aueuYC5R69TXy/MF72Rf3P4LeNt37vwd5MLWe+x7ys/ + 6H7o/ujz8cGn1E+f/gUDmPP8usTo0wAAAAlwSFlzAAAOawAADmsBVP4NBgAAAVJJREFUaEPdmL1KA0EU + RtMFTDRaCRaSrAsWvpeNhU8QsAykCAR2ZQsfzxDy5+Kmn4zghWX4srk4S2Y+PzjNaeYe2DTpGGP+BVAy + AiUjUDICJSNQMgIlI1AyAiUjUDICJSNQMgIlI1AyAqWGtveUrfppUT6gtzRAqaGt3eflTZLvJ6O8+ra8 + oLc0QKnBd06A+YUnZDj7ugYBPCEnAoR4Q5QBQnwhSbEbJFn1Zo/bOsc2EU9ILWDjHKkhfIhngBAuJJ1v + r1oIEM4fUgtYO8f4cP6Qn09p9F6N7eM75xgfwn1at9Nlb5jtX+0RC+eovxD+x35XmIsWgsKHyDyD4gmR + 1YI+nWObiC9Els5N1x74bNEExRsiUwbFHyI7EcQTIjsSxBcic4J4Q2SPH+vLIH8+xAaUjEDJCJSMQMkI + lIxAyQiUjEDJCJSMQMkIlIxAyQiUfJjOAYSEGeapo4flAAAAAElFTkSuQmCC + + + + + iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + awAADmsBVP4NBgAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAHmSURBVGhD7Zg/ + S8NAGIerIAo6OCiiVnuJRfwziZOruAtOIuLoV3DtKDgIrUnMJLi4iqvoN1BHNycFqUM1lxZRNL4v9Opx + vUTBkhzyPvCjzeUgvwcu16M5giAIgiAIgiA6TcGvj7KjqK95GU8UdTGvxppXZmG74Ybl8udimQ83h7QU + y4287fJL2+Gl5pAZzDvVAcsNjkEiwiSJMI+voyzOM0qkcBgsWW54JyTiREb2HvtlWYwZIqWoG4tAoXe5 + HEYVKXj1Bcvht+q8zEXyXmMc17haTKQl8i37ps7BZCrCDoJVKPGklpKDIhOV+hh8P1fvyclERLfGY1N5 + 2YTPatu4ktRF4tZ4Qj41Y21JTwTWOHODHXjoq1qiE0lF5Ddr/K9JRQR3JubyC12BTiW9pQXnIXjgNqQu + F+hUUn/ZJw/5HDz4Ri2SkA/NWFtSF0HwRAuHwl0o8GNJ22mswQ/mve6enExEBCCzAiUe1FJy8Adx2g+G + LI+f6u6LZCqCYFE4KJ7pymHks5blhFswN9TNy1xEEFdSFkGYH8zYHr9S5xkjghQcPgulruWCqgiy6Ec9 + WBzut94xo0SQYjnqlTcCnYhgyuPLYiMwTkQgNoIkEYTt1wZB5sRYEeRf/PlAEARBEARBEAaQy30BMyN3 + UqNFwykAAAAASUVORK5CYII= + + + + 557, 17 + + + + iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + wwAADsMBx2+oZAAAABBJREFUGFdjqMcBhpREfT0AN/NfQdTsp04AAAAASUVORK5CYII= + + + + + iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + wwAADsMBx2+oZAAAABBJREFUGFdjqMcBhpREfT0AN/NfQdTsp04AAAAASUVORK5CYII= + + + + + iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + wwAADsMBx2+oZAAAABBJREFUGFdjqMcBhpREfT0AN/NfQdTsp04AAAAASUVORK5CYII= + + + + + iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + wwAADsMBx2+oZAAAABBJREFUGFdjqMcBhpREfT0AN/NfQdTsp04AAAAASUVORK5CYII= + + + + + iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + wwAADsMBx2+oZAAAABBJREFUGFdjqMcBhpREfT0AN/NfQdTsp04AAAAASUVORK5CYII= + + + + 651, 17 + + + 743, 17 + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAACM + DAAAAk1TRnQBSQFMAwEBAAFIAQkBSAEJASABAAEgAQAE/wEJAQAI/wFCAU0BNgEEBgABNgEEAgABKAMA + AYADAAEgAwABAQEAAQgGAAEQGAABgAIAAYADAAKAAQABgAMAAYABAAGAAQACgAIAA8ABAAHAAdwBwAEA + AfABygGmAQABMwUAATMBAAEzAQABMwEAAjMCAAMWAQADHAEAAyIBAAMpAQADVQEAA00BAANCAQADOQEA + AYABfAH/AQACUAH/AQABkwEAAdYBAAH/AewBzAEAAcYB1gHvAQAB1gLnAQABkAGpAa0CAAH/ATMDAAFm + AwABmQMAAcwCAAEzAwACMwIAATMBZgIAATMBmQIAATMBzAIAATMB/wIAAWYDAAFmATMCAAJmAgABZgGZ + AgABZgHMAgABZgH/AgABmQMAAZkBMwIAAZkBZgIAApkCAAGZAcwCAAGZAf8CAAHMAwABzAEzAgABzAFm + AgABzAGZAgACzAIAAcwB/wIAAf8BZgIAAf8BmQIAAf8BzAEAATMB/wIAAf8BAAEzAQABMwEAAWYBAAEz + AQABmQEAATMBAAHMAQABMwEAAf8BAAH/ATMCAAMzAQACMwFmAQACMwGZAQACMwHMAQACMwH/AQABMwFm + AgABMwFmATMBAAEzAmYBAAEzAWYBmQEAATMBZgHMAQABMwFmAf8BAAEzAZkCAAEzAZkBMwEAATMBmQFm + AQABMwKZAQABMwGZAcwBAAEzAZkB/wEAATMBzAIAATMBzAEzAQABMwHMAWYBAAEzAcwBmQEAATMCzAEA + ATMBzAH/AQABMwH/ATMBAAEzAf8BZgEAATMB/wGZAQABMwH/AcwBAAEzAv8BAAFmAwABZgEAATMBAAFm + AQABZgEAAWYBAAGZAQABZgEAAcwBAAFmAQAB/wEAAWYBMwIAAWYCMwEAAWYBMwFmAQABZgEzAZkBAAFm + ATMBzAEAAWYBMwH/AQACZgIAAmYBMwEAA2YBAAJmAZkBAAJmAcwBAAFmAZkCAAFmAZkBMwEAAWYBmQFm + AQABZgKZAQABZgGZAcwBAAFmAZkB/wEAAWYBzAIAAWYBzAEzAQABZgHMAZkBAAFmAswBAAFmAcwB/wEA + AWYB/wIAAWYB/wEzAQABZgH/AZkBAAFmAf8BzAEAAcwBAAH/AQAB/wEAAcwBAAKZAgABmQEzAZkBAAGZ + AQABmQEAAZkBAAHMAQABmQMAAZkCMwEAAZkBAAFmAQABmQEzAcwBAAGZAQAB/wEAAZkBZgIAAZkBZgEz + AQABmQEzAWYBAAGZAWYBmQEAAZkBZgHMAQABmQEzAf8BAAKZATMBAAKZAWYBAAOZAQACmQHMAQACmQH/ + AQABmQHMAgABmQHMATMBAAFmAcwBZgEAAZkBzAGZAQABmQLMAQABmQHMAf8BAAGZAf8CAAGZAf8BMwEA + AZkBzAFmAQABmQH/AZkBAAGZAf8BzAEAAZkC/wEAAcwDAAGZAQABMwEAAcwBAAFmAQABzAEAAZkBAAHM + AQABzAEAAZkBMwIAAcwCMwEAAcwBMwFmAQABzAEzAZkBAAHMATMBzAEAAcwBMwH/AQABzAFmAgABzAFm + ATMBAAGZAmYBAAHMAWYBmQEAAcwBZgHMAQABmQFmAf8BAAHMAZkCAAHMAZkBMwEAAcwBmQFmAQABzAKZ + AQABzAGZAcwBAAHMAZkB/wEAAswCAALMATMBAALMAWYBAALMAZkBAAPMAQACzAH/AQABzAH/AgABzAH/ + ATMBAAGZAf8BZgEAAcwB/wGZAQABzAH/AcwBAAHMAv8BAAHMAQABMwEAAf8BAAFmAQAB/wEAAZkBAAHM + ATMCAAH/AjMBAAH/ATMBZgEAAf8BMwGZAQAB/wEzAcwBAAH/ATMB/wEAAf8BZgIAAf8BZgEzAQABzAJm + AQAB/wFmAZkBAAH/AWYBzAEAAcwBZgH/AQAB/wGZAgAB/wGZATMBAAH/AZkBZgEAAf8CmQEAAf8BmQHM + AQAB/wGZAf8BAAH/AcwCAAH/AcwBMwEAAf8BzAFmAQAB/wHMAZkBAAH/AswBAAH/AcwB/wEAAv8BMwEA + AcwB/wFmAQAC/wGZAQAC/wHMAQACZgH/AQABZgH/AWYBAAFmAv8BAAH/AmYBAAH/AWYB/wEAAv8BZgEA + ASEBAAGlAQADXwEAA3cBAAOGAQADlgEAA8sBAAOyAQAD1wEAA90BAAPjAQAD6gEAA/EBAAP4AQAB8AH7 + Af8BAAGkAqABAAOAAwAB/wIAAf8DAAL/AQAB/wMAAf8BAAH/AQAC/wIAA/8BAAX/AvQB/w8AAd4B0gG9 + AdIB/wGrAd4B0gHeYAABqwG3AX8B9ALSAasBtw8AAb0BfwG3AasB/wF/AbcBfwHzYAABqwG3AX8B8wPS + AbcPAAG9AasBtwGrAf8BfwG3AX8B9GAAAdIBtwGrAd4BtwPSDwABvQGrAbcBqwH/AX8BtwF/AfRgAAHS + AbcBqwG9AbcBqwG3AdIPAAG3AasBtwGrAf8BfwG3AX8B/2AAAbcC0gG3Ab0BqwG3AdIPAAG3AasBtwGr + Af8BfwG3AX8B/2AAAbcC0gG3Ad4BfwG3AasPAAG3AasBtwHSAfQBfwG3AX8B/2AAAb0BqwG3AdIB3gF/ + AbcBqw8AAbcBqwG3AdIB9AF/AbcBfwH/YAAB3gGrAbcB0gHzAX8BtwF/Af8OAAG3AasC0gH0AX8BtwGr + Af9gAAH0AX8BtwGrAfQBfwG3AX8B/w4AAbcD0gHzAX8BtwGrYQAB9AF/AbcBqwH/AX8BtwF/Af8OAAG3 + A9IB3gF/AbcBq2EAAf8BfwG3AX8B/wF/AbcBfwH/BQAB9AL/BgABtwLSAbcB3gF/AbcB0mEAAf8BfwG3 + AX8B/wF/AbcBfwH0AwAB/wHzAbcB9AG3Ad4B/wQAAbcC0gG3Ad4BqwG3AdJiAAGrAbcBfwHzAX8BtwF/ + Ab0DAAG9An8B3gJ/Af8EAAPSArcBqwG3AdJiAAGrAbcCfwGrAbcBfwGrAwABvQJ/AbcBqwF/AdIB/wMA + AasB0gGrAbcBfwGrAdIBt2IAAd4BtwF/AasBtwGrAX8B0gL0Af8B3gJ/AdIBfwKrAdIB/wK3AasBvQHe + Ab0BqwF/AfQB/2MAAfMGfwKrAdIBqwd/Ad4Bqwd/Af9jAAH/AfMBqwHSAqsB0gG3BH8E0gGrAbcB0gGr + AbcBvQG3AdIBtwHeArcB8wH/YgAB3gGrAdIBtwHeAasB0gG9AasD0gH/Ab0BtwL/AQAB/wGrAf8BAALS + AasB3gG3AdIBqwG3YgAB8wF/AbcB0gHeAasB0gG3Af8EAAH/Ad4EAAHeAfQBAAHSAbcBqwG9AtIBqwG3 + YgAB9AF/AbcB0gH0AasCtwYAAf8EAAL/AQAB0gG3AX8B3gHSAbcBqwG9YgAB/wF/AbcBqwH0AX8Ctw4A + AdIBtwF/Ad4B0gG3AasBvWIAAf8BfwG3AasB/wF/ArcOAAHSAbcBfwHeAasBtwF/Ad5iAAH/AX8BtwF/ + Af8BfwG3AdIOAAGrAbcBfwHzAasBtwF/Ad5jAAGrAbcBfwH/AX8BtwHSDgABqwG3AX8B9AGrAbcBfwHz + YwAB0gG3AX8B9AGrAbcBqw4AAasBtwF/AfQBqwG3AX8B9GMAAdIBtwF/AfQBqwG3AasOAAGrAbcBfwH0 + AX8BtwF/AfRjAAK3AasB8wHSAbcBqw4AAasBtwF/AfQBfwG3AX8B/2MAAbcB0gGrAd4B0gG3AX8B/w0A + AasBtwF/AfQBfwG3AX8B/2MAAb0C0gG9ArcBfwH/DQABqwG3AX8B9AF/AbcBfwH/YwAB/wLeAfQB/wHe + AbcB/w0AAbcB0gF/Af8C0gF/Af95AAP/AQAC/2IAAUIBTQE+BwABPgMAASgDAAGAAwABIAMAAQEBAAEB + BgABAhYAA/8CAAH/Af4OAAH/Af4OAAH/Af4OAAH/Af4OAAH/Af4OAAH/Af4OAAH/Af4OAAH/Af4OAAF/ + Af4OAAF/Af4BAQ0AAX8B/gEBDQABfAF+AQENAAFwAR4BAQwAAYABcAEeAQEMAAGAAXABDgEBDAABgAIA + AQEMAAHAAgABAwwAAYACAAEBDAABgAEAASIBAQwAAYABPAHyAQEMAAGAAX4B8gEBDAABgAF/Af4BAQwA + AYABfwH+AQEMAAGAAX8B/gEBDAABwAF/Af4BAQwAAcABfwH+AQEMAAHAAX8B/gEBDAABwAF/Af4BAQwA + AcABPwH+AQEMAAHAAT8B/gEBDAABwAE/Af4BAQwAA/8BEwwACw== + + + + 823, 17 + + + 17, 17 + + + 981, 17 + + + 546, 56 + + + 1245, 17 + + + 838, 56 + + + 949, 56 + + + 1057, 56 + + + 707, 56 + + + 1166, 56 + + + 1302, 56 + + + 17, 95 + + + 123, 95 + + + 301, 95 + + + 405, 95 + + + 546, 56 + + + 195, 56 + + + 376, 56 + + + 17, 56 + + + 1347, 17 + + + 93 + + + + AAABAAkAAAAAAAEAIAAoIAQAlgAAAICAAAABACAAKAgBAL4gBABgYAAAAQAgAKiUAADmKAUASEgAAAEA + IACIVAAAjr0FAEBAAAABACAAKEIAABYSBgAwMAAAAQAgAKglAAA+VAYAICAAAAEAIACoEAAA5nkGABgY + AAABACAAiAkAAI6KBgAQEAAAAQAgAGgEAAAWlAYAKAAAAAABAAAAAgAAAQAgAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRz + cgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBw + cABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJ + CQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9v + bwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFx + cQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGho + aAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNy + cgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1s + awBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFx + cABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBv + cABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNz + dABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNy + cwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1t + bABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxs + bQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9u + bgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBw + cABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4 + dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNy + cgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5t + bAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1t + bABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1s + bABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBw + bwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBw + cABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNz + cwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1t + bQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1s + bABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1t + bQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFw + cABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFx + cQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJx + cgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBw + cABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5u + bQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1t + bABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRz + cgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBw + cABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJ + CQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9v + bwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFx + cQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGho + aAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNy + cgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1s + awBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFx + cABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBv + cABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNz + dABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAAlIMAAJSDAACUg + wAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAAlIMAAJSDAACUg + wAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAAlIMAAJSDAACUg + wAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNy + cwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1t + bABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxs + bQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9u + bgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBw + cABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4 + dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNy + cgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5t + bAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1t + bABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1s + bABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBw + bwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBw + cABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNz + cwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1t + bQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1s + bABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1t + bQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFw + cABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFx + cQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJx + cgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBw + cABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5u + bQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1t + bABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRz + cgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBw + cABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJ + CQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9v + bwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFx + cQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGho + aAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNy + cgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1s + awBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFx + cABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBv + cABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNz + dABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNy + cwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1t + bABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxs + bQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9u + bgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBw + cABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4 + dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNy + cgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5t + bAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQAAABaAAAAlAAAALgAAAC/AAAAvgAAAL8AAAC+AAAApAAA + AIIAAABVAAAALQAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1t + bABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1s + bABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAGgAAAHIAAADHAAAA9gAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA9QAAANMAAACVAAAARgAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAD///8A////ACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBw + bwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBw + cABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAJAAAAdgAAAOMAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADIAAAAWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA////AP///wD///8AJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNz + cwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1t + bQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAyAAAAxQAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAACoAAAAGgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP///wD///8A////AP///wAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1s + bABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1t + bQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAABcAAAA7AAAAP8AAAD/AQMA/wUHEv8MDDD/ExVB/xUYUP8VGFD/EhQ//xET + Of8JCCn/BQcV/wUHBv8AAQD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAANEAAAAkAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////ACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFw + cABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFx + cQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAB4AAAA/wAAAP8EBgT/EhRO/x4dnf8lI8//Jh7r/ycg7f8oIfD/JyHv/ycg + 7f8nIOv/Jh7q/yUj1P8kIrf/HBqR/xUYVf8ICRj/AAEA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA0AAA + ABUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8AJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJx + cgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBw + cABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAACBAAAA/wECAP8SFEj/IyG4/ygh8v8mHvb/JRzu/yUe6f8lHej/JR3p/yUd + 6f8lHOr/Ixrs/yIa7v8hGPP/IRj4/yMa/P8kHPn/Ix7b/xoah/8KCyH/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAACjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5u + bQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1t + bABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAA + AAAAAAAAAAAAAAAAAAAAAACAAAAA/wcKEP8dHov/KCHw/yYd9P8lHej/JR3n/yUd5/8lHej/Ixvr/yEZ + 7f8gF+//Hxvo/yUi2v8tKsv/NDnA/zk+uP84Pbv/MzfB/ywn0/8oJOz/IR/d/xETYv8AAQD/AAAA/wAA + AP8AAAD/AAAA/gAAAEcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////ACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRz + cgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBw + cABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJ + CQAAAAAAAAAAAAAAAAAAAABrAAAA/w0PJf8kIrv/JyD3/yUd6v8lHef/JBzp/yIa7P8gGO7/Hxnq/yUi + 3P8wMMb/RUms/2Von/+Gi5z/qKml/7q7tP/Dxbz/w8S8/7q7s/+lpqP/g4ea/15ho/89QcP/HCF3/wAA + BP8AAAD/AAAA/wAAAP8AAADGAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP// + /wD///8AJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9v + bwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFx + cQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGho + aAAJCQkAAAAAAAAAAAAAAABbAAAA/A8SNP8mIdD/Jhz2/yMa6/8hGe3/Hxfu/yAd5P8qJ8//Oz+0/11g + nf+Bh5n/q6yn/9HSxv/s6+D////5//////////////////////////////////389//j49f/t7mw/3V6 + n/8qLkf/AAAA/wAAAP8AAAD/AAAA/wAAAEcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gACYe4AAmHuAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNy + cgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1s + awBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFx + cABoaGgACQkJAAAAAAAAAABFAAAA8g8TPf8kH97/IRj5/x4Z6v8kIdv/MTHE/0pOp/9wc5n/mZ2f/8bH + u//k5Nj//Pv1//////////////////////////////////////////////////////////////////// + ///t7ef/p6eo/0tLS/8NDQ3/AAAA/wAAAP8AAACmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBv + cABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNz + dABxcXAAaGhoAAkJCQAAAAAtAAAA5gkMNf8hINn/KSXd/zg7t/9YXZ7/gYaY/7Cxq//X2Mz/8vHo//// + /v////////////////////////////////////////////////////////////////////////////// + ///////////////////f3tr/goGA/xoaGf8AAAD/AAAA6gAAABYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wAlHt8AJR7fACUe3wAlHt8AJR7fACUe3wAlHt8AJR7fACUe + 3wAlHt8AJR7fACUe3wAlHt8AJR7fACUe3wB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNy + cwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1t + bABzc3QAcXFwAGhoaAAJCQkiAAAA1BcZH/9CSZH/aGqm/5CUm/+/wLX/4+PX//v69P////////////// + ///////////////////////////////////////////9/////f/+/PX/6+vp/9rf6v/S3PT/zdr9/8PO + +/++yPr/vsn6/8DL+//M2f3/4Ov//+/x8v+TkIb/GxoW/wAAAP8AAABTAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8AJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxs + bQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9u + bgBtbWwAc3N0AHFxcBRoaGhJS0tMz21tbf+ioaP/z87L/+zs4f////v///////////////////////// + ///////////////////////////+//j59f/a4vD/v8np/5im3f90i9v/V3Hd/z1a4f8wUeH/KU7p/yVN + 8f8cQu//GD3v/xk+7/8aQO//I0vw/y9V8f9aevr/l6vr/3BzdP8SEAf/AAAAfgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBw + cABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4 + dwBvbm4hbW1sWHNzdKCLjIvYrq6u/9jY2f/29vf///////////////////////////////////////// + ///////////+/+3y+v/F0vb/obHv/3CL6f9LaOf/MFPn/x1D5/8WPO//Fzzv/xk+8f8bQPL/HkLx/x9D + 8P8gQ+//IkXv/yJG8P8iRu//IkXw/yBE7/8eQe//GD3u/xxD9f8+XuP/Rk9w/wkHAIkJBwAACQcAAAkH + AAAJBwAACQcAAAkHAAAJBwAACQcAAAkHAAAJBwAACQcAAAkHAAAJBwAACQcAAAkHAAAJBwAACQcAAAkH + AAAJBwAACQcAAAkHAAAJBwAACQcAAAkHAAAJBwAACQcAAAkHAAAJBwAACQcAAAkHAAAJBwAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNy + cgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHACbWxsL25t + bGp4eHe0k5ST5Le3t//e3t//+Pj5///////////////////////////////////////////////9/+3y + 9//AzvT/kqjy/1597/89Xu7/Ikjs/xY87v8XPe7/GT/w/x5C8f8hRfH/I0fw/yNH8P8jRvD/I0fw/yNG + 7/8jR+//I0fv/yNH7/8jRu//I0bv/yNH7/8jR/D/I0fw/yNH7/8iRe//HkP3/yxP7f8/SnKbO13kACFE + 8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE + 8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1t + bABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxBm1tbT1tbW14fX19w5yb + nPDDw8T/5ubm//39/v/////////////////////////////////////////+////9v/d4u7/qrjs/2uG + 6/88Xez/IEbr/xY87v8YPe//G0Dv/yFE8P8jRu//I0fv/yNH8P8jR+//I0bv/yNG7/8jRvD/I0bw/yNH + 7/8jRvD/I0fv/yNG7/8jR+//I0fw/yNH8P8jR+//I0fw/yNH8P8jRu//I0fv/yNH7/8gRfT/LlHr8Dtd + 5DEhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE + 8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJh7gACYe + 4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gAHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBw + bwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcAttbWxGcHBwh4KCgsyjo6T4zc3P/+zs + 7f//////////////////////////////////////////////+//8+vD/2Nna/6Wv0/9geNj/MVPn/xo/ + 7P8YPe//HEHw/yFE8P8jR/D/I0fw/yNG7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH7/8jR/D/I0bw/yNH + 8P8jRu//I0bv/yNH8P8jR+//I0bv/yNH8P8jR/D/I0fv/yNH7/8jR+//I0bv/yNH8P8jR+//I0fv/yFG + 8f8hRfLNIUTyFSFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE + 8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE + 8gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wAmHt8AJh7fACYe3wAmHt8AJh7fACYe3wAmHt8AJh7fACYe3wAmHt8AJh7fACYe3wB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNz + cwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3ARbWxrTXFxcZaIiIjVqamq/NPT0//x8fL///////// + //////////////////////////////////////j/8/Lp/8rN1f+Un8r/XHLL/zFR2P8bQOf/GT/w/x5D + 8f8iRfD/I0fw/yNH8P8jR+//I0fw/yNH8P8jR+//I0bv/yNG7/8jR/D/I0bw/yNG7/8jRu//I0fw/yNG + 8P8jR+//I0fv/yNH8P8jR/D/I0fv/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fw/yNH7/8jR/D/I0jx/yNH + 8P8jR+//I0fv/yNH76IjR+8AJEfwACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8AJh7fACYe3wAmHt8AJh7fACYe3wAmHt8AJh7fACYe3wAmHt8AJh7fACYe3wAmHt8AdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1s + bABzc3MAcHBvAG1tbABzcnIAcHBwE2xsbVRzcnOdioqL3a+vsP/X19f/9vb2//////////////////// + ///////////////////////////2/+Xl4f+6v9H/gI7G/01mzv8pSdv/G0Dp/xk/8v8eQ/L/Ikbx/yNH + 8P8jR/D/I0fv/yNH8P8jR+//I0fv/yNH7/8jRu//I0fv/yNH7/8jRu//I0fv/yNG7/8jRu//I0bv/yNH + 7/8jR+//I0fw/yNH7/8jR/D/I0fv/yNH7/8jR/D/I0fv/yNH8P8jR+//I0bv/yNH8P8jSPD/I0fv/yRC + 7f8jR+//I0jw/yNH7/8jRu//I0fvciRH8AAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AB4W3wAeFt8AHhbfAB4W3wAeFt8AHhbfAB4W3wAeFt8AHhbfAB4W3wAeFt8AHhbfAHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFw + cABtbGwAc3NzAHBwbxJtbWxUc3JynIqKi9+xsbL/29vc//j4+f////////////////////////////// + ///////////8//399P/X2dv/prHV/2t+y/89WtT/IkTh/xk/7v8bQPL/IEXy/yJG8f8jR/D/I0bv/yNH + 7/8jR/D/I0fw/yNH8P8jR+//I0bv/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0fv/yNG7/8jR/D/I0fv/yNH + 7/8jR+//I0fv/yNH8P8jRvD/I0fv/yNH7/8jR/D/I0fw/yNG7/8jR+//I0fv/yNG7/8jSPD/JETu/yYq + 4/8mIOD/JS3k/yNF7/8jSfD/I0fv/yNH7/MkR/A7I0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8AUFDnAFBQ5wBQUOcAUFDnAFBQ5wBQUOcAUFDnAFBQ5wBQUOcAUFDnAFBQ + 5wB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJx + cgBxcHARbWxsVHNzc52JiorhsrGy/9zb3P/5+fn///////////////////////////////////////// + +v/19fH/x8ze/5Gf1f9ZcdH/MFDd/xxA5/8ZP/H/HUPy/yFF8f8jRvD/I0fw/yNH7/8jR+//I0bv/yNH + 8P8jR+//I0fw/yNH8P8jRvD/I0bv/yNH8P8jR/D/I0bv/yNH8P8jR/D/I0fw/yNH8P8jR+//I0fv/yNH + 7/8jR/D/I0fw/yNG7/8jR/D/I0bv/yNH7/8jR+//I0fv/yNG7/8jRu//I0fv/yNH7/8jR+//I0bv/yYp + 4/8mHN7/Jh7f/yYc3v8mJ+P/I0Ht/yNJ8P8jR+//I0fv0CNH7xMjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AMTM+gDEzPoAxMz6AMTM+gDEzPoAxMz6AMTM+gDEzPoAxMz6AMTM + +gDEzPoAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyBW5u + bUVycXKViYmJ4LKysv/c3Nz/+fn5//////////////////////////////////////////r/6+zw/7TB + 5f97jtr/RmPb/yVH5f8ZP+z/GkDy/x9E8v8iRvH/I0bv/yNH8P8jR+//I0fw/yNH7/8jR/D/I0fv/yNH + 7/8jRvD/I0bv/yNH8P8jR+//I0fv/yNH7/8jR+//I0bv/yNG7/8jR/D/I0fw/yNG7/8jR/D/I0fv/yNH + 7/8jR+//I0bv/yNG8P8kRu//I0fw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0nw/yU0 + 5/8mHN//Jh7f/yYf4P8lHuD/Jhzf/yYk4f8kQez/I0nw/yNG7/8jRu+aI0fwACNH8AAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A09L8ANPS/ADT0vwA09L8ANPS/ADT0vwA09L8ANPS + /ADT0vwA09L8AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vYH9/ + f8ypqKr/19fY//j4+f///////////////////////////////////////f76/9zi8P+ktOn/aIDh/zhY + 4v8eQ+j/GD7v/x1C8v8gRfH/Ikfw/yNH8P8jRu//I0fv/yNH7/8jR/D/I0bw/yNH7/8jR+//I0fv/yNH + 7/8jRu//I0bv/yNH8P8jR/D/I0fv/yNG7/8jR+//I0fw/yNG8P8jRu//I0bv/yNH7/8jR+//I0fw/yNG + 8P8jR+//I0fw/yNH8P8jRu//JEfw/yNH8P8jR/D/I0bv/yNH7/8jR+//I0bv/yNH7/8jR/D/I0jw/yRD + 7f8mIuD/Jh3f/yYf4P8mH+D/Jh/f/yYf4P8mHd//JiXh/yRB7P8jSPD/I0bv/yNH8FwjR/AAI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AM/P+wDPz/sAz8/7AM/P+wDPz/sAz8/7AM/P + +wDPz/sAz8/7AM/P+wB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQCc3Jyiays + rf/u7u/////////////////////////////////////+//n7/P/L1vL/kqbt/1Vz5v8tT+j/Gj/s/xg+ + 8P8eQ/H/IUXw/yNG7/8jR/D/I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNG7/8jRu//I0fw/yNH + 7/8jRu//I0bv/yNH7/8jR/D/I0fv/yNH8P8jR/D/I0fv/yNH8P8jR/D/I0fv/yNG7/8jRu//I0fw/yNG + 8P8jR+//I0fw/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fw/yNG7/8jR+//I0fv/yNH7/8jR/D/I0fw/yNJ + 8P8lNej/Jh3f/yYf4P8mH+D/Jh7g/yYe4P8mH9//Jh/g/yYd3/8mJOH/JELt/yNJ8P8jR/DjI0fwIiNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Az8/7AM/P+wDPz/sAz8/7AM/P + +wDPz/sAz8/7AM/P+wDPz/sAcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBw + bwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBw + bwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBw + bwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBw + bwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBw + bwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvPaOj + o////////////////////////////////v/x9fr/vszz/4OZ7f9HaOv/JEjs/xc97f8bQPH/IETx/yJG + 8P8jRu//I0bv/yNH7/8jRu//I0fw/yNH8P8jRu//I0fv/yNH7/8jR/D/I0fv/yNH7/8jRu//I0bv/yNH + 8P8jR+//I0bv/yNG7/8jR+//I0bv/yNH7/8jR+//I0fw/yNH7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNG + 7/8jRu//I0fv/yNH7/8jR+//I0bv/yNH8P8jR/D/I0bv/yNH8P8jRu//I0bv/yNH7/8jR+//I0bv/yNH + 8P8jR+//Jifj/yYd3/8mH+D/Jh7g/yYe4P8mH+D/Jh7f/yYf4P8mHuD/Jhzf/yUp4/8jR+//I0fv/yNH + 77AjRu8GI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM/P+gDPz/oAz8/6AM/P + +gDPz/oAz8/6AM/P+gDPz/oAz8/6AHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cDafn6D7/f3+////////////9/n6/7nG7P94kfD/PF7s/x5D7v8WPO7/HEDv/yFF7/8jR/D/I0bv/yNH + 8P8jR+//I0fw/yNG7/8jRu//I0fw/yNH8P8jR/D/I0bv/yNH7/8jRvD/I0fw/yNH7/8jR/D/I0bw/yNH + 8P8jR/D/I0fv/yNH7/8jR/D/I0fv/yNG7/8jR/D/I0fw/yNH7/8jR+//I0bv/yNH7/8jR/D/I0fw/yNG + 7/8jRu//I0bv/yNH7/8jRu//I0fv/yNG7/8jR/D/I0fv/yNG7/8jR/D/I0bw/yNG7/8jR+//I0fv/yNH + 8P8jSPD/JEHt/yYg4P8mHd//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mHN//JDTo/yNJ + 8P8jR+//I0fvTyNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Azs/6AM7P + +gDOz/oAzs/6AM7P+gDOz/oAzs/6AM7P+gBwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwjMLCw///////0d38/0Vn7f8aP+v/Fjzv/x1B7/8iRfD/I0bw/yNG7/8jR+//I0bv/yNH + 8P8jRu//I0bv/yNH8P8jR+//I0fw/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH + 8P8jR/D/I0bv/yNH7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH8P8jR+//I0fv/yNH7/8jR/D/I0fw/yNH + 7/8jRu//I0fv/yNH7/8jR/D/I0fv/yNG8P8jR/D/I0fv/yNH7/8jR/D/I0bv/yNH8P8jR/D/I0fv/yNH + 8P8jR/D/I0nx/yQ56v8mHd//Jh7f/yYf4P8mHuD/Jh/g/yYf4P8lHt//Jh7g/yYf4P8mH+D/Jh3f/yYk + 4f8kRe7/I0jw/yNH8GgjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM7P + +wDOz/sAzs/7AM7P+wDOz/sAzs/7AM7P+wDOz/sAJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNI8ABycnIAcnJyAHJycgBycnIAcnJyAHJy + cgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJy + cgBycnIAcnJyAHJychGCgHrLsbrW/zlf9/8VO+//Ikbw/yNH7/8jRu//I0fw/yNH8P8jRvD/I0bv/yNG + 7/8jR/D/I0bw/yNG7/8jR+//I0fv/yNH8P8jR/D/I0bv/yNG7/8jR/D/I0fw/yNH7/8jRu//I0fw/yNH + 7/8jR+//I0fw/yNG8P8jR+//I0fv/yNH7/8jR/D/I0bv/yNG8P8jR+//I0bv/yNG7/8jR+//I0fv/yNH + 7/8jR+//I0fw/yNH8P8jR+//I0fv/yNG7/8jRu//I0fv/yNH7/8jR/D/I0fw/yNG7/8jR/D/I0fv/yNH + 7/8jR+//I0fv/yNJ8P8lNOf/Jhzf/yYe3/8mH9//Jh/g/yYe4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mIOD/JD7s/yNJ8P8jRu9mI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8Az8/6AM/P+gDPz/oAz8/6AM/P+gDPz/oAz8/6ACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjSPAAI0bvACNJ8QAjSPAAcnJyAHJy + cgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJy + cgBycnIAcnJyAHJycgBycnIAeXdtI1RgmJcaQPX/JEfw/yNH7/8jRu//I0bw/yNH7/8jR+//I0fw/yNH + 8P8jRu//I0bw/yNH8P8jR+//I0fw/yNH7/8jR/D/I0fv/yNH7/8jR+//I0bw/yNH8P8jRu//I0fv/yNH + 7/8jR/D/I0fw/yNH8P8jR/D/I0bv/yNH7/8jR+//I0bv/yNH8P8jR/D/I0fw/yNG7/8jRu//I0fv/yNG + 7/8jR/D/I0fv/yNH7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH + 7/8jR+//I0fv/yNH8P8jSfD/JS/l/yYd3/8mHt//Jh7f/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7f/yYf + 4P8mH+D/Jhvf/yQ66v8jSvH/I0fvWyNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////ANPS/ADT0vwA09L8ANPS/ADT0vwA09L8ANPS/AAmIcAAJiHAACYhwAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0jwACNG7wAjSfEAI0jwACNI + 8AAjR/AAI0jwAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJy + cgBycnIAcnJyAHJycgBycnIAcnJyAHl3bQBUYJgAI0bwwSNG8P8jR/D/I0fw/yNH8P8jR+//I0fv/yNG + 7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fw/yNG7/8jR+//I0fv/yNG7/8jR/D/I0fv/yNH + 7/8jR+//I0fw/yNG7/8jRu//I0fv/yNG8P8jR/D/I0fv/yNH7/8jRu//I0fv/yNH8P8jR+//I0fw/yNH + 8P8jR+//I0fw/yNG7/8jRu//I0fv/yNH8P8jRvD/I0bv/yNH8P8jR+//I0fv/yNG8P8jR/D/I0fw/yNG + 7/8jRu//I0fv/yNH8P8jR/D/I0jw/yYr5P8mHd//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYd3/8kPOv/I0nx/yNH70kjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8AvsL5AL7C+QC+wvkAvsL5AL7C+QC+wvkAJiHAACYhwAAmIcAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNI8AAjRu8AI0nxACNI + 8AAjSPAAI0fwACNI8AAjSfAAI0XuACNI8ABycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJy + cgBycnIAcnJyAHJycgBycnIAcnJyAHJycgB5d20AI0fvACNH71MjR/D+I0fw/yNH8P8jR+//I0fw/yNH + 8P8jRu//I0fw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fw/yNH8P8jRu//I0fv/yNH + 7/8jR+//I0fw/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0fw/yNH7/8jR+//I0fv/yNG7/8jRvD/I0fv/yNH + 7/8jR+//I0fv/yNH7/8jR+//I0bv/yNG7/8jR+//I0bv/yNH7/8jR+//I0bv/yNG8P8jR/D/I0fv/yNG + 7/8jRu//I0bv/yNH7/8jR+//I0fw/yNH7/8mJuL/Jh3g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mIeD/JEHt/yNI8PEjRu8xI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AKGt9AChrfQAoa30AKGt9AChrfQAoa30ACYhwAAmIcAAJiHAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjSPAAI0bvACNJ + 8QAjSPAAI0jwACNH8AAjSPAAI0nwACNF7gAjSPAAI0rxACNJ8AAkR/AAJEfwACRH8AAkR/AAJEfwACRH + 8AAkR/AAJEfwACRH8AAkR/AAJEfwACRH8AAkR/AAJEfwACRH8AAkR/AHI0fwwSNH7/8jR+//I0fw/yNH + 8P8jR/D/I0fv/yNG7/8jRvD/I0bv/yNH7/8jR/D/I0fv/yNH7/8jR/D/I0fv/yNH7/8jR+//I0fv/yNH + 7/8jRu//I0bw/yNH8P8jRvD/I0fw/yNH8P8jR+//I0bv/yNG7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH + 7/8jR+//I0fv/yNG7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0bv/yNG7/8jR+//I0bv/yNG + 8P8jRu//I0bv/yNG7/8jRu//I0fv/yNH8P8kRO7/JiLh/yYd4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mHd//JiTh/yRF7v8jR/DYI0fwEiNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8Ac4ntAHOJ7QBzie0Ac4ntAHOJ7QAmIcAAJiHAACYh + wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0jwACNG + 7wAjSfEAI0jwACNI8AAjR/AAI0jwACNJ8AAjRe4AI0jwACNK8QAjSfAAI0fvACRH8AAkR/AAJEfwACRH + 8AAkR/AAJEfwACRH8AAkR/AAJEfwACRH8AAkR/AAJEfwACRH8AAkR/AAJEfwACNH8FMjR+/+I0fv/yNH + 8P8jR/D/I0fv/yNH7/8jR+//I0bv/yNG7/8jRu//I0fw/yNH7/8jR+//I0bv/yNH7/8jR/D/I0fw/yNH + 7/8jRu//I0bv/yNH8P8jR/D/I0bv/yNH8P8jRvD/I0fv/yNG7/8jR/D/I0fv/yNG8P8jRu//I0fw/yNH + 7/8jRu//I0bv/yNG8P8jR+//I0fv/yNG7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH8P8jRvD/I0fv/yNG + 7/8jRvD/I0fv/yNH8P8jRu//I0fv/yNH8P8jSPD/JEDs/yYg4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jhzf/yUp4/8jSfD/I0fwwCNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////ADU54wA1OeMANTnjADU54wA1OeMAJiHAACYh + wAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNI + 8AAjRu8AI0nxACNI8AAjSPAAI0fwACNI8AAjSfAAI0XuACNI8AAjSvEAI0nwACNH7wAjR/AAI0jwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AHI0fvwiNH + 7/8jR+//I0fw/yNH7/8jRu//I0fv/yNH7/8jRvD/I0fv/yNH7/8jR+//I0fv/yNH8P8jR/D/I0fw/yNH + 8P8jR+//I0bv/yNG7/8jR+//I0fv/yNH7/8jRvD/I0bv/yNH7/8jR/D/I0fw/yNG7/8jRu//I0fw/yNH + 8P8jRu//I0bv/yNG7/8jR/D/I0fv/yNH7/8jRu//I0bv/yNH8P8jR/D/I0fv/yNH8P8jR/D/I0fw/yNH + 7/8jRu//I0fw/yNH7/8jRu//I0fv/yNG7/8jRu//I0nw/yQ46f8mHd//Jh/g/yYe3/8mHuD/Jh7g/yYe + 4P8mH+D/Jh/g/yYe3/8mH+D/Jh7g/yYc3/8lL+X/I0nw/yNH8JAjR/AAI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AIxnfACMZ3wAjGd8AIxnfACYh + wAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjSPAAI0bvACNJ8QAjSPAAI0jwACNH8AAjSPAAI0nwACNF7gAjSPAAI0rxACNJ8AAjR+8AI0fwACNI + 8AAjSPAAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNG + 71IjR/D9I0fv/yNG7/8jR+//I0fw/yNH8P8jR/D/I0bv/yNH8P8jRvD/I0fv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNG7/8jR+//I0fw/yNH8P8jR+//I0fw/yNH + 8P8jR+//I0bv/yNH7/8jR+//I0fw/yNH8P8jRu//I0fv/yNG7/8jRu//I0fv/yNH7/8jR/D/I0bv/yNG + 7/8jRvD/I0fv/yNH7/8jRu//I0fv/yNH7/8jRu//I0bw/yNJ8P8lLuX/Jh3f/yYf4P8mH+D/Jh7g/yYe + 4P8mHuD/JR7f/yYf4P8mH+D/Jh/g/yYf4P8mHN//JTHm/yNJ8P8jR+9eI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ACUe3wAlHt8AJR7fACUe + 3wAmIcAAJiHAACYhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0jwACNG7wAjSfEAI0jwACNI8AAjR/AAI0jwACNJ8AAjRe4AI0jwACNK8QAjSfAAI0fvACNH + 8AAjSPAAI0jwACNF7gAjSO8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8FI0fvuCNH8P8jRvD/I0fv/yNH8P8jR/D/I0fv/yNH7/8jRvD/I0bw/yNG7/8jRu//I0fv/yNH + 7/8jRu//I0fw/yNH8P8jR/D/I0fw/yNG7/8jR+//I0fv/yNH8P8jR/D/I0fw/yNH8P8jR/D/I0fw/yNH + 8P8jRvD/I0bw/yNG8P8jR/D/I0fv/yNH8P8jR/D/I0bw/yNH8P8jR+//I0bw/yNG8P8jRvD/I0fw/yNH + 8P8jRvD/I0bw/yNH7/8kR/D/JEfw/yNG8P8jRu//I0fw/yNI8P8jRO7/JSPh/yYd3/8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yUe3/8mH9//Jh/g/yYe4P8mHuD/Jhzf/yUx5f8jSfD0I0bwMyNG8AAjRvAAI0bwACNG + 8AAjRvAAI0bwACNG8AAjRvAAI0bwACNG8AAjRvAAI0bwACNG8AAjRvAAI0bwACNG8AAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJh/gACYf + 4AAmH+AAJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNI8AAjRu8AI0nxACNI8AAjSPAAI0fwACNI8AAjSfAAI0XuACNI8AAjSvEAI0nwACNH + 7wAjR/AAI0jwACNI8AAjRe4AI0jvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNG70gjR/D8I0fw/yNG8P8jRu//I0bv/yNH8P8jRu//I0fv/yNH8P8jR/D/I0fw/yNH + 7/8jR+//I0bv/yNG8P8jRvD/I0bv/yNH8P8jRvD/I0fw/yNH7/8jR/D/I0bw/yRH8P8jRu//I0bv/yNH + 8P8jR+//I0bw/yNH7/8jR/D/I0bw/yNH8P8jR+//I0fw/yNH8P8jR/D/I0fv/yNH8P8jRvD/I0fw/yNH + 8P8jRvD/I0bw/yNH8P8jRu//I0bv/yNH8P8jRu//I0bv/yNG8P8jSfH/JTnp/yYd3/8mH+D/JR7f/yYf + 4P8lH9//JR7f/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh7f/yYd3/8lMeb/I0nwySNG7wcjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAABAAAAHAAAADsAAABJAAAAVgAAAGIAAABSAAAARQAAADQAAAAWAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ACYf + 4AAmH+AAJh/gACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjSPAAI0bvACNJ8QAjSPAAI0jwACNH8AAjSPAAI0nwACNF7gAjSPAAI0rxACNJ + 8AAjR+8AI0fwACNI8AAjSPAAI0XuACNI7wAlOOkAJD3rACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8DI0bvsyNH7/8jRvD/I0fv/yNH8P8jR/D/I0bw/yNH8P8jR/D/I0fw/yNH + 8P8jR/D/I0fw/yNH7/8jR/D/I0bw/yNH7/8jR+//I0fw/yNH8P8jRvD/I0bv/yNH8P8jRvD/I0fv/yNH + 7/8jR+//I0bw/yNH7/8jRu//I0fv/yNG7/8jRvD/I0bw/yNG7/8jR/D/I0fv/yNH8P8jR+//I0bv/yNH + 8P8jR/D/I0fv/yNH7/8jR+//JEfw/yNG8P8kR/D/I0fv/yNG7/8jR+//I0jw/yYq5P8mHd//Jh/g/yUe + 3/8mH+D/Jh/g/yYf4P8mH9//Jh7f/yYf3/8mHuD/Jh/g/yYf3/8mHd//JS3l/yNL8H0jRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAgAAADsAAACBAAAAvQAAAN4AAAD6AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD0AAAA2QAA + ALUAAABmAAAAHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wAlHt8AJR7fACUe3wAmIcAAJiHAACYhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0jwACNG7wAjSfEAI0jwACNI8AAjR/AAI0jwACNJ8AAjRe4AI0jwACNK + 8QAjSfAAI0fvACNH8AAjSPAAI0jwACNF7gAjSO8AJTjpACQ96wAkP+sAI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7zojRu/4I0fv/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fv/yNG + 7/8jRu//I0fv/yNH7/8jR/D/I0bw/yNG7/8jRu//I0fv/yNH8P8jR+//I0fv/yNG7/8jRu//I0fv/yNH + 8P8jR/D/I0fv/yNG8P8jR/D/I0fw/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0fv/yNG7/8jRu//I0bv/yNH + 7/8jR+//I0fv/yNH7/8jRu//I0fw/yRH7/8jR/D/JEfv/yNH7/8jRu//I0jw/yQ/7P8mIOD/Jh7g/yYe + 4P8mHuD/Jh7g/yYf4P8mHuD/Jh7f/yUd3/8lK+X/JS3l/yYf4P8mH+D/Jh7g/yYf4P8lLeVPJS3lACUt + 5QAlLeUAJS3lACUt5QAlLeUAJS3lACUt5QAlLeUAJS3lACUt5QAlLeUAJS3lACUt5QAlLeUAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAHAAAAXQAAAL4AAAD4AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAN8AAAB8AAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AB8V3wAfFd8AJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNI8AAjRu8AI0nxACNI8AAjSPAAI0fwACNI8AAjSfAAI0XuACNI + 8AAjSvEAI0nwACNH7wAjR/AAI0jwACNI8AAjRe4AI0jvACU46QAkPesAJD/rACNE7gAjR+8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0fwpiNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH + 8P8jR+//I0fw/yNH7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH7/8jR/D/I0fv/yNH8P8jRvD/I0bw/yNH + 7/8jRvD/I0fw/yNH7/8jR+//I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH7/8jR+//I0fw/yNH + 7/8jR/D/I0fv/yNH7/8jRu//I0bw/yNH8P8jRu//JEfw/yNH7/8jRu//I0bv/yNJ8f8lMOb/Jhzf/yYe + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYd3/8mIuD/JTXo/yU26f8mJeL/Jh7f/yYe3/8mH+DwJhzfLiYc + 3wAmHN8AJhzfACYc3wAmHN8AJhzfACYc3wAmHN8AJhzfACYc3wAmHN8AJhzfACYc3wAmHN8AJhzfAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAA3AAAAvwAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAM0AAABEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wBSY+cAUmPnACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjSPAAI0bvACNJ8QAjSPAAI0jwACNH8AAjSPAAI0nwACNF + 7gAjSPAAI0rxACNJ8AAjR+8AI0fwACNI8AAjSPAAI0XuACNI7wAlOOkAJD3rACQ/6wAjRO4AI0fvACNG + 7gAkRu4AI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8C4jRvDyI0fv/yNH7/8jR+//I0fv/yNH + 7/8jRu//I0fw/yNH8P8jR+//I0fv/yNH7/8jR/D/I0fw/yNH7/8jR+//I0bv/yNG7/8jR+//I0fw/yNG + 8P8jRu//I0fv/yNH8P8jRu//I0bv/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH7/8jRu//I0bv/yNG + 7/8jRvD/I0bw/yNH7/8jR/D/I0fw/yNH8P8jR/D/I0fv/yRH8P8jR+//I0bw/yNI8P8kRO7/JiLh/yYd + 3/8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHeD/Jinj/yU26f8mNun/Jink/yYd4P8mHuD/Jh/g0SYf + 4AgmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAB1AAAA8QAAAP8AAAD/AAAA/wABAP8BAgD/BQcJ/wwPG/8NEST/DREq/w0RJf8MEB3/BQYJ/wEC + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA9QAAAHIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AIaZ7wAmIcAAJiHAACYhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0jwACNG7wAjSfEAI0jwACNI8AAjR/AAI0jwACNJ + 8AAjRe4AI0jwACNK8QAjSfAAI0fvACNH8AAjSPAAI0jwACNF7gAjSO8AJTjpACQ96wAkP+sAI0TuACNH + 7wAjRu4AJEbuACNJ8QAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0bvjiNH7/8jR+//I0fw/yNH + 8P8jR+//I0fv/yNH8P8jR+//I0fv/yNH7/8jRu//I0bv/yNH7/8jR+//I0fw/yNH8P8jRu//I0bv/yNG + 8P8jRu//I0fv/yNH7/8jRu//I0bv/yNG7/8jR+//I0fv/yNH8P8jR+//I0bv/yNH8P8jRu//I0fw/yNH + 7/8jRu//I0fw/yNH8P8jRu//I0fw/yNH8P8jR+//I0bw/yNH8P8jR/D/I0fv/yNH7/8jSfD/JDbo/yYc + 3/8mHt//Jirk/yYi4f8mHt//Jh7g/yYe4P8mHuD/Jh7g/yUw5v8lNun/Jjbp/yUu5v8mHuD/Jh7g/yYf + 4JMmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + ABEAAACiAAAA/wAAAP8AAQD/BggL/w8SPv8aGnD/HyCo/yIdx/8mIdL/JiLX/ycj2v8mItf/JiLU/yId + xv8fIJr/Fxhk/wwOJ/8BAgD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAApQAAABUAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wDGyPkAJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNI8AAjRu8AI0nxACNI8AAjSPAAI0fwACNI + 8AAjSfAAI0XuACNI8AAjSvEAI0nwACNH7wAjR/AAI0jwACNI8AAjRe4AI0jvACU46QAkPesAJD/rACNE + 7gAjR+8AI0buACRG7gAjSfEAJDzqACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7xwjR+/iI0fv/yNH + 7/8jR+//I0fv/yNH7/8jRu//I0bv/yNH8P8jR/D/I0fw/yNH7/8jR+//I0bw/yNH8P8jR+//I0bv/yNH + 8P8jRu//I0fw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fw/yNH7/8jR/D/I0fw/yNH7/8jR/D/I0bv/yNH + 8P8jR/D/I0fv/yNH8P8jR/D/I0fv/yNG7/8jR/D/I0bv/yNH8P8jR/D/I0fv/yNG8P8jSPD/I0bv/yYl + 4v8mHN//JiLh/yU06f8mIuH/Jh7g/yYf4P8mH+D/Jh3g/yYj4f8lNen/JTXo/yU26P8lL+b/Jh7f/yYe + 3/YlH983JR/fACUf3wAlH98AJR/fACUf3wAlH98AJR/fACUf3wAlH98AJR/fACUf3wAlH98AJR/fACUf + 3wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AB0AAAC9AAAA/wABAP8MDiT/Ght1/yIfxf8nIuj/Jx/1/yYf8v8mHvD/JR3u/yUd7f8lHe3/JRzt/yUd + 7v8mHu//Jh7z/ycg9f8mIdz/HR6U/wsOK/8AAQD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADGAAAAIgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////ACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjSPAAI0bvACNJ8QAjSPAAI0jwACNH + 8AAjSPAAI0nwACNF7gAjSPAAI0rxACNJ8AAjR+8AI0fwACNI8AAjSPAAI0XuACNI7wAlOOkAJD3rACQ/ + 6wAjRO4AI0fvACNG7gAkRu4AI0nxACQ86gAkQe0AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvbSNG + 7/8jR+//I0jw/yNJ8f8jR+//I0fv/yNH7/8jR/D/I0fw/yNH8P8jRu//I0fv/yNG8P8jR/D/I0fv/yNG + 7/8jR/D/I0bv/yNH7/8jR/D/I0bv/yNH8P8jR/D/I0fw/yNH8P8jR/D/I0fw/yNH8P8jR+//I0bv/yNH + 7/8jR/D/I0fw/yNG7/8jRvD/I0fw/yNH7/8jR+//I0fw/yNH7/8jR/D/I0fw/yNH7/8jR/D/I0rx/yQ5 + 6f8mHd7/Jh/f/yUv5v8lNOj/JiHh/yYe3/8mH+D/Jh/g/yYd3/8mK+X/JTbp/yU16P8lNen/Jifj/yYd + 3/8mHuCnJR/fACUf3wAlH98AJR/fACUf3wAlH98AJR/fACUf3wAlH98AJR/fACUf3wAlH98AJR/fACUf + 3wAmH+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AB4AAADMAAAA/wgLFP8bHHv/JiPa/ycf9f8lHvD/JR3r/yUd6P8lHej/JR3n/yUd5/8lHej/JR3n/yUd + 6P8lHef/JR3n/yUd5/8lHuj/JR3s/yce9f8mIt7/Ghtz/wQHBP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + ANEAAAAiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wAmIcAAJiHAACYhwAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0jwACNG7wAjSfEAI0jwACNI + 8AAjR/AAI0jwACNJ8AAjRe4AI0jwACNK8QAjSfAAI0fvACNH8AAjSPAAI0jwACNF7gAjSO8AJTjpACQ9 + 6wAkP+sAI0TuACNH7wAjRu4AJEbuACNJ8QAkPOoAJEHtACRE7gAjRu8AI0fvACNH7wAjR+8AI0fvACNH + 7wcjRu/AI0jv/yRA7f8kOur/I0fv/yNI8P8jR/D/I0fw/yNH8P8jR+//I0fv/yNH7/8jR/D/I0fw/yNH + 8P8jR/D/JEbv/yNG7/8kRu//I0fv/yNH8P8jR/D/I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH + 8P8jR+//I0fv/yNH7/8jR+//I0bv/yNH7/8jR/D/I0fv/yNH8P8jR+//I0fv/yNH7/8jRvD/I0fv/yNI + 8P8lKeP/Jhvf/yYo4/8lN+r/JjLn/yYg4P8mHt//Jh/g/yYd4P8mIuD/JTPo/yU26P8lNun/Ji3m/yYe + 4P8mHuDzJh7gMSYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJh/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + ABwAAADMAAEA/xIVRP8lIsb/Jx/1/yUd7f8lHej/JR3o/yUd5/8lHej/JR3o/yUe5/8lHej/JR3n/yUe + 6P8lHef/JR3n/yUd5/8lHuj/JR3n/yUd6P8lHej/Jh7w/ycf+v8gIKr/CAkk/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAAuAAAAAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJiHAACYhwAAmIcAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNI8AAjRu8AI0nxACNI + 8AAjSPAAI0fwACNI8AAjSfAAI0XuACNI8AAjSvEAI0nwACNH7wAjR/AAI0jwACNI8AAjRe4AI0jvACU4 + 6QAkPesAJD/rACNE7gAjR+8AI0buACRG7gAjSfEAJDzqACRB7QAkRO4AI0bvACNJ8AAjR+8AI0fvACNH + 7wAjR+8AI0bvMiNK8fMlNOj/Jhvf/yUq4/8kQe3/I0nw/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fw/yNH + 7/8jR+//I0fw/yNH8P8jRu//I0bv/yNH7/8jR+//I0fw/yNH7/8jRu//I0fv/yNH8P8jRu//I0bw/yNH + 7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fw/yNH7/8jRu//I0fv/yNH8P8jRu//I0bw/yNJ + 8P8kPer/Jh3f/yYf4P8lM+j/JTfq/yYu5v8mH+D/Jh/g/yYe4P8mIOH/JjDn/yU16f8lNej/JTTo/yYi + 4f8mHuD/Jh7gkiYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmH+AAJh/gACYf4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + ABMAAADABAUA/xkbdf8nIun/Jh3x/yUd6P8lHef/JR3n/yUd6P8lHej/JR3n/yUe6P8lHef/JR3o/yUe + 6P8lHuj/JR7o/yUd6P8lHun/JR3s/yUc8P8mHvT/Jh/p/yQc3v8jHs3/Jia7/x4jbP8GCAD/AQIA/wAA + AP8AAAD/AAAA/wAAAPUAAAAlAAAAAAAAAAAjR/AAI0fwACNH8AAjR+8AI0fvACRH7wAkR+8AJEfvACRH + 7wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ACYhwAAmIcAAJiHAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjSPAAI0bvACNJ + 8QAjSPAAI0jwACNH8AAjSPAAI0nwACNF7gAjSPAAI0rxACNJ8AAjR+8AI0fwACNI8AAjSPAAI0XuACNI + 7wAlOOkAJD3rACQ/6wAjRO4AI0fvACNG7gAkRu4AI0nxACQ86gAkQe0AJETuACNG7wAjSfAAI0nxACNI + 8AAjR+8AI0fvACNG7wAjSfFhJTvq/yYe3/8mHN//JiHg/yU36f8jSPD/I0fv/yNH7/8jR/D/I0fw/yNG + 7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNG7/8jR+//I0fw/yNG8P8jR/D/I0fw/yNG7/8jRvD/I0bv/yNH + 8P8jR+//I0fv/yNH8P8jR/D/I0fv/yNG8P8jR+//I0fw/yNH8P8jRu//I0fv/yNH8P8jR/D/I0bv/yNI + 8P8jR+//JSfi/yYd3/8lLub/JTbp/yU26f8mKeP/Jh3g/yYd4P8mIeH/JjHn/yU26f8lNej/JTbp/yYr + 5f8mHt//Jh/g4yYf3xsmH98AJh/fACYf3wAmH98AJh/fACYf3wAmH98AJh/fACYf3wAmH98AJh/fACYe + 3wAmHt8AJh/gACYf4AAmH+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAYAAACpBAcJ/x4fkf8nH/b/JR3s/yUe5/8lHej/JR7n/yUd6P8lHuj/JR7o/yUd6P8lHuj/JR3n/yUd + 5/8lHun/JR3s/yUc8P8mHu7/JB7f/yQhyv8kJav/HRyS/yIpg/8hJXr/GBZ6/yAhgf8jJJD/ISKT/x4d + gf8WHnH/EiBO/woOGv8CBAm6AAAAACVL/gAjR+8AI0fwACNH8AAjR/AAI0fvACNH7wAkR+8AJEfvACRH + 7wAkR+8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wAmIcAAJiHAACYh + wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0jwACNG + 7wAjSfEAI0jwACNI8AAjR/AAI0jwACNJ8AAjRe4AI0jwBCNK8SEjSfBPI0fvYSNH8F4jSPBFI0jwKyNF + 7hEjSO8IJTjpACQ96wAkP+sAI0TuACNH7wAjRu4AJEbuACNJ8QAkPOoAJEHtACRE7gAjRu8AI0nwACNJ + 8QAjSPAAI0jxACNH7wAjRu8AI0nxACRA7GEmIOD1Jh7g/yYe4P8mHeD/JS7l/yNF7v8jSfH/I0fw/yNI + 8v8jR/H/I0fw/yNH7/8jRu//I0fv/yNH7/8jR+//I0fv/yNH8P8jRu//I0fv/yNH7/8jRu//I0bv/yNH + 8P8jR+//I0bv/yNH7/8jR/D/I0fw/yNH8P8jR/D/I0fw/yNH8P8jR/D/I0fw/yNH7/8jRvD/I0bv/yNG + 7/8jSvD/JDbo/yYd3/8lLeX/JTbp/yU16P8lNOj/JiLg/yYd4P8mHeD/Jibi/yY26f8lNen/JTbp/yYs + 5f8mHuD/Jh7g/yYf4GMmH98AJh/fACYf3wAmH98AJh/fACYf3wAmH98AJh/fACYf3wAmH98AJh/fACYf + 4AAmHt8AJh7fACYf4AAmH+AAJh/gACYf4AAmHt8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAB/BAcK/x8gm/8mH/f/JR3q/yUd6P8lHuj/JR3o/yUd6P8lHef/JR3o/yUe5/8lHej/JR3q/yUd + 7f8mHfH/JR3h/yQhzP8kJKv/IiOV/yIniv8fH5D/JSil/yMfvf8lH83/JyHb/ycf5/8mHun/Jh7p/yce + 6v8nHuz/Jybv/yZF8P8jReD/IEDN1yFC4GUlS/4SI0fvACNH8AAjR/AAI0fwACNH7wAjR+8AJEfvACRH + 7wAkR+8AJEfvAP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJiHAACYh + wAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNI + 8AAjRu8AI0nxACNI8AAjSPAAI0fwACNI8BAjSfBMI0XujyQ968slNOjzJS/l/yUr4/8kKuP/Iyjj/iMq + 4/YjLOXtJDLn1yU46bokPeucJD/rfSNE7mIjR+9FI0buJiRG7g4jSfEFJDzqACRB7QAkRO4AI0bvACNJ + 8AAjSfEAI0jwACNI8QAjQ+4AI0XuACNJ8QAkQOwAJh3fZSYe3/8mH+D/Jh7f/yYc3/8mJ+L/JD/t/yNI + 7f8iP9v/I0Xq/yRJ9P8jSfX/I0jx/yNH8P8jRvD/I0fw/yNH7/8jR/D/I0bv/yNH7/8jR+//I0fv/yNH + 7/8jR/D/I0bv/yNH8P8jR/D/I0fw/yNH8P8jR/D/I0fw/yNG7/8jR/D/I0fv/yNG8P8jR+//I0bv/yNG + 8P8jSfH/Iz/s/yYh4P8mLeX/JTbp/yU16f8lNun/Ji7m/yYe3/8mHuD/Jh3g/yYj4v8lNuj/JTTo/yYn + 4/8mHuD/Jh7g/yYe4LcmHuACJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJh7fACYe3wAmH+AAJh/gACYf4AAmH+AAJh7fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAABLBAYI+x8gnP8nIPb/JB3p/yUe6P8lHej/JR3n/yUd5/8lHef/JR3n/yUe6v8lHO7/JR3s/yQe + 2f8lJL7/IiGf/yMokv8jI5n/JSWz/yQeyv8mH9v/Jx/p/yYd5v8mHuT/Jh/j/yYf4v8mH+D/Jh7f/yUe + 3/8mH+D/Jh/g/yYc3/8lK+b/JEj0/yRJ9v8kSPP/I0fv2SNH72gjR/ACI0fwACNH8AAjR+8AI0fvACRH + 7wAkR+8AJEfvACRH7wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ACYh + wAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjSPAAI0bvACNJ8QAjSPADI0jwSCNH8J0kPuzjJTHm/yYl4f8mHuD/Ixfe/yAU3f8hGN3/JyDf/y0l + 4f8vJ+H/Lyfh/yUc3/8kGt//Jh/f/yYg4P8mIuH/JSjj/yUs5PYlMOfoJTbpyyQ86qwkQe2PJETuciNG + 71MjSfAzI0nxFiNI8AsjSPEBI0PuACNF7gAjSO8AI0rxACYd3wAmH+CDJh7g/yYf4P8mHt//Jh3g/yci + 5v8lNeb/GR6S/xYXgv8cKKn/IDrR/yNG7v8kSfb/I0n0/yNH8f8jRu//I0fv/yNH7/8jR+//I0fw/yNH + 7/8jR+//I0fv/yNH8P8jR/D/I0fw/yNH7/8jR/D/I0bv/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH + 7/8jSfD/I0Lt/yYm4/8lLuX/JTbp/yU16P8lNuj/JjTo/yYj4v8mHd//Jh/g/yYe4P8mH+D/JTDn/yYn + 4/8mHd//Jh7g/yYf4O4mHuApJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJh/gACYe3wAmHt8AJh/gACYf4AAmH+AAJh/gACYe3wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAbAwQA3R0dh/8oH/j/JR3o/yUe5/8kHef/JR7o/yUd6P8lHer/JRzu/yUe7P8kH9f/JCO3/yMm + nf8kJ5n/JCWq/yUgyf8mH9r/Jx/o/yYd5f8mHuL/Jh7h/yYf4P8mH+D/Jh7g/yYe4P8mHuD/Jh7g/yYf + 4P8mHt//Jh7f/yUf3/8mHuD/Jh7f/yQ96/8jSfD/I0bv/yNG7/8jR/D/I0fwuiNH8CkjR/AAI0fvACNH + 7wAkR+8AJEfvACRH7wAkR+8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wAmIcAAJiHAACYhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0jwACNG7wAjSfFQI0XvxiQ26f8lKOP/Jh7f/yYd3/8mHt//Jh/g/zc65P9UWun/bXjv/4GR + 8/+PnPb/laD4/5Sg9/9qdO7/Kifh/yMa3/8mHt//Jh7f/yYc3/8mHN//Jhzf/yYd3/8mH9//Jh/g/yUj + 4f8mKeP/JS7k+iUz5/AkOenXJD/suSND7pYjRe56I0jvWiNK8TgjSfEYJDbpCSYg4J0mHt/8Jxzi/yMb + zP8cFaH/JBzR/yQjz/8ZGo7/Ew9w/xUSef8YHY//Hi20/yI+2v8kSPL/JEn1/yNI8f8jR/D/I0fv/yNH + 7/8jR+//I0bv/yNH7/8jRu//I0bv/yNG7/8jR/D/I0fw/yNH7/8jR/D/I0fw/yNH8P8jR/D/I0fw/yNH + 8P8jSfH/I0Lt/yYk4f8mKOP/JTfp/yU16P8lNej/JTfp/yUo4/8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mHuBkJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJh/gACYf4AAmHt8AJh7fACYf4AAmH+AAJh/gACYf4AAmHt8AJh7fAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAohgbZf8oIPT/JRzp/yUe5/8lHej/JB7q/yYd8P8mHu//JB/Y/yQjtf8jJZ3/Iyab/yQl + sv8lIND/Jh/j/yYe6P8mH+P/Jh7h/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYc3/8lLuX/I0nw/yNH7/8jR/D/I0fw/yNH7/8jR+/iI0fwQiNH + 7wAjR+8AJEfvACRH7wAkR+8AJEfvAP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8AJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvASNI8EEjRu+yJDzq/yUm4v8mHN//Jh3f/yUe3/8mHt//Ixnf/zEz4v+Pn/b/orH6/3WA + 8P9YWuv/Y2/t/3aD8f+Ik/X/kaD2/3SE8P8wL+L/Ixvg/yYe4P8mHuD/Jh/g/yYf4P8mHuD/Jh/f/yYe + 4P8mHd//Jh3f/yYc3/8mHd//Jh7f/yYf4P8mIOD/JSbi/yUs5P8lMef7JDjp8iQ+69IjP+y4JDbp9CUy + 6v8hLcv/Fx+L/xcai/8cGpn/GBaE/xQQcv8UEHT/FA9y/xQPcf8VE3v/GiKa/x82yf8jRuz/JEr3/yNI + 8/8jR/H/I0fw/yNH8P8jR+//I0fw/yNG7/8jR+//I0fv/yNH7/8jR/D/I0fw/yNH7/8jR/D/I0fw/yNI + 8P8jSfD/JD3r/yYj4v8mG9//JS3k/yU26f8lNen/JTfp/yYs5f8mHt//Jh7g/yYf4P8mHuD/Jh7g/yYf + 4P8mHt//Jh/g/yYf4P8mH+CiJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7fACYe3wAmH+AAJh/gACYf4AAmH+AAJh7fACYe3wAAAAAAAAAAAAAA + AAAAAAAAAAAATg8TOv4nI+b/JR3t/yUe6v8mHu//Jh/0/yUg4P8gH63/HyGD/yImif8jI63/JSHS/yYf + 5f8mHeb/Jh7i/yYf4f8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/f/yYf4P8mH+D/Jh/g/yYf4P8mHeD/JiLh/yRD7v8jSO//I0fv/yNH7/8jR/D/I0fw/yNG + 7/MjR+9FI0fvACRH7wAkR+8AJEfvACRH7wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////ACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0jwACNI8AAjSPAAI0jwACNI + 8AAjSPAAI0jwACNI8E4kPev/JSXi/yYd3/8lHd//Jh7f/yYf4P8mH+D/Jh7f/yUd3/8mIeD/UVfp/1lk + 6/8mIeD/HBPe/yAa3/8kHd//KCHg/y8w4v86PeT/LCnh/yUd4P8mH+D/Jh7f/yYf3/8mH+D/Jh7g/yYf + 4P8mHt//JR7f/yYf4P8mH+D/Jh/g/yYf4P8mH9//JR7f/yYd4P8mHd//Jhzf/yYd3/8mHuD/JiDg/yUp + 4/8fJ+P/Hynn/yAx6P8gNeH/IDXR/x81yP8dMb7/Gymm/xgfkP8WGIP/FBJ3/xINbv8TDXD/GBqK/x4u + uP8iQOD/JEfw/yRK9v8kSfb/JEn2/yRJ9v8kSfX/I0n0/yNI8v8jR/D/I0fw/yNH8P8jR/H/I0jx/yNJ + 8v8kQ+7/JTHn/yYf4P8mHd//Jh3f/yYv5v8lN+n/JTbp/yUs5f8mHuD/Jh7f/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYe4P8mH+DPJh/gEyYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe3wAmHt8AJh/gACYf4AAmH+AAJh/gACYe3wAmHt8AAAAAAAAA + AAAAAAAAAAAADgYID9EkIsX/Jh35/yYe9P8mIOj/IR+6/xoccP8XG0r/HB50/yUhv/8mHuT/Jh7o/yYf + 4v8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHt//Jh/g/yYe4P8mHt//Jh/g/yYf + 4P8mHuD/Jh/f/yYe3/8mHt//Jh7g/yYf4P8mHt//Jh/g/yYd3/8kPOv/I0nx/yNG7/8jR+//I0bw/yNH + 8P8jRvD/I0fw6CNH7yokR+8AJEfvACRH7wAkR+8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wAmIcAAJiHAACYhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkQu4AJELuACRC + 7gAkQu4AJELuACRC7gAkQu4mJiDg2yYb3v8mH+D/Jh/g/yYf3/8mH9//Jh/g/yYf4P8mH+D/Jh7g/yAX + 3/8gGN//JR7g/yYf4P8mHt//JR3f/yQd3/8kG+D/Ixnf/yUd3/8mH+D/Jh/g/yYe3/8mHt//Jh/g/yYf + 4P8mHuD/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/x8V + 3v9ESeb/bnTu/1db6v9BReb/MS3k/ygo5f8gJOj/ISfq/yEt6v8hNej/Ijfd/yA0y/8dL7j/HCei/xcb + if8VE3n/FhV9/xkdkP8aJ6j/Hi+5/x8yv/8fMsD/HzLA/yA2yf8iQOD/JEjy/yRK9/8kSvT/I0bt/yNB + 5/8kM+T/JiLh/ycc4v8nHuX/Jx7j/yYg4v8mNOj/JTLn/yYn4/8mHuD/Jh7g/yYe4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+DpJh/gLyYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHt8AJh7fACYf4AAmH+AAJh/gACYf4AAmHt8AJh7fACYf + 4QAAAAAAAAAAAAECAXgdHZP/KCH9/yIfx/8bG4D/ExdH/xMXPP8bG3r/JCDG/ygf7P8mHub/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh7g/yYe4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mHN7/JTLn/yNJ8f8jR+//I0fv/yNG + 8P8jR+//I0fv/yNH7/8jR+/GJEfvCyRH7wAkR+8AJEfvAP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8AJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAkQu4AJELuACRC7gAkQu4AJELuACYc3yomH9+cJh/g8iYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYe + 4P8mH+D/Jh/g/yUf3/8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yQc + 3/84OuT/bXru/52q+f+aqPj/l6T4/46b9v99ivL/bHLu/1NZ6P8+QOT/MCvi/yUh4/8gHuf/ICbp/yEu + 6v8iNun/Ijja/x82yf8dLbD/GiOY/xcahv8WFHr/FA9x/xMOb/8UDnD/FRR8/xkim/8aJ6j/Giaj/xke + k/8YGIv/GROO/xsVlf8aFpn/HRmq/yMbx/8mIt//Jizq/yYg4/8mHd//Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYe3/8mHuD2Jh/gTCYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACNH + 7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7fACYe3wAmH+AAJh/gACYf4AAmH+AAJh7fACYe + 3wAmH+EAAAAAAAAAABsMDyXoICKV/xcaWv8VGkD/GRtr/yIfq/8nIeD/KB/u/yYe5v8mH+D/Jh7f/yYe + 4P8mHuD/Jh7g/yYf4P8mHuD/Jh/f/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mHt//Jh/g/yYf + 4P8mHuD/Jh7g/yYf4P8mH+D/Jh/f/yYe3/8mHuD/Jh/g/yYf4P8mH+D/Jhzf/yUr5P8jSPD/I0fv/yNH + 7/8jR+//I0fv/yNH8P8jR/D/I0fv/yRH8HojRu8AI0bvAP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////ACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACRC7gAkQu4AJELuACRC7gAmHN8AJh/fACYf4DEmH+CQJh/g6SYe4P8mH+D/Jh7g/yYe + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yUf3/8mH+D/Jh/g/yYf + 4P8mHuD/Jh7g/yYe4P8mH+D/Jh7f/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yUd + 4P8jHt//cIDw/56q+f+UoPf/lKD3/5Wh9/+Wovf/mqX4/5yo+f+Zp/j/mKT4/4yZ9f94hfH/aG/t/05V + 5/85OOP/LSni/yIg4/8fIej/ISvr/yE17P8iPOr/IjvY/x83yv8dLrL/GiSc/xcZh/8VEXX/Ewxs/xMM + bP8TDnD/ExBz/xQRdP8UEnX/ExJ0/xQSdP8UE3j/GRaM/yAZsv8lHtj/JyDm/ycf5P8mH+H/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/gaCYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAjRu8AI0nwACNI + 8AAjR+8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe3wAmHt8AJh/gACYf4AAmH+AAJh/gACYe + 3wAmHt8AJh/hACYg3gAICiGOFhlL/xsce/8iIKv/JiDe/ykh7/8mHur/Jh7h/yYe4P8mH+D/Jh/g/yYe + 3/8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7f/yYf4P8mHuD/Jh/g/yYc3/8lJ+L/I0bu/yNH + 8P8jR/D/I0fv/yNH7/8jR+//I0fw/yNH8P8jR/DrI0bvHSNG7wD///8A////AP///wz///8b////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wAmIcAAJiHAACYhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACRC7gAkQu4AJhzfACYf3wAmH+AAJh/gACYf4CQmHuB/Jh/f2iYe + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYe3/8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf + 4P8mH+D/Hxfe/2Rq7f+eq/n/lKD3/5Sg9/+UoPf/laD3/5Sg9/+UoPf/laD3/5Wh9/+Xo/j/m6b4/5yo + +P+ap/j/lqL3/4WV9P91f/D/XWPq/0RH5f80L+L/JyTh/x8f5P8hJ+v/ITHt/yM87/8jQOf/Ij3X/x84 + x/8dLK//GSGW/xcZh/8WE3r/FBBz/xQQdP8UEXX/FBJ3/xMRdf8TEXP/FxSE/x4arv8kHtL/Jh/j/ycg + 5v8mH+L/Jh/g/yYe4P8mH+D/Jh7geSYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACNJ8QAjSPAAI0bvACNJ + 8AAjSPAAI0fvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHt8AJh7fACYf4AAmH+AAJh/gACYf + 4AAmHt8OJh7fNCYf4XAmIN6uJiTP/Scg5v8oIOz/Jx7q/yYe4v8mH+D/Jh/g/yYf4P8lH9//Jh/f/yYf + 4P8mH9//Jh7g/yYe4P8mHuD/Jh7f/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf + 4P8mHt//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh7g/yYf3/8mHeD/JiTh/yNE + 7v8jSPD/I0fw/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fw/yNH74YUOu4A////AP///wD///9l////xf// + /wT///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8AJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJELuACYc3wAmH98AJh/gACYf4AAmH+AAJh7gACUe + 3xUmH+BpJh/gyCYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/yYe4P8mHt//Jh/f/yYf4P8mHt//Jh7f/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf + 4P8mHt//Jh/g/yAX3/9NTuj/k6L3/5uo+P+apvn/mKT4/5ei9/+VoPf/lKD3/5Sg9/+UoPf/lKD3/5Sg + 9/+VoPf/laD3/5ah9/+YpPj/m6f5/5uo+P+apvj/j531/36L8v9qce3/TlTn/zo44/8qJuD/IB/i/yAk + 6P8gLu3/Ijnw/yJA8P8jQ+b/IT7W/x80wf8cKan/GB6S/xcXgv8VEXj/ExB0/xQQcf8TEG//FRJ7/xoX + l/8hG7r/JR7Y/ycf5f8mH+H/Jh/gjiYe4AAmH+AAJh/gACYf4AAjSPAAI0fwACNJ8QAjSfEAI0jwACNG + 7wAjSfAAI0jwACNH7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7fACYe3wAmH+ANJh/gMCYf + 4GImH+CeJh7gziYf4PomH+D/Jh7h/yYe5v8mH+H/Jh7f/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf + 4P8mH9//Jh/f/yYf4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe + 3/8lHt//Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yUf3/8mHuD/Jh7g/yYf4P8mHuD/Jh7f/yYi + 4f8jQ+7/I0jw/yNH7/8jR/D/I0fw/yNG7/8jR+//I0fw/yNH7/8jR+/oFDruHf///wD///8G////w9LS + 08qpqasCqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrAKmp + qwCpqasAqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmHN8AJh/fACYf4AAmH+AAJh/gACYe + 4AAlHt8AJh/gACYe4AcmHuBUJh/gtiYf4PcmH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mHuD/Jh/g/yYe + 4P8mH+D/Jh7g/yYf4P8mHuD/Jh7f/yYe3/8mHuD/Jh/f/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh7f/yYe4P8mH+D/Ixzf/zU25P9UXur/c37w/4WT9P+Rn/b/m6j4/5qn+P+ap/j/mqb4/5ik + +P+Wovf/laD3/5Wg9/+UoPf/lKD3/5Sg9/+UoPf/laD3/5ei9/+apvj/m6j4/5uo+P+Uovf/gpD0/3F5 + 7/9YX+n/QUDk/zMu4v8nJuH/HyHk/x8q6v8gNe//ITzx/yJE8f8iQ+L/IDzS/x4yu/8aJaD/GBuN/xYV + ff8TD27/ExBu/xUSe/8eF6n7JhzeiiYf4AAmHuAAI0fvACNJ8AAjSPAAI0jwACNH8AAjSfEAI0nxACNI + 8AAjRu8AI0nwACNI8AAjR+8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AUJh/gNyYe32ImHt+fJh7gzCYf + 4PcmH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yUf + 4P8lH+D/Jh7f/yYe3/8mHuD/JR7g/yYf4P8mHuD/Jh/g/yYe4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/JR/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8lH9//JR7f/yUe4P8mH+D/Jh/g/yYe + 4P8lI+H/I0Tu/yNI8P8jR/D/I0fw/yNH7/8jR+//I0fv/yNH8P8jR/D/HkPv/zFS8U7///8A////lu3t + 7f8tLS6zqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrAKmp + qwCpqasAqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrAKmpqwAmIcAAJiHAACYhwAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAACQAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmH+AAJh/gACYf + 4AAmHuAAJR7fACYf4AAmHuAAJh7gACYf4AAmH+A8Jh/gnyYf4O0mH+D/Jh7g/yYe4P8mHuD/Jh/g/yYf + 4P8mHt//JR7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/f/yUf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh7f/yUe3/8hGd//Hhbe/yAZ3v8qKeH/Ojnk/0hL5/9YZOv/a3Xu/3yG + 8f+Ek/P/j532/5ml+P+bp/j/mqf4/5qn+P+Yo/j/laH3/5Wg9/+Un/f/lJ/3/5Sg9/+UoPf/laH3/5mk + +P+bp/j/mqj4/5ml+P+Nm/X/f4zy/3J37/9aYen/RUfm/zY04/8mKuL/HiXn/yEy7v8kQPP/JEf0/yRI + 7f8iQt3/HznL/xwrrf8XH5L/GiGcjiYc3gAjS/EAI0fvACNH7wAjSfAAI0jwACNI8AAjR/AAI0nxACNJ + 8QAjSPAAI0bvACNJ8AAjSPAAI0fvACYf4AAmH+AAJh/gACYf4AEmH+BxJh7g1SYe4PomHuD/Jh7g/yYe + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYe3/8mHt//Jh7g/yYe4P8mH+D/Jh7g/yYe4P8mH9//Jh/g/yYf + 4P8mHt//Jh/g/yYe4P8lHuD/Jh/g/yYe4P8mH+D/Jh7g/yYe4P8mH9//Jh/g/yYf4P8lHuD/Jh7g/yYf + 4P8mHuD/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf3/8mHt//Jh7g/yUf3/8lHt//Jh/g/yYf + 4P8mHd//JSXi/yNF7v8jSPD/I0fw/yNG8P8jR+//I0bw/yNH8P8jR+//I0fw/xM57/qOoPda////lf3+ + /v9hYWL/AAAAnwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYhwAAmIcAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAADgAAACFAAAAuAAAAMgAAADDAAAAogAAAFQAAAALAAAAAAAAAAAAAAAAAAAAAAAA + AAAmH+AAJh7gACUe3wAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8sJh7fiyYf4N8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yUe3/8mHuD/Jh/g/yYf3/8mHt//JR/g/yUe4P8lH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh7g/yYf4P8mH+D/JR/f/yUf4P8lHt//Jh7f/yYe4P8lHt//JBrg/yEY3/8fF97/IBje/yAY + 3/8iHd//Kivg/zc24/9DQ+b/UFno/2Vv7f95gvH/iJf0/5el9/+bp/n/m6f4/5ij+P+UoPf/lKD3/5Sg + 9/+Un/f/lJ/3/5Wg9/+VoPf/l6L4/5ql+P+cqPj/mqf4/5qm+P+Qnvb/f4rx/2Np6/86OeP/Hxbe/yMf + 4P8lK+X/JTbs/yU/8v8kRvT/JEr1/yNM9dkjTvO1I0vxgiNH70QjR+8TI0nwACNI8AAjSPAAI0fwACNJ + 8QAjSfEAI0jwACNG7wAjSfAAI0jwACNH7wAmH+AAJh/gACYf4AAmH+ACJh/gwyYe4P8mHuD/Jh7f/yYe + 4P8mH+D/Jh7f/yYf4P8mH+D/Jh7g/yYf4P8mHt//Jh/f/yYf4P8mHuD/Jh/g/yYe3/8mH+D/Jh/g/yUe + 3/8mH+D/Jh7g/yYf4P8lH+D/JR/g/yYe4P8lHt//Jh/g/yYf4P8mHuD/Jh/f/yYf3/8mH+D/JR7g/yUe + 3/8mH+D/Jh/g/yYe3/8mHuD/Jh/f/yYf4P8mHt//JR/g/yYf4P8mHt//JR/g/yUe4P8mH+D/JR/g/yYf + 4P8mH+D/Jhzf/yUp4/8jR/D/I0fv/yNH8P8jRvD/I0fw/yNH8P8jR/D/I0fw/x9C7/8gR/D2xtT90/// + //9+f4D/AAAA/wAAAJMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAJQAAAKMBAQD3BQcL/wECAf8AAAD/AAAA/wAAAP8AAAD/AAAAxwAAAE4AAAAAAAAAAAAA + AAAAAAAAAAAAACYe4AAlHt8AJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYe3wAmH+AcJh/gbiYf + 4MwmH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH9//Jh/f/yUf3/8mHt//Jh7g/yYe4P8mH+D/Jh7f/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yUf3/8mH+D/Jh/g/yYf3/8lH9//Jh/g/yYf4P8mH+D/Jh/g/yYe + 3/8mHuD/JR3g/yQb3/8hGN//IBff/yAX3v8fF97/IRvf/y4t4f9AQeX/WGLr/3eB8f+Mm/X/mqf4/5uo + +P+apfj/lqL4/5Sg9/+VoPf/laD3/5Sf9/+UoPf/laD3/5Wg9/+VoPf/l6L4/5qm+P+cqfj/lKL3/2Js + 7f8tKuD/Ihbe/ycd3/8mHd//JiLh/yUo5P8lMef/JDnp/yRB7P8jR+7/I0nw1SNJ8KcjSPBlI0jwNSNH + 8AojSfEAI0nxACNI8AAjRu8AI0nwACNI8AAjR+8AI0zxACYf4AAmH+AAJh/gACYf4DkmH+DgJR7f/yUf + 3/8mH+H/Jh/i/yYf4f8mH+D/Jh/f/yYf4P8mH9//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe + 4P8mH+D/Jh/f/yYf4P8mHuD/Jh/f/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/JR/g/yYe + 3/8lH9//Jh/g/yYf4P8mH+D/Jh/f/yYe4P8mHt//Jh7f/yUf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYc3v8lMuf/I0nx/yNH8P8jR+//I0fv/yNH8P8jRu//I0fv/yFE7/8YQO//mK/5//// + //+hoaHhAAAA/AAAAP8AAACHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYh + wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAATgAAAOIICh7/ERJX/xUWdP8JCS//AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD7AAAAhgAA + AAYAAAAAAAAAAAAAAAAAAAAAJR7fACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmHt8AJh/gACYf + 4AAmH98NJh7gWSYf4LkmH+D6Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe + 3/8lH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/JR/f/yYe4P8mH+D/Jh/f/yYe + 4P8mH+D/Jh/g/yYf4P8mHt//Jh7g/yYe3/8mHuD/JR7g/yUd4P8jGt//IBfe/x8W3v8hHN//MTDj/0dM + 5/9lbuz/fovy/5Cf9v+ap/j/m6j4/5yo+P+apvj/mqX4/5qm+P+apfj/maT4/5mk+P+Xovj/lqH4/5ij + +P+dqfj/i5j1/zk75P8iGt//Jh7g/yYd3/8mHN//Jh3e/yYc3/8mIeD/JSbi/yUv5f8lOen/JEDs/yNG + 7/UjSfHLI0nxnyNJ8WAjSPA0I0bvCCNJ8AAjSPAAI0fvACNM8QAlPOkAJh/gACYf4AAmH+AAJh/gJyUf + 37YmH+D/Jh/h/yYf2v8mH9//Jx/n/yYf4/8mHt//Jh7f/yYe3/8mHuD/Jh7g/yYf3/8mH+D/Jh/g/yYf + 4P8mH+D/Jh7f/yYe3/8lHt//JR7f/yYe4P8mHuD/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh7g/yUe + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf3/8lH9//Jh7f/yYe4P8mH+D/Jh/f/yYf4P8mHt//Jh7f/yYf + 4P8mH+D/Jh/g/yYe3/8mHt//JD7s/yNI8P8jR/D/I0bv/yNG7/8jR+//I0fw/yJG7/8WO+7/fpn3//// + ///c3NveFhYW4QAAAP8AAAD/AAAAcgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYh + wAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAWgMEBfQPEUD/FRZ6/xYThP8VFHz/Bgkd/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAACbAAAABwAAAAAAAAAAAAAAAAAAAAAAAAAAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh7fACYf + 4AAmH+AAJh/fACYe4AAmH+ACJh/gPiYe4KEmH+DuJh7g/yYe4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Ixnf/x4T + 3v8aEN3/GA7d/x8c3v8vLuL/QUfm/1dh6v9rce7/c3zw/3SD8P9zf/D/eYbx/4KQ8/+DkvP/ipn1/42b + 9f+Qnfb/nKf5/5Sj9/85PeT/Ihnf/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYc3/8mHN//Jhzf/yYg + 4P8lJeH/JS7l/yQ46f8kQO3/I0bv9CNI8MojSfCQI0jwSSNH7wUjTPEAJTzpACQ/7AAmH+AAJh/gACYf + 4AAmHuAFJyDjZh0ZpNoWFIH/GBWI/x8ar/8lHtX/JyDl/ycg5v8nH+X/Jh7i/yYe4f8mH+D/Jh/g/yYf + 3/8mHt//Jh/g/yYf4P8mHuD/Jh/g/yUe3/8mHt//Jh/f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHt//JR/f/yUf + 4P8mHuD/JR7f/yYf4P8mG9//JSzk/yNJ8P8jR/D/I0bv/yNH7/8jRu//I0bv/yNH7/8WPO7/W3z0//f7 + //////71UFBR1gAAAP0AAAD/AAAA/wAAAGYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYh + wAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAWQMGDfUSFFj/FhWC/xUTff8VE3n/FhR+/w8PU/8MECb/AgQA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAJAAAAAAAAAAAAAAAAAAAAAAAAAAACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYe + 3wAmH+AAJh/gACYf3wAmHuAAJh/gACYf4AAmHuAAJh7gKyYe4IYlHt/cJh7f/yUe3/8mH9//Jh/f/yYf + 4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh7g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yUf4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/y4u + 4v85OuT/QUDm/0hH5/9OVun/U13q/1pk6/9aY+v/WGPq/11r6/9XZ+r/SlXo/0BG5f8wNOL/JSPg/y0q + 4f80MuP/NjPj/z465f82M+P/JB3g/yYf4P8mHuD/Jh/g/yYf4P8lHt//Jh7g/yYf4P8mHuD/Jh7f/yYe + 3/8mHeD/Jhzf/yYc3v8mHN//JiDg/yYl4v8lLeX/JDjp/yRB7P8jR++4I0zxJiU86QAkP+wAI0nwACNH + 8AAmH+AAJh7gACcg4wAdFaEZHxmrgRoWk+IUEnL/FhOA/xsYnP8gG7r/Ix3O/yYf2P8mH+H/Jx/k/ycf + 5P8mH+P/Jx/k/ycf5f8nH+X/Jx/m/ycf5/8mH+f/Jx/l/ycf5f8nHuT/Jh/i/yYf4f8mH+D/Jh7g/yYf + 4P8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/f/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh7f/yYf3/8mHN//JiLh/yRD7f8jSPD/I0bv/yNG8P8jRu//I0fw/yRH7/8YPe//Qmby/+nv + /v/////+o6Ok5AAAAPUAAAD/AAAA/wAAAP8AAABfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAmIcAAJiHAACYhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAOQUIE/ASFGT/FhSC/xUTe/8UEnj/FBJ1/yAbx/8oIPb/JyLb/yAfpf8RFT7/AgMA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAAYQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYf4AAmH+AAJh/gACYe + 3wAmHt8AJh/gACYf4AAmH98AJh7gACYf4AAmH+AAJh7gACYe4AAmHuAAJR7fGCYf4GomH+DIJh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH9//Jh7f/yYf + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Ixvf/yws + 4v+BkvP/lqX3/5Wj9/+ap/j/m6f4/5qn+P+apvj/mqb4/5qm+P+Zpfj/maT4/5ql+P+SoPf/g47z/2Ns + 7f8zNOP/GxLe/yEY3/8gGd//Ihnf/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 3/8mH9//Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHd//Jhzf/yYc3/8mIOD/JiXi/yU16N8lPOlAJD/sACNJ + 8AAjR/AAI0fwACNH8AAjSfEAHRWhAB8ZqwAnH+McIBiwmRQSdv8UEnT/FBJ1/xUUff8XFIX/FxWM/xgW + j/8bF5j/Gxia/x0Zof8dGaT/Hhqs/x8asf8fGrf/IBu//yIdxP8jHcn/JB7P/yUf1v8mH+D/JyDk/ycg + 5v8mH+H/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh7f/yYe3/8mH+D/Jh/g/yYf + 3/8mH+D/JR/f/yYf3/8mHuD/Jh7f/yM86/8jSfD/I0fv/yNH7/8jR/D/I0fw/yRH8P8dQO//LFLx/8/c + /f//////5ubn/R0dHvcAAAD/AAAA/wAAAP8AAAD/AAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAHgcJF9sUFW3/FROB/xUTe/8UEnf/FBJ1/x0ZrP8lHuv/JR3q/yUd7f8mHvX/JyLq/x4f + i/8HCRH/AAAA/wAAAP8AAAD/AAAA/wAAAOYAAAAhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh/gACYf + 4AAmHt8AJh7fACYf4AAmH+AAJh/fACYe4AAmH+AAJh/gACYe4AAmHuAAJh7gACUe3wAmH+AAJh/gCSYe + 304mH+CwJh/g+yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yYe + 3/8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/JR/f/yUf3/8mHt//Jh/f/yYe3/8mHuD/Jh/g/yYe + 4P8iHd//TVPo/4+d9f+eq/n/l6P4/5Sg9/+VoPf/laD3/5Sg9/+UoPf/lKD3/5Wg9/+UoPf/lqH3/5ik + +P+cqfn/kJ72/1BY6f8eGN//JR3f/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/f/yYf3/8mH+D/Jh7f/yYd3/8mHd//JiPh6SQ/ + 7HEjSfBhI0fwTiNH8EojR/A4I0nxMSNK9CMjR+4VJD/vCSQn0wMXE399FBF0/BQQdf8UEHP/FA9w/xMP + b/8SD27/Eg5t/xMPbf8TEG7/ExBv/xIQcP8TEHH/ExFy/xQSc/8VE3f/FRN7/xYUgP8XFIT/FxWJ/xsY + mf8gG7v/Jh/h/yYf4/8mH+D/Jh7g/yYf4P8mH+D/Jh7f/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHt//Jh7g/yYf4P8lHt//Jh3f/yU46P8jSfD/I0fw/yNG7/8jR+//I0fw/yNH8P8gRO//G0Pv/7HF + +////////////25ub/8AAAD/AAAA/wAAAP8AAAD/AAAA/AAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAABAYIE7ITFGz/FRKA/xUTe/8UEnX/FhR+/yAavf8mHuz/JR7p/yUe6P8lHuj/JR7n/yUd + 7P8nH/b/IyK3/wsOIf8AAAD/AAAA/wAAAP8AAAD/AAAAhgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAmH+AAJh7fACYe3wAmH+AAJh/gACYf3wAmHuAAJh/gACYf4AAmHuAAJh7gACYe4AAlHt8AJh/gACYf + 4AAmHt8AJh/gACYe4DcmHuCXJh/g6iYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/JB3g/yUd + 4P8lHuD/JR7g/yYe4P8mHuD/Jh7g/yYe4P8mHuD/Jh7g/yYe3/8mH9//Jh/g/yUf3/8lHt//Jh/g/yYe + 3/8mHuD/JR3g/x4V3v8uLOP/Y23t/5Cd9f+bp/j/lqH3/5Sg9/+UoPf/laD3/5Wg9/+VoPf/lKD3/5Wg + 9/+UoPf/lJ/3/5ij+P+Zpvj/U1vp/x4X3v8lHt//Jh/g/yYf4P8mH9//Jh/g/yYe4P8mHuD/Jh7g/yYe + 4P8mHuD/Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYb + 3/8lLOX/I0nw/yNH8P8jR/D+I0bw9CNG7/AjR/DoI0jw4CNJ8dwjSPDTIT/ezR80w/gfMbz/HzG+/x4t + tf8cLLH/HCqt/xkmpP8aJaT/GiGZ/xofl/8ZG4//GBmK/xcWg/8WE37/FRF3/xURdv8UEHP/ExBy/xQS + dv8UE3X/FBN0/xsXmf8mH9v/Jx/l/yYf4f8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf + 4P8mH+D/Jh/g/yUe3/8mHuD/Jh3f/yQz5/8jSfH/I0fw/yNH7/8jRu//I0bv/yNH7/8jRu//GD3u/42m + +P///////////9TU1f8JCQn/AAAA/wAAAP8AAAD/AAAA/wAAAPAAAAAyAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAQEAAAMFCHcPEF3/GBiE/xgZf/8UEnP/FxWI/yIc0P8mHu//JR7p/yUd6P8lHef/JR7n/yUd + 5/8lHuf/JR3o/yYe9v8kIsf/DA8h/wAAAP8AAAD/AAAA/wAAAOAAAAAaAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAI0bvACNH7wAjRu8AI0fvACNG8AAmH98AJh7gACYf4AAmH+AAJh7gACYe4AAmHuAAJR7fACYf + 4AAmH+AAJh7fACYf4AAmHuAAJh7gACYe4CMmHuB7Jh/g1iYf4P8mH+D/Jh7g/yYf4P8mH+D/JyDg/ycf + 4P8lHuD/Ixzg/yAb4f8lH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf + 3/8mH+D/Ihvh/yIc4P8mHuD/Kize/yEf3v8zM+L/aHTt/5ak+P+Wovf/lKD3/5Sg9/+Un/f/lJ/3/5Wg + 9/+UoPf/lKD3/5Wg9/+UoPf/l6P4/5ak9/9ESuf/HhXe/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf + 3/8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mHt//Jh/g/yYf4P8mH+D/Jh/g/yUf + 3/8mHN//JSXh/yNH7/8jR/D/I0fv/yNH7/8jR/D/I0fw/yNH8P8jRu//I0fv/yRJ9P8kSvX/JEr2/yRJ + 9f8kSvb/JEn1/yRJ9v8kSvf/JEr2/yRH8P8kRu//I0Tq/yND6P8iQeT/Ij/g/yE62f8hNcz/Hy69/xoh + m/8VEnf/FBF2/xUTev8UEnT/GBaN/yQdzf8nIOX/Jx/m/ycg5f8nIOb/Jx/n/ycf5v8mH+T/Jh/h/yYe + 4P8mHuD/Jh/g/yYf4P8mHt//Jhve/yUw5v8jSfD/I0fv/yNH7/8jR+//I0fv/yNH7/8kR+//GD3v/1R2 + 9P/7/v////////////9jY2T/AAAA/wAAAP8AAAD/AAAA/wAAAP8aGhrngoOEI4KDhACCg4QAgoOEAIKD + hACCg4QAgoOEAIKDhACCg4QAgoOEAIKDhACCg4QAgoOEAIKDhACCg4QAgoOEAIKDhACCg4QAgoOEAIKD + hACCg4QAgoOEAIKDhACCg4QAJiHAACYhwAAmIcAAJiHAACYgwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAEBAEALDEP0ISGN/zg/nv8XF3f/GRWR/yQd3f8lHu7/JR3o/yUd5/8lHuj/JR7o/yUd + 5/8lHej/JR3n/yUd6P8lHef/Jx72/yMjtf8GCRD/AAAA/wAAAP8AAAD/AAAASwAAAAAAAAAAAAAAAAAA + AAAAAAAAI0bvACNG7wAjR+8AI0bvACNH7wAjRvAAI0fwACNH7wAjR+8AI0fwACNG7wAjR+8AI0fvACNH + 7wAjRu8AJh/gACYe3wAjR+8AJh7gACYe4AAmHuAAJh7gACYf4AsmG99XJhresCYc3+8mHd//Ihjh/z9E + 2/+tr8j/p6nK/5iezP+TnM7/TlrZ/yEZ4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7f/yYe + 3/8lHt//Ihvh/3F61P+co8v/rq3I/7i+xv9VXNj/GxHg/yAZ3/9RWun/l6T3/5ei9/+UoPf/lKD3/5Sg + 9/+VoPf/lKD3/5Sg9/+VoPf/lKD3/5Sg9/+ZpPj/jJr1/zg65P8eFd7/Jh7f/yYf3/8mHuD/Jh/g/yYf + 4P8mHt//Jh7g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh7f/yYf4P8mHuD/Jh/g/yYf + 4P8mHd//JiDg/yQ76v8jSPD/I0fv/yNH7/8jR+//I0bv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH + 8P8jR+//I0bv/yNH8P8jR+//I0fv/yNH7/8jR/H/I0fw/yNH8f8jSPL/I0jz/yRI8/8kSfP/I0n1/yRK + 9v8kSfP/HzjL/xgai/8VEHX/FBN5/xMSdP8WFHz/Gxic/x4Zq/8eGan/Hhms/x8Ztv8hG7//JB3N/yUf + 3f8nH+T/Jx/l/yYf4f8mHt//Jh3f/yUu5v8kR/D/JEfw/yNG7/8jR/D/I0bv/yNH8P8jR+//Ikbv/xtA + 7v+6yvv////////////d3d7/DAwM/wAAAP8AAAD/AAAA/wAAAP8AAAD/R0dJ3uHh4RPh4eEA4eHhAOHh + 4QDh4eEA4eHhAOHh4QDh4eEA4eHhAOHh4QDh4eEA4eHhAOHh4QDh4eEA4eHhAOHh4QDh4eEA4eHhAOHh + 4QDh4eEA4eHhAOHh4QDh4eEA4eHhACYhwAAmIcAAJiHAACYhwAAmIMAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAABcFBh7WHh6C/15lwP8iI4L/FxOY/yUe4/8lHuz/JR7n/yUe6P8lHef/JR3o/yUe + 6P8lHuj/JR3o/yUd5/8lHef/JR3n/yUd6P8nIPj/Ghx6/wAAAP8AAAD/AAAA/wAAAHAAAAAAAAAAAAAA + AAAAAAAAI0bvACNG7wAjRu8AI0fvACNG7wAjR+8AI0bwACNH8AAjR+8AI0fvACNH8AAjRu8AI0fvACNH + 7wAjR+8AI0bvACNH8AAjR/AAI0fvACNG7wAjR+8AI0jwACNI8AAkPOsKJTPnISUv5l0kNejCJS3l/yMc + 4P8xLdz/vb7F/8zMw//Pz8L/qrDJ/zIy3v8iGuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 3/8mH9//JR7g/x4X4f+Kk8//09LB/8nJw//S0sH/fH7R/xwW4v8kHOD/Hxnf/1BZ6f+XpPf/mKP3/5Sg + 9/+UoPf/lKD3/5Sg9/+UoPf/lJ/3/5Sg9/+UoPf/lJ/3/5ql+P+Jl/T/PD/k/x0W3v8gF97/Ixrf/yQc + 3/8lHeD/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mHt//Jh/f/yYf4P8lHt//Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mHN//JSPh/yM+7P8jSfD/I0bv/yNH8P8jR/D/I0bv/yNH7/8jR/D/I0bv/yNH7/8jR/D/I0fv/yNH + 7/8jR+//I0fw/yNG7/8jR/D/I0fv/yNH7/8jR/D/I0fw/yNH7/8jRu//I0fw/yNH7/8kRu//JEfv/yNH + 7/8jR+//I0fw/yRM+/8kRev/Gx+Y/xcUgf8ZFo3/FxWC/xQSdv8UEnX/FBJ1/xQSdf8UEnP/FRN3/xYU + fv8XFYj/HBie/yIcv/8mHuD/Jh7j/yUz6P8jSPD/I0jw/yNG8P8jRu//I0fw/yNH7/8jR/D/I0fv/xw/ + 7v9CZfL/9fn/////////////eXl6/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/2BgYdY4ODkJODg5ADg4 + OQA4ODkAODg5ADg4OQA4ODkAODg5ADg4OQA4ODkAODg5ADg4OQA4ODkAODg5ADg4OQA4ODkAODg5ADg4 + OQA4ODkAODg5ADg4OQA4ODkAODg5ADg4OQAmIcAAJiHAACYhwAAmIcAAJiDAACYgwAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAABAgSXEBJg/2xxz/88P5n/ExCQ/yUe5f8lHuz/JR3n/yUd5/8lHej/JR3o/yUd + 6P8lHej/JR7o/yUe6P8lHej/JR3o/yUd6P8lHej/JR3v/yYi2f8JCyD/AAAA/wAAAP8AAACMAAAAAAAA + AAAAAAAAAAAAACNG7wAjRu8AI0bvACNH7wAjRu8AI0fvACNG8AAjR/AAI0fvACNH7wAjR/AAI0bvACNH + 7wAjR+8AI0fvACNG7wAjR/AAI0fwACNH7wsjRu8xI0fvYyNI8IsjSPC5I0jw5CNK8fQjSfH/I0nw/yNI + 8P8iPu3/JTPm/56lzP/KycP/zMvD/4GJ0P8aFeL/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8bFOL/X2PW/83Mwv/ExMT/zcvC/5Wezf8jIeH/JR3g/yUd4P8gGd7/UFnp/5Si + 9/+cp/j/mKP4/5ik+P+YpPj/maX4/5ml+P+Xo/j/lqH3/5Wg9/+UoPf/maT4/5Cd9v9kbO3/Q0bm/zQ3 + 4/8tK+H/JyHg/yEc3/8hGN//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYe4P8mH+D/Jh/g/yYe + 4P8mG9//JSrj/yND7f8jSfD/I0fv/yNH7/8jR/D/I0fw/yNG7/8jR/D/I0fw/yNH7/8jR/D/I0bw/yNH + 7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNH8P8jRvD/JEbw/yNH + 8P8jR/D/I0nz/yRK9f8iQN//HjC7/xoflf8WFID/FhR//xURev8UEHT/FA9y/xQPcv8UEHP/Ew90/xQQ + df8UEnb/FBJ3/xQSdf8UEnT/Hhqt/yU04f8jR+7/JEn0/yRK9v8jSPP/I0fw/yNH7/8jRu//I0fv/yNH + 7/8VOu7/epT2////////////7O3t/xwcHP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP+oqKnS7+/vB+/v + 7wDv7+8A7+/vAO/v7wDv7+8A7+/vAO/v7wDv7+8A7+/vAO/v7wDv7+8A7+/vAO/v7wDv7+8A7+/vAO/v + 7wDv7+8A7+/vAO/v7wDv7+8A7+/vAO/v7wDv7+8AJiHAACYhwAAmIcAAJiHAACYgwAAmIMAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAABLAwUv/F1hwv9tcsX/EhCH/yMc3v8lHuv/JR3n/yUd5/8lHen/JR3o/yUe + 6P8lHej/JR3n/yUd5/8lHej/JR3n/yUd6P8lHej/JR3o/yUd6P8nH/b/GBpt/wAAAP8AAAD/AAAAmAAA + AAAAAAAAAAAAACNH8AAjRu8AI0bvACNG7wAjR+8AI0bvACNH7wAjRvAAI0fwACNH7wAjR+8AI0fwACNG + 7wAjR+8AI0fvCCNH7yMjRu9YI0fwhCNH8LQjR+/iI0fv9yNG7/8jR+//I0fv/yNH8P8jR/D/I0bw/yNG + 7/8jR/D/Ikfw/xxE8v+EltX/zsvB/8fHxP+4vMb/ODrc/yEX4f8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yUe + 3/8mHuD/Jh/g/yUf3/8mH+D/Hxbg/0FD3P+9w8X/x8bD/8fHw/+1ucb/MS3e/yIb4f8mH+D/Jh3f/yEZ + 3/9AQ+X/cX3w/4GP8/+BjvL/gY7y/3aE8f92hfD/g5Dz/5Gd9v+Ypfj/mqX4/5Wg9/+Woff/mqb4/5Wj + 9/+OnPb/jJj1/4OP8/9seO//QUXm/yId3/8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe + 4P8mHd//JTDm/yNG7/8jSPD/I0fw/yNH7/8jRu//I0fv/yNH8P8jRu//I0bv/yNH8P8jR/D/I0fw/yNG + 7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNG7/8jR+//I0fw/yNH8P8jR/D/I0fv/yNH8P8jR/D/I0fw/yNG + 8P8jR/H/JEr2/yI+3P8bJ6X/FhV//xMOcf8TD3H/FBF2/xcXgf8aIJb/HCem/xwssf8bLLP/Gymq/xwk + n/8ZH5b/FxmH/xUTev8UEHT/FBF2/xUTev8WGIL/GR2P/xsnpf8eMb//Ij/b/yNH8P8kSvb/I0jz/yNH + 8P8iRu//GT/v/6zA+////////////52dnf8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8ODg7/0dHS0v// + /wf///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////ACYhwAAmIcAAJiHAACYhwAAmIMAAJiDAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAALAAAHyTA0if+WnPD/KyyR/x0Wz/8mHu7/JR7o/yUe6/8lHuv/JR3m/yYd + 6v8lHer/JR3o/yUd5/8lHef/JR3o/yUd6P8lHuj/JR7n/yUd5/8lHef/Jh70/yAesv8DBAL/AAAA/wAA + AKgAAAAAAAAAACNG7wAjR/AAI0bvACNG7wAjRu8AI0fvACNG7wAjR+8AI0bwACNH8AAjR+8AI0fvECNH + 8D8jRu9xI0fvoCNH79UjR+/1I0fv/yNH7/8jRu//I0fw/yNH8P8jR+//I0fw/yNG7/8jR+//I0fv/yNG + 7/8jR/D/I0bv/yNG7/8ZP/L/a4Dc/8zLwv/ExMT/zczD/32E0v8bF+L/Jhzf/yYe4P8mH+D/Jh/g/yYf + 4P8lHt//Jh7g/yYe4P8lH9//Jh/g/yQb3/8nJuD/rK/I/8nJw//FxcT/xsfE/0dN2/8gGOH/Jh7f/yYf + 4P8mHuD/Ihnf/yMd3/8pJOH/KSTg/ykk3/8mIOD/JiHg/yoj4f80NOP/TFLo/3R/8f+Wo/f/mKT4/5Sg + 9/+VoPf/lqL3/5ei9/+Yo/j/nKf4/5Sk9/9FSeb/Ihnf/yYe4P8mH+D/Jh7f/yYf4P8mHuD/Jh/g/yYd + 4P8mIOD/JDjo/yNJ8P8jR/D/I0bw/yNG8P8jRu//I0bv/yNH7/8jRu//I0fv/yNH7/8jR/D/I0fw/yNG + 7/8jRu//I0fw/yNG7/8jR/D/I0fw/yNH8P8jRu//I0bv/yNH8P8jR/D/I0fw/yNH7/8jRvD/I0fv/yNH + 7/8kSfX/I0bs/x0ss/8WFX7/Ew5x/xQPcf8WFoH/GyWj/x82x/8iQuT/I0bv/yRI8/8kSvf/JEr4/yRJ + 9f8kSPH/I0bv/yJE6f8hO9P/HCuv/xgcjf8VEnn/FA9y/xMPc/8TD3P/FBB1/xUUff8ZHpP/HS+5/yJB + 4P8kSfT/IUb0/yhN8f/e5v3///////7///9BQUL/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/LCws//n5 + +dP///8H////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wAmIcAAJiHAACYhwAAmIcAAJiDAACYg + wAAmIMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAagkMQP+AhOH/Zmu9/xYRq/8mHe7/JR3n/yYd7P8jHdr/HBmm/xoW + kv8cGKH/Ix3Z/yYe6/8kHef/JB3n/yUe6P8lHef/JR7o/yUe6P8lHef/JR3n/yUd7/8mI9H/Cg8Z/wAA + AP8AAACsAAAAACZM/QAjRu8AI0fwACNG7wAjRu8AI0bvACNH7wAjRu8EI0fvGyNG8E4jR/CDI0fvtiNG + 7+cjR/D+I0fv/yNG7/8jRu//I0fw/yNG7/8jR+//I0bv/yNH8P8jR/D/I0fw/yNG7/8jRu//I0fv/yNH + 8P8jRu//I0fv/yNH8P8jR+//HEHx/09v5P/Fx8T/xcTD/8jGw/+7wMf/OFLn/x8p5/8mIeD/Jh3f/yYe + 4P8mHt//Jh/g/yYf4P8lHt//Jh7g/yYf4P8mHuD/Hhfh/4ePz//My8L/xMTE/8vNw/9pbNT/HRTh/yYe + 4P8mH+D/Jh/g/yYe4P8lHd//JB3f/yUd4P8lHeD/JR3g/yUd4P8kHeD/Ixrf/x8X3v8hHN//Qkfm/4iV + 9P+ZpPj/lKD3/5Sg9/+UoPf/lJ/3/5Sg9/+erPn/X2Xs/x8W3/8mHuD/Jh/g/yYe3/8lHt//Jh7g/yYc + 3/8mJuL/JD/s/yNJ8P8jR/D/I0fv/yNH8P8jRu//I0fv/yNH7/8jR/D/I0bv/yNH7/8jR+//I0fw/yNG + 7/8jRu//I0fv/yNH8P8jRu//I0fv/yNH8P8jR+//I0bw/yNH7/8jRu//I0fv/yNH7/8jRu//I0bv/yNH + 8P8kSvb/IT3Y/xkekf8TD3D/FBFz/xcZif8eLrf/IkHg/yRJ9P8kSvb/I0jy/yNH8P8jR/D/I0fw/yNH + 7/8jR/D/I0fw/yRH8P8kSPH/I0n0/yRJ9f8jRez/ITvV/x0ttf8YHpH/FhR8/xQPc/8UEHT/FBB1/xQQ + dP8WFH//GyWj/xg01f9JbfT/9fn+///////T09T/CQkK/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/01O + T//////B////A////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJiHAACYhwAAmIcAAJiHAACYg + wAAmIMAAJiDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAEQAACdU4PJP/lZrt/yQkj/8fGNn/Jh7r/yUd6/8kHd7/GBaL/xMS + cv8UEnX/ExJx/xkWlP8kHeb/JR7o/yUd5/8lHuj/JR7o/yUd5/8lHef/JR3o/yUd6P8lHer/JyHq/wsL + Of8AAAD/AAAAnwAAAAAmTP0AI0bvACNH8AAjRu8FI0bvIyNG71UjR++OI0bvxCNH7/IjRvD/I0fv/yNH + 7/8jRu//I0bv/yNH8P8jRvD/I0fv/yNH8P8jRvD/I0fv/yRH8P8jRu//I0fv/yNH8P8jR+//I0fv/yNH + 7/8jR+//I0fw/yNH8P8jR/D/I0bv/x9E8P81V+r/vr/F/8bGw//ExMT/zcvB/36T2P8YQfL/I0Lt/yUz + 6P8mJOL/JRzf/yYd3v8mHt//Jh/g/yYf4P8mHuD/Jh/g/xoU4f9jZdX/zc3C/8TExP/OzcL/iI/P/x4b + 4f8lHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8lH9//JR3f/xwT + 3f89QeT/k6D3/5ei+P+Un/f/lKD3/5Wg9/+apvj/jZv2/zc45P8jG9//Jh7f/yYf4P8mH+D/Jh3f/yYd + 3/8lLeX/I0Xu/yNJ8P8jR/D/I0bw/yNH7/8jRu//JEbw/yNH8P8jR+//I0fw/yNH7/8jRu//I0bv/yNH + 7/8jRu//I0fw/yNH8P8jR+//I0fw/yNH7/8jR/D/I0fw/yNG7/8jR+//I0bv/yNH8P8jR/D/I0bv/yNH + 8P8kSvb/IDXI/xYVf/8TD2//FxOB/x8htf8jROj/JEr3/yNI8/8jR/D/I0fv/yNH8P8jRu//I0bv/yNH + 7/8jR+//I0fw/yNH7/8jR+//I0fv/yNG8P8jR/D/I0jx/yNJ9P8kSfb/JEfv/yE92P8dLbX/GR6T/xYU + ff8UEHX/FBF2/xQQdP8GBXX/bHO/////////////oqKi/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP+Ghob/////t////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ACYhwAAmIcAAJiHAACYh + wAAmIMAAJiDAACYgwAAlIMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF8FBzf/cHXU/1tftP8QDZT/Jh7s/yUd6P8mHu3/Gxik/xQT + c/8VE3v/FBN6/xQSef8UEnf/IRvH/yYe7P8lHef/JR3n/yUd5/8lHef/JR7o/yUe5/8lHej/JR3o/ycf + 9f8RD07/AAAA/wEDCYInTf8AJkz9HiNG71MjR/CNI0fvxCNH7/UjRu//I0fv/yNH8P8jR+//I0fw/yNH + 8P8jR/D/I0fv/yNG7/8jR/D/I0fw/yNH8P8jR+//I0fw/yNH7/8kRvD/I0bv/yNH7/8jR+//I0fv/yNH + 7/8jR/D/I0fv/yNH7/8jRvD/I0fw/yNG7/8hRO//KEvu/6myy//Jx8L/xMTE/8nIw/+5vcf/Mlbr/x5E + 8f8jSfD/JEXu/yQ36f8lJ+P/Jh7f/yYc3/8mHt//Jh/g/yYf4P8eFeH/RUfb/8DFxf/GxcT/ysnD/6Wt + yv8qJd//JBzg/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH9//Jh/g/yYe4P8mH9//JR7f/yYe + 4P8mHuD/HhXe/1Ze6v+bqPj/mKT4/5mk+P+cqPj/hZPz/z9D5f8hGd//Jh/g/yYf4P8mHt//JR3e/yYf + 4P8lNuj/I0jw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fw/yNH8P8jR/D/I0fv/yNH7/8jRu//I0bv/yNH + 7/8jR+//I0fv/yNH8P8jR/D/I0fw/yNH8P8jR+//I0bv/yNH8P8jR+//I0fw/yNH8P8jR+//I0fv/yNH + 8P8jSff/HjPB/xURd/8TEHD/GheT/yUd0v8nH+f/JTPp/yNI8P8jR/D/I0bv/yNH7/8jR/D/I0bv/yNH + 8P8jR/D/I0bv/yNH8P8jR+//I0fw/yNH7/8jRu//I0fv/yNH8P8jR/D/I0fw/yNH8f8jSPT/JEr2/yNH + 7/8iPtv/Hi+4/xcZhv8UEHT/BQNv/36Etv///////////3x9fv8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8BAQH/srKy/////7H///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wAmIcAAJiHAACYh + wAAmIcAAJiDAACYgwAAmIMAAJSDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIBAga5Ghxv/3+E3/8nKIX/GRSs/yYe7/8mHuz/IhzU/xUT + fP8VE3n/EhB5/xQSev8VE3r/FBJ2/yAavv8mHu7/JR3n/yUd5/8lHej/JR7n/yUe6P8lHuf/JR3o/yUd + 6P8nH/X/FhVQ/wIDAP0YK3+mJ03/pyNH7+8jR+//I0fw/yNH7/8jR/D/I0bv/yNH7/8jR/D/I0fv/yNH + 7/8jR+//I0fw/yNH8P8jR/D/I0bv/yNH7/8jR/D/I0fv/yNH7/8jR+//I0bw/yNH7/8jR+//I0bv/yNH + 7/8jRu//I0bv/yNH7/8jR+//I0fv/yNG7/8jR+//IkXw/x9G8f+MndP/z8vB/6u1y/+8v8f/zszB/3aL + 2f8YPvL/I0fv/yNH8P8jSfD/I0fv/yQ96/8lLOX/Jh/g/yYc3/8mHt//JBrg/ykp4P+ussj/ycjD/8XF + xP/BwsT/Ojvc/yIZ4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/f/yYf4P8mHuD/Jh/g/yYf + 3/8mH9//Jh/g/yQc3/8hHN//WGHq/4iS9P9+ivL/WmPr/ywr4f8gGN//Jh7g/yYe4P8mHuD/Jhzf/yUm + 4v8kP+z/I0nw/yNH7/8jRvD/I0fw/yNG7/8jR+//I0fw/yNH8P8jRu//I0bv/yNH7/8jR+//I0bv/yNH + 7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH8P8jR+//I0fw/yNG7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH + 8P8kSvb/HjPB/xURdv8UEXL/Gxif/yYf3/8nH+X/Jh7f/yYc3/8lNef/I0jw/yNH8P8jR+//I0fv/yNH + 7/8jR/D/I0fw/yNH8P8jR+//I0bv/yNH8P8jR+//I0bv/yNG7/8jR/D/I0fv/yNH8P8jR+//I0fw/yNH + 7/8kR/H/I0j0/yRL+P8jQ+b/HCqt/wcGc/98gbP//////+Dl9v8mKln/AAAC/wAAAP8AAAD/AAAA/wAA + AP8AAAD/FRUW/+Dg4f////+U////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJiHAACYh + wAAmIcAAJiHAACYgwAAmIMAAJiDAACUgwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1AwUh9jQ1mv9lasX/Dw1x/yAavv8mHu7/Jh7t/xwY + ov8TEnP/FhR7/y41jf8aG3//FBJ5/xUTfP8iHNH/Jh7s/yUe6P8lHuf/JR3n/yUd5/8lHef/JR7o/yUd + 6P8lHOj/Jhzt/yErmf8fO6n+Jkr2/yNH8f8jR+//I0fv/yNG7/8jR+//I0fw/yNH8P8jRu//I0fv/yNH + 7/8jR/D/I0fv/yNH7/8jRvD/I0bv/yNH7/8jRu//I0fv/yNH8P8jR/D/I0fv/yNH8P8jR+//I0fv/yNH + 7/8jR+//I0fv/yNG7/8jR/D/I0fv/yNH7/8jRvD/I0fw/yNH8P8ZQPL/dYnZ/9LOwP+lsM3/kaLS/9HN + wf+3vMf/MVXq/x5C8f8jR/D/I0fw/yNH7/8jSPH/I0jw/yRA7P8lMOX/JiLh/yUc3/8fF+H/jJPP/8vL + wv/FxcT/x8rE/1Vc2P8fFuH/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh7f/yYf3/8mH+D/JR3f/yEa3v8mH+D/JB7f/yAY3/8jGt//Jh7g/yYd3/8mHuD/JiTh/yU1 + 6P8jRe//I0nw/yNH8P8jR+//I0bv/yNH8P8jR/D/I0bv/yNG7/8jRu//I0bv/yNH8P8jR+//I0bv/yNG + 7/8jR+//I0fv/yNH7/8jRu//I0fw/yNH8P8jR/D/I0fw/yNH8P8jR/D/I0fw/yNH7/8jR/D/I0fv/yNH + 7/8kSfb/IDfL/xUSd/8TEHD/HBmh/ycg5P8mH+P/Jh7g/yYf4P8mHuD/Jh3f/yQ46f8jSPD/I0fw/yNH + 7/8jR/D/I0fv/yNH8P8jR+//I0fv/yNH7/8jR+//I0bv/yNH7/8jR+//I0fw/yNH8P8jRu//I0fv/yNH + 7/8jR+//I0fw/yNG7/8jR+//I0j0/yRK+P8QKsr/e4DJ/9vc2/82O1H/AAAK/wAAAf8AAAD/AAAA/wAA + AP8AAAD/AAAA/z09Pv/+/v7/////dv///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ACYh + wAAmIcAAJiHAACYhwAAmIMAAJiDAACYgwAAlIMAAJiHAADQzxwAfF7wAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQcJRP9GR63/Sk+r/w0Kb/8hG8T/Jh/x/yMd + 2P8WFH//FBJ4/xQRef8jJYT/HB+A/xMRdf8ZFZH/JR7o/yUe6f8lHej/JR7o/yQe6P8kHef/JR7o/yUd + 5/8lGuf/JSLo/yQ47P8lSfb/JEr7/yNH8f8jR+//I0fw/yNH8P8jR/D/I0fv/yNH8P8jR/D/I0bv/yNG + 7/8jR+//I0bw/yNH7/8jRu//I0fv/yNH7/8jRu//I0bv/yNH8P8jR+//I0fw/yNH7/8jR+//I0fv/yNG + 7/8jR+//I0fv/yNH7/8jR+//I0fw/yNG7/8jRu//I0fw/yNH8P8jR+//G0Dx/1p14f/LysL/trvI/2GA + 3//AwsX/z8zB/3eM2f8YP/L/I0bv/yNG7/8jR/D/I0bw/yNH7/8jSfD/I0nw/yRE7f8lNOj/Gxrj/2pw + 0//NzcL/xMTE/87Owv93etL/HBXh/yYe4P8mH+D/Jh7f/yYe4P8lH+D/JR7f/yYf4P8mHuD/Jh/g/yYf + 4P8mHt//Jh/g/yYf4P8mHt//Jh7g/yYd4P8mHd//JRzf/yUb3/8mHN//JiDg/yYk4v8lL+b/JDzr/yRF + 7v8jSfD/I0fw/yNH8P8jR+//I0bv/yNH7/8jR/D/I0fw/yNH8P8jRu//I0bw/yNH7/8jR+//I0bw/yNG + 7/8jR+//I0fv/yNH8P8jRu//I0bv/yNG7/8jR/D/I0fv/yNH8P8jR+//I0fw/yNH8P8jR+//I0fw/yNG + 7/8kSfX/Ij/c/xYVgP8TEHD/GheX/yYf4P8mH+P/Jh/g/yYf4P8mHuD/Jh/g/yYd3/8lJOL/I0Tv/yNH + 8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH + 7/8jRvD/I0fw/yNH8P8jSPD/I0jw/yNI8P8jSfD/G0T7/1l05v8xMi3/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP98fH3//////////1v///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wAmIcAAJiHAACYhwAAmIcAAJiDAACYgwAAmIMAAJSDAACYhwAA0M8cAHxe8AAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQMFB8AMDGH/TlK0/zs7nf8NC2//IRvD/yYf + 9f8fGsH/FBN0/xQTev8VE3r/EhB5/xMRev8UE3X/IBu//yUe7v8lHuj/JR3n/yUd6P8lHuj/JR3o/yUc + 5/8lHOf/JS3q/yRE7/8jSvH/I0fw/yNG7/8jR+//I0fv/yNG7/8jR/D/I0fw/yNH7/8jR+//I0fv/yNG + 7/8jRu//I0fw/yNG7/8jR/D/I0fw/yNH8P8jR/D/I0bw/yNH7/8jR/D/I0fw/yNH8P8jRu//I0fv/yNH + 8P8jR+//I0fv/yNH8P8jRu//I0bv/yNH7/8jR/D/I0fv/yNG7/8jR/D/I0fv/x9D8P8+YOj/w8TE/8rI + w/9TcOP/iprU/9PPwP+2vMj/LlPs/x5C8f8jRu//I0fw/yNG8P8jR/D/I0fv/yFF8P8iSPH/I0rx/xs9 + 7/9JX+P/w8XF/8bFxP/MysL/lJ3N/yMf4P8lGt//Jh3f/yYd3/8mHN7/Jhzf/yYc3/8mHd//Jh3f/yYd + 3/8mHd//Jhzf/yUc3/8lHN7/Jh3e/yYg4P8mIuH/JSTi/yUq4/8lMeb/JTjq/yRA7P8jRu//I0nx/yNJ + 8f8jSPD/I0bv/yNH8P8jR/D/I0fw/yNH7/8jR+//I0fw/yNH7/8jR+//I0bv/yNG8P8jR/D/I0bv/yNG + 7/8jR/D/I0bv/yNG7/8jR/D/I0bv/yNH7/8jR/D/I0bw/yNH7/8jR+//I0bv/yNH7/8jR+//I0fv/yNG + 7/8jSPL/JEfu/xkelP8SD27/GBaJ/yYf2f8mH+T/Jh/f/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh3f/yQ9 + 6/8jSPD/I0fw/yNH8P8jR/D/I0bv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNG8P8jRu//I0fv/yNH + 7/8jR+//I0nw/yNJ8f8jR/D/I0Tt/yRB7f8kQO3/JD3r/yY99/8bJ3z/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/qamq//////T///8z////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8AJiHAACYhwAAmIcAAJiHAACYgwAAmIMAAJiDAACUgwAAmIcAANDPHAB8XvAAeF7wAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC4FBxj0EBBy/0JHqP8zM5X/Dw1x/yEc + x/8nHvX/HxnA/xQSdP8VE3r/FRN6/xUTe/8UEnb/GBWO/yUe5f8lHun/JR3n/yUd5/8lHuj/JRzn/yUb + 5/8lJOn/JDzt/yNK8P8jSPD/I0bv/yNH7/8jR/D/I0fv/yNH7/8jRu//I0fv/yNH8P8jR/D/I0fw/yNH + 8P8jR+//I0fv/yNH7/8jR+//I0bv/yNH7/8jR/D/I0fv/yNG7/8jRu//I0fv/yNH8P8jR/D/I0bv/yNG + 7/8jR/D/I0fw/yNH8P8jR/D/I0bv/yNG7/8jR/D/I0fw/yNG7/8jRu//I0fv/yNG7/8gRPD/LE7t/7K4 + yf/SzcD/boXc/0do5f/JycP/z8zB/22F3P8XPvP/I0fw/yNH8P8jRu//I0fv/yNH7/8hSO//HkPv/yNH + 8P8gRfD/LFPt/7S5yP/Ix8P/x8bD/7S5yP8uR+n/Ijfr/yQ26P8lM+f/JTHm/yUu5f8lK+T/JSzk/yUs + 5P8lLOP/JSzk/yUv5f8lMeb/JDbo/yQ76v8kP+v/JELt/yNG7/8jSPD/I0nw/yNJ8P8jSfH/I0fv/yNH + 8P8jR/D/I0bv/yNH7/8jR+//I0fv/yNG7/8jR/D/I0fv/yNH7/8jR+//I0fv/yNG7/8jRu//I0fw/yNH + 7/8jR/D/I0fv/yNG7/8jR+//I0fw/yNH7/8jR+//I0bv/yNH8P8jR+//I0fv/yNH8P8jR/D/I0bw/yNG + 7/8jR/D/JEr2/x4vuP8TD3D/FRR7/yMdx/8nIOf/Jh7f/yYf4P8mH9//Jh7g/yYf4P8mH+D/Jh/g/yYc + 3/8kPOr/I0nw/yNH7/8jRvD/I0bv/yNH8P8jR/D/I0fv/yNG7/8jR+//I0fv/yNH8P8jR/D/I0bv/yNH + 8P8jSfD/I0fv/yQ/7P8lMuf/JSnj/yYl4f8mH+D/Jh7g/yYd3/8pIOv/Fhhn/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/CwsM/8zMzv/////T////Cf///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////ACYhwAAmIcAAJiHAACYhwAAmIMAAJiDAACYgwAAlIMAAJiHAADQzxwAfF7wAHhe8AB0W + uwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbCQoo/xEPff88Q6H/LS6R/xAO + cv8gG8L/Jh7w/yQd2P8WFH//FBJ1/xUTe/8UEnb/FRN3/yIcx/8mHu3/JR3n/yUd5/8lHej/JRvn/yUf + 6P8kM+v/I0Xv/yNJ8P8jR/D/JEfw/yRG8P8jR+//I0bw/yNH8P8jR+//I0fv/yNH8P8jRu//I0fv/yNH + 7/8jR+//I0fv/yNH7/8jRvD/I0fv/yNH8P8jR+//I0fv/yNH7/8jR+//I0bv/yNH8P8jR+//I0fw/yNG + 8P8jR+//I0fw/yNG7/8jR/D/I0fw/yNH7/8jR+//Ikbv/yNG7/8jR+//I0fw/yNH7/8jRu//IUXv/yFI + 8P+XptD/08/A/5ak0f8bQ/L/m6nQ/9LOwP+yucr/K1Du/x5D8f8jRu//I0fw/yNH8P8dQe7/kKn4/1l5 + 9P8bQO//Ikbv/x5D8f+WpdH/y8jC/8XExP/DxMT/RWno/x1E8v8jSfD/I0nx/yNJ8P8jSPD/I0jv/yNI + 8P8jSfD/I0jv/yNJ8P8jSfH/I0nx/yNJ8f8jSfD/I0nw/yNI8P8jR+//I0fw/yNH8P8jR/D/I0fw/yNH + 7/8jR+//I0fw/yNG7/8jR+//I0fv/yNG7/8jRvD/I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//JEfw/yNG + 7/8jR/D/I0fw/yNG7/8jR+//I0fv/yNH8P8jRu//I0bv/yNH7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNG + 8P8jR/D/JEn0/yI/3v8WFX//ExBx/x4Zq/8nH+f/Jh/g/yYf4P8mH+D/JR7g/yYf4P8mH+D/Jh/g/yYc + 3/8lJOL/I0Xv/yNH8P8jR+//I0fv/yNG7/8jRvD/I0bv/yNH8P8jR+//I0fw/yNH8P8jR+//I0fv/yNH + 8P8jSfH/JD7r/yUp4/8mHuD/Jhvf/yYc3/8mHuD/Jh7g/yYe4P8mHt//KSDv/xoYhv8AAQD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/x4eH//w8PD/////hf///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wAmIcAAJiHAACYhwAAmIcAAJiDAACYgwAAmIMAAJSDAACYhwAA0M8cAHxe8AB4X + vAAdFrsAHBa6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkw4ROP8TEH7/MjiY/yYo + jP8RDnH/HRir/yYe7/8mHu3/IBq//xUUf/8TEnP/FhR+/x8au/8lHu3/JR3o/yUc6v8lG+j/JR3n/yQs + 6v8kQe7/I0rw/yNI8P8jRu//I0fv/yNH8P8jR+//I0bv/yNH7/8jR/D/I0fv/yNG7/8jR+//I0bv/yNH + 7/8jR+//I0fv/yNG7/8jR+//I0bv/yNG7/8jR+//I0fw/yNH7/8jR+//I0bv/yNH7/8jR+//I0fw/yNH + 7/8jRu//I0fw/yNG8P8jRu//I0fw/yNH8P8jR+//Ikbv/xxC7/8iRu//I0fv/yNH7/8jR+//HEDv/xY8 + 7/8ON/H/d4rV/9PPwP+0ucj/Iknw/1Zy4v/MysL/zszB/22E3P8XPvP/I0fv/yNG8P8hRfD/HUPv/9zj + /f+zwfr/Fzzu/yNG7/8ZP/L/b4fb/83Kwf/FxMT/y8rC/2V93v8ZPvL/I0fv/yNH7/8jR/D/I0fw/yNH + 7/8jRu//I0fv/yNH7/8jR+//I0fw/yNG8P8jR+//I0fw/yNH7/8jRu//I0fv/yNH7/8jRu//I0fv/yNH + 8P8jR+//I0bv/yNH7/8jR+//I0fv/yNG7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH8P8jR+//I0fv/yNH + 8P8jRu//I0bv/yNH7/8jRu//I0fw/yNH7/8jR+//I0bv/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0fw/yNH + 7/8jR+//I0fw/yRJ9f8cJ6f/Ew5u/xgVi/8mH93/Jx/j/yYf4P8mHt//Jh7g/yYf4P8mHt//Jh/g/yYe + 3/8mHt//JD7r/yNI7/8jR/D/I0fv/yNH7/8jR+//I0bv/yNG7/8jR+//I0fv/yNH8P8jR+//I0fv/yNI + 8P8jSO//JTTn/yYe3/8mG9//Jh7g/yYe3/8lHt//Jh/g/yYf4P8mH+D/Jh/g/ycf6P8lIr//BgcM/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP89PT///v7+8f///yr///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8AJiHAACYhwAAmIcAAJiHAACYgwAAmIMAAJiDAACUgwAAmIcAANDPHAB8X + vAAeF7wAHRa7ABwWugAeF7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALYPEUL/ExF//yMk + if8iJIf/EhBz/xkWkf8mHuf/JR3p/yYe7f8jHdn/IBrH/yQc2f8lHe7/JRzt/yUb6P8mH9z/JSvk/yQ9 + 7v8jSfD/I0jw/yNG7/8jR+//I0fw/yNH7/8jR/D/I0fv/yRH7/8jR/D/I0fv/yNG7/8jR+//I0fw/yNH + 8P8jRvD/I0bv/yNG8P8jRu//I0bv/yNH8P8jRu//I0fw/yNG8P8jRu//I0fv/yNG7/8kR+//IETv/xo/ + 7/8hRO//I0bv/yNH8P8kR/D/I0bv/yNG7/8jR+//I0fv/xxB7/+Amvf/QmHx/x5C7/8jR/D/Gj/v/0dq + 8/+ouvr/sb/7/6e14v/Ix8H/wcPF/0Jj5/8cQ/H/pK7N/9HNwP+yucn/K1Dt/x5C8f8jR+//HkLv/y5V + 8P/q8v7/tcH6/xc87v8jRvD/GT/y/1Bt5P/Gx8T/xcTE/87Lwf+DldX/HELx/yNG7/8jR+//I0fw/yNH + 7/8jR+//I0fw/yNG7/8jR+//I0fv/yNH7/8jRu//I0fv/yNG8P8jR/D/I0bv/yNH7/8jR+//I0bv/yNG + 8P8jR+//I0bv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNG8P8jR+//I0fv/yNG7/8jRvD/I0bv/yNG + 7/8jRu//I0fw/yNH8P8jR+//I0fv/yNH8P8jRvD/I0bv/yNH8P8jR+//I0fw/yJF7/8jRu//I0bv/yNG + 7/8jR+//I0fv/yNJ9P8iPtv/FRR9/xQRdP8hHL7/JyDn/yYe3/8mHuD/Jh7f/yYf3/8lH9//Jh7f/yYe + 4P8mHN//JDbo/yNK8P8jR+//I0fw/yNH8P8jRu//I0fw/yNH8P8jR+//I0bv/yNG7/8jR+//I0fw/yNH + 8P8jSfD/JTLm/yYc3/8lHt//Jh/g/yUf3/8mHt//JR7g/yYe3/8mHuD/Jh/g/yYf4P8lH+D/KCHp/xET + Q/8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/jo6P/////5n///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////ACYhwAAmIcAAJiHAACYhwAAmIMAAJiDAACYgwAAlIMAAJiHAADQz + xwAfF7wAHhe8AB0WuwAcFroAHhe8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABUAAADWDxJM/xUT + gf8YF37/Fxh9/xQSeP8WFH7/IxzY/yUd7P8kHef/JR3s/yYd8v8lHPD/JRzj/yUhxv8lMbj/JUDQ/yRI + 7P8jSfH/I0bv/yNH8P8jR+//I0fw/yNH8P8jR+//I0fw/yNH7/8jR+//I0fv/yNH8P8jRvD/I0fw/yNG + 7/8jRu//I0bw/yNG8P8jR/D/I0bw/yNG8P8jR/D/I0fv/yNG8P8jR/D/I0bw/yNH7/8jR+//IETv/zZZ + 8f9VdPP/KE3w/xg97/8jRu//I0fw/yNH8P8jR+//I0fv/x1B7v86XvH/5u/+/0Vj8v8dQe//HUHv/zle + 8f/j7P3////////////z8/D/wsLD/8zKwf9ie97/Djb1/1x44P/OzMH/zszB/2eA3f8XPvL/JEfw/xxA + 7/89YfL/3un+/1p59P8bQO//I0fw/x9D8P8xVev/uLzH/8fGw//Jx8P/o6/N/yVJ7/8iRfD/I0bw/yFE + 7/8iRe//I0fw/yNH7/8jRu//I0bv/yNH8P8jRvD/I0fv/yNH7/8jR+//I0fw/yNH8P8jRu//I0fv/yNH + 8P8jR/D/I0fw/yNH7/8jR/D/I0fw/yNG8P8jR+//I0fw/yNG7/8jR/D/I0fw/yNG8P8jR/D/I0bv/yNG + 7/8jRu//I0fv/yNH8P8jR/D/I0fv/yNH8P8jR/D/I0fv/yNG8P8jRvD/I0fw/yNG7/8kSfD/I0fv/yNH + 7/8jR/D/I0bv/yNH8P8kSvX/HSuv/xMObv8ZFpD/Jh/h/yYe4f8mHt//Jh/g/yYf4P8lHt//Jh7f/yYf + 4P8mHN//Jivk/yNI8P8jR/D/I0bw/yNH7/8jRu//I0bv/yNH8P8jR/D/I0bw/yNH7/8jR/D/I0fw/yNH + 8P8jSfD/JTfo/yYc3/8mHuD/Jh/g/yYf4P8lH9//Jh7g/yUf4P8mH+D/Jh7g/yYf4P8mH+D/JR7f/ygg + 7f8fH5X/AQIA/wAAAP8AAAD/AAAA/wAAAP8AAAD/JCQl//Hx8e////8n////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wAmIcAAJiHAACYhwAAmIcAAJiDAACYgwAAmIMAAJSDAACYh + wAA0M8cAHxe8AB4XvAAdFrsAHBa6AB4XvAAhGr0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsAAAA8g8S + S/8WFIL/FBJ6/xQSev8UEnr/FBJ1/yEbzv8mHu//JRzt/yUb5/8mH9b/JSnD/yU2s/8kQsb/I0no/yNJ + 8/8jR/H/I0fv/yNH7/8jRvD/I0bv/yNG7/8jRvD/I0fv/yNH8P8jR/D/JEfw/yRH7/8jRvD/I0fv/yNH + 7/8jRvD/I0bv/yNH7/8jRvD/I0fw/yNH7/8jR/D/I0fw/yNH7/8jR+//I0bw/yNH7/8jR/D/I0fw/x9D + 7/84XvH/i6j4/6zC+v9ig/X/GT7v/yJF7/8jRu//I0fw/yNH8P8VO+7/coz2/8/c/f8nTPD/IEPv/xY8 + 7/+Pp/j////////////////////5/8jIxv/LyMH/gpbW/xg+8/8nTe7/rLTL/9HNwP+qtMv/JEnu/xM4 + 8P8XPO//QmXy/8LW/P8tUvH/H0Pv/yNH7/8iRvD/H0Tw/5yoz//LycP/xsbD/7y/xv80V+v/H0Pw/yNF + 8P8pTvD/IUfw/yNH8P8jR+//I0bw/yNH8P8jR+//I0bv/yNG8P8jRvD/I0bv/yNH7/8jR/D/I0fv/yNG + 7/8jR/D/I0fv/yNG7/8jR+//JEfw/x9C7/8aPu//I0fv/yNH7/8jR/D/I0fv/yRH8P8kR/D/I0fv/yNH + 7/8jR+//I0bv/yJG7/8aP+//Gz/v/yJG7/8jR/D/I0bv/yNH8P8jR/D/I0fw/yNG8P8dQO//T3P0/zpg + 8v8gRO//I0bw/yNG7/8jSPL/I0To/xcZhv8UEXT/IRy//ycf5v8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mHuD/JiDg/yNA7P8jSPD/I0fv/yNH7/8jR/D/I0bw/yNG7/8jR+//I0fw/yNH8P8jR/D/I0bv/yNG + 7/8jSfD/JDzr/yYf3/8lHd//Jh/g/yYf4P8lHt//Jh7f/yYf3/8mH9//JR/f/yYf4f8mH+L/Jh/i/yYf + 4P8mHuP/JyPZ/wsNI/8AAAD/AAAA/wAAAP8AAAD/AAAA/0lJSf////+F////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8AJiHAACYhwAAmIcAAJiHAACYgwAAmIMAAJiDAACUg + wAAmIcAANDPHAB8XvAAeF7wAHRa7ABwWugAeF7wAIRq9AB8YvQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOQAA + AP0PEUH/FhOB/xUTe/8UEnv/FBJ3/xYThP8jGt//Jh3i/yYkyv8lMsH/JUDK/yRH3f8jSfD/I0f1/yNH + 8f8jRvD/I0fv/yNH8P8jR/D/I0fw/yNG7/8jRu//I0bv/yNH7/8jR/D/I0fw/yRH7/8kRu//I0bv/yNH + 8P8jR+//I0fw/yNH8P8jR/D/I0fv/yNH8P8jR+//I0bv/yNG7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH + 8P8kR/D/H0Pv/xg97/85XfL/obr5/3WT9/8YPe7/I0bv/yNG7/8jRvD/Fz7v/5y0+f+Bmvf/Fzzu/yJF + 7/8fRe//yNj8/9Hb/P9ujfb/dpP3/7TF+f/Hys//ysfA/6Wuzf8hRvD/Fz3y/2R+3v/QzcH/ysnB/26H + 3/9Scff/Kk/w/zZb8f/J3Pz/Nlrx/x5C7/8jR/D/I0bw/xpA8v94jtn/zcvB/8XFxP/Gx8P/Um/j/xs/ + 8f8bP+//d5X3/0tu8/8cQO//I0fv/yNH8P8hRfD/GT7v/xY77/8YPe//Fjzu/xo/7v8jR+//I0fw/yNH + 7/8jR+//I0fw/yNH7/8jR+//I0fv/x9C7/8yVfD/fJv3/ylN8P8hRe//I0fw/yNG7/8hQ+//I0fw/yNH + 8P8jR/D/I0fv/x9D7/8dQ+//SWvz/0Nm8v8bQe//IkXw/yNH7/8jR/D/I0fv/yBD7/8iRu//FTvu/4GY + 9v9oiPb/Gz/v/yNG8P8jR/D/JEn1/yA2x/8TEHH/GRaO/yYf4P8mH+H/Jh/g/yYf4P8mHuD/Jh/g/yYf + 3/8mHuD/Jhvf/yUy5v8jSvD/I0fw/yNH8P8jR/D/I0fw/yNH8P8jRu//I0bv/yNH8P8jR+//I0bv/yNH + 7/8jSfD/I0Dr/yYg4P8mHd//Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH9//Jh/h/ycf5v8nIOL/Jh/b/yYf + 2v8mH+L/KB/r/ygj1/8MDiP/AAAA/wAAAP8AAAD/AAAA/wAAAP9TVVfe/f7/D/3+/wD9/v8A/f7/AP3+ + /wD9/v8A/f7/AP3+/wD9/v8A/f7/AP3+/wD9/v8A/f7/AP3+/wD9/v8A/f7/AP3+/wD9/v8A/f7/AP3+ + /wD9/v8A/f7/AP3+/wD9/v8A/f7/AP3+/wD9/v8A/f7/ACYhwAAmIcAAJiHAACYhwAAmIMAAJiDAACYg + wAAlIMAAJiHAADQzxwAfF7wAHhe8AB0WuwAcFroAHhe8ACEavQAfGL0AIBm9AAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AE0AAAD/DhA3/xYTgv8VE3v/FRN7/xQRdf8aF4L/Ji22/yU6vf8kRdT/I0nt/yNI9P8jR/P/I0bw/yNG + 7/8jR+//I0bv/yNG7/8jR/D/I0fv/yNH7/8jR/D/I0fv/yNH7/8jRu//I0bw/yNH8P8jR/D/I0bw/yNG + 7/8jR/D/I0fv/yNH8P8jR/D/I0fw/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH8P8jR+//I0fw/yNH + 8P8jR+//I0bv/yNH8P8jR/D/HD/v/xxB7/+Pq/j/WHn1/xk97/8jR+//Ikbv/x1B7/+zxvv/R2fy/xxB + 7v8gRO//MFHw/77N/P8yV/H/Eznu/xY77v8WP/H/kJ/U/9HNwP+3vMf/M1js/xs/8v8sUO3/s7rJ/8nH + wP/Iysz////8/93l/f9oivX/zdz8/0Vk8v8bQO//I0fv/yNH7/8ZPvL/V3Pi/8jIw//FxcT/zszC/3OG + 2/8YP/L/GD3v/46l+P+Pp/j/ETfu/xs/7/8gRO//LlPw/1h49P+Jo/f/oLn5/4yl+P9HaPL/Fj3v/x9C + 7/8kR/D/I0bv/yNH7/8iRu//Fjzv/xc87v8ZP+7/orj5/+Hs/v8mS/D/IETv/yFF7/8vU/D/P2by/x9D + 7/8jRu//I0fw/yBE7/8jSe//orj5//H4///z+v//jKX4/xpA7/8iRu//Ikbv/yZK8P9CaPP/I0jw/xU6 + 7v+BmPb/b472/xs/7/8jRvD/I0fw/yRI8/8cJqP/Ew9t/x8btP8nIOf/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh7g/yUh4P8kQu3/I0nx/yNH8P8jR/D/I0fw/yNG7/8jRu//I0fw/yNH7/8jR+//I0bv/yNH + 8P8jSvH/Iz7r/yYi4P8mHd//Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mHt//Jh/k/ycg4v8gG7n/GReR/xQQ + hf8KB4D/DAmK/xIOsP8PEGj/AAAA/wAAAP8AAAD/AAAA/wAAAP8BAQD/IiSE7FZQ8S5WUPEAVlDxAFZQ + 8QBWUPEAVlDxAFZQ8QBWUPEAVlDxAFZQ8QBWUPEAVlDxAFZQ8QBWUPEAVlDxAFZQ8QBWUPEAVlDxAFZQ + 8QBWUPEAVlDxAFZQ8QBWUPEAVlDxAFZQ8QBWUPEAVlDxAFZQ8QAmIcAAJiHAACYhwAAmIcAAJiDAACYg + wAAmIMAAJSDAACYhwAA0M8cAHxe8AB4XvAAdFrsAHBa6AB4XvAAhGr0AHxi9ACAZvQAmH8AAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAABTAAAA/wgIJv8WFID/FRN7/xMQd/8WFoD/Iju0/yRI3v8jSfT/I0f0/yNH8P8jR+//I0fv/yNG + 7/8jR+//I0fw/yNH7/8jRvD/I0fw/yNH8P8jR+//I0fw/yNH7/8jR+//I0fw/yNH8P8jR+//I0bw/yNH + 8P8jR/D/I0fw/yNG7/8jR/D/I0bw/yNH8P8jR+//I0fw/yNH8P8jR/D/I0fv/yNG7/8jR/D/I0fw/yNH + 8P8jRu//I0fv/yNH8P8jR/D/I0fw/yRH8P8eQe//Kk/w/4em+P8tUfD/IETv/x5B7/85W/L/ssf6/ydO + 8P8gRO//IETv/zpa8f+Emfb/Fjzv/yNH7/8jR+//GD7y/2+E2//PzcH/x8fE/05q5P8ZP/L/GT/x/2yE + 2//PzcH/yMbB/7fB5P/S3f7/8fn//+zy/v9ScPP/GT7v/yNH8P8jR+//H0Pw/zRZ6v+6vsb/xsbD/8zJ + wv+SotL/H0Xx/xo+7/9hgfT/xdX8/zdd8f9NcPP/XIL1/3mX9/9+nfj/hqH4/4qk+P+wwvr/3un9/5+1 + +f8jS/D/HEDv/yRH7/8iRe//H0bv/1159P9Rb/P/IEXv/+fu/v+Rqfj/GkDv/yJG7/8gQ+//NFjx/1qC + 9f8gRO//I0bw/yNH8P8aP+//Y4T0/561+f9EY/L/c471/+rx/v9NbvL/Gj/v/yJG7/8hRe//fJ74/zZZ + 8f8UOe7/bov1/2iJ9f8bP+//I0bv/yNH8v8jROr/FxqG/xYTfv8lH9b/Jh/j/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYc3/8lMOX/I0nw/yNH7/8jR+//I0fv/yNG7/8jRu//I0fv/yNH8P8jR+//I0fv/yNH + 8P8jSfH/Izvq/yYg4P8mHN//Jh7g/yYe4P8mH+D/Jh/g/yYf4P8lH9//Jh/j/yYf3v8bGJr/FBN0/wkG + b/8XGXr/Wl+d/3mCrv9iaKX/JClM/wAAF/8AAAb/AAAA/wAAAP8AAAD/ExRH/yYg4f8dFeGtJh7gACYe + 4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJiHAACYhwAAmIcAAJiHAACYg + wAAmIMAAJiDAACUgwAAmIcAANDPHAB8XvAAeF7wAHRa7ABwWugAeF7wAIRq9AB8YvQAgGb0AJh/AACYf + vgAjSPAAI0jwACNH7wAjRu8AI0bvACNH7wAjR+8AI0bvACNH8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAaQAAAP8DBA7/FBNt/xQQef8UEHT/HzPB/yRK+/8jR/T/I0fv/yNH7/8jRu//I0fv/yNH + 7/8jR+//I0bv/yNH8P8jR+//I0fw/yNH8P8jR+//I0bv/yNH7/8jR/D/I0fv/yNG7/8jR/D/I0fw/yNH + 7/8kR/D/I0fw/yNH8P8jR+//I0fw/yNG8P8jRu//I0fw/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNG + 8P8jR+//I0bv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0bw/xk87v9UdfT/XoH1/xo+7/8NNO7/fZj3/5ux + +f8YPe7/I0bv/yBE7/83V/D/mK74/x9G8P8iRu//I0fv/x1B8f9Uc+L/yMjD/8/Mwf9shdv/GT/y/x5D + 8f8tU+z/tbzI/9HNwP+Wo8z/KVHx/3ST+P/M3/3/dZP1/xg97v8jRu//I0fv/yJF8P8iR+//pK7N/8nH + w//IxsP/r7bJ/ylN7v8bP/D/SGvz/9rn/v9dhPX/P2Py/zJW8v8fRO//Gz/v/xg97/8XPO7/Fjzv/z9i + 8f/A0Pv/xdT7/ytR8P8cQO//GD3v/4Gc9////////////6q++v+yxfr/Jkzw/yBE7/8jR+//IETw/zJW + 8f9qkPb/Ikbv/yJG7/8jR+//IkXv/1h89P8iRu//GD3v/w007v9/mvf/jKf4/xg97v8iRu//GUDv/6a9 + +v9nhfX/CzLu/4Wd9/9ggfT/G0Dv/yNH8P8jSPT/ITrS/xMSc/8bF5r/Jx/l/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYe3/8mH9//JD7s/yNJ8P8jR+//I0fv/yNH7/8jR/D/I0fw/yNH7/8jR+//I0fw/yNI + 8P8jSPD/JTXo/yYe4P8mHt//Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/i/yYg4P8bF5j/FBJz/wYE + cv89QZP/xMjY//b38P/39/D/9vbx/9vd3/+OlLz/Jy5w/wAAFP8AAQD/EhRF/ych1f8nH+f/Jh/f+SYe + 4DYmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYhwAAmIcAAJiHAACYh + wAAmIMAAJiDAACYgwAAlIMAAJiHAADQzxwAfF7wAHhe8AB0WuwAcFroAHhe8ACEavQAfGL0AIBm9ACYf + wAAmH74AI0jwACNI8AAjR+8AI0bvACNG7wAjR+8AI0fvACNG7wAjR/AAI0fwACNG7wAjR+8AI0bvACNH + 8AAAAAAAAAAAAAAAAHQAAAD/DBEg/xkghv8WFoT/HjC8/yNJ9f8jR/D/I0bv/yNH7/8jR+//I0bv/yNG + 7/8jR+//I0fv/yNG7/8jR+//I0fw/yNG7/8jR+//I0fv/yNG7/8jR/D/I0fv/yNH8P8jR/D/I0bv/yNG + 8P8jR+//I0fv/yNG7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fw/yNH + 7/8jR+//I0fw/yNH8P8jR/D/I0bw/yNH8P8jR/D/I0fw/yNH8P8hRe//JEjv/2uM9v8fRu//WHjz/+Lu + /v9SdPP/Gj/v/yNH7/8gRO//L1Hw/8LR/P8uUfH/H0Pv/yJG7/8fQ/D/OFrp/8PDxP/Ny8H/jp/T/xxB + 8f8iRvD/GkDx/3WL2f/Py8H/zMvC/1Rx4P8BK/D/gJv3/6S2+f8XPO7/I0fw/yNH8P8jRu//G0Ly/3+T + 1//NysH/xcXD/8DCxf8/Yuj/HEDx/yJI7/+zyPv/Tm/z/xY67v8hRO//Ikbw/yNH8P8jR/D/I0bw/yNG + 7/8ZPu7/HUXv/7DC+v/B0fv/HULv/ypQ8P+3yfv/eZf3/6m9+v//////gZv3/xI57v8jR+//I0fw/yBE + 8P8xVfH/f6D3/yRI7/8iRu//I0bv/yNH8P9PdPT/IEXv/yNH7/8fQu//NFny/5Kq+P8fQu//IkXv/xxB + 7/+Kp/j/rL/6/xI87v+uwvv/QmPy/x5C7/8jR/D/JEn3/x0vuf8TEG7/IBu5/ycf5v8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yUe4P8mHN//JSji/yNG7/8jR+//I0fv/yNH8P8jR+//I0fw/yNG8P8jR/D/I0fv/yNJ + 8f8kRe//JS3k/yYd3/8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/ycf5v8eGqz/FBJz/wkG + c/8/Q5P/3ODl//j28f/r6ur/6urq/+rq6v/w8O7/+Pfz/9fb5/9cY5v/CAxX/yEbyf8oIOz/Jh7f/yYc + 3/8mH+COJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmIcAAJiHAACYh + wAAmIcAAJiDAACYgwAAmIMAAJSDAACYhwAA0M8cAHxe8AB4XvAAdFrsAHBa6AB4XvAAhGr0AHxi9ACAZ + vQAmH8AAJh++ACNI8AAjSPAAI0fvACNG7wAjRu8AI0fvACNH7wAjRu8AI0fwACNH8AAjRu8AI0fvACNG + 7wAjR/AAI0bvACRK+QAGCh1sFCFW/yRF1/8kR/D/IkLl/yRI8/8jRvD/I0bv/yNH7/8jRu//I0fv/yNH + 7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fw/yNH + 8P8jRu//I0fw/yNH7/8jRu//I0bv/yNH7/8jR/D/I0fw/yNH7/8jR+//I0bv/yNH7/8jR+//I0fw/yNH + 8P8jR/D/I0fw/yNH7/8jR/D/I0fw/yNH7/8jR/D/I0jw/yNJ8f8jSvD/I0nw/xtB7/9KcvT/mbb6//z9 + //+uwPn/HEPv/yFG7/8jR+//IUfv/yNK8P/H1/3/X330/xM57v8bQe//Eznv/ydM7f+stsv/zsvB/6yz + yv8kSu//IUXw/xxA8f8+YOz/wcTH/87LwP+jr83/GkDv/0dn8/+zxvr/IUjw/yJF7/8jR+//I0fw/xk/ + 8v9cduD/yMnD/8XFw//KycL/X3jf/xg+8v8ZPu7/n7L5/2WE9f8YPe7/I0fv/yNH8P8jRu//I0fv/yNH + 8P8jR/D/JEfw/x1B7/8dRO//rcH6/4uj+P8+ZfP/T3b0/xE27v8YP+7/rsD6/8/c/P8jSO//IUTv/yNH + 7/8gRO//LlLx/6a9+v8rTvD/IUXv/yNG7/8cQO//XYH1/05x9P8TOO7/Gj7v/yhO8P+TqPf/IETv/yJG + 8P8kSO//SnHz/83a/P+pvfn/uMr7/yVK7/8hRe//I0fw/yRJ9P8aJqT/FRB3/yQez/8mH+P/Jh7f/yYf + 4P8mH+D/Jh/g/yYf4P8lHt//Jhze/yUy5/8jSfD/I0fw/yNH7/8jR/D/I0fv/yNH7/8jRvD/I0fw/yNJ + 8P8kPev/JiXh/yYd3v8mHuD/Jh/g/yYe3/8mHt//Jh7g/yYf4P8mH+D/Jh7g/ycf5P8kHdD/FhR+/wwJ + c/8mKYX/0dXe//b08P/q6er/6urq/+nq6v/p6en/6enp/+np6f/y8e3/8PHw/4aNu/8SE4f/IBjQ/ych + 5f8lJ+L/Jh7g2iYf4A4mH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJiHAACYh + wAAmIcAAJiHAACYgwAAmIMAAJiDAACUgwAAmIcAANDPHAB8XvAAeF7wAHRa7ABwWugAeF7wAIRq9AB8Y + vQAgGb0AJh/AACYfvgAjSPAAI0jwACNH7wAjRu8AI0bvACNH7wAjR+8AI0bvACNH8AAjR/AAI0bvACNH + 7wAjRu8AI0fwACNG7wAkSvkwIkLWxiVI8P8jSPf/I0jx/yRI8/8jR+//I0bv/yNG7/8jR/D/I0bv/yNH + 7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH + 7/8jR+//I0fw/yNH7/8jR/D/I0fw/yNG7/8jR+//I0fw/yNG7/8jR+//I0fv/yNG7/8jR+//I0bv/yNH + 8P8jR/D/I0fw/yNH8P8jSPD/I0nw/yNJ8f8jR/D/JETu/yQ/6/8kO+n/JDbp/yU05/8hKuX/Ljzm/87b + +v//////iJrx/xcf4v8kJ+P/Jirj/yQq5P8aJ+T/q7n1/9rk/f+uw/r/vNH8/3+Z9/8mRe3/i5jP/9LO + wf+8wMb/Pl/o/xxC8f8fRPD/L1j1/7vH2v/Jx8D/zMrC/1h04f8aQPL/pr77/zhc8v8fQu//I0fv/yNH + 8P8eQvH/PF/o/77Bxv/GxcP/zcvB/4CU1v8aQfL/GT3v/4ah+P9zjfb/Fjvu/yNH8P8jR/D/I0bv/yNH + 8P8jR+//I0fw/yNH8P8jR/D/HkLw/yFH8P+et/n/e5z3/2mL9v8WPO//Gz/v/zhc8f/y+P//XHz0/xo/ + 7/8jR/D/IUXv/yNJ7/+6zfv/Wnr0/xE37v8jRu//HkLv/zZZ8f+jvPr/WHjz/zFX8f+ww/r/l6/4/xc9 + 7v8iRu//Jkrv/ytU8P+Lovf//////4Oc9/8XPe7/I0bv/yNI8f8jRu3/GR6Q/xcThv8mH9//Jh7h/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYc3/8kO+v/I0nw/yNH7/8jRu//I0fv/yNH7/8jR/D/I0nx/yRH + 7/8lMub/JiDf/yYd3/8mH+D/JR7g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8nIOb/HRmj/xIQ + cv8LDHf/oKXG//j38f/p6er/6urq/+rq6v/p6en/6unp/+np6f/q6ur/6enq/+zs6//39/D/kpm5/xMS + jv8hH9z/I0jy/yUv5vwmHN5GJhzeACYc3gAmHN4AJhzeACYc3gAmHN4AJhzeACYc3gAmHN4AJhzeACYc + 3gAmHN4AJhzeACYc3gAmHN4AJhzeACYc3gAmHN4AJhzeACYc3gAmHN4AJhzeACYc3gAmHN4AJhzeACYh + wAAmIcAAJiHAACYhwAAmIMAAJiDAACYgwAAlIMAAJiHAADQzxwAfF7wAHhe8AB0WuwAcFroAHhe8ACEa + vQAfGL0AIBm9ACYfwAAmH74AI0jwACNI8AAjR+8AI0bvACNG7wAjR+8AI0fvACNG7wAjR/AAI0fwACNG + 7wAjR+8AI0bvACNH8AIjRu9zI0fw7SRJ9f8jR/H/I0bv/yNG7/8jRvD/I0fw/yNH7/8jR/D/I0fw/yNH + 7/8jRu//I0fv/yNH7/8jR+//I0bv/yNG7/8jR/D/I0fw/yNG8P8jR+//I0fv/yNH7/8jRu//I0bv/yNH + 8P8jR/D/I0fw/yNH8P8jR/D/I0bv/yNH8P8jR/D/I0fw/yNH7/8jRvD/I0fv/yNH8P8jR+//I0fw/yNH + 7/8jR+//I0nx/yNJ8P8jRu7/JD/r/yQ36f8lL+X/Jifj/yYi4f8mIOD/Jh3f/yYb3/8mHd//JRzf/yEZ + 3v95h+3/0Nj5//////98hOz/GRLd/yYc3/8mHd//GxLe/3eB7P//////oq7y/42S7//o7vz/xc36/4mR + 0//KycH/zMzC/1pg2P8bIOb/JS7l/x0t6f+suO7/09LH/8rIwP+hrM3/Fz3v/4ml+P9TcfP/G0Hv/yNJ + 8P8jSvD/IUjx/yVM8P+ossz/yMbD/8rIw/+grM7/Ikfw/xk+7/91k/f/fpT2/xQ67v8jR/D/I0fw/yJG + 7/8jR/D/I0fw/yNH8P8jRu//Ikbv/yJF7/8UOO//NFrx/6rB+v/H2P3/UHD0/xA27f8WO+7/ucv7/3mV + 9v8YPe//I0bw/yJF7/8fRu//nbf5/+Do/f9EaPP/Fj3u/xc97/8dQO//L1Px/6K7+v/5/P///////01w + 8/8aP+//I0bv/yZK8P85YPL/OVvx/8DR+/82WvH/H0Lv/yNG7/8jSPL/I0Po/xYYgf8bF5j/Jx/i/yYe + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYd3/8mIeD/JEHt/yNI8P8jRu//I0fv/yNH7/8jRu//I0nx/yQ/ + 7P8mKOP/Jhze/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf3/8mH9//Jh/g/yYf4P8mH+P/JR7W/xcV + gv8HBHD/Ulie//Lz7//s6+v/6enp/+rq6v/q6ur/6enp/+np6v/q6ur/6urq/+rq6v/p6en/7Ovr//X2 + 8P+PlLf/Fhaq/yND8/8jRe7/JSLgdSUi4AAlIuAAJSLgACUi4AAlIuAAJSLgACUi4AAlIuAAJSLgACUi + 4AAlIuAAJSLgACUi4AAlIuAAJSLgACUi4AAlIuAAJSLgACUi4AAlIuAAJSLgACUi4AAlIuAAJSLgACUi + 4AAmIcAAJiHAACYhwAAmIcAAJiDAACYgwAAmIMAAJSDAACYhwAA0M8cAHxe8AB4XvAAdFrsAHBa6AB4X + vAAhGr0AHxi9ACAZvQAmH8AAJh++ACNI8AAjSPAAI0fvACNG7wAjRu8AI0fvACNH7wAjRu8AI0fwACNH + 8AAjRu8AI0fvACNG7yUjR++3I0fv/yNH8P8jR/D/I0bv/yNH8P8jRu//I0bw/yNH8P8jR+//I0bw/yNG + 7/8jRu//I0bv/yNH7/8jRu//I0fv/yNG7/8jRu//I0fv/yNH7/8jRu//I0bv/yNH8P8jR+//I0fw/yNG + 7/8jR/D/I0fw/yNH8P8jR+//I0fw/yNH7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fw/yNI + 8P8jSfD/JETv/yQ76/8lMOX/Jibi/yYg4P8mHd//Jhzf/yYd4P8mHuD/Jh7g/yYe3/8mHuD/Jh7g/yYf + 3/8jGt//P0vl/z5G5P+1vvX//////1pf6P8cE97/Jh/g/yIZ3/8wMuH/6/D8/9fa+f8wNuP/JSXg/7e7 + 9//e5Of/v7/B/83Mwv92gNL/HRXh/yYc3/8bEN3/aW/t/+Pm2//CwsD/ysvE/0RM2P9dYOz/b4Hu/xwl + 4/8lMeX/JTTn/yQ26f8eNuv/hpXT/83Lwv/HxsP/ub3H/zJV6/8XO/D/Y4T1/3qQ9f8VO+7/JEnw/yNI + 7/8aQO7/HEDv/xxA7/8ZPe//FDvv/xg/7/8fRe//PGDy/5Sr+f+6z/z/d5j3/+Xw//+En/j/Q2jz/4mp + +f9QdvT/HUPw/yNJ8P8jSPD/HkTw/12E9v/t9///+fz//7HD+v9igfX/JEzx/xQ87/94mff/xtn9/3mW + 9/8cRfD/Iknw/yJJ8P8mTfH/OmTz/yBH8P8sU/H/IUXv/yNH7/8jR+//I0jy/yJA4f8UE3f/HRio/ycg + 5f8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8lHd//JSTh/yRE7v8jSPD/I0fv/yNH7/8jSPD/I0jx/yU2 + 6P8mIOD/Jh3f/yYe3/8mH+D/Jh7g/yYe3/8mHt//Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mH+D/Jx/m/yAb + uP8QDXL/Fxl9/8LG1//09O//6enp/+rq6v/q6ur/6urq/+np6f/q6ur/6urq/+np6f/p6en/6unp/+rp + 6v/s6+r/9PTt/25ys/8WL9n/I0v0/yQ66acjSPAAI0jwACNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI + 8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI + 8AAjSPAAJiHAACYhwAAmIcAAJiHAACYgwAAmIMAAJiDAACUgwAAmIcAANDPHAB8XvAAeF7wAHRa7ABwW + ugAeF7wAIRq9AB8YvQAgGb0AJh/AACYfvgAjSPAAI0jwACNH7wAjRu8AI0bvACNH7wAjR+8AI0bvACNH + 8AAjR/AAI0bvACNH708jR+/hI0fv/yNG7/8jR+//I0fv/yNG7/8jR/D/I0bw/yNG8P8jRu//I0fv/yNG + 7/8jRu//I0fv/yNG7/8jRu//I0fv/yNH8P8jRvD/I0bv/yNG7/8jR+//I0fv/yNG7/8jR/D/I0fv/yNG + 8P8jR+//I0bv/yNH7/8jR+//I0bw/yNH7/8jRu//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0nw/yNH + 7/8kPOr/JS/m/yYj4f8mHt//Jh3f/yYd4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH9//JR7f/yYf + 4P8mH+D/IBbf/0VI5P84PeP/HRzf/7/I9v/c5vv/Kynh/yIa3/8mHt//GxTe/4+Y8P//////4+r8/3J4 + 6/9NWun/197v/8nIw//KysL/naDM/yAZ4f8lHeD/JBvg/yYj4/+yu+r/1NPH/8rJwf+bn8v/i5jw/3uH + 7v8dE93/Jhzf/yYc3v8mHd//HBPh/2Zn1P/LzMP/xsXE/8PHxP9PVdr/FxPi/1Va6f96fO7/Fx3i/x8l + 4/9IUen/kqf2/0Ba6v9FYOr/XXnw/4Sb9/+ovfr/2OT+//f+///o7vv/XnDt/xQs6P9gcO7/t8H1/7LB + 9f9ne+//ITfq/yQ56f8kOen/JDnp/yE16f9MZ+//b4Lw//j9/////////////0xg7f8aKuf/JDXo/yY4 + 6P8dLOf/JDPo/yQ06P8lNOn/JDbp/yU66v8kPOv/IT3s/yNE7v8jR+//I0nw/yNK9P8hPNT/ExJ0/x4Z + sv8nIOf/Jh/f/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jhzf/yUn4/8jR+//I0jw/yNH8P8jSfD/I0Xu/yUu + 5f8mHd//Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH9//Jh7f/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 5P8bF5r/BgRt/21zrP/29/D/6urq/+rq6v/q6ur/6unp/+rq6v/q6ur/6urq/+rq6v/q6ur/6unp/+rp + 6f/q6ur/6enp/+/v7P/e3t7/O1DM/xxB8v8jSPDeI0jwFCNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI + 8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI + 8AAjSPAAI0jwACYhwAAmIcAAJiHAACYhwAAmIMAAJiDAACYgwAAlIMAAJiHAADQzxwAfF7wAHhe8AB0W + uwAcFroAHhe8ACEavQAfGL0AIBm9ACYfwAAmH74AI0jwACNI8AAjR+8AI0bvACNG7wAjR+8AI0fvACNG + 7wAjR/AAI0fwACNG73gjR+/9I0bv/yNG8P8jR/D/I0fw/yNG7/8jR/D/I0fw/yNH8P8jR/D/I0fv/yNH + 8P8jR+//I0fv/yNH7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH7/8jR/D/I0fv/yNH7/8jR+//I0fv/yNH + 7/8jRu//I0fv/yNG7/8jR/D/I0fw/yNH8P8jR/D/I0bv/yNH7/8jRu//Ikbx/yJH8v8iR/L/IT/x/yMy + 6v8lJeT/Jh3f/yYc3/8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/f/yYe4P8mH+D/Jh/g/yYf + 4P8lHt//Jh7f/yQb4P8vL+H/SFbl/x0T3/8vL+L/2eP6/3J57P8bE97/Jh7g/yEZ3/8xMuL/2N/6//// + ////////8vb+//P0+f/HyMj/yMfB/7K3x/8vMt//Ixnh/yYf4P8eFt7/Ojzl/6uz0v/JyMH/zMvD/46h + 4f98gu//IBff/yUe3/8mH+D/Jh/g/yAX4P9BRdv/v8PF/8bFxP/NzsL/b3HT/xUN4P9RSeb/d3Hr/xgO + 3f8cEt3/U1Pm//////////7/9/n9//H1/P/4+P3/2+H6/6218/9yeOv/MzTi/x8X3v8mHd//HRbf/yEd + 3/8mI9//IBnf/yYd3/8mHt//Jh7f/yYd3/8kG9//QUbk/yIe3/9WWuj/nqXx/5ih8f84MuL/Ihbe/yUb + 3v8kGd//Jhve/yYc3/8mHN7/Jhvf/yYd3/8mHt//Jh/g/yYg4P8mIuH/Jijj/yUy5/8lQPH/IDfJ/xQS + dP8gGrn/Jx/m/yYf3/8mH+D/Jh/g/yYe4P8mHt//Jh/g/yYc3v8lKOT/I0rx/yNI8P8jSfD/JD/s/yYn + 4/8mHd//Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe + 4v8lH9j/ExCC/xwefv/Q1eP/9/by/+np6f/q6ur/6unq/+rp6f/q6ur/6urq/+rq6v/q6ur/6urq/+np + 6f/q6er/6urq/+rq6v/q6er/9vPr/5up4/8ZPu7/I0fw/yNH71kjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAmIcAAJiHAACYhwAAmIcAAJiDAACYgwAAmIMAAJSDAACYhwAA0M8cAHxe8AB4X + vAAdFrsAHBa6AB4XvAAhGr0AHxi9ACAZvQAmH8AAJh++ACNI8AAjSPAAI0fvACNG7wAjRu8AI0fvACNH + 7wAjRu8AI0fwDSNH8KIjR/D/I0bv/yNG7/8jR/D/I0fw/yNH7/8jRu//I0bv/yNH7/8jR/D/I0bw/yNH + 7/8jR/D/I0fv/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fv/yNH8P8jR+//I0fw/yNH8P8jR/D/I0fw/yNH + 8P8jRu//I0fw/yNH7/8jR+//I0bv/yNH8P8jR/D/I0fv/yNH7/8iRvL/H0T2/yFG7/8lQOT/KDLX/y4y + xv8sKcr/JR3e/yUd4v8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mHuD/Jh/g/yYf3/8mH9//Jh/g/yYe + 4P8mHuD/JyDg/yYf3/8lHuD/KCDg/1Fb5/8lHt//GA/d/2Np6v+jpfP/HRXf/yYe4P8mH+D/HRXe/0RH + 5f/T2fn////////////n7/v/p6/N/8rIwf/Dx8X/Skna/x4V4v8mH+D/JiDg/xoR4P9FSdr/xMfF/8zL + wv+fpsz/h5Dv/yIc4P8lHuD/Jh/g/yYf4P8kHOD/KSXf/6ywyP/IyMP/y8rC/4yVzv8cFeD/Q0Dl/11g + 6P8cFN7/Ixvf/zAs4f9+gO3/wMj3//////++y/b/Mzbi/yIc3/8dGt//GxPe/yIa3/8mHuD/Jh7g/yYe + 4P8lHeD/JB3g/yYd4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yQd4P8mHuD/HhXf/xoW3v8aFt7/Ixvf/ycg + 4P8mH+D/Jh7g/yYf3/8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh7g/yYd3/8mHN//JyHl/yAj + vf8VE3X/IRu+/ycf5v8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8lHd//JSPh/yQ96/8kP+z/JTHm/yYg + 4P8mHeD/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mHt//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe + 3/8nH+T/IhzJ/wwKdf9BRY//sbOw/+Df3//v8O//6enp/+zs7P/t7e3/7u7u/+/v7//u7u7/7e3t/+rq + 6v/q6ur/6urq/+rp6v/p6un/6unq/+7t6v/a4Ov/NVnv/x5C8P8jR/CoI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAJiHAACYhwAAmIcAAJiHAACYgwAAmIMAAJiDAACUgwAAmIcAANDPHAB8X + vAAeF7wAHRa7ABwWugAeF7wAIRq9AB8YvQAgGb0AJh/AACYfvgAjSPAAI0jwACNH7wAjRu8AI0bvACNH + 7wAjR+8AI0bvHSNH78AjR/D/I0fw/yNG7/8jR+//I0fv/yNG7/8jRu//I0fw/yNH8P8jRu//I0bv/yNG + 7/8jR+//I0fv/yNH7/8jRu//I0fv/yNG8P8jR/D/I0fv/yNH7/8jRvD/I0fw/yNG7/8jR/D/I0fw/yNH + 8P8jRu//I0bv/yNH8P8jR+//I0fv/yNG7/8jR+//I0fv/yJH8f8fRff/JUjm/z9Ws/9baJX/b3GL/3x7 + if+HiJH/goSK/2Bljf8tLNH/JB3j/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mH9//Ixnf/zk84/87Q+P/Ihjf/yQb4P9EU+b/MjLi/yIZ3/8qJOD/c4Ds/yYi4P8lHd//Jh7g/yYf + 4P8eFt//Li7h/21x6/+Hhe7/S1Ho/4KGzv/OzsL/zc3C/2Vq1f8cFeL/JR7g/yYf4P8lHeD/HRrh/46U + zv/OzcL/ysrD/5iq4/8nJ+P/JBvf/yYf4P8mHuD/JR3g/yAc4f+Lk87/zczC/8jIw/+usMj/JCHf/0A9 + 5P9bXuj/HRTf/yYf4P8kHeD/Fw7d/x8c3/9rcer/4ej7/7a/9f8rKOH/HxXf/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mHuD/Jh7g/yYf4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8lHt//Jh7g/yYf4P8mH+D/Jh/f/yYe4P8mHuD/Jh/g/yYf4P8mH+D/JR7f/ycf + 5f8fGrP/FBNz/yEcv/8nH+b/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/JiDg/yYd + 3/8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yYe + 4P8mH+D/Jx/l/yAatf8UFHj/FBg6/wICAf9KSkv/7ezt//38/f/t7O3/3t7e/9jY2P/W1tb/2tra/+Pj + 4//09PT/9/f3//Hx8f/u7e3/6urq/+np6v/r6un/7u7q/1x47v8XPfD/I0fw4SNH8BQjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACYhwAAmIcAAJiHAACYhwAAmIMAAJiDAACYgwAAlIMAAJiHAADQz + xwAfF7wAHhe8AB0WuwAcFroAHhe8ACEavQAfGL0AIBm9ACYfwAAmH74AI0jwACNI8AAjR+8AI0bvACNG + 7wAjR+8AI0fvKCNG79QjR+//I0fv/yNG7/8jR+//I0bv/yNH8P8jR/D/I0bv/yNG8P8jR/D/I0fw/yNG + 7/8jR/D/I0fw/yNG7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH8P8jR+//I0fv/yNG7/8jR+//I0fv/yNG + 7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0jw/yFF9P8hRPH/OlK4/2txhv+Zl5b/sbC1/7i4 + wv+4ucX/urrI/76+zP+eoJf/QEKr/yIa5v8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYe + 4P8mH+D/Jh7g/yUc4P8rKOD/X27p/yQd4P8eE9//QEXk/zk64/8hF97/JSDg/0dR5v8kG+D/JR7f/yYe + 4P8mH+D/Jh/g/yMa4P8aEt7/Fw/e/xUL4P9nbdX/y83C/8zMwv+GjtD/Hhbh/yYe4P8mHt//Jh/f/x0V + 4v9JTdr/xsnE/8zKwf+vt8//LSzi/yMa3/8mH+D/Jh7f/yYe4P8cFeH/amzU/8zNwv/HxsP/vcHF/zo+ + 2/89OeX/XF/p/x0U3/8mH9//Jh/g/yYf4P8kG+D/GBDe/zc54v/Hz/j/vsj3/yYk4P8iGd//Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mHt//Jh/g/yYe4P8mHuD/Jh7f/yYe + 3/8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mHuD/Jh7g/yUe + 3/8nH+b/Hxuz/xUUeP8iHcf/Jx/l/yYe4P8mH+D/Jh7f/yYe4P8mH+D/Jh7f/yYe3/8lHt//JR/g/yYe + 3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/ycf5v8eGan/EhJh/wABBP8AAAD/AAAA/09QUP+Dg4T/TEtM/yIiI/8QEBD/DAwM/xgY + GP8yMjL/Y2Nj/42Njv/AwMD/4uHi//X09f/v7+//6urq//bz6f98kez/Fjzw/yNH7/ojR+9EI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAmIcAAJiHAACYhwAAmIcAAJiDAACYgwAAmIMAAJSDAACYh + wAA0M8cAHxe8AB4XvAAdFrsAHBa6AB4XvAAhGr0AHxi9ACAZvQAmH8AAJh++ACNI8AAjSPAAI0fvACNG + 7wAjRu8AI0fvOSNH7+MjR/D/I0fw/yNG7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jRu//I0fv/yNH + 8P8jR/D/I0fw/yNH7/8jRvD/I0fw/yNH7/8jR+//I0fv/yNH7/8jR/D/I0bv/yNH7/8jR+//I0bv/yNG + 7/8jR/D/I0fw/yNG7/8jR+//I0fv/yNG7/8jSO//I0nx/x9B9v8nPt7/UVuR/42Mif+zs7v/vL3N/7i5 + yf+3uMf/t7jH/7a4x/+7vMz/kpSU/zc5t/8iGuT/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh7g/yYf4P8mH+D/HxXf/1de5/9yfez/Ihvf/xwX3/8cFt7/HRje/11m6f9IUeb/Ihjf/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8gGOH/SlDZ/8jJw//My8P/p6rK/yQi4P8kHOD/Jh/f/yYf + 4P8kHN//IR3h/5WczP/OzcL/xcfE/01P2v8eFuH/Jh/g/yYf4P8mH+D/IBfh/0VL2//CxcT/xsXD/8vM + w/9cW9X/MC7l/0xQ5v8fFt//Jh7g/yYf4P8mH+D/Jh7g/yYf4P8eFN7/Hx3f/6659P+dp/H/HBbf/yUd + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/f/yYf4P8mH+D/Jh/g/yYe + 3/8mHt//Jh/g/yUf3/8lHt//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8mH+D/Jx/m/x8btP8VFHf/Ih3I/ycf5P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH9//Jh/g/yUe + 3/8mH+D/Jh/g/yYe4P8mHd//Jh7f/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe3/8nH+b/Gxii/w8SSP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/BgYG/ykpKv93dnf/z8/P/+zs7P/08ur/lqvs/xpA8P8jRu//I0fvbCNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AJiHAACYhwAAmIcAAJiHAACYgwAAmIMAAJiDAACUg + wAAmIcAANDPHAB8XvAAeF7wAHRa7ABwWugAeF7wAIRq9AB8YvQAgGb0AJh/AACYfvgAjSPAAI0jwACNH + 7wAjRu8AI0bvOSNH7+YjR+//I0fw/yNH8P8jR+//I0bv/yNH7/8jR+//I0fv/yNH7/8jR+//I0bw/yRH + 7/8kR+//I0bw/yNG7/8jR+//I0fw/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fw/yNH7/8jRu//I0bv/yNG + 8P8jRu//I0fw/yNH8P8jR+//I0fw/yNH7/8jSfD/I0Xv/yAz8f8vN8f/ZWeA/6Ggnf+7vMv/ubrK/7a3 + xv+4ucn/ubrJ/7i5yP+3uMj/trfA/25yhv8oJdb/JR3i/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yUd4P8fF9//bnns/6az8/+Hje//g4nu/7O79f+Kl+//JR/f/yUc + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Ihnh/zcz3f+8v8b/y8nD/7i+x/85O93/IBfg/yYf + 4P8mH9//Jh/f/xwT4f9QVNn/x8rD/8/Owf+Wnc3/Ih3g/yQd4P8mH+D/Jh/g/yQb4P8uLN7/s7bI/8fH + w//NzMP/eoHQ/y0u5P9HVOX/Ihjf/yYe4P8mH+D/Jh/g/yYf3/8mH9//Jh/g/yAX3/8kIuD/ws33/11i + 6P8dFN//Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/Jh/g/yYf4P8lH+D/JR7f/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh7f/yYf3/8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/ycf5f8eGq//FRR3/yIdyP8mH+T/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/JR7f/yYf + 4P8mH+D/Jh7f/yYc3/8jHOD/ISLi/yYj4f8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh7f/yYe + 3/8mHt//Jh/g/yYf4P8mH+H/Jx/i/xsXm/8PEkP/AAEA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/2trbP/s7Oz/8/Lr/6y76v8dQu//Ikbv/yNH + 8JAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACYhwAAmIcAAJiHAACYhwAAmIMAAJiDAACYg + wAAlIMAAJiHAADQzxwAfF7wAHhe8AB0WuwAcFroAHhe8ACEavQAfGL0AIBm9ACYfwAAmH74AI0jwACNI + 8AAjR+8AI0bvNSNG7+YjR+//I0fv/yNH7/8jR+//I0fv/yNG7/8jR+//I0fv/yNH7/8jRu//I0fv/yNH + 8P8jR+//I0fw/yNH8P8jRvD/I0fw/yNG7/8jR/D/I0fv/yNH7/8jR/D/I0fv/yNG7/8jR+//I0fv/yNH + 7/8jR/D/I0fv/yNH7/8jR+//I0fw/yNJ8f8jRu7/JDfq/yIh6f83N7X/dXd+/62tsf+8vc3/t7jH/7a3 + xv+5usr/pKWw/4SEif+ur7z/vL3O/6Kjof9JS53/IRvl/yYf4f8mH+D/Jh/g/yYe4P8mHt//Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe3/8mH+D/JB3f/xwT3v9EROT/g4nt/5Sh8P9pbOr/Ix3f/yMb + 4P8mH+D/Jh7g/yYf3/8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yQa4P8pKOD/oqzL/8zKwv/KzMP/UlLY/xwU + 4f8mH9//Jh7g/yYf4P8kHOD/Ix/h/56ky//NzML/yMrE/0tP2v8fFuH/Jh/g/yYf4P8lHeD/Ih7h/5CY + zv/MysL/ycnD/5mey/8yMuP/YHfq/yQc3/8mHuD/Jh/g/yYf4P8mH9//JR/f/yUe4P8mH+D/GA7e/2ds + 6v+iq/L/HRXe/yUd3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/f/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYe4P8mHuD/Jh7f/yYf4P8mHt//Jh7g/yYe + 3/8mH+D/Jh7f/yYf4P8nH+b/Hhqx/xUTdf8iHML/Jh/l/yYe4P8mH+D/Jh/g/yYe4P8mHt//JR/g/yUf + 3/8mH+D/Jh7f/yYf4P8gKOT/Mkrr/0Jk8/8kPev/JR3f/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf + 3/8mH+D/Jh/g/yYf4P8mHuD/Jh/h/yYf3v8aF5b/EBNO/wABAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/xQUFP/j4+P/7e3t//Hw6v/Gy+r/IUHu/yFG + 8P8jR/CzI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAmIcAAJiHAACYhwAAmIcAAJiDAACYg + wAAmIMAAJSDAACYhwAA0M8cAHxe8AB4XvAAdFrsAHBa6AB4XvAAhGr0AHxi9ACAZvQAmH8AAJh++ACNI + 8AAjSPAAI0fvGyNH79ojR+//I0fw/yNH7/8jR/D/I0fv/yNG7/8jR+//I0bv/yNG7/8jR/D/I0bv/yNH + 7/8jR/D/I0fv/yNG7/8jR+//I0bv/yNH8P8jRu//I0fw/yNH8P8jR+//I0fw/yNH8P8jRu//I0bv/yNG + 8P8jR+//I0fv/yNH7/8jR+//I0nw/yNG7/8lOOn/JCTj/yEX5v8+Pqr/gIJ//7S1vP+6u8z/trfG/7a4 + xv+2t8b/u7zM/4eIjv9cXFn/mZqk/72+zP9/goj/LS3H/yQb5P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf3/8mH+D/IBff/xkQ3v8YEt7/GxLe/yUd + 4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHeD/Hhri/46Tzv/PzsL/zc3D/3J7 + 0/8dFeL/Jh7f/yYe4P8mHuD/Jh/g/xsT4f9UWNj/yMrE/8/Owv+NlM3/IBvh/yUd4P8mH+D/Jh7g/xwV + 4v9ydNP/zs7C/8jIw/+ytcb/PEXg/52r8/8rJ+H/JBvg/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yIY + 3/83OOP/qbrz/yko4P8kG+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7f/yYe3/8mH+D/JR/g/yUe3/8mH+D/Jh7g/yYf + 4P8mH+D/Jh7f/yYe3/8mH+D/Jx/m/yAbtP8UE3P/IRu9/ycf5v8mH+D/Jh7g/yYf4P8mH9//Jh/g/yUe + 4P8mH+D/Jh3g/yYk4v8cOOv/OV7y/4yd9/+Pofj/LkHq/yIa3/8mHt//Jh/g/yYe4P8mH9//Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4f8mH+H/GheZ/w8TTv8AAQD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8EBAT/TU1O/2BgYP8QEBD/AAAA/wAAAP8MDAz/w8PE//Dw8P/w7+r/0NDn/yNA + 6P8hRvH/I0fvySNG7wQjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AJiHAACYhwAAmIcAAJiHAACYg + wAAmIMAAJiDAACUgwAAmIcAANDPHAB8XvAAeF7wAHRa7ABwWugAeF7wAIRq9AB8YvQAgGb0AJh/AACYf + vgAjSPAAI0jwBSNH764jR+//I0fv/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0fw/yNG8P8jR/D/I0fw/yNG + 7/8jRu//I0fv/yNG7/8jR/D/I0fv/yNG8P8jRvD/I0fv/yNH8P8jR/D/I0fw/yNG7/8jR/D/I0fv/yNH + 7/8jRvD/I0bv/yNH8P8jSfH/I0Xu/yQ36f8mJeH/JRrh/yIb4/9DRaH/hYiD/7i4w/+5usv/trfG/7a4 + x/+2uMf/trfG/7q7y/+fn6r/eXl8/62uvP+zs7j/XWGP/yQe3/8lHeH/Jh7g/yYe4P8mHt//Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH9//JR7f/yYe3/8mHt//Jh/f/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe3/8mH+D/Jh/g/x0V4v90d9P/zc/D/8zM + w/+Vmc3/Hxnh/yUe4P8mH+D/Jh7f/yYe4P8jG9//IyLg/6Oqyv/NzML/xMfE/0VI2/8fFuH/Jh/g/yYe + 4P8gF+H/TlTZ/8XIxP/HxsP/wMPE/1Ja2v/AzPn/Z27q/xUM3v8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8XDd3/U1bm/7S+9f8iIN//JRzg/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mHuD/Jh/g/yYe + 3/8lH9//Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mH9//Jh7g/yYe4P8mHuD/Jh7g/yYf + 4P8lHt//Jh/g/yYe3/8mHt//Jh7g/ycf5f8fG7P/FBJy/yAbvP8nIOb/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHt//Jh7f/yYc3/8eM+j/Ol/z/42c9/+dpvf/jJ33/zI55v8iGt//Jh/g/yYf4P8mH+D/Jh/f/yYf + 4P8kG9//Ixrf/yYf4P8mH+D/Jh/g/yYf4P8mH+D/JyDm/xsXof8PEkz/AAEA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wcHB/9FRUX/sLCw//T09P/5+fn/wsLC/zMzNf8AAAD/AAAA/3d3eP/19PX/8fDs/9fb + 6v8sUOn/IETx/yNH780jR+8FI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACYhwAAmIcAAJiHAACYh + wAAmIMAAJiDAACYgwAAlIMAAJiHAADQzxwAfF7wAHhe8AB0WuwAcFroAHhe8ACEavQAfGL0AIBm9ACYf + wAAmH74AI0v0ACNL9EkjRu//I0bv/yNH7/8jR/D/I0fw/yNG7/8jR+//I0fv/yNH7/8jR/D/I0fw/yNH + 7/8jR+//I0fv/yNH8P8jR+//I0bw/yNG8P8jRu//I0fv/yNH7/8jR+//I0fw/yNH8P8jR/D/I0fv/yNH + 7/8jR+//I0jw/yNJ8P8jRe7/JTbn/yYl4f8mHN7/JRvh/yIc5P9FSJ7/jY+H/7m5xv+4ucr/trfG/7a4 + x/+2t8f/trfH/7a3x/+2t8b/uLnI/7m6yf+8vc3/pael/0pIov8hGOf/Jh7g/yYf4P8lH9//JR/g/yYe + 3/8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/f/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/JR7f/yYf4P8fF+H/VFzY/8rL + w//LysP/rbPI/yos4P8jGuD/Jh/f/yUf3/8lHt//Jh/f/xsU4f9eZNf/yszD/8/Owv+Mk83/IBvh/yUd + 4P8mHuD/Ixvg/zEw3v+3ucb/x8bD/83Mwv9hadL/o6v2/+Xp+/89P+P/Fgze/x8X3/8iGd//IRjf/xwU + 3v8WD93/Mzbi/8rV+P+GjO//GxLe/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/JR7g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh7f/yYf4P8lH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/JR/f/yYe4P8mH+D/Jh/g/yYe4P8nH+X/IBu3/xMSb/8eGrT/JyDo/yYe4P8mH+D/Jh/g/yYe + 3/8mHuD/Jh/f/yYe4P8iGd7/N0vr/4yd9/+apPf/l6L3/4ia9/8vNOT/Ixrf/yYf4P8mH+D/Jh7f/yYf + 4P8kG9//ODvk/zo+5P8iGt//Jh/g/yYe4P8mH+D/Jh/g/ycg5v8dGan/DxBX/wAAAf8AAAD/AAAA/wAA + AP8AAAD/AAAA/ycnKP+5ubr/9fX1//b29v/r6+v/6urq//f29//j4uP/Xl5e/wAAAP+2trj//v3+//b1 + 8v/Jztn/L1Lj/x9D8v8jR+/MI0fvBSNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAmIcAAJiHAACYh + wAAmIcAAJiDAACYgwAAmIMAAJSDAACYhwAA0M8cAHxe8Bh4XvBsdFrsuHBa6OB4XvEshGr1VHxi9TSAZ + vTcmH8AmJh++DiomvwAhRvGUI0nz/yNI8f8jSPH/I0jx/yNH8P8jR/D/I0fw/yNH8P8jR/D/I0fv/yNG + 7/8jR/D/I0fw/yNH7/8jR+//I0fw/yNG7/8jR+//I0fv/yNG7/8jR+//I0fw/yNH7/8jR+//I0fw/yNH + 8P8jSfH/I0nw/yRD7v8kNOf/JiTh/yYc3/8mHd//JBzh/yEc5f9ER57/jZCJ/7q7yP+4ucn/trfG/7a3 + x/+2t8b/trfG/7a3xv+2t8b/trfG/7a3xv+2t8b/urvM/6eop/9MS6D/IRnm/yYe4P8mH+D/Jh/g/yYf + 4P8mH9//Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 3/8lHt//JR/g/yYf4P8mHuD/Jh/g/yYe4P8mHt//Jh7g/yYf4P8mH+D/Jh/g/yYe3/8mHuD/IBjh/z89 + 3P/ExcT/yMfD/7/ExP9CQNv/Hhbh/yYf3/8lH9//JR/f/yYf4P8jG+D/JiXg/6qvyf/Ny8L/w8fD/0RI + 2/8gFuH/Jh7g/yQd4P8kIOD/mKDM/8zLwv/MzMP/gonO/05W6P/8////5en8/3V77f83NuP/Li/h/zM0 + 4v9ISOX/g47u/+bt/P/H0vf/Jibf/yMa3/8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yUe3/8lHt//JR/g/yUe3/8mH+D/Jh7g/yYf + 4P8mH+D/Jh7g/yYf4P8mH9//Jh/g/yYf4P8mHuD/Jx/l/yIcv/8UE3L/Hhmq/ycf5f8mH+D/Jh/g/yYf + 4P8mH9//Jh7g/yYf4P8mH+D/Hxfe/1Ve6v+fq/j/lKD3/5mj9/+AlPb/KjDk/yQc3/8mH+D/Jh/g/yYe + 4P8mH+D/IBfe/1dd6f94iPH/Ih7f/yUe3/8mH+D/Jh/g/yYe4P8nH+X/IRy4/w8OXf8AAAD/AAAA/wAA + AP8AAAD/AAAA/xAQEP/CwsP//fz8/+vr6//p6en/6urq/+np6f/p6en/8fHx//T09P9oaGn/R0dI/8jI + yP9/fnv/foOO/ztd7v8fQ/H/I0fvzCNH7wUjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AJiHAACYh + wAAmIcAAJiHAACYgwAomIMAmJiDAPiUgwGEmIcCONDPHrUhO0cJVX9jcYWrd9WNs3vxjbd7/XGTb/1Nc + 1/9ARc37JiDA6yUfwM0qJr+1JjHN7h8u1P8eMdr/HTfh/x075f8ePur/IEPv/yFF8f8iR/H/I0ny/yNJ + 8v8jSPH/I0fw/yNH8P8jR+//I0fw/yNH7/8jRu//I0fw/yNH7/8jRu//I0bv/yNG7/8jR/D/I0jw/yNJ + 8f8jSfD/JEHs/yUy5v8mIuH/Jhzf/yYd3/8mH+D/JB3h/yEc5f9ESJ7/jY+J/7m6yP+4ucn/trfG/7a3 + xv+2t8f/trjH/7a3x/+2t8b/trfG/7a3xv+2t8b/trfG/7i5yf+ys7j/YWaN/yUg3f8lHeD/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh7f/yYf4P8mH9//Jh7g/yYe4P8lHt//Jh/g/yYe + 4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yIa + 4P8uLd7/qrPI/8vJw//OzcL/XmPW/xsV4f8mH9//Jh7g/yYf4P8mH+D/Jh/g/xsU4v9mbdX/zM3C/8/O + wv+GjM7/HRjh/yYd4P8mHuD/HRbh/3p70f/OzsL/ycnD/6uuyf8cHN7/fITt//7/////////7PP9/9bg + +v/i6vz///////////+8xvX/MDPh/x4V3v8mH+D/Jh7g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYe4P8mHuD/JR7f/yYf4P8mHuD/Jh/g/yYe + 4P8mHt//Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/ycf5P8iHMX/FBN1/x0Zn/8nH+P/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/f/yQc3/8lJOH/a4Hz/5ql+P+Yo/f/gpb2/yow5P8jG9//Jh/g/yYf + 4P8mH9//Jh7g/yAX3/9RVuj/l6f3/zs95P8hGN//Jh/g/yYe4P8mH+D/Jx/l/yMczP8SEmX/AwQF/wAA + AP8AAAD/AAAA/wAAAP96env/+vr6/+rq6v/q6en/6erq/+np6f/q6ur/6erq/+np6f/v7+//6Ojo/yEh + If8EBAT/AAAA/4uNnP8yUOj/IEXz/yNH780jR+8FI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACYh + wAAmIcAAJiHAbSYhwK0mIcDJJiDA6yYgwP0iHL7/NTPH/5Gf9f+dqfv/nqv8/6Ct/f+ir/3/oq/+/6Ct + /f+irv7/mab4/zY0x/8fGL3/d4Ho/4OO7P90euP/Ymja/1BY1P9FStH/O0DQ/zE70P8oN9P/ITLW/x4z + 3P8eOeT/HTvn/x0/6/8fRPD/IUbx/yNJ8v8jSPL/JEjx/yNH8P8kR+//JEfv/yNH7/8jSfD/I0nx/yNG + 7/8lO+v/JSvk/yYg4P8lHN7/JR7f/yYf4P8mHuD/JR3h/yEc5P9ER5//jY+J/7m6yP+4ucn/trfG/7a3 + xv+2t8f/trfH/7a3xv+2t8b/trfH/7a3x/+2t8b/trfG/7a3x/+2t8f/urvJ/4GDiP8sK8v/JBvi/yYe + 4P8mHuD/Jh/g/yYf4P8mH+D/JR7f/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYe3/8mH+D/Jh/g/yYf + 4P8mH+D/JR/g/yYe4P8mH+D/Jh/g/yUf4P8mHuD/Jh/g/yYe4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8kHOD/Ih/g/5edzP/OzML/zMzC/36G0f8dFuH/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8jGuD/Kirg/62y + yP/MysL/vsLF/zw+3P8hGOH/Jh/g/x8W4f9SWNj/x8nD/8jHw/+8wcb/Oz7c/xYO3/9aYej/vML2/+Xt + /P/5/v7/5/D8/8PJ9/9wd+r/IyDg/x4W3/8nH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/JR7f/yUf + 4P8mH+D/Jh7f/yYe3/8mH9//Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mHuD/JR/g/yYf4P8mH9//Jh/g/yYe + 4P8mHt//Jh7f/yUe3/8mH+D/Jh7g/yYe3/8mH+D/Jh/g/yYf4P8mH+P/JB3P/xQSeP8aFpL/Jh/i/yYf + 4f8lH9//JR7g/yYe4P8mHuD/Jh/g/yUe3/8mH+D/Ihjf/yg+6v+Fl/f/mqT3/5Oi9/82POX/IRje/yYf + 4P8mH+D/Jh/g/yYe4P8gF97/UVXo/5+t+f9gauz/Hxfe/yYe3/8mH+D/JR/f/yYe4/8lHtr/Fxd3/wUI + C/8AAAD/AAAA/wAAAP8jIyP/4eHh//Dw8P/p6en/6urp/+rq6v/q6ur/6urq/+rq6v/q6er/7u7u/9vb + 2/8ZGRr/AAAA/xQUD/+srL3/JkLe/yFH9P8jRu/HI0fvAyNH7wAjR+8AI0fvACNH7wAjR+8AJR7gACQd + 4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAmIMAAJiDAACYgwMcmIMD/JiHA/yYgwP8mIMD/Ixy+/y4sxf+MmfP/laD3/4CN7v92gen/cHfn/25z + 5f9weef/fYnr/4mY8P85N8j/HBW5/3eB5/+ms/7/oa79/6Gv/f+hrv3/oq78/5un+P+Pm/L/hZDt/3yD + 6P9tcuD/W2ba/0tY1/8/R9X/MT7U/yQ31v8eM9z/Hjnj/x4/6v8iR/H/JEny/yNJ8P8jR+//JD7s/yUx + 5v8lJeH/Jh3f/yYc3/8lHt//Jh/f/yYf3/8mH9//Jh7h/yIb5/9ERqL/jY+I/7q6x/+4ucn/trfG/7a4 + xv+2t8b/trfG/7a4x/+3uMb/trfH/7a3x/+2t8f/trfG/7a3x/+2t8f/trjG/7u8zP+XmZf/Oj2v/x8W + 6/8mHuH/Jh/g/yYf4P8mH+H/JR7j/yMe5v8jHeb/JB7l/yQe4/8mHuH/Jh7g/yYe4P8mH9//Jh7f/yYe + 4P8mHuD/Jh7g/yUf3/8mHuD/Jh/g/yYf3/8mH+D/Jh7g/yYe4P8mH+D/JR/f/yYe4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh7g/xwU4v9+gNH/zc7C/8zLwv+fo8r/IR7h/yUd4P8mHt//Jh7f/yYf4P8mH9//Jh/f/xoT + 4v9tdNP/zc3C/8/Pwv+CidD/Hhji/yUe4P8iGuD/Nzfc/77Axf/HxsP/ysvD/1pb1/8bEuL/HBLe/x4b + 3/81OOL/R0jl/zc64/8fHd//GRHe/yMb4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe3/8mHt//Jh7g/yUe + 3/8lHt//Jh/g/yYe3/8mH+D/Jh7g/yYe3/8mH9//JR/f/yYe4P8mH+D/Jh/g/yUe4P8mH9//Jh/g/yUf + 3/8mH+D/Jh/g/yYf3/8lHt//Jh7g/yYf4P8mH+D/Jh/g/yUe4P8mH+D/Jh/i/yQe2P8VFH//FxSF/yUf + 3f8mH+H/Jh/g/yYe4P8mH+D/Jh/g/yUf3/8mH+D/Jh/g/yYc3/8eJeT/bYX1/5uk9/+bpvj/aXTu/yAZ + 3v8lHeD/Jh7f/yYe4P8mHuD/IBjf/05T6P+cqfj/hJH0/ygk4P8kHN//Jh7g/yYe4P8mH+H/Jx/l/xsZ + l/8FBxb/AAAA/wAAAP8AAAD/iomK//n5+f/q6en/6urq/+rp6v/q6ur/6urq/+np6f/p6en/6urp//b2 + 9v+cnZ7/AAAA/wAAAP8vLin/rrPQ/x451v8iR/P/I0jvqyNH7wAjR+8AI0fvACUd4AAhGd8AHhXeACUe + 4AAkHeAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJiDAACYgwAAmIMDGJiHA/yYfwP8fGL3/Gxa9/x0Xvf8bFrz/OUHL/0NLzv86Qcn/Oz/H/0E+ + yf9PSc3/T03N/y4yyP8qKcn/JyLG/yMcxP8yMsz/UVnY/11m2/9mb93/cXjj/3d+5/97hen/go/t/4uZ + 8v+bqPr/oa79/6Gu/f+gq/v/mqT3/4uW7/+Aiuv/bnXh/1Rd1/88RNH/JTDQ/yQ64v8lNOj/Jibj/yYe + 4P8mHN//Jh3f/yUe3/8mHuD/JR7f/yYe4P8mH+D/Jh/g/yIb5v88Pqz/h4qF/7m6x/+4usn/trfG/7a3 + xv+2t8b/trfH/7a3x/+2t8b/t7jH/7e3x/+2t8b/trfG/7a3xv+2t8f/trfH/7a3x/+5usr/ra6z/11g + hv8pKcr/Ihrl/yId6v8iHuv/Ix7o/ykg2f8vIsv/MCLJ/y8iy/8qIdn/JB7m/yMe6v8iH+v/Ih7q/yMe + 5f8lHuH/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHt//Jh7f/yUe + 4P8mH+D/Jh/g/yYe4P8eFuH/YGnW/8vMw//JyMP/s7rH/zM03v8iGOD/Jh/g/yYe4P8mH+D/Jh7f/yYf + 4P8iGeD/MDHe/7K4x//LysP/vsLF/zw+3P8gGOD/JB3g/ygj4P+fp8v/y8nC/83Mwv92gdL/HRfi/yYe + 4P8lHd//IBff/x0U3v8fF97/JR3g/yYf4P8mHt//Jh7f/yYf4P8mHuD/JR7f/yUe3/8mHuD/JR7g/yYe + 4P8lH9//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8lHt//JR7f/yUe3/8lH+D/JR/f/yYf4P8mH+D/Jh7f/yUf + 3/8mHuD/Jh7f/yUe3/8mH9//Jh/f/yYe4P8mH+D/Jh7g/yYf3/8mHuD/Jh/g/yYe4f8mHtz/GBaK/xYU + fP8kHtH/Jh7j/yYf4P8mH+D/Jh/g/yYf4P8lHt//Jh/f/yYf4P8lHN//ICDh/2+C8/+bpfj/laD3/56r + +f9XX+r/IRrf/yUe3/8mH+D/Jh7g/yEY3/9DSeX/laP3/5il+P9ESOb/Hxfe/yYf3/8mHuD/JR7f/ycf + 5v8hHL//DA00/wAAAP8AAAD/Njc4/+jn6P/t7e3/6enp/+np6f/p6en/6urq/+rp6f/q6un/6unq/+vr + 6//z8/T/T09P/wAAAP8AAAD/V1ZQ/5Weyv8YNtb/I0n0/yQ/7KEgEt0AIhjfAB4W3gAlHeAAIRnfAB4V + 3gAlHuAAJB3gACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJRzgACYhwAAmIcAAJiHAiCUgwPYkHr3/REPK/2512v+HjOL/oqLr/6qs7v+vtO//ub3z/8PG + 9v/Nzvr/2dr+/9/f/v90euz/GhHc/yUe3/8mH9//Ixve/yAY2f8gGNf/IBjT/yAYzv8gGMv/Ih/I/ycm + x/8uLcf/OTnK/05V0/9vd+L/gpDs/5Kf9f+dqvv/n6z8/6Kv/v+irf3/nKj4/0pPz/8gGLv/Jh3S/yYd + 4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yIa5v8yNb7/fH9//7e3wv+5usr/trfH/7a3 + x/+2t8f/trfG/7a3x/+2t8f/trfG/7a3x/+2t8f/trfH/7a3x/+2t8f/trfH/7a3xv+2t8b/trfH/7q7 + zP+lpqv/f4KM/1ZanP85LbD/SCSC/1UnWP9dKT//Yik1/2IpM/9iKTX/Xig//1cmTv9QJ23/RCWP/zUh + tf8uIsz/JB/k/yIe6v8iHuz/Ih7p/yQe4v8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/f/yYe + 4P8mHuD/Jh/g/yYf4P8mH9//IBjh/0VG2v/JycP/xsXD/8jKxP9MTNn/HhXh/yYf4P8lH9//Jh/g/yYe + 4P8mH+D/Jh/g/xsU4f92fNL/zc7C/87Nwv9/htD/HRjh/yUe4P8dGOL/f4LR/87Owv/LysP/m5/M/yAZ + 4f8lHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yUe + 4P8mHt//Jh/g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/JR7g/yUe3/8mH+D/Jh/g/yUe3/8mH+D/Jh/g/yYe + 4P8mHt//Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/f/yYe3/8mH+H/Jx/i/xsX + mv8TEnP/IhzC/ycf5v8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7f/x8c4P9acPD/n6r4/5ql + +P+KmPX/WWHr/yIc4P8lHuD/Jh/g/yYf4P8hGN//REnm/5Wi9/+cp/j/anTu/yAY3v8mH+D/Jh/g/yYe + 3/8mH+P/JR7Y/xYXcP8BAwf/AAAA/5+foP/5+fn/6enp/+np6f/q6er/6unq/+nq6f/q6ur/6unq/+np + 6f/19PT/wsLD/wkJCf8AAAD/AAAA/42Nhf+AiMX/FDTe/yNK8/8kLubyIBLdfCIY3wkeFt4AJR3gACEZ + 3wAeFd4AJR7gACQd4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJRzgACUc4AAmIcAAJiHAACYhwAQgGc48Sk7hj8vP+cbc2//12Nj+/9jY/v/X1/7/1dX9/9XU + /f/T0/z/0dH8/8/P+//b2/3/lZby/xwU3/8mHuH/Jh/g/yYf4P8mHuH/Jh/h/yYe4f8mH+L/Jh/i/yUd + 4f8kHN//Ixvc/yEZ2P8gGNP/IBnN/ykozP83Ncv/RUvO/1Zf1v9sdOH/fIfq/5Wj9f9ncd7/Hxm6/yYf + z/8mH+H/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yQd4v8nJNj/ZmmE/6+vtf+6u8v/trfH/7a3 + x/+2t8b/trfG/7a3xv+2t8b/trfH/7a4xv+2uMb/t7fG/7a3x/+2t8f/trfG/7a3xv+4ucj/u7zM/7q8 + y/+0t8b/r7PB/6Kmrf97fXb/aT0w/2glFv9pKRr/aCgc/2coH/9nKB//Zygf/2gpHf9pKRr/aSka/2ko + Gf9oKSL/YSk0/1gmTP9OJ3P/QCSX/zUiuv8pINz/Ih7r/yIe7P8kHuT/JR7f/yYe3/8mH+D/Jh7g/yYe + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yIZ4f80Md3/tbvH/8nHxP/NzcL/aHHV/x0V4f8mH9//Jh/f/yYe + 4P8mH+D/Jh/g/yYf4P8hGOH/Nzfe/7q+xv/LysP/uL3G/zU33f8iGeH/HxXh/1xh1//Jy8P/ycjD/7S4 + x/8vMN7/Ixrg/yYe4P8mH+D/Jh/f/yYe4P8mHuD/Jh7f/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf + 4P8mHt//Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yUe + 4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/ycg + 5v8eGav/ExFv/x4Zr/8nH+b/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/JR/f/yYe3/8kHt//KETt/01u + 8/9IaPP/Kjfm/yEY3/8mHuD/Jh/g/yYe3/8mHuD/IRjf/0FG5v+Uoff/mKP4/4qX9f8sK+H/JBvg/yYf + 4P8lHuD/Jh/g/yYg5f8cGKX/GR1L/wAAAP9sbG3/9/f3/+nq6v/p6en/6unq/+rp6v/q6er/6enp/+np + 6f/v7+//8fHx/0VFRv8AAAD/AAAA/wMDAv+pqqn/UFqz/xk96v8iR/D/KC3k/0JA5f8vL+LCHhbeUSUd + 4AchGd8AHhXeACUe4AAkHeAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJRzgACUc4AAlHOAAJiHAACYhwAAmIcAAIBnOAEpO4QDY2v4J0ND7Ls/P+1/Pz/ucz8/7yM/P + +/HPz/v/0dD7/9XU/P/S0vz/2tn9/6is9f8hH9//JBzf/yYf3/8lHt//Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/h/yYf4f8mH+H/Jh/i/yYe4f8kG+D/Ihrb/yAZ0/8gGM//HxjL/yUizf81MtX/Mi7W/yQe + 1f8mH9//Jh/h/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8iG+b/Rkmg/5ydmv+7vM3/trfG/7a3 + xv+2t8b/trfH/7a3x/+2t8b/trfH/7a3x/+3uMf/trjH/7a3xv+4ucn/uLnJ/7e4x/+7vMz/tLfH/5uf + qf+BgIX/dWpr/21aWf9nSUX/ZjYy/2UoI/9mKCT/ZSgk/2YpJP9lKCT/ZSgk/2YpJP9mKST/Zigk/2Uo + JP9lKCT/Zigi/2cpHv9oKRv/aSoa/2kpGP9nKSX/XShB/08mcP86I6j/KiDY/yIf7P8iHun/JR7h/yYf + 4P8mH9//Jh/g/yYf4P8mH9//Jh/g/yYf4P8kHOD/JSTh/56ny//Ny8L/zMzC/4uPzv8dFeH/JR7g/yYf + 3/8mH+D/Jh/g/yYf4P8mH+D/Jh7g/xwV4v98gdL/zM3C/87Owv92ftL/HBbh/yIZ4P87Ptz/wMLF/8jH + w//Ex8X/S0vZ/x0V4f8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYe + 4P8lH9//Jh/f/yUf4P8mH9//Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/f/yYe3/8mHt//Jh/g/yYe + 4P8nH+X/IRy+/xQSc/8bGJr/JyDj/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/f/yYe3/8mHt//Jh3f/yI9 + 7f8dRPD/HUPw/yIt5f8mHd//Jh/g/yYf4P8mHt//Jh/g/yMa3/83POP/kp/2/5ai9/+Zpvj/TlPo/x8X + 3v8mH+D/Jh/g/yYf3/8nH+X/HhjF/yYphP9SVl7/hISC//Tz8//q6ur/6unp/+rp6v/q6ur/6enp/+3t + 7f/5+fn/8PDw/29vcP8AAAD/AAAA/wAAAP8iIh//ubvI/yAuqP8hR/P/Hz/u/z1E5v+bqPj/i5n1/1Ja + 6f8hG97GIRnfVx4V3gAlHuAAJB3gACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJRzgACUc4AAlHOAAJRzgACYhwAAmIcAAJiHAACAZzgBKTuEA2Nr+ANDQ+wDPz/sAz8/7AM/P + +wvQ0Psoy8z7Vrq/+JOxt/bHwsX599va/f+ip/T/IR7f/yQc3/8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4f8mH+H/Jh7h/yYe4v8lHOH/Ihrf/yMb + 4P8mH+L/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8kHOP/KijQ/3R3hf+3t8L/t7jI/7a3 + xv+2t8f/trfH/7a3x/+2t8b/trfH/7a3x/+2t8b/trfG/7a3xv+6u8v/srLA/6qruP+6vMv/mp+p/3dw + cv9lRUH/Yy4p/2MnI/9kJB//ZSQf/2UlIf9lKCT/ZSgk/2UoJP9lKCT/ZSgk/2YoJP9lKCT/ZSgk/2Yo + JP9lKCT/ZSgk/2UpJP9lKCT/Zikk/2YoJP9mKST/Zygh/2goHf9pKBn/aSkc/2AoPP9NJ3L/NyO1/yUf + 4/8iHuz/JB7k/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/x0X4v+KjM7/zs/C/8vKw/+orsj/Jyjg/yQb + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe3/8gF+H/PD7c/77Cxv/LysP/trzH/zQ13v8gFuH/KSbg/6Or + yv/LycL/zs3C/2Zr1f8bFeL/Jh/g/yYe3/8mH+D/Jh/g/yUf3/8mHuD/Jh/g/yYe4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/Jh7g/yYe3/8lHt//JR/g/yYe4P8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYe4P8mHt//Jh7g/yYf + 4P8mHuD/Jh/i/yQd0v8VE3v/FxWH/yYf3v8mH+H/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHt//Jh7g/yYd + 3/8kO+v/I0nw/yNJ8P8lMOX/Jhzf/yYe4P8mH+D/Jh/g/yYf4P8jGt//Nzzj/5Gf9v+Voff/mqX4/3SA + 8f8hHN7/Jh3g/yYf4P8mH+D/Jh/h/yYf4v8ODI//fISy//v79f/q6ur/6enp/+vr6//u7u7/9PT0//n5 + +f/n5+j/pKSl/z0+Pv8AAAD/AAAA/wAAAP8AAAD/enp1/36CtP8RJbf/JEz4/x856/9AROX/kqH3/6Gt + +f+eq/n/cHzv/y8w4v8eFd6jJR7gDyQd4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAmIcAAJiHAACYhwAAgGc4ASk7hANja/gDQ0PsAz8/7AM/P + +wDPz/sA0ND7AMvM+wC6v/gAdYrsCoWM7yxbaeleRlDmmSEX38gmHuD3Jhze/yYc3/8mHN//Jhzf/yYc + 3/8mHd//Jh/g/yYe4P8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/IRrm/0JFpv+cnZv/u7vL/7a3 + xv+2t8b/trfH/7a3xv+2t8b/trfG/7a3x/+2t8b/trfG/7a3xv+7vMz/qKm1/319gP+bnaj/h4uQ/2VM + Sf9jKiX/ZSQf/2UmIf9lJyP/Zigk/2UoJP9lKCT/ZSgk/2YoJP9mKST/ZSgk/2UoJP9lKCT/Zikk/2Yo + JP9mKCT/ZSgk/2YoJP9lKST/Zikk/2UpJP9lKCT/Zigk/2YoJP9mKST/Zikk/2YpI/9nKR7/aSkZ/2cp + I/9ZJ1D/QSWW/ysg1P8iH+z/Ix7n/yYe4P8mH9//Jh7g/yYf4P8eFeH/anLV/8zNwv/Hx8P/u8HG/zw7 + 3P8gF+H/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH9//JR7g/x0Y4f+FjND/zMzC/83Nwv91fdL/HBXi/x4Z + 4f+HjM//zs7C/8zLw/+Fjc//Hhbh/yYe4P8mH+D/Jh/g/yYf3/8mH+D/Jh7g/yYe4P8mHuD/Jh7g/yYf + 4P8mHuD/Jh/g/yUe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 3/8mH+D/Jh7g/yUf4P8mH+D/Jh7f/yUe3/8mHuD/JR7f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mHt//Jh7f/yYf4f8mH97/GRaN/xQSd/8jHcn/Jx/k/yYe3/8mH+D/Jh/g/yYf4P8mH+D/JR/g/yUf + 3/8mHd//JTjo/yNJ8P8jSfD/JTLm/yYc3v8mH+D/Jh/g/yYe4P8mH+D/Ixrf/zc85P+Rn/b/lqH3/5ah + 9/+ToPb/OTvk/yEY3v8mH+D/Jh/g/yYe3/8nH+X/HRbB/xsdfP/Hy9n/9vXv/+np6f/k5OT/3t3e/8LC + w/+FhYX/LS0u/wAAAP8AAAD/AAAA/wAAAP8AAAD/MjEu/8HF0f8lKIr/HTrb/yNL9f8kNej/JBrf/zU2 + 4/9ia+z/jJr1/6Kv+v+Rn/b/T1bp/yIb37skHeAYJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJiHAACYhwAAmIcAAIBnOAEpO4QDY2v4A0ND7AM/P + +wDPz/sAz8/7ANDQ+wDLzPsAur/4AHWK7ACFjO8AW2npAEZQ5gAjGd4IJDzrjyQ66f8lMOb/JSvk/yUt + 5v8kMef/JDPm/yYn4/8mHuD/Jh7f/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 3/8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH9//JR3g/yQd3/9hZYz/srK5/7i5 + yf+2t8b/trfG/7a3xv+2t8b/trfG/7a3xv+2t8b/trfG/7a3xv+7vMz/p6ey/3Rzdf+OkJj/fn2B/2M7 + N/9lJB//ZiYi/2YoJP9lKCT/Zigk/2YoJP9mKCT/Zigk/2YoJP9mKCT/Zikk/2YoJP9mKST/Zigk/2Yp + JP9mKCT/ZSgk/2UoJP9mKCT/Zigk/2YpJP9lKCT/ZSgk/2YoJP9mKCT/Zigk/2YpJP9lKCT/ZSkk/2Yp + JP9mKCL/aCkc/2kpG/9gKTr/SiZ9/zEhxv8jH+r/Ix7p/yYe4P8mH+D/Hhfh/09S2f/Ly8P/xcXE/8zN + wv9TV9f/HRXh/yYf3/8mHuD/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8fFuH/Q0Xb/8HFxf/KycP/tbrH/zEy + 3/8aD+H/YmjW/8rMw//KysP/qavJ/yUh4P8lHOD/Jh/g/yYf3/8mH9//Jh/g/yYf3/8mHuD/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/JR7g/yUe4P8mHuD/Jh7g/yYf4P8mHuD/Jh7g/yYe + 4P8mH9//Jh/g/yYe4P8mH9//Jh7f/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYe + 4P8mH+D/Jh7f/yYe3/8mH+H/JyDl/x0Zp/8SEW7/Hxqy/ycg5v8mH+D/Jh/g/yYf4P8lHuD/Jh/g/yYf + 3/8mH+D/Jhzf/yU26P8jSfD/I0nw/yRB7f8mIOD/Jh3f/yYe4P8mH+D/Jh/g/yMa3/83POT/kZ/2/5ah + 9/+UoPf/mqb4/2dw7f8fGN7/Jh7g/yYf4P8mH+D/Jh/g/ycg5P8JApX/Q0eQ/+3v7P/39vP/nZ6f/yws + LP8JCQr/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/FBQT/8XGwv9gY6P/Dhia/yRK9f8jSPD/JSnk/yYc + 3/8iGd//Hxnf/y4t4f9OVOj/doLw/5mp+P9mce3/JB7gvCUc4A8lHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACYhwAAmIcAAJiHAACAZzgBKTuEA2Nr+ANDQ + +wDPz/sAz8/7AM/P+wDQ0PsAy8z7ALq/+AB1iuwAhYzvAFtp6QBGUOYAIxneACNJ8K8jSfD/I0nw/yNJ + 8P8jSvD/JD3q/yYo4/8mH+D/Jh7g/yYe3/8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYe4P8mH+D/Jh7f/yYf + 4P8mH+D/Jh7g/yYe3/8mH9//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yUc4/8qKc7/fH+M/7m6 + xv+3uMb/trfG/7a3x/+2t8b/trfG/7a3xv+2t8b/trfH/7a3xv+6u8v/rK26/3Z2eP+RlJ3/fHp8/2M0 + MP9lJCD/Zigk/2YoJf9lKCT/ZSgk/2YoJP9mKST/Zikk/2YpJP9mKST/ZSgk/2UoJP9mKST/Zikk/2Uo + JP9lKCT/ZSgk/2UoJP9mKCT/Zigk/2UoJP9mKCT/Zigk/2UoJP9mKCT/Zikk/2YoJP9lKCT/Zigk/2Yo + JP9lKCT/Zigk/2UpJP9lKCP/Zygf/2kpGv9kKS3/TiZv/zMivf8jHuj/Ix7p/yAY4f87N9z/vsLG/8fG + w//NzcL/dX3S/xwU4v8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh3g/x4b4f+NlM3/zczC/8zN + wv9tcdX/Fw7i/0FC2//FxsP/ycjD/7q/xv87Pd3/IRfh/yYf4P8mH+D/Jh/g/yUf3/8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8lHt//Jh/g/yYf4P8mH+D/Jh7g/yYe + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh7g/yYe + 4P8mH9//Jh/g/yYf4P8mH+D/Jh/g/ycf5P8iHMT/FBJz/xoWlf8nIOT/Jh7g/yYf4P8mH+D/Jh/g/yYe + 3/8mHt//Jh/g/yYc3v8lMeb/I0nw/yNH7/8jSfD/JTTn/yYc3v8mHt//Jh/g/yYe4P8jGt//Nzzk/5Gf + 9v+Woff/lKD3/5ai+P+Rnfb/Njbj/yEY3v8mH+D/Jh/g/yYe3/8kG+P/QUXc/xQYg/9dYqL/8vPw//z6 + 9/+dnZ7/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/EhIR/7q7tf+NksD/Cwt4/yE82f8kSvT/JEHs/yYg + 4P8mHuD/Jh7g/yYe4P8jG9//IBff/yAa3v9AQ+X/k6T3/z9A5f8hGN9HIRjfACEY3wAhGN8AIRjfACEY + 3wAhGN8AIRjfACEY3wAhGN8AIRjfACEY3wAhGN8AIRjfACEY3wAmIcAAJiHAACYhwAAgGc4ASk7hANja + /gDQ0PsAz8/7AM/P+wDPz/sA0ND7AMvM+wC6v/gAdYrsAIWM7wAiSvAAIkrwACJK8AYjR/DQI0fv/yNJ + 8P8kQu3/JSzk/yYd4P8mHd//Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe + 4P8mH+D/Jh/g/yYf3/8mH9//Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8jGub/Nja4/5GU + mP+7vMv/trfG/7a3x/+2t8b/trfG/7a3x/+2t8f/trfG/7a3x/+5usr/r7G+/3R1d/+QkJn/hYWL/2M4 + NP9lIx//Zigk/2YoJP9lKCT/ZSgk/2YpJP9mKCT/Zikk/2YpJP9mKCT/Zigk/2YoJP9lKCT/Zigk/2Uo + JP9mKCT/Zigk/2UoJP9mKCT/ZSgk/2UoJP9mKST/Zigk/2YpJP9mKCT/Zigk/2YpJP9lKCT/ZSgk/2Uo + JP9mKCT/ZSgk/2YoJP9mKCT/ZSgk/2YoJP9mKCT/Zygg/2koGv9mKSf/Uidk/zQiuv8hG+r/Jino/6Wt + yv/LysL/zMzD/5mdzP8fG+H/JR3i/yYf4v8mH+H/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8fFuH/Rkna/8TG + xP/KycP/sbXI/ysp3/8pJd//q7LI/8rJw//Jy8P/V1jY/xwV4v8mH+D/Jh7f/yUf3/8mH+D/Jh7g/yYe + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh7f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHt//Jh/g/yYe3/8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/f/yYf4P8mH+D/Jh/g/yYf4P8mH+H/JR/Z/xcVhP8WE37/JR7V/yYf4/8mH9//Jh/g/yYe + 4P8mH+D/Jh/g/yYf4P8mHN//JSvk/yNJ8P8jR+//I0jw/yNH7/8mKeP/Jhzf/yYf4P8mH+D/Ixrf/zc8 + 5P+Rn/b/laH3/5Sg9/+UoPf/mqb4/2977/8hG9//JR3g/yYf4P8mHuD/GhLe/7K3+v/M0uf/Ki+L/15k + qP/o6/P/5+bg/xoZF/8AAAD/AAAA/wAAAP8AAAD/Hx4b/7m6tf+Wmsb/Dgxz/xwtuv8kSvX/I0nw/yU0 + 6P8mHd//Jh7g/yYf4P8mHuD/Jh/g/yYf4P8lHuD/Hhbe/0pO6P85OeX7IxvfNSMb3wAjG98AIxvfACMb + 3wAjG98AIxvfACMb3wAjG98AIxvfACMb3wAjG98AIxvfACMb3wAjG98AJiHAACYhwAAmIcAAIBnOAEpO + 4QDY2v4A0ND7AM/P+wDPz/sAz8/7ANDQ+wDLzPsAur/4AHWK7AAmHN8AJhzfACYc3wAlMOctJEDt7CNJ + 8P8lOur/JiLh/yYc3/8mHuD/JR/g/yYe4P8mHt//Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/JR/f/yYe4P8mHuD/Jh/g/yYe3/8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/IBjn/0A8 + rf+dn57/urvL/7a3x/+2t8f/trfH/7a3xv+2t8b/trfG/7a3xv+3uMj/trfG/3t7ff+EhIn/lpqi/2RE + QP9lIx//Zigk/2YpJP9mKCT/ZSgk/2YoJP9lKCT/Zigk/2UoJP9mKCT/Zigk/2UoJP9lKCT/Zikk/2Yp + JP9mKCT/ZSgk/2UoJP9mKCT/Zikk/2UpJP9lKCT/Zikk/2UpJf9mKST/Zigj/2YoI/9mKCL/Zigi/2Yo + Iv9mKCP/ZSgj/2YoI/9mKCP/Zikk/2UoJP9lKCT/Zikk/2YoJf9mKCT/Zygh/2kpGv9mKSX/USZk/y0d + u/+Sl9H/zc3D/8nIw/+utsj/LjDf/yEY1v8lHtX/Jh/f/ycf4v8nIOf/Jx/l/yYf4/8mHuL/JR3i/yId + 4v+Wnc3/zMzD/8vNwv9rcNT/FBDj/4yQzv/OzsH/zczC/3R+0/8cFuL/Jh7g/yYe3/8lH9//Jh7f/yYf + 4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/JR7f/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/f/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/ycg5f8dGab/ExFv/yAauf8nH+b/Jh/f/yYf + 4P8mH+D/JR/f/yYe4P8mH+D/Jh3f/yYl4v8jRe7/I0fv/yNH7/8jSPD/I0Lt/yYi4f8mHd//Jh/g/yMa + 3/83PeT/kp/2/5ah9/+UoPf/lKD3/5Wg9/+Ypfj/Ulvp/x4W3v8mHuD/Ixvf/yMl3//N0/j//////9jc + 5f8VGUX/LDJv/5Sayf8xND7/AAAA/wAAAP8DBAD/P0A7/6mrtf9vc6z/EA95/xosuP8kSfT/I0fw/yNH + 7/UmKOPVJh3f/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYe3/8hGN//JBzguCYf4AcmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYhwAAmIcAAJiHAACAZ + zgBKTuEA2Nr+ANDQ+wDPz/sAz8/7AM/P+wDQ0PsAJhzfACYc3wAmHN8AJhzfACYc3wAmHN8dJhvezSU5 + 6v8kPev/Jh7f/yYd4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yUf4P8mHuD/Jh/g/yYe4P8mHuD/Jh7g/yYf4P8lH9//JR7f/yYf4P8mH+D/Jh/g/yAa + 5v9ISqP/paal/7m6yv+2t8f/trfG/7a3x/+2t8f/trfG/7a3x/+2t8b/u7zM/5SUnP9ubW7/qKu4/3Rr + bP9jJyL/ZSYj/2YoJP9mKCT/Zigk/2UoJP9mKST/Zikk/2YoJP9mKCT/ZSgk/2UoJP9lKCT/ZSgk/2Yp + JP9mKCT/ZSgk/2UoJP9lKCT/Zikk/2YoJP9lKST/Zigj/2coHv9pJxj/aiYY/2knHf9nJyH/aCgh/2go + If9nJyH/aCcd/2gmHP9oJhz/aCYa/2olFf9qJRX/aSUX/2gmGf9pJhn/aCYa/2gnG/9oKBz/aCgb/2oo + FP9gHx3/kHeE/8zPy//Ix8P/yMnI/z4+qf8OC37/FhSA/xkWi/8dGJ7/Hxmz/yIcwv8kHc3/JR7X/yUe + 3P8eFt7/TlPX/8bJxf/KycP/r7TI/yYj4f9kaNn/y83D/8vLw/+Zncz/IRrm/yYe5v8nH+T/Jh/j/yYf + 4f8mH+H/Jh/h/yYe4P8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYf + 4P8mHuD/Jh/g/yUe3/8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8lHt//JR7f/yYf4P8mH+D/Jh7f/yYe + 3/8mHt//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8nH+T/Ix3M/xQTdv8aFpf/Jx/k/yYf + 4f8mH+D/Jh/g/yYf3/8mH+D/JR/f/yUd3/8mH+D/JD/s/yNI8P8jR+//I0bv/yNJ8P8kO+n/Jh7f/yYc + 3/8kGt7/MjDh/5Gc9v+Voff/lKD3/5Sg9/+UoPf/l6L4/5Cf9v88QOX/IRnf/x0U3v9ISOT/8/j9//// + ///m5uX/HR0Y/wAAAP8FCT3/GRp2/wsNKf8JChz/HBxO/1JWmv8uMpT/ERyd/x861f8kSfT/I0fx/yNI + 7/8jP+vEJiThECYe4FsmH+DDJh7g9SYf3/4mH+D/Jh7f/yYf4P8mHt//Jh7g4CYe3zAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmIcAAJiHAACYh + wAAgGc4ASk7hANja/gDQ0PsAz8/7AM/P+wDPz/sAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gmyYd + 3/8jQO3/IjDm/yUa3/8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf3/8lHt//Jh7g/yYe4P8mH+D/JR7f/yUe3/8mH+D/Jh7g/yYf + 4P8iHOX/TFKf/6mpq/+5usr/trfH/7a4x/+2t8f/trfH/7a3xv+2t8f/t7jI/7S1xP90dHb/kJCY/6Gm + sf9lRUH/ZSQf/2YoJP9mKST/Zigk/2YpJP9mKST/ZSgk/2YpJP9mKST/ZSgk/2YpJP9mKCT/Zigk/2Yo + JP9mKCT/Zikk/2YoJP9mKCT/ZSgk/2YpJP9mKSX/Zycf/2gnHv9fLEH/UDRu/0Q4kP9AO6L/PD2v/zw+ + sv86PLL/NTiy/zo3ov88OJ3/Pzqe/0I6lv9FOIf/RTiJ/0c1ff9MMW7/SzFw/08vY/9RK1j/UipY/1oq + Sf9dLUf/WyQz/39ZXf/Cx8z/wcPG/87QyP9VW5X/Cwhz/xUSdv8UEXP/FBBv/xMPbf8UEHP/FRB3/xUR + fP8WEoD/FxKI/xUViv+Xm7P/zczH/8zNx/9aXrX/MTal/8bIxP/LysX/sbXE/ygru/8dF73/Ix3G/yQe + 0P8lHtr/Jh/e/ycf4v8nH+b/Jx/m/ycf5P8mH+L/Jh/h/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe + 4P8lH9//Jh/g/yYf4P8mHuD/Jh7g/yYe3/8mH+D/Jh/g/yYe4P8mHuD/JR/f/yUf4P8mH+D/Jh/g/yYe + 3/8mH+D/Jh7g/yUe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mH+D/Jh/h/ycf4v8aF5b/FRN5/yUf + 0v8nH+T/Jh7g/yYf4P8mH+D/Jh/g/yUf3/8lH+D/Jhzf/yUy5/8jSfH/I0fw/yNG7/8jRu//I0jw/yQ5 + 6v8lMuf/Ijbo/yw96f+MmPb/lqL3/5Sg9/+UoPf/lKD3/5Sf9/+apvj/gY7z/yso4f8WDd7/gIft//// + ////////rq6w/wICA/8AAAD/AAEA/x8fif8lINf/IS7D/yA2z/8aMs7/Hj3i/yNI8v8kSPT/I0bv/yNH + 8P8jR+//JSnjeSYk4QAmHuAAJh/gAyYf4CImH+BBJR7fUCUe32QmH+BqJh7fYiYf3x4mHt8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJiHAACYh + wAAmIcAAIBnOAEpO4QDY2v4A0ND7ACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gCiYe + 4NElHN//KEHs/zJN7f8nI+D/Ihnf/yYf4P8mH+D/Jh/g/yYf3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mHt//Jh7g/yYf4P8mH+D/JR/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe + 4P8mHuD/Ix3j/1JYmf+srbH/ubrJ/7a4x/+2t8b/trfG/7a3xv+2t8b/trfG/7m6yv+np7P/a2tr/6ut + uv+MjJP/YjIt/2UlIP9mKST/Zigk/2YpJP9mKST/Zikk/2UoJP9mKST/Zikk/2YoJP9mKST/Zigk/2Yo + JP9mKST/Zigk/2YpJP9lKCT/Zigk/2YoJP9lKST/Zygg/2MrNP8+PKb/JkXq/yFI9/8fSP3/H0j7/yBJ + +P8eR/j/I035/09v+/9kgf//ZIH//22J//9wi///cIv//3CM//9sh///ZoD7/2aA+/9ngPf/Ynfz/1hw + 8/84Vuz/IkLp/yVE5v8rSuP/O1/p/0Jk6P9Pb+b/N1Ta/x42zP8gNsj/HzPB/x0xvf8cLbf/Gyqu/xsm + pP8ZIZn/FxyN/xUXgv8MC3n/SUyT/87NxP/W0sT/qKmx/yMme/+oqrj/0c/H/8PEw/83Oob/DAlz/xUT + e/8VE33/FxSD/xkWj/8bGJv/HRmq/yAbuP8jHcr/JR7Y/yYf3/8nH+T/Jx/m/yYf4/8mH+H/Jh/g/yYf + 4P8mH+D/JR7f/yYf4P8mH+D/Jh7g/yYf4P8mH9//Jh/g/yYf4P8mH+D/Jh7g/yYf3/8mHt//Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh7g/yYe4P8nH+X/IhzD/xMS + cv8cGKf/JyDm/yYf4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYc3/8lJeL/I0bu/yNH8P8jRu//I0bv/yNH + 8P8jSfD/I0jw/yJI8P8kSvD/eIz2/5mk9/+UoPf/lKD3/5Sg9/+UoPf/mKP4/46c9v8wL+L/HRnf/8HH + 9///////8vLy/zk6Ov8AAAD/AAAA/wEBAP8fKIb/JT/7/yRJ9v8jSvX/I0n0/yNI8v8jRvD/I0bv/yNH + 7/8jSfD/JDrp8CYb3iQmG94AJh7gACYf4AAmH+AAJh/gACUe3wAlHt8AJh/gACYe3wAmH98AJh7fACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYh + wAAmIcAAJiHAACAZzgBKTuEA2Nr+ACUe3wAlHt8AJR7fACUe3wAlHt8AJR7fACUe3wAlHt8AJR7fACUe + 3y4mH+DvIhnf/zY75P+On/f/eYTx/zEx4v8gF9//JBzg/yYe4P8mH+D/Jh/g/yYe3/8lHt//Jh7g/yYf + 4P8mH+D/Jh7f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/JR7g/yMd4v9XXZX/srO5/7i5yP+2t8b/trfH/7a3x/+2t8f/trfG/7a3xv+6u8r/lpaf/3Jy + c/+ytcT/fHR3/2IoI/9lJyL/ZSkk/2UoJP9mKST/Zigk/2YpJP9mKST/Zikk/2YpJP9lKCT/ZSgk/2Yo + JP9lKCT/Zikk/2YoJP9lKCT/ZSgk/2YoJP9mKCT/ZSgk/2kmGP9EOZP/HUr//yJH8/8jR/D/I0bv/yNH + 8P8jR/D/HULv/2B69P+fqff/nKX4/5ul9/+ZpPf/maP3/5mj9/+Zo/f/mqT4/5ql+P+apfj/mqX5/5um + +f+bpvr/kZ/5/0Vl9f8eRPP/IUbz/x5C8P8eQvH/HEDx/yFF8/8kSvX/JEn2/yRJ9v8kSvf/IUj4/x5E + 9f8dRPP/IEXw/yNG7f8jRuv/Hj7f/xw50/9Zctz/dozX/4ue1f87TK3/eYO4/8LFyf/Lzcr/W2CY/wkG + cf8UEHL/FBB0/xQQdP8TEHT/FBFz/xQRc/8UE3b/FRR7/xYVgv8aF5H/HBij/yAbt/8jHcz/JR7b/ycf + 4v8nIOb/Jx/k/yYf4v8mHuD/Jh/g/yYe4P8mHuD/Jh7g/yYf4P8mH9//Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf + 4f8aFpT/FRN5/yQe0v8mH+T/Jh/g/yYe3/8mHuD/Jh/f/yUe3/8mHuD/Jhze/yU66v8jSfH/I0bw/yNH + 7/8jR/D/I0fv/yNG7/8jRu//HULv/09s8v+apff/lZ/3/5Sg9/+UoPf/laD3/5uo+P9ha+3/GA/d/0VG + 5f/y+P3//////4uLjP8AAAD/AAAA/wAAAP8BAAD/HjKG/yRL/P8jR/D/I0fw/yNH8P8jR+//I0bv/yNG + 7/8jR/D/I0Xu/yUm4aImG94AJhveACYe4AAmH+AAJh/gACYf4AAlHt8AJR7fACYf4AAmHt8AJh/fACYe + 3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmIcAAJiHAACYhwAAgGc4AJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuBHJh/f/yYe4P8hGN7/RUnm/5Si9/+ToPb/Vl7p/ysn4f8gGN7/Ixrf/yQd4P8lHuD/JR7f/yYe + 3/8mH+D/Jh/g/yYe3/8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/f/yYf4P8mHuD/Jh7g/yYf + 4P8mH9//Jh/g/yUd4f8kHeD/XGGT/7Ozuf+4ucn/trfH/7a3x/+2t8b/trfG/7a3xv+2t8b/urvM/4yN + lP96en3/srbF/3JlZf9jJB//Zigk/2YpJP9lKST/Zikk/2YoJP9mKST/Zigk/2YpJP9mKST/Zigk/2Yo + JP9mKCT/Zigk/2UoJP9lKCT/Zikk/2YpJP9lKCT/ZSgk/2YoIv9pKB7/NUC6/x9J/P8jRu//I0fv/yNH + 7/8jRu//I0fw/x1C7/9qgfT/n6n3/5ei9/+Xovf/l6L3/5eh9/+Woff/lqH3/5ah9/+Woff/lqH3/5ah + 9/+Woff/lqH3/56n9/95jPb/H0Pv/yNG7/8jR/D/JEfw/yNH8P8jR/D/I0fv/yNG7/8jRvD/Ikbv/zFT + 8P9RbfP/Znz0/22E9f91ivf/d4z3/26H+P9ogfn/VG72/0lo9f9BY/T/OFrz/zBR7/86Xuv/SGrn/zZU + 2v8WLsT/GCy5/xklp/8YIJr/FhuN/xYWg/8WFH3/FRF2/xQPcv8UEHP/FBFy/xMRcf8UEnT/FRN6/xgV + h/8bF5v/Hhmv/yIcxv8lHtj/Jh/h/ycg5/8nH+T/Jh/i/yYe4P8mH+D/Jh7f/yYf4P8mH+D/Jh/f/yYe + 3/8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mHt//Jh/g/yYe + 3/8mHuT/JB3L/xQSdf8bGJv/Jh/k/yYf4f8mH+D/JR/f/yYf4P8mH9//Jh7g/yYc3/8lKOL/I0fv/yNI + 8P8jR+//I0fv/yNH8P8jR/D/I0bv/yFF7/8mSu//fpH2/5uk9/+UoPf/lKD3/5qm+P9+jfP/KSfg/xYL + 3f+Bie7//////+np6f8hISL/AAAA/wAAAP8AAAD/AAAA/xsrdP8kSvz/I0fw/yNH7/8jR+//I0bv/yNH + 8P8jR/D/I0nw/yQy5/cmHd8yJh3fACYd3wAmHd8AJh/gACYf4AAmH+AAJR7fACUe3wAmH+AAJh7fACYf + 3wAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJiHAACYhwAAmIcAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gXiYe3/8mH+D/Jh7g/x8X3v82N+P/fYny/6Gu+f+JlvT/W2Tr/zY35P8pJeH/Ih3f/x8X + 3v8gF97/IRjf/yIZ3/8iGt//Ixvf/yQc3/8kHOD/JBzg/yUd4P8lHeD/JR7g/yYe3/8mH9//Jh7g/yYe + 4P8mH+D/Jh7g/yYe4P8lHeH/JB3e/2NmkP+0tLz/t7jJ/7a3xv+2t8b/trfH/7a4x/+2t8f/trjH/7q7 + zP+IiI//goKI/66ywf9tWFf/ZCMf/2UoJP9mKST/Zigk/2YoJP9mKST/Zigk/2YpJP9mKCT/Zikk/2Uo + JP9lKCT/Zikk/2YpJP9lKST/ZSgk/2YoJP9mKCT/ZSgk/2UpJP9mKST/aScZ/zk2rv8dSf//I0jv/yNH + 8P8jR/D/I0fw/yNH8P8gRO//M1bx/3OI9f+JmPb/i5r2/4ua9v+Kmvb/jpz3/5Ge9/+Qnvf/kJ73/5Cd + 9/+Rnvf/kJ73/5Ce9/+Vovf/aYD1/x5D7/8jRu//I0fw/yRH8P8jR/D/I0fw/yNH8P8jR+//IETv/yxR + 8P+Kmvb/oKn4/56n+P+bpff/mqT3/5mk9/+bpff/nKb3/5ul9/+ZpPf/lqH3/5Sg+P+Mmvf/fI73/2uB + 9v9hePj/VnP7/0Rn+v81V/X/LE7y/yRI7/8cPeT/GzbU/xoxyf8aLbv/Giao/xkhmP8XGor/FxaB/xYT + ev8TEHL/Eg9u/xMQb/8UEXX/FhOB/xoWmP8fGq//IxzJ/yUf2v8nH+T/Jx/m/yYf4/8mH+H/Jh7g/yYf + 3/8mHt//Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh7f/yYe + 4P8mHuD/Jh7g/ycf5v8eGan/ExJv/yAat/8nIOn/Jh/g/yYf4P8mHuD/Jh/f/yYf4P8mH+D/Jhze/yQ3 + 6P8jSfD/I0bv/yNG7/8jR+//I0fv/yNG7/8jR/D/HkPv/zla8f+OnPf/m6T3/5ql+P+NmfT/Nzjj/x4T + 3v8iIuH/w8r4//////+HiIn/AAAA/wAAAP8AAAD/AAAA/wAAAP8YJ27/JEr9/yNH8P8jR+//I0fw/yNH + 8P8jR+//I0nw/yQ+6/8mH9+XJh3fACYd3wAmHd8AJh3fACYf4AAmH+AAJh/gACUe3wAlHt8AJh/gACYe + 3wAmH98AJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACUe3wAlHt8AJR7fACUe3wAlHt8AJR7fACUe3wAlHt8AJR7fACUe3wAlHt8AJR7fACUe + 3wAlHt8AJR7fACUe330mH+D/Jh/g/yYf4P8mH+D/IRjf/yUg3/9XX+r/kZ72/6Kv+f+Wo/f/gY/z/3B7 + 7/9kae3/VVzp/0pR5/8/RuX/ODfj/zMt4/8uK+H/Kyfg/ykm4P8mI+D/Ih/f/yIe3v8fF9//Hxbe/x8X + 3v8gGN//IBjf/yAY3/8gGN//IBfg/x4X4f9VW5X/srO5/7i5yP+2t8b/trfG/7a3x/+2t8b/trfH/7a3 + x/+6u8z/iYmQ/4qJkP+us8D/bFRT/2QkH/9lKCT/Zigk/2UpJP9lKCT/Zikk/2YoJP9mKCT/Zikk/2Yo + JP9mKCT/ZSgk/2YoJP9lKST/Zikk/2YoJP9lKCT/Zikk/2YpJP9mKST/Zikk/2goHP9WJ1f/KC7f/yA8 + 9v8jQe//I0Pu/yNF7v8jRu7/I0fv/yBF7/8fRe//J03w/y5V8f8tVPH/LFPx/zNZ8f85XPL/OVzy/zhc + 8v84XPL/OFzy/zhc8v84XPL/OVzx/ypQ8f8hR/D/I0nw/yNJ8P8jSfD/I0jw/yNI8P8jSPD/I0jw/yJG + 7/8kSPD/VG/z/4CS9v+Qnff/mqX3/5ul9/+Zo/f/mKL3/5ah9/+VoPf/laD3/5Wh9/+Woff/lqH3/5ij + 9/+bpfj/nab4/5ql9/+Xovf/lKD4/4iY9/94jfb/aoH3/1h1+f9Gafr/NFj5/yxQ9f8iSPL/HEDt/x07 + 3/8dNtH/GzHE/xsqsf8bI57/GB2P/xYXgf8VE3b/ExBu/xMQcv8XE4L/GhWZ/x8atP8jHM//Jh7c/ycf + 5f8mH+b/Jh7k/yYf4/8mHuH/Jh7g/yYf4P8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/f/yYe3/8mH+L/Jh/h/xoWlP8WFHr/IBu5/yYf4v8mH+L/Jh/g/yYe3/8mH+D/Jh/g/yYd + 3/8lIuH/I0Pt/yNI8P8jRu//I0bv/yNH8P8jR+//I0fw/yNH7/8dQu//Olrx/4GS9v+Kmvf/P1ft/x4s + 5v8cMej/RmXw//X7///t7Or/ISIi/wAAAP8AAAD/AAAA/wAAAP8AAAD/Dhxi/yVK/f8jR/D/I0fw/yNH + 7/8jR+//I0fw/yNF7/8lJuLeJh3fGCYd3wAmHd8AJh3fACYd3wAmH+AAJh/gACYf4AAlHt8AJR7fACYf + 4AAmHt8AJh/fACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+CaJh/g/yYe4P8mH+D/Jh/g/yYf4P8kHN//Hhbf/zIy4/9jbe3/jJn1/56r + +f+irvr/oq/6/5+t+f+dq/n/m6r5/5un+P+Wo/f/jZv2/4aV9P+AjvP/eojy/3OA8P9xfe//a2/u/2xv + 7v9ma+3/WmLr/1tj6/9aYer/Tlbp/01V6f9MVe3/YGif/6emrP+5usn/trfH/7a3x/+2t8f/trfG/7a3 + xv+2t8b/urvL/4+Pl/+DhIn/r7PB/21aWf9kIx7/ZSgk/2YoJP9mKST/Zigk/2YoJP9lKST/Zigk/2Yp + JP9mKCT/ZSgk/2UoJP9mKST/ZSgk/2UoJP9mKCT/ZSgk/2YoJP9lKCT/ZSgk/2YoJP9mKCT/aSga/1wm + Rf86Iqz/JCDn/yIi6v8kJuT/JSfi/yUp4/8lK+T/JCrj/yMr5f8iK+b/Iivm/yIr5v8hLOb/Hy/m/x8v + 5v8fL+b/Hy/m/x8w5v8fMOb/HzLn/x806P8iNun/JDjp/yQ46f8kOOj/JDjp/yQ86v8kPOr/JEDt/yND + 7v8jR+7/Ikjv/x1E8P8jSvD/N1ry/0xq8/9he/T/eIz1/4eW9v+Qnvf/maX3/5ul9/+ao/f/maP3/5ei + 9/+Woff/laD3/5Sf9/+UoPf/laD3/5Wg9/+Yovf/mqT3/5ym+P+apff/mKP3/5Sg9/+Glvb/dYn2/2Z9 + 9f9Sb/b/PV/3/y9U+P8mTfb/HkTy/x1C8P8ePuT/HjnT/x0zxf8dK6//GiOa/xYah/8WFnz/FhN5/xcT + hP8bFZr/IBmv/yIbxf8kHdX/Jh/d/ycf5f8nIOb/Jx/l/yce5P8nH+P/Jh/j/yYf4v8mHuH/Jh/h/yYe + 4P8mHuD/Jh7h/yYf4v8mH+P/Jx/m/yYf4f8fGrD/FBJ1/xQSdP8dGKH/Jh/g/yYf5P8mHuD/Jh7g/yYe + 4P8mH+D/Jhzf/yUs5P8jSPD/I0fw/yNG7/8jR/D/I0fv/yNG7/8jR+//I0fv/x5C7/8mS/H/K0/w/x5E + 7/8jSvD/Fj/v/4qk+f//////lpaX/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/w8cWP8mSvj/I0fx/yNH + 7/8jR+//I0jw/yNH8P8lLeX6JhzfTiYd3wAmHd8AJh3fACYd3wAmHd8AJh/gACYf4AAmH+AAJR7fACUe + 3wAmH+AAJh7fACYf3wAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAIBfeACAX3gAgF94AIBfeACAX3gAgF94AIBfeACAX3gAgF94AIBfeACAX + 3gAgF94AIBfeACAX3gAgF94AJh/gsCYf4P8mHuD/JBvg/yEZ3/8eF9//Hhbe/x0V3v8ZD97/HhXe/y8u + 4f9MT+j/WWTr/2Rw7f9lcO3/ZHDt/2Vy7f9xfe//cn7w/3yG8v+GkPT/jJn1/5Sj9/+hrfr/oq76/6Cs + +f+cqfn/nKn5/5uo+P+bqPj/mqj4/5mm+P+Zpvj/m6n//4GHuP+UlJn/urvM/7a3x/+2t8f/trfG/7a3 + xv+2t8b/trfG/7q7y/+XmKL/eHl7/7W4yP92bnD/YyYh/2UnIv9lKST/Zigk/2YoJP9lKCT/ZSkk/2Yo + JP9mKCT/Zigk/2YoJP9mKCT/Zikk/2YoJP9lKCT/Zigk/2YpJP9lKCT/ZSgk/2UpJP9mKST/Zigk/2Yo + JP9oKRv/aCkf/1UnXf83IrT/Jh7j/yIc6v8lHOL/Jh3f/yYc3/8mHN7/Jhzf/yYb3v8mHN//Jhzf/yYc + 3/8mHd//Jh3f/yYd3/8mHd//Jh3f/yYd3/8mHt//Jh3f/yYd3/8mHd//Jh7f/yYd3/8lHd//Jh7e/yYe + 3/8lIuH/JSfj/yUr5P8lMeb/Izfp/x866/8cPO3/HUHu/yBG8P8nTvH/OFzy/0dm8v9YdPT/boT1/4CQ + 9v+ImPb/kZ73/5qk9/+bpff/m6T3/5qk9/+Yovf/l6H3/5Wg9/+Un/f/lKD3/5Sg9/+VoPf/mKL3/5uk + 9/+cpvj/mqT3/5ij9/+Mm/f/fI/2/2yC9f9WcPT/PmD1/y9T9v8kS/j/HUT1/x5E8/8fQu//ID7f/x85 + z/8dMLn/Gyah/xgfjv8WGIL/GBWB/xYRiP8RDJj/GROn/yEbuv8jHMv/JB3T/yUf2P8mH9z/Jh/f/ycf + 5P8nH+b/Jx/m/ycf4v8mH93/JR7W/yAatv8ZFo3/FBN4/xUTe/8VE3r/ExJz/xsXmP8mHtz/Jh/l/yYf + 3/8mH+D/Jh/g/yYf3/8mHd//JDXo/yNJ8P8jR+//I0fv/yNH7/8jRu//I0fw/yNH7/8jR+//IUXv/yBF + 8P8jR/D/IETv/yNJ8P/O2/7/+Pf1/zMzNP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8QGkH/Jknt/yNH + 8v8jR+//I0fw/yNI8P8lMeb/JhzffCYc3wAmHd8AJh3fACYd3wAmHd8AJh3fACYf4AAmH+AAJh/gACUe + 3wAlHt8AJh/gACYe3wAmH98AJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACAX3gAgF94AIBfeACAX3gAgF94AIBfeACAX3gAgF94AIBfeACAX + 3gAgF94AIBfeACAX3gAgF94AIBfeAh8X378dFN7/HBbe/yYl4P83M+P/SEfm/1xk6f9xduv/eoDu/zM0 + 4v8hF9//Hxbf/x8Y3v8gGd7/IBne/yAZ3v8hGt//Ihvf/yIa3v8iGt//Ix7f/ysr4f82OOT/SEvn/2Zy + 7v+LmPX/m6j4/5qm+f+Zpfj/mKP4/5ii+P+Xovj/lqL4/5ij/f+Jktf/hIWL/7i5yP+2t8f/trfH/7q7 + y/+4ucn/trfH/7a3x/+6u8v/pqaz/29wcP+ys8L/kZSd/2I9OP9lIx7/Zigk/2YpJP9mKCT/ZSgk/2Uo + JP9mKCT/ZSgk/2UoJP9mKCT/ZSgk/2UoJP9mKCT/Zigk/2YoJP9lKCT/Zigk/2YoJP9lKST/Zikk/2Yo + JP9mKST/Zikl/2YpI/9pKRj/Zykg/1goVv87I6f/Jh/f/yIe7P8kHuX/Jh/g/yYf3/8mHt//Jh/g/yYf + 4P8mHuD/Jh7f/yYe3/8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh7g/yYe4P8mHt//Jh7f/yYe + 4P8mHuD/Jh3g/yYd3/8mHN//Jhze/yYd3/8mHd//JiLh/yYn4/8lK+T/IzHm/yA16f8cOez/HD7t/x5E + 7/8hSPD/KlHx/zlc8v9IZfL/VnP0/2mA9P98jfX/hpb2/46c9v+Yo/f/m6X3/5qk9/+apPf/maP3/5ii + 9/+Woff/laD3/5Wg9/+VoPf/l6H3/5qk9/+cpvj/mqT3/5ij+P+Mm/b/eo72/2Z99f9QbfP/Olry/ytQ + 9P8hSPb/HUT3/x9F9f8gRfL/IUHk/xo10P87TMD/ZG2q/zE4j/8UFX//ExB1/wwJd/8RDYH/GBOJ/xkW + kf8aF5r/Gxed/xsXnv8aF5f/GRaN/xcUgP8UEnX/FBJ1/xUTev8VE3v/FBN7/xQTev8TEnL/GheQ/yUe + 1v8nIOf/Jh7f/yYf4P8mH+D/Jh7g/yYe3/8kPOr/I0nw/yNG7/8jR+//I0fw/yNG8P8jRu//I0fv/yNH + 7/8jR+//I0fv/xo/7/9OcPP//f///7Szsf8BAAH/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/EBtD/yZK + 7v8jR/L/I0jw/yNI8P8lMeb/JhzfliYc3wAmHN8AJh3fACYd3wAmHd8AJh3fACYd3wAmH+AAJh/gACYf + 4AAlHt8AJR7fACYf4AAmHt8AJh/fACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AA9OeQAPTnkAD055AA9OeQAPTnkAD055AA9OeQAPTnkAD05 + 5AA9OeQAPTnkAD055AA9OeQAPTnkAD055AtDQ+XNZGzq/5GT8f+psPX/wsb5/9TU/P/V1/z/2dr8/9re + /f9OVeb/IBff/yYf4P8mHuD/Jh/g/yYf4P8mHt//Jh/g/yYe4P8mHuD/JR7g/yUe4P8kGt//Ihjf/x8W + 3v8eF97/MC7i/1Ja6f9xfPD/go7z/4eW9P+LmfX/j532/5Og9v+Zpvn/mqf4/3p9j/+rq7b/ubrK/7S1 + xP+UlJ3/q6u5/7i5yf+2t8b/t7jI/7O0w/9zdHX/m5ym/7i7y/95eXv/Yjcy/2UjHv9mKCP/Zikk/2Yo + JP9mKCT/Zigk/2UoJP9mKST/Zigk/2YoJP9mKST/Zikk/2YoJP9lKCT/Zigk/2YoJP9mKCT/Zikk/2Yp + JP9lKCT/Zikk/2YpJP9lKST/Zigl/2YoIv9pKBn/aSkb/1spR/9BJJT/KyDS/yIc6f8jG+X/JR3g/yYf + 4P8mH+D/Jh/g/yYe3/8mHt//Jh7f/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mHuD/Jh/g/yYf3/8mHt//Jh/g/yYe4P8mHuD/Jh/g/yYe4P8mHd//Jhzf/yYc3/8mHd//Jh7g/yYj + 4P8lKOP/JC7l/yIz6P8fOev/HDzt/xxB7v8dRfD/H0bw/yhO8f81WPH/QmDy/09s8/9he/T/dIf1/4KS + 9v+Kmvb/lKH3/5ul9/+apff/mqT3/5qk9/+Yovf/lqH3/5Sg9/+VoPf/l6L3/5uk9/+bpvf/mqX3/5ei + 9/+Hl/b/doj1/1958/9FZPP/M1Ty/yVM8/8QOvT/V3b6/+Tv//+dtfn/Gjzh/zNM0f9pdbn/QUud/xYa + hf8TEnX/Eg9t/xMPbf8TEHD/ExFz/xQSdv8UE3j/FRN7/xUTe/8VE3r/FRN7/xQTev8VE3v/FRJ6/xQS + dP8YFoj/JB7P/ycf5/8mHuD/Jh/g/yYf4P8mHd//JiLg/yNA7P8jSfD/I0fv/yNH8P8jRvD/I0bv/yNG + 7/8jR+//I0fw/yNG7/8XPO7/lq36//////9VVFX/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/xAY + N/8mSOj/I0n0/yNH8P8lMOb/Jh3flCYe4AUmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJR7fACUe3wAmH+AAJh7fACYf3wAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAx8v6AMfL+gDHy/oAx8v6AMfL+gDHy/oAx8v6AMfL + +gDHy/oAx8v6AMfL+gDHy/oAx8v6AMfL+gDHy/og0NL74dfZ/P/Z2vz/19f8/9PT/P/Q0Pv/0M/7/8/Q + +//W2Pz/aWzq/x0V3v8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mH9//Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/JR7f/yIa3/8fF9//IBnf/yUi4P8tLuH/MjTi/zg44/88POT/QkHm/1Ja8f9sdLH/k5SU/7y+ + zv+urr3/YmJg/4mKkP+6u8z/trfG/7a3xv+7vMz/k5Sb/3R0df+4ucj/srXE/3t7f/9jPjr/ZCUf/2Ul + IP9mJyP/Zigk/2YoJP9mKCT/Zikk/2YoJP9mKCT/Zigk/2UoJP9lKCT/ZSgk/2UoJP9mKST/Zigk/2Yo + JP9lKST/Zigk/2YoJP9mKCT/Zigk/2YpJP9mJyP/Zici/2UkIP9oJRf/aSQS/14nNf9ILIH/LSnI/x4Z + 4f8fF+H/JBvg/yYe3/8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7f/yYe4P8mH+D/Jh/g/yYe + 4P8mH9//Jh/g/yYf4P8mH9//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mHuD/Jh3f/yYc3/8mHd//Jh7f/yYi4f8mJ+P/JSzk/yQ15/8iOur/Hz7t/xxB7v8bQ/D/HUXw/x5F + 8P8jSfD/L1Pw/zta8f9JZvP/Wnb0/22C9f9+j/X/iJj2/5Kg9/+bpPf/mqT3/5ql9/+ao/f/l6L3/5Wg + 9/+Voff/maP3/5ul9/+bpff/mqX4/5Ge9/9/kfb/Y3r0/3SN9f/X4P7/r8P8/yBH8/85Yfn/0eH//563 + /P8hROn/IDrV/x4xu/8aJJ//GBmH/xUSd/8UEHT/FBF1/xQSef8UE3r/FRN7/xUTe/8VE3r/FRN7/xUS + e/8VEnv/FBN1/xYUgP8iHMX/Jx/n/yYf4P8mHuD/Jh/g/yYd3/8lI+H/JD/r/yNJ8P8jR/D/I0fw/yNH + 7/8jR+//I0fw/yNH8P8fQ+//Jkzv/9jk///d29b/DQ0O/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8PFSj/JUri/yRE8v8mK+T6JhzfeSYd3wAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmH+AAJh/gACUe3wAlHt8AJh/gACYe3wAmH98AJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gANLS+wDS0vsA0tL7ANLS+wDS0vsA0tL7ANLS + +wDS0vsA0tL7ANLS+wDS0vsA0tL7ANLS+wDS0vsA0tL7NdHR+/XPz/v/z8/7/8/P+//Pz/v/z8/6/87P + +//Oz/v/29z9/4WE7/8bFN7/Jh7g/yYe4P8mH+D/Jh/g/yUf3/8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/yYe3/8mHuD/Jh7g/yYe4P8lHN//Ixrf/yIZ3/8hGN7/IBje/x8V3v8YEd//XWnh/3+B + iP+ys7//tbbG/4SFiv+Xl6H/ubrK/7a3xv+2t8b/t7nI/7W2xv95eXz/fn6B/7i6yv+6vc3/jI+W/2hT + Uf9iMSz/Yygj/2QkH/9lJB//ZSQg/2UkIP9lJCD/ZSQg/2UlIP9lJSD/ZSUg/2YmIf9lJiH/ZiUh/2Yl + IP9lJSD/ZSUg/2UkIP9mJCD/ZSUg/2UkIP9kJB//ZCgk/2MtJ/9jNC//Y0A9/2pUUv92aGP/hX51/5CS + nf+Bh83/SU3b/yUg4P8cFOL/Ihrh/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh7f/yYe3/8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/f/yYe3/8mHt//Jh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh3f/yYc3v8mHN//Jh3f/yYi4P8mKOP/JS3l/yQ1 + 5/8jPev/IkHt/yBF7/8dRPD/HEPw/x1E8P8dQ/D/IUbw/yxQ8P85WfH/SGXy/1p18/9xhfX/gJH1/4yc + 9v+YpPj/m6X3/5ql9/+ZpPf/l6L3/5ag9/+Xoff/mqP3/5ei9/+rtPn/6uz+/93j/f95jfX/cYr1/9Xf + /f+Wrvn/HETy/xxD9f8eRfb/H0b1/yJD5/8gOtH/Hiyx/xkekv8WFH3/Ew9z/xMQdP8UEnj/FRN6/xUT + e/8VE3v/FRN7/xUTe/8TEnb/FRN7/yAbuf8mH+X/Jh/i/yYf3/8mH+D/Jhzf/yYg4P8lOOj/I0nw/yNI + 8P8jR/D/I0fv/yNG8P8jRu//Gj7v/1R19P//////hYSE/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/DhUq/yI23f8lIuXhJhzfUSYc3wAmHd8AJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmHt8AJh/fACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ADOz/oAzs/6AM7P+gDOz/oAzs/6AM7P + +gDOz/oAzs/6AM7P+gDOz/oAzs/6AM7P+gDOz/oAzs/6AM7P+jXPz/v1z8/7/8/P+//Pz/v/z8/7/8/P + +v/R0fv/19f8/+Xl//+MkPD/HBbe/yUe4P8mH+D/Jh/g/yYf4P8lH9//Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYe4P8mH+D/Jh7g/yYf3/8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8iGd//MC/h/4ya + /f+AhrX/j4+S/7u9zf+6u8v/uLrK/7a3xv+2t8b/trfH/7a3xv+5usr/r7C+/3N0dv9zc3X/oaKt/7q7 + y/+prbr/iImP/3dub/9vXl//aVBP/2VGQv9kPzv/Yzw4/2M2Mv9jNTD/YzUw/2MzL/9jMCv/Yy8q/2Ix + LP9jNDD/YzQw/2M0MP9jNjP/ZDw4/2VAPP9mSkf/a1lY/3Npaf9+eX3/jI6V/5ufqf+orbn/srXF/7q8 + zP+8vcr/wsPI/77By/+codL/Ymjb/zEx4v8iGuX/Jh7g/yYf3/8mHuD/Jh/g/yYe3/8mHuD/Jh7f/yUf + 3/8mH+D/Jh/g/yYf4P8mHuD/Jh7f/yYf4P8mH+D/Jh7g/yYf4P8lH9//Jh7f/yYf3/8lHt//Jh7g/yUe + 3/8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/JR/g/yUe4P8mH9//Jh7f/yYe4P8mHuD/Jhzf/yUc + 3v8mHN//Jh3f/yYj4f8lKeT/JTHm/yQ56v8kQOz/I0Xv/yJH8P8gRvD/HUTw/xxC8P8dQu//HULv/yNJ + 8P8xVPD/QF/y/1Zy8/9sgvX/gJH2/46c9/+Zo/j/nKX3/5ul9/+VoPf/oq34/+Xo/f/k5/3/oKn4/6ix + +P/u7/7/w8r7/3aJ9f9kfPP/R2by/zJU8f8iSPP/HUT2/x1F9v8gRO//Ij7c/x8xvP8ZIJf/FhR8/xQP + c/8UEHb/FRJ6/xUTev8UE3v/FRN7/xQSd/8TEnT/Hhqp/yYf4f8mH+T/Jh/g/yYf4P8mHeD/Jh3f/yUt + 5P8kQez/I0nw/yNJ8f8jR/D/I0bv/xY97v+ftv3/+/n0/zExMv8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/xIWKP82MtDKIBblKCYc3wAmHN8AJh3fACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh7fACYf3wAmHt8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AA0dH7ANHR+wDR0fsA0dH7ANHR + +wDR0fsA0dH7ANHR+wDR0fsA0dH7ANHR+wDR0fsA0dH7ANHR+wDR0fs0z8/79c/P+//Q0Pv/1dT8/9nZ + /P/Z2v3/y8/6/66z9f+Ei+//QEDk/yIa3/8mH+D/Jh/g/yUe3/8lHt//Jh/g/yYf4P8mHuD/Jh/g/yYf + 3/8mH9//Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8kHOD/Ihzf/3B7 + 7/+bp/v/kp3x/3Z4h/+pqbL/urzM/7a3x/+2t8b/trfH/7a3x/+2t8b/trfG/7q7y/+xssD/f36D/2Ni + Yf90dHX/k5Ob/7K0wv+2usr/rbG//6eruP+kqLP/mp2n/5ebpf+Qk5v/jpKa/4+Tmv+PkZn/iIaN/4aE + iv+Hh43/j5Ka/4+Smv+Pkpr/kJOc/5eapP+bnqn/pKm0/6ituf+wtMP/ubzM/7u8zf+9vs7/vr/Q/7y9 + zv+7vM3/uLrK/7Cxv/+oqLP/oqOk/5SVkv5vdITpSk2hwScj3scmHuH/Jh7g/yYe4P8mH+D/JR/g/yYe + 3/8lH9//Jh/g/yYf4P8mHuD/Jh7g/yYf3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8lH9//JR7f/yYf4P8mHuD/JR/f/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8lHt//Jh/f/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jhze/yYc3/8mHN//JR/g/yUl4v8lLOX/JTbo/yQ+7P8kRO7/I0jw/yNJ + 8P8hR/D/H0Xw/xxC7/8cQe//HEHv/yNI8P8zVPH/Q2Hx/1t18/93ivX/hZX2/6Cs+P/l5/7/5uj9/56o + +P+irfn/6ez9/8DH+/+WoPj/m6X3/5ql+P+Pnff/fY/2/2N89P9GZfL/MFPy/x9G9P8cRPb/IUXy/yI/ + 3P8dLbT/GBqJ/xQQdP8TEHT/FBJ5/xUTev8VE3v/FRN5/xQSdP8bF5n/JR7Y/ycf5v8mH+D/Jh/g/yYf + 4P8mHN//JiHg/yUw5v8kP+z/I0jv/x9G8P8rUvH/4O3//8PBvf8CAgP/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8hIiL/rK27hCAW5QAmHN8AJhzfACYd3wAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYe3wAmH98AJh7fACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAMvM+wDLzPsAy8z7AMvM + +wDLzPsAy8z7AMvM+wDLzPsAy8z7AMvM+wDLzPsAy8z7AMvM+wDLzPsAy8z7N9XV/PjZ2/3/1tf8/73D + +P+bn/P/aG/r/0FB5P8lJOD/GxTe/yEZ3v8nH+D/Jh/f/yYe4P8lHt//Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mH9//Jh/f/yYe3/8mHt//JR/g/yYe4P8lH9//JR7f/yYe4P8mHuD/Jh/f/yYe3/8lHt//Hhfe/1hi + 6/+ap/j/laD3/5ai/v+GjtD/fHt+/7Kzv/+6vMv/trfG/7a3xv+2t8b/trfG/7a3xv+2t8b/ubrK/7i5 + yf+amqT/ent+/3Bwcf+XmKL/u7zM/7m6yv+6u8v/urvL/7q7zP+6u8z/urzM/7q7zP+6vMz/urzM/7q8 + zf+6vc3/urzM/7q7zP+6u8v/urvM/7q8zP+7vMz/u7zN/76/z/++wND/vL7P/7u8zP+3uMj/rK27/6Wm + sf+XmKL/iYqQ/H19gONzc3W9ampqnmZnZm1mZmRCbmxhJmJoegQoJ9sJJR3iSSYe4KUmHuDsJh7g/yYf + 3/8mH+D/Jh/g/yYf4P8mHt//Jh7f/yUf3/8lH9//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yUe3/8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8lHt//JR7f/yYf + 3/8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yUe3/8mHd//Jhzf/yYc3/8mHt//JiTh/yUp + 4/8lNOf/JDzr/yND7f8jSPD/I0nw/yNJ8f8hRu//HkPv/x1C7/8cQe//HkPv/yZK8P9ObfP/0dv9/9bd + /f+Jmvb/qLL5/+3u/v+3v/n/k573/5Sf9/+VoPf/l6H3/5qk9/+bpff/mqX4/42b9v90iPX/T23y/ytO + 8f8bQvP/Ikn3/yNG6/8gNcb/GSCV/xQSeP8UD3P/FRJ4/xUTe/8UE3r/FBJ0/xcVhP8hHL//Jh/l/ycf + 5P8mH+D/Jh7g/yYe4P8mHN7/Jh/f/yUn4/8bKOX/WHDx//////9xcHD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/LCws/8HBvavGxsYAxsbGAMbGxgDGxsYAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmHt8AJh/fACYe3wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACJmvAAiZrwAIma + 8ACJmvAAiZrwAIma8ACJmvAAiZrwAIma8ACJmvAAiZrwAIma8ACJmvAAiZrwAIma8EejqvT/hYrw/1JW + 5/8yMuL/HRrf/xwU3v8fF9//JBzg/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yUf4P8lHuD/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8lH+D/Jh/f/yYe4P8mH+D/Jh/g/yYe3/8mH9//Hxfe/0hM + 5/+Vovf/lqL3/5Sg9/+UoPj/l6P+/4GIvv99fH7/r6+5/7y9zv+3uMj/trfG/7a3xv+2t8b/trfH/7a3 + x/+3uMf/u7zM/7m6yv+ztMP/t7fH/7a3xv+2t8b/trfH/7a3x/+2t8b/trfH/7a3x/+2t8f/trfH/7a3 + x/+2t8b/trfG/7a3xv+3uMj/ubrK/7u8zf+6u8z/urvL/7W2xf+rrLn/oqOu/5WVnv+FhYv7e3t+2XBw + crNqammVZ2dlYGdnZTpoaGYfaGdmBGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gKiYf + 4HwmHuDMJh/g/yYe4P8mH+D/Jh7f/yUf3/8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/JR7f/yYe + 4P8mHt//Jh7g/yYe4P8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/f/yYf + 4P8mHuD/JR7f/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh7g/yYd + 4P8mHd//Jhzf/yYe3/8mIuH/JSnk/yUz5/8kPOv/JETt/yNI8P8jSfH/I0nw/yNH7/8TOe7/U3T0/9Xh + /f+ds/n/HkXw/3KL9v/i6P7/jJ33/4ya9v+bpfj/m6X4/5ei9/+UoPf/lKD3/5Wg9/+Xovf/mqT3/5yl + +P+Kmff/VnDz/yJH7/8fRPL/JEr3/yRI8f8hO9T/GyWi/xYVfv8UD3L/FBF2/xUTe/8UEnb/FBN3/xwY + n/8kHtT/JyDn/yYf4/8mH+D/Jh/g/yYe4P8mHN//GhHd/56j9P/7+vP/Ly8w/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/zg4OP/Dw8TPxsbGDcbGxgDGxsYAxsbGAMbGxgDGxsYAxsbGAMbG + xgDGxsYAxsbGAMbGxgDGxsYAxsbGAMbGxgDGxsYAJR7fACUe3wAmH+AAJh7fACYf3wAmHt8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AALSzhAC0s + 4QAtLOEALSzhAC0s4QAtLOEALSzhAC0s4QAtLOEALSzhAC0s4QAtLOEALSzhAC0s4QAtLOFIJiTf/xwV + 3/8dFd7/IRnf/yYd4P8mH+D/Jh7g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/JR/g/yYf3/8mH9//Jh7g/yYe4P8mHuD/Ihrf/zY4 + 5PmNm/XsmKT48JSf9/qUoPf9lKD3/5Sg+P+Wo///g4vG/3Z3f/+cnKH/uLnJ/7u8zP+3uMj/trfG/7a3 + xv+2t8b/trfG/7a4x/+3uMf/t7nI/7a3xv+2t8b/trfG/7a3x/+2t8f/trfG/7a3xv+2t8f/trfH/7e4 + yP+5usr/u7zM/7q7y/+6usv/tLXF/6mquP+goKv/kZKZ/4SEifR4eHrRb29wrWlpaItmZmVTZ2ZmNGho + ZxdwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf + 4AAmH+AAJh7gDSYe30kmHt+gJh/g5iYf4P8mH9//Jh/g/yYf4P8mH9//Jh/g/yYf4P8mH+D/Jh7f/yYe + 3/8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYe + 3/8mH+D/Jh/g/yUe3/8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe + 3/8mH+D/Jh/g/yYf4P8mH+D/Jh3g/yYc3/8mHN//Jh7f/yYi4P8lKeP/JTPn/yQ97P8gQO3/Iknw/6a7 + +v/a5v7/Y4P1/w007v+Goff/ucv8/ytQ8P8wUfD/UW3z/3GF9f+Pnfb/m6b4/5qk9/+Woff/laD3/5Sg + 9/+VoPf/mKL3/52n+P92i/X/Kk7w/x9D7v8jR/H/JEn1/yRK9f8iQN7/HSux/xcZiP8UEHP/FBB2/xQS + ef8UEnT/FhSA/x4arv8mHtv/Jx/n/yYf4/8mH+D/Ihrf/yop4f/X4f7/1dPN/wUFBf8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP9MTE3/zc3N7sTExRzExMUAxMTFAMTExQDExMUAxMTFAMTE + xQDExMUAxMTFAMTExQDExMUAxMTFAMTExQDExMUAxMTFAMTExQAlHt8AJh/gACYe3wAmH98AJh7fACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACMb + 3wAjG98AIxvfACMb3wAjG98AIxvfACMb3wAjG98AIxvfACMb3wAjG98AIxvfACMb3wAjG98AIxvfSCQb + 3/8lHt//JB3g/yEY3/8cFN7/GhPe/xwW3v8dF97/Hxff/yYe4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf + 4P8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mHt//Jh7g/yYf4P8mH+D/Jh7f/yYf4P8mHuD/Jh/g/x4V + 3vNNT+hMnar4IpSg9yiUn/cxlKD3P5Wg91CUn/dslaD4hZej/5mNmOWsdXiTxH18fvOfoKr/trfG/7y8 + zf+6u8z/uLnI/7e3x/+3uMf/trfG/7a3x/+2t8f/trfH/7a3x/+3uMf/uLnJ/7q7y/+7vM3/urzM/7m6 + yv+1tsb/qqu4/6Chqv+RkZn/goKI8HZ2eMtub26maGhngGdnZU9nZ2YraGhnEW9vcABpaWgAZmZlAGdm + ZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4CAmHuBmJh7fuSYe3/kmHuD/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH9//Jh/f/yUf3/8lHt//Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh7f/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe3/8mHd//JR3e/yYc3/8mHt//GRjg/3KB + 7//b4v3/o7b5/x9E7v8oTvD/wdH8/5Ss+f8cQe//HkLv/x1B8P8fRO//MVPw/1Jv8/94jPX/laH3/5ym + +P+Zo/f/laD3/5Sg9/+Un/f/nqf3/3eM9f8jSO//IUXw/yNH7/8jR/D/I0n0/yRK9v8jROn/HzPC/xke + k/8UEXf/FBBz/xQRdv8UEnX/FxWH/x8asv8lHtn/Jh/h/x8W3/9OVef//////5OTkv8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/cHBx/8/PzvvDw8Q1w8PEAMPDxADDw8QAw8PEAMPD + xADDw8QAw8PEAMPDxADDw8QAw8PEAMPDxADDw8QAw8PEAMPDxADDw8QAw8PEACYf4AAmHt8AJh/fACYe + 3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAgFt8AIBbfACAW3wAgFt8AIBbfACAW3wAgFt8AIBbfACAW3wAgFt8AIBbfACAW3wAgFt8AIBbfACAW + 31obE97/HRbf/yUh4P84O+P/UU/n/2dp6v96hO7/h4/w/2hy6v8mIeD/JR7g/yYe4P8mHuD/Jh/g/yYf + 4P8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/f/yYe3/8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf + 4P8kHOCQTU/oAJ2q+ACUoPcAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QdmZmIwaWlnjnp7 + feSUlZz/paay/7Gywf+4usn/ubrK/7m7zP+6u8z/ubrK/7m6yv+5usr/t7jI/6+vvf+mp7P/n5+p/5CR + mf+Dg4j1dnZ41G5ub6hoaGh6Z2dlSWdnZidoaGgMbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZm + ZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd + 4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wImHuAzJh/ggiYe4M0mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYe3/8mH+D/Jh/g/yYf + 4P8mH+D/Jh7f/yYf4P8mH9//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7f/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/HhTf/z9C + 4//Hy/j/z9T7/0xR5v8QD9//Z3fu/9ji/f9fe/L/Gj/u/yNJ8P8jSfD/Ikfv/x9E7/8cQe//Ikfv/zhZ + 8f9fePP/g5T2/5ik9/+cpff/mKL3/5ah9/+dpvf/Smjy/x1B7/8jR/D/I0fv/yNH7/8jR/D/I0jz/yRK + 9/8jR+//ITrR/xslov8WFX7/FBBy/xMPb/8TEG7PHhqoVyUe5FQGANtjjJHxuf////9sbG3/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/CAgI/Kampv7Kysv/xMTEYMTExADExMQAxMTEAMTE + xADExMQAxMTEAMTExADExMQAxMTEAMTExADExMQAxMTEAMTExADExMQAxMTEAMTExADExMQAJh7fACYf + 3wAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAI0bvACNH7wAjR+8AI0bvACNG + 7wAjRu8AP0DkAD9A5AA/QOQAP0DkAD9A5AA/QOQAP0DkAD9A5AA/QOQAP0DkAD9A5AA/QOQAP0DkAD9A + 5AA/QOReZ2vr/4uT8f+ztfb/wcf5/9DT+//Z2v3/2Nj9/+Df/v+vtvb/KSbh/yMc4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYe + 4P8mH+DWJh/gFyYf4ACdqvgAlKD3AJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlp + ZwBoaGcdZ2dmW2xra5lzc3W8fX1/5IWGjPeOj5f9kZKa/omKkPmGhov2f3+D6nh4etFycnOzbGxtmWlp + Z3lnZ2ZJZ2dmJ2lpaBJubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlp + aABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn + 2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AMJh/gRiYf + 4J0mH+DiJh/g/yYf4P8mHuD/Jh7f/yYf4P8mHt//Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYe4P8mHuD/Jh7g/yYe4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH9//JBzg/x4Z + 3/+Wn/H/4OT9/4KL7/8YD93/Lyzh/7zD9/+7wvf/LzDi/yIk4v8lM+j/JD7r/yRF7v8jSe//I0nw/yJH + 8P8eQ+//HEHv/yZL8P9AYfH/Z3/0/4WW9v+Xovf/oKn3/1x38/8cQu//I0bv/yNH7/8jRvD/I0fw/yNG + 8P8jR+//I0fx/yRK9f8kSfT/Ij/d/xwssf8ZIpuoFBN4Dx4aqAAlHuQABgDbAOzr/ab+/v7/VFVV/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/woKCrCrqqvNysrL/8PDxIbDw8QAw8PEAMPD + xADDw8QAw8PEAMPDxADDw8QAw8PEAMPDxADDw8QAw8PEAMPDxADDw8QAw8PEAMPDxADDw8QAw8PEAMPD + xAAmH98AJh7fACYf4AAmH+AAI0fvACNH7wAjRvAAI0fwACNG7wAjR+8AI0fvACNG7wAjR+8AI0fvACNG + 7wAjRu8AI0bvALS99gC0vfYAtL32ALS99gC0vfYAtL32ALS99gC0vfYAtL32ALS99gC0vfYAtL32ALS9 + 9gC0vfYAtL32etjZ/f/Y2P3/1dX8/9PT/P/R0Pv/z8/7/8/P+//T0vv/wcT5/zMu4v8iGt//Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mHt//JR7f/yYf4P8mH+D/Jh7f/yYe3/8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf + 4P8mH+DzJh/gPSYf4AAmH+AAnar4AJSg9wCUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZm + YgBpaWcAaGhnAGdnZgBsa2sAampoBWloZxloaGYpaGhmP2dnZkRnZ2YuZ2dmJWhoZh1paWgQcnJzAGxs + bQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9v + cABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJo + egAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gGCYf4F4mH+CuJh7g8yYf3/8mH+D/Jh7f/yYf4P8mH9//Jh/f/yYf4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mHt//Jh/f/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8mHuD/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf3/8mH+D/Jh7g/yYf4P8mH+D/Ixvf/xcQ + 3f9mb+r/2uD8/6ev9P8nI+D/GBLd/4uT8f/e4/3/bnXs/x0U3v8mHd//Jhzf/yYf4P8mJOL/JS/l/yU5 + 6f8kQ+3/I0jw/yNJ8P8hRvD/HkLv/x1D7/8pTfD/PF3x/0lp8/8uUPD/IUXw/yNG7/8jR/D/I0bv/yNH + 8P8jR/D/I0fv/yNH7/8jR+//I0fx/yNJ8/8kSvb/JEjx5CJC5p8gQutFJEXvAc7Z/AL////T8vLy/zMz + M/8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP89PT1MyMjIm8XFxv/Dw8ShxMPEAMTD + xADEw8QAxMPEAMTDxADEw8QAxMPEAMTDxADEw8QAxMPEAMTDxADEw8QAxMPEAMTDxADEw8QAI0fvACNG + 7wAjR/AAI0fvACNH7wAkR/AAI0fwACNH7wAjR+8AI0bwACNH8AAjRu8AI0fvACNH7wAjRu8AI0fvACNH + 7wAjRu8AI0bvACNG7wDU0/wA1NP8ANTT/ADU0/wA1NP8ANTT/ADU0/wA1NP8ANTT/ADU0/wA1NP8ANTT + /ADU0/wA1NP8ANTT/H/Pz/v/z8/7/8/P+//Pz/v/z8/7/8/P+//Pz/v/0dD7/9HS+/9ESOX/IBjf/yYe + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mHt//Jh/g/yYe4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/gcSYf4AAmH+AAJh/gAJ2q+ACUoPcAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ + 0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJy + cwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGho + ZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5s + YQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4CkmH+B4Jh/gxSYf4PwmH+D/JR7f/yYe3/8mH+D/Jh/g/yYe + 4P8mH+D/Jh7g/yYf4P8mHuD/Jh7f/yYf3/8mHuD/Jh7f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/f/yYf4P8mH+D/Jh/g/yYf4P8mH9//Jh/g/yYf4P8kHOD/HBPe/yEe + 3/9ze+z/1tv7/6+39f8vLeH/FhHd/3qD7f/c4fz/oar0/ygm4P8jG9//Jh/g/yYf4P8mHuD/Jh7g/yYd + 3/8mHt//JiLg/yUq5P8lNun/JUHs/yNI7/8jSPD/IUfw/x5E7/8eQu//IUTv/yNH8P8jR+//I0fv/yNH + 7/8jR/D/I0bv/yNG7/8jR+//I0fv/yNG7/8jR+//I0fw/yNH8f8jSPL/I0jx/ho/8LxCYPFx/v//8OXk + 4/8YGBn/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADKZmZmBtfX137DwsP/xMTEycTD + xAnEw8QAxMPEAMTDxADEw8QAxMPEAMTDxADEw8QAxMPEAMTDxADEw8QAxMPEAMTDxADEw8QAxMPEACNH + 7wAjRu8AI0fwACNH7wAjR+8AJEfwACNH8AAjR+8AI0fvACNG8AAjR/AAI0bvACNH7wAjR+8AI0bvACNH + 7wAjR+8AI0bvACNG7wAjRu8AqbL2AKmy9gCpsvYAqbL2AKmy9gCpsvYAqbL2AKmy9gCpsvYAqbL2AKmy + 9gCpsvYAqbL2AKmy9gCpsvaT0dH7/8/P+//Pz/v/z8/7/9DP+//Q0fv/1tX8/9zb/f/R1fz/Q0fl/yAY + 3/8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mHt//Jh/g/yYf4P8lHt//Jh/g/yYf + 4P8mH+D/Jh/fmyYf4AAmH+AAJh/gACYf4ACdqvgAlKD3AJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y + 5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlp + aABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdn + ZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZm + ZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AYmH+A5Jh/gkCYf4NwmHuD/Jh/g/yYf + 4P8mH+D/Jh7g/yYd3/8mHuD/JR7g/yUd3/8lHd//Jh7g/yUd3/8mHuD/Jh7g/yYe3/8lHd//JR3f/yUd + 3/8lHd//JR7g/yUd3/8lHd//JR7f/yUd4P8kHOD/JB3g/yQd4P8kHOD/Ihng/x4U3/8bFN7/JiPg/1RZ + 6P+osPT/1tz8/46U8P8kIuD/NTXj/5qh8v/Z4Pz/n6j0/zIx4v8gGN//Jh/g/yYf4P8mH+D/Jh7g/yYe + 4P8mHuD/Jh7f/yYd4P8mHd//Jh3f/yYg4P8mKOP/JTTo/yRB7P8jR+//I0nw/yNH8P8jR/D/I0fv/yNG + 7/8jR+//I0fv/yNG7/8jR+//I0fw/yNH8P8jRu//I0fw/yNH7/8jR+//I0fv/yNH7/8cQe//QmTy//7/ + ///Pz83/BQUF/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAAY2ZmZgDW1tdkw8PD/8TE + xOrExMQXxMTEAMTExADExMQAxMTEAMTExADExMQAxMTEAMTExADExMQAxMTEAMTExADExMQAI0fvACNG + 7wAjR+8AI0bvACNH8AAjR+8AI0fvACRH8AAjR/AAI0fvACNH7wAjRvAAI0fwACNG7wAjR+8AI0fvACNG + 7wAjR+8AI0fvACNG7wAjRu8AI0bvAMPG+QDDxvkAw8b5AMPG+QDDxvkAw8b5AMPG+QDDxvkAw8b5AMPG + +QDDxvkAw8b5AMPG+QDDxvkAw8b5oNDQ+//Q0Pv/09L8/9nY/f/X2Pz/zc/7/6yy9v+EiO//VVnn/yci + 3/8lHd//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/JR7f/yYf4P8mH+D/Jh/f/yYe + 4P8mHuD/Jh7gySYe3xEmHt8AJh7fACYe3wAmHt8AJh7fAJSg9wCUn/cAlKD3AJWg9wCUn/cAlaD4AJej + /wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGho + ZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdn + ZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZn + ZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AQJh/gUiYf + 4KQmHuDqJh7g/yEa3/8dGN7/Hxrf/x8Z3/8fGd7/Hxrf/x8a3/8fGt//Hxne/x8a3/8fGt7/Hhne/yAa + 3/8iHd//Ix3g/yQe4P8jHt//JB7f/yMd3/8mIOD/KSPg/ygi4P8rJOH/Lynh/zs/4/9YWuj/gYbu/663 + 9f/ByPj/m6Py/1Rd5/9CR+T/hYzu/8fR+f+zvPb/YWjq/ycj4P8hGN//Jh/f/yYe4P8mHuD/Jh/g/yYe + 4P8mHuD/Jh/g/yUe3/8mHuD/Jh/g/yYf4P8mHeD/Jhzf/yYd3/8mH9//Jijj/yU06P8kQOv/I0fv/yNJ + 8P8jSPD/I0fv/yNG7/8jR+//I0fv/yNH8P8jR/D/I0bv/yNH7/8jR/D/I0fv/yNH7/8jRu//Gz/u/1R3 + 9P//////m5ub/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAAyAAAAArS0tIA0tLSQsTE + xPvExMT9xMTELcTExADExMQAxMTEAMTExADExMQAxMTEAMTExADExMQAxMTEAMTExAAjR+8AI0fvACNH + 7wAjRu8AI0fvACNG7wAjR/AAI0fvACNH7wAkR/AAI0fwACNH7wAjR+8AI0bwACNH8AAjRu8AI0fvACNH + 7wAjRu8AI0fvACNH7wAjRu8AI0bvACNG7wDHy/oAx8v6AMfL+gDHy/oAx8v6AMfL+gDHy/oAx8v6AMfL + +gDHy/oAx8v6AMfL+gDHy/oAx8v6AMfL+p/b2v3/0tX8/8DD+f+WnfL/bW/r/0JD5P8nI+D/HBff/x8W + 3/8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh7g/yYe4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g4CYe4CQmHt8AJh7fACYe3wAmHt8AJh7fACYe3wCUoPcAlJ/3AJSg9wCVoPcAlJ/3AJWg + +ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdn + ZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGho + ZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpq + agBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe + 3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJRzgHCYh4GVda+m5go3u+H6L7v9/jO7/fYnt/3uI7f98iO3/e4jt/3uI7f97iO3/fInt/3+L + 7v+Fku7/kp/y/46c8f+MmvD/jJrx/4yY8f+KlvH/lqDy/5qk8v+WoPL/naPz/6ip9f+VnvH/j57x/42W + 8P92e+z/ZXTq/3l/7f+hqvP/xs75/6u09f9iaOn/KSbh/x0V3/8kHOD/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYe3/8mH+D/Jh7g/yYd3/8mHd//JiDg/yUp + 4/8lNej/JEHs/yNH7/8jSfD/I0jw/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0fw/yNH7/8jRu//I0bv/xs/ + 7v9UdvT//////4mJif8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/QAAAFYAAAAAzs7MAM7O + zB7Ew8Tqw8PE/8PDxFbDw8QAw8PEAMPDxADDw8QAw8PEAMPDxADDw8QAI0fwACNH8AAjR+8AI0fvACNH + 7wAjR+8AI0bvACNH7wAjRu8AI0fwACNH7wAjR+8AJEfwACNH8AAjR+8AI0fvACNG8AAjR/AAI0bvACNH + 7wAjR+8AI0bvACNH7wAjR+8AI0bvACNG7wAjRu8AeoruAHqK7gB6iu4AeoruAHqK7gB6iu4AeoruAHqK + 7gB6iu4AeoruAHqK7gB6iu4AeoruAHqK7gB6iu6fhInv/1RZ5/8yLuH/IRzf/xwV3/8hGN//JBzg/yYe + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf + 4P8lH+D/Jh/g8SYf4EQmHuAAJh7fACYe3wAmHt8AJh7fACYe3wAmHt8AlKD3AJSf9wCUoPcAlaD3AJSf + 9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdn + ZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5v + bgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhn + ZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe + 4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/DBzeux7Ulfnyl9l6f1jaer/ZWvq/2hv6v9pb+v/aG7r/2tx + 6/96fu3/e3/t/3p+7f9tc+v/Zmzq/2ht6/9veuz/d4nt/3OF7P9jder/boHs/2x37P9tcuz/dnrs/4KK + 7/+JmfD/pa30/7a99/+krfX/fILu/0dL5v8lIuD/Hhbf/yQb4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYe + 3/8mHd//Jh3f/yYh4P8lKuT/JDjp/yRD7v8jSfH/I0nw/yNI7/8jRu//I0fw/yNH7/8jR/D/I0fw/yNH + 8P8bQO//VHb0//////+trKz/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wIDB7kAAAAAJEv/AMnI + wgDJyMIIxcTE18PDxP/ExMR+xMTEAMTExADExMQAxMTEAMTExAAjR+8AI0fwACNH8AAjR/AAI0fvACNH + 7wAjR+8AI0fvACNG7wAjR+8AI0bvACNH8AAjR+8AI0fvACRH8AAjR/AAI0fvACNH7wAjRvAAI0fwACNG + 7wAjR+8AI0fvACNG7wAjR+8AI0fvACNG7wAjRu8AI0bvACUh4AAlIeAAJSHgACUh4AAlIeAAJSHgACUh + 4AAlIeAAJSHgACUh4AAlIeAAJSHgACUh4AAlIeAAJSHgoR0X3/8eFN//Ixrf/yMc3/8jG9//IRnf/yAW + 3/8eFN7/IBff/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYe32ImH+AAJh7gACYe3wAmHt8AJh7fACYe3wAmHt8AJh7fAJSg9wCUn/cAlKD3AJWg + 9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdn + ZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGho + aABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGho + ZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf + 4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ASBgu48yMr5kqes9N2Sl/D/k5jx/5SZ + 8f+TmfH/kpfx/5GX8f+Rl/H/k5nx/5SZ8f+Di+//g43v/4SQ7/+Fke//h5Pv/4SQ7/97iO3/f43u/32B + 7f92dez/X2Pp/0dM5f8zMOL/JB/g/xwW3v8gGN//JR3f/yYe3/8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf + 3/8mH+D/Jh/g/yYf4P8mHuD/Jh3f/yYd3/8mIuH/JS7l/yQ86/8kRu//I0nw/yNH8P8jR+//I0fw/yNH + 7/8jR/D/Gz/v/1N28///////5+bl/xkZGv8AAAD/AAAA/wAAAP8AAAD/AAAA/wUGCf8gOrDqJUz/jCRL + /y0gQ/AAycjCAM/NwbHExMT/xMTEnsnHwQDJx8EAycfBAGqG9QAZPu8AI0fvACNH8AAjR/AAI0fwACNH + 7wAjR+8AI0fvACNH7wAjRu8AI0fvACNG7wAjR/AAI0fvACNH7wAkR/AAI0fwACNH7wAjR+8AI0bwACNH + 8AAjRu8AI0fvACNH7wAjRu8AI0fvACNH7wAjRu8AI0bvACNG7wAlHeAAJR3gACUd4AAlHeAAJR3gACUd + 4AAlHeAAJR3gACUd4AAlHeAAJR3gACUd4AAlHeAAJR3gACUd4JMiGd//HhXf/xwV3v8jIN//Lyrh/z5A + 5P9QV+f/Y2fq/1FX5/8lIOD/JR7f/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh7g/yYf4H4mHt8AJh/gACYe4AAmHt8AJh7fACYe3wAmHt8AJh7fACYe3wCUoPcAlJ/3AJSg + 9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGho + ZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdn + ZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdn + ZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe + 3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAMIBrfUCkj + 4J4pJODnKiTh/yok4f8pI+D/KSPg/yok4f8pJOH/JSDg/yQg4P8kH+D/JB/g/yQf3/8iHd//Hxrf/x8Z + 3/8cFd7/GxLe/x4V3v8hF9//Ixvg/yQd3/8lHt//Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mH9//Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yUf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mH9//Jh7g/yYf4P8mH+D/Jh7g/yYc3/8mHuD/JSbi/yU05/8jQe3/I0jw/yNI + 8P8jR/D/I0fv/xtA7/9TdvP//v////////9hYWL/AAAA/wAAAP8AAAD/AAAA/wAAAP8YJmn/Jkz+/yNH + 8f8jR+/0IEPwpyRH7zWts8uKzszB/8PDxL/Jx8EEycfBAMnHwQBqhvUAGT7vACNH7wAjR/AAI0fwACNH + 8AAjR+8AI0fvACNH7wAjR+8AI0bvACNH7wAjRu8AI0fwACNH7wAjR+8AJEfwACNH8AAjR+8AI0fvACNG + 8AAjR/AAI0bvACNH7wAjR+8AI0bvACNH7wAjR+8AI0bvACNG7wAjRu8AIxvfACMb3wAjG98AIxvfACMb + 3wAjG98AIxvfACMb3wAjG98AIxvfACMb3wAjG98AIxvfACMb3wAjG99qNzbj/1pg6P+Ehe//m6Tz/7q+ + +P/Nz/v/0NP7/9zd/f+os/X/JyTg/yQc4P8mH+D/Jh/g/yYe4P8mH9//Jh/g/yYf4P8mH+D/Jh7g/yYe + 4P8mH+D/Jh/g/yYf4JYmH+AAJh7fACYf4AAmHuAAJh7fACYe3wAmHt8AJh7fACYe3wAmHt8AlKD3AJSf + 9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGho + ZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdn + ZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdn + ZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe + 3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa + 3wApI+AAJB3gFiQd4F8kHd+vJB3f8CQc3/8kHeD/JBzg/yUd3/8lHuD/JR3g/yUd4P8lHeD/JR3f/yUe + 4P8lHuD/Jh7f/yYe4P8mH+D/Jh/g/yYf4P8mHt//Jh7g/yYf4P8mH+D/Jh7f/yYf4P8mHt//Jh7f/yYf + 4P8mH+D/Jh7f/yYf4P8mH+D/Jh7f/yYe4P8lH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7f/yYd3/8mHd//JiDg/yUr + 5P8kOur/I0Xv/yNJ8P8bQe//VHbz//z+////////tLS1/wAAAf8AAAD/AAAA/wAAAP8LDyD/JUbe/yRI + 9v8jR+//I0fw/yNH7/8bQPH3TGvl5LC3yv/IxsDnqbPPCxtE8wCjufoAaob1ABk+7wAjR+8AI0fwACNH + 8AAjR/AAI0fvACNH7wAjR+8AI0fvACNG7wAjR+8AI0bvACNH8AAjR+8AI0fvACRH8AAjR/AAI0fvACNH + 7wAjRvAAI0fwACNG7wAjR+8AI0fvACNG7wAjR+8AI0fvACNG7wAjRu8AI0bvAE5S5wBOUucATlLnAE5S + 5wBOUucATlLnAE5S5wBOUucATlLnAE5S5wBOUucATlLnAE5S5wBOUucATlLnPb7D+PvY2f3/2tv9/9nX + /f/T0/v/0dH7/9DQ+//V1Pz/ur74/y8r4f8jHOD/Jh7g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/Jh7g/yYf4LAmH+AJJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGlo + ZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGho + aABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpq + aQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S + 8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4CMmH+BwJh/gwSYe3/UmH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe + 3/8mH9//Jh/g/yYf4P8mHt//Jh7g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf3/8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf3/8mH9//Jh7g/yYf4P8lH9//Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe + 4P8mHd//Jh3f/yUl4f8lMuf/HDrr/0tu8//6/P////////Pz8/8xMTL/AAAA/wAAAP8DAgD/Hzad/yVL + //8jRu//I0fv/yNG7/8jRu//I0fv/xtA8f8uUu3/2tzk+5Su8mgbRPMKo7n6AGqG9QAZPu8AI0fvACNH + 8AAjR/AAI0fwACNH7wAjR+8AI0fvACNH7wAjRu8AI0fvACNG7wAjR/AAI0fvACNH7wAkR/AAI0fwACNH + 7wAjR+8AI0bwACNH8AAjRu8AI0fvACNH7wAjRu8AI0fvACNH7wAjRu8AI0bvACNG7wCHmPAAh5jwAIeY + 8ACHmPAAh5jwAIeY8ACHmPAAh5jwAIeY8ACHmPAAh5jwAIeY8ACHmPAAh5jwAIeY8Aq3vffN1NP8/8/P + +//Pz/v/z8/7/8/P+//Pz/v/0ND7/87Q+/8/QeT/IRnf/yYe4P8mHuD/Jh/g/yYe4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4MUmHt8TJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpq + aABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5u + bwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBw + cgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf + 4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK + +QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AImH+A1Jh7gfiYe4M8mHuD8Jh/g/yUf + 3/8mH+D/Jh/f/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHt//Jh7g/yUe4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/JR7f/yYf4P8mH+D/Jh7g/yYf4P8mH9//Jh7f/yYf + 4P8mH+D/Jh/f/yYf4P8mHeD/Jhzf/x8a3/9BR+f/9/j+////////////d3d4/wAAAP8AAAD/Ex9P/yVL + +f8jR/L/I0fw/yNG7/8jR+//I0bv/yNH7/8hRe//Ikfw/+zu///c5/7/LVLwyqO5+l5qhvUKGT7vACNH + 7wAjR/AAI0fwACNH8AAjR+8AI0fvACNH7wAjR+8AI0bvACNH7wAjRu8AI0fwACNH7wAjR+8AJEfwACNH + 8AAjR+8AI0fvACNG8AAjR/AAI0bvACNH7wAjR+8AI0bvACNH7wAjR+8AI0bvACNG7wAjRu8Ah5jwAIeY + 8ACHmPAAh5jwAIeY8ACHmPAAh5jwAIeY8ACHmPAAh5jwAIeY8ACHmPAAh5jwAIeY8ACHmPAAnarzgdXU + /P/Pz/v/z8/7/8/P+//Pz/v/0dD7/9bV/P/Y2v3/R07l/yAX3v8mHuD/Jh7f/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4MwmH+AeJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxr + awBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlp + aABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGho + ZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf + 4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC + 7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAGJh7gQyYf + 4JAmH+DYJh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYe4P8mH+D/Jh/g/yUe3/8lHuD/Jh/g/yYe + 3/8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yUe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yUe3/8lH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mHt//JR/f/yYf4P8mH+D/Jh/g/yYe4P8fFd//T1Xl//r9/v///////////769vv8AAAD/BgsV/yRE + 0/8jSPn/I0bv/yNG7/8jRu//I0fv/yNH7/8jR+//IETv/yZJ8P/r7f7/2eT+/zJV8f+4yPv/aYX1yRk+ + 710jR+8KI0fwACNH8AAjR/AAI0fvACNH7wAjR+8AI0fvACNG7wAjR+8AI0bvACNH8AAjR+8AI0fvACRH + 8AAjR/AAI0fvACNH7wAjRvAAI0fwACNG7wAjR+8AI0fvACNG7wAjR+8AI0fvACNG7wAjRu8AI0bvAISQ + 7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ + 7yXHyPrr1dT8/9LS/P/W1fz/2dn9/9PV+/+4vPf/goju/y0q4f8kG+D/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/Jh/g/yYf4NomH+AiJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdn + ZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdn + ZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdm + ZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ + 3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh7gECYf4FEmHuCdJh/g4CYf4P8mH+D/Jh7f/yYe3/8mH+D/Jh7g/yUf3/8lH9//Jh/f/yYe + 4P8mHt//Jh/g/yYf3/8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8lH9//Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8lHuD/JR/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7f/yUf4P8mH+D/Jh/g/yYf4P8mHuD/HhXf/1Vg5v/8//7////////////j4+P/GhkS/xco + h/8mTP//I0jw/yNH8P8jR+//I0fw/yNH8P8jR/D/I0fw/yFF8P8lSPD/7e/+/8TU/P8tUfH/v838/159 + 9P8aPu7/I0fvyCNH8FwjR/AEI0fwACNH7wAjR+8AI0fvACNH7wAjRu8AI0fvACNG7wAjR/AAI0fvACNH + 7wAkR/AAI0fwACNH7wAjR+8AI0bwACNH8AAjRu8AI0fvACNH7wAjRu8AI0fvACNH7wAjRu8AI0bvACNG + 7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ + 7wCEkO8Ag4nvmtLW+//HzPr/tLj3/4eN7/9TVOf/King/xwU3v8kHN//Jh7g/yYe4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh7g/yYf4NsmH+AoJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGho + ZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdn + ZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZm + ZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd + 4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN6 + 7AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4BgmH+BXJh/gqSYf3+YmHuD/Jh/g/yYe4P8mH+D/Jh/f/yYf + 3/8mH+D/Jh/g/yYe4P8lHt//Jh/g/yYe3/8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yUe + 3/8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe3/8lH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/x8X3/9IR+X/+fv+///////////////8/2Jl + jf8aFNX/JSnn/yQ56f8jRu7/I0nw/yNI8P8jR/D/I0bv/yNG8P8hRe//Jkrw/+3w/v+/0Pv/L1bx/7rL + /P9QcvT/HEDv/yNH7/8jR/D/I0fwvCNH8EwjR+8AI0fvACNH7wAjR+8AI0bvACNH7wAjRu8AI0fwACNH + 7wAjR+8AJEfwACNH8AAjR+8AI0fvACNG8AAjR/AAI0bvACNH7wAjR+8AI0bvACNH7wAjR+8AI0bvACNG + 7wAjRu8ALCrhACwq4QAsKuEALCrhACwq4QAsKuEALCrhACwq4QAsKuEALCrhACwq4QAsKuEALCrhACwq + 4QAsKuEALCrhACwq4TJHR+X2QUPk/ycm4P8cFN7/HBTe/yMb4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe4NgmHuAoJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlp + ZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlp + ZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlp + aABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn + 2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre + /ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AgJh7gXyYe4K4mH+DrJh/g/yYe + 3/8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYe4P8lHt//Jh/g/yUf + 3/8mH+D/Jh/g/yYe4P8mH+D/Jh7f/yYe4P8mH+D/Jh7g/yYf4P8mH9//JR/g/yYe4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe3/8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8iGuD/MS7i/9vj+v////////////// + //+QnPX/GBHg/yYc3v8mHd7/JSXh/yU06P8jQ+3/I0nw/yNI8P8jR+//HkLw/zBY8f/3+v//rsD6/zdf + 8v+6zPv/Qmfz/x5B7/8jR+//I0fv/yNH8P8jR+//I0fvryNH7zcjR+8AI0fvACNG7wAjR+8AI0bvACNH + 8AAjR+8AI0fvACRH8AAjR/AAI0fvACNH7wAjRvAAI0fwACNG7wAjR+8AI0fvACNG7wAjR+8AI0fvACNG + 7wAjRu8AI0bvACwq4QAsKuEALCrhACwq4QAsKuEALCrhACwq4QAsKuEALCrhACwq4QAsKuEALCrhACwq + 4QAsKuEALCrhACwq4QAsKuEAHhXfnh8W3v8kHOD/Jh7g/yYf4P8mH+D/Jh7f/yYe3/8mH+D/Jh/g/yYe + 4P8mHt//Jh7g/yUe38kmHuAgJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZm + YgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxs + bQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9v + cABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJo + egAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r + 6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gKSYf + 4GkmHuC3Jh7g7CYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHt//Jh7f/yYf3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8lHt//Jh7f/yYf3/8mH+D/Jh/g/yYe4P8mH+D/JR3g/xwW3v+epfH///////// + ////////aW/r/xkR3f8mHuD/Jh7g/yYd3/8mG97/JSHg/yUw5f8kQO3/I0nw/xM57/9lhPX//////5+x + +P9AZvP/vM38/zVZ8P8gQ+//I0fv/yNH7/8jR+//I0fw/yNH8P8jR/D1I0fvkCNH7x8jRu8AI0fvACNG + 7wAjR/AAI0fvACNH7wAkR/AAI0fwACNH7wAjR+8AI0bwACNH8AAjRu8AI0fvACNH7wAjRu8AI0fvACNH + 7wAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4BQmH+CuJh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4KomH+AYJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ + 0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJy + cwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGho + ZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5s + YQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh + 4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYe3yomH+BiJh/grCYf4N0mH+D/JR7f/yYe4P8mH+D/Jh/g/yYf3/8mH+D/Jh/g/yYe + 3/8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yUf3/8mH+D/Jh/f/yUe3/8mH9//Jh/g/yYe4P8mH9//JR7f/yUe + 3/8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8lHt//JR/g/yYf3/8fFt7/Pj/j/+bu + /P//////2OL6/ysr4P8iGd//JR/g/yUf3/8lH+D/JR/f/yUe3/8mHN//Jx/g/xkf4/86WO7/4+r+//// + //95k/f/U3Hz/77N/P8rTvD/IUXv/yNG7/8jRu//I0bv/yNH7/8jRvD/I0bv/yNH7/8jR+/gI0bvZSNH + 7wQjRu8AI0fwACNH7wAjR+8AJEfwACNH8AAjR+8AI0fvACNG8AAjR/AAI0bvACNH7wAjR+8AI0bvACNH + 7wAjR+8AI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gBiYe4GAmH+C7Jh/g6CYf4P4mH+D/Jh/g/yYf + 4P8mH+D1Jh7g0yYf4G8mH+AFJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y + 5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlp + aABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdn + ZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZm + ZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc + 4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AZJh/gRyUe34smHuDHJh/g9SYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/f/yYf3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH9//Jh/g/yUf + 4P8lHt//Jh/g/yYf4P8mHuD/Jh/g/yYf3/8mH9//Jh7g/yYe4P8mH+D/JR/f/yUf4P8mH+D/Jh/g/xwV + 3/9ZYOj/u7z1/1pf6P8cFN7/Jh/g/yUf3/8mHuD/Jh7f/yYf4P8mH+D/JiDf/xsR3v8yM+H/ztX4//// + ///u9P3/MFXw/4ae9/+uwvv/JUrw/yFF7/8jR+//I0bv/yNG8P8jR/D/I0fw/yNH7/8jR/D/I0fw/yNH + 7/8jR/CyI0bvLCNH8AAjR+8AI0fvACRH8AAjR/AAI0fvACNH7wAjRvAAI0fwACNG7wAjR+8AI0fvACNG + 7wAjR+8AI0fvACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gASYf4CUmHuBCJh/gRyYf + 4EcmH+BGJh/gNCYf4BImH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej + /wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGho + ZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdn + ZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZn + ZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gByYf4DcmH+B4Jh/gvCYf + 4PImH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yUe4P8mH+D/JR7f/yUf + 4P8mHuD/Jh/g/yYf4P8mHuD/Jh7f/yYf4P8mH9//Jh/f/yYe3/8mH9//Jh/g/yYf4P8mH9//Jh/g/yYf + 4P8mHt//Hhbf/xsU3v8eFt7/Jh/f/yYe3/8mHuD/Jh/g/yYf4P8mH+D/Jh/g/xkQ3v8xNeH/zdb4//// + ////////b3bq/0NP5//L1vv/l7D5/yBI8P8iR/D/I0fv/yNH7/8jR/D/I0fw/yNH8P8jR/D/I0bw/yNG + 8P8kRu//I0bw/yNG8OgjR/BpI0fvACNH7wAkR/AAI0fwACNH7wAjR+8AI0bwACNH8AAjRu8AI0fvACNH + 7wAjRu8AI0fvACNH7wAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg + +ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdn + ZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGho + ZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpq + agBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe + 3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd + 3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf + 4AMmHuAvJh7gaiYf4LUmH+DvJh/g/yYe4P8mHt//Jh/g/yYe4P8mHuD/Jh/f/yYf4P8mHt//Jh7g/yUe + 4P8lH9//Jh/f/yYf4P8mH+D/Jh/g/yUe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/f/yYf + 4P8mH+D/Jh/g/yYe4P8lHuD/JR/f/yYe3/8mH+D/Jh/g/yYf3/8mHuD/IBff/xQM3f8/ROT/z9n4//// + ///5/v7/eoDt/zA44v+3vfb/2978/3V+7f8bLef/I0Tu/yNK8P8jSPD/I0fw/yNH7/8jR+//I0bv/yNG + 8P8jR+//JEfw/yNG7/8jRu//I0fw/yNH76QjR+8bJEfwACNH8AAjR+8AI0fvACNG8AAjR/AAI0bvACNH + 7wAjR+8AI0bvACNH7wAjR+8AI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf + 9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdn + ZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5v + bgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhn + ZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe + 4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd + 4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh7gKyYf4GMmH+CuJh/f6iYf4P8mHuD/Jh7g/yYe3/8mHuD/Jh7f/yYf + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mHt//Jh7f/yQa3/8bE97/FxLe/zM14v+Jke//8PP9//// + ///h5vv/XWXo/zk95P+xuPb/2t38/6y29f8wL+H/Ixje/yYj4f8lM+f/I0Pu/yNK8f8jSfH/I0fv/yNH + 8P8jR+//I0fv/yNH7/8kRvD/I0fv/yNH7/8jR/D/I0fv1iRH8EgjR/AAI0fvACNH7wAjRvAAI0fwACNG + 7wAjR+8AI0fvACNG7wAjR+8AI0fvACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe + 3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg + 9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdn + ZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGho + aABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGho + ZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf + 4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd + 4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4CQmH+BhJh/gqyYf4OMmH+D/Jh7g/yUe + 3/8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mHt//Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/JR7g/yMa4P8eFd7/GBDe/xkR3v8kJOD/VFjo/56o8v/s8v3//////+Pn + +/+SmfD/RE3k/2Zu6v/ByPj/3N/8/7O89v87P+P/Hxff/yYf4P8mHd//Jh3f/yYh4P8lMeb/JELt/yNJ + 8f8jSfD/I0fv/yNG7/8jRu//JEbw/yNH8P8jR+//I0bv/yNH8P8jR+/3I0fwhiNH7wwjR+8AI0bwACNH + 8AAjRu8AI0fvACNH7wAjRu8AI0fvACNH7wAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf + 4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg + 9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGho + ZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdn + ZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdn + ZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe + 3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj + 4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AdJh/gVyYe + 4KUmH+DkJh/g/yUd3/8eFd7/Fw/e/xcP3v8XD97/Fw/e/xcP3v8XD97/Fg/d/xcP3v8XD97/Fw/e/xcP + 3v8XD97/FxDe/xgS3v8ZE97/HBXe/x8X3/8sLOH/Rknl/3Bz6/+krvP/2+D6//n+///8////ydL4/4uc + 7/9ncuv/d4Dt/7W79v/b4Pz/0tj7/6Kr9P88P+P/HRbf/yYf4P8mHt//Jh7g/yYf4P8mHuD/Jhzf/yYh + 4P8lL+b/JEHs/yNJ8f8jSfD/I0fw/yNG7/8jRu//I0fw/yNH7/8jR+//I0bv/yNG7/8jR/DCI0fvNCNG + 8AAjR/AAI0bvACNH7wAjR+8AI0bvACNH7wAjR+8AI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf + 9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGho + ZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdn + ZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdn + ZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe + 3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa + 3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe + 3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh/gHCUe31cmIuChS07m4m5r6v94c+z/enXs/3Vw6/91b+v/dG/r/3Nv6/9ybuv/cm7r/3Ju + 6/9ybuv/cm3r/3Fv6v99h+z/i5fv/56p8f+5vvX/ztX4/93p+//c5vr/z9r5/8nS+P+1w/b/pLHz/6Cs + 8/+0vPb/z9b6/9Xa+/+3wPf/h47v/1BW5/8lI+D/IBjf/yYf3/8mH9//Jh7f/yYf4P8mHuD/Jh7g/yYe + 4P8mHd//Jhzf/yYg4f8lLub/JEHt/yNK8f8jSPD/I0fv/yNH7/8jR+//I0fv/yNH8P8jR/D/I0bv/yNG + 7+8jRvBuI0fwASNG7wAjR+8AI0fvACNG7wAjR+8AI0fvACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGlo + ZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGho + aABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpq + aQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S + 8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf + 4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89Bn///9UzNb4nKe39N69zff/3Ob8//z////s9P7/2+f7/9zo + +//d6Pv/3en7/93p+//Y5fv/1uH6/9jj+//F0vj/tMP2/6Ox9P+ktPT/p7b0/7O+9/+xuvb/rbb1/6q0 + 9f+irfP/mJ7z/3l+7v9NUeb/LSrh/x0Y3v8eFt//JBzg/yYf4P8mH+D/Jh7f/yYe4P8mH+D/Jh7g/yYe + 3/8mH+D/Jh/g/yYf4P8mHuD/Jhzf/yYg4P8lL+X/JEDs/yNJ8P8jSPD/I0fw/yNG7/8jR/D/I0fw/yNH + 8P8jRu//I0bv/yNH8K0jRu8kI0fvACNH7wAjRu8AI0fvACNH7wAjRu8AI0bvACNG7wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpq + aABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5u + bwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBw + cgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf + 4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK + +QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYVusT4TZOb8pxqb+vfb3Ps/290 + 7P9vc+z/b3Ts/2907P9vdOz/b3Ts/2907P9vdOz/cHXs/3J27P9la+r/XWTp/11k6v9OVeb/Qj/k/zYx + 4/8sKeH/JSTg/x8a3/8dFN7/Hxjf/yMa3/8lHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh3f/yYg4P8lLuX/JEDs/yNJ8P8jSPD/I0fw/yNH + 7/8jRu//I0fv/yNH7/8jR+//I0fv4SNH71wjR+8AI0bvACNH7wAjR+8AI0bvACNG7wAjRu8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxr + awBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlp + aABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGho + ZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf + 4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC + 7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf + 4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf + 4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndEx0V + 30cdFd+SHRXf2B0V3/8dFd//HRXf/x0V3/8dFd//HRXf/x0W3/8dFd//Hxbf/x8X3v8fFt//IBff/yEZ + 3/8iGuD/Ixvg/yQc4P8lHuD/Jh7f/yYe4P8mH+D/Jh7g/yYf3/8mH+D/Jh/g/yYe3/8lHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYe3/8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mHt//Jhze/yYf3/8lLuX/JEDs/yNJ + 8P8jSfH/I0fw/yNG7/8jRvD/I0fv/yNH7/8jR/D/I0fvmSNG7xYjR+8AI0fvACNG7wAjRu8AI0bvACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdn + ZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdn + ZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdm + ZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ + 3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf + 4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ + 3QAdFd8AHRXfACYf4A4mH+BFJh7gkiYe39omHuD/Jh7f/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh7f/yYf + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYe3/8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mHt//JR7g/yYe + 4P8mH+D/JR7f/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/JR/g/yYf4P8mHd//Jh3f/yYg + 4P8lLuX/JEDs/yNJ8P8jSPD/I0bw/yNH8P8jR+//I0fw/yNH8P8jR+/RI0fvOyNH7wAjRu8AI0bvACNG + 7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGho + ZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdn + ZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZm + ZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd + 4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN6 + 7AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe + 3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb + 8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8NJh/gRiYf4JMmH+DYJh/g/SYe4P8mH+D/Jh/g/yYe + 3/8lHt//JR7f/yYe3/8lHt//Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yUe3/8mHt//Jh/g/yYf4P8mHt//Jh7g/yYf4P8mH+D/Jh7f/yUf4P8mHuD/Jh7g/yYf + 4P8mHd//Jh3f/yYg4P8lMOX/JELt/yNJ8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH8PIjR+9eI0bvAyNG + 7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlp + ZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlp + ZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlp + aABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn + 2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre + /ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE + +ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fCiYf4D0mH+CKJh7g0yYe + 4P0mHuD/Jh/f/yYe3/8mHt//Jh/f/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh/f/yYf4P8mH+D/Jh/g/yYe + 3/8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH9//Jh/g/yYe + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh3f/yYg4P8lMub/JELt/yNI8P8jR/D/I0fv/yNH7/8jR+//I0fv+iNG + 74gjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZm + YgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxs + bQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9v + cABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJo + egAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r + 6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY + 5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe + 4AcmHuA9Jh/giSYf39MmH+D9Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mHt//Jh7f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe3/8mHd//Jhze/yYi4P8lNOj/I0Xu/yNJ8P8jR/D/I0fw/yNG + 7/8jR/DEI0fwACNH8AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ + 0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJy + cwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGho + ZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5s + YQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh + 4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW + +ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf + 4AAmHuAAJh7gACYf4AAmHt8HJh7gPCYf4IAmH+DKJh/g+iYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7f/yYe3/8mH+D/Jh7g/yYf3/8mHt//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe3/8mHuD/Jh/g/yYe4P8mHd//Jhzf/yUm4/8lOur/I0jw/yNI + 8P8jRvD/I0fwviNH8AAjR/AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y + 5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlp + aABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdn + ZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZm + ZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc + 4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP// + /wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fBCYe4DUmH+B9Jh/gySYe4PcmH+D/Jh/g/yYe + 3/8mHt//Jh7g/yYf4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8lH9//JR7f/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHeD/Jh7f/yUt + 5f8kRe7/I0jw/yNH8IojR/AAI0fwACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej + /wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGho + ZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdn + ZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZn + ZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW8 + 9AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe + 3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AQlHt8zJR7fdSYe + 4MEmHt/0Jh7f/yYf4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mHt//Jh7g/yYe + 4P8mHuD/JR/f/yYf3/8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mHuD/Jh7g/yYe + 4P8mG9//JTXo/yNJ8M4jRu8sI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg + +ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdn + ZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGho + ZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpq + agBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe + 3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd + 3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi + 4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf + 4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe + 3wAmHt8CJh/gLyYf4G4mHt+5Jh/g7iYf4P8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7f/yYe + 4P8mH+D/Jh/g/yYf4P8lHt//JR/f/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jhzf/yUw5sEjSvAxI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf + 9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdn + ZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5v + bgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhn + ZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe + 4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd + 4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe + 3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf + 4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe + 3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4CYmH+BkJh7grCYf4OMmH+D/Jh/f/yYe4P8mH+D/Jh7f/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/JR/g/yUe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh7g/yYc36klMeYKI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe + 3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg + 9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdn + ZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGho + aABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGho + ZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf + 4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd + 4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf + 4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe + 3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf + 4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAWJh7gUSYf4IEmHuDAJh/g5SYf + 4P8mH+D/Jh7g/yYe4P8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P0mH+DjJh/gtyYe31YlHN4BJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf + 4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg + 9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGho + ZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdn + ZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdn + ZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe + 3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj + 4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe + 4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf + 4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gAiYe + 4BwmHuBRJh/geCYf4LMmHuDYJh/g8CYe3/8lH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4OUmH+DFJh/glCYf + 4GwmH+BIJh7gFyYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf + 9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGho + ZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdn + ZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdn + ZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe + 3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa + 3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe + 3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf + 4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe + 4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe + 4AAmHuAAJh7gACYf4AAmH+AZJR7fOyUe328mH+CYJh/guCYe374mH+C+Jh7gvyYf4JwmH+BWJh/gJSYf + 4AsmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGlo + ZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGho + aABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpq + aQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S + 8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf + 4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf + 4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe + 3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpq + aABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5u + bwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBw + cgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf + 4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK + +QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V + 3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf + 4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxr + awBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlp + aABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGho + ZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf + 4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC + 7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf + 4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf + 4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V + 3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe + 4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdn + ZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdn + ZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdm + ZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ + 3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf + 4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ + 3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe + 3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe + 4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG + 7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGho + ZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdn + ZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZm + ZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd + 4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN6 + 7AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe + 3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb + 8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf + 4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG + 7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlp + ZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlp + ZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlp + aABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn + 2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre + /ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE + +ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe + 3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG + 7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZm + YgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxs + bQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9v + cABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJo + egAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r + 6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY + 5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe + 4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe + 3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf + 4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK + 8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ + 0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJy + cwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGho + ZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5s + YQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh + 4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW + +ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf + 4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf + 4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf + 4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx + 5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y + 5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlp + aABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdn + ZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZm + ZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc + 4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP// + /wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf + 4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe + 3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc + 3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej + /wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGho + ZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdn + ZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZn + ZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW8 + 9AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe + 3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe + 3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe + 3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe + 3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg + +ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdn + ZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGho + ZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpq + agBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe + 3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd + 3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi + 4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf + 4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe + 3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf + 4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf + 4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf + 9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdn + ZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5v + bgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhn + ZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe + 4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd + 4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe + 3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf + 4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe + 3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe + 3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg + 9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdn + ZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGho + aABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGho + ZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf + 4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd + 4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf + 4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe + 3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf + 4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf + 4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg + 9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGho + ZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdn + ZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdn + ZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe + 3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj + 4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe + 4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf + 4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf + 9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGho + ZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdn + ZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdn + ZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe + 3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa + 3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe + 3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf + 4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe + 4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe + 4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGlo + ZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGho + aABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpq + aQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S + 8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf + 4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf + 4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe + 3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpq + aABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5u + bwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBw + cgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf + 4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK + +QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V + 3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf + 4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxr + awBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlp + aABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGho + ZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf + 4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC + 7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf + 4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf + 4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V + 3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe + 4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdn + ZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdn + ZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdm + ZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ + 3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf + 4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ + 3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe + 3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe + 4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG + 7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGho + ZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdn + ZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZm + ZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd + 4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN6 + 7AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe + 3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb + 8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf + 4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG + 7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlp + ZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlp + ZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlp + aABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn + 2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre + /ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE + +ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe + 3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG + 7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZm + YgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxs + bQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9v + cABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJo + egAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r + 6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY + 5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe + 4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe + 3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf + 4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK + 8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ + 0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJy + cwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGho + ZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5s + YQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh + 4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAhYaJAIWGiQCCjLsAMi81ADIx + VAAODgwAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW + +ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf + 4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf + 4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf + 4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx + 5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y + 5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlp + aABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdn + ZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZm + ZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc + 4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwAAcGHAAHBhwAEyeOAIWGiQCFhokAgoy7ADIv + NQAyMVQADg4MAB88vQAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP// + /wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf + 4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe + 3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc + 3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej + /wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGho + ZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdn + ZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZn + ZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3AAjHMEAIxzBACMcwQAHBhwABwYcABMnjgCFhokAhYaJAIKM + uwAyLzUAMjFUAA4ODAAfPL0ABg4uAAMDEQAAAAAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW8 + 9AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe + 3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe + 3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe + 3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe + 3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg + +ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdn + ZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGho + ZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpq + agBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe + 3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJRzgACYh4AAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEABwYcAAcGHAATJ44AhYaJAIWG + iQCCjLsAMi81ADIxVAAODgwAHzy9AAYOLgADAxEAAAAAAJmn2QCZp9kAJh7gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi + 4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf + 4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe + 3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf + 4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf + 4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf + 9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdn + ZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5v + bgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhn + ZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe + 4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBAAcGHAAHBhwAEyeOAIWG + iQCFhokAgoy7ADIvNQAyMVQADg4MAB88vQAGDi4AAwMRAAAAAACZp9kAmafZAJmn2QAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe + 3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf + 4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe + 3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe + 3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg + 9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdn + ZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGho + aABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGho + ZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf + 4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAHBhwABwYcABMn + jgCFhokAhYaJAIKMuwAyLzUAMjFUAA4ODAAfPL0ABg4uAAMDEQAAAAAAmafZAJmn2QCZp9kAmafZAJmn + 2QAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf + 4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe + 3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf + 4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf + 4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg + 9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGho + ZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdn + ZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdn + ZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe + 3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEABwYcAAcG + HAATJ44AhYaJAIWGiQCCjLsAMi81ADIxVAAODgwAHzy9AAYOLgADAxEAAAAAAJmn2QCZp9kAmafZAJmn + 2QCZp9kAmafZACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe + 4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf + 4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf + 9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGho + ZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdn + ZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdn + ZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe + 3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBAAcG + HAAHBhwAEyeOAIWGiQCFhokAgoy7ADIvNQAyMVQADg4MAB88vQAGDi4AAwMRAAAAAACZp9kAmafZAJmn + 2QCZp9kAmafZAJmn2QCZp9kAmafZACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe + 3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf + 4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe + 4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe + 4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGlo + ZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGho + aABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpq + aQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAHBhwABwYcABMnjgCFhokAhYaJAIKMuwAyLzUAMjFUAA4ODAAfPL0ABg4uAAMDEQAAAAAAmafZAJmn + 2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf + 4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf + 4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe + 3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpq + aABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5u + bwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBw + cgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf + 4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEABwYcAAcGHAATJ44AhYaJAIWGiQCCjLsAMi81ADIxVAAODgwAHzy9AAYOLgADAxEAAAAAAJmn + 2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAJh7gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V + 3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf + 4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxr + awBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlp + aABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGho + ZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf + 4AAmH+AAJh7gACYe3wAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBAAcGHAAHBhwAEyeOAIWGiQCFhokAgoy7ADIvNQAyMVQADg4MAB88vQAGDi4AAwMRAAAA + AACZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QAmHt8AJh/gACYf + 4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V + 3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe + 4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdn + ZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdn + ZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdm + ZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe + 4AAmH+AAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAHBhwABwYcABMnjgCFhokAhYaJAIKMuwAyLzUAMjFUAA4ODAAfPL0ABg4uAAMD + EQAAAAAAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn + 2QAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ + 3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe + 3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe + 4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG + 7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGho + ZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdn + ZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZm + ZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEABwYcAAcGHAATJ44AhYaJAIWGiQCCjLsAMi81ADIxVAAODgwAHzy9AAYO + LgADAxEAAAAAAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn + 2QCZp9kAmafZACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb + 8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf + 4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG + 7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlp + ZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlp + ZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlp + aABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBAAcGHAAHBhwAEyeOAIWGiQCFhokAgoy7ADIvNQAyMVQADg4MAB88 + vQAGDi4AAwMRAAAAAACZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn + 2QCZp9kAmafZAJmn2QCZp9kAmafZACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE + +ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe + 3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG + 7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZm + YgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxs + bQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9v + cABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAHBhwABwYcABMnjgCFhokAhYaJAIKMuwAyLzUAMjFUAA4O + DAAfPL0ABg4uAAMDEQAAAAAAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn + 2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY + 5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe + 4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe + 3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf + 4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK + 8AAjRu8AI0bvACNG7wCIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI////// + //////////////////////////iIiIiIiIiIj///////////////////////////////+IiIiIiIiIiP + ///////////////////////////////4iIiIiIiIiI////////////////////////////////iIiIiI + iIiIj///////////////////////////////+IiIiIiIiIiP///////////////////////////////4 + iIiIiIiIiI////////////////////////////////iIiIiIiIiIj/////////////////////////// + ////+IiIiIiIiIiP///////////////////////////////4iIiIiIiIiI////////////////////// + //////////iIiIiIiIiIj///////////////////////////////+IiIiIiIiIiP//////////////// + ///////////////4iIiIiIiIiI////////////////////////////////iIiIiIiIiIj/////////// + ////////////////////+IiIiIiIiIiP///////////////////////////////4iIiIiIiIiI////// + //////////////////////////iIiIiIiIiIj///////////////////////////////+IiIiIiIiIiP + ///////////////////////////////4iIiIiIiIiI////////////////////////////////iIiIiI + iIiIj///////////////////////////////+IiIiIiIiIiP///////////////////////////////4 + iIiIiIiIiI////////////////////////////////iIiIiIiIiIj/////////////////////////// + ////+IiIiIiIiIiP///////////////////////////////4iIiIiIiAiI////////////////////// + //////////iIiIiIAACIj/////////////////////////////////iIiIgAAIiP//////////////// + /////////////////4iIiAAAiI///////////////////////////////////4iIgACIj/////////// + ////////////////////////+IiIgIiP////////////////////////////////////+IiIiI////// + ////////////////////////////////iIiIj//////////////////////////////////////4iIiP + //////////////////////////////////d////4iI////////////////////////////////93d3d3 + //+Ij///////////////////////////////d0AHd3d//4iP//////////////////////////////dw + AAd3d3d/iI//////////////////////////////cAAAB3d3d3eIj/////////////////////////// + //cAAAAAR3d3d4iP////////////////////////////cAAAAAAAR3d3iI////////////////////// + //////cAAAAAAAAAd3eIj///////////////////////////cAAAAAAAAAAHd4iIj/////////////// + //////////wAAAAAAAAAAAB3iIiP////////////////////////9AAAAAAAAAAAAAeIiI////////// + //////////////9wAAAAAAAAAAAAB4iIiP///////////////////////wAAAAAAAAAAAAAAiIiI//// + ///////////////////3AAAAAAAAAAAAAACIiIj//////////////////////3MAAAAAAAAAAAAAAIiI + iI//////////////////////cAAAAAAAAAAAAAAAiIiIj/////////////////////cwAAAAAAAAAAAA + AACIiIiI////////////////////9wAAAAAAAAAAAAAAAIiIiIj///////////////////9wAAAAAAAA + AAAAAAAAiIiIiI///////////////////3AAAAAAAAAAAAAAAACIiIiIiI//////////////////cAAA + AAAAAAAAAAAAAIiIiIiIj/////////////////cAAAAAAAAAAAAAAAAAiIiIiIiI//////////////// + 9wAAAAAAAAAAAAAAAACIiIiIiIiP///////////////3AAAAAAAAAAAAAAAAAIiIiIiIiIj///////// + //////cAAAAAAAAAAAAAAAAAiIiIiIiIiI//////////////9wAAAAAAAAAAAAAAAACIiIiIiIiIj/// + //////////93AAAAAAAAAAAAAAAAAIiIiIiIiIiI/////////////3cAAAAAAAAAAAAAAAAAiIiIiIiI + iIiP////////////dwAAAAAAAAAAAAAAAACIiIiIiIiIiIj///////////93AAAAAAAAAAAAAAAAAIiI + iIiIiIiIiI///////////3cAAAAAAAAAAAAAAAAAiIiIiIiIiIiIiIiIiIiP////dwAAAAAAAAAAAAAA + AACIiIiIiIiIiIiIiIiIiIiIiP93AAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiIiIiIiAAAAAAAAAAA + AAAAAAAAiIiIiIiIiIiIiIiIiIiIiIiAAAAAAAAAAAAAAAAAAACIiIiIiIiIiIiIiIiIiIiIgAAAAAAA + AAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiIiIiIiIiIiIgAAA + AAAAAAAAAAAAAAAAAACIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiI + AAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAACIiIiIiIiIiIiI + iIiIAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiI + iIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAIiI + iIiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAA + AACIiIiIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIAAAAAACAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIgAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAiIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIgAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiI + iIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiI + iIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AACIiIiIiIiIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIiIiIgAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAIiIiIiIiIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiIAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIiAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIiIiIAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiI + iAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiI + iIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AACIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAiIiIiIiIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIiIiIAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAiIiIiIgAAAAAiIiIiIiIiAAAAAAAAAAAAAAA + AAAAAACIiIiIiIiAAACIiIiIiIiIAAAAAAAAAAAAAAAAAAAACIiIiIiIiIiIAIiIiIiIiIgAAAAAAAAA + AAAAAAAAAACIiIiIiIiIiIiIiIiIiIiIiAAAAAAAAAAAAAAAAAAACIiIiIiIiIiIiIiIiIiIiIiIAAAA + AAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiIiIiI + iAAAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiIiIiIiIAAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiI + iIiIiIgAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiIiIiIiIiAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiI + iIiIiIiIiIiIAAAAAAAAAAAAAAAAiIiIiIiIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAAAAAiIiIiIiIiI + iIiIiIiIiIiIiIiIiIAAAAAAAAAAAAAAiIiIiIiIiIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAAAiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIAAAAAAAAAAAAiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIAAAAAAAAAAiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIgAAAAAAA + AAiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIAAAAAAAAiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + AAAAAIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiCgAAACAAAAAAAEAAAEA + IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAAB7AAAAnwAAAJ8AAACZAAAAdQAAAEIAAAAJAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABfAAAAyAAA + APkAAAD/AAAA/wAAAP8AAAD2AAAA1AAAAIQAAAAbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAaAAAAswABAP8GBhj/Cwsr/wsLK/8HByD/BAQO/wABAP8AAAD/AAAA/wAA + AOQAAABIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJQABANcPDkb/Hhut/yUe + 3f8kHer/IRnr/xwU5f8ZFNH/FBGs/wkIYv8AAA3/AAAA/wAAAPQAAAAxAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAB4DBAjaGhiQ/yMb9v8eFff/GxTn/yUh1/9AP8n/Z2bD/3+Bx/9/gc7/aGbQ/zc4 + n/8BAx3/AAAA/wAAAMAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAAAUzRURr/8gGu7/Mi/O/1ta + wP+RksH/xcfP/+3u5P////b////+/////v////T/7u3p/5SUoP8aGRf/AAAA/wAAAD4AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAKSgoBwICBbY5O4r/cnLO/6qsxf/e39r////x/////////////////////P/09fb/5er3/97m + ///d4///4+r//8HG0P85Ny7/AAAAlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbm5tJH5+fmaPj4/Burq7/+/v7f////n///////// + ///4+v//1N35/6m48v99kuz/V3Hn/zxa5/8tT+v/J0vw/yJG8P8lSvD/QmX+/1pwzf8aHCG9AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1tbAJwb28yg4KDeqam + psLQz9D18/Pz///////////////9/93j9/+is/T/aIPx/z1e7/8iRu7/Fz3u/xY87/8ZPvD/HULx/yBE + 8P8hRO//IUbv/yFF7/8dQu//HkT6/y9My94zSZ0NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAbGxsB3Jycj6JiYmHr6+vz9nZ2v77+/r////////////+/ff/09fo/5ak3/9VcOP/J0rs/xY8 + 7v8XPe//HUHv/yFF7/8jRu//I0fv/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH8P8jSfD/Ikn2/yRJ + +Z8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsbGwIc3NzQYuLjI+0tLTY39/f/////f////////////Hy + 9P+/x+f/gpPd/0lk3v8kRuX/Fjzt/xo/8f8gRfD/I0fv/yNH8P8jR+//I0fv/yNG7/8jR+//I0fv/yNH + 8P8jR+//I0fv/yNG7/8jR+//I0bv/yU06P8kPuz/I0nw/yNH8GkAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABzcnI7jIyLj7S0 + tNni4eD////+////////////4+f2/6q36v9rgeP/OFfj/xxB6f8WPO//G0Hx/yFG8f8jR+//I0fv/yNH + 7/8jRu//I0fv/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH7/8jRu//I0fw/yNJ8P8lMeb/Jhre/yYf + 4P8kO+v/I0nw8yNG7y8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAB2dnUspaSlw97d3f////7////////////U3Pj/lafv/1dy6v8rTen/GD3t/xc9 + 8P8eQvH/Ikbw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH7/8jR+//I0fw/yNH + 8P8jR/D/I0bv/yNH7/8jSPD/JEHs/yYf4P8mHuD/Jh3f/yYf4P8kO+r/I0nwyyNH7woAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIuLjJj//////////8jS + +P+DmfP/SGfu/yJG7f8WPO7/Gj/w/yBE8P8jR/D/I0fv/yNH8P8jR+//I0bv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fv/yNH7/8jR/D/I0bv/yNH7/8jR+//I0fv/yNG7/8jRu//I0fv/yNJ8P8lM+f/Jhzf/yYf + 4P8mH+D/Jh3f/yYh4P8jQu3/I0jwgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAdnVzML/Aw/BohPn/GT7t/xY77v8bQO//IUXv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fw/yNH7/8jR+//I0bv/yNH7/8jR+//I0bv/yNH7/8jR+//I0fw/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fv/yNH7/8jR+//I0jw/yUr5P8mHN//Jh7g/yYf4P8mHuD/Jhzf/yUx5/8jSfC0AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW2WSNho/ + 8e0iRfH/I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH + 7/8jR+//I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0bv/yNH7/8jR/D/Jifj/yYd + 3/8mH+D/Jh/g/yYf4P8mHN//JS3l/yNJ8KUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIkXyhiNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH7/8jRu//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fv/yNG7/8jRu//I0fv/yNF7v8mI+H/Jh7g/yYf4P8mH+D/Jh/g/yYc3/8lM+f/I0nwhAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAjR/AZI0fv7SNH7/8jRu//I0bv/yNG7/8jR+//I0fv/yNH8P8jR+//I0bv/yNH7/8jRu//I0bv/yNH + 7/8jRu//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH7/8jSPD/JEHt/yYf + 4P8mHt//Jh/g/yYf4P8mHuD/Jhzf/yQ56v8jSfBYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjR++DI0fv/yNH7/8jR+//I0bv/yNG + 8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0bv/yNH7/8jRvD/I0bw/yNJ8P8kN+n/Jhze/yYf4P8mHuD/Jh7g/yYe4P8mHd//JDzr+yNI + 8CkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACNG7xUjR+/pI0fv/yNH8P8jRvD/I0fw/yNH8P8jR+//I0fw/yNH7/8jR/D/I0fv/yNH + 8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH8P8jR/D/I0jw/yUo + 5P8mHd//Jh/g/yYf4P8mHt//Jh7g/yYd3/8kOurMI0vxBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVAAAAVAAAAIoAAACkAAAAqAAA + AJgAAABrAAAAJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNH8HYjR+//I0fv/yNH + 7/8jRu//I0fw/yNH8P8jR+//I0fv/yNG7/8jRvD/I0bv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH + 8P8jRu//I0fv/yNH7/8jR/D/I0fv/yNI8P8kP+z/Jh7f/yYe4P8mH+D/Jh3f/yUp4/8mKuT/Jh3f/yYj + 4ZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAPAAAAhwAAAOYAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADzAAAAlAAAABIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAI0bvDSNH790jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNG + 7/8jRu//I0fv/yNH7/8jR+//I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNG8P8jR/D/I0nw/yUu + 5f8mHuD/JiDh/yYe4P8mHuD/JTHn/yUy5/8mH+D/Jh7gXgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMwAAANICAwP/Cwwx/xIRYv8YF3v/GRmA/xYU + cv8ODkX/BAUN/wAAAP8AAAD/AAAA0wAAADUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0fvWyNH + 7/8jSfD/I0jw/yNH7/8jR+//I0bv/yNH7/8jR+//I0bw/yNH8P8jR+//I0fv/yNH7/8jR/D/I0bv/yNH + 7/8jRvD/I0fw/yNH7/8jR+//I0fw/yNI8P8jQ+7/JiDg/yUs5f8mIuH/Jh3f/yYj4f8lNun/JTDm/yYe + 3+QlHt8SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AEYCAwPuExNf/yEdwP8mH+r/Jx/1/ycf9v8nH/b/Jx/2/ycg8f8jHs7/ExJj/wEBBP8AAAD/AAAA8AAA + AEkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjRu8BI0fvtSQ06P8kPOr/I0nx/yNI7/8jR+//I0fv/yNH + 7/8jRu//I0bv/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0rw/yUx + 5v8mJOL/JTPo/yYf4P8mHuD/JS/m/yU26f8mJOL/Jh3fbQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/CAkh8yAdsv8oIPb/Jh7x/yUd6f8lHef/JR3o/yUd + 7P8lHe3/JR3q/yYe6P8mIOL/GRtu/wMFAf8AAAD/AAAA5AAAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACNJ8REjRvAxIUTvLyFD7hgiR+8JAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAjSfAZJSzk0CYc3/8lM+f/I0jv/yNJ8/8kSfb/I0jy/yNH8P8jR+//I0fv/yNH8P8jR+//I0fv/yNG + 7/8jR/D/I0fv/yNH7/8jR/D/I0fv/yNI8P8jQe3/JSXi/yU06P8lLuX/Jhzf/yYr5f8lOOr/Jirk/yYd + 380mH+AHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJAoM + MOgkH9X/Jx72/yUd6P8lHen/JR3s/yUd6v8lHuD/JCDP/yMhvv8jIrP/IyGv/yIgsv8kIb3/Ih2y/xwn + n/8TJnLlFiyVKgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0nwGiNE7mYlOOmxIifk5iQm4v8wM+T9MjXl8CUq + 5N0kLubDJDXopSQ56YQkP+1jI0TuQyNH7ycjSfARI0nxAQAAAAAlMucNJhzfzyYa3v8mKuf/ITbU/x4y + vv8iQuT/JEn1/yRJ9f8jR/D/I0fv/yNH7/8jR+//I0fw/yNH7/8jR/D/I0fv/yNH7/8jSPD/I0bv/yUu + 5f8lMuf/JTbp/yYj4v8mHd//JSrl/yYp5P8mHd/8Jh7gOwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcJCijEJB/Z/yYe9P8lHe3/JR3q/yUf3f8kIcr/JCO9/yQi + uv8lIcP/JR/R/yYf3P8mHuP/Jx7m/yYe5v8nHuj/JyXt/yVI/f8lS/3xI0jyiSNH8AkAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0jwGyND + 7oElMObrJiHh/yIX3v9ISOb/cHnv/2lw7f+CivP/YGbr/yce3/8lG9//Jh3f/yYf4P8mIeH/Jibi+SUr + 5OklMefOJDfpsSQ964UlMuiWIiTl+hsbuv8cHKr/FhR8/xUUev8ZI5v/HzfK/yNG7v8kSvb/JEn1/yNJ + 9P8jSPT/I0fx/yNH8P8jSPH/I0ry/yNF7/8lKuT/JS7l/yU46v8mKuT/Jh3f/yYf4P8mH+D/Jh7g/yYe + 4IEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAUQeyMe + zP8nH/3/Ix7b/yIfvv8kIrf/JSK//yUhzf8mINz/Jh7j/yYe5P8mHuL/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHN//JTbp/yNJ8P8jR/D/I0fvvyNH7xIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkOuqJJiDg/yYc3/8mHuD/JBzf/zc15P86OeT/Ixzf/ysm + 4f80MuP/KSPg/yUe3/8mH+D/Jh7g/yYe4P8mHeD/Jh3f/yYc3/8mHN//Jh7g/yMf4f89Rej+PUfk/ykz + 2P8fKtD/GCG8/xUaof8VGZD/GCKb/x0wuP8fOMz/IDnP/yA60v8iQ+f/I0jx/yNE6v8kOOb/JiPj/yce + 5f8mMuv/Jirk/yYe4P8mHuD/Jh/g/yYe4P8mH+C2Jh/gAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQaGYzzIx7M/xwcj/8dHYv/Ix+6/yYf3P8mHuT/Jh7j/yYf + 4f8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYd3/8lK+T/I0jw/yNH7/8jR+//I0fvsiBE + 7wIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYv + 7A8mG95wJh/g1yYf4P8mH+D/Ixvf/yMb3/8mH+D/JR3g/yQb4P8lHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8iGd//PT7l/5Ge9v+Xo/j/ipT3/3mC9f9ka/P/TVTv/zhA6f8nL9r/HCXI/xci + uP8WIKX/FhyR/xcekP8YHo//FhaB/xcQgP8YEor/HRqp/yQh0v8nHuX/Jx/l/yYf4f8mHuD/Jh7g0iYe + 4BMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADA0spR8f + kv8fHZ7/Ix7E/yYf5P8nHub/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/Jh3f/yUk4v8jRu//I0fv/yNH7/8jR+//Gz/vYAAAAAD7/P8C////CgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmH+ALJh7fXiYf4MUmH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yEY3/9BQOb/l6X4/5yp + +f+eqvn/nqv5/56r+f+bqPj/lJ/3/4eR9f9ze/T/W2Lx/0NL7v8uOef/IC3a/xkoyv8XJbb/FSGg/xUb + i/8UFnj/FRJ4/xoUl/8hGr7/JR3a/yYc4N0mHN8iAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACYf4BYmH+BEJh7ggicg5MMmINn/Jh/h/ycf6f8mH+T/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/JiLh/yNE7v8jSPD/I0fv/yNH + 7/8YPe7ZAAAAAP///1Pc3NxoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAABAAA + AAAAAAAAAAAAAAAAAAAAAAAAJh7fASYe4EgmH+CwJh/g+yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh7f/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yUd3/83NuP/U1bp/2lw7v96hPH/h5P0/5Gd9v+Zpvj/nqz5/6Gt + +v+eq/n/maT4/42X9v97g/P/ZWzy/1Fa8f89Su7/Kzzo/x0w3P8bL8r/Gy2v/xgjlv8bH6LgJSzkJwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh/gJCYf4IcmHt+7Jh/g6SYf4P8mHuD/Jh7h/yYf + 4/8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYe4P8mIuH/I0Tu/yNI8P8jR+//I0fv/xM57vizwv9g19bO4xISEmIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAA4AgMFqAEBA9oAAADUAAAAiAAAABUAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAmHt80Jh/gmCYf4O0mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh7f/yIa + 3v8fF9//Hxff/yMc3/8pJOD/NDHj/0FC5f9VWen/b3jv/4iU9P+YpPj/nar5/56r+f+eq/n/nqr5/5mk + 9/+Pmfb/cnry/zo96/8lKer/JTHq/yQ55vAjQ+3BI0nwiSNJ8EgjSfEWAAAAAAAAAAAAAAAAAAAAAAAA + AAAmHd9DJh/g/yYf4v8nIOb/Jx/j/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/Jh/g/yYe3/8mH+D/Jh7f/yYe4P8mHuD/Jh/f/yYe4P8mH+D/Jh3f/yYm4v8jR+//I0fw/yRH + 8P8YPe7/S2z4+t/i5/EoJyT/AAAASgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAaAsMN/gTE2n/AwQK/wAA + AP8AAAD/AAAA2QAAAC8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh7gHyYe4H0mHuDdJh7g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/JR7g/yUc4P8jGt//IRjf/x8W + 3v8gGN7/JB7g/zg35P9WXOr/cHrw/4CK8/+AjfP/gY3z/4mU9f+Xo/j/jJj1/zAs4f8jGd//Jh3g/yYf + 4P8mJeL/JS7m/yQ46ekkQe27I0bvfiNI8DsAAAAAAAAAAAAAAAAmHeI+IxvOyR4Zrf8jHcz/Jx/k/ycf + 5/8nH+b/Jx/m/ycf5f8nH+X/Jh/k/ycf4/8mH+L/Jh/h/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/f/yYe + 4P8mHuD/Jh7g/yYf4P8mG9//JDTo/yNJ8P8jR+//HUHv/y5T8v/k7f/6W1lT7QAAAP8AAAA7AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAECBGIPEFD/FRR8/xgVk/8aGJT/DQ4+/wABAP8AAAD/AAAA3wAAABwAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACYe4A8mHt9iJR/fxiYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH9//JR3g/ygj4P9dZOv/bHHu/2x17/90fvD/eYXx/3aC + 8P9lbu3/QkXm/ywn4f8zLuP/JyDg/yYf4P8mH+D/Jh7g/yYd3/8mHN//Jhzf/yYf4P8mJeL/JS7l/yQ5 + 6qokQuwRAAAAAAAAAAAAAAAAHxyyVBgSh9MYE4z/HBam/x4Ysv8gGbf/IRq9/yEbw/8iG8r/Ix3Q/yUe + 1v8mH97/Jx/l/yYf4f8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jhzf/yUo4/8jR+//I0fw/yBE + 7/8dQ+//ydf//7a0qvcAAAD8AAAA/gAAADEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQ5Dw9X9xUTfP8YFY3/Ixza/ycf + +f8nIPD/GhmN/wMEB/8AAAD/AAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAmHd8BJh3fRiYe4K4mHuD5Jh/g/yYf4P8nH+D/IBnh/xwT4f8hGeH/Jh/g/yYf4P8mH+D/Jh/g/yYf + 3/8mHuD/HRXh/0dK6v+LmPb/oK75/5qm+P+YpPj/maT4/5qm+P+Uoff/U1jp/xwU3f8lHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYd3/8mHN//Jhve/yUr5NQjSfCnI0nxmCNJ8YUlRPVqHzDIkxki + l/sZIZT/Fx+P/xcdjP8XGoj/FxiE/xYVgP8WFH7/FhJ8/xYTg/8eGaz/Jx/h/ycf5v8nH+T/Jx/k/yYf + 4v8mH+D/Jh7g/yYc3/8mJOH/JEPu/yNI8P8jRu//GD7v/52x+//+/PL/ISEh/wAAAP8AAAD1AAAAJAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAERITTdcgIoj/GRac/yUd5P8mHuz/JR3n/yUd6f8oIPr/HRud/wIDBP8AAAD1AAAAHwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYh4SEmHuCAJh3f3yIZ + 4P9PS9j/a2rU/0pK2v8jHeD/Jh7g/yYf4P8mH+D/JR7g/yYf4P9XVtf/aWnS/zY13v9bYez/mKb4/5ai + 9/+UoPf/lKD3/5ei9/+Zp/j/TE/o/xwT3v8jGt//JB3f/yYd4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mG97/JSjj/yNH7/8jR+//I0fw/yNI8P8jSvT/JEn1/yRJ8/8jSPL/I0fx/yNF7P8jROn/IkLl/yE+ + 3f8gN8v/GCCV/xQRcv8aF5f/IRvC/yIcxP8jHMv/JR3X/ycf4/8nHeP/JiPi/yRB7f8jSfD/I0fv/xk+ + 7/9XdPP//////5aVlP8AAAD/AAAA/xkZGemOjo8VAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJCiWfQ0ai/yIfqv8kHOr/JR7s/yUd + 5/8lHej/JR3o/yUd6P8oIPn/EhJc/wAAAP8AAABEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACNJ8QkjRe4qI0TuTiQ+7HgkPOvKGivq/3N51P/e4L//ZmjV/xwU4f8mH+D/Jh/g/yYe + 4P8lHuD/IRrh/6Ckyv/c3b//WFfW/xUN4P9WXOr/mKX4/5up+P+ap/j/mqf4/52p+f+Wo/f/Vlrq/zIw + 4v8rJ+H/Ixzf/yQc4P8mH+D/Jh/g/yYe4P8mH+D/Jhzf/yUs5f8jRe7/I0jv/yNH7/8jR/D/I0fv/yNH + 8P8jR/D/I0fw/yNH8P8jR/D/I0jx/yNI8v8jR/L/JEr3/yVM/P8hPNf/GBeF/xUQdf8UEXH/FBFz/xQQ + c/8VEXr/GhSS/yMizv8kQe3/JEz3/yRK9f8jR/H/Fjzu/62+/P///vn/KCgp/wAAAP8AAAD/RERF4aen + pwsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAASklOk/89Pbz/IBjl/yYe7f8lHeb/JR3q/yUd5/8lHej/JR3n/yYe8v8gHLr/AgMD/wAA + AFYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjR+8dI0fwSSNH730jR++xI0fv3SNH7/sjSPD/I0jw/yNJ + 8P8bQ/P/T2vj/9HQwf+Ii8//HRfh/yYc3/8mHuD/Jh7g/yYf4P8cFOL/dHbS/9fYwP97fdH/HBTh/yEZ + 3/9BQeX/WFzq/1NW6f9TVun/Z27t/4yY9f+eq/n/kJ32/4uW9f9zffD/MCzi/yQc4P8mH+D/Jh3g/yYe + 3/8lM+f/I0jw/yNI8P8jR+//I0fv/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0bv/yNH8P8jR/D/I0fw/yRK + 9v8hP93/GSGZ/xUUff8ZIJn/HTC6/x84zf8fOM7/HzPD/xwrrv8XG4r/FRV9/xYZhv8aJKH/HzbI/yBC + 6/8nTfb/6vH//7u5tP8AAAD/AAAA/wAAAP+BgYHg////CwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUbHT/MYmbH/x8Y1P8lHev/Gxej/xgV + iP8hG8z/Jh7r/yUe6P8lHef/JR3r/yUf3/8FBRX/AQMKTgAAAAAlSv0CI0buJSNH71ojR++VI0fvyyNH + 7/IjR/D/I0fv/yNH7/8jR/D/I0fw/yNH7/8jR/D/I0fw/x9D8f83WOr/wMLF/8fIw/9BV+T/Hyjn/yYg + 4P8mHN//Jh3g/x4W4f9PTtj/0NHC/52fy/8hHOH/JR3g/yEZ3/8fFt//IBff/x8X3/8eF97/LSnh/3qF + 8f+eqvn/nan5/5qo+P86OOT/Ihrf/yYc3/8mIuH/JDzr/yNJ8P8jR+//I0bv/yNH8P8jR+//I0fv/yNH + 7/8jR+//I0fv/yNH8P8jRu//I0fv/yNH8P8kSvf/HjPB/xYTfP8aIaH/IT/b/yRJ8/8kSvf/JEn1/yRJ + 9f8kSvb/JEr3/yNH7/8gPNf/HCux/xcaiv8UEHX/Dw+A/0BQwf//////ZmVj/wAAAP8AAAD/AAAA/7Gx + sdj///8GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAABSkNGjv83NrL/IRnt/x8ZuP8UE3L/ExJw/xsXnv8mHu3/JR3o/yUd6P8lHen/Jh3p/wsJ + J/wVKod/Jkz/kCNH79AjR+/4I0fv/yNH7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNH8P8jR+//I0fv/yNH + 7/8jR+//IUXw/yZK7/+qssr/zMvC/4qb1P8ZQvP/I0Lt/yUy5/8mI+H/Ixjg/zMt3f+8vsX/urvF/zAt + 3v8jG+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8gGN7/MS7i/4CK8v+CjPP/Rkfn/yMZ3v8mHN//JSnj/yRD + 7v8jSfD/I0fv/yNH7/8jR+//I0fv/yNG7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH8P8jR/D/JEr2/xwv + t/8WEXz/IhnD/ycm6v8kRPL/I0jw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fx/yNJ9P8kSvf/I0jw/yE+ + 2/8PGpz/UlGZ//r8//8rLTj/AAAA/wAAAP8KCgr/39/fy////wIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBR2oQ0aj/x0apP8lHef/FhOH/xwe + ff8YGHb/HRew/yYe7v8lHej/JR3n/yUa5/8lJuf/Ij3K/iRJ8v8jR/D/I0fv/yNH7/8jRu//I0fv/yNH + 7/8jR+//I0bv/yNH8P8jR+//I0fv/yNG7/8jR+//I0fv/yNH7/8jRvD/HELx/5ai0P+jrs3/vcDG/z5e + 6P8dQ/H/I0nw/yNF7/8jNen/ISPj/56gy//Oz8L/SEba/yAV4P8mHd//Jh3f/yYd3/8mHeD/Jh3f/yYd + 3/8jGd//Jx7f/yYe3/8hG+D/JSvk/yQ56v8jR/D/I0jw/yNH7/8jR/D/I0bv/yNH7/8jR+//I0fv/yNG + 7/8jRu//I0fw/yNH8P8jR+//I0fv/yRK9/8eNML/FRF5/yMczf8nIOf/Jhzf/yUn4/8jRu//I0fw/yNH + 7/8jR/D/I0fv/yNG7/8jR+//I0fv/yNH8P8jSfL/I0z1/x5G9v9JYdr/VFVa/wAABP8AAAD/AAAA/zMz + NP/9/f2xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAEg4PQug5O53/Ghak/yMc2P8UE3X/ExJ2/xUTfv8jHdv/JR3q/yUb5/8lH+j/JDPr/yNH + 8P8kSff/I0fx/yNH7/8jR/D/I0fv/yNH7/8jR+//I0fv/yNH7/8jRu//I0fv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fw/yNH7/8YPvL/fY/X/5ek0P+dqM7/jZvT/xk/8v8jR+//I0jw/yFJ8f8ZPvH/eorX/9fV + v/9ocdf/GyLn/yUp4/8lJuL/JSbi/yYm4v8lKOP/JSzl/yUw5v8jNej/Izzr/yNE7v8jSPH/I0nw/yNH + 8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0bv/yNG7/8jR+//I0fv/yNH7/8jSPP/IkDf/xUV + e/8hGr7/Jx/n/yYf4P8mHuD/Jh3f/yRA7f8jSPD/I0fv/yNH7/8jR+//I0fv/yNH7/8jSfD/I0Tu/yQ5 + 6v8lMuf/JjDx/x4mrP8AAAD/AAAA/wAAAP8AAAD/ZGRk/////4IAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAERJX/ywvkv8ZFpv/JR3o/xkV + kv8UE3f/Hxm8/yYc7/8lHOf/JSvq/yNB7v8jSvD/I0fv/yNG7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH + 7/8jR+//I0fv/yNG7/8gRO//IUXv/yNH7/8jR+//I0fw/yBE7/8kR+//IETv/xM68f9bc9//r7TJ/1hz + 4f/FxsT/Olrq/xxB8f8sT/D/jaL3/xxB8v9Tb+L/1NC//4ub1P8bQ/L/I0jw/yNH7/8jR+//I0fv/yNI + 8P8jSfD/I0nw/yNJ8P8jSfD/I0jw/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0bv/yNG7/8jR+//I0fw/yRJ9f8ZI5r/GxWc/ycg5/8mH+D/Jh/g/yYc3/8mKOT/I0bv/yNH + 7/8jRu//I0fv/yNH7/8jR+//I0jw/yU26f8mIeH/Jhzf/yYc3/8nHeb/IRu7/wICBP8AAAD/AAAA/wAA + AP+Pj5D7////MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAABAG0SEmD/HByE/xYUg/8kHeP/JRvo/yMZ2f8mH93/JSvW/yQ96/8jSfD/I0jw/yNH + 7/8jR+//I0fw/yNG7/8jR+//I0fw/yNH7/8jR+//I0fw/yNG7/8jR+//Ikbv/zda8f8sT/D/HkLv/yNH + 7/8bP+//aIT1/zxd8f8kSfD/t8b8/97i8v/Cw8T/Olvo/6auzP+KmdT/EDfy/zlb8f+ouvn/HUHw/zVW + 6//DxMT/q7LL/yVJ7/8fQ/D/IUXv/yRH7/8jR+//Ikbv/yNG7/8jR/D/I0bv/yNH7/8jR+//IUXv/x9D + 7/8jR+//I0bv/yNG7/8jR+//Ikbv/x5C7/8iRvD/I0fv/yNG7/8jR+//Ikbv/yNG7/8kSfT/ID3W/xYT + f/8lHdb/Jh/j/yYf4P8mHd//JiHg/yRC7v8jSPD/I0bw/yNH7/8jR+//I0fw/yNK8f8lNOf/Jhvf/yYd + 3/8mH9//Jh7h/yYf4v8oIef/DQ4+/wAAAP8AAAD/Dg8O/9zc2akAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAkhESX/8UEn7/FRF8/yMb + 1P8mJd3/JTTW/yRB2P8jSer/I0nw/yNH7/8jRu//I0bv/yNH7/8jRu//I0fv/yNH7/8jR/D/I0fw/yNH + 7/8jR/D/I0bw/yNH7/8gRO//QGLy/3GO9v87XfH/HkLv/yJG7/+Lovf/I0bw/2qF9f/Z4v3/x9P4/9DP + xv9SbeH/Um3j/8XGw/9LaOz/Rmf0/3CN9v8YPe//IUbw/6auzP/FxcT/NFXp/zVY8/8vUvD/HUHv/yBE + 7/8nSvD/HkPv/xxB7/8kR+//Ikbw/xtA7/8rTvD/PV/y/yBE7/8iRu//I0fw/yJG7/8mSu//QmPy/yJG + 7/8jRu//IUXw/yRH7/9TdPP/I0fw/yRJ9/8bKKX/HBWi/ycf5/8mH+D/Jh/g/yYc3/8kN+n/I0nx/yNH + 8P8jR+//I0fv/yNH8P8jSvD/JDjp/yYd3/8mHuD/Jh/f/ycg5P8mH+L/IBjX/x8X6v8VE3b/AAAA/wAA + AP8sLCrz+fn/JgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAACjDw1Q/xQQev8aH4//JD3U/yNH5v8jSfD/I0jz/yNH8P8jRu//I0fv/yNH + 7/8jRu//I0fv/yNH7/8jR+//I0fw/yNH8P8jR+//I0fw/yNH7/8jR+//I0fv/yNH8P8dQe//Jknw/1+A + 9f8lSe//MVPw/22J9f8UOe7/ZYD0/y9S8f8ZQPD/q7PM/4OT1v8ZQPL/qbDI/9PV2v/F1P7/jKP3/xg9 + 7/8ZQPL/gpLW/9TRwP9NaOH/Tm/2/22K9f8wVfH/Vnf0/3+b9/+NpPj/S2vy/xk+7v8mSu//PFzx/3qT + 9v91j/b/Fzzu/zlf8f8mSvD/HULv/2WD9f+luPn/dI72/xY77v8/Y/L/LlHw/2eE9f8kSfH/IkXr/xga + jP8jG83/Jh/k/yYf4P8mHd//JSTh/yNG7/8jR+//I0fv/yNH7/8jR+//I0nw/yQ46f8mHt//Jh7g/yYf + 4P8nH+X/Ix3N/w8Mi/8uLpD/S0yv/xMVQP8AAAD/AAAA/xoZbPZFP+43AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK4PEkz/GR6Y/yJC + 5P8jSvX/I0fx/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH7/8jR+//I0fw/yNH + 8P8jR+//I0fv/yNH7/8jR+//I0fw/yNI8P8eRPD/M1ny/z5k8v9xjPb/UXP0/xU97/9lgvX/Kk7w/w00 + 8f+Il9P/qbHM/xU88/9het7/vsDD/1l37v+Mpfn/I0fv/xk/8v9cdeD/1tK//3KG2f8xVfT/hqH3/ytR + 8f8qTfD/I0fv/zVX8f+Qp/j/T27z/1Fw8//G0/z/t8f7/yRI8P8eQu//TnHz/ypO8P8oTPD/PWHy/xM4 + 7v9ph/X/IkXv/15+9f9nhfX/YoD0/yBG9P8gPNT/GRaT/yYf4v8mH+D/Jh/g/yYc3/8lM+f/I0nw/yNH + 7/8jR+//I0jw/yNI8P8lMuf/Jh3f/yYe4P8mHuD/Jh/j/yQe0/8KB3n/WVya/+Lk5P/x8+v/zc/Y/1db + ef8HCFX/JB3i/yAW4a0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAYMqkMFy2PyyNE4f8jRu//I0jy/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH + 7/8jR+//I0fw/yNH7/8jR+//I0bv/yNH7/8jR+//I0fv/yNH7/8jSPD/I0nw/yNJ8P8jR/D/I0Pt/yQ/ + 7P8aMun/f5T0/9fi/P8fNOj/GCvo/3GE8v+Mo/f/VXX3/22B3P+/wcX/K0zs/y1S8P/Jy8j/b4LV/11+ + +P82WvL/G0Lx/ztc6v/IyMP/mKTP/yJI8f9wi/b/H0Pv/yFF7/8jR/D/HULv/yFF8P9wjfb/Zoj1/xxC + 7/+csPj/QGHy/xY77v9qiPX/PV7y/xI47v9UdvT/RGXy/3iT9v8mSfD/NFnx/8bU+/9mgvX/GkH1/x0z + vf8dFqj/Jx/m/yYf4P8mHuD/Jh7f/yQ+7P8jSPD/I0fv/yNJ8P8kQe3/JSjj/yYc3/8mHt//Jh/g/yYf + 4P8nH+X/EQ2W/zw+iv/q6+v/8vHt/+rq6v/y8e//9fbz/4eJwv8ZGMb/JCzp8SYc3xoAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJEfzPiVK+9ElS/z/I0j0/yNH + 8P8jR+//I0fv/yNG7/8jR+//I0bv/yNH7/8jR+//I0fw/yNH7/8jR/D/I0fv/yNH8P8jR+//I0fv/yNH + 8P8jSfH/I0jw/yNC7v8kOOr/JS7l/yYm4v8mIeH/Jh7f/yAW3v9DROT/uMD1/4SF7f8WCtz/SEbk/9zi + +v+DiPD/qbDn/8jJwv9ITNv/Gx7n/6+z3f++wcT/Wm/r/0Na7v8dNev/JkHs/66zyf+4vMf/K03s/2WA + 9f8lSO//IUXv/xc87v8bQO//JErw/19+9f+owfv/a4j2/26O9v9LbvP/FDvv/1l89P/E1fz/Xnz1/ylQ + 8f+wxfv/gJn3/xg/7/8oUPH/Vnf0/zFW8f8iSff/Gyyv/yAYuf8nH+X/Jh/g/yYe3/8mIuH/I0Tu/yNI + 8P8jSfD/JDfp/yYg4P8mHd//Jh/g/yYf4P8mH+D/Jh/j/yIb0f8REHr/uLvQ//b18P/p6er/6unq/+rp + 6v/s6+v/9/fs/4CEw/8YN+X/JSzmSgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACNG73EjRu/5I0fw/yNH7/8jR/D/I0fv/yNH7/8jR+//I0fv/yNH7/8jRu//I0bv/yNH + 7/8jR/D/I0fv/yNH7/8jR/D/I0fv/yFF8/8dQ/X/HD7y/x4z7v8lKuX/JiHg/yYc3/8mHN//Jh7f/yYe + 4P8mH+D/JBrf/ywp4f8wMuL/oKby/0hF5f8WDt7/qKvz/9bZ+v/P1PP/1tfG/2lo0/8UCuD/UE/k/8jJ + xv+lrNf/S0vo/x8V3v8eF+H/iozO/87Pwv9CQ9v/VFTq/x8f4f+DjPD/q7r3/6Gw9v+suPf/mZ7x/zY8 + 5v9dYen/V17p/yMr5f8iKeT/LDjm/4mO7//Lz/j/NDvm/yUq5P8iJuT/JSnk/yYt5f8eKuX/IjPo/yVC + 8f8bJ6X/IRnD/ycf5P8mH+D/Jh3f/yYk4v8jSfD/I0Xv/yUu5v8mHd//Jh7g/yYf4P8mHuD/Jh/g/yYf + 4P8nH+b/Ew6v/2Nmpf/9/vf/6+vr/+7u7v/v7+//7Ozs/+rp6v/t7ev/7Ovm/0pk5P8cQvGRAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNH7wUjR/CaI0bv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH7/8jR+//I0fv/yJH8v8dQ/T/KkrZ/0lb + vv9bYLP/UU60/ykh2f8lHeH/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8vLeH/JyLg/zI14v85NeP/VVfn/xwT + 3v8sJ+H/pKXy/7O19P+2ucj/j5HN/x0X4v8YEeH/cHLS/8/Txv9ZXeP/HhXg/x0U4f9jY9X/1tfB/2Jh + 1f9BPub/JBvf/0M94/+mpvL/tr31/zIt4f8ZEd7/Ihnf/x8W3/8fFt//Jhzf/yYd3/8mHd//HRbf/yEc + 3/8mHd//JBvf/yYc3/8mHd//Jhzf/yYc3/8mHN//Jx/j/xsanP8iHMn/Jx/k/yYf4P8mHuD/JSHg/yUv + 5v8mJeL/Jhzf/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/ycf5f8WE5v/QUNX/8DAu//08/T/2tra/9fX + 1//n5+f/9vb2//b29v/59+z/lKbs/xc98N0jR/AKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAjR+8OI0fvsyNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH + 7/8jR+//I0fv/yNI7/8hRvT/Hj7p/1BhtP+anKn/uri8/7+/w/+oqqj/MS/G/yMb5P8mH+D/Jh/g/yYf + 4P8mH+D/Ixrf/0BD5P9EROX/Ly3h/zw85P82N+P/JBzg/yMc4P8bE97/FQ7g/5KTzP+ytcf/JiHg/yEY + 4f8vK97/wcTF/3+C0/8bE+H/IBjh/0JA2//NzsP/h4jP/zk55v8oIOD/IBjf/xcP3f9dXuj/g4nu/yMc + 3/8lHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/JR7g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuL/Gxeb/yMdzf8mH+P/Jh/g/yYf4P8mHuD/Jhzf/yUa3/8kG9//Jh7g/yYe4P8mH+D/Jh/g/yYe + 4P8mH+D/Jx/l/xcWfP8AAAD/FBQT/zk5Of8UFBT/EBAQ/ykpKf9ZWVn/mJiZ/+fm3v+6xO3/HUPv/SJG + 7zEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0bvDSNH77wjR+//I0fv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fw/yNH7/8jR+//I0fw/yNH7/8jR+//I0fv/yNI8P8jSvD/ID7y/yQy2f9tcaP/trW2/72+ + y/+kpbH/uLnC/4SGqP8jHtj/JR7h/yYf4P8mH+D/Jh/g/yYf4P8lHuD/JR7g/1lc6P99g+3/aW3q/yYf + 4P8lHuD/Jh/g/yYe4P8cFOL/c3PS/8nMw/85Ntz/Ihng/xsT4f95e9H/wMPE/zAs3v8gGOH/Kibf/7W3 + x/+oqcn/P0fl/ych4P8mHuD/Jh/g/xcP3f9hY+n/Vlfn/x8X3/8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf4v8bF5r/Ix3L/yYf4/8mH+D/Jh7g/yYe + 4P8jHN//KC/l/y476P8lHuD/Jh7g/yYf4P8mHuD/Jh7g/yYf4f8mH+L/FRRv/wABAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8BAQH/ysnE/9PY8f8mSO7/IUXwVwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNF + 7QEjR/CmI0fv/yNH7/8jR+//I0fw/yNH7/8jR+//I0fv/yNH7/8jRu//I0fv/yNH7/8jRu//I0fv/yNI + 8P8jSfD/JELt/yAr7P8pJs3/fn2f/7y9wP+5usr/t7jI/4eIjf+oqar/UFC2/x8Y5f8mH9//Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8lHt//IRnf/ych4P8iGt//JR3g/yYf4P8mH+D/Jh7g/x4W4f9VVNj/0tTC/1ZV + 2P8dFeH/Ihrg/zMw3f/FyMT/cnPS/xsU4v8fGOH/kpTN/7y9w/9/iuj/Pjzl/xoQ3v8jG9//GRDe/z49 + 4/9rcOr/HRXf/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8mHuD/Jx/j/xoXmP8iHMj/Jx/j/yYf4P8mHuD/JBvf/yk86v99lPf/YG/w/x8Y3/8mHuD/Jh/g/yMb + 4P8lHuD/Jh/h/ycf4/8VFHT/AAEA/wAAAP8AAAD/AAAA/0ZGR/+hoaH/RkZG/wEBAf/Ozcr/5uj3/y1O + 7P8gRPBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAB0WvA4fGbwhIhy+LiAavSglHL0QHjPdPh1D8P8fRPD/IEbx/yJH8f8iSPH/I0fw/yNH + 7/8jR+//I0fv/yNH7/8jR+//I0fw/yNJ8P8jSfD/JEHt/yUv5v8hG+X/KiPK/4WGoP++v8T/t7jI/7a3 + xv+2t8b/ubrJ/62vs/85NcH/Ihrl/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/JR7g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/IRng/zw63P/LzcP/eHnR/xsU4f8mH+D/GxTh/4GD0P++wcX/LSne/xoR + 4v9rbNT/y8zA/4mQ4P+8wfj/SETl/zEt4f9TUub/pKzy/0VE5f8gGN//Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8nH+T/GxeZ/yEbwv8nH+T/Jh/g/yYe + 4P8gGd//aXjx/6ay+v9YY+3/Hxjf/yYf4P8mHt//SU7n/ygj4P8lHuD/JyDm/xcUg/8AAAD/AAAA/wQE + BP+dnZ7/9vb2//r6+v/v7+//b29w/5uamP+vsr3/MFLq/yBE8G0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcA/JiDAeiEavaJISdHJeILp4X2H7PV7huv8cHnl+S4q + wuRTWtnmT17i/z1Q4P8zSOH/KUPk/yE/5/8dPun/HD/s/x1C7/8hR/H/I0nx/yNJ8P8jR/D/JD3r/yUs + 5f8mH+D/Ihjk/yoly/+Fh6L/vb/F/7e4yP+2t8b/trfG/7a3xv+4ucj/t7m8/05OtP8eFub/Jh/g/yUf + 4v8kHuT/JR7j/yUe4f8mHuD/Jh/g/yYf4P8mHuD/Jh7f/yYf4P8mH+D/Jh7g/yYf4P8kHOD/KiXf/7e5 + xv+cnsv/Hxnh/yYe4P8hGeH/OTfd/8nMw/9sbdT/FQ3i/0lH2v/Q0sL/bG7R/3p87//Z4Pr/1Nz6/8TJ + 9/9YWef/HBXe/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf + 4P8mHuD/Jh/g/ycf5f8bF5//Hxu2/ycf5f8mH+D/Jh7g/yIZ3v8+Ruj/l6j5/19o7f8dFd7/Jh/g/yIa + 3/9zffD/QEDl/yEZ3/8nIOn/HBic/wIDAv8AAAD/X19f//z8/P/s6+v/6enp//Hw8f/j4+P/EBAN/0ZH + VP83VvL/IUfyaQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYh + v84fGb3/GxW7/01R0/90gOb/aG7e/29y4P9eZd//LCjH/2Vr4f+Ci+v/g4rq/4CJ6P+Aiun/fonr/3OB + 6v9fbuf/R1nj/y1D4f8iPej/JTLo/yYl4v8mHd//Jhzf/yMb4/8nI9D/g4Wh/76/xf+3uMj/trfG/7a3 + xv+2t8f/trfG/7a3x/+9vsf/enyn/yMe2P8iHen/KB/b/y0gz/8rINP/Jh/g/yMe6v8hHuz/Ix7p/yQe + 5P8mHuD/Jh/f/yYf4P8mHuD/Jh/g/yUe4P8gGuH/nJ7L/7q8xv8sKN//Ixvg/yYf4P8cFeH/iYzO/7q8 + xv8oI9//LCje/72/xf+Ymcz/FA7f/zAs4f8+O+T/Ix7g/x0V3v8mH+D/Jh/g/yYe3/8lHuD/Jh/g/yYf + 4P8mHt//Jh/f/yYf4P8mH+D/JR/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jx/m/x0Zqv8dGaf/Jx/m/yYf + 4P8mH+D/JR3f/yEh4f+FlPb/lZ/2/zQy4/8jGt//IBnf/3R98P9nbu3/Hxbe/ycf5v8iHMP/AAAN/xQU + Ef/R0NH/8fHx/+np6v/p6en/9vb2/7CwsP8AAAD/ZGh9/y9P7/8gPe9XAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIBrDYF5e2dCoqu35s7Tx/7i58v/ExPb/0dD5/1BN + 4/8cFN3/IRrb/yMd2v8mH9j/KiXW/zMw1f9ISdv/ZGnh/3iA5v+Lle7/bnbf/yIbyP8mHOD/Jh3f/yYf + 4P8lHuH/IRvc/3Byo/+9vsL/t7jI/7a3xv+2t8f/trfG/7e4x/+3uMf/urzM/7q+z/+us7z/dXeT/1Es + Y/9eJjv/Yygw/2EoM/9bJ0X/USZk/0Mki/83IrD/LCDR/yMf6P8iHuv/JR7k/yYf4P8mH+D/Jh/g/xwV + 4f99ftH/z9HC/0NB2/8gGOD/Jh/g/yAY4f8/Pdz/zc/D/2Zn1P8WD+P/nJ7L/7i7xv8rJt//IRjg/yEZ + 3/8lHd//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8nH+X/IBu7/xoXlv8nH+T/Jh/g/yYf4P8mHt//Ih7g/1lw8f9qfPL/MzDi/yMb + 3/8gGN7/bHTv/4uX9f8oIuD/JR3h/yYe4/8KClH/Rkc///X09P/q6ur/6enp//Lx8f/39/f/RENE/wIC + AP9zeaH/H0Hr/zAz5tQpH99NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAmJz3A+Lh/ynU1P1f0tL8mcXI+tHT1vz6ZmXq/xwS3v8mHeD/JR3g/yUd4f8lHeH/JBvh/yEZ + 3/8fFtv/IRnX/ygi1/8wLNz/Jh/c/yYf4P8mH+D/Jh/g/yEZ5f9EQ7f/srO0/7m6yv+2t8b/trfH/7a3 + xv+4ucj/tbbF/7W4yP+fn6n/e2Zo/25JSP9rOjH/aCkb/2coHv9mKCH/Zygg/2goHf9pKBn/aikY/2gp + Hv9iKDD/VCZa/z8jmP8rINL/Ih7s/yMe5/8mH+D/HRXh/19f1v/W18H/YWHV/xwV4f8mH+D/Jh7g/x0W + 4f+TlM3/tLfH/x4Z4f9yc9L/ztDD/0I/2/8hGOH/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4/8jHc7/GBWL/yYf + 3P8mH+H/Jh/g/yYe3/8mHuD/H0Du/x016v8kGt7/Jh/g/yAY3v9kbO3/n635/0RF5v8gF9//JR3m/xwZ + rP+OkZv/8vHt//Ly8v/39/f/19fX/2RkZP8AAAD/MC8m/1xnuv8VNez/ZnDu/4uX9f9BQuXAIBjfMwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmKTzA2p3 + 6ikwM+JeIyjk4CUn4/8lKeT/JSXi/yYe4P8mHuD/Jh7g/yYf4f8mHuH/JR3h/yQc4P8mH+H/Jh/g/yYf + 4P8mHuH/IRrc/3t8qP++v8f/trfG/7a3xv+2t8b/uLnJ/7Kzwv+Qk5v/gHV5/2QxLf9hIBv/ZCIe/2Ul + If9lKCT/Zigk/2YoJP9mKCT/Zigk/2YoJP9lKCT/Zigj/2coIP9pKRr/aSkZ/2EoMv9MJXL/MiG//yMe + 6f8dF+n/RUPb/8/Rwv+Ehs//HBXh/yYe4P8mH+D/Hxfh/0dG2v/P0cL/W1rX/0VD2v/S1ML/YGDV/x0U + 4f8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYe4P8mHuD/Jh7f/yYe + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf3/8YFoz/IxzK/ycf4/8mHuD/Jh7f/yYe3/8kP+z/I0Ht/yYe + 3/8mHuD/IBje/2Nr7f+hrvr/cHjv/x8Y3v8mH+L/GxTU/1JTq//09O//sK+u/1VVVv8QEBH/AAAA/w0M + B/94eIn/Jz3H/yE68P8sJeD/Vlrq/3uG8f9cYuvtJh/gPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiSfHUI0fv/yQz5/8mIuH/Jh7g/yYf + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yQc4/8sJ83/nZ+t/7u8yf+2t8b/trfG/7e4 + x/+0tcT/jpGZ/3lsb/9jKCP/ZSQg/2UoJP9mKCT/Zikk/2YoJP9mKCT/ZSgk/2YoJP9mKCT/ZSgk/2Yo + I/9mKCL/Zigi/2YoIv9mKCP/Zygf/2opGP9mKST/UyZg/zIetv8uLOf/vL/K/6aoyf8kHuX/Jh7m/ycf + 5f8mHuP/Hxnj/5udzf+srsn/MjDe/8HDxP+Fh8//HBXi/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYe4P8mH+D/Jx/m/xwY + of8eGa3/Jx/m/yYf4P8mHuD/Jh3f/yQ66v8jS/H/JTDm/yYc3/8gGN7/Y2vt/5yo+P+Vovf/Pj7l/x8W + 3v8oIeX/ZWnJ/4mNu//Awcb/AwMA/wAAAP8TEgr/e3qA/0BGq/8bP+f/Ji/n/yQa3/8fFt7/Ihvf/1JW + 6f81MuOfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJCnkSyM96/glKOP/Jhzf/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yYf + 4P8mH+D/Ihrk/zczxP+qrLL/ubrJ/7a3xv+2t8b/ubrK/5KTm/+Fg4j/ZjEt/2UlIP9mKCT/Zigk/2Yo + JP9mKCT/ZSgk/2YoJP9mKCT/Zikk/2YoIv9qJxn/aScc/2coJP9lKCf/YSQk/2EjIf9iIx7/YiIc/2Qi + Gf9nIw//ZyMV/1QiUf+yqLH/yszI/zAssf8ZEqf/IRm9/yMbzf8dFNb/TUzT/9DSw/9ZWtb/mpvM/6qs + yf8kHuT/Jh7m/ycg5v8nH+X/Jx/k/yYf4v8mHuD/Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8nH+T/IhzG/xkWkv8nH+P/Jh/g/yYf4P8mHN//JTLn/yNK + 8P8jRu//JSjj/x8V3f9jaez/m6j4/5ml+P+BjPP/KCPg/zAs4f/8////e3yK/y4xV/8aGjz/DAwf/1FU + gv87R7b/GTnf/yRH8ugmIuGUJh7f5CYf4PslHd//Ixrf8yYf4EEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUd3wclHuDXLkLq/yYf3/8iGd//Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8hGeX/Pj2//6+xtv+4ucn/trfG/7i5 + yP+srbr/i4+W/4Brbv9hIBv/Zigk/2YoJP9mKST/Zigk/2YoJP9mKCT/Zigk/2YoJP9mKCL/ZSko/0s1 + fP83Prf/MEDJ/zNEzf9PWcr/WmHG/19jwf9fYLn/X1uv/2BXpv9USZv/QTOG/2hvuv+Hm9z/MkCs/xUf + mf8XIJj/FBuW/xIVkv8ND43/mZy1/6Ciuv92eKn/zMvD/ywpnP8XEZ7/Hhiu/yEbvv8jHc3/JR7b/ycf + 4/8nH+b/Jx/l/yYf4v8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+L/GheV/yIcyP8nH+T/Jh/g/yYd3/8lJuL/I0fv/yNI8P8jRO7/HTfr/1Zr8f+cp/f/lJ/3/5+s + +f9PVOn/YF/q/////f9BQDz/AAAA/x4nrP8kPeH/Hz/l/x5C8P8kS/T/JDHmoQAAAAAmH+AQJh/gKiYf + 4DkmH98lAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJh7gIyEa3/dKT+j/c33w/zk45P8gGN//Hxfe/yEY3/8iGt//Ixvf/yQc4P8lHeD/JR3g/yUe + 3/8mHuD/Jh7g/yEY5f9EQ7v/tLW6/7i5yP+2t8b/ubrK/6ChrP+RlZ3/dFFR/2MiHf9mKCT/Zigk/2Yp + JP9mKCT/Zigk/2UoJP9mKCT/Zigk/2kmGf9QMmj/HEv//x9J+v8bRPf/QGP4/56s//+erP//nav//52r + //+cq///nav//5ip//8/Y/7/Fj71/xpA8v8iSPX/I0j0/yxQ9P9DYfL/TWjt/0hi5f9TauT/a4Li/1Jo + 0f+El9n/MD+q/w0Wj/8QFIT/EBF8/xIQef8VEX//GBOO/xwWpP8gGb3/JB3S/yYf4P8nH+b/Jx/l/yYf + 4v8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf5f8hG8H/GheY/ycf5f8mH+H/Jh/g/yYd + 3/8kO+v/I0nw/yNI7/8gRvD/MVPx/5Cd9/+cpvj/kZz1/ywp4f+2uf//sbGn/wAAAP8BAAD/Hzu2/yRL + //8jSPL/I0jw/yNB7f4mIOA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmHuA8Jh7g/yEZ3/9GR+b/fory/3R9 + 8P9XW+r/R0fm/z085f81MeP/MCzi/ysn4f8mIuD/JB3f/yMb3/8iG9//GxTk/0JBuv+0trr/uLnI/7a3 + xv+6u8v/n6Cq/5OXoP9uREP/ZCQf/2UoJP9mKCT/Zikk/2YoJP9mKCT/Zigk/2UoJP9mKCT/aCgc/1Ys + Vf8iPfH/IEP4/yFE7/8pS+//Umzy/1138/9eePP/Y3z0/2N89P9jfPT/Z3/0/zla8v8fRe//I0nw/yNJ + 8P8eRfD/SGjz/5Gg+P+eqfn/oqz6/5yn+v+Snvr/jJv7/3yO+P93i/v/aH73/1Vu8P9CXOX/MUnX/yI5 + xf8YKrH/EyCd/xEZjv8TFYr/FxWR/xwWpP8hGb3/JBvS/yYd3v8nH+T/Jx7l/ycf5P8mH+P/Jh/i/yYf + 4f8mH+D/Jh/h/ycf6P8dGab/Hhqu/ycf5f8mH+H/Jh3f/yUm4v8jR+//I0fv/yNH7/8dQu//Smby/42d + 9/9HVOv/LTfp/+bq+f8zMi//AAAA/wAAAP8aNKr/JUr7/yNH8P8jSO//JSnjogAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACIa31ceFt//HBPe/xcP3f8jH+D/Vlrq/3qF8f9+ivL/eYbx/3uF8f96hPH/fYny/4SQ + 9P+FjvT/fYby/3iB8f9xe/X/cXrN/6mpsv+5usr/uLnJ/7m6yv+io6//lJii/3BLSv9kIhz/Zigk/2Yo + JP9mKCT/Zigk/2YoJP9lKCT/ZSgk/2UoJP9mKST/aCce/1gmT/82JbP/JCPn/yAj7f8eIOT/HiHi/x8i + 4/8eJOP/HiXj/x4l4/8eJuT/Iyrk/yUs5P8lLeX/JS/l/yQ06P8iOer/K0ft/z9b8P9TbvP/aID1/3uP + 9v+Km/f/lqP4/56o+P+iq/j/oqv5/56p+/+Wo/z/iZr9/3iN/P9kfPr/Tmr0/zhW6f8mQ9j/GjPE/xUo + s/8WIan/GR2o/x0brP8bFbb/HhbC/yAXzv8iGtf/Jh7e/yYe4v8mH9//JB3S/xwYpP8UEnT/HBik/yYf + 4v8mH+P/Jhzf/yUx5/8jSfD/I0fv/yNH7/8fRO//J0vw/xQ67v92k///trWu/wAAAP8AAAD/AAAA/xgw + mv8lS/z/I0nw/yUw5tkmHN8VAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAODPjalFQ5/90dO3/kpPx/5uf + 8/8xLeH/HRbe/yQe4P8lH+D/JyHg/yok4f8xLuL/REPm/2x07/+KlvX/kZ/2/5ek+v+Snen/mpuq/7e4 + xv+pqrf/uLnJ/6ytuv+RlJz/joSL/18lIP9lIx//Zicj/2YoJP9mKCT/ZSgk/2UoJP9lKCT/Zigk/2Yo + JP9mKCT/aSkb/2gnHf9XJFD/Oh2g/yQY1/8dFOL/Ixng/yYc3/8mHN//Jh3f/yYc3/8mHd//Jh3f/yYc + 3/8mHN//Jhzf/yYd3/8kHd//IR7h/x4i4v8eKOX/IjTo/ylA6/80UO//QmHx/1Rw8/9lfvX/dYr2/4WW + 9v+Sn/f/nKb4/6Kq+P+iq/n/m6b6/42c+v95jfv/YXv7/0hn9v8yU+z/HDzd/21/0v9CUrr/KjSl/ycp + lf8WE4r/FxKH/xcSg/8VEnn/FBJ1/xQTev8TEnP/Gxea/yYf3/8mH+T/Jh3f/yQ46v8jSvD/I0fv/yNH + 7/8hRe//GT/w/7rK/v9QTkX/AAAA/wAAAP8AAAD/GTCP/yVM/v8lMufhJhzfKwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAADIyfqJ1NX8/9ra/f/b2/3/4uP+/1RR5/8eFt7/JR7g/yUe4P8lHuD/JR3g/yQc + 3/8hGd7/IBjf/yci4P8uK+L/Mi/i/zY26P95frL/r7C1/4yMk/+3uMf/ubrK/5GRmf+fo67/j4aN/2c4 + NP9hJB7/YSId/2EhHP9iIRz/YiEb/2IhHP9iIRz/YiEb/2IhHP9hIhz/YiUf/2YsHv9rPC7/alRx/1xb + yf81MuP/IRrn/yQc4/8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe3/8mHt//Jh3f/yYd + 3/8mHN//JBzf/yId4P8gIOH/Hibk/x0u5/8gOer/JUTu/y9R8P88XvL/TWvz/1959P9zh/X/hpX2/5ah + 9/+gqvj/oar4/5ij+P9+kPn/ws3//4+l/v+Sqvz/aYXv/xMy1v8WK7r/FB+b/xUWg/8UEHX/FBBz/xQS + ef8TEnT/GRaQ/yUe1/8nHuX/Jh/f/yQ36f8jSfH/I0nw/x5D7/89YPr/wMXQ/wgHBf8AAAD/AAAA/wAA + AP8UJIP/Hif0wyAW3yMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANfW/JPY2Pz/2tr9/87Q + +/+0t/f/RkPl/yAY3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/JR3f/yQc4P8dE97/OTjo/4eS + 3/+bnKb/u7zK/7a3x/+3uMj/tLXE/4qKkf+Mj5X/lpih/5KLk/+HeH3/gW1w/3xmaP98ZGb/eF1e/3ph + Y/98ZWf/fmls/4Z1ef+QiZD/oaCr/6+ywP+5vMX/vL3A/6Cjuf9hY7HyLirO3CQd4v8mH+D/Jh/f/yYf + 4P8mH+D/Jh7f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh7g/yYd3/8mHN//Jhzf/yYd + 3/8lH+D/IyTi/yAq5f8eMun/HDrs/x5C7/8kSvD/MFXx/0Ni8v9Zc/T/c4b1/4WU9v/Gzfv/ys/7/8jO + +/+qtfr/cYb5/1l1+/8+X/f/J0jo/xgyy/8XIqL/FhV+/xQPc/8TEXH/FxWF/yMdyv8nH+f/Jh3g/yUr + 5P8kPuz/Fzzv/4Ca//+Eg3r/AAAA/wAAAP8AAAD/AAAA/0JCdNFdVeMIAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAArrT2mZ6g8/9sbev/QD7k/yQe4P8iG9//Jh/g/yYe4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/JBvf/y4r4f+EkPP/mab+/4eNw/+ioqn/u7zK/7i5yf+4ucj/t7jH/5qb + pf+goaz/vL/Q/7u/0P+6vs7/ub7O/7q/z/+6v9D/vMHS/73C0/+7v9D/trrJ/6yvvf+foKvtj4+WzYCA + hKFzc3Vzbm1pRnNzXR9GR6kJIhrnQCYf4KEmHuDtJh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mHuD/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHd//Jhzf/yYc3/8mH+D/JSPi/yQr + 5P8hMuj/Hjrs/xw/7v8eRfD/G0Lv/5it+f+QpPj/q7j5/6Kt+P+cpfj/o6v4/5ql+P+ImPj/Z4D7/zFV + 9/8dPeH/HS20/xcZh/8TD3D/FBF1/x4Zrf8mH9//Jx3l/yYe4P8eIeT/tb30/zc2Lf8AAAD/AAAA/wAA + AP8AAAD/enp01NbWzgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkIN+fFQ/d/xEJ + 3f8ZEt7/IRvf/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8fF979aG/ulZ2q + +YyUoPielKD8t4ePyMySk6Dnr7C8/7q7y/+7vMz/vr/Q/72+z/+6u8v/u7zM/7u8zP+4ucn/sbLB/6eo + s/+YmaPnioqQxHx9f5hxcXJnamppPGZmZRkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYe + 3xwmHuBsJh/fwiYe4P0mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mHeD/Jh3f/yYc3/8mHuD/JiPh/x8l5P87T+z/tMX7/ztc + 8f+Qqvn/M1fx/0Fg8v9pf/T/jJr2/56o+P+jq/j/kZ33/z1d9P8gR/f/I0Xr/x4zxP8YHpL/FBFw/xcS + g/8gGrr/IBfd/Tk26/6/wc//BwcE/wAAAP8AAAD/AAAA/wAAAP+Pj5D1zc3NHAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAADw646ljY+r/hYfv/6Ol9P+UmfL/KSTh/yQd4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yAX3p0AAAAAAAAAAAAAAAAAAAAAAAAAAHZ5mRVubWhlhYaKt5aX + n+Cgoav2oaKt95ycpvCTlJzaiIiOwHx8f5ZxcXJmaWlpNWZmZRMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh/gMyYf4IcmH+DZJh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh7g/yYe4P8kGt//HBPd/6Sn8/9rbev/X2Tr/4uX8/8ZL+n/Hj7t/xxD7/8qUPH/TGnz/3OH + 9f+Xovf/dIj1/x5D7/8jR/H/JEr2/yRI8v8gOtP/GiWf/xQWfqQeGsQjm5n8dLKzrP8AAAD/AAAA/wAA + AP8AAAD/CgoK77CwsPTJyck/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAxsn5uNna + /f/a2v3/3t79/9TV/P84NeP/Ihrf/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+DVIhvfEAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ2dlDWhoZyNpaWclZmdmGWZmZQkAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACYf4AkmH+BJJh/goiYf4OkmHuD/Jh/g/yYf4P8lHuD/JR7g/yUd + 4P8lHeD/JR7g/yUe4P8lHeD/JR3g/yUd4P8kHd//JBzg/yQc4P8iGt//HBTe/yMc3/98gO7/i4/w/0ND + 5P+xuPb/Pzvj/yEX3v8mIeH/JSrk/yMz6P8dO+z/H0Tv/zBU8f8yVfH/Ikbv/yNH7/8jR+//I0fw/yRJ + 9f8kSvX/IUPp1B9B627S3P+LkpGJ/wAAAP8AAAD/AAAA/wAAAP8rKyuHx8fIxcXExWoAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADNz/vH29v9/9bX/P/Cxfn/mJvy/zAt4f8jHN//Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g8iYe3zAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJh/gFyYf4GEmH+C4Jh/g9yMe3/8qJeD/KSTg/ygj4P8pJOD/KCPg/yol4f8tKOH/LCfg/y0n + 4f8wKuH/Mizi/zs44/9aWuj/jJPw/5CX8P9vdOv/l53y/09O5v8eF9//Jh/g/yYe4P8mHN//Jhzf/yYf + 4P8lJ+P/IjHn/yE97P8jSO//I0nw/yNI8P8jR+//I0fv/yNH7/8jR/H/Fj3w/6e6//9qZ17/AAAA/wAA + AP8AAAD/AAAA7TY0LRXOzs2rxMTElgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKKn + 9MuGh+//U1Ln/y0n4f8aE97/JBzg/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P4mH+BVAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnI+EnhY/veW92 + 68tocer/dHzs/3Z/7f97g+3/hY3v/3+I7v98iO7/g5Hv/4OQ7/+Hju//i5Xw/5Wg8v+PmPH/e4Hu/11e + 6P8oIuD/Hxff/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYd3/8mHN//Jh/g/yUo4/8lNej/I0Ht/yNJ + 8P8jSfD/I0fw/yNG7/8YPu//rsD//1pYUP8AAAD/AAAA/wAAAP8DCB6DAAAAAMzMy4LExMTAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHRffyBUO3f8jHt//Ozjj/0E/5f8mIOD/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/gdwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQz/kAZ+i8ziAge6NVVXn2lxc6f9cW+j/Wlno/1JU + 5/9TV+f/UFXn/0xP5v9LSOX/QD7k/zMt4v8iG9//Hhbf/yUd4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh3f/yYc3/8mIOD/JSrk/yQ56v8jRe7/I0nw/xc/8P+yxf//oZ+W/wAA + AP8AAAD/CAwf/yFD39QfRf1rhZPVbtDNv9kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABaWuimoKL0/7q8+P/P0Pv/u7/4/y4p4f8jHN//Jh7g/yYf4P8mH+D/Jh/g/yYf4JIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAVDd0JHxjfSB8X350fF9/mIBjf/yAX3/8gFt//IBff/x8X3/8hGd//JBzf/yYe + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mHN//Jh3f/yYj4f8lMOb/Fzbr/6i5/v/v7ub/EBAP/wAAAP8cNKL/JUv//x9D8P8mSu30o63Y8Ieb + 5S4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKCv9Fne3f3/19f8/9zc/f/S1fv/Ojjj/yEZ + 3/8mHuD/Jh/g/yYf4P8mH+CoJh/gAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYf + 4BQmHuBYJh7gryYf4PAmH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh3g/yYc3/8ZE97/oKXz//// + //9OTUT/AxJE/yVM+/8jR/D/I0fv/xI47/+Ln/n/m7D67XON9owgRO8gAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAZnDqDLS3997GyPn/nZ7z/2Jh6f8oIuD/JR3g/yYf4P8mH+D/Jh/gsiYf4AcAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh7gHyYf4GgmH+C6Jh7f9iYe + 3/8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/xkS3v+jpvL//////5ubpf8WIsL/JEP1/yNJ8P8jSfD/FTvv/4yf + 9/+Zrvn/dI72/x5C7+0jR/CHI0fwGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMi7hcDMw4v8fGN//HBTe/yUe + 4P8mH+D/Jh7g/yYe4KwmHuAJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmHuAlJh/gciYe4MImH+D4Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mH+D/GhLe/3Z3 + 7P//////v8P6/xwU4f8lIN//JS7l/yQ/7P8ROO7/prj5/5Sr+P9qh/X/HEDv/yNH7/8jR/DlI0fvcCNH + 7wgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAjG98FIhrfgyYe4OEmH+D6Jh/g+yYf4OomH+CGJh/gAwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACYe4CkmH+BrJh7fryYe4OgmH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8jHN//LCfh/7q99f9oaOr/HRXe/yYe3/8lHN//Fg7d/2Jt + 7P/v9v//eZL2/2uI9v8aP+//I0bv/yNH7/8jR+//I0fwyiNH7z8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh/gECYf + 4CkmH+AqJh/gGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh/gFiYf + 4FImHuCfJh/g4iYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8jHOD/IBnf/yIa3/8lHd//HRTe/xYP3v9mZ+n/9fX9/46T8P+irPT/XHHw/xs/7v8jSfH/I0fw/yNH + 8P8jR+//I0bv+yNH74MjR+8FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmHuAQJh/gSiYf4JgmH+DdJh/g/yYf + 4P8kG+D/Hhbf/x8X3/8eF9//Hhff/x8X3/8fF9//Hhbf/x0U3/8aEt7/HBTe/ykk4P9VVef/pKjy/9LX + +f+PlfD/o6n0/56i8v8mIN//JCPh/yQ36f8jRu//I0rw/yNH8P8jR+//I0fv/yNH778jR+8rAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACYf4AsmH+BEJBzgkzEt4tpQS+b/T0rm/1VQ5/9WUef/VE/n/1NP + 5v9VVef/YmXp/3d57P+ZofH/wsv3/9vl+//Bzfj/oKjz/6Gn8/9/g+7/KCTg/yIa3/8mHd//Jhzf/yYk + 4v8lNuj/I0Xv/yNJ8P8jR+//I0fv/yNH7+4jR/BlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAb3nrCtff+kGbqPKOtbr22KKo8/+fpvL/n6by/56m8v+ep/L/kpvx/4mU7/9/he7/am3r/2Bi + 6f9NS+b/KiXg/xwU3v8kHOD/Jh/g/yYf4P8mHuD/Jh3f/yYc3/8mJOL/JTXo/yNF7v8jSvD/I0jw/yNH + 7/8jR++oI0bvGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSUOcHGBDePCEa + 34khGt/VIRrf/yEa3/8iG9//IBnf/x4W3/8dFN//HRXf/yAX3/8kHeD/Jh7g/yYe4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYd4P8mHN//JiPh/yU16P8jRe7/I0nw/yNI8P8jR+/gI0fvTQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUe4AUlHuA7JR7ghyYe4NEmHuD/Jh7f/yYe + 3/8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHd//Jhzf/yYk + 4v8kN+n/I0bv/yNJ8P8jR+/+I0bvagAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAJh7gAyYe4DYmH+CEJh/gzSYf4P4mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh3f/yYc3/8mJeL/JDvq/yNI8P8jR/DIAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAmH+ABJh/gMiYf4H0mHt/IJh/g/CYe3/8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYd3/8mHN//JTLn/yNI8IgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmHt8tJh/gdCYf + 4L8mH+DzJh/g/yYf4P8mHuD/Jh7g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYd3/8lJ+ObI0nwDAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYe4CEmHuBWJh7gkiYf4MYmHuDsJh/g/yYf + 4P8mH+D/Jh/g5CYf4LwmH+COJhzfRwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAmHuAPJh/gNSUe32cmH+CVJh/gnyYf4JYmH+BaJh/gLCYe4AwAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////+Af///////////////////AD////// + /////////////AAf//////////////////gAD//////////////////wAAf/////////////////4AAH + /////////////////8AAB/////////////////8AAAf////////////////wAAAD//////////////// + gAAAA////////////////AAAAAH///////////////AAAAAA///////////////AAAAAAH////////// + ////wAAAAAB//////////////8AAAAAAf//////////////gAAAAAH//////////////8AAAAAB///// + //////////AAAAAAf//////////////4AAAAAH//////////////+AAAAAB//4B///////////wAAAAA + //4AH//////////8AAAAAP/8AA///////////gAAAAD/+AAH//////////4AAAAB//AAA////////+D/ + AAAAAf/gAAP///////8AAIAAAAP/wAAA///////8AAAAAAAH/8AAAH///////AAAAAAAB/+AAAA///// + //wAAAAAAA//gAAAJ///////AAAAAAAf+AAAACf/////58AAAAAAP8AAAAAH/////4H4AAAAAAfAAAAA + B/////8A/gAAAAAA4AAAAAf////+AH+AAAAAADgAAAAH/////AB/4AAAAAAAAAAAB/////gAP/wAAAAA + AAAAAAf////4AD/wAAAAAAAAAAAH////8AA/AAAAAAAAAAAAB////+AAIAAAAAAAAAAAAAf////gAAAA + AAAAAAAAAAAH////4AAAAAAAAAAAAAAAD////8AAAAAAAAAAAAAAAA/////AAAAAAAAAAAAAAAAP//// + wAAAAAAAAAAAAAAAH////8AAAAAAAAAAAAAAAB/////AAAAAAAAAAAAAAAAf////wAAAAAAAAAAAAAAA + H////4AAAAAAAAAAAAAAAA////8AAAAAAAAAAAAAAAAP///+AAAAAAAAAAAAAAAAD///+AAAAAAAAAAA + AAAAAAf///AAAAAAAAAAAAAAAAAH///gAAAAAAAAAAAAAAAAB///wAAAAAAAAAAAAAAAAAf/+AAAAAAA + AAAAAAAAAAAH/4AAAAAAAAAAAAAAAAAAB/+AAAAAAAAAAAAAAAAAAAf/gAAAAAAAAAAAAAAAAAAD/8AA + AAAAAAAAAAAAAAAAAP/8AAAAAAAAAAAAAAAAAAB//4AAAAAAAAAAAAAAAAAAf/8AAAAAAAAAAAAAAAAA + AH/+AAAAAAAAAAAAAAAAABD//gAAAAAAAAAAAAAAAAAf//4AAAAAAAAAAAAAAAAAP//+AAAAAAAAAAAA + AAAAAD///gAAAAAAAAAAAAAAAAB///4AAAAAAAAAAAAAAAAA///+AAAAAAAAAAAAAAAAAf///gAAAAAA + AAAAAAAAAAH///4AAAAAA/wAAAAAAAAB///+AAHwAH//gAAAAAAAAf///gAB/g///+AAAAAAAAH///4A + A//////8AAAAAAAB///+AAf//////4AAAAAACf///gAP///////gAAAAAAH///4AH////////AAAAAAA + ///+AB////////+AAAAAAD///gA/////////8AAAAAAP//8Af/////////4AAAAAA///AP////////// + wAAAAAH//8P///////////gAAAAAf///////////////AAAAAD///////////////+AAAAAf//////// + ///////8AAAAB////////////////4AAAAP////////////////wAAAB/////////////////gAAAf// + ///////////////AAAH//////////////////AAB//////////////////+AB/////////////////// + 4A////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////8oAAAAYAAAAMAA + AAABACAAAAAAAICUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcG + KAgGBSYWBgUkFwUEHxIEBBsEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAANDE8BBQUiVgAAAr8AAADrAAAA7gAAAOIAAAWyBgYYUhISKwQAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwbXxcDAwqwBwck/g8OTv8REFf/DQxI/ggI + Kf8CAgf/AAAA/gICArQiIiwSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBtGQwM + JcUaF5L/Ix3g/iQd6P8oIuP/NTHX/j08zv8xL7L/EBFG/gAAAv4LCw+ZAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABxcowJEREwvx8awv4qJdv+UE7G/ouLyf7Bwdb+5+fn/u/v7f7m5uf+srTM/jk6 + Q/4BAQH5IShNIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIuOoRAwMDSoaGqs/qmqy//i4uH//f37/v7+ + /v/19vr/3eL0/sPM8P+0wvn/rbr5/rC+7/9cYGz+DBAjdwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAur7QAq2xxRacn6xMoaKlm8bG + x9zp6en9+vr6/u/y+//J0/j/k6b0/mB77/88XOz/KUvt/iNG7v8iRu//IUXv/iJG7/8uUOn+KjqEmwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ6i + tBSSlJ5ar7G4qs/Pz+Xw8PD+/v7+/vDx8v67w+X+cIfo/jpa7f4iRu7+IUTv/iJG7/4iRu/+Ikbv/iJG + 7/4iRu/+Ikbv/iJG7/4iRu/+JEjv7CY/1DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAqa/MAZqftx+Ym6hhqamquNjY2PX6+vr/+vr7/trf8f+ir+f/XnXf/itM5v8gRO//Ikbv/iNG + 7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNF7v8kMuf+Izvq/yNG7dIkOdsUAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAJqiyAKSmLNHq6yxs9fX1+z19fX+8fT7/svT9f+Lnuz/S2fp/ipM + 6/8iRe7/Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//I0bv/iNG7/8jRu//Ikfv/iQ2 + 6P8lHt/+JR/g/yQ46f4jQuycJDXlAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIaMqyza2tv39/j8/r3J + 9/53jvH+PV3u/iNH7v4hRe/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG + 7/4iRu/+Ikbv/iJG7/4iRu7+I0bv/iUm4v4lHt/+JR7f/iUf4P4jPev8Iz3rSwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAIKOxweanamoP2Dy/h9D7/8iRu//Ikbv/iNG7/8jR+//Ikbv/iNG7/8jRu//Ikbv/iNG + 7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jR+//I0Pt/iUg4P8lHt/+JR7f/yYe + 3/8lK+T+I0LthQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABrf9gIJUjvzyNG7/8jRu//Ikbv/iNG + 7/8jR+//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG + 7/8jRu//Iz/s/iUf4P8lHt/+Jh7g/yYe3/8lKuT+I0HtcgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAMlPrWSJG7/wiRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG + 7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+JDrq/iUe3/4lHt/+JR7f/iUe3/4kMeb+IzfpRQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ0nrCCNG78cjRu//Ikbv/iNG7/8jRu//Ikbv/iNG + 7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//I0bv/iNG7/8jRu//JC7l/iUe + 3/8lHt/+JR7f/yYe3/8kNOf4JDHmFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAGBiQIBQUeFQQEGxIFBBwFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG + 708jRu/7Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG + 7/8jRu//I0bv/iNG7/8jRO7/JSHg/iUe3/8lHt/+JSPh/yUf4P8lK+TGAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAODFQCCQg4PQIBDZ4AAALXAAAA6QAAAOgAAALRAQELgQUE + IRkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACJF7gciRu+8Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG + 7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+I0bv/iNG7/4kN+n+JR/g/iUe3/4lHt/+JTHm/iUl + 4v4lH+CNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8MXQkEBByOAgIH+AoL + NP4REVj+ExNe/g0NQv4DBA7+AAAA/gAAAdgFBR88AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjQu0zI0bu+SNF + 7v8jRu//Ikbv/iNG7/8jRu//I0bv/iNG7/8jR+//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNF + 7/8lJuL/JSzl/iYe3/8lIuH+JTTo/yUk4fIlIuAxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAEA1jEgQEE7YVE3D+Ix3W/yUd6/8lHen+JRzp/yUd6v8kHt7+FBNu/wECCP8AAAD2BQciUAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAyO+YBMz3nCzI85wYAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAI0DshiUo4/wjPOv/Ikbv/iNG7/8jRu//I0bv/iNG7/8jRu//Ikbv/iNG + 7/8jR+//Ikbv/iNG7/8jRu//Ikbv/iM56f8lKeT/JSzl/iYg4P8lMOb+JS3l/yUf4JglJOEBAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVE4EGBwgksB4atf4lHej+JR3n/yUd5/8lHef+JB3j/yQf + 1v8jIMX+Ih60/xkZbv8NEUT+CA4utRcklwQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJzLmGSY6 + 6WomMOavIyvk1SYv5csmLuWwJzDlkCY153ElMOZMJDLnLCM36Q4kMeYBKDXnBicl4ZYlH+D/JDTo/h83 + zP8iQuX/I0fx/iNG7/8jRu//Ikbv/iNG7/8jR+//Ikbv/iNG7/8jRu//I0Lt/iUu5f8lNOj/JSPh/iUi + 4f8lLeX+Jh/g4SUj4RsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABYUgAELCjZ/IRzG/yQd + 6P8kHOj+JB7i/yQgyv8kIcL+JCDE/yQfzP8lHtj+Jh7g/yUe4f8lJOT+I0Xr8SFC5ZoeNssXAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACQt5BIkNuiNJCjj9iUe3/48POT+ZGzs/mdu7f5gZ+z+JyDf/iUd3/4lHt/+JR/f/iUk + 4folKuTiJDLmwiQ06KIkKeTtHiK9/hwgp/4WF4P+GSKc/h83y/4iRev+I0fw/iNH8P4iR+/+Ikbv/iNH + 8P4jQOz+JSfi/iU06P4lKuT+JR7f/iUe3/4lHt/6JSDgSgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAABEQYjseGrL2JB3e/iEevf4jIb3+JCHI/iUf3f4lHuH+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JDnp/iJG7/4iRu/eKkrmIwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB0luRklH9u6JR7f+CUe3/8nIOD+JyDg/yUd + 3/8mH+D+Jh7f/yYe3/8mHuD/JR7f/iYe4P8lHt//JR7f/icg4P9cY+v/aHHv/lBY6f85QeD/KDDP/h8n + u/8cKLP/HCuy/hsnqf8cLbX/HTPD/h0ouP8fHLv/JCHU/iUm4v8mHuD/JR7f/iYe3/wlIN+BAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBejBhMTWr8eHZ7+Hhyf/yQezP8lHt/+Jh7g/yYe + 3/8lHt/+Jh7f/yYe3/8lHt/+Jh7f/yYe3/8lHt/+JC7l/yNH7/8iRu/+I0fvwDhW5AsAAAAAZXnUAQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAZGp8EIh7IRCUe3q8lHt/5Jh7f/yYe4P8lHt/+JR7f/yYe4P8mHt//JR7f/iYe4P8mHuD/JR7f/iol + 4P+JlfT/laH3/pWh9/+UoPf/i5b0/nqD8f9ja+7/R1Hp/i033f8fKsv/HCi3/hkkoP8WGYf/FRN9/hoW + m/8iHMf/JR7f/iUg35skJd4GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJB7bASQd2RwkHdhYIx3NqCId + w/smHt/+JR7g/yUe3/8lHt/+Jh7f/yYe4P8lHt/+Jh7f/yUe3/8lHt/+Jh/g/yUe3/8lHt/+JSnj/yNG + 7/8iRu/+I0fv/SpL5VmJmN8rk53HOwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAgIMwkFBiItCQo7Jw4OWQMAAAAAAAAAAAAAAAAlINImJR7eliUe3+klHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4sKOH+SUrn/l9l6/5weO/+gIry/oyY9f6UoPf+lqL3/pSh + 9/6HkvP+bnXu/lhg7f5CUOz+KDng/h8vy/4cKrD+Hie5wyQt4B8jJ98BAAAAAAAAAAAAAAAAAAAAACQn + 3gslHt+SJR7f1iUe3/slHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+JCrk/iJG7/4iRu/+Ikbv/lJu8biwsrzOOkFhPQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQk5OwgIKsoEBRf1AAAA8gEBC64OC1QcAAAAAAAA + AAAAAAAAIxzSASUe3h4lHt95JR7f3CUe3/wlHt//JR7f/iUe3/8lHuD/JR7f/iUe3/8lHt//JR7f/iUd + 3/8lHt//JyDg/i0o4f89POT/W2Dr/nmD8f+Ll/X/kp32/pOf9v+Un/f/h5L0/kdI5v8lIeH/JSnk+iQz + 5+kkOum5Iz3rdiQ36TskMOYRAAAAACQr4QUlId+FJB7a9iQe2P8mHt/+Jh7g/yYe3/8lHt/+JR7f/yUe + 3/8lHt/+Jh7f/yUe3/8lHt/+Jh7g/yUe3/8lHt/+JR7f/yUe3/8lHt/+JDLn/yNG7/8iRu/+MFPw/663 + 2/IeHx/4LTNMLgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANDEg2DQ5K7RUT + fP8VFHv+Bwgg/wAAAP8BAQjODApODQAAAAAAAAAAAAAAAAAAAAAAAAAAJR7fECUf4F8lHt/JJR7f/iYe + 3/8mHuD/JR7f/iYe4P8mHt//JR7f/iYe4P8mHt//JR7f/iUe4P8mIOD/UVTo/l1h6/9sde7/d4Lw/m96 + 7/9SWOn/REPm/jc04/8lHt//JR7f/iYe3/8lHt/+JSHg/yUo4/wkMufgJDboaSQv4wMAAAAAICTFPRwY + o7obF6D+Hxq6/yEbxP8iHMn+Ix3Q/yQd1v8lHtv+Jh/h/yYe4P8lHt/+Jh7g/yYe4P8lHt/+JR7f/yUe + 3/8lIuH+I0Tu/yNG7/8iRu/+ssH3/lhYWfMAAAD+Jys9IgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAABYVah0PD1PdFROA/h8Zv/4kHen+JB7i/hMSZP4AAAL+AgIPigAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAkKOMBJSXiQCUg4KslHt/yJR7f/iYe3/4lHd/+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4kHd/+S03n/oKM8v6UoPf+lJ/2/pSg9/6Voff+V1zq/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/CM66ssiRu+3IkfwpCJA5pkcLLTsGyms/hkmpv4ZJJ/+GCCY/hgckP4XGIj+FhSE/h8a + uf4mHuD+Jh7h/iYe4f4lHt/+JR7f/iUh4P4jP+z+Ikbv/iJG7/6Oo/f+tbW2/gUFBf4CAgL6LjA5EwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBxzBhcYUK4mJ5P+IRvP/yUd5/8kHef+JR3n/yQd + 5P8PDk/+AAAA1A4RWgsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIzPnAiQy5xIkLeVJJSnj0jk4 + 3f+lpcn/UVPY/iUe4P8mHuD/JR7f/iQd3/9wcdP/m53K/jAt4P9udu//laD3/pSg9/+UoPf/kp32/k9R + 6P8nIeD/JR7f/iUe3/8lHt/+JR7f/yUe3/8lHt/+JSXi/yNB7P8iRu/+I0bv/yNG7/8jR/D+I0fw/yNH + 8P8jR+/+Ikbv/yNG7v8iROr+HCy0/xYUgf8ZFZH+GhaX/xwXpP8gGr3+JSLf/yQ97P8jRu/+I0bv/z1d + 8f/x9P3+Q0NE/wAAAP8gICHxTk9SBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADxA+YUlM + p/siHM7+JR3o/yUd6P8kHef+JR3n/yUd5/8gG77+AAAA5hMbfRMAAAAAAAAAAAAAAAAAAAAAI0PtFSND + 7UgjQ+6BI0XutCNG79wjR+/3Ikbv/ixL6/+3usf/ZmbU/iUd4P8mHuD/JR7f/iQe3/9lZdT/wsPE/jw4 + 3P8oIuD/WFzq/mVr7P9iaOz/eoPx/pKe9v+Ai/L/c3rv/kVG5v8lHt/+Jh7f/yYe4P8lKuP+I0Pu/yNG + 7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNF7f8dMLz+FxuN/xkinv8cLbb+HC64/xoo + qv8XGon+GBqQ/xsoq/8fOM/+Ikbu/3KL9f/U1NT+AgIC/wAAAP9YWFjuMzM0AgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAbGmILMzZu5DQxwf4kHOL+GBWQ/hoWnP4kHeX+JB3n/iQd5/4lHuf+BAUT5hgp + oxgcNb8lIEHfXSJG7pgiRu/SIkbv+yJG7/4iRu/+I0bv/iJG7/4iRu/+Ikbv/iRH7v6fqM3+sLXJ/ixB + 5/4lKeP+JR7f/iUe3/47ONv+yMjD/lta1v4lHt/+JR7f/iUe3/4lHt/+Ixze/kRF5v6Rnfb+lKD2/l5j + 6/4lHd/+JR/f/iQz5/4jRu/+I0bv/iNG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+IUDi/hke + lv4cIKz+IkPn/iNH8P4iRu/+Ikbv/iNH7/4jR/D+IT7c/hwstP4XHJD+ExaG/qSt4P6AgYH+AAAA/gAA + AP6Ojo7kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPEEBaP0Gd/yEbyf8cGK7+GRl9/xYU + hP8kHeL+JB3n/yQd5/8lHuv+FyZ+8iJE59EiRu/xI0bv/iNG7/8iRu/+I0bv/yNG7/8jR+//I0bv/iNG + 7/8jRu//Ikbv/iJG7/+FlNT/sLfJ/mR73f8iRe//Iz7s/iUt5f8lIeD/t7nG/oCBz/8kHOD/JR7f/iUe + 3/8mHt//JR7f/iUe3/9FReb/Rkfm/ici4P8lJuL+Iz3r/yNG7/8iRu/+I0bv/yNG7/8iRu7+I0bv/yNG + 7/8iRu/+I0bv/yNH7/8iQub+GBuR/yEbxf8mH+D+JDnp/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNH + 8P8iRu3+GzPJ/5CVwf8tLjr+AAAA/wUFBf/CwsPRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABoa + XQIKCzutNDWY/yAazP8XFIv+FRR6/xsXpP8kHej+JB3n/yQk6P8jPe3+I0fx/yNG7/8iRu/+I0bv/yNG + 7/8iRu/+I0bv/yNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iJG7/9mfN3/jp3S/qevy/8nS+7/Ikbv/iJH + 7/8gP+3/k53P/qKlyv8kJ+P/JSTh/iUi4f8lI+H/JSTh/iUp4/8kL+b/JDfp/iM/7P8jRu/+I0bv/yNG + 7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yJG7v8ZIpz+IBu//yYe4P8lHt/+JSbi/yNG + 7/8iRu/+I0bv/yNG7/8iRu/+I0fv/yNB7f8kOOn+JDTr/xgeWP8AAAD+AAAA/xYWF//p6eqoAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAABQVUxALC0LgJSeK/h8ZxP4dGLL+GRWW/iMc3f4kIef+IzXr/iNG + 7/4jRu/+I0bv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbu/iJG7/4iRe/+I0bv/iJG7/4pTe/+JUjv/ihL + 7/5cd+f+mKPP/oua0/5iet7+IkXv/muG9P46WvH+aH7c/rq8xv4vUuz+Ikfv/iJH7/4iR+/+Ikfv/iJH + 7/4iRu/+Ikbv/iJG7/4iRu7+Ikbv/iJG7/4iRu/+I0bv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/h41 + yP4bF5/+Jh7h/iUe3/4lHt/+JDPn/iJG7/4iRu/+Ikbv/iJG7/4jRe7+JCzk/iUd3/4lHt/+Jh7h/hMS + aP4AAAD+AAAA/jk5Ofufn6ZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsNNygNDUv3FhV7/xwX + rP8kHub+JCXZ/yQ11f8jQ+v+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/y5R + 8P9Sc/P/JEjv/iFF7/9wi/X/JUjv/per+P/k6fn/srfJ/khl5f+rssr/I0fv/nKO9f8rTvD/RWPl/sDC + xP9IZeX/KEvw/iNH7/8iRu//IUXv/iJG7/8jRu//Ikbv/iNG7/8mSe/+Ikbv/yNG7/8iRu/+Ikbv/yNH + 7/8iRu/+Ikbv/y5R8P8mSu/+I0bu/xkfmf8jHdH+Jh7f/yUe3/8lJ+P+I0bv/yNG7/8iRu/+I0bv/yNF + 7/8kLOT+JR7f/yUe3/8lHuD+JR7g/yActv8BAQT+AAAA/39/gcNTUm8IAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAsRQTsMDEP+FBJ6/x8psP8jPt7+I0br/yNG8P8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG + 7/8iRu/+I0fv/yNG7/8iRu/+I0bv/yJG7/8tUPD/T3Dz/iJF7/9lg/T/Ikbv/mmE9P82WPH/srfJ/jxb + 6P+UoND/sr3l/qS4+f8oTPD/K07s/re7x/9pftz/Wnn0/jda8f9Ia/P/c4/1/lx58/8hRe//Jkrv/jdY + 8P90j/X+JEjv/y5T8P8hRe/+Z4P0/4ad9/8kSO/+NVnx/0hn8v8xVPD+IT7c/xsZof8mH+H+Jh7g/yUf + 4P8jPOv+I0bv/yNG7/8iRu/+I0bv/yQu5f8lHt/+JR7f/yUe3f8bF6r+Kiig/yYmi/8AAAX+AQEG/0NB + pZkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0YV0kQGVj+Gyuy/iJG7/4iRu7+Ikbu/iJG + 7/4iRu/+Ikbv/iJG7/4iRu/+I0bv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iR+/+Nlnx/l18 + 9P5Zd/P+JEjv/mB99P4dQu/+kp/Q/lt03/5EYub+p6/L/mWD8/48XfH+IUXv/pmkz/6PnNH+Smrz/kxt + 8v4iRu/+HkLv/kdn8v5rhvT+ZoP0/rDA+f5BYvH+JUnv/khr8v4hRe/+N1vx/jpc8f47XPH+Y4H0/myI + 9f4pTfD+HTG//iEbw/4lHt/+JR7f/iUl4v4jRu7+Ikbv/iJG7/4jQ+3+JSnj/iUe3/4lHt/+Jh7g/hoW + nv5gYqL+6enp/ufn6P6Qk6z+Hx+H/iYf4O5GRrAhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHTnGHSBB + 264iRer+I0bv/yNG7/8iRu7+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG + 7/8jRO7+Izzr/yQ06P8kLeX/JSrj/qWu8/9gaOv/IiXi/p2n8/+NnvT/l6La/oKN0/8iOOv/v8PP/mZ8 + 4f9KaPH/IkTu/nKF2f+zuMj/NFfx/khn8v8iRu//Ikbv/iBE7/9FZvL/epf2/kVl8v9gffT+Ikbv/4Kc + 9v8wUvD+T3Dz/5Op+P8xVPD+PF/x/32V9v8hRe/+HCmv/yQd1f8lHt/+JR7f/yQv5v8iR+/+I0bv/yQ6 + 6v8lI+H+JR7f/yYe3/8lHt/+IhzM/zY3j//f3+T+6enp/+np6f/q6ur+r7HQ/ys41v49RNJhAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAhQ+M/Ikbv3yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu7+I0bv/yNG + 7/8iRu/+I0bv/yJG7/8iRfD+Izrr/yUr5f8lIeH+Jh7f/yYe3/8lHt//JB3f/jo+4/+QlO//Lijh/l9e + 6P+5vPX/xMnm/qOkyf8jHN//eXvd/qyvy/9YXuf/JSLh/kxO2v/FxsP/PUXi/kRL6P9baO3/f5Py/pOm + 9v+UofP/UFrq/mdx7P8zQej+IzHn/2Jw7f+7w/f+Ljvn/z1G6P8kL+b+JjXn/yQ36f8jQO7+GyOn/yUe + 3P8lHt/+JR3f/yQ16P8jRu/+JDDm/yUf4P8lHt/+Jh7f/yYe4P8lHuD+Gxeq/6Wnx//q6ur+6unp/+rq + 6v/p6en+6+rq/42Z3f8oSOqhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIkXuAiJG72kiRu/2Ikbv/iJG7/4iRu/+Ikbv/iJG + 7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+KErk/lBlwv5scbX+V1a3/iYf3v4lHt/+JR7f/iUe + 3/4lHd/+MC/h/jIy4v4uKeD+Q0Lk/iUe3/5oZ+n+iInm/rW2xv4xLN7+JR/f/p+hyv51edr+JR3f/jEs + 3f67vMX+XFzY/jk24/4xK+H+goTt/mlr6f4iGt/+JR7f/iUe3/4lHt/+JR7f/iUd3/4hGt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4mHuH+Gxmg/iUe3v4lHt/+JR7f/iUl4v4lJOH+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4mHuH+GhiD/l9gZf7Ly8v+sLCw/re3t/7Y2Nj+6urq/tfb6v4rTu/tXG/eCQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApRecCIkbufSNG + 7/gjRu//Ikbv/iNG7/8jRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yND7f84S87+kZWw/7O0 + wf+1tsX+f4Cw/yYf3v8lHt/+Jh7g/yYe4P8lHuD/Oznj/lZY5/9cYOj/Kybg/iUe3/8lHd//KiTe/rKz + x/9KSNn/JR7f/lZU1/+nqcn/JR7f/icg3/+eoMr/f4HR/jQz4v8mHuD/JB3f/lJS5v9LSuX/JR7f/iYe + 4P8lHt/+Jh7f/yYe3/8lHt/+JR7f/yUe3/8lHt/+Jh7f/yYe3/8mHuL+Gxih/yUe3v8lHt/+JR7f/yUe + 3/8lIOD+JiTh/yUe4P8lHt/+Jh7f/yYe3/8mHuH+EA9X/wEBAf8MDAz+AgIC/wUFBf8TExP+WVlZ/93f + 4/8/Xu7+VGvdLwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAsSutkI0bv/CNG7/8jRu//Ikbv/iNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG + 7/8jRO7+IzLo/0RGvv+io7X+trjH/6Okr/+foKn+SUe5/yUe4P8lHt/+Jh7g/yYe4P8lHt//JR3f/jEs + 4f8vKuH/JR7f/iYe3/8mHt//JB3g/p6fy/9qadT/JR7f/ikj3/+oqcn/UE7Y/iQd4P98fND/naDL/l5j + 6P8kHN//JR7f/iYf4P9gZej/JR3f/iYe4P8lHt/+Jh7f/yUe3/8lHt/+Jh7g/yYe3/8lHt/+JR7f/yUe + 3/8mHuH+Gxed/yUe3f8lHt/+JR7f/yUl4v9Xa/D+R1Pr/yUe3/8lHt/+JR3f/yYe4P8mH+H+Dw9X/wAA + AP8AAAD+BwcH/1lZWv9FRUX+Ly8v/+Tk5f9OaOz+T2bUSwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAFNR0QJUUtERW1vVKkhJz0ZGR9BbPT7OVDY91EEiQeniIUTt/iJG7/4iR/D+Ikbv/iJG + 7/4iRu/+Ikbu/iJG7/4iR+/+I0Lt/iQy5v4kIOD+S0m5/qqruf62t8b+tbbG/ra3xv6ytL3+NC/I/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JBzf/n5+0P6Qksz+Ixzf/iQe + 3/5bW9b+oaLJ/igh3/5SUNf+uLnF/pme7f54eOz+WVjn/oeL7f5PTuX+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4mHuH+Gxec/iUe2/4lHt/+JR7f/jk75f6UoPb+SlHp/iUe + 3/4kHN/+TFLn/iUe3/4mHuH+EhBn/gAAAP4NDQ3+x8fH/uvs7P7r6+v+hISE/pWVlv5GYd3+RlzNTgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQj/KIykkwcEjHb/lX2Tc9H2I6/t7g+r+aHDh/kdI + 0Ph2gen+aHTm/1lo5P9LXeT/OlHk/ixH5v8kROr+I0Tu/yM86/8kLeX+JSDg/yUe3/9HRbz+qqu6/7a3 + xv+2t8b+trfG/7a3xv+3uMf+UVC1/yUd4f8lHuD+Jh7f/yUe4P8lHuD/JR7g/iUe3/8mHt//JR7f/iUe + 3/8lHt//JR7f/l1c1v+0tsb/Ix3g/iYe3/8mId//rrHH/k5N2P8wLN3/w8TD/kxK2/90dev/io3u/klG + 5f8lHt//JR7f/iUe3/8lHt/+Jh7f/yUe3/8lHt/+JR7f/yUe3/8lHt/+JR7f/yUe3/8lHuD+Gxeg/yQd + 1f8lHt/+JR7f/yUg4P9zgfL+YWjs/yUe3/8jG9/+c33v/y8q4f8lHuD+GBaL/wEBA/94eHj+6+vr/+np + 6f/p6en+oaGh/yQkJf9CXN3+P1PLRQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARkLLGTUx + yryAgN/4m53p/qWo7P6zsvD+Pjvd/ich2f4xLdv+NzTa/j8/2f5TVt7+b3fl/n2H6v5sd+P+IyHS/iUe + 3/4lHt/+JR7g/jo4xv6lprb+trfG/rW2xv62t8b+trbG/ra3xv63uMj+mZyz/lJFlP5QJWP+VyZR/lIl + Xf5IJH3+OSKn/iwgzv4kHuL+JR7g/iUe3/4lHt/+JR7f/kM/2v7FxsP+My/d/iUe3/4lHt/+Z2bU/pqb + y/4jHeD+rK7I/mVl1P4kHd/+JBzf/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+HRms/iIbx/4lHt/+JR7f/iUe3/5UY+7+ZnPv/igi4P4kHN/+d4Dw/k5Q + 6P4lHt/+IRzC/hQVKP7Pz9D+6ejp/unp6f7s7Oz+RERE/kJDSv4yT97+LzLcnTtC2BUAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUlegUrKzwTbGz9Iu3u/fGSknl7yUd3/4lHt/+Jh7f/yYe + 4P8mHuD/JB3e/iMc2f8oItv+JR7e/yYe4P8lHt/+JyDc/4aHrv+2t8f+trfG/7a3xv+2t8b+ra27/6Ce + qP95XV/+aDo3/2UqJv9lKCP+ZSgk/2UoJP9lKCP/Zigg/mYoIv9cJ0D/RSSG/i8gxv8lHuH/JR7f/jAr + 3v+7vMX/VFPX/iUe3/8mHuD/LSfe/q6wx/9LSdn/goPP/o2Ozf8jHOD/JR7f/iUe3/8mHuD/JR7f/iUe + 4P8lHt/+Jh7f/yYe4P8lHt/+JR7f/yUe3/8lHt/+Jh7g/yYe3/8lHt/+IRvB/x4Zsf8mHuD+Jh/f/yUe + 3/8jOOn+IzPn/yYe3/8kHN/+cHrv/3mC8f8lHt/+JR7e/0ZGnv/i4uH+5+fn/8bGxv9gYGD+AwMC/1xf + g/8fPOL+Zm7t/2tz7u8zMuNyODbjAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABeY+YGPEPlPSM76vUkNOf+JSPh/yYe3/8mHuD/JR7f/iYe3/8lHt/+Jh7f/yYe4P8lHt/+ODTI/6yt + vP+1tsb+trfG/7a3xv+lprL+f3d7/2g2M/9lJyP+ZSgj/2UoJP9lKCP+ZSgk/2UoJP9lKCT/ZSgj/mUo + JP9lKCP/ZSgi/mIoLP9SJWD/MyG4/igh3v+kpcn/eXnQ/iQd4P8mHt//JR3g/m5v0/+Xmcz/WlnW/qyt + yP8nId//JR7f/iUe3/8mHuD/JR7f/iUe3/8lHt/+Jh7g/yYe4P8lHt/+JR7f/yUe3/8lHt/+Jh7g/yYe + 3/8lHt/+JB7W/xoXm/8mHuD+JR7f/yUe3/8kNej+Iz/s/yUf4P8kHN/+cHrv/5Gd9v89POT+JR7g/zAs + yP+kp8j+iIiI/wwMDP8BAQH+UFFZ/zhGs/8kOer+Jx/g/zs65P9fZuv7LyrhWQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjjlYiQ56fwlIeH+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+S0m+/rS1w/61tsb+tbbG/qusuf6IiI7+ZzQx/mUnI/5lKCP+ZSgj/mUo + I/5lKCP+ZScj/mUoI/5lKCP+Zycf/mYnIv5mJyH+Zicg/mcnHv5mJx/+Zici/lMlWP6Ui6/+mpvH/iAb + u/4iHMn+JR7Z/jIt3P62uMb+WFnX/ri6xv44NN7+JR7g/iUe4P4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+Jh/g/hsXn/4kHdb+JR7f/iUe3/4kLuX+Ikfv/iQz + 5/4kHN/+b3jv/pSg9/59h/L+Jh/f/oOE7f6Okaj+T1Jy/ggJFv44OVb+Pkmw/iFB5fslK+S5JR7f9SUe + 3/4nIeD6Mi7iPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4OOQFJiXi6TlB + 5/8lHt/+Jh7f/yUe3/8mHuD/JR7f/iYe3/8lHt/+Jh7g/yUe3/8lHt/+Vla7/7W3xP+1t8b+trfH/5SU + nf+Dc3f+ZSci/2UoJP9lKCP+ZSgk/2UoJP9lKCP+ZSgk/2UoJv9HNoX/L0HM/ixF2v9VZdz/ZXDX/mlx + 0P9pbMf/aWe8/k5NsP9PW8H/YXfX/h8wtv8bKq3/GSWn/hghn/9pcLT/goi3/qirvP9MTKL/GhaZ/h0Y + qP8fGrr/IhzL/iUe2f8mHuD+JR7g/yYe4P8lHt/+Jh7f/yUe3/8lHt/+JR7f/yUe3/8lHt/+Jh7f/yEb + xv8eGrP+Jh7g/yUe3/8lIeD+I0Xv/yNG7/8jQOz+X3Xy/5Sg9/+Woff+R0nn/8PF8/9XWFj+Bwoh/yI4 + 2/8gQef+I0bw/yM+6tssNcwOLzPYCyws4CEuK+EbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAA6OOQfJR7f/k1P5/9mbOz+PDvk/ywn4f8oIuD/Jh/f/iUe4P8lHt/+JR3f/yUd + 3/8kHeD+XFy5/7a3xf+1t8b+t7jH/5CRmf97XmD+ZSgj/2UoJP9lKCP+ZSgk/2UoJP9lKCP+ZSgk/2Eq + MP8nQuD/Ikbv/iZJ7/9zh/T/hZX1/oeW9v+Il/b/iJf2/m6D9f8hRe//Ikbw/iJH8P82V/H/dIf1/nyO + 9f94ivP/cYbw/muB7P9Tad7/M0bJ/iU1t/8cJ6T/GB6U/hcZj/8ZF5b+HRms/yEbxv8kHdj+JR7f/yYe + 4P8lHuD+Jh7g/yYe4P8lHt/+Jh7f/yUe3v8cGKL+JB3V/yUe3/8lHt/+JDbo/yNG7/8iRu/+MlPw/4mX + 9v96hvL+REXl/7e4wv8HBwf+Bgoc/yJE5P8iRu/+I0Xu/iUu3XIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLSOY6JR7f/iMb3/4xLeL+Z27t/nmE + 8f5yfu/+cHjv/m537/5veO/+am7u/mRq7P5fZez+c3jC/rS1xP61tsb+trfH/pOUnf57XmD+ZSgj/mUo + I/5lKCP+ZScj/mUoI/5lKCP+ZSgj/mUoJP5RKWH+LSrN/iQr5v4kLOT+Ji/l/icx5v4nM+b+JzTm/iY0 + 5v4kMub+JDLn/iQ26P4nQOv+Ql3w/lpz8/5xhvT+hpX2/pOf9v6Yovf+laD2/o2b9/6Bkvf+b4P1/ltx + 7/5CW+P+KULS/hwwvv4bJ7H+HSGu/h4bsv4fGcD+IxzS/iUe3P4mHuD+Jh7h/iUe3f4fGrT+FxSH/iMc + zv4lHuD+JSHg/iNB7f4iRu/+Ikbv/jBS8P4qS+7+gJX1/lRUVf4AAAD+AwYU/iFC3v4iR+/+JDLlwSQ0 + 3QwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABmZOpRc3Ps/5eY8v+vsvX+MCzh/yMc3/8jHN//JBzf/iYg4P8yMOL+XGHr/3R98P98hfH+gIjZ/6qr + uv+jpLD+t7jH/5mapP+VkZn+ZjIu/2UnI/9lJyP+ZSgk/2UoJP9lJyP+ZSgk/2UoJP9lKCT/ZSgl/lAk + Yv80IbT/JB3e/iUe3/8mHt//JR7f/iYe3/8mHuD/JR7f/iYe3/8mHd//JR7f/iUh4f8jJuP/Ii/m/ig8 + 6f81Tu3/SWLw/lt08/9tgvT/fY71/oqY9v+UoPb+lqH3/4mY9v9xhfX+VnD1/z1b8P9rgOL+QFTF/0ZP + rf8YHZf+FxWH/xUSfP8UEnn+FBN6/xYTgP8hG8T+Jh7g/yUm4v8jQ+3+I0bv/yNG7/8fRO7+qrPS/wwM + DP8AAAD+AwYN/yFB1v4kMt7LJTS9FQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC2tvZt0ND7/9DQ+v/P0fv+Qj7k/yUe3/8lHt//JR7f/iYe + 3/8lHt/+JR3f/yUe3/8mH9/+Pz7j/5KWs/+lprL+trfH/7CxwP+QkZj+lJCY/3hcXf9uRUT+ajw5/2k4 + Nf9oNjP+aTc0/2k5Nv9sPz3/c1BQ/oFqbf+SiZL/j5DF/lRT0/4qJNv4JR7f/iUe3/8lHt//JR7f/iYe + 3/8mHuD/JR7f/iUe3/8lHt//JR7f/iUe3/8lH9//JSHg/iQm4v8kLuX/JDrq/ilF7f8yUu/+Q2Hx/1ly + 8/9xhfT+hpX2/5Cc9v+1vvn+o7H5/6+9+v9FYe/+LUrh/x81xP8YIp7+FRWA/xQSef8VE33+Hxq4/yUe + 3/8lJeL+Iz7r/yNG7/9BYvL+fn5//wAAAP8AAAD+BAUK/ykuuKsjKocVAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACsr/V0paf0/nFx + 7P5FQuT+JyDf/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4oIt/+f4ny/o6X3v6io7H+trfH/ra3 + xv6xssD+k5Sc/q2uvP6ztcT+r7G//q2vvf6srbr+ra68/q+xv/6wssD+qqy5/p2eqOmNjpXCeniTjWhm + lGBAPb44KSLdeyUe39olHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR3f/iUf3/4lI+H+JSzk/iM16P4hP+z+JUjv/jFT8P6Emfb+lKX3/rrD+v6Woff+kJ32/n2O + 9f5bdPT+J0Xj/hwuuf4WGIf+FBJ5/hsXof4lHtr+JR/g/iQn4/6JlfH+KCgo/gAAAP4AAAD+CwsL/nV3 + h2MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABBPON8Ihvf/y8p4f83NOL+JR7g/yYe4P8mHuD/JR7f/iYe3/8lHt/+Jh7f/yUd + 3/tRU+eLhY7vb4yV7YOLktKZkJGew6qruPm1tsX+t7jI/7a3x/+2tsb+sbLB/6iotfeam6TbjIuSsoJ9 + gYZ5cXNTeGxyJHhrfgUAAAAAAAAAAAAAAAAAAAAAAAAAACoj2wUmHt9CJR7foiYe3+gmH+D/JR7f/iYe + 4P8mHuD/JR7f/iYe3/8mHuD/JR7f/iUe3/8mH+D/JR7f/iYe4P8lHt/+Jh7f/yYe4P8lHd/+JSHg/y01 + 5v+gr/b+UWvx/1589P8zVPD+XXbz/4OS9f+VoPb+hpb2/y1P8P8jRu7+HjXJ/xgfl/8WFIH+Hhm08CYg + 3tiqrNT6AAAA/wAAAP8AAAD+HBwc/qurrpAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACHiO+Ira/1/8LD+f/Bw/j+KCLg/yYe + 4P8mHt//JR7f/iYe3/8lHt/+Jh7g/ych4K0+OuAGAAAAAAAAAAAAAAAAl5q4B4qLljSKipJshoeMkoSF + ioyFhYpxiYiOTYaEiSePiZAOjoeNAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJR3fASUe3xklHt9fJR7fvSYe3/IlHt/+JR7f/iYe4P8mHuD/JR7f/iYe3/8mHuD/JR7f/iYe + 4P8lHt/+Jh7g/yYe3/8lHt/+JR/f/3h77P9eX+j+j5Tw/zQ25P8kMOb+Izzq/yZH7v89XPH+Ynrz/zZW + 8P8iRu/+I0bw/yJG7v8gPNj+HzK+jkVRwzivsLTvAAAA/wAAAP8AAAD+Ozs8ura2uK0AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAC0tfaW0ND7/s/P+/6ztfb+Kybg/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f1zAq4RoAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJR7eAiUe3yomH996JR7f1yQd + 3/0jHN/+Ixze/iMc3/4jHN/+Ix3f/iQd3/4kHt/+Jh/g/ism4P5IRuX+fYHt/nh97P6DiO7+VFPn/iUe + 3/4lHt/+JR7f/iUi4f4lLOT+Iznq/iJE7v4iR+/+Ikbv/iJG7/4iRu/+Ikfv/klo8uqKior9AAAA/gAA + AP4AAAD3SUtUOsXFxcV6gJoGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACHie+bZGPp/zUx4v8hGt/+Jh7g/yYe4P8mHuD/JR7f/iYe + 4P8lHt/uJh/fLgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAKyXgAkFA5Ddtc+uRfITt4HqC7f59he3/hY3v/n6H7v+Bj+7+gY7u/4SL + 7/9/h+7+dHrs/15g6f8pI+D+JR7g/yYf4P8lHt/+Jh7g/yYe4P8lHt/+Jh7f/yUg4P8lLOT+Izvr/yNF + 7v8iR+/+I0bv/01s8v+Dg4P+AAAA/wAAAP8PFzmsU16QB8HBwsB5hLcdAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLR+WNWFbo/3t7 + 7f+Hiu/+JR/f/yYe4P8mHt//JR7f/iYe4PIlHt9QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU1LmEUZD + 5E04NOKlMy7h6TIt4v0wLOH+Lyvh/y0o4P8pI+D+JR7f/yUe3/8lHt/+Jh7f/yYe3/8lHt/+JR7g/yYe + 4P8lHt/+Jh7f/yYe3/8lHt/+JR/g/yUk4f8kMuf+I0Ht/01s8v/R0dH+AgIC/wYLH/8hQtv4JEbov4yZ + 0tOBkM87AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACNk/BN09L7/s/P+/7P0Pv+LSng/iUe3/4lHt/+JR7f+iUe32gAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQkDjGjQv4WApIuC4JR7f+SUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR/g/kpP + 6P78/P3+MDAy/hkxnf4jRvD+Ikbu/jtb8P6UqPbSc4zxV1hz7AUAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB0dOwFn6Dz2ZOT8f9aWOf+Jh/g/yYe + 4P8mHuD7JR7fbSUe3wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAMCvhHCgi4G4mHt+/JR7f9SUe3/8lHt/+JR7f/yYe4P8lHt/+JR7f/yUe3/8lHt/+Jh7g/yUe + 3/8lHt/+Jh7f/yYe3/8lHt/+Jh7g/0lH5f/7/P7+gIK3/yMz5f8jQ+3+I0fv/z1d8f+Uqfj+bIf1/yRI + 79ExU/BUJ0rvBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAASUXlVyYf4OwlHt/+Jh7f/iYe3+4oIOBqJR7fAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlHt8FJR7fKiUe33YlHt/BJR7f7yYe + 4P0lHt/+JR7f/yUe3/8lHt/+JR7f/yUe3/8lHt/+Jh7g/yUe3/8lHt/+JR7f/y0n4P/Nz/f+dnfr/yQd + 3/8lIeD+Iy7m/3WL9P+Oo/f+YH30/yJG7/8iRu/7I0bvuCJE7jMiRO4BAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEpF5SMzLOFUMCnhVy8p4SoAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAlHt4CJR7fICUe314lHt+sJR7f8iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/41L+L+KCLf/iUe3/4iG9/+ZGXo/s3Q9/6NmfL+SmXw/iJH7/4iRu/+Ikbv/iNG + 7/AiRe56IkPtAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJR7fDyUe + 31MmHt+jJR7f5SUe3/4iG9/+Ihvf/yIa3/8iGt/+Ihvf/yIb3/8kHd/+My7h/15c6P+gpfH+tbv1/5GY + 8P+PlPD+Jh/f/yUp4/8jPOv+I0bv/yNG7/8iRu/+I0bvuyND7ScAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnIN8BKyTgFTMv4U+Bgu2ghoru45WW8PyOkO/+jZDv/5Ka + 8P+aovH+qbT0/6y09P+Xn/H+gIPu/1JS5/8pI+D+Jh7f/yYe3/8lHt/+JSjj/yM86/8jRu7+I0bv/yNG + 7+UiQ+1eIz/sBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAU1PmEkI/5E04NOKbNDDi6jQw4v40L+L+MCzh/ikj4P4kHt/+JBzf/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lKOL+Izzr/iNG7/4iRu/8IkXunyM96xMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASEXkCDk14kYxK+GWJR7f3iUe + 3/0lHt/+Jh7g/yYe3/8lHt/+Jh7f/yUe3/8lHt/+Jh7g/yUe3/8lHt/+Jh7f/yUo4/8jPOv+I0fv/yNG + 79IjOuoZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAPzvjEC4o4UMnIOCTJR7f3CUe3/klHt/+Jh7f/yYe3/8lHt/+Jh7g/yUe + 3/8lHt/+Jh7f/yYe3/8lH+D+JC3l/yNE7u8jO+omAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJR7eDCUe + 30AlHt+KJR7f2iUe3/0lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR3f9iQ16GsjNugCAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJR7fAiUe3yclHt9lJR7foyUe39slHt/uJh7g4iUf + 4KclIOByJSXiJQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJR7fAyUe3xAlHt8XJR7fEiUf3wMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////g//////////////+AP/////////////8 + AH/////////////4AH/////////////wAD/////////////gAD////////////4AAD////////////gA + AB///////////4AAAA///////////gAAAAf//////////gAAAAf//////////gAAAAf//////////wAA + AAf//////////4AAAAf//////////4AAAAf/w////////8AAAA/+AP///////8AAAA/8AH///////+AA + AA/4AD//////x/AAAA/wAB//////AAAAAB/gAA/////8AAAAAD/gAAf////8AAAAAH/AAAL////+AAAA + AH4AAAD////hwAAAAHgAAAD////A4AAAAAgAAAD///+AfAAAAAIAAAD///8AfwAAAAAAAAD///4AP4AA + AAAAAAD///4APAAAAAAAAAD///wAAAAAAAAAAAH///wAAAAAAAAAAAH///gAAAAAAAAAAAH///gAAAAA + AAAAAAH///gAAAAAAAAAAAH///gAAAAAAAAAAAP///gAAAAAAAAAAAH///AAAAAAAAAAAAH//+AAAAAA + AAAAAAH//4AAAAAAAAAAAAD//wAAAAAAAAAAAAD//wAAAAAAAAAAAAD/gAAAAAAAAAAAAAD/AAAAAAAA + AAAAAAD/AAAAAAAAAAAAAAB/wAAAAAAAAAAAAAAf+AAAAAAAAAAAAAAf/AAAAAAAAAAAAAAf+AAAAAAA + AAAAAAA/+AAAAAAAAAAAAAP/+AAAAAAAAAAAAAP/+AAAAAAAAAAAAAf/+AAAAAAAAAAAAA//+AAAAAAA + AAAAAB//+AAAAB8AAAAAAB//+AA4Af/AAAAAAB//+AB////4AAAAAA//+AD/////AAAAAA//+AH///// + 4AAAAA//+AP//////AAAAAP/+AP//////4AAAAD//Af//////+AAAAA//h////////wAAAAf//////// + ///AAAAP///////////wAAAD////////////AAAB////////////4AAA/////////////AAA//////// + /////4AA//////////////AD//////////////4P//////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + ////////KAAAAEgAAACQAAAAAQAgAAAAAABgVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAgHMRsDAxZxAwMVhQICEXEFBBspCAggAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcGmkCCAglbgYG + IfMMDED/Cgk4/gUFGv8AAAD7BgYKniAgKwoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFhYgwEdHT6FGRaV/SQd4v8vKt3/UU7R/2Bf + y/88PI//BwcT/hIUHYQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAADY3SW87OqX9eHfS/7i52f7p6ur//f38/vj5+//s7vf+pqmz/x4d + HO0hLWIMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvsHNAa+y + whaipbBWsLG2ps7Oz/nx8vX/3OL6/6e39v92jfD/S2fr/zJT7P8qTfD/LlHw/ztUw/8kNIUyAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKOmtxujpbBlvr/FtuDg4PD19fX+zNLs/4mb + 6P5HZOz/JEju/yFF7/4jRu//Ikbv/iNG7/8iRu/+I0bv/yJG7/4lRuirJDbGBAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAJ2jwRego7NovLy/vebn5/fq7fj+tcDw/3OI6f83Vuf/Ikbu/yJG7/8jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//I0Ht/yUj4f8kOOn+I0DocgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjZS2GtPT + 1efk6Pj/obH1/1137/8uUO3/IUXv/yJG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//JDDm/yYe3/8lH+D/Izrq9SM66SsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhpLIBIGNwaAkSO/+Ikbv/yJG + 7/4jRu//Ikbv/iNG7/8iRu/+I0bv/yJG7/4jRu//I0bv/yJG7/4jRu//Ikbv/iNG7/8iR+/+JSbi/yUe + 3/4lHt//JSfj/iM+62IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFhx5QwjR+/jI0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//JSHh/yYe4P8mHuD/JSrk/yM5 + 6kUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlSO1tI0bv/iJG7/4jRu//Ikbv/iNG7/8iRu/+I0bv/yJG + 7/4jRu//I0bv/yJG7/4jRu//I0bv/iNG7/8jP+z+JR7f/yUe3/4lHt//JC7l+yQw5hUAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGBSIBBAQXDgMDFAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAjRu4NI0bv2SNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8kMeb/JR7f/yUe3/8lJeL/JSXizgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAADQtRCgYFJHcBAQXPAAEC7gAAAOsAAAPDAwMXTAYGJAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAI0TuWyJH7/4jRu//Ikbv/iNG7/8iRu/+I0bv/yJG7/4jRu//I0bv/yJG7/4jRu//I0bv/iNE + 7v8lJuL+JSDg/yUj4f4lL+b/JSDghgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOC1YeBwckyBQS + dP8dGaz/IBu5/hsXof8ODUz+AAAB+wMDEokNEVADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAND7nBzM95wQAAAAAAAAAAAAAAAAAAAAAIzfpAiQ8 + 67YkNej+I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yQ26P8lLuX/JiDg/yUv + 5v8lJuLpJSPhGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABIQbRcPDlPVIx3Y/yUd6P8lHej/JB3l/yQe + 2P8jHsf/Fxds/wkMLf4OFlpFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJCrkBSY051gmL+WrMTrm3EBK6dQqL+S3Ji7lmCUr5HQkMudUJDXoMSc46CQmIuHGJCre/h4w + wv8gPtz+I0fv/yJG7/4jRu//I0bv/yJG7/4jRu//I0Ds/iUw5v8lK+T+JSDg/yUn4/4lIOBnAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAFxSDBBIQYrIkHeT/JB3k/iQg0f8kIMz/JSDK/iUf0v8lHt7+JR7h/yUp + 5P4iRezaHzvWUgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgJ8oEJC7ksiUf + 3/4nIeD/R0jn/z495f81M+P/JR7f/yUe3/8lHt/+JR/g/SUj4fgrL+TwO0TZ/ycuwf8bIaj/Gyas/x82 + yf8gOtT/ID7c/yFC5f8iNt//JSTg/yUt5f8lH+D/Jh7g/iUf36glI+ECAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAExFnXyAduP4gHa7/JB/F/yUf3v8mHuD/Jh7f/yYe3/8mHt//Jh7f/yUe3/8jQu3/I0bv/CpM + 61YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGxqlFyMe0XwlHt/lJh7f/yYe + 4P8lHt//JR7g/yUe3/8mHt//Jh7g/yUe3/9VWOn/lqL3/4+a9f9/ifP/aHLv/09Y5f8yO9L/Hie8/xok + qf8YHJD/FxWL/x4Zsv8kHtn/JR/fxSQl3xAAAAAAAAAAAAAAAAAAAAAAAAAAACQd1xwjHdNcHhqk4iQe + zf8mHuD/Jh7f/yYe3/8mHuD/Jh7f/yUe3/8mHuD/JR7f/yYe3/8jO+r/I0bv/yNH7+E/WtkToarVNwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAACAcvHwUFHVsICTY4EhFwAQAAAAAiHb8IJR7aYiUe380lHt/9JR7f/yUe + 3/4lHt//Jh7f/yUe3/4mIN//PDrk/lJV6f9kaez+dn/w/4qV9P6Wovf/jJj1/3V98P5fa+//P07m/iEt + zf8dKrr1IzLiWyMs4hUAAAAAAAAAAAAAAAAlI95RJR7f2SYe3/0lHt/+Jh7f/yUe3/4lHt//JR7f/iYe + 3/8lHt//JR7f/iUe3/8lHt/+JR7f/yUe3/4jPOv/Ikbv/iNH7/6Zpdi5QUZdawAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAMC0VLDg5Q7AgIKf8AAAD6BQQhigAAAAAAAAAAAAAAACUe3gYlHt9IJR7ftiUe3/kmHt//JR7f/yUe + 3/8lHt//JR7f/yYe3/8lHd//JR3f/yol4P9HSOf/anPu/3uG8f90ffD/dHzw/0FA5f8lH+D/JSPh/iUs + 5O4kNOi/JDjpfiQx5hYkK+EPIiDNkh8at/UkHdT/JR7e/yUe3/8lHuD/Jh7g/yYe4P8lHuD/JR7f/yUe + 3/8lHt//JR7f/yUi4f8jRO7/IkXv/4CW8f1CQkPzICMzXQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQTYDAREWTwGxel/yQe + 4/4YFYn/AgIJ/gYFJVQAAAAAAAAAAAAAAAAAAAAAAAAAACUl4iwlIeGYJh7g7iUe3/4lHt//JR7f/iYe + 4P8lHt/+JR7f/y0o4f51fvD/laH3/5Wh9/6Pm/b/VVnp/iUe3/8lHt/+Jh7f/yUe3/4mHt//JR7f/iUl + 4t0jP+uPIkHrfSAz0J4aJKL+GSOh/xogn/4aHZ3/Ghqa/hsXof8kHdb/Jh7g/iYe4P8lHt/+JSDg/yM9 + 6/4iRu//X3v0/pucnv4BAQH+IiMsTgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHRxyDSQmbs8gHLb/JB3n/yUd5/8lHej/FhSD/wID + EasAAAAAAAAAAAAAAAAAAAAAAAAAACM26AcjOOkhJDXoZSQv5uV1eNP/Z2jU/yUe4P8lHt//JR3f/4CB + z/9oZ9T/U1bp/5Ke9v+Tn/b/k572/1ZZ6f8vKuH/JyDg/yYe3/8lHt//JR7f/yQv5v8jRu//I0bv/yNG + 7/8jR+//I0fv/yNG7/8jRu//IkXr/xsmpv8XFIb/GRWR/xoXnP8gHMD/Iznn/yNG7/8lSe//1Nz5/ycn + J/8DAwP9UVFUOQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAHB1Qfzw7wP4iHNf+IBrE/yQd5/4lHef/JB3c/wUHHr0AAAAAHDjADSJF + 7TsiRe51I0XuriNG798jRu/8I0bv/yJG7/5ke93/nJ7L/iYo4v8lHt/+JR7f/25t0v6Sksz/JB3f/zQx + 4v44NuP/RETm/nuE8f+Tn/b+a3Pu/yUd3/4lIOD/JDfp/iNG7/8jRu//Ikbv/iNG7/8iRu/+I0bv/yJG + 7/4hP97/GiSi/hwttf8gPdn/IUHi/iA71f8cLLP+GCCX/xkkof49VNb/uLq9/gAAAP8aGhr9ZGVlMwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAcHGMNMDJ26SMdzv8ZF5D/FhWC/yQd4/8kHef/JR7q/xUkfNoiReu4Ikbu7iNG7/0jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/9FYub/ur7G/0tm5P8jPev/JSvk/0dF2v+xssf/KCLf/yUe3/8mHt//JR7f/y0o + 4f9hZez/NjPj/yUn4v8jP+z/I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yA+3P8bGqD/JB/Z/yM/ + 7P8jRu//I0bv/yNG7/8jRu//Ikbu/yA+2/9JVLX/YWJq/wAAAP9OTk/7b29vJAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEUFNKSqK/h8a + xv8VE3n+Gxeh/yQd5/4kJOj/Izzt/yNH8P4jRu//Ikbv/iNG7/8iRu/+I0bv/yJG7/4jRu//Ikbv/yJG + 7/4sTuz/mKTP/o+d0v8iRe/+K07w/ylI7P66vMb/OkTh/yQt5f4kLOT/JC3l/iQy5/8kOOn+I0Dt/yNG + 7/4jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8iRu/+IkTr/xodnf4lHtr/JR7f/iQu5f8jRu//Ikbv/iNG + 7/8jRu/+Izrq/yQx5v4hKbT/AQEB/gAAAP+GhobramptCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALDDOHGhqB/x8Zw/8gGsX/IyPX/yQ1 + 6f8jRe//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//J0rv/y1Q8P8jRu//QmLx/yhM7/+Infb/qrPR/32P + 1v9HZOX/YX30/zFS8f+dps7/WHHg/yJG7/8jRu//I0bv/yNG7/8jRu//Ikbv/yJG7/8jRu//I0bv/yJG + 7/8iRu//I0bv/yVI7/8iRu//HTG+/yEbxP8lHt//JSDg/yNB7f8jRu//I0bv/yNG7/8kLOT/JR7f/yUe + 3/8jHc7/AwMP/wMDA/+SkpmLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHCCOpFBJ7/yAouv8jPOH/I0Xq/yNG7/8jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//JEjv/0Vn8v8vUvD/WHbz/zdY8P9rhfT/oavU/0Vj5v+nsNP/k6n3/yZK + 7/90h9n/fI3X/01t8/8yVvH/WHfz/0Ji8f8iRu//MFLw/1Jx8/8rT/D/IUXv/1578/9HZvL/LVHw/0ho + 8v8hRfD/Gx+m/yUe3/8mHt//JDLn/yNG7/8jRu//I0bv/yQv5v8lHt//JR7e/x4ZtP8uLLX/BgYd/woK + GvxYV5c2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAABkupwEPG1i7HTC8/iJG7/8iRu7+I0bv/yJG7/4jRu//I0bv/yNG7/4jRu//Ikbv/iNG + 7/8iRu/+I0bv/yJE7v5Qb/L/Y3zy/ztY7/5NbPL/dIfZ/lFs4v+HltX+aoTp/zhZ8P5MaOT/oqvM/1Ny + 8/4qTvD/H0Pv/kxr8v9be/T+j6T3/z9f8f5EZvL/Ikbv/jdb8f9ObfL/Tm7y/miE9P8gQOP+Hxq2/yUe + 3/4lH+D/I0Lt/iNG7/8jQ+7/JSrk/iYe3/8mHuD+HRmo/5WWvv7s7Or/tbfG/jEvpv8vLdOXAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHz/ZCSFC + 4pIjRu/5I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8iRu//I0Xv/yQ76v8kLuX/JSXi/yUh + 4P9AQOT/iY7u/yol4P+rsfT/oqnj/3Z60/9cYOH/maHR/0VW6/8wQeX/t7rG/0Vc7f8uSOz/N1fv/0tq + 8v9+lfX/aIHz/0Nf8P9KZvD/j6P2/01o8P9TavD/K0ru/z1c8P8fOtL/IhzJ/yYe4P8lI+H/I0bv/yM7 + 6v8lIuH/JR7f/yYe3/8kHdn/VVah/+np6f/p6en/6enp/8fI3P8vReLcXmnLAgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiRu4cIkbuxCJG7/4jRu//Ikbv/iNG + 7/8iRu/+I0bv/yJG7/4jRu//Ikbv/yxM4v5RY8j/T1HC/iYe3/8lHt/+Jh7f/ycg3/4uLOH/PTzj/zs3 + 4/5VU+b/oqPr/piZy/8kHOD+jo/O/1xd3f4lHt//o6TJ/1ZV3P4vKeH/k5Tv/nV36/8pI+D+JB3f/yUe + 3/4nIOD/NzLi/iYe3/8lHd//JR7f/iUf4P8gIMD+Ix3R/yUe3/4lIeD/JC3l/iYf4P8mHt//JR7f/iYe + 3/8gGsH+YmR6/87Ozv6+vr7/2NjY/uvq6v92i+z8UGXhMgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAChG6SAjRu/YI0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG + 7/8jQu3/QlPL/6Cjuf+urrv/hoe3/yYf3/8mHt//Jh7g/yUd3/9HSOX/WVvn/ygi4P8lHuD/JyHf/6iq + yf8pI9//Qj/a/42Ozf8lHeD/fXzQ/3V41f8qJeD/JR7f/1VV5/8wK+H/Jh7g/yYe4P8mHt//Jh7f/yUe + 3/8lHuD/JR7f/yYe3/8gG73/Ix3T/yYe3/8lHt//KCbh/ykp4/8mHuD/Jh7f/yYe3/8eGq3/AQIF/wcH + B/8CAgL/Dw8P/3R0dP+bqez/R2HgZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAElJ + 0AlITNQQQk3dCSVI7sIjRu//I0bv/yJG7/4jRu//Ikbv/iNG7/8iR+/+I0Lt/yQv5v5QT7//ra6+/7a3 + xv6lprH/VlO//iUe3/8lHt/+Jh7g/yUe3/4lHd//Ixzf/yUe3/4mHt//JB3g/pucy/9APNv+JR7f/5GS + zP4+O9v/VlTX/6Ck0P5lZen/JyHg/ldX5/85NeL+Jh7f/yUe3/4lHt//JR7f/iUe3/8lHt//JR7f/iUe + 3/8gG73+IxzP/yUe3/4mIeD/cYLz/kNH5/8lHt//Lyzh/iUe3/8eGrL+AAEF/w4ODv6RkZH/sbCx/mpq + a/+eqdz/QFrYegAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEI+yjQrJsKoS03S0XF65exuduTzP0DN5V9t + 5vxRY+f/Q1nn/zJN5/8mRer/I0Xu/yM96/8kLeX/JSDg/09Nv/+vsL//trfG/7a3xv+2t8b/ZmW7/yUe + 4f8lHuD/JR7g/yUe4P8lHt//JR7f/yUe4P8lHt//JR7g/3180P9hX9X/JR7f/0hF2f+Jic7/MSzd/62v + x/9qaun/lprw/1lX5/8lHd//JR7f/yUe3/8lHt//JR7f/yUe3/8lHt//JR7f/yUe3/8hG8X/IRvD/yUe + 3/8lHt//Z3Lv/01R6P8lHt//W2Dq/ysl4P8iHMn/AwMM/4WFhf/q6ur/6enp/39/gP9EToT/OFHVdQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAERAyyptbNuupabs6bKz8P6JiOv+Jh/a/y8r3P41Mtv/RUXe/15i + 4v5ob+T/LCvU/iUe3/8lHt/+PzzI/6yuvv62t8b/trfG/7a3xv60tcT/lZOj/lY5c/9YJkr+WCZM/00l + a/4/I5T/MCDC/yUe4f4lHt//JR7f/l5d1f+FhM7+JR7f/yUe3/6Xmcv/Ojfc/6iqyP43Mtz/JB3f/iUe + 3/8lHt/+JR7f/yUe3/4lHt//JR7f/iUe3/8lHt//JR7f/iYe3/8kHdP+Hhmz/yUe3/4lHt//Pknp/ktU + 6v8lHt//YGXr/klK5/8lHt/+Hh5V/9bW1v7p6en/5OTk/icnJ/9OW6f/OEXlyDpB3TgAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAhoflCISJ6zFoa+lyJSnj6yUm4v8lH+D/Jh7g/yUe3/8lHt//JR7f/yYe + 4P8lHuD/hoe1/7a3xv+2t8b/s7TC/5OPl/9xSUj/ZSkl/2UoI/9lKCT/ZSgk/2UoI/9lKCP/ZSgj/1on + Rf9AI5L/KR/V/0A82/+nqcj/Jh/f/yYe3/9QTtj/hIXP/4WEz/9VU9f/JR7f/yUe3/8mHuD/JR7f/yUe + 3/8mHuD/Jh7f/yUe3/8lHt//Jh7g/yYe3/8lHt//HBim/yYe4P8lHt//JDDm/yQx5v8lHt//XWLr/3Z+ + 8P8lHt//My/B/9LT2/+Ghob/JCQk/zM0Ov8ySMr/Oz7m/2Jo7Po9POR6AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABJSuUSJT3r6iUl4v4lHt//Jh7g/yUe3/4lHt//JR7f/iUe3/8pI9f+pqe6/7W2 + xv61tsX/kZGZ/2o6OP5lKCP/ZSgj/mUoJP9lKCP+ZSgk/2UoIv5mJyH/Zici/2YnIf5mJx//YCcz/ksr + g/+4ucL+KyfH/yMd0f4nIN3/n6HK/25u0/56edH/JR7g/iUe4P8lHt/+Jh7f/yUe3/4lHt//JR7f/iUe + 3/8lHt//JR7f/iYe3/8mHuD+HRmt/yUe2v4lHt//JCvk/iND7f8lI+H/XGHr/pOe9v9EReb+X13m/4SG + ov4yM0X/ISI0/kJOrv8jPOfhJR7f7SUe3/4yLuK0ODbjAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAsKOF6Pkbo/ysl4P8lHt//JR7f/yYe3/8lHt//JR7f/yUe3/8uKtH/rq++/7a3xv+oqbX/g3V5/2Un + I/9lKCT/ZSgk/2UoJP9lKCT/ZCgn/zs8qf8pQ9z/V2ni/3N93v91fNb/dHbN/09VwP9Sadz/KD/J/x4y + vv8nNbf/U2C+/3R/wP+Gjb7/FxaX/xsXoP8eGbD/IRvF/yQd1/8lHuD/Jh7g/yUe3/8lHt//JR7f/yUe + 4P8mHt//Ix3Q/x8auv8lHt//JR/g/yNE7v8jRO7/SmTx/5Sg9/9xee//oaHr/zQ0Nf8XJIX/IULo/yNG + 7/4nNNliLjXVBiws4RoyL+IIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwKuGhLinh/11i + 6/9hZuz/U1bp/0lJ5/9BQeX/Ozjk/zYz4/87O9P/r7C//7a3xv+kpbH/gGpt/2UoI/9lKCT/ZSgk/2Uo + JP9lKCT/ZCgo/zI2vv8jO+v/PVLt/1Fk7/9UaPD/VGjw/z5Y7/8jQu3/I0Pu/0Be8f97jfX/i5r2/4qY + 9v+AkfT/bX/u/1ls4/9DVdL/LD2+/x0rr/8bIav/Hhyz/yEcxv8kHdj/JR7f/yYe4P8mHuD/Jh7g/x4Z + rv8iHMv/Jh7f/yQx5v8jRu//JUnv/2F48/86S+v/lJan/wAAAP8UJoD/I0fv/yQ46M8mM9gLAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbWei5dXTs/3d47P4sKOH/NDHi/zk2 + 4/5FROb/Zmzt/n+J8v+Eju/+oaO3/6ysuv6rrLn/jYeN/2YtKf5lJyP/ZSgj/mUoJP9lKCP+ZSgk/2Uo + Jf5QJGP/NCC1/yUd3v4lHd//JR7f/iYe3/8lHt/+JR7f/yUi4P4jJ+P/JzLm/zZG6v5IW+7/WnDx/muA + 9P99jvX+jJr2/4ya9v55i/b/YHfz/kVd5P82S9D/TFfE/jU6tP8bGqT+GRWU/xUTff4VE37/IBvA/iUf + 4P8jOun/Ikbv/iJG7/9jffL+OTk5/wAAAP4TJHD/Izvm4SY2yyYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAHV07APQ0PvPzc76/6iq9P8lHt//JR7f/yYe3/8lHt//JR7f/yUe + 3/8tKOH/iY/F/6ytuv+1t8b/m5ul/4uFi/+CZ2v/eFdY/3VRUf9zTk7/dlJS/3tcXf+JdHn/mJCa/4yN + wPpHRcXqJh7f8iUe3/4lHt//JR7f/yUe3/8lHt//JR7f/yUe3/8lHuD/JSDg/yQl4v8kLuX/Jzrq/zFM + 7v9DYPH/WXLz/3OG9P+NnPb/t8H6/5+u+P9PaO3/MEfU/xwqrv8WF4X/FBJ7/x4Zs/8lH9//JDPn/yJD + 7v+Aj8n/BAQE/wAAAP8jJ1vcJCmJHwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAI2N8ARkZOnVOzfj/yQd3/4mHt//JR7f/yUe3/4lHt//JR7f/iUe3/tude7Okp3y2Zaa + u+uxssD9t7fH/6usuv62t8b/t7jI/ra3x/+ur7z7oKGs4ZSSm7eGgoyHeHCIV2FamSlLRrYJMSvXGycg + 3nQlHt/QJR7f/SUe3/4lHt//Jh7g/yUe3/4mHuD/JR7f/iUe3/8lHt/+JR7f/yUe3/4lI+H/JSvk/iM1 + 6P9GYfD/gJf2/oGW9v94ivX+k5/2/36P9f43Vuz/HjTE/hcdkf8ZFpb/IxzO/i8r4f9vcX3+AAAA/wAA + AP5gYGHXR0pqAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIuL + 7wZ6eu3doKHz/4qL8P8lHuD/Jh7f/yUe3/8mHt//Jh7f/ykj4JtPTt4DcXLWBZCTyQqLjZw/k5Sdi5OT + nK2QkJegkpGZfYiGjFCGgYcgi4GIBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlHt4FJR7fNyUe + 35QlHt/kJR7f/iYe3/8mHuD/JR7f/yUe3/8mHt//Jh7g/yYe3/8mHt//JR7f/yok4P+Fie7/cHXs/zU/ + 5/8kOur/MU/v/1hx8v9bdPP/Ikbv/yJG7v8gO9T/HSuxt2Rq1W5MTEz/AAAA/wAAAP2KiovEZWZzFAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKB7gnMzfrntrf3/3d3 + 7P4lHt//Jh7g/yUe3/4lHt//Jh7fyzEs4Q4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlHt8NJh/fUCYf + 3641MuL0OTfi/jk34v87OeL+Pj3j/0A+5P5FQeX/U1Tn/oKI7v90eOz/U1Ln/iUe3/8lHt/+JSDg/yUq + 5P4jN+n/I0Tu/iNG7/8jRu//Ikbv/nKK8PMoKCj+AAAA/wMDBr+anKR8jZGoMQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIyM7wk9O+PnOjbj/z064/8lHt//Jh7g/yYe + 4P8lHuDjJR7fIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABKSOUWYmPpaF9h + 6MFeX+j3WVvo/1dd6P9VWef/VFXn/0ZD5f8oIeD/JR7f/yYe4P8lHt//Jh7g/yYe3/8mHt//JSDg/yUs + 5f8jO+r/I0Xv/3eP8/9AQED/AQIG/x82obV0gb97jZfDVgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAISE7gKkp/TAzM36/6ip9P4lHd//Jh7f/yUe3+4lHt83AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEpK5QFAPuMnMy7heycg + 4NQlHt/+JR7f/iUe3/8lHt//JR7f/iYe3/8lHt/+Jh7g/yUe3/4lHt//JR7f/iYe3/8lHt//JSTh/naA + 7/+Xl5f+ESBk/yNG7/4mSe79jJ/uxGqD7TQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAB9fe1ZfHvt/UM/5P8lHuD/JR7g7yUe3z8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADg04gErJeAyJh/fiSUe + 39olHt/8JR7f/yYe4P8lHt//JR7f/yYe3/8lHt//JR7f/yYe3/8lHt//Jh7g/25s6v/Z2uX/JCzY/yM9 + 6/8jR+//jaP3/1d18/wpTO+vKEvvLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAB0cuwFPDbjeicf4L8nIOCxLCXgNQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUe3wglHt84JR7fgiUe + 384mHt/6Jh7f/yUe3/8lHt//JR7f/yYe3/8lHt//JR7f/zAq4f94eOv/JR7f/yQe3/9udu3/m6r2/0pp + 8v8jRu//I0bv8yNF74YjRO4JAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlHt8mJR7fdCYe + 38MlHt/3Ixzf/iMb3/8jG9//Ixvf/iMc3/8oIuD+SUbl/4mL7v6qsPP/k5nx/ior4/8kOOn/I0Xu/iNG + 7/8jRu/LI0PtLgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgh3wItJ+AjY2PoboSI + 7sKDhO72gYPt/4WL7v+Nle//kJbw/3h87P9ZWOf/LCfh/yUe3/8mHt//JSTi/yQ36f8jRe7/I0bv8CND + 7W0jP+wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAO+MeMy3haycg + 4LwkHN/2JB3f/yUe3/4mHt//JR7f/iUe3/8lHt//JR7f/iUe3/8lJOH+JDfp/yNF7v4jRe6wIzzqFQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE9N5QFBPeMbLynhZiYf + 4LglHt/yJh7f/iYe3/8mHt//Jh7f/yUe3/8mHt//Jh7f/yUl4v8jPev+Iz7rVAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlHt8aJR7fXiUe + 364mHt/qJR7f/iUe3/8lHt/+Jh7g/yUe3/glJ+OWIzboCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlHt8IJR7fNSUe + 33UlHt+DJR/fSiUi4RQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////wAAAP///////////wAAAP///////////wAAAP// + /////////wAAAP///////////wAAAP///////////wAAAP///////////wAAAP///////////wAAAP// + /////////wAAAP///////////wAAAP/////wP////wAAAP/////AH////wAAAP////+AH////wAAAP// + //+AD////wAAAP////gAD////wAAAP///+AAB////wAAAP///wAAB////wAAAP///gAAA////wAAAP// + /gAAA////wAAAP///wAAA////wAAAP///4AAA/4//wAAAP///4AAB/gH/wAAAP///8AAB/AD/wAAAP// + 88AAB+AD/wAAAP//gAAAD8AB/wAAAP//AAAAD8AA/wAAAP//gAAAHwAAPwAAAP/8IAAAHAAAPwAAAP/4 + OAAAAAAAPwAAAP/wHwAAAAAAPwAAAP/gHwAAAAAAPwAAAP/gEAAAAAAAPwAAAP/AAAAAAAAAPwAAAP/A + AAAAAAAAPwAAAP/AAAAAAAAAfwAAAP/AAAAAAAAAfwAAAP+AAAAAAAAAfwAAAP8AAAAAAAAAPwAAAP4A + AAAAAAAAPwAAAPwAAAAAAAAAPwAAAOAAAAAAAAAAPwAAAAAAAAAAAAAAPwAAAAAAAAAAAAAAHwAAAMAA + AAAAAAAADwAAAPAAAAAAAAAABwAAAPAAAAAAAAAADwAAAPAAAAAAAAAAfwAAAPAAAAAAAAAA/wAAAOAA + AAAAAAAB/wAAAOAAAAAAAAAB/wAAAOAAAP4AAAAB/wAAAOAH///AAAAB/wAAAOAP///4AAAB/wAAAOAf + ///+AAAA/wAAAPA/////wAAAPwAAAPB/////+AAADwAAAP///////4AABwAAAP///////+AAAQAAAP// + //////4AAAAAAP////////+AAAAAAP/////////4AAAAAP//////////AwAAAP///////////wAAAP// + /////////wAAAP///////////wAAAP///////////wAAAP///////////wAAAP///////////wAAAP// + /////////wAAAP///////////wAAAP///////////wAAAP///////////wAAACgAAABAAAAAgAAAAAEA + IAAAAAAAAEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYF + JUwCAhClAgIOrQICDoUICCAmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJyZrCAkJJaoSEGr/GBSH/xQRev8LCkX/AAAC+RUVHFkAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAc3SPBBwbT7IkIM7/SkfR/4aF1/+2ttz/tbbd/2tskf8FBQXvJi5UDwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmp2pIHJzeaGjpMz/4+Pm//P1+//J0vb/nq3w/4WZ + 8/9/lPb/YXCv/xojUFUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtrnJAaOmszCztcGAysrNz/Dw7/3b3+//lKXw/1Fv + 8P8mSe7/IUXv/yNG7/8jRu//I0bv/yJG7/8oR9yjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACan7MxsLK9h83Nzdvx8fT/xMzx/4GV + 6v9AXuX/IUXu/yJG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8kMuf/JDjp/yM+5GEAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACsrrih6ev0/6+9 + 9/9rg/D/LlDs/yFF7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jQe3/Jh7f/yYe + 3/8kOerzIznpIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAho+7US5R8PsiRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//JDfp/yYe3/8lHt//JSbi/yM861YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqTe2iI0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//I0bv/yQx5/8mHt//Jh7f/yUq5P8jNug1AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJUftJSNG7/ojRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8lJuL/JR7f/yUe3/8lLeXyJCzlCQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJCDYDBQUfNgMDFFQDAxNBBAQcCAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAjRu+YI0fv/yNG7/8jRu//I0bv/yNG7/8jRu//I0fv/yNG7/8jRu//I0bv/yNG7/8jPuz/Jh/g/yYe + 3/8lLOX/JR/gvQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJCDtEBAQR3AoKNv8MCzz/BAQS/wAA + AOIFBB1GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAIz7rFyNC7e0jRO7/I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//JC7l/yUo4/8lI+H/JS/m/yUh4FgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKCTlbFhSC+iQd + 4P8lHen/JR3p/yUd5v8UE3D/AAAA+wgLM08AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAKDPmHSk05msoMeWUKTLleykx5VsmL+U4JDHmGCQz5wInM+ZBJSPh9yM75f8hQeL/I0fw/yNG + 7/8jRu//I0bv/yNG7/8jRu//Iz/s/yUw5v8lIuH/JSzl/yUi4cAlJOEBAAAAAAAAAAAAAAAAAAAAAAAA + AAARD108HRmy+SUd6P8kHuD/JCDP/yQgzP8kIMX/JB7J/yMiyP8eOca9HTTFJAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAJDHmiCUj4fswLOH/TlHo/1BS6P8mH9//Jh7f/yUg4P4lJeLwJCzl0Cov + 5OEmKsr/GR+k/xsnqf8fNsr/IUHj/yJE6v8jR+//JDnq/yUq5P8lKeT/Jh7f/yYe3+0lIeAhAAAAAAAA + AAAAAAAAAAAAAAAAAAAZFpQIGhiZ2yActv8jH8b/JSDT/yYe4P8mHt//Jh7f/yYe3/8mHt//Izvr/yNG + 7/AsTekyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsapx4kHtSQJh7g8iYe3/8mHuD/JR7f/yYe + 3/8lHt//Jh7g/yUe3/9nbe3/lKD3/4KM8/9qcvD/UVvm/zM8zP8eJbb/GiSm/xgZjf8aF5z/IhzI/yYe + 4PckI95CAAAAAAAAAAAAAAAAAAAAACQe3AMjHdIwHRmomiIeuv8lHt7/Jh7f/yYe3/8mHuD/JR7f/yYe + 3/8lHt//Jh7f/yQz5/8jRu//JEfuzmJ43RV+ib0bAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALCkEMBAQYYwYHKlgREGoEAAAAACId + xBElHt13Jh7g4iUe3/8lHt//JR7f/yYe3/8lHt//KCLg/0FA5f9UV+n/Zmvt/3yF8f+UoPb/jpr1/3iA + 8f9hbvD/OUbh/x8pxf8hL9KxIzLkMyQr5AMAAAAAAAAAACUh33YlHt/rJh7f/yYe3/8mHt//JR7f/yUe + 3/8mHt//JR7f/yUe3/8lHt//JR7f/yUe3/8kNOj/I0bv/ydL8P6SmLHHP0VjJgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAREF0YDQ1I1hIR + af8DAw7/AgISuw8MXQYAAAAAAAAAACUe3wYlHt9aJR7fyiYe3/8lHt//Jh7g/yUe3/8mHt//Jh7f/yUe + 3/8lHd//Skzn/2537/9+ivL/bXbu/19j7P8nIeD/JR7f/yUj4f8kLeXrJDTnrCQx5jAjLOAOISDEjR4Z + svgiHMj/Ix3Q/yQd1v8lHtz/Jh7g/yUe4P8mHt//JR7f/yUe3/8lIOD/I0Lt/yJG7/+bquT8FBQU9zA1 + SBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAcG3UEExRdwRoWof8kHeX/IhzU/wgILP8FBB9sAAAAAAAAAAAAAAAAAAAAAAAAAAAkKOM8JSDgqyUd + 3/k+Otv/Line/yUe3/8lHt//MCvd/1xf4/+HkfT/lJ/3/5Wg9/9WWer/JR7f/yYe3/8mHuD/Jh7f/yYe + 3/8lI+H0I0fv0SNG778fOdHgHTPC/xwwu/8cLLP/Gyep/xgXjf8hHMb/JB3W/yUe3/8lH+D/Izvr/yNG + 7/+Al/b/aWlp/wYGBvo8PkUKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAHh9ZeS4tuP8lHej/JR3n/yUd5/8fGr3/BQchowAAAAAAAAAAAAAAACNA + 7BYjP+xMI0HtgiNC7bMjP+zxkprP/0xK2f8mHuD/JR7f/1ZU1/+Ymcv/MCzi/2927/90e/D/iJL0/2tx + 7v9UVun/JyDg/yUe3/8lI+H/Iz/s/yNG7/8jRu//I0bv/yNG7/8jR+//I0bv/yA81/8ZIp7/GiOg/xkk + oP8YG5H/HCSw/x861P8hRe//z9Xq/wkJCf8wMDD0MjIzAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBxiEjk7jPMiG9j/FhSF/yEbz/8lHef/JR7o/w0T + SbEeOs9bIUTnmyNG79gjRu/9I0bv/yNG7/8jRu//I0bv/3KF2f+SnND/IzTo/yUk4f8wK97/ubvF/yYf + 3/8lHt//JR7f/yYf3/9xeO//fIXx/ykj4P8lKuT/I0Tu/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/x0v + vP8eHbT/IkTr/yNG7/8jRu//I0fw/yE+3P8cLLT/KTGe/56fpP8AAAD/Z2dn6wAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQRmkuLaH/HBiv/xcX + fP8iHNj/JB3n/yQu6/8jROf+I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/9UbuH/oqzN/0Be + 5/8jRu//ITjq/6uwyf88Pd3/JSLg/yUh4P8lI+H/JCjj/yQv5v8jPOv/I0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//I0bv/x40xf8gGrz/Jh7g/yQz5/8jRu//I0bv/yNG7/8jRu//Iz7s/yo92f8UFBb/AAAA/6Oj + o8wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAMDEGoHh6K/yEb0P8fGbz/JCjj/yM+7f8jR+//I0bv/yNG7/8jRu//I0bv/yJG7/8mSe//I0bv/zVW + 8P8mSu//fJLv/4CQ1v+JmNP/Ikfv/2N+9P+CkdX/X3ff/yJH7/8jR+//I0fv/yNH7/8jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//I0bv/yJF6/8bGqT/Jh7g/yUf3/8jPuv/I0bv/yNG7/8jQu3/JSTi/yUd + 3/8lHtv/AwMO/wICAv+RkJh0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAACAguzhUVfv8kMdz/I0Di/yNH7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG + 7/8iRu//PmDx/zdZ8f9TcvP/Q2Lx/3SM9f+SntL/eYvX/3+V7f9TcfP/W3Pg/4KS1f9EZfL/Nlrx/1h2 + 8/8pTO//J0rv/05t8v8pTe//IUXv/1p38/82WPD/KEzw/0Nk8v8eM8P/Ix3P/yYe3/8kL+b/I0bv/yNG + 7/8jQ+7/JSXi/yUe3/8hG8f/Kyi7/wkJLP8PDyT7WVmQFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGjCsAxIhcOAfOtT/I0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//I0fv/yNE7v9BX/D/aH/y/0hh7/9HZ/L/k5/Q/zJU6/+OndT/UnHz/zRV + 6v+or8v/U3P0/yJG7/8hRu//Xnv0/2aD9P9rhvT/QGLy/yRI7/84XPH/TGzy/2WC9P9FZfL/HSi2/yYe + 4P8lHt//I0Ds/yNH7/8jPuv/JSPh/yYe3/8iHMz/YmOk/+rq6v/Aws3/NzWv/zk6yG4AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIELhHCJE6cEjRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//I0bv/yJG7/8jPOv/JC7l/yUj4f8lHt//Ling/4CF7f8tKOD/sbX0/8PG + 2P87Otz/oKLT/15r5f8jLeX/r7LI/01c6v84TOv/Y3zy/3GH8/9ndu//SFzt/zJI6/+Vo/X/S1vs/zhJ + 6/8ySOv/JEHt/x4iuP8mHt//JR/g/yNG7/8kMuf/Jh7f/yYe3/8mHt//Kiio/+Dh5f/p6en/6enp/9HR + 3/8zTeO1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIkXuMyNG + 7+YjRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//Ikbv/01jyv+CiLz/U1HC/yYe4P8mHt//JR7f/zIw + 4f8yMeH/NTLi/0M+5P+Cg9r/YV7V/zg03P+Ul9D/JB3f/4qKzf9eX9v/KCHg/2lp6f88OeP/JR7g/yUe + 4P8lHt//JBzf/yUe3/8lHt//Jh7f/yYe4f8fGrT/Jh7f/yUe3/8lI+H/Jh7f/yYe3/8mHuD/Jh7h/xwb + Xf9+fn7/d3d3/5WVlf/W1dX/ZH3t915w4AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAMkznLSNG7+8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//JTjl/3J3t/+2t8X/oqKt/0ZD + xP8mHt//Jh7g/yUe3/8yLuH/T07m/yUe3/8mHt//Qj7b/4WEz/8lHd//iInO/zg03P9kYtT/iY/W/ygi + 4P8lHt//UFHm/yUe3/8mHuD/JR7f/yYe3/8lHt//JR7f/yUe3/8mHuH/Hhqz/yYe3/8lHt//O0To/zQ4 + 5f8mHt//JR7f/yYf4P8KCjn/AAAA/xAQEP85OTr/ZmZm/36R6/9XbdUuAAAAAAAAAAAAAAAAAAAAAAAA + AABIRcwRQ0DLR15h2W9fY9yPREXRhjtP48cpROb/IkLq/yFF7v8jR+//I0bv/yNE7v8kNej/JiTd/3t7 + tf+2t8b/trfG/7W2wv8zLsz/JR7f/yUe3/8mHt//JR7f/yUe3/8lHt//Jh7f/ykj3/+kpsn/Ixzf/z47 + 2/+EhM7/PTjb/56gzf+Vl/D/hojt/1VU5v8mHuD/JR7f/yUe3/8mHuD/JR7f/yUe3/8lHt//Jh7h/x4Z + r/8lHt//JR3f/3R/8f8/Qeb/JB3f/0hK5v8mHuH/DQxK/xgYGP/c3Nz/6urq/4WFhv9UZrn/S1/LMwAA + AAAAAAAAAAAAAAAAAAAAAAAAOzbIVFNR0POKjub/l5nq/z072P9MTOH/V1ni/11h4P9mcOP/Wmnl/yQq + 3v8lH+D/JR7f/3V1tv+2t8b/trfG/7a3xv+3uMf/cXK0/z8klf9GJIL/PiKZ/zAgwf8mHt//JR7g/yUe + 3/8kHOD/p6nJ/ysm3v8lHd//j5HN/zUx3f+qrMj/JB3g/yYg3/8lHt//JR7f/yUe3/8lHt//JR7f/yUe + 3/8lHt//JR7f/yUe4P8dGaz/Jh7h/yUe3/9JUOn/VFrq/yQc3/9ye+//JR7f/xcVhf+FhYb/6enp/+vq + 6v8+Pj7/S16//zc/1V0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCg+IJm5zsPZmd8X45OOPOJSHh/yUg + 4P8mHuD/JB3e/yYg3P8lHt//Jh7f/0E+xv+1tsT/trfG/7W2xf+dm6X/eFZX/2YxLf9lKCP/ZSgk/2Uo + I/9mKCH/YScx/0wlcP8xIb7/JR7h/4uLzv9KR9n/Jh7f/0ZD2v9/f9D/k5TM/zg03P8lHt//Jh7g/yUe + 3/8lHuD/Jh7f/yYe3/8lHt//JR7f/yYe3/8mHt//Hhmw/yUe3P8lHt//JC/m/yQt5f8lHd//fYfy/z89 + 5f8jHdL/rq/G/7S0tP9TU1P/LCww/y5F1P9aXur/T1PouDYz4w4AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAMUHpiCQv5v8lHuD/Jh7g/yYe3/8lHt//JR7f/yUe3/9qab//trfG/7a3xv+Wlp//azw5/2Uo + JP9lKCT/ZSgk/2UoJP9lKCP/Zich/2YoIv9mJyH/Zicg/1UlVP95bK3/bGvJ/yMczf8kHd3/lpfM/3h5 + 0f9cWtf/JR7g/yYe4P8mHt//JR7f/yYe4P8lHt//JR7f/yUe3/8mHt//Jh7f/yMczf8gG8D/JR7f/yUq + 5P8jQez/JR/g/3yG8f95gvH/JiDg/5WYwf9JS13/Ghsq/0FNrP8kNujeJR7f+i8q4f01MeI2AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAOTrkBy8y5PQ8OuT/JR3f/yUe3/8mHt//JR7f/yUe3/8lHt//d3e9/7a3 + xv+xscD/gXR3/2UoI/9lKCT/ZSgk/2UoJP9lKCP/SDV+/ypD2/9UaOT/d4Hg/3h/2f92eM7/QlLM/z5Y + 2f8eNcX/LT2//1Ffw/9wfsb/ZW25/xkZmv8bF6H/Hhmz/yIcy/8lHt7/Jh7g/yUe3/8lHt//JR7f/yYe + 3/8mHuD/Hhmw/yUe4P8lH+D/I0Tu/yND7v9qfvP/lKD3/2Rm6v92dnf/EBld/yFC6P8jRe/+KTPTNi0y + 2wstK+EUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE1M5yIlHd//QUDl/2Jo7P9cYuv/V1rq/1Za + 6v9RUuj/TE7o/4SGwP+2t8b/rK27/4Ftcf9lKCP/ZSgk/2UoJP9lKCT/ZSgk/04uav8nM97/MD/p/0BN + 6/9DUev/Q1Lr/yg86v8kOur/LEfs/2N48/97jfX/jZr2/4uZ9/96jPb/ZXnt/1Bi2/83SMX/ITC3/x0l + u/8gH7//IRvI/yQd2f8mHt//Jh7g/yAbv/8dGKz/Jh7g/yQu5f8jRu//K03v/0dg8P+JlOD/CgoK/w0a + VP8jR+//JDTkpAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5d+08mJny/7e5 + 9/8wK+H/JB3f/yQd3/8sJ+H/Tk/o/15i6/91ecn/pqaz/7a3xv+SkZn/bkE//2UnI/9lJyP/ZScj/2Un + I/9lJyP/Yigt/0sxhv8zLtn/JR3f/yYe3/8mHt//Jh7f/yYe3/8mH9//JCPh/yQr5P8uO+j/P1Hs/1Fo + 8f9ievP/don1/4iX9v+Ckvb/aH71/3OH7P9ic9L/LDu4/xkemf8UE3r/FBJ5/xsXn/8lHt7/JDXo/yNG + 7/8kSO//c3eF/wAAAP8NFkb/IzPVsyo8xQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAn6DzTKus9f96ee3/LCbg/yUe3/8mHt//JR7f/yUe3/8nIN//eIHu/5+iuP+2t8b/qqu4/5qa + pP+inqn/mZCa/5eMlP+YjZb/m5Od/52cp/ybnabdiomlrEhGu34nIN63JR7f+yUe3/8mHuD/JR7f/yUe + 3/8lHt//JR7f/yUe3/8lHt//JSPh/yQs5f8jNej/LEjt/0Nh8f9/k/b/sr35/5Og9/9vhPX/TWXp/x8y + vf8WGor/GBWQ/yQd0/8lKeT/W23v/ykoKP8AAAD/Ly876zI3cgMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAGJg6VRKRuX/YWDp/yUf4P8mHuD/Jh7g/yUe3/8mHt//PTvji3l+ + 50yKkNZjkpOil6Kireisrbv9p6i09ZucptaUkpqkiYSKcYB2ej9+cnoSAAAAAAAAAAAAAAAAAAAAACYf + 3R8lHt98Jh7f2SYe3/8mHt//Jh7f/yYe4P8lHt//Jh7g/yYe3/8mHt//Jh7f/yYe3/8kIeD/f4rx/2x/ + 8f8qSO7/UGvy/3uM9f91iPT/Ikbv/x83zf8ZIZz/IR+2soaG0NsAAAD/AAAA/1RUVfVkZnUTAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACjpPNi0dH7/8HC+P8pI+D/Jh7f/yUe + 3/8lHt//JyDgvTs34gMAAAAAAAAAAAAAAACcnasBk5ScDZaXnwUAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACUe3wElHt85JR7flyYe3+0kHd//JBzf/yQd3/8kHd//JR7f/yYf + 4P83MuL/aWzq/3V57P9WVef/Jh7g/yUm4v8jMuf/J0Tt/yJG7/8jRu//I0fw/yJE6teJkLDiAAAA/wAA + APtzdHiDio2dOQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeXntZkZD + 5f8vK+H/JR7f/yYe4P8mHt//JR7f3CYf3xQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtJ+AIVljnUnJ3 + 669pbur3bnPr/2lx6/9qcer/ZWjp/1hZ6P8wK+H/JR7f/yYe4P8lHuD/Jh7f/yYe3/8lJeL/JDTo/yND + 7f8hRe//jZa1/wAAAP8OGEjXaHWzUZKav2MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAH+B7j/Bwvj/zc/6/ykj4P8mHt//Jh7g6iUe3yYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAREPkEjUx4mYpI+DBJR7f/CYe3/8lHt//JR7f/yYe3/8lHt//JR7f/yYe + 4P8lHt//JR7f/yYe3/8lH9//Iynk/8vR9P8aHSr/IULe/yRH7vyGmurCaIHrKQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2duwCfHvt1FFO5v8mHt//JR7f6yUe3zIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwm4B8mH992JR7fzSYe + 3/0lHt//JR7f/yUe3/8mHt//JR7f/yUe3/8mHt//JR7f/yQd3//Fxvb/ZGfR/yQ16P8hQ+7/hZz3/1Fv + 8/sqTe+gJUfuHQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFhU5yIyLOGELCXgiS0m + 4CMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAlHt8iJR7fbCUe37smHt/5JR7f/yUe3/8lHt//JR7f/yYe3/8lHt//S0fl/zMu + 4f8kHd//b3Hr/6Cs9f9BX/D/I0bv/yNG7/MjRO5tI0TuAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJR7fEyYe32EmH9+0Lynh9zYv + 4v81LuL/NTDi/0VC5P9oaen/nKLx/6mw8/94e+z/JSDg/yQv5v8jQu3/I0bv/yNG77YjQe0ZAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAFdV5hBydetcaGnqr2Nj6fVbXOj/UVHm/0VD5f8sJuD/JR7f/yUe3/8mHt//JR/g/yQu + 5f8jQu3/I0bv6iNB7VEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABOTOUOPTnjVy0m4KwlHt/zJh7f/yYe + 3/8lHt//Jh7f/yYe3/8lHt//JR/g/yQv5v8jRO7+Iz7sVgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJyHfCyUe31IlHt+lJh7f7iYe3/8mHt//JR7f/yYe3/8mHuD/JSTi5iM76isAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUe3wYlHt85JR7feyUe36wlHt+XJSDgVCUm + 4hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////////////////////////////////// + /////////////////////////////////////////////////////////////wf////////8A/////// + //gB////////8AH///////8AAf///////AAA///////4AAB///////gAAH///////AAAf//////8AAB/ + B/////4AAP4D/////gAA/AH///8AAAD4AP///gAAAfAAf//+AAADwAAf//CAAAGAAB//4GAAAAAAH//A + fAAAAAAf/8BwAAAAAB//gAAAAAAAP/+AAAAAAAA//4AAAAAAAD//gAAAAAAAP/8AAAAAAAA//gAAAAAA + AD/8AAAAAAAAH/gAAAAAAAAfAAAAAAAAAB8AAAAAAAAAH4AAAAAAAAAH8AAAAAAAAAfgAAAAAAAAD+AA + AAAAAAB/4AAAAAAAAH/gAAAAAAAA/+AAAeAAAAD/4A4/+AAAAP/gH///AAAA/+A////gAAB/4H////wA + AB/w/////4AAB///////8AAD///////+AAH////////AAP////////gA/////////wH///////////// + //////////////////////////////////////////////////////////////////////////////// + //8oAAAAMAAAAGAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI1AAArxwAANvEAABnnAAAAkQAA + AAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABTghHYbwYlzf/5mV + 6f+opt//XFtp/wIBAHoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDgXwfnZuPZ6mo + r+bFyvj/vMj0/5Km9P94kPn/a4b+/ys7fMsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFxbwGIhn8qrKmecMjI + x8XFzOf2o7Lz/2eB9/8zVfL/G0Dv/xY77/8YPe//GT7x/yU/8/8nS+1ZAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeHZwCLm5 + uLa5w+/+jaD6/1Nw9v8mSfD/Fzzu/xk+7/8gRO//I0bv/yNG7/8jSPD/JDvr/yYc3/8kNOrrI0jyIwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGJ1z5YeRPf/Fjvv/xtA7/8hRfD/I0bv/yNG7/8jR+//I0fv/yNG7/8jSfD/JTLn/yYb + 3v8mI+H/I0bvUgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAB9C7hUiRe/jI0bv/yNH7/8jR+//I0bv/yNG7/8jRu//I0bv/yNG + 7/8jSfD/JSzl/yYa3v8lJ+P7I0nwKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAABcAAAAXAAAAAQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjRu94I0jv/yNI8P8jR+//I0bv/yNH + 7/8jRu//I0bv/yNH7/8jQu7/JiDh/yYj4f8lJuLWJS3lBAAAAAAAAAAAAAAAAAAAAAAAAAAqBQUbtAwL + QfIHByjyAAAAqAAAABoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACJF7wohQ+4KI0fvAQAAAAAjSvEFIz/swCM/ + 7f8jSPL/I0j0/yNH8f8jR/D/I0fw/yNJ8P8kN+n/JiTi/yUs5f8mIuF3AAAAAAAAAAAAAAAAAAAAAAIC + CDQWE4bqJB3f/ycg7P8lINv/FhR2/wUJHcULF08CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkRvUsIzLnsTU75uZARujgJyvkyiQw + 5qokOOp1JTHofiYn3PsZKLv/GDLM/xw95P8gQ+v/Ikbu/yQ87f8mL+r/JiLi/yYg4MgmHN8JAAAAAAAA + AAAAAAAAAAAADhgVhtQnIef/JiDY/yUf1f8mH9v/Jx7i/yUt4f8jSviwIUn/DwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKtAsKBzsuSoi + 4f4rJOD/Jx7f/yYc3/8lG9//JyDg/m947/50fun/Xmfa/0hU1P8sOsr/HSu8/xgZqf8fHcH/Jh7c2yYm + 5B0AAAAAAAAAACYe4QkmH+ElGhaVlyMexP8lH9b/Jh/g/yYe4f8mHuH/Jh3h/yYk5P8iRfD/I0fzlM/V + 8RPk4t0BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgIISAEB + AqUDAhI+AAAAACce6CklHN+TJh7f7yYe3/8mHt//JR3g/zQw5P9KSej/WVrt/3B28v+AifX/d4Lx/15p + 4/8rLsz/ISvcwyRB7k4jSvEeAAAAACUb25AlG9b9Jx7o/yYd5P8mHeP/Jh/l/yYf4f8mHuD/Jh3f/yYl + 4v8ZPvH/YXz2+UZGRIwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAFBRJLFxWH/BwYqf8GBh/5AAAANQAAAAAAAAAAJiLhDiYf4GgmHd/MKR/e/yki3v8gGN//HRPd/zk2 + 4f99h/L/l6X4/3R88v8pIeP/Hxbf/yYg4P8lI+L6JTTomCM86GUfKb7OHSOy/x4juP8gIbz/IBu9/yUb + 1v8mHN7/Jh/l/x837f9JbP//d3uI/wUEAJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAUGBRspKZHlJB3m/ygf+/8fGr3/AAAAewAAAAAlSvoDI0XvHyRB7UkiOuyQMkXm9ISH + 0P8jHOD/IRfg/39+z/9SUuD/cXjx/3yE8v9qcO7/S0vn/yUb3v8lH+D/JDrq/yNK8v8jSfL+I0ju/yNJ + 8P8fPtP/GSOa/xskrP8dIa//Hia7/xg43P+cq/D/IyEW/3V1dYIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAABkaPogtKc//GBSS/yQb3v8lG+L/FyeQuCNJ9ZkjR/DUI0fw+CNI + 8P8iR/D/H0bw/5ikz/9GWeL/FB3o/3d30f9YU9b/GQ7f/yAW3v9WVun/YGXs/yMn4/8jQe3/I0nw/yNG + 7/8jR+//JEn1/yA50f8fHrn/Iz3m/yNK8/8jR+z/Hz7X/yM4xf95fZn/AAAA/8DAwHcAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhgYYtUeGLb/Gxah/yUl6P8kPO7/JEn3/yNH + 8P8jRu//JEfv/yNH7/8kR+//K07y/4+e2v9ziNn/IUj0/1px4/94g9X/GCzq/yEy6P8dM+n/Gzjs/yFF + 7/8jSPD/H0Pv/yFF7/8iRvH/IUPh/x4dtf8nG+P/JDjs/yNJ8P8jSPD/JTfu/yYq8v8HBzf/FxcM/+/v + 7UoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFg8NVO4hKMz/JDrr/yNH + 7v8jSfD/I0fv/yNG7/8jR/D/Jkvw/zZb8v88X/L/VXX2/4uc5/90h9f/fJPs/0Ji7v+Hl9T/NFrz/z1h + 8v82WvL/NVfx/zdZ8f8hRu//Q2Py/zFU8f81WvX/HjHI/yQZ0/8mKOX/I0jw/yNJ8P8lMef/Ihjd/ysj + zf8XFlT/KShD3bGs/QkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFSqOQxw0 + uP0jSPH/I0ny/yNH7/8jR+//H0Ty/x1C8/8jRO//Ij3s/ytA6v9oefD/Sl7v/4GW5/9oeNr/laPe/zNU + 8f+DkdP/WXXw/zJV8f9QcfP/dpL2/0Vm8v9HavP/W3r0/1Bx8/9QcvP/GiK//yYa3/8kOur/I0fv/yUr + 5P8kGuL/KSS//8HC0v/g4dn/UVTD/hMR4zQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAkSfhnJUr9+SRJ9/8jR+//I0fv/yNI8P8cQfL/NlTg/0xb0/8oJt7/JR3g/ycf3/9KSeX/RkLl/6eo + 6f9ubNX/bW3Y/01O4P9sa9P/W13d/2Bm7f9weO3/Ojvl/ycq4/9OT+f/OTrl/ygp5P8mLd//IB/B/yce + 4v8iLeb/JCLi/yYc3/8fF9//R0aT/8bHu//Ix8L/0dbn/y1N7nMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAB9F8XUfRfD/IETw/yJG8P8jSPD/I0jx/xs37v9SYM3/ra+3/4yJsf8jHN7/Ixzj/ysl + 4f8+POP/JR7g/yoj3f94d9H/NC/c/3Bu0/9TT9f/foDY/yUc4v9KSOX/Jh7f/yUc3/8hGN7/Ixrf/yUc + 4P8lG9n/IRvA/yQc4v84OeX/NDLj/yQb4P8lHuL/CQk3/wYGAP8xMTD/mpmW/1Vz+aYAAAAAAAAAAAAA + AAAAAAAAIhy+TTQyx6ZhZdu6Q0TRsURX5PE8Vej/OFbt/ylJ7f8jPOz/HiXn/1dVxf+5uL//w8XJ/4SG + yP8gGuD/KB/a/yQd5P8fG+v/Ix3n/yEa4f97etH/KiTf/2lo1P9ZV9X/kZPU/3R17f9QTub/JBzf/yYe + 4P8mHt//Jh7g/yYe4P8lHtv/IBq6/yAX4P9XXuz/TlDp/zc05P8wK+r/AgBA/4GCef//////eHdz/zVP + 068AAAAAAAAAAAAAAAAAAAAAPTrOPrO086PCx/nSRkTj9i8q3v84M93/SUnf/zo53f8fFuH/QDrN/7W2 + vv+8vsv/rq+8/459hf9eMU7/XCU7/1cmT/9II33/NCC0/xwU4/9yctn/SEPb/zYw4P93d9P/fHvQ/ywl + 3/8jG+D/Jh7g/yYe4P8mHuD/Jh7f/yYe4P8mHuH/Hxq5/yQb3f8vNOb/MTfl/0xL5/9PUPD/HRih/9TV + 0f+3tq3/OTpA/zxL3vI/O+duJhvfBAAAAAAAAAAAAAAAAAAAAAAAAAAAIjXonR8f4v8hF9//IBff/yMa + 3/8dFeH/dXS//8LEyP+lprP/b0dG/2EfGf9nJx3/aCge/2gpH/9mKyP/ZSsp/00fWP9zYqb/ZmXL/xEJ + zP9kY8v/kJDJ/zcw1P8gFt3/JR3i/yYe4/8mHuL/Jh/g/yYe4P8nH+P/IRzE/yQc1P8jJ+X/HDHp/0xM + 5/+CivP/SUTs/4OEm/8PEB7/M0Sj/CYt6uY8OOT/LCXhMwAAAAAAAAAAAAAAAAAAAAAaEt4PKCji5jw7 + 5f80L+P/MCvi/y0n4v8jHN//hYa+/7/Cyv+IfYL/YSEc/2UoI/9mKCL/ZSgn/zE5u/9DWej/d37a/3x+ + y/9TYc7/L07d/zBI1P9lc9n/h5bb/0xVwv8oLLH/IiKz/xwawf8dF9D/IBjY/yAX3v8kG+P/JR7b/yIb + xP8nIOb/ID7t/zZV8P9+jPj/hYjl/zEzN/8aOc7/JD300iUe4BEjG98ZJh7fAwAAAAAAAAAAAAAAAAAA + AABMR+YoZWHq/Wpr6/9EReb/R0jm/1ha6v9ka/D/io7E/7i6wv+QipH/ZS0o/2AeGf9hIBr/YiEZ/1Ak + VP86L7n/MDPv/ywu7f8mKuj/ICTm/zE66/9FUe//VGPy/2Z49P9ug/P/bYPt/2R55P9TZ97/QlDU/0dM + zv8uMMn/FxW2/xIOg/8eFq3/Jijp/yFG8f8yWP7/ZW2Q/wAAAv8YMtrzHRrpPwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACytPY1pqf1/11Z6f8cE97/Ihrf/yMb3/8qJeT/dXvd/6+wvP+ztcP/mpOc/5aB + if+ReoD/jXZ8/453dPqOg4LcdHOdqi8qy5ojGuLpJh3f/yQb3/8hGN7/Hxjf/yEb3/8kJOL/KzPn/zdF + 7P9KXvL/V3D1/5+y+v+QpPT/XHDh/zlJv/8WHpL/HBWi/x8f2v9CVO//MTMz/wEBAf9rabtTAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABfYek8hIPv/1pX6P8gGN//Jh7g/yMb3/8yLuKRk5/9Lo2U + yU2NjI+cnJ6nxpaao62JjZN3foKFSnR3eSdsbm0IAAAAAAAAAAAiGuYVJh/gXCYf4LsmH+D3JRzg/yAY + 3v8hGN7/IBbe/x8U3v8bEt7/PEDm/3uH8P9HVu3/SmDz/2yE/P8xVfL/HDnN/xgivdlubc+5ERAH/xUV + FPzGxbtVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACVmPJGlpby/1BN5/8hGd//Jh7g/yUd + 4LwoIN8FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAkHeAiMCvhe09P5s1OTeb8TU3m/0xN5v9XWej/cXPr/0tI5f8iGd//Hxrg/yIl4/8lNun/JEPy/x9J + +/hxg8jxBgQA/ycuS53IyMdcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABXXOgyn6Dz/2xq + 6/8fF9//Jh/g1iUe4BcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKF7gFIROU3LijhijEt4t0xLOL/Jh/f/yEZ3/8mHuD/Jh7f/yYd + 3/8mHN//Jh/g/xsj5/+SmuD/MjhK/xk/5fCGmefBTmvuJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAYmDqrjw34/8jHN/TJh/gIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQd4AokHeBDJR7glyYe + 4OEmH+D+Jh7g/yYf4P8lHuD/JBzf/xkP3f+HhOv/XF7d/xIj6v+Bmfj/RGf09R5D75MjR+8TAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAHRbfCiIb3yomH+APAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACYf4AkmH+A4Jh/gjCYe4NMmHt/9Lifh/zIr4f84M+P/S0fo/4KB7P+hpvP/MkDp/yA/ + 7f8jSfDjI0jwSQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8X3wNgYuk3fYDtg1ZU589dX+j/aWvq/2Fh + 6f8zLeL/Ixnf/yYg4P8lMOb/I0Lt/yNJ8JAjR+8QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABoS + 3gUgGN8zHhXeex4W39AjG9/7Jh/f/yYe4P8mHN//JiDg/yQy5/8jRO6NAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACYf4AMmH+AqJh/geCYe4LgmH+DqJh7f3CYb36IkO+srAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA////////h/////////8AAP///////wAA////////AAD///////8AAP// + /////5yA////////AAD///4H///cRv///Af//wU////wB///AAD//wAD///i///+AAH//y8D//8AAf// + /////wAB8P+6Cf//gAHgf/////iAA8A/bwD/4AADgB/////gAAYAB2QA/4gAAgAP/v//BgAAAA+DAP4E + AAAAD+j//gAAAAAPHAD8AAAAAA////wAAAAADwAA/AAAAAAPm+/4AAAAAA8AAPAAAAAADwAsAAAAAAAP + AAAAAAAAAAMAAOAAAAAAAwAAwAAAAAADAADAAAAAAB8AAMAAAAAAPwADwAAwAAA/AAHAf/4AAD8AAMD/ + /4AAHwAB4f//8AAHAAHj///+AAMAAP/////AAAAI//////gAAAD//////wAAAP///////wAA//////// + AAD///////8AAP///////4gH////////AAD///////8EgP///////wAYKAAAACAAAABAAAAAAQAgAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFQAA + HIUAABOYAAAAPQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAUEUSaVVC1/3V0qf8sLDHPAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISC + eBWfnZVYqay1tJOf6v97kvn/Y3/8/0Zj5P8bLXlQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiG + eyqqr8W3i5zn8WJ89P84Wfb/HULy/xg+8P8bQvD/IDLr/yc19d0lTP4VAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAV2i2GTFT9egXPvL/GkDv/yBF8P8jR+//I0fw/yNJ8P8lKeT/JiDh/yNI7zUAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAIUXvXyNJ8P8jSPH/I0fw/yNH8P8jSPD/I0Tu/yYj4v8mJeLsJTTtDgAA + AAAAAAAAAAAAEAUEGo0GBR+uAAAAXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACZO/wUkRO03LEbsUSRB7DYjRu8aIjnquxw34P8dQer/IUfx/yNK9P8lOu7/Jibj/yYi + 4Y4AAAAAAAAAAAAAAAwWE4PNJSDf/yYg2v8YFof/DR97XgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAHS+/Fysm8NE1MOP/KCHg/yMf4fM4OOblX2fb/0pY1v8vRNj/HzDF/yAi + x/8mIuC2AAAAACYh4wEkHNIeFxSCnCYg3P8mIN7/Jh/g/ygf6v8bOPn+Wnn/Ue3s6wUAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAABQcGKJoDAQyTFRB9CyQb4U8mHOCzJhvf+Sce4P88OOj/TUvq/3R8 + 8f96he3/NjjW/x8k4L0kPexvJS/mLyMez8klHdb/JB3W/yYe3v8nHeP/Jh/j/yhG+v9baqfuCAcEIwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWFk+OJR/k/xsUpf8DBxVDAAAAACU67iEgMeqOSlHe+Dkz + 2/8xKNr/aWvg/3mA8/9hZO3/ODDj/yQa3v8kPu3qIkTl5CE+3P8fN8r/HCOs/yAhv/8ZJs7/ZH3p/zo4 + L/eGhocbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQYKIiYjoPodFbT/JSLn/x46ycskSvXWIknw/htD + 8v9MauX/coHZ/y9A5v9patT/GBTh/zxA5/89Sur/IDvs/yFH8P8iR/P/IkTn/yEjx/8jPun/Ikvu/x87 + 4v8xN3//QUAy7P///w8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAQhPHR6o/yM04v8kRvL/I0n2/yJH + 8P8pT/H/Mlfy/1578f+Cldn/WXnw/2yC3f80VvD/K0/w/zBV8f8oTvH/NFjy/zBX8/8gLcr/JSDe/yNH + 8f8kPu7/JSHo/w0KZ/9VU3GnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH0DUBRs1s6UiROb/JEvz/x9G + 8v8fQ+//JT7q/yIw6P9MV+v/a3jv/4SO3f9peOL/YnHd/1Fn6v9WcPP/WW3w/0Zf7/9OZPD/Plfp/yEg + yv8kLun/JDbq/x0V3/9oZMb/6Ond/15k28wTG+YEAAAAAAAAAAAAAAAAAAAAACMz2g0iS/i9IUj6/yNK + 8/8cQfL/OE/f/42Uvf88Ntb/Hxfp/zUy5v82MOP/Z2TY/1ZR1/9lYtX/Y2Db/0pG5/8oIeD/KCDg/yYe + 4f8iG9f/IRvK/y8u5/8tKOP/IBje/yUlSf9mZVv/mqTK/CFL9yIAAAAAAAAAADMuxlpzdN2+Rkjb2j5O + 5v88Uun/ITPp/zg51/+qqcL/vL/G/0k8tP80Ha3/LR/H/xsU5v9VU93/R0Lb/2lo1f95edv/Qj7n/yUe + 4P8lHuD/Jh/h/yUe2f8eF8T/QEHq/0VH6P82M+X/Ly1o/9jXxf9gZ4j/Kj3uRQAAAAAAAAAAa2nhC62x + +DEpMObXIhne/ykg3v8dEt7/iIfF/7i6vf96V1j/aSsf/2goHf9iLTT/Uyhi/2BRrP9FQ9H/VFLM/3Fv + zP8aEdj/Ihrf/yUd4f8mHuH/Jh/h/yMbyP8kJeT/Kjbn/2pu8f9vbd3/Y2Vn/yc2n/s6OOzXKSDgHAAA + AAAAAAAAJx/gDDg45OpCQOb/OTbk/z895P+eocr/nZaZ/14dFv9jHhP/ViU+/zQ/1f9dY+L/TVfW/yg/ + 3f9TY93/eYjd/0xXy/8+Rcv/MDPQ/yYk1v8hGtf/HBXA/yEayP8jPvL/XHf//2Fjj/8LIo3/Hin3dyEX + 3wsmHt8DAAAAAAAAAACKifAliIfw+jYx4/8sJuH/QkHp/4+Tz/6urrb/jHR5/4NgY/9/W1n/eGR/5UpJ + v8AgGuLZJR7i/ycj5P8rK+b/PUPs/0lW7v9NYO7/Y3nu/3mJ5P9BT8P/ICWc/x0cw/80SfD9IiYr/zI2 + fasAAAAAAAAAAAAAAAAAAAAAAAAAAHx+7i58e+7/KyTh/yEZ3/8uKOOQjZfzIoWHlleNkJR8g4eLWnV5 + eydydGsIAAAAACcg3wQlHuFMJR7gqygh4O8uJ+H/MCrh/zEs4v9gZOv/T1jt/0ZV8v87WPL/GDbX+0pT + xNAKCAD4mJWIawAAAAAAAAAAAAAAAAAAAAAAAAAAbHHrKYiH8P8tJuH/Ixzfuigh4QQAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUVDmFGFh6Wc/PeS+REPl+Tk04/8jGt//Ihnf/yMe + 4P8fKO3/Xm7W/yg1ZPNwhNyiW3XoFAAAAAAAAAAAAAAAAAAAAAA6OOMCSkXmlScg4KgkHuAOAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiGt8jIxzgdSYf + 4L8mIOD1Ixvf/xsQ3f9PSOT/TlHp/26B9/85WvLpH0bwbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACIa3xhEQeRlXl3ouUdF5fNoaOv/X1zo/yUh4f8kNOj/I0XvtiNI8CMAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHhbfFyEZ32MgGN+1Jh7g7iYc3/8mIuH/JD3rkgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYf4BQmHuBNJh/gjyYb + 3oMkNOgqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////////////////4f///+ + D///8A///8AH///AB///4AYf/gAMD/4AEAP4AAAD+EAAA/AAAAPwAAAH4AAAA8AAAAMAAAADAAAAAYAA + AAGAAAAPgAgAD4P/AAeH/+AD///8AP///4D////g/////////////////////ygAAAAYAAAAMAAAAAEA + IAAAAAAAYAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkI + KwwFBR5yAwMOWwYGCQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAk5ScHnR0psSFi+f+anbD/iIsYUwAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdHV9Arm9 + 0HWUod3LbYPs/TJT7v4iRu/+I0Pu/iQ048kfONQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADVU6KkjRu//I0bv/yNG7/8jRu/+Iznq/yUi + 4f4hN9sVAAAAAAAAAAABAQoBAQEGAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACM+6yQjQ+72I0bv/yNG7/8jRu/+JC7m/yUm4tAAAAAAAAAAABAOXlEWEoboDw5Y4goP + PTMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwjsxcrK+HNLSzi5yUk4sU6P+TeP07U/ig/ + 3P4fMc3+JCPc+CUl4TwAAAAAGxeeMSEcxPYlH9n+JR7e/iM35udEYOUqAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAEA9ZYgwLR60XFIsVJSDgbCUf4NkoIeD/Ojfk/2px7v9wevD+Jyja6yUp45UkLOFsISHK8yIe + yf4kHdj/JR7f/ypB6/5KU3u/AAAAAAAAAAAAAAAAAAAAAAAAAAAcHVgrIx+y+SMc2/4VJoxwI0LteyNB + 7bVdbdz8KSzi/2Ni1v9RUuj+U1Xp/yUq5P4jRO7/I0bv/yA31/4eMsb/HzLM/1RlyP4xMTG5AAAAAAAA + AAAAAAAAAAAAAAAAAAATFVl/HyTA/yM77f4jRu//I0fv/zJV8P5kfOb/ZHzl/1903v8wSe3+KEbt/ylM + 7/4uUfD/JUPh/yMg1f4jQ+7/JDLn/xkZiv5GRVmFAAAAAAAAAAAAAAAAAAAAACFC4hoePM/OIkbv/iJG + 7/4uS+T+JDHn/kJL6P5ocej+cXrZ/mNu3P5EV+v+Wmvu/kBS7P47TOv+LDfW/iUp5P4lLOX+MCvD/tHR + 1/5qc9KgAAAAAAAAAABAPskvTFLYUjNQ69slR+3/Iz7r/2Bq0P6YmL3/JR7g/y4p4f4mH9//WFXW/19d + 1f9nZt7+OTXi/yUe3/4lHt//IxzO/zU05P41MuP/GBSK/2tra/5mc7HQAAAAAAAAAAB8e+AzXWDonSwo + 3/40MN/+Qj3T/rCxv/6DZWn+YCk1/lkmSf5EI4f+XVfJ/lFO1v5XVNf+JR7f/iUe3/4lHt/+IxzN/igm + 4f48Quf+SUnT/o2Olv40Pa3zPDzjYAAAAAAAAAAASUjmiUNC5f48OeT/a2zV/6Ccpv5lKCT/ZSgk/z87 + sf5PVeH/MT/e/0JP2/9gbtv+SlTS/z5D1v4uMNf/JCDK/x8auv4oQez/ZXPU/xgoffsmN99EJiPHAwAA + AAAAAAAAi4rwnT864/4lHt//U1PfsqGitr+XkJndjoCGs4l+iIJSTr9ZJh/enCUe3/AlHt/+JSTh/zA2 + 5v5qd/D/W2/u/zdKy/4fKMT3OD147jU2TKEAAAAAAAAAAAAAAAAAAAAAjo7woEA65P4lHt+pNC/QAQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACUf2ApAPeNZSEfltEE/5PpBPuT+JR7f/iUn4/4kNej+RU14/Vlp + sqJjedgFAAAAAAAAAAAAAAAAZWPpNC0n4IIkHdsHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACch3RUlHt9mJR7ftSUe3/clHd//UE3i/2t47/4vTO7dI0XuSAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAExK + 4RBhYehcUE/msDUw4vUlH+D/JC3l/SM964AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQd2Q0lHt9QJR7feCQm + 4i8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP// + /wD/4f8A/8H/AP4A/wD/AM8A/wGHAPgBAwDwAAMA4AADAOAAAwDAAAMAAAADAAAAAQCAAAEAgAAHAIfg + AwCP/AEA//+AAP//8AD///8A////AP///wAoAAAAEAAAACAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMi81LDIxVJ4ODgw5AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACFhok9goy7m2B14O9Laf//Kj7KyB88 + vQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOFjkVh5G+f8aQfT/HkHw/yUo + 7PcjPOwUBg4uAgMDEUsAAAAVAAAAAAAAAAAAAAAAAAAAAAAAAAATJ44GKTf4jiIt6ZoyQeLoMk7j/yQ3 + 3f8lJueFIRi9BBYQfaAlHNj/FR+30Zmn2RwAAAAAAAAAAAAAAAAHBhwvEQpfvxkbmkAmKu2oNjPg/lpZ + 4/9eX+T/JirjviIy5bMjLNb/IB7Q/zNC5f9UW2mDAAAAAAAAAAAAAAAAExNbniMv6v8cQubiK1P072B4 + 5P9YaOD/O0zq/zJP7/8qUvH/IjTZ/yE87f8lLab/npusZAAAAAAjHMELIjrRQB5D4OouUe7/QlTk/ywx + 8P9gZeb/Z27b/1Ng6P88Rur/Mjjh/ygp3v8hIdv/dXGV/1Zm228AAAAAXl3cWDpB5+woL+j/hofF/4Bg + c/8/GXj/Rje7/11d1P9STtj/HhTd/x4T2f8nI9v/QUTm/21ulP8/SsK6IiXpCWVj6hNJRufxPDnl/5iV + tf57Sjv/Zkts70RFzOI0OuH/Rk3e/zxF3v9IT9//KCzD/zVG1v04QX3jOzfbLycc3wN+g+4jV1Po/ygi + 6JF9g7slenx3NXZ2ZxFDPq0FHxXnPz045Zs9O+XoR0jo/yw05/8sOcD9W2J+twAAAAAAAAAALirhBi0m + 4UcsJuoDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJyDgDSEZ304rI+GfRUDk5lle7f8oP+3cIUbvRgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAERC5QsiGt9IJRrenCUu + 5YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP//AAP//wAD/j/nFvgf5xb4A4AU4AEAAMABAADAAQAAAAEAAAAAAAAAAAAAAAMAAB+A + AAP/8HMf//8AAP//AAQ= + + + \ No newline at end of file diff --git a/AirScout/MapProviderDlg.Designer.cs b/AirScout/MapProviderDlg.Designer.cs new file mode 100644 index 0000000..189a72e --- /dev/null +++ b/AirScout/MapProviderDlg.Designer.cs @@ -0,0 +1,92 @@ +namespace AirScout +{ + partial class MapProviderDlg + { + /// + /// Erforderliche Designervariable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Verwendete Ressourcen bereinigen. + /// + /// True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Vom Windows Form-Designer generierter Code + + /// + /// Erforderliche Methode für die Designerunterstützung. + /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MapProviderDlg)); + this.richTextBox1 = new System.Windows.Forms.RichTextBox(); + this.btn_MapProviderDlg_Decline = new System.Windows.Forms.Button(); + this.btn_MapProviderDlg_Accept = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // richTextBox1 + // + this.richTextBox1.BackColor = System.Drawing.Color.White; + this.richTextBox1.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.richTextBox1.Location = new System.Drawing.Point(12, 12); + this.richTextBox1.Name = "richTextBox1"; + this.richTextBox1.ReadOnly = true; + this.richTextBox1.ScrollBars = System.Windows.Forms.RichTextBoxScrollBars.None; + this.richTextBox1.ShortcutsEnabled = false; + this.richTextBox1.Size = new System.Drawing.Size(454, 210); + this.richTextBox1.TabIndex = 15; + this.richTextBox1.Text = resources.GetString("richTextBox1.Text"); + // + // btn_MapProviderDlg_Decline + // + this.btn_MapProviderDlg_Decline.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btn_MapProviderDlg_Decline.Location = new System.Drawing.Point(246, 258); + this.btn_MapProviderDlg_Decline.Name = "btn_MapProviderDlg_Decline"; + this.btn_MapProviderDlg_Decline.Size = new System.Drawing.Size(75, 23); + this.btn_MapProviderDlg_Decline.TabIndex = 17; + this.btn_MapProviderDlg_Decline.Text = "&Decline"; + this.btn_MapProviderDlg_Decline.UseVisualStyleBackColor = true; + // + // btn_MapProviderDlg_Accept + // + this.btn_MapProviderDlg_Accept.DialogResult = System.Windows.Forms.DialogResult.OK; + this.btn_MapProviderDlg_Accept.Location = new System.Drawing.Point(148, 258); + this.btn_MapProviderDlg_Accept.Name = "btn_MapProviderDlg_Accept"; + this.btn_MapProviderDlg_Accept.Size = new System.Drawing.Size(75, 23); + this.btn_MapProviderDlg_Accept.TabIndex = 16; + this.btn_MapProviderDlg_Accept.Text = "&Accept"; + this.btn_MapProviderDlg_Accept.UseVisualStyleBackColor = true; + // + // MapProviderDlg + // + this.AcceptButton = this.btn_MapProviderDlg_Accept; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btn_MapProviderDlg_Decline; + this.ClientSize = new System.Drawing.Size(478, 293); + this.Controls.Add(this.btn_MapProviderDlg_Decline); + this.Controls.Add(this.btn_MapProviderDlg_Accept); + this.Controls.Add(this.richTextBox1); + this.Name = "MapProviderDlg"; + this.Text = "Map Provider - Term of Use"; + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.RichTextBox richTextBox1; + private System.Windows.Forms.Button btn_MapProviderDlg_Decline; + private System.Windows.Forms.Button btn_MapProviderDlg_Accept; + } +} \ No newline at end of file diff --git a/AirScout/MapProviderDlg.cs b/AirScout/MapProviderDlg.cs new file mode 100644 index 0000000..d656d80 --- /dev/null +++ b/AirScout/MapProviderDlg.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace AirScout +{ + public partial class MapProviderDlg : Form + { + public MapProviderDlg() + { + InitializeComponent(); + } + } +} diff --git a/AirScout/MapProviderDlg.resx b/AirScout/MapProviderDlg.resx new file mode 100644 index 0000000..3477c38 --- /dev/null +++ b/AirScout/MapProviderDlg.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The map selection is part of the Great Maps Open Source library +(see http://greatmaps.codeplex.com/). + +WARNING: This software can access different map providers and may violate their Terms of Service. + +By clicking on Accept you understand that you are + + DOING THAT AT YOUR OWN RISK. + +The auhor of this software will not be responsible in any case. + + \ No newline at end of file diff --git a/AirScout/MapStationDlg.Designer.cs b/AirScout/MapStationDlg.Designer.cs new file mode 100644 index 0000000..be4241c --- /dev/null +++ b/AirScout/MapStationDlg.Designer.cs @@ -0,0 +1,332 @@ +namespace AirScout +{ + partial class MapStationDlg + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MapStationDlg)); + this.label19 = new System.Windows.Forms.Label(); + this.label18 = new System.Windows.Forms.Label(); + this.tb_Zoom = new System.Windows.Forms.TextBox(); + this.btn_Zoom_Out = new System.Windows.Forms.Button(); + this.btn_Zoom_In = new System.Windows.Forms.Button(); + this.label11 = new System.Windows.Forms.Label(); + this.label10 = new System.Windows.Forms.Label(); + this.label9 = new System.Windows.Forms.Label(); + this.label8 = new System.Windows.Forms.Label(); + this.gm_Callsign = new GMap.NET.WindowsForms.GMapControl(); + this.tb_Callsign = new ScoutBase.Core.CallsignTextBox(); + this.tb_Locator = new ScoutBase.Core.LocatorTextBox(); + this.tb_Longitude = new ScoutBase.Core.DoubleTextBox(); + this.tb_Latitude = new ScoutBase.Core.DoubleTextBox(); + this.btn_Cancel = new System.Windows.Forms.Button(); + this.btn_OK = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.tb_Elevation = new ScoutBase.Core.DoubleTextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.SuspendLayout(); + // + // label19 + // + this.label19.AutoSize = true; + this.label19.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label19.Location = new System.Drawing.Point(12, 9); + this.label19.Name = "label19"; + this.label19.Size = new System.Drawing.Size(702, 60); + this.label19.TabIndex = 43; + this.label19.Text = resources.GetString("label19.Text"); + // + // label18 + // + this.label18.AutoSize = true; + this.label18.Location = new System.Drawing.Point(801, 352); + this.label18.Name = "label18"; + this.label18.Size = new System.Drawing.Size(61, 13); + this.label18.TabIndex = 42; + this.label18.Text = "Map Zoom:"; + // + // tb_Zoom + // + this.tb_Zoom.BackColor = System.Drawing.Color.FloralWhite; + this.tb_Zoom.Location = new System.Drawing.Point(914, 346); + this.tb_Zoom.Name = "tb_Zoom"; + this.tb_Zoom.Size = new System.Drawing.Size(30, 20); + this.tb_Zoom.TabIndex = 41; + // + // btn_Zoom_Out + // + this.btn_Zoom_Out.Location = new System.Drawing.Point(952, 345); + this.btn_Zoom_Out.Name = "btn_Zoom_Out"; + this.btn_Zoom_Out.Size = new System.Drawing.Size(30, 25); + this.btn_Zoom_Out.TabIndex = 40; + this.btn_Zoom_Out.Text = "-"; + this.btn_Zoom_Out.UseVisualStyleBackColor = true; + this.btn_Zoom_Out.Click += new System.EventHandler(this.btn_Zoom_Out_Click); + // + // btn_Zoom_In + // + this.btn_Zoom_In.Location = new System.Drawing.Point(875, 345); + this.btn_Zoom_In.Name = "btn_Zoom_In"; + this.btn_Zoom_In.Size = new System.Drawing.Size(30, 25); + this.btn_Zoom_In.TabIndex = 39; + this.btn_Zoom_In.Text = "+"; + this.btn_Zoom_In.UseVisualStyleBackColor = true; + this.btn_Zoom_In.Click += new System.EventHandler(this.btn_Zoom_In_Click); + // + // label11 + // + this.label11.AutoSize = true; + this.label11.Location = new System.Drawing.Point(801, 144); + this.label11.Name = "label11"; + this.label11.Size = new System.Drawing.Size(46, 13); + this.label11.TabIndex = 37; + this.label11.Text = "Locator:"; + // + // label10 + // + this.label10.AutoSize = true; + this.label10.Location = new System.Drawing.Point(801, 201); + this.label10.Name = "label10"; + this.label10.Size = new System.Drawing.Size(57, 13); + this.label10.TabIndex = 36; + this.label10.Text = "Longitude:"; + // + // label9 + // + this.label9.AutoSize = true; + this.label9.Location = new System.Drawing.Point(801, 172); + this.label9.Name = "label9"; + this.label9.Size = new System.Drawing.Size(48, 13); + this.label9.TabIndex = 34; + this.label9.Text = "Latitude:"; + // + // label8 + // + this.label8.AutoSize = true; + this.label8.Location = new System.Drawing.Point(801, 117); + this.label8.Name = "label8"; + this.label8.Size = new System.Drawing.Size(46, 13); + this.label8.TabIndex = 31; + this.label8.Text = "Callsign:"; + // + // gm_Callsign + // + this.gm_Callsign.Bearing = 0F; + this.gm_Callsign.CanDragMap = true; + this.gm_Callsign.EmptyTileColor = System.Drawing.Color.Navy; + this.gm_Callsign.GrayScaleMode = false; + this.gm_Callsign.HelperLineOption = GMap.NET.WindowsForms.HelperLineOptions.DontShow; + this.gm_Callsign.LevelsKeepInMemmory = 5; + this.gm_Callsign.Location = new System.Drawing.Point(15, 87); + this.gm_Callsign.MarkersEnabled = true; + this.gm_Callsign.MaxZoom = 2; + this.gm_Callsign.MinZoom = 2; + this.gm_Callsign.MouseWheelZoomType = GMap.NET.MouseWheelZoomType.MousePositionAndCenter; + this.gm_Callsign.Name = "gm_Callsign"; + this.gm_Callsign.NegativeMode = false; + this.gm_Callsign.PolygonsEnabled = true; + this.gm_Callsign.RetryLoadTile = 0; + this.gm_Callsign.RoutesEnabled = true; + this.gm_Callsign.SelectedAreaFillColor = System.Drawing.Color.FromArgb(((int)(((byte)(33)))), ((int)(((byte)(65)))), ((int)(((byte)(105)))), ((int)(((byte)(225))))); + this.gm_Callsign.ShowTileGridLines = false; + this.gm_Callsign.Size = new System.Drawing.Size(760, 621); + this.gm_Callsign.TabIndex = 29; + this.gm_Callsign.Zoom = 0D; + this.gm_Callsign.OnMarkerEnter += new GMap.NET.WindowsForms.MarkerEnter(this.gm_Callsign_OnMarkerEnter); + this.gm_Callsign.OnMapZoomChanged += new GMap.NET.MapZoomChanged(this.gm_Callsign_OnMapZoomChanged); + this.gm_Callsign.MouseDown += new System.Windows.Forms.MouseEventHandler(this.gm_Callsign_MouseDown); + this.gm_Callsign.MouseMove += new System.Windows.Forms.MouseEventHandler(this.gm_Callsign_MouseMove); + this.gm_Callsign.MouseUp += new System.Windows.Forms.MouseEventHandler(this.gm_Callsign_MouseUp); + // + // tb_Callsign + // + this.tb_Callsign.BackColor = System.Drawing.SystemColors.Control; + this.tb_Callsign.CharacterCasing = System.Windows.Forms.CharacterCasing.Upper; + this.tb_Callsign.ErrorBackColor = System.Drawing.Color.Red; + this.tb_Callsign.ErrorForeColor = System.Drawing.Color.White; + this.tb_Callsign.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Callsign.ForeColor = System.Drawing.SystemColors.WindowText; + this.tb_Callsign.Location = new System.Drawing.Point(875, 113); + this.tb_Callsign.Name = "tb_Callsign"; + this.tb_Callsign.ReadOnly = true; + this.tb_Callsign.Size = new System.Drawing.Size(107, 21); + this.tb_Callsign.TabIndex = 30; + // + // tb_Locator + // + this.tb_Locator.DataBindings.Add(new System.Windows.Forms.Binding("SmallLettersForSubsquares", global::AirScout.Properties.Settings.Default, "Locator_SmallLettersForSubsquares", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Locator.ErrorBackColor = System.Drawing.Color.Red; + this.tb_Locator.ErrorForeColor = System.Drawing.Color.White; + this.tb_Locator.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Locator.Location = new System.Drawing.Point(875, 140); + this.tb_Locator.Name = "tb_Locator"; + this.tb_Locator.Size = new System.Drawing.Size(107, 21); + this.tb_Locator.SmallLettersForSubsquares = global::AirScout.Properties.Settings.Default.Locator_SmallLettersForSubsquares; + this.tb_Locator.TabIndex = 35; + this.tb_Locator.TextChanged += new System.EventHandler(this.tb_Locator_TextChanged); + // + // tb_Longitude + // + this.tb_Longitude.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Longitude.FormatSpecifier = ""; + this.tb_Longitude.Location = new System.Drawing.Point(875, 197); + this.tb_Longitude.MaxValue = 180D; + this.tb_Longitude.MinValue = -180D; + this.tb_Longitude.Name = "tb_Longitude"; + this.tb_Longitude.Size = new System.Drawing.Size(107, 21); + this.tb_Longitude.TabIndex = 33; + this.tb_Longitude.Text = "10.68327000"; + this.tb_Longitude.Value = 10.68327D; + this.tb_Longitude.TextChanged += new System.EventHandler(this.tb_Longitude_TextChanged); + // + // tb_Latitude + // + this.tb_Latitude.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Latitude.FormatSpecifier = ""; + this.tb_Latitude.Location = new System.Drawing.Point(875, 167); + this.tb_Latitude.MaxValue = 90D; + this.tb_Latitude.MinValue = -90D; + this.tb_Latitude.Name = "tb_Latitude"; + this.tb_Latitude.Size = new System.Drawing.Size(107, 21); + this.tb_Latitude.TabIndex = 32; + this.tb_Latitude.Text = "50.93706700"; + this.tb_Latitude.Value = 50.937067D; + this.tb_Latitude.TextChanged += new System.EventHandler(this.tb_Latitude_TextChanged); + // + // btn_Cancel + // + this.btn_Cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btn_Cancel.Location = new System.Drawing.Point(806, 685); + this.btn_Cancel.Name = "btn_Cancel"; + this.btn_Cancel.Size = new System.Drawing.Size(75, 23); + this.btn_Cancel.TabIndex = 44; + this.btn_Cancel.Text = "Cancel"; + this.btn_Cancel.UseVisualStyleBackColor = true; + this.btn_Cancel.Click += new System.EventHandler(this.btn_Cancel_Click); + // + // btn_OK + // + this.btn_OK.DialogResult = System.Windows.Forms.DialogResult.OK; + this.btn_OK.Location = new System.Drawing.Point(907, 685); + this.btn_OK.Name = "btn_OK"; + this.btn_OK.Size = new System.Drawing.Size(75, 23); + this.btn_OK.TabIndex = 45; + this.btn_OK.Text = "OK"; + this.btn_OK.UseVisualStyleBackColor = true; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(801, 228); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(54, 13); + this.label1.TabIndex = 47; + this.label1.Text = "Elevation:"; + // + // tb_Elevation + // + this.tb_Elevation.BackColor = System.Drawing.SystemColors.Control; + this.tb_Elevation.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Elevation.FormatSpecifier = "F0"; + this.tb_Elevation.Location = new System.Drawing.Point(875, 223); + this.tb_Elevation.MaxValue = 0D; + this.tb_Elevation.MinValue = 0D; + this.tb_Elevation.Name = "tb_Elevation"; + this.tb_Elevation.ReadOnly = true; + this.tb_Elevation.Size = new System.Drawing.Size(53, 21); + this.tb_Elevation.TabIndex = 48; + this.tb_Elevation.Text = "0"; + this.tb_Elevation.Value = 0D; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(938, 227); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(31, 13); + this.label2.TabIndex = 49; + this.label2.Text = "m asl"; + // + // MapStationDlg + // + this.AcceptButton = this.btn_OK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btn_Cancel; + this.ClientSize = new System.Drawing.Size(1008, 729); + this.Controls.Add(this.label2); + this.Controls.Add(this.tb_Elevation); + this.Controls.Add(this.label1); + this.Controls.Add(this.btn_OK); + this.Controls.Add(this.btn_Cancel); + this.Controls.Add(this.label19); + this.Controls.Add(this.label18); + this.Controls.Add(this.tb_Zoom); + this.Controls.Add(this.btn_Zoom_Out); + this.Controls.Add(this.btn_Zoom_In); + this.Controls.Add(this.label11); + this.Controls.Add(this.label10); + this.Controls.Add(this.label9); + this.Controls.Add(this.label8); + this.Controls.Add(this.gm_Callsign); + this.Controls.Add(this.tb_Callsign); + this.Controls.Add(this.tb_Locator); + this.Controls.Add(this.tb_Longitude); + this.Controls.Add(this.tb_Latitude); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "MapStationDlg"; + this.Text = "MapStationDlg"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label label19; + private System.Windows.Forms.Label label18; + private System.Windows.Forms.TextBox tb_Zoom; + private System.Windows.Forms.Button btn_Zoom_Out; + private System.Windows.Forms.Button btn_Zoom_In; + private System.Windows.Forms.Label label11; + private System.Windows.Forms.Label label10; + private System.Windows.Forms.Label label9; + private System.Windows.Forms.Label label8; + private GMap.NET.WindowsForms.GMapControl gm_Callsign; + private ScoutBase.Core.CallsignTextBox tb_Callsign; + private ScoutBase.Core.LocatorTextBox tb_Locator; + private ScoutBase.Core.DoubleTextBox tb_Longitude; + private ScoutBase.Core.DoubleTextBox tb_Latitude; + private System.Windows.Forms.Button btn_Cancel; + private System.Windows.Forms.Button btn_OK; + private System.Windows.Forms.Label label1; + private ScoutBase.Core.DoubleTextBox tb_Elevation; + private System.Windows.Forms.Label label2; + } +} \ No newline at end of file diff --git a/AirScout/MapStationDlg.cs b/AirScout/MapStationDlg.cs new file mode 100644 index 0000000..4d64b29 --- /dev/null +++ b/AirScout/MapStationDlg.cs @@ -0,0 +1,269 @@ +using GMap.NET; +using GMap.NET.WindowsForms; +using GMap.NET.WindowsForms.Markers; +using ScoutBase; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using ScoutBase.Core; +using ScoutBase.Stations; +using ScoutBase.Elevation; + +namespace AirScout +{ + public partial class MapStationDlg : Form + { + + GMapOverlay Callsignpolygons = new GMapOverlay("Callsignpolygons"); + GMapOverlay Callsignspositions = new GMapOverlay("Callsignpositions"); + + GMarkerGoogle UserPos = new GMarkerGoogle(new PointLatLng(0.0, 0.0), GMarkerGoogleType.red_dot); + + private bool IsDraggingMarker = false; + + public LocationDesignator StationLocation; + + public MapStationDlg(LocationDesignator ld) + { + StationLocation = ld; + InitializeComponent(); + + // set initial settings for user details + GMap.NET.MapProviders.GMapProvider.UserAgent = "AirScout"; + gm_Callsign.MapProvider = GMap.NET.MapProviders.GMapProviders.Find(Properties.Settings.Default.Map_Provider); + gm_Callsign.IgnoreMarkerOnMouseWheel = true; + gm_Callsign.MinZoom = 0; + gm_Callsign.MaxZoom = 20; + gm_Callsign.Zoom = 1; + gm_Callsign.DragButton = System.Windows.Forms.MouseButtons.Left; + gm_Callsign.CanDragMap = true; + gm_Callsign.ScalePen = new Pen(Color.Black, 3); + gm_Callsign.HelperLinePen = null; + gm_Callsign.SelectionPen = null; + gm_Callsign.MapScaleInfoEnabled = true; + gm_Callsign.Overlays.Add(Callsignpolygons); + gm_Callsign.Overlays.Add(Callsignspositions); + Callsignspositions.Markers.Add(UserPos); + // initially set textboxes + tb_Callsign.SilentText = StationLocation.Call; + tb_Latitude.SilentValue = StationLocation.Lat; + tb_Longitude.SilentValue = StationLocation.Lon; + tb_Locator.SilentText = MaidenheadLocator.LocFromLatLon(StationLocation.Lat, StationLocation.Lon, Properties.Settings.Default.Locator_SmallLettersForSubsquares, (int)Properties.Settings.Default.Locator_MaxLength / 2, Properties.Settings.Default.Locator_AutoLength); + tb_Elevation.SilentValue = GetElevation(StationLocation.Lat, StationLocation.Lon); + ValidateDetails(); + // show initial zoom level in text box + gm_Callsign_OnMapZoomChanged(); + + } + + private void btn_Cancel_Click(object sender, EventArgs e) + { + + } + + public int GetElevation(double lat, double lon) + { + int elv = ElevationData.Database.ElvMissingFlag; + // try to get elevation data from distinct elevation model + // start with detailed one + if (Properties.Settings.Default.Elevation_SRTM1_Enabled && (elv == ElevationData.Database.ElvMissingFlag)) + elv = ElevationData.Database[lat, lon, ELEVATIONMODEL.SRTM1, false]; + if (Properties.Settings.Default.Elevation_SRTM3_Enabled && (elv == ElevationData.Database.ElvMissingFlag)) + elv = ElevationData.Database[lat, lon, ELEVATIONMODEL.SRTM3, false]; + if (Properties.Settings.Default.Elevation_GLOBE_Enabled && (elv == ElevationData.Database.ElvMissingFlag)) + elv = ElevationData.Database[lat, lon, ELEVATIONMODEL.GLOBE, false]; + // set it to zero if still invalid + if (elv == ElevationData.Database.ElvMissingFlag) + elv = 0; + + return elv; + } + + + private bool ValidateDetails() + { + // validates user details and sets position on map + // enables/disables next button + double mlat, mlon; + // colour Textbox if more precise lat/lon information is available + if (MaidenheadLocator.IsPrecise(tb_Latitude.Value, tb_Longitude.Value, 3)) + { + if (tb_Locator.BackColor != Color.PaleGreen) + tb_Locator.BackColor = Color.PaleGreen; + } + else + { + if (tb_Locator.BackColor != Color.FloralWhite) + tb_Locator.BackColor = Color.FloralWhite; + } + if (GeographicalPoint.Check(tb_Latitude.Value, tb_Longitude.Value)) + { + // update locator text if not focusd + if (!tb_Locator.Focused) + { + tb_Locator.SilentText = MaidenheadLocator.LocFromLatLon(tb_Latitude.Value, tb_Longitude.Value, Properties.Settings.Default.Locator_SmallLettersForSubsquares, (int)Properties.Settings.Default.Locator_MaxLength / 2, true); + } + // get locator polygon + Callsignpolygons.Clear(); + List l = new List(); + // add loc bounds to map polygons + MaidenheadLocator.LatLonFromLoc(tb_Locator.Text, PositionInRectangle.TopLeft, out mlat, out mlon); + l.Add(new PointLatLng(mlat, mlon)); + MaidenheadLocator.LatLonFromLoc(tb_Locator.Text, PositionInRectangle.TopRight, out mlat, out mlon); + l.Add(new PointLatLng(mlat, mlon)); + MaidenheadLocator.LatLonFromLoc(tb_Locator.Text, PositionInRectangle.BottomRight, out mlat, out mlon); + l.Add(new PointLatLng(mlat, mlon)); + MaidenheadLocator.LatLonFromLoc(tb_Locator.Text, PositionInRectangle.BottomLeft, out mlat, out mlon); + l.Add(new PointLatLng(mlat, mlon)); + GMapPolygon p = new GMapPolygon(l, tb_Locator.Text.ToString()); + p.Stroke = new Pen(Color.FromArgb(255, Color.Magenta), 3); + p.Fill = new SolidBrush(Color.FromArgb(0, Color.Magenta)); + Callsignpolygons.Polygons.Add(p); + // update user position + UserPos.Position = new PointLatLng(tb_Latitude.Value, tb_Longitude.Value); + // update map position + if (!IsDraggingMarker) + { + string loc = MaidenheadLocator.LocFromLatLon(tb_Latitude.Value, tb_Longitude.Value, Properties.Settings.Default.Locator_SmallLettersForSubsquares, (int)Properties.Settings.Default.Locator_MaxLength / 2, true); + MaidenheadLocator.LatLonFromLoc(loc, PositionInRectangle.MiddleMiddle, out mlat, out mlon); + gm_Callsign.Position = new PointLatLng(mlat, mlon); + // adjust map zoom level + int zoom = loc.Length; + switch (zoom) + { + case 6: + gm_Callsign.Zoom = 12; + break; + case 8: + gm_Callsign.Zoom = 15; + break; + case 10: + gm_Callsign.Zoom = 17; + break; + } + } + } + // check all values + if (Callsign.Check(tb_Callsign.Text) && MaidenheadLocator.Check(tb_Locator.Text) && !double.IsNaN(tb_Latitude.Value) && !double.IsNaN(tb_Longitude.Value)) + { + StationLocation.Lat = tb_Latitude.Value; + StationLocation.Lon = tb_Longitude.Value; + StationLocation.Source = MaidenheadLocator.IsPrecise(tb_Latitude.Value, tb_Longitude.Value, 3) ? GEOSOURCE.FROMUSER : GEOSOURCE.FROMLOC; + StationLocation.Loc = MaidenheadLocator.LocFromLatLon(StationLocation.Lat, StationLocation.Lon, false, 3); + tb_Elevation.SilentValue = GetElevation(StationLocation.Lat, StationLocation.Lon); + return true; + } + else + { + return false; + } + } + + private void tb_Callsign_TextChanged(object sender, EventArgs e) + { + } + + private void tb_Latitude_TextChanged(object sender, EventArgs e) + { + ValidateDetails(); + } + + private void tb_Longitude_TextChanged(object sender, EventArgs e) + { + ValidateDetails(); + } + + private void tb_Locator_TextChanged(object sender, EventArgs e) + { + // update lat/lon + double mlat, mlon; + if (tb_Locator.Focused) + { + // locator box is focused --> update lat/lon + if (MaidenheadLocator.Check(tb_Locator.Text) && tb_Locator.Text.Length >= 6) + { + MaidenheadLocator.LatLonFromLoc(tb_Locator.Text, PositionInRectangle.MiddleMiddle, out mlat, out mlon); + tb_Latitude.SilentValue = mlat; + tb_Longitude.SilentValue = mlon; + } + else + { + tb_Latitude.SilentValue = double.NaN; + tb_Longitude.SilentValue = double.NaN; + } + } + ValidateDetails(); + } + + private void gm_Callsign_MouseDown(object sender, MouseEventArgs e) + { + if (e.Button == MouseButtons.Left && (UserPos != null && UserPos.IsMouseOver)) + { + // dummy set user position to set mouse position exact to marker's location + UserPos.Position = UserPos.Position; + gm_Callsign.CanDragMap = false; + IsDraggingMarker = true; + } + } + + private void gm_Callsign_MouseMove(object sender, MouseEventArgs e) + { + if ((UserPos != null) && IsDraggingMarker) + { + if (Callsign.Check(tb_Callsign.Text)) + { + // get geographic coordinates of mouse pointer + PointLatLng p = gm_Callsign.FromLocalToLatLng(e.X, e.Y); + tb_Latitude.SilentValue = p.Lat; + tb_Longitude.SilentValue = p.Lng; + UserPos.ToolTipMode = MarkerTooltipMode.OnMouseOver; + UserPos.ToolTipText = tb_Callsign.Text; + StationLocation.Lat = p.Lat; + StationLocation.Lon = p.Lng; + ValidateDetails(); + } + else + { + UserPos.ToolTipMode = MarkerTooltipMode.OnMouseOver; + UserPos.ToolTipText = "Please enter a valid callsign first."; + } + } + } + + private void gm_Callsign_MouseUp(object sender, MouseEventArgs e) + { + if (IsDraggingMarker) + { + gm_Callsign.CanDragMap = true; + IsDraggingMarker = false; + } + } + + private void gm_Callsign_OnMarkerEnter(GMapMarker item) + { + } + + private void btn_Zoom_In_Click(object sender, EventArgs e) + { + if (gm_Callsign.Zoom < 20) + gm_Callsign.Zoom++; + } + + private void btn_Zoom_Out_Click(object sender, EventArgs e) + { + if (gm_Callsign.Zoom > 0) + gm_Callsign.Zoom--; + } + + private void gm_Callsign_OnMapZoomChanged() + { + // maintain zoom level + tb_Zoom.Text = gm_Callsign.Zoom.ToString(); + } + } +} diff --git a/AirScout/MapStationDlg.resx b/AirScout/MapStationDlg.resx new file mode 100644 index 0000000..55bd07d --- /dev/null +++ b/AirScout/MapStationDlg.resx @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Update station details here. Exact lat/long values are essential for calculation of the radio horizon. +Use the lat/lon textboxes for numeric input or enter a valid Maidenhead Locator. +Drag the needle on the map for exact location. Use mouse wheel or +/- buttons to zoom in and out. + + \ No newline at end of file diff --git a/AirScout/OptionsDlg.Designer.cs b/AirScout/OptionsDlg.Designer.cs new file mode 100644 index 0000000..4049a2b --- /dev/null +++ b/AirScout/OptionsDlg.Designer.cs @@ -0,0 +1,5853 @@ +namespace AirScout +{ + partial class OptionsDlg + { + /// + /// Erforderliche Designervariable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Verwendete Ressourcen bereinigen. + /// + /// True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Vom Windows Form-Designer generierter Code + + /// + /// Erforderliche Methode für die Designerunterstützung. + /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(OptionsDlg)); + this.btn_Options_OK = new System.Windows.Forms.Button(); + this.btn_Options_Cancel = new System.Windows.Forms.Button(); + this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); + this.btn_Open_LogDirectory = new System.Windows.Forms.Button(); + this.btn_Open_TmpDirectory = new System.Windows.Forms.Button(); + this.btn_Options_Import_Callsigns = new System.Windows.Forms.Button(); + this.btn_Options_MyUpdate = new System.Windows.Forms.Button(); + this.btn_Options_DXUpdate = new System.Windows.Forms.Button(); + this.pb_Donate = new System.Windows.Forms.PictureBox(); + this.btn_Options_DeleteAllElevationPaths = new System.Windows.Forms.Button(); + this.btn_Options_DeleteAllPropagationPaths = new System.Windows.Forms.Button(); + this.cb_Options_Path_BestCaseElevation = new System.Windows.Forms.CheckBox(); + this.tab_Options_Planes = new System.Windows.Forms.TabPage(); + this.groupBox40 = new System.Windows.Forms.GroupBox(); + this.label55 = new System.Windows.Forms.Label(); + this.ud_Options_Planes_Position_DatabaseLifetime = new System.Windows.Forms.NumericUpDown(); + this.label53 = new System.Windows.Forms.Label(); + this.label32 = new System.Windows.Forms.Label(); + this.groupBox38 = new System.Windows.Forms.GroupBox(); + this.tb_Options_Planes_Positions_TTL = new ScoutBase.Core.Int32TextBox(); + this.tb_Options_Planes_MaxAlt = new ScoutBase.Core.Int32TextBox(); + this.tb_Options_Planes_MinAlt = new ScoutBase.Core.Int32TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.label1 = new System.Windows.Forms.Label(); + this.label9 = new System.Windows.Forms.Label(); + this.label8 = new System.Windows.Forms.Label(); + this.label7 = new System.Windows.Forms.Label(); + this.label5 = new System.Windows.Forms.Label(); + this.groupBox37 = new System.Windows.Forms.GroupBox(); + this.label22 = new System.Windows.Forms.Label(); + this.tb_Options_Planes_MaxLat = new System.Windows.Forms.TextBox(); + this.label37 = new System.Windows.Forms.Label(); + this.tb_Options_Planes_MinLat = new System.Windows.Forms.TextBox(); + this.label38 = new System.Windows.Forms.Label(); + this.tb_Options_Planes_MaxLon = new System.Windows.Forms.TextBox(); + this.label33 = new System.Windows.Forms.Label(); + this.tb_Options_Planes_MinLon = new System.Windows.Forms.TextBox(); + this.label34 = new System.Windows.Forms.Label(); + this.groupBox26 = new System.Windows.Forms.GroupBox(); + this.tb_Options_Planes_Filter_MinAlt = new ScoutBase.Core.Int32TextBox(); + this.tb_Options_Planes_Filter_Max_Circumcircle = new ScoutBase.Core.Int32TextBox(); + this.label96 = new System.Windows.Forms.Label(); + this.label94 = new System.Windows.Forms.Label(); + this.label95 = new System.Windows.Forms.Label(); + this.label92 = new System.Windows.Forms.Label(); + this.label93 = new System.Windows.Forms.Label(); + this.label91 = new System.Windows.Forms.Label(); + this.cb_Options_Planes_Filter_Min_Cat = new System.Windows.Forms.ComboBox(); + this.groupBox6 = new System.Windows.Forms.GroupBox(); + this.btn_Options_PlaneFeed3_Export = new System.Windows.Forms.Button(); + this.btn_Options_PlaneFeed2_Export = new System.Windows.Forms.Button(); + this.btn_Options_PlaneFeed1_Export = new System.Windows.Forms.Button(); + this.btn_Options_PlaneFeed3_Import = new System.Windows.Forms.Button(); + this.btn_Options_PlaneFeed2_Import = new System.Windows.Forms.Button(); + this.btn_Options_PlaneFeed1_Import = new System.Windows.Forms.Button(); + this.label79 = new System.Windows.Forms.Label(); + this.btn_Options_PlaneFeed3_Settings = new System.Windows.Forms.Button(); + this.cb_Options_PlaneFeed3 = new System.Windows.Forms.ComboBox(); + this.label78 = new System.Windows.Forms.Label(); + this.btn_Options_PlaneFeed2_Settings = new System.Windows.Forms.Button(); + this.cb_Options_PlaneFeed2 = new System.Windows.Forms.ComboBox(); + this.label77 = new System.Windows.Forms.Label(); + this.btn_Options_PlaneFeed1_Settings = new System.Windows.Forms.Button(); + this.cb_Options_PlaneFeed1 = new System.Windows.Forms.ComboBox(); + this.tab_Options_Path = new System.Windows.Forms.TabPage(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.tb_Options_Path_MaxLength = new ScoutBase.Core.DoubleTextBox(); + this.label131 = new System.Windows.Forms.Label(); + this.label130 = new System.Windows.Forms.Label(); + this.tb_Options_Path_StepWidth = new System.Windows.Forms.TextBox(); + this.label129 = new System.Windows.Forms.Label(); + this.dgv_BandSettings = new System.Windows.Forms.DataGridView(); + this.label57 = new System.Windows.Forms.Label(); + this.tab_Options_SRTM1 = new System.Windows.Forms.TabPage(); + this.groupBox43 = new System.Windows.Forms.GroupBox(); + this.label111 = new System.Windows.Forms.Label(); + this.label112 = new System.Windows.Forms.Label(); + this.btn_Options_SRTM1_Copyright = new System.Windows.Forms.Button(); + this.groupBox13 = new System.Windows.Forms.GroupBox(); + this.gm_Options_SRTM1 = new GMap.NET.WindowsForms.GMapControl(); + this.groupBox12 = new System.Windows.Forms.GroupBox(); + this.cb_Options_Elevation_SRTM1_EnableCache = new System.Windows.Forms.CheckBox(); + this.cb_Options_Elevation_SRTM1 = new System.Windows.Forms.CheckBox(); + this.tab_Options_SRTM3 = new System.Windows.Forms.TabPage(); + this.groupBox42 = new System.Windows.Forms.GroupBox(); + this.label100 = new System.Windows.Forms.Label(); + this.label110 = new System.Windows.Forms.Label(); + this.btn_Options_SRTM3_Copyright = new System.Windows.Forms.Button(); + this.groupBox9 = new System.Windows.Forms.GroupBox(); + this.gm_Options_SRTM3 = new GMap.NET.WindowsForms.GMapControl(); + this.groupBox8 = new System.Windows.Forms.GroupBox(); + this.cb_Options_Elevation_SRTM3_EnableCache = new System.Windows.Forms.CheckBox(); + this.cb_Options_Elevation_SRTM3 = new System.Windows.Forms.CheckBox(); + this.tab_Options_GLOBE = new System.Windows.Forms.TabPage(); + this.groupBox41 = new System.Windows.Forms.GroupBox(); + this.label99 = new System.Windows.Forms.Label(); + this.label98 = new System.Windows.Forms.Label(); + this.btn_Options_GLOBE_Copyright = new System.Windows.Forms.Button(); + this.groupBox11 = new System.Windows.Forms.GroupBox(); + this.gm_Options_GLOBE = new GMap.NET.WindowsForms.GMapControl(); + this.groupBox10 = new System.Windows.Forms.GroupBox(); + this.cb_Options_Elevation_GLOBE_EnableCache = new System.Windows.Forms.CheckBox(); + this.cb_Options_Elevation_GLOBE = new System.Windows.Forms.CheckBox(); + this.tab_Options_Map = new System.Windows.Forms.TabPage(); + this.groupBox39 = new System.Windows.Forms.GroupBox(); + this.tb_Options_Map_Update_Interval = new ScoutBase.Core.Int32TextBox(); + this.label97 = new System.Windows.Forms.Label(); + this.label29 = new System.Windows.Forms.Label(); + this.groupBox23 = new System.Windows.Forms.GroupBox(); + this.cb_Options_Map_SmallMarkers = new System.Windows.Forms.CheckBox(); + this.cb_Options_Watchlist_Activate = new System.Windows.Forms.CheckBox(); + this.cb_Options_Airports_Activate = new System.Windows.Forms.CheckBox(); + this.groupBox30 = new System.Windows.Forms.GroupBox(); + this.tb_Options_Planes_IconSize_S = new ScoutBase.Core.Int32TextBox(); + this.tb_Options_Planes_IconSize_H = new ScoutBase.Core.Int32TextBox(); + this.tb_Options_Planes_IconSize_M = new ScoutBase.Core.Int32TextBox(); + this.tb_Options_Planes_IconSize_L = new ScoutBase.Core.Int32TextBox(); + this.label84 = new System.Windows.Forms.Label(); + this.label83 = new System.Windows.Forms.Label(); + this.label82 = new System.Windows.Forms.Label(); + this.label81 = new System.Windows.Forms.Label(); + this.label80 = new System.Windows.Forms.Label(); + this.groupBox7 = new System.Windows.Forms.GroupBox(); + this.groupBox29 = new System.Windows.Forms.GroupBox(); + this.label76 = new System.Windows.Forms.Label(); + this.label74 = new System.Windows.Forms.Label(); + this.label75 = new System.Windows.Forms.Label(); + this.cb_Options_InfoWin_Angle = new System.Windows.Forms.CheckBox(); + this.cb_Options_InfoWin_Speed = new System.Windows.Forms.CheckBox(); + this.cb_Options_InfoWin_Squint = new System.Windows.Forms.CheckBox(); + this.cb_Options_InfoWin_Epsilon = new System.Windows.Forms.CheckBox(); + this.cb_Options_InfoWin_Dist = new System.Windows.Forms.CheckBox(); + this.cb_Options_InfoWin_Time = new System.Windows.Forms.CheckBox(); + this.cb_Options_InfoWin_Type = new System.Windows.Forms.CheckBox(); + this.cb_Options_InfoWin_Track = new System.Windows.Forms.CheckBox(); + this.cb_Options_InfoWin_Alt = new System.Windows.Forms.CheckBox(); + this.cb_Options_InfoWin_Position = new System.Windows.Forms.CheckBox(); + this.groupBox22 = new System.Windows.Forms.GroupBox(); + this.rb_Options_InfoWin_Imperial = new System.Windows.Forms.RadioButton(); + this.rb_Options_InfoWin_Metric = new System.Windows.Forms.RadioButton(); + this.label72 = new System.Windows.Forms.Label(); + this.btn_Options_SelectFont = new System.Windows.Forms.Button(); + this.label62 = new System.Windows.Forms.Label(); + this.tb_Options_Map_ToolTipFont = new System.Windows.Forms.TextBox(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.label61 = new System.Windows.Forms.Label(); + this.cb_Options_Map_Provider = new System.Windows.Forms.ComboBox(); + this.tab_Options_Stations = new System.Windows.Forms.TabPage(); + this.groupBox18 = new System.Windows.Forms.GroupBox(); + this.lbl_Options_LocalObstructions = new System.Windows.Forms.Label(); + this.btn_Options_LocalObstructions = new System.Windows.Forms.Button(); + this.groupBox46 = new System.Windows.Forms.GroupBox(); + this.lbl_Options_DXLastUpdated = new System.Windows.Forms.Label(); + this.label128 = new System.Windows.Forms.Label(); + this.tb_Options_DXPower = new ScoutBase.Core.DoubleTextBox(); + this.tb_Options_DXAntennaGain = new ScoutBase.Core.DoubleTextBox(); + this.tb_Options_DXAntennaHeight = new ScoutBase.Core.DoubleTextBox(); + this.label13 = new System.Windows.Forms.Label(); + this.label58 = new System.Windows.Forms.Label(); + this.label107 = new System.Windows.Forms.Label(); + this.label124 = new System.Windows.Forms.Label(); + this.label125 = new System.Windows.Forms.Label(); + this.label126 = new System.Windows.Forms.Label(); + this.groupBox45 = new System.Windows.Forms.GroupBox(); + this.btn_Options_BandDown = new System.Windows.Forms.Button(); + this.btn_Options_BandUp = new System.Windows.Forms.Button(); + this.tb_Options_Band = new System.Windows.Forms.TextBox(); + this.groupBox44 = new System.Windows.Forms.GroupBox(); + this.lbl_Options_MyLastUpdated = new System.Windows.Forms.Label(); + this.label127 = new System.Windows.Forms.Label(); + this.tb_Options_MyPower = new ScoutBase.Core.DoubleTextBox(); + this.tb_Options_MyAntennaGain = new ScoutBase.Core.DoubleTextBox(); + this.tb_Options_MyAntennaHeight = new ScoutBase.Core.DoubleTextBox(); + this.label122 = new System.Windows.Forms.Label(); + this.label123 = new System.Windows.Forms.Label(); + this.label106 = new System.Windows.Forms.Label(); + this.label121 = new System.Windows.Forms.Label(); + this.label28 = new System.Windows.Forms.Label(); + this.label41 = new System.Windows.Forms.Label(); + this.groupBox14 = new System.Windows.Forms.GroupBox(); + this.cb_Options_SmallLettersForSubSquares = new System.Windows.Forms.CheckBox(); + this.cb_Options_Locator_AutoLength = new System.Windows.Forms.CheckBox(); + this.label48 = new System.Windows.Forms.Label(); + this.ud_Options_Locator_MaxLength = new System.Windows.Forms.NumericUpDown(); + this.groupBox16 = new System.Windows.Forms.GroupBox(); + this.label52 = new System.Windows.Forms.Label(); + this.groupBox5 = new System.Windows.Forms.GroupBox(); + this.btn_Options_DXMap = new System.Windows.Forms.Button(); + this.tb_Options_DXLon = new ScoutBase.Core.DoubleTextBox(); + this.tb_Options_DXLat = new ScoutBase.Core.DoubleTextBox(); + this.tb_Options_DXLoc = new ScoutBase.Core.LocatorTextBox(); + this.tb_Options_DXCall = new ScoutBase.Core.CallsignTextBox(); + this.label21 = new System.Windows.Forms.Label(); + this.btn_Options_DXHorizon = new System.Windows.Forms.Button(); + this.btn_DXCall_QRZ = new System.Windows.Forms.Button(); + this.label14 = new System.Windows.Forms.Label(); + this.label15 = new System.Windows.Forms.Label(); + this.label42 = new System.Windows.Forms.Label(); + this.tb_Options_DXElevation = new System.Windows.Forms.TextBox(); + this.label43 = new System.Windows.Forms.Label(); + this.label44 = new System.Windows.Forms.Label(); + this.groupBox4 = new System.Windows.Forms.GroupBox(); + this.btn_Options_MyMap = new System.Windows.Forms.Button(); + this.btn_Options_MyHorizon = new System.Windows.Forms.Button(); + this.tb_Options_MyLon = new ScoutBase.Core.DoubleTextBox(); + this.tb_Options_MyLat = new ScoutBase.Core.DoubleTextBox(); + this.tb_Options_MyLoc = new ScoutBase.Core.LocatorTextBox(); + this.tb_Options_MyCall = new ScoutBase.Core.CallsignTextBox(); + this.label18 = new System.Windows.Forms.Label(); + this.btn_MyCall_QRZ = new System.Windows.Forms.Button(); + this.label40 = new System.Windows.Forms.Label(); + this.label39 = new System.Windows.Forms.Label(); + this.label10 = new System.Windows.Forms.Label(); + this.tb_Options_MyElevation = new System.Windows.Forms.TextBox(); + this.label12 = new System.Windows.Forms.Label(); + this.label11 = new System.Windows.Forms.Label(); + this.tab_Options_General = new System.Windows.Forms.TabPage(); + this.groupBox25 = new System.Windows.Forms.GroupBox(); + this.tb_Coverage_MaxLat = new ScoutBase.Core.DoubleTextBox(); + this.tb_Coverage_MinLat = new ScoutBase.Core.DoubleTextBox(); + this.tb_Coverage_MaxLon = new ScoutBase.Core.DoubleTextBox(); + this.tb_Coverage_MinLon = new ScoutBase.Core.DoubleTextBox(); + this.gm_Options_Coverage = new GMap.NET.WindowsForms.GMapControl(); + this.label35 = new System.Windows.Forms.Label(); + this.label54 = new System.Windows.Forms.Label(); + this.label59 = new System.Windows.Forms.Label(); + this.label60 = new System.Windows.Forms.Label(); + this.groupBox17 = new System.Windows.Forms.GroupBox(); + this.cb_Options_Watchlist_SyncWithKST = new System.Windows.Forms.CheckBox(); + this.tb_Options_Watchlist_MaxCount = new ScoutBase.Core.Int32TextBox(); + this.btn_Options_Watchlist_Manage = new System.Windows.Forms.Button(); + this.label31 = new System.Windows.Forms.Label(); + this.tc_Options = new System.Windows.Forms.TabControl(); + this.tab_Options_Database = new System.Windows.Forms.TabPage(); + this.groupBox47 = new System.Windows.Forms.GroupBox(); + this.groupBox27 = new System.Windows.Forms.GroupBox(); + this.label108 = new System.Windows.Forms.Label(); + this.groupBox15 = new System.Windows.Forms.GroupBox(); + this.label105 = new System.Windows.Forms.Label(); + this.gb_Options_Database_Settings = new System.Windows.Forms.GroupBox(); + this.cb_Options_Background_Calculations_Enable = new System.Windows.Forms.CheckBox(); + this.label47 = new System.Windows.Forms.Label(); + this.ud_Options_Database_Update_Period = new System.Windows.Forms.NumericUpDown(); + this.rb_Options_Database_Update_Periodically = new System.Windows.Forms.RadioButton(); + this.rb_Options_Database_Update_OnStartup = new System.Windows.Forms.RadioButton(); + this.rb_Options_Database_Update_Never = new System.Windows.Forms.RadioButton(); + this.gb_Options_Database_Info = new System.Windows.Forms.GroupBox(); + this.btn_Options_Elevation_SRTM1_Database_Maintenance = new System.Windows.Forms.Button(); + this.btn_Options_Elevation_SRTM3_Database_Maintenance = new System.Windows.Forms.Button(); + this.btn_Options_Elevation_GLOBE_Database_Maintenance = new System.Windows.Forms.Button(); + this.btn_Options_Propagation_SRTM1_Database_Maintenance = new System.Windows.Forms.Button(); + this.btn_Options_Propagation_SRTM3_Database_Maintenance = new System.Windows.Forms.Button(); + this.btn_Options_Propagation_GLOBE_Database_Maintenance = new System.Windows.Forms.Button(); + this.btn_Options_AirScout_Database_Maintenance = new System.Windows.Forms.Button(); + this.btn_Options_ScoutBase_Database_Maintenance = new System.Windows.Forms.Button(); + this.label141 = new System.Windows.Forms.Label(); + this.lbl_Options_Database_TotalSize = new System.Windows.Forms.Label(); + this.label139 = new System.Windows.Forms.Label(); + this.label136 = new System.Windows.Forms.Label(); + this.tb_Options_Propagation_SRTM1_Database_FileSize = new System.Windows.Forms.TextBox(); + this.label137 = new System.Windows.Forms.Label(); + this.tb_Options_Propagation_SRTM1_Database_FileName = new System.Windows.Forms.TextBox(); + this.label138 = new System.Windows.Forms.Label(); + this.label46 = new System.Windows.Forms.Label(); + this.tb_Options_Propagation_SRTM3_Database_FileSize = new System.Windows.Forms.TextBox(); + this.label134 = new System.Windows.Forms.Label(); + this.tb_Options_Propagation_SRTM3_Database_FileName = new System.Windows.Forms.TextBox(); + this.label135 = new System.Windows.Forms.Label(); + this.label132 = new System.Windows.Forms.Label(); + this.tb_Options_Propagation_GLOBE_Database_FileSize = new System.Windows.Forms.TextBox(); + this.label133 = new System.Windows.Forms.Label(); + this.tb_Options_Propagation_GLOBE_Database_FileName = new System.Windows.Forms.TextBox(); + this.label109 = new System.Windows.Forms.Label(); + this.label118 = new System.Windows.Forms.Label(); + this.tb_Options_Elevation_SRTM1_Database_FileSize = new System.Windows.Forms.TextBox(); + this.label119 = new System.Windows.Forms.Label(); + this.tb_Options_Elevation_SRTM1_Database_FileName = new System.Windows.Forms.TextBox(); + this.label120 = new System.Windows.Forms.Label(); + this.label115 = new System.Windows.Forms.Label(); + this.tb_Options_Elevation_SRTM3_Database_FileSize = new System.Windows.Forms.TextBox(); + this.label116 = new System.Windows.Forms.Label(); + this.tb_Options_Elevation_SRTM3_Database_FileName = new System.Windows.Forms.TextBox(); + this.label117 = new System.Windows.Forms.Label(); + this.label16 = new System.Windows.Forms.Label(); + this.tb_Options_Elevation_GLOBE_Database_FileSize = new System.Windows.Forms.TextBox(); + this.label113 = new System.Windows.Forms.Label(); + this.tb_Options_Elevation_GLOBE_Database_FileName = new System.Windows.Forms.TextBox(); + this.label114 = new System.Windows.Forms.Label(); + this.label104 = new System.Windows.Forms.Label(); + this.label101 = new System.Windows.Forms.Label(); + this.tb_Options_AirScout_Database_FileSize = new System.Windows.Forms.TextBox(); + this.label102 = new System.Windows.Forms.Label(); + this.tb_Options_AirScout_Database_FileName = new System.Windows.Forms.TextBox(); + this.label103 = new System.Windows.Forms.Label(); + this.label73 = new System.Windows.Forms.Label(); + this.tb_Options_ScoutBase_Database_FileSize = new System.Windows.Forms.TextBox(); + this.label50 = new System.Windows.Forms.Label(); + this.tb_Options_ScoutBase_Database_FileName = new System.Windows.Forms.TextBox(); + this.label49 = new System.Windows.Forms.Label(); + this.tab_Options_Alarm = new System.Windows.Forms.TabPage(); + this.groupBox21 = new System.Windows.Forms.GroupBox(); + this.cb_Options_Alarm_Activate = new System.Windows.Forms.CheckBox(); + this.groupBox19 = new System.Windows.Forms.GroupBox(); + this.cb_Options_Alarm_PlaySound = new System.Windows.Forms.CheckBox(); + this.cb_Options_Alarm_BringWindowToFront = new System.Windows.Forms.CheckBox(); + this.groupBox20 = new System.Windows.Forms.GroupBox(); + this.tb_Options_Alarm_Distance = new ScoutBase.Core.DoubleTextBox(); + this.label36 = new System.Windows.Forms.Label(); + this.label56 = new System.Windows.Forms.Label(); + this.tab_Options_Network = new System.Windows.Forms.TabPage(); + this.label86 = new System.Windows.Forms.Label(); + this.groupBox32 = new System.Windows.Forms.GroupBox(); + this.tb_Options_Webserver_Port = new ScoutBase.Core.Int32TextBox(); + this.label85 = new System.Windows.Forms.Label(); + this.groupBox31 = new System.Windows.Forms.GroupBox(); + this.cb_Options_Server_Activate = new System.Windows.Forms.CheckBox(); + this.label3 = new System.Windows.Forms.Label(); + this.groupBox24 = new System.Windows.Forms.GroupBox(); + this.tb_Options_Server_Port = new ScoutBase.Core.Int32TextBox(); + this.label6 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.tb_Options_Server_Name = new System.Windows.Forms.TextBox(); + this.tab_Options_SpecLab = new System.Windows.Forms.TabPage(); + this.groupBox3 = new System.Windows.Forms.GroupBox(); + this.tb_Options_SpecLab_UpdateInterval = new ScoutBase.Core.DoubleTextBox(); + this.tb_Options_SpecLab_F2 = new ScoutBase.Core.Int32TextBox(); + this.tb_Options_SpecLab_F1 = new ScoutBase.Core.Int32TextBox(); + this.label70 = new System.Windows.Forms.Label(); + this.label71 = new System.Windows.Forms.Label(); + this.label68 = new System.Windows.Forms.Label(); + this.label69 = new System.Windows.Forms.Label(); + this.label67 = new System.Windows.Forms.Label(); + this.label66 = new System.Windows.Forms.Label(); + this.tb_SpecLab_FileName = new System.Windows.Forms.TextBox(); + this.label63 = new System.Windows.Forms.Label(); + this.label64 = new System.Windows.Forms.Label(); + this.label65 = new System.Windows.Forms.Label(); + this.tb_SpecLab_URL = new System.Windows.Forms.TextBox(); + this.cb_SpecLab_Enabled = new System.Windows.Forms.CheckBox(); + this.tc_Track = new System.Windows.Forms.TabPage(); + this.groupBox36 = new System.Windows.Forms.GroupBox(); + this.rb_Options_Track_File_None = new System.Windows.Forms.RadioButton(); + this.rb_Options_Track_File_WSJT = new System.Windows.Forms.RadioButton(); + this.rb_Options_Track_File_Native = new System.Windows.Forms.RadioButton(); + this.groupBox35 = new System.Windows.Forms.GroupBox(); + this.rb_Options_Track_DDE_None = new System.Windows.Forms.RadioButton(); + this.rb_Options_Track_DDE_HRD = new System.Windows.Forms.RadioButton(); + this.groupBox34 = new System.Windows.Forms.GroupBox(); + this.tb_Options_Track_UDP_AirScout_Port = new ScoutBase.Core.Int32TextBox(); + this.tb_Options_Track_UDP_WinTest_Port = new ScoutBase.Core.Int32TextBox(); + this.label90 = new System.Windows.Forms.Label(); + this.label89 = new System.Windows.Forms.Label(); + this.rb_Options_Track_UDP_None = new System.Windows.Forms.RadioButton(); + this.rb_Options_Track_UDP_AirScout = new System.Windows.Forms.RadioButton(); + this.rb_Options_Track_UDP_WinTest = new System.Windows.Forms.RadioButton(); + this.groupBox33 = new System.Windows.Forms.GroupBox(); + this.tb_Options_Track_Serial_Baudrate = new ScoutBase.Core.Int32TextBox(); + this.rb_Options_Track_Serial_None = new System.Windows.Forms.RadioButton(); + this.label88 = new System.Windows.Forms.Label(); + this.label87 = new System.Windows.Forms.Label(); + this.tb_Options_Track_Serial_Port = new System.Windows.Forms.TextBox(); + this.rb_Options_Track_Serial_GS232_AZEL = new System.Windows.Forms.RadioButton(); + this.rb_Options_Track_Serial_GS232_AZ = new System.Windows.Forms.RadioButton(); + this.groupBox28 = new System.Windows.Forms.GroupBox(); + this.cb_Options_Track_Activate = new System.Windows.Forms.CheckBox(); + this.tab_Options_Info = new System.Windows.Forms.TabPage(); + this.label30 = new System.Windows.Forms.Label(); + this.label45 = new System.Windows.Forms.Label(); + this.lbl_Options_Elevation_SRTM1 = new System.Windows.Forms.LinkLabel(); + this.label17 = new System.Windows.Forms.Label(); + this.lbl_Options_Elevation_SRTM3 = new System.Windows.Forms.LinkLabel(); + this.lbl_Options_Elevation_GLOBE = new System.Windows.Forms.LinkLabel(); + this.lbl_Options_Spherical = new System.Windows.Forms.LinkLabel(); + this.label51 = new System.Windows.Forms.Label(); + this.label25 = new System.Windows.Forms.Label(); + this.lbl_Options_Map = new System.Windows.Forms.LinkLabel(); + this.label27 = new System.Windows.Forms.Label(); + this.label26 = new System.Windows.Forms.Label(); + this.label24 = new System.Windows.Forms.Label(); + this.label23 = new System.Windows.Forms.Label(); + this.lbl_Options_Version = new System.Windows.Forms.Label(); + this.label20 = new System.Windows.Forms.Label(); + this.label19 = new System.Windows.Forms.Label(); + this.btn_Options_License = new System.Windows.Forms.Button(); + this.bw_SRTM3_MapUpdater = new System.ComponentModel.BackgroundWorker(); + this.ss_Options = new System.Windows.Forms.StatusStrip(); + this.tsl_Options_Status = new System.Windows.Forms.ToolStripStatusLabel(); + this.bw_SRTM1_MapUpdater = new System.ComponentModel.BackgroundWorker(); + this.bw_GLOBE_MapUpdater = new System.ComponentModel.BackgroundWorker(); + this.bw_SFTP = new System.ComponentModel.BackgroundWorker(); + this.elevationDatabaseUpdater1 = new ScoutBase.Elevation.ElevationDatabaseUpdater(); + this.cb_Options_Planes_KeepHistory = new System.Windows.Forms.CheckBox(); + ((System.ComponentModel.ISupportInitialize)(this.pb_Donate)).BeginInit(); + this.tab_Options_Planes.SuspendLayout(); + this.groupBox40.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.ud_Options_Planes_Position_DatabaseLifetime)).BeginInit(); + this.groupBox38.SuspendLayout(); + this.groupBox37.SuspendLayout(); + this.groupBox26.SuspendLayout(); + this.groupBox6.SuspendLayout(); + this.tab_Options_Path.SuspendLayout(); + this.groupBox1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.dgv_BandSettings)).BeginInit(); + this.tab_Options_SRTM1.SuspendLayout(); + this.groupBox43.SuspendLayout(); + this.groupBox13.SuspendLayout(); + this.groupBox12.SuspendLayout(); + this.tab_Options_SRTM3.SuspendLayout(); + this.groupBox42.SuspendLayout(); + this.groupBox9.SuspendLayout(); + this.groupBox8.SuspendLayout(); + this.tab_Options_GLOBE.SuspendLayout(); + this.groupBox41.SuspendLayout(); + this.groupBox11.SuspendLayout(); + this.groupBox10.SuspendLayout(); + this.tab_Options_Map.SuspendLayout(); + this.groupBox39.SuspendLayout(); + this.groupBox23.SuspendLayout(); + this.groupBox30.SuspendLayout(); + this.groupBox7.SuspendLayout(); + this.groupBox29.SuspendLayout(); + this.groupBox22.SuspendLayout(); + this.groupBox2.SuspendLayout(); + this.tab_Options_Stations.SuspendLayout(); + this.groupBox18.SuspendLayout(); + this.groupBox46.SuspendLayout(); + this.groupBox45.SuspendLayout(); + this.groupBox44.SuspendLayout(); + this.groupBox14.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.ud_Options_Locator_MaxLength)).BeginInit(); + this.groupBox16.SuspendLayout(); + this.groupBox5.SuspendLayout(); + this.groupBox4.SuspendLayout(); + this.tab_Options_General.SuspendLayout(); + this.groupBox25.SuspendLayout(); + this.groupBox17.SuspendLayout(); + this.tc_Options.SuspendLayout(); + this.tab_Options_Database.SuspendLayout(); + this.groupBox47.SuspendLayout(); + this.groupBox27.SuspendLayout(); + this.groupBox15.SuspendLayout(); + this.gb_Options_Database_Settings.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.ud_Options_Database_Update_Period)).BeginInit(); + this.gb_Options_Database_Info.SuspendLayout(); + this.tab_Options_Alarm.SuspendLayout(); + this.groupBox21.SuspendLayout(); + this.groupBox19.SuspendLayout(); + this.groupBox20.SuspendLayout(); + this.tab_Options_Network.SuspendLayout(); + this.groupBox32.SuspendLayout(); + this.groupBox31.SuspendLayout(); + this.groupBox24.SuspendLayout(); + this.tab_Options_SpecLab.SuspendLayout(); + this.groupBox3.SuspendLayout(); + this.tc_Track.SuspendLayout(); + this.groupBox36.SuspendLayout(); + this.groupBox35.SuspendLayout(); + this.groupBox34.SuspendLayout(); + this.groupBox33.SuspendLayout(); + this.groupBox28.SuspendLayout(); + this.tab_Options_Info.SuspendLayout(); + this.ss_Options.SuspendLayout(); + this.SuspendLayout(); + // + // btn_Options_OK + // + this.btn_Options_OK.DialogResult = System.Windows.Forms.DialogResult.OK; + this.btn_Options_OK.Location = new System.Drawing.Point(697, 443); + this.btn_Options_OK.Name = "btn_Options_OK"; + this.btn_Options_OK.Size = new System.Drawing.Size(75, 23); + this.btn_Options_OK.TabIndex = 26; + this.btn_Options_OK.Text = "&OK"; + this.btn_Options_OK.UseVisualStyleBackColor = true; + // + // btn_Options_Cancel + // + this.btn_Options_Cancel.CausesValidation = false; + this.btn_Options_Cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btn_Options_Cancel.Location = new System.Drawing.Point(697, 472); + this.btn_Options_Cancel.Name = "btn_Options_Cancel"; + this.btn_Options_Cancel.Size = new System.Drawing.Size(75, 23); + this.btn_Options_Cancel.TabIndex = 25; + this.btn_Options_Cancel.Text = "&Cancel"; + this.btn_Options_Cancel.UseVisualStyleBackColor = true; + // + // btn_Open_LogDirectory + // + this.btn_Open_LogDirectory.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Open_LogDirectory.Location = new System.Drawing.Point(13, 50); + this.btn_Open_LogDirectory.Name = "btn_Open_LogDirectory"; + this.btn_Open_LogDirectory.Size = new System.Drawing.Size(119, 27); + this.btn_Open_LogDirectory.TabIndex = 1; + this.btn_Open_LogDirectory.Text = "Log Directory"; + this.toolTip1.SetToolTip(this.btn_Open_LogDirectory, "Click to see your logfiles in Explorer window."); + this.btn_Open_LogDirectory.UseVisualStyleBackColor = true; + this.btn_Open_LogDirectory.Click += new System.EventHandler(this.btn_Open_LogDirectory_Click); + // + // btn_Open_TmpDirectory + // + this.btn_Open_TmpDirectory.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Open_TmpDirectory.Location = new System.Drawing.Point(138, 50); + this.btn_Open_TmpDirectory.Name = "btn_Open_TmpDirectory"; + this.btn_Open_TmpDirectory.Size = new System.Drawing.Size(119, 27); + this.btn_Open_TmpDirectory.TabIndex = 2; + this.btn_Open_TmpDirectory.Text = "Tmp Directory"; + this.toolTip1.SetToolTip(this.btn_Open_TmpDirectory, "Click to see your temp files in Explorer window. "); + this.btn_Open_TmpDirectory.UseVisualStyleBackColor = true; + this.btn_Open_TmpDirectory.Click += new System.EventHandler(this.btn_Open_TmpDirectory_Click); + // + // btn_Options_Import_Callsigns + // + this.btn_Options_Import_Callsigns.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Options_Import_Callsigns.Location = new System.Drawing.Point(244, 19); + this.btn_Options_Import_Callsigns.Name = "btn_Options_Import_Callsigns"; + this.btn_Options_Import_Callsigns.Size = new System.Drawing.Size(85, 27); + this.btn_Options_Import_Callsigns.TabIndex = 1; + this.btn_Options_Import_Callsigns.Text = "Select File"; + this.toolTip1.SetToolTip(this.btn_Options_Import_Callsigns, "Click to see your ScoutBase database files in Explorer window."); + this.btn_Options_Import_Callsigns.UseVisualStyleBackColor = true; + this.btn_Options_Import_Callsigns.Click += new System.EventHandler(this.btn_Options_Import_Callsigns_Click); + // + // btn_Options_MyUpdate + // + this.btn_Options_MyUpdate.BackColor = System.Drawing.Color.PaleGreen; + this.btn_Options_MyUpdate.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Options_MyUpdate.Location = new System.Drawing.Point(89, 183); + this.btn_Options_MyUpdate.Name = "btn_Options_MyUpdate"; + this.btn_Options_MyUpdate.Size = new System.Drawing.Size(100, 33); + this.btn_Options_MyUpdate.TabIndex = 23; + this.btn_Options_MyUpdate.Text = "Send Update!"; + this.toolTip1.SetToolTip(this.btn_Options_MyUpdate, "Send Update to AirScout web database"); + this.btn_Options_MyUpdate.UseVisualStyleBackColor = false; + this.btn_Options_MyUpdate.Click += new System.EventHandler(this.btn_Options_MyUpdate_Click); + // + // btn_Options_DXUpdate + // + this.btn_Options_DXUpdate.BackColor = System.Drawing.Color.PaleGreen; + this.btn_Options_DXUpdate.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Options_DXUpdate.Location = new System.Drawing.Point(90, 183); + this.btn_Options_DXUpdate.Name = "btn_Options_DXUpdate"; + this.btn_Options_DXUpdate.Size = new System.Drawing.Size(100, 33); + this.btn_Options_DXUpdate.TabIndex = 28; + this.btn_Options_DXUpdate.Text = "Send Update!"; + this.toolTip1.SetToolTip(this.btn_Options_DXUpdate, "Send Update to AirScout web database"); + this.btn_Options_DXUpdate.UseVisualStyleBackColor = false; + this.btn_Options_DXUpdate.Click += new System.EventHandler(this.btn_Options_DXUpdate_Click); + // + // pb_Donate + // + this.pb_Donate.Image = ((System.Drawing.Image)(resources.GetObject("pb_Donate.Image"))); + this.pb_Donate.InitialImage = ((System.Drawing.Image)(resources.GetObject("pb_Donate.InitialImage"))); + this.pb_Donate.Location = new System.Drawing.Point(227, 368); + this.pb_Donate.Name = "pb_Donate"; + this.pb_Donate.Size = new System.Drawing.Size(180, 80); + this.pb_Donate.TabIndex = 37; + this.pb_Donate.TabStop = false; + this.toolTip1.SetToolTip(this.pb_Donate, "Click here to open a browser window with link."); + this.pb_Donate.Click += new System.EventHandler(this.pb_Donate_Click); + // + // btn_Options_DeleteAllElevationPaths + // + this.btn_Options_DeleteAllElevationPaths.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Options_DeleteAllElevationPaths.Location = new System.Drawing.Point(15, 19); + this.btn_Options_DeleteAllElevationPaths.Name = "btn_Options_DeleteAllElevationPaths"; + this.btn_Options_DeleteAllElevationPaths.Size = new System.Drawing.Size(266, 23); + this.btn_Options_DeleteAllElevationPaths.TabIndex = 0; + this.btn_Options_DeleteAllElevationPaths.Text = "Delete All Elevation Paths"; + this.toolTip1.SetToolTip(this.btn_Options_DeleteAllElevationPaths, "Deletes all precalculated elevation paths from database"); + this.btn_Options_DeleteAllElevationPaths.UseVisualStyleBackColor = true; + this.btn_Options_DeleteAllElevationPaths.Click += new System.EventHandler(this.btn_Options_DeleteAllElevationPaths_Click); + // + // btn_Options_DeleteAllPropagationPaths + // + this.btn_Options_DeleteAllPropagationPaths.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Options_DeleteAllPropagationPaths.Location = new System.Drawing.Point(15, 48); + this.btn_Options_DeleteAllPropagationPaths.Name = "btn_Options_DeleteAllPropagationPaths"; + this.btn_Options_DeleteAllPropagationPaths.Size = new System.Drawing.Size(266, 23); + this.btn_Options_DeleteAllPropagationPaths.TabIndex = 1; + this.btn_Options_DeleteAllPropagationPaths.Text = "Delete All Propagation Paths"; + this.toolTip1.SetToolTip(this.btn_Options_DeleteAllPropagationPaths, "Deletes all precalculated propagation paths from database"); + this.btn_Options_DeleteAllPropagationPaths.UseVisualStyleBackColor = true; + this.btn_Options_DeleteAllPropagationPaths.Click += new System.EventHandler(this.btn_Options_DeleteAllPropagationPaths_Click); + // + // cb_Options_Path_BestCaseElevation + // + this.cb_Options_Path_BestCaseElevation.AutoSize = true; + this.cb_Options_Path_BestCaseElevation.Checked = global::AirScout.Properties.Settings.Default.Path_BestCaseElevation; + this.cb_Options_Path_BestCaseElevation.CheckState = System.Windows.Forms.CheckState.Checked; + this.cb_Options_Path_BestCaseElevation.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Path_BestCaseElevation", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Options_Path_BestCaseElevation.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Options_Path_BestCaseElevation.Location = new System.Drawing.Point(6, 411); + this.cb_Options_Path_BestCaseElevation.Name = "cb_Options_Path_BestCaseElevation"; + this.cb_Options_Path_BestCaseElevation.Size = new System.Drawing.Size(438, 17); + this.cb_Options_Path_BestCaseElevation.TabIndex = 28; + this.cb_Options_Path_BestCaseElevation.Tag = ""; + this.cb_Options_Path_BestCaseElevation.Text = "Use best case elevation from grid square for both stations if precise location is" + + " unknown"; + this.toolTip1.SetToolTip(this.cb_Options_Path_BestCaseElevation, "If an exact position of a station is not available, use highest available elevati" + + "on within the given grid square. \r\nThis position is ONLY used for path calculati" + + "on and is not kept in database."); + this.cb_Options_Path_BestCaseElevation.UseVisualStyleBackColor = true; + // + // tab_Options_Planes + // + this.tab_Options_Planes.BackColor = System.Drawing.SystemColors.Control; + this.tab_Options_Planes.Controls.Add(this.groupBox40); + this.tab_Options_Planes.Controls.Add(this.groupBox38); + this.tab_Options_Planes.Controls.Add(this.groupBox37); + this.tab_Options_Planes.Controls.Add(this.groupBox26); + this.tab_Options_Planes.Controls.Add(this.groupBox6); + this.tab_Options_Planes.Location = new System.Drawing.Point(4, 22); + this.tab_Options_Planes.Name = "tab_Options_Planes"; + this.tab_Options_Planes.Size = new System.Drawing.Size(671, 460); + this.tab_Options_Planes.TabIndex = 4; + this.tab_Options_Planes.Text = "Planes"; + this.tab_Options_Planes.Enter += new System.EventHandler(this.tab_Options_Planes_Enter); + this.tab_Options_Planes.Validating += new System.ComponentModel.CancelEventHandler(this.tab_Options_Planes_Validating); + // + // groupBox40 + // + this.groupBox40.Controls.Add(this.cb_Options_Planes_KeepHistory); + this.groupBox40.Controls.Add(this.label55); + this.groupBox40.Controls.Add(this.ud_Options_Planes_Position_DatabaseLifetime); + this.groupBox40.Controls.Add(this.label53); + this.groupBox40.Controls.Add(this.label32); + this.groupBox40.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox40.Location = new System.Drawing.Point(12, 305); + this.groupBox40.Name = "groupBox40"; + this.groupBox40.Size = new System.Drawing.Size(291, 141); + this.groupBox40.TabIndex = 38; + this.groupBox40.TabStop = false; + this.groupBox40.Text = "Database Maintenance"; + // + // label55 + // + this.label55.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label55.Location = new System.Drawing.Point(10, 55); + this.label55.Name = "label55"; + this.label55.Size = new System.Drawing.Size(275, 33); + this.label55.TabIndex = 3; + this.label55.Text = "Aircraft positions older than above entered value will be deleted on next startup" + + " (0 = forever)."; + // + // ud_Options_Planes_Position_DatabaseLifetime + // + this.ud_Options_Planes_Position_DatabaseLifetime.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScout.Properties.Settings.Default, "AircraftDatabase_MaxDaysLifetime", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.ud_Options_Planes_Position_DatabaseLifetime.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.ud_Options_Planes_Position_DatabaseLifetime.Location = new System.Drawing.Point(198, 15); + this.ud_Options_Planes_Position_DatabaseLifetime.Maximum = new decimal(new int[] { + 365, + 0, + 0, + 0}); + this.ud_Options_Planes_Position_DatabaseLifetime.Minimum = new decimal(new int[] { + 1, + 0, + 0, + 0}); + this.ud_Options_Planes_Position_DatabaseLifetime.Name = "ud_Options_Planes_Position_DatabaseLifetime"; + this.ud_Options_Planes_Position_DatabaseLifetime.Size = new System.Drawing.Size(49, 22); + this.ud_Options_Planes_Position_DatabaseLifetime.TabIndex = 2; + this.ud_Options_Planes_Position_DatabaseLifetime.Value = global::AirScout.Properties.Settings.Default.AircraftDatabase_MaxDaysLifetime; + // + // label53 + // + this.label53.AutoSize = true; + this.label53.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label53.Location = new System.Drawing.Point(253, 24); + this.label53.Name = "label53"; + this.label53.Size = new System.Drawing.Size(32, 13); + this.label53.TabIndex = 1; + this.label53.Text = "days."; + // + // label32 + // + this.label32.AutoSize = true; + this.label32.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label32.Location = new System.Drawing.Point(9, 22); + this.label32.Name = "label32"; + this.label32.Size = new System.Drawing.Size(129, 13); + this.label32.TabIndex = 0; + this.label32.Text = "Keep aircraft positions for:"; + // + // groupBox38 + // + this.groupBox38.Controls.Add(this.tb_Options_Planes_Positions_TTL); + this.groupBox38.Controls.Add(this.tb_Options_Planes_MaxAlt); + this.groupBox38.Controls.Add(this.tb_Options_Planes_MinAlt); + this.groupBox38.Controls.Add(this.label2); + this.groupBox38.Controls.Add(this.label1); + this.groupBox38.Controls.Add(this.label9); + this.groupBox38.Controls.Add(this.label8); + this.groupBox38.Controls.Add(this.label7); + this.groupBox38.Controls.Add(this.label5); + this.groupBox38.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox38.Location = new System.Drawing.Point(309, 305); + this.groupBox38.Name = "groupBox38"; + this.groupBox38.Size = new System.Drawing.Size(348, 141); + this.groupBox38.TabIndex = 37; + this.groupBox38.TabStop = false; + this.groupBox38.Text = "Plane Position Database Filters"; + // + // tb_Options_Planes_Positions_TTL + // + this.tb_Options_Planes_Positions_TTL.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScout.Properties.Settings.Default, "Planes_Position_TTL", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Options_Planes_Positions_TTL.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Planes_Positions_TTL.FormatSpecifier = "F0"; + this.tb_Options_Planes_Positions_TTL.Location = new System.Drawing.Point(192, 97); + this.tb_Options_Planes_Positions_TTL.MaxValue = 30; + this.tb_Options_Planes_Positions_TTL.MinValue = 0; + this.tb_Options_Planes_Positions_TTL.Name = "tb_Options_Planes_Positions_TTL"; + this.tb_Options_Planes_Positions_TTL.Size = new System.Drawing.Size(52, 22); + this.tb_Options_Planes_Positions_TTL.TabIndex = 43; + this.tb_Options_Planes_Positions_TTL.Text = "5"; + this.tb_Options_Planes_Positions_TTL.Value = global::AirScout.Properties.Settings.Default.Planes_Position_TTL; + // + // tb_Options_Planes_MaxAlt + // + this.tb_Options_Planes_MaxAlt.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScout.Properties.Settings.Default, "Planes_MaxAlt", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Options_Planes_MaxAlt.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Planes_MaxAlt.FormatSpecifier = "F0"; + this.tb_Options_Planes_MaxAlt.Location = new System.Drawing.Point(192, 46); + this.tb_Options_Planes_MaxAlt.MaxValue = 20000; + this.tb_Options_Planes_MaxAlt.MinValue = 0; + this.tb_Options_Planes_MaxAlt.Name = "tb_Options_Planes_MaxAlt"; + this.tb_Options_Planes_MaxAlt.Size = new System.Drawing.Size(52, 22); + this.tb_Options_Planes_MaxAlt.TabIndex = 42; + this.tb_Options_Planes_MaxAlt.Text = "12200"; + this.tb_Options_Planes_MaxAlt.Value = global::AirScout.Properties.Settings.Default.Planes_MaxAlt; + // + // tb_Options_Planes_MinAlt + // + this.tb_Options_Planes_MinAlt.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScout.Properties.Settings.Default, "Planes_MinAlt", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Options_Planes_MinAlt.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Planes_MinAlt.FormatSpecifier = "F0"; + this.tb_Options_Planes_MinAlt.Location = new System.Drawing.Point(192, 21); + this.tb_Options_Planes_MinAlt.MaxValue = 20000; + this.tb_Options_Planes_MinAlt.MinValue = 0; + this.tb_Options_Planes_MinAlt.Name = "tb_Options_Planes_MinAlt"; + this.tb_Options_Planes_MinAlt.Size = new System.Drawing.Size(52, 22); + this.tb_Options_Planes_MinAlt.TabIndex = 41; + this.tb_Options_Planes_MinAlt.Text = "5000"; + this.tb_Options_Planes_MinAlt.Value = global::AirScout.Properties.Settings.Default.Planes_MinAlt; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label2.Location = new System.Drawing.Point(250, 101); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(31, 13); + this.label2.TabIndex = 40; + this.label2.Text = "mins."; + // + // label1 + // + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(8, 74); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(117, 59); + this.label1.TabIndex = 39; + this.label1.Text = "Discard plane when last position report is older than:"; + // + // label9 + // + this.label9.AutoSize = true; + this.label9.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label9.Location = new System.Drawing.Point(250, 22); + this.label9.Name = "label9"; + this.label9.Size = new System.Drawing.Size(15, 13); + this.label9.TabIndex = 37; + this.label9.Text = "m"; + // + // label8 + // + this.label8.AutoSize = true; + this.label8.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label8.Location = new System.Drawing.Point(250, 48); + this.label8.Name = "label8"; + this.label8.Size = new System.Drawing.Size(15, 13); + this.label8.TabIndex = 36; + this.label8.Text = "m"; + // + // label7 + // + this.label7.AutoSize = true; + this.label7.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label7.Location = new System.Drawing.Point(8, 50); + this.label7.Name = "label7"; + this.label7.Size = new System.Drawing.Size(86, 13); + this.label7.TabIndex = 34; + this.label7.Text = "Maximal Altitude:"; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label5.Location = new System.Drawing.Point(8, 24); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(83, 13); + this.label5.TabIndex = 32; + this.label5.Text = "Minimal Altitude:"; + // + // groupBox37 + // + this.groupBox37.Controls.Add(this.label22); + this.groupBox37.Controls.Add(this.tb_Options_Planes_MaxLat); + this.groupBox37.Controls.Add(this.label37); + this.groupBox37.Controls.Add(this.tb_Options_Planes_MinLat); + this.groupBox37.Controls.Add(this.label38); + this.groupBox37.Controls.Add(this.tb_Options_Planes_MaxLon); + this.groupBox37.Controls.Add(this.label33); + this.groupBox37.Controls.Add(this.tb_Options_Planes_MinLon); + this.groupBox37.Controls.Add(this.label34); + this.groupBox37.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox37.Location = new System.Drawing.Point(12, 148); + this.groupBox37.Name = "groupBox37"; + this.groupBox37.Size = new System.Drawing.Size(291, 151); + this.groupBox37.TabIndex = 36; + this.groupBox37.TabStop = false; + this.groupBox37.Text = "Coverage Info"; + // + // label22 + // + this.label22.AutoSize = true; + this.label22.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label22.Location = new System.Drawing.Point(10, 22); + this.label22.Name = "label22"; + this.label22.Size = new System.Drawing.Size(238, 13); + this.label22.TabIndex = 34; + this.label22.Text = "You can change the values in the \"General\" tab."; + // + // tb_Options_Planes_MaxLat + // + this.tb_Options_Planes_MaxLat.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Planes_MaxLat.Location = new System.Drawing.Point(198, 123); + this.tb_Options_Planes_MaxLat.Name = "tb_Options_Planes_MaxLat"; + this.tb_Options_Planes_MaxLat.ReadOnly = true; + this.tb_Options_Planes_MaxLat.Size = new System.Drawing.Size(50, 20); + this.tb_Options_Planes_MaxLat.TabIndex = 33; + this.tb_Options_Planes_MaxLat.Tag = "MaxLat;F2;-90;90"; + this.tb_Options_Planes_MaxLat.Text = "70"; + // + // label37 + // + this.label37.AutoSize = true; + this.label37.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label37.Location = new System.Drawing.Point(10, 126); + this.label37.Name = "label37"; + this.label37.Size = new System.Drawing.Size(148, 13); + this.label37.TabIndex = 32; + this.label37.Text = "Maximal Latitude (-90°.. +90°):"; + // + // tb_Options_Planes_MinLat + // + this.tb_Options_Planes_MinLat.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Planes_MinLat.Location = new System.Drawing.Point(198, 97); + this.tb_Options_Planes_MinLat.Name = "tb_Options_Planes_MinLat"; + this.tb_Options_Planes_MinLat.ReadOnly = true; + this.tb_Options_Planes_MinLat.Size = new System.Drawing.Size(50, 20); + this.tb_Options_Planes_MinLat.TabIndex = 31; + this.tb_Options_Planes_MinLat.Tag = "MinLat;F2;-90;90"; + this.tb_Options_Planes_MinLat.Text = "35"; + // + // label38 + // + this.label38.AutoSize = true; + this.label38.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label38.Location = new System.Drawing.Point(10, 100); + this.label38.Name = "label38"; + this.label38.Size = new System.Drawing.Size(145, 13); + this.label38.TabIndex = 30; + this.label38.Text = "Minimal Latitude (-90° ..+90°):"; + // + // tb_Options_Planes_MaxLon + // + this.tb_Options_Planes_MaxLon.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Planes_MaxLon.Location = new System.Drawing.Point(198, 70); + this.tb_Options_Planes_MaxLon.Name = "tb_Options_Planes_MaxLon"; + this.tb_Options_Planes_MaxLon.ReadOnly = true; + this.tb_Options_Planes_MaxLon.Size = new System.Drawing.Size(50, 20); + this.tb_Options_Planes_MaxLon.TabIndex = 29; + this.tb_Options_Planes_MaxLon.Tag = "MaxLon;F2;-180;180"; + this.tb_Options_Planes_MaxLon.Text = "40"; + // + // label33 + // + this.label33.AutoSize = true; + this.label33.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label33.Location = new System.Drawing.Point(10, 73); + this.label33.Name = "label33"; + this.label33.Size = new System.Drawing.Size(169, 13); + this.label33.TabIndex = 28; + this.label33.Text = "Maximal Longitude (-180°.. +180°):"; + // + // tb_Options_Planes_MinLon + // + this.tb_Options_Planes_MinLon.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Planes_MinLon.Location = new System.Drawing.Point(198, 44); + this.tb_Options_Planes_MinLon.Name = "tb_Options_Planes_MinLon"; + this.tb_Options_Planes_MinLon.ReadOnly = true; + this.tb_Options_Planes_MinLon.Size = new System.Drawing.Size(50, 20); + this.tb_Options_Planes_MinLon.TabIndex = 27; + this.tb_Options_Planes_MinLon.Tag = "MinLon;F2;-180;180"; + this.tb_Options_Planes_MinLon.Text = "-15"; + // + // label34 + // + this.label34.AutoSize = true; + this.label34.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label34.Location = new System.Drawing.Point(10, 47); + this.label34.Name = "label34"; + this.label34.Size = new System.Drawing.Size(166, 13); + this.label34.TabIndex = 26; + this.label34.Text = "Minimal Longitude (-180° ..+180°):"; + // + // groupBox26 + // + this.groupBox26.Controls.Add(this.tb_Options_Planes_Filter_MinAlt); + this.groupBox26.Controls.Add(this.tb_Options_Planes_Filter_Max_Circumcircle); + this.groupBox26.Controls.Add(this.label96); + this.groupBox26.Controls.Add(this.label94); + this.groupBox26.Controls.Add(this.label95); + this.groupBox26.Controls.Add(this.label92); + this.groupBox26.Controls.Add(this.label93); + this.groupBox26.Controls.Add(this.label91); + this.groupBox26.Controls.Add(this.cb_Options_Planes_Filter_Min_Cat); + this.groupBox26.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox26.Location = new System.Drawing.Point(309, 148); + this.groupBox26.Name = "groupBox26"; + this.groupBox26.Size = new System.Drawing.Size(348, 151); + this.groupBox26.TabIndex = 33; + this.groupBox26.TabStop = false; + this.groupBox26.Text = "Plane Live Position Filters"; + // + // tb_Options_Planes_Filter_MinAlt + // + this.tb_Options_Planes_Filter_MinAlt.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScout.Properties.Settings.Default, "Planes_Filter_Min_Alt", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Options_Planes_Filter_MinAlt.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Planes_Filter_MinAlt.FormatSpecifier = "F0"; + this.tb_Options_Planes_Filter_MinAlt.Location = new System.Drawing.Point(192, 86); + this.tb_Options_Planes_Filter_MinAlt.MaxValue = 12000; + this.tb_Options_Planes_Filter_MinAlt.MinValue = 0; + this.tb_Options_Planes_Filter_MinAlt.Name = "tb_Options_Planes_Filter_MinAlt"; + this.tb_Options_Planes_Filter_MinAlt.Size = new System.Drawing.Size(52, 22); + this.tb_Options_Planes_Filter_MinAlt.TabIndex = 43; + this.tb_Options_Planes_Filter_MinAlt.Text = "0"; + this.tb_Options_Planes_Filter_MinAlt.Value = global::AirScout.Properties.Settings.Default.Planes_Filter_Min_Alt; + // + // tb_Options_Planes_Filter_Max_Circumcircle + // + this.tb_Options_Planes_Filter_Max_Circumcircle.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScout.Properties.Settings.Default, "Planes_Filter_Max_Circumcircle", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Options_Planes_Filter_Max_Circumcircle.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Planes_Filter_Max_Circumcircle.FormatSpecifier = "F0"; + this.tb_Options_Planes_Filter_Max_Circumcircle.Location = new System.Drawing.Point(192, 59); + this.tb_Options_Planes_Filter_Max_Circumcircle.MaxValue = 1000; + this.tb_Options_Planes_Filter_Max_Circumcircle.MinValue = -1; + this.tb_Options_Planes_Filter_Max_Circumcircle.Name = "tb_Options_Planes_Filter_Max_Circumcircle"; + this.tb_Options_Planes_Filter_Max_Circumcircle.Size = new System.Drawing.Size(52, 22); + this.tb_Options_Planes_Filter_Max_Circumcircle.TabIndex = 42; + this.tb_Options_Planes_Filter_Max_Circumcircle.Text = "0"; + this.tb_Options_Planes_Filter_Max_Circumcircle.Value = global::AirScout.Properties.Settings.Default.Planes_Filter_Max_Circumcircle; + // + // label96 + // + this.label96.AutoSize = true; + this.label96.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label96.Location = new System.Drawing.Point(8, 22); + this.label96.Name = "label96"; + this.label96.Size = new System.Drawing.Size(274, 13); + this.label96.TabIndex = 41; + this.label96.Text = "You can change the values here or in the Main Window."; + // + // label94 + // + this.label94.AutoSize = true; + this.label94.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label94.Location = new System.Drawing.Point(254, 66); + this.label94.Name = "label94"; + this.label94.Size = new System.Drawing.Size(21, 13); + this.label94.TabIndex = 40; + this.label94.Text = "km"; + // + // label95 + // + this.label95.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label95.Location = new System.Drawing.Point(8, 39); + this.label95.Name = "label95"; + this.label95.Size = new System.Drawing.Size(180, 42); + this.label95.TabIndex = 39; + this.label95.Text = "Maximal Path Midpoint Circumcircle\r\n(Filter AUTO = 0)\r\n(Filter OFF = -1):"; + // + // label92 + // + this.label92.AutoSize = true; + this.label92.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label92.Location = new System.Drawing.Point(254, 92); + this.label92.Name = "label92"; + this.label92.Size = new System.Drawing.Size(15, 13); + this.label92.TabIndex = 37; + this.label92.Text = "m"; + // + // label93 + // + this.label93.AutoSize = true; + this.label93.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label93.Location = new System.Drawing.Point(11, 87); + this.label93.Name = "label93"; + this.label93.Size = new System.Drawing.Size(83, 26); + this.label93.TabIndex = 36; + this.label93.Text = "Minimal Altitude \r\n(Filter OFF = 0:"; + // + // label91 + // + this.label91.AutoSize = true; + this.label91.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label91.Location = new System.Drawing.Point(8, 119); + this.label91.Name = "label91"; + this.label91.Size = new System.Drawing.Size(90, 13); + this.label91.TabIndex = 34; + this.label91.Text = "Minimal Category:"; + // + // cb_Options_Planes_Filter_Min_Cat + // + this.cb_Options_Planes_Filter_Min_Cat.AllowDrop = true; + this.cb_Options_Planes_Filter_Min_Cat.BackColor = System.Drawing.Color.FloralWhite; + this.cb_Options_Planes_Filter_Min_Cat.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cb_Options_Planes_Filter_Min_Cat.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Options_Planes_Filter_Min_Cat.FormattingEnabled = true; + this.cb_Options_Planes_Filter_Min_Cat.Items.AddRange(new object[] { + "Light", + "Medium", + "Heavy", + "Super"}); + this.cb_Options_Planes_Filter_Min_Cat.Location = new System.Drawing.Point(192, 115); + this.cb_Options_Planes_Filter_Min_Cat.Name = "cb_Options_Planes_Filter_Min_Cat"; + this.cb_Options_Planes_Filter_Min_Cat.Size = new System.Drawing.Size(77, 24); + this.cb_Options_Planes_Filter_Min_Cat.TabIndex = 33; + this.cb_Options_Planes_Filter_Min_Cat.SelectedIndexChanged += new System.EventHandler(this.cb_Options_Planes_Filter_Min_Cat_SelectedIndexChanged); + // + // groupBox6 + // + this.groupBox6.Controls.Add(this.btn_Options_PlaneFeed3_Export); + this.groupBox6.Controls.Add(this.btn_Options_PlaneFeed2_Export); + this.groupBox6.Controls.Add(this.btn_Options_PlaneFeed1_Export); + this.groupBox6.Controls.Add(this.btn_Options_PlaneFeed3_Import); + this.groupBox6.Controls.Add(this.btn_Options_PlaneFeed2_Import); + this.groupBox6.Controls.Add(this.btn_Options_PlaneFeed1_Import); + this.groupBox6.Controls.Add(this.label79); + this.groupBox6.Controls.Add(this.btn_Options_PlaneFeed3_Settings); + this.groupBox6.Controls.Add(this.cb_Options_PlaneFeed3); + this.groupBox6.Controls.Add(this.label78); + this.groupBox6.Controls.Add(this.btn_Options_PlaneFeed2_Settings); + this.groupBox6.Controls.Add(this.cb_Options_PlaneFeed2); + this.groupBox6.Controls.Add(this.label77); + this.groupBox6.Controls.Add(this.btn_Options_PlaneFeed1_Settings); + this.groupBox6.Controls.Add(this.cb_Options_PlaneFeed1); + this.groupBox6.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox6.Location = new System.Drawing.Point(12, 12); + this.groupBox6.Name = "groupBox6"; + this.groupBox6.Size = new System.Drawing.Size(645, 130); + this.groupBox6.TabIndex = 5; + this.groupBox6.TabStop = false; + this.groupBox6.Text = "Plane Feeds"; + // + // btn_Options_PlaneFeed3_Export + // + this.btn_Options_PlaneFeed3_Export.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Options_PlaneFeed3_Export.Location = new System.Drawing.Point(578, 80); + this.btn_Options_PlaneFeed3_Export.Name = "btn_Options_PlaneFeed3_Export"; + this.btn_Options_PlaneFeed3_Export.Size = new System.Drawing.Size(55, 23); + this.btn_Options_PlaneFeed3_Export.TabIndex = 39; + this.btn_Options_PlaneFeed3_Export.Text = "Export"; + this.btn_Options_PlaneFeed3_Export.UseVisualStyleBackColor = true; + this.btn_Options_PlaneFeed3_Export.Click += new System.EventHandler(this.btn_Options_PlaneFeed3_Export_Click); + // + // btn_Options_PlaneFeed2_Export + // + this.btn_Options_PlaneFeed2_Export.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Options_PlaneFeed2_Export.Location = new System.Drawing.Point(578, 53); + this.btn_Options_PlaneFeed2_Export.Name = "btn_Options_PlaneFeed2_Export"; + this.btn_Options_PlaneFeed2_Export.Size = new System.Drawing.Size(55, 23); + this.btn_Options_PlaneFeed2_Export.TabIndex = 38; + this.btn_Options_PlaneFeed2_Export.Text = "Export"; + this.btn_Options_PlaneFeed2_Export.UseVisualStyleBackColor = true; + this.btn_Options_PlaneFeed2_Export.Click += new System.EventHandler(this.btn_Options_PlaneFeed2_Export_Click); + // + // btn_Options_PlaneFeed1_Export + // + this.btn_Options_PlaneFeed1_Export.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Options_PlaneFeed1_Export.Location = new System.Drawing.Point(578, 26); + this.btn_Options_PlaneFeed1_Export.Name = "btn_Options_PlaneFeed1_Export"; + this.btn_Options_PlaneFeed1_Export.Size = new System.Drawing.Size(55, 23); + this.btn_Options_PlaneFeed1_Export.TabIndex = 37; + this.btn_Options_PlaneFeed1_Export.Text = "Export"; + this.btn_Options_PlaneFeed1_Export.UseVisualStyleBackColor = true; + this.btn_Options_PlaneFeed1_Export.Click += new System.EventHandler(this.btn_Options_PlaneFeed1_Export_Click); + // + // btn_Options_PlaneFeed3_Import + // + this.btn_Options_PlaneFeed3_Import.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Options_PlaneFeed3_Import.Location = new System.Drawing.Point(527, 80); + this.btn_Options_PlaneFeed3_Import.Name = "btn_Options_PlaneFeed3_Import"; + this.btn_Options_PlaneFeed3_Import.Size = new System.Drawing.Size(45, 23); + this.btn_Options_PlaneFeed3_Import.TabIndex = 36; + this.btn_Options_PlaneFeed3_Import.Text = "Import"; + this.btn_Options_PlaneFeed3_Import.UseVisualStyleBackColor = true; + this.btn_Options_PlaneFeed3_Import.Click += new System.EventHandler(this.btn_Options_PlaneFeed3_Import_Click); + // + // btn_Options_PlaneFeed2_Import + // + this.btn_Options_PlaneFeed2_Import.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Options_PlaneFeed2_Import.Location = new System.Drawing.Point(527, 53); + this.btn_Options_PlaneFeed2_Import.Name = "btn_Options_PlaneFeed2_Import"; + this.btn_Options_PlaneFeed2_Import.Size = new System.Drawing.Size(45, 23); + this.btn_Options_PlaneFeed2_Import.TabIndex = 35; + this.btn_Options_PlaneFeed2_Import.Text = "Import"; + this.btn_Options_PlaneFeed2_Import.UseVisualStyleBackColor = true; + this.btn_Options_PlaneFeed2_Import.Click += new System.EventHandler(this.btn_Options_PlaneFeed2_Import_Click); + // + // btn_Options_PlaneFeed1_Import + // + this.btn_Options_PlaneFeed1_Import.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Options_PlaneFeed1_Import.Location = new System.Drawing.Point(527, 26); + this.btn_Options_PlaneFeed1_Import.Name = "btn_Options_PlaneFeed1_Import"; + this.btn_Options_PlaneFeed1_Import.Size = new System.Drawing.Size(45, 23); + this.btn_Options_PlaneFeed1_Import.TabIndex = 34; + this.btn_Options_PlaneFeed1_Import.Text = "Import"; + this.btn_Options_PlaneFeed1_Import.UseVisualStyleBackColor = true; + this.btn_Options_PlaneFeed1_Import.Click += new System.EventHandler(this.btn_Options_PlaneFeed1_Import_Click); + // + // label79 + // + this.label79.AutoSize = true; + this.label79.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label79.Location = new System.Drawing.Point(9, 86); + this.label79.Name = "label79"; + this.label79.Size = new System.Drawing.Size(73, 13); + this.label79.TabIndex = 33; + this.label79.Text = "Plane Feed 3:"; + // + // btn_Options_PlaneFeed3_Settings + // + this.btn_Options_PlaneFeed3_Settings.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Options_PlaneFeed3_Settings.Location = new System.Drawing.Point(466, 80); + this.btn_Options_PlaneFeed3_Settings.Name = "btn_Options_PlaneFeed3_Settings"; + this.btn_Options_PlaneFeed3_Settings.Size = new System.Drawing.Size(55, 23); + this.btn_Options_PlaneFeed3_Settings.TabIndex = 32; + this.btn_Options_PlaneFeed3_Settings.Text = "Settings"; + this.btn_Options_PlaneFeed3_Settings.UseVisualStyleBackColor = true; + this.btn_Options_PlaneFeed3_Settings.Click += new System.EventHandler(this.btn_Options_PlaneFeed3_Settings_Click); + // + // cb_Options_PlaneFeed3 + // + this.cb_Options_PlaneFeed3.DisplayMember = "Name"; + this.cb_Options_PlaneFeed3.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cb_Options_PlaneFeed3.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Options_PlaneFeed3.FormattingEnabled = true; + this.cb_Options_PlaneFeed3.Location = new System.Drawing.Point(91, 81); + this.cb_Options_PlaneFeed3.Name = "cb_Options_PlaneFeed3"; + this.cb_Options_PlaneFeed3.Size = new System.Drawing.Size(367, 24); + this.cb_Options_PlaneFeed3.TabIndex = 31; + this.cb_Options_PlaneFeed3.SelectedIndexChanged += new System.EventHandler(this.cb_Options_PlaneFeed3_SelectedIndexChanged); + // + // label78 + // + this.label78.AutoSize = true; + this.label78.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label78.Location = new System.Drawing.Point(9, 59); + this.label78.Name = "label78"; + this.label78.Size = new System.Drawing.Size(73, 13); + this.label78.TabIndex = 30; + this.label78.Text = "Plane Feed 2:"; + // + // btn_Options_PlaneFeed2_Settings + // + this.btn_Options_PlaneFeed2_Settings.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Options_PlaneFeed2_Settings.Location = new System.Drawing.Point(466, 53); + this.btn_Options_PlaneFeed2_Settings.Name = "btn_Options_PlaneFeed2_Settings"; + this.btn_Options_PlaneFeed2_Settings.Size = new System.Drawing.Size(55, 23); + this.btn_Options_PlaneFeed2_Settings.TabIndex = 29; + this.btn_Options_PlaneFeed2_Settings.Text = "Settings"; + this.btn_Options_PlaneFeed2_Settings.UseVisualStyleBackColor = true; + this.btn_Options_PlaneFeed2_Settings.Click += new System.EventHandler(this.btn_Options_PlaneFeed2_Settings_Click); + // + // cb_Options_PlaneFeed2 + // + this.cb_Options_PlaneFeed2.DisplayMember = "Name"; + this.cb_Options_PlaneFeed2.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cb_Options_PlaneFeed2.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Options_PlaneFeed2.FormattingEnabled = true; + this.cb_Options_PlaneFeed2.Location = new System.Drawing.Point(91, 54); + this.cb_Options_PlaneFeed2.Name = "cb_Options_PlaneFeed2"; + this.cb_Options_PlaneFeed2.Size = new System.Drawing.Size(367, 24); + this.cb_Options_PlaneFeed2.TabIndex = 28; + this.cb_Options_PlaneFeed2.SelectedIndexChanged += new System.EventHandler(this.cb_Options_PlaneFeed2_SelectedIndexChanged); + // + // label77 + // + this.label77.AutoSize = true; + this.label77.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label77.Location = new System.Drawing.Point(9, 32); + this.label77.Name = "label77"; + this.label77.Size = new System.Drawing.Size(73, 13); + this.label77.TabIndex = 27; + this.label77.Text = "Plane Feed 1:"; + // + // btn_Options_PlaneFeed1_Settings + // + this.btn_Options_PlaneFeed1_Settings.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Options_PlaneFeed1_Settings.Location = new System.Drawing.Point(466, 26); + this.btn_Options_PlaneFeed1_Settings.Name = "btn_Options_PlaneFeed1_Settings"; + this.btn_Options_PlaneFeed1_Settings.Size = new System.Drawing.Size(55, 23); + this.btn_Options_PlaneFeed1_Settings.TabIndex = 1; + this.btn_Options_PlaneFeed1_Settings.Text = "Settings"; + this.btn_Options_PlaneFeed1_Settings.UseVisualStyleBackColor = true; + this.btn_Options_PlaneFeed1_Settings.Click += new System.EventHandler(this.btn_Options_PlaneFeed1_Settings_Click); + // + // cb_Options_PlaneFeed1 + // + this.cb_Options_PlaneFeed1.DisplayMember = "Name"; + this.cb_Options_PlaneFeed1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cb_Options_PlaneFeed1.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Options_PlaneFeed1.FormattingEnabled = true; + this.cb_Options_PlaneFeed1.Location = new System.Drawing.Point(91, 27); + this.cb_Options_PlaneFeed1.Name = "cb_Options_PlaneFeed1"; + this.cb_Options_PlaneFeed1.Size = new System.Drawing.Size(367, 24); + this.cb_Options_PlaneFeed1.TabIndex = 0; + this.cb_Options_PlaneFeed1.SelectedIndexChanged += new System.EventHandler(this.cb_Options_PlaneFeed1_SelectedIndexChanged); + // + // tab_Options_Path + // + this.tab_Options_Path.BackColor = System.Drawing.SystemColors.Control; + this.tab_Options_Path.Controls.Add(this.groupBox1); + this.tab_Options_Path.Location = new System.Drawing.Point(4, 22); + this.tab_Options_Path.Name = "tab_Options_Path"; + this.tab_Options_Path.Size = new System.Drawing.Size(671, 460); + this.tab_Options_Path.TabIndex = 3; + this.tab_Options_Path.Text = "Path"; + this.tab_Options_Path.Enter += new System.EventHandler(this.tab_Options_Path_Enter); + this.tab_Options_Path.Leave += new System.EventHandler(this.tab_Options_Path_Leave); + this.tab_Options_Path.Validating += new System.ComponentModel.CancelEventHandler(this.tab_Options_Path_Validating); + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.tb_Options_Path_MaxLength); + this.groupBox1.Controls.Add(this.label131); + this.groupBox1.Controls.Add(this.label130); + this.groupBox1.Controls.Add(this.tb_Options_Path_StepWidth); + this.groupBox1.Controls.Add(this.label129); + this.groupBox1.Controls.Add(this.dgv_BandSettings); + this.groupBox1.Controls.Add(this.cb_Options_Path_BestCaseElevation); + this.groupBox1.Controls.Add(this.label57); + this.groupBox1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox1.Location = new System.Drawing.Point(14, 5); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(581, 440); + this.groupBox1.TabIndex = 3; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Path Options"; + // + // tb_Options_Path_MaxLength + // + this.tb_Options_Path_MaxLength.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScout.Properties.Settings.Default, "Path_MaxLength", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Options_Path_MaxLength.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Path_MaxLength.FormatSpecifier = "F0"; + this.tb_Options_Path_MaxLength.Location = new System.Drawing.Point(318, 385); + this.tb_Options_Path_MaxLength.MaxValue = double.NaN; + this.tb_Options_Path_MaxLength.MinValue = double.NaN; + this.tb_Options_Path_MaxLength.Name = "tb_Options_Path_MaxLength"; + this.tb_Options_Path_MaxLength.Size = new System.Drawing.Size(57, 20); + this.tb_Options_Path_MaxLength.TabIndex = 39; + this.tb_Options_Path_MaxLength.Text = "1000"; + this.tb_Options_Path_MaxLength.Value = global::AirScout.Properties.Settings.Default.Path_MaxLength; + // + // label131 + // + this.label131.AutoSize = true; + this.label131.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label131.Location = new System.Drawing.Point(381, 388); + this.label131.Name = "label131"; + this.label131.Size = new System.Drawing.Size(21, 13); + this.label131.TabIndex = 38; + this.label131.Text = "km"; + // + // label130 + // + this.label130.AutoSize = true; + this.label130.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label130.Location = new System.Drawing.Point(6, 388); + this.label130.Name = "label130"; + this.label130.Size = new System.Drawing.Size(297, 13); + this.label130.TabIndex = 36; + this.label130.Text = "Do not calculate path automatically when length is more than:"; + // + // tb_Options_Path_StepWidth + // + this.tb_Options_Path_StepWidth.BackColor = System.Drawing.Color.FloralWhite; + this.tb_Options_Path_StepWidth.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Path_StepWidth.Location = new System.Drawing.Point(318, 361); + this.tb_Options_Path_StepWidth.Name = "tb_Options_Path_StepWidth"; + this.tb_Options_Path_StepWidth.ReadOnly = true; + this.tb_Options_Path_StepWidth.Size = new System.Drawing.Size(57, 20); + this.tb_Options_Path_StepWidth.TabIndex = 35; + // + // label129 + // + this.label129.AutoSize = true; + this.label129.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label129.Location = new System.Drawing.Point(6, 364); + this.label129.Name = "label129"; + this.label129.Size = new System.Drawing.Size(275, 13); + this.label129.TabIndex = 34; + this.label129.Text = "Path calculation stepwidth according to Elevation Model:"; + // + // dgv_BandSettings + // + this.dgv_BandSettings.AllowUserToAddRows = false; + this.dgv_BandSettings.AllowUserToDeleteRows = false; + this.dgv_BandSettings.AllowUserToResizeColumns = false; + this.dgv_BandSettings.AllowUserToResizeRows = false; + this.dgv_BandSettings.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.dgv_BandSettings.Location = new System.Drawing.Point(9, 19); + this.dgv_BandSettings.Name = "dgv_BandSettings"; + this.dgv_BandSettings.RowHeadersVisible = false; + this.dgv_BandSettings.Size = new System.Drawing.Size(566, 336); + this.dgv_BandSettings.TabIndex = 29; + // + // label57 + // + this.label57.AutoSize = true; + this.label57.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label57.Location = new System.Drawing.Point(381, 364); + this.label57.Name = "label57"; + this.label57.Size = new System.Drawing.Size(15, 13); + this.label57.TabIndex = 13; + this.label57.Text = "m"; + // + // tab_Options_SRTM1 + // + this.tab_Options_SRTM1.BackColor = System.Drawing.SystemColors.Control; + this.tab_Options_SRTM1.Controls.Add(this.groupBox43); + this.tab_Options_SRTM1.Controls.Add(this.btn_Options_SRTM1_Copyright); + this.tab_Options_SRTM1.Controls.Add(this.groupBox13); + this.tab_Options_SRTM1.Controls.Add(this.groupBox12); + this.tab_Options_SRTM1.Location = new System.Drawing.Point(4, 22); + this.tab_Options_SRTM1.Name = "tab_Options_SRTM1"; + this.tab_Options_SRTM1.Size = new System.Drawing.Size(671, 460); + this.tab_Options_SRTM1.TabIndex = 9; + this.tab_Options_SRTM1.Text = "SRTM1"; + this.tab_Options_SRTM1.Enter += new System.EventHandler(this.tab_Options_SRTM1_Enter); + this.tab_Options_SRTM1.Leave += new System.EventHandler(this.tab_Options_SRTM1_Leave); + // + // groupBox43 + // + this.groupBox43.BackColor = System.Drawing.Color.White; + this.groupBox43.Controls.Add(this.label111); + this.groupBox43.Controls.Add(this.label112); + this.groupBox43.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox43.Location = new System.Drawing.Point(217, 15); + this.groupBox43.Name = "groupBox43"; + this.groupBox43.Size = new System.Drawing.Size(312, 69); + this.groupBox43.TabIndex = 21; + this.groupBox43.TabStop = false; + this.groupBox43.Text = "(S)huttle (R)adar (T)opography (M)ission 1 arscec"; + // + // label111 + // + this.label111.AutoSize = true; + this.label111.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label111.Location = new System.Drawing.Point(14, 47); + this.label111.Name = "label111"; + this.label111.Size = new System.Drawing.Size(258, 13); + this.label111.TabIndex = 1; + this.label111.Text = "Best performanc. slow calculation, excellent precision"; + // + // label112 + // + this.label112.AutoSize = true; + this.label112.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label112.Location = new System.Drawing.Point(44, 17); + this.label112.Name = "label112"; + this.label112.Size = new System.Drawing.Size(204, 26); + this.label112.TabIndex = 0; + this.label112.Text = "A 1 arcsec (30m) grided elevation model.\r\nCoverage: 58°S ... 60°N; 180°W ... 180°" + + "E"; + // + // btn_Options_SRTM1_Copyright + // + this.btn_Options_SRTM1_Copyright.Location = new System.Drawing.Point(535, 20); + this.btn_Options_SRTM1_Copyright.Name = "btn_Options_SRTM1_Copyright"; + this.btn_Options_SRTM1_Copyright.Size = new System.Drawing.Size(116, 64); + this.btn_Options_SRTM1_Copyright.TabIndex = 20; + this.btn_Options_SRTM1_Copyright.Text = "Licence/Copyright \r\nInformation"; + this.btn_Options_SRTM1_Copyright.UseVisualStyleBackColor = true; + this.btn_Options_SRTM1_Copyright.Click += new System.EventHandler(this.btn_Options_SRTM1_Copyright_Click); + // + // groupBox13 + // + this.groupBox13.Controls.Add(this.gm_Options_SRTM1); + this.groupBox13.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox13.Location = new System.Drawing.Point(12, 90); + this.groupBox13.Name = "groupBox13"; + this.groupBox13.Size = new System.Drawing.Size(642, 358); + this.groupBox13.TabIndex = 17; + this.groupBox13.TabStop = false; + this.groupBox13.Text = "Info"; + // + // gm_Options_SRTM1 + // + this.gm_Options_SRTM1.Bearing = 0F; + this.gm_Options_SRTM1.CanDragMap = true; + this.gm_Options_SRTM1.Dock = System.Windows.Forms.DockStyle.Fill; + this.gm_Options_SRTM1.EmptyTileColor = System.Drawing.Color.Navy; + this.gm_Options_SRTM1.GrayScaleMode = false; + this.gm_Options_SRTM1.HelperLineOption = GMap.NET.WindowsForms.HelperLineOptions.DontShow; + this.gm_Options_SRTM1.LevelsKeepInMemmory = 5; + this.gm_Options_SRTM1.Location = new System.Drawing.Point(3, 16); + this.gm_Options_SRTM1.MarkersEnabled = true; + this.gm_Options_SRTM1.MaxZoom = 2; + this.gm_Options_SRTM1.MinZoom = 2; + this.gm_Options_SRTM1.MouseWheelZoomType = GMap.NET.MouseWheelZoomType.MousePositionAndCenter; + this.gm_Options_SRTM1.Name = "gm_Options_SRTM1"; + this.gm_Options_SRTM1.NegativeMode = false; + this.gm_Options_SRTM1.PolygonsEnabled = true; + this.gm_Options_SRTM1.RetryLoadTile = 0; + this.gm_Options_SRTM1.RoutesEnabled = true; + this.gm_Options_SRTM1.SelectedAreaFillColor = System.Drawing.Color.FromArgb(((int)(((byte)(33)))), ((int)(((byte)(65)))), ((int)(((byte)(105)))), ((int)(((byte)(225))))); + this.gm_Options_SRTM1.ShowTileGridLines = false; + this.gm_Options_SRTM1.Size = new System.Drawing.Size(636, 339); + this.gm_Options_SRTM1.TabIndex = 20; + this.gm_Options_SRTM1.Zoom = 0D; + // + // groupBox12 + // + this.groupBox12.Controls.Add(this.cb_Options_Elevation_SRTM1_EnableCache); + this.groupBox12.Controls.Add(this.cb_Options_Elevation_SRTM1); + this.groupBox12.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox12.Location = new System.Drawing.Point(12, 15); + this.groupBox12.Name = "groupBox12"; + this.groupBox12.Size = new System.Drawing.Size(191, 69); + this.groupBox12.TabIndex = 16; + this.groupBox12.TabStop = false; + this.groupBox12.Text = "Use Elevation Model"; + // + // cb_Options_Elevation_SRTM1_EnableCache + // + this.cb_Options_Elevation_SRTM1_EnableCache.AutoSize = true; + this.cb_Options_Elevation_SRTM1_EnableCache.Checked = global::AirScout.Properties.Settings.Default.Elevation_SRTM1_EnableCache; + this.cb_Options_Elevation_SRTM1_EnableCache.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Elevation_SRTM1_EnableCache", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Options_Elevation_SRTM1_EnableCache.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Options_Elevation_SRTM1_EnableCache.Location = new System.Drawing.Point(6, 42); + this.cb_Options_Elevation_SRTM1_EnableCache.Name = "cb_Options_Elevation_SRTM1_EnableCache"; + this.cb_Options_Elevation_SRTM1_EnableCache.Size = new System.Drawing.Size(179, 17); + this.cb_Options_Elevation_SRTM1_EnableCache.TabIndex = 17; + this.cb_Options_Elevation_SRTM1_EnableCache.Tag = ""; + this.cb_Options_Elevation_SRTM1_EnableCache.Text = "Keep downloaded elevation tiles"; + this.cb_Options_Elevation_SRTM1_EnableCache.UseVisualStyleBackColor = true; + // + // cb_Options_Elevation_SRTM1 + // + this.cb_Options_Elevation_SRTM1.AutoSize = true; + this.cb_Options_Elevation_SRTM1.Checked = global::AirScout.Properties.Settings.Default.Elevation_SRTM1_Enabled; + this.cb_Options_Elevation_SRTM1.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Elevation_SRTM1_Enabled", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Options_Elevation_SRTM1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Options_Elevation_SRTM1.Location = new System.Drawing.Point(6, 19); + this.cb_Options_Elevation_SRTM1.Name = "cb_Options_Elevation_SRTM1"; + this.cb_Options_Elevation_SRTM1.Size = new System.Drawing.Size(155, 17); + this.cb_Options_Elevation_SRTM1.TabIndex = 16; + this.cb_Options_Elevation_SRTM1.Tag = ""; + this.cb_Options_Elevation_SRTM1.Text = "Use SRTM1 elevation data"; + this.cb_Options_Elevation_SRTM1.UseVisualStyleBackColor = true; + // + // tab_Options_SRTM3 + // + this.tab_Options_SRTM3.BackColor = System.Drawing.SystemColors.Control; + this.tab_Options_SRTM3.Controls.Add(this.groupBox42); + this.tab_Options_SRTM3.Controls.Add(this.btn_Options_SRTM3_Copyright); + this.tab_Options_SRTM3.Controls.Add(this.groupBox9); + this.tab_Options_SRTM3.Controls.Add(this.groupBox8); + this.tab_Options_SRTM3.Location = new System.Drawing.Point(4, 22); + this.tab_Options_SRTM3.Name = "tab_Options_SRTM3"; + this.tab_Options_SRTM3.Size = new System.Drawing.Size(671, 460); + this.tab_Options_SRTM3.TabIndex = 8; + this.tab_Options_SRTM3.Text = "SRTM3"; + this.tab_Options_SRTM3.Enter += new System.EventHandler(this.tab_Options_SRTM3_Enter); + this.tab_Options_SRTM3.Leave += new System.EventHandler(this.tab_Options_SRTM3_Leave); + // + // groupBox42 + // + this.groupBox42.BackColor = System.Drawing.Color.White; + this.groupBox42.Controls.Add(this.label100); + this.groupBox42.Controls.Add(this.label110); + this.groupBox42.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox42.Location = new System.Drawing.Point(216, 11); + this.groupBox42.Name = "groupBox42"; + this.groupBox42.Size = new System.Drawing.Size(312, 69); + this.groupBox42.TabIndex = 19; + this.groupBox42.TabStop = false; + this.groupBox42.Text = "(S)huttle (R)adar (T)opography (M)ission 3 arscec"; + // + // label100 + // + this.label100.AutoSize = true; + this.label100.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label100.Location = new System.Drawing.Point(14, 47); + this.label100.Name = "label100"; + this.label100.Size = new System.Drawing.Size(272, 13); + this.label100.TabIndex = 1; + this.label100.Text = "Needs more performanc. fast calculation, good precision"; + // + // label110 + // + this.label110.AutoSize = true; + this.label110.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label110.Location = new System.Drawing.Point(44, 17); + this.label110.Name = "label110"; + this.label110.Size = new System.Drawing.Size(204, 26); + this.label110.TabIndex = 0; + this.label110.Text = "A 3 arcsec (90m) grided elevation model.\r\nCoverage: 58°S ... 60°N; 180°W ... 180°" + + "E"; + // + // btn_Options_SRTM3_Copyright + // + this.btn_Options_SRTM3_Copyright.Location = new System.Drawing.Point(534, 16); + this.btn_Options_SRTM3_Copyright.Name = "btn_Options_SRTM3_Copyright"; + this.btn_Options_SRTM3_Copyright.Size = new System.Drawing.Size(116, 64); + this.btn_Options_SRTM3_Copyright.TabIndex = 18; + this.btn_Options_SRTM3_Copyright.Text = "Licence/Copyright \r\nInformation"; + this.btn_Options_SRTM3_Copyright.UseVisualStyleBackColor = true; + this.btn_Options_SRTM3_Copyright.Click += new System.EventHandler(this.btn_Options_SRTM3_Copyright_Click); + // + // groupBox9 + // + this.groupBox9.Controls.Add(this.gm_Options_SRTM3); + this.groupBox9.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox9.Location = new System.Drawing.Point(3, 86); + this.groupBox9.Name = "groupBox9"; + this.groupBox9.Size = new System.Drawing.Size(650, 368); + this.groupBox9.TabIndex = 13; + this.groupBox9.TabStop = false; + this.groupBox9.Text = "Info"; + // + // gm_Options_SRTM3 + // + this.gm_Options_SRTM3.Bearing = 0F; + this.gm_Options_SRTM3.CanDragMap = true; + this.gm_Options_SRTM3.Dock = System.Windows.Forms.DockStyle.Fill; + this.gm_Options_SRTM3.EmptyTileColor = System.Drawing.Color.Navy; + this.gm_Options_SRTM3.GrayScaleMode = false; + this.gm_Options_SRTM3.HelperLineOption = GMap.NET.WindowsForms.HelperLineOptions.DontShow; + this.gm_Options_SRTM3.LevelsKeepInMemmory = 5; + this.gm_Options_SRTM3.Location = new System.Drawing.Point(3, 16); + this.gm_Options_SRTM3.MarkersEnabled = true; + this.gm_Options_SRTM3.MaxZoom = 2; + this.gm_Options_SRTM3.MinZoom = 2; + this.gm_Options_SRTM3.MouseWheelZoomType = GMap.NET.MouseWheelZoomType.MousePositionAndCenter; + this.gm_Options_SRTM3.Name = "gm_Options_SRTM3"; + this.gm_Options_SRTM3.NegativeMode = false; + this.gm_Options_SRTM3.PolygonsEnabled = true; + this.gm_Options_SRTM3.RetryLoadTile = 0; + this.gm_Options_SRTM3.RoutesEnabled = true; + this.gm_Options_SRTM3.SelectedAreaFillColor = System.Drawing.Color.FromArgb(((int)(((byte)(33)))), ((int)(((byte)(65)))), ((int)(((byte)(105)))), ((int)(((byte)(225))))); + this.gm_Options_SRTM3.ShowTileGridLines = false; + this.gm_Options_SRTM3.Size = new System.Drawing.Size(644, 349); + this.gm_Options_SRTM3.TabIndex = 15; + this.gm_Options_SRTM3.Zoom = 0D; + // + // groupBox8 + // + this.groupBox8.Controls.Add(this.cb_Options_Elevation_SRTM3_EnableCache); + this.groupBox8.Controls.Add(this.cb_Options_Elevation_SRTM3); + this.groupBox8.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox8.Location = new System.Drawing.Point(3, 11); + this.groupBox8.Name = "groupBox8"; + this.groupBox8.Size = new System.Drawing.Size(194, 69); + this.groupBox8.TabIndex = 12; + this.groupBox8.TabStop = false; + this.groupBox8.Text = "Use Elevation Model"; + // + // cb_Options_Elevation_SRTM3_EnableCache + // + this.cb_Options_Elevation_SRTM3_EnableCache.AutoSize = true; + this.cb_Options_Elevation_SRTM3_EnableCache.Checked = global::AirScout.Properties.Settings.Default.Elevation_SRTM3_EnableCache; + this.cb_Options_Elevation_SRTM3_EnableCache.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Elevation_SRTM3_EnableCache", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Options_Elevation_SRTM3_EnableCache.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Options_Elevation_SRTM3_EnableCache.Location = new System.Drawing.Point(6, 42); + this.cb_Options_Elevation_SRTM3_EnableCache.Name = "cb_Options_Elevation_SRTM3_EnableCache"; + this.cb_Options_Elevation_SRTM3_EnableCache.Size = new System.Drawing.Size(179, 17); + this.cb_Options_Elevation_SRTM3_EnableCache.TabIndex = 13; + this.cb_Options_Elevation_SRTM3_EnableCache.Tag = ""; + this.cb_Options_Elevation_SRTM3_EnableCache.Text = "Keep downloaded elevation tiles"; + this.cb_Options_Elevation_SRTM3_EnableCache.UseVisualStyleBackColor = true; + // + // cb_Options_Elevation_SRTM3 + // + this.cb_Options_Elevation_SRTM3.AutoSize = true; + this.cb_Options_Elevation_SRTM3.Checked = global::AirScout.Properties.Settings.Default.Elevation_SRTM3_Enabled; + this.cb_Options_Elevation_SRTM3.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Elevation_SRTM3_Enabled", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Options_Elevation_SRTM3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Options_Elevation_SRTM3.Location = new System.Drawing.Point(6, 19); + this.cb_Options_Elevation_SRTM3.Name = "cb_Options_Elevation_SRTM3"; + this.cb_Options_Elevation_SRTM3.Size = new System.Drawing.Size(155, 17); + this.cb_Options_Elevation_SRTM3.TabIndex = 12; + this.cb_Options_Elevation_SRTM3.Tag = ""; + this.cb_Options_Elevation_SRTM3.Text = "Use SRTM3 elevation data"; + this.cb_Options_Elevation_SRTM3.UseVisualStyleBackColor = true; + // + // tab_Options_GLOBE + // + this.tab_Options_GLOBE.BackColor = System.Drawing.SystemColors.Control; + this.tab_Options_GLOBE.Controls.Add(this.groupBox41); + this.tab_Options_GLOBE.Controls.Add(this.btn_Options_GLOBE_Copyright); + this.tab_Options_GLOBE.Controls.Add(this.groupBox11); + this.tab_Options_GLOBE.Controls.Add(this.groupBox10); + this.tab_Options_GLOBE.Location = new System.Drawing.Point(4, 22); + this.tab_Options_GLOBE.Name = "tab_Options_GLOBE"; + this.tab_Options_GLOBE.Size = new System.Drawing.Size(671, 460); + this.tab_Options_GLOBE.TabIndex = 6; + this.tab_Options_GLOBE.Text = "GLOBE"; + this.tab_Options_GLOBE.Enter += new System.EventHandler(this.tab_Options_GLOBE_Enter); + this.tab_Options_GLOBE.Leave += new System.EventHandler(this.tab_Options_GLOBE_Leave); + // + // groupBox41 + // + this.groupBox41.BackColor = System.Drawing.Color.White; + this.groupBox41.Controls.Add(this.label99); + this.groupBox41.Controls.Add(this.label98); + this.groupBox41.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox41.Location = new System.Drawing.Point(211, 9); + this.groupBox41.Name = "groupBox41"; + this.groupBox41.Size = new System.Drawing.Size(312, 69); + this.groupBox41.TabIndex = 17; + this.groupBox41.TabStop = false; + this.groupBox41.Text = "(G)lobal (L)and (O)ne-km (B)ase (E)levation Project"; + // + // label99 + // + this.label99.AutoSize = true; + this.label99.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label99.Location = new System.Drawing.Point(14, 47); + this.label99.Name = "label99"; + this.label99.Size = new System.Drawing.Size(283, 13); + this.label99.TabIndex = 1; + this.label99.Text = "Recommed for basic usage: Fast calculation, low precision"; + // + // label98 + // + this.label98.AutoSize = true; + this.label98.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label98.Location = new System.Drawing.Point(44, 17); + this.label98.Name = "label98"; + this.label98.Size = new System.Drawing.Size(205, 26); + this.label98.TabIndex = 0; + this.label98.Text = "A 30 arcsec (1km) grided elevation model.\r\nCoverage: 90°S ... 90°N; 180°W ... 180" + + "°E"; + // + // btn_Options_GLOBE_Copyright + // + this.btn_Options_GLOBE_Copyright.Location = new System.Drawing.Point(529, 14); + this.btn_Options_GLOBE_Copyright.Name = "btn_Options_GLOBE_Copyright"; + this.btn_Options_GLOBE_Copyright.Size = new System.Drawing.Size(116, 64); + this.btn_Options_GLOBE_Copyright.TabIndex = 16; + this.btn_Options_GLOBE_Copyright.Text = "Licence/Copyright \r\nInformation"; + this.btn_Options_GLOBE_Copyright.UseVisualStyleBackColor = true; + this.btn_Options_GLOBE_Copyright.Click += new System.EventHandler(this.btn_Options_GLOBE_Copyright_Click); + // + // groupBox11 + // + this.groupBox11.Controls.Add(this.gm_Options_GLOBE); + this.groupBox11.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox11.Location = new System.Drawing.Point(8, 84); + this.groupBox11.Name = "groupBox11"; + this.groupBox11.Size = new System.Drawing.Size(640, 367); + this.groupBox11.TabIndex = 14; + this.groupBox11.TabStop = false; + this.groupBox11.Text = "Info"; + // + // gm_Options_GLOBE + // + this.gm_Options_GLOBE.Bearing = 0F; + this.gm_Options_GLOBE.CanDragMap = true; + this.gm_Options_GLOBE.Dock = System.Windows.Forms.DockStyle.Fill; + this.gm_Options_GLOBE.EmptyTileColor = System.Drawing.Color.Navy; + this.gm_Options_GLOBE.GrayScaleMode = false; + this.gm_Options_GLOBE.HelperLineOption = GMap.NET.WindowsForms.HelperLineOptions.DontShow; + this.gm_Options_GLOBE.LevelsKeepInMemmory = 5; + this.gm_Options_GLOBE.Location = new System.Drawing.Point(3, 16); + this.gm_Options_GLOBE.MarkersEnabled = true; + this.gm_Options_GLOBE.MaxZoom = 2; + this.gm_Options_GLOBE.MinZoom = 2; + this.gm_Options_GLOBE.MouseWheelZoomType = GMap.NET.MouseWheelZoomType.MousePositionAndCenter; + this.gm_Options_GLOBE.Name = "gm_Options_GLOBE"; + this.gm_Options_GLOBE.NegativeMode = false; + this.gm_Options_GLOBE.PolygonsEnabled = true; + this.gm_Options_GLOBE.RetryLoadTile = 0; + this.gm_Options_GLOBE.RoutesEnabled = true; + this.gm_Options_GLOBE.SelectedAreaFillColor = System.Drawing.Color.FromArgb(((int)(((byte)(33)))), ((int)(((byte)(65)))), ((int)(((byte)(105)))), ((int)(((byte)(225))))); + this.gm_Options_GLOBE.ShowTileGridLines = false; + this.gm_Options_GLOBE.Size = new System.Drawing.Size(634, 348); + this.gm_Options_GLOBE.TabIndex = 15; + this.gm_Options_GLOBE.Zoom = 0D; + // + // groupBox10 + // + this.groupBox10.Controls.Add(this.cb_Options_Elevation_GLOBE_EnableCache); + this.groupBox10.Controls.Add(this.cb_Options_Elevation_GLOBE); + this.groupBox10.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox10.Location = new System.Drawing.Point(3, 9); + this.groupBox10.Name = "groupBox10"; + this.groupBox10.Size = new System.Drawing.Size(202, 69); + this.groupBox10.TabIndex = 13; + this.groupBox10.TabStop = false; + this.groupBox10.Text = "Use Elevation Model"; + // + // cb_Options_Elevation_GLOBE_EnableCache + // + this.cb_Options_Elevation_GLOBE_EnableCache.AutoSize = true; + this.cb_Options_Elevation_GLOBE_EnableCache.Checked = global::AirScout.Properties.Settings.Default.Elevation_GLOBE_EnableCache; + this.cb_Options_Elevation_GLOBE_EnableCache.CheckState = System.Windows.Forms.CheckState.Checked; + this.cb_Options_Elevation_GLOBE_EnableCache.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Elevation_GLOBE_EnableCache", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Options_Elevation_GLOBE_EnableCache.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Options_Elevation_GLOBE_EnableCache.Location = new System.Drawing.Point(12, 42); + this.cb_Options_Elevation_GLOBE_EnableCache.Name = "cb_Options_Elevation_GLOBE_EnableCache"; + this.cb_Options_Elevation_GLOBE_EnableCache.Size = new System.Drawing.Size(179, 17); + this.cb_Options_Elevation_GLOBE_EnableCache.TabIndex = 8; + this.cb_Options_Elevation_GLOBE_EnableCache.Tag = ""; + this.cb_Options_Elevation_GLOBE_EnableCache.Text = "Keep downloaded elevation tiles"; + this.cb_Options_Elevation_GLOBE_EnableCache.UseVisualStyleBackColor = true; + // + // cb_Options_Elevation_GLOBE + // + this.cb_Options_Elevation_GLOBE.AutoSize = true; + this.cb_Options_Elevation_GLOBE.Checked = global::AirScout.Properties.Settings.Default.Elevation_GLOBE_Enabled; + this.cb_Options_Elevation_GLOBE.CheckState = System.Windows.Forms.CheckState.Checked; + this.cb_Options_Elevation_GLOBE.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Elevation_GLOBE_Enabled", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Options_Elevation_GLOBE.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Options_Elevation_GLOBE.Location = new System.Drawing.Point(12, 19); + this.cb_Options_Elevation_GLOBE.Name = "cb_Options_Elevation_GLOBE"; + this.cb_Options_Elevation_GLOBE.Size = new System.Drawing.Size(154, 17); + this.cb_Options_Elevation_GLOBE.TabIndex = 7; + this.cb_Options_Elevation_GLOBE.Tag = ""; + this.cb_Options_Elevation_GLOBE.Text = "Use GLOBE elevation data"; + this.cb_Options_Elevation_GLOBE.UseVisualStyleBackColor = true; + // + // tab_Options_Map + // + this.tab_Options_Map.BackColor = System.Drawing.SystemColors.Control; + this.tab_Options_Map.Controls.Add(this.groupBox39); + this.tab_Options_Map.Controls.Add(this.groupBox23); + this.tab_Options_Map.Controls.Add(this.groupBox30); + this.tab_Options_Map.Controls.Add(this.groupBox7); + this.tab_Options_Map.Controls.Add(this.groupBox22); + this.tab_Options_Map.Controls.Add(this.groupBox2); + this.tab_Options_Map.Location = new System.Drawing.Point(4, 22); + this.tab_Options_Map.Name = "tab_Options_Map"; + this.tab_Options_Map.Size = new System.Drawing.Size(671, 460); + this.tab_Options_Map.TabIndex = 2; + this.tab_Options_Map.Text = "Map"; + this.tab_Options_Map.Enter += new System.EventHandler(this.tab_Options_Map_Enter); + this.tab_Options_Map.Validating += new System.ComponentModel.CancelEventHandler(this.tab_Options_Map_Validating); + // + // groupBox39 + // + this.groupBox39.Controls.Add(this.tb_Options_Map_Update_Interval); + this.groupBox39.Controls.Add(this.label97); + this.groupBox39.Controls.Add(this.label29); + this.groupBox39.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox39.Location = new System.Drawing.Point(415, 156); + this.groupBox39.Name = "groupBox39"; + this.groupBox39.Size = new System.Drawing.Size(242, 64); + this.groupBox39.TabIndex = 8; + this.groupBox39.TabStop = false; + this.groupBox39.Text = "Screen Updates"; + // + // tb_Options_Map_Update_Interval + // + this.tb_Options_Map_Update_Interval.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScout.Properties.Settings.Default, "Map_Update", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Options_Map_Update_Interval.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Map_Update_Interval.FormatSpecifier = "F0"; + this.tb_Options_Map_Update_Interval.Location = new System.Drawing.Point(121, 18); + this.tb_Options_Map_Update_Interval.MaxValue = 3600; + this.tb_Options_Map_Update_Interval.MinValue = 0; + this.tb_Options_Map_Update_Interval.Name = "tb_Options_Map_Update_Interval"; + this.tb_Options_Map_Update_Interval.Size = new System.Drawing.Size(37, 22); + this.tb_Options_Map_Update_Interval.TabIndex = 23; + this.tb_Options_Map_Update_Interval.Text = "1"; + this.tb_Options_Map_Update_Interval.Value = global::AirScout.Properties.Settings.Default.Map_Update; + // + // label97 + // + this.label97.AutoSize = true; + this.label97.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label97.Location = new System.Drawing.Point(173, 21); + this.label97.Name = "label97"; + this.label97.Size = new System.Drawing.Size(56, 13); + this.label97.TabIndex = 22; + this.label97.Text = "second(s)."; + // + // label29 + // + this.label29.AutoSize = true; + this.label29.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label29.Location = new System.Drawing.Point(6, 21); + this.label29.Name = "label29"; + this.label29.Size = new System.Drawing.Size(109, 13); + this.label29.TabIndex = 20; + this.label29.Text = "Update screen every "; + // + // groupBox23 + // + this.groupBox23.Controls.Add(this.cb_Options_Map_SmallMarkers); + this.groupBox23.Controls.Add(this.cb_Options_Watchlist_Activate); + this.groupBox23.Controls.Add(this.cb_Options_Airports_Activate); + this.groupBox23.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox23.Location = new System.Drawing.Point(14, 156); + this.groupBox23.Name = "groupBox23"; + this.groupBox23.Size = new System.Drawing.Size(394, 64); + this.groupBox23.TabIndex = 7; + this.groupBox23.TabStop = false; + this.groupBox23.Text = "General "; + // + // cb_Options_Map_SmallMarkers + // + this.cb_Options_Map_SmallMarkers.AutoSize = true; + this.cb_Options_Map_SmallMarkers.Checked = global::AirScout.Properties.Settings.Default.Map_SmallMarkers; + this.cb_Options_Map_SmallMarkers.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Map_SmallMarkers", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Options_Map_SmallMarkers.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Options_Map_SmallMarkers.Location = new System.Drawing.Point(24, 41); + this.cb_Options_Map_SmallMarkers.Name = "cb_Options_Map_SmallMarkers"; + this.cb_Options_Map_SmallMarkers.Size = new System.Drawing.Size(268, 17); + this.cb_Options_Map_SmallMarkers.TabIndex = 2; + this.cb_Options_Map_SmallMarkers.Text = "Small Markers in Map (better visability for multi-path)"; + this.cb_Options_Map_SmallMarkers.UseVisualStyleBackColor = true; + // + // cb_Options_Watchlist_Activate + // + this.cb_Options_Watchlist_Activate.AutoSize = true; + this.cb_Options_Watchlist_Activate.Checked = global::AirScout.Properties.Settings.Default.Watchlist_Activated; + this.cb_Options_Watchlist_Activate.CheckState = System.Windows.Forms.CheckState.Checked; + this.cb_Options_Watchlist_Activate.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Watchlist_Activated", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Options_Watchlist_Activate.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Options_Watchlist_Activate.Location = new System.Drawing.Point(122, 18); + this.cb_Options_Watchlist_Activate.Name = "cb_Options_Watchlist_Activate"; + this.cb_Options_Watchlist_Activate.Size = new System.Drawing.Size(148, 17); + this.cb_Options_Watchlist_Activate.TabIndex = 1; + this.cb_Options_Watchlist_Activate.Text = "Show Calls from Watchlist"; + this.cb_Options_Watchlist_Activate.UseVisualStyleBackColor = true; + // + // cb_Options_Airports_Activate + // + this.cb_Options_Airports_Activate.AutoSize = true; + this.cb_Options_Airports_Activate.Checked = global::AirScout.Properties.Settings.Default.Airports_Activate; + this.cb_Options_Airports_Activate.CheckState = System.Windows.Forms.CheckState.Checked; + this.cb_Options_Airports_Activate.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Airports_Activate", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Options_Airports_Activate.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Options_Airports_Activate.Location = new System.Drawing.Point(25, 19); + this.cb_Options_Airports_Activate.Name = "cb_Options_Airports_Activate"; + this.cb_Options_Airports_Activate.Size = new System.Drawing.Size(91, 17); + this.cb_Options_Airports_Activate.TabIndex = 0; + this.cb_Options_Airports_Activate.Text = "Show Airports"; + this.cb_Options_Airports_Activate.UseVisualStyleBackColor = true; + // + // groupBox30 + // + this.groupBox30.Controls.Add(this.tb_Options_Planes_IconSize_S); + this.groupBox30.Controls.Add(this.tb_Options_Planes_IconSize_H); + this.groupBox30.Controls.Add(this.tb_Options_Planes_IconSize_M); + this.groupBox30.Controls.Add(this.tb_Options_Planes_IconSize_L); + this.groupBox30.Controls.Add(this.label84); + this.groupBox30.Controls.Add(this.label83); + this.groupBox30.Controls.Add(this.label82); + this.groupBox30.Controls.Add(this.label81); + this.groupBox30.Controls.Add(this.label80); + this.groupBox30.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox30.Location = new System.Drawing.Point(446, 301); + this.groupBox30.Name = "groupBox30"; + this.groupBox30.Size = new System.Drawing.Size(211, 154); + this.groupBox30.TabIndex = 6; + this.groupBox30.TabStop = false; + this.groupBox30.Text = "Plane Icon Sizes per Category"; + // + // tb_Options_Planes_IconSize_S + // + this.tb_Options_Planes_IconSize_S.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScout.Properties.Settings.Default, "Planes_IconSize_S", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Options_Planes_IconSize_S.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Planes_IconSize_S.FormatSpecifier = "F0"; + this.tb_Options_Planes_IconSize_S.Location = new System.Drawing.Point(81, 103); + this.tb_Options_Planes_IconSize_S.MaxValue = 128; + this.tb_Options_Planes_IconSize_S.MinValue = 0; + this.tb_Options_Planes_IconSize_S.Name = "tb_Options_Planes_IconSize_S"; + this.tb_Options_Planes_IconSize_S.Size = new System.Drawing.Size(37, 22); + this.tb_Options_Planes_IconSize_S.TabIndex = 16; + this.tb_Options_Planes_IconSize_S.Text = "48"; + this.tb_Options_Planes_IconSize_S.Value = global::AirScout.Properties.Settings.Default.Planes_IconSize_S; + // + // tb_Options_Planes_IconSize_H + // + this.tb_Options_Planes_IconSize_H.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScout.Properties.Settings.Default, "Planes_IconSize_H", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Options_Planes_IconSize_H.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Planes_IconSize_H.FormatSpecifier = "F0"; + this.tb_Options_Planes_IconSize_H.Location = new System.Drawing.Point(81, 78); + this.tb_Options_Planes_IconSize_H.MaxValue = 128; + this.tb_Options_Planes_IconSize_H.MinValue = 0; + this.tb_Options_Planes_IconSize_H.Name = "tb_Options_Planes_IconSize_H"; + this.tb_Options_Planes_IconSize_H.Size = new System.Drawing.Size(37, 22); + this.tb_Options_Planes_IconSize_H.TabIndex = 15; + this.tb_Options_Planes_IconSize_H.Text = "36"; + this.tb_Options_Planes_IconSize_H.Value = global::AirScout.Properties.Settings.Default.Planes_IconSize_H; + // + // tb_Options_Planes_IconSize_M + // + this.tb_Options_Planes_IconSize_M.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScout.Properties.Settings.Default, "Planes_IconSize_M", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Options_Planes_IconSize_M.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Planes_IconSize_M.FormatSpecifier = "F0"; + this.tb_Options_Planes_IconSize_M.Location = new System.Drawing.Point(81, 53); + this.tb_Options_Planes_IconSize_M.MaxValue = 128; + this.tb_Options_Planes_IconSize_M.MinValue = 0; + this.tb_Options_Planes_IconSize_M.Name = "tb_Options_Planes_IconSize_M"; + this.tb_Options_Planes_IconSize_M.Size = new System.Drawing.Size(37, 22); + this.tb_Options_Planes_IconSize_M.TabIndex = 14; + this.tb_Options_Planes_IconSize_M.Text = "24"; + this.tb_Options_Planes_IconSize_M.Value = global::AirScout.Properties.Settings.Default.Planes_IconSize_M; + // + // tb_Options_Planes_IconSize_L + // + this.tb_Options_Planes_IconSize_L.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScout.Properties.Settings.Default, "Planes_IconSize_L", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Options_Planes_IconSize_L.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Planes_IconSize_L.FormatSpecifier = "F0"; + this.tb_Options_Planes_IconSize_L.Location = new System.Drawing.Point(81, 28); + this.tb_Options_Planes_IconSize_L.MaxValue = 128; + this.tb_Options_Planes_IconSize_L.MinValue = 0; + this.tb_Options_Planes_IconSize_L.Name = "tb_Options_Planes_IconSize_L"; + this.tb_Options_Planes_IconSize_L.Size = new System.Drawing.Size(37, 22); + this.tb_Options_Planes_IconSize_L.TabIndex = 13; + this.tb_Options_Planes_IconSize_L.Text = "16"; + this.tb_Options_Planes_IconSize_L.Value = global::AirScout.Properties.Settings.Default.Planes_IconSize_L; + // + // label84 + // + this.label84.AutoSize = true; + this.label84.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label84.Location = new System.Drawing.Point(37, 128); + this.label84.Name = "label84"; + this.label84.Size = new System.Drawing.Size(137, 13); + this.label84.TabIndex = 12; + this.label84.Text = "Restart of AirScout required"; + // + // label83 + // + this.label83.AutoSize = true; + this.label83.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label83.Location = new System.Drawing.Point(23, 59); + this.label83.Name = "label83"; + this.label83.Size = new System.Drawing.Size(47, 13); + this.label83.TabIndex = 7; + this.label83.Text = "Medium:"; + // + // label82 + // + this.label82.AutoSize = true; + this.label82.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label82.Location = new System.Drawing.Point(24, 83); + this.label82.Name = "label82"; + this.label82.Size = new System.Drawing.Size(41, 13); + this.label82.TabIndex = 6; + this.label82.Text = "Heavy:"; + // + // label81 + // + this.label81.AutoSize = true; + this.label81.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label81.Location = new System.Drawing.Point(24, 108); + this.label81.Name = "label81"; + this.label81.Size = new System.Drawing.Size(38, 13); + this.label81.TabIndex = 5; + this.label81.Text = "Super:"; + // + // label80 + // + this.label80.AutoSize = true; + this.label80.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label80.Location = new System.Drawing.Point(23, 32); + this.label80.Name = "label80"; + this.label80.Size = new System.Drawing.Size(33, 13); + this.label80.TabIndex = 4; + this.label80.Text = "Light:"; + // + // groupBox7 + // + this.groupBox7.Controls.Add(this.groupBox29); + this.groupBox7.Controls.Add(this.cb_Options_InfoWin_Angle); + this.groupBox7.Controls.Add(this.cb_Options_InfoWin_Speed); + this.groupBox7.Controls.Add(this.cb_Options_InfoWin_Squint); + this.groupBox7.Controls.Add(this.cb_Options_InfoWin_Epsilon); + this.groupBox7.Controls.Add(this.cb_Options_InfoWin_Dist); + this.groupBox7.Controls.Add(this.cb_Options_InfoWin_Time); + this.groupBox7.Controls.Add(this.cb_Options_InfoWin_Type); + this.groupBox7.Controls.Add(this.cb_Options_InfoWin_Track); + this.groupBox7.Controls.Add(this.cb_Options_InfoWin_Alt); + this.groupBox7.Controls.Add(this.cb_Options_InfoWin_Position); + this.groupBox7.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox7.Location = new System.Drawing.Point(14, 301); + this.groupBox7.Name = "groupBox7"; + this.groupBox7.Size = new System.Drawing.Size(415, 154); + this.groupBox7.TabIndex = 5; + this.groupBox7.TabStop = false; + this.groupBox7.Text = "Info Window Elements"; + // + // groupBox29 + // + this.groupBox29.Controls.Add(this.label76); + this.groupBox29.Controls.Add(this.label74); + this.groupBox29.Controls.Add(this.label75); + this.groupBox29.Location = new System.Drawing.Point(256, 19); + this.groupBox29.Name = "groupBox29"; + this.groupBox29.Size = new System.Drawing.Size(139, 123); + this.groupBox29.TabIndex = 19; + this.groupBox29.TabStop = false; + // + // label76 + // + this.label76.AutoSize = true; + this.label76.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label76.Location = new System.Drawing.Point(10, 41); + this.label76.Name = "label76"; + this.label76.Size = new System.Drawing.Size(116, 16); + this.label76.TabIndex = 18; + this.label76.Text = "bold characters"; + // + // label74 + // + this.label74.AutoSize = true; + this.label74.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label74.Location = new System.Drawing.Point(10, 19); + this.label74.Name = "label74"; + this.label74.Size = new System.Drawing.Size(88, 13); + this.label74.TabIndex = 16; + this.label74.Text = "Checkboxes with"; + // + // label75 + // + this.label75.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label75.Location = new System.Drawing.Point(10, 65); + this.label75.Name = "label75"; + this.label75.Size = new System.Drawing.Size(116, 41); + this.label75.TabIndex = 17; + this.label75.Text = "are affecting both Simple and Detailed Info Window."; + // + // cb_Options_InfoWin_Angle + // + this.cb_Options_InfoWin_Angle.AutoSize = true; + this.cb_Options_InfoWin_Angle.Checked = global::AirScout.Properties.Settings.Default.InfoWin_Angle; + this.cb_Options_InfoWin_Angle.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "InfoWin_Angle", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Options_InfoWin_Angle.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Options_InfoWin_Angle.Location = new System.Drawing.Point(139, 75); + this.cb_Options_InfoWin_Angle.Name = "cb_Options_InfoWin_Angle"; + this.cb_Options_InfoWin_Angle.Size = new System.Drawing.Size(96, 17); + this.cb_Options_InfoWin_Angle.TabIndex = 15; + this.cb_Options_InfoWin_Angle.Tag = ""; + this.cb_Options_InfoWin_Angle.Text = "Crossing Angle"; + this.cb_Options_InfoWin_Angle.UseVisualStyleBackColor = true; + // + // cb_Options_InfoWin_Speed + // + this.cb_Options_InfoWin_Speed.AutoSize = true; + this.cb_Options_InfoWin_Speed.Checked = global::AirScout.Properties.Settings.Default.InfoWin_Speed; + this.cb_Options_InfoWin_Speed.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "InfoWin_Speed", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Options_InfoWin_Speed.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Options_InfoWin_Speed.Location = new System.Drawing.Point(26, 100); + this.cb_Options_InfoWin_Speed.Name = "cb_Options_InfoWin_Speed"; + this.cb_Options_InfoWin_Speed.Size = new System.Drawing.Size(87, 17); + this.cb_Options_InfoWin_Speed.TabIndex = 14; + this.cb_Options_InfoWin_Speed.Tag = ""; + this.cb_Options_InfoWin_Speed.Text = "Plane Speed"; + this.cb_Options_InfoWin_Speed.UseVisualStyleBackColor = true; + // + // cb_Options_InfoWin_Squint + // + this.cb_Options_InfoWin_Squint.AutoSize = true; + this.cb_Options_InfoWin_Squint.Checked = global::AirScout.Properties.Settings.Default.InfoWin_Squint; + this.cb_Options_InfoWin_Squint.CheckState = System.Windows.Forms.CheckState.Checked; + this.cb_Options_InfoWin_Squint.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "InfoWin_Squint", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Options_InfoWin_Squint.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Options_InfoWin_Squint.Location = new System.Drawing.Point(139, 121); + this.cb_Options_InfoWin_Squint.Name = "cb_Options_InfoWin_Squint"; + this.cb_Options_InfoWin_Squint.Size = new System.Drawing.Size(86, 17); + this.cb_Options_InfoWin_Squint.TabIndex = 13; + this.cb_Options_InfoWin_Squint.Tag = ""; + this.cb_Options_InfoWin_Squint.Text = "Squint Angle"; + this.cb_Options_InfoWin_Squint.UseVisualStyleBackColor = true; + // + // cb_Options_InfoWin_Epsilon + // + this.cb_Options_InfoWin_Epsilon.AutoSize = true; + this.cb_Options_InfoWin_Epsilon.Checked = global::AirScout.Properties.Settings.Default.InfoWin_Epsilon; + this.cb_Options_InfoWin_Epsilon.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "InfoWin_Epsilon", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Options_InfoWin_Epsilon.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Options_InfoWin_Epsilon.Location = new System.Drawing.Point(139, 98); + this.cb_Options_InfoWin_Epsilon.Name = "cb_Options_InfoWin_Epsilon"; + this.cb_Options_InfoWin_Epsilon.Size = new System.Drawing.Size(105, 17); + this.cb_Options_InfoWin_Epsilon.TabIndex = 12; + this.cb_Options_InfoWin_Epsilon.Tag = ""; + this.cb_Options_InfoWin_Epsilon.Text = "Elevation Angles"; + this.cb_Options_InfoWin_Epsilon.UseVisualStyleBackColor = true; + // + // cb_Options_InfoWin_Dist + // + this.cb_Options_InfoWin_Dist.AutoSize = true; + this.cb_Options_InfoWin_Dist.Checked = global::AirScout.Properties.Settings.Default.InfoWin_Dist; + this.cb_Options_InfoWin_Dist.CheckState = System.Windows.Forms.CheckState.Checked; + this.cb_Options_InfoWin_Dist.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "InfoWin_Dist", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Options_InfoWin_Dist.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Options_InfoWin_Dist.Location = new System.Drawing.Point(139, 29); + this.cb_Options_InfoWin_Dist.Name = "cb_Options_InfoWin_Dist"; + this.cb_Options_InfoWin_Dist.Size = new System.Drawing.Size(111, 17); + this.cb_Options_InfoWin_Dist.TabIndex = 11; + this.cb_Options_InfoWin_Dist.Tag = ""; + this.cb_Options_InfoWin_Dist.Text = "Crossing Distance"; + this.cb_Options_InfoWin_Dist.UseVisualStyleBackColor = true; + // + // cb_Options_InfoWin_Time + // + this.cb_Options_InfoWin_Time.AutoSize = true; + this.cb_Options_InfoWin_Time.Checked = global::AirScout.Properties.Settings.Default.InfoWin_Time; + this.cb_Options_InfoWin_Time.CheckState = System.Windows.Forms.CheckState.Checked; + this.cb_Options_InfoWin_Time.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "InfoWin_Time", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Options_InfoWin_Time.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Options_InfoWin_Time.Location = new System.Drawing.Point(139, 52); + this.cb_Options_InfoWin_Time.Name = "cb_Options_InfoWin_Time"; + this.cb_Options_InfoWin_Time.Size = new System.Drawing.Size(92, 17); + this.cb_Options_InfoWin_Time.TabIndex = 10; + this.cb_Options_InfoWin_Time.Tag = ""; + this.cb_Options_InfoWin_Time.Text = "Crossing Time"; + this.cb_Options_InfoWin_Time.UseVisualStyleBackColor = true; + // + // cb_Options_InfoWin_Type + // + this.cb_Options_InfoWin_Type.AutoSize = true; + this.cb_Options_InfoWin_Type.Checked = global::AirScout.Properties.Settings.Default.InfoWin_Type; + this.cb_Options_InfoWin_Type.CheckState = System.Windows.Forms.CheckState.Checked; + this.cb_Options_InfoWin_Type.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "InfoWin_Type", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Options_InfoWin_Type.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Options_InfoWin_Type.Location = new System.Drawing.Point(26, 123); + this.cb_Options_InfoWin_Type.Name = "cb_Options_InfoWin_Type"; + this.cb_Options_InfoWin_Type.Size = new System.Drawing.Size(90, 17); + this.cb_Options_InfoWin_Type.TabIndex = 9; + this.cb_Options_InfoWin_Type.Tag = ""; + this.cb_Options_InfoWin_Type.Text = "Plane Type"; + this.cb_Options_InfoWin_Type.UseVisualStyleBackColor = true; + // + // cb_Options_InfoWin_Track + // + this.cb_Options_InfoWin_Track.AutoSize = true; + this.cb_Options_InfoWin_Track.Checked = global::AirScout.Properties.Settings.Default.InfoWin_Track; + this.cb_Options_InfoWin_Track.CheckState = System.Windows.Forms.CheckState.Checked; + this.cb_Options_InfoWin_Track.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "InfoWin_Track", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Options_InfoWin_Track.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Options_InfoWin_Track.Location = new System.Drawing.Point(26, 77); + this.cb_Options_InfoWin_Track.Name = "cb_Options_InfoWin_Track"; + this.cb_Options_InfoWin_Track.Size = new System.Drawing.Size(95, 17); + this.cb_Options_InfoWin_Track.TabIndex = 8; + this.cb_Options_InfoWin_Track.Tag = ""; + this.cb_Options_InfoWin_Track.Text = "Plane Track"; + this.cb_Options_InfoWin_Track.UseVisualStyleBackColor = true; + // + // cb_Options_InfoWin_Alt + // + this.cb_Options_InfoWin_Alt.AutoSize = true; + this.cb_Options_InfoWin_Alt.Checked = global::AirScout.Properties.Settings.Default.InfoWin_Alt; + this.cb_Options_InfoWin_Alt.CheckState = System.Windows.Forms.CheckState.Checked; + this.cb_Options_InfoWin_Alt.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "InfoWin_Alt", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Options_InfoWin_Alt.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Options_InfoWin_Alt.Location = new System.Drawing.Point(26, 54); + this.cb_Options_InfoWin_Alt.Name = "cb_Options_InfoWin_Alt"; + this.cb_Options_InfoWin_Alt.Size = new System.Drawing.Size(105, 17); + this.cb_Options_InfoWin_Alt.TabIndex = 7; + this.cb_Options_InfoWin_Alt.Tag = ""; + this.cb_Options_InfoWin_Alt.Text = "Plane Altitude"; + this.cb_Options_InfoWin_Alt.UseVisualStyleBackColor = true; + // + // cb_Options_InfoWin_Position + // + this.cb_Options_InfoWin_Position.AutoSize = true; + this.cb_Options_InfoWin_Position.Checked = global::AirScout.Properties.Settings.Default.InfoWin_Position; + this.cb_Options_InfoWin_Position.CheckState = System.Windows.Forms.CheckState.Checked; + this.cb_Options_InfoWin_Position.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "InfoWin_Position", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Options_InfoWin_Position.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Options_InfoWin_Position.Location = new System.Drawing.Point(26, 31); + this.cb_Options_InfoWin_Position.Name = "cb_Options_InfoWin_Position"; + this.cb_Options_InfoWin_Position.Size = new System.Drawing.Size(107, 17); + this.cb_Options_InfoWin_Position.TabIndex = 6; + this.cb_Options_InfoWin_Position.Tag = ""; + this.cb_Options_InfoWin_Position.Text = "Plane Position"; + this.cb_Options_InfoWin_Position.UseVisualStyleBackColor = true; + // + // groupBox22 + // + this.groupBox22.Controls.Add(this.rb_Options_InfoWin_Imperial); + this.groupBox22.Controls.Add(this.rb_Options_InfoWin_Metric); + this.groupBox22.Controls.Add(this.label72); + this.groupBox22.Controls.Add(this.btn_Options_SelectFont); + this.groupBox22.Controls.Add(this.label62); + this.groupBox22.Controls.Add(this.tb_Options_Map_ToolTipFont); + this.groupBox22.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox22.Location = new System.Drawing.Point(13, 226); + this.groupBox22.Name = "groupBox22"; + this.groupBox22.Size = new System.Drawing.Size(644, 69); + this.groupBox22.TabIndex = 4; + this.groupBox22.TabStop = false; + this.groupBox22.Text = "Info Window Options"; + // + // rb_Options_InfoWin_Imperial + // + this.rb_Options_InfoWin_Imperial.AutoSize = true; + this.rb_Options_InfoWin_Imperial.Checked = global::AirScout.Properties.Settings.Default.InfoWin_Imperial; + this.rb_Options_InfoWin_Imperial.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "InfoWin_Imperial", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.rb_Options_InfoWin_Imperial.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.rb_Options_InfoWin_Imperial.Location = new System.Drawing.Point(156, 18); + this.rb_Options_InfoWin_Imperial.Name = "rb_Options_InfoWin_Imperial"; + this.rb_Options_InfoWin_Imperial.Size = new System.Drawing.Size(61, 17); + this.rb_Options_InfoWin_Imperial.TabIndex = 5; + this.rb_Options_InfoWin_Imperial.Tag = ""; + this.rb_Options_InfoWin_Imperial.Text = "Imperial"; + this.rb_Options_InfoWin_Imperial.UseVisualStyleBackColor = true; + // + // rb_Options_InfoWin_Metric + // + this.rb_Options_InfoWin_Metric.AutoSize = true; + this.rb_Options_InfoWin_Metric.Checked = global::AirScout.Properties.Settings.Default.InfoWin_Metric; + this.rb_Options_InfoWin_Metric.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "InfoWin_Metric", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.rb_Options_InfoWin_Metric.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.rb_Options_InfoWin_Metric.Location = new System.Drawing.Point(83, 18); + this.rb_Options_InfoWin_Metric.Name = "rb_Options_InfoWin_Metric"; + this.rb_Options_InfoWin_Metric.Size = new System.Drawing.Size(54, 17); + this.rb_Options_InfoWin_Metric.TabIndex = 4; + this.rb_Options_InfoWin_Metric.TabStop = true; + this.rb_Options_InfoWin_Metric.Tag = ""; + this.rb_Options_InfoWin_Metric.Text = "Metric"; + this.rb_Options_InfoWin_Metric.UseVisualStyleBackColor = true; + // + // label72 + // + this.label72.AutoSize = true; + this.label72.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label72.Location = new System.Drawing.Point(25, 22); + this.label72.Name = "label72"; + this.label72.Size = new System.Drawing.Size(34, 13); + this.label72.TabIndex = 3; + this.label72.Text = "Units:"; + // + // btn_Options_SelectFont + // + this.btn_Options_SelectFont.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Options_SelectFont.Location = new System.Drawing.Point(514, 37); + this.btn_Options_SelectFont.Name = "btn_Options_SelectFont"; + this.btn_Options_SelectFont.Size = new System.Drawing.Size(106, 23); + this.btn_Options_SelectFont.TabIndex = 2; + this.btn_Options_SelectFont.Text = "Select Font"; + this.btn_Options_SelectFont.UseVisualStyleBackColor = true; + this.btn_Options_SelectFont.Click += new System.EventHandler(this.btn_Options_SelectFont_Click); + // + // label62 + // + this.label62.AutoSize = true; + this.label62.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label62.Location = new System.Drawing.Point(25, 42); + this.label62.Name = "label62"; + this.label62.Size = new System.Drawing.Size(31, 13); + this.label62.TabIndex = 1; + this.label62.Text = "Font:"; + // + // tb_Options_Map_ToolTipFont + // + this.tb_Options_Map_ToolTipFont.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::AirScout.Properties.Settings.Default, "Map_ToolTipFont", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Options_Map_ToolTipFont.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Map_ToolTipFont.Location = new System.Drawing.Point(72, 39); + this.tb_Options_Map_ToolTipFont.Name = "tb_Options_Map_ToolTipFont"; + this.tb_Options_Map_ToolTipFont.ReadOnly = true; + this.tb_Options_Map_ToolTipFont.Size = new System.Drawing.Size(417, 20); + this.tb_Options_Map_ToolTipFont.TabIndex = 0; + this.tb_Options_Map_ToolTipFont.Text = global::AirScout.Properties.Settings.Default.Map_ToolTipFont; + // + // groupBox2 + // + this.groupBox2.Controls.Add(this.label61); + this.groupBox2.Controls.Add(this.cb_Options_Map_Provider); + this.groupBox2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox2.Location = new System.Drawing.Point(13, 10); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Size = new System.Drawing.Size(644, 141); + this.groupBox2.TabIndex = 3; + this.groupBox2.TabStop = false; + this.groupBox2.Text = "Map Source"; + // + // label61 + // + this.label61.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label61.Location = new System.Drawing.Point(26, 17); + this.label61.Name = "label61"; + this.label61.Size = new System.Drawing.Size(612, 87); + this.label61.TabIndex = 1; + this.label61.Text = resources.GetString("label61.Text"); + this.label61.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // cb_Options_Map_Provider + // + this.cb_Options_Map_Provider.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cb_Options_Map_Provider.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Options_Map_Provider.FormattingEnabled = true; + this.cb_Options_Map_Provider.Location = new System.Drawing.Point(218, 107); + this.cb_Options_Map_Provider.Name = "cb_Options_Map_Provider"; + this.cb_Options_Map_Provider.Size = new System.Drawing.Size(210, 21); + this.cb_Options_Map_Provider.TabIndex = 0; + this.cb_Options_Map_Provider.DropDown += new System.EventHandler(this.cb_Options_Map_Provider_DropDown); + // + // tab_Options_Stations + // + this.tab_Options_Stations.BackColor = System.Drawing.SystemColors.Control; + this.tab_Options_Stations.Controls.Add(this.groupBox18); + this.tab_Options_Stations.Controls.Add(this.groupBox46); + this.tab_Options_Stations.Controls.Add(this.groupBox45); + this.tab_Options_Stations.Controls.Add(this.groupBox44); + this.tab_Options_Stations.Controls.Add(this.groupBox14); + this.tab_Options_Stations.Controls.Add(this.groupBox16); + this.tab_Options_Stations.Controls.Add(this.groupBox5); + this.tab_Options_Stations.Controls.Add(this.groupBox4); + this.tab_Options_Stations.Location = new System.Drawing.Point(4, 22); + this.tab_Options_Stations.Name = "tab_Options_Stations"; + this.tab_Options_Stations.Padding = new System.Windows.Forms.Padding(3); + this.tab_Options_Stations.Size = new System.Drawing.Size(671, 460); + this.tab_Options_Stations.TabIndex = 1; + this.tab_Options_Stations.Text = "Stations"; + this.tab_Options_Stations.Enter += new System.EventHandler(this.tab_Options_Stations_Enter); + this.tab_Options_Stations.Leave += new System.EventHandler(this.tab_Options_Stations_Leave); + this.tab_Options_Stations.Validating += new System.ComponentModel.CancelEventHandler(this.tab_Options_Stations_Validating); + // + // groupBox18 + // + this.groupBox18.Controls.Add(this.lbl_Options_LocalObstructions); + this.groupBox18.Controls.Add(this.btn_Options_LocalObstructions); + this.groupBox18.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox18.Location = new System.Drawing.Point(6, 376); + this.groupBox18.Name = "groupBox18"; + this.groupBox18.Size = new System.Drawing.Size(235, 75); + this.groupBox18.TabIndex = 28; + this.groupBox18.TabStop = false; + this.groupBox18.Text = "Local Obstructions for My Station"; + // + // lbl_Options_LocalObstructions + // + this.lbl_Options_LocalObstructions.AutoSize = true; + this.lbl_Options_LocalObstructions.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lbl_Options_LocalObstructions.Location = new System.Drawing.Point(6, 22); + this.lbl_Options_LocalObstructions.Name = "lbl_Options_LocalObstructions"; + this.lbl_Options_LocalObstructions.Size = new System.Drawing.Size(97, 13); + this.lbl_Options_LocalObstructions.TabIndex = 19; + this.lbl_Options_LocalObstructions.Text = "Status is unknown."; + // + // btn_Options_LocalObstructions + // + this.btn_Options_LocalObstructions.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Options_LocalObstructions.Location = new System.Drawing.Point(9, 46); + this.btn_Options_LocalObstructions.Name = "btn_Options_LocalObstructions"; + this.btn_Options_LocalObstructions.Size = new System.Drawing.Size(220, 23); + this.btn_Options_LocalObstructions.TabIndex = 28; + this.btn_Options_LocalObstructions.Text = "Manage"; + this.btn_Options_LocalObstructions.UseVisualStyleBackColor = true; + this.btn_Options_LocalObstructions.Click += new System.EventHandler(this.btn_Options_LocalObstructions_Click); + // + // groupBox46 + // + this.groupBox46.Controls.Add(this.lbl_Options_DXLastUpdated); + this.groupBox46.Controls.Add(this.label128); + this.groupBox46.Controls.Add(this.tb_Options_DXPower); + this.groupBox46.Controls.Add(this.tb_Options_DXAntennaGain); + this.groupBox46.Controls.Add(this.tb_Options_DXAntennaHeight); + this.groupBox46.Controls.Add(this.label13); + this.groupBox46.Controls.Add(this.label58); + this.groupBox46.Controls.Add(this.label107); + this.groupBox46.Controls.Add(this.label124); + this.groupBox46.Controls.Add(this.label125); + this.groupBox46.Controls.Add(this.label126); + this.groupBox46.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox46.Location = new System.Drawing.Point(429, 242); + this.groupBox46.Name = "groupBox46"; + this.groupBox46.Size = new System.Drawing.Size(236, 128); + this.groupBox46.TabIndex = 28; + this.groupBox46.TabStop = false; + this.groupBox46.Text = "DX QRV"; + // + // lbl_Options_DXLastUpdated + // + this.lbl_Options_DXLastUpdated.AutoSize = true; + this.lbl_Options_DXLastUpdated.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lbl_Options_DXLastUpdated.Location = new System.Drawing.Point(88, 101); + this.lbl_Options_DXLastUpdated.Name = "lbl_Options_DXLastUpdated"; + this.lbl_Options_DXLastUpdated.Size = new System.Drawing.Size(68, 13); + this.lbl_Options_DXLastUpdated.TabIndex = 43; + this.lbl_Options_DXLastUpdated.Text = "LastUpdated"; + // + // label128 + // + this.label128.AutoSize = true; + this.label128.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label128.Location = new System.Drawing.Point(6, 101); + this.label128.Name = "label128"; + this.label128.Size = new System.Drawing.Size(74, 13); + this.label128.TabIndex = 42; + this.label128.Text = "Last Updated:"; + // + // tb_Options_DXPower + // + this.tb_Options_DXPower.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_DXPower.FormatSpecifier = "F0"; + this.tb_Options_DXPower.Location = new System.Drawing.Point(91, 70); + this.tb_Options_DXPower.MaxValue = 0D; + this.tb_Options_DXPower.MinValue = 0D; + this.tb_Options_DXPower.Name = "tb_Options_DXPower"; + this.tb_Options_DXPower.Size = new System.Drawing.Size(100, 21); + this.tb_Options_DXPower.TabIndex = 41; + this.tb_Options_DXPower.Value = double.NaN; + // + // tb_Options_DXAntennaGain + // + this.tb_Options_DXAntennaGain.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_DXAntennaGain.FormatSpecifier = "F0"; + this.tb_Options_DXAntennaGain.Location = new System.Drawing.Point(91, 43); + this.tb_Options_DXAntennaGain.MaxValue = 0D; + this.tb_Options_DXAntennaGain.MinValue = 0D; + this.tb_Options_DXAntennaGain.Name = "tb_Options_DXAntennaGain"; + this.tb_Options_DXAntennaGain.Size = new System.Drawing.Size(100, 21); + this.tb_Options_DXAntennaGain.TabIndex = 40; + this.tb_Options_DXAntennaGain.Value = double.NaN; + // + // tb_Options_DXAntennaHeight + // + this.tb_Options_DXAntennaHeight.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_DXAntennaHeight.FormatSpecifier = "F0"; + this.tb_Options_DXAntennaHeight.Location = new System.Drawing.Point(91, 16); + this.tb_Options_DXAntennaHeight.MaxValue = 0D; + this.tb_Options_DXAntennaHeight.MinValue = 0D; + this.tb_Options_DXAntennaHeight.Name = "tb_Options_DXAntennaHeight"; + this.tb_Options_DXAntennaHeight.Size = new System.Drawing.Size(100, 21); + this.tb_Options_DXAntennaHeight.TabIndex = 39; + this.tb_Options_DXAntennaHeight.Value = double.NaN; + // + // label13 + // + this.label13.AutoSize = true; + this.label13.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label13.Location = new System.Drawing.Point(216, 73); + this.label13.Name = "label13"; + this.label13.Size = new System.Drawing.Size(18, 13); + this.label13.TabIndex = 38; + this.label13.Text = "W"; + // + // label58 + // + this.label58.AutoSize = true; + this.label58.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label58.Location = new System.Drawing.Point(8, 73); + this.label58.Name = "label58"; + this.label58.Size = new System.Drawing.Size(40, 13); + this.label58.TabIndex = 37; + this.label58.Text = "Power:"; + // + // label107 + // + this.label107.AutoSize = true; + this.label107.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label107.Location = new System.Drawing.Point(204, 45); + this.label107.Name = "label107"; + this.label107.Size = new System.Drawing.Size(27, 13); + this.label107.TabIndex = 35; + this.label107.Text = "dbD"; + // + // label124 + // + this.label124.AutoSize = true; + this.label124.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label124.Location = new System.Drawing.Point(8, 45); + this.label124.Name = "label124"; + this.label124.Size = new System.Drawing.Size(75, 13); + this.label124.TabIndex = 34; + this.label124.Text = "Antenna Gain:"; + // + // label125 + // + this.label125.AutoSize = true; + this.label125.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label125.Location = new System.Drawing.Point(216, 19); + this.label125.Name = "label125"; + this.label125.Size = new System.Drawing.Size(15, 13); + this.label125.TabIndex = 32; + this.label125.Text = "m"; + // + // label126 + // + this.label126.AutoSize = true; + this.label126.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label126.Location = new System.Drawing.Point(8, 19); + this.label126.Name = "label126"; + this.label126.Size = new System.Drawing.Size(84, 13); + this.label126.TabIndex = 31; + this.label126.Text = "Antenna Height:"; + // + // groupBox45 + // + this.groupBox45.Controls.Add(this.btn_Options_BandDown); + this.groupBox45.Controls.Add(this.btn_Options_BandUp); + this.groupBox45.Controls.Add(this.tb_Options_Band); + this.groupBox45.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox45.Location = new System.Drawing.Point(247, 242); + this.groupBox45.Name = "groupBox45"; + this.groupBox45.Size = new System.Drawing.Size(177, 128); + this.groupBox45.TabIndex = 27; + this.groupBox45.TabStop = false; + this.groupBox45.Text = "Band"; + // + // btn_Options_BandDown + // + this.btn_Options_BandDown.Font = new System.Drawing.Font("Courier New", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Options_BandDown.Location = new System.Drawing.Point(52, 79); + this.btn_Options_BandDown.Name = "btn_Options_BandDown"; + this.btn_Options_BandDown.Size = new System.Drawing.Size(70, 24); + this.btn_Options_BandDown.TabIndex = 18; + this.btn_Options_BandDown.Text = "▼"; + this.btn_Options_BandDown.UseVisualStyleBackColor = true; + this.btn_Options_BandDown.Click += new System.EventHandler(this.btn_Options_BandDown_Click); + // + // btn_Options_BandUp + // + this.btn_Options_BandUp.Font = new System.Drawing.Font("Courier New", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Options_BandUp.Location = new System.Drawing.Point(52, 16); + this.btn_Options_BandUp.Name = "btn_Options_BandUp"; + this.btn_Options_BandUp.Size = new System.Drawing.Size(70, 24); + this.btn_Options_BandUp.TabIndex = 17; + this.btn_Options_BandUp.Text = "▲"; + this.btn_Options_BandUp.UseVisualStyleBackColor = true; + this.btn_Options_BandUp.Click += new System.EventHandler(this.btn_Options_BandUp_Click); + // + // tb_Options_Band + // + this.tb_Options_Band.Font = new System.Drawing.Font("Courier New", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Band.Location = new System.Drawing.Point(52, 46); + this.tb_Options_Band.Name = "tb_Options_Band"; + this.tb_Options_Band.ReadOnly = true; + this.tb_Options_Band.Size = new System.Drawing.Size(70, 26); + this.tb_Options_Band.TabIndex = 16; + this.tb_Options_Band.Tag = ""; + // + // groupBox44 + // + this.groupBox44.Controls.Add(this.lbl_Options_MyLastUpdated); + this.groupBox44.Controls.Add(this.label127); + this.groupBox44.Controls.Add(this.tb_Options_MyPower); + this.groupBox44.Controls.Add(this.tb_Options_MyAntennaGain); + this.groupBox44.Controls.Add(this.tb_Options_MyAntennaHeight); + this.groupBox44.Controls.Add(this.label122); + this.groupBox44.Controls.Add(this.label123); + this.groupBox44.Controls.Add(this.label106); + this.groupBox44.Controls.Add(this.label121); + this.groupBox44.Controls.Add(this.label28); + this.groupBox44.Controls.Add(this.label41); + this.groupBox44.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox44.Location = new System.Drawing.Point(6, 242); + this.groupBox44.Name = "groupBox44"; + this.groupBox44.Size = new System.Drawing.Size(235, 128); + this.groupBox44.TabIndex = 26; + this.groupBox44.TabStop = false; + this.groupBox44.Text = "My QRV"; + // + // lbl_Options_MyLastUpdated + // + this.lbl_Options_MyLastUpdated.AutoSize = true; + this.lbl_Options_MyLastUpdated.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lbl_Options_MyLastUpdated.Location = new System.Drawing.Point(86, 101); + this.lbl_Options_MyLastUpdated.Name = "lbl_Options_MyLastUpdated"; + this.lbl_Options_MyLastUpdated.Size = new System.Drawing.Size(68, 13); + this.lbl_Options_MyLastUpdated.TabIndex = 44; + this.lbl_Options_MyLastUpdated.Text = "LastUpdated"; + // + // label127 + // + this.label127.AutoSize = true; + this.label127.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label127.Location = new System.Drawing.Point(6, 101); + this.label127.Name = "label127"; + this.label127.Size = new System.Drawing.Size(74, 13); + this.label127.TabIndex = 33; + this.label127.Text = "Last Updated:"; + // + // tb_Options_MyPower + // + this.tb_Options_MyPower.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_MyPower.FormatSpecifier = "F0"; + this.tb_Options_MyPower.Location = new System.Drawing.Point(89, 70); + this.tb_Options_MyPower.MaxValue = 0D; + this.tb_Options_MyPower.MinValue = 0D; + this.tb_Options_MyPower.Name = "tb_Options_MyPower"; + this.tb_Options_MyPower.Size = new System.Drawing.Size(100, 21); + this.tb_Options_MyPower.TabIndex = 32; + this.tb_Options_MyPower.Value = double.NaN; + // + // tb_Options_MyAntennaGain + // + this.tb_Options_MyAntennaGain.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_MyAntennaGain.FormatSpecifier = "F0"; + this.tb_Options_MyAntennaGain.Location = new System.Drawing.Point(89, 44); + this.tb_Options_MyAntennaGain.MaxValue = 0D; + this.tb_Options_MyAntennaGain.MinValue = 0D; + this.tb_Options_MyAntennaGain.Name = "tb_Options_MyAntennaGain"; + this.tb_Options_MyAntennaGain.Size = new System.Drawing.Size(100, 21); + this.tb_Options_MyAntennaGain.TabIndex = 31; + this.tb_Options_MyAntennaGain.Value = double.NaN; + // + // tb_Options_MyAntennaHeight + // + this.tb_Options_MyAntennaHeight.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_MyAntennaHeight.FormatSpecifier = "F0"; + this.tb_Options_MyAntennaHeight.Location = new System.Drawing.Point(89, 16); + this.tb_Options_MyAntennaHeight.MaxValue = 0D; + this.tb_Options_MyAntennaHeight.MinValue = 0D; + this.tb_Options_MyAntennaHeight.Name = "tb_Options_MyAntennaHeight"; + this.tb_Options_MyAntennaHeight.Size = new System.Drawing.Size(100, 21); + this.tb_Options_MyAntennaHeight.TabIndex = 30; + this.tb_Options_MyAntennaHeight.Value = double.NaN; + // + // label122 + // + this.label122.AutoSize = true; + this.label122.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label122.Location = new System.Drawing.Point(214, 74); + this.label122.Name = "label122"; + this.label122.Size = new System.Drawing.Size(18, 13); + this.label122.TabIndex = 29; + this.label122.Text = "W"; + // + // label123 + // + this.label123.AutoSize = true; + this.label123.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label123.Location = new System.Drawing.Point(6, 74); + this.label123.Name = "label123"; + this.label123.Size = new System.Drawing.Size(40, 13); + this.label123.TabIndex = 28; + this.label123.Text = "Power:"; + // + // label106 + // + this.label106.AutoSize = true; + this.label106.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label106.Location = new System.Drawing.Point(202, 47); + this.label106.Name = "label106"; + this.label106.Size = new System.Drawing.Size(27, 13); + this.label106.TabIndex = 26; + this.label106.Text = "dbD"; + // + // label121 + // + this.label121.AutoSize = true; + this.label121.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label121.Location = new System.Drawing.Point(6, 47); + this.label121.Name = "label121"; + this.label121.Size = new System.Drawing.Size(75, 13); + this.label121.TabIndex = 25; + this.label121.Text = "Antenna Gain:"; + // + // label28 + // + this.label28.AutoSize = true; + this.label28.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label28.Location = new System.Drawing.Point(214, 20); + this.label28.Name = "label28"; + this.label28.Size = new System.Drawing.Size(15, 13); + this.label28.TabIndex = 23; + this.label28.Text = "m"; + // + // label41 + // + this.label41.AutoSize = true; + this.label41.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label41.Location = new System.Drawing.Point(6, 20); + this.label41.Name = "label41"; + this.label41.Size = new System.Drawing.Size(84, 13); + this.label41.TabIndex = 21; + this.label41.Text = "Antenna Height:"; + // + // groupBox14 + // + this.groupBox14.Controls.Add(this.cb_Options_SmallLettersForSubSquares); + this.groupBox14.Controls.Add(this.cb_Options_Locator_AutoLength); + this.groupBox14.Controls.Add(this.label48); + this.groupBox14.Controls.Add(this.ud_Options_Locator_MaxLength); + this.groupBox14.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox14.Location = new System.Drawing.Point(247, 376); + this.groupBox14.Name = "groupBox14"; + this.groupBox14.Size = new System.Drawing.Size(418, 78); + this.groupBox14.TabIndex = 25; + this.groupBox14.TabStop = false; + this.groupBox14.Text = "Locator Settings"; + // + // cb_Options_SmallLettersForSubSquares + // + this.cb_Options_SmallLettersForSubSquares.AutoSize = true; + this.cb_Options_SmallLettersForSubSquares.Checked = global::AirScout.Properties.Settings.Default.Locator_SmallLettersForSubsquares; + this.cb_Options_SmallLettersForSubSquares.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Locator_SmallLettersForSubsquares", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Options_SmallLettersForSubSquares.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Options_SmallLettersForSubSquares.Location = new System.Drawing.Point(12, 58); + this.cb_Options_SmallLettersForSubSquares.Name = "cb_Options_SmallLettersForSubSquares"; + this.cb_Options_SmallLettersForSubSquares.Size = new System.Drawing.Size(177, 17); + this.cb_Options_SmallLettersForSubSquares.TabIndex = 11; + this.cb_Options_SmallLettersForSubSquares.Text = "Use small letters for subsquares:"; + this.cb_Options_SmallLettersForSubSquares.UseVisualStyleBackColor = true; + this.cb_Options_SmallLettersForSubSquares.CheckedChanged += new System.EventHandler(this.cb_Options_SmallLettersForSubSquares_CheckedChanged); + // + // cb_Options_Locator_AutoLength + // + this.cb_Options_Locator_AutoLength.AutoSize = true; + this.cb_Options_Locator_AutoLength.Checked = global::AirScout.Properties.Settings.Default.Locator_AutoLength; + this.cb_Options_Locator_AutoLength.CheckState = System.Windows.Forms.CheckState.Checked; + this.cb_Options_Locator_AutoLength.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Locator_AutoLength", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Options_Locator_AutoLength.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Options_Locator_AutoLength.Location = new System.Drawing.Point(12, 38); + this.cb_Options_Locator_AutoLength.Name = "cb_Options_Locator_AutoLength"; + this.cb_Options_Locator_AutoLength.Size = new System.Drawing.Size(319, 17); + this.cb_Options_Locator_AutoLength.TabIndex = 10; + this.cb_Options_Locator_AutoLength.Text = "Cut locator to significant digits automatically (Minimum 6 digits):"; + this.cb_Options_Locator_AutoLength.UseVisualStyleBackColor = true; + // + // label48 + // + this.label48.AutoSize = true; + this.label48.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label48.Location = new System.Drawing.Point(14, 17); + this.label48.Name = "label48"; + this.label48.Size = new System.Drawing.Size(261, 13); + this.label48.TabIndex = 1; + this.label48.Text = "Number of digits on maps and in all text boxes (6 ..14):"; + // + // ud_Options_Locator_MaxLength + // + this.ud_Options_Locator_MaxLength.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScout.Properties.Settings.Default, "Locator_MaxLength", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.ud_Options_Locator_MaxLength.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.ud_Options_Locator_MaxLength.Increment = new decimal(new int[] { + 2, + 0, + 0, + 0}); + this.ud_Options_Locator_MaxLength.Location = new System.Drawing.Point(281, 15); + this.ud_Options_Locator_MaxLength.Maximum = new decimal(new int[] { + 14, + 0, + 0, + 0}); + this.ud_Options_Locator_MaxLength.Minimum = new decimal(new int[] { + 6, + 0, + 0, + 0}); + this.ud_Options_Locator_MaxLength.Name = "ud_Options_Locator_MaxLength"; + this.ud_Options_Locator_MaxLength.Size = new System.Drawing.Size(50, 20); + this.ud_Options_Locator_MaxLength.TabIndex = 9; + this.ud_Options_Locator_MaxLength.Value = global::AirScout.Properties.Settings.Default.Locator_MaxLength; + // + // groupBox16 + // + this.groupBox16.Controls.Add(this.label52); + this.groupBox16.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox16.Location = new System.Drawing.Point(247, 8); + this.groupBox16.Name = "groupBox16"; + this.groupBox16.Size = new System.Drawing.Size(177, 228); + this.groupBox16.TabIndex = 24; + this.groupBox16.TabStop = false; + this.groupBox16.Text = "Stations Information"; + // + // label52 + // + this.label52.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label52.Location = new System.Drawing.Point(16, 18); + this.label52.Name = "label52"; + this.label52.Size = new System.Drawing.Size(155, 204); + this.label52.TabIndex = 0; + this.label52.Text = resources.GetString("label52.Text"); + this.label52.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // groupBox5 + // + this.groupBox5.Controls.Add(this.btn_Options_DXMap); + this.groupBox5.Controls.Add(this.btn_Options_DXUpdate); + this.groupBox5.Controls.Add(this.tb_Options_DXLon); + this.groupBox5.Controls.Add(this.tb_Options_DXLat); + this.groupBox5.Controls.Add(this.tb_Options_DXLoc); + this.groupBox5.Controls.Add(this.tb_Options_DXCall); + this.groupBox5.Controls.Add(this.label21); + this.groupBox5.Controls.Add(this.btn_Options_DXHorizon); + this.groupBox5.Controls.Add(this.btn_DXCall_QRZ); + this.groupBox5.Controls.Add(this.label14); + this.groupBox5.Controls.Add(this.label15); + this.groupBox5.Controls.Add(this.label42); + this.groupBox5.Controls.Add(this.tb_Options_DXElevation); + this.groupBox5.Controls.Add(this.label43); + this.groupBox5.Controls.Add(this.label44); + this.groupBox5.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox5.Location = new System.Drawing.Point(430, 8); + this.groupBox5.Name = "groupBox5"; + this.groupBox5.Size = new System.Drawing.Size(235, 228); + this.groupBox5.TabIndex = 23; + this.groupBox5.TabStop = false; + this.groupBox5.Text = "DX Station"; + // + // btn_Options_DXMap + // + this.btn_Options_DXMap.BackgroundImage = global::AirScout.Properties.Resources.Map2; + this.btn_Options_DXMap.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch; + this.btn_Options_DXMap.Location = new System.Drawing.Point(8, 135); + this.btn_Options_DXMap.Name = "btn_Options_DXMap"; + this.btn_Options_DXMap.Size = new System.Drawing.Size(75, 81); + this.btn_Options_DXMap.TabIndex = 29; + this.btn_Options_DXMap.Text = "\r\nMap"; + this.btn_Options_DXMap.UseVisualStyleBackColor = true; + this.btn_Options_DXMap.Click += new System.EventHandler(this.btn_Options_DXMap_Click); + // + // tb_Options_DXLon + // + this.tb_Options_DXLon.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_DXLon.FormatSpecifier = "R"; + this.tb_Options_DXLon.Location = new System.Drawing.Point(90, 83); + this.tb_Options_DXLon.MaxValue = 180D; + this.tb_Options_DXLon.MinValue = -180D; + this.tb_Options_DXLon.Name = "tb_Options_DXLon"; + this.tb_Options_DXLon.Size = new System.Drawing.Size(100, 21); + this.tb_Options_DXLon.TabIndex = 8; + this.tb_Options_DXLon.Text = "1.28022909"; + this.tb_Options_DXLon.Value = 1.28022909D; + this.tb_Options_DXLon.TextChanged += new System.EventHandler(this.tb_Options_DXLon_TextChanged); + // + // tb_Options_DXLat + // + this.tb_Options_DXLat.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_DXLat.FormatSpecifier = "R"; + this.tb_Options_DXLat.Location = new System.Drawing.Point(90, 59); + this.tb_Options_DXLat.MaxValue = 90D; + this.tb_Options_DXLat.MinValue = -90D; + this.tb_Options_DXLat.Name = "tb_Options_DXLat"; + this.tb_Options_DXLat.Size = new System.Drawing.Size(100, 21); + this.tb_Options_DXLat.TabIndex = 7; + this.tb_Options_DXLat.Text = "52.05626084"; + this.tb_Options_DXLat.Value = 52.05626084D; + this.tb_Options_DXLat.TextChanged += new System.EventHandler(this.tb_Options_DXLat_TextChanged); + // + // tb_Options_DXLoc + // + this.tb_Options_DXLoc.BackColor = System.Drawing.SystemColors.Window; + this.tb_Options_DXLoc.DataBindings.Add(new System.Windows.Forms.Binding("SmallLettersForSubsquares", global::AirScout.Properties.Settings.Default, "Locator_SmallLettersForSubsquares", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Options_DXLoc.ErrorBackColor = System.Drawing.Color.Red; + this.tb_Options_DXLoc.ErrorForeColor = System.Drawing.Color.White; + this.tb_Options_DXLoc.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_DXLoc.ForeColor = System.Drawing.SystemColors.WindowText; + this.tb_Options_DXLoc.Location = new System.Drawing.Point(90, 36); + this.tb_Options_DXLoc.Name = "tb_Options_DXLoc"; + this.tb_Options_DXLoc.Size = new System.Drawing.Size(100, 21); + this.tb_Options_DXLoc.SmallLettersForSubsquares = global::AirScout.Properties.Settings.Default.Locator_SmallLettersForSubsquares; + this.tb_Options_DXLoc.TabIndex = 6; + this.tb_Options_DXLoc.TextChanged += new System.EventHandler(this.tb_Options_DXLoc_TextChanged); + // + // tb_Options_DXCall + // + this.tb_Options_DXCall.BackColor = System.Drawing.SystemColors.Window; + this.tb_Options_DXCall.CharacterCasing = System.Windows.Forms.CharacterCasing.Upper; + this.tb_Options_DXCall.ErrorBackColor = System.Drawing.Color.Red; + this.tb_Options_DXCall.ErrorForeColor = System.Drawing.Color.White; + this.tb_Options_DXCall.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_DXCall.ForeColor = System.Drawing.SystemColors.WindowText; + this.tb_Options_DXCall.Location = new System.Drawing.Point(90, 12); + this.tb_Options_DXCall.Name = "tb_Options_DXCall"; + this.tb_Options_DXCall.Size = new System.Drawing.Size(100, 21); + this.tb_Options_DXCall.TabIndex = 27; + this.tb_Options_DXCall.TextChanged += new System.EventHandler(this.tb_Options_DXCall_TextChanged); + // + // label21 + // + this.label21.AutoSize = true; + this.label21.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label21.Location = new System.Drawing.Point(194, 115); + this.label21.Name = "label21"; + this.label21.Size = new System.Drawing.Size(31, 13); + this.label21.TabIndex = 23; + this.label21.Text = "m asl"; + // + // btn_Options_DXHorizon + // + this.btn_Options_DXHorizon.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Options_DXHorizon.Location = new System.Drawing.Point(90, 157); + this.btn_Options_DXHorizon.Name = "btn_Options_DXHorizon"; + this.btn_Options_DXHorizon.Size = new System.Drawing.Size(100, 20); + this.btn_Options_DXHorizon.TabIndex = 24; + this.btn_Options_DXHorizon.Text = "Radio Horizon"; + this.btn_Options_DXHorizon.UseVisualStyleBackColor = true; + this.btn_Options_DXHorizon.Click += new System.EventHandler(this.btn_Options_DXHorizon_Click); + // + // btn_DXCall_QRZ + // + this.btn_DXCall_QRZ.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_DXCall_QRZ.Location = new System.Drawing.Point(90, 134); + this.btn_DXCall_QRZ.Name = "btn_DXCall_QRZ"; + this.btn_DXCall_QRZ.Size = new System.Drawing.Size(100, 20); + this.btn_DXCall_QRZ.TabIndex = 23; + this.btn_DXCall_QRZ.Text = "QRZ lookup"; + this.btn_DXCall_QRZ.UseVisualStyleBackColor = true; + this.btn_DXCall_QRZ.Click += new System.EventHandler(this.btn_DXCall_QRZ_Click); + // + // label14 + // + this.label14.AutoSize = true; + this.label14.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label14.Location = new System.Drawing.Point(26, 90); + this.label14.Name = "label14"; + this.label14.Size = new System.Drawing.Size(28, 13); + this.label14.TabIndex = 18; + this.label14.Text = "Lon:"; + // + // label15 + // + this.label15.AutoSize = true; + this.label15.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label15.Location = new System.Drawing.Point(26, 64); + this.label15.Name = "label15"; + this.label15.Size = new System.Drawing.Size(25, 13); + this.label15.TabIndex = 16; + this.label15.Text = "Lat:"; + // + // label42 + // + this.label42.AutoSize = true; + this.label42.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label42.Location = new System.Drawing.Point(26, 16); + this.label42.Name = "label42"; + this.label42.Size = new System.Drawing.Size(27, 13); + this.label42.TabIndex = 10; + this.label42.Text = "Call:"; + // + // tb_Options_DXElevation + // + this.tb_Options_DXElevation.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_DXElevation.Location = new System.Drawing.Point(90, 108); + this.tb_Options_DXElevation.Name = "tb_Options_DXElevation"; + this.tb_Options_DXElevation.ReadOnly = true; + this.tb_Options_DXElevation.Size = new System.Drawing.Size(100, 20); + this.tb_Options_DXElevation.TabIndex = 15; + this.tb_Options_DXElevation.Tag = ""; + // + // label43 + // + this.label43.AutoSize = true; + this.label43.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label43.Location = new System.Drawing.Point(26, 115); + this.label43.Name = "label43"; + this.label43.Size = new System.Drawing.Size(54, 13); + this.label43.TabIndex = 14; + this.label43.Text = "Elevation:"; + // + // label44 + // + this.label44.AutoSize = true; + this.label44.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label44.Location = new System.Drawing.Point(26, 40); + this.label44.Name = "label44"; + this.label44.Size = new System.Drawing.Size(28, 13); + this.label44.TabIndex = 12; + this.label44.Text = "Loc:"; + // + // groupBox4 + // + this.groupBox4.Controls.Add(this.btn_Options_MyMap); + this.groupBox4.Controls.Add(this.btn_Options_MyUpdate); + this.groupBox4.Controls.Add(this.btn_Options_MyHorizon); + this.groupBox4.Controls.Add(this.tb_Options_MyLon); + this.groupBox4.Controls.Add(this.tb_Options_MyLat); + this.groupBox4.Controls.Add(this.tb_Options_MyLoc); + this.groupBox4.Controls.Add(this.tb_Options_MyCall); + this.groupBox4.Controls.Add(this.label18); + this.groupBox4.Controls.Add(this.btn_MyCall_QRZ); + this.groupBox4.Controls.Add(this.label40); + this.groupBox4.Controls.Add(this.label39); + this.groupBox4.Controls.Add(this.label10); + this.groupBox4.Controls.Add(this.tb_Options_MyElevation); + this.groupBox4.Controls.Add(this.label12); + this.groupBox4.Controls.Add(this.label11); + this.groupBox4.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox4.Location = new System.Drawing.Point(6, 8); + this.groupBox4.Name = "groupBox4"; + this.groupBox4.Size = new System.Drawing.Size(235, 228); + this.groupBox4.TabIndex = 16; + this.groupBox4.TabStop = false; + this.groupBox4.Text = "My Station"; + // + // btn_Options_MyMap + // + this.btn_Options_MyMap.BackgroundImage = global::AirScout.Properties.Resources.Map2; + this.btn_Options_MyMap.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch; + this.btn_Options_MyMap.Location = new System.Drawing.Point(7, 134); + this.btn_Options_MyMap.Name = "btn_Options_MyMap"; + this.btn_Options_MyMap.Size = new System.Drawing.Size(75, 81); + this.btn_Options_MyMap.TabIndex = 30; + this.btn_Options_MyMap.Text = "\r\nMap"; + this.btn_Options_MyMap.UseVisualStyleBackColor = true; + this.btn_Options_MyMap.Click += new System.EventHandler(this.btn_Options_MyMap_Click); + // + // btn_Options_MyHorizon + // + this.btn_Options_MyHorizon.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Options_MyHorizon.Location = new System.Drawing.Point(89, 157); + this.btn_Options_MyHorizon.Name = "btn_Options_MyHorizon"; + this.btn_Options_MyHorizon.Size = new System.Drawing.Size(100, 20); + this.btn_Options_MyHorizon.TabIndex = 17; + this.btn_Options_MyHorizon.Text = "Radio Horizon"; + this.btn_Options_MyHorizon.UseVisualStyleBackColor = true; + this.btn_Options_MyHorizon.Click += new System.EventHandler(this.btn_Options_MyHorizon_Click); + // + // tb_Options_MyLon + // + this.tb_Options_MyLon.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_MyLon.FormatSpecifier = "R"; + this.tb_Options_MyLon.Location = new System.Drawing.Point(89, 83); + this.tb_Options_MyLon.MaxValue = 180D; + this.tb_Options_MyLon.MinValue = -180D; + this.tb_Options_MyLon.Name = "tb_Options_MyLon"; + this.tb_Options_MyLon.Size = new System.Drawing.Size(100, 21); + this.tb_Options_MyLon.TabIndex = 4; + this.tb_Options_MyLon.Text = "10.68327"; + this.tb_Options_MyLon.Value = 10.68327D; + this.tb_Options_MyLon.TextChanged += new System.EventHandler(this.tb_Options_MyLon_TextChanged); + // + // tb_Options_MyLat + // + this.tb_Options_MyLat.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_MyLat.FormatSpecifier = "R"; + this.tb_Options_MyLat.Location = new System.Drawing.Point(89, 59); + this.tb_Options_MyLat.MaxValue = 90D; + this.tb_Options_MyLat.MinValue = -90D; + this.tb_Options_MyLat.Name = "tb_Options_MyLat"; + this.tb_Options_MyLat.Size = new System.Drawing.Size(100, 21); + this.tb_Options_MyLat.TabIndex = 3; + this.tb_Options_MyLat.Text = "50.937067"; + this.tb_Options_MyLat.Value = 50.937067D; + this.tb_Options_MyLat.TextChanged += new System.EventHandler(this.tb_Options_MyLat_TextChanged); + // + // tb_Options_MyLoc + // + this.tb_Options_MyLoc.BackColor = System.Drawing.SystemColors.Window; + this.tb_Options_MyLoc.DataBindings.Add(new System.Windows.Forms.Binding("SmallLettersForSubsquares", global::AirScout.Properties.Settings.Default, "Locator_SmallLettersForSubsquares", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Options_MyLoc.ErrorBackColor = System.Drawing.Color.Red; + this.tb_Options_MyLoc.ErrorForeColor = System.Drawing.Color.White; + this.tb_Options_MyLoc.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_MyLoc.ForeColor = System.Drawing.SystemColors.WindowText; + this.tb_Options_MyLoc.Location = new System.Drawing.Point(89, 36); + this.tb_Options_MyLoc.Name = "tb_Options_MyLoc"; + this.tb_Options_MyLoc.Size = new System.Drawing.Size(100, 21); + this.tb_Options_MyLoc.SmallLettersForSubsquares = global::AirScout.Properties.Settings.Default.Locator_SmallLettersForSubsquares; + this.tb_Options_MyLoc.TabIndex = 2; + this.tb_Options_MyLoc.TextChanged += new System.EventHandler(this.tb_Options_MyLoc_TextChanged); + // + // tb_Options_MyCall + // + this.tb_Options_MyCall.BackColor = System.Drawing.SystemColors.Window; + this.tb_Options_MyCall.CharacterCasing = System.Windows.Forms.CharacterCasing.Upper; + this.tb_Options_MyCall.ErrorBackColor = System.Drawing.Color.Red; + this.tb_Options_MyCall.ErrorForeColor = System.Drawing.Color.White; + this.tb_Options_MyCall.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_MyCall.ForeColor = System.Drawing.SystemColors.WindowText; + this.tb_Options_MyCall.Location = new System.Drawing.Point(89, 12); + this.tb_Options_MyCall.Name = "tb_Options_MyCall"; + this.tb_Options_MyCall.Size = new System.Drawing.Size(100, 21); + this.tb_Options_MyCall.TabIndex = 1; + this.tb_Options_MyCall.TextChanged += new System.EventHandler(this.tb_Options_MyCall_TextChanged); + // + // label18 + // + this.label18.AutoSize = true; + this.label18.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label18.Location = new System.Drawing.Point(198, 111); + this.label18.Name = "label18"; + this.label18.Size = new System.Drawing.Size(31, 13); + this.label18.TabIndex = 22; + this.label18.Text = "m asl"; + // + // btn_MyCall_QRZ + // + this.btn_MyCall_QRZ.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_MyCall_QRZ.Location = new System.Drawing.Point(89, 134); + this.btn_MyCall_QRZ.Name = "btn_MyCall_QRZ"; + this.btn_MyCall_QRZ.Size = new System.Drawing.Size(100, 20); + this.btn_MyCall_QRZ.TabIndex = 16; + this.btn_MyCall_QRZ.Text = "QRZ lookup"; + this.btn_MyCall_QRZ.UseVisualStyleBackColor = true; + this.btn_MyCall_QRZ.Click += new System.EventHandler(this.btn_MyCall_QRZ_Click); + // + // label40 + // + this.label40.AutoSize = true; + this.label40.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label40.Location = new System.Drawing.Point(16, 87); + this.label40.Name = "label40"; + this.label40.Size = new System.Drawing.Size(28, 13); + this.label40.TabIndex = 18; + this.label40.Text = "Lon:"; + // + // label39 + // + this.label39.AutoSize = true; + this.label39.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label39.Location = new System.Drawing.Point(16, 63); + this.label39.Name = "label39"; + this.label39.Size = new System.Drawing.Size(25, 13); + this.label39.TabIndex = 16; + this.label39.Text = "Lat:"; + // + // label10 + // + this.label10.AutoSize = true; + this.label10.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label10.Location = new System.Drawing.Point(16, 16); + this.label10.Name = "label10"; + this.label10.Size = new System.Drawing.Size(27, 13); + this.label10.TabIndex = 10; + this.label10.Text = "Call:"; + // + // tb_Options_MyElevation + // + this.tb_Options_MyElevation.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_MyElevation.Location = new System.Drawing.Point(89, 107); + this.tb_Options_MyElevation.Name = "tb_Options_MyElevation"; + this.tb_Options_MyElevation.ReadOnly = true; + this.tb_Options_MyElevation.Size = new System.Drawing.Size(100, 21); + this.tb_Options_MyElevation.TabIndex = 15; + this.tb_Options_MyElevation.Tag = ""; + // + // label12 + // + this.label12.AutoSize = true; + this.label12.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label12.Location = new System.Drawing.Point(14, 111); + this.label12.Name = "label12"; + this.label12.Size = new System.Drawing.Size(54, 13); + this.label12.TabIndex = 14; + this.label12.Text = "Elevation:"; + // + // label11 + // + this.label11.AutoSize = true; + this.label11.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label11.Location = new System.Drawing.Point(16, 40); + this.label11.Name = "label11"; + this.label11.Size = new System.Drawing.Size(28, 13); + this.label11.TabIndex = 12; + this.label11.Text = "Loc:"; + // + // tab_Options_General + // + this.tab_Options_General.BackColor = System.Drawing.SystemColors.Control; + this.tab_Options_General.Controls.Add(this.groupBox25); + this.tab_Options_General.Controls.Add(this.groupBox17); + this.tab_Options_General.ForeColor = System.Drawing.SystemColors.ControlText; + this.tab_Options_General.Location = new System.Drawing.Point(4, 22); + this.tab_Options_General.Name = "tab_Options_General"; + this.tab_Options_General.Padding = new System.Windows.Forms.Padding(3); + this.tab_Options_General.Size = new System.Drawing.Size(671, 460); + this.tab_Options_General.TabIndex = 0; + this.tab_Options_General.Text = "General"; + this.tab_Options_General.Enter += new System.EventHandler(this.tab_Options_General_Enter); + this.tab_Options_General.Validating += new System.ComponentModel.CancelEventHandler(this.tab_Options_General_Validating); + // + // groupBox25 + // + this.groupBox25.Controls.Add(this.tb_Coverage_MaxLat); + this.groupBox25.Controls.Add(this.tb_Coverage_MinLat); + this.groupBox25.Controls.Add(this.tb_Coverage_MaxLon); + this.groupBox25.Controls.Add(this.tb_Coverage_MinLon); + this.groupBox25.Controls.Add(this.gm_Options_Coverage); + this.groupBox25.Controls.Add(this.label35); + this.groupBox25.Controls.Add(this.label54); + this.groupBox25.Controls.Add(this.label59); + this.groupBox25.Controls.Add(this.label60); + this.groupBox25.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox25.Location = new System.Drawing.Point(6, 6); + this.groupBox25.Name = "groupBox25"; + this.groupBox25.Size = new System.Drawing.Size(599, 378); + this.groupBox25.TabIndex = 1; + this.groupBox25.TabStop = false; + this.groupBox25.Text = "Covered Area"; + // + // tb_Coverage_MaxLat + // + this.tb_Coverage_MaxLat.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScout.Properties.Settings.Default, "MaxLat", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Coverage_MaxLat.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Coverage_MaxLat.FormatSpecifier = "F0"; + this.tb_Coverage_MaxLat.Location = new System.Drawing.Point(535, 351); + this.tb_Coverage_MaxLat.MaxValue = 90D; + this.tb_Coverage_MaxLat.MinValue = -90D; + this.tb_Coverage_MaxLat.Name = "tb_Coverage_MaxLat"; + this.tb_Coverage_MaxLat.Size = new System.Drawing.Size(50, 22); + this.tb_Coverage_MaxLat.TabIndex = 4; + this.tb_Coverage_MaxLat.Text = "60"; + this.tb_Coverage_MaxLat.Value = global::AirScout.Properties.Settings.Default.MaxLat; + this.tb_Coverage_MaxLat.TextChanged += new System.EventHandler(this.tab_Options_General_Update); + // + // tb_Coverage_MinLat + // + this.tb_Coverage_MinLat.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScout.Properties.Settings.Default, "MinLat", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Coverage_MinLat.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Coverage_MinLat.FormatSpecifier = "F0"; + this.tb_Coverage_MinLat.Location = new System.Drawing.Point(535, 326); + this.tb_Coverage_MinLat.MaxValue = 90D; + this.tb_Coverage_MinLat.MinValue = -90D; + this.tb_Coverage_MinLat.Name = "tb_Coverage_MinLat"; + this.tb_Coverage_MinLat.Size = new System.Drawing.Size(50, 22); + this.tb_Coverage_MinLat.TabIndex = 3; + this.tb_Coverage_MinLat.Text = "35"; + this.tb_Coverage_MinLat.Value = global::AirScout.Properties.Settings.Default.MinLat; + this.tb_Coverage_MinLat.TextChanged += new System.EventHandler(this.tab_Options_General_Update); + // + // tb_Coverage_MaxLon + // + this.tb_Coverage_MaxLon.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScout.Properties.Settings.Default, "MaxLon", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Coverage_MaxLon.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Coverage_MaxLon.FormatSpecifier = "F0"; + this.tb_Coverage_MaxLon.Location = new System.Drawing.Point(535, 299); + this.tb_Coverage_MaxLon.MaxValue = 180D; + this.tb_Coverage_MaxLon.MinValue = -180D; + this.tb_Coverage_MaxLon.Name = "tb_Coverage_MaxLon"; + this.tb_Coverage_MaxLon.Size = new System.Drawing.Size(50, 22); + this.tb_Coverage_MaxLon.TabIndex = 2; + this.tb_Coverage_MaxLon.Text = "30"; + this.tb_Coverage_MaxLon.Value = global::AirScout.Properties.Settings.Default.MaxLon; + this.tb_Coverage_MaxLon.TextChanged += new System.EventHandler(this.tab_Options_General_Update); + // + // tb_Coverage_MinLon + // + this.tb_Coverage_MinLon.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScout.Properties.Settings.Default, "MinLon", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Coverage_MinLon.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Coverage_MinLon.FormatSpecifier = "F0"; + this.tb_Coverage_MinLon.Location = new System.Drawing.Point(535, 272); + this.tb_Coverage_MinLon.MaxValue = 180D; + this.tb_Coverage_MinLon.MinValue = -180D; + this.tb_Coverage_MinLon.Name = "tb_Coverage_MinLon"; + this.tb_Coverage_MinLon.Size = new System.Drawing.Size(50, 22); + this.tb_Coverage_MinLon.TabIndex = 1; + this.tb_Coverage_MinLon.Text = "-15"; + this.tb_Coverage_MinLon.Value = global::AirScout.Properties.Settings.Default.MinLon; + this.tb_Coverage_MinLon.TextChanged += new System.EventHandler(this.tab_Options_General_Update); + // + // gm_Options_Coverage + // + this.gm_Options_Coverage.Bearing = 0F; + this.gm_Options_Coverage.CanDragMap = true; + this.gm_Options_Coverage.EmptyTileColor = System.Drawing.Color.Navy; + this.gm_Options_Coverage.GrayScaleMode = false; + this.gm_Options_Coverage.HelperLineOption = GMap.NET.WindowsForms.HelperLineOptions.DontShow; + this.gm_Options_Coverage.LevelsKeepInMemmory = 5; + this.gm_Options_Coverage.Location = new System.Drawing.Point(9, 28); + this.gm_Options_Coverage.MarkersEnabled = true; + this.gm_Options_Coverage.MaxZoom = 2; + this.gm_Options_Coverage.MinZoom = 2; + this.gm_Options_Coverage.MouseWheelZoomType = GMap.NET.MouseWheelZoomType.MousePositionAndCenter; + this.gm_Options_Coverage.Name = "gm_Options_Coverage"; + this.gm_Options_Coverage.NegativeMode = false; + this.gm_Options_Coverage.PolygonsEnabled = true; + this.gm_Options_Coverage.RetryLoadTile = 0; + this.gm_Options_Coverage.RoutesEnabled = true; + this.gm_Options_Coverage.SelectedAreaFillColor = System.Drawing.Color.FromArgb(((int)(((byte)(33)))), ((int)(((byte)(65)))), ((int)(((byte)(105)))), ((int)(((byte)(225))))); + this.gm_Options_Coverage.ShowTileGridLines = false; + this.gm_Options_Coverage.Size = new System.Drawing.Size(418, 341); + this.gm_Options_Coverage.TabIndex = 26; + this.gm_Options_Coverage.Zoom = 0D; + // + // label35 + // + this.label35.AutoSize = true; + this.label35.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label35.Location = new System.Drawing.Point(449, 356); + this.label35.Name = "label35"; + this.label35.Size = new System.Drawing.Size(74, 13); + this.label35.TabIndex = 24; + this.label35.Text = "Max. Latitude:"; + // + // label54 + // + this.label54.AutoSize = true; + this.label54.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label54.Location = new System.Drawing.Point(449, 330); + this.label54.Name = "label54"; + this.label54.Size = new System.Drawing.Size(71, 13); + this.label54.TabIndex = 22; + this.label54.Text = "Min. Latitude:"; + // + // label59 + // + this.label59.AutoSize = true; + this.label59.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label59.Location = new System.Drawing.Point(449, 303); + this.label59.Name = "label59"; + this.label59.Size = new System.Drawing.Size(83, 13); + this.label59.TabIndex = 20; + this.label59.Text = "Max. Longitude:"; + // + // label60 + // + this.label60.AutoSize = true; + this.label60.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label60.Location = new System.Drawing.Point(449, 277); + this.label60.Name = "label60"; + this.label60.Size = new System.Drawing.Size(80, 13); + this.label60.TabIndex = 18; + this.label60.Text = "Min. Longitude:"; + // + // groupBox17 + // + this.groupBox17.Controls.Add(this.cb_Options_Watchlist_SyncWithKST); + this.groupBox17.Controls.Add(this.tb_Options_Watchlist_MaxCount); + this.groupBox17.Controls.Add(this.btn_Options_Watchlist_Manage); + this.groupBox17.Controls.Add(this.label31); + this.groupBox17.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox17.Location = new System.Drawing.Point(6, 390); + this.groupBox17.Name = "groupBox17"; + this.groupBox17.Size = new System.Drawing.Size(599, 58); + this.groupBox17.TabIndex = 0; + this.groupBox17.TabStop = false; + this.groupBox17.Text = "Watchlist"; + // + // cb_Options_Watchlist_SyncWithKST + // + this.cb_Options_Watchlist_SyncWithKST.AutoSize = true; + this.cb_Options_Watchlist_SyncWithKST.CheckAlign = System.Drawing.ContentAlignment.MiddleRight; + this.cb_Options_Watchlist_SyncWithKST.Checked = global::AirScout.Properties.Settings.Default.Watchlist_SyncWithKST; + this.cb_Options_Watchlist_SyncWithKST.CheckState = System.Windows.Forms.CheckState.Checked; + this.cb_Options_Watchlist_SyncWithKST.DataBindings.Add(new System.Windows.Forms.Binding("Enabled", global::AirScout.Properties.Settings.Default, "Server_Activate", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Options_Watchlist_SyncWithKST.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Watchlist_SyncWithKST", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Options_Watchlist_SyncWithKST.Enabled = global::AirScout.Properties.Settings.Default.Server_Activate; + this.cb_Options_Watchlist_SyncWithKST.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Options_Watchlist_SyncWithKST.Location = new System.Drawing.Point(6, 37); + this.cb_Options_Watchlist_SyncWithKST.Name = "cb_Options_Watchlist_SyncWithKST"; + this.cb_Options_Watchlist_SyncWithKST.Size = new System.Drawing.Size(436, 17); + this.cb_Options_Watchlist_SyncWithKST.TabIndex = 6; + this.cb_Options_Watchlist_SyncWithKST.Text = "Keep in sync with KST user list (needs wtKST > V3.1 and network functions activat" + + "ed):"; + this.cb_Options_Watchlist_SyncWithKST.UseVisualStyleBackColor = true; + this.cb_Options_Watchlist_SyncWithKST.CheckedChanged += new System.EventHandler(this.cb_Options_Watchlist_SyncWithKST_CheckedChanged); + // + // tb_Options_Watchlist_MaxCount + // + this.tb_Options_Watchlist_MaxCount.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScout.Properties.Settings.Default, "Watchlist_MaxCount", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Options_Watchlist_MaxCount.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Watchlist_MaxCount.FormatSpecifier = "F0"; + this.tb_Options_Watchlist_MaxCount.Location = new System.Drawing.Point(395, 13); + this.tb_Options_Watchlist_MaxCount.MaxValue = 1000; + this.tb_Options_Watchlist_MaxCount.MinValue = 1; + this.tb_Options_Watchlist_MaxCount.Name = "tb_Options_Watchlist_MaxCount"; + this.tb_Options_Watchlist_MaxCount.Size = new System.Drawing.Size(47, 22); + this.tb_Options_Watchlist_MaxCount.TabIndex = 5; + this.tb_Options_Watchlist_MaxCount.Text = "1000"; + this.tb_Options_Watchlist_MaxCount.Value = global::AirScout.Properties.Settings.Default.Watchlist_MaxCount; + // + // btn_Options_Watchlist_Manage + // + this.btn_Options_Watchlist_Manage.Enabled = false; + this.btn_Options_Watchlist_Manage.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Options_Watchlist_Manage.Location = new System.Drawing.Point(472, 21); + this.btn_Options_Watchlist_Manage.Name = "btn_Options_Watchlist_Manage"; + this.btn_Options_Watchlist_Manage.Size = new System.Drawing.Size(113, 23); + this.btn_Options_Watchlist_Manage.TabIndex = 7; + this.btn_Options_Watchlist_Manage.Text = "Manage Watchlist"; + this.btn_Options_Watchlist_Manage.UseVisualStyleBackColor = true; + this.btn_Options_Watchlist_Manage.Click += new System.EventHandler(this.btn_Options_Watchlist_Manage_Click); + // + // label31 + // + this.label31.AutoSize = true; + this.label31.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label31.Location = new System.Drawing.Point(6, 17); + this.label31.Name = "label31"; + this.label31.Size = new System.Drawing.Size(155, 13); + this.label31.TabIndex = 0; + this.label31.Text = "Number of entrys to keep in list:"; + // + // tc_Options + // + this.tc_Options.Controls.Add(this.tab_Options_General); + this.tc_Options.Controls.Add(this.tab_Options_Database); + this.tc_Options.Controls.Add(this.tab_Options_Stations); + this.tc_Options.Controls.Add(this.tab_Options_Map); + this.tc_Options.Controls.Add(this.tab_Options_GLOBE); + this.tc_Options.Controls.Add(this.tab_Options_SRTM3); + this.tc_Options.Controls.Add(this.tab_Options_SRTM1); + this.tc_Options.Controls.Add(this.tab_Options_Path); + this.tc_Options.Controls.Add(this.tab_Options_Planes); + this.tc_Options.Controls.Add(this.tab_Options_Alarm); + this.tc_Options.Controls.Add(this.tab_Options_Network); + this.tc_Options.Controls.Add(this.tab_Options_SpecLab); + this.tc_Options.Controls.Add(this.tc_Track); + this.tc_Options.Controls.Add(this.tab_Options_Info); + this.tc_Options.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tc_Options.Location = new System.Drawing.Point(12, 12); + this.tc_Options.Name = "tc_Options"; + this.tc_Options.SelectedIndex = 0; + this.tc_Options.Size = new System.Drawing.Size(679, 486); + this.tc_Options.TabIndex = 5; + // + // tab_Options_Database + // + this.tab_Options_Database.BackColor = System.Drawing.SystemColors.Control; + this.tab_Options_Database.Controls.Add(this.groupBox47); + this.tab_Options_Database.Controls.Add(this.groupBox27); + this.tab_Options_Database.Controls.Add(this.groupBox15); + this.tab_Options_Database.Controls.Add(this.gb_Options_Database_Settings); + this.tab_Options_Database.Controls.Add(this.gb_Options_Database_Info); + this.tab_Options_Database.Location = new System.Drawing.Point(4, 22); + this.tab_Options_Database.Name = "tab_Options_Database"; + this.tab_Options_Database.Padding = new System.Windows.Forms.Padding(3); + this.tab_Options_Database.Size = new System.Drawing.Size(671, 460); + this.tab_Options_Database.TabIndex = 15; + this.tab_Options_Database.Text = "Database"; + this.tab_Options_Database.Enter += new System.EventHandler(this.tab_Options_Database_Enter); + this.tab_Options_Database.Validating += new System.ComponentModel.CancelEventHandler(this.tab_Options_Database_Validating); + // + // groupBox47 + // + this.groupBox47.Controls.Add(this.btn_Options_DeleteAllPropagationPaths); + this.groupBox47.Controls.Add(this.btn_Options_DeleteAllElevationPaths); + this.groupBox47.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox47.Location = new System.Drawing.Point(359, 370); + this.groupBox47.Name = "groupBox47"; + this.groupBox47.Size = new System.Drawing.Size(306, 79); + this.groupBox47.TabIndex = 4; + this.groupBox47.TabStop = false; + this.groupBox47.Text = "Maintenance"; + // + // groupBox27 + // + this.groupBox27.Controls.Add(this.label108); + this.groupBox27.Controls.Add(this.btn_Options_Import_Callsigns); + this.groupBox27.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox27.Location = new System.Drawing.Point(15, 392); + this.groupBox27.Name = "groupBox27"; + this.groupBox27.Size = new System.Drawing.Size(338, 57); + this.groupBox27.TabIndex = 3; + this.groupBox27.TabStop = false; + this.groupBox27.Text = "Import"; + // + // label108 + // + this.label108.AutoSize = true; + this.label108.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label108.Location = new System.Drawing.Point(6, 26); + this.label108.Name = "label108"; + this.label108.Size = new System.Drawing.Size(235, 13); + this.label108.TabIndex = 6; + this.label108.Text = "Import Callsign Database from previous versions:"; + // + // groupBox15 + // + this.groupBox15.Controls.Add(this.label105); + this.groupBox15.Controls.Add(this.btn_Open_TmpDirectory); + this.groupBox15.Controls.Add(this.btn_Open_LogDirectory); + this.groupBox15.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox15.Location = new System.Drawing.Point(362, 276); + this.groupBox15.Name = "groupBox15"; + this.groupBox15.Size = new System.Drawing.Size(306, 88); + this.groupBox15.TabIndex = 2; + this.groupBox15.TabStop = false; + this.groupBox15.Text = "OpenDirectories"; + // + // label105 + // + this.label105.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label105.Location = new System.Drawing.Point(12, 16); + this.label105.Name = "label105"; + this.label105.Size = new System.Drawing.Size(219, 31); + this.label105.TabIndex = 5; + this.label105.Text = "Open directories to see database files, logs, hardcopies, path calculations and m" + + "ore:"; + // + // gb_Options_Database_Settings + // + this.gb_Options_Database_Settings.Controls.Add(this.cb_Options_Background_Calculations_Enable); + this.gb_Options_Database_Settings.Controls.Add(this.label47); + this.gb_Options_Database_Settings.Controls.Add(this.ud_Options_Database_Update_Period); + this.gb_Options_Database_Settings.Controls.Add(this.rb_Options_Database_Update_Periodically); + this.gb_Options_Database_Settings.Controls.Add(this.rb_Options_Database_Update_OnStartup); + this.gb_Options_Database_Settings.Controls.Add(this.rb_Options_Database_Update_Never); + this.gb_Options_Database_Settings.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.gb_Options_Database_Settings.Location = new System.Drawing.Point(15, 276); + this.gb_Options_Database_Settings.Name = "gb_Options_Database_Settings"; + this.gb_Options_Database_Settings.Size = new System.Drawing.Size(338, 110); + this.gb_Options_Database_Settings.TabIndex = 1; + this.gb_Options_Database_Settings.TabStop = false; + this.gb_Options_Database_Settings.Text = "Background Update and Pre-Calculation Settings"; + // + // cb_Options_Background_Calculations_Enable + // + this.cb_Options_Background_Calculations_Enable.AutoSize = true; + this.cb_Options_Background_Calculations_Enable.Checked = global::AirScout.Properties.Settings.Default.Background_Calculations_Enable; + this.cb_Options_Background_Calculations_Enable.CheckState = System.Windows.Forms.CheckState.Checked; + this.cb_Options_Background_Calculations_Enable.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Background_Calculations_Enable", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Options_Background_Calculations_Enable.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Options_Background_Calculations_Enable.Location = new System.Drawing.Point(5, 90); + this.cb_Options_Background_Calculations_Enable.Name = "cb_Options_Background_Calculations_Enable"; + this.cb_Options_Background_Calculations_Enable.Size = new System.Drawing.Size(308, 17); + this.cb_Options_Background_Calculations_Enable.TabIndex = 6; + this.cb_Options_Background_Calculations_Enable.Text = "Enable Background Pre-Calculations for paths/horizons etc."; + this.cb_Options_Background_Calculations_Enable.UseVisualStyleBackColor = true; + // + // label47 + // + this.label47.AutoSize = true; + this.label47.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label47.Location = new System.Drawing.Point(150, 66); + this.label47.Name = "label47"; + this.label47.Size = new System.Drawing.Size(107, 13); + this.label47.TabIndex = 5; + this.label47.Text = "Update interval [min]:"; + // + // ud_Options_Database_Update_Period + // + this.ud_Options_Database_Update_Period.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScout.Properties.Settings.Default, "Background_Update_Period", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.ud_Options_Database_Update_Period.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.ud_Options_Database_Update_Period.Location = new System.Drawing.Point(263, 68); + this.ud_Options_Database_Update_Period.Maximum = new decimal(new int[] { + 6000, + 0, + 0, + 0}); + this.ud_Options_Database_Update_Period.Minimum = new decimal(new int[] { + 1, + 0, + 0, + 0}); + this.ud_Options_Database_Update_Period.Name = "ud_Options_Database_Update_Period"; + this.ud_Options_Database_Update_Period.Size = new System.Drawing.Size(39, 20); + this.ud_Options_Database_Update_Period.TabIndex = 3; + this.ud_Options_Database_Update_Period.Value = global::AirScout.Properties.Settings.Default.Background_Update_Period; + // + // rb_Options_Database_Update_Periodically + // + this.rb_Options_Database_Update_Periodically.AutoSize = true; + this.rb_Options_Database_Update_Periodically.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.rb_Options_Database_Update_Periodically.Location = new System.Drawing.Point(7, 64); + this.rb_Options_Database_Update_Periodically.Name = "rb_Options_Database_Update_Periodically"; + this.rb_Options_Database_Update_Periodically.Size = new System.Drawing.Size(78, 17); + this.rb_Options_Database_Update_Periodically.TabIndex = 2; + this.rb_Options_Database_Update_Periodically.Text = "Periodically"; + this.rb_Options_Database_Update_Periodically.UseVisualStyleBackColor = true; + this.rb_Options_Database_Update_Periodically.CheckedChanged += new System.EventHandler(this.rb_Options_Database_Update_Periodically_CheckedChanged); + // + // rb_Options_Database_Update_OnStartup + // + this.rb_Options_Database_Update_OnStartup.AutoSize = true; + this.rb_Options_Database_Update_OnStartup.Checked = true; + this.rb_Options_Database_Update_OnStartup.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.rb_Options_Database_Update_OnStartup.Location = new System.Drawing.Point(7, 41); + this.rb_Options_Database_Update_OnStartup.Name = "rb_Options_Database_Update_OnStartup"; + this.rb_Options_Database_Update_OnStartup.Size = new System.Drawing.Size(118, 17); + this.rb_Options_Database_Update_OnStartup.TabIndex = 1; + this.rb_Options_Database_Update_OnStartup.TabStop = true; + this.rb_Options_Database_Update_OnStartup.Text = "On Program Startup"; + this.rb_Options_Database_Update_OnStartup.UseVisualStyleBackColor = true; + this.rb_Options_Database_Update_OnStartup.CheckedChanged += new System.EventHandler(this.rb_Options_Database_Update_OnStartup_CheckedChanged); + // + // rb_Options_Database_Update_Never + // + this.rb_Options_Database_Update_Never.AutoSize = true; + this.rb_Options_Database_Update_Never.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.rb_Options_Database_Update_Never.Location = new System.Drawing.Point(7, 18); + this.rb_Options_Database_Update_Never.Name = "rb_Options_Database_Update_Never"; + this.rb_Options_Database_Update_Never.Size = new System.Drawing.Size(54, 17); + this.rb_Options_Database_Update_Never.TabIndex = 0; + this.rb_Options_Database_Update_Never.Text = "Never"; + this.rb_Options_Database_Update_Never.UseVisualStyleBackColor = true; + this.rb_Options_Database_Update_Never.CheckedChanged += new System.EventHandler(this.rb_Options_Database_Update_Never_CheckedChanged); + // + // gb_Options_Database_Info + // + this.gb_Options_Database_Info.Controls.Add(this.btn_Options_Elevation_SRTM1_Database_Maintenance); + this.gb_Options_Database_Info.Controls.Add(this.btn_Options_Elevation_SRTM3_Database_Maintenance); + this.gb_Options_Database_Info.Controls.Add(this.btn_Options_Elevation_GLOBE_Database_Maintenance); + this.gb_Options_Database_Info.Controls.Add(this.btn_Options_Propagation_SRTM1_Database_Maintenance); + this.gb_Options_Database_Info.Controls.Add(this.btn_Options_Propagation_SRTM3_Database_Maintenance); + this.gb_Options_Database_Info.Controls.Add(this.btn_Options_Propagation_GLOBE_Database_Maintenance); + this.gb_Options_Database_Info.Controls.Add(this.btn_Options_AirScout_Database_Maintenance); + this.gb_Options_Database_Info.Controls.Add(this.btn_Options_ScoutBase_Database_Maintenance); + this.gb_Options_Database_Info.Controls.Add(this.label141); + this.gb_Options_Database_Info.Controls.Add(this.lbl_Options_Database_TotalSize); + this.gb_Options_Database_Info.Controls.Add(this.label139); + this.gb_Options_Database_Info.Controls.Add(this.label136); + this.gb_Options_Database_Info.Controls.Add(this.tb_Options_Propagation_SRTM1_Database_FileSize); + this.gb_Options_Database_Info.Controls.Add(this.label137); + this.gb_Options_Database_Info.Controls.Add(this.tb_Options_Propagation_SRTM1_Database_FileName); + this.gb_Options_Database_Info.Controls.Add(this.label138); + this.gb_Options_Database_Info.Controls.Add(this.label46); + this.gb_Options_Database_Info.Controls.Add(this.tb_Options_Propagation_SRTM3_Database_FileSize); + this.gb_Options_Database_Info.Controls.Add(this.label134); + this.gb_Options_Database_Info.Controls.Add(this.tb_Options_Propagation_SRTM3_Database_FileName); + this.gb_Options_Database_Info.Controls.Add(this.label135); + this.gb_Options_Database_Info.Controls.Add(this.label132); + this.gb_Options_Database_Info.Controls.Add(this.tb_Options_Propagation_GLOBE_Database_FileSize); + this.gb_Options_Database_Info.Controls.Add(this.label133); + this.gb_Options_Database_Info.Controls.Add(this.tb_Options_Propagation_GLOBE_Database_FileName); + this.gb_Options_Database_Info.Controls.Add(this.label109); + this.gb_Options_Database_Info.Controls.Add(this.label118); + this.gb_Options_Database_Info.Controls.Add(this.tb_Options_Elevation_SRTM1_Database_FileSize); + this.gb_Options_Database_Info.Controls.Add(this.label119); + this.gb_Options_Database_Info.Controls.Add(this.tb_Options_Elevation_SRTM1_Database_FileName); + this.gb_Options_Database_Info.Controls.Add(this.label120); + this.gb_Options_Database_Info.Controls.Add(this.label115); + this.gb_Options_Database_Info.Controls.Add(this.tb_Options_Elevation_SRTM3_Database_FileSize); + this.gb_Options_Database_Info.Controls.Add(this.label116); + this.gb_Options_Database_Info.Controls.Add(this.tb_Options_Elevation_SRTM3_Database_FileName); + this.gb_Options_Database_Info.Controls.Add(this.label117); + this.gb_Options_Database_Info.Controls.Add(this.label16); + this.gb_Options_Database_Info.Controls.Add(this.tb_Options_Elevation_GLOBE_Database_FileSize); + this.gb_Options_Database_Info.Controls.Add(this.label113); + this.gb_Options_Database_Info.Controls.Add(this.tb_Options_Elevation_GLOBE_Database_FileName); + this.gb_Options_Database_Info.Controls.Add(this.label114); + this.gb_Options_Database_Info.Controls.Add(this.label104); + this.gb_Options_Database_Info.Controls.Add(this.label101); + this.gb_Options_Database_Info.Controls.Add(this.tb_Options_AirScout_Database_FileSize); + this.gb_Options_Database_Info.Controls.Add(this.label102); + this.gb_Options_Database_Info.Controls.Add(this.tb_Options_AirScout_Database_FileName); + this.gb_Options_Database_Info.Controls.Add(this.label103); + this.gb_Options_Database_Info.Controls.Add(this.label73); + this.gb_Options_Database_Info.Controls.Add(this.tb_Options_ScoutBase_Database_FileSize); + this.gb_Options_Database_Info.Controls.Add(this.label50); + this.gb_Options_Database_Info.Controls.Add(this.tb_Options_ScoutBase_Database_FileName); + this.gb_Options_Database_Info.Controls.Add(this.label49); + this.gb_Options_Database_Info.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.gb_Options_Database_Info.Location = new System.Drawing.Point(15, 6); + this.gb_Options_Database_Info.Name = "gb_Options_Database_Info"; + this.gb_Options_Database_Info.Size = new System.Drawing.Size(650, 264); + this.gb_Options_Database_Info.TabIndex = 0; + this.gb_Options_Database_Info.TabStop = false; + this.gb_Options_Database_Info.Text = "Info"; + // + // btn_Options_Elevation_SRTM1_Database_Maintenance + // + this.btn_Options_Elevation_SRTM1_Database_Maintenance.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Options_Elevation_SRTM1_Database_Maintenance.Location = new System.Drawing.Point(485, 213); + this.btn_Options_Elevation_SRTM1_Database_Maintenance.Name = "btn_Options_Elevation_SRTM1_Database_Maintenance"; + this.btn_Options_Elevation_SRTM1_Database_Maintenance.Size = new System.Drawing.Size(147, 23); + this.btn_Options_Elevation_SRTM1_Database_Maintenance.TabIndex = 51; + this.btn_Options_Elevation_SRTM1_Database_Maintenance.Text = "Database Maintenance"; + this.btn_Options_Elevation_SRTM1_Database_Maintenance.UseVisualStyleBackColor = true; + this.btn_Options_Elevation_SRTM1_Database_Maintenance.Click += new System.EventHandler(this.btn_Options_Elevation_SRTM1_Database_Maintenance_Click); + // + // btn_Options_Elevation_SRTM3_Database_Maintenance + // + this.btn_Options_Elevation_SRTM3_Database_Maintenance.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Options_Elevation_SRTM3_Database_Maintenance.Location = new System.Drawing.Point(485, 187); + this.btn_Options_Elevation_SRTM3_Database_Maintenance.Name = "btn_Options_Elevation_SRTM3_Database_Maintenance"; + this.btn_Options_Elevation_SRTM3_Database_Maintenance.Size = new System.Drawing.Size(147, 23); + this.btn_Options_Elevation_SRTM3_Database_Maintenance.TabIndex = 50; + this.btn_Options_Elevation_SRTM3_Database_Maintenance.Text = "Database Maintenance"; + this.btn_Options_Elevation_SRTM3_Database_Maintenance.UseVisualStyleBackColor = true; + this.btn_Options_Elevation_SRTM3_Database_Maintenance.Click += new System.EventHandler(this.btn_Options_Elevation_SRTM3_Database_Maintenance_Click); + // + // btn_Options_Elevation_GLOBE_Database_Maintenance + // + this.btn_Options_Elevation_GLOBE_Database_Maintenance.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Options_Elevation_GLOBE_Database_Maintenance.Location = new System.Drawing.Point(485, 161); + this.btn_Options_Elevation_GLOBE_Database_Maintenance.Name = "btn_Options_Elevation_GLOBE_Database_Maintenance"; + this.btn_Options_Elevation_GLOBE_Database_Maintenance.Size = new System.Drawing.Size(147, 23); + this.btn_Options_Elevation_GLOBE_Database_Maintenance.TabIndex = 49; + this.btn_Options_Elevation_GLOBE_Database_Maintenance.Text = "Database Maintenance"; + this.btn_Options_Elevation_GLOBE_Database_Maintenance.UseVisualStyleBackColor = true; + this.btn_Options_Elevation_GLOBE_Database_Maintenance.Click += new System.EventHandler(this.btn_Options_Elevation_GLOBE_Database_Maintenance_Click); + // + // btn_Options_Propagation_SRTM1_Database_Maintenance + // + this.btn_Options_Propagation_SRTM1_Database_Maintenance.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Options_Propagation_SRTM1_Database_Maintenance.Location = new System.Drawing.Point(485, 135); + this.btn_Options_Propagation_SRTM1_Database_Maintenance.Name = "btn_Options_Propagation_SRTM1_Database_Maintenance"; + this.btn_Options_Propagation_SRTM1_Database_Maintenance.Size = new System.Drawing.Size(147, 23); + this.btn_Options_Propagation_SRTM1_Database_Maintenance.TabIndex = 48; + this.btn_Options_Propagation_SRTM1_Database_Maintenance.Text = "Database Maintenance"; + this.btn_Options_Propagation_SRTM1_Database_Maintenance.UseVisualStyleBackColor = true; + this.btn_Options_Propagation_SRTM1_Database_Maintenance.Click += new System.EventHandler(this.btn_Options_Propagation_SRTM1_Database_Maintenance_Click); + // + // btn_Options_Propagation_SRTM3_Database_Maintenance + // + this.btn_Options_Propagation_SRTM3_Database_Maintenance.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Options_Propagation_SRTM3_Database_Maintenance.Location = new System.Drawing.Point(485, 109); + this.btn_Options_Propagation_SRTM3_Database_Maintenance.Name = "btn_Options_Propagation_SRTM3_Database_Maintenance"; + this.btn_Options_Propagation_SRTM3_Database_Maintenance.Size = new System.Drawing.Size(147, 23); + this.btn_Options_Propagation_SRTM3_Database_Maintenance.TabIndex = 47; + this.btn_Options_Propagation_SRTM3_Database_Maintenance.Text = "Database Maintenance"; + this.btn_Options_Propagation_SRTM3_Database_Maintenance.UseVisualStyleBackColor = true; + this.btn_Options_Propagation_SRTM3_Database_Maintenance.Click += new System.EventHandler(this.btn_Options_Propagation_SRTM3_Database_Maintenance_Click); + // + // btn_Options_Propagation_GLOBE_Database_Maintenance + // + this.btn_Options_Propagation_GLOBE_Database_Maintenance.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Options_Propagation_GLOBE_Database_Maintenance.Location = new System.Drawing.Point(485, 83); + this.btn_Options_Propagation_GLOBE_Database_Maintenance.Name = "btn_Options_Propagation_GLOBE_Database_Maintenance"; + this.btn_Options_Propagation_GLOBE_Database_Maintenance.Size = new System.Drawing.Size(147, 23); + this.btn_Options_Propagation_GLOBE_Database_Maintenance.TabIndex = 46; + this.btn_Options_Propagation_GLOBE_Database_Maintenance.Text = "Database Maintenance"; + this.btn_Options_Propagation_GLOBE_Database_Maintenance.UseVisualStyleBackColor = true; + this.btn_Options_Propagation_GLOBE_Database_Maintenance.Click += new System.EventHandler(this.btn_Options_Propagation_GLOBE_Database_Maintenance_Click); + // + // btn_Options_AirScout_Database_Maintenance + // + this.btn_Options_AirScout_Database_Maintenance.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Options_AirScout_Database_Maintenance.Location = new System.Drawing.Point(485, 57); + this.btn_Options_AirScout_Database_Maintenance.Name = "btn_Options_AirScout_Database_Maintenance"; + this.btn_Options_AirScout_Database_Maintenance.Size = new System.Drawing.Size(147, 23); + this.btn_Options_AirScout_Database_Maintenance.TabIndex = 45; + this.btn_Options_AirScout_Database_Maintenance.Text = "Database Maintenance"; + this.btn_Options_AirScout_Database_Maintenance.UseVisualStyleBackColor = true; + this.btn_Options_AirScout_Database_Maintenance.Click += new System.EventHandler(this.btn_Options_AirScout_Database_Maintenance_Click); + // + // btn_Options_ScoutBase_Database_Maintenance + // + this.btn_Options_ScoutBase_Database_Maintenance.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Options_ScoutBase_Database_Maintenance.Location = new System.Drawing.Point(485, 31); + this.btn_Options_ScoutBase_Database_Maintenance.Name = "btn_Options_ScoutBase_Database_Maintenance"; + this.btn_Options_ScoutBase_Database_Maintenance.Size = new System.Drawing.Size(147, 23); + this.btn_Options_ScoutBase_Database_Maintenance.TabIndex = 44; + this.btn_Options_ScoutBase_Database_Maintenance.Text = "Database Maintenance"; + this.btn_Options_ScoutBase_Database_Maintenance.UseVisualStyleBackColor = true; + this.btn_Options_ScoutBase_Database_Maintenance.Click += new System.EventHandler(this.btn_Options_ScoutBase_Database_Maintenance_Click); + // + // label141 + // + this.label141.AutoSize = true; + this.label141.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label141.Location = new System.Drawing.Point(446, 242); + this.label141.Name = "label141"; + this.label141.Size = new System.Drawing.Size(25, 13); + this.label141.TabIndex = 43; + this.label141.Text = "MB"; + // + // lbl_Options_Database_TotalSize + // + this.lbl_Options_Database_TotalSize.AutoSize = true; + this.lbl_Options_Database_TotalSize.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lbl_Options_Database_TotalSize.Location = new System.Drawing.Point(404, 242); + this.lbl_Options_Database_TotalSize.Name = "lbl_Options_Database_TotalSize"; + this.lbl_Options_Database_TotalSize.Size = new System.Drawing.Size(25, 13); + this.lbl_Options_Database_TotalSize.TabIndex = 42; + this.lbl_Options_Database_TotalSize.Text = "xxx"; + // + // label139 + // + this.label139.AutoSize = true; + this.label139.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label139.Location = new System.Drawing.Point(348, 242); + this.label139.Name = "label139"; + this.label139.Size = new System.Drawing.Size(36, 13); + this.label139.TabIndex = 41; + this.label139.Text = "total:"; + // + // label136 + // + this.label136.AutoSize = true; + this.label136.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label136.Location = new System.Drawing.Point(446, 140); + this.label136.Name = "label136"; + this.label136.Size = new System.Drawing.Size(23, 13); + this.label136.TabIndex = 40; + this.label136.Text = "MB"; + // + // tb_Options_Propagation_SRTM1_Database_FileSize + // + this.tb_Options_Propagation_SRTM1_Database_FileSize.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Propagation_SRTM1_Database_FileSize.Location = new System.Drawing.Point(390, 137); + this.tb_Options_Propagation_SRTM1_Database_FileSize.Name = "tb_Options_Propagation_SRTM1_Database_FileSize"; + this.tb_Options_Propagation_SRTM1_Database_FileSize.ReadOnly = true; + this.tb_Options_Propagation_SRTM1_Database_FileSize.Size = new System.Drawing.Size(50, 20); + this.tb_Options_Propagation_SRTM1_Database_FileSize.TabIndex = 39; + this.tb_Options_Propagation_SRTM1_Database_FileSize.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; + // + // label137 + // + this.label137.AutoSize = true; + this.label137.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label137.Location = new System.Drawing.Point(354, 140); + this.label137.Name = "label137"; + this.label137.Size = new System.Drawing.Size(30, 13); + this.label137.TabIndex = 38; + this.label137.Text = "Size:"; + // + // tb_Options_Propagation_SRTM1_Database_FileName + // + this.tb_Options_Propagation_SRTM1_Database_FileName.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Propagation_SRTM1_Database_FileName.Location = new System.Drawing.Point(168, 137); + this.tb_Options_Propagation_SRTM1_Database_FileName.Name = "tb_Options_Propagation_SRTM1_Database_FileName"; + this.tb_Options_Propagation_SRTM1_Database_FileName.ReadOnly = true; + this.tb_Options_Propagation_SRTM1_Database_FileName.Size = new System.Drawing.Size(180, 20); + this.tb_Options_Propagation_SRTM1_Database_FileName.TabIndex = 37; + // + // label138 + // + this.label138.AutoSize = true; + this.label138.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label138.Location = new System.Drawing.Point(6, 140); + this.label138.Name = "label138"; + this.label138.Size = new System.Drawing.Size(156, 13); + this.label138.TabIndex = 36; + this.label138.Text = "SRTM1 Propagation Data:"; + // + // label46 + // + this.label46.AutoSize = true; + this.label46.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label46.Location = new System.Drawing.Point(446, 114); + this.label46.Name = "label46"; + this.label46.Size = new System.Drawing.Size(23, 13); + this.label46.TabIndex = 35; + this.label46.Text = "MB"; + // + // tb_Options_Propagation_SRTM3_Database_FileSize + // + this.tb_Options_Propagation_SRTM3_Database_FileSize.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Propagation_SRTM3_Database_FileSize.Location = new System.Drawing.Point(390, 111); + this.tb_Options_Propagation_SRTM3_Database_FileSize.Name = "tb_Options_Propagation_SRTM3_Database_FileSize"; + this.tb_Options_Propagation_SRTM3_Database_FileSize.ReadOnly = true; + this.tb_Options_Propagation_SRTM3_Database_FileSize.Size = new System.Drawing.Size(50, 20); + this.tb_Options_Propagation_SRTM3_Database_FileSize.TabIndex = 34; + this.tb_Options_Propagation_SRTM3_Database_FileSize.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; + // + // label134 + // + this.label134.AutoSize = true; + this.label134.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label134.Location = new System.Drawing.Point(354, 114); + this.label134.Name = "label134"; + this.label134.Size = new System.Drawing.Size(30, 13); + this.label134.TabIndex = 33; + this.label134.Text = "Size:"; + // + // tb_Options_Propagation_SRTM3_Database_FileName + // + this.tb_Options_Propagation_SRTM3_Database_FileName.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Propagation_SRTM3_Database_FileName.Location = new System.Drawing.Point(168, 111); + this.tb_Options_Propagation_SRTM3_Database_FileName.Name = "tb_Options_Propagation_SRTM3_Database_FileName"; + this.tb_Options_Propagation_SRTM3_Database_FileName.ReadOnly = true; + this.tb_Options_Propagation_SRTM3_Database_FileName.Size = new System.Drawing.Size(180, 20); + this.tb_Options_Propagation_SRTM3_Database_FileName.TabIndex = 32; + // + // label135 + // + this.label135.AutoSize = true; + this.label135.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label135.Location = new System.Drawing.Point(6, 114); + this.label135.Name = "label135"; + this.label135.Size = new System.Drawing.Size(156, 13); + this.label135.TabIndex = 31; + this.label135.Text = "SRTM3 Propagation Data:"; + // + // label132 + // + this.label132.AutoSize = true; + this.label132.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label132.Location = new System.Drawing.Point(446, 88); + this.label132.Name = "label132"; + this.label132.Size = new System.Drawing.Size(23, 13); + this.label132.TabIndex = 30; + this.label132.Text = "MB"; + // + // tb_Options_Propagation_GLOBE_Database_FileSize + // + this.tb_Options_Propagation_GLOBE_Database_FileSize.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Propagation_GLOBE_Database_FileSize.Location = new System.Drawing.Point(390, 85); + this.tb_Options_Propagation_GLOBE_Database_FileSize.Name = "tb_Options_Propagation_GLOBE_Database_FileSize"; + this.tb_Options_Propagation_GLOBE_Database_FileSize.ReadOnly = true; + this.tb_Options_Propagation_GLOBE_Database_FileSize.Size = new System.Drawing.Size(50, 20); + this.tb_Options_Propagation_GLOBE_Database_FileSize.TabIndex = 29; + this.tb_Options_Propagation_GLOBE_Database_FileSize.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; + // + // label133 + // + this.label133.AutoSize = true; + this.label133.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label133.Location = new System.Drawing.Point(354, 88); + this.label133.Name = "label133"; + this.label133.Size = new System.Drawing.Size(30, 13); + this.label133.TabIndex = 28; + this.label133.Text = "Size:"; + // + // tb_Options_Propagation_GLOBE_Database_FileName + // + this.tb_Options_Propagation_GLOBE_Database_FileName.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Propagation_GLOBE_Database_FileName.Location = new System.Drawing.Point(168, 85); + this.tb_Options_Propagation_GLOBE_Database_FileName.Name = "tb_Options_Propagation_GLOBE_Database_FileName"; + this.tb_Options_Propagation_GLOBE_Database_FileName.ReadOnly = true; + this.tb_Options_Propagation_GLOBE_Database_FileName.Size = new System.Drawing.Size(180, 20); + this.tb_Options_Propagation_GLOBE_Database_FileName.TabIndex = 27; + // + // label109 + // + this.label109.AutoSize = true; + this.label109.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label109.Location = new System.Drawing.Point(6, 88); + this.label109.Name = "label109"; + this.label109.Size = new System.Drawing.Size(155, 13); + this.label109.TabIndex = 26; + this.label109.Text = "GLOBE Propagation Data:"; + // + // label118 + // + this.label118.AutoSize = true; + this.label118.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label118.Location = new System.Drawing.Point(446, 218); + this.label118.Name = "label118"; + this.label118.Size = new System.Drawing.Size(23, 13); + this.label118.TabIndex = 25; + this.label118.Text = "MB"; + // + // tb_Options_Elevation_SRTM1_Database_FileSize + // + this.tb_Options_Elevation_SRTM1_Database_FileSize.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Elevation_SRTM1_Database_FileSize.Location = new System.Drawing.Point(390, 215); + this.tb_Options_Elevation_SRTM1_Database_FileSize.Name = "tb_Options_Elevation_SRTM1_Database_FileSize"; + this.tb_Options_Elevation_SRTM1_Database_FileSize.ReadOnly = true; + this.tb_Options_Elevation_SRTM1_Database_FileSize.Size = new System.Drawing.Size(50, 20); + this.tb_Options_Elevation_SRTM1_Database_FileSize.TabIndex = 24; + this.tb_Options_Elevation_SRTM1_Database_FileSize.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; + // + // label119 + // + this.label119.AutoSize = true; + this.label119.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label119.Location = new System.Drawing.Point(354, 218); + this.label119.Name = "label119"; + this.label119.Size = new System.Drawing.Size(30, 13); + this.label119.TabIndex = 23; + this.label119.Text = "Size:"; + // + // tb_Options_Elevation_SRTM1_Database_FileName + // + this.tb_Options_Elevation_SRTM1_Database_FileName.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Elevation_SRTM1_Database_FileName.Location = new System.Drawing.Point(168, 215); + this.tb_Options_Elevation_SRTM1_Database_FileName.Name = "tb_Options_Elevation_SRTM1_Database_FileName"; + this.tb_Options_Elevation_SRTM1_Database_FileName.ReadOnly = true; + this.tb_Options_Elevation_SRTM1_Database_FileName.Size = new System.Drawing.Size(180, 20); + this.tb_Options_Elevation_SRTM1_Database_FileName.TabIndex = 22; + // + // label120 + // + this.label120.AutoSize = true; + this.label120.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label120.Location = new System.Drawing.Point(6, 218); + this.label120.Name = "label120"; + this.label120.Size = new System.Drawing.Size(141, 13); + this.label120.TabIndex = 21; + this.label120.Text = "SRTM1 Elevation Data:"; + // + // label115 + // + this.label115.AutoSize = true; + this.label115.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label115.Location = new System.Drawing.Point(446, 192); + this.label115.Name = "label115"; + this.label115.Size = new System.Drawing.Size(23, 13); + this.label115.TabIndex = 20; + this.label115.Text = "MB"; + // + // tb_Options_Elevation_SRTM3_Database_FileSize + // + this.tb_Options_Elevation_SRTM3_Database_FileSize.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Elevation_SRTM3_Database_FileSize.Location = new System.Drawing.Point(390, 189); + this.tb_Options_Elevation_SRTM3_Database_FileSize.Name = "tb_Options_Elevation_SRTM3_Database_FileSize"; + this.tb_Options_Elevation_SRTM3_Database_FileSize.ReadOnly = true; + this.tb_Options_Elevation_SRTM3_Database_FileSize.Size = new System.Drawing.Size(50, 20); + this.tb_Options_Elevation_SRTM3_Database_FileSize.TabIndex = 19; + this.tb_Options_Elevation_SRTM3_Database_FileSize.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; + // + // label116 + // + this.label116.AutoSize = true; + this.label116.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label116.Location = new System.Drawing.Point(354, 192); + this.label116.Name = "label116"; + this.label116.Size = new System.Drawing.Size(30, 13); + this.label116.TabIndex = 18; + this.label116.Text = "Size:"; + // + // tb_Options_Elevation_SRTM3_Database_FileName + // + this.tb_Options_Elevation_SRTM3_Database_FileName.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Elevation_SRTM3_Database_FileName.Location = new System.Drawing.Point(168, 189); + this.tb_Options_Elevation_SRTM3_Database_FileName.Name = "tb_Options_Elevation_SRTM3_Database_FileName"; + this.tb_Options_Elevation_SRTM3_Database_FileName.ReadOnly = true; + this.tb_Options_Elevation_SRTM3_Database_FileName.Size = new System.Drawing.Size(180, 20); + this.tb_Options_Elevation_SRTM3_Database_FileName.TabIndex = 17; + // + // label117 + // + this.label117.AutoSize = true; + this.label117.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label117.Location = new System.Drawing.Point(6, 192); + this.label117.Name = "label117"; + this.label117.Size = new System.Drawing.Size(141, 13); + this.label117.TabIndex = 16; + this.label117.Text = "SRTM3 Elevation Data:"; + // + // label16 + // + this.label16.AutoSize = true; + this.label16.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label16.Location = new System.Drawing.Point(446, 166); + this.label16.Name = "label16"; + this.label16.Size = new System.Drawing.Size(23, 13); + this.label16.TabIndex = 15; + this.label16.Text = "MB"; + // + // tb_Options_Elevation_GLOBE_Database_FileSize + // + this.tb_Options_Elevation_GLOBE_Database_FileSize.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Elevation_GLOBE_Database_FileSize.Location = new System.Drawing.Point(390, 163); + this.tb_Options_Elevation_GLOBE_Database_FileSize.Name = "tb_Options_Elevation_GLOBE_Database_FileSize"; + this.tb_Options_Elevation_GLOBE_Database_FileSize.ReadOnly = true; + this.tb_Options_Elevation_GLOBE_Database_FileSize.Size = new System.Drawing.Size(50, 20); + this.tb_Options_Elevation_GLOBE_Database_FileSize.TabIndex = 14; + this.tb_Options_Elevation_GLOBE_Database_FileSize.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; + // + // label113 + // + this.label113.AutoSize = true; + this.label113.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label113.Location = new System.Drawing.Point(354, 166); + this.label113.Name = "label113"; + this.label113.Size = new System.Drawing.Size(30, 13); + this.label113.TabIndex = 13; + this.label113.Text = "Size:"; + // + // tb_Options_Elevation_GLOBE_Database_FileName + // + this.tb_Options_Elevation_GLOBE_Database_FileName.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Elevation_GLOBE_Database_FileName.Location = new System.Drawing.Point(168, 163); + this.tb_Options_Elevation_GLOBE_Database_FileName.Name = "tb_Options_Elevation_GLOBE_Database_FileName"; + this.tb_Options_Elevation_GLOBE_Database_FileName.ReadOnly = true; + this.tb_Options_Elevation_GLOBE_Database_FileName.Size = new System.Drawing.Size(180, 20); + this.tb_Options_Elevation_GLOBE_Database_FileName.TabIndex = 12; + // + // label114 + // + this.label114.AutoSize = true; + this.label114.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label114.Location = new System.Drawing.Point(6, 166); + this.label114.Name = "label114"; + this.label114.Size = new System.Drawing.Size(140, 13); + this.label114.TabIndex = 11; + this.label114.Text = "GLOBE Elevation Data;"; + // + // label104 + // + this.label104.AutoSize = true; + this.label104.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label104.Location = new System.Drawing.Point(60, 10); + this.label104.Name = "label104"; + this.label104.Size = new System.Drawing.Size(473, 16); + this.label104.TabIndex = 10; + this.label104.Text = "This box is showing general info about the SQLite databases used by AirScout."; + // + // label101 + // + this.label101.AutoSize = true; + this.label101.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label101.Location = new System.Drawing.Point(446, 62); + this.label101.Name = "label101"; + this.label101.Size = new System.Drawing.Size(23, 13); + this.label101.TabIndex = 9; + this.label101.Text = "MB"; + // + // tb_Options_AirScout_Database_FileSize + // + this.tb_Options_AirScout_Database_FileSize.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_AirScout_Database_FileSize.Location = new System.Drawing.Point(390, 59); + this.tb_Options_AirScout_Database_FileSize.Name = "tb_Options_AirScout_Database_FileSize"; + this.tb_Options_AirScout_Database_FileSize.ReadOnly = true; + this.tb_Options_AirScout_Database_FileSize.Size = new System.Drawing.Size(50, 20); + this.tb_Options_AirScout_Database_FileSize.TabIndex = 8; + this.tb_Options_AirScout_Database_FileSize.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; + // + // label102 + // + this.label102.AutoSize = true; + this.label102.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label102.Location = new System.Drawing.Point(354, 62); + this.label102.Name = "label102"; + this.label102.Size = new System.Drawing.Size(30, 13); + this.label102.TabIndex = 7; + this.label102.Text = "Size:"; + // + // tb_Options_AirScout_Database_FileName + // + this.tb_Options_AirScout_Database_FileName.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_AirScout_Database_FileName.Location = new System.Drawing.Point(168, 59); + this.tb_Options_AirScout_Database_FileName.Name = "tb_Options_AirScout_Database_FileName"; + this.tb_Options_AirScout_Database_FileName.ReadOnly = true; + this.tb_Options_AirScout_Database_FileName.Size = new System.Drawing.Size(180, 20); + this.tb_Options_AirScout_Database_FileName.TabIndex = 6; + // + // label103 + // + this.label103.AutoSize = true; + this.label103.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label103.Location = new System.Drawing.Point(6, 62); + this.label103.Name = "label103"; + this.label103.Size = new System.Drawing.Size(83, 13); + this.label103.TabIndex = 5; + this.label103.Text = "Aircraft Data:"; + // + // label73 + // + this.label73.AutoSize = true; + this.label73.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label73.Location = new System.Drawing.Point(446, 36); + this.label73.Name = "label73"; + this.label73.Size = new System.Drawing.Size(23, 13); + this.label73.TabIndex = 4; + this.label73.Text = "MB"; + // + // tb_Options_ScoutBase_Database_FileSize + // + this.tb_Options_ScoutBase_Database_FileSize.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_ScoutBase_Database_FileSize.Location = new System.Drawing.Point(390, 33); + this.tb_Options_ScoutBase_Database_FileSize.Name = "tb_Options_ScoutBase_Database_FileSize"; + this.tb_Options_ScoutBase_Database_FileSize.ReadOnly = true; + this.tb_Options_ScoutBase_Database_FileSize.Size = new System.Drawing.Size(50, 20); + this.tb_Options_ScoutBase_Database_FileSize.TabIndex = 3; + this.tb_Options_ScoutBase_Database_FileSize.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; + // + // label50 + // + this.label50.AutoSize = true; + this.label50.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label50.Location = new System.Drawing.Point(354, 36); + this.label50.Name = "label50"; + this.label50.Size = new System.Drawing.Size(30, 13); + this.label50.TabIndex = 2; + this.label50.Text = "Size:"; + // + // tb_Options_ScoutBase_Database_FileName + // + this.tb_Options_ScoutBase_Database_FileName.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_ScoutBase_Database_FileName.Location = new System.Drawing.Point(168, 33); + this.tb_Options_ScoutBase_Database_FileName.Name = "tb_Options_ScoutBase_Database_FileName"; + this.tb_Options_ScoutBase_Database_FileName.ReadOnly = true; + this.tb_Options_ScoutBase_Database_FileName.Size = new System.Drawing.Size(180, 20); + this.tb_Options_ScoutBase_Database_FileName.TabIndex = 1; + // + // label49 + // + this.label49.AutoSize = true; + this.label49.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label49.Location = new System.Drawing.Point(6, 36); + this.label49.Name = "label49"; + this.label49.Size = new System.Drawing.Size(82, 13); + this.label49.TabIndex = 0; + this.label49.Text = "Station Data:"; + // + // tab_Options_Alarm + // + this.tab_Options_Alarm.BackColor = System.Drawing.SystemColors.Control; + this.tab_Options_Alarm.Controls.Add(this.groupBox21); + this.tab_Options_Alarm.Controls.Add(this.groupBox19); + this.tab_Options_Alarm.Controls.Add(this.groupBox20); + this.tab_Options_Alarm.Location = new System.Drawing.Point(4, 22); + this.tab_Options_Alarm.Name = "tab_Options_Alarm"; + this.tab_Options_Alarm.Size = new System.Drawing.Size(671, 460); + this.tab_Options_Alarm.TabIndex = 11; + this.tab_Options_Alarm.Text = "Alarm"; + this.tab_Options_Alarm.Enter += new System.EventHandler(this.tab_Options_Alarm_Enter); + // + // groupBox21 + // + this.groupBox21.Controls.Add(this.cb_Options_Alarm_Activate); + this.groupBox21.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox21.Location = new System.Drawing.Point(3, 13); + this.groupBox21.Name = "groupBox21"; + this.groupBox21.Size = new System.Drawing.Size(506, 45); + this.groupBox21.TabIndex = 8; + this.groupBox21.TabStop = false; + this.groupBox21.Text = "Activate Alarm"; + // + // cb_Options_Alarm_Activate + // + this.cb_Options_Alarm_Activate.AutoSize = true; + this.cb_Options_Alarm_Activate.Checked = global::AirScout.Properties.Settings.Default.Alarm_Activate; + this.cb_Options_Alarm_Activate.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Alarm_Activate", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Options_Alarm_Activate.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Options_Alarm_Activate.Location = new System.Drawing.Point(13, 19); + this.cb_Options_Alarm_Activate.Name = "cb_Options_Alarm_Activate"; + this.cb_Options_Alarm_Activate.Size = new System.Drawing.Size(94, 17); + this.cb_Options_Alarm_Activate.TabIndex = 1; + this.cb_Options_Alarm_Activate.Tag = ""; + this.cb_Options_Alarm_Activate.Text = "Activate Alarm"; + this.cb_Options_Alarm_Activate.UseVisualStyleBackColor = true; + // + // groupBox19 + // + this.groupBox19.Controls.Add(this.cb_Options_Alarm_PlaySound); + this.groupBox19.Controls.Add(this.cb_Options_Alarm_BringWindowToFront); + this.groupBox19.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox19.Location = new System.Drawing.Point(3, 160); + this.groupBox19.Name = "groupBox19"; + this.groupBox19.Size = new System.Drawing.Size(506, 96); + this.groupBox19.TabIndex = 7; + this.groupBox19.TabStop = false; + this.groupBox19.Text = "Alarm Settings"; + // + // cb_Options_Alarm_PlaySound + // + this.cb_Options_Alarm_PlaySound.AutoSize = true; + this.cb_Options_Alarm_PlaySound.Checked = global::AirScout.Properties.Settings.Default.Alarm_PlaySound; + this.cb_Options_Alarm_PlaySound.CheckState = System.Windows.Forms.CheckState.Checked; + this.cb_Options_Alarm_PlaySound.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Alarm_PlaySound", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Options_Alarm_PlaySound.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Options_Alarm_PlaySound.Location = new System.Drawing.Point(13, 53); + this.cb_Options_Alarm_PlaySound.Name = "cb_Options_Alarm_PlaySound"; + this.cb_Options_Alarm_PlaySound.Size = new System.Drawing.Size(78, 17); + this.cb_Options_Alarm_PlaySound.TabIndex = 1; + this.cb_Options_Alarm_PlaySound.Tag = ""; + this.cb_Options_Alarm_PlaySound.Text = "Play sound"; + this.cb_Options_Alarm_PlaySound.UseVisualStyleBackColor = true; + // + // cb_Options_Alarm_BringWindowToFront + // + this.cb_Options_Alarm_BringWindowToFront.AutoSize = true; + this.cb_Options_Alarm_BringWindowToFront.Checked = true; + this.cb_Options_Alarm_BringWindowToFront.CheckState = System.Windows.Forms.CheckState.Checked; + this.cb_Options_Alarm_BringWindowToFront.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Options_Alarm_BringWindowToFront.Location = new System.Drawing.Point(13, 30); + this.cb_Options_Alarm_BringWindowToFront.Name = "cb_Options_Alarm_BringWindowToFront"; + this.cb_Options_Alarm_BringWindowToFront.Size = new System.Drawing.Size(125, 17); + this.cb_Options_Alarm_BringWindowToFront.TabIndex = 0; + this.cb_Options_Alarm_BringWindowToFront.Tag = ""; + this.cb_Options_Alarm_BringWindowToFront.Text = "Bring window to front"; + this.cb_Options_Alarm_BringWindowToFront.UseVisualStyleBackColor = true; + // + // groupBox20 + // + this.groupBox20.Controls.Add(this.tb_Options_Alarm_Distance); + this.groupBox20.Controls.Add(this.label36); + this.groupBox20.Controls.Add(this.label56); + this.groupBox20.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox20.Location = new System.Drawing.Point(3, 64); + this.groupBox20.Name = "groupBox20"; + this.groupBox20.Size = new System.Drawing.Size(506, 90); + this.groupBox20.TabIndex = 6; + this.groupBox20.TabStop = false; + this.groupBox20.Text = "Generate Alarm"; + // + // tb_Options_Alarm_Distance + // + this.tb_Options_Alarm_Distance.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScout.Properties.Settings.Default, "Alarm_Distance", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Options_Alarm_Distance.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Alarm_Distance.FormatSpecifier = "F0"; + this.tb_Options_Alarm_Distance.Location = new System.Drawing.Point(143, 49); + this.tb_Options_Alarm_Distance.MaxValue = 1000D; + this.tb_Options_Alarm_Distance.MinValue = 0D; + this.tb_Options_Alarm_Distance.Name = "tb_Options_Alarm_Distance"; + this.tb_Options_Alarm_Distance.Size = new System.Drawing.Size(75, 22); + this.tb_Options_Alarm_Distance.TabIndex = 3; + this.tb_Options_Alarm_Distance.Text = "100"; + this.tb_Options_Alarm_Distance.Value = global::AirScout.Properties.Settings.Default.Alarm_Distance; + // + // label36 + // + this.label36.AutoSize = true; + this.label36.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label36.Location = new System.Drawing.Point(249, 52); + this.label36.Name = "label36"; + this.label36.Size = new System.Drawing.Size(21, 13); + this.label36.TabIndex = 2; + this.label36.Text = "km"; + // + // label56 + // + this.label56.AutoSize = true; + this.label56.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label56.Location = new System.Drawing.Point(10, 23); + this.label56.Name = "label56"; + this.label56.Size = new System.Drawing.Size(443, 13); + this.label56.TabIndex = 0; + this.label56.Text = "If a plane is heading towards the path with suitable altitude, generate an alarm " + + "at distance of:"; + // + // tab_Options_Network + // + this.tab_Options_Network.BackColor = System.Drawing.SystemColors.Control; + this.tab_Options_Network.Controls.Add(this.label86); + this.tab_Options_Network.Controls.Add(this.groupBox32); + this.tab_Options_Network.Controls.Add(this.groupBox31); + this.tab_Options_Network.Controls.Add(this.groupBox24); + this.tab_Options_Network.Location = new System.Drawing.Point(4, 22); + this.tab_Options_Network.Name = "tab_Options_Network"; + this.tab_Options_Network.Size = new System.Drawing.Size(671, 460); + this.tab_Options_Network.TabIndex = 12; + this.tab_Options_Network.Text = "Network"; + this.tab_Options_Network.Enter += new System.EventHandler(this.tab_Options_Network_Enter); + this.tab_Options_Network.Validating += new System.ComponentModel.CancelEventHandler(this.tab_Options_Network_Validating); + // + // label86 + // + this.label86.BackColor = System.Drawing.Color.LightSalmon; + this.label86.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label86.ForeColor = System.Drawing.Color.White; + this.label86.Location = new System.Drawing.Point(12, 339); + this.label86.Margin = new System.Windows.Forms.Padding(10); + this.label86.Name = "label86"; + this.label86.Size = new System.Drawing.Size(647, 116); + this.label86.TabIndex = 10; + this.label86.Text = resources.GetString("label86.Text"); + // + // groupBox32 + // + this.groupBox32.Controls.Add(this.tb_Options_Webserver_Port); + this.groupBox32.Controls.Add(this.label85); + this.groupBox32.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox32.Location = new System.Drawing.Point(325, 245); + this.groupBox32.Name = "groupBox32"; + this.groupBox32.Size = new System.Drawing.Size(334, 91); + this.groupBox32.TabIndex = 4; + this.groupBox32.TabStop = false; + this.groupBox32.Text = "HTTP Server Settings"; + // + // tb_Options_Webserver_Port + // + this.tb_Options_Webserver_Port.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScout.Properties.Settings.Default, "Webserver_Port", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Options_Webserver_Port.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Webserver_Port.FormatSpecifier = "F0"; + this.tb_Options_Webserver_Port.Location = new System.Drawing.Point(150, 28); + this.tb_Options_Webserver_Port.MaxValue = 65535; + this.tb_Options_Webserver_Port.MinValue = 0; + this.tb_Options_Webserver_Port.Name = "tb_Options_Webserver_Port"; + this.tb_Options_Webserver_Port.Size = new System.Drawing.Size(57, 22); + this.tb_Options_Webserver_Port.TabIndex = 8; + this.tb_Options_Webserver_Port.Text = "9880"; + this.tb_Options_Webserver_Port.Value = global::AirScout.Properties.Settings.Default.Webserver_Port; + // + // label85 + // + this.label85.AutoSize = true; + this.label85.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label85.Location = new System.Drawing.Point(6, 33); + this.label85.Name = "label85"; + this.label85.Size = new System.Drawing.Size(138, 13); + this.label85.TabIndex = 7; + this.label85.Text = "AirScout HTTP Server Port:"; + // + // groupBox31 + // + this.groupBox31.Controls.Add(this.cb_Options_Server_Activate); + this.groupBox31.Controls.Add(this.label3); + this.groupBox31.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox31.Location = new System.Drawing.Point(12, 12); + this.groupBox31.Name = "groupBox31"; + this.groupBox31.Size = new System.Drawing.Size(647, 227); + this.groupBox31.TabIndex = 9; + this.groupBox31.TabStop = false; + this.groupBox31.Text = "Activate Server"; + // + // cb_Options_Server_Activate + // + this.cb_Options_Server_Activate.AutoSize = true; + this.cb_Options_Server_Activate.Checked = global::AirScout.Properties.Settings.Default.Server_Activate; + this.cb_Options_Server_Activate.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Server_Activate", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Options_Server_Activate.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Options_Server_Activate.Location = new System.Drawing.Point(13, 23); + this.cb_Options_Server_Activate.Name = "cb_Options_Server_Activate"; + this.cb_Options_Server_Activate.Size = new System.Drawing.Size(145, 17); + this.cb_Options_Server_Activate.TabIndex = 1; + this.cb_Options_Server_Activate.Tag = ""; + this.cb_Options_Server_Activate.Text = "Activate Nertwork Server"; + this.cb_Options_Server_Activate.UseVisualStyleBackColor = true; + // + // label3 + // + this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label3.Location = new System.Drawing.Point(10, 49); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(631, 176); + this.label3.TabIndex = 3; + this.label3.Text = resources.GetString("label3.Text"); + // + // groupBox24 + // + this.groupBox24.Controls.Add(this.tb_Options_Server_Port); + this.groupBox24.Controls.Add(this.label6); + this.groupBox24.Controls.Add(this.label4); + this.groupBox24.Controls.Add(this.tb_Options_Server_Name); + this.groupBox24.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox24.Location = new System.Drawing.Point(12, 245); + this.groupBox24.Name = "groupBox24"; + this.groupBox24.Size = new System.Drawing.Size(307, 91); + this.groupBox24.TabIndex = 8; + this.groupBox24.TabStop = false; + this.groupBox24.Text = "UDP Server Settings"; + // + // tb_Options_Server_Port + // + this.tb_Options_Server_Port.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScout.Properties.Settings.Default, "Server_Port", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Options_Server_Port.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Server_Port.FormatSpecifier = "F0"; + this.tb_Options_Server_Port.Location = new System.Drawing.Point(153, 56); + this.tb_Options_Server_Port.MaxValue = 65535; + this.tb_Options_Server_Port.MinValue = 0; + this.tb_Options_Server_Port.Name = "tb_Options_Server_Port"; + this.tb_Options_Server_Port.Size = new System.Drawing.Size(57, 22); + this.tb_Options_Server_Port.TabIndex = 6; + this.tb_Options_Server_Port.Text = "9872"; + this.tb_Options_Server_Port.Value = global::AirScout.Properties.Settings.Default.Server_Port; + // + // label6 + // + this.label6.AutoSize = true; + this.label6.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label6.Location = new System.Drawing.Point(6, 60); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(132, 13); + this.label6.TabIndex = 5; + this.label6.Text = "AirScout UDP Server Port:"; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label4.Location = new System.Drawing.Point(6, 33); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(141, 13); + this.label4.TabIndex = 4; + this.label4.Text = "AirScout UDP Server Name:"; + // + // tb_Options_Server_Name + // + this.tb_Options_Server_Name.BackColor = System.Drawing.Color.FloralWhite; + this.tb_Options_Server_Name.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::AirScout.Properties.Settings.Default, "Server_Name", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Options_Server_Name.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Server_Name.Location = new System.Drawing.Point(153, 30); + this.tb_Options_Server_Name.Name = "tb_Options_Server_Name"; + this.tb_Options_Server_Name.Size = new System.Drawing.Size(57, 20); + this.tb_Options_Server_Name.TabIndex = 2; + this.tb_Options_Server_Name.Tag = ""; + this.tb_Options_Server_Name.Text = global::AirScout.Properties.Settings.Default.Server_Name; + // + // tab_Options_SpecLab + // + this.tab_Options_SpecLab.BackColor = System.Drawing.SystemColors.Control; + this.tab_Options_SpecLab.Controls.Add(this.groupBox3); + this.tab_Options_SpecLab.Location = new System.Drawing.Point(4, 22); + this.tab_Options_SpecLab.Name = "tab_Options_SpecLab"; + this.tab_Options_SpecLab.Size = new System.Drawing.Size(671, 460); + this.tab_Options_SpecLab.TabIndex = 13; + this.tab_Options_SpecLab.Text = "Spectrum"; + this.tab_Options_SpecLab.Enter += new System.EventHandler(this.tab_Options_SpecLab_Enter); + this.tab_Options_SpecLab.Validating += new System.ComponentModel.CancelEventHandler(this.tab_Options_SpecLab_Validating); + // + // groupBox3 + // + this.groupBox3.Controls.Add(this.tb_Options_SpecLab_UpdateInterval); + this.groupBox3.Controls.Add(this.tb_Options_SpecLab_F2); + this.groupBox3.Controls.Add(this.tb_Options_SpecLab_F1); + this.groupBox3.Controls.Add(this.label70); + this.groupBox3.Controls.Add(this.label71); + this.groupBox3.Controls.Add(this.label68); + this.groupBox3.Controls.Add(this.label69); + this.groupBox3.Controls.Add(this.label67); + this.groupBox3.Controls.Add(this.label66); + this.groupBox3.Controls.Add(this.tb_SpecLab_FileName); + this.groupBox3.Controls.Add(this.label63); + this.groupBox3.Controls.Add(this.label64); + this.groupBox3.Controls.Add(this.label65); + this.groupBox3.Controls.Add(this.tb_SpecLab_URL); + this.groupBox3.Controls.Add(this.cb_SpecLab_Enabled); + this.groupBox3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox3.Location = new System.Drawing.Point(3, 13); + this.groupBox3.Name = "groupBox3"; + this.groupBox3.Size = new System.Drawing.Size(506, 279); + this.groupBox3.TabIndex = 8; + this.groupBox3.TabStop = false; + this.groupBox3.Text = "Spectrum Lab Settings"; + // + // tb_Options_SpecLab_UpdateInterval + // + this.tb_Options_SpecLab_UpdateInterval.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScout.Properties.Settings.Default, "SpecLab_Update", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Options_SpecLab_UpdateInterval.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_SpecLab_UpdateInterval.FormatSpecifier = "F1"; + this.tb_Options_SpecLab_UpdateInterval.Location = new System.Drawing.Point(106, 230); + this.tb_Options_SpecLab_UpdateInterval.MaxValue = 100D; + this.tb_Options_SpecLab_UpdateInterval.MinValue = 0D; + this.tb_Options_SpecLab_UpdateInterval.Name = "tb_Options_SpecLab_UpdateInterval"; + this.tb_Options_SpecLab_UpdateInterval.Size = new System.Drawing.Size(63, 22); + this.tb_Options_SpecLab_UpdateInterval.TabIndex = 18; + this.tb_Options_SpecLab_UpdateInterval.Text = "1.0"; + this.tb_Options_SpecLab_UpdateInterval.Value = global::AirScout.Properties.Settings.Default.SpecLab_Update; + // + // tb_Options_SpecLab_F2 + // + this.tb_Options_SpecLab_F2.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScout.Properties.Settings.Default, "SpecLab_F2", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Options_SpecLab_F2.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_SpecLab_F2.FormatSpecifier = "F0"; + this.tb_Options_SpecLab_F2.Location = new System.Drawing.Point(109, 205); + this.tb_Options_SpecLab_F2.MaxValue = 10000; + this.tb_Options_SpecLab_F2.MinValue = 0; + this.tb_Options_SpecLab_F2.Name = "tb_Options_SpecLab_F2"; + this.tb_Options_SpecLab_F2.Size = new System.Drawing.Size(60, 22); + this.tb_Options_SpecLab_F2.TabIndex = 17; + this.tb_Options_SpecLab_F2.Text = "1600"; + this.tb_Options_SpecLab_F2.Value = global::AirScout.Properties.Settings.Default.SpecLab_F2; + // + // tb_Options_SpecLab_F1 + // + this.tb_Options_SpecLab_F1.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScout.Properties.Settings.Default, "SpecLab_F1", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Options_SpecLab_F1.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_SpecLab_F1.FormatSpecifier = "F0"; + this.tb_Options_SpecLab_F1.Location = new System.Drawing.Point(109, 179); + this.tb_Options_SpecLab_F1.MaxValue = 10000; + this.tb_Options_SpecLab_F1.MinValue = 0; + this.tb_Options_SpecLab_F1.Name = "tb_Options_SpecLab_F1"; + this.tb_Options_SpecLab_F1.Size = new System.Drawing.Size(60, 22); + this.tb_Options_SpecLab_F1.TabIndex = 16; + this.tb_Options_SpecLab_F1.Text = "400"; + this.tb_Options_SpecLab_F1.Value = global::AirScout.Properties.Settings.Default.SpecLab_F1; + // + // label70 + // + this.label70.AutoSize = true; + this.label70.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label70.Location = new System.Drawing.Point(175, 234); + this.label70.Name = "label70"; + this.label70.Size = new System.Drawing.Size(24, 13); + this.label70.TabIndex = 15; + this.label70.Text = "sec"; + // + // label71 + // + this.label71.AutoSize = true; + this.label71.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label71.Location = new System.Drawing.Point(17, 234); + this.label71.Name = "label71"; + this.label71.Size = new System.Drawing.Size(83, 13); + this.label71.TabIndex = 14; + this.label71.Text = "Update Interval:"; + // + // label68 + // + this.label68.AutoSize = true; + this.label68.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label68.Location = new System.Drawing.Point(175, 208); + this.label68.Name = "label68"; + this.label68.Size = new System.Drawing.Size(20, 13); + this.label68.TabIndex = 12; + this.label68.Text = "Hz"; + // + // label69 + // + this.label69.AutoSize = true; + this.label69.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label69.Location = new System.Drawing.Point(17, 208); + this.label69.Name = "label69"; + this.label69.Size = new System.Drawing.Size(22, 13); + this.label69.TabIndex = 11; + this.label69.Text = "F2:"; + // + // label67 + // + this.label67.AutoSize = true; + this.label67.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label67.Location = new System.Drawing.Point(175, 182); + this.label67.Name = "label67"; + this.label67.Size = new System.Drawing.Size(20, 13); + this.label67.TabIndex = 9; + this.label67.Text = "Hz"; + // + // label66 + // + this.label66.AutoSize = true; + this.label66.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label66.Location = new System.Drawing.Point(17, 182); + this.label66.Name = "label66"; + this.label66.Size = new System.Drawing.Size(22, 13); + this.label66.TabIndex = 8; + this.label66.Text = "F1:"; + // + // tb_SpecLab_FileName + // + this.tb_SpecLab_FileName.BackColor = System.Drawing.Color.FloralWhite; + this.tb_SpecLab_FileName.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::AirScout.Properties.Settings.Default, "SpecLab_FileName", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_SpecLab_FileName.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_SpecLab_FileName.Location = new System.Drawing.Point(109, 153); + this.tb_SpecLab_FileName.Name = "tb_SpecLab_FileName"; + this.tb_SpecLab_FileName.Size = new System.Drawing.Size(347, 20); + this.tb_SpecLab_FileName.TabIndex = 6; + this.tb_SpecLab_FileName.Tag = ""; + this.tb_SpecLab_FileName.Text = global::AirScout.Properties.Settings.Default.SpecLab_FileName; + // + // label63 + // + this.label63.AutoSize = true; + this.label63.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label63.Location = new System.Drawing.Point(15, 156); + this.label63.Name = "label63"; + this.label63.Size = new System.Drawing.Size(86, 13); + this.label63.TabIndex = 5; + this.label63.Text = "Server Filename:"; + // + // label64 + // + this.label64.AutoSize = true; + this.label64.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label64.Location = new System.Drawing.Point(15, 129); + this.label64.Name = "label64"; + this.label64.Size = new System.Drawing.Size(63, 13); + this.label64.TabIndex = 4; + this.label64.Text = "Server URL"; + // + // label65 + // + this.label65.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label65.Location = new System.Drawing.Point(15, 30); + this.label65.Name = "label65"; + this.label65.Size = new System.Drawing.Size(441, 57); + this.label65.TabIndex = 3; + this.label65.Text = resources.GetString("label65.Text"); + // + // tb_SpecLab_URL + // + this.tb_SpecLab_URL.BackColor = System.Drawing.Color.FloralWhite; + this.tb_SpecLab_URL.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::AirScout.Properties.Settings.Default, "SpecLab_URL", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_SpecLab_URL.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_SpecLab_URL.Location = new System.Drawing.Point(109, 126); + this.tb_SpecLab_URL.Name = "tb_SpecLab_URL"; + this.tb_SpecLab_URL.Size = new System.Drawing.Size(347, 20); + this.tb_SpecLab_URL.TabIndex = 2; + this.tb_SpecLab_URL.Tag = ""; + this.tb_SpecLab_URL.Text = global::AirScout.Properties.Settings.Default.SpecLab_URL; + // + // cb_SpecLab_Enabled + // + this.cb_SpecLab_Enabled.AutoSize = true; + this.cb_SpecLab_Enabled.Checked = global::AirScout.Properties.Settings.Default.SpecLab_Enabled; + this.cb_SpecLab_Enabled.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "SpecLab_Enabled", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_SpecLab_Enabled.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_SpecLab_Enabled.Location = new System.Drawing.Point(18, 90); + this.cb_SpecLab_Enabled.Name = "cb_SpecLab_Enabled"; + this.cb_SpecLab_Enabled.Size = new System.Drawing.Size(122, 17); + this.cb_SpecLab_Enabled.TabIndex = 1; + this.cb_SpecLab_Enabled.Tag = ""; + this.cb_SpecLab_Enabled.Text = "Activate Connection"; + this.cb_SpecLab_Enabled.UseVisualStyleBackColor = true; + // + // tc_Track + // + this.tc_Track.BackColor = System.Drawing.SystemColors.Control; + this.tc_Track.Controls.Add(this.groupBox36); + this.tc_Track.Controls.Add(this.groupBox35); + this.tc_Track.Controls.Add(this.groupBox34); + this.tc_Track.Controls.Add(this.groupBox33); + this.tc_Track.Controls.Add(this.groupBox28); + this.tc_Track.Location = new System.Drawing.Point(4, 22); + this.tc_Track.Name = "tc_Track"; + this.tc_Track.Size = new System.Drawing.Size(671, 460); + this.tc_Track.TabIndex = 14; + this.tc_Track.Text = "Track"; + this.tc_Track.Validating += new System.ComponentModel.CancelEventHandler(this.tc_Track_Validating); + // + // groupBox36 + // + this.groupBox36.Controls.Add(this.rb_Options_Track_File_None); + this.groupBox36.Controls.Add(this.rb_Options_Track_File_WSJT); + this.groupBox36.Controls.Add(this.rb_Options_Track_File_Native); + this.groupBox36.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox36.Location = new System.Drawing.Point(16, 347); + this.groupBox36.Name = "groupBox36"; + this.groupBox36.Size = new System.Drawing.Size(619, 96); + this.groupBox36.TabIndex = 12; + this.groupBox36.TabStop = false; + this.groupBox36.Text = "File Output"; + // + // rb_Options_Track_File_None + // + this.rb_Options_Track_File_None.AutoSize = true; + this.rb_Options_Track_File_None.Checked = global::AirScout.Properties.Settings.Default.Track_File_None; + this.rb_Options_Track_File_None.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Track_File_None", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.rb_Options_Track_File_None.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.rb_Options_Track_File_None.Location = new System.Drawing.Point(17, 19); + this.rb_Options_Track_File_None.Name = "rb_Options_Track_File_None"; + this.rb_Options_Track_File_None.Size = new System.Drawing.Size(51, 17); + this.rb_Options_Track_File_None.TabIndex = 12; + this.rb_Options_Track_File_None.TabStop = true; + this.rb_Options_Track_File_None.Tag = ""; + this.rb_Options_Track_File_None.Text = "None"; + this.rb_Options_Track_File_None.UseVisualStyleBackColor = true; + // + // rb_Options_Track_File_WSJT + // + this.rb_Options_Track_File_WSJT.AutoSize = true; + this.rb_Options_Track_File_WSJT.Checked = global::AirScout.Properties.Settings.Default.Track_File_WSJT; + this.rb_Options_Track_File_WSJT.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Track_File_WSJT", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.rb_Options_Track_File_WSJT.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.rb_Options_Track_File_WSJT.Location = new System.Drawing.Point(17, 66); + this.rb_Options_Track_File_WSJT.Name = "rb_Options_Track_File_WSJT"; + this.rb_Options_Track_File_WSJT.Size = new System.Drawing.Size(84, 17); + this.rb_Options_Track_File_WSJT.TabIndex = 7; + this.rb_Options_Track_File_WSJT.Tag = ""; + this.rb_Options_Track_File_WSJT.Text = "WSJT Az/El"; + this.rb_Options_Track_File_WSJT.UseVisualStyleBackColor = true; + // + // rb_Options_Track_File_Native + // + this.rb_Options_Track_File_Native.AutoSize = true; + this.rb_Options_Track_File_Native.Checked = global::AirScout.Properties.Settings.Default.Track_File_Native; + this.rb_Options_Track_File_Native.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Track_File_Native", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.rb_Options_Track_File_Native.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.rb_Options_Track_File_Native.Location = new System.Drawing.Point(17, 43); + this.rb_Options_Track_File_Native.Name = "rb_Options_Track_File_Native"; + this.rb_Options_Track_File_Native.Size = new System.Drawing.Size(85, 17); + this.rb_Options_Track_File_Native.TabIndex = 6; + this.rb_Options_Track_File_Native.Tag = ""; + this.rb_Options_Track_File_Native.Text = "Native Az/El"; + this.rb_Options_Track_File_Native.UseVisualStyleBackColor = true; + // + // groupBox35 + // + this.groupBox35.Controls.Add(this.rb_Options_Track_DDE_None); + this.groupBox35.Controls.Add(this.rb_Options_Track_DDE_HRD); + this.groupBox35.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox35.Location = new System.Drawing.Point(16, 261); + this.groupBox35.Name = "groupBox35"; + this.groupBox35.Size = new System.Drawing.Size(619, 80); + this.groupBox35.TabIndex = 11; + this.groupBox35.TabStop = false; + this.groupBox35.Text = "DDE Output"; + // + // rb_Options_Track_DDE_None + // + this.rb_Options_Track_DDE_None.AutoSize = true; + this.rb_Options_Track_DDE_None.Checked = global::AirScout.Properties.Settings.Default.Track_DDE_None; + this.rb_Options_Track_DDE_None.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Track_DDE_None", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.rb_Options_Track_DDE_None.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.rb_Options_Track_DDE_None.Location = new System.Drawing.Point(17, 19); + this.rb_Options_Track_DDE_None.Name = "rb_Options_Track_DDE_None"; + this.rb_Options_Track_DDE_None.Size = new System.Drawing.Size(51, 17); + this.rb_Options_Track_DDE_None.TabIndex = 11; + this.rb_Options_Track_DDE_None.TabStop = true; + this.rb_Options_Track_DDE_None.Tag = ""; + this.rb_Options_Track_DDE_None.Text = "None"; + this.rb_Options_Track_DDE_None.UseVisualStyleBackColor = true; + // + // rb_Options_Track_DDE_HRD + // + this.rb_Options_Track_DDE_HRD.AutoSize = true; + this.rb_Options_Track_DDE_HRD.Checked = global::AirScout.Properties.Settings.Default.Track_DDE_HRD; + this.rb_Options_Track_DDE_HRD.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Track_DDE_HRD", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.rb_Options_Track_DDE_HRD.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.rb_Options_Track_DDE_HRD.Location = new System.Drawing.Point(17, 42); + this.rb_Options_Track_DDE_HRD.Name = "rb_Options_Track_DDE_HRD"; + this.rb_Options_Track_DDE_HRD.Size = new System.Drawing.Size(202, 17); + this.rb_Options_Track_DDE_HRD.TabIndex = 6; + this.rb_Options_Track_DDE_HRD.Tag = ""; + this.rb_Options_Track_DDE_HRD.Text = "Ham Radio Deluxe (HRDRotator.exe)"; + this.rb_Options_Track_DDE_HRD.UseVisualStyleBackColor = true; + // + // groupBox34 + // + this.groupBox34.Controls.Add(this.tb_Options_Track_UDP_AirScout_Port); + this.groupBox34.Controls.Add(this.tb_Options_Track_UDP_WinTest_Port); + this.groupBox34.Controls.Add(this.label90); + this.groupBox34.Controls.Add(this.label89); + this.groupBox34.Controls.Add(this.rb_Options_Track_UDP_None); + this.groupBox34.Controls.Add(this.rb_Options_Track_UDP_AirScout); + this.groupBox34.Controls.Add(this.rb_Options_Track_UDP_WinTest); + this.groupBox34.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox34.Location = new System.Drawing.Point(16, 157); + this.groupBox34.Name = "groupBox34"; + this.groupBox34.Size = new System.Drawing.Size(619, 98); + this.groupBox34.TabIndex = 10; + this.groupBox34.TabStop = false; + this.groupBox34.Text = "Network Output"; + // + // tb_Options_Track_UDP_AirScout_Port + // + this.tb_Options_Track_UDP_AirScout_Port.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScout.Properties.Settings.Default, "Track_UDP_AirScout_Port", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Options_Track_UDP_AirScout_Port.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Track_UDP_AirScout_Port.FormatSpecifier = "F0"; + this.tb_Options_Track_UDP_AirScout_Port.Location = new System.Drawing.Point(526, 71); + this.tb_Options_Track_UDP_AirScout_Port.MaxValue = 0; + this.tb_Options_Track_UDP_AirScout_Port.MinValue = 0; + this.tb_Options_Track_UDP_AirScout_Port.Name = "tb_Options_Track_UDP_AirScout_Port"; + this.tb_Options_Track_UDP_AirScout_Port.Size = new System.Drawing.Size(43, 22); + this.tb_Options_Track_UDP_AirScout_Port.TabIndex = 17; + this.tb_Options_Track_UDP_AirScout_Port.Text = "9872"; + this.tb_Options_Track_UDP_AirScout_Port.Value = global::AirScout.Properties.Settings.Default.Track_UDP_AirScout_Port; + // + // tb_Options_Track_UDP_WinTest_Port + // + this.tb_Options_Track_UDP_WinTest_Port.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScout.Properties.Settings.Default, "Track_UDP_WinTest_Port", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Options_Track_UDP_WinTest_Port.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Track_UDP_WinTest_Port.FormatSpecifier = "F0"; + this.tb_Options_Track_UDP_WinTest_Port.Location = new System.Drawing.Point(526, 42); + this.tb_Options_Track_UDP_WinTest_Port.MaxValue = 0; + this.tb_Options_Track_UDP_WinTest_Port.MinValue = 0; + this.tb_Options_Track_UDP_WinTest_Port.Name = "tb_Options_Track_UDP_WinTest_Port"; + this.tb_Options_Track_UDP_WinTest_Port.Size = new System.Drawing.Size(43, 22); + this.tb_Options_Track_UDP_WinTest_Port.TabIndex = 16; + this.tb_Options_Track_UDP_WinTest_Port.Text = "9871"; + this.tb_Options_Track_UDP_WinTest_Port.Value = global::AirScout.Properties.Settings.Default.Track_UDP_WinTest_Port; + // + // label90 + // + this.label90.AutoSize = true; + this.label90.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label90.Location = new System.Drawing.Point(491, 75); + this.label90.Name = "label90"; + this.label90.Size = new System.Drawing.Size(29, 13); + this.label90.TabIndex = 15; + this.label90.Text = "Port:"; + // + // label89 + // + this.label89.AutoSize = true; + this.label89.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label89.Location = new System.Drawing.Point(491, 46); + this.label89.Name = "label89"; + this.label89.Size = new System.Drawing.Size(29, 13); + this.label89.TabIndex = 13; + this.label89.Text = "Port:"; + // + // rb_Options_Track_UDP_None + // + this.rb_Options_Track_UDP_None.AutoSize = true; + this.rb_Options_Track_UDP_None.Checked = global::AirScout.Properties.Settings.Default.Track_UDP_None; + this.rb_Options_Track_UDP_None.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Track_UDP_None", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.rb_Options_Track_UDP_None.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.rb_Options_Track_UDP_None.Location = new System.Drawing.Point(17, 19); + this.rb_Options_Track_UDP_None.Name = "rb_Options_Track_UDP_None"; + this.rb_Options_Track_UDP_None.Size = new System.Drawing.Size(51, 17); + this.rb_Options_Track_UDP_None.TabIndex = 11; + this.rb_Options_Track_UDP_None.TabStop = true; + this.rb_Options_Track_UDP_None.Tag = ""; + this.rb_Options_Track_UDP_None.Text = "None"; + this.rb_Options_Track_UDP_None.UseVisualStyleBackColor = true; + // + // rb_Options_Track_UDP_AirScout + // + this.rb_Options_Track_UDP_AirScout.AutoSize = true; + this.rb_Options_Track_UDP_AirScout.Checked = global::AirScout.Properties.Settings.Default.Track_UDP_AirScout; + this.rb_Options_Track_UDP_AirScout.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Track_UDP_AirScout", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.rb_Options_Track_UDP_AirScout.Enabled = false; + this.rb_Options_Track_UDP_AirScout.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.rb_Options_Track_UDP_AirScout.Location = new System.Drawing.Point(17, 65); + this.rb_Options_Track_UDP_AirScout.Name = "rb_Options_Track_UDP_AirScout"; + this.rb_Options_Track_UDP_AirScout.Size = new System.Drawing.Size(280, 17); + this.rb_Options_Track_UDP_AirScout.TabIndex = 7; + this.rb_Options_Track_UDP_AirScout.Tag = ""; + this.rb_Options_Track_UDP_AirScout.Text = "UDP Broadcast (AirScout) Az/El (not implemented yet)"; + this.rb_Options_Track_UDP_AirScout.UseVisualStyleBackColor = true; + // + // rb_Options_Track_UDP_WinTest + // + this.rb_Options_Track_UDP_WinTest.AutoSize = true; + this.rb_Options_Track_UDP_WinTest.Checked = global::AirScout.Properties.Settings.Default.Track_UDP_WinTest; + this.rb_Options_Track_UDP_WinTest.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Track_UDP_WinTest", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.rb_Options_Track_UDP_WinTest.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.rb_Options_Track_UDP_WinTest.Location = new System.Drawing.Point(17, 42); + this.rb_Options_Track_UDP_WinTest.Name = "rb_Options_Track_UDP_WinTest"; + this.rb_Options_Track_UDP_WinTest.Size = new System.Drawing.Size(188, 17); + this.rb_Options_Track_UDP_WinTest.TabIndex = 6; + this.rb_Options_Track_UDP_WinTest.Tag = ""; + this.rb_Options_Track_UDP_WinTest.Text = "UDP Broadcast (Win-Test) Az only"; + this.rb_Options_Track_UDP_WinTest.UseVisualStyleBackColor = true; + // + // groupBox33 + // + this.groupBox33.Controls.Add(this.tb_Options_Track_Serial_Baudrate); + this.groupBox33.Controls.Add(this.rb_Options_Track_Serial_None); + this.groupBox33.Controls.Add(this.label88); + this.groupBox33.Controls.Add(this.label87); + this.groupBox33.Controls.Add(this.tb_Options_Track_Serial_Port); + this.groupBox33.Controls.Add(this.rb_Options_Track_Serial_GS232_AZEL); + this.groupBox33.Controls.Add(this.rb_Options_Track_Serial_GS232_AZ); + this.groupBox33.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox33.Location = new System.Drawing.Point(16, 64); + this.groupBox33.Name = "groupBox33"; + this.groupBox33.Size = new System.Drawing.Size(619, 87); + this.groupBox33.TabIndex = 9; + this.groupBox33.TabStop = false; + this.groupBox33.Text = "Serial Output"; + // + // tb_Options_Track_Serial_Baudrate + // + this.tb_Options_Track_Serial_Baudrate.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScout.Properties.Settings.Default, "Track_Serial_Baudrate", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Options_Track_Serial_Baudrate.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Track_Serial_Baudrate.FormatSpecifier = "F0"; + this.tb_Options_Track_Serial_Baudrate.Location = new System.Drawing.Point(526, 13); + this.tb_Options_Track_Serial_Baudrate.MaxValue = 115200; + this.tb_Options_Track_Serial_Baudrate.MinValue = 0; + this.tb_Options_Track_Serial_Baudrate.Name = "tb_Options_Track_Serial_Baudrate"; + this.tb_Options_Track_Serial_Baudrate.Size = new System.Drawing.Size(72, 22); + this.tb_Options_Track_Serial_Baudrate.TabIndex = 11; + this.tb_Options_Track_Serial_Baudrate.Text = "4800"; + this.tb_Options_Track_Serial_Baudrate.Value = global::AirScout.Properties.Settings.Default.Track_Serial_Baudrate; + // + // rb_Options_Track_Serial_None + // + this.rb_Options_Track_Serial_None.AutoSize = true; + this.rb_Options_Track_Serial_None.Checked = global::AirScout.Properties.Settings.Default.Track_Serial_None; + this.rb_Options_Track_Serial_None.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Track_Serial_None", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.rb_Options_Track_Serial_None.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.rb_Options_Track_Serial_None.Location = new System.Drawing.Point(17, 15); + this.rb_Options_Track_Serial_None.Name = "rb_Options_Track_Serial_None"; + this.rb_Options_Track_Serial_None.Size = new System.Drawing.Size(51, 17); + this.rb_Options_Track_Serial_None.TabIndex = 10; + this.rb_Options_Track_Serial_None.TabStop = true; + this.rb_Options_Track_Serial_None.Tag = ""; + this.rb_Options_Track_Serial_None.Text = "None"; + this.rb_Options_Track_Serial_None.UseVisualStyleBackColor = true; + // + // label88 + // + this.label88.AutoSize = true; + this.label88.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label88.Location = new System.Drawing.Point(467, 17); + this.label88.Name = "label88"; + this.label88.Size = new System.Drawing.Size(53, 13); + this.label88.TabIndex = 9; + this.label88.Text = "Baudrate:"; + // + // label87 + // + this.label87.AutoSize = true; + this.label87.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label87.Location = new System.Drawing.Point(373, 17); + this.label87.Name = "label87"; + this.label87.Size = new System.Drawing.Size(29, 13); + this.label87.TabIndex = 7; + this.label87.Text = "Port:"; + // + // tb_Options_Track_Serial_Port + // + this.tb_Options_Track_Serial_Port.CharacterCasing = System.Windows.Forms.CharacterCasing.Upper; + this.tb_Options_Track_Serial_Port.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::AirScout.Properties.Settings.Default, "Track_Serial_Port", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Options_Track_Serial_Port.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Options_Track_Serial_Port.Location = new System.Drawing.Point(407, 14); + this.tb_Options_Track_Serial_Port.Name = "tb_Options_Track_Serial_Port"; + this.tb_Options_Track_Serial_Port.Size = new System.Drawing.Size(54, 22); + this.tb_Options_Track_Serial_Port.TabIndex = 6; + this.tb_Options_Track_Serial_Port.Tag = ""; + this.tb_Options_Track_Serial_Port.Text = global::AirScout.Properties.Settings.Default.Track_Serial_Port; + // + // rb_Options_Track_Serial_GS232_AZEL + // + this.rb_Options_Track_Serial_GS232_AZEL.AutoSize = true; + this.rb_Options_Track_Serial_GS232_AZEL.Checked = global::AirScout.Properties.Settings.Default.Track_Serial_GS232_AZEL; + this.rb_Options_Track_Serial_GS232_AZEL.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Track_Serial_GS232_AZEL", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.rb_Options_Track_Serial_GS232_AZEL.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.rb_Options_Track_Serial_GS232_AZEL.Location = new System.Drawing.Point(17, 61); + this.rb_Options_Track_Serial_GS232_AZEL.Name = "rb_Options_Track_Serial_GS232_AZEL"; + this.rb_Options_Track_Serial_GS232_AZEL.Size = new System.Drawing.Size(97, 17); + this.rb_Options_Track_Serial_GS232_AZEL.TabIndex = 5; + this.rb_Options_Track_Serial_GS232_AZEL.Tag = ""; + this.rb_Options_Track_Serial_GS232_AZEL.Text = "GS-232A Az/El"; + this.rb_Options_Track_Serial_GS232_AZEL.UseVisualStyleBackColor = true; + // + // rb_Options_Track_Serial_GS232_AZ + // + this.rb_Options_Track_Serial_GS232_AZ.AutoSize = true; + this.rb_Options_Track_Serial_GS232_AZ.Checked = global::AirScout.Properties.Settings.Default.Track_Serial_GS232_AZ; + this.rb_Options_Track_Serial_GS232_AZ.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Track_Serial_GS232_AZ", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.rb_Options_Track_Serial_GS232_AZ.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.rb_Options_Track_Serial_GS232_AZ.Location = new System.Drawing.Point(17, 38); + this.rb_Options_Track_Serial_GS232_AZ.Name = "rb_Options_Track_Serial_GS232_AZ"; + this.rb_Options_Track_Serial_GS232_AZ.Size = new System.Drawing.Size(105, 17); + this.rb_Options_Track_Serial_GS232_AZ.TabIndex = 0; + this.rb_Options_Track_Serial_GS232_AZ.Tag = ""; + this.rb_Options_Track_Serial_GS232_AZ.Text = "GS-232A Az only"; + this.rb_Options_Track_Serial_GS232_AZ.UseVisualStyleBackColor = true; + // + // groupBox28 + // + this.groupBox28.Controls.Add(this.cb_Options_Track_Activate); + this.groupBox28.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox28.Location = new System.Drawing.Point(3, 13); + this.groupBox28.Name = "groupBox28"; + this.groupBox28.Size = new System.Drawing.Size(632, 45); + this.groupBox28.TabIndex = 8; + this.groupBox28.TabStop = false; + this.groupBox28.Text = "Activate Antenna Tracking"; + this.groupBox28.Enter += new System.EventHandler(this.tab_Options_Track_Enter); + // + // cb_Options_Track_Activate + // + this.cb_Options_Track_Activate.AutoSize = true; + this.cb_Options_Track_Activate.Checked = global::AirScout.Properties.Settings.Default.Track_Activate; + this.cb_Options_Track_Activate.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Track_Activate", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Options_Track_Activate.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Options_Track_Activate.Location = new System.Drawing.Point(13, 19); + this.cb_Options_Track_Activate.Name = "cb_Options_Track_Activate"; + this.cb_Options_Track_Activate.Size = new System.Drawing.Size(153, 17); + this.cb_Options_Track_Activate.TabIndex = 1; + this.cb_Options_Track_Activate.Tag = ""; + this.cb_Options_Track_Activate.Text = "Activate Antenna Tracking"; + this.cb_Options_Track_Activate.UseVisualStyleBackColor = true; + // + // tab_Options_Info + // + this.tab_Options_Info.BackColor = System.Drawing.SystemColors.Control; + this.tab_Options_Info.Controls.Add(this.label30); + this.tab_Options_Info.Controls.Add(this.label45); + this.tab_Options_Info.Controls.Add(this.lbl_Options_Elevation_SRTM1); + this.tab_Options_Info.Controls.Add(this.label17); + this.tab_Options_Info.Controls.Add(this.lbl_Options_Elevation_SRTM3); + this.tab_Options_Info.Controls.Add(this.lbl_Options_Elevation_GLOBE); + this.tab_Options_Info.Controls.Add(this.lbl_Options_Spherical); + this.tab_Options_Info.Controls.Add(this.label51); + this.tab_Options_Info.Controls.Add(this.label25); + this.tab_Options_Info.Controls.Add(this.lbl_Options_Map); + this.tab_Options_Info.Controls.Add(this.label27); + this.tab_Options_Info.Controls.Add(this.label26); + this.tab_Options_Info.Controls.Add(this.label24); + this.tab_Options_Info.Controls.Add(this.label23); + this.tab_Options_Info.Controls.Add(this.lbl_Options_Version); + this.tab_Options_Info.Controls.Add(this.label20); + this.tab_Options_Info.Controls.Add(this.label19); + this.tab_Options_Info.Controls.Add(this.btn_Options_License); + this.tab_Options_Info.Controls.Add(this.pb_Donate); + this.tab_Options_Info.Location = new System.Drawing.Point(4, 22); + this.tab_Options_Info.Name = "tab_Options_Info"; + this.tab_Options_Info.Size = new System.Drawing.Size(671, 460); + this.tab_Options_Info.TabIndex = 5; + this.tab_Options_Info.Text = "Info"; + this.tab_Options_Info.Enter += new System.EventHandler(this.tab_Options_Info_Enter); + // + // label30 + // + this.label30.AutoSize = true; + this.label30.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label30.Location = new System.Drawing.Point(187, 284); + this.label30.Name = "label30"; + this.label30.Size = new System.Drawing.Size(230, 13); + this.label30.TabIndex = 43; + this.label30.Text = "ASTER GDEM is a product of METI and NASA"; + // + // label45 + // + this.label45.AutoSize = true; + this.label45.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label45.Location = new System.Drawing.Point(139, 268); + this.label45.Name = "label45"; + this.label45.Size = new System.Drawing.Size(356, 13); + this.label45.TabIndex = 42; + this.label45.Text = "USGS-authored or produced data and information are in the public domain"; + // + // lbl_Options_Elevation_SRTM1 + // + this.lbl_Options_Elevation_SRTM1.AutoSize = true; + this.lbl_Options_Elevation_SRTM1.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lbl_Options_Elevation_SRTM1.Location = new System.Drawing.Point(124, 250); + this.lbl_Options_Elevation_SRTM1.Name = "lbl_Options_Elevation_SRTM1"; + this.lbl_Options_Elevation_SRTM1.Size = new System.Drawing.Size(446, 15); + this.lbl_Options_Elevation_SRTM1.TabIndex = 41; + this.lbl_Options_Elevation_SRTM1.TabStop = true; + this.lbl_Options_Elevation_SRTM1.Text = "1arsec (30m x 30m) Elevation Data from SRTM - Project and ASTER"; + // + // label17 + // + this.label17.AutoSize = true; + this.label17.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label17.Location = new System.Drawing.Point(155, 235); + this.label17.Name = "label17"; + this.label17.Size = new System.Drawing.Size(356, 13); + this.label17.TabIndex = 40; + this.label17.Text = "USGS-authored or produced data and information are in the public domain"; + // + // lbl_Options_Elevation_SRTM3 + // + this.lbl_Options_Elevation_SRTM3.AutoSize = true; + this.lbl_Options_Elevation_SRTM3.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lbl_Options_Elevation_SRTM3.Location = new System.Drawing.Point(140, 215); + this.lbl_Options_Elevation_SRTM3.Name = "lbl_Options_Elevation_SRTM3"; + this.lbl_Options_Elevation_SRTM3.Size = new System.Drawing.Size(393, 16); + this.lbl_Options_Elevation_SRTM3.TabIndex = 39; + this.lbl_Options_Elevation_SRTM3.TabStop = true; + this.lbl_Options_Elevation_SRTM3.Text = "3arsec (90m x 90m) Elevation Data from SRTM - Project"; + // + // lbl_Options_Elevation_GLOBE + // + this.lbl_Options_Elevation_GLOBE.AutoSize = true; + this.lbl_Options_Elevation_GLOBE.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lbl_Options_Elevation_GLOBE.Location = new System.Drawing.Point(165, 192); + this.lbl_Options_Elevation_GLOBE.Name = "lbl_Options_Elevation_GLOBE"; + this.lbl_Options_Elevation_GLOBE.Size = new System.Drawing.Size(340, 16); + this.lbl_Options_Elevation_GLOBE.TabIndex = 38; + this.lbl_Options_Elevation_GLOBE.TabStop = true; + this.lbl_Options_Elevation_GLOBE.Text = "1km based Elevation Data from GLOBE - Project"; + // + // lbl_Options_Spherical + // + this.lbl_Options_Spherical.AutoSize = true; + this.lbl_Options_Spherical.Location = new System.Drawing.Point(241, 146); + this.lbl_Options_Spherical.Name = "lbl_Options_Spherical"; + this.lbl_Options_Spherical.Size = new System.Drawing.Size(251, 13); + this.lbl_Options_Spherical.TabIndex = 35; + this.lbl_Options_Spherical.TabStop = true; + this.lbl_Options_Spherical.Text = "http://www.movable-type.co.uk/scripts/latlong.html"; + // + // label51 + // + this.label51.AutoSize = true; + this.label51.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label51.Location = new System.Drawing.Point(165, 146); + this.label51.Name = "label51"; + this.label51.Size = new System.Drawing.Size(54, 13); + this.label51.TabIndex = 34; + this.label51.Text = "Spherical:"; + // + // label25 + // + this.label25.AutoSize = true; + this.label25.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label25.Location = new System.Drawing.Point(121, 309); + this.label25.Name = "label25"; + this.label25.Size = new System.Drawing.Size(412, 13); + this.label25.TabIndex = 33; + this.label25.Text = "special tnx to DF9IC and DL8AAU for extensive discussions and testing"; + // + // lbl_Options_Map + // + this.lbl_Options_Map.AutoSize = true; + this.lbl_Options_Map.Location = new System.Drawing.Point(238, 165); + this.lbl_Options_Map.Name = "lbl_Options_Map"; + this.lbl_Options_Map.Size = new System.Drawing.Size(228, 13); + this.lbl_Options_Map.TabIndex = 32; + this.lbl_Options_Map.TabStop = true; + this.lbl_Options_Map.Text = "GMap.NET Copyright (c) 2008 - 2011 Universe"; + // + // label27 + // + this.label27.AutoSize = true; + this.label27.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label27.Location = new System.Drawing.Point(165, 165); + this.label27.Name = "label27"; + this.label27.Size = new System.Drawing.Size(66, 13); + this.label27.TabIndex = 31; + this.label27.Text = "Maps library:"; + // + // label26 + // + this.label26.AutoSize = true; + this.label26.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label26.Location = new System.Drawing.Point(168, 129); + this.label26.Name = "label26"; + this.label26.Size = new System.Drawing.Size(31, 13); + this.label26.TabIndex = 30; + this.label26.Text = "Idea:"; + // + // label24 + // + this.label24.AutoSize = true; + this.label24.Location = new System.Drawing.Point(243, 129); + this.label24.Name = "label24"; + this.label24.Size = new System.Drawing.Size(46, 13); + this.label24.TabIndex = 29; + this.label24.Text = "DL2ALF"; + // + // label23 + // + this.label23.AutoSize = true; + this.label23.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Italic | System.Drawing.FontStyle.Underline))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label23.Location = new System.Drawing.Point(180, 95); + this.label23.Name = "label23"; + this.label23.Size = new System.Drawing.Size(291, 20); + this.label23.TabIndex = 28; + this.label23.Text = "Free for personal ham radio related use."; + // + // lbl_Options_Version + // + this.lbl_Options_Version.AutoSize = true; + this.lbl_Options_Version.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lbl_Options_Version.Location = new System.Drawing.Point(270, 70); + this.lbl_Options_Version.Name = "lbl_Options_Version"; + this.lbl_Options_Version.Size = new System.Drawing.Size(111, 20); + this.lbl_Options_Version.TabIndex = 27; + this.lbl_Options_Version.Text = "Version: x.x.x.x"; + // + // label20 + // + this.label20.AutoSize = true; + this.label20.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label20.Location = new System.Drawing.Point(180, 45); + this.label20.Name = "label20"; + this.label20.Size = new System.Drawing.Size(312, 20); + this.label20.TabIndex = 26; + this.label20.Text = "Aircraft scatter prediction (c) 2014 DL2ALF"; + // + // label19 + // + this.label19.AutoSize = true; + this.label19.Font = new System.Drawing.Font("Microsoft Sans Serif", 20.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label19.Location = new System.Drawing.Point(269, 7); + this.label19.Name = "label19"; + this.label19.Size = new System.Drawing.Size(125, 31); + this.label19.TabIndex = 25; + this.label19.Text = "AirScout"; + // + // btn_Options_License + // + this.btn_Options_License.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Options_License.Location = new System.Drawing.Point(3, 334); + this.btn_Options_License.Name = "btn_Options_License"; + this.btn_Options_License.Size = new System.Drawing.Size(645, 26); + this.btn_Options_License.TabIndex = 14; + this.btn_Options_License.Text = "Click here to see the complete license information."; + this.btn_Options_License.UseVisualStyleBackColor = true; + this.btn_Options_License.Click += new System.EventHandler(this.btn_Options_License_Click); + // + // bw_SRTM3_MapUpdater + // + this.bw_SRTM3_MapUpdater.WorkerReportsProgress = true; + this.bw_SRTM3_MapUpdater.WorkerSupportsCancellation = true; + this.bw_SRTM3_MapUpdater.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bw_SRTM3_MapUpdater_DoWork); + this.bw_SRTM3_MapUpdater.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.bw_SRTM3_MapUpdater_ProgressChanged); + this.bw_SRTM3_MapUpdater.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.bw_SRTM3_MapUpdater_RunWorkerCompleted); + // + // ss_Options + // + this.ss_Options.ImageScalingSize = new System.Drawing.Size(20, 20); + this.ss_Options.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.tsl_Options_Status}); + this.ss_Options.Location = new System.Drawing.Point(0, 515); + this.ss_Options.Name = "ss_Options"; + this.ss_Options.Padding = new System.Windows.Forms.Padding(1, 0, 10, 0); + this.ss_Options.Size = new System.Drawing.Size(784, 22); + this.ss_Options.TabIndex = 27; + this.ss_Options.Text = "statusStrip1"; + // + // tsl_Options_Status + // + this.tsl_Options_Status.Name = "tsl_Options_Status"; + this.tsl_Options_Status.Size = new System.Drawing.Size(39, 17); + this.tsl_Options_Status.Text = "Status"; + // + // bw_SRTM1_MapUpdater + // + this.bw_SRTM1_MapUpdater.WorkerReportsProgress = true; + this.bw_SRTM1_MapUpdater.WorkerSupportsCancellation = true; + this.bw_SRTM1_MapUpdater.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bw_SRTM1_MapUpdater_DoWork); + this.bw_SRTM1_MapUpdater.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.bw_SRTM1_MapUpdater_ProgressChanged); + this.bw_SRTM1_MapUpdater.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.bw_SRTM1_MapUpdater_RunWorkerCompleted); + // + // bw_GLOBE_MapUpdater + // + this.bw_GLOBE_MapUpdater.WorkerReportsProgress = true; + this.bw_GLOBE_MapUpdater.WorkerSupportsCancellation = true; + this.bw_GLOBE_MapUpdater.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bw_GLOBE_MapUpdater_DoWork); + this.bw_GLOBE_MapUpdater.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.bw_GLOBE_MapUpdater_ProgressChanged); + this.bw_GLOBE_MapUpdater.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.bw_GLOBE_MapUpdater_RunWorkerCompleted); + // + // bw_SFTP + // + this.bw_SFTP.WorkerReportsProgress = true; + this.bw_SFTP.WorkerSupportsCancellation = true; + this.bw_SFTP.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bw_SFTP_DoWork); + this.bw_SFTP.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.bw_SFTP_ProgressChanged); + this.bw_SFTP.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.bw_SFTP_RunWorkerCompleted); + // + // elevationDatabaseUpdater1 + // + this.elevationDatabaseUpdater1.WorkerReportsProgress = true; + this.elevationDatabaseUpdater1.WorkerSupportsCancellation = true; + // + // cb_Options_Planes_KeepHistory + // + this.cb_Options_Planes_KeepHistory.AutoSize = true; + this.cb_Options_Planes_KeepHistory.Checked = global::AirScout.Properties.Settings.Default.Planes_KeepHistory; + this.cb_Options_Planes_KeepHistory.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Planes_KeepHistory", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Options_Planes_KeepHistory.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Options_Planes_KeepHistory.Location = new System.Drawing.Point(17, 98); + this.cb_Options_Planes_KeepHistory.Name = "cb_Options_Planes_KeepHistory"; + this.cb_Options_Planes_KeepHistory.Size = new System.Drawing.Size(156, 17); + this.cb_Options_Planes_KeepHistory.TabIndex = 4; + this.cb_Options_Planes_KeepHistory.Text = "Keep Plane Position History"; + this.toolTip1.SetToolTip(this.cb_Options_Planes_KeepHistory, "Check this option to keep plane positions in database for history analysis.\r\nCAUT" + + "ION! This will need lot of CPU performance and space on disk!"); + this.cb_Options_Planes_KeepHistory.UseVisualStyleBackColor = true; + // + // OptionsDlg + // + this.AcceptButton = this.btn_Options_OK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch; + this.CancelButton = this.btn_Options_Cancel; + this.ClientSize = new System.Drawing.Size(784, 537); + this.Controls.Add(this.ss_Options); + this.Controls.Add(this.tc_Options); + this.Controls.Add(this.btn_Options_Cancel); + this.Controls.Add(this.btn_Options_OK); + this.Name = "OptionsDlg"; + this.Text = "Air Scout Options"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.OptionsDlg_FormClosing); + this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.OptionsDlg_FormClosed); + this.Load += new System.EventHandler(this.OptionsDlg_Load); + ((System.ComponentModel.ISupportInitialize)(this.pb_Donate)).EndInit(); + this.tab_Options_Planes.ResumeLayout(false); + this.groupBox40.ResumeLayout(false); + this.groupBox40.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.ud_Options_Planes_Position_DatabaseLifetime)).EndInit(); + this.groupBox38.ResumeLayout(false); + this.groupBox38.PerformLayout(); + this.groupBox37.ResumeLayout(false); + this.groupBox37.PerformLayout(); + this.groupBox26.ResumeLayout(false); + this.groupBox26.PerformLayout(); + this.groupBox6.ResumeLayout(false); + this.groupBox6.PerformLayout(); + this.tab_Options_Path.ResumeLayout(false); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.dgv_BandSettings)).EndInit(); + this.tab_Options_SRTM1.ResumeLayout(false); + this.groupBox43.ResumeLayout(false); + this.groupBox43.PerformLayout(); + this.groupBox13.ResumeLayout(false); + this.groupBox12.ResumeLayout(false); + this.groupBox12.PerformLayout(); + this.tab_Options_SRTM3.ResumeLayout(false); + this.groupBox42.ResumeLayout(false); + this.groupBox42.PerformLayout(); + this.groupBox9.ResumeLayout(false); + this.groupBox8.ResumeLayout(false); + this.groupBox8.PerformLayout(); + this.tab_Options_GLOBE.ResumeLayout(false); + this.groupBox41.ResumeLayout(false); + this.groupBox41.PerformLayout(); + this.groupBox11.ResumeLayout(false); + this.groupBox10.ResumeLayout(false); + this.groupBox10.PerformLayout(); + this.tab_Options_Map.ResumeLayout(false); + this.groupBox39.ResumeLayout(false); + this.groupBox39.PerformLayout(); + this.groupBox23.ResumeLayout(false); + this.groupBox23.PerformLayout(); + this.groupBox30.ResumeLayout(false); + this.groupBox30.PerformLayout(); + this.groupBox7.ResumeLayout(false); + this.groupBox7.PerformLayout(); + this.groupBox29.ResumeLayout(false); + this.groupBox29.PerformLayout(); + this.groupBox22.ResumeLayout(false); + this.groupBox22.PerformLayout(); + this.groupBox2.ResumeLayout(false); + this.tab_Options_Stations.ResumeLayout(false); + this.groupBox18.ResumeLayout(false); + this.groupBox18.PerformLayout(); + this.groupBox46.ResumeLayout(false); + this.groupBox46.PerformLayout(); + this.groupBox45.ResumeLayout(false); + this.groupBox45.PerformLayout(); + this.groupBox44.ResumeLayout(false); + this.groupBox44.PerformLayout(); + this.groupBox14.ResumeLayout(false); + this.groupBox14.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.ud_Options_Locator_MaxLength)).EndInit(); + this.groupBox16.ResumeLayout(false); + this.groupBox5.ResumeLayout(false); + this.groupBox5.PerformLayout(); + this.groupBox4.ResumeLayout(false); + this.groupBox4.PerformLayout(); + this.tab_Options_General.ResumeLayout(false); + this.groupBox25.ResumeLayout(false); + this.groupBox25.PerformLayout(); + this.groupBox17.ResumeLayout(false); + this.groupBox17.PerformLayout(); + this.tc_Options.ResumeLayout(false); + this.tab_Options_Database.ResumeLayout(false); + this.groupBox47.ResumeLayout(false); + this.groupBox27.ResumeLayout(false); + this.groupBox27.PerformLayout(); + this.groupBox15.ResumeLayout(false); + this.gb_Options_Database_Settings.ResumeLayout(false); + this.gb_Options_Database_Settings.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.ud_Options_Database_Update_Period)).EndInit(); + this.gb_Options_Database_Info.ResumeLayout(false); + this.gb_Options_Database_Info.PerformLayout(); + this.tab_Options_Alarm.ResumeLayout(false); + this.groupBox21.ResumeLayout(false); + this.groupBox21.PerformLayout(); + this.groupBox19.ResumeLayout(false); + this.groupBox19.PerformLayout(); + this.groupBox20.ResumeLayout(false); + this.groupBox20.PerformLayout(); + this.tab_Options_Network.ResumeLayout(false); + this.groupBox32.ResumeLayout(false); + this.groupBox32.PerformLayout(); + this.groupBox31.ResumeLayout(false); + this.groupBox31.PerformLayout(); + this.groupBox24.ResumeLayout(false); + this.groupBox24.PerformLayout(); + this.tab_Options_SpecLab.ResumeLayout(false); + this.groupBox3.ResumeLayout(false); + this.groupBox3.PerformLayout(); + this.tc_Track.ResumeLayout(false); + this.groupBox36.ResumeLayout(false); + this.groupBox36.PerformLayout(); + this.groupBox35.ResumeLayout(false); + this.groupBox35.PerformLayout(); + this.groupBox34.ResumeLayout(false); + this.groupBox34.PerformLayout(); + this.groupBox33.ResumeLayout(false); + this.groupBox33.PerformLayout(); + this.groupBox28.ResumeLayout(false); + this.groupBox28.PerformLayout(); + this.tab_Options_Info.ResumeLayout(false); + this.tab_Options_Info.PerformLayout(); + this.ss_Options.ResumeLayout(false); + this.ss_Options.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button btn_Options_OK; + private System.Windows.Forms.Button btn_Options_Cancel; + private System.Windows.Forms.ToolTip toolTip1; + private System.Windows.Forms.TabPage tab_Options_Planes; + private System.Windows.Forms.GroupBox groupBox6; + private System.Windows.Forms.TabPage tab_Options_Path; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.TabPage tab_Options_SRTM1; + private System.Windows.Forms.TabPage tab_Options_SRTM3; + private System.Windows.Forms.TabPage tab_Options_GLOBE; + private System.Windows.Forms.TabPage tab_Options_Map; + private System.Windows.Forms.GroupBox groupBox2; + private System.Windows.Forms.TabPage tab_Options_Stations; + private System.Windows.Forms.GroupBox groupBox5; + private System.Windows.Forms.Button btn_Options_DXHorizon; + private System.Windows.Forms.Button btn_DXCall_QRZ; + private System.Windows.Forms.Label label14; + private System.Windows.Forms.Label label15; + private System.Windows.Forms.Label label42; + public System.Windows.Forms.TextBox tb_Options_DXElevation; + private System.Windows.Forms.Label label43; + private System.Windows.Forms.Label label44; + private System.Windows.Forms.GroupBox groupBox4; + private System.Windows.Forms.Button btn_Options_MyHorizon; + private System.Windows.Forms.Label label41; + private System.Windows.Forms.Button btn_MyCall_QRZ; + private System.Windows.Forms.Label label40; + private System.Windows.Forms.Label label39; + private System.Windows.Forms.Label label10; + public System.Windows.Forms.TextBox tb_Options_MyElevation; + private System.Windows.Forms.Label label12; + private System.Windows.Forms.Label label11; + private System.Windows.Forms.TabPage tab_Options_General; + private System.Windows.Forms.TabControl tc_Options; + private System.Windows.Forms.GroupBox groupBox9; + private System.Windows.Forms.GroupBox groupBox8; + private System.Windows.Forms.CheckBox cb_Options_Elevation_SRTM3; + private System.Windows.Forms.GroupBox groupBox10; + private System.Windows.Forms.CheckBox cb_Options_Elevation_GLOBE; + private System.Windows.Forms.GroupBox groupBox13; + private System.Windows.Forms.GroupBox groupBox12; + private System.Windows.Forms.CheckBox cb_Options_Elevation_SRTM1; + private System.Windows.Forms.GroupBox groupBox16; + private System.Windows.Forms.Label label52; + private System.Windows.Forms.GroupBox groupBox17; + private System.Windows.Forms.Button btn_Options_Watchlist_Manage; + private System.Windows.Forms.Label label31; + private System.Windows.Forms.TabPage tab_Options_Alarm; + private System.Windows.Forms.GroupBox groupBox19; + private System.Windows.Forms.GroupBox groupBox20; + private System.Windows.Forms.Label label56; + private System.Windows.Forms.CheckBox cb_Options_Alarm_BringWindowToFront; + private System.Windows.Forms.CheckBox cb_Options_Alarm_PlaySound; + private System.Windows.Forms.Label label36; + private System.Windows.Forms.GroupBox groupBox21; + private System.Windows.Forms.CheckBox cb_Options_Alarm_Activate; + private System.Windows.Forms.Label label57; + private System.Windows.Forms.GroupBox groupBox22; + private System.Windows.Forms.CheckBox cb_Options_Path_BestCaseElevation; + private System.Windows.Forms.Label label21; + private System.Windows.Forms.Label label28; + private System.Windows.Forms.Label label18; + private System.Windows.Forms.DataGridView dgv_BandSettings; + private System.Windows.Forms.TabPage tab_Options_Network; + private System.Windows.Forms.GroupBox groupBox24; + private System.Windows.Forms.Label label3; + public System.Windows.Forms.TextBox tb_Options_Server_Name; + private System.Windows.Forms.CheckBox cb_Options_Server_Activate; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.GroupBox groupBox25; + private GMap.NET.WindowsForms.GMapControl gm_Options_Coverage; + private GMap.NET.WindowsForms.GMapControl gm_Options_SRTM3; + private GMap.NET.WindowsForms.GMapControl gm_Options_SRTM1; + private System.Windows.Forms.ComboBox cb_Options_Map_Provider; + private System.Windows.Forms.Label label61; + private System.Windows.Forms.Button btn_Options_SelectFont; + private System.Windows.Forms.Label label62; + private System.Windows.Forms.TextBox tb_Options_Map_ToolTipFont; + private System.Windows.Forms.GroupBox groupBox26; + private System.Windows.Forms.TabPage tab_Options_SpecLab; + private System.Windows.Forms.GroupBox groupBox3; + public System.Windows.Forms.TextBox tb_SpecLab_FileName; + private System.Windows.Forms.Label label63; + private System.Windows.Forms.Label label64; + private System.Windows.Forms.Label label65; + public System.Windows.Forms.TextBox tb_SpecLab_URL; + private System.Windows.Forms.CheckBox cb_SpecLab_Enabled; + private System.Windows.Forms.Label label68; + private System.Windows.Forms.Label label69; + private System.Windows.Forms.Label label67; + private System.Windows.Forms.Label label66; + private System.Windows.Forms.Label label70; + private System.Windows.Forms.Label label71; + private System.Windows.Forms.RadioButton rb_Options_InfoWin_Imperial; + private System.Windows.Forms.RadioButton rb_Options_InfoWin_Metric; + private System.Windows.Forms.Label label72; + private System.Windows.Forms.GroupBox groupBox7; + private System.Windows.Forms.CheckBox cb_Options_InfoWin_Type; + private System.Windows.Forms.CheckBox cb_Options_InfoWin_Track; + private System.Windows.Forms.CheckBox cb_Options_InfoWin_Alt; + private System.Windows.Forms.CheckBox cb_Options_InfoWin_Position; + private System.Windows.Forms.CheckBox cb_Options_InfoWin_Epsilon; + private System.Windows.Forms.CheckBox cb_Options_InfoWin_Dist; + private System.Windows.Forms.CheckBox cb_Options_InfoWin_Time; + private System.Windows.Forms.CheckBox cb_Options_InfoWin_Squint; + private System.Windows.Forms.CheckBox cb_Options_InfoWin_Angle; + private System.Windows.Forms.CheckBox cb_Options_InfoWin_Speed; + private System.Windows.Forms.Label label76; + private System.Windows.Forms.Label label75; + private System.Windows.Forms.Label label74; + private System.Windows.Forms.GroupBox groupBox29; + private System.Windows.Forms.TabPage tc_Track; + private System.Windows.Forms.GroupBox groupBox28; + private System.Windows.Forms.CheckBox cb_Options_Track_Activate; + private System.Windows.Forms.ComboBox cb_Options_PlaneFeed1; + private System.Windows.Forms.Button btn_Options_PlaneFeed1_Settings; + private System.Windows.Forms.Label label79; + private System.Windows.Forms.Button btn_Options_PlaneFeed3_Settings; + private System.Windows.Forms.ComboBox cb_Options_PlaneFeed3; + private System.Windows.Forms.Label label78; + private System.Windows.Forms.Button btn_Options_PlaneFeed2_Settings; + private System.Windows.Forms.ComboBox cb_Options_PlaneFeed2; + private System.Windows.Forms.Label label77; + private System.Windows.Forms.GroupBox groupBox30; + private System.Windows.Forms.Label label83; + private System.Windows.Forms.Label label82; + private System.Windows.Forms.Label label81; + private System.Windows.Forms.Label label80; + private System.Windows.Forms.Label label84; + private System.Windows.Forms.Label label85; + private System.Windows.Forms.GroupBox groupBox31; + private System.Windows.Forms.Label label86; + private System.Windows.Forms.GroupBox groupBox32; + private System.Windows.Forms.GroupBox groupBox33; + private System.Windows.Forms.Label label88; + private System.Windows.Forms.Label label87; + private System.Windows.Forms.TextBox tb_Options_Track_Serial_Port; + private System.Windows.Forms.RadioButton rb_Options_Track_Serial_GS232_AZEL; + private System.Windows.Forms.RadioButton rb_Options_Track_Serial_GS232_AZ; + private System.Windows.Forms.GroupBox groupBox34; + private System.Windows.Forms.RadioButton rb_Options_Track_UDP_WinTest; + private System.Windows.Forms.GroupBox groupBox35; + private System.Windows.Forms.RadioButton rb_Options_Track_DDE_HRD; + private System.Windows.Forms.RadioButton rb_Options_Track_UDP_AirScout; + private System.Windows.Forms.GroupBox groupBox36; + private System.Windows.Forms.RadioButton rb_Options_Track_File_WSJT; + private System.Windows.Forms.RadioButton rb_Options_Track_File_Native; + private System.Windows.Forms.RadioButton rb_Options_Track_Serial_None; + private System.Windows.Forms.RadioButton rb_Options_Track_DDE_None; + private System.Windows.Forms.RadioButton rb_Options_Track_UDP_None; + private System.Windows.Forms.RadioButton rb_Options_Track_File_None; + private System.Windows.Forms.Label label89; + private System.Windows.Forms.Label label90; + private System.Windows.Forms.TabPage tab_Options_Info; + private System.Windows.Forms.Button btn_Options_License; + private System.Windows.Forms.LinkLabel lbl_Options_Spherical; + private System.Windows.Forms.Label label51; + private System.Windows.Forms.Label label25; + private System.Windows.Forms.LinkLabel lbl_Options_Map; + private System.Windows.Forms.Label label27; + private System.Windows.Forms.Label label26; + private System.Windows.Forms.Label label24; + private System.Windows.Forms.Label label23; + private System.Windows.Forms.Label lbl_Options_Version; + private System.Windows.Forms.Label label20; + private System.Windows.Forms.Label label19; + private System.Windows.Forms.PictureBox pb_Donate; + private System.Windows.Forms.GroupBox groupBox37; + private System.Windows.Forms.Label label22; + private System.Windows.Forms.TextBox tb_Options_Planes_MaxLat; + private System.Windows.Forms.Label label37; + private System.Windows.Forms.TextBox tb_Options_Planes_MinLat; + private System.Windows.Forms.Label label38; + private System.Windows.Forms.TextBox tb_Options_Planes_MaxLon; + private System.Windows.Forms.Label label33; + private System.Windows.Forms.TextBox tb_Options_Planes_MinLon; + private System.Windows.Forms.Label label34; + private System.Windows.Forms.GroupBox groupBox38; + private System.Windows.Forms.Label label9; + private System.Windows.Forms.Label label8; + private System.Windows.Forms.Label label7; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label91; + private System.Windows.Forms.ComboBox cb_Options_Planes_Filter_Min_Cat; + private System.Windows.Forms.Label label94; + private System.Windows.Forms.Label label95; + private System.Windows.Forms.Label label92; + private System.Windows.Forms.Label label93; + private System.Windows.Forms.Label label96; + private System.Windows.Forms.GroupBox groupBox11; + private GMap.NET.WindowsForms.GMapControl gm_Options_GLOBE; + private System.Windows.Forms.GroupBox groupBox23; + private System.Windows.Forms.Button btn_Options_PlaneFeed3_Export; + private System.Windows.Forms.Button btn_Options_PlaneFeed2_Export; + private System.Windows.Forms.Button btn_Options_PlaneFeed1_Export; + private System.Windows.Forms.Button btn_Options_PlaneFeed3_Import; + private System.Windows.Forms.Button btn_Options_PlaneFeed2_Import; + private System.Windows.Forms.Button btn_Options_PlaneFeed1_Import; + private System.Windows.Forms.GroupBox groupBox39; + private System.Windows.Forms.Label label97; + private System.Windows.Forms.Label label29; + private System.Windows.Forms.LinkLabel lbl_Options_Elevation_GLOBE; + private System.Windows.Forms.LinkLabel lbl_Options_Elevation_SRTM3; + private System.Windows.Forms.Label label30; + private System.Windows.Forms.Label label45; + private System.Windows.Forms.LinkLabel lbl_Options_Elevation_SRTM1; + private System.Windows.Forms.Label label17; + private System.Windows.Forms.TabPage tab_Options_Database; + private System.Windows.Forms.GroupBox gb_Options_Database_Settings; + private System.Windows.Forms.GroupBox gb_Options_Database_Info; + private System.Windows.Forms.Label label47; + private System.Windows.Forms.NumericUpDown ud_Options_Database_Update_Period; + private System.Windows.Forms.RadioButton rb_Options_Database_Update_Periodically; + private System.Windows.Forms.RadioButton rb_Options_Database_Update_OnStartup; + private System.Windows.Forms.RadioButton rb_Options_Database_Update_Never; + private System.Windows.Forms.GroupBox groupBox14; + private System.Windows.Forms.CheckBox cb_Options_Locator_AutoLength; + private System.Windows.Forms.Label label48; + private System.Windows.Forms.NumericUpDown ud_Options_Locator_MaxLength; + private ScoutBase.Core.DoubleTextBox tb_Options_MyLon; + private ScoutBase.Core.DoubleTextBox tb_Options_MyLat; + private ScoutBase.Core.LocatorTextBox tb_Options_MyLoc; + private ScoutBase.Core.CallsignTextBox tb_Options_MyCall; + private ScoutBase.Core.DoubleTextBox tb_Options_DXLon; + private ScoutBase.Core.DoubleTextBox tb_Options_DXLat; + private ScoutBase.Core.LocatorTextBox tb_Options_DXLoc; + private ScoutBase.Core.CallsignTextBox tb_Options_DXCall; + private System.Windows.Forms.CheckBox cb_Options_SmallLettersForSubSquares; + private System.Windows.Forms.GroupBox groupBox15; + private System.Windows.Forms.Button btn_Open_LogDirectory; + private System.Windows.Forms.Button btn_Open_TmpDirectory; + private ScoutBase.Core.Int32TextBox tb_Options_Watchlist_MaxCount; + private ScoutBase.Core.Int32TextBox tb_Options_Planes_IconSize_S; + private ScoutBase.Core.Int32TextBox tb_Options_Planes_IconSize_H; + private ScoutBase.Core.Int32TextBox tb_Options_Planes_IconSize_M; + private ScoutBase.Core.Int32TextBox tb_Options_Planes_IconSize_L; + private ScoutBase.Core.Int32TextBox tb_Options_Map_Update_Interval; + private System.Windows.Forms.CheckBox cb_Options_Airports_Activate; + private ScoutBase.Core.Int32TextBox tb_Options_Planes_Filter_Max_Circumcircle; + private ScoutBase.Core.Int32TextBox tb_Options_Planes_Filter_MinAlt; + private ScoutBase.Core.Int32TextBox tb_Options_Planes_MaxAlt; + private ScoutBase.Core.Int32TextBox tb_Options_Planes_MinAlt; + private ScoutBase.Core.Int32TextBox tb_Options_Planes_Positions_TTL; + private ScoutBase.Core.DoubleTextBox tb_Options_Alarm_Distance; + private ScoutBase.Core.Int32TextBox tb_Options_Server_Port; + private ScoutBase.Core.Int32TextBox tb_Options_Webserver_Port; + private ScoutBase.Core.Int32TextBox tb_Options_SpecLab_F1; + private ScoutBase.Core.Int32TextBox tb_Options_SpecLab_F2; + private ScoutBase.Core.DoubleTextBox tb_Options_SpecLab_UpdateInterval; + private ScoutBase.Core.Int32TextBox tb_Options_Track_Serial_Baudrate; + private ScoutBase.Core.Int32TextBox tb_Options_Track_UDP_AirScout_Port; + private ScoutBase.Core.Int32TextBox tb_Options_Track_UDP_WinTest_Port; + private System.Windows.Forms.Label label73; + private System.Windows.Forms.TextBox tb_Options_ScoutBase_Database_FileSize; + private System.Windows.Forms.Label label50; + private System.Windows.Forms.TextBox tb_Options_ScoutBase_Database_FileName; + private System.Windows.Forms.Label label49; + private System.Windows.Forms.Label label101; + private System.Windows.Forms.TextBox tb_Options_AirScout_Database_FileSize; + private System.Windows.Forms.Label label102; + private System.Windows.Forms.TextBox tb_Options_AirScout_Database_FileName; + private System.Windows.Forms.Label label103; + private System.Windows.Forms.Label label104; + private System.Windows.Forms.Label label105; + private System.Windows.Forms.CheckBox cb_Options_Watchlist_SyncWithKST; + private System.Windows.Forms.CheckBox cb_Options_Watchlist_Activate; + private ScoutBase.Core.DoubleTextBox tb_Coverage_MaxLat; + private ScoutBase.Core.DoubleTextBox tb_Coverage_MinLat; + private ScoutBase.Core.DoubleTextBox tb_Coverage_MaxLon; + private ScoutBase.Core.DoubleTextBox tb_Coverage_MinLon; + private System.Windows.Forms.Label label35; + private System.Windows.Forms.Label label54; + private System.Windows.Forms.Label label59; + private System.Windows.Forms.Label label60; + private System.Windows.Forms.GroupBox groupBox27; + private System.Windows.Forms.Label label108; + private System.Windows.Forms.Button btn_Options_Import_Callsigns; + private System.Windows.Forms.GroupBox groupBox40; + private System.ComponentModel.BackgroundWorker bw_SRTM3_MapUpdater; + private System.Windows.Forms.StatusStrip ss_Options; + private System.Windows.Forms.ToolStripStatusLabel tsl_Options_Status; + private System.ComponentModel.BackgroundWorker bw_SRTM1_MapUpdater; + private System.ComponentModel.BackgroundWorker bw_GLOBE_MapUpdater; + private System.Windows.Forms.CheckBox cb_Options_Elevation_SRTM3_EnableCache; + private System.Windows.Forms.CheckBox cb_Options_Elevation_GLOBE_EnableCache; + private System.Windows.Forms.CheckBox cb_Options_Elevation_SRTM1_EnableCache; + private System.Windows.Forms.GroupBox groupBox41; + private System.Windows.Forms.Label label98; + private System.Windows.Forms.Button btn_Options_GLOBE_Copyright; + private System.Windows.Forms.Label label99; + private System.Windows.Forms.GroupBox groupBox42; + private System.Windows.Forms.Label label100; + private System.Windows.Forms.Label label110; + private System.Windows.Forms.Button btn_Options_SRTM3_Copyright; + private System.Windows.Forms.GroupBox groupBox43; + private System.Windows.Forms.Label label111; + private System.Windows.Forms.Label label112; + private System.Windows.Forms.Button btn_Options_SRTM1_Copyright; + private System.Windows.Forms.CheckBox cb_Options_Background_Calculations_Enable; + private System.Windows.Forms.Label label118; + private System.Windows.Forms.TextBox tb_Options_Elevation_SRTM1_Database_FileSize; + private System.Windows.Forms.Label label119; + private System.Windows.Forms.TextBox tb_Options_Elevation_SRTM1_Database_FileName; + private System.Windows.Forms.Label label120; + private System.Windows.Forms.Label label115; + private System.Windows.Forms.TextBox tb_Options_Elevation_SRTM3_Database_FileSize; + private System.Windows.Forms.Label label116; + private System.Windows.Forms.TextBox tb_Options_Elevation_SRTM3_Database_FileName; + private System.Windows.Forms.Label label117; + private System.Windows.Forms.Label label16; + private System.Windows.Forms.TextBox tb_Options_Elevation_GLOBE_Database_FileSize; + private System.Windows.Forms.Label label113; + private System.Windows.Forms.TextBox tb_Options_Elevation_GLOBE_Database_FileName; + private System.Windows.Forms.Label label114; + private System.Windows.Forms.GroupBox groupBox44; + private System.Windows.Forms.GroupBox groupBox45; + private System.Windows.Forms.Button btn_Options_BandDown; + private System.Windows.Forms.Button btn_Options_BandUp; + public System.Windows.Forms.TextBox tb_Options_Band; + private System.Windows.Forms.Label label122; + private System.Windows.Forms.Label label123; + private System.Windows.Forms.Label label106; + private System.Windows.Forms.Label label121; + private System.Windows.Forms.GroupBox groupBox46; + private System.Windows.Forms.Label label13; + private System.Windows.Forms.Label label58; + private System.Windows.Forms.Label label107; + private System.Windows.Forms.Label label124; + private System.Windows.Forms.Label label125; + private System.Windows.Forms.Label label126; + private System.Windows.Forms.Button btn_Options_DXUpdate; + private System.Windows.Forms.Button btn_Options_MyUpdate; + private ScoutBase.Core.DoubleTextBox tb_Options_MyPower; + private ScoutBase.Core.DoubleTextBox tb_Options_MyAntennaGain; + private ScoutBase.Core.DoubleTextBox tb_Options_MyAntennaHeight; + private ScoutBase.Core.DoubleTextBox tb_Options_DXPower; + private ScoutBase.Core.DoubleTextBox tb_Options_DXAntennaGain; + private ScoutBase.Core.DoubleTextBox tb_Options_DXAntennaHeight; + private System.Windows.Forms.Label label128; + private System.Windows.Forms.Label label127; + private System.Windows.Forms.Label lbl_Options_DXLastUpdated; + private System.Windows.Forms.Label lbl_Options_MyLastUpdated; + private System.Windows.Forms.Button btn_Options_DXMap; + private System.Windows.Forms.Button btn_Options_MyMap; + private System.ComponentModel.BackgroundWorker bw_SFTP; + private System.Windows.Forms.TextBox tb_Options_Path_StepWidth; + private System.Windows.Forms.Label label129; + private System.Windows.Forms.CheckBox cb_Options_Map_SmallMarkers; + private System.Windows.Forms.GroupBox groupBox47; + private System.Windows.Forms.Button btn_Options_DeleteAllPropagationPaths; + private System.Windows.Forms.Button btn_Options_DeleteAllElevationPaths; + private ScoutBase.Core.DoubleTextBox tb_Options_Path_MaxLength; + private System.Windows.Forms.Label label131; + private System.Windows.Forms.Label label130; + private System.Windows.Forms.GroupBox groupBox18; + private System.Windows.Forms.Label lbl_Options_LocalObstructions; + private System.Windows.Forms.Button btn_Options_LocalObstructions; + private System.Windows.Forms.NumericUpDown ud_Options_Planes_Position_DatabaseLifetime; + private System.Windows.Forms.Label label53; + private System.Windows.Forms.Label label32; + private System.Windows.Forms.Label label55; + private System.Windows.Forms.Label label132; + private System.Windows.Forms.TextBox tb_Options_Propagation_GLOBE_Database_FileSize; + private System.Windows.Forms.Label label133; + private System.Windows.Forms.TextBox tb_Options_Propagation_GLOBE_Database_FileName; + private System.Windows.Forms.Label label109; + private System.Windows.Forms.Label label141; + private System.Windows.Forms.Label lbl_Options_Database_TotalSize; + private System.Windows.Forms.Label label139; + private System.Windows.Forms.Label label136; + private System.Windows.Forms.TextBox tb_Options_Propagation_SRTM1_Database_FileSize; + private System.Windows.Forms.Label label137; + private System.Windows.Forms.TextBox tb_Options_Propagation_SRTM1_Database_FileName; + private System.Windows.Forms.Label label138; + private System.Windows.Forms.Label label46; + private System.Windows.Forms.TextBox tb_Options_Propagation_SRTM3_Database_FileSize; + private System.Windows.Forms.Label label134; + private System.Windows.Forms.TextBox tb_Options_Propagation_SRTM3_Database_FileName; + private System.Windows.Forms.Label label135; + private System.Windows.Forms.Button btn_Options_Elevation_SRTM1_Database_Maintenance; + private System.Windows.Forms.Button btn_Options_Elevation_SRTM3_Database_Maintenance; + private System.Windows.Forms.Button btn_Options_Elevation_GLOBE_Database_Maintenance; + private System.Windows.Forms.Button btn_Options_Propagation_SRTM1_Database_Maintenance; + private System.Windows.Forms.Button btn_Options_Propagation_SRTM3_Database_Maintenance; + private System.Windows.Forms.Button btn_Options_Propagation_GLOBE_Database_Maintenance; + private System.Windows.Forms.Button btn_Options_AirScout_Database_Maintenance; + private System.Windows.Forms.Button btn_Options_ScoutBase_Database_Maintenance; + private System.Windows.Forms.CheckBox cb_Options_Planes_KeepHistory; + private ScoutBase.Elevation.ElevationDatabaseUpdater elevationDatabaseUpdater1; + } +} \ No newline at end of file diff --git a/AirScout/OptionsDlg.cs b/AirScout/OptionsDlg.cs new file mode 100644 index 0000000..a868f98 --- /dev/null +++ b/AirScout/OptionsDlg.cs @@ -0,0 +1,2164 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using System.IO; +using System.Net; +using System.Globalization; +using GMap.NET; +using GMap.NET.MapProviders; +using GMap.NET.WindowsForms; +using GMap.NET.WindowsForms.Markers; +using GMap.NET.WindowsForms.ToolTips; +using System.Configuration; +using System.Diagnostics; +using System.Runtime.InteropServices; +using Microsoft.Win32; +using AirScout.PlaneFeeds.Generic; +using AirScout.PlaneFeeds; +using AirScout.Core; +using AirScout.Aircrafts; +using ScoutBase.Core; +using ScoutBase.Stations; +using ScoutBase.Elevation; +using ScoutBase.Propagation; +using ScoutBase; +using Renci.SshNet; +using Newtonsoft.Json; +using static ScoutBase.Core.ZIP; + +namespace AirScout +{ + public partial class OptionsDlg : Form + { + + GMapOverlay Coveragepolygons = new GMapOverlay("Coveragepolygons"); + GMapOverlay GLOBEpolygons = new GMapOverlay("GLOBEpolygons"); + GMapOverlay SRTM3polygons = new GMapOverlay("SRTM3polygons"); + GMapOverlay SRTM1polygons = new GMapOverlay("SRTM1polygons"); + + MapDlg ParentDlg; + + NumberFormatInfo ENprovider; + + private LocalObstructionDesignator LocalObstructions; + + private LogWriter Log = LogWriter.Instance; + + public OptionsDlg(MapDlg parentDlg) + { + InitializeComponent(); + ParentDlg = parentDlg; + // get an EN format provider + ENprovider = new NumberFormatInfo(); + ENprovider.NumberDecimalSeparator = "."; + ENprovider.NumberGroupSeparator = ","; + + + } + + private void OptionsDlg_Load(object sender, EventArgs e) + { + Log.WriteMessage("Loading."); + // fill data grid with band settings + dgv_BandSettings.DataSource = Properties.Settings.Default.Path_Band_Settings; + dgv_BandSettings.Columns[0].Width = 45; + dgv_BandSettings.Columns[0].HeaderText = "Band\n"; + dgv_BandSettings.Columns[0].ToolTipText = "Band\n50M ... 76G"; + dgv_BandSettings.Columns[0].ReadOnly = true; + dgv_BandSettings.Columns[1].Width = 60; + dgv_BandSettings.Columns[1].HeaderText = "K-Factor\n"; + dgv_BandSettings.Columns[1].ToolTipText = "K-Factor\n1 ... 2 x Earth Radius\nDefault = 1.33"; + dgv_BandSettings.Columns[2].Width = 70; + dgv_BandSettings.Columns[2].HeaderText = "F1-Clearance\n"; + dgv_BandSettings.Columns[2].ToolTipText = "Fresnel Zone Clearance\n0 ... 1 x Fresnel Zone F1\nDefault = 0.6"; + dgv_BandSettings.Columns[3].Width = 105; + dgv_BandSettings.Columns[3].HeaderText = "Ground Clearance [m]"; + dgv_BandSettings.Columns[3].ToolTipText = "Additional Ground Clearance in m\nDefault = 0"; + dgv_BandSettings.Columns[4].Width = 95; + dgv_BandSettings.Columns[4].HeaderText = "Max. Distance [km]"; + dgv_BandSettings.Columns[4].ToolTipText = "Max. Distance in km\nan aircraft is considered to be \"on the path\"\nDefault = 10"; + dgv_BandSettings.Columns[5].Width = 80; + dgv_BandSettings.Columns[5].HeaderText = "Max. Squint [deg]"; + dgv_BandSettings.Columns[5].ToolTipText = "Max. Squint an asymmetric reflection can have"; + dgv_BandSettings.Columns[6].Width = 95; + dgv_BandSettings.Columns[6].HeaderText = "Max. Elevation [deg]"; + dgv_BandSettings.Columns[6].ToolTipText = "Max. Elevation an aircraft can have to be inside the main lobe of antenna"; + + // set initial settings for CoverageMap + gm_Options_Coverage.MapProvider = GMapProviders.Find(Properties.Settings.Default.Map_Provider); + gm_Options_Coverage.IgnoreMarkerOnMouseWheel = true; + gm_Options_Coverage.MinZoom = 0; + gm_Options_Coverage.MaxZoom = 20; + gm_Options_Coverage.Zoom = 6; + gm_Options_Coverage.DragButton = System.Windows.Forms.MouseButtons.Left; + gm_Options_Coverage.CanDragMap = true; + gm_Options_Coverage.ScalePen = new Pen(Color.Black, 3); + gm_Options_Coverage.HelperLinePen = null; + gm_Options_Coverage.SelectionPen = null; + gm_Options_Coverage.MapScaleInfoEnabled = true; + gm_Options_Coverage.Overlays.Add(Coveragepolygons); + + // set initial settings for GLOBEMap + gm_Options_GLOBE.MapProvider = GMapProviders.Find(Properties.Settings.Default.Map_Provider); + gm_Options_GLOBE.IgnoreMarkerOnMouseWheel = true; + gm_Options_GLOBE.MinZoom = 0; + gm_Options_GLOBE.MaxZoom = 20; + gm_Options_GLOBE.Zoom = 1; + gm_Options_GLOBE.DragButton = System.Windows.Forms.MouseButtons.Left; + gm_Options_GLOBE.CanDragMap = true; + gm_Options_GLOBE.ScalePen = new Pen(Color.Black, 3); + gm_Options_GLOBE.HelperLinePen = null; + gm_Options_GLOBE.SelectionPen = null; + gm_Options_GLOBE.MapScaleInfoEnabled = true; + gm_Options_GLOBE.Overlays.Add(GLOBEpolygons); + + // set initial settings for SRTM3Map + gm_Options_SRTM3.MapProvider = GMapProviders.Find(Properties.Settings.Default.Map_Provider); + gm_Options_SRTM3.IgnoreMarkerOnMouseWheel = true; + gm_Options_SRTM3.MinZoom = 0; + gm_Options_SRTM3.MaxZoom = 20; + gm_Options_SRTM3.Zoom = 6; + gm_Options_SRTM3.DragButton = System.Windows.Forms.MouseButtons.Left; + gm_Options_SRTM3.CanDragMap = true; + gm_Options_SRTM3.ScalePen = new Pen(Color.Black, 3); + gm_Options_SRTM3.HelperLinePen = null; + gm_Options_SRTM3.SelectionPen = null; + gm_Options_SRTM3.MapScaleInfoEnabled = true; + gm_Options_SRTM3.Overlays.Add(SRTM3polygons); + + // set initial settings for SRTM1Map + gm_Options_SRTM1.MapProvider = GMapProviders.Find(Properties.Settings.Default.Map_Provider); + gm_Options_SRTM1.IgnoreMarkerOnMouseWheel = true; + gm_Options_SRTM1.MinZoom = 0; + gm_Options_SRTM1.MaxZoom = 20; + gm_Options_SRTM1.Zoom = 6; + gm_Options_SRTM1.DragButton = System.Windows.Forms.MouseButtons.Left; + gm_Options_SRTM1.CanDragMap = true; + gm_Options_SRTM1.ScalePen = new Pen(Color.Black, 3); + gm_Options_SRTM1.HelperLinePen = null; + gm_Options_SRTM1.SelectionPen = null; + gm_Options_SRTM1.MapScaleInfoEnabled = true; + gm_Options_SRTM1.Overlays.Add(SRTM1polygons); + Log.WriteMessage("Finished."); + } + + #region Helpers + private bool IsInside(string loc, double lat, double lon) + { + string newloc = MaidenheadLocator.LocFromLatLon(lat, lon, false, loc.Length); + if (loc.ToUpper().Trim() != newloc.ToUpper().Trim()) + return false; + return true; + } + + private bool IsPrecise(string loc, double lat, double lon) + { + loc = loc.ToUpper(); + double mlat = MaidenheadLocator.LatFromLoc(loc); + double mlon = MaidenheadLocator.LonFromLoc(loc); + string mloc = MaidenheadLocator.LocFromLatLon(lat, lon, false, loc.Length); + if ((loc == mloc) && ((Math.Abs(mlat - lat) > 0.00001) || (mlon - lon) > 0.00001)) + { + return true; + } + return false; + } + + private void Say(string text) + { + if (tsl_Options_Status.Text != text) + { + tsl_Options_Status.Text = text; + } + } + + private ELEVATIONMODEL GetElevationModel() + { + if (Properties.Settings.Default.Elevation_SRTM1_Enabled) + return ELEVATIONMODEL.SRTM1; + if (Properties.Settings.Default.Elevation_SRTM3_Enabled) + return ELEVATIONMODEL.SRTM3; + if (Properties.Settings.Default.Elevation_GLOBE_Enabled) + return ELEVATIONMODEL.GLOBE; + return ELEVATIONMODEL.NONE; + } + + #endregion + + #region tab_Options_General + + private void tab_Options_General_Enter(object sender, EventArgs e) + { + tab_Options_General_Update(this,null); + } + + private void tab_Options_General_Validating(object sender, CancelEventArgs e) + { + /* + if (tb_Coverage_MaxLat.Value <= tb_Coverage_MinLat.Value) + { + MessageBox.Show("MaxLat must be greater than MinLat.", "Parameter Error"); + e.Cancel = true; + } + if (tb_Coverage_MaxLon.Value <= tb_Coverage_MinLon.Value) + { + MessageBox.Show("MaxLon must be greater than MinLon.", "Parameter Error"); + e.Cancel = true; + } + */ + } + + private void tab_Options_General_Update(object sender, EventArgs e) + { + Coveragepolygons.Clear(); + // check values + if (double.IsNaN(tb_Coverage_MinLat.Value) || double.IsNaN(tb_Coverage_MaxLat.Value) || double.IsNaN(tb_Coverage_MinLon.Value) | double.IsNaN(tb_Coverage_MaxLon.Value)) + return; + // add tile to map polygons + List l = new List(); + l.Add(new PointLatLng(tb_Coverage_MinLat.Value, tb_Coverage_MinLon.Value)); + l.Add(new PointLatLng(tb_Coverage_MinLat.Value, tb_Coverage_MaxLon.Value)); + l.Add(new PointLatLng(tb_Coverage_MaxLat.Value, tb_Coverage_MaxLon.Value)); + l.Add(new PointLatLng(tb_Coverage_MaxLat.Value, tb_Coverage_MinLon.Value)); + GMapPolygon p = new GMapPolygon(l, "Coverage"); + p.Stroke = new Pen(Color.FromArgb(255, Color.Magenta), 3); + p.Fill = new SolidBrush(Color.FromArgb(0, Color.Magenta)); + Coveragepolygons.Polygons.Add(p); + // zoom the map + gm_Options_Coverage.SetZoomToFitRect(RectLatLng.FromLTRB(tb_Coverage_MinLon.Value - 1, tb_Coverage_MaxLat.Value + 1, tb_Coverage_MaxLon.Value + 1, tb_Coverage_MinLat.Value - 1)); + } + + private void btn_Options_Watchlist_Manage_Click(object sender, EventArgs e) + { + WatchlistDlg Dlg = new WatchlistDlg(); + if (Dlg.ShowDialog() == DialogResult.OK) + { + // sync watchlist + foreach (WatchlistItem item in Properties.Settings.Default.Watchlist) + item.Remove = true; + foreach (ListViewItem lvi in Dlg.lv_Watchlist_Selected.Items) + { + // search item in watchlist + int index = Properties.Settings.Default.Watchlist.IndexOf(lvi.Text); + // reset remove flag if found, create and add new entry if not + if (index >= 0) + Properties.Settings.Default.Watchlist[index].Remove = false; + else + { + // try to find last recent locator from database and add to watchlist + LocationDesignator dxcall = StationData.Database.LocationFindLastRecent(lvi.Text); + if (dxcall != null) + { + double qrb = LatLon.Distance(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, dxcall.Lat, dxcall.Lon); + Properties.Settings.Default.Watchlist.Add(new WatchlistItem(dxcall.Call, dxcall.Loc, qrb > Properties.Settings.Default.Path_MaxLength)); + } + } + } + // remove the rest of items + Properties.Settings.Default.Watchlist.RemoveAll(item => item.Remove); + } + } + + #endregion + + #region tab_Options_Database + + private string GetDatabaseDir(string dir) + { + if (dir.IndexOf(Path.DirectorySeparatorChar) == 0) + return Path.GetFileName(dir); + try + { + string databasedir = "..." + Path.DirectorySeparatorChar + dir.Split(Path.DirectorySeparatorChar).Reverse().Take(2).Aggregate((s1, s2) => s2 + Path.DirectorySeparatorChar + s1); + return databasedir; + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + return Path.GetFileName(dir); + } + + private void tab_Options_Database_Enter(object sender, EventArgs e) + { + tb_Options_ScoutBase_Database_FileName.Text = GetDatabaseDir(StationData.Database.GetDBLocation()); + tb_Options_ScoutBase_Database_FileSize.Text = StationData.Database.GetDBSize().ToString("F0"); + tb_Options_AirScout_Database_FileName.Text = GetDatabaseDir(AircraftData.Database.GetDBLocation()); + tb_Options_AirScout_Database_FileSize.Text = AircraftData.Database.GetDBSize().ToString("F0"); + tb_Options_Propagation_GLOBE_Database_FileName.Text = GetDatabaseDir(PropagationData.Database.GetDBLocation(ELEVATIONMODEL.GLOBE)); + tb_Options_Propagation_GLOBE_Database_FileSize.Text = PropagationData.Database.GetDBSize(ELEVATIONMODEL.GLOBE).ToString("F0"); + tb_Options_Propagation_SRTM3_Database_FileName.Text = GetDatabaseDir(PropagationData.Database.GetDBLocation(ELEVATIONMODEL.SRTM3)); + tb_Options_Propagation_SRTM3_Database_FileSize.Text = PropagationData.Database.GetDBSize(ELEVATIONMODEL.SRTM3).ToString("F0"); + tb_Options_Propagation_SRTM1_Database_FileName.Text = GetDatabaseDir(PropagationData.Database.GetDBLocation(ELEVATIONMODEL.SRTM1)); + tb_Options_Propagation_SRTM1_Database_FileSize.Text = PropagationData.Database.GetDBSize(ELEVATIONMODEL.SRTM1).ToString("F0"); + tb_Options_Elevation_GLOBE_Database_FileName.Text = GetDatabaseDir(ElevationData.Database.GetDBLocation(ELEVATIONMODEL.GLOBE)); + tb_Options_Elevation_GLOBE_Database_FileSize.Text = ElevationData.Database.GetDBSize(ELEVATIONMODEL.GLOBE).ToString("F0"); + tb_Options_Elevation_SRTM3_Database_FileName.Text = GetDatabaseDir(ElevationData.Database.GetDBLocation(ELEVATIONMODEL.SRTM3)); + tb_Options_Elevation_SRTM3_Database_FileSize.Text = ElevationData.Database.GetDBSize(ELEVATIONMODEL.SRTM3).ToString("F0"); + tb_Options_Elevation_SRTM1_Database_FileName.Text = GetDatabaseDir(ElevationData.Database.GetDBLocation(ELEVATIONMODEL.SRTM1)); + tb_Options_Elevation_SRTM1_Database_FileSize.Text = ElevationData.Database.GetDBSize(ELEVATIONMODEL.SRTM1).ToString("F0"); + double total = StationData.Database.GetDBSize() + + AircraftData.Database.GetDBSize() + + PropagationData.Database.GetDBSize(ELEVATIONMODEL.GLOBE) + + PropagationData.Database.GetDBSize(ELEVATIONMODEL.SRTM3) + + PropagationData.Database.GetDBSize(ELEVATIONMODEL.SRTM1) + + ElevationData.Database.GetDBSize(ELEVATIONMODEL.GLOBE) + + ElevationData.Database.GetDBSize(ELEVATIONMODEL.SRTM3) + + ElevationData.Database.GetDBSize(ELEVATIONMODEL.SRTM1); + lbl_Options_Database_TotalSize.Text = total.ToString("F0"); + rb_Options_Database_Update_Never.Checked = !Properties.Settings.Default.Background_Update_OnStartup && !Properties.Settings.Default.Background_Update_Periodically; + rb_Options_Database_Update_OnStartup.Checked = Properties.Settings.Default.Background_Update_OnStartup; + rb_Options_Database_Update_Periodically.Checked = Properties.Settings.Default.Background_Update_Periodically; + ud_Options_Database_Update_Period.Enabled = Properties.Settings.Default.Background_Update_Periodically; + } + + private void tab_Options_Database_Validating(object sender, CancelEventArgs e) + { + + } + + private void rb_Options_Database_Update_Never_CheckedChanged(object sender, EventArgs e) + { + if (rb_Options_Database_Update_Never.Checked) + { + ud_Options_Database_Update_Period.Enabled = false; + } + } + + private void rb_Options_Database_Update_OnStartup_CheckedChanged(object sender, EventArgs e) + { + if (rb_Options_Database_Update_OnStartup.Checked) + { + Properties.Settings.Default.Background_Update_OnStartup = true; + ud_Options_Database_Update_Period.Enabled = false; + } + else + Properties.Settings.Default.Background_Update_OnStartup = false; + } + + private void rb_Options_Database_Update_Periodically_CheckedChanged(object sender, EventArgs e) + { + if (rb_Options_Database_Update_Periodically.Checked) + { + Properties.Settings.Default.Background_Update_Periodically = true; + ud_Options_Database_Update_Period.Enabled = true; + } + else + Properties.Settings.Default.Background_Update_Periodically = false; + } + + private void btn_Open_LogDirectory_Click(object sender, EventArgs e) + { + Process.Start(ParentDlg.LogDirectory); + } + + private void btn_Open_TmpDirectory_Click(object sender, EventArgs e) + { + Process.Start(ParentDlg.TmpDirectory); + } + + private void cb_Options_Watchlist_SyncWithKST_CheckedChanged(object sender, EventArgs e) + { + btn_Options_Watchlist_Manage.Enabled = !cb_Options_Watchlist_SyncWithKST.Checked; + } + + private void btn_Options_DeleteAllElevationPaths_Click(object sender, EventArgs e) + { + if (MessageBox.Show("Are you sure to delete all precalculated elevation paths from database?", "Delete all elevation paths", MessageBoxButtons.YesNo) == DialogResult.Yes) + { + ElevationData.Database.ElevationPathDeleteAll(ELEVATIONMODEL.GLOBE); + ElevationData.Database.ElevationPathDeleteAll(ELEVATIONMODEL.SRTM3); + ElevationData.Database.ElevationPathDeleteAll(ELEVATIONMODEL.SRTM1); + } + } + + private void btn_Options_DeleteAllPropagationPaths_Click(object sender, EventArgs e) + { + if (MessageBox.Show("Are you sure to delete all precalculated propagation paths from database?", "Delete all propagation paths", MessageBoxButtons.YesNo) == DialogResult.Yes) + { + PropagationData.Database.PropagationPathDeleteAll(ELEVATIONMODEL.GLOBE); + PropagationData.Database.PropagationPathDeleteAll(ELEVATIONMODEL.SRTM3); + PropagationData.Database.PropagationPathDeleteAll(ELEVATIONMODEL.SRTM1); + } + } + + private void btn_Options_ScoutBase_Database_Maintenance_Click(object sender, EventArgs e) + { + ScoutBaseDatabaseMaintenanceDlg Dlg = new ScoutBaseDatabaseMaintenanceDlg(); + Dlg.ShowDialog(); + } + + private void btn_Options_AirScout_Database_Maintenance_Click(object sender, EventArgs e) + { + + } + + private void btn_Options_Propagation_GLOBE_Database_Maintenance_Click(object sender, EventArgs e) + { + + } + + private void btn_Options_Propagation_SRTM3_Database_Maintenance_Click(object sender, EventArgs e) + { + + } + + private void btn_Options_Propagation_SRTM1_Database_Maintenance_Click(object sender, EventArgs e) + { + + } + + private void btn_Options_Elevation_GLOBE_Database_Maintenance_Click(object sender, EventArgs e) + { + + } + + private void btn_Options_Elevation_SRTM3_Database_Maintenance_Click(object sender, EventArgs e) + { + + } + + private void btn_Options_Elevation_SRTM1_Database_Maintenance_Click(object sender, EventArgs e) + { + + } + + #endregion + + #region tab_Options_Stations + + private void tab_Options_Stations_Enter(object sender, EventArgs e) + { + // initially set textboxes + tb_Options_MyCall.SilentText = Properties.Settings.Default.MyCall; + tb_Options_MyLat.SilentValue = (double)Properties.Settings.Default.MyLat; + tb_Options_MyLon.SilentValue = (double)Properties.Settings.Default.MyLon; + MyLocator_Set(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon); + tb_Options_MyElevation.Text = ParentDlg.GetElevation(tb_Options_MyLat.Value, tb_Options_MyLon.Value).ToString(); + tb_Options_DXCall.SilentText = Properties.Settings.Default.DXCall; + tb_Options_DXLat.SilentValue = Properties.Settings.Default.DXLat; + tb_Options_DXLon.SilentValue = Properties.Settings.Default.DXLon; + DXLocator_Set(Properties.Settings.Default.DXLat, Properties.Settings.Default.DXLon); + tb_Options_DXElevation.Text = ParentDlg.GetElevation(tb_Options_DXLat.Value, tb_Options_DXLon.Value).ToString(); + tb_Options_Band.Text = Bands.GetStringValue(Properties.Settings.Default.Band); + MyQRV_Load(); + DXQRV_Load(); + MyLocation_Check(); + DXLocation_Check(); + } + + private void MyLocator_Set(double lat, double lon) + { + if (GeographicalPoint.Check(tb_Options_MyLat.Value, tb_Options_MyLon.Value)) + { + // colour Textbox if more precise lat/lon information is available + if (MaidenheadLocator.IsPrecise(tb_Options_MyLat.Value, tb_Options_MyLon.Value, 3)) + { + tb_Options_MyLoc.SilentText = MaidenheadLocator.LocFromLatLon(lat, lon, Properties.Settings.Default.Locator_SmallLettersForSubsquares, (int)Properties.Settings.Default.Locator_MaxLength / 2); + if (tb_Options_MyLoc.BackColor != Color.PaleGreen) + tb_Options_MyLoc.BackColor = Color.PaleGreen; + } + else + { + tb_Options_MyLoc.SilentText = MaidenheadLocator.LocFromLatLon(lat, lon, Properties.Settings.Default.Locator_SmallLettersForSubsquares, 3); + if (tb_Options_MyLoc.BackColor != Color.FloralWhite) + tb_Options_MyLoc.BackColor = Color.FloralWhite; + } + + } + else + { + tb_Options_MyLoc.SilentText = ""; + if (tb_Options_MyLoc.BackColor != Color.FloralWhite) + tb_Options_MyLoc.BackColor = Color.FloralWhite; + } + } + + private void tb_Options_MyCall_TextChanged(object sender, EventArgs e) + { + // try to find callsign in database + LocationDesignator ld = StationData.Database.LocationFind(tb_Options_MyCall.Text); + if (ld != null) + { + // update location info + tb_Options_MyLat.SilentValue = ld.Lat; + tb_Options_MyLon.SilentValue = ld.Lon; + MyLocator_Set(tb_Options_MyLat.Value, tb_Options_MyLon.Value); + tb_Options_DXElevation.Text = ParentDlg.GetElevation(tb_Options_DXLat.Value, tb_Options_DXLon.Value).ToString("F0"); + // update QRV info + MyQRV_Load(); + return; + } + else + { + MyLocation_Clear(); + MyQRV_Clear(); + } + MyLocation_Check(); + } + + private void tb_Options_MyLat_TextChanged(object sender, EventArgs e) + { + MyLocator_Set(tb_Options_MyLat.Value, tb_Options_MyLon.Value); + tb_Options_DXElevation.Text = ParentDlg.GetElevation(tb_Options_DXLat.Value, tb_Options_DXLon.Value).ToString("F0"); + } + + private void tb_Options_MyLon_TextChanged(object sender, EventArgs e) + { + MyLocator_Set(tb_Options_MyLat.Value, tb_Options_MyLon.Value); + tb_Options_DXElevation.Text = ParentDlg.GetElevation(tb_Options_DXLat.Value, tb_Options_DXLon.Value).ToString("F0"); + } + + private void tb_Options_MyLoc_TextChanged(object sender, EventArgs e) + { + // update lat/lon + double mlat, mlon; + if (tb_Options_MyLoc.Focused) + { + // locator box is focused --> update lat/lon + if (MaidenheadLocator.Check(tb_Options_MyLoc.Text) && tb_Options_MyLoc.Text.Length >= 6) + { + // colour Textbox if more precise lat/lon information is available + if (MaidenheadLocator.IsPrecise(tb_Options_DXLat.Value, tb_Options_DXLon.Value, 3)) + { + if (tb_Options_DXLoc.BackColor != Color.PaleGreen) + tb_Options_DXLoc.BackColor = Color.PaleGreen; + } + else + { + if (tb_Options_DXLoc.BackColor != Color.FloralWhite) + tb_Options_DXLoc.BackColor = Color.FloralWhite; + } + MaidenheadLocator.LatLonFromLoc(tb_Options_MyLoc.Text, PositionInRectangle.MiddleMiddle, out mlat, out mlon); + tb_Options_MyLat.SilentValue = mlat; + tb_Options_MyLon.SilentValue = mlon; + tb_Options_MyElevation.Text = ParentDlg.GetElevation(tb_Options_MyLat.Value, tb_Options_MyLon.Value).ToString("F0"); + // update QRV info + MyQRV_Load(); + } + else + { + tb_Options_MyLat.SilentValue = double.NaN; + tb_Options_MyLon.SilentValue = double.NaN; + MyQRV_Clear(); + } + } + } + + + private void btn_MyCall_QRZ_Click(object sender, EventArgs e) + { + try + { + WebRequest myWebRequest = WebRequest.Create(Properties.Settings.Default.QRZ_URL_Database + Properties.Settings.Default.MyCall); + myWebRequest.Timeout = 10000; + WebResponse myWebResponse = myWebRequest.GetResponse(); + Stream ReceiveStream = myWebResponse.GetResponseStream(); + Encoding encode = System.Text.Encoding.GetEncoding("utf-8"); + StreamReader readStream = new StreamReader(ReceiveStream, encode); + string s = readStream.ReadToEnd(); + if (s.IndexOf("cs_lat") >= 0) + { + string loc; + double lat = 0; + double lon = 0; + try + { + s = s.Remove(0, s.IndexOf("cs_lat = \"") + 10); + lat = System.Convert.ToDouble(s.Substring(0, s.IndexOf("\n") - 2), ENprovider); + s = s.Remove(0, s.IndexOf("cs_lon = \"") + 10); + lon = System.Convert.ToDouble(s.Substring(0, s.IndexOf("\n") - 2), ENprovider); + } + catch + { + } + loc = MaidenheadLocator.LocFromLatLon(lat, lon, false, 3); + // check if loc is matching --> refine lat/lon + if ((tb_Options_MyLoc.Text.Length >= 6) && (loc == MaidenheadLocator.Convert(tb_Options_MyLoc.Text, false).Substring(0, 6))) + { + Properties.Settings.Default["MyLat"] = lat; + Properties.Settings.Default.MyLon = lon; + tb_Options_MyLat.Text = Properties.Settings.Default.MyLat.ToString("F8", ENprovider); + tb_Options_MyLon.Text = Properties.Settings.Default.MyLon.ToString("F8", ENprovider); + MessageBox.Show("Position update from QRZ.com was performed succesfully.", "QRZ.com"); + return; + } + } + } + catch + { + } + MessageBox.Show("Position query at QRZ.com failed or does not match with current locator. \nNo update was performed.", "QRZ.com"); + } + + private void btn_Options_MyHorizon_Click(object sender, EventArgs e) + { + if (!MyLocation_Check()) + return; + HorizonDlg Dlg = new HorizonDlg(tb_Options_MyCall.Text, + tb_Options_MyLat.Value, + tb_Options_MyLon.Value, + LocalObstructions); + if (Dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) + { + // do nothing + } + } + + private void DXLocator_Set(double lat, double lon) + { + if (GeographicalPoint.Check(tb_Options_DXLat.Value, tb_Options_DXLon.Value)) + { + // colour Textbox if more precise lat/lon information is available + if (MaidenheadLocator.IsPrecise(tb_Options_DXLat.Value, tb_Options_DXLon.Value, 3)) + { + tb_Options_DXLoc.SilentText = MaidenheadLocator.LocFromLatLon(lat, lon, Properties.Settings.Default.Locator_SmallLettersForSubsquares, (int)Properties.Settings.Default.Locator_MaxLength / 2); + if (tb_Options_DXLoc.BackColor != Color.PaleGreen) + tb_Options_DXLoc.BackColor = Color.PaleGreen; + } + else + { + tb_Options_DXLoc.SilentText = MaidenheadLocator.LocFromLatLon(lat, lon, Properties.Settings.Default.Locator_SmallLettersForSubsquares, 3); + if (tb_Options_DXLoc.BackColor != Color.FloralWhite) + tb_Options_DXLoc.BackColor = Color.FloralWhite; + } + + } + else + { + tb_Options_DXLoc.SilentText = ""; + if (tb_Options_DXLoc.BackColor != Color.FloralWhite) + tb_Options_DXLoc.BackColor = Color.FloralWhite; + } + } + + private void tb_Options_DXCall_TextChanged(object sender, EventArgs e) + { + // try to find callsign in database + LocationDesignator ld = StationData.Database.LocationFind(tb_Options_DXCall.Text); + if (ld != null) + { + // update location info + tb_Options_DXLat.SilentValue = ld.Lat; + tb_Options_DXLon.SilentValue = ld.Lon; + DXLocator_Set(tb_Options_DXLat.Value, tb_Options_DXLon.Value); + tb_Options_DXElevation.Text = ParentDlg.GetElevation(tb_Options_DXLat.Value, tb_Options_DXLon.Value).ToString("F0"); + // update QRV info + DXQRV_Load(); + return; + } + else + { + DXLocation_Clear(); + DXQRV_Clear(); + } + DXLocation_Check(); + } + + private void tb_Options_DXLat_TextChanged(object sender, EventArgs e) + { + DXLocator_Set(tb_Options_DXLat.Value, tb_Options_DXLon.Value); + tb_Options_DXElevation.Text = ParentDlg.GetElevation(tb_Options_DXLat.Value, tb_Options_DXLon.Value).ToString("F0"); + } + + private void tb_Options_DXLon_TextChanged(object sender, EventArgs e) + { + DXLocator_Set(tb_Options_DXLat.Value, tb_Options_DXLon.Value); + tb_Options_DXElevation.Text = ParentDlg.GetElevation(tb_Options_DXLat.Value, tb_Options_DXLon.Value).ToString("F0"); + } + + private void tb_Options_DXLoc_TextChanged(object sender, EventArgs e) + { + // update lat/lon + double mlat, mlon; + if (tb_Options_DXLoc.Focused) + { + // locator box is focused --> update lat/lon + if (MaidenheadLocator.Check(tb_Options_DXLoc.Text) && tb_Options_DXLoc.Text.Length >= 6) + { + // colour Textbox if more precise lat/lon information is available + if (MaidenheadLocator.IsPrecise(tb_Options_DXLat.Value, tb_Options_DXLon.Value, 3)) + { + if (tb_Options_DXLoc.BackColor != Color.PaleGreen) + tb_Options_DXLoc.BackColor = Color.PaleGreen; + } + else + { + if (tb_Options_DXLoc.BackColor != Color.FloralWhite) + tb_Options_DXLoc.BackColor = Color.FloralWhite; + } + MaidenheadLocator.LatLonFromLoc(tb_Options_DXLoc.Text, PositionInRectangle.MiddleMiddle, out mlat, out mlon); + tb_Options_DXLat.SilentValue = mlat; + tb_Options_DXLon.SilentValue = mlon; + tb_Options_DXElevation.Text = ParentDlg.GetElevation(tb_Options_DXLat.Value, tb_Options_DXLon.Value).ToString("F0"); + // update QRV info + DXQRV_Load(); + } + else + { + tb_Options_DXLat.SilentValue = double.NaN; + tb_Options_DXLon.SilentValue = double.NaN; + DXQRV_Clear(); + } + } + } + + + private void btn_DXCall_QRZ_Click(object sender, EventArgs e) + { + try + { + WebRequest DXWebRequest = WebRequest.Create(Properties.Settings.Default.QRZ_URL_Database + Properties.Settings.Default.DXCall); + DXWebRequest.Timeout = 10000; + WebResponse DXWebResponse = DXWebRequest.GetResponse(); + Stream ReceiveStream = DXWebResponse.GetResponseStream(); + Encoding encode = System.Text.Encoding.GetEncoding("utf-8"); + StreamReader readStream = new StreamReader(ReceiveStream, encode); + string s = readStream.ReadToEnd(); + if (s.IndexOf("cs_lat") >= 0) + { + string loc; + double lat = 0; + double lon = 0; + try + { + s = s.Remove(0, s.IndexOf("cs_lat = \"") + 10); + lat = System.Convert.ToDouble(s.Substring(0, s.IndexOf("\n") - 2), ENprovider); + s = s.Remove(0, s.IndexOf("cs_lon = \"") + 10); + lon = System.Convert.ToDouble(s.Substring(0, s.IndexOf("\n") - 2), ENprovider); + } + catch + { + } + loc = MaidenheadLocator.LocFromLatLon(lat, lon, false, 3); + // check if loc is matching --> refine lat/lon + if ((tb_Options_DXLoc.Text.Length >= 6) && (loc == MaidenheadLocator.Convert(tb_Options_DXLoc.Text, false).Substring(0, 6))) + { + Properties.Settings.Default["DXLat"] = lat; + Properties.Settings.Default.DXLon = lon; + tb_Options_DXLat.Text = Properties.Settings.Default.DXLat.ToString("F8", ENprovider); + tb_Options_DXLon.Text = Properties.Settings.Default.DXLon.ToString("F8", ENprovider); + MessageBox.Show("Position update from QRZ.com was performed succesfully.", "QRZ.com"); + return; + } + } + } + catch + { + } + MessageBox.Show("Position query at QRZ.com failed or does not match with current locator. \nNo update was performed.", "QRZ.com"); + } + + private void btn_Options_DXHorizon_Click(object sender, EventArgs e) + { + if (!DXLocation_Check()) + return; + HorizonDlg Dlg = new HorizonDlg(tb_Options_DXCall.Text, + tb_Options_DXLat.Value, + tb_Options_DXLon.Value, + null); + if (Dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) + { + // do nothing + } + } + + private void cb_Options_SmallLettersForSubSquares_CheckedChanged(object sender, EventArgs e) + { + Properties.Settings.Default.Locator_SmallLettersForSubsquares = cb_Options_SmallLettersForSubSquares.Checked; + // refresh layout + tab_Options_Stations_Enter(this, null); + } + + private void tab_Options_Stations_Validating(object sender, CancelEventArgs e) + { + // validates station details + // enables leaving tab + if (!MyLocation_Check() || !MyQRV_Check() || !DXLocation_Check() || !DXQRV_Check()) + e.Cancel = true; + } + + private void btn_Options_BandDown_Click(object sender, EventArgs e) + { + if (MyQRV_Check()) + MyQRV_Save(); + if (DXQRV_Check()) + DXQRV_Save(); + if (MyLocation_Check() && DXLocation_Check()) + { + Properties.Settings.Default.Band = Bands.Previous(Properties.Settings.Default.Band); + tb_Options_Band.Text = Bands.GetStringValue(Properties.Settings.Default.Band); + MyQRV_Load(); + DXQRV_Load(); + } + } + + private void btn_Options_BandUp_Click(object sender, EventArgs e) + { + if (MyQRV_Check()) + MyQRV_Save(); + if (DXQRV_Check()) + DXQRV_Save(); + if (MyLocation_Check() && DXLocation_Check()) + { + Properties.Settings.Default.Band = Bands.Next(Properties.Settings.Default.Band); + tb_Options_Band.Text = Bands.GetStringValue(Properties.Settings.Default.Band); + MyQRV_Load(); + DXQRV_Load(); + } + } + + private void MyLocation_Clear() + { + tb_Options_MyLoc.SilentText = ""; + tb_Options_MyLat.SilentText = ""; + tb_Options_MyLon.SilentText = ""; + tb_Options_MyElevation.Text = ""; + if (tb_Options_MyLoc.BackColor != Color.FloralWhite) + tb_Options_MyLoc.BackColor = Color.FloralWhite; + } + + private void DXLocation_Clear() + { + tb_Options_DXLoc.SilentText = ""; + tb_Options_DXLat.SilentText = ""; + tb_Options_DXLon.SilentText = ""; + tb_Options_DXElevation.Text = ""; + if (tb_Options_DXLoc.BackColor != Color.FloralWhite) + tb_Options_DXLoc.BackColor = Color.FloralWhite; + } + + private bool MyLocation_Check() + { + // check all values + if (Callsign.Check(tb_Options_MyCall.Text) && MaidenheadLocator.Check(tb_Options_MyLoc.Text) && GeographicalPoint.Check(tb_Options_MyLat.Value, tb_Options_MyLon.Value) && MyQRV_Check()) + { + // check for local obstructions + LocalObstructions = ElevationData.Database.LocalObstructionFind(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, Properties.Settings.Default.ElevationModel); + if (LocalObstructions != null) + lbl_Options_LocalObstructions.Text = "Local Obstruction found for this location."; + else + lbl_Options_LocalObstructions.Text = "Local Obstruction not found for this location."; + return true; + } + return false; + } + + private bool DXLocation_Check() + { + // check all values + if (Callsign.Check(tb_Options_DXCall.Text) && MaidenheadLocator.Check(tb_Options_DXLoc.Text) && GeographicalPoint.Check(tb_Options_DXLat.Value, tb_Options_DXLon.Value) && DXQRV_Check()) + { + return true; + } + return false; + } + + private void MyLocation_Save() + { + // save location to settings && database + if (MyLocation_Check()) + { + Properties.Settings.Default.MyCall = tb_Options_MyCall.Text; + Properties.Settings.Default.MyLat = tb_Options_MyLat.Value; + Properties.Settings.Default.MyLon = tb_Options_MyLon.Value; + // find entry in database + LocationDesignator ld = StationData.Database.LocationFind(tb_Options_MyCall.Text, MaidenheadLocator.LocFromLatLon(tb_Options_MyLat.Value, tb_Options_MyLon.Value, false, 3)); + // update if not found or different values + if ((ld == null) || (ld.Lat != tb_Options_MyLat.Value) || (ld.Lon != tb_Options_MyLon.Value)) + StationData.Database.LocationInsertOrUpdateIfNewer(new LocationDesignator(tb_Options_MyCall.Text, tb_Options_MyLat.Value, tb_Options_MyLon.Value, (MaidenheadLocator.IsPrecise(tb_Options_MyLat.Value, tb_Options_MyLon.Value, 3) ? GEOSOURCE.FROMUSER : GEOSOURCE.FROMLOC))); + } + } + + private void DXLocation_Save() + { + // save location to settings && database + if (DXLocation_Check()) + { + Properties.Settings.Default.DXCall = tb_Options_DXCall.Text; + Properties.Settings.Default.DXLat = tb_Options_DXLat.Value; + Properties.Settings.Default.DXLon = tb_Options_DXLon.Value; + // find entry in database + LocationDesignator ld = StationData.Database.LocationFind(tb_Options_DXCall.Text, MaidenheadLocator.LocFromLatLon(tb_Options_DXLat.Value, tb_Options_DXLon.Value, false, 3)); + // update if not found or different values + if ((ld == null) || (ld.Lat != tb_Options_DXLat.Value) || (ld.Lon != tb_Options_DXLon.Value)) + StationData.Database.LocationInsertOrUpdateIfNewer(new LocationDesignator(tb_Options_DXCall.Text, tb_Options_DXLat.Value, tb_Options_DXLon.Value, (MaidenheadLocator.IsPrecise(tb_Options_DXLat.Value, tb_Options_DXLon.Value, 3) ? GEOSOURCE.FROMUSER : GEOSOURCE.FROMLOC))); + } + } + + private void btn_Options_MyUpdate_Click(object sender, EventArgs e) + { + if (MyLocation_Check() && MyQRV_Check()) + { + SFTPDoWorkEventArgs args = new SFTPDoWorkEventArgs(); + args.ld = StationData.Database.LocationFind(tb_Options_MyCall.Text, MaidenheadLocator.LocFromLatLon(tb_Options_MyLat.Value, tb_Options_MyLon.Value, false, 3)); + args.qrvs = StationData.Database.QRVFind(tb_Options_MyCall.Text, MaidenheadLocator.LocFromLatLon(tb_Options_MyLat.Value, tb_Options_MyLon.Value, false, 3)); + bw_SFTP.RunWorkerAsync(args); + } + } + + private void btn_Options_DXUpdate_Click(object sender, EventArgs e) + { + if (DXLocation_Check() && DXQRV_Check()) + { + SFTPDoWorkEventArgs args = new SFTPDoWorkEventArgs(); + args.ld = StationData.Database.LocationFind(tb_Options_DXCall.Text, MaidenheadLocator.LocFromLatLon(tb_Options_DXLat.Value, tb_Options_DXLon.Value, false, 3)); + args.qrvs = StationData.Database.QRVFind(tb_Options_DXCall.Text, MaidenheadLocator.LocFromLatLon(tb_Options_DXLat.Value, tb_Options_DXLon.Value, false, 3)); + bw_SFTP.RunWorkerAsync(args); + } + } + + private bool MyQRV_Check() + { + if (double.IsNaN(tb_Options_MyAntennaHeight.Value) & double.IsNaN(tb_Options_MyAntennaGain.Value) & double.IsNaN(tb_Options_MyPower.Value)) + return true; + if (!double.IsNaN(tb_Options_MyAntennaHeight.Value) & !double.IsNaN(tb_Options_MyAntennaGain.Value) & !double.IsNaN(tb_Options_MyPower.Value)) + return true; + MessageBox.Show("The QRV information you entered is incorrect.\n The fields \"Antenna Heigth\", \"Antenna Gain\" and \"Power\" must either be all filled or all left empty.\nPlease use one of the following options:\n\n<>0 : station is QRV and value is known\n=0 : Station is QRV and value is not konwn\nEmpty: Station is not QRV", "QRV Information incorrect"); + return false; + } + + private bool DXQRV_Check() + { + if (double.IsNaN(tb_Options_DXAntennaHeight.Value) & double.IsNaN(tb_Options_DXAntennaGain.Value) & double.IsNaN(tb_Options_DXPower.Value)) + return true; + if (!double.IsNaN(tb_Options_DXAntennaHeight.Value) & !double.IsNaN(tb_Options_DXAntennaGain.Value) & !double.IsNaN(tb_Options_DXPower.Value)) + return true; + MessageBox.Show("The QRV information you entered is incorrect.\n The fields \"Antenna Heigth\", \"Antenna Gain\" and \"Power\" must either be all filled or all left empty.\nPlease use one of the following options:\n\n<>0 : station is QRV and value is known\n=0 : Station is QRV and value is not konwn\nEmpty: Station is not QRV", "QRV Information incorrect"); + return false; + } + + private void MyQRV_Clear() + { + tb_Options_MyAntennaHeight.Text = ""; + tb_Options_MyAntennaGain.Text = ""; + tb_Options_MyPower.Text = ""; + lbl_Options_MyLastUpdated.Text = ""; + } + + private void DXQRV_Clear() + { + tb_Options_DXAntennaHeight.Text = ""; + tb_Options_DXAntennaGain.Text = ""; + tb_Options_DXPower.Text = ""; + lbl_Options_DXLastUpdated.Text = ""; + } + + private void MyQRV_Load() + { + if (!Callsign.Check(tb_Options_MyCall.Text) || !MaidenheadLocator.Check(tb_Options_MyLoc.Text)) + return; + if (tb_Options_MyLoc.Text.Length < 6) + return; + QRVDesignator qrv = StationData.Database.QRVFind(tb_Options_MyCall.Text, tb_Options_MyLoc.Text, Properties.Settings.Default.Band); + if (qrv != null) + { + tb_Options_MyAntennaHeight.Value = qrv.AntennaHeight; + tb_Options_MyAntennaGain.Value = qrv.AntennaGain; + tb_Options_MyPower.Value = qrv.Power; + lbl_Options_MyLastUpdated.Text = qrv.LastUpdated.ToString("yyyy-MM-dd HH:mm:ss"); + } + else + { + tb_Options_MyAntennaHeight.Value = double.NaN; + tb_Options_MyAntennaGain.Value = double.NaN; + tb_Options_MyPower.Value = double.NaN; + lbl_Options_MyLastUpdated.Text = ""; + } + } + + private void DXQRV_Load() + { + if (!Callsign.Check(tb_Options_DXCall.Text) || !MaidenheadLocator.Check(tb_Options_DXLoc.Text)) + return; + if (tb_Options_DXLoc.Text.Length < 6) + return; + QRVDesignator qrv = StationData.Database.QRVFind(tb_Options_DXCall.Text, tb_Options_DXLoc.Text, Properties.Settings.Default.Band); + if (qrv != null) + { + tb_Options_DXAntennaHeight.Value = qrv.AntennaHeight; + tb_Options_DXAntennaGain.Value = qrv.AntennaGain; + tb_Options_DXPower.Value = qrv.Power; + lbl_Options_DXLastUpdated.Text = qrv.LastUpdated.ToString("yyyy-MM-dd HH:mm:ss"); + } + else + { + tb_Options_DXAntennaHeight.Value = double.NaN; + tb_Options_DXAntennaGain.Value = double.NaN; + tb_Options_DXPower.Value = double.NaN; + lbl_Options_DXLastUpdated.Text = ""; + } + } + + private void MyQRV_Save() + { + if (MyQRV_Check()) + { + QRVDesignator qrv = StationData.Database.QRVFind(tb_Options_MyCall.Text, tb_Options_MyLoc.Text, Properties.Settings.Default.Band); + if (double.IsNaN(tb_Options_MyAntennaHeight.Value) && double.IsNaN(tb_Options_MyAntennaGain.Value) && double.IsNaN(tb_Options_MyPower.Value)) + { + if (qrv != null) + StationData.Database.QRVDelete(qrv); + } + else + { + if ((qrv == null) || (qrv.AntennaHeight != tb_Options_MyAntennaHeight.Value) || (qrv.AntennaGain != tb_Options_MyAntennaGain.Value) || (qrv.Power != tb_Options_MyPower.Value)) + { + qrv = new QRVDesignator(tb_Options_MyCall.Text, tb_Options_MyLoc.Text, Properties.Settings.Default.Band, tb_Options_MyAntennaHeight.Value, tb_Options_MyAntennaGain.Value, tb_Options_MyPower.Value); + StationData.Database.QRVInsertOrUpdateIfNewer(qrv); + } + } + } + } + + private void DXQRV_Save() + { + if (DXQRV_Check()) + { + QRVDesignator qrv = StationData.Database.QRVFind(tb_Options_DXCall.Text, tb_Options_DXLoc.Text, Properties.Settings.Default.Band); + if (double.IsNaN(tb_Options_DXAntennaHeight.Value) && double.IsNaN(tb_Options_DXAntennaGain.Value) && double.IsNaN(tb_Options_DXPower.Value)) + { + if (qrv != null) + StationData.Database.QRVDelete(qrv); + } + else + { + if ((qrv == null) || (qrv.AntennaHeight != tb_Options_DXAntennaHeight.Value) || (qrv.AntennaGain != tb_Options_DXAntennaGain.Value) || (qrv.Power != tb_Options_DXPower.Value)) + { + qrv = new QRVDesignator(tb_Options_DXCall.Text, tb_Options_DXLoc.Text, Properties.Settings.Default.Band, tb_Options_DXAntennaHeight.Value, tb_Options_DXAntennaGain.Value, tb_Options_DXPower.Value); + StationData.Database.QRVInsertOrUpdateIfNewer(qrv); + } + } + } + } + + private void btn_Options_MyMap_Click(object sender, EventArgs e) + { + if (Callsign.Check(tb_Options_MyCall.Text) && MaidenheadLocator.Check(tb_Options_MyLoc.Text)) + { + LocationDesignator ld = new LocationDesignator(tb_Options_MyCall.Text, tb_Options_MyLoc.Text); + MapStationDlg Dlg = new MapStationDlg(ld); + if (Dlg.ShowDialog() == DialogResult.OK) + { + StationData.Database.LocationInsertOrUpdateIfNewer(Dlg.StationLocation); + tb_Options_MyLat.SilentValue = Dlg.StationLocation.Lat; + tb_Options_MyLon.SilentValue = Dlg.StationLocation.Lon; + MyLocator_Set(tb_Options_MyLat.Value, tb_Options_MyLon.Value); + } + } + } + + private void btn_Options_DXMap_Click(object sender, EventArgs e) + { + if (Callsign.Check(tb_Options_DXCall.Text) && MaidenheadLocator.Check(tb_Options_DXLoc.Text)) + { + LocationDesignator ld = new LocationDesignator(tb_Options_DXCall.Text, tb_Options_DXLoc.Text); + MapStationDlg Dlg = new MapStationDlg(ld); + if (Dlg.ShowDialog() == DialogResult.OK) + { + StationData.Database.LocationInsertOrUpdateIfNewer(Dlg.StationLocation); + tb_Options_DXLat.SilentValue = Dlg.StationLocation.Lat; + tb_Options_DXLon.SilentValue = Dlg.StationLocation.Lon; + DXLocator_Set(tb_Options_DXLat.Value, tb_Options_DXLon.Value); + } + } + } + + private void btn_Options_LocalObstructions_Click(object sender, EventArgs e) + { + try + { + LocalObstructions = ElevationData.Database.LocalObstructionFindOrCreateDefault(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, Properties.Settings.Default.ElevationModel); + LocalObstructionsDlg Dlg = new LocalObstructionsDlg(LocalObstructions); + if (Dlg.ShowDialog() == DialogResult.OK) + { + // create new local obstructions object and save it to database + LocalObstructions = new LocalObstructionDesignator(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, Dlg.LocalObstructions); + ElevationData.Database.LocalObstructionInsertOrUpdateIfNewer(LocalObstructions, Properties.Settings.Default.ElevationModel); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + + private void tab_Options_Stations_Leave(object sender, EventArgs e) + { + MyLocation_Save(); + MyQRV_Save(); + DXLocation_Save(); + DXQRV_Save(); + } + + + #endregion + + #region tab_Options_Map + + private void tab_Options_Map_Enter(object sender, EventArgs e) + { + // populate the list of providers depending on user acception + cb_Options_Map_Provider.DataSource = null; + cb_Options_Map_Provider.Items.Clear(); + if (!String.IsNullOrEmpty(Properties.Settings.Default.Map_Provider_Accepted)) + { + // get a full list of all map providers + cb_Options_Map_Provider.ValueMember = "Name"; + cb_Options_Map_Provider.DataSource = GMapProviders.List; + } + else + { + cb_Options_Map_Provider.Items.Add(GMapProviders.Find("None")); + cb_Options_Map_Provider.Items.Add(GMapProviders.Find("OpenStreetMap")); + } + cb_Options_Map_Provider.SelectedItem = GMapProviders.Find(Properties.Settings.Default.Map_Provider); + } + + private void tab_Options_Map_Validating(object sender, CancelEventArgs e) + { + // save map provider + if (cb_Options_Map_Provider.SelectedItem != null) + { + Properties.Settings.Default.Map_Provider = cb_Options_Map_Provider.SelectedItem.ToString(); + } + } + + private void cb_Options_Map_Provider_DropDown(object sender, EventArgs e) + { + // show the MapProviderAcceptDlg on first run + if (String.IsNullOrEmpty(Properties.Settings.Default.Map_Provider_Accepted)) + { + MapProviderDlg Dlg = new MapProviderDlg(); + if (Dlg.ShowDialog() == DialogResult.OK) + { + // making a unique ID for confirmation + string ID = ""; + try + { + ID = (string)Registry.GetValue("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion", "ProductId", ""); + } + catch + { + ID = "Key not found!"; + } + ID = ID + "," + DateTime.UtcNow.ToString("u"); + ID = ID + "," + System.Security.Principal.WindowsIdentity.GetCurrent().Name; + Properties.Settings.Default.Map_Provider_Accepted = ID; + Properties.Settings.Default.Map_Provider_Accepted = ID; + // get a full list of all map providers + cb_Options_Map_Provider.DataSource = null; + cb_Options_Map_Provider.Items.Clear(); + cb_Options_Map_Provider.ValueMember = "Name"; + cb_Options_Map_Provider.DataSource = GMapProviders.List; + } + else + { + Properties.Settings.Default.Map_Provider_Accepted = ""; + Properties.Settings.Default.Map_Provider_Accepted = ""; + } + } + } + + private void btn_Options_SelectFont_Click(object sender, EventArgs e) + { + try + { + FontDialog Dlg = new FontDialog(); + string[] a = Properties.Settings.Default.Map_ToolTipFont.Split(';'); + string fontfamily = a[0].Trim(); + float emsize = 0; + float.TryParse(a[1].Trim(), NumberStyles.Float, ENprovider, out emsize); + FontStyle fontstyle = 0; + // check if any additional font style is given + if (a.Length > 2) + { + if (a[2].ToUpper().IndexOf("BOLD") >= 0) + fontstyle = fontstyle | FontStyle.Bold; + if (a[2].ToUpper().IndexOf("ITALIC") >= 0) + fontstyle = fontstyle | FontStyle.Italic; + if (a[2].ToUpper().IndexOf("UNDERLINE") >= 0) + fontstyle = fontstyle | FontStyle.Underline; + if (a[2].ToUpper().IndexOf("STRIKEOUT") >= 0) + fontstyle = fontstyle | FontStyle.Strikeout; + } + else + { + fontstyle = FontStyle.Regular; + } + Dlg.Font = new Font(fontfamily, emsize, fontstyle, GraphicsUnit.Point); + if (Dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) + { + string font = Dlg.Font.FontFamily.Name + ";" + Dlg.Font.Size.ToString(ENprovider); + string style = Dlg.Font.Style.ToString(); + if (style != "Regular") + font = font + ";style=" + style; + tb_Options_Map_ToolTipFont.Text = font; + Properties.Settings.Default.Map_ToolTipFont = font; + } + } + catch + { + Properties.Settings.Default.Map_ToolTipFont = "Microsoft Sans Serif;14;style=Bold"; + } + } + + + #endregion + + #region tab_Options_GLOBE + + private void bw_GLOBE_MapUpdater_DoWork(object sender, DoWorkEventArgs e) + { + bw_GLOBE_MapUpdater.ReportProgress(0, "GLOBE: Creating elevation tile catalogue..."); + // get all locs needed for covered area + ElevationCatalogue availabletiles = ElevationData.Database.ElevationCatalogueCreateCheckBoundsAndLastModified(ELEVATIONMODEL.GLOBE, Properties.Settings.Default.MinLat, Properties.Settings.Default.MinLon, Properties.Settings.Default.MaxLat, Properties.Settings.Default.MaxLon); + bw_GLOBE_MapUpdater.ReportProgress(0, "GLOBE: Processing tiles..."); + int missing = 0; + int found = 0; + foreach (string tilename in availabletiles.Files.Keys) + { + if (ElevationData.Database.ElevationTileExists(tilename.Substring(0, 6), ELEVATIONMODEL.GLOBE)) + { + bw_GLOBE_MapUpdater.ReportProgress(1, tilename); + found++; + } + else + { + bw_GLOBE_MapUpdater.ReportProgress(-1, tilename); + missing++; + } + if (bw_GLOBE_MapUpdater.CancellationPending) + { + bw_GLOBE_MapUpdater.ReportProgress(0, "GLOBE: Processing cancelled..."); + return; + } + } + bw_GLOBE_MapUpdater.ReportProgress(0, "GLOBE: " + found.ToString() + " tile(s) found, " + missing.ToString() + " more tile(s) available and missing."); + } + + private void bw_GLOBE_MapUpdater_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + if (e.ProgressPercentage == 1) + { + // add a tile found in database to map polygons + double baselat; + double baselon; + MaidenheadLocator.LatLonFromLoc(((string)e.UserState).Substring(0, 6), PositionInRectangle.BottomLeft, out baselat, out baselon); + List l = new List(); + l.Add(new PointLatLng((decimal)baselat, (decimal)baselon)); + l.Add(new PointLatLng((decimal)(baselat + 1 / 24.0), (decimal)baselon)); + l.Add(new PointLatLng((decimal)(baselat + 1 / 24.0), (decimal)(baselon + 2 / 24.0))); + l.Add(new PointLatLng((decimal)baselat, (decimal)(baselon + 2 / 24.0))); + GMapPolygon p = new GMapPolygon(l, (string)e.UserState); + p.Stroke = new Pen(Color.FromArgb(50, Color.Green)); + p.Fill = new SolidBrush(Color.FromArgb(50, Color.Green)); + GLOBEpolygons.Polygons.Add(p); + } + else if (e.ProgressPercentage == -1) + { + // add missing tile to map polygons + double baselat; + double baselon; + MaidenheadLocator.LatLonFromLoc(((string)e.UserState).Substring(0, 6), PositionInRectangle.BottomLeft, out baselat, out baselon); + List l = new List(); + l.Add(new PointLatLng((decimal)baselat, (decimal)baselon)); + l.Add(new PointLatLng((decimal)(baselat + 1 / 24.0), (decimal)baselon)); + l.Add(new PointLatLng((decimal)(baselat + 1 / 24.0), (decimal)(baselon + 2 / 24.0))); + l.Add(new PointLatLng((decimal)baselat, (decimal)(baselon + 2 / 24.0))); + GMapPolygon p = new GMapPolygon(l, (string)e.UserState); + p.Stroke = new Pen(Color.FromArgb(50, Color.Red)); + p.Fill = new SolidBrush(Color.FromArgb(50, Color.Red)); + GLOBEpolygons.Polygons.Add(p); + } + else + { + Say((string)e.UserState); + } + } + + private void bw_GLOBE_MapUpdater_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + } + + private void tab_Options_GLOBE_Enter(object sender, EventArgs e) + { + // clear map polygons + GLOBEpolygons.Clear(); + // add coverage to map polygons + List cl = new List(); + cl.Add(new PointLatLng(Properties.Settings.Default.MinLat, Properties.Settings.Default.MinLon)); + cl.Add(new PointLatLng(Properties.Settings.Default.MinLat, Properties.Settings.Default.MaxLon)); + cl.Add(new PointLatLng(Properties.Settings.Default.MaxLat, Properties.Settings.Default.MaxLon)); + cl.Add(new PointLatLng(Properties.Settings.Default.MaxLat, Properties.Settings.Default.MinLon)); + GMapPolygon c = new GMapPolygon(cl, "Coverage"); + c.Stroke = new Pen(Color.FromArgb(255, Color.Magenta), 3); + c.Fill = new SolidBrush(Color.FromArgb(0, Color.Magenta)); + GLOBEpolygons.Polygons.Add(c); + // zoom the map initally + gm_Options_GLOBE.SetZoomToFitRect(RectLatLng.FromLTRB(Properties.Settings.Default.MinLon, Properties.Settings.Default.MaxLat, Properties.Settings.Default.MaxLon, Properties.Settings.Default.MinLat)); + // start map updater + if (!bw_GLOBE_MapUpdater.IsBusy) + bw_GLOBE_MapUpdater.RunWorkerAsync(); + // zoom the map + gm_Options_Coverage.SetZoomToFitRect(RectLatLng.FromLTRB(Properties.Settings.Default.MinLon - 1, Properties.Settings.Default.MaxLat + 1, Properties.Settings.Default.MaxLon + 1, Properties.Settings.Default.MinLat - 1)); + } + + private void tab_Options_GLOBE_Leave(object sender, EventArgs e) + { + // stop map updater + bw_GLOBE_MapUpdater.CancelAsync(); + // clear map polygons + GLOBEpolygons.Clear(); + // do garbage collection + GC.Collect(); + Say(""); + } + + private void btn_Options_GLOBE_Copyright_Click(object sender, EventArgs e) + { + ElevationCopyrightDlg Dlg = new ElevationCopyrightDlg(); + Dlg.Text = "GLOBE Copyright Information"; + Dlg.rtb_Copyright.Text = Properties.Settings.Default.Elevation_GLOBE_Copyright; + Dlg.ShowDialog(); + } + + #endregion + + #region tab_Options_SRTM3 + + private void bw_SRTM3_MapUpdater_DoWork(object sender, DoWorkEventArgs e) + { + bw_SRTM3_MapUpdater.ReportProgress(0, "SRTM3: Creating elevation tile catalogue..."); + ElevationCatalogue availabletiles = ElevationData.Database.ElevationCatalogueCreateCheckBoundsAndLastModified(ELEVATIONMODEL.SRTM3, Properties.Settings.Default.MinLat, Properties.Settings.Default.MinLon, Properties.Settings.Default.MaxLat, Properties.Settings.Default.MaxLon); + bw_SRTM3_MapUpdater.ReportProgress(0, "SRTM3: Processing tiles..."); + int missing = 0; + int found = 0; + foreach (string tilename in availabletiles.Files.Keys) + { + if (ElevationData.Database.ElevationTileExists(tilename.Substring(0, 6), ELEVATIONMODEL.SRTM3)) + { + bw_SRTM3_MapUpdater.ReportProgress(1, tilename); + found++; + } + else + { + bw_SRTM3_MapUpdater.ReportProgress(-1, tilename); + missing++; + } + if (bw_SRTM3_MapUpdater.CancellationPending) + { + bw_SRTM3_MapUpdater.ReportProgress(0, "SRTM3: Processing cancelled..."); + return; + } + } + bw_SRTM3_MapUpdater.ReportProgress(0, "SRTM3: " + found.ToString() + " tile(s) found, " + missing.ToString() + " more tile(s) available and missing."); + } + + private void bw_SRTM3_MapUpdater_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + if (e.ProgressPercentage == 1) + { + // add a tile found in database to map polygons + double baselat; + double baselon; + MaidenheadLocator.LatLonFromLoc(((string)e.UserState).Substring(0, 6), PositionInRectangle.BottomLeft, out baselat, out baselon); + List l = new List(); + l.Add(new PointLatLng((decimal)baselat, (decimal)baselon)); + l.Add(new PointLatLng((decimal)(baselat + 1 / 24.0), (decimal)baselon)); + l.Add(new PointLatLng((decimal)(baselat + 1 / 24.0), (decimal)(baselon + 2 / 24.0))); + l.Add(new PointLatLng((decimal)baselat, (decimal)(baselon + 2 / 24.0))); + GMapPolygon p = new GMapPolygon(l, (string)e.UserState); + p.Stroke = new Pen(Color.FromArgb(50, Color.Green)); + p.Fill = new SolidBrush(Color.FromArgb(50, Color.Green)); + SRTM3polygons.Polygons.Add(p); + } + else if (e.ProgressPercentage == -1) + { + // add missing tile to map polygons + double baselat; + double baselon; + MaidenheadLocator.LatLonFromLoc(((string)e.UserState).Substring(0, 6), PositionInRectangle.BottomLeft, out baselat, out baselon); + List l = new List(); + l.Add(new PointLatLng((decimal)baselat, (decimal)baselon)); + l.Add(new PointLatLng((decimal)(baselat + 1 / 24.0), (decimal)baselon)); + l.Add(new PointLatLng((decimal)(baselat + 1 / 24.0), (decimal)(baselon + 2 / 24.0))); + l.Add(new PointLatLng((decimal)baselat, (decimal)(baselon + 2 / 24.0))); + GMapPolygon p = new GMapPolygon(l, (string)e.UserState); + p.Stroke = new Pen(Color.FromArgb(50, Color.Red)); + p.Fill = new SolidBrush(Color.FromArgb(50, Color.Red)); + SRTM3polygons.Polygons.Add(p); + } + else + { + Say((string)e.UserState); + } + } + + private void bw_SRTM3_MapUpdater_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + } + + private void tab_Options_SRTM3_Enter(object sender, EventArgs e) + { + // clear map polygons + SRTM3polygons.Clear(); + // add coverage to map polygons + List cl = new List(); + cl.Add(new PointLatLng(Properties.Settings.Default.MinLat, Properties.Settings.Default.MinLon)); + cl.Add(new PointLatLng(Properties.Settings.Default.MinLat, Properties.Settings.Default.MaxLon)); + cl.Add(new PointLatLng(Properties.Settings.Default.MaxLat, Properties.Settings.Default.MaxLon)); + cl.Add(new PointLatLng(Properties.Settings.Default.MaxLat, Properties.Settings.Default.MinLon)); + GMapPolygon c = new GMapPolygon(cl, "Coverage"); + c.Stroke = new Pen(Color.FromArgb(255, Color.Magenta), 3); + c.Fill = new SolidBrush(Color.FromArgb(0, Color.Magenta)); + SRTM3polygons.Polygons.Add(c); + // zoom the map initally + gm_Options_SRTM3.SetZoomToFitRect(RectLatLng.FromLTRB(Properties.Settings.Default.MinLon, Properties.Settings.Default.MaxLat, Properties.Settings.Default.MaxLon, Properties.Settings.Default.MinLat)); + // start map updater + if (!bw_SRTM3_MapUpdater.IsBusy) + bw_SRTM3_MapUpdater.RunWorkerAsync(); + // zoom the map + gm_Options_Coverage.SetZoomToFitRect(RectLatLng.FromLTRB(Properties.Settings.Default.MinLon - 1, Properties.Settings.Default.MaxLat + 1, Properties.Settings.Default.MaxLon + 1, Properties.Settings.Default.MinLat - 1)); + } + + private void tab_Options_SRTM3_Leave(object sender, EventArgs e) + { + // stop map updater + bw_SRTM3_MapUpdater.CancelAsync(); + // clear map polygons + SRTM3polygons.Clear(); + // do garbage collection + GC.Collect(); + Say(""); + } + + private void btn_Options_SRTM3_Copyright_Click(object sender, EventArgs e) + { + ElevationCopyrightDlg Dlg = new ElevationCopyrightDlg(); + Dlg.Text = "SRTM3 Copyright Information"; + Dlg.rtb_Copyright.Text = Properties.Settings.Default.Elevation_SRTM3_Copyright; + Dlg.ShowDialog(); + } + + #endregion + + #region tab_Options_SRTM1 + + private void bw_SRTM1_MapUpdater_DoWork(object sender, DoWorkEventArgs e) + { + bw_SRTM1_MapUpdater.ReportProgress(0, "SRTM1: Creating elevation tile catalogue..."); + ElevationCatalogue availabletiles = ElevationData.Database.ElevationCatalogueCreateCheckBoundsAndLastModified(ELEVATIONMODEL.SRTM1, Properties.Settings.Default.MinLat, Properties.Settings.Default.MinLon, Properties.Settings.Default.MaxLat, Properties.Settings.Default.MaxLon); + bw_SRTM1_MapUpdater.ReportProgress(0, "SRTM1: Processing tiles..."); + int missing = 0; + int found = 0; + foreach (string tilename in availabletiles.Files.Keys) + { + if (ElevationData.Database.ElevationTileExists(tilename.Substring(0, 6), ELEVATIONMODEL.SRTM1)) + { + bw_SRTM1_MapUpdater.ReportProgress(1, tilename); + found++; + } + else + { + bw_SRTM1_MapUpdater.ReportProgress(-1, tilename); + missing++; + } + if (bw_SRTM1_MapUpdater.CancellationPending) + { + bw_SRTM1_MapUpdater.ReportProgress(0, "SRTM1: Processing cancelled..."); + return; + } + } + bw_SRTM1_MapUpdater.ReportProgress(0, "SRTM1: " + found.ToString() + " tile(s) found, " + missing.ToString() + " more tile(s) available and missing."); + } + + private void bw_SRTM1_MapUpdater_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + if (e.ProgressPercentage == 1) + { + // add a tile found in database to map polygons + double baselat; + double baselon; + MaidenheadLocator.LatLonFromLoc(((string)e.UserState).Substring(0, 6), PositionInRectangle.BottomLeft, out baselat, out baselon); + List l = new List(); + l.Add(new PointLatLng((decimal)baselat, (decimal)baselon)); + l.Add(new PointLatLng((decimal)(baselat + 1 / 24.0), (decimal)baselon)); + l.Add(new PointLatLng((decimal)(baselat + 1 / 24.0), (decimal)(baselon + 2 / 24.0))); + l.Add(new PointLatLng((decimal)baselat, (decimal)(baselon + 2 / 24.0))); + GMapPolygon p = new GMapPolygon(l, (string)e.UserState); + p.Stroke = new Pen(Color.FromArgb(50, Color.Green)); + p.Fill = new SolidBrush(Color.FromArgb(50, Color.Green)); + SRTM1polygons.Polygons.Add(p); + } + else if (e.ProgressPercentage == -1) + { + // add missing tile to map polygons + double baselat; + double baselon; + MaidenheadLocator.LatLonFromLoc(((string)e.UserState).Substring(0, 6), PositionInRectangle.BottomLeft, out baselat, out baselon); + List l = new List(); + l.Add(new PointLatLng((decimal)baselat, (decimal)baselon)); + l.Add(new PointLatLng((decimal)(baselat + 1 / 24.0), (decimal)baselon)); + l.Add(new PointLatLng((decimal)(baselat + 1 / 24.0), (decimal)(baselon + 2 / 24.0))); + l.Add(new PointLatLng((decimal)baselat, (decimal)(baselon + 2 / 24.0))); + GMapPolygon p = new GMapPolygon(l, (string)e.UserState); + p.Stroke = new Pen(Color.FromArgb(50, Color.Red)); + p.Fill = new SolidBrush(Color.FromArgb(50, Color.Red)); + SRTM1polygons.Polygons.Add(p); + } + else + { + Say((string)e.UserState); + } + } + + private void bw_SRTM1_MapUpdater_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + } + + private void tab_Options_SRTM1_Enter(object sender, EventArgs e) + { + // clear map polygons + SRTM1polygons.Clear(); + // add coverage to map polygons + List cl = new List(); + cl.Add(new PointLatLng(Properties.Settings.Default.MinLat, Properties.Settings.Default.MinLon)); + cl.Add(new PointLatLng(Properties.Settings.Default.MinLat, Properties.Settings.Default.MaxLon)); + cl.Add(new PointLatLng(Properties.Settings.Default.MaxLat, Properties.Settings.Default.MaxLon)); + cl.Add(new PointLatLng(Properties.Settings.Default.MaxLat, Properties.Settings.Default.MinLon)); + GMapPolygon c = new GMapPolygon(cl, "Coverage"); + c.Stroke = new Pen(Color.FromArgb(255, Color.Magenta), 3); + c.Fill = new SolidBrush(Color.FromArgb(0, Color.Magenta)); + SRTM1polygons.Polygons.Add(c); + // zoom the map initally + gm_Options_SRTM1.SetZoomToFitRect(RectLatLng.FromLTRB(Properties.Settings.Default.MinLon, Properties.Settings.Default.MaxLat, Properties.Settings.Default.MaxLon, Properties.Settings.Default.MinLat)); + // start map updater + if (!bw_SRTM1_MapUpdater.IsBusy) + bw_SRTM1_MapUpdater.RunWorkerAsync(); + // zoom the map + gm_Options_Coverage.SetZoomToFitRect(RectLatLng.FromLTRB(Properties.Settings.Default.MinLon - 1, Properties.Settings.Default.MaxLat + 1, Properties.Settings.Default.MaxLon + 1, Properties.Settings.Default.MinLat - 1)); + } + + private void tab_Options_SRTM1_Leave(object sender, EventArgs e) + { + // stop map updater + bw_SRTM1_MapUpdater.CancelAsync(); + // clear map polygons + SRTM1polygons.Clear(); + // do garbage collection + GC.Collect(); + Say(""); + } + + private void btn_Options_SRTM1_Copyright_Click(object sender, EventArgs e) + { + ElevationCopyrightDlg Dlg = new ElevationCopyrightDlg(); + Dlg.Text = "SRTM1 Copyright Information"; + Dlg.rtb_Copyright.Text = Properties.Settings.Default.Elevation_SRTM1_Copyright; + Dlg.ShowDialog(); + } + + #endregion + + #region tab_Options_Path + + private void tab_Options_Path_Enter(object sender, EventArgs e) + { + dgv_BandSettings.DefaultCellStyle.Font = new Font(FontFamily.GenericMonospace, 8, FontStyle.Regular); + tb_Options_Path_StepWidth.Text = ElevationData.Database.GetDefaultStepWidth(GetElevationModel()).ToString("F0"); + } + + private void tab_Options_Path_Validating(object sender, CancelEventArgs e) + { + // include range checking here! + } + + + private void tab_Options_Path_Leave(object sender, EventArgs e) + { + // validate settings + + } + + #endregion + + #region tab_Options_Planes + + private void tab_Options_Planes_Enter(object sender, EventArgs e) + { + // set initial settings for planes tab + cb_Options_PlaneFeed1.DisplayMember = "Name"; + cb_Options_PlaneFeed2.DisplayMember = "Name"; + cb_Options_PlaneFeed3.DisplayMember = "Name"; + cb_Options_PlaneFeed1.Items.Clear(); + cb_Options_PlaneFeed2.Items.Clear(); + cb_Options_PlaneFeed3.Items.Clear(); + cb_Options_PlaneFeed1.Items.Add("[none]"); + cb_Options_PlaneFeed2.Items.Add("[none]"); + cb_Options_PlaneFeed3.Items.Add("[none]"); + ArrayList feeds = new PlaneFeedEnumeration().EnumFeeds(); + foreach (PlaneFeed feed in feeds) + { + cb_Options_PlaneFeed1.Items.Add(feed); + cb_Options_PlaneFeed2.Items.Add(feed); + cb_Options_PlaneFeed3.Items.Add(feed); + } + cb_Options_PlaneFeed1.SelectedIndex = cb_Options_PlaneFeed1.FindStringExact(Properties.Settings.Default.Planes_PlaneFeed1); + cb_Options_PlaneFeed2.SelectedIndex = cb_Options_PlaneFeed1.FindStringExact(Properties.Settings.Default.Planes_PlaneFeed2); + cb_Options_PlaneFeed3.SelectedIndex = cb_Options_PlaneFeed1.FindStringExact(Properties.Settings.Default.Planes_PlaneFeed3); + // fill planes filter dropdown + string[] cats = PlaneCategories.GetStringValues(); + foreach (string cat in cats) + cb_Options_Planes_Filter_Min_Cat.Items.Add(cat); + try + { + cb_Options_Planes_Filter_Min_Cat.SelectedItem = PlaneCategories.GetStringValue(Properties.Settings.Default.Planes_Filter_Min_Category); + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + + private void tab_Options_Planes_Validating(object sender, CancelEventArgs e) + { + if (tb_Options_Planes_MaxAlt.Value < tb_Options_Planes_MinAlt.Value) + { + MessageBox.Show("MaxAlt must be greater than MinAlt.", "Parameter Error"); + e.Cancel = true; + } + } + + private void cb_Options_PlaneFeed1_SelectedIndexChanged(object sender, EventArgs e) + { + if ((cb_Options_PlaneFeed1.SelectedItem == null) || (cb_Options_PlaneFeed1.GetItemText(cb_Options_PlaneFeed1.SelectedItem) == "[none]")) + { + btn_Options_PlaneFeed1_Settings.Enabled = false; + btn_Options_PlaneFeed1_Import.Enabled = false; + btn_Options_PlaneFeed1_Export.Enabled = false; + Properties.Settings.Default.Planes_PlaneFeed1 = "[none]"; + return; + } + PlaneFeed feed = (PlaneFeed)cb_Options_PlaneFeed1.SelectedItem; + // show disclaimer if necessary + if (!String.IsNullOrEmpty(feed.Disclaimer) && (String.IsNullOrEmpty(feed.DisclaimerAccepted))) + { + PlaneFeedDisclaimerDlg Dlg = new PlaneFeedDisclaimerDlg(); + Dlg.tb_DisclaimerText.Text = feed.Disclaimer; + if (Dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) + { + // making a unique ID for confirmation + string ID = ""; + try + { + ID = (string)Registry.GetValue("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion", "ProductId", ""); + } + catch + { + ID = "Key not found!"; + } + ID = ID + "," + DateTime.UtcNow.ToString("u"); + ID = ID + "," + System.Security.Principal.WindowsIdentity.GetCurrent().Name; + feed.DisclaimerAccepted = ID; + } + } + btn_Options_PlaneFeed1_Settings.Enabled = feed.HasSettings; + btn_Options_PlaneFeed1_Import.Enabled = feed.CanImport; + btn_Options_PlaneFeed1_Export.Enabled = feed.CanExport; + Properties.Settings.Default.Planes_PlaneFeed1 = feed.Name; + } + + private void cb_Options_PlaneFeed2_SelectedIndexChanged(object sender, EventArgs e) + { + if ((cb_Options_PlaneFeed2.SelectedItem == null) || (cb_Options_PlaneFeed2.GetItemText(cb_Options_PlaneFeed2.SelectedItem) == "[none]")) + { + btn_Options_PlaneFeed2_Settings.Enabled = false; + btn_Options_PlaneFeed2_Import.Enabled = false; + btn_Options_PlaneFeed2_Export.Enabled = false; + Properties.Settings.Default.Planes_PlaneFeed2 = "[none]"; + return; + } + PlaneFeed feed = (PlaneFeed)cb_Options_PlaneFeed2.SelectedItem; + // show disclaimer if necessary + if (!String.IsNullOrEmpty(feed.Disclaimer) && (String.IsNullOrEmpty(feed.DisclaimerAccepted))) + { + PlaneFeedDisclaimerDlg Dlg = new PlaneFeedDisclaimerDlg(); + Dlg.tb_DisclaimerText.Text = feed.Disclaimer; + if (Dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) + { + // making a unique ID for confirmation + string ID = ""; + try + { + ID = (string)Registry.GetValue("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion", "ProductId", ""); + } + catch + { + ID = "Key not found!"; + } + ID = ID + "," + DateTime.UtcNow.ToString("u"); + ID = ID + "," + System.Security.Principal.WindowsIdentity.GetCurrent().Name; + feed.DisclaimerAccepted = ID; + } + } + btn_Options_PlaneFeed2_Settings.Enabled = feed.HasSettings; + btn_Options_PlaneFeed2_Import.Enabled = feed.CanImport; + btn_Options_PlaneFeed2_Export.Enabled = feed.CanExport; + Properties.Settings.Default.Planes_PlaneFeed2 = feed.Name; + } + + private void cb_Options_PlaneFeed3_SelectedIndexChanged(object sender, EventArgs e) + { + if ((cb_Options_PlaneFeed3.SelectedItem == null) || (cb_Options_PlaneFeed3.GetItemText(cb_Options_PlaneFeed3.SelectedItem) == "[none]")) + { + btn_Options_PlaneFeed3_Settings.Enabled = false; + btn_Options_PlaneFeed3_Import.Enabled = false; + btn_Options_PlaneFeed3_Export.Enabled = false; + Properties.Settings.Default.Planes_PlaneFeed3 = "[none]"; + return; + } + PlaneFeed feed = (PlaneFeed)cb_Options_PlaneFeed3.SelectedItem; + // show disclaimer if necessary + if (!String.IsNullOrEmpty(feed.Disclaimer) && (String.IsNullOrEmpty(feed.DisclaimerAccepted))) + { + PlaneFeedDisclaimerDlg Dlg = new PlaneFeedDisclaimerDlg(); + Dlg.tb_DisclaimerText.Text = feed.Disclaimer; + if (Dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) + { + // making a unique ID for confirmation + string ID = ""; + try + { + ID = (string)Registry.GetValue("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion", "ProductId", ""); + } + catch + { + ID = "Key not found!"; + } + ID = ID + "," + DateTime.UtcNow.ToString("u"); + ID = ID + "," + System.Security.Principal.WindowsIdentity.GetCurrent().Name; + feed.DisclaimerAccepted = ID; + } + } + btn_Options_PlaneFeed3_Settings.Enabled = feed.HasSettings; + btn_Options_PlaneFeed3_Import.Enabled = feed.CanImport; + btn_Options_PlaneFeed3_Export.Enabled = feed.CanExport; + Properties.Settings.Default.Planes_PlaneFeed3 = feed.Name; + } + + private void btn_Options_PlaneFeed1_Settings_Click(object sender, EventArgs e) + { + try + { + PlaneFeedSettingsDlg Dlg = new PlaneFeedSettingsDlg(); + Dlg.lbl_Info.Text = ((PlaneFeed)cb_Options_PlaneFeed1.SelectedItem).Info; + Dlg.pg_Main.SelectedObject = ((PlaneFeed)cb_Options_PlaneFeed1.SelectedItem).GetFeedSettings(); + if (Dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) + { + } + } + catch + { + // do nothing + } + } + + private void btn_Options_PlaneFeed2_Settings_Click(object sender, EventArgs e) + { + try + { + PlaneFeedSettingsDlg Dlg = new PlaneFeedSettingsDlg(); + Dlg.lbl_Info.Text = ((PlaneFeed)cb_Options_PlaneFeed2.SelectedItem).Info; + Dlg.pg_Main.SelectedObject = ((PlaneFeed)cb_Options_PlaneFeed2.SelectedItem).GetFeedSettings(); + if (Dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) + { + } + } + catch + { + // do nothing + } + } + + private void btn_Options_PlaneFeed3_Settings_Click(object sender, EventArgs e) + { + try + { + PlaneFeedSettingsDlg Dlg = new PlaneFeedSettingsDlg(); + Dlg.lbl_Info.Text = ((PlaneFeed)cb_Options_PlaneFeed3.SelectedItem).Info; + Dlg.pg_Main.SelectedObject = ((PlaneFeed)cb_Options_PlaneFeed3.SelectedItem).GetFeedSettings(); + if (Dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) + { + } + } + catch + { + // do nothing + } + } + + private void btn_Options_PlaneFeed1_Import_Click(object sender, EventArgs e) + { + try + { + PlaneFeed feed = (PlaneFeed)cb_Options_PlaneFeed1.SelectedItem; + feed.Import(); + } + catch (Exception ex) + { + // do nothing + } + } + + private void btn_Options_PlaneFeed2_Import_Click(object sender, EventArgs e) + { + try + { + PlaneFeed feed = (PlaneFeed)cb_Options_PlaneFeed2.SelectedItem; + feed.Import(); + } + catch (Exception ex) + { + // do nothing + } + } + + private void btn_Options_PlaneFeed3_Import_Click(object sender, EventArgs e) + { + try + { + PlaneFeed feed = (PlaneFeed)cb_Options_PlaneFeed3.SelectedItem; + feed.Import(); + } + catch (Exception ex) + { + // do nothing + } + } + + private void btn_Options_PlaneFeed1_Export_Click(object sender, EventArgs e) + { + try + { + PlaneFeed feed = (PlaneFeed)cb_Options_PlaneFeed1.SelectedItem; + feed.Export(); + } + catch (Exception ex) + { + // do nothing + } + } + + private void btn_Options_PlaneFeed2_Export_Click(object sender, EventArgs e) + { + try + { + PlaneFeed feed = (PlaneFeed)cb_Options_PlaneFeed2.SelectedItem; + feed.Export(); + } + catch (Exception ex) + { + // do nothing + } + + } + + private void btn_Options_PlaneFeed3_Export_Click(object sender, EventArgs e) + { + try + { + PlaneFeed feed = (PlaneFeed)cb_Options_PlaneFeed3.SelectedItem; + feed.Export(); + } + catch (Exception ex) + { + // do nothing + } + + } + + private void cb_Options_Planes_Filter_Min_Cat_SelectedIndexChanged(object sender, EventArgs e) + { + try + { + Properties.Settings.Default.Planes_Filter_Min_Category = PlaneCategories.ParseStringValue((string) cb_Options_Planes_Filter_Min_Cat.SelectedItem); + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + + #endregion + + #region tab_Options_Alarm + + private void tab_Options_Alarm_Enter(object sender, EventArgs e) + { + + } + + #endregion + + #region tab_Options_Network + + private void tab_Options_Network_Enter(object sender, EventArgs e) + { + } + + private void tab_Options_Network_Validating(object sender, CancelEventArgs e) + { + if (tb_Options_Server_Port.Value == tb_Options_Webserver_Port.Value) + { + MessageBox.Show("UDP Server and HTTP Server must have different port numbers.", "Parameter Error"); + e.Cancel = true; + } + } + + #endregion + + #region tab_Options_SpecLab + + private void tab_Options_SpecLab_Enter(object sender, EventArgs e) + { + } + + private void tab_Options_SpecLab_Validating(object sender, CancelEventArgs e) + { + if (tb_Options_SpecLab_F1.Value > tb_Options_SpecLab_F2.Value) + { + MessageBox.Show("F1 must be lesser than F2.", "Parameter Error"); + e.Cancel = true; + } + } + #endregion + + #region tab_Options_Track + + private void tab_Options_Track_Enter(object sender, EventArgs e) + { + + } + + private void tc_Track_Validating(object sender, CancelEventArgs e) + { + if (tb_Options_Track_UDP_WinTest_Port.Value == tb_Options_Track_UDP_AirScout_Port.Value) + { + MessageBox.Show("Win-Test port and AirScout port must have different values.", "Parameter Error"); + e.Cancel = true; + } + List baudrates = new List(new int[] { 50, 100, 110, 150, 300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200 }); + if (!baudrates.Contains(tb_Options_Track_Serial_Baudrate.Value)) + { + string s = ""; + foreach (int b in baudrates) + s = s + b.ToString() + ", "; + s = s.Trim().TrimEnd(','); + MessageBox.Show("This is not a valid baudrate: " + tb_Options_Track_Serial_Baudrate.Text + "\nValid baudrates are:\n\n" + s, "Parameter Error"); + e.Cancel = true; + } + } + + #endregion + + #region tab_Options_Info + + private void tab_Options_Info_Enter(object sender, EventArgs e) + { + // populate link labels + lbl_Options_Version.Text = "Version: " + Application.ProductVersion; + lbl_Options_Map.Text = "GMap.NET Copyright (c) 2008 - 2011 Universe"; + lbl_Options_Map.Links.Add(0, 8, "http://greatmaps.codeplex.com/"); + lbl_Options_Spherical.Text = "http://www.movable-type.co.uk/scripts/latlong.html"; + lbl_Options_Spherical.Links.Add(0, lbl_Options_Spherical.Text.Length - 1, "http://www.movable-type.co.uk/scripts/latlong.html"); + lbl_Options_Elevation_GLOBE.Text = "1km based Elevation Data from GLOBE - Project"; + lbl_Options_Elevation_GLOBE.Links.Add(30, 5, "http://www.ngdc.noaa.gov/mgg/topo/globe.html"); + lbl_Options_Elevation_SRTM3.Text = "3arsec (90m x 90m) Elevation Data from SRTM - Project"; + lbl_Options_Elevation_SRTM3.Links.Add(40, 14, "http://srtm.usgs.gov/"); + lbl_Options_Elevation_SRTM1.Text = "1arsec (30m x 30m) Elevation Data from SRTM - Project and ASTER"; + lbl_Options_Elevation_SRTM1.Links.Add(40, 14, "http://srtm.usgs.gov"); + lbl_Options_Elevation_SRTM1.Links.Add(58, 6, "http://asterweb.jpl.nasa.gov/index.asp"); + } + + + private void lbl_Options_Map_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + System.Diagnostics.Process.Start(e.Link.LinkData.ToString()); + } + + private void lbl_Options_ElevationSource_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + System.Diagnostics.Process.Start(e.Link.LinkData.ToString()); + } + + private void lbl_Options_Spherical_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + System.Diagnostics.Process.Start(e.Link.LinkData.ToString()); + } + + + + private void lbl_Options_ElevationDataHowTo_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + System.Diagnostics.Process.Start(e.Link.LinkData.ToString()); + } + + private void lbl_Options_Elevation_SRTM3_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + System.Diagnostics.Process.Start(e.Link.LinkData.ToString()); + } + + private void lbl_Options_Elevation_SRTM1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + System.Diagnostics.Process.Start(e.Link.LinkData.ToString()); + } + + private void btn_Options_License_Click(object sender, EventArgs e) + { + LicenseDlg Dlg = new LicenseDlg(); + Dlg.ShowDialog(); + } + + + private void pb_Donate_Click(object sender, EventArgs e) + { + try + { + System.Diagnostics.Process.Start(Properties.Settings.Default.Donate_URL); + } + catch + { + } + + } + + #endregion + + + private void OptionsDlg_FormClosing(object sender, FormClosingEventArgs e) + { + bw_GLOBE_MapUpdater.CancelAsync(); + bw_SRTM3_MapUpdater.CancelAsync(); + bw_SRTM1_MapUpdater.CancelAsync(); + bw_SFTP.CancelAsync(); + // do garbage collection + GC.Collect(); + } + + + private void OptionsDlg_FormClosed(object sender, FormClosedEventArgs e) + { + } + + private void btn_Options_Import_Callsigns_Click(object sender, EventArgs e) + { + OpenFileDialog Dlg = new OpenFileDialog(); + Dlg.DefaultExt = ".txt"; + Dlg.CheckFileExists = true; + Dlg.Multiselect = false; + Dlg.Title = "Import Callsign Database as TXT-File"; + if (Dlg.ShowDialog() == DialogResult.OK) + { + // import old callsign database + using (StreamReader sr = new StreamReader(Dlg.FileName)) + { + int count = 0; + string s = sr.ReadToEnd(); + List l = StationData.Database.LocationFromTXT(s); + foreach (LocationDesignator ld in l) + { + int i = StationData.Database.LocationInsertOrUpdateIfNewer(ld); + if (i > 0) + count += i; + } + MessageBox.Show("The callsign database was imported succuessfully.\n" + count.ToString() + " entries were inserted/updated.", "Import Callsign Database as TXT-File", MessageBoxButtons.OK); + } + } + } + + #region SFTP + + private void bw_SFTP_DoWork(object sender, DoWorkEventArgs e) + { + Log.WriteMessage("Started."); + try + { + SFTPDoWorkEventArgs args = (SFTPDoWorkEventArgs)e.Argument; + bw_SFTP.ReportProgress(0, "Connecting FTP - Server..."); + // generate file with user info + string locfilename = ParentDlg.TmpDirectory + Path.DirectorySeparatorChar + Properties.Settings.Default.MyCall.Replace("/", "_") + ".loc"; + string qrvfilename = ParentDlg.TmpDirectory + Path.DirectorySeparatorChar + Properties.Settings.Default.MyCall.Replace("/", "_") + ".qrv"; + JsonSerializerSettings settings = new JsonSerializerSettings(); + settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; + settings.FloatFormatHandling = FloatFormatHandling.String; + settings.Formatting = Newtonsoft.Json.Formatting.Indented; + string json = JsonConvert.SerializeObject(args.ld, settings); + using (StreamWriter sw = new StreamWriter(locfilename)) + { + sw.WriteLine(json); + } + json = JsonConvert.SerializeObject(args.qrvs, settings); + using (StreamWriter sw = new StreamWriter(qrvfilename)) + { + sw.WriteLine(json); + } + using (var sftp = new SftpClient(Password.GetSFTPURL(Properties.Settings.Default.Password), Password.GetSFTPUser(Properties.Settings.Default.Password), Password.GetSFTPPassword(Properties.Settings.Default.Password))) + { + var stream = File.OpenRead(locfilename); + sftp.Connect(); + sftp.UploadFile(stream, Path.GetFileName(locfilename), true); + sftp.Disconnect(); + stream.Close(); + stream = File.OpenRead(qrvfilename); + sftp.Connect(); + sftp.UploadFile(stream, Path.GetFileName(qrvfilename), true); + sftp.Disconnect(); + stream.Close(); + } + bw_SFTP.ReportProgress(100, "Location upload successful."); + } + catch (Exception ex) + { + Log.WriteMessage(ex.Message); + bw_SFTP.ReportProgress(-1, "Error while uploading location:" + ex.Message); + } + Log.WriteMessage("Finished."); + } + + private void bw_SFTP_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + try + { + Say((string)e.UserState); + } + catch + { + } + } + + private void bw_SFTP_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + + } + + + #endregion + + } + + public class SFTPDoWorkEventArgs + { + public LocationDesignator ld; + public List qrvs; + } + + + +} diff --git a/AirScout/OptionsDlg.resx b/AirScout/OptionsDlg.resx new file mode 100644 index 0000000..d3b44dd --- /dev/null +++ b/AirScout/OptionsDlg.resx @@ -0,0 +1,287 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + + + iVBORw0KGgoAAAANSUhEUgAAALMAAABOCAYAAABrGCc0AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + vAAADrwBlbxySQAAABJ0RVh0U29mdHdhcmUAR3JlZW5zaG90XlUIBQAADGdJREFUeF7tnOuPHlUdx5ca + 7Fvf+BcYNTFSLwVrotyMRI0EDCESTcRLqlVCvNAQXpDG9E2bSKppNAKbyIumNEssKVRWm0YBk5qspSlN + qwURS6AgFBpqu4K2sXucz9n5jr85O/Ncdp9n2zn+vsk3c+5zds5nfs+Z57ITweXKRA6zKxs5zK5s5DC7 + spHD7MpGDrMrGznMrmzkMLuykcPsykYOsysbOcyubOQwu7KRw+zKRg6zKxvlBfPbr4Rw/2r3MM5IecBs + Id5/VwgvPRbC7PMhnP+3u80n/xjC0Z9nBXUeMLMYT2+cB/jsm+5hzc2fAdDdh7lYhLlX985HZ/fS3HGg + uw0zF5+XSyKyezTuMNDdhLmMInMvbAvh1GH3qN1RoLsbmR//8nxUdo/ccy9OzV/jc6fnjx1RN2EmKh/5 + UZg78eTIvO2Bn4aJ1d+q+V1XfjvcvXlTePP53Y19lsvHDu4ID0092Fg3LncxOncT5l2Xx3cvePAblYEW + gN9//e3hmq/eFQ3MlH3htrsb+yyHucmYx+N7ftFYPzbv+Ux5sbuj7sFMVOZCF9uMub9Pj8zAC7gRmrKM + iCygD/7+f+Wqo61t32YiK+1ihE/q+o2j86d9NWY6r5H4uXvnt3Edi86dhTmarcbLj4zEABOheWaqVq6I + /eOtWxaUyQBHBFU9kFFOlP/anT+stQPCXuNEOIs6jrYOcw7mZ8fE3IjpvBftAuYYKAgYDvOYZWHGRRTh + gWUpFjjAFxfU1Ak4juQFEm0f3bm12gZQFkEt2gA+ecwWBbhpb8ehr8ahXueJW5qinjLSakM9wKqMedhx + 481W9Fuq+QRV19ZhHrdSmDERumFhBrXgA5Ba3QvbajADK2ngjZEwaSOgBLzAtGUxgpdl1pRTT5RVmT03 + +dpNV7bRq4DttyjzNmcZkWWHedziAu/+5EKzEEBdLMqwFmgRxqROkQ+QatCbNjXoTB9AUxs7DnmOwK6o + LmsMrH08UZy8zk8f6rDGjTCX/YbysQeqaLzgmjrMY1YbzKXjovDwwrsdwF0sVj/X4DPlgod68jVoTTu9 + 9Mc97ZHJmMaqT8vIC2LGAuwauGU/zStuX4q8zk9b0tYx4pf9epqHO77HwheyuE4N17CywzxmcYF5a26E + BhCs/Kkdq8Om73++Kt+16epYvnPDp2P+szd9sWr77OSaWAac9HviJ1cuaJOWpXn6CW7GS8fVODr/N9fe + XJUxN/UZuR3mMYsLvPODI7PAajMAqa2FDqCw8mqnmyACV/ZLywSqyhSBsfpoXoxPf/o0nV9tqFPfkdlh + XgaNEGgiGxEytQAKD72v1p4y6gUfIMbIXbajH/XA2K/M9td5dR5sz6O5pOcH6KZ5LtkdAxl1F2YWzz0+ + O8zLKC5200OLe+kWyHw7sUPqNsw8jbtH7w5GZdRdmBEXnbeY3KOzQO7Y1z9Rt2FGxcXn/eT440z30iyQ + O7a9kLoPM2IR+CCAH2a6hzYfpnQdZJQHzIjFUJTmFxNNPwdy1w3MXDfevUAd3FpY5QOzFqKE2j2AeecC + dTgaW+UDs+v/Xg6zKxs5zK5s5DC7spHD7MpGDrMrGw0M84c/ccfYLF166apwycQtlVes/EGYuP7+8M5b + tld+x1XbYzn1773kxvClFWvC9hXvrpkyOw5eLl12x6/C8ROzZW78uvV3b0Tftu9Ulf7c7uPhPZNHK39g + 475o5oY/dPt0dD+deO31MDMzE/8BDSaNnz36TNni4tKSYF635uvhwBUfDw+vuS6avOr4zi3f2eVL65j0 + d25eV+svS8AMqBhoJ9YeaDX9APfQxESjqQN2gb9cAhYgiP8qlv+DbFXkZ986G86e+09liTR1tT5l+16y + MAtoYLZADwVzcU7AZf1WrlxZs8CmDpO/mLQomIH2lY99tNHADbhvbL280YBtx8KSYO4HMr7qxsmeMGMB + vdwwE7niJ5H6Flr5ocSGbU+Fazf8Okw+eCDctPm3MQ+sWx/+Uyyn77qf7Yttie60ofzw8X/EsiZZkC3M + i4nMzLsJYnl29p+xHTer2vHjgHjzjkjVDZ7c1INoaJh7gSy/9pUrGkGWn958XU+Ym+C1BmTmgQcB+oJs + MwqA+ZEpfy8/5adMWyXgZfsEuIBNWqAJ5o2/PBTLMek2tcHcFpkFcgozUXbVqlULALYWzIibUO05Cmh+ + XPu99XdGE8UpJ31k5lBVThuVk56eno7n58bgJuGHxJg8bagfREPD3ASvNSDLKcTWRO8mmFnsJoBl6gXy + IDBjonMl/gcxX3VU1CwEaERAQFL0ZLFIC0BBtmvmxVhP1GxSBXMhoI3/O6OI0vfufS6CyZikefXhyLgC + mwXW9oNx4rUo69oEwFsOnKzZQn3r9j9XZhzM3O38gbQfyDiNwMCqOsBj3owDoMCpNEe1VZ4bnSPQUkca + cHl1kNU/PW+bhoIZeP76jbURVI79YD5536fC6Ue+G48WZPI2OkvALGiBRgtPmovPAlO3fv2OGJ05sg9n + LqempuIWhznoKJh5IEzFxeRHoPzHS84BOERAQaQy8sxD0VH1uGk/S71gZntAOxaM+QPmU395vQKYv4s8 + bcirL+WCmDJcvfwmOn76bPTsufPxuOfYmaoMMwcdORdz5ogl5icoZUCyURLQUqgAzvYRwIrIpIFcZRoX + W1h1fhuBFfnpN6iGgpmHPF4u9EedO3I4QvSvJ58Ib790MOZJU0b6/JkzcdKzr74c/wMQbfimFse3ZiYX + wMzkFX3po4sfF6RYZC0IC8uCK89x//6/xXnRjznSfu8N17bCDKxETb7HK0Dpw5HIa2HWwnOkTFa5lYBE + zF1jY0V8jrY/8xfsipyk1Q7TpkmCFpgxMKucPpg058I2Ldl9MmnByF4VwARgCrOAVV+g1FYBcwMAuMbk + yNrYPG1I05exKCPNGqvdoBoa5tq+yaZL+CTyQEsE1kWIi1zsJakjYuvdDYnIzMsvi0gf/iD1BQJuDMo4 + D/CyUKRpc8+m+Qcv9qBcMOqI2G0wUw8kRAaOAMRYpAHKQod3FTcP5aRtJE9lYUaKwpgxkB2T8+oVgTbk + VS/bvqm0P2ZbkW43MHNNzfYHS0ADjNoqSIqYstZCIm9hBkyuPf2AkGuodumRel4dWTOVk9YYiHLLWD8N + vc3ghBInIvqyeKSpI80kmGj8Y8zXC5kk7XBTZBbMbCGAE7OV4Aik2lqonDKbB17Gu+e+P1Qg49qe2cgC + AywsNGngAnZEmjK7f5YBL1UKM+PSlnKuB3XqqzqZc+iG4bxcR6Ajz9yapIc8gJYUlTkfR/JIcDEHO0cA + AkYLDucWpLKgk1hvW8+ac2NwU2hvLF6I8NQTqZEisS1jfObCEVaoZ6z0vG0aGmb2o7LeX8Yq46Uds2cm + KmO915ymm2Dm3QwtLmDbI1sQ0lh9eQBsskAm3fZuhqIgoCGBm0KmNhxtJFU/K8osKChGO/P2ks2Tlm3e + qqlM0jsVPOBVEJfbDeW1tWBeAE0aS4CWwkragoot7IjoqjrAow9HbQ1IK0rTBtDVTjAr+gO9+pMGcNL0 + sfPqpaFgxgBrH/iarAdA+9CXmolqTEkwA6seBJsMVIO8k0F9rw9NWFDABF7EkTyLzlGmHZFRdUj5VMCs + qL5kDfD+qmAW0PiGLTMxwuvYZisiIuAoenNuRWzZQgXYrKHqLMBEU/qSBkqiL20oU+QFYs6nGwmAMX0U + zamPc0puojYNDbOicxPEcj+Q0w9OJME8CND06wUzdRfiE0BF8SZ4epkthrYZ6dG2ScW5BDPp1HpP+SPr + Hl1gKyAWPAAYlQCtVwfgAliVA6YEnOSxoGRsgKZM0FNHnvHpgxiXet00bDXYrmicfhoaZgzQbC2aQKac + Bzu2EU0g2/eXZcnCnEINJBzJ2/q272bYNssNc5xr+dDXZCBju8KRh0zSynMEWn1KCMSkaUd9KspS2w9G + 2kDGTQIwoiNQAyBAARtQATllisg18C8CLQpmGagFNse0nj8WeHt9LwNLXCCAHoeXS4K5zcAMpI/95miE + lgc8HlhJA6Jgpl4gkydNvUQkRim41k0AW7dJEZLoq2iNgRgTTS8miKUlwTwq5yRF0F620Rgrz9Fa9baP + xNtuqAliuQlg60EE2Lzsy4Ps4y+UBobZ5brY5TC7spHD7MpGDrMrGznMrmzkMLuykcPsykYOsysbOcyu + bOQwu7KRw+zKRg6zKxs5zK5s5DC7spHD7MpGDrMrGznMrkwUwn8BkAmqaXV391cAAAAASUVORK5CYII= + + + + + iVBORw0KGgoAAAANSUhEUgAAALMAAABOCAYAAABrGCc0AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + vAAADrwBlbxySQAAABJ0RVh0U29mdHdhcmUAR3JlZW5zaG90XlUIBQAADGdJREFUeF7tnOuPHlUdx5ca + 7Fvf+BcYNTFSLwVrotyMRI0EDCESTcRLqlVCvNAQXpDG9E2bSKppNAKbyIumNEssKVRWm0YBk5qspSlN + qwURS6AgFBpqu4K2sXucz9n5jr85O/Ncdp9n2zn+vsk3c+5zds5nfs+Z57ITweXKRA6zKxs5zK5s5DC7 + spHD7MpGDrMrGznMrmzkMLuykcPsykYOsysbOcyubOQwu7KRw+zKRg6zKxvlBfPbr4Rw/2r3MM5IecBs + Id5/VwgvPRbC7PMhnP+3u80n/xjC0Z9nBXUeMLMYT2+cB/jsm+5hzc2fAdDdh7lYhLlX985HZ/fS3HGg + uw0zF5+XSyKyezTuMNDdhLmMInMvbAvh1GH3qN1RoLsbmR//8nxUdo/ccy9OzV/jc6fnjx1RN2EmKh/5 + UZg78eTIvO2Bn4aJ1d+q+V1XfjvcvXlTePP53Y19lsvHDu4ID0092Fg3LncxOncT5l2Xx3cvePAblYEW + gN9//e3hmq/eFQ3MlH3htrsb+yyHucmYx+N7ftFYPzbv+Ux5sbuj7sFMVOZCF9uMub9Pj8zAC7gRmrKM + iCygD/7+f+Wqo61t32YiK+1ihE/q+o2j86d9NWY6r5H4uXvnt3Edi86dhTmarcbLj4zEABOheWaqVq6I + /eOtWxaUyQBHBFU9kFFOlP/anT+stQPCXuNEOIs6jrYOcw7mZ8fE3IjpvBftAuYYKAgYDvOYZWHGRRTh + gWUpFjjAFxfU1Ak4juQFEm0f3bm12gZQFkEt2gA+ecwWBbhpb8ehr8ahXueJW5qinjLSakM9wKqMedhx + 481W9Fuq+QRV19ZhHrdSmDERumFhBrXgA5Ba3QvbajADK2ngjZEwaSOgBLzAtGUxgpdl1pRTT5RVmT03 + +dpNV7bRq4DttyjzNmcZkWWHedziAu/+5EKzEEBdLMqwFmgRxqROkQ+QatCbNjXoTB9AUxs7DnmOwK6o + LmsMrH08UZy8zk8f6rDGjTCX/YbysQeqaLzgmjrMY1YbzKXjovDwwrsdwF0sVj/X4DPlgod68jVoTTu9 + 9Mc97ZHJmMaqT8vIC2LGAuwauGU/zStuX4q8zk9b0tYx4pf9epqHO77HwheyuE4N17CywzxmcYF5a26E + BhCs/Kkdq8Om73++Kt+16epYvnPDp2P+szd9sWr77OSaWAac9HviJ1cuaJOWpXn6CW7GS8fVODr/N9fe + XJUxN/UZuR3mMYsLvPODI7PAajMAqa2FDqCw8mqnmyACV/ZLywSqyhSBsfpoXoxPf/o0nV9tqFPfkdlh + XgaNEGgiGxEytQAKD72v1p4y6gUfIMbIXbajH/XA2K/M9td5dR5sz6O5pOcH6KZ5LtkdAxl1F2YWzz0+ + O8zLKC5200OLe+kWyHw7sUPqNsw8jbtH7w5GZdRdmBEXnbeY3KOzQO7Y1z9Rt2FGxcXn/eT440z30iyQ + O7a9kLoPM2IR+CCAH2a6hzYfpnQdZJQHzIjFUJTmFxNNPwdy1w3MXDfevUAd3FpY5QOzFqKE2j2AeecC + dTgaW+UDs+v/Xg6zKxs5zK5s5DC7spHD7MpGDrMrGw0M84c/ccfYLF166apwycQtlVes/EGYuP7+8M5b + tld+x1XbYzn1773kxvClFWvC9hXvrpkyOw5eLl12x6/C8ROzZW78uvV3b0Tftu9Ulf7c7uPhPZNHK39g + 475o5oY/dPt0dD+deO31MDMzE/8BDSaNnz36TNni4tKSYF635uvhwBUfDw+vuS6avOr4zi3f2eVL65j0 + d25eV+svS8AMqBhoJ9YeaDX9APfQxESjqQN2gb9cAhYgiP8qlv+DbFXkZ986G86e+09liTR1tT5l+16y + MAtoYLZADwVzcU7AZf1WrlxZs8CmDpO/mLQomIH2lY99tNHADbhvbL280YBtx8KSYO4HMr7qxsmeMGMB + vdwwE7niJ5H6Flr5ocSGbU+Fazf8Okw+eCDctPm3MQ+sWx/+Uyyn77qf7Yttie60ofzw8X/EsiZZkC3M + i4nMzLsJYnl29p+xHTer2vHjgHjzjkjVDZ7c1INoaJh7gSy/9pUrGkGWn958XU+Ym+C1BmTmgQcB+oJs + MwqA+ZEpfy8/5adMWyXgZfsEuIBNWqAJ5o2/PBTLMek2tcHcFpkFcgozUXbVqlULALYWzIibUO05Cmh+ + XPu99XdGE8UpJ31k5lBVThuVk56eno7n58bgJuGHxJg8bagfREPD3ASvNSDLKcTWRO8mmFnsJoBl6gXy + IDBjonMl/gcxX3VU1CwEaERAQFL0ZLFIC0BBtmvmxVhP1GxSBXMhoI3/O6OI0vfufS6CyZikefXhyLgC + mwXW9oNx4rUo69oEwFsOnKzZQn3r9j9XZhzM3O38gbQfyDiNwMCqOsBj3owDoMCpNEe1VZ4bnSPQUkca + cHl1kNU/PW+bhoIZeP76jbURVI79YD5536fC6Ue+G48WZPI2OkvALGiBRgtPmovPAlO3fv2OGJ05sg9n + LqempuIWhznoKJh5IEzFxeRHoPzHS84BOERAQaQy8sxD0VH1uGk/S71gZntAOxaM+QPmU395vQKYv4s8 + bcirL+WCmDJcvfwmOn76bPTsufPxuOfYmaoMMwcdORdz5ogl5icoZUCyURLQUqgAzvYRwIrIpIFcZRoX + W1h1fhuBFfnpN6iGgpmHPF4u9EedO3I4QvSvJ58Ib790MOZJU0b6/JkzcdKzr74c/wMQbfimFse3ZiYX + wMzkFX3po4sfF6RYZC0IC8uCK89x//6/xXnRjznSfu8N17bCDKxETb7HK0Dpw5HIa2HWwnOkTFa5lYBE + zF1jY0V8jrY/8xfsipyk1Q7TpkmCFpgxMKucPpg058I2Ldl9MmnByF4VwARgCrOAVV+g1FYBcwMAuMbk + yNrYPG1I05exKCPNGqvdoBoa5tq+yaZL+CTyQEsE1kWIi1zsJakjYuvdDYnIzMsvi0gf/iD1BQJuDMo4 + D/CyUKRpc8+m+Qcv9qBcMOqI2G0wUw8kRAaOAMRYpAHKQod3FTcP5aRtJE9lYUaKwpgxkB2T8+oVgTbk + VS/bvqm0P2ZbkW43MHNNzfYHS0ADjNoqSIqYstZCIm9hBkyuPf2AkGuodumRel4dWTOVk9YYiHLLWD8N + vc3ghBInIvqyeKSpI80kmGj8Y8zXC5kk7XBTZBbMbCGAE7OV4Aik2lqonDKbB17Gu+e+P1Qg49qe2cgC + AywsNGngAnZEmjK7f5YBL1UKM+PSlnKuB3XqqzqZc+iG4bxcR6Ajz9yapIc8gJYUlTkfR/JIcDEHO0cA + AkYLDucWpLKgk1hvW8+ac2NwU2hvLF6I8NQTqZEisS1jfObCEVaoZ6z0vG0aGmb2o7LeX8Yq46Uds2cm + KmO915ymm2Dm3QwtLmDbI1sQ0lh9eQBsskAm3fZuhqIgoCGBm0KmNhxtJFU/K8osKChGO/P2ks2Tlm3e + qqlM0jsVPOBVEJfbDeW1tWBeAE0aS4CWwkragoot7IjoqjrAow9HbQ1IK0rTBtDVTjAr+gO9+pMGcNL0 + sfPqpaFgxgBrH/iarAdA+9CXmolqTEkwA6seBJsMVIO8k0F9rw9NWFDABF7EkTyLzlGmHZFRdUj5VMCs + qL5kDfD+qmAW0PiGLTMxwuvYZisiIuAoenNuRWzZQgXYrKHqLMBEU/qSBkqiL20oU+QFYs6nGwmAMX0U + zamPc0puojYNDbOicxPEcj+Q0w9OJME8CND06wUzdRfiE0BF8SZ4epkthrYZ6dG2ScW5BDPp1HpP+SPr + Hl1gKyAWPAAYlQCtVwfgAliVA6YEnOSxoGRsgKZM0FNHnvHpgxiXet00bDXYrmicfhoaZgzQbC2aQKac + Bzu2EU0g2/eXZcnCnEINJBzJ2/q272bYNssNc5xr+dDXZCBju8KRh0zSynMEWn1KCMSkaUd9KspS2w9G + 2kDGTQIwoiNQAyBAARtQATllisg18C8CLQpmGagFNse0nj8WeHt9LwNLXCCAHoeXS4K5zcAMpI/95miE + lgc8HlhJA6Jgpl4gkydNvUQkRim41k0AW7dJEZLoq2iNgRgTTS8miKUlwTwq5yRF0F620Rgrz9Fa9baP + xNtuqAliuQlg60EE2Lzsy4Ps4y+UBobZ5brY5TC7spHD7MpGDrMrGznMrmzkMLuykcPsykYOsysbOcyu + bOQwu7KRw+zKRg6zKxs5zK5s5DC7spHD7MpGDrMrGznMrkwUwn8BkAmqaXV391cAAAAASUVORK5CYII= + + + + You can select different map provider from the list below. Depending on your selection some maps may not be available for your choosen area. Please note that the selection of maps is taken from Great Maps regardless of their legal status. Some of them might be copyrighted or otherwise restricted in use. You were asked to agree with the terms of use first. +Open Street Map is recommended as a default. + + + Information from callsign database and other sources are used to prefill fields. You can overwrite and complete entries here. Your local database is updated. If you want to share the information with the AirScout community please use the "Send Update!" buttons. + + + CAUTION: Running a web service is a potential security hole! You should activate this function only inside a private network. + +Depending on your user profile you will prompted several times by the Operating System on first run. Please accept all security queries with "Yes" or "Accept". +Otherwise the web service will not run properly. + + + AirScout can work as a server in a network. + +UDP Server: +You can ask for a path calculation between two stations and AirScout will return the planes near path and their portential for a reflection. + +HTTP Server: +You can ask for latest plane positions via simple http-request (e.g. from a web browser. The result is delivered as a JSON file which can used to run own services and calculations. + +See documentation for further details. + + + AirScout can get FFT data from Spectrum Lab software via Network. You must have Spectrum Lab software installed with default settings. Do not forget to activate http - server functionaltiy in Spectrum Lab. + + + 130, 17 + + + 353, 17 + + + 483, 17 + + + 706, 17 + + + 890, 17 + + + 989, 17 + + \ No newline at end of file diff --git a/AirScout/PathCalculator.cs b/AirScout/PathCalculator.cs new file mode 100644 index 0000000..15a38e9 --- /dev/null +++ b/AirScout/PathCalculator.cs @@ -0,0 +1,257 @@ + +using System; +using System.ComponentModel; +using System.Windows.Forms; +using System.Net; +using System.IO; +using System.Threading; +using System.Diagnostics; +using System.Collections; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Globalization; +using System.Drawing; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using ScoutBase.Core; +using ScoutBase.Stations; +using ScoutBase.Elevation; +using ScoutBase.Propagation; +using ScoutBase; +using System.Data.SQLite; + +namespace AirScout +{ + + public partial class MapDlg : Form + { + + #region PathCalculator + + // Background worker for path calculation + [DefaultPropertyAttribute("Name")] + public class PathCalculator : BackgroundWorker + { + ELEVATIONMODEL Model = ELEVATIONMODEL.NONE; + + string Name = ""; + double StepWidth = 1000; + + public PathCalculator(ELEVATIONMODEL model) : base() + { + this.WorkerReportsProgress = true; + this.WorkerSupportsCancellation = true; + this.Model = model; + } + + protected override void OnDoWork(DoWorkEventArgs e) + { + // get all parameters + BACKGROUNDUPDATERSTARTOPTIONS Options = (BACKGROUNDUPDATERSTARTOPTIONS)e.Argument; + // get update interval + int interval = (int)Properties.Settings.Default.Background_Update_Period * 60; + do + { + if (Properties.Settings.Default.Background_Calculations_Enable) + { + // get all parameters + // set name and stepwidth according to model + switch (Model) + { + case ELEVATIONMODEL.GLOBE: + Name = "GLOBE"; + StepWidth = ElevationData.Database.GetDefaultStepWidth(ELEVATIONMODEL.GLOBE); + break; + case ELEVATIONMODEL.SRTM3: + Name = "SRTM3"; + StepWidth = ElevationData.Database.GetDefaultStepWidth(ELEVATIONMODEL.SRTM3); + break; + case ELEVATIONMODEL.SRTM1: + Name = "SRTM1"; + StepWidth = ElevationData.Database.GetDefaultStepWidth(ELEVATIONMODEL.SRTM1); + break; + } + // name the thread for debugging + if (String.IsNullOrEmpty(Thread.CurrentThread.Name)) + Thread.CurrentThread.Name = Name + "_" + this.GetType().Name; + try + { + // iterate through all locations in the database and calculate the propagation path + // chek if database is ready first + while (ElevationData.Database.GetDBStatusBit(Model, DATABASESTATUS.ERROR) || (!ElevationData.Database.GetDBStatusBit(Model, DATABASESTATUS.COMPLETE) && !ElevationData.Database.GetDBStatusBit(Model, DATABASESTATUS.UPTODATE))) + { + this.ReportProgress(0, Name + " waiting for database is complete..."); + // sleep 10 sec + int i = 0; + while (!this.CancellationPending && (i < 10)) + { + Thread.Sleep(1000); + i++; + } + if (this.CancellationPending) + break; + } + this.ReportProgress(0, Name + " getting locations..."); + // get all locations in covered area but don't report progress + this.WorkerReportsProgress = false; + List lds = StationData.Database.LocationGetAll(this, Properties.Settings.Default.MinLat, Properties.Settings.Default.MinLon, Properties.Settings.Default.MaxLat, Properties.Settings.Default.MaxLon); + this.WorkerReportsProgress = true; + // start over again with main loog when lds = null for some reason + if (lds == null) + continue; + // iterate through locations + QRVDesignator myqrv = null; + QRVDesignator dxqrv = null; + foreach (LocationDesignator ld in lds) + { + Stopwatch st = new Stopwatch(); + st.Start(); + try + { + // leave the iteration when something went wrong --> start new + if (ld == null) + break; + // check lat/lon if valid + if (!GeographicalPoint.Check(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon)) + continue; + // check lat/lon if valid + if (!GeographicalPoint.Check(ld.Lat, ld.Lon)) + continue; + // chek for path < MaxDistance + double dist = LatLon.Distance(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, ld.Lat, ld.Lon); + if (dist <= Properties.Settings.Default.Path_MaxLength) + { + // start calculation for each band + foreach (BAND band in Bands.GetValuesExceptNoneAndAll()) + { + PropagationPathDesignator pp; + // get my lat/lon from settings + string mycall = Properties.Settings.Default.MyCall; + double mylat = Properties.Settings.Default.MyLat; + double mylon = Properties.Settings.Default.MyLon; + string myloc = MaidenheadLocator.LocFromLatLon(mylat, mylon, false, 3); + double myelv = ElevationData.Database[mylat, mylon, Model]; + // modify location in case of best case elevation is selected --> but do not store in database or settings! + if (Properties.Settings.Default.Path_BestCaseElevation) + { + if (!MaidenheadLocator.IsPrecise(mylat, mylon, 3)) + { + ElvMinMaxInfo maxinfo = ElevationData.Database.ElevationTileFindMinMaxInfo(myloc, Model); + if (maxinfo != null) + { + mylat = maxinfo.MaxLat; + mylon = maxinfo.MaxLon; + myelv = maxinfo.MaxElv; + } + } + } + myqrv = StationData.Database.QRVFind(mycall, myloc, band); + double myheight = ((myqrv != null) && (myqrv.AntennaHeight != 0)) ? myqrv.AntennaHeight : StationData.Database.QRVGetDefaultAntennaHeight(band); + string dxcall = ld.Call; + // get my lat/lon from settings + double dxlat = ld.Lat; + double dxlon = ld.Lon; + string dxloc = MaidenheadLocator.LocFromLatLon(dxlat, dxlon, false, 3); + double dxelv = ElevationData.Database[dxlat, dxlon, Model]; + // modify location in case of best case elevation is selected --> but do not store in database or settings! + if (Properties.Settings.Default.Path_BestCaseElevation) + { + if (!MaidenheadLocator.IsPrecise(dxlat, dxlon, 3)) + { + ElvMinMaxInfo maxinfo = ElevationData.Database.ElevationTileFindMinMaxInfo(dxloc, Model); + if (maxinfo != null) + { + dxlat = maxinfo.MaxLat; + dxlon = maxinfo.MaxLon; + dxelv = maxinfo.MaxElv; + } + } + } + dxqrv = StationData.Database.QRVFind(dxcall, dxloc, band); + double dxheight = ((dxqrv != null) && (dxqrv.AntennaHeight != 0)) ? dxqrv.AntennaHeight : StationData.Database.QRVGetDefaultAntennaHeight(band); + LocalObstructionDesignator o = ElevationData.Database.LocalObstructionFind(mylat, mylon, Model); + double myobstr = (o != null) ? o.GetObstruction(myheight, LatLon.Bearing(mylat, mylon, dxlat, dxlon)) : double.MinValue; + pp = PropagationData.Database.PropagationPathFind( + mylat, + mylon, + myelv + myheight, + dxlat, + dxlon, + dxelv + dxheight, + Bands.ToGHz(band), + LatLon.Earth.Radius * Properties.Settings.Default.Path_Band_Settings[band].K_Factor, + Properties.Settings.Default.Path_Band_Settings[band].F1_Clearance, + ElevationData.Database.GetDefaultStepWidth(Model), + Model, + myobstr); + // skip if path already in database + if (pp != null) + { + Thread.Sleep(Properties.Settings.Default.Background_Calculations_ThreadWait); + continue; + } + // create new propagation path + pp = PropagationData.Database.PropagationPathCreateFromLatLon( + this, + mylat, + mylon, + myelv + myheight, + dxlat, + dxlon, + dxelv + dxheight, + Bands.ToGHz(band), + LatLon.Earth.Radius * Properties.Settings.Default.Path_Band_Settings[band].K_Factor, + Properties.Settings.Default.Path_Band_Settings[band].F1_Clearance, + ElevationData.Database.GetDefaultStepWidth(Model), + Model, + myobstr); + st.Stop(); + this.ReportProgress(0, Name + " calculating path[ " + Bands.GetStringValue(band) + "]: " + Properties.Settings.Default.MyCall + "<>" + ld.Call + ", " + st.ElapsedMilliseconds.ToString() + " ms."); + } + if (this.CancellationPending) + break; + } + } + catch (Exception ex) + { + Log.WriteMessage(Name + " error processing call [" + ld.Call + "]: " + ex.ToString()); + } + } + // wait to keep cpu load low + Thread.Sleep(Properties.Settings.Default.Background_Calculations_ThreadWait); + this.ReportProgress(0, Name + " finished."); + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + // sleep when running periodically + if (Options == BACKGROUNDUPDATERSTARTOPTIONS.RUNPERIODICALLY) + { + int i = 0; + while (!this.CancellationPending && (i < interval)) + { + Thread.Sleep(1000); + i++; + } + } + } + while (Options == BACKGROUNDUPDATERSTARTOPTIONS.RUNPERIODICALLY); + if (this.CancellationPending) + { + this.ReportProgress(0, Name + " cancelled."); + Log.WriteMessage(Name + " cancelled."); + } + else + { + this.ReportProgress(0, Name + "finished."); + Log.WriteMessage(Name + " finished."); + } + } + + #endregion + } + } +} diff --git a/AirScout/PlaneFeedDisclaimerDlg.Designer.cs b/AirScout/PlaneFeedDisclaimerDlg.Designer.cs new file mode 100644 index 0000000..6bf7180 --- /dev/null +++ b/AirScout/PlaneFeedDisclaimerDlg.Designer.cs @@ -0,0 +1,92 @@ +namespace AirScout +{ + partial class PlaneFeedDisclaimerDlg + { + /// + /// Erforderliche Designervariable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Verwendete Ressourcen bereinigen. + /// + /// True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Vom Windows Form-Designer generierter Code + + /// + /// Erforderliche Methode für die Designerunterstützung. + /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden. + /// + private void InitializeComponent() + { + this.btn_DeepLinkDlg_Accept = new System.Windows.Forms.Button(); + this.btn_DeepLinkDlg_Decline = new System.Windows.Forms.Button(); + this.tb_DisclaimerText = new System.Windows.Forms.RichTextBox(); + this.SuspendLayout(); + // + // btn_DeepLinkDlg_Accept + // + this.btn_DeepLinkDlg_Accept.DialogResult = System.Windows.Forms.DialogResult.OK; + this.btn_DeepLinkDlg_Accept.Location = new System.Drawing.Point(135, 253); + this.btn_DeepLinkDlg_Accept.Name = "btn_DeepLinkDlg_Accept"; + this.btn_DeepLinkDlg_Accept.Size = new System.Drawing.Size(75, 23); + this.btn_DeepLinkDlg_Accept.TabIndex = 12; + this.btn_DeepLinkDlg_Accept.Text = "&Accept"; + this.btn_DeepLinkDlg_Accept.UseVisualStyleBackColor = true; + // + // btn_DeepLinkDlg_Decline + // + this.btn_DeepLinkDlg_Decline.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btn_DeepLinkDlg_Decline.Location = new System.Drawing.Point(231, 253); + this.btn_DeepLinkDlg_Decline.Name = "btn_DeepLinkDlg_Decline"; + this.btn_DeepLinkDlg_Decline.Size = new System.Drawing.Size(75, 23); + this.btn_DeepLinkDlg_Decline.TabIndex = 13; + this.btn_DeepLinkDlg_Decline.Text = "&Decline"; + this.btn_DeepLinkDlg_Decline.UseVisualStyleBackColor = true; + // + // tb_DisclaimerText + // + this.tb_DisclaimerText.BackColor = System.Drawing.Color.White; + this.tb_DisclaimerText.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_DisclaimerText.Location = new System.Drawing.Point(12, 12); + this.tb_DisclaimerText.Name = "tb_DisclaimerText"; + this.tb_DisclaimerText.ReadOnly = true; + this.tb_DisclaimerText.ScrollBars = System.Windows.Forms.RichTextBoxScrollBars.None; + this.tb_DisclaimerText.ShortcutsEnabled = false; + this.tb_DisclaimerText.Size = new System.Drawing.Size(454, 210); + this.tb_DisclaimerText.TabIndex = 14; + this.tb_DisclaimerText.Text = ""; + // + // PlaneFeedDisclaimerDlg + // + this.AcceptButton = this.btn_DeepLinkDlg_Accept; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btn_DeepLinkDlg_Decline; + this.ClientSize = new System.Drawing.Size(482, 288); + this.Controls.Add(this.tb_DisclaimerText); + this.Controls.Add(this.btn_DeepLinkDlg_Decline); + this.Controls.Add(this.btn_DeepLinkDlg_Accept); + this.Name = "PlaneFeedDisclaimerDlg"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "Use of Deep Links from Internet Server"; + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Button btn_DeepLinkDlg_Accept; + private System.Windows.Forms.Button btn_DeepLinkDlg_Decline; + public System.Windows.Forms.RichTextBox tb_DisclaimerText; + } +} \ No newline at end of file diff --git a/AirScout/PlaneFeedDisclaimerDlg.cs b/AirScout/PlaneFeedDisclaimerDlg.cs new file mode 100644 index 0000000..83ff50a --- /dev/null +++ b/AirScout/PlaneFeedDisclaimerDlg.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace AirScout +{ + public partial class PlaneFeedDisclaimerDlg : Form + { + public PlaneFeedDisclaimerDlg() + { + InitializeComponent(); + } + } +} diff --git a/AirScout/PlaneFeedDisclaimerDlg.resx b/AirScout/PlaneFeedDisclaimerDlg.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/AirScout/PlaneFeedDisclaimerDlg.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/AirScout/PlaneFeedSettingsDlg.Designer.cs b/AirScout/PlaneFeedSettingsDlg.Designer.cs new file mode 100644 index 0000000..5b3b4c5 --- /dev/null +++ b/AirScout/PlaneFeedSettingsDlg.Designer.cs @@ -0,0 +1,71 @@ +namespace AirScout +{ + partial class PlaneFeedSettingsDlg + { + /// + /// Erforderliche Designervariable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Verwendete Ressourcen bereinigen. + /// + /// True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Vom Windows Form-Designer generierter Code + + /// + /// Erforderliche Methode für die Designerunterstützung. + /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden. + /// + private void InitializeComponent() + { + this.pg_Main = new System.Windows.Forms.PropertyGrid(); + this.lbl_Info = new System.Windows.Forms.Label(); + this.SuspendLayout(); + // + // pg_Main + // + this.pg_Main.Location = new System.Drawing.Point(12, 15); + this.pg_Main.Name = "pg_Main"; + this.pg_Main.PropertySort = System.Windows.Forms.PropertySort.Categorized; + this.pg_Main.Size = new System.Drawing.Size(413, 253); + this.pg_Main.TabIndex = 0; + // + // lbl_Info + // + this.lbl_Info.Location = new System.Drawing.Point(431, 41); + this.lbl_Info.Name = "lbl_Info"; + this.lbl_Info.Size = new System.Drawing.Size(176, 227); + this.lbl_Info.TabIndex = 1; + this.lbl_Info.Text = "PlaneFeed Info"; + // + // PlaneFeedSettingsDlg + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(619, 277); + this.Controls.Add(this.lbl_Info); + this.Controls.Add(this.pg_Main); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow; + this.Name = "PlaneFeedSettingsDlg"; + this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; + this.Text = "PlaneFeedSettingsDlg"; + this.ResumeLayout(false); + + } + + #endregion + + public System.Windows.Forms.PropertyGrid pg_Main; + public System.Windows.Forms.Label lbl_Info; + } +} \ No newline at end of file diff --git a/AirScout/PlaneFeedSettingsDlg.cs b/AirScout/PlaneFeedSettingsDlg.cs new file mode 100644 index 0000000..337d156 --- /dev/null +++ b/AirScout/PlaneFeedSettingsDlg.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace AirScout +{ + public partial class PlaneFeedSettingsDlg : Form + { + public PlaneFeedSettingsDlg() + { + InitializeComponent(); + } + } +} diff --git a/AirScout/PlaneFeedSettingsDlg.resx b/AirScout/PlaneFeedSettingsDlg.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/AirScout/PlaneFeedSettingsDlg.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/AirScout/PlaneHistoryDlg.Designer.cs b/AirScout/PlaneHistoryDlg.Designer.cs new file mode 100644 index 0000000..422d03c --- /dev/null +++ b/AirScout/PlaneHistoryDlg.Designer.cs @@ -0,0 +1,193 @@ +namespace AirScout +{ + partial class PlaneHistoryDlg + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.cb_Hex = new System.Windows.Forms.ComboBox(); + this.cb_Call = new System.Windows.Forms.ComboBox(); + this.btn_Export = new System.Windows.Forms.Button(); + this.ss_Main = new System.Windows.Forms.StatusStrip(); + this.tsl_Status = new System.Windows.Forms.ToolStripStatusLabel(); + this.label4 = new System.Windows.Forms.Label(); + this.btn_Close = new System.Windows.Forms.Button(); + this.bw_GetHexAndCalls = new System.ComponentModel.BackgroundWorker(); + this.ss_Main.SuspendLayout(); + this.SuspendLayout(); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(77, 97); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(29, 13); + this.label1.TabIndex = 1; + this.label1.Text = "Hex:"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(77, 152); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(27, 13); + this.label2.TabIndex = 3; + this.label2.Text = "Call:"; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(164, 128); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(16, 13); + this.label3.TabIndex = 4; + this.label3.Text = "or"; + // + // cb_Hex + // + this.cb_Hex.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Hex.FormattingEnabled = true; + this.cb_Hex.Location = new System.Drawing.Point(118, 94); + this.cb_Hex.Name = "cb_Hex"; + this.cb_Hex.Size = new System.Drawing.Size(121, 24); + this.cb_Hex.TabIndex = 5; + this.cb_Hex.TextChanged += new System.EventHandler(this.cb_Hex_TextChanged); + // + // cb_Call + // + this.cb_Call.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Call.FormattingEnabled = true; + this.cb_Call.Location = new System.Drawing.Point(118, 152); + this.cb_Call.Name = "cb_Call"; + this.cb_Call.Size = new System.Drawing.Size(121, 24); + this.cb_Call.TabIndex = 6; + this.cb_Call.TextChanged += new System.EventHandler(this.cb_Call_TextChanged); + // + // btn_Export + // + this.btn_Export.Location = new System.Drawing.Point(118, 199); + this.btn_Export.Name = "btn_Export"; + this.btn_Export.Size = new System.Drawing.Size(54, 23); + this.btn_Export.TabIndex = 7; + this.btn_Export.Text = "Export"; + this.btn_Export.UseVisualStyleBackColor = true; + this.btn_Export.Click += new System.EventHandler(this.btn_Export_Click); + // + // ss_Main + // + this.ss_Main.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.tsl_Status}); + this.ss_Main.Location = new System.Drawing.Point(0, 240); + this.ss_Main.Name = "ss_Main"; + this.ss_Main.Size = new System.Drawing.Size(352, 22); + this.ss_Main.TabIndex = 8; + this.ss_Main.Text = "statusStrip1"; + // + // tsl_Status + // + this.tsl_Status.Name = "tsl_Status"; + this.tsl_Status.Size = new System.Drawing.Size(39, 17); + this.tsl_Status.Text = "Status"; + // + // label4 + // + this.label4.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; + this.label4.Font = new System.Drawing.Font("Microsoft Sans Serif", 11.25F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label4.Location = new System.Drawing.Point(18, 9); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(312, 65); + this.label4.TabIndex = 9; + this.label4.Text = "Use this dialog to export a single aircraft\'s position history. \r\nYou can select " + + "the aircraft by Hex or by Call."; + this.label4.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // btn_Close + // + this.btn_Close.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btn_Close.Location = new System.Drawing.Point(185, 199); + this.btn_Close.Name = "btn_Close"; + this.btn_Close.Size = new System.Drawing.Size(54, 23); + this.btn_Close.TabIndex = 10; + this.btn_Close.Text = "Close"; + this.btn_Close.UseVisualStyleBackColor = true; + // + // bw_GetHexAndCalls + // + this.bw_GetHexAndCalls.WorkerReportsProgress = true; + this.bw_GetHexAndCalls.WorkerSupportsCancellation = true; + this.bw_GetHexAndCalls.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bw_GetHexAndCalls_DoWork); + this.bw_GetHexAndCalls.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.bw_GetHexAndCalls_ProgressChanged); + this.bw_GetHexAndCalls.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.bw_GetHexAndCalls_RunWorkerCompleted); + // + // PlaneHistoryDlg + // + this.AcceptButton = this.btn_Export; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btn_Close; + this.ClientSize = new System.Drawing.Size(352, 262); + this.Controls.Add(this.btn_Close); + this.Controls.Add(this.label4); + this.Controls.Add(this.ss_Main); + this.Controls.Add(this.btn_Export); + this.Controls.Add(this.cb_Call); + this.Controls.Add(this.cb_Hex); + this.Controls.Add(this.label3); + this.Controls.Add(this.label2); + this.Controls.Add(this.label1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "PlaneHistoryDlg"; + this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; + this.Text = "PlaneHistoryDlg"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.PlaneHistoryDlg_FormClosing); + this.Load += new System.EventHandler(this.PlaneHistoryDlg_Load); + this.ss_Main.ResumeLayout(false); + this.ss_Main.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.ComboBox cb_Hex; + private System.Windows.Forms.ComboBox cb_Call; + private System.Windows.Forms.Button btn_Export; + private System.Windows.Forms.StatusStrip ss_Main; + private System.Windows.Forms.ToolStripStatusLabel tsl_Status; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Button btn_Close; + private System.ComponentModel.BackgroundWorker bw_GetHexAndCalls; + } +} \ No newline at end of file diff --git a/AirScout/PlaneHistoryDlg.cs b/AirScout/PlaneHistoryDlg.cs new file mode 100644 index 0000000..a2b8796 --- /dev/null +++ b/AirScout/PlaneHistoryDlg.cs @@ -0,0 +1,227 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using System.IO; +using AirScout.Aircrafts; +using ScoutBase.Core; +using System.Globalization; +using AirScout.AircraftPositions; + +namespace AirScout +{ + public partial class PlaneHistoryDlg : Form + { + DateTime From; + DateTime To; + string TmpDirectory; + + List Hexs; + List Calls; + List AircraftPositions; + + PlaneInfoConverter c = new PlaneInfoConverter(); + + public PlaneHistoryDlg(DateTime from, DateTime to, string tmpdir) + { + InitializeComponent(); + From = from; + To = to; + TmpDirectory = tmpdir; + } + + private void Say (string msg) + { + if (tsl_Status.Text != msg) + { + tsl_Status.Text = msg; + ss_Main.Refresh(); + } + } + + private void PlaneHistoryDlg_Load(object sender, EventArgs e) + { + this.Show(); + this.BringToFront(); + cb_Hex.Enabled = false; + cb_Call.Enabled = false; + btn_Export.Enabled = false; + bw_GetHexAndCalls.RunWorkerAsync(); + } + + private void cb_Hex_TextChanged(object sender, EventArgs e) + { + cb_Call.SelectedIndex = -1; + } + + private void cb_Call_TextChanged(object sender, EventArgs e) + { + cb_Hex.SelectedIndex = -1; + } + + private void btn_Export_Click(object sender, EventArgs e) + { + Say("Getting positions..."); + CultureInfo uiculture = CultureInfo.CurrentUICulture; + if (!String.IsNullOrEmpty(cb_Hex.Text)) + { + AircraftPositions = AircraftPositionData.Database.AircraftPositionGetAllByHex(cb_Hex.Text, From, To); + if (AircraftPositions == null) + return; + // export by hex and save it with current UI culture + SaveFileDialog Dlg = new SaveFileDialog(); + Dlg.AddExtension = true; + Dlg.DefaultExt = "csv"; + Dlg.FileName = "Plane History_" + cb_Hex.Text + "_" + From.ToString("yyyyMMdd_HHmmss") + "_to_" + To.ToString("yyyyMMdd_HHmmss"); + Dlg.InitialDirectory = TmpDirectory; + Dlg.OverwritePrompt = true; + if (Dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) + { + using (StreamWriter sw = new StreamWriter(Dlg.FileName)) + { + sw.WriteLine("time;hex;call;lat[deg];lon[deg];alt[m];track[deg];speed[kmh];dist[km];av_speed[km/h]"); + for (int i = 0; i < AircraftPositions.Count; i++) + { + double dist = 0; + double av_speed = 0; + if (i > 0) + { + dist = LatLon.Distance(AircraftPositions[i - 1].Lat, AircraftPositions[i - 1].Lon, AircraftPositions[i].Lat, AircraftPositions[i].Lon); + av_speed = dist/(AircraftPositions[i].LastUpdated - AircraftPositions[i - 1].LastUpdated).TotalHours; + } + sw.WriteLine(AircraftPositions[i].LastUpdated.ToString("yyyy-MM-dd HH:mm:ss") + ";" + + AircraftPositions[i].Hex + ";" + + AircraftPositions[i].Call + ";" + + AircraftPositions[i].Lat.ToString("F8", uiculture) + ";" + + AircraftPositions[i].Lon.ToString("F8", uiculture) + ";" + + UnitConverter.ft_m(AircraftPositions[i].Alt).ToString("F8", uiculture) + ";" + + AircraftPositions[i].Track.ToString("F8", uiculture) + ";" + + UnitConverter.kts_kmh(AircraftPositions[i].Speed).ToString("F8", uiculture) + ";" + + dist.ToString("F8", uiculture) + ";" + + av_speed.ToString("F8", uiculture) + ); + } + } + } + } + else if (!String.IsNullOrEmpty(cb_Call.Text)) + { + // export by call + AircraftPositions = AircraftPositionData.Database.AircraftPositionGetAllByCall(cb_Call.Text, From, To); + if (AircraftPositions == null) + return; + // export by hex + SaveFileDialog Dlg = new SaveFileDialog(); + Dlg.AddExtension = true; + Dlg.DefaultExt = "csv"; + Dlg.FileName = "Plane History_" + cb_Call.Text + "_" + From.ToString("yyyyMMdd_HHmmss") + "_to_" + To.ToString("yyyyMMdd_HHmmss"); + Dlg.InitialDirectory = TmpDirectory; + Dlg.OverwritePrompt = true; + if (Dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) + { + using (StreamWriter sw = new StreamWriter(Dlg.FileName)) + { + sw.WriteLine("time;hex;call;lat[deg];lon[deg];alt[m];track[deg];speed[kmh];dist[km];av_speed[km/h]"); + for (int i = 0; i < AircraftPositions.Count; i++) + { + double dist = 0; + double av_speed = 0; + if (i > 0) + { + dist = LatLon.Distance(AircraftPositions[i - 1].Lat, AircraftPositions[i - 1].Lon, AircraftPositions[i].Lat, AircraftPositions[i].Lon); + av_speed = dist / (AircraftPositions[i].LastUpdated - AircraftPositions[i - 1].LastUpdated).TotalHours; + } + sw.WriteLine(AircraftPositions[i].LastUpdated.ToString("yyyy-MM-dd HH:mm:ss") + ";" + + AircraftPositions[i].Hex + ";" + + AircraftPositions[i].Call + ";" + + AircraftPositions[i].Lat.ToString("F8", uiculture) + ";" + + AircraftPositions[i].Lon.ToString("F8", uiculture) + ";" + + UnitConverter.ft_m(AircraftPositions[i].Alt).ToString("F8", uiculture) + ";" + + AircraftPositions[i].Track.ToString("F8", uiculture) + ";" + + UnitConverter.kts_kmh(AircraftPositions[i].Speed).ToString("F8", uiculture) + ";" + + dist.ToString("F8", uiculture) + ";" + + av_speed.ToString("F8", uiculture) + ); + } + } + } + } + Say("Ready."); + } + + private void bw_GetHexAndCalls_DoWork(object sender, DoWorkEventArgs e) + { + bw_GetHexAndCalls.ReportProgress(0, ("Collecting data, please wait...")); + List hexs = AircraftPositionData.Database.AircraftPositionGetAllHex(From, To); + if (hexs != null) + bw_GetHexAndCalls.ReportProgress(1, hexs); + List calls = AircraftPositionData.Database.AircraftPositionGetAllCalls(From, To); + if (calls != null) + bw_GetHexAndCalls.ReportProgress(2, calls); + } + + private void bw_GetHexAndCalls_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + if (e.ProgressPercentage <= 0) + { + Say((string)e.UserState); + } + else if (e.ProgressPercentage == 1) + { + // hexs completed + Hexs = (List)e.UserState; + if (Hexs != null) + { + cb_Hex.BeginUpdate(); + foreach (string hex in Hexs) + { + string s = c.To_Hex(hex); + if (!String.IsNullOrEmpty(s)) + cb_Hex.Items.Add(s); + } + cb_Hex.EndUpdate(); + } + } + else if (e.ProgressPercentage == 2) + { + // calls compeleted + Calls = (List)e.UserState; + if (Calls != null) + { + cb_Call.BeginUpdate(); + foreach (string call in Calls) + { + string s = c.To_Call(call); + if (!String.IsNullOrEmpty(s)) + cb_Call.Items.Add(s); + } + cb_Call.EndUpdate(); + } + } + } + + private void bw_GetHexAndCalls_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + if (bw_GetHexAndCalls.CancellationPending) + { + Say("Cancelled."); + } + else + { + Say("Ready."); + cb_Hex.Enabled = true; + cb_Call.Enabled = true; + btn_Export.Enabled = true; + } + } + + private void PlaneHistoryDlg_FormClosing(object sender, FormClosingEventArgs e) + { + bw_GetHexAndCalls.CancelAsync(); + } + } +} diff --git a/AirScout/PlaneHistoryDlg.resx b/AirScout/PlaneHistoryDlg.resx new file mode 100644 index 0000000..3d1ef0c --- /dev/null +++ b/AirScout/PlaneHistoryDlg.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 111, 17 + + \ No newline at end of file diff --git a/AirScout/Planefinder.cs b/AirScout/Planefinder.cs new file mode 100644 index 0000000..128230a --- /dev/null +++ b/AirScout/Planefinder.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Forms; +using GMap.NET; +using GMap.NET.MapProviders; +using GMap.NET.WindowsForms; +using GMap.NET.WindowsForms.Markers; +using GMap.NET.WindowsForms.ToolTips; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Reflection; +using WinTest; +using System.Diagnostics; +using ScoutBase.Core; + +public static class Planefinder +{ + public static string Read() + { + String getURL = "http://planefinder.net/endpoints/update.php?faa=1&bounds=37.729817%2C-98.840955%2C52.451268%2C-76.868299"; + HttpWebRequest get = (HttpWebRequest)HttpWebRequest.Create(getURL); + get.Referer = "http://planefinder.net/"; + get.UserAgent = "Mozilla/5.0 (X11; Linux x86_64; rv:17.0) Gecko/20130807 Firefox/17.0"; + HttpWebResponse responseGet = (HttpWebResponse)get.GetResponse(); + string response; + using (StreamReader sr = new StreamReader(responseGet.GetResponseStream())) + { + response = sr.ReadToEnd(); + // more stuff + } +// HttpEntity resEntityGet = responseGet.getEntity(); + return response; + } +} \ No newline at end of file diff --git a/AirScout/Program.cs b/AirScout/Program.cs new file mode 100644 index 0000000..e693b81 --- /dev/null +++ b/AirScout/Program.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; +using System.Threading; + +namespace AirScout +{ + static class Program + { + // Mutex to ensure that only one instance is running + static System.Threading.Mutex singleton = new Mutex(true, Application.ProductName); + /// + /// Der Haupteinstiegspunkt für die Anwendung. + /// + [STAThread] + static void Main() + { + /* + if (!singleton.WaitOne(TimeSpan.Zero, true)) + { + //there is already another instance running! + MessageBox.Show("AirScout is already running on this computer.","Application Check"); + Application.Exit(); + } + else + */ + { + // run program normally + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new MapDlg()); + } + } + } +} diff --git a/AirScout/Properties/AssemblyInfo.cs b/AirScout/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..3868d20 --- /dev/null +++ b/AirScout/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Allgemeine Informationen über eine Assembly werden über die folgenden +// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, +// die mit einer Assembly verknüpft sind. +[assembly: AssemblyTitle("AirScout")] +[assembly: AssemblyDescription("Aircraft Scatter Prediction")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("DL2ALF")] +[assembly: AssemblyProduct("AirScout")] +[assembly: AssemblyCopyright("Copyright © 2013-2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar +// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von +// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest. +[assembly: ComVisible(false)] + +// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird +[assembly: Guid("0eeacbb0-947a-4f06-ad80-0c7380156ae5")] + +// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: +// +// Hauptversion +// Nebenversion +// Buildnummer +// Revision +// +// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern +// übernehmen, indem Sie "*" eingeben: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.3.0.4")] +[assembly: AssemblyFileVersion("1.3.0.4")] diff --git a/AirScout/Properties/DataSources/AirScout.Properties.Settings.datasource b/AirScout/Properties/DataSources/AirScout.Properties.Settings.datasource new file mode 100644 index 0000000..4ff8e98 --- /dev/null +++ b/AirScout/Properties/DataSources/AirScout.Properties.Settings.datasource @@ -0,0 +1,10 @@ + + + + AirScout.Properties.Settings, Properties.Resources.Designer.cs, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null + \ No newline at end of file diff --git a/AirScout/Properties/DataSources/OptionsDlg.datasource b/AirScout/Properties/DataSources/OptionsDlg.datasource new file mode 100644 index 0000000..abde48c --- /dev/null +++ b/AirScout/Properties/DataSources/OptionsDlg.datasource @@ -0,0 +1,10 @@ + + + + AirScout.OptionsDlg, AirScout, Version=0.9.9.5, Culture=neutral, PublicKeyToken=null + \ No newline at end of file diff --git a/AirScout/Properties/New.Designer.cs b/AirScout/Properties/New.Designer.cs new file mode 100644 index 0000000..7750323 --- /dev/null +++ b/AirScout/Properties/New.Designer.cs @@ -0,0 +1,2277 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.18444 +// +// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn +// der Code erneut generiert wird. +// +//------------------------------------------------------------------------------ + +namespace AirScout.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] + internal sealed partial class New : global::System.Configuration.ApplicationSettingsBase { + + private static New defaultInstance = ((New)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new New()))); + + public static New Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("DL2ALF")] + public string MyCall + { + get + { + return ((string)(this["MyCall"])); + } + set + { + this["MyCall"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("JO50IW")] + public string MyLoc + { + get + { + return ((string)(this["MyLoc"])); + } + set + { + this["MyLoc"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("GB3MHL")] + public string DXCall + { + get + { + return ((string)(this["DXCall"])); + } + set + { + this["DXCall"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("JO02PB")] + public string DXLoc + { + get + { + return ((string)(this["DXLoc"])); + } + set + { + this["DXLoc"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string Band + { + get + { + return ((string)(this["Band"])); + } + set + { + this["Band"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("60")] + public int Planes_Update + { + get + { + return ((int)(this["Planes_Update"])); + } + set + { + this["Planes_Update"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("5000")] + public int Planes_MinAlt + { + get + { + return ((int)(this["Planes_MinAlt"])); + } + set + { + this["Planes_MinAlt"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Map_AutoCenter + { + get + { + return ((bool)(this["Map_AutoCenter"])); + } + set + { + this["Map_AutoCenter"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Planes_Draw_Path + { + get + { + return ((bool)(this["Planes_Draw_Path"])); + } + set + { + this["Planes_Draw_Path"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public double Path_GroundClearance + { + get + { + return ((double)(this["Path_GroundClearance"])); + } + set + { + this["Path_GroundClearance"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("12200")] + public int Planes_MaxAlt + { + get + { + return ((int)(this["Planes_MaxAlt"])); + } + set + { + this["Planes_MaxAlt"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("14")] + public double MyHeight + { + get + { + return ((double)(this["MyHeight"])); + } + set + { + this["MyHeight"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("30")] + public double DXHeight + { + get + { + return ((double)(this["DXHeight"])); + } + set + { + this["DXHeight"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("\\Database\\calls.txt")] + public string Calls_FileName + { + get + { + return ((string)(this["Calls_FileName"])); + } + set + { + this["Calls_FileName"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("\\Database\\lastcalls.txt")] + public string LastCalls_FileName + { + get + { + return ((string)(this["LastCalls_FileName"])); + } + set + { + this["LastCalls_FileName"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("\\Database\\call3.txt")] + public string WSJTCalls_FileName + { + get + { + return ((string)(this["WSJTCalls_FileName"])); + } + set + { + this["WSJTCalls_FileName"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("\r\n\r\n DL2ALF\r\n")] + public global::System.Collections.Specialized.StringCollection LastCalls + { + get + { + return ((global::System.Collections.Specialized.StringCollection)(this["LastCalls"])); + } + set + { + this["LastCalls"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("\\Database\\Aircrafts.txt")] + public string AircraftTypes_FileName + { + get + { + return ((string)(this["AircraftTypes_FileName"])); + } + set + { + this["AircraftTypes_FileName"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://www.flugzeuginfo.net/table_accodes_en.php")] + public string AircraftTypes_URL + { + get + { + return ((string)(this["AircraftTypes_URL"])); + } + set + { + this["AircraftTypes_URL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("20")] + public int LastCalls_MaxCount + { + get + { + return ((int)(this["LastCalls_MaxCount"])); + } + set + { + this["LastCalls_MaxCount"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("ElevationData\\GLOBE")] + public string Elevation_GLOBE_DataPath + { + get + { + return ((string)(this["Elevation_GLOBE_DataPath"])); + } + set + { + this["Elevation_GLOBE_DataPath"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute(@" + + 50M + 70M + 144M + 432M + 1.2G + 2.3G + 3.4G + 5.7G + 10G + 24G + 47G + 76G +")] + public global::System.Collections.Specialized.StringCollection Band_Settings + { + get + { + return ((global::System.Collections.Specialized.StringCollection)(this["Band_Settings"])); + } + set + { + this["Band_Settings"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public string Planes_Filter_Min_Alt + { + get + { + return ((string)(this["Planes_Filter_Min_Alt"])); + } + set + { + this["Planes_Filter_Min_Alt"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string Planes_Filter_Min_Cat + { + get + { + return ((string)(this["Planes_Filter_Min_Cat"])); + } + set + { + this["Planes_Filter_Min_Cat"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("360")] + public int General_ShowISSPath + { + get + { + return ((int)(this["General_ShowISSPath"])); + } + set + { + this["General_ShowISSPath"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-15")] + public double MinLon + { + get + { + return ((double)(this["MinLon"])); + } + set + { + this["MinLon"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("30")] + public double MaxLon + { + get + { + return ((double)(this["MaxLon"])); + } + set + { + this["MaxLon"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("35")] + public double MinLat + { + get + { + return ((double)(this["MinLat"])); + } + set + { + this["MinLat"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("60")] + public double MaxLat + { + get + { + return ((double)(this["MaxLat"])); + } + set + { + this["MaxLat"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("50.937067")] + public double MyLat + { + get + { + return ((double)(this["MyLat"])); + } + set + { + this["MyLat"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("10.68327")] + public double MyLon + { + get + { + return ((double)(this["MyLon"])); + } + set + { + this["MyLon"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("52.05626084")] + public double DXLat + { + get + { + return ((double)(this["DXLat"])); + } + set + { + this["DXLat"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1.28022909")] + public double DXLon + { + get + { + return ((double)(this["DXLon"])); + } + set + { + this["DXLon"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("23")] + public double DXElevation + { + get + { + return ((double)(this["DXElevation"])); + } + set + { + this["DXElevation"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("328")] + public double MyElevation + { + get + { + return ((double)(this["MyElevation"])); + } + set + { + this["MyElevation"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("ElevationData\\SRTM3")] + public string Elevation_SRTM3_DataPath + { + get + { + return ((string)(this["Elevation_SRTM3_DataPath"])); + } + set + { + this["Elevation_SRTM3_DataPath"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("ElevationData\\SRTM1")] + public string Elevation_SRTM1_DataPath + { + get + { + return ((string)(this["Elevation_SRTM1_DataPath"])); + } + set + { + this["Elevation_SRTM1_DataPath"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Elevation_SRTM3_Enabled + { + get + { + return ((bool)(this["Elevation_SRTM3_Enabled"])); + } + set + { + this["Elevation_SRTM3_Enabled"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Elevation_SRTM1_Enabled + { + get + { + return ((bool)(this["Elevation_SRTM1_Enabled"])); + } + set + { + this["Elevation_SRTM1_Enabled"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://www.qrz.com/db/")] + public string QRZ_URL_Database + { + get + { + return ((string)(this["QRZ_URL_Database"])); + } + set + { + this["QRZ_URL_Database"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("100")] + public double Path_NearFieldSuppression + { + get + { + return ((double)(this["Path_NearFieldSuppression"])); + } + set + { + this["Path_NearFieldSuppression"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1000")] + public double Path_StepWidth + { + get + { + return ((double)(this["Path_StepWidth"])); + } + set + { + this["Path_StepWidth"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://dds.cr.usgs.gov/srtm/version2_1/SRTM3/Eurasia/")] + public string Elevation_SRTM3_URL + { + get + { + return ((string)(this["Elevation_SRTM3_URL"])); + } + set + { + this["Elevation_SRTM3_URL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1")] + public int Path_History_StepWidth + { + get + { + return ((int)(this["Path_History_StepWidth"])); + } + set + { + this["Path_History_StepWidth"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Alarm_BringWindowToFront + { + get + { + return ((bool)(this["Alarm_BringWindowToFront"])); + } + set + { + this["Alarm_BringWindowToFront"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Alarm_PlaySound + { + get + { + return ((bool)(this["Alarm_PlaySound"])); + } + set + { + this["Alarm_PlaySound"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("100")] + public double Alarm_Distance + { + get + { + return ((double)(this["Alarm_Distance"])); + } + set + { + this["Alarm_Distance"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Alarm_Activate + { + get + { + return ((bool)(this["Alarm_Activate"])); + } + set + { + this["Alarm_Activate"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Database\\localobstructions.txt")] + public string LocalObstruction_FileName + { + get + { + return ((string)(this["LocalObstruction_FileName"])); + } + set + { + this["LocalObstruction_FileName"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Path_Cubic_Spline + { + get + { + return ((bool)(this["Path_Cubic_Spline"])); + } + set + { + this["Path_Cubic_Spline"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Horizon_Plot_Cartesian + { + get + { + return ((bool)(this["Horizon_Plot_Cartesian"])); + } + set + { + this["Horizon_Plot_Cartesian"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Horizon_Plot_Polar + { + get + { + return ((bool)(this["Horizon_Plot_Polar"])); + } + set + { + this["Horizon_Plot_Polar"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("9871")] + public int WinTest_Port + { + get + { + return ((int)(this["WinTest_Port"])); + } + set + { + this["WinTest_Port"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("9872")] + public int Server_Port + { + get + { + return ((int)(this["Server_Port"])); + } + set + { + this["Server_Port"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Path_BestCaseElevation + { + get + { + return ((bool)(this["Path_BestCaseElevation"])); + } + set + { + this["Path_BestCaseElevation"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Icon")] + public string Planes_IconPath + { + get + { + return ((string)(this["Planes_IconPath"])); + } + set + { + this["Planes_IconPath"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("plane.png")] + public string Planes_IconFileName + { + get + { + return ((string)(this["Planes_IconFileName"])); + } + set + { + this["Planes_IconFileName"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("[none]")] + public string Planes_PlaneFeed2 + { + get + { + return ((string)(this["Planes_PlaneFeed2"])); + } + set + { + this["Planes_PlaneFeed2"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("[none]")] + public string Planes_PlaneFeed3 + { + get + { + return ((string)(this["Planes_PlaneFeed3"])); + } + set + { + this["Planes_PlaneFeed3"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("[none]")] + public string Planes_PlaneFeed1 + { + get + { + return ((string)(this["Planes_PlaneFeed1"])); + } + set + { + this["Planes_PlaneFeed1"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string Path_BandSettings + { + get + { + return ((string)(this["Path_BandSettings"])); + } + set + { + this["Path_BandSettings"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("5")] + public int Planes_Position_TTL + { + get + { + return ((int)(this["Planes_Position_TTL"])); + } + set + { + this["Planes_Position_TTL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Server_Activate + { + get + { + return ((bool)(this["Server_Activate"])); + } + set + { + this["Server_Activate"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("AS")] + public string Server_Name + { + get + { + return ((string)(this["Server_Name"])); + } + set + { + this["Server_Name"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute(@"http://dds.cr.usgs.gov/srtm/version2_1/SRTM3/Africa/ +http://dds.cr.usgs.gov/srtm/version2_1/SRTM3/Australia/ +http://dds.cr.usgs.gov/srtm/version2_1/SRTM3/Eurasia/ +http://dds.cr.usgs.gov/srtm/version2_1/SRTM3/Islands/ +http://dds.cr.usgs.gov/srtm/version2_1/SRTM3/North_America/ +http://dds.cr.usgs.gov/srtm/version2_1/SRTM3/South_America/")] + public string Elevation_SRTM3_URLs + { + get + { + return ((string)(this["Elevation_SRTM3_URLs"])); + } + set + { + this["Elevation_SRTM3_URLs"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("OpenStreetMap")] + public string Map_Provider + { + get + { + return ((string)(this["Map_Provider"])); + } + set + { + this["Map_Provider"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string Map_Provider_Accepted + { + get + { + return ((string)(this["Map_Provider_Accepted"])); + } + set + { + this["Map_Provider_Accepted"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Microsoft Sans Serif; 11;Bold")] + public string Map_ToolTipFont + { + get + { + return ((string)(this["Map_ToolTipFont"])); + } + set + { + this["Map_ToolTipFont"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1.33")] + public double Path_Default_K_Factor + { + get + { + return ((double)(this["Path_Default_K_Factor"])); + } + set + { + this["Path_Default_K_Factor"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0.6")] + public double Path_Default_F1_Clearance + { + get + { + return ((double)(this["Path_Default_F1_Clearance"])); + } + set + { + this["Path_Default_F1_Clearance"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public double Path_Default_Ground_Clearance + { + get + { + return ((double)(this["Path_Default_Ground_Clearance"])); + } + set + { + this["Path_Default_Ground_Clearance"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("10")] + public double Path_Default_Max_Distance + { + get + { + return ((double)(this["Path_Default_Max_Distance"])); + } + set + { + this["Path_Default_Max_Distance"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public double Path_Default_Max_Squint + { + get + { + return ((double)(this["Path_Default_Max_Squint"])); + } + set + { + this["Path_Default_Max_Squint"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public double Path_Default_Max_Elevation + { + get + { + return ((double)(this["Path_Default_Max_Elevation"])); + } + set + { + this["Path_Default_Max_Elevation"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://localhost/")] + public string SpecLab_URL + { + get + { + return ((string)(this["SpecLab_URL"])); + } + set + { + this["SpecLab_URL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1")] + public double SpecLab_Update + { + get + { + return ((double)(this["SpecLab_Update"])); + } + set + { + this["SpecLab_Update"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("_fft_expt.json")] + public string SpecLab_FileName + { + get + { + return ((string)(this["SpecLab_FileName"])); + } + set + { + this["SpecLab_FileName"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("400")] + public int SpecLab_F1 + { + get + { + return ((int)(this["SpecLab_F1"])); + } + set + { + this["SpecLab_F1"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1600")] + public int SpecLab_F2 + { + get + { + return ((int)(this["SpecLab_F2"])); + } + set + { + this["SpecLab_F2"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool SpecLab_Enabled + { + get + { + return ((bool)(this["SpecLab_Enabled"])); + } + set + { + this["SpecLab_Enabled"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool InfoWin_Metric + { + get + { + return ((bool)(this["InfoWin_Metric"])); + } + set + { + this["InfoWin_Metric"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool InfoWin_Imperial + { + get + { + return ((bool)(this["InfoWin_Imperial"])); + } + set + { + this["InfoWin_Imperial"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool InfoWin_Position + { + get + { + return ((bool)(this["InfoWin_Position"])); + } + set + { + this["InfoWin_Position"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool InfoWin_Time + { + get + { + return ((bool)(this["InfoWin_Time"])); + } + set + { + this["InfoWin_Time"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool InfoWin_Epsilon + { + get + { + return ((bool)(this["InfoWin_Epsilon"])); + } + set + { + this["InfoWin_Epsilon"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool InfoWin_Squint + { + get + { + return ((bool)(this["InfoWin_Squint"])); + } + set + { + this["InfoWin_Squint"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool InfoWin_Track + { + get + { + return ((bool)(this["InfoWin_Track"])); + } + set + { + this["InfoWin_Track"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool InfoWin_Type + { + get + { + return ((bool)(this["InfoWin_Type"])); + } + set + { + this["InfoWin_Type"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool InfoWin_Alt + { + get + { + return ((bool)(this["InfoWin_Alt"])); + } + set + { + this["InfoWin_Alt"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool InfoWin_Dist + { + get + { + return ((bool)(this["InfoWin_Dist"])); + } + set + { + this["InfoWin_Dist"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool InfoWin_Speed + { + get + { + return ((bool)(this["InfoWin_Speed"])); + } + set + { + this["InfoWin_Speed"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool InfoWin_Angle + { + get + { + return ((bool)(this["InfoWin_Angle"])); + } + set + { + this["InfoWin_Angle"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool SFTP_Enabled + { + get + { + return ((bool)(this["SFTP_Enabled"])); + } + set + { + this["SFTP_Enabled"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://www.airscout.eu/downloads/airscout.pwd")] + public string SFTP_PwdURL + { + get + { + return ((string)(this["SFTP_PwdURL"])); + } + set + { + this["SFTP_PwdURL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool First_Agree + { + get + { + return ((bool)(this["First_Agree"])); + } + set + { + this["First_Agree"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool First_Disagree + { + get + { + return ((bool)(this["First_Disagree"])); + } + set + { + this["First_Disagree"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool First_Privacy + { + get + { + return ((bool)(this["First_Privacy"])); + } + set + { + this["First_Privacy"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool FirstRun + { + get + { + return ((bool)(this["FirstRun"])); + } + set + { + this["FirstRun"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Map_ShowZoomBox + { + get + { + return ((bool)(this["Map_ShowZoomBox"])); + } + set + { + this["Map_ShowZoomBox"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Map_ShowFilterBox + { + get + { + return ((bool)(this["Map_ShowFilterBox"])); + } + set + { + this["Map_ShowFilterBox"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Map_ShowAlarmBox + { + get + { + return ((bool)(this["Map_ShowAlarmBox"])); + } + set + { + this["Map_ShowAlarmBox"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Map_ShowInfoBox + { + get + { + return ((bool)(this["Map_ShowInfoBox"])); + } + set + { + this["Map_ShowInfoBox"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public int MainSplitter_Distance + { + get + { + return ((int)(this["MainSplitter_Distance"])); + } + set + { + this["MainSplitter_Distance"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public int MapSplitter_Distance + { + get + { + return ((int)(this["MapSplitter_Distance"])); + } + set + { + this["MapSplitter_Distance"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Track_Activate + { + get + { + return ((bool)(this["Track_Activate"])); + } + set + { + this["Track_Activate"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute(@" + + [Network] AirScout + [Network] Win-Test + [DDE] Ham Radio Deluxe + [DDE] Nova + [Serial] Yaesu GS-232A (Az only) + [Serial] Yaesu GS-232A (Az/El) + +")] + public global::System.Collections.Specialized.StringCollection Track_Protocol + { + get + { + return ((global::System.Collections.Specialized.StringCollection)(this["Track_Protocol"])); + } + set + { + this["Track_Protocol"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public double Track_SetAz + { + get + { + return ((double)(this["Track_SetAz"])); + } + set + { + this["Track_SetAz"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public double Track_SetEl + { + get + { + return ((double)(this["Track_SetEl"])); + } + set + { + this["Track_SetEl"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("16")] + public int Planes_IconSize_L + { + get + { + return ((int)(this["Planes_IconSize_L"])); + } + set + { + this["Planes_IconSize_L"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("24")] + public int Planes_IconSize_M + { + get + { + return ((int)(this["Planes_IconSize_M"])); + } + set + { + this["Planes_IconSize_M"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("36")] + public int Planes_IconSize_H + { + get + { + return ((int)(this["Planes_IconSize_H"])); + } + set + { + this["Planes_IconSize_H"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("48")] + public int Planes_IconSize_S + { + get + { + return ((int)(this["Planes_IconSize_S"])); + } + set + { + this["Planes_IconSize_S"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("9880")] + public int Webserver_Port + { + get + { + return ((int)(this["Webserver_Port"])); + } + set + { + this["Webserver_Port"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("01/31/2015 15:21:00")] + public global::System.DateTime News_LastUpdate + { + get + { + return ((global::System.DateTime)(this["News_LastUpdate"])); + } + set + { + this["News_LastUpdate"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("3600")] + public int News_Interval + { + get + { + return ((int)(this["News_Interval"])); + } + set + { + this["News_Interval"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://www.airscout.eu/news.html")] + public global::System.Uri News_URL + { + get + { + return ((global::System.Uri)(this["News_URL"])); + } + set + { + this["News_URL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Track_UDP_WinTest + { + get + { + return ((bool)(this["Track_UDP_WinTest"])); + } + set + { + this["Track_UDP_WinTest"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Track_DDE_HRD + { + get + { + return ((bool)(this["Track_DDE_HRD"])); + } + set + { + this["Track_DDE_HRD"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("COM13")] + public string Track_Serial_Port + { + get + { + return ((string)(this["Track_Serial_Port"])); + } + set + { + this["Track_Serial_Port"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("4800")] + public int Track_Serial_Baudrate + { + get + { + return ((int)(this["Track_Serial_Baudrate"])); + } + set + { + this["Track_Serial_Baudrate"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Track_Serial_GS232_AZ + { + get + { + return ((bool)(this["Track_Serial_GS232_AZ"])); + } + set + { + this["Track_Serial_GS232_AZ"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Track_File_WSJT + { + get + { + return ((bool)(this["Track_File_WSJT"])); + } + set + { + this["Track_File_WSJT"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Track_File_Native + { + get + { + return ((bool)(this["Track_File_Native"])); + } + set + { + this["Track_File_Native"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Track_Serial_GS232_AZEL + { + get + { + return ((bool)(this["Track_Serial_GS232_AZEL"])); + } + set + { + this["Track_Serial_GS232_AZEL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Track_UDP_AirScout + { + get + { + return ((bool)(this["Track_UDP_AirScout"])); + } + set + { + this["Track_UDP_AirScout"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Track_Serial_None + { + get + { + return ((bool)(this["Track_Serial_None"])); + } + set + { + this["Track_Serial_None"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Track_UDP_None + { + get + { + return ((bool)(this["Track_UDP_None"])); + } + set + { + this["Track_UDP_None"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Track_DDE_None + { + get + { + return ((bool)(this["Track_DDE_None"])); + } + set + { + this["Track_DDE_None"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Track_File_None + { + get + { + return ((bool)(this["Track_File_None"])); + } + set + { + this["Track_File_None"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("9871")] + public int Track_UDP_WinTest_Port + { + get + { + return ((int)(this["Track_UDP_WinTest_Port"])); + } + set + { + this["Track_UDP_WinTest_Port"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("9872")] + public int Track_UDP_AirScout_Port + { + get + { + return ((int)(this["Track_UDP_AirScout_Port"])); + } + set + { + this["Track_UDP_AirScout_Port"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://www.airscout.eu/downloads/donate.html")] + public string Donate_URL + { + get + { + return ((string)(this["Donate_URL"])); + } + set + { + this["Donate_URL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public string Planes_Filter_Max_Circumcircle + { + get + { + return ((string)(this["Planes_Filter_Max_Circumcircle"])); + } + set + { + this["Planes_Filter_Max_Circumcircle"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://www.ngdc.noaa.gov/mgg/topo/DATATILES/elev/")] + public string Elevation_GLOBE_URL + { + get + { + return ((string)(this["Elevation_GLOBE_URL"])); + } + set + { + this["Elevation_GLOBE_URL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://www.ngdc.noaa.gov/mgg/topo/DATATILES/elev/\r\nhttp://www.airscout.eu/downloa" + + "ds/GLOBE")] + public string Elevation_GLOBE_URLs + { + get + { + return ((string)(this["Elevation_GLOBE_URLs"])); + } + set + { + this["Elevation_GLOBE_URLs"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Time_Mode_Online + { + get + { + return ((bool)(this["Time_Mode_Online"])); + } + set + { + this["Time_Mode_Online"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("2015-02-25")] + public global::System.DateTime Time_Offline + { + get + { + return ((global::System.DateTime)(this["Time_Offline"])); + } + set + { + this["Time_Offline"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("airport.png")] + public string Airports_IconFileName + { + get + { + return ((string)(this["Airports_IconFileName"])); + } + set + { + this["Airports_IconFileName"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Airports_Activate + { + get + { + return ((bool)(this["Airports_Activate"])); + } + set + { + this["Airports_Activate"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("\\Database\\Airlines.txt")] + public string Database_Airlines_FileName + { + get + { + return ((string)(this["Database_Airlines_FileName"])); + } + set + { + this["Database_Airlines_FileName"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://www.flugzeuginfo.net/table_airlinecodes_airline_en.php")] + public string Database_Airlines_URL + { + get + { + return ((string)(this["Database_Airlines_URL"])); + } + set + { + this["Database_Airlines_URL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1")] + public int Map_Update + { + get + { + return ((int)(this["Map_Update"])); + } + set + { + this["Map_Update"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Elevation_GLOBE_Enabled + { + get + { + return ((bool)(this["Elevation_GLOBE_Enabled"])); + } + set + { + this["Elevation_GLOBE_Enabled"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://www.airscout.eu/downloads/database/")] + public string Database_Update_URL + { + get + { + return ((string)(this["Database_Update_URL"])); + } + set + { + this["Database_Update_URL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Database_Update_Enabled + { + get + { + return ((bool)(this["Database_Update_Enabled"])); + } + set + { + this["Database_Update_Enabled"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Maximized")] + public global::System.Windows.Forms.FormWindowState General_WindowState + { + get + { + return ((global::System.Windows.Forms.FormWindowState)(this["General_WindowState"])); + } + set + { + this["General_WindowState"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0, 0")] + public global::System.Drawing.Size General_WindowSize + { + get + { + return ((global::System.Drawing.Size)(this["General_WindowSize"])); + } + set + { + this["General_WindowSize"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0, 0")] + public global::System.Drawing.Point General_WindowLocation + { + get + { + return ((global::System.Drawing.Point)(this["General_WindowLocation"])); + } + set + { + this["General_WindowLocation"] = value; + } + } + } +} diff --git a/AirScout/Properties/New.settings b/AirScout/Properties/New.settings new file mode 100644 index 0000000..049245f --- /dev/null +++ b/AirScout/Properties/New.settings @@ -0,0 +1,6 @@ + + + + + + diff --git a/AirScout/Properties/Resources.Designer.cs b/AirScout/Properties/Resources.Designer.cs new file mode 100644 index 0000000..72f5983 --- /dev/null +++ b/AirScout/Properties/Resources.Designer.cs @@ -0,0 +1,103 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.42000 +// +// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn +// der Code erneut generiert wird. +// +//------------------------------------------------------------------------------ + +namespace AirScout.Properties { + using System; + + + /// + /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw. + /// + // Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert + // -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert. + // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen + // mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AirScout.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle + /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Intro { + get { + object obj = ResourceManager.GetObject("Intro", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Map { + get { + object obj = ResourceManager.GetObject("Map", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Map2 { + get { + object obj = ResourceManager.GetObject("Map2", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap question_mark { + get { + object obj = ResourceManager.GetObject("question mark", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/AirScout/Properties/Resources.resx b/AirScout/Properties/Resources.resx new file mode 100644 index 0000000..d423dd7 --- /dev/null +++ b/AirScout/Properties/Resources.resx @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Icons\Map.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Icons\Intro.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Icons\question mark.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Icons\Map2.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/AirScout/Properties/Settings.Designer.cs b/AirScout/Properties/Settings.Designer.cs new file mode 100644 index 0000000..145be61 --- /dev/null +++ b/AirScout/Properties/Settings.Designer.cs @@ -0,0 +1,2117 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.42000 +// +// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn +// der Code erneut generiert wird. +// +//------------------------------------------------------------------------------ + +namespace AirScout.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("DL2ALF")] + public string MyCall { + get { + return ((string)(this["MyCall"])); + } + set { + this["MyCall"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("GB3MHZ")] + public string DXCall { + get { + return ((string)(this["DXCall"])); + } + set { + this["DXCall"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("B1_2G")] + public global::ScoutBase.Core.BAND Band { + get { + return ((global::ScoutBase.Core.BAND)(this["Band"])); + } + set { + this["Band"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("60")] + public int Planes_Update { + get { + return ((int)(this["Planes_Update"])); + } + set { + this["Planes_Update"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("5000")] + public int Planes_MinAlt { + get { + return ((int)(this["Planes_MinAlt"])); + } + set { + this["Planes_MinAlt"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Map_AutoCenter { + get { + return ((bool)(this["Map_AutoCenter"])); + } + set { + this["Map_AutoCenter"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Planes_Draw_Path { + get { + return ((bool)(this["Planes_Draw_Path"])); + } + set { + this["Planes_Draw_Path"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public double Path_GroundClearance { + get { + return ((double)(this["Path_GroundClearance"])); + } + set { + this["Path_GroundClearance"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("12200")] + public int Planes_MaxAlt { + get { + return ((int)(this["Planes_MaxAlt"])); + } + set { + this["Planes_MaxAlt"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("14")] + public double MyHeight { + get { + return ((double)(this["MyHeight"])); + } + set { + this["MyHeight"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("30")] + public double DXHeight { + get { + return ((double)(this["DXHeight"])); + } + set { + this["DXHeight"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("\\Database\\calls.txt")] + public string Calls_FileName { + get { + return ((string)(this["Calls_FileName"])); + } + set { + this["Calls_FileName"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("\\Database\\call3.txt")] + public string WSJTCalls_FileName { + get { + return ((string)(this["WSJTCalls_FileName"])); + } + set { + this["WSJTCalls_FileName"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1000")] + public int Watchlist_MaxCount { + get { + return ((int)(this["Watchlist_MaxCount"])); + } + set { + this["Watchlist_MaxCount"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public int Planes_Filter_Min_Alt { + get { + return ((int)(this["Planes_Filter_Min_Alt"])); + } + set { + this["Planes_Filter_Min_Alt"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("NONE")] + public global::AirScout.Aircrafts.PLANECATEGORY Planes_Filter_Min_Category { + get { + return ((global::AirScout.Aircrafts.PLANECATEGORY)(this["Planes_Filter_Min_Category"])); + } + set { + this["Planes_Filter_Min_Category"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-15")] + public double MinLon { + get { + return ((double)(this["MinLon"])); + } + set { + this["MinLon"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("30")] + public double MaxLon { + get { + return ((double)(this["MaxLon"])); + } + set { + this["MaxLon"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("35")] + public double MinLat { + get { + return ((double)(this["MinLat"])); + } + set { + this["MinLat"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("60")] + public double MaxLat { + get { + return ((double)(this["MaxLat"])); + } + set { + this["MaxLat"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("50.937067")] + public double MyLat { + get { + return ((double)(this["MyLat"])); + } + set { + this["MyLat"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("10.68327")] + public double MyLon { + get { + return ((double)(this["MyLon"])); + } + set { + this["MyLon"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("52.05626084")] + public double DXLat { + get { + return ((double)(this["DXLat"])); + } + set { + this["DXLat"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1.28022909")] + public double DXLon { + get { + return ((double)(this["DXLon"])); + } + set { + this["DXLon"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("23")] + public double DXElevation { + get { + return ((double)(this["DXElevation"])); + } + set { + this["DXElevation"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("328")] + public double MyElevation { + get { + return ((double)(this["MyElevation"])); + } + set { + this["MyElevation"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Elevation_SRTM3_Enabled { + get { + return ((bool)(this["Elevation_SRTM3_Enabled"])); + } + set { + this["Elevation_SRTM3_Enabled"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Elevation_SRTM1_Enabled { + get { + return ((bool)(this["Elevation_SRTM1_Enabled"])); + } + set { + this["Elevation_SRTM1_Enabled"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://www.qrz.com/db/")] + public string QRZ_URL_Database { + get { + return ((string)(this["QRZ_URL_Database"])); + } + set { + this["QRZ_URL_Database"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("100")] + public double Path_NearFieldSuppression { + get { + return ((double)(this["Path_NearFieldSuppression"])); + } + set { + this["Path_NearFieldSuppression"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1")] + public int Path_History_StepWidth { + get { + return ((int)(this["Path_History_StepWidth"])); + } + set { + this["Path_History_StepWidth"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Alarm_BringWindowToFront { + get { + return ((bool)(this["Alarm_BringWindowToFront"])); + } + set { + this["Alarm_BringWindowToFront"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Alarm_PlaySound { + get { + return ((bool)(this["Alarm_PlaySound"])); + } + set { + this["Alarm_PlaySound"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("100")] + public double Alarm_Distance { + get { + return ((double)(this["Alarm_Distance"])); + } + set { + this["Alarm_Distance"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Alarm_Activate { + get { + return ((bool)(this["Alarm_Activate"])); + } + set { + this["Alarm_Activate"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Database\\localobstructions.txt")] + public string LocalObstruction_FileName { + get { + return ((string)(this["LocalObstruction_FileName"])); + } + set { + this["LocalObstruction_FileName"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Path_Cubic_Spline { + get { + return ((bool)(this["Path_Cubic_Spline"])); + } + set { + this["Path_Cubic_Spline"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Horizon_Plot_Cartesian { + get { + return ((bool)(this["Horizon_Plot_Cartesian"])); + } + set { + this["Horizon_Plot_Cartesian"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Horizon_Plot_Polar { + get { + return ((bool)(this["Horizon_Plot_Polar"])); + } + set { + this["Horizon_Plot_Polar"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("9871")] + public int WinTest_Port { + get { + return ((int)(this["WinTest_Port"])); + } + set { + this["WinTest_Port"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("9872")] + public int Server_Port { + get { + return ((int)(this["Server_Port"])); + } + set { + this["Server_Port"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Path_BestCaseElevation { + get { + return ((bool)(this["Path_BestCaseElevation"])); + } + set { + this["Path_BestCaseElevation"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("\\Icon")] + public string Icon_Directory { + get { + return ((string)(this["Icon_Directory"])); + } + set { + this["Icon_Directory"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("plane.png")] + public string Planes_IconFileName { + get { + return ((string)(this["Planes_IconFileName"])); + } + set { + this["Planes_IconFileName"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("[none]")] + public string Planes_PlaneFeed2 { + get { + return ((string)(this["Planes_PlaneFeed2"])); + } + set { + this["Planes_PlaneFeed2"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("[none]")] + public string Planes_PlaneFeed3 { + get { + return ((string)(this["Planes_PlaneFeed3"])); + } + set { + this["Planes_PlaneFeed3"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("[WebFeed] Virtual Radar Server")] + public string Planes_PlaneFeed1 { + get { + return ((string)(this["Planes_PlaneFeed1"])); + } + set { + this["Planes_PlaneFeed1"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("5")] + public int Planes_Position_TTL { + get { + return ((int)(this["Planes_Position_TTL"])); + } + set { + this["Planes_Position_TTL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Server_Activate { + get { + return ((bool)(this["Server_Activate"])); + } + set { + this["Server_Activate"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("AS")] + public string Server_Name { + get { + return ((string)(this["Server_Name"])); + } + set { + this["Server_Name"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("OpenStreetMap")] + public string Map_Provider { + get { + return ((string)(this["Map_Provider"])); + } + set { + this["Map_Provider"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string Map_Provider_Accepted { + get { + return ((string)(this["Map_Provider_Accepted"])); + } + set { + this["Map_Provider_Accepted"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Microsoft Sans Serif; 11;Bold")] + public string Map_ToolTipFont { + get { + return ((string)(this["Map_ToolTipFont"])); + } + set { + this["Map_ToolTipFont"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://localhost:10080/")] + public string SpecLab_URL { + get { + return ((string)(this["SpecLab_URL"])); + } + set { + this["SpecLab_URL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1")] + public double SpecLab_Update { + get { + return ((double)(this["SpecLab_Update"])); + } + set { + this["SpecLab_Update"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("_fft_expt.json")] + public string SpecLab_FileName { + get { + return ((string)(this["SpecLab_FileName"])); + } + set { + this["SpecLab_FileName"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("400")] + public int SpecLab_F1 { + get { + return ((int)(this["SpecLab_F1"])); + } + set { + this["SpecLab_F1"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1600")] + public int SpecLab_F2 { + get { + return ((int)(this["SpecLab_F2"])); + } + set { + this["SpecLab_F2"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool SpecLab_Enabled { + get { + return ((bool)(this["SpecLab_Enabled"])); + } + set { + this["SpecLab_Enabled"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool InfoWin_Metric { + get { + return ((bool)(this["InfoWin_Metric"])); + } + set { + this["InfoWin_Metric"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool InfoWin_Imperial { + get { + return ((bool)(this["InfoWin_Imperial"])); + } + set { + this["InfoWin_Imperial"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool InfoWin_Position { + get { + return ((bool)(this["InfoWin_Position"])); + } + set { + this["InfoWin_Position"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool InfoWin_Time { + get { + return ((bool)(this["InfoWin_Time"])); + } + set { + this["InfoWin_Time"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool InfoWin_Epsilon { + get { + return ((bool)(this["InfoWin_Epsilon"])); + } + set { + this["InfoWin_Epsilon"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool InfoWin_Squint { + get { + return ((bool)(this["InfoWin_Squint"])); + } + set { + this["InfoWin_Squint"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool InfoWin_Track { + get { + return ((bool)(this["InfoWin_Track"])); + } + set { + this["InfoWin_Track"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool InfoWin_Type { + get { + return ((bool)(this["InfoWin_Type"])); + } + set { + this["InfoWin_Type"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool InfoWin_Alt { + get { + return ((bool)(this["InfoWin_Alt"])); + } + set { + this["InfoWin_Alt"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool InfoWin_Dist { + get { + return ((bool)(this["InfoWin_Dist"])); + } + set { + this["InfoWin_Dist"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool InfoWin_Speed { + get { + return ((bool)(this["InfoWin_Speed"])); + } + set { + this["InfoWin_Speed"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool InfoWin_Angle { + get { + return ((bool)(this["InfoWin_Angle"])); + } + set { + this["InfoWin_Angle"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("HTTP://WWW.AIRSCOUT.EU/DOWNLOADS/AIRSCOUT.PWD")] + public string SFTP_PwdURL { + get { + return ((string)(this["SFTP_PwdURL"])); + } + set { + this["SFTP_PwdURL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool First_Agree { + get { + return ((bool)(this["First_Agree"])); + } + set { + this["First_Agree"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool First_Disagree { + get { + return ((bool)(this["First_Disagree"])); + } + set { + this["First_Disagree"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool First_Privacy { + get { + return ((bool)(this["First_Privacy"])); + } + set { + this["First_Privacy"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool FirstRun { + get { + return ((bool)(this["FirstRun"])); + } + set { + this["FirstRun"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Map_ShowZoomBox { + get { + return ((bool)(this["Map_ShowZoomBox"])); + } + set { + this["Map_ShowZoomBox"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Map_ShowFilterBox { + get { + return ((bool)(this["Map_ShowFilterBox"])); + } + set { + this["Map_ShowFilterBox"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Map_ShowAlarmBox { + get { + return ((bool)(this["Map_ShowAlarmBox"])); + } + set { + this["Map_ShowAlarmBox"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Map_ShowInfoBox { + get { + return ((bool)(this["Map_ShowInfoBox"])); + } + set { + this["Map_ShowInfoBox"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public int MainSplitter_Distance { + get { + return ((int)(this["MainSplitter_Distance"])); + } + set { + this["MainSplitter_Distance"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public int MapSplitter_Distance { + get { + return ((int)(this["MapSplitter_Distance"])); + } + set { + this["MapSplitter_Distance"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Track_Activate { + get { + return ((bool)(this["Track_Activate"])); + } + set { + this["Track_Activate"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public double Track_SetAz { + get { + return ((double)(this["Track_SetAz"])); + } + set { + this["Track_SetAz"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public double Track_SetEl { + get { + return ((double)(this["Track_SetEl"])); + } + set { + this["Track_SetEl"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("16")] + public int Planes_IconSize_L { + get { + return ((int)(this["Planes_IconSize_L"])); + } + set { + this["Planes_IconSize_L"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("24")] + public int Planes_IconSize_M { + get { + return ((int)(this["Planes_IconSize_M"])); + } + set { + this["Planes_IconSize_M"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("36")] + public int Planes_IconSize_H { + get { + return ((int)(this["Planes_IconSize_H"])); + } + set { + this["Planes_IconSize_H"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("48")] + public int Planes_IconSize_S { + get { + return ((int)(this["Planes_IconSize_S"])); + } + set { + this["Planes_IconSize_S"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("9880")] + public int Webserver_Port { + get { + return ((int)(this["Webserver_Port"])); + } + set { + this["Webserver_Port"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("01/31/2015 15:21:00")] + public global::System.DateTime News_LastUpdate { + get { + return ((global::System.DateTime)(this["News_LastUpdate"])); + } + set { + this["News_LastUpdate"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("3600")] + public int News_Interval { + get { + return ((int)(this["News_Interval"])); + } + set { + this["News_Interval"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://www.airscout.eu/news.html")] + public global::System.Uri News_URL { + get { + return ((global::System.Uri)(this["News_URL"])); + } + set { + this["News_URL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Track_UDP_WinTest { + get { + return ((bool)(this["Track_UDP_WinTest"])); + } + set { + this["Track_UDP_WinTest"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Track_DDE_HRD { + get { + return ((bool)(this["Track_DDE_HRD"])); + } + set { + this["Track_DDE_HRD"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("COM13")] + public string Track_Serial_Port { + get { + return ((string)(this["Track_Serial_Port"])); + } + set { + this["Track_Serial_Port"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("4800")] + public int Track_Serial_Baudrate { + get { + return ((int)(this["Track_Serial_Baudrate"])); + } + set { + this["Track_Serial_Baudrate"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Track_Serial_GS232_AZ { + get { + return ((bool)(this["Track_Serial_GS232_AZ"])); + } + set { + this["Track_Serial_GS232_AZ"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Track_File_WSJT { + get { + return ((bool)(this["Track_File_WSJT"])); + } + set { + this["Track_File_WSJT"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Track_File_Native { + get { + return ((bool)(this["Track_File_Native"])); + } + set { + this["Track_File_Native"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Track_Serial_GS232_AZEL { + get { + return ((bool)(this["Track_Serial_GS232_AZEL"])); + } + set { + this["Track_Serial_GS232_AZEL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Track_UDP_AirScout { + get { + return ((bool)(this["Track_UDP_AirScout"])); + } + set { + this["Track_UDP_AirScout"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Track_Serial_None { + get { + return ((bool)(this["Track_Serial_None"])); + } + set { + this["Track_Serial_None"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Track_UDP_None { + get { + return ((bool)(this["Track_UDP_None"])); + } + set { + this["Track_UDP_None"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Track_DDE_None { + get { + return ((bool)(this["Track_DDE_None"])); + } + set { + this["Track_DDE_None"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Track_File_None { + get { + return ((bool)(this["Track_File_None"])); + } + set { + this["Track_File_None"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("9871")] + public int Track_UDP_WinTest_Port { + get { + return ((int)(this["Track_UDP_WinTest_Port"])); + } + set { + this["Track_UDP_WinTest_Port"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("9872")] + public int Track_UDP_AirScout_Port { + get { + return ((int)(this["Track_UDP_AirScout_Port"])); + } + set { + this["Track_UDP_AirScout_Port"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://www.airscout.eu/downloads/donate.html")] + public string Donate_URL { + get { + return ((string)(this["Donate_URL"])); + } + set { + this["Donate_URL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public int Planes_Filter_Max_Circumcircle { + get { + return ((int)(this["Planes_Filter_Max_Circumcircle"])); + } + set { + this["Planes_Filter_Max_Circumcircle"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Time_Mode_Online { + get { + return ((bool)(this["Time_Mode_Online"])); + } + set { + this["Time_Mode_Online"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("2015-02-25")] + public global::System.DateTime Time_Offline { + get { + return ((global::System.DateTime)(this["Time_Offline"])); + } + set { + this["Time_Offline"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("airport.png")] + public string Airports_IconFileName { + get { + return ((string)(this["Airports_IconFileName"])); + } + set { + this["Airports_IconFileName"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Airports_Activate { + get { + return ((bool)(this["Airports_Activate"])); + } + set { + this["Airports_Activate"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("\\Database\\Airlines.txt")] + public string Database_Airlines_FileName { + get { + return ((string)(this["Database_Airlines_FileName"])); + } + set { + this["Database_Airlines_FileName"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://www.flugzeuginfo.net/table_airlinecodes_airline_en.php")] + public string Database_Airlines_URL { + get { + return ((string)(this["Database_Airlines_URL"])); + } + set { + this["Database_Airlines_URL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1")] + public int Map_Update { + get { + return ((int)(this["Map_Update"])); + } + set { + this["Map_Update"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Elevation_GLOBE_Enabled { + get { + return ((bool)(this["Elevation_GLOBE_Enabled"])); + } + set { + this["Elevation_GLOBE_Enabled"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://www.airscout.eu/downloads/database/")] + public string Database_Update_URL { + get { + return ((string)(this["Database_Update_URL"])); + } + set { + this["Database_Update_URL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Background_Update_OnStartup { + get { + return ((bool)(this["Background_Update_OnStartup"])); + } + set { + this["Background_Update_OnStartup"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Maximized")] + public global::System.Windows.Forms.FormWindowState General_WindowState { + get { + return ((global::System.Windows.Forms.FormWindowState)(this["General_WindowState"])); + } + set { + this["General_WindowState"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0, 0")] + public global::System.Drawing.Size General_WindowSize { + get { + return ((global::System.Drawing.Size)(this["General_WindowSize"])); + } + set { + this["General_WindowSize"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0, 0")] + public global::System.Drawing.Point General_WindowLocation { + get { + return ((global::System.Drawing.Point)(this["General_WindowLocation"])); + } + set { + this["General_WindowLocation"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Background_Update_Periodically { + get { + return ((bool)(this["Background_Update_Periodically"])); + } + set { + this["Background_Update_Periodically"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("60")] + public decimal Background_Update_Period { + get { + return ((decimal)(this["Background_Update_Period"])); + } + set { + this["Background_Update_Period"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("\\Tmp")] + public string Tmp_Directory { + get { + return ((string)(this["Tmp_Directory"])); + } + set { + this["Tmp_Directory"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("\\Log")] + public string Log_Directory { + get { + return ((string)(this["Log_Directory"])); + } + set { + this["Log_Directory"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("\\Database")] + public string Database_Directory { + get { + return ((string)(this["Database_Directory"])); + } + set { + this["Database_Directory"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("10")] + public decimal Locator_MaxLength { + get { + return ((decimal)(this["Locator_MaxLength"])); + } + set { + this["Locator_MaxLength"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Locator_AutoLength { + get { + return ((bool)(this["Locator_AutoLength"])); + } + set { + this["Locator_AutoLength"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Locator_SmallLettersForSubsquares { + get { + return ((bool)(this["Locator_SmallLettersForSubsquares"])); + } + set { + this["Locator_SmallLettersForSubsquares"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("\\ElevationData")] + public string Elevation_Directory { + get { + return ((string)(this["Elevation_Directory"])); + } + set { + this["Elevation_Directory"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public global::AirScout.Watchlist Watchlist { + get { + return ((global::AirScout.Watchlist)(this["Watchlist"])); + } + set { + this["Watchlist"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://www.airscout.eu")] + public string Connectivity_URL { + get { + return ((string)(this["Connectivity_URL"])); + } + set { + this["Connectivity_URL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Watchlist_SyncWithKST { + get { + return ((bool)(this["Watchlist_SyncWithKST"])); + } + set { + this["Watchlist_SyncWithKST"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Watchlist_Activated { + get { + return ((bool)(this["Watchlist_Activated"])); + } + set { + this["Watchlist_Activated"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://history.adsbexchange.com/Aircraftlist.json/")] + public string Analysis_History_URL { + get { + return ((string)(this["Analysis_History_URL"])); + } + set { + this["Analysis_History_URL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string Analysis_History_Directory { + get { + return ((string)(this["Analysis_History_Directory"])); + } + set { + this["Analysis_History_Directory"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("2017-07-01")] + public global::System.DateTime Analysis_History_Date { + get { + return ((global::System.DateTime)(this["Analysis_History_Date"])); + } + set { + this["Analysis_History_Date"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string Analysis_History_ZIPFileName { + get { + return ((string)(this["Analysis_History_ZIPFileName"])); + } + set { + this["Analysis_History_ZIPFileName"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("5")] + public decimal Analysis_History_Stepwidth { + get { + return ((decimal)(this["Analysis_History_Stepwidth"])); + } + set { + this["Analysis_History_Stepwidth"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string Analysis_History_LoadFileName { + get { + return ((string)(this["Analysis_History_LoadFileName"])); + } + set { + this["Analysis_History_LoadFileName"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Horizon_Plot_Map { + get { + return ((bool)(this["Horizon_Plot_Map"])); + } + set { + this["Horizon_Plot_Map"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string Version { + get { + return ((string)(this["Version"])); + } + set { + this["Version"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Elevation_GLOBE_EnableCache { + get { + return ((bool)(this["Elevation_GLOBE_EnableCache"])); + } + set { + this["Elevation_GLOBE_EnableCache"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Elevation_SRTM3_EnableCache { + get { + return ((bool)(this["Elevation_SRTM3_EnableCache"])); + } + set { + this["Elevation_SRTM3_EnableCache"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Elevation_SRTM1_EnableCache { + get { + return ((bool)(this["Elevation_SRTM1_EnableCache"])); + } + set { + this["Elevation_SRTM1_EnableCache"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute(@" +Maidenhead locator system elvation tiles calculated by DL2ALF based on elevation data of the GLOBE project: + +GLOBE Task Team and others (Hastings, David A., Paula K. Dunbar, Gerald M. Elphingstone, Mark Bootz, Hiroshi Murakami, Hiroshi Maruyama, +Hiroshi Masaharu, Peter Holland, John Payne, Nevin A. Bryant, Thomas L. Logan, J.-P. Muller, Gunter Schreier, and John S. MacDonald), eds., 1999. + +The Global Land One-kilometer Base Elevation (GLOBE) Digital Elevation Model, +Version 1.0. National Oceanic and Atmospheric Administration, National Geophysical Data Center, 325 Broadway, Boulder, Colorado 80305-3328, U.S.A. + +Digital data base on the World Wide Web (URL: http://www.ngdc.noaa.gov/mgg/topo/globe.html) and CD-ROMs. ")] + public string Elevation_GLOBE_Copyright { + get { + return ((string)(this["Elevation_GLOBE_Copyright"])); + } + set { + this["Elevation_GLOBE_Copyright"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("\r\nMaidenhead locator system elvation tiles calculated by DL2ALF based on elevatio" + + "n data of the SRTM project:\r\n\r\nThis material is based on SRTM3 data V3.0 service" + + "s provided by the OpenTopography Facility with support from the National Science" + + " Foundation under NSF Award Numbers 1226353 & 1225810\r\nhttp://opentopo.sdsc.edu/" + + "raster?opentopoID=OTSRTM.042013.4326.1\r\n\r\nThe Shuttle Radar Topography Mission (" + + "SRTM) obtained elevation data on a near-global scale to generate the most comple" + + "te high-resolution digital topographic database of Earth. SRTM consisted of a sp" + + "ecially modified radar system that flew onboard the Space Shuttle Endeavour duri" + + "ng an 11-day mission in February of 2000. SRTM is an international project spear" + + "headed by the National Geospatial-Intelligence Agency (NGA) and the National Aer" + + "onautics and Space Administration (NASA).\r\n\r\nVersion 3: Elimination of the voids" + + " in the NASA SRTM DEM was the primary goal of a project under the NASA MEaSUREs " + + "(Making Earth System Data Records for Use in Research Environments) Program. Ult" + + "imately this was achieved by filling the voids with elevation data primarily fro" + + "m the ASTER GDEM2 (Global Digital Elevation Model Version 2) and secondarily fro" + + "m the USGS GMTED2010 elevation model or the USGS National Elevation Dataset (NED" + + "). For more information on this dataset visit the LP DAAC NASA Shuttle Radar Top" + + "ography Mission Global 3 arc second page.\r\n\r\nASTER GDEM is a product of NASA and" + + " METI. See https://lpdaac.usgs.gov/dataset_discovery/aster\r\n\r\nMEaSUREs data and " + + "products are available at no charge from the LP DAAC.See https://lpdaac.usgs.gov" + + "/dataset_discovery/measures\r\n\r\n\r\n\r\n\r\n\r\n")] + public string Elevation_SRTM3_Copyright { + get { + return ((string)(this["Elevation_SRTM3_Copyright"])); + } + set { + this["Elevation_SRTM3_Copyright"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("\r\nMaidenhead locator system elvation tiles calculated by DL2ALF based on elevatio" + + "n data of the SRTM project:\r\n\r\nThis material is based on SRTM1 data V3.0 service" + + "s provided by the OpenTopography Facility with support from the National Science" + + " Foundation under NSF Award Numbers 1226353 & 1225810\r\nhttp://opentopo.sdsc.edu/" + + "raster?opentopoID=OTSRTM.082015.4326.1\r\n\r\nThe Shuttle Radar Topography Mission (" + + "SRTM) obtained elevation data on a near-global scale to generate the most comple" + + "te high-resolution digital topographic database of Earth. SRTM consisted of a sp" + + "ecially modified radar system that flew onboard the Space Shuttle Endeavour duri" + + "ng an 11-day mission in February of 2000. SRTM is an international project spear" + + "headed by the National Geospatial-Intelligence Agency (NGA) and the National Aer" + + "onautics and Space Administration (NASA).\r\nVersion 3: Elimination of the voids i" + + "n the NASA SRTM DEM was the primary goal of a project under the NASA MEaSUREs (M" + + "aking Earth System Data Records for Use in Research Environments) Program. Ultim" + + "ately this was achieved by filling the voids with elevation data primarily from " + + "the ASTER GDEM2 (Global Digital Elevation Model Version 2) and secondarily from " + + "the USGS GMTED2010 elevation model or the USGS National Elevation Dataset (NED)." + + " NASA SRTM V3.0 three-arc-second data are provided in two forms: (1) by three-by" + + "-three averaging of the one arc-second samples, and (2) by extracting the middle" + + " sample of those same three-by-three samples. For more information on this datas" + + "et visit the LP DAAC NASA Shuttle Radar Topography Mission Global 1 arc second p" + + "age.\r\nhttps://lpdaac.usgs.gov/dataset_discovery/measures/measures_products_table" + + "/srtmgl1\r\n\r\nASTER GDEM is a product of NASA and METI. See https://lpdaac.usgs.go" + + "v/dataset_discovery/aster\r\n\r\nMEaSUREs data and products are available at no char" + + "ge from the LP DAAC.See https://lpdaac.usgs.gov/dataset_discovery/measures\r\n\r\n\r\n" + + "\r\n\r\n")] + public string Elevation_SRTM1_Copyright { + get { + return ((string)(this["Elevation_SRTM1_Copyright"])); + } + set { + this["Elevation_SRTM1_Copyright"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1000")] + public double Path_MaxLength { + get { + return ((double)(this["Path_MaxLength"])); + } + set { + this["Path_MaxLength"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Background_Calculations_Enable { + get { + return ((bool)(this["Background_Calculations_Enable"])); + } + set { + this["Background_Calculations_Enable"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("UNDEFINED")] + public global::System.Data.SQLite.DATABASESTATUS StationsDatabase_Status { + get { + return ((global::System.Data.SQLite.DATABASESTATUS)(this["StationsDatabase_Status"])); + } + set { + this["StationsDatabase_Status"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public global::AirScout.Core.BandSettings Path_Band_Settings { + get { + return ((global::AirScout.Core.BandSettings)(this["Path_Band_Settings"])); + } + set { + this["Path_Band_Settings"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public int Background_Calculations_ThreadWait { + get { + return ((int)(this["Background_Calculations_ThreadWait"])); + } + set { + this["Background_Calculations_ThreadWait"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string Password { + get { + return ((string)(this["Password"])); + } + set { + this["Password"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("DL2ALF")] + public global::System.Collections.Generic.List MyCalls { + get { + return ((global::System.Collections.Generic.List)(this["MyCalls"])); + } + set { + this["MyCalls"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Map_SmallMarkers { + get { + return ((bool)(this["Map_SmallMarkers"])); + } + set { + this["Map_SmallMarkers"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("NONE")] + public global::ScoutBase.Elevation.ELEVATIONMODEL ElevationModel { + get { + return ((global::ScoutBase.Elevation.ELEVATIONMODEL)(this["ElevationModel"])); + } + set { + this["ElevationModel"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("UNDEFINED")] + public global::System.Data.SQLite.DATABASESTATUS AircraftDatabase_Status { + get { + return ((global::System.Data.SQLite.DATABASESTATUS)(this["AircraftDatabase_Status"])); + } + set { + this["AircraftDatabase_Status"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("500")] + public double Horizon_MaxDist { + get { + return ((double)(this["Horizon_MaxDist"])); + } + set { + this["Horizon_MaxDist"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("UNDEFINED")] + public global::System.Data.SQLite.DATABASESTATUS Elevation_GLOBE_DatabaseStatus { + get { + return ((global::System.Data.SQLite.DATABASESTATUS)(this["Elevation_GLOBE_DatabaseStatus"])); + } + set { + this["Elevation_GLOBE_DatabaseStatus"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("UNDEFINED")] + public global::System.Data.SQLite.DATABASESTATUS Elevation_SRTM3_DatabaseStatus { + get { + return ((global::System.Data.SQLite.DATABASESTATUS)(this["Elevation_SRTM3_DatabaseStatus"])); + } + set { + this["Elevation_SRTM3_DatabaseStatus"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("UNDEFINED")] + public global::System.Data.SQLite.DATABASESTATUS Elevation_SRTM1_DatabaseStatus { + get { + return ((global::System.Data.SQLite.DATABASESTATUS)(this["Elevation_SRTM1_DatabaseStatus"])); + } + set { + this["Elevation_SRTM1_DatabaseStatus"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1500")] + public decimal AircraftDatabase_MaxSize { + get { + return ((decimal)(this["AircraftDatabase_MaxSize"])); + } + set { + this["AircraftDatabase_MaxSize"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("7")] + public decimal AircraftDatabase_MaxDaysLifetime { + get { + return ((decimal)(this["AircraftDatabase_MaxDaysLifetime"])); + } + set { + this["AircraftDatabase_MaxDaysLifetime"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("10000000")] + public decimal AircraftDatabase_MaxCount { + get { + return ((decimal)(this["AircraftDatabase_MaxCount"])); + } + set { + this["AircraftDatabase_MaxCount"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Analysis_CrossingHistory_WithSignalLevel { + get { + return ((bool)(this["Analysis_CrossingHistory_WithSignalLevel"])); + } + set { + this["Analysis_CrossingHistory_WithSignalLevel"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("10")] + public decimal Analysis_CrossingHistory_AmbiguousGap { + get { + return ((decimal)(this["Analysis_CrossingHistory_AmbiguousGap"])); + } + set { + this["Analysis_CrossingHistory_AmbiguousGap"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Planes_KeepHistory { + get { + return ((bool)(this["Planes_KeepHistory"])); + } + set { + this["Planes_KeepHistory"] = value; + } + } + } +} diff --git a/AirScout/Properties/Settings.settings b/AirScout/Properties/Settings.settings new file mode 100644 index 0000000..bfa1c00 --- /dev/null +++ b/AirScout/Properties/Settings.settings @@ -0,0 +1,560 @@ + + + + + + DL2ALF + + + GB3MHZ + + + B1_2G + + + 60 + + + 5000 + + + True + + + True + + + 0 + + + 12200 + + + 14 + + + 30 + + + \Database\calls.txt + + + \Database\call3.txt + + + 1000 + + + 0 + + + NONE + + + -15 + + + 30 + + + 35 + + + 60 + + + 50.937067 + + + 10.68327 + + + 52.05626084 + + + 1.28022909 + + + 23 + + + 328 + + + False + + + False + + + http://www.qrz.com/db/ + + + 100 + + + 1 + + + True + + + True + + + 100 + + + False + + + Database\localobstructions.txt + + + True + + + False + + + False + + + 9871 + + + 9872 + + + True + + + \Icon + + + plane.png + + + [none] + + + [none] + + + [WebFeed] Virtual Radar Server + + + 5 + + + False + + + AS + + + OpenStreetMap + + + + + + Microsoft Sans Serif; 11;Bold + + + http://localhost:10080/ + + + 1 + + + _fft_expt.json + + + 400 + + + 1600 + + + False + + + True + + + False + + + True + + + True + + + False + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + HTTP://WWW.AIRSCOUT.EU/DOWNLOADS/AIRSCOUT.PWD + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + -1 + + + -1 + + + False + + + 0 + + + 0 + + + 16 + + + 24 + + + 36 + + + 48 + + + 9880 + + + 01/31/2015 15:21:00 + + + 3600 + + + http://www.airscout.eu/news.html + + + False + + + False + + + COM13 + + + 4800 + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + 9871 + + + 9872 + + + http://www.airscout.eu/downloads/donate.html + + + 0 + + + True + + + 2015-02-25 + + + airport.png + + + True + + + \Database\Airlines.txt + + + http://www.flugzeuginfo.net/table_airlinecodes_airline_en.php + + + 1 + + + True + + + http://www.airscout.eu/downloads/database/ + + + True + + + Maximized + + + 0, 0 + + + 0, 0 + + + False + + + 60 + + + \Tmp + + + \Log + + + \Database + + + 10 + + + True + + + False + + + \ElevationData + + + + + + http://www.airscout.eu + + + False + + + True + + + http://history.adsbexchange.com/Aircraftlist.json/ + + + + + + 2017-07-01 + + + + + + 5 + + + + + + False + + + + + + True + + + False + + + False + + + +Maidenhead locator system elvation tiles calculated by DL2ALF based on elevation data of the GLOBE project: + +GLOBE Task Team and others (Hastings, David A., Paula K. Dunbar, Gerald M. Elphingstone, Mark Bootz, Hiroshi Murakami, Hiroshi Maruyama, +Hiroshi Masaharu, Peter Holland, John Payne, Nevin A. Bryant, Thomas L. Logan, J.-P. Muller, Gunter Schreier, and John S. MacDonald), eds., 1999. + +The Global Land One-kilometer Base Elevation (GLOBE) Digital Elevation Model, +Version 1.0. National Oceanic and Atmospheric Administration, National Geophysical Data Center, 325 Broadway, Boulder, Colorado 80305-3328, U.S.A. + +Digital data base on the World Wide Web (URL: http://www.ngdc.noaa.gov/mgg/topo/globe.html) and CD-ROMs. + + + +Maidenhead locator system elvation tiles calculated by DL2ALF based on elevation data of the SRTM project: + +This material is based on SRTM3 data V3.0 services provided by the OpenTopography Facility with support from the National Science Foundation under NSF Award Numbers 1226353 & 1225810 +http://opentopo.sdsc.edu/raster?opentopoID=OTSRTM.042013.4326.1 + +The Shuttle Radar Topography Mission (SRTM) obtained elevation data on a near-global scale to generate the most complete high-resolution digital topographic database of Earth. SRTM consisted of a specially modified radar system that flew onboard the Space Shuttle Endeavour during an 11-day mission in February of 2000. SRTM is an international project spearheaded by the National Geospatial-Intelligence Agency (NGA) and the National Aeronautics and Space Administration (NASA). + +Version 3: Elimination of the voids in the NASA SRTM DEM was the primary goal of a project under the NASA MEaSUREs (Making Earth System Data Records for Use in Research Environments) Program. Ultimately this was achieved by filling the voids with elevation data primarily from the ASTER GDEM2 (Global Digital Elevation Model Version 2) and secondarily from the USGS GMTED2010 elevation model or the USGS National Elevation Dataset (NED). For more information on this dataset visit the LP DAAC NASA Shuttle Radar Topography Mission Global 3 arc second page. + +ASTER GDEM is a product of NASA and METI. See https://lpdaac.usgs.gov/dataset_discovery/aster + +MEaSUREs data and products are available at no charge from the LP DAAC.See https://lpdaac.usgs.gov/dataset_discovery/measures + + + + + + + + + +Maidenhead locator system elvation tiles calculated by DL2ALF based on elevation data of the SRTM project: + +This material is based on SRTM1 data V3.0 services provided by the OpenTopography Facility with support from the National Science Foundation under NSF Award Numbers 1226353 & 1225810 +http://opentopo.sdsc.edu/raster?opentopoID=OTSRTM.082015.4326.1 + +The Shuttle Radar Topography Mission (SRTM) obtained elevation data on a near-global scale to generate the most complete high-resolution digital topographic database of Earth. SRTM consisted of a specially modified radar system that flew onboard the Space Shuttle Endeavour during an 11-day mission in February of 2000. SRTM is an international project spearheaded by the National Geospatial-Intelligence Agency (NGA) and the National Aeronautics and Space Administration (NASA). +Version 3: Elimination of the voids in the NASA SRTM DEM was the primary goal of a project under the NASA MEaSUREs (Making Earth System Data Records for Use in Research Environments) Program. Ultimately this was achieved by filling the voids with elevation data primarily from the ASTER GDEM2 (Global Digital Elevation Model Version 2) and secondarily from the USGS GMTED2010 elevation model or the USGS National Elevation Dataset (NED). NASA SRTM V3.0 three-arc-second data are provided in two forms: (1) by three-by-three averaging of the one arc-second samples, and (2) by extracting the middle sample of those same three-by-three samples. For more information on this dataset visit the LP DAAC NASA Shuttle Radar Topography Mission Global 1 arc second page. +https://lpdaac.usgs.gov/dataset_discovery/measures/measures_products_table/srtmgl1 + +ASTER GDEM is a product of NASA and METI. See https://lpdaac.usgs.gov/dataset_discovery/aster + +MEaSUREs data and products are available at no charge from the LP DAAC.See https://lpdaac.usgs.gov/dataset_discovery/measures + + + + + + + + 1000 + + + True + + + UNDEFINED + + + + + + 0 + + + + + + DL2ALF + + + False + + + NONE + + + UNDEFINED + + + 500 + + + UNDEFINED + + + UNDEFINED + + + UNDEFINED + + + 1500 + + + 7 + + + 10000000 + + + False + + + 10 + + + False + + + \ No newline at end of file diff --git a/AirScout/Properties/app.manifest b/AirScout/Properties/app.manifest new file mode 100644 index 0000000..515d2e6 --- /dev/null +++ b/AirScout/Properties/app.manifest @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AirScout/Readme_Linux.txt b/AirScout/Readme_Linux.txt new file mode 100644 index 0000000..909c1db --- /dev/null +++ b/AirScout/Readme_Linux.txt @@ -0,0 +1,57 @@ +************************************************************************************** +AirScout Linux compatibility +Last modified: 2018-04-14 +************************************************************************************** + ++++ INSTALLATION + +This software is running on 32bit/64bit - Linux with the help of the Mono runtime environment without any modifications. +There is no special installation package or archive to download. + +Nevertheless some precautions are necessary depending on the your Linux system. +Linux support remains experimental, I simply cannot verify all existing configurations. +AirScout was tested with a 64bit SUSE Linux (Leap 42.3) with KDE-Desktop and Mono 4.6.1 in a VMWare virtual machine. + +To get ist running on your system, please follow the steps below + +1. Check if your version of Linux is up to date +2. Install the full Mono package by running: + + sudo apt-get install mono-complete (on most systems) + + or + + sudo zypper install mono-complete (on SUSE Linux) + +3. Unzip archive or simple copy all unpacked files in a directory of your choice, e.g. /home/[Linux Username]/AirScout +4. Open a terminal window there +5. Run AirScout by typing "mono AirScout.exe" +6. Have fun with AirScout running on Linux with 99% functionality + + ++++ KNOWN BUGS AND LIMITATIONS + +- Web browser window not working +- Main window's last location, size and state cannot be saved properly --> AirScout is always starting maximized +- Splitter position cannot be saved properly --> use default splitter positions +- Frameless splash window on startup does not work --> show it with frame +- Startup Wizard cannot be shown with Aero design --> show it in standard form design (causes some errors in layout) +- some minor graphics issues, sometimes text does not fit in bounds +- some minor textbox select issues (text is not selected when clicking once in the MyCall or DXCall textbox) +- Settings upgrade from previous versions do not work, always use default settings when upgrading +- Program sometimes does not close properly, stops working while saving settings to disk + + + +VERY IMPORTANT!!! ON SOME OLDER/SMALLER SYSTEMS THE SSL ENCRYPTED CONNECTIONS ("https://......") DO NOT WORK +IN THIS CASE YOU HAVE TO IMPORT SSL CERTIFICATES TO THE MONO CERTIFICATE STORE MANUALLY!!! + +Mono is using its own certificate store different from system or Mozilla's web browser. +The synchronisation should be done automatically but I found it not working sometimes after downloading the Mono package. + +1. Install the "ca-certificates-mozilla" package to get SSL certificates +2. Import the certificates into the Mono certificate store by typing "sudo cert-sync /etc/ssl/ca-bundle.pem" (can be a different location on other versions!) + + + + diff --git a/AirScout/ReflectionDlg.Designer.cs b/AirScout/ReflectionDlg.Designer.cs new file mode 100644 index 0000000..93df6c9 --- /dev/null +++ b/AirScout/ReflectionDlg.Designer.cs @@ -0,0 +1,187 @@ +namespace AirScout +{ + partial class ReflectionDlg + { + /// + /// Erforderliche Designervariable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Verwendete Ressourcen bereinigen. + /// + /// True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Vom Windows Form-Designer generierter Code + + /// + /// Erforderliche Methode für die Designerunterstützung. + /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden. + /// + private void InitializeComponent() + { + this.label3 = new System.Windows.Forms.Label(); + this.tb_Reflection_UTC = new System.Windows.Forms.TextBox(); + this.tb_Reflection_Call = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.tb_Reflection_Lat = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.tb_Reflection_Lon = new System.Windows.Forms.TextBox(); + this.label4 = new System.Windows.Forms.Label(); + this.tb_Reflection_Alt = new System.Windows.Forms.TextBox(); + this.label5 = new System.Windows.Forms.Label(); + this.btn_Reflection_Yes = new System.Windows.Forms.Button(); + this.btn_Reflection_No = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(5, 12); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(32, 13); + this.label3.TabIndex = 4; + this.label3.Text = "UTC:"; + // + // tb_Reflection_UTC + // + this.tb_Reflection_UTC.Location = new System.Drawing.Point(43, 9); + this.tb_Reflection_UTC.Name = "tb_Reflection_UTC"; + this.tb_Reflection_UTC.Size = new System.Drawing.Size(115, 20); + this.tb_Reflection_UTC.TabIndex = 5; + // + // tb_Reflection_Call + // + this.tb_Reflection_Call.Location = new System.Drawing.Point(43, 35); + this.tb_Reflection_Call.Name = "tb_Reflection_Call"; + this.tb_Reflection_Call.Size = new System.Drawing.Size(115, 20); + this.tb_Reflection_Call.TabIndex = 7; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(5, 38); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(27, 13); + this.label1.TabIndex = 6; + this.label1.Text = "Call:"; + // + // tb_Reflection_Lat + // + this.tb_Reflection_Lat.Location = new System.Drawing.Point(43, 61); + this.tb_Reflection_Lat.Name = "tb_Reflection_Lat"; + this.tb_Reflection_Lat.Size = new System.Drawing.Size(115, 20); + this.tb_Reflection_Lat.TabIndex = 9; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(5, 64); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(25, 13); + this.label2.TabIndex = 8; + this.label2.Text = "Lat:"; + // + // tb_Reflection_Lon + // + this.tb_Reflection_Lon.Location = new System.Drawing.Point(43, 87); + this.tb_Reflection_Lon.Name = "tb_Reflection_Lon"; + this.tb_Reflection_Lon.Size = new System.Drawing.Size(115, 20); + this.tb_Reflection_Lon.TabIndex = 11; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(5, 90); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(28, 13); + this.label4.TabIndex = 10; + this.label4.Text = "Lon:"; + // + // tb_Reflection_Alt + // + this.tb_Reflection_Alt.Location = new System.Drawing.Point(43, 113); + this.tb_Reflection_Alt.Name = "tb_Reflection_Alt"; + this.tb_Reflection_Alt.Size = new System.Drawing.Size(115, 20); + this.tb_Reflection_Alt.TabIndex = 13; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(5, 116); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(22, 13); + this.label5.TabIndex = 12; + this.label5.Text = "Alt:"; + // + // btn_Reflection_Yes + // + this.btn_Reflection_Yes.DialogResult = System.Windows.Forms.DialogResult.Yes; + this.btn_Reflection_Yes.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Reflection_Yes.Location = new System.Drawing.Point(43, 151); + this.btn_Reflection_Yes.Name = "btn_Reflection_Yes"; + this.btn_Reflection_Yes.Size = new System.Drawing.Size(54, 42); + this.btn_Reflection_Yes.TabIndex = 14; + this.btn_Reflection_Yes.Text = "&Yes"; + this.btn_Reflection_Yes.UseVisualStyleBackColor = true; + // + // btn_Reflection_No + // + this.btn_Reflection_No.DialogResult = System.Windows.Forms.DialogResult.No; + this.btn_Reflection_No.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Reflection_No.Location = new System.Drawing.Point(104, 151); + this.btn_Reflection_No.Name = "btn_Reflection_No"; + this.btn_Reflection_No.Size = new System.Drawing.Size(54, 42); + this.btn_Reflection_No.TabIndex = 15; + this.btn_Reflection_No.Text = "&No"; + this.btn_Reflection_No.UseVisualStyleBackColor = true; + // + // ReflectionDlg + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(182, 205); + this.Controls.Add(this.btn_Reflection_No); + this.Controls.Add(this.btn_Reflection_Yes); + this.Controls.Add(this.tb_Reflection_Alt); + this.Controls.Add(this.label5); + this.Controls.Add(this.tb_Reflection_Lon); + this.Controls.Add(this.label4); + this.Controls.Add(this.tb_Reflection_Lat); + this.Controls.Add(this.label2); + this.Controls.Add(this.tb_Reflection_Call); + this.Controls.Add(this.label1); + this.Controls.Add(this.tb_Reflection_UTC); + this.Controls.Add(this.label3); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow; + this.Name = "ReflectionDlg"; + this.Text = "Log Reflection"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.Button btn_Reflection_Yes; + private System.Windows.Forms.Button btn_Reflection_No; + public System.Windows.Forms.TextBox tb_Reflection_UTC; + public System.Windows.Forms.TextBox tb_Reflection_Call; + public System.Windows.Forms.TextBox tb_Reflection_Lat; + public System.Windows.Forms.TextBox tb_Reflection_Lon; + public System.Windows.Forms.TextBox tb_Reflection_Alt; + } +} \ No newline at end of file diff --git a/AirScout/ReflectionDlg.cs b/AirScout/ReflectionDlg.cs new file mode 100644 index 0000000..2b445ec --- /dev/null +++ b/AirScout/ReflectionDlg.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace AirScout +{ + public partial class ReflectionDlg : Form + { + public ReflectionDlg() + { + InitializeComponent(); + } + } +} diff --git a/AirScout/ReflectionDlg.resx b/AirScout/ReflectionDlg.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/AirScout/ReflectionDlg.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/AirScout/ScoutBaseDatabaseMaintenanceDlg.Designer.cs b/AirScout/ScoutBaseDatabaseMaintenanceDlg.Designer.cs new file mode 100644 index 0000000..9ca3160 --- /dev/null +++ b/AirScout/ScoutBaseDatabaseMaintenanceDlg.Designer.cs @@ -0,0 +1,133 @@ +namespace AirScout +{ + partial class ScoutBaseDatabaseMaintenanceDlg + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.ss_Main = new System.Windows.Forms.StatusStrip(); + this.tsl_Status = new System.Windows.Forms.ToolStripStatusLabel(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.lv_Tables = new System.Windows.Forms.ListView(); + this.ch_TableName = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.ch_Entries = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.ch_Description = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.ch_Action = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.ss_Main.SuspendLayout(); + this.groupBox1.SuspendLayout(); + this.SuspendLayout(); + // + // ss_Main + // + this.ss_Main.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.tsl_Status}); + this.ss_Main.Location = new System.Drawing.Point(0, 321); + this.ss_Main.Name = "ss_Main"; + this.ss_Main.Size = new System.Drawing.Size(543, 22); + this.ss_Main.TabIndex = 0; + this.ss_Main.Text = "statusStrip1"; + // + // tsl_Status + // + this.tsl_Status.Name = "tsl_Status"; + this.tsl_Status.Size = new System.Drawing.Size(39, 17); + this.tsl_Status.Text = "Status"; + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.lv_Tables); + this.groupBox1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox1.Location = new System.Drawing.Point(0, 12); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(527, 148); + this.groupBox1.TabIndex = 1; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Tables"; + // + // lv_Tables + // + this.lv_Tables.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.ch_TableName, + this.ch_Description, + this.ch_Entries, + this.ch_Action}); + this.lv_Tables.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lv_Tables.Location = new System.Drawing.Point(6, 19); + this.lv_Tables.Name = "lv_Tables"; + this.lv_Tables.Size = new System.Drawing.Size(508, 114); + this.lv_Tables.TabIndex = 0; + this.lv_Tables.UseCompatibleStateImageBehavior = false; + this.lv_Tables.View = System.Windows.Forms.View.Details; + // + // ch_TableName + // + this.ch_TableName.Text = "TableName"; + this.ch_TableName.Width = 78; + // + // ch_Entries + // + this.ch_Entries.Text = "Entries"; + this.ch_Entries.Width = 77; + // + // ch_Description + // + this.ch_Description.Text = "Description"; + this.ch_Description.Width = 203; + // + // ch_Action + // + this.ch_Action.Text = "Action"; + this.ch_Action.Width = 107; + // + // ScoutBaseDatabaseMaintenanceDlg + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(543, 343); + this.Controls.Add(this.groupBox1); + this.Controls.Add(this.ss_Main); + this.Name = "ScoutBaseDatabaseMaintenanceDlg"; + this.Text = "ScoutBaseDatabaseMaintenanceDlg"; + this.ss_Main.ResumeLayout(false); + this.ss_Main.PerformLayout(); + this.groupBox1.ResumeLayout(false); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.StatusStrip ss_Main; + private System.Windows.Forms.ToolStripStatusLabel tsl_Status; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.ListView lv_Tables; + private System.Windows.Forms.ColumnHeader ch_TableName; + private System.Windows.Forms.ColumnHeader ch_Entries; + private System.Windows.Forms.ColumnHeader ch_Description; + private System.Windows.Forms.ColumnHeader ch_Action; + } +} \ No newline at end of file diff --git a/AirScout/ScoutBaseDatabaseMaintenanceDlg.cs b/AirScout/ScoutBaseDatabaseMaintenanceDlg.cs new file mode 100644 index 0000000..cf4eb26 --- /dev/null +++ b/AirScout/ScoutBaseDatabaseMaintenanceDlg.cs @@ -0,0 +1,302 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using System.Windows.Forms.VisualStyles; +using ScoutBase.Stations; + +namespace AirScout +{ + public partial class ScoutBaseDatabaseMaintenanceDlg : Form + { + public ScoutBaseDatabaseMaintenanceDlg() + { + InitializeComponent(); + lv_Tables.FullRowSelect = true; + ListViewExtender extender = new ListViewExtender(lv_Tables); + // extend 4th column + ListViewButtonColumn buttonAction = new ListViewButtonColumn(3); + buttonAction.Click += OnButtonActionClick; + buttonAction.FixedWidth = true; + // add extender + extender.AddColumn(buttonAction); + // add items + ListViewItem lvi_Location = lv_Tables.Items.Add(LocationDesignator.TableName); + lvi_Location.SubItems.Add("Location info"); + lvi_Location.SubItems.Add(StationData.Database.LocationCount().ToString()); + lvi_Location.SubItems.Add("Delete All"); + ListViewItem lvi_QRV = lv_Tables.Items.Add(QRVDesignator.TableName); + lvi_QRV.SubItems.Add("QRV info"); + lvi_QRV.SubItems.Add(StationData.Database.QRVCount().ToString()); + lvi_QRV.SubItems.Add("Delete All"); + + } + + private void OnButtonActionClick(object sender, ListViewColumnMouseEventArgs e) + { + if ((e.Item != null) && (e.Item.Text == LocationDesignator.TableName)) + StationData.Database.LocationDeleteAll(); + else if ((e.Item != null) && (e.Item.Text == QRVDesignator.TableName)) + StationData.Database.QRVDeleteAll(); + } + } + + + public class ListViewExtender : IDisposable + { + private readonly Dictionary _columns = new Dictionary(); + + public ListViewExtender(ListView listView) + { + if (listView == null) + throw new ArgumentNullException("listView"); + + if (listView.View != View.Details) + throw new ArgumentException(null, "listView"); + + ListView = listView; + ListView.OwnerDraw = true; + ListView.DrawItem += OnDrawItem; + ListView.DrawSubItem += OnDrawSubItem; + ListView.DrawColumnHeader += OnDrawColumnHeader; + ListView.MouseMove += OnMouseMove; + ListView.MouseClick += OnMouseClick; + + Font = new Font(ListView.Font.FontFamily, ListView.Font.Size - 2); + } + + public virtual Font Font { get; private set; } + public ListView ListView { get; private set; } + + protected virtual void OnMouseClick(object sender, MouseEventArgs e) + { + ListViewItem item; + ListViewItem.ListViewSubItem sub; + ListViewColumn column = GetColumnAt(e.X, e.Y, out item, out sub); + if (column != null) + { + column.MouseClick(e, item, sub); + } + } + + public ListViewColumn GetColumnAt(int x, int y, out ListViewItem item, out ListViewItem.ListViewSubItem subItem) + { + subItem = null; + item = ListView.GetItemAt(x, y); + if (item == null) + return null; + + subItem = item.GetSubItemAt(x, y); + if (subItem == null) + return null; + + for (int i = 0; i < item.SubItems.Count; i++) + { + if (item.SubItems[i] == subItem) + return GetColumn(i); + } + return null; + } + + protected virtual void OnMouseMove(object sender, MouseEventArgs e) + { + ListViewItem item; + ListViewItem.ListViewSubItem sub; + ListViewColumn column = GetColumnAt(e.X, e.Y, out item, out sub); + if (column != null) + { + column.Invalidate(item, sub); + return; + } + if (item != null) + { + ListView.Invalidate(item.Bounds); + } + } + + protected virtual void OnDrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e) + { + e.DrawDefault = true; + } + + protected virtual void OnDrawSubItem(object sender, DrawListViewSubItemEventArgs e) + { + ListViewColumn column = GetColumn(e.ColumnIndex); + if (column == null) + { + e.DrawDefault = true; + return; + } + + column.Draw(e); + } + + protected virtual void OnDrawItem(object sender, DrawListViewItemEventArgs e) + { + // do nothing + } + + public void AddColumn(ListViewColumn column) + { + if (column == null) + throw new ArgumentNullException("column"); + + column.Extender = this; + _columns[column.ColumnIndex] = column; + } + + public ListViewColumn GetColumn(int index) + { + ListViewColumn column; + return _columns.TryGetValue(index, out column) ? column : null; + } + + public IEnumerable Columns + { + get + { + return _columns.Values; + } + } + + public virtual void Dispose() + { + if (Font != null) + { + Font.Dispose(); + Font = null; + } + } + } + + public abstract class ListViewColumn + { + public event EventHandler Click; + + protected ListViewColumn(int columnIndex) + { + if (columnIndex < 0) + throw new ArgumentException(null, "columnIndex"); + + ColumnIndex = columnIndex; + } + + public virtual ListViewExtender Extender { get; protected internal set; } + public int ColumnIndex { get; private set; } + + public virtual Font Font + { + get + { + return Extender == null ? null : Extender.Font; + } + } + + public ListView ListView + { + get + { + return Extender == null ? null : Extender.ListView; + } + } + + public abstract void Draw(DrawListViewSubItemEventArgs e); + + public virtual void MouseClick(MouseEventArgs e, ListViewItem item, ListViewItem.ListViewSubItem subItem) + { + if (Click != null) + { + Click(this, new ListViewColumnMouseEventArgs(e, item, subItem)); + } + } + + public virtual void Invalidate(ListViewItem item, ListViewItem.ListViewSubItem subItem) + { + if (Extender != null) + { + Extender.ListView.Invalidate(subItem.Bounds); + } + } + } + + public class ListViewColumnMouseEventArgs : MouseEventArgs + { + public ListViewColumnMouseEventArgs(MouseEventArgs e, ListViewItem item, ListViewItem.ListViewSubItem subItem) + : base(e.Button, e.Clicks, e.X, e.Y, e.Delta) + { + Item = item; + SubItem = subItem; + } + + public ListViewItem Item { get; private set; } + public ListViewItem.ListViewSubItem SubItem { get; private set; } + } + + public class ListViewButtonColumn : ListViewColumn + { + private Rectangle _hot = Rectangle.Empty; + + public ListViewButtonColumn(int columnIndex) + : base(columnIndex) + { + } + + public bool FixedWidth { get; set; } + public bool DrawIfEmpty { get; set; } + + public override ListViewExtender Extender + { + get + { + return base.Extender; + } + protected internal set + { + base.Extender = value; + if (FixedWidth) + { + base.Extender.ListView.ColumnWidthChanging += OnColumnWidthChanging; + } + } + } + + protected virtual void OnColumnWidthChanging(object sender, ColumnWidthChangingEventArgs e) + { + if (e.ColumnIndex == ColumnIndex) + { + e.Cancel = true; + e.NewWidth = ListView.Columns[e.ColumnIndex].Width; + } + } + + public override void Draw(DrawListViewSubItemEventArgs e) + { + if (_hot != Rectangle.Empty) + { + if (_hot != e.Bounds) + { + ListView.Invalidate(_hot); + _hot = Rectangle.Empty; + } + } + + if ((!DrawIfEmpty) && (string.IsNullOrEmpty(e.SubItem.Text))) + return; + + Point mouse = e.Item.ListView.PointToClient(Control.MousePosition); + if ((ListView.GetItemAt(mouse.X, mouse.Y) == e.Item) && (e.Item.GetSubItemAt(mouse.X, mouse.Y) == e.SubItem)) + { + ButtonRenderer.DrawButton(e.Graphics, e.Bounds, e.SubItem.Text, Font, true, PushButtonState.Hot); + _hot = e.Bounds; + } + else + { + ButtonRenderer.DrawButton(e.Graphics, e.Bounds, e.SubItem.Text, Font, false, PushButtonState.Default); + } + } + } +} diff --git a/AirScout/ScoutBaseDatabaseMaintenanceDlg.resx b/AirScout/ScoutBaseDatabaseMaintenanceDlg.resx new file mode 100644 index 0000000..05efec9 --- /dev/null +++ b/AirScout/ScoutBaseDatabaseMaintenanceDlg.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/AirScout/SetTimeDlg.Designer.cs b/AirScout/SetTimeDlg.Designer.cs new file mode 100644 index 0000000..f0475ed --- /dev/null +++ b/AirScout/SetTimeDlg.Designer.cs @@ -0,0 +1,120 @@ +namespace AirScout +{ + partial class SetTimeDlg + { + /// + /// Erforderliche Designervariable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Verwendete Ressourcen bereinigen. + /// + /// True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Vom Windows Form-Designer generierter Code + + /// + /// Erforderliche Methode für die Designerunterstützung. + /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.dtp_SetTimeDlg_Start = new System.Windows.Forms.DateTimePicker(); + this.btn_SetTimeDlg_OK = new System.Windows.Forms.Button(); + this.btn_SetTimeDlg_Cancel = new System.Windows.Forms.Button(); + this.cb_Time_Online = new System.Windows.Forms.CheckBox(); + this.SuspendLayout(); + // + // label1 + // + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(52, 9); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(292, 45); + this.label1.TabIndex = 0; + this.label1.Text = "You can set online/offline mode here and set a start time for offline mode."; + // + // dtp_SetTimeDlg_Start + // + this.dtp_SetTimeDlg_Start.CustomFormat = "yyyy-MM-dd HH:mm"; + this.dtp_SetTimeDlg_Start.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScout.Properties.Settings.Default, "Time_Offline", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.dtp_SetTimeDlg_Start.Format = System.Windows.Forms.DateTimePickerFormat.Custom; + this.dtp_SetTimeDlg_Start.Location = new System.Drawing.Point(129, 98); + this.dtp_SetTimeDlg_Start.Name = "dtp_SetTimeDlg_Start"; + this.dtp_SetTimeDlg_Start.Size = new System.Drawing.Size(125, 20); + this.dtp_SetTimeDlg_Start.TabIndex = 1; + this.dtp_SetTimeDlg_Start.Value = global::AirScout.Properties.Settings.Default.Time_Offline; + // + // btn_SetTimeDlg_OK + // + this.btn_SetTimeDlg_OK.DialogResult = System.Windows.Forms.DialogResult.OK; + this.btn_SetTimeDlg_OK.Location = new System.Drawing.Point(108, 133); + this.btn_SetTimeDlg_OK.Name = "btn_SetTimeDlg_OK"; + this.btn_SetTimeDlg_OK.Size = new System.Drawing.Size(75, 23); + this.btn_SetTimeDlg_OK.TabIndex = 2; + this.btn_SetTimeDlg_OK.Text = "OK"; + this.btn_SetTimeDlg_OK.UseVisualStyleBackColor = true; + // + // btn_SetTimeDlg_Cancel + // + this.btn_SetTimeDlg_Cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btn_SetTimeDlg_Cancel.Location = new System.Drawing.Point(189, 133); + this.btn_SetTimeDlg_Cancel.Name = "btn_SetTimeDlg_Cancel"; + this.btn_SetTimeDlg_Cancel.Size = new System.Drawing.Size(75, 23); + this.btn_SetTimeDlg_Cancel.TabIndex = 3; + this.btn_SetTimeDlg_Cancel.Text = "Cancel"; + this.btn_SetTimeDlg_Cancel.UseVisualStyleBackColor = true; + // + // cb_Time_Online + // + this.cb_Time_Online.AutoSize = true; + this.cb_Time_Online.Checked = global::AirScout.Properties.Settings.Default.Time_Mode_Online; + this.cb_Time_Online.CheckState = System.Windows.Forms.CheckState.Checked; + this.cb_Time_Online.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScout.Properties.Settings.Default, "Time_Online", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Time_Online.Location = new System.Drawing.Point(70, 66); + this.cb_Time_Online.Name = "cb_Time_Online"; + this.cb_Time_Online.Size = new System.Drawing.Size(229, 17); + this.cb_Time_Online.TabIndex = 4; + this.cb_Time_Online.Text = "Online mode: use current PC time (Default)."; + this.cb_Time_Online.UseVisualStyleBackColor = true; + this.cb_Time_Online.CheckedChanged += new System.EventHandler(this.cb_Time_Online_CheckedChanged); + // + // SetTimeDlg + // + this.AcceptButton = this.btn_SetTimeDlg_OK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(392, 174); + this.Controls.Add(this.cb_Time_Online); + this.Controls.Add(this.btn_SetTimeDlg_Cancel); + this.Controls.Add(this.btn_SetTimeDlg_OK); + this.Controls.Add(this.dtp_SetTimeDlg_Start); + this.Controls.Add(this.label1); + this.Name = "SetTimeDlg"; + this.Text = "SetTimeDlg"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.SetTimeDlg_FormClosing); + this.Load += new System.EventHandler(this.SetTimeDlg_Load); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Button btn_SetTimeDlg_OK; + private System.Windows.Forms.Button btn_SetTimeDlg_Cancel; + public System.Windows.Forms.DateTimePicker dtp_SetTimeDlg_Start; + public System.Windows.Forms.CheckBox cb_Time_Online; + } +} \ No newline at end of file diff --git a/AirScout/SetTimeDlg.cs b/AirScout/SetTimeDlg.cs new file mode 100644 index 0000000..8c2960b --- /dev/null +++ b/AirScout/SetTimeDlg.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace AirScout +{ + public partial class SetTimeDlg : Form + { + public SetTimeDlg() + { + InitializeComponent(); + dtp_SetTimeDlg_Start.Value = DateTime.UtcNow; + } + + private void cb_Time_Online_CheckedChanged(object sender, EventArgs e) + { + dtp_SetTimeDlg_Start.Enabled = !cb_Time_Online.Checked; + } + + private void SetTimeDlg_Load(object sender, EventArgs e) + { + } + + private void SetTimeDlg_FormClosing(object sender, FormClosingEventArgs e) + { + } + } +} diff --git a/AirScout/SetTimeDlg.resx b/AirScout/SetTimeDlg.resx new file mode 100644 index 0000000..1af7de1 --- /dev/null +++ b/AirScout/SetTimeDlg.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/AirScout/Settings.cs b/AirScout/Settings.cs new file mode 100644 index 0000000..24ee44b --- /dev/null +++ b/AirScout/Settings.cs @@ -0,0 +1,28 @@ +namespace AirScout.Properties { + + + // Diese Klasse ermöglicht die Behandlung bestimmter Ereignisse der Einstellungsklasse: + // Das SettingChanging-Ereignis wird ausgelöst, bevor der Wert einer Einstellung geändert wird. + // Das PropertyChanged-Ereignis wird ausgelöst, nachdem der Wert einer Einstellung geändert wurde. + // Das SettingsLoaded-Ereignis wird ausgelöst, nachdem die Einstellungswerte geladen wurden. + // Das SettingsSaving-Ereignis wird ausgelöst, bevor die Einstellungswerte gespeichert werden. + internal sealed partial class Settings { + + public Settings() { + // // Heben Sie die Auskommentierung der unten angezeigten Zeilen auf, um Ereignishandler zum Speichern und Ändern von Einstellungen hinzuzufügen: + // + // this.SettingChanging += this.SettingChangingEventHandler; + // + // this.SettingsSaving += this.SettingsSavingEventHandler; + // + } + + private void SettingChangingEventHandler(object sender, System.Configuration.SettingChangingEventArgs e) { + // Fügen Sie hier Code zum Behandeln des SettingChangingEvent-Ereignisses hinzu. + } + + private void SettingsSavingEventHandler(object sender, System.ComponentModel.CancelEventArgs e) { + // Fügen Sie hier Code zum Behandeln des SettingsSaving-Ereignisses hinzu. + } + } +} diff --git a/AirScout/SftpClient.cs b/AirScout/SftpClient.cs new file mode 100644 index 0000000..64791a9 --- /dev/null +++ b/AirScout/SftpClient.cs @@ -0,0 +1,1584 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using System.IO; +using Renci.SshNet.Sftp; +using System.Text; +using Renci.SshNet.Common; +using System.Globalization; +using System.Threading; +using System.Diagnostics.CodeAnalysis; + +namespace Renci.SshNet +{ + /// + /// Implementation of the SSH File Transfer Protocol (SFTP) over SSH. + /// + public partial class SftpClient : BaseClient + { + /// + /// Holds SftpSession instance that used to communicate to the SFTP server + /// + private SftpSession _sftpSession; + + private bool _disposeConnectionInfo; + + /// + /// Gets or sets the operation timeout. + /// + /// The operation timeout. + public TimeSpan OperationTimeout { get; set; } + + /// + /// Gets or sets the size of the buffer. + /// + /// The size of the buffer. + public uint BufferSize { get; set; } + + /// + /// Gets remote working directory. + /// + public string WorkingDirectory + { + get + { + if (this._sftpSession == null) + return null; + return this._sftpSession.WorkingDirectory; + } + } + + /// + /// Gets sftp protocol version. + /// + public int ProtocolVersion { get; private set; } + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The connection info. + /// is null. + public SftpClient(ConnectionInfo connectionInfo) + : base(connectionInfo) + { + this.OperationTimeout = new TimeSpan(0, 0, 0, 0, -1); + this.BufferSize = 1024 * 16; + } + + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Connection port. + /// Authentication username. + /// Authentication password. + /// is null. + /// is invalid. -or- is null or contains whitespace characters. + /// is not within and . + [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")] + public SftpClient(string host, int port, string username, string password) + : this(new PasswordConnectionInfo(host, port, username, password)) + { + this._disposeConnectionInfo = true; + } + + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Authentication username. + /// Authentication password. + /// is null. + /// is invalid. -or- is null contains whitespace characters. + public SftpClient(string host, string username, string password) + : this(host, ConnectionInfo.DEFAULT_PORT, username, password) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Connection port. + /// Authentication username. + /// Authentication private key file(s) . + /// is null. + /// is invalid. -or- is nunullll or contains whitespace characters. + /// is not within and . + [SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")] + public SftpClient(string host, int port, string username, params PrivateKeyFile[] keyFiles) + : this(new PrivateKeyConnectionInfo(host, port, username, keyFiles)) + { + this._disposeConnectionInfo = true; + } + + /// + /// Initializes a new instance of the class. + /// + /// Connection host. + /// Authentication username. + /// Authentication private key file(s) . + /// is null. + /// is invalid. -or- is null or contains whitespace characters. + public SftpClient(string host, string username, params PrivateKeyFile[] keyFiles) + : this(host, ConnectionInfo.DEFAULT_PORT, username, keyFiles) + { + } + + #endregion + + /// + /// Changes remote directory to path. + /// + /// New directory path. + /// is null. + /// Client is not connected. + /// Permission to change directory denied by remote host. -or- A SSH command was denied by the server. + /// The path in was not found on the remote host. + /// A SSH error where is the message from the remote host. + public void ChangeDirectory(string path) + { + if (path == null) + throw new ArgumentNullException("path"); + + if (this._sftpSession == null) + throw new SshConnectionException("Client not connected."); + + this._sftpSession.ChangeDirectory(path); + } + + /// + /// Changes permissions of file(s) to specified mode. + /// + /// File(s) path, may match multiple files. + /// The mode. + /// is null. + /// Client is not connected. + /// Permission to change permission on the path(s) was denied by the remote host. -or- A SSH command was denied by the server. + /// The path in was not found on the remote host. + /// A SSH error where is the message from the remote host. + public void ChangePermissions(string path, short mode) + { + var file = this.Get(path); + + file.SetPermissions(mode); + } + + /// + /// Creates remote directory specified by path. + /// + /// Directory path to create. + /// is null or contains whitespace characters. + /// Client is not connected. + /// Permission to create the directory was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + public void CreateDirectory(string path) + { + if (path.IsNullOrWhiteSpace()) + throw new ArgumentException(path); + + if (this._sftpSession == null) + throw new SshConnectionException("Client not connected."); + + var fullPath = this._sftpSession.GetCanonicalPath(path); + + this._sftpSession.RequestMkDir(fullPath); + } + + /// + /// Deletes remote directory specified by path. + /// + /// Directory to be deleted path. + /// is null or contains whitespace characters. + /// Client is not connected. + /// Permission to delete the directory was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + public void DeleteDirectory(string path) + { + if (path.IsNullOrWhiteSpace()) + throw new ArgumentException("path"); + + if (this._sftpSession == null) + throw new SshConnectionException("Client not connected."); + + var fullPath = this._sftpSession.GetCanonicalPath(path); + + this._sftpSession.RequestRmDir(fullPath); + } + + /// + /// Deletes remote file specified by path. + /// + /// File to be deleted path. + /// is null or contains whitespace characters. + /// Client is not connected. + /// Permission to delete the file was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + public void DeleteFile(string path) + { + if (path.IsNullOrWhiteSpace()) + throw new ArgumentException("path"); + + if (this._sftpSession == null) + throw new SshConnectionException("Client not connected."); + + var fullPath = this._sftpSession.GetCanonicalPath(path); + + this._sftpSession.RequestRemove(fullPath); + } + + /// + /// Renames remote file from old path to new path. + /// + /// Path to the old file location. + /// Path to the new file location. + /// is null. -or- or is null. + /// Client is not connected. + /// Permission to rename the file was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + public void RenameFile(string oldPath, string newPath) + { + this.RenameFile(oldPath, newPath, false); + } + + /// + /// Renames remote file from old path to new path. + /// + /// Path to the old file location. + /// Path to the new file location. + /// if set to true then perform a posix rename. + /// oldPath + /// is null. -or- or is null. + /// Client is not connected. + /// Permission to rename the file was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + public void RenameFile(string oldPath, string newPath, bool isPosix) + { + if (oldPath == null) + throw new ArgumentNullException("oldPath"); + + if (newPath == null) + throw new ArgumentNullException("newPath"); + + if (this._sftpSession == null) + throw new SshConnectionException("Client not connected."); + + var oldFullPath = this._sftpSession.GetCanonicalPath(oldPath); + + var newFullPath = this._sftpSession.GetCanonicalPath(newPath); + + if (isPosix) + { + this._sftpSession.RequestPosixRename(oldFullPath, newFullPath); + } + else + { + this._sftpSession.RequestRename(oldFullPath, newFullPath); + } + } + + /// + /// Creates a symbolic link from old path to new path. + /// + /// The old path. + /// The new path. + /// is null. -or- is null or contains whitespace characters. + /// Client is not connected. + /// Permission to create the symbolic link was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + public void SymbolicLink(string path, string linkPath) + { + if (path.IsNullOrWhiteSpace()) + throw new ArgumentException("path"); + + if (linkPath.IsNullOrWhiteSpace()) + throw new ArgumentException("linkPath"); + + if (this._sftpSession == null) + throw new SshConnectionException("Client not connected."); + + var fullPath = this._sftpSession.GetCanonicalPath(path); + + var linkFullPath = this._sftpSession.GetCanonicalPath(linkPath); + + this._sftpSession.RequestSymLink(fullPath, linkFullPath); + } + + /// + /// Retrieves list of files in remote directory. + /// + /// The path. + /// The list callback. + /// + /// List of directory entries + /// + /// is null. + /// Client is not connected. + /// Permission to list the contents of the directory was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + public IEnumerable ListDirectory(string path, Action listCallback = null) + { + return InternalListDirectory(path, listCallback); + } + + /// + /// Begins an asynchronous operation of retrieving list of files in remote directory. + /// + /// The path. + /// The method to be called when the asynchronous write operation is completed. + /// A user-provided object that distinguishes this particular asynchronous write request from other requests. + /// The list callback. + /// + /// An that references the asynchronous operation. + /// + public IAsyncResult BeginListDirectory(string path, AsyncCallback asyncCallback, object state, Action listCallback = null) + { + var asyncResult = new SftpListDirectoryAsyncResult(asyncCallback, state); + + this.ExecuteThread(() => + { + try + { + var result = this.InternalListDirectory(path, (count) => + { + asyncResult.Update(count); + + if (listCallback != null) + { + listCallback(count); + } + }); + + asyncResult.SetAsCompleted(result, false); + } + catch (Exception exp) + { + asyncResult.SetAsCompleted(exp, false); + } + }); + + return asyncResult; + } + + /// + /// Ends an asynchronous operation of retrieving list of files in remote directory. + /// + /// The pending asynchronous SFTP request. + /// + /// List of files + /// + /// The IAsyncResult object () did not come from the corresponding async method on this type. -or- EndExecute was called multiple times with the same IAsyncResult. + public IEnumerable EndListDirectory(IAsyncResult asyncResult) + { + var ar = asyncResult as SftpListDirectoryAsyncResult; + + if (ar == null || ar.EndInvokeCalled) + throw new ArgumentException("Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult."); + + // Wait for operation to complete, then return result or throw exception + return ar.EndInvoke(); + } + + /// + /// Gets reference to remote file or directory. + /// + /// The path. + /// Reference to file object. + /// path + /// Client is not connected. + /// is null. + public SftpFile Get(string path) + { + if (path == null) + throw new ArgumentNullException("path"); + + if (this._sftpSession == null) + throw new SshConnectionException("Client not connected."); + + var fullPath = this._sftpSession.GetCanonicalPath(path); + + var attributes = this._sftpSession.RequestLStat(fullPath); + + return new SftpFile(this._sftpSession, fullPath, attributes); + } + + /// + /// Checks whether file pr directory exists; + /// + /// The path. + /// true if directory or file exists; otherwise false. + /// is null or contains whitespace characters. + /// Client is not connected. + /// Permission to perform the operation was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + public bool Exists(string path) + { + if (path.IsNullOrWhiteSpace()) + throw new ArgumentException("path"); + + if (this._sftpSession == null) + throw new SshConnectionException("Client not connected."); + + var fullPath = this._sftpSession.GetFullRemotePath(path); + + if (this._sftpSession.RequestRealPath(fullPath, true) == null) + { + return false; + } + else + { + return true; + } + } + + /// + /// Downloads remote file specified by the path into the stream. + /// + /// File to download. + /// Stream to write the file into. + /// The download callback. + /// is null. + /// is null or contains whitespace characters. + /// Client is not connected. + /// Permission to perform the operation was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// + /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. + /// + public void DownloadFile(string path, Stream output, Action downloadCallback = null) + { + this.InternalDownloadFile(path, output, null, downloadCallback); + } + + /// + /// Begins an asynchronous file downloading into the stream. + /// + /// The path. + /// The output. + /// + /// An that references the asynchronous operation. + /// + /// path + /// output + /// is null. + /// is null or contains whitespace characters. + /// Client is not connected. + /// Permission to perform the operation was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// + /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. + /// + public IAsyncResult BeginDownloadFile(string path, Stream output) + { + return this.BeginDownloadFile(path, output, null, null, null); + } + + /// + /// Begins an asynchronous file downloading into the stream. + /// + /// The path. + /// The output. + /// The method to be called when the asynchronous write operation is completed. + /// + /// An that references the asynchronous operation. + /// + /// path + /// output + /// is null. + /// is null or contains whitespace characters. + /// Client is not connected. + /// Permission to perform the operation was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// + /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. + /// + public IAsyncResult BeginDownloadFile(string path, Stream output, AsyncCallback asyncCallback) + { + return this.BeginDownloadFile(path, output, asyncCallback, null, null); + } + + /// + /// Begins an asynchronous file downloading into the stream. + /// + /// The path. + /// The output. + /// The method to be called when the asynchronous write operation is completed. + /// A user-provided object that distinguishes this particular asynchronous write request from other requests. + /// The download callback. + /// + /// An that references the asynchronous operation. + /// + /// path + /// output + /// is null. + /// is null or contains whitespace characters. + /// Client is not connected. + /// Permission to perform the operation was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// + /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. + /// + public IAsyncResult BeginDownloadFile(string path, Stream output, AsyncCallback asyncCallback, object state, Action downloadCallback = null) + { + if (path.IsNullOrWhiteSpace()) + throw new ArgumentException("path"); + + if (output == null) + throw new ArgumentNullException("output"); + + var asyncResult = new SftpDownloadAsyncResult(asyncCallback, state); + + this.ExecuteThread(() => + { + try + { + this.InternalDownloadFile(path, output, asyncResult, (offset) => + { + asyncResult.Update(offset); + + if (downloadCallback != null) + { + downloadCallback(offset); + } + }); + + asyncResult.SetAsCompleted(null, false); + } + catch (Exception exp) + { + asyncResult.SetAsCompleted(exp, false); + } + }); + + return asyncResult; + } + + /// + /// Ends an asynchronous file downloading into the stream. + /// + /// The pending asynchronous SFTP request. + /// The IAsyncResult object () did not come from the corresponding async method on this type. -or- EndExecute was called multiple times with the same IAsyncResult. + public void EndDownloadFile(IAsyncResult asyncResult) + { + var ar = asyncResult as SftpDownloadAsyncResult; + + if (ar == null || ar.EndInvokeCalled) + throw new ArgumentException("Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult."); + + // Wait for operation to complete, then return result or throw exception + ar.EndInvoke(); + } + + /// + /// Uploads stream into remote file.. + /// + /// Data input stream. + /// Remote file path. + /// The upload callback. + /// is null. + /// is null or contains whitespace characters. + /// Client is not connected. + /// Permission to upload the file was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// + /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. + /// + public void UploadFile(Stream input, string path, Action uploadCallback = null) + { + this.UploadFile(input, path, true, uploadCallback); + } + + /// + /// Uploads stream into remote file.. + /// + /// Data input stream. + /// Remote file path. + /// if set to true then existing file will be overwritten. + /// The upload callback. + /// is null. + /// is null or contains whitespace characters. + /// Client is not connected. + /// Permission to upload the file was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// + /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. + /// + public void UploadFile(Stream input, string path, bool canOverride, Action uploadCallback = null) + { + var flags = Flags.Write | Flags.Truncate; + + if (canOverride) + flags |= Flags.CreateNewOrOpen; + else + flags |= Flags.CreateNew; + + this.InternalUploadFile(input, path, flags, null, uploadCallback); + } + + /// + /// Begins an asynchronous uploading the steam into remote file. + /// + /// Data input stream. + /// Remote file path. + /// + /// An that references the asynchronous operation. + /// + /// is null. + /// is null or contains whitespace characters. + /// Client is not connected. + /// Permission to list the contents of the directory was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// + /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. + /// + public IAsyncResult BeginUploadFile(Stream input, string path) + { + return this.BeginUploadFile(input, path, true, null, null, null); + } + + /// + /// Begins an asynchronous uploading the steam into remote file. + /// + /// Data input stream. + /// Remote file path. + /// The method to be called when the asynchronous write operation is completed. + /// + /// An that references the asynchronous operation. + /// + /// is null. + /// is null or contains whitespace characters. + /// Client is not connected. + /// Permission to list the contents of the directory was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// + /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. + /// + public IAsyncResult BeginUploadFile(Stream input, string path, AsyncCallback asyncCallback) + { + return this.BeginUploadFile(input, path, true, asyncCallback, null, null); + } + + /// + /// Begins an asynchronous uploading the steam into remote file. + /// + /// Data input stream. + /// Remote file path. + /// The method to be called when the asynchronous write operation is completed. + /// A user-provided object that distinguishes this particular asynchronous write request from other requests. + /// The upload callback. + /// + /// An that references the asynchronous operation. + /// + /// is null. + /// is null or contains whitespace characters. + /// Client is not connected. + /// Permission to list the contents of the directory was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// + /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. + /// + public IAsyncResult BeginUploadFile(Stream input, string path, AsyncCallback asyncCallback, object state, Action uploadCallback = null) + { + return this.BeginUploadFile(input, path, true, asyncCallback, state, uploadCallback); + } + + /// + /// Begins an asynchronous uploading the steam into remote file. + /// + /// Data input stream. + /// Remote file path. + /// if set to true then existing file will be overwritten. + /// The method to be called when the asynchronous write operation is completed. + /// A user-provided object that distinguishes this particular asynchronous write request from other requests. + /// The upload callback. + /// + /// An that references the asynchronous operation. + /// + /// input + /// path + /// is null. + /// is null or contains whitespace characters. + /// Client is not connected. + /// Permission to list the contents of the directory was denied by the remote host. -or- A SSH command was denied by the server. + /// A SSH error where is the message from the remote host. + /// + /// Method calls made by this method to , may under certain conditions result in exceptions thrown by the stream. + /// + public IAsyncResult BeginUploadFile(Stream input, string path, bool canOverride, AsyncCallback asyncCallback, object state, Action uploadCallback = null) + { + if (input == null) + throw new ArgumentNullException("input"); + + if (path.IsNullOrWhiteSpace()) + throw new ArgumentException("path"); + + var flags = Flags.Write | Flags.Truncate; + + if (canOverride) + flags |= Flags.CreateNewOrOpen; + else + flags |= Flags.CreateNew; + + var asyncResult = new SftpUploadAsyncResult(asyncCallback, state); + + this.ExecuteThread(() => + { + try + { + this.InternalUploadFile(input, path, flags, asyncResult, (offset) => + { + asyncResult.Update(offset); + + if (uploadCallback != null) + { + uploadCallback(offset); + } + + }); + + asyncResult.SetAsCompleted(null, false); + } + catch (Exception exp) + { + asyncResult.SetAsCompleted(exp, false); + } + }); + + return asyncResult; + } + + /// + /// Ends an asynchronous uploading the steam into remote file. + /// + /// The pending asynchronous SFTP request. + /// Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult. + /// The IAsyncResult object () did not come from the corresponding async method on this type. -or- EndExecute was called multiple times with the same IAsyncResult. + public void EndUploadFile(IAsyncResult asyncResult) + { + var ar = asyncResult as SftpUploadAsyncResult; + + if (ar == null || ar.EndInvokeCalled) + throw new ArgumentException("Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult."); + + // Wait for operation to complete, then return result or throw exception + ar.EndInvoke(); + } + + /// + /// Gets status using statvfs@openssh.com request. + /// + /// The path. + /// Reference to object that contains file status information. + /// path + /// Client is not connected. + /// is null. + public SftpFileSytemInformation GetStatus(string path) + { + if (path == null) + throw new ArgumentNullException("path"); + + if (this._sftpSession == null) + throw new SshConnectionException("Client not connected."); + + var fullPath = this._sftpSession.GetCanonicalPath(path); + + return this._sftpSession.RequestStatVfs(fullPath); + } + + #region File Methods + + /// + /// Appends lines to a file, and then closes the file. + /// + /// The file to append the lines to. The file is created if it does not already exist. + /// The lines to append to the file. + /// isnull -or- is null. + public void AppendAllLines(string path, IEnumerable contents) + { + if (contents == null) + throw new ArgumentNullException("contents"); + + using (var stream = this.AppendText(path)) + { + foreach (var line in contents) + { + stream.WriteLine(line); + } + } + } + + /// + /// Appends lines to a file by using a specified encoding, and then closes the file. + /// + /// The file to append the lines to. The file is created if it does not already exist. + /// The lines to append to the file. + /// The character encoding to use. + /// is null. -or- is null. -or- is null. + public void AppendAllLines(string path, IEnumerable contents, Encoding encoding) + { + if (contents == null) + throw new ArgumentNullException("contents"); + + using (var stream = this.AppendText(path, encoding)) + { + foreach (var line in contents) + { + stream.WriteLine(line); + } + } + } + + /// + /// Opens a file, appends the specified string to the file, and then closes the file. + /// If the file does not exist, this method creates a file, writes the specified string to the file, then closes the file. + /// + /// The file to append the specified string to. + /// The string to append to the file. + /// is null. -or- is null. + public void AppendAllText(string path, string contents) + { + using (var stream = this.AppendText(path)) + { + stream.Write(contents); + } + } + + /// + /// Opens a file, appends the specified string to the file, and then closes the file. + /// If the file does not exist, this method creates a file, writes the specified string to the file, then closes the file. + /// + /// The file to append the specified string to. + /// The string to append to the file. + /// The character encoding to use. + /// is null. -or- is null. -or- is null. + public void AppendAllText(string path, string contents, Encoding encoding) + { + using (var stream = this.AppendText(path, encoding)) + { + stream.Write(contents); + } + } + + /// + /// Creates a that appends UTF-8 encoded text to an existing file. + /// + /// The path to the file to append to. + /// A StreamWriter that appends UTF-8 encoded text to an existing file. + /// is null. + public StreamWriter AppendText(string path) + { + return this.AppendText(path, Encoding.UTF8); + } + + /// + /// Creates a that appends UTF-8 encoded text to an existing file. + /// + /// The path to the file to append to. + /// The character encoding to use. + /// + /// A StreamWriter that appends UTF-8 encoded text to an existing file. + /// + /// is null. -or- is null. + public StreamWriter AppendText(string path, Encoding encoding) + { + if (encoding == null) + throw new ArgumentNullException("encoding"); + + return new StreamWriter(new SftpFileStream(this._sftpSession, path, FileMode.Append, FileAccess.Write), encoding); + } + + /// + /// Creates or overwrites a file in the specified path. + /// + /// The path and name of the file to create. + /// A that provides read/write access to the file specified in path + /// is null. + public SftpFileStream Create(string path) + { + return new SftpFileStream(this._sftpSession, path, FileMode.Create, FileAccess.ReadWrite); + } + + /// + /// Creates or overwrites the specified file. + /// + /// The path and name of the file to create. + /// The number of bytes buffered for reads and writes to the file. + /// A that provides read/write access to the file specified in path + /// is null. + public SftpFileStream Create(string path, int bufferSize) + { + return new SftpFileStream(this._sftpSession, path, FileMode.Create, FileAccess.ReadWrite, bufferSize); + } + + /// + /// Creates or opens a file for writing UTF-8 encoded text. + /// + /// The file to be opened for writing. + /// A that writes to the specified file using UTF-8 encoding. + /// is null. + public StreamWriter CreateText(string path) + { + return new StreamWriter(this.OpenWrite(path), Encoding.UTF8); + } + + /// + /// Creates or opens a file for writing UTF-8 encoded text. + /// + /// The file to be opened for writing. + /// The character encoding to use. + /// A that writes to the specified file using UTF-8 encoding. + /// is null. + public StreamWriter CreateText(string path, Encoding encoding) + { + return new StreamWriter(this.OpenWrite(path), encoding); + } + + /// + /// Deletes the specified file or directory. An exception is not thrown if the specified file does not exist. + /// + /// The name of the file or directory to be deleted. Wildcard characters are not supported. + /// is null. + /// Client is not connected. + public void Delete(string path) + { + var file = this.Get(path); + + file.Delete(); + } + + /// + /// Returns the date and time the specified file or directory was last accessed. + /// + /// The file or directory for which to obtain access date and time information. + /// A structure set to the date and time that the specified file or directory was last accessed. This value is expressed in local time. + /// is null. + /// Client is not connected. + public DateTime GetLastAccessTime(string path) + { + var file = this.Get(path); + + return file.LastAccessTime; + } + + /// + /// Returns the date and time, in coordinated universal time (UTC), that the specified file or directory was last accessed. + /// + /// The file or directory for which to obtain access date and time information. + /// A structure set to the date and time that the specified file or directory was last accessed. This value is expressed in UTC time. + /// is null. + /// Client is not connected. + public DateTime GetLastAccessTimeUtc(string path) + { + var file = this.Get(path); + + return file.LastAccessTime.ToUniversalTime(); + } + + /// + /// Returns the date and time the specified file or directory was last written to. + /// + /// The file or directory for which to obtain write date and time information. + /// A structure set to the date and time that the specified file or directory was last written to. This value is expressed in local time. + /// is null. + /// Client is not connected. + public DateTime GetLastWriteTime(string path) + { + var file = this.Get(path); + + return file.LastWriteTime; + } + + /// + /// Returns the date and time, in coordinated universal time (UTC), that the specified file or directory was last written to. + /// + /// The file or directory for which to obtain write date and time information. + /// A structure set to the date and time that the specified file or directory was last written to. This value is expressed in UTC time. + /// is null. + /// Client is not connected. + public DateTime GetLastWriteTimeUtc(string path) + { + var file = this.Get(path); + + return file.LastWriteTime.ToUniversalTime(); + } + + /// + /// Opens a on the specified path with read/write access. + /// + /// The file to open. + /// A value that specifies whether a file is created if one does not exist, and determines whether the contents of existing files are retained or overwritten. + /// An unshared that provides access to the specified file, with the specified mode and access. + /// is null. + public SftpFileStream Open(string path, FileMode mode) + { + return new SftpFileStream(this._sftpSession, path, mode, FileAccess.ReadWrite); + } + + /// + /// Opens a on the specified path, with the specified mode and access. + /// + /// The file to open. + /// A value that specifies whether a file is created if one does not exist, and determines whether the contents of existing files are retained or overwritten. + /// A value that specifies the operations that can be performed on the file. + /// An unshared that provides access to the specified file, with the specified mode and access. + /// is null. + public SftpFileStream Open(string path, FileMode mode, FileAccess access) + { + return new SftpFileStream(this._sftpSession, path, mode, access); + } + + /// + /// Opens an existing file for reading. + /// + /// The file to be opened for reading. + /// A read-only System.IO.FileStream on the specified path. + /// is null. + public SftpFileStream OpenRead(string path) + { + return new SftpFileStream(this._sftpSession, path, FileMode.Open, FileAccess.Read); + } + + /// + /// Opens an existing UTF-8 encoded text file for reading. + /// + /// The file to be opened for reading. + /// A on the specified path. + /// is null. + public StreamReader OpenText(string path) + { + return new StreamReader(this.OpenRead(path), Encoding.UTF8); + } + + /// + /// Opens an existing file for writing. + /// + /// The file to be opened for writing. + /// An unshared object on the specified path with access. + /// is null. + public SftpFileStream OpenWrite(string path) + { + return new SftpFileStream(this._sftpSession, path, FileMode.OpenOrCreate, FileAccess.Write); + } + + /// + /// Opens a binary file, reads the contents of the file into a byte array, and then closes the file. + /// + /// The file to open for reading. + /// A byte array containing the contents of the file. + /// is null. + public byte[] ReadAllBytes(string path) + { + using (var stream = this.OpenRead(path)) + { + var buffer = new byte[stream.Length]; + stream.Read(buffer, 0, buffer.Length); + return buffer; + } + } + + /// + /// Opens a text file, reads all lines of the file, and then closes the file. + /// + /// The file to open for reading. + /// A string array containing all lines of the file. + /// is null. + public string[] ReadAllLines(string path) + { + return this.ReadAllLines(path, Encoding.UTF8); + } + + /// + /// Opens a file, reads all lines of the file with the specified encoding, and then closes the file. + /// + /// The file to open for reading. + /// The encoding applied to the contents of the file. + /// A string array containing all lines of the file. + /// is null. + public string[] ReadAllLines(string path, Encoding encoding) + { + var lines = new List(); + using (var stream = new StreamReader(this.OpenRead(path), encoding)) + { + while (!stream.EndOfStream) + { + lines.Add(stream.ReadLine()); + } + } + return lines.ToArray(); + } + + /// + /// Opens a text file, reads all lines of the file, and then closes the file. + /// + /// The file to open for reading. + /// A string containing all lines of the file. + /// is null. + public string ReadAllText(string path) + { + return this.ReadAllText(path, Encoding.UTF8); + } + + /// + /// Opens a file, reads all lines of the file with the specified encoding, and then closes the file. + /// + /// The file to open for reading. + /// The encoding applied to the contents of the file. + /// A string containing all lines of the file. + /// is null. + public string ReadAllText(string path, Encoding encoding) + { + var lines = new List(); + using (var stream = new StreamReader(this.OpenRead(path), encoding)) + { + return stream.ReadToEnd(); + } + } + + /// + /// Reads the lines of a file. + /// + /// The file to read. + /// The lines of the file. + /// is null. + public IEnumerable ReadLines(string path) + { + return this.ReadAllLines(path); + } + + /// + /// Read the lines of a file that has a specified encoding. + /// + /// The file to read. + /// The encoding that is applied to the contents of the file. + /// The lines of the file. + /// is null. + public IEnumerable ReadLines(string path, Encoding encoding) + { + return this.ReadAllLines(path, encoding); + } + + /// + /// Sets the date and time the specified file was last accessed. + /// + /// The file for which to set the access date and time information. + /// A containing the value to set for the last access date and time of path. This value is expressed in local time. + [Obsolete("Note: This method currently throws NotImplementedException because it has not yet been implemented.")] + public void SetLastAccessTime(string path, DateTime lastAccessTime) + { + throw new NotImplementedException(); + } + + /// + /// Sets the date and time, in coordinated universal time (UTC), that the specified file was last accessed. + /// + /// The file for which to set the access date and time information. + /// A containing the value to set for the last access date and time of path. This value is expressed in UTC time. + [Obsolete("Note: This method currently throws NotImplementedException because it has not yet been implemented.")] + public void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc) + { + throw new NotImplementedException(); + } + + /// + /// Sets the date and time that the specified file was last written to. + /// + /// The file for which to set the date and time information. + /// A System.DateTime containing the value to set for the last write date and time of path. This value is expressed in local time. + [Obsolete("Note: This method currently throws NotImplementedException because it has not yet been implemented.")] + public void SetLastWriteTime(string path, DateTime lastWriteTime) + { + throw new NotImplementedException(); + } + + /// + /// Sets the date and time, in coordinated universal time (UTC), that the specified file was last written to. + /// + /// The file for which to set the date and time information. + /// A System.DateTime containing the value to set for the last write date and time of path. This value is expressed in UTC time. + [Obsolete("Note: This method currently throws NotImplementedException because it has not yet been implemented.")] + public void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc) + { + throw new NotImplementedException(); + } + + /// + /// Creates a new file, writes the specified byte array to the file, and then closes the file. If the target file already exists, it is overwritten. + /// + /// The file to write to. + /// The bytes to write to the file. + /// is null. + public void WriteAllBytes(string path, byte[] bytes) + { + using (var stream = this.OpenWrite(path)) + { + stream.Write(bytes, 0, bytes.Length); + } + } + + /// + /// Creates a new file, writes a collection of strings to the file, and then closes the file. + /// + /// The file to write to. + /// The lines to write to the file. + /// is null. + public void WriteAllLines(string path, IEnumerable contents) + { + this.WriteAllLines(path, contents, Encoding.UTF8); + } + + /// + /// Creates a new file, write the specified string array to the file, and then closes the file. + /// + /// The file to write to. + /// The string array to write to the file. + /// is null. + public void WriteAllLines(string path, string[] contents) + { + this.WriteAllLines(path, contents, Encoding.UTF8); + } + + /// + /// Creates a new file by using the specified encoding, writes a collection of strings to the file, and then closes the file. + /// + /// The file to write to. + /// The lines to write to the file. + /// The character encoding to use. + /// is null. + public void WriteAllLines(string path, IEnumerable contents, Encoding encoding) + { + using (var stream = this.CreateText(path, encoding)) + { + foreach (var line in contents) + { + stream.WriteLine(line); + } + } + } + + /// + /// Creates a new file, writes the specified string array to the file by using the specified encoding, and then closes the file. + /// + /// The file to write to. + /// The string array to write to the file. + /// An object that represents the character encoding applied to the string array. + /// is null. + public void WriteAllLines(string path, string[] contents, Encoding encoding) + { + using (var stream = this.CreateText(path, encoding)) + { + foreach (var line in contents) + { + stream.WriteLine(line); + } + } + } + + /// + /// Creates a new file, writes the specified string to the file, and then closes the file. If the target file already exists, it is overwritten. + /// + /// The file to write to. + /// The string to write to the file. + /// is null. + public void WriteAllText(string path, string contents) + { + using (var stream = this.CreateText(path)) + { + stream.Write(contents); + } + } + + /// + /// Creates a new file, writes the specified string to the file using the specified encoding, and then closes the file. If the target file already exists, it is overwritten. + /// + /// The file to write to. + /// The string to write to the file. + /// The encoding to apply to the string. + /// is null. + public void WriteAllText(string path, string contents, Encoding encoding) + { + using (var stream = this.CreateText(path, encoding)) + { + stream.Write(contents); + } + } + + /// + /// Gets the of the file on the path. + /// + /// The path to the file. + /// The of the file on the path. + /// is null. + public SftpFileAttributes GetAttributes(string path) + { + if (this._sftpSession == null) + throw new SshConnectionException("Client not connected."); + + var fullPath = this._sftpSession.GetCanonicalPath(path); + + return this._sftpSession.RequestLStat(fullPath); + } + + /// + /// Sets the specified of the file on the specified path. + /// + /// The path to the file. + /// The desired . + /// is null. + public void SetAttributes(string path, SftpFileAttributes fileAttributes) + { + if (this._sftpSession == null) + throw new SshConnectionException("Client not connected."); + + var fullPath = this._sftpSession.GetCanonicalPath(path); + + this._sftpSession.RequestSetStat(fullPath, fileAttributes); + } + + // Please don't forget this when you implement these methods: is null. + //public FileSecurity GetAccessControl(string path); + //public FileSecurity GetAccessControl(string path, AccessControlSections includeSections); + //public DateTime GetCreationTime(string path); + //public DateTime GetCreationTimeUtc(string path); + //public void SetAccessControl(string path, FileSecurity fileSecurity); + //public void SetCreationTime(string path, DateTime creationTime); + //public void SetCreationTimeUtc(string path, DateTime creationTimeUtc); + + #endregion + + /// + /// Internals the list directory. + /// + /// The path. + /// The list callback. + /// + /// path + /// is null. + /// Client not connected. + private IEnumerable InternalListDirectory(string path, Action listCallback) + { + if (path == null) + throw new ArgumentNullException("path"); + + if (this._sftpSession == null) + throw new SshConnectionException("Client not connected."); + + var fullPath = this._sftpSession.GetCanonicalPath(path); + + var handle = this._sftpSession.RequestOpenDir(fullPath); + + var basePath = fullPath; + + if (!basePath.EndsWith("/")) + basePath = string.Format("{0}/", fullPath); + + var result = new List(); + + var files = this._sftpSession.RequestReadDir(handle); + + while (files != null) + { + result.AddRange(from f in files + select new SftpFile(this._sftpSession, string.Format(CultureInfo.InvariantCulture, "{0}{1}", basePath, f.Key), f.Value)); + + // Call callback to report number of files read + if (listCallback != null) + { + // Execute callback on different thread + this.ExecuteThread(() => { listCallback(result.Count); }); + } + + files = this._sftpSession.RequestReadDir(handle); + } + + this._sftpSession.RequestClose(handle); + + return result; + } + + /// + /// Internals the download file. + /// + /// The path. + /// The output. + /// The download callback. + /// output + /// path + /// is null or contains whitespace. + /// is null. + /// Client not connected. + private void InternalDownloadFile(string path, Stream output, SftpDownloadAsyncResult asyncResult, Action downloadCallback) + { + if (output == null) + throw new ArgumentNullException("output"); + + if (path.IsNullOrWhiteSpace()) + throw new ArgumentException("path"); + + if (this._sftpSession == null) + throw new SshConnectionException("Client not connected."); + + var fullPath = this._sftpSession.GetCanonicalPath(path); + + var handle = this._sftpSession.RequestOpen(fullPath, Flags.Read); + + ulong offset = 0; + + var data = this._sftpSession.RequestRead(handle, offset, this.BufferSize); + + // Read data while available + while (data.Length > 0) + { + // Cancel download + if (asyncResult != null && asyncResult.IsDownloadCanceled) + break; + + output.Write(data, 0, data.Length); + + output.Flush(); + + offset += (ulong)data.Length; + + // Call callback to report number of bytes read + if (downloadCallback != null) + { + // Execute callback on different thread + this.ExecuteThread(() => { downloadCallback(offset); }); + } + + data = this._sftpSession.RequestRead(handle, offset, this.BufferSize); + } + + this._sftpSession.RequestClose(handle); + } + + /// + /// Internals the upload file. + /// + /// The input. + /// The path. + /// The flags. + /// The upload callback. + /// input + /// path + /// is null. + /// is null or contains whitespace. + /// Client not connected. + private void InternalUploadFile(Stream input, string path, Flags flags, SftpUploadAsyncResult asyncResult, Action uploadCallback) + { + if (input == null) + throw new ArgumentNullException("input"); + + if (path.IsNullOrWhiteSpace()) + throw new ArgumentException("path"); + + if (this._sftpSession == null) + throw new SshConnectionException("Client not connected."); + + var fullPath = this._sftpSession.GetCanonicalPath(path); + + var handle = this._sftpSession.RequestOpen(fullPath, flags); + + ulong offset = 0; + + var buffer = new byte[this.BufferSize]; + + var bytesRead = input.Read(buffer, 0, buffer.Length); + var expectedResponses = 0; + var responseReceivedWaitHandle = new AutoResetEvent(false); + + do + { + // Cancel upload + if (asyncResult != null && asyncResult.IsUploadCanceled) + break; + + if (bytesRead > 0) + { + if (bytesRead < this.BufferSize) + { + // Replace buffer for last chunk of data + var data = new byte[bytesRead]; + Buffer.BlockCopy(buffer, 0, data, 0, bytesRead); + buffer = data; + } + + var writtenBytes = offset + (ulong)buffer.Length; + this._sftpSession.RequestWrite(handle, offset, buffer, null, (s) => + { + if (s.StatusCode == StatusCodes.Ok) + { + Interlocked.Decrement(ref expectedResponses); + responseReceivedWaitHandle.Set(); + + // Call callback to report number of bytes written + if (uploadCallback != null) + { + // Execute callback on different thread + this.ExecuteThread(() => { uploadCallback(writtenBytes); }); + } + } + }); + Interlocked.Increment(ref expectedResponses); + + offset += (uint)bytesRead; + + bytesRead = input.Read(buffer, 0, buffer.Length); + } + else if (expectedResponses > 0) + { + // Wait for expectedResponses to change + this._sftpSession.WaitHandle(responseReceivedWaitHandle, this.OperationTimeout); + } + } while (expectedResponses > 0 || bytesRead > 0); + + this._sftpSession.RequestClose(handle); + } + + partial void ExecuteThread(Action action); + + /// + /// Called when client is connected to the server. + /// + protected override void OnConnected() + { + base.OnConnected(); + + this._sftpSession = new SftpSession(this.Session, this.OperationTimeout, this.ConnectionInfo.Encoding); + + this._sftpSession.Connect(); + + // Resolve current running version + this.ProtocolVersion = (int)this._sftpSession.ProtocolVersion; + } + + /// + /// Called when client is disconnecting from the server. + /// + protected override void OnDisconnecting() + { + base.OnDisconnecting(); + + this._sftpSession.Disconnect(); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged ResourceMessages. + protected override void Dispose(bool disposing) + { + if (this._sftpSession != null) + { + this._sftpSession.Dispose(); + this._sftpSession = null; + } + + if (this._disposeConnectionInfo) + ((IDisposable)this.ConnectionInfo).Dispose(); + + base.Dispose(disposing); + } + } +} diff --git a/AirScout/Splash.Designer.cs b/AirScout/Splash.Designer.cs new file mode 100644 index 0000000..ee5fac7 --- /dev/null +++ b/AirScout/Splash.Designer.cs @@ -0,0 +1,73 @@ +namespace AirScout +{ + partial class Splash + { + /// + /// Erforderliche Designervariable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Verwendete Ressourcen bereinigen. + /// + /// True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Vom Windows Form-Designer generierter Code + + /// + /// Erforderliche Methode für die Designerunterstützung. + /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Splash)); + this.pb_Main = new System.Windows.Forms.PictureBox(); + ((System.ComponentModel.ISupportInitialize)(this.pb_Main)).BeginInit(); + this.SuspendLayout(); + // + // pb_Main + // + this.pb_Main.Dock = System.Windows.Forms.DockStyle.Fill; + this.pb_Main.Image = global::AirScout.Properties.Resources.Intro; + this.pb_Main.Location = new System.Drawing.Point(0, 0); + this.pb_Main.Name = "pb_Main"; + this.pb_Main.Size = new System.Drawing.Size(522, 394); + this.pb_Main.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; + this.pb_Main.TabIndex = 0; + this.pb_Main.TabStop = false; + // + // Splash + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(522, 394); + this.ControlBox = false; + this.Controls.Add(this.pb_Main); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "Splash"; + this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "Splash"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Splash_FormClosing); + this.Load += new System.EventHandler(this.Splash_Load); + ((System.ComponentModel.ISupportInitialize)(this.pb_Main)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.PictureBox pb_Main; + } +} \ No newline at end of file diff --git a/AirScout/Splash.cs b/AirScout/Splash.cs new file mode 100644 index 0000000..50e7f27 --- /dev/null +++ b/AirScout/Splash.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using System.Threading; +using ScoutBase.Core; + +namespace AirScout +{ + public partial class Splash : Form + { + Label status; + + // Define the CS_DROPSHADOW constant + private const int CS_DROPSHADOW = 0x00020000; + + // Override the CreateParams property + protected override CreateParams CreateParams + { + get + { + CreateParams cp = base.CreateParams; + // change window style to dropshadow + // does not work under Linux/Mono + if (!SupportFunctions.IsMono) + cp.ClassStyle |= CS_DROPSHADOW; + return cp; + } + } + + public Splash() + { + InitializeComponent(); + // set border fram to NONE + // does not work under Linux/Mono + if (!SupportFunctions.IsMono) + FormBorderStyle = FormBorderStyle.None; + else + FormBorderStyle = FormBorderStyle.FixedSingle; + status = new Label(); + status.Parent = pb_Main; + status.BackColor = Color.Transparent; + status.ForeColor = Color.White; + status.Location = new Point(84, this.Height - 20); + status.Height = 14; + status.Width = 350; + status.Font = new System.Drawing.Font(this.Font, FontStyle.Italic); + status.TextAlign = ContentAlignment.MiddleCenter; + } + + public void Status(string s) + { + Status(s, Color.White); + } + + public void Status(string s, Color color) + { + status.ForeColor = color; + status.Text = s; + this.Refresh(); + Application.DoEvents(); + } + + private void Splash_Load(object sender, EventArgs e) + { + } + + private void Splash_FormClosing(object sender, FormClosingEventArgs e) + { + } + } +} diff --git a/AirScout/Splash.resx b/AirScout/Splash.resx new file mode 100644 index 0000000..0262d6e --- /dev/null +++ b/AirScout/Splash.resx @@ -0,0 +1,7330 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + AAABAAkAAAAAAAEAIAAoIAQAlgAAAICAAAABACAAKAgBAL4gBABgYAAAAQAgAKiUAADmKAUASEgAAAEA + IACIVAAAjr0FAEBAAAABACAAKEIAABYSBgAwMAAAAQAgAKglAAA+VAYAICAAAAEAIACoEAAA5nkGABgY + AAABACAAiAkAAI6KBgAQEAAAAQAgAGgEAAAWlAYAKAAAAAABAAAAAgAAAQAgAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRz + cgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBw + cABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJ + CQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9v + bwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFx + cQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGho + aAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNy + cgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1s + awBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFx + cABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBv + cABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNz + dABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNy + cwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1t + bABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxs + bQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9u + bgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBw + cABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4 + dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNy + cgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5t + bAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1t + bABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1s + bABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBw + bwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBw + cABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNz + cwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1t + bQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1s + bABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1t + bQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFw + cABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFx + cQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJx + cgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBw + cABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5u + bQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1t + bABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRz + cgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBw + cABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJ + CQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9v + bwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFx + cQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGho + aAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNy + cgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1s + awBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFx + cABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYgwAAmIMAAJiDAACYg + wAAmIMAAJiDAACYgwAAmIMAAJiDAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBv + cABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNz + dABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAAlIMAAJSDAACUg + wAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAAlIMAAJSDAACUg + wAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAAlIMAAJSDAACUg + wAAlIMAAJSDAACUgwAAlIMAAJSDAACUgwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNy + cwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1t + bABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxs + bQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9u + bgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBw + cABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4 + dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNy + cgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5t + bAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1t + bABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1s + bABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBw + bwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBw + cABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNz + cwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1t + bQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1s + bABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1t + bQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFw + cABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFx + cQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJx + cgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBw + cABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5u + bQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1t + bABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRz + cgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBw + cABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJ + CQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9v + bwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFx + cQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGho + aAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNy + cgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1s + awBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFx + cABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBv + cABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNz + dABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNy + cwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1t + bABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxs + bQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9u + bgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBw + cABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4 + dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNy + cgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5t + bAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQAAABaAAAAlAAAALgAAAC/AAAAvgAAAL8AAAC+AAAApAAA + AIIAAABVAAAALQAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1t + bABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1s + bABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAGgAAAHIAAADHAAAA9gAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA9QAAANMAAACVAAAARgAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAD///8A////ACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBw + bwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBw + cABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAJAAAAdgAAAOMAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADIAAAAWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA////AP///wD///8AJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNz + cwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1t + bQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAyAAAAxQAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAACoAAAAGgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP///wD///8A////AP///wAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1s + bABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1t + bQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAABcAAAA7AAAAP8AAAD/AQMA/wUHEv8MDDD/ExVB/xUYUP8VGFD/EhQ//xET + Of8JCCn/BQcV/wUHBv8AAQD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAANEAAAAkAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////ACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAAHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFw + cABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFx + cQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJCQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAB4AAAA/wAAAP8EBgT/EhRO/x4dnf8lI8//Jh7r/ycg7f8oIfD/JyHv/ycg + 7f8nIOv/Jh7q/yUj1P8kIrf/HBqR/xUYVf8ICRj/AAEA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA0AAA + ABUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8AJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJx + cgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBw + cABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGhoaAAJCQkAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAACBAAAA/wECAP8SFEj/IyG4/ygh8v8mHvb/JRzu/yUe6f8lHej/JR3p/yUd + 6f8lHOr/Ixrs/yIa7v8hGPP/IRj4/yMa/P8kHPn/Ix7b/xoah/8KCyH/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAACjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5u + bQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1t + bABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFxcABoaGgACQkJAAAA + AAAAAAAAAAAAAAAAAAAAAACAAAAA/wcKEP8dHov/KCHw/yYd9P8lHej/JR3n/yUd5/8lHej/Ixvr/yEZ + 7f8gF+//Hxvo/yUi2v8tKsv/NDnA/zk+uP84Pbv/MzfB/ywn0/8oJOz/IR/d/xETYv8AAQD/AAAA/wAA + AP8AAAD/AAAA/gAAAEcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////ACYh + wAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRz + cgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBw + cABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNzdABxcXAAaGhoAAkJ + CQAAAAAAAAAAAAAAAAAAAABrAAAA/w0PJf8kIrv/JyD3/yUd6v8lHef/JBzp/yIa7P8gGO7/Hxnq/yUi + 3P8wMMb/RUms/2Von/+Gi5z/qKml/7q7tP/Dxbz/w8S8/7q7s/+lpqP/g4ea/15ho/89QcP/HCF3/wAA + BP8AAAD/AAAA/wAAAP8AAADGAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP// + /wD///8AJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYhwAAmIcAAJiHAACYh + wAAmIcAAJiHAACYhwAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9v + bwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3AAbWxrAHFx + cQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1tbABzc3QAcXFwAGho + aAAJCQkAAAAAAAAAAAAAAABbAAAA/A8SNP8mIdD/Jhz2/yMa6/8hGe3/Hxfu/yAd5P8qJ8//Oz+0/11g + nf+Bh5n/q6yn/9HSxv/s6+D////5//////////////////////////////////389//j49f/t7mw/3V6 + n/8qLkf/AAAA/wAAAP8AAAD/AAAA/wAAAEcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gACYe4AAmHuAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNy + cgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxsbQBzcnMAcG9wAG1s + awBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9ubgBtbWwAc3N0AHFx + cABoaGgACQkJAAAAAAAAAABFAAAA8g8TPf8kH97/IRj5/x4Z6v8kIdv/MTHE/0pOp/9wc5n/mZ2f/8bH + u//k5Nj//Pv1//////////////////////////////////////////////////////////////////// + ///t7ef/p6eo/0tLS/8NDQ3/AAAA/wAAAP8AAACmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBv + cABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4dwBvbm4AbW1sAHNz + dABxcXAAaGhoAAkJCQAAAAAtAAAA5gkMNf8hINn/KSXd/zg7t/9YXZ7/gYaY/7Cxq//X2Mz/8vHo//// + /v////////////////////////////////////////////////////////////////////////////// + ///////////////////f3tr/goGA/xoaGf8AAAD/AAAA6gAAABYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wAlHt8AJR7fACUe3wAlHt8AJR7fACUe3wAlHt8AJR7fACUe + 3wAlHt8AJR7fACUe3wAlHt8AJR7fACUe3wB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNycgBwcHAAbGxtAHNy + cwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHAAbWxsAG5tbAB4eHcAb25uAG1t + bABzc3QAcXFwAGhoaAAJCQkiAAAA1BcZH/9CSZH/aGqm/5CUm/+/wLX/4+PX//v69P////////////// + ///////////////////////////////////////////9/////f/+/PX/6+vp/9rf6v/S3PT/zdr9/8PO + +/++yPr/vsn6/8DL+//M2f3/4Ov//+/x8v+TkIb/GxoW/wAAAP8AAABTAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8AJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1tbABzcnIAcHBwAGxs + bQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxAG1tbQBtbW0AcHBwAG1sbABubWwAeHh3AG9u + bgBtbWwAc3N0AHFxcBRoaGhJS0tMz21tbf+ioaP/z87L/+zs4f////v///////////////////////// + ///////////////////////////+//j59f/a4vD/v8np/5im3f90i9v/V3Hd/z1a4f8wUeH/KU7p/yVN + 8f8cQu//GD3v/xk+7/8aQO//I0vw/y9V8f9aevr/l6vr/3BzdP8SEAf/AAAAfgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBwbwBtbWwAc3JyAHBw + cABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcABtbWwAcHBwAHFxcQBtbW0AbW1tAHBwcABtbGwAbm1sAHh4 + dwBvbm4hbW1sWHNzdKCLjIvYrq6u/9jY2f/29vf///////////////////////////////////////// + ///////////+/+3y+v/F0vb/obHv/3CL6f9LaOf/MFPn/x1D5/8WPO//Fzzv/xk+8f8bQPL/HkLx/x9D + 8P8gQ+//IkXv/yJG8P8iRu//IkXw/yBE7/8eQe//GD3u/xxD9f8+XuP/Rk9w/wkHAIkJBwAACQcAAAkH + AAAJBwAACQcAAAkHAAAJBwAACQcAAAkHAAAJBwAACQcAAAkHAAAJBwAACQcAAAkHAAAJBwAACQcAAAkH + AAAJBwAACQcAAAkHAAAJBwAACQcAAAkHAAAJBwAACQcAAAkHAAAJBwAACQcAAAkHAAAJBwAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNzcwBwcG8AbW1sAHNy + cgBwcHAAbGxtAHNycwBwb3AAbWxrAHFxcQBwcHAAbW1sAHBwcABxcXEAbW1tAG1tbQBwcHACbWxsL25t + bGp4eHe0k5ST5Le3t//e3t//+Pj5///////////////////////////////////////////////9/+3y + 9//AzvT/kqjy/1597/89Xu7/Ikjs/xY87v8XPe7/GT/w/x5C8f8hRfH/I0fw/yNH8P8jRvD/I0fw/yNG + 7/8jR+//I0fv/yNH7/8jRu//I0bv/yNH7/8jR/D/I0fw/yNH7/8iRe//HkP3/yxP7f8/SnKbO13kACFE + 8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE + 8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1sbABzc3MAcHBvAG1t + bABzcnIAcHBwAGxsbQBzcnMAcG9wAG1sawBxcXEAcHBwAG1tbABwcHAAcXFxBm1tbT1tbW14fX19w5yb + nPDDw8T/5ubm//39/v/////////////////////////////////////////+////9v/d4u7/qrjs/2uG + 6/88Xez/IEbr/xY87v8YPe//G0Dv/yFE8P8jRu//I0fv/yNH8P8jR+//I0bv/yNG7/8jRvD/I0bw/yNH + 7/8jRvD/I0fv/yNG7/8jR+//I0fw/yNH8P8jR+//I0fw/yNH8P8jRu//I0fv/yNH7/8gRfT/LlHr8Dtd + 5DEhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE + 8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJh7gACYe + 4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gAHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFwcABtbGwAc3NzAHBw + bwBtbWwAc3JyAHBwcABsbG0Ac3JzAHBvcABtbGsAcXFxAHBwcAttbWxGcHBwh4KCgsyjo6T4zc3P/+zs + 7f//////////////////////////////////////////////+//8+vD/2Nna/6Wv0/9geNj/MVPn/xo/ + 7P8YPe//HEHw/yFE8P8jR/D/I0fw/yNG7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH7/8jR/D/I0bw/yNH + 8P8jRu//I0bv/yNH8P8jR+//I0bv/yNH8P8jR/D/I0fv/yNH7/8jR+//I0bv/yNH8P8jR+//I0fv/yFG + 8f8hRfLNIUTyFSFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE + 8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE8gAhRPIAIUTyACFE + 8gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wAmHt8AJh7fACYe3wAmHt8AJh7fACYe3wAmHt8AJh7fACYe3wAmHt8AJh7fACYe3wB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJxcgBxcHAAbWxsAHNz + cwBwcG8AbW1sAHNycgBwcHAAbGxtAHNycwBwb3ARbWxrTXFxcZaIiIjVqamq/NPT0//x8fL///////// + //////////////////////////////////////j/8/Lp/8rN1f+Un8r/XHLL/zFR2P8bQOf/GT/w/x5D + 8f8iRfD/I0fw/yNH8P8jR+//I0fw/yNH8P8jR+//I0bv/yNG7/8jR/D/I0bw/yNG7/8jRu//I0fw/yNG + 8P8jR+//I0fv/yNH8P8jR/D/I0fv/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fw/yNH7/8jR/D/I0jx/yNH + 8P8jR+//I0fv/yNH76IjR+8AJEfwACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8AJh7fACYe3wAmHt8AJh7fACYe3wAmHt8AJh7fACYe3wAmHt8AJh7fACYe3wAmHt8AdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyAG5ubQBycXIAcXBwAG1s + bABzc3MAcHBvAG1tbABzcnIAcHBwE2xsbVRzcnOdioqL3a+vsP/X19f/9vb2//////////////////// + ///////////////////////////2/+Xl4f+6v9H/gI7G/01mzv8pSdv/G0Dp/xk/8v8eQ/L/Ikbx/yNH + 8P8jR/D/I0fv/yNH8P8jR+//I0fv/yNH7/8jRu//I0fv/yNH7/8jRu//I0fv/yNG7/8jRu//I0bv/yNH + 7/8jR+//I0fw/yNH7/8jR/D/I0fv/yNH7/8jR/D/I0fv/yNH8P8jR+//I0bv/yNH8P8jSPD/I0fv/yRC + 7f8jR+//I0jw/yNH7/8jRu//I0fvciRH8AAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AB4W3wAeFt8AHhbfAB4W3wAeFt8AHhbfAB4W3wAeFt8AHhbfAB4W3wAeFt8AHhbfAHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vAHRzcgBubm0AcnFyAHFw + cABtbGwAc3NzAHBwbxJtbWxUc3JynIqKi9+xsbL/29vc//j4+f////////////////////////////// + ///////////8//399P/X2dv/prHV/2t+y/89WtT/IkTh/xk/7v8bQPL/IEXy/yJG8f8jR/D/I0bv/yNH + 7/8jR/D/I0fw/yNH8P8jR+//I0bv/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0fv/yNG7/8jR/D/I0fv/yNH + 7/8jR+//I0fv/yNH8P8jRvD/I0fv/yNH7/8jR/D/I0fw/yNG7/8jR+//I0fv/yNG7/8jSPD/JETu/yYq + 4/8mIOD/JS3k/yNF7/8jSfD/I0fv/yNH7/MkR/A7I0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8AUFDnAFBQ5wBQUOcAUFDnAFBQ5wBQUOcAUFDnAFBQ5wBQUOcAUFDnAFBQ + 5wB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAc3JyAG9vbwB0c3IAbm5tAHJx + cgBxcHARbWxsVHNzc52JiorhsrGy/9zb3P/5+fn///////////////////////////////////////// + +v/19fH/x8ze/5Gf1f9ZcdH/MFDd/xxA5/8ZP/H/HUPy/yFF8f8jRvD/I0fw/yNH7/8jR+//I0bv/yNH + 8P8jR+//I0fw/yNH8P8jRvD/I0bv/yNH8P8jR/D/I0bv/yNH8P8jR/D/I0fw/yNH8P8jR+//I0fv/yNH + 7/8jR/D/I0fw/yNG7/8jR/D/I0bv/yNH7/8jR+//I0fv/yNG7/8jRu//I0fv/yNH7/8jR+//I0bv/yYp + 4/8mHN7/Jh7f/yYc3v8mJ+P/I0Ht/yNJ8P8jR+//I0fv0CNH7xMjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AMTM+gDEzPoAxMz6AMTM+gDEzPoAxMz6AMTM+gDEzPoAxMz6AMTM + +gDEzPoAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHNycgBvb28AdHNyBW5u + bUVycXKViYmJ4LKysv/c3Nz/+fn5//////////////////////////////////////////r/6+zw/7TB + 5f97jtr/RmPb/yVH5f8ZP+z/GkDy/x9E8v8iRvH/I0bv/yNH8P8jR+//I0fw/yNH7/8jR/D/I0fv/yNH + 7/8jRvD/I0bv/yNH8P8jR+//I0fv/yNH7/8jR+//I0bv/yNG7/8jR/D/I0fw/yNG7/8jR/D/I0fv/yNH + 7/8jR+//I0bv/yNG8P8kRu//I0fw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0nw/yU0 + 5/8mHN//Jh7f/yYf4P8lHuD/Jhzf/yYk4f8kQez/I0nw/yNG7/8jRu+aI0fwACNH8AAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A09L8ANPS/ADT0vwA09L8ANPS/ADT0vwA09L8ANPS + /ADT0vwA09L8AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dABzcnIAb29vYH9/ + f8ypqKr/19fY//j4+f///////////////////////////////////////f76/9zi8P+ktOn/aIDh/zhY + 4v8eQ+j/GD7v/x1C8v8gRfH/Ikfw/yNH8P8jRu//I0fv/yNH7/8jR/D/I0bw/yNH7/8jR+//I0fv/yNH + 7/8jRu//I0bv/yNH8P8jR/D/I0fv/yNG7/8jR+//I0fw/yNG8P8jRu//I0bv/yNH7/8jR+//I0fw/yNG + 8P8jR+//I0fw/yNH8P8jRu//JEfw/yNH8P8jR/D/I0bv/yNH7/8jR+//I0bv/yNH7/8jR/D/I0jw/yRD + 7f8mIuD/Jh3f/yYf4P8mH+D/Jh/f/yYf4P8mHd//JiXh/yRB7P8jSPD/I0bv/yNH8FwjR/AAI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AM/P+wDPz/sAz8/7AM/P+wDPz/sAz8/7AM/P + +wDPz/sAz8/7AM/P+wB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1 + dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQAdXV0AHV1dAB1dXQCc3Jyiays + rf/u7u/////////////////////////////////////+//n7/P/L1vL/kqbt/1Vz5v8tT+j/Gj/s/xg+ + 8P8eQ/H/IUXw/yNG7/8jR/D/I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNG7/8jRu//I0fw/yNH + 7/8jRu//I0bv/yNH7/8jR/D/I0fv/yNH8P8jR/D/I0fv/yNH8P8jR/D/I0fv/yNG7/8jRu//I0fw/yNG + 8P8jR+//I0fw/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fw/yNG7/8jR+//I0fv/yNH7/8jR/D/I0fw/yNJ + 8P8lNej/Jh3f/yYf4P8mH+D/Jh7g/yYe4P8mH9//Jh/g/yYd3/8mJOH/JELt/yNJ8P8jR/DjI0fwIiNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Az8/7AM/P+wDPz/sAz8/7AM/P + +wDPz/sAz8/7AM/P+wDPz/sAcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBw + bwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBw + bwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBw + bwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBw + bwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBw + bwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvAHBwbwBwcG8AcHBvPaOj + o////////////////////////////////v/x9fr/vszz/4OZ7f9HaOv/JEjs/xc97f8bQPH/IETx/yJG + 8P8jRu//I0bv/yNH7/8jRu//I0fw/yNH8P8jRu//I0fv/yNH7/8jR/D/I0fv/yNH7/8jRu//I0bv/yNH + 8P8jR+//I0bv/yNG7/8jR+//I0bv/yNH7/8jR+//I0fw/yNH7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNG + 7/8jRu//I0fv/yNH7/8jR+//I0bv/yNH8P8jR/D/I0bv/yNH8P8jRu//I0bv/yNH7/8jR+//I0bv/yNH + 8P8jR+//Jifj/yYd3/8mH+D/Jh7g/yYe4P8mH+D/Jh7f/yYf4P8mHuD/Jhzf/yUp4/8jR+//I0fv/yNH + 77AjRu8GI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM/P+gDPz/oAz8/6AM/P + +gDPz/oAz8/6AM/P+gDPz/oAz8/6AHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cDafn6D7/f3+////////////9/n6/7nG7P94kfD/PF7s/x5D7v8WPO7/HEDv/yFF7/8jR/D/I0bv/yNH + 8P8jR+//I0fw/yNG7/8jRu//I0fw/yNH8P8jR/D/I0bv/yNH7/8jRvD/I0fw/yNH7/8jR/D/I0bw/yNH + 8P8jR/D/I0fv/yNH7/8jR/D/I0fv/yNG7/8jR/D/I0fw/yNH7/8jR+//I0bv/yNH7/8jR/D/I0fw/yNG + 7/8jRu//I0bv/yNH7/8jRu//I0fv/yNG7/8jR/D/I0fv/yNG7/8jR/D/I0bw/yNG7/8jR+//I0fv/yNH + 8P8jSPD/JEHt/yYg4P8mHd//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mHN//JDTo/yNJ + 8P8jR+//I0fvTyNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Azs/6AM7P + +gDOz/oAzs/6AM7P+gDOz/oAzs/6AM7P+gBwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBwcABwcHAAcHBwAHBw + cABwcHAAcHBwjMLCw///////0d38/0Vn7f8aP+v/Fjzv/x1B7/8iRfD/I0bw/yNG7/8jR+//I0bv/yNH + 8P8jRu//I0bv/yNH8P8jR+//I0fw/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH + 8P8jR/D/I0bv/yNH7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH8P8jR+//I0fv/yNH7/8jR/D/I0fw/yNH + 7/8jRu//I0fv/yNH7/8jR/D/I0fv/yNG8P8jR/D/I0fv/yNH7/8jR/D/I0bv/yNH8P8jR/D/I0fv/yNH + 8P8jR/D/I0nx/yQ56v8mHd//Jh7f/yYf4P8mHuD/Jh/g/yYf4P8lHt//Jh7g/yYf4P8mH+D/Jh3f/yYk + 4f8kRe7/I0jw/yNH8GgjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AM7P + +wDOz/sAzs/7AM7P+wDOz/sAzs/7AM7P+wDOz/sAJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNI8ABycnIAcnJyAHJycgBycnIAcnJyAHJy + cgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJy + cgBycnIAcnJyAHJychGCgHrLsbrW/zlf9/8VO+//Ikbw/yNH7/8jRu//I0fw/yNH8P8jRvD/I0bv/yNG + 7/8jR/D/I0bw/yNG7/8jR+//I0fv/yNH8P8jR/D/I0bv/yNG7/8jR/D/I0fw/yNH7/8jRu//I0fw/yNH + 7/8jR+//I0fw/yNG8P8jR+//I0fv/yNH7/8jR/D/I0bv/yNG8P8jR+//I0bv/yNG7/8jR+//I0fv/yNH + 7/8jR+//I0fw/yNH8P8jR+//I0fv/yNG7/8jRu//I0fv/yNH7/8jR/D/I0fw/yNG7/8jR/D/I0fv/yNH + 7/8jR+//I0fv/yNJ8P8lNOf/Jhzf/yYe3/8mH9//Jh/g/yYe4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mIOD/JD7s/yNJ8P8jRu9mI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8Az8/6AM/P+gDPz/oAz8/6AM/P+gDPz/oAz8/6ACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjSPAAI0bvACNJ8QAjSPAAcnJyAHJy + cgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJy + cgBycnIAcnJyAHJycgBycnIAeXdtI1RgmJcaQPX/JEfw/yNH7/8jRu//I0bw/yNH7/8jR+//I0fw/yNH + 8P8jRu//I0bw/yNH8P8jR+//I0fw/yNH7/8jR/D/I0fv/yNH7/8jR+//I0bw/yNH8P8jRu//I0fv/yNH + 7/8jR/D/I0fw/yNH8P8jR/D/I0bv/yNH7/8jR+//I0bv/yNH8P8jR/D/I0fw/yNG7/8jRu//I0fv/yNG + 7/8jR/D/I0fv/yNH7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH + 7/8jR+//I0fv/yNH8P8jSfD/JS/l/yYd3/8mHt//Jh7f/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7f/yYf + 4P8mH+D/Jhvf/yQ66v8jSvH/I0fvWyNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////ANPS/ADT0vwA09L8ANPS/ADT0vwA09L8ANPS/AAmIcAAJiHAACYhwAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0jwACNG7wAjSfEAI0jwACNI + 8AAjR/AAI0jwAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJy + cgBycnIAcnJyAHJycgBycnIAcnJyAHl3bQBUYJgAI0bwwSNG8P8jR/D/I0fw/yNH8P8jR+//I0fv/yNG + 7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fw/yNG7/8jR+//I0fv/yNG7/8jR/D/I0fv/yNH + 7/8jR+//I0fw/yNG7/8jRu//I0fv/yNG8P8jR/D/I0fv/yNH7/8jRu//I0fv/yNH8P8jR+//I0fw/yNH + 8P8jR+//I0fw/yNG7/8jRu//I0fv/yNH8P8jRvD/I0bv/yNH8P8jR+//I0fv/yNG8P8jR/D/I0fw/yNG + 7/8jRu//I0fv/yNH8P8jR/D/I0jw/yYr5P8mHd//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYd3/8kPOv/I0nx/yNH70kjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8AvsL5AL7C+QC+wvkAvsL5AL7C+QC+wvkAJiHAACYhwAAmIcAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNI8AAjRu8AI0nxACNI + 8AAjSPAAI0fwACNI8AAjSfAAI0XuACNI8ABycnIAcnJyAHJycgBycnIAcnJyAHJycgBycnIAcnJyAHJy + cgBycnIAcnJyAHJycgBycnIAcnJyAHJycgB5d20AI0fvACNH71MjR/D+I0fw/yNH8P8jR+//I0fw/yNH + 8P8jRu//I0fw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fw/yNH8P8jRu//I0fv/yNH + 7/8jR+//I0fw/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0fw/yNH7/8jR+//I0fv/yNG7/8jRvD/I0fv/yNH + 7/8jR+//I0fv/yNH7/8jR+//I0bv/yNG7/8jR+//I0bv/yNH7/8jR+//I0bv/yNG8P8jR/D/I0fv/yNG + 7/8jRu//I0bv/yNH7/8jR+//I0fw/yNH7/8mJuL/Jh3g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mIeD/JEHt/yNI8PEjRu8xI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AKGt9AChrfQAoa30AKGt9AChrfQAoa30ACYhwAAmIcAAJiHAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjSPAAI0bvACNJ + 8QAjSPAAI0jwACNH8AAjSPAAI0nwACNF7gAjSPAAI0rxACNJ8AAkR/AAJEfwACRH8AAkR/AAJEfwACRH + 8AAkR/AAJEfwACRH8AAkR/AAJEfwACRH8AAkR/AAJEfwACRH8AAkR/AHI0fwwSNH7/8jR+//I0fw/yNH + 8P8jR/D/I0fv/yNG7/8jRvD/I0bv/yNH7/8jR/D/I0fv/yNH7/8jR/D/I0fv/yNH7/8jR+//I0fv/yNH + 7/8jRu//I0bw/yNH8P8jRvD/I0fw/yNH8P8jR+//I0bv/yNG7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH + 7/8jR+//I0fv/yNG7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0bv/yNG7/8jR+//I0bv/yNG + 8P8jRu//I0bv/yNG7/8jRu//I0fv/yNH8P8kRO7/JiLh/yYd4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mHd//JiTh/yRF7v8jR/DYI0fwEiNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8Ac4ntAHOJ7QBzie0Ac4ntAHOJ7QAmIcAAJiHAACYh + wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0jwACNG + 7wAjSfEAI0jwACNI8AAjR/AAI0jwACNJ8AAjRe4AI0jwACNK8QAjSfAAI0fvACRH8AAkR/AAJEfwACRH + 8AAkR/AAJEfwACRH8AAkR/AAJEfwACRH8AAkR/AAJEfwACRH8AAkR/AAJEfwACNH8FMjR+/+I0fv/yNH + 8P8jR/D/I0fv/yNH7/8jR+//I0bv/yNG7/8jRu//I0fw/yNH7/8jR+//I0bv/yNH7/8jR/D/I0fw/yNH + 7/8jRu//I0bv/yNH8P8jR/D/I0bv/yNH8P8jRvD/I0fv/yNG7/8jR/D/I0fv/yNG8P8jRu//I0fw/yNH + 7/8jRu//I0bv/yNG8P8jR+//I0fv/yNG7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH8P8jRvD/I0fv/yNG + 7/8jRvD/I0fv/yNH8P8jRu//I0fv/yNH8P8jSPD/JEDs/yYg4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jhzf/yUp4/8jSfD/I0fwwCNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////ADU54wA1OeMANTnjADU54wA1OeMAJiHAACYh + wAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNI + 8AAjRu8AI0nxACNI8AAjSPAAI0fwACNI8AAjSfAAI0XuACNI8AAjSvEAI0nwACNH7wAjR/AAI0jwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AHI0fvwiNH + 7/8jR+//I0fw/yNH7/8jRu//I0fv/yNH7/8jRvD/I0fv/yNH7/8jR+//I0fv/yNH8P8jR/D/I0fw/yNH + 8P8jR+//I0bv/yNG7/8jR+//I0fv/yNH7/8jRvD/I0bv/yNH7/8jR/D/I0fw/yNG7/8jRu//I0fw/yNH + 8P8jRu//I0bv/yNG7/8jR/D/I0fv/yNH7/8jRu//I0bv/yNH8P8jR/D/I0fv/yNH8P8jR/D/I0fw/yNH + 7/8jRu//I0fw/yNH7/8jRu//I0fv/yNG7/8jRu//I0nw/yQ46f8mHd//Jh/g/yYe3/8mHuD/Jh7g/yYe + 4P8mH+D/Jh/g/yYe3/8mH+D/Jh7g/yYc3/8lL+X/I0nw/yNH8JAjR/AAI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AIxnfACMZ3wAjGd8AIxnfACYh + wAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjSPAAI0bvACNJ8QAjSPAAI0jwACNH8AAjSPAAI0nwACNF7gAjSPAAI0rxACNJ8AAjR+8AI0fwACNI + 8AAjSPAAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNG + 71IjR/D9I0fv/yNG7/8jR+//I0fw/yNH8P8jR/D/I0bv/yNH8P8jRvD/I0fv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNG7/8jR+//I0fw/yNH8P8jR+//I0fw/yNH + 8P8jR+//I0bv/yNH7/8jR+//I0fw/yNH8P8jRu//I0fv/yNG7/8jRu//I0fv/yNH7/8jR/D/I0bv/yNG + 7/8jRvD/I0fv/yNH7/8jRu//I0fv/yNH7/8jRu//I0bw/yNJ8P8lLuX/Jh3f/yYf4P8mH+D/Jh7g/yYe + 4P8mHuD/JR7f/yYf4P8mH+D/Jh/g/yYf4P8mHN//JTHm/yNJ8P8jR+9eI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ACUe3wAlHt8AJR7fACUe + 3wAmIcAAJiHAACYhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0jwACNG7wAjSfEAI0jwACNI8AAjR/AAI0jwACNJ8AAjRe4AI0jwACNK8QAjSfAAI0fvACNH + 8AAjSPAAI0jwACNF7gAjSO8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8FI0fvuCNH8P8jRvD/I0fv/yNH8P8jR/D/I0fv/yNH7/8jRvD/I0bw/yNG7/8jRu//I0fv/yNH + 7/8jRu//I0fw/yNH8P8jR/D/I0fw/yNG7/8jR+//I0fv/yNH8P8jR/D/I0fw/yNH8P8jR/D/I0fw/yNH + 8P8jRvD/I0bw/yNG8P8jR/D/I0fv/yNH8P8jR/D/I0bw/yNH8P8jR+//I0bw/yNG8P8jRvD/I0fw/yNH + 8P8jRvD/I0bw/yNH7/8kR/D/JEfw/yNG8P8jRu//I0fw/yNI8P8jRO7/JSPh/yYd3/8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yUe3/8mH9//Jh/g/yYe4P8mHuD/Jhzf/yUx5f8jSfD0I0bwMyNG8AAjRvAAI0bwACNG + 8AAjRvAAI0bwACNG8AAjRvAAI0bwACNG8AAjRvAAI0bwACNG8AAjRvAAI0bwACNG8AAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJh/gACYf + 4AAmH+AAJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNI8AAjRu8AI0nxACNI8AAjSPAAI0fwACNI8AAjSfAAI0XuACNI8AAjSvEAI0nwACNH + 7wAjR/AAI0jwACNI8AAjRe4AI0jvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNG70gjR/D8I0fw/yNG8P8jRu//I0bv/yNH8P8jRu//I0fv/yNH8P8jR/D/I0fw/yNH + 7/8jR+//I0bv/yNG8P8jRvD/I0bv/yNH8P8jRvD/I0fw/yNH7/8jR/D/I0bw/yRH8P8jRu//I0bv/yNH + 8P8jR+//I0bw/yNH7/8jR/D/I0bw/yNH8P8jR+//I0fw/yNH8P8jR/D/I0fv/yNH8P8jRvD/I0fw/yNH + 8P8jRvD/I0bw/yNH8P8jRu//I0bv/yNH8P8jRu//I0bv/yNG8P8jSfH/JTnp/yYd3/8mH+D/JR7f/yYf + 4P8lH9//JR7f/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh7f/yYd3/8lMeb/I0nwySNG7wcjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAABAAAAHAAAADsAAABJAAAAVgAAAGIAAABSAAAARQAAADQAAAAWAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ACYf + 4AAmH+AAJh/gACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjSPAAI0bvACNJ8QAjSPAAI0jwACNH8AAjSPAAI0nwACNF7gAjSPAAI0rxACNJ + 8AAjR+8AI0fwACNI8AAjSPAAI0XuACNI7wAlOOkAJD3rACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8DI0bvsyNH7/8jRvD/I0fv/yNH8P8jR/D/I0bw/yNH8P8jR/D/I0fw/yNH + 8P8jR/D/I0fw/yNH7/8jR/D/I0bw/yNH7/8jR+//I0fw/yNH8P8jRvD/I0bv/yNH8P8jRvD/I0fv/yNH + 7/8jR+//I0bw/yNH7/8jRu//I0fv/yNG7/8jRvD/I0bw/yNG7/8jR/D/I0fv/yNH8P8jR+//I0bv/yNH + 8P8jR/D/I0fv/yNH7/8jR+//JEfw/yNG8P8kR/D/I0fv/yNG7/8jR+//I0jw/yYq5P8mHd//Jh/g/yUe + 3/8mH+D/Jh/g/yYf4P8mH9//Jh7f/yYf3/8mHuD/Jh/g/yYf3/8mHd//JS3l/yNL8H0jRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAgAAADsAAACBAAAAvQAAAN4AAAD6AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD0AAAA2QAA + ALUAAABmAAAAHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wAlHt8AJR7fACUe3wAmIcAAJiHAACYhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0jwACNG7wAjSfEAI0jwACNI8AAjR/AAI0jwACNJ8AAjRe4AI0jwACNK + 8QAjSfAAI0fvACNH8AAjSPAAI0jwACNF7gAjSO8AJTjpACQ96wAkP+sAI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7zojRu/4I0fv/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fv/yNG + 7/8jRu//I0fv/yNH7/8jR/D/I0bw/yNG7/8jRu//I0fv/yNH8P8jR+//I0fv/yNG7/8jRu//I0fv/yNH + 8P8jR/D/I0fv/yNG8P8jR/D/I0fw/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0fv/yNG7/8jRu//I0bv/yNH + 7/8jR+//I0fv/yNH7/8jRu//I0fw/yRH7/8jR/D/JEfv/yNH7/8jRu//I0jw/yQ/7P8mIOD/Jh7g/yYe + 4P8mHuD/Jh7g/yYf4P8mHuD/Jh7f/yUd3/8lK+X/JS3l/yYf4P8mH+D/Jh7g/yYf4P8lLeVPJS3lACUt + 5QAlLeUAJS3lACUt5QAlLeUAJS3lACUt5QAlLeUAJS3lACUt5QAlLeUAJS3lACUt5QAlLeUAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAHAAAAXQAAAL4AAAD4AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAN8AAAB8AAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AB8V3wAfFd8AJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNI8AAjRu8AI0nxACNI8AAjSPAAI0fwACNI8AAjSfAAI0XuACNI + 8AAjSvEAI0nwACNH7wAjR/AAI0jwACNI8AAjRe4AI0jvACU46QAkPesAJD/rACNE7gAjR+8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0fwpiNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH + 8P8jR+//I0fw/yNH7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH7/8jR/D/I0fv/yNH8P8jRvD/I0bw/yNH + 7/8jRvD/I0fw/yNH7/8jR+//I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH7/8jR+//I0fw/yNH + 7/8jR/D/I0fv/yNH7/8jRu//I0bw/yNH8P8jRu//JEfw/yNH7/8jRu//I0bv/yNJ8f8lMOb/Jhzf/yYe + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYd3/8mIuD/JTXo/yU26f8mJeL/Jh7f/yYe3/8mH+DwJhzfLiYc + 3wAmHN8AJhzfACYc3wAmHN8AJhzfACYc3wAmHN8AJhzfACYc3wAmHN8AJhzfACYc3wAmHN8AJhzfAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAA3AAAAvwAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAM0AAABEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wBSY+cAUmPnACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjSPAAI0bvACNJ8QAjSPAAI0jwACNH8AAjSPAAI0nwACNF + 7gAjSPAAI0rxACNJ8AAjR+8AI0fwACNI8AAjSPAAI0XuACNI7wAlOOkAJD3rACQ/6wAjRO4AI0fvACNG + 7gAkRu4AI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8C4jRvDyI0fv/yNH7/8jR+//I0fv/yNH + 7/8jRu//I0fw/yNH8P8jR+//I0fv/yNH7/8jR/D/I0fw/yNH7/8jR+//I0bv/yNG7/8jR+//I0fw/yNG + 8P8jRu//I0fv/yNH8P8jRu//I0bv/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH7/8jRu//I0bv/yNG + 7/8jRvD/I0bw/yNH7/8jR/D/I0fw/yNH8P8jR/D/I0fv/yRH8P8jR+//I0bw/yNI8P8kRO7/JiLh/yYd + 3/8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHeD/Jinj/yU26f8mNun/Jink/yYd4P8mHuD/Jh/g0SYf + 4AgmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAB1AAAA8QAAAP8AAAD/AAAA/wABAP8BAgD/BQcJ/wwPG/8NEST/DREq/w0RJf8MEB3/BQYJ/wEC + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA9QAAAHIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AIaZ7wAmIcAAJiHAACYhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0jwACNG7wAjSfEAI0jwACNI8AAjR/AAI0jwACNJ + 8AAjRe4AI0jwACNK8QAjSfAAI0fvACNH8AAjSPAAI0jwACNF7gAjSO8AJTjpACQ96wAkP+sAI0TuACNH + 7wAjRu4AJEbuACNJ8QAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0bvjiNH7/8jR+//I0fw/yNH + 8P8jR+//I0fv/yNH8P8jR+//I0fv/yNH7/8jRu//I0bv/yNH7/8jR+//I0fw/yNH8P8jRu//I0bv/yNG + 8P8jRu//I0fv/yNH7/8jRu//I0bv/yNG7/8jR+//I0fv/yNH8P8jR+//I0bv/yNH8P8jRu//I0fw/yNH + 7/8jRu//I0fw/yNH8P8jRu//I0fw/yNH8P8jR+//I0bw/yNH8P8jR/D/I0fv/yNH7/8jSfD/JDbo/yYc + 3/8mHt//Jirk/yYi4f8mHt//Jh7g/yYe4P8mHuD/Jh7g/yUw5v8lNun/Jjbp/yUu5v8mHuD/Jh7g/yYf + 4JMmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + ABEAAACiAAAA/wAAAP8AAQD/BggL/w8SPv8aGnD/HyCo/yIdx/8mIdL/JiLX/ycj2v8mItf/JiLU/yId + xv8fIJr/Fxhk/wwOJ/8BAgD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAApQAAABUAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wDGyPkAJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNI8AAjRu8AI0nxACNI8AAjSPAAI0fwACNI + 8AAjSfAAI0XuACNI8AAjSvEAI0nwACNH7wAjR/AAI0jwACNI8AAjRe4AI0jvACU46QAkPesAJD/rACNE + 7gAjR+8AI0buACRG7gAjSfEAJDzqACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7xwjR+/iI0fv/yNH + 7/8jR+//I0fv/yNH7/8jRu//I0bv/yNH8P8jR/D/I0fw/yNH7/8jR+//I0bw/yNH8P8jR+//I0bv/yNH + 8P8jRu//I0fw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fw/yNH7/8jR/D/I0fw/yNH7/8jR/D/I0bv/yNH + 8P8jR/D/I0fv/yNH8P8jR/D/I0fv/yNG7/8jR/D/I0bv/yNH8P8jR/D/I0fv/yNG8P8jSPD/I0bv/yYl + 4v8mHN//JiLh/yU06f8mIuH/Jh7g/yYf4P8mH+D/Jh3g/yYj4f8lNen/JTXo/yU26P8lL+b/Jh7f/yYe + 3/YlH983JR/fACUf3wAlH98AJR/fACUf3wAlH98AJR/fACUf3wAlH98AJR/fACUf3wAlH98AJR/fACUf + 3wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AB0AAAC9AAAA/wABAP8MDiT/Ght1/yIfxf8nIuj/Jx/1/yYf8v8mHvD/JR3u/yUd7f8lHe3/JRzt/yUd + 7v8mHu//Jh7z/ycg9f8mIdz/HR6U/wsOK/8AAQD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADGAAAAIgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////ACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjSPAAI0bvACNJ8QAjSPAAI0jwACNH + 8AAjSPAAI0nwACNF7gAjSPAAI0rxACNJ8AAjR+8AI0fwACNI8AAjSPAAI0XuACNI7wAlOOkAJD3rACQ/ + 6wAjRO4AI0fvACNG7gAkRu4AI0nxACQ86gAkQe0AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvbSNG + 7/8jR+//I0jw/yNJ8f8jR+//I0fv/yNH7/8jR/D/I0fw/yNH8P8jRu//I0fv/yNG8P8jR/D/I0fv/yNG + 7/8jR/D/I0bv/yNH7/8jR/D/I0bv/yNH8P8jR/D/I0fw/yNH8P8jR/D/I0fw/yNH8P8jR+//I0bv/yNH + 7/8jR/D/I0fw/yNG7/8jRvD/I0fw/yNH7/8jR+//I0fw/yNH7/8jR/D/I0fw/yNH7/8jR/D/I0rx/yQ5 + 6f8mHd7/Jh/f/yUv5v8lNOj/JiHh/yYe3/8mH+D/Jh/g/yYd3/8mK+X/JTbp/yU16P8lNen/Jifj/yYd + 3/8mHuCnJR/fACUf3wAlH98AJR/fACUf3wAlH98AJR/fACUf3wAlH98AJR/fACUf3wAlH98AJR/fACUf + 3wAmH+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AB4AAADMAAAA/wgLFP8bHHv/JiPa/ycf9f8lHvD/JR3r/yUd6P8lHej/JR3n/yUd5/8lHej/JR3n/yUd + 6P8lHef/JR3n/yUd5/8lHuj/JR3s/yce9f8mIt7/Ghtz/wQHBP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + ANEAAAAiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wAmIcAAJiHAACYhwAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0jwACNG7wAjSfEAI0jwACNI + 8AAjR/AAI0jwACNJ8AAjRe4AI0jwACNK8QAjSfAAI0fvACNH8AAjSPAAI0jwACNF7gAjSO8AJTjpACQ9 + 6wAkP+sAI0TuACNH7wAjRu4AJEbuACNJ8QAkPOoAJEHtACRE7gAjRu8AI0fvACNH7wAjR+8AI0fvACNH + 7wcjRu/AI0jv/yRA7f8kOur/I0fv/yNI8P8jR/D/I0fw/yNH8P8jR+//I0fv/yNH7/8jR/D/I0fw/yNH + 8P8jR/D/JEbv/yNG7/8kRu//I0fv/yNH8P8jR/D/I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH + 8P8jR+//I0fv/yNH7/8jR+//I0bv/yNH7/8jR/D/I0fv/yNH8P8jR+//I0fv/yNH7/8jRvD/I0fv/yNI + 8P8lKeP/Jhvf/yYo4/8lN+r/JjLn/yYg4P8mHt//Jh/g/yYd4P8mIuD/JTPo/yU26P8lNun/Ji3m/yYe + 4P8mHuDzJh7gMSYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJh/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + ABwAAADMAAEA/xIVRP8lIsb/Jx/1/yUd7f8lHej/JR3o/yUd5/8lHej/JR3o/yUe5/8lHej/JR3n/yUe + 6P8lHef/JR3n/yUd5/8lHuj/JR3n/yUd6P8lHej/Jh7w/ycf+v8gIKr/CAkk/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAAuAAAAAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJiHAACYhwAAmIcAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNI8AAjRu8AI0nxACNI + 8AAjSPAAI0fwACNI8AAjSfAAI0XuACNI8AAjSvEAI0nwACNH7wAjR/AAI0jwACNI8AAjRe4AI0jvACU4 + 6QAkPesAJD/rACNE7gAjR+8AI0buACRG7gAjSfEAJDzqACRB7QAkRO4AI0bvACNJ8AAjR+8AI0fvACNH + 7wAjR+8AI0bvMiNK8fMlNOj/Jhvf/yUq4/8kQe3/I0nw/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fw/yNH + 7/8jR+//I0fw/yNH8P8jRu//I0bv/yNH7/8jR+//I0fw/yNH7/8jRu//I0fv/yNH8P8jRu//I0bw/yNH + 7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fw/yNH7/8jRu//I0fv/yNH8P8jRu//I0bw/yNJ + 8P8kPer/Jh3f/yYf4P8lM+j/JTfq/yYu5v8mH+D/Jh/g/yYe4P8mIOH/JjDn/yU16f8lNej/JTTo/yYi + 4f8mHuD/Jh7gkiYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmH+AAJh/gACYf4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + ABMAAADABAUA/xkbdf8nIun/Jh3x/yUd6P8lHef/JR3n/yUd6P8lHej/JR3n/yUe6P8lHef/JR3o/yUe + 6P8lHuj/JR7o/yUd6P8lHun/JR3s/yUc8P8mHvT/Jh/p/yQc3v8jHs3/Jia7/x4jbP8GCAD/AQIA/wAA + AP8AAAD/AAAA/wAAAPUAAAAlAAAAAAAAAAAjR/AAI0fwACNH8AAjR+8AI0fvACRH7wAkR+8AJEfvACRH + 7wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ACYhwAAmIcAAJiHAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjSPAAI0bvACNJ + 8QAjSPAAI0jwACNH8AAjSPAAI0nwACNF7gAjSPAAI0rxACNJ8AAjR+8AI0fwACNI8AAjSPAAI0XuACNI + 7wAlOOkAJD3rACQ/6wAjRO4AI0fvACNG7gAkRu4AI0nxACQ86gAkQe0AJETuACNG7wAjSfAAI0nxACNI + 8AAjR+8AI0fvACNG7wAjSfFhJTvq/yYe3/8mHN//JiHg/yU36f8jSPD/I0fv/yNH7/8jR/D/I0fw/yNG + 7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNG7/8jR+//I0fw/yNG8P8jR/D/I0fw/yNG7/8jRvD/I0bv/yNH + 8P8jR+//I0fv/yNH8P8jR/D/I0fv/yNG8P8jR+//I0fw/yNH8P8jRu//I0fv/yNH8P8jR/D/I0bv/yNI + 8P8jR+//JSfi/yYd3/8lLub/JTbp/yU26f8mKeP/Jh3g/yYd4P8mIeH/JjHn/yU26f8lNej/JTbp/yYr + 5f8mHt//Jh/g4yYf3xsmH98AJh/fACYf3wAmH98AJh/fACYf3wAmH98AJh/fACYf3wAmH98AJh/fACYe + 3wAmHt8AJh/gACYf4AAmH+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAYAAACpBAcJ/x4fkf8nH/b/JR3s/yUe5/8lHej/JR7n/yUd6P8lHuj/JR7o/yUd6P8lHuj/JR3n/yUd + 5/8lHun/JR3s/yUc8P8mHu7/JB7f/yQhyv8kJav/HRyS/yIpg/8hJXr/GBZ6/yAhgf8jJJD/ISKT/x4d + gf8WHnH/EiBO/woOGv8CBAm6AAAAACVL/gAjR+8AI0fwACNH8AAjR/AAI0fvACNH7wAkR+8AJEfvACRH + 7wAkR+8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wAmIcAAJiHAACYh + wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0jwACNG + 7wAjSfEAI0jwACNI8AAjR/AAI0jwACNJ8AAjRe4AI0jwBCNK8SEjSfBPI0fvYSNH8F4jSPBFI0jwKyNF + 7hEjSO8IJTjpACQ96wAkP+sAI0TuACNH7wAjRu4AJEbuACNJ8QAkPOoAJEHtACRE7gAjRu8AI0nwACNJ + 8QAjSPAAI0jxACNH7wAjRu8AI0nxACRA7GEmIOD1Jh7g/yYe4P8mHeD/JS7l/yNF7v8jSfH/I0fw/yNI + 8v8jR/H/I0fw/yNH7/8jRu//I0fv/yNH7/8jR+//I0fv/yNH8P8jRu//I0fv/yNH7/8jRu//I0bv/yNH + 8P8jR+//I0bv/yNH7/8jR/D/I0fw/yNH8P8jR/D/I0fw/yNH8P8jR/D/I0fw/yNH7/8jRvD/I0bv/yNG + 7/8jSvD/JDbo/yYd3/8lLeX/JTbp/yU16P8lNOj/JiLg/yYd4P8mHeD/Jibi/yY26f8lNen/JTbp/yYs + 5f8mHuD/Jh7g/yYf4GMmH98AJh/fACYf3wAmH98AJh/fACYf3wAmH98AJh/fACYf3wAmH98AJh/fACYf + 4AAmHt8AJh7fACYf4AAmH+AAJh/gACYf4AAmHt8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAB/BAcK/x8gm/8mH/f/JR3q/yUd6P8lHuj/JR3o/yUd6P8lHef/JR3o/yUe5/8lHej/JR3q/yUd + 7f8mHfH/JR3h/yQhzP8kJKv/IiOV/yIniv8fH5D/JSil/yMfvf8lH83/JyHb/ycf5/8mHun/Jh7p/yce + 6v8nHuz/Jybv/yZF8P8jReD/IEDN1yFC4GUlS/4SI0fvACNH8AAjR/AAI0fwACNH7wAjR+8AJEfvACRH + 7wAkR+8AJEfvAP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJiHAACYh + wAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNI + 8AAjRu8AI0nxACNI8AAjSPAAI0fwACNI8BAjSfBMI0XujyQ968slNOjzJS/l/yUr4/8kKuP/Iyjj/iMq + 4/YjLOXtJDLn1yU46bokPeucJD/rfSNE7mIjR+9FI0buJiRG7g4jSfEFJDzqACRB7QAkRO4AI0bvACNJ + 8AAjSfEAI0jwACNI8QAjQ+4AI0XuACNJ8QAkQOwAJh3fZSYe3/8mH+D/Jh7f/yYc3/8mJ+L/JD/t/yNI + 7f8iP9v/I0Xq/yRJ9P8jSfX/I0jx/yNH8P8jRvD/I0fw/yNH7/8jR/D/I0bv/yNH7/8jR+//I0fv/yNH + 7/8jR/D/I0bv/yNH8P8jR/D/I0fw/yNH8P8jR/D/I0fw/yNG7/8jR/D/I0fv/yNG8P8jR+//I0bv/yNG + 8P8jSfH/Iz/s/yYh4P8mLeX/JTbp/yU16f8lNun/Ji7m/yYe3/8mHuD/Jh3g/yYj4v8lNuj/JTTo/yYn + 4/8mHuD/Jh7g/yYe4LcmHuACJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJh7fACYe3wAmH+AAJh/gACYf4AAmH+AAJh7fAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAABLBAYI+x8gnP8nIPb/JB3p/yUe6P8lHej/JR3n/yUd5/8lHef/JR3n/yUe6v8lHO7/JR3s/yQe + 2f8lJL7/IiGf/yMokv8jI5n/JSWz/yQeyv8mH9v/Jx/p/yYd5v8mHuT/Jh/j/yYf4v8mH+D/Jh7f/yUe + 3/8mH+D/Jh/g/yYc3/8lK+b/JEj0/yRJ9v8kSPP/I0fv2SNH72gjR/ACI0fwACNH8AAjR+8AI0fvACRH + 7wAkR+8AJEfvACRH7wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ACYh + wAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjSPAAI0bvACNJ8QAjSPADI0jwSCNH8J0kPuzjJTHm/yYl4f8mHuD/Ixfe/yAU3f8hGN3/JyDf/y0l + 4f8vJ+H/Lyfh/yUc3/8kGt//Jh/f/yYg4P8mIuH/JSjj/yUs5PYlMOfoJTbpyyQ86qwkQe2PJETuciNG + 71MjSfAzI0nxFiNI8AsjSPEBI0PuACNF7gAjSO8AI0rxACYd3wAmH+CDJh7g/yYf4P8mHt//Jh3g/yci + 5v8lNeb/GR6S/xYXgv8cKKn/IDrR/yNG7v8kSfb/I0n0/yNH8f8jRu//I0fv/yNH7/8jR+//I0fw/yNH + 7/8jR+//I0fv/yNH8P8jR/D/I0fw/yNH7/8jR/D/I0bv/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH + 7/8jSfD/I0Lt/yYm4/8lLuX/JTbp/yU16P8lNuj/JjTo/yYj4v8mHd//Jh/g/yYe4P8mH+D/JTDn/yYn + 4/8mHd//Jh7g/yYf4O4mHuApJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJh/gACYe3wAmHt8AJh/gACYf4AAmH+AAJh/gACYe3wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAbAwQA3R0dh/8oH/j/JR3o/yUe5/8kHef/JR7o/yUd6P8lHer/JRzu/yUe7P8kH9f/JCO3/yMm + nf8kJ5n/JCWq/yUgyf8mH9r/Jx/o/yYd5f8mHuL/Jh7h/yYf4P8mH+D/Jh7g/yYe4P8mHuD/Jh7g/yYf + 4P8mHt//Jh7f/yUf3/8mHuD/Jh7f/yQ96/8jSfD/I0bv/yNG7/8jR/D/I0fwuiNH8CkjR/AAI0fvACNH + 7wAkR+8AJEfvACRH7wAkR+8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wAmIcAAJiHAACYhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0jwACNG7wAjSfFQI0XvxiQ26f8lKOP/Jh7f/yYd3/8mHt//Jh/g/zc65P9UWun/bXjv/4GR + 8/+PnPb/laD4/5Sg9/9qdO7/Kifh/yMa3/8mHt//Jh7f/yYc3/8mHN//Jhzf/yYd3/8mH9//Jh/g/yUj + 4f8mKeP/JS7k+iUz5/AkOenXJD/suSND7pYjRe56I0jvWiNK8TgjSfEYJDbpCSYg4J0mHt/8Jxzi/yMb + zP8cFaH/JBzR/yQjz/8ZGo7/Ew9w/xUSef8YHY//Hi20/yI+2v8kSPL/JEn1/yNI8f8jR/D/I0fv/yNH + 7/8jR+//I0bv/yNH7/8jRu//I0bv/yNG7/8jR/D/I0fw/yNH7/8jR/D/I0fw/yNH8P8jR/D/I0fw/yNH + 8P8jSfH/I0Lt/yYk4f8mKOP/JTfp/yU16P8lNej/JTfp/yUo4/8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mHuBkJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJh/gACYf4AAmHt8AJh7fACYf4AAmH+AAJh/gACYf4AAmHt8AJh7fAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAohgbZf8oIPT/JRzp/yUe5/8lHej/JB7q/yYd8P8mHu//JB/Y/yQjtf8jJZ3/Iyab/yQl + sv8lIND/Jh/j/yYe6P8mH+P/Jh7h/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYc3/8lLuX/I0nw/yNH7/8jR/D/I0fw/yNH7/8jR+/iI0fwQiNH + 7wAjR+8AJEfvACRH7wAkR+8AJEfvAP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8AJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvASNI8EEjRu+yJDzq/yUm4v8mHN//Jh3f/yUe3/8mHt//Ixnf/zEz4v+Pn/b/orH6/3WA + 8P9YWuv/Y2/t/3aD8f+Ik/X/kaD2/3SE8P8wL+L/Ixvg/yYe4P8mHuD/Jh/g/yYf4P8mHuD/Jh/f/yYe + 4P8mHd//Jh3f/yYc3/8mHd//Jh7f/yYf4P8mIOD/JSbi/yUs5P8lMef7JDjp8iQ+69IjP+y4JDbp9CUy + 6v8hLcv/Fx+L/xcai/8cGpn/GBaE/xQQcv8UEHT/FA9y/xQPcf8VE3v/GiKa/x82yf8jRuz/JEr3/yNI + 8/8jR/H/I0fw/yNH8P8jR+//I0fw/yNG7/8jR+//I0fv/yNH7/8jR/D/I0fw/yNH7/8jR/D/I0fw/yNI + 8P8jSfD/JD3r/yYj4v8mG9//JS3k/yU26f8lNen/JTfp/yYs5f8mHt//Jh7g/yYf4P8mHuD/Jh7g/yYf + 4P8mHt//Jh/g/yYf4P8mH+CiJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7fACYe3wAmH+AAJh/gACYf4AAmH+AAJh7fACYe3wAAAAAAAAAAAAAA + AAAAAAAAAAAATg8TOv4nI+b/JR3t/yUe6v8mHu//Jh/0/yUg4P8gH63/HyGD/yImif8jI63/JSHS/yYf + 5f8mHeb/Jh7i/yYf4f8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/f/yYf4P8mH+D/Jh/g/yYf4P8mHeD/JiLh/yRD7v8jSO//I0fv/yNH7/8jR/D/I0fw/yNG + 7/MjR+9FI0fvACRH7wAkR+8AJEfvACRH7wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////ACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0jwACNI8AAjSPAAI0jwACNI + 8AAjSPAAI0jwACNI8E4kPev/JSXi/yYd3/8lHd//Jh7f/yYf4P8mH+D/Jh7f/yUd3/8mIeD/UVfp/1lk + 6/8mIeD/HBPe/yAa3/8kHd//KCHg/y8w4v86PeT/LCnh/yUd4P8mH+D/Jh7f/yYf3/8mH+D/Jh7g/yYf + 4P8mHt//JR7f/yYf4P8mH+D/Jh/g/yYf4P8mH9//JR7f/yYd4P8mHd//Jhzf/yYd3/8mHuD/JiDg/yUp + 4/8fJ+P/Hynn/yAx6P8gNeH/IDXR/x81yP8dMb7/Gymm/xgfkP8WGIP/FBJ3/xINbv8TDXD/GBqK/x4u + uP8iQOD/JEfw/yRK9v8kSfb/JEn2/yRJ9v8kSfX/I0n0/yNI8v8jR/D/I0fw/yNH8P8jR/H/I0jx/yNJ + 8v8kQ+7/JTHn/yYf4P8mHd//Jh3f/yYv5v8lN+n/JTbp/yUs5f8mHuD/Jh7f/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYe4P8mH+DPJh/gEyYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe3wAmHt8AJh/gACYf4AAmH+AAJh/gACYe3wAmHt8AAAAAAAAA + AAAAAAAAAAAADgYID9EkIsX/Jh35/yYe9P8mIOj/IR+6/xoccP8XG0r/HB50/yUhv/8mHuT/Jh7o/yYf + 4v8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHt//Jh/g/yYe4P8mHt//Jh/g/yYf + 4P8mHuD/Jh/f/yYe3/8mHt//Jh7g/yYf4P8mHt//Jh/g/yYd3/8kPOv/I0nx/yNG7/8jR+//I0bw/yNH + 8P8jRvD/I0fw6CNH7yokR+8AJEfvACRH7wAkR+8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wAmIcAAJiHAACYhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkQu4AJELuACRC + 7gAkQu4AJELuACRC7gAkQu4mJiDg2yYb3v8mH+D/Jh/g/yYf3/8mH9//Jh/g/yYf4P8mH+D/Jh7g/yAX + 3/8gGN//JR7g/yYf4P8mHt//JR3f/yQd3/8kG+D/Ixnf/yUd3/8mH+D/Jh/g/yYe3/8mHt//Jh/g/yYf + 4P8mHuD/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/x8V + 3v9ESeb/bnTu/1db6v9BReb/MS3k/ygo5f8gJOj/ISfq/yEt6v8hNej/Ijfd/yA0y/8dL7j/HCei/xcb + if8VE3n/FhV9/xkdkP8aJ6j/Hi+5/x8yv/8fMsD/HzLA/yA2yf8iQOD/JEjy/yRK9/8kSvT/I0bt/yNB + 5/8kM+T/JiLh/ycc4v8nHuX/Jx7j/yYg4v8mNOj/JTLn/yYn4/8mHuD/Jh7g/yYe4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+DpJh/gLyYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHt8AJh7fACYf4AAmH+AAJh/gACYf4AAmHt8AJh7fACYf + 4QAAAAAAAAAAAAECAXgdHZP/KCH9/yIfx/8bG4D/ExdH/xMXPP8bG3r/JCDG/ygf7P8mHub/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh7g/yYe4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mHN7/JTLn/yNJ8f8jR+//I0fv/yNG + 8P8jR+//I0fv/yNH7/8jR+/GJEfvCyRH7wAkR+8AJEfvAP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8AJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAkQu4AJELuACRC7gAkQu4AJELuACYc3yomH9+cJh/g8iYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYe + 4P8mH+D/Jh/g/yUf3/8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yQc + 3/84OuT/bXru/52q+f+aqPj/l6T4/46b9v99ivL/bHLu/1NZ6P8+QOT/MCvi/yUh4/8gHuf/ICbp/yEu + 6v8iNun/Ijja/x82yf8dLbD/GiOY/xcahv8WFHr/FA9x/xMOb/8UDnD/FRR8/xkim/8aJ6j/Giaj/xke + k/8YGIv/GROO/xsVlf8aFpn/HRmq/yMbx/8mIt//Jizq/yYg4/8mHd//Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYe3/8mHuD2Jh/gTCYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACNH + 7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7fACYe3wAmH+AAJh/gACYf4AAmH+AAJh7fACYe + 3wAmH+EAAAAAAAAAABsMDyXoICKV/xcaWv8VGkD/GRtr/yIfq/8nIeD/KB/u/yYe5v8mH+D/Jh7f/yYe + 4P8mHuD/Jh7g/yYf4P8mHuD/Jh/f/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mHt//Jh/g/yYf + 4P8mHuD/Jh7g/yYf4P8mH+D/Jh/f/yYe3/8mHuD/Jh/g/yYf4P8mH+D/Jhzf/yUr5P8jSPD/I0fv/yNH + 7/8jR+//I0fv/yNH8P8jR/D/I0fv/yRH8HojRu8AI0bvAP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////ACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACRC7gAkQu4AJELuACRC7gAmHN8AJh/fACYf4DEmH+CQJh/g6SYe4P8mH+D/Jh7g/yYe + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yUf3/8mH+D/Jh/g/yYf + 4P8mHuD/Jh7g/yYe4P8mH+D/Jh7f/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yUd + 4P8jHt//cIDw/56q+f+UoPf/lKD3/5Wh9/+Wovf/mqX4/5yo+f+Zp/j/mKT4/4yZ9f94hfH/aG/t/05V + 5/85OOP/LSni/yIg4/8fIej/ISvr/yE17P8iPOr/IjvY/x83yv8dLrL/GiSc/xcZh/8VEXX/Ewxs/xMM + bP8TDnD/ExBz/xQRdP8UEnX/ExJ0/xQSdP8UE3j/GRaM/yAZsv8lHtj/JyDm/ycf5P8mH+H/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/gaCYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAjRu8AI0nwACNI + 8AAjR+8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe3wAmHt8AJh/gACYf4AAmH+AAJh/gACYe + 3wAmHt8AJh/hACYg3gAICiGOFhlL/xsce/8iIKv/JiDe/ykh7/8mHur/Jh7h/yYe4P8mH+D/Jh/g/yYe + 3/8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7f/yYf4P8mHuD/Jh/g/yYc3/8lJ+L/I0bu/yNH + 8P8jR/D/I0fv/yNH7/8jR+//I0fw/yNH8P8jR/DrI0bvHSNG7wD///8A////AP///wz///8b////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wAmIcAAJiHAACYhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACRC7gAkQu4AJhzfACYf3wAmH+AAJh/gACYf4CQmHuB/Jh/f2iYe + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYe3/8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf + 4P8mH+D/Hxfe/2Rq7f+eq/n/lKD3/5Sg9/+UoPf/laD3/5Sg9/+UoPf/laD3/5Wh9/+Xo/j/m6b4/5yo + +P+ap/j/lqL3/4WV9P91f/D/XWPq/0RH5f80L+L/JyTh/x8f5P8hJ+v/ITHt/yM87/8jQOf/Ij3X/x84 + x/8dLK//GSGW/xcZh/8WE3r/FBBz/xQQdP8UEXX/FBJ3/xMRdf8TEXP/FxSE/x4arv8kHtL/Jh/j/ycg + 5v8mH+L/Jh/g/yYe4P8mH+D/Jh7geSYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACNJ8QAjSPAAI0bvACNJ + 8AAjSPAAI0fvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHt8AJh7fACYf4AAmH+AAJh/gACYf + 4AAmHt8OJh7fNCYf4XAmIN6uJiTP/Scg5v8oIOz/Jx7q/yYe4v8mH+D/Jh/g/yYf4P8lH9//Jh/f/yYf + 4P8mH9//Jh7g/yYe4P8mHuD/Jh7f/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf + 4P8mHt//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh7g/yYf3/8mHeD/JiTh/yNE + 7v8jSPD/I0fw/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fw/yNH74YUOu4A////AP///wD///9l////xf// + /wT///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8AJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJELuACYc3wAmH98AJh/gACYf4AAmH+AAJh7gACUe + 3xUmH+BpJh/gyCYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/yYe4P8mHt//Jh/f/yYf4P8mHt//Jh7f/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf + 4P8mHt//Jh/g/yAX3/9NTuj/k6L3/5uo+P+apvn/mKT4/5ei9/+VoPf/lKD3/5Sg9/+UoPf/lKD3/5Sg + 9/+VoPf/laD3/5ah9/+YpPj/m6f5/5uo+P+apvj/j531/36L8v9qce3/TlTn/zo44/8qJuD/IB/i/yAk + 6P8gLu3/Ijnw/yJA8P8jQ+b/IT7W/x80wf8cKan/GB6S/xcXgv8VEXj/ExB0/xQQcf8TEG//FRJ7/xoX + l/8hG7r/JR7Y/ycf5f8mH+H/Jh/gjiYe4AAmH+AAJh/gACYf4AAjSPAAI0fwACNJ8QAjSfEAI0jwACNG + 7wAjSfAAI0jwACNH7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7fACYe3wAmH+ANJh/gMCYf + 4GImH+CeJh7gziYf4PomH+D/Jh7h/yYe5v8mH+H/Jh7f/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf + 4P8mH9//Jh/f/yYf4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe + 3/8lHt//Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yUf3/8mHuD/Jh7g/yYf4P8mHuD/Jh7f/yYi + 4f8jQ+7/I0jw/yNH7/8jR/D/I0fw/yNG7/8jR+//I0fw/yNH7/8jR+/oFDruHf///wD///8G////w9LS + 08qpqasCqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrAKmp + qwCpqasAqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmHN8AJh/fACYf4AAmH+AAJh/gACYe + 4AAlHt8AJh/gACYe4AcmHuBUJh/gtiYf4PcmH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mHuD/Jh/g/yYe + 4P8mH+D/Jh7g/yYf4P8mHuD/Jh7f/yYe3/8mHuD/Jh/f/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh7f/yYe4P8mH+D/Ixzf/zU25P9UXur/c37w/4WT9P+Rn/b/m6j4/5qn+P+ap/j/mqb4/5ik + +P+Wovf/laD3/5Wg9/+UoPf/lKD3/5Sg9/+UoPf/laD3/5ei9/+apvj/m6j4/5uo+P+Uovf/gpD0/3F5 + 7/9YX+n/QUDk/zMu4v8nJuH/HyHk/x8q6v8gNe//ITzx/yJE8f8iQ+L/IDzS/x4yu/8aJaD/GBuN/xYV + ff8TD27/ExBu/xUSe/8eF6n7JhzeiiYf4AAmHuAAI0fvACNJ8AAjSPAAI0jwACNH8AAjSfEAI0nxACNI + 8AAjRu8AI0nwACNI8AAjR+8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AUJh/gNyYe32ImHt+fJh7gzCYf + 4PcmH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yUf + 4P8lH+D/Jh7f/yYe3/8mHuD/JR7g/yYf4P8mHuD/Jh/g/yYe4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/JR/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8lH9//JR7f/yUe4P8mH+D/Jh/g/yYe + 4P8lI+H/I0Tu/yNI8P8jR/D/I0fw/yNH7/8jR+//I0fv/yNH8P8jR/D/HkPv/zFS8U7///8A////lu3t + 7f8tLS6zqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrAKmp + qwCpqasAqamrAKmpqwCpqasAqamrAKmpqwCpqasAqamrAKmpqwAmIcAAJiHAACYhwAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAACQAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmH+AAJh/gACYf + 4AAmHuAAJR7fACYf4AAmHuAAJh7gACYf4AAmH+A8Jh/gnyYf4O0mH+D/Jh7g/yYe4P8mHuD/Jh/g/yYf + 4P8mHt//JR7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/f/yUf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh7f/yUe3/8hGd//Hhbe/yAZ3v8qKeH/Ojnk/0hL5/9YZOv/a3Xu/3yG + 8f+Ek/P/j532/5ml+P+bp/j/mqf4/5qn+P+Yo/j/laH3/5Wg9/+Un/f/lJ/3/5Sg9/+UoPf/laH3/5mk + +P+bp/j/mqj4/5ml+P+Nm/X/f4zy/3J37/9aYen/RUfm/zY04/8mKuL/HiXn/yEy7v8kQPP/JEf0/yRI + 7f8iQt3/HznL/xwrrf8XH5L/GiGcjiYc3gAjS/EAI0fvACNH7wAjSfAAI0jwACNI8AAjR/AAI0nxACNJ + 8QAjSPAAI0bvACNJ8AAjSPAAI0fvACYf4AAmH+AAJh/gACYf4AEmH+BxJh7g1SYe4PomHuD/Jh7g/yYe + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYe3/8mHt//Jh7g/yYe4P8mH+D/Jh7g/yYe4P8mH9//Jh/g/yYf + 4P8mHt//Jh/g/yYe4P8lHuD/Jh/g/yYe4P8mH+D/Jh7g/yYe4P8mH9//Jh/g/yYf4P8lHuD/Jh7g/yYf + 4P8mHuD/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf3/8mHt//Jh7g/yUf3/8lHt//Jh/g/yYf + 4P8mHd//JSXi/yNF7v8jSPD/I0fw/yNG8P8jR+//I0bw/yNH8P8jR+//I0fw/xM57/qOoPda////lf3+ + /v9hYWL/AAAAnwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYhwAAmIcAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAADgAAACFAAAAuAAAAMgAAADDAAAAogAAAFQAAAALAAAAAAAAAAAAAAAAAAAAAAAA + AAAmH+AAJh7gACUe3wAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8sJh7fiyYf4N8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yUe3/8mHuD/Jh/g/yYf3/8mHt//JR/g/yUe4P8lH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh7g/yYf4P8mH+D/JR/f/yUf4P8lHt//Jh7f/yYe4P8lHt//JBrg/yEY3/8fF97/IBje/yAY + 3/8iHd//Kivg/zc24/9DQ+b/UFno/2Vv7f95gvH/iJf0/5el9/+bp/n/m6f4/5ij+P+UoPf/lKD3/5Sg + 9/+Un/f/lJ/3/5Wg9/+VoPf/l6L4/5ql+P+cqPj/mqf4/5qm+P+Qnvb/f4rx/2Np6/86OeP/Hxbe/yMf + 4P8lK+X/JTbs/yU/8v8kRvT/JEr1/yNM9dkjTvO1I0vxgiNH70QjR+8TI0nwACNI8AAjSPAAI0fwACNJ + 8QAjSfEAI0jwACNG7wAjSfAAI0jwACNH7wAmH+AAJh/gACYf4AAmH+ACJh/gwyYe4P8mHuD/Jh7f/yYe + 4P8mH+D/Jh7f/yYf4P8mH+D/Jh7g/yYf4P8mHt//Jh/f/yYf4P8mHuD/Jh/g/yYe3/8mH+D/Jh/g/yUe + 3/8mH+D/Jh7g/yYf4P8lH+D/JR/g/yYe4P8lHt//Jh/g/yYf4P8mHuD/Jh/f/yYf3/8mH+D/JR7g/yUe + 3/8mH+D/Jh/g/yYe3/8mHuD/Jh/f/yYf4P8mHt//JR/g/yYf4P8mHt//JR/g/yUe4P8mH+D/JR/g/yYf + 4P8mH+D/Jhzf/yUp4/8jR/D/I0fv/yNH8P8jRvD/I0fw/yNH8P8jR/D/I0fw/x9C7/8gR/D2xtT90/// + //9+f4D/AAAA/wAAAJMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwAAmIcAAJiHAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAJQAAAKMBAQD3BQcL/wECAf8AAAD/AAAA/wAAAP8AAAD/AAAAxwAAAE4AAAAAAAAAAAAA + AAAAAAAAAAAAACYe4AAlHt8AJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYe3wAmH+AcJh/gbiYf + 4MwmH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH9//Jh/f/yUf3/8mHt//Jh7g/yYe4P8mH+D/Jh7f/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yUf3/8mH+D/Jh/g/yYf3/8lH9//Jh/g/yYf4P8mH+D/Jh/g/yYe + 3/8mHuD/JR3g/yQb3/8hGN//IBff/yAX3v8fF97/IRvf/y4t4f9AQeX/WGLr/3eB8f+Mm/X/mqf4/5uo + +P+apfj/lqL4/5Sg9/+VoPf/laD3/5Sf9/+UoPf/laD3/5Wg9/+VoPf/l6L4/5qm+P+cqfj/lKL3/2Js + 7f8tKuD/Ihbe/ycd3/8mHd//JiLh/yUo5P8lMef/JDnp/yRB7P8jR+7/I0nw1SNJ8KcjSPBlI0jwNSNH + 8AojSfEAI0nxACNI8AAjRu8AI0nwACNI8AAjR+8AI0zxACYf4AAmH+AAJh/gACYf4DkmH+DgJR7f/yUf + 3/8mH+H/Jh/i/yYf4f8mH+D/Jh/f/yYf4P8mH9//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe + 4P8mH+D/Jh/f/yYf4P8mHuD/Jh/f/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/JR/g/yYe + 3/8lH9//Jh/g/yYf4P8mH+D/Jh/f/yYe4P8mHt//Jh7f/yUf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYc3v8lMuf/I0nx/yNH8P8jR+//I0fv/yNH8P8jRu//I0fv/yFE7/8YQO//mK/5//// + //+hoaHhAAAA/AAAAP8AAACHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcAAJiHAACYh + wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAATgAAAOIICh7/ERJX/xUWdP8JCS//AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD7AAAAhgAA + AAYAAAAAAAAAAAAAAAAAAAAAJR7fACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmHt8AJh/gACYf + 4AAmH98NJh7gWSYf4LkmH+D6Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe + 3/8lH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/JR/f/yYe4P8mH+D/Jh/f/yYe + 4P8mH+D/Jh/g/yYf4P8mHt//Jh7g/yYe3/8mHuD/JR7g/yUd4P8jGt//IBfe/x8W3v8hHN//MTDj/0dM + 5/9lbuz/fovy/5Cf9v+ap/j/m6j4/5yo+P+apvj/mqX4/5qm+P+apfj/maT4/5mk+P+Xovj/lqH4/5ij + +P+dqfj/i5j1/zk75P8iGt//Jh7g/yYd3/8mHN//Jh3e/yYc3/8mIeD/JSbi/yUv5f8lOen/JEDs/yNG + 7/UjSfHLI0nxnyNJ8WAjSPA0I0bvCCNJ8AAjSPAAI0fvACNM8QAlPOkAJh/gACYf4AAmH+AAJh/gJyUf + 37YmH+D/Jh/h/yYf2v8mH9//Jx/n/yYf4/8mHt//Jh7f/yYe3/8mHuD/Jh7g/yYf3/8mH+D/Jh/g/yYf + 4P8mH+D/Jh7f/yYe3/8lHt//JR7f/yYe4P8mHuD/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh7g/yUe + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf3/8lH9//Jh7f/yYe4P8mH+D/Jh/f/yYf4P8mHt//Jh7f/yYf + 4P8mH+D/Jh/g/yYe3/8mHt//JD7s/yNI8P8jR/D/I0bv/yNG7/8jR+//I0fw/yJG7/8WO+7/fpn3//// + ///c3NveFhYW4QAAAP8AAAD/AAAAcgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJiHAACYh + wAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAWgMEBfQPEUD/FRZ6/xYThP8VFHz/Bgkd/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAACbAAAABwAAAAAAAAAAAAAAAAAAAAAAAAAAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh7fACYf + 4AAmH+AAJh/fACYe4AAmH+ACJh/gPiYe4KEmH+DuJh7g/yYe4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Ixnf/x4T + 3v8aEN3/GA7d/x8c3v8vLuL/QUfm/1dh6v9rce7/c3zw/3SD8P9zf/D/eYbx/4KQ8/+DkvP/ipn1/42b + 9f+Qnfb/nKf5/5Sj9/85PeT/Ihnf/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYc3/8mHN//Jhzf/yYg + 4P8lJeH/JS7l/yQ46f8kQO3/I0bv9CNI8MojSfCQI0jwSSNH7wUjTPEAJTzpACQ/7AAmH+AAJh/gACYf + 4AAmHuAFJyDjZh0ZpNoWFIH/GBWI/x8ar/8lHtX/JyDl/ycg5v8nH+X/Jh7i/yYe4f8mH+D/Jh/g/yYf + 3/8mHt//Jh/g/yYf4P8mHuD/Jh/g/yUe3/8mHt//Jh/f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHt//JR/f/yUf + 4P8mHuD/JR7f/yYf4P8mG9//JSzk/yNJ8P8jR/D/I0bv/yNH7/8jRu//I0bv/yNH7/8WPO7/W3z0//f7 + //////71UFBR1gAAAP0AAAD/AAAA/wAAAGYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYh + wAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAWQMGDfUSFFj/FhWC/xUTff8VE3n/FhR+/w8PU/8MECb/AgQA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAJAAAAAAAAAAAAAAAAAAAAAAAAAAACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYe + 3wAmH+AAJh/gACYf3wAmHuAAJh/gACYf4AAmHuAAJh7gKyYe4IYlHt/cJh7f/yUe3/8mH9//Jh/f/yYf + 4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh7g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yUf4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/y4u + 4v85OuT/QUDm/0hH5/9OVun/U13q/1pk6/9aY+v/WGPq/11r6/9XZ+r/SlXo/0BG5f8wNOL/JSPg/y0q + 4f80MuP/NjPj/z465f82M+P/JB3g/yYf4P8mHuD/Jh/g/yYf4P8lHt//Jh7g/yYf4P8mHuD/Jh7f/yYe + 3/8mHeD/Jhzf/yYc3v8mHN//JiDg/yYl4v8lLeX/JDjp/yRB7P8jR++4I0zxJiU86QAkP+wAI0nwACNH + 8AAmH+AAJh7gACcg4wAdFaEZHxmrgRoWk+IUEnL/FhOA/xsYnP8gG7r/Ix3O/yYf2P8mH+H/Jx/k/ycf + 5P8mH+P/Jx/k/ycf5f8nH+X/Jx/m/ycf5/8mH+f/Jx/l/ycf5f8nHuT/Jh/i/yYf4f8mH+D/Jh7g/yYf + 4P8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/f/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh7f/yYf3/8mHN//JiLh/yRD7f8jSPD/I0bv/yNG8P8jRu//I0fw/yRH7/8YPe//Qmby/+nv + /v/////+o6Ok5AAAAPUAAAD/AAAA/wAAAP8AAABfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAmIcAAJiHAACYhwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAOQUIE/ASFGT/FhSC/xUTe/8UEnj/FBJ1/yAbx/8oIPb/JyLb/yAfpf8RFT7/AgMA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAAYQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYf4AAmH+AAJh/gACYe + 3wAmHt8AJh/gACYf4AAmH98AJh7gACYf4AAmH+AAJh7gACYe4AAmHuAAJR7fGCYf4GomH+DIJh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH9//Jh7f/yYf + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Ixvf/yws + 4v+BkvP/lqX3/5Wj9/+ap/j/m6f4/5qn+P+apvj/mqb4/5qm+P+Zpfj/maT4/5ql+P+SoPf/g47z/2Ns + 7f8zNOP/GxLe/yEY3/8gGd//Ihnf/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 3/8mH9//Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHd//Jhzf/yYc3/8mIOD/JiXi/yU16N8lPOlAJD/sACNJ + 8AAjR/AAI0fwACNH8AAjSfEAHRWhAB8ZqwAnH+McIBiwmRQSdv8UEnT/FBJ1/xUUff8XFIX/FxWM/xgW + j/8bF5j/Gxia/x0Zof8dGaT/Hhqs/x8asf8fGrf/IBu//yIdxP8jHcn/JB7P/yUf1v8mH+D/JyDk/ycg + 5v8mH+H/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh7f/yYe3/8mH+D/Jh/g/yYf + 3/8mH+D/JR/f/yYf3/8mHuD/Jh7f/yM86/8jSfD/I0fv/yNH7/8jR/D/I0fw/yRH8P8dQO//LFLx/8/c + /f//////5ubn/R0dHvcAAAD/AAAA/wAAAP8AAAD/AAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJiHAACYhwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAHgcJF9sUFW3/FROB/xUTe/8UEnf/FBJ1/x0ZrP8lHuv/JR3q/yUd7f8mHvX/JyLq/x4f + i/8HCRH/AAAA/wAAAP8AAAD/AAAA/wAAAOYAAAAhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh/gACYf + 4AAmHt8AJh7fACYf4AAmH+AAJh/fACYe4AAmH+AAJh/gACYe4AAmHuAAJh7gACUe3wAmH+AAJh/gCSYe + 304mH+CwJh/g+yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yYe + 3/8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/JR/f/yUf3/8mHt//Jh/f/yYe3/8mHuD/Jh/g/yYe + 4P8iHd//TVPo/4+d9f+eq/n/l6P4/5Sg9/+VoPf/laD3/5Sg9/+UoPf/lKD3/5Wg9/+UoPf/lqH3/5ik + +P+cqfn/kJ72/1BY6f8eGN//JR3f/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/f/yYf3/8mH+D/Jh7f/yYd3/8mHd//JiPh6SQ/ + 7HEjSfBhI0fwTiNH8EojR/A4I0nxMSNK9CMjR+4VJD/vCSQn0wMXE399FBF0/BQQdf8UEHP/FA9w/xMP + b/8SD27/Eg5t/xMPbf8TEG7/ExBv/xIQcP8TEHH/ExFy/xQSc/8VE3f/FRN7/xYUgP8XFIT/FxWJ/xsY + mf8gG7v/Jh/h/yYf4/8mH+D/Jh7g/yYf4P8mH+D/Jh7f/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHt//Jh7g/yYf4P8lHt//Jh3f/yU46P8jSfD/I0fw/yNG7/8jR+//I0fw/yNH8P8gRO//G0Pv/7HF + +////////////25ub/8AAAD/AAAA/wAAAP8AAAD/AAAA/AAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACYhwAAmIcAAJiHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAABAYIE7ITFGz/FRKA/xUTe/8UEnX/FhR+/yAavf8mHuz/JR7p/yUe6P8lHuj/JR7n/yUd + 7P8nH/b/IyK3/wsOIf8AAAD/AAAA/wAAAP8AAAD/AAAAhgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAmH+AAJh7fACYe3wAmH+AAJh/gACYf3wAmHuAAJh/gACYf4AAmHuAAJh7gACYe4AAlHt8AJh/gACYf + 4AAmHt8AJh/gACYe4DcmHuCXJh/g6iYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/JB3g/yUd + 4P8lHuD/JR7g/yYe4P8mHuD/Jh7g/yYe4P8mHuD/Jh7g/yYe3/8mH9//Jh/g/yUf3/8lHt//Jh/g/yYe + 3/8mHuD/JR3g/x4V3v8uLOP/Y23t/5Cd9f+bp/j/lqH3/5Sg9/+UoPf/laD3/5Wg9/+VoPf/lKD3/5Wg + 9/+UoPf/lJ/3/5ij+P+Zpvj/U1vp/x4X3v8lHt//Jh/g/yYf4P8mH9//Jh/g/yYe4P8mHuD/Jh7g/yYe + 4P8mHuD/Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYb + 3/8lLOX/I0nw/yNH8P8jR/D+I0bw9CNG7/AjR/DoI0jw4CNJ8dwjSPDTIT/ezR80w/gfMbz/HzG+/x4t + tf8cLLH/HCqt/xkmpP8aJaT/GiGZ/xofl/8ZG4//GBmK/xcWg/8WE37/FRF3/xURdv8UEHP/ExBy/xQS + dv8UE3X/FBN0/xsXmf8mH9v/Jx/l/yYf4f8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf + 4P8mH+D/Jh/g/yUe3/8mHuD/Jh3f/yQz5/8jSfH/I0fw/yNH7/8jRu//I0bv/yNH7/8jRu//GD3u/42m + +P///////////9TU1f8JCQn/AAAA/wAAAP8AAAD/AAAA/wAAAPAAAAAyAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAmIcAAJiHAACYhwAAmIcAAJiDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAQEAAAMFCHcPEF3/GBiE/xgZf/8UEnP/FxWI/yIc0P8mHu//JR7p/yUd6P8lHef/JR7n/yUd + 5/8lHuf/JR3o/yYe9v8kIsf/DA8h/wAAAP8AAAD/AAAA/wAAAOAAAAAaAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAI0bvACNH7wAjRu8AI0fvACNG8AAmH98AJh7gACYf4AAmH+AAJh7gACYe4AAmHuAAJR7fACYf + 4AAmH+AAJh7fACYf4AAmHuAAJh7gACYe4CMmHuB7Jh/g1iYf4P8mH+D/Jh7g/yYf4P8mH+D/JyDg/ycf + 4P8lHuD/Ixzg/yAb4f8lH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf + 3/8mH+D/Ihvh/yIc4P8mHuD/Kize/yEf3v8zM+L/aHTt/5ak+P+Wovf/lKD3/5Sg9/+Un/f/lJ/3/5Wg + 9/+UoPf/lKD3/5Wg9/+UoPf/l6P4/5ak9/9ESuf/HhXe/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf + 3/8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mHt//Jh/g/yYf4P8mH+D/Jh/g/yUf + 3/8mHN//JSXh/yNH7/8jR/D/I0fv/yNH7/8jR/D/I0fw/yNH8P8jRu//I0fv/yRJ9P8kSvX/JEr2/yRJ + 9f8kSvb/JEn1/yRJ9v8kSvf/JEr2/yRH8P8kRu//I0Tq/yND6P8iQeT/Ij/g/yE62f8hNcz/Hy69/xoh + m/8VEnf/FBF2/xUTev8UEnT/GBaN/yQdzf8nIOX/Jx/m/ycg5f8nIOb/Jx/n/ycf5v8mH+T/Jh/h/yYe + 4P8mHuD/Jh/g/yYf4P8mHt//Jhve/yUw5v8jSfD/I0fv/yNH7/8jR+//I0fv/yNH7/8kR+//GD3v/1R2 + 9P/7/v////////////9jY2T/AAAA/wAAAP8AAAD/AAAA/wAAAP8aGhrngoOEI4KDhACCg4QAgoOEAIKD + hACCg4QAgoOEAIKDhACCg4QAgoOEAIKDhACCg4QAgoOEAIKDhACCg4QAgoOEAIKDhACCg4QAgoOEAIKD + hACCg4QAgoOEAIKDhACCg4QAJiHAACYhwAAmIcAAJiHAACYgwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAEBAEALDEP0ISGN/zg/nv8XF3f/GRWR/yQd3f8lHu7/JR3o/yUd5/8lHuj/JR7o/yUd + 5/8lHej/JR3n/yUd6P8lHef/Jx72/yMjtf8GCRD/AAAA/wAAAP8AAAD/AAAASwAAAAAAAAAAAAAAAAAA + AAAAAAAAI0bvACNG7wAjR+8AI0bvACNH7wAjRvAAI0fwACNH7wAjR+8AI0fwACNG7wAjR+8AI0fvACNH + 7wAjRu8AJh/gACYe3wAjR+8AJh7gACYe4AAmHuAAJh7gACYf4AsmG99XJhresCYc3+8mHd//Ihjh/z9E + 2/+tr8j/p6nK/5iezP+TnM7/TlrZ/yEZ4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7f/yYe + 3/8lHt//Ihvh/3F61P+co8v/rq3I/7i+xv9VXNj/GxHg/yAZ3/9RWun/l6T3/5ei9/+UoPf/lKD3/5Sg + 9/+VoPf/lKD3/5Sg9/+VoPf/lKD3/5Sg9/+ZpPj/jJr1/zg65P8eFd7/Jh7f/yYf3/8mHuD/Jh/g/yYf + 4P8mHt//Jh7g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh7f/yYf4P8mHuD/Jh/g/yYf + 4P8mHd//JiDg/yQ76v8jSPD/I0fv/yNH7/8jR+//I0bv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH + 8P8jR+//I0bv/yNH8P8jR+//I0fv/yNH7/8jR/H/I0fw/yNH8f8jSPL/I0jz/yRI8/8kSfP/I0n1/yRK + 9v8kSfP/HzjL/xgai/8VEHX/FBN5/xMSdP8WFHz/Gxic/x4Zq/8eGan/Hhms/x8Ztv8hG7//JB3N/yUf + 3f8nH+T/Jx/l/yYf4f8mHt//Jh3f/yUu5v8kR/D/JEfw/yNG7/8jR/D/I0bv/yNH8P8jR+//Ikbv/xtA + 7v+6yvv////////////d3d7/DAwM/wAAAP8AAAD/AAAA/wAAAP8AAAD/R0dJ3uHh4RPh4eEA4eHhAOHh + 4QDh4eEA4eHhAOHh4QDh4eEA4eHhAOHh4QDh4eEA4eHhAOHh4QDh4eEA4eHhAOHh4QDh4eEA4eHhAOHh + 4QDh4eEA4eHhAOHh4QDh4eEA4eHhACYhwAAmIcAAJiHAACYhwAAmIMAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAABcFBh7WHh6C/15lwP8iI4L/FxOY/yUe4/8lHuz/JR7n/yUe6P8lHef/JR3o/yUe + 6P8lHuj/JR3o/yUd5/8lHef/JR3n/yUd6P8nIPj/Ghx6/wAAAP8AAAD/AAAA/wAAAHAAAAAAAAAAAAAA + AAAAAAAAI0bvACNG7wAjRu8AI0fvACNG7wAjR+8AI0bwACNH8AAjR+8AI0fvACNH8AAjRu8AI0fvACNH + 7wAjR+8AI0bvACNH8AAjR/AAI0fvACNG7wAjR+8AI0jwACNI8AAkPOsKJTPnISUv5l0kNejCJS3l/yMc + 4P8xLdz/vb7F/8zMw//Pz8L/qrDJ/zIy3v8iGuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 3/8mH9//JR7g/x4X4f+Kk8//09LB/8nJw//S0sH/fH7R/xwW4v8kHOD/Hxnf/1BZ6f+XpPf/mKP3/5Sg + 9/+UoPf/lKD3/5Sg9/+UoPf/lJ/3/5Sg9/+UoPf/lJ/3/5ql+P+Jl/T/PD/k/x0W3v8gF97/Ixrf/yQc + 3/8lHeD/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mHt//Jh/f/yYf4P8lHt//Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mHN//JSPh/yM+7P8jSfD/I0bv/yNH8P8jR/D/I0bv/yNH7/8jR/D/I0bv/yNH7/8jR/D/I0fv/yNH + 7/8jR+//I0fw/yNG7/8jR/D/I0fv/yNH7/8jR/D/I0fw/yNH7/8jRu//I0fw/yNH7/8kRu//JEfv/yNH + 7/8jR+//I0fw/yRM+/8kRev/Gx+Y/xcUgf8ZFo3/FxWC/xQSdv8UEnX/FBJ1/xQSdf8UEnP/FRN3/xYU + fv8XFYj/HBie/yIcv/8mHuD/Jh7j/yUz6P8jSPD/I0jw/yNG8P8jRu//I0fw/yNH7/8jR/D/I0fv/xw/ + 7v9CZfL/9fn/////////////eXl6/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/2BgYdY4ODkJODg5ADg4 + OQA4ODkAODg5ADg4OQA4ODkAODg5ADg4OQA4ODkAODg5ADg4OQA4ODkAODg5ADg4OQA4ODkAODg5ADg4 + OQA4ODkAODg5ADg4OQA4ODkAODg5ADg4OQAmIcAAJiHAACYhwAAmIcAAJiDAACYgwAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAABAgSXEBJg/2xxz/88P5n/ExCQ/yUe5f8lHuz/JR3n/yUd5/8lHej/JR3o/yUd + 6P8lHej/JR7o/yUe6P8lHej/JR3o/yUd6P8lHej/JR3v/yYi2f8JCyD/AAAA/wAAAP8AAACMAAAAAAAA + AAAAAAAAAAAAACNG7wAjRu8AI0bvACNH7wAjRu8AI0fvACNG8AAjR/AAI0fvACNH7wAjR/AAI0bvACNH + 7wAjR+8AI0fvACNG7wAjR/AAI0fwACNH7wsjRu8xI0fvYyNI8IsjSPC5I0jw5CNK8fQjSfH/I0nw/yNI + 8P8iPu3/JTPm/56lzP/KycP/zMvD/4GJ0P8aFeL/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8bFOL/X2PW/83Mwv/ExMT/zcvC/5Wezf8jIeH/JR3g/yUd4P8gGd7/UFnp/5Si + 9/+cp/j/mKP4/5ik+P+YpPj/maX4/5ml+P+Xo/j/lqH3/5Wg9/+UoPf/maT4/5Cd9v9kbO3/Q0bm/zQ3 + 4/8tK+H/JyHg/yEc3/8hGN//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYe4P8mH+D/Jh/g/yYe + 4P8mG9//JSrj/yND7f8jSfD/I0fv/yNH7/8jR/D/I0fw/yNG7/8jR/D/I0fw/yNH7/8jR/D/I0bw/yNH + 7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNH8P8jRvD/JEbw/yNH + 8P8jR/D/I0nz/yRK9f8iQN//HjC7/xoflf8WFID/FhR//xURev8UEHT/FA9y/xQPcv8UEHP/Ew90/xQQ + df8UEnb/FBJ3/xQSdf8UEnT/Hhqt/yU04f8jR+7/JEn0/yRK9v8jSPP/I0fw/yNH7/8jRu//I0fv/yNH + 7/8VOu7/epT2////////////7O3t/xwcHP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP+oqKnS7+/vB+/v + 7wDv7+8A7+/vAO/v7wDv7+8A7+/vAO/v7wDv7+8A7+/vAO/v7wDv7+8A7+/vAO/v7wDv7+8A7+/vAO/v + 7wDv7+8A7+/vAO/v7wDv7+8A7+/vAO/v7wDv7+8AJiHAACYhwAAmIcAAJiHAACYgwAAmIMAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAABLAwUv/F1hwv9tcsX/EhCH/yMc3v8lHuv/JR3n/yUd5/8lHen/JR3o/yUe + 6P8lHej/JR3n/yUd5/8lHej/JR3n/yUd6P8lHej/JR3o/yUd6P8nH/b/GBpt/wAAAP8AAAD/AAAAmAAA + AAAAAAAAAAAAACNH8AAjRu8AI0bvACNG7wAjR+8AI0bvACNH7wAjRvAAI0fwACNH7wAjR+8AI0fwACNG + 7wAjR+8AI0fvCCNH7yMjRu9YI0fwhCNH8LQjR+/iI0fv9yNG7/8jR+//I0fv/yNH8P8jR/D/I0bw/yNG + 7/8jR/D/Ikfw/xxE8v+EltX/zsvB/8fHxP+4vMb/ODrc/yEX4f8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yUe + 3/8mHuD/Jh/g/yUf3/8mH+D/Hxbg/0FD3P+9w8X/x8bD/8fHw/+1ucb/MS3e/yIb4f8mH+D/Jh3f/yEZ + 3/9AQ+X/cX3w/4GP8/+BjvL/gY7y/3aE8f92hfD/g5Dz/5Gd9v+Ypfj/mqX4/5Wg9/+Woff/mqb4/5Wj + 9/+OnPb/jJj1/4OP8/9seO//QUXm/yId3/8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe + 4P8mHd//JTDm/yNG7/8jSPD/I0fw/yNH7/8jRu//I0fv/yNH8P8jRu//I0bv/yNH8P8jR/D/I0fw/yNG + 7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNG7/8jR+//I0fw/yNH8P8jR/D/I0fv/yNH8P8jR/D/I0fw/yNG + 8P8jR/H/JEr2/yI+3P8bJ6X/FhV//xMOcf8TD3H/FBF2/xcXgf8aIJb/HCem/xwssf8bLLP/Gymq/xwk + n/8ZH5b/FxmH/xUTev8UEHT/FBF2/xUTev8WGIL/GR2P/xsnpf8eMb//Ij/b/yNH8P8kSvb/I0jz/yNH + 8P8iRu//GT/v/6zA+////////////52dnf8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8ODg7/0dHS0v// + /wf///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////ACYhwAAmIcAAJiHAACYhwAAmIMAAJiDAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAALAAAHyTA0if+WnPD/KyyR/x0Wz/8mHu7/JR7o/yUe6/8lHuv/JR3m/yYd + 6v8lHer/JR3o/yUd5/8lHef/JR3o/yUd6P8lHuj/JR7n/yUd5/8lHef/Jh70/yAesv8DBAL/AAAA/wAA + AKgAAAAAAAAAACNG7wAjR/AAI0bvACNG7wAjRu8AI0fvACNG7wAjR+8AI0bwACNH8AAjR+8AI0fvECNH + 8D8jRu9xI0fvoCNH79UjR+/1I0fv/yNH7/8jRu//I0fw/yNH8P8jR+//I0fw/yNG7/8jR+//I0fv/yNG + 7/8jR/D/I0bv/yNG7/8ZP/L/a4Dc/8zLwv/ExMT/zczD/32E0v8bF+L/Jhzf/yYe4P8mH+D/Jh/g/yYf + 4P8lHt//Jh7g/yYe4P8lH9//Jh/g/yQb3/8nJuD/rK/I/8nJw//FxcT/xsfE/0dN2/8gGOH/Jh7f/yYf + 4P8mHuD/Ihnf/yMd3/8pJOH/KSTg/ykk3/8mIOD/JiHg/yoj4f80NOP/TFLo/3R/8f+Wo/f/mKT4/5Sg + 9/+VoPf/lqL3/5ei9/+Yo/j/nKf4/5Sk9/9FSeb/Ihnf/yYe4P8mH+D/Jh7f/yYf4P8mHuD/Jh/g/yYd + 4P8mIOD/JDjo/yNJ8P8jR/D/I0bw/yNG8P8jRu//I0bv/yNH7/8jRu//I0fv/yNH7/8jR/D/I0fw/yNG + 7/8jRu//I0fw/yNG7/8jR/D/I0fw/yNH8P8jRu//I0bv/yNH8P8jR/D/I0fw/yNH7/8jRvD/I0fv/yNH + 7/8kSfX/I0bs/x0ss/8WFX7/Ew5x/xQPcf8WFoH/GyWj/x82x/8iQuT/I0bv/yRI8/8kSvf/JEr4/yRJ + 9f8kSPH/I0bv/yJE6f8hO9P/HCuv/xgcjf8VEnn/FA9y/xMPc/8TD3P/FBB1/xUUff8ZHpP/HS+5/yJB + 4P8kSfT/IUb0/yhN8f/e5v3///////7///9BQUL/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/LCws//n5 + +dP///8H////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wAmIcAAJiHAACYhwAAmIcAAJiDAACYg + wAAmIMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAagkMQP+AhOH/Zmu9/xYRq/8mHe7/JR3n/yYd7P8jHdr/HBmm/xoW + kv8cGKH/Ix3Z/yYe6/8kHef/JB3n/yUe6P8lHef/JR7o/yUe6P8lHef/JR3n/yUd7/8mI9H/Cg8Z/wAA + AP8AAACsAAAAACZM/QAjRu8AI0fwACNG7wAjRu8AI0bvACNH7wAjRu8EI0fvGyNG8E4jR/CDI0fvtiNG + 7+cjR/D+I0fv/yNG7/8jRu//I0fw/yNG7/8jR+//I0bv/yNH8P8jR/D/I0fw/yNG7/8jRu//I0fv/yNH + 8P8jRu//I0fv/yNH8P8jR+//HEHx/09v5P/Fx8T/xcTD/8jGw/+7wMf/OFLn/x8p5/8mIeD/Jh3f/yYe + 4P8mHt//Jh/g/yYf4P8lHt//Jh7g/yYf4P8mHuD/Hhfh/4ePz//My8L/xMTE/8vNw/9pbNT/HRTh/yYe + 4P8mH+D/Jh/g/yYe4P8lHd//JB3f/yUd4P8lHeD/JR3g/yUd4P8kHeD/Ixrf/x8X3v8hHN//Qkfm/4iV + 9P+ZpPj/lKD3/5Sg9/+UoPf/lJ/3/5Sg9/+erPn/X2Xs/x8W3/8mHuD/Jh/g/yYe3/8lHt//Jh7g/yYc + 3/8mJuL/JD/s/yNJ8P8jR/D/I0fv/yNH8P8jRu//I0fv/yNH7/8jR/D/I0bv/yNH7/8jR+//I0fw/yNG + 7/8jRu//I0fv/yNH8P8jRu//I0fv/yNH8P8jR+//I0bw/yNH7/8jRu//I0fv/yNH7/8jRu//I0bv/yNH + 8P8kSvb/IT3Y/xkekf8TD3D/FBFz/xcZif8eLrf/IkHg/yRJ9P8kSvb/I0jy/yNH8P8jR/D/I0fw/yNH + 7/8jR/D/I0fw/yRH8P8kSPH/I0n0/yRJ9f8jRez/ITvV/x0ttf8YHpH/FhR8/xQPc/8UEHT/FBB1/xQQ + dP8WFH//GyWj/xg01f9JbfT/9fn+///////T09T/CQkK/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/01O + T//////B////A////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJiHAACYhwAAmIcAAJiHAACYg + wAAmIMAAJiDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAEQAACdU4PJP/lZrt/yQkj/8fGNn/Jh7r/yUd6/8kHd7/GBaL/xMS + cv8UEnX/ExJx/xkWlP8kHeb/JR7o/yUd5/8lHuj/JR7o/yUd5/8lHef/JR3o/yUd6P8lHer/JyHq/wsL + Of8AAAD/AAAAnwAAAAAmTP0AI0bvACNH8AAjRu8FI0bvIyNG71UjR++OI0bvxCNH7/IjRvD/I0fv/yNH + 7/8jRu//I0bv/yNH8P8jRvD/I0fv/yNH8P8jRvD/I0fv/yRH8P8jRu//I0fv/yNH8P8jR+//I0fv/yNH + 7/8jR+//I0fw/yNH8P8jR/D/I0bv/x9E8P81V+r/vr/F/8bGw//ExMT/zcvB/36T2P8YQfL/I0Lt/yUz + 6P8mJOL/JRzf/yYd3v8mHt//Jh/g/yYf4P8mHuD/Jh/g/xoU4f9jZdX/zc3C/8TExP/OzcL/iI/P/x4b + 4f8lHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8lH9//JR3f/xwT + 3f89QeT/k6D3/5ei+P+Un/f/lKD3/5Wg9/+apvj/jZv2/zc45P8jG9//Jh7f/yYf4P8mH+D/Jh3f/yYd + 3/8lLeX/I0Xu/yNJ8P8jR/D/I0bw/yNH7/8jRu//JEbw/yNH8P8jR+//I0fw/yNH7/8jRu//I0bv/yNH + 7/8jRu//I0fw/yNH8P8jR+//I0fw/yNH7/8jR/D/I0fw/yNG7/8jR+//I0bv/yNH8P8jR/D/I0bv/yNH + 8P8kSvb/IDXI/xYVf/8TD2//FxOB/x8htf8jROj/JEr3/yNI8/8jR/D/I0fv/yNH8P8jRu//I0bv/yNH + 7/8jR+//I0fw/yNH7/8jR+//I0fv/yNG8P8jR/D/I0jx/yNJ9P8kSfb/JEfv/yE92P8dLbX/GR6T/xYU + ff8UEHX/FBF2/xQQdP8GBXX/bHO/////////////oqKi/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP+Ghob/////t////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ACYhwAAmIcAAJiHAACYh + wAAmIMAAJiDAACYgwAAlIMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF8FBzf/cHXU/1tftP8QDZT/Jh7s/yUd6P8mHu3/Gxik/xQT + c/8VE3v/FBN6/xQSef8UEnf/IRvH/yYe7P8lHef/JR3n/yUd5/8lHef/JR7o/yUe5/8lHej/JR3o/ycf + 9f8RD07/AAAA/wEDCYInTf8AJkz9HiNG71MjR/CNI0fvxCNH7/UjRu//I0fv/yNH8P8jR+//I0fw/yNH + 8P8jR/D/I0fv/yNG7/8jR/D/I0fw/yNH8P8jR+//I0fw/yNH7/8kRvD/I0bv/yNH7/8jR+//I0fv/yNH + 7/8jR/D/I0fv/yNH7/8jRvD/I0fw/yNG7/8hRO//KEvu/6myy//Jx8L/xMTE/8nIw/+5vcf/Mlbr/x5E + 8f8jSfD/JEXu/yQ36f8lJ+P/Jh7f/yYc3/8mHt//Jh/g/yYf4P8eFeH/RUfb/8DFxf/GxcT/ysnD/6Wt + yv8qJd//JBzg/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH9//Jh/g/yYe4P8mH9//JR7f/yYe + 4P8mHuD/HhXe/1Ze6v+bqPj/mKT4/5mk+P+cqPj/hZPz/z9D5f8hGd//Jh/g/yYf4P8mHt//JR3e/yYf + 4P8lNuj/I0jw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fw/yNH8P8jR/D/I0fv/yNH7/8jRu//I0bv/yNH + 7/8jR+//I0fv/yNH8P8jR/D/I0fw/yNH8P8jR+//I0bv/yNH8P8jR+//I0fw/yNH8P8jR+//I0fv/yNH + 8P8jSff/HjPB/xURd/8TEHD/GheT/yUd0v8nH+f/JTPp/yNI8P8jR/D/I0bv/yNH7/8jR/D/I0bv/yNH + 8P8jR/D/I0bv/yNH8P8jR+//I0fw/yNH7/8jRu//I0fv/yNH8P8jR/D/I0fw/yNH8f8jSPT/JEr2/yNH + 7/8iPtv/Hi+4/xcZhv8UEHT/BQNv/36Etv///////////3x9fv8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8BAQH/srKy/////7H///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wAmIcAAJiHAACYh + wAAmIcAAJiDAACYgwAAmIMAAJSDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIBAga5Ghxv/3+E3/8nKIX/GRSs/yYe7/8mHuz/IhzU/xUT + fP8VE3n/EhB5/xQSev8VE3r/FBJ2/yAavv8mHu7/JR3n/yUd5/8lHej/JR7n/yUe6P8lHuf/JR3o/yUd + 6P8nH/X/FhVQ/wIDAP0YK3+mJ03/pyNH7+8jR+//I0fw/yNH7/8jR/D/I0bv/yNH7/8jR/D/I0fv/yNH + 7/8jR+//I0fw/yNH8P8jR/D/I0bv/yNH7/8jR/D/I0fv/yNH7/8jR+//I0bw/yNH7/8jR+//I0bv/yNH + 7/8jRu//I0bv/yNH7/8jR+//I0fv/yNG7/8jR+//IkXw/x9G8f+MndP/z8vB/6u1y/+8v8f/zszB/3aL + 2f8YPvL/I0fv/yNH8P8jSfD/I0fv/yQ96/8lLOX/Jh/g/yYc3/8mHt//JBrg/ykp4P+ussj/ycjD/8XF + xP/BwsT/Ojvc/yIZ4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/f/yYf4P8mHuD/Jh/g/yYf + 3/8mH9//Jh/g/yQc3/8hHN//WGHq/4iS9P9+ivL/WmPr/ywr4f8gGN//Jh7g/yYe4P8mHuD/Jhzf/yUm + 4v8kP+z/I0nw/yNH7/8jRvD/I0fw/yNG7/8jR+//I0fw/yNH8P8jRu//I0bv/yNH7/8jR+//I0bv/yNH + 7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH8P8jR+//I0fw/yNG7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH + 8P8kSvb/HjPB/xURdv8UEXL/Gxif/yYf3/8nH+X/Jh7f/yYc3/8lNef/I0jw/yNH8P8jR+//I0fv/yNH + 7/8jR/D/I0fw/yNH8P8jR+//I0bv/yNH8P8jR+//I0bv/yNG7/8jR/D/I0fv/yNH8P8jR+//I0fw/yNH + 7/8kR/H/I0j0/yRL+P8jQ+b/HCqt/wcGc/98gbP//////+Dl9v8mKln/AAAC/wAAAP8AAAD/AAAA/wAA + AP8AAAD/FRUW/+Dg4f////+U////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AJiHAACYh + wAAmIcAAJiHAACYgwAAmIMAAJiDAACUgwAAmIcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1AwUh9jQ1mv9lasX/Dw1x/yAavv8mHu7/Jh7t/xwY + ov8TEnP/FhR7/y41jf8aG3//FBJ5/xUTfP8iHNH/Jh7s/yUe6P8lHuf/JR3n/yUd5/8lHef/JR7o/yUd + 6P8lHOj/Jhzt/yErmf8fO6n+Jkr2/yNH8f8jR+//I0fv/yNG7/8jR+//I0fw/yNH8P8jRu//I0fv/yNH + 7/8jR/D/I0fv/yNH7/8jRvD/I0bv/yNH7/8jRu//I0fv/yNH8P8jR/D/I0fv/yNH8P8jR+//I0fv/yNH + 7/8jR+//I0fv/yNG7/8jR/D/I0fv/yNH7/8jRvD/I0fw/yNH8P8ZQPL/dYnZ/9LOwP+lsM3/kaLS/9HN + wf+3vMf/MVXq/x5C8f8jR/D/I0fw/yNH7/8jSPH/I0jw/yRA7P8lMOX/JiLh/yUc3/8fF+H/jJPP/8vL + wv/FxcT/x8rE/1Vc2P8fFuH/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh7f/yYf3/8mH+D/JR3f/yEa3v8mH+D/JB7f/yAY3/8jGt//Jh7g/yYd3/8mHuD/JiTh/yU1 + 6P8jRe//I0nw/yNH8P8jR+//I0bv/yNH8P8jR/D/I0bv/yNG7/8jRu//I0bv/yNH8P8jR+//I0bv/yNG + 7/8jR+//I0fv/yNH7/8jRu//I0fw/yNH8P8jR/D/I0fw/yNH8P8jR/D/I0fw/yNH7/8jR/D/I0fv/yNH + 7/8kSfb/IDfL/xUSd/8TEHD/HBmh/ycg5P8mH+P/Jh7g/yYf4P8mHuD/Jh3f/yQ46f8jSPD/I0fw/yNH + 7/8jR/D/I0fv/yNH8P8jR+//I0fv/yNH7/8jR+//I0bv/yNH7/8jR+//I0fw/yNH8P8jRu//I0fv/yNH + 7/8jR+//I0fw/yNG7/8jR+//I0j0/yRK+P8QKsr/e4DJ/9vc2/82O1H/AAAK/wAAAf8AAAD/AAAA/wAA + AP8AAAD/AAAA/z09Pv/+/v7/////dv///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ACYh + wAAmIcAAJiHAACYhwAAmIMAAJiDAACYgwAAlIMAAJiHAADQzxwAfF7wAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgQcJRP9GR63/Sk+r/w0Kb/8hG8T/Jh/x/yMd + 2P8WFH//FBJ4/xQRef8jJYT/HB+A/xMRdf8ZFZH/JR7o/yUe6f8lHej/JR7o/yQe6P8kHef/JR7o/yUd + 5/8lGuf/JSLo/yQ47P8lSfb/JEr7/yNH8f8jR+//I0fw/yNH8P8jR/D/I0fv/yNH8P8jR/D/I0bv/yNG + 7/8jR+//I0bw/yNH7/8jRu//I0fv/yNH7/8jRu//I0bv/yNH8P8jR+//I0fw/yNH7/8jR+//I0fv/yNG + 7/8jR+//I0fv/yNH7/8jR+//I0fw/yNG7/8jRu//I0fw/yNH8P8jR+//G0Dx/1p14f/LysL/trvI/2GA + 3//AwsX/z8zB/3eM2f8YP/L/I0bv/yNG7/8jR/D/I0bw/yNH7/8jSfD/I0nw/yRE7f8lNOj/Gxrj/2pw + 0//NzcL/xMTE/87Owv93etL/HBXh/yYe4P8mH+D/Jh7f/yYe4P8lH+D/JR7f/yYf4P8mHuD/Jh/g/yYf + 4P8mHt//Jh/g/yYf4P8mHt//Jh7g/yYd4P8mHd//JRzf/yUb3/8mHN//JiDg/yYk4v8lL+b/JDzr/yRF + 7v8jSfD/I0fw/yNH8P8jR+//I0bv/yNH7/8jR/D/I0fw/yNH8P8jRu//I0bw/yNH7/8jR+//I0bw/yNG + 7/8jR+//I0fv/yNH8P8jRu//I0bv/yNG7/8jR/D/I0fv/yNH8P8jR+//I0fw/yNH8P8jR+//I0fw/yNG + 7/8kSfX/Ij/c/xYVgP8TEHD/GheX/yYf4P8mH+P/Jh/g/yYf4P8mHuD/Jh/g/yYd3/8lJOL/I0Tv/yNH + 8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH + 7/8jRvD/I0fw/yNH8P8jSPD/I0jw/yNI8P8jSfD/G0T7/1l05v8xMi3/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP98fH3//////////1v///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wAmIcAAJiHAACYhwAAmIcAAJiDAACYgwAAmIMAAJSDAACYhwAA0M8cAHxe8AAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQMFB8AMDGH/TlK0/zs7nf8NC2//IRvD/yYf + 9f8fGsH/FBN0/xQTev8VE3r/EhB5/xMRev8UE3X/IBu//yUe7v8lHuj/JR3n/yUd6P8lHuj/JR3o/yUc + 5/8lHOf/JS3q/yRE7/8jSvH/I0fw/yNG7/8jR+//I0fv/yNG7/8jR/D/I0fw/yNH7/8jR+//I0fv/yNG + 7/8jRu//I0fw/yNG7/8jR/D/I0fw/yNH8P8jR/D/I0bw/yNH7/8jR/D/I0fw/yNH8P8jRu//I0fv/yNH + 8P8jR+//I0fv/yNH8P8jRu//I0bv/yNH7/8jR/D/I0fv/yNG7/8jR/D/I0fv/x9D8P8+YOj/w8TE/8rI + w/9TcOP/iprU/9PPwP+2vMj/LlPs/x5C8f8jRu//I0fw/yNG8P8jR/D/I0fv/yFF8P8iSPH/I0rx/xs9 + 7/9JX+P/w8XF/8bFxP/MysL/lJ3N/yMf4P8lGt//Jh3f/yYd3/8mHN7/Jhzf/yYc3/8mHd//Jh3f/yYd + 3/8mHd//Jhzf/yUc3/8lHN7/Jh3e/yYg4P8mIuH/JSTi/yUq4/8lMeb/JTjq/yRA7P8jRu//I0nx/yNJ + 8f8jSPD/I0bv/yNH8P8jR/D/I0fw/yNH7/8jR+//I0fw/yNH7/8jR+//I0bv/yNG8P8jR/D/I0bv/yNG + 7/8jR/D/I0bv/yNG7/8jR/D/I0bv/yNH7/8jR/D/I0bw/yNH7/8jR+//I0bv/yNH7/8jR+//I0fv/yNG + 7/8jSPL/JEfu/xkelP8SD27/GBaJ/yYf2f8mH+T/Jh/f/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh3f/yQ9 + 6/8jSPD/I0fw/yNH8P8jR/D/I0bv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNG8P8jRu//I0fv/yNH + 7/8jR+//I0nw/yNJ8f8jR/D/I0Tt/yRB7f8kQO3/JD3r/yY99/8bJ3z/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/qamq//////T///8z////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8AJiHAACYhwAAmIcAAJiHAACYgwAAmIMAAJiDAACUgwAAmIcAANDPHAB8XvAAeF7wAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC4FBxj0EBBy/0JHqP8zM5X/Dw1x/yEc + x/8nHvX/HxnA/xQSdP8VE3r/FRN6/xUTe/8UEnb/GBWO/yUe5f8lHun/JR3n/yUd5/8lHuj/JRzn/yUb + 5/8lJOn/JDzt/yNK8P8jSPD/I0bv/yNH7/8jR/D/I0fv/yNH7/8jRu//I0fv/yNH8P8jR/D/I0fw/yNH + 8P8jR+//I0fv/yNH7/8jR+//I0bv/yNH7/8jR/D/I0fv/yNG7/8jRu//I0fv/yNH8P8jR/D/I0bv/yNG + 7/8jR/D/I0fw/yNH8P8jR/D/I0bv/yNG7/8jR/D/I0fw/yNG7/8jRu//I0fv/yNG7/8gRPD/LE7t/7K4 + yf/SzcD/boXc/0do5f/JycP/z8zB/22F3P8XPvP/I0fw/yNH8P8jRu//I0fv/yNH7/8hSO//HkPv/yNH + 8P8gRfD/LFPt/7S5yP/Ix8P/x8bD/7S5yP8uR+n/Ijfr/yQ26P8lM+f/JTHm/yUu5f8lK+T/JSzk/yUs + 5P8lLOP/JSzk/yUv5f8lMeb/JDbo/yQ76v8kP+v/JELt/yNG7/8jSPD/I0nw/yNJ8P8jSfH/I0fv/yNH + 8P8jR/D/I0bv/yNH7/8jR+//I0fv/yNG7/8jR/D/I0fv/yNH7/8jR+//I0fv/yNG7/8jRu//I0fw/yNH + 7/8jR/D/I0fv/yNG7/8jR+//I0fw/yNH7/8jR+//I0bv/yNH8P8jR+//I0fv/yNH8P8jR/D/I0bw/yNG + 7/8jR/D/JEr2/x4vuP8TD3D/FRR7/yMdx/8nIOf/Jh7f/yYf4P8mH9//Jh7g/yYf4P8mH+D/Jh/g/yYc + 3/8kPOr/I0nw/yNH7/8jRvD/I0bv/yNH8P8jR/D/I0fv/yNG7/8jR+//I0fv/yNH8P8jR/D/I0bv/yNH + 8P8jSfD/I0fv/yQ/7P8lMuf/JSnj/yYl4f8mH+D/Jh7g/yYd3/8pIOv/Fhhn/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/CwsM/8zMzv/////T////Cf///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////ACYhwAAmIcAAJiHAACYhwAAmIMAAJiDAACYgwAAlIMAAJiHAADQzxwAfF7wAHhe8AB0W + uwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbCQoo/xEPff88Q6H/LS6R/xAO + cv8gG8L/Jh7w/yQd2P8WFH//FBJ1/xUTe/8UEnb/FRN3/yIcx/8mHu3/JR3n/yUd5/8lHej/JRvn/yUf + 6P8kM+v/I0Xv/yNJ8P8jR/D/JEfw/yRG8P8jR+//I0bw/yNH8P8jR+//I0fv/yNH8P8jRu//I0fv/yNH + 7/8jR+//I0fv/yNH7/8jRvD/I0fv/yNH8P8jR+//I0fv/yNH7/8jR+//I0bv/yNH8P8jR+//I0fw/yNG + 8P8jR+//I0fw/yNG7/8jR/D/I0fw/yNH7/8jR+//Ikbv/yNG7/8jR+//I0fw/yNH7/8jRu//IUXv/yFI + 8P+XptD/08/A/5ak0f8bQ/L/m6nQ/9LOwP+yucr/K1Du/x5D8f8jRu//I0fw/yNH8P8dQe7/kKn4/1l5 + 9P8bQO//Ikbv/x5D8f+WpdH/y8jC/8XExP/DxMT/RWno/x1E8v8jSfD/I0nx/yNJ8P8jSPD/I0jv/yNI + 8P8jSfD/I0jv/yNJ8P8jSfH/I0nx/yNJ8f8jSfD/I0nw/yNI8P8jR+//I0fw/yNH8P8jR/D/I0fw/yNH + 7/8jR+//I0fw/yNG7/8jR+//I0fv/yNG7/8jRvD/I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//JEfw/yNG + 7/8jR/D/I0fw/yNG7/8jR+//I0fv/yNH8P8jRu//I0bv/yNH7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNG + 8P8jR/D/JEn0/yI/3v8WFX//ExBx/x4Zq/8nH+f/Jh/g/yYf4P8mH+D/JR7g/yYf4P8mH+D/Jh/g/yYc + 3/8lJOL/I0Xv/yNH8P8jR+//I0fv/yNG7/8jRvD/I0bv/yNH8P8jR+//I0fw/yNH8P8jR+//I0fv/yNH + 8P8jSfH/JD7r/yUp4/8mHuD/Jhvf/yYc3/8mHuD/Jh7g/yYe4P8mHt//KSDv/xoYhv8AAQD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/x4eH//w8PD/////hf///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wAmIcAAJiHAACYhwAAmIcAAJiDAACYgwAAmIMAAJSDAACYhwAA0M8cAHxe8AB4X + vAAdFrsAHBa6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkw4ROP8TEH7/MjiY/yYo + jP8RDnH/HRir/yYe7/8mHu3/IBq//xUUf/8TEnP/FhR+/x8au/8lHu3/JR3o/yUc6v8lG+j/JR3n/yQs + 6v8kQe7/I0rw/yNI8P8jRu//I0fv/yNH8P8jR+//I0bv/yNH7/8jR/D/I0fv/yNG7/8jR+//I0bv/yNH + 7/8jR+//I0fv/yNG7/8jR+//I0bv/yNG7/8jR+//I0fw/yNH7/8jR+//I0bv/yNH7/8jR+//I0fw/yNH + 7/8jRu//I0fw/yNG8P8jRu//I0fw/yNH8P8jR+//Ikbv/xxC7/8iRu//I0fv/yNH7/8jR+//HEDv/xY8 + 7/8ON/H/d4rV/9PPwP+0ucj/Iknw/1Zy4v/MysL/zszB/22E3P8XPvP/I0fv/yNG8P8hRfD/HUPv/9zj + /f+zwfr/Fzzu/yNG7/8ZP/L/b4fb/83Kwf/FxMT/y8rC/2V93v8ZPvL/I0fv/yNH7/8jR/D/I0fw/yNH + 7/8jRu//I0fv/yNH7/8jR+//I0fw/yNG8P8jR+//I0fw/yNH7/8jRu//I0fv/yNH7/8jRu//I0fv/yNH + 8P8jR+//I0bv/yNH7/8jR+//I0fv/yNG7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH8P8jR+//I0fv/yNH + 8P8jRu//I0bv/yNH7/8jRu//I0fw/yNH7/8jR+//I0bv/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0fw/yNH + 7/8jR+//I0fw/yRJ9f8cJ6f/Ew5u/xgVi/8mH93/Jx/j/yYf4P8mHt//Jh7g/yYf4P8mHt//Jh/g/yYe + 3/8mHt//JD7r/yNI7/8jR/D/I0fv/yNH7/8jR+//I0bv/yNG7/8jR+//I0fv/yNH8P8jR+//I0fv/yNI + 8P8jSO//JTTn/yYe3/8mG9//Jh7g/yYe3/8lHt//Jh/g/yYf4P8mH+D/Jh/g/ycf6P8lIr//BgcM/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP89PT///v7+8f///yr///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8AJiHAACYhwAAmIcAAJiHAACYgwAAmIMAAJiDAACUgwAAmIcAANDPHAB8X + vAAeF7wAHRa7ABwWugAeF7wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALYPEUL/ExF//yMk + if8iJIf/EhBz/xkWkf8mHuf/JR3p/yYe7f8jHdn/IBrH/yQc2f8lHe7/JRzt/yUb6P8mH9z/JSvk/yQ9 + 7v8jSfD/I0jw/yNG7/8jR+//I0fw/yNH7/8jR/D/I0fv/yRH7/8jR/D/I0fv/yNG7/8jR+//I0fw/yNH + 8P8jRvD/I0bv/yNG8P8jRu//I0bv/yNH8P8jRu//I0fw/yNG8P8jRu//I0fv/yNG7/8kR+//IETv/xo/ + 7/8hRO//I0bv/yNH8P8kR/D/I0bv/yNG7/8jR+//I0fv/xxB7/+Amvf/QmHx/x5C7/8jR/D/Gj/v/0dq + 8/+ouvr/sb/7/6e14v/Ix8H/wcPF/0Jj5/8cQ/H/pK7N/9HNwP+yucn/K1Dt/x5C8f8jR+//HkLv/y5V + 8P/q8v7/tcH6/xc87v8jRvD/GT/y/1Bt5P/Gx8T/xcTE/87Lwf+DldX/HELx/yNG7/8jR+//I0fw/yNH + 7/8jR+//I0fw/yNG7/8jR+//I0fv/yNH7/8jRu//I0fv/yNG8P8jR/D/I0bv/yNH7/8jR+//I0bv/yNG + 8P8jR+//I0bv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNG8P8jR+//I0fv/yNG7/8jRvD/I0bv/yNG + 7/8jRu//I0fw/yNH8P8jR+//I0fv/yNH8P8jRvD/I0bv/yNH8P8jR+//I0fw/yJF7/8jRu//I0bv/yNG + 7/8jR+//I0fv/yNJ9P8iPtv/FRR9/xQRdP8hHL7/JyDn/yYe3/8mHuD/Jh7f/yYf3/8lH9//Jh7f/yYe + 4P8mHN//JDbo/yNK8P8jR+//I0fw/yNH8P8jRu//I0fw/yNH8P8jR+//I0bv/yNG7/8jR+//I0fw/yNH + 8P8jSfD/JTLm/yYc3/8lHt//Jh/g/yUf3/8mHt//JR7g/yYe3/8mHuD/Jh/g/yYf4P8lH+D/KCHp/xET + Q/8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/jo6P/////5n///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////ACYhwAAmIcAAJiHAACYhwAAmIMAAJiDAACYgwAAlIMAAJiHAADQz + xwAfF7wAHhe8AB0WuwAcFroAHhe8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABUAAADWDxJM/xUT + gf8YF37/Fxh9/xQSeP8WFH7/IxzY/yUd7P8kHef/JR3s/yYd8v8lHPD/JRzj/yUhxv8lMbj/JUDQ/yRI + 7P8jSfH/I0bv/yNH8P8jR+//I0fw/yNH8P8jR+//I0fw/yNH7/8jR+//I0fv/yNH8P8jRvD/I0fw/yNG + 7/8jRu//I0bw/yNG8P8jR/D/I0bw/yNG8P8jR/D/I0fv/yNG8P8jR/D/I0bw/yNH7/8jR+//IETv/zZZ + 8f9VdPP/KE3w/xg97/8jRu//I0fw/yNH8P8jR+//I0fv/x1B7v86XvH/5u/+/0Vj8v8dQe//HUHv/zle + 8f/j7P3////////////z8/D/wsLD/8zKwf9ie97/Djb1/1x44P/OzMH/zszB/2eA3f8XPvL/JEfw/xxA + 7/89YfL/3un+/1p59P8bQO//I0fw/x9D8P8xVev/uLzH/8fGw//Jx8P/o6/N/yVJ7/8iRfD/I0bw/yFE + 7/8iRe//I0fw/yNH7/8jRu//I0bv/yNH8P8jRvD/I0fv/yNH7/8jR+//I0fw/yNH8P8jRu//I0fv/yNH + 8P8jR/D/I0fw/yNH7/8jR/D/I0fw/yNG8P8jR+//I0fw/yNG7/8jR/D/I0fw/yNG8P8jR/D/I0bv/yNG + 7/8jRu//I0fv/yNH8P8jR/D/I0fv/yNH8P8jR/D/I0fv/yNG8P8jRvD/I0fw/yNG7/8kSfD/I0fv/yNH + 7/8jR/D/I0bv/yNH8P8kSvX/HSuv/xMObv8ZFpD/Jh/h/yYe4f8mHt//Jh/g/yYf4P8lHt//Jh7f/yYf + 4P8mHN//Jivk/yNI8P8jR/D/I0bw/yNH7/8jRu//I0bv/yNH8P8jR/D/I0bw/yNH7/8jR/D/I0fw/yNH + 8P8jSfD/JTfo/yYc3/8mHuD/Jh/g/yYf4P8lH9//Jh7g/yUf4P8mH+D/Jh7g/yYf4P8mH+D/JR7f/ygg + 7f8fH5X/AQIA/wAAAP8AAAD/AAAA/wAAAP8AAAD/JCQl//Hx8e////8n////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wAmIcAAJiHAACYhwAAmIcAAJiDAACYgwAAmIMAAJSDAACYh + wAA0M8cAHxe8AB4XvAAdFrsAHBa6AB4XvAAhGr0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsAAAA8g8S + S/8WFIL/FBJ6/xQSev8UEnr/FBJ1/yEbzv8mHu//JRzt/yUb5/8mH9b/JSnD/yU2s/8kQsb/I0no/yNJ + 8/8jR/H/I0fv/yNH7/8jRvD/I0bv/yNG7/8jRvD/I0fv/yNH8P8jR/D/JEfw/yRH7/8jRvD/I0fv/yNH + 7/8jRvD/I0bv/yNH7/8jRvD/I0fw/yNH7/8jR/D/I0fw/yNH7/8jR+//I0bw/yNH7/8jR/D/I0fw/x9D + 7/84XvH/i6j4/6zC+v9ig/X/GT7v/yJF7/8jRu//I0fw/yNH8P8VO+7/coz2/8/c/f8nTPD/IEPv/xY8 + 7/+Pp/j////////////////////5/8jIxv/LyMH/gpbW/xg+8/8nTe7/rLTL/9HNwP+qtMv/JEnu/xM4 + 8P8XPO//QmXy/8LW/P8tUvH/H0Pv/yNH7/8iRvD/H0Tw/5yoz//LycP/xsbD/7y/xv80V+v/H0Pw/yNF + 8P8pTvD/IUfw/yNH8P8jR+//I0bw/yNH8P8jR+//I0bv/yNG8P8jRvD/I0bv/yNH7/8jR/D/I0fv/yNG + 7/8jR/D/I0fv/yNG7/8jR+//JEfw/x9C7/8aPu//I0fv/yNH7/8jR/D/I0fv/yRH8P8kR/D/I0fv/yNH + 7/8jR+//I0bv/yJG7/8aP+//Gz/v/yJG7/8jR/D/I0bv/yNH8P8jR/D/I0fw/yNG8P8dQO//T3P0/zpg + 8v8gRO//I0bw/yNG7/8jSPL/I0To/xcZhv8UEXT/IRy//ycf5v8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mHuD/JiDg/yNA7P8jSPD/I0fv/yNH7/8jR/D/I0bw/yNG7/8jR+//I0fw/yNH8P8jR/D/I0bv/yNG + 7/8jSfD/JDzr/yYf3/8lHd//Jh/g/yYf4P8lHt//Jh7f/yYf3/8mH9//JR/f/yYf4f8mH+L/Jh/i/yYf + 4P8mHuP/JyPZ/wsNI/8AAAD/AAAA/wAAAP8AAAD/AAAA/0lJSf////+F////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// + /wD///8A////AP///wD///8A////AP///wD///8AJiHAACYhwAAmIcAAJiHAACYgwAAmIMAAJiDAACUg + wAAmIcAANDPHAB8XvAAeF7wAHRa7ABwWugAeF7wAIRq9AB8YvQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOQAA + AP0PEUH/FhOB/xUTe/8UEnv/FBJ3/xYThP8jGt//Jh3i/yYkyv8lMsH/JUDK/yRH3f8jSfD/I0f1/yNH + 8f8jRvD/I0fv/yNH8P8jR/D/I0fw/yNG7/8jRu//I0bv/yNH7/8jR/D/I0fw/yRH7/8kRu//I0bv/yNH + 8P8jR+//I0fw/yNH8P8jR/D/I0fv/yNH8P8jR+//I0bv/yNG7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH + 8P8kR/D/H0Pv/xg97/85XfL/obr5/3WT9/8YPe7/I0bv/yNG7/8jRvD/Fz7v/5y0+f+Bmvf/Fzzu/yJF + 7/8fRe//yNj8/9Hb/P9ujfb/dpP3/7TF+f/Hys//ysfA/6Wuzf8hRvD/Fz3y/2R+3v/QzcH/ysnB/26H + 3/9Scff/Kk/w/zZb8f/J3Pz/Nlrx/x5C7/8jR/D/I0bw/xpA8v94jtn/zcvB/8XFxP/Gx8P/Um/j/xs/ + 8f8bP+//d5X3/0tu8/8cQO//I0fv/yNH8P8hRfD/GT7v/xY77/8YPe//Fjzu/xo/7v8jR+//I0fw/yNH + 7/8jR+//I0fw/yNH7/8jR+//I0fv/x9C7/8yVfD/fJv3/ylN8P8hRe//I0fw/yNG7/8hQ+//I0fw/yNH + 8P8jR/D/I0fv/x9D7/8dQ+//SWvz/0Nm8v8bQe//IkXw/yNH7/8jR/D/I0fv/yBD7/8iRu//FTvu/4GY + 9v9oiPb/Gz/v/yNG8P8jR/D/JEn1/yA2x/8TEHH/GRaO/yYf4P8mH+H/Jh/g/yYf4P8mHuD/Jh/g/yYf + 3/8mHuD/Jhvf/yUy5v8jSvD/I0fw/yNH8P8jR/D/I0fw/yNH8P8jRu//I0bv/yNH8P8jR+//I0bv/yNH + 7/8jSfD/I0Dr/yYg4P8mHd//Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH9//Jh/h/ycf5v8nIOL/Jh/b/yYf + 2v8mH+L/KB/r/ygj1/8MDiP/AAAA/wAAAP8AAAD/AAAA/wAAAP9TVVfe/f7/D/3+/wD9/v8A/f7/AP3+ + /wD9/v8A/f7/AP3+/wD9/v8A/f7/AP3+/wD9/v8A/f7/AP3+/wD9/v8A/f7/AP3+/wD9/v8A/f7/AP3+ + /wD9/v8A/f7/AP3+/wD9/v8A/f7/AP3+/wD9/v8A/f7/ACYhwAAmIcAAJiHAACYhwAAmIMAAJiDAACYg + wAAlIMAAJiHAADQzxwAfF7wAHhe8AB0WuwAcFroAHhe8ACEavQAfGL0AIBm9AAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AE0AAAD/DhA3/xYTgv8VE3v/FRN7/xQRdf8aF4L/Ji22/yU6vf8kRdT/I0nt/yNI9P8jR/P/I0bw/yNG + 7/8jR+//I0bv/yNG7/8jR/D/I0fv/yNH7/8jR/D/I0fv/yNH7/8jRu//I0bw/yNH8P8jR/D/I0bw/yNG + 7/8jR/D/I0fv/yNH8P8jR/D/I0fw/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH8P8jR+//I0fw/yNH + 8P8jR+//I0bv/yNH8P8jR/D/HD/v/xxB7/+Pq/j/WHn1/xk97/8jR+//Ikbv/x1B7/+zxvv/R2fy/xxB + 7v8gRO//MFHw/77N/P8yV/H/Eznu/xY77v8WP/H/kJ/U/9HNwP+3vMf/M1js/xs/8v8sUO3/s7rJ/8nH + wP/Iysz////8/93l/f9oivX/zdz8/0Vk8v8bQO//I0fv/yNH7/8ZPvL/V3Pi/8jIw//FxcT/zszC/3OG + 2/8YP/L/GD3v/46l+P+Pp/j/ETfu/xs/7/8gRO//LlPw/1h49P+Jo/f/oLn5/4yl+P9HaPL/Fj3v/x9C + 7/8kR/D/I0bv/yNH7/8iRu//Fjzv/xc87v8ZP+7/orj5/+Hs/v8mS/D/IETv/yFF7/8vU/D/P2by/x9D + 7/8jRu//I0fw/yBE7/8jSe//orj5//H4///z+v//jKX4/xpA7/8iRu//Ikbv/yZK8P9CaPP/I0jw/xU6 + 7v+BmPb/b472/xs/7/8jRvD/I0fw/yRI8/8cJqP/Ew9t/x8btP8nIOf/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh7g/yUh4P8kQu3/I0nx/yNH8P8jR/D/I0fw/yNG7/8jRu//I0fw/yNH7/8jR+//I0bv/yNH + 8P8jSvH/Iz7r/yYi4P8mHd//Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mHt//Jh/k/ycg4v8gG7n/GReR/xQQ + hf8KB4D/DAmK/xIOsP8PEGj/AAAA/wAAAP8AAAD/AAAA/wAAAP8BAQD/IiSE7FZQ8S5WUPEAVlDxAFZQ + 8QBWUPEAVlDxAFZQ8QBWUPEAVlDxAFZQ8QBWUPEAVlDxAFZQ8QBWUPEAVlDxAFZQ8QBWUPEAVlDxAFZQ + 8QBWUPEAVlDxAFZQ8QBWUPEAVlDxAFZQ8QBWUPEAVlDxAFZQ8QAmIcAAJiHAACYhwAAmIcAAJiDAACYg + wAAmIMAAJSDAACYhwAA0M8cAHxe8AB4XvAAdFrsAHBa6AB4XvAAhGr0AHxi9ACAZvQAmH8AAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAABTAAAA/wgIJv8WFID/FRN7/xMQd/8WFoD/Iju0/yRI3v8jSfT/I0f0/yNH8P8jR+//I0fv/yNG + 7/8jR+//I0fw/yNH7/8jRvD/I0fw/yNH8P8jR+//I0fw/yNH7/8jR+//I0fw/yNH8P8jR+//I0bw/yNH + 8P8jR/D/I0fw/yNG7/8jR/D/I0bw/yNH8P8jR+//I0fw/yNH8P8jR/D/I0fv/yNG7/8jR/D/I0fw/yNH + 8P8jRu//I0fv/yNH8P8jR/D/I0fw/yRH8P8eQe//Kk/w/4em+P8tUfD/IETv/x5B7/85W/L/ssf6/ydO + 8P8gRO//IETv/zpa8f+Emfb/Fjzv/yNH7/8jR+//GD7y/2+E2//PzcH/x8fE/05q5P8ZP/L/GT/x/2yE + 2//PzcH/yMbB/7fB5P/S3f7/8fn//+zy/v9ScPP/GT7v/yNH8P8jR+//H0Pw/zRZ6v+6vsb/xsbD/8zJ + wv+SotL/H0Xx/xo+7/9hgfT/xdX8/zdd8f9NcPP/XIL1/3mX9/9+nfj/hqH4/4qk+P+wwvr/3un9/5+1 + +f8jS/D/HEDv/yRH7/8iRe//H0bv/1159P9Rb/P/IEXv/+fu/v+Rqfj/GkDv/yJG7/8gQ+//NFjx/1qC + 9f8gRO//I0bw/yNH8P8aP+//Y4T0/561+f9EY/L/c471/+rx/v9NbvL/Gj/v/yJG7/8hRe//fJ74/zZZ + 8f8UOe7/bov1/2iJ9f8bP+//I0bv/yNH8v8jROr/FxqG/xYTfv8lH9b/Jh/j/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYc3/8lMOX/I0nw/yNH7/8jR+//I0fv/yNG7/8jRu//I0fv/yNH8P8jR+//I0fv/yNH + 8P8jSfH/Izvq/yYg4P8mHN//Jh7g/yYe4P8mH+D/Jh/g/yYf4P8lH9//Jh/j/yYf3v8bGJr/FBN0/wkG + b/8XGXr/Wl+d/3mCrv9iaKX/JClM/wAAF/8AAAb/AAAA/wAAAP8AAAD/ExRH/yYg4f8dFeGtJh7gACYe + 4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJiHAACYhwAAmIcAAJiHAACYg + wAAmIMAAJiDAACUgwAAmIcAANDPHAB8XvAAeF7wAHRa7ABwWugAeF7wAIRq9AB8YvQAgGb0AJh/AACYf + vgAjSPAAI0jwACNH7wAjRu8AI0bvACNH7wAjR+8AI0bvACNH8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAaQAAAP8DBA7/FBNt/xQQef8UEHT/HzPB/yRK+/8jR/T/I0fv/yNH7/8jRu//I0fv/yNH + 7/8jR+//I0bv/yNH8P8jR+//I0fw/yNH8P8jR+//I0bv/yNH7/8jR/D/I0fv/yNG7/8jR/D/I0fw/yNH + 7/8kR/D/I0fw/yNH8P8jR+//I0fw/yNG8P8jRu//I0fw/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNG + 8P8jR+//I0bv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0bw/xk87v9UdfT/XoH1/xo+7/8NNO7/fZj3/5ux + +f8YPe7/I0bv/yBE7/83V/D/mK74/x9G8P8iRu//I0fv/x1B8f9Uc+L/yMjD/8/Mwf9shdv/GT/y/x5D + 8f8tU+z/tbzI/9HNwP+Wo8z/KVHx/3ST+P/M3/3/dZP1/xg97v8jRu//I0fv/yJF8P8iR+//pK7N/8nH + w//IxsP/r7bJ/ylN7v8bP/D/SGvz/9rn/v9dhPX/P2Py/zJW8v8fRO//Gz/v/xg97/8XPO7/Fjzv/z9i + 8f/A0Pv/xdT7/ytR8P8cQO//GD3v/4Gc9////////////6q++v+yxfr/Jkzw/yBE7/8jR+//IETw/zJW + 8f9qkPb/Ikbv/yJG7/8jR+//IkXv/1h89P8iRu//GD3v/w007v9/mvf/jKf4/xg97v8iRu//GUDv/6a9 + +v9nhfX/CzLu/4Wd9/9ggfT/G0Dv/yNH8P8jSPT/ITrS/xMSc/8bF5r/Jx/l/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYe3/8mH9//JD7s/yNJ8P8jR+//I0fv/yNH7/8jR/D/I0fw/yNH7/8jR+//I0fw/yNI + 8P8jSPD/JTXo/yYe4P8mHt//Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/i/yYg4P8bF5j/FBJz/wYE + cv89QZP/xMjY//b38P/39/D/9vbx/9vd3/+OlLz/Jy5w/wAAFP8AAQD/EhRF/ych1f8nH+f/Jh/f+SYe + 4DYmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYhwAAmIcAAJiHAACYh + wAAmIMAAJiDAACYgwAAlIMAAJiHAADQzxwAfF7wAHhe8AB0WuwAcFroAHhe8ACEavQAfGL0AIBm9ACYf + wAAmH74AI0jwACNI8AAjR+8AI0bvACNG7wAjR+8AI0fvACNG7wAjR/AAI0fwACNG7wAjR+8AI0bvACNH + 8AAAAAAAAAAAAAAAAHQAAAD/DBEg/xkghv8WFoT/HjC8/yNJ9f8jR/D/I0bv/yNH7/8jR+//I0bv/yNG + 7/8jR+//I0fv/yNG7/8jR+//I0fw/yNG7/8jR+//I0fv/yNG7/8jR/D/I0fv/yNH8P8jR/D/I0bv/yNG + 8P8jR+//I0fv/yNG7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fw/yNH + 7/8jR+//I0fw/yNH8P8jR/D/I0bw/yNH8P8jR/D/I0fw/yNH8P8hRe//JEjv/2uM9v8fRu//WHjz/+Lu + /v9SdPP/Gj/v/yNH7/8gRO//L1Hw/8LR/P8uUfH/H0Pv/yJG7/8fQ/D/OFrp/8PDxP/Ny8H/jp/T/xxB + 8f8iRvD/GkDx/3WL2f/Py8H/zMvC/1Rx4P8BK/D/gJv3/6S2+f8XPO7/I0fw/yNH8P8jRu//G0Ly/3+T + 1//NysH/xcXD/8DCxf8/Yuj/HEDx/yJI7/+zyPv/Tm/z/xY67v8hRO//Ikbw/yNH8P8jR/D/I0bw/yNG + 7/8ZPu7/HUXv/7DC+v/B0fv/HULv/ypQ8P+3yfv/eZf3/6m9+v//////gZv3/xI57v8jR+//I0fw/yBE + 8P8xVfH/f6D3/yRI7/8iRu//I0bv/yNH8P9PdPT/IEXv/yNH7/8fQu//NFny/5Kq+P8fQu//IkXv/xxB + 7/+Kp/j/rL/6/xI87v+uwvv/QmPy/x5C7/8jR/D/JEn3/x0vuf8TEG7/IBu5/ycf5v8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yUe4P8mHN//JSji/yNG7/8jR+//I0fv/yNH8P8jR+//I0fw/yNG8P8jR/D/I0fv/yNJ + 8f8kRe//JS3k/yYd3/8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/ycf5v8eGqz/FBJz/wkG + c/8/Q5P/3ODl//j28f/r6ur/6urq/+rq6v/w8O7/+Pfz/9fb5/9cY5v/CAxX/yEbyf8oIOz/Jh7f/yYc + 3/8mH+COJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmIcAAJiHAACYh + wAAmIcAAJiDAACYgwAAmIMAAJSDAACYhwAA0M8cAHxe8AB4XvAAdFrsAHBa6AB4XvAAhGr0AHxi9ACAZ + vQAmH8AAJh++ACNI8AAjSPAAI0fvACNG7wAjRu8AI0fvACNH7wAjRu8AI0fwACNH8AAjRu8AI0fvACNG + 7wAjR/AAI0bvACRK+QAGCh1sFCFW/yRF1/8kR/D/IkLl/yRI8/8jRvD/I0bv/yNH7/8jRu//I0fv/yNH + 7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fw/yNH + 8P8jRu//I0fw/yNH7/8jRu//I0bv/yNH7/8jR/D/I0fw/yNH7/8jR+//I0bv/yNH7/8jR+//I0fw/yNH + 8P8jR/D/I0fw/yNH7/8jR/D/I0fw/yNH7/8jR/D/I0jw/yNJ8f8jSvD/I0nw/xtB7/9KcvT/mbb6//z9 + //+uwPn/HEPv/yFG7/8jR+//IUfv/yNK8P/H1/3/X330/xM57v8bQe//Eznv/ydM7f+stsv/zsvB/6yz + yv8kSu//IUXw/xxA8f8+YOz/wcTH/87LwP+jr83/GkDv/0dn8/+zxvr/IUjw/yJF7/8jR+//I0fw/xk/ + 8v9cduD/yMnD/8XFw//KycL/X3jf/xg+8v8ZPu7/n7L5/2WE9f8YPe7/I0fv/yNH8P8jRu//I0fv/yNH + 8P8jR/D/JEfw/x1B7/8dRO//rcH6/4uj+P8+ZfP/T3b0/xE27v8YP+7/rsD6/8/c/P8jSO//IUTv/yNH + 7/8gRO//LlLx/6a9+v8rTvD/IUXv/yNG7/8cQO//XYH1/05x9P8TOO7/Gj7v/yhO8P+TqPf/IETv/yJG + 8P8kSO//SnHz/83a/P+pvfn/uMr7/yVK7/8hRe//I0fw/yRJ9P8aJqT/FRB3/yQez/8mH+P/Jh7f/yYf + 4P8mH+D/Jh/g/yYf4P8lHt//Jhze/yUy5/8jSfD/I0fw/yNH7/8jR/D/I0fv/yNH7/8jRvD/I0fw/yNJ + 8P8kPev/JiXh/yYd3v8mHuD/Jh/g/yYe3/8mHt//Jh7g/yYf4P8mH+D/Jh7g/ycf5P8kHdD/FhR+/wwJ + c/8mKYX/0dXe//b08P/q6er/6urq/+nq6v/p6en/6enp/+np6f/y8e3/8PHw/4aNu/8SE4f/IBjQ/ych + 5f8lJ+L/Jh7g2iYf4A4mH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJiHAACYh + wAAmIcAAJiHAACYgwAAmIMAAJiDAACUgwAAmIcAANDPHAB8XvAAeF7wAHRa7ABwWugAeF7wAIRq9AB8Y + vQAgGb0AJh/AACYfvgAjSPAAI0jwACNH7wAjRu8AI0bvACNH7wAjR+8AI0bvACNH8AAjR/AAI0bvACNH + 7wAjRu8AI0fwACNG7wAkSvkwIkLWxiVI8P8jSPf/I0jx/yRI8/8jR+//I0bv/yNG7/8jR/D/I0bv/yNH + 7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH + 7/8jR+//I0fw/yNH7/8jR/D/I0fw/yNG7/8jR+//I0fw/yNG7/8jR+//I0fv/yNG7/8jR+//I0bv/yNH + 8P8jR/D/I0fw/yNH8P8jSPD/I0nw/yNJ8f8jR/D/JETu/yQ/6/8kO+n/JDbp/yU05/8hKuX/Ljzm/87b + +v//////iJrx/xcf4v8kJ+P/Jirj/yQq5P8aJ+T/q7n1/9rk/f+uw/r/vNH8/3+Z9/8mRe3/i5jP/9LO + wf+8wMb/Pl/o/xxC8f8fRPD/L1j1/7vH2v/Jx8D/zMrC/1h04f8aQPL/pr77/zhc8v8fQu//I0fv/yNH + 8P8eQvH/PF/o/77Bxv/GxcP/zcvB/4CU1v8aQfL/GT3v/4ah+P9zjfb/Fjvu/yNH8P8jR/D/I0bv/yNH + 8P8jR+//I0fw/yNH8P8jR/D/HkLw/yFH8P+et/n/e5z3/2mL9v8WPO//Gz/v/zhc8f/y+P//XHz0/xo/ + 7/8jR/D/IUXv/yNJ7/+6zfv/Wnr0/xE37v8jRu//HkLv/zZZ8f+jvPr/WHjz/zFX8f+ww/r/l6/4/xc9 + 7v8iRu//Jkrv/ytU8P+Lovf//////4Oc9/8XPe7/I0bv/yNI8f8jRu3/GR6Q/xcThv8mH9//Jh7h/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYc3/8kO+v/I0nw/yNH7/8jRu//I0fv/yNH7/8jR/D/I0nx/yRH + 7/8lMub/JiDf/yYd3/8mH+D/JR7g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8nIOb/HRmj/xIQ + cv8LDHf/oKXG//j38f/p6er/6urq/+rq6v/p6en/6unp/+np6f/q6ur/6enq/+zs6//39/D/kpm5/xMS + jv8hH9z/I0jy/yUv5vwmHN5GJhzeACYc3gAmHN4AJhzeACYc3gAmHN4AJhzeACYc3gAmHN4AJhzeACYc + 3gAmHN4AJhzeACYc3gAmHN4AJhzeACYc3gAmHN4AJhzeACYc3gAmHN4AJhzeACYc3gAmHN4AJhzeACYh + wAAmIcAAJiHAACYhwAAmIMAAJiDAACYgwAAlIMAAJiHAADQzxwAfF7wAHhe8AB0WuwAcFroAHhe8ACEa + vQAfGL0AIBm9ACYfwAAmH74AI0jwACNI8AAjR+8AI0bvACNG7wAjR+8AI0fvACNG7wAjR/AAI0fwACNG + 7wAjR+8AI0bvACNH8AIjRu9zI0fw7SRJ9f8jR/H/I0bv/yNG7/8jRvD/I0fw/yNH7/8jR/D/I0fw/yNH + 7/8jRu//I0fv/yNH7/8jR+//I0bv/yNG7/8jR/D/I0fw/yNG8P8jR+//I0fv/yNH7/8jRu//I0bv/yNH + 8P8jR/D/I0fw/yNH8P8jR/D/I0bv/yNH8P8jR/D/I0fw/yNH7/8jRvD/I0fv/yNH8P8jR+//I0fw/yNH + 7/8jR+//I0nx/yNJ8P8jRu7/JD/r/yQ36f8lL+X/Jifj/yYi4f8mIOD/Jh3f/yYb3/8mHd//JRzf/yEZ + 3v95h+3/0Nj5//////98hOz/GRLd/yYc3/8mHd//GxLe/3eB7P//////oq7y/42S7//o7vz/xc36/4mR + 0//KycH/zMzC/1pg2P8bIOb/JS7l/x0t6f+suO7/09LH/8rIwP+hrM3/Fz3v/4ml+P9TcfP/G0Hv/yNJ + 8P8jSvD/IUjx/yVM8P+ossz/yMbD/8rIw/+grM7/Ikfw/xk+7/91k/f/fpT2/xQ67v8jR/D/I0fw/yJG + 7/8jR/D/I0fw/yNH8P8jRu//Ikbv/yJF7/8UOO//NFrx/6rB+v/H2P3/UHD0/xA27f8WO+7/ucv7/3mV + 9v8YPe//I0bw/yJF7/8fRu//nbf5/+Do/f9EaPP/Fj3u/xc97/8dQO//L1Px/6K7+v/5/P///////01w + 8/8aP+//I0bv/yZK8P85YPL/OVvx/8DR+/82WvH/H0Lv/yNG7/8jSPL/I0Po/xYYgf8bF5j/Jx/i/yYe + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYd3/8mIeD/JEHt/yNI8P8jRu//I0fv/yNH7/8jRu//I0nx/yQ/ + 7P8mKOP/Jhze/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf3/8mH9//Jh/g/yYf4P8mH+P/JR7W/xcV + gv8HBHD/Ulie//Lz7//s6+v/6enp/+rq6v/q6ur/6enp/+np6v/q6ur/6urq/+rq6v/p6en/7Ovr//X2 + 8P+PlLf/Fhaq/yND8/8jRe7/JSLgdSUi4AAlIuAAJSLgACUi4AAlIuAAJSLgACUi4AAlIuAAJSLgACUi + 4AAlIuAAJSLgACUi4AAlIuAAJSLgACUi4AAlIuAAJSLgACUi4AAlIuAAJSLgACUi4AAlIuAAJSLgACUi + 4AAmIcAAJiHAACYhwAAmIcAAJiDAACYgwAAmIMAAJSDAACYhwAA0M8cAHxe8AB4XvAAdFrsAHBa6AB4X + vAAhGr0AHxi9ACAZvQAmH8AAJh++ACNI8AAjSPAAI0fvACNG7wAjRu8AI0fvACNH7wAjRu8AI0fwACNH + 8AAjRu8AI0fvACNG7yUjR++3I0fv/yNH8P8jR/D/I0bv/yNH8P8jRu//I0bw/yNH8P8jR+//I0bw/yNG + 7/8jRu//I0bv/yNH7/8jRu//I0fv/yNG7/8jRu//I0fv/yNH7/8jRu//I0bv/yNH8P8jR+//I0fw/yNG + 7/8jR/D/I0fw/yNH8P8jR+//I0fw/yNH7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fw/yNI + 8P8jSfD/JETv/yQ76/8lMOX/Jibi/yYg4P8mHd//Jhzf/yYd4P8mHuD/Jh7g/yYe3/8mHuD/Jh7g/yYf + 3/8jGt//P0vl/z5G5P+1vvX//////1pf6P8cE97/Jh/g/yIZ3/8wMuH/6/D8/9fa+f8wNuP/JSXg/7e7 + 9//e5Of/v7/B/83Mwv92gNL/HRXh/yYc3/8bEN3/aW/t/+Pm2//CwsD/ysvE/0RM2P9dYOz/b4Hu/xwl + 4/8lMeX/JTTn/yQ26f8eNuv/hpXT/83Lwv/HxsP/ub3H/zJV6/8XO/D/Y4T1/3qQ9f8VO+7/JEnw/yNI + 7/8aQO7/HEDv/xxA7/8ZPe//FDvv/xg/7/8fRe//PGDy/5Sr+f+6z/z/d5j3/+Xw//+En/j/Q2jz/4mp + +f9QdvT/HUPw/yNJ8P8jSPD/HkTw/12E9v/t9///+fz//7HD+v9igfX/JEzx/xQ87/94mff/xtn9/3mW + 9/8cRfD/Iknw/yJJ8P8mTfH/OmTz/yBH8P8sU/H/IUXv/yNH7/8jR+//I0jy/yJA4f8UE3f/HRio/ycg + 5f8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8lHd//JSTh/yRE7v8jSPD/I0fv/yNH7/8jSPD/I0jx/yU2 + 6P8mIOD/Jh3f/yYe3/8mH+D/Jh7g/yYe3/8mHt//Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mH+D/Jx/m/yAb + uP8QDXL/Fxl9/8LG1//09O//6enp/+rq6v/q6ur/6urq/+np6f/q6ur/6urq/+np6f/p6en/6unp/+rp + 6v/s6+r/9PTt/25ys/8WL9n/I0v0/yQ66acjSPAAI0jwACNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI + 8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI + 8AAjSPAAJiHAACYhwAAmIcAAJiHAACYgwAAmIMAAJiDAACUgwAAmIcAANDPHAB8XvAAeF7wAHRa7ABwW + ugAeF7wAIRq9AB8YvQAgGb0AJh/AACYfvgAjSPAAI0jwACNH7wAjRu8AI0bvACNH7wAjR+8AI0bvACNH + 8AAjR/AAI0bvACNH708jR+/hI0fv/yNG7/8jR+//I0fv/yNG7/8jR/D/I0bw/yNG8P8jRu//I0fv/yNG + 7/8jRu//I0fv/yNG7/8jRu//I0fv/yNH8P8jRvD/I0bv/yNG7/8jR+//I0fv/yNG7/8jR/D/I0fv/yNG + 8P8jR+//I0bv/yNH7/8jR+//I0bw/yNH7/8jRu//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0nw/yNH + 7/8kPOr/JS/m/yYj4f8mHt//Jh3f/yYd4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH9//JR7f/yYf + 4P8mH+D/IBbf/0VI5P84PeP/HRzf/7/I9v/c5vv/Kynh/yIa3/8mHt//GxTe/4+Y8P//////4+r8/3J4 + 6/9NWun/197v/8nIw//KysL/naDM/yAZ4f8lHeD/JBvg/yYj4/+yu+r/1NPH/8rJwf+bn8v/i5jw/3uH + 7v8dE93/Jhzf/yYc3v8mHd//HBPh/2Zn1P/LzMP/xsXE/8PHxP9PVdr/FxPi/1Va6f96fO7/Fx3i/x8l + 4/9IUen/kqf2/0Ba6v9FYOr/XXnw/4Sb9/+ovfr/2OT+//f+///o7vv/XnDt/xQs6P9gcO7/t8H1/7LB + 9f9ne+//ITfq/yQ56f8kOen/JDnp/yE16f9MZ+//b4Lw//j9/////////////0xg7f8aKuf/JDXo/yY4 + 6P8dLOf/JDPo/yQ06P8lNOn/JDbp/yU66v8kPOv/IT3s/yNE7v8jR+//I0nw/yNK9P8hPNT/ExJ0/x4Z + sv8nIOf/Jh/f/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jhzf/yUn4/8jR+//I0jw/yNH8P8jSfD/I0Xu/yUu + 5f8mHd//Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH9//Jh7f/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 5P8bF5r/BgRt/21zrP/29/D/6urq/+rq6v/q6ur/6unp/+rq6v/q6ur/6urq/+rq6v/q6ur/6unp/+rp + 6f/q6ur/6enp/+/v7P/e3t7/O1DM/xxB8v8jSPDeI0jwFCNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI + 8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI8AAjSPAAI0jwACNI + 8AAjSPAAI0jwACYhwAAmIcAAJiHAACYhwAAmIMAAJiDAACYgwAAlIMAAJiHAADQzxwAfF7wAHhe8AB0W + uwAcFroAHhe8ACEavQAfGL0AIBm9ACYfwAAmH74AI0jwACNI8AAjR+8AI0bvACNG7wAjR+8AI0fvACNG + 7wAjR/AAI0fwACNG73gjR+/9I0bv/yNG8P8jR/D/I0fw/yNG7/8jR/D/I0fw/yNH8P8jR/D/I0fv/yNH + 8P8jR+//I0fv/yNH7/8jR/D/I0fw/yNH8P8jR+//I0fv/yNH7/8jR/D/I0fv/yNH7/8jR+//I0fv/yNH + 7/8jRu//I0fv/yNG7/8jR/D/I0fw/yNH8P8jR/D/I0bv/yNH7/8jRu//Ikbx/yJH8v8iR/L/IT/x/yMy + 6v8lJeT/Jh3f/yYc3/8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/f/yYe4P8mH+D/Jh/g/yYf + 4P8lHt//Jh7f/yQb4P8vL+H/SFbl/x0T3/8vL+L/2eP6/3J57P8bE97/Jh7g/yEZ3/8xMuL/2N/6//// + ////////8vb+//P0+f/HyMj/yMfB/7K3x/8vMt//Ixnh/yYf4P8eFt7/Ojzl/6uz0v/JyMH/zMvD/46h + 4f98gu//IBff/yUe3/8mH+D/Jh/g/yAX4P9BRdv/v8PF/8bFxP/NzsL/b3HT/xUN4P9RSeb/d3Hr/xgO + 3f8cEt3/U1Pm//////////7/9/n9//H1/P/4+P3/2+H6/6218/9yeOv/MzTi/x8X3v8mHd//HRbf/yEd + 3/8mI9//IBnf/yYd3/8mHt//Jh7f/yYd3/8kG9//QUbk/yIe3/9WWuj/nqXx/5ih8f84MuL/Ihbe/yUb + 3v8kGd//Jhve/yYc3/8mHN7/Jhvf/yYd3/8mHt//Jh/g/yYg4P8mIuH/Jijj/yUy5/8lQPH/IDfJ/xQS + dP8gGrn/Jx/m/yYf3/8mH+D/Jh/g/yYe4P8mHt//Jh/g/yYc3v8lKOT/I0rx/yNI8P8jSfD/JD/s/yYn + 4/8mHd//Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe + 4v8lH9j/ExCC/xwefv/Q1eP/9/by/+np6f/q6ur/6unq/+rp6f/q6ur/6urq/+rq6v/q6ur/6urq/+np + 6f/q6er/6urq/+rq6v/q6er/9vPr/5up4/8ZPu7/I0fw/yNH71kjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAmIcAAJiHAACYhwAAmIcAAJiDAACYgwAAmIMAAJSDAACYhwAA0M8cAHxe8AB4X + vAAdFrsAHBa6AB4XvAAhGr0AHxi9ACAZvQAmH8AAJh++ACNI8AAjSPAAI0fvACNG7wAjRu8AI0fvACNH + 7wAjRu8AI0fwDSNH8KIjR/D/I0bv/yNG7/8jR/D/I0fw/yNH7/8jRu//I0bv/yNH7/8jR/D/I0bw/yNH + 7/8jR/D/I0fv/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fv/yNH8P8jR+//I0fw/yNH8P8jR/D/I0fw/yNH + 8P8jRu//I0fw/yNH7/8jR+//I0bv/yNH8P8jR/D/I0fv/yNH7/8iRvL/H0T2/yFG7/8lQOT/KDLX/y4y + xv8sKcr/JR3e/yUd4v8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mHuD/Jh/g/yYf3/8mH9//Jh/g/yYe + 4P8mHuD/JyDg/yYf3/8lHuD/KCDg/1Fb5/8lHt//GA/d/2Np6v+jpfP/HRXf/yYe4P8mH+D/HRXe/0RH + 5f/T2fn////////////n7/v/p6/N/8rIwf/Dx8X/Skna/x4V4v8mH+D/JiDg/xoR4P9FSdr/xMfF/8zL + wv+fpsz/h5Dv/yIc4P8lHuD/Jh/g/yYf4P8kHOD/KSXf/6ywyP/IyMP/y8rC/4yVzv8cFeD/Q0Dl/11g + 6P8cFN7/Ixvf/zAs4f9+gO3/wMj3//////++y/b/Mzbi/yIc3/8dGt//GxPe/yIa3/8mHuD/Jh7g/yYe + 4P8lHeD/JB3g/yYd4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yQd4P8mHuD/HhXf/xoW3v8aFt7/Ixvf/ycg + 4P8mH+D/Jh7g/yYf3/8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh7g/yYd3/8mHN//JyHl/yAj + vf8VE3X/IRu+/ycf5v8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8lHd//JSPh/yQ96/8kP+z/JTHm/yYg + 4P8mHeD/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mHt//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe + 3/8nH+T/IhzJ/wwKdf9BRY//sbOw/+Df3//v8O//6enp/+zs7P/t7e3/7u7u/+/v7//u7u7/7e3t/+rq + 6v/q6ur/6urq/+rp6v/p6un/6unq/+7t6v/a4Ov/NVnv/x5C8P8jR/CoI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAJiHAACYhwAAmIcAAJiHAACYgwAAmIMAAJiDAACUgwAAmIcAANDPHAB8X + vAAeF7wAHRa7ABwWugAeF7wAIRq9AB8YvQAgGb0AJh/AACYfvgAjSPAAI0jwACNH7wAjRu8AI0bvACNH + 7wAjR+8AI0bvHSNH78AjR/D/I0fw/yNG7/8jR+//I0fv/yNG7/8jRu//I0fw/yNH8P8jRu//I0bv/yNG + 7/8jR+//I0fv/yNH7/8jRu//I0fv/yNG8P8jR/D/I0fv/yNH7/8jRvD/I0fw/yNG7/8jR/D/I0fw/yNH + 8P8jRu//I0bv/yNH8P8jR+//I0fv/yNG7/8jR+//I0fv/yJH8f8fRff/JUjm/z9Ws/9baJX/b3GL/3x7 + if+HiJH/goSK/2Bljf8tLNH/JB3j/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mH9//Ixnf/zk84/87Q+P/Ihjf/yQb4P9EU+b/MjLi/yIZ3/8qJOD/c4Ds/yYi4P8lHd//Jh7g/yYf + 4P8eFt//Li7h/21x6/+Hhe7/S1Ho/4KGzv/OzsL/zc3C/2Vq1f8cFeL/JR7g/yYf4P8lHeD/HRrh/46U + zv/OzcL/ysrD/5iq4/8nJ+P/JBvf/yYf4P8mHuD/JR3g/yAc4f+Lk87/zczC/8jIw/+usMj/JCHf/0A9 + 5P9bXuj/HRTf/yYf4P8kHeD/Fw7d/x8c3/9rcer/4ej7/7a/9f8rKOH/HxXf/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mHuD/Jh7g/yYf4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8lHt//Jh7g/yYf4P8mH+D/Jh/f/yYe4P8mHuD/Jh/g/yYf4P8mH+D/JR7f/ycf + 5f8fGrP/FBNz/yEcv/8nH+b/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/JiDg/yYd + 3/8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yYe + 4P8mH+D/Jx/l/yAatf8UFHj/FBg6/wICAf9KSkv/7ezt//38/f/t7O3/3t7e/9jY2P/W1tb/2tra/+Pj + 4//09PT/9/f3//Hx8f/u7e3/6urq/+np6v/r6un/7u7q/1x47v8XPfD/I0fw4SNH8BQjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACYhwAAmIcAAJiHAACYhwAAmIMAAJiDAACYgwAAlIMAAJiHAADQz + xwAfF7wAHhe8AB0WuwAcFroAHhe8ACEavQAfGL0AIBm9ACYfwAAmH74AI0jwACNI8AAjR+8AI0bvACNG + 7wAjR+8AI0fvKCNG79QjR+//I0fv/yNG7/8jR+//I0bv/yNH8P8jR/D/I0bv/yNG8P8jR/D/I0fw/yNG + 7/8jR/D/I0fw/yNG7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH8P8jR+//I0fv/yNG7/8jR+//I0fv/yNG + 7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0jw/yFF9P8hRPH/OlK4/2txhv+Zl5b/sbC1/7i4 + wv+4ucX/urrI/76+zP+eoJf/QEKr/yIa5v8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYe + 4P8mH+D/Jh7g/yUc4P8rKOD/X27p/yQd4P8eE9//QEXk/zk64/8hF97/JSDg/0dR5v8kG+D/JR7f/yYe + 4P8mH+D/Jh/g/yMa4P8aEt7/Fw/e/xUL4P9nbdX/y83C/8zMwv+GjtD/Hhbh/yYe4P8mHt//Jh/f/x0V + 4v9JTdr/xsnE/8zKwf+vt8//LSzi/yMa3/8mH+D/Jh7f/yYe4P8cFeH/amzU/8zNwv/HxsP/vcHF/zo+ + 2/89OeX/XF/p/x0U3/8mH9//Jh/g/yYf4P8kG+D/GBDe/zc54v/Hz/j/vsj3/yYk4P8iGd//Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mHt//Jh/g/yYe4P8mHuD/Jh7f/yYe + 3/8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mHuD/Jh7g/yUe + 3/8nH+b/Hxuz/xUUeP8iHcf/Jx/l/yYe4P8mH+D/Jh7f/yYe4P8mH+D/Jh7f/yYe3/8lHt//JR/g/yYe + 3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/ycf5v8eGan/EhJh/wABBP8AAAD/AAAA/09QUP+Dg4T/TEtM/yIiI/8QEBD/DAwM/xgY + GP8yMjL/Y2Nj/42Njv/AwMD/4uHi//X09f/v7+//6urq//bz6f98kez/Fjzw/yNH7/ojR+9EI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAmIcAAJiHAACYhwAAmIcAAJiDAACYgwAAmIMAAJSDAACYh + wAA0M8cAHxe8AB4XvAAdFrsAHBa6AB4XvAAhGr0AHxi9ACAZvQAmH8AAJh++ACNI8AAjSPAAI0fvACNG + 7wAjRu8AI0fvOSNH7+MjR/D/I0fw/yNG7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jRu//I0fv/yNH + 8P8jR/D/I0fw/yNH7/8jRvD/I0fw/yNH7/8jR+//I0fv/yNH7/8jR/D/I0bv/yNH7/8jR+//I0bv/yNG + 7/8jR/D/I0fw/yNG7/8jR+//I0fv/yNG7/8jSO//I0nx/x9B9v8nPt7/UVuR/42Mif+zs7v/vL3N/7i5 + yf+3uMf/t7jH/7a4x/+7vMz/kpSU/zc5t/8iGuT/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh7g/yYf4P8mH+D/HxXf/1de5/9yfez/Ihvf/xwX3/8cFt7/HRje/11m6f9IUeb/Ihjf/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8gGOH/SlDZ/8jJw//My8P/p6rK/yQi4P8kHOD/Jh/f/yYf + 4P8kHN//IR3h/5WczP/OzcL/xcfE/01P2v8eFuH/Jh/g/yYf4P8mH+D/IBfh/0VL2//CxcT/xsXD/8vM + w/9cW9X/MC7l/0xQ5v8fFt//Jh7g/yYf4P8mH+D/Jh7g/yYf4P8eFN7/Hx3f/6659P+dp/H/HBbf/yUd + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/f/yYf4P8mH+D/Jh/g/yYe + 3/8mHt//Jh/g/yUf3/8lHt//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8mH+D/Jx/m/x8btP8VFHf/Ih3I/ycf5P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH9//Jh/g/yUe + 3/8mH+D/Jh/g/yYe4P8mHd//Jh7f/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe3/8nH+b/Gxii/w8SSP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/BgYG/ykpKv93dnf/z8/P/+zs7P/08ur/lqvs/xpA8P8jRu//I0fvbCNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AJiHAACYhwAAmIcAAJiHAACYgwAAmIMAAJiDAACUg + wAAmIcAANDPHAB8XvAAeF7wAHRa7ABwWugAeF7wAIRq9AB8YvQAgGb0AJh/AACYfvgAjSPAAI0jwACNH + 7wAjRu8AI0bvOSNH7+YjR+//I0fw/yNH8P8jR+//I0bv/yNH7/8jR+//I0fv/yNH7/8jR+//I0bw/yRH + 7/8kR+//I0bw/yNG7/8jR+//I0fw/yNH8P8jR+//I0fv/yNH8P8jR/D/I0fw/yNH7/8jRu//I0bv/yNG + 8P8jRu//I0fw/yNH8P8jR+//I0fw/yNH7/8jSfD/I0Xv/yAz8f8vN8f/ZWeA/6Ggnf+7vMv/ubrK/7a3 + xv+4ucn/ubrJ/7i5yP+3uMj/trfA/25yhv8oJdb/JR3i/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yUd4P8fF9//bnns/6az8/+Hje//g4nu/7O79f+Kl+//JR/f/yUc + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Ihnh/zcz3f+8v8b/y8nD/7i+x/85O93/IBfg/yYf + 4P8mH9//Jh/f/xwT4f9QVNn/x8rD/8/Owf+Wnc3/Ih3g/yQd4P8mH+D/Jh/g/yQb4P8uLN7/s7bI/8fH + w//NzMP/eoHQ/y0u5P9HVOX/Ihjf/yYe4P8mH+D/Jh/g/yYf3/8mH9//Jh/g/yAX3/8kIuD/ws33/11i + 6P8dFN//Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/Jh/g/yYf4P8lH+D/JR7f/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh7f/yYf3/8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/ycf5f8eGq//FRR3/yIdyP8mH+T/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/JR7f/yYf + 4P8mH+D/Jh7f/yYc3/8jHOD/ISLi/yYj4f8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh7f/yYe + 3/8mHt//Jh/g/yYf4P8mH+H/Jx/i/xsXm/8PEkP/AAEA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/2trbP/s7Oz/8/Lr/6y76v8dQu//Ikbv/yNH + 8JAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH + 8AAjR/AAI0fwACNH8AAjR/AAI0fwACNH8AAjR/AAI0fwACYhwAAmIcAAJiHAACYhwAAmIMAAJiDAACYg + wAAlIMAAJiHAADQzxwAfF7wAHhe8AB0WuwAcFroAHhe8ACEavQAfGL0AIBm9ACYfwAAmH74AI0jwACNI + 8AAjR+8AI0bvNSNG7+YjR+//I0fv/yNH7/8jR+//I0fv/yNG7/8jR+//I0fv/yNH7/8jRu//I0fv/yNH + 8P8jR+//I0fw/yNH8P8jRvD/I0fw/yNG7/8jR/D/I0fv/yNH7/8jR/D/I0fv/yNG7/8jR+//I0fv/yNH + 7/8jR/D/I0fv/yNH7/8jR+//I0fw/yNJ8f8jRu7/JDfq/yIh6f83N7X/dXd+/62tsf+8vc3/t7jH/7a3 + xv+5usr/pKWw/4SEif+ur7z/vL3O/6Kjof9JS53/IRvl/yYf4f8mH+D/Jh/g/yYe4P8mHt//Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe3/8mH+D/JB3f/xwT3v9EROT/g4nt/5Sh8P9pbOr/Ix3f/yMb + 4P8mH+D/Jh7g/yYf3/8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yQa4P8pKOD/oqzL/8zKwv/KzMP/UlLY/xwU + 4f8mH9//Jh7g/yYf4P8kHOD/Ix/h/56ky//NzML/yMrE/0tP2v8fFuH/Jh/g/yYf4P8lHeD/Ih7h/5CY + zv/MysL/ycnD/5mey/8yMuP/YHfq/yQc3/8mHuD/Jh/g/yYf4P8mH9//JR/f/yUe4P8mH+D/GA7e/2ds + 6v+iq/L/HRXe/yUd3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/f/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYe4P8mHuD/Jh7f/yYf4P8mHt//Jh7g/yYe + 3/8mH+D/Jh7f/yYf4P8nH+b/Hhqx/xUTdf8iHML/Jh/l/yYe4P8mH+D/Jh/g/yYe4P8mHt//JR/g/yUf + 3/8mH+D/Jh7f/yYf4P8gKOT/Mkrr/0Jk8/8kPev/JR3f/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf + 3/8mH+D/Jh/g/yYf4P8mHuD/Jh/h/yYf3v8aF5b/EBNO/wABAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/xQUFP/j4+P/7e3t//Hw6v/Gy+r/IUHu/yFG + 8P8jR/CzI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAmIcAAJiHAACYhwAAmIcAAJiDAACYg + wAAmIMAAJSDAACYhwAA0M8cAHxe8AB4XvAAdFrsAHBa6AB4XvAAhGr0AHxi9ACAZvQAmH8AAJh++ACNI + 8AAjSPAAI0fvGyNH79ojR+//I0fw/yNH7/8jR/D/I0fv/yNG7/8jR+//I0bv/yNG7/8jR/D/I0bv/yNH + 7/8jR/D/I0fv/yNG7/8jR+//I0bv/yNH8P8jRu//I0fw/yNH8P8jR+//I0fw/yNH8P8jRu//I0bv/yNG + 8P8jR+//I0fv/yNH7/8jR+//I0nw/yNG7/8lOOn/JCTj/yEX5v8+Pqr/gIJ//7S1vP+6u8z/trfG/7a4 + xv+2t8b/u7zM/4eIjv9cXFn/mZqk/72+zP9/goj/LS3H/yQb5P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf3/8mH+D/IBff/xkQ3v8YEt7/GxLe/yUd + 4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHeD/Hhri/46Tzv/PzsL/zc3D/3J7 + 0/8dFeL/Jh7f/yYe4P8mHuD/Jh/g/xsT4f9UWNj/yMrE/8/Owv+NlM3/IBvh/yUd4P8mH+D/Jh7g/xwV + 4v9ydNP/zs7C/8jIw/+ytcb/PEXg/52r8/8rJ+H/JBvg/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yIY + 3/83OOP/qbrz/yko4P8kG+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7f/yYe3/8mH+D/JR/g/yUe3/8mH+D/Jh7g/yYf + 4P8mH+D/Jh7f/yYe3/8mH+D/Jx/m/yAbtP8UE3P/IRu9/ycf5v8mH+D/Jh7g/yYf4P8mH9//Jh/g/yUe + 4P8mH+D/Jh3g/yYk4v8cOOv/OV7y/4yd9/+Pofj/LkHq/yIa3/8mHt//Jh/g/yYe4P8mH9//Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4f8mH+H/GheZ/w8TTv8AAQD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8EBAT/TU1O/2BgYP8QEBD/AAAA/wAAAP8MDAz/w8PE//Dw8P/w7+r/0NDn/yNA + 6P8hRvH/I0fvySNG7wQjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG + 7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AI0bvACNG7wAjRu8AJiHAACYhwAAmIcAAJiHAACYg + wAAmIMAAJiDAACUgwAAmIcAANDPHAB8XvAAeF7wAHRa7ABwWugAeF7wAIRq9AB8YvQAgGb0AJh/AACYf + vgAjSPAAI0jwBSNH764jR+//I0fv/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0fw/yNG8P8jR/D/I0fw/yNG + 7/8jRu//I0fv/yNG7/8jR/D/I0fv/yNG8P8jRvD/I0fv/yNH8P8jR/D/I0fw/yNG7/8jR/D/I0fv/yNH + 7/8jRvD/I0bv/yNH8P8jSfH/I0Xu/yQ36f8mJeH/JRrh/yIb4/9DRaH/hYiD/7i4w/+5usv/trfG/7a4 + x/+2uMf/trfG/7q7y/+fn6r/eXl8/62uvP+zs7j/XWGP/yQe3/8lHeH/Jh7g/yYe4P8mHt//Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH9//JR7f/yYe3/8mHt//Jh/f/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe3/8mH+D/Jh/g/x0V4v90d9P/zc/D/8zM + w/+Vmc3/Hxnh/yUe4P8mH+D/Jh7f/yYe4P8jG9//IyLg/6Oqyv/NzML/xMfE/0VI2/8fFuH/Jh/g/yYe + 4P8gF+H/TlTZ/8XIxP/HxsP/wMPE/1Ja2v/AzPn/Z27q/xUM3v8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8XDd3/U1bm/7S+9f8iIN//JRzg/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mHuD/Jh/g/yYe + 3/8lH9//Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mH9//Jh7g/yYe4P8mHuD/Jh7g/yYf + 4P8lHt//Jh/g/yYe3/8mHt//Jh7g/ycf5f8fG7P/FBJy/yAbvP8nIOb/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHt//Jh7f/yYc3/8eM+j/Ol/z/42c9/+dpvf/jJ33/zI55v8iGt//Jh/g/yYf4P8mH+D/Jh/f/yYf + 4P8kG9//Ixrf/yYf4P8mH+D/Jh/g/yYf4P8mH+D/JyDm/xsXof8PEkz/AAEA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wcHB/9FRUX/sLCw//T09P/5+fn/wsLC/zMzNf8AAAD/AAAA/3d3eP/19PX/8fDs/9fb + 6v8sUOn/IETx/yNH780jR+8FI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACYhwAAmIcAAJiHAACYh + wAAmIMAAJiDAACYgwAAlIMAAJiHAADQzxwAfF7wAHhe8AB0WuwAcFroAHhe8ACEavQAfGL0AIBm9ACYf + wAAmH74AI0v0ACNL9EkjRu//I0bv/yNH7/8jR/D/I0fw/yNG7/8jR+//I0fv/yNH7/8jR/D/I0fw/yNH + 7/8jR+//I0fv/yNH8P8jR+//I0bw/yNG8P8jRu//I0fv/yNH7/8jR+//I0fw/yNH8P8jR/D/I0fv/yNH + 7/8jR+//I0jw/yNJ8P8jRe7/JTbn/yYl4f8mHN7/JRvh/yIc5P9FSJ7/jY+H/7m5xv+4ucr/trfG/7a4 + x/+2t8f/trfH/7a3x/+2t8b/uLnI/7m6yf+8vc3/pael/0pIov8hGOf/Jh7g/yYf4P8lH9//JR/g/yYe + 3/8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/f/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/JR7f/yYf4P8fF+H/VFzY/8rL + w//LysP/rbPI/yos4P8jGuD/Jh/f/yUf3/8lHt//Jh/f/xsU4f9eZNf/yszD/8/Owv+Mk83/IBvh/yUd + 4P8mHuD/Ixvg/zEw3v+3ucb/x8bD/83Mwv9hadL/o6v2/+Xp+/89P+P/Fgze/x8X3/8iGd//IRjf/xwU + 3v8WD93/Mzbi/8rV+P+GjO//GxLe/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/JR7g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh7f/yYf4P8lH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/JR/f/yYe4P8mH+D/Jh/g/yYe4P8nH+X/IBu3/xMSb/8eGrT/JyDo/yYe4P8mH+D/Jh/g/yYe + 3/8mHuD/Jh/f/yYe4P8iGd7/N0vr/4yd9/+apPf/l6L3/4ia9/8vNOT/Ixrf/yYf4P8mH+D/Jh7f/yYf + 4P8kG9//ODvk/zo+5P8iGt//Jh/g/yYe4P8mH+D/Jh/g/ycg5v8dGan/DxBX/wAAAf8AAAD/AAAA/wAA + AP8AAAD/AAAA/ycnKP+5ubr/9fX1//b29v/r6+v/6urq//f29//j4uP/Xl5e/wAAAP+2trj//v3+//b1 + 8v/Jztn/L1Lj/x9D8v8jR+/MI0fvBSNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAmIcAAJiHAACYh + wAAmIcAAJiDAACYgwAAmIMAAJSDAACYhwAA0M8cAHxe8Bh4XvBsdFrsuHBa6OB4XvEshGr1VHxi9TSAZ + vTcmH8AmJh++DiomvwAhRvGUI0nz/yNI8f8jSPH/I0jx/yNH8P8jR/D/I0fw/yNH8P8jR/D/I0fv/yNG + 7/8jR/D/I0fw/yNH7/8jR+//I0fw/yNG7/8jR+//I0fv/yNG7/8jR+//I0fw/yNH7/8jR+//I0fw/yNH + 8P8jSfH/I0nw/yRD7v8kNOf/JiTh/yYc3/8mHd//JBzh/yEc5f9ER57/jZCJ/7q7yP+4ucn/trfG/7a3 + x/+2t8b/trfG/7a3xv+2t8b/trfG/7a3xv+2t8b/urvM/6eop/9MS6D/IRnm/yYe4P8mH+D/Jh/g/yYf + 4P8mH9//Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 3/8lHt//JR/g/yYf4P8mHuD/Jh/g/yYe4P8mHt//Jh7g/yYf4P8mH+D/Jh/g/yYe3/8mHuD/IBjh/z89 + 3P/ExcT/yMfD/7/ExP9CQNv/Hhbh/yYf3/8lH9//JR/f/yYf4P8jG+D/JiXg/6qvyf/Ny8L/w8fD/0RI + 2/8gFuH/Jh7g/yQd4P8kIOD/mKDM/8zLwv/MzMP/gonO/05W6P/8////5en8/3V77f83NuP/Li/h/zM0 + 4v9ISOX/g47u/+bt/P/H0vf/Jibf/yMa3/8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yUe3/8lHt//JR/g/yUe3/8mH+D/Jh7g/yYf + 4P8mH+D/Jh7g/yYf4P8mH9//Jh/g/yYf4P8mHuD/Jx/l/yIcv/8UE3L/Hhmq/ycf5f8mH+D/Jh/g/yYf + 4P8mH9//Jh7g/yYf4P8mH+D/Hxfe/1Ve6v+fq/j/lKD3/5mj9/+AlPb/KjDk/yQc3/8mH+D/Jh/g/yYe + 4P8mH+D/IBfe/1dd6f94iPH/Ih7f/yUe3/8mH+D/Jh/g/yYe4P8nH+X/IRy4/w8OXf8AAAD/AAAA/wAA + AP8AAAD/AAAA/xAQEP/CwsP//fz8/+vr6//p6en/6urq/+np6f/p6en/8fHx//T09P9oaGn/R0dI/8jI + yP9/fnv/foOO/ztd7v8fQ/H/I0fvzCNH7wUjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AJiHAACYh + wAAmIcAAJiHAACYgwAomIMAmJiDAPiUgwGEmIcCONDPHrUhO0cJVX9jcYWrd9WNs3vxjbd7/XGTb/1Nc + 1/9ARc37JiDA6yUfwM0qJr+1JjHN7h8u1P8eMdr/HTfh/x075f8ePur/IEPv/yFF8f8iR/H/I0ny/yNJ + 8v8jSPH/I0fw/yNH8P8jR+//I0fw/yNH7/8jRu//I0fw/yNH7/8jRu//I0bv/yNG7/8jR/D/I0jw/yNJ + 8f8jSfD/JEHs/yUy5v8mIuH/Jhzf/yYd3/8mH+D/JB3h/yEc5f9ESJ7/jY+J/7m6yP+4ucn/trfG/7a3 + xv+2t8f/trjH/7a3x/+2t8b/trfG/7a3xv+2t8b/trfG/7i5yf+ys7j/YWaN/yUg3f8lHeD/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh7f/yYf4P8mH9//Jh7g/yYe4P8lHt//Jh/g/yYe + 4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yIa + 4P8uLd7/qrPI/8vJw//OzcL/XmPW/xsV4f8mH9//Jh7g/yYf4P8mH+D/Jh/g/xsU4v9mbdX/zM3C/8/O + wv+GjM7/HRjh/yYd4P8mHuD/HRbh/3p70f/OzsL/ycnD/6uuyf8cHN7/fITt//7/////////7PP9/9bg + +v/i6vz///////////+8xvX/MDPh/x4V3v8mH+D/Jh7g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYe4P8mHuD/JR7f/yYf4P8mHuD/Jh/g/yYe + 4P8mHt//Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/ycf5P8iHMX/FBN1/x0Zn/8nH+P/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/f/yQc3/8lJOH/a4Hz/5ql+P+Yo/f/gpb2/yow5P8jG9//Jh/g/yYf + 4P8mH9//Jh7g/yAX3/9RVuj/l6f3/zs95P8hGN//Jh/g/yYe4P8mH+D/Jx/l/yMczP8SEmX/AwQF/wAA + AP8AAAD/AAAA/wAAAP96env/+vr6/+rq6v/q6en/6erq/+np6f/q6ur/6erq/+np6f/v7+//6Ojo/yEh + If8EBAT/AAAA/4uNnP8yUOj/IEXz/yNH780jR+8FI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH + 7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACNH7wAjR+8AI0fvACYh + wAAmIcAAJiHAbSYhwK0mIcDJJiDA6yYgwP0iHL7/NTPH/5Gf9f+dqfv/nqv8/6Ct/f+ir/3/oq/+/6Ct + /f+irv7/mab4/zY0x/8fGL3/d4Ho/4OO7P90euP/Ymja/1BY1P9FStH/O0DQ/zE70P8oN9P/ITLW/x4z + 3P8eOeT/HTvn/x0/6/8fRPD/IUbx/yNJ8v8jSPL/JEjx/yNH8P8kR+//JEfv/yNH7/8jSfD/I0nx/yNG + 7/8lO+v/JSvk/yYg4P8lHN7/JR7f/yYf4P8mHuD/JR3h/yEc5P9ER5//jY+J/7m6yP+4ucn/trfG/7a3 + xv+2t8f/trfH/7a3xv+2t8b/trfH/7a3x/+2t8b/trfG/7a3x/+2t8f/urvJ/4GDiP8sK8v/JBvi/yYe + 4P8mHuD/Jh/g/yYf4P8mH+D/JR7f/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYe3/8mH+D/Jh/g/yYf + 4P8mH+D/JR/g/yYe4P8mH+D/Jh/g/yUf4P8mHuD/Jh/g/yYe4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8kHOD/Ih/g/5edzP/OzML/zMzC/36G0f8dFuH/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8jGuD/Kirg/62y + yP/MysL/vsLF/zw+3P8hGOH/Jh/g/x8W4f9SWNj/x8nD/8jHw/+8wcb/Oz7c/xYO3/9aYej/vML2/+Xt + /P/5/v7/5/D8/8PJ9/9wd+r/IyDg/x4W3/8nH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/JR7f/yUf + 4P8mH+D/Jh7f/yYe3/8mH9//Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mHuD/JR/g/yYf4P8mH9//Jh/g/yYe + 4P8mHt//Jh7f/yUe3/8mH+D/Jh7g/yYe3/8mH+D/Jh/g/yYf4P8mH+P/JB3P/xQSeP8aFpL/Jh/i/yYf + 4f8lH9//JR7g/yYe4P8mHuD/Jh/g/yUe3/8mH+D/Ihjf/yg+6v+Fl/f/mqT3/5Oi9/82POX/IRje/yYf + 4P8mH+D/Jh/g/yYe4P8gF97/UVXo/5+t+f9gauz/Hxfe/yYe3/8mH+D/JR/f/yYe4/8lHtr/Fxd3/wUI + C/8AAAD/AAAA/wAAAP8jIyP/4eHh//Dw8P/p6en/6urp/+rq6v/q6ur/6urq/+rq6v/q6er/7u7u/9vb + 2/8ZGRr/AAAA/xQUD/+srL3/JkLe/yFH9P8jRu/HI0fvAyNH7wAjR+8AI0fvACNH7wAjR+8AJR7gACQd + 4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAmIMAAJiDAACYgwMcmIMD/JiHA/yYgwP8mIMD/Ixy+/y4sxf+MmfP/laD3/4CN7v92gen/cHfn/25z + 5f9weef/fYnr/4mY8P85N8j/HBW5/3eB5/+ms/7/oa79/6Gv/f+hrv3/oq78/5un+P+Pm/L/hZDt/3yD + 6P9tcuD/W2ba/0tY1/8/R9X/MT7U/yQ31v8eM9z/Hjnj/x4/6v8iR/H/JEny/yNJ8P8jR+//JD7s/yUx + 5v8lJeH/Jh3f/yYc3/8lHt//Jh/f/yYf3/8mH9//Jh7h/yIb5/9ERqL/jY+I/7q6x/+4ucn/trfG/7a4 + xv+2t8b/trfG/7a4x/+3uMb/trfH/7a3x/+2t8f/trfG/7a3x/+2t8f/trjG/7u8zP+XmZf/Oj2v/x8W + 6/8mHuH/Jh/g/yYf4P8mH+H/JR7j/yMe5v8jHeb/JB7l/yQe4/8mHuH/Jh7g/yYe4P8mH9//Jh7f/yYe + 4P8mHuD/Jh7g/yUf3/8mHuD/Jh/g/yYf3/8mH+D/Jh7g/yYe4P8mH+D/JR/f/yYe4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh7g/xwU4v9+gNH/zc7C/8zLwv+fo8r/IR7h/yUd4P8mHt//Jh7f/yYf4P8mH9//Jh/f/xoT + 4v9tdNP/zc3C/8/Pwv+CidD/Hhji/yUe4P8iGuD/Nzfc/77Axf/HxsP/ysvD/1pb1/8bEuL/HBLe/x4b + 3/81OOL/R0jl/zc64/8fHd//GRHe/yMb4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe3/8mHt//Jh7g/yUe + 3/8lHt//Jh/g/yYe3/8mH+D/Jh7g/yYe3/8mH9//JR/f/yYe4P8mH+D/Jh/g/yUe4P8mH9//Jh/g/yUf + 3/8mH+D/Jh/g/yYf3/8lHt//Jh7g/yYf4P8mH+D/Jh/g/yUe4P8mH+D/Jh/i/yQe2P8VFH//FxSF/yUf + 3f8mH+H/Jh/g/yYe4P8mH+D/Jh/g/yUf3/8mH+D/Jh/g/yYc3/8eJeT/bYX1/5uk9/+bpvj/aXTu/yAZ + 3v8lHeD/Jh7f/yYe4P8mHuD/IBjf/05T6P+cqfj/hJH0/ygk4P8kHN//Jh7g/yYe4P8mH+H/Jx/l/xsZ + l/8FBxb/AAAA/wAAAP8AAAD/iomK//n5+f/q6en/6urq/+rp6v/q6ur/6urq/+np6f/p6en/6urp//b2 + 9v+cnZ7/AAAA/wAAAP8vLin/rrPQ/x451v8iR/P/I0jvqyNH7wAjR+8AI0fvACUd4AAhGd8AHhXeACUe + 4AAkHeAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJiDAACYgwAAmIMDGJiHA/yYfwP8fGL3/Gxa9/x0Xvf8bFrz/OUHL/0NLzv86Qcn/Oz/H/0E+ + yf9PSc3/T03N/y4yyP8qKcn/JyLG/yMcxP8yMsz/UVnY/11m2/9mb93/cXjj/3d+5/97hen/go/t/4uZ + 8v+bqPr/oa79/6Gu/f+gq/v/mqT3/4uW7/+Aiuv/bnXh/1Rd1/88RNH/JTDQ/yQ64v8lNOj/Jibj/yYe + 4P8mHN//Jh3f/yUe3/8mHuD/JR7f/yYe4P8mH+D/Jh/g/yIb5v88Pqz/h4qF/7m6x/+4usn/trfG/7a3 + xv+2t8b/trfH/7a3x/+2t8b/t7jH/7e3x/+2t8b/trfG/7a3xv+2t8f/trfH/7a3x/+5usr/ra6z/11g + hv8pKcr/Ihrl/yId6v8iHuv/Ix7o/ykg2f8vIsv/MCLJ/y8iy/8qIdn/JB7m/yMe6v8iH+v/Ih7q/yMe + 5f8lHuH/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHt//Jh7f/yUe + 4P8mH+D/Jh/g/yYe4P8eFuH/YGnW/8vMw//JyMP/s7rH/zM03v8iGOD/Jh/g/yYe4P8mH+D/Jh7f/yYf + 4P8iGeD/MDHe/7K4x//LysP/vsLF/zw+3P8gGOD/JB3g/ygj4P+fp8v/y8nC/83Mwv92gdL/HRfi/yYe + 4P8lHd//IBff/x0U3v8fF97/JR3g/yYf4P8mHt//Jh7f/yYf4P8mHuD/JR7f/yUe3/8mHuD/JR7g/yYe + 4P8lH9//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8lHt//JR7f/yUe3/8lH+D/JR/f/yYf4P8mH+D/Jh7f/yUf + 3/8mHuD/Jh7f/yUe3/8mH9//Jh/f/yYe4P8mH+D/Jh7g/yYf3/8mHuD/Jh/g/yYe4f8mHtz/GBaK/xYU + fP8kHtH/Jh7j/yYf4P8mH+D/Jh/g/yYf4P8lHt//Jh/f/yYf4P8lHN//ICDh/2+C8/+bpfj/laD3/56r + +f9XX+r/IRrf/yUe3/8mH+D/Jh7g/yEY3/9DSeX/laP3/5il+P9ESOb/Hxfe/yYf3/8mHuD/JR7f/ycf + 5v8hHL//DA00/wAAAP8AAAD/Njc4/+jn6P/t7e3/6enp/+np6f/p6en/6urq/+rp6f/q6un/6unq/+vr + 6//z8/T/T09P/wAAAP8AAAD/V1ZQ/5Weyv8YNtb/I0n0/yQ/7KEgEt0AIhjfAB4W3gAlHeAAIRnfAB4V + 3gAlHuAAJB3gACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJRzgACYhwAAmIcAAJiHAiCUgwPYkHr3/REPK/2512v+HjOL/oqLr/6qs7v+vtO//ub3z/8PG + 9v/Nzvr/2dr+/9/f/v90euz/GhHc/yUe3/8mH9//Ixve/yAY2f8gGNf/IBjT/yAYzv8gGMv/Ih/I/ycm + x/8uLcf/OTnK/05V0/9vd+L/gpDs/5Kf9f+dqvv/n6z8/6Kv/v+irf3/nKj4/0pPz/8gGLv/Jh3S/yYd + 4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yIa5v8yNb7/fH9//7e3wv+5usr/trfH/7a3 + x/+2t8f/trfG/7a3x/+2t8f/trfG/7a3x/+2t8f/trfH/7a3x/+2t8f/trfH/7a3xv+2t8b/trfH/7q7 + zP+lpqv/f4KM/1ZanP85LbD/SCSC/1UnWP9dKT//Yik1/2IpM/9iKTX/Xig//1cmTv9QJ23/RCWP/zUh + tf8uIsz/JB/k/yIe6v8iHuz/Ih7p/yQe4v8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/f/yYe + 4P8mHuD/Jh/g/yYf4P8mH9//IBjh/0VG2v/JycP/xsXD/8jKxP9MTNn/HhXh/yYf4P8lH9//Jh/g/yYe + 4P8mH+D/Jh/g/xsU4f92fNL/zc7C/87Nwv9/htD/HRjh/yUe4P8dGOL/f4LR/87Owv/LysP/m5/M/yAZ + 4f8lHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yUe + 4P8mHt//Jh/g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/JR7g/yUe3/8mH+D/Jh/g/yUe3/8mH+D/Jh/g/yYe + 4P8mHt//Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/f/yYe3/8mH+H/Jx/i/xsX + mv8TEnP/IhzC/ycf5v8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7f/x8c4P9acPD/n6r4/5ql + +P+KmPX/WWHr/yIc4P8lHuD/Jh/g/yYf4P8hGN//REnm/5Wi9/+cp/j/anTu/yAY3v8mH+D/Jh/g/yYe + 3/8mH+P/JR7Y/xYXcP8BAwf/AAAA/5+foP/5+fn/6enp/+np6f/q6er/6unq/+nq6f/q6ur/6unq/+np + 6f/19PT/wsLD/wkJCf8AAAD/AAAA/42Nhf+AiMX/FDTe/yNK8/8kLubyIBLdfCIY3wkeFt4AJR3gACEZ + 3wAeFd4AJR7gACQd4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJRzgACUc4AAmIcAAJiHAACYhwAQgGc48Sk7hj8vP+cbc2//12Nj+/9jY/v/X1/7/1dX9/9XU + /f/T0/z/0dH8/8/P+//b2/3/lZby/xwU3/8mHuH/Jh/g/yYf4P8mHuH/Jh/h/yYe4f8mH+L/Jh/i/yUd + 4f8kHN//Ixvc/yEZ2P8gGNP/IBnN/ykozP83Ncv/RUvO/1Zf1v9sdOH/fIfq/5Wj9f9ncd7/Hxm6/yYf + z/8mH+H/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yQd4v8nJNj/ZmmE/6+vtf+6u8v/trfH/7a3 + x/+2t8b/trfG/7a3xv+2t8b/trfH/7a4xv+2uMb/t7fG/7a3x/+2t8f/trfG/7a3xv+4ucj/u7zM/7q8 + y/+0t8b/r7PB/6Kmrf97fXb/aT0w/2glFv9pKRr/aCgc/2coH/9nKB//Zygf/2gpHf9pKRr/aSka/2ko + Gf9oKSL/YSk0/1gmTP9OJ3P/QCSX/zUiuv8pINz/Ih7r/yIe7P8kHuT/JR7f/yYe3/8mH+D/Jh7g/yYe + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yIZ4f80Md3/tbvH/8nHxP/NzcL/aHHV/x0V4f8mH9//Jh/f/yYe + 4P8mH+D/Jh/g/yYf4P8hGOH/Nzfe/7q+xv/LysP/uL3G/zU33f8iGeH/HxXh/1xh1//Jy8P/ycjD/7S4 + x/8vMN7/Ixrg/yYe4P8mH+D/Jh/f/yYe4P8mHuD/Jh7f/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf + 4P8mHt//Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yUe + 4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/ycg + 5v8eGav/ExFv/x4Zr/8nH+b/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/JR/f/yYe3/8kHt//KETt/01u + 8/9IaPP/Kjfm/yEY3/8mHuD/Jh/g/yYe3/8mHuD/IRjf/0FG5v+Uoff/mKP4/4qX9f8sK+H/JBvg/yYf + 4P8lHuD/Jh/g/yYg5f8cGKX/GR1L/wAAAP9sbG3/9/f3/+nq6v/p6en/6unq/+rp6v/q6er/6enp/+np + 6f/v7+//8fHx/0VFRv8AAAD/AAAA/wMDAv+pqqn/UFqz/xk96v8iR/D/KC3k/0JA5f8vL+LCHhbeUSUd + 4AchGd8AHhXeACUe4AAkHeAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJRzgACUc4AAlHOAAJiHAACYhwAAmIcAAIBnOAEpO4QDY2v4J0ND7Ls/P+1/Pz/ucz8/7yM/P + +/HPz/v/0dD7/9XU/P/S0vz/2tn9/6is9f8hH9//JBzf/yYf3/8lHt//Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/h/yYf4f8mH+H/Jh/i/yYe4f8kG+D/Ihrb/yAZ0/8gGM//HxjL/yUizf81MtX/Mi7W/yQe + 1f8mH9//Jh/h/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8iG+b/Rkmg/5ydmv+7vM3/trfG/7a3 + xv+2t8b/trfH/7a3x/+2t8b/trfH/7a3x/+3uMf/trjH/7a3xv+4ucn/uLnJ/7e4x/+7vMz/tLfH/5uf + qf+BgIX/dWpr/21aWf9nSUX/ZjYy/2UoI/9mKCT/ZSgk/2YpJP9lKCT/ZSgk/2YpJP9mKST/Zigk/2Uo + JP9lKCT/Zigi/2cpHv9oKRv/aSoa/2kpGP9nKSX/XShB/08mcP86I6j/KiDY/yIf7P8iHun/JR7h/yYf + 4P8mH9//Jh/g/yYf4P8mH9//Jh/g/yYf4P8kHOD/JSTh/56ny//Ny8L/zMzC/4uPzv8dFeH/JR7g/yYf + 3/8mH+D/Jh/g/yYf4P8mH+D/Jh7g/xwV4v98gdL/zM3C/87Owv92ftL/HBbh/yIZ4P87Ptz/wMLF/8jH + w//Ex8X/S0vZ/x0V4f8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYe + 4P8lH9//Jh/f/yUf4P8mH9//Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/f/yYe3/8mHt//Jh/g/yYe + 4P8nH+X/IRy+/xQSc/8bGJr/JyDj/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/f/yYe3/8mHt//Jh3f/yI9 + 7f8dRPD/HUPw/yIt5f8mHd//Jh/g/yYf4P8mHt//Jh/g/yMa3/83POP/kp/2/5ai9/+Zpvj/TlPo/x8X + 3v8mH+D/Jh/g/yYf3/8nH+X/HhjF/yYphP9SVl7/hISC//Tz8//q6ur/6unp/+rp6v/q6ur/6enp/+3t + 7f/5+fn/8PDw/29vcP8AAAD/AAAA/wAAAP8iIh//ubvI/yAuqP8hR/P/Hz/u/z1E5v+bqPj/i5n1/1Ja + 6f8hG97GIRnfVx4V3gAlHuAAJB3gACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJRzgACUc4AAlHOAAJRzgACYhwAAmIcAAJiHAACAZzgBKTuEA2Nr+ANDQ+wDPz/sAz8/7AM/P + +wvQ0Psoy8z7Vrq/+JOxt/bHwsX599va/f+ip/T/IR7f/yQc3/8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4f8mH+H/Jh7h/yYe4v8lHOH/Ihrf/yMb + 4P8mH+L/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8kHOP/KijQ/3R3hf+3t8L/t7jI/7a3 + xv+2t8f/trfH/7a3x/+2t8b/trfH/7a3x/+2t8b/trfG/7a3xv+6u8v/srLA/6qruP+6vMv/mp+p/3dw + cv9lRUH/Yy4p/2MnI/9kJB//ZSQf/2UlIf9lKCT/ZSgk/2UoJP9lKCT/ZSgk/2YoJP9lKCT/ZSgk/2Yo + JP9lKCT/ZSgk/2UpJP9lKCT/Zikk/2YoJP9mKST/Zygh/2goHf9pKBn/aSkc/2AoPP9NJ3L/NyO1/yUf + 4/8iHuz/JB7k/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/x0X4v+KjM7/zs/C/8vKw/+orsj/Jyjg/yQb + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe3/8gF+H/PD7c/77Cxv/LysP/trzH/zQ13v8gFuH/KSbg/6Or + yv/LycL/zs3C/2Zr1f8bFeL/Jh/g/yYe3/8mH+D/Jh/g/yUf3/8mHuD/Jh/g/yYe4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/Jh7g/yYe3/8lHt//JR/g/yYe4P8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYe4P8mHt//Jh7g/yYf + 4P8mHuD/Jh/i/yQd0v8VE3v/FxWH/yYf3v8mH+H/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHt//Jh7g/yYd + 3/8kO+v/I0nw/yNJ8P8lMOX/Jhzf/yYe4P8mH+D/Jh/g/yYf4P8jGt//Nzzj/5Gf9v+Voff/mqX4/3SA + 8f8hHN7/Jh3g/yYf4P8mH+D/Jh/h/yYf4v8ODI//fISy//v79f/q6ur/6enp/+vr6//u7u7/9PT0//n5 + +f/n5+j/pKSl/z0+Pv8AAAD/AAAA/wAAAP8AAAD/enp1/36CtP8RJbf/JEz4/x856/9AROX/kqH3/6Gt + +f+eq/n/cHzv/y8w4v8eFd6jJR7gDyQd4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAmIcAAJiHAACYhwAAgGc4ASk7hANja/gDQ0PsAz8/7AM/P + +wDPz/sA0ND7AMvM+wC6v/gAdYrsCoWM7yxbaeleRlDmmSEX38gmHuD3Jhze/yYc3/8mHN//Jhzf/yYc + 3/8mHd//Jh/g/yYe4P8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/IRrm/0JFpv+cnZv/u7vL/7a3 + xv+2t8b/trfH/7a3xv+2t8b/trfG/7a3x/+2t8b/trfG/7a3xv+7vMz/qKm1/319gP+bnaj/h4uQ/2VM + Sf9jKiX/ZSQf/2UmIf9lJyP/Zigk/2UoJP9lKCT/ZSgk/2YoJP9mKST/ZSgk/2UoJP9lKCT/Zikk/2Yo + JP9mKCT/ZSgk/2YoJP9lKST/Zikk/2UpJP9lKCT/Zigk/2YoJP9mKST/Zikk/2YpI/9nKR7/aSkZ/2cp + I/9ZJ1D/QSWW/ysg1P8iH+z/Ix7n/yYe4P8mH9//Jh7g/yYf4P8eFeH/anLV/8zNwv/Hx8P/u8HG/zw7 + 3P8gF+H/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH9//JR7g/x0Y4f+FjND/zMzC/83Nwv91fdL/HBXi/x4Z + 4f+HjM//zs7C/8zLw/+Fjc//Hhbh/yYe4P8mH+D/Jh/g/yYf3/8mH+D/Jh7g/yYe4P8mHuD/Jh7g/yYf + 4P8mHuD/Jh/g/yUe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 3/8mH+D/Jh7g/yUf4P8mH+D/Jh7f/yUe3/8mHuD/JR7f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mHt//Jh7f/yYf4f8mH97/GRaN/xQSd/8jHcn/Jx/k/yYe3/8mH+D/Jh/g/yYf4P8mH+D/JR/g/yUf + 3/8mHd//JTjo/yNJ8P8jSfD/JTLm/yYc3v8mH+D/Jh/g/yYe4P8mH+D/Ixrf/zc85P+Rn/b/lqH3/5ah + 9/+ToPb/OTvk/yEY3v8mH+D/Jh/g/yYe3/8nH+X/HRbB/xsdfP/Hy9n/9vXv/+np6f/k5OT/3t3e/8LC + w/+FhYX/LS0u/wAAAP8AAAD/AAAA/wAAAP8AAAD/MjEu/8HF0f8lKIr/HTrb/yNL9f8kNej/JBrf/zU2 + 4/9ia+z/jJr1/6Kv+v+Rn/b/T1bp/yIb37skHeAYJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJiHAACYhwAAmIcAAIBnOAEpO4QDY2v4A0ND7AM/P + +wDPz/sAz8/7ANDQ+wDLzPsAur/4AHWK7ACFjO8AW2npAEZQ5gAjGd4IJDzrjyQ66f8lMOb/JSvk/yUt + 5v8kMef/JDPm/yYn4/8mHuD/Jh7f/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 3/8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH9//JR3g/yQd3/9hZYz/srK5/7i5 + yf+2t8b/trfG/7a3xv+2t8b/trfG/7a3xv+2t8b/trfG/7a3xv+7vMz/p6ey/3Rzdf+OkJj/fn2B/2M7 + N/9lJB//ZiYi/2YoJP9lKCT/Zigk/2YoJP9mKCT/Zigk/2YoJP9mKCT/Zikk/2YoJP9mKST/Zigk/2Yp + JP9mKCT/ZSgk/2UoJP9mKCT/Zigk/2YpJP9lKCT/ZSgk/2YoJP9mKCT/Zigk/2YpJP9lKCT/ZSkk/2Yp + JP9mKCL/aCkc/2kpG/9gKTr/SiZ9/zEhxv8jH+r/Ix7p/yYe4P8mH+D/Hhfh/09S2f/Ly8P/xcXE/8zN + wv9TV9f/HRXh/yYf3/8mHuD/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8fFuH/Q0Xb/8HFxf/KycP/tbrH/zEy + 3/8aD+H/YmjW/8rMw//KysP/qavJ/yUh4P8lHOD/Jh/g/yYf3/8mH9//Jh/g/yYf3/8mHuD/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/JR7g/yUe4P8mHuD/Jh7g/yYf4P8mHuD/Jh7g/yYe + 4P8mH9//Jh/g/yYe4P8mH9//Jh7f/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYe + 4P8mH+D/Jh7f/yYe3/8mH+H/JyDl/x0Zp/8SEW7/Hxqy/ycg5v8mH+D/Jh/g/yYf4P8lHuD/Jh/g/yYf + 3/8mH+D/Jhzf/yU26P8jSfD/I0nw/yRB7f8mIOD/Jh3f/yYe4P8mH+D/Jh/g/yMa3/83POT/kZ/2/5ah + 9/+UoPf/mqb4/2dw7f8fGN7/Jh7g/yYf4P8mH+D/Jh/g/ycg5P8JApX/Q0eQ/+3v7P/39vP/nZ6f/yws + LP8JCQr/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/FBQT/8XGwv9gY6P/Dhia/yRK9f8jSPD/JSnk/yYc + 3/8iGd//Hxnf/y4t4f9OVOj/doLw/5mp+P9mce3/JB7gvCUc4A8lHOAAJRzgACUc4AAlHOAAJRzgACUc + 4AAlHOAAJRzgACUc4AAlHOAAJRzgACUc4AAlHOAAJRzgACYhwAAmIcAAJiHAACAZzgBKTuEA2Nr+ANDQ + +wDPz/sAz8/7AM/P+wDQ0PsAy8z7ALq/+AB1iuwAhYzvAFtp6QBGUOYAIxneACNJ8K8jSfD/I0nw/yNJ + 8P8jSvD/JD3q/yYo4/8mH+D/Jh7g/yYe3/8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYe4P8mH+D/Jh7f/yYf + 4P8mH+D/Jh7g/yYe3/8mH9//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yUc4/8qKc7/fH+M/7m6 + xv+3uMb/trfG/7a3x/+2t8b/trfG/7a3xv+2t8b/trfH/7a3xv+6u8v/rK26/3Z2eP+RlJ3/fHp8/2M0 + MP9lJCD/Zigk/2YoJf9lKCT/ZSgk/2YoJP9mKST/Zikk/2YpJP9mKST/ZSgk/2UoJP9mKST/Zikk/2Uo + JP9lKCT/ZSgk/2UoJP9mKCT/Zigk/2UoJP9mKCT/Zigk/2UoJP9mKCT/Zikk/2YoJP9lKCT/Zigk/2Yo + JP9lKCT/Zigk/2UpJP9lKCP/Zygf/2kpGv9kKS3/TiZv/zMivf8jHuj/Ix7p/yAY4f87N9z/vsLG/8fG + w//NzcL/dX3S/xwU4v8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh3g/x4b4f+NlM3/zczC/8zN + wv9tcdX/Fw7i/0FC2//FxsP/ycjD/7q/xv87Pd3/IRfh/yYf4P8mH+D/Jh/g/yUf3/8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8lHt//Jh/g/yYf4P8mH+D/Jh7g/yYe + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh7g/yYe + 4P8mH9//Jh/g/yYf4P8mH+D/Jh/g/ycf5P8iHMT/FBJz/xoWlf8nIOT/Jh7g/yYf4P8mH+D/Jh/g/yYe + 3/8mHt//Jh/g/yYc3v8lMeb/I0nw/yNH7/8jSfD/JTTn/yYc3v8mHt//Jh/g/yYe4P8jGt//Nzzk/5Gf + 9v+Woff/lKD3/5ai+P+Rnfb/Njbj/yEY3v8mH+D/Jh/g/yYe3/8kG+P/QUXc/xQYg/9dYqL/8vPw//z6 + 9/+dnZ7/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/EhIR/7q7tf+NksD/Cwt4/yE82f8kSvT/JEHs/yYg + 4P8mHuD/Jh7g/yYe4P8jG9//IBff/yAa3v9AQ+X/k6T3/z9A5f8hGN9HIRjfACEY3wAhGN8AIRjfACEY + 3wAhGN8AIRjfACEY3wAhGN8AIRjfACEY3wAhGN8AIRjfACEY3wAmIcAAJiHAACYhwAAgGc4ASk7hANja + /gDQ0PsAz8/7AM/P+wDPz/sA0ND7AMvM+wC6v/gAdYrsAIWM7wAiSvAAIkrwACJK8AYjR/DQI0fv/yNJ + 8P8kQu3/JSzk/yYd4P8mHd//Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe + 4P8mH+D/Jh/g/yYf3/8mH9//Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8jGub/Nja4/5GU + mP+7vMv/trfG/7a3x/+2t8b/trfG/7a3x/+2t8f/trfG/7a3x/+5usr/r7G+/3R1d/+QkJn/hYWL/2M4 + NP9lIx//Zigk/2YoJP9lKCT/ZSgk/2YpJP9mKCT/Zikk/2YpJP9mKCT/Zigk/2YoJP9lKCT/Zigk/2Uo + JP9mKCT/Zigk/2UoJP9mKCT/ZSgk/2UoJP9mKST/Zigk/2YpJP9mKCT/Zigk/2YpJP9lKCT/ZSgk/2Uo + JP9mKCT/ZSgk/2YoJP9mKCT/ZSgk/2YoJP9mKCT/Zygg/2koGv9mKSf/Uidk/zQiuv8hG+r/Jino/6Wt + yv/LysL/zMzD/5mdzP8fG+H/JR3i/yYf4v8mH+H/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8fFuH/Rkna/8TG + xP/KycP/sbXI/ysp3/8pJd//q7LI/8rJw//Jy8P/V1jY/xwV4v8mH+D/Jh7f/yUf3/8mH+D/Jh7g/yYe + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh7f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHt//Jh/g/yYe3/8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/f/yYf4P8mH+D/Jh/g/yYf4P8mH+H/JR/Z/xcVhP8WE37/JR7V/yYf4/8mH9//Jh/g/yYe + 4P8mH+D/Jh/g/yYf4P8mHN//JSvk/yNJ8P8jR+//I0jw/yNH7/8mKeP/Jhzf/yYf4P8mH+D/Ixrf/zc8 + 5P+Rn/b/laH3/5Sg9/+UoPf/mqb4/2977/8hG9//JR3g/yYf4P8mHuD/GhLe/7K3+v/M0uf/Ki+L/15k + qP/o6/P/5+bg/xoZF/8AAAD/AAAA/wAAAP8AAAD/Hx4b/7m6tf+Wmsb/Dgxz/xwtuv8kSvX/I0nw/yU0 + 6P8mHd//Jh7g/yYf4P8mHuD/Jh/g/yYf4P8lHuD/Hhbe/0pO6P85OeX7IxvfNSMb3wAjG98AIxvfACMb + 3wAjG98AIxvfACMb3wAjG98AIxvfACMb3wAjG98AIxvfACMb3wAjG98AJiHAACYhwAAmIcAAIBnOAEpO + 4QDY2v4A0ND7AM/P+wDPz/sAz8/7ANDQ+wDLzPsAur/4AHWK7AAmHN8AJhzfACYc3wAlMOctJEDt7CNJ + 8P8lOur/JiLh/yYc3/8mHuD/JR/g/yYe4P8mHt//Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/JR/f/yYe4P8mHuD/Jh/g/yYe3/8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/IBjn/0A8 + rf+dn57/urvL/7a3x/+2t8f/trfH/7a3xv+2t8b/trfG/7a3xv+3uMj/trfG/3t7ff+EhIn/lpqi/2RE + QP9lIx//Zigk/2YpJP9mKCT/ZSgk/2YoJP9lKCT/Zigk/2UoJP9mKCT/Zigk/2UoJP9lKCT/Zikk/2Yp + JP9mKCT/ZSgk/2UoJP9mKCT/Zikk/2UpJP9lKCT/Zikk/2UpJf9mKST/Zigj/2YoI/9mKCL/Zigi/2Yo + Iv9mKCP/ZSgj/2YoI/9mKCP/Zikk/2UoJP9lKCT/Zikk/2YoJf9mKCT/Zygh/2kpGv9mKSX/USZk/y0d + u/+Sl9H/zc3D/8nIw/+utsj/LjDf/yEY1v8lHtX/Jh/f/ycf4v8nIOf/Jx/l/yYf4/8mHuL/JR3i/yId + 4v+Wnc3/zMzD/8vNwv9rcNT/FBDj/4yQzv/OzsH/zczC/3R+0/8cFuL/Jh7g/yYe3/8lH9//Jh7f/yYf + 4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/JR7f/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/f/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/ycg5f8dGab/ExFv/yAauf8nH+b/Jh/f/yYf + 4P8mH+D/JR/f/yYe4P8mH+D/Jh3f/yYl4v8jRe7/I0fv/yNH7/8jSPD/I0Lt/yYi4f8mHd//Jh/g/yMa + 3/83PeT/kp/2/5ah9/+UoPf/lKD3/5Wg9/+Ypfj/Ulvp/x4W3v8mHuD/Ixvf/yMl3//N0/j//////9jc + 5f8VGUX/LDJv/5Sayf8xND7/AAAA/wAAAP8DBAD/P0A7/6mrtf9vc6z/EA95/xosuP8kSfT/I0fw/yNH + 7/UmKOPVJh3f/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYe3/8hGN//JBzguCYf4AcmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYhwAAmIcAAJiHAACAZ + zgBKTuEA2Nr+ANDQ+wDPz/sAz8/7AM/P+wDQ0PsAJhzfACYc3wAmHN8AJhzfACYc3wAmHN8dJhvezSU5 + 6v8kPev/Jh7f/yYd4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yUf4P8mHuD/Jh/g/yYe4P8mHuD/Jh7g/yYf4P8lH9//JR7f/yYf4P8mH+D/Jh/g/yAa + 5v9ISqP/paal/7m6yv+2t8f/trfG/7a3x/+2t8f/trfG/7a3x/+2t8b/u7zM/5SUnP9ubW7/qKu4/3Rr + bP9jJyL/ZSYj/2YoJP9mKCT/Zigk/2UoJP9mKST/Zikk/2YoJP9mKCT/ZSgk/2UoJP9lKCT/ZSgk/2Yp + JP9mKCT/ZSgk/2UoJP9lKCT/Zikk/2YoJP9lKST/Zigj/2coHv9pJxj/aiYY/2knHf9nJyH/aCgh/2go + If9nJyH/aCcd/2gmHP9oJhz/aCYa/2olFf9qJRX/aSUX/2gmGf9pJhn/aCYa/2gnG/9oKBz/aCgb/2oo + FP9gHx3/kHeE/8zPy//Ix8P/yMnI/z4+qf8OC37/FhSA/xkWi/8dGJ7/Hxmz/yIcwv8kHc3/JR7X/yUe + 3P8eFt7/TlPX/8bJxf/KycP/r7TI/yYj4f9kaNn/y83D/8vLw/+Zncz/IRrm/yYe5v8nH+T/Jh/j/yYf + 4f8mH+H/Jh/h/yYe4P8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYf + 4P8mHuD/Jh/g/yUe3/8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8lHt//JR7f/yYf4P8mH+D/Jh7f/yYe + 3/8mHt//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8nH+T/Ix3M/xQTdv8aFpf/Jx/k/yYf + 4f8mH+D/Jh/g/yYf3/8mH+D/JR/f/yUd3/8mH+D/JD/s/yNI8P8jR+//I0bv/yNJ8P8kO+n/Jh7f/yYc + 3/8kGt7/MjDh/5Gc9v+Voff/lKD3/5Sg9/+UoPf/l6L4/5Cf9v88QOX/IRnf/x0U3v9ISOT/8/j9//// + ///m5uX/HR0Y/wAAAP8FCT3/GRp2/wsNKf8JChz/HBxO/1JWmv8uMpT/ERyd/x861f8kSfT/I0fx/yNI + 7/8jP+vEJiThECYe4FsmH+DDJh7g9SYf3/4mH+D/Jh7f/yYf4P8mHt//Jh7g4CYe3zAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmIcAAJiHAACYh + wAAgGc4ASk7hANja/gDQ0PsAz8/7AM/P+wDPz/sAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gmyYd + 3/8jQO3/IjDm/yUa3/8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf3/8lHt//Jh7g/yYe4P8mH+D/JR7f/yUe3/8mH+D/Jh7g/yYf + 4P8iHOX/TFKf/6mpq/+5usr/trfH/7a4x/+2t8f/trfH/7a3xv+2t8f/t7jI/7S1xP90dHb/kJCY/6Gm + sf9lRUH/ZSQf/2YoJP9mKST/Zigk/2YpJP9mKST/ZSgk/2YpJP9mKST/ZSgk/2YpJP9mKCT/Zigk/2Yo + JP9mKCT/Zikk/2YoJP9mKCT/ZSgk/2YpJP9mKSX/Zycf/2gnHv9fLEH/UDRu/0Q4kP9AO6L/PD2v/zw+ + sv86PLL/NTiy/zo3ov88OJ3/Pzqe/0I6lv9FOIf/RTiJ/0c1ff9MMW7/SzFw/08vY/9RK1j/UipY/1oq + Sf9dLUf/WyQz/39ZXf/Cx8z/wcPG/87QyP9VW5X/Cwhz/xUSdv8UEXP/FBBv/xMPbf8UEHP/FRB3/xUR + fP8WEoD/FxKI/xUViv+Xm7P/zczH/8zNx/9aXrX/MTal/8bIxP/LysX/sbXE/ygru/8dF73/Ix3G/yQe + 0P8lHtr/Jh/e/ycf4v8nH+b/Jx/m/ycf5P8mH+L/Jh/h/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe + 4P8lH9//Jh/g/yYf4P8mHuD/Jh7g/yYe3/8mH+D/Jh/g/yYe4P8mHuD/JR/f/yUf4P8mH+D/Jh/g/yYe + 3/8mH+D/Jh7g/yUe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mH+D/Jh/h/ycf4v8aF5b/FRN5/yUf + 0v8nH+T/Jh7g/yYf4P8mH+D/Jh/g/yUf3/8lH+D/Jhzf/yUy5/8jSfH/I0fw/yNG7/8jRu//I0jw/yQ5 + 6v8lMuf/Ijbo/yw96f+MmPb/lqL3/5Sg9/+UoPf/lKD3/5Sf9/+apvj/gY7z/yso4f8WDd7/gIft//// + ////////rq6w/wICA/8AAAD/AAEA/x8fif8lINf/IS7D/yA2z/8aMs7/Hj3i/yNI8v8kSPT/I0bv/yNH + 8P8jR+//JSnjeSYk4QAmHuAAJh/gAyYf4CImH+BBJR7fUCUe32QmH+BqJh7fYiYf3x4mHt8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJiHAACYh + wAAmIcAAIBnOAEpO4QDY2v4A0ND7ACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gCiYe + 4NElHN//KEHs/zJN7f8nI+D/Ihnf/yYf4P8mH+D/Jh/g/yYf3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mHt//Jh7g/yYf4P8mH+D/JR/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe + 4P8mHuD/Ix3j/1JYmf+srbH/ubrJ/7a4x/+2t8b/trfG/7a3xv+2t8b/trfG/7m6yv+np7P/a2tr/6ut + uv+MjJP/YjIt/2UlIP9mKST/Zigk/2YpJP9mKST/Zikk/2UoJP9mKST/Zikk/2YoJP9mKST/Zigk/2Yo + JP9mKST/Zigk/2YpJP9lKCT/Zigk/2YoJP9lKST/Zygg/2MrNP8+PKb/JkXq/yFI9/8fSP3/H0j7/yBJ + +P8eR/j/I035/09v+/9kgf//ZIH//22J//9wi///cIv//3CM//9sh///ZoD7/2aA+/9ngPf/Ynfz/1hw + 8/84Vuz/IkLp/yVE5v8rSuP/O1/p/0Jk6P9Pb+b/N1Ta/x42zP8gNsj/HzPB/x0xvf8cLbf/Gyqu/xsm + pP8ZIZn/FxyN/xUXgv8MC3n/SUyT/87NxP/W0sT/qKmx/yMme/+oqrj/0c/H/8PEw/83Oob/DAlz/xUT + e/8VE33/FxSD/xkWj/8bGJv/HRmq/yAbuP8jHcr/JR7Y/yYf3/8nH+T/Jx/m/yYf4/8mH+H/Jh/g/yYf + 4P8mH+D/JR7f/yYf4P8mH+D/Jh7g/yYf4P8mH9//Jh/g/yYf4P8mH+D/Jh7g/yYf3/8mHt//Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh7g/yYe4P8nH+X/IhzD/xMS + cv8cGKf/JyDm/yYf4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYc3/8lJeL/I0bu/yNH8P8jRu//I0bv/yNH + 8P8jSfD/I0jw/yJI8P8kSvD/eIz2/5mk9/+UoPf/lKD3/5Sg9/+UoPf/mKP4/46c9v8wL+L/HRnf/8HH + 9///////8vLy/zk6Ov8AAAD/AAAA/wEBAP8fKIb/JT/7/yRJ9v8jSvX/I0n0/yNI8v8jRvD/I0bv/yNH + 7/8jSfD/JDrp8CYb3iQmG94AJh7gACYf4AAmH+AAJh/gACUe3wAlHt8AJh/gACYe3wAmH98AJh7fACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYh + wAAmIcAAJiHAACAZzgBKTuEA2Nr+ACUe3wAlHt8AJR7fACUe3wAlHt8AJR7fACUe3wAlHt8AJR7fACUe + 3y4mH+DvIhnf/zY75P+On/f/eYTx/zEx4v8gF9//JBzg/yYe4P8mH+D/Jh/g/yYe3/8lHt//Jh7g/yYf + 4P8mH+D/Jh7f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/JR7g/yMd4v9XXZX/srO5/7i5yP+2t8b/trfH/7a3x/+2t8f/trfG/7a3xv+6u8r/lpaf/3Jy + c/+ytcT/fHR3/2IoI/9lJyL/ZSkk/2UoJP9mKST/Zigk/2YpJP9mKST/Zikk/2YpJP9lKCT/ZSgk/2Yo + JP9lKCT/Zikk/2YoJP9lKCT/ZSgk/2YoJP9mKCT/ZSgk/2kmGP9EOZP/HUr//yJH8/8jR/D/I0bv/yNH + 8P8jR/D/HULv/2B69P+fqff/nKX4/5ul9/+ZpPf/maP3/5mj9/+Zo/f/mqT4/5ql+P+apfj/mqX5/5um + +f+bpvr/kZ/5/0Vl9f8eRPP/IUbz/x5C8P8eQvH/HEDx/yFF8/8kSvX/JEn2/yRJ9v8kSvf/IUj4/x5E + 9f8dRPP/IEXw/yNG7f8jRuv/Hj7f/xw50/9Zctz/dozX/4ue1f87TK3/eYO4/8LFyf/Lzcr/W2CY/wkG + cf8UEHL/FBB0/xQQdP8TEHT/FBFz/xQRc/8UE3b/FRR7/xYVgv8aF5H/HBij/yAbt/8jHcz/JR7b/ycf + 4v8nIOb/Jx/k/yYf4v8mHuD/Jh/g/yYe4P8mHuD/Jh7g/yYf4P8mH9//Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf + 4f8aFpT/FRN5/yQe0v8mH+T/Jh/g/yYe3/8mHuD/Jh/f/yUe3/8mHuD/Jhze/yU66v8jSfH/I0bw/yNH + 7/8jR/D/I0fv/yNG7/8jRu//HULv/09s8v+apff/lZ/3/5Sg9/+UoPf/laD3/5uo+P9ha+3/GA/d/0VG + 5f/y+P3//////4uLjP8AAAD/AAAA/wAAAP8BAAD/HjKG/yRL/P8jR/D/I0fw/yNH8P8jR+//I0bv/yNG + 7/8jR/D/I0Xu/yUm4aImG94AJhveACYe4AAmH+AAJh/gACYf4AAlHt8AJR7fACYf4AAmHt8AJh/fACYe + 3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmIcAAJiHAACYhwAAgGc4AJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuBHJh/f/yYe4P8hGN7/RUnm/5Si9/+ToPb/Vl7p/ysn4f8gGN7/Ixrf/yQd4P8lHuD/JR7f/yYe + 3/8mH+D/Jh/g/yYe3/8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/f/yYf4P8mHuD/Jh7g/yYf + 4P8mH9//Jh/g/yUd4f8kHeD/XGGT/7Ozuf+4ucn/trfH/7a3x/+2t8b/trfG/7a3xv+2t8b/urvM/4yN + lP96en3/srbF/3JlZf9jJB//Zigk/2YpJP9lKST/Zikk/2YoJP9mKST/Zigk/2YpJP9mKST/Zigk/2Yo + JP9mKCT/Zigk/2UoJP9lKCT/Zikk/2YpJP9lKCT/ZSgk/2YoIv9pKB7/NUC6/x9J/P8jRu//I0fv/yNH + 7/8jRu//I0fw/x1C7/9qgfT/n6n3/5ei9/+Xovf/l6L3/5eh9/+Woff/lqH3/5ah9/+Woff/lqH3/5ah + 9/+Woff/lqH3/56n9/95jPb/H0Pv/yNG7/8jR/D/JEfw/yNH8P8jR/D/I0fv/yNG7/8jRvD/Ikbv/zFT + 8P9RbfP/Znz0/22E9f91ivf/d4z3/26H+P9ogfn/VG72/0lo9f9BY/T/OFrz/zBR7/86Xuv/SGrn/zZU + 2v8WLsT/GCy5/xklp/8YIJr/FhuN/xYWg/8WFH3/FRF2/xQPcv8UEHP/FBFy/xMRcf8UEnT/FRN6/xgV + h/8bF5v/Hhmv/yIcxv8lHtj/Jh/h/ycg5/8nH+T/Jh/i/yYe4P8mH+D/Jh7f/yYf4P8mH+D/Jh/f/yYe + 3/8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mHt//Jh/g/yYe + 3/8mHuT/JB3L/xQSdf8bGJv/Jh/k/yYf4f8mH+D/JR/f/yYf4P8mH9//Jh7g/yYc3/8lKOL/I0fv/yNI + 8P8jR+//I0fv/yNH8P8jR/D/I0bv/yFF7/8mSu//fpH2/5uk9/+UoPf/lKD3/5qm+P9+jfP/KSfg/xYL + 3f+Bie7//////+np6f8hISL/AAAA/wAAAP8AAAD/AAAA/xsrdP8kSvz/I0fw/yNH7/8jR+//I0bv/yNH + 8P8jR/D/I0nw/yQy5/cmHd8yJh3fACYd3wAmHd8AJh/gACYf4AAmH+AAJR7fACUe3wAmH+AAJh7fACYf + 3wAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJiHAACYhwAAmIcAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gXiYe3/8mH+D/Jh7g/x8X3v82N+P/fYny/6Gu+f+JlvT/W2Tr/zY35P8pJeH/Ih3f/x8X + 3v8gF97/IRjf/yIZ3/8iGt//Ixvf/yQc3/8kHOD/JBzg/yUd4P8lHeD/JR7g/yYe3/8mH9//Jh7g/yYe + 4P8mH+D/Jh7g/yYe4P8lHeH/JB3e/2NmkP+0tLz/t7jJ/7a3xv+2t8b/trfH/7a4x/+2t8f/trjH/7q7 + zP+IiI//goKI/66ywf9tWFf/ZCMf/2UoJP9mKST/Zigk/2YoJP9mKST/Zigk/2YpJP9mKCT/Zikk/2Uo + JP9lKCT/Zikk/2YpJP9lKST/ZSgk/2YoJP9mKCT/ZSgk/2UpJP9mKST/aScZ/zk2rv8dSf//I0jv/yNH + 8P8jR/D/I0fw/yNH8P8gRO//M1bx/3OI9f+JmPb/i5r2/4ua9v+Kmvb/jpz3/5Ge9/+Qnvf/kJ73/5Cd + 9/+Rnvf/kJ73/5Ce9/+Vovf/aYD1/x5D7/8jRu//I0fw/yRH8P8jR/D/I0fw/yNH8P8jR+//IETv/yxR + 8P+Kmvb/oKn4/56n+P+bpff/mqT3/5mk9/+bpff/nKb3/5ul9/+ZpPf/lqH3/5Sg+P+Mmvf/fI73/2uB + 9v9hePj/VnP7/0Rn+v81V/X/LE7y/yRI7/8cPeT/GzbU/xoxyf8aLbv/Giao/xkhmP8XGor/FxaB/xYT + ev8TEHL/Eg9u/xMQb/8UEXX/FhOB/xoWmP8fGq//IxzJ/yUf2v8nH+T/Jx/m/yYf4/8mH+H/Jh7g/yYf + 3/8mHt//Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh7f/yYe + 4P8mHuD/Jh7g/ycf5v8eGan/ExJv/yAat/8nIOn/Jh/g/yYf4P8mHuD/Jh/f/yYf4P8mH+D/Jhze/yQ3 + 6P8jSfD/I0bv/yNG7/8jR+//I0fv/yNG7/8jR/D/HkPv/zla8f+OnPf/m6T3/5ql+P+NmfT/Nzjj/x4T + 3v8iIuH/w8r4//////+HiIn/AAAA/wAAAP8AAAD/AAAA/wAAAP8YJ27/JEr9/yNH8P8jR+//I0fw/yNH + 8P8jR+//I0nw/yQ+6/8mH9+XJh3fACYd3wAmHd8AJh3fACYf4AAmH+AAJh/gACUe3wAlHt8AJh/gACYe + 3wAmH98AJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACUe3wAlHt8AJR7fACUe3wAlHt8AJR7fACUe3wAlHt8AJR7fACUe3wAlHt8AJR7fACUe + 3wAlHt8AJR7fACUe330mH+D/Jh/g/yYf4P8mH+D/IRjf/yUg3/9XX+r/kZ72/6Kv+f+Wo/f/gY/z/3B7 + 7/9kae3/VVzp/0pR5/8/RuX/ODfj/zMt4/8uK+H/Kyfg/ykm4P8mI+D/Ih/f/yIe3v8fF9//Hxbe/x8X + 3v8gGN//IBjf/yAY3/8gGN//IBfg/x4X4f9VW5X/srO5/7i5yP+2t8b/trfG/7a3x/+2t8b/trfH/7a3 + x/+6u8z/iYmQ/4qJkP+us8D/bFRT/2QkH/9lKCT/Zigk/2UpJP9lKCT/Zikk/2YoJP9mKCT/Zikk/2Yo + JP9mKCT/ZSgk/2YoJP9lKST/Zikk/2YoJP9lKCT/Zikk/2YpJP9mKST/Zikk/2goHP9WJ1f/KC7f/yA8 + 9v8jQe//I0Pu/yNF7v8jRu7/I0fv/yBF7/8fRe//J03w/y5V8f8tVPH/LFPx/zNZ8f85XPL/OVzy/zhc + 8v84XPL/OFzy/zhc8v84XPL/OVzx/ypQ8f8hR/D/I0nw/yNJ8P8jSfD/I0jw/yNI8P8jSPD/I0jw/yJG + 7/8kSPD/VG/z/4CS9v+Qnff/mqX3/5ul9/+Zo/f/mKL3/5ah9/+VoPf/laD3/5Wh9/+Woff/lqH3/5ij + 9/+bpfj/nab4/5ql9/+Xovf/lKD4/4iY9/94jfb/aoH3/1h1+f9Gafr/NFj5/yxQ9f8iSPL/HEDt/x07 + 3/8dNtH/GzHE/xsqsf8bI57/GB2P/xYXgf8VE3b/ExBu/xMQcv8XE4L/GhWZ/x8atP8jHM//Jh7c/ycf + 5f8mH+b/Jh7k/yYf4/8mHuH/Jh7g/yYf4P8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/f/yYe3/8mH+L/Jh/h/xoWlP8WFHr/IBu5/yYf4v8mH+L/Jh/g/yYe3/8mH+D/Jh/g/yYd + 3/8lIuH/I0Pt/yNI8P8jRu//I0bv/yNH8P8jR+//I0fw/yNH7/8dQu//Olrx/4GS9v+Kmvf/P1ft/x4s + 5v8cMej/RmXw//X7///t7Or/ISIi/wAAAP8AAAD/AAAA/wAAAP8AAAD/Dhxi/yVK/f8jR/D/I0fw/yNH + 7/8jR+//I0fw/yNF7/8lJuLeJh3fGCYd3wAmHd8AJh3fACYd3wAmH+AAJh/gACYf4AAlHt8AJR7fACYf + 4AAmHt8AJh/fACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+CaJh/g/yYe4P8mH+D/Jh/g/yYf4P8kHN//Hhbf/zIy4/9jbe3/jJn1/56r + +f+irvr/oq/6/5+t+f+dq/n/m6r5/5un+P+Wo/f/jZv2/4aV9P+AjvP/eojy/3OA8P9xfe//a2/u/2xv + 7v9ma+3/WmLr/1tj6/9aYer/Tlbp/01V6f9MVe3/YGif/6emrP+5usn/trfH/7a3x/+2t8f/trfG/7a3 + xv+2t8b/urvL/4+Pl/+DhIn/r7PB/21aWf9kIx7/ZSgk/2YoJP9mKST/Zigk/2YoJP9lKST/Zigk/2Yp + JP9mKCT/ZSgk/2UoJP9mKST/ZSgk/2UoJP9mKCT/ZSgk/2YoJP9lKCT/ZSgk/2YoJP9mKCT/aSga/1wm + Rf86Iqz/JCDn/yIi6v8kJuT/JSfi/yUp4/8lK+T/JCrj/yMr5f8iK+b/Iivm/yIr5v8hLOb/Hy/m/x8v + 5v8fL+b/Hy/m/x8w5v8fMOb/HzLn/x806P8iNun/JDjp/yQ46f8kOOj/JDjp/yQ86v8kPOr/JEDt/yND + 7v8jR+7/Ikjv/x1E8P8jSvD/N1ry/0xq8/9he/T/eIz1/4eW9v+Qnvf/maX3/5ul9/+ao/f/maP3/5ei + 9/+Woff/laD3/5Sf9/+UoPf/laD3/5Wg9/+Yovf/mqT3/5ym+P+apff/mKP3/5Sg9/+Glvb/dYn2/2Z9 + 9f9Sb/b/PV/3/y9U+P8mTfb/HkTy/x1C8P8ePuT/HjnT/x0zxf8dK6//GiOa/xYah/8WFnz/FhN5/xcT + hP8bFZr/IBmv/yIbxf8kHdX/Jh/d/ycf5f8nIOb/Jx/l/yce5P8nH+P/Jh/j/yYf4v8mHuH/Jh/h/yYe + 4P8mHuD/Jh7h/yYf4v8mH+P/Jx/m/yYf4f8fGrD/FBJ1/xQSdP8dGKH/Jh/g/yYf5P8mHuD/Jh7g/yYe + 4P8mH+D/Jhzf/yUs5P8jSPD/I0fw/yNG7/8jR/D/I0fv/yNG7/8jR+//I0fv/x5C7/8mS/H/K0/w/x5E + 7/8jSvD/Fj/v/4qk+f//////lpaX/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/w8cWP8mSvj/I0fx/yNH + 7/8jR+//I0jw/yNH8P8lLeX6JhzfTiYd3wAmHd8AJh3fACYd3wAmHd8AJh/gACYf4AAmH+AAJR7fACUe + 3wAmH+AAJh7fACYf3wAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAIBfeACAX3gAgF94AIBfeACAX3gAgF94AIBfeACAX3gAgF94AIBfeACAX + 3gAgF94AIBfeACAX3gAgF94AJh/gsCYf4P8mHuD/JBvg/yEZ3/8eF9//Hhbe/x0V3v8ZD97/HhXe/y8u + 4f9MT+j/WWTr/2Rw7f9lcO3/ZHDt/2Vy7f9xfe//cn7w/3yG8v+GkPT/jJn1/5Sj9/+hrfr/oq76/6Cs + +f+cqfn/nKn5/5uo+P+bqPj/mqj4/5mm+P+Zpvj/m6n//4GHuP+UlJn/urvM/7a3x/+2t8f/trfG/7a3 + xv+2t8b/trfG/7q7y/+XmKL/eHl7/7W4yP92bnD/YyYh/2UnIv9lKST/Zigk/2YoJP9lKCT/ZSkk/2Yo + JP9mKCT/Zigk/2YoJP9mKCT/Zikk/2YoJP9lKCT/Zigk/2YpJP9lKCT/ZSgk/2UpJP9mKST/Zigk/2Yo + JP9oKRv/aCkf/1UnXf83IrT/Jh7j/yIc6v8lHOL/Jh3f/yYc3/8mHN7/Jhzf/yYb3v8mHN//Jhzf/yYc + 3/8mHd//Jh3f/yYd3/8mHd//Jh3f/yYd3/8mHt//Jh3f/yYd3/8mHd//Jh7f/yYd3/8lHd//Jh7e/yYe + 3/8lIuH/JSfj/yUr5P8lMeb/Izfp/x866/8cPO3/HUHu/yBG8P8nTvH/OFzy/0dm8v9YdPT/boT1/4CQ + 9v+ImPb/kZ73/5qk9/+bpff/m6T3/5qk9/+Yovf/l6H3/5Wg9/+Un/f/lKD3/5Sg9/+VoPf/mKL3/5uk + 9/+cpvj/mqT3/5ij9/+Mm/f/fI/2/2yC9f9WcPT/PmD1/y9T9v8kS/j/HUT1/x5E8/8fQu//ID7f/x85 + z/8dMLn/Gyah/xgfjv8WGIL/GBWB/xYRiP8RDJj/GROn/yEbuv8jHMv/JB3T/yUf2P8mH9z/Jh/f/ycf + 5P8nH+b/Jx/m/ycf4v8mH93/JR7W/yAatv8ZFo3/FBN4/xUTe/8VE3r/ExJz/xsXmP8mHtz/Jh/l/yYf + 3/8mH+D/Jh/g/yYf3/8mHd//JDXo/yNJ8P8jR+//I0fv/yNH7/8jRu//I0fw/yNH7/8jR+//IUXv/yBF + 8P8jR/D/IETv/yNJ8P/O2/7/+Pf1/zMzNP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8QGkH/Jknt/yNH + 8v8jR+//I0fw/yNI8P8lMeb/JhzffCYc3wAmHd8AJh3fACYd3wAmHd8AJh3fACYf4AAmH+AAJh/gACUe + 3wAlHt8AJh/gACYe3wAmH98AJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACAX3gAgF94AIBfeACAX3gAgF94AIBfeACAX3gAgF94AIBfeACAX + 3gAgF94AIBfeACAX3gAgF94AIBfeAh8X378dFN7/HBbe/yYl4P83M+P/SEfm/1xk6f9xduv/eoDu/zM0 + 4v8hF9//Hxbf/x8Y3v8gGd7/IBne/yAZ3v8hGt//Ihvf/yIa3v8iGt//Ix7f/ysr4f82OOT/SEvn/2Zy + 7v+LmPX/m6j4/5qm+f+Zpfj/mKP4/5ii+P+Xovj/lqL4/5ij/f+Jktf/hIWL/7i5yP+2t8f/trfH/7q7 + y/+4ucn/trfH/7a3x/+6u8v/pqaz/29wcP+ys8L/kZSd/2I9OP9lIx7/Zigk/2YpJP9mKCT/ZSgk/2Uo + JP9mKCT/ZSgk/2UoJP9mKCT/ZSgk/2UoJP9mKCT/Zigk/2YoJP9lKCT/Zigk/2YoJP9lKST/Zikk/2Yo + JP9mKST/Zikl/2YpI/9pKRj/Zykg/1goVv87I6f/Jh/f/yIe7P8kHuX/Jh/g/yYf3/8mHt//Jh/g/yYf + 4P8mHuD/Jh7f/yYe3/8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mHuD/Jh7g/yYe4P8mHt//Jh7f/yYe + 4P8mHuD/Jh3g/yYd3/8mHN//Jhze/yYd3/8mHd//JiLh/yYn4/8lK+T/IzHm/yA16f8cOez/HD7t/x5E + 7/8hSPD/KlHx/zlc8v9IZfL/VnP0/2mA9P98jfX/hpb2/46c9v+Yo/f/m6X3/5qk9/+apPf/maP3/5ii + 9/+Woff/laD3/5Wg9/+VoPf/l6H3/5qk9/+cpvj/mqT3/5ij+P+Mm/b/eo72/2Z99f9QbfP/Olry/ytQ + 9P8hSPb/HUT3/x9F9f8gRfL/IUHk/xo10P87TMD/ZG2q/zE4j/8UFX//ExB1/wwJd/8RDYH/GBOJ/xkW + kf8aF5r/Gxed/xsXnv8aF5f/GRaN/xcUgP8UEnX/FBJ1/xUTev8VE3v/FBN7/xQTev8TEnL/GheQ/yUe + 1v8nIOf/Jh7f/yYf4P8mH+D/Jh7g/yYe3/8kPOr/I0nw/yNG7/8jR+//I0fw/yNG8P8jRu//I0fv/yNH + 7/8jR+//I0fv/xo/7/9OcPP//f///7Szsf8BAAH/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/EBtD/yZK + 7v8jR/L/I0jw/yNI8P8lMeb/JhzfliYc3wAmHN8AJh3fACYd3wAmHd8AJh3fACYd3wAmH+AAJh/gACYf + 4AAlHt8AJR7fACYf4AAmHt8AJh/fACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AA9OeQAPTnkAD055AA9OeQAPTnkAD055AA9OeQAPTnkAD05 + 5AA9OeQAPTnkAD055AA9OeQAPTnkAD055AtDQ+XNZGzq/5GT8f+psPX/wsb5/9TU/P/V1/z/2dr8/9re + /f9OVeb/IBff/yYf4P8mHuD/Jh/g/yYf4P8mHt//Jh/g/yYe4P8mHuD/JR7g/yUe4P8kGt//Ihjf/x8W + 3v8eF97/MC7i/1Ja6f9xfPD/go7z/4eW9P+LmfX/j532/5Og9v+Zpvn/mqf4/3p9j/+rq7b/ubrK/7S1 + xP+UlJ3/q6u5/7i5yf+2t8b/t7jI/7O0w/9zdHX/m5ym/7i7y/95eXv/Yjcy/2UjHv9mKCP/Zikk/2Yo + JP9mKCT/Zigk/2UoJP9mKST/Zigk/2YoJP9mKST/Zikk/2YoJP9lKCT/Zigk/2YoJP9mKCT/Zikk/2Yp + JP9lKCT/Zikk/2YpJP9lKST/Zigl/2YoIv9pKBn/aSkb/1spR/9BJJT/KyDS/yIc6f8jG+X/JR3g/yYf + 4P8mH+D/Jh/g/yYe3/8mHt//Jh7f/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mHuD/Jh/g/yYf3/8mHt//Jh/g/yYe4P8mHuD/Jh/g/yYe4P8mHd//Jhzf/yYc3/8mHd//Jh7g/yYj + 4P8lKOP/JC7l/yIz6P8fOev/HDzt/xxB7v8dRfD/H0bw/yhO8f81WPH/QmDy/09s8/9he/T/dIf1/4KS + 9v+Kmvb/lKH3/5ul9/+apff/mqT3/5qk9/+Yovf/lqH3/5Sg9/+VoPf/l6L3/5uk9/+bpvf/mqX3/5ei + 9/+Hl/b/doj1/1958/9FZPP/M1Ty/yVM8/8QOvT/V3b6/+Tv//+dtfn/Gjzh/zNM0f9pdbn/QUud/xYa + hf8TEnX/Eg9t/xMPbf8TEHD/ExFz/xQSdv8UE3j/FRN7/xUTe/8VE3r/FRN7/xQTev8VE3v/FRJ6/xQS + dP8YFoj/JB7P/ycf5/8mHuD/Jh/g/yYf4P8mHd//JiLg/yNA7P8jSfD/I0fv/yNH8P8jRvD/I0bv/yNG + 7/8jR+//I0fw/yNG7/8XPO7/lq36//////9VVFX/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/xAY + N/8mSOj/I0n0/yNH8P8lMOb/Jh3flCYe4AUmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJR7fACUe3wAmH+AAJh7fACYf3wAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAx8v6AMfL+gDHy/oAx8v6AMfL+gDHy/oAx8v6AMfL + +gDHy/oAx8v6AMfL+gDHy/oAx8v6AMfL+gDHy/og0NL74dfZ/P/Z2vz/19f8/9PT/P/Q0Pv/0M/7/8/Q + +//W2Pz/aWzq/x0V3v8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mH9//Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/JR7f/yIa3/8fF9//IBnf/yUi4P8tLuH/MjTi/zg44/88POT/QkHm/1Ja8f9sdLH/k5SU/7y+ + zv+urr3/YmJg/4mKkP+6u8z/trfG/7a3xv+7vMz/k5Sb/3R0df+4ucj/srXE/3t7f/9jPjr/ZCUf/2Ul + IP9mJyP/Zigk/2YoJP9mKCT/Zikk/2YoJP9mKCT/Zigk/2UoJP9lKCT/ZSgk/2UoJP9mKST/Zigk/2Yo + JP9lKST/Zigk/2YoJP9mKCT/Zigk/2YpJP9mJyP/Zici/2UkIP9oJRf/aSQS/14nNf9ILIH/LSnI/x4Z + 4f8fF+H/JBvg/yYe3/8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7f/yYe4P8mH+D/Jh/g/yYe + 4P8mH9//Jh/g/yYf4P8mH9//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mHuD/Jh3f/yYc3/8mHd//Jh7f/yYi4f8mJ+P/JSzk/yQ15/8iOur/Hz7t/xxB7v8bQ/D/HUXw/x5F + 8P8jSfD/L1Pw/zta8f9JZvP/Wnb0/22C9f9+j/X/iJj2/5Kg9/+bpPf/mqT3/5ql9/+ao/f/l6L3/5Wg + 9/+Voff/maP3/5ul9/+bpff/mqX4/5Ge9/9/kfb/Y3r0/3SN9f/X4P7/r8P8/yBH8/85Yfn/0eH//563 + /P8hROn/IDrV/x4xu/8aJJ//GBmH/xUSd/8UEHT/FBF1/xQSef8UE3r/FRN7/xUTe/8VE3r/FRN7/xUS + e/8VEnv/FBN1/xYUgP8iHMX/Jx/n/yYf4P8mHuD/Jh/g/yYd3/8lI+H/JD/r/yNJ8P8jR/D/I0fw/yNH + 7/8jR+//I0fw/yNH8P8fQ+//Jkzv/9jk///d29b/DQ0O/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8PFSj/JUri/yRE8v8mK+T6JhzfeSYd3wAmHuAAJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmH+AAJh/gACUe3wAlHt8AJh/gACYe3wAmH98AJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gANLS+wDS0vsA0tL7ANLS+wDS0vsA0tL7ANLS + +wDS0vsA0tL7ANLS+wDS0vsA0tL7ANLS+wDS0vsA0tL7NdHR+/XPz/v/z8/7/8/P+//Pz/v/z8/6/87P + +//Oz/v/29z9/4WE7/8bFN7/Jh7g/yYe4P8mH+D/Jh/g/yUf3/8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/yYe3/8mHuD/Jh7g/yYe4P8lHN//Ixrf/yIZ3/8hGN7/IBje/x8V3v8YEd//XWnh/3+B + iP+ys7//tbbG/4SFiv+Xl6H/ubrK/7a3xv+2t8b/t7nI/7W2xv95eXz/fn6B/7i6yv+6vc3/jI+W/2hT + Uf9iMSz/Yygj/2QkH/9lJB//ZSQg/2UkIP9lJCD/ZSQg/2UlIP9lJSD/ZSUg/2YmIf9lJiH/ZiUh/2Yl + IP9lJSD/ZSUg/2UkIP9mJCD/ZSUg/2UkIP9kJB//ZCgk/2MtJ/9jNC//Y0A9/2pUUv92aGP/hX51/5CS + nf+Bh83/SU3b/yUg4P8cFOL/Ihrh/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh7f/yYe3/8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/f/yYe3/8mHt//Jh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh3f/yYc3v8mHN//Jh3f/yYi4P8mKOP/JS3l/yQ1 + 5/8jPev/IkHt/yBF7/8dRPD/HEPw/x1E8P8dQ/D/IUbw/yxQ8P85WfH/SGXy/1p18/9xhfX/gJH1/4yc + 9v+YpPj/m6X3/5ql9/+ZpPf/l6L3/5ag9/+Xoff/mqP3/5ei9/+rtPn/6uz+/93j/f95jfX/cYr1/9Xf + /f+Wrvn/HETy/xxD9f8eRfb/H0b1/yJD5/8gOtH/Hiyx/xkekv8WFH3/Ew9z/xMQdP8UEnj/FRN6/xUT + e/8VE3v/FRN7/xUTe/8TEnb/FRN7/yAbuf8mH+X/Jh/i/yYf3/8mH+D/Jhzf/yYg4P8lOOj/I0nw/yNI + 8P8jR/D/I0fv/yNG8P8jRu//Gj7v/1R19P//////hYSE/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/DhUq/yI23f8lIuXhJhzfUSYc3wAmHd8AJh7gACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmHt8AJh/fACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ADOz/oAzs/6AM7P+gDOz/oAzs/6AM7P + +gDOz/oAzs/6AM7P+gDOz/oAzs/6AM7P+gDOz/oAzs/6AM7P+jXPz/v1z8/7/8/P+//Pz/v/z8/7/8/P + +v/R0fv/19f8/+Xl//+MkPD/HBbe/yUe4P8mH+D/Jh/g/yYf4P8lH9//Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYe4P8mH+D/Jh7g/yYf3/8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8iGd//MC/h/4ya + /f+AhrX/j4+S/7u9zf+6u8v/uLrK/7a3xv+2t8b/trfH/7a3xv+5usr/r7C+/3N0dv9zc3X/oaKt/7q7 + y/+prbr/iImP/3dub/9vXl//aVBP/2VGQv9kPzv/Yzw4/2M2Mv9jNTD/YzUw/2MzL/9jMCv/Yy8q/2Ix + LP9jNDD/YzQw/2M0MP9jNjP/ZDw4/2VAPP9mSkf/a1lY/3Npaf9+eX3/jI6V/5ufqf+orbn/srXF/7q8 + zP+8vcr/wsPI/77By/+codL/Ymjb/zEx4v8iGuX/Jh7g/yYf3/8mHuD/Jh/g/yYe3/8mHuD/Jh7f/yUf + 3/8mH+D/Jh/g/yYf4P8mHuD/Jh7f/yYf4P8mH+D/Jh7g/yYf4P8lH9//Jh7f/yYf3/8lHt//Jh7g/yUe + 3/8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/JR/g/yUe4P8mH9//Jh7f/yYe4P8mHuD/Jhzf/yUc + 3v8mHN//Jh3f/yYj4f8lKeT/JTHm/yQ56v8kQOz/I0Xv/yJH8P8gRvD/HUTw/xxC8P8dQu//HULv/yNJ + 8P8xVPD/QF/y/1Zy8/9sgvX/gJH2/46c9/+Zo/j/nKX3/5ul9/+VoPf/oq34/+Xo/f/k5/3/oKn4/6ix + +P/u7/7/w8r7/3aJ9f9kfPP/R2by/zJU8f8iSPP/HUT2/x1F9v8gRO//Ij7c/x8xvP8ZIJf/FhR8/xQP + c/8UEHb/FRJ6/xUTev8UE3v/FRN7/xQSd/8TEnT/Hhqp/yYf4f8mH+T/Jh/g/yYf4P8mHeD/Jh3f/yUt + 5P8kQez/I0nw/yNJ8f8jR/D/I0bv/xY97v+ftv3/+/n0/zExMv8AAAD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/xIWKP82MtDKIBblKCYc3wAmHN8AJh3fACYe4AAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh7fACYf3wAmHt8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AA0dH7ANHR+wDR0fsA0dH7ANHR + +wDR0fsA0dH7ANHR+wDR0fsA0dH7ANHR+wDR0fsA0dH7ANHR+wDR0fs0z8/79c/P+//Q0Pv/1dT8/9nZ + /P/Z2v3/y8/6/66z9f+Ei+//QEDk/yIa3/8mH+D/Jh/g/yUe3/8lHt//Jh/g/yYf4P8mHuD/Jh/g/yYf + 3/8mH9//Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8kHOD/Ihzf/3B7 + 7/+bp/v/kp3x/3Z4h/+pqbL/urzM/7a3x/+2t8b/trfH/7a3x/+2t8b/trfG/7q7y/+xssD/f36D/2Ni + Yf90dHX/k5Ob/7K0wv+2usr/rbG//6eruP+kqLP/mp2n/5ebpf+Qk5v/jpKa/4+Tmv+PkZn/iIaN/4aE + iv+Hh43/j5Ka/4+Smv+Pkpr/kJOc/5eapP+bnqn/pKm0/6ituf+wtMP/ubzM/7u8zf+9vs7/vr/Q/7y9 + zv+7vM3/uLrK/7Cxv/+oqLP/oqOk/5SVkv5vdITpSk2hwScj3scmHuH/Jh7g/yYe4P8mH+D/JR/g/yYe + 3/8lH9//Jh/g/yYf4P8mHuD/Jh7g/yYf3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8lH9//JR7f/yYf4P8mHuD/JR/f/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8lHt//Jh/f/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jhze/yYc3/8mHN//JR/g/yUl4v8lLOX/JTbo/yQ+7P8kRO7/I0jw/yNJ + 8P8hR/D/H0Xw/xxC7/8cQe//HEHv/yNI8P8zVPH/Q2Hx/1t18/93ivX/hZX2/6Cs+P/l5/7/5uj9/56o + +P+irfn/6ez9/8DH+/+WoPj/m6X3/5ql+P+Pnff/fY/2/2N89P9GZfL/MFPy/x9G9P8cRPb/IUXy/yI/ + 3P8dLbT/GBqJ/xQQdP8TEHT/FBJ5/xUTev8VE3v/FRN5/xQSdP8bF5n/JR7Y/ycf5v8mH+D/Jh/g/yYf + 4P8mHN//JiHg/yUw5v8kP+z/I0jv/x9G8P8rUvH/4O3//8PBvf8CAgP/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8hIiL/rK27hCAW5QAmHN8AJhzfACYd3wAmHuAAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYe3wAmH98AJh7fACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAMvM+wDLzPsAy8z7AMvM + +wDLzPsAy8z7AMvM+wDLzPsAy8z7AMvM+wDLzPsAy8z7AMvM+wDLzPsAy8z7N9XV/PjZ2/3/1tf8/73D + +P+bn/P/aG/r/0FB5P8lJOD/GxTe/yEZ3v8nH+D/Jh/f/yYe4P8lHt//Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mH9//Jh/f/yYe3/8mHt//JR/g/yYe4P8lH9//JR7f/yYe4P8mHuD/Jh/f/yYe3/8lHt//Hhfe/1hi + 6/+ap/j/laD3/5ai/v+GjtD/fHt+/7Kzv/+6vMv/trfG/7a3xv+2t8b/trfG/7a3xv+2t8b/ubrK/7i5 + yf+amqT/ent+/3Bwcf+XmKL/u7zM/7m6yv+6u8v/urvL/7q7zP+6u8z/urzM/7q7zP+6vMz/urzM/7q8 + zf+6vc3/urzM/7q7zP+6u8v/urvM/7q8zP+7vMz/u7zN/76/z/++wND/vL7P/7u8zP+3uMj/rK27/6Wm + sf+XmKL/iYqQ/H19gONzc3W9ampqnmZnZm1mZmRCbmxhJmJoegQoJ9sJJR3iSSYe4KUmHuDsJh7g/yYf + 3/8mH+D/Jh/g/yYf4P8mHt//Jh7f/yUf3/8lH9//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yUe3/8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8lHt//JR7f/yYf + 3/8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yUe3/8mHd//Jhzf/yYc3/8mHt//JiTh/yUp + 4/8lNOf/JDzr/yND7f8jSPD/I0nw/yNJ8f8hRu//HkPv/x1C7/8cQe//HkPv/yZK8P9ObfP/0dv9/9bd + /f+Jmvb/qLL5/+3u/v+3v/n/k573/5Sf9/+VoPf/l6H3/5qk9/+bpff/mqX4/42b9v90iPX/T23y/ytO + 8f8bQvP/Ikn3/yNG6/8gNcb/GSCV/xQSeP8UD3P/FRJ4/xUTe/8UE3r/FBJ0/xcVhP8hHL//Jh/l/ycf + 5P8mH+D/Jh7g/yYe4P8mHN7/Jh/f/yUn4/8bKOX/WHDx//////9xcHD/AAAA/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/LCws/8HBvavGxsYAxsbGAMbGxgDGxsYAJh7gACYe4AAmHuAAJh7gACYe + 4AAmHuAAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmHt8AJh/fACYe3wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACJmvAAiZrwAIma + 8ACJmvAAiZrwAIma8ACJmvAAiZrwAIma8ACJmvAAiZrwAIma8ACJmvAAiZrwAIma8EejqvT/hYrw/1JW + 5/8yMuL/HRrf/xwU3v8fF9//JBzg/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yUf4P8lHuD/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8lH+D/Jh/f/yYe4P8mH+D/Jh/g/yYe3/8mH9//Hxfe/0hM + 5/+Vovf/lqL3/5Sg9/+UoPj/l6P+/4GIvv99fH7/r6+5/7y9zv+3uMj/trfG/7a3xv+2t8b/trfH/7a3 + x/+3uMf/u7zM/7m6yv+ztMP/t7fH/7a3xv+2t8b/trfH/7a3x/+2t8b/trfH/7a3x/+2t8f/trfH/7a3 + x/+2t8b/trfG/7a3xv+3uMj/ubrK/7u8zf+6u8z/urvL/7W2xf+rrLn/oqOu/5WVnv+FhYv7e3t+2XBw + crNqammVZ2dlYGdnZTpoaGYfaGdmBGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gKiYf + 4HwmHuDMJh/g/yYe4P8mH+D/Jh7f/yUf3/8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/JR7f/yYe + 4P8mHt//Jh7g/yYe4P8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/f/yYf + 4P8mHuD/JR7f/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh7g/yYd + 4P8mHd//Jhzf/yYe3/8mIuH/JSnk/yUz5/8kPOv/JETt/yNI8P8jSfH/I0nw/yNH7/8TOe7/U3T0/9Xh + /f+ds/n/HkXw/3KL9v/i6P7/jJ33/4ya9v+bpfj/m6X4/5ei9/+UoPf/lKD3/5Wg9/+Xovf/mqT3/5yl + +P+Kmff/VnDz/yJH7/8fRPL/JEr3/yRI8f8hO9T/GyWi/xYVfv8UD3L/FBF2/xUTe/8UEnb/FBN3/xwY + n/8kHtT/JyDn/yYf4/8mH+D/Jh/g/yYe4P8mHN//GhHd/56j9P/7+vP/Ly8w/wAAAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/zg4OP/Dw8TPxsbGDcbGxgDGxsYAxsbGAMbGxgDGxsYAxsbGAMbG + xgDGxsYAxsbGAMbGxgDGxsYAxsbGAMbGxgDGxsYAJR7fACUe3wAmH+AAJh7fACYf3wAmHt8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AALSzhAC0s + 4QAtLOEALSzhAC0s4QAtLOEALSzhAC0s4QAtLOEALSzhAC0s4QAtLOEALSzhAC0s4QAtLOFIJiTf/xwV + 3/8dFd7/IRnf/yYd4P8mH+D/Jh7g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/JR/g/yYf3/8mH9//Jh7g/yYe4P8mHuD/Ihrf/zY4 + 5PmNm/XsmKT48JSf9/qUoPf9lKD3/5Sg+P+Wo///g4vG/3Z3f/+cnKH/uLnJ/7u8zP+3uMj/trfG/7a3 + xv+2t8b/trfG/7a4x/+3uMf/t7nI/7a3xv+2t8b/trfG/7a3x/+2t8f/trfG/7a3xv+2t8f/trfH/7e4 + yP+5usr/u7zM/7q7y/+6usv/tLXF/6mquP+goKv/kZKZ/4SEifR4eHrRb29wrWlpaItmZmVTZ2ZmNGho + ZxdwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf + 4AAmH+AAJh7gDSYe30kmHt+gJh/g5iYf4P8mH9//Jh/g/yYf4P8mH9//Jh/g/yYf4P8mH+D/Jh7f/yYe + 3/8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYe + 3/8mH+D/Jh/g/yUe3/8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe + 3/8mH+D/Jh/g/yYf4P8mH+D/Jh3g/yYc3/8mHN//Jh7f/yYi4P8lKeP/JTPn/yQ97P8gQO3/Iknw/6a7 + +v/a5v7/Y4P1/w007v+Goff/ucv8/ytQ8P8wUfD/UW3z/3GF9f+Pnfb/m6b4/5qk9/+Woff/laD3/5Sg + 9/+VoPf/mKL3/52n+P92i/X/Kk7w/x9D7v8jR/H/JEn1/yRK9f8iQN7/HSux/xcZiP8UEHP/FBB2/xQS + ef8UEnT/FhSA/x4arv8mHtv/Jx/n/yYf4/8mH+D/Ihrf/yop4f/X4f7/1dPN/wUFBf8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP9MTE3/zc3N7sTExRzExMUAxMTFAMTExQDExMUAxMTFAMTE + xQDExMUAxMTFAMTExQDExMUAxMTFAMTExQDExMUAxMTFAMTExQAlHt8AJh/gACYe3wAmH98AJh7fACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACMb + 3wAjG98AIxvfACMb3wAjG98AIxvfACMb3wAjG98AIxvfACMb3wAjG98AIxvfACMb3wAjG98AIxvfSCQb + 3/8lHt//JB3g/yEY3/8cFN7/GhPe/xwW3v8dF97/Hxff/yYe4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf + 4P8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mHt//Jh7g/yYf4P8mH+D/Jh7f/yYf4P8mHuD/Jh/g/x4V + 3vNNT+hMnar4IpSg9yiUn/cxlKD3P5Wg91CUn/dslaD4hZej/5mNmOWsdXiTxH18fvOfoKr/trfG/7y8 + zf+6u8z/uLnI/7e3x/+3uMf/trfG/7a3x/+2t8f/trfH/7a3x/+3uMf/uLnJ/7q7y/+7vM3/urzM/7m6 + yv+1tsb/qqu4/6Chqv+RkZn/goKI8HZ2eMtub26maGhngGdnZU9nZ2YraGhnEW9vcABpaWgAZmZlAGdm + ZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4CAmHuBmJh7fuSYe3/kmHuD/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH9//Jh/f/yUf3/8lHt//Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh7f/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe3/8mHd//JR3e/yYc3/8mHt//GRjg/3KB + 7//b4v3/o7b5/x9E7v8oTvD/wdH8/5Ss+f8cQe//HkLv/x1B8P8fRO//MVPw/1Jv8/94jPX/laH3/5ym + +P+Zo/f/laD3/5Sg9/+Un/f/nqf3/3eM9f8jSO//IUXw/yNH7/8jR/D/I0n0/yRK9v8jROn/HzPC/xke + k/8UEXf/FBBz/xQRdv8UEnX/FxWH/x8asv8lHtn/Jh/h/x8W3/9OVef//////5OTkv8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/cHBx/8/PzvvDw8Q1w8PEAMPDxADDw8QAw8PEAMPD + xADDw8QAw8PEAMPDxADDw8QAw8PEAMPDxADDw8QAw8PEAMPDxADDw8QAw8PEACYf4AAmHt8AJh/fACYe + 3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAgFt8AIBbfACAW3wAgFt8AIBbfACAW3wAgFt8AIBbfACAW3wAgFt8AIBbfACAW3wAgFt8AIBbfACAW + 31obE97/HRbf/yUh4P84O+P/UU/n/2dp6v96hO7/h4/w/2hy6v8mIeD/JR7g/yYe4P8mHuD/Jh/g/yYf + 4P8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/f/yYe3/8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf + 4P8kHOCQTU/oAJ2q+ACUoPcAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QdmZmIwaWlnjnp7 + feSUlZz/paay/7Gywf+4usn/ubrK/7m7zP+6u8z/ubrK/7m6yv+5usr/t7jI/6+vvf+mp7P/n5+p/5CR + mf+Dg4j1dnZ41G5ub6hoaGh6Z2dlSWdnZidoaGgMbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZm + ZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd + 4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wImHuAzJh/ggiYe4M0mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYe3/8mH+D/Jh/g/yYf + 4P8mH+D/Jh7f/yYf4P8mH9//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7f/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/HhTf/z9C + 4//Hy/j/z9T7/0xR5v8QD9//Z3fu/9ji/f9fe/L/Gj/u/yNJ8P8jSfD/Ikfv/x9E7/8cQe//Ikfv/zhZ + 8f9fePP/g5T2/5ik9/+cpff/mKL3/5ah9/+dpvf/Smjy/x1B7/8jR/D/I0fv/yNH7/8jR/D/I0jz/yRK + 9/8jR+//ITrR/xslov8WFX7/FBBy/xMPb/8TEG7PHhqoVyUe5FQGANtjjJHxuf////9sbG3/AAAA/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/CAgI/Kampv7Kysv/xMTEYMTExADExMQAxMTEAMTE + xADExMQAxMTEAMTExADExMQAxMTEAMTExADExMQAxMTEAMTExADExMQAxMTEAMTExADExMQAJh7fACYf + 3wAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAI0bvACNH7wAjR+8AI0bvACNG + 7wAjRu8AP0DkAD9A5AA/QOQAP0DkAD9A5AA/QOQAP0DkAD9A5AA/QOQAP0DkAD9A5AA/QOQAP0DkAD9A + 5AA/QOReZ2vr/4uT8f+ztfb/wcf5/9DT+//Z2v3/2Nj9/+Df/v+vtvb/KSbh/yMc4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYe + 4P8mH+DWJh/gFyYf4ACdqvgAlKD3AJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlp + ZwBoaGcdZ2dmW2xra5lzc3W8fX1/5IWGjPeOj5f9kZKa/omKkPmGhov2f3+D6nh4etFycnOzbGxtmWlp + Z3lnZ2ZJZ2dmJ2lpaBJubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlp + aABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn + 2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AMJh/gRiYf + 4J0mH+DiJh/g/yYf4P8mHuD/Jh7f/yYf4P8mHt//Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYe4P8mHuD/Jh7g/yYe4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH9//JBzg/x4Z + 3/+Wn/H/4OT9/4KL7/8YD93/Lyzh/7zD9/+7wvf/LzDi/yIk4v8lM+j/JD7r/yRF7v8jSe//I0nw/yJH + 8P8eQ+//HEHv/yZL8P9AYfH/Z3/0/4WW9v+Xovf/oKn3/1x38/8cQu//I0bv/yNH7/8jRvD/I0fw/yNG + 8P8jR+//I0fx/yRK9f8kSfT/Ij/d/xwssf8ZIpuoFBN4Dx4aqAAlHuQABgDbAOzr/ab+/v7/VFVV/wAA + AP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/woKCrCrqqvNysrL/8PDxIbDw8QAw8PEAMPD + xADDw8QAw8PEAMPDxADDw8QAw8PEAMPDxADDw8QAw8PEAMPDxADDw8QAw8PEAMPDxADDw8QAw8PEAMPD + xAAmH98AJh7fACYf4AAmH+AAI0fvACNH7wAjRvAAI0fwACNG7wAjR+8AI0fvACNG7wAjR+8AI0fvACNG + 7wAjRu8AI0bvALS99gC0vfYAtL32ALS99gC0vfYAtL32ALS99gC0vfYAtL32ALS99gC0vfYAtL32ALS9 + 9gC0vfYAtL32etjZ/f/Y2P3/1dX8/9PT/P/R0Pv/z8/7/8/P+//T0vv/wcT5/zMu4v8iGt//Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mHt//JR7f/yYf4P8mH+D/Jh7f/yYe3/8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf + 4P8mH+DzJh/gPSYf4AAmH+AAnar4AJSg9wCUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZm + YgBpaWcAaGhnAGdnZgBsa2sAampoBWloZxloaGYpaGhmP2dnZkRnZ2YuZ2dmJWhoZh1paWgQcnJzAGxs + bQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9v + cABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJo + egAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gGCYf4F4mH+CuJh7g8yYf3/8mH+D/Jh7f/yYf4P8mH9//Jh/f/yYf4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mHt//Jh/f/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8mHuD/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf3/8mH+D/Jh7g/yYf4P8mH+D/Ixvf/xcQ + 3f9mb+r/2uD8/6ev9P8nI+D/GBLd/4uT8f/e4/3/bnXs/x0U3v8mHd//Jhzf/yYf4P8mJOL/JS/l/yU5 + 6f8kQ+3/I0jw/yNJ8P8hRvD/HkLv/x1D7/8pTfD/PF3x/0lp8/8uUPD/IUXw/yNG7/8jR/D/I0bv/yNH + 8P8jR/D/I0fv/yNH7/8jR+//I0fx/yNJ8/8kSvb/JEjx5CJC5p8gQutFJEXvAc7Z/AL////T8vLy/zMz + M/8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP89PT1MyMjIm8XFxv/Dw8ShxMPEAMTD + xADEw8QAxMPEAMTDxADEw8QAxMPEAMTDxADEw8QAxMPEAMTDxADEw8QAxMPEAMTDxADEw8QAI0fvACNG + 7wAjR/AAI0fvACNH7wAkR/AAI0fwACNH7wAjR+8AI0bwACNH8AAjRu8AI0fvACNH7wAjRu8AI0fvACNH + 7wAjRu8AI0bvACNG7wDU0/wA1NP8ANTT/ADU0/wA1NP8ANTT/ADU0/wA1NP8ANTT/ADU0/wA1NP8ANTT + /ADU0/wA1NP8ANTT/H/Pz/v/z8/7/8/P+//Pz/v/z8/7/8/P+//Pz/v/0dD7/9HS+/9ESOX/IBjf/yYe + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mHt//Jh/g/yYe4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/gcSYf4AAmH+AAJh/gAJ2q+ACUoPcAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ + 0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJy + cwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGho + ZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5s + YQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4CkmH+B4Jh/gxSYf4PwmH+D/JR7f/yYe3/8mH+D/Jh/g/yYe + 4P8mH+D/Jh7g/yYf4P8mHuD/Jh7f/yYf3/8mHuD/Jh7f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/f/yYf4P8mH+D/Jh/g/yYf4P8mH9//Jh/g/yYf4P8kHOD/HBPe/yEe + 3/9ze+z/1tv7/6+39f8vLeH/FhHd/3qD7f/c4fz/oar0/ygm4P8jG9//Jh/g/yYf4P8mHuD/Jh7g/yYd + 3/8mHt//JiLg/yUq5P8lNun/JUHs/yNI7/8jSPD/IUfw/x5E7/8eQu//IUTv/yNH8P8jR+//I0fv/yNH + 7/8jR/D/I0bv/yNG7/8jR+//I0fv/yNG7/8jR+//I0fw/yNH8f8jSPL/I0jx/ho/8LxCYPFx/v//8OXk + 4/8YGBn/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADKZmZmBtfX137DwsP/xMTEycTD + xAnEw8QAxMPEAMTDxADEw8QAxMPEAMTDxADEw8QAxMPEAMTDxADEw8QAxMPEAMTDxADEw8QAxMPEACNH + 7wAjRu8AI0fwACNH7wAjR+8AJEfwACNH8AAjR+8AI0fvACNG8AAjR/AAI0bvACNH7wAjR+8AI0bvACNH + 7wAjR+8AI0bvACNG7wAjRu8AqbL2AKmy9gCpsvYAqbL2AKmy9gCpsvYAqbL2AKmy9gCpsvYAqbL2AKmy + 9gCpsvYAqbL2AKmy9gCpsvaT0dH7/8/P+//Pz/v/z8/7/9DP+//Q0fv/1tX8/9zb/f/R1fz/Q0fl/yAY + 3/8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mHt//Jh/g/yYf4P8lHt//Jh/g/yYf + 4P8mH+D/Jh/fmyYf4AAmH+AAJh/gACYf4ACdqvgAlKD3AJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y + 5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlp + aABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdn + ZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZm + ZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AYmH+A5Jh/gkCYf4NwmHuD/Jh/g/yYf + 4P8mH+D/Jh7g/yYd3/8mHuD/JR7g/yUd3/8lHd//Jh7g/yUd3/8mHuD/Jh7g/yYe3/8lHd//JR3f/yUd + 3/8lHd//JR7g/yUd3/8lHd//JR7f/yUd4P8kHOD/JB3g/yQd4P8kHOD/Ihng/x4U3/8bFN7/JiPg/1RZ + 6P+osPT/1tz8/46U8P8kIuD/NTXj/5qh8v/Z4Pz/n6j0/zIx4v8gGN//Jh/g/yYf4P8mH+D/Jh7g/yYe + 4P8mHuD/Jh7f/yYd4P8mHd//Jh3f/yYg4P8mKOP/JTTo/yRB7P8jR+//I0nw/yNH8P8jR/D/I0fv/yNG + 7/8jR+//I0fv/yNG7/8jR+//I0fw/yNH8P8jRu//I0fw/yNH7/8jR+//I0fv/yNH7/8cQe//QmTy//7/ + ///Pz83/BQUF/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAAY2ZmZgDW1tdkw8PD/8TE + xOrExMQXxMTEAMTExADExMQAxMTEAMTExADExMQAxMTEAMTExADExMQAxMTEAMTExADExMQAI0fvACNG + 7wAjR+8AI0bvACNH8AAjR+8AI0fvACRH8AAjR/AAI0fvACNH7wAjRvAAI0fwACNG7wAjR+8AI0fvACNG + 7wAjR+8AI0fvACNG7wAjRu8AI0bvAMPG+QDDxvkAw8b5AMPG+QDDxvkAw8b5AMPG+QDDxvkAw8b5AMPG + +QDDxvkAw8b5AMPG+QDDxvkAw8b5oNDQ+//Q0Pv/09L8/9nY/f/X2Pz/zc/7/6yy9v+EiO//VVnn/yci + 3/8lHd//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/JR7f/yYf4P8mH+D/Jh/f/yYe + 4P8mHuD/Jh7gySYe3xEmHt8AJh7fACYe3wAmHt8AJh7fAJSg9wCUn/cAlKD3AJWg9wCUn/cAlaD4AJej + /wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGho + ZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdn + ZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZn + ZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AQJh/gUiYf + 4KQmHuDqJh7g/yEa3/8dGN7/Hxrf/x8Z3/8fGd7/Hxrf/x8a3/8fGt//Hxne/x8a3/8fGt7/Hhne/yAa + 3/8iHd//Ix3g/yQe4P8jHt//JB7f/yMd3/8mIOD/KSPg/ygi4P8rJOH/Lynh/zs/4/9YWuj/gYbu/663 + 9f/ByPj/m6Py/1Rd5/9CR+T/hYzu/8fR+f+zvPb/YWjq/ycj4P8hGN//Jh/f/yYe4P8mHuD/Jh/g/yYe + 4P8mHuD/Jh/g/yUe3/8mHuD/Jh/g/yYf4P8mHeD/Jhzf/yYd3/8mH9//Jijj/yU06P8kQOv/I0fv/yNJ + 8P8jSPD/I0fv/yNG7/8jR+//I0fv/yNH8P8jR/D/I0bv/yNH7/8jR/D/I0fv/yNH7/8jRu//Gz/u/1R3 + 9P//////m5ub/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAAyAAAAArS0tIA0tLSQsTE + xPvExMT9xMTELcTExADExMQAxMTEAMTExADExMQAxMTEAMTExADExMQAxMTEAMTExAAjR+8AI0fvACNH + 7wAjRu8AI0fvACNG7wAjR/AAI0fvACNH7wAkR/AAI0fwACNH7wAjR+8AI0bwACNH8AAjRu8AI0fvACNH + 7wAjRu8AI0fvACNH7wAjRu8AI0bvACNG7wDHy/oAx8v6AMfL+gDHy/oAx8v6AMfL+gDHy/oAx8v6AMfL + +gDHy/oAx8v6AMfL+gDHy/oAx8v6AMfL+p/b2v3/0tX8/8DD+f+WnfL/bW/r/0JD5P8nI+D/HBff/x8W + 3/8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh7g/yYe4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g4CYe4CQmHt8AJh7fACYe3wAmHt8AJh7fACYe3wCUoPcAlJ/3AJSg9wCVoPcAlJ/3AJWg + +ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdn + ZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGho + ZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpq + agBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe + 3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJRzgHCYh4GVda+m5go3u+H6L7v9/jO7/fYnt/3uI7f98iO3/e4jt/3uI7f97iO3/fInt/3+L + 7v+Fku7/kp/y/46c8f+MmvD/jJrx/4yY8f+KlvH/lqDy/5qk8v+WoPL/naPz/6ip9f+VnvH/j57x/42W + 8P92e+z/ZXTq/3l/7f+hqvP/xs75/6u09f9iaOn/KSbh/x0V3/8kHOD/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYe3/8mH+D/Jh7g/yYd3/8mHd//JiDg/yUp + 4/8lNej/JEHs/yNH7/8jSfD/I0jw/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0fw/yNH7/8jRu//I0bv/xs/ + 7v9UdvT//////4mJif8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/QAAAFYAAAAAzs7MAM7O + zB7Ew8Tqw8PE/8PDxFbDw8QAw8PEAMPDxADDw8QAw8PEAMPDxADDw8QAI0fwACNH8AAjR+8AI0fvACNH + 7wAjR+8AI0bvACNH7wAjRu8AI0fwACNH7wAjR+8AJEfwACNH8AAjR+8AI0fvACNG8AAjR/AAI0bvACNH + 7wAjR+8AI0bvACNH7wAjR+8AI0bvACNG7wAjRu8AeoruAHqK7gB6iu4AeoruAHqK7gB6iu4AeoruAHqK + 7gB6iu4AeoruAHqK7gB6iu4AeoruAHqK7gB6iu6fhInv/1RZ5/8yLuH/IRzf/xwV3/8hGN//JBzg/yYe + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf + 4P8lH+D/Jh/g8SYf4EQmHuAAJh7fACYe3wAmHt8AJh7fACYe3wAmHt8AlKD3AJSf9wCUoPcAlaD3AJSf + 9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdn + ZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5v + bgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhn + ZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe + 4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/DBzeux7Ulfnyl9l6f1jaer/ZWvq/2hv6v9pb+v/aG7r/2tx + 6/96fu3/e3/t/3p+7f9tc+v/Zmzq/2ht6/9veuz/d4nt/3OF7P9jder/boHs/2x37P9tcuz/dnrs/4KK + 7/+JmfD/pa30/7a99/+krfX/fILu/0dL5v8lIuD/Hhbf/yQb4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYe + 3/8mHd//Jh3f/yYh4P8lKuT/JDjp/yRD7v8jSfH/I0nw/yNI7/8jRu//I0fw/yNH7/8jR/D/I0fw/yNH + 8P8bQO//VHb0//////+trKz/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wIDB7kAAAAAJEv/AMnI + wgDJyMIIxcTE18PDxP/ExMR+xMTEAMTExADExMQAxMTEAMTExAAjR+8AI0fwACNH8AAjR/AAI0fvACNH + 7wAjR+8AI0fvACNG7wAjR+8AI0bvACNH8AAjR+8AI0fvACRH8AAjR/AAI0fvACNH7wAjRvAAI0fwACNG + 7wAjR+8AI0fvACNG7wAjR+8AI0fvACNG7wAjRu8AI0bvACUh4AAlIeAAJSHgACUh4AAlIeAAJSHgACUh + 4AAlIeAAJSHgACUh4AAlIeAAJSHgACUh4AAlIeAAJSHgoR0X3/8eFN//Ixrf/yMc3/8jG9//IRnf/yAW + 3/8eFN7/IBff/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYe32ImH+AAJh7gACYe3wAmHt8AJh7fACYe3wAmHt8AJh7fAJSg9wCUn/cAlKD3AJWg + 9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdn + ZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGho + aABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGho + ZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf + 4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ASBgu48yMr5kqes9N2Sl/D/k5jx/5SZ + 8f+TmfH/kpfx/5GX8f+Rl/H/k5nx/5SZ8f+Di+//g43v/4SQ7/+Fke//h5Pv/4SQ7/97iO3/f43u/32B + 7f92dez/X2Pp/0dM5f8zMOL/JB/g/xwW3v8gGN//JR3f/yYe3/8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf + 3/8mH+D/Jh/g/yYf4P8mHuD/Jh3f/yYd3/8mIuH/JS7l/yQ86/8kRu//I0nw/yNH8P8jR+//I0fw/yNH + 7/8jR/D/Gz/v/1N28///////5+bl/xkZGv8AAAD/AAAA/wAAAP8AAAD/AAAA/wUGCf8gOrDqJUz/jCRL + /y0gQ/AAycjCAM/NwbHExMT/xMTEnsnHwQDJx8EAycfBAGqG9QAZPu8AI0fvACNH8AAjR/AAI0fwACNH + 7wAjR+8AI0fvACNH7wAjRu8AI0fvACNG7wAjR/AAI0fvACNH7wAkR/AAI0fwACNH7wAjR+8AI0bwACNH + 8AAjRu8AI0fvACNH7wAjRu8AI0fvACNH7wAjRu8AI0bvACNG7wAlHeAAJR3gACUd4AAlHeAAJR3gACUd + 4AAlHeAAJR3gACUd4AAlHeAAJR3gACUd4AAlHeAAJR3gACUd4JMiGd//HhXf/xwV3v8jIN//Lyrh/z5A + 5P9QV+f/Y2fq/1FX5/8lIOD/JR7f/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh7g/yYf4H4mHt8AJh/gACYe4AAmHt8AJh7fACYe3wAmHt8AJh7fACYe3wCUoPcAlJ/3AJSg + 9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGho + ZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdn + ZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdn + ZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe + 3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAMIBrfUCkj + 4J4pJODnKiTh/yok4f8pI+D/KSPg/yok4f8pJOH/JSDg/yQg4P8kH+D/JB/g/yQf3/8iHd//Hxrf/x8Z + 3/8cFd7/GxLe/x4V3v8hF9//Ixvg/yQd3/8lHt//Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mH9//Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yUf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mH9//Jh7g/yYf4P8mH+D/Jh7g/yYc3/8mHuD/JSbi/yU05/8jQe3/I0jw/yNI + 8P8jR/D/I0fv/xtA7/9TdvP//v////////9hYWL/AAAA/wAAAP8AAAD/AAAA/wAAAP8YJmn/Jkz+/yNH + 8f8jR+/0IEPwpyRH7zWts8uKzszB/8PDxL/Jx8EEycfBAMnHwQBqhvUAGT7vACNH7wAjR/AAI0fwACNH + 8AAjR+8AI0fvACNH7wAjR+8AI0bvACNH7wAjRu8AI0fwACNH7wAjR+8AJEfwACNH8AAjR+8AI0fvACNG + 8AAjR/AAI0bvACNH7wAjR+8AI0bvACNH7wAjR+8AI0bvACNG7wAjRu8AIxvfACMb3wAjG98AIxvfACMb + 3wAjG98AIxvfACMb3wAjG98AIxvfACMb3wAjG98AIxvfACMb3wAjG99qNzbj/1pg6P+Ehe//m6Tz/7q+ + +P/Nz/v/0NP7/9zd/f+os/X/JyTg/yQc4P8mH+D/Jh/g/yYe4P8mH9//Jh/g/yYf4P8mH+D/Jh7g/yYe + 4P8mH+D/Jh/g/yYf4JYmH+AAJh7fACYf4AAmHuAAJh7fACYe3wAmHt8AJh7fACYe3wAmHt8AlKD3AJSf + 9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGho + ZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdn + ZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdn + ZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe + 3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa + 3wApI+AAJB3gFiQd4F8kHd+vJB3f8CQc3/8kHeD/JBzg/yUd3/8lHuD/JR3g/yUd4P8lHeD/JR3f/yUe + 4P8lHuD/Jh7f/yYe4P8mH+D/Jh/g/yYf4P8mHt//Jh7g/yYf4P8mH+D/Jh7f/yYf4P8mHt//Jh7f/yYf + 4P8mH+D/Jh7f/yYf4P8mH+D/Jh7f/yYe4P8lH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7f/yYd3/8mHd//JiDg/yUr + 5P8kOur/I0Xv/yNJ8P8bQe//VHbz//z+////////tLS1/wAAAf8AAAD/AAAA/wAAAP8LDyD/JUbe/yRI + 9v8jR+//I0fw/yNH7/8bQPH3TGvl5LC3yv/IxsDnqbPPCxtE8wCjufoAaob1ABk+7wAjR+8AI0fwACNH + 8AAjR/AAI0fvACNH7wAjR+8AI0fvACNG7wAjR+8AI0bvACNH8AAjR+8AI0fvACRH8AAjR/AAI0fvACNH + 7wAjRvAAI0fwACNG7wAjR+8AI0fvACNG7wAjR+8AI0fvACNG7wAjRu8AI0bvAE5S5wBOUucATlLnAE5S + 5wBOUucATlLnAE5S5wBOUucATlLnAE5S5wBOUucATlLnAE5S5wBOUucATlLnPb7D+PvY2f3/2tv9/9nX + /f/T0/v/0dH7/9DQ+//V1Pz/ur74/y8r4f8jHOD/Jh7g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/Jh7g/yYf4LAmH+AJJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGlo + ZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGho + aABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpq + aQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S + 8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4CMmH+BwJh/gwSYe3/UmH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe + 3/8mH9//Jh/g/yYf4P8mHt//Jh7g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf3/8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf3/8mH9//Jh7g/yYf4P8lH9//Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe + 4P8mHd//Jh3f/yUl4f8lMuf/HDrr/0tu8//6/P////////Pz8/8xMTL/AAAA/wAAAP8DAgD/Hzad/yVL + //8jRu//I0fv/yNG7/8jRu//I0fv/xtA8f8uUu3/2tzk+5Su8mgbRPMKo7n6AGqG9QAZPu8AI0fvACNH + 8AAjR/AAI0fwACNH7wAjR+8AI0fvACNH7wAjRu8AI0fvACNG7wAjR/AAI0fvACNH7wAkR/AAI0fwACNH + 7wAjR+8AI0bwACNH8AAjRu8AI0fvACNH7wAjRu8AI0fvACNH7wAjRu8AI0bvACNG7wCHmPAAh5jwAIeY + 8ACHmPAAh5jwAIeY8ACHmPAAh5jwAIeY8ACHmPAAh5jwAIeY8ACHmPAAh5jwAIeY8Aq3vffN1NP8/8/P + +//Pz/v/z8/7/8/P+//Pz/v/0ND7/87Q+/8/QeT/IRnf/yYe4P8mHuD/Jh/g/yYe4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4MUmHt8TJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpq + aABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5u + bwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBw + cgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf + 4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK + +QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AImH+A1Jh7gfiYe4M8mHuD8Jh/g/yUf + 3/8mH+D/Jh/f/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHt//Jh7g/yUe4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/JR7f/yYf4P8mH+D/Jh7g/yYf4P8mH9//Jh7f/yYf + 4P8mH+D/Jh/f/yYf4P8mHeD/Jhzf/x8a3/9BR+f/9/j+////////////d3d4/wAAAP8AAAD/Ex9P/yVL + +f8jR/L/I0fw/yNG7/8jR+//I0bv/yNH7/8hRe//Ikfw/+zu///c5/7/LVLwyqO5+l5qhvUKGT7vACNH + 7wAjR/AAI0fwACNH8AAjR+8AI0fvACNH7wAjR+8AI0bvACNH7wAjRu8AI0fwACNH7wAjR+8AJEfwACNH + 8AAjR+8AI0fvACNG8AAjR/AAI0bvACNH7wAjR+8AI0bvACNH7wAjR+8AI0bvACNG7wAjRu8Ah5jwAIeY + 8ACHmPAAh5jwAIeY8ACHmPAAh5jwAIeY8ACHmPAAh5jwAIeY8ACHmPAAh5jwAIeY8ACHmPAAnarzgdXU + /P/Pz/v/z8/7/8/P+//Pz/v/0dD7/9bV/P/Y2v3/R07l/yAX3v8mHuD/Jh7f/yYf4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4MwmH+AeJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxr + awBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlp + aABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGho + ZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf + 4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC + 7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAGJh7gQyYf + 4JAmH+DYJh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYe4P8mH+D/Jh/g/yUe3/8lHuD/Jh/g/yYe + 3/8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yUe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yUe3/8lH+D/Jh/g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mHt//JR/f/yYf4P8mH+D/Jh/g/yYe4P8fFd//T1Xl//r9/v///////////769vv8AAAD/BgsV/yRE + 0/8jSPn/I0bv/yNG7/8jRu//I0fv/yNH7/8jR+//IETv/yZJ8P/r7f7/2eT+/zJV8f+4yPv/aYX1yRk+ + 710jR+8KI0fwACNH8AAjR/AAI0fvACNH7wAjR+8AI0fvACNG7wAjR+8AI0bvACNH8AAjR+8AI0fvACRH + 8AAjR/AAI0fvACNH7wAjRvAAI0fwACNG7wAjR+8AI0fvACNG7wAjR+8AI0fvACNG7wAjRu8AI0bvAISQ + 7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ + 7yXHyPrr1dT8/9LS/P/W1fz/2dn9/9PV+/+4vPf/goju/y0q4f8kG+D/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/Jh/g/yYf4NomH+AiJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdn + ZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdn + ZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdm + ZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ + 3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh7gECYf4FEmHuCdJh/g4CYf4P8mH+D/Jh7f/yYe3/8mH+D/Jh7g/yUf3/8lH9//Jh/f/yYe + 4P8mHt//Jh/g/yYf3/8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8lH9//Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8lHuD/JR/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7f/yUf4P8mH+D/Jh/g/yYf4P8mHuD/HhXf/1Vg5v/8//7////////////j4+P/GhkS/xco + h/8mTP//I0jw/yNH8P8jR+//I0fw/yNH8P8jR/D/I0fw/yFF8P8lSPD/7e/+/8TU/P8tUfH/v838/159 + 9P8aPu7/I0fvyCNH8FwjR/AEI0fwACNH7wAjR+8AI0fvACNH7wAjRu8AI0fvACNG7wAjR/AAI0fvACNH + 7wAkR/AAI0fwACNH7wAjR+8AI0bwACNH8AAjRu8AI0fvACNH7wAjRu8AI0fvACNH7wAjRu8AI0bvACNG + 7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ7wCEkO8AhJDvAISQ + 7wCEkO8Ag4nvmtLW+//HzPr/tLj3/4eN7/9TVOf/King/xwU3v8kHN//Jh7g/yYe4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh7g/yYf4NsmH+AoJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGho + ZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdn + ZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZm + ZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd + 4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN6 + 7AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4BgmH+BXJh/gqSYf3+YmHuD/Jh/g/yYe4P8mH+D/Jh/f/yYf + 3/8mH+D/Jh/g/yYe4P8lHt//Jh/g/yYe3/8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yUe + 3/8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe3/8lH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/x8X3/9IR+X/+fv+///////////////8/2Jl + jf8aFNX/JSnn/yQ56f8jRu7/I0nw/yNI8P8jR/D/I0bv/yNG8P8hRe//Jkrw/+3w/v+/0Pv/L1bx/7rL + /P9QcvT/HEDv/yNH7/8jR/D/I0fwvCNH8EwjR+8AI0fvACNH7wAjR+8AI0bvACNH7wAjRu8AI0fwACNH + 7wAjR+8AJEfwACNH8AAjR+8AI0fvACNG8AAjR/AAI0bvACNH7wAjR+8AI0bvACNH7wAjR+8AI0bvACNG + 7wAjRu8ALCrhACwq4QAsKuEALCrhACwq4QAsKuEALCrhACwq4QAsKuEALCrhACwq4QAsKuEALCrhACwq + 4QAsKuEALCrhACwq4TJHR+X2QUPk/ycm4P8cFN7/HBTe/yMb4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe4NgmHuAoJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlp + ZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlp + ZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlp + aABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn + 2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre + /ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AgJh7gXyYe4K4mH+DrJh/g/yYe + 3/8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYe4P8lHt//Jh/g/yUf + 3/8mH+D/Jh/g/yYe4P8mH+D/Jh7f/yYe4P8mH+D/Jh7g/yYf4P8mH9//JR/g/yYe4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe3/8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8iGuD/MS7i/9vj+v////////////// + //+QnPX/GBHg/yYc3v8mHd7/JSXh/yU06P8jQ+3/I0nw/yNI8P8jR+//HkLw/zBY8f/3+v//rsD6/zdf + 8v+6zPv/Qmfz/x5B7/8jR+//I0fv/yNH8P8jR+//I0fvryNH7zcjR+8AI0fvACNG7wAjR+8AI0bvACNH + 8AAjR+8AI0fvACRH8AAjR/AAI0fvACNH7wAjRvAAI0fwACNG7wAjR+8AI0fvACNG7wAjR+8AI0fvACNG + 7wAjRu8AI0bvACwq4QAsKuEALCrhACwq4QAsKuEALCrhACwq4QAsKuEALCrhACwq4QAsKuEALCrhACwq + 4QAsKuEALCrhACwq4QAsKuEAHhXfnh8W3v8kHOD/Jh7g/yYf4P8mH+D/Jh7f/yYe3/8mH+D/Jh/g/yYe + 4P8mHt//Jh7g/yUe38kmHuAgJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZm + YgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxs + bQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9v + cABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJo + egAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r + 6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gKSYf + 4GkmHuC3Jh7g7CYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf + 4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHt//Jh7f/yYf3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8lHt//Jh7f/yYf3/8mH+D/Jh/g/yYe4P8mH+D/JR3g/xwW3v+epfH///////// + ////////aW/r/xkR3f8mHuD/Jh7g/yYd3/8mG97/JSHg/yUw5f8kQO3/I0nw/xM57/9lhPX//////5+x + +P9AZvP/vM38/zVZ8P8gQ+//I0fv/yNH7/8jR+//I0fw/yNH8P8jR/D1I0fvkCNH7x8jRu8AI0fvACNG + 7wAjR/AAI0fvACNH7wAkR/AAI0fwACNH7wAjR+8AI0bwACNH8AAjRu8AI0fvACNH7wAjRu8AI0fvACNH + 7wAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4BQmH+CuJh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4KomH+AYJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ + 0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJy + cwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGho + ZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5s + YQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh + 4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYe3yomH+BiJh/grCYf4N0mH+D/JR7f/yYe4P8mH+D/Jh/g/yYf3/8mH+D/Jh/g/yYe + 3/8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yUf3/8mH+D/Jh/f/yUe3/8mH9//Jh/g/yYe4P8mH9//JR7f/yUe + 3/8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8lHt//JR/g/yYf3/8fFt7/Pj/j/+bu + /P//////2OL6/ysr4P8iGd//JR/g/yUf3/8lH+D/JR/f/yUe3/8mHN//Jx/g/xkf4/86WO7/4+r+//// + //95k/f/U3Hz/77N/P8rTvD/IUXv/yNG7/8jRu//I0bv/yNH7/8jRvD/I0bv/yNH7/8jR+/gI0bvZSNH + 7wQjRu8AI0fwACNH7wAjR+8AJEfwACNH8AAjR+8AI0fvACNG8AAjR/AAI0bvACNH7wAjR+8AI0bvACNH + 7wAjR+8AI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gBiYe4GAmH+C7Jh/g6CYf4P4mH+D/Jh/g/yYf + 4P8mH+D1Jh7g0yYf4G8mH+AFJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y + 5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlp + aABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdn + ZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZm + ZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc + 4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AZJh/gRyUe34smHuDHJh/g9SYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/f/yYf3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH9//Jh/g/yUf + 4P8lHt//Jh/g/yYf4P8mHuD/Jh/g/yYf3/8mH9//Jh7g/yYe4P8mH+D/JR/f/yUf4P8mH+D/Jh/g/xwV + 3/9ZYOj/u7z1/1pf6P8cFN7/Jh/g/yUf3/8mHuD/Jh7f/yYf4P8mH+D/JiDf/xsR3v8yM+H/ztX4//// + ///u9P3/MFXw/4ae9/+uwvv/JUrw/yFF7/8jR+//I0bv/yNG8P8jR/D/I0fw/yNH7/8jR/D/I0fw/yNH + 7/8jR/CyI0bvLCNH8AAjR+8AI0fvACRH8AAjR/AAI0fvACNH7wAjRvAAI0fwACNG7wAjR+8AI0fvACNG + 7wAjR+8AI0fvACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gASYf4CUmHuBCJh/gRyYf + 4EcmH+BGJh/gNCYf4BImH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej + /wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGho + ZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdn + ZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZn + ZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gByYf4DcmH+B4Jh/gvCYf + 4PImH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yUe4P8mH+D/JR7f/yUf + 4P8mHuD/Jh/g/yYf4P8mHuD/Jh7f/yYf4P8mH9//Jh/f/yYe3/8mH9//Jh/g/yYf4P8mH9//Jh/g/yYf + 4P8mHt//Hhbf/xsU3v8eFt7/Jh/f/yYe3/8mHuD/Jh/g/yYf4P8mH+D/Jh/g/xkQ3v8xNeH/zdb4//// + ////////b3bq/0NP5//L1vv/l7D5/yBI8P8iR/D/I0fv/yNH7/8jR/D/I0fw/yNH8P8jR/D/I0bw/yNG + 8P8kRu//I0bw/yNG8OgjR/BpI0fvACNH7wAkR/AAI0fwACNH7wAjR+8AI0bwACNH8AAjRu8AI0fvACNH + 7wAjRu8AI0fvACNH7wAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg + +ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdn + ZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGho + ZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpq + agBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe + 3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd + 3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf + 4AMmHuAvJh7gaiYf4LUmH+DvJh/g/yYe4P8mHt//Jh/g/yYe4P8mHuD/Jh/f/yYf4P8mHt//Jh7g/yUe + 4P8lH9//Jh/f/yYf4P8mH+D/Jh/g/yUe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/f/yYf + 4P8mH+D/Jh/g/yYe4P8lHuD/JR/f/yYe3/8mH+D/Jh/g/yYf3/8mHuD/IBff/xQM3f8/ROT/z9n4//// + ///5/v7/eoDt/zA44v+3vfb/2978/3V+7f8bLef/I0Tu/yNK8P8jSPD/I0fw/yNH7/8jR+//I0bv/yNG + 8P8jR+//JEfw/yNG7/8jRu//I0fw/yNH76QjR+8bJEfwACNH8AAjR+8AI0fvACNG8AAjR/AAI0bvACNH + 7wAjR+8AI0bvACNH7wAjR+8AI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf + 9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdn + ZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5v + bgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhn + ZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe + 4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd + 4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh7gKyYf4GMmH+CuJh/f6iYf4P8mHuD/Jh7g/yYe3/8mHuD/Jh7f/yYf + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mHt//Jh7f/yQa3/8bE97/FxLe/zM14v+Jke//8PP9//// + ///h5vv/XWXo/zk95P+xuPb/2t38/6y29f8wL+H/Ixje/yYj4f8lM+f/I0Pu/yNK8f8jSfH/I0fv/yNH + 8P8jR+//I0fv/yNH7/8kRvD/I0fv/yNH7/8jR/D/I0fv1iRH8EgjR/AAI0fvACNH7wAjRvAAI0fwACNG + 7wAjR+8AI0fvACNG7wAjR+8AI0fvACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe + 3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg + 9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdn + ZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGho + aABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGho + ZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf + 4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd + 4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4CQmH+BhJh/gqyYf4OMmH+D/Jh7g/yUe + 3/8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mHt//Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/JR7g/yMa4P8eFd7/GBDe/xkR3v8kJOD/VFjo/56o8v/s8v3//////+Pn + +/+SmfD/RE3k/2Zu6v/ByPj/3N/8/7O89v87P+P/Hxff/yYf4P8mHd//Jh3f/yYh4P8lMeb/JELt/yNJ + 8f8jSfD/I0fv/yNG7/8jRu//JEbw/yNH8P8jR+//I0bv/yNH8P8jR+/3I0fwhiNH7wwjR+8AI0bwACNH + 8AAjRu8AI0fvACNH7wAjRu8AI0fvACNH7wAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf + 4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg + 9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGho + ZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdn + ZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdn + ZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe + 3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj + 4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AdJh/gVyYe + 4KUmH+DkJh/g/yUd3/8eFd7/Fw/e/xcP3v8XD97/Fw/e/xcP3v8XD97/Fg/d/xcP3v8XD97/Fw/e/xcP + 3v8XD97/FxDe/xgS3v8ZE97/HBXe/x8X3/8sLOH/Rknl/3Bz6/+krvP/2+D6//n+///8////ydL4/4uc + 7/9ncuv/d4Dt/7W79v/b4Pz/0tj7/6Kr9P88P+P/HRbf/yYf4P8mHt//Jh7g/yYf4P8mHuD/Jhzf/yYh + 4P8lL+b/JEHs/yNJ8f8jSfD/I0fw/yNG7/8jRu//I0fw/yNH7/8jR+//I0bv/yNG7/8jR/DCI0fvNCNG + 8AAjR/AAI0bvACNH7wAjR+8AI0bvACNH7wAjR+8AI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf + 9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGho + ZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdn + ZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdn + ZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe + 3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa + 3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe + 3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh/gHCUe31cmIuChS07m4m5r6v94c+z/enXs/3Vw6/91b+v/dG/r/3Nv6/9ybuv/cm7r/3Ju + 6/9ybuv/cm3r/3Fv6v99h+z/i5fv/56p8f+5vvX/ztX4/93p+//c5vr/z9r5/8nS+P+1w/b/pLHz/6Cs + 8/+0vPb/z9b6/9Xa+/+3wPf/h47v/1BW5/8lI+D/IBjf/yYf3/8mH9//Jh7f/yYf4P8mHuD/Jh7g/yYe + 4P8mHd//Jhzf/yYg4f8lLub/JEHt/yNK8f8jSPD/I0fv/yNH7/8jR+//I0fv/yNH8P8jR/D/I0bv/yNG + 7+8jRvBuI0fwASNG7wAjR+8AI0fvACNG7wAjR+8AI0fvACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGlo + ZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGho + aABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpq + aQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S + 8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf + 4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89Bn///9UzNb4nKe39N69zff/3Ob8//z////s9P7/2+f7/9zo + +//d6Pv/3en7/93p+//Y5fv/1uH6/9jj+//F0vj/tMP2/6Ox9P+ktPT/p7b0/7O+9/+xuvb/rbb1/6q0 + 9f+irfP/mJ7z/3l+7v9NUeb/LSrh/x0Y3v8eFt//JBzg/yYf4P8mH+D/Jh7f/yYe4P8mH+D/Jh7g/yYe + 3/8mH+D/Jh/g/yYf4P8mHuD/Jhzf/yYg4P8lL+X/JEDs/yNJ8P8jSPD/I0fw/yNG7/8jR/D/I0fw/yNH + 8P8jRu//I0bv/yNH8K0jRu8kI0fvACNH7wAjRu8AI0fvACNH7wAjRu8AI0bvACNG7wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpq + aABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5u + bwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBw + cgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf + 4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK + +QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYVusT4TZOb8pxqb+vfb3Ps/290 + 7P9vc+z/b3Ts/2907P9vdOz/b3Ts/2907P9vdOz/cHXs/3J27P9la+r/XWTp/11k6v9OVeb/Qj/k/zYx + 4/8sKeH/JSTg/x8a3/8dFN7/Hxjf/yMa3/8lHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh3f/yYg4P8lLuX/JEDs/yNJ8P8jSPD/I0fw/yNH + 7/8jRu//I0fv/yNH7/8jR+//I0fv4SNH71wjR+8AI0bvACNH7wAjR+8AI0bvACNG7wAjRu8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxr + awBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlp + aABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGho + ZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf + 4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC + 7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf + 4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf + 4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndEx0V + 30cdFd+SHRXf2B0V3/8dFd//HRXf/x0V3/8dFd//HRXf/x0W3/8dFd//Hxbf/x8X3v8fFt//IBff/yEZ + 3/8iGuD/Ixvg/yQc4P8lHuD/Jh7f/yYe4P8mH+D/Jh7g/yYf3/8mH+D/Jh/g/yYe3/8lHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYe3/8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mHt//Jhze/yYf3/8lLuX/JEDs/yNJ + 8P8jSfH/I0fw/yNG7/8jRvD/I0fv/yNH7/8jR/D/I0fvmSNG7xYjR+8AI0fvACNG7wAjRu8AI0bvACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdn + ZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdn + ZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdm + ZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ + 3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf + 4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ + 3QAdFd8AHRXfACYf4A4mH+BFJh7gkiYe39omHuD/Jh7f/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh7f/yYf + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYe3/8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mHt//JR7g/yYe + 4P8mH+D/JR7f/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/JR/g/yYf4P8mHd//Jh3f/yYg + 4P8lLuX/JEDs/yNJ8P8jSPD/I0bw/yNH8P8jR+//I0fw/yNH8P8jR+/RI0fvOyNH7wAjRu8AI0bvACNG + 7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGho + ZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdn + ZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZm + ZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd + 4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN6 + 7AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe + 3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb + 8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8NJh/gRiYf4JMmH+DYJh/g/SYe4P8mH+D/Jh/g/yYe + 3/8lHt//JR7f/yYe3/8lHt//Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yUe3/8mHt//Jh/g/yYf4P8mHt//Jh7g/yYf4P8mH+D/Jh7f/yUf4P8mHuD/Jh7g/yYf + 4P8mHd//Jh3f/yYg4P8lMOX/JELt/yNJ8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH8PIjR+9eI0bvAyNG + 7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlp + ZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlp + ZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlp + aABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn + 2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre + /ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE + +ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fCiYf4D0mH+CKJh7g0yYe + 4P0mHuD/Jh/f/yYe3/8mHt//Jh/f/yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh/f/yYf4P8mH+D/Jh/g/yYe + 3/8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH9//Jh/g/yYe + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh3f/yYg4P8lMub/JELt/yNI8P8jR/D/I0fv/yNH7/8jR+//I0fv+iNG + 74gjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZm + YgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxs + bQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9v + cABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJo + egAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r + 6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY + 5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe + 4AcmHuA9Jh/giSYf39MmH+D9Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8mHt//Jh7f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe3/8mHd//Jhze/yYi4P8lNOj/I0Xu/yNJ8P8jR/D/I0fw/yNG + 7/8jR/DEI0fwACNH8AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ + 0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJy + cwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGho + ZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5s + YQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh + 4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW + +ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf + 4AAmHuAAJh7gACYf4AAmHt8HJh7gPCYf4IAmH+DKJh/g+iYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7f/yYe3/8mH+D/Jh7g/yYf3/8mHt//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe3/8mHuD/Jh/g/yYe4P8mHd//Jhzf/yUm4/8lOur/I0jw/yNI + 8P8jRvD/I0fwviNH8AAjR/AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y + 5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlp + aABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdn + ZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZm + ZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc + 4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP// + /wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fBCYe4DUmH+B9Jh/gySYe4PcmH+D/Jh/g/yYe + 3/8mHt//Jh7g/yYf4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8lH9//JR7f/yYf4P8mH+D/Jh/g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHeD/Jh7f/yUt + 5f8kRe7/I0jw/yNH8IojR/AAI0fwACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej + /wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGho + ZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdn + ZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZn + ZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW8 + 9AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe + 3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AQlHt8zJR7fdSYe + 4MEmHt/0Jh7f/yYf4P8mH+D/Jh7f/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mHt//Jh7g/yYe + 4P8mHuD/JR/f/yYf3/8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mHuD/Jh7g/yYe + 4P8mG9//JTXo/yNJ8M4jRu8sI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg + +ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdn + ZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGho + ZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpq + agBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe + 3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd + 3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi + 4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf + 4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe + 3wAmHt8CJh/gLyYf4G4mHt+5Jh/g7iYf4P8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7f/yYe + 4P8mH+D/Jh/g/yYf4P8lHt//JR/f/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jhzf/yUw5sEjSvAxI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf + 9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdn + ZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5v + bgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhn + ZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe + 4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd + 4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe + 3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf + 4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe + 3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4CYmH+BkJh7grCYf4OMmH+D/Jh/f/yYe4P8mH+D/Jh7f/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/JR/g/yUe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh7g/yYc36klMeYKI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe + 3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg + 9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdn + ZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGho + aABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGho + ZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf + 4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd + 4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf + 4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe + 3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf + 4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAWJh7gUSYf4IEmHuDAJh/g5SYf + 4P8mH+D/Jh7g/yYe4P8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P0mH+DjJh/gtyYe31YlHN4BJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf + 4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg + 9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGho + ZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdn + ZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdn + ZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe + 3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj + 4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe + 4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf + 4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gAiYe + 4BwmHuBRJh/geCYf4LMmHuDYJh/g8CYe3/8lH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4OUmH+DFJh/glCYf + 4GwmH+BIJh7gFyYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf + 9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGho + ZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdn + ZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdn + ZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe + 3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa + 3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe + 3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf + 4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe + 4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe + 4AAmHuAAJh7gACYf4AAmH+AZJR7fOyUe328mH+CYJh/guCYe374mH+C+Jh7gvyYf4JwmH+BWJh/gJSYf + 4AsmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGlo + ZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGho + aABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpq + aQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S + 8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf + 4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf + 4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe + 3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpq + aABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5u + bwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBw + cgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf + 4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK + +QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V + 3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf + 4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxr + awBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlp + aABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGho + ZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf + 4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC + 7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf + 4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf + 4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V + 3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe + 4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdn + ZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdn + ZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdm + ZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ + 3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf + 4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ + 3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe + 3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe + 4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG + 7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGho + ZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdn + ZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZm + ZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd + 4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN6 + 7AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe + 3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb + 8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf + 4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG + 7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlp + ZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlp + ZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlp + aABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn + 2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre + /ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE + +ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe + 3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG + 7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZm + YgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxs + bQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9v + cABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJo + egAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r + 6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY + 5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe + 4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe + 3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf + 4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK + 8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ + 0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJy + cwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGho + ZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5s + YQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh + 4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW + +ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf + 4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf + 4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf + 4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx + 5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y + 5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlp + aABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdn + ZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZm + ZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc + 4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP// + /wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf + 4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe + 3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc + 3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej + /wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGho + ZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdn + ZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZn + ZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW8 + 9AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe + 3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe + 3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe + 3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe + 3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg + +ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdn + ZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGho + ZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpq + agBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe + 3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd + 3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi + 4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf + 4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe + 3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf + 4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf + 4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf + 9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdn + ZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5v + bgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhn + ZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe + 4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd + 4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe + 3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf + 4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe + 3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe + 3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg + 9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdn + ZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGho + aABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGho + ZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf + 4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd + 4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf + 4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe + 3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf + 4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf + 4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg + 9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGho + ZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdn + ZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdn + ZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe + 3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj + 4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe + 4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf + 4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf + 9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGho + ZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdn + ZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdn + ZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe + 3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwACAa + 3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe + 3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf + 4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe + 4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe + 4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGlo + ZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGho + aABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpq + aQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S + 8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf + 4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf + 4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe + 3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpq + aABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5u + bwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBw + cgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf + 4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN67AATCdwAgYLuAMjK + +QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V + 3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf + 4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxr + awBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlp + aABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGho + ZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf + 4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre/ABzeuwAEwncAIGC + 7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh7gACYf + 4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf + 4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V + 3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe + 4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdn + ZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdn + ZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdm + ZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ + 3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf + 4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ + 3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe + 3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe + 4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG + 7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGho + ZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdn + ZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZm + ZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd + 4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh4ABda+kA2t78AHN6 + 7AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAJB3gACQd3wAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe + 3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb + 8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf + 4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG + 7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlp + ZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlp + ZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlp + aABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn + 2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc4AAmIeAAXWvpANre + /ABzeuwAEwncAIGC7gDIyvkAjpLwACAa3wApI+AAJB3gACQd4AAkHd8AJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE + +ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe + 3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG + 7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZm + YgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxs + bQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9v + cABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJo + egAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAlHOAAJiHgAF1r + 6QDa3vwAc3rsABMJ3ACBgu4AyMr5AI6S8AAgGt8AKSPgACQd4AAkHeAAJB3fACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY + 5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe + 4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe + 3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf + 4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK + 8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ + 0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJy + cwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGho + ZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5s + YQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJRzgACYh + 4ABda+kA2t78AHN67AATCdwAgYLuAMjK+QCOkvAAIBrfACkj4AAkHeAAhYaJAIWGiQCCjLsAMi81ADIx + VAAODgwAJh7gACYe4AAmHuAAJh/gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW + +ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf + 4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf + 4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf + 4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx + 5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y + 5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlp + aABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdn + ZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZm + ZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACUc + 4AAmIeAAXWvpANre/ABzeuwAEwncAIGC7gDIyvkAjpLwAAcGHAAHBhwAEyeOAIWGiQCFhokAgoy7ADIv + NQAyMVQADg4MAB88vQAmHuAAJh7gACYf4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP// + /wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf + 4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe + 3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc + 3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej + /wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGho + ZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdn + ZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZn + ZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAlHOAAJiHgAF1r6QDa3vwAc3rsABMJ3AAjHMEAIxzBACMcwQAHBhwABwYcABMnjgCFhokAhYaJAIKM + uwAyLzUAMjFUAA4ODAAfPL0ABg4uAAMDEQAAAAAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW8 + 9AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe + 3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe + 3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe + 3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe + 3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg + +ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdn + ZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGho + ZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpq + agBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe + 3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJRzgACYh4AAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEABwYcAAcGHAATJ44AhYaJAIWG + iQCCjLsAMi81ADIxVAAODgwAHzy9AAYOLgADAxEAAAAAAJmn2QCZp9kAJh7gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi + 4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf + 4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe + 3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf + 4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf + 4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf + 9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdn + ZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5v + bgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhn + ZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe3wAmHt8AJh/gACYe + 4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBAAcGHAAHBhwAEyeOAIWG + iQCFhokAgoy7ADIvNQAyMVQADg4MAB88vQAGDi4AAwMRAAAAAACZp9kAmafZAJmn2QAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe + 3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf + 4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe + 3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf + 4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe + 3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg + 9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdn + ZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGho + aABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGho + ZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe4AAmHt8AJh7fACYf + 4AAmHuAAJh7fACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAHBhwABwYcABMn + jgCFhokAhYaJAIKMuwAyLzUAMjFUAA4ODAAfPL0ABg4uAAMDEQAAAAAAmafZAJmn2QCZp9kAmafZAJmn + 2QAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf4AAlHt8AJh7gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf + 4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe + 3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf + 4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf + 4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg + 9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGho + ZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdn + ZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdn + ZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf4AAmHuAAJh7fACYe + 3wAmH+AAJh7gACYe3wAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEABwYcAAcG + HAATJ44AhYaJAIWGiQCCjLsAMi81ADIxVAAODgwAHzy9AAYOLgADAxEAAAAAAJmn2QCZp9kAmafZAJmn + 2QCZp9kAmafZACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACYf4AAmH+AAJR7fACYe + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe + 4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf + 4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf + 4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf + 9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxrawBqamgAaWhnAGho + ZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdn + ZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGhoZwBwcHIAamppAGdn + ZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf4AAmH+AAJh7gACYe + 3wAmHt8AJh/gACYe4AAmHt8AJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBAAcG + HAAHBhwAEyeOAIWGiQCFhokAgoy7ADIvNQAyMVQADg4MAB88vQAGDi4AAwMRAAAAAACZp9kAmafZAJmn + 2QCZp9kAmafZAJmn2QCZp9kAmafZACYe4AAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAmH+AAJh/gACUe + 3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V3wAdFd8AJh/gACYf + 4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe + 4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe + 4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf + 4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdnZgBsa2sAampoAGlo + ZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGho + aABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdmZgBoaGcAcHByAGpq + aQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe4AAmH+AAJh/gACYe + 4AAmHt8AJh7fACYf4AAmHuAAJh7fACYe4AAmH+AAJh/gACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAHBhwABwYcABMnjgCFhokAhYaJAIKMuwAyLzUAMjFUAA4ODAAfPL0ABg4uAAMDEQAAAAAAmafZAJmn + 2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJh/gACYf + 4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf + 4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe + 3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG7wAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGhoZwBnZ2YAbGtrAGpq + aABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdnZgBnZ2YAaWloAG5u + bwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZmZQBnZmYAaGhnAHBw + cgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACUd4gAmHuAAJh/gACYf + 4AAmHuAAJh7fACYe3wAmH+AAJh7gACYe3wAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEABwYcAAcGHAATJ44AhYaJAIWGiQCCjLsAMi81ADIxVAAODgwAHzy9AAYOLgADAxEAAAAAAJmn + 2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAJh7gACYe3wAmH+AAJh/gACYf + 4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb8gARCd0AHRXfAB0V + 3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf4AAmHt8AJh7gACYf + 4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf4AAmHuAAJh7gACYe + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG7wAjRu8AJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe + 4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlpZwBoaGcAZ2dmAGxr + awBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlpZwBnZ2YAZ2dmAGlp + aABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlpaABmZmUAZ2ZmAGho + ZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAYmh6ACgn2wAlHeIAJh7gACYf + 4AAmH+AAJh7gACYe3wAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBAAcGHAAHBhwAEyeOAIWGiQCFhokAgoy7ADIvNQAyMVQADg4MAB88vQAGDi4AAwMRAAAA + AACZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QAmHt8AJh/gACYf + 4AAmH+AAJh/gACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE+ACTm/IAEQndAB0V + 3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe4AAmH+AAJh7fACYe + 4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf4AAmH+AAJh7gACYe + 4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe3wAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG7wAjRu8AI0bvACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZmYgBpaWcAaGhnAGdn + ZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxsbQBpaWcAZ2dmAGdn + ZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9vcABpaWgAZmZlAGdm + ZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgBmZmQAbmxhAGJoegAoJ9sAJR3iACYe + 4AAmH+AAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAHBhwABwYcABMnjgCFhokAhYaJAIKMuwAyLzUAMjFUAA4ODAAfPL0ABg4uAAMD + EQAAAAAAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn + 2QAmH+AAJh/gACYf4AAlHt8AJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY5gC6xPgAk5vyABEJ + 3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe4AAmHuAAJh/gACYe + 3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe3wAmH+AAJh/gACYe + 4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf4AAmHt8AJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK8AAjRu8AI0bvACNG + 7wAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh7gACYe4AAmH+AAJh/gACYf4AAmHt8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAlJ/3AJSg9wCVoPcAlJ/3AJWg+ACXo/8AjZjlAIiQ0QBmZmIAaWlnAGho + ZwBnZ2YAbGtrAGpqaABpaGcAaGhmAGhoZgBnZ2YAZ2dmAGdnZgBoaGYAaWloAHJycwBsbG0AaWlnAGdn + ZgBnZ2YAaWloAG5ubwBoaGgAZ2dlAGdnZgBoaGgAbm9uAGhoZwBnZ2UAZ2dmAGhoZwBvb3AAaWloAGZm + ZQBnZmYAaGhnAHBwcgBqamkAZ2dlAGdnZQBoaGYAaGdmAGpqagBmZ2YAZmZkAG5sYQBiaHoAKCfbACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEABwYcAAcGHAATJ44AhYaJAIWGiQCCjLsAMi81ADIxVAAODgwAHzy9AAYO + LgADAxEAAAAAAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn + 2QCZp9kAmafZACYf4AAmH+AAJR7fACYe4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmHuAAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJR7fACYi4AClvPQA////AMzW+ABIWOYAusT4AJOb + 8gARCd0AHRXfAB0V3wAmH+AAJh/gACYe4AAmHt8AJh/gACYf4AAlHt8AJh/gACYf4AAmHuAAJh7gACYf + 4AAmHt8AJh7gACYf4AAlHt8AJh7gACYf4AAmH+AAJR7fACUe3wAmHt8AJh/gACYf4AAmHt8AJh/gACYf + 4AAmHuAAJh7gACYe4AAmH+AAJh7gACYe4AAmHuAAJh/gACYf4AAlHt8AJR7fACYf4AAmH+AAJh7fACYf + 4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAmHt8AJRzeACUx5gAjSvAAI0bvACNG + 7wAjRu8AJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmH+AAJh7fACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gAJSf9wCUoPcAlaD3AJSf9wCVoPgAl6P/AI2Y5QCIkNEAZmZiAGlp + ZwBoaGcAZ2dmAGxrawBqamgAaWhnAGhoZgBoaGYAZ2dmAGdnZgBnZ2YAaGhmAGlpaABycnMAbGxtAGlp + ZwBnZ2YAZ2dmAGlpaABubm8AaGhoAGdnZQBnZ2YAaGhoAG5vbgBoaGcAZ2dlAGdnZgBoaGcAb29wAGlp + aABmZmUAZ2ZmAGhoZwBwcHIAamppAGdnZQBnZ2UAaGhmAGhnZgBqamoAZmdmAGZmZABubGEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBAAcGHAAHBhwAEyeOAIWGiQCFhokAgoy7ADIvNQAyMVQADg4MAB88 + vQAGDi4AAwMRAAAAAACZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn + 2QCZp9kAmafZAJmn2QCZp9kAmafZACUe3wAmHuAAJh/gACYf4AAmH+AAJh7gACYe4AAmH+AAJh7gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACUe3wAmIuAApbz0AP///wDM1vgASFjmALrE + +ACTm/IAEQndAB0V3wAdFd8AJh/gACYf4AAmHuAAJh7fACYf4AAmH+AAJR7fACYf4AAmH+AAJh7gACYe + 4AAmH+AAJh7fACYe4AAmH+AAJR7fACYe4AAmH+AAJh/gACUe3wAlHt8AJh7fACYf4AAmH+AAJh7fACYf + 4AAmH+AAJh7gACYe4AAmHuAAJh/gACYe4AAmHuAAJh7gACYf4AAmH+AAJR7fACUe3wAmH+AAJh/gACYe + 3wAmH+AAJh7gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmH+AAJh7fACUc3gAlMeYAI0rwACNG + 7wAjRu8AI0bvACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYf4AAmHuAAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmHuAAJh7gACYf4AAmH+AAJh/gACYe3wAmH+AAJh/gACYf4AAmH+AAJh/gACYf + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4ACUn/cAlKD3AJWg9wCUn/cAlaD4AJej/wCNmOUAiJDRAGZm + YgBpaWcAaGhnAGdnZgBsa2sAampoAGloZwBoaGYAaGhmAGdnZgBnZ2YAZ2dmAGhoZgBpaWgAcnJzAGxs + bQBpaWcAZ2dmAGdnZgBpaWgAbm5vAGhoaABnZ2UAZ2dmAGhoaABub24AaGhnAGdnZQBnZ2YAaGhnAG9v + cABpaWgAZmZlAGdmZgBoaGcAcHByAGpqaQBnZ2UAZ2dlAGhoZgBoZ2YAampqAGZnZgAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAjHMEAIxzBACMc + wQAjHMEAIxzBACMcwQAjHMEAIxzBACMcwQAHBhwABwYcABMnjgCFhokAhYaJAIKMuwAyLzUAMjFUAA4O + DAAfPL0ABg4uAAMDEQAAAAAAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn + 2QCZp9kAmafZAJmn2QCZp9kAmafZAJmn2QCZp9kAJh7gACYf4AAmH+AAJh/gACYe4AAmHuAAJh/gACYe + 4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmH+AAJh7gACYf4AAlHt8AJiLgAKW89AD///8AzNb4AEhY + 5gC6xPgAk5vyABEJ3QAdFd8AHRXfACYf4AAmH+AAJh7gACYe3wAmH+AAJh/gACUe3wAmH+AAJh/gACYe + 4AAmHuAAJh/gACYe3wAmHuAAJh/gACUe3wAmHuAAJh/gACYf4AAlHt8AJR7fACYe3wAmH+AAJh/gACYe + 3wAmH+AAJh/gACYe4AAmHuAAJh7gACYf4AAmHuAAJh7gACYe4AAmH+AAJh/gACUe3wAlHt8AJh/gACYf + 4AAmHt8AJh/gACYe4AAmH+AAJh/gACYf4AAmH+AAJh/gACYf4AAmHuAAJh/gACYe3wAlHN4AJTHmACNK + 8AAjRu8AI0bvACNG7wCIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI////// + //////////////////////////iIiIiIiIiIj///////////////////////////////+IiIiIiIiIiP + ///////////////////////////////4iIiIiIiIiI////////////////////////////////iIiIiI + iIiIj///////////////////////////////+IiIiIiIiIiP///////////////////////////////4 + iIiIiIiIiI////////////////////////////////iIiIiIiIiIj/////////////////////////// + ////+IiIiIiIiIiP///////////////////////////////4iIiIiIiIiI////////////////////// + //////////iIiIiIiIiIj///////////////////////////////+IiIiIiIiIiP//////////////// + ///////////////4iIiIiIiIiI////////////////////////////////iIiIiIiIiIj/////////// + ////////////////////+IiIiIiIiIiP///////////////////////////////4iIiIiIiIiI////// + //////////////////////////iIiIiIiIiIj///////////////////////////////+IiIiIiIiIiP + ///////////////////////////////4iIiIiIiIiI////////////////////////////////iIiIiI + iIiIj///////////////////////////////+IiIiIiIiIiP///////////////////////////////4 + iIiIiIiIiI////////////////////////////////iIiIiIiIiIj/////////////////////////// + ////+IiIiIiIiIiP///////////////////////////////4iIiIiIiAiI////////////////////// + //////////iIiIiIAACIj/////////////////////////////////iIiIgAAIiP//////////////// + /////////////////4iIiAAAiI///////////////////////////////////4iIgACIj/////////// + ////////////////////////+IiIgIiP////////////////////////////////////+IiIiI////// + ////////////////////////////////iIiIj//////////////////////////////////////4iIiP + //////////////////////////////////d////4iI////////////////////////////////93d3d3 + //+Ij///////////////////////////////d0AHd3d//4iP//////////////////////////////dw + AAd3d3d/iI//////////////////////////////cAAAB3d3d3eIj/////////////////////////// + //cAAAAAR3d3d4iP////////////////////////////cAAAAAAAR3d3iI////////////////////// + //////cAAAAAAAAAd3eIj///////////////////////////cAAAAAAAAAAHd4iIj/////////////// + //////////wAAAAAAAAAAAB3iIiP////////////////////////9AAAAAAAAAAAAAeIiI////////// + //////////////9wAAAAAAAAAAAAB4iIiP///////////////////////wAAAAAAAAAAAAAAiIiI//// + ///////////////////3AAAAAAAAAAAAAACIiIj//////////////////////3MAAAAAAAAAAAAAAIiI + iI//////////////////////cAAAAAAAAAAAAAAAiIiIj/////////////////////cwAAAAAAAAAAAA + AACIiIiI////////////////////9wAAAAAAAAAAAAAAAIiIiIj///////////////////9wAAAAAAAA + AAAAAAAAiIiIiI///////////////////3AAAAAAAAAAAAAAAACIiIiIiI//////////////////cAAA + AAAAAAAAAAAAAIiIiIiIj/////////////////cAAAAAAAAAAAAAAAAAiIiIiIiI//////////////// + 9wAAAAAAAAAAAAAAAACIiIiIiIiP///////////////3AAAAAAAAAAAAAAAAAIiIiIiIiIj///////// + //////cAAAAAAAAAAAAAAAAAiIiIiIiIiI//////////////9wAAAAAAAAAAAAAAAACIiIiIiIiIj/// + //////////93AAAAAAAAAAAAAAAAAIiIiIiIiIiI/////////////3cAAAAAAAAAAAAAAAAAiIiIiIiI + iIiP////////////dwAAAAAAAAAAAAAAAACIiIiIiIiIiIj///////////93AAAAAAAAAAAAAAAAAIiI + iIiIiIiIiI///////////3cAAAAAAAAAAAAAAAAAiIiIiIiIiIiIiIiIiIiP////dwAAAAAAAAAAAAAA + AACIiIiIiIiIiIiIiIiIiIiIiP93AAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiIiIiIiAAAAAAAAAAA + AAAAAAAAiIiIiIiIiIiIiIiIiIiIiIiAAAAAAAAAAAAAAAAAAACIiIiIiIiIiIiIiIiIiIiIgAAAAAAA + AAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiIiIiIiIiIiIgAAA + AAAAAAAAAAAAAAAAAACIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiI + AAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAACIiIiIiIiIiIiI + iIiIAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiI + iIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAIiI + iIiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAA + AACIiIiIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIAAAAAACAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIgAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAiIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIgAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiI + iIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiI + iIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AACIiIiIiIiIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIiIiIgAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAIiIiIiIiIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiIAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIiAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIiIiIAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiI + iAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiI + iIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiIiIiIiIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AACIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAiIiIiIiIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACIiIiIiIiIAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAIiIiIiIiIgAAAAAAAAAAAAAAAAAAAAAiIiIiIgAAAAAiIiIiIiIiAAAAAAAAAAAAAAA + AAAAAACIiIiIiIiAAACIiIiIiIiIAAAAAAAAAAAAAAAAAAAACIiIiIiIiIiIAIiIiIiIiIgAAAAAAAAA + AAAAAAAAAACIiIiIiIiIiIiIiIiIiIiIiAAAAAAAAAAAAAAAAAAACIiIiIiIiIiIiIiIiIiIiIiIAAAA + AAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiIiIiI + iAAAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiIiIiIiIAAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiI + iIiIiIgAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiIiIiIiIiIiIiAAAAAAAAAAAAAAAAIiIiIiIiIiIiIiI + iIiIiIiIiIiIAAAAAAAAAAAAAAAAiIiIiIiIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAAAAAiIiIiIiIiI + iIiIiIiIiIiIiIiIiIAAAAAAAAAAAAAAiIiIiIiIiIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAAAiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIAAAAAAAAAAAAiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIgAAAAAAAAAAAiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIAAAAAAAAAAiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIgAAAAAAA + AAiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIAAAAAAAAiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + AAAAAIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiI + iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiCgAAACAAAAAAAEAAAEA + IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAAB7AAAAnwAAAJ8AAACZAAAAdQAAAEIAAAAJAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABfAAAAyAAA + APkAAAD/AAAA/wAAAP8AAAD2AAAA1AAAAIQAAAAbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAaAAAAswABAP8GBhj/Cwsr/wsLK/8HByD/BAQO/wABAP8AAAD/AAAA/wAA + AOQAAABIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJQABANcPDkb/Hhut/yUe + 3f8kHer/IRnr/xwU5f8ZFNH/FBGs/wkIYv8AAA3/AAAA/wAAAPQAAAAxAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAB4DBAjaGhiQ/yMb9v8eFff/GxTn/yUh1/9AP8n/Z2bD/3+Bx/9/gc7/aGbQ/zc4 + n/8BAx3/AAAA/wAAAMAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAAAUzRURr/8gGu7/Mi/O/1ta + wP+RksH/xcfP/+3u5P////b////+/////v////T/7u3p/5SUoP8aGRf/AAAA/wAAAD4AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAKSgoBwICBbY5O4r/cnLO/6qsxf/e39r////x/////////////////////P/09fb/5er3/97m + ///d4///4+r//8HG0P85Ny7/AAAAlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbm5tJH5+fmaPj4/Burq7/+/v7f////n///////// + ///4+v//1N35/6m48v99kuz/V3Hn/zxa5/8tT+v/J0vw/yJG8P8lSvD/QmX+/1pwzf8aHCG9AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAG1tbAJwb28yg4KDeqam + psLQz9D18/Pz///////////////9/93j9/+is/T/aIPx/z1e7/8iRu7/Fz3u/xY87/8ZPvD/HULx/yBE + 8P8hRO//IUbv/yFF7/8dQu//HkT6/y9My94zSZ0NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAbGxsB3Jycj6JiYmHr6+vz9nZ2v77+/r////////////+/ff/09fo/5ak3/9VcOP/J0rs/xY8 + 7v8XPe//HUHv/yFF7/8jRu//I0fv/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH8P8jSfD/Ikn2/yRJ + +Z8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsbGwIc3NzQYuLjI+0tLTY39/f/////f////////////Hy + 9P+/x+f/gpPd/0lk3v8kRuX/Fjzt/xo/8f8gRfD/I0fv/yNH8P8jR+//I0fv/yNG7/8jR+//I0fv/yNH + 8P8jR+//I0fv/yNG7/8jR+//I0bv/yU06P8kPuz/I0nw/yNH8GkAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABzcnI7jIyLj7S0 + tNni4eD////+////////////4+f2/6q36v9rgeP/OFfj/xxB6f8WPO//G0Hx/yFG8f8jR+//I0fv/yNH + 7/8jRu//I0fv/yNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH7/8jRu//I0fw/yNJ8P8lMeb/Jhre/yYf + 4P8kO+v/I0nw8yNG7y8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAB2dnUspaSlw97d3f////7////////////U3Pj/lafv/1dy6v8rTen/GD3t/xc9 + 8P8eQvH/Ikbw/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH7/8jR+//I0fw/yNH + 8P8jR/D/I0bv/yNH7/8jSPD/JEHs/yYf4P8mHuD/Jh3f/yYf4P8kO+r/I0nwyyNH7woAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIuLjJj//////////8jS + +P+DmfP/SGfu/yJG7f8WPO7/Gj/w/yBE8P8jR/D/I0fv/yNH8P8jR+//I0bv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fv/yNH7/8jR/D/I0bv/yNH7/8jR+//I0fv/yNG7/8jRu//I0fv/yNJ8P8lM+f/Jhzf/yYf + 4P8mH+D/Jh3f/yYh4P8jQu3/I0jwgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAdnVzML/Aw/BohPn/GT7t/xY77v8bQO//IUXv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fw/yNH7/8jR+//I0bv/yNH7/8jR+//I0bv/yNH7/8jR+//I0fw/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fv/yNH7/8jR+//I0jw/yUr5P8mHN//Jh7g/yYf4P8mHuD/Jhzf/yUx5/8jSfC0AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW2WSNho/ + 8e0iRfH/I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH + 7/8jR+//I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0bv/yNH7/8jR/D/Jifj/yYd + 3/8mH+D/Jh/g/yYf4P8mHN//JS3l/yNJ8KUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIkXyhiNH8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH7/8jRu//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fv/yNG7/8jRu//I0fv/yNF7v8mI+H/Jh7g/yYf4P8mH+D/Jh/g/yYc3/8lM+f/I0nwhAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAjR/AZI0fv7SNH7/8jRu//I0bv/yNG7/8jR+//I0fv/yNH8P8jR+//I0bv/yNH7/8jRu//I0bv/yNH + 7/8jRu//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH7/8jSPD/JEHt/yYf + 4P8mHt//Jh/g/yYf4P8mHuD/Jhzf/yQ56v8jSfBYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjR++DI0fv/yNH7/8jR+//I0bv/yNG + 8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0bv/yNH7/8jRvD/I0bw/yNJ8P8kN+n/Jhze/yYf4P8mHuD/Jh7g/yYe4P8mHd//JDzr+yNI + 8CkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACNG7xUjR+/pI0fv/yNH8P8jRvD/I0fw/yNH8P8jR+//I0fw/yNH7/8jR/D/I0fv/yNH + 8P8jR/D/I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH8P8jR/D/I0jw/yUo + 5P8mHd//Jh/g/yYf4P8mHt//Jh7g/yYd3/8kOurMI0vxBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVAAAAVAAAAIoAAACkAAAAqAAA + AJgAAABrAAAAJgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNH8HYjR+//I0fv/yNH + 7/8jRu//I0fw/yNH8P8jR+//I0fv/yNG7/8jRvD/I0bv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH + 8P8jRu//I0fv/yNH7/8jR/D/I0fv/yNI8P8kP+z/Jh7f/yYe4P8mH+D/Jh3f/yUp4/8mKuT/Jh3f/yYj + 4ZgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAPAAAAhwAAAOYAAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAADzAAAAlAAAABIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAI0bvDSNH790jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNG + 7/8jRu//I0fv/yNH7/8jR+//I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNG8P8jR/D/I0nw/yUu + 5f8mHuD/JiDh/yYe4P8mHuD/JTHn/yUy5/8mH+D/Jh7gXgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMwAAANICAwP/Cwwx/xIRYv8YF3v/GRmA/xYU + cv8ODkX/BAUN/wAAAP8AAAD/AAAA0wAAADUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0fvWyNH + 7/8jSfD/I0jw/yNH7/8jR+//I0bv/yNH7/8jR+//I0bw/yNH8P8jR+//I0fv/yNH7/8jR/D/I0bv/yNH + 7/8jRvD/I0fw/yNH7/8jR+//I0fw/yNI8P8jQ+7/JiDg/yUs5f8mIuH/Jh3f/yYj4f8lNun/JTDm/yYe + 3+QlHt8SAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AEYCAwPuExNf/yEdwP8mH+r/Jx/1/ycf9v8nH/b/Jx/2/ycg8f8jHs7/ExJj/wEBBP8AAAD/AAAA8AAA + AEkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjRu8BI0fvtSQ06P8kPOr/I0nx/yNI7/8jR+//I0fv/yNH + 7/8jRu//I0bv/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0rw/yUx + 5v8mJOL/JTPo/yYf4P8mHuD/JS/m/yU26f8mJOL/Jh3fbQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/CAkh8yAdsv8oIPb/Jh7x/yUd6f8lHef/JR3o/yUd + 7P8lHe3/JR3q/yYe6P8mIOL/GRtu/wMFAf8AAAD/AAAA5AAAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACNJ8REjRvAxIUTvLyFD7hgiR+8JAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAjSfAZJSzk0CYc3/8lM+f/I0jv/yNJ8/8kSfb/I0jy/yNH8P8jR+//I0fv/yNH8P8jR+//I0fv/yNG + 7/8jR/D/I0fv/yNH7/8jR/D/I0fv/yNI8P8jQe3/JSXi/yU06P8lLuX/Jhzf/yYr5f8lOOr/Jirk/yYd + 380mH+AHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJAoM + MOgkH9X/Jx72/yUd6P8lHen/JR3s/yUd6v8lHuD/JCDP/yMhvv8jIrP/IyGv/yIgsv8kIb3/Ih2y/xwn + n/8TJnLlFiyVKgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0nwGiNE7mYlOOmxIifk5iQm4v8wM+T9MjXl8CUq + 5N0kLubDJDXopSQ56YQkP+1jI0TuQyNH7ycjSfARI0nxAQAAAAAlMucNJhzfzyYa3v8mKuf/ITbU/x4y + vv8iQuT/JEn1/yRJ9f8jR/D/I0fv/yNH7/8jR+//I0fw/yNH7/8jR/D/I0fv/yNH7/8jSPD/I0bv/yUu + 5f8lMuf/JTbp/yYj4v8mHd//JSrl/yYp5P8mHd/8Jh7gOwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcJCijEJB/Z/yYe9P8lHe3/JR3q/yUf3f8kIcr/JCO9/yQi + uv8lIcP/JR/R/yYf3P8mHuP/Jx7m/yYe5v8nHuj/JyXt/yVI/f8lS/3xI0jyiSNH8AkAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0jwGyND + 7oElMObrJiHh/yIX3v9ISOb/cHnv/2lw7f+CivP/YGbr/yce3/8lG9//Jh3f/yYf4P8mIeH/Jibi+SUr + 5OklMefOJDfpsSQ964UlMuiWIiTl+hsbuv8cHKr/FhR8/xUUev8ZI5v/HzfK/yNG7v8kSvb/JEn1/yNJ + 9P8jSPT/I0fx/yNH8P8jSPH/I0ry/yNF7/8lKuT/JS7l/yU46v8mKuT/Jh3f/yYf4P8mH+D/Jh7g/yYe + 4IEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAUQeyMe + zP8nH/3/Ix7b/yIfvv8kIrf/JSK//yUhzf8mINz/Jh7j/yYe5P8mHuL/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHN//JTbp/yNJ8P8jR/D/I0fvvyNH7xIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkOuqJJiDg/yYc3/8mHuD/JBzf/zc15P86OeT/Ixzf/ysm + 4f80MuP/KSPg/yUe3/8mH+D/Jh7g/yYe4P8mHeD/Jh3f/yYc3/8mHN//Jh7g/yMf4f89Rej+PUfk/ykz + 2P8fKtD/GCG8/xUaof8VGZD/GCKb/x0wuP8fOMz/IDnP/yA60v8iQ+f/I0jx/yNE6v8kOOb/JiPj/yce + 5f8mMuv/Jirk/yYe4P8mHuD/Jh/g/yYe4P8mH+C2Jh/gAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQaGYzzIx7M/xwcj/8dHYv/Ix+6/yYf3P8mHuT/Jh7j/yYf + 4f8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYd3/8lK+T/I0jw/yNH7/8jR+//I0fvsiBE + 7wIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYv + 7A8mG95wJh/g1yYf4P8mH+D/Ixvf/yMb3/8mH+D/JR3g/yQb4P8lHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8iGd//PT7l/5Ge9v+Xo/j/ipT3/3mC9f9ka/P/TVTv/zhA6f8nL9r/HCXI/xci + uP8WIKX/FhyR/xcekP8YHo//FhaB/xcQgP8YEor/HRqp/yQh0v8nHuX/Jx/l/yYf4f8mHuD/Jh7g0iYe + 4BMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADA0spR8f + kv8fHZ7/Ix7E/yYf5P8nHub/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/Jh3f/yUk4v8jRu//I0fv/yNH7/8jR+//Gz/vYAAAAAD7/P8C////CgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmH+ALJh7fXiYf4MUmH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe3/8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yEY3/9BQOb/l6X4/5yp + +f+eqvn/nqv5/56r+f+bqPj/lJ/3/4eR9f9ze/T/W2Lx/0NL7v8uOef/IC3a/xkoyv8XJbb/FSGg/xUb + i/8UFnj/FRJ4/xoUl/8hGr7/JR3a/yYc4N0mHN8iAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACYf4BYmH+BEJh7ggicg5MMmINn/Jh/h/ycf6f8mH+T/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/JiLh/yNE7v8jSPD/I0fv/yNH + 7/8YPe7ZAAAAAP///1Pc3NxoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAABAAA + AAAAAAAAAAAAAAAAAAAAAAAAJh7fASYe4EgmH+CwJh/g+yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh7f/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yUd3/83NuP/U1bp/2lw7v96hPH/h5P0/5Gd9v+Zpvj/nqz5/6Gt + +v+eq/n/maT4/42X9v97g/P/ZWzy/1Fa8f89Su7/Kzzo/x0w3P8bL8r/Gy2v/xgjlv8bH6LgJSzkJwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh/gJCYf4IcmHt+7Jh/g6SYf4P8mHuD/Jh7h/yYf + 4/8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYe4P8mIuH/I0Tu/yNI8P8jR+//I0fv/xM57vizwv9g19bO4xISEmIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAA4AgMFqAEBA9oAAADUAAAAiAAAABUAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAmHt80Jh/gmCYf4O0mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh7f/yIa + 3v8fF9//Hxff/yMc3/8pJOD/NDHj/0FC5f9VWen/b3jv/4iU9P+YpPj/nar5/56r+f+eq/n/nqr5/5mk + 9/+Pmfb/cnry/zo96/8lKer/JTHq/yQ55vAjQ+3BI0nwiSNJ8EgjSfEWAAAAAAAAAAAAAAAAAAAAAAAA + AAAmHd9DJh/g/yYf4v8nIOb/Jx/j/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuD/Jh/g/yYe3/8mH+D/Jh7f/yYe4P8mHuD/Jh/f/yYe4P8mH+D/Jh3f/yYm4v8jR+//I0fw/yRH + 8P8YPe7/S2z4+t/i5/EoJyT/AAAASgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAaAsMN/gTE2n/AwQK/wAA + AP8AAAD/AAAA2QAAAC8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh7gHyYe4H0mHuDdJh7g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/JR7g/yUc4P8jGt//IRjf/x8W + 3v8gGN7/JB7g/zg35P9WXOr/cHrw/4CK8/+AjfP/gY3z/4mU9f+Xo/j/jJj1/zAs4f8jGd//Jh3g/yYf + 4P8mJeL/JS7m/yQ46ekkQe27I0bvfiNI8DsAAAAAAAAAAAAAAAAmHeI+IxvOyR4Zrf8jHcz/Jx/k/ycf + 5/8nH+b/Jx/m/ycf5f8nH+X/Jh/k/ycf4/8mH+L/Jh/h/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/f/yYe + 4P8mHuD/Jh7g/yYf4P8mG9//JDTo/yNJ8P8jR+//HUHv/y5T8v/k7f/6W1lT7QAAAP8AAAA7AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAECBGIPEFD/FRR8/xgVk/8aGJT/DQ4+/wABAP8AAAD/AAAA3wAAABwAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACYe4A8mHt9iJR/fxiYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH9//JR3g/ygj4P9dZOv/bHHu/2x17/90fvD/eYXx/3aC + 8P9lbu3/QkXm/ywn4f8zLuP/JyDg/yYf4P8mH+D/Jh7g/yYd3/8mHN//Jhzf/yYf4P8mJeL/JS7l/yQ5 + 6qokQuwRAAAAAAAAAAAAAAAAHxyyVBgSh9MYE4z/HBam/x4Ysv8gGbf/IRq9/yEbw/8iG8r/Ix3Q/yUe + 1v8mH97/Jx/l/yYf4f8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe4P8mH+D/Jhzf/yUo4/8jR+//I0fw/yBE + 7/8dQ+//ydf//7a0qvcAAAD8AAAA/gAAADEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQ5Dw9X9xUTfP8YFY3/Ixza/ycf + +f8nIPD/GhmN/wMEB/8AAAD/AAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAmHd8BJh3fRiYe4K4mHuD5Jh/g/yYf4P8nH+D/IBnh/xwT4f8hGeH/Jh/g/yYf4P8mH+D/Jh/g/yYf + 3/8mHuD/HRXh/0dK6v+LmPb/oK75/5qm+P+YpPj/maT4/5qm+P+Uoff/U1jp/xwU3f8lHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYd3/8mHN//Jhve/yUr5NQjSfCnI0nxmCNJ8YUlRPVqHzDIkxki + l/sZIZT/Fx+P/xcdjP8XGoj/FxiE/xYVgP8WFH7/FhJ8/xYTg/8eGaz/Jx/h/ycf5v8nH+T/Jx/k/yYf + 4v8mH+D/Jh7g/yYc3/8mJOH/JEPu/yNI8P8jRu//GD7v/52x+//+/PL/ISEh/wAAAP8AAAD1AAAAJAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAERITTdcgIoj/GRac/yUd5P8mHuz/JR3n/yUd6f8oIPr/HRud/wIDBP8AAAD1AAAAHwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYh4SEmHuCAJh3f3yIZ + 4P9PS9j/a2rU/0pK2v8jHeD/Jh7g/yYf4P8mH+D/JR7g/yYf4P9XVtf/aWnS/zY13v9bYez/mKb4/5ai + 9/+UoPf/lKD3/5ei9/+Zp/j/TE/o/xwT3v8jGt//JB3f/yYd4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mG97/JSjj/yNH7/8jR+//I0fw/yNI8P8jSvT/JEn1/yRJ8/8jSPL/I0fx/yNF7P8jROn/IkLl/yE+ + 3f8gN8v/GCCV/xQRcv8aF5f/IRvC/yIcxP8jHMv/JR3X/ycf4/8nHeP/JiPi/yRB7f8jSfD/I0fv/xk+ + 7/9XdPP//////5aVlP8AAAD/AAAA/xkZGemOjo8VAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJCiWfQ0ai/yIfqv8kHOr/JR7s/yUd + 5/8lHej/JR3o/yUd6P8oIPn/EhJc/wAAAP8AAABEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACNJ8QkjRe4qI0TuTiQ+7HgkPOvKGivq/3N51P/e4L//ZmjV/xwU4f8mH+D/Jh/g/yYe + 4P8lHuD/IRrh/6Ckyv/c3b//WFfW/xUN4P9WXOr/mKX4/5up+P+ap/j/mqf4/52p+f+Wo/f/Vlrq/zIw + 4v8rJ+H/Ixzf/yQc4P8mH+D/Jh/g/yYe4P8mH+D/Jhzf/yUs5f8jRe7/I0jv/yNH7/8jR/D/I0fv/yNH + 8P8jR/D/I0fw/yNH8P8jR/D/I0jx/yNI8v8jR/L/JEr3/yVM/P8hPNf/GBeF/xUQdf8UEXH/FBFz/xQQ + c/8VEXr/GhSS/yMizv8kQe3/JEz3/yRK9f8jR/H/Fjzu/62+/P///vn/KCgp/wAAAP8AAAD/RERF4aen + pwsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAASklOk/89Pbz/IBjl/yYe7f8lHeb/JR3q/yUd5/8lHej/JR3n/yYe8v8gHLr/AgMD/wAA + AFYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjR+8dI0fwSSNH730jR++xI0fv3SNH7/sjSPD/I0jw/yNJ + 8P8bQ/P/T2vj/9HQwf+Ii8//HRfh/yYc3/8mHuD/Jh7g/yYf4P8cFOL/dHbS/9fYwP97fdH/HBTh/yEZ + 3/9BQeX/WFzq/1NW6f9TVun/Z27t/4yY9f+eq/n/kJ32/4uW9f9zffD/MCzi/yQc4P8mH+D/Jh3g/yYe + 3/8lM+f/I0jw/yNI8P8jR+//I0fv/yNH8P8jR/D/I0fv/yNH7/8jR/D/I0bv/yNH8P8jR/D/I0fw/yRK + 9v8hP93/GSGZ/xUUff8ZIJn/HTC6/x84zf8fOM7/HzPD/xwrrv8XG4r/FRV9/xYZhv8aJKH/HzbI/yBC + 6/8nTfb/6vH//7u5tP8AAAD/AAAA/wAAAP+BgYHg////CwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUbHT/MYmbH/x8Y1P8lHev/Gxej/xgV + iP8hG8z/Jh7r/yUe6P8lHef/JR3r/yUf3/8FBRX/AQMKTgAAAAAlSv0CI0buJSNH71ojR++VI0fvyyNH + 7/IjR/D/I0fv/yNH7/8jR/D/I0fw/yNH7/8jR/D/I0fw/x9D8f83WOr/wMLF/8fIw/9BV+T/Hyjn/yYg + 4P8mHN//Jh3g/x4W4f9PTtj/0NHC/52fy/8hHOH/JR3g/yEZ3/8fFt//IBff/x8X3/8eF97/LSnh/3qF + 8f+eqvn/nan5/5qo+P86OOT/Ihrf/yYc3/8mIuH/JDzr/yNJ8P8jR+//I0bv/yNH8P8jR+//I0fv/yNH + 7/8jR+//I0fv/yNH8P8jRu//I0fv/yNH8P8kSvf/HjPB/xYTfP8aIaH/IT/b/yRJ8/8kSvf/JEn1/yRJ + 9f8kSvb/JEr3/yNH7/8gPNf/HCux/xcaiv8UEHX/Dw+A/0BQwf//////ZmVj/wAAAP8AAAD/AAAA/7Gx + sdj///8GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAABSkNGjv83NrL/IRnt/x8ZuP8UE3L/ExJw/xsXnv8mHu3/JR3o/yUd6P8lHen/Jh3p/wsJ + J/wVKod/Jkz/kCNH79AjR+/4I0fv/yNH7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNH8P8jR+//I0fv/yNH + 7/8jR+//IUXw/yZK7/+qssr/zMvC/4qb1P8ZQvP/I0Lt/yUy5/8mI+H/Ixjg/zMt3f+8vsX/urvF/zAt + 3v8jG+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8gGN7/MS7i/4CK8v+CjPP/Rkfn/yMZ3v8mHN//JSnj/yRD + 7v8jSfD/I0fv/yNH7/8jR+//I0fv/yNG7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH8P8jR/D/JEr2/xwv + t/8WEXz/IhnD/ycm6v8kRPL/I0jw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fx/yNJ9P8kSvf/I0jw/yE+ + 2/8PGpz/UlGZ//r8//8rLTj/AAAA/wAAAP8KCgr/39/fy////wIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBR2oQ0aj/x0apP8lHef/FhOH/xwe + ff8YGHb/HRew/yYe7v8lHej/JR3n/yUa5/8lJuf/Ij3K/iRJ8v8jR/D/I0fv/yNH7/8jRu//I0fv/yNH + 7/8jR+//I0bv/yNH8P8jR+//I0fv/yNG7/8jR+//I0fv/yNH7/8jRvD/HELx/5ai0P+jrs3/vcDG/z5e + 6P8dQ/H/I0nw/yNF7/8jNen/ISPj/56gy//Oz8L/SEba/yAV4P8mHd//Jh3f/yYd3/8mHeD/Jh3f/yYd + 3/8jGd//Jx7f/yYe3/8hG+D/JSvk/yQ56v8jR/D/I0jw/yNH7/8jR/D/I0bv/yNH7/8jR+//I0fv/yNG + 7/8jRu//I0fw/yNH8P8jR+//I0fv/yRK9/8eNML/FRF5/yMczf8nIOf/Jhzf/yUn4/8jRu//I0fw/yNH + 7/8jR/D/I0fv/yNG7/8jR+//I0fv/yNH8P8jSfL/I0z1/x5G9v9JYdr/VFVa/wAABP8AAAD/AAAA/zMz + NP/9/f2xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAEg4PQug5O53/Ghak/yMc2P8UE3X/ExJ2/xUTfv8jHdv/JR3q/yUb5/8lH+j/JDPr/yNH + 8P8kSff/I0fx/yNH7/8jR/D/I0fv/yNH7/8jR+//I0fv/yNH7/8jRu//I0fv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fw/yNH7/8YPvL/fY/X/5ek0P+dqM7/jZvT/xk/8v8jR+//I0jw/yFJ8f8ZPvH/eorX/9fV + v/9ocdf/GyLn/yUp4/8lJuL/JSbi/yYm4v8lKOP/JSzl/yUw5v8jNej/Izzr/yNE7v8jSPH/I0nw/yNH + 8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0bv/yNG7/8jR+//I0fv/yNH7/8jSPP/IkDf/xUV + e/8hGr7/Jx/n/yYf4P8mHuD/Jh3f/yRA7f8jSPD/I0fv/yNH7/8jR+//I0fv/yNH7/8jSfD/I0Tu/yQ5 + 6v8lMuf/JjDx/x4mrP8AAAD/AAAA/wAAAP8AAAD/ZGRk/////4IAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAERJX/ywvkv8ZFpv/JR3o/xkV + kv8UE3f/Hxm8/yYc7/8lHOf/JSvq/yNB7v8jSvD/I0fv/yNG7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH + 7/8jR+//I0fv/yNG7/8gRO//IUXv/yNH7/8jR+//I0fw/yBE7/8kR+//IETv/xM68f9bc9//r7TJ/1hz + 4f/FxsT/Olrq/xxB8f8sT/D/jaL3/xxB8v9Tb+L/1NC//4ub1P8bQ/L/I0jw/yNH7/8jR+//I0fv/yNI + 8P8jSfD/I0nw/yNJ8P8jSfD/I0jw/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0bv/yNG7/8jR+//I0fw/yRJ9f8ZI5r/GxWc/ycg5/8mH+D/Jh/g/yYc3/8mKOT/I0bv/yNH + 7/8jRu//I0fv/yNH7/8jR+//I0jw/yU26f8mIeH/Jhzf/yYc3/8nHeb/IRu7/wICBP8AAAD/AAAA/wAA + AP+Pj5D7////MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAABAG0SEmD/HByE/xYUg/8kHeP/JRvo/yMZ2f8mH93/JSvW/yQ96/8jSfD/I0jw/yNH + 7/8jR+//I0fw/yNG7/8jR+//I0fw/yNH7/8jR+//I0fw/yNG7/8jR+//Ikbv/zda8f8sT/D/HkLv/yNH + 7/8bP+//aIT1/zxd8f8kSfD/t8b8/97i8v/Cw8T/Olvo/6auzP+KmdT/EDfy/zlb8f+ouvn/HUHw/zVW + 6//DxMT/q7LL/yVJ7/8fQ/D/IUXv/yRH7/8jR+//Ikbv/yNG7/8jR/D/I0bv/yNH7/8jR+//IUXv/x9D + 7/8jR+//I0bv/yNG7/8jR+//Ikbv/x5C7/8iRvD/I0fv/yNG7/8jR+//Ikbv/yNG7/8kSfT/ID3W/xYT + f/8lHdb/Jh/j/yYf4P8mHd//JiHg/yRC7v8jSPD/I0bw/yNH7/8jR+//I0fw/yNK8f8lNOf/Jhvf/yYd + 3/8mH9//Jh7h/yYf4v8oIef/DQ4+/wAAAP8AAAD/Dg8O/9zc2akAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAkhESX/8UEn7/FRF8/yMb + 1P8mJd3/JTTW/yRB2P8jSer/I0nw/yNH7/8jRu//I0bv/yNH7/8jRu//I0fv/yNH7/8jR/D/I0fw/yNH + 7/8jR/D/I0bw/yNH7/8gRO//QGLy/3GO9v87XfH/HkLv/yJG7/+Lovf/I0bw/2qF9f/Z4v3/x9P4/9DP + xv9SbeH/Um3j/8XGw/9LaOz/Rmf0/3CN9v8YPe//IUbw/6auzP/FxcT/NFXp/zVY8/8vUvD/HUHv/yBE + 7/8nSvD/HkPv/xxB7/8kR+//Ikbw/xtA7/8rTvD/PV/y/yBE7/8iRu//I0fw/yJG7/8mSu//QmPy/yJG + 7/8jRu//IUXw/yRH7/9TdPP/I0fw/yRJ9/8bKKX/HBWi/ycf5/8mH+D/Jh/g/yYc3/8kN+n/I0nx/yNH + 8P8jR+//I0fv/yNH8P8jSvD/JDjp/yYd3/8mHuD/Jh/f/ycg5P8mH+L/IBjX/x8X6v8VE3b/AAAA/wAA + AP8sLCrz+fn/JgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAACjDw1Q/xQQev8aH4//JD3U/yNH5v8jSfD/I0jz/yNH8P8jRu//I0fv/yNH + 7/8jRu//I0fv/yNH7/8jR+//I0fw/yNH8P8jR+//I0fw/yNH7/8jR+//I0fv/yNH8P8dQe//Jknw/1+A + 9f8lSe//MVPw/22J9f8UOe7/ZYD0/y9S8f8ZQPD/q7PM/4OT1v8ZQPL/qbDI/9PV2v/F1P7/jKP3/xg9 + 7/8ZQPL/gpLW/9TRwP9NaOH/Tm/2/22K9f8wVfH/Vnf0/3+b9/+NpPj/S2vy/xk+7v8mSu//PFzx/3qT + 9v91j/b/Fzzu/zlf8f8mSvD/HULv/2WD9f+luPn/dI72/xY77v8/Y/L/LlHw/2eE9f8kSfH/IkXr/xga + jP8jG83/Jh/k/yYf4P8mHd//JSTh/yNG7/8jR+//I0fv/yNH7/8jR+//I0nw/yQ46f8mHt//Jh7g/yYf + 4P8nH+X/Ix3N/w8Mi/8uLpD/S0yv/xMVQP8AAAD/AAAA/xoZbPZFP+43AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK4PEkz/GR6Y/yJC + 5P8jSvX/I0fx/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH7/8jR+//I0fw/yNH + 8P8jR+//I0fv/yNH7/8jR+//I0fw/yNI8P8eRPD/M1ny/z5k8v9xjPb/UXP0/xU97/9lgvX/Kk7w/w00 + 8f+Il9P/qbHM/xU88/9het7/vsDD/1l37v+Mpfn/I0fv/xk/8v9cdeD/1tK//3KG2f8xVfT/hqH3/ytR + 8f8qTfD/I0fv/zVX8f+Qp/j/T27z/1Fw8//G0/z/t8f7/yRI8P8eQu//TnHz/ypO8P8oTPD/PWHy/xM4 + 7v9ph/X/IkXv/15+9f9nhfX/YoD0/yBG9P8gPNT/GRaT/yYf4v8mH+D/Jh/g/yYc3/8lM+f/I0nw/yNH + 7/8jR+//I0jw/yNI8P8lMuf/Jh3f/yYe4P8mHuD/Jh/j/yQe0/8KB3n/WVya/+Lk5P/x8+v/zc/Y/1db + ef8HCFX/JB3i/yAW4a0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAYMqkMFy2PyyNE4f8jRu//I0jy/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH + 7/8jR+//I0fw/yNH7/8jR+//I0bv/yNH7/8jR+//I0fv/yNH7/8jSPD/I0nw/yNJ8P8jR/D/I0Pt/yQ/ + 7P8aMun/f5T0/9fi/P8fNOj/GCvo/3GE8v+Mo/f/VXX3/22B3P+/wcX/K0zs/y1S8P/Jy8j/b4LV/11+ + +P82WvL/G0Lx/ztc6v/IyMP/mKTP/yJI8f9wi/b/H0Pv/yFF7/8jR/D/HULv/yFF8P9wjfb/Zoj1/xxC + 7/+csPj/QGHy/xY77v9qiPX/PV7y/xI47v9UdvT/RGXy/3iT9v8mSfD/NFnx/8bU+/9mgvX/GkH1/x0z + vf8dFqj/Jx/m/yYf4P8mHuD/Jh7f/yQ+7P8jSPD/I0fv/yNJ8P8kQe3/JSjj/yYc3/8mHt//Jh/g/yYf + 4P8nH+X/EQ2W/zw+iv/q6+v/8vHt/+rq6v/y8e//9fbz/4eJwv8ZGMb/JCzp8SYc3xoAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJEfzPiVK+9ElS/z/I0j0/yNH + 8P8jR+//I0fv/yNG7/8jR+//I0bv/yNH7/8jR+//I0fw/yNH7/8jR/D/I0fv/yNH8P8jR+//I0fv/yNH + 8P8jSfH/I0jw/yNC7v8kOOr/JS7l/yYm4v8mIeH/Jh7f/yAW3v9DROT/uMD1/4SF7f8WCtz/SEbk/9zi + +v+DiPD/qbDn/8jJwv9ITNv/Gx7n/6+z3f++wcT/Wm/r/0Na7v8dNev/JkHs/66zyf+4vMf/K03s/2WA + 9f8lSO//IUXv/xc87v8bQO//JErw/19+9f+owfv/a4j2/26O9v9LbvP/FDvv/1l89P/E1fz/Xnz1/ylQ + 8f+wxfv/gJn3/xg/7/8oUPH/Vnf0/zFW8f8iSff/Gyyv/yAYuf8nH+X/Jh/g/yYe3/8mIuH/I0Tu/yNI + 8P8jSfD/JDfp/yYg4P8mHd//Jh/g/yYf4P8mH+D/Jh/j/yIb0f8REHr/uLvQ//b18P/p6er/6unq/+rp + 6v/s6+v/9/fs/4CEw/8YN+X/JSzmSgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACNG73EjRu/5I0fw/yNH7/8jR/D/I0fv/yNH7/8jR+//I0fv/yNH7/8jRu//I0bv/yNH + 7/8jR/D/I0fv/yNH7/8jR/D/I0fv/yFF8/8dQ/X/HD7y/x4z7v8lKuX/JiHg/yYc3/8mHN//Jh7f/yYe + 4P8mH+D/JBrf/ywp4f8wMuL/oKby/0hF5f8WDt7/qKvz/9bZ+v/P1PP/1tfG/2lo0/8UCuD/UE/k/8jJ + xv+lrNf/S0vo/x8V3v8eF+H/iozO/87Pwv9CQ9v/VFTq/x8f4f+DjPD/q7r3/6Gw9v+suPf/mZ7x/zY8 + 5v9dYen/V17p/yMr5f8iKeT/LDjm/4mO7//Lz/j/NDvm/yUq5P8iJuT/JSnk/yYt5f8eKuX/IjPo/yVC + 8f8bJ6X/IRnD/ycf5P8mH+D/Jh3f/yYk4v8jSfD/I0Xv/yUu5v8mHd//Jh7g/yYf4P8mHuD/Jh/g/yYf + 4P8nH+b/Ew6v/2Nmpf/9/vf/6+vr/+7u7v/v7+//7Ozs/+rp6v/t7ev/7Ovm/0pk5P8cQvGRAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNH7wUjR/CaI0bv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH7/8jR+//I0fv/yJH8v8dQ/T/KkrZ/0lb + vv9bYLP/UU60/ykh2f8lHeH/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8vLeH/JyLg/zI14v85NeP/VVfn/xwT + 3v8sJ+H/pKXy/7O19P+2ucj/j5HN/x0X4v8YEeH/cHLS/8/Txv9ZXeP/HhXg/x0U4f9jY9X/1tfB/2Jh + 1f9BPub/JBvf/0M94/+mpvL/tr31/zIt4f8ZEd7/Ihnf/x8W3/8fFt//Jhzf/yYd3/8mHd//HRbf/yEc + 3/8mHd//JBvf/yYc3/8mHd//Jhzf/yYc3/8mHN//Jx/j/xsanP8iHMn/Jx/k/yYf4P8mHuD/JSHg/yUv + 5v8mJeL/Jhzf/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/ycf5f8WE5v/QUNX/8DAu//08/T/2tra/9fX + 1//n5+f/9vb2//b29v/59+z/lKbs/xc98N0jR/AKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAjR+8OI0fvsyNH7/8jR+//I0fv/yNH7/8jR+//I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0bv/yNH + 7/8jR+//I0fv/yNI7/8hRvT/Hj7p/1BhtP+anKn/uri8/7+/w/+oqqj/MS/G/yMb5P8mH+D/Jh/g/yYf + 4P8mH+D/Ixrf/0BD5P9EROX/Ly3h/zw85P82N+P/JBzg/yMc4P8bE97/FQ7g/5KTzP+ytcf/JiHg/yEY + 4f8vK97/wcTF/3+C0/8bE+H/IBjh/0JA2//NzsP/h4jP/zk55v8oIOD/IBjf/xcP3f9dXuj/g4nu/yMc + 3/8lHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/JR7g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mHuL/Gxeb/yMdzf8mH+P/Jh/g/yYf4P8mHuD/Jhzf/yUa3/8kG9//Jh7g/yYe4P8mH+D/Jh/g/yYe + 4P8mH+D/Jx/l/xcWfP8AAAD/FBQT/zk5Of8UFBT/EBAQ/ykpKf9ZWVn/mJiZ/+fm3v+6xO3/HUPv/SJG + 7zEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0bvDSNH77wjR+//I0fv/yNH7/8jR+//I0fv/yNH + 7/8jR+//I0fw/yNH7/8jR+//I0fw/yNH7/8jR+//I0fv/yNI8P8jSvD/ID7y/yQy2f9tcaP/trW2/72+ + y/+kpbH/uLnC/4SGqP8jHtj/JR7h/yYf4P8mH+D/Jh/g/yYf4P8lHuD/JR7g/1lc6P99g+3/aW3q/yYf + 4P8lHuD/Jh/g/yYe4P8cFOL/c3PS/8nMw/85Ntz/Ihng/xsT4f95e9H/wMPE/zAs3v8gGOH/Kibf/7W3 + x/+oqcn/P0fl/ych4P8mHuD/Jh/g/xcP3f9hY+n/Vlfn/x8X3/8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/Jh7g/yYe4P8mHuD/Jh/g/yYf4v8bF5r/Ix3L/yYf4/8mH+D/Jh7g/yYe + 4P8jHN//KC/l/y476P8lHuD/Jh7g/yYf4P8mHuD/Jh7g/yYf4f8mH+L/FRRv/wABAP8AAAD/AAAA/wAA + AP8AAAD/AAAA/wAAAP8BAQH/ysnE/9PY8f8mSO7/IUXwVwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNF + 7QEjR/CmI0fv/yNH7/8jR+//I0fw/yNH7/8jR+//I0fv/yNH7/8jRu//I0fv/yNH7/8jRu//I0fv/yNI + 8P8jSfD/JELt/yAr7P8pJs3/fn2f/7y9wP+5usr/t7jI/4eIjf+oqar/UFC2/x8Y5f8mH9//Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8lHt//IRnf/ych4P8iGt//JR3g/yYf4P8mH+D/Jh7g/x4W4f9VVNj/0tTC/1ZV + 2P8dFeH/Ihrg/zMw3f/FyMT/cnPS/xsU4v8fGOH/kpTN/7y9w/9/iuj/Pjzl/xoQ3v8jG9//GRDe/z49 + 4/9rcOr/HRXf/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8mHuD/Jx/j/xoXmP8iHMj/Jx/j/yYf4P8mHuD/JBvf/yk86v99lPf/YG/w/x8Y3/8mHuD/Jh/g/yMb + 4P8lHuD/Jh/h/ycf4/8VFHT/AAEA/wAAAP8AAAD/AAAA/0ZGR/+hoaH/RkZG/wEBAf/Ozcr/5uj3/y1O + 7P8gRPBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAB0WvA4fGbwhIhy+LiAavSglHL0QHjPdPh1D8P8fRPD/IEbx/yJH8f8iSPH/I0fw/yNH + 7/8jR+//I0fv/yNH7/8jR+//I0fw/yNJ8P8jSfD/JEHt/yUv5v8hG+X/KiPK/4WGoP++v8T/t7jI/7a3 + xv+2t8b/ubrJ/62vs/85NcH/Ihrl/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/JR7g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/IRng/zw63P/LzcP/eHnR/xsU4f8mH+D/GxTh/4GD0P++wcX/LSne/xoR + 4v9rbNT/y8zA/4mQ4P+8wfj/SETl/zEt4f9TUub/pKzy/0VE5f8gGN//Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8nH+T/GxeZ/yEbwv8nH+T/Jh/g/yYe + 4P8gGd//aXjx/6ay+v9YY+3/Hxjf/yYf4P8mHt//SU7n/ygj4P8lHuD/JyDm/xcUg/8AAAD/AAAA/wQE + BP+dnZ7/9vb2//r6+v/v7+//b29w/5uamP+vsr3/MFLq/yBE8G0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmIcA/JiDAeiEavaJISdHJeILp4X2H7PV7huv8cHnl+S4q + wuRTWtnmT17i/z1Q4P8zSOH/KUPk/yE/5/8dPun/HD/s/x1C7/8hR/H/I0nx/yNJ8P8jR/D/JD3r/yUs + 5f8mH+D/Ihjk/yoly/+Fh6L/vb/F/7e4yP+2t8b/trfG/7a3xv+4ucj/t7m8/05OtP8eFub/Jh/g/yUf + 4v8kHuT/JR7j/yUe4f8mHuD/Jh/g/yYf4P8mHuD/Jh7f/yYf4P8mH+D/Jh7g/yYf4P8kHOD/KiXf/7e5 + xv+cnsv/Hxnh/yYe4P8hGeH/OTfd/8nMw/9sbdT/FQ3i/0lH2v/Q0sL/bG7R/3p87//Z4Pr/1Nz6/8TJ + 9/9YWef/HBXe/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf + 4P8mHuD/Jh/g/ycf5f8bF5//Hxu2/ycf5f8mH+D/Jh7g/yIZ3v8+Ruj/l6j5/19o7f8dFd7/Jh/g/yIa + 3/9zffD/QEDl/yEZ3/8nIOn/HBic/wIDAv8AAAD/X19f//z8/P/s6+v/6enp//Hw8f/j4+P/EBAN/0ZH + VP83VvL/IUfyaQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYh + v84fGb3/GxW7/01R0/90gOb/aG7e/29y4P9eZd//LCjH/2Vr4f+Ci+v/g4rq/4CJ6P+Aiun/fonr/3OB + 6v9fbuf/R1nj/y1D4f8iPej/JTLo/yYl4v8mHd//Jhzf/yMb4/8nI9D/g4Wh/76/xf+3uMj/trfG/7a3 + xv+2t8f/trfG/7a3x/+9vsf/enyn/yMe2P8iHen/KB/b/y0gz/8rINP/Jh/g/yMe6v8hHuz/Ix7p/yQe + 5P8mHuD/Jh/f/yYf4P8mHuD/Jh/g/yUe4P8gGuH/nJ7L/7q8xv8sKN//Ixvg/yYf4P8cFeH/iYzO/7q8 + xv8oI9//LCje/72/xf+Ymcz/FA7f/zAs4f8+O+T/Ix7g/x0V3v8mH+D/Jh/g/yYe3/8lHuD/Jh/g/yYf + 4P8mHt//Jh/f/yYf4P8mH+D/JR/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jx/m/x0Zqv8dGaf/Jx/m/yYf + 4P8mH+D/JR3f/yEh4f+FlPb/lZ/2/zQy4/8jGt//IBnf/3R98P9nbu3/Hxbe/ycf5v8iHMP/AAAN/xQU + Ef/R0NH/8fHx/+np6v/p6en/9vb2/7CwsP8AAAD/ZGh9/y9P7/8gPe9XAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIBrDYF5e2dCoqu35s7Tx/7i58v/ExPb/0dD5/1BN + 4/8cFN3/IRrb/yMd2v8mH9j/KiXW/zMw1f9ISdv/ZGnh/3iA5v+Lle7/bnbf/yIbyP8mHOD/Jh3f/yYf + 4P8lHuH/IRvc/3Byo/+9vsL/t7jI/7a3xv+2t8f/trfG/7e4x/+3uMf/urzM/7q+z/+us7z/dXeT/1Es + Y/9eJjv/Yygw/2EoM/9bJ0X/USZk/0Mki/83IrD/LCDR/yMf6P8iHuv/JR7k/yYf4P8mH+D/Jh/g/xwV + 4f99ftH/z9HC/0NB2/8gGOD/Jh/g/yAY4f8/Pdz/zc/D/2Zn1P8WD+P/nJ7L/7i7xv8rJt//IRjg/yEZ + 3/8lHd//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8nH+X/IBu7/xoXlv8nH+T/Jh/g/yYf4P8mHt//Ih7g/1lw8f9qfPL/MzDi/yMb + 3/8gGN7/bHTv/4uX9f8oIuD/JR3h/yYe4/8KClH/Rkc///X09P/q6ur/6enp//Lx8f/39/f/RENE/wIC + AP9zeaH/H0Hr/zAz5tQpH99NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAmJz3A+Lh/ynU1P1f0tL8mcXI+tHT1vz6ZmXq/xwS3v8mHeD/JR3g/yUd4f8lHeH/JBvh/yEZ + 3/8fFtv/IRnX/ygi1/8wLNz/Jh/c/yYf4P8mH+D/Jh/g/yEZ5f9EQ7f/srO0/7m6yv+2t8b/trfH/7a3 + xv+4ucj/tbbF/7W4yP+fn6n/e2Zo/25JSP9rOjH/aCkb/2coHv9mKCH/Zygg/2goHf9pKBn/aikY/2gp + Hv9iKDD/VCZa/z8jmP8rINL/Ih7s/yMe5/8mH+D/HRXh/19f1v/W18H/YWHV/xwV4f8mH+D/Jh7g/x0W + 4f+TlM3/tLfH/x4Z4f9yc9L/ztDD/0I/2/8hGOH/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYe3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4/8jHc7/GBWL/yYf + 3P8mH+H/Jh/g/yYe3/8mHuD/H0Du/x016v8kGt7/Jh/g/yAY3v9kbO3/n635/0RF5v8gF9//JR3m/xwZ + rP+OkZv/8vHt//Ly8v/39/f/19fX/2RkZP8AAAD/MC8m/1xnuv8VNez/ZnDu/4uX9f9BQuXAIBjfMwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmKTzA2p3 + 6ikwM+JeIyjk4CUn4/8lKeT/JSXi/yYe4P8mHuD/Jh7g/yYf4f8mHuH/JR3h/yQc4P8mH+H/Jh/g/yYf + 4P8mHuH/IRrc/3t8qP++v8f/trfG/7a3xv+2t8b/uLnJ/7Kzwv+Qk5v/gHV5/2QxLf9hIBv/ZCIe/2Ul + If9lKCT/Zigk/2YoJP9mKCT/Zigk/2YoJP9lKCT/Zigj/2coIP9pKRr/aSkZ/2EoMv9MJXL/MiG//yMe + 6f8dF+n/RUPb/8/Rwv+Ehs//HBXh/yYe4P8mH+D/Hxfh/0dG2v/P0cL/W1rX/0VD2v/S1ML/YGDV/x0U + 4f8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYe4P8mHuD/Jh7f/yYe + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf3/8YFoz/IxzK/ycf4/8mHuD/Jh7f/yYe3/8kP+z/I0Ht/yYe + 3/8mHuD/IBje/2Nr7f+hrvr/cHjv/x8Y3v8mH+L/GxTU/1JTq//09O//sK+u/1VVVv8QEBH/AAAA/w0M + B/94eIn/Jz3H/yE68P8sJeD/Vlrq/3uG8f9cYuvtJh/gPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiSfHUI0fv/yQz5/8mIuH/Jh7g/yYf + 4P8mH+D/Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yQc4/8sJ83/nZ+t/7u8yf+2t8b/trfG/7e4 + x/+0tcT/jpGZ/3lsb/9jKCP/ZSQg/2UoJP9mKCT/Zikk/2YoJP9mKCT/ZSgk/2YoJP9mKCT/ZSgk/2Yo + I/9mKCL/Zigi/2YoIv9mKCP/Zygf/2opGP9mKST/UyZg/zIetv8uLOf/vL/K/6aoyf8kHuX/Jh7m/ycf + 5f8mHuP/Hxnj/5udzf+srsn/MjDe/8HDxP+Fh8//HBXi/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYe4P8mH+D/Jx/m/xwY + of8eGa3/Jx/m/yYf4P8mHuD/Jh3f/yQ66v8jS/H/JTDm/yYc3/8gGN7/Y2vt/5yo+P+Vovf/Pj7l/x8W + 3v8oIeX/ZWnJ/4mNu//Awcb/AwMA/wAAAP8TEgr/e3qA/0BGq/8bP+f/Ji/n/yQa3/8fFt7/Ihvf/1JW + 6f81MuOfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJCnkSyM96/glKOP/Jhzf/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yYf + 4P8mH+D/Ihrk/zczxP+qrLL/ubrJ/7a3xv+2t8b/ubrK/5KTm/+Fg4j/ZjEt/2UlIP9mKCT/Zigk/2Yo + JP9mKCT/ZSgk/2YoJP9mKCT/Zikk/2YoIv9qJxn/aScc/2coJP9lKCf/YSQk/2EjIf9iIx7/YiIc/2Qi + Gf9nIw//ZyMV/1QiUf+yqLH/yszI/zAssf8ZEqf/IRm9/yMbzf8dFNb/TUzT/9DSw/9ZWtb/mpvM/6qs + yf8kHuT/Jh7m/ycg5v8nH+X/Jx/k/yYf4v8mHuD/Jh7g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8nH+T/IhzG/xkWkv8nH+P/Jh/g/yYf4P8mHN//JTLn/yNK + 8P8jRu//JSjj/x8V3f9jaez/m6j4/5ml+P+BjPP/KCPg/zAs4f/8////e3yK/y4xV/8aGjz/DAwf/1FU + gv87R7b/GTnf/yRH8ugmIuGUJh7f5CYf4PslHd//Ixrf8yYf4EEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUd3wclHuDXLkLq/yYf3/8iGd//Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8hGeX/Pj2//6+xtv+4ucn/trfG/7i5 + yP+srbr/i4+W/4Brbv9hIBv/Zigk/2YoJP9mKST/Zigk/2YoJP9mKCT/Zigk/2YoJP9mKCL/ZSko/0s1 + fP83Prf/MEDJ/zNEzf9PWcr/WmHG/19jwf9fYLn/X1uv/2BXpv9USZv/QTOG/2hvuv+Hm9z/MkCs/xUf + mf8XIJj/FBuW/xIVkv8ND43/mZy1/6Ciuv92eKn/zMvD/ywpnP8XEZ7/Hhiu/yEbvv8jHc3/JR7b/ycf + 4/8nH+b/Jx/l/yYf4v8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mH+L/GheV/yIcyP8nH+T/Jh/g/yYd3/8lJuL/I0fv/yNI8P8jRO7/HTfr/1Zr8f+cp/f/lJ/3/5+s + +f9PVOn/YF/q/////f9BQDz/AAAA/x4nrP8kPeH/Hz/l/x5C8P8kS/T/JDHmoQAAAAAmH+AQJh/gKiYf + 4DkmH98lAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJh7gIyEa3/dKT+j/c33w/zk45P8gGN//Hxfe/yEY3/8iGt//Ixvf/yQc4P8lHeD/JR3g/yUe + 3/8mHuD/Jh7g/yEY5f9EQ7v/tLW6/7i5yP+2t8b/ubrK/6ChrP+RlZ3/dFFR/2MiHf9mKCT/Zigk/2Yp + JP9mKCT/Zigk/2UoJP9mKCT/Zigk/2kmGf9QMmj/HEv//x9J+v8bRPf/QGP4/56s//+erP//nav//52r + //+cq///nav//5ip//8/Y/7/Fj71/xpA8v8iSPX/I0j0/yxQ9P9DYfL/TWjt/0hi5f9TauT/a4Li/1Jo + 0f+El9n/MD+q/w0Wj/8QFIT/EBF8/xIQef8VEX//GBOO/xwWpP8gGb3/JB3S/yYf4P8nH+b/Jx/l/yYf + 4v8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf5f8hG8H/GheY/ycf5f8mH+H/Jh/g/yYd + 3/8kO+v/I0nw/yNI7/8gRvD/MVPx/5Cd9/+cpvj/kZz1/ywp4f+2uf//sbGn/wAAAP8BAAD/Hzu2/yRL + //8jSPL/I0jw/yNB7f4mIOA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmHuA8Jh7g/yEZ3/9GR+b/fory/3R9 + 8P9XW+r/R0fm/z085f81MeP/MCzi/ysn4f8mIuD/JB3f/yMb3/8iG9//GxTk/0JBuv+0trr/uLnI/7a3 + xv+6u8v/n6Cq/5OXoP9uREP/ZCQf/2UoJP9mKCT/Zikk/2YoJP9mKCT/Zigk/2UoJP9mKCT/aCgc/1Ys + Vf8iPfH/IEP4/yFE7/8pS+//Umzy/1138/9eePP/Y3z0/2N89P9jfPT/Z3/0/zla8v8fRe//I0nw/yNJ + 8P8eRfD/SGjz/5Gg+P+eqfn/oqz6/5yn+v+Snvr/jJv7/3yO+P93i/v/aH73/1Vu8P9CXOX/MUnX/yI5 + xf8YKrH/EyCd/xEZjv8TFYr/FxWR/xwWpP8hGb3/JBvS/yYd3v8nH+T/Jx7l/ycf5P8mH+P/Jh/i/yYf + 4f8mH+D/Jh/h/ycf6P8dGab/Hhqu/ycf5f8mH+H/Jh3f/yUm4v8jR+//I0fv/yNH7/8dQu//Smby/42d + 9/9HVOv/LTfp/+bq+f8zMi//AAAA/wAAAP8aNKr/JUr7/yNH8P8jSO//JSnjogAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACIa31ceFt//HBPe/xcP3f8jH+D/Vlrq/3qF8f9+ivL/eYbx/3uF8f96hPH/fYny/4SQ + 9P+FjvT/fYby/3iB8f9xe/X/cXrN/6mpsv+5usr/uLnJ/7m6yv+io6//lJii/3BLSv9kIhz/Zigk/2Yo + JP9mKCT/Zigk/2YoJP9lKCT/ZSgk/2UoJP9mKST/aCce/1gmT/82JbP/JCPn/yAj7f8eIOT/HiHi/x8i + 4/8eJOP/HiXj/x4l4/8eJuT/Iyrk/yUs5P8lLeX/JS/l/yQ06P8iOer/K0ft/z9b8P9TbvP/aID1/3uP + 9v+Km/f/lqP4/56o+P+iq/j/oqv5/56p+/+Wo/z/iZr9/3iN/P9kfPr/Tmr0/zhW6f8mQ9j/GjPE/xUo + s/8WIan/GR2o/x0brP8bFbb/HhbC/yAXzv8iGtf/Jh7e/yYe4v8mH9//JB3S/xwYpP8UEnT/HBik/yYf + 4v8mH+P/Jhzf/yUx5/8jSfD/I0fv/yNH7/8fRO//J0vw/xQ67v92k///trWu/wAAAP8AAAD/AAAA/xgw + mv8lS/z/I0nw/yUw5tkmHN8VAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAODPjalFQ5/90dO3/kpPx/5uf + 8/8xLeH/HRbe/yQe4P8lH+D/JyHg/yok4f8xLuL/REPm/2x07/+KlvX/kZ/2/5ek+v+Snen/mpuq/7e4 + xv+pqrf/uLnJ/6ytuv+RlJz/joSL/18lIP9lIx//Zicj/2YoJP9mKCT/ZSgk/2UoJP9lKCT/Zigk/2Yo + JP9mKCT/aSkb/2gnHf9XJFD/Oh2g/yQY1/8dFOL/Ixng/yYc3/8mHN//Jh3f/yYc3/8mHd//Jh3f/yYc + 3/8mHN//Jhzf/yYd3/8kHd//IR7h/x4i4v8eKOX/IjTo/ylA6/80UO//QmHx/1Rw8/9lfvX/dYr2/4WW + 9v+Sn/f/nKb4/6Kq+P+iq/n/m6b6/42c+v95jfv/YXv7/0hn9v8yU+z/HDzd/21/0v9CUrr/KjSl/ycp + lf8WE4r/FxKH/xcSg/8VEnn/FBJ1/xQTev8TEnP/Gxea/yYf3/8mH+T/Jh3f/yQ46v8jSvD/I0fv/yNH + 7/8hRe//GT/w/7rK/v9QTkX/AAAA/wAAAP8AAAD/GTCP/yVM/v8lMufhJhzfKwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAADIyfqJ1NX8/9ra/f/b2/3/4uP+/1RR5/8eFt7/JR7g/yUe4P8lHuD/JR3g/yQc + 3/8hGd7/IBjf/yci4P8uK+L/Mi/i/zY26P95frL/r7C1/4yMk/+3uMf/ubrK/5GRmf+fo67/j4aN/2c4 + NP9hJB7/YSId/2EhHP9iIRz/YiEb/2IhHP9iIRz/YiEb/2IhHP9hIhz/YiUf/2YsHv9rPC7/alRx/1xb + yf81MuP/IRrn/yQc4/8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYe3/8mHt//Jh3f/yYd + 3/8mHN//JBzf/yId4P8gIOH/Hibk/x0u5/8gOer/JUTu/y9R8P88XvL/TWvz/1959P9zh/X/hpX2/5ah + 9/+gqvj/oar4/5ij+P9+kPn/ws3//4+l/v+Sqvz/aYXv/xMy1v8WK7r/FB+b/xUWg/8UEHX/FBBz/xQS + ef8TEnT/GRaQ/yUe1/8nHuX/Jh/f/yQ36f8jSfH/I0nw/x5D7/89YPr/wMXQ/wgHBf8AAAD/AAAA/wAA + AP8UJIP/Hif0wyAW3yMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANfW/JPY2Pz/2tr9/87Q + +/+0t/f/RkPl/yAY3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/JR3f/yQc4P8dE97/OTjo/4eS + 3/+bnKb/u7zK/7a3x/+3uMj/tLXE/4qKkf+Mj5X/lpih/5KLk/+HeH3/gW1w/3xmaP98ZGb/eF1e/3ph + Y/98ZWf/fmls/4Z1ef+QiZD/oaCr/6+ywP+5vMX/vL3A/6Cjuf9hY7HyLirO3CQd4v8mH+D/Jh/f/yYf + 4P8mH+D/Jh7f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh7g/yYd3/8mHN//Jhzf/yYd + 3/8lH+D/IyTi/yAq5f8eMun/HDrs/x5C7/8kSvD/MFXx/0Ni8v9Zc/T/c4b1/4WU9v/Gzfv/ys/7/8jO + +/+qtfr/cYb5/1l1+/8+X/f/J0jo/xgyy/8XIqL/FhV+/xQPc/8TEXH/FxWF/yMdyv8nH+f/Jh3g/yUr + 5P8kPuz/Fzzv/4Ca//+Eg3r/AAAA/wAAAP8AAAD/AAAA/0JCdNFdVeMIAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAArrT2mZ6g8/9sbev/QD7k/yQe4P8iG9//Jh/g/yYe4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mHuD/JBvf/y4r4f+EkPP/mab+/4eNw/+ioqn/u7zK/7i5yf+4ucj/t7jH/5qb + pf+goaz/vL/Q/7u/0P+6vs7/ub7O/7q/z/+6v9D/vMHS/73C0/+7v9D/trrJ/6yvvf+foKvtj4+WzYCA + hKFzc3Vzbm1pRnNzXR9GR6kJIhrnQCYf4KEmHuDtJh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf + 4P8mHuD/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHd//Jhzf/yYc3/8mH+D/JSPi/yQr + 5P8hMuj/Hjrs/xw/7v8eRfD/G0Lv/5it+f+QpPj/q7j5/6Kt+P+cpfj/o6v4/5ql+P+ImPj/Z4D7/zFV + 9/8dPeH/HS20/xcZh/8TD3D/FBF1/x4Zrf8mH9//Jx3l/yYe4P8eIeT/tb30/zc2Lf8AAAD/AAAA/wAA + AP8AAAD/enp01NbWzgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkIN+fFQ/d/xEJ + 3f8ZEt7/IRvf/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8fF979aG/ulZ2q + +YyUoPielKD8t4ePyMySk6Dnr7C8/7q7y/+7vMz/vr/Q/72+z/+6u8v/u7zM/7u8zP+4ucn/sbLB/6eo + s/+YmaPnioqQxHx9f5hxcXJnamppPGZmZRkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYe + 3xwmHuBsJh/fwiYe4P0mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mHeD/Jh3f/yYc3/8mHuD/JiPh/x8l5P87T+z/tMX7/ztc + 8f+Qqvn/M1fx/0Fg8v9pf/T/jJr2/56o+P+jq/j/kZ33/z1d9P8gR/f/I0Xr/x4zxP8YHpL/FBFw/xcS + g/8gGrr/IBfd/Tk26/6/wc//BwcE/wAAAP8AAAD/AAAA/wAAAP+Pj5D1zc3NHAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAADw646ljY+r/hYfv/6Ol9P+UmfL/KSTh/yQd4P8mH+D/Jh/g/yYe + 4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yAX3p0AAAAAAAAAAAAAAAAAAAAAAAAAAHZ5mRVubWhlhYaKt5aX + n+Cgoav2oaKt95ycpvCTlJzaiIiOwHx8f5ZxcXJmaWlpNWZmZRMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh/gMyYf4IcmH+DZJh/g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh7g/yYe4P8kGt//HBPd/6Sn8/9rbev/X2Tr/4uX8/8ZL+n/Hj7t/xxD7/8qUPH/TGnz/3OH + 9f+Xovf/dIj1/x5D7/8jR/H/JEr2/yRI8v8gOtP/GiWf/xQWfqQeGsQjm5n8dLKzrP8AAAD/AAAA/wAA + AP8AAAD/CgoK77CwsPTJyck/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAxsn5uNna + /f/a2v3/3t79/9TV/P84NeP/Ihrf/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+DVIhvfEAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ2dlDWhoZyNpaWclZmdmGWZmZQkAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACYf4AkmH+BJJh/goiYf4OkmHuD/Jh/g/yYf4P8lHuD/JR7g/yUd + 4P8lHeD/JR7g/yUe4P8lHeD/JR3g/yUd4P8kHd//JBzg/yQc4P8iGt//HBTe/yMc3/98gO7/i4/w/0ND + 5P+xuPb/Pzvj/yEX3v8mIeH/JSrk/yMz6P8dO+z/H0Tv/zBU8f8yVfH/Ikbv/yNH7/8jR+//I0fw/yRJ + 9f8kSvX/IUPp1B9B627S3P+LkpGJ/wAAAP8AAAD/AAAA/wAAAP8rKyuHx8fIxcXExWoAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADNz/vH29v9/9bX/P/Cxfn/mJvy/zAt4f8jHN//Jh/g/yYf + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g8iYe3zAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJh/gFyYf4GEmH+C4Jh/g9yMe3/8qJeD/KSTg/ygj4P8pJOD/KCPg/yol4f8tKOH/LCfg/y0n + 4f8wKuH/Mizi/zs44/9aWuj/jJPw/5CX8P9vdOv/l53y/09O5v8eF9//Jh/g/yYe4P8mHN//Jhzf/yYf + 4P8lJ+P/IjHn/yE97P8jSO//I0nw/yNI8P8jR+//I0fv/yNH7/8jR/H/Fj3w/6e6//9qZ17/AAAA/wAA + AP8AAAD/AAAA7TY0LRXOzs2rxMTElgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKKn + 9MuGh+//U1Ln/y0n4f8aE97/JBzg/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P4mH+BVAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnI+EnhY/veW92 + 68tocer/dHzs/3Z/7f97g+3/hY3v/3+I7v98iO7/g5Hv/4OQ7/+Hju//i5Xw/5Wg8v+PmPH/e4Hu/11e + 6P8oIuD/Hxff/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYd3/8mHN//Jh/g/yUo4/8lNej/I0Ht/yNJ + 8P8jSfD/I0fw/yNG7/8YPu//rsD//1pYUP8AAAD/AAAA/wAAAP8DCB6DAAAAAMzMy4LExMTAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHRffyBUO3f8jHt//Ozjj/0E/5f8mIOD/Jh7g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/gdwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQz/kAZ+i8ziAge6NVVXn2lxc6f9cW+j/Wlno/1JU + 5/9TV+f/UFXn/0xP5v9LSOX/QD7k/zMt4v8iG9//Hhbf/yUd4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mHuD/Jh3f/yYc3/8mIOD/JSrk/yQ56v8jRe7/I0nw/xc/8P+yxf//oZ+W/wAA + AP8AAAD/CAwf/yFD39QfRf1rhZPVbtDNv9kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABaWuimoKL0/7q8+P/P0Pv/u7/4/y4p4f8jHN//Jh7g/yYf4P8mH+D/Jh/g/yYf4JIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAVDd0JHxjfSB8X350fF9/mIBjf/yAX3/8gFt//IBff/x8X3/8hGd//JBzf/yYe + 4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe + 4P8mHN//Jh3f/yYj4f8lMOb/Fzbr/6i5/v/v7ub/EBAP/wAAAP8cNKL/JUv//x9D8P8mSu30o63Y8Ieb + 5S4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKCv9Fne3f3/19f8/9zc/f/S1fv/Ojjj/yEZ + 3/8mHuD/Jh/g/yYf4P8mH+CoJh/gAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYf + 4BQmHuBYJh7gryYf4PAmH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh3g/yYc3/8ZE97/oKXz//// + //9OTUT/AxJE/yVM+/8jR/D/I0fv/xI47/+Ln/n/m7D67XON9owgRO8gAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAZnDqDLS3997GyPn/nZ7z/2Jh6f8oIuD/JR3g/yYf4P8mH+D/Jh/gsiYf4AcAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh7gHyYf4GgmH+C6Jh7f9iYe + 3/8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/xkS3v+jpvL//////5ubpf8WIsL/JEP1/yNJ8P8jSfD/FTvv/4yf + 9/+Zrvn/dI72/x5C7+0jR/CHI0fwGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMi7hcDMw4v8fGN//HBTe/yUe + 4P8mH+D/Jh7g/yYe4KwmHuAJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmHuAlJh/gciYe4MImH+D4Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYf4P8mH+D/GhLe/3Z3 + 7P//////v8P6/xwU4f8lIN//JS7l/yQ/7P8ROO7/prj5/5Sr+P9qh/X/HEDv/yNH7/8jR/DlI0fvcCNH + 7wgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAjG98FIhrfgyYe4OEmH+D6Jh/g+yYf4OomH+CGJh/gAwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACYe4CkmH+BrJh7fryYe4OgmH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yYf + 4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8jHN//LCfh/7q99f9oaOr/HRXe/yYe3/8lHN//Fg7d/2Jt + 7P/v9v//eZL2/2uI9v8aP+//I0bv/yNH7/8jR+//I0fwyiNH7z8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh/gECYf + 4CkmH+AqJh/gGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh/gFiYf + 4FImHuCfJh/g4iYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf + 4P8jHOD/IBnf/yIa3/8lHd//HRTe/xYP3v9mZ+n/9fX9/46T8P+irPT/XHHw/xs/7v8jSfH/I0fw/yNH + 8P8jR+//I0bv+yNH74MjR+8FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmHuAQJh/gSiYf4JgmH+DdJh/g/yYf + 4P8kG+D/Hhbf/x8X3/8eF9//Hhff/x8X3/8fF9//Hhbf/x0U3/8aEt7/HBTe/ykk4P9VVef/pKjy/9LX + +f+PlfD/o6n0/56i8v8mIN//JCPh/yQ36f8jRu//I0rw/yNH8P8jR+//I0fv/yNH778jR+8rAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACYf4AsmH+BEJBzgkzEt4tpQS+b/T0rm/1VQ5/9WUef/VE/n/1NP + 5v9VVef/YmXp/3d57P+ZofH/wsv3/9vl+//Bzfj/oKjz/6Gn8/9/g+7/KCTg/yIa3/8mHd//Jhzf/yYk + 4v8lNuj/I0Xv/yNJ8P8jR+//I0fv/yNH7+4jR/BlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAb3nrCtff+kGbqPKOtbr22KKo8/+fpvL/n6by/56m8v+ep/L/kpvx/4mU7/9/he7/am3r/2Bi + 6f9NS+b/KiXg/xwU3v8kHOD/Jh/g/yYf4P8mHuD/Jh3f/yYc3/8mJOL/JTXo/yNF7v8jSvD/I0jw/yNH + 7/8jR++oI0bvGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABSUOcHGBDePCEa + 34khGt/VIRrf/yEa3/8iG9//IBnf/x4W3/8dFN//HRXf/yAX3/8kHeD/Jh7g/yYe4P8mHuD/Jh/g/yYf + 4P8mH+D/Jh/g/yYd4P8mHN//JiPh/yU16P8jRe7/I0nw/yNI8P8jR+/gI0fvTQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUe4AUlHuA7JR7ghyYe4NEmHuD/Jh7f/yYe + 3/8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHd//Jhzf/yYk + 4v8kN+n/I0bv/yNJ8P8jR+/+I0bvagAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAJh7gAyYe4DYmH+CEJh/gzSYf4P4mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf + 4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh3f/yYc3/8mJeL/JDvq/yNI8P8jR/DIAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAmH+ABJh/gMiYf4H0mHt/IJh/g/CYe3/8mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf + 4P8mH+D/Jh7g/yYd3/8mHN//JTLn/yNI8IgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmHt8tJh/gdCYf + 4L8mH+DzJh/g/yYf4P8mHuD/Jh7g/yYf4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYd3/8lJ+ObI0nwDAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYe4CEmHuBWJh7gkiYf4MYmHuDsJh/g/yYf + 4P8mH+D/Jh/g5CYf4LwmH+COJhzfRwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAmHuAPJh/gNSUe32cmH+CVJh/gnyYf4JYmH+BaJh/gLCYe4AwAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////+Af///////////////////AD////// + /////////////AAf//////////////////gAD//////////////////wAAf/////////////////4AAH + /////////////////8AAB/////////////////8AAAf////////////////wAAAD//////////////// + gAAAA////////////////AAAAAH///////////////AAAAAA///////////////AAAAAAH////////// + ////wAAAAAB//////////////8AAAAAAf//////////////gAAAAAH//////////////8AAAAAB///// + //////////AAAAAAf//////////////4AAAAAH//////////////+AAAAAB//4B///////////wAAAAA + //4AH//////////8AAAAAP/8AA///////////gAAAAD/+AAH//////////4AAAAB//AAA////////+D/ + AAAAAf/gAAP///////8AAIAAAAP/wAAA///////8AAAAAAAH/8AAAH///////AAAAAAAB/+AAAA///// + //wAAAAAAA//gAAAJ///////AAAAAAAf+AAAACf/////58AAAAAAP8AAAAAH/////4H4AAAAAAfAAAAA + B/////8A/gAAAAAA4AAAAAf////+AH+AAAAAADgAAAAH/////AB/4AAAAAAAAAAAB/////gAP/wAAAAA + AAAAAAf////4AD/wAAAAAAAAAAAH////8AA/AAAAAAAAAAAAB////+AAIAAAAAAAAAAAAAf////gAAAA + AAAAAAAAAAAH////4AAAAAAAAAAAAAAAD////8AAAAAAAAAAAAAAAA/////AAAAAAAAAAAAAAAAP//// + wAAAAAAAAAAAAAAAH////8AAAAAAAAAAAAAAAB/////AAAAAAAAAAAAAAAAf////wAAAAAAAAAAAAAAA + H////4AAAAAAAAAAAAAAAA////8AAAAAAAAAAAAAAAAP///+AAAAAAAAAAAAAAAAD///+AAAAAAAAAAA + AAAAAAf///AAAAAAAAAAAAAAAAAH///gAAAAAAAAAAAAAAAAB///wAAAAAAAAAAAAAAAAAf/+AAAAAAA + AAAAAAAAAAAH/4AAAAAAAAAAAAAAAAAAB/+AAAAAAAAAAAAAAAAAAAf/gAAAAAAAAAAAAAAAAAAD/8AA + AAAAAAAAAAAAAAAAAP/8AAAAAAAAAAAAAAAAAAB//4AAAAAAAAAAAAAAAAAAf/8AAAAAAAAAAAAAAAAA + AH/+AAAAAAAAAAAAAAAAABD//gAAAAAAAAAAAAAAAAAf//4AAAAAAAAAAAAAAAAAP//+AAAAAAAAAAAA + AAAAAD///gAAAAAAAAAAAAAAAAB///4AAAAAAAAAAAAAAAAA///+AAAAAAAAAAAAAAAAAf///gAAAAAA + AAAAAAAAAAH///4AAAAAA/wAAAAAAAAB///+AAHwAH//gAAAAAAAAf///gAB/g///+AAAAAAAAH///4A + A//////8AAAAAAAB///+AAf//////4AAAAAACf///gAP///////gAAAAAAH///4AH////////AAAAAAA + ///+AB////////+AAAAAAD///gA/////////8AAAAAAP//8Af/////////4AAAAAA///AP////////// + wAAAAAH//8P///////////gAAAAAf///////////////AAAAAD///////////////+AAAAAf//////// + ///////8AAAAB////////////////4AAAAP////////////////wAAAB/////////////////gAAAf// + ///////////////AAAH//////////////////AAB//////////////////+AB/////////////////// + 4A////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////8oAAAAYAAAAMAA + AAABACAAAAAAAICUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcG + KAgGBSYWBgUkFwUEHxIEBBsEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAANDE8BBQUiVgAAAr8AAADrAAAA7gAAAOIAAAWyBgYYUhISKwQAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwbXxcDAwqwBwck/g8OTv8REFf/DQxI/ggI + Kf8CAgf/AAAA/gICArQiIiwSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBtGQwM + JcUaF5L/Ix3g/iQd6P8oIuP/NTHX/j08zv8xL7L/EBFG/gAAAv4LCw+ZAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABxcowJEREwvx8awv4qJdv+UE7G/ouLyf7Bwdb+5+fn/u/v7f7m5uf+srTM/jk6 + Q/4BAQH5IShNIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIuOoRAwMDSoaGqs/qmqy//i4uH//f37/v7+ + /v/19vr/3eL0/sPM8P+0wvn/rbr5/rC+7/9cYGz+DBAjdwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAur7QAq2xxRacn6xMoaKlm8bG + x9zp6en9+vr6/u/y+//J0/j/k6b0/mB77/88XOz/KUvt/iNG7v8iRu//IUXv/iJG7/8uUOn+KjqEmwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ6i + tBSSlJ5ar7G4qs/Pz+Xw8PD+/v7+/vDx8v67w+X+cIfo/jpa7f4iRu7+IUTv/iJG7/4iRu/+Ikbv/iJG + 7/4iRu/+Ikbv/iJG7/4iRu/+JEjv7CY/1DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAqa/MAZqftx+Ym6hhqamquNjY2PX6+vr/+vr7/trf8f+ir+f/XnXf/itM5v8gRO//Ikbv/iNG + 7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNF7v8kMuf+Izvq/yNG7dIkOdsUAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAJqiyAKSmLNHq6yxs9fX1+z19fX+8fT7/svT9f+Lnuz/S2fp/ipM + 6/8iRe7/Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//I0bv/iNG7/8jRu//Ikfv/iQ2 + 6P8lHt/+JR/g/yQ46f4jQuycJDXlAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIaMqyza2tv39/j8/r3J + 9/53jvH+PV3u/iNH7v4hRe/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG + 7/4iRu/+Ikbv/iJG7/4iRu7+I0bv/iUm4v4lHt/+JR7f/iUf4P4jPev8Iz3rSwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAIKOxweanamoP2Dy/h9D7/8iRu//Ikbv/iNG7/8jR+//Ikbv/iNG7/8jRu//Ikbv/iNG + 7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jR+//I0Pt/iUg4P8lHt/+JR7f/yYe + 3/8lK+T+I0LthQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABrf9gIJUjvzyNG7/8jRu//Ikbv/iNG + 7/8jR+//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG + 7/8jRu//Iz/s/iUf4P8lHt/+Jh7g/yYe3/8lKuT+I0HtcgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAMlPrWSJG7/wiRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG + 7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+JDrq/iUe3/4lHt/+JR7f/iUe3/4kMeb+IzfpRQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ0nrCCNG78cjRu//Ikbv/iNG7/8jRu//Ikbv/iNG + 7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//I0bv/iNG7/8jRu//JC7l/iUe + 3/8lHt/+JR7f/yYe3/8kNOf4JDHmFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAGBiQIBQUeFQQEGxIFBBwFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG + 708jRu/7Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNG + 7/8jRu//I0bv/iNG7/8jRO7/JSHg/iUe3/8lHt/+JSPh/yUf4P8lK+TGAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAODFQCCQg4PQIBDZ4AAALXAAAA6QAAAOgAAALRAQELgQUE + IRkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACJF7gciRu+8Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG + 7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+I0bv/iNG7/4kN+n+JR/g/iUe3/4lHt/+JTHm/iUl + 4v4lH+CNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8MXQkEBByOAgIH+AoL + NP4REVj+ExNe/g0NQv4DBA7+AAAA/gAAAdgFBR88AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjQu0zI0bu+SNF + 7v8jRu//Ikbv/iNG7/8jRu//I0bv/iNG7/8jR+//Ikbv/iNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iNF + 7/8lJuL/JSzl/iYe3/8lIuH+JTTo/yUk4fIlIuAxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAEA1jEgQEE7YVE3D+Ix3W/yUd6/8lHen+JRzp/yUd6v8kHt7+FBNu/wECCP8AAAD2BQciUAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAyO+YBMz3nCzI85wYAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAI0DshiUo4/wjPOv/Ikbv/iNG7/8jRu//I0bv/iNG7/8jRu//Ikbv/iNG + 7/8jR+//Ikbv/iNG7/8jRu//Ikbv/iM56f8lKeT/JSzl/iYg4P8lMOb+JS3l/yUf4JglJOEBAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVE4EGBwgksB4atf4lHej+JR3n/yUd5/8lHef+JB3j/yQf + 1v8jIMX+Ih60/xkZbv8NEUT+CA4utRcklwQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJzLmGSY6 + 6WomMOavIyvk1SYv5csmLuWwJzDlkCY153ElMOZMJDLnLCM36Q4kMeYBKDXnBicl4ZYlH+D/JDTo/h83 + zP8iQuX/I0fx/iNG7/8jRu//Ikbv/iNG7/8jR+//Ikbv/iNG7/8jRu//I0Lt/iUu5f8lNOj/JSPh/iUi + 4f8lLeX+Jh/g4SUj4RsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABYUgAELCjZ/IRzG/yQd + 6P8kHOj+JB7i/yQgyv8kIcL+JCDE/yQfzP8lHtj+Jh7g/yUe4f8lJOT+I0Xr8SFC5ZoeNssXAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACQt5BIkNuiNJCjj9iUe3/48POT+ZGzs/mdu7f5gZ+z+JyDf/iUd3/4lHt/+JR/f/iUk + 4folKuTiJDLmwiQ06KIkKeTtHiK9/hwgp/4WF4P+GSKc/h83y/4iRev+I0fw/iNH8P4iR+/+Ikbv/iNH + 8P4jQOz+JSfi/iU06P4lKuT+JR7f/iUe3/4lHt/6JSDgSgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAABEQYjseGrL2JB3e/iEevf4jIb3+JCHI/iUf3f4lHuH+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JDnp/iJG7/4iRu/eKkrmIwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB0luRklH9u6JR7f+CUe3/8nIOD+JyDg/yUd + 3/8mH+D+Jh7f/yYe3/8mHuD/JR7f/iYe4P8lHt//JR7f/icg4P9cY+v/aHHv/lBY6f85QeD/KDDP/h8n + u/8cKLP/HCuy/hsnqf8cLbX/HTPD/h0ouP8fHLv/JCHU/iUm4v8mHuD/JR7f/iYe3/wlIN+BAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBejBhMTWr8eHZ7+Hhyf/yQezP8lHt/+Jh7g/yYe + 3/8lHt/+Jh7f/yYe3/8lHt/+Jh7f/yYe3/8lHt/+JC7l/yNH7/8iRu/+I0fvwDhW5AsAAAAAZXnUAQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAZGp8EIh7IRCUe3q8lHt/5Jh7f/yYe4P8lHt/+JR7f/yYe4P8mHt//JR7f/iYe4P8mHuD/JR7f/iol + 4P+JlfT/laH3/pWh9/+UoPf/i5b0/nqD8f9ja+7/R1Hp/i033f8fKsv/HCi3/hkkoP8WGYf/FRN9/hoW + m/8iHMf/JR7f/iUg35skJd4GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJB7bASQd2RwkHdhYIx3NqCId + w/smHt/+JR7g/yUe3/8lHt/+Jh7f/yYe4P8lHt/+Jh7f/yUe3/8lHt/+Jh/g/yUe3/8lHt/+JSnj/yNG + 7/8iRu/+I0fv/SpL5VmJmN8rk53HOwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAgIMwkFBiItCQo7Jw4OWQMAAAAAAAAAAAAAAAAlINImJR7eliUe3+klHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4sKOH+SUrn/l9l6/5weO/+gIry/oyY9f6UoPf+lqL3/pSh + 9/6HkvP+bnXu/lhg7f5CUOz+KDng/h8vy/4cKrD+Hie5wyQt4B8jJ98BAAAAAAAAAAAAAAAAAAAAACQn + 3gslHt+SJR7f1iUe3/slHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+JCrk/iJG7/4iRu/+Ikbv/lJu8biwsrzOOkFhPQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQk5OwgIKsoEBRf1AAAA8gEBC64OC1QcAAAAAAAA + AAAAAAAAIxzSASUe3h4lHt95JR7f3CUe3/wlHt//JR7f/iUe3/8lHuD/JR7f/iUe3/8lHt//JR7f/iUd + 3/8lHt//JyDg/i0o4f89POT/W2Dr/nmD8f+Ll/X/kp32/pOf9v+Un/f/h5L0/kdI5v8lIeH/JSnk+iQz + 5+kkOum5Iz3rdiQ36TskMOYRAAAAACQr4QUlId+FJB7a9iQe2P8mHt/+Jh7g/yYe3/8lHt/+JR7f/yUe + 3/8lHt/+Jh7f/yUe3/8lHt/+Jh7g/yUe3/8lHt/+JR7f/yUe3/8lHt/+JDLn/yNG7/8iRu/+MFPw/663 + 2/IeHx/4LTNMLgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANDEg2DQ5K7RUT + fP8VFHv+Bwgg/wAAAP8BAQjODApODQAAAAAAAAAAAAAAAAAAAAAAAAAAJR7fECUf4F8lHt/JJR7f/iYe + 3/8mHuD/JR7f/iYe4P8mHt//JR7f/iYe4P8mHt//JR7f/iUe4P8mIOD/UVTo/l1h6/9sde7/d4Lw/m96 + 7/9SWOn/REPm/jc04/8lHt//JR7f/iYe3/8lHt/+JSHg/yUo4/wkMufgJDboaSQv4wMAAAAAICTFPRwY + o7obF6D+Hxq6/yEbxP8iHMn+Ix3Q/yQd1v8lHtv+Jh/h/yYe4P8lHt/+Jh7g/yYe4P8lHt/+JR7f/yUe + 3/8lIuH+I0Tu/yNG7/8iRu/+ssH3/lhYWfMAAAD+Jys9IgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAABYVah0PD1PdFROA/h8Zv/4kHen+JB7i/hMSZP4AAAL+AgIPigAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAkKOMBJSXiQCUg4KslHt/yJR7f/iYe3/4lHd/+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4kHd/+S03n/oKM8v6UoPf+lJ/2/pSg9/6Voff+V1zq/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/CM66ssiRu+3IkfwpCJA5pkcLLTsGyms/hkmpv4ZJJ/+GCCY/hgckP4XGIj+FhSE/h8a + uf4mHuD+Jh7h/iYe4f4lHt/+JR7f/iUh4P4jP+z+Ikbv/iJG7/6Oo/f+tbW2/gUFBf4CAgL6LjA5EwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBxzBhcYUK4mJ5P+IRvP/yUd5/8kHef+JR3n/yQd + 5P8PDk/+AAAA1A4RWgsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIzPnAiQy5xIkLeVJJSnj0jk4 + 3f+lpcn/UVPY/iUe4P8mHuD/JR7f/iQd3/9wcdP/m53K/jAt4P9udu//laD3/pSg9/+UoPf/kp32/k9R + 6P8nIeD/JR7f/iUe3/8lHt/+JR7f/yUe3/8lHt/+JSXi/yNB7P8iRu/+I0bv/yNG7/8jR/D+I0fw/yNH + 8P8jR+/+Ikbv/yNG7v8iROr+HCy0/xYUgf8ZFZH+GhaX/xwXpP8gGr3+JSLf/yQ97P8jRu/+I0bv/z1d + 8f/x9P3+Q0NE/wAAAP8gICHxTk9SBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADxA+YUlM + p/siHM7+JR3o/yUd6P8kHef+JR3n/yUd5/8gG77+AAAA5hMbfRMAAAAAAAAAAAAAAAAAAAAAI0PtFSND + 7UgjQ+6BI0XutCNG79wjR+/3Ikbv/ixL6/+3usf/ZmbU/iUd4P8mHuD/JR7f/iQe3/9lZdT/wsPE/jw4 + 3P8oIuD/WFzq/mVr7P9iaOz/eoPx/pKe9v+Ai/L/c3rv/kVG5v8lHt/+Jh7f/yYe4P8lKuP+I0Pu/yNG + 7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNF7f8dMLz+FxuN/xkinv8cLbb+HC64/xoo + qv8XGon+GBqQ/xsoq/8fOM/+Ikbu/3KL9f/U1NT+AgIC/wAAAP9YWFjuMzM0AgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAbGmILMzZu5DQxwf4kHOL+GBWQ/hoWnP4kHeX+JB3n/iQd5/4lHuf+BAUT5hgp + oxgcNb8lIEHfXSJG7pgiRu/SIkbv+yJG7/4iRu/+I0bv/iJG7/4iRu/+Ikbv/iRH7v6fqM3+sLXJ/ixB + 5/4lKeP+JR7f/iUe3/47ONv+yMjD/lta1v4lHt/+JR7f/iUe3/4lHt/+Ixze/kRF5v6Rnfb+lKD2/l5j + 6/4lHd/+JR/f/iQz5/4jRu/+I0bv/iNG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+IUDi/hke + lv4cIKz+IkPn/iNH8P4iRu/+Ikbv/iNH7/4jR/D+IT7c/hwstP4XHJD+ExaG/qSt4P6AgYH+AAAA/gAA + AP6Ojo7kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPEEBaP0Gd/yEbyf8cGK7+GRl9/xYU + hP8kHeL+JB3n/yQd5/8lHuv+FyZ+8iJE59EiRu/xI0bv/iNG7/8iRu/+I0bv/yNG7/8jR+//I0bv/iNG + 7/8jRu//Ikbv/iJG7/+FlNT/sLfJ/mR73f8iRe//Iz7s/iUt5f8lIeD/t7nG/oCBz/8kHOD/JR7f/iUe + 3/8mHt//JR7f/iUe3/9FReb/Rkfm/ici4P8lJuL+Iz3r/yNG7/8iRu/+I0bv/yNG7/8iRu7+I0bv/yNG + 7/8iRu/+I0bv/yNH7/8iQub+GBuR/yEbxf8mH+D+JDnp/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNH + 8P8iRu3+GzPJ/5CVwf8tLjr+AAAA/wUFBf/CwsPRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABoa + XQIKCzutNDWY/yAazP8XFIv+FRR6/xsXpP8kHej+JB3n/yQk6P8jPe3+I0fx/yNG7/8iRu/+I0bv/yNG + 7/8iRu/+I0bv/yNG7/8jRu//Ikbv/iNG7/8jRu//Ikbv/iJG7/9mfN3/jp3S/qevy/8nS+7/Ikbv/iJH + 7/8gP+3/k53P/qKlyv8kJ+P/JSTh/iUi4f8lI+H/JSTh/iUp4/8kL+b/JDfp/iM/7P8jRu/+I0bv/yNG + 7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yJG7v8ZIpz+IBu//yYe4P8lHt/+JSbi/yNG + 7/8iRu/+I0bv/yNG7/8iRu/+I0fv/yNB7f8kOOn+JDTr/xgeWP8AAAD+AAAA/xYWF//p6eqoAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAABQVUxALC0LgJSeK/h8ZxP4dGLL+GRWW/iMc3f4kIef+IzXr/iNG + 7/4jRu/+I0bv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbu/iJG7/4iRe/+I0bv/iJG7/4pTe/+JUjv/ihL + 7/5cd+f+mKPP/oua0/5iet7+IkXv/muG9P46WvH+aH7c/rq8xv4vUuz+Ikfv/iJH7/4iR+/+Ikfv/iJH + 7/4iRu/+Ikbv/iJG7/4iRu7+Ikbv/iJG7/4iRu/+I0bv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/h41 + yP4bF5/+Jh7h/iUe3/4lHt/+JDPn/iJG7/4iRu/+Ikbv/iJG7/4jRe7+JCzk/iUd3/4lHt/+Jh7h/hMS + aP4AAAD+AAAA/jk5Ofufn6ZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsNNygNDUv3FhV7/xwX + rP8kHub+JCXZ/yQ11f8jQ+v+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/y5R + 8P9Sc/P/JEjv/iFF7/9wi/X/JUjv/per+P/k6fn/srfJ/khl5f+rssr/I0fv/nKO9f8rTvD/RWPl/sDC + xP9IZeX/KEvw/iNH7/8iRu//IUXv/iJG7/8jRu//Ikbv/iNG7/8mSe/+Ikbv/yNG7/8iRu/+Ikbv/yNH + 7/8iRu/+Ikbv/y5R8P8mSu/+I0bu/xkfmf8jHdH+Jh7f/yUe3/8lJ+P+I0bv/yNG7/8iRu/+I0bv/yNF + 7/8kLOT+JR7f/yUe3/8lHuD+JR7g/yActv8BAQT+AAAA/39/gcNTUm8IAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAsRQTsMDEP+FBJ6/x8psP8jPt7+I0br/yNG8P8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG + 7/8iRu/+I0fv/yNG7/8iRu/+I0bv/yJG7/8tUPD/T3Dz/iJF7/9lg/T/Ikbv/mmE9P82WPH/srfJ/jxb + 6P+UoND/sr3l/qS4+f8oTPD/K07s/re7x/9pftz/Wnn0/jda8f9Ia/P/c4/1/lx58/8hRe//Jkrv/jdY + 8P90j/X+JEjv/y5T8P8hRe/+Z4P0/4ad9/8kSO/+NVnx/0hn8v8xVPD+IT7c/xsZof8mH+H+Jh7g/yUf + 4P8jPOv+I0bv/yNG7/8iRu/+I0bv/yQu5f8lHt/+JR7f/yUe3f8bF6r+Kiig/yYmi/8AAAX+AQEG/0NB + pZkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0YV0kQGVj+Gyuy/iJG7/4iRu7+Ikbu/iJG + 7/4iRu/+Ikbv/iJG7/4iRu/+I0bv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iR+/+Nlnx/l18 + 9P5Zd/P+JEjv/mB99P4dQu/+kp/Q/lt03/5EYub+p6/L/mWD8/48XfH+IUXv/pmkz/6PnNH+Smrz/kxt + 8v4iRu/+HkLv/kdn8v5rhvT+ZoP0/rDA+f5BYvH+JUnv/khr8v4hRe/+N1vx/jpc8f47XPH+Y4H0/myI + 9f4pTfD+HTG//iEbw/4lHt/+JR7f/iUl4v4jRu7+Ikbv/iJG7/4jQ+3+JSnj/iUe3/4lHt/+Jh7g/hoW + nv5gYqL+6enp/ufn6P6Qk6z+Hx+H/iYf4O5GRrAhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHTnGHSBB + 264iRer+I0bv/yNG7/8iRu7+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG + 7/8jRO7+Izzr/yQ06P8kLeX/JSrj/qWu8/9gaOv/IiXi/p2n8/+NnvT/l6La/oKN0/8iOOv/v8PP/mZ8 + 4f9KaPH/IkTu/nKF2f+zuMj/NFfx/khn8v8iRu//Ikbv/iBE7/9FZvL/epf2/kVl8v9gffT+Ikbv/4Kc + 9v8wUvD+T3Dz/5Op+P8xVPD+PF/x/32V9v8hRe/+HCmv/yQd1f8lHt/+JR7f/yQv5v8iR+/+I0bv/yQ6 + 6v8lI+H+JR7f/yYe3/8lHt/+IhzM/zY3j//f3+T+6enp/+np6f/q6ur+r7HQ/ys41v49RNJhAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAhQ+M/Ikbv3yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu7+I0bv/yNG + 7/8iRu/+I0bv/yJG7/8iRfD+Izrr/yUr5f8lIeH+Jh7f/yYe3/8lHt//JB3f/jo+4/+QlO//Lijh/l9e + 6P+5vPX/xMnm/qOkyf8jHN//eXvd/qyvy/9YXuf/JSLh/kxO2v/FxsP/PUXi/kRL6P9baO3/f5Py/pOm + 9v+UofP/UFrq/mdx7P8zQej+IzHn/2Jw7f+7w/f+Ljvn/z1G6P8kL+b+JjXn/yQ36f8jQO7+GyOn/yUe + 3P8lHt/+JR3f/yQ16P8jRu/+JDDm/yUf4P8lHt/+Jh7f/yYe4P8lHuD+Gxeq/6Wnx//q6ur+6unp/+rq + 6v/p6en+6+rq/42Z3f8oSOqhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIkXuAiJG72kiRu/2Ikbv/iJG7/4iRu/+Ikbv/iJG + 7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+KErk/lBlwv5scbX+V1a3/iYf3v4lHt/+JR7f/iUe + 3/4lHd/+MC/h/jIy4v4uKeD+Q0Lk/iUe3/5oZ+n+iInm/rW2xv4xLN7+JR/f/p+hyv51edr+JR3f/jEs + 3f67vMX+XFzY/jk24/4xK+H+goTt/mlr6f4iGt/+JR7f/iUe3/4lHt/+JR7f/iUd3/4hGt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4mHuH+Gxmg/iUe3v4lHt/+JR7f/iUl4v4lJOH+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4mHuH+GhiD/l9gZf7Ly8v+sLCw/re3t/7Y2Nj+6urq/tfb6v4rTu/tXG/eCQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApRecCIkbufSNG + 7/gjRu//Ikbv/iNG7/8jRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yND7f84S87+kZWw/7O0 + wf+1tsX+f4Cw/yYf3v8lHt/+Jh7g/yYe4P8lHuD/Oznj/lZY5/9cYOj/Kybg/iUe3/8lHd//KiTe/rKz + x/9KSNn/JR7f/lZU1/+nqcn/JR7f/icg3/+eoMr/f4HR/jQz4v8mHuD/JB3f/lJS5v9LSuX/JR7f/iYe + 4P8lHt/+Jh7f/yYe3/8lHt/+JR7f/yUe3/8lHt/+Jh7f/yYe3/8mHuL+Gxih/yUe3v8lHt/+JR7f/yUe + 3/8lIOD+JiTh/yUe4P8lHt/+Jh7f/yYe3/8mHuH+EA9X/wEBAf8MDAz+AgIC/wUFBf8TExP+WVlZ/93f + 4/8/Xu7+VGvdLwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAsSutkI0bv/CNG7/8jRu//Ikbv/iNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG + 7/8jRO7+IzLo/0RGvv+io7X+trjH/6Okr/+foKn+SUe5/yUe4P8lHt/+Jh7g/yYe4P8lHt//JR3f/jEs + 4f8vKuH/JR7f/iYe3/8mHt//JB3g/p6fy/9qadT/JR7f/ikj3/+oqcn/UE7Y/iQd4P98fND/naDL/l5j + 6P8kHN//JR7f/iYf4P9gZej/JR3f/iYe4P8lHt/+Jh7f/yUe3/8lHt/+Jh7g/yYe3/8lHt/+JR7f/yUe + 3/8mHuH+Gxed/yUe3f8lHt/+JR7f/yUl4v9Xa/D+R1Pr/yUe3/8lHt/+JR3f/yYe4P8mH+H+Dw9X/wAA + AP8AAAD+BwcH/1lZWv9FRUX+Ly8v/+Tk5f9OaOz+T2bUSwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAFNR0QJUUtERW1vVKkhJz0ZGR9BbPT7OVDY91EEiQeniIUTt/iJG7/4iR/D+Ikbv/iJG + 7/4iRu/+Ikbu/iJG7/4iR+/+I0Lt/iQy5v4kIOD+S0m5/qqruf62t8b+tbbG/ra3xv6ytL3+NC/I/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JBzf/n5+0P6Qksz+Ixzf/iQe + 3/5bW9b+oaLJ/igh3/5SUNf+uLnF/pme7f54eOz+WVjn/oeL7f5PTuX+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4mHuH+Gxec/iUe2/4lHt/+JR7f/jk75f6UoPb+SlHp/iUe + 3/4kHN/+TFLn/iUe3/4mHuH+EhBn/gAAAP4NDQ3+x8fH/uvs7P7r6+v+hISE/pWVlv5GYd3+RlzNTgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQj/KIykkwcEjHb/lX2Tc9H2I6/t7g+r+aHDh/kdI + 0Ph2gen+aHTm/1lo5P9LXeT/OlHk/ixH5v8kROr+I0Tu/yM86/8kLeX+JSDg/yUe3/9HRbz+qqu6/7a3 + xv+2t8b+trfG/7a3xv+3uMf+UVC1/yUd4f8lHuD+Jh7f/yUe4P8lHuD/JR7g/iUe3/8mHt//JR7f/iUe + 3/8lHt//JR7f/l1c1v+0tsb/Ix3g/iYe3/8mId//rrHH/k5N2P8wLN3/w8TD/kxK2/90dev/io3u/klG + 5f8lHt//JR7f/iUe3/8lHt/+Jh7f/yUe3/8lHt/+JR7f/yUe3/8lHt/+JR7f/yUe3/8lHuD+Gxeg/yQd + 1f8lHt/+JR7f/yUg4P9zgfL+YWjs/yUe3/8jG9/+c33v/y8q4f8lHuD+GBaL/wEBA/94eHj+6+vr/+np + 6f/p6en+oaGh/yQkJf9CXN3+P1PLRQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARkLLGTUx + yryAgN/4m53p/qWo7P6zsvD+Pjvd/ich2f4xLdv+NzTa/j8/2f5TVt7+b3fl/n2H6v5sd+P+IyHS/iUe + 3/4lHt/+JR7g/jo4xv6lprb+trfG/rW2xv62t8b+trbG/ra3xv63uMj+mZyz/lJFlP5QJWP+VyZR/lIl + Xf5IJH3+OSKn/iwgzv4kHuL+JR7g/iUe3/4lHt/+JR7f/kM/2v7FxsP+My/d/iUe3/4lHt/+Z2bU/pqb + y/4jHeD+rK7I/mVl1P4kHd/+JBzf/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+HRms/iIbx/4lHt/+JR7f/iUe3/5UY+7+ZnPv/igi4P4kHN/+d4Dw/k5Q + 6P4lHt/+IRzC/hQVKP7Pz9D+6ejp/unp6f7s7Oz+RERE/kJDSv4yT97+LzLcnTtC2BUAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUlegUrKzwTbGz9Iu3u/fGSknl7yUd3/4lHt/+Jh7f/yYe + 4P8mHuD/JB3e/iMc2f8oItv+JR7e/yYe4P8lHt/+JyDc/4aHrv+2t8f+trfG/7a3xv+2t8b+ra27/6Ce + qP95XV/+aDo3/2UqJv9lKCP+ZSgk/2UoJP9lKCP/Zigg/mYoIv9cJ0D/RSSG/i8gxv8lHuH/JR7f/jAr + 3v+7vMX/VFPX/iUe3/8mHuD/LSfe/q6wx/9LSdn/goPP/o2Ozf8jHOD/JR7f/iUe3/8mHuD/JR7f/iUe + 4P8lHt/+Jh7f/yYe4P8lHt/+JR7f/yUe3/8lHt/+Jh7g/yYe3/8lHt/+IRvB/x4Zsf8mHuD+Jh/f/yUe + 3/8jOOn+IzPn/yYe3/8kHN/+cHrv/3mC8f8lHt/+JR7e/0ZGnv/i4uH+5+fn/8bGxv9gYGD+AwMC/1xf + g/8fPOL+Zm7t/2tz7u8zMuNyODbjAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABeY+YGPEPlPSM76vUkNOf+JSPh/yYe3/8mHuD/JR7f/iYe3/8lHt/+Jh7f/yYe4P8lHt/+ODTI/6yt + vP+1tsb+trfG/7a3xv+lprL+f3d7/2g2M/9lJyP+ZSgj/2UoJP9lKCP+ZSgk/2UoJP9lKCT/ZSgj/mUo + JP9lKCP/ZSgi/mIoLP9SJWD/MyG4/igh3v+kpcn/eXnQ/iQd4P8mHt//JR3g/m5v0/+Xmcz/WlnW/qyt + yP8nId//JR7f/iUe3/8mHuD/JR7f/iUe3/8lHt/+Jh7g/yYe4P8lHt/+JR7f/yUe3/8lHt/+Jh7g/yYe + 3/8lHt/+JB7W/xoXm/8mHuD+JR7f/yUe3/8kNej+Iz/s/yUf4P8kHN/+cHrv/5Gd9v89POT+JR7g/zAs + yP+kp8j+iIiI/wwMDP8BAQH+UFFZ/zhGs/8kOer+Jx/g/zs65P9fZuv7LyrhWQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjjlYiQ56fwlIeH+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+S0m+/rS1w/61tsb+tbbG/qusuf6IiI7+ZzQx/mUnI/5lKCP+ZSgj/mUo + I/5lKCP+ZScj/mUoI/5lKCP+Zycf/mYnIv5mJyH+Zicg/mcnHv5mJx/+Zici/lMlWP6Ui6/+mpvH/iAb + u/4iHMn+JR7Z/jIt3P62uMb+WFnX/ri6xv44NN7+JR7g/iUe4P4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+Jh/g/hsXn/4kHdb+JR7f/iUe3/4kLuX+Ikfv/iQz + 5/4kHN/+b3jv/pSg9/59h/L+Jh/f/oOE7f6Okaj+T1Jy/ggJFv44OVb+Pkmw/iFB5fslK+S5JR7f9SUe + 3/4nIeD6Mi7iPgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4OOQFJiXi6TlB + 5/8lHt/+Jh7f/yUe3/8mHuD/JR7f/iYe3/8lHt/+Jh7g/yUe3/8lHt/+Vla7/7W3xP+1t8b+trfH/5SU + nf+Dc3f+ZSci/2UoJP9lKCP+ZSgk/2UoJP9lKCP+ZSgk/2UoJv9HNoX/L0HM/ixF2v9VZdz/ZXDX/mlx + 0P9pbMf/aWe8/k5NsP9PW8H/YXfX/h8wtv8bKq3/GSWn/hghn/9pcLT/goi3/qirvP9MTKL/GhaZ/h0Y + qP8fGrr/IhzL/iUe2f8mHuD+JR7g/yYe4P8lHt/+Jh7f/yUe3/8lHt/+JR7f/yUe3/8lHt/+Jh7f/yEb + xv8eGrP+Jh7g/yUe3/8lIeD+I0Xv/yNG7/8jQOz+X3Xy/5Sg9/+Woff+R0nn/8PF8/9XWFj+Bwoh/yI4 + 2/8gQef+I0bw/yM+6tssNcwOLzPYCyws4CEuK+EbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAA6OOQfJR7f/k1P5/9mbOz+PDvk/ywn4f8oIuD/Jh/f/iUe4P8lHt/+JR3f/yUd + 3/8kHeD+XFy5/7a3xf+1t8b+t7jH/5CRmf97XmD+ZSgj/2UoJP9lKCP+ZSgk/2UoJP9lKCP+ZSgk/2Eq + MP8nQuD/Ikbv/iZJ7/9zh/T/hZX1/oeW9v+Il/b/iJf2/m6D9f8hRe//Ikbw/iJH8P82V/H/dIf1/nyO + 9f94ivP/cYbw/muB7P9Tad7/M0bJ/iU1t/8cJ6T/GB6U/hcZj/8ZF5b+HRms/yEbxv8kHdj+JR7f/yYe + 4P8lHuD+Jh7g/yYe4P8lHt/+Jh7f/yUe3v8cGKL+JB3V/yUe3/8lHt/+JDbo/yNG7/8iRu/+MlPw/4mX + 9v96hvL+REXl/7e4wv8HBwf+Bgoc/yJE5P8iRu/+I0Xu/iUu3XIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLSOY6JR7f/iMb3/4xLeL+Z27t/nmE + 8f5yfu/+cHjv/m537/5veO/+am7u/mRq7P5fZez+c3jC/rS1xP61tsb+trfH/pOUnf57XmD+ZSgj/mUo + I/5lKCP+ZScj/mUoI/5lKCP+ZSgj/mUoJP5RKWH+LSrN/iQr5v4kLOT+Ji/l/icx5v4nM+b+JzTm/iY0 + 5v4kMub+JDLn/iQ26P4nQOv+Ql3w/lpz8/5xhvT+hpX2/pOf9v6Yovf+laD2/o2b9/6Bkvf+b4P1/ltx + 7/5CW+P+KULS/hwwvv4bJ7H+HSGu/h4bsv4fGcD+IxzS/iUe3P4mHuD+Jh7h/iUe3f4fGrT+FxSH/iMc + zv4lHuD+JSHg/iNB7f4iRu/+Ikbv/jBS8P4qS+7+gJX1/lRUVf4AAAD+AwYU/iFC3v4iR+/+JDLlwSQ0 + 3QwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABmZOpRc3Ps/5eY8v+vsvX+MCzh/yMc3/8jHN//JBzf/iYg4P8yMOL+XGHr/3R98P98hfH+gIjZ/6qr + uv+jpLD+t7jH/5mapP+VkZn+ZjIu/2UnI/9lJyP+ZSgk/2UoJP9lJyP+ZSgk/2UoJP9lKCT/ZSgl/lAk + Yv80IbT/JB3e/iUe3/8mHt//JR7f/iYe3/8mHuD/JR7f/iYe3/8mHd//JR7f/iUh4f8jJuP/Ii/m/ig8 + 6f81Tu3/SWLw/lt08/9tgvT/fY71/oqY9v+UoPb+lqH3/4mY9v9xhfX+VnD1/z1b8P9rgOL+QFTF/0ZP + rf8YHZf+FxWH/xUSfP8UEnn+FBN6/xYTgP8hG8T+Jh7g/yUm4v8jQ+3+I0bv/yNG7/8fRO7+qrPS/wwM + DP8AAAD+AwYN/yFB1v4kMt7LJTS9FQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC2tvZt0ND7/9DQ+v/P0fv+Qj7k/yUe3/8lHt//JR7f/iYe + 3/8lHt/+JR3f/yUe3/8mH9/+Pz7j/5KWs/+lprL+trfH/7CxwP+QkZj+lJCY/3hcXf9uRUT+ajw5/2k4 + Nf9oNjP+aTc0/2k5Nv9sPz3/c1BQ/oFqbf+SiZL/j5DF/lRT0/4qJNv4JR7f/iUe3/8lHt//JR7f/iYe + 3/8mHuD/JR7f/iUe3/8lHt//JR7f/iUe3/8lH9//JSHg/iQm4v8kLuX/JDrq/ilF7f8yUu/+Q2Hx/1ly + 8/9xhfT+hpX2/5Cc9v+1vvn+o7H5/6+9+v9FYe/+LUrh/x81xP8YIp7+FRWA/xQSef8VE33+Hxq4/yUe + 3/8lJeL+Iz7r/yNG7/9BYvL+fn5//wAAAP8AAAD+BAUK/ykuuKsjKocVAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACsr/V0paf0/nFx + 7P5FQuT+JyDf/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4oIt/+f4ny/o6X3v6io7H+trfH/ra3 + xv6xssD+k5Sc/q2uvP6ztcT+r7G//q2vvf6srbr+ra68/q+xv/6wssD+qqy5/p2eqOmNjpXCeniTjWhm + lGBAPb44KSLdeyUe39olHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR3f/iUf3/4lI+H+JSzk/iM16P4hP+z+JUjv/jFT8P6Emfb+lKX3/rrD+v6Woff+kJ32/n2O + 9f5bdPT+J0Xj/hwuuf4WGIf+FBJ5/hsXof4lHtr+JR/g/iQn4/6JlfH+KCgo/gAAAP4AAAD+CwsL/nV3 + h2MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABBPON8Ihvf/y8p4f83NOL+JR7g/yYe4P8mHuD/JR7f/iYe3/8lHt/+Jh7f/yUd + 3/tRU+eLhY7vb4yV7YOLktKZkJGew6qruPm1tsX+t7jI/7a3x/+2tsb+sbLB/6iotfeam6TbjIuSsoJ9 + gYZ5cXNTeGxyJHhrfgUAAAAAAAAAAAAAAAAAAAAAAAAAACoj2wUmHt9CJR7foiYe3+gmH+D/JR7f/iYe + 4P8mHuD/JR7f/iYe3/8mHuD/JR7f/iUe3/8mH+D/JR7f/iYe4P8lHt/+Jh7f/yYe4P8lHd/+JSHg/y01 + 5v+gr/b+UWvx/1589P8zVPD+XXbz/4OS9f+VoPb+hpb2/y1P8P8jRu7+HjXJ/xgfl/8WFIH+Hhm08CYg + 3tiqrNT6AAAA/wAAAP8AAAD+HBwc/qurrpAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACHiO+Ira/1/8LD+f/Bw/j+KCLg/yYe + 4P8mHt//JR7f/iYe3/8lHt/+Jh7g/ych4K0+OuAGAAAAAAAAAAAAAAAAl5q4B4qLljSKipJshoeMkoSF + ioyFhYpxiYiOTYaEiSePiZAOjoeNAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJR3fASUe3xklHt9fJR7fvSYe3/IlHt/+JR7f/iYe4P8mHuD/JR7f/iYe3/8mHuD/JR7f/iYe + 4P8lHt/+Jh7g/yYe3/8lHt/+JR/f/3h77P9eX+j+j5Tw/zQ25P8kMOb+Izzq/yZH7v89XPH+Ynrz/zZW + 8P8iRu/+I0bw/yJG7v8gPNj+HzK+jkVRwzivsLTvAAAA/wAAAP8AAAD+Ozs8ura2uK0AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAC0tfaW0ND7/s/P+/6ztfb+Kybg/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f1zAq4RoAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJR7eAiUe3yomH996JR7f1yQd + 3/0jHN/+Ixze/iMc3/4jHN/+Ix3f/iQd3/4kHt/+Jh/g/ism4P5IRuX+fYHt/nh97P6DiO7+VFPn/iUe + 3/4lHt/+JR7f/iUi4f4lLOT+Iznq/iJE7v4iR+/+Ikbv/iJG7/4iRu/+Ikfv/klo8uqKior9AAAA/gAA + AP4AAAD3SUtUOsXFxcV6gJoGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACHie+bZGPp/zUx4v8hGt/+Jh7g/yYe4P8mHuD/JR7f/iYe + 4P8lHt/uJh/fLgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAKyXgAkFA5Ddtc+uRfITt4HqC7f59he3/hY3v/n6H7v+Bj+7+gY7u/4SL + 7/9/h+7+dHrs/15g6f8pI+D+JR7g/yYf4P8lHt/+Jh7g/yYe4P8lHt/+Jh7f/yUg4P8lLOT+Izvr/yNF + 7v8iR+/+I0bv/01s8v+Dg4P+AAAA/wAAAP8PFzmsU16QB8HBwsB5hLcdAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLR+WNWFbo/3t7 + 7f+Hiu/+JR/f/yYe4P8mHt//JR7f/iYe4PIlHt9QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU1LmEUZD + 5E04NOKlMy7h6TIt4v0wLOH+Lyvh/y0o4P8pI+D+JR7f/yUe3/8lHt/+Jh7f/yYe3/8lHt/+JR7g/yYe + 4P8lHt/+Jh7f/yYe3/8lHt/+JR/g/yUk4f8kMuf+I0Ht/01s8v/R0dH+AgIC/wYLH/8hQtv4JEbov4yZ + 0tOBkM87AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACNk/BN09L7/s/P+/7P0Pv+LSng/iUe3/4lHt/+JR7f+iUe32gAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQkDjGjQv4WApIuC4JR7f+SUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR/g/kpP + 6P78/P3+MDAy/hkxnf4jRvD+Ikbu/jtb8P6UqPbSc4zxV1hz7AUAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB0dOwFn6Dz2ZOT8f9aWOf+Jh/g/yYe + 4P8mHuD7JR7fbSUe3wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAMCvhHCgi4G4mHt+/JR7f9SUe3/8lHt/+JR7f/yYe4P8lHt/+JR7f/yUe3/8lHt/+Jh7g/yUe + 3/8lHt/+Jh7f/yYe3/8lHt/+Jh7g/0lH5f/7/P7+gIK3/yMz5f8jQ+3+I0fv/z1d8f+Uqfj+bIf1/yRI + 79ExU/BUJ0rvBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAASUXlVyYf4OwlHt/+Jh7f/iYe3+4oIOBqJR7fAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlHt8FJR7fKiUe33YlHt/BJR7f7yYe + 4P0lHt/+JR7f/yUe3/8lHt/+JR7f/yUe3/8lHt/+Jh7g/yUe3/8lHt/+JR7f/y0n4P/Nz/f+dnfr/yQd + 3/8lIeD+Iy7m/3WL9P+Oo/f+YH30/yJG7/8iRu/7I0bvuCJE7jMiRO4BAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEpF5SMzLOFUMCnhVy8p4SoAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAlHt4CJR7fICUe314lHt+sJR7f8iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/41L+L+KCLf/iUe3/4iG9/+ZGXo/s3Q9/6NmfL+SmXw/iJH7/4iRu/+Ikbv/iNG + 7/AiRe56IkPtAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJR7fDyUe + 31MmHt+jJR7f5SUe3/4iG9/+Ihvf/yIa3/8iGt/+Ihvf/yIb3/8kHd/+My7h/15c6P+gpfH+tbv1/5GY + 8P+PlPD+Jh/f/yUp4/8jPOv+I0bv/yNG7/8iRu/+I0bvuyND7ScAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnIN8BKyTgFTMv4U+Bgu2ghoru45WW8PyOkO/+jZDv/5Ka + 8P+aovH+qbT0/6y09P+Xn/H+gIPu/1JS5/8pI+D+Jh7f/yYe3/8lHt/+JSjj/yM86/8jRu7+I0bv/yNG + 7+UiQ+1eIz/sBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAU1PmEkI/5E04NOKbNDDi6jQw4v40L+L+MCzh/ikj4P4kHt/+JBzf/iUe3/4lHt/+JR7f/iUe + 3/4lHt/+JR7f/iUe3/4lKOL+Izzr/iNG7/4iRu/8IkXunyM96xMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASEXkCDk14kYxK+GWJR7f3iUe + 3/0lHt/+Jh7g/yYe3/8lHt/+Jh7f/yUe3/8lHt/+Jh7g/yUe3/8lHt/+Jh7f/yUo4/8jPOv+I0fv/yNG + 79IjOuoZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAPzvjEC4o4UMnIOCTJR7f3CUe3/klHt/+Jh7f/yYe3/8lHt/+Jh7g/yUe + 3/8lHt/+Jh7f/yYe3/8lH+D+JC3l/yNE7u8jO+omAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJR7eDCUe + 30AlHt+KJR7f2iUe3/0lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR3f9iQ16GsjNugCAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJR7fAiUe3yclHt9lJR7foyUe39slHt/uJh7g4iUf + 4KclIOByJSXiJQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJR7fAyUe3xAlHt8XJR7fEiUf3wMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////g//////////////+AP/////////////8 + AH/////////////4AH/////////////wAD/////////////gAD////////////4AAD////////////gA + AB///////////4AAAA///////////gAAAAf//////////gAAAAf//////////gAAAAf//////////wAA + AAf//////////4AAAAf//////////4AAAAf/w////////8AAAA/+AP///////8AAAA/8AH///////+AA + AA/4AD//////x/AAAA/wAB//////AAAAAB/gAA/////8AAAAAD/gAAf////8AAAAAH/AAAL////+AAAA + AH4AAAD////hwAAAAHgAAAD////A4AAAAAgAAAD///+AfAAAAAIAAAD///8AfwAAAAAAAAD///4AP4AA + AAAAAAD///4APAAAAAAAAAD///wAAAAAAAAAAAH///wAAAAAAAAAAAH///gAAAAAAAAAAAH///gAAAAA + AAAAAAH///gAAAAAAAAAAAH///gAAAAAAAAAAAP///gAAAAAAAAAAAH///AAAAAAAAAAAAH//+AAAAAA + AAAAAAH//4AAAAAAAAAAAAD//wAAAAAAAAAAAAD//wAAAAAAAAAAAAD/gAAAAAAAAAAAAAD/AAAAAAAA + AAAAAAD/AAAAAAAAAAAAAAB/wAAAAAAAAAAAAAAf+AAAAAAAAAAAAAAf/AAAAAAAAAAAAAAf+AAAAAAA + AAAAAAA/+AAAAAAAAAAAAAP/+AAAAAAAAAAAAAP/+AAAAAAAAAAAAAf/+AAAAAAAAAAAAA//+AAAAAAA + AAAAAB//+AAAAB8AAAAAAB//+AA4Af/AAAAAAB//+AB////4AAAAAA//+AD/////AAAAAA//+AH///// + 4AAAAA//+AP//////AAAAAP/+AP//////4AAAAD//Af//////+AAAAA//h////////wAAAAf//////// + ///AAAAP///////////wAAAD////////////AAAB////////////4AAA/////////////AAA//////// + /////4AA//////////////AD//////////////4P//////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + ////////KAAAAEgAAACQAAAAAQAgAAAAAABgVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAgHMRsDAxZxAwMVhQICEXEFBBspCAggAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcGmkCCAglbgYG + IfMMDED/Cgk4/gUFGv8AAAD7BgYKniAgKwoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFhYgwEdHT6FGRaV/SQd4v8vKt3/UU7R/2Bf + y/88PI//BwcT/hIUHYQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAADY3SW87OqX9eHfS/7i52f7p6ur//f38/vj5+//s7vf+pqmz/x4d + HO0hLWIMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvsHNAa+y + whaipbBWsLG2ps7Oz/nx8vX/3OL6/6e39v92jfD/S2fr/zJT7P8qTfD/LlHw/ztUw/8kNIUyAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKOmtxujpbBlvr/FtuDg4PD19fX+zNLs/4mb + 6P5HZOz/JEju/yFF7/4jRu//Ikbv/iNG7/8iRu/+I0bv/yJG7/4lRuirJDbGBAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAJ2jwRego7NovLy/vebn5/fq7fj+tcDw/3OI6f83Vuf/Ikbu/yJG7/8jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//I0Ht/yUj4f8kOOn+I0DocgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjZS2GtPT + 1efk6Pj/obH1/1137/8uUO3/IUXv/yJG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//JDDm/yYe3/8lH+D/Izrq9SM66SsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhpLIBIGNwaAkSO/+Ikbv/yJG + 7/4jRu//Ikbv/iNG7/8iRu/+I0bv/yJG7/4jRu//I0bv/yJG7/4jRu//Ikbv/iNG7/8iR+/+JSbi/yUe + 3/4lHt//JSfj/iM+62IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFhx5QwjR+/jI0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//JSHh/yYe4P8mHuD/JSrk/yM5 + 6kUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlSO1tI0bv/iJG7/4jRu//Ikbv/iNG7/8iRu/+I0bv/yJG + 7/4jRu//I0bv/yJG7/4jRu//I0bv/iNG7/8jP+z+JR7f/yUe3/4lHt//JC7l+yQw5hUAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGBSIBBAQXDgMDFAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAjRu4NI0bv2SNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8kMeb/JR7f/yUe3/8lJeL/JSXizgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAADQtRCgYFJHcBAQXPAAEC7gAAAOsAAAPDAwMXTAYGJAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAI0TuWyJH7/4jRu//Ikbv/iNG7/8iRu/+I0bv/yJG7/4jRu//I0bv/yJG7/4jRu//I0bv/iNE + 7v8lJuL+JSDg/yUj4f4lL+b/JSDghgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOC1YeBwckyBQS + dP8dGaz/IBu5/hsXof8ODUz+AAAB+wMDEokNEVADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAND7nBzM95wQAAAAAAAAAAAAAAAAAAAAAIzfpAiQ8 + 67YkNej+I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yQ26P8lLuX/JiDg/yUv + 5v8lJuLpJSPhGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABIQbRcPDlPVIx3Y/yUd6P8lHej/JB3l/yQe + 2P8jHsf/Fxds/wkMLf4OFlpFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJCrkBSY051gmL+WrMTrm3EBK6dQqL+S3Ji7lmCUr5HQkMudUJDXoMSc46CQmIuHGJCre/h4w + wv8gPtz+I0fv/yJG7/4jRu//I0bv/yJG7/4jRu//I0Ds/iUw5v8lK+T+JSDg/yUn4/4lIOBnAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAFxSDBBIQYrIkHeT/JB3k/iQg0f8kIMz/JSDK/iUf0v8lHt7+JR7h/yUp + 5P4iRezaHzvWUgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgJ8oEJC7ksiUf + 3/4nIeD/R0jn/z495f81M+P/JR7f/yUe3/8lHt/+JR/g/SUj4fgrL+TwO0TZ/ycuwf8bIaj/Gyas/x82 + yf8gOtT/ID7c/yFC5f8iNt//JSTg/yUt5f8lH+D/Jh7g/iUf36glI+ECAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAExFnXyAduP4gHa7/JB/F/yUf3v8mHuD/Jh7f/yYe3/8mHt//Jh7f/yUe3/8jQu3/I0bv/CpM + 61YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGxqlFyMe0XwlHt/lJh7f/yYe + 4P8lHt//JR7g/yUe3/8mHt//Jh7g/yUe3/9VWOn/lqL3/4+a9f9/ifP/aHLv/09Y5f8yO9L/Hie8/xok + qf8YHJD/FxWL/x4Zsv8kHtn/JR/fxSQl3xAAAAAAAAAAAAAAAAAAAAAAAAAAACQd1xwjHdNcHhqk4iQe + zf8mHuD/Jh7f/yYe3/8mHuD/Jh7f/yUe3/8mHuD/JR7f/yYe3/8jO+r/I0bv/yNH7+E/WtkToarVNwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAACAcvHwUFHVsICTY4EhFwAQAAAAAiHb8IJR7aYiUe380lHt/9JR7f/yUe + 3/4lHt//Jh7f/yUe3/4mIN//PDrk/lJV6f9kaez+dn/w/4qV9P6Wovf/jJj1/3V98P5fa+//P07m/iEt + zf8dKrr1IzLiWyMs4hUAAAAAAAAAAAAAAAAlI95RJR7f2SYe3/0lHt/+Jh7f/yUe3/4lHt//JR7f/iYe + 3/8lHt//JR7f/iUe3/8lHt/+JR7f/yUe3/4jPOv/Ikbv/iNH7/6Zpdi5QUZdawAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAMC0VLDg5Q7AgIKf8AAAD6BQQhigAAAAAAAAAAAAAAACUe3gYlHt9IJR7ftiUe3/kmHt//JR7f/yUe + 3/8lHt//JR7f/yYe3/8lHd//JR3f/yol4P9HSOf/anPu/3uG8f90ffD/dHzw/0FA5f8lH+D/JSPh/iUs + 5O4kNOi/JDjpfiQx5hYkK+EPIiDNkh8at/UkHdT/JR7e/yUe3/8lHuD/Jh7g/yYe4P8lHuD/JR7f/yUe + 3/8lHt//JR7f/yUi4f8jRO7/IkXv/4CW8f1CQkPzICMzXQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQTYDAREWTwGxel/yQe + 4/4YFYn/AgIJ/gYFJVQAAAAAAAAAAAAAAAAAAAAAAAAAACUl4iwlIeGYJh7g7iUe3/4lHt//JR7f/iYe + 4P8lHt/+JR7f/y0o4f51fvD/laH3/5Wh9/6Pm/b/VVnp/iUe3/8lHt/+Jh7f/yUe3/4mHt//JR7f/iUl + 4t0jP+uPIkHrfSAz0J4aJKL+GSOh/xogn/4aHZ3/Ghqa/hsXof8kHdb/Jh7g/iYe4P8lHt/+JSDg/yM9 + 6/4iRu//X3v0/pucnv4BAQH+IiMsTgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHRxyDSQmbs8gHLb/JB3n/yUd5/8lHej/FhSD/wID + EasAAAAAAAAAAAAAAAAAAAAAAAAAACM26AcjOOkhJDXoZSQv5uV1eNP/Z2jU/yUe4P8lHt//JR3f/4CB + z/9oZ9T/U1bp/5Ke9v+Tn/b/k572/1ZZ6f8vKuH/JyDg/yYe3/8lHt//JR7f/yQv5v8jRu//I0bv/yNG + 7/8jR+//I0fv/yNG7/8jRu//IkXr/xsmpv8XFIb/GRWR/xoXnP8gHMD/Iznn/yNG7/8lSe//1Nz5/ycn + J/8DAwP9UVFUOQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAHB1Qfzw7wP4iHNf+IBrE/yQd5/4lHef/JB3c/wUHHr0AAAAAHDjADSJF + 7TsiRe51I0XuriNG798jRu/8I0bv/yJG7/5ke93/nJ7L/iYo4v8lHt/+JR7f/25t0v6Sksz/JB3f/zQx + 4v44NuP/RETm/nuE8f+Tn/b+a3Pu/yUd3/4lIOD/JDfp/iNG7/8jRu//Ikbv/iNG7/8iRu/+I0bv/yJG + 7/4hP97/GiSi/hwttf8gPdn/IUHi/iA71f8cLLP+GCCX/xkkof49VNb/uLq9/gAAAP8aGhr9ZGVlMwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAcHGMNMDJ26SMdzv8ZF5D/FhWC/yQd4/8kHef/JR7q/xUkfNoiReu4Ikbu7iNG7/0jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/9FYub/ur7G/0tm5P8jPev/JSvk/0dF2v+xssf/KCLf/yUe3/8mHt//JR7f/y0o + 4f9hZez/NjPj/yUn4v8jP+z/I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yA+3P8bGqD/JB/Z/yM/ + 7P8jRu//I0bv/yNG7/8jRu//Ikbu/yA+2/9JVLX/YWJq/wAAAP9OTk/7b29vJAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEUFNKSqK/h8a + xv8VE3n+Gxeh/yQd5/4kJOj/Izzt/yNH8P4jRu//Ikbv/iNG7/8iRu/+I0bv/yJG7/4jRu//Ikbv/yJG + 7/4sTuz/mKTP/o+d0v8iRe/+K07w/ylI7P66vMb/OkTh/yQt5f4kLOT/JC3l/iQy5/8kOOn+I0Dt/yNG + 7/4jRu//Ikbv/iNG7/8jRu//Ikbv/iNG7/8iRu/+IkTr/xodnf4lHtr/JR7f/iQu5f8jRu//Ikbv/iNG + 7/8jRu/+Izrq/yQx5v4hKbT/AQEB/gAAAP+GhobramptCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALDDOHGhqB/x8Zw/8gGsX/IyPX/yQ1 + 6f8jRe//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//J0rv/y1Q8P8jRu//QmLx/yhM7/+Infb/qrPR/32P + 1v9HZOX/YX30/zFS8f+dps7/WHHg/yJG7/8jRu//I0bv/yNG7/8jRu//Ikbv/yJG7/8jRu//I0bv/yJG + 7/8iRu//I0bv/yVI7/8iRu//HTG+/yEbxP8lHt//JSDg/yNB7f8jRu//I0bv/yNG7/8kLOT/JR7f/yUe + 3/8jHc7/AwMP/wMDA/+SkpmLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHCCOpFBJ7/yAouv8jPOH/I0Xq/yNG7/8jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//JEjv/0Vn8v8vUvD/WHbz/zdY8P9rhfT/oavU/0Vj5v+nsNP/k6n3/yZK + 7/90h9n/fI3X/01t8/8yVvH/WHfz/0Ji8f8iRu//MFLw/1Jx8/8rT/D/IUXv/1578/9HZvL/LVHw/0ho + 8v8hRfD/Gx+m/yUe3/8mHt//JDLn/yNG7/8jRu//I0bv/yQv5v8lHt//JR7e/x4ZtP8uLLX/BgYd/woK + GvxYV5c2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAABkupwEPG1i7HTC8/iJG7/8iRu7+I0bv/yJG7/4jRu//I0bv/yNG7/4jRu//Ikbv/iNG + 7/8iRu/+I0bv/yJE7v5Qb/L/Y3zy/ztY7/5NbPL/dIfZ/lFs4v+HltX+aoTp/zhZ8P5MaOT/oqvM/1Ny + 8/4qTvD/H0Pv/kxr8v9be/T+j6T3/z9f8f5EZvL/Ikbv/jdb8f9ObfL/Tm7y/miE9P8gQOP+Hxq2/yUe + 3/4lH+D/I0Lt/iNG7/8jQ+7/JSrk/iYe3/8mHuD+HRmo/5WWvv7s7Or/tbfG/jEvpv8vLdOXAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHz/ZCSFC + 4pIjRu/5I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8iRu//I0Xv/yQ76v8kLuX/JSXi/yUh + 4P9AQOT/iY7u/yol4P+rsfT/oqnj/3Z60/9cYOH/maHR/0VW6/8wQeX/t7rG/0Vc7f8uSOz/N1fv/0tq + 8v9+lfX/aIHz/0Nf8P9KZvD/j6P2/01o8P9TavD/K0ru/z1c8P8fOtL/IhzJ/yYe4P8lI+H/I0bv/yM7 + 6v8lIuH/JR7f/yYe3/8kHdn/VVah/+np6f/p6en/6enp/8fI3P8vReLcXmnLAgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiRu4cIkbuxCJG7/4jRu//Ikbv/iNG + 7/8iRu/+I0bv/yJG7/4jRu//Ikbv/yxM4v5RY8j/T1HC/iYe3/8lHt/+Jh7f/ycg3/4uLOH/PTzj/zs3 + 4/5VU+b/oqPr/piZy/8kHOD+jo/O/1xd3f4lHt//o6TJ/1ZV3P4vKeH/k5Tv/nV36/8pI+D+JB3f/yUe + 3/4nIOD/NzLi/iYe3/8lHd//JR7f/iUf4P8gIMD+Ix3R/yUe3/4lIeD/JC3l/iYf4P8mHt//JR7f/iYe + 3/8gGsH+YmR6/87Ozv6+vr7/2NjY/uvq6v92i+z8UGXhMgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAChG6SAjRu/YI0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG + 7/8jQu3/QlPL/6Cjuf+urrv/hoe3/yYf3/8mHt//Jh7g/yUd3/9HSOX/WVvn/ygi4P8lHuD/JyHf/6iq + yf8pI9//Qj/a/42Ozf8lHeD/fXzQ/3V41f8qJeD/JR7f/1VV5/8wK+H/Jh7g/yYe4P8mHt//Jh7f/yUe + 3/8lHuD/JR7f/yYe3/8gG73/Ix3T/yYe3/8lHt//KCbh/ykp4/8mHuD/Jh7f/yYe3/8eGq3/AQIF/wcH + B/8CAgL/Dw8P/3R0dP+bqez/R2HgZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAElJ + 0AlITNQQQk3dCSVI7sIjRu//I0bv/yJG7/4jRu//Ikbv/iNG7/8iR+/+I0Lt/yQv5v5QT7//ra6+/7a3 + xv6lprH/VlO//iUe3/8lHt/+Jh7g/yUe3/4lHd//Ixzf/yUe3/4mHt//JB3g/pucy/9APNv+JR7f/5GS + zP4+O9v/VlTX/6Ck0P5lZen/JyHg/ldX5/85NeL+Jh7f/yUe3/4lHt//JR7f/iUe3/8lHt//JR7f/iUe + 3/8gG73+IxzP/yUe3/4mIeD/cYLz/kNH5/8lHt//Lyzh/iUe3/8eGrL+AAEF/w4ODv6RkZH/sbCx/mpq + a/+eqdz/QFrYegAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEI+yjQrJsKoS03S0XF65exuduTzP0DN5V9t + 5vxRY+f/Q1nn/zJN5/8mRer/I0Xu/yM96/8kLeX/JSDg/09Nv/+vsL//trfG/7a3xv+2t8b/ZmW7/yUe + 4f8lHuD/JR7g/yUe4P8lHt//JR7f/yUe4P8lHt//JR7g/3180P9hX9X/JR7f/0hF2f+Jic7/MSzd/62v + x/9qaun/lprw/1lX5/8lHd//JR7f/yUe3/8lHt//JR7f/yUe3/8lHt//JR7f/yUe3/8hG8X/IRvD/yUe + 3/8lHt//Z3Lv/01R6P8lHt//W2Dq/ysl4P8iHMn/AwMM/4WFhf/q6ur/6enp/39/gP9EToT/OFHVdQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAERAyyptbNuupabs6bKz8P6JiOv+Jh/a/y8r3P41Mtv/RUXe/15i + 4v5ob+T/LCvU/iUe3/8lHt/+PzzI/6yuvv62t8b/trfG/7a3xv60tcT/lZOj/lY5c/9YJkr+WCZM/00l + a/4/I5T/MCDC/yUe4f4lHt//JR7f/l5d1f+FhM7+JR7f/yUe3/6Xmcv/Ojfc/6iqyP43Mtz/JB3f/iUe + 3/8lHt/+JR7f/yUe3/4lHt//JR7f/iUe3/8lHt//JR7f/iYe3/8kHdP+Hhmz/yUe3/4lHt//Pknp/ktU + 6v8lHt//YGXr/klK5/8lHt/+Hh5V/9bW1v7p6en/5OTk/icnJ/9OW6f/OEXlyDpB3TgAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAhoflCISJ6zFoa+lyJSnj6yUm4v8lH+D/Jh7g/yUe3/8lHt//JR7f/yYe + 4P8lHuD/hoe1/7a3xv+2t8b/s7TC/5OPl/9xSUj/ZSkl/2UoI/9lKCT/ZSgk/2UoI/9lKCP/ZSgj/1on + Rf9AI5L/KR/V/0A82/+nqcj/Jh/f/yYe3/9QTtj/hIXP/4WEz/9VU9f/JR7f/yUe3/8mHuD/JR7f/yUe + 3/8mHuD/Jh7f/yUe3/8lHt//Jh7g/yYe3/8lHt//HBim/yYe4P8lHt//JDDm/yQx5v8lHt//XWLr/3Z+ + 8P8lHt//My/B/9LT2/+Ghob/JCQk/zM0Ov8ySMr/Oz7m/2Jo7Po9POR6AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABJSuUSJT3r6iUl4v4lHt//Jh7g/yUe3/4lHt//JR7f/iUe3/8pI9f+pqe6/7W2 + xv61tsX/kZGZ/2o6OP5lKCP/ZSgj/mUoJP9lKCP+ZSgk/2UoIv5mJyH/Zici/2YnIf5mJx//YCcz/ksr + g/+4ucL+KyfH/yMd0f4nIN3/n6HK/25u0/56edH/JR7g/iUe4P8lHt/+Jh7f/yUe3/4lHt//JR7f/iUe + 3/8lHt//JR7f/iYe3/8mHuD+HRmt/yUe2v4lHt//JCvk/iND7f8lI+H/XGHr/pOe9v9EReb+X13m/4SG + ov4yM0X/ISI0/kJOrv8jPOfhJR7f7SUe3/4yLuK0ODbjAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAsKOF6Pkbo/ysl4P8lHt//JR7f/yYe3/8lHt//JR7f/yUe3/8uKtH/rq++/7a3xv+oqbX/g3V5/2Un + I/9lKCT/ZSgk/2UoJP9lKCT/ZCgn/zs8qf8pQ9z/V2ni/3N93v91fNb/dHbN/09VwP9Sadz/KD/J/x4y + vv8nNbf/U2C+/3R/wP+Gjb7/FxaX/xsXoP8eGbD/IRvF/yQd1/8lHuD/Jh7g/yUe3/8lHt//JR7f/yUe + 4P8mHt//Ix3Q/x8auv8lHt//JR/g/yNE7v8jRO7/SmTx/5Sg9/9xee//oaHr/zQ0Nf8XJIX/IULo/yNG + 7/4nNNliLjXVBiws4RoyL+IIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwKuGhLinh/11i + 6/9hZuz/U1bp/0lJ5/9BQeX/Ozjk/zYz4/87O9P/r7C//7a3xv+kpbH/gGpt/2UoI/9lKCT/ZSgk/2Uo + JP9lKCT/ZCgo/zI2vv8jO+v/PVLt/1Fk7/9UaPD/VGjw/z5Y7/8jQu3/I0Pu/0Be8f97jfX/i5r2/4qY + 9v+AkfT/bX/u/1ls4/9DVdL/LD2+/x0rr/8bIav/Hhyz/yEcxv8kHdj/JR7f/yYe4P8mHuD/Jh7g/x4Z + rv8iHMv/Jh7f/yQx5v8jRu//JUnv/2F48/86S+v/lJan/wAAAP8UJoD/I0fv/yQ46M8mM9gLAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABbWei5dXTs/3d47P4sKOH/NDHi/zk2 + 4/5FROb/Zmzt/n+J8v+Eju/+oaO3/6ysuv6rrLn/jYeN/2YtKf5lJyP/ZSgj/mUoJP9lKCP+ZSgk/2Uo + Jf5QJGP/NCC1/yUd3v4lHd//JR7f/iYe3/8lHt/+JR7f/yUi4P4jJ+P/JzLm/zZG6v5IW+7/WnDx/muA + 9P99jvX+jJr2/4ya9v55i/b/YHfz/kVd5P82S9D/TFfE/jU6tP8bGqT+GRWU/xUTff4VE37/IBvA/iUf + 4P8jOun/Ikbv/iJG7/9jffL+OTk5/wAAAP4TJHD/Izvm4SY2yyYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAHV07APQ0PvPzc76/6iq9P8lHt//JR7f/yYe3/8lHt//JR7f/yUe + 3/8tKOH/iY/F/6ytuv+1t8b/m5ul/4uFi/+CZ2v/eFdY/3VRUf9zTk7/dlJS/3tcXf+JdHn/mJCa/4yN + wPpHRcXqJh7f8iUe3/4lHt//JR7f/yUe3/8lHt//JR7f/yUe3/8lHuD/JSDg/yQl4v8kLuX/Jzrq/zFM + 7v9DYPH/WXLz/3OG9P+NnPb/t8H6/5+u+P9PaO3/MEfU/xwqrv8WF4X/FBJ7/x4Zs/8lH9//JDPn/yJD + 7v+Aj8n/BAQE/wAAAP8jJ1vcJCmJHwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAI2N8ARkZOnVOzfj/yQd3/4mHt//JR7f/yUe3/4lHt//JR7f/iUe3/tude7Okp3y2Zaa + u+uxssD9t7fH/6usuv62t8b/t7jI/ra3x/+ur7z7oKGs4ZSSm7eGgoyHeHCIV2FamSlLRrYJMSvXGycg + 3nQlHt/QJR7f/SUe3/4lHt//Jh7g/yUe3/4mHuD/JR7f/iUe3/8lHt/+JR7f/yUe3/4lI+H/JSvk/iM1 + 6P9GYfD/gJf2/oGW9v94ivX+k5/2/36P9f43Vuz/HjTE/hcdkf8ZFpb/IxzO/i8r4f9vcX3+AAAA/wAA + AP5gYGHXR0pqAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIuL + 7wZ6eu3doKHz/4qL8P8lHuD/Jh7f/yUe3/8mHt//Jh7f/ykj4JtPTt4DcXLWBZCTyQqLjZw/k5Sdi5OT + nK2QkJegkpGZfYiGjFCGgYcgi4GIBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlHt4FJR7fNyUe + 35QlHt/kJR7f/iYe3/8mHuD/JR7f/yUe3/8mHt//Jh7g/yYe3/8mHt//JR7f/yok4P+Fie7/cHXs/zU/ + 5/8kOur/MU/v/1hx8v9bdPP/Ikbv/yJG7v8gO9T/HSuxt2Rq1W5MTEz/AAAA/wAAAP2KiovEZWZzFAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKB7gnMzfrntrf3/3d3 + 7P4lHt//Jh7g/yUe3/4lHt//Jh7fyzEs4Q4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlHt8NJh/fUCYf + 3641MuL0OTfi/jk34v87OeL+Pj3j/0A+5P5FQeX/U1Tn/oKI7v90eOz/U1Ln/iUe3/8lHt/+JSDg/yUq + 5P4jN+n/I0Tu/iNG7/8jRu//Ikbv/nKK8PMoKCj+AAAA/wMDBr+anKR8jZGoMQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIyM7wk9O+PnOjbj/z064/8lHt//Jh7g/yYe + 4P8lHuDjJR7fIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABKSOUWYmPpaF9h + 6MFeX+j3WVvo/1dd6P9VWef/VFXn/0ZD5f8oIeD/JR7f/yYe4P8lHt//Jh7g/yYe3/8mHt//JSDg/yUs + 5f8jO+r/I0Xv/3eP8/9AQED/AQIG/x82obV0gb97jZfDVgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAISE7gKkp/TAzM36/6ip9P4lHd//Jh7f/yUe3+4lHt83AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEpK5QFAPuMnMy7heycg + 4NQlHt/+JR7f/iUe3/8lHt//JR7f/iYe3/8lHt/+Jh7g/yUe3/4lHt//JR7f/iYe3/8lHt//JSTh/naA + 7/+Xl5f+ESBk/yNG7/4mSe79jJ/uxGqD7TQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAB9fe1ZfHvt/UM/5P8lHuD/JR7g7yUe3z8AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADg04gErJeAyJh/fiSUe + 39olHt/8JR7f/yYe4P8lHt//JR7f/yYe3/8lHt//JR7f/yYe3/8lHt//Jh7g/25s6v/Z2uX/JCzY/yM9 + 6/8jR+//jaP3/1d18/wpTO+vKEvvLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAB0cuwFPDbjeicf4L8nIOCxLCXgNQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUe3wglHt84JR7fgiUe + 384mHt/6Jh7f/yUe3/8lHt//JR7f/yYe3/8lHt//JR7f/zAq4f94eOv/JR7f/yQe3/9udu3/m6r2/0pp + 8v8jRu//I0bv8yNF74YjRO4JAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlHt8mJR7fdCYe + 38MlHt/3Ixzf/iMb3/8jG9//Ixvf/iMc3/8oIuD+SUbl/4mL7v6qsPP/k5nx/ior4/8kOOn/I0Xu/iNG + 7/8jRu/LI0PtLgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgh3wItJ+AjY2PoboSI + 7sKDhO72gYPt/4WL7v+Nle//kJbw/3h87P9ZWOf/LCfh/yUe3/8mHt//JSTi/yQ36f8jRe7/I0bv8CND + 7W0jP+wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAO+MeMy3haycg + 4LwkHN/2JB3f/yUe3/4mHt//JR7f/iUe3/8lHt//JR7f/iUe3/8lJOH+JDfp/yNF7v4jRe6wIzzqFQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE9N5QFBPeMbLynhZiYf + 4LglHt/yJh7f/iYe3/8mHt//Jh7f/yUe3/8mHt//Jh7f/yUl4v8jPev+Iz7rVAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlHt8aJR7fXiUe + 364mHt/qJR7f/iUe3/8lHt/+Jh7g/yUe3/glJ+OWIzboCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlHt8IJR7fNSUe + 33UlHt+DJR/fSiUi4RQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////////wAAAP///////////wAAAP///////////wAAAP// + /////////wAAAP///////////wAAAP///////////wAAAP///////////wAAAP///////////wAAAP// + /////////wAAAP///////////wAAAP/////wP////wAAAP/////AH////wAAAP////+AH////wAAAP// + //+AD////wAAAP////gAD////wAAAP///+AAB////wAAAP///wAAB////wAAAP///gAAA////wAAAP// + /gAAA////wAAAP///wAAA////wAAAP///4AAA/4//wAAAP///4AAB/gH/wAAAP///8AAB/AD/wAAAP// + 88AAB+AD/wAAAP//gAAAD8AB/wAAAP//AAAAD8AA/wAAAP//gAAAHwAAPwAAAP/8IAAAHAAAPwAAAP/4 + OAAAAAAAPwAAAP/wHwAAAAAAPwAAAP/gHwAAAAAAPwAAAP/gEAAAAAAAPwAAAP/AAAAAAAAAPwAAAP/A + AAAAAAAAPwAAAP/AAAAAAAAAfwAAAP/AAAAAAAAAfwAAAP+AAAAAAAAAfwAAAP8AAAAAAAAAPwAAAP4A + AAAAAAAAPwAAAPwAAAAAAAAAPwAAAOAAAAAAAAAAPwAAAAAAAAAAAAAAPwAAAAAAAAAAAAAAHwAAAMAA + AAAAAAAADwAAAPAAAAAAAAAABwAAAPAAAAAAAAAADwAAAPAAAAAAAAAAfwAAAPAAAAAAAAAA/wAAAOAA + AAAAAAAB/wAAAOAAAAAAAAAB/wAAAOAAAP4AAAAB/wAAAOAH///AAAAB/wAAAOAP///4AAAB/wAAAOAf + ///+AAAA/wAAAPA/////wAAAPwAAAPB/////+AAADwAAAP///////4AABwAAAP///////+AAAQAAAP// + //////4AAAAAAP////////+AAAAAAP/////////4AAAAAP//////////AwAAAP///////////wAAAP// + /////////wAAAP///////////wAAAP///////////wAAAP///////////wAAAP///////////wAAAP// + /////////wAAAP///////////wAAAP///////////wAAAP///////////wAAACgAAABAAAAAgAAAAAEA + IAAAAAAAAEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYF + JUwCAhClAgIOrQICDoUICCAmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJyZrCAkJJaoSEGr/GBSH/xQRev8LCkX/AAAC+RUVHFkAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAc3SPBBwbT7IkIM7/SkfR/4aF1/+2ttz/tbbd/2tskf8FBQXvJi5UDwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmp2pIHJzeaGjpMz/4+Pm//P1+//J0vb/nq3w/4WZ + 8/9/lPb/YXCv/xojUFUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtrnJAaOmszCztcGAysrNz/Dw7/3b3+//lKXw/1Fv + 8P8mSe7/IUXv/yNG7/8jRu//I0bv/yJG7/8oR9yjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACan7MxsLK9h83Nzdvx8fT/xMzx/4GV + 6v9AXuX/IUXu/yJG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8kMuf/JDjp/yM+5GEAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACsrrih6ev0/6+9 + 9/9rg/D/LlDs/yFF7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jQe3/Jh7f/yYe + 3/8kOerzIznpIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAho+7US5R8PsiRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//JDfp/yYe3/8lHt//JSbi/yM861YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqTe2iI0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//I0bv/yQx5/8mHt//Jh7f/yUq5P8jNug1AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJUftJSNG7/ojRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8lJuL/JR7f/yUe3/8lLeXyJCzlCQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJCDYDBQUfNgMDFFQDAxNBBAQcCAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAjRu+YI0fv/yNG7/8jRu//I0bv/yNG7/8jRu//I0fv/yNG7/8jRu//I0bv/yNG7/8jPuz/Jh/g/yYe + 3/8lLOX/JR/gvQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJCDtEBAQR3AoKNv8MCzz/BAQS/wAA + AOIFBB1GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAIz7rFyNC7e0jRO7/I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//JC7l/yUo4/8lI+H/JS/m/yUh4FgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKCTlbFhSC+iQd + 4P8lHen/JR3p/yUd5v8UE3D/AAAA+wgLM08AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAKDPmHSk05msoMeWUKTLleykx5VsmL+U4JDHmGCQz5wInM+ZBJSPh9yM75f8hQeL/I0fw/yNG + 7/8jRu//I0bv/yNG7/8jRu//Iz/s/yUw5v8lIuH/JSzl/yUi4cAlJOEBAAAAAAAAAAAAAAAAAAAAAAAA + AAARD108HRmy+SUd6P8kHuD/JCDP/yQgzP8kIMX/JB7J/yMiyP8eOca9HTTFJAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAJDHmiCUj4fswLOH/TlHo/1BS6P8mH9//Jh7f/yUg4P4lJeLwJCzl0Cov + 5OEmKsr/GR+k/xsnqf8fNsr/IUHj/yJE6v8jR+//JDnq/yUq5P8lKeT/Jh7f/yYe3+0lIeAhAAAAAAAA + AAAAAAAAAAAAAAAAAAAZFpQIGhiZ2yActv8jH8b/JSDT/yYe4P8mHt//Jh7f/yYe3/8mHt//Izvr/yNG + 7/AsTekyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsapx4kHtSQJh7g8iYe3/8mHuD/JR7f/yYe + 3/8lHt//Jh7g/yUe3/9nbe3/lKD3/4KM8/9qcvD/UVvm/zM8zP8eJbb/GiSm/xgZjf8aF5z/IhzI/yYe + 4PckI95CAAAAAAAAAAAAAAAAAAAAACQe3AMjHdIwHRmomiIeuv8lHt7/Jh7f/yYe3/8mHuD/JR7f/yYe + 3/8lHt//Jh7f/yQz5/8jRu//JEfuzmJ43RV+ib0bAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALCkEMBAQYYwYHKlgREGoEAAAAACId + xBElHt13Jh7g4iUe3/8lHt//JR7f/yYe3/8lHt//KCLg/0FA5f9UV+n/Zmvt/3yF8f+UoPb/jpr1/3iA + 8f9hbvD/OUbh/x8pxf8hL9KxIzLkMyQr5AMAAAAAAAAAACUh33YlHt/rJh7f/yYe3/8mHt//JR7f/yUe + 3/8mHt//JR7f/yUe3/8lHt//JR7f/yUe3/8kNOj/I0bv/ydL8P6SmLHHP0VjJgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAREF0YDQ1I1hIR + af8DAw7/AgISuw8MXQYAAAAAAAAAACUe3wYlHt9aJR7fyiYe3/8lHt//Jh7g/yUe3/8mHt//Jh7f/yUe + 3/8lHd//Skzn/2537/9+ivL/bXbu/19j7P8nIeD/JR7f/yUj4f8kLeXrJDTnrCQx5jAjLOAOISDEjR4Z + svgiHMj/Ix3Q/yQd1v8lHtz/Jh7g/yUe4P8mHt//JR7f/yUe3/8lIOD/I0Lt/yJG7/+bquT8FBQU9zA1 + SBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAcG3UEExRdwRoWof8kHeX/IhzU/wgILP8FBB9sAAAAAAAAAAAAAAAAAAAAAAAAAAAkKOM8JSDgqyUd + 3/k+Otv/Line/yUe3/8lHt//MCvd/1xf4/+HkfT/lJ/3/5Wg9/9WWer/JR7f/yYe3/8mHuD/Jh7f/yYe + 3/8lI+H0I0fv0SNG778fOdHgHTPC/xwwu/8cLLP/Gyep/xgXjf8hHMb/JB3W/yUe3/8lH+D/Izvr/yNG + 7/+Al/b/aWlp/wYGBvo8PkUKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAHh9ZeS4tuP8lHej/JR3n/yUd5/8fGr3/BQchowAAAAAAAAAAAAAAACNA + 7BYjP+xMI0HtgiNC7bMjP+zxkprP/0xK2f8mHuD/JR7f/1ZU1/+Ymcv/MCzi/2927/90e/D/iJL0/2tx + 7v9UVun/JyDg/yUe3/8lI+H/Iz/s/yNG7/8jRu//I0bv/yNG7/8jR+//I0bv/yA81/8ZIp7/GiOg/xkk + oP8YG5H/HCSw/x861P8hRe//z9Xq/wkJCf8wMDD0MjIzAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBxiEjk7jPMiG9j/FhSF/yEbz/8lHef/JR7o/w0T + SbEeOs9bIUTnmyNG79gjRu/9I0bv/yNG7/8jRu//I0bv/3KF2f+SnND/IzTo/yUk4f8wK97/ubvF/yYf + 3/8lHt//JR7f/yYf3/9xeO//fIXx/ykj4P8lKuT/I0Tu/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/x0v + vP8eHbT/IkTr/yNG7/8jRu//I0fw/yE+3P8cLLT/KTGe/56fpP8AAAD/Z2dn6wAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQRmkuLaH/HBiv/xcX + fP8iHNj/JB3n/yQu6/8jROf+I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/9UbuH/oqzN/0Be + 5/8jRu//ITjq/6uwyf88Pd3/JSLg/yUh4P8lI+H/JCjj/yQv5v8jPOv/I0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//I0bv/x40xf8gGrz/Jh7g/yQz5/8jRu//I0bv/yNG7/8jRu//Iz7s/yo92f8UFBb/AAAA/6Oj + o8wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAMDEGoHh6K/yEb0P8fGbz/JCjj/yM+7f8jR+//I0bv/yNG7/8jRu//I0bv/yJG7/8mSe//I0bv/zVW + 8P8mSu//fJLv/4CQ1v+JmNP/Ikfv/2N+9P+CkdX/X3ff/yJH7/8jR+//I0fv/yNH7/8jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//I0bv/yJF6/8bGqT/Jh7g/yUf3/8jPuv/I0bv/yNG7/8jQu3/JSTi/yUd + 3/8lHtv/AwMO/wICAv+RkJh0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAACAguzhUVfv8kMdz/I0Di/yNH7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG + 7/8iRu//PmDx/zdZ8f9TcvP/Q2Lx/3SM9f+SntL/eYvX/3+V7f9TcfP/W3Pg/4KS1f9EZfL/Nlrx/1h2 + 8/8pTO//J0rv/05t8v8pTe//IUXv/1p38/82WPD/KEzw/0Nk8v8eM8P/Ix3P/yYe3/8kL+b/I0bv/yNG + 7/8jQ+7/JSXi/yUe3/8hG8f/Kyi7/wkJLP8PDyT7WVmQFgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGjCsAxIhcOAfOtT/I0bv/yNG7/8jRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//I0fv/yNE7v9BX/D/aH/y/0hh7/9HZ/L/k5/Q/zJU6/+OndT/UnHz/zRV + 6v+or8v/U3P0/yJG7/8hRu//Xnv0/2aD9P9rhvT/QGLy/yRI7/84XPH/TGzy/2WC9P9FZfL/HSi2/yYe + 4P8lHt//I0Ds/yNH7/8jPuv/JSPh/yYe3/8iHMz/YmOk/+rq6v/Aws3/NzWv/zk6yG4AAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIELhHCJE6cEjRu//I0bv/yNG + 7/8jRu//I0bv/yNG7/8jRu//I0bv/yJG7/8jPOv/JC7l/yUj4f8lHt//Ling/4CF7f8tKOD/sbX0/8PG + 2P87Otz/oKLT/15r5f8jLeX/r7LI/01c6v84TOv/Y3zy/3GH8/9ndu//SFzt/zJI6/+Vo/X/S1vs/zhJ + 6/8ySOv/JEHt/x4iuP8mHt//JR/g/yNG7/8kMuf/Jh7f/yYe3/8mHt//Kiio/+Dh5f/p6en/6enp/9HR + 3/8zTeO1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIkXuMyNG + 7+YjRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//Ikbv/01jyv+CiLz/U1HC/yYe4P8mHt//JR7f/zIw + 4f8yMeH/NTLi/0M+5P+Cg9r/YV7V/zg03P+Ul9D/JB3f/4qKzf9eX9v/KCHg/2lp6f88OeP/JR7g/yUe + 4P8lHt//JBzf/yUe3/8lHt//Jh7f/yYe4f8fGrT/Jh7f/yUe3/8lI+H/Jh7f/yYe3/8mHuD/Jh7h/xwb + Xf9+fn7/d3d3/5WVlf/W1dX/ZH3t915w4AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAMkznLSNG7+8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//JTjl/3J3t/+2t8X/oqKt/0ZD + xP8mHt//Jh7g/yUe3/8yLuH/T07m/yUe3/8mHt//Qj7b/4WEz/8lHd//iInO/zg03P9kYtT/iY/W/ygi + 4P8lHt//UFHm/yUe3/8mHuD/JR7f/yYe3/8lHt//JR7f/yUe3/8mHuH/Hhqz/yYe3/8lHt//O0To/zQ4 + 5f8mHt//JR7f/yYf4P8KCjn/AAAA/xAQEP85OTr/ZmZm/36R6/9XbdUuAAAAAAAAAAAAAAAAAAAAAAAA + AABIRcwRQ0DLR15h2W9fY9yPREXRhjtP48cpROb/IkLq/yFF7v8jR+//I0bv/yNE7v8kNej/JiTd/3t7 + tf+2t8b/trfG/7W2wv8zLsz/JR7f/yUe3/8mHt//JR7f/yUe3/8lHt//Jh7f/ykj3/+kpsn/Ixzf/z47 + 2/+EhM7/PTjb/56gzf+Vl/D/hojt/1VU5v8mHuD/JR7f/yUe3/8mHuD/JR7f/yUe3/8lHt//Jh7h/x4Z + r/8lHt//JR3f/3R/8f8/Qeb/JB3f/0hK5v8mHuH/DQxK/xgYGP/c3Nz/6urq/4WFhv9UZrn/S1/LMwAA + AAAAAAAAAAAAAAAAAAAAAAAAOzbIVFNR0POKjub/l5nq/z072P9MTOH/V1ni/11h4P9mcOP/Wmnl/yQq + 3v8lH+D/JR7f/3V1tv+2t8b/trfG/7a3xv+3uMf/cXK0/z8klf9GJIL/PiKZ/zAgwf8mHt//JR7g/yUe + 3/8kHOD/p6nJ/ysm3v8lHd//j5HN/zUx3f+qrMj/JB3g/yYg3/8lHt//JR7f/yUe3/8lHt//JR7f/yUe + 3/8lHt//JR7f/yUe4P8dGaz/Jh7h/yUe3/9JUOn/VFrq/yQc3/9ye+//JR7f/xcVhf+FhYb/6enp/+vq + 6v8+Pj7/S16//zc/1V0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCg+IJm5zsPZmd8X45OOPOJSHh/yUg + 4P8mHuD/JB3e/yYg3P8lHt//Jh7f/0E+xv+1tsT/trfG/7W2xf+dm6X/eFZX/2YxLf9lKCP/ZSgk/2Uo + I/9mKCH/YScx/0wlcP8xIb7/JR7h/4uLzv9KR9n/Jh7f/0ZD2v9/f9D/k5TM/zg03P8lHt//Jh7g/yUe + 3/8lHuD/Jh7f/yYe3/8lHt//JR7f/yYe3/8mHt//Hhmw/yUe3P8lHt//JC/m/yQt5f8lHd//fYfy/z89 + 5f8jHdL/rq/G/7S0tP9TU1P/LCww/y5F1P9aXur/T1PouDYz4w4AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAMUHpiCQv5v8lHuD/Jh7g/yYe3/8lHt//JR7f/yUe3/9qab//trfG/7a3xv+Wlp//azw5/2Uo + JP9lKCT/ZSgk/2UoJP9lKCP/Zich/2YoIv9mJyH/Zicg/1UlVP95bK3/bGvJ/yMczf8kHd3/lpfM/3h5 + 0f9cWtf/JR7g/yYe4P8mHt//JR7f/yYe4P8lHt//JR7f/yUe3/8mHt//Jh7f/yMczf8gG8D/JR7f/yUq + 5P8jQez/JR/g/3yG8f95gvH/JiDg/5WYwf9JS13/Ghsq/0FNrP8kNujeJR7f+i8q4f01MeI2AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAOTrkBy8y5PQ8OuT/JR3f/yUe3/8mHt//JR7f/yUe3/8lHt//d3e9/7a3 + xv+xscD/gXR3/2UoI/9lKCT/ZSgk/2UoJP9lKCP/SDV+/ypD2/9UaOT/d4Hg/3h/2f92eM7/QlLM/z5Y + 2f8eNcX/LT2//1Ffw/9wfsb/ZW25/xkZmv8bF6H/Hhmz/yIcy/8lHt7/Jh7g/yUe3/8lHt//JR7f/yYe + 3/8mHuD/Hhmw/yUe4P8lH+D/I0Tu/yND7v9qfvP/lKD3/2Rm6v92dnf/EBld/yFC6P8jRe/+KTPTNi0y + 2wstK+EUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE1M5yIlHd//QUDl/2Jo7P9cYuv/V1rq/1Za + 6v9RUuj/TE7o/4SGwP+2t8b/rK27/4Ftcf9lKCP/ZSgk/2UoJP9lKCT/ZSgk/04uav8nM97/MD/p/0BN + 6/9DUev/Q1Lr/yg86v8kOur/LEfs/2N48/97jfX/jZr2/4uZ9/96jPb/ZXnt/1Bi2/83SMX/ITC3/x0l + u/8gH7//IRvI/yQd2f8mHt//Jh7g/yAbv/8dGKz/Jh7g/yQu5f8jRu//K03v/0dg8P+JlOD/CgoK/w0a + VP8jR+//JDTkpAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5d+08mJny/7e5 + 9/8wK+H/JB3f/yQd3/8sJ+H/Tk/o/15i6/91ecn/pqaz/7a3xv+SkZn/bkE//2UnI/9lJyP/ZScj/2Un + I/9lJyP/Yigt/0sxhv8zLtn/JR3f/yYe3/8mHt//Jh7f/yYe3/8mH9//JCPh/yQr5P8uO+j/P1Hs/1Fo + 8f9ievP/don1/4iX9v+Ckvb/aH71/3OH7P9ic9L/LDu4/xkemf8UE3r/FBJ5/xsXn/8lHt7/JDXo/yNG + 7/8kSO//c3eF/wAAAP8NFkb/IzPVsyo8xQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAn6DzTKus9f96ee3/LCbg/yUe3/8mHt//JR7f/yUe3/8nIN//eIHu/5+iuP+2t8b/qqu4/5qa + pP+inqn/mZCa/5eMlP+YjZb/m5Od/52cp/ybnabdiomlrEhGu34nIN63JR7f+yUe3/8mHuD/JR7f/yUe + 3/8lHt//JR7f/yUe3/8lHt//JSPh/yQs5f8jNej/LEjt/0Nh8f9/k/b/sr35/5Og9/9vhPX/TWXp/x8y + vf8WGor/GBWQ/yQd0/8lKeT/W23v/ykoKP8AAAD/Ly876zI3cgMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAGJg6VRKRuX/YWDp/yUf4P8mHuD/Jh7g/yUe3/8mHt//PTvji3l+ + 50yKkNZjkpOil6Kireisrbv9p6i09ZucptaUkpqkiYSKcYB2ej9+cnoSAAAAAAAAAAAAAAAAAAAAACYf + 3R8lHt98Jh7f2SYe3/8mHt//Jh7f/yYe4P8lHt//Jh7g/yYe3/8mHt//Jh7f/yYe3/8kIeD/f4rx/2x/ + 8f8qSO7/UGvy/3uM9f91iPT/Ikbv/x83zf8ZIZz/IR+2soaG0NsAAAD/AAAA/1RUVfVkZnUTAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACjpPNi0dH7/8HC+P8pI+D/Jh7f/yUe + 3/8lHt//JyDgvTs34gMAAAAAAAAAAAAAAACcnasBk5ScDZaXnwUAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACUe3wElHt85JR7flyYe3+0kHd//JBzf/yQd3/8kHd//JR7f/yYf + 4P83MuL/aWzq/3V57P9WVef/Jh7g/yUm4v8jMuf/J0Tt/yJG7/8jRu//I0fw/yJE6teJkLDiAAAA/wAA + APtzdHiDio2dOQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeXntZkZD + 5f8vK+H/JR7f/yYe4P8mHt//JR7f3CYf3xQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtJ+AIVljnUnJ3 + 669pbur3bnPr/2lx6/9qcer/ZWjp/1hZ6P8wK+H/JR7f/yYe4P8lHuD/Jh7f/yYe3/8lJeL/JDTo/yND + 7f8hRe//jZa1/wAAAP8OGEjXaHWzUZKav2MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAH+B7j/Bwvj/zc/6/ykj4P8mHt//Jh7g6iUe3yYAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAREPkEjUx4mYpI+DBJR7f/CYe3/8lHt//JR7f/yYe3/8lHt//JR7f/yYe + 4P8lHt//JR7f/yYe3/8lH9//Iynk/8vR9P8aHSr/IULe/yRH7vyGmurCaIHrKQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2duwCfHvt1FFO5v8mHt//JR7f6yUe3zIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwm4B8mH992JR7fzSYe + 3/0lHt//JR7f/yUe3/8mHt//JR7f/yUe3/8mHt//JR7f/yQd3//Fxvb/ZGfR/yQ16P8hQ+7/hZz3/1Fv + 8/sqTe+gJUfuHQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFhU5yIyLOGELCXgiS0m + 4CMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAlHt8iJR7fbCUe37smHt/5JR7f/yUe3/8lHt//JR7f/yYe3/8lHt//S0fl/zMu + 4f8kHd//b3Hr/6Cs9f9BX/D/I0bv/yNG7/MjRO5tI0TuAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJR7fEyYe32EmH9+0Lynh9zYv + 4v81LuL/NTDi/0VC5P9oaen/nKLx/6mw8/94e+z/JSDg/yQv5v8jQu3/I0bv/yNG77YjQe0ZAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAFdV5hBydetcaGnqr2Nj6fVbXOj/UVHm/0VD5f8sJuD/JR7f/yUe3/8mHt//JR/g/yQu + 5f8jQu3/I0bv6iNB7VEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABOTOUOPTnjVy0m4KwlHt/zJh7f/yYe + 3/8lHt//Jh7f/yYe3/8lHt//JR/g/yQv5v8jRO7+Iz7sVgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJyHfCyUe31IlHt+lJh7f7iYe3/8mHt//JR7f/yYe3/8mHuD/JSTi5iM76isAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUe3wYlHt85JR7feyUe36wlHt+XJSDgVCUm + 4hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////////////////////////////////////// + /////////////////////////////////////////////////////////////wf////////8A/////// + //gB////////8AH///////8AAf///////AAA///////4AAB///////gAAH///////AAAf//////8AAB/ + B/////4AAP4D/////gAA/AH///8AAAD4AP///gAAAfAAf//+AAADwAAf//CAAAGAAB//4GAAAAAAH//A + fAAAAAAf/8BwAAAAAB//gAAAAAAAP/+AAAAAAAA//4AAAAAAAD//gAAAAAAAP/8AAAAAAAA//gAAAAAA + AD/8AAAAAAAAH/gAAAAAAAAfAAAAAAAAAB8AAAAAAAAAH4AAAAAAAAAH8AAAAAAAAAfgAAAAAAAAD+AA + AAAAAAB/4AAAAAAAAH/gAAAAAAAA/+AAAeAAAAD/4A4/+AAAAP/gH///AAAA/+A////gAAB/4H////wA + AB/w/////4AAB///////8AAD///////+AAH////////AAP////////gA/////////wH///////////// + //////////////////////////////////////////////////////////////////////////////// + //8oAAAAMAAAAGAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI1AAArxwAANvEAABnnAAAAkQAA + AAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABTghHYbwYlzf/5mV + 6f+opt//XFtp/wIBAHoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDgXwfnZuPZ6mo + r+bFyvj/vMj0/5Km9P94kPn/a4b+/ys7fMsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFxbwGIhn8qrKmecMjI + x8XFzOf2o7Lz/2eB9/8zVfL/G0Dv/xY77/8YPe//GT7x/yU/8/8nS+1ZAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeHZwCLm5 + uLa5w+/+jaD6/1Nw9v8mSfD/Fzzu/xk+7/8gRO//I0bv/yNG7/8jSPD/JDvr/yYc3/8kNOrrI0jyIwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAGJ1z5YeRPf/Fjvv/xtA7/8hRfD/I0bv/yNG7/8jR+//I0fv/yNG7/8jSfD/JTLn/yYb + 3v8mI+H/I0bvUgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAB9C7hUiRe/jI0bv/yNH7/8jR+//I0bv/yNG7/8jRu//I0bv/yNG + 7/8jSfD/JSzl/yYa3v8lJ+P7I0nwKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAABcAAAAXAAAAAQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjRu94I0jv/yNI8P8jR+//I0bv/yNH + 7/8jRu//I0bv/yNH7/8jQu7/JiDh/yYj4f8lJuLWJS3lBAAAAAAAAAAAAAAAAAAAAAAAAAAqBQUbtAwL + QfIHByjyAAAAqAAAABoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACJF7wohQ+4KI0fvAQAAAAAjSvEFIz/swCM/ + 7f8jSPL/I0j0/yNH8f8jR/D/I0fw/yNJ8P8kN+n/JiTi/yUs5f8mIuF3AAAAAAAAAAAAAAAAAAAAAAIC + CDQWE4bqJB3f/ycg7P8lINv/FhR2/wUJHcULF08CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkRvUsIzLnsTU75uZARujgJyvkyiQw + 5qokOOp1JTHofiYn3PsZKLv/GDLM/xw95P8gQ+v/Ikbu/yQ87f8mL+r/JiLi/yYg4MgmHN8JAAAAAAAA + AAAAAAAAAAAADhgVhtQnIef/JiDY/yUf1f8mH9v/Jx7i/yUt4f8jSviwIUn/DwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhKtAsKBzsuSoi + 4f4rJOD/Jx7f/yYc3/8lG9//JyDg/m947/50fun/Xmfa/0hU1P8sOsr/HSu8/xgZqf8fHcH/Jh7c2yYm + 5B0AAAAAAAAAACYe4QkmH+ElGhaVlyMexP8lH9b/Jh/g/yYe4f8mHuH/Jh3h/yYk5P8iRfD/I0fzlM/V + 8RPk4t0BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgIISAEB + AqUDAhI+AAAAACce6CklHN+TJh7f7yYe3/8mHt//JR3g/zQw5P9KSej/WVrt/3B28v+AifX/d4Lx/15p + 4/8rLsz/ISvcwyRB7k4jSvEeAAAAACUb25AlG9b9Jx7o/yYd5P8mHeP/Jh/l/yYf4f8mHuD/Jh3f/yYl + 4v8ZPvH/YXz2+UZGRIwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAFBRJLFxWH/BwYqf8GBh/5AAAANQAAAAAAAAAAJiLhDiYf4GgmHd/MKR/e/yki3v8gGN//HRPd/zk2 + 4f99h/L/l6X4/3R88v8pIeP/Hxbf/yYg4P8lI+L6JTTomCM86GUfKb7OHSOy/x4juP8gIbz/IBu9/yUb + 1v8mHN7/Jh/l/x837f9JbP//d3uI/wUEAJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAUGBRspKZHlJB3m/ygf+/8fGr3/AAAAewAAAAAlSvoDI0XvHyRB7UkiOuyQMkXm9ISH + 0P8jHOD/IRfg/39+z/9SUuD/cXjx/3yE8v9qcO7/S0vn/yUb3v8lH+D/JDrq/yNK8v8jSfL+I0ju/yNJ + 8P8fPtP/GSOa/xskrP8dIa//Hia7/xg43P+cq/D/IyEW/3V1dYIAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAABkaPogtKc//GBSS/yQb3v8lG+L/FyeQuCNJ9ZkjR/DUI0fw+CNI + 8P8iR/D/H0bw/5ikz/9GWeL/FB3o/3d30f9YU9b/GQ7f/yAW3v9WVun/YGXs/yMn4/8jQe3/I0nw/yNG + 7/8jR+//JEn1/yA50f8fHrn/Iz3m/yNK8/8jR+z/Hz7X/yM4xf95fZn/AAAA/8DAwHcAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhgYYtUeGLb/Gxah/yUl6P8kPO7/JEn3/yNH + 8P8jRu//JEfv/yNH7/8kR+//K07y/4+e2v9ziNn/IUj0/1px4/94g9X/GCzq/yEy6P8dM+n/Gzjs/yFF + 7/8jSPD/H0Pv/yFF7/8iRvH/IUPh/x4dtf8nG+P/JDjs/yNJ8P8jSPD/JTfu/yYq8v8HBzf/FxcM/+/v + 7UoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFg8NVO4hKMz/JDrr/yNH + 7v8jSfD/I0fv/yNG7/8jR/D/Jkvw/zZb8v88X/L/VXX2/4uc5/90h9f/fJPs/0Ji7v+Hl9T/NFrz/z1h + 8v82WvL/NVfx/zdZ8f8hRu//Q2Py/zFU8f81WvX/HjHI/yQZ0/8mKOX/I0jw/yNJ8P8lMef/Ihjd/ysj + zf8XFlT/KShD3bGs/QkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFSqOQxw0 + uP0jSPH/I0ny/yNH7/8jR+//H0Ty/x1C8/8jRO//Ij3s/ytA6v9oefD/Sl7v/4GW5/9oeNr/laPe/zNU + 8f+DkdP/WXXw/zJV8f9QcfP/dpL2/0Vm8v9HavP/W3r0/1Bx8/9QcvP/GiK//yYa3/8kOur/I0fv/yUr + 5P8kGuL/KSS//8HC0v/g4dn/UVTD/hMR4zQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAkSfhnJUr9+SRJ9/8jR+//I0fv/yNI8P8cQfL/NlTg/0xb0/8oJt7/JR3g/ycf3/9KSeX/RkLl/6eo + 6f9ubNX/bW3Y/01O4P9sa9P/W13d/2Bm7f9weO3/Ojvl/ycq4/9OT+f/OTrl/ygp5P8mLd//IB/B/yce + 4v8iLeb/JCLi/yYc3/8fF9//R0aT/8bHu//Ix8L/0dbn/y1N7nMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAB9F8XUfRfD/IETw/yJG8P8jSPD/I0jx/xs37v9SYM3/ra+3/4yJsf8jHN7/Ixzj/ysl + 4f8+POP/JR7g/yoj3f94d9H/NC/c/3Bu0/9TT9f/foDY/yUc4v9KSOX/Jh7f/yUc3/8hGN7/Ixrf/yUc + 4P8lG9n/IRvA/yQc4v84OeX/NDLj/yQb4P8lHuL/CQk3/wYGAP8xMTD/mpmW/1Vz+aYAAAAAAAAAAAAA + AAAAAAAAIhy+TTQyx6ZhZdu6Q0TRsURX5PE8Vej/OFbt/ylJ7f8jPOz/HiXn/1dVxf+5uL//w8XJ/4SG + yP8gGuD/KB/a/yQd5P8fG+v/Ix3n/yEa4f97etH/KiTf/2lo1P9ZV9X/kZPU/3R17f9QTub/JBzf/yYe + 4P8mHt//Jh7g/yYe4P8lHtv/IBq6/yAX4P9XXuz/TlDp/zc05P8wK+r/AgBA/4GCef//////eHdz/zVP + 068AAAAAAAAAAAAAAAAAAAAAPTrOPrO086PCx/nSRkTj9i8q3v84M93/SUnf/zo53f8fFuH/QDrN/7W2 + vv+8vsv/rq+8/459hf9eMU7/XCU7/1cmT/9II33/NCC0/xwU4/9yctn/SEPb/zYw4P93d9P/fHvQ/ywl + 3/8jG+D/Jh7g/yYe4P8mHuD/Jh7f/yYe4P8mHuH/Hxq5/yQb3f8vNOb/MTfl/0xL5/9PUPD/HRih/9TV + 0f+3tq3/OTpA/zxL3vI/O+duJhvfBAAAAAAAAAAAAAAAAAAAAAAAAAAAIjXonR8f4v8hF9//IBff/yMa + 3/8dFeH/dXS//8LEyP+lprP/b0dG/2EfGf9nJx3/aCge/2gpH/9mKyP/ZSsp/00fWP9zYqb/ZmXL/xEJ + zP9kY8v/kJDJ/zcw1P8gFt3/JR3i/yYe4/8mHuL/Jh/g/yYe4P8nH+P/IRzE/yQc1P8jJ+X/HDHp/0xM + 5/+CivP/SUTs/4OEm/8PEB7/M0Sj/CYt6uY8OOT/LCXhMwAAAAAAAAAAAAAAAAAAAAAaEt4PKCji5jw7 + 5f80L+P/MCvi/y0n4v8jHN//hYa+/7/Cyv+IfYL/YSEc/2UoI/9mKCL/ZSgn/zE5u/9DWej/d37a/3x+ + y/9TYc7/L07d/zBI1P9lc9n/h5bb/0xVwv8oLLH/IiKz/xwawf8dF9D/IBjY/yAX3v8kG+P/JR7b/yIb + xP8nIOb/ID7t/zZV8P9+jPj/hYjl/zEzN/8aOc7/JD300iUe4BEjG98ZJh7fAwAAAAAAAAAAAAAAAAAA + AABMR+YoZWHq/Wpr6/9EReb/R0jm/1ha6v9ka/D/io7E/7i6wv+QipH/ZS0o/2AeGf9hIBr/YiEZ/1Ak + VP86L7n/MDPv/ywu7f8mKuj/ICTm/zE66/9FUe//VGPy/2Z49P9ug/P/bYPt/2R55P9TZ97/QlDU/0dM + zv8uMMn/FxW2/xIOg/8eFq3/Jijp/yFG8f8yWP7/ZW2Q/wAAAv8YMtrzHRrpPwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACytPY1pqf1/11Z6f8cE97/Ihrf/yMb3/8qJeT/dXvd/6+wvP+ztcP/mpOc/5aB + if+ReoD/jXZ8/453dPqOg4LcdHOdqi8qy5ojGuLpJh3f/yQb3/8hGN7/Hxjf/yEb3/8kJOL/KzPn/zdF + 7P9KXvL/V3D1/5+y+v+QpPT/XHDh/zlJv/8WHpL/HBWi/x8f2v9CVO//MTMz/wEBAf9rabtTAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABfYek8hIPv/1pX6P8gGN//Jh7g/yMb3/8yLuKRk5/9Lo2U + yU2NjI+cnJ6nxpaao62JjZN3foKFSnR3eSdsbm0IAAAAAAAAAAAiGuYVJh/gXCYf4LsmH+D3JRzg/yAY + 3v8hGN7/IBbe/x8U3v8bEt7/PEDm/3uH8P9HVu3/SmDz/2yE/P8xVfL/HDnN/xgivdlubc+5ERAH/xUV + FPzGxbtVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACVmPJGlpby/1BN5/8hGd//Jh7g/yUd + 4LwoIN8FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAkHeAiMCvhe09P5s1OTeb8TU3m/0xN5v9XWej/cXPr/0tI5f8iGd//Hxrg/yIl4/8lNun/JEPy/x9J + +/hxg8jxBgQA/ycuS53IyMdcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABXXOgyn6Dz/2xq + 6/8fF9//Jh/g1iUe4BcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKF7gFIROU3LijhijEt4t0xLOL/Jh/f/yEZ3/8mHuD/Jh7f/yYd + 3/8mHN//Jh/g/xsj5/+SmuD/MjhK/xk/5fCGmefBTmvuJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAYmDqrjw34/8jHN/TJh/gIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQd4AokHeBDJR7glyYe + 4OEmH+D+Jh7g/yYf4P8lHuD/JBzf/xkP3f+HhOv/XF7d/xIj6v+Bmfj/RGf09R5D75MjR+8TAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAHRbfCiIb3yomH+APAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACYf4AkmH+A4Jh/gjCYe4NMmHt/9Lifh/zIr4f84M+P/S0fo/4KB7P+hpvP/MkDp/yA/ + 7f8jSfDjI0jwSQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8X3wNgYuk3fYDtg1ZU589dX+j/aWvq/2Fh + 6f8zLeL/Ixnf/yYg4P8lMOb/I0Lt/yNJ8JAjR+8QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABoS + 3gUgGN8zHhXeex4W39AjG9/7Jh/f/yYe4P8mHN//JiDg/yQy5/8jRO6NAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACYf4AMmH+AqJh/geCYe4LgmH+DqJh7f3CYb36IkO+srAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA////////h/////////8AAP///////wAA////////AAD///////8AAP// + /////5yA////////AAD///4H///cRv///Af//wU////wB///AAD//wAD///i///+AAH//y8D//8AAf// + /////wAB8P+6Cf//gAHgf/////iAA8A/bwD/4AADgB/////gAAYAB2QA/4gAAgAP/v//BgAAAA+DAP4E + AAAAD+j//gAAAAAPHAD8AAAAAA////wAAAAADwAA/AAAAAAPm+/4AAAAAA8AAPAAAAAADwAsAAAAAAAP + AAAAAAAAAAMAAOAAAAAAAwAAwAAAAAADAADAAAAAAB8AAMAAAAAAPwADwAAwAAA/AAHAf/4AAD8AAMD/ + /4AAHwAB4f//8AAHAAHj///+AAMAAP/////AAAAI//////gAAAD//////wAAAP///////wAA//////// + AAD///////8AAP///////4gH////////AAD///////8EgP///////wAYKAAAACAAAABAAAAAAQAgAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFQAA + HIUAABOYAAAAPQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAUEUSaVVC1/3V0qf8sLDHPAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAISC + eBWfnZVYqay1tJOf6v97kvn/Y3/8/0Zj5P8bLXlQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiG + eyqqr8W3i5zn8WJ89P84Wfb/HULy/xg+8P8bQvD/IDLr/yc19d0lTP4VAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAV2i2GTFT9egXPvL/GkDv/yBF8P8jR+//I0fw/yNJ8P8lKeT/JiDh/yNI7zUAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAIUXvXyNJ8P8jSPH/I0fw/yNH8P8jSPD/I0Tu/yYj4v8mJeLsJTTtDgAA + AAAAAAAAAAAAEAUEGo0GBR+uAAAAXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACZO/wUkRO03LEbsUSRB7DYjRu8aIjnquxw34P8dQer/IUfx/yNK9P8lOu7/Jibj/yYi + 4Y4AAAAAAAAAAAAAAAwWE4PNJSDf/yYg2v8YFof/DR97XgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAHS+/Fysm8NE1MOP/KCHg/yMf4fM4OOblX2fb/0pY1v8vRNj/HzDF/yAi + x/8mIuC2AAAAACYh4wEkHNIeFxSCnCYg3P8mIN7/Jh/g/ygf6v8bOPn+Wnn/Ue3s6wUAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAABQcGKJoDAQyTFRB9CyQb4U8mHOCzJhvf+Sce4P88OOj/TUvq/3R8 + 8f96he3/NjjW/x8k4L0kPexvJS/mLyMez8klHdb/JB3W/yYe3v8nHeP/Jh/j/yhG+v9baqfuCAcEIwAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWFk+OJR/k/xsUpf8DBxVDAAAAACU67iEgMeqOSlHe+Dkz + 2/8xKNr/aWvg/3mA8/9hZO3/ODDj/yQa3v8kPu3qIkTl5CE+3P8fN8r/HCOs/yAhv/8ZJs7/ZH3p/zo4 + L/eGhocbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQYKIiYjoPodFbT/JSLn/x46ycskSvXWIknw/htD + 8v9MauX/coHZ/y9A5v9patT/GBTh/zxA5/89Sur/IDvs/yFH8P8iR/P/IkTn/yEjx/8jPun/Ikvu/x87 + 4v8xN3//QUAy7P///w8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAQhPHR6o/yM04v8kRvL/I0n2/yJH + 8P8pT/H/Mlfy/1578f+Cldn/WXnw/2yC3f80VvD/K0/w/zBV8f8oTvH/NFjy/zBX8/8gLcr/JSDe/yNH + 8f8kPu7/JSHo/w0KZ/9VU3GnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH0DUBRs1s6UiROb/JEvz/x9G + 8v8fQ+//JT7q/yIw6P9MV+v/a3jv/4SO3f9peOL/YnHd/1Fn6v9WcPP/WW3w/0Zf7/9OZPD/Plfp/yEg + yv8kLun/JDbq/x0V3/9oZMb/6Ond/15k28wTG+YEAAAAAAAAAAAAAAAAAAAAACMz2g0iS/i9IUj6/yNK + 8/8cQfL/OE/f/42Uvf88Ntb/Hxfp/zUy5v82MOP/Z2TY/1ZR1/9lYtX/Y2Db/0pG5/8oIeD/KCDg/yYe + 4f8iG9f/IRvK/y8u5/8tKOP/IBje/yUlSf9mZVv/mqTK/CFL9yIAAAAAAAAAADMuxlpzdN2+Rkjb2j5O + 5v88Uun/ITPp/zg51/+qqcL/vL/G/0k8tP80Ha3/LR/H/xsU5v9VU93/R0Lb/2lo1f95edv/Qj7n/yUe + 4P8lHuD/Jh/h/yUe2f8eF8T/QEHq/0VH6P82M+X/Ly1o/9jXxf9gZ4j/Kj3uRQAAAAAAAAAAa2nhC62x + +DEpMObXIhne/ykg3v8dEt7/iIfF/7i6vf96V1j/aSsf/2goHf9iLTT/Uyhi/2BRrP9FQ9H/VFLM/3Fv + zP8aEdj/Ihrf/yUd4f8mHuH/Jh/h/yMbyP8kJeT/Kjbn/2pu8f9vbd3/Y2Vn/yc2n/s6OOzXKSDgHAAA + AAAAAAAAJx/gDDg45OpCQOb/OTbk/z895P+eocr/nZaZ/14dFv9jHhP/ViU+/zQ/1f9dY+L/TVfW/yg/ + 3f9TY93/eYjd/0xXy/8+Rcv/MDPQ/yYk1v8hGtf/HBXA/yEayP8jPvL/XHf//2Fjj/8LIo3/Hin3dyEX + 3wsmHt8DAAAAAAAAAACKifAliIfw+jYx4/8sJuH/QkHp/4+Tz/6urrb/jHR5/4NgY/9/W1n/eGR/5UpJ + v8AgGuLZJR7i/ycj5P8rK+b/PUPs/0lW7v9NYO7/Y3nu/3mJ5P9BT8P/ICWc/x0cw/80SfD9IiYr/zI2 + fasAAAAAAAAAAAAAAAAAAAAAAAAAAHx+7i58e+7/KyTh/yEZ3/8uKOOQjZfzIoWHlleNkJR8g4eLWnV5 + eydydGsIAAAAACcg3wQlHuFMJR7gqygh4O8uJ+H/MCrh/zEs4v9gZOv/T1jt/0ZV8v87WPL/GDbX+0pT + xNAKCAD4mJWIawAAAAAAAAAAAAAAAAAAAAAAAAAAbHHrKYiH8P8tJuH/Ixzfuigh4QQAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUVDmFGFh6Wc/PeS+REPl+Tk04/8jGt//Ihnf/yMe + 4P8fKO3/Xm7W/yg1ZPNwhNyiW3XoFAAAAAAAAAAAAAAAAAAAAAA6OOMCSkXmlScg4KgkHuAOAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiGt8jIxzgdSYf + 4L8mIOD1Ixvf/xsQ3f9PSOT/TlHp/26B9/85WvLpH0bwbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACIa3xhEQeRlXl3ouUdF5fNoaOv/X1zo/yUh4f8kNOj/I0XvtiNI8CMAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHhbfFyEZ32MgGN+1Jh7g7iYc3/8mIuH/JD3rkgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYf4BQmHuBNJh/gjyYb + 3oMkNOgqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////////////////4f///+ + D///8A///8AH///AB///4AYf/gAMD/4AEAP4AAAD+EAAA/AAAAPwAAAH4AAAA8AAAAMAAAADAAAAAYAA + AAGAAAAPgAgAD4P/AAeH/+AD///8AP///4D////g/////////////////////ygAAAAYAAAAMAAAAAEA + IAAAAAAAYAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkI + KwwFBR5yAwMOWwYGCQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAk5ScHnR0psSFi+f+anbD/iIsYUwAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdHV9Arm9 + 0HWUod3LbYPs/TJT7v4iRu/+I0Pu/iQ048kfONQEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADVU6KkjRu//I0bv/yNG7/8jRu/+Iznq/yUi + 4f4hN9sVAAAAAAAAAAABAQoBAQEGAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACM+6yQjQ+72I0bv/yNG7/8jRu/+JC7m/yUm4tAAAAAAAAAAABAOXlEWEoboDw5Y4goP + PTMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwjsxcrK+HNLSzi5yUk4sU6P+TeP07U/ig/ + 3P4fMc3+JCPc+CUl4TwAAAAAGxeeMSEcxPYlH9n+JR7e/iM35udEYOUqAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAEA9ZYgwLR60XFIsVJSDgbCUf4NkoIeD/Ojfk/2px7v9wevD+Jyja6yUp45UkLOFsISHK8yIe + yf4kHdj/JR7f/ypB6/5KU3u/AAAAAAAAAAAAAAAAAAAAAAAAAAAcHVgrIx+y+SMc2/4VJoxwI0LteyNB + 7bVdbdz8KSzi/2Ni1v9RUuj+U1Xp/yUq5P4jRO7/I0bv/yA31/4eMsb/HzLM/1RlyP4xMTG5AAAAAAAA + AAAAAAAAAAAAAAAAAAATFVl/HyTA/yM77f4jRu//I0fv/zJV8P5kfOb/ZHzl/1903v8wSe3+KEbt/ylM + 7/4uUfD/JUPh/yMg1f4jQ+7/JDLn/xkZiv5GRVmFAAAAAAAAAAAAAAAAAAAAACFC4hoePM/OIkbv/iJG + 7/4uS+T+JDHn/kJL6P5ocej+cXrZ/mNu3P5EV+v+Wmvu/kBS7P47TOv+LDfW/iUp5P4lLOX+MCvD/tHR + 1/5qc9KgAAAAAAAAAABAPskvTFLYUjNQ69slR+3/Iz7r/2Bq0P6YmL3/JR7g/y4p4f4mH9//WFXW/19d + 1f9nZt7+OTXi/yUe3/4lHt//IxzO/zU05P41MuP/GBSK/2tra/5mc7HQAAAAAAAAAAB8e+AzXWDonSwo + 3/40MN/+Qj3T/rCxv/6DZWn+YCk1/lkmSf5EI4f+XVfJ/lFO1v5XVNf+JR7f/iUe3/4lHt/+IxzN/igm + 4f48Quf+SUnT/o2Olv40Pa3zPDzjYAAAAAAAAAAASUjmiUNC5f48OeT/a2zV/6Ccpv5lKCT/ZSgk/z87 + sf5PVeH/MT/e/0JP2/9gbtv+SlTS/z5D1v4uMNf/JCDK/x8auv4oQez/ZXPU/xgoffsmN99EJiPHAwAA + AAAAAAAAi4rwnT864/4lHt//U1PfsqGitr+XkJndjoCGs4l+iIJSTr9ZJh/enCUe3/AlHt/+JSTh/zA2 + 5v5qd/D/W2/u/zdKy/4fKMT3OD147jU2TKEAAAAAAAAAAAAAAAAAAAAAjo7woEA65P4lHt+pNC/QAQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACUf2ApAPeNZSEfltEE/5PpBPuT+JR7f/iUn4/4kNej+RU14/Vlp + sqJjedgFAAAAAAAAAAAAAAAAZWPpNC0n4IIkHdsHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAACch3RUlHt9mJR7ftSUe3/clHd//UE3i/2t47/4vTO7dI0XuSAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAExK + 4RBhYehcUE/msDUw4vUlH+D/JC3l/SM964AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQd2Q0lHt9QJR7feCQm + 4i8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8A////AP// + /wD/4f8A/8H/AP4A/wD/AM8A/wGHAPgBAwDwAAMA4AADAOAAAwDAAAMAAAADAAAAAQCAAAEAgAAHAIfg + AwCP/AEA//+AAP//8AD///8A////AP///wAoAAAAEAAAACAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMi81LDIxVJ4ODgw5AAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACFhok9goy7m2B14O9Laf//Kj7KyB88 + vQkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOFjkVh5G+f8aQfT/HkHw/yUo + 7PcjPOwUBg4uAgMDEUsAAAAVAAAAAAAAAAAAAAAAAAAAAAAAAAATJ44GKTf4jiIt6ZoyQeLoMk7j/yQ3 + 3f8lJueFIRi9BBYQfaAlHNj/FR+30Zmn2RwAAAAAAAAAAAAAAAAHBhwvEQpfvxkbmkAmKu2oNjPg/lpZ + 4/9eX+T/JirjviIy5bMjLNb/IB7Q/zNC5f9UW2mDAAAAAAAAAAAAAAAAExNbniMv6v8cQubiK1P072B4 + 5P9YaOD/O0zq/zJP7/8qUvH/IjTZ/yE87f8lLab/npusZAAAAAAjHMELIjrRQB5D4OouUe7/QlTk/ywx + 8P9gZeb/Z27b/1Ng6P88Rur/Mjjh/ygp3v8hIdv/dXGV/1Zm228AAAAAXl3cWDpB5+woL+j/hofF/4Bg + c/8/GXj/Rje7/11d1P9STtj/HhTd/x4T2f8nI9v/QUTm/21ulP8/SsK6IiXpCWVj6hNJRufxPDnl/5iV + tf57Sjv/Zkts70RFzOI0OuH/Rk3e/zxF3v9IT9//KCzD/zVG1v04QX3jOzfbLycc3wN+g+4jV1Po/ygi + 6JF9g7slenx3NXZ2ZxFDPq0FHxXnPz045Zs9O+XoR0jo/yw05/8sOcD9W2J+twAAAAAAAAAALirhBi0m + 4UcsJuoDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJyDgDSEZ304rI+GfRUDk5lle7f8oP+3cIUbvRgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAERC5QsiGt9IJRrenCUu + 5YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP//AAP//wAD/j/nFvgf5xb4A4AU4AEAAMABAADAAQAAAAEAAAAAAAAAAAAAAAMAAB+A + AAP/8HMf//8AAP//AAQ= + + + \ No newline at end of file diff --git a/AirScout/TERMSANDCONDITIONS b/AirScout/TERMSANDCONDITIONS new file mode 100644 index 0000000..7a60eb9 --- /dev/null +++ b/AirScout/TERMSANDCONDITIONS @@ -0,0 +1,82 @@ ++++ TERMS AND CONDITIONS +++ + +Copyright (c) 2018 by DL2ALF + +AirScout is free for personal ham radio related use. You may free distribute the package without changes. It has been developed using C# and Microsoft Visual Studio Express. It is released under the GNU Public Licence V3, the source code is available on request. It could not have been developed without the great help of Open Source software found on the Internet for almost all presentation and calculation tasks inside the software. You may use it "as it is" without any warranties. For further information see the disclaimer on the "Options/Info" tab. + ++++ PRIVACY POLICY +++ + +Personal data (usually referred to just as "data" below) will only be processed by us to the extent necessary and for the purpose of providing a functional and user-friendly software. + +Per Art. 4 No. 1 of Regulation (EU) 2016/679, i.e. the General Data Protection Regulation (hereinafter referred to as the "GDPR"), "processing" refers to any operation or set of operations such as collection, recording, organization, structuring, storage, adaptation, alteration, retrieval, consultation, use, disclosure by transmission, dissemination, or otherwise making available, alignment, or combination, restriction, erasure, or destruction performed on personal data, whether by automated means or not. + +The following privacy policy is intended to inform you in particular about the type, scope, purpose, duration, and legal basis for the processing of such data either under my own control or in conjunction with others. + +My privacy policy is structured as follows: + +I. Information about me as a controller of your data +II. The rights of users and data subjects +III. Information about the data processing + +I. Information about me as a controller of your data + +The party responsible (the "controller") for AirScout (this "software") for purposes of data protection law is: + +Frank Schmähling +Tabarzer Str. 15 +99867 Gotha +Germany + +Email: dl2alf@darc.de + +II. The rights of users and data subjects + +With regard to the data processing to be described in more detail below, users and data subjects have the right + + to confirmation of whether data concerning them is being processed, information about the data being processed, further information about the nature of the data processing, and copies of the data (cf. also Art. 15 GDPR); + to correct or complete incorrect or incomplete data (cf. also Art. 16 GDPR); + to the immediate deletion of data concerning them (cf. also Art. 17 DSGVO), or, alternatively, if further processing is necessary as stipulated in Art. 17 Para. 3 GDPR, to restrict said processing per Art. 18 GDPR; + to receive copies of the data concerning them and/or provided by them and to have the same transmitted to other providers/controllers (cf. also Art. 20 GDPR); + to file complaints with the supervisory authority if they believe that data concerning them is being processed by the controller in breach of data protection provisions (see also Art. 77 GDPR). + +In addition, the controller is obliged to inform all recipients to whom it discloses data of any such corrections, deletions, or restrictions placed on processing the same per Art. 16, 17 Para. 1, 18 GDPR. However, this obligation does not apply if such notification is impossible or involves a disproportionate effort. Nevertheless, users have a right to information about these recipients. + +Likewise, under Art. 21 GDPR, users and data subjects have the right to object to the controller's future processing of their data pursuant to Art. 6 Para. 1 lit. f) GDPR. In particular, an objection to data processing for the purpose of direct advertising is permissible. + +III. Information about the data processing + +This software is retrieveing user information to maintain a AirScout global station database. Therefore a minimum of personal data will be stored on the AirScout server. Most of them is collected from publically available sources. Please support the community with details about your station and your QTH. + +The following information is collected by this software and is stored per user/station: + + - call sign + - latitude + - longitude + - grid square + - elevation + - timestamp of last change (UTC) + +The following information is collected by this software and is stored per user/station and band: + + - antenna height + - antenna gain + - output power + - timestamp of last change (UTC) + +The data are uploaded either at program startup or on user action. The transfer of the collected data is encrypted via Secure FTP (SFTP) to the AirScout web server. +All information is kept confidental and is used only to maintain the AirScout global station database and for statistical purposes. +In case of any change and after successful review of the data an automatic update is redistributed to all AirScout local databases. +No further user tracking, analysis or advertisement is performed. + +THE USER ACCEPTS THE FACT THAT HIS REQUEST FOR DELETING OF PERSONAL DATA DOES ONLY AFFECT THE AIRSCOUT GLOBAL DATABASE AND THAT HIS PERSONAL DATA MAY STILL EXIST IN ANY LOCAL DATABASE OF ANY OTHER AIRSCOUT USER. + +The basis for this storage is Art. 6 Para. 1 lit. f) GDPR. My legitimate interest lies in the improvement, stability, functionality, and security of this software. + + + ++++ LIABILITY DISCLAIMER +++ + +THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +You must agree to these terms and conditions to run AirScout. +To view complete license information please refer to "Options/Info". \ No newline at end of file diff --git a/AirScout/TrafficDlg.Designer.cs b/AirScout/TrafficDlg.Designer.cs new file mode 100644 index 0000000..a7c0463 --- /dev/null +++ b/AirScout/TrafficDlg.Designer.cs @@ -0,0 +1,199 @@ +namespace AirScout +{ + partial class TrafficDlg + { + /// + /// Erforderliche Designervariable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Verwendete Ressourcen bereinigen. + /// + /// True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Vom Windows Form-Designer generierter Code + + /// + /// Erforderliche Methode für die Designerunterstützung. + /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden. + /// + private void InitializeComponent() + { + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.gm_Options_Traffic = new GMap.NET.WindowsForms.GMapControl(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.btn_Options_Traffic_Show = new System.Windows.Forms.Button(); + this.dtp_Options_Traffic_Stop = new System.Windows.Forms.DateTimePicker(); + this.dtp_Options_Traffic_Start = new System.Windows.Forms.DateTimePicker(); + this.btn_Options_Traffic_Close = new System.Windows.Forms.Button(); + this.ss_Main = new System.Windows.Forms.StatusStrip(); + this.tsl_Main = new System.Windows.Forms.ToolStripStatusLabel(); + this.bw_Calculate = new System.ComponentModel.BackgroundWorker(); + this.groupBox1.SuspendLayout(); + this.groupBox2.SuspendLayout(); + this.ss_Main.SuspendLayout(); + this.SuspendLayout(); + // + // groupBox1 + // + this.groupBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.groupBox1.Controls.Add(this.gm_Options_Traffic); + this.groupBox1.Location = new System.Drawing.Point(12, 12); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(808, 482); + this.groupBox1.TabIndex = 0; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Traftic Map"; + // + // gm_Options_Traffic + // + this.gm_Options_Traffic.Bearing = 0F; + this.gm_Options_Traffic.CanDragMap = true; + this.gm_Options_Traffic.Dock = System.Windows.Forms.DockStyle.Fill; + this.gm_Options_Traffic.EmptyTileColor = System.Drawing.Color.Navy; + this.gm_Options_Traffic.GrayScaleMode = false; + this.gm_Options_Traffic.HelperLineOption = GMap.NET.WindowsForms.HelperLineOptions.DontShow; + this.gm_Options_Traffic.LevelsKeepInMemmory = 5; + this.gm_Options_Traffic.Location = new System.Drawing.Point(3, 16); + this.gm_Options_Traffic.MarkersEnabled = true; + this.gm_Options_Traffic.MaxZoom = 2; + this.gm_Options_Traffic.MinZoom = 2; + this.gm_Options_Traffic.MouseWheelZoomType = GMap.NET.MouseWheelZoomType.MousePositionAndCenter; + this.gm_Options_Traffic.Name = "gm_Options_Traffic"; + this.gm_Options_Traffic.NegativeMode = false; + this.gm_Options_Traffic.PolygonsEnabled = true; + this.gm_Options_Traffic.RetryLoadTile = 0; + this.gm_Options_Traffic.RoutesEnabled = true; + this.gm_Options_Traffic.SelectedAreaFillColor = System.Drawing.Color.FromArgb(((int)(((byte)(33)))), ((int)(((byte)(65)))), ((int)(((byte)(105)))), ((int)(((byte)(225))))); + this.gm_Options_Traffic.ShowTileGridLines = false; + this.gm_Options_Traffic.Size = new System.Drawing.Size(802, 463); + this.gm_Options_Traffic.TabIndex = 0; + this.gm_Options_Traffic.Zoom = 0D; + this.gm_Options_Traffic.OnRouteEnter += new GMap.NET.WindowsForms.RouteEnter(this.gm_Options_Traffic_OnRouteEnter); + // + // groupBox2 + // + this.groupBox2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.groupBox2.Controls.Add(this.btn_Options_Traffic_Show); + this.groupBox2.Controls.Add(this.dtp_Options_Traffic_Stop); + this.groupBox2.Controls.Add(this.dtp_Options_Traffic_Start); + this.groupBox2.Controls.Add(this.btn_Options_Traffic_Close); + this.groupBox2.Location = new System.Drawing.Point(12, 500); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Size = new System.Drawing.Size(808, 55); + this.groupBox2.TabIndex = 1; + this.groupBox2.TabStop = false; + this.groupBox2.Text = "Traffic Data"; + // + // btn_Options_Traffic_Show + // + this.btn_Options_Traffic_Show.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btn_Options_Traffic_Show.Location = new System.Drawing.Point(630, 19); + this.btn_Options_Traffic_Show.Name = "btn_Options_Traffic_Show"; + this.btn_Options_Traffic_Show.Size = new System.Drawing.Size(75, 23); + this.btn_Options_Traffic_Show.TabIndex = 3; + this.btn_Options_Traffic_Show.Text = "Show"; + this.btn_Options_Traffic_Show.UseVisualStyleBackColor = true; + this.btn_Options_Traffic_Show.Click += new System.EventHandler(this.btn_Options_Traffic_Show_Click); + // + // dtp_Options_Traffic_Stop + // + this.dtp_Options_Traffic_Stop.CustomFormat = "yyyy-MM-dd HH:mm"; + this.dtp_Options_Traffic_Stop.Format = System.Windows.Forms.DateTimePickerFormat.Custom; + this.dtp_Options_Traffic_Stop.Location = new System.Drawing.Point(365, 19); + this.dtp_Options_Traffic_Stop.Name = "dtp_Options_Traffic_Stop"; + this.dtp_Options_Traffic_Stop.Size = new System.Drawing.Size(131, 20); + this.dtp_Options_Traffic_Stop.TabIndex = 2; + // + // dtp_Options_Traffic_Start + // + this.dtp_Options_Traffic_Start.CustomFormat = "yyyy-MM-dd HH:mm"; + this.dtp_Options_Traffic_Start.Format = System.Windows.Forms.DateTimePickerFormat.Custom; + this.dtp_Options_Traffic_Start.Location = new System.Drawing.Point(162, 19); + this.dtp_Options_Traffic_Start.Name = "dtp_Options_Traffic_Start"; + this.dtp_Options_Traffic_Start.Size = new System.Drawing.Size(128, 20); + this.dtp_Options_Traffic_Start.TabIndex = 1; + // + // btn_Options_Traffic_Close + // + this.btn_Options_Traffic_Close.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.btn_Options_Traffic_Close.DialogResult = System.Windows.Forms.DialogResult.OK; + this.btn_Options_Traffic_Close.Location = new System.Drawing.Point(711, 19); + this.btn_Options_Traffic_Close.Name = "btn_Options_Traffic_Close"; + this.btn_Options_Traffic_Close.Size = new System.Drawing.Size(75, 23); + this.btn_Options_Traffic_Close.TabIndex = 0; + this.btn_Options_Traffic_Close.Text = "Close"; + this.btn_Options_Traffic_Close.UseVisualStyleBackColor = true; + this.btn_Options_Traffic_Close.Click += new System.EventHandler(this.btn_Options_Traffic_Close_Click); + // + // ss_Main + // + this.ss_Main.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.tsl_Main}); + this.ss_Main.Location = new System.Drawing.Point(0, 547); + this.ss_Main.Name = "ss_Main"; + this.ss_Main.Size = new System.Drawing.Size(832, 22); + this.ss_Main.TabIndex = 2; + this.ss_Main.Text = "statusStrip1"; + // + // tsl_Main + // + this.tsl_Main.Name = "tsl_Main"; + this.tsl_Main.Size = new System.Drawing.Size(170, 17); + this.tsl_Main.Text = "Press Show to start calculation."; + // + // bw_Calculate + // + this.bw_Calculate.WorkerReportsProgress = true; + this.bw_Calculate.WorkerSupportsCancellation = true; + this.bw_Calculate.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bw_Calculate_DoWork); + this.bw_Calculate.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.bw_Calculate_ProgressChanged); + this.bw_Calculate.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.bw_Calculate_RunWorkerCompleted); + // + // TrafficDlg + // + this.AcceptButton = this.btn_Options_Traffic_Close; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(832, 569); + this.Controls.Add(this.ss_Main); + this.Controls.Add(this.groupBox2); + this.Controls.Add(this.groupBox1); + this.Name = "TrafficDlg"; + this.Text = "TrafficDlg"; + this.SizeChanged += new System.EventHandler(this.TrafficDlg_SizeChanged); + this.groupBox1.ResumeLayout(false); + this.groupBox2.ResumeLayout(false); + this.ss_Main.ResumeLayout(false); + this.ss_Main.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.GroupBox groupBox2; + private GMap.NET.WindowsForms.GMapControl gm_Options_Traffic; + private System.Windows.Forms.Button btn_Options_Traffic_Close; + private System.Windows.Forms.DateTimePicker dtp_Options_Traffic_Stop; + private System.Windows.Forms.DateTimePicker dtp_Options_Traffic_Start; + private System.Windows.Forms.Button btn_Options_Traffic_Show; + private System.Windows.Forms.StatusStrip ss_Main; + private System.Windows.Forms.ToolStripStatusLabel tsl_Main; + private System.ComponentModel.BackgroundWorker bw_Calculate; + } +} \ No newline at end of file diff --git a/AirScout/TrafficDlg.cs b/AirScout/TrafficDlg.cs new file mode 100644 index 0000000..0a81969 --- /dev/null +++ b/AirScout/TrafficDlg.cs @@ -0,0 +1,172 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using GMap.NET; +using GMap.NET.MapProviders; +using GMap.NET.WindowsForms; +using GMap.NET.WindowsForms.Markers; +using GMap.NET.WindowsForms.ToolTips; +using System.IO; +using AirScout.Core; +using AirScout.Aircrafts; +using ScoutBase.Core; +using AirScout.AircraftPositions; + +namespace AirScout +{ + public partial class TrafficDlg : Form + { + GMapOverlay Coveragepolygons = new GMapOverlay("Coveragepolygons"); + GMapOverlay routes = new GMapOverlay("routes"); + + public TrafficDlg() + { + InitializeComponent(); + + // set initial settings for Map + gm_Options_Traffic.MapProvider = GMapProviders.Find(Properties.Settings.Default.Map_Provider); + gm_Options_Traffic.IgnoreMarkerOnMouseWheel = true; + gm_Options_Traffic.MinZoom = 0; + gm_Options_Traffic.MaxZoom = 20; + gm_Options_Traffic.Zoom = 6; + gm_Options_Traffic.DragButton = System.Windows.Forms.MouseButtons.Left; + gm_Options_Traffic.CanDragMap = true; + gm_Options_Traffic.ScalePen = new Pen(Color.Black, 3); + gm_Options_Traffic.HelperLinePen = null; + gm_Options_Traffic.SelectionPen = null; + gm_Options_Traffic.MapScaleInfoEnabled = true; + gm_Options_Traffic.Overlays.Add(Coveragepolygons); + gm_Options_Traffic.Overlays.Add(routes); + + // add tile to map polygons + List l = new List(); + l.Add(new PointLatLng(Properties.Settings.Default.MinLat, Properties.Settings.Default.MinLon)); + l.Add(new PointLatLng(Properties.Settings.Default.MinLat, Properties.Settings.Default.MaxLon)); + l.Add(new PointLatLng(Properties.Settings.Default.MaxLat, Properties.Settings.Default.MaxLon)); + l.Add(new PointLatLng(Properties.Settings.Default.MaxLat, Properties.Settings.Default.MinLon)); + GMapPolygon p = new GMapPolygon(l, "Coverage"); + p.Stroke = new Pen(Color.FromArgb(255, Color.Magenta), 3); + p.Fill = new SolidBrush(Color.FromArgb(0, Color.Magenta)); + Coveragepolygons.Polygons.Add(p); + // zoom the map + gm_Options_Traffic.SetZoomToFitRect(RectLatLng.FromLTRB(Properties.Settings.Default.MinLon - 1, Properties.Settings.Default.MaxLat + 1, Properties.Settings.Default.MaxLon + 1, Properties.Settings.Default.MinLat - 1)); + + try + { + dtp_Options_Traffic_Start.Value = AircraftPositionData.Database.AircraftPositionOldestEntry(); + dtp_Options_Traffic_Stop.Value = AircraftPositionData.Database.AircraftPositionYoungestEntry(); + } + catch + { + dtp_Options_Traffic_Start.Value = DateTime.UtcNow; + dtp_Options_Traffic_Stop.Value = DateTime.UtcNow; + } + } + + private void Say(string text) + { + if (tsl_Main.Text != text) + { + tsl_Main.Text = text; + ss_Main.Refresh(); + } + } + + private void TrafficDlg_SizeChanged(object sender, EventArgs e) + { + gm_Options_Traffic.SetZoomToFitRect(RectLatLng.FromLTRB(Properties.Settings.Default.MinLon - 1, Properties.Settings.Default.MaxLat + 1, Properties.Settings.Default.MaxLon + 1, Properties.Settings.Default.MinLat - 1)); + } + + private void btn_Options_Traffic_Show_Click(object sender, EventArgs e) + { + if (!bw_Calculate.IsBusy) + { + // clear routes + routes.Clear(); + // start background worker + bw_Calculate.RunWorkerAsync(); + } + } + + private void gm_Options_Traffic_OnRouteEnter(GMapRoute item) + { + int k = 0; + } + + private void bw_Calculate_DoWork(object sender, DoWorkEventArgs e) + { + // show plane tracks + List allhex = AircraftPositionData.Database.AircraftPositionGetAllHex(dtp_Options_Traffic_Start.Value, dtp_Options_Traffic_Stop.Value); + int i = 1; + foreach (string hex in allhex) + { + bw_Calculate.ReportProgress(0,"Calculating " + hex + " [" + i.ToString() + " of " + allhex.Count().ToString() + "]"); + List allpos = AircraftPositionData.Database.AircraftPositionGetAllByHex(hex, dtp_Options_Traffic_Start.Value, dtp_Options_Traffic_Stop.Value); + int count = 0; + GMapRoute r = new GMapRoute(hex + count.ToString()); + r.Stroke = new Pen(Color.Black, 1); + foreach (AircraftPositionDesignator pos in allpos) + { + PointLatLng po = new PointLatLng(pos.Lat, pos.Lon); + if (r.Points.Count < 1) + { + r.Points.Add(po); + } + else + { + // check the distance between last waypoint for disruption + // start a new route then + double d = LatLon.Distance(r.Points[r.Points.Count - 1].Lat, r.Points[r.Points.Count - 1].Lng, po.Lat, po.Lng); + if (d < 100) + r.Points.Add(po); + else + { + // add old route + bw_Calculate.ReportProgress(1, r); + count++; + // create a new one + r = new GMapRoute(hex + count.ToString()); + r.Stroke = new Pen(Color.Black, 1); + } + } + } + i++; + bw_Calculate.ReportProgress(1, r); + if (bw_Calculate.CancellationPending) + return; + } + } + + private void bw_Calculate_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + if (e.ProgressPercentage == 0) + { + Say((string)(e.UserState)); + } + else if (e.ProgressPercentage == 1) + { + GMapRoute r = (GMapRoute)e.UserState; + routes.Routes.Add(r); + } + } + + private void bw_Calculate_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + + } + + private void btn_Options_Traffic_Close_Click(object sender, EventArgs e) + { + if (bw_Calculate.IsBusy) + { + bw_Calculate.CancelAsync(); + } + this.Close(); + } + } +} diff --git a/AirScout/TrafficDlg.resx b/AirScout/TrafficDlg.resx new file mode 100644 index 0000000..8a6aee7 --- /dev/null +++ b/AirScout/TrafficDlg.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 111, 17 + + \ No newline at end of file diff --git a/AirScout/VersionHistory.txt b/AirScout/VersionHistory.txt new file mode 100644 index 0000000..259c920 --- /dev/null +++ b/AirScout/VersionHistory.txt @@ -0,0 +1,153 @@ +2014-01-04: V1.0.0.0 +==================== + +Initial Version + +2014-01-09: V1.0.0.1 +==================== + +- Bugfix: Activation of AirScout server via "Activate Server" checkbox on "Options/Network" does not work. No server functions available --> fixed (tnx SM7EYW) + + +2015-02-08: V1.1.0.0 +==================== + +- Bugfix: Lat/Lon textboxes at "Options/Planes" do not show the current settings from "Options/General" tab. --> fixed (tnx VK6KDX) +- Bugfix: Program is crashing when pressing the "Show Traffic" button on the "Options/Planes" tab on an empty plane position database + (e.g. no plane positions were loaded neither from Internet nor from a local file) --> fixed (tnx VK6KDX) +- Bugfix: InfoWindow: Elevation is showing wrong values --> fixed +- Feature: Accessiblity enhancement, minimize Zoom, Fliter and Alarm Boxes to save screen space when using larger system fonts. + New splitters between map, info box and elevation details, positions were saved now. + Ensure that the basic window elements are always visible. (tnx IK1ZOF) +- Bugfix: several text box input in the Options window (e.g. Sepctrum Lab Server URL) is not saved correctly after pressing "Apply" or "OK" --> fixed +- Feature: new plane feeds created including random source and new web feeds, file feeds and raw data input from ADS-B receiver (DVB-T stick based with RTL1090) +- Feature: AirScout web server functionalitity +- Feature: News Feed included in separate tab +- Feature: Plane tracking by several ways included (experimental) +- Feature: Import call sign database from previous versions or other sources +- Feature: New (scalable) plane icons according to category (tnx OK1TEH) + + +2015-xx-xx: V1.1.0.1 (not published) +==================================== + +- Bugfix: Donate button issue - Program hangs or shows "Script error" while trying to load the website from the server --> fixed (tnx SM6CEN) +- Bugfix: Antenna tracking does not stop when selected AP disappeared, sending "0.0" values for Az/El instead of stop sending data--> fixed +- Bugfix: Planes with category "S" (Superheavy) were shown as "M" sized icons --> fixed +- Bugfix: GLOBE source unusable outside Europe: changed URL, tiles and data format --> fixed, complete new tile handling and download strategy is implemented (tnx VK2BJP) +- Bugfix: Corrupted lat/lon information in feed, major amount of planes is dropped --> not fixed so far(tnx W3SZ) +- Bugfix: Error in 2-line TLE handling for ISS --> fixed +- Feature: Playing of historical data (Offline mode) when double-clicking on UTC field in "Pause" mode + +2016-12-11: V1.2.0.1 +==================== + +- Feature: SQLite database for callsigns, locations and elevation as basic function in ScoutBase +- Feature: SQLite database for aircrafts, types, registrations, airlines and airports +- Feature: Airports can be shown on map now +- Feature: New web feed for Virtual Radar Server +- Feature: New web feed for www.planefinder.net +- Feature: New Auto JSON web feed, with auto detection of data for work with different feeds and formats not yet known (experimental) +- Feature: Screen update rate can be changed now (Default = 1sec) +- Bugfix: "Play/Pause" îs now the default button again. You can toggle the mode by hitting . +- Feature: Wizard on startup guiding through all basic settings +- Bugfix: complete rework of Options dialog +- Feature: enhanced locator in/output, 6-14 digits available +- Feature: new watchlist, keeps history, can be maintained, calls are shown on the map, clicking on it changes path +- Feature: no admin rights necessary anymore, all files are now stored in local user space +- Feature: window layout, position and size is kept now + +2016-12-20: V1.2.0.2 +==================== + +- Bugfix: FirstRunWizard windows remained on topmost position blocking other dialogs--> fixed (tnx G3XDY) +- Feature: downgrade FirstRunWizard to Microsoft .Net4.0 and get back Winodws XP compatibitliy (tnx OK1TEH) +- Feature: design back button as classic button in FirstRunWizard regardless of Windows Design Rules (tnx DL8AAU) +- Bugfix: Reset settings to factory default does not work properly for plane feeds --> fixed +- Bugfix: use of "Best Case Elevation" even when given location is exact --> fixed (tnx G3XDY) +- Bugfix: "Best Case Elevation" did not work properly when more than one elevation model is activated --> fixed +- Feature: added QRZ lookup for own callsign on FirstRunWizard (tnx DL8AAU) +- Bugfix: proper handling of input (callsign, lat/lon, locator textboxes) +- Bugfix: proper handling of drag marker for own locaton on map (FirstRunWizard) --> fixed +- Bugfix: aircrafts did not show up on first run --> fixed (tnx G3XDY) +- Bugfix: database updater did not load elevation tiles from web --> fixed +- Feature: buttons for default elevation path added at Options/GLOBE, SRTM3, SRTM1 + +2016-12-23: V1.2.0.3 +==================== + +- Feature: new threadsafe LogWriter object created and extensive logging implemented +- Bugfix: download of SRTM elevation tiles does not work on WinXP due to missing TLS1.2 implementation --> Workaround + +2016-12-24: V1.2.0.4 +==================== + +- Bugfix: several issues when entering a completly unknown callsign --> fixed (tnx SM6CEN) +- Bugfix: station detail boxes in FirtsRuznWizard and Options/Stations differ in order --> fixed +- Feature: adjust MapWindow zoom automatically in FirstRunWizard when changing locator +- Bugfix: initial DXCall was set to GB3MHL, this is outdated --> fixed, DXCall now set to GB3MHZ (tnx G3XDY) +- Bugfix: loss of callsign database if closed before DatabaseUpdater was finished > fixed +- Bugfix: LastUpdated timestamps are not handled correctly, preventing user from altering locator once set as new --> fixed + +2017-01-02: V1.2.0.5 +==================== + +- Bugfix: Antenna height was not set to 10m by default in the HorizonCalculationDlg --> fixed (tnx DL8AAU) +- Bugfix: Elevation data and antenna height textboxes were not set in Options/Stations --> fixed +- Bugfix: DXStation details were not filled when enetering Options/Stations --> fixed +- Bugfix: ADSBSharp and RTL1090 feeds: exeptions while receiving misformatted messages were blocking the decoder for 10sec, no position messages could be decoded --> fixed (tnx PE1ITR) +- Bugfix: ADSBSharp and RTL1090 feeds: endless loop when not receiving a start character via TCP(binary mode only) --> fixed with receiving timeout (10sec) +- Feature: Aircraft database is now updated also from received web feeds +- Bugfix: Redraw loop to maximaized window when alarms occur --> fixed (tnx DL8AAU) +- Bugfix: Frequency calcluation wrong in HorizonDlg with special CultureInfo --> fixed (tnx DL8AAU) +- Bugfix: Callsign does not update in DXCall combobox when clicking on a marker from watchlist --> fixed (tnx OZ9GE) +- Bugfix: Changes on database were discarded when done before database background update was finished --> fixed + +2017-xx-xx: V1.2.0.6 (not published) +==================== + +- Bugfix: AirScout crashes when Options/Database/Update local database is set to "Never" --> fixed (tnx DL3IAE) +- Bugfix: AirScout does not set DXCall properly when triggered from wtKST to show path and DXLocator does not match with database --> fixed (tnx DL8AAU) +- Feature: Import old callsign database from previous versions as TXT-file (tnx F1EBK) +- Bugfix: Import of initial database files crashed +- Bugfix: misformatted tracking file in WSJT format (azel.dat) --> fixed (tnx DJ5AR) +- Bugfix: creating an empty database under unknown circumstances which causes AirScout to crash on next startup --> Workaround implemented (check for empty lookup tables before saving to database) + +2017-xx-xx: V1.2.0.7 (not published) +==================== + +- Bugfix: "Jumping planes" due to incorrect timestamps when using VirtualRadarServer --> Workaround implemented (check server time and calculate offset) +- Feature: using GAlt (corrected altitude by air pressure) when using VirtualRadarServer giving more accurate altitude asl +- Feature: History Analysis tab implemented to load an play with historical data +- Bugfix: band specific setting of maximum distance to path not working --> fixed (tnx DL8AAU) +- Feature: show Radio Horizon on map as an additional option to easy locate obstructions on topology + +2018-xx-xx: V1.3.0.3 (not published) +==================== + +- Bugfix: Web downloader now allows redirection (e.g. http:// --> https://) --> fixed (tnx DL8AAU) +- Feature: new database structure (internal) +- Feature: extended database structure --> storing location per user & 6digit locator, storing QRV info per user & 6digit locator & band +- Feature: new elevation tile structure and download procedure +- Feature: latest SQLite library version and "Any CPU" feature, AirScout now running as 32bit or 64bit application on both Windows and Linux +- Feature: Linux/Mono compatibility allows running AirScout native in a Linux/Mono environment (details and limitations see readme_Linux.txt) +- Feature: New charts library for enhanced presentation and Linux/Mono compatibility +- Feature: Enhanced station details dialog with map for easier location change and QRV info per callsign, locator and band +- Feature: new background procedures for station and elevation database updates +- Feature: new background procedure for propagation path pre-calculation +- Feature: extended error logging + +2019-xx-xx: V1.3.0.4 +==================== + +- Feature: new "Multi-Path" feature to monitor more than one path at the same time, enhanced watch list features +- Feature: new common database layout for aircraft database +- Feature: keep aircraft positions in database instead of in memory +- Feature: local obstruction is now kept in database, new dialog window in "Stations" tab to enter obstruction data +- Bugfix: Showing of airports in map could not be switched off --> fixed (tnx DL8AAU) +- Bugfix: Calculation of squint angle was intransparent and probably incorrect, its now defined as the difference between both angles theta (refer to handbook) --> fixed +- Feature: Enhanced spectrum tab with zoomed map for supervising nearest plane in real time, singal strength is kept in database for further analysis +- Feature: Enhanced webserver functionality, deliver path and nearest planes info on HTTP-request via JSON, makes it possible to run one ore more AirScout view clients +- Feature: Enhanced analysis tab with history player and import/export functions +- Feature: [Virtual Radar Server] Extended plausibility check to avoid "jumping planes" due to incorrect position messages (estimate position from older messages and check against latest message) +- Feature: [PlaneFeeds] improved recovery of missing data/empty fields in captured messages diff --git a/AirScout/WCCheck.cs b/AirScout/WCCheck.cs new file mode 100644 index 0000000..bf0c91f --- /dev/null +++ b/AirScout/WCCheck.cs @@ -0,0 +1,499 @@ +using System; +using System.Collections; + +namespace WCCheck +{ + /// + /// + /// + public class WCCheck : Object + { + public static double Radius = 6378.2; + + private static PrefixList Prefixes; + + static WCCheck() + { + Prefixes = new PrefixList(); + } + + public class PrefixEntry : System.Object + { + public string Prefix = ""; + public string Name = ""; + public string Start = ""; + public string Stop = ""; + + public PrefixEntry (string APrefix, string AName, string AStart, string AStop) + { + Prefix = APrefix; + Name = AName; + Start = AStart; + Stop = AStop; + } + + public string PREFIX + { + get + { + return Prefix; + } + } + + public string STOP + { + get + { + return Stop; + } + } + public string START + { + get + { + return Start; + } + } + + } + + public class PrefixList : System.Collections.ArrayList + { + public class LengthComparer : IComparer + { + int IComparer.Compare( Object x, Object y ) + { + if (((PrefixEntry)x).Start.Length < ((PrefixEntry)y).Start.Length) + return 1; + if (((PrefixEntry)x).Start.Length > ((PrefixEntry)y).Start.Length) + return -1; + return ((PrefixEntry)x).Start.CompareTo(((PrefixEntry)y).Start); + } + } + + public PrefixList() + { + this.Add(new PrefixEntry("C3","Andorra","C3","C3")); + this.Add(new PrefixEntry("OE","Austria","OE","OE")); + this.Add(new PrefixEntry("ON","Belgium","ON","OT")); + this.Add(new PrefixEntry("LZ","Bulgaria","LZ","LZ")); + this.Add(new PrefixEntry("EA8","Canary Islands","EA8","EA8")); + this.Add(new PrefixEntry("EA8","Canary Islands","EH8","EH8")); + this.Add(new PrefixEntry("SV9","Crete","SV9","SV9")); + this.Add(new PrefixEntry("5B","Cyprus","5B","5B")); + this.Add(new PrefixEntry("5B","Cyprus","C4","C4")); + this.Add(new PrefixEntry("5B","Cyprus","H2","H2")); + this.Add(new PrefixEntry("5B","Cyprus","P3","P4")); + this.Add(new PrefixEntry("OK","Czechia","OK","OL")); + this.Add(new PrefixEntry("OM","Slovakia","OM","OM")); + this.Add(new PrefixEntry("OZ","Denmark","OU","OZ")); + this.Add(new PrefixEntry("EA","Spain","AM","AO")); + this.Add(new PrefixEntry("EA","Spain","EA","EH")); + this.Add(new PrefixEntry("DL","Germany","DA","DR")); + this.Add(new PrefixEntry("EI","Ireland","EI","EJ")); + this.Add(new PrefixEntry("F","France","F","F")); + this.Add(new PrefixEntry("F","France","TP","TP")); + this.Add(new PrefixEntry("F","France","TM","TM")); + this.Add(new PrefixEntry("G","England","G","G")); + this.Add(new PrefixEntry("G","England","M","M")); + this.Add(new PrefixEntry("G","England","2E","2E")); + this.Add(new PrefixEntry("HA","Hungaria","HA","HA")); + this.Add(new PrefixEntry("HA","Hungaria","HG","HG")); + this.Add(new PrefixEntry("HB9","Switzerland","HB1","HB9")); + this.Add(new PrefixEntry("HB9","Switzerland","HE","HE")); + this.Add(new PrefixEntry("HB0","Liechtenstein","HB0","HB0")); + this.Add(new PrefixEntry("SP","Poland","HF","HF")); + this.Add(new PrefixEntry("SP","Poland","SP","SP")); + this.Add(new PrefixEntry("SP","Poland","SO","SO")); + this.Add(new PrefixEntry("SP","Poland","SQ","SQ")); + this.Add(new PrefixEntry("SP","Poland","SN","SN")); + this.Add(new PrefixEntry("SV","Greece","J4","J4")); + this.Add(new PrefixEntry("SV","Greece","SV","SV")); + this.Add(new PrefixEntry("LA","Norway","LA","LN")); + this.Add(new PrefixEntry("LX","Luxembuorg","LX","LX")); + this.Add(new PrefixEntry("PA","Netherlands","PA","PI")); + this.Add(new PrefixEntry("I","Italy","I0","I9")); + this.Add(new PrefixEntry("I","Italy","IA","IZ")); + this.Add(new PrefixEntry("YU","Yugoslavia","YU1","YU1")); + this.Add(new PrefixEntry("YU","Yugoslavia","YU6","YU9")); + this.Add(new PrefixEntry("YU","Yugoslavia","YT","YT")); + this.Add(new PrefixEntry("YU","Yugoslavia","YZ","YZ")); + this.Add(new PrefixEntry("YU","Yugoslavia","4N","4N")); + this.Add(new PrefixEntry("9A","Croatia","9A","9A")); + this.Add(new PrefixEntry("9A","Croatia","YU2","YU2")); + this.Add(new PrefixEntry("S5","Slovenia","S5","S5")); + this.Add(new PrefixEntry("S5","Slovenia","YU3","YU3")); + this.Add(new PrefixEntry("T9","Bosnia-Herzegovina","T9","T9")); + this.Add(new PrefixEntry("T9","Bosnia-Herzegovina","YU4","YU4")); + this.Add(new PrefixEntry("Z3","Mazedonia","Z3","Z3")); + this.Add(new PrefixEntry("Z3","Mazedonia","YU5","YU5")); + this.Add(new PrefixEntry("SM","Sweden","SA","SM")); + this.Add(new PrefixEntry("HV","Vatikan","HV","HV")); + this.Add(new PrefixEntry("EA6","Balearic Islands","EA6","EA6")); + this.Add(new PrefixEntry("EA6","Balearic Islands","EH6","EH6")); + this.Add(new PrefixEntry("GD","Isle Of Man","GD","GD")); + this.Add(new PrefixEntry("GI","Northern Ireland","GI","GI")); + this.Add(new PrefixEntry("GJ","Jersey","GJ","GJ")); + this.Add(new PrefixEntry("GM","Scotland","GM","GM")); + this.Add(new PrefixEntry("GW","Wales","GW","GW")); + this.Add(new PrefixEntry("IS","Sardinia","IS","IS")); + this.Add(new PrefixEntry("TK","Corsica","TK","TK")); + this.Add(new PrefixEntry("LY","Lithuania","LY","LY")); + this.Add(new PrefixEntry("ER","Moldavia","ER","ER")); + this.Add(new PrefixEntry("ES","Estonia","ES","ES")); + this.Add(new PrefixEntry("EV","Byelorussia","EV","EV")); + this.Add(new PrefixEntry("EV","Byelorussia","EW","EW")); + this.Add(new PrefixEntry("UA", "European Russia", "UA", "UA")); + this.Add(new PrefixEntry("UA", "European Russia", "UA", "RA")); + this.Add(new PrefixEntry("UA2", "Kaliningrad", "UA2", "UA2")); + this.Add(new PrefixEntry("UA2", "Kaliningrad", "RA2", "RA2")); + this.Add(new PrefixEntry("UA2", "Kaliningrad", "RN2", "RN2")); + this.Add(new PrefixEntry("UA2", "Kaliningrad", "RK2", "RK2")); + this.Add(new PrefixEntry("YO", "Romania", "YO", "YR")); + this.Add(new PrefixEntry("ZA","Albania","ZA","ZA")); + this.Add(new PrefixEntry("ZB4","Gibraltar","ZB4","ZB4")); + this.Add(new PrefixEntry("9H","Malta","9H","9H")); + this.Add(new PrefixEntry("TA","Turkey","TA","TA")); + this.Add(new PrefixEntry("T7","San Marino","T7","T7")); + // nach Lnge sortieren, lngste Eintrge zuerst + this.Sort(new LengthComparer()); + } + } + + // eigentliche Check - Funtionen + + public static double Lat ( string S ) + { + double StepB1 = 10; + double StepB2 = StepB1/10; + double StepB3 = StepB2/24; + double StartB1 = -90; + double StartB2 = 0; + double StartB3 = StepB3/2; + try + { + S = S.ToUpper(); + if ((S[1] < 'A') || (S[1] > 'Z') || + (S[3] < '0') || (S[3] > '9') || + (S[5] < 'A') || (S[5] > 'X')) + { + return -360; + } + return StartB1+StepB1*(System.Convert.ToInt16(S[1])-0x41)+ + StartB2+StepB2*(System.Convert.ToInt16(S[3])-0x30)+ + StartB3+StepB3*(System.Convert.ToInt16(S[5])-0x41); + } + catch + { + // Fehler bei der Breitenberechnung + return -360; + } + } + + public static double Lon ( string S ) + { + try + { + double StepL1 = 20; + double StepL2 = StepL1/10; + double StepL3 = StepL2/24; + double StartL1 = -180; + double StartL2 = 0; + double StartL3 = StepL3/2; + S = S.ToUpper(); + if ((S[0] < 'A') || (S[0] > 'Z') || + (S[2] < '0') || (S[2] > '9') || + (S[4] < 'A') || (S[4] > 'X')) + { + return -360; + } + return StartL1+StepL1*(System.Convert.ToInt16(S[0])-0x41)+ + StartL2+StepL2*(System.Convert.ToInt16(S[2])-0x30)+ + StartL3+StepL3*(System.Convert.ToInt16(S[4])-0x41); + } + catch + { + // Fehler bei der Lngenberechnung + return -360; + } + } + + public static string Loc ( double L,double B ) + { + try + { + double StepB1 = 10; + double StepB2 = StepB1/10; + double StepB3 = StepB2/24; + double StartB1 = -90; + double StartB2 = 0; + double StartB3 = StepB3/2; + double StepL1 = 20; + double StepL2 = StepL1/10; + double StepL3 = StepL2/24; + double StartL1 = -180; + double StartL2 = 0; + double StartL3 = StepL3/2; + int i0,i1,i2,i3,i4,i5; + char S0,S1,S2,S3,S4,S5; + i0 = System.Convert.ToInt32(Math.Floor((L - StartL1)/StepL1)); + S0 = System.Convert.ToChar(i0+0x41); + L = L - i0 * StepL1 - StartL1; + i2 = System.Convert.ToInt32(Math.Floor((L - StartL2)/StepL2)); + S2 = System.Convert.ToChar(i2+0x30); + L = L - i2 * StepL2 - StartL2; + i4 = System.Convert.ToInt32((L - StartL3)/StepL3); + S4 = System.Convert.ToChar(i4+0x41); + i1 = System.Convert.ToInt32(Math.Floor((B - StartB1)/StepB1)); + S1 = System.Convert.ToChar(i1+0x41); + B = B - i1 * StepB1 - StartB1; + i3 = System.Convert.ToInt32(Math.Floor((B - StartB2)/StepB2)); + S3 = System.Convert.ToChar(i3+0x30); + B = B - i3 * StepB2 - StartB2; + i5 = System.Convert.ToInt32((B - StartB3)/StepB3); + S5 = System.Convert.ToChar(i5+0x41); + string S = System.String.Concat(S0,S1,S2,S3,S4,S5); + return S; + } + catch + { + // Fehler bei der Locatorberechnung + return ""; + } + } + + public static int QRB ( string MyLoc, string Loc ) + { + return QRB(Lat(MyLoc), Lon(MyLoc), Lat(Loc), Lon(Loc)); + } + + public static int QRB (double MyLat, double MyLon, double Lat, double Lon) + { + try + { + // double F = 2*Math.PI*Radius/360; + // acording to IARU Reg1 contest rules + double F = 111.2; + double E; + if ((MyLon<-180) || (MyLon>180) || + (MyLat<-90) || (MyLat>90) || + (Lon<-180) || (Lon>180) || + (Lat<-90) || (Lat>90)) + return -1; + E = Math.Sin(MyLat/180*Math.PI)*Math.Sin(Lat/180*Math.PI)+ + Math.Cos(MyLat/180*Math.PI)*Math.Cos(Lat/180*Math.PI)*Math.Cos((MyLon-Lon)/180*Math.PI); + if (E == 0) return 0; + E = Math.Sqrt(1-E*E)/E; + E = Math.Atan(E)/Math.PI*180*F+0.5; + return System.Convert.ToInt32(Math.Round(E,0)); + } + catch + { + // Fehler bei der Entfernungsberechnung + return -1; + } + } + + public static double QTF(string MyLoc, string Loc) + { + return QTF(Lat(MyLoc), Lon(MyLoc), Lat(Loc), Lon(Loc)); + } + + public static double QTF (double MyLat, double MyLon, double Lat, double Lon) + { + try + { + double F = 2*Math.PI*Radius/360; + double E; + double CE,DL; + if ((MyLon<-180) || (MyLon>180) || + (MyLat<-90) || (MyLat>90) || + (Lon<-180) || (Lon>180) || + (Lat<-90) || (Lat>90)) + return -1; + DL = Lon - MyLon; + CE = (Math.Tan(Lat/180*Math.PI) * Math.Cos(MyLat/180*Math.PI) - Math.Sin(MyLat/180*Math.PI)) * Math.Cos(DL/180*Math.PI); + if (CE == 0) + { + if (DL < 0) return 270; + if (DL > 0) return 90; + return 0; + } + E = Math.Atan(Math.Sin(DL/180*Math.PI)/CE)/Math.PI*180; + if (CE < 0) + E += 180; + if ((CE > 0) && (E < 0)) + E = E + 360; + return E; + } + catch + { + // Fehler bei der Winkelberechnung + return -1; + } + } + + public static string Cut ( string S ) + { + try + { + S.Trim().ToUpper(); + if (S.IndexOf('/')>= 0) + { + // hinteren Teil abschneiden + if(S.IndexOf('/') >= S.Length-4) + S = S.Remove(S.IndexOf('/'),S.Length-S.IndexOf('/')); + // evtl noch vorderen Teil abschneiden + if (S.IndexOf('/') >= 0) + S = S.Remove(0,S.IndexOf('/')+1); + // nochmals hinteren Teil abschneiden + if(S.IndexOf('/') >= S.Length-4) + S = S.Remove(S.IndexOf('/'),S.Length-S.IndexOf('/')); + } + return S; + } + catch + { + // Fehler beim Abschneiden des Rufzeichens + return ""; + } + } + + public static string Prefix (string S) + { + S = WCCheck.Cut(S).ToUpper(); + string Start = ""; + string Stop = ""; + string Prefix = "???"; + for (int i = 0; i < Prefixes.Count; i++) + { + try + { + Start = ((PrefixEntry)Prefixes[i]).Start; + Stop = ((PrefixEntry)Prefixes[i]).Stop; + Prefix = ((PrefixEntry)Prefixes[i]).Prefix; + // Call zurechtstutzen auf Lnge des Prefixes + S = S.Substring(0, Start.Length); + if ((S.CompareTo(Start) >= 0) && (S.CompareTo(Stop) <= 0)) + return Prefix; + } + catch + { + // Fehler beim Suchen des Prefixes, i.allg. ist das Call zu kurz + // --> nichts weiter unternehmen + } + + } + return "???"; + } + + public static int IsCall (string S) + { + // bewertet bergabestring als Call + // Rckgabewerte : -1 - auf keinen Fall + // 0 - vielleicht + // +1 - wahrscheinlich + try + { + S = S.Trim(); + S = S.ToUpper(); + // auf unerlaubte Zeichen testen + for (int j = 0;j < S.Length;j++) + { + if (((S[j] <'A') || (S[j] > 'Z')) && + ((S[j] < '0') || (S[j] > '9')) && + ((S[j] != '/'))) + { + return -1; + } + } + // Rufzeichen von Zustzen befreien + S = Cut(S); + // Rufzeichen zu kurz + if (S.Length < 3) + return -1; + // eigentlicher Test + // erstes Zeichen Zahl + if (Char.IsNumber(S,0)) + { + // zweites Zeichen muss Buchstabe sein + // drittes Zeichen muss Zahl sein + // Beispiel : 9A5O + if (Char.IsLetter(S,1) && Char.IsNumber(S,2)) + return 1; + return -1; + } + else + { + // erstes Zeichen Buchstabe + // zweites Zeichen Buchstabe + if (Char.IsLetter(S,1)) + { + // drittes Zeichen muss Zahl sein + // Beispiel DL0GTH + if (Char.IsNumber(S,2)) + return 1; + return -1; + } + else + { + // zweites Zeichen Zahl + // drittes Zeichen Buchstabe + // Beispiel G7RAU + if (Char.IsLetter(S,2)) + return 1; + // drittes Zeichen Zahl + // Beispiel T91CO + if (Char.IsLetter(S,3)) + return 1; + return -1; + } + } + } + catch + { + return -1; + } + } + + public static int IsLoc (string S) + { + // bewertet bergabestring als Locator + // Rckgabewerte : -1 - auf keinen Fall + // 0 - vielleicht + // +1 - wahrscheinlich + S = S.Trim(); + S = S.ToUpper(); + if (S.Length == 6) + { + if ((S[0] >= 'A') && (S[0] <= 'X') && + (S[1] >= 'A') && (S[1] <= 'X') && + (S[2] >= '0') && (S[2] <= '9') && + (S[3] >= '0') && (S[3] <= '9') && + (S[4] >= 'A') && (S[4] <= 'X') && + (S[5] >= 'A') && (S[5] <= 'X')) + { + return 1; + } + } + return -1; + } + + public static bool IsNumeric (string S) + { + // bewertet bergabestring als Ganzzahl + int i = 0; + while ((i< S.Length) && (S[i] >= '0') && (S[i] <= '9')) + i+=1; + return (i >= S.Length); + } + } + + public class WCUtils : Object + { + public static void Debug ( string S, int priority ) + { + System.Console.WriteLine(System.DateTime.UtcNow+"("+priority+") - "+S); + } + } +} diff --git a/AirScout/Wait.cmd b/AirScout/Wait.cmd new file mode 100644 index 0000000..fc11601 --- /dev/null +++ b/AirScout/Wait.cmd @@ -0,0 +1 @@ +TIMEOUT 5 || exit /b 0 \ No newline at end of file diff --git a/AirScout/Watchlist.cs b/AirScout/Watchlist.cs new file mode 100644 index 0000000..5787b7d --- /dev/null +++ b/AirScout/Watchlist.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace AirScout +{ + /// + /// Holds the watchlist item + /// + public class WatchlistItem + { + public string Call { get; set; } + public string Loc { get; set; } + public bool Checked { get; set; } + public bool Selected { get; set; } + public bool OutOfRange { get; set; } + public bool Remove { get; set; } + + public WatchlistItem() + { + Call = ""; + Loc = ""; + Checked = false; + Selected = false; + OutOfRange = false; + Remove = false; + } + + public WatchlistItem(string call, string loc, bool oor) : this(call, loc, oor, false, false) { } + public WatchlistItem(string call, string loc, bool check, bool oor, bool selected ) + { + Call = call; + Loc = loc; + Checked = check; + Selected = selected; + OutOfRange = oor; + Remove = false; + } + + } + + /// + /// Holds the watchlist, e.g. a list of watchlist items + /// + public class Watchlist : List + { + public Watchlist() + { + + } + + public int IndexOf(string call) + { + // TODO: optimize!!! + for (int i = 0; i < this.Count; i++) + { + if (this[i].Call == call) + return i; + } + return -1; + } + + public int IndexOf(string call, string loc) + { + // TODO: optimize!!! + for (int i = 0; i < this.Count; i++) + { + if ((this[i].Call == call) && (this[i].Loc == loc)) + return i; + } + return -1; + } + + } +} diff --git a/AirScout/WatchlistDlg.Designer.cs b/AirScout/WatchlistDlg.Designer.cs new file mode 100644 index 0000000..cb31cb8 --- /dev/null +++ b/AirScout/WatchlistDlg.Designer.cs @@ -0,0 +1,212 @@ +namespace AirScout +{ + partial class WatchlistDlg + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.lv_Watchlist_Callsigns = new System.Windows.Forms.ListView(); + this.ch_Watchlist_Callsgings_Call = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.lv_Watchlist_Selected = new System.Windows.Forms.ListView(); + this.ch_Watchlist_Selected_Calls = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); + this.ss_Watchlist_Main = new System.Windows.Forms.StatusStrip(); + this.tsl_Watchlist_Main = new System.Windows.Forms.ToolStripStatusLabel(); + this.btn_Watchlist_Add = new System.Windows.Forms.Button(); + this.btn_Watchlist_Remove = new System.Windows.Forms.Button(); + this.btn_Watchlist_RemoveAll = new System.Windows.Forms.Button(); + this.tt_Watchlist_Main = new System.Windows.Forms.ToolTip(this.components); + this.btn_Watchlist_Cancel = new System.Windows.Forms.Button(); + this.btn_Watchlist_OK = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.ss_Watchlist_Main.SuspendLayout(); + this.SuspendLayout(); + // + // lv_Watchlist_Callsigns + // + this.lv_Watchlist_Callsigns.AutoArrange = false; + this.lv_Watchlist_Callsigns.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.ch_Watchlist_Callsgings_Call}); + this.lv_Watchlist_Callsigns.FullRowSelect = true; + this.lv_Watchlist_Callsigns.Location = new System.Drawing.Point(12, 50); + this.lv_Watchlist_Callsigns.Name = "lv_Watchlist_Callsigns"; + this.lv_Watchlist_Callsigns.ShowGroups = false; + this.lv_Watchlist_Callsigns.Size = new System.Drawing.Size(140, 300); + this.lv_Watchlist_Callsigns.TabIndex = 2; + this.lv_Watchlist_Callsigns.UseCompatibleStateImageBehavior = false; + this.lv_Watchlist_Callsigns.View = System.Windows.Forms.View.Details; + this.lv_Watchlist_Callsigns.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.lv_Watchlist_Callsigns_MouseDoubleClick); + // + // ch_Watchlist_Callsgings_Call + // + this.ch_Watchlist_Callsgings_Call.Text = "Available Callsigns"; + this.ch_Watchlist_Callsgings_Call.Width = 118; + // + // lv_Watchlist_Selected + // + this.lv_Watchlist_Selected.AutoArrange = false; + this.lv_Watchlist_Selected.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { + this.ch_Watchlist_Selected_Calls}); + this.lv_Watchlist_Selected.FullRowSelect = true; + this.lv_Watchlist_Selected.Location = new System.Drawing.Point(318, 50); + this.lv_Watchlist_Selected.Name = "lv_Watchlist_Selected"; + this.lv_Watchlist_Selected.ShowGroups = false; + this.lv_Watchlist_Selected.Size = new System.Drawing.Size(140, 300); + this.lv_Watchlist_Selected.Sorting = System.Windows.Forms.SortOrder.Ascending; + this.lv_Watchlist_Selected.TabIndex = 3; + this.lv_Watchlist_Selected.UseCompatibleStateImageBehavior = false; + this.lv_Watchlist_Selected.View = System.Windows.Forms.View.Details; + this.lv_Watchlist_Selected.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.lv_Watchlist_Selected_MouseDoubleClick); + // + // ch_Watchlist_Selected_Calls + // + this.ch_Watchlist_Selected_Calls.Text = "Selected Callsigns"; + this.ch_Watchlist_Selected_Calls.Width = 118; + // + // ss_Watchlist_Main + // + this.ss_Watchlist_Main.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.tsl_Watchlist_Main}); + this.ss_Watchlist_Main.Location = new System.Drawing.Point(0, 400); + this.ss_Watchlist_Main.Name = "ss_Watchlist_Main"; + this.ss_Watchlist_Main.Size = new System.Drawing.Size(487, 22); + this.ss_Watchlist_Main.TabIndex = 4; + // + // tsl_Watchlist_Main + // + this.tsl_Watchlist_Main.Name = "tsl_Watchlist_Main"; + this.tsl_Watchlist_Main.Size = new System.Drawing.Size(51, 17); + this.tsl_Watchlist_Main.Text = "tsl_Main"; + // + // btn_Watchlist_Add + // + this.btn_Watchlist_Add.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Watchlist_Add.Location = new System.Drawing.Point(201, 131); + this.btn_Watchlist_Add.Name = "btn_Watchlist_Add"; + this.btn_Watchlist_Add.Size = new System.Drawing.Size(69, 29); + this.btn_Watchlist_Add.TabIndex = 5; + this.btn_Watchlist_Add.Text = ">"; + this.tt_Watchlist_Main.SetToolTip(this.btn_Watchlist_Add, "Add selected call to watchlist."); + this.btn_Watchlist_Add.UseVisualStyleBackColor = true; + this.btn_Watchlist_Add.Click += new System.EventHandler(this.btn_Watchlist_Add_Click); + // + // btn_Watchlist_Remove + // + this.btn_Watchlist_Remove.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Watchlist_Remove.Location = new System.Drawing.Point(201, 181); + this.btn_Watchlist_Remove.Name = "btn_Watchlist_Remove"; + this.btn_Watchlist_Remove.Size = new System.Drawing.Size(69, 29); + this.btn_Watchlist_Remove.TabIndex = 6; + this.btn_Watchlist_Remove.Text = "<"; + this.tt_Watchlist_Main.SetToolTip(this.btn_Watchlist_Remove, "Remove selected call from watchlist."); + this.btn_Watchlist_Remove.UseVisualStyleBackColor = true; + this.btn_Watchlist_Remove.Click += new System.EventHandler(this.btn_Watchlist_Remove_Click); + // + // btn_Watchlist_RemoveAll + // + this.btn_Watchlist_RemoveAll.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Watchlist_RemoveAll.Location = new System.Drawing.Point(201, 229); + this.btn_Watchlist_RemoveAll.Name = "btn_Watchlist_RemoveAll"; + this.btn_Watchlist_RemoveAll.Size = new System.Drawing.Size(69, 29); + this.btn_Watchlist_RemoveAll.TabIndex = 7; + this.btn_Watchlist_RemoveAll.Text = "<<"; + this.tt_Watchlist_Main.SetToolTip(this.btn_Watchlist_RemoveAll, "Remove all calls from watchlist."); + this.btn_Watchlist_RemoveAll.UseVisualStyleBackColor = true; + this.btn_Watchlist_RemoveAll.Click += new System.EventHandler(this.btn_Watchlist_RemoveAll_Click); + // + // btn_Watchlist_Cancel + // + this.btn_Watchlist_Cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btn_Watchlist_Cancel.Location = new System.Drawing.Point(161, 365); + this.btn_Watchlist_Cancel.Name = "btn_Watchlist_Cancel"; + this.btn_Watchlist_Cancel.Size = new System.Drawing.Size(75, 23); + this.btn_Watchlist_Cancel.TabIndex = 8; + this.btn_Watchlist_Cancel.Text = "Cancel"; + this.btn_Watchlist_Cancel.UseVisualStyleBackColor = true; + // + // btn_Watchlist_OK + // + this.btn_Watchlist_OK.DialogResult = System.Windows.Forms.DialogResult.OK; + this.btn_Watchlist_OK.Location = new System.Drawing.Point(242, 365); + this.btn_Watchlist_OK.Name = "btn_Watchlist_OK"; + this.btn_Watchlist_OK.Size = new System.Drawing.Size(75, 23); + this.btn_Watchlist_OK.TabIndex = 9; + this.btn_Watchlist_OK.Text = "OK"; + this.btn_Watchlist_OK.UseVisualStyleBackColor = true; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(43, 20); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(406, 16); + this.label1.TabIndex = 10; + this.label1.Text = "You can select callsigns from database and set them on a watchlist."; + // + // WatchlistDlg + // + this.AcceptButton = this.btn_Watchlist_OK; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btn_Watchlist_Cancel; + this.ClientSize = new System.Drawing.Size(487, 422); + this.Controls.Add(this.label1); + this.Controls.Add(this.btn_Watchlist_OK); + this.Controls.Add(this.btn_Watchlist_Cancel); + this.Controls.Add(this.btn_Watchlist_RemoveAll); + this.Controls.Add(this.btn_Watchlist_Remove); + this.Controls.Add(this.btn_Watchlist_Add); + this.Controls.Add(this.ss_Watchlist_Main); + this.Controls.Add(this.lv_Watchlist_Selected); + this.Controls.Add(this.lv_Watchlist_Callsigns); + this.Name = "WatchlistDlg"; + this.Text = "Manage Watchlist"; + this.Load += new System.EventHandler(this.WatchlistDlg_Load); + this.Shown += new System.EventHandler(this.WatchlistDlg_Shown); + this.ss_Watchlist_Main.ResumeLayout(false); + this.ss_Watchlist_Main.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + private System.Windows.Forms.ColumnHeader ch_Watchlist_Callsgings_Call; + private System.Windows.Forms.ColumnHeader ch_Watchlist_Selected_Calls; + public System.Windows.Forms.ListView lv_Watchlist_Callsigns; + public System.Windows.Forms.ListView lv_Watchlist_Selected; + private System.Windows.Forms.StatusStrip ss_Watchlist_Main; + private System.Windows.Forms.ToolStripStatusLabel tsl_Watchlist_Main; + private System.Windows.Forms.Button btn_Watchlist_Add; + private System.Windows.Forms.Button btn_Watchlist_Remove; + private System.Windows.Forms.Button btn_Watchlist_RemoveAll; + private System.Windows.Forms.ToolTip tt_Watchlist_Main; + private System.Windows.Forms.Button btn_Watchlist_Cancel; + private System.Windows.Forms.Button btn_Watchlist_OK; + private System.Windows.Forms.Label label1; + } +} \ No newline at end of file diff --git a/AirScout/WatchlistDlg.cs b/AirScout/WatchlistDlg.cs new file mode 100644 index 0000000..e341af0 --- /dev/null +++ b/AirScout/WatchlistDlg.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using ScoutBase.Stations; + +namespace AirScout +{ + public partial class WatchlistDlg : Form + { + public WatchlistDlg() + { + InitializeComponent(); + } + + private void WatchlistDlg_Load(object sender, EventArgs e) + { + + } + + private void WatchlistDlg_Shown(object sender, EventArgs e) + { + tsl_Watchlist_Main.Text = "Please wait while lists are being populated..."; + ss_Watchlist_Main.Refresh(); + List l = StationData.Database.LocationGetAll(); + lv_Watchlist_Callsigns.BeginUpdate(); + foreach (LocationDesignator ld in l) + { + ListViewItem item = new ListViewItem(ld.Call); + item.Name = ld.Call; + lv_Watchlist_Callsigns.Items.Add(item); + } + lv_Watchlist_Callsigns.EndUpdate(); + lv_Watchlist_Selected.BeginUpdate(); + lv_Watchlist_Selected.Items.Clear(); + foreach (WatchlistItem item in Properties.Settings.Default.Watchlist) + { + ListViewItem lvi = new ListViewItem(item.Call); + lvi.SubItems.Add(new ListViewItem.ListViewSubItem(lvi, item.Loc)); + if (item.Checked) + lvi.Checked = true; + if (item.Selected) + lvi.Selected = true; + lv_Watchlist_Selected.Items.Add(lvi); + lv_Watchlist_Selected.Sort(); + } + lv_Watchlist_Selected.EndUpdate(); + tsl_Watchlist_Main.Text = ""; + } + + private void btn_Watchlist_Add_Click(object sender, EventArgs e) + { + if(lv_Watchlist_Callsigns.SelectedItems.Count > 0) + { + foreach (ListViewItem item in lv_Watchlist_Callsigns.SelectedItems) + { + ListViewItem newitem = new ListViewItem(item.Name); + newitem.Name = item.Name; + lv_Watchlist_Selected.Items.Add(newitem); + } + } + } + + private void btn_Watchlist_Remove_Click(object sender, EventArgs e) + { + if (lv_Watchlist_Selected.SelectedItems.Count > 0) + { + foreach (ListViewItem item in lv_Watchlist_Selected.SelectedItems) + { + try + { + lv_Watchlist_Selected.Items.RemoveByKey(item.Name); + } + catch + { + + } + } + } + } + + private void btn_Watchlist_RemoveAll_Click(object sender, EventArgs e) + { + lv_Watchlist_Selected.Items.Clear(); + } + + private void lv_Watchlist_Callsigns_MouseDoubleClick(object sender, MouseEventArgs e) + { + btn_Watchlist_Add_Click(this, null); + } + + private void lv_Watchlist_Selected_MouseDoubleClick(object sender, MouseEventArgs e) + { + btn_Watchlist_Remove_Click(this, null); + } + } +} diff --git a/AirScout/WatchlistDlg.resx b/AirScout/WatchlistDlg.resx new file mode 100644 index 0000000..9f817c5 --- /dev/null +++ b/AirScout/WatchlistDlg.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 167, 17 + + \ No newline at end of file diff --git a/AirScout/Webserver.cs b/AirScout/Webserver.cs new file mode 100644 index 0000000..deb9d41 --- /dev/null +++ b/AirScout/Webserver.cs @@ -0,0 +1,854 @@ +// AirScout Aircraft Scatter Prediction +// Copyright (C) DL2ALF +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Forms; +using System.Windows.Forms.VisualStyles; +using GMap.NET; +using GMap.NET.MapProviders; +using GMap.NET.WindowsForms; +using GMap.NET.WindowsForms.Markers; +using GMap.NET.WindowsForms.ToolTips; +using System.IO; +using System.IO.Ports; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using Zeptomoby.OrbitTools; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Reflection; +using System.Configuration; +using WinTest; +using Renci.SshNet; +using System.Diagnostics; +using AquaControls; +using AirScout.Core; +using AirScout.PlaneFeeds.Generic; +using AirScout.Aircrafts; +using NDde; +using NDde.Server; +using NDde.Client; +using ScoutBase; +using ScoutBase.Core; +using ScoutBase.Elevation; +using ScoutBase.Stations; +using ScoutBase.Propagation; +using SerializableGenerics; +using Ionic.Zip; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using SQLiteDatabase; +using System.Xml; +using System.Xml.Serialization; +using System.Security.Cryptography; +using OxyPlot; +using OxyPlot.WindowsForms; +using OxyPlot.Series; +using OxyPlot.Axes; +using System.Data.SQLite; + +namespace AirScout +{ + public partial class MapDlg + { + #region Webserver + + private void bw_Webserver_DoWork(object sender, DoWorkEventArgs e) + { + // name the thread for debugging + if (String.IsNullOrEmpty(Thread.CurrentThread.Name)) + Thread.CurrentThread.Name = this.GetType().Name; + Log.WriteMessage("started."); + // run simple web server + string hosturl = "http://+:" + Properties.Settings.Default.Webserver_Port.ToString() + "/"; + string userName = System.Security.Principal.WindowsIdentity.GetCurrent().Name; + HttpListener listener = null; + while (!bw_Webserver.CancellationPending) + { + string[] prefixes = new string[1]; + prefixes[0] = hosturl; + int id = 0; + try + { + if (!HttpListener.IsSupported) + { + Console.WriteLine("Windows XP SP2 or Server 2003 is required to use the HttpListener class."); + return; + } + // URI prefixes are required, + if (prefixes == null || prefixes.Length == 0) + throw new ArgumentException("prefixes"); + + // Create a listener. + listener = new HttpListener(); + // Add the prefixes. + foreach (string s in prefixes) + { + listener.Prefixes.Add(s); + } + listener.Start(); + Console.WriteLine("Listening..."); + while (!bw_Webserver.CancellationPending) + { + // Note: The GetContext method blocks while waiting for a request. + HttpListenerContext context = listener.GetContext(); + List allplanes = Planes.GetAll(DateTime.UtcNow, Properties.Settings.Default.Planes_Position_TTL); + id++; + // send response from a background thread + WebServerDelivererArgs args = new WebServerDelivererArgs(); + args.ID = id; + args.Context = context; + args.AllPlanes = allplanes; + WebserverDeliver bw = new WebserverDeliver(); + bw.RunWorkerAsync(args); + } + } + catch (HttpListenerException ex) + { + if (ex.ErrorCode == 5) + { + // gain additional access rights for that specific host url + // user will be prompted for allow changes + // DO NOT USE THE "listener=yes" option as recommended!!! + string args = "http add urlacl " + hosturl + " user=" + userName; + ProcessStartInfo psi = new ProcessStartInfo("netsh", args); + psi.Verb = "runas"; + psi.CreateNoWindow = true; + psi.WindowStyle = ProcessWindowStyle.Hidden; + psi.UseShellExecute = true; + + Process.Start(psi).WaitForExit(); + } + } + catch (Exception ex) + { + // do almost nothing + // wait 10 seconds and restart the listener + Thread.Sleep(10000); + } + finally + { + } + } + if (listener != null) + { + lock (listener) + { + listener.Stop(); + } + } + Log.WriteMessage("Finished."); + } + + private void bw_Webserver_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + + } + + #endregion + + } + + public class WebserverDeliver : BackgroundWorker + { + + public short GetElevation(double lat, double lon) + { + if (!GeographicalPoint.Check(lat, lon)) + return 0; + short elv = ElevationData.Database.ElvMissingFlag; + // try to get elevation data from distinct elevation model + // start with detailed one + if (Properties.Settings.Default.Elevation_SRTM1_Enabled && (elv == ElevationData.Database.ElvMissingFlag)) + elv = ElevationData.Database[lat, lon, ELEVATIONMODEL.SRTM1, false]; + if (Properties.Settings.Default.Elevation_SRTM3_Enabled && (elv == ElevationData.Database.ElvMissingFlag)) + elv = ElevationData.Database[lat, lon, ELEVATIONMODEL.SRTM3, false]; + if (Properties.Settings.Default.Elevation_GLOBE_Enabled && (elv == ElevationData.Database.ElvMissingFlag)) + elv = ElevationData.Database[lat, lon, ELEVATIONMODEL.GLOBE, false]; + // set it to zero if still invalid + if (elv <= ElevationData.Database.TileMissingFlag) + elv = 0; + return elv; + } + + public LocationDesignator LocationFind(string call, string loc = "") + { + // check all parameters + if (!Callsign.Check(call)) + return null; + if (!String.IsNullOrEmpty(loc) && !MaidenheadLocator.Check(loc)) + return null; + // get location info + LocationDesignator ld = (String.IsNullOrEmpty(loc)) ? StationData.Database.LocationFind(call) : StationData.Database.LocationFind(call, loc); + // return null if not found + if (ld == null) + return null; + // get elevation + ld.Elevation = GetElevation(ld.Lat, ld.Lon); + ld.BestCaseElevation = false; + // modify location in case of best case elevation is selected --> but do not store in database or settings! + if (Properties.Settings.Default.Path_BestCaseElevation) + { + if (!MaidenheadLocator.IsPrecise(ld.Lat, ld.Lon, 3)) + { + ElvMinMaxInfo maxinfo = GetMinMaxElevationLoc(ld.Loc); + if (maxinfo != null) + { + ld.Lat = maxinfo.MaxLat; + ld.Lon = maxinfo.MaxLon; + ld.Elevation = maxinfo.MaxElv; + ld.BestCaseElevation = true; + } + } + } + return ld; + } + + public List LocationFindAll(string call) + { + // check all parameters + if (!Callsign.Check(call)) + return null; + // get location info + List l = StationData.Database.LocationFindAll(call); + // return null if not found + if (l == null) + return null; + foreach (LocationDesignator ld in l) + { + // get elevation + ld.Elevation = GetElevation(ld.Lat, ld.Lon); + ld.BestCaseElevation = false; + // modify location in case of best case elevation is selected --> but do not store in database or settings! + if (Properties.Settings.Default.Path_BestCaseElevation) + { + if (!MaidenheadLocator.IsPrecise(ld.Lat, ld.Lon, 3)) + { + ElvMinMaxInfo maxinfo = GetMinMaxElevationLoc(ld.Loc); + if (maxinfo != null) + { + ld.Lat = maxinfo.MaxLat; + ld.Lon = maxinfo.MaxLon; + ld.Elevation = maxinfo.MaxElv; + ld.BestCaseElevation = true; + } + } + } + } + return l; + } + + public ElvMinMaxInfo GetMinMaxElevationLoc(string loc) + { + ElvMinMaxInfo elv = new ElvMinMaxInfo(); + // try to get elevation data from distinct elevation model + // start with detailed one + if (Properties.Settings.Default.Elevation_SRTM1_Enabled && (elv.MaxElv == ElevationData.Database.ElvMissingFlag)) + { + ElvMinMaxInfo info = ElevationData.Database.GetMaxElvLoc(loc, ELEVATIONMODEL.SRTM1, false); + if (info != null) + { + elv.MaxLat = info.MaxLat; + elv.MaxLon = info.MaxLon; + elv.MaxElv = info.MaxElv; + elv.MinLat = info.MinLat; + elv.MinLon = info.MinLon; + elv.MinElv = info.MinElv; + } + } + if (Properties.Settings.Default.Elevation_SRTM3_Enabled && (elv.MaxElv == ElevationData.Database.ElvMissingFlag)) + { + ElvMinMaxInfo info = ElevationData.Database.GetMaxElvLoc(loc, ELEVATIONMODEL.SRTM3, false); + if (info != null) + { + elv.MaxLat = info.MaxLat; + elv.MaxLon = info.MaxLon; + elv.MaxElv = info.MaxElv; + elv.MinLat = info.MinLat; + elv.MinLon = info.MinLon; + elv.MinElv = info.MinElv; + } + } + if (Properties.Settings.Default.Elevation_GLOBE_Enabled && (elv.MaxElv == ElevationData.Database.ElvMissingFlag)) + { + ElvMinMaxInfo info = ElevationData.Database.GetMaxElvLoc(loc, ELEVATIONMODEL.GLOBE, false); + if (info != null) + { + elv.MaxLat = info.MaxLat; + elv.MaxLon = info.MaxLon; + elv.MaxElv = info.MaxElv; + elv.MinLat = info.MinLat; + elv.MinLon = info.MinLon; + elv.MinElv = info.MinElv; + } + } + // set it to zero if still invalid + if (elv.MaxElv == ElevationData.Database.ElvMissingFlag) + elv.MaxElv = 0; + if (elv.MinElv == ElevationData.Database.ElvMissingFlag) + elv.MinElv = 0; + + return elv; + } + + + private string DeliverPlanes() + { + string json = ""; + var fs = File.OpenRead(Application.StartupPath + Path.DirectorySeparatorChar + "planes.json"); + using (StreamReader sr = new StreamReader(fs)) + { + json = sr.ReadToEnd(); + } + return json; + } + + private string DeliverVersion(string paramstr) + { + string version = Application.ProductVersion; + string json = ""; + // convert path to json + JsonSerializerSettings settings = new JsonSerializerSettings(); + settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; + settings.FloatFormatHandling = FloatFormatHandling.String; + settings.Formatting = Newtonsoft.Json.Formatting.Indented; + json = JsonConvert.SerializeObject(version, settings); + return json; + } + + private string DeliverSettings(string paramstr) + { + //save settings + Properties.Settings.Default.Save(); + string json = ""; + // convert path to json + JsonSerializerSettings settings = new JsonSerializerSettings(); + settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; + settings.FloatFormatHandling = FloatFormatHandling.String; + settings.Formatting = Newtonsoft.Json.Formatting.Indented; + json = JsonConvert.SerializeObject(Properties.Settings.Default, settings); + return json; + } + + private string DeliverLocation(string paramstr) + { + string json = ""; + // set default values + string callstr = ""; + string locstr = ""; + // get parameters + try + { + if (paramstr.Contains("?")) + { + // OK, we have parameters --> cut them out and make all uppercase + paramstr = paramstr.Substring(paramstr.IndexOf("?") + 1).ToUpper(); + var pars = System.Web.HttpUtility.ParseQueryString(paramstr); + callstr = pars.Get("CALL"); + locstr = pars.Get("LOC"); + } + } + catch (Exception ex) + { + // return error + return "Error while parsing parameters!"; + } + // check parameters + if (!Callsign.Check(callstr)) + return "Error: " + callstr + " is not a valid callsign!"; + LocationDesignator ld = null; + // locstr == null or empty --> return last recent location + if (String.IsNullOrEmpty(locstr)) + { + ld = LocationFind(callstr); + if (ld == null) + return "Error: Location not found in database!"; + json = ld.ToJSON(); + return json; + } + if(locstr == "ALL") + { + List l = LocationFindAll(callstr); + if (l == null) + return "Error: Location not found in database!"; + JsonSerializerSettings settings = new JsonSerializerSettings(); + settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; + settings.FloatFormatHandling = FloatFormatHandling.String; + settings.Formatting = Newtonsoft.Json.Formatting.Indented; + settings.Culture = CultureInfo.InvariantCulture; + json = JsonConvert.SerializeObject(l, settings); + return json; + } + if (!MaidenheadLocator.Check(locstr)) + return "Error: " + locstr + " is not a valid Maidenhead locator!"; + // search call in station database, return empty string if not found + ld = LocationFind(callstr, locstr); + if (ld == null) + return "Error: Location not found in database!"; + json = ld.ToJSON(); + return json; + } + + private string DeliverQRV(string paramstr) + { + string json = ""; + // set default values + string callstr = ""; + string locstr = ""; + string bandstr = ""; + BAND band = Properties.Settings.Default.Band; + // get parameters + try + { + if (paramstr.Contains("?")) + { + // OK, we have parameters --> cut them out and make all uppercase + paramstr = paramstr.Substring(paramstr.IndexOf("?") + 1).ToUpper(); + var pars = System.Web.HttpUtility.ParseQueryString(paramstr); + callstr = pars.Get("CALL"); + locstr = pars.Get("LOC"); + bandstr = pars.Get("BAND"); + } + } + catch (Exception ex) + { + // return error + return "Error while parsing parameters!"; + } + // check parameters + if (!Callsign.Check(callstr)) + return "Error: " + callstr + " is not a valid callsign!"; + if (!MaidenheadLocator.Check(locstr)) + return "Error: " + locstr + " is not a valid Maidenhead locator!"; + // set band to currently selected if empty + if (string.IsNullOrEmpty(bandstr)) + band = Properties.Settings.Default.Band; + else + band = Bands.ParseStringValue(bandstr); + if (band == BAND.BNONE) + return "Error: " + bandstr + " is not a valid band value!"; + // search call in station database, return empty string if not found + if (band != BAND.BALL) + { + QRVDesignator qrv = StationData.Database.QRVFind(callstr, locstr, band); + if (qrv == null) + return "Error: QRV info not found in database!"; + json = qrv.ToJSON(); + return json; + } + List qrvs = StationData.Database.QRVFind(callstr, locstr); + if ((qrvs == null) || (qrvs.Count() == 0)) + return "Error: QRV info not found in database!"; + JsonSerializerSettings settings = new JsonSerializerSettings(); + settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; + settings.FloatFormatHandling = FloatFormatHandling.String; + settings.Formatting = Newtonsoft.Json.Formatting.Indented; + json = JsonConvert.SerializeObject(qrvs, settings); + return json; + } + + private string DeliverElevationPath(string paramstr) + { + string json = ""; + // set default values + string mycallstr = ""; + string mylocstr = ""; + string dxcallstr = ""; + string dxlocstr = ""; + BAND band = Properties.Settings.Default.Band; + // get parameters + try + { + if (paramstr.Contains("?")) + { + // OK, we have parameters --> cut them out and make all uppercase + paramstr = paramstr.Substring(paramstr.IndexOf("?") + 1).ToUpper(); + var pars = System.Web.HttpUtility.ParseQueryString(paramstr); + mycallstr = pars.Get("MYCALL"); + mylocstr = pars.Get("MYLOC"); + dxcallstr = pars.Get("DXCALL"); + dxlocstr = pars.Get("DXLOC"); + } + } + catch (Exception ex) + { + // return error + return "Error while parsing parameters!"; + } + // check parameters + if (!Callsign.Check(mycallstr)) + return "Error: " + mycallstr + " is not a valid callsign!"; + if (!Callsign.Check(dxcallstr)) + return "Error: " + dxcallstr + " is not a valid callsign!"; + if (!String.IsNullOrEmpty(mylocstr) && !MaidenheadLocator.Check(mylocstr)) + return "Error: " + mylocstr + " is not a valid Maidenhead locator!"; + if (!String.IsNullOrEmpty(dxlocstr) && !MaidenheadLocator.Check(dxlocstr)) + return "Error: " + dxlocstr + " is not a valid Maidenhead locator!"; + // search call in station database, return empty string if not found + LocationDesignator myloc = LocationFind(mycallstr, mylocstr); + if (myloc == null) + return "Error: MyLocation not found in database!"; + LocationDesignator dxloc = LocationFind(dxcallstr, dxlocstr); + if (dxloc == null) + return "Error: DXLocation not found in database!"; + + // get qrv info or create default + QRVDesignator myqrv = StationData.Database.QRVFindOrCreateDefault(myloc.Call, myloc.Loc, band); + // set qrv defaults if zero + if (myqrv.AntennaHeight == 0) + myqrv.AntennaHeight = StationData.Database.QRVGetDefaultAntennaHeight(band); + if (myqrv.AntennaGain == 0) + myqrv.AntennaGain = StationData.Database.QRVGetDefaultAntennaGain(band); + if (myqrv.Power == 0) + myqrv.Power = StationData.Database.QRVGetDefaultPower(band); + + // get qrv info or create default + QRVDesignator dxqrv = StationData.Database.QRVFindOrCreateDefault(dxloc.Call, dxloc.Loc, band); + // set qrv defaults if zero + if (dxqrv.AntennaHeight == 0) + dxqrv.AntennaHeight = StationData.Database.QRVGetDefaultAntennaHeight(band); + if (dxqrv.AntennaGain == 0) + dxqrv.AntennaGain = StationData.Database.QRVGetDefaultAntennaGain(band); + if (dxqrv.Power == 0) + dxqrv.Power = StationData.Database.QRVGetDefaultPower(band); + + // get or calculate elevation path + ElevationPathDesignator epath = ElevationData.Database.ElevationPathFindOrCreateFromLatLon( + this, + myloc.Lat, + myloc.Lon, + dxloc.Lat, + dxloc.Lon, + ElevationData.Database.GetDefaultStepWidth(Properties.Settings.Default.ElevationModel), + Properties.Settings.Default.ElevationModel); + if (epath == null) + return json; + // add additional info to ppath + epath.Location1 = myloc; + epath.Location2 = dxloc; + epath.QRV1 = myqrv; + epath.QRV2 = dxqrv; + // convert path to json + json = epath.ToJSON(); + return json; + } + + private string DeliverPropagationPath(string paramstr) + { + string json = ""; + // set default values + string mycallstr = ""; + string mylocstr = ""; + string dxcallstr = ""; + string dxlocstr = ""; + string bandstr = ""; + BAND band = Properties.Settings.Default.Band; + // get parameters + try + { + if (paramstr.Contains("?")) + { + // OK, we have parameters --> cut them out and make all uppercase + paramstr = paramstr.Substring(paramstr.IndexOf("?") + 1).ToUpper(); + var pars = System.Web.HttpUtility.ParseQueryString(paramstr); + mycallstr = pars.Get("MYCALL"); + mylocstr = pars.Get("MYLOC"); + dxcallstr = pars.Get("DXCALL"); + dxlocstr = pars.Get("DXLOC"); + bandstr = pars.Get("BAND"); + } + } + catch (Exception ex) + { + // return error + return "Error while parsing parameters!"; + } + // check parameters + if (!Callsign.Check(mycallstr)) + return "Error: " + mycallstr + " is not a valid callsign!"; + if (!Callsign.Check(dxcallstr)) + return "Error: " + dxcallstr + " is not a valid callsign!"; + if (!String.IsNullOrEmpty(mylocstr) && !MaidenheadLocator.Check(mylocstr)) + return "Error: " + mylocstr + " is not a valid Maidenhead locator!"; + if (!String.IsNullOrEmpty(dxlocstr) && !MaidenheadLocator.Check(dxlocstr)) + return "Error: " + dxlocstr + " is not a valid Maidenhead locator!"; + // set band to currently selected if empty + if (string.IsNullOrEmpty(bandstr)) + band = Properties.Settings.Default.Band; + else + band = Bands.ParseStringValue(bandstr); + if (band == BAND.BNONE) + return "Error: " + bandstr + " is not a valid band value!"; + // search call in station database, return empty string if not found + LocationDesignator myloc = LocationFind(mycallstr, mylocstr); + if (myloc == null) + return "Error: MyLocation not found in database!"; + LocationDesignator dxloc = LocationFind(dxcallstr, dxlocstr); + if (dxloc == null) + return "Error: DXLocation not found in database!"; + + // get qrv info or create default + QRVDesignator myqrv = StationData.Database.QRVFindOrCreateDefault(myloc.Call, myloc.Loc, band); + // set qrv defaults if zero + if (myqrv.AntennaHeight == 0) + myqrv.AntennaHeight = StationData.Database.QRVGetDefaultAntennaHeight(band); + if (myqrv.AntennaGain == 0) + myqrv.AntennaGain = StationData.Database.QRVGetDefaultAntennaGain(band); + if (myqrv.Power == 0) + myqrv.Power = StationData.Database.QRVGetDefaultPower(band); + + // get qrv info or create default + QRVDesignator dxqrv = StationData.Database.QRVFindOrCreateDefault(dxloc.Call, dxloc.Loc, band); + // set qrv defaults if zero + if (dxqrv.AntennaHeight == 0) + dxqrv.AntennaHeight = StationData.Database.QRVGetDefaultAntennaHeight(band); + if (dxqrv.AntennaGain == 0) + dxqrv.AntennaGain = StationData.Database.QRVGetDefaultAntennaGain(band); + if (dxqrv.Power == 0) + dxqrv.Power = StationData.Database.QRVGetDefaultPower(band); + + // find local obstruction, if any + LocalObstructionDesignator o = ElevationData.Database.LocalObstructionFind(myloc.Lat, myloc.Lon, Properties.Settings.Default.ElevationModel); + double mybearing = LatLon.Bearing(myloc.Lat, myloc.Lon, dxloc.Lat, dxloc.Lon); + double myobstr = (o != null) ? o.GetObstruction(myqrv.AntennaHeight, mybearing) : double.MinValue; + + // get or calculate propagation path + PropagationPathDesignator ppath = PropagationData.Database.PropagationPathFindOrCreateFromLatLon( + this, + myloc.Lat, + myloc.Lon, + GetElevation(myloc.Lat, myloc.Lon) + myqrv.AntennaHeight, + dxloc.Lat, + dxloc.Lon, + GetElevation(dxloc.Lat, dxloc.Lon) + dxqrv.AntennaHeight, + Bands.ToGHz(band), + LatLon.Earth.Radius * Properties.Settings.Default.Path_Band_Settings[band].K_Factor, + Properties.Settings.Default.Path_Band_Settings[band].F1_Clearance, + ElevationData.Database.GetDefaultStepWidth(Properties.Settings.Default.ElevationModel), + Properties.Settings.Default.ElevationModel, + myobstr); + if (ppath == null) + return json; + // add additional info to ppath + ppath.Location1 = myloc; + ppath.Location2 = dxloc; + ppath.QRV1 = myqrv; + ppath.QRV2 = dxqrv; + // convert path to json + json = ppath.ToJSON(); + return json; + } + + private string DeliverNearestPlanes(string paramstr, List allplanes) + { + string json = ""; + // set default values + string mycallstr = ""; + string mylocstr = ""; + string dxcallstr = ""; + string dxlocstr = ""; + string bandstr = ""; + BAND band = Properties.Settings.Default.Band; + // get parameters + try + { + if (paramstr.Contains("?")) + { + // OK, we have parameters --> cut them out and make all uppercase + paramstr = paramstr.Substring(paramstr.IndexOf("?") + 1).ToUpper(); + var pars = System.Web.HttpUtility.ParseQueryString(paramstr); + mycallstr = pars.Get("MYCALL"); + mylocstr = pars.Get("MYLOC"); + dxcallstr = pars.Get("DXCALL"); + dxlocstr = pars.Get("DXLOC"); + bandstr = pars.Get("BAND"); + } + } + catch (Exception ex) + { + // return error + return "Error while parsing parameters!"; + } + // check parameters + if (!Callsign.Check(mycallstr)) + return "Error: " + mycallstr + " is not a valid callsign!"; + if (!Callsign.Check(dxcallstr)) + return "Error: " + dxcallstr + " is not a valid callsign!"; + if (!String.IsNullOrEmpty(mylocstr) && !MaidenheadLocator.Check(mylocstr)) + return "Error: " + mylocstr + " is not a valid Maidenhead locator!"; + if (!String.IsNullOrEmpty(dxlocstr) && !MaidenheadLocator.Check(dxlocstr)) + return "Error: " + dxlocstr + " is not a valid Maidenhead locator!"; + // set band to currently selected if empty + if (string.IsNullOrEmpty(bandstr)) + band = Properties.Settings.Default.Band; + else + band = Bands.ParseStringValue(bandstr); + if (band == BAND.BNONE) + return "Error: " + bandstr + " is not a valid band value!"; + // search call in station database, return empty string if not found + LocationDesignator myloc = LocationFind(mycallstr, mylocstr); + if (myloc == null) + return "Error: MyLocation not found in database!"; + LocationDesignator dxloc = LocationFind(dxcallstr, dxlocstr); + if (dxloc == null) + return "Error: DXLocation not found in database!"; + + // get qrv info or create default + QRVDesignator myqrv = StationData.Database.QRVFindOrCreateDefault(myloc.Call, myloc.Loc, band); + // set qrv defaults if zero + if (myqrv.AntennaHeight == 0) + myqrv.AntennaHeight = StationData.Database.QRVGetDefaultAntennaHeight(band); + if (myqrv.AntennaGain == 0) + myqrv.AntennaGain = StationData.Database.QRVGetDefaultAntennaGain(band); + if (myqrv.Power == 0) + myqrv.Power = StationData.Database.QRVGetDefaultPower(band); + + // get qrv info or create default + QRVDesignator dxqrv = StationData.Database.QRVFindOrCreateDefault(dxloc.Call, dxloc.Loc, band); + // set qrv defaults if zero + if (dxqrv.AntennaHeight == 0) + dxqrv.AntennaHeight = StationData.Database.QRVGetDefaultAntennaHeight(band); + if (dxqrv.AntennaGain == 0) + dxqrv.AntennaGain = StationData.Database.QRVGetDefaultAntennaGain(band); + if (dxqrv.Power == 0) + dxqrv.Power = StationData.Database.QRVGetDefaultPower(band); + + // find local obstruction, if any + LocalObstructionDesignator o = ElevationData.Database.LocalObstructionFind(myloc.Lat, myloc.Lon, Properties.Settings.Default.ElevationModel); + double mybearing = LatLon.Bearing(myloc.Lat, myloc.Lon, dxloc.Lat, dxloc.Lon); + double myobstr = (o != null) ? o.GetObstruction(myqrv.AntennaHeight, mybearing) : double.MinValue; + + // get or calculate propagation path + PropagationPathDesignator ppath = PropagationData.Database.PropagationPathFindOrCreateFromLatLon( + this, + myloc.Lat, + myloc.Lon, + GetElevation(myloc.Lat, myloc.Lon) + myqrv.AntennaHeight, + dxloc.Lat, + dxloc.Lon, + GetElevation(dxloc.Lat, dxloc.Lon) + dxqrv.AntennaHeight, + Bands.ToGHz(band), + LatLon.Earth.Radius * Properties.Settings.Default.Path_Band_Settings[band].K_Factor, + Properties.Settings.Default.Path_Band_Settings[band].F1_Clearance, + ElevationData.Database.GetDefaultStepWidth(Properties.Settings.Default.ElevationModel), + Properties.Settings.Default.ElevationModel, + myobstr); + if (ppath == null) + return json; + // add additional info to ppath + ppath.Location1 = myloc; + ppath.Location2 = dxloc; + ppath.QRV1 = myqrv; + ppath.QRV2 = dxqrv; + /* + // estimate positions according to time + DateTime time = DateTime.UtcNow; + foreach(PlaneInfo plane in allplanes.Planes) + { + // change speed to km/h + double speed = plane.Speed_kmh; + // calculate distance after timespan + double dist = speed * (time - allplanes.At).TotalHours; + LatLon.GPoint newpos = LatLon.DestinationPoint(plane.Lat, plane.Lon, plane.Track, dist); + plane.Lat = newpos.Lat; + plane.Lon = newpos.Lon; + plane.Time = time; + } + */ + // get nearest planes + List nearestplanes = AircraftData.Database.GetNearestPlanes(DateTime.UtcNow, ppath, allplanes, Properties.Settings.Default.Planes_Filter_Max_Circumcircle, Properties.Settings.Default.Path_Band_Settings[band].MaxDistance, Properties.Settings.Default.Planes_MaxAlt); + // convert nearestplanes to json + JsonSerializerSettings settings = new JsonSerializerSettings(); + settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; + settings.FloatFormatHandling = FloatFormatHandling.String; + settings.Formatting = Newtonsoft.Json.Formatting.Indented; + json = JsonConvert.SerializeObject(nearestplanes, settings); + return json; + } + + protected override void OnDoWork(DoWorkEventArgs e) + { + WebServerDelivererArgs args = (WebServerDelivererArgs)e.Argument; + if (String.IsNullOrEmpty(Thread.CurrentThread.Name)) + Thread.CurrentThread.Name = this.GetType().Name + "_" + args.ID; + HttpListenerRequest request = args.Context.Request; + // Obtain a response object. + HttpListenerResponse response = args.Context.Response; + // Construct a default response. + string responsestring = " Welcome to AirScout!"; + // check for content delivery request + if (request.RawUrl.ToLower() == "/planes.json") + { + responsestring = DeliverPlanes(); + } + else if (request.RawUrl.ToLower().StartsWith("/version.json")) + { + responsestring = DeliverVersion(request.RawUrl); + } + else if (request.RawUrl.ToLower().StartsWith("/settings.json")) + { + responsestring = DeliverSettings(request.RawUrl); + } + else if (request.RawUrl.ToLower().StartsWith("/location.json")) + { + responsestring = DeliverLocation(request.RawUrl); + } + else if (request.RawUrl.ToLower().StartsWith("/qrv.json")) + { + responsestring = DeliverQRV(request.RawUrl); + } + else if (request.RawUrl.ToLower().StartsWith("/elevationpath.json")) + { + responsestring = DeliverElevationPath(request.RawUrl); + } + else if (request.RawUrl.ToLower().StartsWith("/propagationpath.json")) + { + responsestring = DeliverPropagationPath(request.RawUrl); + } + else if (request.RawUrl.ToLower().StartsWith("/nearestplanes.json")) + { + responsestring = DeliverNearestPlanes(request.RawUrl, args.AllPlanes); + } + // copy bytes to buffer + byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responsestring); + // Get a response stream and write the response to it. + response.ContentLength64 = buffer.Length; + System.IO.Stream output = response.OutputStream; + output.Write(buffer, 0, buffer.Length); +// Thread.Sleep(1000); + // You must close the output stream. + output.Close(); + } + } + + public class WebServerDelivererArgs + { + public int ID; + public HttpListenerContext Context; + public List AllPlanes; + } +} diff --git a/AirScout/WinTest.cs b/AirScout/WinTest.cs new file mode 100644 index 0000000..5067a63 --- /dev/null +++ b/AirScout/WinTest.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace WinTest +{ + public enum WTMESSAGES + { + NONE = 0, + STATUS = 1, + PKTRCVD = 2, + SETAZIMUTH = 250, + SETELEVATION = 251, + ASSETPATH = 252, + ASSHOWPATH = 253, + ASNEAREST = 254, + UNKNOWN = 255 + } + + public class wtMessage + { + public WTMESSAGES Msg; + public string Src; + public string Dst; + public string Data; + public string Extra; + public byte Checksum; + public bool HasChecksum; + + public wtMessage(byte[] bytes) + { + Msg = WTMESSAGES.NONE; + Src = ""; + Dst = ""; + Data = ""; + Extra = ""; + Checksum = 0; + HasChecksum = false; + this.FromBytes(bytes); + } + + public wtMessage(WTMESSAGES MSG, string src, string dst, string data) + { + Msg = MSG; + Src = src; + Dst = dst; + Data = data; + Extra = ""; + Checksum = 0; + HasChecksum = false; + } + + public wtMessage(WTMESSAGES MSG, string src, string dst, string data, string extra) + { + Msg = MSG; + Src = src; + Dst = dst; + Data = data; + Extra = extra; + Checksum = 0; + HasChecksum = false; + } + + public void FromBytes(byte[] bytes) + { + // convert bytes coded in UTF8 to text + string text = Encoding.ASCII.GetString(bytes); + string msg = text.Substring(0, text.IndexOf(": ")); + try + { + Msg = (WTMESSAGES)Enum.Parse(typeof(WTMESSAGES), msg); + } + catch + { + Msg = WTMESSAGES.UNKNOWN; + } + text = text.Remove(0, text.IndexOf(": ") + 2); + Src = text.Substring(0, text.IndexOf(" ")).Replace("\"", ""); + text = text.Remove(0, text.IndexOf(" ") + 1); + Dst = text.Substring(0, text.IndexOf(" ")).Replace("\"", ""); + text = text.Remove(0, text.IndexOf(" ") + 1); + // Clean up the message --> scrub last byte + text = text.Substring(0, text.Length - 1).Replace("\"", ""); + // convert bytes coded in UTF8 to text + Data = text.Substring(0, text.Length); + // get checksum + Checksum = (byte)(bytes[bytes.Length - 1] | 0x80); + byte sum = 0; + for (int i = 0; i < bytes.Length-1; i++) + sum += bytes[i]; + sum = (byte)(sum | 0x80); + if (Checksum == sum) + HasChecksum = true; + else + HasChecksum = false; + } + + public byte[] ToBytes() + { + byte[] b = null; + string s; + try + { + switch (Msg) + { + case WTMESSAGES.NONE: + break; + case WTMESSAGES.UNKNOWN: + break; + default: + // combine all fields to string incl. placeholder for checksum + s = Msg + ": " + "\"" + Src + "\" \"" + Dst + "\" \"" + Data + "\"" + Extra + "?"; + // translate into ASCII bytes + b = Encoding.ASCII.GetBytes(s); + // calculate checksum + byte sum = 0; + for (int i = 0; i < b.Length-1; i++) + sum += b[i]; + sum = (byte)(sum | (byte)0x80); + b[b.Length - 1] = sum; + break; + } + } + catch + { + throw new ArgumentOutOfRangeException(Msg.ToString(), "Unkwon Message."); + } + return b; + } + } + + public class WinTest + { + } +} diff --git a/AirScout/airport.png b/AirScout/airport.png new file mode 100644 index 0000000..bec3a61 Binary files /dev/null and b/AirScout/airport.png differ diff --git a/AirScout/app.config b/AirScout/app.config new file mode 100644 index 0000000..ba8724c --- /dev/null +++ b/AirScout/app.config @@ -0,0 +1,589 @@ + + + + +
+ + +
+ + + + + DL2ALF + + + GB3MHZ + + + B1_2G + + + 60 + + + 5000 + + + True + + + True + + + 0 + + + 12200 + + + 14 + + + 30 + + + \Database\calls.txt + + + \Database\call3.txt + + + 1000 + + + 0 + + + NONE + + + -15 + + + 30 + + + 35 + + + 60 + + + 50.937067 + + + 10.68327 + + + 52.05626084 + + + 1.28022909 + + + 23 + + + 328 + + + False + + + False + + + http://www.qrz.com/db/ + + + 100 + + + 1 + + + True + + + True + + + 100 + + + False + + + Database\localobstructions.txt + + + True + + + False + + + False + + + 9871 + + + 9872 + + + True + + + \Icon + + + plane.png + + + [none] + + + [none] + + + [WebFeed] Virtual Radar Server + + + 5 + + + False + + + AS + + + OpenStreetMap + + + + + + Microsoft Sans Serif; 11;Bold + + + http://localhost:10080/ + + + 1 + + + _fft_expt.json + + + 400 + + + 1600 + + + False + + + True + + + False + + + True + + + True + + + False + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + HTTP://WWW.AIRSCOUT.EU/DOWNLOADS/AIRSCOUT.PWD + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + True + + + -1 + + + -1 + + + False + + + 0 + + + 0 + + + 16 + + + 24 + + + 36 + + + 48 + + + 9880 + + + 01/31/2015 15:21:00 + + + 3600 + + + http://www.airscout.eu/news.html + + + False + + + False + + + COM13 + + + 4800 + + + False + + + False + + + False + + + False + + + False + + + True + + + True + + + True + + + True + + + 9871 + + + 9872 + + + http://www.airscout.eu/downloads/donate.html + + + 0 + + + True + + + 2015-02-25 + + + airport.png + + + True + + + \Database\Airlines.txt + + + http://www.flugzeuginfo.net/table_airlinecodes_airline_en.php + + + 1 + + + True + + + http://www.airscout.eu/downloads/database/ + + + True + + + Maximized + + + 0, 0 + + + 0, 0 + + + False + + + 60 + + + \Tmp + + + \Log + + + \Database + + + 10 + + + True + + + False + + + \ElevationData + + + http://www.airscout.eu + + + False + + + True + + + http://history.adsbexchange.com/Aircraftlist.json/ + + + + + + 2017-07-01 + + + + + + 5 + + + + + + False + + + + + + True + + + False + + + False + + + +Maidenhead locator system elvation tiles calculated by DL2ALF based on elevation data of the GLOBE project: + +GLOBE Task Team and others (Hastings, David A., Paula K. Dunbar, Gerald M. Elphingstone, Mark Bootz, Hiroshi Murakami, Hiroshi Maruyama, +Hiroshi Masaharu, Peter Holland, John Payne, Nevin A. Bryant, Thomas L. Logan, J.-P. Muller, Gunter Schreier, and John S. MacDonald), eds., 1999. + +The Global Land One-kilometer Base Elevation (GLOBE) Digital Elevation Model, +Version 1.0. National Oceanic and Atmospheric Administration, National Geophysical Data Center, 325 Broadway, Boulder, Colorado 80305-3328, U.S.A. + +Digital data base on the World Wide Web (URL: http://www.ngdc.noaa.gov/mgg/topo/globe.html) and CD-ROMs. + + + +Maidenhead locator system elvation tiles calculated by DL2ALF based on elevation data of the SRTM project: + +This material is based on SRTM3 data V3.0 services provided by the OpenTopography Facility with support from the National Science Foundation under NSF Award Numbers 1226353 & 1225810 +http://opentopo.sdsc.edu/raster?opentopoID=OTSRTM.042013.4326.1 + +The Shuttle Radar Topography Mission (SRTM) obtained elevation data on a near-global scale to generate the most complete high-resolution digital topographic database of Earth. SRTM consisted of a specially modified radar system that flew onboard the Space Shuttle Endeavour during an 11-day mission in February of 2000. SRTM is an international project spearheaded by the National Geospatial-Intelligence Agency (NGA) and the National Aeronautics and Space Administration (NASA). + +Version 3: Elimination of the voids in the NASA SRTM DEM was the primary goal of a project under the NASA MEaSUREs (Making Earth System Data Records for Use in Research Environments) Program. Ultimately this was achieved by filling the voids with elevation data primarily from the ASTER GDEM2 (Global Digital Elevation Model Version 2) and secondarily from the USGS GMTED2010 elevation model or the USGS National Elevation Dataset (NED). For more information on this dataset visit the LP DAAC NASA Shuttle Radar Topography Mission Global 3 arc second page. + +ASTER GDEM is a product of NASA and METI. See https://lpdaac.usgs.gov/dataset_discovery/aster + +MEaSUREs data and products are available at no charge from the LP DAAC.See https://lpdaac.usgs.gov/dataset_discovery/measures + + + + + + + + + +Maidenhead locator system elvation tiles calculated by DL2ALF based on elevation data of the SRTM project: + +This material is based on SRTM1 data V3.0 services provided by the OpenTopography Facility with support from the National Science Foundation under NSF Award Numbers 1226353 & 1225810 +http://opentopo.sdsc.edu/raster?opentopoID=OTSRTM.082015.4326.1 + +The Shuttle Radar Topography Mission (SRTM) obtained elevation data on a near-global scale to generate the most complete high-resolution digital topographic database of Earth. SRTM consisted of a specially modified radar system that flew onboard the Space Shuttle Endeavour during an 11-day mission in February of 2000. SRTM is an international project spearheaded by the National Geospatial-Intelligence Agency (NGA) and the National Aeronautics and Space Administration (NASA). +Version 3: Elimination of the voids in the NASA SRTM DEM was the primary goal of a project under the NASA MEaSUREs (Making Earth System Data Records for Use in Research Environments) Program. Ultimately this was achieved by filling the voids with elevation data primarily from the ASTER GDEM2 (Global Digital Elevation Model Version 2) and secondarily from the USGS GMTED2010 elevation model or the USGS National Elevation Dataset (NED). NASA SRTM V3.0 three-arc-second data are provided in two forms: (1) by three-by-three averaging of the one arc-second samples, and (2) by extracting the middle sample of those same three-by-three samples. For more information on this dataset visit the LP DAAC NASA Shuttle Radar Topography Mission Global 1 arc second page. +https://lpdaac.usgs.gov/dataset_discovery/measures/measures_products_table/srtmgl1 + +ASTER GDEM is a product of NASA and METI. See https://lpdaac.usgs.gov/dataset_discovery/aster + +MEaSUREs data and products are available at no charge from the LP DAAC.See https://lpdaac.usgs.gov/dataset_discovery/measures + + + + + + + + 1000 + + + True + + + UNDEFINED + + + 0 + + + + + + False + + + NONE + + + UNDEFINED + + + 500 + + + UNDEFINED + + + UNDEFINED + + + UNDEFINED + + + 1500 + + + 7 + + + 10000000 + + + False + + + 10 + + + False + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AirScout/libSQLite.Interop.so b/AirScout/libSQLite.Interop.so new file mode 100644 index 0000000..15d0e3e Binary files /dev/null and b/AirScout/libSQLite.Interop.so differ diff --git a/AirScout/packages.config b/AirScout/packages.config new file mode 100644 index 0000000..2e76bb5 --- /dev/null +++ b/AirScout/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/AirScout/plane.png b/AirScout/plane.png new file mode 100644 index 0000000..b521502 Binary files /dev/null and b/AirScout/plane.png differ diff --git a/AirScout/wtElevationData.cs b/AirScout/wtElevationData.cs new file mode 100644 index 0000000..275e80e --- /dev/null +++ b/AirScout/wtElevationData.cs @@ -0,0 +1,707 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Net; +using System.IO; +using System.Xml.Linq; +using System.Globalization; +using SphericalEarth; +using System.Drawing; +using System.Drawing.Imaging; +using System.Windows.Forms; + +namespace AirScout +{ + public class wtElevationData + { + + private int[,] Data = null; + + public int number_of_rows = 0; + public int number_of_columns = 0; + + public double left_map_x = -10.00000000000; + public double right_map_x = 20.00000000000; + public double upper_map_y = 60.00000000000; + public double lower_map_y = 35.00000000000; + public double grid_size = 0.00833333333; + + public int elev_m_min = -30; + public int elev_m_max = 4570; + public static int elev_m_missing_flag = -500; + + static int latbase = int.MinValue; + static int lonbase = int.MinValue; + static string hgtfilename = ""; + static bool hgtexists = false; + static BinaryReader strmbr = null; + static int xysize = int.MinValue; + + static GLOBETile currenttile; + static BinaryReader globebr = null; + public static int globe_elv_missing = -500; + + GLOBETileCollection GLOBETiles; + + public wtElevationData() + { + GLOBETiles = new GLOBETileCollection(Properties.Settings.Default.Elevation_GLOBE_DataPath); + foreach (GLOBETile tile in GLOBETiles) + { + Bitmap bm = DrawGLOBEBitmap(tile);ergrthrt + bm.Save(tile.Name + ".jpg", ImageFormat.Jpeg); + } + + // uses elevation data from the (G)lobal (L)and (O)ne-km (B)ase (E)levation + /* + try + { + // search for header file in the given directory + string[] files = Directory.GetFiles(Application.StartupPath + "\\" + Properties.Settings.Default.Elevation_GLOBE_DataPath, "*.hdr"); + // must be exactly 1 header file in the dir + if (files.Length == 1) + { + Properties.Settings.Default.Elevation_GLOBE_HeaderFileName = files[0]; + Properties.Settings.Default.Elevation_GLOBE_DataFileName = Path.ChangeExtension(Properties.Settings.Default.Elevation_GLOBE_HeaderFileName, ".bin"); + // get an EN - formatinfo to convert the doubles + NumberFormatInfo provider = new NumberFormatInfo(); + provider.NumberDecimalSeparator = "."; + provider.NumberGroupSeparator = ","; + provider.NumberGroupSizes = new int[] { 3 }; + // processing the header file first + using (StreamReader sr = new StreamReader(Properties.Settings.Default.Elevation_GLOBE_HeaderFileName)) + { + while (!sr.EndOfStream) + { + string s = sr.ReadLine(); + if (s.IndexOf("left_map_x") >= 0) + { + left_map_x = System.Convert.ToDouble(s.Remove(0, s.IndexOf("=") + 1), provider); + } + else if (s.IndexOf("right_map_x") >= 0) + { + right_map_x = System.Convert.ToDouble(s.Remove(0, s.IndexOf("=") + 1), provider); + } + else if (s.IndexOf("upper_map_y") >= 0) + { + upper_map_y = System.Convert.ToDouble(s.Remove(0, s.IndexOf("=") + 1), provider); + } + else if (s.IndexOf("lower_map_y") >= 0) + { + lower_map_y = System.Convert.ToDouble(s.Remove(0, s.IndexOf("=") + 1), provider); + } + else if (s.IndexOf("number_of_rows") >= 0) + { + number_of_rows = System.Convert.ToInt32(s.Remove(0, s.IndexOf("=") + 1)); + } + else if (s.IndexOf("number_of_columns") >= 0) + { + number_of_columns = System.Convert.ToInt32(s.Remove(0, s.IndexOf("=") + 1)); + } + else if (s.IndexOf("grid_size") >= 0) + { + grid_size = System.Convert.ToDouble(s.Remove(0, s.IndexOf("=") + 1), provider); + } + else if (s.IndexOf("elev_m_min") >= 0) + { + elev_m_min = System.Convert.ToInt32(s.Remove(0, s.IndexOf("=") + 1)); + } + else if (s.IndexOf("elev_m_max") >= 0) + { + elev_m_max = System.Convert.ToInt32(s.Remove(0, s.IndexOf("=") + 1)); + } + else if (s.IndexOf("elev_m_missing_flag") >= 0) + { + elev_m_missing_flag = System.Convert.ToInt32(s.Remove(0, s.IndexOf("=") + 1)); + } + } + } + Data = new int[number_of_columns, number_of_rows]; + using (Stream stream = File.Open(Properties.Settings.Default.Elevation_GLOBE_DataFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + byte[] buf = new byte[2]; + for (int i = 0; i < number_of_rows; i++) + { + for (int j = 0; j < number_of_columns; j++) + { + stream.Read(buf, 0, 2); + Data[j, number_of_rows - i - 1] = BitConverter.ToInt16(buf, 0); + } + } + } + } + else + { + throw new FileNotFoundException(); + } + } + catch (Exception e1) + { + MessageBox.Show("Error while reading " + Properties.Settings.Default.Elevation_GLOBE_DataFileName + "\n" + e1); + } + */ + } + + public Bitmap DrawGLOBEBitmap(GLOBETile tile) + { + byte[] a16 = new byte[2]; + DEMColorPalette palette = new DEMColorPalette(); + byte[] buffer; + short[] Data = new short[tile.Columns * tile.Rows]; + Bitmap bm = new Bitmap(tile.Columns, tile.Rows); + int index = 0; + using (BinaryReader br = new BinaryReader (File.OpenRead(tile.Name))) + { + buffer = br.ReadBytes(Data.Length * 2); + Buffer.BlockCopy(buffer, 0, Data, 0, buffer.Length); + index++; + } + for (int i = 0; i < tile.Rows; i++) + { + // System.Console.WriteLine(i); + for (int j = 0; j < tile.Columns; j++) + { + int e1 = Data[(i * tile.Columns + j)]; + if (e1 != elev_m_missing_flag) + { + double e = (double)(e1 - tile.MinElv) / (double)(tile.MaxElv - tile.MinElv) * 100.0; + if (e < 0) + e = 0; + if (e > 100) + e = 100; + bm.SetPixel(j, i, palette.GetColor(e)); + } + else + { + bm.SetPixel(j, i, Color.FromArgb(0, 0, 128)); + } + } + } + return bm; + } + + public ElvMaxInfo GetMaxElvLoc (string loc) + { + if (!(WCCheck.WCCheck.IsLoc(loc) > 0)) + return null; + double startlat = ((int)(LatLon.Lat(loc) * 24.0)) / 24.0; + double startlon = ((int)(LatLon.Lon(loc) * 12.0)) / 12.0; + double stoplat = startlat + 1.0/24.0; + double stoplon = startlon + 2.0/24.0; + double stepwidthi = (stoplat - startlat) / 100.0; + double stepwidthj = (stoplon - startlon) / 100.0; + ElvMaxInfo maxinfo = new ElvMaxInfo(int.MinValue, 0, 0); + for (double i = startlat; i <= stoplat; i += stepwidthi) + { + for (double j = startlon; j <= stoplon; j += stepwidthj) + { + int elv = this[i, j]; + if (elv > maxinfo.Elv) + { + maxinfo.Elv = elv; + maxinfo.Lat = i; + maxinfo.Lon = j; + } + } + } + return maxinfo; + } + + public int this[double lat, double lon] + { + get + { + lock (this) + { + int e = elev_m_missing_flag; + // get elevation data from SRTM1 if enabled + if (Properties.Settings.Default.Elevation_SRTM1_Enabled) + { + e = GetSRTMElevation(Properties.Settings.Default.Elevation_SRTM1_DataPath, lat, lon); + if (e != elev_m_missing_flag) + return e; + } + else if (Properties.Settings.Default.Elevation_SRTM3_Enabled) + { + e = GetSRTMElevation(Properties.Settings.Default.Elevation_SRTM3_DataPath, lat, lon); + if (e != elev_m_missing_flag) + return e; + } + else if (Properties.Settings.Default.Elevation_GLOBE_Enabled) + { + e = GetGLOBEElevation(Properties.Settings.Default.Elevation_GLOBE_DataPath, lat, lon); + if (e != elev_m_missing_flag) + return e; + } + if (lat < lower_map_y) + return 0; + if (lat > upper_map_y) + return 0; + if (lon < left_map_x) + return 0; + if (lon > right_map_x) + return 0; + int i = (int)((lon - left_map_x) / grid_size); + int j = (int)((lat - lower_map_y) / grid_size); + // simple data + // int e = Data[i, j]; + /* + // get maximum elevation around the point + e = 0; + try + { + Bitmap bm = new Bitmap(5, 3); + int elevmax = int.MinValue; + int elevmin = int.MaxValue; + int elevavg = 0; + int elevsum = 0; + int count = 0; + for (int i1 = 0; i1 < bm.Width; i1++) + { + for (int j1 = 0; j1 < bm.Height; j1++) + { + int c = Data[i1 + i - bm.Width / 2, j1 + j - bm.Height / 2]; + if (c != elev_m_missing_flag) + { + if (elevmin > c) + elevmin = c; + if (elevmax < c) + elevmax = c; + count++; + elevsum += c; + } + } + } + if (count > 0) + elevavg = elevsum / count; + e = elevavg; + for (int i1 = 0; i1 < bm.Width; i1++) + { + for (int j1 = 0; j1 < bm.Height; j1++) + { + if (Data[i1 + i - bm.Width / 2, j1 + j - bm.Height / 2] != elev_m_missing_flag) + { + int c = 0; + if ((elevmax - elevmin) != 0) + c = (Data[i1 + i - bm.Width / 2, j1 + j - bm.Height / 2] - elevmin) * 255 / (elevmax - elevmin); + double percent = c * 100 / 255; + bm.SetPixel(i1, bm.Height - j1 - 1, Color.FromArgb(60, (int)(235.0f - (percent * 2.15f)), 235)); + } + else + { + bm.SetPixel(i1, bm.Height - j1 - 1, Color.FromArgb(0, 0, 128)); + } + } + } + bm.SetPixel(bm.Width / 2, bm.Height / 2, Color.FromArgb(255, 0, 0)); + // bm.Save(WCCheck.WCCheck.Loc(lon, lat) + ".bmp"); + } + catch (Exception ex) + { + // do nothing + } + */ + // set elevation to 0 if data missing flag is set + if (e == elev_m_missing_flag) + return 0; + // return Elevation + Correction + return e; + } + } + set + { + lock (this) + { + if (lat < lower_map_y) + throw (new IndexOutOfRangeException()); + if (lat > upper_map_y) + throw (new IndexOutOfRangeException()); + if (lon < left_map_x) + throw (new IndexOutOfRangeException()); + if (lon > right_map_x) + throw (new IndexOutOfRangeException()); + int i = (int)((lon - left_map_x) / grid_size); + int j = (int)((lat - lower_map_y) / grid_size); + Data[i, j] = value; + } + } + } + + public static int GetSRTMElevation(string datapath, double lat, double lon) + { + int x, y; + byte[] a16 = new byte[2]; + try + { + // get the lat/lon base values for a single tile to load + int newlatint = (int)Math.Floor(lat); + int newlonint = (int)Math.Floor(lon); + // check if right tile is already loaded --> work with cached values only + if ((strmbr != null) && (newlatint == latbase) && (newlonint == lonbase)) + { + if (hgtexists) + { + // get file x/y dimensions (SRTM3: 1201x1201, SRTM1: 3601x3601) + x = xysize - (int)((lat - latbase) * xysize); + y = (int)((lon - lonbase) * xysize); + strmbr.BaseStream.Position = ((x - 1) * xysize + (y - 1)) * 2; + strmbr.Read(a16, 0, 2); + Array.Reverse(a16); + int e1 = BitConverter.ToInt16(a16, 0); + if (e1 == -32768) + e1 = elev_m_missing_flag; + return e1; + + } + else + return elev_m_missing_flag; + } + // tile does not match --> load new tile + // store new lat/lon for tile + latbase = newlatint; + lonbase = newlonint; + // calculate datafilename + string latstr = Math.Abs(latbase).ToString("00"); + if (lat > 0) + { + latstr = "N" + latstr; + } + else + { + latstr = "S" + latstr; + + } + string lonstr = Math.Abs(lonbase).ToString("000"); + if (lon > 0) + lonstr = "E" + lonstr; + else + lonstr = "W" + lonstr; + hgtfilename = Application.StartupPath + "\\" + datapath + "\\" + latstr + lonstr + ".hgt"; + if (!File.Exists(hgtfilename)) + { + hgtexists = false; + return elev_m_missing_flag; + } + hgtexists = true; + if (strmbr != null) + strmbr.Dispose(); + strmbr = new BinaryReader(File.OpenRead(hgtfilename)); + // get file x/y dimensions (SRTM3: 1201x1201, SRTM1: 3601x3601) + xysize = (int)Math.Sqrt(strmbr.BaseStream.Length / 2); + x = xysize - (int)((lat - latbase) * xysize); + y = (int)((lon - lonbase) * xysize); + strmbr.BaseStream.Position = ((x-1) * xysize + (y-1)) * 2; + strmbr.Read(a16, 0, 2); + Array.Reverse(a16); + int e = BitConverter.ToInt16(a16,0); + if (e == -32768) + e = elev_m_missing_flag; + return e; + } + catch + { + } + return elev_m_missing_flag; + } + + public int GetGLOBEElevation(string datapath, double lat, double lon) + { + int x, y; + byte[] a16 = new byte[2]; + try + { + // get the lat/lon base values for a single tile to load + GLOBETile tile = this.GLOBETiles.GetTileFromPoint(lat, lon); + if (tile == null) + return elev_m_missing_flag; // tile is missing --> no elevation data available + if ((globebr == null) || (tile != currenttile)) + { + globebr = new BinaryReader(File.OpenRead(tile.Name)); + currenttile = tile; + } + x = (int)((tile.MaxLat - lat) / tile.DivLat); + y = (int)((lon - tile.MinLon) / tile.DivLon); + long streampos = (x * tile.Columns + y) * 2; + globebr.BaseStream.Position = streampos; + globebr.Read(a16, 0, 2); + // Array.Reverse(a16); + int e1 = BitConverter.ToInt16(a16, 0); + if ((e1 == -32768) || (e1 == globe_elv_missing)) + e1 = elev_m_missing_flag; + if (e1 < tile.MinElv) + e1 = tile.MinElv; + if (e1 > tile.MaxElv) + e1 = tile.MaxElv; + return e1; + } + catch (Exception ex) + { + System.Console.WriteLine("[" + System.Reflection.MethodBase.GetCurrentMethod().Name + "]:" + ex.Message); + } + return elev_m_missing_flag; + } + } + + public class ElvMaxInfo + { + public int Elv; + public double Lat; + public double Lon; + + public ElvMaxInfo(int elv, double lat, double lon) + { + Elv = elv; + Lat = lat; + Lon = lon; + } + } + + public class DEMColorPalette + { + private Dictionary Base; + private Color[] Lookup; + + private int Count = 1000; + + public DEMColorPalette() + { + // fill initial palette + Base = new Dictionary(); + this.Base.Add(0.00, Color.FromArgb(0,97,71)); + this.Base.Add(1.02, Color.FromArgb(16,122,47)); + this.Base.Add(10.20, Color.FromArgb(232,215,125)); + this.Base.Add(24.49, Color.FromArgb(161,67,0)); + this.Base.Add(34.69, Color.FromArgb(158,0,0)); + this.Base.Add(57.14, Color.FromArgb(110,110,110)); + this.Base.Add(81.63, Color.FromArgb(255,255,255)); + this.Base.Add(100.00, Color.FromArgb(255,255,255)); + Lookup = new Color[Count+1]; + Bitmap bm = new Bitmap(Count+1, 100); + for (int i = 0; i <= Count; i++) + { + Color c = GetColorFromBase((double)i / (double)Count * 100.0); + this.Lookup[i] = c; + for (int j = 0; j < bm.Height; j++) + bm.SetPixel(i, j, c); + } + bm.Save("DEMPalette.bmp"); + } + + public Color GetColor(double percentage) + { + int i = (int)(percentage / 100.0 * Count); + try + { + return Lookup[i]; + } + catch + { + return Color.FromArgb(0,0,0); + } + } + + private Color GetColorFromBase(double percentage) + { + if ((percentage < 0) || (percentage > 100)) + return Color.FromArgb(0, 0, 0); + for ( int i = 0; i < this.Base.Count-1; i++) + { + KeyValuePair low = this.Base.ElementAt(i); + KeyValuePair high = this.Base.ElementAt(i+1); + if ((percentage >= low.Key) && (percentage <= high.Key)) + { + double p = (percentage-low.Key)/(high.Key-low.Key); + int red = low.Value.R + (int)(p * (high.Value.R - low.Value.R)); + int green = low.Value.G + (int)(p * (high.Value.G - low.Value.G)); + int blue = low.Value.B + (int)(p * (high.Value.B - low.Value.B)); + return Color.FromArgb(red, green, blue); + } + } + return Color.FromArgb(0, 0, 0); + } + } + + public class GLOBETileCollection : List + { + + public GLOBETileCollection(string path = "") + : base() + { + if (!String.IsNullOrEmpty(path)) + ScanDir(path); + } + + public void ScanDir(string path) + { + try + { + this.Clear(); + FileInfo[] GLOBEfiles = new DirectoryInfo(path).GetFiles("????.*"); + foreach (FileInfo file in GLOBEfiles) + { + GLOBETile tile = GetTileInfoFromFile(file.FullName); + if (tile != null) + this.Add(tile); + } + } + catch + { + } + } + + public static bool IsGLOBEFile(string filename) + { + // checks for a valid filename with/witout extension + // filename must have a 4.xxx notation + // [0]: 'A'..'P' + // [1],[2]: nn = version + // [3]: 'G','B','S','T' = status + // 'S' and 'T' are not used + if (String.IsNullOrEmpty(filename)) + return false; + string upperfilename = Path.GetFileNameWithoutExtension(filename).ToUpper(); + char index = upperfilename[0]; + char status = upperfilename[3]; + if (upperfilename.Length != 4) + return false; + if ((index < 'A') + || (index > 'P') + || !Char.IsDigit(upperfilename[1]) + || !Char.IsDigit(upperfilename[2]) + || ((status != 'G') && (status != 'B')) + ) + return false; + return true; + } + + public GLOBETile GetTileFromPoint(double lat, double lon) + { + foreach(GLOBETile tile in this) + { + if ((lat >= tile.MinLat) && + (lat <= tile.MaxLat) && + (lon >= tile.MinLon) && + (lon <= tile.MaxLon)) + return tile; + } + return null; + } + + public static GLOBETile GetTileInfoFromFile(string filename) + { + if (!IsGLOBEFile(filename)) + return null; + char index = Path.GetFileNameWithoutExtension(filename).ToUpper()[0]; + char status = Path.GetFileNameWithoutExtension(filename).ToUpper()[3]; + GLOBETile tile = null; + switch (index) + { + case 'A': + tile = new GLOBETile(filename, 'A', 50.0, 90.0, -180.0, -90.0, 1, 6098, 10800, 4800); + break; + case 'B': + tile = new GLOBETile(filename, 'B', 50.0, 90.0, -90.0, -0.0, 1, 3940, 10800, 4800); + break; + case 'C': + tile = new GLOBETile(filename, 'C', 50.0, 90.0, 0.0, 90.0, -30, 4010, 10800, 4800); + break; + case 'D': + tile = new GLOBETile(filename, 'D', 50.0, 90.0, 90.0, 180.0, 1, 4588, 10800, 4800); + break; + case 'E': + tile = new GLOBETile(filename, 'E', 0.0, 50.0, -180.0, -90.0, -84, 5443, 10800, 4800); + break; + case 'F': + tile = new GLOBETile(filename, 'F', 0.0, 50.0, -90.0, -0.0, -40, 6085, 10800, 4800); + break; + case 'G': + tile = new GLOBETile(filename, 'G', 0.0, 50.0, 0.0, 90.0, -407, 8752, 10800, 4800); + break; + case 'H': + tile = new GLOBETile(filename, 'H', 0.0, 50.0, 90.0, 180.0, -63, 7491, 10800, 4800); + break; + case 'I': + tile = new GLOBETile(filename, 'I', -50.0, 0.0, -180.0, -90.0, 1, 2732, 10800, 4800); + break; + case 'J': + tile = new GLOBETile(filename, 'J', -50.0, 0.0, -90.0, -0.0, -127, 6798, 10800, 4800); + break; + case 'K': + tile = new GLOBETile(filename, 'K', -50.0, 0.0, 0.0, 90.0, 1, 5825, 10800, 4800); + break; + case 'L': + tile = new GLOBETile(filename, 'L', -50.0, 0.0, 90.0, 180.0, 1, 5179, 10800, 4800); + break; + case 'M': + tile = new GLOBETile(filename, 'M', -90.0, -50.0, -180.0, -90.0, 1, 4009, 10800, 4800); + break; + case 'N': + tile = new GLOBETile(filename, 'N', -90.0, -50.0, -90.0, -0.0, 1, 4743, 10800, 4800); + break; + case 'O': + tile = new GLOBETile(filename, 'O', -90.0, -50.0, 0.0, 90.0, 1, 4039, 10800, 4800); + break; + case 'P': + tile = new GLOBETile(filename, 'P', -90.0, -50.0, 90.0, 180.0, 1, 4363, 10800, 4800); + break; + default: + return null; + } + try + { + int.TryParse(filename.Substring(1, 2), out tile.Version); + tile.Status = (GLOBETileStatus)Enum.ToObject(typeof(GLOBETileStatus), filename[3]); + } + catch + { + } + return tile; + } + } + + public enum GLOBETileStatus + { + UNDEFINED = 0, + BAD = 1, + GOOD = 2, + } + + public class GLOBETile + { + public string Name = ""; + public char Index; + public double MinLat = 0; + public double MaxLat = 0; + public double MinLon = 0; + public double MaxLon = 0; + public int MinElv = 0; + public int MaxElv = 0; + public double DivLat = 0; + public double DivLon = 0; + public int Columns = 0; + public int Rows = 0; + public int Version = 0; + public GLOBETileStatus Status; + + public GLOBETile(string name, char index, double minlat, double maxlat, double minlon, double maxlon, int minelv, int maxelv, int columns, int rows, int version = 0, GLOBETileStatus status = GLOBETileStatus.UNDEFINED) + { + Name = name; + Index = index; + MinLat = minlat; + MaxLat = maxlat; + MinLon = minlon; + MaxLon = maxlon; + MinElv = minelv; + MaxElv = maxelv; + Columns = columns; + Rows = rows; + Version = version; + Status = status; + if (Rows > 0) + DivLat = (MaxLat - MinLat) / (double)Rows; + if (Columns > 0) + DivLon = (MaxLon - MinLon) / (double)Columns; + } + } +} diff --git a/AirScoutDatabaseManager/AirScoutDatabaseManager.csproj b/AirScoutDatabaseManager/AirScoutDatabaseManager.csproj new file mode 100644 index 0000000..b9c64a4 --- /dev/null +++ b/AirScoutDatabaseManager/AirScoutDatabaseManager.csproj @@ -0,0 +1,206 @@ + + + + Debug + x86 + 8.0.30703 + 2.0 + {00062ABB-C482-4B06-BD1B-C49DE9ABA9E5} + WinExe + Properties + AirScoutDatabaseManager + AirScoutDatabaseManager + v4.0 + Client + 512 + + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + x86 + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + x86 + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + bin\Debug\ + DEBUG;TRACE + full + AnyCPU + prompt + MinimumRecommendedRules.ruleset + + + bin\Release\ + TRACE + true + pdbonly + AnyCPU + prompt + MinimumRecommendedRules.ruleset + + + + ..\packages\HtmlAgilityPack.1.8.4\lib\Net40-client\HtmlAgilityPack.dll + True + + + ..\packages\Newtonsoft.Json.12.0.1\lib\net40\Newtonsoft.Json.dll + True + + + ..\packages\SSH.NET.2016.1.0\lib\net40\Renci.SshNet.dll + True + + + + + ..\packages\System.Data.SQLite.Core.1.0.110.0\lib\net40\System.Data.SQLite.dll + True + + + + + + + + + + + + + Form + + + MainDlg.cs + + + + + True + True + Settings.settings + + + + Form + + + MainDlg.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + + + + + PublicSettingsSingleFileGenerator + Settings.Designer.cs + + + + + {288a26ec-b690-41a2-84e5-61c9b7b74046} + AirScout.Aircrafts + + + {ee86e933-d883-4b18-80eb-0fba55ec67c6} + ScoutBase.Core + + + {009cabfd-726d-481f-972d-0a218e0ad9b9} + ScoutBase.Elevation + + + {ea78ad40-1505-406f-8049-744e58d93f54} + AirScout.PlaneFeeds + + + {d0c39d9d-bed0-418b-9a5e-713176caf40c} + GMap.NET.Core + + + {e06def77-f933-42fb-afd7-db2d0d8d6a98} + GMap.NET.WindowsForms + + + {358e87d7-849f-412a-b487-f7b7d585a7f9} + ScoutBase.Stations + + + {6056d3be-7002-4a6a-a9ea-6ff45122a3c7} + SQLiteDatabase + + + {7b815c51-6896-4989-bd1b-8d2d7a116aa3} + WinTest + + + + + False + Microsoft .NET Framework 4 Client Profile %28x86 und x64%29 + true + + + False + .NET Framework 3.5 SP1 + false + + + False + Windows Installer 4.5 + true + + + + + + + Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}". + + + + + \ No newline at end of file diff --git a/AirScoutDatabaseManager/MainDlg.Designer.cs b/AirScoutDatabaseManager/MainDlg.Designer.cs new file mode 100644 index 0000000..c5def3d --- /dev/null +++ b/AirScoutDatabaseManager/MainDlg.Designer.cs @@ -0,0 +1,1423 @@ +namespace AirScoutDatabaseManager +{ + partial class MainDlg + { + /// + /// Erforderliche Designervariable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Verwendete Ressourcen bereinigen. + /// + /// True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Vom Windows Form-Designer generierter Code + + /// + /// Erforderliche Methode für die Designerunterstützung. + /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden. + /// + private void InitializeComponent() + { + this.ss_Main = new System.Windows.Forms.StatusStrip(); + this.tsl_Status = new System.Windows.Forms.ToolStripStatusLabel(); + this.tc_Main = new System.Windows.Forms.TabControl(); + this.tp_General = new System.Windows.Forms.TabPage(); + this.gm_Coverage = new GMap.NET.WindowsForms.GMapControl(); + this.panel1 = new System.Windows.Forms.Panel(); + this.label35 = new System.Windows.Forms.Label(); + this.label54 = new System.Windows.Forms.Label(); + this.label59 = new System.Windows.Forms.Label(); + this.label60 = new System.Windows.Forms.Label(); + this.tp_Locations = new System.Windows.Forms.TabPage(); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.dgv_Locations = new System.Windows.Forms.DataGridView(); + this.gm_Locations = new GMap.NET.WindowsForms.GMapControl(); + this.panel2 = new System.Windows.Forms.Panel(); + this.label2 = new System.Windows.Forms.Label(); + this.label1 = new System.Windows.Forms.Label(); + this.tb_Locations_Callsign_Filter = new System.Windows.Forms.TextBox(); + this.cb_Locations_ChangedOnly = new System.Windows.Forms.CheckBox(); + this.btn_Locations_Sort = new System.Windows.Forms.Button(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.btn_Locations_Import_USR = new System.Windows.Forms.Button(); + this.btn_Locations_Import_CSV = new System.Windows.Forms.Button(); + this.btn_Locations_Import_DTB = new System.Windows.Forms.Button(); + this.btn_Locations_Import_CALL3 = new System.Windows.Forms.Button(); + this.btn_Locations_Import_AirScout = new System.Windows.Forms.Button(); + this.btn_Locations_Export = new System.Windows.Forms.Button(); + this.btn_Locations_Save = new System.Windows.Forms.Button(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.btn_QRZ_Stop = new System.Windows.Forms.Button(); + this.btn_QRZ_Start = new System.Windows.Forms.Button(); + this.tb_Locations_Status = new System.Windows.Forms.TextBox(); + this.tp_QRV = new System.Windows.Forms.TabPage(); + this.dgv_QRV = new System.Windows.Forms.DataGridView(); + this.panel4 = new System.Windows.Forms.Panel(); + this.cb_QRV_ChangedOnly = new System.Windows.Forms.CheckBox(); + this.btn_QRV_Sort = new System.Windows.Forms.Button(); + this.btn_QRV_Export = new System.Windows.Forms.Button(); + this.btn_QRV_Save = new System.Windows.Forms.Button(); + this.label4 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.tb_QRV_Callsign_Filter = new System.Windows.Forms.TextBox(); + this.tb_QRV_Status = new System.Windows.Forms.TextBox(); + this.groupBox3 = new System.Windows.Forms.GroupBox(); + this.btn_QRV_Import_EDI = new System.Windows.Forms.Button(); + this.btn_QRV_Import_WinTest = new System.Windows.Forms.Button(); + this.tp_Aircrafts = new System.Windows.Forms.TabPage(); + this.lbl_Aircrafts_UnkownHex = new System.Windows.Forms.Label(); + this.lbl_Aircrafts_UnkownType = new System.Windows.Forms.Label(); + this.lbl_Aircrafts_UnkownCall = new System.Windows.Forms.Label(); + this.lbl_Aircrafts_Total = new System.Windows.Forms.Label(); + this.label8 = new System.Windows.Forms.Label(); + this.button1 = new System.Windows.Forms.Button(); + this.btn_Update_Aircrafts_Stop = new System.Windows.Forms.Button(); + this.btn_Update_Aicrafts_Start = new System.Windows.Forms.Button(); + this.btn_Update_Airports = new System.Windows.Forms.Button(); + this.btn_Update_Airlines = new System.Windows.Forms.Button(); + this.tp_Upload = new System.Windows.Forms.TabPage(); + this.groupBox5 = new System.Windows.Forms.GroupBox(); + this.label13 = new System.Windows.Forms.Label(); + this.label12 = new System.Windows.Forms.Label(); + this.label11 = new System.Windows.Forms.Label(); + this.btn_StationDatabase_Export = new System.Windows.Forms.Button(); + this.label10 = new System.Windows.Forms.Label(); + this.label9 = new System.Windows.Forms.Label(); + this.tp_Extras = new System.Windows.Forms.TabPage(); + this.groupBox4 = new System.Windows.Forms.GroupBox(); + this.btn_SFTP_GenarateFile = new System.Windows.Forms.Button(); + this.label7 = new System.Windows.Forms.Label(); + this.label6 = new System.Windows.Forms.Label(); + this.label5 = new System.Windows.Forms.Label(); + this.bw_QRZ = new System.ComponentModel.BackgroundWorker(); + this.bw_DatabaseUpdater = new System.ComponentModel.BackgroundWorker(); + this.bw_AircraftUpdater = new System.ComponentModel.BackgroundWorker(); + this.groupBox6 = new System.Windows.Forms.GroupBox(); + this.label14 = new System.Windows.Forms.Label(); + this.label15 = new System.Windows.Forms.Label(); + this.label16 = new System.Windows.Forms.Label(); + this.btn_AircraftDatabase_Export = new System.Windows.Forms.Button(); + this.label17 = new System.Windows.Forms.Label(); + this.label18 = new System.Windows.Forms.Label(); + this.tb_Coverage_MaxLat = new ScoutBase.Core.DoubleTextBox(); + this.tb_Coverage_MinLat = new ScoutBase.Core.DoubleTextBox(); + this.tb_Coverage_MaxLon = new ScoutBase.Core.DoubleTextBox(); + this.tb_Coverage_MinLon = new ScoutBase.Core.DoubleTextBox(); + this.tb_Update_Aircrafts = new System.Windows.Forms.TextBox(); + this.tb_Update_Airports = new System.Windows.Forms.TextBox(); + this.tb_Update_Airlines = new System.Windows.Forms.TextBox(); + this.tb_AircraftDatabase_RemoteHost = new System.Windows.Forms.TextBox(); + this.tb_AircraftDatabase_Password = new System.Windows.Forms.TextBox(); + this.tb_AircraftDatabase_User = new System.Windows.Forms.TextBox(); + this.tb_AircraftDatabase_RemoteDir = new System.Windows.Forms.TextBox(); + this.tb_AircraftDabase_LocalDir = new System.Windows.Forms.TextBox(); + this.tb_StationDatabase_RemoteHost = new System.Windows.Forms.TextBox(); + this.tb_StationDatabase_Export_Password = new System.Windows.Forms.TextBox(); + this.tb_StationDatabase_Export_User = new System.Windows.Forms.TextBox(); + this.tb_StationDatabase_RemoteDir = new System.Windows.Forms.TextBox(); + this.tb_StationDatabase_LocalDir = new System.Windows.Forms.TextBox(); + this.tb_SFTP_Password = new System.Windows.Forms.TextBox(); + this.tb_SFTP_User = new System.Windows.Forms.TextBox(); + this.tb_SFTP_URL = new System.Windows.Forms.TextBox(); + this.ss_Main.SuspendLayout(); + this.tc_Main.SuspendLayout(); + this.tp_General.SuspendLayout(); + this.panel1.SuspendLayout(); + this.tp_Locations.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.dgv_Locations)).BeginInit(); + this.panel2.SuspendLayout(); + this.groupBox2.SuspendLayout(); + this.groupBox1.SuspendLayout(); + this.tp_QRV.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.dgv_QRV)).BeginInit(); + this.panel4.SuspendLayout(); + this.groupBox3.SuspendLayout(); + this.tp_Aircrafts.SuspendLayout(); + this.tp_Upload.SuspendLayout(); + this.groupBox5.SuspendLayout(); + this.tp_Extras.SuspendLayout(); + this.groupBox4.SuspendLayout(); + this.groupBox6.SuspendLayout(); + this.SuspendLayout(); + // + // ss_Main + // + this.ss_Main.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.tsl_Status}); + this.ss_Main.Location = new System.Drawing.Point(0, 575); + this.ss_Main.Name = "ss_Main"; + this.ss_Main.Size = new System.Drawing.Size(1106, 22); + this.ss_Main.TabIndex = 0; + this.ss_Main.Text = "statusStrip1"; + // + // tsl_Status + // + this.tsl_Status.Name = "tsl_Status"; + this.tsl_Status.Size = new System.Drawing.Size(39, 17); + this.tsl_Status.Text = "Status"; + // + // tc_Main + // + this.tc_Main.Controls.Add(this.tp_General); + this.tc_Main.Controls.Add(this.tp_Locations); + this.tc_Main.Controls.Add(this.tp_QRV); + this.tc_Main.Controls.Add(this.tp_Aircrafts); + this.tc_Main.Controls.Add(this.tp_Upload); + this.tc_Main.Controls.Add(this.tp_Extras); + this.tc_Main.Dock = System.Windows.Forms.DockStyle.Fill; + this.tc_Main.Location = new System.Drawing.Point(0, 0); + this.tc_Main.Name = "tc_Main"; + this.tc_Main.SelectedIndex = 0; + this.tc_Main.Size = new System.Drawing.Size(1106, 575); + this.tc_Main.TabIndex = 1; + // + // tp_General + // + this.tp_General.BackColor = System.Drawing.SystemColors.Control; + this.tp_General.Controls.Add(this.gm_Coverage); + this.tp_General.Controls.Add(this.panel1); + this.tp_General.Location = new System.Drawing.Point(4, 22); + this.tp_General.Name = "tp_General"; + this.tp_General.Padding = new System.Windows.Forms.Padding(3); + this.tp_General.Size = new System.Drawing.Size(1098, 549); + this.tp_General.TabIndex = 0; + this.tp_General.Text = "General"; + this.tp_General.Enter += new System.EventHandler(this.tp_General_Enter); + // + // gm_Coverage + // + this.gm_Coverage.Bearing = 0F; + this.gm_Coverage.CanDragMap = true; + this.gm_Coverage.Dock = System.Windows.Forms.DockStyle.Fill; + this.gm_Coverage.EmptyTileColor = System.Drawing.Color.Navy; + this.gm_Coverage.GrayScaleMode = false; + this.gm_Coverage.HelperLineOption = GMap.NET.WindowsForms.HelperLineOptions.DontShow; + this.gm_Coverage.LevelsKeepInMemmory = 5; + this.gm_Coverage.Location = new System.Drawing.Point(3, 3); + this.gm_Coverage.MarkersEnabled = true; + this.gm_Coverage.MaxZoom = 2; + this.gm_Coverage.MinZoom = 2; + this.gm_Coverage.MouseWheelZoomType = GMap.NET.MouseWheelZoomType.MousePositionAndCenter; + this.gm_Coverage.Name = "gm_Coverage"; + this.gm_Coverage.NegativeMode = false; + this.gm_Coverage.PolygonsEnabled = true; + this.gm_Coverage.RetryLoadTile = 0; + this.gm_Coverage.RoutesEnabled = true; + this.gm_Coverage.SelectedAreaFillColor = System.Drawing.Color.FromArgb(((int)(((byte)(33)))), ((int)(((byte)(65)))), ((int)(((byte)(105)))), ((int)(((byte)(225))))); + this.gm_Coverage.ShowTileGridLines = false; + this.gm_Coverage.Size = new System.Drawing.Size(917, 543); + this.gm_Coverage.TabIndex = 1; + this.gm_Coverage.Zoom = 0D; + // + // panel1 + // + this.panel1.Controls.Add(this.tb_Coverage_MaxLat); + this.panel1.Controls.Add(this.tb_Coverage_MinLat); + this.panel1.Controls.Add(this.tb_Coverage_MaxLon); + this.panel1.Controls.Add(this.tb_Coverage_MinLon); + this.panel1.Controls.Add(this.label35); + this.panel1.Controls.Add(this.label54); + this.panel1.Controls.Add(this.label59); + this.panel1.Controls.Add(this.label60); + this.panel1.Dock = System.Windows.Forms.DockStyle.Right; + this.panel1.Location = new System.Drawing.Point(920, 3); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(175, 543); + this.panel1.TabIndex = 0; + // + // label35 + // + this.label35.AutoSize = true; + this.label35.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label35.Location = new System.Drawing.Point(19, 109); + this.label35.Name = "label35"; + this.label35.Size = new System.Drawing.Size(74, 13); + this.label35.TabIndex = 32; + this.label35.Text = "Max. Latitude:"; + // + // label54 + // + this.label54.AutoSize = true; + this.label54.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label54.Location = new System.Drawing.Point(19, 83); + this.label54.Name = "label54"; + this.label54.Size = new System.Drawing.Size(71, 13); + this.label54.TabIndex = 31; + this.label54.Text = "Min. Latitude:"; + // + // label59 + // + this.label59.AutoSize = true; + this.label59.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label59.Location = new System.Drawing.Point(19, 56); + this.label59.Name = "label59"; + this.label59.Size = new System.Drawing.Size(83, 13); + this.label59.TabIndex = 30; + this.label59.Text = "Max. Longitude:"; + // + // label60 + // + this.label60.AutoSize = true; + this.label60.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label60.Location = new System.Drawing.Point(19, 30); + this.label60.Name = "label60"; + this.label60.Size = new System.Drawing.Size(80, 13); + this.label60.TabIndex = 29; + this.label60.Text = "Min. Longitude:"; + // + // tp_Locations + // + this.tp_Locations.BackColor = System.Drawing.SystemColors.Control; + this.tp_Locations.Controls.Add(this.splitContainer1); + this.tp_Locations.Controls.Add(this.panel2); + this.tp_Locations.Location = new System.Drawing.Point(4, 22); + this.tp_Locations.Name = "tp_Locations"; + this.tp_Locations.Padding = new System.Windows.Forms.Padding(3); + this.tp_Locations.Size = new System.Drawing.Size(1098, 549); + this.tp_Locations.TabIndex = 1; + this.tp_Locations.Text = "Locations"; + this.tp_Locations.Enter += new System.EventHandler(this.tp_Locations_Enter); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(3, 3); + this.splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.Controls.Add(this.dgv_Locations); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.gm_Locations); + this.splitContainer1.Size = new System.Drawing.Size(1092, 414); + this.splitContainer1.SplitterDistance = 363; + this.splitContainer1.TabIndex = 3; + // + // dgv_Locations + // + this.dgv_Locations.AllowUserToResizeRows = false; + this.dgv_Locations.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.dgv_Locations.Dock = System.Windows.Forms.DockStyle.Fill; + this.dgv_Locations.Location = new System.Drawing.Point(0, 0); + this.dgv_Locations.Name = "dgv_Locations"; + this.dgv_Locations.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; + this.dgv_Locations.Size = new System.Drawing.Size(363, 414); + this.dgv_Locations.TabIndex = 1; + this.dgv_Locations.CellFormatting += new System.Windows.Forms.DataGridViewCellFormattingEventHandler(this.dgv_Locations_CellFormatting); + this.dgv_Locations.SelectionChanged += new System.EventHandler(this.dgv_Locations_SelectionChanged); + // + // gm_Locations + // + this.gm_Locations.Bearing = 0F; + this.gm_Locations.CanDragMap = true; + this.gm_Locations.Dock = System.Windows.Forms.DockStyle.Fill; + this.gm_Locations.EmptyTileColor = System.Drawing.Color.Navy; + this.gm_Locations.GrayScaleMode = false; + this.gm_Locations.HelperLineOption = GMap.NET.WindowsForms.HelperLineOptions.DontShow; + this.gm_Locations.LevelsKeepInMemmory = 5; + this.gm_Locations.Location = new System.Drawing.Point(0, 0); + this.gm_Locations.MarkersEnabled = true; + this.gm_Locations.MaxZoom = 2; + this.gm_Locations.MinZoom = 2; + this.gm_Locations.MouseWheelZoomType = GMap.NET.MouseWheelZoomType.MousePositionAndCenter; + this.gm_Locations.Name = "gm_Locations"; + this.gm_Locations.NegativeMode = false; + this.gm_Locations.PolygonsEnabled = true; + this.gm_Locations.RetryLoadTile = 0; + this.gm_Locations.RoutesEnabled = true; + this.gm_Locations.SelectedAreaFillColor = System.Drawing.Color.FromArgb(((int)(((byte)(33)))), ((int)(((byte)(65)))), ((int)(((byte)(105)))), ((int)(((byte)(225))))); + this.gm_Locations.ShowTileGridLines = false; + this.gm_Locations.Size = new System.Drawing.Size(725, 414); + this.gm_Locations.TabIndex = 2; + this.gm_Locations.Zoom = 0D; + this.gm_Locations.OnMarkerEnter += new GMap.NET.WindowsForms.MarkerEnter(this.gm_Locations_OnMarkerEnter); + this.gm_Locations.OnMarkerLeave += new GMap.NET.WindowsForms.MarkerLeave(this.gm_Locations_OnMarkerLeave); + this.gm_Locations.MouseDown += new System.Windows.Forms.MouseEventHandler(this.gm_Locations_MouseDown); + this.gm_Locations.MouseMove += new System.Windows.Forms.MouseEventHandler(this.gm_Locations_MouseMove); + this.gm_Locations.MouseUp += new System.Windows.Forms.MouseEventHandler(this.gm_Locations_MouseUp); + // + // panel2 + // + this.panel2.Controls.Add(this.label2); + this.panel2.Controls.Add(this.label1); + this.panel2.Controls.Add(this.tb_Locations_Callsign_Filter); + this.panel2.Controls.Add(this.cb_Locations_ChangedOnly); + this.panel2.Controls.Add(this.btn_Locations_Sort); + this.panel2.Controls.Add(this.groupBox2); + this.panel2.Controls.Add(this.btn_Locations_Export); + this.panel2.Controls.Add(this.btn_Locations_Save); + this.panel2.Controls.Add(this.groupBox1); + this.panel2.Controls.Add(this.tb_Locations_Status); + this.panel2.Dock = System.Windows.Forms.DockStyle.Bottom; + this.panel2.Location = new System.Drawing.Point(3, 417); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(1092, 129); + this.panel2.TabIndex = 0; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(668, 12); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(68, 13); + this.label2.TabIndex = 10; + this.label2.Text = "Callsign Filter"; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(17, 12); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(37, 13); + this.label1.TabIndex = 9; + this.label1.Text = "Status"; + // + // tb_Locations_Callsign_Filter + // + this.tb_Locations_Callsign_Filter.CharacterCasing = System.Windows.Forms.CharacterCasing.Upper; + this.tb_Locations_Callsign_Filter.Location = new System.Drawing.Point(671, 36); + this.tb_Locations_Callsign_Filter.Name = "tb_Locations_Callsign_Filter"; + this.tb_Locations_Callsign_Filter.Size = new System.Drawing.Size(100, 20); + this.tb_Locations_Callsign_Filter.TabIndex = 8; + this.tb_Locations_Callsign_Filter.TextChanged += new System.EventHandler(this.tb_Locations_Callsign_Filter_TextChanged); + // + // cb_Locations_ChangedOnly + // + this.cb_Locations_ChangedOnly.AutoSize = true; + this.cb_Locations_ChangedOnly.Location = new System.Drawing.Point(797, 38); + this.cb_Locations_ChangedOnly.Name = "cb_Locations_ChangedOnly"; + this.cb_Locations_ChangedOnly.Size = new System.Drawing.Size(145, 17); + this.cb_Locations_ChangedOnly.TabIndex = 7; + this.cb_Locations_ChangedOnly.Text = "Show changed rows only"; + this.cb_Locations_ChangedOnly.UseVisualStyleBackColor = true; + this.cb_Locations_ChangedOnly.CheckedChanged += new System.EventHandler(this.cb_Locations_ChangedOnly_CheckedChanged); + // + // btn_Locations_Sort + // + this.btn_Locations_Sort.Location = new System.Drawing.Point(735, 94); + this.btn_Locations_Sort.Name = "btn_Locations_Sort"; + this.btn_Locations_Sort.Size = new System.Drawing.Size(75, 23); + this.btn_Locations_Sort.TabIndex = 6; + this.btn_Locations_Sort.Text = "Sort"; + this.btn_Locations_Sort.UseVisualStyleBackColor = true; + this.btn_Locations_Sort.Click += new System.EventHandler(this.btn_Locations_Sort_Click); + // + // groupBox2 + // + this.groupBox2.Controls.Add(this.btn_Locations_Import_USR); + this.groupBox2.Controls.Add(this.btn_Locations_Import_CSV); + this.groupBox2.Controls.Add(this.btn_Locations_Import_DTB); + this.groupBox2.Controls.Add(this.btn_Locations_Import_CALL3); + this.groupBox2.Controls.Add(this.btn_Locations_Import_AirScout); + this.groupBox2.Location = new System.Drawing.Point(188, 68); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Size = new System.Drawing.Size(531, 55); + this.groupBox2.TabIndex = 5; + this.groupBox2.TabStop = false; + this.groupBox2.Text = "Import"; + // + // btn_Locations_Import_USR + // + this.btn_Locations_Import_USR.Location = new System.Drawing.Point(113, 26); + this.btn_Locations_Import_USR.Name = "btn_Locations_Import_USR"; + this.btn_Locations_Import_USR.Size = new System.Drawing.Size(92, 23); + this.btn_Locations_Import_USR.TabIndex = 4; + this.btn_Locations_Import_USR.Text = "AirScout (USR)"; + this.btn_Locations_Import_USR.UseVisualStyleBackColor = true; + this.btn_Locations_Import_USR.Click += new System.EventHandler(this.btn_Locations_Import_USR_Click); + // + // btn_Locations_Import_CSV + // + this.btn_Locations_Import_CSV.Location = new System.Drawing.Point(432, 26); + this.btn_Locations_Import_CSV.Name = "btn_Locations_Import_CSV"; + this.btn_Locations_Import_CSV.Size = new System.Drawing.Size(93, 23); + this.btn_Locations_Import_CSV.TabIndex = 3; + this.btn_Locations_Import_CSV.Text = "LOC (CSV)"; + this.btn_Locations_Import_CSV.UseVisualStyleBackColor = true; + this.btn_Locations_Import_CSV.Click += new System.EventHandler(this.btn_Locations_Import_CSV_Click); + // + // btn_Locations_Import_DTB + // + this.btn_Locations_Import_DTB.Location = new System.Drawing.Point(333, 26); + this.btn_Locations_Import_DTB.Name = "btn_Locations_Import_DTB"; + this.btn_Locations_Import_DTB.Size = new System.Drawing.Size(93, 23); + this.btn_Locations_Import_DTB.TabIndex = 2; + this.btn_Locations_Import_DTB.Text = "DTB (Win-Test)"; + this.btn_Locations_Import_DTB.UseVisualStyleBackColor = true; + this.btn_Locations_Import_DTB.Click += new System.EventHandler(this.btn_Locations_Import_DTB_Click); + // + // btn_Locations_Import_CALL3 + // + this.btn_Locations_Import_CALL3.Location = new System.Drawing.Point(239, 26); + this.btn_Locations_Import_CALL3.Name = "btn_Locations_Import_CALL3"; + this.btn_Locations_Import_CALL3.Size = new System.Drawing.Size(88, 23); + this.btn_Locations_Import_CALL3.TabIndex = 1; + this.btn_Locations_Import_CALL3.Text = "CALL3 (WSJT)"; + this.btn_Locations_Import_CALL3.UseVisualStyleBackColor = true; + this.btn_Locations_Import_CALL3.Click += new System.EventHandler(this.btn_Locations_Import_CALL3_Click); + // + // btn_Locations_Import_AirScout + // + this.btn_Locations_Import_AirScout.Location = new System.Drawing.Point(15, 26); + this.btn_Locations_Import_AirScout.Name = "btn_Locations_Import_AirScout"; + this.btn_Locations_Import_AirScout.Size = new System.Drawing.Size(92, 23); + this.btn_Locations_Import_AirScout.TabIndex = 0; + this.btn_Locations_Import_AirScout.Text = "AirScout (TXT)"; + this.btn_Locations_Import_AirScout.UseVisualStyleBackColor = true; + this.btn_Locations_Import_AirScout.Click += new System.EventHandler(this.btn_Locations_Import_AirScout_Click); + // + // btn_Locations_Export + // + this.btn_Locations_Export.Location = new System.Drawing.Point(897, 94); + this.btn_Locations_Export.Name = "btn_Locations_Export"; + this.btn_Locations_Export.Size = new System.Drawing.Size(75, 23); + this.btn_Locations_Export.TabIndex = 4; + this.btn_Locations_Export.Text = "Export"; + this.btn_Locations_Export.UseVisualStyleBackColor = true; + this.btn_Locations_Export.Click += new System.EventHandler(this.btn_Locations_Export_Click); + // + // btn_Locations_Save + // + this.btn_Locations_Save.Location = new System.Drawing.Point(816, 94); + this.btn_Locations_Save.Name = "btn_Locations_Save"; + this.btn_Locations_Save.Size = new System.Drawing.Size(75, 23); + this.btn_Locations_Save.TabIndex = 3; + this.btn_Locations_Save.Text = "Save"; + this.btn_Locations_Save.UseVisualStyleBackColor = true; + this.btn_Locations_Save.Click += new System.EventHandler(this.btn_Locations_Save_Click); + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.btn_QRZ_Stop); + this.groupBox1.Controls.Add(this.btn_QRZ_Start); + this.groupBox1.Location = new System.Drawing.Point(5, 68); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(177, 55); + this.groupBox1.TabIndex = 0; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "QRZ.COM Update"; + // + // btn_QRZ_Stop + // + this.btn_QRZ_Stop.Location = new System.Drawing.Point(96, 26); + this.btn_QRZ_Stop.Name = "btn_QRZ_Stop"; + this.btn_QRZ_Stop.Size = new System.Drawing.Size(75, 23); + this.btn_QRZ_Stop.TabIndex = 1; + this.btn_QRZ_Stop.Text = "Stop"; + this.btn_QRZ_Stop.UseVisualStyleBackColor = true; + this.btn_QRZ_Stop.Click += new System.EventHandler(this.btn_QRZ_Stop_Click); + // + // btn_QRZ_Start + // + this.btn_QRZ_Start.Location = new System.Drawing.Point(15, 26); + this.btn_QRZ_Start.Name = "btn_QRZ_Start"; + this.btn_QRZ_Start.Size = new System.Drawing.Size(75, 23); + this.btn_QRZ_Start.TabIndex = 0; + this.btn_QRZ_Start.Text = "Start"; + this.btn_QRZ_Start.UseVisualStyleBackColor = true; + this.btn_QRZ_Start.Click += new System.EventHandler(this.btn_QRZ_Start_Click); + // + // tb_Locations_Status + // + this.tb_Locations_Status.Location = new System.Drawing.Point(20, 35); + this.tb_Locations_Status.Name = "tb_Locations_Status"; + this.tb_Locations_Status.ReadOnly = true; + this.tb_Locations_Status.Size = new System.Drawing.Size(608, 20); + this.tb_Locations_Status.TabIndex = 2; + // + // tp_QRV + // + this.tp_QRV.Controls.Add(this.dgv_QRV); + this.tp_QRV.Controls.Add(this.panel4); + this.tp_QRV.Location = new System.Drawing.Point(4, 22); + this.tp_QRV.Name = "tp_QRV"; + this.tp_QRV.Size = new System.Drawing.Size(1098, 549); + this.tp_QRV.TabIndex = 2; + this.tp_QRV.Text = "QRV"; + this.tp_QRV.UseVisualStyleBackColor = true; + this.tp_QRV.Enter += new System.EventHandler(this.tp_QRV_Enter); + // + // dgv_QRV + // + this.dgv_QRV.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.dgv_QRV.Dock = System.Windows.Forms.DockStyle.Fill; + this.dgv_QRV.Location = new System.Drawing.Point(0, 0); + this.dgv_QRV.Name = "dgv_QRV"; + this.dgv_QRV.Size = new System.Drawing.Size(1098, 404); + this.dgv_QRV.TabIndex = 2; + this.dgv_QRV.CellEndEdit += new System.Windows.Forms.DataGridViewCellEventHandler(this.dgv_QRV_CellEndEdit); + this.dgv_QRV.CellFormatting += new System.Windows.Forms.DataGridViewCellFormattingEventHandler(this.dgv_QRV_CellFormatting); + this.dgv_QRV.CellValueChanged += new System.Windows.Forms.DataGridViewCellEventHandler(this.dgv_QRV_CellValueChanged); + // + // panel4 + // + this.panel4.BackColor = System.Drawing.SystemColors.ControlLight; + this.panel4.Controls.Add(this.cb_QRV_ChangedOnly); + this.panel4.Controls.Add(this.btn_QRV_Sort); + this.panel4.Controls.Add(this.btn_QRV_Export); + this.panel4.Controls.Add(this.btn_QRV_Save); + this.panel4.Controls.Add(this.label4); + this.panel4.Controls.Add(this.label3); + this.panel4.Controls.Add(this.tb_QRV_Callsign_Filter); + this.panel4.Controls.Add(this.tb_QRV_Status); + this.panel4.Controls.Add(this.groupBox3); + this.panel4.Dock = System.Windows.Forms.DockStyle.Bottom; + this.panel4.Location = new System.Drawing.Point(0, 404); + this.panel4.Name = "panel4"; + this.panel4.Size = new System.Drawing.Size(1098, 145); + this.panel4.TabIndex = 1; + // + // cb_QRV_ChangedOnly + // + this.cb_QRV_ChangedOnly.AutoSize = true; + this.cb_QRV_ChangedOnly.Location = new System.Drawing.Point(799, 38); + this.cb_QRV_ChangedOnly.Name = "cb_QRV_ChangedOnly"; + this.cb_QRV_ChangedOnly.Size = new System.Drawing.Size(145, 17); + this.cb_QRV_ChangedOnly.TabIndex = 10; + this.cb_QRV_ChangedOnly.Text = "Show changed rows only"; + this.cb_QRV_ChangedOnly.UseVisualStyleBackColor = true; + this.cb_QRV_ChangedOnly.CheckedChanged += new System.EventHandler(this.cb_QRV_ChangedOnly_CheckedChanged); + // + // btn_QRV_Sort + // + this.btn_QRV_Sort.Location = new System.Drawing.Point(707, 99); + this.btn_QRV_Sort.Name = "btn_QRV_Sort"; + this.btn_QRV_Sort.Size = new System.Drawing.Size(75, 23); + this.btn_QRV_Sort.TabIndex = 9; + this.btn_QRV_Sort.Text = "Sort"; + this.btn_QRV_Sort.UseVisualStyleBackColor = true; + this.btn_QRV_Sort.Click += new System.EventHandler(this.btn_QRV_Sort_Click); + // + // btn_QRV_Export + // + this.btn_QRV_Export.Location = new System.Drawing.Point(869, 99); + this.btn_QRV_Export.Name = "btn_QRV_Export"; + this.btn_QRV_Export.Size = new System.Drawing.Size(75, 23); + this.btn_QRV_Export.TabIndex = 8; + this.btn_QRV_Export.Text = "Export"; + this.btn_QRV_Export.UseVisualStyleBackColor = true; + this.btn_QRV_Export.Click += new System.EventHandler(this.btn_QRV_Export_Click); + // + // btn_QRV_Save + // + this.btn_QRV_Save.Location = new System.Drawing.Point(788, 99); + this.btn_QRV_Save.Name = "btn_QRV_Save"; + this.btn_QRV_Save.Size = new System.Drawing.Size(75, 23); + this.btn_QRV_Save.TabIndex = 7; + this.btn_QRV_Save.Text = "Save"; + this.btn_QRV_Save.UseVisualStyleBackColor = true; + this.btn_QRV_Save.Click += new System.EventHandler(this.btn_QRV_Save_Click); + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(628, 12); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(68, 13); + this.label4.TabIndex = 4; + this.label4.Text = "Callsign Filter"; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(11, 12); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(37, 13); + this.label3.TabIndex = 3; + this.label3.Text = "Status"; + // + // tb_QRV_Callsign_Filter + // + this.tb_QRV_Callsign_Filter.CharacterCasing = System.Windows.Forms.CharacterCasing.Upper; + this.tb_QRV_Callsign_Filter.Location = new System.Drawing.Point(631, 38); + this.tb_QRV_Callsign_Filter.Name = "tb_QRV_Callsign_Filter"; + this.tb_QRV_Callsign_Filter.Size = new System.Drawing.Size(100, 20); + this.tb_QRV_Callsign_Filter.TabIndex = 2; + this.tb_QRV_Callsign_Filter.TextChanged += new System.EventHandler(this.tb_QRV_Callsign_Filter_TextChanged); + // + // tb_QRV_Status + // + this.tb_QRV_Status.Location = new System.Drawing.Point(14, 38); + this.tb_QRV_Status.Name = "tb_QRV_Status"; + this.tb_QRV_Status.ReadOnly = true; + this.tb_QRV_Status.Size = new System.Drawing.Size(485, 20); + this.tb_QRV_Status.TabIndex = 1; + // + // groupBox3 + // + this.groupBox3.Controls.Add(this.btn_QRV_Import_EDI); + this.groupBox3.Controls.Add(this.btn_QRV_Import_WinTest); + this.groupBox3.Location = new System.Drawing.Point(8, 76); + this.groupBox3.Name = "groupBox3"; + this.groupBox3.Size = new System.Drawing.Size(284, 52); + this.groupBox3.TabIndex = 0; + this.groupBox3.TabStop = false; + this.groupBox3.Text = "Import"; + // + // btn_QRV_Import_EDI + // + this.btn_QRV_Import_EDI.Location = new System.Drawing.Point(87, 23); + this.btn_QRV_Import_EDI.Name = "btn_QRV_Import_EDI"; + this.btn_QRV_Import_EDI.Size = new System.Drawing.Size(75, 23); + this.btn_QRV_Import_EDI.TabIndex = 1; + this.btn_QRV_Import_EDI.Text = "EDI"; + this.btn_QRV_Import_EDI.UseVisualStyleBackColor = true; + this.btn_QRV_Import_EDI.Click += new System.EventHandler(this.btn_QRV_Import_EDI_Click); + // + // btn_QRV_Import_WinTest + // + this.btn_QRV_Import_WinTest.Location = new System.Drawing.Point(6, 23); + this.btn_QRV_Import_WinTest.Name = "btn_QRV_Import_WinTest"; + this.btn_QRV_Import_WinTest.Size = new System.Drawing.Size(75, 23); + this.btn_QRV_Import_WinTest.TabIndex = 0; + this.btn_QRV_Import_WinTest.Text = "Win-Test (QRV)"; + this.btn_QRV_Import_WinTest.UseVisualStyleBackColor = true; + this.btn_QRV_Import_WinTest.Click += new System.EventHandler(this.btn_QRV_Import_WinTest_Click); + // + // tp_Aircrafts + // + this.tp_Aircrafts.BackColor = System.Drawing.SystemColors.Control; + this.tp_Aircrafts.Controls.Add(this.lbl_Aircrafts_UnkownHex); + this.tp_Aircrafts.Controls.Add(this.lbl_Aircrafts_UnkownType); + this.tp_Aircrafts.Controls.Add(this.lbl_Aircrafts_UnkownCall); + this.tp_Aircrafts.Controls.Add(this.lbl_Aircrafts_Total); + this.tp_Aircrafts.Controls.Add(this.label8); + this.tp_Aircrafts.Controls.Add(this.button1); + this.tp_Aircrafts.Controls.Add(this.btn_Update_Aircrafts_Stop); + this.tp_Aircrafts.Controls.Add(this.btn_Update_Aicrafts_Start); + this.tp_Aircrafts.Controls.Add(this.btn_Update_Airports); + this.tp_Aircrafts.Controls.Add(this.btn_Update_Airlines); + this.tp_Aircrafts.Controls.Add(this.tb_Update_Aircrafts); + this.tp_Aircrafts.Controls.Add(this.tb_Update_Airports); + this.tp_Aircrafts.Controls.Add(this.tb_Update_Airlines); + this.tp_Aircrafts.Location = new System.Drawing.Point(4, 22); + this.tp_Aircrafts.Name = "tp_Aircrafts"; + this.tp_Aircrafts.Size = new System.Drawing.Size(1098, 549); + this.tp_Aircrafts.TabIndex = 4; + this.tp_Aircrafts.Text = "Aircrafts"; + this.tp_Aircrafts.Enter += new System.EventHandler(this.tp_Aircrafts_Enter); + // + // lbl_Aircrafts_UnkownHex + // + this.lbl_Aircrafts_UnkownHex.AutoSize = true; + this.lbl_Aircrafts_UnkownHex.Location = new System.Drawing.Point(330, 133); + this.lbl_Aircrafts_UnkownHex.Name = "lbl_Aircrafts_UnkownHex"; + this.lbl_Aircrafts_UnkownHex.Size = new System.Drawing.Size(30, 13); + this.lbl_Aircrafts_UnkownHex.TabIndex = 12; + this.lbl_Aircrafts_UnkownHex.Text = "total:"; + // + // lbl_Aircrafts_UnkownType + // + this.lbl_Aircrafts_UnkownType.AutoSize = true; + this.lbl_Aircrafts_UnkownType.Location = new System.Drawing.Point(738, 133); + this.lbl_Aircrafts_UnkownType.Name = "lbl_Aircrafts_UnkownType"; + this.lbl_Aircrafts_UnkownType.Size = new System.Drawing.Size(30, 13); + this.lbl_Aircrafts_UnkownType.TabIndex = 11; + this.lbl_Aircrafts_UnkownType.Text = "total:"; + // + // lbl_Aircrafts_UnkownCall + // + this.lbl_Aircrafts_UnkownCall.AutoSize = true; + this.lbl_Aircrafts_UnkownCall.Location = new System.Drawing.Point(517, 133); + this.lbl_Aircrafts_UnkownCall.Name = "lbl_Aircrafts_UnkownCall"; + this.lbl_Aircrafts_UnkownCall.Size = new System.Drawing.Size(30, 13); + this.lbl_Aircrafts_UnkownCall.TabIndex = 10; + this.lbl_Aircrafts_UnkownCall.Text = "total:"; + // + // lbl_Aircrafts_Total + // + this.lbl_Aircrafts_Total.AutoSize = true; + this.lbl_Aircrafts_Total.Location = new System.Drawing.Point(161, 133); + this.lbl_Aircrafts_Total.Name = "lbl_Aircrafts_Total"; + this.lbl_Aircrafts_Total.Size = new System.Drawing.Size(30, 13); + this.lbl_Aircrafts_Total.TabIndex = 9; + this.lbl_Aircrafts_Total.Text = "total:"; + // + // label8 + // + this.label8.AutoSize = true; + this.label8.Location = new System.Drawing.Point(8, 133); + this.label8.Name = "label8"; + this.label8.Size = new System.Drawing.Size(134, 13); + this.label8.TabIndex = 8; + this.label8.Text = "Aircraft Database Statistics"; + // + // button1 + // + this.button1.Enabled = false; + this.button1.Location = new System.Drawing.Point(8, 96); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(150, 23); + this.button1.TabIndex = 6; + this.button1.Text = "Update Aircrafts"; + this.button1.UseVisualStyleBackColor = true; + // + // btn_Update_Aircrafts_Stop + // + this.btn_Update_Aircrafts_Stop.Location = new System.Drawing.Point(1001, 96); + this.btn_Update_Aircrafts_Stop.Name = "btn_Update_Aircrafts_Stop"; + this.btn_Update_Aircrafts_Stop.Size = new System.Drawing.Size(75, 23); + this.btn_Update_Aircrafts_Stop.TabIndex = 5; + this.btn_Update_Aircrafts_Stop.Text = "Stop"; + this.btn_Update_Aircrafts_Stop.UseVisualStyleBackColor = true; + this.btn_Update_Aircrafts_Stop.Click += new System.EventHandler(this.btn_Update_Aircrafts_Stop_Click); + // + // btn_Update_Aicrafts_Start + // + this.btn_Update_Aicrafts_Start.Location = new System.Drawing.Point(906, 96); + this.btn_Update_Aicrafts_Start.Name = "btn_Update_Aicrafts_Start"; + this.btn_Update_Aicrafts_Start.Size = new System.Drawing.Size(75, 23); + this.btn_Update_Aicrafts_Start.TabIndex = 4; + this.btn_Update_Aicrafts_Start.Text = "Start"; + this.btn_Update_Aicrafts_Start.UseVisualStyleBackColor = true; + this.btn_Update_Aicrafts_Start.Click += new System.EventHandler(this.btn_Update_Aicrafts_Start_Click); + // + // btn_Update_Airports + // + this.btn_Update_Airports.Location = new System.Drawing.Point(8, 50); + this.btn_Update_Airports.Name = "btn_Update_Airports"; + this.btn_Update_Airports.Size = new System.Drawing.Size(150, 23); + this.btn_Update_Airports.TabIndex = 2; + this.btn_Update_Airports.Text = "Update Airports"; + this.btn_Update_Airports.UseVisualStyleBackColor = true; + this.btn_Update_Airports.Click += new System.EventHandler(this.btn_Update_Airports_Click); + // + // btn_Update_Airlines + // + this.btn_Update_Airlines.Location = new System.Drawing.Point(8, 21); + this.btn_Update_Airlines.Name = "btn_Update_Airlines"; + this.btn_Update_Airlines.Size = new System.Drawing.Size(150, 23); + this.btn_Update_Airlines.TabIndex = 0; + this.btn_Update_Airlines.Text = "Update Airlines"; + this.btn_Update_Airlines.UseVisualStyleBackColor = true; + this.btn_Update_Airlines.Click += new System.EventHandler(this.btn_Update_Airlines_Click); + // + // tp_Upload + // + this.tp_Upload.BackColor = System.Drawing.SystemColors.Control; + this.tp_Upload.Controls.Add(this.groupBox6); + this.tp_Upload.Controls.Add(this.groupBox5); + this.tp_Upload.Location = new System.Drawing.Point(4, 22); + this.tp_Upload.Name = "tp_Upload"; + this.tp_Upload.Size = new System.Drawing.Size(1098, 549); + this.tp_Upload.TabIndex = 5; + this.tp_Upload.Text = "Upload"; + // + // groupBox5 + // + this.groupBox5.Controls.Add(this.tb_StationDatabase_RemoteHost); + this.groupBox5.Controls.Add(this.label13); + this.groupBox5.Controls.Add(this.tb_StationDatabase_Export_Password); + this.groupBox5.Controls.Add(this.label12); + this.groupBox5.Controls.Add(this.tb_StationDatabase_Export_User); + this.groupBox5.Controls.Add(this.label11); + this.groupBox5.Controls.Add(this.btn_StationDatabase_Export); + this.groupBox5.Controls.Add(this.tb_StationDatabase_RemoteDir); + this.groupBox5.Controls.Add(this.label10); + this.groupBox5.Controls.Add(this.tb_StationDatabase_LocalDir); + this.groupBox5.Controls.Add(this.label9); + this.groupBox5.Location = new System.Drawing.Point(8, 15); + this.groupBox5.Name = "groupBox5"; + this.groupBox5.Size = new System.Drawing.Size(881, 189); + this.groupBox5.TabIndex = 2; + this.groupBox5.TabStop = false; + this.groupBox5.Text = "Station Database"; + // + // label13 + // + this.label13.AutoSize = true; + this.label13.Location = new System.Drawing.Point(9, 48); + this.label13.Name = "label13"; + this.label13.Size = new System.Drawing.Size(72, 13); + this.label13.TabIndex = 9; + this.label13.Text = "Remote Host:"; + // + // label12 + // + this.label12.AutoSize = true; + this.label12.Location = new System.Drawing.Point(9, 127); + this.label12.Name = "label12"; + this.label12.Size = new System.Drawing.Size(56, 13); + this.label12.TabIndex = 7; + this.label12.Text = "Password:"; + // + // label11 + // + this.label11.AutoSize = true; + this.label11.Location = new System.Drawing.Point(9, 101); + this.label11.Name = "label11"; + this.label11.Size = new System.Drawing.Size(32, 13); + this.label11.TabIndex = 5; + this.label11.Text = "User:"; + // + // btn_StationDatabase_Export + // + this.btn_StationDatabase_Export.Location = new System.Drawing.Point(107, 150); + this.btn_StationDatabase_Export.Name = "btn_StationDatabase_Export"; + this.btn_StationDatabase_Export.Size = new System.Drawing.Size(178, 23); + this.btn_StationDatabase_Export.TabIndex = 4; + this.btn_StationDatabase_Export.Text = "Export to JSON and Upload"; + this.btn_StationDatabase_Export.UseVisualStyleBackColor = true; + this.btn_StationDatabase_Export.Click += new System.EventHandler(this.btn_StationDatabase_Export_Click); + // + // label10 + // + this.label10.AutoSize = true; + this.label10.Location = new System.Drawing.Point(9, 74); + this.label10.Name = "label10"; + this.label10.Size = new System.Drawing.Size(92, 13); + this.label10.TabIndex = 2; + this.label10.Text = "Remote Directory:"; + // + // label9 + // + this.label9.AutoSize = true; + this.label9.Location = new System.Drawing.Point(9, 22); + this.label9.Name = "label9"; + this.label9.Size = new System.Drawing.Size(81, 13); + this.label9.TabIndex = 0; + this.label9.Text = "Local Directory:"; + // + // tp_Extras + // + this.tp_Extras.BackColor = System.Drawing.SystemColors.Control; + this.tp_Extras.Controls.Add(this.groupBox4); + this.tp_Extras.Location = new System.Drawing.Point(4, 22); + this.tp_Extras.Name = "tp_Extras"; + this.tp_Extras.Size = new System.Drawing.Size(1098, 549); + this.tp_Extras.TabIndex = 3; + this.tp_Extras.Text = "Extras"; + // + // groupBox4 + // + this.groupBox4.Controls.Add(this.btn_SFTP_GenarateFile); + this.groupBox4.Controls.Add(this.tb_SFTP_Password); + this.groupBox4.Controls.Add(this.label7); + this.groupBox4.Controls.Add(this.tb_SFTP_User); + this.groupBox4.Controls.Add(this.label6); + this.groupBox4.Controls.Add(this.tb_SFTP_URL); + this.groupBox4.Controls.Add(this.label5); + this.groupBox4.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, ((System.Drawing.FontStyle)((System.Drawing.FontStyle.Bold | System.Drawing.FontStyle.Italic))), System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.groupBox4.Location = new System.Drawing.Point(8, 14); + this.groupBox4.Name = "groupBox4"; + this.groupBox4.Size = new System.Drawing.Size(778, 111); + this.groupBox4.TabIndex = 0; + this.groupBox4.TabStop = false; + this.groupBox4.Text = "SFTP Password File"; + // + // btn_SFTP_GenarateFile + // + this.btn_SFTP_GenarateFile.Location = new System.Drawing.Point(423, 23); + this.btn_SFTP_GenarateFile.Name = "btn_SFTP_GenarateFile"; + this.btn_SFTP_GenarateFile.Size = new System.Drawing.Size(252, 72); + this.btn_SFTP_GenarateFile.TabIndex = 6; + this.btn_SFTP_GenarateFile.Text = "Generate File"; + this.btn_SFTP_GenarateFile.UseVisualStyleBackColor = true; + this.btn_SFTP_GenarateFile.Click += new System.EventHandler(this.btn_SFTP_GenerateFile_Click); + // + // label7 + // + this.label7.AutoSize = true; + this.label7.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label7.Location = new System.Drawing.Point(6, 78); + this.label7.Name = "label7"; + this.label7.Size = new System.Drawing.Size(56, 13); + this.label7.TabIndex = 4; + this.label7.Text = "Password:"; + // + // label6 + // + this.label6.AutoSize = true; + this.label6.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label6.Location = new System.Drawing.Point(6, 52); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(32, 13); + this.label6.TabIndex = 2; + this.label6.Text = "User:"; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label5.Location = new System.Drawing.Point(6, 26); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(32, 13); + this.label5.TabIndex = 0; + this.label5.Text = "URL:"; + // + // bw_QRZ + // + this.bw_QRZ.WorkerReportsProgress = true; + this.bw_QRZ.WorkerSupportsCancellation = true; + this.bw_QRZ.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bw_QRZ_DoWork); + this.bw_QRZ.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.bw_QRZ_ProgressChanged); + this.bw_QRZ.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.bw_QRZ_RunWorkerCompleted); + // + // bw_DatabaseUpdater + // + this.bw_DatabaseUpdater.WorkerReportsProgress = true; + this.bw_DatabaseUpdater.WorkerSupportsCancellation = true; + this.bw_DatabaseUpdater.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bw_DatabaseUpdater_DoWork); + this.bw_DatabaseUpdater.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.bw_DatabaseUpdater_ProgressChanged); + this.bw_DatabaseUpdater.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.bw_DatabaseUpdater_RunWorkerCompleted); + // + // bw_AircraftUpdater + // + this.bw_AircraftUpdater.WorkerReportsProgress = true; + this.bw_AircraftUpdater.WorkerSupportsCancellation = true; + this.bw_AircraftUpdater.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bw_AircraftUpdater_DoWork); + this.bw_AircraftUpdater.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.bw_AircraftUpdater_ProgressChanged); + this.bw_AircraftUpdater.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.bw_AircraftUpdater_RunWorkerCompleted); + // + // groupBox6 + // + this.groupBox6.Controls.Add(this.tb_AircraftDatabase_RemoteHost); + this.groupBox6.Controls.Add(this.label14); + this.groupBox6.Controls.Add(this.tb_AircraftDatabase_Password); + this.groupBox6.Controls.Add(this.label15); + this.groupBox6.Controls.Add(this.tb_AircraftDatabase_User); + this.groupBox6.Controls.Add(this.label16); + this.groupBox6.Controls.Add(this.btn_AircraftDatabase_Export); + this.groupBox6.Controls.Add(this.tb_AircraftDatabase_RemoteDir); + this.groupBox6.Controls.Add(this.label17); + this.groupBox6.Controls.Add(this.tb_AircraftDabase_LocalDir); + this.groupBox6.Controls.Add(this.label18); + this.groupBox6.Location = new System.Drawing.Point(8, 210); + this.groupBox6.Name = "groupBox6"; + this.groupBox6.Size = new System.Drawing.Size(881, 189); + this.groupBox6.TabIndex = 3; + this.groupBox6.TabStop = false; + this.groupBox6.Text = "Aicraft Database"; + // + // label14 + // + this.label14.AutoSize = true; + this.label14.Location = new System.Drawing.Point(9, 48); + this.label14.Name = "label14"; + this.label14.Size = new System.Drawing.Size(72, 13); + this.label14.TabIndex = 9; + this.label14.Text = "Remote Host:"; + // + // label15 + // + this.label15.AutoSize = true; + this.label15.Location = new System.Drawing.Point(9, 127); + this.label15.Name = "label15"; + this.label15.Size = new System.Drawing.Size(56, 13); + this.label15.TabIndex = 7; + this.label15.Text = "Password:"; + // + // label16 + // + this.label16.AutoSize = true; + this.label16.Location = new System.Drawing.Point(9, 101); + this.label16.Name = "label16"; + this.label16.Size = new System.Drawing.Size(32, 13); + this.label16.TabIndex = 5; + this.label16.Text = "User:"; + // + // btn_AircraftDatabase_Export + // + this.btn_AircraftDatabase_Export.Location = new System.Drawing.Point(107, 150); + this.btn_AircraftDatabase_Export.Name = "btn_AircraftDatabase_Export"; + this.btn_AircraftDatabase_Export.Size = new System.Drawing.Size(178, 23); + this.btn_AircraftDatabase_Export.TabIndex = 4; + this.btn_AircraftDatabase_Export.Text = "Export to JSON and Upload"; + this.btn_AircraftDatabase_Export.UseVisualStyleBackColor = true; + this.btn_AircraftDatabase_Export.Click += new System.EventHandler(this.btn_AircraftDatabase_Export_Click); + // + // label17 + // + this.label17.AutoSize = true; + this.label17.Location = new System.Drawing.Point(9, 74); + this.label17.Name = "label17"; + this.label17.Size = new System.Drawing.Size(92, 13); + this.label17.TabIndex = 2; + this.label17.Text = "Remote Directory:"; + // + // label18 + // + this.label18.AutoSize = true; + this.label18.Location = new System.Drawing.Point(9, 22); + this.label18.Name = "label18"; + this.label18.Size = new System.Drawing.Size(81, 13); + this.label18.TabIndex = 0; + this.label18.Text = "Local Directory:"; + // + // tb_Coverage_MaxLat + // + this.tb_Coverage_MaxLat.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScoutDatabaseManager.Properties.Settings.Default, "MaxLat", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Coverage_MaxLat.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Coverage_MaxLat.FormatSpecifier = "F0"; + this.tb_Coverage_MaxLat.Location = new System.Drawing.Point(105, 104); + this.tb_Coverage_MaxLat.MaxValue = 90D; + this.tb_Coverage_MaxLat.MinValue = -90D; + this.tb_Coverage_MaxLat.Name = "tb_Coverage_MaxLat"; + this.tb_Coverage_MaxLat.Size = new System.Drawing.Size(50, 22); + this.tb_Coverage_MaxLat.TabIndex = 28; + this.tb_Coverage_MaxLat.Text = "60"; + this.tb_Coverage_MaxLat.Value = global::AirScoutDatabaseManager.Properties.Settings.Default.MaxLat; + this.tb_Coverage_MaxLat.TextChanged += new System.EventHandler(this.tp_General_Update); + // + // tb_Coverage_MinLat + // + this.tb_Coverage_MinLat.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScoutDatabaseManager.Properties.Settings.Default, "MinLat", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Coverage_MinLat.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Coverage_MinLat.FormatSpecifier = "F0"; + this.tb_Coverage_MinLat.Location = new System.Drawing.Point(105, 79); + this.tb_Coverage_MinLat.MaxValue = 90D; + this.tb_Coverage_MinLat.MinValue = -90D; + this.tb_Coverage_MinLat.Name = "tb_Coverage_MinLat"; + this.tb_Coverage_MinLat.Size = new System.Drawing.Size(50, 22); + this.tb_Coverage_MinLat.TabIndex = 27; + this.tb_Coverage_MinLat.Text = "35"; + this.tb_Coverage_MinLat.Value = global::AirScoutDatabaseManager.Properties.Settings.Default.MinLat; + this.tb_Coverage_MinLat.TextChanged += new System.EventHandler(this.tp_General_Update); + // + // tb_Coverage_MaxLon + // + this.tb_Coverage_MaxLon.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScoutDatabaseManager.Properties.Settings.Default, "MaxLon", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Coverage_MaxLon.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Coverage_MaxLon.FormatSpecifier = "F0"; + this.tb_Coverage_MaxLon.Location = new System.Drawing.Point(105, 52); + this.tb_Coverage_MaxLon.MaxValue = 180D; + this.tb_Coverage_MaxLon.MinValue = -180D; + this.tb_Coverage_MaxLon.Name = "tb_Coverage_MaxLon"; + this.tb_Coverage_MaxLon.Size = new System.Drawing.Size(50, 22); + this.tb_Coverage_MaxLon.TabIndex = 26; + this.tb_Coverage_MaxLon.Text = "30"; + this.tb_Coverage_MaxLon.Value = global::AirScoutDatabaseManager.Properties.Settings.Default.MaxLon; + this.tb_Coverage_MaxLon.TextChanged += new System.EventHandler(this.tp_General_Update); + // + // tb_Coverage_MinLon + // + this.tb_Coverage_MinLon.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScoutDatabaseManager.Properties.Settings.Default, "MinLon", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Coverage_MinLon.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Coverage_MinLon.FormatSpecifier = "F0"; + this.tb_Coverage_MinLon.Location = new System.Drawing.Point(105, 25); + this.tb_Coverage_MinLon.MaxValue = 180D; + this.tb_Coverage_MinLon.MinValue = -180D; + this.tb_Coverage_MinLon.Name = "tb_Coverage_MinLon"; + this.tb_Coverage_MinLon.Size = new System.Drawing.Size(50, 22); + this.tb_Coverage_MinLon.TabIndex = 25; + this.tb_Coverage_MinLon.Text = "-15"; + this.tb_Coverage_MinLon.Value = global::AirScoutDatabaseManager.Properties.Settings.Default.MinLon; + this.tb_Coverage_MinLon.TextChanged += new System.EventHandler(this.tp_General_Update); + // + // tb_Update_Aircrafts + // + this.tb_Update_Aircrafts.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::AirScoutDatabaseManager.Properties.Settings.Default, "Aircrafts_BaseURL", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Update_Aircrafts.Location = new System.Drawing.Point(164, 98); + this.tb_Update_Aircrafts.Name = "tb_Update_Aircrafts"; + this.tb_Update_Aircrafts.Size = new System.Drawing.Size(722, 20); + this.tb_Update_Aircrafts.TabIndex = 7; + this.tb_Update_Aircrafts.Text = global::AirScoutDatabaseManager.Properties.Settings.Default.Aircrafts_BaseURL; + // + // tb_Update_Airports + // + this.tb_Update_Airports.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::AirScoutDatabaseManager.Properties.Settings.Default, "Airports_Update_URL", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Update_Airports.Location = new System.Drawing.Point(164, 53); + this.tb_Update_Airports.Name = "tb_Update_Airports"; + this.tb_Update_Airports.Size = new System.Drawing.Size(722, 20); + this.tb_Update_Airports.TabIndex = 3; + this.tb_Update_Airports.Text = global::AirScoutDatabaseManager.Properties.Settings.Default.Airports_Update_URL; + // + // tb_Update_Airlines + // + this.tb_Update_Airlines.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::AirScoutDatabaseManager.Properties.Settings.Default, "Airlines_Update_URL", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Update_Airlines.Location = new System.Drawing.Point(164, 24); + this.tb_Update_Airlines.Name = "tb_Update_Airlines"; + this.tb_Update_Airlines.Size = new System.Drawing.Size(722, 20); + this.tb_Update_Airlines.TabIndex = 1; + this.tb_Update_Airlines.Text = global::AirScoutDatabaseManager.Properties.Settings.Default.Airlines_Update_URL; + // + // tb_AircraftDatabase_RemoteHost + // + this.tb_AircraftDatabase_RemoteHost.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::AirScoutDatabaseManager.Properties.Settings.Default, "AircraftDatabase_Export_RemoteHost", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_AircraftDatabase_RemoteHost.Location = new System.Drawing.Point(107, 45); + this.tb_AircraftDatabase_RemoteHost.Name = "tb_AircraftDatabase_RemoteHost"; + this.tb_AircraftDatabase_RemoteHost.Size = new System.Drawing.Size(543, 20); + this.tb_AircraftDatabase_RemoteHost.TabIndex = 10; + this.tb_AircraftDatabase_RemoteHost.Text = global::AirScoutDatabaseManager.Properties.Settings.Default.AircraftDatabase_Export_RemoteHost; + // + // tb_AircraftDatabase_Password + // + this.tb_AircraftDatabase_Password.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::AirScoutDatabaseManager.Properties.Settings.Default, "AircraftDatabase_Export_Password", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_AircraftDatabase_Password.Location = new System.Drawing.Point(107, 124); + this.tb_AircraftDatabase_Password.Name = "tb_AircraftDatabase_Password"; + this.tb_AircraftDatabase_Password.Size = new System.Drawing.Size(107, 20); + this.tb_AircraftDatabase_Password.TabIndex = 8; + this.tb_AircraftDatabase_Password.Text = global::AirScoutDatabaseManager.Properties.Settings.Default.AircraftDatabase_Export_Password; + // + // tb_AircraftDatabase_User + // + this.tb_AircraftDatabase_User.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::AirScoutDatabaseManager.Properties.Settings.Default, "AircraftDatabase_Export_User", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_AircraftDatabase_User.Location = new System.Drawing.Point(107, 98); + this.tb_AircraftDatabase_User.Name = "tb_AircraftDatabase_User"; + this.tb_AircraftDatabase_User.Size = new System.Drawing.Size(107, 20); + this.tb_AircraftDatabase_User.TabIndex = 6; + this.tb_AircraftDatabase_User.Text = global::AirScoutDatabaseManager.Properties.Settings.Default.AircraftDatabase_Export_User; + // + // tb_AircraftDatabase_RemoteDir + // + this.tb_AircraftDatabase_RemoteDir.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::AirScoutDatabaseManager.Properties.Settings.Default, "AircraftDatabase_Export_RemoteDir", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_AircraftDatabase_RemoteDir.Location = new System.Drawing.Point(107, 71); + this.tb_AircraftDatabase_RemoteDir.Name = "tb_AircraftDatabase_RemoteDir"; + this.tb_AircraftDatabase_RemoteDir.Size = new System.Drawing.Size(543, 20); + this.tb_AircraftDatabase_RemoteDir.TabIndex = 3; + this.tb_AircraftDatabase_RemoteDir.Text = global::AirScoutDatabaseManager.Properties.Settings.Default.AircraftDatabase_Export_RemoteDir; + // + // tb_AircraftDabase_LocalDir + // + this.tb_AircraftDabase_LocalDir.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::AirScoutDatabaseManager.Properties.Settings.Default, "AircraftDatabase_Export_LocalDir", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_AircraftDabase_LocalDir.Location = new System.Drawing.Point(107, 19); + this.tb_AircraftDabase_LocalDir.Name = "tb_AircraftDabase_LocalDir"; + this.tb_AircraftDabase_LocalDir.Size = new System.Drawing.Size(543, 20); + this.tb_AircraftDabase_LocalDir.TabIndex = 1; + this.tb_AircraftDabase_LocalDir.Text = global::AirScoutDatabaseManager.Properties.Settings.Default.AircraftDatabase_Export_LocalDir; + // + // tb_StationDatabase_RemoteHost + // + this.tb_StationDatabase_RemoteHost.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::AirScoutDatabaseManager.Properties.Settings.Default, "StationDatabase_Export_RemoteHost", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_StationDatabase_RemoteHost.Location = new System.Drawing.Point(107, 45); + this.tb_StationDatabase_RemoteHost.Name = "tb_StationDatabase_RemoteHost"; + this.tb_StationDatabase_RemoteHost.Size = new System.Drawing.Size(543, 20); + this.tb_StationDatabase_RemoteHost.TabIndex = 10; + this.tb_StationDatabase_RemoteHost.Text = global::AirScoutDatabaseManager.Properties.Settings.Default.StationDatabase_Export_RemoteHost; + // + // tb_StationDatabase_Export_Password + // + this.tb_StationDatabase_Export_Password.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::AirScoutDatabaseManager.Properties.Settings.Default, "StationDatabase_Export_Password", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_StationDatabase_Export_Password.Location = new System.Drawing.Point(107, 124); + this.tb_StationDatabase_Export_Password.Name = "tb_StationDatabase_Export_Password"; + this.tb_StationDatabase_Export_Password.Size = new System.Drawing.Size(107, 20); + this.tb_StationDatabase_Export_Password.TabIndex = 8; + this.tb_StationDatabase_Export_Password.Text = global::AirScoutDatabaseManager.Properties.Settings.Default.StationDatabase_Export_Password; + // + // tb_StationDatabase_Export_User + // + this.tb_StationDatabase_Export_User.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::AirScoutDatabaseManager.Properties.Settings.Default, "StationDatabase_Export_User", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_StationDatabase_Export_User.Location = new System.Drawing.Point(107, 98); + this.tb_StationDatabase_Export_User.Name = "tb_StationDatabase_Export_User"; + this.tb_StationDatabase_Export_User.Size = new System.Drawing.Size(107, 20); + this.tb_StationDatabase_Export_User.TabIndex = 6; + this.tb_StationDatabase_Export_User.Text = global::AirScoutDatabaseManager.Properties.Settings.Default.StationDatabase_Export_User; + // + // tb_StationDatabase_RemoteDir + // + this.tb_StationDatabase_RemoteDir.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::AirScoutDatabaseManager.Properties.Settings.Default, "StationDatabase_Export_RemoteDir", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_StationDatabase_RemoteDir.Location = new System.Drawing.Point(107, 71); + this.tb_StationDatabase_RemoteDir.Name = "tb_StationDatabase_RemoteDir"; + this.tb_StationDatabase_RemoteDir.Size = new System.Drawing.Size(543, 20); + this.tb_StationDatabase_RemoteDir.TabIndex = 3; + this.tb_StationDatabase_RemoteDir.Text = global::AirScoutDatabaseManager.Properties.Settings.Default.StationDatabase_Export_RemoteDir; + // + // tb_StationDatabase_LocalDir + // + this.tb_StationDatabase_LocalDir.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::AirScoutDatabaseManager.Properties.Settings.Default, "StationDatabase_Export_LocalDir", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_StationDatabase_LocalDir.Location = new System.Drawing.Point(107, 19); + this.tb_StationDatabase_LocalDir.Name = "tb_StationDatabase_LocalDir"; + this.tb_StationDatabase_LocalDir.Size = new System.Drawing.Size(543, 20); + this.tb_StationDatabase_LocalDir.TabIndex = 1; + this.tb_StationDatabase_LocalDir.Text = global::AirScoutDatabaseManager.Properties.Settings.Default.StationDatabase_Export_LocalDir; + // + // tb_SFTP_Password + // + this.tb_SFTP_Password.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::AirScoutDatabaseManager.Properties.Settings.Default, "SFTP_Password", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_SFTP_Password.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_SFTP_Password.Location = new System.Drawing.Point(71, 75); + this.tb_SFTP_Password.Name = "tb_SFTP_Password"; + this.tb_SFTP_Password.Size = new System.Drawing.Size(318, 20); + this.tb_SFTP_Password.TabIndex = 5; + this.tb_SFTP_Password.Text = global::AirScoutDatabaseManager.Properties.Settings.Default.SFTP_Password; + // + // tb_SFTP_User + // + this.tb_SFTP_User.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::AirScoutDatabaseManager.Properties.Settings.Default, "SFTP_User", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_SFTP_User.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_SFTP_User.Location = new System.Drawing.Point(71, 49); + this.tb_SFTP_User.Name = "tb_SFTP_User"; + this.tb_SFTP_User.Size = new System.Drawing.Size(318, 20); + this.tb_SFTP_User.TabIndex = 3; + this.tb_SFTP_User.Text = global::AirScoutDatabaseManager.Properties.Settings.Default.SFTP_User; + // + // tb_SFTP_URL + // + this.tb_SFTP_URL.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::AirScoutDatabaseManager.Properties.Settings.Default, "SFTP_URL", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_SFTP_URL.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_SFTP_URL.Location = new System.Drawing.Point(71, 23); + this.tb_SFTP_URL.Name = "tb_SFTP_URL"; + this.tb_SFTP_URL.Size = new System.Drawing.Size(318, 20); + this.tb_SFTP_URL.TabIndex = 1; + this.tb_SFTP_URL.Text = global::AirScoutDatabaseManager.Properties.Settings.Default.SFTP_URL; + // + // MainDlg + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(1106, 597); + this.Controls.Add(this.tc_Main); + this.Controls.Add(this.ss_Main); + this.Name = "MainDlg"; + this.Text = "AirScout Database Manager"; + this.WindowState = System.Windows.Forms.FormWindowState.Maximized; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainDlg_FormClosing); + this.Load += new System.EventHandler(this.MainDlg_Load); + this.ss_Main.ResumeLayout(false); + this.ss_Main.PerformLayout(); + this.tc_Main.ResumeLayout(false); + this.tp_General.ResumeLayout(false); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.tp_Locations.ResumeLayout(false); + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.dgv_Locations)).EndInit(); + this.panel2.ResumeLayout(false); + this.panel2.PerformLayout(); + this.groupBox2.ResumeLayout(false); + this.groupBox1.ResumeLayout(false); + this.tp_QRV.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.dgv_QRV)).EndInit(); + this.panel4.ResumeLayout(false); + this.panel4.PerformLayout(); + this.groupBox3.ResumeLayout(false); + this.tp_Aircrafts.ResumeLayout(false); + this.tp_Aircrafts.PerformLayout(); + this.tp_Upload.ResumeLayout(false); + this.groupBox5.ResumeLayout(false); + this.groupBox5.PerformLayout(); + this.tp_Extras.ResumeLayout(false); + this.groupBox4.ResumeLayout(false); + this.groupBox4.PerformLayout(); + this.groupBox6.ResumeLayout(false); + this.groupBox6.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.StatusStrip ss_Main; + private System.Windows.Forms.ToolStripStatusLabel tsl_Status; + private System.Windows.Forms.TabControl tc_Main; + private System.Windows.Forms.TabPage tp_General; + private System.Windows.Forms.TabPage tp_Locations; + private System.Windows.Forms.Panel panel1; + private GMap.NET.WindowsForms.GMapControl gm_Coverage; + private ScoutBase.Core.DoubleTextBox tb_Coverage_MaxLat; + private ScoutBase.Core.DoubleTextBox tb_Coverage_MinLat; + private ScoutBase.Core.DoubleTextBox tb_Coverage_MaxLon; + private ScoutBase.Core.DoubleTextBox tb_Coverage_MinLon; + private System.Windows.Forms.Label label35; + private System.Windows.Forms.Label label54; + private System.Windows.Forms.Label label59; + private System.Windows.Forms.Label label60; + private System.Windows.Forms.Panel panel2; + private System.Windows.Forms.DataGridView dgv_Locations; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.TextBox tb_Locations_Status; + private System.Windows.Forms.Button btn_QRZ_Stop; + private System.Windows.Forms.Button btn_QRZ_Start; + private System.ComponentModel.BackgroundWorker bw_QRZ; + private System.Windows.Forms.Button btn_Locations_Export; + private System.Windows.Forms.Button btn_Locations_Save; + private System.ComponentModel.BackgroundWorker bw_DatabaseUpdater; + private System.Windows.Forms.GroupBox groupBox2; + private System.Windows.Forms.Button btn_Locations_Import_CALL3; + private System.Windows.Forms.Button btn_Locations_Sort; + private System.Windows.Forms.Button btn_Locations_Import_DTB; + private System.Windows.Forms.Button btn_Locations_Import_CSV; + private System.Windows.Forms.CheckBox cb_Locations_ChangedOnly; + private GMap.NET.WindowsForms.GMapControl gm_Locations; + private System.Windows.Forms.SplitContainer splitContainer1; + private System.Windows.Forms.TextBox tb_Locations_Callsign_Filter; + private System.Windows.Forms.Button btn_Locations_Import_USR; + private System.Windows.Forms.Button btn_Locations_Import_AirScout; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TabPage tp_QRV; + private System.Windows.Forms.Panel panel4; + private System.Windows.Forms.GroupBox groupBox3; + private System.Windows.Forms.Button btn_QRV_Import_WinTest; + private System.Windows.Forms.DataGridView dgv_QRV; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.TextBox tb_QRV_Callsign_Filter; + private System.Windows.Forms.TextBox tb_QRV_Status; + private System.Windows.Forms.Button btn_QRV_Sort; + private System.Windows.Forms.Button btn_QRV_Export; + private System.Windows.Forms.Button btn_QRV_Save; + private System.Windows.Forms.CheckBox cb_QRV_ChangedOnly; + private System.Windows.Forms.Button btn_QRV_Import_EDI; + private System.Windows.Forms.TabPage tp_Extras; + private System.Windows.Forms.GroupBox groupBox4; + private System.Windows.Forms.Button btn_SFTP_GenarateFile; + private System.Windows.Forms.TextBox tb_SFTP_Password; + private System.Windows.Forms.Label label7; + private System.Windows.Forms.TextBox tb_SFTP_User; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.TextBox tb_SFTP_URL; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.TabPage tp_Aircrafts; + private System.Windows.Forms.TextBox tb_Update_Airlines; + private System.Windows.Forms.Button btn_Update_Airlines; + private System.Windows.Forms.TextBox tb_Update_Airports; + private System.Windows.Forms.Button btn_Update_Airports; + private System.ComponentModel.BackgroundWorker bw_AircraftUpdater; + private System.Windows.Forms.Button btn_Update_Aircrafts_Stop; + private System.Windows.Forms.Button btn_Update_Aicrafts_Start; + private System.Windows.Forms.TextBox tb_Update_Aircrafts; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.Label lbl_Aircrafts_UnkownHex; + private System.Windows.Forms.Label lbl_Aircrafts_UnkownType; + private System.Windows.Forms.Label lbl_Aircrafts_UnkownCall; + private System.Windows.Forms.Label lbl_Aircrafts_Total; + private System.Windows.Forms.Label label8; + private System.Windows.Forms.TabPage tp_Upload; + private System.Windows.Forms.GroupBox groupBox5; + private System.Windows.Forms.Button btn_StationDatabase_Export; + private System.Windows.Forms.TextBox tb_StationDatabase_RemoteDir; + private System.Windows.Forms.Label label10; + private System.Windows.Forms.TextBox tb_StationDatabase_LocalDir; + private System.Windows.Forms.Label label9; + private System.Windows.Forms.TextBox tb_StationDatabase_Export_User; + private System.Windows.Forms.Label label11; + private System.Windows.Forms.TextBox tb_StationDatabase_Export_Password; + private System.Windows.Forms.Label label12; + private System.Windows.Forms.TextBox tb_StationDatabase_RemoteHost; + private System.Windows.Forms.Label label13; + private System.Windows.Forms.GroupBox groupBox6; + private System.Windows.Forms.TextBox tb_AircraftDatabase_RemoteHost; + private System.Windows.Forms.Label label14; + private System.Windows.Forms.TextBox tb_AircraftDatabase_Password; + private System.Windows.Forms.Label label15; + private System.Windows.Forms.TextBox tb_AircraftDatabase_User; + private System.Windows.Forms.Label label16; + private System.Windows.Forms.Button btn_AircraftDatabase_Export; + private System.Windows.Forms.TextBox tb_AircraftDatabase_RemoteDir; + private System.Windows.Forms.Label label17; + private System.Windows.Forms.TextBox tb_AircraftDabase_LocalDir; + private System.Windows.Forms.Label label18; + } +} + diff --git a/AirScoutDatabaseManager/MainDlg.cs b/AirScoutDatabaseManager/MainDlg.cs new file mode 100644 index 0000000..578c965 --- /dev/null +++ b/AirScoutDatabaseManager/MainDlg.cs @@ -0,0 +1,1898 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using System.Net; +using System.IO; +using System.Globalization; +using System.Threading; +using System.Xml; +using System.Xml.Linq; +using GMap.NET; +using GMap.NET.MapProviders; +using GMap.NET.WindowsForms; +using GMap.NET.WindowsForms.Markers; +using GMap.NET.WindowsForms.ToolTips; +using ScoutBase; +using ScoutBase.Core; +using ScoutBase.Elevation; +using ScoutBase.Stations; +using SQLiteDatabase; +using Newtonsoft.Json; +using HtmlAgilityPack; +using AirScout.Aircrafts; +using Newtonsoft.Json.Linq; +using Renci.SshNet.Sftp; +using Renci.SshNet; + +namespace AirScoutDatabaseManager +{ + public partial class MainDlg : Form + { + + [CategoryAttribute("Directories")] + [DescriptionAttribute("Application Directory")] + public string AppDirectory + { + get + { + return Application.StartupPath.TrimEnd(Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar); + } + } + + [CategoryAttribute("Directories")] + [DescriptionAttribute("Local Application Data Directory")] + public string AppDataDirectory + { + get + { + return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), Application.CompanyName, Application.ProductName).TrimEnd(Path.DirectorySeparatorChar); + } + } + + [CategoryAttribute("Directories")] + [DescriptionAttribute("Logfile Directory")] + public string LogDirectory + { + get + { + // get Property + string logdir = Properties.Settings.Default.Log_Directory; + // replace Windows/Linux directory spearator chars + logdir = logdir.Replace('\\', Path.DirectorySeparatorChar); + logdir = logdir.Replace('/', Path.DirectorySeparatorChar); + // set to default value if empty + if (String.IsNullOrEmpty(logdir)) + logdir = "Log"; + // replace variables, if any + logdir = VC.ReplaceAllVars(logdir); + // remove directory separator chars at begin and end + logdir = logdir.TrimStart(Path.DirectorySeparatorChar); + logdir = logdir.TrimEnd(Path.DirectorySeparatorChar); + // fully qualify path + if (!logdir.Contains(Path.VolumeSeparatorChar)) + logdir = Path.Combine(AppDataDirectory, logdir); + return logdir; + } + } + + [CategoryAttribute("Directories")] + [DescriptionAttribute("Tempfile Directory")] + public string TmpDirectory + { + get + { + // get Property + string tmpdir = Properties.Settings.Default.Tmp_Directory; + // replace Windows/Linux directory spearator chars + tmpdir = tmpdir.Replace('\\', Path.DirectorySeparatorChar); + tmpdir = tmpdir.Replace('/', Path.DirectorySeparatorChar); + // set to default value if empty + if (String.IsNullOrEmpty(tmpdir)) + tmpdir = "Tmp"; + // replace variables, if any + tmpdir = VC.ReplaceAllVars(tmpdir); + // remove directory separator chars at begin and end + tmpdir = tmpdir.TrimStart(Path.DirectorySeparatorChar); + tmpdir = tmpdir.TrimEnd(Path.DirectorySeparatorChar); + // fully qualify path + if (!tmpdir.Contains(Path.VolumeSeparatorChar)) + tmpdir = Path.Combine(AppDataDirectory, tmpdir); + return tmpdir; + } + } + + [CategoryAttribute("Directories")] + [DescriptionAttribute("Database Directory")] + public string DatabaseDirectory + { + get + { + // get Property + string databasedir = Properties.Settings.Default.Database_Directory; + // replace Windows/Linux directory spearator chars + databasedir = databasedir.Replace('\\', Path.DirectorySeparatorChar); + databasedir = databasedir.Replace('/', Path.DirectorySeparatorChar); + // set to default value if empty + if (String.IsNullOrEmpty(databasedir)) + databasedir = "Database"; + // replace variables, if any + databasedir = VC.ReplaceAllVars(databasedir); + // remove directory separator chars at begin and end + databasedir = databasedir.TrimStart(Path.DirectorySeparatorChar); + databasedir = databasedir.TrimEnd(Path.DirectorySeparatorChar); + // fully qualify path + if (!databasedir.Contains(Path.VolumeSeparatorChar)) + databasedir = Path.Combine(AppDataDirectory, databasedir); + return databasedir; + } + } + + + [CategoryAttribute("Directories")] + [DescriptionAttribute("Tempfile Directory")] + public string ExportDirectory + { + get + { + // get Property + string expdir = Properties.Settings.Default.Export_Directory; + // replace Windows/Linux directory spearator chars + expdir = expdir.Replace('\\', Path.DirectorySeparatorChar); + expdir = expdir.Replace('/', Path.DirectorySeparatorChar); + // set to default value if empty + if (String.IsNullOrEmpty(expdir)) + expdir = "Tmp"; + // replace variables, if any + expdir = VC.ReplaceAllVars(expdir); + // remove directory separator chars at begin and end + expdir = expdir.TrimStart(Path.DirectorySeparatorChar); + expdir = expdir.TrimEnd(Path.DirectorySeparatorChar); + // fully qualify path + if (!expdir.Contains(Path.VolumeSeparatorChar)) + expdir = Path.Combine(AppDataDirectory, expdir); + return expdir; + } + } + + GMapOverlay Coverageoverlay = new GMapOverlay("Coveragepolygons"); + GMapOverlay Locationsoverlay = new GMapOverlay("Locations"); + + DataSet SB = new DataSet(); + + DataTableLocations Locations = new DataTableLocations(); + DataView LocationsView; + + DataTable QRV = new DataTable(); + DataView QRVView; + + NumberFormatInfo ENprovider; + + public LogWriter Log = LogWriter.Instance; + public VarConverter VC = new VarConverter(); + + private bool IsMarkerDragging = false; + private bool IsMarkerDragged = false; + + private GMarkerGoogle CurrentMarker = null; + private double CurrentMarkerLat; + private double CurrentMarkerLon; + + private double OfsLat, OfsLon; + + public MainDlg() + { + InitializeComponent(); + ENprovider = new NumberFormatInfo(); + ENprovider.NumberDecimalSeparator = "."; + ENprovider.NumberGroupSeparator = ","; + CheckDirectories(); + // set directories and formats for logfile + ScoutBase.Core.Properties.Settings.Default.LogWriter_Directory = LogDirectory; + ScoutBase.Core.Properties.Settings.Default.LogWriter_FileFormat = "Log_{0:yyyy_MM_dd}.log"; + ScoutBase.Core.Properties.Settings.Default.LogWriter_MessageFormat = "{0:u} {1}"; + Locations.RowChanged += new DataRowChangeEventHandler(Locations_Row_Changed); + QRV.RowChanged += new DataRowChangeEventHandler(QRV_Row_Changed); + tc_Main.Enabled = false; + } + + private void MainDlg_Load(object sender, EventArgs e) + { + Log.WriteMessage("Starting up."); + // set initial settings for CoverageMap + GMap.NET.MapProviders.GMapProvider.UserAgent = "AirScout"; + gm_Coverage.MapProvider = GMapProviders.Find(Properties.Settings.Default.Map_Provider); + gm_Coverage.IgnoreMarkerOnMouseWheel = true; + gm_Coverage.MinZoom = 0; + gm_Coverage.MaxZoom = 20; + gm_Coverage.Zoom = 6; + gm_Coverage.DragButton = System.Windows.Forms.MouseButtons.Left; + gm_Coverage.CanDragMap = true; + gm_Coverage.ScalePen = new Pen(Color.Black, 3); + gm_Coverage.HelperLinePen = null; + gm_Coverage.SelectionPen = null; + gm_Coverage.MapScaleInfoEnabled = true; + gm_Coverage.Overlays.Add(Coverageoverlay); + // set initial settings for locations map + GMap.NET.MapProviders.GMapProvider.UserAgent = "AirScout"; + gm_Locations.MapProvider = GMapProviders.Find(Properties.Settings.Default.Map_Provider); + gm_Locations.IgnoreMarkerOnMouseWheel = true; + gm_Locations.MinZoom = 0; + gm_Locations.MaxZoom = 20; + gm_Locations.Zoom = 6; + gm_Locations.DragButton = System.Windows.Forms.MouseButtons.Left; + gm_Locations.CanDragMap = true; + gm_Locations.ScalePen = new Pen(Color.Black, 3); + gm_Locations.HelperLinePen = null; + gm_Locations.SelectionPen = null; + gm_Locations.MapScaleInfoEnabled = true; + gm_Locations.Overlays.Add(Locationsoverlay); + gm_Locations.ShowCenter = false; + // initialize QRV table + DataColumn qrv_call = new DataColumn("Call", typeof(string)); + QRV.Columns.Add(qrv_call); + DataColumn qrv_loc = new DataColumn("Loc", typeof(string)); + QRV.Columns.Add(qrv_loc); + QRV.PrimaryKey = new DataColumn[2] { qrv_call, qrv_loc }; + string[] bands = Bands.GetStringValuesExceptNoneAndAll(); + foreach (string band in bands) + { + if (!band.StartsWith("50M") && !band.StartsWith("70M")) + { + QRV.Columns.Add(band + "_AH", typeof(double)); + QRV.Columns.Add(band + "_AG", typeof(double)); + QRV.Columns.Add(band + "_P", typeof(double)); + } + } + QRV.Columns.Add("LastUpdated", typeof(DateTime)); + // initilize databases + AircraftData.Database.GetDBLocation(); + StationData.Database.GetDBLocation(); + bw_DatabaseUpdater.RunWorkerAsync(); + } + + private void MainDlg_FormClosing(object sender, FormClosingEventArgs e) + { + Properties.Settings.Default.Save(); + bw_QRZ.CancelAsync(); + Log.WriteMessage("Closing."); + } + + public void CheckDirectories() + { + // check if directories exist + if (!Directory.Exists(TmpDirectory)) + Directory.CreateDirectory(TmpDirectory); + if (!Directory.Exists(LogDirectory)) + Directory.CreateDirectory(LogDirectory); + if (!Directory.Exists(ExportDirectory)) + Directory.CreateDirectory(ExportDirectory); + } + + + private void Say(string text) + { + if (tsl_Status.Text != text) + { + tsl_Status.Text = text; + ss_Main.Refresh(); + } + } + + private void SayLocations(string text) + { + if (tb_Locations_Status.Text != text) + { + tb_Locations_Status.Text = text; + tb_Locations_Status.Refresh(); + Application.DoEvents(); + } + } + + private void SayQRV(string text) + { + if (tb_QRV_Status.Text != text) + { + tb_QRV_Status.Text = text; + tb_QRV_Status.Refresh(); + Application.DoEvents(); + } + } + + #region tp_General + + private void tp_General_Enter(object sender, EventArgs e) + { + tp_General_Update(this, null); + } + + private void tp_General_Update(object sender, EventArgs e) + { + Coverageoverlay.Clear(); + // add tile to map polygons + List l = new List(); + l.Add(new PointLatLng(tb_Coverage_MinLat.Value, tb_Coverage_MinLon.Value)); + l.Add(new PointLatLng(tb_Coverage_MinLat.Value, tb_Coverage_MaxLon.Value)); + l.Add(new PointLatLng(tb_Coverage_MaxLat.Value, tb_Coverage_MaxLon.Value)); + l.Add(new PointLatLng(tb_Coverage_MaxLat.Value, tb_Coverage_MinLon.Value)); + GMapPolygon p = new GMapPolygon(l, "Coverage"); + p.Stroke = new Pen(Color.FromArgb(255, Color.Magenta), 3); + p.Fill = new SolidBrush(Color.FromArgb(0, Color.Magenta)); + Coverageoverlay.Polygons.Add(p); + // zoom the map + gm_Coverage.SetZoomToFitRect(RectLatLng.FromLTRB(tb_Coverage_MinLon.Value - 1, tb_Coverage_MaxLat.Value + 1, tb_Coverage_MaxLon.Value + 1, tb_Coverage_MinLat.Value - 1)); + } + + + #endregion + + #region tp_Locations + + private void tp_Locations_Enter(object sender, EventArgs e) + { + // clear map + Locationsoverlay.Clear(); + Locations.Clear(); + Locations.Merge(StationData.Database.LocationToDataTable()); + Locations.AcceptChanges(); + LocationsView = new DataView(Locations); + BindingSource source = new BindingSource(); + source.DataSource = LocationsView; + dgv_Locations.DataSource = source; + dgv_Locations.ShowRowErrors = true; + } + + private void btn_QRZ_Start_Click(object sender, EventArgs e) + { + if (!bw_QRZ.IsBusy) + bw_QRZ.RunWorkerAsync(); + } + + private void btn_QRZ_Stop_Click(object sender, EventArgs e) + { + bw_QRZ.CancelAsync(); + } + + private void btn_Locations_Sort_Click(object sender, EventArgs e) + { + // sort data table + DataTableLocations sorted = (DataTableLocations)Locations.Clone(); + DataRow[] rows = Locations.Select("", "Call ASC"); + if (rows.Length > 0) + { + foreach (DataRow row in rows) + sorted.ImportRow(row); + } + Locations.Clear(); + foreach (DataRow row in sorted.Rows) + Locations.ImportRow(row); + } + + private void btn_Locations_Save_Click(object sender, EventArgs e) + { + SayLocations("Saving changes to database..."); + foreach (DataRow row in Locations.Rows) + { + LocationDesignator ld = new LocationDesignator(row); + StationData.Database.LocationInsertOrUpdateIfNewer(ld); + } + SayLocations("Finished."); + } + + private void btn_Locations_Export_Click(object sender, EventArgs e) + { + string filename = Path.Combine(ExportDirectory, "locations.json"); + SayLocations("Exporting database to " + filename); + string json = StationData.Database.LocationToJSON(); + SupportFunctions.WriteStringToFile(json, filename); + SayLocations("Finished."); + } + + private void btn_Locations_Import_AirScout_Click(object sender, EventArgs e) + { + } + + private void btn_Locations_Import_CALL3_Click(object sender, EventArgs e) + { + } + + private void btn_Locations_Import_DTB_Click(object sender, EventArgs e) + { + } + + private void btn_Locations_Import_CSV_Click(object sender, EventArgs e) + { + } + + private void btn_Locations_Import_USR_Click(object sender, EventArgs e) + { + FolderBrowserDialog Dlg = new FolderBrowserDialog(); + Dlg.ShowNewFolderButton = false; + if (Dlg.ShowDialog() == DialogResult.OK) + { + DataTableLocations dt = new DataTableLocations(); + string[] files = Directory.GetFiles(Dlg.SelectedPath, "*.usr"); + foreach (string file in files) + { + try + { + SayLocations("Importing " + file + "..."); + string s = ""; + using (StreamReader sr = new StreamReader(File.OpenRead(file))) + { + while (!sr.EndOfStream) + { + s = sr.ReadLine(); + if (!String.IsNullOrEmpty(s) && !s.StartsWith("//")) + { + string[] a = s.Split(';'); + // store array values in DataTable + DataRow row = dt.NewRow(); + string call = a[0]; + if (Callsign.Check(call)) + { + double lat = System.Convert.ToDouble(a[1], CultureInfo.InvariantCulture); + double lon = System.Convert.ToDouble(a[2], CultureInfo.InvariantCulture); + GEOSOURCE source = (MaidenheadLocator.IsPrecise(lat, lon, 3) ? GEOSOURCE.FROMUSER : GEOSOURCE.FROMLOC); + string lastupdated = a[6]; + DateTime lu = System.Convert.ToDateTime(lastupdated).ToUniversalTime(); + if (GeographicalPoint.Check(lat, lon)) + { + row["Call"] = call; + row["Lat"] = lat; + row["Lon"] = lon; + row["Source"] = source; + row["LastUpdated"] = lu; + dt.Rows.Add(row); + } + } + } + } + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + ImportLocations(dt); + } + } + + private void cb_Locations_ChangedOnly_CheckedChanged(object sender, EventArgs e) + { + if (cb_Locations_ChangedOnly.Checked) + { + LocationsView.RowStateFilter = DataViewRowState.ModifiedCurrent | DataViewRowState.Added; + } + else + { + LocationsView.RowStateFilter = DataViewRowState.CurrentRows; + } + } + + private void gm_Locations_MouseDown(object sender, MouseEventArgs e) + { + if (e.Button == MouseButtons.Left && CurrentMarker != null && CurrentMarker.IsMouseOver) + { + // get geographic coordinates of mouse pointer and calulate offsets + PointLatLng p = gm_Locations.FromLocalToLatLng(e.X, e.Y); + OfsLat = p.Lat - CurrentMarker.Position.Lat; + OfsLon = p.Lng - CurrentMarker.Position.Lng; + IsMarkerDragging = true; + IsMarkerDragged = false; + CurrentMarkerLat = CurrentMarker.Position.Lat; + CurrentMarkerLon = CurrentMarker.Position.Lng; + foreach (DataGridViewRow row in dgv_Locations.Rows) + { + try + { + string call = row.Cells["Call"].Value.ToString(); + string markercall = (string)CurrentMarker.Tag; + if (String.Equals(call, markercall)) + { + dgv_Locations.ClearSelection(); + row.Selected = true; + dgv_Locations.FirstDisplayedScrollingRowIndex = row.Index; + break; + } + } + catch + { + + } + } + } + } + + private void gm_Locations_MouseMove(object sender, MouseEventArgs e) + { + if (IsMarkerDragging && (CurrentMarker != null)) + { + // get geographic coordinates of mouse pointer + PointLatLng p = gm_Locations.FromLocalToLatLng(e.X, e.Y); + p.Lat = p.Lat - OfsLat; + p.Lng = p.Lng - OfsLon; + CurrentMarker.Position = p; + GPoint c = gm_Locations.FromLatLngToLocal(new PointLatLng(CurrentMarkerLat, CurrentMarkerLon)); + if ((Math.Abs(c.X - e.X) > 20) || (Math.Abs(c.Y - e.Y) > 20)) + { + IsMarkerDragged = true; + } + } + } + + private void gm_Locations_MouseUp(object sender, MouseEventArgs e) + { + if (CurrentMarker != null) + { + if (IsMarkerDragged) + { + // get geographic coordinates of mouse pointer + PointLatLng p = gm_Locations.FromLocalToLatLng(e.X, e.Y); + double lat = p.Lat - OfsLat; + double lon = p.Lng - OfsLon; + string call = CurrentMarker.Tag.ToString(); + string loc = MaidenheadLocator.LocFromLatLon(lat, lon, false, 3); + GEOSOURCE source = (MaidenheadLocator.IsPrecise(lat, lon, 3) ? GEOSOURCE.FROMUSER : GEOSOURCE.FROMLOC); + DataRow oldrow = Locations.Rows.Find(new string[] { call, loc }); + if (oldrow != null) + { + // call found --> check for update + if ((double)oldrow["Lat"] != lat) + { + oldrow["Lat"] = lat; + AddRowError(oldrow, "UPDATED", "Lat", "UpdatedValue", "OldValue:" + ((double)oldrow["Lat"]).ToString("F8", CultureInfo.InvariantCulture)); + } + if ((double)oldrow["Lon"] != lon) + { + oldrow["Lon"] = lon; + AddRowError(oldrow, "UPDATED", "Lon", "UpdatedValue", "OldValue:" + ((double)oldrow["Lon"]).ToString("F8", CultureInfo.InvariantCulture)); + } + oldrow["Source"] = source; + AddRowError(oldrow, "UPDATED", "Source", "UpdatedValue", "OldValue:" + oldrow["Source"].ToString()); + oldrow["LastUpdated"] = DateTime.UtcNow; + } + else + { + // marker is mpved beyond old locator + // create new line + if (MessageBox.Show("Marker is moved to a different locator which is not in database so far. Create new entry?","Create new entry", MessageBoxButtons.YesNo) == DialogResult.Yes) + { + DataRow row = Locations.NewRow(); + row["Call"] = call; + row["Loc"] = loc; + row["Lat"] = lat; + row["Lon"] = lon; + row["Source"] = source; + row["Hits"] = 0; + row["LastUpdated"] = DateTime.UtcNow; + Locations.Rows.Add(row); + } + } + } + else + { + // restore original marker position + CurrentMarker.Position = new PointLatLng(CurrentMarkerLat, CurrentMarkerLon); + } + } + gm_Locations.CanDragMap = true; + IsMarkerDragging = false; + IsMarkerDragged = false; + } + + private void gm_Locations_OnMarkerEnter(GMapMarker item) + { + CurrentMarker = (GMarkerGoogle)item; + } + + private void gm_Locations_OnMarkerLeave(GMapMarker item) + { + // CurrentMarker = null; + } + + private void tb_Locations_Callsign_Filter_TextChanged(object sender, EventArgs e) + { + if (String.IsNullOrEmpty(tb_Locations_Callsign_Filter.Text)) + { + LocationsView.RowFilter = "Call LIKE '*'"; + return; + } + string filter = tb_Locations_Callsign_Filter.Text; + if (!filter.EndsWith("*")) + filter = filter + "*"; + LocationsView.RowFilter = "Call LIKE '" + filter + "'"; + } + + #endregion + + private void dgv_Locations_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) + { + if ((e.RowIndex < 0) || (e.RowIndex >= dgv_Locations.Rows.Count)) + return; + DataGridViewRow dgvrow = dgv_Locations.Rows[e.RowIndex]; + if ((e.ColumnIndex < 0) || (e.ColumnIndex >= dgvrow.Cells.Count)) + return; + DataGridViewCell cell = dgvrow.Cells[e.ColumnIndex]; + if (!cell.Displayed) + return; + if (String.IsNullOrEmpty(dgvrow.ErrorText)) + return; + XElement xml = XElement.Parse(dgvrow.ErrorText); + LOCATIONSTATE state = LOCATIONSTATE.UNKNOWN; + try + { + state = (LOCATIONSTATE)Enum.Parse(typeof(LOCATIONSTATE), xml.Name.ToString()); + } + catch + { + + } + if (state == LOCATIONSTATE.ERROR) + cell.Style.BackColor = Color.Red; + else if (state == LOCATIONSTATE.LOCDIFF) + cell.Style.BackColor = Color.Khaki; + else if (state == LOCATIONSTATE.UPTODATE) + cell.Style.BackColor = Color.LightGreen; + else if (state == LOCATIONSTATE.ADDED) + cell.Style.BackColor = Color.LightBlue; + else if (state == LOCATIONSTATE.UPDATED) + { + string s = xml.ToString(); + string propertyname = dgv_Locations.Columns[e.ColumnIndex].DataPropertyName; + if (s.IndexOf("<" + propertyname + " />") >= 0) + { + cell.Style.BackColor = Color.Bisque; + } + } + } + + private void bw_QRZ_DoWork(object sender, DoWorkEventArgs e) + { + // check callsign location against QRZ.com entry + // name current thread + if (String.IsNullOrEmpty(Thread.CurrentThread.Name)) + Thread.CurrentThread.Name = "QRZ"; + int callschecked = 0; + int callsfound = 0; + int callsnotfound = 0; + int callsuptodate = 0; + int callsupdated = 0; + int callsdiffloc = 0; + int errors = 0; + // get session key + WebRequest myWebRequest = WebRequest.Create(Properties.Settings.Default.QRZ_URL_Login); + myWebRequest.Timeout = 10000; + WebResponse myWebResponse = myWebRequest.GetResponse(); + Stream ReceiveStream = myWebResponse.GetResponseStream(); + Encoding encode = System.Text.Encoding.GetEncoding("utf-8"); + StreamReader readStream = new StreamReader(ReceiveStream, encode); + string s = readStream.ReadToEnd(); + XmlDocument doc = new XmlDocument(); + doc.LoadXml(s); + var nodes = doc.GetElementsByTagName("Key"); + string sessionkey = nodes[0].InnerText; + foreach (DataRow row in Locations.Rows) + { + try + { + callschecked++; + string call = row["Call"].ToString(); + double lat = (double)row["Lat"]; + double lon = (double)row["Lon"]; + GEOSOURCE source = (GEOSOURCE)row["Source"]; + DateTime lastupdated = (DateTime)row["LastUpdated"]; + string loc = MaidenheadLocator.LocFromLatLon(lat, lon, false, 3); + string qrzloc = ""; + double qrzlat = 0; + double qrzlon = 0; + string geoloc = ""; + string addr1 = ""; + string addr2 = ""; + string zip = ""; + string country = ""; + string error = ""; + // get xml data + myWebRequest = WebRequest.Create(Properties.Settings.Default.QRZ_URL_XMLData + "?s=" + sessionkey + ";callsign=" + call); + myWebRequest.Timeout = 10000; + myWebResponse = myWebRequest.GetResponse(); + ReceiveStream = myWebResponse.GetResponseStream(); + encode = System.Text.Encoding.GetEncoding("utf-8"); + readStream = new StreamReader(ReceiveStream, encode); + s = readStream.ReadToEnd(); + // load xml document + doc = new XmlDocument(); + doc.LoadXml(s); + // check for errors + nodes = doc.GetElementsByTagName("Error"); + if (nodes.Count > 0) + { + error = nodes[0].InnerText; + if (error.ToUpper().Contains("NOT FOUND")) + { + callsnotfound++; + } + else if (error.ToUpper().Contains("SESSION TIMEOUT")) + { + // session timeout --> obtain a new session key and try again + myWebRequest = WebRequest.Create(Properties.Settings.Default.QRZ_URL_Login); + myWebRequest.Timeout = 10000; + myWebResponse = myWebRequest.GetResponse(); + ReceiveStream = myWebResponse.GetResponseStream(); + encode = System.Text.Encoding.GetEncoding("utf-8"); + readStream = new StreamReader(ReceiveStream, encode); + s = readStream.ReadToEnd(); + doc = new XmlDocument(); + doc.LoadXml(s); + nodes = doc.GetElementsByTagName("Key"); + sessionkey = nodes[0].InnerText; + bw_QRZ.ReportProgress((int)LOCATIONSTATE.INFO, "Obtained new session key: " + sessionkey); + // get xml data + myWebRequest = WebRequest.Create(Properties.Settings.Default.QRZ_URL_XMLData + "?s=" + sessionkey + ";callsign=" + call); + myWebRequest.Timeout = 10000; + myWebResponse = myWebRequest.GetResponse(); + ReceiveStream = myWebResponse.GetResponseStream(); + encode = System.Text.Encoding.GetEncoding("utf-8"); + readStream = new StreamReader(ReceiveStream, encode); + s = readStream.ReadToEnd(); + // load xml document + doc = new XmlDocument(); + doc.LoadXml(s); + } + else + { + // report error + errors++; + bw_QRZ.ReportProgress((int)LOCATIONSTATE.ERROR, error); + } + } + // write xml to file + else + { + using (StreamWriter sw = new StreamWriter(call.Replace("/", "_") + ".xml")) + { + sw.WriteLine(s); + } + callsfound++; + nodes = doc.GetElementsByTagName("lat"); + if (nodes.Count > 0) + qrzlat = System.Convert.ToDouble(nodes[0].InnerText, CultureInfo.InvariantCulture); + nodes = doc.GetElementsByTagName("lon"); + if (nodes.Count > 0) + qrzlon = System.Convert.ToDouble(nodes[0].InnerText, CultureInfo.InvariantCulture); + nodes = doc.GetElementsByTagName("grid"); + if (nodes.Count > 0) + qrzloc = nodes[0].InnerText.ToUpper().Trim(); + nodes = doc.GetElementsByTagName("geoloc"); + if (nodes.Count > 0) + geoloc = nodes[0].InnerText; + nodes = doc.GetElementsByTagName("addr1"); + if (nodes.Count > 0) + addr1 = nodes[0].InnerText; + nodes = doc.GetElementsByTagName("addr2"); + if (nodes.Count > 0) + addr2 = nodes[0].InnerText; + nodes = doc.GetElementsByTagName("zip"); + if (nodes.Count > 0) + zip = nodes[0].InnerText; + nodes = doc.GetElementsByTagName("country"); + if (nodes.Count > 0) + country = nodes[0].InnerText; + // different loc? + if (loc != qrzloc) + { + Log.WriteMessage("QRZ.COM: Locator is different [" + call + "]: " + loc + " <> " + qrzloc); + callsdiffloc++; + } + // precise location by user or geocode? + else if (geoloc.ToUpper().Contains("USER") || geoloc.ToUpper().Contains("GEOCODE")) + { + if ((qrzlat != lat) || (qrzlon != lon)) + { + Log.WriteMessage("QRZ.COM: Location updated [" + call + "]."); + callsupdated++; + LocationDesignator ld = new LocationDesignator(call, qrzlat, qrzlon, GEOSOURCE.FROMUSER); + bw_QRZ.ReportProgress((int)LOCATIONSTATE.UPDATED, ld); + } + else + { + // already up to date + Log.WriteMessage("QRZ.COM: Location up to date [" + call + "]."); + callsuptodate++; + } + } + else if (geoloc.ToUpper().Contains("GRID")) + { + // try to get info by OpenStreetMaps API + string url = "https://nominatim.openstreetmap.org/search?q=" + addr1 + "+" + addr2 + "+" + zip + "+" + country + "&format=xml&polygon=1&addressdetails=1"; + HttpWebRequest httpWebRequest = (HttpWebRequest)HttpWebRequest.Create(url); + httpWebRequest.UserAgent = "Mozilla / 5.0(Windows NT 10.0; Win64; x64; rv: 61.0) Gecko / 20100101 Firefox / 61.0"; + HttpWebResponse httpWebResponse= (HttpWebResponse)httpWebRequest.GetResponse(); + ReceiveStream = httpWebResponse.GetResponseStream(); + // encode = System.Text.Encoding.GetEncoding("utf-8"); + readStream = new StreamReader(ReceiveStream, encode); + s = readStream.ReadToEnd(); + // load xml document + doc = new XmlDocument(); + doc.LoadXml(s); + double glat = 0; + double glon = 0; + string gloc = ""; + nodes = doc.GetElementsByTagName("place"); + if (nodes.Count > 0) + { + glat = System.Convert.ToDouble(nodes[0].Attributes["lat"].Value.ToString(), CultureInfo.InvariantCulture); + glon = System.Convert.ToDouble(nodes[0].Attributes["lon"].Value.ToString(), CultureInfo.InvariantCulture); + } + gloc = MaidenheadLocator.LocFromLatLon(glat, glon, false, 3); + if (gloc == qrzloc) + { + // precise location from address + Log.WriteMessage("QRZ.COM: Location updated from postal address[" + call + "]."); + callsupdated++; + LocationDesignator ld = new LocationDesignator(call, glat, glon, GEOSOURCE.FROMUSER); + bw_QRZ.ReportProgress((int)LOCATIONSTATE.UPDATED, ld); + } + else + { + Log.WriteMessage("QRZ.COM: Locator is different [" + call + "]: " + loc + " <> " + qrzloc); + callsdiffloc++; + } + } + // alreadyup to date? + else if ((lat == qrzlat) && (lon == qrzlon)) + { + callsuptodate++; + } + } + string status = "QRZ.COM query is running: " + + callschecked.ToString() + " checked, " + + callsnotfound.ToString() + " not found, " + + callsfound.ToString() + " found, " + + callsuptodate.ToString() + " up to date, " + + callsupdated.ToString() + " updated, " + + callsdiffloc.ToString() + " different loc, " + + errors.ToString() + " errors"; + bw_QRZ.ReportProgress((int)LOCATIONSTATE.INFO, status); + if (bw_QRZ.CancellationPending) + return; + Thread.Sleep(10); + } + catch (Exception ex) + { + errors++; + bw_QRZ.ReportProgress((int)LOCATIONSTATE.ERROR, ex.Message); + Log.WriteMessage(ex.Message); + } + } + } + + private void bw_QRZ_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + LOCATIONSTATE state = (LOCATIONSTATE)e.ProgressPercentage; + if (state <= 0) + SayLocations((string)e.UserState); + else + { + LocationDesignator ld = (LocationDesignator)e.UserState; + // update data table + try + { + DataRow row = Locations.Rows.Find(new string[2] { ld.Call, ld.Loc }); + if (row != null) + { + if (state == LOCATIONSTATE.UPTODATE) + AddRowError(row, state.ToString(), "", "", ""); + else if (state == LOCATIONSTATE.LOCDIFF) + AddRowError(row, state.ToString(), "", "", ""); + else + { + if ((double)row["Lat"] != ld.Lat) + { + AddRowError(row, state.ToString(), "Lat", "UpdatedValue", "OldValue:" + ((double)row["Lat"]).ToString("F8", CultureInfo.InvariantCulture)); + row["Lat"] = ld.Lat; + } + if ((double)row["Lon"] != ld.Lon) + { + AddRowError(row, state.ToString(), "Lon", "UpdatedValue", "OldValue:" + ((double)row["Lon"]).ToString("F8", CultureInfo.InvariantCulture)); + row["Lon"] = ld.Lon; + } + row["Source"] = ld.Source; + row["LastUpdated"] = ld.LastUpdated; + } + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.Message); + } + } + } + + private void bw_QRZ_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + SayLocations("Finished."); + } + + + private void AddRowError(DataRow row, string category, string node, string item, string text) + { + try + { + // create XML document if not already created + if (String.IsNullOrEmpty(row.RowError)) + { + XElement x = new XElement(category); + row.RowError = x.ToString(); + } + // read out Errors as XML from RowError + XElement xml = XElement.Parse(row.RowError); + if (String.IsNullOrEmpty(node)) + return; + xml.Add(new XElement(node)); + if (String.IsNullOrEmpty(item)) + return; + xml.Add(new XElement(item, text)); + row.RowError = xml.ToString(); + } + catch (Exception ex) + { + Log.WriteMessage(ex.Message); + } + } + + private void ImportLocations(DataTable dt) + { + if (dt != null) + { + int callsimported = dt.Rows.Count; + int callsupdated = 0; + int callsadded = 0; + foreach (DataRow row in dt.Rows) + { + DataRow oldrow = Locations.Rows.Find(row["Call"].ToString()); + if (oldrow != null) + { + // call found --> check for update + if ((DateTime)row["LastUpdated"] > (DateTime)oldrow["LastUpdated"]) + { + if (oldrow["Lat"] != row["Lat"]) + { + oldrow["Lat"] = row["Lat"]; + AddRowError(oldrow, "UPDATED", "Lat", "UpdatedValue", "OldValue:" + ((double)oldrow["Lat"]).ToString("F8", CultureInfo.InvariantCulture)); + } + if (oldrow["Lon"] != row["Lon"]) + { + oldrow["Lon"] = row["Lon"]; + AddRowError(oldrow, "UPDATED", "Lon", "UpdatedValue", "OldValue:" + ((double)oldrow["Lon"]).ToString("F8", CultureInfo.InvariantCulture)); + } + if (oldrow["Source"] != row["Source"]) + { + oldrow["Source"] = row["Source"]; + AddRowError(oldrow, "UPDATED", "Source", "UpdatedValue", "OldValue:" + oldrow["Source"].ToString()); + } + oldrow["LastUpdated"] = row["LastUpdated"]; + callsupdated++; + } + } + else + { + // add new row + AddRowError(row, LOCATIONSTATE.ADDED.ToString(), "", "", ""); + Locations.ImportRow(row); + callsadded++; + } + } + SayLocations("Import of " + dt.TableName + " finished: " + callsimported.ToString() + " calls imported, " + callsadded.ToString() + " calls added, " + callsupdated.ToString() + " calls updated."); + } + } + + private void Locations_Row_Changed(object sender, DataRowChangeEventArgs e) + { + + try + { + string call = e.Row["Call"].ToString(); + double lat = (double)e.Row["Lat"]; + double lon = (double)e.Row["Lon"]; + GEOSOURCE source = (GEOSOURCE)Enum.Parse(typeof(GEOSOURCE), e.Row["Source"].ToString()); + if (e.Action == DataRowAction.Add) + { + GMarkerGoogle gm = new GMarkerGoogle(new PointLatLng(lat, lon), (source == GEOSOURCE.FROMUSER) ? GMarkerGoogleType.green_small : GMarkerGoogleType.white_small); + gm.ToolTipText = call; + gm.ToolTipMode = MarkerTooltipMode.OnMouseOver; + gm.Tag = call; + Locationsoverlay.Markers.Add(gm); + } + else if (e.Action == DataRowAction.Change) + { + GMarkerGoogle gm = (GMarkerGoogle)Locationsoverlay.Markers.First(c => (string)c.Tag == call); + if (gm != null) + gm.Position = new PointLatLng(lat, lon); + } + else if (e.Action == DataRowAction.Delete) + { + GMarkerGoogle gm = (GMarkerGoogle)Locationsoverlay.Markers.First(c => (string)c.Tag == call); + if (gm != null) + Locationsoverlay.Markers.Remove(gm); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.Message); + } + } + + private void btn_QRV_Import_WinTest_Click(object sender, EventArgs e) + { + DataTable dt = QRV.Clone(); + dt.Rows.Clear(); + OpenFileDialog Dlg = new OpenFileDialog(); + Dlg.DefaultExt = ".xdt"; + Dlg.Filter = "Win-Test database|*.xdt"; + Dlg.CheckFileExists = true; + if (Dlg.ShowDialog() == DialogResult.OK) + { + try + { + int qrv_ok = 0; + int qrv_err = 0; + using (StreamReader sr = new StreamReader(File.OpenRead(Dlg.FileName))) + { + while (!sr.EndOfStream) + { + string s = sr.ReadLine().Trim(); + string[] a = s.Split(); + DataRow row = dt.NewRow(); + row["Call"] = a[0]; + SayQRV("Importing call: " + a[0] + "..."); + DateTime lastupdated = DateTime.MinValue; + try + { + a[1] = a[1].Replace("[", "").Replace("]", ""); + lastupdated = DateTime.ParseExact(a[1], "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal); + lastupdated = lastupdated.ToUniversalTime(); + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + qrv_err++; + } + row["LastUpdated"] = lastupdated; + for (int i = 2; i < a.Length; i++) + { + a[i] = a[i].Trim(); + if (!String.IsNullOrEmpty(a[i])) + { + try + { + row[a[i] + "_AH"] = 0; + row[a[i] + "_AG"] = 0; + row[a[i] + "_P"] = 0; + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + qrv_err++; + } + } + } + dt.Rows.Add(row); + qrv_ok++; + } + } + QRV.Merge(dt); + SayQRV("Importing calls finished: " + qrv_ok + " calls, " + qrv_err + " error(s)."); + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + } + + private void tp_QRV_Enter(object sender, EventArgs e) + { + QRV.Rows.Clear(); + BAND[] bands = Bands.GetValuesExceptNoneAndAll(); + foreach (BAND band in bands) + { + if (band > BAND.B70M) + { + SayQRV("Importing band: " + Bands.GetStringValue(band) + "..."); + List qrvs = StationData.Database.QRVGetAll(band); + string band_ah = Bands.GetStringValue(band) + "_AH"; + string band_ag = Bands.GetStringValue(band) + "_AG"; + string band_p = Bands.GetStringValue(band) + "_P"; + if (qrvs != null) + { + foreach (QRVDesignator qrv in qrvs) + { + DataRow row = QRV.Rows.Find(new string[]{ qrv.Call, qrv.Loc}); + if (row == null) + { + row = QRV.NewRow(); + row["Call"] = qrv.Call; + row["Loc"] = qrv.Loc; + row["LastUpdated"] = DateTime.MinValue; + QRV.Rows.Add(row); + } + row[band_ah] = qrv.AntennaHeight; + row[band_ag] = qrv.AntennaGain; + row[band_p] = qrv.Power; + if ((DateTime)row["LastUpdated"] < qrv.LastUpdated) + row["LastUpdated"] = qrv.LastUpdated; + } + } + } + } + QRV.AcceptChanges(); + QRVView = new DataView(QRV); + BindingSource source = new BindingSource(); + source.DataSource = QRVView; + dgv_QRV.DataSource = source; + dgv_QRV.ShowRowErrors = true; + dgv_QRV.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill; + for (int i = 0; i < dgv_QRV.Columns.Count; i++) + { + if (i % 2 == 0) + dgv_QRV.Columns[i].DefaultCellStyle.BackColor = Color.LightGray; + } + SayQRV("Finished."); + } + + private void tb_QRV_Callsign_Filter_TextChanged(object sender, EventArgs e) + { + if (String.IsNullOrEmpty(tb_QRV_Callsign_Filter.Text)) + { + QRVView.RowFilter = "Call LIKE '*'"; + return; + } + string filter = tb_QRV_Callsign_Filter.Text; + if (!filter.EndsWith("*")) + filter = filter + "*"; + QRVView.RowFilter = "Call LIKE '" + filter + "'"; + + } + + private void btn_QRV_Sort_Click(object sender, EventArgs e) + { + // sort data table + DataTable sorted = QRV.Clone(); + DataRow[] rows = QRV.Select("", "Call ASC"); + if (rows.Length > 0) + { + foreach (DataRow row in rows) + sorted.ImportRow(row); + } + QRV.Clear(); + foreach (DataRow row in sorted.Rows) + QRV.ImportRow(row); + } + + private void btn_QRV_Save_Click(object sender, EventArgs e) + { + SayQRV("Saving changes to database..."); + try + { + foreach (DataRow row in QRV.Rows) + { + if ((row.RowState == DataRowState.Added) || (row.RowState == DataRowState.Modified)) + { + BAND[] bands = Bands.GetValuesExceptNoneAndAll(); + foreach (BAND band in bands) + { + if (band > BAND.B70M) + { + string band_ah = Bands.GetStringValue(band) + "_AH"; + string band_ag = Bands.GetStringValue(band) + "_AG"; + string band_p = Bands.GetStringValue(band) + "_P"; + QRVDesignator qrv = new QRVDesignator(); + qrv.Call = row["Call"].ToString().ToUpper(); + qrv.Loc = row["Loc"].ToString().ToUpper(); + qrv.Band = band; + if ((row[band_ah].GetType() != typeof(DBNull)) && (row[band_ah].GetType() != typeof(DBNull)) && (row[band_ah].GetType() != typeof(DBNull))) + { + qrv.AntennaHeight = (double)row[band_ah]; + qrv.AntennaGain = (double)row[band_ag]; + qrv.Power = (double)row[band_p]; + qrv.LastUpdated = (DateTime)row["LastUpdated"]; + SayQRV("Updating " + qrv.Call + ", " + qrv.Loc + "..."); + StationData.Database.QRVInsertOrUpdateIfNewer(qrv); + } + } + } + } + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + SayQRV("Finished."); + + } + + private void btn_QRV_Export_Click(object sender, EventArgs e) + { + string filename = Path.Combine(ExportDirectory, "qrv.json"); + SayQRV("Exporting database to " + filename); + string json = StationData.Database.QRVToJSON(); + SupportFunctions.WriteStringToFile(json, filename); + SayQRV("Finished."); + } + + private void QRV_Row_Changed(object sender, DataRowChangeEventArgs e) + { + + } + + private void dgv_QRV_CellValueChanged(object sender, DataGridViewCellEventArgs e) + { + // update LastUpdated column in case of changes + if (e.ColumnIndex < dgv_QRV.Columns.Count - 1) + { + dgv_QRV.Rows[e.RowIndex].Cells["LastUpdated"].Value = DateTime.UtcNow; + } + } + + private void dgv_QRV_CellEndEdit(object sender, DataGridViewCellEventArgs e) + { + } + + private void btn_QRV_Import_EDI_Click(object sender, EventArgs e) + { + + } + + private void cb_QRV_ChangedOnly_CheckedChanged(object sender, EventArgs e) + { + if (cb_QRV_ChangedOnly.Checked) + { + QRVView.RowStateFilter = DataViewRowState.ModifiedCurrent | DataViewRowState.Added; + } + else + { + QRVView.RowStateFilter = DataViewRowState.CurrentRows; + } + } + + private void btn_SFTP_GenerateFile_Click(object sender, EventArgs e) + { + // generates password file for SFTP and other + if (String.IsNullOrEmpty(tb_SFTP_URL.Text) || String.IsNullOrEmpty(tb_SFTP_User.Text) || String.IsNullOrEmpty(tb_SFTP_Password.Text)) + { + MessageBox.Show("Invalid entries for URL, user or password!"); + return; + } + SaveFileDialog Dlg = new SaveFileDialog(); + Dlg.FileName = "airscout.pwd"; + if (Dlg.ShowDialog() == DialogResult.OK) + { + using (StreamWriter sw = new StreamWriter(Dlg.FileName,false)) + { + string s = tb_SFTP_URL.Text + "\t" + Encryption.EncryptString(tb_SFTP_User.Text) + "\t" + Encryption.EncryptString(tb_SFTP_Password.Text); + sw.WriteLine(s); + } + } + } + + private void dgv_QRV_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) + { + if ((e.Value != null) && (e.Value != DBNull.Value)) + { + e.Value = e.Value.ToString().ToUpper(); + e.FormattingApplied = true; + } + } + + private void btn_Update_Airlines_Click(object sender, EventArgs e) + { + try + { + string json = ""; + using (var client = new WebClient()) + { + json = client.DownloadString(Properties.Settings.Default.Airlines_Update_URL); + } + JsonSerializerSettings settings = new JsonSerializerSettings(); + settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; + settings.FloatFormatHandling = FloatFormatHandling.String; + settings.Formatting = Newtonsoft.Json.Formatting.Indented; + FR24Airlines fr24airlines = (FR24Airlines)JsonConvert.DeserializeObject(json, settings); + int count = 0; + int errors = 0; + foreach (FR24AirlineDesignator fr24ad in fr24airlines.rows) + { + if (!String.IsNullOrEmpty(fr24ad.Code) && !String.IsNullOrEmpty(fr24ad.ICAO)) + { + AirlineDesignator ad = new AirlineDesignator(fr24ad.ICAO, fr24ad.Code, fr24ad.Name, "[unknown]"); + int result = AircraftData.Database.AirlineInsertOrUpdateIfNewer(ad); + if (result >= 0) + count += result; + else + errors++; + } + } + Say("Airlines updated from " + Properties.Settings.Default.Airlines_Update_URL + ": " + count.ToString() + " updated, " + errors.ToString() + " error(s)."); + } + catch (Exception ex) + { + Say(ex.Message); + } + } + + private void btn_Update_Airports_Click(object sender, EventArgs e) + { + try + { + string json = ""; + using (var client = new WebClient()) + { + json = client.DownloadString(Properties.Settings.Default.Airports_Update_URL); + } + JsonSerializerSettings settings = new JsonSerializerSettings(); + settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; + settings.FloatFormatHandling = FloatFormatHandling.String; + settings.Formatting = Newtonsoft.Json.Formatting.Indented; + FR24Airports fr24airports = (FR24Airports)JsonConvert.DeserializeObject(json, settings); + int count = 0; + int errors = 0; + foreach (FR24AirportDesignator fr24ad in fr24airports.rows) + { + if (!String.IsNullOrEmpty(fr24ad.icao) && !String.IsNullOrEmpty(fr24ad.iata)) + { + AirportDesignator ad = new AirportDesignator(fr24ad.icao, fr24ad.iata, fr24ad.lat, fr24ad.lon, fr24ad.alt, fr24ad.name.Replace("\t","").Replace("\r","").Replace("\n",""), fr24ad.country); + int result = AircraftData.Database.AirportInsertOrUpdateIfNewer(ad); + if (result >= 0) + count += result; + else + errors++; + } + } + Say("Airports updated from " + Properties.Settings.Default.Airports_Update_URL + ": " + count.ToString() + " updated, " + errors.ToString() + " error(s)."); + } + catch (Exception ex) + { + Say(ex.Message); + } + + } + + private void bw_AircraftUpdater_DoWork(object sender, DoWorkEventArgs e) + { + // Divide the earth surface into zones optimized for flights density + List world_zones = new List(new string[] { + "90, 70, -180, 180", + "70, 50, -180, -20", + "70, 50, -20, 0", + "70, 50, 0, 20", + "70, 50, 20, 40", + "70, 50, 40, 180", + "50, 30, -180, -120", + "50, 40, -120, -110", + "50, 40, -110, -100", + "40, 30, -120, -110", + "40, 30, -110, -100", + "50, 40, -100, -90", + "50, 40, -90, -80", + "40, 30, -100, -90", + "40, 30, -90, -80", + "50, 30, -80, -60", + "50, 30, -60, -40", + "50, 30, -40, -20", + "50, 30, -20, 0", + "50, 40, 0, 10", + "50, 40, 10, 20", + "40, 30, 0, 10", + "40, 30, 10, 20", + "50, 30, 20, 40", + "50, 30, 40, 60", + "50, 30, 60, 180", + "30, 10, -180, -100", + "30, 10, -100, -80", + "30, 10, -80, 100", + "30, 10, 100, 180", + "10, -10, -180, 180", + "-10, -30, -180, 180", + "-30, -90, -180, 180" + } ); + while (!bw_AircraftUpdater.CancellationPending) + { + try + { + List aircrafts = new List(); + int errors = 0; + foreach (string zone in world_zones) + { + string url = Properties.Settings.Default.Aircrafts_BaseURL + "&bounds=" + zone; + bw_AircraftUpdater.ReportProgress(0, "getting aircrafts from: " + url); + string json = ""; + using (var client = new WebClient()) + { + json = client.DownloadString(url); + } + // modify the JSON string to get a list of aircrafts + json = json.Substring(json.IndexOf(",") + 1); + json = json.Substring(json.IndexOf(",") + 1); + json = "{rows:{" + json; + json = json.Remove(json.IndexOf(",\"stats\"")); + json = json + "}}"; + JsonSerializerSettings settings = new JsonSerializerSettings(); + settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; + settings.FloatFormatHandling = FloatFormatHandling.String; + settings.Formatting = Newtonsoft.Json.Formatting.Indented; + FR24Aircrafts fr24aircrafts = (FR24Aircrafts)JsonConvert.DeserializeObject(json, new FR24AircraftConverter()); + foreach (FR24AircraftDesignator fr24ad in fr24aircrafts.rows.Values) + { + try + { + AircraftDesignator ad = new AircraftDesignator(); + ad.Hex = fr24ad.hex; + ad.Call = fr24ad.call; + ad.Reg = fr24ad.reg; + ad.TypeCode = fr24ad.typecode; + ad.LastUpdated = DateTime.UtcNow; + if (PlaneInfo.Check_Hex(ad.Hex) && PlaneInfo.Check_Call(ad.Call) && PlaneInfo.Check_Reg(ad.Reg) && PlaneInfo.Check_Type(ad.TypeCode)) + aircrafts.Add(ad); + else + { +// Console.WriteLine("Invalid aircraft data: " + fr24ad.hex + "," + fr24ad.call + "," + fr24ad.reg + "," + fr24ad.typecode); + errors++; + } + } + catch (Exception ex) + { + bw_AircraftUpdater.ReportProgress(-1, ex.Message); + } + } + Thread.Sleep(1000); + } + bw_AircraftUpdater.ReportProgress(0, "Updating aircrafts..."); + // update aircraft data + AircraftData.Database.AircraftBulkInsertOrUpdateIfNewer(aircrafts); + bw_AircraftUpdater.ReportProgress(0, "Aircafts updated from " + Properties.Settings.Default.Aircrafts_BaseURL + ": " + aircrafts.Count.ToString() + " updated, " + errors.ToString() + " error(s)."); + int timeout = 0; + while (!bw_AircraftUpdater.CancellationPending && (timeout < 600)) + { + Thread.Sleep(1000); + timeout++; + } + } + catch (Exception ex) + { + bw_AircraftUpdater.ReportProgress(-1, ex.Message); + } + } + } + + private void bw_AircraftUpdater_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + if (e.ProgressPercentage < 0) + Say((string)(e.UserState)); + else if (e.ProgressPercentage == 0) + Say((string)(e.UserState)); + ReportAircraftsStats(); + } + + private void bw_AircraftUpdater_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + btn_Update_Aicrafts_Start.Enabled = true; + btn_Update_Aircrafts_Stop.Enabled = false; + } + + private void ReportAircraftsStats() + { + lbl_Aircrafts_Total.Text = "total: " + AircraftData.Database.AircraftCount().ToString(); + lbl_Aircrafts_UnkownHex.Text = "unknown hex: " + AircraftData.Database.AircraftCountUnknownHex().ToString(); + lbl_Aircrafts_UnkownCall.Text = "unknown call: " + AircraftData.Database.AircraftCountUnknownCall().ToString(); + lbl_Aircrafts_UnkownType.Text = "unknown type: " + AircraftData.Database.AircraftCountUnknownType().ToString(); + } + + private void btn_Update_Aicrafts_Start_Click(object sender, EventArgs e) + { + bw_AircraftUpdater.RunWorkerAsync(); + while (!bw_AircraftUpdater.IsBusy) + Application.DoEvents(); + btn_Update_Aicrafts_Start.Enabled = false; + btn_Update_Aircrafts_Stop.Enabled = true; + } + + private void btn_Update_Aircrafts_Stop_Click(object sender, EventArgs e) + { + bw_AircraftUpdater.CancelAsync(); + } + + private void tp_Aircrafts_Enter(object sender, EventArgs e) + { + if (bw_AircraftUpdater.IsBusy) + { + btn_Update_Aicrafts_Start.Enabled = false; + btn_Update_Aircrafts_Stop.Enabled = true; + } + else + { + btn_Update_Aicrafts_Start.Enabled = true; + btn_Update_Aircrafts_Stop.Enabled = false; + } + + ReportAircraftsStats(); + } + + private void btn_StationDatabase_Export_Click(object sender, EventArgs e) + { + // export and upload station database + if (!SupportFunctions.ValidateDirectoryPath(Properties.Settings.Default.StationDatabase_Export_LocalDir)) + { + MessageBox.Show("Local Path is not valid: " + Properties.Settings.Default.StationDatabase_Export_LocalDir, " Export Station Database"); + return; + } + Say("Getting locations..."); + string locations = StationData.Database.LocationToJSON(); + string locationsfile = Path.Combine(Properties.Settings.Default.StationDatabase_Export_LocalDir, "locations.json"); + string locationszipfile = Path.Combine(Properties.Settings.Default.StationDatabase_Export_LocalDir, "locations.zip"); + SupportFunctions.WriteStringToFile(locations, locationsfile); + Say("Creating zip file..."); + ZIP.CompressFile(locationsfile, false, 60); + string qrvs = StationData.Database.QRVToJSON(); + Say("Getting qrv information..."); + string qrvfile = Path.Combine(Properties.Settings.Default.StationDatabase_Export_LocalDir, "qrv.json"); + string qrvzipfile = Path.Combine(Properties.Settings.Default.StationDatabase_Export_LocalDir, "qrv.zip"); + SupportFunctions.WriteStringToFile(qrvs, qrvfile); + Say("Creating zip file..."); + ZIP.CompressFile(qrvfile, false, 60); + Say("Upload files..."); + SftpClient client = new SftpClient(Properties.Settings.Default.StationDatabase_Export_RemoteHost, Properties.Settings.Default.StationDatabase_Export_User, Properties.Settings.Default.StationDatabase_Export_Password); + try + { + client.Connect(); + using (FileStream file = new FileStream(locationszipfile, FileMode.Open)) + { + string uploadfile = Properties.Settings.Default.StationDatabase_Export_RemoteDir + "/" + "locations.zip"; + client.BufferSize = 4 * 1024; + client.UploadFile(file, uploadfile, true); + } + using (FileStream file = new FileStream(qrvzipfile, FileMode.Open)) + { + string uploadfile = Properties.Settings.Default.StationDatabase_Export_RemoteDir + "/" + "qrv.zip"; + client.BufferSize = 4 * 1024; + client.UploadFile(file, uploadfile, true); + } + + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + Say("Station database exported and uploaded."); + } + + private void btn_AircraftDatabase_Export_Click(object sender, EventArgs e) + { + // export and upload aircraftdatabase + if (!SupportFunctions.ValidateDirectoryPath(Properties.Settings.Default.AircraftDatabase_Export_LocalDir)) + { + MessageBox.Show("Local Path is not valid: " + Properties.Settings.Default.AircraftDatabase_Export_LocalDir, " Export Aircraft Database"); + return; + } + Say("Getting aircraft registrations..."); + string ars = AircraftData.Database.AircraftRegistrationToJSON(); + string arsfile = Path.Combine(Properties.Settings.Default.AircraftDatabase_Export_LocalDir, "AircraftRegistrations.json"); + string arszipfile = Path.Combine(Properties.Settings.Default.AircraftDatabase_Export_LocalDir, "AircraftRegistrations.zip"); + SupportFunctions.WriteStringToFile(ars, arsfile); + Say("Creating zip file..."); + ZIP.CompressFile(arsfile, false, 60); + Say("Getting aircrafts..."); + string acs = AircraftData.Database.AircraftToJSON(); + string acsfile = Path.Combine(Properties.Settings.Default.AircraftDatabase_Export_LocalDir, "Aircrafts.json"); + string acszipfile = Path.Combine(Properties.Settings.Default.AircraftDatabase_Export_LocalDir, "Aircrafts.zip"); + SupportFunctions.WriteStringToFile(acs, acsfile); + Say("Creating zip file..."); + ZIP.CompressFile(acsfile, false, 60); + Say("Getting aircraft typess..."); + string ats = AircraftData.Database.AircraftTypeToJSON(); + string atsfile = Path.Combine(Properties.Settings.Default.AircraftDatabase_Export_LocalDir, "AircraftTypes.json"); + string atszipfile = Path.Combine(Properties.Settings.Default.AircraftDatabase_Export_LocalDir, "AircraftTypes.zip"); + SupportFunctions.WriteStringToFile(ats, atsfile); + Say("Creating zip file..."); + ZIP.CompressFile(atsfile, false, 60); + Say("Getting airlines..."); + string als = AircraftData.Database.AirlineToJSON(); + string alsfile = Path.Combine(Properties.Settings.Default.AircraftDatabase_Export_LocalDir, "Airlines.json"); + string alszipfile = Path.Combine(Properties.Settings.Default.AircraftDatabase_Export_LocalDir, "Airlines.zip"); + SupportFunctions.WriteStringToFile(als, alsfile); + Say("Creating zip file..."); + ZIP.CompressFile(alsfile, false, 60); + Say("Getting airports..."); + string aps = AircraftData.Database.AirportToJSON(); + string apsfile = Path.Combine(Properties.Settings.Default.AircraftDatabase_Export_LocalDir, "Airports.json"); + string apszipfile = Path.Combine(Properties.Settings.Default.AircraftDatabase_Export_LocalDir, "Airports.zip"); + SupportFunctions.WriteStringToFile(aps, apsfile); + Say("Creating zip file..."); + ZIP.CompressFile(apsfile, false, 60); + Say("Upload files..."); + SftpClient client = new SftpClient(Properties.Settings.Default.AircraftDatabase_Export_RemoteHost, Properties.Settings.Default.AircraftDatabase_Export_User, Properties.Settings.Default.AircraftDatabase_Export_Password); + try + { + client.Connect(); + using (FileStream file = new FileStream(arszipfile, FileMode.Open)) + { + string uploadfile = Properties.Settings.Default.AircraftDatabase_Export_RemoteDir + "/" + "AircraftRegistrations.zip"; + client.BufferSize = 4 * 1024; + client.UploadFile(file, uploadfile, true); + } + using (FileStream file = new FileStream(acszipfile, FileMode.Open)) + { + string uploadfile = Properties.Settings.Default.AircraftDatabase_Export_RemoteDir + "/" + "Aircrafts.zip"; + client.BufferSize = 4 * 1024; + client.UploadFile(file, uploadfile, true); + } + using (FileStream file = new FileStream(atszipfile, FileMode.Open)) + { + string uploadfile = Properties.Settings.Default.AircraftDatabase_Export_RemoteDir + "/" + "AircraftTypes.zip"; + client.BufferSize = 4 * 1024; + client.UploadFile(file, uploadfile, true); + } + using (FileStream file = new FileStream(alszipfile, FileMode.Open)) + { + string uploadfile = Properties.Settings.Default.AircraftDatabase_Export_RemoteDir + "/" + "Airlines.zip"; + client.BufferSize = 4 * 1024; + client.UploadFile(file, uploadfile, true); + } + using (FileStream file = new FileStream(apszipfile, FileMode.Open)) + { + string uploadfile = Properties.Settings.Default.AircraftDatabase_Export_RemoteDir + "/" + "Airports.zip"; + client.BufferSize = 4 * 1024; + client.UploadFile(file, uploadfile, true); + } + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + Say("Aircraft database exported and uploaded."); + } + + private void dgv_Locations_SelectionChanged(object sender, EventArgs e) + { + DataGridViewSelectedRowCollection rows = dgv_Locations.SelectedRows; + if (!IsMarkerDragging && (rows.Count > 0)) + { + try + { + // clear locations + Locationsoverlay.Clear(); + double minlat = double.MaxValue; + double maxlat = double.MinValue; + double minlon = double.MaxValue; + double maxlon = double.MinValue; + foreach (DataGridViewRow row in rows) + { + // get info + string call = row.Cells["Call"].Value.ToString(); + double lat = (double)row.Cells["Lat"].Value; + double lon = (double)row.Cells["Lon"].Value; + GEOSOURCE source = (GEOSOURCE)row.Cells["Source"].Value; + // add location + GMarkerGoogle gm = new GMarkerGoogle(new PointLatLng(lat, lon), (source == GEOSOURCE.FROMUSER) ? GMarkerGoogleType.green_small : GMarkerGoogleType.white_small); + gm.ToolTipText = call; + gm.ToolTipMode = MarkerTooltipMode.OnMouseOver; + gm.Tag = call; + Locationsoverlay.Markers.Add(gm); + if (minlat > lat) + minlat = lat; + if (maxlat < lat) + maxlat = lat; + if (minlon > lon) + minlon = lon; + if (maxlon < lon) + maxlon = lon; + } + // ensure that all location are visible + if (rows.Count > 1) + { + gm_Locations.SetZoomToFitRect(RectLatLng.FromLTRB(minlon, maxlat, maxlon, minlat)); + } + else + { + // set standard zoom if only 1 location + gm_Locations.Zoom = 15; + gm_Locations.Position = new PointLatLng(minlat,minlon); + } + } + catch (Exception ex) + { + // cannot set position -- > do nothing + } + } + } + + } + + public class FR24Airlines + { + public int version; + public FR24AirlineDesignator[] rows; + + public FR24Airlines() + { + version = 0; + rows = null; + } + } + + public class FR24AirlineDesignator + { + public string Name; + public string Code; + public string ICAO; + + public FR24AirlineDesignator() + { + Name = ""; + Code = ""; + ICAO = ""; + } + } + + public class FR24Airports + { + public int version; + public FR24AirportDesignator[] rows; + + public FR24Airports() + { + version = 0; + rows = null; + } + } + + public class FR24AirportDesignator + { + public string name; + public string iata; + public string icao; + public string city; + public double lat; + public double lon; + public string country; + public double alt; + public double size; + + public FR24AirportDesignator() + { + name = ""; + iata = ""; + icao = ""; + city = ""; + lat = 0; + lon = 0; + country = ""; + alt = 0; + size = 0; + } + } + + public class FR24Aircrafts + { + public Dictionary rows; + public FR24Aircrafts() + { + rows = null; + } + } + + public class FR24AircraftDesignator + { + public string hex; + public double lat; + public double lon; + public int track; + public int alt; + public int speed; + public string squawk; + public string radar; + public string typecode; + public string reg; + public long time; + public string src; + public string dst; + public string call; + public int dummy1; + public int dummy2; + public string flight; + public int dummy3; + public string airline; + + public FR24AircraftDesignator() + { + hex = ""; + lat = 0; + lon = 0; + track = 0; + alt = 0; + speed = 0; + squawk = ""; + radar = ""; + typecode = ""; + reg = ""; + time = 0; + src = ""; + dst = ""; + call = ""; + dummy1 = 0; + dummy2 = 0; + flight = ""; + dummy3 = 0; + airline = ""; + } + } + + class FR24AircraftConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return (objectType == typeof(FR24AircraftDesignator)); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + JArray array = JArray.Load(reader); + return new FR24AircraftDesignator + { + hex = (string)array[0], + lat = (double)array[1], + lon = (double)array[2], + track = (int)array[3], + alt = (int)array[4], + speed = (int)array[5], + squawk = (string)array[6], + radar = (string)array[7], + typecode = (string)array[8], + reg = (string)array[9], + time = (long)array[10], + src = (string)array[11], + dst = (string)array[12], + flight = (string)array[13], + dummy1 = (int)array[14], + dummy2 = (int)array[15], + call = (string)array[16], + dummy3 = (int)array[17], + airline = (string)array[18] + }; + } + + public override bool CanWrite + { + get { return false; } + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + } + public enum DATABASEUPDATERSTARTOPTIONS + { + NONE = 0, + FIRSTRUN = 1, + RUNONCE = 2, + RUNPERIODICALLY = 3 + } + + public enum LOCATIONSTATE + { + UNKNOWN = -2, + ERROR = -1, + INFO = 0, + UPTODATE = 1, + UPDATED = 2, + LOCDIFF = 3, + ADDED = 4 + } +} diff --git a/AirScoutDatabaseManager/MainDlg.resx b/AirScoutDatabaseManager/MainDlg.resx new file mode 100644 index 0000000..6273ff6 --- /dev/null +++ b/AirScoutDatabaseManager/MainDlg.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 111, 17 + + + 208, 17 + + + 376, 17 + + \ No newline at end of file diff --git a/AirScoutDatabaseManager/Program.cs b/AirScoutDatabaseManager/Program.cs new file mode 100644 index 0000000..3fd3f1a --- /dev/null +++ b/AirScoutDatabaseManager/Program.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; + +namespace AirScoutDatabaseManager +{ + static class Program + { + /// + /// Der Haupteinstiegspunkt für die Anwendung. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new MainDlg()); + } + } +} diff --git a/AirScoutDatabaseManager/Properties/AssemblyInfo.cs b/AirScoutDatabaseManager/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..f0f7519 --- /dev/null +++ b/AirScoutDatabaseManager/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Allgemeine Informationen über eine Assembly werden über die folgenden +// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, +// die mit einer Assembly verknüpft sind. +[assembly: AssemblyTitle("AirScoutDatabaseManager")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("DL2ALF")] +[assembly: AssemblyProduct("AirScoutDatabaseManager")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar +// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von +// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest. +[assembly: ComVisible(false)] + +// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird +[assembly: Guid("7f477336-7117-46ad-b163-ca7b00545885")] + +// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: +// +// Hauptversion +// Nebenversion +// Buildnummer +// Revision +// +// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern +// übernehmen, indem Sie "*" eingeben: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.3.0.0")] +[assembly: AssemblyFileVersion("1.3.0.0")] diff --git a/AirScoutDatabaseManager/Properties/Resources.Designer.cs b/AirScoutDatabaseManager/Properties/Resources.Designer.cs new file mode 100644 index 0000000..0536a52 --- /dev/null +++ b/AirScoutDatabaseManager/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.42000 +// +// Änderungen an dieser Datei können fehlerhaftes Verhalten verursachen und gehen verloren, wenn +// der Code neu generiert wird. +// +//------------------------------------------------------------------------------ + +namespace AirScoutDatabaseManager.Properties +{ + + + /// + /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw. + /// + // Diese Klasse wurde von der StronglyTypedResourceBuilder-Klasse + // über ein Tool wie ResGen oder Visual Studio automatisch generiert. + // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen + // mit der Option /str erneut aus, oder erstellen Sie Ihr VS-Projekt neu. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AirScoutDatabaseManager.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle + /// Ressourcenlookups, die diese stark typisierte Ressourcenklasse verwenden. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/AirScoutDatabaseManager/Properties/Resources.resx b/AirScoutDatabaseManager/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/AirScoutDatabaseManager/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/AirScoutDatabaseManager/Properties/Settings.Designer.cs b/AirScoutDatabaseManager/Properties/Settings.Designer.cs new file mode 100644 index 0000000..c155d71 --- /dev/null +++ b/AirScoutDatabaseManager/Properties/Settings.Designer.cs @@ -0,0 +1,376 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.42000 +// +// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn +// der Code erneut generiert wird. +// +//------------------------------------------------------------------------------ + +namespace AirScoutDatabaseManager.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] + public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-15")] + public double MinLon { + get { + return ((double)(this["MinLon"])); + } + set { + this["MinLon"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("30")] + public double MaxLon { + get { + return ((double)(this["MaxLon"])); + } + set { + this["MaxLon"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("35")] + public double MinLat { + get { + return ((double)(this["MinLat"])); + } + set { + this["MinLat"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("60")] + public double MaxLat { + get { + return ((double)(this["MaxLat"])); + } + set { + this["MaxLat"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("OpenStreetMap")] + public string Map_Provider { + get { + return ((string)(this["Map_Provider"])); + } + set { + this["Map_Provider"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://www.qrz.com/db/")] + public string QRZ_URL_Database { + get { + return ((string)(this["QRZ_URL_Database"])); + } + set { + this["QRZ_URL_Database"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("\\Log")] + public string Log_Directory { + get { + return ((string)(this["Log_Directory"])); + } + set { + this["Log_Directory"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("\\Database")] + public string Database_Directory { + get { + return ((string)(this["Database_Directory"])); + } + set { + this["Database_Directory"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("\\Tmp")] + public string Tmp_Directory { + get { + return ((string)(this["Tmp_Directory"])); + } + set { + this["Tmp_Directory"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("\\Export")] + public string Export_Directory { + get { + return ((string)(this["Export_Directory"])); + } + set { + this["Export_Directory"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://www.airscout.eu/downloads/ScoutBase/1/StationData/")] + public string Station_URL { + get { + return ((string)(this["Station_URL"])); + } + set { + this["Station_URL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("https://xmldata.qrz.com/xml/current/?username=dl2alf;password=271192;agent=AirSco" + + "ut")] + public string QRZ_URL_Login { + get { + return ((string)(this["QRZ_URL_Login"])); + } + set { + this["QRZ_URL_Login"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://xmldata.qrz.com/xml/current/")] + public string QRZ_URL_XMLData { + get { + return ((string)(this["QRZ_URL_XMLData"])); + } + set { + this["QRZ_URL_XMLData"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("www.airscout.eu")] + public string SFTP_URL { + get { + return ((string)(this["SFTP_URL"])); + } + set { + this["SFTP_URL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("u45565180-airscout")] + public string SFTP_User { + get { + return ((string)(this["SFTP_User"])); + } + set { + this["SFTP_User"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("airscout")] + public string SFTP_Password { + get { + return ((string)(this["SFTP_Password"])); + } + set { + this["SFTP_Password"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("https://www.flightradar24.com/_json/airlines.php")] + public string Airlines_Update_URL { + get { + return ((string)(this["Airlines_Update_URL"])); + } + set { + this["Airlines_Update_URL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("https://www.flightradar24.com/_json/airports2.php")] + public string Airports_Update_URL { + get { + return ((string)(this["Airports_Update_URL"])); + } + set { + this["Airports_Update_URL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("https://data.flightradar24.com/zones/fcgi/feed.js?faa=1&mlat=1&flarm=0&adsb=1&gnd" + + "=0&air=1&vehicles=0&estimated=0&maxage=0&gliders=0&stats=1")] + public string Aircrafts_BaseURL { + get { + return ((string)(this["Aircrafts_BaseURL"])); + } + set { + this["Aircrafts_BaseURL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Z:\\CSharp\\AirScout\\Database\\ScoutBase\\V1")] + public string StationDatabase_Export_LocalDir { + get { + return ((string)(this["StationDatabase_Export_LocalDir"])); + } + set { + this["StationDatabase_Export_LocalDir"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("/wsb4556518002/downloads/ScoutBase/1/StationData")] + public string StationDatabase_Export_RemoteDir { + get { + return ((string)(this["StationDatabase_Export_RemoteDir"])); + } + set { + this["StationDatabase_Export_RemoteDir"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("u45565180")] + public string StationDatabase_Export_User { + get { + return ((string)(this["StationDatabase_Export_User"])); + } + set { + this["StationDatabase_Export_User"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string StationDatabase_Export_Password { + get { + return ((string)(this["StationDatabase_Export_Password"])); + } + set { + this["StationDatabase_Export_Password"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("home208518495.1and1-/data.host")] + public string StationDatabase_Export_RemoteHost { + get { + return ((string)(this["StationDatabase_Export_RemoteHost"])); + } + set { + this["StationDatabase_Export_RemoteHost"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Z:\\CSharp\\AirScout\\Database\\AirScout\\V1")] + public string AircraftDatabase_Export_LocalDir { + get { + return ((string)(this["AircraftDatabase_Export_LocalDir"])); + } + set { + this["AircraftDatabase_Export_LocalDir"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string AircraftDatabase_Export_Password { + get { + return ((string)(this["AircraftDatabase_Export_Password"])); + } + set { + this["AircraftDatabase_Export_Password"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("/wsb4556518002/downloads/AirScout/1/AircraftData")] + public string AircraftDatabase_Export_RemoteDir { + get { + return ((string)(this["AircraftDatabase_Export_RemoteDir"])); + } + set { + this["AircraftDatabase_Export_RemoteDir"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("home208518495.1and1-/data.host")] + public string AircraftDatabase_Export_RemoteHost { + get { + return ((string)(this["AircraftDatabase_Export_RemoteHost"])); + } + set { + this["AircraftDatabase_Export_RemoteHost"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("u45565180")] + public string AircraftDatabase_Export_User { + get { + return ((string)(this["AircraftDatabase_Export_User"])); + } + set { + this["AircraftDatabase_Export_User"] = value; + } + } + } +} diff --git a/AirScoutDatabaseManager/Properties/Settings.settings b/AirScoutDatabaseManager/Properties/Settings.settings new file mode 100644 index 0000000..c5e540f --- /dev/null +++ b/AirScoutDatabaseManager/Properties/Settings.settings @@ -0,0 +1,93 @@ + + + + + + -15 + + + 30 + + + 35 + + + 60 + + + OpenStreetMap + + + http://www.qrz.com/db/ + + + \Log + + + \Database + + + \Tmp + + + \Export + + + http://www.airscout.eu/downloads/ScoutBase/1/StationData/ + + + https://xmldata.qrz.com/xml/current/?username=dl2alf;password=271192;agent=AirScout + + + http://xmldata.qrz.com/xml/current/ + + + www.airscout.eu + + + u45565180-airscout + + + airscout + + + https://www.flightradar24.com/_json/airlines.php + + + https://www.flightradar24.com/_json/airports2.php + + + https://data.flightradar24.com/zones/fcgi/feed.js?faa=1&mlat=1&flarm=0&adsb=1&gnd=0&air=1&vehicles=0&estimated=0&maxage=0&gliders=0&stats=1 + + + Z:\CSharp\AirScout\Database\ScoutBase\V1 + + + /wsb4556518002/downloads/ScoutBase/1/StationData + + + u45565180 + + + + + + home208518495.1and1-/data.host + + + Z:\CSharp\AirScout\Database\AirScout\V1 + + + + + + /wsb4556518002/downloads/AirScout/1/AircraftData + + + home208518495.1and1-/data.host + + + u45565180 + + + \ No newline at end of file diff --git a/AirScoutDatabaseManager/Settings.cs b/AirScoutDatabaseManager/Settings.cs new file mode 100644 index 0000000..864557e --- /dev/null +++ b/AirScoutDatabaseManager/Settings.cs @@ -0,0 +1,28 @@ +namespace AirScoutDatabaseManager.Properties { + + + // Diese Klasse ermöglicht die Behandlung bestimmter Ereignisse der Einstellungsklasse: + // Das SettingChanging-Ereignis wird ausgelöst, bevor der Wert einer Einstellung geändert wird. + // Das PropertyChanged-Ereignis wird ausgelöst, nachdem der Wert einer Einstellung geändert wurde. + // Das SettingsLoaded-Ereignis wird ausgelöst, nachdem die Einstellungswerte geladen wurden. + // Das SettingsSaving-Ereignis wird ausgelöst, bevor die Einstellungswerte gespeichert werden. + public sealed partial class Settings { + + public Settings() { + // // Heben Sie die Auskommentierung der unten angezeigten Zeilen auf, um Ereignishandler zum Speichern und Ändern von Einstellungen hinzuzufügen: + // + // this.SettingChanging += this.SettingChangingEventHandler; + // + // this.SettingsSaving += this.SettingsSavingEventHandler; + // + } + + private void SettingChangingEventHandler(object sender, System.Configuration.SettingChangingEventArgs e) { + // Fügen Sie hier Code zum Behandeln des SettingChangingEvent-Ereignisses hinzu. + } + + private void SettingsSavingEventHandler(object sender, System.ComponentModel.CancelEventArgs e) { + // Fügen Sie hier Code zum Behandeln des SettingsSaving-Ereignisses hinzu. + } + } +} diff --git a/AirScoutDatabaseManager/StationDatabaseUpdater.cs b/AirScoutDatabaseManager/StationDatabaseUpdater.cs new file mode 100644 index 0000000..2d27d79 --- /dev/null +++ b/AirScoutDatabaseManager/StationDatabaseUpdater.cs @@ -0,0 +1,132 @@ + +using System; +using System.ComponentModel; +using System.Windows.Forms; +using System.Net; +using System.IO; +using System.Threading; +using System.Diagnostics; +using System.Collections; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Globalization; +using System.Drawing; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using ScoutBase.Core; +using ScoutBase.Elevation; +using ScoutBase.Stations; + +namespace AirScoutDatabaseManager +{ + public partial class MainDlg : Form + { + + #region StationDatabaseUpdater + + private bool ReadLocationsFromURL(string url, string filename) + { + try + { + AutoDecompressionWebClient cl = new AutoDecompressionWebClient(); + DOWNLOADFILESTATUS status = cl.DownloadFileIfNewer(url, filename, true, true); + if ((status & DOWNLOADFILESTATUS.ERROR) > 0) + { + Log.WriteMessage("Error while downloading and extracting " + filename); + return false; + } + else if (((status & DOWNLOADFILESTATUS.NEWER) > 0) || ((status & DOWNLOADFILESTATUS.NOTNEWER) > 0)) + { + string json = ""; + using (StreamReader sr = new StreamReader(filename)) + json = sr.ReadToEnd(); + List lds = StationData.Database.LocationFromJSON(json); + // chek for empty database + if (StationData.Database.LocationCount() == 0) + { + // do bulk insert + StationData.Database.LocationBulkInsert(lds); + } + else + { + // do update on single elements + foreach(LocationDesignator ld in lds) + { + StationData.Database.LocationInsertOrUpdateIfNewer(ld); + // return if cancellation is pending + if (bw_DatabaseUpdater.CancellationPending) + return false; + } + } + return true; + } + } + catch (Exception ex) + { + // Error loading database + Log.WriteMessage("[" + url + "]: " + ex.ToString()); + } + return false; + } + + private void bw_DatabaseUpdater_DoWork(object sender, DoWorkEventArgs e) + { + Log.WriteMessage("Started."); + // name the thread for debugging + if (String.IsNullOrEmpty(Thread.CurrentThread.Name)) + Thread.CurrentThread.Name = nameof(bw_DatabaseUpdater); + bw_DatabaseUpdater.ReportProgress(0, "Updating database..."); + // get temp directory + string TmpDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), Application.CompanyName, Application.ProductName, "Tmp").TrimEnd(Path.DirectorySeparatorChar); + if (!Directory.Exists(TmpDirectory)) + Directory.CreateDirectory(TmpDirectory); + int errors = 0; + try + { + Stopwatch st = new Stopwatch(); + st.Start(); + // update callsign database + bw_DatabaseUpdater.ReportProgress(0, "Updating callsigns from web database..."); + if (!ReadLocationsFromURL(Properties.Settings.Default.Station_URL + "locations.json", Path.Combine(TmpDirectory, "locations.json"))) + errors++; + st.Stop(); + Log.WriteMessage("Database update completed in " + st.Elapsed.ToString(@"hh\:mm\:ss") + ", errors: " + errors.ToString()); + + // sleep once to get all messages to main thread + Thread.Sleep(1000); + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + bw_DatabaseUpdater.ReportProgress(0, "Updating callsigns finished."); + Log.WriteMessage("Finished."); + } + + private void bw_DatabaseUpdater_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + try + { + if (e.ProgressPercentage == 0) + { + // status message received + string msg = (string)e.UserState; + Say(msg); + } + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + + private void bw_DatabaseUpdater_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + tc_Main.Enabled = true; + } + + #endregion + + } +} diff --git a/AirScoutDatabaseManager/app.config b/AirScoutDatabaseManager/app.config new file mode 100644 index 0000000..fa12240 --- /dev/null +++ b/AirScoutDatabaseManager/app.config @@ -0,0 +1,107 @@ + + + + +
+ + + + + + -15 + + + 30 + + + 35 + + + 60 + + + OpenStreetMap + + + http://www.qrz.com/db/ + + + \Log + + + \Database + + + \Tmp + + + \Export + + + http://www.airscout.eu/downloads/ScoutBase/1/StationData/ + + + https://xmldata.qrz.com/xml/current/?username=dl2alf;password=271192;agent=AirScout + + + http://xmldata.qrz.com/xml/current/ + + + www.airscout.eu + + + u45565180-airscout + + + airscout + + + https://www.flightradar24.com/_json/airlines.php + + + https://www.flightradar24.com/_json/airports2.php + + + https://data.flightradar24.com/zones/fcgi/feed.js?faa=1&mlat=1&flarm=0&adsb=1&gnd=0&air=1&vehicles=0&estimated=0&maxage=0&gliders=0&stats=1 + + + Z:\CSharp\AirScout\Database\ScoutBase\V1 + + + /wsb4556518002/downloads/ScoutBase/1/StationData + + + u45565180 + + + + + + home208518495.1and1-/data.host + + + Z:\CSharp\AirScout\Database\AirScout\V1 + + + + + + /wsb4556518002/downloads/AirScout/1/AircraftData + + + home208518495.1and1-/data.host + + + u45565180 + + + + + + + + + + + + \ No newline at end of file diff --git a/AirScoutDatabaseManager/packages.config b/AirScoutDatabaseManager/packages.config new file mode 100644 index 0000000..6919630 --- /dev/null +++ b/AirScoutDatabaseManager/packages.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/AirScoutPlaneServer/AirScoutPlaneServer.csproj b/AirScoutPlaneServer/AirScoutPlaneServer.csproj new file mode 100644 index 0000000..ff44fe1 --- /dev/null +++ b/AirScoutPlaneServer/AirScoutPlaneServer.csproj @@ -0,0 +1,181 @@ + + + + Debug + x86 + 8.0.30703 + 2.0 + {5F5F4A33-9061-4F30-A08A-E68B4FB84D64} + WinExe + Properties + AirScoutPlaneServer + AirScoutPlaneServer + v4.0 + Client + 512 + + + + + x86 + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + x86 + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + true + bin\Debug\ + DEBUG;TRACE + full + AnyCPU + prompt + false + false + false + + + bin\Release\ + TRACE + true + pdbonly + AnyCPU + prompt + false + false + false + + + + ..\packages\Newtonsoft.Json.12.0.1\lib\net40\Newtonsoft.Json.dll + True + + + + + + + ..\packages\System.Data.SQLite.Core.1.0.110.0\lib\net40\System.Data.SQLite.dll + True + + + + + + + + + + + + + Form + + + Form + + + Form + + + Form + + + MainDlg.cs + + + + + + MainDlg.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + + + + Always + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + {ee86e933-d883-4b18-80eb-0fba55ec67c6} + ScoutBase.Core + + + {009CABFD-726D-481F-972D-0A218E0AD9B9} + ScoutBase.Elevation + + + {EA78AD40-1505-406F-8049-744E58D93F54} + AirScout.PlaneFeeds + + + {D0C39D9D-BED0-418B-9A5E-713176CAF40C} + GMap.NET.Core + + + {E06DEF77-F933-42FB-AFD7-DB2D0D8D6A98} + GMap.NET.WindowsForms + + + {6056d3be-7002-4a6a-a9ea-6ff45122a3c7} + SQLiteDatabase + + + + + Always + + + Always + + + + + + + Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}". + + + + + \ No newline at end of file diff --git a/AirScoutPlaneServer/DatabaseUpdater.cs b/AirScoutPlaneServer/DatabaseUpdater.cs new file mode 100644 index 0000000..8380147 --- /dev/null +++ b/AirScoutPlaneServer/DatabaseUpdater.cs @@ -0,0 +1,274 @@ + +using System; +using System.ComponentModel; +using System.Windows.Forms; +using System.Net; +using System.IO; +using System.Threading; +using System.Diagnostics; +using System.Collections; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using AirScout.Database.Core; +using AirScout.Database.Aircrafts; +using ScoutBase.Core; + +namespace AirScoutPlaneServer +{ + + public partial class MainDlg : Form + { + + Dictionary JArrays = new Dictionary(); + Dictionary JProperties = new Dictionary(); + + private void ParseToken(JToken token) + { + foreach (var t in token.Children()) + { + try + { + if (t.GetType() == typeof(JProperty)) + { + string number = JProperties.Count.ToString("00000000"); + JProperties.Add(number, (JProperty)t); + } + else if (t.GetType() == typeof(JArray)) + { + // generate a unique key in case of no parent property is found + string number = JArrays.Count.ToString("00000000"); + // try to use parent's property name as a key + if (t.Parent.GetType() == typeof(JProperty)) + { + string pval = ((JProperty)t.Parent).Name; + JArray a; + if (!JArrays.TryGetValue(pval, out a)) + { + JArrays.Add(pval, (JArray)t); + } + else + { + // use number as unique key + JArrays.Add(number, (JArray)t); + } + } + } + } + catch (Exception ex) + { + // do nothing + } + ParseToken(t); + } + } + + private void ReadAircraftsFromURL(string url, string filename) + { + string json = ""; + try + { + AutoDecompressionWebClient cl = new AutoDecompressionWebClient(); + cl.DownloadFileIfNewer(url, filename, true); + if (!File.Exists(filename)) + return; + // deserialize JSON file + JObject o = (JObject)JsonConvert.DeserializeObject(json); + // clear collections + JArrays.Clear(); + JProperties.Clear(); + // parse all child tokens recursively --> can be either a property or an array + ParseToken(o); + // we've got all properties and arrays here + // store array values in DataTable + DataTableAircrafts dt = new DataTableAircrafts(); + foreach (KeyValuePair a in JArrays) + { + DataRow row = dt.NewRow(); + row[0] = a.Value[0].ToString(); + row[1] = a.Value[1].ToString(); + row[2] = a.Value[2].ToString(); + row[3] = a.Value[6].ToString(); + dt.Rows.Add(row); + } + AircraftDatabase_old.InsertOrUpdateTable(dt); + } + catch (Exception ex) + { + // Error loading database + Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().ToString() + "[" + url + "]: " + ex.Message); + } + } + + private void ReadAircraftTypesFromURL(string url, string filename) + { + + string json = ""; + try + { + AutoDecompressionWebClient cl = new AutoDecompressionWebClient(); + cl.DownloadFileIfNewer(url, filename, true); + if (!File.Exists(filename)) + return; + // deserialize JSON file + JObject o = (JObject)JsonConvert.DeserializeObject(json); + // clear collections + JArrays.Clear(); + JProperties.Clear(); + // parse all child tokens recursively --> can be either a property or an array + ParseToken(o); + // we've got all properties and arrays here + // store array values in DataTable + DataTableAircraftTypes dt = new DataTableAircraftTypes(); + foreach (KeyValuePair a in JArrays) + { + DataRow row = dt.NewRow(); + for (int i = 0; i < a.Value.Count; i++) + row[i] = a.Value[i].ToString(); + dt.Rows.Add(row); + } + AircraftDatabase_old.InsertOrUpdateTable(dt); + } + catch (Exception ex) + { + // Error loading database + Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().ToString() + "[" + url + "]: " + ex.Message); + } + } + + private void ReadAircraftRegistrationsFromURL(string url, string filename) + { + + string json = ""; + try + { + AutoDecompressionWebClient cl = new AutoDecompressionWebClient(); + cl.DownloadFileIfNewer(url, filename, true); + if (!File.Exists(filename)) + return; + // deserialize JSON file + JObject o = (JObject)JsonConvert.DeserializeObject(json); + // clear collections + JArrays.Clear(); + JProperties.Clear(); + // parse all child tokens recursively --> can be either a property or an array + ParseToken(o); + // we've got all properties and arrays here + // store array values in DataTable + DataTableAircraftRegistrations dt = new DataTableAircraftRegistrations(); + foreach (KeyValuePair a in JArrays) + { + DataRow row = dt.NewRow(); + for (int i = 0; i < a.Value.Count; i++) + row[i] = a.Value[i].ToString(); + dt.Rows.Add(row); + } + AircraftDatabase_old.InsertOrUpdateTable(dt); + } + catch (Exception ex) + { + // Error loading database + Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().ToString() + "[" + url + "]: " + ex.Message); + } + } + + private void ReadAirlinesFromURL(string url, string filename) + { + + string json = ""; + try + { + AutoDecompressionWebClient cl = new AutoDecompressionWebClient(); + cl.DownloadFileIfNewer(url, filename, true); + if (!File.Exists(filename)) + return; + // deserialize JSON file + JObject o = (JObject)JsonConvert.DeserializeObject(json); + // clear collections + JArrays.Clear(); + JProperties.Clear(); + // parse all child tokens recursively --> can be either a property or an array + ParseToken(o); + // we've got all properties and arrays here + // store array values in DataTable + DataTableAirlines dt = new DataTableAirlines(); + foreach (KeyValuePair a in JArrays) + { + DataRow row = dt.NewRow(); + for (int i = 0; i < a.Value.Count; i++) + row[i] = a.Value[i].ToString(); + dt.Rows.Add(row); + } + AircraftDatabase_old.InsertOrUpdateTable(dt); + } + catch (Exception ex) + { + // Error loading database + Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().ToString() + "[" + url + "]: " + ex.Message); + } + } + + private void ReadAirportsFromURL(string url, string filename) + { + + string json = ""; + try + { + AutoDecompressionWebClient cl = new AutoDecompressionWebClient(); + cl.DownloadFileIfNewer(url, filename, true); + if (!File.Exists(filename)) + return; + // deserialize JSON file + JObject o = (JObject)JsonConvert.DeserializeObject(json); + // clear collections + JArrays.Clear(); + JProperties.Clear(); + // parse all child tokens recursively --> can be either a property or an array + ParseToken(o); + // we've got all properties and arrays here + // store array values in DataTable + DataTableAirports dt = new DataTableAirports(); + foreach (KeyValuePair a in JArrays) + { + DataRow row = dt.NewRow(); + for (int i = 0; i < a.Value.Count; i++) + row[i] = a.Value[i].ToString(); + dt.Rows.Add(row); + } + AircraftDatabase_old.InsertOrUpdateTable(dt); + } + catch (Exception ex) + { + // Error loading database + Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().ToString() + "[" + url + "]: " + ex.Message); + } + } + + private void bw_DatabaseUpdater_DoWork(object sender, DoWorkEventArgs e) + { + while (!bw_DatabaseUpdater.CancellationPending) + { + ReadAircraftsFromURL("http://www.airscout.eu/downloads/database/Aircrafts.json",Path.Combine(TmpDirectory,"Aircrafts.json")); + ReadAircraftTypesFromURL("http://www.airscout.eu/downloads/database/AircraftTypes.json", Path.Combine(TmpDirectory, "AircraftTypes.json")); + ReadAircraftRegistrationsFromURL("http://www.airscout.eu/downloads/database/AircraftRegistrations.json", Path.Combine(TmpDirectory, "AircraftRegistrations.json")); + ReadAirlinesFromURL("http://www.airscout.eu/downloads/database/Airlines.json", Path.Combine(TmpDirectory, "Airlines.json")); + ReadAirportsFromURL("http://www.airscout.eu/downloads/database/Airports.json", Path.Combine(TmpDirectory, "Airports.json")); + + Thread.Sleep(60000); + } + } + + private void bw_DatabaseUpdater_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + + } + + private void bw_DatabaseUpdater_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + + } + + } +} diff --git a/AirScoutPlaneServer/JSONWriter.cs b/AirScoutPlaneServer/JSONWriter.cs new file mode 100644 index 0000000..e8a233e --- /dev/null +++ b/AirScoutPlaneServer/JSONWriter.cs @@ -0,0 +1,96 @@ + +using System; +using System.ComponentModel; +using System.Data; +using System.Windows.Forms; +using System.IO; +using System.Threading; +using System.Globalization; +using System.Reflection; +using AirScout.Database.Core; +using AirScout.Database.Aircrafts; +using ScoutBase.Core; + +namespace AirScoutPlaneServer +{ + + public partial class MainDlg : Form + { + + private void bw_JSONWriter_DoWork(object sender, DoWorkEventArgs e) + { + while (!bw_JSONWriter.CancellationPending) + { + Thread.Sleep(60000); + DataTable dt = new DataTable(); + // DataTable dt = FlightRadar.GetPlanePositions((int)Properties.Settings.Default.Planes_Lifetime); + // get planes each minute + // write json file + try + { + using (StreamWriter sw = new StreamWriter(TmpDirectory + Path.DirectorySeparatorChar + "planes.json")) + { + int major = Assembly.GetExecutingAssembly().GetName().Version.Major; + sw.Write("{\"full_count\":" + dt.Rows.Count.ToString() + ",\"version\":" + major.ToString()); + for (int i = 0; i < dt.Rows.Count; i++) + { + string index = "\"" + i.ToString("x8") + "\""; + string hex = "\"" + dt.Rows[i]["Hex"].ToString() + "\""; + string lat = ((double)dt.Rows[i]["Lat"]).ToString("F4", CultureInfo.InvariantCulture); + string lon = ((double)dt.Rows[i]["Lon"]).ToString("F4", CultureInfo.InvariantCulture); + string track = dt.Rows[i]["Track"].ToString(); + string alt = UnitConverter.m_ft((double)dt.Rows[i]["Alt"]).ToString("F0"); + string speed = UnitConverter.kmh_kts((double)dt.Rows[i]["Speed"]).ToString("F0"); + string squawk = "\"" + dt.Rows[i]["Squawk"].ToString() + "\""; + string radar = "\"" + dt.Rows[i]["Radar"].ToString() + "\""; + AircraftDesignator d = AircraftDatabase_old.AircraftFindByHex(dt.Rows[i]["Hex"].ToString()); + string type; + if (d != null) + type = "\"" + d.TypeCode + "\""; + else + type = "\"" + "\""; + string reg = "\"" + dt.Rows[i]["Reg"].ToString() + "\""; + DateTime rtime = System.Convert.ToDateTime(dt.Rows[i]["Time"].ToString()); + rtime = rtime.ToUniversalTime(); + DateTime sTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + string time = ((long)(rtime - sTime).TotalSeconds).ToString(); + string dep = "\"\""; + string dest = "\"\""; + string flight = "\"\""; + string dummy0 = "\"\""; + string dummy1 = "0"; + string dummy2 = "0"; + string call = "\"" + dt.Rows[i]["Call"].ToString() + "\""; + string dummy3 = "0"; + sw.WriteLine("," + index + ":[" + + hex + "," + + lat + "," + + lon + "," + + track + "," + + alt + "," + + speed + "," + + squawk + "," + + radar + "," + + type + "," + + reg + "," + + time + "," + + dep + "," + + dest + "," + + flight + "," + + dummy1 + "," + + dummy2 + "," + + call + "," + + dummy3 + + "]"); + } + sw.WriteLine("}"); + } + } + catch + { + // do nothing + } + } + } + } +} diff --git a/AirScoutPlaneServer/MainDlg.Designer.cs b/AirScoutPlaneServer/MainDlg.Designer.cs new file mode 100644 index 0000000..1878f67 --- /dev/null +++ b/AirScoutPlaneServer/MainDlg.Designer.cs @@ -0,0 +1,1275 @@ +namespace AirScoutPlaneServer +{ + partial class MainDlg + { + /// + /// Erforderliche Designervariable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Verwendete Ressourcen bereinigen. + /// + /// True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Vom Windows Form-Designer generierter Code + + /// + /// Erforderliche Methode für die Designerunterstützung. + /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.ss_Main = new System.Windows.Forms.StatusStrip(); + this.tsl_Main = new System.Windows.Forms.ToolStripStatusLabel(); + this.tsl_Spring = new System.Windows.Forms.ToolStripStatusLabel(); + this.tsl_CPULoad = new System.Windows.Forms.ToolStripStatusLabel(); + this.tsl_MemoryAvailable = new System.Windows.Forms.ToolStripStatusLabel(); + this.tsl_DBSize = new System.Windows.Forms.ToolStripStatusLabel(); + this.tsl_Uptime = new System.Windows.Forms.ToolStripStatusLabel(); + this.btn_Close = new System.Windows.Forms.Button(); + this.ni_Main = new System.Windows.Forms.NotifyIcon(this.components); + this.cms_NotifyIcon = new System.Windows.Forms.ContextMenuStrip(this.components); + this.tsi_Restore = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); + this.tsi_Close = new System.Windows.Forms.ToolStripMenuItem(); + this.bw_Webserver = new System.ComponentModel.BackgroundWorker(); + this.ti_Main = new System.Windows.Forms.Timer(this.components); + this.bw_ADSBFeed = new AirScout.PlaneFeeds.PlaneFeed_ADSB(); + this.bw_JSONWriter = new System.ComponentModel.BackgroundWorker(); + this.bw_DatabaseUpdater = new System.ComponentModel.BackgroundWorker(); + this.tp_ADSBFeed = new System.Windows.Forms.TabPage(); + this.tp_WebFeed1 = new System.Windows.Forms.TabPage(); + this.panel2 = new System.Windows.Forms.Panel(); + this.pg_WebFeed1 = new System.Windows.Forms.PropertyGrid(); + this.panel1 = new System.Windows.Forms.Panel(); + this.cb_WebFeed1 = new System.Windows.Forms.ComboBox(); + this.btn_WebFeed1_Export = new System.Windows.Forms.Button(); + this.btn_WebFeed1_Import = new System.Windows.Forms.Button(); + this.tp_Planes = new System.Windows.Forms.TabPage(); + this.gb_CoveredArea = new System.Windows.Forms.GroupBox(); + this.ud_Planes_Lifetime = new System.Windows.Forms.NumericUpDown(); + this.label15 = new System.Windows.Forms.Label(); + this.ud_Planes_MaxAlt = new System.Windows.Forms.NumericUpDown(); + this.label9 = new System.Windows.Forms.Label(); + this.ud_Planes_MinAlt = new System.Windows.Forms.NumericUpDown(); + this.label8 = new System.Windows.Forms.Label(); + this.gm_Planes_Coverage = new GMap.NET.WindowsForms.GMapControl(); + this.ud_MinLat = new System.Windows.Forms.NumericUpDown(); + this.ud_MaxLat = new System.Windows.Forms.NumericUpDown(); + this.ud_MinLon = new System.Windows.Forms.NumericUpDown(); + this.ud_MaxLon = new System.Windows.Forms.NumericUpDown(); + this.label4 = new System.Windows.Forms.Label(); + this.label5 = new System.Windows.Forms.Label(); + this.label6 = new System.Windows.Forms.Label(); + this.label7 = new System.Windows.Forms.Label(); + this.tp_Server = new System.Windows.Forms.TabPage(); + this.gb_Network_Server = new System.Windows.Forms.GroupBox(); + this.label1 = new System.Windows.Forms.Label(); + this.ud_Server_Port = new System.Windows.Forms.NumericUpDown(); + this.cb_Server_Active = new System.Windows.Forms.CheckBox(); + this.tp_Database = new System.Windows.Forms.TabPage(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.label13 = new System.Windows.Forms.Label(); + this.label14 = new System.Windows.Forms.Label(); + this.tb_Database_PlanePositions_RowCount = new System.Windows.Forms.TextBox(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.label12 = new System.Windows.Forms.Label(); + this.label11 = new System.Windows.Forms.Label(); + this.tb_Database_Filesize = new System.Windows.Forms.TextBox(); + this.label10 = new System.Windows.Forms.Label(); + this.tb_Database_Location = new System.Windows.Forms.TextBox(); + this.tp_General = new System.Windows.Forms.TabPage(); + this.gb_LogWriter = new System.Windows.Forms.GroupBox(); + this.btn_Log_Show = new System.Windows.Forms.Button(); + this.ud_Log_Verbosity = new System.Windows.Forms.NumericUpDown(); + this.label3 = new System.Windows.Forms.Label(); + this.btn_Log_Directory = new System.Windows.Forms.Button(); + this.cb_Log_Active = new System.Windows.Forms.CheckBox(); + this.label2 = new System.Windows.Forms.Label(); + this.tb_Log_Directory = new System.Windows.Forms.TextBox(); + this.gb_General_Windows = new System.Windows.Forms.GroupBox(); + this.cb_Windows_Startup_Autorun = new System.Windows.Forms.CheckBox(); + this.cb_Windows_Startup_Systray = new System.Windows.Forms.CheckBox(); + this.cb_Windows_Startup_Minimized = new System.Windows.Forms.CheckBox(); + this.tc_Main = new System.Windows.Forms.TabControl(); + this.tp_WebFeed2 = new System.Windows.Forms.TabPage(); + this.panel3 = new System.Windows.Forms.Panel(); + this.pg_WebFeed2 = new System.Windows.Forms.PropertyGrid(); + this.panel4 = new System.Windows.Forms.Panel(); + this.cb_WebFeed2 = new System.Windows.Forms.ComboBox(); + this.btn_WebFeed2_Export = new System.Windows.Forms.Button(); + this.btn_WebFeed2_Import = new System.Windows.Forms.Button(); + this.tp_WebFeed3 = new System.Windows.Forms.TabPage(); + this.panel5 = new System.Windows.Forms.Panel(); + this.pg_WebFeed3 = new System.Windows.Forms.PropertyGrid(); + this.panel6 = new System.Windows.Forms.Panel(); + this.cb_WebFeed3 = new System.Windows.Forms.ComboBox(); + this.btn_WebFeed3_Export = new System.Windows.Forms.Button(); + this.btn_WebFeed3_Import = new System.Windows.Forms.Button(); + this.ss_Main.SuspendLayout(); + this.cms_NotifyIcon.SuspendLayout(); + this.tp_WebFeed1.SuspendLayout(); + this.panel2.SuspendLayout(); + this.panel1.SuspendLayout(); + this.tp_Planes.SuspendLayout(); + this.gb_CoveredArea.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.ud_Planes_Lifetime)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.ud_Planes_MaxAlt)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.ud_Planes_MinAlt)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.ud_MinLat)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.ud_MaxLat)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.ud_MinLon)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.ud_MaxLon)).BeginInit(); + this.tp_Server.SuspendLayout(); + this.gb_Network_Server.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.ud_Server_Port)).BeginInit(); + this.tp_Database.SuspendLayout(); + this.groupBox2.SuspendLayout(); + this.groupBox1.SuspendLayout(); + this.tp_General.SuspendLayout(); + this.gb_LogWriter.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.ud_Log_Verbosity)).BeginInit(); + this.gb_General_Windows.SuspendLayout(); + this.tc_Main.SuspendLayout(); + this.tp_WebFeed2.SuspendLayout(); + this.panel3.SuspendLayout(); + this.panel4.SuspendLayout(); + this.tp_WebFeed3.SuspendLayout(); + this.panel5.SuspendLayout(); + this.panel6.SuspendLayout(); + this.SuspendLayout(); + // + // ss_Main + // + this.ss_Main.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.tsl_Main, + this.tsl_Spring, + this.tsl_CPULoad, + this.tsl_MemoryAvailable, + this.tsl_DBSize, + this.tsl_Uptime}); + this.ss_Main.Location = new System.Drawing.Point(0, 428); + this.ss_Main.Name = "ss_Main"; + this.ss_Main.Size = new System.Drawing.Size(634, 24); + this.ss_Main.TabIndex = 0; + this.ss_Main.Text = "statusStrip1"; + // + // tsl_Main + // + this.tsl_Main.BorderSides = ((System.Windows.Forms.ToolStripStatusLabelBorderSides)((System.Windows.Forms.ToolStripStatusLabelBorderSides.Left | System.Windows.Forms.ToolStripStatusLabelBorderSides.Right))); + this.tsl_Main.BorderStyle = System.Windows.Forms.Border3DStyle.Etched; + this.tsl_Main.Name = "tsl_Main"; + this.tsl_Main.Size = new System.Drawing.Size(46, 19); + this.tsl_Main.Text = "Ready."; + // + // tsl_Spring + // + this.tsl_Spring.Name = "tsl_Spring"; + this.tsl_Spring.Size = new System.Drawing.Size(353, 19); + this.tsl_Spring.Spring = true; + // + // tsl_CPULoad + // + this.tsl_CPULoad.BorderSides = ((System.Windows.Forms.ToolStripStatusLabelBorderSides)((System.Windows.Forms.ToolStripStatusLabelBorderSides.Left | System.Windows.Forms.ToolStripStatusLabelBorderSides.Right))); + this.tsl_CPULoad.BorderStyle = System.Windows.Forms.Border3DStyle.Etched; + this.tsl_CPULoad.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; + this.tsl_CPULoad.Name = "tsl_CPULoad"; + this.tsl_CPULoad.Size = new System.Drawing.Size(60, 19); + this.tsl_CPULoad.Text = "CPU load"; + // + // tsl_MemoryAvailable + // + this.tsl_MemoryAvailable.BorderSides = ((System.Windows.Forms.ToolStripStatusLabelBorderSides)((System.Windows.Forms.ToolStripStatusLabelBorderSides.Left | System.Windows.Forms.ToolStripStatusLabelBorderSides.Right))); + this.tsl_MemoryAvailable.BorderStyle = System.Windows.Forms.Border3DStyle.Etched; + this.tsl_MemoryAvailable.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; + this.tsl_MemoryAvailable.Name = "tsl_MemoryAvailable"; + this.tsl_MemoryAvailable.Size = new System.Drawing.Size(62, 19); + this.tsl_MemoryAvailable.Text = "MEM free"; + // + // tsl_DBSize + // + this.tsl_DBSize.BorderSides = ((System.Windows.Forms.ToolStripStatusLabelBorderSides)((System.Windows.Forms.ToolStripStatusLabelBorderSides.Left | System.Windows.Forms.ToolStripStatusLabelBorderSides.Right))); + this.tsl_DBSize.BorderStyle = System.Windows.Forms.Border3DStyle.Etched; + this.tsl_DBSize.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; + this.tsl_DBSize.Name = "tsl_DBSize"; + this.tsl_DBSize.Size = new System.Drawing.Size(48, 19); + this.tsl_DBSize.Text = "DB size"; + // + // tsl_Uptime + // + this.tsl_Uptime.BorderSides = ((System.Windows.Forms.ToolStripStatusLabelBorderSides)((System.Windows.Forms.ToolStripStatusLabelBorderSides.Left | System.Windows.Forms.ToolStripStatusLabelBorderSides.Right))); + this.tsl_Uptime.BorderStyle = System.Windows.Forms.Border3DStyle.Etched; + this.tsl_Uptime.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; + this.tsl_Uptime.Name = "tsl_Uptime"; + this.tsl_Uptime.Size = new System.Drawing.Size(50, 19); + this.tsl_Uptime.Text = "Uptime"; + // + // btn_Close + // + this.btn_Close.Location = new System.Drawing.Point(543, 390); + this.btn_Close.Name = "btn_Close"; + this.btn_Close.Size = new System.Drawing.Size(75, 23); + this.btn_Close.TabIndex = 2; + this.btn_Close.Text = "Close"; + this.btn_Close.UseVisualStyleBackColor = true; + this.btn_Close.Click += new System.EventHandler(this.btn_Close_Click); + // + // ni_Main + // + this.ni_Main.ContextMenuStrip = this.cms_NotifyIcon; + this.ni_Main.Text = "AirScout PlaneServer"; + this.ni_Main.Visible = true; + this.ni_Main.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.cms_NotifyIcon_MouseDoubleClick); + // + // cms_NotifyIcon + // + this.cms_NotifyIcon.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.tsi_Restore, + this.toolStripSeparator1, + this.tsi_Close}); + this.cms_NotifyIcon.Name = "cms_NotifyIcon"; + this.cms_NotifyIcon.Size = new System.Drawing.Size(119, 54); + this.cms_NotifyIcon.Opening += new System.ComponentModel.CancelEventHandler(this.cms_NotifyIcon_Opening); + this.cms_NotifyIcon.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.cms_NotifyIcon_MouseDoubleClick); + // + // tsi_Restore + // + this.tsi_Restore.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tsi_Restore.Name = "tsi_Restore"; + this.tsi_Restore.Size = new System.Drawing.Size(118, 22); + this.tsi_Restore.Text = "&Restore"; + this.tsi_Restore.Click += new System.EventHandler(this.tsi_Restore_Click); + // + // toolStripSeparator1 + // + this.toolStripSeparator1.Name = "toolStripSeparator1"; + this.toolStripSeparator1.Size = new System.Drawing.Size(115, 6); + // + // tsi_Close + // + this.tsi_Close.Name = "tsi_Close"; + this.tsi_Close.Size = new System.Drawing.Size(118, 22); + this.tsi_Close.Text = "&Close"; + this.tsi_Close.Click += new System.EventHandler(this.tsi_Close_Click); + // + // bw_Webserver + // + this.bw_Webserver.WorkerReportsProgress = true; + this.bw_Webserver.WorkerSupportsCancellation = true; + this.bw_Webserver.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bw_Webserver_DoWork); + this.bw_Webserver.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.bw_Webserver_ProgressChanged); + // + // ti_Main + // + this.ti_Main.Interval = 1000; + this.ti_Main.Tick += new System.EventHandler(this.ti_Main_Tick); + // + // bw_ADSBFeed + // + this.bw_ADSBFeed.DisclaimerAccepted = ""; + this.bw_ADSBFeed.WorkerReportsProgress = true; + this.bw_ADSBFeed.WorkerSupportsCancellation = true; + // + // bw_JSONWriter + // + this.bw_JSONWriter.WorkerReportsProgress = true; + this.bw_JSONWriter.WorkerSupportsCancellation = true; + this.bw_JSONWriter.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bw_JSONWriter_DoWork); + // + // bw_DatabaseUpdater + // + this.bw_DatabaseUpdater.WorkerReportsProgress = true; + this.bw_DatabaseUpdater.WorkerSupportsCancellation = true; + this.bw_DatabaseUpdater.DoWork += new System.ComponentModel.DoWorkEventHandler(this.bw_DatabaseUpdater_DoWork); + this.bw_DatabaseUpdater.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.bw_DatabaseUpdater_ProgressChanged); + // + // tp_ADSBFeed + // + this.tp_ADSBFeed.BackColor = System.Drawing.SystemColors.Control; + this.tp_ADSBFeed.Location = new System.Drawing.Point(4, 22); + this.tp_ADSBFeed.Name = "tp_ADSBFeed"; + this.tp_ADSBFeed.Padding = new System.Windows.Forms.Padding(3); + this.tp_ADSBFeed.Size = new System.Drawing.Size(602, 337); + this.tp_ADSBFeed.TabIndex = 6; + this.tp_ADSBFeed.Text = "ADS-B Feed"; + // + // tp_WebFeed1 + // + this.tp_WebFeed1.BackColor = System.Drawing.SystemColors.Control; + this.tp_WebFeed1.Controls.Add(this.panel2); + this.tp_WebFeed1.Controls.Add(this.panel1); + this.tp_WebFeed1.Location = new System.Drawing.Point(4, 22); + this.tp_WebFeed1.Name = "tp_WebFeed1"; + this.tp_WebFeed1.Padding = new System.Windows.Forms.Padding(3); + this.tp_WebFeed1.Size = new System.Drawing.Size(602, 337); + this.tp_WebFeed1.TabIndex = 3; + this.tp_WebFeed1.Text = "WebFeed 1"; + this.tp_WebFeed1.Enter += new System.EventHandler(this.tp_WebFeed1_Enter); + // + // panel2 + // + this.panel2.Controls.Add(this.pg_WebFeed1); + this.panel2.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel2.Location = new System.Drawing.Point(3, 59); + this.panel2.Name = "panel2"; + this.panel2.Size = new System.Drawing.Size(596, 275); + this.panel2.TabIndex = 4; + // + // pg_WebFeed1 + // + this.pg_WebFeed1.Dock = System.Windows.Forms.DockStyle.Fill; + this.pg_WebFeed1.Location = new System.Drawing.Point(0, 0); + this.pg_WebFeed1.Name = "pg_WebFeed1"; + this.pg_WebFeed1.Size = new System.Drawing.Size(596, 275); + this.pg_WebFeed1.TabIndex = 0; + // + // panel1 + // + this.panel1.Controls.Add(this.cb_WebFeed1); + this.panel1.Controls.Add(this.btn_WebFeed1_Export); + this.panel1.Controls.Add(this.btn_WebFeed1_Import); + this.panel1.Dock = System.Windows.Forms.DockStyle.Top; + this.panel1.Location = new System.Drawing.Point(3, 3); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(596, 56); + this.panel1.TabIndex = 3; + // + // cb_WebFeed1 + // + this.cb_WebFeed1.DisplayMember = "Name"; + this.cb_WebFeed1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cb_WebFeed1.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_WebFeed1.FormattingEnabled = true; + this.cb_WebFeed1.Location = new System.Drawing.Point(22, 16); + this.cb_WebFeed1.Name = "cb_WebFeed1"; + this.cb_WebFeed1.Size = new System.Drawing.Size(367, 24); + this.cb_WebFeed1.TabIndex = 3; + this.cb_WebFeed1.SelectedIndexChanged += new System.EventHandler(this.cb_WebFeed1_SelectedIndexChanged); + // + // btn_WebFeed1_Export + // + this.btn_WebFeed1_Export.Location = new System.Drawing.Point(493, 15); + this.btn_WebFeed1_Export.Name = "btn_WebFeed1_Export"; + this.btn_WebFeed1_Export.Size = new System.Drawing.Size(75, 23); + this.btn_WebFeed1_Export.TabIndex = 2; + this.btn_WebFeed1_Export.Text = "Export"; + this.btn_WebFeed1_Export.UseVisualStyleBackColor = true; + this.btn_WebFeed1_Export.Click += new System.EventHandler(this.btn_WebFeed1_Export_Click); + // + // btn_WebFeed1_Import + // + this.btn_WebFeed1_Import.Location = new System.Drawing.Point(412, 15); + this.btn_WebFeed1_Import.Name = "btn_WebFeed1_Import"; + this.btn_WebFeed1_Import.Size = new System.Drawing.Size(75, 23); + this.btn_WebFeed1_Import.TabIndex = 1; + this.btn_WebFeed1_Import.Text = "Import"; + this.btn_WebFeed1_Import.UseVisualStyleBackColor = true; + this.btn_WebFeed1_Import.Click += new System.EventHandler(this.btn_WebFeed1_Import_Click); + // + // tp_Planes + // + this.tp_Planes.BackColor = System.Drawing.SystemColors.Control; + this.tp_Planes.Controls.Add(this.gb_CoveredArea); + this.tp_Planes.Location = new System.Drawing.Point(4, 22); + this.tp_Planes.Name = "tp_Planes"; + this.tp_Planes.Padding = new System.Windows.Forms.Padding(3); + this.tp_Planes.Size = new System.Drawing.Size(602, 337); + this.tp_Planes.TabIndex = 2; + this.tp_Planes.Text = "Planes"; + this.tp_Planes.Enter += new System.EventHandler(this.tab_Planes_Enter); + // + // gb_CoveredArea + // + this.gb_CoveredArea.Controls.Add(this.ud_Planes_Lifetime); + this.gb_CoveredArea.Controls.Add(this.label15); + this.gb_CoveredArea.Controls.Add(this.ud_Planes_MaxAlt); + this.gb_CoveredArea.Controls.Add(this.label9); + this.gb_CoveredArea.Controls.Add(this.ud_Planes_MinAlt); + this.gb_CoveredArea.Controls.Add(this.label8); + this.gb_CoveredArea.Controls.Add(this.gm_Planes_Coverage); + this.gb_CoveredArea.Controls.Add(this.ud_MinLat); + this.gb_CoveredArea.Controls.Add(this.ud_MaxLat); + this.gb_CoveredArea.Controls.Add(this.ud_MinLon); + this.gb_CoveredArea.Controls.Add(this.ud_MaxLon); + this.gb_CoveredArea.Controls.Add(this.label4); + this.gb_CoveredArea.Controls.Add(this.label5); + this.gb_CoveredArea.Controls.Add(this.label6); + this.gb_CoveredArea.Controls.Add(this.label7); + this.gb_CoveredArea.Location = new System.Drawing.Point(15, 15); + this.gb_CoveredArea.Name = "gb_CoveredArea"; + this.gb_CoveredArea.Size = new System.Drawing.Size(571, 316); + this.gb_CoveredArea.TabIndex = 0; + this.gb_CoveredArea.TabStop = false; + this.gb_CoveredArea.Text = "Covered Area"; + // + // ud_Planes_Lifetime + // + this.ud_Planes_Lifetime.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScoutPlaneServer.Properties.Settings.Default, "Planes_Lifetime", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.ud_Planes_Lifetime.Location = new System.Drawing.Point(476, 228); + this.ud_Planes_Lifetime.Maximum = new decimal(new int[] { + 60, + 0, + 0, + 0}); + this.ud_Planes_Lifetime.Minimum = new decimal(new int[] { + 1, + 0, + 0, + 0}); + this.ud_Planes_Lifetime.Name = "ud_Planes_Lifetime"; + this.ud_Planes_Lifetime.Size = new System.Drawing.Size(77, 20); + this.ud_Planes_Lifetime.TabIndex = 14; + this.ud_Planes_Lifetime.Value = global::AirScoutPlaneServer.Properties.Settings.Default.Planes_Lifetime; + // + // label15 + // + this.label15.AutoSize = true; + this.label15.Location = new System.Drawing.Point(346, 230); + this.label15.Name = "label15"; + this.label15.Size = new System.Drawing.Size(71, 13); + this.label15.TabIndex = 13; + this.label15.Text = "Lifetime [min]:"; + // + // ud_Planes_MaxAlt + // + this.ud_Planes_MaxAlt.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScoutPlaneServer.Properties.Settings.Default, "Planes_MaxAlt", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.ud_Planes_MaxAlt.Location = new System.Drawing.Point(479, 181); + this.ud_Planes_MaxAlt.Maximum = new decimal(new int[] { + 100000, + 0, + 0, + 0}); + this.ud_Planes_MaxAlt.Name = "ud_Planes_MaxAlt"; + this.ud_Planes_MaxAlt.Size = new System.Drawing.Size(77, 20); + this.ud_Planes_MaxAlt.TabIndex = 12; + this.ud_Planes_MaxAlt.Value = global::AirScoutPlaneServer.Properties.Settings.Default.Planes_MaxAlt; + // + // label9 + // + this.label9.AutoSize = true; + this.label9.Location = new System.Drawing.Point(349, 183); + this.label9.Name = "label9"; + this.label9.Size = new System.Drawing.Size(88, 13); + this.label9.TabIndex = 11; + this.label9.Text = "Max. Altitude [m]:"; + // + // ud_Planes_MinAlt + // + this.ud_Planes_MinAlt.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScoutPlaneServer.Properties.Settings.Default, "Planes_MinAlt", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.ud_Planes_MinAlt.Location = new System.Drawing.Point(479, 155); + this.ud_Planes_MinAlt.Maximum = new decimal(new int[] { + 12000, + 0, + 0, + 0}); + this.ud_Planes_MinAlt.Name = "ud_Planes_MinAlt"; + this.ud_Planes_MinAlt.Size = new System.Drawing.Size(77, 20); + this.ud_Planes_MinAlt.TabIndex = 10; + this.ud_Planes_MinAlt.Value = global::AirScoutPlaneServer.Properties.Settings.Default.Planes_MinAlt; + // + // label8 + // + this.label8.AutoSize = true; + this.label8.Location = new System.Drawing.Point(349, 157); + this.label8.Name = "label8"; + this.label8.Size = new System.Drawing.Size(85, 13); + this.label8.TabIndex = 9; + this.label8.Text = "Min. Altitude [m]:"; + // + // gm_Planes_Coverage + // + this.gm_Planes_Coverage.Bearing = 0F; + this.gm_Planes_Coverage.CanDragMap = true; + this.gm_Planes_Coverage.EmptyTileColor = System.Drawing.Color.Navy; + this.gm_Planes_Coverage.GrayScaleMode = false; + this.gm_Planes_Coverage.HelperLineOption = GMap.NET.WindowsForms.HelperLineOptions.DontShow; + this.gm_Planes_Coverage.LevelsKeepInMemmory = 5; + this.gm_Planes_Coverage.Location = new System.Drawing.Point(6, 19); + this.gm_Planes_Coverage.MarkersEnabled = true; + this.gm_Planes_Coverage.MaxZoom = 2; + this.gm_Planes_Coverage.MinZoom = 2; + this.gm_Planes_Coverage.MouseWheelZoomType = GMap.NET.MouseWheelZoomType.MousePositionAndCenter; + this.gm_Planes_Coverage.Name = "gm_Planes_Coverage"; + this.gm_Planes_Coverage.NegativeMode = false; + this.gm_Planes_Coverage.PolygonsEnabled = true; + this.gm_Planes_Coverage.RetryLoadTile = 0; + this.gm_Planes_Coverage.RoutesEnabled = true; + this.gm_Planes_Coverage.SelectedAreaFillColor = System.Drawing.Color.FromArgb(((int)(((byte)(33)))), ((int)(((byte)(65)))), ((int)(((byte)(105)))), ((int)(((byte)(225))))); + this.gm_Planes_Coverage.ShowTileGridLines = false; + this.gm_Planes_Coverage.Size = new System.Drawing.Size(337, 291); + this.gm_Planes_Coverage.TabIndex = 0; + this.gm_Planes_Coverage.Zoom = 0D; + // + // ud_MinLat + // + this.ud_MinLat.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScoutPlaneServer.Properties.Settings.Default, "Planes_MinLat", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.ud_MinLat.DecimalPlaces = 2; + this.ud_MinLat.Location = new System.Drawing.Point(479, 81); + this.ud_MinLat.Maximum = new decimal(new int[] { + 90, + 0, + 0, + 0}); + this.ud_MinLat.Minimum = new decimal(new int[] { + 90, + 0, + 0, + -2147483648}); + this.ud_MinLat.Name = "ud_MinLat"; + this.ud_MinLat.Size = new System.Drawing.Size(77, 20); + this.ud_MinLat.TabIndex = 6; + this.ud_MinLat.Value = global::AirScoutPlaneServer.Properties.Settings.Default.Planes_MinLat; + this.ud_MinLat.ValueChanged += new System.EventHandler(this.ud_Planes_LatLon_ValueChanged); + // + // ud_MaxLat + // + this.ud_MaxLat.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScoutPlaneServer.Properties.Settings.Default, "Planes_MaxLat", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.ud_MaxLat.DecimalPlaces = 2; + this.ud_MaxLat.Location = new System.Drawing.Point(479, 107); + this.ud_MaxLat.Maximum = new decimal(new int[] { + 90, + 0, + 0, + 0}); + this.ud_MaxLat.Minimum = new decimal(new int[] { + 90, + 0, + 0, + -2147483648}); + this.ud_MaxLat.Name = "ud_MaxLat"; + this.ud_MaxLat.Size = new System.Drawing.Size(77, 20); + this.ud_MaxLat.TabIndex = 8; + this.ud_MaxLat.Value = global::AirScoutPlaneServer.Properties.Settings.Default.Planes_MaxLat; + this.ud_MaxLat.ValueChanged += new System.EventHandler(this.ud_Planes_LatLon_ValueChanged); + // + // ud_MinLon + // + this.ud_MinLon.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScoutPlaneServer.Properties.Settings.Default, "Planes_MinLon", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.ud_MinLon.DecimalPlaces = 2; + this.ud_MinLon.Location = new System.Drawing.Point(479, 29); + this.ud_MinLon.Maximum = new decimal(new int[] { + 180, + 0, + 0, + 0}); + this.ud_MinLon.Minimum = new decimal(new int[] { + 180, + 0, + 0, + -2147483648}); + this.ud_MinLon.Name = "ud_MinLon"; + this.ud_MinLon.Size = new System.Drawing.Size(77, 20); + this.ud_MinLon.TabIndex = 2; + this.ud_MinLon.Value = global::AirScoutPlaneServer.Properties.Settings.Default.Planes_MinLon; + this.ud_MinLon.ValueChanged += new System.EventHandler(this.ud_Planes_LatLon_ValueChanged); + // + // ud_MaxLon + // + this.ud_MaxLon.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScoutPlaneServer.Properties.Settings.Default, "Planes_MaxLon", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.ud_MaxLon.DecimalPlaces = 2; + this.ud_MaxLon.Location = new System.Drawing.Point(479, 55); + this.ud_MaxLon.Maximum = new decimal(new int[] { + 180, + 0, + 0, + 0}); + this.ud_MaxLon.Minimum = new decimal(new int[] { + 180, + 0, + 0, + -2147483648}); + this.ud_MaxLon.Name = "ud_MaxLon"; + this.ud_MaxLon.Size = new System.Drawing.Size(77, 20); + this.ud_MaxLon.TabIndex = 4; + this.ud_MaxLon.Value = global::AirScoutPlaneServer.Properties.Settings.Default.Planes_MaxLon; + this.ud_MaxLon.ValueChanged += new System.EventHandler(this.ud_Planes_LatLon_ValueChanged); + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(349, 31); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(120, 13); + this.label4.TabIndex = 1; + this.label4.Text = "Min. Lon [-90 ... 90deg]:"; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(349, 57); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(123, 13); + this.label5.TabIndex = 3; + this.label5.Text = "Max. Lon [-90 ... 90deg]:"; + // + // label6 + // + this.label6.AutoSize = true; + this.label6.Location = new System.Drawing.Point(349, 83); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(129, 13); + this.label6.TabIndex = 5; + this.label6.Text = "Min. Lat [-180 ... 180deg]:"; + // + // label7 + // + this.label7.AutoSize = true; + this.label7.Location = new System.Drawing.Point(349, 109); + this.label7.Name = "label7"; + this.label7.Size = new System.Drawing.Size(129, 13); + this.label7.TabIndex = 7; + this.label7.Text = "Max. Lat [-180 .. 180deg]:"; + // + // tp_Server + // + this.tp_Server.BackColor = System.Drawing.SystemColors.Control; + this.tp_Server.Controls.Add(this.gb_Network_Server); + this.tp_Server.Location = new System.Drawing.Point(4, 22); + this.tp_Server.Name = "tp_Server"; + this.tp_Server.Padding = new System.Windows.Forms.Padding(3); + this.tp_Server.Size = new System.Drawing.Size(602, 337); + this.tp_Server.TabIndex = 1; + this.tp_Server.Text = "Server"; + // + // gb_Network_Server + // + this.gb_Network_Server.Controls.Add(this.label1); + this.gb_Network_Server.Controls.Add(this.ud_Server_Port); + this.gb_Network_Server.Controls.Add(this.cb_Server_Active); + this.gb_Network_Server.Location = new System.Drawing.Point(15, 15); + this.gb_Network_Server.Name = "gb_Network_Server"; + this.gb_Network_Server.Size = new System.Drawing.Size(307, 94); + this.gb_Network_Server.TabIndex = 0; + this.gb_Network_Server.TabStop = false; + this.gb_Network_Server.Text = "Server"; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(15, 58); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(171, 13); + this.label1.TabIndex = 1; + this.label1.Text = "Port (1024...65535, Default=9872):"; + // + // ud_Server_Port + // + this.ud_Server_Port.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScoutPlaneServer.Properties.Settings.Default, "Webserver_Port", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.ud_Server_Port.Location = new System.Drawing.Point(218, 56); + this.ud_Server_Port.Maximum = new decimal(new int[] { + 65535, + 0, + 0, + 0}); + this.ud_Server_Port.Minimum = new decimal(new int[] { + 1024, + 0, + 0, + 0}); + this.ud_Server_Port.Name = "ud_Server_Port"; + this.ud_Server_Port.Size = new System.Drawing.Size(71, 20); + this.ud_Server_Port.TabIndex = 3; + this.ud_Server_Port.Value = global::AirScoutPlaneServer.Properties.Settings.Default.Webserver_Port; + // + // cb_Server_Active + // + this.cb_Server_Active.CheckAlign = System.Drawing.ContentAlignment.MiddleRight; + this.cb_Server_Active.Checked = global::AirScoutPlaneServer.Properties.Settings.Default.Webserver_Active; + this.cb_Server_Active.CheckState = System.Windows.Forms.CheckState.Checked; + this.cb_Server_Active.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScoutPlaneServer.Properties.Settings.Default, "Webserver_Active", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Server_Active.Location = new System.Drawing.Point(15, 29); + this.cb_Server_Active.Name = "cb_Server_Active"; + this.cb_Server_Active.Size = new System.Drawing.Size(274, 21); + this.cb_Server_Active.TabIndex = 2; + this.cb_Server_Active.Text = "Activate Server:"; + this.cb_Server_Active.UseVisualStyleBackColor = true; + this.cb_Server_Active.CheckedChanged += new System.EventHandler(this.cb_Server_Active_CheckedChanged); + // + // tp_Database + // + this.tp_Database.BackColor = System.Drawing.SystemColors.Control; + this.tp_Database.Controls.Add(this.groupBox2); + this.tp_Database.Controls.Add(this.groupBox1); + this.tp_Database.Location = new System.Drawing.Point(4, 22); + this.tp_Database.Name = "tp_Database"; + this.tp_Database.Padding = new System.Windows.Forms.Padding(3); + this.tp_Database.Size = new System.Drawing.Size(602, 337); + this.tp_Database.TabIndex = 7; + this.tp_Database.Text = "Database"; + this.tp_Database.Enter += new System.EventHandler(this.tp_Database_Enter); + // + // groupBox2 + // + this.groupBox2.Controls.Add(this.label13); + this.groupBox2.Controls.Add(this.label14); + this.groupBox2.Controls.Add(this.tb_Database_PlanePositions_RowCount); + this.groupBox2.Location = new System.Drawing.Point(15, 105); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Size = new System.Drawing.Size(570, 84); + this.groupBox2.TabIndex = 1; + this.groupBox2.TabStop = false; + this.groupBox2.Text = "Plane Positions"; + // + // label13 + // + this.label13.AutoSize = true; + this.label13.Location = new System.Drawing.Point(216, 31); + this.label13.Name = "label13"; + this.label13.Size = new System.Drawing.Size(41, 13); + this.label13.TabIndex = 4; + this.label13.Text = "entries."; + // + // label14 + // + this.label14.AutoSize = true; + this.label14.Location = new System.Drawing.Point(6, 31); + this.label14.Name = "label14"; + this.label14.Size = new System.Drawing.Size(38, 13); + this.label14.TabIndex = 3; + this.label14.Text = "Count:"; + // + // tb_Database_PlanePositions_RowCount + // + this.tb_Database_PlanePositions_RowCount.Location = new System.Drawing.Point(63, 28); + this.tb_Database_PlanePositions_RowCount.Name = "tb_Database_PlanePositions_RowCount"; + this.tb_Database_PlanePositions_RowCount.ReadOnly = true; + this.tb_Database_PlanePositions_RowCount.Size = new System.Drawing.Size(136, 20); + this.tb_Database_PlanePositions_RowCount.TabIndex = 2; + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.label12); + this.groupBox1.Controls.Add(this.label11); + this.groupBox1.Controls.Add(this.tb_Database_Filesize); + this.groupBox1.Controls.Add(this.label10); + this.groupBox1.Controls.Add(this.tb_Database_Location); + this.groupBox1.Location = new System.Drawing.Point(15, 15); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(570, 84); + this.groupBox1.TabIndex = 0; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Database Information"; + // + // label12 + // + this.label12.AutoSize = true; + this.label12.Location = new System.Drawing.Point(141, 46); + this.label12.Name = "label12"; + this.label12.Size = new System.Drawing.Size(26, 13); + this.label12.TabIndex = 4; + this.label12.Text = "MB."; + // + // label11 + // + this.label11.AutoSize = true; + this.label11.Location = new System.Drawing.Point(6, 46); + this.label11.Name = "label11"; + this.label11.Size = new System.Drawing.Size(44, 13); + this.label11.TabIndex = 3; + this.label11.Text = "Filesize:"; + // + // tb_Database_Filesize + // + this.tb_Database_Filesize.Location = new System.Drawing.Point(63, 43); + this.tb_Database_Filesize.Name = "tb_Database_Filesize"; + this.tb_Database_Filesize.ReadOnly = true; + this.tb_Database_Filesize.Size = new System.Drawing.Size(72, 20); + this.tb_Database_Filesize.TabIndex = 2; + // + // label10 + // + this.label10.AutoSize = true; + this.label10.Location = new System.Drawing.Point(6, 22); + this.label10.Name = "label10"; + this.label10.Size = new System.Drawing.Size(51, 13); + this.label10.TabIndex = 1; + this.label10.Text = "Location:"; + // + // tb_Database_Location + // + this.tb_Database_Location.Location = new System.Drawing.Point(63, 19); + this.tb_Database_Location.Name = "tb_Database_Location"; + this.tb_Database_Location.ReadOnly = true; + this.tb_Database_Location.Size = new System.Drawing.Size(485, 20); + this.tb_Database_Location.TabIndex = 0; + // + // tp_General + // + this.tp_General.BackColor = System.Drawing.SystemColors.Control; + this.tp_General.Controls.Add(this.gb_LogWriter); + this.tp_General.Controls.Add(this.gb_General_Windows); + this.tp_General.Location = new System.Drawing.Point(4, 22); + this.tp_General.Name = "tp_General"; + this.tp_General.Padding = new System.Windows.Forms.Padding(3); + this.tp_General.Size = new System.Drawing.Size(602, 337); + this.tp_General.TabIndex = 0; + this.tp_General.Text = "General"; + // + // gb_LogWriter + // + this.gb_LogWriter.Controls.Add(this.btn_Log_Show); + this.gb_LogWriter.Controls.Add(this.ud_Log_Verbosity); + this.gb_LogWriter.Controls.Add(this.label3); + this.gb_LogWriter.Controls.Add(this.btn_Log_Directory); + this.gb_LogWriter.Controls.Add(this.cb_Log_Active); + this.gb_LogWriter.Controls.Add(this.label2); + this.gb_LogWriter.Controls.Add(this.tb_Log_Directory); + this.gb_LogWriter.Location = new System.Drawing.Point(15, 134); + this.gb_LogWriter.Name = "gb_LogWriter"; + this.gb_LogWriter.Size = new System.Drawing.Size(570, 113); + this.gb_LogWriter.TabIndex = 1; + this.gb_LogWriter.TabStop = false; + this.gb_LogWriter.Text = "Log"; + // + // btn_Log_Show + // + this.btn_Log_Show.Location = new System.Drawing.Point(474, 15); + this.btn_Log_Show.Name = "btn_Log_Show"; + this.btn_Log_Show.Size = new System.Drawing.Size(75, 23); + this.btn_Log_Show.TabIndex = 6; + this.btn_Log_Show.Text = "Show"; + this.btn_Log_Show.UseVisualStyleBackColor = true; + this.btn_Log_Show.Click += new System.EventHandler(this.btn_Log_Show_Click); + // + // ud_Log_Verbosity + // + this.ud_Log_Verbosity.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::AirScoutPlaneServer.Properties.Settings.Default, "Log_Verbosity", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.ud_Log_Verbosity.Location = new System.Drawing.Point(116, 77); + this.ud_Log_Verbosity.Maximum = new decimal(new int[] { + 10, + 0, + 0, + 0}); + this.ud_Log_Verbosity.Minimum = new decimal(new int[] { + 1, + 0, + 0, + 0}); + this.ud_Log_Verbosity.Name = "ud_Log_Verbosity"; + this.ud_Log_Verbosity.Size = new System.Drawing.Size(37, 20); + this.ud_Log_Verbosity.TabIndex = 5; + this.ud_Log_Verbosity.Value = global::AirScoutPlaneServer.Properties.Settings.Default.Log_Verbosity; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(12, 79); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(80, 13); + this.label3.TabIndex = 4; + this.label3.Text = "Verbosity (1..9):"; + // + // btn_Log_Directory + // + this.btn_Log_Directory.Location = new System.Drawing.Point(474, 46); + this.btn_Log_Directory.Name = "btn_Log_Directory"; + this.btn_Log_Directory.Size = new System.Drawing.Size(75, 23); + this.btn_Log_Directory.TabIndex = 3; + this.btn_Log_Directory.Text = "Select"; + this.btn_Log_Directory.UseVisualStyleBackColor = true; + this.btn_Log_Directory.Click += new System.EventHandler(this.btn_Log_Directory_Click); + // + // cb_Log_Active + // + this.cb_Log_Active.CheckAlign = System.Drawing.ContentAlignment.MiddleRight; + this.cb_Log_Active.Checked = global::AirScoutPlaneServer.Properties.Settings.Default.Log_Active; + this.cb_Log_Active.CheckState = System.Windows.Forms.CheckState.Checked; + this.cb_Log_Active.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScoutPlaneServer.Properties.Settings.Default, "Log_Active", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Log_Active.Location = new System.Drawing.Point(13, 19); + this.cb_Log_Active.Name = "cb_Log_Active"; + this.cb_Log_Active.Size = new System.Drawing.Size(120, 19); + this.cb_Log_Active.TabIndex = 3; + this.cb_Log_Active.Text = "Activate Logging:"; + this.cb_Log_Active.UseVisualStyleBackColor = true; + this.cb_Log_Active.CheckedChanged += new System.EventHandler(this.cb_Log_Active_CheckedChanged); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(12, 51); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(52, 13); + this.label2.TabIndex = 1; + this.label2.Text = "Directory:"; + // + // tb_Log_Directory + // + this.tb_Log_Directory.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::AirScoutPlaneServer.Properties.Settings.Default, "Log_Directory", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_Log_Directory.Location = new System.Drawing.Point(116, 48); + this.tb_Log_Directory.Name = "tb_Log_Directory"; + this.tb_Log_Directory.Size = new System.Drawing.Size(333, 20); + this.tb_Log_Directory.TabIndex = 0; + this.tb_Log_Directory.Text = global::AirScoutPlaneServer.Properties.Settings.Default.Log_Directory; + // + // gb_General_Windows + // + this.gb_General_Windows.Controls.Add(this.cb_Windows_Startup_Autorun); + this.gb_General_Windows.Controls.Add(this.cb_Windows_Startup_Systray); + this.gb_General_Windows.Controls.Add(this.cb_Windows_Startup_Minimized); + this.gb_General_Windows.Location = new System.Drawing.Point(15, 15); + this.gb_General_Windows.Name = "gb_General_Windows"; + this.gb_General_Windows.Size = new System.Drawing.Size(570, 113); + this.gb_General_Windows.TabIndex = 0; + this.gb_General_Windows.TabStop = false; + this.gb_General_Windows.Text = "Windows"; + // + // cb_Windows_Startup_Autorun + // + this.cb_Windows_Startup_Autorun.AutoSize = true; + this.cb_Windows_Startup_Autorun.Checked = global::AirScoutPlaneServer.Properties.Settings.Default.Windows_Startup_Autorun; + this.cb_Windows_Startup_Autorun.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScoutPlaneServer.Properties.Settings.Default, "Windows_Startup_Autorun", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Windows_Startup_Autorun.Location = new System.Drawing.Point(15, 29); + this.cb_Windows_Startup_Autorun.Name = "cb_Windows_Startup_Autorun"; + this.cb_Windows_Startup_Autorun.Size = new System.Drawing.Size(212, 17); + this.cb_Windows_Startup_Autorun.TabIndex = 2; + this.cb_Windows_Startup_Autorun.Text = "Startup on System Start (Windows only)"; + this.cb_Windows_Startup_Autorun.UseVisualStyleBackColor = true; + this.cb_Windows_Startup_Autorun.CheckedChanged += new System.EventHandler(this.cb_Windows_Startup_Autorun_CheckedChanged); + // + // cb_Windows_Startup_Systray + // + this.cb_Windows_Startup_Systray.AutoSize = true; + this.cb_Windows_Startup_Systray.Checked = global::AirScoutPlaneServer.Properties.Settings.Default.Windows_Startup_Systray; + this.cb_Windows_Startup_Systray.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScoutPlaneServer.Properties.Settings.Default, "Windows_Startup_Systray", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Windows_Startup_Systray.Location = new System.Drawing.Point(15, 75); + this.cb_Windows_Startup_Systray.Name = "cb_Windows_Startup_Systray"; + this.cb_Windows_Startup_Systray.Size = new System.Drawing.Size(277, 17); + this.cb_Windows_Startup_Systray.TabIndex = 1; + this.cb_Windows_Startup_Systray.Text = "Show on systray only when minimized (Windows only)"; + this.cb_Windows_Startup_Systray.UseVisualStyleBackColor = true; + // + // cb_Windows_Startup_Minimized + // + this.cb_Windows_Startup_Minimized.AutoSize = true; + this.cb_Windows_Startup_Minimized.Checked = global::AirScoutPlaneServer.Properties.Settings.Default.Windows_Startup_Minimized; + this.cb_Windows_Startup_Minimized.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScoutPlaneServer.Properties.Settings.Default, "Windows_Startup_Minimized", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Windows_Startup_Minimized.Location = new System.Drawing.Point(15, 52); + this.cb_Windows_Startup_Minimized.Name = "cb_Windows_Startup_Minimized"; + this.cb_Windows_Startup_Minimized.Size = new System.Drawing.Size(183, 17); + this.cb_Windows_Startup_Minimized.TabIndex = 0; + this.cb_Windows_Startup_Minimized.Text = "Startup minimized (Windows only)"; + this.cb_Windows_Startup_Minimized.UseVisualStyleBackColor = true; + // + // tc_Main + // + this.tc_Main.Controls.Add(this.tp_General); + this.tc_Main.Controls.Add(this.tp_Database); + this.tc_Main.Controls.Add(this.tp_Server); + this.tc_Main.Controls.Add(this.tp_Planes); + this.tc_Main.Controls.Add(this.tp_WebFeed1); + this.tc_Main.Controls.Add(this.tp_WebFeed2); + this.tc_Main.Controls.Add(this.tp_WebFeed3); + this.tc_Main.Controls.Add(this.tp_ADSBFeed); + this.tc_Main.Location = new System.Drawing.Point(12, 12); + this.tc_Main.Name = "tc_Main"; + this.tc_Main.SelectedIndex = 0; + this.tc_Main.Size = new System.Drawing.Size(610, 363); + this.tc_Main.TabIndex = 1; + // + // tp_WebFeed2 + // + this.tp_WebFeed2.BackColor = System.Drawing.SystemColors.Control; + this.tp_WebFeed2.Controls.Add(this.panel3); + this.tp_WebFeed2.Controls.Add(this.panel4); + this.tp_WebFeed2.Location = new System.Drawing.Point(4, 22); + this.tp_WebFeed2.Name = "tp_WebFeed2"; + this.tp_WebFeed2.Padding = new System.Windows.Forms.Padding(3); + this.tp_WebFeed2.Size = new System.Drawing.Size(602, 337); + this.tp_WebFeed2.TabIndex = 8; + this.tp_WebFeed2.Text = "WebFeed 2"; + this.tp_WebFeed2.Enter += new System.EventHandler(this.tp_WebFeed2_Enter); + // + // panel3 + // + this.panel3.Controls.Add(this.pg_WebFeed2); + this.panel3.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel3.Location = new System.Drawing.Point(3, 59); + this.panel3.Name = "panel3"; + this.panel3.Size = new System.Drawing.Size(596, 275); + this.panel3.TabIndex = 4; + // + // pg_WebFeed2 + // + this.pg_WebFeed2.Dock = System.Windows.Forms.DockStyle.Fill; + this.pg_WebFeed2.Location = new System.Drawing.Point(0, 0); + this.pg_WebFeed2.Name = "pg_WebFeed2"; + this.pg_WebFeed2.Size = new System.Drawing.Size(596, 275); + this.pg_WebFeed2.TabIndex = 0; + // + // panel4 + // + this.panel4.Controls.Add(this.cb_WebFeed2); + this.panel4.Controls.Add(this.btn_WebFeed2_Export); + this.panel4.Controls.Add(this.btn_WebFeed2_Import); + this.panel4.Dock = System.Windows.Forms.DockStyle.Top; + this.panel4.Location = new System.Drawing.Point(3, 3); + this.panel4.Name = "panel4"; + this.panel4.Size = new System.Drawing.Size(596, 56); + this.panel4.TabIndex = 3; + // + // cb_WebFeed2 + // + this.cb_WebFeed2.DisplayMember = "Name"; + this.cb_WebFeed2.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cb_WebFeed2.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_WebFeed2.FormattingEnabled = true; + this.cb_WebFeed2.Location = new System.Drawing.Point(22, 16); + this.cb_WebFeed2.Name = "cb_WebFeed2"; + this.cb_WebFeed2.Size = new System.Drawing.Size(367, 24); + this.cb_WebFeed2.TabIndex = 4; + this.cb_WebFeed2.SelectedIndexChanged += new System.EventHandler(this.cb_WebFeed2_SelectedIndexChanged); + // + // btn_WebFeed2_Export + // + this.btn_WebFeed2_Export.Location = new System.Drawing.Point(509, 15); + this.btn_WebFeed2_Export.Name = "btn_WebFeed2_Export"; + this.btn_WebFeed2_Export.Size = new System.Drawing.Size(75, 23); + this.btn_WebFeed2_Export.TabIndex = 2; + this.btn_WebFeed2_Export.Text = "Export"; + this.btn_WebFeed2_Export.UseVisualStyleBackColor = true; + this.btn_WebFeed2_Export.Click += new System.EventHandler(this.btn_WebFeed2_Export_Click); + // + // btn_WebFeed2_Import + // + this.btn_WebFeed2_Import.Location = new System.Drawing.Point(428, 15); + this.btn_WebFeed2_Import.Name = "btn_WebFeed2_Import"; + this.btn_WebFeed2_Import.Size = new System.Drawing.Size(75, 23); + this.btn_WebFeed2_Import.TabIndex = 1; + this.btn_WebFeed2_Import.Text = "Import"; + this.btn_WebFeed2_Import.UseVisualStyleBackColor = true; + this.btn_WebFeed2_Import.Click += new System.EventHandler(this.btn_WebFeed2_Import_Click); + // + // tp_WebFeed3 + // + this.tp_WebFeed3.BackColor = System.Drawing.SystemColors.Control; + this.tp_WebFeed3.Controls.Add(this.panel5); + this.tp_WebFeed3.Controls.Add(this.panel6); + this.tp_WebFeed3.Location = new System.Drawing.Point(4, 22); + this.tp_WebFeed3.Name = "tp_WebFeed3"; + this.tp_WebFeed3.Padding = new System.Windows.Forms.Padding(3); + this.tp_WebFeed3.Size = new System.Drawing.Size(602, 337); + this.tp_WebFeed3.TabIndex = 9; + this.tp_WebFeed3.Text = "WebFeed 3"; + this.tp_WebFeed3.Enter += new System.EventHandler(this.tp_WebFeed3_Enter); + // + // panel5 + // + this.panel5.Controls.Add(this.pg_WebFeed3); + this.panel5.Dock = System.Windows.Forms.DockStyle.Fill; + this.panel5.Location = new System.Drawing.Point(3, 59); + this.panel5.Name = "panel5"; + this.panel5.Size = new System.Drawing.Size(596, 275); + this.panel5.TabIndex = 4; + // + // pg_WebFeed3 + // + this.pg_WebFeed3.Dock = System.Windows.Forms.DockStyle.Fill; + this.pg_WebFeed3.Location = new System.Drawing.Point(0, 0); + this.pg_WebFeed3.Name = "pg_WebFeed3"; + this.pg_WebFeed3.Size = new System.Drawing.Size(596, 275); + this.pg_WebFeed3.TabIndex = 0; + // + // panel6 + // + this.panel6.Controls.Add(this.cb_WebFeed3); + this.panel6.Controls.Add(this.btn_WebFeed3_Export); + this.panel6.Controls.Add(this.btn_WebFeed3_Import); + this.panel6.Dock = System.Windows.Forms.DockStyle.Top; + this.panel6.Location = new System.Drawing.Point(3, 3); + this.panel6.Name = "panel6"; + this.panel6.Size = new System.Drawing.Size(596, 56); + this.panel6.TabIndex = 3; + // + // cb_WebFeed3 + // + this.cb_WebFeed3.DisplayMember = "Name"; + this.cb_WebFeed3.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cb_WebFeed3.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_WebFeed3.FormattingEnabled = true; + this.cb_WebFeed3.Location = new System.Drawing.Point(22, 16); + this.cb_WebFeed3.Name = "cb_WebFeed3"; + this.cb_WebFeed3.Size = new System.Drawing.Size(367, 24); + this.cb_WebFeed3.TabIndex = 4; + this.cb_WebFeed3.SelectedIndexChanged += new System.EventHandler(this.cb_WebFeed3_SelectedIndexChanged); + // + // btn_WebFeed3_Export + // + this.btn_WebFeed3_Export.Location = new System.Drawing.Point(503, 15); + this.btn_WebFeed3_Export.Name = "btn_WebFeed3_Export"; + this.btn_WebFeed3_Export.Size = new System.Drawing.Size(75, 23); + this.btn_WebFeed3_Export.TabIndex = 2; + this.btn_WebFeed3_Export.Text = "Export"; + this.btn_WebFeed3_Export.UseVisualStyleBackColor = true; + this.btn_WebFeed3_Export.Click += new System.EventHandler(this.btn_WebFeed3_Export_Click); + // + // btn_WebFeed3_Import + // + this.btn_WebFeed3_Import.Location = new System.Drawing.Point(422, 15); + this.btn_WebFeed3_Import.Name = "btn_WebFeed3_Import"; + this.btn_WebFeed3_Import.Size = new System.Drawing.Size(75, 23); + this.btn_WebFeed3_Import.TabIndex = 1; + this.btn_WebFeed3_Import.Text = "Import"; + this.btn_WebFeed3_Import.UseVisualStyleBackColor = true; + this.btn_WebFeed3_Import.Click += new System.EventHandler(this.btn_WebFeed3_Import_Click); + // + // MainDlg + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(634, 452); + this.Controls.Add(this.btn_Close); + this.Controls.Add(this.tc_Main); + this.Controls.Add(this.ss_Main); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.Name = "MainDlg"; + this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; + this.Text = "AirScout Plane Server"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainDlg_FormClosing); + this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.MainDlg_FormClosed); + this.Load += new System.EventHandler(this.MainDlg_Load); + this.Resize += new System.EventHandler(this.MainDlg_Resize); + this.ss_Main.ResumeLayout(false); + this.ss_Main.PerformLayout(); + this.cms_NotifyIcon.ResumeLayout(false); + this.tp_WebFeed1.ResumeLayout(false); + this.panel2.ResumeLayout(false); + this.panel1.ResumeLayout(false); + this.tp_Planes.ResumeLayout(false); + this.gb_CoveredArea.ResumeLayout(false); + this.gb_CoveredArea.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.ud_Planes_Lifetime)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.ud_Planes_MaxAlt)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.ud_Planes_MinAlt)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.ud_MinLat)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.ud_MaxLat)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.ud_MinLon)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.ud_MaxLon)).EndInit(); + this.tp_Server.ResumeLayout(false); + this.gb_Network_Server.ResumeLayout(false); + this.gb_Network_Server.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.ud_Server_Port)).EndInit(); + this.tp_Database.ResumeLayout(false); + this.groupBox2.ResumeLayout(false); + this.groupBox2.PerformLayout(); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.tp_General.ResumeLayout(false); + this.gb_LogWriter.ResumeLayout(false); + this.gb_LogWriter.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.ud_Log_Verbosity)).EndInit(); + this.gb_General_Windows.ResumeLayout(false); + this.gb_General_Windows.PerformLayout(); + this.tc_Main.ResumeLayout(false); + this.tp_WebFeed2.ResumeLayout(false); + this.panel3.ResumeLayout(false); + this.panel4.ResumeLayout(false); + this.tp_WebFeed3.ResumeLayout(false); + this.panel5.ResumeLayout(false); + this.panel6.ResumeLayout(false); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.StatusStrip ss_Main; + private System.Windows.Forms.ToolStripStatusLabel tsl_Main; + private System.Windows.Forms.Button btn_Close; + private System.Windows.Forms.NotifyIcon ni_Main; + private System.Windows.Forms.ContextMenuStrip cms_NotifyIcon; + private System.Windows.Forms.ToolStripMenuItem tsi_Restore; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator1; + private System.Windows.Forms.ToolStripMenuItem tsi_Close; + private System.ComponentModel.BackgroundWorker bw_Webserver; + private System.Windows.Forms.ToolStripStatusLabel tsl_Spring; + private System.Windows.Forms.ToolStripStatusLabel tsl_Uptime; + private System.Windows.Forms.Timer ti_Main; + private AirScout.PlaneFeeds.PlaneFeed_ADSB bw_ADSBFeed; + private System.Windows.Forms.ToolStripStatusLabel tsl_CPULoad; + private System.Windows.Forms.ToolStripStatusLabel tsl_MemoryAvailable; + private System.Windows.Forms.ToolStripStatusLabel tsl_DBSize; + private System.ComponentModel.BackgroundWorker bw_JSONWriter; + private System.ComponentModel.BackgroundWorker bw_DatabaseUpdater; + private System.Windows.Forms.TabPage tp_ADSBFeed; + private System.Windows.Forms.TabPage tp_WebFeed1; + private System.Windows.Forms.Panel panel2; + private System.Windows.Forms.PropertyGrid pg_WebFeed1; + private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.Button btn_WebFeed1_Export; + private System.Windows.Forms.Button btn_WebFeed1_Import; + private System.Windows.Forms.TabPage tp_Planes; + private System.Windows.Forms.GroupBox gb_CoveredArea; + private System.Windows.Forms.NumericUpDown ud_Planes_Lifetime; + private System.Windows.Forms.Label label15; + private System.Windows.Forms.NumericUpDown ud_Planes_MaxAlt; + private System.Windows.Forms.Label label9; + private System.Windows.Forms.NumericUpDown ud_Planes_MinAlt; + private System.Windows.Forms.Label label8; + private GMap.NET.WindowsForms.GMapControl gm_Planes_Coverage; + private System.Windows.Forms.NumericUpDown ud_MinLat; + private System.Windows.Forms.NumericUpDown ud_MaxLat; + private System.Windows.Forms.NumericUpDown ud_MinLon; + private System.Windows.Forms.NumericUpDown ud_MaxLon; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.Label label7; + private System.Windows.Forms.TabPage tp_Server; + private System.Windows.Forms.GroupBox gb_Network_Server; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.NumericUpDown ud_Server_Port; + private System.Windows.Forms.CheckBox cb_Server_Active; + private System.Windows.Forms.TabPage tp_Database; + private System.Windows.Forms.GroupBox groupBox2; + private System.Windows.Forms.Label label13; + private System.Windows.Forms.Label label14; + private System.Windows.Forms.TextBox tb_Database_PlanePositions_RowCount; + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.Label label12; + private System.Windows.Forms.Label label11; + private System.Windows.Forms.TextBox tb_Database_Filesize; + private System.Windows.Forms.Label label10; + private System.Windows.Forms.TextBox tb_Database_Location; + private System.Windows.Forms.TabPage tp_General; + private System.Windows.Forms.GroupBox gb_LogWriter; + private System.Windows.Forms.Button btn_Log_Show; + private System.Windows.Forms.NumericUpDown ud_Log_Verbosity; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Button btn_Log_Directory; + private System.Windows.Forms.CheckBox cb_Log_Active; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TextBox tb_Log_Directory; + private System.Windows.Forms.GroupBox gb_General_Windows; + private System.Windows.Forms.CheckBox cb_Windows_Startup_Autorun; + private System.Windows.Forms.CheckBox cb_Windows_Startup_Systray; + private System.Windows.Forms.CheckBox cb_Windows_Startup_Minimized; + private System.Windows.Forms.TabControl tc_Main; + private System.Windows.Forms.TabPage tp_WebFeed2; + private System.Windows.Forms.Panel panel3; + private System.Windows.Forms.PropertyGrid pg_WebFeed2; + private System.Windows.Forms.Panel panel4; + private System.Windows.Forms.Button btn_WebFeed2_Export; + private System.Windows.Forms.Button btn_WebFeed2_Import; + private System.Windows.Forms.TabPage tp_WebFeed3; + private System.Windows.Forms.Panel panel5; + private System.Windows.Forms.PropertyGrid pg_WebFeed3; + private System.Windows.Forms.Panel panel6; + private System.Windows.Forms.Button btn_WebFeed3_Export; + private System.Windows.Forms.Button btn_WebFeed3_Import; + private System.Windows.Forms.ComboBox cb_WebFeed1; + private System.Windows.Forms.ComboBox cb_WebFeed2; + private System.Windows.Forms.ComboBox cb_WebFeed3; + } +} + diff --git a/AirScoutPlaneServer/MainDlg.cs b/AirScoutPlaneServer/MainDlg.cs new file mode 100644 index 0000000..d4199ec --- /dev/null +++ b/AirScoutPlaneServer/MainDlg.cs @@ -0,0 +1,1054 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using Microsoft.Win32; +using System.Net; +using System.Net.Sockets; +using System.IO; +using System.Threading; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Reflection; +using System.Diagnostics; +using AirScout.PlaneFeeds; +using AirScout.PlaneFeeds.Generic; +using System.Configuration; +using System.Xml; +using System.Xml.Serialization; +using System.Deployment; +using System.Deployment.Application; +using System.Security.Cryptography; +using GMap.NET; +using GMap.NET.MapProviders; +using GMap.NET.WindowsForms; +using GMap.NET.WindowsForms.Markers; +using GMap.NET.WindowsForms.ToolTips; +using AirScout.Database.Flightradar; +using System.Collections; +using AirScout.Database.Core; +using ScoutBase.Core; +using ScoutBase.Data; +using ScoutBase.Elevation; + +namespace AirScoutPlaneServer +{ + public partial class MainDlg : Form + { + string AppName = "AirScout PlaneServer"; + + + // temp variable for local culture + private CultureInfo LocalCulture; + + // private Flightradar FlightRadar; + + private LogWriter Log; + + GMapOverlay gmo_Planes_Coverage = new GMapOverlay("Coveragepolygons"); + + private DateTime StartupTime; + + [CategoryAttribute("Directories")] + [DescriptionAttribute("Application Directory")] + public string AppDirectory + { + get + { + return Application.StartupPath.TrimEnd(Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar); + } + } + + [CategoryAttribute("Directories")] + [DescriptionAttribute("Local Application Data Directory")] + public string AppDataDirectory + { + get + { + return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), Application.CompanyName, Application.ProductName).TrimEnd(Path.DirectorySeparatorChar); + } + } + + [CategoryAttribute("Directories")] + [DescriptionAttribute("Logfile Directory")] + public string LogDirectory + { + get + { + return VC.ReplaceAllVars(Properties.Settings.Default.Log_Directory).TrimEnd(Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar); + } + } + + [CategoryAttribute("Directories")] + [DescriptionAttribute("Tempfile Directory")] + public string TmpDirectory + { + get + { + return VC.ReplaceAllVars(Properties.Settings.Default.Tmp_Directory).TrimEnd(Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar); + } + } + + [CategoryAttribute("Directories")] + [DescriptionAttribute("Database Directory")] + public string DatabaseDirectory + { + get + { + return VC.ReplaceAllVars(Properties.Settings.Default.Database_Directory).TrimEnd(Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar); + } + } + + [CategoryAttribute("Directories")] + [DescriptionAttribute("Icon Directory")] + public string IconDirectory + { + get + { + return VC.ReplaceAllVars(Properties.Settings.Default.Icon_Directory).TrimEnd(Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar); + } + } + + [CategoryAttribute("Directories")] + [DescriptionAttribute("Icon Path")] + public string IconPath + { + get + { + return Path.Combine(IconDirectory, Properties.Settings.Default.Icon_Filename); + } + } + + private ELEVATIONMODEL model; + [CategoryAttribute("Elevation")] + [DescriptionAttribute("Elevation Model")] + public ELEVATIONMODEL Model + { + get + { + return model; + } + set + { + model = value; + } + + } + + VarConverter VC; + + public ArrayList PlaneFeeds; + public PlaneFeed bw_WebFeed1; + public PlaneFeed bw_WebFeed2; + public PlaneFeed bw_WebFeed3; + + public MainDlg() + { + // save current local LocalCulture + LocalCulture = Application.CurrentCulture; + + // force culture invariant language for GUI + Application.CurrentCulture = CultureInfo.InvariantCulture; + + // Initailize variables + VC = new VarConverter(); + VC.AddVar("APPDIR", AppDirectory); + VC.AddVar("DATADIR", AppDataDirectory); + VC.AddVar("LOGDIR", LogDirectory); + VC.AddVar("DATABASEDIR", DatabaseDirectory); + VC.AddVar("MINLAT", Properties.Settings.Default.Planes_MinLat); + VC.AddVar("MAXLAT", Properties.Settings.Default.Planes_MaxLat); + VC.AddVar("MINLON", Properties.Settings.Default.Planes_MinLon); + VC.AddVar("MAXLON", Properties.Settings.Default.Planes_MaxLon); + VC.AddVar("MINALTM", Properties.Settings.Default.Planes_MinAlt); + VC.AddVar("MAXALTM", Properties.Settings.Default.Planes_MaxAlt); + VC.AddVar("MINALTFT", (int)UnitConverter.m_ft((double)Properties.Settings.Default.Planes_MinAlt)); + VC.AddVar("MAXALTFT", (int)UnitConverter.m_ft((double)Properties.Settings.Default.Planes_MaxAlt)); + + // initilaize web feeds selection boxes and background workers + InitializeWebFeeds(); + + InitializeComponent(); + + // uprade settings from previous versions on first run (if not Linux) + if (Properties.Settings.Default.Application_FirstRun && !SupportFunctions.IsMono) + { + Console.WriteLine("First run..."); + Properties.Settings.Default.Upgrade(); + foreach (SettingsPropertyValue p in Properties.Settings.Default.PropertyValues) + { + Console.WriteLine(p.Name + ":" + p.PropertyValue.ToString()); + } + } + + // open database + if (Properties.Settings.Default.WebFeed1_Settings != null) + { + Console.WriteLine("WebFeed1 Settings:" + Properties.Settings.Default.WebFeed1_Settings.URL); + } + if (Properties.Settings.Default.WebFeed2_Settings != null) + { + Console.WriteLine("WebFeed2 Settings:" + Properties.Settings.Default.WebFeed2_Settings.URL); + } + if (Properties.Settings.Default.WebFeed3_Settings != null) + { + Console.WriteLine("WebFeed3 Settings:" + Properties.Settings.Default.WebFeed3_Settings.URL); + } + // load application icon here + Icon = new Icon(IconPath); + // load notifictaion icon here (if not Linux) + if (!SupportFunctions.IsMono) + ni_Main.Icon = new Icon(IconPath); + + // check for all necessary directories + CheckDirectories(); + // implement an Idle procedure +// Application.Idle += new EventHandler(OnIdle); + + // configure LogWriter + ScoutBase.Core.Properties.Settings.Default.LogWriter_Directory = LogDirectory; + ScoutBase.Core.Properties.Settings.Default.LogWriter_FileFormat = Application.ProductName + "_{0:yyyy-MM-dd}.log"; + ScoutBase.Core.Properties.Settings.Default.LogWriter_MessageFormat = "{0:u}: {1}"; + Log = LogWriter.Instance; + Console.WriteLine("Initializing logfile in: " + LogDirectory); + Log.WriteMessage(Application.ProductName + " is starting up.", 0, false); + + Model = ELEVATIONMODEL.GLOBE; + + // initialize database + InitializeDatabase(); + // set initial settings for planes tab + cb_WebFeed1.DisplayMember = "Name"; + cb_WebFeed2.DisplayMember = "Name"; + cb_WebFeed3.DisplayMember = "Name"; + cb_WebFeed1.Items.Clear(); + cb_WebFeed2.Items.Clear(); + cb_WebFeed3.Items.Clear(); + cb_WebFeed1.Items.Add("[none]"); + cb_WebFeed2.Items.Add("[none]"); + cb_WebFeed3.Items.Add("[none]"); + + // get installed plane feeds + ArrayList feeds = new PlaneFeedEnumeration().EnumFeeds(); + foreach (PlaneFeed feed in feeds) + { + cb_WebFeed1.Items.Add(feed); + cb_WebFeed2.Items.Add(feed); + cb_WebFeed3.Items.Add(feed); + } + cb_WebFeed1.SelectedIndex = cb_WebFeed1.FindStringExact(Properties.Settings.Default.Planes_WebFeed1); + cb_WebFeed2.SelectedIndex = cb_WebFeed1.FindStringExact(Properties.Settings.Default.Planes_WebFeed2); + cb_WebFeed3.SelectedIndex = cb_WebFeed1.FindStringExact(Properties.Settings.Default.Planes_WebFeed3); + + double d = ElevationData.Database[51, 10, Model]; + } + + private void InitializeDatabase() + { + Log.WriteMessage("Initialize database: " + AircraftDatabase_old.GetDBLocation()); + } + + private void InitializeWebFeeds() + { + // get available plane feeds + PlaneFeeds = new PlaneFeedEnumeration().EnumFeeds(); + // set plane feed event handler + foreach (PlaneFeed feed in PlaneFeeds) + { + feed.ProgressChanged += new ProgressChangedEventHandler(bw_WebFeed_ProgressChanged); + } + PlaneFeedWorkEventArgs args = new PlaneFeedWorkEventArgs(); + args.AppDirectory = AppDirectory; + args.AppDataDirectory = AppDataDirectory; + args.LogDirectory = LogDirectory; + args.TmpDirectory = TmpDirectory; + args.DatabaseDirectory = DatabaseDirectory; + args.MinLon = (double)Properties.Settings.Default.Planes_MinLon; + args.MaxLon = (double)Properties.Settings.Default.Planes_MaxLon; + args.MinLat = (double)Properties.Settings.Default.Planes_MinLat; + args.MaxLat = (double)Properties.Settings.Default.Planes_MaxLat; + args.MinAlt = (int)Properties.Settings.Default.Planes_MinAlt; + args.MaxAlt = (int)Properties.Settings.Default.Planes_MaxAlt; + // select feeds and start them + foreach (PlaneFeed feed in PlaneFeeds) + { + if (Properties.Settings.Default.Planes_WebFeed1 == feed.Name) + { + bw_WebFeed1 = feed; + bw_WebFeed1.RunWorkerAsync(args); + } + if (Properties.Settings.Default.Planes_WebFeed2 == feed.Name) + { + bw_WebFeed2 = feed; + bw_WebFeed2.RunWorkerAsync(args); + } + if (Properties.Settings.Default.Planes_WebFeed3 == feed.Name) + { + bw_WebFeed3 = feed; + bw_WebFeed3.RunWorkerAsync(args); + } + } + } + + private void btn_Close_Click(object sender, EventArgs e) + { + // close window + this.Close(); + } + + private void MainDlg_Load(object sender, EventArgs e) + { + // start minimized if enabled + if (Properties.Settings.Default.Windows_Startup_Minimized) + { + WindowState = FormWindowState.Minimized; + } + else + { + WindowState = FormWindowState.Normal; + } + // reset FirstRun flag + Properties.Settings.Default.Application_FirstRun = false; + + // set initial settings for CoverageMap + // setting User Agent to fix Open Street Map issue 2016-09-20 + GMap.NET.MapProviders.GMapProvider.UserAgent = "AirScout"; + gm_Planes_Coverage.MapProvider = GMapProviders.Find(Properties.Settings.Default.Planes_MapProvider); + gm_Planes_Coverage.IgnoreMarkerOnMouseWheel = true; + gm_Planes_Coverage.MinZoom = 0; + gm_Planes_Coverage.MaxZoom = 20; + gm_Planes_Coverage.Zoom = 6; + gm_Planes_Coverage.DragButton = System.Windows.Forms.MouseButtons.Left; + gm_Planes_Coverage.CanDragMap = true; + gm_Planes_Coverage.ScalePen = new Pen(Color.Black, 3); + gm_Planes_Coverage.HelperLinePen = null; + gm_Planes_Coverage.SelectionPen = null; + gm_Planes_Coverage.MapScaleInfoEnabled = true; + gm_Planes_Coverage.Overlays.Add(gmo_Planes_Coverage); + + // keep startup time + StartupTime = DateTime.UtcNow; + + // start database + + // enable/disable functions + cb_WebFeed1_Active_CheckedChanged(this, null); + cb_WebFeed2_Active_CheckedChanged(this, null); + cb_WebFeed3_Active_CheckedChanged(this, null); + cb_Log_Active_CheckedChanged(this, null); + cb_Server_Active_CheckedChanged(this, null); + + // start timer + ti_Main.Enabled = true; + ti_Main.Start(); + + // start background workers + bw_JSONWriter.RunWorkerAsync(); + bw_DatabaseUpdater.RunWorkerAsync(); + } + + #region Misc Functions + + private void Say(string s) + { + if ((s != null) && (tsl_Main.Text != s)) + { + tsl_Main.Text = s; + ss_Main.Update(); + } + } + + + #endregion + + #region Directory Functions + + private void CheckDirectories() + { + // check if all direcrtories exist --> create them if not + if (!Directory.Exists(LogDirectory)) + Directory.CreateDirectory(LogDirectory); + if (!Directory.Exists(TmpDirectory)) + Directory.CreateDirectory(TmpDirectory); + if (!Directory.Exists(AppDataDirectory)) + Directory.CreateDirectory(AppDataDirectory); + if (!Directory.Exists(DatabaseDirectory)) + Directory.CreateDirectory(DatabaseDirectory); + if (!Directory.Exists(IconDirectory)) + Directory.CreateDirectory(IconDirectory); + + } + + #endregion + + private void OnIdle(object sender, EventArgs args) + { + } + + private void MainDlg_FormClosing(object sender, FormClosingEventArgs e) + { + // clean up map + gm_Planes_Coverage.Dispose(); + + // save user settings + SaveUserSettings(); + // close database updater + bw_DatabaseUpdater.CancelAsync(); + // close JSON writer + bw_JSONWriter.CancelAsync(); + // close webserver + bw_Webserver.CancelAsync(); + // close all feeds + if (bw_WebFeed1 != null) + bw_WebFeed1.CancelAsync(); + if (bw_WebFeed2 != null) + bw_WebFeed2.CancelAsync(); + if (bw_WebFeed3 != null) + bw_WebFeed3.CancelAsync(); + bw_ADSBFeed.CancelAsync(); + // stop timer + ti_Main.Stop(); + // close database + } + + private void MainDlg_FormClosed(object sender, FormClosedEventArgs e) + { + Log.WriteMessage(Application.ProductName + " is closed.", 0, false); + } + + private void MainDlg_Resize(object sender, EventArgs e) + { + // cut window to normal size when maximized + if (WindowState == FormWindowState.Maximized) + WindowState = FormWindowState.Normal; + // move to systray when enabled (not under Linux) + if (!SupportFunctions.IsMono && (WindowState == FormWindowState.Minimized)) + { + if (Properties.Settings.Default.Windows_Startup_Systray) + { + ni_Main.Visible = true; + ni_Main.BalloonTipText = "AirScout PlaneServer minimized to systray."; + ni_Main.ShowBalloonTip(500); + this.Hide(); + this.ShowInTaskbar = false; + } + } + } + + private void tsi_Restore_Click(object sender, EventArgs e) + { + this.Show(); + WindowState = FormWindowState.Normal; + this.ShowInTaskbar = true; + ni_Main.Visible = false; + } + + private void tsi_Close_Click(object sender, EventArgs e) + { + // close window + this.Close(); + } + + private void cms_NotifyIcon_MouseDoubleClick(object sender, MouseEventArgs e) + { + this.Show(); + WindowState = FormWindowState.Normal; + this.ShowInTaskbar = true; + ni_Main.Visible = false; + } + + private void cms_NotifyIcon_Opening(object sender, CancelEventArgs e) + { + tsi_Restore.Select(); + } + + private void cb_Windows_Startup_Autorun_CheckedChanged(object sender, EventArgs e) + { + // try to manage an autorun on Windows startup + // this does not work with Linux + // this may not work with Windows 8 or later versions + try + { + // The path to the key where Windows looks for startup applications + RegistryKey rkApp = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true); + if (cb_Windows_Startup_Autorun.Checked) + { + // Add the value in the registry so that the application runs at startup + rkApp.SetValue(AppName, Application.ExecutablePath.ToString()); + } + else + { + // Remove the value from the registry so that the application doesn't start + rkApp.DeleteValue(AppName, false); + } + } + catch (Exception ex) + { + MessageBox.Show(ex.Message, "Error while accessing registry key"); + } + } + + private void SaveUserSettings() + { + Log.WriteMessage("Saving configuration..."); + Properties.Settings.Default.Save(); + Console.WriteLine("Creating XML document..."); + XmlDocument doc = new XmlDocument(); + XmlDeclaration xmlDeclaration = doc.CreateXmlDeclaration("1.0", "UTF-8", null); + XmlElement root = doc.DocumentElement; + doc.InsertBefore(xmlDeclaration, root); + XmlElement configuration = doc.CreateElement(string.Empty, "configuration", string.Empty); + doc.AppendChild(configuration); + XmlElement configsections = doc.CreateElement(string.Empty, "configSections", string.Empty); + configuration.AppendChild(configsections); + XmlElement usersettingsgroup = doc.CreateElement(string.Empty, "sectionGroup", string.Empty); + XmlAttribute usersettingsname = doc.CreateAttribute(string.Empty, "name", string.Empty); + usersettingsname.InnerText = "userSettings"; + usersettingsgroup.Attributes.Append(usersettingsname); + XmlElement usersection = doc.CreateElement(string.Empty, "section", string.Empty); + XmlAttribute sectionname = doc.CreateAttribute(string.Empty, "name", string.Empty); + sectionname.InnerText = "AirScout.PlaneFeeds.Properties.Settings"; + usersection.Attributes.Append(sectionname); + XmlAttribute sectiontype = doc.CreateAttribute(string.Empty, "type", string.Empty); + sectiontype.InnerText = "System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"; + usersection.Attributes.Append(sectiontype); + XmlAttribute sectionallowexedefinition = doc.CreateAttribute(string.Empty, "allowExeDefinition", string.Empty); + sectionallowexedefinition.InnerText = "MachineToLocalUser"; + usersection.Attributes.Append(sectionallowexedefinition); + XmlAttribute sectionrequirepermission = doc.CreateAttribute(string.Empty, "requirePermission", string.Empty); + sectionrequirepermission.InnerText = "false"; + usersection.Attributes.Append(sectionrequirepermission); + usersettingsgroup.AppendChild(usersection); + configsections.AppendChild(usersettingsgroup); + XmlElement usersettings = doc.CreateElement(string.Empty, "userSettings", string.Empty); + configuration.AppendChild(usersettings); + XmlElement properties = doc.CreateElement(string.Empty, Properties.Settings.Default.ToString(), string.Empty); + usersettings.AppendChild(properties); + Console.WriteLine("Writing Properties.Settings..."); + foreach (SettingsPropertyValue p in Properties.Settings.Default.PropertyValues) + { + Console.WriteLine(string.Concat(new object[] + { + "Writing ", + p.Name, + " = ", + p.PropertyValue + })); + XmlElement setting = doc.CreateElement(string.Empty, "setting", string.Empty); + XmlAttribute name = doc.CreateAttribute(string.Empty, "name", string.Empty); + name.InnerText = p.Name.ToString(); + setting.Attributes.Append(name); + XmlAttribute serializeas = doc.CreateAttribute(string.Empty, "serializeAs", string.Empty); + serializeas.InnerText = p.Property.SerializeAs.ToString(); + setting.Attributes.Append(serializeas); + XmlElement value = doc.CreateElement(string.Empty, "value", string.Empty); + if (p.PropertyValue != null && p.Property.SerializeAs == SettingsSerializeAs.String) + { + XmlText text = doc.CreateTextNode(p.PropertyValue.ToString()); + value.AppendChild(text); + } + else + { + if (p.PropertyValue != null && p.Property.SerializeAs == SettingsSerializeAs.Xml) + { + MemoryStream ms = new MemoryStream(); + XmlWriter writer = XmlWriter.Create(ms, new XmlWriterSettings + { + NewLineOnAttributes = true, + OmitXmlDeclaration = true + }); + XmlSerializer serializer = new XmlSerializer(p.PropertyValue.GetType()); + serializer.Serialize(writer, p.PropertyValue); + byte[] text2 = new byte[ms.ToArray().Length - 3]; + Array.Copy(ms.ToArray(), 3, text2, 0, text2.Length); + XmlText xml = doc.CreateTextNode(Encoding.UTF8.GetString(text2.ToArray())); + value.AppendChild(xml); + value.InnerXml = WebUtility.HtmlDecode(value.InnerXml); + } + } + setting.AppendChild(value); + properties.AppendChild(setting); + } + string usersettingspath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + usersettingspath = Path.Combine(usersettingspath, Application.CompanyName, AppDomain.CurrentDomain.FriendlyName); + usersettingspath += "_Url_"; + Assembly assembly = Assembly.GetEntryAssembly(); + if (assembly == null) + { + assembly = Assembly.GetCallingAssembly(); + } + byte[] pkt = assembly.GetName().GetPublicKeyToken(); + byte[] hash = SHA1.Create().ComputeHash((pkt != null && pkt.Length > 0) ? pkt : Encoding.UTF8.GetBytes(assembly.EscapedCodeBase)); + StringBuilder evidence_string = new StringBuilder(); + byte[] array = hash; + for (int i = 0; i < array.Length; i++) + { + byte b = array[i]; + evidence_string.AppendFormat("{0:x2}", b); + } + usersettingspath += evidence_string.ToString(); + if (!Directory.Exists(usersettingspath)) + { + Directory.CreateDirectory(usersettingspath); + } + usersettingspath = Path.Combine(usersettingspath, "user.config"); + doc.Save(usersettingspath); + } + + + # region User Interface + + private void btn_Log_Directory_Click(object sender, EventArgs e) + { + FolderBrowserDialog Dlg = new FolderBrowserDialog(); + Dlg.SelectedPath = LogDirectory; + Dlg.ShowNewFolderButton = true; + Dlg.Description = "Select Logfile Directory"; + if (Dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) + { + tb_Log_Directory.Text = Dlg.SelectedPath; + CheckDirectories(); + } + } + + private void cb_Log_Active_CheckedChanged(object sender, EventArgs e) + { + if (cb_Log_Active.Checked) + { + // disable directory change + tb_Log_Directory.Enabled = false; + btn_Log_Directory.Enabled = false; + // set logfile path if path exists + if (Directory.Exists(LogDirectory)) + ScoutBase.Core.Properties.Settings.Default.LogWriter_Directory = LogDirectory; + } + else + { + // enable directory change + tb_Log_Directory.Enabled = true; + btn_Log_Directory.Enabled = true; + } + } + + private void btn_Log_Show_Click(object sender, EventArgs e) + { + OpenFileDialog Dlg = new OpenFileDialog(); + Dlg.DefaultExt = ".log"; + Dlg.Filter = "Log Files | *.log"; + Dlg.InitialDirectory = LogDirectory; + Dlg.CheckFileExists = true; + if (Dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK) + { + Process.Start(Dlg.FileName); + } + } + + private void ti_Main_Tick(object sender, EventArgs e) + { + // check and update status + TimeSpan uptime = DateTime.UtcNow - StartupTime; + tsl_Uptime.Text = uptime.ToString(@"d\.hh\:mm\:ss"); + // get performance counter values and avoid exceptions + try + { + tsl_CPULoad.Text = "cpu: " + SupportFunctions.CPUCounter.GetLoad().ToString("F0") + " %"; + tsl_MemoryAvailable.Text = "free: " + SupportFunctions.MemoryCounter.GetAvailable().ToString("F0") + " MB"; + } + catch (Exception ex) + { + tsl_CPULoad.Text = "cpu: " + "***"; + tsl_MemoryAvailable.Text = "free: " + "***"; + } + tsl_DBSize.Text = "DB: " + AircraftDatabase_old.GetDBSize().ToString("F0") + " MB"; + } + + private void Planes_AdjustMap() + { + gmo_Planes_Coverage.Clear(); + // add tile to map polygons + List l = new List(); + l.Add(new PointLatLng(Properties.Settings.Default.Planes_MinLat, Properties.Settings.Default.Planes_MinLon)); + l.Add(new PointLatLng(Properties.Settings.Default.Planes_MinLat, Properties.Settings.Default.Planes_MaxLon)); + l.Add(new PointLatLng(Properties.Settings.Default.Planes_MaxLat, Properties.Settings.Default.Planes_MaxLon)); + l.Add(new PointLatLng(Properties.Settings.Default.Planes_MaxLat, Properties.Settings.Default.Planes_MinLon)); + GMapPolygon p = new GMapPolygon(l, "Coverage"); + p.Stroke = new Pen(Color.FromArgb(255, Color.Magenta), 3); + p.Fill = new SolidBrush(Color.FromArgb(0, Color.Magenta)); + gmo_Planes_Coverage.Polygons.Add(p); + // zoom the map + gm_Planes_Coverage.SetZoomToFitRect(RectLatLng.FromLTRB((double)Properties.Settings.Default.Planes_MinLon - 1, (double)Properties.Settings.Default.Planes_MaxLat + 1, (double)Properties.Settings.Default.Planes_MaxLon + 1, (double)Properties.Settings.Default.Planes_MinLat - 1)); + } + + private void tab_Planes_Enter(object sender, EventArgs e) + { + Planes_AdjustMap(); + } + + private void ud_Planes_LatLon_ValueChanged(object sender, EventArgs e) + { + Planes_AdjustMap(); + } + + private void tp_WebFeed1_Enter(object sender, EventArgs e) + { + cb_WebFeed1.SelectedIndex = cb_WebFeed1.FindStringExact(Properties.Settings.Default.Planes_WebFeed1); + } + + private void tp_WebFeed2_Enter(object sender, EventArgs e) + { + cb_WebFeed2.SelectedIndex = cb_WebFeed1.FindStringExact(Properties.Settings.Default.Planes_WebFeed2); + } + + private void tp_WebFeed3_Enter(object sender, EventArgs e) + { + cb_WebFeed3.SelectedIndex = cb_WebFeed1.FindStringExact(Properties.Settings.Default.Planes_WebFeed3); + } + + private void cb_WebFeed1_Active_CheckedChanged(object sender, EventArgs e) + { + /* + if (cb_WebFeed1_Active.Checked) + { + if (!bw_WebFeed1.IsBusy) + { + if (Properties.Settings.Default.WebFeed1_Settings != null) + { + PlaneFeedWorkEventArgs args = new PlaneFeedWorkEventArgs(); + args.AppDirectory = AppDirectory; + args.AppDataDirectory = AppDataDirectory; + args.LogDirectory = LogDirectory; + args.TmpDirectory = TmpDirectory; + args.DatabaseDirectory = DatabaseDirectory; + args.MinLon = (double)Properties.Settings.Default.Planes_MinLon; + args.MaxLon = (double)Properties.Settings.Default.Planes_MaxLon; + args.MinLat = (double)Properties.Settings.Default.Planes_MinLat; + args.MaxLat = (double)Properties.Settings.Default.Planes_MaxLat; + args.MinAlt = (int)Properties.Settings.Default.Planes_MinAlt; + args.MaxAlt = (int)Properties.Settings.Default.Planes_MaxAlt; + bw_WebFeed1.RunWorkerAsync(args); + } + } + } + else + { + if (bw_WebFeed1.IsBusy) + bw_WebFeed1.CancelAsync(); + } + // enable/disable property grid + pg_WebFeed1.Enabled = !cb_WebFeed1_Active.Checked; + */ + } + + private void cb_WebFeed2_Active_CheckedChanged(object sender, EventArgs e) + { + /* + if (cb_WebFeed2_Active.Checked) + { + if (!bw_WebFeed2.IsBusy) + { + if (Properties.Settings.Default.WebFeed2_Settings != null) + { + PlaneFeedWorkEventArgs args = new PlaneFeedWorkEventArgs(); + args.AppDirectory = AppDirectory; + args.AppDataDirectory = AppDataDirectory; + args.LogDirectory = LogDirectory; + args.TmpDirectory = TmpDirectory; + args.DatabaseDirectory = DatabaseDirectory; + args.MinLon = (double)Properties.Settings.Default.Planes_MinLon; + args.MaxLon = (double)Properties.Settings.Default.Planes_MaxLon; + args.MinLat = (double)Properties.Settings.Default.Planes_MinLat; + args.MaxLat = (double)Properties.Settings.Default.Planes_MaxLat; + args.MinAlt = (int)Properties.Settings.Default.Planes_MinAlt; + args.MaxAlt = (int)Properties.Settings.Default.Planes_MaxAlt; + bw_WebFeed2.RunWorkerAsync(args); + } + } + } + else + { + if (bw_WebFeed2.IsBusy) + bw_WebFeed2.CancelAsync(); + } + // enable/disable property grid + pg_WebFeed2.Enabled = !cb_WebFeed2_Active.Checked; + */ + } + + private void cb_WebFeed3_Active_CheckedChanged(object sender, EventArgs e) + { + /* + if (cb_WebFeed3_Active.Checked) + { + if (!bw_WebFeed3.IsBusy) + { + if (Properties.Settings.Default.WebFeed3_Settings != null) + { + PlaneFeedWorkEventArgs args = new PlaneFeedWorkEventArgs(); + args.AppDirectory = AppDirectory; + args.AppDataDirectory = AppDataDirectory; + args.LogDirectory = LogDirectory; + args.TmpDirectory = TmpDirectory; + args.DatabaseDirectory = DatabaseDirectory; + args.MinLon = (double)Properties.Settings.Default.Planes_MinLon; + args.MaxLon = (double)Properties.Settings.Default.Planes_MaxLon; + args.MinLat = (double)Properties.Settings.Default.Planes_MinLat; + args.MaxLat = (double)Properties.Settings.Default.Planes_MaxLat; + args.MinAlt = (int)Properties.Settings.Default.Planes_MinAlt; + args.MaxAlt = (int)Properties.Settings.Default.Planes_MaxAlt; + bw_WebFeed3.RunWorkerAsync(args); + } + } + } + else + { + if (bw_WebFeed3.IsBusy) + bw_WebFeed3.CancelAsync(); + } + // enable/disable property grid + pg_WebFeed3.Enabled = !cb_WebFeed3_Active.Checked; + */ + } + + private void cb_WebFeed1_SelectedIndexChanged(object sender, EventArgs e) + { + if ((cb_WebFeed1.SelectedItem == null) || (cb_WebFeed1.GetItemText(cb_WebFeed1.SelectedItem) == "[none]")) + { + pg_WebFeed1.Enabled = false; + btn_WebFeed1_Import.Enabled = false; + btn_WebFeed1_Export.Enabled = false; + bw_WebFeed1 = null; + Properties.Settings.Default.Planes_WebFeed1 = "[none]"; + return; + } + // get the selected type of feed + PlaneFeed feed = (PlaneFeed)cb_WebFeed1.SelectedItem; + // check for changes + if ((bw_WebFeed1 == null) || bw_WebFeed1.Name != feed.Name) + { + // stop background worker + if (bw_WebFeed1 != null) + { + bw_WebFeed1.CancelAsync(); + while (bw_WebFeed1.IsBusy) + Application.DoEvents(); + } + // select feed + foreach (PlaneFeed newfeed in PlaneFeeds) + { + if (feed.Name == newfeed.Name) + bw_WebFeed1 = newfeed; + } + PlaneFeedWorkEventArgs args = new PlaneFeedWorkEventArgs(); + args.AppDirectory = AppDirectory; + args.AppDataDirectory = AppDataDirectory; + args.LogDirectory = LogDirectory; + args.TmpDirectory = TmpDirectory; + args.DatabaseDirectory = DatabaseDirectory; + args.MinLon = (double)Properties.Settings.Default.Planes_MinLon; + args.MaxLon = (double)Properties.Settings.Default.Planes_MaxLon; + args.MinLat = (double)Properties.Settings.Default.Planes_MinLat; + args.MaxLat = (double)Properties.Settings.Default.Planes_MaxLat; + args.MinAlt = (int)Properties.Settings.Default.Planes_MinAlt; + args.MaxAlt = (int)Properties.Settings.Default.Planes_MaxAlt; + bw_WebFeed1.RunWorkerAsync(args); + } + pg_WebFeed1.SelectedObject = bw_WebFeed1.GetFeedSettings(); + pg_WebFeed1.Enabled = feed.HasSettings; + btn_WebFeed1_Import.Enabled = feed.CanImport; + btn_WebFeed1_Export.Enabled = feed.CanExport; + Properties.Settings.Default.Planes_WebFeed1 = feed.Name; + } + + private void cb_WebFeed2_SelectedIndexChanged(object sender, EventArgs e) + { + if ((cb_WebFeed2.SelectedItem == null) || (cb_WebFeed2.GetItemText(cb_WebFeed2.SelectedItem) == "[none]")) + { + pg_WebFeed2.Enabled = false; + btn_WebFeed2_Import.Enabled = false; + btn_WebFeed2_Export.Enabled = false; + bw_WebFeed2 = null; + Properties.Settings.Default.Planes_WebFeed2 = "[none]"; + return; + } + // get the selected type of feed + PlaneFeed feed = (PlaneFeed)cb_WebFeed2.SelectedItem; + // check for changes + if ((bw_WebFeed2 == null) || bw_WebFeed2.Name != feed.Name) + { + // stop background worker + if (bw_WebFeed2 != null) + { + bw_WebFeed2.CancelAsync(); + while (bw_WebFeed2.IsBusy) + Application.DoEvents(); + } + // select feed + foreach (PlaneFeed newfeed in PlaneFeeds) + { + if (feed.Name == newfeed.Name) + bw_WebFeed2 = newfeed; + } + PlaneFeedWorkEventArgs args = new PlaneFeedWorkEventArgs(); + args.AppDirectory = AppDirectory; + args.AppDataDirectory = AppDataDirectory; + args.LogDirectory = LogDirectory; + args.TmpDirectory = TmpDirectory; + args.DatabaseDirectory = DatabaseDirectory; + args.MinLon = (double)Properties.Settings.Default.Planes_MinLon; + args.MaxLon = (double)Properties.Settings.Default.Planes_MaxLon; + args.MinLat = (double)Properties.Settings.Default.Planes_MinLat; + args.MaxLat = (double)Properties.Settings.Default.Planes_MaxLat; + args.MinAlt = (int)Properties.Settings.Default.Planes_MinAlt; + args.MaxAlt = (int)Properties.Settings.Default.Planes_MaxAlt; + bw_WebFeed2.RunWorkerAsync(args); + } + pg_WebFeed2.SelectedObject = bw_WebFeed2.GetFeedSettings(); + pg_WebFeed2.Enabled = feed.HasSettings; + btn_WebFeed2_Import.Enabled = feed.CanImport; + btn_WebFeed2_Export.Enabled = feed.CanExport; + Properties.Settings.Default.Planes_WebFeed2 = feed.Name; + } + + private void cb_WebFeed3_SelectedIndexChanged(object sender, EventArgs e) + { + if ((cb_WebFeed3.SelectedItem == null) || (cb_WebFeed3.GetItemText(cb_WebFeed3.SelectedItem) == "[none]")) + { + pg_WebFeed3.Enabled = false; + btn_WebFeed3_Import.Enabled = false; + btn_WebFeed3_Export.Enabled = false; + bw_WebFeed3 = null; + Properties.Settings.Default.Planes_WebFeed3 = "[none]"; + return; + } + // get the selected type of feed + PlaneFeed feed = (PlaneFeed)cb_WebFeed3.SelectedItem; + // check for changes + if ((bw_WebFeed3 == null) || bw_WebFeed3.Name != feed.Name) + { + // stop background worker + if (bw_WebFeed3 != null) + { + bw_WebFeed3.CancelAsync(); + while (bw_WebFeed3.IsBusy) + Application.DoEvents(); + } + // select feed + foreach (PlaneFeed newfeed in PlaneFeeds) + { + if (feed.Name == newfeed.Name) + bw_WebFeed3 = newfeed; + } + PlaneFeedWorkEventArgs args = new PlaneFeedWorkEventArgs(); + args.AppDirectory = AppDirectory; + args.AppDataDirectory = AppDataDirectory; + args.LogDirectory = LogDirectory; + args.TmpDirectory = TmpDirectory; + args.DatabaseDirectory = DatabaseDirectory; + args.MinLon = (double)Properties.Settings.Default.Planes_MinLon; + args.MaxLon = (double)Properties.Settings.Default.Planes_MaxLon; + args.MinLat = (double)Properties.Settings.Default.Planes_MinLat; + args.MaxLat = (double)Properties.Settings.Default.Planes_MaxLat; + args.MinAlt = (int)Properties.Settings.Default.Planes_MinAlt; + args.MaxAlt = (int)Properties.Settings.Default.Planes_MaxAlt; + bw_WebFeed3.RunWorkerAsync(args); + } + pg_WebFeed3.SelectedObject = bw_WebFeed3.GetFeedSettings(); + pg_WebFeed3.Enabled = feed.HasSettings; + btn_WebFeed3_Import.Enabled = feed.CanImport; + btn_WebFeed3_Export.Enabled = feed.CanExport; + Properties.Settings.Default.Planes_WebFeed3 = feed.Name; + } + + private void btn_WebFeed1_Import_Click(object sender, EventArgs e) + { + bw_WebFeed1.Import(); + } + + private void btn_WebFeed2_Import_Click(object sender, EventArgs e) + { + bw_WebFeed2.Import(); + } + + private void btn_WebFeed3_Import_Click(object sender, EventArgs e) + { + bw_WebFeed3.Import(); + } + + private void btn_WebFeed1_Export_Click(object sender, EventArgs e) + { + bw_WebFeed1.Export(); + } + + private void btn_WebFeed2_Export_Click(object sender, EventArgs e) + { + bw_WebFeed2.Export(); + } + + private void btn_WebFeed3_Export_Click(object sender, EventArgs e) + { + bw_WebFeed3.Export(); + } + + private void bw_WebFeed_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + if (e.ProgressPercentage == (int)PROGRESS.STATUS) + { + if (e.UserState == null) + return; + // NASTY: shorten the message text if too long + string s = (string)e.UserState; + int maxlen = 50; + if (s.Length > maxlen) + { + try + { + // cut the string intelligent + s = s.Remove(s.IndexOf("from"), s.IndexOf(",")-s.IndexOf("from")); + } + catch + { + // simple cut the string + s = s.Substring(0, maxlen); + } + } + this.Say(s); + } + else if (e.ProgressPercentage == (int)PROGRESS.FINISHED) + { + + } + } + + private void tp_Database_Enter(object sender, EventArgs e) + { + // udpate database status + tb_Database_Location.Text = AircraftDatabase_old.GetDBLocation(); + tb_Database_Filesize.Text = AircraftDatabase_old.GetDBSize().ToString("F0"); + tb_Database_PlanePositions_RowCount.Text = AircraftDatabase_old.GetAircraftPositionsCount().ToString(); + } + + private void cb_Server_Active_CheckedChanged(object sender, EventArgs e) + { + if (this.cb_Server_Active.Checked) + { + if (!this.bw_Webserver.IsBusy) + { + this.bw_Webserver.RunWorkerAsync(); + } + } + else + { + this.bw_Webserver.CancelAsync(); + } + } + + + + #endregion + + } +} diff --git a/AirScoutPlaneServer/MainDlg.resx b/AirScoutPlaneServer/MainDlg.resx new file mode 100644 index 0000000..6e611e9 --- /dev/null +++ b/AirScoutPlaneServer/MainDlg.resx @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 111, 17 + + + 205, 17 + + + 340, 17 + + + 471, 17 + + + 955, 17 + + + 185, 56 + + + 17, 56 + + \ No newline at end of file diff --git a/AirScoutPlaneServer/PlaneServer.ico b/AirScoutPlaneServer/PlaneServer.ico new file mode 100644 index 0000000..6945851 Binary files /dev/null and b/AirScoutPlaneServer/PlaneServer.ico differ diff --git a/AirScoutPlaneServer/Program.cs b/AirScoutPlaneServer/Program.cs new file mode 100644 index 0000000..a327c9c --- /dev/null +++ b/AirScoutPlaneServer/Program.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; + +namespace AirScoutPlaneServer +{ + static class Program + { + /// + /// Der Haupteinstiegspunkt für die Anwendung. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new MainDlg()); + } + } +} diff --git a/AirScoutPlaneServer/Properties/AssemblyInfo.cs b/AirScoutPlaneServer/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..f4011e4 --- /dev/null +++ b/AirScoutPlaneServer/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Allgemeine Informationen über eine Assembly werden über die folgenden +// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, +// die mit einer Assembly verknüpft sind. +[assembly: AssemblyTitle("AirScoutPlaneServer")] +[assembly: AssemblyDescription("AirScout Plane Server Application")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("DL2ALF")] +[assembly: AssemblyProduct("AirScoutPlaneServer")] +[assembly: AssemblyCopyright("Copyright © 2016 DL2ALF")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar +// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von +// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest. +[assembly: ComVisible(false)] + +// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird +[assembly: Guid("d1982513-59ec-4eae-a080-5cf12af09e3c")] + +// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: +// +// Hauptversion +// Nebenversion +// Buildnummer +// Revision +// +// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern +// übernehmen, indem Sie "*" eingeben: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("0.9.0.0")] +[assembly: AssemblyFileVersion("0.9.0.0")] diff --git a/AirScoutPlaneServer/Properties/Resources.Designer.cs b/AirScoutPlaneServer/Properties/Resources.Designer.cs new file mode 100644 index 0000000..5f97390 --- /dev/null +++ b/AirScoutPlaneServer/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.34209 +// +// Änderungen an dieser Datei können fehlerhaftes Verhalten verursachen und gehen verloren, wenn +// der Code neu generiert wird. +// +//------------------------------------------------------------------------------ + +namespace AirScoutPlaneServer.Properties +{ + + + /// + /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw. + /// + // Diese Klasse wurde von der StronglyTypedResourceBuilder-Klasse + // über ein Tool wie ResGen oder Visual Studio automatisch generiert. + // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen + // mit der Option /str erneut aus, oder erstellen Sie Ihr VS-Projekt neu. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AirScoutPlaneServer.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle + /// Ressourcenlookups, die diese stark typisierte Ressourcenklasse verwenden. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/AirScoutPlaneServer/Properties/Resources.resx b/AirScoutPlaneServer/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/AirScoutPlaneServer/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/AirScoutPlaneServer/Properties/Settings.Designer.cs b/AirScoutPlaneServer/Properties/Settings.Designer.cs new file mode 100644 index 0000000..3bb252e --- /dev/null +++ b/AirScoutPlaneServer/Properties/Settings.Designer.cs @@ -0,0 +1,407 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.42000 +// +// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn +// der Code erneut generiert wird. +// +//------------------------------------------------------------------------------ + +namespace AirScoutPlaneServer.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Windows_Startup_Minimized { + get { + return ((bool)(this["Windows_Startup_Minimized"])); + } + set { + this["Windows_Startup_Minimized"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Windows_Startup_Systray { + get { + return ((bool)(this["Windows_Startup_Systray"])); + } + set { + this["Windows_Startup_Systray"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Windows_Startup_Autorun { + get { + return ((bool)(this["Windows_Startup_Autorun"])); + } + set { + this["Windows_Startup_Autorun"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Webserver_Active { + get { + return ((bool)(this["Webserver_Active"])); + } + set { + this["Webserver_Active"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("9872")] + public decimal Webserver_Port { + get { + return ((decimal)(this["Webserver_Port"])); + } + set { + this["Webserver_Port"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute(" Welcome to AirScout PlaneServer")] + public string Webserver_Welcome { + get { + return ((string)(this["Webserver_Welcome"])); + } + set { + this["Webserver_Welcome"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Log_Active { + get { + return ((bool)(this["Log_Active"])); + } + set { + this["Log_Active"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("%DataDir%\\Log")] + public string Log_Directory { + get { + return ((string)(this["Log_Directory"])); + } + set { + this["Log_Directory"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("%DataDir%\\Database")] + public string Database_Directory { + get { + return ((string)(this["Database_Directory"])); + } + set { + this["Database_Directory"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Application_FirstRun { + get { + return ((bool)(this["Application_FirstRun"])); + } + set { + this["Application_FirstRun"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("%AppDir%")] + public string Icon_Directory { + get { + return ((string)(this["Icon_Directory"])); + } + set { + this["Icon_Directory"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("PlaneServer.ico")] + public string Icon_Filename { + get { + return ((string)(this["Icon_Filename"])); + } + set { + this["Icon_Filename"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("5")] + public decimal Log_Verbosity { + get { + return ((decimal)(this["Log_Verbosity"])); + } + set { + this["Log_Verbosity"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-15")] + public decimal Planes_MinLon { + get { + return ((decimal)(this["Planes_MinLon"])); + } + set { + this["Planes_MinLon"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("30")] + public decimal Planes_MaxLon { + get { + return ((decimal)(this["Planes_MaxLon"])); + } + set { + this["Planes_MaxLon"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("35")] + public decimal Planes_MinLat { + get { + return ((decimal)(this["Planes_MinLat"])); + } + set { + this["Planes_MinLat"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("60")] + public decimal Planes_MaxLat { + get { + return ((decimal)(this["Planes_MaxLat"])); + } + set { + this["Planes_MaxLat"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("OpenStreetMap")] + public string Planes_MapProvider { + get { + return ((string)(this["Planes_MapProvider"])); + } + set { + this["Planes_MapProvider"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("5000")] + public decimal Planes_MinAlt { + get { + return ((decimal)(this["Planes_MinAlt"])); + } + set { + this["Planes_MinAlt"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool ADSBFeed_Active { + get { + return ((bool)(this["ADSBFeed_Active"])); + } + set { + this["ADSBFeed_Active"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public global::AirScout.PlaneFeeds.PlanefeedSettings_WF WebFeed1_Settings { + get { + return ((global::AirScout.PlaneFeeds.PlanefeedSettings_WF)(this["WebFeed1_Settings"])); + } + set { + this["WebFeed1_Settings"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("20000")] + public decimal Planes_MaxAlt { + get { + return ((decimal)(this["Planes_MaxAlt"])); + } + set { + this["Planes_MaxAlt"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("5")] + public decimal Planes_Lifetime { + get { + return ((decimal)(this["Planes_Lifetime"])); + } + set { + this["Planes_Lifetime"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public decimal Setting { + get { + return ((decimal)(this["Setting"])); + } + set { + this["Setting"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("%DataDir%\\Tmp")] + public string Tmp_Directory { + get { + return ((string)(this["Tmp_Directory"])); + } + set { + this["Tmp_Directory"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("http://www.airscout.de/UpdateURL.txt")] + public string Database_UpdateURL { + get { + return ((string)(this["Database_UpdateURL"])); + } + set { + this["Database_UpdateURL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1")] + public decimal Database_UpdateCycle { + get { + return ((decimal)(this["Database_UpdateCycle"])); + } + set { + this["Database_UpdateCycle"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public global::AirScout.PlaneFeeds.PlanefeedSettings_WF WebFeed2_Settings { + get { + return ((global::AirScout.PlaneFeeds.PlanefeedSettings_WF)(this["WebFeed2_Settings"])); + } + set { + this["WebFeed2_Settings"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public global::AirScout.PlaneFeeds.PlanefeedSettings_WF WebFeed3_Settings { + get { + return ((global::AirScout.PlaneFeeds.PlanefeedSettings_WF)(this["WebFeed3_Settings"])); + } + set { + this["WebFeed3_Settings"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("[none]")] + public string Planes_WebFeed1 { + get { + return ((string)(this["Planes_WebFeed1"])); + } + set { + this["Planes_WebFeed1"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("[none]")] + public string Planes_WebFeed2 { + get { + return ((string)(this["Planes_WebFeed2"])); + } + set { + this["Planes_WebFeed2"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("[none]")] + public string Planes_WebFeed3 { + get { + return ((string)(this["Planes_WebFeed3"])); + } + set { + this["Planes_WebFeed3"] = value; + } + } + } +} diff --git a/AirScoutPlaneServer/Properties/Settings.settings b/AirScoutPlaneServer/Properties/Settings.settings new file mode 100644 index 0000000..16e6b5a --- /dev/null +++ b/AirScoutPlaneServer/Properties/Settings.settings @@ -0,0 +1,102 @@ + + + + + + False + + + False + + + False + + + True + + + 9872 + + + <HTML><BODY> Welcome to AirScout PlaneServer</BODY></HTML> + + + True + + + %DataDir%\Log + + + %DataDir%\Database + + + True + + + %AppDir% + + + PlaneServer.ico + + + 5 + + + -15 + + + 30 + + + 35 + + + 60 + + + OpenStreetMap + + + 5000 + + + False + + + + + + 20000 + + + 5 + + + 0 + + + %DataDir%\Tmp + + + http://www.airscout.de/UpdateURL.txt + + + 1 + + + + + + + + + [none] + + + [none] + + + [none] + + + \ No newline at end of file diff --git a/AirScoutPlaneServer/Readme.txt b/AirScoutPlaneServer/Readme.txt new file mode 100644 index 0000000..c3eecef --- /dev/null +++ b/AirScoutPlaneServer/Readme.txt @@ -0,0 +1,46 @@ +************************************************************************************** +Linux compatibility +Last modified: 2018-02-09 +************************************************************************************** + +This software is running on Linux with the help of the Mono runtime environment without any modifications. +Some precautions are necessary depending on the your Linux system. +I cannot verify all existing variants but see some samples below. +After you have finished all preparations: + + - simple copy all the files in a directory of your choice + - open a terminal window there + - run AirScout Plane Server by typing "mono AirScoutPlaneServer.exe" + +VERY IMPORTANT TO IMPORT SSL CERTIFICATES TO THE MONO CERTIFICATE STORE MANUALLY!!! +This guarantees the download from websites with SSL encryption (https://). +Mono is using its own certificate store different from system or Mozilla's web browser. +The synchronisation should be done automatically but I found it not working after downloading the Mono package. + +************************************************************************************** +Installation samples +************************************************************************************** + + +*** Suse Linux 13.2 KDE 4 (64bit) *** + +1. Install Mono 4.x complete package "mono-complete.ymp" +2. Install the "ca-certificates-mozilla" package to get SSL certificates +3. Import the certificates into the Mono certificate store by typing "sudo cert-sync /etc/ssl/ca-bundle.pem" (can be a different location on other versions!) + + +*** Raspberry Pi V1 with jessie *** + +The latest Mono package in the Raspian repository is V3.2.8. You will need to upgrade manually to V4.xxx. +1. Open a Terminal window. +2. Type this in or paste: + + sudo su - + echo "deb http://plugwash.raspbian.org/mono4 jessie-mono4 main" >> /etc/apt/sources.list + apt-get update + +3. Please follow the instructions on https://www.raspberrypi.org/forums/viewtopic.php?f=34&t=99595 and https://www.raspberrypi.org/forums/viewtopic.php?f=38&t=105708&p=791212#p791212. +4. SSL IMPORT PROCEDURE NOT PROVEN!!! + + + diff --git a/AirScoutPlaneServer/Settings.cs b/AirScoutPlaneServer/Settings.cs new file mode 100644 index 0000000..96c4d44 --- /dev/null +++ b/AirScoutPlaneServer/Settings.cs @@ -0,0 +1,28 @@ +namespace AirScoutPlaneServer.Properties { + + + // Diese Klasse ermöglicht die Behandlung bestimmter Ereignisse der Einstellungsklasse: + // Das SettingChanging-Ereignis wird ausgelöst, bevor der Wert einer Einstellung geändert wird. + // Das PropertyChanged-Ereignis wird ausgelöst, nachdem der Wert einer Einstellung geändert wurde. + // Das SettingsLoaded-Ereignis wird ausgelöst, nachdem die Einstellungswerte geladen wurden. + // Das SettingsSaving-Ereignis wird ausgelöst, bevor die Einstellungswerte gespeichert werden. + internal sealed partial class Settings { + + public Settings() { + // // Heben Sie die Auskommentierung der unten angezeigten Zeilen auf, um Ereignishandler zum Speichern und Ändern von Einstellungen hinzuzufügen: + // + // this.SettingChanging += this.SettingChangingEventHandler; + // + this.SettingsSaving += this.SettingsSavingEventHandler; + // + } + + private void SettingChangingEventHandler(object sender, System.Configuration.SettingChangingEventArgs e) { + // Fügen Sie hier Code zum Behandeln des SettingChangingEvent-Ereignisses hinzu. + } + + private void SettingsSavingEventHandler(object sender, System.ComponentModel.CancelEventArgs e) { + // Fügen Sie hier Code zum Behandeln des SettingsSaving-Ereignisses hinzu. + } + } +} diff --git a/AirScoutPlaneServer/Webserver.cs b/AirScoutPlaneServer/Webserver.cs new file mode 100644 index 0000000..c0ec9ac --- /dev/null +++ b/AirScoutPlaneServer/Webserver.cs @@ -0,0 +1,111 @@ + +using System; +using System.ComponentModel; +using System.Windows.Forms; +using System.Net; +using System.IO; +using System.Threading; +using System.Diagnostics; + +namespace AirScoutPlaneServer +{ + + public partial class MainDlg : Form + { + + private void bw_Webserver_DoWork(object sender, DoWorkEventArgs e) + { + // run simple web server + string hosturl = "http://+:" + Properties.Settings.Default.Webserver_Port.ToString() + "/"; + string userName = System.Security.Principal.WindowsIdentity.GetCurrent().Name; + while (!bw_Webserver.CancellationPending) + { + string[] prefixes = new string[1]; + prefixes[0] = hosturl; + try + { + if (!HttpListener.IsSupported) + { + Console.WriteLine("Windows XP SP2 or Server 2003 is required to use the HttpListener class."); + return; + } + // URI prefixes are required, + if (prefixes == null || prefixes.Length == 0) + throw new ArgumentException("prefixes"); + + // Create a listener. + HttpListener listener = new HttpListener(); + // Add the prefixes. + foreach (string s in prefixes) + { + listener.Prefixes.Add(s); + } + listener.Start(); + Console.WriteLine("Listening..."); + // Note: The GetContext method blocks while waiting for a request. + HttpListenerContext context = listener.GetContext(); + HttpListenerRequest request = context.Request; + // Obtain a response object. + HttpListenerResponse response = context.Response; + // Construct a default response. + string responseString = Properties.Settings.Default.Webserver_Welcome; + if (request.RawUrl == "/planes.json") + { + try + { + var fs = File.OpenRead(TmpDirectory + Path.DirectorySeparatorChar + "planes.json"); + using (StreamReader sr = new StreamReader(fs)) + { + responseString = sr.ReadToEnd(); + } + } + catch + { + // do nothing + } + } + byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString); + // Get a response stream and write the response to it. + response.ContentLength64 = buffer.Length; + System.IO.Stream output = response.OutputStream; + output.Write(buffer, 0, buffer.Length); + // You must close the output stream. + output.Close(); + listener.Stop(); + } + catch (HttpListenerException ex) + { + if (ex.ErrorCode == 5) + { + // gain additional access rights for that specific host url + // user will be prompted for allow changes + // DO NOT USE THE "listener=yes" option as recommended!!! + string args = "http add urlacl " + hosturl + " user=" + userName; + ProcessStartInfo psi = new ProcessStartInfo("netsh", args); + psi.Verb = "runas"; + psi.CreateNoWindow = true; + psi.WindowStyle = ProcessWindowStyle.Hidden; + psi.UseShellExecute = true; + + Process.Start(psi).WaitForExit(); + } + } + catch (Exception ex) + { + // do almost nothing + // wait 10 seconds and restart the listener + Thread.Sleep(10000); + } + finally + { + } + } + } + + private void bw_Webserver_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + + } + + } +} diff --git a/AirScoutPlaneServer/app.config b/AirScoutPlaneServer/app.config new file mode 100644 index 0000000..e825bd2 --- /dev/null +++ b/AirScoutPlaneServer/app.config @@ -0,0 +1,141 @@ + + + + +
+ + +
+ + + + + + + + + + + + + + + + + + False + + + False + + + False + + + True + + + 9872 + + + <HTML><BODY> Welcome to AirScout PlaneServer</BODY></HTML> + + + True + + + %DataDir%\Log + + + %DataDir%\Database + + + True + + + %AppDir% + + + PlaneServer.ico + + + 5 + + + -15 + + + 30 + + + 35 + + + 60 + + + OpenStreetMap + + + 5000 + + + False + + + 20000 + + + 5 + + + 0 + + + %DataDir%\Tmp + + + http://www.airscout.de/UpdateURL.txt + + + 1 + + + [none] + + + [none] + + + [none] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AirScoutPlaneServer/libSQLite.Interop.so b/AirScoutPlaneServer/libSQLite.Interop.so new file mode 100644 index 0000000..15d0e3e Binary files /dev/null and b/AirScoutPlaneServer/libSQLite.Interop.so differ diff --git a/AirScoutPlaneServer/packages.config b/AirScoutPlaneServer/packages.config new file mode 100644 index 0000000..1512388 --- /dev/null +++ b/AirScoutPlaneServer/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/AirScoutViewClient/AirScoutViewClient.csproj b/AirScoutViewClient/AirScoutViewClient.csproj new file mode 100644 index 0000000..a3363ad --- /dev/null +++ b/AirScoutViewClient/AirScoutViewClient.csproj @@ -0,0 +1,170 @@ + + + + + Debug + AnyCPU + {930668AA-0DD9-42A9-889A-D319567F1473} + WinExe + Properties + AirScoutViewClient + AirScoutViewClient + v4.0 + 512 + + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Newtonsoft.Json.12.0.1\lib\net40\Newtonsoft.Json.dll + True + + + + + ..\packages\System.Data.SQLite.Core.1.0.110.0\lib\net40\System.Data.SQLite.dll + True + + + + + + + + + + + + + + Form + + + MapViewDlg.cs + + + + + + MapViewDlg.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + + + + Always + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + Always + + + + + {288a26ec-b690-41a2-84e5-61c9b7b74046} + AirScout.Aircrafts + + + {41b66be4-6086-4ae3-be31-c81ee6b10154} + AirScout.Core + + + {d0c39d9d-bed0-418b-9a5e-713176caf40c} + GMap.NET.Core + + + {e06def77-f933-42fb-afd7-db2d0d8d6a98} + GMap.NET.WindowsForms + + + {8bc521ad-81cf-4e6c-8898-be67527e7064} + OxyPlot.WindowsForms + + + {507de008-21ac-469b-bc30-23b239832af6} + OxyPlot + + + {ee86e933-d883-4b18-80eb-0fba55ec67c6} + ScoutBase.Core + + + {009cabfd-726d-481f-972d-0a218e0ad9b9} + ScoutBase.Elevation + + + {610db007-5f74-4b5d-8b71-5e2c163a99b3} + ScoutBase.Propagation + + + {358e87d7-849f-412a-b487-f7b7d585a7f9} + ScoutBase.Stations + + + {6056d3be-7002-4a6a-a9ea-6ff45122a3c7} + SQLiteDatabase + + + + + PreserveNewest + + + + PreserveNewest + + + Always + + + + + + + Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}". + + + + + \ No newline at end of file diff --git a/AirScoutViewClient/Enums.cs b/AirScoutViewClient/Enums.cs new file mode 100644 index 0000000..676240d --- /dev/null +++ b/AirScoutViewClient/Enums.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace AirScoutViewClient +{ + public enum VIEWCLIENTSTATUS + { + NONE = 0, + INIT = 1, + CONNECTING = 2, + CONNECTED = 4, + ERROR = 128 + } + +} diff --git a/AirScoutViewClient/LICENSE b/AirScoutViewClient/LICENSE new file mode 100644 index 0000000..167ff25 --- /dev/null +++ b/AirScoutViewClient/LICENSE @@ -0,0 +1,2524 @@ +******************************************************************************** +* AirScout AirCraft Scatter Prediction * +* * +* General Licence Information: 2018-08-03 * +******************************************************************************** + +Copyright (c) 2018 by DL2ALF +http://www.dl2alf.de + +******************************************************************************** +General Information +******************************************************************************** + +Writing a complex software is simply not possible to do "from the scratch". +Therefor I as the author of AirScout do rely on a lot of other open source +software provided by other people allover the world. Without the help of +these generous contributors I would never finish the work. + +I am not a skilled software engineer or a lawyer. For me, dealing with all the +different kinds of open source licences is a mess. It took me a long time to +figure out what is a combined/derived work and which licenses are compatible +to each other and I am still learning. I try my best to list below all the +licenses of any library used for the project. If you are the license holder +and feel something is wrong please tell me. It is not my intention to violate +any copyright or license. + +This is a compilation of different licences used in this project: + +- Gnu General Public License, Version 3.0 +- The MIT License (MIT) +- The Code Project Open License (CPOL) 1.02 +- Microsoft Public License (Ms-PL) +- other personal/non-commercial or otherwise specific licenses + +This license information file consists of >1500 lines. This is a good +reflection of my situation as a programmer of free software. + + +******************************************************************************** +Main Licence Information +(AirScout.exe) +******************************************************************************** + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + +******************************************************************************** +Licence Information for Great Maps .NET +(GMAP.NET.*.dll) +******************************************************************************** + +http://greatmaps.codeplex.com/ + +The MIT License (MIT) + +Copyright (c) 2008-2011 Universe, + +WARNING: This software can access some map providers and may viotile their +Terms of Service, you use it at your own risk, nothing is forcing you to +accept this ;} Source itself is legal! + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +******************************************************************************** +Licence Information for Cubic Spline +(Cubicspline.dll) +******************************************************************************** + +http://www.codeproject.com/Articles/560163/Csharp-Cubic-Spline-Interpolation + +Author: Ryan Seghers +Copyright (C) 2013 Ryan Seghers + +The Code Project Open License (CPOL) 1.02 + +Preamble + +This License governs Your use of the Work. This License is intended to allow +developers to use the Source Code and Executable Files provided as part of +the Work in any application in any form. + +The main points subject to the terms of the License are: + + Source Code and Executable Files can be used in commercial applications; + Source Code and Executable Files can be redistributed; and + Source Code can be modified to create derivative works. + No claim of suitability, guarantee, or any warranty whatsoever is + provided. + The software is provided "as-is". The Article(s) accompanying the Work + may not be distributed or republished without the Author's consent + +This License is entered between You, the individual or other entity reading +or otherwise making use of the Work licensed pursuant to this License and the +individual or other entity which offers the Work under the terms of this +License ("Author"). + +License + +THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CODE PROJECT +OPEN LICENSE ("LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER +APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE +OR COPYRIGHT LAW IS PROHIBITED. BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED +HEREIN, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. +THE AUTHOR GRANTS YOU THE RIGHTS CONTAINED HEREIN IN CONSIDERATION OF YOUR +ACCEPTANCE OF SUCH TERMS AND CONDITIONS. IF YOU DO NOT AGREE TO ACCEPT AND BE +BOUND BY THE TERMS OF THIS LICENSE, YOU CANNOT MAKE ANY USE OF THE WORK. + + 1. Definitions + a) "Articles" means, collectively, all articles written by Author + which describes how the Source Code and Executable Files for the Work + may be used by a user. + b) "Author" means the individual or entity that offers the Work under + the terms of this License. + c) "Derivative Work" means a work based upon the Work or upon the Work + and other pre-existing works. + d) "Executable Files" refer to the executables, binary files, + configuration and any required data files included in the Work. + e) "Publisher" means the provider of the website, magazine, CD-ROM, + DVD or other medium from or by which the Work is obtained by You. + f) "Source Code" refers to the collection of source code and + configuration files used to create the Executable Files. + g) "Standard Version" refers to such a Work if it has not been + modified, or has been modified in accordance with the consent of the + Author, such consent being in the full discretion of the Author. + h) "Work" refers to the collection of files distributed by the + Publisher, including the Source Code, Executable Files, binaries, data + files, documentation, whitepapers and the Articles. + i) "You" is you, an individual or entity wishing to use the Work and + exercise your rights under this License. + + 2. Fair Use/Fair Use Rights. + Nothing in this License is intended to reduce, limit, or restrict any + rights arising from fair use, fair dealing, first sale or other + limitations on the exclusive rights of the copyright owner under copyright + law or other applicable laws. + + 3. License Grant. + Subject to the terms and conditions of this License, the Author hereby + grants You a worldwide, royalty-free, non-exclusive, perpetual (for the + duration of the applicable copyright) license to exercise the rights in + the Work as stated below: + + a) You may use the standard version of the Source Code or Executable + Files in Your own applications. + b) You may apply bug fixes, portability fixes and other modifications + obtained from the Public Domain or from the Author. A Work modified in + such a way shall still be considered the standard version and will be + subject to this License. + c) You may otherwise modify Your copy of this Work (excluding the + Articles) in any way to create a Derivative Work, provided that You + insert a prominent notice in each changed file stating how, when and + where You changed that file. + d) You may distribute the standard version of the Executable Files and + Source Code or Derivative Work in aggregate with other (possibly + commercial) programs as part of a larger (possibly commercial) + software distribution. + e) The Articles discussing the Work published in any form by the + author may not be distributed or republished without the Author's + consent. The author retains copyright to any such Articles. You may + use the Executable Files and Source Code pursuant to this License but + you may not repost or republish or otherwise distribute or make + available the Articles, without the prior written consent of the + Author. + + Any subroutines or modules supplied by You and linked into the Source Code + or Executable Files of this Work shall not be considered part of this Work + and will not be subject to the terms of this License. + + 4. Patent License. + Subject to the terms and conditions of this License, each Author hereby + grants to You a perpetual, worldwide, non-exclusive, no-charge, + royalty-free, irrevocable (except as stated in this section) patent + license to make, have made, use, import, and otherwise transfer the Work. + + 5. Restrictions. The license granted in Section 3 above is expressly made + subject to and limited by the following restrictions: + + a) You agree not to remove any of the original copyright, patent, + trademark, and attribution notices and associated disclaimers that may + appear in the Source Code or Executable Files. + b) You agree not to advertise or in any way imply that this Work is a + product of Your own. + c) The name of the Author may not be used to endorse or promote products + derived from the Work without the prior written consent of the Author. + d) You agree not to sell, lease, or rent any part of the Work. This + does not restrict you from including the Work or any part of the Work + inside a larger software distribution that itself is being sold. The + Work by itself, though, cannot be sold, leased or rented. + e) You may distribute the Executable Files and Source Code only under + the terms of this License, and You must include a copy of, or the + Uniform Resource Identifier for, this License with every copy of the + Executable Files or Source Code You distribute and ensure that anyone + receiving such Executable Files and Source Code agrees that the terms + of this License apply to such Executable Files and/or Source Code. + You may not offer or impose any terms on the Work that alter or + restrict the terms of this License or the recipients' exercise of the + rights granted hereunder. You may not sublicense the Work. You must + keep intact all notices that refer to this License and to the + disclaimer of warranties. You may not distribute the Executable Files + or Source Code with any technological measures that control access or + use of the Work in a manner inconsistent with the terms of this + License. + f) You agree not to use the Work for illegal, immoral or improper + purposes, or on pages containing illegal, immoral or improper material. + The Work is subject to applicable export laws. You agree to comply with + all such laws and regulations that may apply to the Work + after Your receipt of the Work. + + 6. Representations, Warranties and Disclaimer. + THIS WORK IS PROVIDED "AS IS", "WHERE IS" AND "AS AVAILABLE", WITHOUT + ANY EXPRESS OR IMPLIED WARRANTIES OR CONDITIONS OR GUARANTEES. YOU, THE + USER, ASSUME ALL RISK IN ITS USE, INCLUDING COPYRIGHT INFRINGEMENT, + PATENT INFRINGEMENT, SUITABILITY, ETC. AUTHOR EXPRESSLY DISCLAIMS ALL + EXPRESS, IMPLIED OR STATUTORY WARRANTIES OR CONDITIONS, INCLUDING + WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF MERCHANTABILITY, + MERCHANTABLE QUALITY OR FITNESS FOR A PARTICULAR PURPOSE, OR ANY + WARRANTY OF TITLE OR NON-INFRINGEMENT, OR THAT THE WORK (OR ANY PORTION + THEREOF) IS CORRECT, USEFUL, BUG-FREE OR FREE OF VIRUSES. YOU MUST PASS + THIS DISCLAIMER ON WHENEVER YOU DISTRIBUTE THE WORK OR DERIVATIVE WORKS. + + 7. Indemnity. + You agree to defend, indemnify and hold harmless the Author and the + Publisher from and against any claims, suits, losses, damages, liabilities, + costs, and expenses (including reasonable legal or attorneys fees) + resulting from or relating to any use of the Work by You. + + 8. Limitation on Liability. + EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL THE + AUTHOR OR THE PUBLISHER BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY + SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING + OUT OF THIS LICENSE OR THE USE OF THE WORK OR OTHERWISE, EVEN IF THE + AUTHOR OR THE PUBLISHER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + DAMAGES. + + 9. Termination. + + a) This License and the rights granted hereunder will terminate + automatically upon any breach by You of any term of this License. + Individuals or entities who have received Derivative Works from You + under this License, however, will not have their licenses terminated + provided such individuals or entities remain in full compliance with + those licenses. Sections 1, 2, 6, 7, 8, 9, 10 and 11 will survive any + termination of this License. + b) If You bring a copyright, trademark, patent or any other + infringement claim against any contributor over infringements You claim + are made by the Work, your License from such contributor to the Work + ends automatically. + c) Subject to the above terms and conditions, this License is perpetual + (for the duration of the applicable copyright in the Work). + Notwithstanding the above, the Author reserves the right to release the + Work under different license terms or to stop distributing the Work at + any time; provided, however that any such election will not serve to + withdraw this License (or any other license that has been, or is + required to be, granted under the terms of this License), and this + License will continue in full force and effect unless terminated as + stated above. + + 10. Publisher. + The parties hereby confirm that the Publisher shall not, under any + circumstances, be responsible for and shall not have any liability in + respect of the subject matter of this License. The Publisher makes no + warranty whatsoever in connection with the Work and shall not be liable + to You or any party on any legal theory for any damages whatsoever, + including without limitation any general, special, incidental or + consequential damages arising in connection to this license. The Publisher + reserves the right to cease making the Work available to You at any time + without notice. + + 11. Miscellaneous + a) This License shall be governed by the laws of the location of the + head office of the Author or if the Author is an individual, the laws + of location of the principal place of residence of the Author. + b) If any provision of this License is invalid or unenforceable under + applicable law, it shall not affect the validity or enforceability of + the remainder of the terms of this License, and without further action + by the parties to this License, such provision shall be reformed to the + minimum extent necessary to make such provision valid and enforceable. + c) No term or provision of this License shall be deemed waived and no + breach consented to unless such waiver or consent shall be in writing + and signed by the party to be charged with such waiver or consent. + d) This License constitutes the entire agreement between the parties + with respect to the Work licensed herein. There are no understandings, + agreements or representations with respect to the Work not specified + herein. The Author shall not be bound by any additional provisions + that may appear in any communication from You. This License may not be + modified without the mutual written agreement of the Author and You. + + +************************************************************************************** +Licence Information for Renci SSH .NET +(Renci.SshNet.dll) +************************************************************************************** + +https://sshnet.codeplex.com/ + +New BSD License (BSD) + +Copyright (c) 2010, RENCI +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +* Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +* Neither the name of RENCI nor the names of its contributors may be used to +endorse or promote products derived from this software without specific prior +written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +************************************************************************************** +Licence Information for Orbit Tools +(Zeptomoby.OrbitTools.*.dll) +************************************************************************************** + +http://www.zeptomoby.com/satellites/ + +The OrbitTools Libraries +NORAD SGP4/SDP4 Implementations in C++ and C# by Michael F. Henry + +Provided here are implementations of NORAD algorithms for determining satellite +location and velocity in earth orbit. The algorithms come from the December, +1980 NORAD document "Space Track Report No. 3". The two orbital models +implemented here are: SGP4, for "near-earth" objects, and SDP4 for "deep +space" objects. These two models are widely used in satellite tracking software +and can produce very accurate results when used with current NORAD two-line +element data. + +Public and Standard Editions + +In 2002, I created a modern, object-oriented C++ version of the original 1980's +era NORAD FORTRAN IV SGP4/SDP4 implementations. In 2003, I ported this C++ +implementation to C#. These two implementations represent the Public Edition +of my software, and are available free of charge for non-commercial use. +Commercial users must purchase a software license agreement, for which they +receive the Standard Edition of the software. The Standard Edition includes +all the features of the Public Edition, plus WGS-84 support, and ECF coordinate +support. + +Professional Edition and Track Library + +In 2009, I integrated the changes recommended in "Revisiting Space Track Report +No. 3" (D. Valledo, et al.) into my C# implementation, and did the same for my +C++ implementation in 2010. These two implementations of the software +represent the Professional Edition, and are available only to customers who +purchase a license agreement. In 2012, I developed the Track Library as an +extension of the Professional Edition. The Track Library provides +implementations of common tasks performed by satellite tracking software, +including satellite pass predictions. Information about the Track Library can +be found here. + +More information about the the Public, Standard, and Professional Editions of +the OrbitTools libraries can be found here. + +For excellent information on the underlying physics of orbits, visible +satellite observations, current NORAD TLE data, and other related material, +see www.celestrak.com which is maintained by Dr. T. S. Kelso. + +Michael F. Henry +mfh@zeptomoby.com +April, 2014 + +************************************************************************************** +Licence Information for Ionic ZIP Tools +(ionic.dll) +************************************************************************************** + +http://dotnetzip.codeplex.com/ + +Microsoft Public License (Ms-PL) + +This license governs use of the accompanying software. If you use the software, +you accept this license. If you do not accept the license, do not use the +software. + +1. Definitions + +The terms "reproduce," "reproduction," "derivative works," and "distribution" +have the same meaning here as under U.S. copyright law. + +A "contribution" is the original software, or any additions or changes to the +software. +A "contributor" is any person that distributes its contribution under this +license. +"Licensed patents" are a contributor's patent claims that read directly on its +contribution. + +2. Grant of Rights + +(A) Copyright Grant- Subject to the terms of this license, including the +license conditions and limitations in section 3, each contributor grants you a +non-exclusive, worldwide, royalty-free copyright license to reproduce its +contribution, prepare derivative works of its contribution, and distribute its +contribution or any derivative works that you create. + +(B) Patent Grant- Subject to the terms of this license, including the license +conditions and limitations in section 3, each contributor grants you a +non-exclusive, worldwide, royalty-free license under its licensed patents to +make, have made, use, sell, offer for sale, import, and/or otherwise dispose +of its contribution in the software or derivative works of the contribution +in the software. + +3. Conditions and Limitations + +(A) No Trademark License- This license does not grant you rights to use any +contributors' name, logo, or trademarks. +(B) If you bring a patent claim against any contributor over patents that you +claim are infringed by the software, your patent license from such contributor +to the software ends automatically. +(C) If you distribute any portion of the software, you must retain all +copyright, patent, trademark, and attribution notices that are present in the +software. +(D) If you distribute any portion of the software in source code form, you may +do so only under this license by including a complete copy of this license with +your distribution. If you distribute any portion of the software in compiled or +object code form, you may only do so under a license that complies with this +license. +(E) The software is licensed "as-is." You bear the risk of using it. The +contributors give no express warranties, guarantees or conditions. You may have +additional consumer rights under your local laws which this license cannot +change. To the extent permitted under your local laws, the contributors exclude +the implied warranties of merchantability, fitness for a particular purpose and +non-infringement. + + +************************************************************************************** +Licence Information for LibASDB +(LibASDB.dll) +************************************************************************************** + +This library is ported and modified from the openskynetwork java-adsb library +by DL2ALF. See https://github.com/openskynetwork/java-adsb for more details. + +You may consider yourself whether this is a derived work or not. +Therefor the C# code is under GPL V3.0 to avoid any license issues. + + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + +************************************************************************************** +Licence Information for JSON +(LibASDB.dll) +************************************************************************************** + +The MIT License (MIT) + +Copyright (c) 2007 James Newton-King + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +************************************************************************************** +Licence Information for HtmlAgilityPack +(HtmlAgilityPack.dll) +************************************************************************************** + +https://htmlagilitypack.codeplex.com/ + +Microsoft Public License (Ms-PL) + +This license governs use of the accompanying software. If you use the software, +you accept this license. If you do not accept the license, do not use the +software. + +1. Definitions + +The terms "reproduce," "reproduction," "derivative works," and "distribution" +have the same meaning here as under U.S. copyright law. + +A "contribution" is the original software, or any additions or changes to the +software. +A "contributor" is any person that distributes its contribution under this +license. +"Licensed patents" are a contributor's patent claims that read directly on its +contribution. + +2. Grant of Rights + +(A) Copyright Grant- Subject to the terms of this license, including the +license conditions and limitations in section 3, each contributor grants you a +non-exclusive, worldwide, royalty-free copyright license to reproduce its +contribution, prepare derivative works of its contribution, and distribute its +contribution or any derivative works that you create. + +(B) Patent Grant- Subject to the terms of this license, including the license +conditions and limitations in section 3, each contributor grants you a +non-exclusive, worldwide, royalty-free license under its licensed patents to +make, have made, use, sell, offer for sale, import, and/or otherwise dispose +of its contribution in the software or derivative works of the contribution +in the software. + +3. Conditions and Limitations + +(A) No Trademark License- This license does not grant you rights to use any +contributors' name, logo, or trademarks. +(B) If you bring a patent claim against any contributor over patents that you +claim are infringed by the software, your patent license from such contributor +to the software ends automatically. +(C) If you distribute any portion of the software, you must retain all +copyright, patent, trademark, and attribution notices that are present in the +software. +(D) If you distribute any portion of the software in source code form, you may +do so only under this license by including a complete copy of this license with +your distribution. If you distribute any portion of the software in compiled or +object code form, you may only do so under a license that complies with this +license. +(E) The software is licensed "as-is." You bear the risk of using it. The +contributors give no express warranties, guarantees or conditions. You may have +additional consumer rights under your local laws which this license cannot +change. To the extent permitted under your local laws, the contributors exclude +the implied warranties of merchantability, fitness for a particular purpose and +non-infringement. + +************************************************************************************** +Licence Information for SQLite database +(System.Data.SQlite.dll, System.Data.SQliteEF6.dll, System.Data.SQlite.Linq.dll) +************************************************************************************** + +http://www.sqlite.org + +SQLite Is Public Domain + +All of the code and documentation in SQLite has been dedicated to the public domain +by the authors. All code authors, and representatives of the companies they work for, +have signed affidavits dedicating their contributions to the public domain and +originals of those signed affidavits are stored in a firesafe at the main offices +of Hwaci. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute the +original SQLite code, either in source code form or as a compiled binary, for any +purpose, commercial or non-commercial, and by any means. + +The previous paragraph applies to the deliverable code and documentation in +SQLite - those parts of the SQLite library that you actually bundle and ship with a +larger application. Some scripts used as part of the build process (for example +the "configure" scripts generated by autoconf) might fall under other open-source +licenses. Nothing from these build scripts ever reaches the final deliverable SQLite +library, however, and so the licenses associated with those scripts should not be a +factor in assessing your rights to copy and use the SQLite library. + +All of the deliverable code in SQLite has been written from scratch. No code has been +taken from other projects or from the open internet. Every line of code can be +traced back to its original author, and all of those authors have public domain +dedications on file. So the SQLite code base is clean and is uncontaminated with +licensed code from other projects. + + +******************************************************************************** +Licence Information for Wizard.NET Library +(AeroWizard.dll) +******************************************************************************** + +https://aerowizard.codeplex.com/ + +The MIT License (MIT) + +Copyright (c) 2013 David Hall + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the +Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************************** +Licence Information for Serializable Generics +(SerializableGenerics.dll) +******************************************************************************** + + Copyright (c) 2009, Eduardo Sanchez-Ros + http://eduardosanchezros.blogspot.de/2011/03/serializing-generics_606.html + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + +******************************************************************************** +Licence Information for OxyPlot +(OxyPlot.dll, OxyPlotWindowsForms.dll) +******************************************************************************** + +The MIT License (MIT) + +Copyright (c) 2014 OxyPlot contributors + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + + * * * + + 2015-02-08 + diff --git a/AirScoutViewClient/Main.ico b/AirScoutViewClient/Main.ico new file mode 100644 index 0000000..ea92206 Binary files /dev/null and b/AirScoutViewClient/Main.ico differ diff --git a/AirScoutViewClient/MapViewDlg.Designer.cs b/AirScoutViewClient/MapViewDlg.Designer.cs new file mode 100644 index 0000000..b147426 --- /dev/null +++ b/AirScoutViewClient/MapViewDlg.Designer.cs @@ -0,0 +1,674 @@ +namespace AirScoutViewClient +{ + partial class MapViewDlg + { + /// + /// Erforderliche Designervariable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Verwendete Ressourcen bereinigen. + /// + /// True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Vom Windows Form-Designer generierter Code + + /// + /// Erforderliche Methode für die Designerunterstützung. + /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + ScoutBase.Core.LatLon.GPoint gPoint5 = new ScoutBase.Core.LatLon.GPoint(); + ScoutBase.Core.LatLon.GPoint gPoint6 = new ScoutBase.Core.LatLon.GPoint(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MapViewDlg)); + this.mnu_Main = new System.Windows.Forms.MenuStrip(); + this.tsi_Exit = new System.Windows.Forms.ToolStripMenuItem(); + this.settingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.tsi_Info = new System.Windows.Forms.ToolStripMenuItem(); + this.ss_Main = new System.Windows.Forms.StatusStrip(); + this.tsl_Status = new System.Windows.Forms.ToolStripStatusLabel(); + this.gb_Info = new System.Windows.Forms.GroupBox(); + this.cb_DXLoc = new ScoutBase.Core.LocatorComboBox(); + this.cb_MyLoc = new ScoutBase.Core.LocatorComboBox(); + this.label8 = new System.Windows.Forms.Label(); + this.cb_Band = new System.Windows.Forms.ComboBox(); + this.tb_QTF = new System.Windows.Forms.TextBox(); + this.label6 = new System.Windows.Forms.Label(); + this.tb_QRB = new System.Windows.Forms.TextBox(); + this.label16 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.tb_DXCall = new ScoutBase.Core.CallsignTextBox(); + this.label5 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.tb_MyCall = new ScoutBase.Core.CallsignTextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.label1 = new System.Windows.Forms.Label(); + this.tb_UTC = new System.Windows.Forms.TextBox(); + this.gb_Map = new System.Windows.Forms.GroupBox(); + this.spc_Main = new System.Windows.Forms.SplitContainer(); + this.gm_Main = new GMap.NET.WindowsForms.GMapControl(); + this.gb_Path = new System.Windows.Forms.GroupBox(); + this.ti_Progress = new System.Windows.Forms.Timer(this.components); + this.ti_Startup = new System.Windows.Forms.Timer(this.components); + this.il_Planes_H = new System.Windows.Forms.ImageList(this.components); + this.il_Planes_L = new System.Windows.Forms.ImageList(this.components); + this.il_Planes_S = new System.Windows.Forms.ImageList(this.components); + this.il_Airports = new System.Windows.Forms.ImageList(this.components); + this.il_Planes_M = new System.Windows.Forms.ImageList(this.components); + this.ti_ShowLegends = new System.Windows.Forms.Timer(this.components); + this.btn_Map_PlayPause = new System.Windows.Forms.Button(); + this.gb_Control = new System.Windows.Forms.GroupBox(); + this.il_Main = new System.Windows.Forms.ImageList(this.components); + this.gb_Map_Zoom = new System.Windows.Forms.GroupBox(); + this.pa_Map_Zoom = new System.Windows.Forms.Panel(); + this.tb_Map_Zoom = new System.Windows.Forms.TextBox(); + this.btn_Map_Zoom_Out = new System.Windows.Forms.Button(); + this.btn_Map_Zoom_In = new System.Windows.Forms.Button(); + this.tsl_ConnectionStatus = new System.Windows.Forms.ToolStripStatusLabel(); + this.cb_Map_AutoCenter = new System.Windows.Forms.CheckBox(); + this.mnu_Main.SuspendLayout(); + this.ss_Main.SuspendLayout(); + this.gb_Info.SuspendLayout(); + this.gb_Map.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.spc_Main)).BeginInit(); + this.spc_Main.Panel1.SuspendLayout(); + this.spc_Main.Panel2.SuspendLayout(); + this.spc_Main.SuspendLayout(); + this.gb_Control.SuspendLayout(); + this.gb_Map_Zoom.SuspendLayout(); + this.pa_Map_Zoom.SuspendLayout(); + this.SuspendLayout(); + // + // mnu_Main + // + this.mnu_Main.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.tsi_Exit, + this.settingsToolStripMenuItem, + this.tsi_Info}); + this.mnu_Main.Location = new System.Drawing.Point(0, 0); + this.mnu_Main.Name = "mnu_Main"; + this.mnu_Main.Size = new System.Drawing.Size(836, 24); + this.mnu_Main.TabIndex = 0; + this.mnu_Main.Text = "menuStrip1"; + // + // tsi_Exit + // + this.tsi_Exit.Name = "tsi_Exit"; + this.tsi_Exit.Size = new System.Drawing.Size(37, 20); + this.tsi_Exit.Text = "E&xit"; + this.tsi_Exit.Click += new System.EventHandler(this.tsi_Exit_Click); + // + // settingsToolStripMenuItem + // + this.settingsToolStripMenuItem.Name = "settingsToolStripMenuItem"; + this.settingsToolStripMenuItem.Size = new System.Drawing.Size(61, 20); + this.settingsToolStripMenuItem.Text = "&Settings"; + // + // tsi_Info + // + this.tsi_Info.Name = "tsi_Info"; + this.tsi_Info.Size = new System.Drawing.Size(40, 20); + this.tsi_Info.Text = "&Info"; + this.tsi_Info.Click += new System.EventHandler(this.tsi_Info_Click); + // + // ss_Main + // + this.ss_Main.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.tsl_Status, + this.tsl_ConnectionStatus}); + this.ss_Main.Location = new System.Drawing.Point(0, 518); + this.ss_Main.Name = "ss_Main"; + this.ss_Main.Size = new System.Drawing.Size(836, 22); + this.ss_Main.TabIndex = 1; + this.ss_Main.Text = "statusStrip1"; + // + // tsl_Status + // + this.tsl_Status.Name = "tsl_Status"; + this.tsl_Status.Size = new System.Drawing.Size(782, 17); + this.tsl_Status.Spring = true; + this.tsl_Status.Text = "Status"; + this.tsl_Status.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // gb_Info + // + this.gb_Info.Controls.Add(this.gb_Map_Zoom); + this.gb_Info.Controls.Add(this.gb_Control); + this.gb_Info.Controls.Add(this.cb_DXLoc); + this.gb_Info.Controls.Add(this.cb_MyLoc); + this.gb_Info.Controls.Add(this.label8); + this.gb_Info.Controls.Add(this.cb_Band); + this.gb_Info.Controls.Add(this.tb_QTF); + this.gb_Info.Controls.Add(this.label6); + this.gb_Info.Controls.Add(this.tb_QRB); + this.gb_Info.Controls.Add(this.label16); + this.gb_Info.Controls.Add(this.label4); + this.gb_Info.Controls.Add(this.tb_DXCall); + this.gb_Info.Controls.Add(this.label5); + this.gb_Info.Controls.Add(this.label3); + this.gb_Info.Controls.Add(this.tb_MyCall); + this.gb_Info.Controls.Add(this.label2); + this.gb_Info.Controls.Add(this.label1); + this.gb_Info.Controls.Add(this.tb_UTC); + this.gb_Info.Dock = System.Windows.Forms.DockStyle.Right; + this.gb_Info.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.gb_Info.Location = new System.Drawing.Point(691, 24); + this.gb_Info.Name = "gb_Info"; + this.gb_Info.Size = new System.Drawing.Size(145, 494); + this.gb_Info.TabIndex = 3; + this.gb_Info.TabStop = false; + this.gb_Info.Text = "Info"; + // + // cb_DXLoc + // + this.cb_DXLoc.AutoLength = false; + this.cb_DXLoc.BackColor = System.Drawing.SystemColors.Window; + this.cb_DXLoc.CharacterCasing = System.Windows.Forms.CharacterCasing.Normal; + this.cb_DXLoc.ErrorBackColor = System.Drawing.Color.Red; + this.cb_DXLoc.ErrorForeColor = System.Drawing.Color.White; + this.cb_DXLoc.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_DXLoc.ForeColor = System.Drawing.SystemColors.WindowText; + this.cb_DXLoc.FormattingEnabled = true; + this.cb_DXLoc.GeoLocation = gPoint5; + this.cb_DXLoc.Location = new System.Drawing.Point(6, 243); + this.cb_DXLoc.Name = "cb_DXLoc"; + this.cb_DXLoc.Precision = 3; + this.cb_DXLoc.SilentItemChange = false; + this.cb_DXLoc.Size = new System.Drawing.Size(133, 24); + this.cb_DXLoc.SmallLettersForSubsquares = true; + this.cb_DXLoc.TabIndex = 63; + this.cb_DXLoc.TextChanged += new System.EventHandler(this.cb_DXLoc_TextChanged); + // + // cb_MyLoc + // + this.cb_MyLoc.AutoLength = false; + this.cb_MyLoc.BackColor = System.Drawing.SystemColors.Window; + this.cb_MyLoc.CharacterCasing = System.Windows.Forms.CharacterCasing.Normal; + this.cb_MyLoc.ErrorBackColor = System.Drawing.Color.Red; + this.cb_MyLoc.ErrorForeColor = System.Drawing.Color.White; + this.cb_MyLoc.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_MyLoc.ForeColor = System.Drawing.SystemColors.WindowText; + this.cb_MyLoc.FormattingEnabled = true; + this.cb_MyLoc.GeoLocation = gPoint6; + this.cb_MyLoc.Location = new System.Drawing.Point(6, 161); + this.cb_MyLoc.Name = "cb_MyLoc"; + this.cb_MyLoc.Precision = 3; + this.cb_MyLoc.SilentItemChange = false; + this.cb_MyLoc.Size = new System.Drawing.Size(133, 24); + this.cb_MyLoc.SmallLettersForSubsquares = true; + this.cb_MyLoc.TabIndex = 62; + this.cb_MyLoc.TextChanged += new System.EventHandler(this.cb_MyLoc_TextChanged); + // + // label8 + // + this.label8.AutoSize = true; + this.label8.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label8.Location = new System.Drawing.Point(7, 58); + this.label8.Name = "label8"; + this.label8.Size = new System.Drawing.Size(32, 13); + this.label8.TabIndex = 28; + this.label8.Text = "Band"; + // + // cb_Band + // + this.cb_Band.AllowDrop = true; + this.cb_Band.BackColor = System.Drawing.Color.FloralWhite; + this.cb_Band.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.cb_Band.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.cb_Band.FormattingEnabled = true; + this.cb_Band.Location = new System.Drawing.Point(6, 74); + this.cb_Band.Name = "cb_Band"; + this.cb_Band.Size = new System.Drawing.Size(133, 24); + this.cb_Band.TabIndex = 29; + // + // tb_QTF + // + this.tb_QTF.BackColor = System.Drawing.Color.FloralWhite; + this.tb_QTF.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_QTF.Location = new System.Drawing.Point(6, 323); + this.tb_QTF.Name = "tb_QTF"; + this.tb_QTF.ReadOnly = true; + this.tb_QTF.Size = new System.Drawing.Size(133, 22); + this.tb_QTF.TabIndex = 27; + // + // label6 + // + this.label6.AutoSize = true; + this.label6.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label6.Location = new System.Drawing.Point(7, 307); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(28, 13); + this.label6.TabIndex = 26; + this.label6.Text = "QTF"; + // + // tb_QRB + // + this.tb_QRB.BackColor = System.Drawing.Color.FloralWhite; + this.tb_QRB.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_QRB.Location = new System.Drawing.Point(6, 282); + this.tb_QRB.Name = "tb_QRB"; + this.tb_QRB.ReadOnly = true; + this.tb_QRB.Size = new System.Drawing.Size(133, 22); + this.tb_QRB.TabIndex = 25; + // + // label16 + // + this.label16.AutoSize = true; + this.label16.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label16.Location = new System.Drawing.Point(7, 266); + this.label16.Name = "label16"; + this.label16.Size = new System.Drawing.Size(30, 13); + this.label16.TabIndex = 24; + this.label16.Text = "QRB"; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label4.Location = new System.Drawing.Point(7, 225); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(44, 15); + this.label4.TabIndex = 22; + this.label4.Text = "DXLoc"; + // + // tb_DXCall + // + this.tb_DXCall.BackColor = System.Drawing.SystemColors.Window; + this.tb_DXCall.CharacterCasing = System.Windows.Forms.CharacterCasing.Upper; + this.tb_DXCall.ErrorBackColor = System.Drawing.Color.Red; + this.tb_DXCall.ErrorForeColor = System.Drawing.Color.White; + this.tb_DXCall.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_DXCall.ForeColor = System.Drawing.SystemColors.WindowText; + this.tb_DXCall.Location = new System.Drawing.Point(6, 202); + this.tb_DXCall.Name = "tb_DXCall"; + this.tb_DXCall.Size = new System.Drawing.Size(133, 22); + this.tb_DXCall.TabIndex = 21; + this.tb_DXCall.TextChanged += new System.EventHandler(this.tb_DXCall_TextChanged); + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label5.Location = new System.Drawing.Point(7, 184); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(45, 15); + this.label5.TabIndex = 20; + this.label5.Text = "DXCall"; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label3.Location = new System.Drawing.Point(7, 143); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(43, 15); + this.label3.TabIndex = 18; + this.label3.Text = "MyLoc"; + // + // tb_MyCall + // + this.tb_MyCall.BackColor = System.Drawing.SystemColors.Window; + this.tb_MyCall.CharacterCasing = System.Windows.Forms.CharacterCasing.Upper; + this.tb_MyCall.ErrorBackColor = System.Drawing.Color.Red; + this.tb_MyCall.ErrorForeColor = System.Drawing.Color.White; + this.tb_MyCall.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_MyCall.ForeColor = System.Drawing.SystemColors.WindowText; + this.tb_MyCall.Location = new System.Drawing.Point(6, 120); + this.tb_MyCall.Name = "tb_MyCall"; + this.tb_MyCall.Size = new System.Drawing.Size(133, 22); + this.tb_MyCall.TabIndex = 17; + this.tb_MyCall.TextChanged += new System.EventHandler(this.tb_MyCall_TextChanged); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label2.Location = new System.Drawing.Point(7, 102); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(44, 15); + this.label2.TabIndex = 16; + this.label2.Text = "MyCall"; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label1.Location = new System.Drawing.Point(7, 17); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(29, 13); + this.label1.TabIndex = 15; + this.label1.Text = "UTC"; + // + // tb_UTC + // + this.tb_UTC.BackColor = System.Drawing.Color.LightSalmon; + this.tb_UTC.Font = new System.Drawing.Font("Arial", 9.75F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_UTC.ForeColor = System.Drawing.Color.White; + this.tb_UTC.Location = new System.Drawing.Point(6, 33); + this.tb_UTC.Name = "tb_UTC"; + this.tb_UTC.ReadOnly = true; + this.tb_UTC.Size = new System.Drawing.Size(133, 22); + this.tb_UTC.TabIndex = 14; + // + // gb_Map + // + this.gb_Map.Controls.Add(this.spc_Main); + this.gb_Map.Dock = System.Windows.Forms.DockStyle.Fill; + this.gb_Map.Location = new System.Drawing.Point(0, 24); + this.gb_Map.Name = "gb_Map"; + this.gb_Map.Size = new System.Drawing.Size(691, 494); + this.gb_Map.TabIndex = 4; + this.gb_Map.TabStop = false; + this.gb_Map.Text = "Map"; + // + // spc_Main + // + this.spc_Main.BackColor = System.Drawing.SystemColors.ControlDark; + this.spc_Main.Dock = System.Windows.Forms.DockStyle.Fill; + this.spc_Main.Location = new System.Drawing.Point(3, 16); + this.spc_Main.Name = "spc_Main"; + this.spc_Main.Orientation = System.Windows.Forms.Orientation.Horizontal; + // + // spc_Main.Panel1 + // + this.spc_Main.Panel1.BackColor = System.Drawing.SystemColors.ControlDark; + this.spc_Main.Panel1.Controls.Add(this.gm_Main); + this.spc_Main.Panel1.RightToLeft = System.Windows.Forms.RightToLeft.No; + // + // spc_Main.Panel2 + // + this.spc_Main.Panel2.BackColor = System.Drawing.SystemColors.ControlDark; + this.spc_Main.Panel2.Controls.Add(this.gb_Path); + this.spc_Main.Panel2.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.spc_Main.Size = new System.Drawing.Size(685, 475); + this.spc_Main.SplitterDistance = 300; + this.spc_Main.TabIndex = 0; + // + // gm_Main + // + this.gm_Main.Bearing = 0F; + this.gm_Main.CanDragMap = true; + this.gm_Main.Dock = System.Windows.Forms.DockStyle.Fill; + this.gm_Main.EmptyTileColor = System.Drawing.Color.Navy; + this.gm_Main.GrayScaleMode = false; + this.gm_Main.HelperLineOption = GMap.NET.WindowsForms.HelperLineOptions.DontShow; + this.gm_Main.LevelsKeepInMemmory = 5; + this.gm_Main.Location = new System.Drawing.Point(0, 0); + this.gm_Main.MarkersEnabled = true; + this.gm_Main.MaxZoom = 2; + this.gm_Main.MinZoom = 2; + this.gm_Main.MouseWheelZoomType = GMap.NET.MouseWheelZoomType.MousePositionAndCenter; + this.gm_Main.Name = "gm_Main"; + this.gm_Main.NegativeMode = false; + this.gm_Main.PolygonsEnabled = true; + this.gm_Main.RetryLoadTile = 0; + this.gm_Main.RoutesEnabled = true; + this.gm_Main.SelectedAreaFillColor = System.Drawing.Color.FromArgb(((int)(((byte)(33)))), ((int)(((byte)(65)))), ((int)(((byte)(105)))), ((int)(((byte)(225))))); + this.gm_Main.ShowTileGridLines = false; + this.gm_Main.Size = new System.Drawing.Size(685, 300); + this.gm_Main.TabIndex = 0; + this.gm_Main.Zoom = 0D; + this.gm_Main.OnMarkerClick += new GMap.NET.WindowsForms.MarkerClick(this.gm_Main_OnMarkerClick); + this.gm_Main.OnMapZoomChanged += new GMap.NET.MapZoomChanged(this.gm_Main_OnMapZoomChanged); + // + // gb_Path + // + this.gb_Path.Dock = System.Windows.Forms.DockStyle.Fill; + this.gb_Path.Location = new System.Drawing.Point(0, 0); + this.gb_Path.Name = "gb_Path"; + this.gb_Path.Size = new System.Drawing.Size(685, 171); + this.gb_Path.TabIndex = 0; + this.gb_Path.TabStop = false; + this.gb_Path.Text = "Path"; + this.gb_Path.Resize += new System.EventHandler(this.gb_Path_Resize); + // + // ti_Progress + // + this.ti_Progress.Enabled = true; + this.ti_Progress.Interval = 1000; + this.ti_Progress.Tick += new System.EventHandler(this.ti_Progress_Tick); + // + // ti_Startup + // + this.ti_Startup.Enabled = true; + this.ti_Startup.Interval = 1000; + this.ti_Startup.Tick += new System.EventHandler(this.ti_Startup_Tick); + // + // il_Planes_H + // + this.il_Planes_H.ColorDepth = System.Windows.Forms.ColorDepth.Depth24Bit; + this.il_Planes_H.ImageSize = new System.Drawing.Size(36, 36); + this.il_Planes_H.TransparentColor = System.Drawing.Color.Transparent; + // + // il_Planes_L + // + this.il_Planes_L.ColorDepth = System.Windows.Forms.ColorDepth.Depth24Bit; + this.il_Planes_L.ImageSize = new System.Drawing.Size(16, 16); + this.il_Planes_L.TransparentColor = System.Drawing.Color.Transparent; + // + // il_Planes_S + // + this.il_Planes_S.ColorDepth = System.Windows.Forms.ColorDepth.Depth24Bit; + this.il_Planes_S.ImageSize = new System.Drawing.Size(48, 48); + this.il_Planes_S.TransparentColor = System.Drawing.Color.Transparent; + // + // il_Airports + // + this.il_Airports.ColorDepth = System.Windows.Forms.ColorDepth.Depth24Bit; + this.il_Airports.ImageSize = new System.Drawing.Size(16, 16); + this.il_Airports.TransparentColor = System.Drawing.Color.Transparent; + // + // il_Planes_M + // + this.il_Planes_M.ColorDepth = System.Windows.Forms.ColorDepth.Depth24Bit; + this.il_Planes_M.ImageSize = new System.Drawing.Size(24, 24); + this.il_Planes_M.TransparentColor = System.Drawing.Color.Transparent; + // + // ti_ShowLegends + // + this.ti_ShowLegends.Enabled = true; + this.ti_ShowLegends.Interval = 5000; + this.ti_ShowLegends.Tick += new System.EventHandler(this.ti_ShowLegends_Tick); + // + // btn_Map_PlayPause + // + this.btn_Map_PlayPause.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.btn_Map_PlayPause.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.btn_Map_PlayPause.ImageIndex = 1; + this.btn_Map_PlayPause.ImageList = this.il_Main; + this.btn_Map_PlayPause.Location = new System.Drawing.Point(14, 17); + this.btn_Map_PlayPause.Name = "btn_Map_PlayPause"; + this.btn_Map_PlayPause.Size = new System.Drawing.Size(114, 29); + this.btn_Map_PlayPause.TabIndex = 64; + this.btn_Map_PlayPause.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + this.btn_Map_PlayPause.UseVisualStyleBackColor = true; + this.btn_Map_PlayPause.Click += new System.EventHandler(this.btn_Map_PlayPause_Click); + // + // gb_Control + // + this.gb_Control.Controls.Add(this.btn_Map_PlayPause); + this.gb_Control.Dock = System.Windows.Forms.DockStyle.Bottom; + this.gb_Control.Location = new System.Drawing.Point(3, 436); + this.gb_Control.Name = "gb_Control"; + this.gb_Control.Size = new System.Drawing.Size(139, 55); + this.gb_Control.TabIndex = 64; + this.gb_Control.TabStop = false; + // + // il_Main + // + this.il_Main.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("il_Main.ImageStream"))); + this.il_Main.TransparentColor = System.Drawing.Color.Transparent; + this.il_Main.Images.SetKeyName(0, "PauseHS.png"); + this.il_Main.Images.SetKeyName(1, "PlayHS.png"); + this.il_Main.Images.SetKeyName(2, "RecordHS.png"); + // + // gb_Map_Zoom + // + this.gb_Map_Zoom.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.gb_Map_Zoom.Controls.Add(this.pa_Map_Zoom); + this.gb_Map_Zoom.Dock = System.Windows.Forms.DockStyle.Bottom; + this.gb_Map_Zoom.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.gb_Map_Zoom.Location = new System.Drawing.Point(3, 357); + this.gb_Map_Zoom.Name = "gb_Map_Zoom"; + this.gb_Map_Zoom.Size = new System.Drawing.Size(139, 79); + this.gb_Map_Zoom.TabIndex = 66; + this.gb_Map_Zoom.TabStop = false; + this.gb_Map_Zoom.Text = "Map Zoom"; + // + // pa_Map_Zoom + // + this.pa_Map_Zoom.Controls.Add(this.cb_Map_AutoCenter); + this.pa_Map_Zoom.Controls.Add(this.tb_Map_Zoom); + this.pa_Map_Zoom.Controls.Add(this.btn_Map_Zoom_Out); + this.pa_Map_Zoom.Controls.Add(this.btn_Map_Zoom_In); + this.pa_Map_Zoom.Dock = System.Windows.Forms.DockStyle.Fill; + this.pa_Map_Zoom.Location = new System.Drawing.Point(3, 16); + this.pa_Map_Zoom.Name = "pa_Map_Zoom"; + this.pa_Map_Zoom.Size = new System.Drawing.Size(133, 60); + this.pa_Map_Zoom.TabIndex = 65; + // + // tb_Map_Zoom + // + this.tb_Map_Zoom.BackColor = System.Drawing.Color.FloralWhite; + this.tb_Map_Zoom.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.tb_Map_Zoom.Location = new System.Drawing.Point(45, 9); + this.tb_Map_Zoom.Name = "tb_Map_Zoom"; + this.tb_Map_Zoom.Size = new System.Drawing.Size(41, 22); + this.tb_Map_Zoom.TabIndex = 23; + // + // btn_Map_Zoom_Out + // + this.btn_Map_Zoom_Out.Location = new System.Drawing.Point(92, 8); + this.btn_Map_Zoom_Out.Name = "btn_Map_Zoom_Out"; + this.btn_Map_Zoom_Out.Size = new System.Drawing.Size(30, 23); + this.btn_Map_Zoom_Out.TabIndex = 22; + this.btn_Map_Zoom_Out.Text = "-"; + this.btn_Map_Zoom_Out.UseVisualStyleBackColor = true; + this.btn_Map_Zoom_Out.Click += new System.EventHandler(this.btn_Map_Zoom_Out_Click); + // + // btn_Map_Zoom_In + // + this.btn_Map_Zoom_In.Location = new System.Drawing.Point(9, 8); + this.btn_Map_Zoom_In.Name = "btn_Map_Zoom_In"; + this.btn_Map_Zoom_In.Size = new System.Drawing.Size(30, 23); + this.btn_Map_Zoom_In.TabIndex = 21; + this.btn_Map_Zoom_In.Text = "+"; + this.btn_Map_Zoom_In.UseVisualStyleBackColor = true; + this.btn_Map_Zoom_In.Click += new System.EventHandler(this.btn_Map_Zoom_In_Click); + // + // tsl_ConnectionStatus + // + this.tsl_ConnectionStatus.Name = "tsl_ConnectionStatus"; + this.tsl_ConnectionStatus.Size = new System.Drawing.Size(39, 17); + this.tsl_ConnectionStatus.Text = "Status"; + // + // cb_Map_AutoCenter + // + this.cb_Map_AutoCenter.AutoSize = true; + this.cb_Map_AutoCenter.Checked = global::AirScoutViewClient.Properties.Settings.Default.Map_AutoCenter; + this.cb_Map_AutoCenter.CheckState = System.Windows.Forms.CheckState.Checked; + this.cb_Map_AutoCenter.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::AirScoutViewClient.Properties.Settings.Default, "Map_AutoCenter", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.cb_Map_AutoCenter.Location = new System.Drawing.Point(12, 37); + this.cb_Map_AutoCenter.Name = "cb_Map_AutoCenter"; + this.cb_Map_AutoCenter.Size = new System.Drawing.Size(93, 17); + this.cb_Map_AutoCenter.TabIndex = 24; + this.cb_Map_AutoCenter.Text = "Auto Center"; + this.cb_Map_AutoCenter.UseVisualStyleBackColor = true; + // + // MapViewDlg + // + this.AcceptButton = this.btn_Map_PlayPause; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(836, 540); + this.Controls.Add(this.gb_Map); + this.Controls.Add(this.gb_Info); + this.Controls.Add(this.ss_Main); + this.Controls.Add(this.mnu_Main); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.MainMenuStrip = this.mnu_Main; + this.Name = "MapViewDlg"; + this.Text = "AirScoutViewClient"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MapViewDlg_FormClosing); + this.Load += new System.EventHandler(this.MapViewDlg_Load); + this.mnu_Main.ResumeLayout(false); + this.mnu_Main.PerformLayout(); + this.ss_Main.ResumeLayout(false); + this.ss_Main.PerformLayout(); + this.gb_Info.ResumeLayout(false); + this.gb_Info.PerformLayout(); + this.gb_Map.ResumeLayout(false); + this.spc_Main.Panel1.ResumeLayout(false); + this.spc_Main.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.spc_Main)).EndInit(); + this.spc_Main.ResumeLayout(false); + this.gb_Control.ResumeLayout(false); + this.gb_Map_Zoom.ResumeLayout(false); + this.pa_Map_Zoom.ResumeLayout(false); + this.pa_Map_Zoom.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.MenuStrip mnu_Main; + private System.Windows.Forms.ToolStripMenuItem tsi_Exit; + private System.Windows.Forms.ToolStripMenuItem tsi_Info; + private System.Windows.Forms.StatusStrip ss_Main; + private System.Windows.Forms.ToolStripStatusLabel tsl_Status; + private System.Windows.Forms.ToolStripMenuItem settingsToolStripMenuItem; + private System.Windows.Forms.GroupBox gb_Info; + private System.Windows.Forms.GroupBox gb_Map; + private System.Windows.Forms.Timer ti_Progress; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox tb_UTC; + private ScoutBase.Core.CallsignTextBox tb_MyCall; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label label4; + private ScoutBase.Core.CallsignTextBox tb_DXCall; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.TextBox tb_QTF; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.TextBox tb_QRB; + private System.Windows.Forms.Label label16; + private System.Windows.Forms.Label label8; + private System.Windows.Forms.ComboBox cb_Band; + private System.Windows.Forms.SplitContainer spc_Main; + private GMap.NET.WindowsForms.GMapControl gm_Main; + private System.Windows.Forms.Timer ti_Startup; + public System.Windows.Forms.ImageList il_Planes_H; + public System.Windows.Forms.ImageList il_Planes_L; + public System.Windows.Forms.ImageList il_Planes_S; + public System.Windows.Forms.ImageList il_Airports; + public System.Windows.Forms.ImageList il_Planes_M; + private System.Windows.Forms.GroupBox gb_Path; + private System.Windows.Forms.Timer ti_ShowLegends; + private ScoutBase.Core.LocatorComboBox cb_DXLoc; + private ScoutBase.Core.LocatorComboBox cb_MyLoc; + private System.Windows.Forms.GroupBox gb_Control; + private System.Windows.Forms.Button btn_Map_PlayPause; + private System.Windows.Forms.ImageList il_Main; + private System.Windows.Forms.GroupBox gb_Map_Zoom; + private System.Windows.Forms.Panel pa_Map_Zoom; + private System.Windows.Forms.CheckBox cb_Map_AutoCenter; + private System.Windows.Forms.TextBox tb_Map_Zoom; + private System.Windows.Forms.Button btn_Map_Zoom_Out; + private System.Windows.Forms.Button btn_Map_Zoom_In; + private System.Windows.Forms.ToolStripStatusLabel tsl_ConnectionStatus; + } +} + diff --git a/AirScoutViewClient/MapViewDlg.cs b/AirScoutViewClient/MapViewDlg.cs new file mode 100644 index 0000000..cadfccd --- /dev/null +++ b/AirScoutViewClient/MapViewDlg.cs @@ -0,0 +1,1727 @@ +// AirScout Aircraft Scatter Prediction +// Copyright (C) DL2ALF +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.Linq; +using System.Text; +using System.Windows; +using System.Windows.Forms; +using System.Windows.Forms.VisualStyles; +using GMap.NET; +using GMap.NET.MapProviders; +using GMap.NET.WindowsForms; +using GMap.NET.WindowsForms.Markers; +using GMap.NET.WindowsForms.ToolTips; +using System.IO; +using System.IO.Ports; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Reflection; +using System.Configuration; +using System.Diagnostics; +using AirScout; +using AirScout.Core; +using AirScout.Aircrafts; +using ScoutBase; +using ScoutBase.Core; +using ScoutBase.Elevation; +using ScoutBase.Stations; +using ScoutBase.Propagation; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using SQLiteDatabase; +using System.Xml; +using System.Xml.Serialization; +using System.Security.Cryptography; +using OxyPlot; +using OxyPlot.WindowsForms; +using OxyPlot.Series; +using OxyPlot.Axes; +using System.Data.SQLite; + +namespace AirScoutViewClient +{ + + public partial class MapViewDlg : Form + { + + // Map + // Overlays + GMapOverlay gmo_Routes = new GMapOverlay("Routes"); + GMapOverlay gmo_PropagationPaths = new GMapOverlay("PropagationPaths"); + GMapOverlay gmo_Objects = new GMapOverlay("Objects"); + GMapOverlay gmo_Planes = new GMapOverlay("Planes"); + GMapOverlay gmo_Airports = new GMapOverlay("Airports"); + GMapOverlay gmo_Callsigns = new GMapOverlay("Callsigns"); + GMapOverlay gmo_NearestPaths = new GMapOverlay("PropagationPaths"); + GMapOverlay gmo_NearestPlanes = new GMapOverlay("Planes"); + + // Routes + GMapRoute gmr_FullPath; + GMapRoute gmr_VisiblePpath; + GMapRoute gmr_NearestFull; + GMapRoute gmr_NearestVisible; + + // Markers + GMapMarker gmm_MyLoc; + GMapMarker gmm_DXLoc; + GMapMarker gmm_CurrentMarker; + bool isDraggingMarker; + + // Bitmap indices + private int bmindex_darkorange; + private int bmindex_lightgreen; + private int bmindex_red; + private int bmindex_gray; + private int bmindex_magenta; + + // charting + + // path chart + PlotModel pm_Path = new PlotModel(); + PlotView pv_Path = new PlotView(); + LinearAxis Path_X = new LinearAxis(); + LinearAxis Path_Y = new LinearAxis(); + AreaSeries Path_Elevation = new AreaSeries(); + LineSeries Min_H1 = new LineSeries(); + LineSeries Min_H2 = new LineSeries(); + LineSeries Max_H = new LineSeries(); + AreaSeries Min_H = new AreaSeries(); + LineSeries Planes_Hi = new LineSeries(); + LineSeries Planes_Lo = new LineSeries(); + + // elevation chart + PlotModel pm_Elevation = new PlotModel(); + PlotView pv_Elevation = new PlotView(); + LinearAxis Elevation_X = new LinearAxis(); + LinearAxis Elevation_Y = new LinearAxis(); + AreaSeries Elevation = new AreaSeries(); + LineSeries LOS = new LineSeries(); + + + // Tooltip font + public Font ToolTipFont; + + public VarConverter VC = new VarConverter(); + + public static LogWriter Log; + + public DateTime CurrentTime; + + AIRSCOUTPLAYMODE PlayMode = AIRSCOUTPLAYMODE.NONE; + + CultureInfo CurrentCulture = Application.CurrentCulture; + + ElevationPathDesignator EPath = null; + PropagationPathDesignator PPath = null; + + SortedList ActivePlanes = new SortedList(); + private List SelectedPlanes = new List(); + + private VIEWCLIENTSTATUS VieClientStatus = VIEWCLIENTSTATUS.NONE; + + [CategoryAttribute("Directories")] + [DescriptionAttribute("Application Directory")] + public string AppDirectory + { + get + { + return Application.StartupPath.TrimEnd(Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar); + } + } + + [CategoryAttribute("Directories")] + [DescriptionAttribute("Local Application Data Directory")] + public string AppDataDirectory + { + get + { + return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), Application.CompanyName, Application.ProductName).TrimEnd(Path.DirectorySeparatorChar); + } + } + + [CategoryAttribute("Directories")] + [DescriptionAttribute("Logfile Directory")] + public string LogDirectory + { + get + { + // get Property + string logdir = Properties.Settings.Default.Log_Directory; + // replace Windows/Linux directory spearator chars + logdir = logdir.Replace('\\', Path.DirectorySeparatorChar); + logdir = logdir.Replace('/', Path.DirectorySeparatorChar); + // set to default value if empty + if (String.IsNullOrEmpty(logdir)) + logdir = "Log"; + // replace variables, if any + logdir = VC.ReplaceAllVars(logdir); + // remove directory separator chars at begin and end + logdir = logdir.TrimStart(Path.DirectorySeparatorChar); + logdir = logdir.TrimEnd(Path.DirectorySeparatorChar); + // fully qualify path + if (!logdir.Contains(Path.VolumeSeparatorChar)) + logdir = Path.Combine(AppDataDirectory, logdir); + return logdir; + } + } + + [CategoryAttribute("Directories")] + [DescriptionAttribute("Tempfile Directory")] + public string TmpDirectory + { + get + { + // get Property + string tmpdir = Properties.Settings.Default.Tmp_Directory; + // replace Windows/Linux directory spearator chars + tmpdir = tmpdir.Replace('\\', Path.DirectorySeparatorChar); + tmpdir = tmpdir.Replace('/', Path.DirectorySeparatorChar); + // set to default value if empty + if (String.IsNullOrEmpty(tmpdir)) + tmpdir = "Tmp"; + // replace variables, if any + tmpdir = VC.ReplaceAllVars(tmpdir); + // remove directory separator chars at begin and end + tmpdir = tmpdir.TrimStart(Path.DirectorySeparatorChar); + tmpdir = tmpdir.TrimEnd(Path.DirectorySeparatorChar); + // fully qualify path + if (!tmpdir.Contains(Path.VolumeSeparatorChar)) + tmpdir = Path.Combine(AppDataDirectory, tmpdir); + return tmpdir; + } + } + + public MapViewDlg() + { + Application.CurrentCulture = CultureInfo.InvariantCulture; + // gets an instance of a LogWriter + Log = LogWriter.Instance; + InitializeComponent(); + + } + + private void Say (string msg) + { + if (tsl_Status.Text != msg) + { + tsl_Status.Text = msg; + ss_Main.Refresh(); + } + } + + #region Initialization + + private void InitializeSettings() + { + Say("Getting settings..."); + if (!SettingsFromJSON()) + return; + // initialize map + // setting User Agent to fix Open Street Map issue 2016-09-20 + GMap.NET.MapProviders.GMapProvider.UserAgent = "AirScout"; + // set initial settings for main map + gm_Main.MapProvider = GMapProviders.Find(Properties.Settings.Default.Map_Provider); + gm_Main.IgnoreMarkerOnMouseWheel = true; + gm_Main.MinZoom = 0; + gm_Main.MaxZoom = 20; + gm_Main.Zoom = 6; + gm_Main.DragButton = System.Windows.Forms.MouseButtons.Left; + gm_Main.CanDragMap = true; + gm_Main.ScalePen = new Pen(Color.Black, 3); + gm_Main.MapScaleInfoEnabled = true; + gm_Main.Overlays.Add(gmo_Airports); + gm_Main.Overlays.Add(gmo_Callsigns); + gm_Main.Overlays.Add(gmo_PropagationPaths); + gm_Main.Overlays.Add(gmo_Routes); + gm_Main.Overlays.Add(gmo_Objects); + gm_Main.Overlays.Add(gmo_Planes); + // center map + gm_Main.Position = new PointLatLng(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon); + // intially fill dialog box elements and set band + string[] bands = Bands.GetStringValuesExceptNoneAndAll(); + foreach (string b in bands) + cb_Band.Items.Add(b); + BAND band = Properties.Settings.Default.Band; + cb_Band.SelectedItem = Bands.GetStringValue(band); + PlayMode = AIRSCOUTPLAYMODE.PAUSE; + VieClientStatus = VIEWCLIENTSTATUS.CONNECTED; + UpdateStatus(); + Say(""); + } + + private Bitmap CreatePlaneIcon(Color color) + { + // get the basic icon + Bitmap bm = new Bitmap(AppDirectory + Path.DirectorySeparatorChar + Properties.Settings.Default.Planes_IconFileName); + // read the content and change color of each pixel + for (int j = 0; j < bm.Width; j++) + { + for (int k = 0; k < bm.Height; k++) + { + // get the color of each pixel + Color c = bm.GetPixel(j, k); + // check if not transparent + if (c.A > 0) + { + // change color + bm.SetPixel(j, k, Color.FromArgb(c.A, color.R, color.G, color.B)); + } + } + } + return bm; + } + + public static Color ColorFromHSV(double hue, double saturation, double value) + { + int hi = Convert.ToInt32(Math.Floor(hue / 60)) % 6; + double f = hue / 60 - Math.Floor(hue / 60); + + value = value * 255; + int v = Convert.ToInt32(value); + int p = Convert.ToInt32(value * (1 - saturation)); + int q = Convert.ToInt32(value * (1 - f * saturation)); + int t = Convert.ToInt32(value * (1 - (1 - f) * saturation)); + + if (hi == 0) + return Color.FromArgb(255, v, t, p); + else if (hi == 1) + return Color.FromArgb(255, q, v, p); + else if (hi == 2) + return Color.FromArgb(255, p, v, t); + else if (hi == 3) + return Color.FromArgb(255, p, q, v); + else if (hi == 4) + return Color.FromArgb(255, t, p, v); + else + return Color.FromArgb(255, v, p, q); + } + + public Color GetColor(double power) + { + double H = power * 0.3; // Hue (note 0.4 = Green, see huge chart below) + double S = 0.95; // Saturation + double B = 0.95; // Brightness + + return ColorFromHSV((float)H * 360, (float)S, (float)B); + } + + private Bitmap CreateAirportIcon(int alpha) + { + // get the basic icon + Bitmap bm = new Bitmap(AppDirectory + Path.DirectorySeparatorChar + Properties.Settings.Default.Airports_IconFileName); + // read the content and change opacity of each pixel + for (int j = 0; j < bm.Width; j++) + { + for (int k = 0; k < bm.Height; k++) + { + // get the color of each pixel + Color c = bm.GetPixel(j, k); + // check if not transparent + if (c.A > 0) + { + // change color + bm.SetPixel(j, k, Color.FromArgb(alpha, c.R, c.G, c.B)); + } + } + } + return bm; + } + + + private void InitializeIcons() + { + // create extra icons regular size + Log.WriteMessage("Started."); + try + { + // now generate 0% - 100% colored planes + for (int i = 0; i <= 100; i++) + { + Bitmap bm = CreatePlaneIcon(GetColor(1.0f - (float)i / 100.0f)); + il_Planes_L.Images.Add(bm); + il_Planes_M.Images.Add(bm); + il_Planes_H.Images.Add(bm); + il_Planes_S.Images.Add(bm); + } + il_Planes_L.Images.Add(CreatePlaneIcon(Color.Gray)); + il_Planes_M.Images.Add(CreatePlaneIcon(Color.Gray)); + il_Planes_H.Images.Add(CreatePlaneIcon(Color.Gray)); + il_Planes_S.Images.Add(CreatePlaneIcon(Color.Gray)); + bmindex_gray = il_Planes_M.Images.Count - 1; + il_Planes_L.Images.Add(CreatePlaneIcon(Color.LightGreen)); + il_Planes_M.Images.Add(CreatePlaneIcon(Color.LightGreen)); + il_Planes_H.Images.Add(CreatePlaneIcon(Color.LightGreen)); + il_Planes_S.Images.Add(CreatePlaneIcon(Color.LightGreen)); + bmindex_lightgreen = il_Planes_M.Images.Count - 1; + il_Planes_L.Images.Add(CreatePlaneIcon(Color.DarkOrange)); + il_Planes_M.Images.Add(CreatePlaneIcon(Color.DarkOrange)); + il_Planes_H.Images.Add(CreatePlaneIcon(Color.DarkOrange)); + il_Planes_S.Images.Add(CreatePlaneIcon(Color.DarkOrange)); + bmindex_darkorange = il_Planes_M.Images.Count - 1; + il_Planes_L.Images.Add(CreatePlaneIcon(Color.Red)); + il_Planes_M.Images.Add(CreatePlaneIcon(Color.Red)); + il_Planes_H.Images.Add(CreatePlaneIcon(Color.Red)); + il_Planes_S.Images.Add(CreatePlaneIcon(Color.Red)); + bmindex_red = il_Planes_M.Images.Count - 1; + il_Planes_L.Images.Add(CreatePlaneIcon(Color.Magenta)); + il_Planes_M.Images.Add(CreatePlaneIcon(Color.Magenta)); + il_Planes_H.Images.Add(CreatePlaneIcon(Color.Magenta)); + il_Planes_S.Images.Add(CreatePlaneIcon(Color.Magenta)); + bmindex_magenta = il_Planes_M.Images.Count - 1; + il_Airports.Images.Add(CreateAirportIcon(255)); + } + catch (Exception ex) + { + Log.WriteMessage(ex.Message); + } + Log.WriteMessage("Finished."); + } + + private void InitializeCharts() + { + // propagation path chart + pm_Path.Title = String.Empty; + pm_Path.DefaultFontSize = 6F; + pm_Path.IsLegendVisible = false; + pv_Path.BackColor = Color.White; + pv_Path.Model = pm_Path; + // add axes + pm_Path.Axes.Clear(); + // add X-axis + Path_X.IsZoomEnabled = false; + Path_X.Maximum = 1000; + Path_X.Minimum = 0; + Path_X.MajorGridlineStyle = LineStyle.Solid; + Path_X.MinorGridlineStyle = LineStyle.Dot; + Path_X.Position = AxisPosition.Bottom; + this.pm_Path.Axes.Add(Path_X); + // add Y-axis + Path_Y.IsZoomEnabled = false; + Path_Y.Maximum = 20000; + Path_Y.Minimum = 0; + Path_Y.MajorGridlineStyle = LineStyle.Solid; + Path_Y.MinorGridlineStyle = LineStyle.Dot; + Path_Y.Position = AxisPosition.Left; + this.pm_Path.Axes.Add(Path_Y); + // add series + pm_Path.Series.Clear(); + Path_Elevation.Title = "Elevation"; + Min_H1.Title = "Min_H1"; + Min_H1.StrokeThickness = 2; + Min_H1.LineStyle = LineStyle.Solid; + Min_H1.Color = OxyColors.Red; + Min_H2.Title = "Min_H2"; + Min_H2.StrokeThickness = 2; + Min_H2.LineStyle = LineStyle.Solid; + Min_H2.Color = OxyColors.Gold; + Max_H.Title = "Max_H"; + Max_H.StrokeThickness = 2; + Max_H.LineStyle = LineStyle.Dot; + Max_H.Color = OxyColors.DarkBlue; + Min_H.Title = "Min_H"; + Min_H.StrokeThickness = 0; + Min_H.LineStyle = LineStyle.Solid; + Min_H.Color = OxyColors.Magenta.ChangeSaturation(0.4); + Planes_Hi.Title = "Planes_Hi"; + Planes_Hi.Color = OxyColors.Transparent; + Planes_Hi.MarkerType = MarkerType.Square; + Planes_Hi.MarkerFill = OxyColors.Magenta; + Planes_Lo.Title = "Planes_Lo"; + Planes_Lo.Color = OxyColors.Transparent; + Planes_Lo.MarkerType = MarkerType.Square; + Planes_Lo.MarkerFill = OxyColors.Gray; + pm_Path.Series.Add(Path_Elevation); + pm_Path.Series.Add(Min_H1); + pm_Path.Series.Add(Min_H2); + pm_Path.Series.Add(Max_H); + pm_Path.Series.Add(Min_H); + pm_Path.Series.Add(Planes_Hi); + pm_Path.Series.Add(Planes_Lo); + // add legend + pm_Path.LegendTitle = ""; + pm_Path.LegendPosition = LegendPosition.TopRight; + pm_Path.LegendBackground = OxyColors.White; + pm_Path.LegendBorder = OxyColors.Black; + pm_Path.LegendBorderThickness = 1; + // add control + this.gb_Path.Controls.Add(pv_Path); + pv_Path.Paint += new PaintEventHandler(pv_Path_Paint); + + // zoomed elevation chart + pm_Elevation.Title = String.Empty; + pm_Elevation.DefaultFontSize = 6F; + pm_Elevation.IsLegendVisible = false; + pv_Elevation.BackColor = Color.White; + pv_Elevation.Model = pm_Elevation; + // add series + pm_Elevation.Series.Clear(); + Elevation.Title = "Elevation"; + LOS.Title = "LOS"; + LOS.StrokeThickness = 2; + LOS.Color = OxyColors.Black; + pm_Elevation.Series.Add(Elevation); + pm_Elevation.Series.Add(LOS); + // create axes + pm_Elevation.Axes.Clear(); + // add X-axis + Elevation_X.IsZoomEnabled = false; + Elevation_X.Maximum = 1000; + Elevation_X.Minimum = 0; + Elevation_X.MajorGridlineStyle = LineStyle.Solid; + Elevation_X.MinorGridlineStyle = LineStyle.Dot; + Elevation_X.Position = AxisPosition.Bottom; + this.pm_Elevation.Axes.Add(Elevation_X); + // add Y-axis + Elevation_Y.IsZoomEnabled = false; + // auto size maximum + // Elevation_Y.Maximum = maxelv, + Elevation_Y.Minimum = 0; + Elevation_Y.MajorGridlineStyle = LineStyle.Solid; + Elevation_Y.MinorGridlineStyle = LineStyle.Dot; + Elevation_Y.Position = AxisPosition.Left; + this.pm_Elevation.Axes.Add(Elevation_Y); + // add legend + pm_Elevation.LegendTitle = ""; + pm_Elevation.LegendPosition = LegendPosition.TopRight; + pm_Elevation.LegendBackground = OxyColors.White; + pm_Elevation.LegendBorder = OxyColors.Black; + pm_Elevation.LegendBorderThickness = 1; + // add control + this.gb_Path.Controls.Add(pv_Elevation); + gb_Path_Resize(this, null); + } + + #endregion + + private void UpdateStatus() + { + // upddate TextBoxes + tb_UTC.Text = CurrentTime.ToString("yyyy-MM-dd HH:mm:ss"); + tb_UTC.BackColor = Color.LightSalmon; + string call = Properties.Settings.Default.MyCall; + tb_MyCall.SilentText = Properties.Settings.Default.MyCall; + cb_MyLoc.SilentText = MaidenheadLocator.LocFromLatLon(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, + Properties.Settings.Default.Locator_SmallLettersForSubsquares, + (int)Properties.Settings.Default.Locator_MaxLength / 2, + Properties.Settings.Default.Locator_AutoLength); + tb_DXCall.Text = Properties.Settings.Default.DXCall; + cb_DXLoc.SilentText = MaidenheadLocator.LocFromLatLon(Properties.Settings.Default.DXLat, + Properties.Settings.Default.DXLon, + Properties.Settings.Default.Locator_SmallLettersForSubsquares, + (int)Properties.Settings.Default.Locator_MaxLength / 2, + Properties.Settings.Default.Locator_AutoLength); + if (MaidenheadLocator.Check(cb_MyLoc.Text) && MaidenheadLocator.Check(cb_DXLoc.Text)) + { + tb_QTF.Text = Math.Round(LatLon.Bearing(Properties.Settings.Default.MyLat, + Properties.Settings.Default.MyLon, + Properties.Settings.Default.DXLat, + Properties.Settings.Default.DXLon)).ToString("F0"); + tb_QRB.Text = Math.Round(LatLon.Distance(Properties.Settings.Default.MyLat, + Properties.Settings.Default.MyLon, + Properties.Settings.Default.DXLat, + Properties.Settings.Default.DXLon)).ToString("F0"); + } + else + { + tb_QRB.Text = "0"; + tb_QTF.Text = "0"; + } + // colour Textbox if more precise lat/lon information is available + if (MaidenheadLocator.IsPrecise(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, 3)) + { + cb_MyLoc.BackColor = Color.PaleGreen; + } + else + { + cb_MyLoc.BackColor = Color.FloralWhite; + } + // colour Textbox if more precise lat/lon information is available + if (MaidenheadLocator.IsPrecise(Properties.Settings.Default.DXLat, Properties.Settings.Default.DXLon, 3)) + { + cb_DXLoc.BackColor = Color.PaleGreen; + } + else + { + cb_DXLoc.BackColor = Color.FloralWhite; + } + cb_Band.SelectedItem = Bands.GetStringValue(Properties.Settings.Default.Band); + } + + private string GetServerURL(string server, int port, string filename, string paramstr = "") + { + if (!server.StartsWith("http://")) + server = "http://" + server; + server = server.TrimEnd('/'); + return (String.IsNullOrEmpty(paramstr)) ? server + ":" + port.ToString() + "/" + filename : server + ":" + port.ToString() + "/" + filename + "?" + paramstr; + } + + private string GetJSONFromURL(string url) + { + // reads JSON from webserver + // returns an empty string or null if webserver is not responding + // returns error message from webserver in case of error + // returns JSON string if successful + string json = ""; + // calculate url and get json + // replace all vaiables, if any + url = VC.ReplaceAllVars(url); + try + { + Console.WriteLine("Creating web request: " + url); + HttpWebRequest webrequest = (HttpWebRequest)HttpWebRequest.Create(url); + webrequest.Referer = ""; + webrequest.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:37.0) Gecko/20100101 Firefox/37.0"; + webrequest.Accept = "application/json, text/javascript, */*;q=0.01"; + webrequest.AutomaticDecompression = System.Net.DecompressionMethods.Deflate | System.Net.DecompressionMethods.GZip; + Console.WriteLine("Getting web response"); + HttpWebResponse webresponse = (HttpWebResponse)webrequest.GetResponse(); + Console.WriteLine("Reading stream"); + using (StreamReader sr = new StreamReader(webresponse.GetResponseStream())) + { + json = sr.ReadToEnd(); + } + } + catch (Exception ex) + { + Say(ex.Message); + Log.WriteMessage(ex.ToString()); + // do nothing + } + return json; + } + + public T PropertyFromJSON(string propertyName, string json) + { + var allobjects = JObject.Parse(json); + JToken jo = allobjects[propertyName]; + return (T)jo.ToObject(typeof(T)); + } + + private bool SettingsFromJSON() + { + try + { + string json = ""; + // get settings + json = GetJSONFromURL(GetServerURL(Properties.Settings.Default.Server_URL, Properties.Settings.Default.Server_Port, "settings.json")); + if (String.IsNullOrEmpty(json)) + { + VieClientStatus = VIEWCLIENTSTATUS.CONNECTING; + return false; + } + if (json.StartsWith("Error:")) + { + Say(json); + return false; + } + // copy settings + Properties.Settings.Default.Path_Band_Settings = PropertyFromJSON("Path_Band_Settings", json); + Properties.Settings.Default.Map_Provider = PropertyFromJSON("Map_Provider", json); + Properties.Settings.Default.MinLat = PropertyFromJSON("MinLat", json); + Properties.Settings.Default.MinLon = PropertyFromJSON("MinLon", json); + Properties.Settings.Default.MaxLat = PropertyFromJSON("MaxLat", json); + Properties.Settings.Default.MaxLon = PropertyFromJSON("MaxLon", json); + Properties.Settings.Default.MyCall = PropertyFromJSON("MyCall", json); + Properties.Settings.Default.MyLat = PropertyFromJSON("MyLat", json); + Properties.Settings.Default.MyLon = PropertyFromJSON("MyLon", json); + // copy DXCall only if empty + if (String.IsNullOrEmpty(Properties.Settings.Default.DXCall)) + { + Properties.Settings.Default.DXCall = PropertyFromJSON("DXCall", json); + Properties.Settings.Default.DXLat = PropertyFromJSON("DXLat", json); + Properties.Settings.Default.DXLon = PropertyFromJSON("DXLon", json); + } + // set band only if NONE + if (Properties.Settings.Default.Band != BAND.BNONE) + Properties.Settings.Default.Band = PropertyFromJSON("Band", json); + Properties.Settings.Default.InfoWin_Position = PropertyFromJSON("InfoWin_Position", json); + Properties.Settings.Default.InfoWin_Metric = PropertyFromJSON("InfoWin_Metric", json); + Properties.Settings.Default.InfoWin_Track = PropertyFromJSON("InfoWin_Track", json); + Properties.Settings.Default.InfoWin_Speed = PropertyFromJSON("InfoWin_Speed", json); + Properties.Settings.Default.InfoWin_Alt = PropertyFromJSON("InfoWin_Alt", json); + Properties.Settings.Default.InfoWin_Type = PropertyFromJSON("InfoWin_Type", json); + Properties.Settings.Default.InfoWin_Time = PropertyFromJSON("InfoWin_Time", json); + Properties.Settings.Default.InfoWin_Dist = PropertyFromJSON("InfoWin_Dist", json); + Properties.Settings.Default.InfoWin_Angle = PropertyFromJSON("InfoWin_Angle", json); + Properties.Settings.Default.InfoWin_Epsilon = PropertyFromJSON("InfoWin_Epsilon", json); + Properties.Settings.Default.InfoWin_Squint = PropertyFromJSON("InfoWin_Squint", json); + Properties.Settings.Default.Locator_AutoLength = PropertyFromJSON("Locator_AutoLength", json); + Properties.Settings.Default.Locator_SmallLettersForSubsquares = PropertyFromJSON("Locator_SmallLettersForSubsquares", json); + Properties.Settings.Default.Locator_MaxLength = PropertyFromJSON("Locator_MaxLength", json); + } + catch (Exception ex) + { + Say(ex.Message); + Log.WriteMessage(ex.ToString()); + } + return true; + } + + private List MyLocationsFromJSON() + { + string json = ""; + List l = null; + try + { + string url = GetServerURL( + Properties.Settings.Default.Server_URL, + Properties.Settings.Default.Server_Port, + "location.json", + "call=" + Properties.Settings.Default.MyCall + + "&loc=all"); + json = GetJSONFromURL(url); + if (String.IsNullOrEmpty(json)) + { + VieClientStatus = VIEWCLIENTSTATUS.CONNECTING; + return null; + } + if (json.StartsWith("Error:")) + { + Say(json); + return null; + } + JsonSerializerSettings settings = new JsonSerializerSettings(); + settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; + settings.FloatFormatHandling = FloatFormatHandling.String; + settings.Formatting = Newtonsoft.Json.Formatting.Indented; + settings.Culture = CultureInfo.InvariantCulture; + l = JsonConvert.DeserializeObject>(json, settings); + } + catch (Exception ex) + { + Say(ex.Message); + Log.WriteMessage(ex.ToString()); + } + return l; + } + + private LocationDesignator MyLocationFromJSON() + { + string json = ""; + LocationDesignator ld = null; + try + { + string url = GetServerURL( + Properties.Settings.Default.Server_URL, + Properties.Settings.Default.Server_Port, + "location.json", + "call=" + Properties.Settings.Default.MyCall + + "&loc=" + MaidenheadLocator.LocFromLatLon(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, false, 3)); + json = GetJSONFromURL(url); + if (String.IsNullOrEmpty(json)) + { + VieClientStatus = VIEWCLIENTSTATUS.CONNECTING; + return null; + } + if (json.StartsWith("Error:")) + { + Say(json); + return null; + } + ld = LocationDesignator.FromJSON(json); + } + catch (Exception ex) + { + Say(ex.Message); + Log.WriteMessage(ex.ToString()); + } + return ld; + } + + private List DXLocationsFromJSON() + { + string json = ""; + List l = null; + try + { + string url = GetServerURL( + Properties.Settings.Default.Server_URL, + Properties.Settings.Default.Server_Port, + "location.json", + "call=" + Properties.Settings.Default.DXCall + + "&loc=all"); + json = GetJSONFromURL(url); + if (String.IsNullOrEmpty(json)) + { + VieClientStatus = VIEWCLIENTSTATUS.CONNECTING; + return null; + } + if (json.StartsWith("Error:")) + { + Say(json); + return null; + } + JsonSerializerSettings settings = new JsonSerializerSettings(); + settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; + settings.FloatFormatHandling = FloatFormatHandling.String; + settings.Formatting = Newtonsoft.Json.Formatting.Indented; + settings.Culture = CultureInfo.InvariantCulture; + l = JsonConvert.DeserializeObject>(json, settings); + } + catch (Exception ex) + { + Say(ex.Message); + Log.WriteMessage(ex.ToString()); + } + return l; + } + + private LocationDesignator DXLocationFromJSON() + { + string json = ""; + LocationDesignator ld = null; + try + { + string url = GetServerURL( + Properties.Settings.Default.Server_URL, + Properties.Settings.Default.Server_Port, + "location.json", + "&call=" + Properties.Settings.Default.DXCall + + "&loc=" + MaidenheadLocator.LocFromLatLon(Properties.Settings.Default.DXLat, Properties.Settings.Default.DXLon, false, 3)); + json = GetJSONFromURL(url); + if (String.IsNullOrEmpty(json)) + { + VieClientStatus = VIEWCLIENTSTATUS.CONNECTING; + return null; + } + if (json.StartsWith("Error:")) + { + Say(json); + return null; + } + ld = LocationDesignator.FromJSON(json); + } + catch (Exception ex) + { + Say(ex.Message); + Log.WriteMessage(ex.ToString()); + } + return ld; + } + + private ElevationPathDesignator ElevationPathFromJSON() + { + string json = ""; + try + { + json = GetJSONFromURL(GetServerURL( + Properties.Settings.Default.Server_URL, + Properties.Settings.Default.Server_Port, + "elevationpath.json", + "mycall=" + Properties.Settings.Default.MyCall + + "&myloc=" + MaidenheadLocator.LocFromLatLon(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, false, 3) + + "&dxcall=" + Properties.Settings.Default.DXCall + + "&dxloc=" + MaidenheadLocator.LocFromLatLon(Properties.Settings.Default.DXLat, Properties.Settings.Default.DXLon, false, 3))); + if (String.IsNullOrEmpty(json)) + { + VieClientStatus = VIEWCLIENTSTATUS.CONNECTING; + return null; + } + if (json.StartsWith("Error:")) + { + Say(json); + return null; + } + EPath = ElevationPathDesignator.FromJSON(json); + } + catch (Exception ex) + { + Say(ex.Message); + Log.WriteMessage(ex.ToString()); + } + return EPath; + } + + private PropagationPathDesignator PropagationPathFromJSON() + { + string json = ""; + try + { + json = GetJSONFromURL(GetServerURL( + Properties.Settings.Default.Server_URL, + Properties.Settings.Default.Server_Port, + "propagationpath.json", + "mycall=" + Properties.Settings.Default.MyCall + + "&myloc=" + MaidenheadLocator.LocFromLatLon(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, false, 3) + + "&dxcall=" + Properties.Settings.Default.DXCall + + "&dxloc=" + MaidenheadLocator.LocFromLatLon(Properties.Settings.Default.DXLat, Properties.Settings.Default.DXLon, false, 3))); + if (String.IsNullOrEmpty(json)) + { + VieClientStatus = VIEWCLIENTSTATUS.CONNECTING; + return null; + } + if (json.StartsWith("Error:")) + { + Say(json); + return null; + } + PPath = PropagationPathDesignator.FromJSON(json); + } + catch (Exception ex) + { + Say(ex.Message); + Log.WriteMessage(ex.ToString()); + } + return PPath; + } + + private List NearestPlanesFromJSON() + { + string json = ""; + List l = null; + try + { + json = GetJSONFromURL(GetServerURL( + Properties.Settings.Default.Server_URL, + Properties.Settings.Default.Server_Port, + "nearestplanes.json", + "mycall=" + Properties.Settings.Default.MyCall + + "&myloc=" + MaidenheadLocator.LocFromLatLon(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, false, 3) + + "&dxcall=" + Properties.Settings.Default.DXCall + + "&dxloc=" + MaidenheadLocator.LocFromLatLon(Properties.Settings.Default.DXLat, Properties.Settings.Default.DXLon, false, 3))); + if (String.IsNullOrEmpty(json)) + { + VieClientStatus = VIEWCLIENTSTATUS.CONNECTING; + return null; + } + if (json.StartsWith("Error:")) + { + Say(json); + return null; + } + JsonSerializerSettings settings = new JsonSerializerSettings(); + settings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; + settings.FloatFormatHandling = FloatFormatHandling.String; + settings.Formatting = Newtonsoft.Json.Formatting.Indented; + settings.Culture = CultureInfo.InvariantCulture; + l = (List)JsonConvert.DeserializeObject>(json, settings); + } + catch (Exception ex) + { + Say(ex.Message); + Log.WriteMessage(ex.ToString()); + } + return l; + } + + private static Bitmap RotateImageByAngle(System.Drawing.Image oldBitmap, float angle) + { + var newBitmap = new Bitmap(oldBitmap.Width, oldBitmap.Height); + var graphics = Graphics.FromImage(newBitmap); + graphics.TranslateTransform((float)oldBitmap.Width / 2, (float)oldBitmap.Height / 2); + graphics.RotateTransform(angle); + graphics.TranslateTransform(-(float)oldBitmap.Width / 2, -(float)oldBitmap.Height / 2); + graphics.DrawImage(oldBitmap, new System.Drawing.Point(0, 0)); + return newBitmap; + } + + private GMarkerGoogle CreatePlaneSimple(PlaneInfo info, bool selected) + { + // return on empty info + if (info == null) + return null; + // show flight info only + // get bitmap according to category + Bitmap bm; + int bmindex = bmindex_gray; + Brush brush = new SolidBrush(Color.FromArgb(180, Color.White)); + if (info.Potential == 100) + { + bmindex = bmindex_magenta; + brush = new SolidBrush(Color.FromArgb(150, Color.Plum)); + } + else if (info.Potential == 75) + { + bmindex = bmindex_red; + brush = new SolidBrush(Color.FromArgb(150, Color.Red)); + } + else if (info.Potential == 50) + { + bmindex = bmindex_darkorange; + brush = new SolidBrush(Color.FromArgb(150, Color.DarkOrange)); + } + if (info.Category == PLANECATEGORY.SUPERHEAVY) + bm = new Bitmap(il_Planes_S.Images[bmindex]); + else if (info.Category == PLANECATEGORY.HEAVY) + bm = new Bitmap(il_Planes_H.Images[bmindex]); + else if (info.Category == PLANECATEGORY.MEDIUM) + bm = new Bitmap(il_Planes_M.Images[bmindex]); + else if (info.Category == PLANECATEGORY.LIGHT) + bm = new Bitmap(il_Planes_L.Images[bmindex]); + else + bm = new Bitmap(il_Planes_M.Images[bmindex]); + GMarkerGoogle m = new GMarkerGoogle(new PointLatLng(info.Lat, info.Lon), ToolTipFont, RotateImageByAngle(bm, (float)info.Track)); + m.Tag = info.Call; + string lat = ""; + if (info.Lat >= 0) + lat = Math.Abs(info.Lat).ToString("00.00") + "°N"; + else + lat = Math.Abs(info.Lat).ToString("00.00") + "°S"; + string lon = ""; + if (info.Lon >= 0) + lon = Math.Abs(info.Lon).ToString("000.00") + "°E"; + else + lon = Math.Abs(info.Lon).ToString("000.00") + "°W"; + m.ToolTipText = info.Call + "\n--------------------"; + if (Properties.Settings.Default.InfoWin_Position) + m.ToolTipText += "\nPos: " + lat + " , " + lon; + if (Properties.Settings.Default.InfoWin_Alt) + { + if (Properties.Settings.Default.InfoWin_Metric) + m.ToolTipText += "\nAlt: " + (int)info.Alt_m + "m"; + else + m.ToolTipText += "\nAlt: " + (int)info.Alt + "ft"; + } + if (Properties.Settings.Default.InfoWin_Track) + m.ToolTipText += "\nTrack: " + (int)info.Track + "°"; + if (Properties.Settings.Default.InfoWin_Type) + m.ToolTipText += "\nType: " + info.Manufacturer + " " + info.Model + " [" + PlaneCategories.GetShortStringValue(info.Category) + "]"; + // set tooltip on if hot + if (selected) + m.ToolTipMode = MarkerTooltipMode.Always; + else + m.ToolTipMode = MarkerTooltipMode.OnMouseOver; + if (m.ToolTip != null) + m.ToolTip.Fill = brush; + return m; + } + + private GMarkerGoogle CreatePlaneDetailed(PlaneInfo info, bool selected) + { + // return on empty info + if (info == null) + return null; + // get bitmap according to category + Bitmap bm; + int bmindex = bmindex_gray; + Brush brush = new SolidBrush(Color.FromArgb(180, Color.White)); + if (info.Potential == 100) + { + bmindex = bmindex_magenta; + brush = new SolidBrush(Color.FromArgb(150, Color.Plum)); + } + else if (info.Potential == 75) + { + bmindex = bmindex_red; + brush = new SolidBrush(Color.FromArgb(150, Color.Red)); + } + else if (info.Potential == 50) + { + bmindex = bmindex_darkorange; + brush = new SolidBrush(Color.FromArgb(150, Color.DarkOrange)); + } + if (info.Category == PLANECATEGORY.SUPERHEAVY) + bm = new Bitmap(il_Planes_S.Images[bmindex]); + else if (info.Category == PLANECATEGORY.HEAVY) + bm = new Bitmap(il_Planes_H.Images[bmindex]); + else if (info.Category == PLANECATEGORY.MEDIUM) + bm = new Bitmap(il_Planes_M.Images[bmindex]); + else if (info.Category == PLANECATEGORY.LIGHT) + bm = new Bitmap(il_Planes_L.Images[bmindex]); + else + bm = new Bitmap(il_Planes_M.Images[bmindex]); + GMarkerGoogle m = new GMarkerGoogle(new PointLatLng(info.Lat, info.Lon), ToolTipFont, RotateImageByAngle(bm, (float)info.Track)); + m.Tag = info.Call; + string lat = ""; + if (info.Lat >= 0) + lat = Math.Abs(info.Lat).ToString("00.00") + "°N"; + else + lat = Math.Abs(info.Lat).ToString("00.00") + "°S"; + string lon = ""; + if (info.Lon >= 0) + lon = Math.Abs(info.Lon).ToString("000.00") + "°E"; + else + lon = Math.Abs(info.Lon).ToString("000.00") + "°W"; + int mins = 0; + if (info.Speed > 0) + mins = (int)(info.IntQRB / UnitConverter.kts_kmh(info.Speed) * 60.0); + // fill tooltip texts + m.ToolTipText = info.Call + "\n--------------------"; + if (Properties.Settings.Default.InfoWin_Position) + m.ToolTipText += "\nPos: " + lat + " , " + lon; + if (Properties.Settings.Default.InfoWin_Alt) + { + if (Properties.Settings.Default.InfoWin_Metric) + m.ToolTipText += "\nAlt: " + (int)info.Alt_m + "m [" + info.AltDiff.ToString("+#;-#;0") + "m]"; + else + m.ToolTipText += "\nAlt: " + (int)info.Alt + "ft [" + UnitConverter.m_ft(info.AltDiff).ToString("+#;-#;0") + "ft]"; + } + if (Properties.Settings.Default.InfoWin_Track) + m.ToolTipText += "\nTrack: " + info.Track + "°"; + if (Properties.Settings.Default.InfoWin_Speed) + { + if (Properties.Settings.Default.InfoWin_Metric) + m.ToolTipText += "\nSpeed: " + info.Speed_kmh.ToString("F0") + "km/h"; + else + m.ToolTipText += "\nSpeed: " + info.Speed.ToString("F0") + "kts"; + } + if (Properties.Settings.Default.InfoWin_Type) + m.ToolTipText += "\nType: " + info.Manufacturer + " " + info.Model + " [" + PlaneCategories.GetShortStringValue(info.Category) + "]"; + if (Properties.Settings.Default.InfoWin_Dist) + { + if (Properties.Settings.Default.InfoWin_Metric) + m.ToolTipText += "\nDist: " + info.IntQRB.ToString("F0") + "km"; + else + m.ToolTipText += "\nDist: " + UnitConverter.km_mi(info.IntQRB).ToString("F0") + "mi"; + } + if (Properties.Settings.Default.InfoWin_Time) + m.ToolTipText += "\nTime: " + (CurrentTime + new TimeSpan(0, mins, 0)).ToString("HH:mm") + " [ " + mins.ToString("") + "min]"; + if (Properties.Settings.Default.InfoWin_Angle) + m.ToolTipText += "\nAngle: " + (info.Angle / Math.PI * 180.0).ToString("F0") + "°"; + if (Properties.Settings.Default.InfoWin_Epsilon) + m.ToolTipText += "\nEps: " + (info.Eps1 / Math.PI * 180.0).ToString("00.00") + "° <> " + (info.Eps2 / Math.PI * 180.0).ToString("00.00") + "°"; + if (Properties.Settings.Default.InfoWin_Squint) + m.ToolTipText += "\nSquint: " + (info.Squint / Math.PI * 180).ToString("00.00") + "°"; + if (selected) + { + m.ToolTipMode = MarkerTooltipMode.Always; + } + else + { + m.ToolTipMode = MarkerTooltipMode.OnMouseOver; + } + if (m.ToolTip != null) + m.ToolTip.Fill = brush; + return m; + } + + private void DrawPlanes() + { + bool isselected = false; + + if (EPath == null) + return; + if (PPath == null) + return; + + // check for filter settings + // and color filter box + int planes_filter_minalt = 0; + planes_filter_minalt = Properties.Settings.Default.Planes_Filter_Min_Alt; + if (planes_filter_minalt < 0) + planes_filter_minalt = 0; + Stopwatch st = new Stopwatch(); + st.Start(); + + // clear planes overlay in map + gmo_Planes.Clear(); + // clear all routes except paths + gmo_Routes.Clear(); + // clear active planes + ActivePlanes.Clear(); + + // clear data points in chart + Planes_Hi.Points.Clear(); + Planes_Lo.Points.Clear(); + + // get nearest planes per path + List nearestplanes = NearestPlanesFromJSON(); + + if (nearestplanes == null) + return; + foreach (PlaneInfo plane in nearestplanes) + { + // add or update plane in active planes list + PlaneInfo activeplane = null; + if (ActivePlanes.TryGetValue(plane.Hex, out activeplane)) + { + // plane found --> update if necessary + bool update = false; + // plane has higher potential + if (plane.Potential > activeplane.Potential) + update = true; + // plane has same potetial but is nearer to path + else if ((plane.Potential == activeplane.Potential) && (plane.IntQRB < activeplane.IntQRB)) + update = true; + // update if necessary + if (update) + { + ActivePlanes.Remove(activeplane.Hex); + ActivePlanes.Add(plane.Hex, plane); + } + } + else + { + // add plane to list if not foound + ActivePlanes.Add(plane.Hex, plane); + } + } + st.Stop(); + Say("Get nearest planes: " + ActivePlanes.Count.ToString() + " plane(s), " + st.ElapsedMilliseconds.ToString() + " ms."); + st.Reset(); + st.Start(); + Log.WriteMessage("Drawing planes: " + ActivePlanes.Count.ToString() + " plane(s), " + st.ElapsedMilliseconds.ToString() + " ms."); + + // check if any plane is on list --> return empty list + if ((ActivePlanes == null) || (ActivePlanes.Count == 0)) + return; + // draw all planes + foreach (PlaneInfo plane in ActivePlanes.Values) + { + try + { + // show planes if it meets filter criteria + if ((plane.Alt_m >= Properties.Settings.Default.Planes_MinAlt) && (plane.Category >= Properties.Settings.Default.Planes_Filter_Min_Category)) + { + // check selected state + isselected = SelectedPlanes.IndexOf(plane.Call) >= 0; + // now, show plane according to potential + switch (plane.Potential) + { + case 100: + gmo_Planes.Markers.Add(CreatePlaneDetailed(plane, isselected)); + break; + case 75: + gmo_Planes.Markers.Add(CreatePlaneDetailed(plane, isselected)); + break; + case 50: + gmo_Planes.Markers.Add(CreatePlaneDetailed(plane, isselected)); + break; + default: + gmo_Planes.Markers.Add(CreatePlaneSimple(plane, isselected)); + break; + } + + // if selected: draw the thin path to crossing point if one + if (isselected) + { + if (plane.IntPoint != null) + { + GMapRoute intpath = new GMapRoute(plane.Call); + intpath.Stroke = new Pen(Color.Black, 1); + intpath.Points.Add(new PointLatLng(plane.Lat, plane.Lon)); + intpath.Points.Add(new PointLatLng(plane.IntPoint.Lat, plane.IntPoint.Lon)); + gmo_Routes.Routes.Add(intpath); + // Console.WriteLine(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss") + ";" + info.IntQRB.ToString("F3")); + } + } + + // show planes on chart if in sigle path mode + if ((plane.IntPoint != null) && (plane.IntQRB <= Properties.Settings.Default.Path_Band_Settings[Properties.Settings.Default.Band].MaxDistance)) + { + // calculate distance from mylat/mylon + double dist = LatLon.Distance(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, plane.IntPoint.Lat, plane.IntPoint.Lon); + // add new data points + if (plane.AltDiff > 0) + { + Planes_Hi.Points.Add(new DataPoint(dist, plane.Alt_m)); + } + else + { + Planes_Lo.Points.Add(new DataPoint(dist, plane.Alt_m)); + } + } + } + + // invalidate chart + pm_Path.InvalidatePlot(true); + + } + catch (Exception ex) + { + Log.WriteMessage(ex.Message); + } + } + } + + private void DrawPath() + { + ElevationPathDesignator epath = ElevationPathFromJSON(); + if (epath == null) + return; + // set MyDetails and DXDetails + Properties.Settings.Default.MyCall = epath.Location1.Call; + Properties.Settings.Default.MyLat = epath.Lat1; + Properties.Settings.Default.MyLon = epath.Lon1; + Properties.Settings.Default.DXCall = epath.Location2.Call; + Properties.Settings.Default.DXLat = epath.Lat2; + Properties.Settings.Default.DXLon = epath.Lon2; + PropagationPathDesignator ppath = PropagationPathFromJSON(); + if (ppath == null) + return; + // clear charts + ClearCharts(); + // draw my end on the map + gmm_MyLoc = new GMarkerGoogle(new PointLatLng(epath.Location1.Lat, epath.Location1.Lon), ToolTipFont, Properties.Settings.Default.Map_SmallMarkers ? GMarkerGoogleType.red_small : GMarkerGoogleType.red_dot); + gmm_MyLoc.ToolTipText = epath.Location1.Call + "\n" + + epath.Location1.Lat.ToString("F8", CultureInfo.InvariantCulture) + "\n" + + epath.Location1.Lon.ToString("F8", CultureInfo.InvariantCulture) + "\n" + + epath.Location1.Loc + "\n" + + epath.Location1.Elevation.ToString("F0") + "m"; + gmm_MyLoc.ToolTipMode = MarkerTooltipMode.OnMouseOver; + gmm_MyLoc.Tag = epath.Location1.Call; + gmo_Objects.Markers.Add(gmm_MyLoc); + // draws a propagation path to map + PropagationPoint[] ppoints = new PropagationPoint[0]; + try + { + // get infopoints for map + ppoints = ppath.GetInfoPoints(); + // calculate midpoint + ScoutBase.Core.LatLon.GPoint midpoint = LatLon.MidPoint(ppath.Lat1, ppath.Lon1, ppath.Lat2, ppath.Lon2); + GMapMarker gmmid = new GMarkerGoogle(new PointLatLng(midpoint.Lat, midpoint.Lon), ToolTipFont, Properties.Settings.Default.Map_SmallMarkers ? GMarkerGoogleType.blue_small : GMarkerGoogleType.blue_dot); + gmmid.ToolTipText = ppath.Location1.Call + " <> " + ppath.Location2.Call; + gmmid.ToolTipMode = MarkerTooltipMode.OnMouseOver; + gmo_Objects.Markers.Add(gmmid); + // calculate dx end + gmm_DXLoc = new GMarkerGoogle(new PointLatLng(epath.Lat2, epath.Lon2), ToolTipFont, Properties.Settings.Default.Map_SmallMarkers ? GMarkerGoogleType.yellow_small : GMarkerGoogleType.yellow_dot); + gmm_DXLoc.ToolTipText = epath.Location2.Call + "\n" + + epath.Location2.Lat.ToString("F8", CultureInfo.InvariantCulture) + "\n" + + epath.Location2.Lon.ToString("F8", CultureInfo.InvariantCulture) + "\n" + + epath.Location2.Loc + "\n" + + epath.Location2.Elevation.ToString("F0") + "m"; + gmm_DXLoc.ToolTipMode = MarkerTooltipMode.OnMouseOver; + gmm_DXLoc.Tag = epath.Location2.Call; + gmo_Objects.Markers.Add(gmm_DXLoc); + // set three small points for hot path, if one + if (!Properties.Settings.Default.Map_SmallMarkers) + { + int i1 = -1; + int i3 = -1; + for (int i = 0; i < ppoints.Length; i++) + { + if (Math.Max(ppoints[i].H1, ppoints[i].H2) < Properties.Settings.Default.Planes_MaxAlt) + { + if (i1 == -1) + i1 = i; + else i3 = i; + + } + } + if ((i1 >= 0) && (i3 >= 0)) + { + GMapMarker gmi1 = new GMarkerGoogle(new PointLatLng(ppoints[i1].Lat, ppoints[i1].Lon), GMarkerGoogleType.red_small); + gmo_Objects.Markers.Add(gmi1); + LatLon.GPoint gp = LatLon.MidPoint(ppoints[i1].Lat, ppoints[i1].Lon, ppoints[i3].Lat, ppoints[i3].Lon); + GMapMarker gmi2 = new GMarkerGoogle(new PointLatLng(gp.Lat, gp.Lon), GMarkerGoogleType.blue_small); + gmo_Objects.Markers.Add(gmi2); + GMapMarker gmi3 = new GMarkerGoogle(new PointLatLng(ppoints[i3].Lat, ppoints[i3].Lon), GMarkerGoogleType.yellow_small); + gmo_Objects.Markers.Add(gmi3); + } + } + // draw propagation path according to path status + // valid: black + // invalid: red + gmr_FullPath = new GMapRoute("fullpath"); + gmr_FullPath.Stroke = (ppath.Valid) ? new Pen(Color.Black, 3) : new Pen(Color.Red, 3); + gmo_PropagationPaths.Routes.Add(gmr_FullPath); + gmr_NearestFull = new GMapRoute("fullpath"); + gmr_NearestFull.Stroke = (ppath.Valid) ? new Pen(Color.Black, 3) : new Pen(Color.Red, 3); + gmo_NearestPaths.Routes.Add(gmr_NearestFull); + foreach (PropagationPoint ppoint in ppoints) + { + gmr_FullPath.Points.Add(new PointLatLng(ppoint.Lat, ppoint.Lon)); + gmr_NearestFull.Points.Add(new PointLatLng(ppoint.Lat, ppoint.Lon)); + } + // draw mutual visible path + gmr_VisiblePpath = new GMapRoute("visiblepath"); + gmr_VisiblePpath.Stroke = new Pen(Color.Magenta, 3); + gmr_NearestVisible = new GMapRoute("visiblepath"); + gmr_NearestVisible.Stroke = new Pen(Color.Magenta, 3); + for (int i = 0; i < ppoints.Length; i++) + { + if ((Math.Max(ppoints[i].H1, ppoints[i].H2) > 0) && (Math.Max(ppoints[i].H1, ppoints[i].H2) < Properties.Settings.Default.Planes_MaxAlt)) + { + PointLatLng p = new PointLatLng(ppoints[i].Lat, ppoints[i].Lon); + gmr_VisiblePpath.Points.Add(p); + gmr_NearestVisible.Points.Add(p); + } + } + gmo_PropagationPaths.Routes.Add(gmr_VisiblePpath); + gmo_NearestPaths.Routes.Add(gmr_NearestVisible); + + // calculate bounds + double minlat = Math.Min(epath.Lat1, epath.Lat2); + double minlon = Math.Min(epath.Lon1, epath.Lon2); + double maxlat = Math.Max(epath.Lat1, epath.Lat2); + double maxlon = Math.Max(epath.Lon1, epath.Lon2); + + // calculate center + double centerlat = LatLon.MidPoint(minlat, minlon, maxlat, maxlon).Lat; + double centerlon = LatLon.MidPoint(minlat, minlon, maxlat, maxlon).Lon; + + // ensure that whole path is visible and optionally centered + gm_Main.SetZoomToFitRect(RectLatLng.FromLTRB(minlon, maxlat, maxlon, minlat)); + gm_Main.Position = new PointLatLng(centerlat, centerlon); + + UpdateCharts(epath, ppath); + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + + private void ti_Startup_Tick(object sender, EventArgs e) + { + ti_Startup.Stop(); + // start timer + ti_Progress.Start(); + } + + private void ti_Progress_Tick(object sender, EventArgs e) + { + tsl_ConnectionStatus.Text = VieClientStatus.ToString(); + switch (VieClientStatus) + { + case VIEWCLIENTSTATUS.CONNECTING: + { + tsl_ConnectionStatus.BackColor = Color.Yellow; + tsl_ConnectionStatus.ForeColor = Color.Green; + InitializeSettings(); + break; + } + case VIEWCLIENTSTATUS.CONNECTED: + { + tsl_ConnectionStatus.BackColor = Color.Green; + tsl_ConnectionStatus.ForeColor = Color.White; + if (PlayMode != AIRSCOUTPLAYMODE.FORWARD) + return; + CurrentTime = DateTime.UtcNow; + UpdateStatus(); + DrawPlanes(); + break; + } + case VIEWCLIENTSTATUS.ERROR: + { + tsl_ConnectionStatus.BackColor = Color.Red; + tsl_ConnectionStatus.ForeColor = Color.White; + break; + } + default: + { + tsl_ConnectionStatus.BackColor = SystemColors.Control; + tsl_ConnectionStatus.ForeColor = SystemColors.ControlText; + break; + } + } + } + + private void MapViewDlg_Load(object sender, EventArgs e) + { + VieClientStatus = VIEWCLIENTSTATUS.INIT; + InitializeIcons(); + InitializeCharts(); + this.Text = "AirScout View Client V" + Application.ProductVersion; + this.Show(); + ti_Progress.Start(); + VieClientStatus = VIEWCLIENTSTATUS.CONNECTING; + } + + private void tsi_Exit_Click(object sender, EventArgs e) + { + this.Close(); + } + + private void MapViewDlg_FormClosing(object sender, FormClosingEventArgs e) + { + ti_Progress.Stop(); + Properties.Settings.Default.Save(); + } + + private double GetMinH(double max_alt, double H1, double H2) + { + double max = Math.Max(H1, H2); + if (max <= max_alt) + return max; + return max_alt; + } + + + private void UpdateCharts(ElevationPathDesignator epath, PropagationPathDesignator ppath) + { + // updates the diagram area + short[] epoints = new short[0]; + PropagationPoint[] ppoints = new PropagationPoint[0]; + try + { + ClearCharts(); + // adjust diagram axes + Path_X.Maximum = ppath.Distance; + Elevation_X.Maximum = epath.Distance; + // get infopoints for charting + epoints = epath.GetInfoPoints(); + ppoints = ppath.GetInfoPoints(); + // calculate epsilon for LOS + double eps_los = Propagation.EpsilonFromHeights(epath.Location1.Elevation + ppath.QRV1.AntennaHeight, ppath.Distance, epath.Location2.Elevation + ppath.QRV2.AntennaHeight, LatLon.Earth.Radius); + // fill chart + short maxelv = short.MinValue; + double myelev = epath.Location1.Elevation; + for (int i = 0; i < epoints.Length; i++) + { + Path_Elevation.Points.Add(new OxyPlot.DataPoint(i, epoints[i])); + Min_H1.Points.Add(new OxyPlot.DataPoint(i, ppoints[i].H1)); + Min_H2.Points.Add(new OxyPlot.DataPoint(i, ppoints[i].H2)); + Max_H.Points.Add(new OxyPlot.DataPoint(i, Properties.Settings.Default.Planes_MaxAlt)); + Min_H.Points.Add(new OxyPlot.DataPoint(i, Properties.Settings.Default.Planes_MaxAlt)); + Min_H.Points2.Add(new OxyPlot.DataPoint(i, GetMinH(Properties.Settings.Default.Planes_MaxAlt, Min_H1.Points[i].Y, Min_H2.Points[i].Y))); + LOS.Points.Add(new OxyPlot.DataPoint(i, Propagation.HeightFromEpsilon(myelev + ppath.QRV1.AntennaHeight, i, eps_los, LatLon.Earth.Radius))); + Elevation.Points.Add(new OxyPlot.DataPoint(i, epoints[i])); + if (maxelv < epoints[i]) + maxelv = epoints[i]; + } + // adjust Y-axis --> max elv + 10% + Elevation_Y.Maximum = maxelv + maxelv * 0.1; + // invalidate plots + pm_Path.InvalidatePlot(true); + pm_Elevation.InvalidatePlot(true); + // show path legends + Charts_ShowLegends(5000); + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + + private void ClearCharts() + { + try + { + // clear all points + Path_Elevation.Points.Clear(); + Min_H1.Points.Clear(); + Min_H2.Points.Clear(); + Max_H.Points.Clear(); + Min_H.Points.Clear(); + Min_H.Points2.Clear(); + Planes_Hi.Points.Clear(); + Planes_Lo.Points.Clear(); + Elevation.Points.Clear(); + LOS.Points.Clear(); + // update view + pm_Path.InvalidatePlot(true); + pm_Elevation.InvalidatePlot(true); + } + catch (Exception ex) + { + Log.WriteMessage(ex.ToString()); + } + } + + private void Charts_ShowLegends(int ms) + { + // enable path legends for some seconds + pm_Path.IsLegendVisible = true; + pm_Path.InvalidatePlot(true); + pm_Elevation.IsLegendVisible = true; + pm_Elevation.InvalidatePlot(true); + ti_ShowLegends.Interval = ms; + ti_ShowLegends.Start(); + } + + private void ti_ShowLegends_Tick(object sender, EventArgs e) + { + pm_Path.IsLegendVisible = false; + pm_Path.InvalidatePlot(true); + pm_Elevation.IsLegendVisible = false; + pm_Elevation.InvalidatePlot(true); + ti_ShowLegends.Enabled = false; + } + + private void pv_Path_Paint(object sender, PaintEventArgs e) + { + // draw calsign s on chart + string mycall = Properties.Settings.Default.MyCall; + string dxcall = Properties.Settings.Default.DXCall; + int top = pv_Path.Bottom - pv_Path.Height / 2; + int left = pv_Path.Left + 50; + int right = pv_Path.Width - 38; + Font font = new Font(FontFamily.GenericSansSerif, 8, FontStyle.Bold); + Graphics g = e.Graphics; + using (StringFormat format = new StringFormat(StringFormatFlags.DirectionVertical)) + { + // measure text to emulate TextAlign.Middle + int mywidth = (int)g.MeasureString(mycall, font).Width; + int dxwidth = (int)g.MeasureString(dxcall, font).Width; + using (SolidBrush brush = new SolidBrush(Color.Black)) + { + if (pv_Path.Height - 50 > mywidth) + g.DrawString(mycall, font, brush, left, top - mywidth / 2, format); + if (pv_Path.Height - 50 > dxwidth) + g.DrawString(dxcall, font, brush, right, top - dxwidth / 2, format); + /* + if (pv_Path.Height > 2 * mywidth + 50) + g.DrawString(mycall, font, brush, left, top, format); + if (pv_Path.Height > 2 * dxwdith + 50) + g.DrawString(dxcall, font, brush, right, top, format); + */ + } + } + } + + private void gb_Path_Resize(object sender, EventArgs e) + { + // adjust charts + pv_Path.Location = new System.Drawing.Point(0, 0); + pv_Path.Width = gb_Path.Width; + pv_Path.Height = gb_Path.Height / 2; + pv_Elevation.Location = new System.Drawing.Point(0, gb_Path.Height / 2); + pv_Elevation.Width = gb_Path.Width; + pv_Elevation.Height = gb_Path.Height / 2; + } + + private void gm_Main_OnMapZoomChanged() + { + double midlat = LatLon.MidPoint(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, Properties.Settings.Default.DXLat, Properties.Settings.Default.DXLon).Lat; + double midlon = LatLon.MidPoint(Properties.Settings.Default.MyLat, Properties.Settings.Default.MyLon, Properties.Settings.Default.DXLat, Properties.Settings.Default.DXLon).Lon; + if ((PlayMode == AIRSCOUTPLAYMODE.FORWARD) && Properties.Settings.Default.Map_AutoCenter) + gm_Main.Position = new PointLatLng(midlat, midlon); + tb_Map_Zoom.Text = gm_Main.Zoom.ToString(); + } + + private void gm_Main_OnMarkerClick(GMapMarker item, MouseEventArgs e) + { + try + { + if (e.Button == System.Windows.Forms.MouseButtons.Left) + { + // check if plane clicked + if (item.Overlay == gmo_Planes) + { + // keep the selected state + int selectedindex = SelectedPlanes.IndexOf((string)item.Tag); + // clear all other selections if tracking is en + // toogle selection of the selected plane + if (selectedindex >= 0) + { + // remove item from selected planes list + SelectedPlanes.RemoveAt(selectedindex); + } + else + { + SelectedPlanes.Add((string)item.Tag); + } + } + } + } + catch (Exception ex) + { + // do nothing if item not found in planes list + Log.WriteMessage(ex.Message); + } + } + + private void btn_Map_Zoom_In_Click(object sender, EventArgs e) + { + if (gm_Main.Zoom < 20) + gm_Main.Zoom++; + } + + private void btn_Map_Zoom_Out_Click(object sender, EventArgs e) + { + if (gm_Main.Zoom > 0) + gm_Main.Zoom--; + } + + private void tb_MyCall_TextChanged(object sender, EventArgs e) + { + if (!Callsign.Check(tb_MyCall.Text)) + return; + Properties.Settings.Default.MyCall = tb_MyCall.Text; + // get callsign info + List l = MyLocationsFromJSON(); + if (l == null) + { + cb_MyLoc.Items.Clear(); + cb_MyLoc.SilentText = ""; + return; + } + cb_MyLoc.Items.Clear(); + cb_MyLoc.DisplayMember = "Loc"; + foreach (LocationDesignator ld in l) + { + cb_MyLoc.Items.Add(ld); + } + if (cb_MyLoc.Items.Count > 0) + { + cb_MyLoc.SelectedItem = cb_MyLoc.Items[0]; + Properties.Settings.Default.MyLat = ((LocationDesignator)cb_MyLoc.Items[0]).Lat; + Properties.Settings.Default.MyLon = ((LocationDesignator)cb_MyLoc.Items[0]).Lon; + } + } + + private void tb_DXCall_TextChanged(object sender, EventArgs e) + { + if (!Callsign.Check(tb_DXCall.Text)) + return; + Properties.Settings.Default.DXCall = tb_DXCall.Text; + // get callsign info + List l = DXLocationsFromJSON(); + if (l == null) + { + cb_DXLoc.Items.Clear(); + cb_DXLoc.SilentText = ""; + return; + } + cb_DXLoc.Items.Clear(); + cb_DXLoc.DisplayMember = "Loc"; + foreach (LocationDesignator ld in l) + { + cb_DXLoc.Items.Add(ld); + } + if (cb_DXLoc.Items.Count > 0) + { + cb_DXLoc.SelectedItem = cb_DXLoc.Items[0]; + Properties.Settings.Default.DXLat = ((LocationDesignator)cb_DXLoc.Items[0]).Lat; + Properties.Settings.Default.DXLon = ((LocationDesignator)cb_DXLoc.Items[0]).Lon; + } + } + + private void cb_MyLoc_TextChanged(object sender, EventArgs e) + { + + } + + private void cb_DXLoc_TextChanged(object sender, EventArgs e) + { + + } + + private void tsi_Info_Click(object sender, EventArgs e) + { + MessageBox.Show("AirScout View Client V" + Application.ProductVersion.ToString(), "Info", MessageBoxButtons.OK); + } + + private void btn_Map_PlayPause_Click(object sender, EventArgs e) + { + if (PlayMode != AIRSCOUTPLAYMODE.FORWARD) + { + PlayMode = AIRSCOUTPLAYMODE.FORWARD; + btn_Map_PlayPause.ImageIndex = 0; + // draw path + DrawPath(); + } + else + { + PlayMode = AIRSCOUTPLAYMODE.PAUSE; + btn_Map_PlayPause.ImageIndex = 1; + } + + } + } +} diff --git a/AirScoutViewClient/MapViewDlg.resx b/AirScoutViewClient/MapViewDlg.resx new file mode 100644 index 0000000..d0f2a4f --- /dev/null +++ b/AirScoutViewClient/MapViewDlg.resx @@ -0,0 +1,3704 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 126, 17 + + + 220, 17 + + + 331, 17 + + + 435, 17 + + + 546, 17 + + + 654, 17 + + + 763, 17 + + + 869, 17 + + + 983, 17 + + + 1122, 17 + + + + AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj00LjAuMC4w + LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0 + ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAACc + DQAAAk1TRnQBSQFMAgEBAwEAARABCwEQAQsBIAEAASABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo + AwABgAMAASADAAEBAQABCAYAARAYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB3AHA + AQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEAAykBAANVAQADTQEAA0IBAAM5 + AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8BAAHWAucBAAGQAakBrQIAAf8BMwMA + AWYDAAGZAwABzAIAATMDAAIzAgABMwFmAgABMwGZAgABMwHMAgABMwH/AgABZgMAAWYBMwIAAmYCAAFm + AZkCAAFmAcwCAAFmAf8CAAGZAwABmQEzAgABmQFmAgACmQIAAZkBzAIAAZkB/wIAAcwDAAHMATMCAAHM + AWYCAAHMAZkCAALMAgABzAH/AgAB/wFmAgAB/wGZAgAB/wHMAQABMwH/AgAB/wEAATMBAAEzAQABZgEA + ATMBAAGZAQABMwEAAcwBAAEzAQAB/wEAAf8BMwIAAzMBAAIzAWYBAAIzAZkBAAIzAcwBAAIzAf8BAAEz + AWYCAAEzAWYBMwEAATMCZgEAATMBZgGZAQABMwFmAcwBAAEzAWYB/wEAATMBmQIAATMBmQEzAQABMwGZ + AWYBAAEzApkBAAEzAZkBzAEAATMBmQH/AQABMwHMAgABMwHMATMBAAEzAcwBZgEAATMBzAGZAQABMwLM + AQABMwHMAf8BAAEzAf8BMwEAATMB/wFmAQABMwH/AZkBAAEzAf8BzAEAATMC/wEAAWYDAAFmAQABMwEA + AWYBAAFmAQABZgEAAZkBAAFmAQABzAEAAWYBAAH/AQABZgEzAgABZgIzAQABZgEzAWYBAAFmATMBmQEA + AWYBMwHMAQABZgEzAf8BAAJmAgACZgEzAQADZgEAAmYBmQEAAmYBzAEAAWYBmQIAAWYBmQEzAQABZgGZ + AWYBAAFmApkBAAFmAZkBzAEAAWYBmQH/AQABZgHMAgABZgHMATMBAAFmAcwBmQEAAWYCzAEAAWYBzAH/ + AQABZgH/AgABZgH/ATMBAAFmAf8BmQEAAWYB/wHMAQABzAEAAf8BAAH/AQABzAEAApkCAAGZATMBmQEA + AZkBAAGZAQABmQEAAcwBAAGZAwABmQIzAQABmQEAAWYBAAGZATMBzAEAAZkBAAH/AQABmQFmAgABmQFm + ATMBAAGZATMBZgEAAZkBZgGZAQABmQFmAcwBAAGZATMB/wEAApkBMwEAApkBZgEAA5kBAAKZAcwBAAKZ + Af8BAAGZAcwCAAGZAcwBMwEAAWYBzAFmAQABmQHMAZkBAAGZAswBAAGZAcwB/wEAAZkB/wIAAZkB/wEz + AQABmQHMAWYBAAGZAf8BmQEAAZkB/wHMAQABmQL/AQABzAMAAZkBAAEzAQABzAEAAWYBAAHMAQABmQEA + AcwBAAHMAQABmQEzAgABzAIzAQABzAEzAWYBAAHMATMBmQEAAcwBMwHMAQABzAEzAf8BAAHMAWYCAAHM + AWYBMwEAAZkCZgEAAcwBZgGZAQABzAFmAcwBAAGZAWYB/wEAAcwBmQIAAcwBmQEzAQABzAGZAWYBAAHM + ApkBAAHMAZkBzAEAAcwBmQH/AQACzAIAAswBMwEAAswBZgEAAswBmQEAA8wBAALMAf8BAAHMAf8CAAHM + Af8BMwEAAZkB/wFmAQABzAH/AZkBAAHMAf8BzAEAAcwC/wEAAcwBAAEzAQAB/wEAAWYBAAH/AQABmQEA + AcwBMwIAAf8CMwEAAf8BMwFmAQAB/wEzAZkBAAH/ATMBzAEAAf8BMwH/AQAB/wFmAgAB/wFmATMBAAHM + AmYBAAH/AWYBmQEAAf8BZgHMAQABzAFmAf8BAAH/AZkCAAH/AZkBMwEAAf8BmQFmAQAB/wKZAQAB/wGZ + AcwBAAH/AZkB/wEAAf8BzAIAAf8BzAEzAQAB/wHMAWYBAAH/AcwBmQEAAf8CzAEAAf8BzAH/AQAC/wEz + AQABzAH/AWYBAAL/AZkBAAL/AcwBAAJmAf8BAAFmAf8BZgEAAWYC/wEAAf8CZgEAAf8BZgH/AQAC/wFm + AQABIQEAAaUBAANfAQADdwEAA4YBAAOWAQADywEAA7IBAAPXAQAD3QEAA+MBAAPqAQAD8QEAA/gBAAHw + AfsB/wEAAaQCoAEAA4ADAAH/AgAB/wMAAv8BAAH/AwAB/wEAAf8BAAL/AgAD//8A/wAtAAL0fgABKAH0 + WwAB9AHxAbwDBwH0AQAB/wHxAbwDBwHzFAACJwL0WQABGQG0Aa0DpgG8AQAB8wG0Aa0DpgEHFAACJwEo + AfRZAAEZAtUBzwGLAaYBvAEAAfMC1QHPAYsBpgEHFAADKAEnAvQZAAL/AfQB8wHxAfACvAHxAfMB9AL/ + MQAB8QG0AdwBCQHPAaYBvAEAAfMBtAHcAQkBzwGmAQcUAAIoAicBKAH0GQAB/wH0AfABBwF0AW4BSwJu + Ae8BvAHzAf8xAAHxAbQCCQG0AaYBvAEAAfMBtAIJAbQBpgEHFAADSQEoAicC9BUAAv8B8wG8AZMBbwFM + BEUBbgHtAQcB8QL/LwAB8QG0AgkBtAGmAbwBAAHzAbQCCQG0AaYBBxQABEkCJwEoAfQVAAH/AfQBvAF0 + AUwERgIlASQBRQFuAQcB8wH/LwAB8QHVAdwBCQG0AaYBvAEAAfMB1QHcAQkBtAGmAQcUAAJJAU8CSQIo + AScC9BMAAfQBGwGTBkwCRgElASQBRQHtAbwB9C8AAfEB1QEJAd0BtAGmAbwBAAHzAdUBCQHdAbQBpgEH + FAACSQJPAkkCJwEoAfQTAAH0AQcBdAhMAUYBJQEkAW4B7wHzLwAB8QHVAQkB3QG0AaYBvAEAAfMB1QEJ + Ad0BtAGmAQcUAAJJAXICTwJJAigBJwL0EQAB8gGTAm8DdQLjA0wBRgElAUUBbgHxLwABGQHVAQkB3QG0 + AaYBvAEAAfMB1QEJAd0BtAGmAQcUAAJJAnICTwJJAicBKAH0EQABGgF0AW8BFgV1AeMCTAFGASUBRQFu + AbwvAAEZAdUB3QEZAbQBhgG8AQAB8wHVAd0BGQG0AYYBBxQAAk8BlwJyAk8CSQMoAvQPAAEaAm8BdQSU + AnUB4wJMAUYBRQFLAbwvAAEZAdYCGQG0AYYBvAEAAfMB1gIZAbQBhgEJFAACTwKXAnICTwJJAigC9A8A + ARoCdAKUApoClAF1AeMCTAFGAUUBbgHwLwABGQHWAhkBtQGGAbwBAAHzAdYCGQG1AYYBCRQAAk8DlwNy + A0kB9BEAAfIBkwF0AZQEmgGUAnUCTAFGAUwBdAHxLwABGQHWAhkBtQGGAbwBAAHzAdYCGQG1AYYBvBQA + Ak8ElwFyA0kC9BEAAfMBGgGTAXQBmgG9ApoBlAJ1AkwBRgFvAQcB8y8AARkB1gIZAbUBhgG8AQAB8wHW + AhkBtQGGAbwUAAJQA5cBCANJAfQTAAH0ARsBkwF0AXUCmgKUAnUDTAGTAfAB9C8AARkB3AIZAbUBiwG8 + AQAB8wHcAhkBtQGLAbwUAAJQApcBCANJAvQTAAH/AfQBGgGTAnQClAF1ARYBbwJMAXQBvAH0Af8vAAHz + AdwBCQHdAdYBzwHdAQAB8wHcAQkB3QHWAc8B8BQAAnIBlwEIA0kB9BUAAv8B8wEaApMCdANvAXQBkwG8 + AfMC/y8AAfMD3AHVAbQB8gEAAfMD3AHVAbQB8RQAAnIBCANJAvQXAAH/AfQBGwEaAZMBdAFvAXQBkwEH + ARsB9AH/MQAB9AHzAxkB8QH0AQAB/wHzAxkB8QH0FAACcgNJAfQZAAL/AfQB8wHyAxoB8gL0Av9UAAJy + AkkC9HoAAnIBSQH0fAACcgL0fAABcgH0fgAC9P8AVQABQgFNAT4HAAE+AwABKAMAAYADAAEgAwABAQEA + AQEGAAECFgAD/wEADP8EAAz/BAAM/wQADP8EAAX/Ac8G/wQABf8Bzwb/BAAB/gECAQMC/wHDBv8EAAH+ + AQIBAwL/AcMG/wQAAf4BAgEDAv8BwAP/AYABAwH/BAAB/gECAQMC/wHAA/8BgAEDAf8EAAH+AQIBAwL/ + AcABPwH/Af4CAAH/BAAB/gECAQMC/wHAAT8B/wH+AgAB/wQAAf4BAgEDAv8BwAEPAf8B/gIAAf8EAAH+ + AQIBAwL/AcABDwH/Af4CAAH/BAAB/gECAQMC/wHAAQMB/wH+AgAB/wQAAf4BAgEDAv8BwAEDAf8B/gIA + Af8EAAH+AQIBAwL/AcABAAH/Af4CAAH/BAAB/gECAQMC/wHAAQAB/wH+AgAB/wQAAf4BAgEDAv8BwAED + Af8B/gIAAf8EAAH+AQIBAwL/AcABAwH/Af4CAAH/BAAB/gECAQMC/wHAAQ8B/wH+AgAB/wQAAf4BAgED + Av8BwAEPAf8B/gIAAf8EAAH+AQIBAwL/AcABPwH/Af4CAAH/BAAB/gECAQMC/wHAAT8C/wGAAQMB/wQA + Af4BAgEDAv8BwAP/AYABAwH/BAAF/wHABv8EAAX/AcMG/wQABf8Bwwb/BAAF/wHPBv8EAAX/Ac8G/wQA + DP8EAAz/BAAL + + + + + + AAABAAkAAAAAAAEAIAAztwAAlgAAAICAAAABACAAKAgBAMm3AABgYAAAAQAgAKiUAADxvwEASEgAAAEA + IACIVAAAmVQCAEBAAAABACAAKEIAACGpAgAwMAAAAQAgAKglAABJ6wIAICAAAAEAIACoEAAA8RADABgY + AAABACAAiAkAAJkhAwAQEAAAAQAgAGgEAAAhKwMAiVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABc + cqhmAAC2+klEQVR42ux9B5xU1fX/9703fbZ3ytKbiDRRUJqCCrZYY40mliTGFnvB3lH/iV2xJNaIYkdR + sWAvKEVRRJa6wMKyfXdmp77yP/e+92betAWBxPzCO3weMzuv33vP99R7rgCbbLJptyXh134Am2yy6dcj + GwBssmk3JhsAbLJpNyYbAGyyaTcmGwBssmk3JhsAbLJpNyYbAGyyaTcmGwBssmk3JhsAbLJpNyYbAGyy + aTcmGwBssmk3JhsAbLJpNyYbAGyyaTcmGwBssmk3JhsAbLJpNyYbAGyyaTcmGwBssmk3JhsAbLJpNyYb + AGyyaTcmGwBssmk3JhsAbLJpNyYbAGyyaTcmGwBssmk3JhsAbLJpNyYbAGyyaTcmGwBssmk3JhsAbLJp + NyYbAGyyaTcmGwBssmk3JhsAbLJpNyYbAGyyaTcmGwBssmk3JhsAbLJpNyYbAGyyaTcmGwBssmk3JhsA + bLJpNyYbAGyy6b+c1vce1E3TtH70dYwgCHvQZx9Ng0tg3KsRDwtYQ9/eo20pbZv71NZ0bu+1bQCwyab/ + Mlrfa5BIH91p24c4dDB9jidGH0jfq4jxCwVBQxbW3cxOhQ4CP9O2ms5Z2mdDzdau7mUDgE02/RcQMX0F + GKMLGEmMO5k+B0IHASblU8nk2qw4kCAVOij8SKDxpKZqL/XbtCr9SjYA2GTTr0Wk2g+ij0nEyNOJE8fS + 956cpw3GJsaFoeancapm/CZ0DQLmPna4ijnucu2M7ktXhayH2ABgk03/IVpHqj3x7F70dTIx9+H0fR/6 + XpyQ8FmYnYMA0n/TfxQgpGoHXQABAQDcZdqnvu7qRUVvr1m6rVNsssmmXUAk5R3EsCPo6xRi2COIF/em + 737B4LyElLeSVXIjlUm1hHpgOc96UDqYmD8rgLe7Bk+ZupS+n1783pofARsAbLJpl9O6XgMdxOyjiLum + 059HECMOp+8etk8zmNKU6lo2Rs7geiRO0gxUENJQIyeQsMPpd1ehBm+VBlHi599dNH/NFYANADbZtEuI + 1Hu3oHvtD6I/D6NtJG3Ors5JgIEp7Y3PdJW/S7L6C8zzNOPatDl8Gqn+BAAFqn68yo/5iABgyi+5jU02 + 2ZRG64np6WNf4iKm2h9GzDSU/hYzDrSq5dafrQBgHpfLw59rn+W7yfTsU3QBnhINrmIVogM64yOhKXxB + ADAhyyPZZJNNXRGp9z5Sv5kdfyQx26EwmF6z2txC0pPPyfhdE9J4ensBwOoTMK+RBSA4c0sk7Ynx3UUa + JJeW8CNYKE4//L34vTVXATYA2GTTNonU+3xi0v3AmB44mLaBxERiiuRN874J5s50j34XMXzu3WdnWh2E + lmvo55u2gv67Zkh2xvhOYnom9SW3xiV+lvSBAP12Pu34FwGAAtgAYJNNWWl970EF9DGWmOVIYprpxCgD + zdA7J6tjTsv+uzVcn7EPJoMbSCBkAkAKJQBD/5LC+AUk8YnxHV7jhirMQIFViVhGH+cWz1/zhfWyNgDY + ZJNBa7zFpWJe3hj6ejhth9A2OOuBCU+9qKvkJpeLgu6dNzdRSOoC6So8LACQy6OfxQzQjJg+V/ULk4zP + D1eTWoNFSWCnPkb/XUdSvyHbq9hk025Lm0+aXl5y/iXjYitWHhX9ccVUweHskzUUZyXO4CLUzgC0aIQf + Jre3Q20IQusIQo2EoMWj0EIxaDEVgkME88TxT2EbLJeuNZg/Gw6+rBLftPMzn3kDbdeQ1H8u1+1sALBp + t6PgB//q5p96yvjoisXT5dpVU6SKir7Obr0hdesDrbMNSqDV4ECLuJbj0GRiZlGE0toMtb0VagsdJzFJ + L0Hy5PH9miyTCq5AJWBAewjx9bWI/PAj4ivXQ2lpg6CSRuBy0Xl6sCAjfIc05jclvoMYn0n84jTGT1Mg + LI7FV+n7lST1V3fVFjYA2LRbENn0PcnGHi84hMNcwwYe6KjuXy16fMS8pMbH4xYRqiW5yCT2k6hBrMqD + VFQAsaQUjvIekCrLIZUVQlM0aEHSBiIxAoYmYnQChrhMx5bB0b0HMa8L8uYNiCz5DpGvFyK6eDkBDd3T + 7UpcP6Hym7c0HoWF8LjEL9UZ31TzrWQ1KYja6O8b6McHiuavyZj8k042ANj0P0vE9NX0sT8x0m9opE+m + 7z00lZgoEgVi0aznaFYPvpXYT6Jp20ukCUgQy/PhqO4O19Ch8IwfA/fosXD26sX19OgPixH+5lPIP62G + WFAE7/hJcA0cRPcO077v0PrgY5BX1ELwejO1ACbxnSx5R+MhPadH07FBTZHwSbxI/vYZfb2EVP5F29tG + NgDY9D9FxPQD6GMi9Gy8cbT11FVrg7G3Zd8jqVbnTK81v5DKrzLtIR7jB4qlfnjGjITv6MPgP+w3EPNL + IddvQufc1xB8ay5cffuh4JRT4ajsgdiaFWi+ZSbknzdB8LiTXn2S+K4iQ9V364zP9yVSiHWvo6mwGEAQ + oY976PN2Yv7gL2kvGwBs+j9P63sN2pNGMmP2w4kJJtCgLk8k38DC8znCcb+IsuTrJ1Ry5gOIhukHGY7+ + PZH/uxNQcOafIeYVQt6yAc03XEsMvxol11xF2sAeiK5chqarb4WyNQDJL+mMX6RCcuvXV9WMOT3E+4Ix + ISixbwWBwiVF81e/uyNtZwOATf/niKS8RCN/GPQCGixktz9tRSlOtLTsu4SazXQBQ99OzcrTMibYWCnb + lPzEtTRL8g70sKAWjUHtDMI9bk+UPXQv3ING0j1iaL35JnS++zbKbruDNIIBaH/qMcReex7uSg9P34Uh + 7a2KhhnWY0+sGWhjSP5/0dcrSerX7Whb7nIAoM5h1xxAT1dNVzcnQ7CnbqFtbZ/ampZdfU+b/veJ590L + fILNgbSxCTf70pafcWCOFFpTX9YZHWkB+dT6GtZzs2bmsj8iEVLNFd0f4PXknMSjtpN0H1iFyqefgHuP + 0fy3xisvgrLyR5TdMRNqzSKEZ93JvIzQjJMzmDKhzQhG4hC20pebiuaveWRn23WXAQB1EMOvU+iKp9In + K3pQkXb9ED37WvrlCfr+MAFBfFfd26b/TWLFMOljODHtQTT0J9DYGU5/+9KP2y6NPjmNPqkJWKfVWvbz + a6VNwbWyphYKwXv4RFLxz0Tg2ScRmvcpRL9PP04TUswNhjNaeyekfpWofIZAYOgo0gxaEThjCtyHngDH + gCHonHkltLjMIxLpQJIFz96k555hzuffWdolAEAdVQaWbQQck97gOegl2i4kEKjfFfe36X+H1vUaNFTQ + 7XnmxBtFWz8utS3iOF0aZ5tPz+zkhLw0DjIzaTXTJkAaAABZx6ypNbArauEonHv0RrfXXoBU3A3xjSux + +dDjoLWEIbikDKUDLO4vCZDQAf9+fVF0/4sQynsi8uAMyGtr4P3deQjedAFdN8STixJkZBhqyWeqpT9u + o+9Pksov76r23iUAsK681z81QThDdDl1FEtzjgjpqhb/qn1DTXNinw0163fVy9j0f5NIgLBS1wfwcJ0+ + p74064HZCmak189D6n7NwtjbHOxGtMA8WEi/r6pBjYZRes/NKDjljMRpTRedi45nXoeUn5+4J/Pciy4N + zkLAw2bmsRh+Wxtchx8L3zWPITr3KcTnPQ3vn69F8P9dDa2hEXBIiWtanjVEl3ucPu8hxq/d1W2/0wBQ + O3TYaEd1j881OeaVN26G2qKXJBfdbkCSMu+Q2nkr6f+jCQR+3tUvZtN/NxHT9+HVb4GjaDywsF1ZRv27 + bFpkxmQb3XmXEr/PUSQjaQKkJ+UjBTByhgBJTRfLClE1bw6cPQckfg794z40Xk72vL+AX0fyaHAXssw9 + FZILyYy9eBxCRQkKnvkK8S/fQeTJO+G94BZEHp8JuWY14HJaX43R67TNJMZf+O/qh50GgNY7b3ks78ij + /qiFOqE0NSOy/AdEvlqE6PIVUBs7IHg8PH0y427JjmSpikeSOWCDwP84EdNX0Qdj+mOp+yeSitstnTmB + VMmbYPBsE2SsZDm2a3XemFJr2dllXT7rT5EoXCP3QLe33uDZfYzYnNr7/nQWpr/2LkrK/RBJ2jvzWekt + pKbr8vuoEJxOFDz/FZT1yxC6/SL4L78bkdkPI75kEeB2m7f8kLZ7i+aveevf3Sc7BQCt99460D9u6pdi + XkEZy3+GxCY8SFDlGOQtdQgv+gaBZ16C1h7Wc59z1DkjWkXbdAKBtf/uF7bpP0vrew0qoj4+kPr7CPqc + Sj/1Zr9nlsNKDgjT9k2pep0t/p4ray8XAGQrsZuFsoEEf8ZwBO7Dx6LqydlgVj2jTZs2YcS+Y3F5oYKr + BhSiXVZSTN0UYoF94pGC2V9DrVuBzuv+Av+19yPy+pOIffIBywpcSOc8SEe+ULQL7fyuaKcAIDDnHzd5 + h467XmOZUClXFTnSCU4R9Zf9FdGFK8kkcGUmY6TefTFtxxIIbPhPvLhN/z4yIkITqX9ZAQ1WGHNw9gIY + 6QUvDG+7KfHTL5ylgEZXsfvMEttGCBBCdqlvnKM73tLAhQMAaQAHjES3F16iIa6r6x8t+AhTpk5Bud+F + Bfv1wiCHhpCa45kVhXjChYLnvgDCLQhcfDJ8l96O2IdvfBOb//qD8PpeIXU/hP8g7TAAtM26w+ceOPIz + Z0X1aE3OEtHjrati68UXI/7zBg4IiTvmVtG+pI75Td8NNc3/yUawaeeJ1Htm5+1tlMli0aC9mJg0pXTW + RJr0ajnmjrTQXFcRpZyMnHEgsoBBlhBgltsncvDVKDx79UPlG29DcPMiv7j6qqsw8847+fejepdizrAS + RFmKsPFsKc+vstmEAgpnfwMt2oaO84751vfnKx6ML/7qZf+Mh/+jjG/SDgPAlmN/M7H44osWOCu6O7jn + X9a46s/VHNa4DifkhjpsveByMgGCPGEiJ/On5DriDfr/ZNIEwr9Gg9j0y4gYvz/0UlnHULePIebxpXvp + zVBWaiEMpDJHFkeceX62382MOwDbBIlE6M88x4ztZxTpS3tu4/qiR4MrH3B4wvCMGYG8v79GqrwTwWAQ + kydNwpKlxjobNMb/MaYnzix3oS2uZox3MRqBXFKK8F0vywXL3rtAePiOZ/I/qPtVGN+kHQaAdT36n+vo + UfaQc2h/uAcOhaN3JZzde0MqLuXhDNGXh8hPi9B48Q1AREnMf053wCYaPvVpHiEAOPfXbBibctP66gEs + yetQ6rajAW7fFyZteSFzaiusjCtkmOHZa99lhpFTACBxTlfqAXKGDTO+IzkFl1+W5JXDr/GqO06/xifp + aJEIHPvsh7yZL/AD3p43D0cccUSK/OpT6MeCsd1RJWqIGiYN20/MHwnuOaq57oize4QKq+oLm2sHjDry + xO1exfffRTsMAOurBz6gycr5kGV6QVJt/G7O9FJFIaTKEkhFpVDaWsj+/1Gf3phR4gRdoi99v4FA4OZf + u4Fs0qnt/r/ntc68/yDB62VTaw8lNmYefUtuupHdwQpiWJLZ9WmsKrd/+c/MSaZoPETMptSyKjmaMc02 + 22IX247fZ5MoqbuRbeilS3tVz8Nh0t5ZAJL4xqQccz/7JAnu2Hcc8u58iZ981pln4p9PPplxy78MqsJD + gwoQiCvMFOgUZOVNzeH4++rz7/xtqFvfy6Vw58eKw3Xw5P3G/kccfV3RjgNA34FPa6HI6VAEPX4pGi3L + O5sVLiBQIKkveJJFD3IyfA6iAXBW3w01//y1G2l3pdXeUkfJ1ReO80+fdkJ8w7rD5cbGfomad3y6mkVk + 8lCvALWjVR8Lkjl3XuTMr0bCEJwOaEGVtiiUlq2Qt9ZD2dQEtbUdajhCDMiqXziTWXvbOzq7CA3CuE42 + H0RCxadbOvI0Pv/e4WPCXUtU1U2xUpgGMJYBwMvYvKUe++w9mj63ZNzWS/wwe0yPxqNKnM/QTZ4W5q3+ + gf3+yeKlbxDzs2SnJydMmHDmr92/v6SJM6jlrhlPCprvD5HvvkNs+Vq9PFI0zm0jFgq0ZgSmdEauu2b3 + EnXQf8eRJvDBr91QuxPJa2tGtj3x6OHxNeuOEgvy9xYcJKZZf1K/CmaGDUlwMb8woavr9fGi9CeBfr4b + QqGHawNqWxvEPD+4azxOx3q8pB3m0X43HGU96XsxHUOa4vKfEVn4LWIr1nKAYN5yOMRtP2w2wQIAOcyG + hIpPWMMq7LBqO056PFZDXzOPUdNCkOalSQNw7rsf/KQBvPLKqzj++ONyPpZbEt+LKuo08+9Pv17oEWV5 + KUnGIXTxGwgA/iu02x0GgNi3Xz0pFZb9QYuHCc2pA2t+QnTZCkR/XA55fR11Ipk3msSLHWyHpE8ekpmQ + wdIfD+qzoWY1bPq30fqeA3vCKRzqPWzs8c5+AydDld08HKeoun6cPjOdSWnm2HW74MjPp+NkXetj+9jh + 7DuZhHAQpxEQCKRPiy4/5E4W4ImRNFUh122FGm2F5C+Da88hcA0YCHlzHYJvvYPIgq9IMwhC4BmlOYAg + l1BJmxasWex6lpLrzNMlPmN6wdRctdRLWq3RxD4CANdYHQDuuusuXHnllV01KWuFk2mbw/5YtGRJr0hn + aBFdpZz+PHHCxIlzfu0+T2+2X0TRLz97SvTl/55fhNlyLhf3BbBJDfGN6xD9iRD9++8J1ZcR8qtGueQc + F0ukbiZDMyleXuAb+jqdzIHWX7vB/pdofe9BRfTB1og7lhqclcEuZyFdTZX17E0rIxmUcNAZfzCJzxhU + n6wjJHPp2T6WCs429p2BBZkAYkUenD2q4Rq2B9yj9uJO49iqGnS+Nx9KQwt8B0+F/+ApUBqbEHz5VYTe + /xJaZ4yAxpnp9QeS48bqLDDDduxvprh46daM4fP0Kjsm0yc0AiF5WfMdrcWD9B8NABhHADDzJTzxxBP4 + 4x//uK0mXmi0b2jZsmVTOjo65utPhJGkAfzwa/e/9Z1/MYXmvviAs/eQ87nNb70c0/wdLh0QYp3Yeskl + iH2/Rq+Emitck2VCR3qSB+16om9tzTZb3KauiZieOWXY0lYnQl+5tn+G9Ez0g2bxxAsZAJ1endLquTdn + s6Wod+xv0ijY1FcNCsRCPzz7DkfeicfDM2w4gu++i7a/PwyptBiF5/8RvsmTEfn6S7Tc+QCULS2kDbhS + ntP6NTFzDoak9+hM78i3SHrVcmqWaED6DMPUtwOvIyj1H4T8xz/Aku++x7h990U8vs1Z7WfT9o8VP/10 + dlNz8+PUfo30914EAFt/1YGQ7KYdI/mHH28gaXFj7iuLHAwarrgUse9WJwEgG2ULGWVPArmkT23NPb92 + o/1fJGPG3VHQY/ZjaZMyVqLJ8HpZYvjp2sB2OHW7wHqdCAy488/vRP5px6LwD2chtnolmq64EfKmLSi6 + 5GwUnn4maZFfoOnaO3glXcGcMWeq5SbzMpveo6v3ErPp3YY5QuaLxiIQjPmZpiqJGZaDlk3vNwBBQAIM + NC0cXiD0HTKv4MkPbthUt6Vw3zF7Y0v9Nme0f/unP/1pwsUXX3xVU2PTTYIofEMXO2DCxAn/FXkuO64B + fPjWDHfPAbfxluENrFrsRfCcZ7WjBVvPvQRKUyv3AFsbN9F5sEiVLuK+BrGCh4cRCHy2sy9+zrnnOjVV + KyMZx9TgCroZS1VkK6eE6aYRURACsx55pO0/0gv/JjIm3zDV/mhq5oM1AXnmgM+YM5MjOzMlFLcd3vZt + JfRk5PZDDw2y8ln5vz8KJRdfhsjSxWi67EYore0ou+lS5B19HFrvvxcdT77Gq+jybD1J995Lft1zz5bB + lpyGtsG0DBZuZOm8vjwIRaUQ8/JJi6gDmLOSOSrTNU7L6zBlgVlAEu0k67WuM6a+74T6vENyfJD/4BxB + GjTq+0AgMOzAAw/E4sWLt9kPEydOPPLuu+8+SlWUs1VVfYbs/9//2mPDpB0GgE0HTD7dveeQp11DBsDZ + bwikkgJe/FB0e3l4iKVKRlYsReNFM/gKKdwWTOv4nFmB2fYnv6+g/w8kEPjFKtSfzzmnmqTeJKfDOcnn + cff3+XxD8r2eUr/X65IkSQhFIvG4rCiRaDQYjka3RGOxDbFYbHNMUdZrqvodXWKtKIqNBAz/tWXNiOlJ + BvIaeUzFZwtZVpttmZhgk2tmXa58/fT+SKftmEmX8XvKpB76yphWiaHk9quQd/Bh6Jj9DFpufRiO6kpU + PPI3/iCNF15GIiAAV6HEGZ/F6QWHMX9ANXIQuPPRB6GiO6RegyFW9oKQVwLQuJSXfAD5248JLdxJEyWR + wKQzvkPS5wlsDan4sTmGhpDy4pr2+J9nNje1m6+hNDY9L5SWnHziiSfgpZde3maf0Dh74+mnnq7u06f3 + 6Gg0egcBwIz//MjYvq7bblpX0ecUTYn/i8f6/R5I3UvhKOtGnxVw9CDE9ecjtrEWodcW6IkfGZ6W3E+S + kpOdDSQU9b4+m1ZftD3P+f29M933/bR2D6cknV5RUnxC7+7de1QUk42ZlwePx82dR6IRdFYtNi0hNTpJ + PQ1FwrRF0dbRjtaOQKgtEFgXjkR+JHBYpmjaUoGBgiDUzZo16xeVY96VtK7XIPYKo+gVjqL2Opw+R2cc + lKH3IoMZs+XVW7UFIYdOn+4ozJYKvK3UXv53ZwSO4X1RfudMSF4PGq+7DtEFn6PwnBNReN7F6Hz0Dsif + vw/R59WPZ3kITPtkG5mYYkkFxN6DIfUcQFK/XLcLFFnXSp1uxD99BcrKHwyHdbIpWMqCg1owQuN0Q0DF + 940xrG6LI0jin/qYgcN3+/v8Myc43Ep4Q21hxX23n1Bw4aWHXHjBBXjgwQe32T8ej0f55z//KQ4cMECg + cXMG2f9P/VpjJZ12JhV4Op3+Dms9gTVSXAGfFMQaWxKNZA5RD+Pksulz2F65UkPN/WooEvTtP+LkolOP + /kkLhVlveul3Vo2hmA7uSSZFN7cklm3uDJW+ur6+qtHhGTFsQL+8npWVcDtdfF62zEwWVbN4kdNbhgED + baQLioZ3W6XjZZIyHZ2d6AiF0NpGoNDe3t4WDK4KRSOrotFYjaqDwmp6hs2Pzpr1b41aGEU1DqG3YAHp + A1hKVkavbkfCVZdz7AXrH8ierYfcvoGuinyYXvhEXj4bNrEACv9yMgrO+AvkH79G59+v5Qyfd8ujUFb9 + gNBDM7l/iY85JnwKS4jh+0KsHgSxvJpMTbdukiqWjESPD2rdSsQ+eFnXNER9ZW++ZB89eCCmYWWrzBl/ + UyDOprVwUGCPVEKa655ODwY7XPDQsWo4Cs/kEaia8wZnfgYC20MXX3QRTvv97+VQMLg/aQDf/jvHxS+h + HQaA2mF7TVcj0XfY+mc8CONw8AlAiYzAXHfQuvg9Y5BYyjFaHYXEhM5eZbHis04MSF6/Q4vLPmOCNhfm + xPz4qr4Z85sC6DdwEIZU9yQLROLMq2bxXJuOMFVN7suFUYIJCmxJKU0fYLF4DAEChUAojBbSFNo6Ap3t + weCaUDi8OhKLrVUU5Wu6Q60gChsJFHbK+7u+16B8uuUE+noiPQqz77t1kd6eZNBtvVh6P2xHZMa8flfT + ajP2WZe2EnUhzdazZ3a808dChzJJfxG+S26FY88xCP/jTkTfmAPvBVfDOXRvBG+7BGzZHM70VX0gVPWC + 6M0ztAE5NbbHnH5OJ5RNKxH/7C2o1EcSjVOHqDs1mJr/Q2McK1piaAwrfAAx2cX8+nnUv8NdHuzl8CCP + XkKPW4BHMMRCD6rffQVLO1SMGz1yeyIBOPKII3DjTTe1RsLh4eMnTNi0M2NgV9IOA0DbY3dOcxR1f1de + vwHhRYsRX70eSn0jtJge/+XTf63ZgOkep4wAc7anMSaTpksNNjBJLffusweKTjhS/8FIQiFjHh/XNeDj + oIzxe49GeQGZItRBVsZPMoMAJw0Ipu6Takb2n8OIY+dmKOvjmmRqCpIBCmwfu2eQtITOcJgBAprb2uRA + KLQ22NlZE47FNsiKwqRADTFUHZ1Q99ijs7rMCydpPxR6vP5oeqC9rQyamLue42HNOHnW3bkiAOnvryV6 + Y/vse8vvibCbvkguZ3jJp2fiiR7mL9YSZgZf+66jDa7xBxDT3wx52dcI338TXIccD89pFyE6bw5JeLqO + v1C/vKIvxpl4GTbmGOOzpbuDLZBXLOKbRFqB0+VEjK6/MaBgSYOh5tMPTBNwEijEjXoBQ0ji70PMX0rX + kTWd8RPvQuczh2Xl/bdCO/E0TNxnDL77/vtt8su4cePw/+6++ztRkibtv//+gW2e8B+iHQaA+KJvxwpe + 3yeCy+FmHle5aQuBwFpEvluM6LKVkDdugtoWopYl24zbXJoRSkobRBbvcOJv9pOg65+aEX8yizNoCb+A + xlNP8w/fH/kHT6HvcbipE5c1teHV5hAOHLcv8siOjMez85WDgIIxxqraDaipXU8SPIRRQwZj6IABHBBM + SZciOdNdGQIsaqxV1dWfVmRAyI6h6ymG+cA0hRA9d3tHB2kLHUpbILilMxSqIaColRV5KR22XHF5NnWT + g2vP+OhLpzukHKAKwu/ociyttDRjBmsCWE3gyoYAWXo8BVTTJHtako3RIbkjAYksOsNmsEh4ttiF6CQJ + 79fDdCJjficSkQVVMScKGQkITD13eyD1HwLfhbdwV3/0wzchVfWGc8LBiH//DdR1q3XhYSzTrdee1CFO + DQWgNW+BUrcKWu1qiME2OMgMDSoCVrUqWNoYxcaOOMhihZNV6xX05ya5hSK6zni3D4PoAdkolS2Tmvgq + PZLxDkIIedMnouCe2bj44ktw773bjkwPGzYM9z300MtTJ0/+7Y7y3L+DdhwAvv9+DLXKJ2B12lnjM/Xf + 6eB9onR2IL5pI2I//4TOeR8gXrMhEQZMnT2GDACwmpxd5XhzYvYc6ZRFv/8N8obtiRCp4A+v2oQ9R+2N + 7qXFnPmzCSYm9Zlk/mzREqzdtAllJcUY1Ls3BvftDY9L38c0gjgNzCABg0oShjkCNX1aI5cCwc4o3V6l + sScg3+9JPBLTBjw04Ng9/H4v//TRgPZ6vLpPxPApMJBhbaHQtZmzMUhbO5kSBApax7pVjdUrV/088ac1 + LimujUvwbyI5R0BKxRqTAbtItkydGGOx5znPZtEg0gAiAQBp1zSXr+KXljTO8Mw7zyW8R/9bdBiOAHaw + asTlTVxhdfDyioD8IohlVWTH94TgL4BY2YO0gIP59OL4x+/y8eOYPA3qqhVQVv4EjVWcYm8S6SSNoQVq + 82aoWzcCLQ0QA60QqV1lMhUaYgJ+boljeXMcjSHdL8CkvWC8DwtaM8k/0OnGZGL+AgKUmAUxmbbBF+os + 1KcGs1RigWVK5uWh4PF5WPDjRhxy4GQ+Frqifv364tG7777t4OOOv3ZHee7fQTsMALGlS/ahJvwYaQs1 + 8HHDUJykvlRQgPaXZqPl9ntJ1fOmqPxd1nNLT0YxPrPZmlqMbLKKAvQ873d4tymINd5CjB8+jNvz3Nmn + qIkXZZdkkp+p5u9+/jkXOONGDEevbt3Q2tGK5avXYX1dK9raIqS2k11PAyYaJSZVSELEjMlv0NVAWTEF + lsZDRyYxG9LpEuBwCPD7BBQXOlFc5EFZkR9V5QUoKy5EEZkleR4f3AQMKg04lbUVDSr3+rXwfvUl3AuX + QtrajjiBh2YJVyWKS/IXylKyCtni7hqs62PpcjJH8cxUfZ8zH6yJfGqqBseZ3aWr8bp0B4/FC6KpKyt6 + eM7047BMPsbcpL4LpRUQiyogFJZxhhfcfmhmJU0CQiE/H87xU/kzxz/9gLnS4Zp0CJRVyxH76E2ogRZo + DRuhtTRyKS8xBzTdJqpJaI6JWE9q/qrWGOqCCsJxNeHp1x9df1Fdvdewj8ePcaT2s7sn9EVjNqCzSIOn + TOPvJ5htwF4vHEDh1XciPPV3mETaZqIoSA4aUlGCF3oO+LZ8U8PpPRrW/9cUwN1xDWDZsn2oJT4mTssE + AGNkidSJHS/MQeu9D0P0+VPumFGRJYuET68Lxx09RsUhzdS9iTkFOQrxpIMxu6ofxo7eG25i8s+pQypL + SrFH/37ErLoVJ9IAYCrn2599DidJkEMn7E8SXMTCZcvw/uersWptDOEwDQxZzwHLaS/D1GAMM8X6Ammp + c+w9mfLDGIPNiyrIA8rL3Cguy0NJRSGGlPkxsrMdBV9+DecPTGUNQ2Mmk5k4lX5pZAHCXBEWayjOElIV + tnGs2fZWDz6TgpKhzrM585zhHWwjc0lTUuOFEj079bfgzSdJScxeVELMXgmw7/48Oj+Pnsdgds3oU9U4 + n4EAXU8sKYNz3GTOaJE5T1A/R+D57dmQl36C6MO3s1pjXNAwkOpQJDSEVazvUFHbwSQ9aWyk4wsG04tZ + tJc4cbKTwHcSMf8wh4vb+YrZjIrunPRUUZ8VG0hgAqxpFUTCcI7dH/67XsJtt92Oa6+9pkt+GUrg/2bf + MniapA2xmHBR3001r+0g6+1S2nEN4Nuv+ghu7yekwvXidhiTtKw4iKneMQlFI77lwQcQfPEtPXabbaB1 + obZyJpeNOC9T27yk9uV5SLtw6vMNSEKKThf83Yrwcffu2NhnMPYfugda2zrw7FtvYSTZ9BNGjUJM1nHd + RUy/9OefUbO+FscdfBA9bgzvf7kQ739Wj7otAmd5SRRSGcyai2B98MR+qyRNiZslzteMMaQSWIVp4Mfp + mP5SJ45wNmCaUodube18BRmNzCge2rL2zHaE1XKUuM/SoFmuq6U6zrlkd+hZdoJTV3klJ0lQBzGUpHCN + x5z7D2b2sUw7NzF1HknxomLqn1JS5+nTS5ahx6fPxDHVCF4oT0naDfxGotFMGl9ZVyPJrm7dRBpCMbzH + nQV1488I33oxXN27wXXjLCg/LUbz325BU0jDZmL6TQEZ9Z0q2qIK97NwpjfdA6bfCEnFho9d6ic/jdmD + CYj6kb0ftThxzAU9fD01HpVImT9gbT42prxuFM2aixVhJ48GdARy+/aGlBfivX0qUBTT0LFFjMfbcQOB + wB07yH67jHYYADbtN9Ev+FyfOvfoO9o9eC84qqvgqOhGan+JHkTlJcIFbL3yMkQ/XaYXBsmWBJLm/dNk + XSLwhRepoxyVJXAP6wd3v96QyFYXPMT8/NoOfg+JSUu60CM/rcfg4aNQVljAPfAr160Hi/szdZvZZ0wK + xKnT5n78MUYNGYKBvavx+oJP8M6CrWhqkkgjyCxllbA7sqnK6ayW2JeaOse0HCZZwqRgMg10GDpwZHwz + JskNqJTDpIbSc3HPtcU80tIum8U7b33ElLCd5bHTMymt8+FhMAlbvpVpJ2zQc0Z3EZNLCpf4okN3sgnM + fPORNPcwiZ7PY++M0ZnqzlRzwe3Ts+ssTjNdU7NMJRaNEIBovCCButbZBq29FWr7VqBxM7S2FgiRDgik + 1rsmTIPz6r8h9uV81N9xHdqKuqF+j32x6aefUb90OdoiCk/cYdfmw03QU3dZM0ppjJ8oTKrxicgoo7Ez + jZi/isA4alEzuYuH3juvl8JxS1OTwGH1wxjWFLRQEHkX3wDnsX/BCb89AS+9/FJOfhlbVYQ3RlYgT1DY + KuIIbRURbRYeoke+uM+GX2+dzB0GgLWVfaittQ81RT0AJBXEQh8xfzGkMtqqy0iFK4dEUj/41nwoG1t4 + a2pccCRvaVaXSfQBDTgxn2yxfD+kUmL8wb3gHjQQUlERBNVQ+1VrXEnjalxNazveCsQxccwY7nHnlzLC + e6rl7/rGJnz4zUIcO3UqVtWuw+Mvfo/NmyXSDASLpE/jmqzfTbKGBSwcKCQFXicNR7L0sa/WjmPiGzFO + bkS+JpPUEREXxFSGNb6n3ClbaC3tCVKeytI8Vp8Ak+hcqjO13aVLdMkh64zuEvkkG5ZnL7DwmrcQYgEx + dz4xOdtImsNlMLooJRHKtBM0NQ21RKPkl6jXCKD9aiQIMLu9tQlqWwO05gaIoXaIkRCZcDL3hRDsoIPM + r6ZgFG2VfdHQdw/UfbsYbWs2kkpPzx2Nwkdah8flhoc43isIMEqUcCkeomcIsAxOIyzoFJI6AHu6CO3v + 5XTjELcfxfRsEVXLaHd/LxWufI0rKolgldmm6dxCZoBj1Bjk3fMa5r71Do4+8shEGDmdpvYowksjKuBg + 1ZGM63AQaBQeIU3gV6t/ucMAwGh970Fs6aKj+DsrhtSmTZc0DG5lXi5M6lYMd99qSOX5enUYk6GYfUY2 + IU+uIXVRZDncXhd9EgiQxNE97qqR1WUJSUFLeORY0s/8tZtRSwN178GDuJQ3jkgh5o1fsWYNVtbW4sjJ + E/HC25/gjXebebyYa6GaZSQI1r/TWssq/U1RkGjJpLYQpusyK36S2owT4xswQm6FW5URoSGrmN53JC+R + YXbk6i0t+/NoxmDndrlTt2F1Jx1pP5JM78ikOrWzUy/SIhBYw8cYnbQq5oxj+fJMmrtI9Lm8Fs0n6XdJ + MLo1NsoAgcXdRaOv2DHE7FqQpHuAtpYGCG31EAKM2TuhEBOzjA2ZuKCNmL2NGJvZ7/UhFc2kxgejLEog + wk1isog0uUK3B6X0XMXUniwTj3nwHUbWl5PHVXRivR6HXohzvRzHCjmKRiXOAYK9RpSea6DTg4OI+b0C + 0wTSsJUe21ulcoefZnXopymBer6Jxt9Z43X+JRTc/xLCPYdi8oTxWJRjctCB3YvwMgGAk/jD9DWw/wwQ + uI5A4Nad4cUdpZ0FALYi8B8z7FTmD2CJNT3L4Bs/Cp5he8BRUsJVd93OUhIqIvcfsSIUhKZqjHA8Skoa + nauysBsDlQ69qzSy19Vw0AAF6qTOIB+MLHVnbmkZ8idNQ3VZCeGPknwOy7MyAFj043J00HnjRgzDrNkL + 8MXCsC79IaQJeavn3HKl9AnvKfp3sjlDNOz2VAP4s7wW49UGSKKKmEMk5Be4cGQbAzORe/KtkpO+S2nh + JFPBEM1NStjNCbPd8Bfy5mXeMYeeRyEwzzjLkS8q47nxQjHbKiEQ43O1nk3cYixk1vZLuLjTPICMjPty + qa6pRu1HmdTgDpLsrcTw7RA6mojZGyF0dvBFNOVwhEdjgsTorfQoHWT/NkRI2yf1vTkso1PW3Tt+ukcF + mQflJN0r6PpFtHnYZmRoJh7JDIMCyYQwC/6aNWcY0zMwWBqP4gsWJqTf9iJQm0zvy5pKTgNRNgaZtz+v + p5owlaz4lxjbBEii16P7ajoD3BfFPn1/vAyuUy/HrEcewV/OzS7MT+pZiKeGVyKqKKZbIWGmdW4W47FW + 4XgCgbk7w487QjsFAOt6DbyOpHdKbTOWkAOPA/7Jo5E3eX9IxcVQg0HITY20kb3XQt9JGrC0TL61R4lj + 4jRQ4voUTk1PDGFOFt4RUSU5JTXN/S3wMarirYPGYsBJp6GQBrup8qebzQwAPl+ylP8ycugAPPD0Aiz6 + LkK/mwBg0Zmzaf4JqZ/N2E5ShOyc8VoLZrqXobIggihJCG76iOY7wMhc0/R1E1lowBzhpNoKkstyv1Ti + gShiNC0cToBAtkMZQDJGlQaPhNR/GKn1RTwfHkbGZIbansLoog4yzDzRdAeswLQ6JtXJZheCHRCDZKsH + mTrfAjkSgUJqfDwSQ0ecVHjq/kCcSXPwrT2mEOOTWk5cFyFAZwxYREhVSffo4XSiBzF+Pn33mok8BqMn + LL1sllgOK8zsEsnonjdJYGwmrYvF9/cw1vIzF+xIpKGoemQjr6/KE5RM6W+tCcivG49BrKqGc9w0Dp6h + D+chunwdnUtaw5ihKHz4NTS2RzBu3zFYu3ZdRt+d2bMAjxMABIywdMor0U+BWnGdEhb277uxZpsFBnYl + 7ZwG0GvQ6fQiTyeKxJJ65+xTicJjp8E9cADi9fUIL1mG6A+roLQEdKke0xWgRHXZBHRbQjVm7wjGoDf5 + Lm1ACEYcd+6hkzHsN8cgn+xYxUw0ShswThp0ny1ewhN89hs5DA8//xG+/jbMnX9ZmT7lU0t9rgyjMCkt + O1UJDxV+h2NKN6FNc6XMYeBHKizEVQrH6AO46p1kZE2viptYVRJJQDL/Jsmjrv0Rsc/m6cVXU14yeQpn + 2vw8uA49HWJhRdKpanoBBTGpzRiAy+x0LUbimavurRDDpMfEO3k6bbytHQqBjkL7OzsjaItq6CTVvUPW + mZyp8R2kupNQJwnHGF3hMXbWxy7aWF59JWN6ev7u9I7FZLaRwccxUeGpXBYJn+YPyTpS09s0bTe752ex + ML6MdmK6Jx+jCVjDWrJNrVET1iy+HsTEJYbdn96tgq6h8sSkKb8lIM3n2pUcDKD1yTmIr9oCqZDe7YWn + 4d5nAu66cyauvOrqDF65cHAV7umfRwCgpTwv/87mH3QKCNaKs/rW1vxlZ3jyl9LOAsA0asx32SBXY3F4 + Rg1A8clHcQkUWPAZIguXQ2nv1GsGGmWiTfZMqdSK3LHt9BCO9TfuGKR/bx40DoOOPRFFpJKZGkC6/HSR + BvD1smXY2tyC6RPG4anXPsZ7H7WTBiBZbiBkMn+2iyHtoY2X4LYtKaBPVC3CFG8DdbYjczCRJHFOPhLS + kH14nfnUS+bw9CVegtTPulWIvvO8HmVJA4CEUgLD851fCKl7HwiF5VzTEOl8LuHjYQgEhEpzEy/bpsUj + XKtgKruc74OcV4BAQwsaatYToIloiQok1TUu2UlZ4/n0jNFlw4mmZ/9q3AvPmI9J9DJjY4xfYkh4tk/h + kTVd0ieSm9IkeM6pxWnNnx5RYp8sy3gLgdkc0lYGEeMfSja/AtNlbJkTYWAfqxOY31tNXYgEyevqU4md + cE87CUJpNdkPUf0Y0jbjWzah+bGXoGxqRvH156H4iuvR0NCA/caNw9p1qVrAZXv2wMzeXgQVi5vQ+q4E + AuGtQiTSKI4nLWDJdjPhTtLOmgDDCeU/0WJykbNfGUrPPgmxuka0v/IulM1GFSDLhKBEBSAIGZ2Xs7O7 + ImZHUwe9ecAYDDjh1IQJkE1YsCjA8lWr8ePq1Thm6gF486Nv8PJbm+kSkuEERKrel3afZHOlG4jWZiR7 + V3XgmtJVOL94JXW2k3u2UwY5A4BxUyGNmEw2umJk6qkpvG8toKrzuK4dadEQ5C/fgrpuJdcUFFnR97Mw + Jw1S7uCzYBNT3dkxjOEUQeIbg8dgRJ/r7t1vIuLdq6GWlOPnV1+Hf+wkFE85lKu4Sx64FyuffxWCz4+k + saE7GxnxjEf67qatkKR6CYmxMmL2cnquQvruZk5QQ5W3Mny6VE9p7lzMnkvlz7TA+C6m+tcpcZzsL+TP + JZvHwtQy9DHIZEVeLw3uQsMXlS0YJMcgDR0N536HsxleKaooC213fvYl2p57B56xe6Fq7isQ3fn42//7 + f7js8stTnur20dW4opsLQYsDIt29pOqmwLO9a1ad/gu4YKdoZ52AbB24r0jiD88fSIMvz4fATyRRSEwI + rmQm2/bydebqzWn6WhapLFHPvT1+BKp+ewqq8vN53r6WRZCyFGAm/VkK8FFTDsDKdbV47PmfEAhIPG03 + c6CliaZctqhmOZGphhpjBBmXFK/CQd565EtxECxxxlOMXmbOI7HfMDhKyvXzmENOlJLXjsskaPQVl2Wm + VYRp4BHzRzevh9LUAIUxP4Gdq7wImtMN1Z+PYF09OjZsoeNFQzqriNFgC9N3lgLfSQzPmJ59hoNhdD9w + IkbddBtxsQfNK5bhm+uuwcRbb0PhwD0gd3bgsysuQ/PS5fD4vBwAXMRITJVnG2PwAnoGJtkLOLMLCY98 + QqVPacrsC3GmjIlsJoD1+CzMnjJmoAPSWmL810Lt2Ic5/Tx+xCyDIaVLFb1EOLP9Rav056EUJLmT3tc1 + 5XgIVX2pM1LD9cyprVK/ND3wDJSGZlS9+hw84ybRmApg+vRp+PLLrxIXvX1wL1xeSVqDT+NzL8yItmpi + v55ygWi70N5ZK47pV/efKYO/UwDAiEDgcRJgZxf0VxBt0xBtkHi4KaOTtqXWmb8bT6UZJwoWUZgykci0 + 7RUZ7+07FHknnobexUVkziopzJowF9icbto396OP0a9nT3SrKMa9T34O5q/hjsAMtd86WtJjQZbWS9Pn + 2E8xltVH3/o7w9jL045+jiBKCBTyRZk7qHiv02Bq05w8F4CZSCw82K5KfOwFFRHtMoEEWFzbgU5Z5NzR + zjIKCMiawiqG7jMAf71wKmdylnK9+O47sPaVtyH5fNwPwvOp6JOp3SxsxrrEwSYqMUaVVOxx+80oG7Uv + B62W+/6Otp9XoPf9D/Flr7XGzQhfcSXEpk54CWh8fMVPgZ/PrmVCOy//n+aVz+hL9jXbxCVk2vr6cclh + aU0WS8xfSB8ymsWVRP/eigSwkdr25LwilFALymn55ObMBE2htqjQ4KtUU/2h1NSxdjJVomyCm8JzH/JP + /QMvM5ZSAZtd1k0MXb8FLY/NgUKab+GMc1Fy1Q189xuvvYbjjj9enyREN7yzV2/8RnChqUBGnkfjUa0y + vwNFftbPrPZgckp0cIM4o3rFqjsM/mKSoTfbaPyzOdBl1A4uet4InfZB39qaDTvDvzsNAOt6Dqqmh3md + VKnRLMkkWCtmlo5Kt6Vz3TVhnKWF5YxzUmaxGeQilfrDkUMg/u40DCwr4wCQ4WQxPlkq8Hc/r8SylTU4 + bNL+eOvjRXj7AzJVNJHHsVN7N3XAZnB/hhlg+U3QZ+yRHkQSSGdeJ8tqNJec4adpXLqbA0+flaYr24zx + RaMROZRY/JQsgcgbDuHPJ43EtJMmIBKR4aTTai+/AssXf8fDVH4ClFIWVqPRXEiAwdJefVxK07UjUUQn + jkTLFVeB+eSdjXXo9tcrETl4Ehr++Gc9Hr5sCbpfN1N3OprVkKyt06VGlOU3S0cktIEc+/RmTAJAyhDq + wjxgwFSvyphD0r+v5MKR3nwolrSc5ONp+so/1N95fVRekyDhGyVWi3YICG0U9clAbLZnRQHKz/+9bs5a + a0pQG6tKFK1Pv4zYyjpeKMQ1Zk90e/0lMgPy+JJhe48di/qNGzHR68R+xWV4M9DBowCjS0pxxP77Ikbm + qLdhI8b1KkDvQidPZWZJQnIYX4TqpCO0GC/vdg4922hN4OkLgtXfQR+19HF03w013/0ipk1lq52nLSMH + 7uUq0j4ilao01i4iXE/2VUxHU5OhUzChC+nPpUBiLedUFS/bAHDHYvhyQB+0n3Ua9upZzefcp10yQXwa + Ll3wnc+/QJ7XS1pACf45ZwnWrROMaAAsTJ0tHGC5arrnynxA06FoiDhj7h0sTui0xk9eW0iLOJiRSWbp + R6kxmTtrstKIE8OrUTHjj+jYdzw3F5wtdaiccSO1ewAamV4+I0POabHBE8lZdI3GGy9DaMS+vK1Lnn0K + hU+9SL9dhcCkKdyxWjx3NgpnvQDV4zPUU70PBSHrC2S3063HdGHHJ37KBgzZDs0hRJj6/wmp4wsjIRxG + zL8XmUZRaJZHTE4+484/D9n/fRR9jBptr9CYDa4nxo7rY5eVuXP2L0PJmafqEZpEhpDAE4Da334Pne8v + gujx6HNgSEno9uocuIeNxuY1K/GHiRMxtFslTjnnHFz+2D/w6SJjxqBDwvgJEzDjvHMR/vpLLH7iaUyu + 9mECbTJ3bCMUrJXWy50YKoip75mycnKMl0mfD0k8qk9tTRQ7QLsEANoO6+ciA5et3jOCzwsi9SncQCp3 + u5AIc2QVy7kGTLZ+ztHxLuqkZT1Lsf70U7H3kKG8PJOQ5XDzlqw0WJAk6HwCgX49evL6fi++uRaNjaIO + AtabZZNUKQ+dQ8xlOz9FMnbFIcnvTEOIkcTPFxRMVJtwQnwDRkaboRY4seHWGxCpHsQlmff7xSi//nb6 + 7uLZlZr1Ua0uijBJ/70Ho+G666GRlHQ2bUHlZTPAktO3/u1mxCtJ0xQ1lP/t7/B9+AVUUnszKg51MWq6 + rA/YlSMoiZe5KxabxwmZP7O/I7TzpVAHmV8aTvQXooCX8UoN8CQuwxJ/Csn+76UaK1fr9w1ukCAHdE2A + H8cAoB8BwFmnGABgaChkFoWXL0fbU3ON0K3eNizfpee/ZqG5R3c8d9rpqN5nDA6/4y40xzWM3XtvbN2y + OeW5e5BZ8NgTj6F4/Rq8cslVmNa3AOOrPbw+QYTGY3irAbpZzFMWUveNGwalsVWObWjYv2/dmh2qM7hL + AIBR26H92fTGo61XjgVIctGLKOHUTs6aaJPu6NEszpEcg4aRRB3emO/EghOOxf7jJyYSXdKFlfV2LCmo + taMDCxZ+g4G9emDd5ka89d4WtLWJ3CGYUBWR4wGyLWCQtVmzcECGlmHdp0tq2ZgnUEpGxBSlAb+R67Cn + 3KYP9JiCeO8eaLj7FigOLzSyQ4tefwFFj8yG6vchJ7GBrkbQdPVf0TnuAH6v0ueeRv4/5iA6Zijqb72V + T9aRwu2ovP4mOGo2Q/M4Lc2dpX5DjnBcsplyVBqyNlG2fs3WeebuLN3B1P/13PnXgX4OF47y5XNJmsL0 + Vm87AYCnnOz/KjWxLHi4UddczRQJPv5kKwDoWUIswUolIdP86L8Q39BI6r4zcX0HqffRodWY/cMP6H3Y + ofjNfQ/B7fZh9rNP48yzzsr6PsUlJXjuxRfgX/ED3r36epwxqgI9CiQ2zQDBdSKsthfXMljkiC3JuEcf + lJxxHFqeIBPk5w3T+9avm48doF0CAGu79ffk9xfuIN67SAkJXIXi0T+HxrPglKAOAokhn03651D7snp/ + E+ZCMhfglekTsdeRx6CIGGJb1VkYMX/A5sYmfLZoMfpXd8fW5ja8vaAO9VtFPiVYz8/pQmxlsxCyhQZT + JL6Qdn4y7MHGa4xPDhLQG2FMJcY/NL4FA9QOfhibTcgxIhJBZPwoNFx9LY/lqx43yu+6A74F35DE9mau + 4GPejqR/bMQANNx4PVQCDkd7I6quuhbimq3oPHk6ms8+hw82V+06VF5xrV4ZUxIysDrDv5NLymvZ2sFy + mtVasnr/c6luaZ+Jpb0N5+anpP5/FenEQaT+j3F5Urz/5iBKXIL4yNtdg6dUHydKRECAmI3XARCS907V + AAwAIOnf+c1itL/wLkRX6sK37Dne2LAB8YP3x+9emMOZ/+UXnsff/n4P1q5Zg1A4+2JApWVleG3uG6j9 + x+MIz3sVp46u4E5B5k+LBwRjLowCsawQ7sF94Bk+BJ7B/dH59WIEXlnwIVThuD6bVrVjB2iHAKDprst9 + kljUL7bmp9HKlsbx0a9WjRDczhHEdx4hyx2E9ErB6XfvSrXclhpJv7nkOD7esy86TzoFI0mtihvhmmwm + q/VvBgJbm5vx7Y/LMaRPL9S3NGH+J2uxeq2KWEyfJMSLyGQzftNV+ZQoQVKiJ+5qqjOWD/YTM1jiYJlx + KvYiZj9ErcekeAOqtDCvGxAlxrdWBRJCEQRPOwLNp55JABCHFAuiYsZNcK7eRBLbldFGOtMSrMQiaL3s + HHQcMI0/K9caZs2G5hDRculfEJgyjZfMzvt0PkrufJSu5Ul5zoQqnd5/6c699O7J4cNJuabxctY8kRSv + f1aHof4n6x+W6PNaOIAG6vdjSf1n6cVmgc/0wcJPU8ls6mEU+4A+ISfSICQisYl7pAMAfx8FLY8/j9ia + +hTpz6Itq8m0fN+t4rRXX0bF0BGIBQPcJI1FY6ir24RPP/sML77wApb9kLku6PBRo/Hsg/dg/lln45iS + CLoXuxCgZwrXEbD0LIZ3n73gHTEUzvIKyG2tCLyzAKEvfviANJLT+mxcvcPpw9sFAPGlS/3UmtWQ48OV + QOtENRIeIxUUj3SUd/OEF3+FxitvoEbVp39mvWpXYT+rSmcMFpZJyOL5fJagQ0JWeW65pkQDtznPhXlH + HYaJB0ylztBDYF0JKpP0+oAkVaMRlBYXYe3G9Vj4/RosXd5C2oDK1oMkrUsXC3yuuZTtYXLYNQkGSfoC + FE3QY/vQ57H3IUbfV2vGVLkBw+UWeGmARVj4Kt37Yzy4GA2j6fILEJx6EAQ2QDetQdUMav+Aohe6Tzue + 3VakARgf3BMNt94MxZMPR0cjKkndlGqboBV50XDbDER6DeHOqdJ/zkL+nHmkTfjTUiEsFZy2R5U3HyGb + BmftlJSvBtNaZ2KmSf8ULVLTAaCNJM+LoXaeo3CirxCJZOp0MNSSuM3sf2eBftfOLSJiTYLhtLYcT+3r + YAluhg+A1b2MrqpB8xOv0H2NEvgwQ5DAK8316H/VhZhK/NDZ2qpP+GJzMmjQuEhzYFWoWlta8OGHH+LR + xx7Fp5+mrnB3+YwZmOwhM+zZB3Dg4FIEmgk8/H1RctqxfNUtZruEvl+GwFsfQa5rfU70us/vs2HHJH+X + 3afW/OxVQpGB9Aaj6E8yrDGMNra4ZAEvEMGKcTAGZct//bAEDZdeTSNb0tPDskmCdEZHsiPYjD9WninC + 6vdx+5cazeeCh9UAYPZOWwd8gohcSoR5bZcs473hAxA7/gTs3bs3opZa7VkVCEOqs2eQDOBioMEAIR6P + YkP9Fqyu3YzNDUG0tJHt3BpDc7OG9g7TFra6Y4FsUQGeGszSX5lEE3Q1P59+7amFMIKk/TilESOVNpRp + EZ4iG+HSXsit9FBbCaRjNdxyNcIDRnDJmffZRyi9/X5obJZajsKdLK+/7fw/oO2Io7k6xqR/4aznuSNQ + HtgNW2+9DarTw2AHZbfeAc83y6AZq++kXCqXOdZFVCeXfyDxG/TID5JyP9PrnyXgYiplzPu/UolhXiiA + wSz115PHw2lZ3RWWmGBBHxmSO86/dzY4EWtzJCICCU97AgBONYrQSGh7ZR5Cn38PkU3iMi7pYj4IAuaP + ij2o2WsYBgwfjYv/ehHC1O7W5DYGCCwjNS8vj/aFceMNN+De++5LPGPvfv3w4qOPYP2lf8bh3UgzjBBf + OHuh5OzTobS3IfjJV+j8ZOlXgor7+m5Z9yJ2ASWa6eO+gxwFkdi40n2GHld1001jXXnFIzRF9pktoqnm + VEmjsitLP6UX6Xz/PTRffwefy5+I3lkQOznrSuMMxpNUVJWraDHWKIVeaPkFPN7qKSuGn1Qcb2kR/D1p + YC5ZhtVka5VTx5akd2JaB4t0zZBbwssHT8aoQw5FRZ4/ERLMqrkmOMzMDk8ex1Cblw2nf/F4jJsUTYEA + Fq5Yifc+bsHmeo07C0V6B8lyddVgdnMQeVmYjZT4KiWMwUIIA5QO9Fc70U8NolCNGt5rEXEzjaULByn/ + iMmQq0rQcNdNUPLKoLocKH3uceQ9N4/b/yltb54ajUPpXYH6O26hc4ohBZpROeM6ODY0c4dS6IiJaDr3 + Aq5HO9pIM7jqGogEejDmSCQ0dKRJ33RGzoKF2Y7J4Eohc2fW9PAcvgemen9BzPc52f8HePwY6/Im7f8M + EDAdDioKBjngGjqQh9ICSzcgWhvmGZrW0LNG+9zD+6D41OO4BqAEO9D04DMklCK6tmU8KAsefRwJ4Xmn + gnkbt+CC88/HXXffjUCgw7iWkPoMANcI2Dj77fHH473339fHHY252bOfh+/12Ri7cSFcrKDOZoKX0u5Q + Wps/k7e03iO63W/32bhqh0J+2cgKADPpaS8k9PTmD+yDotFD4ansBm9FD0g+Dy/F5fD6INHm8NBGzM9q + /rc8/hDaHnmeV/HhjhYaVHFjzrNepEG30RS3CI3ZTG43JGJOb89yFPbrC1+3UngLi+Dy+/WS2YzxVHDG + WzH7VTQt/AmVdJ8Cg2lzqpOsUeMyfu5RhE8OOwxTx42HhzQSJa0+wHZGpRKNwzSCdY2NqF1dg6KfV6Ds + h41YFi7ET1I+togehODUV56mzafJnLF7C1HO5IPkdlQKDABCcNFLiUZbsGQeNZuKb322dAkKXZWPDh+I + rbfcyplXcIoov/lWeBYug/u6K+E+7RREX3oVkVvvTIJGZyfazzkVbceeyBuwYP4bKL7/n9BcJPGZX+Dc + M9F+xFFc2/Iu/w7lM27jJbfT/RVW5swdmTEkeDYHaWI/LIk+qXa+1XnZFWCYgkAv8ivg3UgQK+MRHEXq + /wDJqQNArnPZLuqjouNpfOx/MJ/jrwTaEFq4lITZQj0l23w+ttbEqP4oPvkYvuRYaPEStD33Ntn+rsQz + siTSTvoyK9qOR5oaeRr2U08+iZNPOQVtbW1GExrXs4RTWUv4/XlYumQxDpk2jWsEjG77299wQDyAbi8/ + iPKKQgQ2CYHwJvku0SX+rW/92l+0pHhLSwtbuPA82u4rKSnJupZmEgD6DPqS/tqPjWZWtQWqDH1OuIrC + PXvxFNN4KEzCn0k+FxysHhxZAvG6dRDCMp1DTE8SykMPnTe4NwFCHkSvFy6vHw6Gdm4HHD43nIRqHmJ4 + VsuPESuUyfLeIx3tkIOdCDW2IFS/GbEt9dCatqLEp8FFqkK8Q2CFYRMl5szYdPpCI8wU+LJ/d6w84nBM + GTmaZ9UpiprxwomBqFlDfqnjhtluW4NB/DD3dRy0cBF6tEfhNMYHk/Ttogsdoour+uw+BVoc+aSOOrgt + q5sXcW7v6979XJLTOkATTMb/TDHCeQSg87gpaD7rPB67d3Q08AQgcVMrPDMuTQBAmACAn0b9oVYVYOsd + N0Mu6QYxEkDltdfDUbOJr+EA0iAab70S4X7DuPlWNO9FFD08G6rbq2flkQTkoSfaxxZ34TkGaSp4ciWl + JLdrQpaQocHwiXZOuEWSGhOMJLCEPmYuzmL1AVgdopouYF6NdKCDNMDf+gp4+q8iZLmvOaFKYZWM4yg5 + 8yg4+w/jk7NEvxftb80nAPiGzRpLjgt6f9eofig55Vie+dfy7MuILF2lV1Qy3p1NemImyOXtjVgU0NeH + HTd2X7z++ly+EpE1MU0wyt+Zz85zODwenPq73+GNN97gxxx9womYMX0S8mbdgl6lfkRCwjNln6z+/S9h + /B0FADZzYZxV9VRJKriL/Bh+welw0KAINzciHgxBDcVon8pryTmK8uEszMPPT76O9rUbMfz8U1E5egQ1 + bNziuCEmIFCJETPFO0OIB8KItLbT3y1QOtqgtgahhTtIwgWIgWNgAMs3j0OPu0Iv06SEBZ5bEKd2ViPG + CBJTRQX706XIeH/Pfth86KGYvOdefMagmZOdmqOjL2yaGChWgGDqpcOJb9esRtVzz2LCyjqEqOMtSYqG + CWCYRszGZ20GMWlqmmaQdTAKlow0837WXAj65zrkYLjOPD31odgnW2GovARKQSE21G+F9ONSVF5zMw0q + B9yXXQTP6QYA3HanDmDU3u1nHIfWk//A47IF772JknueINPBzTUIubocW++8g/72soIJKLv3Xvjf+YQ7 + KAWvCKm6ElJRCVTqI3kjAXIwxk09bh5YGy31FZKgIFhmQqYEQtJLwidLmlm9AJqxL2M5MuOiTIcKUd/O + DrWTHS7iJBJKDtOgS/EdWK7JkoC8ERT/4SiIvYdzuz+0hCT7v94hYHCm+FFYZSrv+OEoOuE3kOvr0fTA + c9AicsLZzSsQ09FzY0Hc0NKAhmjS73TXnXfhrxf9lbSA9ozEJutqS/mkSb9Aav+ZZ53Nf9pj+AjMvu0G + uG6/CNUFDuZDern43TU7tJrQLwOAvoPYMqcH0NaTHrYffZaosix6K0uJqX9H9kgeRyz+8sbqqrzgI1tJ + JRLG0r//A6FNW7HnX06Cg6R/uKkd4ZYWxNoYk7MZgiGC6xAkOQwpHiXbTQGLWklsYQ0Hm3Ktl5sSuCNR + Z05NSy4qwX8xZhazRA65U0S0jX0S48nGPnMhC8aYGgOB/miYzkBgmF48xFItKH3QZvtbpAf76OuvMPWF + l1BJdp8qZUY5tGwDFF3Zw6YYy/K3wSTuk46H56rLuuzcVRs3wjPvFRTd+yQ0D2lVl/01aQIwAGBrXxW5 + UT/zFsSqSCMjKcmkv7OmjptiLC8gPGVvNF58OQ+L8XDidTdA+m4VvMcdhIIzzoRrzz0gFpcRALQgvnI1 + Aq++gtCLb+qzPb1eC1dnaRPDWZBtJnV6cyTCgEgCohWWk5pa6vkMhppYBKCzDd0dLhzNFgmFnvospIVd + TXNDi8rwD3Ih/+TTgPxyVsUGLY89i+jqZFKP+Vwqaaa+SaNQfNJvEPjwE3S88pG+kpEB1A7qtzB9/iPa + gQfbW9AZSQJA//79sWDBAhQWFiLKNOocDloHaWLNzc2YPGkSnz9QWFaGV2fNQt+HZ6DCGWfzQ76mEycU + z1+j4BfSLwIAk0gTYI6/nrRV0RP2pwffy11S0NvXo6zcXZzf3enP60aM6mZRLIUGkUzMH9raLAdWbW6j + Vt5Ag6u+wC/v51aixS6JVCSXweCSwJmcLc7Ba/yLSdXPmiefENJaUk20evHMRA1zn0yaAJu9FW/T5x+Y + eQcCDQwHwf38Yf3ROC0HCFglUxqxyEArSYDlb76Go9/7nF4WKaW7UwZw+njOcJ5tayWetE7p3RuO4cNS + f2STnPI86Dj+SCg0cNujYZQ8eA/8cz/kITvvJQQApycBQGSrFZ98OJr/8Cew3Na8Be+g9P89Qva9V28j + 6re2P56G9uOOh0DamqtuA8ovvBiF5/wBJdfczB9MDTZDYdV7Cfyl8mr+GKGvPkTTBZdB2dDCi7dmZO7l + cKxkmDtIPS+ljawhv1yxXOjSt1aJ43XSHoeSFnOQywcla4cYtyFTUPQIKDntEDgHjOACTAs0EAC8gHhj + lHv5E6ew+xMAeCePQuExh6D54ecQX7WZAMCZuCabAr2FtM1HQ614ikzYuJzKo9fMmIEbbryR+wL099eM + z2QEijkDGUDst99+2EigzkyGJ/52N474bDb8nS2QRYkVBxlHAPCLS4fvEADkog8qe7Msk3JC0lJ6dJ8o + iKWCJPlILY3QGzWQTd9KrFU/pbamo/2Qvg+KknieZbXtrHP0rYyeMS6yeOyyjTNBNLTjmIBoO5mtbWQy + RPQDmOrvoCExnzSBRsMcEFTdHEgPZ6cTqyC0ipA5+uy/cMiS5Yg5nVkSbKwPDGRcNIc3PBHvRpYBn3ae + 6c9iWX/xgb1Jot8GjcWklTBJ9Fvg+mk1VK8HnksvhOe0U3UAuOl2wCdh6123ItqTMFwOoeqmm+BastpI + FlK5+Gxik4L2HMOfxf/h2+i+eBEqX3wVSsNWtN9/L0Lvf8qLe7KVnqWycngPGY+C35+FeF0t6o//PZu3 + zCfFWPso5bm3x8uKpEqcnAsgpF7PwsRWxYmFAGsIAN4kABhHzD+eQCCeojukRkVY/nzetH1RcORh0Agg + WVk1rX0LWp6Yg3hTTNc+LfdTIzH4DxkLz5590fLwHPByo5bByu7/A9n//yIN6dmOzHB8UVER3pw7F/vt + Px6trS2JRWfN9/WQ/c/8TJdfdhnWrVvHlg/Hm28TUDs1/KHmQ0ibN0F1OFiZ4f1+dQD4JRQ8vP+hJFTe + Nu9gMfVSQ1Rp48H8LT30lO2cjPMMzYD5LqPtZJa0Cjz9WGLmAJkb75EmsHXadEweOow76WRVzZjdxl0K + zAxh896pY34mRlCe/hcO/m4FomaIyDBKNE3I6aVOPFsuJsgGHBYSBw2EY8xoo810ychq58cH9UJw8lS9 + jdesQMXlV1MvR0mqSykAEJ1xPUJHT0XTOecTWDjh/+JDlM18kH/XPZgK1PIC1N95O2kTRXwpsqJ/PIgB + U6bAOWkK6k84CfHFKyH4vYl1AHjlZlbNaHA1Kp58BOF3PkDr7Q/pZd67cm5uY0eqqZSlMRLMn7mUHGPA + ZXIU74YDONjjxxgCgIglB8BaBYiXrYvLyB9djrxjyaR2F+i2ZKQdzY/NRnxzMAFmibCmrMLZp4w7QeOr + 640pwclH5LUHoyG8SgA0h2z9bAJlyODBuP/++/nsP9aPnQQ8rE/z8/LR1NSEa669Bs888wzeffttHELj + MxiJYMvKH+C79U8obG+D8n8RANqm93fSO7JEhWOsN8nQErN4vlNybGABgux+oOQ1LSojn5FIQMAiB5EW + kcVpSHtXsGBoX2yYOgUHjNwbHrYevKKkhJ14yG9THZbV1OCAfcZAdrqw4tU5OPrDr0ibEBLlsDJU0yyk + JV4qaYdub2O7Tjgenqu79gFsenceCi+9lpjfxR/GTSYAdwK++DIid81Ew8wbEek3FGKcNAUu/Wuged16 + 27Koy8ThaLzsSlIGCPCUKCoeuR+9L7kQ7c/MRvCfr0IsLU57Gf0dlJZ2eA+dQKbCGWg480ICJkkvw5vW + n+monjKrML3/04AzvZBqig1vCfEzAFgiR/BRuBOHkf0/1OlOnQNgGSucWK2Dygj8w3rCMfE40pKKgHAr + Wp94HtHatsQK1imPJhuTy5yZKaAMAN4PB/FSPIi3WttzqpRutxuTyMY/77zzcNBBB/GMwIULF+LSiy/G + t8Y6AlddeSWuu+56hNkiKWyVpMt/C08wyArA/HeYAL+UWqf170EfH9E2MENDTlOXE0kkQvZjUqaJIm2c + WQZExrJ8ou71jXWwVE/SBAIKPicJtvygKZi49xgUkeTj6wYa92FzAz5dtIjPDTh80kQMGTAAH3z+KSaT + VK1uCUN25I7dZxVeViBjf5NKqTGHEAtVsKXC2Oy9tCXB2PGuE7ftBNxKUtg/cxYUtpoP/e25VPcBxJ5/ + Ea2ffYCmSy4j9dEN/5cLUHbHA7r0N6atsszA9t+fgNZTfsdNC4ns/O733oXSP52Blmv/Di0YNRJdMkcJ + c6I5+pSj6OZL0HzhjawGeDIFOUeyTvr3XBWi0rWBbOaA9RiWgbeENICPIkEc5snnpb9jFlsq6VQ0bshK + gBMGestCcA+qhnPycdzu73jqSXSuDBohQGT106b3rfkny0GYJ3dibktuADCJZQEeRqZoSWkpXn/99USe + AMsMfPeddzBsr70IwAhka9dDuOg4eF0qyxf5hq47vvi9NTJ+If2qAMCo9ZD++9DHG3SXbhk7cwyWDK3Y + msCR5kfTslwnmxnONQIWOWD1CRpULKoox8IDJmHvfcehZ1EhX0uQp2mS2r+5sZGvK7j3nkNRUlCAHzZv + hvzCbEz/7mcCAGfy2tvhpEo8M3M2hSJwjRoC78GTIVUVI/btcnS+8R4E5pNkzifLoNseAGi+9lq45szl + U4CZxDSjALGXXsYGsv/DQ0ZAVOOouO0WuL/6UZf+jHjpKQXNMy5EcPQEvrKvZ/l3qHrwUbgPHo7Of32u + R3pS/DKGV52FOls7kHf6UfBMHo+mc6/R1zYQtzGMsvhFsvVxumBIOFQ1y27LWGAA8C1pOJ9Hw7wCUH+J + TQJCCujo55iluJNhSYc3Bne/Eni6FyGycgOC65OO6fTnyeB/4xrM5ccA4F05RADQtk0AyEVlZWVYunQp + fKycG1vdZdVyOK44la/wRA/1VtH8/8/edQBIUV7/38z2dnu9wd0BR0cBBcQau9h7xZpEjV00+Zto1BiT + qDGmGo0aNbGk2BITjbH3FgULiHTpcL3sbS8z//e+KTuzt3cIooLew3Vvd2anfPO93/f6W3HElhz3SwcA + JgKB3egsj9GfdTbpcCALuO5T1/5WTRvAgGqE9XiwTxTracQ7L2qEo3JXDh87w3hm6i5o2G1PTB5eL5iU + awaKvADDUMOFGej15nNP4+gnnkUwSbqzXGwGo/93ZjCAKvzJoW8dBc/uuyK7dLVoeuLZeSrSyxeh56Y/ + kgiOvBiNT6cC9J7/HUgvvwv2pfKpWALwEgAkXn4JK4bV0ervRfD9t1Bx3S0QcYpGohaJtEqZH203/wyZ + skoRzRn6z2OofOwJSGPrkX59mVbQtZhhliQYKeBEzSP3ove2PyLx75c1O0HhGFgf0IAPDDYEyFvH7dut + gTOFx2Er/DsEAG+nEziGAGC47MxXAO4njeWjDlXNVwgO3OY+iapEwJ5B/9WnQDKBZV5pJicJzxAA/J1U + gBe6Ny0BDEYXkXrARkDV7YH6v2fguvEyUZKNrvm3BABztuSY2wQAMJE6sBu9PSxp7sX+BSStZJEEbF9Z + 3TkFN6AWYTxbrQ7rNj3F15XMoiXnxRMNE5HcaU/sPG4syv0+4YZRzCfOyUEuvL14EZr/8hfsuLqdpAAn + MNCqX0ynjafgPWRXuJqGoe/ex0WZaXF4uojAkfsgRxJH6q2lkDx5/fPTSADR40+AwmCitxP3XHYJvGee + it5PlmENnJCJKapvuolW/w9FopA5XsTE6elj0HrVNezJph+6UP7r36J0wUeQRg1H6s0FWreigntSYklI + PglVd/8aua5udF36I7pmvYfgYMw9wCpqFeXNWo+bMpQUHFtTAZLCEKcBgEskk/V7Nhbpql/4sTHfirmF + BgAE4/q5GvLzqSj+miYpoGvzk/LYBTi6uRlHHHEkTj7lZDQ0NEDhEO1n7ofztp8SGAiX7cWk//9+sw+O + bQgAmAgExtMAPkpnnFT4cGzx35arKizMW2zFLZaG3+/vYjctaRVcOAvvrZI6vNe0E0rHTkZTXS1CxABa + u2lZ6G3vrlmFyvvvxx6L1yDtdA68uhXqJ2yRDrrhO2g6Ev98g8AgDdfOo6DGUshwPrnPKUJN0++sIEaW + TAb4NAAQO/Ro5Da0mBKS/5ILRfRgV0831rV3oaRzLSr+71pi1rgwYJnuRNL/+046El3f/JZIFJIzcVRf + cw38GQecY5sQ/88rQMCXHzqWGGJROCc0ofr230Alcbv1tPPoHrK20u8DUoE43y9ysNC3uymJwfy9FoY7 + jwDgDQKAowgAGgkAMvqN2rPw8gfSVAhpUO/MpzHyMjEAcQeipwkEHuA2afrvjz76KEyfNh1vv/0/LFr0 + MTq7usTCwrp+fX09dtppJ0yaOBE7T5uGUSNHoaKigtTQtKgdoPqCcNx7A5yP3AU1EGK55FACgOc3fTX9 + aZsCACZSBxrojA/QSfcWA72plbvYVVpFwgIvgNVWYO5uW2nsx2DiwF23kkO3w415JfVYUz4ccV8lFH8I + 3lAAqWwGjtWf4IC35qG8N4GcXDxPv9iKh3QOzuZqWipkZBduFH5m964jkF3ZhdzGHhKfSUw/7RBE73+C + RFDVbAiiyg44xjTAt8dMOMpqkF4wF22TpyDGLkC6obKH/wL/vQ9DceluPdbrCWh6r7gUfaTWKDSZRqx6 + D7jm18iyr94w6PHqR7Ju5/cvRHS3/UTzUFfLBtRcdgVcw4fDOXIYEk+9qon1HC/R2we5xIvACYfD9cPr + hBsw3dqh5Qfog8zJVtlsxiYuZ/RUbFUvosm5GLzyisQscz+t3bvhGzdejoKCCwzA5uPUt2uuWhaiZJFR + mqXPfhqzgMdj+2109llAJFK0+3Jh9Z9+z3OAeWfdxirIB5kUgUAUrxIWvtXWhUMPPhj33/+AiAFgI19v + pBdprtzEgWl0Lz6fD2VlZeI++PtsNkfjktVURUlr0+68+RLIbzxHqB7kkt/TSAXo2BJ+2+YAgKlnVnOI + 3m6j1+mDpNLn1QCr+Ibii0QRydv+PItICoXqJscLOAkIUjSZYmkn+iIOdBPGOxMKKqMpUQtfsRq7ilyM + 7bqMD7JmTxA5BzRJPXtMQPK1j6AQc4VmHwbfnvugY85VWmgt83I8Ac/O41D5u1/DPVaLBsz2tGD+yy8j + WtcMp5pC1fU3wjuXjuHXq/b0RpHedRLar/0RcqT7+5ctQHNbC3pv+aMtu020cPe50PqLnyBTO1wATfDd + V1B+3W/gmjQOjhH1iD+phbvKZSEEjz8S/oP3g3fm3ujq6BB2BFF23YLOvLJlLLUXeJvx2WjTZvzG+tuB + AICZxEpOIzkHMAHCiNUwYzb0fTwFANC37yyoPZpozunoXDPVX6+FjacjkogVEZa8gjoA5t/WSWabUNrj + ZTckdyB6tKsN9XvMwMhL5mDKDlNQWhYWVYD4Wq33ZtyzNUNVm996jAPfR18HnFeeCqm1lePjHyLmP3lL + eW2bBACDSBr4AZ39Z1B1R9hALiL057XCCy/8vqgtpojKYW6ybJORLyWlJlWk+miVoVdGTz7ql8VbiAn9 + PAG6/ZmLeVQG4BxXh/QLC+EYXo7aJx9G5PY7EbnrIciBgEigctSVovJfjyFXqncNymWFJtFDK5lIO6YV + x/vxIqSuuRFKTxeURBKescOR+OXNcNY1IkqfQ28/iZLVXei57UGh45s6L+n/mckj0XbttcT8Hig+D8oe + uB+hu/8K9557wjV+LCL33Ccmom/fmah79FFNcEgn0RONi0MYq5lBHMYqYt31e+Vtxmdjohuf+bcGExj7 + 5nI58/qYoQuZmKPlrEzEPnQDBETdBrpW47sAjaGVovseTGPUaxYCdXMfwFGKmVGajUtItJB0Fi8iDQxm + p4IxV4A4PfCHe0nEL/HhnP8+ifDIcUj0RQbMX7DOC5vNg+eWP4jkU39B4LZrST0UJdlnk/j/N2whbdMA + wNQ9q/koevsdvRoLxeh++uIguqIpKRTeldUPr0sS1vMU+67f4LD0rGjdW7MiE1EregrrClHsAYv/WSzP + Cdb/x4h+8tmV61Hz6P1wNTZgw/5HkTSQEEynxmMo/+n34D3rfMTj8UHHLnrUiVA+WQWHl5jmjlvQN24q + ykMh9K5cgdJ3XoLS3YPuW+4XKdnGJJTpmH3HH4ruc84R+j/kHKp/dQu8L74Nz6xvwDVqDKL33SNce+4d + JiB04qGIPvEPDpCAfON9wmPAzGwFgGQyaeayG0xtfDZSYfk3vM0AD+tKaACAsaIzw1uJXWNWAGDDGTO9 + UWqLX0ZIbSgUso/RfgdrEoCqPUNnQBXNQMy5JWuRo4lWLXLUBgJWVa6YNKBPNq4H8FQyhgWd7dj70u/g + gJ/+nBaMaH43dSAA0OeYrg5xenykuxtdl5+Osb2roXg8K2nzLqVPb5n4z7TNAwATSQJj6CoYBA42x94a + /IP8g5DUgtV9U3oBLACA/g9jINWhmApoMLqa4V59tJoRIIgKyGlNnzRBq4iXUIBMJgf3TsOFrl/2g+/D + v++haD3rVBK5X4UcJLRPZyHXlWDYi88hGYkjHbRP5kLqO+okZBd8jNIzj0TX96+CEk2guqoK8ReehefJ + JyGPLEfswcfo2D6hBcgyiVpIo+XM89C9yyxIpM87ExGMvuNKOKsaIR95Ei+RyC2cx+ZpjVFJt+d6DUy5 + Y88Wbql4Im4DAGbqdCZriva8Taz0/LcuAWhxFhC2AkVRbfvmLM1cmaldLrsK4NUlAknPpnM49dWfAUNi + EKDf0PWy+5bBwjiO+M3fHhaBTsYDlV0SfOWkNix5D/h4njk/+LiJLq0wqKp1r7enMKO/Xcn4zIZADkd+ + NtKDQNCD2Y89gtrJO9P8iNlK49ndkPkZpi0ydB8+L9689iqMfeHvGDY8hFi39PeaN5efsmVcpdF2AQBM + PbOaObX+Mhqfq1yyVJ5V9G6yKAAAYzCN/xXhWltfgcG2WzcVA4ZBbA+ykYCU05qgcPIRZyVyjQKWDkSv + OZ5IRqkgXvFIlA9fcjKCx5wE97gp6PzZNej91T2Q/SHt/mjClFx6GiquvQm977wGdeyOg45Z4vQT4Fi9 + BK5f/BpL6sehnMTf6vIy4L9/g0/tQ2buO8h+8LZYzcWEZoamlXP5uT9BonKEuInAuo8x6o/XQznxAuCY + b1nGQzVXb2ZmJsOQF41FbTosP6usUXBFZ5iM/lvTBqAYRkClvw3AyHXQj+d02HUsLtFu2gD0p2AwrSSe + hWYDYEAwpAfDkMjGNtlitOX9xLZ/3A08dIdtQjFIZlglIGmA603IBQVCbRKBZROfqY/u4VEC0y6SuiYf + fxiO+eOfaT6kzcK2xv1KBRNQKxrqgCvgw1v33oW+62/EITuGkKFzR1bIV4z4ZNkvPiULFaXtBgAMSh06 + euqKpHJ7vduxG2mo4KnHXm7R4NEmMtmvfFPeo8IbLtQkrJKHGY04gH+/H4BIlt9wucQcSQYZTbTkcjVc + QUmJZyGVh1D5pz/DNXonRP74c/T+7NfEnCGRaCJKieTiKPvdb+GdNAXJ3m6k68cOPliXHg13ugfrL70F + G1CC+ppqVJWVQnr6IQRqSpC493fIbdgoKv+IS6RVONHQiE++db0wFHJRkKr/PYHhj92F7KmXA8fmAYAZ + N6NHR7KIz5TJaEzdG4kQw1var4leBvlQYB6qnMVVZ4yrarwXFGCxV+9iY5i9BrSEPLho15bV61Nqcr0w + DMpab0e3bkA0DIl1dXU2r4KhPuCfpOY8fIddHYRm3+HnlWiXkOrSDISSXDAxrBevzwk3nfuFVBzvJ2Jw + kTRw1J23YdKxJyPFGYK2Vd8AMG0SSw5m/gD+d/8fsfDKH+PIUAmqJ8iIbpRzyU7p0FHrlj77aXmnGG13 + AMDU4HdftFtNya0/Hl2KOllra13ilkWlV5stYDAfv07qprZbvQOFNgXr7wsmCgrO0S/0QBchDfuCSkzk + 2mkK3AccBdesbyP92r8R/8klIh1Vm2GKcBGGfvkgEn+9A2kvMfIF1w06TtJFRyJLIu2ys3+ClCeEJo+K + 0oZRkN5+Dv6wD/HrLxEdcEXtBXBT0Di6Zu6PtcddpHWXIXG+4Z+3oeLNZ5A547s2CYBXeLZB8KTt7dWs + 6Ea4dHtnl83q7/IGRLUo87pYPHe6dZemNjIOl1b+TXa4zH3EZ7OyjrYfqwzZlL3sXSqu6dO8mmqfY8iR + KpElFSYn3I8qzIgefR9DEpg4caLNq8B+eGFkJACQCQD6aY/GF3RZbOuJt8iiDJ1sqH8FUqnx0LkhSAed + m7MCY7E4ykfU45RHH0F50xhSBaKmkcrq2nZ6faKa1Lt33465N9yCQ1wlqPJ5kOP4/4S0jH4zfcTqpRF8 + BtouAYCII2B+MaIsiHPDIVo9c9i13ovd6lzIKEVW5oKHMdAK3i/FXF951H7cWwQAigxYUTtBEWASqxUh + veeAWUg9+yScO++GwBU3I3bDHGTffZtmq08kKkihMILX3YrYdRdB2f1g4OwrBx0k6aLD0Tp1d7TsM1uE + tI5tXwHPbgfBsWI+PG1r6fjfhRwImvfqyCax/piz0TbzSLjVLGRaSZse+RW8a1Yjd8I5wAHHm6uzBgAJ + 4cePRLQ5mEprls+Orm5TxGdyef1weSwtyVgv1xneYHSHU0ullrmPnjDmyTpISnY9mUvE9wOAmDivCQCJ + OElYaWFP4HeNsXQAYMmEVQHdbjB+/HibBMBGQgYA9am/Qf7vX8V3sihkm4Hc22UT82VhIJQQa9GKzZiX + 2U9s1N44M/DNTAJvEdCSnoQRe+2CY++5B8Hq4UhF+8wduT6my+NFdN1KPHnTz/D8Q4/i8nAdml0e9PFc + 4RBlB35NzH/5ZnFNEdpeAeB8et1e4nTgzHAZgjmt8OMRzQHsXuMSVVfN9aeIHaBYLQkbMFj2y2/KrwPm + b4u4cPpFig7iRTCOodBK6t5tD0hBP9IvPA813gff2XMg1zUidss1kHwBrcdCRQ28J30TyTtuhHrQKcC3 + vz/oIOWuPg3Lj7kAybLhCH78Bk0g+nLWKXCleiE9chtSDz+gHVvo/1qLtk++cz3idWPQPKJRMInVGs86 + v/E3qwDJpGa574vGtPPpenwfSTPWIqseAgC3x2sOgLDOG757ffCcLm3ldxgSgKHTFzSSERKAbnMwiBle + bFM1AEjSZ77WLFfz5bBtUaI+J4BWAwSYhsQ6UoscFhsAl+fy+XzmNTA4CIlg5WI4rz7T9oytq3yyU0ay + Verv0rO6jyVRWQ1PkxqwLENjR8A5cq+ZOPDGG1E7ZWdtltH4dW1cjxX/fQJP3nUnHpy/BB30mwMryvGb + UBXKFdHkNE3zaq8Ra5a+s6UMZND2CgDH0evRsSTGzQ6WiNBONjL53A5MHxPExJCMalHdQSs7Xiimm/xo + YcpBA4j6+e0tLkipKMb0swUUL5UtiSQgqSRIjH06ko/8FWpfnwjGcdQPg/eiKxG/8f9ErgCTXF0P196H + IvXA7zRx/FuDA0D3X36LtbsdxT42NGe6UTZmEtK+EK1EJEJedw6y771FAKC19+ZiIunqKiw/5wbkvEE0 + jxxFAOAeAgALADh+eKZ5LYVzhQWWFAlC8fUOkUxmxoLYFgdVlCjjxen5VAxLCQQyfTEEKkrQfND+KGtu + QmJDK/72yqt4d9FydNN+1judVVmBu4IVpPMm/tHUvva4LeKcAtpeAYBTH/89s7QEhzm8osc7E7d9DpH4 + NL2a9LiQipqAijqnCo8h/Rl2QsvTK5o+DPT361pW+4FcgoXHKCoJWiWLZErokr4Lvovce/9D+vVX2KkN + PVYWwZ/djsR9v0d26UKt60yoFMGrf4XYzd9DbvJem7QBrFwwDxFXCEGHiqkzZiLVvhFplw+uZATKNWcj + t/xj5k6xr4M7A834BlafcBkkYrKGxkZ4iCkNkZ/F/ChXbKbrYqOfMOSJYsdsgNUZ26n1CvCXVYhiqQYF + fV4EvPngHbbae/SmIg59MNwuh5CynEa+g2XMrCXZ2UWYydqNgEl2MULzFvCuiTSBGe2T0ftPMAhkUknx + Ho/0iH0zOoh0rF9puiKZaqqrhR3AiCDkv2tqauBYswyhn503YNi4wDK6pTSp87F1DpE5aIKAZXXRogO1 + BjGLCAAWZ1OIpjLI0Pj76ZxjSTV6Ws7hjp4iiUP0m582j+y8eIfp+4Uff3j+Z+QhQdsrAHDm4Ov7loXl + fWSPXuBBFat9iEb9VE9IIHcLzblEiQpfCVBBKFAhq3AZUoHFhWdQYW5BnnHzVsCBAMCqOhQbNcnyF+uT + 3FDQMWIkPKefB2XJR0gY4rhxrGQU/it+BmXDeiTuv13flkPo1oeIa3xILFuA7G6HDTpIS9auF6tj44sP + orZtHZK7HAAcTCpAsge5754EpaVF1PIT4mk6gY1HnoGWvU6AI50UWWced14CYADo69MaWXIQD49JTkfT + rKrr8bpeH6yotgFAyF8MAPRQXX1gPPqK7Cpw8WlegLx/XABAzg4AiUzW3MYUT2XFPhoAKMIWoAFABrHe + Hi0UOa1JVS2rlpm2A6ZqAoCSUMgMOmIAEJ6CNUsJAM7vN1dsBmJJY3pS8xFb5dByCQqjQvV5x19znkCW + /k7pKe0MxT7a0kFDcXj7OqyIRFFIJ51w3Mq/P/TwTpLs+Ez9/gzaXgGAA+DfPKw8HJpJAJC0BJ2weHUK + ibkjSZTkCcHVU7qdEjr9KnpCBARBFaMIDJiddHuKdpNqwcotFTCt2Ee1tTYzpAmpiCpgtRrbytTRKiQP + q4Pn0OMg149E5pWnkH7uv8QBPt34pe2nRCPwHnM6PEefjr45J0NNpNjaBd+FV8Jz8hwROZdIDN4EZvHa + DXCuWYjm266CzI0oz/0hqQ7fhCvWidwlx0AprwfOuNy84gRdT84XNHxxtth0Zv5YPG6RACQoumtPgca8 + RiOXUHmlZtTTKeDzEAC4YaAqG9U8Tt36ryOAWzfEOZ0OixE2b3OB+R36VdZN8mc1HxciJABmfMH8/J5B + WqglWQEAYp7oALBx9XIbAJSXliJgiSxkoyADAHdICrWuFvuYnonfXgk51me7NvE9xwv0SYiulbUkLLO6 + daEeqAoXtvGR5yIJa4iTiHAoAfaKvli/Z3rIIYfgqaeeuoGu7YdbwDf9aHsFAA6Cf+Xw8vCEGZIbOadD + JEtk0jm46eHM9oZQKcmi+iujLEdisXjIj2otzctIKVAbBoZ7aTv9TskVNwzaVAJjm8HsVncP0E/2t9kO + rAFHJG7KVbRCDmtCbskCqFzyyRfol3jEtgEHrcKh3z6K6PfORO6TxeJ7uWE4gr96GJlg1SYBYMmadaj6 + 5+9R9eLjUAgU1VMuAo46E+4cKU2/vByZaBK4/p6iv2UXH+vORlhuNBYTxSrZuq/5/XUAEM1OdN+6DgAl + BAB2FcADn8dtieenZ+KQbBKAAQAuS0MRKwAYbKKolqAinZKGBKAPXpz7IxoAoEsAaV0FiEV6tSAm3Raw + cfUKGwCUloTg120AzOgmAOh/MxleA/m8gyFFuszna2MUGhquNZlYb+8onC9c2n/V4OsqkR34u5rAhWvX + 5G/IQmNGj8bcefN6SkpKdqXxW7IZPFOUtlcAYHpqv7LwIXvR5PNXlIpGDx0dPWh0e3AiAQCHDQrfK/dq + J6Yrl52o0gtGckmoKK1AXpIG/OWEwkHVjAW3GQlhMKTRYCy/wQLi/Q2Ikt1QaI9NkPRqpDQBXR6tgYpu + bFALUYVWKe8pZyP91qtQaKVicV1NJuDkTkbnXot008RBB2jl/Llo+vUcOCN9Wlrw7EsAEvO54Yrrw/8h + +vBdkH76Z1vgjUEtpB7wam+AAOvUaTb+CQZUhXHO6fJoGXa66uLSmb4sHLL51j2k37st4bvCuGYGR9kl + AbmwInDhhQlbjv1bYXxEHpRZ7FcUVb92VVRx4hgF3i8a0wyGRpxCa8tGzQ2rU7y3C5lkzBwTZvp6PVgo + pKsGRkJR8PJjIfd1DxgjwiAQb5WRapNEjUchUeloYetybJlMPgKA2ZFWvNDZPeBz/fWvfoU5l112Fx3v + O5vHMv1pewaAW3ctK7noINWF+vFNiPZEsX5DO3b3B3GA2yfEqfnZNF5OxxClBxyggd2Lvt/Z6RYAwDU3 + GWBZfXWECSxIppB9muegEAiKuQgHTBAokAQKDYvWOAKbUbDApahtU0V1IFpa7S4FzpybdQJw3rWDDlD3 + X36HqodvR5bEeqGKnH4pKZenw5VJwr1oLgHA3QMCwIoVK4SaYYCAyioV++hllrY4wcYJL62UvDKWhEvF + b9y6/l5V4oPTGl7r0Ju9fAbq106+YNht70o+lJi/5PLuQiWg91gqq4cxa6t+GzGa9f5Xk1TW3bZB9xwo + gumH1dcLAKBVV1wHMYrYt+KqUyBHevIPFEUWAnpPdmg5BCJq0JF3Jhfek5d+vcih4Ij1qxFN2T0dVtp7 + r73w0iuvROj3u9Hr488yrtszAJw63u978ASXF6OnT0JPey9Wr1qLo0OlmOxwo4X7wSUiiNND5ACMjK7T + Hk2i8FiayEZpaCM8V6K57agkICCJQBKJ/fpWAdhq/+Ii6B9ubA6YRSQstoLZ1A3rvgWxB9ofefSxAdBB + xwPf/sGgA6ReORuOZQuhOFzaOU+j53v4aXCsXQL5b78lFSAlVIAhANhCALjyFDj6eop6hwpdhJwYlmyX + kItZkolk+z2U0tj+IdmLq1o3FI8w06mpsREfzp/PLsvPbAvYngFgcq3H9eZsXyCwy8yd0dnRi+VLl+PE + cDkmy268S6vck6mYYH7jJhgEamninkQgIHjcasllhmfJgNQ/Z60KR8kA0gDQXw2wfN3vi0LGhf23A/UH + 7FcIpdC3eOBxm4wEdFxwCJSu9vzvT9cAAM89Atz5U2Dq7sDVfxgCgM8CANGe/LmLRYoaoK6njOeSkjAQ + crYoRxGqWqCi2C9I57g62Yk7W9oGHYsgqSCvvvIKdpo27SP6uAtd02a1BLfS9gwALtItn5vt8e193EH7 + obW1E/M/mI/jSytJzHdhbiaFJxgA9FtQ9ZzyDE3EQ71BTOEe8ciH+5rGOn0uOMroBDVcIUYViF0sbFj/ + M09WQFH7M3UxY1G/XgewSBdAPwAx3U67HgDlpPORLqsRX7p6O0TL7iyBm5zLwN3VAvmnF0HtasurGEcQ + 8+99OPDms5AfvwfKDrtAvep2W4Ueg1atXk0AkNYLoCqQWN/n+H1OiXNoxTV8Xp9g7HBJWPzGqRcfLQ14 + bADA+v1nBYDBqBC+zD6SOgCwzUDEAxAwsIGQyfAktHf1mO5DprXLF6G7o0UwP4OA3+dFeWkZ4Z4sjIM8 + h9hVyFT1h6vh5DwE3XsjdbdD0vMSrJGitqQ0OS91ivCDnPZA2Q4ZJJT4WVsMP12yfpP3/PBDD+GEE09k + N8HedE3ztnTstmcAYPrxAT7Pted/Y28oXidef+41nFBWiR0JADbSiP4t0YcU55LTAAcqw6ROpxCLJDCc + JvJJrBfDytj5stPiO9bXvDSpq1X4yyThLYjnVCgFA2LDBSvTFgsBLvAd91MRBggXLlYWTU4l0TNjbxG4 + 4+5tR80bj2DDfmci6w+j9uW/o+6J+5DzBPK/M8BH58sc1ywYMQOOm24VTL5hwwbbedt7o8KCbkT3hWiV + 9wdDcLs98Pj8gtn9bqdg7rKAvUDH50GDSQCbS6rFk9DSE7cZ29taWxDti5juzxjp+O0b1wkXYiLaJySe + CRMmiH35nSsNGUFD7ntvhPP1/w5+H9ZnbL0l+jtAUmv71G9g1ouLseC99wc9zjVXX43rf/IT/vM0Ovdf + tnQstncAmDHS5Xjlyqk7+SbN2AmPPvAwjgiVYapTq4P/n3QcH6YT8NKqVT6yFpENHUgn0iL67ARaKYeJ + GvGqXTyHJcxX1YI1Hg1nUFqqqEeEPRKvJKbNuEAVKOjyZY8FsH4p2TGhqK1A31jY2sz4oZyKY+Mhp2Hj + gacjtGYBXJEOdE/aB+7uDWi+8yp4Ojuh6uG1AjD043BNglQPMX+EVq2p0+C/awgAtjoADKAK2CQBy3cc + GeikcZy/qgPqiRei/LSzcMWZp+KZN95BYoC58Y299sLLpAbQeb9Pr5u3dCy2dwCQ3bL03/Pr6w6ac945 + +OuDf8e4jgj294dEHccWJYu/kxQghD1WAfSGHlw74GBSAxgoUlZjYGGeAFGIfnNqvBuPd3UoPxpf/dB1 + o8JSTzp7MG0qtbkLi4jyhYM34D7WOIEinoT8AfTf65171px0Mbom7w9/23Ja+cuQDpWj9iVe/e9HjmML + xAhp4ibXtuOSVqx/qnqRS8fOO8P3x1tFbP/69XaxsyMSE2KyojPCEAB8CgC450a43vivbUEplPaMd1aW + HJIqQp+7kire2pjCm8vbsc8P/w+HX/sTJDasxrcPPAgPfbwUSpF74MpO7777Lhqbmm6jc1+0pWOxvQMA + 05kzPa4/33PJhVjV1o5V//gPTi6rFkzOFVn/m47hfS6SqXteVc22J7wBYxx6jXjrzUr9AeCUnjb8u7cb + Pof0k0ROvTZ3+NjGSDo3g/bdnXabSbvtRD/yF8YQGGRz91m+y4ON7h8uJv4bf1sDkHhV9niw4js/RqJq + hND5OXffGevB6DuvJP2/Q6z+bGTKRLgdOhudJLP6kHEdjsk7wnvvHUMAsBUlANfrFgCQ+kn5Iv6B7SGc + sdqRULGiN4v32lLoSuQInHsx68dX4pAf/ljs/6NrryEx/6cDjse//vUvHHHEEf+mv4/a0rH4KgBAmQ94 + /eY9d504+4zTcf/Nv8ah0QzK3R7BLX30JB5LRbGRa/dz7DV9bqaV/1jSj2VJsrveCuJwhLrslHFU2zq8 + FRNhmX+m1zetJ+8+SCTZNtJrZ/rhbnT4Xeh9MjFsyLaqo0AXh/VvPQhoANdhoRQhkWSTrqjGsvN+DsXj + FaW8ch4fat7+F+r/cRcyUgAZbn/eKyGXVEUbNT63a/aJcB5yEBQRIZcFuBhoY4MwAC5fscJ2TmewQktA + 0qWmkmAAwYBPNEflqD4eO83oJ8FbpCvu1qetOQ1V0/DHYcPW8W7vjoiqySJGhP5FCfhb1qzKhxHTfYdD + mm2lJBQ0E4Y48KnxqT8j/P4r2tUaACDpTE8fUoQ5rfEcVvRk8Qkxfhv9ncwqRtsGSLEIDr3kXOxzy63i + GA888ADOOOOMAe/ihhtuwJVXXvk0XcMhWzoSXwUAYLpgn6Dvtvt+8H0s37gBXX99HPsHS0WWIOvwXaQE + zM2m0EETP0wTeleXB+WSg6QEbRWQBgACXtfWuCUc3LEOG3tFBBn3Lzx+sAshQOCMzxqaANPofQYdahpd + xk507BrVSCpA3lNgcHyxRiUmIBSoDnI6hb7J07Dq5Ct0cd4BZ7oPI353DbBoI1JJj5aSakgz+rFdcy6C + +7RTxIpvlPPKir+zWLJ0qe0+ApXDRHaf7NB6BoSDfgT9XhHR53O79TJbehnuYo1QtjJtbQnAoHRBXkF7 + bwxRrtWnJ+j09XQRAKwUYcRRPY/Aq3c78ri1lGZ2D7IkMPLp+1G18G2d6bU4v0RWJaZXsCqSxfLeDNrp + 70RGMWsW8jkCtMiMLnNh53AOTbvtBP8tj0Jy+7Hwo4+wyy67ID5AyPdRRx6Bx//171fpz/0kScphC+ir + AgABWu9fvH761F2+c965uO83t+EbbRE0eX0iU9BYnzgC0KkPu+72t9+gBJv+FqKn9CId4egNa0mcFuP7 + EL02uwlD96xmthdMoTnFUsIU+oo7Io+gl1Ymp8Ct2C/ISLUABpEjFUfbvsdiw2HfFN4Ale4z/PTjKL31 + XuRcAVvsufi5Xl/efdlFcBUCgN60Y/HSZbZrDtU0wEGSksydaInBw7T6B/0eUVPPL+L682G7zu0YAArz + CtojcfQl02aNQgaAjas/ERJAtKdbDKtTb9IqJCBJqyHAEsCY5/8qAIDT0ttIvF/ekxErfQet9CzyG+gv + ipM6JVT7nRhb5sS4chcqvVp76hypdsE7n4JcN1I8+P332x8vvvRS0fvYYYcdMG/evI/cbvd0Gp/UlozF + VwUAmA6b4nL869bTZjscZaV49d6/4KRgmdDhjX5sonajcUeqHQCKBfWV0G+/n+nFrRtajK/upNd5n/VC + CRBYuBhGJ2U7Amc2TqXzTwYnOanCXGFelyBbmLBKOn8aa489Dx0zDoWDJBs51ovqa6+Da0UrgYGr3/kM + AHAxAJx6clEAWLTELgGE60aI9F6tVJekAYDPI9J2fXojERHlJuL6P/8p8nkBgFJgA2ojAIgmM3kA6CYA + WPOJSCXWJACzvCkxsmoCAEsAfX+7Hz1vvI7etIK+NAcfaR1t2MLvdUkIkapU7ZdR73dgeIkTVV4tKYrj + E0StCj5pKoHgj2+Fc2+t3se9996Lb3/720XvgyWP1197bcGOkyfPGAIAiIH+w2ml4fMuPfM0vPPeB0h8 + uAgnlJSDM9EzBfp9v7srULz5N2300Ga1rsWqmCmC/RJaPcKtTgQKAbqGsXQ9O9OkGg8NECbRal5K3wcM + ySQXV5GKObDh8iuRGL2DWJ1DL/4H5bfcCcUX7H+TFmRzXnqhAABmeKMRB6sAWVIBlq1abbue6uEj4XR7 + 9Mo9smB+XvndJK563a78ab4gANjaZAYLFTz4rmgSsXTGBN1oby9a1q8hAMgh2tcrfphKaGm66URUjCGX + FudqwplFSyC1tZvqWteaNfjktdewm9eP6fUehCoUUR2YQcFk+oJnpPZF4J19DnwX/ky7nq5OTJkyFevW + rSt6Hw/9/e8LDpo1awZdwxAAENU0ul0vnV1bN+Ebs/bBqy++juqeGA4LlIATPM327tboPybVzv8imo3E + u+uzfbhhnc0/fhm9fvNF3Uz7nqP92YRUQ/r8nrJbPSeXlPbKdCvIlpWg9be3QCkJQ+WSVauXoer6myC3 + 9IgS30XFGSL54gvgnH2iqOPPqb0i3okBgMTgNRvt4aeNo0YLiz+X6uLV10dMz1l9LPa6LUU0t+bKPBh9 + pvOog22yb2TxP5XN9ybgKkjtra1inHjMODqwq02bEx0t60XEIHfu5bJhU6ZMQWNjo3ms9c88h84rrsJI + 2SnUUFcViXdVusqh5KU6q0eIq0Q5x45F6PdPAG5NQ/zhVVfhhhtvLHr93zzrrEWLFy+e9tbbb29ROPBX + DQB4RPfZP+B/8tCq6kBZYw2WLF6JekXG4d4Aymi004VZd2p/H22YVryXXFmcSrp/T8KWlXU4vf7zRd3K + qqaxAWL+w+kaT6FL2psusFSm1Tq9QwM6L7wEMjEyi/+ykkHgxefgeWkBF7sr/sQ4z+ESDQB49TdKenOg + D8fFr221d5diAOBJzSqAaMdFwCIAgP52O78Iq7+dvigAYPE/xSXRxUatDoIAAM4jYNCk944WbTVu37BO + 1BKorKwUEgC39LYCQO75F5G54mrheVL0wDJPmQp/XT5XwRo2bpxTTacQuvlPcM44UOy38pNPMJnAhcGo + kA6ZNWvlHXfdNaOpqalzS4bmqwcARF6X4/snBII3jfP6kKGrj6ayqKJV62BPAE2cCajHA/SLzqP3EnoS + HzoVzO5swcpe24CzjLw7vTZs/hVtHq1qHNsMNjZKOJXeNaezLYRUny2iywiHOms1/O2WP2Pf/Gf5kvPh + OOVEUdCDJ5MmAWjRfhs6emzXMGr0GLgJAAw/N9fw43JdDACFnXm+CPq8AKCQWPznegLGT5jp2zs6hQSQ + YPcgj9VqzWW6ftVyUU+wqqpKgOW0adMwYsQI81iZ515E8oof5sV7nnM5mp9VDAJ6A9L+dwrEo3DtticC + N/1NS1MlOufss3H3Pf2Lt9TX17dccP55e159zbUrsAX0lQQAImeF1/2PU4KhIypp9WcQ4PRfH03evUms + muJ0I2fBfn530f+4KOMrjiwu6CDmj/Qrx8Qy2FWf50UT43NQ0Vk04mwBqrJtLBTrFUuaoqDij8kAOZGQ + dnEBAKhaMQ1+bey0l5gb2UwSgNcrinywK5Az/Xjld+j+/+2OPiUIxDPZAQBAa2haCADsHuTkIJYACgEg + /ewLSF95Tf4SdGMsj7u3WoG/WtVaxFmvzzBQJ2Lwn/9/8JyilWz7eOFCTJ8xo1gVKH5w+9HrvS0Zlq8q + ADA11vg8L5wYDI2uVCSR+cfRgcw2Mz0+7O3yas0w6LOXJnXMKeG+XAI3tLWiO9HPnrKRXnvQa+XWvkgS + 8310HQfSJXDbncOg6p7KQipMKrJ4MiRp4H1MmOMqNBd9B/LJJ4hJ3dOjrfgsmvKrO5G1nW5M8yixqrEE + wDXtXA6HsGbLwog1+JQwougMyglVw86BhvvN2E1Rjd/lrfR5gLYXz3AUuB1lKS8h5IWkvJdCgJ/e/agY + eFlvhysfKUYnIXAF7iQ6uyMCJBOkn/OKv2rZIrFt5eKPhHvQagPgYqomvfwapOt+at6nYXdS9Yq0vmEq + vGVaYcrCcoGcvcqlhYPX3w7n7keK7+Zceil++7vfFV4++6c5NP35zZl3Bn2VAYBptwqv67/HBEPhRuIr + jvsXk5E2HOsLYgqBwHpZxTu5FO6M9+LVrl6t1nV/Yub809a8sJWNY2ppch5Lf3Ko18yiDD7Y3ygeYjzQ + /jzBZAIAaQgA+l3vFwIAMDBb+8tIEAs25uAKwVQH8pdCkkIqBbk8jODND0BunoqWjRuVmTNnptesXVsY + e30MvR7f9KzrT191AGA6JeR23H1YSdg/kfSpjKKVD+c8gYWZFBZIOSyNxrlqxEC//y295nz60w1OJOZP + ordTaSKcSEPbbOVga+bYJlXegYIYLDkD/Y5x4bmQTjpBiP/04MVXWsirhGjObtgbM2qE6AzEDTREIw+d + 8T8NAOQU+1iK0twWANDiD4w6fqq5j/bbfHam2TjUciytd4CdiZ1G7xCj6KiV+fUwaOP6PQVhy4Yr07x2 + S3dipngyjY5IVIj+CdHNV8Hyjz8U21Z8PF/EB3CBEFYBdtxxRwwfPtz8rfzam3D/9KZ+z820PeU4m41A + YKQCh0tTB8zHKWnPhe0BjgkTEfz53yCVVqt/efDBDaedfnoZjCAyjc6i133YAvo6AADTbJfTcdceoWBg + d5cb/pyEJEHwfbFerE2kB/vd3fTiTKst8rEaRGI+T9E96Yl+m0bzCHovs+0w0Ape7LuBsgQH+874/QXn + AicdXxQAYorTdsqxo0bSqubWjYBad11ZT2QZDACMdt62aLuCWv7W0t5GIA4XGhVeCasEYK0KbAFJp8PO + xA5LgpO54pvMrwGAU9bevS5nv2u2ShdaFeT8thit+p2RmAC1ZCojJAADAJYv/EDYAAwA4Mg8OwC8Ac/P + bjbvGZLe/ltP/hIh6LQaucpUBBvsqoD1+amxPrj3ORCBH90N1emNH3fMMav/+fjj4wAzJomLg971qSZj + AX1dAICJFanfN/q9Dbt5fWhwOvFcXxQfFo+zZiGB+65fB3t3ps2ilU1jQ/TMD6cRPIs+7gsuL8A0gJ/e + vnpb5XfLPgUrfr7MtCWLsSCvwPQxX3AOcOJxosZ/R0eH/ntJvDKuoO1SxoxsFK3BhBFQtjAT8mHJJvMa + q7seP58tWEULVQDD+2Bcn9hH95MpNgAouG9oEkBhdSFZMgBAMmPsxSdJ6zIkGcCFfNlxg+cLXZoy7IAQ + T6XR1ZcQ95pMawCwjBifaflH74tIytLSUgGU48eNQ21trXlu15tvI/SL39jDu2GXzIzvQiMUOP2aeNA/ + F4TGNRaF74zz4D33x5y4tXKP3Xd3tLW1GT7H79LrV1syR79OAMA0hl7X0hM4PuAmKOWWUv1rr78NzeL/ + 7y09Ca34vAwY+v20YgxvYwfV0gxrIPHdtOWZxaRNAVkqOLhpcOKJw/eX0xtnXHweKUQniC4/7e3tMHOT + ZAdynhLbMcYSAHjcLk0FsHTotQoiBhOnbau7lmprDbEVl1BYytsAAMs1qzqAFAMAK1MWAoABStb9Cm0B + hg2gEAA4k9FmX5AsEg69JWjV745q7j/uP8DuwOU6ACxd8J6QAIxQYK7Zz23EjPO63/ofSn/1OxRlIcuc + 4HJgPq48VaPkvQIoyAnh8com4L/0OriP/g7uv//+NWeeeeYwaHZs1jMGLxA5AH3dAMCgXaH1F5xGL65o + yUGCbOFnpueaTrEtOejKxrEc138mPXvOGBxhbjBXdp1ZLUlHkmENMk3EGFS816rJG8xh9Cym77Kq1gBT + KJI6JLhkSCVeOAIhOEJBZM6Yjeye+wgDIK0e2vFEkTraL1Rpu5exTcPg5TbVxCAybbcyqvauifX8ntLt + JwbTc4adVQJQUdjjQrWE4uaPZz3+YFQIjnZPqNRvwkp6yrVmC9CkZgNEgh6X7XhGrINxvGQ6i56Ylh7M + f3Pgz9IFWrkuBgBuOiKyAUlSGjlyJKoqK/VzSvD9by4qb71dHEi1PWu7GMexAc6QilCjkv+22KJBACS5 + pETol39dIU/abYcfX/cjXPfj63kTM/HFmzFVTfq6AoCVjCjh7JYegFb8PemNMzZYzSgvVhJIW5WLpP2Z + fw4gJhQBA1MzZgmGV3eatI5SP71K4SgvgbOO3ssq6FUGOUQA4A9A9vrR3TgG0doGAQAbN27UOUMWef+u + sjrbqcc11tkkAGZuI4de0cX/dEaLmDMKbRrie1ovJLI16bOGHBu/Ntp6GY1Iwn63zabBRkKjPyGfk5k+ + kkiLe2egYwmAGZ9p8fx5wgho1ANoIP2/Qq8YLBqIzH0PdX+4O//MDHVMBwLjnhizHV4VQVIDuOaqUY6u + WAMhNdKT8X7nh2d5z7xsDwL7C87+9tm4909/+jMK6lR8WhoCgC2kVY1jOV/oCGj+e9bv866ZIoacYnWh + bJstD9s8BmAbfZsvmSaiq74c7vFNcI9ogrOqHDJ3tfX5YVte9fLWzJ09I8YiOmwEuru7RecfcSCuZkOz + zsV9Ai00uqFOJP1wrz4rAKjI6/8ZPWY+YRj0lLwEsK0BgDGUxnEMACjxuW0SgBHubOzH99LHAKBqLsKi + ABAImADABkHjt0EGgDvs0Xuq/uBt3gf+7CIJYCQBgBMFQG+fBtxYVg5XHBX+x/v/prG/nRj4/H332efJ + +QsWHLElYzIEAJtJKxvHVNEDZhH/TGjlwEwa0H03gJXeGqVnMxAV8o5kP76aTMM3c0eEjz8IktsragSq + gtFVPUKwGPNJ6GYAqG8SAMASgGSRANwVw2x7N9XXiOIfbHGXCgDAeDd8+SlLUQ1hA1DsXoCtQVsr6cjq + KmTyu+02AM52NNyMrDawtyKRybdEYwBYZgGArAUAhg8bZvYMEJ2FBwAAyewwm58HzPihkTnILgvQG/NC + sq0nrWpf74zyNzvW0hhz1fsXPlqwwLN40aJdTzjppAF92QPREAB8SiIxfzQ9gVPp2Z1KK/WYAYsIWKkY + hBfbTS3YZTCXH5GSTCF83IEI7rs7FL3XXX5HuwlZlPQSRT0IAIaNRF9NQ1EA8FQOt52jobYKbpIAHLJd + AmAtVTNUaYzOp0vncvp95L0C2yoAGKNkHM8nqvrktwkAkC0AwMY/vWwYA4BSIAGYAEBjOHz4MJEabHhM + gnPfR/2dBfH7BdKhkPzYbONhT0DOJgEA/acQfb6x7JkVZkg6jfM50OIAuD/AZquxQwCwCSLG3wnaan8K + jX41ClfoTUXhWUJybeapgmNYm4n2O66VmLloxpR96wj4Jk6GEtcBQNJ8YaKOH61gKluWeMWKRZHt6EC2 + rQeRKdOQ2XNvAQBcBNQAAC795atutJ2GJzI3/+CXZoRWzCacirJ1mfvT0BeVduyQJVM9KHZOoQLM1wHg + w/eECuD3+4UEMGxYvRg347ehee9j2J1/Kir5We2A/KgcARXBJsXsljzAFOvgqlJlz65Yax5OVdl/G6bz + bbqbSBEaAoABaOXwsRPhwOV08yfSR60v9KaCdAaiwWwBKAIOgx2XJxAxoquxEv59ZiAwdaqw/oP17kQC + WWLubEs30rTCZze2Q2mPQIrH4MxlkLrwXKSPOX4IAAahzxMArIZA21pAj88V1oKBCgUn+3oi/bD0meU3 + bM37HQKAItS13+ixkU/k/ygZjJb1wLGiCzMzI3dxZT5ya6WzVMNKxkwteguqIlZV0idVv2AQ64pQcOz+ + J8x/ryRS8E4fA++kicTorbTCt0Fp6YGUScCRTULmunWkISpeIEbn7mJ9ffa5KD3iBFMFsHoB3AVegDLS + ZZ1Op1nyWoj/qmILAPoi6YsCACPc2Rh61ZIXIO6dAGCJrgIs+ZBUgExG9A/ksWpqahR5AYYK4H9nHmr/ + 8MeBTDImWeMAjChqW68Ijd6ibw8se2b5FrmoB6IhAChCPQc3H5mJSf9KtEiimaMRr2+rGsw+facE3y6T + hKst9sI8KJE46XJOoXNLzDxhD20LINcRgcqdeGXJnvVlifwD+reLVvWTaX0D8vsbzSYdop15WsTCS14J + KTo1p/gkaMd4VkFLUhGlp/tSCqJpBdMuuhg7nT67KAC4Smtt5y6niWxIAEx2N+DXDwDMiEWrDeCDvBuQ + A4EYACr1OAAGTv+781Bz212mS88AAtOTYxh1ielZ/HeHVAEGRm0Ki6TYSm/7ke7/mVqBF6MhAChC3Qc1 + cxm8fejBXJaJS0dwb/dcXOda48lkcnDUl6DyojPgLK9A/P0FyCxdC7kqAGdlBZyl5ZBCPqTXrUXkkeeh + dPQRxzrsI2rx9Zo8ZTx4g9HZU+dWRdKIw4Ws7FET9PcqSVI35BLSnlJWCqzOZPEygQ9HM5TAhfZsFl3p + rAhr1e108BNYHXDFHDQefzJ6e3vtgUBs7CqIBCyvqhLVgNgIKOl9xQqDdr5I+qIAwPp4tHvVXH/QYxxY + Alg8f67YtuSDucIIyGK/y+XGJJLG6uvz7lQOBQ7/Mp++W1SrYwOgC8IFyK5AG0iI8yNKH44l5n/u87jX + IQDYBPUe0ny0ksUV6V5pt3REEi22rCuxcxg9/BENcDfWwVlXCUeghHRqt9DrEx99jL4nXobSl+IMFu2A + VkeNvpILPncyk0s8CTLE6HFi+o0ON9bITnUF7bNOcmAVLSqf0GFXJTul3kSb/Huam2c6FMnRpmaxMJ3C + VI8Xigd4KRHDmlgaPoeM6oADY0rdovx08LTzkNj/uH6hwAwCSdmeYVpeXSsKgnIb8C+S+QaiL+samOEZ + ALQcfs0NuORDDQAWf/CuAAAW+zkZaPLkybZ0YOfrb8F/0y8HTfZi/d9driIwTDGzAY3diNbS57NKn1nx + 4ud1f0MA8CmIVALm3gPpqZyQTUgHpfuk4Zko52vTBEnltGg8WmFlrxsyrQTQ3UjCRcdLsEs2pG2xmktO + JIivIrSSr5Xc6mpi+LXE4GslWV3NjE67ruWuVPQeDz+9ot9yu6pp7E/oWq42xUh6ubnDZIWCR1uj6KFr + GlfmxvhyJxqCDngdknDfRY75DpIHHG8BgHwyUJz9UBaqqK4jiWMIABRRNVlrnc75AMIGoEsAiwkIeJtR + E7CwHgADgO/GWyw3YU30yat2LP67globeu1exRuv+JcS8y/6PO9vCAA2kwgMSmlE9iSpYP9cUtqTxPDx + OVIDFfbAGhYyXlBlkKgutdFrrexS19B3xOjqemZ02mM1DWuLw6Emwv9dkdmc869sGHsOnf8us1EIh5EG + gEAt/eFT0ZVQESZJgkV+nl5Z1bRJIn58IQDoEgCnA7PF0EKVNXWaCuD8egMAr/g5vYcCe180G8A8sW2x + rgIwABSrCux87U34b/wF9MfUz9bDDO8Jqyhp0PoHcCp0VlW5QcOv6eNdxPybHdizuTQEAJ+BCAyctKo3 + 0IOdTOJbOT9DGi0uH7ZByWEDMXui7OkVya11vo59Rn8zukr+Pc0ms6MQ55L7ahQR68MipKwDg2m7toic + iePPQ/rAE0Q6cCe3D4eBWXQvKbugUVU/HE7RGOTrDQC8wqeTSV3/z9rSgZd+9L7IBjSKgk6fPh1NTU3m + b+OvvobMj28QDWaC/GzMTtRaarKDtC6pSUUPTZvOTG5xhSz9qtHnfEz+z7KuL+r+hgBgO6HeQ5q/mYrI + 98ZYK9QNR+w68pQp/SIJrW3FrDpl8oTzkTnoRFESnBOCjJRe9u+v77AXBa0b0QyX2Rfgyy8C+mUBQDqV + RLwvIsT/bForCbZyqWaMX7nkY1ESjGsAeL1e7L777hg9erT52zXPPY+nfvJD0R8QvQl4Al6E2bPCvQUy + OfRISayQs5jblUR3PPlcIqsc9EXf3xAAbAfUfVDzzrITryTa5GBiowQniY3+UUHINeVmgwmmQbKIxef0 + wScjvduszQMAp0sLJ97OaUsBRAOAXuEC3VwAkJ9/CdkfXI0YjXG3TOqZ14F/5ZL4/foWOnCmMEeaQzqn + 0mvZ5lzfZ6UhANjGqXtWs4c49RmSGfdWkhJyaRL7QzRxDjoB6jev2KxjcUcgoyuQURRUtKgiAFjXbu8L + UD9ytAAAtgN8FVSAzw4ALAGkNwsA1OdfhHLF1aLqtJ9ej0spXNXaguWJolrhM/TicvBbNdBnUzQEANs4 + EQBcTADwO8PtKHrJ86pPAIAhAPjcf/9ZAAAEAP7vX4tWp4rbUlH8tqMN2VTRfJ350Cr7fvJFj+sQAGzD + RMzPKXpv0quh/1ZZCywaKP24SFJS9qQLkJul9Qbk5iA8qTPZLFL0ed77H9oO0TBuR7i9fmEI5MYgojKw + nhq8LQDC5tKWXnNPZwdWr1gqIv4i3V3CGNjXo3XhivV0iTE0moNOnDAedfV1Zq6/b+57eOXnN+EPtN/C + vljxkGAR4ovT8CUwP9MQAGzDRLr/r2j0uSFp0Qzhgipb+Q0oMAjqnzOzL4ZyyCn9ASCVwtxCABhLAOAr + DgDyV8Am8Gmpu7MdawgA2BtgAEC0VzPSMwDwZy4KygAwYfw41NdrORU8Tu+8+iou+sGVhbq+lf4KreR8 + +5d1f0MAsI1Sz6zmycTUr6uSnolopYLKYgYVgkRBaQBkT7kYuSEA2Cz6LADwwosv4sqrri52WDb4cc+w + LarkuzVpCAC2USIA4NXhFP67WIGgIlnFKNzXtp1VgNlDALC59DkAAGcSfR9b2Mpra9MQAGyDRKI/dyF+ + kUbeU6xGiBngY/nSYHITAKQCNYEDfk69FOphs0U0G9ezF5V9aWKzDeD9D+wA4C6p1IqL6MlAXn8AXp+W + 9+72ePFVpUQ8RmOSNiOpujvbhNGPowF7ujpE7GRQ1gz1JVIMsqTA5Q0JkAxXNcAXKtczRyV09UaxYpVW + p8Pt9vQ1NjYs3muvvRYGAgFuNPODqqrqni2/0q1DQwCwjRExP+cdPE6jfriRE24+gEGKj1hXerXADmAC + w+n0fA8/zezdJyzbugQwf/4C2/HiORkKJDN3KRguRbAkLDwDPgKDrypFaFVPJhJ68g8xSHsrVi75iCSA + NHpIGmAAaCzResU0hpIiFTvn8NFgO5D1VkFxlYiR59iJ0vJqjBw3xXZ8jhrUaTgBwBZV8dmaNAQA2xgR + ABxMi8eT4IYP1srBEuy1BHSyrvA2w7+tHJkuDeyyPzB5Zh4ASiuQm7I7AUCaAGC+7Tr6MlpagxEuHCor + FwDgZgAIBPFVpV4S81kKMNJ/uwkAPlm8QMT893S0iVDrprAGAKNKU+C6KznZS6DrQMpdhawzpFdaklBW + UWsDAP6emN74OLy6eggAhshC3bOauXXYEzTgs1Cw+luSyGyfrTsUerqKhQRbSZk4HdkrbxUSwIIFdgmg + O54RySmGAbuEACBEUoDL44E/EMJXlXo6OwUAGIVAutpaCADsEsDoci1/a0y5VowlA48AgJijAmmHNjZC + AiAAaJ6ws+34NTVm4ZUhABgiO9Hqfywx8SMqO/mLrfaWwqHqAE+lnyfASgXHzE2ajtwPbhV2gCVLlth2 + be3uE3X/jTJgodJyBFgFIP1fqABGCy16OQqadW7rZOtaVFDcpH3jevRFenUVQEGkqxPrVy4VST99vZ1C + AhhToeXtjq3MCQDIgus/yEg4wkhLfjNiKxCuQlXjDvlnQ98NG65lC9KYH0Gf29mmwi+ilTU1NW1f9FgM + AcA2Qj2zmj2qlgO+F382DXjWVbwI4xctGVgkOcDoSmUFAGXSDODq24VRkKsEWenDBR8hkUgKjwHbCryh + UniCJXC43HB7A8IT4PF6tfLXoe1LIjBUIKZ02t74edEHc9G6bo2I+OM2YGqWtmdiWqplLi0AYFK9ljo9 + cZgbLtIBjNaIGdF/JZ9iHc350ZKtyT8DScYOO80Qf/v8QTGG7D3QvSrnEgD88YseiyEA2EaIAOA4mjiP + wF4vNE+WD5JU4OuXinsIrDUMbZKD/nmTABBPIE3SAW/3lZTBTaK/0+WBy+sXq76b1AHRAGM7BwCrFMA5 + /i0MAMT8DAIi+SId1QatAAAmEQA4udiKnpDFLRJFe3O9zkI0F8DGjKnzi7GaNFUDAFajRANRCwDU1tZu + EwDQ3d2tlpWV9WtTMUSfE/XMGu2hafMsTZpv8Od+Rj7VIhEUuPf6RQRafihZdygoTS1oRwaAP5juQCu9 + 9dZbImeA7QMMAK5AGG5/SFQ8MgDA5/eLVa1UL4W9vZAVAJJJe3v49994GetWLhP3zADgkhUEXFpDVrfW + XwU7NGjlGHZs8MFFOgDXCWDKZdOiH4NRh7Et7sbCjnytRWb0nXfdR/wdCJWIz5xDoKtQ59bV1Q0BwNeR + umn1l/TVnz/bQvkHiPqzUqGdwCb6W7cVfKfuMAQAQwAwpAJ86dRzUPMr4NW/WOOgAut+0dz/guQfm4oA + o7x1/oPZcdYCAIpirz61fPlyES3IcQK8LZ7MIJHOkpiriIaZQgUgIGBrdzCsAYBmzJIQLA2LWoLm+Q2D + Ib6cwh5G4BOL9Sm9uk86pen+a1cs0UR9nTo2rEJURPhpTVVD7gzq/Qk4aNBCroy438amUWLfxhEjREsw + 1bwnyfTX8jl6I31Ys9Zi6CewVMomaY/J6RP7VlXXwuPxIhAMbfT5/D2WSMsXqqqqtqjl9+bQEAB8iXTx + 969sntS67Hunr3rzvIzHb+scWxjMY+sMjP7hwAaZzD2IC9DcZgGAQmv4ypUrhQEwo9fD64slEEukiPk5 + ezBDk9QhQoVZAgiUGgCgpQ6zy5ArCZnXtA0AQEa3ZSQTcQ0AklpOPvv4rQDQ274ByWivBgC0X9iVRkMw + TgCgoITAgO93+IixYt9hI8aIkmkm43OPBUk2KwhHeruxfk0+yU8YBv3jtGviKsy0b03tMHh9PoRKwvD7 + A9ZQ638SABz7eY/NEAB8SXT55ZeXeUtKnz1t9ynTR/7lJiRpxZUCIW01Ue2WQGswkLWrsOWtX8ch03Yg + FUgNVk9CuByYNKNonf/WA09GtqTcLInd3tGJru4eJGnljEZjIvTV5dEkAF9Y64jLQUJ8kqq6YXC580VG + ZUv+wJcBACLakRie7yXaF9FamseiYtvCuW+ZVX/F8KRjmuVfH7UKTxLNoQicpAqUe1IiNLpypBbcUzVq + ZxoHvWQa36PDLbZDB4949wa0r5ibPzYNzrKEltmdVN3i83CSJvz+IMoqKhEMhsQ4GQBQU1MzBABfVZoz + Z84txx177Hf33Gsv5D6Zj96bvwdl8UI4WZ90eSA59VZjvLPFcm9l9MK0fxT5jMLvB+k5aAWLriv/gFz9 + CK0pBtHGjS0EAh2inFiERFue6GwL4FXMBACPRzB4bUOT+NugLxsARL6DUGcy6OvVfPzxaJ/Y9v4bL9kA + wCVl4ZQUc4wrPQmMC/eI76p8SXG/ZaN2EdvLRs0UPSA4DJjvy+H0CEBQRftkBYnO1ehe/pp5bPYQvN+l + BQLFsi4BAE2jxrL4j4rKaiEFSPm4in/W1tYOAcBXkYj5d5k6ZcobZ5xxhtOot5dJJxGZ9yqc79KEnPc6 + 5PVrQLOWgIAmmEury6eqdsbv12cQeQY3VQH0tyz28yAUMTRGrvkjlGEjhf7PDNPR2YlukgBSugQg9pUd + msvLqa32nDnIVFJRZQS3COL8Ae6co3kwNDeGrK+aBiBYAaIQJCTRc9Hoz9ffXpHTjXDGxed0uwXr8BzI + xAzOoj9LAiKjj/6lElpX5dUL54piHwb51QjcSOhDpaLMm8OIMMf8a38zs5eOmCa2MhBI+qovGNflEwCg + 2QAUpPva0Lc+n2TFxsGPNmr3mdBLAlbUjSJVKoBguIJUgZBQnbjNGIHAQgKE+623WVlZ+cutPReHAOBL + oEvnzNlr+rRpr5522mn9tgl/cjyC1KJ5SM97A8r7r0NatQJSrE8YnGiGEOPJUK06gJWBC3T/wtoA5ipf + iAIFv0lc/2eow0eZzNYbiQivgJZKnBJlxNgWwMUyY0k9mMYQhd1eTRTWyRsImglEBoOzvYBJ1qMIjVRj + 490gY38DAIwEJiuldEu+qsct82cGAfEicODsPgEA9N7b1amBSFa75uiGpVp3ZZ3C2XXwqb1iFHiUgx4J + daVOYf0P+RziHkubtPDeMAEBSwCS7BRqAAMASwFGGHE2Sc+xZ515bB6rZataxN+JlJZP4CgZBtnlh8Nb + SuMWEJITe1d8Pj8qq2pst0kAsNXTMIcA4Eug7/3f/8lKTrlxj913u2K//fYb0I0mGkpk04gvm4/MB28h + Ne81yMsXw9nTCafKveTc2iosyZt0FQ4GAIV1BUQF4TO+B7WyVmsESv9Fw5VIBEv17MG0UA1i8YRgyO7e + iPidoh8hKznyAAV2eYXhJzFXs5VJglk0iSAfRuzSjYbOgj4EhlHMCgAMQlbSkndU3ZhJ+jV9ZmbnlZ/F + fgEA4ruMiOc3gnrE8XrWwGzJQ1SWWYlArlPkQbIC5vc6URkmxuZmn15NEgs37iT2DY/YWUgAMreQpnty + uv0aAOiGGiUTRyaeL/HPEsmK5VrR31RK80ZkvbVQHT6o7hJ694uUa38gQGpBELV1w623maqqqhoCgK8S + XXrppWeVlpZeNGnCxGmTp0zGyJEjbbpzIWWI6ePrViI1/20k55JksPgDuNs3wEMTnIt3qsRMqqw1++zn + BSiWPzCA4dDavdjAiezJF0I57FRTJWBVgJuLMCCsWrVK7JPOaCtze28U2VxeTA+TShAu17rmSsLI5RAT + ncnwFnh0+4FHjy40qBAAGHjicbvvnq3t0Bt3MvX1dAspgAt68itD15qIR0nNSqG3s1XcmEvX82t9MZHR + Z1Bt4j2SAtbS/efACdFOD0kvpbUaaPlCgtHDDZoRsKRhKkkALlMC4H1ZCjCvnYuouPLPk92QG+c/rY1V + XLvmlnQZkoobkawfSdWDYKgE4dIyepWjecx4622mampqhgDgq0aXf/d7Llqh9nE5nEfX1NYcNmHChKaJ + EyeKHnNut3vA37HWGu/sQGzh/xCb9xakhe/CT+DgSUbhFl1LXUI64HXMZjeAXTUwcgQKYwcEWQAie9KF + yBEAGDo4AwBNHgEAq1evFvvZACCbX1XZJlBSmi+UoeURaADA/QeZBADQP7fHrdkHjOtjwDCModycU+kP + AFE9eUeI8rRjX28BALDaQiqUUAE6NRHcJWnXV+eN0vHzd16b/AAlmXW6BJCDyxuEN1yjGT2Jwfl6SoZP + 1u6LgEAind/wBDi9nCuR51GWDGRX/hny9W38SGvym4n3CDtBa7oUyZwLfTkfEgQEDAAlYQ0ARjRrLkP9 + eaW9Xm+R4rCC+oh5E9gCGgKAbYjmzLmsjBhqf4/HfUhNTfUB48dNaBRg0LhpMEjE4+hcNB+JBe8gSxJC + cOUi+Hs64CfxViKxWpWdWivwgjyCQm8CLIAgPur75E6+CIqlmAgzfjQaFSsySwJMbBNgauvqtgEAJxA5 + 3PmVUDu/bAcci/HP/m43CvIqnylQAVQlm79YolhfRKz22itNongSuVRE6P2Z3g3iClzQflMltYiqPgaF + 06vhV1hs18qhMEN7/GWauuL2CQkgVKvFAQRrxxHzO+l7v5ACvCW1JCWU6vcDAQ4uT8BynQp612tp19lU + XFxHeyQHrhTel1SQZAeQN0Dn46jLEpTWjBT7GgbV4Y0jBpoC5xPz3rElc24IALZRunTOnHJirv3cLvfB + dbU1+48bN26EKRkMoiYw2yVpom1cvhTpxe8jPu8N+JbPR6htPUKZBLiZIXsVGAxUKa8qFM0/sKgGDADq + EAAMAcAQffE0Z86cilw2u6/b7TmktrZmnzFjxowaN368aETp8/kG/S3Lhe0bNqBn6ULE3n8T3sXzEFq7 + HCWJCHzsrxa2g3w4a7HoQiEV+Elk9/jzVYYU1QQDVd87dfNDkDxetLa12XILuAlJrzAUakY6Bg/2KjAZ + +8VEGS4IA6PwPFhTUQqiohywuwFLw2EStyVhW2CwSCXjooZfNpcVLj5HLgF3tgeykkQgvVoch79jKk98 + TGfIexVklpgsx2fRXtPxJb1FmgRfqVb401c2TKgGbn+5cNeW1E+El74ziMHDHSi3XasBVsYddW1chTRd + bzzShXQiirTkoZdP1BWIyNWiHqPH6xNq0V77Hmg7lgUYhwDg60JzLp1Tlsnl9nS7nKQm1Ow9auTIcRMm + TnQ0NTYiVFIy6G/Z8dXbF0XHskWIzX8H0oK3EVq5EOHuNgEGkrAb6BkvKBJoZDUaFolQTN39IoEEAUBr + qw0AWEIQvQj1FZq39egBOcZ+fTHNL881CBRbZKKUP4F+JU7Yqbys1Fa1WACAxQ3oyMXhyfXAoSRIClot + JAX+m6k0toBkkRwGJg1YxF8GAIS1YB4GAmEbCFQId2DJsB3gK89b7mWXD55ghf1oBR6OrvXLkUnGEO/t + IADoo2fkppcPSXp1o0rs4xUh1xL2PfDQgY41BABfRyLJIJDN5fZwOhz70sM7kMBgh0kTJ3oaSTIor6gY + 9Le8DsUyObStXI7Ux/Pg+t9zCC/7EK6OVtG9lu0G4KAWh2aQK5ZlWBhibACA4R0wiGsNcEtyAwB4Wx+p + D4YqwRTTDXtJQwJAEaOkfi6nbJ+S4XCJGW2oAUBCHJdXfwYAKRuHI90NiVZ9Z1SLzXfmtGCm0shcSGrW + cnzFPLOuiJjNUQ2VxBPSCnt6Q5X0HQNAmTAEhuonwFtarx9IgtMTyn/WyUiSMjouR9rXIkPqQDLaTUAQ + R0pxkgrHLxe6skGtZoDHJ+5tp90PsB3L7/cb6gEBQMUQAHyd6bLLLnPTpJ9Jf+5RWlp6WFNj047jx48L + j2puRm1NzaAdfnm6c2pMcu0nUAgM1PdegbToQ6it6+CIx7XJyqur0625Ga01CSydh9L3vCQAgL0DOUuC + TYTE/ajO8EzMnLF43AYAcb1hJgOAVa0oTFPg87gc9nspCQVN7wK/J81AIA0AkKF7SHVzhBWkvk/EQQ0A + CPf+j8R+S2AR/S1tCgD0Vd1DKz9vc/lLhQTANgGvrh5A9wr4y+2Ge4fTbd4JH6+vcz2y6QQBQA+yqQRS + OVkAQCLnQFfaawIAv+/ARV0txBKfAQAVFZVDADBEGpFkIGezuR1p1uwRCPgPHT582NTRzaOHjR/HjSvq + BzUiMvEamO4jJl6+AMrS+cgtnAt19UoobeshEyPDYDYhIRixB0Du0FN5houEISsAsMuQjXFGkVORncd9 + CRTWxbPI0PUkDz0953A6HWw8tNoWOHrOGvpLKsOa1atW3Wm9Xr/PKwKJDBCgiXyix+2ewkFAbAfIkoid + iHSIlbZnA9c+pPNmNBtEuO05kgDyRkVWDWRVU0sswrolpkISRj8mYeDjlV70BXDCXzmCpINKGGqLJ1SD + 8PAd8xcqpAKtorKsSwIs9is5AirRejyDZDpL45dBPJlGR09Us0FwtCEbH5tm2p5T44hRCGgVms+vqq4e + AoAhKk6XXHLpSEVVZnrd7oNr6mp3bR7VPI5UBTQ2NopU1E2RCE8mQMiuWowUqQzppe9DWbEMaF/PZXTh + Fbn1Oihw0I6ITpS0aEBZzrsaRZYs181SaKFNw+N2omvYKPyy14sr/vDntMfjcVsj/gwJoSD2/62Ghobd + B7vetWvXPkhvp/Jv+RWP9YkGHxwMtGrZIpGoI2e0nhwlax+DrOTrAjqzETjUJGCLZbSTIb6bOQBunzAU + csCQ218KDQBk+Gj1rxy7l+237qAeEOUwui4r5nn4na+RMxXj0QjaW9aKq1AkLXGoLzjZdqwdpkxDaZkw + Mp5fW1c3BABDtGmac9lllclEYsa6tWtvGDlq1NRdps/A+B13RHVjA+orKvoZ2IqRyBVgF1vrGqQ2rkeU + i2qsXw55w1o4WzfQxj54cyl4SZxWYqTbc4CQJDKAIAWIWfxB+MZMQu9Oe+Hnby/Brffcj5Uff5QiAPAU + AgBLC1ZpAlsAAMxUHAKcSMSwevliAQCSDgDBNY9BylkBoMc0EBrlPQ0yGdUCAEyaG9AhjIPsvhOdm7k7 + MAFAxZg9bdfmKdFqBHLqsCZYqLZjc8hyggArHiMA2KgDgKwBQCRgB4Adp04fAoAh2jLy+XyjEonEv5vq + aiZdPnUSqkaORWzieNTvMAWjmkejoaYWAdengQOYTrNkTkW0uxNStBv+XBI+JQMlSro+xwdIelRfSRBy + eSX63GH8/k8P4jc334S2DevZc5AgMd5nrPbGu1UC0K3ebw0bNmxQAFi3bp0JAAwenCsQoetKEgCsWblM + A4C0VgTVtfIfkCwSgCvdQWoJ2wd05lcV5Ov8Qr8O3QYhyRYJwAFvqAouX74zELsEK5qtlyrRd5pRUGQR + SiI30vRy8luSJYC4XQJQZa12QO8QAAzRViZWUJ+ZEfTVPVhZC6fLi6XVYWwcNQLpCRNQMWkKRo4bh+F1 + w1BdEtpqE2HZylX46wMP4M4778DGDRyYQ3O9paXV7XZ7PgUAvE8AcNJgxycA4LTZIwwASCUToViktzaV + SmD92pVa4lBKkwByyx6HapEA3MmNcGS0bEDB9BwXIIyEqiV0STcPGhKAyysYno2DQsfXg5q8pcNQPnKX + /IXRd2wnYJJFyLCkeVwNIKB/DFKJeB8SOgCIMmJCBZDRUwAAk3eaMQQAQ/SZiaNLHturIhy6L1iFYakc + eiV6ed1oLQ1hbUMdImPGwj1xAoZP3BENDY1COvA7N78ZSEtXF1559lk88OCDePXVV4VrUKe/0Wv253WD + 7e3tp9Lbg6Ibsl7ijCsDMb35yrO2giCe9jfgYlehYPwc5FwcrkyX+OxUkoOcRdKDhgzpQCJ1oAyhmmbL + LjJqJmnBPBwxKAlDqlt7lx0i3iBFAMCvJIFAd/t6sfLnJE0C6PDtZDvjlGkzUVYuPBIEAPVDADBEW0zM + IPdNLw877g7XYCzxQ0yhyc/iLxcEdUto9/uxvrYMrSNHITtuNErGTETT2LGoJ+mgrrICA2UqJHMKVm3c + gCUff4TuV17E35/8D56Z/3HhbgfT65nP6+aGAGBgGgKAITKIJ8Hvx4RDuL2iBnsqDvQR87L5TRaRcwpk + LsYhqYj43GgrCWBDXRX6mkYg1zwSvsZRCJeXw+v3Cek5kUjg/9s7uxBJriqOn67qrprpmeqv2ZnMupvJ + zhIDuxoDQYwsAcWH+IWIBiEv+mYgD4HgU0Aw8SHoiyDkTSEk5EVBCYLig6hoBBUV1jXB7G5m52NnJrPT + Pf2x099d3eU551b3VvfuJm52Zzah/j+41O3u6qKq6fuve0+dj2qxSK31SzR94SKdWFulX3P70eomjafz + oL+SqYfg3/op/39AAG4OBABEeZ7bc7m0S989skDftmco6Q+oPnLLMX+GoRhYg4A6SRYE1+ElwxQ1si61 + JZ0VDzCn26NCtUNH+z7Veh16oV6ll8qVSfc+efVFOsC7vwABuDkQADDJ97l9T6xTXyjk6RkvT2coRUme + DbR5wPesSIpyMs/2VQwkwm4gUXbiqmtRn7+/YQX0C79FP6lVaTX09Z9ArPXfPOgLYgEQ390HhkbBYDDw + Uo7zW/msUi5plN6Qtf/+g/Z21jV2XwQj0dkj6+oKL4V8ssSbUGIROqaGp9MtjgUOhb/IqCfuwUk3kiBE + 0oktmWQiSY0S5N/JnpaYQxqIpT+RpF7gUJdcrTi8n8ibRCTulG4/8ch4MJB6AkpWqMHgqSPz8xAAcMd4 + ltsPpJN0kvQl/qN9dXqWHnZcOt5LaOSdrY4+gSa2VO957uymArrM6nCh3aa/DDr0em2fdiT//o3zlEm2 + TPlHFw/74vhPLznYNFfXZEWk/5z9J72zNSwOylfWKpJVfUvye5HV3tPHgk7b5PlzW5dvIADjWJHciDIr + yH7klHb10SEPaon6k4Hvy10+kWKBTVPP9rTMeNO9LxIMZNHnHvvyxLFHLtEIBgJ3HMlY+mNuxvHdTlAh + maSllEv5aZeyoT9+xe9Tlwd9tdGmUsDTfZKCm713SU6onOP2OLe378aFQQDGfgsIALgpkv72RW5nbvdA + EX7O7Tvctu/WRUEAxn4LCAB4V2QR+3TYjt/Gcf5OZkbxs7t9QVEBmHAzpo21ldAuEKhtoNsoUvPKeQok + qKhpBMCqb+i+dn3dGAsHEkwUkO3va9KRIcbHJxpDbZE7d7921RGIP+s599DAnqJ+0uMti4IzS5abIWsq + R8n8srEBSAlx3lc8/6IgHwA4TMSH9Qlu3+Amlqz3iiISq58E3/+Z26+4/ZFM+sK7TqVSGQnAJJKuW0OU + w5iEWqVEW2sXNb14vWaKhvqVVf2st7eiAmD7NfUWdJuX1JU4yniqtQT5aRMeLO69IgDd7GkKUhnypxcp + cHKUns2Qly3QLLd7l01S0OGdPpPJXne+YewEBAAcGnILE//WM2FfYoujmcRkai8DX5Lf8dyZ3iR6j3ny + IcMC4PHmDzf6rNvtLPOsYC4qAJelXLiWFTMC0CsbAfDLK/q0wAhAj6ZUAMZtmpMC0Js2k6hAgoHkdfZj + NHAy1J8+ytuc1lDwMnnycgVaOmnSgod5CALP8/41eb6hAPyQB+8v389vAQEAIAIPiFd4863ha1kOrF+6 + qL4CtWrFLAt44Avd4QxAsg2JANTPk9O9Mna8SQHoTJmcgUEinAHkH9Q7v58+RoFbCOsCFCibl7oAp6OH + Ql0AAA6amwqAJDKtlNUG0NkLBaB8SUo3kd2rUmLAAtA4T6nONQFIXPcoJEHtoQBYrr72WQDkzt+fOcbb + vBYIlboAufwcnfzoqeiXIQAAHDS8PBgTAFNZuGXW/mFkYukdYwQsbq9ptR+/VdVsv63SCvUaJbUfyOC3 + u2UWhWtPOiWir7H4mPYHqawaBRdPnKapGR70hUVKe3mtmShGPymdNut50VPr8ACFAABwkEAArgcCAGID + C4BY6XLD191ORwyD+rhQBnuzsT/T7w/+Jp/tbG2Y/IbNmubz2y+uUmtfngJIHoGBugyna+dGxw4SNtWP + fU37fbegAnDi/lM0M5uhuYWjPP036c1Dq//vXdeNDsiAB+ibd/p6IQAA3AJvnDsrt2WNJNre3FBR6LAA + iJ9AbXeV2iIAgUktnmzv0Ezt36PvqgAcf1z7RgBsWmYBEMPfkYVFXvvnRolNmdcWFha+ftDXAwEA4BZ4 + ++IFz7ItFQAxCspTgU6rzjMAnyrFTWrui3uBZj8lv1Gk7pVrN22J+Mud/or27bQJ9Lln8bhW/slkszSd + NmXEQgef1+bn5yEAAHyQ2NraHM0Amo26DnRJ5CkzgXJxm5r1YQUkfv9qiarbb42+KwKw/LCp7uPOmHBg + sfY7jsuDP02ua5b4QwGYm5uDAADwQYIFQJ7fvSD9dqupMQM8C3iCh/yxSmmHGvXaqK5B42qZrmycH33X + sm36+COf137ay2m9v5lZTw1/yVTydywEajAIBeANFoCXD/p6IAAA3CZbm5uv8+bRvdIuNRr1kQBUeYmw + vrYy2k/W9o9+xpT3Ghr8pKBJuOZ/ktf8Pz3sc4cAAHCbbG9tqQDIgG/ycmAoAPtXa7S5uT7aTwb6Jz9l + AivF8KeBPqlhgRAIAAAfSnZ3d1UAZPBrCbQwjkBqFNaq12KOZKAv3XdS+45r6v4NKxozT/KUHwIAwIcN + FoDf8ObT1wlAq0m1WmW0n8wA7l1a1r4Y/hLhM/9wCfAMC8Crh33uEAAAYgwEAIAYAwEAIMZAAACIMRAA + AGIMBACAGAMBACDGQAAAiDEQAABiDAQAgBgDAQAgxkAAAIgxEAAAYgwEAIAYAwEAIMZAAACIMRAAAGIM + BACAGMMC8FnePMTtLAvAn260DwQAgBgDAQAgxkAAAIgxEAAAYgwEAIAYAwEAIMb8Dw0RRWUGcCYnAAAA + AElFTkSuQmCCKAAAAIAAAAAAAQAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAO7u7gIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADu7u4L7u7uVu7u7hIAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAO7u7hju7u697u7uJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7u7uGO7u7r3u7u4nAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAADu7u4Y7u7uve7u7icAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO7u7hju7u697u7u + JwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAA7u7uGO7u7r3u7u4nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADu7u4Y7u7u + ve7u7icAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AOfn5xDd3t4Y1dbYGMbIyhjAxMUY1tjZGOTk5Rjq6uoX7e3tBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO3t7Rjt7e297e3tJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5ufnlNvc3eHS09XhvsLD4bW6vOHQ0tTh5OTl + 4erq6tPs7OxDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7u7u + GO7u7r3u7u4nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADk5eUB5OXl + H9vd3jPb3N65yczP/7q+wv+Zoqn/ipad/7W7v//Y2dr/4+Pk8ujp6XDs7Oww7e3tDgAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADu7u4Y7u7uve7u7icAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANzd3gXe3+CN09XX58LHy/eksLn/kKKu/2mKo/9XgJ7/ip+v + /7G4vP/MztD+4+Pk7uvr69nr6+s/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AO7u7hju7u697u7uJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADv7+8C7+/vIe7u7jzl5eY82dvc + QM7R07O2u8D/nKix/3eQo/9egZv/NHOi/yFspv9Wf5//gZGd/6yzuP/V19j/5OXl8+rq6nHu7u467u7u + DgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7u7uGO7u7r3u7u4nAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAO7u7gfu7u6N7u7u/9ze3//KztD/srvC/42gsP9xkKj/SX2k/zFyo/8aa6j/D2iq + /zV0pP9Uf6D/gJqt/622vP/Jy87/4+Pk/+zs7PXs7Ow6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAADu7u4Y7u7uve7u7icAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAAAAHsAAACfAAAA + nwAAAJkAAAB1AAAAQgAAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7+/vAe/v7zXv7+9i7u7uZuvr67no6On/xsrN + /6Kqsf+Elqb/XYKg/z92of8Waqr/AWSv/wBkr/8BZK//FGmr/yVvqP9Qf6P/fZGf/6ats//U1tf/5OXl + +evr64bv7+9i7u7uW+7u7hUAAAAAAAAAAAAAAAAAAAAAAAAAAO7u7hju7u697u7uJwAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAF8AAADIAAAA+QAAAP8AAAD/AAAA/wAAAPYAAADUAAAAhAAAABsAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAADu7u4C7u7uhuzs7Prq6ur60tXW/b3BxP+Zqbb/c5Go/1aDpv8zdKb/Hmyo/wpmrf8AZLD/AGSw + /wFksf8ZcLP/LHq1/0iDsf9ljav/hZ6x/663vf/Cxcf/4+Pk++3t7frt7e3o7u7uNgAAAAAAAAAAAAAA + AAAAAAAAAAAA7u7uGO7u7r3u7u4nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABoAAACzAAEA/wYGGP8LCyv/Cwsr + /wcHIP8EBA7/AAEA/wAAAP8AAAD/AAAA5AAAAEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6urqA+np6Qfs7O2L6enp/+Tl5f+6vsH/kpuh + /2yJoP9GeaL/K3Cm/wtnrf8BZLD/AGSw/wBksP8BZbH/BWez/yB4vf82hsP/Q4m//0+Muv9mkLH/iJmm + /56lqv/Z2tv/6urq/+zs7e3t7e03AAAAAAAAAAAAAAAAAAAAAAAAAADu7u4Y7u7uve7u7icAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAlAAEA1w8ORv8eG63/JR7d/yQd6v8hGev/HBTl/xkU0f8UEaz/CQhi/wAADf8AAAD/AAAA + 9AAAADEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAADq6upH5ubmi+Hi4sjHysz/sri8/4Scrv9Zg6L/PHek/yFuqP8SaKv/BGWv/wBksP8BZLH/A2Wy + /xVxuP8heb3/R5HJ/2Wj0/9wqNP/eKvR/3qlx/95nLb/gZ2y/7K6wf/Exsn/3uDh7ejp6jcAAAAAAAAA + AAAAAAAAAAAAAAAAAO7u7hju7u697u7uJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHgMECNoaGJD/Ixv2/x4V9/8bFOf/JSHX + /0A/yf9nZsP/f4HH/3+Bzv9oZtD/Nzif/wEDHf8AAAD/AAAAwAAAAAIAAAAAAAAAAAAAAADu7u4B7Ozs + A+rq6gPm5uYD5OTkA+Tk5AIAAAAAAAAAAAAAAADj4+MC4ODgA+np6YXl5eX/2tvb/6mus/+GkJn/VX+e + /yhvpf8Vaan/A2Su/wBksf8AZLH/AGSx/wNlsf8HaLP/KH2//z2Lxv9rp9X/jrzg/5jC5P+dx+b/jbjb + /22gx/9pmLz/kJ+s/6Korf/T1dft5ufoNwAAAAAAAAAAAAAAAAAAAAAAAAAA7u7uGO7u7r3u7u4nAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAABEAABTNFRGv/yAa7v8yL87/W1rA/5GSwf/Fx8//7e7k////9v////7////+////9P/u7en/lJSg + /xoZF/8AAAD/AAAAPgAAAAAAAAAAAAAAAO7u7kPs7Oyq6urqrebm5q3k5OSt5OTkYgAAAAAAAAAAAAAA + AOPj41Tg4OCt1NXX17q+wv+gqrL/aYyo/0R4ov8ob6b/D2er/wdlrP8BZK//AGSx/wFksf8JabT/GnS6 + /yh9v/9Xms7/cqrX/4273/+hyOb/psvo/6jM6v+UvNz/c6HF/26Zuv+Toaz/paqu/9TV1+3m5+g3AAAA + AAAAAAAAAAAAAAAAAAAAAADu7u4Y7u7uve7u7icAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApKCgHAgIFtjk7iv9ycs7/qqzF/97f2v////H///// + ///////////////8//T19v/l6vf/3ub//93j///j6v7/w8fR/0NBOf8WFhabvLy8EbKyshGoqKcR09PT + bebm5vrk5OT+4ODg/t3d3f7U1NSYmZiXEZ2dnBGlpaUR1NTUhNnZ2f7Dx8n/mqOr/3iLmv8/d6H/GGmo + /wtmq/8BZK//AGSw/wBksP8BZLH/BWez/xJvt/8tgcH/Qo3I/3iu2f+XweP/pMnm/67Q6v+t0Ov/q8/q + /5e92/94osP/dJu4/5mmsP+rr7P/1tfZ7efn6DcAAAAAAAAAAAAAAAAAAAAAAAAAAO7u7hju7u697u7u + JwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABubm0kfn5+ + Zo+Pj8G6urv/7+/t////+f////////////j6///U3fn/qbjy/32S7P9Xcef/PFrn/y1P6/8nS/D/Ikbw + /1Nv7v+2v+n/tbvS/6ioqe+4uLjAsrKywKenpsCpqajYrayq/qmopf+npqP/p6Wi/6GgnuSWlZTAnJua + wKSkpMCpqqrfpqip/5Kco/9tiJz/UHqa/yVtpf8IZaz/A2Su/wBksP8AZLD/AmWx/wxrtf8ddbv/MoPD + /1ud0P91rdf/l8Li/6rN6f+w0Ov/s9Ps/6DF4/+Lttn/harJ/4Wfs/+NobD/vsTJ/8/R0//j5OTt6urr + NwAAAAAAAAAAAAAAAAAAAAAAAAAA7u7uGO7u7r3u7u4nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAbW1sAnBvbzKDgoN6pqamwtDP0PXz8/P///////////////3/3eP3/6Kz9P9og/H/PV7v + /yJG7v8XPe7/Fjzv/xk+8P8iRvD/Nlbv/zdW7f81Vev/Z3zj/9TV2v/Kysn/wMC//62trP+npqX/mpqY + /5STkP+Miob/iIWB/4iFgP+JhoD/iIaB/4eGg/+NjIv/l5aV/5GSkf+Gh4n/c3+J/1N2kv86cJj/G2mk + /wdkrf8DZK7/AGSw/wNlsv8IaLP/GHK5/zSEw/9Rlsz/f7Pb/5rD4/+szun/ttXt/7TT7P+w0ev/lbvc + /3eky/97n7z/kqGt/6Sqr//Z2tz/6enp/+zs7O3t7e03AAAAAAAAAAAAAAAAAAAAAAAAAADu7u4Y7u7u + ve7u7icAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsbGwHcnJyPomJiYevr6/P2dna/vv7+v////////// + //799//T1+j/lqTf/1Vw4/8nSuz/Fjzu/xc97/8dQe//IUXv/yNG7/8jR+//I0fw/0pn7v/Fy+r/wcfk + /7O51v+ussX/ubm3/66rp/+vrKb/tbCn/7Svpv+0rqP/s62i/7Stof+0raH/tq6i/7avo/+zq6D/r6id + /66nnv+tp5//op6X/5KPi/+Ag4X/ZHWB/1Btgf8+bJD/MW2a/xlopv8EZrH/EG62/x93vP86iMT/Y6LS + /36y2v+cxOT/rc/q/7jW7f+82O//osfm/4263f+Fq8r/f5y0/5Kmtv+3vsP/yMrM/uTl5u3t7e3o7e3t + 2O7u7jIAAAAAAAAAAAAAAAAAAAAAAAAAAO7u7hju7u697u7uJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGxsbAhzc3NBi4uM + j7S0tNjf39/////9////////////8fL0/7/H5/+Ck93/SWTe/yRG5f8WPO3/Gj/x/yBF8P8jR+//I0fw + /yNH7/8jR+//I0bv/ypN7v89XO3/Zn3r/9/g4v/Y2Nj/wsHB/6mop/+joJz/nJeP/6ehlv/AuKj/xbys + /87Ds//Uybf/29C//+DVxP/h1sX/4tfG/93Swf/Xy7r/z8W0/8a8rf+5sKP/pp+V/5WPiP98fHj/bHBx + /2Fyfv9YdIn/Mm2b/xBorf8gdrj/NYXD/1ebz/+MvN//pMrm/7LS6/+71+7/vNnv/7vY7v+Qu9z/caXO + /32fuv+PnKf/rbK2/9fZ2v/m5ub16urqSO7u7hPu7u4S7u7uBAAAAAAAAAAAAAAAAAAAAAAAAAAA7u7u + GO7u7r3u7u4nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAHNycjuMjIuPtLS02eLh4P////7////////////j5/b/qrfq/2uB4/84V+P/HEHp + /xY87/8bQfH/IUbx/yNH7/8jR+//I0fv/yNG7/8jR+//I0fw/yNH8P8jR+//UWzr/8nN4v/P0dr/wcC/ + /7a1s/+xrqr/r6qi/7avpf+/t6n/xr2u/9TKuf/XzLv/3NG//97Twf/i18X/5NnH/+XayP/l2sj/4tfF + /+DVw//c0cD/2M28/9HHt//IvrH/vLSn/6iil/+Yk4z/e3+A/2Zxef9SboT/QGyP/0B6qP9CicH/YaHS + /5vF4/+01Or/v9rv/8Hc8P+jyuj/kr/i/4Wrx/9+nbT/mKq4/7a8wf/Lzs/84uPj2+vr68Xr6+suAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADu7u4Y7u7uve7u7icAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHZ2dSylpKXD3t3d/////v////////// + /9Tc+P+Vp+//V3Lq/ytN6f8YPe3/Fz3w/x5C8f8iRvD/I0fw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv + /yNH7/8xUe//VXDu/1lz7v98kOr/zM3V/8jHx/+npaP/n5uY/6qknP+7sqX/ysCx/9/Uwv/h1sT/5NnH + /+XayP/l2sj/5drI/+bbyf/m28n/5tvJ/+bbyf/m28n/5tvJ/+XayP/l2sj/5drI/+TZyP/d08H/zsW0 + /7+2qP+WkYn/e3h1/3Fzdf9pcHb/XH2X/0+Luf9qo8//osnl/7jX7f+/2/D/vtrv/4y63f9yqNH/g6C3 + /5Odpf+1ubz/2Nna/+Xl5fDn5+hP7OzsDOzs7AMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AO7u7hju7u697u7uJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAi4uMmP//////////yNL4/4OZ8/9IZ+7/Ikbt/xY87v8aP/D/IETw/yNH8P8jR+//I0fw + /yNH7/8jRu//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/1Nu7//O1O7/3uDr/9bX3/+8vL3/tLOy + /7CrpP+yrKL/wLeq/9DGtv/Yzr3/49jG/+TZx//l2sj/5tvJ/+fcyv/n3Mr/59zK/+fcyv/n3Mr/59zK + /+fcyv/n3Mr/59zK/+fcyv/n3Mr/59zK/+LYxv/b0L//0si4/7qypf+oopj/ioiE/25xcv9leIf/XoKe + /2+Yt/+Ru9z/nMfn/53H5/+bxeT/hqvG/32etv+crLn/tLm9/8zOz/rg4eHI6enppurq6jAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7u7uGO7u7r3u7u4nAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB2dXMwv8DD8GiE+f8ZPu3/Fjvu + /xtA7/8hRe//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH7/8jRu//I0fv/yNH7/8jRu//I0fv + /yNH7/8jR/D/VXDu/9nc7P/k5OT/1NPT/6empv+kop7/vLSo/8rAsP/Yzbz/5drJ/+bbyf/m28n/5tvJ + /+bbyf/m28n/59zK/+fcyv/n3Mr/59zK/+fcyv/n3Mr/59zK/+fcyv/n3Mr/59zK/+fcyv/n3Mr/5tvJ + /+bbyf/k2cf/3tPB/9fNvP+moZf/eXd0/3F2ef9teIH/cYqe/3yqz/99s97/erDa/3mu1f+Hn7L/kZqi + /7i9wP/X2Nj/4uPj8Obm5lLo6OgI6+vrAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAADu7u4Y7u7uve7u7icAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAABbZZI2Gj/x7SJF8f8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv + /yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH7/8jR/D/I0fv/yRI7v9Wce3/09fm/8vLy//DwsD/q6ej + /62oof/KwbP/2M28/9/Uw//n3Mr/59zK/+fcyv/n3Mr/59zK/+fcyv/n3Mr/59zK/+fcyv/n3Mr/59zK + /+fcyv/n3Mr/59zK/+fcyv/n3Mr/59zK/+fcyv/n3Mr/5tvJ/+XayP/i18X/39TD/763qf+hm5L/iIeE + /290eP9qfY3/bJe6/2+gxv94oL7/gKK5/5+uuf+yt7r/ztDR/9/f37Ll5eWO6OjoLQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO7u7hju7u697u7uJwAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiRfKGI0fw + /yNH8P8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNG7/8jR+//I0fv + /yNH7/81Vu//eY7u/5mo7P/U1t7/tLOz/7Oxr/+yrKP/urKn/9jOvv/l2sj/5tvJ/+fcyv/n3Mr/59zK + /+fcyv/n3Mr/59zK/+fcyv/n3Mr/59zK/+fcyv/n3Mr/59zK/+fcyv/n3Mr/59zK/+fcyv/n3Mr/59zK + /+fcyv/m28n/5tvJ/+bbyf/l2sj/1su7/8e+sP+emJH/c3Nx/2Vzff9ehaP/ZY2t/3yTpP+MmqL/ub/C + /9LT0//h4uL/6OjoROXl5QEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAA7u7uGO7u7r3u7u4nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNH8BkjR+/tI0fv/yNG7/8jRu//I0bv/yNH7/8jR+//I0fw + /yNH7/8jRu//I0fv/yNG7/8jRu//I0fv/yNG7/8jR+//I0fv/0tn7v/b3un/4eHh/9PT0v+npqP/r6yn + /8W9sP/Pxbb/39TD/+bbyf/m28n/5tvJ/+bbyf/m28n/5tvJ/+bbyf/m28n/5tvJ/+bbyf/m28n/5tvJ + /+bbyf/m28n/5tvJ/+bbyf/m28n/5tvJ/+bbyf/m28n/5tvJ/+bbyf/m28n/5tvJ/+bbyf/f1MP/2c6+ + /7KsoP+LiIL/eX5//2t7h/90hZP/l6Kq/6yzufrDx8mk1dbWdeTl5XXp6uoeAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADt7e0Y7e3tve3t7ScAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + ACNH74MjR+//I0fv/yNH7/8jRu//I0bw/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv + /yNH7/8jRu//S2jt/9ja5f/Z2dn/y8vK/5+dmf+tqKH/1Mq6/97Twf/j2Mb/5tvJ/+bbyf/m28n/5tvJ + /+bbyf/m28n/5tvJ/+bbyf/m28n/5tvJ/+bbyf/m28n/5tvJ/+bbyf/m28n/5tvJ/+bbyf/m28n/5tvJ + /+bbyf/m28n/5tvJ/+bbyf/m28n/5tvJ/+bbyf/m28n/wrms/52Xj/+JhYD/dHRz/39/gf+rra//xcfJ + 9cbIyljX2NgC5ebmAunq6gEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAO3t7Rjt7e297e3tJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0bvFSNH7+kjR+//I0fw/yNG8P8jR/D/I0fw + /yNH7/8jR/D/I0fv/yNH8P81Vu7/laXu/6Gv7v+hr+7/oa/u/6Gv7v+vuuz/2tvf/87Ozf/FxML/rKeg + /7iyqP/a0MD/4tfF/+TZx//l2sj/5drI/+XayP/l2sj/5drI/+XayP/l2sj/5drI/+XayP/l2sj/5drI + /+XayP/l2sj/5drI/+XayP/l2sj/5drI/+XayP/l2sj/5drI/+bbyf/m28n/5tvJ/+bbyf/m28n/5tvJ + /+bbyf/Qx7j/urOn/5+bk/95eHb/g4OE/72+vv/b3N323N3eVwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7e3tGO3t7b3t7e0nAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAI0fwdiNH7/8jR+//I0fv/yNG7/8jR/D/I0fw/yNH7/8jR+//I0bv/ytN7/9adO7/YXru + /2F67v9heu7/YXru/3qO6//T1Nz/yMjH/8LBvv+1r6b/wLmt/97Uw//l2sj/5drI/+XayP/l2sj/5drI + /+XayP/l2sj/5drI/+XayP/l2sj/5drI/+XayP/l2sj/5drI/+XayP/l2sj/5tvJ/+bbyf/m28n/5tvJ + /+bbyf/m28n/5tvJ/+bbyf/m28n/5tvJ/+bbyf/m28n/5tvJ/9nPv//Lwrb/rKae/3x6eP+FhYX/xcXF + /9/f3/9tbW25AAAAEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAADt7e0Y7e3tve3t7ScAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjRu8NI0fv3SNH7/8jR+//I0fv + /yNH7/8jR+//I0fv/yNH7/8jR+//I0bv/yNG7/8jR+//I0fv/yNH7/8jR/D/SGXr/8vN2P/AwMD/vry5 + /7+3q//Hv7L/39XD/+XayP/l2sj/5drI/+XayP/l2sj/5drI/+XayP/l2sj/5drI/+XayP/l2sj/5drI + /+XayP/l2sj/5drI/+bbyf/m28n/5tvJ/+bbyf/m28n/5tvJ/+bbyf/m28n/5tvJ/+bbyf/m28n/5tvJ + /+bbyf/m28n/39TD/9fMvf+2r6X/f316/4ODg/+6urr/0dHR/0pKSv8AAADTAAAANQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO3t7Rjt7e297e3t + JwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAjR+9bI0fv/yNJ8P8jSPD/I0fv/yNH7/8jRu//I0fv/yNH7/8jRvD/I0fw + /yNH7/8jR+//I0fv/yNH8P9IZOv/ycvW/7y7u/+8urf/xb2v/8zDtf/g1sT/5drI/+XayP/l2sj/5NnH + /+TZx//k2cf/5NnH/+TZx//k2cf/5NnH/+TZx//k2cf/5NnH/+TZx//k2cf/5drI/+XayP/l2sj/5drI + /+XayP/l2sj/5drI/+XayP/l2sj/5drI/+XayP/l2sj/5drJ/+Xayf/i18b/3tPC/7y0qf+BgH3/goKC + /7S0tP/IyMv/R0dJ/wAAAP8AAADwAAAASQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAA7OzsGOzs7L3s7OwnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG7wEjR++1JDTo + /yQ86v8jSfH/I0jv/yNH7/8jR+//I0fv/yNG7/8jRu//I0fv/yNH7/8jR+//I0fw/0dk6v/Fx9P/tbW0 + /7i2sv/Oxbb/1Mu7/+Tayv/o3c3/5tvK/+XayP/k2cf/5NnH/+TZx//k2cf/5NnH/+TZx//k2cf/5NnH + /+TZx//k2cf/5NnH/+TZx//l2sj/5drI/+XayP/l2sj/5drI/+XayP/l2sj/5drI/+XayP/l2sj/5drI + /+bbyf/n3Mv/6N7N/+bczP/k2sr/xL2x/4uJhv+GhYX/qqqq/7m5wf9SU4r/AwUB/wAAAP8AAADkAAAA + CQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADs7OwY7Ozs + vezs7CcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0nxESNG8DEhRO8vIUPuGCJH7wkAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNJ8BklLOTQJhzf/yUz5/8jSO//I0nz/yRJ9v8jSPL/I0fw + /yNH7/8jR+//I0fw/yNH7/8jR+//R2Pq/8TG0v+ysbH/t7Sw/9LIuP/Yzr//597P/+rh0v/n3s7/5tzL + /+bbyv/l2sn/5drI/+XayP/k2cf/5NnH/+TZx//k2cf/5NnH/+TZx//k2cf/5NnH/+XayP/l2sj/5drI + /+XayP/l2sj/5drI/+XayP/l2sj/5drJ/+bbyf/m3Mr/59zL/+jfz//p4dL/6eDR/+jez//Iwbb/kI2K + /4iHh/+mpqb/srK3/1ZUu/8iHbL/HCef/xMmcuUWLJUqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOzs7Bjs7Oy97OzsJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjSfAaI0Tu + ZiU46bEiJ+TmJCbi/zAz5P0yNeXwJSrk3SQu5sMkNeilJDnphCQ/7WMjRO5DI0fvJyNJ8BEjSfEBAAAA + ACUy5w0mHN/PJhre/yYq5/8hNtT/HjK+/yJC5P8kSfX/JEn1/yNH8P8jR+//I0fv/yNH7/9HZOv/xMbS + /7Ozs/+3tbH/0ce3/9nPwf/s5dv/8Org/+7n3P/t5dn/6+HU/+jezv/n3Mz/5drI/+TZx//k2cf/5NnH + /+TZx//k2cf/5NnH/+TZx//k2cf/5drI/+XayP/l2sj/5drI/+XayP/l2sj/5drI/+Xayf/m28n/59zL + /+rh0v/s5Nf/7ujc//Dr4f/t5dn/6d/Q/8jBtf+QjYn/iIiH/6enp/+zsrr/WFPW/yce6P8nJe3/JUj9 + /yVL/fEjSPKJI0fwCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7Ozs + GOzs7L3s7OwnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAjSPAbI0PugSUw5usmIeH/Ihfe/0hI5v9wee//aXDt/4KK8/9gZuv/Jx7f + /yUb3/8mHd//Jh/g/yYh4f8mJuL5JSvk6SUx584kN+mxJD3rhSUy6JYiJOX6Gxu6/xwcqv8WFHz/FRR6 + /xkjm/8fN8r/I0bu/yRK9v8kSfX/I0n0/0dl7v/GyNT/t7e2/7q4s//Pxrb/2M/C/+7q4//z8On/8u7o + //Ls5f/u6N7/7OPW/+rg0v/n3c3/593M/+fcy//m3Mv/5tzL/+bbyv/m28r/5dvJ/+Xayf/m28r/5tzK + /+fcyv/n3Mv/59zL/+fcy//n3Mz/593M/+jezv/q4NH/7uba//Dr4f/y7ub/9fHr/+/o3v/o3s//x7+z + /4+MiP+JiYf/qqqq/7e2vv9ZVdT/Jh/g/yYc3/8lNun/I0nw/yNH8P8jR++/I0fvEgAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADs7OwY7Ozsvezs7CcAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQ66okmIOD/Jhzf + /yYe4P8kHN//NzXk/zo55P8jHN//Kybh/zQy4/8pI+D/JR7f/yYf4P8mHuD/Jh7g/yYd4P8mHd//Jhzf + /yYc3/8mHuD/Ix/h/z1F6P49R+T/KTPY/x8q0P8YIbz/FRqh/xUZkP8YIpv/HTC4/x84zP8gOc//RVrU + /8nL1v+9vb3/vry5/8rBtf/UzMH/7ujh//Pv6f/08Ov/9PDr//Pu6P/y7OP/8Orh/+7n3f/t5tz/7OTX + /+vk1//q4tT/6uHS/+jf0P/o3c7/593N/+jdzv/p3s//6uDQ/+rg0f/q4dP/6+LU/+vj1f/s5Nj/7ufc + //Dq4P/y7uX/9PDo//Tw6v/08ev/7OXc/+LZyv/Cu6//i4mF/4mJiP+zs7P/xMTL/15Z2f8mHuD/Jh3f + /yUr5P8jSPD/I0fv/yNH7/8jR++yIETvAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AOzs7Bjs7Oy97OzsJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAJi/sDyYb3nAmH+DXJh/g/yYf4P8jG9//Ixvf/yYf4P8lHeD/JBvg + /yUe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yIZ3/89PuX/kZ72/5ej+P+KlPf/eYL1 + /2Rr8/9NVO//OEDp/ycv2v8cJcj/FyK4/xYgpf8+Q6D/zc3V/8XFxf/Dwb//xL2y/8/Ivv/s5t7/8+7n + //by7f/28+//9vPv//bz7//28+//9fLt//Tw6v/y7uX/8ezj/+/o3//u59z/7eTY/+zj1v/r4tX/6+PV + /+zk1v/u5tn/7uba/+/o3f/v6d//8erh//Ls5P/18Or/9vLu//bz7//28+//9fLt//Tw6v/o4tj/29LF + /7y1qv+IhoT/ioqK/7y8vP/R0dn/Y13d/yYe4P8mHd//JSTi/yNG7/8jR+//I0fv/yNH7/8bP+9gAAAA + APv8/wL///8KAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7OzsGOzs7L3s7OwnAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + ACYf4AsmHt9eJh/gxSYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7f/yYe4P8mH+D/Jh7g + /yYf4P8mH+D/IRjf/0FA5v+Xpfj/nKn5/56q+f+eq/n/nqv5/5uo+P+Un/f/h5H1/3N79P9bYvH/Q0vu + /1Ja5v/Q0dz/y8vL/8fGxP/AubD/y8W7/+nk2v/x7OT/9vLt//f08P/39PD/9/Tw//f08P/28+//9fLu + //Xx7P/08ev/8+/p//Pv6P/z7ef/8u3m//Ls5f/y7eX/8+3m//Pu5//07+j/9O/p//Tw6v/18Ov/9vHs + //fy7//38/D/9/Tw//f08P/18Ov/8+3l/+Pd0//Ty7//trCn/4mIhf+Pj4//xcXF/9vb4v9mYeD/Jh7g + /yYe4P8mIuH/I0Tu/yNI8P8jR+//I0fv/xg97tkAAAAA////U9zc3GgAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAADs7OwY7Ozsvezs7CcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAmHt8BJh7gSCYf4LAmH+D7Jh/g + /yYf4P8mH+D/Jh7g/yYe4P8mHt//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/JR3f/zc24/9TVun/aXDu + /3qE8f+Hk/T/kZ32/5mm+P+erPn/oa36/56r+f+ZpPj/nqby/9ra4v/T09L/zMvJ/7u2r//Gwbn/5uDV + /+7o3v/18ez/9/Tx//f08f/39PH/9/Tx//f08f/39PH/9/Tx//f08f/39PH/9/Tx//f08f/39PH/9/Tx + //j18v/49fL/+PXy//j18v/49fL/+PXy//j18v/49fL/+PTy//j08v/49PL/+PTy//Tv6P/w6t//3dbL + /8jBt/+vqqP/jYuJ/5aWlv/Nzc3/4+Pq/2lk4/8mHuD/Jh7g/yYi4f8jRO7/I0jw/yNH7/8jR+//Eznu + +LPC/2DX1s7jEhISYgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOzs7Bjs7Oy97OzsJwAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgCAwWoAQED2gAAANQAAACIAAAA + FQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYe3zQmH+CYJh/g7SYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g + /yYe4P8mH+D/Jh7g/yYf4P8mHt//Ihre/x8X3/8fF9//Ixzf/ykk4P80MeP/QULl/1VZ6f9veO//iJT0 + /5ik+P+rtvX/4OHm/9vb2//S0dD/tbOu/8C8tf/d1cn/59/T//Lt5v/28+//+PXx//j18//49fP/+PXz + //j18//49fP/+PXz//j18//49fP/+PXz//j18//49fP/+fbz//n29P/59vT/+fb0//n29P/59vT/+fb0 + //n29P/59vT/+fXz//j18v/39PH/8evj/+vj1f/RysD/trCp/6ainv+XlpT/n56j/5SRzv+EgOT/R0Dh + /yYf4P8mHd//Jibi/yNH7/8jR/D/JEfw/xg97v9LbPj63+Ln8SgnJP8AAABKAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAA7OzsGOzs7L3s7OwnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAQBoCww3+BMTaf8DBAr/AAAA/wAAAP8AAADZAAAALwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAmHuAfJh7gfSYe4N0mHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh7g + /yYf4P8lHuD/JRzg/yMa3/8hGN//Hxbe/yAY3v8kHuD/ODfk/3N36v/d3uj/39/h/9fX1v+zs7H/vLm0 + /9XMv//e1cj/7ung//Xx7P/39PD/+Pb0//j29P/49vT/+Pb0//j29P/49vT/+Pb0//j29P/49vT/+Pb0 + //j29P/59vT/+fb0//n29P/59vT/+fb0//n29P/59vT/+fb0//n29P/59vT/+PTx//by7//t5t3/5dvM + /8a/tP+mop3/n5ya/6Khof+oqLL/XFfQ/yYe4P8mHuD/Jh/g/yYb3/8kNOj/I0nw/yNH7/8dQe//LlPy + /+Tt//pbWVPtAAAA/wAAADsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADs7OwY7Ozsvezs7CcAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQIEYg8QUP8VFHz/GBWT/xoYlP8NDj7/AAEA + /wAAAP8AAADfAAAAHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh7gDyYe32IlH9/GJh/g + /yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf3/8lHeD/KCPg + /11k6/9sce7/dX7u/52k7f+zuev/2drg/8PDwv/Ewr//x8G4/83HvP/k3tP/7unf//Tw6v/49/X/+Pf1 + //j39f/49/X/+Pf1//j39f/49/X/+Pf1//j39f/49/X/+Pf1//n49v/5+Pb/+fj2//n49v/5+Pb/+fj2 + //n49v/5+Pb/+fj2//n29P/07uj/8Ojg/+DYzf/Ryb3/tK+o/5iVk/+hoJ//v7+//8fG0P9nYtv/Jh7g + /yYf4P8mHN//JSjj/yNH7/8jR/D/IETv/x1D7//J1///trSq9wAAAPwAAAD+AAAAMQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAOzs7Bjs7Oy97OzsJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAABBDkPD1f3FRN8/xgVjf8jHNr/Jx/5/ycg8P8aGY3/AwQH/wAAAP8AAACgAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYd3wEmHd9GJh7griYe4PkmH+D/Jh/g/ycf4P8gGeH/HBPh + /yEZ4f8mH+D/Jh/g/yYf4P8mH+D/Jh/f/yYe4P8dFeH/R0rq/4uY9v+grvn/mqb4/6y19P/d3ub/zc3N + /8nIxv+/u7T/w7+2/97Xy//r5Nj/8u7n//n49v/5+Pb/+fj2//n49v/5+Pb/+fj2//n49v/5+Pb/+fj2 + //n49v/5+Pb/+fj2//r59//6+ff/+vn3//r59//6+ff/+vn3//r59//6+ff/+Pf0//Hr4//s49f/2NDF + /8a/tf+qpqD/kI+N/6Kiov/Q0ND/2dni/21p4f8mHuD/Jhzf/yYk4f8kQ+7/I0jw/yNG7/8YPu//nbH7 + //788v8hISH/AAAA/wAAAPUAAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7e3tGO3t7b3t7e0nAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAREhNN1yAiiP8ZFpz/JR3k/yYe7P8lHef/JR3p + /ygg+v8dG53/AgME/wAAAPUAAAAfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAJiHhISYe4IAmHd/fIhng/09L2P9ratT/Skra/yMd4P8mHuD/Jh/g/zw14f+wrur/v77r + /8vL6f+5ueP/REPf/3F27P/HzvP/sLjz/+Pk7f/l5eX/3Nzc/8LBwP/Avrr/zci9/9bOwv/i2s//7ufd + //Lt5v/49fP/+fb1//r5+P/6+fj/+vn4//r5+P/6+fj/+vn4//r5+P/6+fj/+/r4//v6+P/7+vj/+/r4 + //v59//59/b/9/Tx//Pv6P/v6d//5dzQ/9zSxf+7ta3/nJuX/6ampP+0tLP/xsbG/3560f9PSdn/ODDj + /ycd4/8mI+L/JEHt/yNJ8P8jR+//GT7v/1d08///////lpWU/wAAAP8AAAD/GRkZ6Y6OjxUAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAADt7e0Y7e3tve3t7ScAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAkKJZ9DRqL/Ih+q/yQc6v8lHuz/JR3n/yUd6P8lHej/JR3o/ygg+f8SElz/AAAA/wAAAEQAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0nxCSNF7iojRO5OJD7seCQ868oaK+r/c3nU + /97gv/9maNX/HBTh/yYf4P8mH+D/Qzzi/9vb7f/v7+//7+/v/+rr5P9nZtj/QDrj/8PF7f+zvPX/5ufu + /+7u7v/j4+P/xsbG/8HAv//Hw7r/zce8/9rSxv/o39L/7efe//Xx7v/39PL/+vj2//r49//6+ff/+vn4 + //r5+P/6+fj/+vn4//v6+P/7+vn/+/r4//v59//7+fb/+vj0//j18v/18ez/7+nf/+nh1v/c08b/0sm8 + /7Cspf+Tk5H/qamo/8LCwv/U1NT/WVaX/xURev8aFJL/IyLO/yRB7f8kTPf/JEr1/yNH8f8WPO7/rb78 + ///++f8oKCn/AAAA/wAAAP9EREXhp6enCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO3t7Rjt7e297e3t + JwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABKSU6T/z09vP8gGOX/Jh7t/yUd5v8lHer/JR3n + /yUd6P8lHef/Jh7y/yAcuv8CAwP/AAAAVgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNH7x0jR/BJI0fv + fSNH77EjR+/dI0fv+yNI8P8jSPD/I0nw/xtD8/9Pa+P/0dDB/4iLz/8dF+H/Jhzf/yYe4P9DPOL/3Nvt + /+/v7//v7+//6erl/4aI1P9GP+P/tLLq/3Nz5//P0O3/2trt/9vb6f/b29z/1dXV/8bFwv/CwLv/xsK7 + /87Hvv/Y0cb/5t7S/+vj2P/y7eX/9O/o//Xy7f/29fD/+fj1//r59//6+fb/+vn2//n39P/49fH/9vPu + //Tx6v/y7OP/7eba/+Xd0f/Y0cX/zMW8/7ayq/+npaD/qaim/6ysrP/Dw8P/zM7Z/83Q4v9cas7/HCuu + /xcbiv8VFX3/FhmG/xokof8fNsj/IELr/ydN9v/q8f//u7m0/wAAAP8AAAD/AAAA/4GBgeD///8LAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAA7e3tGO3t7b3t7e0nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + BRsdP8xiZsf/HxjU/yUd6/8bF6P/GBWI/yEbzP8mHuv/JR7o/yUd5/8lHev/JR/f/wUFFf8BAwpOAAAA + ACVK/QIjRu4lI0fvWiNH75UjR+/LI0fv8iNH8P8jR+//I0fv/yNH8P8jR/D/I0fv/yNH8P8jR/D/H0Px + /zdY6v/AwsX/x8jD/0FX5P8fKOf/JiDg/0M64f/c2+3/7+/v/+/v7//o6OX/pafO/0pG4/+1s+r/ODHg + /z024f9COuH/bGfj/9vb5f/f39//ysrK/8LBwP/Cvrv/xL64/87Hvf/c08T/4trM/+vl2v/t597/8Ozl + //Lv6f/18+//9/Ty//f08f/29PD/9fLu//Pv6v/x7eb/7eng/+rj2P/k2sz/2tHD/8vEuP++ua//qaei + /5ycm/+srKz/vLy8/83O0v+Dler/RGPz/y9T9f8kSvf/I0fv/yA81/8cK7H/FxqK/xQQdf8PD4D/QFDB + //////9mZWP/AAAA/wAAAP8AAAD/sbGx2P///wYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADt7e0Y7e3t + ve3t7ScAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFKQ0aO/zc2sv8hGe3/Hxm4/xQTcv8TEnD/Gxee + /yYe7f8lHej/JR3o/yUd6f8mHen/Cwkn/BUqh38mTP+QI0fv0CNH7/gjR+//I0fv/yNH7/8jR+//I0fw + /yNH8P8jR+//I0fw/yNH7/8jR+//I0fv/yNH7/8hRfD/Jkrv/6qyyv/My8L/ipvU/xlC8/8jQu3/Qk3o + /9zb7f/v7+//7+/v/+Tk5v+/wMn/VlPh/7Wz6v80LeH/Jh/g/yYf4P9TTeL/ycfp/9bV5//i4uL/3Nzb + /9PS0f/KyMb/xsO//8C9tv/Gwrn/0szD/9XOxf/a08j/3dbL/+Lazv/l3NH/5t7S/+ff0v/j28//3tfM + /9rSyP/TzcP/y8a8/8C7s/+4ta3/sa+q/6+tqv+8u7r/xcXF/9HR0v/P0t7/yM7m/2qB7f8jR+//I0fv + /yNH7/8jR/H/I0n0/yRK9/8jSPD/IT7b/w8anP9SUZn/+vz//ystOP8AAAD/AAAA/woKCv/f39/L//// + AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO3t7Rjt7e297e3tJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAUFHahDRqP/HRqk/yUd5/8WE4f/HB59/xgYdv8dF7D/Jh7u/yUd6P8lHef/JRrn/yUm5/8iPcr+JEny + /yNH8P8jR+//I0fv/yNG7/8jR+//I0fv/yNH7/8jRu//I0fw/yNH7/8jR+//I0bv/yNH7/8jR+//I0fv + /yNG8P8cQvH/lqLQ/6Ouzf+9wMb/Pl7o/x1D8f9AYe//29/v/+/v7//v7+//3d7n/9HSxv9pZ97/tLHq + /zQr4P8mHd//Jh3f/yce4P8tJN//WlTi/+np6//p6en/3t7e/9LS0f/Ix8b/urm2/768t//Iw7v/ysS8 + /87HvP/Qyb3/1My+/9fNv//Yz8D/2c/B/9XMv//QyL3/zMW7/8bAuP++urP/tLGt/6+uq/+urq3/sbGx + /8rKyv/c3Nz/4eLk/3qN7f8sTu//Jknu/yNH8P8jR+//I0bv/yNH7/8jR+//I0fw/yNJ8v8jTPX/Hkb2 + /0lh2v9UVVr/AAAE/wAAAP8AAAD/MzM0//39/bEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7e3t + GO3t7b3t7e0nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASDg9C6Dk7nf8aFqT/IxzY/xQTdf8TEnb/FRN+ + /yMd2/8lHer/JRvn/yUf6P8kM+v/I0fw/yRJ9/8jR/H/I0fv/yNH8P8jR+//I0fv/yNH7/8jR+//I0fv + /yNG7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fv/xg+8v99j9f/l6TQ/52ozv+Nm9P/GT/y + /0Bf7//a3u7/7u7u/+7u7v/V2On/2dfD/4OK2/+ztez/Mzbj/yUm4v8lJuL/Jibi/yUo4/9HTeb/tLfr + /7S56v+xuOf/vcPj/9ra3P/U1NP/0tLQ/87Myf/Mysb/yMXB/8XCvv/Bvrn/vru2/7y5tP+7uLP/vbq1 + /7+8uP/Avrr/w8G+/8PCv//FxML/yMjH/9DQ0P/X19f/w8Lg/7Kv5P+ysOb/YHTt/yRJ7/8jR+//I0fv + /yNH7/8jR+//I0fv/yNJ8P8jRO7/JDnq/yUy5/8mMPH/Hias/wAAAP8AAAD/AAAA/wAAAP9kZGT///// + ggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADt7e0Y7e3tve3t7ScAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAEARElf/LC+S/xkWm/8lHej/GRWS/xQTd/8fGbz/Jhzv/yUc5/8lK+r/I0Hu/yNK8P8jR+//I0bv + /yNH7/8jR+//I0fv/yNH7/8jRu//I0fv/yNH7/8jR+//I0bv/yBE7/8hRe//I0fv/yNH7/8jR/D/IETv + /yRH7/8gRO//Ezrx/1tz3/+vtMn/WHPh/8XGxP86Wur/MFLw/6Gv7v/O1PD/qLXv/6Wy6P/V0sL/mKbX + /4GV8P8sT+//I0fv/yNH7/8jR+//I0jw/yVK7/8uUu//LVHv/y1R7/9ogO3/4eLo/+Tk4//f39//1tXV + /9LS0v/Ly8v/x8fG/8DAv/+7u7v/uLi4/7e2tv+6urr/v7+//8PDwv/IyMj/zc3N/9PT0//Z2dn/5OTk + /+vr6/+Cf+X/Mijf/zAy5P8nSe7/I0fv/yNG7/8jR+//I0fv/yNH7/8jSPD/JTbp/yYh4f8mHN//Jhzf + /ycd5v8hG7v/AgIE/wAAAP8AAAD/AAAA/4+PkPv///8wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AO3t7Rjt7e297e3tJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAbRISYP8cHIT/FhSD/yQd4/8lG+j/IxnZ + /yYf3f8lK9b/JD3r/yNJ8P8jSPD/I0fv/yNH7/8jR/D/I0bv/yNH7/8jR/D/I0fv/yNH7/8jR/D/I0bv + /yNH7/8iRu//N1rx/yxP8P8eQu//I0fv/xs/7/9ohPX/PF3x/yRJ8P+3xvz/3uLy/8LDxP86W+j/pq7M + /4qZ1P8dQfH/eY/v/8PO9P9uhO//boXr/8TFxf+wts3/Xnfv/yRH7/8hRe//JEfv/yNH7/8iRu//I0bv + /yNH8P8jRu//I0fv/0to7v+grez/oq/q/6Ku6P+equT/nKji/5il3/+Wot3/kp7a/5Ge2f+Rndf/kJzW + /5Ke2P+ToNr/laHc/5il4P+Zo9f/mJe8/6Ce3f+lo+b/qKXo/19Z4/8mIeD/JELu/yNI8P8jRvD/I0fv + /yNH7/8jR/D/I0rx/yU05/8mG9//Jh3f/yYf3/8mHuH/Jh/i/ygh5/8NDj7/AAAA/wAAAP8ODw7/3NzZ + qQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7e3tGO3t7b3t7e0nAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAQCSERJf/xQSfv8VEXz/IxvU/yYl3f8lNNb/JEHY/yNJ6v8jSfD/I0fv/yNG7/8jRu//I0fv + /yNG7/8jR+//I0fv/yNH8P8jR/D/I0fv/yNH8P8jRvD/I0fv/yBE7/9AYvL/cY72/ztd8f8eQu//Ikbv + /4ui9/8jRvD/aoX1/9ni/f/H0/j/0M/G/1Jt4f9SbeP/xcbD/2J77P/e4e7/7u7u/+7u7v/Cyu7/rbTP + /83NzP+6w+3/QmLy/y9S8P8dQe//IETv/ydK8P8eQ+//HEHv/yRH7/8iRvD/G0Dv/ytO8P89X/L/IETv + /yJG7/8jR/D/Ikbv/yZK7/9CY/L/Ikbv/yNG7/8hRfD/JEfv/1N08/8jR/D/JEn3/xsopf8cFaL/Jx/n + /yYf4P8mH+D/Jhzf/yQ36f8jSfH/I0fw/yNH7/8jR+//I0fw/yNK8P8kOOn/Jh3f/yYe4P8mH9//JyDk + /yYf4v8gGNf/Hxfq/xUTdv8AAAD/AAAA/ywsKvP5+f8mAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAADt7e0Y7e3tve3t7ScAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKMPDVD/FBB6/xofj/8kPdT/I0fm + /yNJ8P8jSPP/I0fw/yNG7/8jR+//I0fv/yNG7/8jR+//I0fv/yNH7/8jR/D/I0fw/yNH7/8jR/D/I0fv + /yNH7/8jR+//I0fw/x1B7/8mSfD/X4D1/yVJ7/8xU/D/bYn1/xQ57v9lgPT/L1Lx/xlA8P+rs8z/g5PW + /xlA8v+psMj/19jd/+nr7//t7e7/7e3u/8DJ7v+Nm9j/2dfJ/8HI6/9ZePX/bYr1/zBV8f9Wd/T/f5v3 + /42k+P9La/L/GT7u/yZK7/88XPH/epP2/3WP9v8XPO7/OV/x/yZK8P8dQu//ZYP1/6W4+f90jvb/Fjvu + /z9j8v8uUfD/Z4T1/yRJ8f8iRev/GBqM/yMbzf8mH+T/Jh/g/yYd3/8lJOH/I0bv/yNH7/8jR+//I0fv + /yNH7/8jSfD/JDjp/yYe3/8mHuD/Jh/g/ycf5f8jHc3/DwyL/y4ukP9LTK//ExVA/wAAAP8AAAD/Ghls + 9kU/7jcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO3t7Rjt7e297e3tJwAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAArg8STP8ZHpj/IkLk/yNK9f8jR/H/I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv + /yNH7/8jR/D/I0fv/yNH7/8jR/D/I0fw/yNH7/8jR+//I0fv/yNH7/8jR/D/I0jw/x5E8P8zWfL/PmTy + /3GM9v9Rc/T/FT3v/2WC9f8qTvD/DTTx/4iX0/+pscz/FTzz/2F63v/Bw8X/lqju/7jG8/9/ku7/ZX7w + /2J64P/b18j/y9Ho/z5f8/+Goff/K1Hx/ypN8P8jR+//NVfx/5Cn+P9PbvP/UXDz/8bT/P+3x/v/JEjw + /x5C7/9OcfP/Kk7w/yhM8P89YfL/Ezju/2mH9f8iRe//Xn71/2eF9f9igPT/IEb0/yA81P8ZFpP/Jh/i + /yYf4P8mH+D/Jhzf/yUz5/8jSfD/I0fv/yNH7/8jSPD/I0jw/yUy5/8mHd//Jh7g/yYe4P8mH+P/JB7T + /woHef9ZXJr/4uTk//Hz6//Nz9j/V1t5/wcIVf8kHeL/IBbhrQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAA7e3tGO3t7b3t7e0nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgyqQwXLY/LI0Th/yNG7/8jSPL/I0fv + /yNH7/8jR+//I0fv/yNH8P8jR+//I0fv/yNH7/8jR/D/I0fv/yNH7/8jRu//I0fv/yNH7/8jR+//I0fv + /yNI8P8jSfD/I0nw/yNH8P8jQ+3/JD/s/xoy6f9/lPT/1+L8/x806P8YK+j/cYTy/4yj9/9Vdff/bYHc + /7/Bxf8rTOz/LVLw/8zOy/+yu+L/sr/x/6Kx7/99ke//RmXq/8/Py//V2eX/MFPw/3CL9v8fQ+//IUXv + /yNH8P8dQu//IUXw/3CN9v9miPX/HELv/5yw+P9AYfL/Fjvu/2qI9f89XvL/Ejju/1R29P9EZfL/eJP2 + /yZJ8P80WfH/xtT7/2aC9f8aQfX/HTO9/x0WqP8nH+b/Jh/g/yYe4P8mHt//JD7s/yNI8P8jR+//I0nw + /yRB7f8lKOP/Jhzf/yYe3/8mH+D/Jh/g/ycf5f8RDZb/PD6K/+rr6//y8e3/6urq//Lx7//19vP/h4nC + /xkYxv8kLOnxJhzfGgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADt7e0Y7e3tve3t7ScAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAkR/M+JUr70SVL/P8jSPT/I0fw/yNH7/8jR+//I0bv/yNH7/8jRu//I0fv/yNH7/8jR/D/I0fv + /yNH8P8jR+//I0fw/yNH7/8jR+//I0fw/yNJ8f8jSPD/I0Lu/yQ46v8lLuX/Jibi/yYh4f8mHt//IBbe + /0NE5P+4wPX/hIXt/xYK3P9IRuT/3OL6/4OI8P+psOf/yMnC/0hM2/8bHuf/srbd/83P0f+Nm+v/f43t + /1Zo6/8tR+z/ur7Q/97f4v84WOz/ZYD1/yVI7/8hRe//Fzzu/xtA7/8kSvD/X371/6jB+/9riPb/bo72 + /0tu8/8UO+//WXz0/8TV/P9efPX/KVDx/7DF+/+Amff/GD/v/yhQ8f9Wd/T/MVbx/yJJ9/8bLK//IBi5 + /ycf5f8mH+D/Jh7f/yYi4f8jRO7/I0jw/yNJ8P8kN+n/JiDg/yYd3/8mH+D/Jh/g/yYf4P8mH+P/IhvR + /xEQev+4u9D/9vXw/+np6v/q6er/6unq/+zr6//39+z/gITD/xg35f8lLOZKAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAO3t7Rjt7e297e3tJwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0bvcSNG7/kjR/D/I0fv/yNH8P8jR+//I0fv + /yNH7/8jR+//I0fv/yNG7/8jRu//I0fv/yNH8P8jR+//I0fv/yNH8P8jR+//IUXz/x1D9f8cPvL/HjPu + /yUq5f8mIeD/Jhzf/yYc3/8mHt//Jh7g/yYf4P8kGt//LCnh/zAy4v+gpvL/SEXl/xYO3v+oq/P/1tn6 + /8/U8//W18b/aWjT/xQK4P9QT+T/yMnG/6Ws1/9LS+j/HxXe/x4X4f+Pkc//1NXK/0VG2/9UVOr/Hx/h + /4OM8P+ruvf/obD2/6y49/+ZnvH/Njzm/11h6f9XXun/Iyvl/yIp5P8sOOb/iY7v/8vP+P80O+b/JSrk + /yIm5P8lKeT/Ji3l/x4q5f8iM+j/JULx/xsnpf8hGcP/Jx/k/yYf4P8mHd//JiTi/yNJ8P8jRe//JS7m + /yYd3/8mHuD/Jh/g/yYe4P8mH+D/Jh/g/ycf5v8TDq//Y2al//3+9//r6+v/7u7u/+/v7//s7Oz/6unq + /+3t6//s6+b/SmTk/xxC8ZEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7e3tBu3t7TLt7e0KAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0fv + BSNH8JojRu//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH8P8jR+//I0fv + /yNH7/8jR+//Ikfy/x1D9P8qStn/SVu+/1tgs/9RTrT/KSHZ/yUd4f8mH+D/Jh/g/yYf4P8mHuD/Jh7g + /y8t4f8nIuD/MjXi/zk14/9VV+f/HBPe/ywn4f+kpfL/s7X0/7a5yP+Pkc3/HRfi/xgR4f9wctL/z9PG + /1ld4/8eFeD/HRTh/2Nj1f/W18H/YmHV/0E+5v8kG9//Qz3j/6am8v+2vfX/Mi3h/xkR3v8iGd//Hxbf + /x8W3/8mHN//Jh3f/yYd3/8dFt//IRzf/yYd3/8kG9//Jhzf/yYd3/8mHN//Jhzf/yYc3/8nH+P/Gxqc + /yIcyf8nH+T/Jh/g/yYe4P8lIeD/JS/m/yYl4v8mHN//Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jx/l + /xYTm/9BQ1f/wMC7//Tz9P/a2tr/19fX/+fn5//29vb/9vb2//n37P+Upuz/Fz3w3SNH8AoAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNH7w4jR++zI0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fv + /yNH7/8jR+//I0fv/yNH7/8jRu//I0fv/yNH7/8jR+//I0jv/yFG9P8ePun/UGG0/5qcqf+6uLz/v7/D + /6iqqP8xL8b/Ixvk/yYf4P8mH+D/Jh/g/yYf4P8jGt//QEPk/0RE5f8vLeH/PDzk/zY34/8kHOD/Ixzg + /xsT3v8VDuD/kpPM/7K1x/8mIeD/IRjh/y8r3v/BxMX/f4LT/xsT4f8gGOH/QkDb/83Ow/+HiM//OTnm + /ygg4P8gGN//Fw/d/11e6P+Die7/Ixzf/yUe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8lHuD/Jh/g + /yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4v8bF5v/Ix3N/yYf4/8mH+D/Jh/g/yYe4P8mHN//JRrf + /yQb3/8mHuD/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8nH+X/FxZ8/wAAAP8UFBP/OTk5/xQUFP8QEBD/KSkp + /1lZWf+YmJn/5+be/7rE7f8dQ+/9IkbvMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjRu8NI0fv + vCNH7/8jR+//I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0fv/yNH7/8jR/D/I0fv/yNH7/8jR+//I0jw + /yNK8P8gPvL/JDLZ/21xo/+2tbb/vb7L/6Slsf+4ucL/hIao/yMe2P8lHuH/Jh/g/yYf4P8mH+D/Jh/g + /yUe4P8lHuD/WVzo/32D7f9pber/Jh/g/yUe4P8mH+D/Jh7g/xwU4v9zc9L/yczD/zk23P8iGeD/GxPh + /3l70f/Aw8T/MCze/yAY4f8qJt//tbfH/6ipyf8/R+X/JyHg/yYe4P8mH+D/Fw/d/2Fj6f9WV+f/Hxff + /yYf4P8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8mHuD/Jh7g/yYe4P8mH+D/Jh/i + /xsXmv8jHcv/Jh/j/yYf4P8mHuD/Jh7g/yMc3/8oL+X/Ljvo/yUe4P8mHuD/Jh/g/yYe4P8mHuD/Jh/h + /yYf4v8VFG//AAEA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wEBAf/KycT/09jx/yZI7v8hRfBXAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAI0XtASNH8KYjR+//I0fv/yNH7/8jR/D/I0fv/yNH7/8jR+//I0fv + /yNG7/8jR+//I0fv/yNG7/8jR+//I0jw/yNJ8P8kQu3/ICvs/ykmzf9+fZ//vL3A/7m6yv+3uMj/h4iN + /6ipqv9QULb/Hxjl/yYf3/8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yUe3/8hGd//JyHg/yIa3/8lHeD/Jh/g + /yYf4P8mHuD/Hhbh/1VU2P/S1ML/VlXY/x0V4f8iGuD/MzDd/8XIxP9yc9L/GxTi/x8Y4f+SlM3/vL3D + /3+K6P8+POX/GhDe/yMb3/8ZEN7/Pj3j/2tw6v8dFd//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g + /yYe4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYe4P8nH+P/GheY/yIcyP8nH+P/Jh/g/yYe4P8kG9//KTzq + /32U9/9gb/D/Hxjf/yYe4P8mH+D/Ixvg/yUe4P8mH+H/Jx/j/xUUdP8AAQD/AAAA/wAAAP8AAAD/RkZH + /6Ghof9GRkb/AQEB/87Nyv/m6Pf/LU7s/yBE8GwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHRa8Dh8ZvCEiHL4uIBq9KCUcvRAeM90+HUPw + /x9E8P8gRvH/Ikfx/yJI8f8jR/D/I0fv/yNH7/8jR+//I0fv/yNH7/8jR/D/I0nw/yNJ8P8kQe3/JS/m + /yEb5f8qI8r/hYag/76/xP+3uMj/trfG/7a3xv+5usn/ra+z/zk1wf8iGuX/Jh/g/yYf4P8mH+D/Jh7g + /yYe4P8mH+D/Jh/g/yYf4P8lHuD/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8hGeD/PDrc/8vNw/94edH/GxTh + /yYf4P8bFOH/gYPQ/77Bxf8tKd7/GhHi/2ts1P/LzMD/iZDg/7zB+P9IROX/MS3h/1NS5v+krPL/RUTl + /yAY3/8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g + /ycf5P8bF5n/IRvC/ycf5P8mH+D/Jh7g/yAZ3/9pePH/prL6/1hj7f8fGN//Jh/g/yYe3/9JTuf/KCPg + /yUe4P8nIOb/FxSD/wAAAP8AAAD/BAQE/52dnv/29vb/+vr6/+/v7/9vb3D/m5qY/6+yvf8wUur/IETw + bQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYhwD8mIMB6IRq9 + okhJ0cl4gunhfYfs9XuG6/xweeX5LirC5FNa2eZPXuL/PVDg/zNI4f8pQ+T/IT/n/x0+6f8cP+z/HULv + /yFH8f8jSfH/I0nw/yNH8P8kPev/JSzl/yYf4P8iGOT/KiXL/4WHov+9v8X/t7jI/7a3xv+2t8b/trfG + /7i5yP+3ubz/Tk60/x4W5v8mH+D/JR/i/yQe5P8lHuP/JR7h/yYe4P8mH+D/Jh/g/yYe4P8mHt//Jh/g + /yYf4P8mHuD/Jh/g/yQc4P8qJd//t7nG/5yey/8fGeH/Jh7g/yEZ4f85N93/yczD/2xt1P8VDeL/SUfa + /9DSwv9sbtH/enzv/9ng+v/U3Pr/xMn3/1hZ5/8cFd7/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g + /yYf4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jx/l/xsXn/8fG7b/Jx/l/yYf4P8mHuD/Ihne + /z5G6P+XqPn/X2jt/x0V3v8mH+D/Ihrf/3N98P9AQOX/IRnf/ycg6f8cGJz/AgMC/wAAAP9fX1///Pz8 + /+zr6//p6en/8fDx/+Pj4/8QEA3/RkdU/zdW8v8hR/JpAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAJiG/zh8Zvf8bFbv/TVHT/3SA5v9obt7/b3Lg/15l3/8sKMf/ZWvh + /4KL6/+Diur/gIno/4CK6f9+iev/c4Hq/19u5/9HWeP/LUPh/yI96P8lMuj/JiXi/yYd3/8mHN//Ixvj + /ycj0P+DhaH/vr/F/7e4yP+2t8b/trfG/7a3x/+2t8b/trfH/72+x/96fKf/Ix7Y/yId6f8oH9v/LSDP + /ysg0/8mH+D/Ix7q/yEe7P8jHun/JB7k/yYe4P8mH9//Jh/g/yYe4P8mH+D/JR7g/yAa4f+cnsv/urzG + /ywo3/8jG+D/Jh/g/xwV4f+JjM7/urzG/ygj3/8sKN7/vb/F/5iZzP8UDt//MCzh/z475P8jHuD/HRXe + /yYf4P8mH+D/Jh7f/yUe4P8mH+D/Jh/g/yYe3/8mH9//Jh/g/yYf4P8lH+D/Jh7g/yYf4P8mH+D/Jh/g + /yYe4P8nH+b/HRmq/x0Zp/8nH+b/Jh/g/yYf4P8lHd//ISHh/4WU9v+Vn/b/NDLj/yMa3/8gGd//dH3w + /2du7f8fFt7/Jx/m/yIcw/8AAA3/FBQR/9HQ0f/x8fH/6enq/+np6f/29vb/sLCw/wAAAP9kaH3/L0/v + /yA971cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgGsNgXl7Z + 0Kiq7fmztPH/uLny/8TE9v/R0Pn/UE3j/xwU3f8hGtv/Ix3a/yYf2P8qJdb/MzDV/0hJ2/9kaeH/eIDm + /4uV7v9udt//IhvI/yYc4P8mHd//Jh/g/yUe4f8hG9z/cHKj/72+wv+3uMj/trfG/7a3x/+2t8b/t7jH + /7e4x/+6vMz/ur7P/66zvP91d5P/USxj/14mO/9jKDD/YSgz/1snRf9RJmT/QySL/zcisP8sINH/Ix/o + /yIe6/8lHuT/Jh/g/yYf4P8mH+D/HBXh/31+0f/P0cL/Q0Hb/yAY4P8mH+D/IBjh/z893P/Nz8P/ZmfU + /xYP4/+cnsv/uLvG/ysm3/8hGOD/IRnf/yUd3/8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g + /yYf4P8mHuD/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/ycf5f8gG7v/GheW/ycf5P8mH+D/Jh/g + /yYe3/8iHuD/WXDx/2p88v8zMOL/Ixvf/yAY3v9sdO//i5f1/ygi4P8lHeH/Jh7j/woKUf9GRz//9fT0 + /+rq6v/p6en/8vHx//f39/9EQ0T/AgIA/3N5of8fQev/MDPm1Ckf300AAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYnPcD4uH/KdTU/V/S0vyZxcj60dPW/PpmZer/HBLe + /yYd4P8lHeD/JR3h/yUd4f8kG+H/IRnf/x8W2/8hGdf/KCLX/zAs3P8mH9z/Jh/g/yYf4P8mH+D/IRnl + /0RDt/+ys7T/ubrK/7a3xv+2t8f/trfG/7i5yP+1tsX/tbjI/5+fqf97Zmj/bklI/2s6Mf9oKRv/Zyge + /2YoIf9nKCD/aCgd/2koGf9qKRj/aCke/2IoMP9UJlr/PyOY/ysg0v8iHuz/Ix7n/yYf4P8dFeH/X1/W + /9bXwf9hYdX/HBXh/yYf4P8mHuD/HRbh/5OUzf+0t8f/Hhnh/3Jz0v/O0MP/Qj/b/yEY4f8mHuD/Jh7g + /yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh7f/yYf4P8mH+D/Jh/g + /yYf4P8mH+D/Jh/j/yMdzv8YFYv/Jh/c/yYf4f8mH+D/Jh7f/yYe4P8fQO7/HTXq/yQa3v8mH+D/IBje + /2Rs7f+frfn/REXm/yAX3/8lHeb/HBms/46Rm//y8e3/8vLy//f39//X19f/ZGRk/wAAAP8wLyb/XGe6 + /xU17P9mcO7/i5f1/0FC5cAgGN8zAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAACYpPMDanfqKTAz4l4jKOTgJSfj/yUp5P8lJeL/Jh7g/yYe4P8mHuD/Jh/h + /yYe4f8lHeH/JBzg/yYf4f8mH+D/Jh/g/yYe4f8hGtz/e3yo/76/x/+2t8b/trfG/7a3xv+4ucn/srPC + /5CTm/+AdXn/ZDEt/2EgG/9kIh7/ZSUh/2UoJP9mKCT/Zigk/2YoJP9mKCT/Zigk/2UoJP9mKCP/Zygg + /2kpGv9pKRn/YSgy/0wlcv8yIb//Ix7p/x0X6f9FQ9v/z9HC/4SGz/8cFeH/Jh7g/yYf4P8fF+H/R0ba + /8/Rwv9bWtf/RUPa/9LUwv9gYNX/HRTh/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh7g + /yYf4P8mHuD/Jh7g/yYe4P8mHt//Jh7g/yYf4P8mHuD/Jh7g/yYf4P8mH+D/Jh/f/xgWjP8jHMr/Jx/j + /yYe4P8mHt//Jh7f/yQ/7P8jQe3/Jh7f/yYe4P8gGN7/Y2vt/6Gu+v9weO//Hxje/yYf4v8bFNT/UlOr + //T07/+wr67/VVVW/xAQEf8AAAD/DQwH/3h4if8nPcf/ITrw/ywl4P9WWur/e4bx/1xi6+0mH+A/AAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + ACJJ8dQjR+//JDPn/yYi4f8mHuD/Jh/g/yYf4P8mHuD/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/JBzj + /ywnzf+dn63/u7zJ/7a3xv+2t8b/t7jH/7S1xP+OkZn/eWxv/2MoI/9lJCD/ZSgk/2YoJP9mKST/Zigk + /2YoJP9lKCT/Zigk/2YoJP9lKCT/Zigj/2YoIv9mKCL/Zigi/2YoI/9nKB//aikY/2YpJP9TJmD/Mh62 + /y4s5/+8v8r/pqjJ/yQe5f8mHub/Jx/l/yYe4/8fGeP/m53N/6yuyf8yMN7/wcPE/4WHz/8cFeL/Jh/g + /yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g + /yYe4P8mH+D/Jh7g/yYf4P8nH+b/HBih/x4Zrf8nH+b/Jh/g/yYe4P8mHd//JDrq/yNL8f8lMOb/Jhzf + /yAY3v9ja+3/nKj4/5Wi9/8+PuX/Hxbe/ygh5f9lacn/iY27/8DBxv8DAwD/AAAA/xMSCv97eoD/QEar + /xs/5/8mL+f/JBrf/x8W3v8iG9//Ulbp/zUy458AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkKeRLIz3r+CUo4/8mHN//Jh7g/yYf4P8mH+D/Jh/g + /yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g/yYf4P8iGuT/NzPE/6qssv+5usn/trfG/7a3xv+5usr/kpOb + /4WDiP9mMS3/ZSUg/2YoJP9mKCT/Zigk/2YoJP9lKCT/Zigk/2YoJP9mKST/Zigi/2onGf9pJxz/Zygk + /2UoJ/9hJCT/YSMh/2IjHv9iIhz/ZCIZ/2cjD/9nIxX/VCJR/7Kosf/KzMj/MCyx/xkSp/8hGb3/IxvN + /x0U1v9NTNP/0NLD/1la1v+am8z/qqzJ/yQe5P8mHub/JyDm/ycf5f8nH+T/Jh/i/yYe4P8mHuD/Jh7g + /yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/ycf5P8iHMb/GRaS + /ycf4/8mH+D/Jh/g/yYc3/8lMuf/I0rw/yNG7/8lKOP/HxXd/2Np7P+bqPj/maX4/4GM8/8oI+D/MCzh + //z///97fIr/LjFX/xoaPP8MDB//UVSC/ztHtv8ZOd//JEfy6CYi4ZQmHt/kJh/g+yUd3/8jGt/zJh/g + QQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJR3f + ByUe4NcuQur/Jh/f/yIZ3/8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g + /yEZ5f8+Pb//r7G2/7i5yf+2t8b/uLnI/6ytuv+Lj5b/gGtu/2EgG/9mKCT/Zigk/2YpJP9mKCT/Zigk + /2YoJP9mKCT/Zigk/2YoIv9lKSj/SzV8/zc+t/8wQMn/M0TN/09Zyv9aYcb/X2PB/19guf9fW6//YFem + /1RJm/9BM4b/aG+6/4eb3P8yQKz/FR+Z/xcgmP8UG5b/EhWS/w0Pjf+ZnLX/oKK6/3Z4qf/My8P/LCmc + /xcRnv8eGK7/IRu+/yMdzf8lHtv/Jx/j/ycf5v8nH+X/Jh/i/yYf4P8mHuD/Jh/g/yYf4P8mHuD/Jh/g + /yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYf4v8aF5X/IhzI/ycf5P8mH+D/Jh3f/yUm4v8jR+//I0jw + /yNE7v8dN+v/Vmvx/5yn9/+Un/f/n6z5/09U6f9gX+r////9/0FAPP8AAAD/Hies/yQ94f8fP+X/HkLw + /yRL9P8kMeahAAAAACYf4BAmH+AqJh/gOSYf3yUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmHuAjIRrf90pP6P9zffD/OTjk/yAY3/8fF97/IRjf + /yIa3/8jG9//JBzg/yUd4P8lHeD/JR7f/yYe4P8mHuD/IRjl/0RDu/+0tbr/uLnI/7a3xv+5usr/oKGs + /5GVnf90UVH/YyId/2YoJP9mKCT/Zikk/2YoJP9mKCT/ZSgk/2YoJP9mKCT/aSYZ/1AyaP8cS///H0n6 + /xtE9/9AY/j/nqz//56s//+dq///nav//5yr//+dq///mKn//z9j/v8WPvX/GkDy/yJI9f8jSPT/LFD0 + /0Nh8v9NaO3/SGLl/1Nq5P9rguL/UmjR/4SX2f8wP6r/DRaP/xAUhP8QEXz/EhB5/xURf/8YE47/HBak + /yAZvf8kHdL/Jh/g/ycf5v8nH+X/Jh/i/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/l + /yEbwf8aF5j/Jx/l/yYf4f8mH+D/Jh3f/yQ76/8jSfD/I0jv/yBG8P8xU/H/kJ33/5ym+P+RnPX/LCnh + /7a5//+xsaf/AAAA/wEAAP8fO7b/JEv//yNI8v8jSPD/I0Ht/iYg4DgAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + ACYe4DwmHuD/IRnf/0ZH5v9+ivL/dH3w/1db6v9HR+b/PTzl/zUx4/8wLOL/Kyfh/yYi4P8kHd//Ixvf + /yIb3/8bFOT/QkG6/7S2uv+4ucj/trfG/7q7y/+foKr/k5eg/25EQ/9kJB//ZSgk/2YoJP9mKST/Zigk + /2YoJP9mKCT/ZSgk/2YoJP9oKBz/VixV/yI98f8gQ/j/IUTv/ylL7/9SbPL/XXfz/1548/9jfPT/Y3z0 + /2N89P9nf/T/OVry/x9F7/8jSfD/I0nw/x5F8P9IaPP/kaD4/56p+f+irPr/nKf6/5Ke+v+Mm/v/fI74 + /3eL+/9ofvf/VW7w/0Jc5f8xSdf/IjnF/xgqsf8TIJ3/ERmO/xMViv8XFZH/HBak/yEZvf8kG9L/Jh3e + /ycf5P8nHuX/Jx/k/yYf4/8mH+L/Jh/h/yYf4P8mH+H/Jx/o/x0Zpv8eGq7/Jx/l/yYf4f8mHd//JSbi + /yNH7/8jR+//I0fv/x1C7/9KZvL/jZ33/0dU6/8tN+n/5ur5/zMyL/8AAAD/AAAA/xo0qv8lSvv/I0fw + /yNI7/8lKeOiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIhrfVx4W3/8cE97/Fw/d/yMf4P9WWur/eoXx + /36K8v95hvH/e4Xx/3qE8f99ifL/hJD0/4WO9P99hvL/eIHx/3F79f9xes3/qamy/7m6yv+4ucn/ubrK + /6Kjr/+UmKL/cEtK/2QiHP9mKCT/Zigk/2YoJP9mKCT/Zigk/2UoJP9lKCT/ZSgk/2YpJP9oJx7/WCZP + /zYls/8kI+f/ICPt/x4g5P8eIeL/HyLj/x4k4/8eJeP/HiXj/x4m5P8jKuT/JSzk/yUt5f8lL+X/JDTo + /yI56v8rR+3/P1vw/1Nu8/9ogPX/e4/2/4qb9/+Wo/j/nqj4/6Kr+P+iq/n/nqn7/5aj/P+Jmv3/eI38 + /2R8+v9OavT/OFbp/yZD2P8aM8T/FSiz/xYhqf8ZHaj/HRus/xsVtv8eFsL/IBfO/yIa1/8mHt7/Jh7i + /yYf3/8kHdL/HBik/xQSdP8cGKT/Jh/i/yYf4/8mHN//JTHn/yNJ8P8jR+//I0fv/x9E7/8nS/D/FDru + /3aT//+2ta7/AAAA/wAAAP8AAAD/GDCa/yVL/P8jSfD/JTDm2SYc3xUAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAA4M+NqUVDn/3R07f+Sk/H/m5/z/zEt4f8dFt7/JB7g/yUf4P8nIeD/KiTh/zEu4v9EQ+b/bHTv + /4qW9f+Rn/b/l6T6/5Kd6f+am6r/t7jG/6mqt/+4ucn/rK26/5GUnP+OhIv/XyUg/2UjH/9mJyP/Zigk + /2YoJP9lKCT/ZSgk/2UoJP9mKCT/Zigk/2YoJP9pKRv/aCcd/1ckUP86HaD/JBjX/x0U4v8jGeD/Jhzf + /yYc3/8mHd//Jhzf/yYd3/8mHd//Jhzf/yYc3/8mHN//Jh3f/yQd3/8hHuH/HiLi/x4o5f8iNOj/KUDr + /zRQ7/9CYfH/VHDz/2V+9f91ivb/hZb2/5Kf9/+cpvj/oqr4/6Kr+f+bpvr/jZz6/3mN+/9he/v/SGf2 + /zJT7P8cPN3/bX/S/0JSuv8qNKX/JymV/xYTiv8XEof/FxKD/xUSef8UEnX/FBN6/xMSc/8bF5r/Jh/f + /yYf5P8mHd//JDjq/yNK8P8jR+//I0fv/yFF7/8ZP/D/usr+/1BORf8AAAD/AAAA/wAAAP8ZMI//JUz+ + /yUy5+EmHN8rAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjJ+onU1fz/2tr9/9vb/f/i4/7/VFHn + /x4W3v8lHuD/JR7g/yUe4P8lHeD/JBzf/yEZ3v8gGN//JyLg/y4r4v8yL+L/Njbo/3l+sv+vsLX/jIyT + /7e4x/+5usr/kZGZ/5+jrv+Pho3/Zzg0/2EkHv9hIh3/YSEc/2IhHP9iIRv/YiEc/2IhHP9iIRv/YiEc + /2EiHP9iJR//Ziwe/2s8Lv9qVHH/XFvJ/zUy4/8hGuf/JBzj/yYf4P8mH+D/Jh7g/yYf4P8mHuD/Jh/g + /yYf4P8mHuD/Jh7f/yYe3/8mHd//Jh3f/yYc3/8kHN//Ih3g/yAg4f8eJuT/HS7n/yA56v8lRO7/L1Hw + /zxe8v9Na/P/X3n0/3OH9f+Glfb/lqH3/6Cq+P+hqvj/mKP4/36Q+f/Czf//j6X+/5Kq/P9phe//EzLW + /xYruv8UH5v/FRaD/xQQdf8UEHP/FBJ5/xMSdP8ZFpD/JR7X/yce5f8mH9//JDfp/yNJ8f8jSfD/HkPv + /z1g+v/AxdD/CAcF/wAAAP8AAAD/AAAA/xQkg/8eJ/TDIBbfIwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAA19b8k9jY/P/a2v3/ztD7/7S39/9GQ+X/IBjf/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g + /yYe4P8lHd//JBzg/x0T3v85OOj/h5Lf/5ucpv+7vMr/trfH/7e4yP+0tcT/ioqR/4yPlf+WmKH/kouT + /4d4ff+BbXD/fGZo/3xkZv94XV7/emFj/3xlZ/9+aWz/hnV5/5CJkP+hoKv/r7LA/7m8xf+8vcD/oKO5 + /2FjsfIuKs7cJB3i/yYf4P8mH9//Jh/g/yYf4P8mHt//Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g + /yYe4P8mHuD/Jh3f/yYc3/8mHN//Jh3f/yUf4P8jJOL/ICrl/x4y6f8cOuz/HkLv/yRK8P8wVfH/Q2Ly + /1lz9P9zhvX/hZT2/8bN+//Kz/v/yM77/6q1+v9xhvn/WXX7/z5f9/8nSOj/GDLL/xciov8WFX7/FA9z + /xMRcf8XFYX/Ix3K/ycf5/8mHeD/JSvk/yQ+7P8XPO//gJr//4SDev8AAAD/AAAA/wAAAP8AAAD/QkJ0 + 0V1V4wgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACutPaZnqDz/2xt6/9APuT/JB7g + /yIb3/8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYe4P8kG9//Livh/4SQ8/+Zpv7/h43D + /6Kiqf+7vMr/uLnJ/7i5yP+3uMf/mpul/6ChrP+8v9D/u7/Q/7q+zv+5vs7/ur/P/7q/0P+8wdL/vcLT + /7u/0P+2usn/rK+9/5+gq+2Pj5bNgICEoXNzdXNubWlGc3NdH0ZHqQkiGudAJh/goSYe4O0mH+D/Jh/g + /yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYe4P8mH+D/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g + /yYd3/8mHN//Jhzf/yYf4P8lI+L/JCvk/yEy6P8eOuz/HD/u/x5F8P8bQu//mK35/5Ck+P+ruPn/oq34 + /5yl+P+jq/j/mqX4/4iY+P9ngPv/MVX3/x094f8dLbT/FxmH/xMPcP8UEXX/Hhmt/yYf3/8nHeX/Jh7g + /x4h5P+1vfT/NzYt/wAAAP8AAAD/AAAA/wAAAP96enTU1tbOBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAACQg358VD93/EQnd/xkS3v8hG9//Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g + /yYf4P8mH+D/Jh7g/x8X3v1ob+6Vnar5jJSg+J6UoPy3h4/IzJKToOevsLz/urvL/7u8zP++v9D/vb7P + /7q7y/+7vMz/u7zM/7i5yf+xssH/p6iz/5iZo+eKipDEfH1/mHFxcmdqamk8ZmZlGQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAJh7fHCYe4GwmH9/CJh7g/SYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7g + /yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYd4P8mHd//Jhzf + /yYe4P8mI+H/HyXk/ztP7P+0xfv/O1zx/5Cq+f8zV/H/QWDy/2l/9P+Mmvb/nqj4/6Or+P+Rnff/PV30 + /yBH9/8jRev/HjPE/xgekv8UEXD/FxKD/yAauv8gF939OTbr/r/Bz/8HBwT/AAAA/wAAAP8AAAD/AAAA + /4+PkPXNzc0cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPDrjqWNj6v+Fh+//o6X0 + /5SZ8v8pJOH/JB3g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh7g/yYf4P8mH+D/IBfenQAAAAAAAAAAAAAA + AAAAAAAAAAAAdnmZFW5taGWFhoq3lpef4KChq/ahoq33nJym8JOUnNqIiI7AfHx/lnFxcmZpaWk1ZmZl + EwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAmH+AzJh/ghyYf4NkmH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh7g + /yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mHuD/Jh7g/yQa3/8cE93/pKfz/2tt6/9fZOv/i5fz + /xkv6f8ePu3/HEPv/ypQ8f9MafP/c4f1/5ei9/90iPX/HkPv/yNH8f8kSvb/JEjy/yA60/8aJZ//FBZ+ + pB4axCObmfx0srOs/wAAAP8AAAD/AAAA/wAAAP8KCgrvsLCw9MnJyT8AAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAADGyfm42dr9/9ra/f/e3v3/1NX8/zg14/8iGt//Jh/g/yYf4P8mH+D/Jh7g + /yYf4P8mH+D/Jh/g/yYf4NUiG98QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABnZ2UNaGhn + I2lpZyVmZ2YZZmZlCQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh/gCSYf4EkmH+CiJh/g + 6SYe4P8mH+D/Jh/g/yUe4P8lHuD/JR3g/yUd4P8lHuD/JR7g/yUd4P8lHeD/JR3g/yQd3/8kHOD/JBzg + /yIa3/8cFN7/Ixzf/3yA7v+Lj/D/Q0Pk/7G49v8/O+P/IRfe/yYh4f8lKuT/IzPo/x077P8fRO//MFTx + /zJV8f8iRu//I0fv/yNH7/8jR/D/JEn1/yRK9f8hQ+nUH0HrbtLc/4uSkYn/AAAA/wAAAP8AAAD/AAAA + /ysrK4fHx8jFxcTFagAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAM3P+8fb2/3/1tf8 + /8LF+f+Ym/L/MC3h/yMc3/8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mHuDyJh7fMAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmH+AXJh/gYSYf4LgmH+D3Ix7f/yol4P8pJOD/KCPg + /ykk4P8oI+D/KiXh/y0o4f8sJ+D/LSfh/zAq4f8yLOL/Ozjj/1pa6P+Mk/D/kJfw/2906/+XnfL/T07m + /x4X3/8mH+D/Jh7g/yYc3/8mHN//Jh/g/yUn4/8iMef/IT3s/yNI7/8jSfD/I0jw/yNH7/8jR+//I0fv + /yNH8f8WPfD/p7r//2pnXv8AAAD/AAAA/wAAAP8AAADtNjQtFc7OzavExMSWAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAoqf0y4aH7/9TUuf/LSfh/xoT3v8kHOD/Jh/g/yYf4P8mH+D/Jh/g + /yYf4P8mH+D/Jh/g/iYf4FUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAACcj4SeFj+95b3bry2hx6v90fOz/dn/t/3uD7f+Fje//f4ju/3yI7v+Dke//g5Dv + /4eO7/+LlfD/laDy/4+Y8f97ge7/XV7o/ygi4P8fF9//Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh3f + /yYc3/8mH+D/JSjj/yU16P8jQe3/I0nw/yNJ8P8jR/D/I0bv/xg+7/+uwP//WlhQ/wAAAP8AAAD/AAAA + /wMIHoMAAAAAzMzLgsTExMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdF9/IFQ7d + /yMe3/87OOP/QT/l/yYg4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+B3AAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABDP+QBn6Lz + OICB7o1VVefaXFzp/1xb6P9aWej/UlTn/1NX5/9QVef/TE/m/0tI5f9APuT/My3i/yIb3/8eFt//JR3g + /yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mHd//Jhzf/yYg4P8lKuT/JDnq + /yNF7v8jSfD/Fz/w/7LF//+hn5b/AAAA/wAAAP8IDB//IUPf1B9F/WuFk9Vu0M2/2QAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFpa6KagovT/urz4/8/Q+/+7v/j/Linh/yMc3/8mHuD/Jh/g + /yYf4P8mH+D/Jh/gkgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABUN3QkfGN9IHxffnR8X3+YgGN//IBff + /yAW3/8gF9//Hxff/yEZ3/8kHN//Jh7g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g + /yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh7g/yYc3/8mHd//JiPh/yUw5v8XNuv/qLn+/+/u5v8QEA//AAAA + /xw0ov8lS///H0Pw/yZK7fSjrdjwh5vlLgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoK/0 + Wd7d/f/X1/z/3Nz9/9LV+/86OOP/IRnf/yYe4P8mH+D/Jh/g/yYf4KgmH+ACAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAJh/gFCYe4FgmHuCvJh/g8CYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g + /yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g + /yYf4P8mHeD/Jhzf/xkT3v+gpfP//////05NRP8DEkT/JUz7/yNH8P8jR+//Ejjv/4uf+f+bsPrtc432 + jCBE7yAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABmcOoMtLf33sbI+f+dnvP/YmHp/ygi4P8lHeD/Jh/g + /yYf4P8mH+CyJh/gBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAmHuAfJh/gaCYf4LomHt/2Jh7f/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g + /yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/GRLe/6Om8v//////m5ul + /xYiwv8kQ/X/I0nw/yNJ8P8VO+//jJ/3/5mu+f90jvb/HkLv7SNH8IcjR/AYAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAyLuFwMzDi/x8Y3/8cFN7/JR7g/yYf4P8mHuD/Jh7grCYe4AkAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYe4CUmH+ByJh7g + wiYf4PgmH+D/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g + /yYf4P8mHt//Jh/g/yYf4P8aEt7/dnfs//////+/w/r/HBTh/yUg3/8lLuX/JD/s/xE47v+muPn/lKv4 + /2qH9f8cQO//I0fv/yNH8OUjR+9wI0fvCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACMb3wUiGt+DJh7g4SYf4PomH+D7Jh/g + 6iYf4IYmH+ADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh7gKSYf4GsmHt+vJh7g6CYf4P8mH+D/Jh/g + /yYf4P8mH+D/Jh/g/yYe4P8mHuD/Jh/g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yMc3/8sJ+H/ur31 + /2ho6v8dFd7/Jh7f/yUc3/8WDt3/Ym3s/+/2//95kvb/a4j2/xo/7/8jRu//I0fv/yNH7/8jR/DKI0fv + PwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAmH+AQJh/gKSYf4ComH+AYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAmH+AWJh/gUiYe4J8mH+DiJh/g/yYf4P8mH+D/Jh7g/yYe4P8mH9//Jh/g + /yYf4P8mHuD/Jh/g/yYf4P8mH+D/Jh/g/yMc4P8gGd//Ihrf/yUd3/8dFN7/Fg/e/2Zn6f/19f3/jpPw + /6Ks9P9ccfD/Gz/u/yNJ8f8jR/D/I0fw/yNH7/8jRu/7I0fvgyNH7wUAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + ACYe4BAmH+BKJh/gmCYf4N0mH+D/Jh/g/yQb4P8eFt//Hxff/x4X3/8eF9//Hxff/x8X3/8eFt//HRTf + /xoS3v8cFN7/KSTg/1VV5/+kqPL/0tf5/4+V8P+jqfT/nqLy/yYg3/8kI+H/JDfp/yNG7/8jSvD/I0fw + /yNH7/8jR+//I0fvvyNH7ysAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh/gCyYf4EQkHOCTMS3i + 2lBL5v9PSub/VVDn/1ZR5/9UT+f/U0/m/1VV5/9iZen/d3ns/5mh8f/Cy/f/2+X7/8HN+P+gqPP/oafz + /3+D7v8oJOD/Ihrf/yYd3/8mHN//JiTi/yU26P8jRe//I0nw/yNH7/8jR+//I0fv7iNH8GUAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABveesK19/6QZuo8o61uvbYoqjz/5+m8v+fpvL/nqby + /56n8v+Sm/H/iZTv/3+F7v9qbev/YGLp/01L5v8qJeD/HBTe/yQc4P8mH+D/Jh/g/yYe4P8mHd//Jhzf + /yYk4v8lNej/I0Xu/yNK8P8jSPD/I0fv/yNH76gjRu8bAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAFJQ5wcYEN48IRrfiSEa39UhGt//IRrf/yIb3/8gGd//Hhbf/x0U3/8dFd//IBff + /yQd4P8mHuD/Jh7g/yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh3g/yYc3/8mI+H/JTXo/yNF7v8jSfD/I0jw + /yNH7+AjR+9NAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJR7g + BSUe4DslHuCHJh7g0SYe4P8mHt//Jh7f/yYf4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g + /yYf4P8mH+D/Jh7g/yYd3/8mHN//JiTi/yQ36f8jRu//I0nw/yNH7/4jRu9qAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmHuADJh7gNiYf4IQmH+DNJh/g + /iYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh/g/yYe4P8mH+D/Jh7g/yYf4P8mH+D/Jh/g/yYf4P8mHd//Jhzf + /yYl4v8kO+r/I0jw/yNH8MgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYf4AEmH+AyJh/gfSYe38gmH+D8Jh7f/yYe4P8mH+D/Jh/g + /yYe4P8mH+D/Jh/g/yYf4P8mH+D/Jh/g/yYf4P8mHuD/Jh3f/yYc3/8lMuf/I0jwiAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAACYe3y0mH+B0Jh/gvyYf4PMmH+D/Jh/g/yYe4P8mHuD/Jh/g/yYe3/8mH+D/Jh/g + /yYf4P8mH+D/Jh3f/yUn45sjSfAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh7g + ISYe4FYmHuCSJh/gxiYe4OwmH+D/Jh/g/yYf4P8mH+DkJh/gvCYf4I4mHN9HAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACYe4A8mH+A1JR7fZyYf4JUmH+CfJh/g + liYf4FomH+AsJh7gDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAD///////////////////////////////////////////////////////////////////////// + ////////////////////////////////9////////////////////+P////////////////////j//// + ////////////////4////////////////////+P////////////////////j//////////////////// + 4////////////////////+P////////////////8Af/j/////////////////AH/4/////////////// + /+AAf+P////////////////gAH/j///////////////+AAAf4////////////////gAAH+P///////// + /4B///AAAAPj//////////8AP//wAAAD4//////////8AB//4AAAA+P/////////+AAP/+AAAAPj//// + //////AABwOAAAAD4//////////gAAcDgAAAA+P/////////wAAAAAAAAAPj/////////wAAAAAAAAAD + 4/////////AAAAAAAAAAA+P///////+AAAAAAAAAAAPj///////8AAAAAAAAAAAD4///////8AAAAAAA + AAAAH+P//////8AAAAAAAAAAAB/j///////AAAAAAAAAAAB/4///////wAAAAAAAAAAAf+P//////+AA + AAAAAAAAAf/j///////wAAAAAAAAAAP/4///////8AAAAAAAAAAH/+P///////gAAAAAAAAAB//j//// + ///4AAAAAAAAAD//4////////AAAAAAAAAAf/+P///////wAAAAAAAAAD//j///////+AAAAAAAAAAf/ + 4////////gAAAAAAAAAD/+P/////4P8AAAAAAAAAA//j/////wAAgAAAAAAAAAD/4/////wAAAAAAAAA + AAAAf+P////8AAAAAAAAAAAAAD/j/////AAAAAAAAAAAAAAn4/////8AAAAAAAAAAAAAJ+P////nwAAA + AAAAAAAAAAfj////gfgAAAAAAAAAAAAH4////wD+AAAAAAAAAAAAB+P///4Af4AAAAAAAAAAAAfj///8 + AH/gAAAAAAAAAAAH4///+AA//AAAAAAAAAAAB+P///gAP/AAAAAAAAAAAAfj///wAD8AAAAAAAAAAAAH + 4///4AAgAAAAAAAAAAAAB+P//+AAAAAAAAAAAAAAAAfj///gAAAAAAAAAAAAAAAP4///wAAAAAAAAAAA + AAAAD+P//8AAAAAAAAAAAAAAAA/j///AAAAAAAAAAAAAAAAf4///wAAAAAAAAAAAAAAAH+P//8AAAAAA + AAAAAAAAAB/j///AAAAAAAAAAAAAAAAf4///gAAAAAAAAAAAAAAAD+P//wAAAAAAAAAAAAAAAA/j//4A + AAAAAAAAAAAAAAAP4//4AAAAAAAAAAAAAAAAB///8AAAAAAAAAAAAAAAAAf//+AAAAAAAAAAAAAAAAAH + ///AAAAAAAAAAAAAAAAAB//4AAAAAAAAAAAAAAAAAAf/gAAAAAAAAAAAAAAAAAAH/4AAAAAAAAAAAAAA + AAAAB/+AAAAAAAAAAAAAAAAAAAP/wAAAAAAAAAAAAAAAAAAA//wAAAAAAAAAAAAAAAAAAH//gAAAAAAA + AAAAAAAAAAB//wAAAAAAAAAAAAAAAAAAf/4AAAAAAAAAAAAAAAAAEP/+AAAAAAAAAAAAAAAAAB///gAA + AAAAAAAAAAAAAAA///4AAAAAAAAAAAAAAAAAP//+AAAAAAAAAAAAAAAAAH///gAAAAAAAAAAAAAAAAD/ + //4AAAAAAAAAAAAAAAAB///+AAAAAAAAAAAAAAAAAf///gAAAAAD/AAAAAAAAAH///4AAfAAf/+AAAAA + AAAB///+AAH+D///4AAAAAAAAf///gAD//////wAAAAAAAH///4AB///////gAAAAAAJ///+AA////// + /+AAAAAAAf///gAf///////8AAAAAAD///4AH////////4AAAAAAP//+AD/////////wAAAAAA///wB/ + /////////gAAAAAD//8A///////////AAAAAAf//w///////////+AAAAAB///////////////8AAAAA + P///////////////4AAAAB////////////////wAAAAH////////////////gAAAA/////////////// + //AAAAH////////////////+AAAB/////////////////8AAAf/////////////////8AAH///////// + /////////4AH///////////////////gD/////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + /////////////////ygAAABgAAAAwAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO7u7g7u7u5D7u7uBgAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AO7u7h/u7u6V7u7uDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO7u7h/u7u6X7u7u + DQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5eXl + aNna23zJy818wsbIfN3e3nzq6up87OzsIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAOLj5A3c3d4k2tvd3MTHyv6nrrP+nKSq/srOz/7i4+P+6enpXezs7BwAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO/v7wHt7e0C4uPkAtzd3lLS1dbaucDG+Zemsf9vjqX/YYai + /52qtf/Ex8n/4+Tk5Ovr66zu7u4C7u7uAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO7u7h/u7u6X7u7u + DQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAABwYoCAYFJhYGBSQXBQQfEgQEGwQAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO/v7zXt7e1+4OHi + fs/T1a6xuL7/iJyr/16Cnf82c6L/J2+m/2OEnf+Wo6z/0NPV/+Lj4+Tu7u5+7u7uKAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0MTwEFBSJWAAACvwAAAOsAAADuAAAA + 4gAABbIGBhhSEhIrBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AO/v7wHv7+8Z7+/vJezt7YHp6en/wMTH/52ps/9xjqX/RHqk/xdrqv8KZ63/Cmes/yhwpv9RgaX/kqCr + /7vAw//n5+f+6urqau7u7iXu7u4W7u7uAQAAAAAAAAAAAAAAAO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHBtf + FwMDCrAHByT+Dw5O/xEQV/8NDEj+CAgp/wICB/8AAAD+AgICtCIiLBIAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO7u7gnu7u6V7Ozs397f4O3Hys3/k6Wz/22Np/9BeKT/H2yo + /wRlrv8AZLD/BWaw/x9ysf8/fq7/cJCp/5iptv/JzM3/4uPj6e3t7d7u7u6C7u7uBgAAAAAAAAAAAAAA + AO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAABAQG0ZDAwlxRoXkv8jHeD+JB3o/ygi4/81Mdf+PTzO/zEvsv8QEUb+AAAC + /gsLD5kAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6+vrA+np6RTs7O2v5eXl + /8PHyv+OmaP/WIKi/zRzpf8OaKz/AWSw/wBksP8CZbH/C2u1/yx/wP9BicH/VY65/3CUr/+Zoaf/0NPV + /+np6f7t7e2V7OzsBwAAAAAAAAAAAAAAAO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFyjAkRETC/HxrC/iol2/5QTsb+i4vJ + /sHB1v7n5+f+7+/t/ubm5/6ytMz+OTpD/gEBAfkhKE0hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAA6+vrOebm5rXd3t7qpKqu/3qUqP9BeaT/H22p/w1nrf8CZK//AGSx/wNmsv8Zc7n/NYbE + /2ak0/+As9r/iLbZ/3+qzP9ymbf/nay4/7u+wv7l5ueV5+jpBwAAAAAAAAAAAAAAAO7u7h/u7u6X7u7u + DQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAi46h + EDAwNKhoaqz+qarL/+Li4f/9/fv+/v7+//X2+v/d4vT+w8zw/7TC+f+tuvn+sL7v/1xgbP4MECN3AAAA + AO3t7TTr6+tR5ubmUeTk5Ezl5eUVAAAAAOTk5AXi4uI54ODghtLU1vS9w8f/bYie/0l7of8ba6j/CGWt + /wBksP8AZLH/B2iz/xNvt/82hsP/XJ3Q/4i53v+dxuX/oMfl/4i01v9omL3/jZ+u/6uwtP7j5OWV5ufo + BwAAAAAAAAAAAAAAAO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAC6vtACrbHFFpyfrEyhoqWbxsbH3Onp6f36+vr+7/L7/8nT+P+TpvT+YHvv/zxc7P8pS+3+I0bu + /yNH7v8rTe7+LU/t/zhX5v44RoiiqamoEePj4qTl5eX64eHg+t3d3enFxcRMm5uaEcLCwh/c3Ny0zc/Q + +6CpsP96jZz/IWym/xJoqv8CZK//AGSv/wBksf8FZ7L/GXO5/ziHxf9rptX/lcDi/6fL5/+t0Or/qMzo + /4+21f9wmbn/lKSx/7G1uf7k5OWV5+foBwAAAAAAAAAAAAAAAO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAnqK0FJKUnlqvsbiqz8/P5fDw8P7+/v7+8PHy/rvD5f5wh+j+Olrt + /iJG7v4hRO/+Ikbv/iJG7/4iRu/+Ikbv/jdX7v6aqOj/pq/a/5ihzP+RmcL6nZ+oyquqqeeqqaf/p6aj + /6akovqbmpjPmZmYvqSkpMKqqqvsmqCl/3CJnf9OeZv/C2ar/wRkrf8AZLD/AWSx/whps/8cdbv/P4vH + /2yo1f+RvuD/q83p/7HS6/+nyub/i7XX/4WnxP+Ina7/uMDG/9LU1f7p6uqV6uvrBwAAAAAAAAAAAAAA + AO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACpr8wBmp+3H5ibqGGpqaq42NjY9fr6+v/6+vv+2t/x + /6Kv5/9edd/+K0zm/yBE7/8iRu/+I0bv/yNG7/8iRu/+I0bv/y5P7v9ed+3+ZXzo/26B3v+9wdL/wL+9 + /7Oyr/+rqqb/n52Y/5mWkP+UkIn/k4+I/5SQiP+RjYj/k5CM/5qYlf+Qj47/fIGG/1l1i/8+bpH/FWem + /wxmq/8CZbD/C2q1/xx1uv9Cjcj/bafW/5vD5P+u0Or/t9Xt/6vN6f+Wvdz/eqHC/4miuP+rsLT/2drb + /+zs7P7t7e2V7e7uBwAAAAAAAAAAAAAAAO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAmqLIApKYs0errLGz19fX + 7PX19f7x9Pv+y9P1/4ue7P9LZ+n+Kkzr/yJF7v8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+K0zu + /09q7P/GzOb/0tPY/7W2uv+sqqn/op6X/7Stov+9tqn/xbyt/8nBsf/Pxbb/0ci4/9LIuP/KwbL/xLut + /7uzp/+qpJr/ko+J/3Z6fP9hbnf/THGN/zFsm/8MaK//I3m8/0GNx/96sNn/nMXj/7PS6/+61+7/tNPs + /4m32v99psb/jJ+t/622vf/a29z+5ubnmu3t7Wvu7u4/7u7uAwAAAAAAAAAAAAAAAO7u7h/u7u6X7u7u + DQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAhoyrLNra2/f3+Pz+vcn3/neO8f49Xe7+I0fu/iFF7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv + /iJG7/4iRu/+Ikbv/iJG7v4lSO7+cYbq/ra94P/Fxcv/u7q4/7Csp/+wq6P/uLCj/8i/r//SyLf/2c69 + /97Twf/j2Mb/5drI/+XayP/g1cP/29C//9TJuP/Jv7D/uLCk/6Gbkv+Oi4X/Z3N8/1Fvhv82bZf/PIG3 + /1SYzP+VweH/tNTr/8Da7/+t0Or/l8Dg/4Gmw/+KpLj/sLe9/8vOz/bq6urV6urqRAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgo7HB5qdqag/YPL+H0Pv/yJG7/8iRu/+I0bv + /yNH7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+Jkjv/1937v9uhO3+prDg/8jIyf+urKr/o56Z + /7auo//Hva//39TB/+PYxf/l2sj/5tvJ/+bbyf/m28n/5tvJ/+bbyf/m28n/5dvJ/+XayP/k2cj/4NXE + /9DFtf++taf/gX56/3V3d/9ncXn/VYOn/2CZxv+dxeP/uNft/7zZ7/+Vvt//eKfL/42gr/+rsbX/2drb + /+bm5s3s7OwS7OzsBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AGt/2AglSO/PI0bv/yNG7/8iRu/+I0bv/yNH7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+Kkzu + /8DI7f/j5Oj/ycnL/62sq/+0rqb/wLeq/9TKuv/d0sH/5drH/+XayP/m28n/59zK/+fcyv/n3Mr/59zK + /+fcyv/n3Mr/59zK/+fcyv/n3Mr/5drI/+DVxP/az77/wLiq/6Cbkv9xc3L/Z3qJ/2yLo/+GstT/jr7j + /4u84P+GqcP/jJ+v/7W8wv/R0tP/4uPjjunp6VAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAyU+tZIkbv/CJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv + /iJG7/4iRu/+Ikbv/iJG7/4iRu/+KUzu/sHI6f/Z2dn/vby6/6ekoP+/uKz/1cq5/+LXxv/n3Mr/59zK + /+fcyv/n3Mr/59zK/+fcyv/n3Mr/59zK/+fcyv/n3Mr/59zK/+fcyv/n3Mr/59zK/+bbyf/l2sj/3tTC + /721qP+MiYP/dXp9/21+i/9zn8H/d6nQ/3qnyf+Pp7j/qK6z/9DS0/Pj4+PV5+fnOAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnSesII0bv + xyNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8+Xe7+coft/8fK4P+7u7v/s7Cr + /7avpf/Sybr/5drI/+bbyf/n3Mr/59zK/+fcyv/n3Mr/59zK/+fcyv/n3Mr/59zK/+fcyv/n3Mr/59zK + /+fcyv/n3Mr/59zK/+bbyf/m28n/5tvJ/9jOvv/BuKv/h4SA/2lyef9ghaP/bJCt/4WXpP+vt7v/09TU + /+Tk5Lzm5uYQ6OjoAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO7u7h/u7u6X7u7u + DQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAI0bvTyNG7/siRu/+I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+I0bv + /yNG7/9rgu3+5OTk/9HR0P+sq6j/uLOr/8/Gt//d08L/59zK/+fcyv/n3Mr/59zK/+fcyv/n3Mr/59zK + /+fcyv/n3Mr/59zK/+fcyv/n3Mr/59zK/+fcyv/n3Mr/59zK/+fcyv/n3Mr/59zK/+LXxv/Yzr7/opyT + /4KCgP9ueoP/gY2Y/6ius/6Xmp7Utri8cObn50Xq6+sBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIkXuByJG77wiRu/+Ikbv + /iJG7/4iRu/+JUju/kZk7v5MaO7+TGju/kxo7v6Fl+z+3Nzc/8nIxv+ppqH/vrit/97Twf/j2Mb/5drI + /+XayP/l2sj/5drI/+XayP/l2sj/5drI/+XayP/l2sj/5drI/+XayP/m28n/5tvJ/+bbyf/m28n/5tvJ + /+bbyf/n3Mr/59zK/+fcyv/k2cf/tq+j/5eSi/94d3X/j5CQ/8DBw/9dXl7+AQEC2AUFHzwAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAACNC7TMjRu75I0Xu/yNG7/8iRu/+Kkzu/4KV7v+QoO7/kKDu/5Cg7v+tuOn/0tLS + /8PBv/+1sKj/ysK1/+PYxv/k2cf/5drI/+XayP/l2sj/5drI/+XayP/l2sj/5drI/+XayP/l2sj/5drI + /+XayP/m28n/5tvJ/+bbyf/m28n/5tvJ/+bbyf/n3Mr/59zK/+fcyv/l2sj/z8a4/7StpP9+fXr/mJiY + /9nZ3f9zc6X/AgMJ/wAAAPYFByJQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + ADI75gEzPecLMjznBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjQOyGJSjj/CM86/8iRu/+I0bv + /yNG7/8jRu/+I0bv/yNG7/9mfej+zMzM/7+9uv++t63/0Me4/+TZx//m28n/5tvJ/+bbyf/m28n/5tvJ + /+bbyf/m28n/5tvJ/+bbyf/l2sj/5drI/+bbyf/m28n/5tvJ/+bbyf/m28n/5tvJ/+bbyf/m28n/5tvJ + /+bbyf/l2sj/2c/A/8C4rf+CgH3/lJSU/87O0f92dMb/Ghpu/w0RRP4IDi61FySXBAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAnMuYZJjrpaiYw5q8jK+TVJi/lyyYu5bAnMOWQJjXncSUw5kwkMucsIzfp + DiQx5gEoNecGJyXhliUf4P8kNOj+HzfM/yJC5f8jR/H+I0bv/yNG7/9lfOf+x8bG/7y5tv/FvrH/1cy8 + /+Xayf/m28n/5tvJ/+bbyf/m28n/5tvJ/+bbyf/m28n/5tvJ/+bbyf/l2sj/5drI/+XayP/m28n/5tvJ + /+bbyf/m28n/5tvJ/+bbyf/m28n/5tvJ/+bbyf/m28r/4dfG/8rBtf+HhYH/kZGR/8DAxP9xbdb/Jh/g + /yUk5P4jRevxIULlmh42yxcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO7u7h/u7u6X7u7u + DQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJC3kEiQ26I0kKOP2JR7f/jw85P5kbOz+Z27t + /mBn7P4nIN/+JR3f/iUe3/4lH9/+JSTh+iUq5OIkMubCJDTooiQp5O0eIr3+HCCn/hYXg/4ZIpz+HzfL + /iJF6/5lfOb+wcHB/7i1sv/NxLb/3NLC/+jezv/m3Mv/5tvJ/+bbyf/m28n/5tvJ/+bbyf/m28n/5tvJ + /+bbyf/l2sj/5drI/+XayP/m28n/5tvJ/+bbyf/m28n/5tvJ/+bbyf/m28n/5tvJ/+fcy//o387/597O + /9LKvf+Rjor/kZCQ/7Oztv9pZc/+Jh/e/iUe3/4kOen+Ikbv/iJG794qSuYjAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHSW5 + GSUf27olHt/4JR7f/ycg4P4nIOD/JR3f/yYf4P4mHt//Jh7f/yYe4P8lHt/+Jh7g/yUe3/8lHt/+JyDg + /1xj6/9oce/+UFjp/zlB4P8oMM/+Hye7/xwos/9gar/+wMDA/7e1sf/Pxrj/39fJ/+3l2v/s5Nf/6uDS + /+jezf/m3Mv/5tvJ/+bbyf/m28n/5tvJ/+bbyf/l2sj/5drI/+XayP/m28n/5tvJ/+bbyf/m28n/5tvJ + /+bbyf/n3Mr/6uDR/+vj1v/t5tv/6uHT/9TMv/+TkIz/kZGQ/6+vsv9oZM3/Jx/e/yUe3/4kLuX/I0fv + /yJG7/4jR+/AOFbkCwAAAABledQBAAAAAAAAAAAAAAAAAAAAAO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkanwQiHshEJR7eryUe3/kmHt//Jh7g/yUe3/4lHt//Jh7g + /yYe3/8lHt/+Jh7g/yYe4P8lHt/+KiXg/4mV9P+Voff+laH3/5Sg9/+LlvT+eoPx/2Nr7v98guL+xMTE + /7q4tP/NxLf/4NrP//Lv6f/z7uj/8evj/+3l2f/q4dP/593N/+fdzP/n3cz/5tzL/+bcyv/m28r/5drJ + /+bbyv/m3Mr/5tzK/+bcy//n3Mv/59zM/+jezv/q4NH/8Org//Ht5f/08Or/6+PX/9PKvf+Sj4v/k5OR + /7W0uP9rZ9D/Jh/e/yUe3/4lKeP/I0bv/yJG7/4jR+/9KkvlWYmY3yuTncc7AAAAAAAAAAAAAAAAAAAA + AO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAgzCQUGIi0JCjsnDg5ZAwAAAAAAAAAAAAAA + ACUg0iYlHt6WJR7f6SUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iwo4f5JSuf+X2Xr + /nB47/6AivL+jJj1/pSg9/6vt+z+ysrK/8C9u//Jwbb/3dbN//Lt5//18ev/9fHs//Tv6P/y7eX/7+rh + /+7o3f/t5tr/6+PW/+rh0//p39D/6N7P/+nez//q4NH/6+HT/+vj1f/s5Nf/7ebZ//Dp3//y7OT/9PHp + //Tx6//08Or/5t/S/8zEuP+Ni4f/lpaV/8LCxv9xbtf+Jh/e/iUe3/4kKuT+Ikbv/iJG7/4iRu/+Um7x + uLCyvM46QWE9AAAAAAAAAAAAAAAAAAAAAO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJCTk7CAgq + ygQFF/UAAADyAQELrg4LVBwAAAAAAAAAAAAAAAAjHNIBJR7eHiUe33klHt/cJR7f/CUe3/8lHt/+JR7f + /yUe4P8lHt/+JR7f/yUe3/8lHt/+JR3f/yUe3/8nIOD+LSjh/z085P+Ljub+0dHR/8XDwf/EvrT/2dPK + //Hr5P/18uz/+PXx//j18f/39PD/9fLu//Pw6f/y7ub/8Ovi/+/p4P/u5tz/7ubb/+7m2//v6N3/8Onf + //Hq4f/y6+P/8+3m//Xx6//28+7/9/Tv//Xy7f/y7uf/4NjN/8S9sv+LiYf/m5ub/8/P0/94dN3/Jh/e + /yUe3/4kMuf/I0bv/yJG7/4wU/D/rrfb8h4fH/gtM0wuAAAAAAAAAAAAAAAAAAAAAO7u7h/u7u6X7u7u + DQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAA0MSDYNDkrtFRN8/xUUe/4HCCD/AAAA/wEBCM4MCk4NAAAAAAAAAAAAAAAAAAAA + AAAAAAAlHt8QJR/gXyUe38klHt/+Jh7f/yYe4P8lHt/+Jh7g/yYe3/8lHt/+Jh7g/yYe3/8lHt/+JR7g + /yYg4P+GiOb+2NjY/8vKyP+/u7P/1M7F/+7o3v/08Or/9/Tx//j18v/49fL/+PXy//j18v/49fH/+PXx + //j08f/39PD/9/Tw//f08P/49PH/+PXx//j18f/49fH/+PTy//j08v/59fL/+fXy//bx6//w6uD/1c7D + /7q0q/+OjIr/paWl/93d4P9+euP/Jh/f/yUi4f4jRO7/I0bv/yJG7/6ywff+WFhZ8wAAAP4nKz0iAAAA + AAAAAAAAAAAAAAAAAO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFhVqHQ8PU90VE4D+Hxm//iQd6f4kHuL+ExJk + /gAAAv4CAg+KAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQo4wElJeJAJSDgqyUe3/IlHt/+Jh7f + /iUd3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iQd3/6Eheb+39/f/9HQz/+6uLP/zMa9/+Xd0f/w6+P/9/Tx + //j18//59vP/+fbz//n28//59vP/+fbz//n28//59vP/+PXz//n29P/59vT/+fb0//n29P/59vT/+fb0 + //n29P/59fP/+PXy//Pu5v/q4dT/xL20/6unov+XlpX/oJ+x/46L3/9UT+L+JSHg/iM/7P4iRu/+Ikbv + /o6j9/61tbb+BQUF/gICAvouMDkTAAAAAAAAAAAAAAAAAAAAAO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcHHMGFxhQ + riYnk/4hG8//JR3n/yQd5/4lHef/JB3k/w8OT/4AAADUDhFaCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAjM+cCJDLnEiQt5UklKePSOTjd/6Wlyf9RU9j+JR7g/yYe4P8lHt/+JB3f/3Bx0/+0tdP+zs7k + /9XV1/+8u7n/xsG5/9nRxP/p5Nr/9fHr//j18//59/X/+ff1//n39f/69/X/+vf1//r39f/59/X/+fb0 + //n39f/69/X/+vf1//r39f/69/X/+vf1//n39f/59vT/9fHs/+3n3v/f1sf/s62m/6Cdm/+npqb/mpm0 + /zUwv/4lIt//JD3s/yNG7/4jRu//PV3x//H0/f5DQ0T/AAAA/yAgIfFOT1IFAAAAAAAAAAAAAAAAAAAA + AO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAPED5hSUyn+yIczv4lHej/JR3o/yQd5/4lHef/JR3n/yAbvv4AAADmExt9 + EwAAAAAAAAAAAAAAAAAAAAAjQ+0VI0PtSCND7oEjRe60I0bv3CNH7/ciRu/+LEvr/7e6x/9mZtT+JR3g + /yYe4P8lHt/+JB7f/2Vl1P/Excb+Wlfe/8LB4f/MzMz/xcK+/8XAt//c1sr/7une//f18f/5+Pb/+fj2 + //n49v/6+Pb/+vj2//r49v/5+Pb/+fj2//n49v/6+ff/+vn3//r59//6+ff/+vn3//n49v/49vP/7+bc + /9/Xzf/HwLb/oJ2Z/5uamv/Hx8f/t7rT/y0wkv4YGpD/Gyir/x84z/4iRu7/cov1/9TU1P4CAgL/AAAA + /1hYWO4zMzQCAAAAAAAAAAAAAAAAAAAAAO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsaYgszNm7kNDHB/iQc4v4YFZD+Ghac + /iQd5f4kHef+JB3n/iUe5/4EBRPmGCmjGBw1vyUgQd9dIkbumCJG79IiRu/7Ikbv/iJG7/4jRu/+Ikbv + /iJG7/4iRu/+JEfu/p+ozf6wtcn+Mkfn/nx/5/6Khub/iobm/0RB2/7Ozsr+f37b/sLB5//e3t7/zc3L + /8C+uf/Qy8H/4NnN/+7o4P/08Or/+Pb0//n49v/6+ff/+vn3//r59//6+ff/+vn3//r5+P/6+fj/+vn4 + //r59//6+Pb/+ff0//bz7f/y7eb/5NrN/8zEuv+qp6H/p6ak/7Gxs/+eqdv/b4Xq/i5Q7/4hPtz+HCy0 + /hcckP4TFob+pK3g/oCBgf4AAAD+AAAA/o6OjuQAAAAAAAAAAAAAAAAAAAAAAAAAAO7u7h/u7u6X7u7u + DQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AA8QQFo/QZ3/IRvJ/xwYrv4ZGX3/FhSE/yQd4v4kHef/JB3n/yUe6/4XJn7yIkTn0SJG7/EjRu/+I0bv + /yJG7/4jRu//I0bv/yNH7/8jRu/+I0bv/yNG7/8iRu/+Ikbv/4WU1P+wt8n+bYPe/9LX7v/r6+3/6+vt + /zo24f/JytP+sbLc/8bE6v/s7Oz/2dnY/8TDwv/Gw7z/z8m+/+HZzP/q49r/9PDs//j18v/59/X/+vj2 + //r59//6+fj/+vn3//r5+P/6+ff/+vn3//n49f/49vL/9fHt/+3o3//m39P/0ci7/7izq/+Xl5b/uLi3 + /8vMz/9whOb/JUfu/yJG7/4jRu//I0fw/yJG7f4bM8n/kJXB/y0uOv4AAAD/BQUF/8LCw9EAAAAAAAAA + AAAAAAAAAAAAAAAAAO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAGhpdAgoLO600NZj/IBrM/xcUi/4VFHr/Gxek/yQd6P4kHef/JCTo + /yM97f4jR/H/I0bv/yJG7/4jRu//I0bv/yJG7/4jRu//I0bv/yNG7/8iRu/+I0bv/yNG7/8iRu/+Ikbv + /2Z83f+OndL+rLPN/9Xa7v/u7u7/7u7u/zVR7f+yuNn+w8Xa/5yd6f+5uer/zc3k/9nZ2f/JyMf/wb+7 + /8jCu//TzML/5dzP/+3n3f/y7eT/9PLs//f18f/5+Pb/+fj2//n49f/49fL/9vPu//Pv5//v6d7/597R + /9bPw//Iwbj/pKKe/6empP+xsbH/x8nT/7O85v9fd+z/I0bv/yJG7/4jR+//I0Ht/yQ46f4kNOv/GB5Y + /wAAAP4AAAD/FhYX/+np6qgAAAAAAAAAAAAAAAAAAAAAAAAAAO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBVTEAsLQuAlJ4r+HxnE + /h0Ysv4ZFZb+Ixzd/iQh5/4jNev+I0bv/iNG7/4jRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu7+Ikbv + /iJF7/4jRu/+Ikbv/ilN7/4lSO/+KEvv/lx35/6Yo8/+kZ/U/tzf7P/u7u7/7u7u/01p8P6VpOL+z9DW + /i9S7P4lSe7+jp/s/+Tk5v/X19f/zMzL/8jEwf/JxL7/zsi8/9rTyf/g2tD/5eDX/+nk3P/t6OH/7+ni + /+7q4f/q5d3/5+HZ/+Db0v/Y0sf/zMa6/7+6sv+2s6z/r6+u/7u7u//Ozs7/tb3k/zVV7f4iRu/+Ikbv + /iNF7v4kLOT+JR3f/iUe3/4mHuH+ExJo/gAAAP4AAAD+OTk5+5+fplAAAAAAAAAAAAAAAAAAAAAAAAAA + AO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAACw03KA0NS/cWFXv/HBes/yQe5v4kJdn/JDXV/yND6/4jRu//I0bv/yJG7/4jRu//I0bv + /yJG7/4jRu//I0bv/yJG7/4jRu//LlHw/1Jz8/8kSO/+IUXv/3CL9f8lSO/+l6v4/+Tp+f+yt8n+U27l + /+Xm6f/u7u7/7u7u/z9e7/9+kuj+09TV/0hl5f8oS+/+O1vu/2iA7f/U2Or/5OTk/9XU1P/JyMf/urm1 + /8XBuv/Lxbz/0Mm//9TMwP/Y0ML/29HD/9vSxP/VzcH/0Mm+/8jCuf+9ubL/sbCr/6+uq/+xsbD/1tbW + /9nZ3/+Imev/RWLt/yZJ7v4jRu//I0Xv/yQs5P4lHt//JR7f/yUe4P4lHuD/IBy2/wEBBP4AAAD/f3+B + w1NSbwgAAAAAAAAAAAAAAAAAAAAAAAAAAO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACxFBOwwMQ/4UEnr/Hymw/yM+3v4jRuv/I0bw + /yJG7/4jRu//I0bv/yJG7/4jRu//I0bv/yJG7/4jR+//I0bv/yJG7/4jRu//Ikbv/y1Q8P9PcPP+IkXv + /2WD9P8iRu/+aYT0/zZY8f+yt8n+SGXo/+Hj6f/t7e3/7O3u/zxc7/9thO3+zdDX/2l+3P9aefT+N1rx + /1p58v+8x/D/v8fs/6+45f/T1dz/0tLR/8/Oy//Ny8f/yMbB/8XCvf/Bvrj/v7u1/766tP+/vLf/wb66 + /8PBvf/EwsD/xsXE/87Ozf/V1dX/ravi/6ew6f9Ycu7/I0bv/yJG7/4jRu//JC7l/yUe3/4lHt//JR7d + /xsXqv4qKKD/JiaL/wAABf4BAQb/Q0GlmQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO7u7h/u7u6X7u7u + DQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADRhX + SRAZWP4bK7L+Ikbv/iJG7v4iRu7+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4jRu/+Ikbv/iJG7/4iRu/+Ikbv + /iJG7/4iRu/+Ikbv/iJH7/42WfH+XXz0/ll38/4kSO/+YH30/h1C7/6Sn9D+YHjf/oyd6f7Jzdz+p7bw + /kVk8P5CYO7+qrPV/o+c0f5KavP+TG3y/iJG7v4hRO7+Smnx/n6U8v7T2Ov/5OTl/9vb3f/U1df/zc3P + /8fHyf/AwML/vLy+/7u7vf/AwcL/xcbH/8vLzf/Q0dL/19fZ/+Li5P/p6ev/MjLi/iZJ7f4jR+7+Ikbv + /iND7f4lKeP+JR7f/iUe3/4mHuD+Ghae/mBiov7p6en+5+fo/pCTrP4fH4f+Jh/g7kZGsCEAAAAAAAAA + AAAAAAAAAAAAAAAAAO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAdOcYdIEHbriJF6v4jRu//I0bv/yJG7v4jRu//I0bv/yJG7/4jRu//I0bv + /yJG7/4jRu//I0bv/yJG7/4jRu//I0bv/yNE7v4jPOv/JDTo/yQt5f8lKuP+pa7z/2Bo6/8iJeL+nafz + /42e9P+Xotr+h5LU/6617f/j5Of/z9Tq/1dy8P9Yce7+mqff/7O4yP80V/H+SGfy/yJG7/8iRu/+IETv + /0pq8f+OpvT+aIHw/3yT8P5KZuv/lKnv/1Nu6f5qhOv/n7Du/1Ju6P5bd+n/j6Lu/0hk6v5FT7j/TEfX + /09J4P5QSuL/JjHm/yJH7/4jRu//JDrq/yUj4f4lHt//Jh7f/yUe3/4iHMz/NjeP/9/f5P7p6en/6enp + /+rq6v6vsdD/KzjW/j1E0mEAAAAAAAAAAAAAAAAAAAAAAAAAAO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACFD4z8iRu/fI0bv/yJG7/4jRu//I0bv + /yJG7/4jRu//I0bv/yJG7v4jRu//I0bv/yJG7/4jRu//Ikbv/yJF8P4jOuv/JSvl/yUh4f4mHt//Jh7f + /yUe3/8kHd/+Oj7j/5CU7/8uKOH+X17o/7m89f/Eyeb+qKnL/9LS7P/s7O3/7Ozs/2dt5/9pZ+X+j5Di + /8XGw/89ReL+REvo/1to7f9/k/L+k6b2/5Sh8/9QWur+Z3Hs/zNB6P4jMef/YnDt/7vD9/4uO+f/PUbo + /yQv5v4mNef/JDfp/yNA7v4bI6f/JR7c/yUe3/4lHd//JDXo/yNG7/4kMOb/JR/g/yUe3/4mHt//Jh7g + /yUe4P4bF6r/pafH/+rq6v7q6en/6urq/+np6f7r6ur/jZnd/yhI6qEAAAAAAAAAAAAAAAAAAAAAAAAA + AO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiRe4CIkbv + aSJG7/YiRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4iRu/+Ikbv/iJG7/4oSuT+UGXC + /mxxtf5XVrf+Jh/e/iUe3/4lHt/+JR7f/iUd3/4wL+H+MjLi/i4p4P5DQuT+JR7f/mhn6f6Iieb+t7fH + /oJ/5P6Iheb+xsfb/nt+2v5pY+P+f3zj/ru8xf5cXNj+OTbj/jEr4f6ChO3+aWvp/iIa3/4lHt/+JR7f + /iUe3/4lHt/+JR3f/iEa3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iYe4f4bGaD+JR7e/iUe3/4lHt/+JSXi + /iUk4f4lHt/+JR7f/iUe3/4lHt/+JR7f/iYe4f4aGIP+X2Bl/svLy/6wsLD+t7e3/tjY2P7q6ur+19vq + /itO7+1cb94JAAAAAAAAAAAAAAAAAAAAAO7u7h/u7u6X7u7uDQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAClF5wIiRu59I0bv+CNG7/8iRu/+I0bv/yNG7/4jRu//I0bv/yJG7/4jRu//I0bv + /yJG7/4jRu//I0Pt/zhLzv6RlbD/s7TB/7W2xf5/gLD/Jh/e/yUe3/4mHuD/Jh7g/yUe4P87OeP+Vljn + /1xg6P8rJuD+JR7f/yUd3/8qJN7+tLXI/6Wl5P+louj/tbXk/6uty/9pZOP+eXXl/56gyv9/gdH+NDPi + /yYe4P8kHd/+UlLm/0tK5f8lHt/+Jh7g/yUe3/4mHt//Jh7f/yUe3/4lHt//JR7f/yUe3/4mHt//Jh7f + /yYe4v4bGKH/JR7e/yUe3/4lHt//JR7f/yUg4P4mJOH/JR7g/yUe3/4mHt//Jh7f/yYe4f4QD1f/AQEB + /wwMDP4CAgL/BQUF/xMTE/5ZWVn/3d/j/z9e7v5Ua90vAAAAAAAAAAAAAAAAAAAAAO7u7h/u7u6X7u7u + DQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACxK62QjRu/8I0bv/yNG7/8iRu/+I0bv + /yJG7/4jRu//I0bv/yJG7/4jRu//I0bv/yNE7v4jMuj/REa+/6Kjtf62uMf/o6Sv/5+gqf5JR7n/JR7g + /yUe3/4mHuD/Jh7g/yUe3/8lHd/+MSzh/y8q4f8lHt/+Jh7f/yYe3/8kHeD+np/L/3Fw1f8wKt/+NC7f + /6ipyf9zctz+W1bj/3x80P+doMv+XmPo/yQc3/8lHt/+Jh/g/2Bl6P8lHd/+Jh7g/yUe3/4mHt//JR7f + /yUe3/4mHuD/Jh7f/yUe3/4lHt//JR7f/yYe4f4bF53/JR7d/yUe3/4lHt//JSXi/1dr8P5HU+v/JR7f + /yUe3/4lHd//Jh7g/yYf4f4PD1f/AAAA/wAAAP4HBwf/WVla/0VFRf4vLy//5OTl/05o7P5PZtRLAAAA + AAAAAAAAAAAAAAAAAO7u7hXu7u5l7u7uCAAAAAAAAAAAU1HRAlRS0RFbW9UqSEnPRkZH0Fs9Ps5UNj3U + QSJB6eIhRO3+Ikbv/iJH8P4iRu/+Ikbv/iJG7/4iRu7+Ikbv/iJH7/4jQu3+JDLm/iQg4P5LSbn+qqu5 + /ra3xv61tsb+trfG/rK0vf40L8j+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f + /iUe3/4kHN/+fn7Q/pCSzP4jHN/+JB7f/ltb1v6hosn+KCHf/lJQ1/64ucX+mZ7t/nh47P5ZWOf+h4vt + /k9O5f4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iYe4f4bF5z+JR7b + /iUe3/4lHt/+OTvl/pSg9v5KUen+JR7f/iQc3/5MUuf+JR7f/iYe4f4SEGf+AAAA/g0NDf7Hx8f+6+zs + /uvr6/6EhIT+lZWW/kZh3f5GXM1OAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCP8ojKSTB + wSMdv+VfZNz0fYjr+3uD6v5ocOH+R0jQ+HaB6f5odOb/WWjk/0td5P86UeT+LEfm/yRE6v4jRO7/Izzr + /yQt5f4lIOD/JR7f/0dFvP6qq7r/trfG/7a3xv62t8b/trfG/7e4x/5RULX/JR3h/yUe4P4mHt//JR7g + /yUe4P8lHuD+JR7f/yYe3/8lHt/+JR7f/yUe3/8lHt/+XVzW/7S2xv8jHeD+Jh7f/yYh3/+uscf+Tk3Y + /zAs3f/DxMP+TErb/3R16/+Kje7+SUbl/yUe3/8lHt/+JR7f/yUe3/4mHt//JR7f/yUe3/4lHt//JR7f + /yUe3/4lHt//JR7f/yUe4P4bF6D/JB3V/yUe3/4lHt//JSDg/3OB8v5haOz/JR7f/yMb3/5zfe//Lyrh + /yUe4P4YFov/AQED/3h4eP7r6+v/6enp/+np6f6hoaH/JCQl/0Jc3f4/U8tFAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAABGQssZNTHKvICA3/ibnen+pajs/rOy8P4+O93+JyHZ/jEt2/43NNr+Pz/Z + /lNW3v5vd+X+fYfq/mx34/4jIdL+JR7f/iUe3/4lHuD+OjjG/qWmtv62t8b+tbbG/ra3xv62tsb+trfG + /re4yP6ZnLP+UkWU/lAlY/5XJlH+UiVd/kgkff45Iqf+LCDO/iQe4v4lHuD+JR7f/iUe3/4lHt/+Qz/a + /sXGw/4zL93+JR7f/iUe3/5nZtT+mpvL/iMd4P6srsj+ZWXU/iQd3/4kHN/+JR7f/iUe3/4lHt/+JR7f + /iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4dGaz+IhvH/iUe3/4lHt/+JR7f + /lRj7v5mc+/+KCLg/iQc3/53gPD+TlDo/iUe3/4hHML+FBUo/s/P0P7p6On+6enp/uzs7P5ERET+QkNK + /jJP3v4vMtydO0LYFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJSV6BSsrPBNsbP0 + i7e798ZKSeXvJR3f/iUe3/4mHt//Jh7g/yYe4P8kHd7+IxzZ/ygi2/4lHt7/Jh7g/yUe3/4nINz/hoeu + /7a3x/62t8b/trfG/7a3xv6trbv/oJ6o/3ldX/5oOjf/ZSom/2UoI/5lKCT/ZSgk/2UoI/9mKCD+Zigi + /1wnQP9FJIb+LyDG/yUe4f8lHt/+MCve/7u8xf9UU9f+JR7f/yYe4P8tJ97+rrDH/0tJ2f+Cg8/+jY7N + /yMc4P8lHt/+JR7f/yYe4P8lHt/+JR7g/yUe3/4mHt//Jh7g/yUe3/4lHt//JR7f/yUe3/4mHuD/Jh7f + /yUe3/4hG8H/Hhmx/yYe4P4mH9//JR7f/yM46f4jM+f/Jh7f/yQc3/5weu//eYLx/yUe3/4lHt7/Rkae + /+Li4f7n5+f/xsbG/2BgYP4DAwL/XF+D/x884v5mbu3/a3Pu7zMy43I4NuMCAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF5j5gY8Q+U9Izvq9SQ05/4lI+H/Jh7f/yYe4P8lHt/+Jh7f + /yUe3/4mHt//Jh7g/yUe3/44NMj/rK28/7W2xv62t8b/trfG/6Wmsv5/d3v/aDYz/2UnI/5lKCP/ZSgk + /2UoI/5lKCT/ZSgk/2UoJP9lKCP+ZSgk/2UoI/9lKCL+Yigs/1IlYP8zIbj+KCHe/6Slyf95edD+JB3g + /yYe3/8lHeD+bm/T/5eZzP9aWdb+rK3I/ych3/8lHt/+JR7f/yYe4P8lHt/+JR7f/yUe3/4mHuD/Jh7g + /yUe3/4lHt//JR7f/yUe3/4mHuD/Jh7f/yUe3/4kHtb/Gheb/yYe4P4lHt//JR7f/yQ16P4jP+z/JR/g + /yQc3/5weu//kZ32/z085P4lHuD/MCzI/6SnyP6IiIj/DAwM/wEBAf5QUVn/OEaz/yQ56v4nH+D/Ozrk + /19m6/svKuFZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAyOOViJDnp + /CUh4f4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/5LSb7+tLXD/rW2xv61tsb+q6y5 + /oiIjv5nNDH+ZScj/mUoI/5lKCP+ZSgj/mUoI/5lJyP+ZSgj/mUoI/5nJx/+Zici/mYnIf5mJyD+Zyce + /mYnH/5mJyL+UyVY/pSLr/6am8f+IBu7/iIcyf4lHtn+Mi3c/ra4xv5YWdf+uLrG/jg03v4lHuD+JR7g + /iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4mH+D+Gxef + /iQd1v4lHt/+JR7f/iQu5f4iR+/+JDPn/iQc3/5veO/+lKD3/n2H8v4mH9/+g4Tt/o6RqP5PUnL+CAkW + /jg5Vv4+SbD+IUHl+yUr5LklHt/1JR7f/ich4PoyLuI+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAADg45AUmJeLpOUHn/yUe3/4mHt//JR7f/yYe4P8lHt/+Jh7f/yUe3/4mHuD/JR7f + /yUe3/5WVrv/tbfE/7W3xv62t8f/lJSd/4Nzd/5lJyL/ZSgk/2UoI/5lKCT/ZSgk/2UoI/5lKCT/ZSgm + /0c2hf8vQcz+LEXa/1Vl3P9lcNf+aXHQ/2lsx/9pZ7z+Tk2w/09bwf9hd9f+HzC2/xsqrf8ZJaf+GCGf + /2lwtP+CiLf+qKu8/0xMov8aFpn+HRio/x8auv8iHMv+JR7Z/yYe4P4lHuD/Jh7g/yUe3/4mHt//JR7f + /yUe3/4lHt//JR7f/yUe3/4mHt//IRvG/x4as/4mHuD/JR7f/yUh4P4jRe//I0bv/yNA7P5fdfL/lKD3 + /5ah9/5HSef/w8Xz/1dYWP4HCiH/Ijjb/yBB5/4jRvD/Iz7q2yw1zA4vM9gLLCzgIS4r4RsAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADo45B8lHt/+TU/n/2Zs7P48O+T/LCfh + /ygi4P8mH9/+JR7g/yUe3/4lHd//JR3f/yQd4P5cXLn/trfF/7W3xv63uMf/kJGZ/3teYP5lKCP/ZSgk + /2UoI/5lKCT/ZSgk/2UoI/5lKCT/YSow/ydC4P8iRu/+Jknv/3OH9P+FlfX+h5b2/4iX9v+Il/b+boP1 + /yFF7/8iRvD+Ikfw/zZX8f90h/X+fI71/3iK8/9xhvD+a4Hs/1Np3v8zRsn+JTW3/xwnpP8YHpT+FxmP + /xkXlv4dGaz/IRvG/yQd2P4lHt//Jh7g/yUe4P4mHuD/Jh7g/yUe3/4mHt//JR7e/xwYov4kHdX/JR7f + /yUe3/4kNuj/I0bv/yJG7/4yU/D/iZf2/3qG8v5EReX/t7jC/wcHB/4GChz/IkTk/yJG7/4jRe7+JS7d + cgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AEtI5jolHt/+Ixvf/jEt4v5nbu3+eYTx/nJ+7/5weO/+bnfv/m947/5qbu7+ZGrs/l9l7P5zeML+tLXE + /rW2xv62t8f+k5Sd/nteYP5lKCP+ZSgj/mUoI/5lJyP+ZSgj/mUoI/5lKCP+ZSgk/lEpYf4tKs3+JCvm + /iQs5P4mL+X+JzHm/icz5v4nNOb+JjTm/iQy5v4kMuf+JDbo/idA6/5CXfD+WnPz/nGG9P6Glfb+k5/2 + /pii9/6VoPb+jZv3/oGS9/5vg/X+W3Hv/kJb4/4pQtL+HDC+/hsnsf4dIa7+Hhuy/h8ZwP4jHNL+JR7c + /iYe4P4mHuH+JR7d/h8atP4XFIf+IxzO/iUe4P4lIeD+I0Ht/iJG7/4iRu/+MFLw/ipL7v6AlfX+VFRV + /gAAAP4DBhT+IULe/iJH7/4kMuXBJDTdDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGZk6lFzc+z/l5jy/6+y9f4wLOH/Ixzf/yMc3/8kHN/+JiDg + /zIw4v5cYev/dH3w/3yF8f6AiNn/qqu6/6OksP63uMf/mZqk/5WRmf5mMi7/ZScj/2UnI/5lKCT/ZSgk + /2UnI/5lKCT/ZSgk/2UoJP9lKCX+UCRi/zQhtP8kHd7+JR7f/yYe3/8lHt/+Jh7f/yYe4P8lHt/+Jh7f + /yYd3/8lHt/+JSHh/yMm4/8iL+b+KDzp/zVO7f9JYvD+W3Tz/22C9P99jvX+ipj2/5Sg9v6Woff/iZj2 + /3GF9f5WcPX/PVvw/2uA4v5AVMX/Rk+t/xgdl/4XFYf/FRJ8/xQSef4UE3r/FhOA/yEbxP4mHuD/JSbi + /yND7f4jRu//I0bv/x9E7v6qs9L/DAwM/wAAAP4DBg3/IUHW/iQy3sslNL0VAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALa29m3Q0Pv/0ND6 + /8/R+/5CPuT/JR7f/yUe3/8lHt/+Jh7f/yUe3/4lHd//JR7f/yYf3/4/PuP/kpaz/6Wmsv62t8f/sLHA + /5CRmP6UkJj/eFxd/25FRP5qPDn/aTg1/2g2M/5pNzT/aTk2/2w/Pf9zUFD+gWpt/5KJkv+PkMX+VFPT + /iok2/glHt/+JR7f/yUe3/8lHt/+Jh7f/yYe4P8lHt/+JR7f/yUe3/8lHt/+JR7f/yUf3/8lIeD+JCbi + /yQu5f8kOur+KUXt/zJS7/5DYfH/WXLz/3GF9P6Glfb/kJz2/7W++f6jsfn/r736/0Vh7/4tSuH/HzXE + /xginv4VFYD/FBJ5/xUTff4fGrj/JR7f/yUl4v4jPuv/I0bv/0Fi8v5+fn//AAAA/wAAAP4EBQr/KS64 + qyMqhxUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAKyv9XSlp/T+cXHs/kVC5P4nIN/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f + /igi3/5/ifL+jpfe/qKjsf62t8f+trfG/rGywP6TlJz+ra68/rO1xP6vsb/+ra+9/qytuv6trrz+r7G/ + /rCywP6qrLn+nZ6o6Y2OlcJ6eJONaGaUYEA9vjgpIt17JR7f2iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f + /iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHd/+JR/f/iUj4f4lLOT+IzXo/iE/7P4lSO/+MVPw + /oSZ9v6Upff+usP6/pah9/6Qnfb+fY71/lt09P4nReP+HC65/hYYh/4UEnn+Gxeh/iUe2v4lH+D+JCfj + /omV8f4oKCj+AAAA/gAAAP4LCwv+dXeHYwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEE843wiG9//Lynh/zc04v4lHuD/Jh7g + /yYe4P8lHt/+Jh7f/yUe3/4mHt//JR3f+1FT54uFju9vjJXtg4uS0pmQkZ7Dqqu4+bW2xf63uMj/trfH + /7a2xv6xssH/qKi195qbpNuMi5Kygn2Bhnlxc1N4bHIkeGt+BQAAAAAAAAAAAAAAAAAAAAAAAAAAKiPb + BSYe30IlHt+iJh7f6CYf4P8lHt/+Jh7g/yYe4P8lHt/+Jh7f/yYe4P8lHt/+JR7f/yYf4P8lHt/+Jh7g + /yUe3/4mHt//Jh7g/yUd3/4lIeD/LTXm/6Cv9v5Ra/H/Xnz0/zNU8P5ddvP/g5L1/5Wg9v6Glvb/LU/w + /yNG7v4eNcn/GB+X/xYUgf4eGbTwJiDe2Kqs1PoAAAD/AAAA/wAAAP4cHBz+q6uukAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AIeI74itr/X/wsP5/8HD+P4oIuD/Jh7g/yYe3/8lHt/+Jh7f/yUe3/4mHuD/JyHgrT464AYAAAAAAAAA + AAAAAACXmrgHiouWNIqKkmyGh4yShIWKjIWFinGJiI5NhoSJJ4+JkA6Oh40CAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlHd8BJR7fGSUe318lHt+9Jh7f8iUe3/4lHt/+Jh7g + /yYe4P8lHt/+Jh7f/yYe4P8lHt/+Jh7g/yUe3/4mHuD/Jh7f/yUe3/4lH9//eHvs/15f6P6PlPD/NDbk + /yQw5v4jPOr/Jkfu/z1c8f5ievP/Nlbw/yJG7/4jRvD/Ikbu/yA82P4fMr6ORVHDOK+wtO8AAAD/AAAA + /wAAAP47Ozy6tra4rQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALS19pbQ0Pv+z8/7/rO19v4rJuD+JR7f/iUe3/4lHt/+JR7f + /iUe3/4lHt/XMCrhGgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAlHt4CJR7fKiYf33olHt/XJB3f/SMc3/4jHN7+Ixzf/iMc3/4jHd/+JB3f/iQe3/4mH+D+Kybg + /khG5f59ge3+eH3s/oOI7v5UU+f+JR7f/iUe3/4lHt/+JSLh/iUs5P4jOer+IkTu/iJH7/4iRu/+Ikbv + /iJG7/4iR+/+SWjy6oqKiv0AAAD+AAAA/gAAAPdJS1Q6xcXFxXqAmgYAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIeJ75tkY+n/NTHi + /yEa3/4mHuD/Jh7g/yYe4P8lHt/+Jh7g/yUe3+4mH98uAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAArJeACQUDkN21z65F8hO3geoLt + /n2F7f+Fje/+fofu/4GP7v6Bju7/hIvv/3+H7v50euz/XmDp/ykj4P4lHuD/Jh/g/yUe3/4mHuD/Jh7g + /yUe3/4mHt//JSDg/yUs5P4jO+v/I0Xu/yJH7/4jRu//TWzy/4ODg/4AAAD/AAAA/w8XOaxTXpAHwcHC + wHmEtx0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAEtH5Y1YVuj/e3vt/4eK7/4lH9//Jh7g/yYe3/8lHt/+Jh7g8iUe31AAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAABTUuYRRkPkTTg04qUzLuHpMi3i/TAs4f4vK+H/LSjg/ykj4P4lHt//JR7f + /yUe3/4mHt//Jh7f/yUe3/4lHuD/Jh7g/yUe3/4mHt//Jh7f/yUe3/4lH+D/JSTh/yQy5/4jQe3/TWzy + /9HR0f4CAgL/Bgsf/yFC2/gkRui/jJnS04GQzzsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI2T8E3T0vv+z8/7/s/Q+/4tKeD+JR7f + /iUe3/4lHt/6JR7faAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCQOMaNC/h + YCki4LglHt/5JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f + /iUe3/4lHt/+JR7f/iUe3/4lH+D+Sk/o/vz8/f4wMDL+GTGd/iNG8P4iRu7+O1vw/pSo9tJzjPFXWHPs + BQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AHR07AWfoPPZk5Px/1pY5/4mH+D/Jh7g/yYe4PslHt9tJR7fAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwK+EcKCLgbiYe378lHt/1JR7f/yUe3/4lHt//Jh7g + /yUe3/4lHt//JR7f/yUe3/4mHuD/JR7f/yUe3/4mHt//Jh7f/yUe3/4mHuD/SUfl//v8/v6Agrf/IzPl + /yND7f4jR+//PV3x/5Sp+P5sh/X/JEjv0TFT8FQnSu8FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABJReVXJh/g7CUe3/4mHt/+Jh7f7igg4GolHt8BAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + ACUe3wUlHt8qJR7fdiUe38ElHt/vJh7g/SUe3/4lHt//JR7f/yUe3/4lHt//JR7f/yUe3/4mHuD/JR7f + /yUe3/4lHt//LSfg/83P9/52d+v/JB3f/yUh4P4jLub/dYv0/46j9/5gffT/Ikbv/yJG7/sjRu+4IkTu + MyJE7gEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASkXl + IzMs4VQwKeFXLynhKgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUe3gIlHt8gJR7fXiUe36wlHt/yJR7f + /iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/jUv4v4oIt/+JR7f/iIb3/5kZej+zdD3 + /o2Z8v5KZfD+Ikfv/iJG7/4iRu/+I0bv8CJF7noiQ+0DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAlHt8PJR7fUyYe36MlHt/lJR7f/iIb3/4iG9//Ihrf/yIa3/4iG9//Ihvf + /yQd3/4zLuH/Xlzo/6Cl8f61u/X/kZjw/4+U8P4mH9//JSnj/yM86/4jRu//I0bv/yJG7/4jRu+7I0Pt + JwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACcg3wErJOAVMy/h + T4GC7aCGiu7jlZbw/I6Q7/6NkO//kprw/5qi8f6ptPT/rLT0/5ef8f6Ag+7/UlLn/ykj4P4mHt//Jh7f + /yUe3/4lKOP/Izzr/yNG7v4jRu//I0bv5SJD7V4jP+wEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABTU+YSQj/kTTg04ps0MOLqNDDi/jQv4v4wLOH+KSPg + /iQe3/4kHN/+JR7f/iUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f/iUo4v4jPOv+I0bv/iJG7/wiRe6fIz3r + EwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAABIReQIOTXiRjEr4ZYlHt/eJR7f/SUe3/4mHuD/Jh7f/yUe3/4mHt//JR7f/yUe3/4mHuD/JR7f + /yUe3/4mHt//JSjj/yM86/4jR+//I0bv0iM66hkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/O+MQLijhQycg4JMlHt/cJR7f + +SUe3/4mHt//Jh7f/yUe3/4mHuD/JR7f/yUe3/4mHt//Jh7f/yUf4P4kLeX/I0Tu7yM76iYAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAlHt4MJR7fQCUe34olHt/aJR7f/SUe3/4lHt/+JR7f/iUe3/4lHt/+JR7f + /iUe3/4lHd/2JDXoayM26AIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlHt8CJR7f + JyUe32UlHt+jJR7f2yUe3+4mHuDiJR/gpyUg4HIlJeIlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlHt8DJR7fECUe3xclHt8SJR/fAwAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAD///////////////////////////////////////////////////////// + //////////////////////H///////////////H///////////////H///////////////H///////// + //////H///////////////H////////////wH/H////////////AD/H///////////4AA/H///////+D + //4AA/H///////4A//AAAHH///////wAf/AAAHH///////gAf+AAAHH///////AAP+AAAHH//////+AA + IIAAAHH//////gAAAAAAAHH/////+AAAAAAAAHH/////gAAAAAAAAHH////+AAAAAAAAAHH////+AAAA + AAAAA/H////+AAAAAAAAA/H/////AAAAAAAAD/H/////gAAAAAAAH/H/////gAAAAAAAH/H/////wAAA + AAAAP/H/////wAAAAAAAf/H/////4AAAAAAAP/H////H8AAAAAAAH/H///8AAAAAAAAAD/H///wAAAAA + AAAAB/H///wAAAAAAAAAAvH///4AAAAAAAAAAPH//+HAAAAAAAAAAPH//8DgAAAAAAAAAPH//4B8AAAA + AAAAAPH//wB/AAAAAAAAAPH//gA/gAAAAAAAAPH//gA8AAAAAAAAAPH//AAAAAAAAAAAAfH//AAAAAAA + AAAAAfH/+AAAAAAAAAAAAfH/+AAAAAAAAAAAAfH/+AAAAAAAAAAAAfH/+AAAAAAAAAAAA/H/+AAAAAAA + AAAAAfH/8AAAAAAAAAAAAfH/4AAAAAAAAAAAAfH/gAAAAAAAAAAAAPH/AAAAAAAAAAAAAPH/AAAAAAAA + AAAAAPGAAAAAAAAAAAAAAP8AAAAAAAAAAAAAAP8AAAAAAAAAAAAAAH/AAAAAAAAAAAAAAB/4AAAAAAAA + AAAAAB/8AAAAAAAAAAAAAB/4AAAAAAAAAAAAAD/4AAAAAAAAAAAAA//4AAAAAAAAAAAAA//4AAAAAAAA + AAAAB//4AAAAAAAAAAAAD//4AAAAAAAAAAAAH//4AAAAHwAAAAAAH//4ADgB/8AAAAAAH//4AH////gA + AAAAD//4AP////8AAAAAD//4Af/////gAAAAD//4A//////8AAAAA//4A///////gAAAAP/8B/////// + 4AAAAD/+H////////AAAAB///////////8AAAA////////////AAAAP///////////8AAAH///////// + ///gAAD////////////8AAD/////////////gAD/////////////8AP//////////////g////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////8oAAAASAAAAJAAAAABACAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAA7e3tA+3t7QQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAA7e3tMe3t7TwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7e3t + QO3t7U0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7e3tQO3t7U0AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7e3tQO3t7U0AAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5+foP9bX2Ky6vsCsys3OrOjo6KDr6+sVAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAA7e3tQO3t7U0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAADb3N1Nz9LUxaayuv92kaX/kqWy/83Q0vno6Oil6+vrIgAAAAAAAAAAAAAA + AAAAAAAAAAAA7e3tQO3t7U0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADu7u4E7u7u + Zd/h4prBx8zKlqe0/1aCo/8ocKb/RHqk/4ygr//N0NP65+foqOvr6y8AAAAAAAAAAAAAAAAAAAAA7e3t + QO3t7U0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAcxGwMDFnEDAxWFAgIRcQUEGykICCABAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO3t7THr6+uT4uPj2bnBx/9/l6v/SXuk + /w9orP8DZK//F2yt/0l/qf+Qoq//ztLU+uXm5rLt7e137e3tDgAAAAAAAAAA7e3tQO3t7U0AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAABwaaQIICCVuBgYh8wwMQP8KCTj+BQUa/wAAAPsGBgqeICArCgAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAA6enpC+np6Wfg4eL/q7S6/2iKpP8wc6b/CWat/wBjsP8EZrL/Inm8 + /0eMwv9kk7f/kqKu/87R1P/p6erS6+vrGQAAAAAAAAAA7e3tQO3t7U0AAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWFiD + AR0dPoUZFpX9JB3i/y8q3f9RTtH/YF/L/zw8j/8HBxP+EhQdhAAAAAAAAAAA7e3tAenp6QLk5OQCAAAA + AAAAAADi4uIB6Ojobdvc3O+gqK7/UoCi/xtrqf8EZK7/AGOw/wRmsf8nfL7/YKDR/4+84P+Putz/cp7B + /5Kjsf/Nz9LS5ubnGQAAAAAAAAAA7e3tQO3t7U0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANjdJbzs6pf14d9L/uLnZ + /unq6v/9/fz++Pn7/+zu9/6nqrT/KSgn7nl/lhqurq0P3d3dieXl5Pff3971ycnISLCwrxLY2NiMx8nM + +oSWo/82dKT/DWar/wFjr/8BZLH/Cmq0/zOEw/92rdj/nsbl/6zP6v+fxOL/eaHA/5SksP/Oz9LS5ubn + GQAAAAAAAAAA7e3tQO3t7U0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAC+wc0Br7LCFqKlsFawsbamzs7P+fHy9f/c4vr/p7f2/3aN8P9LZ+v/N1fr + /zJT7/9whun/wMPQ/7e3ueenp6bhnp2c8JmXlP+XlZH/kZCO6JiXl+GdnZ7xg46W/0t3mv8WaKj/AmOu + /wJlsf8Qbrf/NITD/3qw2f+jyeb/sdHs/6HG4/+AqMr/kaOy/8bLzv/l5ubS6urrGQAAAAAAAAAA7e3t + QO3t7U0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAo6a3 + G6OlsGW+v8W24ODg8PX19f7M0uz/iZvo/kdk7P8kSO7/IUXv/iNG7/8uUO7+jJzq/7/E3P+sr7z/raql + /7Supf+7tKj/vrap/8G5rP/Du63/vbWo/7ewpP+oopr/iImH/19yf/9Dbo//HGml/xdxt/9AjMf/fbHa + /6rN6P+51u3/n8Xj/4Oqyv+SpbT/xcrO++Tk5brt7e2A7e3tDwAAAAAAAAAA7e3tQO3t7U0AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnaPBF6Cjs2i8vL+95ufn9+rt+P61wPD/c4jp + /zdW5/8iRu7/Ikbv/yNG7/8jRu//J0nu/y9Q7v+Rn+X/v8DO/7a0sf+yraX/vreq/87Ftf/Zz77/39TC + /+TZx//l2sj/4dbE/9rQvv/Rxrf/vrWo/5yYkP90e3//T2+H/0F7qP9gns7/pszm/7/a7/+gyOb/iK3K + /5KouP/Dx8r64uPjsevr6zTt7e0BAAAAAAAAAAAAAAAA7e3tQO3t7U0AAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAACNlLYa09PV5+To+P+hsfX/XXfv/y5Q7f8hRe//Ikbv/yNG7/8jRu//I0bv + /yNG7/8jRu//XHXu/7zE6/++wMv/tLGt/7Oso//NxLT/39XD/+XayP/m28n/5tvJ/+bbyf/l2sj/5tvJ + /+bbyf/m28n/4tjG/9HHt/+tppv/goF+/2V6iv9ul7f/mcPi/6HJ5/+GrMj/l6q6/8PFx/rk5OSw6enp + JQAAAAAAAAAAAAAAAAAAAAAAAAAA7e3tQO3t7U0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAACGksgEgY3BoCRI7/4iRu//Ikbv/iNG7/8iRu/+I0bv/yJG7/4jRu//Ikbv/iNG7/8kR+7/boTt + /tjY2P+0srD/t7Go/9XLu//l2sn/5tvJ/+bbyf/m28n/5tvJ/+bbyf/l2sj/5tvJ/+bbyf/m28n/5tvJ + /+XayP/e1ML/rqid/4CBgP9ugpH/cqLH/3ulxv+Zqbb/xMbJ+ePj463n5+cWAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAA7e3tQO3t7U0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHHl + DCNH7+MjRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/9of+3/xcrm/7e2tf+4s6r/z8W2 + /+XayP/m28n/5tvJ/+bbyf/m28n/5tvJ/+bbyf/l2sj/5tvJ/+bbyf/m28n/5tvJ/+bbyf/l2sj/2M69 + /6agl/9ze4D/aISb/5Kgqv7BxcfR4OHhrunq6gUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7e3t + QO3t7U0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACVI7W0jRu/+Ikbv + /iNG7/8iRu/+I0bv/yRH7v5JZe7/VXDu/lZw7v+Zp+n/1tbV/6yqpf/KwbT/4dbE/+XayP/l2sj/5drI + /+XayP/l2sj/5drI/+XayP/l2sj/5drI/+XayP/l2sj/5drI/+bbyf/l2sj/5drI/8W9r/+UkIr/enp7 + /7/Bw/zO0NFQ4OHhBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7e3tQO3t7U0AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG7g0jRu/ZI0bv/yNG7/8jRu//I0bv + /yZI7v9KZu7/WXLu/1ly7v+XpOb/ycnI/7mzq//Uy7v/5NnH/+XayP/l2sj/5drI/+XayP/l2sj/5drI + /+XayP/l2sj/5drI/+XayP/l2sj/5drI/+bbyf/m28n/5tvJ/9nPv/+wqqD/gH9+/9DQ0P6Gho6CBgYk + AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7e3tQO3t7U0AAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjRO5bIkfv/iNG7/8iRu/+I0bv/yJG7/4jRu//Ikbv + /iNG7/93iuP/wL+//8O8sf/a0MD/5tvJ/+XayP/l2sj/5drI/+XayP/l2sj/5drI/+XayP/l2sj/5tvJ + /+bbyf/m28n/5tvJ/+bbyf/m28n/5tvK/+LXxv++t6v/gYB//7+/wP8/Pz/8AwMSiQ0RUAMAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAA7e3tQO3t7U0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0PucHMz3n + BAAAAAAAAAAAAAAAAAAAAAAjN+kCJDzrtiQ16P4jRu//I0bv/yNG7/8jRu//I0bv/yNG7/92iOH/uLi3 + /8vDtv/h2Mj/6N7P/+bcy//m28n/5tvJ/+XayP/l2sj/5drI/+XayP/l2sj/5tvJ/+bbyf/m28n/5tvJ + /+bbyf/m3Mr/6N7P/+jf0P/Jwbb/ioiH/6+vsf9ISIP/CQwt/g4WWkUAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAA7e3tQO3t7U0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkKuQFJjTnWCYv5asxOubcQErp1Cov5LcmLuWYJSvk + dCQy51QkNegxJzjoJCYi4cYkKt7+HjDC/yA+3P4jR+//Ikbv/iNG7/92iOH/urm4/8vDtv/m4Nb/8ezk + /+/o3v/q4dL/593M/+bbyv/m28r/5tvJ/+Xayf/m28n/5tvK/+bbyv/m3Mr/59zK/+nezv/t5dn/8ezj + /+7n2//Kwrf/i4qH/7Cwsv9STtb/JSnk/iJF7NofO9ZSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7e3t + QO3t7U0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAACAnygQkLuSyJR/f/ich4P9HSOf/Pj3l/zUz4/8lHt//JR7f/yUe3/4lH+D9JSPh + +Csv5PA7RNn/Jy7B/xshqP8bJqz/HzbJ/yA61P93h9r/w8PC/8fAtv/k3tb/9PDq//Xx7P/z7+j/8ezj + /+/p3v/t5dn/6uLU/+nf0P/p3s//6uDS/+vi1P/s5Nj/7ufb//Lt5P/08On/9fHr/+vk2v/DvLD/iYiG + /8HBw/9YU9n/JR7f/yNC7f8jRu/8KkzrVgAAAAAAAAAAAAAAAAAAAAAAAAAA7e3tQO3t7U0AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAbGqUXIx7RfCUe3+UmHt//Jh7g/yUe3/8lHuD/JR7f/yYe3/8mHuD/JR7f/1VY6f+Wovf/j5r1 + /3+J8/9ocu//T1jl/zI70v95fsz/z8/P/8G8tf/f2c//8+7n//f08f/49fH/9/Tw//bz7v/18ez/9fDq + //Pu6P/z7uf/9fDp//Xw6v/28ez/9/Lu//j08f/49PH/9e/p/+Hb0P+4sqn/jIuK/9PT1v9eWuD/Jh7f + /yM76v8jRu//I0fv4T9a2ROhqtU3AAAAAAAAAAAAAAAA7e3tQO3t7U0AAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIBy8fBQUdWwgJNjgSEXABAAAA + ACIdvwglHtpiJR7fzSUe3/0lHt//JR7f/iUe3/8mHt//JR7f/iYg3/88OuT+UlXp/2Rp7P52f/D/ipX0 + /pai9/+2vO3/29vb/7y6tv/UzsP/7Obc//f08P/59vP/+fbz//n28//59vP/+fbz//n28//59vP/+fb0 + //n29P/59vT/+fb0//n29P/49PH/8Org/9HJv/+ppaH/nJyb/5GO2v9EPuH/JR7f/iM86/8iRu/+I0fv + /pml2LlBRl1rAAAAAAAAAAAAAAAA7e3tQO3t7U0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwLRUsODlDsCAgp/wAAAPoFBCGKAAAAAAAAAAAAAAAAJR7e + BiUe30glHt+2JR7f+SYe3/8lHt//JR7f/yUe3/8lHt//Jh7f/yUd3/8lHd//KiXg/0dI5/+Lkuz/x8rn + /8XFxP/Iw7v/3NXL//Ht5f/59/T/+ff1//n39f/59/X/+ff1//n39f/59/X/+vj2//r49v/6+Pb/+vj2 + //n29P/z7uj/4dnO/7iyqv+ioJ//v76+/0U/3P8lHt//JSLh/yNE7v8iRe//gJbx/UJCQ/MgIzNdAAAA + AAAAAAAAAAAA7e3tQO3t7U0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAFBNgMBERZPAbF6X/JB7j/hgVif8CAgn+BgUlVAAAAAAAAAAAAAAAAAAAAAAAAAAAJSXi + LCUh4ZgmHuDuJR7f/iUe3/8lHt/+Jh7g/ygi3/5gW+P/eXbm/omQ7/+irPX/u8Hy/t3d3f/HxcL/ysa+ + /+Lb0P/z7+j/+PXz//n49v/6+ff/+vn3//n49//5+Pf/+vn3//r59//6+Pb/+Pb0//Tw6v/n39T/w720 + /6imov+trbT/n5zc/zkz3/4lIOD/Iz3r/iJG7/9fe/T+m5ye/gEBAf4iIyxOAAAAAAAAAAAAAAAA7e3t + QO3t7U0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdHHINJCZu + zyActv8kHef/JR3n/yUd6P8WFIP/AgMRqwAAAAAAAAAAAAAAAAAAAAAAAAAAIzboByM46SEkNehlJC/m + 5XV40/9naNT/JR7g/y8o3/+5t+r/7u7u/6Cf3v+Nj+r/wcfx/+fo7f/U1db/yMbD/8vGvf/e1sz/7efg + //Xx7P/49fL/+ff1//r59//6+ff/+vj2//j28//28+3/7+ri/9/Yzv/Iwbf/qqij/7Ozs/+8u83/Ojin + /yAcwP8jOef/I0bv/yVJ7//U3Pn/Jycn/wMDA/1RUVQ5AAAAAAAAAAAAAAAA7e3tQO3t7U0AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcHVB/PDvA/iIc1/4gGsT/JB3n + /iUd5/8kHdz/BQcevQAAAAAcOMANIkXtOyJF7nUjRe6uI0bv3yNG7/wjRu//Ikbv/mR73f+cnsv+Jiji + /zAp3/65t+r/7u7u/7i42v9wa+T/Y2Hl/l9d5f/AwOf/2NjZ/8jHxv/Hwr3/083B/+Haz//p49r/7eni + //Lu6P/y7+n/7+rk/+nk3f/h29D/0su+/724sP+sq6j/ubm4/7zA1v9bcuP+KEHV/xwss/4YIJf/GSSh + /j1U1v+4ur3+AAAA/xoaGv1kZWUzAAAAAAAAAAAAAAAA7e3tQO3t7U0AAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwcYw0wMnbpIx3O/xkXkP8WFYL/JB3j/yQd5/8lHur/FSR8 + 2iJF67giRu7uI0bv/SNG7/8jRu//I0bv/yNG7/8jRu//I0bv/0Vi5v+6vsb/S2bk/y5G6/+5uuv/7u7u + /8rL1/9ybuT/SELh/yYe3/9FP+H/rq3n/+Xl5f/S0tH/w8G+/8fDvf/NyL//0cvA/9XNwf/XzsH/0srA + /8vFvP/Avbb/tbOw/7e2tv/R0dH/x8zl/09q7f8oSu7/I0bv/yNG7/8iRu7/ID7b/0lUtf9hYmr/AAAA + /05OT/tvb28kAAAAAAAAAAAAAAAA7e3tQO3t7U0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAABARQU0pKor+HxrG/xUTef4bF6H/JB3n/iQk6P8jPO3/I0fw/iNG7/8iRu/+I0bv + /yJG7/4jRu//Ikbv/iNG7/8iRu//Ikbv/ixO7P+YpM/+j53S/ypL7v6Rou7/tb7s/8nK0f9qcuT/PUXm + /iQs5P8kLeX+P0vn/1Fh6f6LmOn/39/g/9bV1v/NzMz/xMTE/7y8vP+4uLf/vb28/8PDw//Kycv/09PU + /9rZ4/95deL+SVHl/yVH7v8iRu/+I0bv/yNG7/4jOur/JDHm/iEptP8BAQH+AAAA/4aGhutqam0IAAAA + AAAAAAAAAAAA7e3tQO3t7U0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAsMM4caGoH/HxnD/yAaxf8jI9f/JDXp/yNF7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8nSu//LVDw + /yNG7/9CYvH/KEzv/4id9v+qs9H/fY/W/01p5f+wvPD/wMjt/7a81/+DleT/PVzu/yNG7/8jRu//I0bv + /yNG7/84WO7/WHLt/1dw6/9Ubuj/Umzn/1Bq5f9QauT/U2zl/1Js5/9QXsX/VVHL/1hT4v8zL+D/I0Ht + /yNG7/8jRu//I0bv/yQs5P8lHt//JR7f/yMdzv8DAw//AwMD/5KSmYsAAAAAAAAAAAAAAAAAAAAA7e3t + QO3t7U0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcII6kUEnv/ICi6 + /yM84f8jRer/I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8kSO//RWfy/y9S8P9YdvP/N1jw + /2uF9P+hq9T/RWPm/6qy1P/H0PH/wsrt/5yp3/+msd//aYPy/zJW8f9Yd/P/QmLx/yJG7/8wUvD/UnHz + /ytP8P8hRe//Xnvz/0dm8v8tUfD/SGjy/yFF8P8bH6b/JR7f/yYe3/8kMuf/I0bv/yNG7/8jRu//JC/m + /yUe3/8lHt7/Hhm0/y4stf8GBh3/Cgoa/FhXlzYAAAAAAAAAAAAAAAAAAAAA7e3tQO3t7U0AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGS6nAQ8bWLsdMLz+Ikbv/yJG7v4jRu//Ikbv + /iNG7/8jRu//I0bv/iNG7/8iRu/+I0bv/yJG7/4jRu//IkTu/lBv8v9jfPL/O1jv/k1s8v90h9n+UWzi + /4mY1f6bq+r/lKTu/26E5f++xNn/bojy/ipO8P8fQ+/+TGvy/1t79P6PpPf/P1/x/kRm8v8iRu/+N1vx + /05t8v9ObvL+aIT0/yBA4/4fGrb/JR7f/iUf4P8jQu3+I0bv/yND7v8lKuT+Jh7f/yYe4P4dGaj/lZa+ + /uzs6v+1t8b+MS+m/y8t05cAAAAAAAAAAAAAAAAAAAAA7e3tQO3t7U0AAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAfP9kJIULikiNG7/kjRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv + /yJG7/8jRe//JDvq/yQu5f8lJeL/JSHg/0BA5P+Jju7/KiXg/6ux9P+iqeP/dnrT/1xg4f+bo9H/S1vr + /zJD5f++wcv/UGXt/y5I7P83V+//S2ry/36V9f9ogfP/Q1/w/0pm8P+Po/b/TWjw/1Nq8P8rSu7/PVzw + /x860v8iHMn/Jh7g/yUj4f8jRu//Izvq/yUi4f8lHt//Jh7f/yQd2f9VVqH/6enp/+np6f/p6en/x8jc + /y9F4txeacsCAAAAAAAAAAAAAAAA7e3tGO3t7R0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + ACJG7hwiRu7EIkbv/iNG7/8iRu/+I0bv/yJG7/4jRu//Ikbv/iNG7/8iRu//LEzi/lFjyP9PUcL+Jh7f + /yUe3/4mHt//JyDf/i4s4f89POP/Ozfj/lVT5v+io+v+mJnL/yQc4P6Oj87/XF3d/iUe3/+jpMn/VlXc + /i8p4f+TlO/+dXfr/ykj4P4kHd//JR7f/icg4P83MuL+Jh7f/yUd3/8lHt/+JR/g/yAgwP4jHdH/JR7f + /iUh4P8kLeX+Jh/g/yYe3/8lHt/+Jh7f/yAawf5iZHr/zs7O/r6+vv/Y2Nj+6+rq/3aL7PxQZeEyAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKEbpICNG79gjRu//I0bv + /yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNC7f9CU8v/oKO5/66uu/+Gh7f/Jh/f/yYe3/8mHuD/JR3f + /0dI5f9ZW+f/KCLg/yUe4P8nId//qKrJ/ykj3/9CP9r/jY7N/yUd4P99fND/dXjV/yol4P8lHt//VVXn + /zAr4f8mHuD/Jh7g/yYe3/8mHt//JR7f/yUe4P8lHt//Jh7f/yAbvf8jHdP/Jh7f/yUe3/8oJuH/KSnj + /yYe4P8mHt//Jh7f/x4arf8BAgX/BwcH/wICAv8PDw//dHR0/5up7P9HYeBnAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAASUnQCUhM1BBCTd0JJUjuwiNG7/8jRu//Ikbv/iNG7/8iRu/+I0bv + /yJH7/4jQu3/JC/m/lBPv/+trr7/trfG/qWmsf9WU7/+JR7f/yUe3/4mHuD/JR7f/iUd3/8jHN//JR7f + /iYe3/8kHeD+m5zL/0A82/4lHt//kZLM/j472/9WVNf/oKTQ/mVl6f8nIeD+V1fn/zk14v4mHt//JR7f + /iUe3/8lHt/+JR7f/yUe3/8lHt/+JR7f/yAbvf4jHM//JR7f/iYh4P9xgvP+Q0fn/yUe3/8vLOH+JR7f + /x4asv4AAQX/Dg4O/pGRkf+xsLH+ampr/56p3P9AWth6AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQj7K + NCsmwqhLTdLRcXrl7G525PM/QM3lX23m/FFj5/9DWef/Mk3n/yZF6v8jRe7/Iz3r/yQt5f8lIOD/T02/ + /6+wv/+2t8b/trfG/7a3xv9mZbv/JR7h/yUe4P8lHuD/JR7g/yUe3/8lHt//JR7g/yUe3/8lHuD/fXzQ + /2Ff1f8lHt//SEXZ/4mJzv8xLN3/ra/H/2pq6f+WmvD/WVfn/yUd3/8lHt//JR7f/yUe3/8lHt//JR7f + /yUe3/8lHt//JR7f/yEbxf8hG8P/JR7f/yUe3/9ncu//TVHo/yUe3/9bYOr/KyXg/yIcyf8DAwz/hYWF + /+rq6v/p6en/f3+A/0ROhP84UdV1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAREDLKm1s266lpuzpsrPw + /omI6/4mH9r/Lyvc/jUy2/9FRd7/XmLi/mhv5P8sK9T+JR7f/yUe3/4/PMj/rK6+/ra3xv+2t8b/trfG + /rS1xP+Vk6P+Vjlz/1gmSv5YJkz/TSVr/j8jlP8wIML/JR7h/iUe3/8lHt/+Xl3V/4WEzv4lHt//JR7f + /peZy/86N9z/qKrI/jcy3P8kHd/+JR7f/yUe3/4lHt//JR7f/iUe3/8lHt/+JR7f/yUe3/8lHt/+Jh7f + /yQd0/4eGbP/JR7f/iUe3/8+Sen+S1Tq/yUe3/9gZev+SUrn/yUe3/4eHlX/1tbW/unp6f/k5OT+Jycn + /05bp/84ReXIOkHdOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACGh+UIhInrMWhr6XIlKePrJSbi + /yUf4P8mHuD/JR7f/yUe3/8lHt//Jh7g/yUe4P+Gh7X/trfG/7a3xv+ztML/k4+X/3FJSP9lKSX/ZSgj + /2UoJP9lKCT/ZSgj/2UoI/9lKCP/WidF/0Ajkv8pH9X/QDzb/6epyP8mH9//Jh7f/1BO2P+Ehc//hYTP + /1VT1/8lHt//JR7f/yYe4P8lHt//JR7f/yYe4P8mHt//JR7f/yUe3/8mHuD/Jh7f/yUe3/8cGKb/Jh7g + /yUe3/8kMOb/JDHm/yUe3/9dYuv/dn7w/yUe3/8zL8H/0tPb/4aGhv8kJCT/MzQ6/zJIyv87Pub/Ymjs + +j085HoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAElK5RIlPevqJSXi/iUe3/8mHuD/JR7f + /iUe3/8lHt/+JR7f/ykj1/6mp7r/tbbG/rW2xf+RkZn/ajo4/mUoI/9lKCP+ZSgk/2UoI/5lKCT/ZSgi + /mYnIf9mJyL/Zich/mYnH/9gJzP+SyuD/7i5wv4rJ8f/Ix3R/icg3f+focr/bm7T/np50f8lHuD+JR7g + /yUe3/4mHt//JR7f/iUe3/8lHt/+JR7f/yUe3/8lHt/+Jh7f/yYe4P4dGa3/JR7a/iUe3/8kK+T+I0Pt + /yUj4f9cYev+k572/0RF5v5fXeb/hIai/jIzRf8hIjT+Qk6u/yM85+ElHt/tJR7f/jIu4rQ4NuMBAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwo4Xo+Ruj/KyXg/yUe3/8lHt//Jh7f/yUe3/8lHt//JR7f + /y4q0f+ur77/trfG/6iptf+DdXn/ZScj/2UoJP9lKCT/ZSgk/2UoJP9kKCf/Ozyp/ylD3P9XaeL/c33e + /3V81v90ds3/T1XA/1Jp3P8oP8n/HjK+/yc1t/9TYL7/dH/A/4aNvv8XFpf/Gxeg/x4ZsP8hG8X/JB3X + /yUe4P8mHuD/JR7f/yUe3/8lHt//JR7g/yYe3/8jHdD/Hxq6/yUe3/8lH+D/I0Tu/yNE7v9KZPH/lKD3 + /3F57/+hoev/NDQ1/xckhf8hQuj/I0bv/ic02WIuNdUGLCzhGjIv4ggAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAADAq4aEuKeH/XWLr/2Fm7P9TVun/SUnn/0FB5f87OOT/NjPj/zs70/+vsL//trfG + /6Slsf+Aam3/ZSgj/2UoJP9lKCT/ZSgk/2UoJP9kKCj/Mja+/yM76/89Uu3/UWTv/1Ro8P9UaPD/Pljv + /yNC7f8jQ+7/QF7x/3uN9f+Lmvb/ipj2/4CR9P9tf+7/WWzj/0NV0v8sPb7/HSuv/xshq/8eHLP/IRzG + /yQd2P8lHt//Jh7g/yYe4P8mHuD/Hhmu/yIcy/8mHt//JDHm/yNG7/8lSe//YXjz/zpL6/+Ulqf/AAAA + /xQmgP8jR+//JDjozyYz2AsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AFtZ6Ll1dOz/d3js/iwo4f80MeL/OTbj/kVE5v9mbO3+f4ny/4SO7/6ho7f/rKy6/qusuf+Nh43/Zi0p + /mUnI/9lKCP+ZSgk/2UoI/5lKCT/ZSgl/lAkY/80ILX/JR3e/iUd3/8lHt/+Jh7f/yUe3/4lHt//JSLg + /iMn4/8nMub/Nkbq/khb7v9acPH+a4D0/32O9f6Mmvb/jJr2/nmL9v9gd/P+RV3k/zZL0P9MV8T+NTq0 + /xsapP4ZFZT/FRN9/hUTfv8gG8D+JR/g/yM66f8iRu/+Ikbv/2N98v45OTn/AAAA/hMkcP8jO+bhJjbL + JgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdXTsA9DQ+8/Nzvr/qKr0 + /yUe3/8lHt//Jh7f/yUe3/8lHt//JR7f/y0o4f+Jj8X/rK26/7W3xv+bm6X/i4WL/4Jna/94V1j/dVFR + /3NOTv92UlL/e1xd/4l0ef+YkJr/jI3A+kdFxeomHt/yJR7f/iUe3/8lHt//JR7f/yUe3/8lHt//JR7f + /yUe4P8lIOD/JCXi/yQu5f8nOur/MUzu/0Ng8f9ZcvP/c4b0/42c9v+3wfr/n674/09o7f8wR9T/HCqu + /xYXhf8UEnv/Hhmz/yUf3/8kM+f/IkPu/4CPyf8EBAT/AAAA/yMnW9wkKYkfAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjY3wBGRk6dU7N+P/JB3f/iYe3/8lHt//JR7f + /iUe3/8lHt/+JR7f+2517s6SnfLZlpq767GywP23t8f/q6y6/ra3xv+3uMj+trfH/66vvPugoazhlJKb + t4aCjId4cIhXYVqZKUtGtgkxK9cbJyDedCUe39AlHt/9JR7f/iUe3/8mHuD/JR7f/iYe4P8lHt/+JR7f + /yUe3/4lHt//JR7f/iUj4f8lK+T+IzXo/0Zh8P+Al/b+gZb2/3iK9f6Tn/b/fo/1/jdW7P8eNMT+Fx2R + /xkWlv8jHM7+Lyvh/29xff4AAAD/AAAA/mBgYddHSmoDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAi4vvBnp67d2gofP/iovw/yUe4P8mHt//JR7f/yYe3/8mHt//KSPg + m09O3gNxctYFkJPJCouNnD+TlJ2Lk5OcrZCQl6CSkZl9iIaMUIaBhyCLgYgHAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAACUe3gUlHt83JR7flCUe3+QlHt/+Jh7f/yYe4P8lHt//JR7f/yYe3/8mHuD/Jh7f + /yYe3/8lHt//KiTg/4WJ7v9wdez/NT/n/yQ66v8xT+//WHHy/1t08/8iRu//Ikbu/yA71P8dK7G3ZGrV + bkxMTP8AAAD/AAAA/YqKi8RlZnMUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAgoHuCczN+ue2t/f/d3fs/iUe3/8mHuD/JR7f/iUe3/8mHt/LMSzhDgAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAACUe3w0mH99QJh/frjUy4vQ5N+L+OTfi/zs54v4+PeP/QD7k/kVB5f9TVOf+goju + /3R47P9TUuf+JR7f/yUe3/4lIOD/JSrk/iM36f8jRO7+I0bv/yNG7/8iRu/+corw8ygoKP4AAAD/AwMG + v5qcpHyNkagxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjIzv + CT074+c6NuP/PTrj/yUe3/8mHuD/Jh7g/yUe4OMlHt8hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAEpI5RZiY+loX2HowV5f6PdZW+j/V13o/1VZ5/9UVef/RkPl/ygh4P8lHt//Jh7g + /yUe3/8mHuD/Jh7f/yYe3/8lIOD/JSzl/yM76v8jRe//d4/z/0BAQP8BAgb/HzahtXSBv3uNl8NWAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhITuAqSn9MDMzfr/qKn0 + /iUd3/8mHt//JR7f7iUe3zcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAASkrlAUA+4yczLuF7JyDg1CUe3/4lHt/+JR7f/yUe3/8lHt/+Jh7f/yUe3/4mHuD/JR7f + /iUe3/8lHt/+Jh7f/yUe3/8lJOH+doDv/5eXl/4RIGT/I0bv/iZJ7v2Mn+7EaoPtNAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH197Vl8e+39Qz/k/yUe4P8lHuDvJR7f + PwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAODTiASsl4DImH9+JJR7f2iUe3/wlHt//Jh7g/yUe3/8lHt//Jh7f/yUe3/8lHt//Jh7f + /yUe3/8mHuD/bmzq/9na5f8kLNj/Iz3r/yNH7/+No/f/V3Xz/ClM768oS+8sAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHRy7AU8NuN6Jx/gvycg4LEsJeA1AAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAJR7fCCUe3zglHt+CJR7fziYe3/omHt//JR7f/yUe3/8lHt//Jh7f/yUe3/8lHt//MCrh + /3h46/8lHt//JB7f/2527f+bqvb/Smny/yNG7/8jRu/zI0XvhiNE7gkAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAACUe3yYlHt90Jh7fwyUe3/cjHN/+Ixvf/yMb3/8jG9/+Ixzf/ygi4P5JRuX/iYvu + /qqw8/+TmfH+Kivj/yQ46f8jRe7+I0bv/yNG78sjQ+0uAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAKCHfAi0n4CNjY+huhIjuwoOE7vaBg+3/hYvu/42V7/+QlvD/eHzs/1lY5/8sJ+H/JR7f + /yYe3/8lJOL/JDfp/yNF7v8jRu/wI0PtbSM/7AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAEA74x4zLeFrJyDgvCQc3/YkHd//JR7f/iYe3/8lHt/+JR7f/yUe3/8lHt/+JR7f + /yUk4f4kN+n/I0Xu/iNF7rAjPOoVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAT03lAUE94xsvKeFmJh/guCUe3/ImHt/+Jh7f/yYe3/8mHt//JR7f/yYe3/8mHt//JSXi + /yM96/4jPutUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAACUe3xolHt9eJR7friYe3+olHt/+JR7f/yUe3/4mHuD/JR7f+CUn45YjNugKAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAACUe3wglHt81JR7fdSUe34MlH99KJSLhFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////////AAAA//// + ////////AAAA///////////5AAAA///////////5AAAA///////////5AAAA///////////5AAAA//// + ///////5AAAA/////////4H5AAAA/////////wD5AAAA////////+AB5AAAA//////A/8AAZAAAA//// + /8Af4AAZAAAA/////4AYwAAZAAAA/////4AAAAAZAAAA////+AAAAAAZAAAA////4AAAAAAZAAAA//// + AAAAAAA5AAAA///+AAAAAAD5AAAA///+AAAAAAH5AAAA////AAAAAAP5AAAA////gAAAAAf5AAAA//// + gAAAAAf5AAAA////wAAAAAP5AAAA///zwAAAAAP5AAAA//+AAAAAAAH5AAAA//8AAAAAAAD5AAAA//+A + AAAAAAA5AAAA//wgAAAAAAA5AAAA//g4AAAAAAA5AAAA//AfAAAAAAA5AAAA/+AfAAAAAAA5AAAA/+AQ + AAAAAAA5AAAA/8AAAAAAAAA5AAAA/8AAAAAAAAA5AAAA/8AAAAAAAAB5AAAA/8AAAAAAAAB5AAAA/4AA + AAAAAAB5AAAA/wAAAAAAAAA5AAAA/gAAAAAAAAA/AAAA/AAAAAAAAAA/AAAA4AAAAAAAAAA/AAAAAAAA + AAAAAAA/AAAAAAAAAAAAAAAfAAAAwAAAAAAAAAAPAAAA8AAAAAAAAAAHAAAA8AAAAAAAAAAPAAAA8AAA + AAAAAAB/AAAA8AAAAAAAAAD/AAAA4AAAAAAAAAH/AAAA4AAAAAAAAAH/AAAA4AAA/gAAAAH/AAAA4Af/ + /8AAAAH/AAAA4A////gAAAH/AAAA4B////4AAAD/AAAA8D/////AAAA/AAAA8H/////4AAAPAAAA//// + ////gAAHAAAA////////4AABAAAA/////////gAAAAAA/////////4AAAAAA//////////gAAAAA//// + //////8DAAAA////////////AAAA////////////AAAA////////////AAAA////////////AAAA//// + ////////AAAA////////////AAAA////////////AAAA////////////AAAA////////////AAAA//// + ////////AAAAKAAAAEAAAACAAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO7u7gbu7u4OAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADu7u4l7u7uUAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7u7u + Ke7u7loAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAO3t7Snt7e1aAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4eLiJ9TV12a6vsBp0dPV + aenp6U7r6+sCAAAAAAAAAAAAAAAAAAAAAAAAAADt7e0p7e3tWgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO7u7gHn5+gB3N7e + L9PW2K2xusD8hJem/7G6wP/c3d7f6urrYevr6wYAAAAAAAAAAAAAAAAAAAAA7e3tKe3t7VoAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AO7u7gHu7u5B5OXmk8fM0LySpLL/VYGi/yRupv9Tf6H/oK65/9na2+Tr6+t17OzsCgAAAAAAAAAAAAAA + AO3t7Snt7e1aAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAABgUlTAICEKUCAg6tAgIOhQgIICYAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAO7u7hft7e2R4OHh0bfAx/53k6r/N3Wl/wpmrP8AY7D/HnCv/1WGq/+aq7b/1NbX + 6+vr66/u7u5CAAAAAAAAAADt7e0p7e3tWgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnJmsICQklqhIQav8YFIf/FBF6/wsKRf8AAAL5FRUc + WQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOrq6gfn5+dm2Nna4qOwu/9chaX/J2+o/wVkrv8CZbH/D222 + /0GMx/9hnMn/dp68/5uruf/Q09b56enqYQAAAAAAAAAA7e3tKe3t7VoAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABzdI8EHBtPsiQgzv9KR9H/hoXX + /7a23P+1tt3/a2yR/wUFBe8mLlQP7OzsQ+jo6Grj4+Ne4+PjBeLi4grg4OBtys3Q7Y6isv5CeaP/E2mq + /wJkr/8EZrL/GXO5/0qSyv+HuN3/nsbl/4u11/95nLj/qbC2+eLj5GEAAAAAAAAAAO3t7Snt7e1aAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACanakgcnN5 + oaOkzP/j4+b/8/X7/8nS9v+ere//j6Hw/6u25v+Ql7f/hoiTq7u7utC7urn+t7a075mYl4ekpKSMtLa3 + 9H+To/85dKL/CGWs/wBksP8GZ7L/Ini9/1qcz/+RvuH/rc/q/6HG4/+Jrcv/kqe4/8THy/nn5+hhAAAA + AAAAAADt7e0p7e3tWgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC2uckBo6az + MLO1wYDKys3P8PDv/dvf7/+UpfD/UW/w/ydK7f9JZu3/jp3m/5Sf1P+8urf/tLGr/7Gtpf+sp57/q6Wc + /6ymnP+nopr/p6Oc/5GPjf9leYf/O2yS/xhopv8MarP/J3y+/2ek0/+cxeT/tdTs/6DG5P+Jrcr/lKm6 + /8XJzPTo6OjR7e3tUAAAAAAAAAAA7e3tKe3t7VoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AJqfszGwsr2Hzc3N2/Hx9P/EzPH/gZXq/0Be5f8hRe7/Ikbv/ypL7v9Oaev/r7fd/7++v/+yrqn/ta+k + /8i/sP/Vyrr/3dPB/+PYxv/j2Mb/3NG//9PJuP/At6r/oJuS/3uAgv9QcIj/OXWk/1KVyf+kyub/utfu + /6bL6P+Fqsb/may7/8nLzvDn6OiX6+vrE+3t7QMAAAAAAAAAAO3t7Snt7e1aAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAKyuuKHp6/T/r733/2uD8P8uUOz/IUXv/yNG7/8jRu//I0bv/yZJ7v+bqe3/ucDf + /7i2tP+xq6P/x76v/97Twf/l2sf/5tvJ/+bbyf/m28n/5tvJ/+bbyf/m28n/5drI/9nPvv+7s6b/jYqG + /2l4g/9nkLD/msTj/5/H5P+Fqsb/oa+6/83P0ern5+eC6enpBwAAAAAAAAAAAAAAAAAAAADt7e0p7e3t + WgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACGj7tRLlHw+yJG7/8jRu//I0bv/yNG7/8jRu//I0bv + /yNG7v8sTe7/v8Xj/8LBwP+xrKT/0sm5/+PYxv/m28n/5tvJ/+bbyf/m28n/5tvJ/+bbyf/m28n/5tvJ + /+bbyf/l2sj/4tjG/8S8rv+OjYj/bXuG/2+dwv9+o77/oq+4/9LT1d/l5eV05+fnAgAAAAAAAAAAAAAA + AAAAAAAAAAAA7e3tKe3t7VoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACpN7aIjRu//I0bv + /yNG7/8jRu//I0bv/yNG7/8wUe7/tb7p/8rKy/+xraj/zsW2/+LYxv/l2sj/5drI/+XayP/l2sj/5drI + /+XayP/l2sj/5drI/+XayP/m28n/5drI/+XayP/g1cT/vbWo/4GCgf9wgpH/n6mw9sfKzKDk5eVX6Onp + BQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOzs7Cns7OxaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAlR+0lI0bv+iNG7/8jRu//K03u/2V97v9yh+7/eo7t/8XK3//BwL3/ubOp/+HWxP/k2cf/5drI + /+XayP/l2sj/5drI/+XayP/l2sj/5drI/+XayP/l2sj/5tvJ/+XayP/l2sj/5drI/9XMvP+oopr/gYCA + /8PExfLGx8lL4OHhAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADs7Owp7OzsWgAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACNG75gjR+//I0bv/yRH7v8tTu7/L1Du/zxa7f+wt9f/v7y4 + /8nAs//k2cf/5drI/+XayP/l2sj/5drI/+XayP/l2sj/5drI/+bbyf/m28n/5tvJ/+bbyf/m28n/5tvJ + /+bbyf/h1sX/w7uv/4OCgf+5ubr/Pz8/6gUEHUYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7e3t + Ke3t7VoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjPusXI0Lt7SNE7v8jRu//I0bv + /yNG7/8wUe3/qK/S/725tP/Vy7z/593N/+bbyv/m28n/5drI/+XayP/l2sj/5drI/+XayP/m28n/5tvJ + /+bbyf/n3Mr/5tvJ/+fcyv/n3cz/593N/9HIu/+JiIb/qKi2/0JBhf8AAAD7CAszTwAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAO3t7Snt7e1aAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoM+YdKTTmaygx5ZQpMuV7KTHlWyYv5TgkMeYYJDPn + Aicz5kElI+H3Izvl/yFB4v8jR/D/MFHt/6ev0f++urT/2dHD/+/p4P/u5tz/6uHT/+fdzP/m3Mr/5tvK + /+bbyf/m28n/5tvJ/+fcyv/n3Mr/59zK/+fdzP/q4dP/7ujd/+7n3P/VzL//jIuJ/6WksP9MR8X/IyLI + /x45xr0dNMUkAAAAAAAAAAAAAAAAAAAAAAAAAADt7e0p7e3tWgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAkMeaIJSPh+zAs4f9OUej/UFLo + /yYf3/8mHt//JSDg/iUl4vAkLOXQKi/k4SYqyv8ZH6T/Gyep/y1Cy/+ts9T/wr+6/9TNwv/y7uf/9fHr + //Pu6P/w6+P/7ujd/+zk2f/q4tT/6d/R/+nf0P/r4dP/7OPW/+3l2P/v6d//8+7m//Tw6v/u6N//zcW5 + /4uLif+0s8H/U03a/yYe3/8jO+v/I0bv8CxN6TIAAAAAAAAAAAAAAAAAAAAA7e3tKe3t7VoAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGxqn + HiQe1JAmHuDyJh7f/yYe4P8lHt//Jh7f/yUe3/8mHuD/JR7f/2dt7f+UoPf/gozz/2py8P9bZeX/t7nX + /8jFwv/Lxr3/7+rh//fz7//49fH/9/Tx//bz7v/18e3/9fDr//Tv6f/08On/9vHr//bx7P/28u3/9/Pw + //j08f/18ev/5t/W/7+5r/+QkI//xMTU/1ZR4P8mHt//JDPn/yNG7/8kR+7OYnjdFX6JvRsAAAAAAAAA + AO3t7Snt7e1aAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAsKQQwEBBhjBgcqWBEQagQAAAAAIh3EESUe3XcmHuDiJR7f/yUe3/8lHt//Jh7f/yUe3/8oIuD/QUDl + /1RX6f9ma+3/g4vw/9LV5//Ozcz/wb23/+Pc0P/08Or/+PXz//n29P/59vT/+fb0//n29P/59vT/+vf0 + //r39f/69/X/+vf1//r29P/49fL/8ezk/9bNw/+ppaH/np2k/29r1P82L+D/JR7f/yQ06P8jRu//J0vw + /pKYscc/RWMmAAAAAAAAAADt7e0p7e3tWgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAABEQXRgNDUjWEhFp/wMDDv8CAhK7DwxdBgAAAAAAAAAAJR7fBiUe31olHt/KJh7f + /yUe3/8mHuD/JR7f/yYe3/8mHt//JR7f/ygg3/9qbOf/zc7f/8bEwf/Mxrz/6ePZ//f08f/6+Pb/+vj2 + //r49v/6+Pb/+vj2//r49v/7+ff/+/n3//v59//6+Pb/9O/p/+DZzv+5s6z/nZyb/8fG0P9MR97/JR7f + /yUg4P8jQu3/Ikbv/5uq5PwUFBT3MDVIGAAAAAAAAAAA7u7uKe7u7loAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwbdQQTFF3BGhah/yQd5f8iHNT/CAgs/wUEH2wAAAAAAAAA + AAAAAAAAAAAAAAAAACQo4zwlIOCrJR3f+T462/9AO9//ubjq/8XD6v9dWeH/lJbn/9vd7f/e3t3/w8K+ + /9DKv//l3tL/8+/q//j39P/5+Pf/+fj3//r59//6+fj/+vn4//r59v/49fL/8Ozj/+Lazv+9t67/paSj + /8PDxf9STdf/KiPf/yUf4P8jO+v/I0bv/4CX9v9paWn/BgYG+jw+RQoAAAAAAAAAAO3t7Snt7e1aAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeH1l5Li24/yUd6P8lHef/JR3n + /x8avf8FByGjAAAAAAAAAAAAAAAAI0DsFiM/7EwjQe2CI0LtsyM/7PGSms//XVzb/87N7P/b2uz/f33d + /7y92f9/fef/vL/q/9ra2v/IxsT/yMO9/9nRxv/o4df/7uri//Pw6//29PD/9vTv//Lv6f/u6eH/5NzQ + /8/Ivv+2sqz/ra2r/729w/+PksX/KDKl/xgbkf8cJLD/HzrU/yFF7//P1er/CQkJ/zAwMPQyMjMBAAAA + AAAAAADt7e0p7e3tWgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcHGISOTuM + 8yIb2P8WFIX/IRvP/yUd5/8lHuj/DRNJsR46z1shROebI0bv2CNG7/0jRu//I0bv/yNG7/8jRu//coXZ + /5yl0//N0O3/29rs/2Nf4v/P0Nb/KiPf/zw24P+YleT/3Nvh/9PS0v/Fw7//ysa//9LMw//X0Mb/29TI + /9zUyP/VzsT/zce//766tP+2tLH/wcC//9DQ1/95jOf/M1Pu/yNG7/8jR/D/IT7c/xwstP8pMZ7/np+k + /wAAAP9nZ2frAAAAAAAAAAAAAAAA7e3tKe3t7VoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAEBBGaS4tof8cGK//Fxd8/yIc2P8kHef/JC7r/yNE5/4jRu//I0bv/yNG7/8jRu//I0bv + /yNG7/8jRu//I0bv/1Ru4f+osc//r7rr/6+57v9LXer/wMTV/z9A3f8lIuD/ODXh/19d4/98fuP/2Njf + /9DQ1P/IyMz/v8DD/7q5vP+4t7n/vb3A/8HBxv/IyM3/1NTZ/6Oh1v9dWOL/Ljzn/yNG7/8jRu//I0bv + /yNG7/8jPuz/Kj3Z/xQUFv8AAAD/o6OjzAAAAAAAAAAAAAAAAO7u7inu7u5aAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwMQageHor/IRvQ/x8ZvP8kKOP/Iz7t/yNH7/8jRu//I0bv + /yNG7/8jRu//Ikbv/yZJ7/8jRu//NVbw/yZK7/98ku//iZjX/8vQ5P+yvO3/gJXy/6Wv3f9hed//Ikfv + /yNH7/8jR+//LVDu/1Nt7f9Qa+v/T2np/01n5/9LZub/S2bm/0xn5v9OaOj/T2nn/0xLtP9BOuH/JR/f + /yM+6/8jRu//I0bv/yNC7f8lJOL/JR3f/yUe2/8DAw7/AgIC/5GQmHQAAAAAAAAAAAAAAADt7e0p7e3t + WgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAICC7OFRV+/yQx3P8jQOL/I0fv + /yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yJG7/8+YPH/N1nx/1Ny8/9DYvH/dIz1/5ij0/+5weP/wcrt + /3KK8f+YpuX/hJTV/0Rl8v82WvH/WHbz/ylM7/8nSu//Tm3y/ylN7/8hRe//Wnfz/zZY8P8oTPD/Q2Ty + /x4zw/8jHc//Jh7f/yQv5v8jRu//I0bv/yND7v8lJeL/JR7f/yEbx/8rKLv/CQks/w8PJPtZWZAWAAAA + AAAAAAAAAAAA7e3tKe3t7VoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaMKwDEiFw + 4B861P8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jR+//I0Tu/0Ff8P9of/L/SGHv + /0dn8v+WotH/bIPr/6233P9ogvL/fJHr/6mwy/9Tc/T/Ikbv/yFG7/9ee/T/ZoP0/2uG9P9AYvL/JEjv + /zhc8f9MbPL/ZYL0/0Vl8v8dKLb/Jh7g/yUe3/8jQOz/I0fv/yM+6/8lI+H/Jh7f/yIczP9iY6T/6urq + /8DCzf83Na//OTrIbgAAAAAAAAAAAAAAAO3t7Sft7e1UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAgQuEcIkTpwSNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8jRu//Ikbv/yM86/8kLuX/JSPh + /yUe3/8uKeD/gIXt/y0o4P+xtfT/w8bY/zs63P+gotP/Xmvl/yMt5f+vssj/TVzq/zhM6/9jfPL/cYfz + /2d27/9IXO3/Mkjr/5Wj9f9LW+z/OEnr/zJI6/8kQe3/HiK4/yYe3/8lH+D/I0bv/yQy5/8mHt//Jh7f + /yYe3/8qKKj/4OHl/+np6f/p6en/0dHf/zNN47UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAiRe4zI0bv5iNG7/8jRu//I0bv/yNG7/8jRu//I0bv/yNG7/8iRu//TWPK + /4KIvP9TUcL/Jh7g/yYe3/8lHt//MjDh/zIx4f81MuL/Qz7k/4KD2v9hXtX/ODTc/5SX0P8kHd//iorN + /15f2/8oIeD/aWnp/zw54/8lHuD/JR7g/yUe3/8kHN//JR7f/yUe3/8mHt//Jh7h/x8atP8mHt//JR7f + /yUj4f8mHt//Jh7f/yYe4P8mHuH/HBtd/35+fv93d3f/lZWV/9bV1f9kfe33XnDgDAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAyTOctI0bv7yNG7/8jRu//I0bv/yNG7/8jRu//I0bv + /yNG7/8lOOX/cne3/7a3xf+ioq3/RkPE/yYe3/8mHuD/JR7f/zIu4f9PTub/JR7f/yYe3/9CPtv/hYTP + /yUd3/+Iic7/ODTc/2Ri1P+Jj9b/KCLg/yUe3/9QUeb/JR7f/yYe4P8lHt//Jh7f/yUe3/8lHt//JR7f + /yYe4f8eGrP/Jh7f/yUe3/87ROj/NDjl/yYe3/8lHt//Jh/g/woKOf8AAAD/EBAQ/zk5Ov9mZmb/fpHr + /1dt1S4AAAAAAAAAAAAAAAAAAAAAAAAAAEhFzBFDQMtHXmHZb19j3I9ERdGGO0/jxylE5v8iQur/IUXu + /yNH7/8jRu//I0Tu/yQ16P8mJN3/e3u1/7a3xv+2t8b/tbbC/zMuzP8lHt//JR7f/yYe3/8lHt//JR7f + /yUe3/8mHt//KSPf/6Smyf8jHN//Pjvb/4SEzv89ONv/nqDN/5WX8P+GiO3/VVTm/yYe4P8lHt//JR7f + /yYe4P8lHt//JR7f/yUe3/8mHuH/Hhmv/yUe3/8lHd//dH/x/z9B5v8kHd//SErm/yYe4f8NDEr/GBgY + /9zc3P/q6ur/hYWG/1Rmuf9LX8szAAAAAAAAAAAAAAAAAAAAAAAAAAA7NshUU1HQ84qO5v+Xmer/PTvY + /0xM4f9XWeL/XWHg/2Zw4/9aaeX/JCre/yUf4P8lHt//dXW2/7a3xv+2t8b/trfG/7e4x/9xcrT/PySV + /0Ykgv8+Ipn/MCDB/yYe3/8lHuD/JR7f/yQc4P+nqcn/Kybe/yUd3/+Pkc3/NTHd/6qsyP8kHeD/JiDf + /yUe3/8lHt//JR7f/yUe3/8lHt//JR7f/yUe3/8lHt//JR7g/x0ZrP8mHuH/JR7f/0lQ6f9UWur/JBzf + /3J77/8lHt//FxWF/4WFhv/p6en/6+rq/z4+Pv9LXr//Nz/VXQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AIKD4gmbnOw9mZ3xfjk4484lIeH/JSDg/yYe4P8kHd7/JiDc/yUe3/8mHt//QT7G/7W2xP+2t8b/tbbF + /52bpf94Vlf/ZjEt/2UoI/9lKCT/ZSgj/2YoIf9hJzH/TCVw/zEhvv8lHuH/i4vO/0pH2f8mHt//RkPa + /39/0P+TlMz/ODTc/yUe3/8mHuD/JR7f/yUe4P8mHt//Jh7f/yUe3/8lHt//Jh7f/yYe3/8eGbD/JR7c + /yUe3/8kL+b/JC3l/yUd3/99h/L/Pz3l/yMd0v+ur8b/tLS0/1NTU/8sLDD/LkXU/1pe6v9PU+i4NjPj + DgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAxQemIJC/m/yUe4P8mHuD/Jh7f/yUe3/8lHt//JR7f + /2ppv/+2t8b/trfG/5aWn/9rPDn/ZSgk/2UoJP9lKCT/ZSgk/2UoI/9mJyH/Zigi/2YnIf9mJyD/VSVU + /3lsrf9sa8n/IxzN/yQd3f+Wl8z/eHnR/1xa1/8lHuD/Jh7g/yYe3/8lHt//Jh7g/yUe3/8lHt//JR7f + /yYe3/8mHt//IxzN/yAbwP8lHt//JSrk/yNB7P8lH+D/fIbx/3mC8f8mIOD/lZjB/0lLXf8aGyr/QU2s + /yQ26N4lHt/6Lyrh/TUx4jYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5OuQHLzLk9Dw65P8lHd//JR7f + /yYe3/8lHt//JR7f/yUe3/93d73/trfG/7GxwP+BdHf/ZSgj/2UoJP9lKCT/ZSgk/2UoI/9INX7/KkPb + /1Ro5P93geD/eH/Z/3Z4zv9CUsz/PljZ/x41xf8tPb//UV/D/3B+xv9lbbn/GRma/xsXof8eGbP/IhzL + /yUe3v8mHuD/JR7f/yUe3/8lHt//Jh7f/yYe4P8eGbD/JR7g/yUf4P8jRO7/I0Pu/2p+8/+UoPf/ZGbq + /3Z2d/8QGV3/IULo/yNF7/4pM9M2LTLbCy0r4RQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATUzn + IiUd3/9BQOX/Ymjs/1xi6/9XWur/Vlrq/1FS6P9MTuj/hIbA/7a3xv+srbv/gW1x/2UoI/9lKCT/ZSgk + /2UoJP9lKCT/Ti5q/ycz3v8wP+n/QE3r/0NR6/9DUuv/KDzq/yQ66v8sR+z/Y3jz/3uN9f+Nmvb/i5n3 + /3qM9v9lee3/UGLb/zdIxf8hMLf/HSW7/yAfv/8hG8j/JB3Z/yYe3/8mHuD/IBu//x0YrP8mHuD/JC7l + /yNG7/8rTe//R2Dw/4mU4P8KCgr/DRpU/yNH7/8kNOSkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAHl37TyYmfL/t7n3/zAr4f8kHd//JB3f/ywn4f9OT+j/XmLr/3V5yf+mprP/trfG + /5KRmf9uQT//ZScj/2UnI/9lJyP/ZScj/2UnI/9iKC3/SzGG/zMu2f8lHd//Jh7f/yYe3/8mHt//Jh7f + /yYf3/8kI+H/JCvk/y476P8/Uez/UWjx/2J68/92ifX/iJf2/4KS9v9ofvX/c4fs/2Jz0v8sO7j/GR6Z + /xQTev8UEnn/Gxef/yUe3v8kNej/I0bv/yRI7/9zd4X/AAAA/w0WRv8jM9WzKjzFCQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACfoPNMq6z1/3p57f8sJuD/JR7f/yYe3/8lHt//JR7f + /ycg3/94ge7/n6K4/7a3xv+qq7j/mpqk/6Keqf+ZkJr/l4yU/5iNlv+bk53/nZyn/Judpt2KiaWsSEa7 + ficg3rclHt/7JR7f/yYe4P8lHt//JR7f/yUe3/8lHt//JR7f/yUe3/8lI+H/JCzl/yM16P8sSO3/Q2Hx + /3+T9v+yvfn/k6D3/2+E9f9NZen/HzK9/xYaiv8YFZD/JB3T/yUp5P9bbe//KSgo/wAAAP8vLzvrMjdy + AwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYmDpVEpG5f9hYOn/JR/g + /yYe4P8mHuD/JR7f/yYe3/89O+OLeX7nTIqQ1mOSk6KXoqKt6Kytu/2nqLT1m5ym1pSSmqSJhIpxgHZ6 + P35yehIAAAAAAAAAAAAAAAAAAAAAJh/dHyUe33wmHt/ZJh7f/yYe3/8mHt//Jh7g/yUe3/8mHuD/Jh7f + /yYe3/8mHt//Jh7f/yQh4P9/ivH/bH/x/ypI7v9Qa/L/e4z1/3WI9P8iRu//HzfN/xkhnP8hH7ayhobQ + 2wAAAP8AAAD/VFRV9WRmdRMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AKOk82LR0fv/wcL4/ykj4P8mHt//JR7f/yUe3/8nIOC9OzfiAwAAAAAAAAAAAAAAAJydqwGTlJwNlpef + BQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJR7fASUe3zklHt+XJh7f + 7SQd3/8kHN//JB3f/yQd3/8lHt//Jh/g/zcy4v9pbOr/dXns/1ZV5/8mHuD/JSbi/yMy5/8nRO3/Ikbv + /yNG7/8jR/D/IkTq14mQsOIAAAD/AAAA+3N0eIOKjZ05AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAB5ee1mRkPl/y8r4f8lHt//Jh7g/yYe3/8lHt/cJh/fFAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAC0n4AhWWOdScnfrr2lu6vduc+v/aXHr/2px6v9laOn/WFno/zAr4f8lHt//Jh7g + /yUe4P8mHt//Jh7f/yUl4v8kNOj/I0Pt/yFF7/+NlrX/AAAA/w4YSNdodbNRkpq/YwAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf4HuP8HC+P/Nz/r/KSPg/yYe3/8mHuDqJR7f + JgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABEQ+QSNTHiZikj4MElHt/8Jh7f + /yUe3/8lHt//Jh7f/yUe3/8lHt//Jh7g/yUe3/8lHt//Jh7f/yUf3/8jKeT/y9H0/xodKv8hQt7/JEfu + /Iaa6sJogespAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHZ27AJ8e+3UUU7m + /yYe3/8lHt/rJR7fMgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAALCbgHyYf33YlHt/NJh7f/SUe3/8lHt//JR7f/yYe3/8lHt//JR7f/yYe3/8lHt//JB3f + /8XG9v9kZ9H/JDXo/yFD7v+FnPf/UW/z+ypN76AlR+4dAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAWFTnIjIs4YQsJeCJLSbgIwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACUe3yIlHt9sJR7fuyYe3/klHt//JR7f + /yUe3/8lHt//Jh7f/yUe3/9LR+X/My7h/yQd3/9vcev/oKz1/0Ff8P8jRu//I0bv8yNE7m0jRO4BAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAlHt8TJh7fYSYf37QvKeH3Ni/i/zUu4v81MOL/RULk/2hp6f+covH/qbDz/3h77P8lIOD/JC/m + /yNC7f8jRu//I0bvtiNB7RkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAV1XmEHJ161xoaeqvY2Pp9Vtc6P9RUeb/RUPl + /ywm4P8lHt//JR7f/yYe3/8lH+D/JC7l/yNC7f8jRu/qI0HtUQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AE5M5Q49OeNXLSbgrCUe3/MmHt//Jh7f/yUe3/8mHt//Jh7f/yUe3/8lH+D/JC/m/yNE7v4jPuxWAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnId8LJR7fUiUe36UmHt/uJh7f/yYe3/8lHt//Jh7f + /yYe4P8lJOLmIzvqKwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJR7f + BiUe3zklHt97JR7frCUe35clIOBUJSbiEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////// + //////////////////////n/////////+f/////////5//////////n////////A+f///////gB5//// + ///8ADn/////B/gAGf////wD8AAZ////+AAAABn////wAAAAGf///wAAAAAZ///8AAAAABn///gAAAAA + ef//+AAAAAD5///8AAAAAfn///wAAAAD+f///gAAAAP5///+AAAAAfn//wAAAAAA+f/+AAAAAAB5//4A + AAAAABn/8IAAAAAAGf/gYAAAAAAZ/8B8AAAAABn/wHAAAAAAGf+AAAAAAAA5/4AAAAAAADn/gAAAAAAA + Of+AAAAAAAA5/wAAAAAAADn+AAAAAAAAP/wAAAAAAAAf+AAAAAAAAB8AAAAAAAAAHwAAAAAAAAAfgAAA + AAAAAAfwAAAAAAAAB+AAAAAAAAAP4AAAAAAAAH/gAAAAAAAAf+AAAAAAAAD/4AAB4AAAAP/gDj/4AAAA + /+Af//8AAAD/4D///+AAAH/gf////AAAH/D/////gAAH///////wAAP///////4AAf///////8AA//// + ////+AD/////////Af////////////////////////////////////////////////////////////// + /////////////////////////////////ygAAAAwAAAAYAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAA7e3tBO3t7QEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7e3tSu3t7QsAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7e3tWO3t7Q0AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAA6OjoCdDS0yDJy80g5+foGuvr6wMAAAAAAAAAAAAAAAAAAAAA7e3t + WO3t7Q0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO3t7QPd3t8W1tjahqy3vv2ZqLT91tja4uTk5Vfs7OwCAAAA + AAAAAAAAAAAA7OzsWOzs7A0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADu7u4B7u7uFezs7HjIzNDDjKCy9jR0pf8lb6f/eZWs + /8zQ0+jr6+ti7e3tDgAAAAAAAAAA7OzsWOzs7A0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAjUAACvHAAA28QAAGecAAACRAAAABwAAAAAAAAAAAAAAAAAAAADr6+sY5eXmxLjAxvthiqr/Im+q + /wNlsP8Tb7b/Soq7/4Ogtv/IzND56urqm+zs7AYAAAAA7OzsWOzs7A0AAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAFOCEdhvBiXN//mZXp/6im3/9cW2n/FBMSf+vr60Xl5eVN4+PjCeLi4iHY2dqunK26 + +kN7pv8NZ6z/A2Wx/x52u/9Zm8//lMDh/4aw0f+KobT/1dbZoubm5wYAAAAA7OzsWezs7A0AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAIOBfB+dm49nqaiv5sXK+P+9yfP/mavx/6Sx6P+gqdX/i4+f7rW0se2xsKz2nZyZ + qaurqsSLmKP+PXWg/wdlrf8HaLP/JHq9/3Or1/+iyOb/ocXi/4usxv+suMH/4eLjounq6gYAAAAA7Ozs + WOzs7A0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAcXFvAYiGfyqsqZ5wyMjHxcXM5/ajsvP/Z4H3/zxc8f9of+n/rrTR/62ts/+4sqj/wrut + /8e+sP/JwLL/wrqs/7Wuo/+KjYz/W3WI/yprnf8vf7z/dq7Y/7DR6/+myeT/iavF/6+7xPjg4eKm7Ozs + Quzs7AIAAAAA7OzsWOzs7A0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAB4dnAIubm4trnD7/6NoPr/U3D2/yZJ8P8XPO7/N1fu/5yp5/+7vMP/tK+n + /8vCtP/e1ML/49jG/+XayP/m28n/5drI/+LXxv/Vy7r/raid/3d9gf9fh6b/krzb/6PH4/+Nrcb/tr/E + 9d7f4J3q6uoWAAAAAAAAAAAAAAAA7OzsWOzs7A0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYnXPlh5E9/8WO+//G0Dv/yFF8P8pS+7/bILt + /8jHxv+3sqr/2s+//+XayP/l2sj/5drI/+bbyf/m28n/5tvJ/+bbyf/m28n/4tfF/7y0p/97gYT/bJKx + /4iluv+6wMX14+Tkfebm5goAAAAAAAAAAAAAAAAAAAAA7OzsWOzs7A0AAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH0LuFSJF7+MjRu//MlTu + /0hl7v9je+3/ys7e/7Sxq//Tyrv/5NnH/+XayP/l2sj/5drI/+XayP/m28n/5tvJ/+bbyf/m28n/5tvJ + /+LXxv+nopr/e3+C/7i7vtnR0tND6erqCAAAAAAAAAAAAAAAAAAAAAAAAAAA7OzsWOzs7A0AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + ACNG73gjSO//MFPv/0Nh7v9eduz/vL/P/8K7sf/f1MP/5drI/+TZyP/k2cf/5NnH/+XayP/l2sj/5tvJ + /+bbyf/m28n/5tvJ/+bbyf/Mw7X/i4mG/6ussvwkJCSyAAAAGgAAAAAAAAAAAAAAAAAAAAAAAAAA7Ozs + WOzs7A0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIkXv + CiFD7gojR+8BAAAAACNK8QUjP+zAIz/t/yNI8v9BX+7/rrLF/8/Huf/l28v/59zL/+bbyf/l2sj/5drI + /+XayP/l2sj/5tvJ/+bbyf/m28n/59zL/+jezv/c08T/l5SQ/5ubv/8qKH//BQkdxQsXTwIAAAAAAAAA + AAAAAAAAAAAA7OzsWOzs7A0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + ACRG9SwjMuexNTvm5kBG6OAnK+TKJDDmqiQ46nUlMeh+Jifc+xkou/84Tc3/r7PG/9HKvv/s59//8Ong + /+zj1v/p39D/6N7O/+fcy//m3Mv/593M/+jezv/q4NH/7eXZ//Hs5P/f18v/mZaR/5ybwP85Md7/JS3h + /yNK+LAhSf8PAAAAAAAAAAAAAAAA7OzsWOzs7A0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAACEq0CwoHOy5KiLh/isk4P8nHt//Jhzf/yUb3/8nIOD+b3jv/nR+6f90e9r/wMHQ + /8rFvP/r5t7/9vPv//bz7v/08Or/8u3m//Hq4v/w6eD/8uvi//Pt5f/17+r/9vLu//Tv6f/TzML/lpSR + /6+u1v87M+D/JiTk/yJF8P8jR/OUz9XxE+Ti3QEAAAAA7OzsWOzs7A0AAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAACAghIAQECpQMCEj4AAAAAJx7oKSUc35MmHt/vJh7f/yYe3/8lHeD/NDDk + /0pJ6P9wcev/zs/h/8K/uv/f2M7/9fLt//n28//59vT/+fb0//n29P/59vT/+ff0//r39P/69vT/+PTx + /+3n3f+6ta7/o6Gg/2lk3P8uJd//JiXi/xk+8f9hfPb5RkZEjAAAAAAAAAAA7OzsWOzs7A0AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUFEksXFYf8HBip/wYGH/kAAAA1AAAAAAAAAAAmIuEOJh/g + aCYd38wpH97/QDrg/1pU4/8yKd7/fXvk/9PT0f/Kxr//5uDW//by7v/59/X/+vj3//r49//6+ff/+vn3 + //r59//49vP/7unh/8vFu/+op6X/rqzO/zcu3v8mH+X/Hzft/0ls//93e4j/BQQAkAAAAAAAAAAA7Ozs + WOzs7A0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQYFGykpkeUkHeb/KB/7/x8avf8AAAB7AAAA + ACVK+gMjRe8fJEHtSSI67JAyReb0sLHc/+rp7f9eV+T/r67c/8fH5v/Pz9L/ysa//9zVzP/w6uL/9vPu + //n39f/5+Pb/+Pby//Tw6f/l3tT/x8K4/62rqP+ztMT/UVi7/x0hr/8eJrv/GDjc/5yr8P8jIRb/dXV1 + ggAAAAAAAAAA7OzsWOzs7A0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGRo+iC0pz/8YFJL/JBve + /yUb4v8XJ5C4I0n1mSNH8NQjR/D4I0jw/yJH8P8fRvD/vMPc/+7u7v9WXOn/lZXY/2pm2P+Mh+T/1NTc + /8vJyP/Lx8D/1M/G/9nTyf/b1Mn/1c/G/8rGvv+7uLT/wL/A/7e30/9cbuT/Jkzy/yNH7P8fPtf/IzjF + /3l9mf8AAAD/wMDAdwAAAAAAAAAA7OzsWOzs7A0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACGBhi + 1R4Ytv8bFqH/JSXo/yQ87v8kSff/I0fw/yNG7/8kR+//I0fv/yRH7/8rTvL/q7Xf/8nP5/9Na/L/dIfk + /3iD1f8kN+r/SFXo/5ih5v+yuN3/q7LV/6Wrzv+fpsn/o6rN/6mx1P+zutv/ionP/0E35P8mOuz/I0nw + /yNI8P8lN+7/Jiry/wcHN/8XFwz/7+/tSgAAAAAAAAAA7OzsWOzs7A0AAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAWDw1U7iEozP8kOuv/I0fu/yNJ8P8jR+//I0bv/yNH8P8mS/D/Nlvy/zxf8v9Vdfb/rbjp + /9jb6f+aq+z/aoLu/4eX1P80WvP/PWHy/z1f8f8+XvD/P2Dv/ypN7f9KaPD/OVrv/z5g8/8oOsn/Kh/T + /yYo5f8jSPD/I0nw/yUx5/8iGN3/KyPN/xcWVP8pKEPdsaz9CQAAAAAAAAAA7OzsWOzs7A0AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAVKo5DHDS4/SNI8f8jSfL/I0fv/yNH7/8fRPL/HULz/yNE7/8iPez/K0Dq + /2h58P9KXu//kKLn/5Wf4P+lseH/XXfw/4OR0/9ZdfD/MlXx/1Bx8/92kvb/RWby/0dq8/9bevT/UHHz + /1By8/8aIr//Jhrf/yQ66v8jR+//JSvk/yQa4v8pJL//wcLS/+Dh2f9RVMP+ExHjNAAAAAAAAAAA7Ozs + Uezs7AwAAAAAAAAAAAAAAAAAAAAAAAAAACRJ+GclSv35JEn3/yNH7/8jR+//I0jw/xxB8v82VOD/TFvT + /ygm3v8lHeD/Jx/f/0pJ5f9GQuX/p6jp/25s1f9tbdj/TU7g/2xr0/9bXd3/YGbt/3B47f86O+X/Jyrj + /05P5/85OuX/KCnk/yYt3/8gH8H/Jx7i/yIt5v8kIuL/Jhzf/x8X3/9HRpP/xse7/8jHwv/R1uf/LU3u + cwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH0XxdR9F8P8gRPD/Ikbw/yNI8P8jSPH/Gzfu + /1Jgzf+tr7f/jImx/yMc3v8jHOP/KyXh/z484/8lHuD/KiPd/3h30f80L9z/cG7T/1NP1/9+gNj/JRzi + /0pI5f8mHt//JRzf/yEY3v8jGt//JRzg/yUb2f8hG8D/JBzi/zg55f80MuP/JBvg/yUe4v8JCTf/BgYA + /zExMP+amZb/VXP5pgAAAAAAAAAAAAAAAAAAAAAiHL5NNDLHpmFl27pDRNGxRFfk8TxV6P84Vu3/KUnt + /yM87P8eJef/V1XF/7m4v//Dxcn/hIbI/yAa4P8oH9r/JB3k/x8b6/8jHef/IRrh/3t60f8qJN//aWjU + /1lX1f+Rk9T/dHXt/1BO5v8kHN//Jh7g/yYe3/8mHuD/Jh7g/yUe2/8gGrr/IBfg/1de7P9OUOn/NzTk + /zAr6v8CAED/gYJ5//////94d3P/NU/TrwAAAAAAAAAAAAAAAAAAAAA9Os4+s7Tzo8LH+dJGROP2Lyre + /zgz3f9JSd//Ojnd/x8W4f9AOs3/tba+/7y+y/+ur7z/jn2F/14xTv9cJTv/VyZP/0gjff80ILT/HBTj + /3Jy2f9IQ9v/NjDg/3d30/98e9D/LCXf/yMb4P8mHuD/Jh7g/yYe4P8mHt//Jh7g/yYe4f8fGrn/JBvd + /y805v8xN+X/TEvn/09Q8P8dGKH/1NXR/7e2rf85OkD/PEve8j87524mG98EAAAAAAAAAAAAAAAAAAAA + AAAAAAAiNeidHx/i/yEX3/8gF9//Ixrf/x0V4f91dL//wsTI/6Wms/9vR0b/YR8Z/2cnHf9oKB7/aCkf + /2YrI/9lKyn/TR9Y/3Nipv9mZcv/EQnM/2Rjy/+QkMn/NzDU/yAW3f8lHeL/Jh7j/yYe4v8mH+D/Jh7g + /ycf4/8hHMT/JBzU/yMn5f8cMen/TEzn/4KK8/9JROz/g4Sb/w8QHv8zRKP8Ji3q5jw45P8sJeEzAAAA + AAAAAAAAAAAAAAAAABoS3g8oKOLmPDvl/zQv4/8wK+L/LSfi/yMc3/+Fhr7/v8LK/4h9gv9hIRz/ZSgj + /2YoIv9lKCf/MTm7/0NZ6P93ftr/fH7L/1Nhzv8vTt3/MEjU/2Vz2f+Hltv/TFXC/ygssf8iIrP/HBrB + /x0X0P8gGNj/IBfe/yQb4/8lHtv/IhvE/ycg5v8gPu3/NlXw/36M+P+FiOX/MTM3/xo5zv8kPfTSJR7g + ESMb3xkmHt8DAAAAAAAAAAAAAAAAAAAAAExH5ihlYer9amvr/0RF5v9HSOb/WFrq/2Rr8P+KjsT/uLrC + /5CKkf9lLSj/YB4Z/2EgGv9iIRn/UCRU/zovuf8wM+//LC7t/yYq6P8gJOb/MTrr/0VR7/9UY/L/Znj0 + /26D8/9tg+3/ZHnk/1Nn3v9CUNT/R0zO/y4wyf8XFbb/Eg6D/x4Wrf8mKOn/IUbx/zJY/v9lbZD/AAAC + /xgy2vMdGuk/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALK09jWmp/X/XVnp/xwT3v8iGt//Ixvf + /yol5P91e93/r7C8/7O1w/+ak5z/loGJ/5F6gP+Ndnz/jnd0+o6Dgtx0c52qLyrLmiMa4ukmHd//JBvf + /yEY3v8fGN//IRvf/yQk4v8rM+f/N0Xs/0pe8v9XcPX/n7L6/5Ck9P9ccOH/OUm//xYekv8cFaL/Hx/a + /0JU7/8xMzP/AQEB/2tpu1MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF9h6TyEg+//Wlfo + /yAY3/8mHuD/Ixvf/zIu4pGTn/0ujZTJTY2Mj5ycnqfGlpqjrYmNk3d+goVKdHd5J2xubQgAAAAAAAAA + ACIa5hUmH+BcJh/guyYf4PclHOD/IBje/yEY3v8gFt7/HxTe/xsS3v88QOb/e4fw/0dW7f9KYPP/bIT8 + /zFV8v8cOc3/GCK92W5tz7kREAf/FRUU/MbFu1UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AJWY8kaWlvL/UE3n/yEZ3/8mHuD/JR3gvCgg3wUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQd4CIwK+F7T0/mzU5N5vxNTeb/TE3m/1dZ6P9xc+v/S0jl + /yIZ3/8fGuD/IiXj/yU26f8kQ/L/H0n7+HGDyPEGBAD/Jy5LncjIx1wAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAFdc6DKfoPP/bGrr/x8X3/8mH+DWJR7gFwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgoXuAUhE5TcuKOGKMS3i + 3TEs4v8mH9//IRnf/yYe4P8mHt//Jh3f/yYc3/8mH+D/GyPn/5Ka4P8yOEr/GT/l8IaZ58FOa+4kAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABiYOquPDfj/yMc39MmH+AhAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAJB3gCiQd4EMlHuCXJh7g4SYf4P4mHuD/Jh/g/yUe4P8kHN//GQ/d/4eE6/9cXt3/EiPq + /4GZ+P9EZ/T1HkPvkyNH7xMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdFt8KIhvfKiYf4A8AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh/gCSYf4DgmH+CMJh7g0yYe3/0uJ+H/Mivh + /zgz4/9LR+j/goHs/6Gm8/8yQOn/ID/t/yNJ8OMjSPBJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHxff + A2Bi6Td9gO2DVlTnz11f6P9pa+r/YWHp/zMt4v8jGd//JiDg/yUw5v8jQu3/I0nwkCNH7xAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAGhLeBSAY3zMeFd57Hhbf0CMb3/smH9//Jh7g/yYc3/8mIOD/JDLn + /yNE7o0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJh/gAyYf4ComH+B4Jh7g + uCYf4OomHt/cJhvfoiQ76ysAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////////////////P////// + ///8//////////z////////4PP///////+Ac////////gAz//////geABP/////8AAAE//////AAAAT/ + ////AAAABP////4AAAAc/////wAAADz/////AAAAfP////+AAAB8////+IAAADz////gAAAAHP///+AA + AAAE////iAAAAAz///8GAAAADP///gQAAAAM///+AAAAAAz///wAAAAADP///AAAAAAM///8AAAAAAz/ + //gAAAAAD///8AAAAAAP//8AAAAAAA///wAAAAAAA///4AAAAAAD///AAAAAAAP//8AAAAAAH///wAAA + AAA////AADAAAD///8B//gAAP///wP//gAAf///h///wAAf//+P///4AA////////8AA////////+AD/ + ////////AP////////////////////////////////////////////////////////////////////// + //////8oAAAAIAAAAEAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADt7e0I7e3t + CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AO3t7R/t7e0iAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0NPWE7/EyEXR1Nc96Ojo + DwAAAAAAAAAA7e3tIe3t7SUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAVAAAchQAAE5gAAAA9AAAAAAAAAAAAAAAA7u7uCOPk5EO0vse0aZCt + /o6muPfb3d+f6enpDgAAAADt7e0h7e3tJQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQRRJpVULX/dXSp/ywsMc8AAAALAAAAAOjo6BHY292llay+ + 8jV4rP4MabH/RIi8/46pvv3Y29246urqHu3t7SHt7e0lAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAhIJ4FZ+dlViprLW0k5/p/4CW9/96kPD/cYTX/6+xusKpqKdVvsHD + nnOWsfUcbaz/Em61/0uSyv+RveD/h63K/73EydLl5eYj7e3tIe3t7SUAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAiIZ7KqqvxbeLnOfxYnz0/z9f9f9ke+n/m6TP/7i0sf+8ta//w7yv + /7u0qvmioZz9W3mQ/yVvp/9ens7/pMnm/5e50/+uvcjz4+TleOzs7BLt7e0h7e3tJQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABXaLYZMVP16Bc+8v8bQe7/hJbr/7e2u//IwLP/4NbE + /+XayP/m28n/5drI/+HWxf/Cuq3/foiN/3+lwv+RtdD/uMLK4+Pj5F/p6ekEAAAAAO3t7SHt7e0lAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAArTe5iPl/v/2yD7f+7vMr/zcS3 + /+PYx//l2sj/5drI/+XayP/l2sj/5tvJ/+XayP/Iv7H/hY2R/5Wao+xaW1t+AAAAAAAAAAAAAAAA7Ozs + Iezs7CUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJk7/BSRE7TcsRuxRJEHsNjFR7hwuROq+XW/f + /7u6vv/d08P/5drI/+XayP/l2sj/5drI/+bbyf/m28n/5tvJ/+HXxv+no53/hoPL/x0bif8NH3teAAAA + AAAAAADt7e0h7e3tJQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdL78XKybw0TUw4/8oIeD/Ix/h + 8zg45uV+hNb/v7y+/+jh1v/s5Nj/6N/P/+fdzP/m3Mv/593M/+jezv/r49X/7OXZ/6+spf9+e8X/LCTo + /xs4+f5aef9R7ezrBe3t7SHt7e0lAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBwYomgMBDJMVEH0LJBvh + TyYc4LMmG9/5Jx7g/2pn4//Ewcf/5uHZ//by7v/18u3/8+/o//Ls5f/z7uf/9fDq//by7f/o4tr/rKim + /4mF2P8rJOL/KEb6/1tqp+4IBwQj7e3tIe3t7SUAAAAAAAAAAAAAAAAAAAAAAAAAABYWT44lH+T/GxSl + /wMHFUMAAAAAJTruISU26pBdY9/5W1be/7m32f/Szsb/7+vl//n39P/6+Pb/+vj2//r49v/6+PX/8Ozm + /8fBuv+fn7X/QkPE/xkmzv9kfen/Ojgv94aGhxvt7e0h7e3tJQAAAAAAAAAAAAAAAAAAAAAFBgoiJiOg + +h0VtP8lIuf/HjrJyyRK9dYiSfD+MFTx/8LK6/+gquD/k5vp/8vL1P/Szcj/5N7X/+/r5v/08ez/8Ozn + /+Pe1v/IxL3/s7K9/2Z34/8jTO3/Hzvi/zE3f/9BQDLs////D+3t7SHt7e0lAAAAAAAAAAAAAAAAAAAA + AAMBCE8dHqj/IzTi/yRG8v8jSfb/Ikfw/ylP8f9EZfH/wMnu/6ez4P9phu//i5zf/5mn5f+/wtX/u73O + /7W3yP+3ucn/t7vM/6ao0f9fXN//Jknw/yQ+7v8lIej/DQpn/1VTcacAAAAA7u7uIe7u7iUAAAAAAAAA + AAAAAAAfQNQFGzWzpSJE5v8kS/P/H0by/x9D7/8lPur/IjDo/1dh6/+ss+3/oqnh/3aD4/9icd3/VWrq + /2uB8P9rfOv/WW7p/2Bz6v9Vauf/MjHM/yQu6f8kNur/HRXf/2hkxv/o6d3/XmTbzBMb5gTt7e0h7e3t + JQAAAAAAAAAAIzPaDSJL+L0hSPr/I0rz/xxB8v84T9//jZS9/zw21v8fF+n/ODXm/09K5P91c9r/XVnY + /2Vi1f9jYNv/Skbn/ygh4P8oIOD/Jh7h/yIb1/8hG8r/Ly7n/y0o4/8gGN7/JSVJ/2ZlW/+apMr8IUv3 + Iuzs7A/t7e0QMy7GWnN03b5GSNvaPk7m/zxS6f8hM+n/ODnX/6qpwv+8v8b/STy0/zQdrf8tH8f/GxTm + /1VT3f9HQtv/aWjV/3l52/9CPuf/JR7g/yUe4P8mH+H/JR7Z/x4XxP9AQer/RUfo/zYz5f8vLWj/2NfF + /2BniP8qPe5FAAAAAAAAAABraeELrbH4MSkw5tciGd7/KSDe/x0S3v+Ih8X/uLq9/3pXWP9pKx//aCgd + /2ItNP9TKGL/YFGs/0VD0f9UUsz/cW/M/xoR2P8iGt//JR3h/yYe4f8mH+H/IxvI/yQl5P8qNuf/am7x + /29t3f9jZWf/Jzaf+zo47NcpIOAcAAAAAAAAAAAnH+AMODjk6kJA5v85NuT/Pz3k/56hyv+dlpn/Xh0W + /2MeE/9WJT7/ND/V/11j4v9NV9b/KD/d/1Nj3f95iN3/TFfL/z5Fy/8wM9D/JiTW/yEa1/8cFcD/IRrI + /yM+8v9cd///YWOP/wsijf8eKfd3IRffCyYe3wMAAAAAAAAAAIqJ8CWIh/D6NjHj/ywm4f9CQen/j5PP + /q6utv+MdHn/g2Bj/39bWf94ZH/lSkm/wCAa4tklHuL/JyPk/ysr5v89Q+z/SVbu/01g7v9jee7/eYnk + /0FPw/8gJZz/HRzD/zRJ8P0iJiv/MjZ9qwAAAAAAAAAAAAAAAAAAAAAAAAAAfH7uLnx77v8rJOH/IRnf + /y4o45CNl/MihYeWV42QlHyDh4tadXl7J3J0awgAAAAAJyDfBCUe4UwlHuCrKCHg7y4n4f8wKuH/MSzi + /2Bk6/9PWO3/RlXy/ztY8v8YNtf7SlPE0AoIAPiYlYhrAAAAAAAAAAAAAAAAAAAAAAAAAABscespiIfw + /y0m4f8jHN+6KCHhBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABRUOYUYWHp + Zz895L5EQ+X5OTTj/yMa3/8iGd//Ix7g/x8o7f9ebtb/KDVk83CE3KJbdegUAAAAAAAAAAAAAAAAAAAA + ADo44wJKReaVJyDgqCQe4A4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAACIa3yMjHOB1Jh/gvyYg4PUjG9//GxDd/09I5P9OUen/boH3/zla8ukfRvBsAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIhrfGERB5GVeXei5R0Xl82ho6/9fXOj/JSHh + /yQ06P8jRe+2I0jwIwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeFt8XIRnf + YyAY37UmHuDuJhzf/yYi4f8kPeuSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAJh/gFCYe4E0mH+CPJhvegyQ06CoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AP/////////8/////P///wz//hwE//4IAP/wAAD/wAAA/8AABP/gABz+AAAM/gAAAPgAAAD4QAAA8AAA + APAAAATgAAAAwAAAAAAAAAMAAAABgAAAAYAAAA+ACAAPg/8AB4f/4AP///wA////gP///+D///////// + ////////////KAAAABgAAAAwAAAAAQAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAA7u7uAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADt7e0Q7e3t + GQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAANna2x7KzM0/6+vrDQAAAADt7e0V7e3tIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAACQgrDAUFHnIDAw5bBgYJAQAAAADr6+wQ3uDiUJSsvtBoka/v0Nba + oufo6CXu7u4V7u7uIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACTlJwedHSm + xIWL5/5qdsP+JC5iTQAAAADb3d97hqS78xturv8sfbz/jq3F+c7T15vu7u4V7u7uIQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAB0dX0Cub3QdZSh3cttg+v9R2Ts/nCD3/6Cjcn/tLG6+rKvrbxtip/+FGqr + /0iRyf+WweL/nLbL+szS1pzt7e0V7e3tIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANVTo + qSNG7/9Pauz/oajS/8K9t//Wzb3/2tC//9XMvP+oqaP/X4CY/4u21/+cuc/8zdPXpeXl5ifu7u4V7u7u + IQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQFfrKTZT7venr9z/ysO4/+XayP/m28n/5tvJ + /+bbyf/k2cf/vLSp/4CSo/6Eh6T0Oz9iPgAAAADu7u4V7u7uIQAAAAAAAAAAAAAAAAAAAAAAAAAAHCOz + Fysr4c0tLOLnKyrixkxQ5eG9vMX/29LC/+XayP/l2sj/5drI/+bbyf/m28n/4dfG/5mXpf9OSdj+Izfm + 50Rg5Sru7u4V7u7uIQAAAAAAAAAAAAAAAAAAAAAQD1liDAtHrRcUixUlIOBsJR/g2Sgh4P+6uMb/5t/V + /+zk1//o39D/59zM/+jezv/r49b/7Oba/5yapv9JRNX/KkHr/kpTe7/u7u4V7u7uIQAAAAAAAAAAAAAA + ABwdWCsjH7L5Ixzb/hUmjHAjQu17I0HttV1t3Py4t9T/4NvT//j18v/49PH/9/Pw//j08f/38/D/39nR + /5qdtf87S83/VGXI/jExMbnt7e0V7e3tIQAAAAAAAAAAAAAAABMVWX8fJMD/Izvt/iNG7/8jR+//Tmvv + /pal6P+0veX/z83N/+rm4P/18+//+Pb0//b08f/m4dv/vbm4/26A4f8mNOb/GRmK/kZFWYXt7e0V7e3t + IQAAAAAAAAAAIULiGh48z84iRu/+Ikbv/i5L5P4kMef+Z27p/q6z6/+Ql97+n6Xg/sPE0//X1M3/19PN + /9PQyv+/vsf/iIne/yox5f4wK8P+0dHX/mpz0qDu7u4V7u7uIUA+yS9MUthSM1Dr2yVH7f8jPuv/YGrQ + /piYvf8lHuD/UU3j/n975f90ctr/YmDV/3d23v5WU+D/Qz3b/kQ+3P9AOtH/PDvk/jUy4/8YFIr/a2tr + /mZzsdDu7u4V7u7uIXx74DNdYOidLCjf/jQw3/5CPdP+sLG//oNlaf5gKTX+Xi1O/lIzj/5oYsv+UU7W + /ldU1/4lHt/+JR7f/iUe3/4jHM3+KCbh/jxC5/5JSdP+jY6W/jQ9rfNKSuNl7u7uDAAAAABJSOaJQ0Ll + /jw55P9rbNX/oJym/mUoJP9lKCT/Pzux/k9V4f8xP97/Qk/b/2Bu2/5KVNL/PkPW/i4w1/8kIMr/Hxq6 + /ihB7P9lc9T/GCh9+yY330QmI8cDAAAAAAAAAACLivCdPzrj/iUe3/9TU9+yoaK2v5eQmd2OgIaziX6I + glJOv1kmH96cJR7f8CUe3/4lJOH/MDbm/mp38P9bb+7/N0rL/h8oxPc4PXjuNTZMoQAAAAAAAAAAAAAA + AAAAAACOjvCgQDrk/iUe36k0L9ABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJR/YCkA941lIR+W0QT/k + +kE+5P4lHt/+JSfj/iQ16P5FTXj9WWmyomN52AUAAAAAAAAAAAAAAABlY+k0LSfggiQd2wcAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJyHdFSUe32YlHt+1JR7f9yUd3/9QTeL/a3jv + /i9M7t0jRe5IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAATErhEGFh6FxQT+awNTDi9SUf4P8kLeX9Iz3rgAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAJB3ZDSUe31AlHt94JCbiLwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAP///kH///xB///EQf/hAEH/wQBB/gAAQf8AAEH/AARB+AAAQfAAAEHgAABB4AAA + QcAAAEEAAABBAAAAQYAAAUGAAAdBh+ADQY/8AUH//4BB///wQf///0H///9B////QSgAAAAQAAAAIAAA + AAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADt7e0FAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAwcnPDb/IzhHi4+MB7e3tHQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAyLzUsMjFUng4ODDkAAAAA3N7gOZGtwrCOrMLE09jbNe3t7SIAAAAAAAAAAAAAAAAAAAAAAAAA + AIWGiT2CjLubYXbf72R89f9xe8Xeube6eXqbtOE8hLz+hK7Q/cDJ0Xnt7e0iAAAAAAAAAAAAAAAAAAAA + AAAAAAA4WORWHkb5/09r6v+1tMT/1s2+/9bNvf+apKf/fqK+/rTBzMPU2Nsm7e3tIgAAAAAAAAAAAAAA + ABMnjgYpN/iOIi3pmj5M4ummrNH/4NfG/+Xayf/m28n/4tjH/6ShsP8qM7nWmafZHO3t7SIAAAAAAAAA + AAcGHC8RCl+/GRuaQCYq7ag2M+D+q6jT/+vm3f/u593/7ubb/+3n3f+rqbv/Pkzj/1RbaYPt7e0iAAAA + AAAAAAATE1ueIy/q/xxC5uIrU/Tveo3l/6mv4f/b2db/8/Ds//Xy7v/W09D/fIjZ/yYupv+em6xk7e3t + IiMcwQsiOtFAHkPg6i5R7v9CVOT/LDHw/4aK6P+Ok+D/hY7k/5ea2f+TlNL/dXXZ/y0t2v91cZX/Vmbb + b+3t7SJeXdxYOkHn7Cgv6P+Gh8X/gGBz/z8ZeP9TRb//aWnW/1JO2P8eFN3/HhPZ/ycj2/9BROb/bW6U + /z9KwrqZm+sWZWPqE0lG5/E8OeX/mJW1/ntKO/9mS2zvREXM4jQ64f9GTd7/PEXe/0hP3/8oLMP/NUbW + /ThBfeM7N9svJxzfA36D7iNXU+j/KCLokX2DuyV6fHc1dnZnEUM+rQUfFec/PTjlmz075ehHSOj/LDTn + /yw5wP1bYn63AAAAAAAAAAAuKuEGLSbhRywm6gMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnIOANIRnf + Tisj4Z9FQOTmWV7t/yg/7dwhRu9GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAARELlCyIa30glGt6cJS7lgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//6sQf/wrEH+IKxB+ACsQfgArEHgAKxBwACs + QcAArEEAAKxBAACsQQAArEEAA6xBH4CsQf/wrEH//6xB//+sQQ== + + + \ No newline at end of file diff --git a/AirScoutViewClient/Program.cs b/AirScoutViewClient/Program.cs new file mode 100644 index 0000000..f9dda3e --- /dev/null +++ b/AirScoutViewClient/Program.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; + +namespace AirScoutViewClient +{ + static class Program + { + /// + /// Der Haupteinstiegspunkt für die Anwendung. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new MapViewDlg()); + } + } +} diff --git a/AirScoutViewClient/Properties/AssemblyInfo.cs b/AirScoutViewClient/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..0aa7a54 --- /dev/null +++ b/AirScoutViewClient/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Allgemeine Informationen über eine Assembly werden über die folgenden +// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, +// die einer Assembly zugeordnet sind. +[assembly: AssemblyTitle("AirScoutViewClient")] +[assembly: AssemblyDescription("Aircraft Scatter Prediction")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("DL2ALF")] +[assembly: AssemblyProduct("AirScoutViewClient")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar +// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von +// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen. +[assembly: ComVisible(false)] + +// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird +[assembly: Guid("930668aa-0dd9-42a9-889a-d319567f1473")] + +// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: +// +// Hauptversion +// Nebenversion +// Buildnummer +// Revision +// +// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern +// übernehmen, indem Sie "*" eingeben: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.3.0.4")] +[assembly: AssemblyFileVersion("1.3.0.4")] diff --git a/AirScoutViewClient/Properties/Resources.Designer.cs b/AirScoutViewClient/Properties/Resources.Designer.cs new file mode 100644 index 0000000..4218503 --- /dev/null +++ b/AirScoutViewClient/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion: 4.0.30319.42000 +// +// Änderungen an dieser Datei können fehlerhaftes Verhalten verursachen und gehen verloren, wenn +// der Code neu generiert wird. +// +//------------------------------------------------------------------------------ + +namespace AirScoutViewClient.Properties +{ + + + /// + /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw. + /// + // Diese Klasse wurde von der StronglyTypedResourceBuilder-Klasse + // über ein Tool wie ResGen oder Visual Studio automatisch generiert. + // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen + // mit der Option /str erneut aus, oder erstellen Sie Ihr VS-Projekt neu. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AirScoutViewClient.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle + /// Ressourcenlookups, die diese stark typisierte Ressourcenklasse verwenden. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/AirScoutViewClient/Properties/Resources.resx b/AirScoutViewClient/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/AirScoutViewClient/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/AirScoutViewClient/Properties/Settings.Designer.cs b/AirScoutViewClient/Properties/Settings.Designer.cs new file mode 100644 index 0000000..1048b64 --- /dev/null +++ b/AirScoutViewClient/Properties/Settings.Designer.cs @@ -0,0 +1,517 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.42000 +// +// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn +// der Code erneut generiert wird. +// +//------------------------------------------------------------------------------ + +namespace AirScoutViewClient.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("localhost")] + public string Server_URL { + get { + return ((string)(this["Server_URL"])); + } + set { + this["Server_URL"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("9880")] + public int Server_Port { + get { + return ((int)(this["Server_Port"])); + } + set { + this["Server_Port"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1")] + public int RefreshRate { + get { + return ((int)(this["RefreshRate"])); + } + set { + this["RefreshRate"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("OpenStreetMap")] + public string Map_Provider { + get { + return ((string)(this["Map_Provider"])); + } + set { + this["Map_Provider"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("\\Tmp")] + public string Tmp_Directory { + get { + return ((string)(this["Tmp_Directory"])); + } + set { + this["Tmp_Directory"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("\\Log")] + public string Log_Directory { + get { + return ((string)(this["Log_Directory"])); + } + set { + this["Log_Directory"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("DL2ALF")] + public string MyCall { + get { + return ((string)(this["MyCall"])); + } + set { + this["MyCall"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public double MyLat { + get { + return ((double)(this["MyLat"])); + } + set { + this["MyLat"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public double MyLon { + get { + return ((double)(this["MyLon"])); + } + set { + this["MyLon"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("GB3MHZ")] + public string DXCall { + get { + return ((string)(this["DXCall"])); + } + set { + this["DXCall"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public double DXLat { + get { + return ((double)(this["DXLat"])); + } + set { + this["DXLat"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public double DXLon { + get { + return ((double)(this["DXLon"])); + } + set { + this["DXLon"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public double MinLat { + get { + return ((double)(this["MinLat"])); + } + set { + this["MinLat"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public double MinLon { + get { + return ((double)(this["MinLon"])); + } + set { + this["MinLon"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public double MaxLat { + get { + return ((double)(this["MaxLat"])); + } + set { + this["MaxLat"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public double MaxLon { + get { + return ((double)(this["MaxLon"])); + } + set { + this["MaxLon"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("B1_2G")] + public global::ScoutBase.Core.BAND Band { + get { + return ((global::ScoutBase.Core.BAND)(this["Band"])); + } + set { + this["Band"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Locator_SmallLettersForSubsquares { + get { + return ((bool)(this["Locator_SmallLettersForSubsquares"])); + } + set { + this["Locator_SmallLettersForSubsquares"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Locator_AutoLength { + get { + return ((bool)(this["Locator_AutoLength"])); + } + set { + this["Locator_AutoLength"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("10")] + public decimal Locator_MaxLength { + get { + return ((decimal)(this["Locator_MaxLength"])); + } + set { + this["Locator_MaxLength"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Map_SmallMarkers { + get { + return ((bool)(this["Map_SmallMarkers"])); + } + set { + this["Map_SmallMarkers"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("12200")] + public int Planes_MaxAlt { + get { + return ((int)(this["Planes_MaxAlt"])); + } + set { + this["Planes_MaxAlt"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("plane.png")] + public string Planes_IconFileName { + get { + return ((string)(this["Planes_IconFileName"])); + } + set { + this["Planes_IconFileName"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("airport.png")] + public string Airports_IconFileName { + get { + return ((string)(this["Airports_IconFileName"])); + } + set { + this["Airports_IconFileName"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool InfoWin_Position { + get { + return ((bool)(this["InfoWin_Position"])); + } + set { + this["InfoWin_Position"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool InfoWin_Metric { + get { + return ((bool)(this["InfoWin_Metric"])); + } + set { + this["InfoWin_Metric"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool InfoWin_Track { + get { + return ((bool)(this["InfoWin_Track"])); + } + set { + this["InfoWin_Track"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool InfoWin_Speed { + get { + return ((bool)(this["InfoWin_Speed"])); + } + set { + this["InfoWin_Speed"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool InfoWin_Alt { + get { + return ((bool)(this["InfoWin_Alt"])); + } + set { + this["InfoWin_Alt"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool InfoWin_Type { + get { + return ((bool)(this["InfoWin_Type"])); + } + set { + this["InfoWin_Type"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool InfoWin_Time { + get { + return ((bool)(this["InfoWin_Time"])); + } + set { + this["InfoWin_Time"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool InfoWin_Dist { + get { + return ((bool)(this["InfoWin_Dist"])); + } + set { + this["InfoWin_Dist"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool InfoWin_Angle { + get { + return ((bool)(this["InfoWin_Angle"])); + } + set { + this["InfoWin_Angle"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool InfoWin_Epsilon { + get { + return ((bool)(this["InfoWin_Epsilon"])); + } + set { + this["InfoWin_Epsilon"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool InfoWin_Squint { + get { + return ((bool)(this["InfoWin_Squint"])); + } + set { + this["InfoWin_Squint"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("5000")] + public int Planes_Filter_Min_Alt { + get { + return ((int)(this["Planes_Filter_Min_Alt"])); + } + set { + this["Planes_Filter_Min_Alt"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("NONE")] + public global::AirScout.Aircrafts.PLANECATEGORY Planes_Filter_Min_Category { + get { + return ((global::AirScout.Aircrafts.PLANECATEGORY)(this["Planes_Filter_Min_Category"])); + } + set { + this["Planes_Filter_Min_Category"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + public global::AirScout.Core.BandSettings Path_Band_Settings { + get { + return ((global::AirScout.Core.BandSettings)(this["Path_Band_Settings"])); + } + set { + this["Path_Band_Settings"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("5000")] + public int Planes_MinAlt { + get { + return ((int)(this["Planes_MinAlt"])); + } + set { + this["Planes_MinAlt"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public int Planes_Filter_Max_Circumcircle { + get { + return ((int)(this["Planes_Filter_Max_Circumcircle"])); + } + set { + this["Planes_Filter_Max_Circumcircle"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Map_AutoCenter { + get { + return ((bool)(this["Map_AutoCenter"])); + } + set { + this["Map_AutoCenter"] = value; + } + } + } +} diff --git a/AirScoutViewClient/Properties/Settings.settings b/AirScoutViewClient/Properties/Settings.settings new file mode 100644 index 0000000..c76e4bc --- /dev/null +++ b/AirScoutViewClient/Properties/Settings.settings @@ -0,0 +1,129 @@ + + + + + + localhost + + + 9880 + + + 1 + + + OpenStreetMap + + + \Tmp + + + \Log + + + DL2ALF + + + 0 + + + 0 + + + GB3MHZ + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + B1_2G + + + False + + + True + + + 10 + + + False + + + 12200 + + + plane.png + + + airport.png + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + True + + + 5000 + + + NONE + + + + + + 5000 + + + 0 + + + True + + + \ No newline at end of file diff --git a/AirScoutViewClient/Settings.cs b/AirScoutViewClient/Settings.cs new file mode 100644 index 0000000..d0afc8b --- /dev/null +++ b/AirScoutViewClient/Settings.cs @@ -0,0 +1,28 @@ +namespace AirScoutViewClient.Properties { + + + // Diese Klasse ermöglicht die Behandlung bestimmter Ereignisse der Einstellungsklasse: + // Das SettingChanging-Ereignis wird ausgelöst, bevor der Wert einer Einstellung geändert wird. + // Das PropertyChanged-Ereignis wird ausgelöst, nachdem der Wert einer Einstellung geändert wurde. + // Das SettingsLoaded-Ereignis wird ausgelöst, nachdem die Einstellungswerte geladen wurden. + // Das SettingsSaving-Ereignis wird ausgelöst, bevor die Einstellungswerte gespeichert werden. + internal sealed partial class Settings { + + public Settings() { + // // Heben Sie die Auskommentierung der unten angezeigten Zeilen auf, um Ereignishandler zum Speichern und Ändern von Einstellungen hinzuzufügen: + // + // this.SettingChanging += this.SettingChangingEventHandler; + // + // this.SettingsSaving += this.SettingsSavingEventHandler; + // + } + + private void SettingChangingEventHandler(object sender, System.Configuration.SettingChangingEventArgs e) { + // Fügen Sie hier Code zum Behandeln des SettingChangingEvent-Ereignisses hinzu. + } + + private void SettingsSavingEventHandler(object sender, System.ComponentModel.CancelEventArgs e) { + // Fügen Sie hier Code zum Behandeln des SettingsSaving-Ereignisses hinzu. + } + } +} diff --git a/AirScoutViewClient/TERMSANDCONDITIONS b/AirScoutViewClient/TERMSANDCONDITIONS new file mode 100644 index 0000000..7a60eb9 --- /dev/null +++ b/AirScoutViewClient/TERMSANDCONDITIONS @@ -0,0 +1,82 @@ ++++ TERMS AND CONDITIONS +++ + +Copyright (c) 2018 by DL2ALF + +AirScout is free for personal ham radio related use. You may free distribute the package without changes. It has been developed using C# and Microsoft Visual Studio Express. It is released under the GNU Public Licence V3, the source code is available on request. It could not have been developed without the great help of Open Source software found on the Internet for almost all presentation and calculation tasks inside the software. You may use it "as it is" without any warranties. For further information see the disclaimer on the "Options/Info" tab. + ++++ PRIVACY POLICY +++ + +Personal data (usually referred to just as "data" below) will only be processed by us to the extent necessary and for the purpose of providing a functional and user-friendly software. + +Per Art. 4 No. 1 of Regulation (EU) 2016/679, i.e. the General Data Protection Regulation (hereinafter referred to as the "GDPR"), "processing" refers to any operation or set of operations such as collection, recording, organization, structuring, storage, adaptation, alteration, retrieval, consultation, use, disclosure by transmission, dissemination, or otherwise making available, alignment, or combination, restriction, erasure, or destruction performed on personal data, whether by automated means or not. + +The following privacy policy is intended to inform you in particular about the type, scope, purpose, duration, and legal basis for the processing of such data either under my own control or in conjunction with others. + +My privacy policy is structured as follows: + +I. Information about me as a controller of your data +II. The rights of users and data subjects +III. Information about the data processing + +I. Information about me as a controller of your data + +The party responsible (the "controller") for AirScout (this "software") for purposes of data protection law is: + +Frank Schmähling +Tabarzer Str. 15 +99867 Gotha +Germany + +Email: dl2alf@darc.de + +II. The rights of users and data subjects + +With regard to the data processing to be described in more detail below, users and data subjects have the right + + to confirmation of whether data concerning them is being processed, information about the data being processed, further information about the nature of the data processing, and copies of the data (cf. also Art. 15 GDPR); + to correct or complete incorrect or incomplete data (cf. also Art. 16 GDPR); + to the immediate deletion of data concerning them (cf. also Art. 17 DSGVO), or, alternatively, if further processing is necessary as stipulated in Art. 17 Para. 3 GDPR, to restrict said processing per Art. 18 GDPR; + to receive copies of the data concerning them and/or provided by them and to have the same transmitted to other providers/controllers (cf. also Art. 20 GDPR); + to file complaints with the supervisory authority if they believe that data concerning them is being processed by the controller in breach of data protection provisions (see also Art. 77 GDPR). + +In addition, the controller is obliged to inform all recipients to whom it discloses data of any such corrections, deletions, or restrictions placed on processing the same per Art. 16, 17 Para. 1, 18 GDPR. However, this obligation does not apply if such notification is impossible or involves a disproportionate effort. Nevertheless, users have a right to information about these recipients. + +Likewise, under Art. 21 GDPR, users and data subjects have the right to object to the controller's future processing of their data pursuant to Art. 6 Para. 1 lit. f) GDPR. In particular, an objection to data processing for the purpose of direct advertising is permissible. + +III. Information about the data processing + +This software is retrieveing user information to maintain a AirScout global station database. Therefore a minimum of personal data will be stored on the AirScout server. Most of them is collected from publically available sources. Please support the community with details about your station and your QTH. + +The following information is collected by this software and is stored per user/station: + + - call sign + - latitude + - longitude + - grid square + - elevation + - timestamp of last change (UTC) + +The following information is collected by this software and is stored per user/station and band: + + - antenna height + - antenna gain + - output power + - timestamp of last change (UTC) + +The data are uploaded either at program startup or on user action. The transfer of the collected data is encrypted via Secure FTP (SFTP) to the AirScout web server. +All information is kept confidental and is used only to maintain the AirScout global station database and for statistical purposes. +In case of any change and after successful review of the data an automatic update is redistributed to all AirScout local databases. +No further user tracking, analysis or advertisement is performed. + +THE USER ACCEPTS THE FACT THAT HIS REQUEST FOR DELETING OF PERSONAL DATA DOES ONLY AFFECT THE AIRSCOUT GLOBAL DATABASE AND THAT HIS PERSONAL DATA MAY STILL EXIST IN ANY LOCAL DATABASE OF ANY OTHER AIRSCOUT USER. + +The basis for this storage is Art. 6 Para. 1 lit. f) GDPR. My legitimate interest lies in the improvement, stability, functionality, and security of this software. + + + ++++ LIABILITY DISCLAIMER +++ + +THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +You must agree to these terms and conditions to run AirScout. +To view complete license information please refer to "Options/Info". \ No newline at end of file diff --git a/AirScoutViewClient/VersionHistory.txt b/AirScoutViewClient/VersionHistory.txt new file mode 100644 index 0000000..1a347b4 --- /dev/null +++ b/AirScoutViewClient/VersionHistory.txt @@ -0,0 +1,5 @@ +2018-12-16: V1.3.0.4 +==================== + +Initial Version + diff --git a/AirScoutViewClient/airport.png b/AirScoutViewClient/airport.png new file mode 100644 index 0000000..bec3a61 Binary files /dev/null and b/AirScoutViewClient/airport.png differ diff --git a/AirScoutViewClient/app.config b/AirScoutViewClient/app.config new file mode 100644 index 0000000..621eacf --- /dev/null +++ b/AirScoutViewClient/app.config @@ -0,0 +1,140 @@ + + + + +
+ + + + + + localhost + + + 9880 + + + 1 + + + OpenStreetMap + + + \Tmp + + + \Log + + + DL2ALF + + + 0 + + + 0 + + + GB3MHZ + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + B1_2G + + + False + + + True + + + 10 + + + False + + + 12200 + + + plane.png + + + airport.png + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + True + + + 5000 + + + NONE + + + 5000 + + + 0 + + + True + + + + + + + + + + + + \ No newline at end of file diff --git a/AirScoutViewClient/packages.config b/AirScoutViewClient/packages.config new file mode 100644 index 0000000..0aee17e --- /dev/null +++ b/AirScoutViewClient/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/AirScoutViewClient/plane.png b/AirScoutViewClient/plane.png new file mode 100644 index 0000000..b521502 Binary files /dev/null and b/AirScoutViewClient/plane.png differ diff --git a/AquaGauge/AquaGauge.Designer.cs b/AquaGauge/AquaGauge.Designer.cs new file mode 100644 index 0000000..3734ec4 --- /dev/null +++ b/AquaGauge/AquaGauge.Designer.cs @@ -0,0 +1,44 @@ +namespace AquaControls +{ + partial class AquaGauge + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.SuspendLayout(); + // + // AquaGauge + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Name = "AquaGauge"; + this.ResumeLayout(false); + + } + + #endregion + } +} diff --git a/AquaGauge/AquaGauge.cs b/AquaGauge/AquaGauge.cs new file mode 100644 index 0000000..03c1117 --- /dev/null +++ b/AquaGauge/AquaGauge.cs @@ -0,0 +1,854 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Text; +using System.Windows.Forms; +using System.Drawing.Drawing2D; +using System.Globalization; + +namespace AquaControls +{ + /// + /// Aqua Gauge Control - A Windows User Control. + /// Author : Ambalavanar Thirugnanam + /// Date : 24th August 2007 + /// email : ambalavanar.thiru@gmail.com + /// This is control is for free. You can use for any commercial or non-commercial purposes. + /// [Please do no remove this header when using this control in your application.] + /// + /// Modified to get a full 360deg Gage + /// Author : DL2ALF + /// Date : 2041-02-18 + /// + public partial class AquaGauge : UserControl + { + #region Private Attributes + private float minValue; + private float maxValue; + private float threshold; + private float currentValue; + private float recommendedValue; + private int noOfDivisions; + private int noOfSubDivisions; + private string dialText; + private Color dialColor = Color.Lavender; + private float glossinessAlpha = 25; + private int oldWidth, oldHeight; + public int x, y, width, height; +// float fromAngle = 135F; +// float toAngle = 405F; + public float fromAngle = -90F; + public float toAngle = 270F; + private bool enableTransparentBackground; + private bool requiresRedraw; + private Image backgroundImg; + private Rectangle rectImg; + #endregion + + public AquaGauge() + { + InitializeComponent(); + x = 5; + y = 5; + width = this.Width - 10; + height = this.Height - 10; + this.noOfDivisions = 12; + this.noOfSubDivisions = 3; + this.SetStyle(ControlStyles.SupportsTransparentBackColor, true); + this.SetStyle(ControlStyles.ResizeRedraw, true); + this.SetStyle(ControlStyles.AllPaintingInWmPaint, true); + this.SetStyle(ControlStyles.UserPaint, true); + this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true); + this.BackColor = Color.Transparent; + this.Resize += new EventHandler(AquaGauge_Resize); + this.requiresRedraw = true; + } + + #region Public Properties + /// + /// Mininum value on the scale + /// + [DefaultValue(0)] + [Description("Mininum value on the scale")] + public float MinValue + { + get { return minValue; } + set + { + if (value < maxValue) + { + minValue = value; + if (currentValue < minValue) + currentValue = minValue; + if (recommendedValue < minValue) + recommendedValue = minValue; + requiresRedraw = true; + this.Invalidate(); + } + } + } + + /// + /// Maximum value on the scale + /// + [DefaultValue(100)] + [Description("Maximum value on the scale")] + public float MaxValue + { + get { return maxValue; } + set + { + if (value > minValue) + { + maxValue = value; + if (currentValue > maxValue) + currentValue = maxValue; + if (recommendedValue > maxValue) + recommendedValue = maxValue; + requiresRedraw = true; + this.Invalidate(); + } + } + } + + /// + /// Gets or Sets the Threshold area from the Recommended Value. (1-99%) + /// + [DefaultValue(25)] + [Description("Gets or Sets the Threshold area from the Recommended Value. (1-99%)")] + public float ThresholdPercent + { + get { return threshold; } + set + { + if (value > 0 && value < 100) + { + threshold = value; + requiresRedraw = true; + this.Invalidate(); + } + } + } + + /// + /// Threshold value from which green area will be marked. + /// + [DefaultValue(25)] + [Description("Threshold value from which green area will be marked.")] + public float RecommendedValue + { + get { return recommendedValue; } + set + { + if (value > minValue && value < maxValue) + { + recommendedValue = value; + requiresRedraw = true; + this.Invalidate(); + } + } + } + + /// + /// Value where the pointer will point to. + /// + [DefaultValue(0)] + [Description("Value where the pointer will point to.")] + public float Value + { + get { return currentValue; } + set + { + if (value >= minValue && value <= maxValue) + { + currentValue = value; + this.Refresh(); + } + } + } + + /// + /// Background color of the dial + /// + [Description("Background color of the dial")] + public Color DialColor + { + get { return dialColor; } + set + { + dialColor = value; + requiresRedraw = true; + this.Invalidate(); + } + } + + /// + /// Glossiness strength. Range: 0-100 + /// + [DefaultValue(72)] + [Description("Glossiness strength. Range: 0-100")] + public float Glossiness + { + get + { + return (glossinessAlpha * 100) / 220; + } + set + { + float val = value; + if(val > 100) + value = 100; + if(val < 0) + value = 0; + glossinessAlpha = (value * 220) / 100; + this.Refresh(); + } + } + + /// + /// Get or Sets the number of Divisions in the dial scale. + /// + [DefaultValue(10)] + [Description("Get or Sets the number of Divisions in the dial scale.")] + public int NoOfDivisions + { + get { return this.noOfDivisions; } + set + { + if (value > 1 && value < 25) + { + this.noOfDivisions = value; + requiresRedraw = true; + this.Invalidate(); + } + } + } + + /// + /// Gets or Sets the number of Sub Divisions in the scale per Division. + /// + [DefaultValue(3)] + [Description("Gets or Sets the number of Sub Divisions in the scale per Division.")] + public int NoOfSubDivisions + { + get { return this.noOfSubDivisions; } + set + { + if (value > 0 && value <= 10) + { + this.noOfSubDivisions = value; + requiresRedraw = true; + this.Invalidate(); + } + } + } + + /// + /// Gets or Sets the Text to be displayed in the dial + /// + [Description("Gets or Sets the Text to be displayed in the dial")] + public string DialText + { + get { return this.dialText; } + set + { + this.dialText = value; + requiresRedraw = true; + this.Invalidate(); + } + } + + /// + /// Enables or Disables Transparent Background color. + /// Note: Enabling this will reduce the performance and may make the control flicker. + /// + [DefaultValue(false)] + [Description("Enables or Disables Transparent Background color. Note: Enabling this will reduce the performance and may make the control flicker.")] + public bool EnableTransparentBackground + { + get { return this.enableTransparentBackground; } + set + { + this.enableTransparentBackground = value; + this.SetStyle(ControlStyles.OptimizedDoubleBuffer, !enableTransparentBackground); + requiresRedraw = true; + this.Refresh(); + } + } + #endregion + + #region Overriden Control methods + /// + /// Draws the pointer. + /// + /// + protected override void OnPaint(PaintEventArgs e) + { + // e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; + e.Graphics.SmoothingMode = SmoothingMode.HighQuality; + width = this.Width - x * 2; + height = this.Height - y*2; + DrawPointer(e.Graphics, 0,0); + //Draw Digital Value + DisplayNumber(e.Graphics,0,0); + } + + /// + /// Draws the dial background. + /// + /// + protected override void OnPaintBackground(PaintEventArgs e) + { + if (!enableTransparentBackground) + { + base.OnPaintBackground(e); + } + + e.Graphics.SmoothingMode = SmoothingMode.HighQuality; + e.Graphics.FillRectangle(new SolidBrush(Color.Transparent), new Rectangle(0,0,Width,Height)); + if (backgroundImg == null || requiresRedraw) + { + backgroundImg = new Bitmap(this.Width, this.Height); + Graphics g = Graphics.FromImage(backgroundImg); + g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; + width = this.Width - x * 2; + height = this.Height - y * 2; + rectImg = new Rectangle(x, y, width, height); + + //Draw background color + Brush backGroundBrush = new SolidBrush(Color.FromArgb(120, dialColor)); + if (enableTransparentBackground && this.Parent != null) + { + float gg = width / 60; + //g.FillEllipse(new SolidBrush(this.Parent.BackColor), -gg, -gg, this.Width+gg*2, this.Height+gg*2); + } + g.FillEllipse(backGroundBrush, x, y, width, height); + + //Draw Rim + SolidBrush outlineBrush = new SolidBrush(Color.FromArgb(100, Color.SlateGray)); + Pen outline = new Pen(outlineBrush, (float)(width * .03)); + g.DrawEllipse(outline, rectImg); + Pen darkRim = new Pen(Color.SlateGray); + g.DrawEllipse(darkRim, x, y, width, height); + + //Draw Callibration + DrawCalibration(g, 0, 0); + + //Draw Colored Rim + Pen colorPen = new Pen(Color.FromArgb(190, Color.Gainsboro), this.Width / 40); + Pen blackPen = new Pen(Color.FromArgb(250, Color.Black), this.Width / 200); +// int gap = (int)(this.Width * 0.03F); + int gap = 0; + Rectangle rectg = new Rectangle(rectImg.X + gap, rectImg.Y + gap, rectImg.Width - gap * 2, rectImg.Height - gap * 2); + g.DrawArc(colorPen, rectg, 135, 270); + + //Draw Threshold + colorPen = new Pen(Color.FromArgb(200, Color.LawnGreen), this.Width / 50); + rectg = new Rectangle(rectImg.X + gap, rectImg.Y + gap, rectImg.Width - gap * 2, rectImg.Height - gap * 2); + float val = MaxValue - MinValue; + val = (100 * (this.recommendedValue - MinValue)) / val; + val = ((toAngle - fromAngle) * val) / 100; + val += fromAngle; + float stAngle = val - ((270 * threshold) / 200); + if (stAngle <= 135) stAngle = 135; + float sweepAngle = ((270 * threshold) / 100); + if (stAngle + sweepAngle > 405) sweepAngle = 405 - stAngle; + g.DrawArc(colorPen, rectg, stAngle, sweepAngle); + + //Draw Digital Value +// RectangleF digiRect = new RectangleF((float)this.Width / 2F - (float)this.width / 5F, (float)this.height / 1.2F, (float)this.width / 2.5F, (float)this.Height / 9F); +// RectangleF digiFRect = new RectangleF(this.Width / 2 - this.width / 7, (int)(this.height / 1.18), this.width / 4, this.Height / 12); + RectangleF digiRect = new RectangleF((float)this.Width / 2F - (float)this.width / 5F, (float)this.height / 1.5F, (float)this.width / 2.5F, (float)this.Height / 9F); + RectangleF digiFRect = new RectangleF(this.Width / 2 - this.width / 7, (int)(this.height / 1.47), this.width / 4, this.Height / 12); + g.FillRectangle(new SolidBrush(Color.FromArgb(30, Color.Gray)), digiRect); +// DisplayNumber(g, this.currentValue, digiFRect); + + SizeF textSize = g.MeasureString(this.dialText, this.Font); +// RectangleF digiFRectText = new RectangleF(this.Width / 2 - textSize.Width / 2, (int)(this.height / 1.5), textSize.Width, textSize.Height); + RectangleF digiFRectText = new RectangleF(this.Width / 2 - textSize.Width / 2, (int)(this.height / 4), textSize.Width, textSize.Height); + g.DrawString(dialText, this.Font, new SolidBrush(this.ForeColor), digiFRectText); + requiresRedraw = false; + } + e.Graphics.DrawImage(backgroundImg, rectImg); + } + + protected override CreateParams CreateParams + { + get + { + CreateParams cp = base.CreateParams; + cp.ExStyle |= 0x20; + return cp; + } + } + #endregion + + + #region Drawing methods + + public void DrawDialText (Graphics g, int ofsX, int ofsY) + { + SizeF textSize = g.MeasureString(this.dialText, this.Font); + RectangleF digiFRectText = new RectangleF(this.Width / 2 - textSize.Width / 2, (int)(this.height / 4), textSize.Width, textSize.Height); + digiFRectText.Offset(ofsX, ofsY); + g.DrawString(dialText, this.Font, new SolidBrush(this.ForeColor), digiFRectText); + } + + /// + /// Draws the Pointer. + /// + /// + /// + /// + public void DrawPointer(Graphics gr, int ofsX, int ofsY) + { + int cx = ((width) / 2) + this.x; + int cy = ((height) / 2) + this.y; + + float radius = this.Width / 2 - (this.Width * .12F); + float val = MaxValue - MinValue; + + Image img = new Bitmap(this.Width, this.Height); + Graphics g = Graphics.FromImage(img); + g.SmoothingMode = SmoothingMode.AntiAlias; + + val = (100 * (this.currentValue - MinValue)) / val; + val = ((toAngle - fromAngle) * val) / 100; + val += fromAngle; + + float angle = GetRadian(val); + float gradientAngle = angle; + + PointF[] pts = new PointF[5]; + + pts[0].X = (float)(cx + radius * Math.Cos(angle)); + pts[0].Y = (float)(cy + radius * Math.Sin(angle)); + + pts[4].X = (float)(cx + radius * Math.Cos(angle - 0.02)); + pts[4].Y = (float)(cy + radius * Math.Sin(angle - 0.02)); + + angle = GetRadian((val + 20)); + pts[1].X = (float)(cx + (this.Width * .09F) * Math.Cos(angle)); + pts[1].Y = (float)(cy + (this.Width * .09F) * Math.Sin(angle)); + + pts[2].X = cx; + pts[2].Y = cy; + + angle = GetRadian((val - 20)); + pts[3].X = (float)(cx + (this.Width * .09F) * Math.Cos(angle)); + pts[3].Y = (float)(cy + (this.Width * .09F) * Math.Sin(angle)); + + Brush pointer = new SolidBrush(Color.Black); + g.FillPolygon(pointer, pts); + + PointF[] shinePts = new PointF[3]; + angle = GetRadian(val); + shinePts[0].X = (float)(cx + radius * Math.Cos(angle)); + shinePts[0].Y = (float)(cy + radius * Math.Sin(angle)); + + angle = GetRadian(val + 20); + shinePts[1].X = (float)(cx + (this.Width * .09F) * Math.Cos(angle)); + shinePts[1].Y = (float)(cy + (this.Width * .09F) * Math.Sin(angle)); + + shinePts[2].X = cx; + shinePts[2].Y = cy; + + LinearGradientBrush gpointer = new LinearGradientBrush(shinePts[0], shinePts[2], Color.SlateGray, Color.Black); + g.FillPolygon(gpointer, shinePts); + + Rectangle rect = new Rectangle(x, y, width, height); + DrawCenterPoint(g, ofsX,ofsY); + + DrawGloss(g); + + gr.DrawImage(img, ofsX,ofsY); + } + + /// + /// Draws the glossiness. + /// + /// + private void DrawGloss(Graphics g) + { + RectangleF glossRect = new RectangleF( + x + (float)(width * 0.10), + y + (float)(height * 0.07), + (float)(width * 0.80), + (float)(height * 0.7)); + LinearGradientBrush gradientBrush = + new LinearGradientBrush(glossRect, + Color.FromArgb((int)glossinessAlpha, Color.White), + Color.Transparent, + LinearGradientMode.Vertical); + g.FillEllipse(gradientBrush, glossRect); + + //TODO: Gradient from bottom + glossRect = new RectangleF( + x + (float)(width * 0.25), + y + (float)(height * 0.77), + (float)(width * 0.50), + (float)(height * 0.2)); + int gloss = (int)(glossinessAlpha / 3); + gradientBrush = + new LinearGradientBrush(glossRect, + Color.Transparent, Color.FromArgb(gloss, this.BackColor), + LinearGradientMode.Vertical); + g.FillEllipse(gradientBrush, glossRect); + } + + /// + /// Draws the center point. + /// + /// + /// + /// + /// + public void DrawCenterPoint(Graphics g, int ofsX, int ofsY) + { + int cX = ((width) / 2) + this.x + ofsX; + int cY = ((height) / 2) + this.y + ofsY; + float shift = Width / 5; + Rectangle rect = new Rectangle(this.x, this.y, width, height); + RectangleF rectangle = new RectangleF(cX - (shift / 2), cY - (shift / 2), shift, shift); + LinearGradientBrush brush = new LinearGradientBrush(rect, Color.Black, Color.FromArgb(100,this.dialColor), LinearGradientMode.Vertical); + g.FillEllipse(brush, rectangle); + shift = Width / 7; + rectangle = new RectangleF(cX - (shift / 2), cY - (shift / 2), shift, shift); + brush = new LinearGradientBrush(rect, Color.SlateGray, Color.Black, LinearGradientMode.ForwardDiagonal); + g.FillEllipse(brush, rectangle); + } + + /// + /// Draws the Ruler + /// + /// + /// + /// + /// + public void DrawCalibration(Graphics g, int ofsX, int ofsY) + { + int cX = ((width) / 2) + this.x + ofsX; + int cY = ((height) / 2) + this.y + ofsY; + width = this.Width - this.x * 2; + height = this.Height - this.y * 2; + Rectangle rect = new Rectangle(this.x, this.y, width, height); + int noOfParts = this.noOfDivisions + 1; + int noOfIntermediates = this.noOfSubDivisions; + float currentAngle = GetRadian(fromAngle); + int gap = (int)(this.Width * 0.01F); + float shift = this.Width / 25; + Rectangle rectangle = new Rectangle(rect.Left + gap, rect.Top + gap, rect.Width - gap, rect.Height - gap); + + float x,y,x1,y1,tx,ty,radius; + radius = rectangle.Width/2 - gap*5; + float totalAngle = toAngle - fromAngle; + float incr = GetRadian(((totalAngle) / ((noOfParts - 1) * (noOfIntermediates + 1)))); + + Pen thickPen = new Pen(Color.Black, Width/50); + Pen thinPen = new Pen(Color.Black, Width/100); + float rulerValue = MinValue; + for (int i = 0; i <= noOfParts; i++) + { + //Draw Thick Line + x = (float)(cX + radius * Math.Cos(currentAngle)); + y = (float)(cY + radius * Math.Sin(currentAngle)); + x1 = (float)(cX + (radius - Width/20) * Math.Cos(currentAngle)); + y1 = (float)(cY + (radius - Width/20) * Math.Sin(currentAngle)); + g.DrawLine(thickPen, x, y, x1, y1); + + //Draw Strings + StringFormat format = new StringFormat(); + tx = (float)(cX + (radius - Width / 10) * Math.Cos(currentAngle)); + ty = (float)(cY-shift + (radius - Width / 10) * Math.Sin(currentAngle)); + Brush stringPen = new SolidBrush(this.ForeColor); + StringFormat strFormat = new StringFormat(StringFormatFlags.NoClip); + strFormat.Alignment = StringAlignment.Center; + Font f = new Font(this.Font.FontFamily, (float)(this.Width / 23), this.Font.Style); + g.DrawString(rulerValue.ToString() + "", f, stringPen, new PointF(tx, ty), strFormat); + rulerValue += (float)((MaxValue - MinValue) / (noOfParts - 1)); + rulerValue = (float)Math.Round(rulerValue, 2); + + //currentAngle += incr; + if (i == noOfParts -1) + break; + for (int j = 0; j <= noOfIntermediates; j++) + { + //Draw thin lines + currentAngle += incr; + x = (float)(cX + radius * Math.Cos(currentAngle)); + y = (float)(cY + radius * Math.Sin(currentAngle)); + x1 = (float)(cX + (radius - Width/50) * Math.Cos(currentAngle)); + y1 = (float)(cY + (radius - Width/50) * Math.Sin(currentAngle)); + g.DrawLine(thinPen, x, y, x1, y1); + } + } + } + + /// + /// Converts the given degree to radian. + /// + /// + /// + public float GetRadian(float theta) + { + return theta * (float)Math.PI / 180F; + } + + /// + /// Displays the given number in the 7-Segement format. + /// + /// + /// + /// + public void DisplayNumber(Graphics g, int ofsX, int ofsY) + { + try + { + RectangleF digiRect = new RectangleF((float)this.Width / 2F - (float)this.width / 5F, (float)this.height / 1.5F, (float)this.width / 2.5F, (float)this.Height / 9F); + RectangleF digiFRect = new RectangleF(this.Width / 2 - this.width / 7, (int)(this.height / 1.47), this.width / 3, this.Height / 12); + digiRect.Offset(ofsX,ofsY); + digiFRect.Offset(ofsX,ofsY); + g.FillRectangle(new SolidBrush(Color.FromArgb(30, Color.Gray)), digiRect); + string num = this.currentValue.ToString("000.00",CultureInfo.InvariantCulture); + num.PadLeft(3, '0'); + float shift = 0; + if (this.currentValue < 0) + { + shift -= width/17; + } + bool drawDPS = false; + char[] chars = num.ToCharArray(); + for (int i = 0; i < chars.Length; i++) + { + char c = chars[i]; + if (i < chars.Length - 1 && chars[i + 1] == '.') + drawDPS = true; + else + drawDPS = false; + if (c != '.') + { + if (c == '-') + { + DrawDigit(g, -1, new PointF(digiFRect.X + shift, digiFRect.Y), drawDPS, digiFRect.Height); + } + else + { + DrawDigit(g, Int16.Parse(c.ToString()), new PointF(digiFRect.X + shift, digiFRect.Y), drawDPS, digiFRect.Height); + } + shift += 15 * this.width / 250; + } + else + { + shift += 2 * this.width / 250; + } + } + } + catch (Exception ex) + { + } + } + + /// + /// Draws a digit in 7-Segement format. + /// + /// + /// + /// + /// + /// + private void DrawDigit(Graphics g, int number, PointF position, bool dp, float height) + { + float width; + width = 10F * height/13; + + Pen outline = new Pen(Color.FromArgb(40, this.dialColor)); + Pen fillPen = new Pen(Color.Black); + + #region Form Polygon Points + //Segment A + PointF[] segmentA = new PointF[5]; + segmentA[0] = segmentA[4] = new PointF(position.X + GetX(2.8F, width), position.Y + GetY(1F, height)); + segmentA[1] = new PointF(position.X + GetX(10, width), position.Y + GetY(1F, height)); + segmentA[2] = new PointF(position.X + GetX(8.8F, width), position.Y + GetY(2F, height)); + segmentA[3] = new PointF(position.X + GetX(3.8F, width), position.Y + GetY(2F, height)); + + //Segment B + PointF[] segmentB = new PointF[5]; + segmentB[0] = segmentB[4] = new PointF(position.X + GetX(10, width), position.Y + GetY(1.4F, height)); + segmentB[1] = new PointF(position.X + GetX(9.3F, width), position.Y + GetY(6.8F, height)); + segmentB[2] = new PointF(position.X + GetX(8.4F, width), position.Y + GetY(6.4F, height)); + segmentB[3] = new PointF(position.X + GetX(9F, width), position.Y + GetY(2.2F, height)); + + //Segment C + PointF[] segmentC = new PointF[5]; + segmentC[0] = segmentC[4] = new PointF(position.X + GetX(9.2F, width), position.Y + GetY(7.2F, height)); + segmentC[1] = new PointF(position.X + GetX(8.7F, width), position.Y + GetY(12.7F, height)); + segmentC[2] = new PointF(position.X + GetX(7.6F, width), position.Y + GetY(11.9F, height)); + segmentC[3] = new PointF(position.X + GetX(8.2F, width), position.Y + GetY(7.7F, height)); + + //Segment D + PointF[] segmentD = new PointF[5]; + segmentD[0] = segmentD[4] = new PointF(position.X + GetX(7.4F, width), position.Y + GetY(12.1F, height)); + segmentD[1] = new PointF(position.X + GetX(8.4F, width), position.Y + GetY(13F, height)); + segmentD[2] = new PointF(position.X + GetX(1.3F, width), position.Y + GetY(13F, height)); + segmentD[3] = new PointF(position.X + GetX(2.2F, width), position.Y + GetY(12.1F, height)); + + //Segment E + PointF[] segmentE = new PointF[5]; + segmentE[0] = segmentE[4] = new PointF(position.X + GetX(2.2F, width), position.Y + GetY(11.8F, height)); + segmentE[1] = new PointF(position.X + GetX(1F, width), position.Y + GetY(12.7F, height)); + segmentE[2] = new PointF(position.X + GetX(1.7F, width), position.Y + GetY(7.2F, height)); + segmentE[3] = new PointF(position.X + GetX(2.8F, width), position.Y + GetY(7.7F, height)); + + //Segment F + PointF[] segmentF = new PointF[5]; + segmentF[0] = segmentF[4] = new PointF(position.X + GetX(3F, width), position.Y + GetY(6.4F, height)); + segmentF[1] = new PointF(position.X + GetX(1.8F, width), position.Y + GetY(6.8F, height)); + segmentF[2] = new PointF(position.X + GetX(2.6F, width), position.Y + GetY(1.3F, height)); + segmentF[3] = new PointF(position.X + GetX(3.6F, width), position.Y + GetY(2.2F, height)); + + //Segment G + PointF[] segmentG = new PointF[7]; + segmentG[0] = segmentG[6] = new PointF(position.X + GetX(2F, width), position.Y + GetY(7F, height)); + segmentG[1] = new PointF(position.X + GetX(3.1F, width), position.Y + GetY(6.5F, height)); + segmentG[2] = new PointF(position.X + GetX(8.3F, width), position.Y + GetY(6.5F, height)); + segmentG[3] = new PointF(position.X + GetX(9F, width), position.Y + GetY(7F, height)); + segmentG[4] = new PointF(position.X + GetX(8.2F, width), position.Y + GetY(7.5F, height)); + segmentG[5] = new PointF(position.X + GetX(2.9F, width), position.Y + GetY(7.5F, height)); + + //Segment DP + #endregion + + #region Draw Segments Outline + g.FillPolygon(outline.Brush, segmentA); + g.FillPolygon(outline.Brush, segmentB); + g.FillPolygon(outline.Brush, segmentC); + g.FillPolygon(outline.Brush, segmentD); + g.FillPolygon(outline.Brush, segmentE); + g.FillPolygon(outline.Brush, segmentF); + g.FillPolygon(outline.Brush, segmentG); + #endregion + + #region Fill Segments + //Fill SegmentA + if (IsNumberAvailable(number, 0, 2, 3, 5, 6, 7, 8, 9)) + { + g.FillPolygon(fillPen.Brush, segmentA); + } + + //Fill SegmentB + if (IsNumberAvailable(number, 0, 1, 2, 3, 4, 7, 8, 9)) + { + g.FillPolygon(fillPen.Brush, segmentB); + } + + //Fill SegmentC + if (IsNumberAvailable(number, 0, 1, 3, 4, 5, 6, 7, 8, 9)) + { + g.FillPolygon(fillPen.Brush, segmentC); + } + + //Fill SegmentD + if (IsNumberAvailable(number, 0, 2, 3, 5, 6, 8, 9)) + { + g.FillPolygon(fillPen.Brush, segmentD); + } + + //Fill SegmentE + if (IsNumberAvailable(number, 0, 2, 6, 8)) + { + g.FillPolygon(fillPen.Brush, segmentE); + } + + //Fill SegmentF + if (IsNumberAvailable(number, 0, 4, 5, 6, 7, 8, 9)) + { + g.FillPolygon(fillPen.Brush, segmentF); + } + + //Fill SegmentG + if (IsNumberAvailable(number, 2, 3, 4, 5, 6, 8, 9, -1)) + { + g.FillPolygon(fillPen.Brush, segmentG); + } + #endregion + + //Draw decimal point + if (dp) + { + g.FillEllipse(fillPen.Brush, new RectangleF( + position.X + GetX(10F, width), + position.Y + GetY(12F, height), + width/7, + width/7)); + } + } + + /// + /// Gets Relative X for the given width to draw digit + /// + /// + /// + /// + private float GetX(float x, float width) + { + return x * width / 12; + } + + /// + /// Gets relative Y for the given height to draw digit + /// + /// + /// + /// + private float GetY(float y, float height) + { + return y * height / 15; + } + + /// + /// Returns true if a given number is available in the given list. + /// + /// + /// + /// + private bool IsNumberAvailable(int number, params int[] listOfNumbers) + { + if (listOfNumbers.Length > 0) + { + foreach (int i in listOfNumbers) + { + if (i == number) + return true; + } + } + return false; + } + + /// + /// Restricts the size to make sure the height and width are always same. + /// + /// + /// + private void AquaGauge_Resize(object sender, EventArgs e) + { + if (this.Width < 136) + { + this.Width = 136; + } + if (oldWidth != this.Width) + { + this.Height = this.Width; + oldHeight = this.Width; + } + if (oldHeight != this.Height) + { + this.Width = this.Height; + oldWidth = this.Width; + } + } + #endregion + } +} diff --git a/AquaGauge/AquaGauge.csproj b/AquaGauge/AquaGauge.csproj new file mode 100644 index 0000000..cbf8f0d --- /dev/null +++ b/AquaGauge/AquaGauge.csproj @@ -0,0 +1,88 @@ + + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {0E5542E0-FC5D-4F67-950D-9F28C5D1225A} + Library + Properties + AquaControls + AquaGauge + v4.0 + + + + + 2.0 + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Newtonsoft.Json.12.0.1\lib\net40\Newtonsoft.Json.dll + True + + + + + ..\packages\System.Data.SQLite.Core.1.0.110.0\lib\net40\System.Data.SQLite.dll + True + + + + + + + + UserControl + + + AquaGauge.cs + + + + + + + AquaGauge.cs + Designer + + + + + + + + + + Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}". + + + + + \ No newline at end of file diff --git a/AquaGauge/AquaGauge.resx b/AquaGauge/AquaGauge.resx new file mode 100644 index 0000000..19dc0dd --- /dev/null +++ b/AquaGauge/AquaGauge.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/AquaGauge/Properties/AssemblyInfo.cs b/AquaGauge/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..0b1af0b --- /dev/null +++ b/AquaGauge/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +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("AquaGauge")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("AquaControls")] +[assembly: AssemblyProduct("AquaGauge")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("27affae7-2393-4ce7-bb2b-c0b58249c56d")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/AquaGauge/packages.config b/AquaGauge/packages.config new file mode 100644 index 0000000..0aee17e --- /dev/null +++ b/AquaGauge/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/CubicSpline/ArrayUtil.cs b/CubicSpline/ArrayUtil.cs new file mode 100644 index 0000000..330987e --- /dev/null +++ b/CubicSpline/ArrayUtil.cs @@ -0,0 +1,36 @@ +using System; +using System.Text; + +namespace CubicSpline +{ + /// + /// Utility methods for arrays. + /// + public static class ArrayUtil + { + /// + /// Create a string to display the array values. + /// + /// The array + /// Optional. A string to use to format each value. Must contain the colon, so something like ':0.000' + public static string ToString(T[] array, string format = "") + { + var s = new StringBuilder(); + string formatString = "{0" + format + "}"; + + for (int i = 0; i < array.Length; i++) + { + if (i < array.Length - 1) + { + s.AppendFormat(formatString + ", ", array[i]); + } + else + { + s.AppendFormat(formatString, array[i]); + } + } + + return s.ToString(); + } + } +} diff --git a/CubicSpline/CubicSpline.cs b/CubicSpline/CubicSpline.cs new file mode 100644 index 0000000..7a7939d --- /dev/null +++ b/CubicSpline/CubicSpline.cs @@ -0,0 +1,192 @@ +// +// Author: Ryan Seghers +// +// Copyright (C) 2013 Ryan Seghers +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the irrevocable, perpetual, worldwide, and royalty-free +// rights to use, copy, modify, merge, publish, distribute, sublicense, +// display, perform, create derivative works from and/or sell copies of +// the Software, both in source and object code form, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +using System; + +namespace CubicSpline +{ + /// + /// Cubic spline interpolation. + /// Call Fit to compute spline coefficients, then Eval to evaluate the spline at other X coordinates. + /// + /// + /// + /// This is implemented based on the wikipedia article: + /// http://en.wikipedia.org/wiki/Spline_interpolation + /// I'm not sure I have the right to include a copy of the article so the equation numbers referenced in + /// comments will end up being wrong at some point. + /// + /// + /// This is not optimized, and is not MT safe. + /// This can extrapolate off the ends of the splines. + /// You must provide points in X sort order. + /// + /// + public class CubicSpline + { + // N-1 spline coefficients for N points + private float[] a; + private float[] b; + + // Save the original x and y for Eval + private float[] xOrig; + private float[] yOrig; + + /// + /// Fit x,y and then eval at points xs and return the corresponding y's. + /// This does the "natural spline" style for ends. + /// This can extrapolate off the ends of the splines. + /// You must provide points in X sort order. + /// + /// Input. X coordinates to fit. + /// Input. Y coordinates to fit. + /// Input. X coordinates to evaluate the fitted curve at. + /// The computed y values for each xs. + public float[] FitAndEval(float[] x, float[] y, float[] xs, bool debug = false) + { + Fit(x, y, debug); + return Eval(xs, debug); + } + + /// + /// Compute spline coefficients for the specified x,y points. + /// This does the "natural spline" style for ends. + /// This can extrapolate off the ends of the splines. + /// You must provide points in X sort order. + /// + /// Input. X coordinates to fit. + /// Input. Y coordinates to fit. + /// Turn on console output. Default is false. + public void Fit(float[] x, float[] y, bool debug = false) + { + // Save x and y for eval + this.xOrig = x; + this.yOrig = y; + + int n = x.Length; + float[] r = new float[n]; // the right hand side numbers: wikipedia page overloads b + + TriDiagonalMatrixF m = new TriDiagonalMatrixF(n); + float dx1, dx2, dy1, dy2; + + // First row is different (equation 16 from the article) + dx1 = x[1] - x[0]; + m.C[0] = 1.0f / dx1; + m.B[0] = 2.0f * m.C[0]; + r[0] = 3 * (y[1] - y[0]) / (dx1 * dx1); + + // Body rows (equation 15 from the article) + for (int i = 1; i < n - 1; i++) + { + dx1 = x[i] - x[i - 1]; + dx2 = x[i + 1] - x[i]; + + m.A[i] = 1.0f / dx1; + m.C[i] = 1.0f / dx2; + m.B[i] = 2.0f * (m.A[i] + m.C[i]); + + dy1 = y[i] - y[i - 1]; + dy2 = y[i + 1] - y[i]; + r[i] = 3 * (dy1 / (dx1 * dx1) + dy2 / (dx2 * dx2)); + } + + // Last row also different (equation 17 from the article) + dx1 = x[n - 1] - x[n - 2]; + dy1 = y[n - 1] - y[n - 2]; + m.A[n - 1] = 1.0f / dx1; + m.B[n - 1] = 2.0f * m.A[n - 1]; + r[n - 1] = 3 * (dy1 / (dx1 * dx1)); + + if (debug) Console.WriteLine("Tri-diagonal matrix:\n{0}", m.ToDisplayString(":0.0000", " ")); + if (debug) Console.WriteLine("r: {0}", ArrayUtil.ToString(r)); + + // k is the solution to the matrix + float[] k = m.Solve(r); + if (debug) Console.WriteLine("k = {0}", ArrayUtil.ToString(k)); + + // a and b are each spline's coefficients + this.a = new float[n - 1]; + this.b = new float[n - 1]; + + for (int i = 1; i < n; i++) + { + dx1 = x[i] - x[i - 1]; + dy1 = y[i] - y[i - 1]; + a[i - 1] = k[i - 1] * dx1 - dy1; // equation 10 from the article + b[i - 1] = -k[i] * dx1 + dy1; // equation 11 from the article + } + + if (debug) Console.WriteLine("a: {0}", ArrayUtil.ToString(a)); + if (debug) Console.WriteLine("b: {0}", ArrayUtil.ToString(b)); + } + + /// + /// Evaluate the spline at the specified x coordinates. + /// This can extrapolate off the ends of the splines. + /// You must provide X's in ascending order. + /// + /// Input. X coordinates to evaluate the fitted curve at. + /// Turn on console output. Default is false. + /// The computed y values for each x. + public float[] Eval(float[] x, bool debug = false) + { + int n = x.Length; + float[] y = new float[n]; + _lastIndex = 0; // Reset simultaneous traversal in case there are multiple calls + + for (int i = 0; i < n; i++) + { + // Find which spline can be used to compute this x + int j = GetNextXIndex(x[i]); + + // Evaluate using j'th spline + float t = (x[i] - xOrig[j]) / (xOrig[j + 1] - xOrig[j]); + y[i] = (1 - t) * yOrig[j] + t * yOrig[j + 1] + t * (1 - t) * (a[j] * (1 - t) + b[j] * t); // equation 9 + + if (debug) Console.WriteLine("[{0}]: xs = {1}, j = {2}, t = {3}", i, x[i], j, t); + } + + return y; + } + + private int _lastIndex = 0; + + /// + /// Find where in xOrig the specified x falls, by simultaneous traverse. + /// This allows xs to be less than x[0] and/or greater than x[n-1]. So allows extrapolation. + /// This keeps state, so requires that x be sorted and xs called in ascending order. + /// + private int GetNextXIndex(float x) + { + while ((_lastIndex < xOrig.Length - 2) && (x > xOrig[_lastIndex + 1])) + { + _lastIndex++; + } + + return _lastIndex; + } + } +} diff --git a/CubicSpline/CubicSpline/ArrayUtil.cs b/CubicSpline/CubicSpline/ArrayUtil.cs new file mode 100644 index 0000000..330987e --- /dev/null +++ b/CubicSpline/CubicSpline/ArrayUtil.cs @@ -0,0 +1,36 @@ +using System; +using System.Text; + +namespace CubicSpline +{ + /// + /// Utility methods for arrays. + /// + public static class ArrayUtil + { + /// + /// Create a string to display the array values. + /// + /// The array + /// Optional. A string to use to format each value. Must contain the colon, so something like ':0.000' + public static string ToString(T[] array, string format = "") + { + var s = new StringBuilder(); + string formatString = "{0" + format + "}"; + + for (int i = 0; i < array.Length; i++) + { + if (i < array.Length - 1) + { + s.AppendFormat(formatString + ", ", array[i]); + } + else + { + s.AppendFormat(formatString, array[i]); + } + } + + return s.ToString(); + } + } +} diff --git a/CubicSpline/CubicSpline/CubicSpline.cs b/CubicSpline/CubicSpline/CubicSpline.cs new file mode 100644 index 0000000..7a7939d --- /dev/null +++ b/CubicSpline/CubicSpline/CubicSpline.cs @@ -0,0 +1,192 @@ +// +// Author: Ryan Seghers +// +// Copyright (C) 2013 Ryan Seghers +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the irrevocable, perpetual, worldwide, and royalty-free +// rights to use, copy, modify, merge, publish, distribute, sublicense, +// display, perform, create derivative works from and/or sell copies of +// the Software, both in source and object code form, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +using System; + +namespace CubicSpline +{ + /// + /// Cubic spline interpolation. + /// Call Fit to compute spline coefficients, then Eval to evaluate the spline at other X coordinates. + /// + /// + /// + /// This is implemented based on the wikipedia article: + /// http://en.wikipedia.org/wiki/Spline_interpolation + /// I'm not sure I have the right to include a copy of the article so the equation numbers referenced in + /// comments will end up being wrong at some point. + /// + /// + /// This is not optimized, and is not MT safe. + /// This can extrapolate off the ends of the splines. + /// You must provide points in X sort order. + /// + /// + public class CubicSpline + { + // N-1 spline coefficients for N points + private float[] a; + private float[] b; + + // Save the original x and y for Eval + private float[] xOrig; + private float[] yOrig; + + /// + /// Fit x,y and then eval at points xs and return the corresponding y's. + /// This does the "natural spline" style for ends. + /// This can extrapolate off the ends of the splines. + /// You must provide points in X sort order. + /// + /// Input. X coordinates to fit. + /// Input. Y coordinates to fit. + /// Input. X coordinates to evaluate the fitted curve at. + /// The computed y values for each xs. + public float[] FitAndEval(float[] x, float[] y, float[] xs, bool debug = false) + { + Fit(x, y, debug); + return Eval(xs, debug); + } + + /// + /// Compute spline coefficients for the specified x,y points. + /// This does the "natural spline" style for ends. + /// This can extrapolate off the ends of the splines. + /// You must provide points in X sort order. + /// + /// Input. X coordinates to fit. + /// Input. Y coordinates to fit. + /// Turn on console output. Default is false. + public void Fit(float[] x, float[] y, bool debug = false) + { + // Save x and y for eval + this.xOrig = x; + this.yOrig = y; + + int n = x.Length; + float[] r = new float[n]; // the right hand side numbers: wikipedia page overloads b + + TriDiagonalMatrixF m = new TriDiagonalMatrixF(n); + float dx1, dx2, dy1, dy2; + + // First row is different (equation 16 from the article) + dx1 = x[1] - x[0]; + m.C[0] = 1.0f / dx1; + m.B[0] = 2.0f * m.C[0]; + r[0] = 3 * (y[1] - y[0]) / (dx1 * dx1); + + // Body rows (equation 15 from the article) + for (int i = 1; i < n - 1; i++) + { + dx1 = x[i] - x[i - 1]; + dx2 = x[i + 1] - x[i]; + + m.A[i] = 1.0f / dx1; + m.C[i] = 1.0f / dx2; + m.B[i] = 2.0f * (m.A[i] + m.C[i]); + + dy1 = y[i] - y[i - 1]; + dy2 = y[i + 1] - y[i]; + r[i] = 3 * (dy1 / (dx1 * dx1) + dy2 / (dx2 * dx2)); + } + + // Last row also different (equation 17 from the article) + dx1 = x[n - 1] - x[n - 2]; + dy1 = y[n - 1] - y[n - 2]; + m.A[n - 1] = 1.0f / dx1; + m.B[n - 1] = 2.0f * m.A[n - 1]; + r[n - 1] = 3 * (dy1 / (dx1 * dx1)); + + if (debug) Console.WriteLine("Tri-diagonal matrix:\n{0}", m.ToDisplayString(":0.0000", " ")); + if (debug) Console.WriteLine("r: {0}", ArrayUtil.ToString(r)); + + // k is the solution to the matrix + float[] k = m.Solve(r); + if (debug) Console.WriteLine("k = {0}", ArrayUtil.ToString(k)); + + // a and b are each spline's coefficients + this.a = new float[n - 1]; + this.b = new float[n - 1]; + + for (int i = 1; i < n; i++) + { + dx1 = x[i] - x[i - 1]; + dy1 = y[i] - y[i - 1]; + a[i - 1] = k[i - 1] * dx1 - dy1; // equation 10 from the article + b[i - 1] = -k[i] * dx1 + dy1; // equation 11 from the article + } + + if (debug) Console.WriteLine("a: {0}", ArrayUtil.ToString(a)); + if (debug) Console.WriteLine("b: {0}", ArrayUtil.ToString(b)); + } + + /// + /// Evaluate the spline at the specified x coordinates. + /// This can extrapolate off the ends of the splines. + /// You must provide X's in ascending order. + /// + /// Input. X coordinates to evaluate the fitted curve at. + /// Turn on console output. Default is false. + /// The computed y values for each x. + public float[] Eval(float[] x, bool debug = false) + { + int n = x.Length; + float[] y = new float[n]; + _lastIndex = 0; // Reset simultaneous traversal in case there are multiple calls + + for (int i = 0; i < n; i++) + { + // Find which spline can be used to compute this x + int j = GetNextXIndex(x[i]); + + // Evaluate using j'th spline + float t = (x[i] - xOrig[j]) / (xOrig[j + 1] - xOrig[j]); + y[i] = (1 - t) * yOrig[j] + t * yOrig[j + 1] + t * (1 - t) * (a[j] * (1 - t) + b[j] * t); // equation 9 + + if (debug) Console.WriteLine("[{0}]: xs = {1}, j = {2}, t = {3}", i, x[i], j, t); + } + + return y; + } + + private int _lastIndex = 0; + + /// + /// Find where in xOrig the specified x falls, by simultaneous traverse. + /// This allows xs to be less than x[0] and/or greater than x[n-1]. So allows extrapolation. + /// This keeps state, so requires that x be sorted and xs called in ascending order. + /// + private int GetNextXIndex(float x) + { + while ((_lastIndex < xOrig.Length - 2) && (x > xOrig[_lastIndex + 1])) + { + _lastIndex++; + } + + return _lastIndex; + } + } +} diff --git a/CubicSpline/CubicSpline/CubicSpline.csproj b/CubicSpline/CubicSpline/CubicSpline.csproj new file mode 100644 index 0000000..93312ae --- /dev/null +++ b/CubicSpline/CubicSpline/CubicSpline.csproj @@ -0,0 +1,76 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {CD637EDA-E0C3-4ABF-8E24-A5B94892311C} + Library + Properties + CubicSpline + CubicSpline + v4.0 + 512 + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\..\packages\Newtonsoft.Json.12.0.1\lib\net40\Newtonsoft.Json.dll + True + + + + + ..\..\packages\System.Data.SQLite.Core.1.0.110.0\lib\net40\System.Data.SQLite.dll + True + + + + + + + + + + + + + + + + + + + + + Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}". + + + + + \ No newline at end of file diff --git a/CubicSpline/CubicSpline/Properties/AssemblyInfo.cs b/CubicSpline/CubicSpline/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..b539254 --- /dev/null +++ b/CubicSpline/CubicSpline/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Allgemeine Informationen über eine Assembly werden über die folgenden +// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, +// die mit einer Assembly verknüpft sind. +[assembly: AssemblyTitle("CubicSpline")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("CubicSpline")] +[assembly: AssemblyCopyright("Copyright © 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar +// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von +// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest. +[assembly: ComVisible(false)] + +// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird +[assembly: Guid("414c347f-fc36-4041-ad60-ffd33d2df6fc")] + +// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: +// +// Hauptversion +// Nebenversion +// Buildnummer +// Revision +// +// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern +// übernehmen, indem Sie "*" eingeben: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/CubicSpline/CubicSpline/TriDiagonalMatrix.cs b/CubicSpline/CubicSpline/TriDiagonalMatrix.cs new file mode 100644 index 0000000..f5bfd8d --- /dev/null +++ b/CubicSpline/CubicSpline/TriDiagonalMatrix.cs @@ -0,0 +1,214 @@ +// +// Author: Ryan Seghers +// +// Copyright (C) 2013 Ryan Seghers +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the irrevocable, perpetual, worldwide, and royalty-free +// rights to use, copy, modify, merge, publish, distribute, sublicense, +// display, perform, create derivative works from and/or sell copies of +// the Software, both in source and object code form, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +using System; +using System.Diagnostics; +using System.Text; + +namespace CubicSpline +{ + /// + /// A tri-diagonal matrix has non-zero entries only on the main diagonal, the diagonal above the main (super), and the + /// diagonal below the main (sub). + /// + /// + /// + /// This is based on the wikipedia article: http://en.wikipedia.org/wiki/Tridiagonal_matrix_algorithm + /// + /// + /// The entries in the matrix on a particular row are A[i], B[i], and C[i] where i is the row index. + /// B is the main diagonal, and so for an NxN matrix B is length N and all elements are used. + /// So for row 0, the first two values are B[0] and C[0]. + /// And for row N-1, the last two values are A[N-1] and B[N-1]. + /// That means that A[0] is not actually on the matrix and is therefore never used, and same with C[N-1]. + /// + /// + public class TriDiagonalMatrixF + { + /// + /// The values for the sub-diagonal. A[0] is never used. + /// + public float[] A; + + /// + /// The values for the main diagonal. + /// + public float[] B; + + /// + /// The values for the super-diagonal. C[C.Length-1] is never used. + /// + public float[] C; + + /// + /// The width and height of this matrix. + /// + public int N + { + get { return (A != null ? A.Length : 0); } + } + + /// + /// Indexer. Setter throws an exception if you try to set any not on the super, main, or sub diagonals. + /// + public float this[int row, int col] + { + get + { + int di = row - col; + + if (di == 0) + { + return B[row]; + } + else if (di == -1) + { + Debug.Assert(row < N - 1); + return C[row]; + } + else if (di == 1) + { + Debug.Assert(row > 0); + return A[row]; + } + else return 0; + } + set + { + int di = row - col; + + if (di == 0) + { + B[row] = value; + } + else if (di == -1) + { + Debug.Assert(row < N - 1); + C[row] = value; + } + else if (di == 1) + { + Debug.Assert(row > 0); + A[row] = value; + } + else + { + throw new ArgumentException("Only the main, super, and sub diagonals can be set."); + } + } + } + + /// + /// Construct an NxN matrix. + /// + public TriDiagonalMatrixF(int n) + { + this.A = new float[n]; + this.B = new float[n]; + this.C = new float[n]; + } + + /// + /// Produce a string representation of the contents of this matrix. + /// + /// Optional. For String.Format. Must include the colon. Examples are ':0.000' and ',5:0.00' + /// Optional. Per-line indentation prefix. + public string ToDisplayString(string fmt = "", string prefix = "") + { + if (this.N > 0) + { + var s = new StringBuilder(); + string formatString = "{0" + fmt + "}"; + + for (int r = 0; r < N; r++) + { + s.Append(prefix); + + for (int c = 0; c < N; c++) + { + s.AppendFormat(formatString, this[r, c]); + if (c < N - 1) s.Append(", "); + } + + s.AppendLine(); + } + + return s.ToString(); + } + else + { + return prefix + "0x0 Matrix"; + } + } + + /// + /// Solve the system of equations this*x=d given the specified d. + /// + /// + /// Uses the Thomas algorithm described in the wikipedia article: http://en.wikipedia.org/wiki/Tridiagonal_matrix_algorithm + /// Not optimized. Not destructive. + /// + /// Right side of the equation. + public float[] Solve(float[] d) + { + int n = this.N; + + if (d.Length != n) + { + throw new ArgumentException("The input d is not the same size as this matrix."); + } + + // cPrime + float[] cPrime = new float[n]; + cPrime[0] = C[0] / B[0]; + + for (int i = 1; i < n; i++) + { + cPrime[i] = C[i] / (B[i] - cPrime[i-1] * A[i]); + } + + // dPrime + float[] dPrime = new float[n]; + dPrime[0] = d[0] / B[0]; + + for (int i = 1; i < n; i++) + { + dPrime[i] = (d[i] - dPrime[i-1]*A[i]) / (B[i] - cPrime[i - 1] * A[i]); + } + + // Back substitution + float[] x = new float[n]; + x[n - 1] = dPrime[n - 1]; + + for (int i = n-2; i >= 0; i--) + { + x[i] = dPrime[i] - cPrime[i] * x[i + 1]; + } + + return x; + } + } +} diff --git a/CubicSpline/CubicSpline/packages.config b/CubicSpline/CubicSpline/packages.config new file mode 100644 index 0000000..0aee17e --- /dev/null +++ b/CubicSpline/CubicSpline/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/CubicSpline/TestMySpline/App.config b/CubicSpline/TestMySpline/App.config new file mode 100644 index 0000000..58262a1 --- /dev/null +++ b/CubicSpline/TestMySpline/App.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/CubicSpline/TestMySpline/ArrayUtil.cs b/CubicSpline/TestMySpline/ArrayUtil.cs new file mode 100644 index 0000000..9334e07 --- /dev/null +++ b/CubicSpline/TestMySpline/ArrayUtil.cs @@ -0,0 +1,36 @@ +using System; +using System.Text; + +namespace TestMySpline +{ + /// + /// Utility methods for arrays. + /// + public static class ArrayUtil + { + /// + /// Create a string to display the array values. + /// + /// The array + /// Optional. A string to use to format each value. Must contain the colon, so something like ':0.000' + public static string ToString(T[] array, string format = "") + { + var s = new StringBuilder(); + string formatString = "{0" + format + "}"; + + for (int i = 0; i < array.Length; i++) + { + if (i < array.Length - 1) + { + s.AppendFormat(formatString + ", ", array[i]); + } + else + { + s.AppendFormat(formatString, array[i]); + } + } + + return s.ToString(); + } + } +} diff --git a/CubicSpline/TestMySpline/CubicSpline.cs b/CubicSpline/TestMySpline/CubicSpline.cs new file mode 100644 index 0000000..bb2c974 --- /dev/null +++ b/CubicSpline/TestMySpline/CubicSpline.cs @@ -0,0 +1,192 @@ +// +// Author: Ryan Seghers +// +// Copyright (C) 2013 Ryan Seghers +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the irrevocable, perpetual, worldwide, and royalty-free +// rights to use, copy, modify, merge, publish, distribute, sublicense, +// display, perform, create derivative works from and/or sell copies of +// the Software, both in source and object code form, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +using System; + +namespace TestMySpline +{ + /// + /// Cubic spline interpolation. + /// Call Fit to compute spline coefficients, then Eval to evaluate the spline at other X coordinates. + /// + /// + /// + /// This is implemented based on the wikipedia article: + /// http://en.wikipedia.org/wiki/Spline_interpolation + /// I'm not sure I have the right to include a copy of the article so the equation numbers referenced in + /// comments will end up being wrong at some point. + /// + /// + /// This is not optimized, and is not MT safe. + /// This can extrapolate off the ends of the splines. + /// You must provide points in X sort order. + /// + /// + public class CubicSpline + { + // N-1 spline coefficients for N points + private float[] a; + private float[] b; + + // Save the original x and y for Eval + private float[] xOrig; + private float[] yOrig; + + /// + /// Fit x,y and then eval at points xs and return the corresponding y's. + /// This does the "natural spline" style for ends. + /// This can extrapolate off the ends of the splines. + /// You must provide points in X sort order. + /// + /// Input. X coordinates to fit. + /// Input. Y coordinates to fit. + /// Input. X coordinates to evaluate the fitted curve at. + /// The computed y values for each xs. + public float[] FitAndEval(float[] x, float[] y, float[] xs, bool debug = false) + { + Fit(x, y, debug); + return Eval(xs, debug); + } + + /// + /// Compute spline coefficients for the specified x,y points. + /// This does the "natural spline" style for ends. + /// This can extrapolate off the ends of the splines. + /// You must provide points in X sort order. + /// + /// Input. X coordinates to fit. + /// Input. Y coordinates to fit. + /// Turn on console output. Default is false. + public void Fit(float[] x, float[] y, bool debug = false) + { + // Save x and y for eval + this.xOrig = x; + this.yOrig = y; + + int n = x.Length; + float[] r = new float[n]; // the right hand side numbers: wikipedia page overloads b + + TriDiagonalMatrixF m = new TriDiagonalMatrixF(n); + float dx1, dx2, dy1, dy2; + + // First row is different (equation 16 from the article) + dx1 = x[1] - x[0]; + m.C[0] = 1.0f / dx1; + m.B[0] = 2.0f * m.C[0]; + r[0] = 3 * (y[1] - y[0]) / (dx1 * dx1); + + // Body rows (equation 15 from the article) + for (int i = 1; i < n - 1; i++) + { + dx1 = x[i] - x[i - 1]; + dx2 = x[i + 1] - x[i]; + + m.A[i] = 1.0f / dx1; + m.C[i] = 1.0f / dx2; + m.B[i] = 2.0f * (m.A[i] + m.C[i]); + + dy1 = y[i] - y[i - 1]; + dy2 = y[i + 1] - y[i]; + r[i] = 3 * (dy1 / (dx1 * dx1) + dy2 / (dx2 * dx2)); + } + + // Last row also different (equation 17 from the article) + dx1 = x[n - 1] - x[n - 2]; + dy1 = y[n - 1] - y[n - 2]; + m.A[n - 1] = 1.0f / dx1; + m.B[n - 1] = 2.0f * m.A[n - 1]; + r[n - 1] = 3 * (dy1 / (dx1 * dx1)); + + if (debug) Console.WriteLine("Tri-diagonal matrix:\n{0}", m.ToDisplayString(":0.0000", " ")); + if (debug) Console.WriteLine("r: {0}", ArrayUtil.ToString(r)); + + // k is the solution to the matrix + float[] k = m.Solve(r); + if (debug) Console.WriteLine("k = {0}", ArrayUtil.ToString(k)); + + // a and b are each spline's coefficients + this.a = new float[n - 1]; + this.b = new float[n - 1]; + + for (int i = 1; i < n; i++) + { + dx1 = x[i] - x[i - 1]; + dy1 = y[i] - y[i - 1]; + a[i - 1] = k[i - 1] * dx1 - dy1; // equation 10 from the article + b[i - 1] = -k[i] * dx1 + dy1; // equation 11 from the article + } + + if (debug) Console.WriteLine("a: {0}", ArrayUtil.ToString(a)); + if (debug) Console.WriteLine("b: {0}", ArrayUtil.ToString(b)); + } + + /// + /// Evaluate the spline at the specified x coordinates. + /// This can extrapolate off the ends of the splines. + /// You must provide X's in ascending order. + /// + /// Input. X coordinates to evaluate the fitted curve at. + /// Turn on console output. Default is false. + /// The computed y values for each x. + public float[] Eval(float[] x, bool debug = false) + { + int n = x.Length; + float[] y = new float[n]; + _lastIndex = 0; // Reset simultaneous traversal in case there are multiple calls + + for (int i = 0; i < n; i++) + { + // Find which spline can be used to compute this x + int j = GetNextXIndex(x[i]); + + // Evaluate using j'th spline + float t = (x[i] - xOrig[j]) / (xOrig[j + 1] - xOrig[j]); + y[i] = (1 - t) * yOrig[j] + t * yOrig[j + 1] + t * (1 - t) * (a[j] * (1 - t) + b[j] * t); // equation 9 + + if (debug) Console.WriteLine("[{0}]: xs = {1}, j = {2}, t = {3}", i, x[i], j, t); + } + + return y; + } + + private int _lastIndex = 0; + + /// + /// Find where in xOrig the specified x falls, by simultaneous traverse. + /// This allows xs to be less than x[0] and/or greater than x[n-1]. So allows extrapolation. + /// This keeps state, so requires that x be sorted and xs called in ascending order. + /// + private int GetNextXIndex(float x) + { + while ((_lastIndex < xOrig.Length - 2) && (x > xOrig[_lastIndex + 1])) + { + _lastIndex++; + } + + return _lastIndex; + } + } +} diff --git a/CubicSpline/TestMySpline/Program.cs b/CubicSpline/TestMySpline/Program.cs new file mode 100644 index 0000000..3a3262f --- /dev/null +++ b/CubicSpline/TestMySpline/Program.cs @@ -0,0 +1,273 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms.DataVisualization.Charting; + +namespace TestMySpline +{ + class Program + { + static void Main(string[] args) + { + //TestTdm(); + TestSplineOnWikipediaExample(); + //TestSpline(); + //TestPerf(); + } + + private static void TestSpline() + { + int n = 6; + + // Create the data to be fitted + float[] x = new float[n]; + float[] y = new float[n]; + Random rand = new Random(1); + + for (int i = 0; i < n; i++) + { + x[i] = i; + y[i] = (float)rand.NextDouble() * 10; + } + + // Compute the x values that we will evaluate the spline at. + // Upsample the original data by a const factor. + int upsampleFactor = 10; + int nInterpolated = n * upsampleFactor; + float[] xs = new float[nInterpolated]; + + for (int i = 0; i < nInterpolated; i++) + { + xs[i] = (float)i * (n - 1) / (float)(nInterpolated - 1); + } + + CubicSpline spline = new CubicSpline(); + float[] ys = spline.FitAndEval(x, y, xs, true); + string path = @"..\..\testSpline.png"; + PlotSplineSolution("Cubic Spline Interpolation - Random Data", x, y, xs, ys, path); + } + + private static void TestPerf() + { + int n = 10000; + + // Create the data to be fitted + float[] x = new float[n]; + float[] y = new float[n]; + Random rand = new Random(1); + + for (int i = 0; i < n; i++) + { + x[i] = i; + y[i] = (float)rand.NextDouble() * 10; + } + + // Compute the x values that we will evaluate the spline at. + // Upsample the original data by a const factor. + int upsampleFactor = 10; + int nInterpolate = n * upsampleFactor; + float[] xs = new float[nInterpolate]; + + for (int i = 0; i < nInterpolate; i++) + { + xs[i] = (float)i / upsampleFactor; + } + + // For perf, test multiple reps + int reps = 100; + DateTime start = DateTime.Now; + + for (int i = 0; i < reps; i++) + { + CubicSpline spline = new CubicSpline(); + float[] ys = spline.FitAndEval(x, y, xs, false); + } + + TimeSpan duration = DateTime.Now - start; + Console.WriteLine("CubicSpline upsample from {0:n0} to {1:n0} points took {2:0.00} ms for {3} iterations ({2:0.000} ms per iteration)", + n, nInterpolate, duration.TotalMilliseconds, reps, duration.TotalMilliseconds / reps); + + // Compare to NRinC + //float[] y2 = new float[n]; + //float[] ys2 = new float[nInterpolate]; + //start = DateTime.Now; + + //for (int i = 0; i < reps; i++) + //{ + // CubicSplineNR.Spline(x, y, y2); + // CubicSplineNR.EvalSpline(x, y, y2, xs, ys2); + //} + + //duration = DateTime.Now - start; + //Console.WriteLine("CubicSplineNR upsample from {0:n0} to {1:n0} points took {2:0.00} ms for {3} iterations ({2:0.000} ms per iteration)", + // n, nInterpolate, duration.TotalMilliseconds, reps, duration.TotalMilliseconds / reps); + } + + /// + /// This is the Wikipedia "Spline Interpolation" article example. + /// + private static void TestSplineOnWikipediaExample() + { + // Create the test points. + float[] x = new float[] { -1.0f, 0.0f, 3.0f }; + float[] y = new float[] { 0.5f, 0.0f, 3.0f }; + + Console.WriteLine("x: {0}", ArrayUtil.ToString(x)); + Console.WriteLine("y: {0}", ArrayUtil.ToString(y)); + + // Create the upsampled X values to interpolate + int n = 20; + float[] xs = new float[n]; + float stepSize = (x[x.Length - 1] - x[0]) / (n - 1); + + for (int i = 0; i < n; i++) + { + xs[i] = x[0] + i * stepSize; + } + + // Solve + CubicSpline spline = new CubicSpline(); + float[] ys = spline.FitAndEval(x, y, xs, false); + + Console.WriteLine("xs: {0}", ArrayUtil.ToString(xs)); + Console.WriteLine("ys: {0}", ArrayUtil.ToString(ys)); + + // Plot + string path = @"..\..\spline-wikipedia.png"; + PlotSplineSolution("Cubic Spline Interpolation - Wikipedia Example", x, y, xs, ys, path); + } + + private static TriDiagonalMatrixF TestTdm() + { + TriDiagonalMatrixF m = new TriDiagonalMatrixF(10); + + for (int i = 0; i < m.N; i++) + { + m.A[i] = 1.111111f; + m.B[i] = 2.222222f; + m.C[i] = 3.333333f; + } + + Console.WriteLine("Matrix:\n{0}", m.ToDisplayString(",4:0.00", " ")); + + for (int i = 0; i < m.N; i++) + { + m[i, i] = 4.4444f; + } + + Console.WriteLine("Matrix:\n{0}", m.ToDisplayString(",4:0.00", " ")); + + // Solve + Random rand = new Random(1); + float[] d = new float[m.N]; + + for (int i = 0; i < d.Length; i++) + { + d[i] = (float)rand.NextDouble(); + } + + float[] x = m.Solve(d); + + Console.WriteLine("Solve returned: "); + + for (int i = 0; i < x.Length; i++) + { + Console.Write("{0:0.0000}, ", x[i]); + } + + Console.WriteLine(); + return m; + } + + #region PlotSplineSolution + + private static void PlotSplineSolution(string title, float[] x, float[] y, float[] xs, float[] ys, string path) + { + List points = new List(); + + for (int i = 0; i < x.Length; i++) + { + points.Add(new DataPoint(x[i], y[i])); + } + + var chart = new Chart(); + chart.Size = new Size(600, 400); + chart.Titles.Add(title); + string legendName = "Legend"; + chart.Legends.Add(new Legend(legendName)); + + ChartArea ca = new ChartArea("DefaultChartArea"); + ca.AxisX.Title = "X"; + ca.AxisY.Title = "Y"; + chart.ChartAreas.Add(ca); + + Series s1 = CreateSeries(chart, "Spline", CreateDataPoints(xs, ys), Color.Blue, MarkerStyle.None); + Series s2 = CreateSeries(chart, "Original", CreateDataPoints(x, y), Color.Green, MarkerStyle.Diamond); + + chart.Series.Add(s1); + chart.Series.Add(s2); + + ca.RecalculateAxesScale(); + ca.AxisX.Minimum = Math.Round(ca.AxisX.Minimum); + ca.AxisX.Maximum = Math.Round(ca.AxisX.Maximum); + int nIntervals = (x.Length - 1); + nIntervals = Math.Max(4, nIntervals); + ca.AxisX.Interval = (ca.AxisX.Maximum - ca.AxisX.Minimum) / nIntervals; + + // Save + if (File.Exists(path)) + { + File.Delete(path); + } + + using (FileStream fs = new FileStream(path, FileMode.CreateNew)) + { + chart.SaveImage(fs, ChartImageFormat.Png); + } + } + + private static List CreateDataPoints(float[] x, float[] y) + { + Debug.Assert(x.Length == y.Length); + List points = new List(); + + for (int i = 0; i < x.Length; i++) + { + points.Add(new DataPoint(x[i], y[i])); + } + + return points; + } + + private static Series CreateSeries(Chart chart, string seriesName, IEnumerable points, Color color, MarkerStyle markerStyle = MarkerStyle.None) + { + var s = new Series() + { + XValueType = ChartValueType.Double, + YValueType = ChartValueType.Double, + Legend = chart.Legends[0].Name, + IsVisibleInLegend = true, + ChartType = SeriesChartType.Line, + Name = seriesName, + ChartArea = chart.ChartAreas[0].Name, + MarkerStyle = markerStyle, + Color = color, + MarkerSize = 8 + }; + + foreach (var p in points) + { + s.Points.Add(p); + } + + return s; + } + + #endregion + } +} diff --git a/CubicSpline/TestMySpline/Properties/AssemblyInfo.cs b/CubicSpline/TestMySpline/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..3cbec37 --- /dev/null +++ b/CubicSpline/TestMySpline/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +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("TestMySpline")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("TestMySpline")] +[assembly: AssemblyCopyright("Copyright © 2013")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("9d330dcf-f736-4604-86ce-f13e9ebfba9f")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// 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("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/CubicSpline/TestMySpline/TestMySpline.csproj b/CubicSpline/TestMySpline/TestMySpline.csproj new file mode 100644 index 0000000..5177ea3 --- /dev/null +++ b/CubicSpline/TestMySpline/TestMySpline.csproj @@ -0,0 +1,65 @@ + + + + + Debug + AnyCPU + {2449264D-0767-4C0F-8A6B-C8045546A2A4} + Exe + Properties + TestMySpline + TestMySpline + v4.0 + 512 + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CubicSpline/TestMySpline/TestMySpline.sln b/CubicSpline/TestMySpline/TestMySpline.sln new file mode 100644 index 0000000..4a49771 --- /dev/null +++ b/CubicSpline/TestMySpline/TestMySpline.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestMySpline", "TestMySpline.csproj", "{2449264D-0767-4C0F-8A6B-C8045546A2A4}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2449264D-0767-4C0F-8A6B-C8045546A2A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2449264D-0767-4C0F-8A6B-C8045546A2A4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2449264D-0767-4C0F-8A6B-C8045546A2A4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2449264D-0767-4C0F-8A6B-C8045546A2A4}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/CubicSpline/TestMySpline/TriDiagonalMatrix.cs b/CubicSpline/TestMySpline/TriDiagonalMatrix.cs new file mode 100644 index 0000000..fb50e28 --- /dev/null +++ b/CubicSpline/TestMySpline/TriDiagonalMatrix.cs @@ -0,0 +1,214 @@ +// +// Author: Ryan Seghers +// +// Copyright (C) 2013 Ryan Seghers +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the irrevocable, perpetual, worldwide, and royalty-free +// rights to use, copy, modify, merge, publish, distribute, sublicense, +// display, perform, create derivative works from and/or sell copies of +// the Software, both in source and object code form, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +using System; +using System.Diagnostics; +using System.Text; + +namespace TestMySpline +{ + /// + /// A tri-diagonal matrix has non-zero entries only on the main diagonal, the diagonal above the main (super), and the + /// diagonal below the main (sub). + /// + /// + /// + /// This is based on the wikipedia article: http://en.wikipedia.org/wiki/Tridiagonal_matrix_algorithm + /// + /// + /// The entries in the matrix on a particular row are A[i], B[i], and C[i] where i is the row index. + /// B is the main diagonal, and so for an NxN matrix B is length N and all elements are used. + /// So for row 0, the first two values are B[0] and C[0]. + /// And for row N-1, the last two values are A[N-1] and B[N-1]. + /// That means that A[0] is not actually on the matrix and is therefore never used, and same with C[N-1]. + /// + /// + public class TriDiagonalMatrixF + { + /// + /// The values for the sub-diagonal. A[0] is never used. + /// + public float[] A; + + /// + /// The values for the main diagonal. + /// + public float[] B; + + /// + /// The values for the super-diagonal. C[C.Length-1] is never used. + /// + public float[] C; + + /// + /// The width and height of this matrix. + /// + public int N + { + get { return (A != null ? A.Length : 0); } + } + + /// + /// Indexer. Setter throws an exception if you try to set any not on the super, main, or sub diagonals. + /// + public float this[int row, int col] + { + get + { + int di = row - col; + + if (di == 0) + { + return B[row]; + } + else if (di == -1) + { + Debug.Assert(row < N - 1); + return C[row]; + } + else if (di == 1) + { + Debug.Assert(row > 0); + return A[row]; + } + else return 0; + } + set + { + int di = row - col; + + if (di == 0) + { + B[row] = value; + } + else if (di == -1) + { + Debug.Assert(row < N - 1); + C[row] = value; + } + else if (di == 1) + { + Debug.Assert(row > 0); + A[row] = value; + } + else + { + throw new ArgumentException("Only the main, super, and sub diagonals can be set."); + } + } + } + + /// + /// Construct an NxN matrix. + /// + public TriDiagonalMatrixF(int n) + { + this.A = new float[n]; + this.B = new float[n]; + this.C = new float[n]; + } + + /// + /// Produce a string representation of the contents of this matrix. + /// + /// Optional. For String.Format. Must include the colon. Examples are ':0.000' and ',5:0.00' + /// Optional. Per-line indentation prefix. + public string ToDisplayString(string fmt = "", string prefix = "") + { + if (this.N > 0) + { + var s = new StringBuilder(); + string formatString = "{0" + fmt + "}"; + + for (int r = 0; r < N; r++) + { + s.Append(prefix); + + for (int c = 0; c < N; c++) + { + s.AppendFormat(formatString, this[r, c]); + if (c < N - 1) s.Append(", "); + } + + s.AppendLine(); + } + + return s.ToString(); + } + else + { + return prefix + "0x0 Matrix"; + } + } + + /// + /// Solve the system of equations this*x=d given the specified d. + /// + /// + /// Uses the Thomas algorithm described in the wikipedia article: http://en.wikipedia.org/wiki/Tridiagonal_matrix_algorithm + /// Not optimized. Not destructive. + /// + /// Right side of the equation. + public float[] Solve(float[] d) + { + int n = this.N; + + if (d.Length != n) + { + throw new ArgumentException("The input d is not the same size as this matrix."); + } + + // cPrime + float[] cPrime = new float[n]; + cPrime[0] = C[0] / B[0]; + + for (int i = 1; i < n; i++) + { + cPrime[i] = C[i] / (B[i] - cPrime[i-1] * A[i]); + } + + // dPrime + float[] dPrime = new float[n]; + dPrime[0] = d[0] / B[0]; + + for (int i = 1; i < n; i++) + { + dPrime[i] = (d[i] - dPrime[i-1]*A[i]) / (B[i] - cPrime[i - 1] * A[i]); + } + + // Back substitution + float[] x = new float[n]; + x[n - 1] = dPrime[n - 1]; + + for (int i = n-2; i >= 0; i--) + { + x[i] = dPrime[i] - cPrime[i] * x[i + 1]; + } + + return x; + } + } +} diff --git a/CubicSpline/TestMySpline_2013-03-08.zip b/CubicSpline/TestMySpline_2013-03-08.zip new file mode 100644 index 0000000..5ffccab Binary files /dev/null and b/CubicSpline/TestMySpline_2013-03-08.zip differ diff --git a/CubicSpline/TriDiagonalMatrix.cs b/CubicSpline/TriDiagonalMatrix.cs new file mode 100644 index 0000000..f5bfd8d --- /dev/null +++ b/CubicSpline/TriDiagonalMatrix.cs @@ -0,0 +1,214 @@ +// +// Author: Ryan Seghers +// +// Copyright (C) 2013 Ryan Seghers +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the irrevocable, perpetual, worldwide, and royalty-free +// rights to use, copy, modify, merge, publish, distribute, sublicense, +// display, perform, create derivative works from and/or sell copies of +// the Software, both in source and object code form, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +using System; +using System.Diagnostics; +using System.Text; + +namespace CubicSpline +{ + /// + /// A tri-diagonal matrix has non-zero entries only on the main diagonal, the diagonal above the main (super), and the + /// diagonal below the main (sub). + /// + /// + /// + /// This is based on the wikipedia article: http://en.wikipedia.org/wiki/Tridiagonal_matrix_algorithm + /// + /// + /// The entries in the matrix on a particular row are A[i], B[i], and C[i] where i is the row index. + /// B is the main diagonal, and so for an NxN matrix B is length N and all elements are used. + /// So for row 0, the first two values are B[0] and C[0]. + /// And for row N-1, the last two values are A[N-1] and B[N-1]. + /// That means that A[0] is not actually on the matrix and is therefore never used, and same with C[N-1]. + /// + /// + public class TriDiagonalMatrixF + { + /// + /// The values for the sub-diagonal. A[0] is never used. + /// + public float[] A; + + /// + /// The values for the main diagonal. + /// + public float[] B; + + /// + /// The values for the super-diagonal. C[C.Length-1] is never used. + /// + public float[] C; + + /// + /// The width and height of this matrix. + /// + public int N + { + get { return (A != null ? A.Length : 0); } + } + + /// + /// Indexer. Setter throws an exception if you try to set any not on the super, main, or sub diagonals. + /// + public float this[int row, int col] + { + get + { + int di = row - col; + + if (di == 0) + { + return B[row]; + } + else if (di == -1) + { + Debug.Assert(row < N - 1); + return C[row]; + } + else if (di == 1) + { + Debug.Assert(row > 0); + return A[row]; + } + else return 0; + } + set + { + int di = row - col; + + if (di == 0) + { + B[row] = value; + } + else if (di == -1) + { + Debug.Assert(row < N - 1); + C[row] = value; + } + else if (di == 1) + { + Debug.Assert(row > 0); + A[row] = value; + } + else + { + throw new ArgumentException("Only the main, super, and sub diagonals can be set."); + } + } + } + + /// + /// Construct an NxN matrix. + /// + public TriDiagonalMatrixF(int n) + { + this.A = new float[n]; + this.B = new float[n]; + this.C = new float[n]; + } + + /// + /// Produce a string representation of the contents of this matrix. + /// + /// Optional. For String.Format. Must include the colon. Examples are ':0.000' and ',5:0.00' + /// Optional. Per-line indentation prefix. + public string ToDisplayString(string fmt = "", string prefix = "") + { + if (this.N > 0) + { + var s = new StringBuilder(); + string formatString = "{0" + fmt + "}"; + + for (int r = 0; r < N; r++) + { + s.Append(prefix); + + for (int c = 0; c < N; c++) + { + s.AppendFormat(formatString, this[r, c]); + if (c < N - 1) s.Append(", "); + } + + s.AppendLine(); + } + + return s.ToString(); + } + else + { + return prefix + "0x0 Matrix"; + } + } + + /// + /// Solve the system of equations this*x=d given the specified d. + /// + /// + /// Uses the Thomas algorithm described in the wikipedia article: http://en.wikipedia.org/wiki/Tridiagonal_matrix_algorithm + /// Not optimized. Not destructive. + /// + /// Right side of the equation. + public float[] Solve(float[] d) + { + int n = this.N; + + if (d.Length != n) + { + throw new ArgumentException("The input d is not the same size as this matrix."); + } + + // cPrime + float[] cPrime = new float[n]; + cPrime[0] = C[0] / B[0]; + + for (int i = 1; i < n; i++) + { + cPrime[i] = C[i] / (B[i] - cPrime[i-1] * A[i]); + } + + // dPrime + float[] dPrime = new float[n]; + dPrime[0] = d[0] / B[0]; + + for (int i = 1; i < n; i++) + { + dPrime[i] = (d[i] - dPrime[i-1]*A[i]) / (B[i] - cPrime[i - 1] * A[i]); + } + + // Back substitution + float[] x = new float[n]; + x[n - 1] = dPrime[n - 1]; + + for (int i = n-2; i >= 0; i--) + { + x[i] = dPrime[i] - cPrime[i] * x[i + 1]; + } + + return x; + } + } +} diff --git a/CustomScrollBar/CustomScrollBar.csproj b/CustomScrollBar/CustomScrollBar.csproj new file mode 100644 index 0000000..a10fef3 --- /dev/null +++ b/CustomScrollBar/CustomScrollBar.csproj @@ -0,0 +1,103 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {80C7DC34-E1F6-4DA8-AFFB-904C06A3623B} + Library + Properties + CustomScrollBar + CustomScrollBar + v2.0 + 512 + + + + + 3.5 + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Newtonsoft.Json.12.0.1\lib\net20\Newtonsoft.Json.dll + True + + + + ..\packages\System.Data.SQLite.Core.1.0.110.0\lib\net20\System.Data.SQLite.dll + True + + + + + + + + + + + Component + + + + + True + True + ScrollBarResources.resx + + + + + + ScrollBarEx.cs + + + ResXFileCodeGenerator + ScrollBarResources.Designer.cs + + + + + + + + + + + + + + + + Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}". + + + + + \ No newline at end of file diff --git a/CustomScrollBar/Images/GripNormal.png b/CustomScrollBar/Images/GripNormal.png new file mode 100644 index 0000000..758e1cf Binary files /dev/null and b/CustomScrollBar/Images/GripNormal.png differ diff --git a/CustomScrollBar/Images/ScrollBarArrowDown.png b/CustomScrollBar/Images/ScrollBarArrowDown.png new file mode 100644 index 0000000..81556dd Binary files /dev/null and b/CustomScrollBar/Images/ScrollBarArrowDown.png differ diff --git a/CustomScrollBar/Properties/AssemblyInfo.cs b/CustomScrollBar/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..437d1ed --- /dev/null +++ b/CustomScrollBar/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Allgemeine Informationen über eine Assembly werden über die folgenden +// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, +// die mit einer Assembly verknüpft sind. +[assembly: AssemblyTitle("CustomScrollBar")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("User")] +[assembly: AssemblyProduct("CustomScrollBar")] +[assembly: AssemblyCopyright("Copyright © User 2009")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar +// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von +// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest. +[assembly: ComVisible(false)] + +// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird +[assembly: Guid("6570a0df-033c-4bba-8d24-254a07e29738")] + +// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: +// +// Hauptversion +// Nebenversion +// Buildnummer +// Revision +// +// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern +// übernehmen, indem Sie "*" eingeben: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/CustomScrollBar/Properties/ScrollBarResources.Designer.cs b/CustomScrollBar/Properties/ScrollBarResources.Designer.cs new file mode 100644 index 0000000..e419a2b --- /dev/null +++ b/CustomScrollBar/Properties/ScrollBarResources.Designer.cs @@ -0,0 +1,83 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.42000 +// +// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn +// der Code erneut generiert wird. +// +//------------------------------------------------------------------------------ + +namespace CustomScrollBar.Properties { + using System; + + + /// + /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw. + /// + // Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert + // -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert. + // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen + // mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class ScrollBarResources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal ScrollBarResources() { + } + + /// + /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CustomScrollBar.Properties.ScrollBarResources", typeof(ScrollBarResources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle + /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap GripNormal { + get { + object obj = ResourceManager.GetObject("GripNormal", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap ScrollBarArrowDown { + get { + object obj = ResourceManager.GetObject("ScrollBarArrowDown", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/CustomScrollBar/Properties/ScrollBarResources.resx b/CustomScrollBar/Properties/ScrollBarResources.resx new file mode 100644 index 0000000..f5b94b0 --- /dev/null +++ b/CustomScrollBar/Properties/ScrollBarResources.resx @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Images\GripNormal.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Images\ScrollBarArrowDown.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/CustomScrollBar/ScrollBarArrowButtonState.cs b/CustomScrollBar/ScrollBarArrowButtonState.cs new file mode 100644 index 0000000..a7d97c6 --- /dev/null +++ b/CustomScrollBar/ScrollBarArrowButtonState.cs @@ -0,0 +1,58 @@ +namespace CustomScrollBar +{ + /// + /// The scrollbar arrow button states. + /// + internal enum ScrollBarArrowButtonState + { + /// + /// Indicates the up arrow is in normal state. + /// + UpNormal, + + /// + /// Indicates the up arrow is in hot state. + /// + UpHot, + + /// + /// Indicates the up arrow is in active state. + /// + UpActive, + + /// + /// Indicates the up arrow is in pressed state. + /// + UpPressed, + + /// + /// Indicates the up arrow is in disabled state. + /// + UpDisabled, + + /// + /// Indicates the down arrow is in normal state. + /// + DownNormal, + + /// + /// Indicates the down arrow is in hot state. + /// + DownHot, + + /// + /// Indicates the down arrow is in active state. + /// + DownActive, + + /// + /// Indicates the down arrow is in pressed state. + /// + DownPressed, + + /// + /// Indicates the down arrow is in disabled state. + /// + DownDisabled + } +} diff --git a/CustomScrollBar/ScrollBarControlDesigner.cs b/CustomScrollBar/ScrollBarControlDesigner.cs new file mode 100644 index 0000000..8ac8deb --- /dev/null +++ b/CustomScrollBar/ScrollBarControlDesigner.cs @@ -0,0 +1,67 @@ +namespace CustomScrollBar.Design +{ + using System.ComponentModel; + using System.Windows.Forms.Design; + + /// + /// The designer for the control. + /// + internal class ScrollBarControlDesigner : ControlDesigner + { + /// + /// Gets the for the control. + /// + public override SelectionRules SelectionRules + { + get + { + // gets the property descriptor for the property "Orientation" + PropertyDescriptor propDescriptor = + TypeDescriptor.GetProperties(this.Component)["Orientation"]; + + // if not null - we can read the current orientation of the scroll bar + if (propDescriptor != null) + { + // get the current orientation + ScrollBarOrientation orientation = + (ScrollBarOrientation) propDescriptor.GetValue(this.Component); + + // if vertical orientation + if (orientation == ScrollBarOrientation.Vertical) + { + return SelectionRules.Visible + | SelectionRules.Moveable + | SelectionRules.BottomSizeable + | SelectionRules.TopSizeable; + } + + return SelectionRules.Visible | SelectionRules.Moveable + | SelectionRules.LeftSizeable | SelectionRules.RightSizeable; + } + + return base.SelectionRules; + } + } + + /// + /// Prefilters the properties so that unnecessary properties are hidden + /// in the property browser of Visual Studio. + /// + /// The property dictionary. + protected override void PreFilterProperties( + System.Collections.IDictionary properties) + { + properties.Remove("Text"); + properties.Remove("BackgroundImage"); + properties.Remove("ForeColor"); + properties.Remove("ImeMode"); + properties.Remove("Padding"); + properties.Remove("BackgroundImageLayout"); + properties.Remove("BackColor"); + properties.Remove("Font"); + properties.Remove("RightToLeft"); + + base.PreFilterProperties(properties); + } + } +} diff --git a/CustomScrollBar/ScrollBarEx.cd b/CustomScrollBar/ScrollBarEx.cd new file mode 100644 index 0000000..31756ac --- /dev/null +++ b/CustomScrollBar/ScrollBarEx.cd @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + xwLZOI+MYQRBgCAhYCLBEIFqARgDeiQCYIGggIJnAEI= + ScrollBarEx.cs + + + + + + + + + + SAAABIAQAAAiCAAABAIACACAAgABAIAAAAAGAAAAoAQ= + ScrollBarExRenderer.cs + + + + + + AAAAAEAAAAAAAABCBAACAQAAAAAAAAAIAAAAAAAAIRA= + ScrollBarArrowButtonState.cs + + + + + + AAAAAAAAAABAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAA= + ScrollBarOrientation.cs + + + + + + AAAAAAAAAAAAAAAAAACAAAAAAIAIAAAgAAAAAAAEAAA= + ScrollBarState.cs + + + + \ No newline at end of file diff --git a/CustomScrollBar/ScrollBarEx.cs b/CustomScrollBar/ScrollBarEx.cs new file mode 100644 index 0000000..7b58f9a --- /dev/null +++ b/CustomScrollBar/ScrollBarEx.cs @@ -0,0 +1,1897 @@ +namespace CustomScrollBar +{ + using System; + using System.Collections.Generic; + using System.ComponentModel; + using System.Drawing; + using System.Runtime.InteropServices; + using System.Windows.Forms; + + /// + /// A custom scrollbar control. + /// + [Designer(typeof(Design.ScrollBarControlDesigner))] + [DefaultEvent("Scroll")] + [DefaultProperty("Value")] + public class ScrollBarEx : Control + { + #region fields + + /// + /// Redraw const. + /// + private const int SETREDRAW = 11; + + /// + /// Indicates many changes to the scrollbar are happening, so stop painting till finished. + /// + private bool inUpdate; + + /// + /// The scrollbar orientation - horizontal / vertical. + /// + private ScrollBarOrientation orientation = ScrollBarOrientation.Vertical; + + /// + /// The scroll orientation in scroll events. + /// + private ScrollOrientation scrollOrientation = ScrollOrientation.VerticalScroll; + + /// + /// The clicked channel rectangle. + /// + private Rectangle clickedBarRectangle; + + /// + /// The thumb rectangle. + /// + private Rectangle thumbRectangle; + + /// + /// The top arrow rectangle. + /// + private Rectangle topArrowRectangle; + + /// + /// The bottom arrow rectangle. + /// + private Rectangle bottomArrowRectangle; + + /// + /// The channel rectangle. + /// + private Rectangle channelRectangle; + + /// + /// Indicates if top arrow was clicked. + /// + private bool topArrowClicked; + + /// + /// Indicates if bottom arrow was clicked. + /// + private bool bottomArrowClicked; + + /// + /// Indicates if channel rectangle above the thumb was clicked. + /// + private bool topBarClicked; + + /// + /// Indicates if channel rectangle under the thumb was clicked. + /// + private bool bottomBarClicked; + + /// + /// Indicates if the thumb was clicked. + /// + private bool thumbClicked; + + /// + /// The state of the thumb. + /// + private ScrollBarState thumbState = ScrollBarState.Normal; + + /// + /// The state of the top arrow. + /// + private ScrollBarArrowButtonState topButtonState = ScrollBarArrowButtonState.UpNormal; + + /// + /// The state of the bottom arrow. + /// + private ScrollBarArrowButtonState bottomButtonState = ScrollBarArrowButtonState.DownNormal; + + /// + /// The scrollbar value minimum. + /// + private int minimum; + + /// + /// The scrollbar value maximum. + /// + private int maximum = 100; + + /// + /// The small change value. + /// + private int smallChange = 1; + + /// + /// The large change value. + /// + private int largeChange = 10; + + /// + /// The value of the scrollbar. + /// + private int value; + + /// + /// The width of the thumb. + /// + private int thumbWidth = 15; + + /// + /// The height of the thumb. + /// + private int thumbHeight; + + /// + /// The width of an arrow. + /// + private int arrowWidth = 15; + + /// + /// The height of an arrow. + /// + private int arrowHeight = 17; + + /// + /// The bottom limit for the thumb bottom. + /// + private int thumbBottomLimitBottom; + + /// + /// The bottom limit for the thumb top. + /// + private int thumbBottomLimitTop; + + /// + /// The top limit for the thumb top. + /// + private int thumbTopLimit; + + /// + /// The current position of the thumb. + /// + private int thumbPosition; + + /// + /// The track position. + /// + private int trackPosition; + + /// + /// The progress timer for moving the thumb. + /// + private Timer progressTimer = new Timer(); + + /// + /// The border color. + /// + private Color borderColor = Color.FromArgb(93, 140, 201); + + /// + /// The border color in disabled state. + /// + private Color disabledBorderColor = Color.Gray; + + /// + /// The list of background markers. + /// + private List backgroundmarkers; + + /// + /// The color of background markers. + /// + private Color backgroundmarkercolor = Color.DarkGray; + + #region context menu items + + /// + /// Context menu strip. + /// + private ContextMenuStrip contextMenu; + + /// + /// Container for components. + /// + private IContainer components; + + /// + /// Menu item. + /// + private ToolStripMenuItem tsmiScrollHere; + + /// + /// Menu separator. + /// + private ToolStripSeparator toolStripSeparator1; + + /// + /// Menu item. + /// + private ToolStripMenuItem tsmiTop; + + /// + /// Menu item. + /// + private ToolStripMenuItem tsmiBottom; + + /// + /// Menu separator. + /// + private ToolStripSeparator toolStripSeparator2; + + /// + /// Menu item. + /// + private ToolStripMenuItem tsmiLargeUp; + + /// + /// Menu item. + /// + private ToolStripMenuItem tsmiLargeDown; + + /// + /// Menu separator. + /// + private ToolStripSeparator toolStripSeparator3; + + /// + /// Menu item. + /// + private ToolStripMenuItem tsmiSmallUp; + + /// + /// Menu item. + /// + private ToolStripMenuItem tsmiSmallDown; + + #endregion + + #endregion + + #region constructor + + /// + /// Initializes a new instance of the class. + /// + public ScrollBarEx() + { + // sets the control styles of the control + SetStyle( + ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw + | ControlStyles.Selectable | ControlStyles.AllPaintingInWmPaint + | ControlStyles.UserPaint, true); + + // initializes the context menu + this.InitializeComponent(); + + this.Width = 19; + this.Height = 200; + + // sets the scrollbar up + this.SetUpScrollBar(); + + // timer for clicking and holding the mouse button + // over/below the thumb and on the arrow buttons + this.progressTimer.Interval = 20; + this.progressTimer.Tick += this.ProgressTimerTick; + + // no image margin in context menu + this.contextMenu.ShowImageMargin = false; + this.ContextMenuStrip = this.contextMenu; + } + + #endregion + + #region events + /// + /// Occurs when the scrollbar scrolled. + /// + [Category("Behavior")] + [Description("Is raised, when the scrollbar was scrolled.")] + public event ScrollEventHandler Scroll; + #endregion + + #region properties + + /// + /// Gets or sets the orientation. + /// + [Category("Layout")] + [Description("Gets or sets the orientation.")] + [DefaultValue(ScrollBarOrientation.Vertical)] + public ScrollBarOrientation Orientation + { + get + { + return this.orientation; + } + + set + { + // no change - return + if (value == this.orientation) + { + return; + } + + this.orientation = value; + + // change text of context menu entries + this.ChangeContextMenuItems(); + + // save scroll orientation for scroll event + this.scrollOrientation = value == ScrollBarOrientation.Vertical ? + ScrollOrientation.VerticalScroll : ScrollOrientation.HorizontalScroll; + + // only in DesignMode switch width and height + if (this.DesignMode) + { + this.Size = new Size(this.Height, this.Width); + } + + // sets the scrollbar up + this.SetUpScrollBar(); + } + } + + /// + /// Gets or sets the minimum value. + /// + [Category("Behavior")] + [Description("Gets or sets the minimum value.")] + [DefaultValue(0)] + public int Minimum + { + get + { + return this.minimum; + } + + set + { + // no change or value invalid - return + if (this.minimum == value || value < 0 || value >= this.maximum) + { + return; + } + + this.minimum = value; + + // current value less than new minimum value - adjust + if (this.value < value) + { + this.value = value; + } + + // is current large change value invalid - adjust + if (this.largeChange > this.maximum - this.minimum) + { + this.largeChange = this.maximum - this.minimum; + } + + this.SetUpScrollBar(); + + // current value less than new minimum value - adjust + if (this.value < value) + { + this.Value = value; + } + else + { + // current value is valid - adjust thumb position + this.ChangeThumbPosition(this.GetThumbPosition()); + + this.Refresh(); + } + } + } + + /// + /// Gets or sets the maximum value. + /// + [Category("Behavior")] + [Description("Gets or sets the maximum value.")] + [DefaultValue(100)] + public int Maximum + { + get + { + return this.maximum; + } + + set + { + // no change or new max. value invalid - return + if (value == this.maximum || value < 1 || value <= this.minimum) + { + return; + } + + this.maximum = value; + + // is large change value invalid - adjust + if (this.largeChange > this.maximum - this.minimum) + { + this.largeChange = this.maximum - this.minimum; + } + + this.SetUpScrollBar(); + + // is current value greater than new maximum value - adjust + if (this.value > value) + { + this.Value = this.maximum; + } + else + { + // current value is valid - adjust thumb position + this.ChangeThumbPosition(this.GetThumbPosition()); + + this.Refresh(); + } + } + } + + /// + /// Gets or sets the small change amount. + /// + [Category("Behavior")] + [Description("Gets or sets the small change value.")] + [DefaultValue(1)] + public int SmallChange + { + get + { + return this.smallChange; + } + + set + { + // no change or new small change value invalid - return + if (value == this.smallChange || value < 1 || value >= this.largeChange) + { + return; + } + + this.smallChange = value; + + this.SetUpScrollBar(); + } + } + + /// + /// Gets or sets the large change amount. + /// + [Category("Behavior")] + [Description("Gets or sets the large change value.")] + [DefaultValue(10)] + public int LargeChange + { + get + { + return this.largeChange; + } + + set + { + // no change or new large change value is invalid - return + if (value == this.largeChange || value < this.smallChange || value < 2) + { + return; + } + + // if value is greater than scroll area - adjust + if (value > this.maximum - this.minimum) + { + this.largeChange = this.maximum - this.minimum; + } + else + { + // set new value + this.largeChange = value; + } + + this.SetUpScrollBar(); + } + } + + /// + /// Gets or sets the value. + /// + [Category("Behavior")] + [Description("Gets or sets the current value.")] + [DefaultValue(0)] + public int Value + { + get + { + return this.value; + } + + set + { + // no change or invalid value - return + if (this.value == value || value < this.minimum || value > this.maximum) + { + return; + } + + this.value = value; + + // adjust thumb position + this.ChangeThumbPosition(this.GetThumbPosition()); + + // raise scroll event + this.OnScroll(new ScrollEventArgs(ScrollEventType.ThumbPosition, -1, this.value, this.scrollOrientation)); + + this.Refresh(); + } + } + + /// + /// Gets or sets the list of background markers + /// + [Category("Behavior")] + [Description("Gets or sets the list of background markers.")] + [DefaultValue(null)] + public List BackgroundMarkers + { + get + { + return this.backgroundmarkers; + } + + set + { + this.backgroundmarkers = value; + this.Refresh(); + } + } + + /// + /// Gets or sets the border color. + /// + [Category("Appearance")] + [Description("Gets or sets the border color.")] + [DefaultValue(typeof(Color), "93, 140, 201")] + public Color BorderColor + { + get + { + return this.borderColor; + } + + set + { + this.borderColor = value; + + this.Invalidate(); + } + } + + /// + /// Gets or sets the border color in disabled state. + /// + [Category("Appearance")] + [Description("Gets or sets the border color in disabled state.")] + [DefaultValue(typeof(Color), "Gray")] + public Color DisabledBorderColor + { + get + { + return this.disabledBorderColor; + } + + set + { + this.disabledBorderColor = value; + + this.Invalidate(); + } + } + + /// + /// Gets or sets the opacity of the context menu (from 0 - 1). + /// + [Category("Appearance")] + [Description("Gets or sets the opacity of the context menu (from 0 - 1).")] + [DefaultValue(typeof(double), "1")] + public double Opacity + { + get + { + return this.contextMenu.Opacity; + } + + set + { + // no change - return + if (value == this.contextMenu.Opacity) + { + return; + } + + this.contextMenu.AllowTransparency = value != 1; + + this.contextMenu.Opacity = value; + } + } + + /// + /// Gets or sets the color of background markers + /// + [Category("Behavior")] + [DefaultValue(typeof(Color), "DarkGray")] + [Description("Gets or sets the color of background markers.")] + public Color BackgroundMarkerColor + { + get + { + return this.backgroundmarkercolor; + } + + set + { + this.backgroundmarkercolor = value; + this.Refresh(); + } + } + + #endregion + + #region methods + + #region public methods + + /// + /// Prevents the drawing of the control until is called. + /// + public void BeginUpdate() + { + SendMessage(this.Handle, SETREDRAW, false, 0); + this.inUpdate = true; + } + + /// + /// Ends the updating process and the control can draw itself again. + /// + public void EndUpdate() + { + SendMessage(this.Handle, SETREDRAW, true, 0); + this.inUpdate = false; + this.SetUpScrollBar(); + this.Refresh(); + } + + #endregion + + #region protected methods + + /// + /// Raises the event. + /// + /// The that contains the event data. + protected virtual void OnScroll(ScrollEventArgs e) + { + // if event handler is attached - raise scroll event + if (this.Scroll != null) + { + this.Scroll(this, e); + } + } + + /// + /// Paints the background of the control. + /// + /// A that contains information about the control to paint. + protected override void OnPaintBackground(PaintEventArgs e) + { + // no painting here + } + + /// + /// Paints the control. + /// + /// A that contains information about the control to paint. + protected override void OnPaint(PaintEventArgs e) + { + // sets the smoothing mode to none + e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None; + + // save client rectangle + Rectangle rect = ClientRectangle; + + // adjust the rectangle + if (this.orientation == ScrollBarOrientation.Vertical) + { + rect.X++; + rect.Y += this.arrowHeight + 1; + rect.Width -= 2; + rect.Height -= (this.arrowHeight * 2) + 2; + } + else + { + rect.X += this.arrowWidth + 1; + rect.Y++; + rect.Width -= (this.arrowWidth * 2) + 2; + rect.Height -= 2; + } + + // draws the background + ScrollBarExRenderer.DrawBackground( + e.Graphics, + ClientRectangle, + this.orientation); + + // draws background markers, if any + if ((backgroundmarkers != null) && (backgroundmarkers.Count > 0)) + { + Pen pen = new Pen(backgroundmarkercolor); + foreach (int bm in backgroundmarkers) + { + int pos = (int)(((double)bm - (double)Minimum) / ((double)Maximum - (double)Minimum) * ClientRectangle.Width); + e.Graphics.DrawLine(pen, new Point(pos, ClientRectangle.Top), new Point(pos, ClientRectangle.Bottom)); + } + } + // draws the track + ScrollBarExRenderer.DrawTrack( + e.Graphics, + rect, + ScrollBarState.Normal, + this.orientation); + + // draw thumb and grip + ScrollBarExRenderer.DrawThumb( + e.Graphics, + this.thumbRectangle, + this.thumbState, + this.orientation); + + if (this.Enabled) + { + ScrollBarExRenderer.DrawThumbGrip( + e.Graphics, + this.thumbRectangle, + this.orientation); + } + + // draw arrows + ScrollBarExRenderer.DrawArrowButton( + e.Graphics, + this.topArrowRectangle, + this.topButtonState, + true, + this.orientation); + + ScrollBarExRenderer.DrawArrowButton( + e.Graphics, + this.bottomArrowRectangle, + this.bottomButtonState, + false, + this.orientation); + + // check if top or bottom bar was clicked + if (this.topBarClicked) + { + if (this.orientation == ScrollBarOrientation.Vertical) + { + this.clickedBarRectangle.Y = this.thumbTopLimit; + this.clickedBarRectangle.Height = + this.thumbRectangle.Y - this.thumbTopLimit; + } + else + { + this.clickedBarRectangle.X = this.thumbTopLimit; + this.clickedBarRectangle.Width = + this.thumbRectangle.X - this.thumbTopLimit; + } + + ScrollBarExRenderer.DrawTrack( + e.Graphics, + this.clickedBarRectangle, + ScrollBarState.Pressed, + this.orientation); + } + else if (this.bottomBarClicked) + { + if (this.orientation == ScrollBarOrientation.Vertical) + { + this.clickedBarRectangle.Y = this.thumbRectangle.Bottom + 1; + this.clickedBarRectangle.Height = + this.thumbBottomLimitBottom - this.clickedBarRectangle.Y + 1; + } + else + { + this.clickedBarRectangle.X = this.thumbRectangle.Right + 1; + this.clickedBarRectangle.Width = + this.thumbBottomLimitBottom - this.clickedBarRectangle.X + 1; + } + + ScrollBarExRenderer.DrawTrack( + e.Graphics, + this.clickedBarRectangle, + ScrollBarState.Pressed, + this.orientation); + } + + // draw border + using (Pen pen = new Pen( + (this.Enabled ? this.borderColor : this.disabledBorderColor))) + { + e.Graphics.DrawRectangle(pen, 0, 0, this.Width - 1, this.Height - 1); + } + } + + /// + /// Raises the MouseDown event. + /// + /// A that contains the event data. + protected override void OnMouseDown(MouseEventArgs e) + { + base.OnMouseDown(e); + + this.Focus(); + + if (e.Button == MouseButtons.Left) + { + // prevents showing the context menu if pressing the right mouse + // button while holding the left + this.ContextMenuStrip = null; + + Point mouseLocation = e.Location; + + if (this.thumbRectangle.Contains(mouseLocation)) + { + this.thumbClicked = true; + this.thumbPosition = this.orientation == ScrollBarOrientation.Vertical ? mouseLocation.Y - this.thumbRectangle.Y : mouseLocation.X - this.thumbRectangle.X; + this.thumbState = ScrollBarState.Pressed; + + Invalidate(this.thumbRectangle); + } + else if (this.topArrowRectangle.Contains(mouseLocation)) + { + this.topArrowClicked = true; + this.topButtonState = ScrollBarArrowButtonState.UpPressed; + + this.Invalidate(this.topArrowRectangle); + + this.ProgressThumb(true); + } + else if (this.bottomArrowRectangle.Contains(mouseLocation)) + { + this.bottomArrowClicked = true; + this.bottomButtonState = ScrollBarArrowButtonState.DownPressed; + + this.Invalidate(this.bottomArrowRectangle); + + this.ProgressThumb(true); + } + else + { + this.trackPosition = + this.orientation == ScrollBarOrientation.Vertical ? + mouseLocation.Y : mouseLocation.X; + + if (this.trackPosition < + (this.orientation == ScrollBarOrientation.Vertical ? + this.thumbRectangle.Y : this.thumbRectangle.X)) + { + this.topBarClicked = true; + } + else + { + this.bottomBarClicked = true; + } + + this.ProgressThumb(true); + } + } + else if (e.Button == MouseButtons.Right) + { + this.trackPosition = + this.orientation == ScrollBarOrientation.Vertical ? e.Y : e.X; + } + } + + /// + /// Raises the MouseUp event. + /// + /// A that contains the event data. + protected override void OnMouseUp(MouseEventArgs e) + { + base.OnMouseUp(e); + + if (e.Button == MouseButtons.Left) + { + this.ContextMenuStrip = this.contextMenu; + + if (this.thumbClicked) + { + this.thumbClicked = false; + this.thumbState = ScrollBarState.Normal; + + this.OnScroll(new ScrollEventArgs( + ScrollEventType.EndScroll, + -1, + this.value, + this.scrollOrientation) + ); + } + else if (this.topArrowClicked) + { + this.topArrowClicked = false; + this.topButtonState = ScrollBarArrowButtonState.UpNormal; + this.StopTimer(); + } + else if (this.bottomArrowClicked) + { + this.bottomArrowClicked = false; + this.bottomButtonState = ScrollBarArrowButtonState.DownNormal; + this.StopTimer(); + } + else if (this.topBarClicked) + { + this.topBarClicked = false; + this.StopTimer(); + } + else if (this.bottomBarClicked) + { + this.bottomBarClicked = false; + this.StopTimer(); + } + + Invalidate(); + } + } + + /// + /// Raises the MouseEnter event. + /// + /// A that contains the event data. + protected override void OnMouseEnter(EventArgs e) + { + base.OnMouseEnter(e); + + this.bottomButtonState = ScrollBarArrowButtonState.DownActive; + this.topButtonState = ScrollBarArrowButtonState.UpActive; + this.thumbState = ScrollBarState.Active; + + Invalidate(); + } + + /// + /// Raises the MouseLeave event. + /// + /// A that contains the event data. + protected override void OnMouseLeave(EventArgs e) + { + base.OnMouseLeave(e); + + this.ResetScrollStatus(); + } + + /// + /// Raises the MouseMove event. + /// + /// A that contains the event data. + protected override void OnMouseMove(MouseEventArgs e) + { + base.OnMouseMove(e); + + // moving and holding the left mouse button + if (e.Button == MouseButtons.Left) + { + // Update the thumb position, if the new location is within the bounds. + if (this.thumbClicked) + { + int oldScrollValue = this.value; + + this.topButtonState = ScrollBarArrowButtonState.UpActive; + this.bottomButtonState = ScrollBarArrowButtonState.DownActive; + int pos = this.orientation == ScrollBarOrientation.Vertical ? + e.Location.Y : e.Location.X; + + // The thumb is all the way to the top + if (pos <= (this.thumbTopLimit + this.thumbPosition)) + { + this.ChangeThumbPosition(this.thumbTopLimit); + + this.value = this.minimum; + } + else if (pos >= (this.thumbBottomLimitTop + this.thumbPosition)) + { + // The thumb is all the way to the bottom + this.ChangeThumbPosition(this.thumbBottomLimitTop); + + this.value = this.maximum; + } + else + { + // The thumb is between the ends of the track. + this.ChangeThumbPosition(pos - this.thumbPosition); + + int pixelRange, thumbPos, arrowSize; + + // calculate the value - first some helper variables + // dependent on the current orientation + if (this.orientation == ScrollBarOrientation.Vertical) + { + pixelRange = this.Height - (2 * this.arrowHeight) - this.thumbHeight; + thumbPos = this.thumbRectangle.Y; + arrowSize = this.arrowHeight; + } + else + { + pixelRange = this.Width - (2 * this.arrowWidth) - this.thumbWidth; + thumbPos = this.thumbRectangle.X; + arrowSize = this.arrowWidth; + } + + float perc = 0f; + + if (pixelRange != 0) + { + // percent of the new position + perc = (float)(thumbPos - arrowSize) / (float)pixelRange; + } + + // the new value is somewhere between max and min, starting + // at min position + this.value = Convert.ToInt32((perc * (this.maximum - this.minimum)) + this.minimum); + } + + // raise scroll event if new value different + if (oldScrollValue != this.value) + { + this.OnScroll(new ScrollEventArgs(ScrollEventType.ThumbTrack, oldScrollValue, this.value, this.scrollOrientation)); + + this.Refresh(); + } + } + } + else if (!this.ClientRectangle.Contains(e.Location)) + { + this.ResetScrollStatus(); + } + else if (e.Button == MouseButtons.None) // only moving the mouse + { + if (this.topArrowRectangle.Contains(e.Location)) + { + this.topButtonState = ScrollBarArrowButtonState.UpHot; + + this.Invalidate(this.topArrowRectangle); + } + else if (this.bottomArrowRectangle.Contains(e.Location)) + { + this.bottomButtonState = ScrollBarArrowButtonState.DownHot; + + Invalidate(this.bottomArrowRectangle); + } + else if (this.thumbRectangle.Contains(e.Location)) + { + this.thumbState = ScrollBarState.Hot; + + this.Invalidate(this.thumbRectangle); + } + else if (this.ClientRectangle.Contains(e.Location)) + { + this.topButtonState = ScrollBarArrowButtonState.UpActive; + this.bottomButtonState = ScrollBarArrowButtonState.DownActive; + this.thumbState = ScrollBarState.Active; + + Invalidate(); + } + } + } + + /// + /// Performs the work of setting the specified bounds of this control. + /// + /// The new x value of the control. + /// The new y value of the control. + /// The new width value of the control. + /// The new height value of the control. + /// A bitwise combination of the values. + protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) + { + // only in design mode - constrain size + if (this.DesignMode) + { + if (this.orientation == ScrollBarOrientation.Vertical) + { + if (height < (2 * this.arrowHeight) + 10) + { + height = (2 * this.arrowHeight) + 10; + } + + width = 19; + } + else + { + if (width < (2 * this.arrowWidth) + 10) + { + width = (2 * this.arrowWidth) + 10; + } + + height = 19; + } + } + + base.SetBoundsCore(x, y, width, height, specified); + + if (this.DesignMode) + { + this.SetUpScrollBar(); + } + } + + /// + /// Raises the event. + /// + /// An that contains the event data. + protected override void OnSizeChanged(EventArgs e) + { + base.OnSizeChanged(e); + this.SetUpScrollBar(); + } + + /// + /// Processes a dialog key. + /// + /// One of the values that represents the key to process. + /// true, if the key was processed by the control, false otherwise. + protected override bool ProcessDialogKey(Keys keyData) + { + // key handling is here - keys recognized by the control + // Up&Down or Left&Right, PageUp, PageDown, Home, End + Keys keyUp = Keys.Up; + Keys keyDown = Keys.Down; + + if (this.orientation == ScrollBarOrientation.Horizontal) + { + keyUp = Keys.Left; + keyDown = Keys.Right; + } + + if (keyData == keyUp) + { + this.Value -= this.smallChange; + + return true; + } + + if (keyData == keyDown) + { + this.Value += this.smallChange; + + return true; + } + + if (keyData == Keys.PageUp) + { + this.Value = this.GetValue(false, true); + + return true; + } + + if (keyData == Keys.PageDown) + { + if (this.value + this.largeChange > this.maximum) + { + this.Value = this.maximum; + } + else + { + this.Value += this.largeChange; + } + + return true; + } + + if (keyData == Keys.Home) + { + this.Value = this.minimum; + + return true; + } + + if (keyData == Keys.End) + { + this.Value = this.maximum; + + return true; + } + + return base.ProcessDialogKey(keyData); + } + + /// + /// Raises the event. + /// + /// An that contains the event data. + protected override void OnEnabledChanged(EventArgs e) + { + base.OnEnabledChanged(e); + + if (this.Enabled) + { + this.thumbState = ScrollBarState.Normal; + this.topButtonState = ScrollBarArrowButtonState.UpNormal; + this.bottomButtonState = ScrollBarArrowButtonState.DownNormal; + } + else + { + this.thumbState = ScrollBarState.Disabled; + this.topButtonState = ScrollBarArrowButtonState.UpDisabled; + this.bottomButtonState = ScrollBarArrowButtonState.DownDisabled; + } + + this.Refresh(); + } + + #endregion + + #region misc methods + + /// + /// Sends a message. + /// + /// The handle of the control. + /// The message as int. + /// param - true or false. + /// Additional parameter. + /// 0 or error code. + /// Needed for sending the stop/start drawing of the control. + [DllImport("user32.dll")] + private static extern int SendMessage( + IntPtr wnd, + int msg, + bool param, + int lparam); + + /// + /// Sets up the scrollbar. + /// + private void SetUpScrollBar() + { + // if no drawing - return + if (this.inUpdate) + { + return; + } + + // set up the width's, height's and rectangles for the different + // elements + if (this.orientation == ScrollBarOrientation.Vertical) + { + this.arrowHeight = 17; + this.arrowWidth = 15; + this.thumbWidth = 15; + this.thumbHeight = this.GetThumbSize(); + + this.clickedBarRectangle = this.ClientRectangle; + this.clickedBarRectangle.Inflate(-1, -1); + this.clickedBarRectangle.Y += this.arrowHeight; + this.clickedBarRectangle.Height -= this.arrowHeight * 2; + + this.channelRectangle = this.clickedBarRectangle; + + this.thumbRectangle = new Rectangle( + ClientRectangle.X + 2, + ClientRectangle.Y + this.arrowHeight + 1, + this.thumbWidth - 1, + this.thumbHeight + ); + + this.topArrowRectangle = new Rectangle( + ClientRectangle.X + 2, + ClientRectangle.Y + 1, + this.arrowWidth, + this.arrowHeight + ); + + this.bottomArrowRectangle = new Rectangle( + ClientRectangle.X + 2, + ClientRectangle.Bottom - this.arrowHeight - 1, + this.arrowWidth, + this.arrowHeight + ); + + // Set the default starting thumb position. + this.thumbPosition = this.thumbRectangle.Height / 2; + + // Set the bottom limit of the thumb's bottom border. + this.thumbBottomLimitBottom = + ClientRectangle.Bottom - this.arrowHeight - 2; + + // Set the bottom limit of the thumb's top border. + this.thumbBottomLimitTop = + this.thumbBottomLimitBottom - this.thumbRectangle.Height; + + // Set the top limit of the thumb's top border. + this.thumbTopLimit = ClientRectangle.Y + this.arrowHeight + 1; + } + else + { + this.arrowHeight = 15; + this.arrowWidth = 17; + this.thumbHeight = 15; + this.thumbWidth = this.GetThumbSize(); + + this.clickedBarRectangle = this.ClientRectangle; + this.clickedBarRectangle.Inflate(-1, -1); + this.clickedBarRectangle.X += this.arrowWidth; + this.clickedBarRectangle.Width -= this.arrowWidth * 2; + + this.channelRectangle = this.clickedBarRectangle; + + this.thumbRectangle = new Rectangle( + ClientRectangle.X + this.arrowWidth + 1, + ClientRectangle.Y + 2, + this.thumbWidth, + this.thumbHeight - 1 + ); + + this.topArrowRectangle = new Rectangle( + ClientRectangle.X + 1, + ClientRectangle.Y + 2, + this.arrowWidth, + this.arrowHeight + ); + + this.bottomArrowRectangle = new Rectangle( + ClientRectangle.Right - this.arrowWidth - 1, + ClientRectangle.Y + 2, + this.arrowWidth, + this.arrowHeight + ); + + // Set the default starting thumb position. + this.thumbPosition = this.thumbRectangle.Width / 2; + + // Set the bottom limit of the thumb's bottom border. + this.thumbBottomLimitBottom = + ClientRectangle.Right - this.arrowWidth - 2; + + // Set the bottom limit of the thumb's top border. + this.thumbBottomLimitTop = + this.thumbBottomLimitBottom - this.thumbRectangle.Width; + + // Set the top limit of the thumb's top border. + this.thumbTopLimit = ClientRectangle.X + this.arrowWidth + 1; + } + + this.ChangeThumbPosition(this.GetThumbPosition()); + + this.Refresh(); + } + + /// + /// Handles the updating of the thumb. + /// + /// The sender. + /// An object that contains the event data. + private void ProgressTimerTick(object sender, EventArgs e) + { + this.ProgressThumb(true); + } + + /// + /// Resets the scroll status of the scrollbar. + /// + private void ResetScrollStatus() + { + // get current mouse position + Point pos = this.PointToClient(Cursor.Position); + + // set appearance of buttons in relation to where the mouse is - + // outside or inside the control + if (this.ClientRectangle.Contains(pos)) + { + this.bottomButtonState = ScrollBarArrowButtonState.DownActive; + this.topButtonState = ScrollBarArrowButtonState.UpActive; + } + else + { + this.bottomButtonState = ScrollBarArrowButtonState.DownNormal; + this.topButtonState = ScrollBarArrowButtonState.UpNormal; + } + + // set appearance of thumb + this.thumbState = this.thumbRectangle.Contains(pos) ? + ScrollBarState.Hot : ScrollBarState.Normal; + + this.bottomArrowClicked = this.bottomBarClicked = + this.topArrowClicked = this.topBarClicked = false; + + this.StopTimer(); + + this.Refresh(); + } + + /// + /// Calculates the new value of the scrollbar. + /// + /// true for a small change, false otherwise. + /// true for up movement, false otherwise. + /// The new scrollbar value. + private int GetValue(bool smallIncrement, bool up) + { + int newValue; + + // calculate the new value of the scrollbar + // with checking if new value is in bounds (min/max) + if (up) + { + newValue = this.value - (smallIncrement ? this.smallChange : this.largeChange); + + if (newValue < this.minimum) + { + newValue = this.minimum; + } + } + else + { + newValue = this.value + (smallIncrement ? this.smallChange : this.largeChange); + + if (newValue > this.maximum) + { + newValue = this.maximum; + } + } + + return newValue; + } + + /// + /// Calculates the new thumb position. + /// + /// The new thumb position. + private int GetThumbPosition() + { + int pixelRange, arrowSize; + + if (this.orientation == ScrollBarOrientation.Vertical) + { + pixelRange = this.Height - (2 * this.arrowHeight) - this.thumbHeight; + arrowSize = this.arrowHeight; + } + else + { + pixelRange = this.Width - (2 * this.arrowWidth) - this.thumbWidth; + arrowSize = this.arrowWidth; + } + + int realRange = this.maximum - this.minimum; + float perc = 0f; + + if (realRange != 0) + { + perc = ((float)this.value - (float)this.minimum) / (float)realRange; + } + + return Math.Max(this.thumbTopLimit, Math.Min( + this.thumbBottomLimitTop, + Convert.ToInt32((perc * pixelRange) + arrowSize))); + } + + /// + /// Calculates the height of the thumb. + /// + /// The height of the thumb. + private int GetThumbSize() + { + int trackSize = + this.orientation == ScrollBarOrientation.Vertical ? + this.Height - (2 * this.arrowHeight) : this.Width - (2 * this.arrowWidth); + + if (this.maximum == 0 || this.largeChange == 0) + { + return trackSize; + } + + float newThumbSize = ((float)this.largeChange * (float)trackSize) / (float)this.maximum; + + return Convert.ToInt32(Math.Min((float)trackSize, Math.Max(newThumbSize, 10f))); + } + + /// + /// Enables the timer. + /// + private void EnableTimer() + { + // if timer is not already enabled - enable it + if (!this.progressTimer.Enabled) + { + this.progressTimer.Interval = 600; + this.progressTimer.Start(); + } + else + { + // if already enabled, change tick time + this.progressTimer.Interval = 10; + } + } + + /// + /// Stops the progress timer. + /// + private void StopTimer() + { + this.progressTimer.Stop(); + } + + /// + /// Changes the position of the thumb. + /// + /// The new position. + private void ChangeThumbPosition(int position) + { + if (this.orientation == ScrollBarOrientation.Vertical) + { + this.thumbRectangle.Y = position; + } + else + { + this.thumbRectangle.X = position; + } + } + + /// + /// Controls the movement of the thumb. + /// + /// true for enabling the timer, false otherwise. + private void ProgressThumb(bool enableTimer) + { + int scrollOldValue = this.value; + ScrollEventType type = ScrollEventType.First; + int thumbSize, thumbPos; + + if (this.orientation == ScrollBarOrientation.Vertical) + { + thumbPos = this.thumbRectangle.Y; + thumbSize = this.thumbRectangle.Height; + } + else + { + thumbPos = this.thumbRectangle.X; + thumbSize = this.thumbRectangle.Width; + } + + // arrow down or shaft down clicked + if (this.bottomArrowClicked || (this.bottomBarClicked && (thumbPos + thumbSize) < this.trackPosition)) + { + type = this.bottomArrowClicked ? ScrollEventType.SmallIncrement : ScrollEventType.LargeIncrement; + + this.value = this.GetValue(this.bottomArrowClicked, false); + + if (this.value == this.maximum) + { + this.ChangeThumbPosition(this.thumbBottomLimitTop); + + type = ScrollEventType.Last; + } + else + { + this.ChangeThumbPosition(Math.Min(this.thumbBottomLimitTop, this.GetThumbPosition())); + } + } + else if (this.topArrowClicked || (this.topBarClicked && thumbPos > this.trackPosition)) + { + type = this.topArrowClicked ? ScrollEventType.SmallDecrement : ScrollEventType.LargeDecrement; + + // arrow up or shaft up clicked + this.value = this.GetValue(this.topArrowClicked, true); + + if (this.value == this.minimum) + { + this.ChangeThumbPosition(this.thumbTopLimit); + + type = ScrollEventType.First; + } + else + { + this.ChangeThumbPosition(Math.Max(this.thumbTopLimit, this.GetThumbPosition())); + } + } + else if (!((this.topArrowClicked && thumbPos == this.thumbTopLimit) || (this.bottomArrowClicked && thumbPos == this.thumbBottomLimitTop))) + { + this.ResetScrollStatus(); + + return; + } + + if (scrollOldValue != this.value) + { + this.OnScroll(new ScrollEventArgs(type, scrollOldValue, this.value, this.scrollOrientation)); + + this.Invalidate(this.channelRectangle); + + if (enableTimer) + { + this.EnableTimer(); + } + } + else + { + if (this.topArrowClicked) + { + type = ScrollEventType.SmallDecrement; + } + else if (this.bottomArrowClicked) + { + type = ScrollEventType.SmallIncrement; + } + + this.OnScroll(new ScrollEventArgs(type, this.value)); + } + } + + /// + /// Changes the displayed text of the context menu items dependent of the current . + /// + private void ChangeContextMenuItems() + { + if (this.orientation == ScrollBarOrientation.Vertical) + { + this.tsmiTop.Text = "Top"; + this.tsmiBottom.Text = "Bottom"; + this.tsmiLargeDown.Text = "Page down"; + this.tsmiLargeUp.Text = "Page up"; + this.tsmiSmallDown.Text = "Scroll down"; + this.tsmiSmallUp.Text = "Scroll up"; + this.tsmiScrollHere.Text = "Scroll here"; + } + else + { + this.tsmiTop.Text = "Left"; + this.tsmiBottom.Text = "Right"; + this.tsmiLargeDown.Text = "Page left"; + this.tsmiLargeUp.Text = "Page right"; + this.tsmiSmallDown.Text = "Scroll right"; + this.tsmiSmallUp.Text = "Scroll left"; + this.tsmiScrollHere.Text = "Scroll here"; + } + } + + #endregion + + #region context menu methods + + /// + /// Initializes the context menu. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.contextMenu = new System.Windows.Forms.ContextMenuStrip(this.components); + this.tsmiScrollHere = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); + this.tsmiTop = new System.Windows.Forms.ToolStripMenuItem(); + this.tsmiBottom = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator(); + this.tsmiLargeUp = new System.Windows.Forms.ToolStripMenuItem(); + this.tsmiLargeDown = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator(); + this.tsmiSmallUp = new System.Windows.Forms.ToolStripMenuItem(); + this.tsmiSmallDown = new System.Windows.Forms.ToolStripMenuItem(); + this.contextMenu.SuspendLayout(); + this.SuspendLayout(); + // + // contextMenu + // + this.contextMenu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.tsmiScrollHere, + this.toolStripSeparator1, + this.tsmiTop, + this.tsmiBottom, + this.toolStripSeparator2, + this.tsmiLargeUp, + this.tsmiLargeDown, + this.toolStripSeparator3, + this.tsmiSmallUp, + this.tsmiSmallDown}); + this.contextMenu.Name = "contextMenu"; + this.contextMenu.Size = new System.Drawing.Size(151, 176); + // + // tsmiScrollHere + // + this.tsmiScrollHere.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; + this.tsmiScrollHere.Name = "tsmiScrollHere"; + this.tsmiScrollHere.Size = new System.Drawing.Size(150, 22); + this.tsmiScrollHere.Text = "Scroll here"; + this.tsmiScrollHere.Click += ScrollHereClick; + // + // toolStripSeparator1 + // + this.toolStripSeparator1.Name = "toolStripSeparator1"; + this.toolStripSeparator1.Size = new System.Drawing.Size(147, 6); + // + // tsmiTop + // + this.tsmiTop.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; + this.tsmiTop.Name = "tsmiTop"; + this.tsmiTop.Size = new System.Drawing.Size(150, 22); + this.tsmiTop.Text = "Top"; + this.tsmiTop.Click += this.TopClick; + // + // tsmiBottom + // + this.tsmiBottom.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; + this.tsmiBottom.Name = "tsmiBottom"; + this.tsmiBottom.Size = new System.Drawing.Size(150, 22); + this.tsmiBottom.Text = "Bottom"; + this.tsmiBottom.Click += this.BottomClick; + // + // toolStripSeparator2 + // + this.toolStripSeparator2.Name = "toolStripSeparator2"; + this.toolStripSeparator2.Size = new System.Drawing.Size(147, 6); + // + // tsmiLargeUp + // + this.tsmiLargeUp.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; + this.tsmiLargeUp.Name = "tsmiLargeUp"; + this.tsmiLargeUp.Size = new System.Drawing.Size(150, 22); + this.tsmiLargeUp.Text = "Page up"; + this.tsmiLargeUp.Click += this.LargeUpClick; + // + // tsmiLargeDown + // + this.tsmiLargeDown.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; + this.tsmiLargeDown.Name = "tsmiLargeDown"; + this.tsmiLargeDown.Size = new System.Drawing.Size(150, 22); + this.tsmiLargeDown.Text = "Page down"; + this.tsmiLargeDown.Click += this.LargeDownClick; + // + // toolStripSeparator3 + // + this.toolStripSeparator3.Name = "toolStripSeparator3"; + this.toolStripSeparator3.Size = new System.Drawing.Size(147, 6); + // + // tsmiSmallUp + // + this.tsmiSmallUp.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; + this.tsmiSmallUp.Name = "tsmiSmallUp"; + this.tsmiSmallUp.Size = new System.Drawing.Size(150, 22); + this.tsmiSmallUp.Text = "Scroll up"; + this.tsmiSmallUp.Click += this.SmallUpClick; + // + // tsmiSmallDown + // + this.tsmiSmallDown.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; + this.tsmiSmallDown.Name = "tsmiSmallDown"; + this.tsmiSmallDown.Size = new System.Drawing.Size(150, 22); + this.tsmiSmallDown.Text = "Scroll down"; + this.tsmiSmallDown.Click += this.SmallDownClick; + this.contextMenu.ResumeLayout(false); + this.ResumeLayout(false); + } + + /// + /// Context menu handler. + /// + /// The sender. + /// The event arguments. + private void ScrollHereClick(object sender, EventArgs e) + { + int thumbSize, thumbPos, arrowSize, size; + + if (this.orientation == ScrollBarOrientation.Vertical) + { + thumbSize = this.thumbHeight; + arrowSize = this.arrowHeight; + size = this.Height; + + this.ChangeThumbPosition(Math.Max(this.thumbTopLimit, Math.Min(this.thumbBottomLimitTop, this.trackPosition - (this.thumbRectangle.Height / 2)))); + + thumbPos = this.thumbRectangle.Y; + } + else + { + thumbSize = this.thumbWidth; + arrowSize = this.arrowWidth; + size = this.Width; + + this.ChangeThumbPosition(Math.Max(this.thumbTopLimit, Math.Min(this.thumbBottomLimitTop, this.trackPosition - (this.thumbRectangle.Width / 2)))); + + thumbPos = this.thumbRectangle.X; + } + + int pixelRange = size - (2 * arrowSize) - thumbSize; + float perc = 0f; + + if (pixelRange != 0) + { + perc = (float)(thumbPos - arrowSize) / (float)pixelRange; + } + + int oldValue = this.value; + + this.value = Convert.ToInt32((perc * (this.maximum - this.minimum)) + this.minimum); + + this.OnScroll(new ScrollEventArgs(ScrollEventType.ThumbPosition, oldValue, this.value, this.scrollOrientation)); + + this.Refresh(); + } + + /// + /// Context menu handler. + /// + /// The sender. + /// The event arguments. + private void TopClick(object sender, EventArgs e) + { + this.Value = this.minimum; + } + + /// + /// Context menu handler. + /// + /// The sender. + /// The event arguments. + private void BottomClick(object sender, EventArgs e) + { + this.Value = this.maximum; + } + + /// + /// Context menu handler. + /// + /// The sender. + /// The event arguments. + private void LargeUpClick(object sender, EventArgs e) + { + this.Value = this.GetValue(false, true); + } + + /// + /// Context menu handler. + /// + /// The sender. + /// The event arguments. + private void LargeDownClick(object sender, EventArgs e) + { + this.Value = this.GetValue(false, false); + } + + /// + /// Context menu handler. + /// + /// The sender. + /// The event arguments. + private void SmallUpClick(object sender, EventArgs e) + { + this.Value = this.GetValue(true, true); + } + + /// + /// Context menu handler. + /// + /// The sender. + /// The event arguments. + private void SmallDownClick(object sender, EventArgs e) + { + this.Value = this.GetValue(true, false); + } + + #endregion + + #endregion + } +} \ No newline at end of file diff --git a/CustomScrollBar/ScrollBarEx.resx b/CustomScrollBar/ScrollBarEx.resx new file mode 100644 index 0000000..863c402 --- /dev/null +++ b/CustomScrollBar/ScrollBarEx.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + False + + \ No newline at end of file diff --git a/CustomScrollBar/ScrollBarExRenderer.cs b/CustomScrollBar/ScrollBarExRenderer.cs new file mode 100644 index 0000000..2c7de16 --- /dev/null +++ b/CustomScrollBar/ScrollBarExRenderer.cs @@ -0,0 +1,984 @@ +namespace CustomScrollBar +{ + using System; + using System.Drawing; + using System.Drawing.Drawing2D; + using System.Drawing.Imaging; + + /// + /// The scrollbar renderer class. + /// + internal static class ScrollBarExRenderer + { + #region fields + + /// + /// The colors of the thumb in the 3 states. + /// + private static Color[,] thumbColors = new Color[3, 8]; + + /// + /// The arrow colors in the three states. + /// + private static Color[,] arrowColors = new Color[3, 8]; + + /// + /// The arrow border colors. + /// + private static Color[] arrowBorderColors = new Color[4]; + + /// + /// The background colors. + /// + private static Color[] backgroundColors = new Color[5]; + + /// + /// The track colors. + /// + private static Color[] trackColors = new Color[2]; + + #endregion + + #region constructor + + /// + /// Initializes static members of the class. + /// + static ScrollBarExRenderer() + { + // hot state + thumbColors[0, 0] = Color.FromArgb(96, 111, 148); // border color + thumbColors[0, 1] = Color.FromArgb(232, 233, 233); // left/top start color + thumbColors[0, 2] = Color.FromArgb(230, 233, 241); // left/top end color + thumbColors[0, 3] = Color.FromArgb(233, 237, 242); // right/bottom line color + thumbColors[0, 4] = Color.FromArgb(209, 218, 228); // right/bottom start color + thumbColors[0, 5] = Color.FromArgb(218, 227, 235); // right/bottom end color + thumbColors[0, 6] = Color.FromArgb(190, 202, 219); // right/bottom middle color + thumbColors[0, 7] = Color.FromArgb(96, 11, 148); // left/top line color + + // over state + thumbColors[1, 0] = Color.FromArgb(60, 110, 176); + thumbColors[1, 1] = Color.FromArgb(187, 204, 228); + thumbColors[1, 2] = Color.FromArgb(205, 227, 254); + thumbColors[1, 3] = Color.FromArgb(252, 253, 255); + thumbColors[1, 4] = Color.FromArgb(170, 207, 247); + thumbColors[1, 5] = Color.FromArgb(219, 232, 251); + thumbColors[1, 6] = Color.FromArgb(190, 202, 219); + thumbColors[1, 7] = Color.FromArgb(233, 233, 235); + + // pressed state + thumbColors[2, 0] = Color.FromArgb(23, 73, 138); + thumbColors[2, 1] = Color.FromArgb(154, 184, 225); + thumbColors[2, 2] = Color.FromArgb(166, 202, 250); + thumbColors[2, 3] = Color.FromArgb(221, 235, 251); + thumbColors[2, 4] = Color.FromArgb(110, 166, 240); + thumbColors[2, 5] = Color.FromArgb(194, 218, 248); + thumbColors[2, 6] = Color.FromArgb(190, 202, 219); + thumbColors[2, 7] = Color.FromArgb(194, 211, 231); + + /* picture of colors and indices + *(0,0) + * ----------------------------------------------- + * | | + * | |-----------------------------------------| | + * | | (2) | | + * | | |-------------------------------------| | | + * | | | (0) | | | + * | | | | | | + * | | | | | | + * | |3| (1) |3| | + * | |6| (4) |6| | + * | | | | | | + * | | | (5) | | | + * | | |-------------------------------------| | | + * | | (12) | | + * | |-----------------------------------------| | + * | | + * ----------------------------------------------- (15,17) + */ + + // hot state + arrowColors[0, 0] = Color.FromArgb(223, 236, 252); + arrowColors[0, 1] = Color.FromArgb(207, 225, 248); + arrowColors[0, 2] = Color.FromArgb(245, 249, 255); + arrowColors[0, 3] = Color.FromArgb(237, 244, 252); + arrowColors[0, 4] = Color.FromArgb(244, 249, 255); + arrowColors[0, 5] = Color.FromArgb(244, 249, 255); + arrowColors[0, 6] = Color.FromArgb(251, 253, 255); + arrowColors[0, 7] = Color.FromArgb(251, 253, 255); + + // over state + arrowColors[1, 0] = Color.FromArgb(205, 222, 243); + arrowColors[1, 1] = Color.FromArgb(186, 208, 235); + arrowColors[1, 2] = Color.FromArgb(238, 244, 252); + arrowColors[1, 3] = Color.FromArgb(229, 237, 247); + arrowColors[1, 4] = Color.FromArgb(223, 234, 247); + arrowColors[1, 5] = Color.FromArgb(241, 246, 254); + arrowColors[1, 6] = Color.FromArgb(243, 247, 252); + arrowColors[1, 7] = Color.FromArgb(250, 252, 255); + + // pressed state + arrowColors[2, 0] = Color.FromArgb(215, 220, 225); + arrowColors[2, 1] = Color.FromArgb(195, 202, 210); + arrowColors[2, 2] = Color.FromArgb(242, 244, 245); + arrowColors[2, 3] = Color.FromArgb(232, 235, 238); + arrowColors[2, 4] = Color.FromArgb(226, 228, 230); + arrowColors[2, 5] = Color.FromArgb(230, 233, 236); + arrowColors[2, 6] = Color.FromArgb(244, 245, 245); + arrowColors[2, 7] = Color.FromArgb(245, 247, 248); + + // background colors + backgroundColors[0] = Color.FromArgb(235, 237, 239); + backgroundColors[1] = Color.FromArgb(252, 252, 252); + backgroundColors[2] = Color.FromArgb(247, 247, 247); + backgroundColors[3] = Color.FromArgb(238, 238, 238); + backgroundColors[4] = Color.FromArgb(240, 240, 240); + + // track colors + trackColors[0] = Color.FromArgb(204, 204, 204); + trackColors[1] = Color.FromArgb(220, 220, 220); + + // arrow border colors + arrowBorderColors[0] = Color.FromArgb(135, 146, 160); + arrowBorderColors[1] = Color.FromArgb(140, 151, 165); + arrowBorderColors[2] = Color.FromArgb(128, 139, 153); + arrowBorderColors[3] = Color.FromArgb(99, 110, 125); + } + + #endregion + + #region methods + + #region public methods + + /// + /// Draws the background. + /// + /// The used to paint. + /// The rectangle in which to paint. + /// The . + public static void DrawBackground( + Graphics g, + Rectangle rect, + ScrollBarOrientation orientation) + { + if (g == null) + { + throw new ArgumentNullException("g"); + } + + if (rect.IsEmpty || g.IsVisibleClipEmpty + || !g.VisibleClipBounds.IntersectsWith(rect)) + { + return; + } + + if (orientation == ScrollBarOrientation.Vertical) + { + DrawBackgroundVertical(g, rect); + } + else + { + DrawBackgroundHorizontal(g, rect); + } + } + + /// + /// Draws the channel ( or track ). + /// + /// The used to paint. + /// The rectangle in which to paint. + /// The scrollbar state. + /// The . + public static void DrawTrack( + Graphics g, + Rectangle rect, + ScrollBarState state, + ScrollBarOrientation orientation) + { + if (g == null) + { + throw new ArgumentNullException("g"); + } + + if (rect.Width <= 0 || rect.Height <= 0 + || state != ScrollBarState.Pressed || g.IsVisibleClipEmpty + || !g.VisibleClipBounds.IntersectsWith(rect)) + { + return; + } + + if (orientation == ScrollBarOrientation.Vertical) + { + DrawTrackVertical(g, rect); + } + else + { + DrawTrackHorizontal(g, rect); + } + } + + /// + /// Draws the thumb. + /// + /// The used to paint. + /// The rectangle in which to paint. + /// The of the thumb. + /// The . + public static void DrawThumb( + Graphics g, + Rectangle rect, + ScrollBarState state, + ScrollBarOrientation orientation) + { + if (g == null) + { + throw new ArgumentNullException("g"); + } + + if (rect.IsEmpty || g.IsVisibleClipEmpty + || !g.VisibleClipBounds.IntersectsWith(rect) + || state == ScrollBarState.Disabled) + { + return; + } + + if (orientation == ScrollBarOrientation.Vertical) + { + DrawThumbVertical(g, rect, state); + } + else + { + DrawThumbHorizontal(g, rect, state); + } + } + + /// + /// Draws the grip of the thumb. + /// + /// The used to paint. + /// The rectangle in which to paint. + /// The . + public static void DrawThumbGrip( + Graphics g, + Rectangle rect, + ScrollBarOrientation orientation) + { + if (g == null) + { + throw new ArgumentNullException("g"); + } + + if (rect.IsEmpty || g.IsVisibleClipEmpty + || !g.VisibleClipBounds.IntersectsWith(rect)) + { + return; + } + + // get grip image + using (Image gripImage = Properties.ScrollBarResources.GripNormal) + { + // adjust rectangle and rotate grip image if necessary + Rectangle r = AdjustThumbGrip(rect, orientation, gripImage); + + // adjust alpha channel of grip image + using (ImageAttributes attr = new ImageAttributes()) + { + attr.SetColorMatrix( + new ColorMatrix(new float[][] { + new[] { 1F, 0, 0, 0, 0 }, + new[] { 0, 1F, 0, 0, 0 }, + new[] { 0, 0, 1F, 0, 0 }, + new[] { 0, 0, 0, .8F, 0 }, + new[] { 0, 0, 0, 0, 1F } + }), + ColorMatrixFlag.Default, + ColorAdjustType.Bitmap + ); + + // draw grip image + g.DrawImage(gripImage, r, 0, 0, r.Width, r.Height, GraphicsUnit.Pixel, attr); + } + } + } + + /// + /// Draws an arrow button. + /// + /// The used to paint. + /// The rectangle in which to paint. + /// The of the arrow button. + /// true for an up arrow, false otherwise. + /// The . + public static void DrawArrowButton( + Graphics g, + Rectangle rect, + ScrollBarArrowButtonState state, + bool arrowUp, + ScrollBarOrientation orientation) + { + if (g == null) + { + throw new ArgumentNullException("g"); + } + + if (rect.IsEmpty || g.IsVisibleClipEmpty + || !g.VisibleClipBounds.IntersectsWith(rect)) + { + return; + } + + if (orientation == ScrollBarOrientation.Vertical) + { + DrawArrowButtonVertical(g, rect, state, arrowUp); + } + else + { + DrawArrowButtonHorizontal(g, rect, state, arrowUp); + } + } + + #endregion + + #region private methods + + /// + /// Draws the background. + /// + /// The used to paint. + /// The rectangle in which to paint. + private static void DrawBackgroundVertical(Graphics g, Rectangle rect) + { + using (Pen p = new Pen(backgroundColors[0])) + { + g.DrawLine(p, rect.Left + 1, rect.Top + 1, rect.Left + 1, rect.Bottom - 1); + g.DrawLine(p, rect.Right - 2, rect.Top + 1, rect.Right - 2, rect.Bottom - 1); + } + + using (Pen p = new Pen(backgroundColors[1])) + { + g.DrawLine(p, rect.Left + 2, rect.Top + 1, rect.Left + 2, rect.Bottom - 1); + } + + Rectangle firstRect = new Rectangle( + rect.Left + 3, + rect.Top, + 8, + rect.Height); + + Rectangle secondRect = new Rectangle( + firstRect.Right - 1, + firstRect.Top, + 7, + firstRect.Height); + + using (LinearGradientBrush brush = new LinearGradientBrush( + firstRect, + backgroundColors[2], + backgroundColors[3], + LinearGradientMode.Horizontal)) + { + g.FillRectangle(brush, firstRect); + } + + using (LinearGradientBrush brush = new LinearGradientBrush( + secondRect, + backgroundColors[3], + backgroundColors[4], + LinearGradientMode.Horizontal)) + { + g.FillRectangle(brush, secondRect); + } + } + + /// + /// Draws the background. + /// + /// The used to paint. + /// The rectangle in which to paint. + private static void DrawBackgroundHorizontal(Graphics g, Rectangle rect) + { + using (Pen p = new Pen(backgroundColors[0])) + { + g.DrawLine(p, rect.Left + 1, rect.Top + 1, rect.Right - 1, rect.Top + 1); + g.DrawLine(p, rect.Left + 1, rect.Bottom - 2, rect.Right - 1, rect.Bottom - 2); + } + + using (Pen p = new Pen(backgroundColors[1])) + { + g.DrawLine(p, rect.Left + 1, rect.Top + 2, rect.Right - 1, rect.Top + 2); + } + + Rectangle firstRect = new Rectangle( + rect.Left, + rect.Top + 3, + rect.Width, + 8); + + Rectangle secondRect = new Rectangle( + firstRect.Left, + firstRect.Bottom - 1, + firstRect.Width, + 7); + + using (LinearGradientBrush brush = new LinearGradientBrush( + firstRect, + backgroundColors[2], + backgroundColors[3], + LinearGradientMode.Vertical)) + { + g.FillRectangle(brush, firstRect); + } + + using (LinearGradientBrush brush = new LinearGradientBrush( + secondRect, + backgroundColors[3], + backgroundColors[4], + LinearGradientMode.Vertical)) + { + g.FillRectangle(brush, secondRect); + } + } + + /// + /// Draws the channel ( or track ). + /// + /// The used to paint. + /// The rectangle in which to paint. + private static void DrawTrackVertical(Graphics g, Rectangle rect) + { + Rectangle innerRect = new Rectangle(rect.Left + 1, rect.Top, 15, rect.Height); + + using (LinearGradientBrush brush = new LinearGradientBrush( + innerRect, + trackColors[0], + trackColors[1], + LinearGradientMode.Horizontal)) + { + g.FillRectangle(brush, innerRect); + } + } + + /// + /// Draws the channel ( or track ). + /// + /// The used to paint. + /// The rectangle in which to paint. + private static void DrawTrackHorizontal(Graphics g, Rectangle rect) + { + Rectangle innerRect = new Rectangle(rect.Left, rect.Top + 1, rect.Width, 15); + + using (LinearGradientBrush brush = new LinearGradientBrush( + innerRect, + trackColors[0], + trackColors[1], + LinearGradientMode.Vertical)) + { + g.FillRectangle(brush, innerRect); + } + } + + /// + /// Adjusts the thumb grip according to the specified . + /// + /// The rectangle to adjust. + /// The scrollbar orientation. + /// The grip image. + /// The adjusted rectangle. + /// Also rotates the grip image if necessary. + private static Rectangle AdjustThumbGrip( + Rectangle rect, + ScrollBarOrientation orientation, + Image gripImage) + { + Rectangle r = rect; + + r.Inflate(-1, -1); + + if (orientation == ScrollBarOrientation.Vertical) + { + r.X += 3; + r.Y += (r.Height / 2) - (gripImage.Height / 2); + } + else + { + gripImage.RotateFlip(RotateFlipType.Rotate90FlipNone); + + r.X += (r.Width / 2) - (gripImage.Width / 2); + r.Y += 3; + } + + r.Width = gripImage.Width; + r.Height = gripImage.Height; + + return r; + } + + /// + /// Draws the thumb. + /// + /// The used to paint. + /// The rectangle in which to paint. + /// The of the thumb. + private static void DrawThumbVertical( + Graphics g, + Rectangle rect, + ScrollBarState state) + { + int index = 0; + + switch (state) + { + case ScrollBarState.Hot: + { + index = 1; + break; + } + + case ScrollBarState.Pressed: + { + index = 2; + break; + } + } + + Rectangle innerRect = rect; + innerRect.Inflate(-1, -1); + + Rectangle r = innerRect; + r.Width = 6; + r.Height++; + + // draw left gradient + using (LinearGradientBrush brush = new LinearGradientBrush( + r, + thumbColors[index, 1], + thumbColors[index, 2], + LinearGradientMode.Horizontal)) + { + g.FillRectangle(brush, r); + } + + r.X = r.Right; + + // draw right gradient + if (index == 0) + { + using (LinearGradientBrush brush = new LinearGradientBrush( + r, + thumbColors[index, 4], + thumbColors[index, 5], + LinearGradientMode.Horizontal)) + { + brush.InterpolationColors = new ColorBlend(3) + { + Colors = new[] + { + thumbColors[index, 4], + thumbColors[index, 6], + thumbColors[index, 5] + }, + Positions = new[] { 0f, .5f, 1f } + }; + + g.FillRectangle(brush, r); + } + } + else + { + using (LinearGradientBrush brush = new LinearGradientBrush( + r, + thumbColors[index, 4], + thumbColors[index, 5], + LinearGradientMode.Horizontal)) + { + g.FillRectangle(brush, r); + } + + // draw left line + using (Pen p = new Pen(thumbColors[index, 7])) + { + g.DrawLine(p, innerRect.X, innerRect.Y, innerRect.X, innerRect.Bottom); + } + } + + // draw right line + using (Pen p = new Pen(thumbColors[index, 3])) + { + g.DrawLine(p, innerRect.Right, innerRect.Y, innerRect.Right, innerRect.Bottom); + } + + g.SmoothingMode = SmoothingMode.AntiAlias; + + // draw border + using (Pen p = new Pen(thumbColors[index, 0])) + { + using (GraphicsPath path = CreateRoundPath(rect, 2f, 2f)) + { + g.DrawPath(p, path); + } + } + + g.SmoothingMode = SmoothingMode.None; + } + + /// + /// Draws the thumb. + /// + /// The used to paint. + /// The rectangle in which to paint. + /// The of the thumb. + private static void DrawThumbHorizontal( + Graphics g, + Rectangle rect, + ScrollBarState state) + { + int index = 0; + + switch (state) + { + case ScrollBarState.Hot: + { + index = 1; + break; + } + + case ScrollBarState.Pressed: + { + index = 2; + break; + } + } + + Rectangle innerRect = rect; + innerRect.Inflate(-1, -1); + + Rectangle r = innerRect; + r.Height = 6; + r.Width++; + + // draw left gradient + using (LinearGradientBrush brush = new LinearGradientBrush( + r, thumbColors[index, 1], + thumbColors[index, 2], + LinearGradientMode.Vertical)) + { + g.FillRectangle(brush, r); + } + + r.Y = r.Bottom; + + // draw right gradient + if (index == 0) + { + using (LinearGradientBrush brush = new LinearGradientBrush( + r, + thumbColors[index, 4], + thumbColors[index, 5], + LinearGradientMode.Vertical)) + { + brush.InterpolationColors = new ColorBlend(3) + { + Colors = new[] + { + thumbColors[index, 4], + thumbColors[index, 6], + thumbColors[index, 5] + }, + Positions = new[] { 0f, .5f, 1f } + }; + + g.FillRectangle(brush, r); + } + } + else + { + using (LinearGradientBrush brush = new LinearGradientBrush( + r, thumbColors[index, 4], + thumbColors[index, 5], + LinearGradientMode.Vertical)) + { + g.FillRectangle(brush, r); + } + + // draw left line + using (Pen p = new Pen(thumbColors[index, 7])) + { + g.DrawLine(p, innerRect.X, innerRect.Y, innerRect.Right, innerRect.Y); + } + } + + // draw right line + using (Pen p = new Pen(thumbColors[index, 3])) + { + g.DrawLine(p, innerRect.X, innerRect.Bottom, innerRect.Right, innerRect.Bottom); + } + + g.SmoothingMode = SmoothingMode.AntiAlias; + + // draw border + using (Pen p = new Pen(thumbColors[index, 0])) + { + using (GraphicsPath path = CreateRoundPath(rect, 2f, 2f)) + { + g.DrawPath(p, path); + } + } + + g.SmoothingMode = SmoothingMode.None; + } + + /// + /// Draws an arrow button. + /// + /// The used to paint. + /// The rectangle in which to paint. + /// The of the arrow button. + /// true for an up arrow, false otherwise. + private static void DrawArrowButtonVertical( + Graphics g, + Rectangle rect, + ScrollBarArrowButtonState state, + bool arrowUp) + { + using (Image arrowImage = GetArrowDownButtonImage(state)) + { + if (arrowUp) + { + arrowImage.RotateFlip(RotateFlipType.Rotate180FlipNone); + } + + g.DrawImage(arrowImage, rect); + } + } + + /// + /// Draws an arrow button. + /// + /// The used to paint. + /// The rectangle in which to paint. + /// The of the arrow button. + /// true for an up arrow, false otherwise. + private static void DrawArrowButtonHorizontal( + Graphics g, + Rectangle rect, + ScrollBarArrowButtonState state, + bool arrowUp) + { + using (Image arrowImage = GetArrowDownButtonImage(state)) + { + if (arrowUp) + { + arrowImage.RotateFlip(RotateFlipType.Rotate90FlipNone); + } + else + { + arrowImage.RotateFlip(RotateFlipType.Rotate270FlipNone); + } + + g.DrawImage(arrowImage, rect); + } + } + + /// + /// Draws the arrow down button for the scrollbar. + /// + /// The button state. + /// The arrow down button as . + private static Image GetArrowDownButtonImage( + ScrollBarArrowButtonState state) + { + Rectangle rect = new Rectangle(0, 0, 15, 17); + Bitmap bitmap = new Bitmap(15, 17, PixelFormat.Format32bppArgb); + bitmap.SetResolution(72f, 72f); + + using (Graphics g = Graphics.FromImage(bitmap)) + { + g.SmoothingMode = SmoothingMode.None; + g.InterpolationMode = InterpolationMode.Low; + + int index = -1; + + switch (state) + { + case ScrollBarArrowButtonState.UpHot: + case ScrollBarArrowButtonState.DownHot: + { + index = 1; + + break; + } + + case ScrollBarArrowButtonState.UpActive: + case ScrollBarArrowButtonState.DownActive: + { + index = 0; + + break; + } + + case ScrollBarArrowButtonState.UpPressed: + case ScrollBarArrowButtonState.DownPressed: + { + index = 2; + + break; + } + } + + if (index != -1) + { + using (Pen p1 = new Pen(arrowBorderColors[0]), + p2 = new Pen(arrowBorderColors[1])) + { + g.DrawLine(p1, rect.X, rect.Y, rect.Right - 1, rect.Y); + g.DrawLine(p2, rect.X, rect.Bottom - 1, rect.Right - 1, rect.Bottom - 1); + } + + rect.Inflate(0, -1); + + using (LinearGradientBrush brush = new LinearGradientBrush( + rect, + arrowBorderColors[2], + arrowBorderColors[1], + LinearGradientMode.Vertical)) + { + ColorBlend blend = new ColorBlend(3) + { + Positions = new[] { 0f, .5f, 1f }, + Colors = new[] { + arrowBorderColors[2], + arrowBorderColors[3], + arrowBorderColors[0] } + }; + + brush.InterpolationColors = blend; + + using (Pen p = new Pen(brush)) + { + g.DrawLine(p, rect.X, rect.Y, rect.X, rect.Bottom - 1); + g.DrawLine(p, rect.Right - 1, rect.Y, rect.Right - 1, rect.Bottom - 1); + } + } + + rect.Inflate(0, 1); + + Rectangle upper = rect; + upper.Inflate(-1, 0); + upper.Y++; + upper.Height = 7; + + using (LinearGradientBrush brush = new LinearGradientBrush( + upper, + arrowColors[index, 2], + arrowColors[index, 3], + LinearGradientMode.Vertical)) + { + g.FillRectangle(brush, upper); + } + + upper.Inflate(-1, 0); + upper.Height = 6; + + using (LinearGradientBrush brush = new LinearGradientBrush( + upper, + arrowColors[index, 0], + arrowColors[index, 1], + LinearGradientMode.Vertical)) + { + g.FillRectangle(brush, upper); + } + + Rectangle lower = rect; + lower.Inflate(-1, 0); + lower.Y = 8; + lower.Height = 8; + + using (LinearGradientBrush brush = new LinearGradientBrush( + lower, + arrowColors[index, 6], + arrowColors[index, 7], + LinearGradientMode.Vertical)) + { + g.FillRectangle(brush, lower); + } + + lower.Inflate(-1, 0); + + using (LinearGradientBrush brush = new LinearGradientBrush( + lower, + arrowColors[index, 4], + arrowColors[index, 5], + LinearGradientMode.Vertical)) + { + g.FillRectangle(brush, lower); + } + } + + using (Image arrowIcon = Properties.ScrollBarResources.ScrollBarArrowDown) + { + if (state == ScrollBarArrowButtonState.DownDisabled + || state == ScrollBarArrowButtonState.UpDisabled) + { + System.Windows.Forms.ControlPaint.DrawImageDisabled( + g, + arrowIcon, + 3, + 6, + Color.Transparent); + } + else + { + g.DrawImage(arrowIcon, 3, 6); + } + } + } + + return bitmap; + } + + /// + /// Creates a rounded rectangle. + /// + /// The rectangle to create the rounded rectangle from. + /// The x-radius. + /// The y-radius. + /// A object representing the rounded rectangle. + private static GraphicsPath CreateRoundPath( + Rectangle r, + float radiusX, + float radiusY) + { + // create new graphics path object + GraphicsPath path = new GraphicsPath(); + + // calculate radius of edges + PointF d = new PointF(Math.Min(radiusX * 2, r.Width), Math.Min(radiusY * 2, r.Height)); + + // make sure radius is valid + d.X = Math.Max(1, d.X); + d.Y = Math.Max(1, d.Y); + + // add top left arc + path.AddArc(r.X, r.Y, d.X, d.Y, 180, 90); + + // add top right arc + path.AddArc(r.Right - d.X, r.Y, d.X, d.Y, 270, 90); + + // add bottom right arc + path.AddArc(r.Right - d.X, r.Bottom - d.Y, d.X, d.Y, 0, 90); + + // add bottom left arc + path.AddArc(r.X, r.Bottom - d.Y, d.X, d.Y, 90, 90); + + // close path + path.CloseFigure(); + + return path; + } + + #endregion + + #endregion + } +} diff --git a/CustomScrollBar/ScrollBarOrientation.cs b/CustomScrollBar/ScrollBarOrientation.cs new file mode 100644 index 0000000..fc1c1bf --- /dev/null +++ b/CustomScrollBar/ScrollBarOrientation.cs @@ -0,0 +1,18 @@ +namespace CustomScrollBar +{ + /// + /// Enum for the scrollbar orientation. + /// + public enum ScrollBarOrientation + { + /// + /// Indicates a horizontal scrollbar. + /// + Horizontal, + + /// + /// Indicates a vertical scrollbar. + /// + Vertical + } +} diff --git a/CustomScrollBar/ScrollBarState.cs b/CustomScrollBar/ScrollBarState.cs new file mode 100644 index 0000000..ccc7678 --- /dev/null +++ b/CustomScrollBar/ScrollBarState.cs @@ -0,0 +1,33 @@ +namespace CustomScrollBar +{ + /// + /// The scrollbar states. + /// + internal enum ScrollBarState + { + /// + /// Indicates a normal scrollbar state. + /// + Normal, + + /// + /// Indicates a hot scrollbar state. + /// + Hot, + + /// + /// Indicates an active scrollbar state. + /// + Active, + + /// + /// Indicates a pressed scrollbar state. + /// + Pressed, + + /// + /// Indicates a disabled scrollbar state. + /// + Disabled + } +} diff --git a/CustomScrollBar/app.config b/CustomScrollBar/app.config new file mode 100644 index 0000000..0df7832 --- /dev/null +++ b/CustomScrollBar/app.config @@ -0,0 +1,3 @@ + + + diff --git a/CustomScrollBar/packages.config b/CustomScrollBar/packages.config new file mode 100644 index 0000000..d0c7012 --- /dev/null +++ b/CustomScrollBar/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/DotNetZip/BZip2/BZip2 DLL.csproj b/DotNetZip/BZip2/BZip2 DLL.csproj new file mode 100644 index 0000000..79372c8 --- /dev/null +++ b/DotNetZip/BZip2/BZip2 DLL.csproj @@ -0,0 +1,161 @@ + + + + Local + 2.0 + Debug + AnyCPU + + + + + Ionic.BZip2 + ..\Ionic.snk + JScript + Grid + IE50 + false + Library + Ionic.BZip2 + false + OnBuildSuccess + + + + + 3.5 + + + {e2ce0d56-7af8-4404-bd0c-bc562cbd74d4} + false + true + SAK + SAK + SAK + SAK + v2.0 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + true + + + bin\Debug\ + false + 285212672 + false + + + + + bin\Debug\Ionic.BZip2.XML + true + 4096 + false + + + false + false + false + 4 + full + prompt + AllRules.ruleset + + + bin\Release\ + false + 285212672 + false + + + + + + + true + 4096 + false + + + false + false + false + 4 + full + prompt + AllRules.ruleset + + + + mscorlib + + + System + + + + + + + + + + + + CRC32.cs + + + SolutionInfo.cs + + + + + Ionic.snk + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 2.0 %28x86%29 + true + + + False + .NET Framework 3.0 %28x86%29 + false + + + False + .NET Framework 3.5 + false + + + False + .NET Framework 3.5 SP1 + false + + + + + + + + + + \ No newline at end of file diff --git a/DotNetZip/BZip2/BZip2Compressor.cs b/DotNetZip/BZip2/BZip2Compressor.cs new file mode 100644 index 0000000..b09b16b --- /dev/null +++ b/DotNetZip/BZip2/BZip2Compressor.cs @@ -0,0 +1,1920 @@ +// BZip2Compressor.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2011 Dino Chiesa. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// Last Saved: <2011-July-28 06:17:22> +// +// ------------------------------------------------------------------ +// +// This module defines the BZip2Compressor class, which is a +// BZIP2-compressing encoder. It is used internally in the BZIP2 +// library, by the BZip2OutputStream class and its parallel variant, +// ParallelBZip2OutputStream. This code was originally based on Apache +// commons source code, and significantly modified. The license below +// applies to the original Apache code and to this modified variant. +// +// ------------------------------------------------------------------ + + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* + * This package is based on the work done by Keiron Liddle, Aftex Software + * to whom the Ant project is very grateful for his + * great code. + */ + + +// +// Design notes: +// +// This class performs BZip2 compression. It is derived from the +// BZip2OutputStream from the Apache commons source code, but is +// significantly modified from that code. While the Apache class is a +// stream that compresses, this particular class simply performs +// compression. It follows a Manager pattern. It manages an internal +// buffer for uncompressed data; callers place data into the buffer +// using the Fill() method. This class then compresses the data and +// writes the compressed form out, via the CompressAndWrite() method. +// Because BZip2 uses byte-shredding, this class relies on a BitWriter, +// and not a .NET Stream, to emit its output. (*Think of the BitWriter +// class as an Adapter that enables Bit-oriented output to a standard +// byte-oriented .NET stream.) +// +// This class exists to support the two distinct output streams that +// perform BZip2 compression: BZip2OutputStream and +// ParallelBZip2OutputStream. These streams rely on BZip2Compressor to +// provide the encoder/compression logic. This code has been derived +// from the bzip2 output stream in the Apache commons library; it has +// been significantly modified from that form, in order to provide a +// single compressor that could support both types of streams. +// +// In a bz2 file or stream, there is never any bit padding except for 0..7 +// bits in the final byte in the file. Successive compressed blocks in a +// .bz2 file are not byte-aligned. +// +// + +using System; +using System.IO; + +// flymake: csc.exe /t:module BZip2InputStream.cs BZip2OutputStream.cs Rand.cs BCRC32.cs @@FILE@@ + +namespace Ionic.BZip2 +{ + internal class BZip2Compressor + { + private int blockSize100k; // 0...9 + private int currentByte = -1; + private int runLength = 0; + private int last; // index into the block of the last char processed + private int outBlockFillThreshold; + private CompressionState cstate; + private readonly Ionic.Crc.CRC32 crc = new Ionic.Crc.CRC32(true); + BitWriter bw; + int runs; + + /* + * The following three vars are used when sorting. If too many long + * comparisons happen, we stop sorting, randomise the block slightly, and + * try again. I think this wrinkle in the implementation was removed from + * a later rev of the C-language bzip, not sure. -DPC 24 Jul 2011 + * + */ + private int workDone; + private int workLimit; + private bool firstAttempt; + private bool blockRandomised; + private int origPtr; + + private int nInUse; + private int nMTF; + + private static readonly int SETMASK = (1 << 21); + private static readonly int CLEARMASK = (~SETMASK); + private static readonly byte GREATER_ICOST = 15; + private static readonly byte LESSER_ICOST = 0; + private static readonly int SMALL_THRESH = 20; + private static readonly int DEPTH_THRESH = 10; + private static readonly int WORK_FACTOR = 30; + + /** + * Knuth's increments seem to work better than Incerpi-Sedgewick here. + * Possibly because the number of elems to sort is usually small, typically + * <= 20. + */ + private static readonly int[] increments = { 1, 4, 13, 40, 121, 364, 1093, 3280, + 9841, 29524, 88573, 265720, 797161, + 2391484 }; + + /// + /// BZip2Compressor writes its compressed data out via a BitWriter. This + /// is necessary because BZip2 does byte shredding. + /// + public BZip2Compressor(BitWriter writer) + : this(writer, BZip2.MaxBlockSize) + { + } + + public BZip2Compressor(BitWriter writer, int blockSize) + { + this.blockSize100k = blockSize; + this.bw = writer; + + // 20 provides a margin of slop (not to say "Safety"). The maximum + // size of an encoded run in the output block is 5 bytes, so really, 5 + // bytes ought to do, but this is a margin of slop found in the + // original bzip code. Not sure if important for decoding + // (decompressing). So we'll leave the slop. + this.outBlockFillThreshold = (blockSize * BZip2.BlockSizeMultiple) - 20; + this.cstate = new CompressionState(blockSize); + Reset(); + } + + + void Reset() + { + // initBlock(); + this.crc.Reset(); + this.currentByte = -1; + this.runLength = 0; + this.last = -1; + for (int i = 256; --i >= 0;) + this.cstate.inUse[i] = false; + //bw.Reset(); xxx? want this? no no no + } + + + public int BlockSize + { + get { return this.blockSize100k; } + } + + public uint Crc32 + { + get; private set; + } + + public int AvailableBytesOut + { + get; private set; + } + + /// + /// The number of uncompressed bytes being held in the buffer. + /// + /// + /// + /// I am thinking this may be useful in a Stream that uses this + /// compressor class. In the Close() method on the stream it could + /// check this value to see if anything has been written at all. You + /// may think the stream could easily track the number of bytes it + /// wrote, which would eliminate the need for this. But, there is the + /// case where the stream writes a complete block, and it is full, and + /// then writes no more. In that case the stream may want to check. + /// + /// + public int UncompressedBytes + { + get { return this.last + 1; } + } + + + /// + /// Accept new bytes into the compressor data buffer + /// + /// + /// + /// This method does the first-level (cheap) run-length encoding, and + /// stores the encoded data into the rle block. + /// + /// + public int Fill(byte[] buffer, int offset, int count) + { + if (this.last >= this.outBlockFillThreshold) + return 0; // We're full, I tell you! + + int bytesWritten = 0; + int limit = offset + count; + int rc; + + // do run-length-encoding until block is full + do + { + rc = write0(buffer[offset++]); + if (rc > 0) bytesWritten++; + } while (offset < limit && rc == 1); + + return bytesWritten; + } + + + + /// + /// Process one input byte into the block. + /// + /// + /// + /// + /// To "process" the byte means to do the run-length encoding. + /// There are 3 possible return values: + /// + /// 0 - the byte was not written, in other words, not + /// encoded into the block. This happens when the + /// byte b would require the start of a new run, and + /// the block has no more room for new runs. + /// + /// 1 - the byte was written, and the block is not full. + /// + /// 2 - the byte was written, and the block is full. + /// + /// + /// + /// 0 if the byte was not written, non-zero if written. + private int write0(byte b) + { + bool rc; + // there is no current run in progress + if (this.currentByte == -1) + { + this.currentByte = b; + this.runLength++; + return 1; + } + + // this byte is the same as the current run in progress + if (this.currentByte == b) + { + if (++this.runLength > 254) + { + rc = AddRunToOutputBlock(false); + this.currentByte = -1; + this.runLength = 0; + return (rc) ? 2 : 1; + } + return 1; // not full + } + + // This byte requires a new run. + // Put the prior run into the Run-length-encoded block, + // and try to start a new run. + rc = AddRunToOutputBlock(false); + + if (rc) + { + this.currentByte = -1; + this.runLength = 0; + // returning 0 implies the block is full, and the byte was not written. + return 0; + } + + // start a new run + this.runLength = 1; + this.currentByte = b; + return 1; + } + + /// + /// Append one run to the output block. + /// + /// + /// + /// + /// This compressor does run-length-encoding before BWT and etc. This + /// method simply appends a run to the output block. The append always + /// succeeds. The return value indicates whether the block is full: + /// false (not full) implies that at least one additional run could be + /// processed. + /// + /// + /// true if the block is now full; otherwise false. + private bool AddRunToOutputBlock(bool final) + { + runs++; + /* add_pair_to_block ( EState* s ) */ + int previousLast = this.last; + + // sanity check only - because of the check done at the + // bottom of this method, and the logic in write0(), this + // should never ever happen. + if (previousLast >= this.outBlockFillThreshold && !final) + { + var msg = String.Format("block overrun(final={2}): {0} >= threshold ({1})", + previousLast, this.outBlockFillThreshold, final); + throw new Exception(msg); + } + + // NB: the index used here into block is always (last+2). This is + // because last is -1 based - the initial value is -1, a flag value + // used to indicate that nothing has yet been written into the + // block. The endBlock() fn tests for -1 to detect empty blocks. Also, + // the first byte of block is used, during sorting, to hold block[last + // +1], which is the final byte value that had been written into the + // rle block. For those two reasons, the base offset from last is + // always +2. + + byte b = (byte) this.currentByte; + byte[] block = this.cstate.block; + this.cstate.inUse[b] = true; + int rl = this.runLength; + this.crc.UpdateCRC(b, rl); + + switch (rl) + { + case 1: + block[previousLast + 2] = b; + this.last = previousLast + 1; + break; + + case 2: + block[previousLast + 2] = b; + block[previousLast + 3] = b; + this.last = previousLast + 2; + break; + + case 3: + block[previousLast + 2] = b; + block[previousLast + 3] = b; + block[previousLast + 4] = b; + this.last = previousLast + 3; + break; + + default: + rl -= 4; + this.cstate.inUse[rl] = true; + block[previousLast + 2] = b; + block[previousLast + 3] = b; + block[previousLast + 4] = b; + block[previousLast + 5] = b; + block[previousLast + 6] = (byte) rl; + this.last = previousLast + 5; + break; + } + + // is full? + return (this.last >= this.outBlockFillThreshold); + } + + + /// + /// Compress the data that has been placed (Run-length-encoded) into the + /// block. The compressed data goes into the CompressedBytes array. + /// + /// + /// + /// Side effects: 1. fills the CompressedBytes array. 2. sets the + /// AvailableBytesOut property. + /// + /// + public void CompressAndWrite() // endBlock + { + if (this.runLength > 0) + AddRunToOutputBlock(true); + + this.currentByte = -1; + + // Console.WriteLine(" BZip2Compressor:CompressAndWrite (r={0} bcrc={1:X8})", + // runs, this.crc.Crc32Result); + + // has any data been written? + if (this.last == -1) + return; // no data; nothing to compress + + /* sort the block and establish posn of original string */ + blockSort(); + + /* + * A 6-byte block header, the value chosen arbitrarily as 0x314159265359 + * :-). A 32 bit value does not really give a strong enough guarantee + * that the value will not appear by chance in the compressed + * datastream. Worst-case probability of this event, for a 900k block, + * is about 2.0e-3 for 32 bits, 1.0e-5 for 40 bits and 4.0e-8 for 48 + * bits. For a compressed file of size 100Gb -- about 100000 blocks -- + * only a 48-bit marker will do. NB: normal compression/ decompression + * donot rely on these statistical properties. They are only important + * when trying to recover blocks from damaged files. + */ + this.bw.WriteByte(0x31); + this.bw.WriteByte(0x41); + this.bw.WriteByte(0x59); + this.bw.WriteByte(0x26); + this.bw.WriteByte(0x53); + this.bw.WriteByte(0x59); + + this.Crc32 = (uint) this.crc.Crc32Result; + this.bw.WriteInt(this.Crc32); + + /* Now a single bit indicating randomisation. */ + this.bw.WriteBits(1, (this.blockRandomised)?1U:0U); + + /* Finally, block's contents proper. */ + moveToFrontCodeAndSend(); + + Reset(); + } + + + private void randomiseBlock() + { + bool[] inUse = this.cstate.inUse; + byte[] block = this.cstate.block; + int lastShadow = this.last; + + for (int i = 256; --i >= 0;) + inUse[i] = false; + + int rNToGo = 0; + int rTPos = 0; + for (int i = 0, j = 1; i <= lastShadow; i = j, j++) + { + if (rNToGo == 0) + { + rNToGo = (char) Rand.Rnums(rTPos); + if (++rTPos == 512) + { + rTPos = 0; + } + } + + rNToGo--; + block[j] ^= (byte) ((rNToGo == 1) ? 1 : 0); + + // handle 16 bit signed numbers + inUse[block[j] & 0xff] = true; + } + + this.blockRandomised = true; + } + + private void mainSort() + { + CompressionState dataShadow = this.cstate; + int[] runningOrder = dataShadow.mainSort_runningOrder; + int[] copy = dataShadow.mainSort_copy; + bool[] bigDone = dataShadow.mainSort_bigDone; + int[] ftab = dataShadow.ftab; + byte[] block = dataShadow.block; + int[] fmap = dataShadow.fmap; + char[] quadrant = dataShadow.quadrant; + int lastShadow = this.last; + int workLimitShadow = this.workLimit; + bool firstAttemptShadow = this.firstAttempt; + + // Set up the 2-byte frequency table + for (int i = 65537; --i >= 0;) + { + ftab[i] = 0; + } + + /* + * In the various block-sized structures, live data runs from 0 to + * last+NUM_OVERSHOOT_BYTES inclusive. First, set up the overshoot area + * for block. + */ + for (int i = 0; i < BZip2.NUM_OVERSHOOT_BYTES; i++) + { + block[lastShadow + i + 2] = block[(i % (lastShadow + 1)) + 1]; + } + for (int i = lastShadow + BZip2.NUM_OVERSHOOT_BYTES +1; --i >= 0;) + { + quadrant[i] = '\0'; + } + block[0] = block[lastShadow + 1]; + + // Complete the initial radix sort: + + int c1 = block[0] & 0xff; + for (int i = 0; i <= lastShadow; i++) + { + int c2 = block[i + 1] & 0xff; + ftab[(c1 << 8) + c2]++; + c1 = c2; + } + + for (int i = 1; i <= 65536; i++) + ftab[i] += ftab[i - 1]; + + c1 = block[1] & 0xff; + for (int i = 0; i < lastShadow; i++) + { + int c2 = block[i + 2] & 0xff; + fmap[--ftab[(c1 << 8) + c2]] = i; + c1 = c2; + } + + fmap[--ftab[((block[lastShadow + 1] & 0xff) << 8) + (block[1] & 0xff)]] = lastShadow; + + /* + * Now ftab contains the first loc of every small bucket. Calculate the + * running order, from smallest to largest big bucket. + */ + for (int i = 256; --i >= 0;) + { + bigDone[i] = false; + runningOrder[i] = i; + } + + for (int h = 364; h != 1;) + { + h /= 3; + for (int i = h; i <= 255; i++) + { + int vv = runningOrder[i]; + int a = ftab[(vv + 1) << 8] - ftab[vv << 8]; + int b = h - 1; + int j = i; + for (int ro = runningOrder[j - h]; (ftab[(ro + 1) << 8] - ftab[ro << 8]) > a; ro = runningOrder[j + - h]) + { + runningOrder[j] = ro; + j -= h; + if (j <= b) + { + break; + } + } + runningOrder[j] = vv; + } + } + + /* + * The main sorting loop. + */ + for (int i = 0; i <= 255; i++) + { + /* + * Process big buckets, starting with the least full. + */ + int ss = runningOrder[i]; + + // Step 1: + /* + * Complete the big bucket [ss] by quicksorting any unsorted small + * buckets [ss, j]. Hopefully previous pointer-scanning phases have + * already completed many of the small buckets [ss, j], so we don't + * have to sort them at all. + */ + for (int j = 0; j <= 255; j++) + { + int sb = (ss << 8) + j; + int ftab_sb = ftab[sb]; + if ((ftab_sb & SETMASK) != SETMASK) + { + int lo = ftab_sb & CLEARMASK; + int hi = (ftab[sb + 1] & CLEARMASK) - 1; + if (hi > lo) + { + mainQSort3(dataShadow, lo, hi, 2); + if (firstAttemptShadow + && (this.workDone > workLimitShadow)) + { + return; + } + } + ftab[sb] = ftab_sb | SETMASK; + } + } + + // Step 2: + // Now scan this big bucket so as to synthesise the + // sorted order for small buckets [t, ss] for all t != ss. + + for (int j = 0; j <= 255; j++) + { + copy[j] = ftab[(j << 8) + ss] & CLEARMASK; + } + + for (int j = ftab[ss << 8] & CLEARMASK, hj = (ftab[(ss + 1) << 8] & CLEARMASK); j < hj; j++) + { + int fmap_j = fmap[j]; + c1 = block[fmap_j] & 0xff; + if (!bigDone[c1]) + { + fmap[copy[c1]] = (fmap_j == 0) ? lastShadow : (fmap_j - 1); + copy[c1]++; + } + } + + for (int j = 256; --j >= 0;) + ftab[(j << 8) + ss] |= SETMASK; + + // Step 3: + /* + * The ss big bucket is now done. Record this fact, and update the + * quadrant descriptors. Remember to update quadrants in the + * overshoot area too, if necessary. The "if (i < 255)" test merely + * skips this updating for the last bucket processed, since updating + * for the last bucket is pointless. + */ + bigDone[ss] = true; + + if (i < 255) + { + int bbStart = ftab[ss << 8] & CLEARMASK; + int bbSize = (ftab[(ss + 1) << 8] & CLEARMASK) - bbStart; + int shifts = 0; + + while ((bbSize >> shifts) > 65534) + { + shifts++; + } + + for (int j = 0; j < bbSize; j++) + { + int a2update = fmap[bbStart + j]; + char qVal = (char) (j >> shifts); + quadrant[a2update] = qVal; + if (a2update < BZip2.NUM_OVERSHOOT_BYTES) + { + quadrant[a2update + lastShadow + 1] = qVal; + } + } + } + + } + } + + + private void blockSort() + { + this.workLimit = WORK_FACTOR * this.last; + this.workDone = 0; + this.blockRandomised = false; + this.firstAttempt = true; + mainSort(); + + if (this.firstAttempt && (this.workDone > this.workLimit)) + { + randomiseBlock(); + this.workLimit = this.workDone = 0; + this.firstAttempt = false; + mainSort(); + } + + int[] fmap = this.cstate.fmap; + this.origPtr = -1; + for (int i = 0, lastShadow = this.last; i <= lastShadow; i++) + { + if (fmap[i] == 0) + { + this.origPtr = i; + break; + } + } + + // assert (this.origPtr != -1) : this.origPtr; + } + + + /** + * This is the most hammered method of this class. + * + *

+ * This is the version using unrolled loops. + *

+ */ + private bool mainSimpleSort(CompressionState dataShadow, int lo, + int hi, int d) + { + int bigN = hi - lo + 1; + if (bigN < 2) + { + return this.firstAttempt && (this.workDone > this.workLimit); + } + + int hp = 0; + while (increments[hp] < bigN) + hp++; + + int[] fmap = dataShadow.fmap; + char[] quadrant = dataShadow.quadrant; + byte[] block = dataShadow.block; + int lastShadow = this.last; + int lastPlus1 = lastShadow + 1; + bool firstAttemptShadow = this.firstAttempt; + int workLimitShadow = this.workLimit; + int workDoneShadow = this.workDone; + + // Following block contains unrolled code which could be shortened by + // coding it in additional loops. + + // HP: + while (--hp >= 0) + { + int h = increments[hp]; + int mj = lo + h - 1; + + for (int i = lo + h; i <= hi;) + { + // copy + for (int k = 3; (i <= hi) && (--k >= 0); i++) + { + int v = fmap[i]; + int vd = v + d; + int j = i; + + // for (int a; + // (j > mj) && mainGtU((a = fmap[j - h]) + d, vd, + // block, quadrant, lastShadow); + // j -= h) { + // fmap[j] = a; + // } + // + // unrolled version: + + // start inline mainGTU + bool onceRunned = false; + int a = 0; + + HAMMER: while (true) + { + if (onceRunned) + { + fmap[j] = a; + if ((j -= h) <= mj) + { + goto END_HAMMER; + } + } + else { + onceRunned = true; + } + + a = fmap[j - h]; + int i1 = a + d; + int i2 = vd; + + // following could be done in a loop, but + // unrolled it for performance: + if (block[i1 + 1] == block[i2 + 1]) + { + if (block[i1 + 2] == block[i2 + 2]) + { + if (block[i1 + 3] == block[i2 + 3]) + { + if (block[i1 + 4] == block[i2 + 4]) + { + if (block[i1 + 5] == block[i2 + 5]) + { + if (block[(i1 += 6)] == block[(i2 += 6)]) + { + int x = lastShadow; + X: while (x > 0) + { + x -= 4; + + if (block[i1 + 1] == block[i2 + 1]) + { + if (quadrant[i1] == quadrant[i2]) + { + if (block[i1 + 2] == block[i2 + 2]) + { + if (quadrant[i1 + 1] == quadrant[i2 + 1]) + { + if (block[i1 + 3] == block[i2 + 3]) + { + if (quadrant[i1 + 2] == quadrant[i2 + 2]) + { + if (block[i1 + 4] == block[i2 + 4]) + { + if (quadrant[i1 + 3] == quadrant[i2 + 3]) + { + if ((i1 += 4) >= lastPlus1) + { + i1 -= lastPlus1; + } + if ((i2 += 4) >= lastPlus1) + { + i2 -= lastPlus1; + } + workDoneShadow++; + goto X; + } + else if ((quadrant[i1 + 3] > quadrant[i2 + 3])) + { + goto HAMMER; + } + else { + goto END_HAMMER; + } + } + else if ((block[i1 + 4] & 0xff) > (block[i2 + 4] & 0xff)) + { + goto HAMMER; + } + else { + goto END_HAMMER; + } + } + else if ((quadrant[i1 + 2] > quadrant[i2 + 2])) + { + goto HAMMER; + } + else { + goto END_HAMMER; + } + } + else if ((block[i1 + 3] & 0xff) > (block[i2 + 3] & 0xff)) + { + goto HAMMER; + } + else { + goto END_HAMMER; + } + } + else if ((quadrant[i1 + 1] > quadrant[i2 + 1])) + { + goto HAMMER; + } + else { + goto END_HAMMER; + } + } + else if ((block[i1 + 2] & 0xff) > (block[i2 + 2] & 0xff)) + { + goto HAMMER; + } + else { + goto END_HAMMER; + } + } + else if ((quadrant[i1] > quadrant[i2])) + { + goto HAMMER; + } + else { + goto END_HAMMER; + } + } + else if ((block[i1 + 1] & 0xff) > (block[i2 + 1] & 0xff)) + { + goto HAMMER; + } + else { + goto END_HAMMER; + } + + } + goto END_HAMMER; + } // while x > 0 + else { + if ((block[i1] & 0xff) > (block[i2] & 0xff)) + { + goto HAMMER; + } + else { + goto END_HAMMER; + } + } + } + else if ((block[i1 + 5] & 0xff) > (block[i2 + 5] & 0xff)) + { + goto HAMMER; + } + else { + goto END_HAMMER; + } + } + else if ((block[i1 + 4] & 0xff) > (block[i2 + 4] & 0xff)) + { + goto HAMMER; + } + else { + goto END_HAMMER; + } + } + else if ((block[i1 + 3] & 0xff) > (block[i2 + 3] & 0xff)) + { + goto HAMMER; + } + else { + goto END_HAMMER; + } + } + else if ((block[i1 + 2] & 0xff) > (block[i2 + 2] & 0xff)) + { + goto HAMMER; + } + else { + goto END_HAMMER; + } + } + else if ((block[i1 + 1] & 0xff) > (block[i2 + 1] & 0xff)) + { + goto HAMMER; + } + else { + goto END_HAMMER; + } + + } // HAMMER + + END_HAMMER: + // end inline mainGTU + + fmap[j] = v; + } + + if (firstAttemptShadow && (i <= hi) + && (workDoneShadow > workLimitShadow)) + { + goto END_HP; + } + } + } + END_HP: + + this.workDone = workDoneShadow; + return firstAttemptShadow && (workDoneShadow > workLimitShadow); + } + + + + private static void vswap(int[] fmap, int p1, int p2, int n) + { + n += p1; + while (p1 < n) + { + int t = fmap[p1]; + fmap[p1++] = fmap[p2]; + fmap[p2++] = t; + } + } + + private static byte med3(byte a, byte b, byte c) + { + return (a < b) ? (b < c ? b : a < c ? c : a) : (b > c ? b : a > c ? c + : a); + } + + + /** + * Method "mainQSort3", file "blocksort.c", BZip2 1.0.2 + */ + private void mainQSort3(CompressionState dataShadow, int loSt, + int hiSt, int dSt) + { + int[] stack_ll = dataShadow.stack_ll; + int[] stack_hh = dataShadow.stack_hh; + int[] stack_dd = dataShadow.stack_dd; + int[] fmap = dataShadow.fmap; + byte[] block = dataShadow.block; + + stack_ll[0] = loSt; + stack_hh[0] = hiSt; + stack_dd[0] = dSt; + + for (int sp = 1; --sp >= 0;) + { + int lo = stack_ll[sp]; + int hi = stack_hh[sp]; + int d = stack_dd[sp]; + + if ((hi - lo < SMALL_THRESH) || (d > DEPTH_THRESH)) + { + if (mainSimpleSort(dataShadow, lo, hi, d)) + { + return; + } + } + else { + int d1 = d + 1; + int med = med3(block[fmap[lo] + d1], + block[fmap[hi] + d1], block[fmap[(lo + hi) >> 1] + d1]) & 0xff; + + int unLo = lo; + int unHi = hi; + int ltLo = lo; + int gtHi = hi; + + while (true) + { + while (unLo <= unHi) + { + int n = (block[fmap[unLo] + d1] & 0xff) + - med; + if (n == 0) + { + int temp = fmap[unLo]; + fmap[unLo++] = fmap[ltLo]; + fmap[ltLo++] = temp; + } + else if (n < 0) + { + unLo++; + } + else { + break; + } + } + + while (unLo <= unHi) + { + int n = (block[fmap[unHi] + d1] & 0xff) + - med; + if (n == 0) + { + int temp = fmap[unHi]; + fmap[unHi--] = fmap[gtHi]; + fmap[gtHi--] = temp; + } + else if (n > 0) + { + unHi--; + } + else { + break; + } + } + + if (unLo <= unHi) + { + int temp = fmap[unLo]; + fmap[unLo++] = fmap[unHi]; + fmap[unHi--] = temp; + } + else { + break; + } + } + + if (gtHi < ltLo) + { + stack_ll[sp] = lo; + stack_hh[sp] = hi; + stack_dd[sp] = d1; + sp++; + } + else { + int n = ((ltLo - lo) < (unLo - ltLo)) ? (ltLo - lo) + : (unLo - ltLo); + vswap(fmap, lo, unLo - n, n); + int m = ((hi - gtHi) < (gtHi - unHi)) ? (hi - gtHi) + : (gtHi - unHi); + vswap(fmap, unLo, hi - m + 1, m); + + n = lo + unLo - ltLo - 1; + m = hi - (gtHi - unHi) + 1; + + stack_ll[sp] = lo; + stack_hh[sp] = n; + stack_dd[sp] = d; + sp++; + + stack_ll[sp] = n + 1; + stack_hh[sp] = m - 1; + stack_dd[sp] = d1; + sp++; + + stack_ll[sp] = m; + stack_hh[sp] = hi; + stack_dd[sp] = d; + sp++; + } + } + } + } + + + + private void generateMTFValues() + { + int lastShadow = this.last; + CompressionState dataShadow = this.cstate; + bool[] inUse = dataShadow.inUse; + byte[] block = dataShadow.block; + int[] fmap = dataShadow.fmap; + char[] sfmap = dataShadow.sfmap; + int[] mtfFreq = dataShadow.mtfFreq; + byte[] unseqToSeq = dataShadow.unseqToSeq; + byte[] yy = dataShadow.generateMTFValues_yy; + + // make maps + int nInUseShadow = 0; + for (int i = 0; i < 256; i++) + { + if (inUse[i]) + { + unseqToSeq[i] = (byte) nInUseShadow; + nInUseShadow++; + } + } + this.nInUse = nInUseShadow; + + int eob = nInUseShadow + 1; + + for (int i = eob; i >= 0; i--) + { + mtfFreq[i] = 0; + } + + for (int i = nInUseShadow; --i >= 0;) + { + yy[i] = (byte) i; + } + + int wr = 0; + int zPend = 0; + + for (int i = 0; i <= lastShadow; i++) + { + byte ll_i = unseqToSeq[block[fmap[i]] & 0xff]; + byte tmp = yy[0]; + int j = 0; + + while (ll_i != tmp) + { + j++; + byte tmp2 = tmp; + tmp = yy[j]; + yy[j] = tmp2; + } + yy[0] = tmp; + + if (j == 0) + { + zPend++; + } + else + { + if (zPend > 0) + { + zPend--; + while (true) + { + if ((zPend & 1) == 0) + { + sfmap[wr] = BZip2.RUNA; + wr++; + mtfFreq[BZip2.RUNA]++; + } + else + { + sfmap[wr] = BZip2.RUNB; + wr++; + mtfFreq[BZip2.RUNB]++; + } + + if (zPend >= 2) + { + zPend = (zPend - 2) >> 1; + } + else + { + break; + } + } + zPend = 0; + } + sfmap[wr] = (char) (j + 1); + wr++; + mtfFreq[j + 1]++; + } + } + + if (zPend > 0) + { + zPend--; + while (true) + { + if ((zPend & 1) == 0) + { + sfmap[wr] = BZip2.RUNA; + wr++; + mtfFreq[BZip2.RUNA]++; + } + else + { + sfmap[wr] = BZip2.RUNB; + wr++; + mtfFreq[BZip2.RUNB]++; + } + + if (zPend >= 2) + { + zPend = (zPend - 2) >> 1; + } + else + { + break; + } + } + } + + sfmap[wr] = (char) eob; + mtfFreq[eob]++; + this.nMTF = wr + 1; + } + + + private static void hbAssignCodes(int[] code, byte[] length, + int minLen, int maxLen, + int alphaSize) + { + int vec = 0; + for (int n = minLen; n <= maxLen; n++) + { + for (int i = 0; i < alphaSize; i++) + { + if ((length[i] & 0xff) == n) + { + code[i] = vec; + vec++; + } + } + vec <<= 1; + } + } + + + + + private void sendMTFValues() + { + byte[][] len = this.cstate.sendMTFValues_len; + int alphaSize = this.nInUse + 2; + + for (int t = BZip2.NGroups; --t >= 0;) + { + byte[] len_t = len[t]; + for (int v = alphaSize; --v >= 0;) + { + len_t[v] = GREATER_ICOST; + } + } + + /* Decide how many coding tables to use */ + // assert (this.nMTF > 0) : this.nMTF; + int nGroups = (this.nMTF < 200) ? 2 : (this.nMTF < 600) ? 3 + : (this.nMTF < 1200) ? 4 : (this.nMTF < 2400) ? 5 : 6; + + /* Generate an initial set of coding tables */ + sendMTFValues0(nGroups, alphaSize); + + /* + * Iterate up to N_ITERS times to improve the tables. + */ + int nSelectors = sendMTFValues1(nGroups, alphaSize); + + /* Compute MTF values for the selectors. */ + sendMTFValues2(nGroups, nSelectors); + + /* Assign actual codes for the tables. */ + sendMTFValues3(nGroups, alphaSize); + + /* Transmit the mapping table. */ + sendMTFValues4(); + + /* Now the selectors. */ + sendMTFValues5(nGroups, nSelectors); + + /* Now the coding tables. */ + sendMTFValues6(nGroups, alphaSize); + + /* And finally, the block data proper */ + sendMTFValues7(nSelectors); + } + + private void sendMTFValues0(int nGroups, int alphaSize) + { + byte[][] len = this.cstate.sendMTFValues_len; + int[] mtfFreq = this.cstate.mtfFreq; + + int remF = this.nMTF; + int gs = 0; + + for (int nPart = nGroups; nPart > 0; nPart--) + { + int tFreq = remF / nPart; + int ge = gs - 1; + int aFreq = 0; + + for (int a = alphaSize - 1; (aFreq < tFreq) && (ge < a);) + { + aFreq += mtfFreq[++ge]; + } + + if ((ge > gs) && (nPart != nGroups) && (nPart != 1) + && (((nGroups - nPart) & 1) != 0)) + { + aFreq -= mtfFreq[ge--]; + } + + byte[] len_np = len[nPart - 1]; + for (int v = alphaSize; --v >= 0;) + { + if ((v >= gs) && (v <= ge)) + { + len_np[v] = LESSER_ICOST; + } + else { + len_np[v] = GREATER_ICOST; + } + } + + gs = ge + 1; + remF -= aFreq; + } + } + + + private static void hbMakeCodeLengths(byte[] len, int[] freq, + CompressionState state1, int alphaSize, + int maxLen) + { + /* + * Nodes and heap entries run from 1. Entry 0 for both the heap and + * nodes is a sentinel. + */ + int[] heap = state1.heap; + int[] weight = state1.weight; + int[] parent = state1.parent; + + for (int i = alphaSize; --i >= 0;) + { + weight[i + 1] = (freq[i] == 0 ? 1 : freq[i]) << 8; + } + + for (bool tooLong = true; tooLong;) + { + tooLong = false; + + int nNodes = alphaSize; + int nHeap = 0; + heap[0] = 0; + weight[0] = 0; + parent[0] = -2; + + for (int i = 1; i <= alphaSize; i++) + { + parent[i] = -1; + nHeap++; + heap[nHeap] = i; + + int zz = nHeap; + int tmp = heap[zz]; + while (weight[tmp] < weight[heap[zz >> 1]]) + { + heap[zz] = heap[zz >> 1]; + zz >>= 1; + } + heap[zz] = tmp; + } + + while (nHeap > 1) + { + int n1 = heap[1]; + heap[1] = heap[nHeap]; + nHeap--; + + int yy = 0; + int zz = 1; + int tmp = heap[1]; + + while (true) + { + yy = zz << 1; + + if (yy > nHeap) + { + break; + } + + if ((yy < nHeap) + && (weight[heap[yy + 1]] < weight[heap[yy]])) + { + yy++; + } + + if (weight[tmp] < weight[heap[yy]]) + { + break; + } + + heap[zz] = heap[yy]; + zz = yy; + } + + heap[zz] = tmp; + + int n2 = heap[1]; + heap[1] = heap[nHeap]; + nHeap--; + + yy = 0; + zz = 1; + tmp = heap[1]; + + while (true) + { + yy = zz << 1; + + if (yy > nHeap) + { + break; + } + + if ((yy < nHeap) + && (weight[heap[yy + 1]] < weight[heap[yy]])) + { + yy++; + } + + if (weight[tmp] < weight[heap[yy]]) + { + break; + } + + heap[zz] = heap[yy]; + zz = yy; + } + + heap[zz] = tmp; + nNodes++; + parent[n1] = parent[n2] = nNodes; + + int weight_n1 = weight[n1]; + int weight_n2 = weight[n2]; + weight[nNodes] = (int) (((uint)weight_n1 & 0xffffff00U) + + ((uint)weight_n2 & 0xffffff00U)) + | (1 + (((weight_n1 & 0x000000ff) + > (weight_n2 & 0x000000ff)) + ? (weight_n1 & 0x000000ff) + : (weight_n2 & 0x000000ff))); + + parent[nNodes] = -1; + nHeap++; + heap[nHeap] = nNodes; + + tmp = 0; + zz = nHeap; + tmp = heap[zz]; + int weight_tmp = weight[tmp]; + while (weight_tmp < weight[heap[zz >> 1]]) + { + heap[zz] = heap[zz >> 1]; + zz >>= 1; + } + heap[zz] = tmp; + + } + + for (int i = 1; i <= alphaSize; i++) + { + int j = 0; + int k = i; + + for (int parent_k; (parent_k = parent[k]) >= 0;) + { + k = parent_k; + j++; + } + + len[i - 1] = (byte) j; + if (j > maxLen) + { + tooLong = true; + } + } + + if (tooLong) + { + for (int i = 1; i < alphaSize; i++) + { + int j = weight[i] >> 8; + j = 1 + (j >> 1); + weight[i] = j << 8; + } + } + } + } + + + private int sendMTFValues1(int nGroups, int alphaSize) + { + CompressionState dataShadow = this.cstate; + int[][] rfreq = dataShadow.sendMTFValues_rfreq; + int[] fave = dataShadow.sendMTFValues_fave; + short[] cost = dataShadow.sendMTFValues_cost; + char[] sfmap = dataShadow.sfmap; + byte[] selector = dataShadow.selector; + byte[][] len = dataShadow.sendMTFValues_len; + byte[] len_0 = len[0]; + byte[] len_1 = len[1]; + byte[] len_2 = len[2]; + byte[] len_3 = len[3]; + byte[] len_4 = len[4]; + byte[] len_5 = len[5]; + int nMTFShadow = this.nMTF; + + int nSelectors = 0; + + for (int iter = 0; iter < BZip2.N_ITERS; iter++) + { + for (int t = nGroups; --t >= 0;) + { + fave[t] = 0; + int[] rfreqt = rfreq[t]; + for (int i = alphaSize; --i >= 0;) + { + rfreqt[i] = 0; + } + } + + nSelectors = 0; + + for (int gs = 0; gs < this.nMTF;) + { + /* Set group start & end marks. */ + + /* + * Calculate the cost of this group as coded by each of the + * coding tables. + */ + + int ge = Math.Min(gs + BZip2.G_SIZE - 1, nMTFShadow - 1); + + if (nGroups == BZip2.NGroups) + { + // unrolled version of the else-block + + int[] c = new int[6]; + + for (int i = gs; i <= ge; i++) + { + int icv = sfmap[i]; + c[0] += len_0[icv] & 0xff; + c[1] += len_1[icv] & 0xff; + c[2] += len_2[icv] & 0xff; + c[3] += len_3[icv] & 0xff; + c[4] += len_4[icv] & 0xff; + c[5] += len_5[icv] & 0xff; + } + + cost[0] = (short) c[0]; + cost[1] = (short) c[1]; + cost[2] = (short) c[2]; + cost[3] = (short) c[3]; + cost[4] = (short) c[4]; + cost[5] = (short) c[5]; + } + else + { + for (int t = nGroups; --t >= 0;) + { + cost[t] = 0; + } + + for (int i = gs; i <= ge; i++) + { + int icv = sfmap[i]; + for (int t = nGroups; --t >= 0;) + { + cost[t] += (short) (len[t][icv] & 0xff); + } + } + } + + /* + * Find the coding table which is best for this group, and + * record its identity in the selector table. + */ + int bt = -1; + for (int t = nGroups, bc = 999999999; --t >= 0;) + { + int cost_t = cost[t]; + if (cost_t < bc) + { + bc = cost_t; + bt = t; + } + } + + fave[bt]++; + selector[nSelectors] = (byte) bt; + nSelectors++; + + /* + * Increment the symbol frequencies for the selected table. + */ + int[] rfreq_bt = rfreq[bt]; + for (int i = gs; i <= ge; i++) + { + rfreq_bt[sfmap[i]]++; + } + + gs = ge + 1; + } + + /* + * Recompute the tables based on the accumulated frequencies. + */ + for (int t = 0; t < nGroups; t++) + { + hbMakeCodeLengths(len[t], rfreq[t], this.cstate, alphaSize, 20); + } + } + + return nSelectors; + } + + private void sendMTFValues2(int nGroups, int nSelectors) + { + // assert (nGroups < 8) : nGroups; + + CompressionState dataShadow = this.cstate; + byte[] pos = dataShadow.sendMTFValues2_pos; + + for (int i = nGroups; --i >= 0;) + { + pos[i] = (byte) i; + } + + for (int i = 0; i < nSelectors; i++) + { + byte ll_i = dataShadow.selector[i]; + byte tmp = pos[0]; + int j = 0; + + while (ll_i != tmp) + { + j++; + byte tmp2 = tmp; + tmp = pos[j]; + pos[j] = tmp2; + } + + pos[0] = tmp; + dataShadow.selectorMtf[i] = (byte) j; + } + } + + private void sendMTFValues3(int nGroups, int alphaSize) + { + int[][] code = this.cstate.sendMTFValues_code; + byte[][] len = this.cstate.sendMTFValues_len; + + for (int t = 0; t < nGroups; t++) + { + int minLen = 32; + int maxLen = 0; + byte[] len_t = len[t]; + for (int i = alphaSize; --i >= 0;) + { + int l = len_t[i] & 0xff; + if (l > maxLen) + { + maxLen = l; + } + if (l < minLen) + { + minLen = l; + } + } + + // assert (maxLen <= 20) : maxLen; + // assert (minLen >= 1) : minLen; + + hbAssignCodes(code[t], len[t], minLen, maxLen, alphaSize); + } + } + + private void sendMTFValues4() + { + bool[] inUse = this.cstate.inUse; + bool[] inUse16 = this.cstate.sentMTFValues4_inUse16; + + for (int i = 16; --i >= 0;) + { + inUse16[i] = false; + int i16 = i * 16; + for (int j = 16; --j >= 0;) + { + if (inUse[i16 + j]) + { + inUse16[i] = true; + } + } + } + + uint u = 0; + for (int i = 0; i < 16; i++) + { + if (inUse16[i]) + u |= 1U << (16 - i - 1); + } + this.bw.WriteBits(16, u); + + + for (int i = 0; i < 16; i++) + { + if (inUse16[i]) + { + int i16 = i * 16; + u = 0; + for (int j = 0; j < 16; j++) + { + if (inUse[i16 + j]) + { + u |= 1U << (16 - j - 1); + } + } + this.bw.WriteBits(16, u); + } + } + } + + + private void sendMTFValues5(int nGroups, int nSelectors) + { + this.bw.WriteBits(3, (uint) nGroups); + this.bw.WriteBits(15, (uint) nSelectors); + + byte[] selectorMtf = this.cstate.selectorMtf; + + for (int i = 0; i < nSelectors; i++) + { + for (int j = 0, hj = selectorMtf[i] & 0xff; j < hj; j++) + { + this.bw.WriteBits(1, 1); + } + + this.bw.WriteBits(1, 0); + } + } + + private void sendMTFValues6(int nGroups, int alphaSize) + { + byte[][] len = this.cstate.sendMTFValues_len; + + for (int t = 0; t < nGroups; t++) + { + byte[] len_t = len[t]; + uint curr = (uint) (len_t[0] & 0xff); + this.bw.WriteBits(5, curr); + + for (int i = 0; i < alphaSize; i++) + { + int lti = len_t[i] & 0xff; + while (curr < lti) + { + this.bw.WriteBits(2, 2U); + curr++; /* 10 */ + } + + while (curr > lti) + { + this.bw.WriteBits(2, 3U); + curr--; /* 11 */ + } + + this.bw.WriteBits(1, 0U); + } + } + } + + + private void sendMTFValues7(int nSelectors) + { + byte[][] len = this.cstate.sendMTFValues_len; + int[][] code = this.cstate.sendMTFValues_code; + byte[] selector = this.cstate.selector; + char[] sfmap = this.cstate.sfmap; + int nMTFShadow = this.nMTF; + + int selCtr = 0; + + for (int gs = 0; gs < nMTFShadow;) + { + int ge = Math.Min(gs + BZip2.G_SIZE - 1, nMTFShadow - 1); + int ix = selector[selCtr] & 0xff; + int[] code_selCtr = code[ix]; + byte[] len_selCtr = len[ix]; + + while (gs <= ge) + { + int sfmap_i = sfmap[gs]; + int n = len_selCtr[sfmap_i] & 0xFF; + this.bw.WriteBits(n, (uint) code_selCtr[sfmap_i]); + gs++; + } + + gs = ge + 1; + selCtr++; + } + } + + private void moveToFrontCodeAndSend() + { + this.bw.WriteBits(24, (uint) this.origPtr); + generateMTFValues(); + sendMTFValues(); + } + + + + + + + private class CompressionState + { + // with blockSize 900k + public readonly bool[] inUse = new bool[256]; // 256 byte + public readonly byte[] unseqToSeq = new byte[256]; // 256 byte + public readonly int[] mtfFreq = new int[BZip2.MaxAlphaSize]; // 1032 byte + public readonly byte[] selector = new byte[BZip2.MaxSelectors]; // 18002 byte + public readonly byte[] selectorMtf = new byte[BZip2.MaxSelectors]; // 18002 byte + + public readonly byte[] generateMTFValues_yy = new byte[256]; // 256 byte + public byte[][] sendMTFValues_len; + + // byte + public int[][] sendMTFValues_rfreq; + + // byte + public readonly int[] sendMTFValues_fave = new int[BZip2.NGroups]; // 24 byte + public readonly short[] sendMTFValues_cost = new short[BZip2.NGroups]; // 12 byte + public int[][] sendMTFValues_code; + + // byte + public readonly byte[] sendMTFValues2_pos = new byte[BZip2.NGroups]; // 6 byte + public readonly bool[] sentMTFValues4_inUse16 = new bool[16]; // 16 byte + + public readonly int[] stack_ll = new int[BZip2.QSORT_STACK_SIZE]; // 4000 byte + public readonly int[] stack_hh = new int[BZip2.QSORT_STACK_SIZE]; // 4000 byte + public readonly int[] stack_dd = new int[BZip2.QSORT_STACK_SIZE]; // 4000 byte + + public readonly int[] mainSort_runningOrder = new int[256]; // 1024 byte + public readonly int[] mainSort_copy = new int[256]; // 1024 byte + public readonly bool[] mainSort_bigDone = new bool[256]; // 256 byte + + public int[] heap = new int[BZip2.MaxAlphaSize + 2]; // 1040 byte + public int[] weight = new int[BZip2.MaxAlphaSize * 2]; // 2064 byte + public int[] parent = new int[BZip2.MaxAlphaSize * 2]; // 2064 byte + + public readonly int[] ftab = new int[65537]; // 262148 byte + // ------------ + // 333408 byte + + public byte[] block; // 900021 byte + public int[] fmap; // 3600000 byte + public char[] sfmap; // 3600000 byte + + // ------------ + // 8433529 byte + // ============ + + /** + * Array instance identical to sfmap, both are used only + * temporarily and independently, so we do not need to allocate + * additional memory. + */ + public char[] quadrant; + + public CompressionState(int blockSize100k) + { + int n = blockSize100k * BZip2.BlockSizeMultiple; + this.block = new byte[(n + 1 + BZip2.NUM_OVERSHOOT_BYTES)]; + this.fmap = new int[n]; + this.sfmap = new char[2 * n]; + this.quadrant = this.sfmap; + this.sendMTFValues_len = BZip2.InitRectangularArray(BZip2.NGroups,BZip2.MaxAlphaSize); + this.sendMTFValues_rfreq = BZip2.InitRectangularArray(BZip2.NGroups,BZip2.MaxAlphaSize); + this.sendMTFValues_code = BZip2.InitRectangularArray(BZip2.NGroups,BZip2.MaxAlphaSize); + } + + } + + + + } +} \ No newline at end of file diff --git a/DotNetZip/BZip2/BZip2InputStream.cs b/DotNetZip/BZip2/BZip2InputStream.cs new file mode 100644 index 0000000..1c152d5 --- /dev/null +++ b/DotNetZip/BZip2/BZip2InputStream.cs @@ -0,0 +1,1447 @@ +// BZip2InputStream.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2011 Dino Chiesa. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// Last Saved: <2011-July-31 11:57:32> +// +// ------------------------------------------------------------------ +// +// This module defines the BZip2InputStream class, which is a decompressing +// stream that handles BZIP2. This code is derived from Apache commons source code. +// The license below applies to the original Apache code. +// +// ------------------------------------------------------------------ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* + * This package is based on the work done by Keiron Liddle, Aftex Software + * to whom the Ant project is very grateful for his + * great code. + */ + +// compile: msbuild +// not: csc.exe /t:library /debug+ /out:Ionic.BZip2.dll BZip2InputStream.cs BCRC32.cs Rand.cs + + + +using System; +using System.IO; + +namespace Ionic.BZip2 +{ + + /// + /// A read-only decorator stream that performs BZip2 decompression on Read. + /// + public class BZip2InputStream : System.IO.Stream + { + bool _disposed; + bool _leaveOpen; + Int64 totalBytesRead; + private int last; + + /* for undoing the Burrows-Wheeler transform */ + private int origPtr; + + // blockSize100k: 0 .. 9. + // + // This var name is a misnomer. The actual block size is 100000 + // * blockSize100k. (not 100k * blocksize100k) + private int blockSize100k; + private bool blockRandomised; + private int bsBuff; + private int bsLive; + private readonly Ionic.Crc.CRC32 crc = new Ionic.Crc.CRC32(true); + private int nInUse; + private Stream input; + private int currentChar = -1; + + /// + /// Compressor State + /// + enum CState + { + EOF = 0, + START_BLOCK = 1, + RAND_PART_A = 2, + RAND_PART_B = 3, + RAND_PART_C = 4, + NO_RAND_PART_A = 5, + NO_RAND_PART_B = 6, + NO_RAND_PART_C = 7, + } + + private CState currentState = CState.START_BLOCK; + + private uint storedBlockCRC, storedCombinedCRC; + private uint computedBlockCRC, computedCombinedCRC; + + // Variables used by setup* methods exclusively + private int su_count; + private int su_ch2; + private int su_chPrev; + private int su_i2; + private int su_j2; + private int su_rNToGo; + private int su_rTPos; + private int su_tPos; + private char su_z; + private BZip2InputStream.DecompressionState data; + + + /// + /// Create a BZip2InputStream, wrapping it around the given input Stream. + /// + /// + /// + /// The input stream will be closed when the BZip2InputStream is closed. + /// + /// + /// The stream from which to read compressed data + public BZip2InputStream(Stream input) + : this(input, false) + {} + + + /// + /// Create a BZip2InputStream with the given stream, and + /// specifying whether to leave the wrapped stream open when + /// the BZip2InputStream is closed. + /// + /// The stream from which to read compressed data + /// + /// Whether to leave the input stream open, when the BZip2InputStream closes. + /// + /// + /// + /// + /// This example reads a bzip2-compressed file, decompresses it, + /// and writes the decompressed data into a newly created file. + /// + /// + /// var fname = "logfile.log.bz2"; + /// using (var fs = File.OpenRead(fname)) + /// { + /// using (var decompressor = new Ionic.BZip2.BZip2InputStream(fs)) + /// { + /// var outFname = fname + ".decompressed"; + /// using (var output = File.Create(outFname)) + /// { + /// byte[] buffer = new byte[2048]; + /// int n; + /// while ((n = decompressor.Read(buffer, 0, buffer.Length)) > 0) + /// { + /// output.Write(buffer, 0, n); + /// } + /// } + /// } + /// } + /// + /// + public BZip2InputStream(Stream input, bool leaveOpen) + : base() + { + + this.input = input; + this._leaveOpen = leaveOpen; + init(); + } + + /// + /// Read data from the stream. + /// + /// + /// + /// + /// To decompress a BZip2 data stream, create a BZip2InputStream, + /// providing a stream that reads compressed data. Then call Read() on + /// that BZip2InputStream, and the data read will be decompressed + /// as you read. + /// + /// + /// + /// A BZip2InputStream can be used only for Read(), not for Write(). + /// + /// + /// + /// The buffer into which the read data should be placed. + /// the offset within that data array to put the first byte read. + /// the number of bytes to read. + /// the number of bytes actually read + public override int Read(byte[] buffer, int offset, int count) + { + if (offset < 0) + throw new IndexOutOfRangeException(String.Format("offset ({0}) must be > 0", offset)); + + if (count < 0) + throw new IndexOutOfRangeException(String.Format("count ({0}) must be > 0", count)); + + if (offset + count > buffer.Length) + throw new IndexOutOfRangeException(String.Format("offset({0}) count({1}) bLength({2})", + offset, count, buffer.Length)); + + if (this.input == null) + throw new IOException("the stream is not open"); + + + int hi = offset + count; + int destOffset = offset; + for (int b; (destOffset < hi) && ((b = ReadByte()) >= 0);) + { + buffer[destOffset++] = (byte) b; + } + + return (destOffset == offset) ? -1 : (destOffset - offset); + } + + private void MakeMaps() + { + bool[] inUse = this.data.inUse; + byte[] seqToUnseq = this.data.seqToUnseq; + + int n = 0; + + for (int i = 0; i < 256; i++) + { + if (inUse[i]) + seqToUnseq[n++] = (byte) i; + } + + this.nInUse = n; + } + + /// + /// Read a single byte from the stream. + /// + /// the byte read from the stream, or -1 if EOF + public override int ReadByte() + { + int retChar = this.currentChar; + totalBytesRead++; + switch (this.currentState) + { + case CState.EOF: + return -1; + + case CState.START_BLOCK: + throw new IOException("bad state"); + + case CState.RAND_PART_A: + throw new IOException("bad state"); + + case CState.RAND_PART_B: + SetupRandPartB(); + break; + + case CState.RAND_PART_C: + SetupRandPartC(); + break; + + case CState.NO_RAND_PART_A: + throw new IOException("bad state"); + + case CState.NO_RAND_PART_B: + SetupNoRandPartB(); + break; + + case CState.NO_RAND_PART_C: + SetupNoRandPartC(); + break; + + default: + throw new IOException("bad state"); + } + + return retChar; + } + + + + + /// + /// Indicates whether the stream can be read. + /// + /// + /// The return value depends on whether the captive stream supports reading. + /// + public override bool CanRead + { + get + { + if (_disposed) throw new ObjectDisposedException("BZip2Stream"); + return this.input.CanRead; + } + } + + + /// + /// Indicates whether the stream supports Seek operations. + /// + /// + /// Always returns false. + /// + public override bool CanSeek + { + get { return false; } + } + + + /// + /// Indicates whether the stream can be written. + /// + /// + /// The return value depends on whether the captive stream supports writing. + /// + public override bool CanWrite + { + get + { + if (_disposed) throw new ObjectDisposedException("BZip2Stream"); + return input.CanWrite; + } + } + + /// + /// Flush the stream. + /// + public override void Flush() + { + if (_disposed) throw new ObjectDisposedException("BZip2Stream"); + input.Flush(); + } + + /// + /// Reading this property always throws a . + /// + public override long Length + { + get { throw new NotImplementedException(); } + } + + /// + /// The position of the stream pointer. + /// + /// + /// + /// Setting this property always throws a . Reading will return the + /// total number of uncompressed bytes read in. + /// + public override long Position + { + get + { + return this.totalBytesRead; + } + set { throw new NotImplementedException(); } + } + + /// + /// Calling this method always throws a . + /// + /// this is irrelevant, since it will always throw! + /// this is irrelevant, since it will always throw! + /// irrelevant! + public override long Seek(long offset, System.IO.SeekOrigin origin) + { + throw new NotImplementedException(); + } + + /// + /// Calling this method always throws a . + /// + /// this is irrelevant, since it will always throw! + public override void SetLength(long value) + { + throw new NotImplementedException(); + } + + /// + /// Calling this method always throws a . + /// + /// this parameter is never used + /// this parameter is never used + /// this parameter is never used + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotImplementedException(); + } + + + /// + /// Dispose the stream. + /// + /// + /// indicates whether the Dispose method was invoked by user code. + /// + protected override void Dispose(bool disposing) + { + try + { + if (!_disposed) + { + if (disposing && (this.input != null)) + this.input.Close(); + _disposed = true; + } + } + finally + { + base.Dispose(disposing); + } + } + + + void init() + { + if (null == this.input) + throw new IOException("No input Stream"); + + if (!this.input.CanRead) + throw new IOException("Unreadable input Stream"); + + CheckMagicChar('B', 0); + CheckMagicChar('Z', 1); + CheckMagicChar('h', 2); + + int blockSize = this.input.ReadByte(); + + if ((blockSize < '1') || (blockSize > '9')) + throw new IOException("Stream is not BZip2 formatted: illegal " + + "blocksize " + (char) blockSize); + + this.blockSize100k = blockSize - '0'; + + InitBlock(); + SetupBlock(); + } + + + void CheckMagicChar(char expected, int position) + { + int magic = this.input.ReadByte(); + if (magic != (int)expected) + { + var msg = String.Format("Not a valid BZip2 stream. byte {0}, expected '{1}', got '{2}'", + position, (int)expected, magic); + throw new IOException(msg); + } + } + + + void InitBlock() + { + char magic0 = bsGetUByte(); + char magic1 = bsGetUByte(); + char magic2 = bsGetUByte(); + char magic3 = bsGetUByte(); + char magic4 = bsGetUByte(); + char magic5 = bsGetUByte(); + + if (magic0 == 0x17 && magic1 == 0x72 && magic2 == 0x45 + && magic3 == 0x38 && magic4 == 0x50 && magic5 == 0x90) + { + complete(); // end of file + } + else if (magic0 != 0x31 || + magic1 != 0x41 || + magic2 != 0x59 || + magic3 != 0x26 || + magic4 != 0x53 || + magic5 != 0x59) + { + this.currentState = CState.EOF; + var msg = String.Format("bad block header at offset 0x{0:X}", + this.input.Position); + throw new IOException(msg); + } + else + { + this.storedBlockCRC = bsGetInt(); + // Console.WriteLine(" stored block CRC : {0:X8}", this.storedBlockCRC); + + this.blockRandomised = (GetBits(1) == 1); + + // Lazily allocate data + if (this.data == null) + this.data = new DecompressionState(this.blockSize100k); + + // currBlockNo++; + getAndMoveToFrontDecode(); + + this.crc.Reset(); + this.currentState = CState.START_BLOCK; + } + } + + + private void EndBlock() + { + this.computedBlockCRC = (uint)this.crc.Crc32Result; + + // A bad CRC is considered a fatal error. + if (this.storedBlockCRC != this.computedBlockCRC) + { + // make next blocks readable without error + // (repair feature, not yet documented, not tested) + // this.computedCombinedCRC = (this.storedCombinedCRC << 1) + // | (this.storedCombinedCRC >> 31); + // this.computedCombinedCRC ^= this.storedBlockCRC; + + var msg = String.Format("BZip2 CRC error (expected {0:X8}, computed {1:X8})", + this.storedBlockCRC, this.computedBlockCRC); + throw new IOException(msg); + } + + // Console.WriteLine(" combined CRC (before): {0:X8}", this.computedCombinedCRC); + this.computedCombinedCRC = (this.computedCombinedCRC << 1) + | (this.computedCombinedCRC >> 31); + this.computedCombinedCRC ^= this.computedBlockCRC; + // Console.WriteLine(" computed block CRC : {0:X8}", this.computedBlockCRC); + // Console.WriteLine(" combined CRC (after) : {0:X8}", this.computedCombinedCRC); + // Console.WriteLine(); + } + + + private void complete() + { + this.storedCombinedCRC = bsGetInt(); + this.currentState = CState.EOF; + this.data = null; + + if (this.storedCombinedCRC != this.computedCombinedCRC) + { + var msg = String.Format("BZip2 CRC error (expected {0:X8}, computed {1:X8})", + this.storedCombinedCRC, this.computedCombinedCRC); + + throw new IOException(msg); + } + } + + /// + /// Close the stream. + /// + public override void Close() + { + Stream inShadow = this.input; + if (inShadow != null) + { + try + { + if (!this._leaveOpen) + inShadow.Close(); + } + finally + { + this.data = null; + this.input = null; + } + } + } + + + /// + /// Read n bits from input, right justifying the result. + /// + /// + /// + /// For example, if you read 1 bit, the result is either 0 + /// or 1. + /// + /// + /// + /// The number of bits to read, always between 1 and 32. + /// + private int GetBits(int n) + { + int bsLiveShadow = this.bsLive; + int bsBuffShadow = this.bsBuff; + + if (bsLiveShadow < n) + { + do + { + int thech = this.input.ReadByte(); + + if (thech < 0) + throw new IOException("unexpected end of stream"); + + // Console.WriteLine("R {0:X2}", thech); + + bsBuffShadow = (bsBuffShadow << 8) | thech; + bsLiveShadow += 8; + } while (bsLiveShadow < n); + + this.bsBuff = bsBuffShadow; + } + + this.bsLive = bsLiveShadow - n; + return (bsBuffShadow >> (bsLiveShadow - n)) & ((1 << n) - 1); + } + + + // private bool bsGetBit() + // { + // int bsLiveShadow = this.bsLive; + // int bsBuffShadow = this.bsBuff; + // + // if (bsLiveShadow < 1) + // { + // int thech = this.input.ReadByte(); + // + // if (thech < 0) + // { + // throw new IOException("unexpected end of stream"); + // } + // + // bsBuffShadow = (bsBuffShadow << 8) | thech; + // bsLiveShadow += 8; + // this.bsBuff = bsBuffShadow; + // } + // + // this.bsLive = bsLiveShadow - 1; + // return ((bsBuffShadow >> (bsLiveShadow - 1)) & 1) != 0; + // } + + private bool bsGetBit() + { + int bit = GetBits(1); + return bit != 0; + } + + private char bsGetUByte() + { + return (char) GetBits(8); + } + + private uint bsGetInt() + { + return (uint)((((((GetBits(8) << 8) | GetBits(8)) << 8) | GetBits(8)) << 8) | GetBits(8)); + } + + + /** + * Called by createHuffmanDecodingTables() exclusively. + */ + private static void hbCreateDecodeTables(int[] limit, + int[] bbase, int[] perm, char[] length, + int minLen, int maxLen, int alphaSize) + { + for (int i = minLen, pp = 0; i <= maxLen; i++) + { + for (int j = 0; j < alphaSize; j++) + { + if (length[j] == i) + { + perm[pp++] = j; + } + } + } + + for (int i = BZip2.MaxCodeLength; --i > 0;) + { + bbase[i] = 0; + limit[i] = 0; + } + + for (int i = 0; i < alphaSize; i++) + { + bbase[length[i] + 1]++; + } + + for (int i = 1, b = bbase[0]; i < BZip2.MaxCodeLength; i++) + { + b += bbase[i]; + bbase[i] = b; + } + + for (int i = minLen, vec = 0, b = bbase[i]; i <= maxLen; i++) + { + int nb = bbase[i + 1]; + vec += nb - b; + b = nb; + limit[i] = vec - 1; + vec <<= 1; + } + + for (int i = minLen + 1; i <= maxLen; i++) + { + bbase[i] = ((limit[i - 1] + 1) << 1) - bbase[i]; + } + } + + + + private void recvDecodingTables() + { + var s = this.data; + bool[] inUse = s.inUse; + byte[] pos = s.recvDecodingTables_pos; + //byte[] selector = s.selector; + + int inUse16 = 0; + + /* Receive the mapping table */ + for (int i = 0; i < 16; i++) + { + if (bsGetBit()) + { + inUse16 |= 1 << i; + } + } + + for (int i = 256; --i >= 0;) + { + inUse[i] = false; + } + + for (int i = 0; i < 16; i++) + { + if ((inUse16 & (1 << i)) != 0) + { + int i16 = i << 4; + for (int j = 0; j < 16; j++) + { + if (bsGetBit()) + { + inUse[i16 + j] = true; + } + } + } + } + + MakeMaps(); + int alphaSize = this.nInUse + 2; + + /* Now the selectors */ + int nGroups = GetBits(3); + int nSelectors = GetBits(15); + + for (int i = 0; i < nSelectors; i++) + { + int j = 0; + while (bsGetBit()) + { + j++; + } + s.selectorMtf[i] = (byte) j; + } + + /* Undo the MTF values for the selectors. */ + for (int v = nGroups; --v >= 0;) + { + pos[v] = (byte) v; + } + + for (int i = 0; i < nSelectors; i++) + { + int v = s.selectorMtf[i]; + byte tmp = pos[v]; + while (v > 0) + { + // nearly all times v is zero, 4 in most other cases + pos[v] = pos[v - 1]; + v--; + } + pos[0] = tmp; + s.selector[i] = tmp; + } + + char[][] len = s.temp_charArray2d; + + /* Now the coding tables */ + for (int t = 0; t < nGroups; t++) + { + int curr = GetBits(5); + char[] len_t = len[t]; + for (int i = 0; i < alphaSize; i++) + { + while (bsGetBit()) + { + curr += bsGetBit() ? -1 : 1; + } + len_t[i] = (char) curr; + } + } + + // finally create the Huffman tables + createHuffmanDecodingTables(alphaSize, nGroups); + } + + + /** + * Called by recvDecodingTables() exclusively. + */ + private void createHuffmanDecodingTables(int alphaSize, + int nGroups) + { + var s = this.data; + char[][] len = s.temp_charArray2d; + + for (int t = 0; t < nGroups; t++) + { + int minLen = 32; + int maxLen = 0; + char[] len_t = len[t]; + for (int i = alphaSize; --i >= 0;) + { + char lent = len_t[i]; + if (lent > maxLen) + maxLen = lent; + + if (lent < minLen) + minLen = lent; + } + hbCreateDecodeTables(s.gLimit[t], s.gBase[t], s.gPerm[t], len[t], minLen, + maxLen, alphaSize); + s.gMinlen[t] = minLen; + } + } + + + + private void getAndMoveToFrontDecode() + { + var s = this.data; + this.origPtr = GetBits(24); + + if (this.origPtr < 0) + throw new IOException("BZ_DATA_ERROR"); + if (this.origPtr > 10 + BZip2.BlockSizeMultiple * this.blockSize100k) + throw new IOException("BZ_DATA_ERROR"); + + recvDecodingTables(); + + byte[] yy = s.getAndMoveToFrontDecode_yy; + int limitLast = this.blockSize100k * BZip2.BlockSizeMultiple; + + /* + * Setting up the unzftab entries here is not strictly necessary, but it + * does save having to do it later in a separate pass, and so saves a + * block's worth of cache misses. + */ + for (int i = 256; --i >= 0;) + { + yy[i] = (byte) i; + s.unzftab[i] = 0; + } + + int groupNo = 0; + int groupPos = BZip2.G_SIZE - 1; + int eob = this.nInUse + 1; + int nextSym = getAndMoveToFrontDecode0(0); + int bsBuffShadow = this.bsBuff; + int bsLiveShadow = this.bsLive; + int lastShadow = -1; + int zt = s.selector[groupNo] & 0xff; + int[] base_zt = s.gBase[zt]; + int[] limit_zt = s.gLimit[zt]; + int[] perm_zt = s.gPerm[zt]; + int minLens_zt = s.gMinlen[zt]; + + while (nextSym != eob) + { + if ((nextSym == BZip2.RUNA) || (nextSym == BZip2.RUNB)) + { + int es = -1; + + for (int n = 1; true; n <<= 1) + { + if (nextSym == BZip2.RUNA) + { + es += n; + } + else if (nextSym == BZip2.RUNB) + { + es += n << 1; + } + else + { + break; + } + + if (groupPos == 0) + { + groupPos = BZip2.G_SIZE - 1; + zt = s.selector[++groupNo] & 0xff; + base_zt = s.gBase[zt]; + limit_zt = s.gLimit[zt]; + perm_zt = s.gPerm[zt]; + minLens_zt = s.gMinlen[zt]; + } + else + { + groupPos--; + } + + int zn = minLens_zt; + + // Inlined: + // int zvec = GetBits(zn); + while (bsLiveShadow < zn) + { + int thech = this.input.ReadByte(); + if (thech >= 0) + { + bsBuffShadow = (bsBuffShadow << 8) | thech; + bsLiveShadow += 8; + continue; + } + else + { + throw new IOException("unexpected end of stream"); + } + } + int zvec = (bsBuffShadow >> (bsLiveShadow - zn)) + & ((1 << zn) - 1); + bsLiveShadow -= zn; + + while (zvec > limit_zt[zn]) + { + zn++; + while (bsLiveShadow < 1) + { + int thech = this.input.ReadByte(); + if (thech >= 0) + { + bsBuffShadow = (bsBuffShadow << 8) | thech; + bsLiveShadow += 8; + continue; + } + else + { + throw new IOException("unexpected end of stream"); + } + } + bsLiveShadow--; + zvec = (zvec << 1) + | ((bsBuffShadow >> bsLiveShadow) & 1); + } + nextSym = perm_zt[zvec - base_zt[zn]]; + } + + byte ch = s.seqToUnseq[yy[0]]; + s.unzftab[ch & 0xff] += es + 1; + + while (es-- >= 0) + { + s.ll8[++lastShadow] = ch; + } + + if (lastShadow >= limitLast) + throw new IOException("block overrun"); + } + else + { + if (++lastShadow >= limitLast) + throw new IOException("block overrun"); + + byte tmp = yy[nextSym - 1]; + s.unzftab[s.seqToUnseq[tmp] & 0xff]++; + s.ll8[lastShadow] = s.seqToUnseq[tmp]; + + /* + * This loop is hammered during decompression, hence avoid + * native method call overhead of System.Buffer.BlockCopy for very + * small ranges to copy. + */ + if (nextSym <= 16) + { + for (int j = nextSym - 1; j > 0;) + { + yy[j] = yy[--j]; + } + } + else + { + System.Buffer.BlockCopy(yy, 0, yy, 1, nextSym - 1); + } + + yy[0] = tmp; + + if (groupPos == 0) + { + groupPos = BZip2.G_SIZE - 1; + zt = s.selector[++groupNo] & 0xff; + base_zt = s.gBase[zt]; + limit_zt = s.gLimit[zt]; + perm_zt = s.gPerm[zt]; + minLens_zt = s.gMinlen[zt]; + } + else + { + groupPos--; + } + + int zn = minLens_zt; + + // Inlined: + // int zvec = GetBits(zn); + while (bsLiveShadow < zn) + { + int thech = this.input.ReadByte(); + if (thech >= 0) + { + bsBuffShadow = (bsBuffShadow << 8) | thech; + bsLiveShadow += 8; + continue; + } + else + { + throw new IOException("unexpected end of stream"); + } + } + int zvec = (bsBuffShadow >> (bsLiveShadow - zn)) + & ((1 << zn) - 1); + bsLiveShadow -= zn; + + while (zvec > limit_zt[zn]) + { + zn++; + while (bsLiveShadow < 1) + { + int thech = this.input.ReadByte(); + if (thech >= 0) + { + bsBuffShadow = (bsBuffShadow << 8) | thech; + bsLiveShadow += 8; + continue; + } + else + { + throw new IOException("unexpected end of stream"); + } + } + bsLiveShadow--; + zvec = (zvec << 1) | ((bsBuffShadow >> bsLiveShadow) & 1); + } + nextSym = perm_zt[zvec - base_zt[zn]]; + } + } + + this.last = lastShadow; + this.bsLive = bsLiveShadow; + this.bsBuff = bsBuffShadow; + } + + + private int getAndMoveToFrontDecode0(int groupNo) + { + var s = this.data; + int zt = s.selector[groupNo] & 0xff; + int[] limit_zt = s.gLimit[zt]; + int zn = s.gMinlen[zt]; + int zvec = GetBits(zn); + int bsLiveShadow = this.bsLive; + int bsBuffShadow = this.bsBuff; + + while (zvec > limit_zt[zn]) + { + zn++; + while (bsLiveShadow < 1) + { + int thech = this.input.ReadByte(); + + if (thech >= 0) + { + bsBuffShadow = (bsBuffShadow << 8) | thech; + bsLiveShadow += 8; + continue; + } + else + { + throw new IOException("unexpected end of stream"); + } + } + bsLiveShadow--; + zvec = (zvec << 1) | ((bsBuffShadow >> bsLiveShadow) & 1); + } + + this.bsLive = bsLiveShadow; + this.bsBuff = bsBuffShadow; + + return s.gPerm[zt][zvec - s.gBase[zt][zn]]; + } + + + private void SetupBlock() + { + if (this.data == null) + return; + + int i; + var s = this.data; + int[] tt = s.initTT(this.last + 1); + + // xxxx + + /* Check: unzftab entries in range. */ + for (i = 0; i <= 255; i++) + { + if (s.unzftab[i] < 0 || s.unzftab[i] > this.last) + throw new Exception("BZ_DATA_ERROR"); + } + + /* Actually generate cftab. */ + s.cftab[0] = 0; + for (i = 1; i <= 256; i++) s.cftab[i] = s.unzftab[i-1]; + for (i = 1; i <= 256; i++) s.cftab[i] += s.cftab[i-1]; + /* Check: cftab entries in range. */ + for (i = 0; i <= 256; i++) + { + if (s.cftab[i] < 0 || s.cftab[i] > this.last+1) + { + var msg = String.Format("BZ_DATA_ERROR: cftab[{0}]={1} last={2}", + i, s.cftab[i], this.last); + throw new Exception(msg); + } + } + /* Check: cftab entries non-descending. */ + for (i = 1; i <= 256; i++) + { + if (s.cftab[i-1] > s.cftab[i]) + throw new Exception("BZ_DATA_ERROR"); + } + + int lastShadow; + for (i = 0, lastShadow = this.last; i <= lastShadow; i++) + { + tt[s.cftab[s.ll8[i] & 0xff]++] = i; + } + + if ((this.origPtr < 0) || (this.origPtr >= tt.Length)) + throw new IOException("stream corrupted"); + + this.su_tPos = tt[this.origPtr]; + this.su_count = 0; + this.su_i2 = 0; + this.su_ch2 = 256; /* not a valid 8-bit byte value?, and not EOF */ + + if (this.blockRandomised) + { + this.su_rNToGo = 0; + this.su_rTPos = 0; + SetupRandPartA(); + } + else + { + SetupNoRandPartA(); + } + } + + + + private void SetupRandPartA() + { + if (this.su_i2 <= this.last) + { + this.su_chPrev = this.su_ch2; + int su_ch2Shadow = this.data.ll8[this.su_tPos] & 0xff; + this.su_tPos = this.data.tt[this.su_tPos]; + if (this.su_rNToGo == 0) + { + this.su_rNToGo = Rand.Rnums(this.su_rTPos) - 1; + if (++this.su_rTPos == 512) + { + this.su_rTPos = 0; + } + } + else + { + this.su_rNToGo--; + } + this.su_ch2 = su_ch2Shadow ^= (this.su_rNToGo == 1) ? 1 : 0; + this.su_i2++; + this.currentChar = su_ch2Shadow; + this.currentState = CState.RAND_PART_B; + this.crc.UpdateCRC((byte)su_ch2Shadow); + } + else + { + EndBlock(); + InitBlock(); + SetupBlock(); + } + } + + private void SetupNoRandPartA() + { + if (this.su_i2 <= this.last) + { + this.su_chPrev = this.su_ch2; + int su_ch2Shadow = this.data.ll8[this.su_tPos] & 0xff; + this.su_ch2 = su_ch2Shadow; + this.su_tPos = this.data.tt[this.su_tPos]; + this.su_i2++; + this.currentChar = su_ch2Shadow; + this.currentState = CState.NO_RAND_PART_B; + this.crc.UpdateCRC((byte)su_ch2Shadow); + } + else + { + this.currentState = CState.NO_RAND_PART_A; + EndBlock(); + InitBlock(); + SetupBlock(); + } + } + + private void SetupRandPartB() + { + if (this.su_ch2 != this.su_chPrev) + { + this.currentState = CState.RAND_PART_A; + this.su_count = 1; + SetupRandPartA(); + } + else if (++this.su_count >= 4) + { + this.su_z = (char) (this.data.ll8[this.su_tPos] & 0xff); + this.su_tPos = this.data.tt[this.su_tPos]; + if (this.su_rNToGo == 0) + { + this.su_rNToGo = Rand.Rnums(this.su_rTPos) - 1; + if (++this.su_rTPos == 512) + { + this.su_rTPos = 0; + } + } + else + { + this.su_rNToGo--; + } + this.su_j2 = 0; + this.currentState = CState.RAND_PART_C; + if (this.su_rNToGo == 1) + { + this.su_z ^= (char)1; + } + SetupRandPartC(); + } + else + { + this.currentState = CState.RAND_PART_A; + SetupRandPartA(); + } + } + + private void SetupRandPartC() + { + if (this.su_j2 < this.su_z) + { + this.currentChar = this.su_ch2; + this.crc.UpdateCRC((byte)this.su_ch2); + this.su_j2++; + } + else + { + this.currentState = CState.RAND_PART_A; + this.su_i2++; + this.su_count = 0; + SetupRandPartA(); + } + } + + private void SetupNoRandPartB() + { + if (this.su_ch2 != this.su_chPrev) + { + this.su_count = 1; + SetupNoRandPartA(); + } + else if (++this.su_count >= 4) + { + this.su_z = (char) (this.data.ll8[this.su_tPos] & 0xff); + this.su_tPos = this.data.tt[this.su_tPos]; + this.su_j2 = 0; + SetupNoRandPartC(); + } + else + { + SetupNoRandPartA(); + } + } + + private void SetupNoRandPartC() + { + if (this.su_j2 < this.su_z) + { + int su_ch2Shadow = this.su_ch2; + this.currentChar = su_ch2Shadow; + this.crc.UpdateCRC((byte)su_ch2Shadow); + this.su_j2++; + this.currentState = CState.NO_RAND_PART_C; + } + else + { + this.su_i2++; + this.su_count = 0; + SetupNoRandPartA(); + } + } + + private sealed class DecompressionState + { + // (with blockSize 900k) + readonly public bool[] inUse = new bool[256]; + readonly public byte[] seqToUnseq = new byte[256]; // 256 byte + readonly public byte[] selector = new byte[BZip2.MaxSelectors]; // 18002 byte + readonly public byte[] selectorMtf = new byte[BZip2.MaxSelectors]; // 18002 byte + + /** + * Freq table collected to save a pass over the data during + * decompression. + */ + public readonly int[] unzftab; + public readonly int[][] gLimit; + public readonly int[][] gBase; + public readonly int[][] gPerm; + public readonly int[] gMinlen; + + public readonly int[] cftab; + public readonly byte[] getAndMoveToFrontDecode_yy; + public readonly char[][] temp_charArray2d; + public readonly byte[] recvDecodingTables_pos; + // --------------- + // 60798 byte + + public int[] tt; // 3600000 byte + public byte[] ll8; // 900000 byte + + // --------------- + // 4560782 byte + // =============== + + public DecompressionState(int blockSize100k) + { + this.unzftab = new int[256]; // 1024 byte + + this.gLimit = BZip2.InitRectangularArray(BZip2.NGroups,BZip2.MaxAlphaSize); + this.gBase = BZip2.InitRectangularArray(BZip2.NGroups,BZip2.MaxAlphaSize); + this.gPerm = BZip2.InitRectangularArray(BZip2.NGroups,BZip2.MaxAlphaSize); + this.gMinlen = new int[BZip2.NGroups]; // 24 byte + + this.cftab = new int[257]; // 1028 byte + this.getAndMoveToFrontDecode_yy = new byte[256]; // 512 byte + this.temp_charArray2d = BZip2.InitRectangularArray(BZip2.NGroups,BZip2.MaxAlphaSize); + this.recvDecodingTables_pos = new byte[BZip2.NGroups]; // 6 byte + + this.ll8 = new byte[blockSize100k * BZip2.BlockSizeMultiple]; + } + + /** + * Initializes the tt array. + * + * This method is called when the required length of the array is known. + * I don't initialize it at construction time to avoid unneccessary + * memory allocation when compressing small files. + */ + public int[] initTT(int length) + { + int[] ttShadow = this.tt; + + // tt.length should always be >= length, but theoretically + // it can happen, if the compressor mixed small and large + // blocks. Normally only the last block will be smaller + // than others. + if ((ttShadow == null) || (ttShadow.Length < length)) + { + this.tt = ttShadow = new int[length]; + } + + return ttShadow; + } + } + + + } + + // /** + // * Checks if the signature matches what is expected for a bzip2 file. + // * + // * @param signature + // * the bytes to check + // * @param length + // * the number of bytes to check + // * @return true, if this stream is a bzip2 compressed stream, false otherwise + // * + // * @since Apache Commons Compress 1.1 + // */ + // public static boolean MatchesSig(byte[] signature) + // { + // if ((signature.Length < 3) || + // (signature[0] != 'B') || + // (signature[1] != 'Z') || + // (signature[2] != 'h')) + // return false; + // + // return true; + // } + + + internal static class BZip2 + { + internal static T[][] InitRectangularArray(int d1, int d2) + { + var x = new T[d1][]; + for (int i=0; i < d1; i++) + { + x[i] = new T[d2]; + } + return x; + } + + public static readonly int BlockSizeMultiple = 100000; + public static readonly int MinBlockSize = 1; + public static readonly int MaxBlockSize = 9; + public static readonly int MaxAlphaSize = 258; + public static readonly int MaxCodeLength = 23; + public static readonly char RUNA = (char) 0; + public static readonly char RUNB = (char) 1; + public static readonly int NGroups = 6; + public static readonly int G_SIZE = 50; + public static readonly int N_ITERS = 4; + public static readonly int MaxSelectors = (2 + (900000 / G_SIZE)); + public static readonly int NUM_OVERSHOOT_BYTES = 20; + /* + *

If you are ever unlucky/improbable enough to get a stack + * overflow whilst sorting, increase the following constant and + * try again. In practice I have never seen the stack go above 27 + * elems, so the following limit seems very generous.

+ */ + internal static readonly int QSORT_STACK_SIZE = 1000; + + + } + +} \ No newline at end of file diff --git a/DotNetZip/BZip2/BZip2OutputStream.cs b/DotNetZip/BZip2/BZip2OutputStream.cs new file mode 100644 index 0000000..74dce3c --- /dev/null +++ b/DotNetZip/BZip2/BZip2OutputStream.cs @@ -0,0 +1,530 @@ +//#define Trace + +// BZip2OutputStream.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2011 Dino Chiesa. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// Last Saved: <2011-August-02 16:44:11> +// +// ------------------------------------------------------------------ +// +// This module defines the BZip2OutputStream class, which is a +// compressing stream that handles BZIP2. This code may have been +// derived in part from Apache commons source code. The license below +// applies to the original Apache code. +// +// ------------------------------------------------------------------ +// flymake: csc.exe /t:module BZip2InputStream.cs BZip2Compressor.cs Rand.cs BCRC32.cs @@FILE@@ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +// Design Notes: +// +// This class follows the classic Decorator pattern: it is a Stream that +// wraps itself around a Stream, and in doing so provides bzip2 +// compression as callers Write into it. +// +// BZip2 is a straightforward data format: there are 4 magic bytes at +// the top of the file, followed by 1 or more compressed blocks. There +// is a small "magic byte" trailer after all compressed blocks. This +// class emits the magic bytes for the header and trailer, and relies on +// a BZip2Compressor to generate each of the compressed data blocks. +// +// BZip2 does byte-shredding - it uses partial fractions of bytes to +// represent independent pieces of information. This class relies on the +// BitWriter to adapt the bit-oriented BZip2 output to the byte-oriented +// model of the .NET Stream class. +// +// ---- +// +// Regarding the Apache code base: Most of the code in this particular +// class is related to stream operations, and is my own code. It largely +// does not rely on any code obtained from Apache commons. If you +// compare this code with the Apache commons BZip2OutputStream, you will +// see very little code that is common, except for the +// nearly-boilerplate structure that is common to all subtypes of +// System.IO.Stream. There may be some small remnants of code in this +// module derived from the Apache stuff, which is why I left the license +// in here. Most of the Apache commons compressor magic has been ported +// into the BZip2Compressor class. +// + +using System; +using System.IO; + + +namespace Ionic.BZip2 +{ + /// + /// A write-only decorator stream that compresses data as it is + /// written using the BZip2 algorithm. + /// + public class BZip2OutputStream : System.IO.Stream + { + int totalBytesWrittenIn; + bool leaveOpen; + BZip2Compressor compressor; + uint combinedCRC; + Stream output; + BitWriter bw; + int blockSize100k; // 0...9 + + private TraceBits desiredTrace = TraceBits.Crc | TraceBits.Write; + + /// + /// Constructs a new BZip2OutputStream, that sends its + /// compressed output to the given output stream. + /// + /// + /// + /// The destination stream, to which compressed output will be sent. + /// + /// + /// + /// + /// This example reads a file, then compresses it with bzip2 file, + /// and writes the compressed data into a newly created file. + /// + /// + /// var fname = "logfile.log"; + /// using (var fs = File.OpenRead(fname)) + /// { + /// var outFname = fname + ".bz2"; + /// using (var output = File.Create(outFname)) + /// { + /// using (var compressor = new Ionic.BZip2.BZip2OutputStream(output)) + /// { + /// byte[] buffer = new byte[2048]; + /// int n; + /// while ((n = fs.Read(buffer, 0, buffer.Length)) > 0) + /// { + /// compressor.Write(buffer, 0, n); + /// } + /// } + /// } + /// } + /// + /// + public BZip2OutputStream(Stream output) + : this(output, BZip2.MaxBlockSize, false) + { + } + + + /// + /// Constructs a new BZip2OutputStream with specified blocksize. + /// + /// the destination stream. + /// + /// The blockSize in units of 100000 bytes. + /// The valid range is 1..9. + /// + public BZip2OutputStream(Stream output, int blockSize) + : this(output, blockSize, false) + { + } + + + /// + /// Constructs a new BZip2OutputStream. + /// + /// the destination stream. + /// + /// whether to leave the captive stream open upon closing this stream. + /// + public BZip2OutputStream(Stream output, bool leaveOpen) + : this(output, BZip2.MaxBlockSize, leaveOpen) + { + } + + + /// + /// Constructs a new BZip2OutputStream with specified blocksize, + /// and explicitly specifies whether to leave the wrapped stream open. + /// + /// + /// the destination stream. + /// + /// The blockSize in units of 100000 bytes. + /// The valid range is 1..9. + /// + /// + /// whether to leave the captive stream open upon closing this stream. + /// + public BZip2OutputStream(Stream output, int blockSize, bool leaveOpen) + { + if (blockSize < BZip2.MinBlockSize || + blockSize > BZip2.MaxBlockSize) + { + var msg = String.Format("blockSize={0} is out of range; must be between {1} and {2}", + blockSize, + BZip2.MinBlockSize, BZip2.MaxBlockSize); + throw new ArgumentException(msg, "blockSize"); + } + + this.output = output; + if (!this.output.CanWrite) + throw new ArgumentException("The stream is not writable.", "output"); + + this.bw = new BitWriter(this.output); + this.blockSize100k = blockSize; + this.compressor = new BZip2Compressor(this.bw, blockSize); + this.leaveOpen = leaveOpen; + this.combinedCRC = 0; + EmitHeader(); + } + + + + + /// + /// Close the stream. + /// + /// + /// + /// This may or may not close the underlying stream. Check the + /// constructors that accept a bool value. + /// + /// + public override void Close() + { + if (output != null) + { + Stream o = this.output; + Finish(); + if (!leaveOpen) + o.Close(); + } + } + + + /// + /// Flush the stream. + /// + public override void Flush() + { + if (this.output != null) + { + this.bw.Flush(); + this.output.Flush(); + } + } + + private void EmitHeader() + { + var magic = new byte[] { + (byte) 'B', + (byte) 'Z', + (byte) 'h', + (byte) ('0' + this.blockSize100k) + }; + + // not necessary to shred the initial magic bytes + this.output.Write(magic, 0, magic.Length); + } + + private void EmitTrailer() + { + // A magic 48-bit number, 0x177245385090, to indicate the end + // of the last block. (sqrt(pi), if you want to know) + + TraceOutput(TraceBits.Write, "total written out: {0} (0x{0:X})", + this.bw.TotalBytesWrittenOut); + + // must shred + this.bw.WriteByte(0x17); + this.bw.WriteByte(0x72); + this.bw.WriteByte(0x45); + this.bw.WriteByte(0x38); + this.bw.WriteByte(0x50); + this.bw.WriteByte(0x90); + + this.bw.WriteInt(this.combinedCRC); + + this.bw.FinishAndPad(); + + TraceOutput(TraceBits.Write, "final total: {0} (0x{0:X})", + this.bw.TotalBytesWrittenOut); + } + + void Finish() + { + // Console.WriteLine("BZip2:Finish"); + + try + { + var totalBefore = this.bw.TotalBytesWrittenOut; + this.compressor.CompressAndWrite(); + TraceOutput(TraceBits.Write,"out block length (bytes): {0} (0x{0:X})", + this.bw.TotalBytesWrittenOut - totalBefore); + + TraceOutput(TraceBits.Crc, " combined CRC (before): {0:X8}", + this.combinedCRC); + this.combinedCRC = (this.combinedCRC << 1) | (this.combinedCRC >> 31); + this.combinedCRC ^= (uint) compressor.Crc32; + TraceOutput(TraceBits.Crc, " block CRC : {0:X8}", + this.compressor.Crc32); + TraceOutput(TraceBits.Crc, " combined CRC (final) : {0:X8}", + this.combinedCRC); + + EmitTrailer(); + } + finally + { + this.output = null; + this.compressor = null; + this.bw = null; + } + } + + + /// + /// The blocksize parameter specified at construction time. + /// + public int BlockSize + { + get { return this.blockSize100k; } + } + + + /// + /// Write data to the stream. + /// + /// + /// + /// + /// Use the BZip2OutputStream to compress data while writing: + /// create a BZip2OutputStream with a writable output stream. + /// Then call Write() on that BZip2OutputStream, providing + /// uncompressed data as input. The data sent to the output stream will + /// be the compressed form of the input data. + /// + /// + /// + /// A BZip2OutputStream can be used only for Write() not for Read(). + /// + /// + /// + /// + /// The buffer holding data to write to the stream. + /// the offset within that data array to find the first byte to write. + /// the number of bytes to write. + public override void Write(byte[] buffer, int offset, int count) + { + if (offset < 0) + throw new IndexOutOfRangeException(String.Format("offset ({0}) must be > 0", offset)); + if (count < 0) + throw new IndexOutOfRangeException(String.Format("count ({0}) must be > 0", count)); + if (offset + count > buffer.Length) + throw new IndexOutOfRangeException(String.Format("offset({0}) count({1}) bLength({2})", + offset, count, buffer.Length)); + if (this.output == null) + throw new IOException("the stream is not open"); + + if (count == 0) return; // nothing to do + + int bytesWritten = 0; + int bytesRemaining = count; + + do + { + int n = compressor.Fill(buffer, offset, bytesRemaining); + if (n != bytesRemaining) + { + // The compressor data block is full. Compress and + // write out the compressed data, then reset the + // compressor and continue. + + var totalBefore = this.bw.TotalBytesWrittenOut; + this.compressor.CompressAndWrite(); + TraceOutput(TraceBits.Write,"out block length (bytes): {0} (0x{0:X})", + this.bw.TotalBytesWrittenOut - totalBefore); + + // and now any remaining bits + TraceOutput(TraceBits.Write, + " remaining: {0} 0x{1:X}", + this.bw.NumRemainingBits, + this.bw.RemainingBits); + + TraceOutput(TraceBits.Crc, " combined CRC (before): {0:X8}", + this.combinedCRC); + this.combinedCRC = (this.combinedCRC << 1) | (this.combinedCRC >> 31); + this.combinedCRC ^= (uint) compressor.Crc32; + TraceOutput(TraceBits.Crc, " block CRC : {0:X8}", + compressor.Crc32); + TraceOutput(TraceBits.Crc, " combined CRC (after) : {0:X8}", + this.combinedCRC); + offset += n; + } + bytesRemaining -= n; + bytesWritten += n; + } while (bytesRemaining > 0); + + totalBytesWrittenIn += bytesWritten; + } + + + + + /// + /// Indicates whether the stream can be read. + /// + /// + /// The return value is always false. + /// + public override bool CanRead + { + get { return false; } + } + + /// + /// Indicates whether the stream supports Seek operations. + /// + /// + /// Always returns false. + /// + public override bool CanSeek + { + get { return false; } + } + + /// + /// Indicates whether the stream can be written. + /// + /// + /// The return value should always be true, unless and until the + /// object is disposed and closed. + /// + public override bool CanWrite + { + get + { + if (this.output == null) throw new ObjectDisposedException("BZip2Stream"); + return this.output.CanWrite; + } + } + + /// + /// Reading this property always throws a . + /// + public override long Length + { + get { throw new NotImplementedException(); } + } + + /// + /// The position of the stream pointer. + /// + /// + /// + /// Setting this property always throws a . Reading will return the + /// total number of uncompressed bytes written through. + /// + public override long Position + { + get + { + return this.totalBytesWrittenIn; + } + set { throw new NotImplementedException(); } + } + + /// + /// Calling this method always throws a . + /// + /// this is irrelevant, since it will always throw! + /// this is irrelevant, since it will always throw! + /// irrelevant! + public override long Seek(long offset, System.IO.SeekOrigin origin) + { + throw new NotImplementedException(); + } + + /// + /// Calling this method always throws a . + /// + /// this is irrelevant, since it will always throw! + public override void SetLength(long value) + { + throw new NotImplementedException(); + } + + /// + /// Calling this method always throws a . + /// + /// this parameter is never used + /// this parameter is never used + /// this parameter is never used + /// never returns anything; always throws + public override int Read(byte[] buffer, int offset, int count) + { + throw new NotImplementedException(); + } + + + // used only when Trace is defined + [Flags] + enum TraceBits : uint + { + None = 0, + Crc = 1, + Write = 2, + All = 0xffffffff, + } + + + [System.Diagnostics.ConditionalAttribute("Trace")] + private void TraceOutput(TraceBits bits, string format, params object[] varParams) + { + if ((bits & this.desiredTrace) != 0) + { + //lock(outputLock) + { + int tid = System.Threading.Thread.CurrentThread.GetHashCode(); +#if !SILVERLIGHT && !NETCF + Console.ForegroundColor = (ConsoleColor) (tid % 8 + 10); +#endif + Console.Write("{0:000} PBOS ", tid); + Console.WriteLine(format, varParams); +#if !SILVERLIGHT && !NETCF + Console.ResetColor(); +#endif + } + } + } + + + } + +} diff --git a/DotNetZip/BZip2/BitWriter.cs b/DotNetZip/BZip2/BitWriter.cs new file mode 100644 index 0000000..b739f02 --- /dev/null +++ b/DotNetZip/BZip2/BitWriter.cs @@ -0,0 +1,248 @@ +// BitWriter.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2011 Dino Chiesa. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// Last Saved: <2011-July-25 18:57:31> +// +// ------------------------------------------------------------------ +// +// This module defines the BitWriter class, which writes bits at a time +// to an output stream. It's used by the BZip2Compressor class, and by +// the BZip2OutputStream class and its parallel variant, +// ParallelBZip2OutputStream. +// +// ------------------------------------------------------------------ + +// +// Design notes: +// +// BZip2 employs byte-shredding in its data format - rather than +// aligning all data items in a compressed .bz2 file on byte barriers, +// the BZip2 format uses portions of bytes to represent independent +// pieces of information. This "shredding" starts with the first +// "randomised" bit - just 12 bytes or so into a bz2 file or stream. But +// the approach is used extensively in bzip2 files - sometimes 5 bits +// are used, sometimes 24 or 3 bits, sometimes just 1 bit, and so on. +// It's not possible to send this information directly to a stream in +// this form; Streams in .NET accept byte-oriented input. Therefore, +// when actually writing a bz2 file, the output data must be organized +// into a byte-aligned format before being written to the output stream. +// +// This BitWriter class provides the byte-shredding necessary for BZip2 +// output. Think of this class as an Adapter that enables Bit-oriented +// output to a standard byte-oriented .NET stream. This class writes +// data out to the captive output stream only after the data bits have +// been accumulated and aligned. For example, suppose that during +// operation, the BZip2 compressor emits 5 bits, then 24 bits, then 32 +// bits. When the first 5 bits are sent to the BitWriter, nothing is +// written to the output stream; instead these 5 bits are simply stored +// in the internal accumulator. When the next 24 bits are written, the +// first 3 bits are gathered with the accumulated bits. The resulting +// 5+3 constitutes an entire byte; the BitWriter then actually writes +// that byte to the output stream. This leaves 21 bits. BitWriter writes +// 2 more whole bytes (16 more bits), in 8-bit chunks, leaving 5 in the +// accumulator. BitWriter then follows the same procedure with the 32 +// new bits. And so on. +// +// A quick tour of the implementation: +// +// The accumulator is a uint - so it can accumulate at most 4 bytes of +// information. In practice because of the design of this class, it +// never accumulates more than 3 bytes. +// +// The Flush() method emits all whole bytes available. After calling +// Flush(), there may be between 0-7 bits yet to be emitted into the +// output stream. +// +// FinishAndPad() emits all data, including the last partial byte and +// any necessary padding. In effect, it establishes a byte-alignment +// barrier. To support bzip2, FinishAndPad() should be called only once +// for a bz2 file, after the last bit of data has been written through +// this adapter. Other binary file formats may use byte-alignment at +// various points within the file, and FinishAndPad() would support that +// scenario. +// +// The internal fn Reset() is used to reset the state of the adapter; +// this class is used by BZip2Compressor, instances of which get re-used +// by multiple distinct threads, for different blocks of data. +// + + +using System; +using System.IO; + +namespace Ionic.BZip2 +{ + + internal class BitWriter + { + uint accumulator; + int nAccumulatedBits; + Stream output; + int totalBytesWrittenOut; + + public BitWriter(Stream s) + { + this.output = s; + } + + /// + /// Delivers the remaining bits, left-aligned, in a byte. + /// + /// + /// + /// This is valid only if NumRemainingBits is less than 8; + /// in other words it is valid only after a call to Flush(). + /// + /// + public byte RemainingBits + { + get + { + return (byte) (this.accumulator >> (32 - this.nAccumulatedBits) & 0xff); + } + } + + public int NumRemainingBits + { + get + { + return this.nAccumulatedBits; + } + } + + public int TotalBytesWrittenOut + { + get + { + return this.totalBytesWrittenOut; + } + } + + /// + /// Reset the BitWriter. + /// + /// + /// + /// This is useful when the BitWriter writes into a MemoryStream, and + /// is used by a BZip2Compressor, which itself is re-used for multiple + /// distinct data blocks. + /// + /// + public void Reset() + { + this.accumulator = 0; + this.nAccumulatedBits = 0; + this.totalBytesWrittenOut = 0; + this.output.Seek(0, SeekOrigin.Begin); + this.output.SetLength(0); + } + + /// + /// Write some number of bits from the given value, into the output. + /// + /// + /// + /// The nbits value should be a max of 25, for safety. For performance + /// reasons, this method does not check! + /// + /// + public void WriteBits(int nbits, uint value) + { + int nAccumulated = this.nAccumulatedBits; + uint u = this.accumulator; + + while (nAccumulated >= 8) + { + this.output.WriteByte ((byte)(u >> 24 & 0xff)); + this.totalBytesWrittenOut++; + u <<= 8; + nAccumulated -= 8; + } + + this.accumulator = u | (value << (32 - nAccumulated - nbits)); + this.nAccumulatedBits = nAccumulated + nbits; + + // Console.WriteLine("WriteBits({0}, 0x{1:X2}) => {2:X8} n({3})", + // nbits, value, accumulator, nAccumulatedBits); + // Console.ReadLine(); + + // At this point the accumulator may contain up to 31 bits waiting for + // output. + } + + + /// + /// Write a full 8-bit byte into the output. + /// + public void WriteByte(byte b) + { + WriteBits(8, b); + } + + /// + /// Write four 8-bit bytes into the output. + /// + public void WriteInt(uint u) + { + WriteBits(8, (u >> 24) & 0xff); + WriteBits(8, (u >> 16) & 0xff); + WriteBits(8, (u >> 8) & 0xff); + WriteBits(8, u & 0xff); + } + + /// + /// Write all available byte-aligned bytes. + /// + /// + /// + /// This method writes no new output, but flushes any accumulated + /// bits. At completion, the accumulator may contain up to 7 + /// bits. + /// + /// + /// This is necessary when re-assembling output from N independent + /// compressors, one for each of N blocks. The output of any + /// particular compressor will in general have some fragment of a byte + /// remaining. This fragment needs to be accumulated into the + /// parent BZip2OutputStream. + /// + /// + public void Flush() + { + WriteBits(0,0); + } + + + /// + /// Writes all available bytes, and emits padding for the final byte as + /// necessary. This must be the last method invoked on an instance of + /// BitWriter. + /// + public void FinishAndPad() + { + Flush(); + + if (this.NumRemainingBits > 0) + { + byte b = (byte)((this.accumulator >> 24) & 0xff); + this.output.WriteByte(b); + this.totalBytesWrittenOut++; + } + } + + } + +} \ No newline at end of file diff --git a/DotNetZip/BZip2/NOTICE.txt b/DotNetZip/BZip2/NOTICE.txt new file mode 100644 index 0000000..a5331f0 --- /dev/null +++ b/DotNetZip/BZip2/NOTICE.txt @@ -0,0 +1,5 @@ +Apache Commons Compress +Copyright 2002-2010 The Apache Software Foundation + +This product includes software developed by +The Apache Software Foundation (http://www.apache.org/). diff --git a/DotNetZip/BZip2/ParallelBZip2OutputStream.cs b/DotNetZip/BZip2/ParallelBZip2OutputStream.cs new file mode 100644 index 0000000..b6f241f --- /dev/null +++ b/DotNetZip/BZip2/ParallelBZip2OutputStream.cs @@ -0,0 +1,999 @@ +//#define Trace + +// ParallelBZip2OutputStream.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2011 Dino Chiesa. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// Last Saved: <2011-August-02 16:44:24> +// +// ------------------------------------------------------------------ +// +// This module defines the ParallelBZip2OutputStream class, which is a +// BZip2 compressing stream. This code was derived in part from Apache +// commons source code. The license below applies to the original Apache +// code. +// +// ------------------------------------------------------------------ +// flymake: csc.exe /t:module BZip2InputStream.cs BZip2Compressor.cs Rand.cs BCRC32.cs @@FILE@@ + + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +// Design Notes: +// +// This class follows the classic Decorator pattern: it is a Stream that +// wraps itself around a Stream, and in doing so provides bzip2 +// compression as callers Write into it. It is exactly the same in +// outward function as the BZip2OutputStream, except that this class can +// perform compression using multiple independent threads. Because of +// that, and because of the CPU-intensive nature of BZip2 compression, +// this class can perform significantly better (in terms of wall-click +// time) than the single-threaded variant, at the expense of memory and +// CPU utilization. +// +// BZip2 is a straightforward data format: there are 4 magic bytes at +// the top of the file, followed by 1 or more compressed blocks. There +// is a small "magic byte" trailer after all compressed blocks. +// +// In concept parallelizing BZip2 is simple: do the CPU-intensive +// compression for each block in a separate thread, then emit the +// compressed output, in order, to the output stream. Each block can be +// compressed independently, so a block is the natural candidate for the +// parcel of work that can be passed to an independent worker thread. +// +// The design approach used here is simple: within the Write() method of +// the stream, fill a block. When the block is full, pass it to a +// background worker thread for compression. When the compressor thread +// completes its work, the main thread (the application thread that +// calls Write()) can send the compressed data to the output stream, +// being careful to respect the order of the compressed blocks. +// +// The challenge of ordering the compressed data is a solved and +// well-understood problem - it is the same approach here as DotNetZip +// uses in the ParallelDeflateOutputStream. It is a map/reduce approach +// in design intent. +// +// One new twist for BZip2 is that the compressor output is not +// byte-aligned. In other words the final output of a compressed block +// will in general be a number of bits that is not a multiple of +// 8. Therefore, combining the ordered results of the N compressor +// threads requires additional byte-shredding by the parent +// stream. Hence this stream uses a BitWriter to adapt bit-oriented +// BZip2 output to the byte-oriented .NET Stream. +// +// The approach used here creates N instances of the BZip2Compressor +// type, where N is governed by the number of cores (cpus) and limited +// by the MaxWorkers property exposed by this class. Each +// BZip2Compressor instance gets its own MemoryStream, to which it +// writes its data, via a BitWriter. +// +// along with the bit accumulator described above. The MemoryStream +// would gather the byte-aligned compressed output of the compressor. + +// When reducing the output of the various workers, this class must +// again do the byte-shredding thing. The data from the compressors is +// therefore shredded twice: once when being placed into the +// MemoryStream, and again when emitted into the final output stream +// that this class decorates. This is an unfortunate and seemingly +// unavoidable inefficiency. Two rounds of byte-shredding will use more +// CPU than we'd like, but I haven't imagined a way to avoid it. +// +// The BZip2Compressor is designed to write directly into the parent +// stream's accumulator (BitWriter) when possible, and write into a +// distinct BitWriter when necessary. The former can be used in a +// single-thread scenario, while the latter is required in a +// multi-thread scenario. +// +// ---- +// +// Regarding the Apache code base: Most of the code in this particular +// class is related to stream operations and thread synchronization, and +// is my own code. It largely does not rely on any code obtained from +// Apache commons. If you compare this code with the Apache commons +// BZip2OutputStream, you will see very little code that is common, +// except for the nearly-boilerplate structure that is common to all +// subtypes of System.IO.Stream. There may be some small remnants of +// code in this module derived from the Apache stuff, which is why I +// left the license in here. Most of the Apache commons compressor magic +// has been ported into the BZip2Compressor class. +// + +using System; +using System.IO; +using System.Collections.Generic; +using System.Threading; + +namespace Ionic.BZip2 +{ + internal class WorkItem + { + public int index; + public BZip2Compressor Compressor { get; private set; } + public MemoryStream ms; + public int ordinal; + public BitWriter bw; + + public WorkItem(int ix, int blockSize) + { + // compressed data gets written to a MemoryStream + this.ms = new MemoryStream(); + this.bw = new BitWriter(ms); + this.Compressor = new BZip2Compressor(bw, blockSize); + this.index = ix; + } + } + + + /// + /// A write-only decorator stream that compresses data as it is + /// written using the BZip2 algorithm. This stream compresses by + /// block using multiple threads. + /// + /// + /// This class performs BZIP2 compression through writing. For + /// more information on the BZIP2 algorithm, see + /// . + /// + /// + /// + /// This class is similar to , + /// except that this implementation uses an approach that employs multiple + /// worker threads to perform the compression. On a multi-cpu or multi-core + /// computer, the performance of this class can be significantly higher than + /// the single-threaded BZip2OutputStream, particularly for larger streams. + /// How large? Anything over 10mb is a good candidate for parallel + /// compression. + /// + /// + /// + /// The tradeoff is that this class uses more memory and more CPU than the + /// vanilla BZip2OutputStream. Also, for small files, the + /// ParallelBZip2OutputStream can be much slower than the vanilla + /// BZip2OutputStream, because of the overhead associated to using the + /// thread pool. + /// + /// + /// + public class ParallelBZip2OutputStream : System.IO.Stream + { + private static readonly int BufferPairsPerCore = 4; + private int _maxWorkers; + private bool firstWriteDone; + private int lastFilled; + private int lastWritten; + private int latestCompressed; + private int currentlyFilling; + private volatile Exception pendingException; + private bool handlingException; + private bool emitting; + private System.Collections.Generic.Queue toWrite; + private System.Collections.Generic.Queue toFill; + private System.Collections.Generic.List pool; + private object latestLock = new object(); + private object eLock = new object(); // for exceptions + private object outputLock = new object(); // for multi-thread output + private AutoResetEvent newlyCompressedBlob; + + long totalBytesWrittenIn; + long totalBytesWrittenOut; + bool leaveOpen; + uint combinedCRC; + Stream output; + BitWriter bw; + int blockSize100k; // 0...9 + + private TraceBits desiredTrace = TraceBits.Crc | TraceBits.Write; + + /// + /// Constructs a new ParallelBZip2OutputStream, that sends its + /// compressed output to the given output stream. + /// + /// + /// + /// The destination stream, to which compressed output will be sent. + /// + /// + /// + /// + /// This example reads a file, then compresses it with bzip2 file, + /// and writes the compressed data into a newly created file. + /// + /// + /// var fname = "logfile.log"; + /// using (var fs = File.OpenRead(fname)) + /// { + /// var outFname = fname + ".bz2"; + /// using (var output = File.Create(outFname)) + /// { + /// using (var compressor = new Ionic.BZip2.ParallelBZip2OutputStream(output)) + /// { + /// byte[] buffer = new byte[2048]; + /// int n; + /// while ((n = fs.Read(buffer, 0, buffer.Length)) > 0) + /// { + /// compressor.Write(buffer, 0, n); + /// } + /// } + /// } + /// } + /// + /// + public ParallelBZip2OutputStream(Stream output) + : this(output, BZip2.MaxBlockSize, false) + { + } + + /// + /// Constructs a new ParallelBZip2OutputStream with specified blocksize. + /// + /// the destination stream. + /// + /// The blockSize in units of 100000 bytes. + /// The valid range is 1..9. + /// + public ParallelBZip2OutputStream(Stream output, int blockSize) + : this(output, blockSize, false) + { + } + + /// + /// Constructs a new ParallelBZip2OutputStream. + /// + /// the destination stream. + /// + /// whether to leave the captive stream open upon closing this stream. + /// + public ParallelBZip2OutputStream(Stream output, bool leaveOpen) + : this(output, BZip2.MaxBlockSize, leaveOpen) + { + } + + /// + /// Constructs a new ParallelBZip2OutputStream with specified blocksize, + /// and explicitly specifies whether to leave the wrapped stream open. + /// + /// + /// the destination stream. + /// + /// The blockSize in units of 100000 bytes. + /// The valid range is 1..9. + /// + /// + /// whether to leave the captive stream open upon closing this stream. + /// + public ParallelBZip2OutputStream(Stream output, int blockSize, bool leaveOpen) + { + if (blockSize < BZip2.MinBlockSize || blockSize > BZip2.MaxBlockSize) + { + var msg = String.Format("blockSize={0} is out of range; must be between {1} and {2}", + blockSize, + BZip2.MinBlockSize, BZip2.MaxBlockSize); + throw new ArgumentException(msg, "blockSize"); + } + + this.output = output; + if (!this.output.CanWrite) + throw new ArgumentException("The stream is not writable.", "output"); + + this.bw = new BitWriter(this.output); + this.blockSize100k = blockSize; + this.leaveOpen = leaveOpen; + this.combinedCRC = 0; + this.MaxWorkers = 16; // default + EmitHeader(); + } + + + private void InitializePoolOfWorkItems() + { + this.toWrite = new Queue(); + this.toFill = new Queue(); + this.pool = new System.Collections.Generic.List(); + int nWorkers = BufferPairsPerCore * Environment.ProcessorCount; + nWorkers = Math.Min(nWorkers, this.MaxWorkers); + for(int i=0; i < nWorkers; i++) + { + this.pool.Add(new WorkItem(i, this.blockSize100k)); + this.toFill.Enqueue(i); + } + + this.newlyCompressedBlob = new AutoResetEvent(false); + this.currentlyFilling = -1; + this.lastFilled = -1; + this.lastWritten = -1; + this.latestCompressed = -1; + } + + + /// + /// The maximum number of concurrent compression worker threads to use. + /// + /// + /// + /// + /// This property sets an upper limit on the number of concurrent worker + /// threads to employ for compression. The implementation of this stream + /// employs multiple threads from the .NET thread pool, via + /// ThreadPool.QueueUserWorkItem(), to compress the incoming data by + /// block. As each block of data is compressed, this stream re-orders the + /// compressed blocks and writes them to the output stream. + /// + /// + /// + /// A higher number of workers enables a higher degree of + /// parallelism, which tends to increase the speed of compression on + /// multi-cpu computers. On the other hand, a higher number of buffer + /// pairs also implies a larger memory consumption, more active worker + /// threads, and a higher cpu utilization for any compression. This + /// property enables the application to limit its memory consumption and + /// CPU utilization behavior depending on requirements. + /// + /// + /// + /// By default, DotNetZip allocates 4 workers per CPU core, subject to the + /// upper limit specified in this property. For example, suppose the + /// application sets this property to 16. Then, on a machine with 2 + /// cores, DotNetZip will use 8 workers; that number does not exceed the + /// upper limit specified by this property, so the actual number of + /// workers used will be 4 * 2 = 8. On a machine with 4 cores, DotNetZip + /// will use 16 workers; again, the limit does not apply. On a machine + /// with 8 cores, DotNetZip will use 16 workers, because of the limit. + /// + /// + /// + /// For each compression "worker thread" that occurs in parallel, there is + /// up to 2mb of memory allocated, for buffering and processing. The + /// actual number depends on the property. + /// + /// + /// + /// CPU utilization will also go up with additional workers, because a + /// larger number of buffer pairs allows a larger number of background + /// threads to compress in parallel. If you find that parallel + /// compression is consuming too much memory or CPU, you can adjust this + /// value downward. + /// + /// + /// + /// The default value is 16. Different values may deliver better or + /// worse results, depending on your priorities and the dynamic + /// performance characteristics of your storage and compute resources. + /// + /// + /// + /// The application can set this value at any time, but it is effective + /// only before the first call to Write(), which is when the buffers are + /// allocated. + /// + /// + public int MaxWorkers + { + get + { + return _maxWorkers; + } + set + { + if (value < 4) + throw new ArgumentException("MaxWorkers", + "Value must be 4 or greater."); + _maxWorkers = value; + } + } + + /// + /// Close the stream. + /// + /// + /// + /// This may or may not close the underlying stream. Check the + /// constructors that accept a bool value. + /// + /// + public override void Close() + { + if (this.pendingException != null) + { + this.handlingException = true; + var pe = this.pendingException; + this.pendingException = null; + throw pe; + } + + if (this.handlingException) + return; + + if (output == null) + return; + + Stream o = this.output; + + try + { + FlushOutput(true); + } + finally + { + this.output = null; + this.bw = null; + } + + if (!leaveOpen) + o.Close(); + } + + + private void FlushOutput(bool lastInput) + { + if (this.emitting) return; + + // compress and write whatever is ready + if (this.currentlyFilling >= 0) + { + WorkItem workitem = this.pool[this.currentlyFilling]; + CompressOne(workitem); + this.currentlyFilling = -1; // get a new buffer next Write() + } + + if (lastInput) + { + EmitPendingBuffers(true, false); + EmitTrailer(); + } + else + { + EmitPendingBuffers(false, false); + } + } + + + + /// + /// Flush the stream. + /// + public override void Flush() + { + if (this.output != null) + { + FlushOutput(false); + this.bw.Flush(); + this.output.Flush(); + } + } + + private void EmitHeader() + { + var magic = new byte[] { + (byte) 'B', + (byte) 'Z', + (byte) 'h', + (byte) ('0' + this.blockSize100k) + }; + + // not necessary to shred the initial magic bytes + this.output.Write(magic, 0, magic.Length); + } + + private void EmitTrailer() + { + // A magic 48-bit number, 0x177245385090, to indicate the end + // of the last block. (sqrt(pi), if you want to know) + + TraceOutput(TraceBits.Write, "total written out: {0} (0x{0:X})", + this.bw.TotalBytesWrittenOut); + + // must shred + this.bw.WriteByte(0x17); + this.bw.WriteByte(0x72); + this.bw.WriteByte(0x45); + this.bw.WriteByte(0x38); + this.bw.WriteByte(0x50); + this.bw.WriteByte(0x90); + + this.bw.WriteInt(this.combinedCRC); + + this.bw.FinishAndPad(); + + TraceOutput(TraceBits.Write, "final total : {0} (0x{0:X})", + this.bw.TotalBytesWrittenOut); + } + + + /// + /// The blocksize parameter specified at construction time. + /// + public int BlockSize + { + get { return this.blockSize100k; } + } + + + /// + /// Write data to the stream. + /// + /// + /// + /// + /// Use the ParallelBZip2OutputStream to compress data while + /// writing: create a ParallelBZip2OutputStream with a writable + /// output stream. Then call Write() on that + /// ParallelBZip2OutputStream, providing uncompressed data as + /// input. The data sent to the output stream will be the compressed + /// form of the input data. + /// + /// + /// + /// A ParallelBZip2OutputStream can be used only for + /// Write() not for Read(). + /// + /// + /// + /// + /// The buffer holding data to write to the stream. + /// the offset within that data array to find the first byte to write. + /// the number of bytes to write. + public override void Write(byte[] buffer, int offset, int count) + { + bool mustWait = false; + + // This method does this: + // 0. handles any pending exceptions + // 1. write any buffers that are ready to be written + // 2. fills a compressor buffer; when full, flip state to 'Filled', + // 3. if more data to be written, goto step 1 + + if (this.output == null) + throw new IOException("the stream is not open"); + + // dispense any exceptions that occurred on the BG threads + if (this.pendingException != null) + { + this.handlingException = true; + var pe = this.pendingException; + this.pendingException = null; + throw pe; + } + + if (offset < 0) + throw new IndexOutOfRangeException(String.Format("offset ({0}) must be > 0", offset)); + if (count < 0) + throw new IndexOutOfRangeException(String.Format("count ({0}) must be > 0", count)); + if (offset + count > buffer.Length) + throw new IndexOutOfRangeException(String.Format("offset({0}) count({1}) bLength({2})", + offset, count, buffer.Length)); + + + if (count == 0) return; // nothing to do + + + if (!this.firstWriteDone) + { + // Want to do this on first Write, first session, and not in the + // constructor. Must allow the MaxWorkers to change after + // construction, but before first Write(). + InitializePoolOfWorkItems(); + this.firstWriteDone = true; + } + + int bytesWritten = 0; + int bytesRemaining = count; + + do + { + // may need to make buffers available + EmitPendingBuffers(false, mustWait); + + mustWait = false; + + // get a compressor to fill + int ix = -1; + if (this.currentlyFilling >= 0) + { + ix = this.currentlyFilling; + } + else + { + if (this.toFill.Count == 0) + { + // No compressors available to fill, so... need to emit + // compressed buffers. + mustWait = true; + continue; + } + + ix = this.toFill.Dequeue(); + ++this.lastFilled; + } + + WorkItem workitem = this.pool[ix]; + workitem.ordinal = this.lastFilled; + + int n = workitem.Compressor.Fill(buffer, offset, bytesRemaining); + if (n != bytesRemaining) + { + if (!ThreadPool.QueueUserWorkItem( CompressOne, workitem )) + throw new Exception("Cannot enqueue workitem"); + + this.currentlyFilling = -1; // will get a new buffer next time + offset += n; + } + else + this.currentlyFilling = ix; + + bytesRemaining -= n; + bytesWritten += n; + } + while (bytesRemaining > 0); + + totalBytesWrittenIn += bytesWritten; + return; + } + + + + private void EmitPendingBuffers(bool doAll, bool mustWait) + { + // When combining parallel compression with a ZipSegmentedStream, it's + // possible for the ZSS to throw from within this method. In that + // case, Close/Dispose will be called on this stream, if this stream + // is employed within a using or try/finally pair as required. But + // this stream is unaware of the pending exception, so the Close() + // method invokes this method AGAIN. This can lead to a deadlock. + // Therefore, failfast if re-entering. + + if (emitting) return; + emitting = true; + + if (doAll || mustWait) + this.newlyCompressedBlob.WaitOne(); + + do + { + int firstSkip = -1; + int millisecondsToWait = doAll ? 200 : (mustWait ? -1 : 0); + int nextToWrite = -1; + + do + { + if (Monitor.TryEnter(this.toWrite, millisecondsToWait)) + { + nextToWrite = -1; + try + { + if (this.toWrite.Count > 0) + nextToWrite = this.toWrite.Dequeue(); + } + finally + { + Monitor.Exit(this.toWrite); + } + + if (nextToWrite >= 0) + { + WorkItem workitem = this.pool[nextToWrite]; + if (workitem.ordinal != this.lastWritten + 1) + { + // out of order. requeue and try again. + lock(this.toWrite) + { + this.toWrite.Enqueue(nextToWrite); + } + + if (firstSkip == nextToWrite) + { + // We went around the list once. + // None of the items in the list is the one we want. + // Now wait for a compressor to signal again. + this.newlyCompressedBlob.WaitOne(); + firstSkip = -1; + } + else if (firstSkip == -1) + firstSkip = nextToWrite; + + continue; + } + + firstSkip = -1; + + TraceOutput(TraceBits.Write, + "Writing block {0}", workitem.ordinal); + + // write the data to the output + var bw2 = workitem.bw; + bw2.Flush(); // not bw2.FinishAndPad()! + var ms = workitem.ms; + ms.Seek(0,SeekOrigin.Begin); + + // cannot dump bytes!! + // ms.WriteTo(this.output); + // + // must do byte shredding: + int n; + int y = -1; + long totOut = 0; + var buffer = new byte[1024]; + while ((n = ms.Read(buffer,0,buffer.Length)) > 0) + { +#if Trace + if (y == -1) // diagnostics only + { + var sb1 = new System.Text.StringBuilder(); + sb1.Append("first 16 whole bytes in block: "); + for (int z=0; z < 16; z++) + sb1.Append(String.Format(" {0:X2}", buffer[z])); + TraceOutput(TraceBits.Write, sb1.ToString()); + } +#endif + y = n; + for (int k=0; k < n; k++) + { + this.bw.WriteByte(buffer[k]); + } + totOut += n; + } +#if Trace + TraceOutput(TraceBits.Write,"out block length (bytes): {0} (0x{0:X})", totOut); + var sb = new System.Text.StringBuilder(); + sb.Append("final 16 whole bytes in block: "); + for (int z=0; z < 16; z++) + sb.Append(String.Format(" {0:X2}", buffer[y-1-12+z])); + TraceOutput(TraceBits.Write, sb.ToString()); +#endif + + // and now any remaining bits + TraceOutput(TraceBits.Write, + " remaining bits: {0} 0x{1:X}", + bw2.NumRemainingBits, + bw2.RemainingBits); + if (bw2.NumRemainingBits > 0) + { + this.bw.WriteBits(bw2.NumRemainingBits, bw2.RemainingBits); + } + + TraceOutput(TraceBits.Crc," combined CRC (before): {0:X8}", + this.combinedCRC); + this.combinedCRC = (this.combinedCRC << 1) | (this.combinedCRC >> 31); + this.combinedCRC ^= (uint) workitem.Compressor.Crc32; + + TraceOutput(TraceBits.Crc, + " block CRC : {0:X8}", + workitem.Compressor.Crc32); + TraceOutput(TraceBits.Crc, + " combined CRC (after) : {0:X8}", + this.combinedCRC); + TraceOutput(TraceBits.Write, + "total written out: {0} (0x{0:X})", + this.bw.TotalBytesWrittenOut); + TraceOutput(TraceBits.Write | TraceBits.Crc, ""); + + this.totalBytesWrittenOut += totOut; + + bw2.Reset(); + this.lastWritten = workitem.ordinal; + workitem.ordinal = -1; + this.toFill.Enqueue(workitem.index); + + // don't wait next time through + if (millisecondsToWait == -1) millisecondsToWait = 0; + } + } + else + nextToWrite = -1; + + } while (nextToWrite >= 0); + + } while (doAll && (this.lastWritten != this.latestCompressed)); + + if (doAll) + { + TraceOutput(TraceBits.Crc, + " combined CRC (final) : {0:X8}", this.combinedCRC); + } + + emitting = false; + } + + + private void CompressOne(Object wi) + { + // compress and one buffer + WorkItem workitem = (WorkItem) wi; + try + { + // compress and write to the compressor's MemoryStream + workitem.Compressor.CompressAndWrite(); + + lock(this.latestLock) + { + if (workitem.ordinal > this.latestCompressed) + this.latestCompressed = workitem.ordinal; + } + lock (this.toWrite) + { + this.toWrite.Enqueue(workitem.index); + } + this.newlyCompressedBlob.Set(); + } + catch (System.Exception exc1) + { + lock(this.eLock) + { + // expose the exception to the main thread + if (this.pendingException!=null) + this.pendingException = exc1; + } + } + } + + + + + /// + /// Indicates whether the stream can be read. + /// + /// + /// The return value is always false. + /// + public override bool CanRead + { + get { return false; } + } + + /// + /// Indicates whether the stream supports Seek operations. + /// + /// + /// Always returns false. + /// + public override bool CanSeek + { + get { return false; } + } + + /// + /// Indicates whether the stream can be written. + /// + /// + /// The return value depends on whether the captive stream supports writing. + /// + public override bool CanWrite + { + get + { + if (this.output == null) throw new ObjectDisposedException("BZip2Stream"); + return this.output.CanWrite; + } + } + + /// + /// Reading this property always throws a . + /// + public override long Length + { + get { throw new NotImplementedException(); } + } + + /// + /// The position of the stream pointer. + /// + /// + /// + /// Setting this property always throws a . Reading will return the + /// total number of uncompressed bytes written through. + /// + public override long Position + { + get + { + return this.totalBytesWrittenIn; + } + set { throw new NotImplementedException(); } + } + + /// + /// The total number of bytes written out by the stream. + /// + /// + /// This value is meaningful only after a call to Close(). + /// + public Int64 BytesWrittenOut { get { return totalBytesWrittenOut; } } + + /// + /// Calling this method always throws a . + /// + /// this is irrelevant, since it will always throw! + /// this is irrelevant, since it will always throw! + /// irrelevant! + public override long Seek(long offset, System.IO.SeekOrigin origin) + { + throw new NotImplementedException(); + } + + /// + /// Calling this method always throws a . + /// + /// this is irrelevant, since it will always throw! + public override void SetLength(long value) + { + throw new NotImplementedException(); + } + + /// + /// Calling this method always throws a . + /// + /// this parameter is never used + /// this parameter is never used + /// this parameter is never used + /// never returns anything; always throws + public override int Read(byte[] buffer, int offset, int count) + { + throw new NotImplementedException(); + } + + + // used only when Trace is defined + [Flags] + enum TraceBits : uint + { + None = 0, + Crc = 1, + Write = 2, + All = 0xffffffff, + } + + + [System.Diagnostics.ConditionalAttribute("Trace")] + private void TraceOutput(TraceBits bits, string format, params object[] varParams) + { + if ((bits & this.desiredTrace) != 0) + { + lock(outputLock) + { + int tid = Thread.CurrentThread.GetHashCode(); +#if !SILVERLIGHT + Console.ForegroundColor = (ConsoleColor) (tid % 8 + 10); +#endif + Console.Write("{0:000} PBOS ", tid); + Console.WriteLine(format, varParams); +#if !SILVERLIGHT + Console.ResetColor(); +#endif + } + } + } + + } + +} diff --git a/DotNetZip/BZip2/Properties/AssemblyInfo.cs b/DotNetZip/BZip2/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..9473f11 Binary files /dev/null and b/DotNetZip/BZip2/Properties/AssemblyInfo.cs differ diff --git a/DotNetZip/BZip2/Rand.cs b/DotNetZip/BZip2/Rand.cs new file mode 100644 index 0000000..ac00dec --- /dev/null +++ b/DotNetZip/BZip2/Rand.cs @@ -0,0 +1,99 @@ +// Rand.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2011 Dino Chiesa. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// Last Saved: <2011-July-31 15:09:16> +// +// ------------------------------------------------------------------ +// +// This module defines a helper class for the BZip2 classes. This code +// is derived from the original BZip2 source code. +// +// ------------------------------------------------------------------ + + +namespace Ionic.BZip2 +{ + internal static class Rand + { + private static int[] RNUMS = + { + 619, 720, 127, 481, 931, 816, 813, 233, 566, 247, + 985, 724, 205, 454, 863, 491, 741, 242, 949, 214, + 733, 859, 335, 708, 621, 574, 73, 654, 730, 472, + 419, 436, 278, 496, 867, 210, 399, 680, 480, 51, + 878, 465, 811, 169, 869, 675, 611, 697, 867, 561, + 862, 687, 507, 283, 482, 129, 807, 591, 733, 623, + 150, 238, 59, 379, 684, 877, 625, 169, 643, 105, + 170, 607, 520, 932, 727, 476, 693, 425, 174, 647, + 73, 122, 335, 530, 442, 853, 695, 249, 445, 515, + 909, 545, 703, 919, 874, 474, 882, 500, 594, 612, + 641, 801, 220, 162, 819, 984, 589, 513, 495, 799, + 161, 604, 958, 533, 221, 400, 386, 867, 600, 782, + 382, 596, 414, 171, 516, 375, 682, 485, 911, 276, + 98, 553, 163, 354, 666, 933, 424, 341, 533, 870, + 227, 730, 475, 186, 263, 647, 537, 686, 600, 224, + 469, 68, 770, 919, 190, 373, 294, 822, 808, 206, + 184, 943, 795, 384, 383, 461, 404, 758, 839, 887, + 715, 67, 618, 276, 204, 918, 873, 777, 604, 560, + 951, 160, 578, 722, 79, 804, 96, 409, 713, 940, + 652, 934, 970, 447, 318, 353, 859, 672, 112, 785, + 645, 863, 803, 350, 139, 93, 354, 99, 820, 908, + 609, 772, 154, 274, 580, 184, 79, 626, 630, 742, + 653, 282, 762, 623, 680, 81, 927, 626, 789, 125, + 411, 521, 938, 300, 821, 78, 343, 175, 128, 250, + 170, 774, 972, 275, 999, 639, 495, 78, 352, 126, + 857, 956, 358, 619, 580, 124, 737, 594, 701, 612, + 669, 112, 134, 694, 363, 992, 809, 743, 168, 974, + 944, 375, 748, 52, 600, 747, 642, 182, 862, 81, + 344, 805, 988, 739, 511, 655, 814, 334, 249, 515, + 897, 955, 664, 981, 649, 113, 974, 459, 893, 228, + 433, 837, 553, 268, 926, 240, 102, 654, 459, 51, + 686, 754, 806, 760, 493, 403, 415, 394, 687, 700, + 946, 670, 656, 610, 738, 392, 760, 799, 887, 653, + 978, 321, 576, 617, 626, 502, 894, 679, 243, 440, + 680, 879, 194, 572, 640, 724, 926, 56, 204, 700, + 707, 151, 457, 449, 797, 195, 791, 558, 945, 679, + 297, 59, 87, 824, 713, 663, 412, 693, 342, 606, + 134, 108, 571, 364, 631, 212, 174, 643, 304, 329, + 343, 97, 430, 751, 497, 314, 983, 374, 822, 928, + 140, 206, 73, 263, 980, 736, 876, 478, 430, 305, + 170, 514, 364, 692, 829, 82, 855, 953, 676, 246, + 369, 970, 294, 750, 807, 827, 150, 790, 288, 923, + 804, 378, 215, 828, 592, 281, 565, 555, 710, 82, + 896, 831, 547, 261, 524, 462, 293, 465, 502, 56, + 661, 821, 976, 991, 658, 869, 905, 758, 745, 193, + 768, 550, 608, 933, 378, 286, 215, 979, 792, 961, + 61, 688, 793, 644, 986, 403, 106, 366, 905, 644, + 372, 567, 466, 434, 645, 210, 389, 550, 919, 135, + 780, 773, 635, 389, 707, 100, 626, 958, 165, 504, + 920, 176, 193, 713, 857, 265, 203, 50, 668, 108, + 645, 990, 626, 197, 510, 357, 358, 850, 858, 364, + 936, 638 + }; + + + /// + /// Returns the "random" number at a specific index. + /// + /// the index + /// the random number + internal static int Rnums(int i) + { + return RNUMS[i]; + } + } + +} \ No newline at end of file diff --git a/DotNetZip/CommonSrc/CRC32.cs b/DotNetZip/CommonSrc/CRC32.cs new file mode 100644 index 0000000..97593b9 --- /dev/null +++ b/DotNetZip/CommonSrc/CRC32.cs @@ -0,0 +1,814 @@ +// CRC32.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2011 Dino Chiesa. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// Last Saved: <2011-August-02 18:25:54> +// +// ------------------------------------------------------------------ +// +// This module defines the CRC32 class, which can do the CRC32 algorithm, using +// arbitrary starting polynomials, and bit reversal. The bit reversal is what +// distinguishes this CRC-32 used in BZip2 from the CRC-32 that is used in PKZIP +// files, or GZIP files. This class does both. +// +// ------------------------------------------------------------------ + + +using System; +using Interop = System.Runtime.InteropServices; + +namespace Ionic.Crc +{ + /// + /// Computes a CRC-32. The CRC-32 algorithm is parameterized - you + /// can set the polynomial and enable or disable bit + /// reversal. This can be used for GZIP, BZip2, or ZIP. + /// + /// + /// This type is used internally by DotNetZip; it is generally not used + /// directly by applications wishing to create, read, or manipulate zip + /// archive files. + /// + + [Interop.GuidAttribute("ebc25cf6-9120-4283-b972-0e5520d0000C")] + [Interop.ComVisible(true)] +#if !NETCF + [Interop.ClassInterface(Interop.ClassInterfaceType.AutoDispatch)] +#endif + public class CRC32 + { + /// + /// Indicates the total number of bytes applied to the CRC. + /// + public Int64 TotalBytesRead + { + get + { + return _TotalBytesRead; + } + } + + /// + /// Indicates the current CRC for all blocks slurped in. + /// + public Int32 Crc32Result + { + get + { + return unchecked((Int32)(~_register)); + } + } + + /// + /// Returns the CRC32 for the specified stream. + /// + /// The stream over which to calculate the CRC32 + /// the CRC32 calculation + public Int32 GetCrc32(System.IO.Stream input) + { + return GetCrc32AndCopy(input, null); + } + + /// + /// Returns the CRC32 for the specified stream, and writes the input into the + /// output stream. + /// + /// The stream over which to calculate the CRC32 + /// The stream into which to deflate the input + /// the CRC32 calculation + public Int32 GetCrc32AndCopy(System.IO.Stream input, System.IO.Stream output) + { + if (input == null) + throw new Exception("The input stream must not be null."); + + unchecked + { + byte[] buffer = new byte[BUFFER_SIZE]; + int readSize = BUFFER_SIZE; + + _TotalBytesRead = 0; + int count = input.Read(buffer, 0, readSize); + if (output != null) output.Write(buffer, 0, count); + _TotalBytesRead += count; + while (count > 0) + { + SlurpBlock(buffer, 0, count); + count = input.Read(buffer, 0, readSize); + if (output != null) output.Write(buffer, 0, count); + _TotalBytesRead += count; + } + + return (Int32)(~_register); + } + } + + + /// + /// Get the CRC32 for the given (word,byte) combo. This is a + /// computation defined by PKzip for PKZIP 2.0 (weak) encryption. + /// + /// The word to start with. + /// The byte to combine it with. + /// The CRC-ized result. + public Int32 ComputeCrc32(Int32 W, byte B) + { + return _InternalComputeCrc32((UInt32)W, B); + } + + internal Int32 _InternalComputeCrc32(UInt32 W, byte B) + { + return (Int32)(crc32Table[(W ^ B) & 0xFF] ^ (W >> 8)); + } + + + /// + /// Update the value for the running CRC32 using the given block of bytes. + /// This is useful when using the CRC32() class in a Stream. + /// + /// block of bytes to slurp + /// starting point in the block + /// how many bytes within the block to slurp + public void SlurpBlock(byte[] block, int offset, int count) + { + if (block == null) + throw new Exception("The data buffer must not be null."); + + // bzip algorithm + for (int i = 0; i < count; i++) + { + int x = offset + i; + byte b = block[x]; + if (this.reverseBits) + { + UInt32 temp = (_register >> 24) ^ b; + _register = (_register << 8) ^ crc32Table[temp]; + } + else + { + UInt32 temp = (_register & 0x000000FF) ^ b; + _register = (_register >> 8) ^ crc32Table[temp]; + } + } + _TotalBytesRead += count; + } + + + /// + /// Process one byte in the CRC. + /// + /// the byte to include into the CRC . + public void UpdateCRC(byte b) + { + if (this.reverseBits) + { + UInt32 temp = (_register >> 24) ^ b; + _register = (_register << 8) ^ crc32Table[temp]; + } + else + { + UInt32 temp = (_register & 0x000000FF) ^ b; + _register = (_register >> 8) ^ crc32Table[temp]; + } + } + + /// + /// Process a run of N identical bytes into the CRC. + /// + /// + /// + /// This method serves as an optimization for updating the CRC when a + /// run of identical bytes is found. Rather than passing in a buffer of + /// length n, containing all identical bytes b, this method accepts the + /// byte value and the length of the (virtual) buffer - the length of + /// the run. + /// + /// + /// the byte to include into the CRC. + /// the number of times that byte should be repeated. + public void UpdateCRC(byte b, int n) + { + while (n-- > 0) + { + if (this.reverseBits) + { + uint temp = (_register >> 24) ^ b; + _register = (_register << 8) ^ crc32Table[(temp >= 0) + ? temp + : (temp + 256)]; + } + else + { + UInt32 temp = (_register & 0x000000FF) ^ b; + _register = (_register >> 8) ^ crc32Table[(temp >= 0) + ? temp + : (temp + 256)]; + + } + } + } + + + + private static uint ReverseBits(uint data) + { + unchecked + { + uint ret = data; + ret = (ret & 0x55555555) << 1 | (ret >> 1) & 0x55555555; + ret = (ret & 0x33333333) << 2 | (ret >> 2) & 0x33333333; + ret = (ret & 0x0F0F0F0F) << 4 | (ret >> 4) & 0x0F0F0F0F; + ret = (ret << 24) | ((ret & 0xFF00) << 8) | ((ret >> 8) & 0xFF00) | (ret >> 24); + return ret; + } + } + + private static byte ReverseBits(byte data) + { + unchecked + { + uint u = (uint)data * 0x00020202; + uint m = 0x01044010; + uint s = u & m; + uint t = (u << 2) & (m << 1); + return (byte)((0x01001001 * (s + t)) >> 24); + } + } + + + + private void GenerateLookupTable() + { + crc32Table = new UInt32[256]; + unchecked + { + UInt32 dwCrc; + byte i = 0; + do + { + dwCrc = i; + for (byte j = 8; j > 0; j--) + { + if ((dwCrc & 1) == 1) + { + dwCrc = (dwCrc >> 1) ^ dwPolynomial; + } + else + { + dwCrc >>= 1; + } + } + if (reverseBits) + { + crc32Table[ReverseBits(i)] = ReverseBits(dwCrc); + } + else + { + crc32Table[i] = dwCrc; + } + i++; + } while (i!=0); + } + +#if VERBOSE + Console.WriteLine(); + Console.WriteLine("private static readonly UInt32[] crc32Table = {"); + for (int i = 0; i < crc32Table.Length; i+=4) + { + Console.Write(" "); + for (int j=0; j < 4; j++) + { + Console.Write(" 0x{0:X8}U,", crc32Table[i+j]); + } + Console.WriteLine(); + } + Console.WriteLine("};"); + Console.WriteLine(); +#endif + } + + + private uint gf2_matrix_times(uint[] matrix, uint vec) + { + uint sum = 0; + int i=0; + while (vec != 0) + { + if ((vec & 0x01)== 0x01) + sum ^= matrix[i]; + vec >>= 1; + i++; + } + return sum; + } + + private void gf2_matrix_square(uint[] square, uint[] mat) + { + for (int i = 0; i < 32; i++) + square[i] = gf2_matrix_times(mat, mat[i]); + } + + + + /// + /// Combines the given CRC32 value with the current running total. + /// + /// + /// This is useful when using a divide-and-conquer approach to + /// calculating a CRC. Multiple threads can each calculate a + /// CRC32 on a segment of the data, and then combine the + /// individual CRC32 values at the end. + /// + /// the crc value to be combined with this one + /// the length of data the CRC value was calculated on + public void Combine(int crc, int length) + { + uint[] even = new uint[32]; // even-power-of-two zeros operator + uint[] odd = new uint[32]; // odd-power-of-two zeros operator + + if (length == 0) + return; + + uint crc1= ~_register; + uint crc2= (uint) crc; + + // put operator for one zero bit in odd + odd[0] = this.dwPolynomial; // the CRC-32 polynomial + uint row = 1; + for (int i = 1; i < 32; i++) + { + odd[i] = row; + row <<= 1; + } + + // put operator for two zero bits in even + gf2_matrix_square(even, odd); + + // put operator for four zero bits in odd + gf2_matrix_square(odd, even); + + uint len2 = (uint) length; + + // apply len2 zeros to crc1 (first square will put the operator for one + // zero byte, eight zero bits, in even) + do { + // apply zeros operator for this bit of len2 + gf2_matrix_square(even, odd); + + if ((len2 & 1)== 1) + crc1 = gf2_matrix_times(even, crc1); + len2 >>= 1; + + if (len2 == 0) + break; + + // another iteration of the loop with odd and even swapped + gf2_matrix_square(odd, even); + if ((len2 & 1)==1) + crc1 = gf2_matrix_times(odd, crc1); + len2 >>= 1; + + + } while (len2 != 0); + + crc1 ^= crc2; + + _register= ~crc1; + + //return (int) crc1; + return; + } + + + /// + /// Create an instance of the CRC32 class using the default settings: no + /// bit reversal, and a polynomial of 0xEDB88320. + /// + public CRC32() : this(false) + { + } + + /// + /// Create an instance of the CRC32 class, specifying whether to reverse + /// data bits or not. + /// + /// + /// specify true if the instance should reverse data bits. + /// + /// + /// + /// In the CRC-32 used by BZip2, the bits are reversed. Therefore if you + /// want a CRC32 with compatibility with BZip2, you should pass true + /// here. In the CRC-32 used by GZIP and PKZIP, the bits are not + /// reversed; Therefore if you want a CRC32 with compatibility with + /// those, you should pass false. + /// + /// + public CRC32(bool reverseBits) : + this( unchecked((int)0xEDB88320), reverseBits) + { + } + + + /// + /// Create an instance of the CRC32 class, specifying the polynomial and + /// whether to reverse data bits or not. + /// + /// + /// The polynomial to use for the CRC, expressed in the reversed (LSB) + /// format: the highest ordered bit in the polynomial value is the + /// coefficient of the 0th power; the second-highest order bit is the + /// coefficient of the 1 power, and so on. Expressed this way, the + /// polynomial for the CRC-32C used in IEEE 802.3, is 0xEDB88320. + /// + /// + /// specify true if the instance should reverse data bits. + /// + /// + /// + /// + /// In the CRC-32 used by BZip2, the bits are reversed. Therefore if you + /// want a CRC32 with compatibility with BZip2, you should pass true + /// here for the reverseBits parameter. In the CRC-32 used by + /// GZIP and PKZIP, the bits are not reversed; Therefore if you want a + /// CRC32 with compatibility with those, you should pass false for the + /// reverseBits parameter. + /// + /// + public CRC32(int polynomial, bool reverseBits) + { + this.reverseBits = reverseBits; + this.dwPolynomial = (uint) polynomial; + this.GenerateLookupTable(); + } + + /// + /// Reset the CRC-32 class - clear the CRC "remainder register." + /// + /// + /// + /// Use this when employing a single instance of this class to compute + /// multiple, distinct CRCs on multiple, distinct data blocks. + /// + /// + public void Reset() + { + _register = 0xFFFFFFFFU; + } + + // private member vars + private UInt32 dwPolynomial; + private Int64 _TotalBytesRead; + private bool reverseBits; + private UInt32[] crc32Table; + private const int BUFFER_SIZE = 8192; + private UInt32 _register = 0xFFFFFFFFU; + } + + + /// + /// A Stream that calculates a CRC32 (a checksum) on all bytes read, + /// or on all bytes written. + /// + /// + /// + /// + /// This class can be used to verify the CRC of a ZipEntry when + /// reading from a stream, or to calculate a CRC when writing to a + /// stream. The stream should be used to either read, or write, but + /// not both. If you intermix reads and writes, the results are not + /// defined. + /// + /// + /// + /// This class is intended primarily for use internally by the + /// DotNetZip library. + /// + /// + public class CrcCalculatorStream : System.IO.Stream, System.IDisposable + { + private static readonly Int64 UnsetLengthLimit = -99; + + internal System.IO.Stream _innerStream; + private CRC32 _Crc32; + private Int64 _lengthLimit = -99; + private bool _leaveOpen; + + /// + /// The default constructor. + /// + /// + /// + /// Instances returned from this constructor will leave the underlying + /// stream open upon Close(). The stream uses the default CRC32 + /// algorithm, which implies a polynomial of 0xEDB88320. + /// + /// + /// The underlying stream + public CrcCalculatorStream(System.IO.Stream stream) + : this(true, CrcCalculatorStream.UnsetLengthLimit, stream, null) + { + } + + /// + /// The constructor allows the caller to specify how to handle the + /// underlying stream at close. + /// + /// + /// + /// The stream uses the default CRC32 algorithm, which implies a + /// polynomial of 0xEDB88320. + /// + /// + /// The underlying stream + /// true to leave the underlying stream + /// open upon close of the CrcCalculatorStream; false otherwise. + public CrcCalculatorStream(System.IO.Stream stream, bool leaveOpen) + : this(leaveOpen, CrcCalculatorStream.UnsetLengthLimit, stream, null) + { + } + + /// + /// A constructor allowing the specification of the length of the stream + /// to read. + /// + /// + /// + /// The stream uses the default CRC32 algorithm, which implies a + /// polynomial of 0xEDB88320. + /// + /// + /// Instances returned from this constructor will leave the underlying + /// stream open upon Close(). + /// + /// + /// The underlying stream + /// The length of the stream to slurp + public CrcCalculatorStream(System.IO.Stream stream, Int64 length) + : this(true, length, stream, null) + { + if (length < 0) + throw new ArgumentException("length"); + } + + /// + /// A constructor allowing the specification of the length of the stream + /// to read, as well as whether to keep the underlying stream open upon + /// Close(). + /// + /// + /// + /// The stream uses the default CRC32 algorithm, which implies a + /// polynomial of 0xEDB88320. + /// + /// + /// The underlying stream + /// The length of the stream to slurp + /// true to leave the underlying stream + /// open upon close of the CrcCalculatorStream; false otherwise. + public CrcCalculatorStream(System.IO.Stream stream, Int64 length, bool leaveOpen) + : this(leaveOpen, length, stream, null) + { + if (length < 0) + throw new ArgumentException("length"); + } + + /// + /// A constructor allowing the specification of the length of the stream + /// to read, as well as whether to keep the underlying stream open upon + /// Close(), and the CRC32 instance to use. + /// + /// + /// + /// The stream uses the specified CRC32 instance, which allows the + /// application to specify how the CRC gets calculated. + /// + /// + /// The underlying stream + /// The length of the stream to slurp + /// true to leave the underlying stream + /// open upon close of the CrcCalculatorStream; false otherwise. + /// the CRC32 instance to use to calculate the CRC32 + public CrcCalculatorStream(System.IO.Stream stream, Int64 length, bool leaveOpen, + CRC32 crc32) + : this(leaveOpen, length, stream, crc32) + { + if (length < 0) + throw new ArgumentException("length"); + } + + + // This ctor is private - no validation is done here. This is to allow the use + // of a (specific) negative value for the _lengthLimit, to indicate that there + // is no length set. So we validate the length limit in those ctors that use an + // explicit param, otherwise we don't validate, because it could be our special + // value. + private CrcCalculatorStream + (bool leaveOpen, Int64 length, System.IO.Stream stream, CRC32 crc32) + : base() + { + _innerStream = stream; + _Crc32 = crc32 ?? new CRC32(); + _lengthLimit = length; + _leaveOpen = leaveOpen; + } + + + /// + /// Gets the total number of bytes run through the CRC32 calculator. + /// + /// + /// + /// This is either the total number of bytes read, or the total number of + /// bytes written, depending on the direction of this stream. + /// + public Int64 TotalBytesSlurped + { + get { return _Crc32.TotalBytesRead; } + } + + /// + /// Provides the current CRC for all blocks slurped in. + /// + /// + /// + /// The running total of the CRC is kept as data is written or read + /// through the stream. read this property after all reads or writes to + /// get an accurate CRC for the entire stream. + /// + /// + public Int32 Crc + { + get { return _Crc32.Crc32Result; } + } + + /// + /// Indicates whether the underlying stream will be left open when the + /// CrcCalculatorStream is Closed. + /// + /// + /// + /// Set this at any point before calling . + /// + /// + public bool LeaveOpen + { + get { return _leaveOpen; } + set { _leaveOpen = value; } + } + + /// + /// Read from the stream + /// + /// the buffer to read + /// the offset at which to start + /// the number of bytes to read + /// the number of bytes actually read + public override int Read(byte[] buffer, int offset, int count) + { + int bytesToRead = count; + + // Need to limit the # of bytes returned, if the stream is intended to have + // a definite length. This is especially useful when returning a stream for + // the uncompressed data directly to the application. The app won't + // necessarily read only the UncompressedSize number of bytes. For example + // wrapping the stream returned from OpenReader() into a StreadReader() and + // calling ReadToEnd() on it, We can "over-read" the zip data and get a + // corrupt string. The length limits that, prevents that problem. + + if (_lengthLimit != CrcCalculatorStream.UnsetLengthLimit) + { + if (_Crc32.TotalBytesRead >= _lengthLimit) return 0; // EOF + Int64 bytesRemaining = _lengthLimit - _Crc32.TotalBytesRead; + if (bytesRemaining < count) bytesToRead = (int)bytesRemaining; + } + int n = _innerStream.Read(buffer, offset, bytesToRead); + if (n > 0) _Crc32.SlurpBlock(buffer, offset, n); + return n; + } + + /// + /// Write to the stream. + /// + /// the buffer from which to write + /// the offset at which to start writing + /// the number of bytes to write + public override void Write(byte[] buffer, int offset, int count) + { + if (count > 0) _Crc32.SlurpBlock(buffer, offset, count); + _innerStream.Write(buffer, offset, count); + } + + /// + /// Indicates whether the stream supports reading. + /// + public override bool CanRead + { + get { return _innerStream.CanRead; } + } + + /// + /// Indicates whether the stream supports seeking. + /// + /// + /// + /// Always returns false. + /// + /// + public override bool CanSeek + { + get { return false; } + } + + /// + /// Indicates whether the stream supports writing. + /// + public override bool CanWrite + { + get { return _innerStream.CanWrite; } + } + + /// + /// Flush the stream. + /// + public override void Flush() + { + _innerStream.Flush(); + } + + /// + /// Returns the length of the underlying stream. + /// + public override long Length + { + get + { + if (_lengthLimit == CrcCalculatorStream.UnsetLengthLimit) + return _innerStream.Length; + else return _lengthLimit; + } + } + + /// + /// The getter for this property returns the total bytes read. + /// If you use the setter, it will throw + /// . + /// + public override long Position + { + get { return _Crc32.TotalBytesRead; } + set { throw new NotSupportedException(); } + } + + /// + /// Seeking is not supported on this stream. This method always throws + /// + /// + /// N/A + /// N/A + /// N/A + public override long Seek(long offset, System.IO.SeekOrigin origin) + { + throw new NotSupportedException(); + } + + /// + /// This method always throws + /// + /// + /// N/A + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + + void IDisposable.Dispose() + { + Close(); + } + + /// + /// Closes the stream. + /// + public override void Close() + { + base.Close(); + if (!_leaveOpen) + _innerStream.Close(); + } + + } + +} \ No newline at end of file diff --git a/DotNetZip/CommonSrc/Iso8859Dash1Encoding.cs b/DotNetZip/CommonSrc/Iso8859Dash1Encoding.cs new file mode 100644 index 0000000..b182de8 --- /dev/null +++ b/DotNetZip/CommonSrc/Iso8859Dash1Encoding.cs @@ -0,0 +1,183 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Ionic.Encoding +{ + /// + /// Provides a text encoder for the iso-8859-1 encoding, aka Latin1 encoding, + /// for platforms that do not support it, for example on Silverlight or some + /// Compact Framework platforms. + /// + public class Iso8859Dash1Encoding : System.Text.Encoding + { + /// + /// Gets the name registered with the + /// Internet Assigned Numbers Authority (IANA) for the current encoding. + /// + /// + /// Always returns "iso-8859-1". + /// + public override string WebName + { + get { return "iso-8859-1"; } + } + + /// + /// Encodes a set of characters from a character array into + /// a byte array. + /// + /// + /// The actual number of bytes written into . + /// + /// The character array containing the set of characters to encode. + /// The index of the first character to encode. + /// The number of characters to encode. + /// The byte array to contain the resulting sequence of bytes. + /// The index at which to start writing the resulting sequence of bytes. + /// + public override int GetBytes(char[] chars, int start, int count, byte[] bytes, int byteIndex) + { + if (chars == null) + throw new ArgumentNullException("chars", "null array"); + + if (bytes == null) + throw new ArgumentNullException("bytes", "null array"); + + if (start < 0) + throw new ArgumentOutOfRangeException("start"); + if (count < 0) + throw new ArgumentOutOfRangeException("charCount"); + + if ((chars.Length - start) < count) + throw new ArgumentOutOfRangeException("chars"); + + if ((byteIndex < 0) || (byteIndex > bytes.Length)) + throw new ArgumentOutOfRangeException("byteIndex"); + + // iso-8859-1 is special in that it was adopted as the first page of + // UCS - ISO's Universal Coding Standard, described in ISO 10646, + // which is the same as Unicode. This means that a a Unicode + // character in the range of 0 to FF maps to the iso-8859-1 character + // with the same value. Because of that the encoding and decoding is + // trivial. + for (int i=0; i < count; i++) + { + char c = chars[start+i]; // get the unicode char + + if (c >= '\x00FF') // out of range? + bytes[byteIndex+i] = (byte) '?'; + else + bytes[byteIndex+i] = (byte) c; + } + return count; + } + + + /// + /// Decodes a sequence of bytes from the specified byte array into the specified character array. + /// + /// + /// The actual number of characters written into . + /// + /// The byte array containing the sequence of bytes to decode. + /// The index of the first byte to decode. + /// The number of bytes to decode. + /// The character array to contain the resulting set of characters. + /// The index at which to start writing the resulting set of characters. + /// + public override int GetChars(byte[] bytes, int start, int count, char[] chars, int charIndex) + { + if (chars == null) + throw new ArgumentNullException("chars", "null array"); + + if (bytes == null) + throw new ArgumentNullException("bytes", "null array"); + + if (start < 0) + throw new ArgumentOutOfRangeException("start"); + if (count < 0) + throw new ArgumentOutOfRangeException("charCount"); + + if ((bytes.Length - start) < count) + throw new ArgumentOutOfRangeException("bytes"); + + if ((charIndex < 0) || (charIndex > chars.Length)) + throw new ArgumentOutOfRangeException("charIndex"); + + // In the range 00 to FF, the Unicode characters are the same as the + // iso-8859-1 characters; because of that, decoding is trivial. + for (int i = 0; i < count; i++) + chars[charIndex + i] = (char) bytes[i + start]; + + return count; + } + + + /// + /// Calculates the number of bytes produced by encoding a set of characters + /// from the specified character array. + /// + /// + /// The number of bytes produced by encoding the specified characters. This class + /// alwas returns the value of . + /// + public override int GetByteCount(char[] chars, int index, int count) + { + return count; + } + + + /// + /// Calculates the number of characters produced by decoding a sequence + /// of bytes from the specified byte array. + /// + /// + /// The number of characters produced by decoding the specified sequence of bytes. This class + /// alwas returns the value of . + /// + public override int GetCharCount(byte[] bytes, int index, int count) + { + return count; + } + + + /// + /// Calculates the maximum number of bytes produced by encoding the specified number of characters. + /// + /// + /// The maximum number of bytes produced by encoding the specified number of characters. This + /// class alwas returns the value of . + /// + /// The number of characters to encode. + /// + public override int GetMaxByteCount(int charCount) + { + return charCount; + } + + /// + /// Calculates the maximum number of characters produced by decoding the specified number of bytes. + /// + /// + /// The maximum number of characters produced by decoding the specified number of bytes. This class + /// alwas returns the value of . + /// + /// The number of bytes to decode. + public override int GetMaxCharCount(int byteCount) + { + return byteCount; + } + + /// + /// Gets the number of characters that are supported by this encoding. + /// This property returns a maximum value of 256, as the encoding class + /// only supports single byte encodings (1 byte == 256 possible values). + /// + public static int CharacterCount + { + get { return 256; } + } + + } +} diff --git a/DotNetZip/SolutionInfo.cs b/DotNetZip/SolutionInfo.cs new file mode 100644 index 0000000..4f39da0 Binary files /dev/null and b/DotNetZip/SolutionInfo.cs differ diff --git a/DotNetZip/Zip/ComHelper.cs b/DotNetZip/Zip/ComHelper.cs new file mode 100644 index 0000000..385cef2 --- /dev/null +++ b/DotNetZip/Zip/ComHelper.cs @@ -0,0 +1,116 @@ +// ComHelper.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2009 Dino Chiesa. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2011-June-13 17:04:06> +// +// ------------------------------------------------------------------ +// +// This module defines a COM Helper class. +// +// Created: Tue, 08 Sep 2009 22:03 +// + +using Interop=System.Runtime.InteropServices; + +namespace Ionic.Zip +{ + /// + /// This class exposes a set of COM-accessible wrappers for static + /// methods available on the ZipFile class. You don't need this + /// class unless you are using DotNetZip from a COM environment. + /// + [System.Runtime.InteropServices.GuidAttribute("ebc25cf6-9120-4283-b972-0e5520d0000F")] + [System.Runtime.InteropServices.ComVisible(true)] +#if !NETCF + [System.Runtime.InteropServices.ClassInterface(System.Runtime.InteropServices.ClassInterfaceType.AutoDispatch)] +#endif + + public class ComHelper + { + /// + /// A wrapper for ZipFile.IsZipFile(string) + /// + /// The filename to of the zip file to check. + /// true if the file contains a valid zip file. + public bool IsZipFile(string filename) + { + return ZipFile.IsZipFile(filename); + } + + /// + /// A wrapper for ZipFile.IsZipFile(string, bool) + /// + /// + /// We cannot use "overloaded" Method names in COM interop. + /// So, here, we use a unique name. + /// + /// The filename to of the zip file to check. + /// true if the file contains a valid zip file. + public bool IsZipFileWithExtract(string filename) + { + return ZipFile.IsZipFile(filename, true); + } + +#if !NETCF + /// + /// A wrapper for ZipFile.CheckZip(string) + /// + /// The filename to of the zip file to check. + /// + /// true if the named zip file checks OK. Otherwise, false. + public bool CheckZip(string filename) + { + return ZipFile.CheckZip(filename); + } + + /// + /// A COM-friendly wrapper for the static method . + /// + /// + /// The filename to of the zip file to check. + /// + /// The password to check. + /// + /// true if the named zip file checks OK. Otherwise, false. + public bool CheckZipPassword(string filename, string password) + { + return ZipFile.CheckZipPassword(filename, password); + } + + /// + /// A wrapper for ZipFile.FixZipDirectory(string) + /// + /// The filename to of the zip file to fix. + public void FixZipDirectory(string filename) + { + ZipFile.FixZipDirectory(filename); + } +#endif + + /// + /// A wrapper for ZipFile.LibraryVersion + /// + /// + /// the version number on the DotNetZip assembly, formatted as a string. + /// + public string GetZipLibraryVersion() + { + return ZipFile.LibraryVersion.ToString(); + } + + } +} \ No newline at end of file diff --git a/DotNetZip/Zip/EncryptionAlgorithm.cs b/DotNetZip/Zip/EncryptionAlgorithm.cs new file mode 100644 index 0000000..66b4f40 --- /dev/null +++ b/DotNetZip/Zip/EncryptionAlgorithm.cs @@ -0,0 +1,135 @@ +// EncryptionAlgorithm.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2009 Dino Chiesa +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2009-October-21 17:24:45> +// +// ------------------------------------------------------------------ +// +// This module defines the EncryptionAgorithm enum +// +// +// ------------------------------------------------------------------ + + +namespace Ionic.Zip +{ + /// + /// An enum that provides the various encryption algorithms supported by this + /// library. + /// + /// + /// + /// + /// + /// PkzipWeak implies the use of Zip 2.0 encryption, which is known to be + /// weak and subvertible. + /// + /// + /// + /// A note on interoperability: Values of PkzipWeak and None are + /// specified in PKWARE's zip + /// specification, and are considered to be "standard". Zip archives + /// produced using these options will be interoperable with many other zip tools + /// and libraries, including Windows Explorer. + /// + /// + /// + /// Values of WinZipAes128 and WinZipAes256 are not part of the Zip + /// specification, but rather imply the use of a vendor-specific extension from + /// WinZip. If you want to produce interoperable Zip archives, do not use these + /// values. For example, if you produce a zip archive using WinZipAes256, you + /// will be able to open it in Windows Explorer on Windows XP and Vista, but you + /// will not be able to extract entries; trying this will lead to an "unspecified + /// error". For this reason, some people have said that a zip archive that uses + /// WinZip's AES encryption is not actually a zip archive at all. A zip archive + /// produced this way will be readable with the WinZip tool (Version 11 and + /// beyond). + /// + /// + /// + /// There are other third-party tools and libraries, both commercial and + /// otherwise, that support WinZip's AES encryption. These will be able to read + /// AES-encrypted zip archives produced by DotNetZip, and conversely applications + /// that use DotNetZip to read zip archives will be able to read AES-encrypted + /// archives produced by those tools or libraries. Consult the documentation for + /// those other tools and libraries to find out if WinZip's AES encryption is + /// supported. + /// + /// + /// + /// In case you care: According to the WinZip specification, the + /// actual AES key used is derived from the via an + /// algorithm that complies with RFC 2898, using an iteration + /// count of 1000. The algorithm is sometimes referred to as PBKDF2, which stands + /// for "Password Based Key Derivation Function #2". + /// + /// + /// + /// A word about password strength and length: The AES encryption technology is + /// very good, but any system is only as secure as the weakest link. If you want + /// to secure your data, be sure to use a password that is hard to guess. To make + /// it harder to guess (increase its "entropy"), you should make it longer. If + /// you use normal characters from an ASCII keyboard, a password of length 20 will + /// be strong enough that it will be impossible to guess. For more information on + /// that, I'd encourage you to read this + /// article. + /// + /// + /// + /// The WinZip AES algorithms are not supported with the version of DotNetZip that + /// runs on the .NET Compact Framework. This is because .NET CF lacks the + /// HMACSHA1 class that is required for producing the archive. + /// + /// + public enum EncryptionAlgorithm + { + /// + /// No encryption at all. + /// + None = 0, + + /// + /// Traditional or Classic pkzip encryption. + /// + PkzipWeak, + +#if AESCRYPTO + /// + /// WinZip AES encryption (128 key bits). + /// + WinZipAes128, + + /// + /// WinZip AES encryption (256 key bits). + /// + WinZipAes256, +#endif + + /// + /// An encryption algorithm that is not supported by DotNetZip. + /// + Unsupported = 4, + + + // others... not implemented (yet?) + } + +} diff --git a/DotNetZip/Zip/Events.cs b/DotNetZip/Zip/Events.cs new file mode 100644 index 0000000..e51ff78 --- /dev/null +++ b/DotNetZip/Zip/Events.cs @@ -0,0 +1,684 @@ +// Events.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2006, 2007, 2008, 2009 Dino Chiesa and Microsoft Corporation. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2011-August-06 12:26:24> +// +// ------------------------------------------------------------------ +// +// This module defines events used by the ZipFile class. +// +// + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Ionic.Zip +{ + /// + /// Delegate in which the application writes the ZipEntry content for the named entry. + /// + /// + /// The name of the entry that must be written. + /// The stream to which the entry data should be written. + /// + /// + /// When you add an entry and specify a WriteDelegate, via , the application + /// code provides the logic that writes the entry data directly into the zip file. + /// + /// + /// + /// + /// This example shows how to define a WriteDelegate that obtains a DataSet, and then + /// writes the XML for the DataSet into the zip archive. There's no need to + /// save the XML to a disk file first. + /// + /// + /// private void WriteEntry (String filename, Stream output) + /// { + /// DataSet ds1 = ObtainDataSet(); + /// ds1.WriteXml(output); + /// } + /// + /// private void Run() + /// { + /// using (var zip = new ZipFile()) + /// { + /// zip.AddEntry(zipEntryName, WriteEntry); + /// zip.Save(zipFileName); + /// } + /// } + /// + /// + /// + /// Private Sub WriteEntry (ByVal filename As String, ByVal output As Stream) + /// DataSet ds1 = ObtainDataSet() + /// ds1.WriteXml(stream) + /// End Sub + /// + /// Public Sub Run() + /// Using zip = New ZipFile + /// zip.AddEntry(zipEntryName, New WriteDelegate(AddressOf WriteEntry)) + /// zip.Save(zipFileName) + /// End Using + /// End Sub + /// + /// + /// + public delegate void WriteDelegate(string entryName, System.IO.Stream stream); + + + /// + /// Delegate in which the application opens the stream, just-in-time, for the named entry. + /// + /// + /// + /// The name of the ZipEntry that the application should open the stream for. + /// + /// + /// + /// When you add an entry via , the application code provides the logic that + /// opens and closes the stream for the given ZipEntry. + /// + /// + /// + public delegate System.IO.Stream OpenDelegate(string entryName); + + /// + /// Delegate in which the application closes the stream, just-in-time, for the named entry. + /// + /// + /// + /// The name of the ZipEntry that the application should close the stream for. + /// + /// + /// The stream to be closed. + /// + /// + /// When you add an entry via , the application code provides the logic that + /// opens and closes the stream for the given ZipEntry. + /// + /// + /// + public delegate void CloseDelegate(string entryName, System.IO.Stream stream); + + /// + /// Delegate for the callback by which the application tells the + /// library the CompressionLevel to use for a file. + /// + /// + /// + /// + /// Using this callback, the application can, for example, specify that + /// previously-compressed files (.mp3, .png, .docx, etc) should use a + /// CompressionLevel of None, or can set the compression level based + /// on any other factor. + /// + /// + /// + public delegate Ionic.Zlib.CompressionLevel SetCompressionCallback(string localFileName, string fileNameInArchive); + + /// + /// In an EventArgs type, indicates which sort of progress event is being + /// reported. + /// + /// + /// There are events for reading, events for saving, and events for + /// extracting. This enumeration allows a single EventArgs type to be sued to + /// describe one of multiple subevents. For example, a SaveProgress event is + /// invoked before, after, and during the saving of a single entry. The value + /// of an enum with this type, specifies which event is being triggered. The + /// same applies to Extraction, Reading and Adding events. + /// + public enum ZipProgressEventType + { + /// + /// Indicates that a Add() operation has started. + /// + Adding_Started, + + /// + /// Indicates that an individual entry in the archive has been added. + /// + Adding_AfterAddEntry, + + /// + /// Indicates that a Add() operation has completed. + /// + Adding_Completed, + + /// + /// Indicates that a Read() operation has started. + /// + Reading_Started, + + /// + /// Indicates that an individual entry in the archive is about to be read. + /// + Reading_BeforeReadEntry, + + /// + /// Indicates that an individual entry in the archive has just been read. + /// + Reading_AfterReadEntry, + + /// + /// Indicates that a Read() operation has completed. + /// + Reading_Completed, + + /// + /// The given event reports the number of bytes read so far + /// during a Read() operation. + /// + Reading_ArchiveBytesRead, + + /// + /// Indicates that a Save() operation has started. + /// + Saving_Started, + + /// + /// Indicates that an individual entry in the archive is about to be written. + /// + Saving_BeforeWriteEntry, + + /// + /// Indicates that an individual entry in the archive has just been saved. + /// + Saving_AfterWriteEntry, + + /// + /// Indicates that a Save() operation has completed. + /// + Saving_Completed, + + /// + /// Indicates that the zip archive has been created in a + /// temporary location during a Save() operation. + /// + Saving_AfterSaveTempArchive, + + /// + /// Indicates that the temporary file is about to be renamed to the final archive + /// name during a Save() operation. + /// + Saving_BeforeRenameTempArchive, + + /// + /// Indicates that the temporary file is has just been renamed to the final archive + /// name during a Save() operation. + /// + Saving_AfterRenameTempArchive, + + /// + /// Indicates that the self-extracting archive has been compiled + /// during a Save() operation. + /// + Saving_AfterCompileSelfExtractor, + + /// + /// The given event is reporting the number of source bytes that have run through the compressor so far + /// during a Save() operation. + /// + Saving_EntryBytesRead, + + /// + /// Indicates that an entry is about to be extracted. + /// + Extracting_BeforeExtractEntry, + + /// + /// Indicates that an entry has just been extracted. + /// + Extracting_AfterExtractEntry, + + /// + /// Indicates that extraction of an entry would overwrite an existing + /// filesystem file. You must use + /// + /// ExtractExistingFileAction.InvokeExtractProgressEvent in the call + /// to ZipEntry.Extract() in order to receive this event. + /// + Extracting_ExtractEntryWouldOverwrite, + + /// + /// The given event is reporting the number of bytes written so far for + /// the current entry during an Extract() operation. + /// + Extracting_EntryBytesWritten, + + /// + /// Indicates that an ExtractAll operation is about to begin. + /// + Extracting_BeforeExtractAll, + + /// + /// Indicates that an ExtractAll operation has completed. + /// + Extracting_AfterExtractAll, + + /// + /// Indicates that an error has occurred while saving a zip file. + /// This generally means the file cannot be opened, because it has been + /// removed, or because it is locked by another process. It can also + /// mean that the file cannot be Read, because of a range lock conflict. + /// + Error_Saving, + } + + + /// + /// Provides information about the progress of a save, read, or extract operation. + /// This is a base class; you will probably use one of the classes derived from this one. + /// + public class ZipProgressEventArgs : EventArgs + { + private int _entriesTotal; + private bool _cancel; + private ZipEntry _latestEntry; + private ZipProgressEventType _flavor; + private String _archiveName; + private Int64 _bytesTransferred; + private Int64 _totalBytesToTransfer; + + + internal ZipProgressEventArgs() { } + + internal ZipProgressEventArgs(string archiveName, ZipProgressEventType flavor) + { + this._archiveName = archiveName; + this._flavor = flavor; + } + + /// + /// The total number of entries to be saved or extracted. + /// + public int EntriesTotal + { + get { return _entriesTotal; } + set { _entriesTotal = value; } + } + + /// + /// The name of the last entry saved or extracted. + /// + public ZipEntry CurrentEntry + { + get { return _latestEntry; } + set { _latestEntry = value; } + } + + /// + /// In an event handler, set this to cancel the save or extract + /// operation that is in progress. + /// + public bool Cancel + { + get { return _cancel; } + set { _cancel = _cancel || value; } + } + + /// + /// The type of event being reported. + /// + public ZipProgressEventType EventType + { + get { return _flavor; } + set { _flavor = value; } + } + + /// + /// Returns the archive name associated to this event. + /// + public String ArchiveName + { + get { return _archiveName; } + set { _archiveName = value; } + } + + + /// + /// The number of bytes read or written so far for this entry. + /// + public Int64 BytesTransferred + { + get { return _bytesTransferred; } + set { _bytesTransferred = value; } + } + + + + /// + /// Total number of bytes that will be read or written for this entry. + /// This number will be -1 if the value cannot be determined. + /// + public Int64 TotalBytesToTransfer + { + get { return _totalBytesToTransfer; } + set { _totalBytesToTransfer = value; } + } + } + + + + /// + /// Provides information about the progress of a Read operation. + /// + public class ReadProgressEventArgs : ZipProgressEventArgs + { + + internal ReadProgressEventArgs() { } + + private ReadProgressEventArgs(string archiveName, ZipProgressEventType flavor) + : base(archiveName, flavor) + { } + + internal static ReadProgressEventArgs Before(string archiveName, int entriesTotal) + { + var x = new ReadProgressEventArgs(archiveName, ZipProgressEventType.Reading_BeforeReadEntry); + x.EntriesTotal = entriesTotal; + return x; + } + + internal static ReadProgressEventArgs After(string archiveName, ZipEntry entry, int entriesTotal) + { + var x = new ReadProgressEventArgs(archiveName, ZipProgressEventType.Reading_AfterReadEntry); + x.EntriesTotal = entriesTotal; + x.CurrentEntry = entry; + return x; + } + + internal static ReadProgressEventArgs Started(string archiveName) + { + var x = new ReadProgressEventArgs(archiveName, ZipProgressEventType.Reading_Started); + return x; + } + + internal static ReadProgressEventArgs ByteUpdate(string archiveName, ZipEntry entry, Int64 bytesXferred, Int64 totalBytes) + { + var x = new ReadProgressEventArgs(archiveName, ZipProgressEventType.Reading_ArchiveBytesRead); + x.CurrentEntry = entry; + x.BytesTransferred = bytesXferred; + x.TotalBytesToTransfer = totalBytes; + return x; + } + + internal static ReadProgressEventArgs Completed(string archiveName) + { + var x = new ReadProgressEventArgs(archiveName, ZipProgressEventType.Reading_Completed); + return x; + } + + } + + + /// + /// Provides information about the progress of a Add operation. + /// + public class AddProgressEventArgs : ZipProgressEventArgs + { + internal AddProgressEventArgs() { } + + private AddProgressEventArgs(string archiveName, ZipProgressEventType flavor) + : base(archiveName, flavor) + { } + + internal static AddProgressEventArgs AfterEntry(string archiveName, ZipEntry entry, int entriesTotal) + { + var x = new AddProgressEventArgs(archiveName, ZipProgressEventType.Adding_AfterAddEntry); + x.EntriesTotal = entriesTotal; + x.CurrentEntry = entry; + return x; + } + + internal static AddProgressEventArgs Started(string archiveName) + { + var x = new AddProgressEventArgs(archiveName, ZipProgressEventType.Adding_Started); + return x; + } + + internal static AddProgressEventArgs Completed(string archiveName) + { + var x = new AddProgressEventArgs(archiveName, ZipProgressEventType.Adding_Completed); + return x; + } + + } + + /// + /// Provides information about the progress of a save operation. + /// + public class SaveProgressEventArgs : ZipProgressEventArgs + { + private int _entriesSaved; + + /// + /// Constructor for the SaveProgressEventArgs. + /// + /// the name of the zip archive. + /// whether this is before saving the entry, or after + /// The total number of entries in the zip archive. + /// Number of entries that have been saved. + /// The entry involved in the event. + internal SaveProgressEventArgs(string archiveName, bool before, int entriesTotal, int entriesSaved, ZipEntry entry) + : base(archiveName, (before) ? ZipProgressEventType.Saving_BeforeWriteEntry : ZipProgressEventType.Saving_AfterWriteEntry) + { + this.EntriesTotal = entriesTotal; + this.CurrentEntry = entry; + this._entriesSaved = entriesSaved; + } + + internal SaveProgressEventArgs() { } + + internal SaveProgressEventArgs(string archiveName, ZipProgressEventType flavor) + : base(archiveName, flavor) + { } + + + internal static SaveProgressEventArgs ByteUpdate(string archiveName, ZipEntry entry, Int64 bytesXferred, Int64 totalBytes) + { + var x = new SaveProgressEventArgs(archiveName, ZipProgressEventType.Saving_EntryBytesRead); + x.ArchiveName = archiveName; + x.CurrentEntry = entry; + x.BytesTransferred = bytesXferred; + x.TotalBytesToTransfer = totalBytes; + return x; + } + + internal static SaveProgressEventArgs Started(string archiveName) + { + var x = new SaveProgressEventArgs(archiveName, ZipProgressEventType.Saving_Started); + return x; + } + + internal static SaveProgressEventArgs Completed(string archiveName) + { + var x = new SaveProgressEventArgs(archiveName, ZipProgressEventType.Saving_Completed); + return x; + } + + /// + /// Number of entries saved so far. + /// + public int EntriesSaved + { + get { return _entriesSaved; } + } + } + + + /// + /// Provides information about the progress of the extract operation. + /// + public class ExtractProgressEventArgs : ZipProgressEventArgs + { + private int _entriesExtracted; + private string _target; + + /// + /// Constructor for the ExtractProgressEventArgs. + /// + /// the name of the zip archive. + /// whether this is before saving the entry, or after + /// The total number of entries in the zip archive. + /// Number of entries that have been extracted. + /// The entry involved in the event. + /// The location to which entries are extracted. + internal ExtractProgressEventArgs(string archiveName, bool before, int entriesTotal, int entriesExtracted, ZipEntry entry, string extractLocation) + : base(archiveName, (before) ? ZipProgressEventType.Extracting_BeforeExtractEntry : ZipProgressEventType.Extracting_AfterExtractEntry) + { + this.EntriesTotal = entriesTotal; + this.CurrentEntry = entry; + this._entriesExtracted = entriesExtracted; + this._target = extractLocation; + } + + internal ExtractProgressEventArgs(string archiveName, ZipProgressEventType flavor) + : base(archiveName, flavor) + { } + + internal ExtractProgressEventArgs() + { } + + + internal static ExtractProgressEventArgs BeforeExtractEntry(string archiveName, ZipEntry entry, string extractLocation) + { + var x = new ExtractProgressEventArgs + { + ArchiveName = archiveName, + EventType = ZipProgressEventType.Extracting_BeforeExtractEntry, + CurrentEntry = entry, + _target = extractLocation, + }; + return x; + } + + internal static ExtractProgressEventArgs ExtractExisting(string archiveName, ZipEntry entry, string extractLocation) + { + var x = new ExtractProgressEventArgs + { + ArchiveName = archiveName, + EventType = ZipProgressEventType.Extracting_ExtractEntryWouldOverwrite, + CurrentEntry = entry, + _target = extractLocation, + }; + return x; + } + + internal static ExtractProgressEventArgs AfterExtractEntry(string archiveName, ZipEntry entry, string extractLocation) + { + var x = new ExtractProgressEventArgs + { + ArchiveName = archiveName, + EventType = ZipProgressEventType.Extracting_AfterExtractEntry, + CurrentEntry = entry, + _target = extractLocation, + }; + return x; + } + + internal static ExtractProgressEventArgs ExtractAllStarted(string archiveName, string extractLocation) + { + var x = new ExtractProgressEventArgs(archiveName, ZipProgressEventType.Extracting_BeforeExtractAll); + x._target = extractLocation; + return x; + } + + internal static ExtractProgressEventArgs ExtractAllCompleted(string archiveName, string extractLocation) + { + var x = new ExtractProgressEventArgs(archiveName, ZipProgressEventType.Extracting_AfterExtractAll); + x._target = extractLocation; + return x; + } + + + internal static ExtractProgressEventArgs ByteUpdate(string archiveName, ZipEntry entry, Int64 bytesWritten, Int64 totalBytes) + { + var x = new ExtractProgressEventArgs(archiveName, ZipProgressEventType.Extracting_EntryBytesWritten); + x.ArchiveName = archiveName; + x.CurrentEntry = entry; + x.BytesTransferred = bytesWritten; + x.TotalBytesToTransfer = totalBytes; + return x; + } + + + + /// + /// Number of entries extracted so far. This is set only if the + /// EventType is Extracting_BeforeExtractEntry or Extracting_AfterExtractEntry, and + /// the Extract() is occurring witin the scope of a call to ExtractAll(). + /// + public int EntriesExtracted + { + get { return _entriesExtracted; } + } + + /// + /// Returns the extraction target location, a filesystem path. + /// + public String ExtractLocation + { + get { return _target; } + } + + } + + + + /// + /// Provides information about the an error that occurred while zipping. + /// + public class ZipErrorEventArgs : ZipProgressEventArgs + { + private Exception _exc; + private ZipErrorEventArgs() { } + internal static ZipErrorEventArgs Saving(string archiveName, ZipEntry entry, Exception exception) + { + var x = new ZipErrorEventArgs + { + EventType = ZipProgressEventType.Error_Saving, + ArchiveName = archiveName, + CurrentEntry = entry, + _exc = exception + }; + return x; + } + + /// + /// Returns the exception that occurred, if any. + /// + public Exception @Exception + { + get { return _exc; } + } + + /// + /// Returns the name of the file that caused the exception, if any. + /// + public String FileName + { + get { return CurrentEntry.LocalFileName; } + } + } + + +} diff --git a/DotNetZip/Zip/Exceptions.cs b/DotNetZip/Zip/Exceptions.cs new file mode 100644 index 0000000..9f67077 --- /dev/null +++ b/DotNetZip/Zip/Exceptions.cs @@ -0,0 +1,300 @@ +// Exceptions.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2008, 2009 Dino Chiesa and Microsoft Corporation. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2011-July-12 12:19:10> +// +// ------------------------------------------------------------------ +// +// This module defines exceptions used in the class library. +// + + + +using System; +using System.Collections.Generic; +using System.Text; +#if !NETCF +using System.Runtime.Serialization; +#endif + +namespace Ionic.Zip +{ + ///// + ///// Base exception type for all custom exceptions in the Zip library. It acts as a marker class. + ///// + //[AttributeUsage(AttributeTargets.Class)] + //public class ZipExceptionAttribute : Attribute { } + + + + /// + /// Issued when an ZipEntry.ExtractWithPassword() method is invoked + /// with an incorrect password. + /// +#if !SILVERLIGHT + [Serializable] +#endif + [System.Runtime.InteropServices.GuidAttribute("ebc25cf6-9120-4283-b972-0e5520d0000B")] + public class BadPasswordException : ZipException + { + /// + /// Default ctor. + /// + public BadPasswordException() { } + + /// + /// Come on, you know how exceptions work. Why are you looking at this documentation? + /// + /// The message in the exception. + public BadPasswordException(String message) + : base(message) + { } + + /// + /// Come on, you know how exceptions work. Why are you looking at this documentation? + /// + /// The message in the exception. + /// The innerException for this exception. + public BadPasswordException(String message, Exception innerException) + : base(message, innerException) + { + } + + +#if ! (NETCF || SILVERLIGHT) + /// + /// Come on, you know how exceptions work. Why are you looking at this documentation? + /// + /// The serialization info for the exception. + /// The streaming context from which to deserialize. + protected BadPasswordException(SerializationInfo info, StreamingContext context) + : base(info, context) + { } +#endif + + } + + /// + /// Indicates that a read was attempted on a stream, and bad or incomplete data was + /// received. + /// +#if !SILVERLIGHT + [Serializable] +#endif + [System.Runtime.InteropServices.GuidAttribute("ebc25cf6-9120-4283-b972-0e5520d0000A")] + public class BadReadException : ZipException + { + /// + /// Default ctor. + /// + public BadReadException() { } + + /// + /// Come on, you know how exceptions work. Why are you looking at this documentation? + /// + /// The message in the exception. + public BadReadException(String message) + : base(message) + { } + + /// + /// Come on, you know how exceptions work. Why are you looking at this documentation? + /// + /// The message in the exception. + /// The innerException for this exception. + public BadReadException(String message, Exception innerException) + : base(message, innerException) + { + } + +#if ! (NETCF || SILVERLIGHT) + /// + /// Come on, you know how exceptions work. Why are you looking at this documentation? + /// + /// The serialization info for the exception. + /// The streaming context from which to deserialize. + protected BadReadException(SerializationInfo info, StreamingContext context) + : base(info, context) + { } +#endif + + } + + + + /// + /// Issued when an CRC check fails upon extracting an entry from a zip archive. + /// +#if !SILVERLIGHT + [Serializable] +#endif + [System.Runtime.InteropServices.GuidAttribute("ebc25cf6-9120-4283-b972-0e5520d00009")] + public class BadCrcException : ZipException + { + /// + /// Default ctor. + /// + public BadCrcException() { } + + /// + /// Come on, you know how exceptions work. Why are you looking at this documentation? + /// + /// The message in the exception. + public BadCrcException(String message) + : base(message) + { } + + +#if ! (NETCF || SILVERLIGHT) + /// + /// Come on, you know how exceptions work. Why are you looking at this documentation? + /// + /// The serialization info for the exception. + /// The streaming context from which to deserialize. + protected BadCrcException(SerializationInfo info, StreamingContext context) + : base(info, context) + { } +#endif + + } + + + /// + /// Issued when errors occur saving a self-extracting archive. + /// +#if !SILVERLIGHT + [Serializable] +#endif + [System.Runtime.InteropServices.GuidAttribute("ebc25cf6-9120-4283-b972-0e5520d00008")] + public class SfxGenerationException : ZipException + { + /// + /// Default ctor. + /// + public SfxGenerationException() { } + + /// + /// Come on, you know how exceptions work. Why are you looking at this documentation? + /// + /// The message in the exception. + public SfxGenerationException(String message) + : base(message) + { } + +#if ! (NETCF || SILVERLIGHT) + /// + /// Come on, you know how exceptions work. Why are you looking at this documentation? + /// + /// The serialization info for the exception. + /// The streaming context from which to deserialize. + protected SfxGenerationException(SerializationInfo info, StreamingContext context) + : base(info, context) + { } +#endif + + } + + + /// + /// Indicates that an operation was attempted on a ZipFile which was not possible + /// given the state of the instance. For example, if you call Save() on a ZipFile + /// which has no filename set, you can get this exception. + /// +#if !SILVERLIGHT + [Serializable] +#endif + [System.Runtime.InteropServices.GuidAttribute("ebc25cf6-9120-4283-b972-0e5520d00007")] + public class BadStateException : ZipException + { + /// + /// Default ctor. + /// + public BadStateException() { } + + /// + /// Come on, you know how exceptions work. Why are you looking at this documentation? + /// + /// The message in the exception. + public BadStateException(String message) + : base(message) + { } + + /// + /// Come on, you know how exceptions work. Why are you looking at this documentation? + /// + /// The message in the exception. + /// The innerException for this exception. + public BadStateException(String message, Exception innerException) + : base(message, innerException) + {} + +#if ! (NETCF || SILVERLIGHT) + /// + /// Come on, you know how exceptions work. Why are you looking at this documentation? + /// + /// The serialization info for the exception. + /// The streaming context from which to deserialize. + protected BadStateException(SerializationInfo info, StreamingContext context) + : base(info, context) + { } +#endif + + } + + /// + /// Base class for all exceptions defined by and throw by the Zip library. + /// +#if !SILVERLIGHT + [Serializable] +#endif + [System.Runtime.InteropServices.GuidAttribute("ebc25cf6-9120-4283-b972-0e5520d00006")] + public class ZipException : Exception + { + /// + /// Default ctor. + /// + public ZipException() { } + + /// + /// Come on, you know how exceptions work. Why are you looking at this documentation? + /// + /// The message in the exception. + public ZipException(String message) : base(message) { } + + /// + /// Come on, you know how exceptions work. Why are you looking at this documentation? + /// + /// The message in the exception. + /// The innerException for this exception. + public ZipException(String message, Exception innerException) + : base(message, innerException) + { } + +#if ! (NETCF || SILVERLIGHT) + /// + /// Come on, you know how exceptions work. Why are you looking at this documentation? + /// + /// The serialization info for the exception. + /// The streaming context from which to deserialize. + protected ZipException(SerializationInfo info, StreamingContext context) + : base(info, context) + { } +#endif + + } + +} diff --git a/DotNetZip/Zip/ExtractExistingFileAction.cs b/DotNetZip/Zip/ExtractExistingFileAction.cs new file mode 100644 index 0000000..2de229f --- /dev/null +++ b/DotNetZip/Zip/ExtractExistingFileAction.cs @@ -0,0 +1,85 @@ +// ExtractExistingFileAction.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2009 Dino Chiesa +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2009-August-25 08:44:37> +// +// ------------------------------------------------------------------ +// +// This module defines the ExtractExistingFileAction enum +// +// +// ------------------------------------------------------------------ + + +namespace Ionic.Zip +{ + + /// + /// An enum for the options when extracting an entry would overwrite an existing file. + /// + /// + /// + /// + /// This enum describes the actions that the library can take when an + /// Extract() or ExtractWithPassword() method is called to extract an + /// entry to a filesystem, and the extraction would overwrite an existing filesystem + /// file. + /// + /// + /// + public enum ExtractExistingFileAction + { + /// + /// Throw an exception when extraction would overwrite an existing file. (For + /// COM clients, this is a 0 (zero).) + /// + Throw, + + /// + /// When extraction would overwrite an existing file, overwrite the file silently. + /// The overwrite will happen even if the target file is marked as read-only. + /// (For COM clients, this is a 1.) + /// + OverwriteSilently, + + /// + /// When extraction would overwrite an existing file, don't overwrite the file, silently. + /// (For COM clients, this is a 2.) + /// + DoNotOverwrite, + + /// + /// When extraction would overwrite an existing file, invoke the ExtractProgress + /// event, using an event type of . In + /// this way, the application can decide, just-in-time, whether to overwrite the + /// file. For example, a GUI application may wish to pop up a dialog to allow + /// the user to choose. You may want to examine the property before making + /// the decision. If, after your processing in the Extract progress event, you + /// want to NOT extract the file, set + /// on the ZipProgressEventArgs.CurrentEntry to DoNotOverwrite. + /// If you do want to extract the file, set ZipEntry.ExtractExistingFile + /// to OverwriteSilently. If you want to cancel the Extraction, set + /// ZipProgressEventArgs.Cancel to true. Cancelling differs from using + /// DoNotOverwrite in that a cancel will not extract any further entries, if + /// there are any. (For COM clients, the value of this enum is a 3.) + /// + InvokeExtractProgressEvent, + } + +} diff --git a/DotNetZip/Zip/FileSelector.cs b/DotNetZip/Zip/FileSelector.cs new file mode 100644 index 0000000..874eb1b --- /dev/null +++ b/DotNetZip/Zip/FileSelector.cs @@ -0,0 +1,1608 @@ +//#define SelectorTrace + +// FileSelector.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2008-2011 Dino Chiesa. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved: <2011-August-05 11:03:11> +// +// ------------------------------------------------------------------ +// +// This module implements a "file selector" that finds files based on a +// set of inclusion criteria, including filename, size, file time, and +// potentially file attributes. The criteria are given in a string with +// a simple expression language. Examples: +// +// find all .txt files: +// name = *.txt +// +// shorthand for the above +// *.txt +// +// all files modified after January 1st, 2009 +// mtime > 2009-01-01 +// +// All .txt files modified after the first of the year +// name = *.txt AND mtime > 2009-01-01 +// +// All .txt files modified after the first of the year, or any file with the archive bit set +// (name = *.txt AND mtime > 2009-01-01) or (attribtues = A) +// +// All .txt files or any file greater than 1mb in size +// (name = *.txt or size > 1mb) +// +// and so on. +// ------------------------------------------------------------------ + + +using System; +using System.IO; +using System.Text; +using System.Reflection; +using System.ComponentModel; +using System.Text.RegularExpressions; +using System.Collections.Generic; +#if SILVERLIGHT +using System.Linq; +#endif + +namespace Ionic +{ + + /// + /// Enumerates the options for a logical conjunction. This enum is intended for use + /// internally by the FileSelector class. + /// + internal enum LogicalConjunction + { + NONE, + AND, + OR, + XOR, + } + + internal enum WhichTime + { + atime, + mtime, + ctime, + } + + + internal enum ComparisonOperator + { + [Description(">")] + GreaterThan, + [Description(">=")] + GreaterThanOrEqualTo, + [Description("<")] + LesserThan, + [Description("<=")] + LesserThanOrEqualTo, + [Description("=")] + EqualTo, + [Description("!=")] + NotEqualTo + } + + + internal abstract partial class SelectionCriterion + { + internal virtual bool Verbose + { + get;set; + } + internal abstract bool Evaluate(string filename); + + [System.Diagnostics.Conditional("SelectorTrace")] + protected static void CriterionTrace(string format, params object[] args) + { + //System.Console.WriteLine(" " + format, args); + } + } + + + internal partial class SizeCriterion : SelectionCriterion + { + internal ComparisonOperator Operator; + internal Int64 Size; + + public override String ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("size ").Append(EnumUtil.GetDescription(Operator)).Append(" ").Append(Size.ToString()); + return sb.ToString(); + } + + internal override bool Evaluate(string filename) + { + System.IO.FileInfo fi = new System.IO.FileInfo(filename); + CriterionTrace("SizeCriterion::Evaluate('{0}' [{1}])", + filename, this.ToString()); + return _Evaluate(fi.Length); + } + + private bool _Evaluate(Int64 Length) + { + bool result = false; + switch (Operator) + { + case ComparisonOperator.GreaterThanOrEqualTo: + result = Length >= Size; + break; + case ComparisonOperator.GreaterThan: + result = Length > Size; + break; + case ComparisonOperator.LesserThanOrEqualTo: + result = Length <= Size; + break; + case ComparisonOperator.LesserThan: + result = Length < Size; + break; + case ComparisonOperator.EqualTo: + result = Length == Size; + break; + case ComparisonOperator.NotEqualTo: + result = Length != Size; + break; + default: + throw new ArgumentException("Operator"); + } + return result; + } + + } + + + + internal partial class TimeCriterion : SelectionCriterion + { + internal ComparisonOperator Operator; + internal WhichTime Which; + internal DateTime Time; + + public override String ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append(Which.ToString()).Append(" ").Append(EnumUtil.GetDescription(Operator)).Append(" ").Append(Time.ToString("yyyy-MM-dd-HH:mm:ss")); + return sb.ToString(); + } + + internal override bool Evaluate(string filename) + { + DateTime x; + switch (Which) + { + case WhichTime.atime: + x = System.IO.File.GetLastAccessTime(filename).ToUniversalTime(); + break; + case WhichTime.mtime: + x = System.IO.File.GetLastWriteTime(filename).ToUniversalTime(); + break; + case WhichTime.ctime: + x = System.IO.File.GetCreationTime(filename).ToUniversalTime(); + break; + default: + throw new ArgumentException("Operator"); + } + CriterionTrace("TimeCriterion({0},{1})= {2}", filename, Which.ToString(), x); + return _Evaluate(x); + } + + + private bool _Evaluate(DateTime x) + { + bool result = false; + switch (Operator) + { + case ComparisonOperator.GreaterThanOrEqualTo: + result = (x >= Time); + break; + case ComparisonOperator.GreaterThan: + result = (x > Time); + break; + case ComparisonOperator.LesserThanOrEqualTo: + result = (x <= Time); + break; + case ComparisonOperator.LesserThan: + result = (x < Time); + break; + case ComparisonOperator.EqualTo: + result = (x == Time); + break; + case ComparisonOperator.NotEqualTo: + result = (x != Time); + break; + default: + throw new ArgumentException("Operator"); + } + + CriterionTrace("TimeCriterion: {0}", result); + return result; + } + } + + + + internal partial class NameCriterion : SelectionCriterion + { + private Regex _re; + private String _regexString; + internal ComparisonOperator Operator; + private string _MatchingFileSpec; + internal virtual string MatchingFileSpec + { + set + { + // workitem 8245 + if (Directory.Exists(value)) + { + _MatchingFileSpec = ".\\" + value + "\\*.*"; + } + else + { + _MatchingFileSpec = value; + } + + _regexString = "^" + + Regex.Escape(_MatchingFileSpec) + .Replace(@"\\\*\.\*", @"\\([^\.]+|.*\.[^\\\.]*)") + .Replace(@"\.\*", @"\.[^\\\.]*") + .Replace(@"\*", @".*") + //.Replace(@"\*", @"[^\\\.]*") // ill-conceived + .Replace(@"\?", @"[^\\\.]") + + "$"; + + CriterionTrace("NameCriterion regexString({0})", _regexString); + + _re = new Regex(_regexString, RegexOptions.IgnoreCase); + } + } + + + public override String ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("name ").Append(EnumUtil.GetDescription(Operator)) + .Append(" '") + .Append(_MatchingFileSpec) + .Append("'"); + return sb.ToString(); + } + + + internal override bool Evaluate(string filename) + { + CriterionTrace("NameCriterion::Evaluate('{0}' pattern[{1}])", + filename, _MatchingFileSpec); + return _Evaluate(filename); + } + + private bool _Evaluate(string fullpath) + { + CriterionTrace("NameCriterion::Evaluate({0})", fullpath); + // No slash in the pattern implicitly means recurse, which means compare to + // filename only, not full path. + String f = (_MatchingFileSpec.IndexOf('\\') == -1) + ? System.IO.Path.GetFileName(fullpath) + : fullpath; // compare to fullpath + + bool result = _re.IsMatch(f); + + if (Operator != ComparisonOperator.EqualTo) + result = !result; + return result; + } + } + + + internal partial class TypeCriterion : SelectionCriterion + { + private char ObjectType; // 'D' = Directory, 'F' = File + internal ComparisonOperator Operator; + internal string AttributeString + { + get + { + return ObjectType.ToString(); + } + set + { + if (value.Length != 1 || + (value[0]!='D' && value[0]!='F')) + throw new ArgumentException("Specify a single character: either D or F"); + ObjectType = value[0]; + } + } + + public override String ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("type ").Append(EnumUtil.GetDescription(Operator)).Append(" ").Append(AttributeString); + return sb.ToString(); + } + + internal override bool Evaluate(string filename) + { + CriterionTrace("TypeCriterion::Evaluate({0})", filename); + + bool result = (ObjectType == 'D') + ? Directory.Exists(filename) + : File.Exists(filename); + + if (Operator != ComparisonOperator.EqualTo) + result = !result; + return result; + } + } + + +#if !SILVERLIGHT + internal partial class AttributesCriterion : SelectionCriterion + { + private FileAttributes _Attributes; + internal ComparisonOperator Operator; + internal string AttributeString + { + get + { + string result = ""; + if ((_Attributes & FileAttributes.Hidden) != 0) + result += "H"; + if ((_Attributes & FileAttributes.System) != 0) + result += "S"; + if ((_Attributes & FileAttributes.ReadOnly) != 0) + result += "R"; + if ((_Attributes & FileAttributes.Archive) != 0) + result += "A"; + if ((_Attributes & FileAttributes.ReparsePoint) != 0) + result += "L"; + if ((_Attributes & FileAttributes.NotContentIndexed) != 0) + result += "I"; + return result; + } + + set + { + _Attributes = FileAttributes.Normal; + foreach (char c in value.ToUpper()) + { + switch (c) + { + case 'H': + if ((_Attributes & FileAttributes.Hidden) != 0) + throw new ArgumentException(String.Format("Repeated flag. ({0})", c), "value"); + _Attributes |= FileAttributes.Hidden; + break; + + case 'R': + if ((_Attributes & FileAttributes.ReadOnly) != 0) + throw new ArgumentException(String.Format("Repeated flag. ({0})", c), "value"); + _Attributes |= FileAttributes.ReadOnly; + break; + + case 'S': + if ((_Attributes & FileAttributes.System) != 0) + throw new ArgumentException(String.Format("Repeated flag. ({0})", c), "value"); + _Attributes |= FileAttributes.System; + break; + + case 'A': + if ((_Attributes & FileAttributes.Archive) != 0) + throw new ArgumentException(String.Format("Repeated flag. ({0})", c), "value"); + _Attributes |= FileAttributes.Archive; + break; + + case 'I': + if ((_Attributes & FileAttributes.NotContentIndexed) != 0) + throw new ArgumentException(String.Format("Repeated flag. ({0})", c), "value"); + _Attributes |= FileAttributes.NotContentIndexed; + break; + + case 'L': + if ((_Attributes & FileAttributes.ReparsePoint) != 0) + throw new ArgumentException(String.Format("Repeated flag. ({0})", c), "value"); + _Attributes |= FileAttributes.ReparsePoint; + break; + + default: + throw new ArgumentException(value); + } + } + } + } + + + public override String ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("attributes ").Append(EnumUtil.GetDescription(Operator)).Append(" ").Append(AttributeString); + return sb.ToString(); + } + + private bool _EvaluateOne(FileAttributes fileAttrs, FileAttributes criterionAttrs) + { + bool result = false; + if ((_Attributes & criterionAttrs) == criterionAttrs) + result = ((fileAttrs & criterionAttrs) == criterionAttrs); + else + result = true; + return result; + } + + + + internal override bool Evaluate(string filename) + { + // workitem 10191 + if (Directory.Exists(filename)) + { + // Directories don't have file attributes, so the result + // of an evaluation is always NO. This gets negated if + // the operator is NotEqualTo. + return (Operator != ComparisonOperator.EqualTo); + } +#if NETCF + FileAttributes fileAttrs = NetCfFile.GetAttributes(filename); +#else + FileAttributes fileAttrs = System.IO.File.GetAttributes(filename); +#endif + + return _Evaluate(fileAttrs); + } + + private bool _Evaluate(FileAttributes fileAttrs) + { + bool result = _EvaluateOne(fileAttrs, FileAttributes.Hidden); + if (result) + result = _EvaluateOne(fileAttrs, FileAttributes.System); + if (result) + result = _EvaluateOne(fileAttrs, FileAttributes.ReadOnly); + if (result) + result = _EvaluateOne(fileAttrs, FileAttributes.Archive); + if (result) + result = _EvaluateOne(fileAttrs, FileAttributes.NotContentIndexed); + if (result) + result = _EvaluateOne(fileAttrs, FileAttributes.ReparsePoint); + + if (Operator != ComparisonOperator.EqualTo) + result = !result; + + return result; + } + } +#endif + + + internal partial class CompoundCriterion : SelectionCriterion + { + internal LogicalConjunction Conjunction; + internal SelectionCriterion Left; + + private SelectionCriterion _Right; + internal SelectionCriterion Right + { + get { return _Right; } + set + { + _Right = value; + if (value == null) + Conjunction = LogicalConjunction.NONE; + else if (Conjunction == LogicalConjunction.NONE) + Conjunction = LogicalConjunction.AND; + } + } + + + internal override bool Evaluate(string filename) + { + bool result = Left.Evaluate(filename); + switch (Conjunction) + { + case LogicalConjunction.AND: + if (result) + result = Right.Evaluate(filename); + break; + case LogicalConjunction.OR: + if (!result) + result = Right.Evaluate(filename); + break; + case LogicalConjunction.XOR: + result ^= Right.Evaluate(filename); + break; + default: + throw new ArgumentException("Conjunction"); + } + return result; + } + + + public override String ToString() + { + StringBuilder sb = new StringBuilder(); + sb.Append("(") + .Append((Left != null) ? Left.ToString() : "null") + .Append(" ") + .Append(Conjunction.ToString()) + .Append(" ") + .Append((Right != null) ? Right.ToString() : "null") + .Append(")"); + return sb.ToString(); + } + } + + + + /// + /// FileSelector encapsulates logic that selects files from a source - a zip file + /// or the filesystem - based on a set of criteria. This class is used internally + /// by the DotNetZip library, in particular for the AddSelectedFiles() methods. + /// This class can also be used independently of the zip capability in DotNetZip. + /// + /// + /// + /// + /// + /// The FileSelector class is used internally by the ZipFile class for selecting + /// files for inclusion into the ZipFile, when the method, or one of + /// its overloads, is called. It's also used for the methods. Typically, an + /// application that creates or manipulates Zip archives will not directly + /// interact with the FileSelector class. + /// + /// + /// + /// Some applications may wish to use the FileSelector class directly, to + /// select files from disk volumes based on a set of criteria, without creating or + /// querying Zip archives. The file selection criteria include: a pattern to + /// match the filename; the last modified, created, or last accessed time of the + /// file; the size of the file; and the attributes of the file. + /// + /// + /// + /// Consult the documentation for + /// for more information on specifying the selection criteria. + /// + /// + /// + public partial class FileSelector + { + internal SelectionCriterion _Criterion; + +#if NOTUSED + /// + /// The default constructor. + /// + /// + /// Typically, applications won't use this constructor. Instead they'll + /// call the constructor that accepts a selectionCriteria string. If you + /// use this constructor, you'll want to set the SelectionCriteria + /// property on the instance before calling SelectFiles(). + /// + protected FileSelector() { } +#endif + /// + /// Constructor that allows the caller to specify file selection criteria. + /// + /// + /// + /// + /// This constructor allows the caller to specify a set of criteria for + /// selection of files. + /// + /// + /// + /// See for a description of + /// the syntax of the selectionCriteria string. + /// + /// + /// + /// By default the FileSelector will traverse NTFS Reparse Points. To + /// change this, use FileSelector(String, bool). + /// + /// + /// + /// The criteria for file selection. + public FileSelector(String selectionCriteria) + : this(selectionCriteria, true) + { + } + + /// + /// Constructor that allows the caller to specify file selection criteria. + /// + /// + /// + /// + /// This constructor allows the caller to specify a set of criteria for + /// selection of files. + /// + /// + /// + /// See for a description of + /// the syntax of the selectionCriteria string. + /// + /// + /// + /// The criteria for file selection. + /// + /// whether to traverse NTFS reparse points (junctions). + /// + public FileSelector(String selectionCriteria, bool traverseDirectoryReparsePoints) + { + if (!String.IsNullOrEmpty(selectionCriteria)) + _Criterion = _ParseCriterion(selectionCriteria); + TraverseReparsePoints = traverseDirectoryReparsePoints; + } + + + + /// + /// The string specifying which files to include when retrieving. + /// + /// + /// + /// + /// Specify the criteria in statements of 3 elements: a noun, an operator, + /// and a value. Consider the string "name != *.doc" . The noun is + /// "name". The operator is "!=", implying "Not Equal". The value is + /// "*.doc". That criterion, in English, says "all files with a name that + /// does not end in the .doc extension." + /// + /// + /// + /// Supported nouns include "name" (or "filename") for the filename; + /// "atime", "mtime", and "ctime" for last access time, last modfied time, + /// and created time of the file, respectively; "attributes" (or "attrs") + /// for the file attributes; "size" (or "length") for the file length + /// (uncompressed); and "type" for the type of object, either a file or a + /// directory. The "attributes", "type", and "name" nouns all support = + /// and != as operators. The "size", "atime", "mtime", and "ctime" nouns + /// support = and !=, and >, >=, <, <= as well. The times are + /// taken to be expressed in local time. + /// + /// + /// + /// Specify values for the file attributes as a string with one or more of + /// the characters H,R,S,A,I,L in any order, implying file attributes of + /// Hidden, ReadOnly, System, Archive, NotContextIndexed, and ReparsePoint + /// (symbolic link) respectively. + /// + /// + /// + /// To specify a time, use YYYY-MM-DD-HH:mm:ss or YYYY/MM/DD-HH:mm:ss as + /// the format. If you omit the HH:mm:ss portion, it is assumed to be + /// 00:00:00 (midnight). + /// + /// + /// + /// The value for a size criterion is expressed in integer quantities of + /// bytes, kilobytes (use k or kb after the number), megabytes (m or mb), + /// or gigabytes (g or gb). + /// + /// + /// + /// The value for a name is a pattern to match against the filename, + /// potentially including wildcards. The pattern follows CMD.exe glob + /// rules: * implies one or more of any character, while ? implies one + /// character. If the name pattern contains any slashes, it is matched to + /// the entire filename, including the path; otherwise, it is matched + /// against only the filename without the path. This means a pattern of + /// "*\*.*" matches all files one directory level deep, while a pattern of + /// "*.*" matches all files in all directories. + /// + /// + /// + /// To specify a name pattern that includes spaces, use single quotes + /// around the pattern. A pattern of "'* *.*'" will match all files that + /// have spaces in the filename. The full criteria string for that would + /// be "name = '* *.*'" . + /// + /// + /// + /// The value for a type criterion is either F (implying a file) or D + /// (implying a directory). + /// + /// + /// + /// Some examples: + /// + /// + /// + /// + /// criteria + /// Files retrieved + /// + /// + /// + /// name != *.xls + /// any file with an extension that is not .xls + /// + /// + /// + /// + /// name = *.mp3 + /// any file with a .mp3 extension. + /// + /// + /// + /// + /// *.mp3 + /// (same as above) any file with a .mp3 extension. + /// + /// + /// + /// + /// attributes = A + /// all files whose attributes include the Archive bit. + /// + /// + /// + /// + /// attributes != H + /// all files whose attributes do not include the Hidden bit. + /// + /// + /// + /// + /// mtime > 2009-01-01 + /// all files with a last modified time after January 1st, 2009. + /// + /// + /// + /// + /// ctime > 2009/01/01-03:00:00 + /// all files with a created time after 3am (local time), + /// on January 1st, 2009. + /// + /// + /// + /// + /// size > 2gb + /// all files whose uncompressed size is greater than 2gb. + /// + /// + /// + /// + /// type = D + /// all directories in the filesystem. + /// + /// + /// + /// + /// + /// You can combine criteria with the conjunctions AND, OR, and XOR. Using + /// a string like "name = *.txt AND size >= 100k" for the + /// selectionCriteria retrieves entries whose names end in .txt, and whose + /// uncompressed size is greater than or equal to 100 kilobytes. + /// + /// + /// + /// For more complex combinations of criteria, you can use parenthesis to + /// group clauses in the boolean logic. Absent parenthesis, the + /// precedence of the criterion atoms is determined by order of + /// appearance. Unlike the C# language, the AND conjunction does not take + /// precendence over the logical OR. This is important only in strings + /// that contain 3 or more criterion atoms. In other words, "name = *.txt + /// and size > 1000 or attributes = H" implies "((name = *.txt AND size + /// > 1000) OR attributes = H)" while "attributes = H OR name = *.txt + /// and size > 1000" evaluates to "((attributes = H OR name = *.txt) + /// AND size > 1000)". When in doubt, use parenthesis. + /// + /// + /// + /// Using time properties requires some extra care. If you want to + /// retrieve all entries that were last updated on 2009 February 14, + /// specify "mtime >= 2009-02-14 AND mtime < 2009-02-15". Read this + /// to say: all files updated after 12:00am on February 14th, until + /// 12:00am on February 15th. You can use the same bracketing approach to + /// specify any time period - a year, a month, a week, and so on. + /// + /// + /// + /// The syntax allows one special case: if you provide a string with no + /// spaces, it is treated as a pattern to match for the filename. + /// Therefore a string like "*.xls" will be equivalent to specifying "name + /// = *.xls". This "shorthand" notation does not work with compound + /// criteria. + /// + /// + /// + /// There is no logic in this class that insures that the inclusion + /// criteria are internally consistent. For example, it's possible to + /// specify criteria that says the file must have a size of less than 100 + /// bytes, as well as a size that is greater than 1000 bytes. Obviously + /// no file will ever satisfy such criteria, but this class does not check + /// for or detect such inconsistencies. + /// + /// + /// + /// + /// + /// Thrown in the setter if the value has an invalid syntax. + /// + public String SelectionCriteria + { + get + { + if (_Criterion == null) return null; + return _Criterion.ToString(); + } + set + { + if (value == null) _Criterion = null; + else if (value.Trim() == "") _Criterion = null; + else + _Criterion = _ParseCriterion(value); + } + } + + /// + /// Indicates whether searches will traverse NTFS reparse points, like Junctions. + /// + public bool TraverseReparsePoints + { + get; set; + } + + + private enum ParseState + { + Start, + OpenParen, + CriterionDone, + ConjunctionPending, + Whitespace, + } + + + private static class RegexAssertions + { + public static readonly String PrecededByOddNumberOfSingleQuotes = "(?<=(?:[^']*'[^']*')*'[^']*)"; + public static readonly String FollowedByOddNumberOfSingleQuotesAndLineEnd = "(?=[^']*'(?:[^']*'[^']*')*[^']*$)"; + + public static readonly String PrecededByEvenNumberOfSingleQuotes = "(?<=(?:[^']*'[^']*')*[^']*)"; + public static readonly String FollowedByEvenNumberOfSingleQuotesAndLineEnd = "(?=(?:[^']*'[^']*')*[^']*$)"; + } + + + private static string NormalizeCriteriaExpression(string source) + { + // The goal here is to normalize the criterion expression. At output, in + // the transformed criterion string, every significant syntactic element + // - a property element, grouping paren for the boolean logic, operator + // ( = < > != ), conjunction, or property value - will be separated from + // its neighbors by at least one space. Thus, + // + // before after + // ------------------------------------------------------------------- + // name=*.txt name = *.txt + // (size>100)AND(name=*.txt) ( size > 100 ) AND ( name = *.txt ) + // + // This is relatively straightforward using regular expression + // replacement. This method applies a distinct regex pattern and + // corresponding replacement string for each one of a number of cases: + // an open paren followed by a word; a word followed by a close-paren; a + // pair of open parens; a close paren followed by a word (which should + // then be followed by an open paren). And so on. These patterns and + // replacements are all stored in prPairs. By applying each of these + // regex replacements in turn, we get the transformed string. Easy. + // + // The resulting "normalized" criterion string, is then used as the + // subject that gets parsed, by splitting the string into tokens that + // are separated by spaces. Here, there's a twist. The spaces within + // single-quote delimiters do not delimit distinct tokens. So, this + // normalization method temporarily replaces those spaces with + // ASCII 6 (0x06), a control character which is not a legal + // character in a filename. The parsing logic that happens later will + // revert that change, restoring the original value of the filename + // specification. + // + // To illustrate, for a "before" string of [(size>100)AND(name='Name + // (with Parens).txt')] , the "after" string is [( size > 100 ) AND + // ( name = 'Name\u0006(with\u0006Parens).txt' )]. + // + + string[][] prPairs = + { + // A. opening double parens - insert a space between them + new string[] { @"([^']*)\(\(([^']+)", "$1( ($2" }, + + // B. closing double parens - insert a space between + new string[] { @"(.)\)\)", "$1) )" }, + + // C. single open paren with a following word - insert a space between + new string[] { @"\((\S)", "( $1" }, + + // D. single close paren with a preceding word - insert a space between the two + new string[] { @"(\S)\)", "$1 )" }, + + // E. close paren at line start?, insert a space before the close paren + // this seems like a degenerate case. I don't recall why it's here. + new string[] { @"^\)", " )" }, + + // F. a word (likely a conjunction) followed by an open paren - insert a space between + new string[] { @"(\S)\(", "$1 (" }, + + // G. single close paren followed by word - insert a paren after close paren + new string[] { @"\)(\S)", ") $1" }, + + // H. insert space between = and a following single quote + //new string[] { @"(=|!=)('[^']*')", "$1 $2" }, + new string[] { @"(=)('[^']*')", "$1 $2" }, + + // I. insert space between property names and the following operator + //new string[] { @"([^ ])([><(?:!=)=])", "$1 $2" }, + new string[] { @"([^ !><])(>|<|!=|=)", "$1 $2" }, + + // J. insert spaces between operators and the following values + //new string[] { @"([><(?:!=)=])([^ ])", "$1 $2" }, + new string[] { @"(>|<|!=|=)([^ =])", "$1 $2" }, + + // K. replace fwd slash with backslash + new string[] { @"/", "\\" }, + }; + + string interim = source; + + for (int i=0; i < prPairs.Length; i++) + { + //char caseIdx = (char)('A' + i); + string pattern = RegexAssertions.PrecededByEvenNumberOfSingleQuotes + + prPairs[i][0] + + RegexAssertions.FollowedByEvenNumberOfSingleQuotesAndLineEnd; + + interim = Regex.Replace(interim, pattern, prPairs[i][1]); + } + + // match a fwd slash, followed by an odd number of single quotes. + // This matches fwd slashes only inside a pair of single quote delimiters, + // eg, a filename. This must be done as well as the case above, to handle + // filenames specified inside quotes as well as filenames without quotes. + var regexPattern = @"/" + + RegexAssertions.FollowedByOddNumberOfSingleQuotesAndLineEnd; + // replace with backslash + interim = Regex.Replace(interim, regexPattern, "\\"); + + // match a space, followed by an odd number of single quotes. + // This matches spaces only inside a pair of single quote delimiters. + regexPattern = " " + + RegexAssertions.FollowedByOddNumberOfSingleQuotesAndLineEnd; + + // Replace all spaces that appear inside single quotes, with + // ascii 6. This allows a split on spaces to get tokens in + // the expression. The split will not split any filename or + // wildcard that appears within single quotes. After tokenizing, we + // need to replace ascii 6 with ascii 32 to revert the + // spaces within quotes. + return Regex.Replace(interim, regexPattern, "\u0006"); + } + + + private static SelectionCriterion _ParseCriterion(String s) + { + if (s == null) return null; + + // inject spaces after open paren and before close paren, etc + s = NormalizeCriteriaExpression(s); + + // no spaces in the criteria is shorthand for filename glob + if (s.IndexOf(" ") == -1) + s = "name = " + s; + + // split the expression into tokens + string[] tokens = s.Trim().Split(' ', '\t'); + + if (tokens.Length < 3) throw new ArgumentException(s); + + SelectionCriterion current = null; + + LogicalConjunction pendingConjunction = LogicalConjunction.NONE; + + ParseState state; + var stateStack = new System.Collections.Generic.Stack(); + var critStack = new System.Collections.Generic.Stack(); + stateStack.Push(ParseState.Start); + + for (int i = 0; i < tokens.Length; i++) + { + string tok1 = tokens[i].ToLower(); + switch (tok1) + { + case "and": + case "xor": + case "or": + state = stateStack.Peek(); + if (state != ParseState.CriterionDone) + throw new ArgumentException(String.Join(" ", tokens, i, tokens.Length - i)); + + if (tokens.Length <= i + 3) + throw new ArgumentException(String.Join(" ", tokens, i, tokens.Length - i)); + + pendingConjunction = (LogicalConjunction)Enum.Parse(typeof(LogicalConjunction), tokens[i].ToUpper(), true); + current = new CompoundCriterion { Left = current, Right = null, Conjunction = pendingConjunction }; + stateStack.Push(state); + stateStack.Push(ParseState.ConjunctionPending); + critStack.Push(current); + break; + + case "(": + state = stateStack.Peek(); + if (state != ParseState.Start && state != ParseState.ConjunctionPending && state != ParseState.OpenParen) + throw new ArgumentException(String.Join(" ", tokens, i, tokens.Length - i)); + + if (tokens.Length <= i + 4) + throw new ArgumentException(String.Join(" ", tokens, i, tokens.Length - i)); + + stateStack.Push(ParseState.OpenParen); + break; + + case ")": + state = stateStack.Pop(); + if (stateStack.Peek() != ParseState.OpenParen) + throw new ArgumentException(String.Join(" ", tokens, i, tokens.Length - i)); + + stateStack.Pop(); + stateStack.Push(ParseState.CriterionDone); + break; + + case "atime": + case "ctime": + case "mtime": + if (tokens.Length <= i + 2) + throw new ArgumentException(String.Join(" ", tokens, i, tokens.Length - i)); + + DateTime t; + try + { + t = DateTime.ParseExact(tokens[i + 2], "yyyy-MM-dd-HH:mm:ss", null); + } + catch (FormatException) + { + try + { + t = DateTime.ParseExact(tokens[i + 2], "yyyy/MM/dd-HH:mm:ss", null); + } + catch (FormatException) + { + try + { + t = DateTime.ParseExact(tokens[i + 2], "yyyy/MM/dd", null); + } + catch (FormatException) + { + try + { + t = DateTime.ParseExact(tokens[i + 2], "MM/dd/yyyy", null); + } + catch (FormatException) + { + t = DateTime.ParseExact(tokens[i + 2], "yyyy-MM-dd", null); + } + } + } + } + t= DateTime.SpecifyKind(t, DateTimeKind.Local).ToUniversalTime(); + current = new TimeCriterion + { + Which = (WhichTime)Enum.Parse(typeof(WhichTime), tokens[i], true), + Operator = (ComparisonOperator)EnumUtil.Parse(typeof(ComparisonOperator), tokens[i + 1]), + Time = t + }; + i += 2; + stateStack.Push(ParseState.CriterionDone); + break; + + + case "length": + case "size": + if (tokens.Length <= i + 2) + throw new ArgumentException(String.Join(" ", tokens, i, tokens.Length - i)); + + Int64 sz = 0; + string v = tokens[i + 2]; + if (v.ToUpper().EndsWith("K")) + sz = Int64.Parse(v.Substring(0, v.Length - 1)) * 1024; + else if (v.ToUpper().EndsWith("KB")) + sz = Int64.Parse(v.Substring(0, v.Length - 2)) * 1024; + else if (v.ToUpper().EndsWith("M")) + sz = Int64.Parse(v.Substring(0, v.Length - 1)) * 1024 * 1024; + else if (v.ToUpper().EndsWith("MB")) + sz = Int64.Parse(v.Substring(0, v.Length - 2)) * 1024 * 1024; + else if (v.ToUpper().EndsWith("G")) + sz = Int64.Parse(v.Substring(0, v.Length - 1)) * 1024 * 1024 * 1024; + else if (v.ToUpper().EndsWith("GB")) + sz = Int64.Parse(v.Substring(0, v.Length - 2)) * 1024 * 1024 * 1024; + else sz = Int64.Parse(tokens[i + 2]); + + current = new SizeCriterion + { + Size = sz, + Operator = (ComparisonOperator)EnumUtil.Parse(typeof(ComparisonOperator), tokens[i + 1]) + }; + i += 2; + stateStack.Push(ParseState.CriterionDone); + break; + + case "filename": + case "name": + { + if (tokens.Length <= i + 2) + throw new ArgumentException(String.Join(" ", tokens, i, tokens.Length - i)); + + ComparisonOperator c = + (ComparisonOperator)EnumUtil.Parse(typeof(ComparisonOperator), tokens[i + 1]); + + if (c != ComparisonOperator.NotEqualTo && c != ComparisonOperator.EqualTo) + throw new ArgumentException(String.Join(" ", tokens, i, tokens.Length - i)); + + string m = tokens[i + 2]; + + // handle single-quoted filespecs (used to include + // spaces in filename patterns) + if (m.StartsWith("'") && m.EndsWith("'")) + { + // trim off leading and trailing single quotes and + // revert the control characters to spaces. + m = m.Substring(1, m.Length - 2) + .Replace("\u0006", " "); + } + + // if (m.StartsWith("'")) + // m = m.Replace("\u0006", " "); + + current = new NameCriterion + { + MatchingFileSpec = m, + Operator = c + }; + i += 2; + stateStack.Push(ParseState.CriterionDone); + } + break; + +#if !SILVERLIGHT + case "attrs": + case "attributes": +#endif + case "type": + { + if (tokens.Length <= i + 2) + throw new ArgumentException(String.Join(" ", tokens, i, tokens.Length - i)); + + ComparisonOperator c = + (ComparisonOperator)EnumUtil.Parse(typeof(ComparisonOperator), tokens[i + 1]); + + if (c != ComparisonOperator.NotEqualTo && c != ComparisonOperator.EqualTo) + throw new ArgumentException(String.Join(" ", tokens, i, tokens.Length - i)); + +#if SILVERLIGHT + current = (SelectionCriterion) new TypeCriterion + { + AttributeString = tokens[i + 2], + Operator = c + }; +#else + current = (tok1 == "type") + ? (SelectionCriterion) new TypeCriterion + { + AttributeString = tokens[i + 2], + Operator = c + } + : (SelectionCriterion) new AttributesCriterion + { + AttributeString = tokens[i + 2], + Operator = c + }; +#endif + i += 2; + stateStack.Push(ParseState.CriterionDone); + } + break; + + case "": + // NOP + stateStack.Push(ParseState.Whitespace); + break; + + default: + throw new ArgumentException("'" + tokens[i] + "'"); + } + + state = stateStack.Peek(); + if (state == ParseState.CriterionDone) + { + stateStack.Pop(); + if (stateStack.Peek() == ParseState.ConjunctionPending) + { + while (stateStack.Peek() == ParseState.ConjunctionPending) + { + var cc = critStack.Pop() as CompoundCriterion; + cc.Right = current; + current = cc; // mark the parent as current (walk up the tree) + stateStack.Pop(); // the conjunction is no longer pending + + state = stateStack.Pop(); + if (state != ParseState.CriterionDone) + throw new ArgumentException("??"); + } + } + else stateStack.Push(ParseState.CriterionDone); // not sure? + } + + if (state == ParseState.Whitespace) + stateStack.Pop(); + } + + return current; + } + + + /// + /// Returns a string representation of the FileSelector object. + /// + /// The string representation of the boolean logic statement of the file + /// selection criteria for this instance. + public override String ToString() + { + return "FileSelector("+_Criterion.ToString()+")"; + } + + + private bool Evaluate(string filename) + { + // dinoch - Thu, 11 Feb 2010 18:34 + SelectorTrace("Evaluate({0})", filename); + bool result = _Criterion.Evaluate(filename); + return result; + } + + [System.Diagnostics.Conditional("SelectorTrace")] + private void SelectorTrace(string format, params object[] args) + { + if (_Criterion != null && _Criterion.Verbose) + System.Console.WriteLine(format, args); + } + + /// + /// Returns the names of the files in the specified directory + /// that fit the selection criteria specified in the FileSelector. + /// + /// + /// + /// This is equivalent to calling + /// with recurseDirectories = false. + /// + /// + /// + /// The name of the directory over which to apply the FileSelector + /// criteria. + /// + /// + /// + /// A collection of strings containing fully-qualified pathnames of files + /// that match the criteria specified in the FileSelector instance. + /// + public System.Collections.Generic.ICollection SelectFiles(String directory) + { + return SelectFiles(directory, false); + } + + + /// + /// Returns the names of the files in the specified directory that fit the + /// selection criteria specified in the FileSelector, optionally recursing + /// through subdirectories. + /// + /// + /// + /// This method applies the file selection criteria contained in the + /// FileSelector to the files contained in the given directory, and + /// returns the names of files that conform to the criteria. + /// + /// + /// + /// The name of the directory over which to apply the FileSelector + /// criteria. + /// + /// + /// + /// Whether to recurse through subdirectories when applying the file + /// selection criteria. + /// + /// + /// + /// A collection of strings containing fully-qualified pathnames of files + /// that match the criteria specified in the FileSelector instance. + /// + public System.Collections.ObjectModel.ReadOnlyCollection + SelectFiles(String directory, + bool recurseDirectories) + { + if (_Criterion == null) + throw new ArgumentException("SelectionCriteria has not been set"); + + var list = new List(); + try + { + if (Directory.Exists(directory)) + { + String[] filenames = Directory.GetFiles(directory); + + // add the files: + foreach (String filename in filenames) + { + if (Evaluate(filename)) + list.Add(filename); + } + + if (recurseDirectories) + { + // add the subdirectories: + String[] dirnames = Directory.GetDirectories(directory); + foreach (String dir in dirnames) + { + if (this.TraverseReparsePoints +#if !SILVERLIGHT + || ((File.GetAttributes(dir) & FileAttributes.ReparsePoint) == 0) +#endif + ) + { + // workitem 10191 + if (Evaluate(dir)) list.Add(dir); + list.AddRange(this.SelectFiles(dir, recurseDirectories)); + } + } + } + } + } + // can get System.UnauthorizedAccessException here + catch (System.UnauthorizedAccessException) + { + } + catch (System.IO.IOException) + { + } + + return list.AsReadOnly(); + } + } + + + + /// + /// Summary description for EnumUtil. + /// + internal sealed class EnumUtil + { + private EnumUtil() { } + /// + /// Returns the value of the DescriptionAttribute if the specified Enum + /// value has one. If not, returns the ToString() representation of the + /// Enum value. + /// + /// The Enum to get the description for + /// + internal static string GetDescription(System.Enum value) + { + FieldInfo fi = value.GetType().GetField(value.ToString()); + var attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false); + if (attributes.Length > 0) + return attributes[0].Description; + else + return value.ToString(); + } + + /// + /// Converts the string representation of the name or numeric value of one + /// or more enumerated constants to an equivalent enumerated object. + /// Note: use the DescriptionAttribute on enum values to enable this. + /// + /// The System.Type of the enumeration. + /// + /// A string containing the name or value to convert. + /// + /// + internal static object Parse(Type enumType, string stringRepresentation) + { + return Parse(enumType, stringRepresentation, false); + } + + +#if SILVERLIGHT + public static System.Enum[] GetEnumValues(Type type) + { + if (!type.IsEnum) + throw new ArgumentException("not an enum"); + + return ( + from field in type.GetFields(BindingFlags.Public | BindingFlags.Static) + where field.IsLiteral + select (System.Enum)field.GetValue(null) + ).ToArray(); + } + + public static string[] GetEnumStrings() + { + var type = typeof(T); + if (!type.IsEnum) + throw new ArgumentException("not an enum"); + + return ( + from field in type.GetFields(BindingFlags.Public | BindingFlags.Static) + where field.IsLiteral + select field.Name + ).ToArray(); + } +#endif + + /// + /// Converts the string representation of the name or numeric value of one + /// or more enumerated constants to an equivalent enumerated object. A + /// parameter specified whether the operation is case-sensitive. Note: + /// use the DescriptionAttribute on enum values to enable this. + /// + /// The System.Type of the enumeration. + /// + /// A string containing the name or value to convert. + /// + /// + /// Whether the operation is case-sensitive or not. + /// + internal static object Parse(Type enumType, string stringRepresentation, bool ignoreCase) + { + if (ignoreCase) + stringRepresentation = stringRepresentation.ToLower(); + +#if SILVERLIGHT + foreach (System.Enum enumVal in GetEnumValues(enumType)) +#else + foreach (System.Enum enumVal in System.Enum.GetValues(enumType)) +#endif + { + string description = GetDescription(enumVal); + if (ignoreCase) + description = description.ToLower(); + if (description == stringRepresentation) + return enumVal; + } + + return System.Enum.Parse(enumType, stringRepresentation, ignoreCase); + } + } + + +#if DEMO + public class DemonstrateFileSelector + { + private string _directory; + private bool _recurse; + private bool _traverse; + private bool _verbose; + private string _selectionCriteria; + private FileSelector f; + + public DemonstrateFileSelector() + { + this._directory = "."; + this._recurse = true; + } + + public DemonstrateFileSelector(string[] args) : this() + { + for (int i = 0; i < args.Length; i++) + { + switch(args[i]) + { + case"-?": + Usage(); + Environment.Exit(0); + break; + case "-d": + i++; + if (args.Length <= i) + throw new ArgumentException("-directory"); + this._directory = args[i]; + break; + case "-norecurse": + this._recurse = false; + break; + + case "-j-": + this._traverse = false; + break; + + case "-j+": + this._traverse = true; + break; + + case "-v": + this._verbose = true; + break; + + default: + if (this._selectionCriteria != null) + throw new ArgumentException(args[i]); + this._selectionCriteria = args[i]; + break; + } + + if (this._selectionCriteria != null) + this.f = new FileSelector(this._selectionCriteria); + } + } + + + public static void Main(string[] args) + { + try + { + Console.WriteLine(); + new DemonstrateFileSelector(args).Run(); + } + catch (Exception exc1) + { + Console.WriteLine("Exception: {0}", exc1.ToString()); + Usage(); + } + } + + + public void Run() + { + if (this.f == null) + this.f = new FileSelector("name = *.jpg AND (size > 1000 OR atime < 2009-02-14-01:00:00)"); + + this.f.TraverseReparsePoints = _traverse; + this.f.Verbose = this._verbose; + Console.WriteLine(); + Console.WriteLine(new String(':', 88)); + Console.WriteLine("Selecting files:\n" + this.f.ToString()); + var files = this.f.SelectFiles(this._directory, this._recurse); + if (files.Count == 0) + { + Console.WriteLine("no files."); + } + else + { + Console.WriteLine("files: {0}", files.Count); + foreach (string file in files) + { + Console.WriteLine(" " + file); + } + } + } + + public static void Usage() + { + Console.WriteLine("FileSelector: select files based on selection criteria.\n"); + Console.WriteLine("Usage:\n FileSelector [options]\n" + + "\n" + + " -d directory to select from (Default .)\n" + + " -norecurse don't recurse into subdirs\n" + + " -j- don't traverse junctions\n" + + " -v verbose output\n"); + } + } + +#endif + + + +} + + diff --git a/DotNetZip/Zip/Migrated rules for Zip DLL.ruleset b/DotNetZip/Zip/Migrated rules for Zip DLL.ruleset new file mode 100644 index 0000000..e84dcd6 --- /dev/null +++ b/DotNetZip/Zip/Migrated rules for Zip DLL.ruleset @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/DotNetZip/Zip/OffsetStream.cs b/DotNetZip/Zip/OffsetStream.cs new file mode 100644 index 0000000..525019a --- /dev/null +++ b/DotNetZip/Zip/OffsetStream.cs @@ -0,0 +1,114 @@ +// OffsetStream.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2009 Dino Chiesa +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2009-August-27 12:50:35> +// +// ------------------------------------------------------------------ +// +// This module defines logic for handling reading of zip archives embedded +// into larger streams. The initial position of the stream serves as +// the base offset for all future Seek() operations. +// +// ------------------------------------------------------------------ + + +using System; +using System.IO; + +namespace Ionic.Zip +{ + internal class OffsetStream : System.IO.Stream, System.IDisposable + { + private Int64 _originalPosition; + private Stream _innerStream; + + public OffsetStream(Stream s) + : base() + { + _originalPosition = s.Position; + _innerStream = s; + } + + public override int Read(byte[] buffer, int offset, int count) + { + return _innerStream.Read(buffer, offset, count); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotImplementedException(); + } + + public override bool CanRead + { + get { return _innerStream.CanRead; } + } + + public override bool CanSeek + { + get { return _innerStream.CanSeek; } + } + + public override bool CanWrite + { + get { return false; } + } + + public override void Flush() + { + _innerStream.Flush(); + } + + public override long Length + { + get + { + return _innerStream.Length; + } + } + + public override long Position + { + get { return _innerStream.Position - _originalPosition; } + set { _innerStream.Position = _originalPosition + value; } + } + + + public override long Seek(long offset, System.IO.SeekOrigin origin) + { + return _innerStream.Seek(_originalPosition + offset, origin) - _originalPosition; + } + + + public override void SetLength(long value) + { + throw new NotImplementedException(); + } + + void IDisposable.Dispose() + { + Close(); + } + + public override void Close() + { + base.Close(); + } + + } + +} \ No newline at end of file diff --git a/DotNetZip/Zip/PackResources.vbs b/DotNetZip/Zip/PackResources.vbs new file mode 100644 index 0000000..2e248ee --- /dev/null +++ b/DotNetZip/Zip/PackResources.vbs @@ -0,0 +1,238 @@ +' PackResources.vbs +' ------------------------------------------------------------------ +' +' Copyright (c) 2010 Dino Chiesa +' All rights reserved. +' +' This code module is part of DotNetZip, a zipfile class library. +' +' ------------------------------------------------------------------ +' +' This code is licensed under the Microsoft Public License. +' See the file License.txt for the license details. +' More info on: http://dotnetzip.codeplex.com +' +' ------------------------------------------------------------------ +' +' last saved (in emacs): +' Time-stamp: <2011-July-23 20:02:17> +' +' ------------------------------------------------------------------ +' +' This is a script file that packs the resources files into a zip, +' for inclusion into the zip dll. +' +' This script assumes it will be run by Visual Studio, as a prebuild +' script, starting with the current directory of +' {DotNetZip}\Zip Partial DLL\bin\{Debug,Release} +' +' Wed, 10 Feb 2010 12:24 +' +' ------------------------------------------------------------------ + + +Sub NewZip(pathToZipFile) + + WScript.Echo "Creating a new zip file (" & pathToZipFile & ") " + + Dim fso + Set fso = CreateObject("Scripting.FileSystemObject") + Dim file + Set file = fso.CreateTextFile(pathToZipFile) + + '' this is the content for an empty zip file + file.Write Chr(80) & Chr(75) & Chr(5) & Chr(6) & String(18, 0) + + file.Close + Set fso = Nothing + Set file = Nothing + + WScript.Sleep 500 + +End Sub + + + +Function DatesAreSubstantiallyDifferent(d1, d2) + + Dim result + Dim s + + ''WScript.Echo "d1= " & d1 + ''WScript.Echo "d2= " & d2 + + '' http://www.w3schools.com/vbscript/func_datediff.asp + s = DateDiff("s",d1,d2) + ''WScript.Echo "s= " & s + + '' 2 seconds or less in either direction is ok. + If (s < 3 AND s > -3) Then + result = False + Else + result = True + End If + + DatesAreSubstantiallyDifferent = result + +End Function + + + + + +Sub CreateZip(pathToZipFile, dirToZip) + + Dim fso + Set fso= Wscript.CreateObject("Scripting.FileSystemObject") + + Dim fullPathToZipFile + fullPathToZipFile = fso.GetAbsolutePathName(pathToZipFile) + + Dim fullDirToZip + fullDirToZip = fso.GetAbsolutePathName(dirToZip) + + If Not fso.FolderExists(fullDirToZip) Then + WScript.Echo "The directory to zip does not exist." + Exit Sub + End If + + WScript.Echo "Checking zip " & fullPathToZipFile + WScript.Echo " against directory " & fullDirToZip + + dim sa + set sa = CreateObject("Shell.Application") + + + ' http://msdn.microsoft.com/en-us/library/bb787866(VS.85).aspx + ' =============================================================== + ' 4 = do not display a progress box + ' 16 = Respond with "Yes to All" for any dialog box that is displayed. + ' 128 = Perform the operation on files only if a wildcard file name (*.*) is specified. + ' 256 = Display a progress dialog box but do not show the file names. + ' 2048 = Version 4.71. Do not copy the security attributes of the file. + ' 4096 = Only operate in the local directory. Don't operate recursively into subdirectories. + + Dim fcount + fcount = 0 + + Dim needRepack + needRepack = -1 + Dim zip + Dim folder, file, builtpath, pass, d1, d2, folderItem + Set folder = fso.GetFolder(fullDirToZip) + + '' do this in 2 passes. First pass checks if any file in the zip has been updated. + '' 2nd pass is performed only if necessary, and actually copies all the files into the new zip. + + pass = 0 + Do Until pass > 1 + + For Each file in folder.Files + '' check or zip any file that is not .zip, not .resx and not ending in ~ (emacs backup file) + If (Right(file.name,4) <> ".zip" AND Right(file.name,5) <> ".resx" AND Right(file.name,1) <> "~") Then + builtpath = fso.BuildPath(fullDirToZip, file.Name) + If (pass = 0) Then + If (needRepack = -1) Then + '' first file only + If Not fso.FileExists(fullPathToZipFile) Then + WScript.Echo "The zip file does not exist." + '' no zip means, always need to repack + needRepack = 1 + Else + Set zip = sa.NameSpace(fullPathToZipFile) + needRepack = 0 + End If + End If + + '' only check if we need to repack this file, if + '' necessary; in other words, if none of the prior + '' files need to be repacked. + If (needRepack = 0) Then + '' check if the file has been updated + d1 = file.DateLastModified + Set folderItem = zip.ParseName(file.Name) + If (Not folderItem Is Nothing) Then + d2 = folderItem.ModifyDate + Set folderItem = Nothing + Else + '' dummy + d2 = "01/01/2001 6:05:00 PM" + End If + + If DatesAreSubstantiallyDifferent(d1,d2) Then + needRepack = 1 + End If + End If + + Else + '' pass = 1 + If (fcount = 0) Then + Wscript.Sleep(400) + End If + WScript.Echo builtpath + zip.CopyHere builtpath, 0 + fcount = fcount + 1 + '' Delay between each item. With no, the zip fails with + '' "file not found" or "No Read Permission" or some other + '' spurious error. + Wscript.Sleep(450) + End If + + End If + Next + + If (pass = 0) Then + If (needRepack <> 0) Then + '' reaching pass 1 means we delete and re-create the zip file + WScript.Echo "The resources zip needs to be re-packed. " + Set zip = Nothing + If fso.FileExists(fullPathToZipFile) Then + WScript.Echo "That zip file already exists - deleting it." + fso.DeleteFile fullPathToZipFile + '' give it time to be really deleted + Wscript.Sleep(2400) + End If + NewZip fullPathToZipFile + Set zip = sa.NameSpace(fullPathToZipFile) + Else + WScript.Echo "The resources zip does not need to be updated." + '' insure we skip the 2nd pass. + pass = pass + 1 + End If + + Else + '' the zip process is asynchronous. wait for completion, + '' but don't wait forever. + Dim sLoop + sLoop = 0 + WScript.Echo "Verifying the count..." + Do Until fcount <= zip.Items.Count + Wscript.Sleep(400) + sLoop = sLoop + 1 + If ((sLoop Mod 6) = 0) Then + WScript.Echo " have " & zip.items.Count & " items so far, need " & fcount + ElseIf sLoop > 80 Then + WScript.Echo "Giving up..." + Set zip = Nothing + Wscript.Sleep(1200) + If fso.FileExists(fullPathToZipFile) Then + fso.DeleteFile fullPathToZipFile + End If + Err.Raise 1460, "PackResources.vbs/CreateZip", "Timeout waiting for ZIP completion" + End If + Loop + End If + pass = pass + 1 + + Loop + + Set fso = Nothing + Set sa = Nothing + Set zip = Nothing + Set folder = Nothing + +End Sub + + + +CreateZip "..\..\Resources\zippedResources.zip", "..\..\Resources" diff --git a/DotNetZip/Zip/Properties/AssemblyInfo.cs b/DotNetZip/Zip/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..12afa8a Binary files /dev/null and b/DotNetZip/Zip/Properties/AssemblyInfo.cs differ diff --git a/DotNetZip/Zip/Resources/CommandLineSelfExtractorStub.cs b/DotNetZip/Zip/Resources/CommandLineSelfExtractorStub.cs new file mode 100644 index 0000000..d776f3e --- /dev/null +++ b/DotNetZip/Zip/Resources/CommandLineSelfExtractorStub.cs @@ -0,0 +1,627 @@ +// CommandLineSelfExtractorStub.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2008-2011 Dino Chiesa. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2011-June-18 20:58:45> +// +// ------------------------------------------------------------------ +// +// This is a the source module that implements the stub of a +// command-line self-extracting Zip archive - the code included in all +// command-line SFX files. This code is included as a resource into the +// DotNetZip DLL, and then is compiled at runtime when a SFX is saved. +// +// ------------------------------------------------------------------ + + +namespace Ionic.Zip +{ + // include the using statements inside the namespace declaration, + // because source code will be concatenated together before + // compilation. + using System; + using System.Reflection; + using System.Resources; + using System.IO; + using System.Collections.Generic; + using System.Diagnostics; + using Ionic.Zip; + + public class CommandLineSelfExtractor + { + const string DllResourceName = "Ionic.Zip.dll"; + + string TargetDirectory = "@@EXTRACTLOCATION"; + string PostUnpackCmdLine = "@@POST_UNPACK_CMD_LINE"; + bool ReplacedEnvVarsForTargetDirectory; + bool ReplacedEnvVarsForCmdLine; + bool ListOnly; + bool Verbose; + bool ReallyVerbose; + bool RemoveFilesAfterExe; + bool SkipPostUnpackCommand; + string Password = null; + + // cannot include the following line, because of our use of + // the AssemblyResolver event. + + //Ionic.Zip.ExtractExistingFileAction Overwrite; + int Overwrite; + + // Attention: it isn't possible, with the design of this class as it is + // now, to have a member variable of a type from the Ionic.Zip assembly. + // The class design registers an assembly resolver, but apparently NOT in + // time to allow the assembly to be used in private instance variables. + + private bool PostUnpackCmdLineIsSet() + { + // What is going on here? + // The PostUnpackCmdLine is initialized to a particular value, then + // we test to see if it begins with the first two chars of that value, + // and ends with the last part of the value. Why? + + // Here's the thing. In order to insure the code is right, this module has + // to compile as it is, as a standalone module. But then, inside + // DotNetZip, when generating an SFX, we do a text.Replace on the source + // code, potentially replacing @@POST_UNPACK_CMD_LINE with an actual value. + // The test here checks to see if it has been set. + + bool result = !(PostUnpackCmdLine.StartsWith("@@") && + PostUnpackCmdLine.EndsWith("POST_UNPACK_CMD_LINE")); + + if (result && ReplacedEnvVarsForCmdLine == false) + { + PostUnpackCmdLine= ReplaceEnvVars(PostUnpackCmdLine); + ReplacedEnvVarsForCmdLine = true; + } + + return result; + } + + + private bool TargetDirectoryIsSet() + { + bool result = !(TargetDirectory.StartsWith("@@") && + TargetDirectory.EndsWith("EXTRACTLOCATION")); + + if (result && ReplacedEnvVarsForTargetDirectory == false) + { + TargetDirectory= ReplaceEnvVars(TargetDirectory); + ReplacedEnvVarsForTargetDirectory = true; + } + return result; + } + + + + private string ReplaceEnvVars(string s) + { + System.Collections.IDictionary envVars = Environment.GetEnvironmentVariables(); + foreach (System.Collections.DictionaryEntry de in envVars) + { + string t = "%" + de.Key + "%"; + s= s.Replace(t, de.Value as String); + } + + return s; + } + + + private bool SetRemoveFilesFlag() + { + bool result = false; + Boolean.TryParse("@@REMOVE_AFTER_EXECUTE", out result); + RemoveFilesAfterExe = result; + return result; + } + + + private bool SetVerboseFlag() + { + bool result = false; + Boolean.TryParse("@@QUIET", out result); + Verbose = !result; + return Verbose; + } + + private int SetOverwriteBehavior() + { + Int32 result = 0; + Int32.TryParse("@@EXTRACT_EXISTING_FILE", out result); + Overwrite = (int) result; + return result; + } + + + // ctor + private CommandLineSelfExtractor() + { + SetRemoveFilesFlag(); + SetVerboseFlag(); + SetOverwriteBehavior(); + PostUnpackCmdLineIsSet(); + TargetDirectoryIsSet(); + } + + + // ctor + public CommandLineSelfExtractor(string[] args) : this() + { + string specifiedDirectory = null; + for (int i = 0; i < args.Length; i++) + { + switch (args[i]) + { + case "-d": + i++; + if (args.Length <= i) + { + Console.WriteLine("please supply a directory.\n"); + GiveUsageAndExit(); + } + if (specifiedDirectory != null) + { + Console.WriteLine("You already provided a directory.\n"); + GiveUsageAndExit(); + } + specifiedDirectory = args[i]; + break; + case "-p": + i++; + if (args.Length <= i) + { + Console.WriteLine("please supply a password.\n"); + GiveUsageAndExit(); + } + if (Password != null) + { + Console.WriteLine("You already provided a password.\n"); + GiveUsageAndExit(); + } + Password = args[i]; + break; + case "-o": + Overwrite = 1; + //WantOverwrite = ExtractExistingFileAction.OverwriteSilently; + break; + case "-n": + Overwrite= 2; + //WantOverwrite = ExtractExistingFileAction.DoNotOverwrite; + break; + case "-l": + ListOnly = true; + break; + case "-r+": + RemoveFilesAfterExe = true; + break; + case "-r-": + RemoveFilesAfterExe = false; + break; + case "-x": + SkipPostUnpackCommand = true; + break; + case "-?": + GiveUsageAndExit(); + break; + case "-v-": + Verbose = false; + break; + case "-v+": + if (Verbose) + ReallyVerbose = true; + else + Verbose = true; + break; + default: + Console.WriteLine("unrecognized argument: '{0}'\n", args[i]); + GiveUsageAndExit(); + break; + } + } + + + if (!ListOnly) + { + if (specifiedDirectory!=null) + TargetDirectory = specifiedDirectory; + else if (!TargetDirectoryIsSet()) + TargetDirectory = "."; // cwd + } + + if (ListOnly && ((Overwrite!= 0) || (specifiedDirectory != null))) + { + Console.WriteLine("Inconsistent options.\n"); + GiveUsageAndExit(); + } + } + + + // workitem 8988 + private string[] SplitCommandLine(string cmdline) + { + // if the first char is NOT a double-quote, then just split the line + if (cmdline[0]!='"') + return cmdline.Split( new char[] {' '}, 2); + + // the first char is double-quote. Need to verify that there's another one. + int ix = cmdline.IndexOf('"', 1); + if (ix == -1) return null; // no double-quote - FAIL + + // if the double-quote is the last char, then just return an array of ONE string + if (ix+1 == cmdline.Length) return new string[] { cmdline.Substring(1,ix-1) }; + + if (cmdline[ix+1]!= ' ') return null; // no space following the double-quote - FAIL + + // there's definitely another double quote, followed by a space + string[] args = new string[2]; + args[0] = cmdline.Substring(1,ix-1); + while (cmdline[ix+1]==' ') ix++; // go to next non-space char + args[1] = cmdline.Substring(ix+1); + return args; + } + + + static CommandLineSelfExtractor() + { + AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(Resolver); + } + + + static System.Reflection.Assembly Resolver(object sender, ResolveEventArgs args) + { + // super defensive + Assembly a1 = Assembly.GetExecutingAssembly(); + if (a1==null) + throw new Exception("GetExecutingAssembly returns null."); + + string[] tokens = args.Name.Split(','); + String[] names = a1.GetManifestResourceNames(); + + if (names==null) + throw new Exception("GetManifestResourceNames returns null."); + + // workitem 7978 + Stream s = null; + foreach (string n in names) + { + string root = n.Substring(0,n.Length-4); + string ext = n.Substring(n.Length-3); + if (root.Equals(tokens[0]) && ext.ToLower().Equals("dll")) + { + s= a1.GetManifestResourceStream(n); + if (s!=null) break; + } + } + + if (s==null) + throw new Exception(String.Format("GetManifestResourceStream returns null. Available resources: [{0}]", + String.Join("|", names))); + + byte[] block = new byte[s.Length]; + + if (block==null) + throw new Exception(String.Format("Cannot allocated buffer of length({0}).", s.Length)); + + s.Read(block, 0, block.Length); + Assembly a2 = Assembly.Load(block); + if (a2==null) + throw new Exception("Assembly.Load(block) returns null"); + + return a2; + } + + + + public int Run() + { + //System.Diagnostics.Debugger.Break(); + + List itemsExtracted= new List(); + + global::Ionic.Zip.ExtractExistingFileAction WantOverwrite = + (Ionic.Zip.ExtractExistingFileAction) Overwrite; + + // There way this works: the EXE is a ZIP file. So + // read from the location of the assembly, in other words the path to the exe. + Assembly a = Assembly.GetExecutingAssembly(); + + int rc = 0; + try + { + // workitem 7067 + using (global::Ionic.Zip.ZipFile zip = global::Ionic.Zip.ZipFile.Read(a.Location)) + { + if (Verbose) + Console.WriteLine("Command-Line Self Extractor generated by DotNetZip."); + + if (!ListOnly) + { + if (Verbose) + { + Console.Write("Extracting to {0}", TargetDirectory); + System.Console.WriteLine(" (Existing file action: {0})", WantOverwrite.ToString()); + } + } + + bool header = true; + foreach (global::Ionic.Zip.ZipEntry entry in zip) + { + if (ListOnly || ReallyVerbose) + { + if (header) + { + System.Console.WriteLine("Extracting Zip file: {0}", zip.Name); + if ((zip.Comment != null) && (zip.Comment != "")) + System.Console.WriteLine("Comment: {0}", zip.Comment); + + System.Console.WriteLine("\n{1,-22} {2,9} {3,5} {4,9} {5,3} {6,8} {0}", + "Filename", "Modified", "Size", "Ratio", "Packed", "pw?", "CRC"); + System.Console.WriteLine(new System.String('-', 80)); + header = false; + } + + System.Console.WriteLine("{1,-22} {2,9} {3,5:F0}% {4,9} {5,3} {6:X8} {0}", + entry.FileName, + entry.LastModified.ToString("yyyy-MM-dd HH:mm:ss"), + entry.UncompressedSize, + entry.CompressionRatio, + entry.CompressedSize, + (entry.UsesEncryption) ? "Y" : "N", + entry.Crc); + + } + + if (!ListOnly) + { + if (Verbose && !ReallyVerbose) + System.Console.WriteLine(" {0}", entry.FileName); + + if (entry.Encryption == global::Ionic.Zip.EncryptionAlgorithm.None) + { + try + { + entry.Extract(TargetDirectory, WantOverwrite); + itemsExtracted.Add(entry.FileName); + } + catch (Exception ex1) + { + Console.WriteLine(" Error -- {0}", ex1.Message); + rc++; + } + } + else + { + if (Password == null) + { + Console.WriteLine("Cannot extract entry {0} without a password.", entry.FileName); + rc++; + } + else + { + try + { + entry.ExtractWithPassword(TargetDirectory, WantOverwrite, Password); + itemsExtracted.Add(entry.FileName); + } + catch (Exception ex2) + { + Console.WriteLine(" Error -- {0}", ex2.Message); + rc++; + } + } + } + } + } + } + } + catch (Exception) + { + Console.WriteLine("The self-extracting zip file is corrupted."); + return 4; + } + + if (rc != 0) return rc; + + + // potentially execute the embedded command + if (PostUnpackCmdLineIsSet() && !SkipPostUnpackCommand) + { + if (ListOnly) + { + Console.WriteLine("\nExecute on unpack: {0}", PostUnpackCmdLine); + } + else + { + try + { + string[] args = SplitCommandLine(PostUnpackCmdLine); + + if (args!= null && args.Length > 0) + { + if (Verbose) + System.Console.WriteLine("Running command: {0}", PostUnpackCmdLine); + + ProcessStartInfo startInfo = new ProcessStartInfo(args[0]); + startInfo.WorkingDirectory = TargetDirectory; + startInfo.CreateNoWindow = true; + if (args.Length > 1) startInfo.Arguments = args[1]; + + using (Process p = Process.Start(startInfo)) + { + if (p!=null) + { + p.WaitForExit(); + rc = p.ExitCode; + // workitem 8925 + if (p.ExitCode == 0) + { + if (RemoveFilesAfterExe) + { + foreach (string s in itemsExtracted) + { + string fullPath = Path.Combine(TargetDirectory,s); + try + { + if (File.Exists(fullPath)) + File.Delete(fullPath); + else if (Directory.Exists(fullPath)) + Directory.Delete(fullPath, true); + } + catch + { + } + } + } + } + } + } + + + } + } + catch (Exception exc1) + { + System.Console.WriteLine("{0}", exc1); + rc = 5; + } + + } + } + + return rc; + } + + + + private void GiveUsageAndExit() + { + Assembly a = Assembly.GetExecutingAssembly(); + string s = Path.GetFileName(a.Location); + Console.WriteLine("DotNetZip Command-Line Self Extractor, see http://DotNetZip.codeplex.com/"); + Console.WriteLine("Copyright (c) 2008-2011 Dino Chiesa."); + + Console.WriteLine("usage:\n {0} [-p ] [-d ]", s); + + string more = " Extracts entries from the archive. If any files to be extracted already\n" + + " exist, the program will stop.\n\n Additional Options:\n" + + "{0}" + + "{1}" + + "{2}" + + "{3}"; + + string overwriteString = + String.Format(" -o overwrite any existing files upon extraction{0}.\n" + + " -n do not overwrite any existing files upon extraction{1}.\n", + (Overwrite == 1) ? " (default)" : "", + (Overwrite == 2) ? " (default)" : ""); + + string removeString = PostUnpackCmdLineIsSet() + ? String.Format(" -r+ remove files after the optional post-unpack exe completes{0}.\n" + + " -r- don't remove files after the optional post-unpack exe completes{1}.\n", + RemoveFilesAfterExe ? " (default)" : "", + RemoveFilesAfterExe ? "" : " (default)") + : ""; + + string verbString = String.Format(" -v- turn OFF verbose messages{0}.\n"+ + " -v+ turn ON verbose messages{1}.\n", + Verbose ? "" : " (default)", + Verbose ? " (default)" : ""); + + string cmdString = PostUnpackCmdLineIsSet() + ? String.Format(" -x don't run the post-unpack exe.\n [cmd is: {0}]\n", + PostUnpackCmdLine) + : "" ; + + Console.WriteLine(more, overwriteString, removeString, cmdString, verbString); + + + if (TargetDirectoryIsSet()) + Console.WriteLine(" default extract dir: [{0}]\n", TargetDirectory); + + + Console.WriteLine(" {0} -l", s); + Console.WriteLine(" Lists entries in the archive."); + FreeConsole(); + Environment.Exit(1); + } + + + [STAThread] + public static int Main(string[] args) + { + int left = 0; + int top = 0; + try + { + left = Console.CursorLeft; + top = Console.CursorTop; + } + catch { } // suppress + + bool wantPause = (left==0 && top==0); + int rc = 0; + try + { + CommandLineSelfExtractor me = new CommandLineSelfExtractor(args); + + // Hide my own console window if there is no parent console + // (which means, it was launched rom explorer). + if (!me.Verbose) + { + IntPtr myHandle = Process.GetCurrentProcess().MainWindowHandle; + ShowWindow(myHandle, SW_HIDE); + } + + rc = me.Run(); + + // If there was an error, and this is a new console, and + // we're still displaying the console, then do a + // ReadLine. This gives the user a chance to read the + // window error messages before dismissing. + if (rc != 0 && wantPause && me.Verbose) + { + //Console.WriteLine("rc({0}) wantPause({1}) verbose({2})", rc, wantPause, me.Verbose); + Console.Write(" to continue..."); + Console.ReadLine(); + } + + } + catch (System.Exception exc1) + { + Console.WriteLine("Exception while extracting: {0}", exc1.ToString()); + rc = 255; + } + + FreeConsole(); + return rc; + } + + private static readonly int SW_HIDE= 0; + + [System.Runtime.InteropServices.DllImport("user32.dll")] + private static extern Boolean ShowWindow(IntPtr hWnd, Int32 nCmdShow); + + [System.Runtime.InteropServices.DllImport("kernel32.dll")] + private static extern bool AttachConsole(int pid); + + [System.Runtime.InteropServices.DllImport("kernel32.dll")] + private static extern bool AllocConsole(); + + [System.Runtime.InteropServices.DllImport("kernel32.dll")] + private static extern bool FreeConsole(); + + } +} diff --git a/DotNetZip/Zip/Resources/FolderBrowserDialogEx.cs b/DotNetZip/Zip/Resources/FolderBrowserDialogEx.cs new file mode 100644 index 0000000..42421d5 --- /dev/null +++ b/DotNetZip/Zip/Resources/FolderBrowserDialogEx.cs @@ -0,0 +1,548 @@ +// FolderBrowserDialogEx.cs +// +// A replacement for the builtin System.Windows.Forms.FolderBrowserDialog class. +// This one includes an edit box, and also displays the full path in the edit box. +// +// based on code from http://support.microsoft.com/default.aspx?scid=kb;[LN];306285 +// +// 20 Feb 2009 +// +// ======================================================================================== +// Example usage: +// +// string _folderName = "c:\\dinoch"; +// private void button1_Click(object sender, EventArgs e) +// { +// _folderName = (System.IO.Directory.Exists(_folderName)) ? _folderName : ""; +// var dlg1 = new Ionic.Utils.FolderBrowserDialogEx +// { +// Description = "Select a folder for the extracted files:", +// ShowNewFolderButton = true, +// ShowEditBox = true, +// //NewStyle = false, +// SelectedPath = _folderName, +// ShowFullPathInEditBox= false, +// }; +// dlg1.RootFolder = System.Environment.SpecialFolder.MyComputer; +// +// var result = dlg1.ShowDialog(); +// +// if (result == DialogResult.OK) +// { +// _folderName = dlg1.SelectedPath; +// this.label1.Text = "The folder selected was: "; +// this.label2.Text = _folderName; +// } +// } +// + + +namespace Ionic.Utils +{ +using System; +using System.Windows.Forms; +using System.Runtime.InteropServices; +using System.ComponentModel; +using System.Security.Permissions; +using System.Security; +using System.Threading; + + //[Designer("System.Windows.Forms.Design.FolderBrowserDialogDesigner, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"), DefaultEvent("HelpRequest"), SRDescription("DescriptionFolderBrowserDialog"), DefaultProperty("SelectedPath")] + public class FolderBrowserDialogEx : System.Windows.Forms.CommonDialog + { + private static readonly int MAX_PATH = 260; + + // Fields + private PInvoke.BrowseFolderCallbackProc _callback; + private string _descriptionText; + private Environment.SpecialFolder _rootFolder; + private string _selectedPath; + private bool _selectedPathNeedsCheck; + private bool _showNewFolderButton; + private bool _showEditBox; + private bool _showBothFilesAndFolders; + private bool _newStyle = true; + private bool _showFullPathInEditBox = true; + private bool _dontIncludeNetworkFoldersBelowDomainLevel; + private int _uiFlags; + private IntPtr _hwndEdit; + private IntPtr _rootFolderLocation; + + // Events + //[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler HelpRequest + { + add + { + base.HelpRequest += value; + } + remove + { + base.HelpRequest -= value; + } + } + + // ctor + public FolderBrowserDialogEx() + { + this.Reset(); + } + + // Factory Methods + public static FolderBrowserDialogEx PrinterBrowser() + { + FolderBrowserDialogEx x = new FolderBrowserDialogEx(); + // avoid MBRO comppiler warning when passing _rootFolderLocation as a ref: + x.BecomePrinterBrowser(); + return x; + } + + public static FolderBrowserDialogEx ComputerBrowser() + { + FolderBrowserDialogEx x = new FolderBrowserDialogEx(); + // avoid MBRO comppiler warning when passing _rootFolderLocation as a ref: + x.BecomeComputerBrowser(); + return x; + } + + + // Helpers + private void BecomePrinterBrowser() + { + _uiFlags += BrowseFlags.BIF_BROWSEFORPRINTER; + Description = "Select a printer:"; + PInvoke.Shell32.SHGetSpecialFolderLocation(IntPtr.Zero, CSIDL.PRINTERS, ref this._rootFolderLocation); + ShowNewFolderButton = false; + ShowEditBox = false; + } + + private void BecomeComputerBrowser() + { + _uiFlags += BrowseFlags.BIF_BROWSEFORCOMPUTER; + Description = "Select a computer:"; + PInvoke.Shell32.SHGetSpecialFolderLocation(IntPtr.Zero, CSIDL.NETWORK, ref this._rootFolderLocation); + ShowNewFolderButton = false; + ShowEditBox = false; + } + + + private class CSIDL + { + public const int PRINTERS = 4; + public const int NETWORK = 0x12; + } + + private class BrowseFlags + { + public const int BIF_DEFAULT = 0x0000; + public const int BIF_BROWSEFORCOMPUTER = 0x1000; + public const int BIF_BROWSEFORPRINTER = 0x2000; + public const int BIF_BROWSEINCLUDEFILES = 0x4000; + public const int BIF_BROWSEINCLUDEURLS = 0x0080; + public const int BIF_DONTGOBELOWDOMAIN = 0x0002; + public const int BIF_EDITBOX = 0x0010; + public const int BIF_NEWDIALOGSTYLE = 0x0040; + public const int BIF_NONEWFOLDERBUTTON = 0x0200; + public const int BIF_RETURNFSANCESTORS = 0x0008; + public const int BIF_RETURNONLYFSDIRS = 0x0001; + public const int BIF_SHAREABLE = 0x8000; + public const int BIF_STATUSTEXT = 0x0004; + public const int BIF_UAHINT = 0x0100; + public const int BIF_VALIDATE = 0x0020; + public const int BIF_NOTRANSLATETARGETS = 0x0400; + } + + private static class BrowseForFolderMessages + { + // messages FROM the folder browser + public const int BFFM_INITIALIZED = 1; + public const int BFFM_SELCHANGED = 2; + public const int BFFM_VALIDATEFAILEDA = 3; + public const int BFFM_VALIDATEFAILEDW = 4; + public const int BFFM_IUNKNOWN = 5; + + // messages TO the folder browser + public const int BFFM_SETSTATUSTEXT = 0x464; + public const int BFFM_ENABLEOK = 0x465; + public const int BFFM_SETSELECTIONA = 0x466; + public const int BFFM_SETSELECTIONW = 0x467; + } + + private int FolderBrowserCallback(IntPtr hwnd, int msg, IntPtr lParam, IntPtr lpData) + { + switch (msg) + { + case BrowseForFolderMessages.BFFM_INITIALIZED: + if (this._selectedPath.Length != 0) + { + PInvoke.User32.SendMessage(new HandleRef(null, hwnd), BrowseForFolderMessages.BFFM_SETSELECTIONW, 1, this._selectedPath); + if (this._showEditBox && this._showFullPathInEditBox) + { + // get handle to the Edit box inside the Folder Browser Dialog + _hwndEdit = PInvoke.User32.FindWindowEx(new HandleRef(null, hwnd), IntPtr.Zero, "Edit", null); + PInvoke.User32.SetWindowText(_hwndEdit, this._selectedPath); + } + } + break; + + case BrowseForFolderMessages.BFFM_SELCHANGED: + IntPtr pidl = lParam; + if (pidl != IntPtr.Zero) + { + if (((_uiFlags & BrowseFlags.BIF_BROWSEFORPRINTER) == BrowseFlags.BIF_BROWSEFORPRINTER) || + ((_uiFlags & BrowseFlags.BIF_BROWSEFORCOMPUTER) == BrowseFlags.BIF_BROWSEFORCOMPUTER)) + { + // we're browsing for a printer or computer, enable the OK button unconditionally. + PInvoke.User32.SendMessage(new HandleRef(null, hwnd), BrowseForFolderMessages.BFFM_ENABLEOK, 0, 1); + } + else + { + IntPtr pszPath = Marshal.AllocHGlobal(MAX_PATH * Marshal.SystemDefaultCharSize); + bool haveValidPath = PInvoke.Shell32.SHGetPathFromIDList(pidl, pszPath); + String displayedPath = Marshal.PtrToStringAuto(pszPath); + Marshal.FreeHGlobal(pszPath); + // whether to enable the OK button or not. (if file is valid) + PInvoke.User32.SendMessage(new HandleRef(null, hwnd), BrowseForFolderMessages.BFFM_ENABLEOK, 0, haveValidPath ? 1 : 0); + + // Maybe set the Edit Box text to the Full Folder path + if (haveValidPath && !String.IsNullOrEmpty(displayedPath)) + { + if (_showEditBox && _showFullPathInEditBox) + { + if (_hwndEdit != IntPtr.Zero) + PInvoke.User32.SetWindowText(_hwndEdit, displayedPath); + } + + if ((_uiFlags & BrowseFlags.BIF_STATUSTEXT) == BrowseFlags.BIF_STATUSTEXT) + PInvoke.User32.SendMessage(new HandleRef(null, hwnd), BrowseForFolderMessages.BFFM_SETSTATUSTEXT, 0, displayedPath); + } + } + } + break; + } + return 0; + } + + private static PInvoke.IMalloc GetSHMalloc() + { + PInvoke.IMalloc[] ppMalloc = new PInvoke.IMalloc[1]; + PInvoke.Shell32.SHGetMalloc(ppMalloc); + return ppMalloc[0]; + } + + public override void Reset() + { + this._rootFolder = (Environment.SpecialFolder)0; + this._descriptionText = string.Empty; + this._selectedPath = string.Empty; + this._selectedPathNeedsCheck = false; + this._showNewFolderButton = true; + this._showEditBox = true; + this._newStyle = true; + this._dontIncludeNetworkFoldersBelowDomainLevel = false; + this._hwndEdit = IntPtr.Zero; + this._rootFolderLocation = IntPtr.Zero; + } + + protected override bool RunDialog(IntPtr hWndOwner) + { + bool result = false; + if (_rootFolderLocation == IntPtr.Zero) + { + PInvoke.Shell32.SHGetSpecialFolderLocation(hWndOwner, (int)this._rootFolder, ref _rootFolderLocation); + if (_rootFolderLocation == IntPtr.Zero) + { + PInvoke.Shell32.SHGetSpecialFolderLocation(hWndOwner, 0, ref _rootFolderLocation); + if (_rootFolderLocation == IntPtr.Zero) + { + throw new InvalidOperationException("FolderBrowserDialogNoRootFolder"); + } + } + } + _hwndEdit = IntPtr.Zero; + //_uiFlags = 0; + if (_dontIncludeNetworkFoldersBelowDomainLevel) + _uiFlags += BrowseFlags.BIF_DONTGOBELOWDOMAIN; + if (this._newStyle) + _uiFlags += BrowseFlags.BIF_NEWDIALOGSTYLE; + if (!this._showNewFolderButton) + _uiFlags += BrowseFlags.BIF_NONEWFOLDERBUTTON; + if (this._showEditBox) + _uiFlags += BrowseFlags.BIF_EDITBOX; + if (this._showBothFilesAndFolders) + _uiFlags += BrowseFlags.BIF_BROWSEINCLUDEFILES; + + + if (Control.CheckForIllegalCrossThreadCalls && (Application.OleRequired() != ApartmentState.STA)) + { + throw new ThreadStateException("DebuggingException: ThreadMustBeSTA"); + } + IntPtr pidl = IntPtr.Zero; + IntPtr hglobal = IntPtr.Zero; + IntPtr pszPath = IntPtr.Zero; + try + { + PInvoke.BROWSEINFO browseInfo = new PInvoke.BROWSEINFO(); + hglobal = Marshal.AllocHGlobal(MAX_PATH * Marshal.SystemDefaultCharSize); + pszPath = Marshal.AllocHGlobal(MAX_PATH * Marshal.SystemDefaultCharSize); + this._callback = new PInvoke.BrowseFolderCallbackProc(this.FolderBrowserCallback); + browseInfo.pidlRoot = _rootFolderLocation; + browseInfo.Owner = hWndOwner; + browseInfo.pszDisplayName = hglobal; + browseInfo.Title = this._descriptionText; + browseInfo.Flags = _uiFlags; + browseInfo.callback = this._callback; + browseInfo.lParam = IntPtr.Zero; + browseInfo.iImage = 0; + pidl = PInvoke.Shell32.SHBrowseForFolder(browseInfo); + if (((_uiFlags & BrowseFlags.BIF_BROWSEFORPRINTER) == BrowseFlags.BIF_BROWSEFORPRINTER) || + ((_uiFlags & BrowseFlags.BIF_BROWSEFORCOMPUTER) == BrowseFlags.BIF_BROWSEFORCOMPUTER)) + { + this._selectedPath = Marshal.PtrToStringAuto(browseInfo.pszDisplayName); + result = true; + } + else + { + if (pidl != IntPtr.Zero) + { + PInvoke.Shell32.SHGetPathFromIDList(pidl, pszPath); + this._selectedPathNeedsCheck = true; + this._selectedPath = Marshal.PtrToStringAuto(pszPath); + result = true; + } + } + } + finally + { + PInvoke.IMalloc sHMalloc = GetSHMalloc(); + sHMalloc.Free(_rootFolderLocation); + _rootFolderLocation = IntPtr.Zero; + if (pidl != IntPtr.Zero) + { + sHMalloc.Free(pidl); + } + if (pszPath != IntPtr.Zero) + { + Marshal.FreeHGlobal(pszPath); + } + if (hglobal != IntPtr.Zero) + { + Marshal.FreeHGlobal(hglobal); + } + this._callback = null; + } + return result; + } + + // Properties + //[SRDescription("FolderBrowserDialogDescription"), SRCategory("CatFolderBrowsing"), Browsable(true), DefaultValue(""), Localizable(true)] + + /// + /// This description appears near the top of the dialog box, providing direction to the user. + /// + public string Description + { + get + { + return this._descriptionText; + } + set + { + this._descriptionText = (value == null) ? string.Empty : value; + } + } + + //[Localizable(false), SRCategory("CatFolderBrowsing"), SRDescription("FolderBrowserDialogRootFolder"), TypeConverter(typeof(SpecialFolderEnumConverter)), Browsable(true), DefaultValue(0)] + public Environment.SpecialFolder RootFolder + { + get + { + return this._rootFolder; + } + set + { + if (!Enum.IsDefined(typeof(Environment.SpecialFolder), value)) + { + throw new InvalidEnumArgumentException("value", (int)value, typeof(Environment.SpecialFolder)); + } + this._rootFolder = value; + } + } + + //[Browsable(true), SRDescription("FolderBrowserDialogSelectedPath"), SRCategory("CatFolderBrowsing"), DefaultValue(""), Editor("System.Windows.Forms.Design.SelectedPathEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor)), Localizable(true)] + + /// + /// Set or get the selected path. + /// + public string SelectedPath + { + get + { + if (((this._selectedPath != null) && (this._selectedPath.Length != 0)) && this._selectedPathNeedsCheck) + { + new FileIOPermission(FileIOPermissionAccess.PathDiscovery, this._selectedPath).Demand(); + this._selectedPathNeedsCheck = false; + } + return this._selectedPath; + } + set + { + this._selectedPath = (value == null) ? string.Empty : value; + this._selectedPathNeedsCheck = true; + } + } + + //[SRDescription("FolderBrowserDialogShowNewFolderButton"), Localizable(false), Browsable(true), DefaultValue(true), SRCategory("CatFolderBrowsing")] + + /// + /// Enable or disable the "New Folder" button in the browser dialog. + /// + public bool ShowNewFolderButton + { + get + { + return this._showNewFolderButton; + } + set + { + this._showNewFolderButton = value; + } + } + + /// + /// Show an "edit box" in the folder browser. + /// + /// + /// The "edit box" normally shows the name of the selected folder. + /// The user may also type a pathname directly into the edit box. + /// + /// + public bool ShowEditBox + { + get + { + return this._showEditBox; + } + set + { + this._showEditBox = value; + } + } + + /// + /// Set whether to use the New Folder Browser dialog style. + /// + /// + /// The new style is resizable and includes a "New Folder" button. + /// + public bool NewStyle + { + get + { + return this._newStyle; + } + set + { + this._newStyle = value; + } + } + + + public bool DontIncludeNetworkFoldersBelowDomainLevel + { + get { return _dontIncludeNetworkFoldersBelowDomainLevel; } + set { _dontIncludeNetworkFoldersBelowDomainLevel = value; } + } + + /// + /// Show the full path in the edit box as the user selects it. + /// + /// + /// This works only if ShowEditBox is also set to true. + /// + public bool ShowFullPathInEditBox + { + get { return _showFullPathInEditBox; } + set { _showFullPathInEditBox = value; } + } + + public bool ShowBothFilesAndFolders + { + get { return _showBothFilesAndFolders; } + set { _showBothFilesAndFolders = value; } + } + } + + + + internal static class PInvoke + { + static PInvoke() { } + + public delegate int BrowseFolderCallbackProc(IntPtr hwnd, int msg, IntPtr lParam, IntPtr lpData); + + internal static class User32 + { + [DllImport("user32.dll", CharSet = CharSet.Auto)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, string lParam); + + [DllImport("user32.dll", CharSet = CharSet.Auto)] + public static extern IntPtr SendMessage(HandleRef hWnd, int msg, int wParam, int lParam); + + [DllImport("user32.dll", SetLastError = true)] + //public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow); + //public static extern IntPtr FindWindowEx(HandleRef hwndParent, HandleRef hwndChildAfter, string lpszClass, string lpszWindow); + public static extern IntPtr FindWindowEx(HandleRef hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow); + + [DllImport("user32.dll", SetLastError = true)] + public static extern Boolean SetWindowText(IntPtr hWnd, String text); + } + + [ComImport, Guid("00000002-0000-0000-c000-000000000046"), SuppressUnmanagedCodeSecurity, InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IMalloc + { + [PreserveSig] + IntPtr Alloc(int cb); + [PreserveSig] + IntPtr Realloc(IntPtr pv, int cb); + [PreserveSig] + void Free(IntPtr pv); + [PreserveSig] + int GetSize(IntPtr pv); + [PreserveSig] + int DidAlloc(IntPtr pv); + [PreserveSig] + void HeapMinimize(); + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public class BROWSEINFO + { + public IntPtr Owner; + public IntPtr pidlRoot; + public IntPtr pszDisplayName; + public string Title; + public int Flags; + public BrowseFolderCallbackProc callback; + public IntPtr lParam; + public int iImage; + } + + + + [SuppressUnmanagedCodeSecurity] + internal static class Shell32 + { + // Methods + [DllImport("shell32.dll", CharSet = CharSet.Auto)] + public static extern IntPtr SHBrowseForFolder([In] PInvoke.BROWSEINFO lpbi); + [DllImport("shell32.dll")] + public static extern int SHGetMalloc([Out, MarshalAs(UnmanagedType.LPArray)] PInvoke.IMalloc[] ppMalloc); + [DllImport("shell32.dll", CharSet = CharSet.Auto)] + public static extern bool SHGetPathFromIDList(IntPtr pidl, IntPtr pszPath); + [DllImport("shell32.dll")] + public static extern int SHGetSpecialFolderLocation(IntPtr hwnd, int csidl, ref IntPtr ppidl); + } + + } +} diff --git a/DotNetZip/Zip/Resources/PasswordDialog.Designer.cs b/DotNetZip/Zip/Resources/PasswordDialog.Designer.cs new file mode 100644 index 0000000..dc3bfc6 --- /dev/null +++ b/DotNetZip/Zip/Resources/PasswordDialog.Designer.cs @@ -0,0 +1,126 @@ +namespace Ionic.Zip.Forms +{ + partial class PasswordDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(PasswordDialog)); + this.button1 = new System.Windows.Forms.Button(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.prompt = new System.Windows.Forms.Label(); + this.btnCancel = new System.Windows.Forms.Button(); + this.button2 = new System.Windows.Forms.Button(); + this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); + this.SuspendLayout(); + // + // button1 + // + this.button1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.button1.Location = new System.Drawing.Point(218, 35); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(38, 23); + this.button1.TabIndex = 10; + this.button1.Text = "OK"; + this.toolTip1.SetToolTip(this.button1, "Use the specirfied password for extraction of this entry and subsequent entries."); + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.btnOk_Click); + // + // textBox1 + // + this.textBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textBox1.Location = new System.Drawing.Point(12, 37); + this.textBox1.Name = "textBox1"; + this.textBox1.PasswordChar = '*'; + this.textBox1.Size = new System.Drawing.Size(200, 20); + this.textBox1.TabIndex = 0; + // + // prompt + // + this.prompt.AutoSize = true; + this.prompt.Location = new System.Drawing.Point(12, 15); + this.prompt.Name = "prompt"; + this.prompt.Size = new System.Drawing.Size(138, 13); + this.prompt.TabIndex = 20; + this.prompt.Text = "Enter the password for Xxxx"; + // + // btnCancel + // + this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.btnCancel.Location = new System.Drawing.Point(311, 35); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(58, 23); + this.btnCancel.TabIndex = 30; + this.btnCancel.Text = "Cancel"; + this.toolTip1.SetToolTip(this.btnCancel, "Cancel extraction of this and all subsequent entries"); + this.btnCancel.UseVisualStyleBackColor = true; + this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click); + // + // button2 + // + this.button2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.button2.Location = new System.Drawing.Point(261, 35); + this.button2.Name = "button2"; + this.button2.Size = new System.Drawing.Size(45, 23); + this.button2.TabIndex = 20; + this.button2.Text = "Skip"; + this.toolTip1.SetToolTip(this.button2, "Skip extraction of this entry"); + this.button2.UseVisualStyleBackColor = true; + this.button2.Click += new System.EventHandler(this.button2_Click); + // + // PasswordDialog + // + this.AcceptButton = this.button1; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(381, 69); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.button2); + this.Controls.Add(this.button1); + this.Controls.Add(this.textBox1); + this.Controls.Add(this.prompt); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "PasswordDialog"; + this.Text = "Password for zip extraction?"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button button1; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.Label prompt; + private System.Windows.Forms.Button btnCancel; + private System.Windows.Forms.Button button2; + private System.Windows.Forms.ToolTip toolTip1; + } +} \ No newline at end of file diff --git a/DotNetZip/Zip/Resources/PasswordDialog.cs b/DotNetZip/Zip/Resources/PasswordDialog.cs new file mode 100644 index 0000000..53cb5c7 --- /dev/null +++ b/DotNetZip/Zip/Resources/PasswordDialog.cs @@ -0,0 +1,79 @@ +// PasswordDialog.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2009 Dino Chiesa +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// + +namespace Ionic.Zip.Forms +{ + using System; + using System.Collections.Generic; + using System.Windows.Forms; + + public partial class PasswordDialog : Form + { + public enum PasswordDialogResult { OK, Skip, Cancel }; + + public PasswordDialog() + { + InitializeComponent(); + this.textBox1.Focus(); + } + + public PasswordDialogResult Result + { + get + { + return _result; + } + } + + public string EntryName + { + set + { + prompt.Text = "Enter the password for " + value; + } + } + public string Password + { + get + { + return textBox1.Text; + } + } + + private void btnOk_Click(object sender, EventArgs e) + { + _result = PasswordDialogResult.OK; + this.Close(); + } + + private void btnCancel_Click(object sender, EventArgs e) + { + _result = PasswordDialogResult.Cancel; + this.Close(); + } + private void button2_Click(object sender, EventArgs e) + { + _result = PasswordDialogResult.Skip; + this.Close(); + } + + + private PasswordDialogResult _result; + + + } +} diff --git a/DotNetZip/Zip/Resources/PasswordDialog.resx b/DotNetZip/Zip/Resources/PasswordDialog.resx new file mode 100644 index 0000000..1f4b4fb --- /dev/null +++ b/DotNetZip/Zip/Resources/PasswordDialog.resx @@ -0,0 +1,1398 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + + + AAABAAoAMDAQAAEABABoBgAApgAAACAgEAABAAQA6AIAAA4HAAAQEBAAAQAEACgBAAD2CQAAMDAAAAEA + CACoDgAAHgsAACAgAAABAAgAqAgAAMYZAAAQEAAAAQAIAGgFAABuIgAAAAAAAAEAIAD1xgAA1icAADAw + AAABACAAqCUAAMvuAAAgIAAAAQAgAKgQAABzFAEAEBAAAAEAIABoBAAAGyUBACgAAAAwAAAAYAAAAAEA + BAAAAAAAgAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAACAAAAAgIAAgAAAAIAAgACAgAAAgICAAMDA + wAAAAP8AAP8AAAD//wD/AAAA/wD/AP//AAD///8AAAAAAAAAAAAAAAg3EzAAAAAAAAAAAAAAAAAAAAAA + AIczEzEzM3FwAAAAAAAAAAAAAAAAAABzEzMTMzczFzczAAAAAAAAAAAAAAAAAHM4NzNzd3t4uLc3NwAA + AAAAAAAAAAAIMTe4t4uLi3e3mIuDMwAAAAAAAAAAAAcxe4uIi3mDebeTO3t7eQAAAAAAAAAABzN7eLiI + tbc5NzEzc3l4OAAAAAAAAAAAA4t4uIt4MzMzMzNzOLM5twAAAAAAAAAAB7i4i3e4lzd7d7e3i3gzMwAA + AAAAAAAACIt7mLh4uLe3e3mLmLeDeQAAAAAAAAAACLd4t4uPg4mJtTM3N5O3twAAAAAAAAAAC3e4i4iI + uTMzMzcTODN5cwAAAAAAAAAAB7h7iLi4M3Nzc5e3mLczNwAAAAAAAAAACIuJg4mIt7i3uLd7i4lzMwAA + AAAAAAAAC3mLi4uPe3mLd3tze3uDeQAAAAAAAAAACLi4uIiPt3t5OTE5NzOYtwAAAAAAAAAAB7h4iIuI + OTM3MzNze3MzewAAAAAAAAAACLi4uLi4tzebd7i4uIlzMQAAAAAAAAAACLiLeLiIi4uHuJg4mLezcwAA + AAAAAAAACHt4uIiIt5i3N7OXtzc3lwAAAAAAAAAAC4mLiIiIg3M5MxczOJM7ewAAAAAAAAAACLiIiLi4 + M7l4uIv/iItTNwAAAAAAAAAACIuLi4uPj4+4i3czOz+DOQAAAAAAAAAAC4i4iI+LczczkzkxMTG4gwAA + AAAAAAAACLiIi3M3t7mLd7c/NxNxuAAAAAAAAAAACIi3MxOYN3t3uDc/MTcwAAAAAAAAAAAACxMTPzcz + OXM5M5M4czAAAAAAAAAAAAAAAAB7GDsTc7e3gAA4MTAAAAAAAAAAAAAAAAAAP3cwAAAAAAA4NzAAAAAA + AAAAAAAAAAAAODgwAAAAAAA4OTAAAAAAAAAAAAAAAAAAOLcwAAAAAAA4M3AAAAAAAAAAAAAAAAAAOJgw + AAAAAAA4MzAAAAAAAAAAAAAAAAAAOLgwAAAAAAA4kzAAAAAAAAAAAAAAAAAAODgwAAAAAAA7c3AAAAAA + AAAAAAAAAAAAOLiQAAAAAAA3kzAAAAAAAAAAAAAAAAAAOJgwAAAAAAA4ODAAAAAAAAAAAAAAAAAAM7gw + AAAAAAAzmxAAAAAAAAAAAAAAAAAANTgwAAAAAAA3ODAAAAAAAAAAAAAAAAAAMziwAAAAAAc5ODAAAAAA + AAAAAAAAAAAAcxtzAAAAAAMzczAAAAAAAAAAAAAAAAAAgxOJgAAAADMTg4AAAAAAAAAAAAAAAAAAAzE4 + MwAAA3EXOQAAAAAAAAAAAAAAAAAACDcTtzMzOTA4OAAAAAAAAAAAAAAAAAAAADODE3lzczGDcAAAAAAA + AAAAAAAAAAAAAAM4t7e3txc3AAAAAAAAAAAAAAAAAAAAAABze4j3EzOAAAAAAAAAAAAAAAAAAAAAAAAA + NTMTM4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///4H///AAD//AAH//8AAP/A + AAP//wAA/wAAAP//AAD4AAAA//8AAOAAAAD//wAAgAAAAP//AACAAAAA//8AAIAAAAD//wAAgAAAAP// + AACAAAAA//8AAIAAAAD//wAAgAAAAP//AACAAAAA//8AAIAAAAD//wAAgAAAAP//AACAAAAA//8AAIAA + AAD//wAAgAAAAP//AACAAAAA//8AAIAAAAD//wAAgAAAAP//AACAAAAA//8AAIAAAAD//wAAgAAAAP// + AACAAAAH//8AAIAAAB///wAA8AAcH///AAD8H/wf//8AAPwf/B///wAA/B/8H///AAD8H/wf//8AAPwf + /B///wAA/B/8H///AAD8H/wf//8AAPwf/B///wAA/B/8H///AAD8H/wf//8AAPwf+B///wAA/A/4H/// + AAD8B/Af//8AAP4D4D///wAA/gAAP///AAD/AAB///8AAP+AAP///wAA/8AB////AAD/8Af///8AAP// + /////wAAKAAAACAAAABAAAAAAQAEAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAIAAAACA + gACAAAAAgACAAICAAACAgIAAwMDAAAAA/wAA/wAAAP//AP8AAAD/AP8A//8AAP///wAAAAAACHMxISEA + AAAAAAAAAAAAAzM3c3ODNwAAAAAAAAAAcxg3uYt7g3MwAAAAAAAAcze/e3ezk4t5cAAAAAAAAzi4iLWz + U3N5t7AAAAAAAAOLd7gzc7c7iDcwAAAAAAADiYt4i3eYN7i3MAAAAAAAA3uIiDl7M5OJN4AAAAAAAAOL + e7i3Nzg4uDMwAAAAAAAHt3iIi4t7c4t7cAAAAAAAA4uLiDk3k5OJN7AAAAAAAAOHuHi3N7d7iDNwAAAA + AAAHt4uIi4t7e4t5MAAAAAAAA4uIiLU5c5N5N4AAAAAAAAOIuL97i4uIuLMwAAAAAAAHuIiIt4e3MTF4 + sAAAAAAAC4uTM3uYN5PzMzAAAAAAAAeXPxN3t7cz8QAAAAAAAAAACDg4AAAAA4MAAAAAAAAAAAA4MAAA + AAODAAAAAAAAAAAAOzAAAAABgwAAAAAAAAAAADeQAAAAAzMAAAAAAAAAAAA3MAAAAANzAAAAAAAAAAAA + M3AAAAADlwAAAAAAAAAAADl4AAAAg3MAAAAAAAAAAAAzswAAADMzAAAAAAAAAAAAc3OAAAgTcwAAAAAA + AAAAAAMXNwBzM5AAAAAAAAAAAAAHM5MzMTdwAAAAAAAAAAAAAHOLiDM3AAAAAAAAAAAAAAAIN5MTgAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+AP//+AA//8AAH/8AAB/+AAAf/gAAH/4AAB/+AAAf/gAAH/4AA + B/+AAAf/gAAH/4AAB/+AAAf/gAAH/4AAB/+AAAf/gAA//+D+P//x/j//8f4///H+P//x/j//8f4///D8 + P//w/D//8Hg///gwf//4AH///AD///4B////////KAAAABAAAAAgAAAAAQAEAAAAAACAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAIAAAIAAAACAgACAAAAAgACAAICAAACAgIAAwMDAAAAA/wAA/wAAAP//AP8A + AAD/AP8A//8AAP///wADMzMzMzMzAAuLe4t7m4kAB7mzmzMzMwALe4uIt7mzAAe3k7k5NzMAC4uLi4s7 + gwADm3m3M5OTAAi4uLi3t7MAAzc3OTc3MwAAB7cAB7cAAAAHgwADlwAAAAODAAe3AAAAB7cAB7cAAAAH + iHd7lwAAAAA7i4NwAAAAAAd3mAAAAIADAACAAwAAgAMAAIADAACAAwAAgAMAAIADAACAAwAAgAMAAOOP + AADjjwAA448AAOOPAADgDwAA8B8AAPg/AAAoAAAAMAAAAGAAAAABAAgAAAAAAAAJAAAAAAAAAAAAAAAB + AAAAAQAAAAAAAAQTGwAGHCgACzVIAAw6TwANPlQADkBWAA5FXQASR18AFUZcACRLXQAPSGEAEEpjABBL + ZQAUTWUAEE5oABVOaQAaT2kAEFBrABFSbQAfUWoAElVyABJXdQASWHUAElp4ABNcegATXXwAFF9/ACxg + eQAwZX4AOGV6AEhrfQAUYYEAFGSFABVniAAYZ4kAFWiJABZqjAAWbY8AGGmKABhqjAAXbpAAGW6RABtx + kwAccpQAHnWWACNzlQAhd5gAIniZACR6mwAjepwAJHqcACZ8nQApepwAKH2eACh/oAAufqAAQHWOACmA + oQAsgaIALYOkAC+EpQA3g6QAMIWmAD2GpAAxhqgAM4ipADSJqgAziawANYusADeMrQA/iKgAO4uuADiN + rgA6j7AAO5CxADyRsgA9krQAP5S1AECCnwBQgpsAU4WeAFiIoABAlbYAQpe4AEOYuQBEmboARpu8AEec + vQBJmbsASJ2+AFOUsABWmrcAW5mzAFubtgBdmbUAZpOqAGeVqwB4lKIAZZ66AEqfwABLoMEATKHCAE6j + xABPpMUAUKXGAFKnyABTqMkAVKnKAFarzABWrM0AWa7OAFuw0ABcsdEAX7TUAGmsyQBhttYAZrraAGm+ + 3QBrwN8AbcLhAG/E4wBxxuUAc8jnAHTJ6AB5y+oAfMzqAImtvwCBscYAh7TIAI+5zACAzusAhtDsAInR + 7ACQ0+0Ak9XuAJfX7wCa2O8AndrwAKTc8QCu3/IAsuHzALTi8wC45PQAvub1AMHm9QDF6PYAyOn2AMzr + 9wDS7fgA2e/5ANzw+QDl9fsA6Pb7AO/4/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAH9RHAkGHgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAf1IdEAwTFhkhISExPQxiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgERAW + GSQkKSkkKSkkKSRMVDEtCgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOQcxiEMvLS8xPENVZGtvcHV1 + d1o3NhAfAAAAAAAAAAAAAAAAAAAAAAAAAAAAfxQQTGpqhW9vcHV1cHRva2tnZ1R0eXBwSi8hAAAAAAAA + AAAAAAAAAAAAAAAAAFEJJGRna3B7jXRrcG9raWdkVUM9PC1Kb290a1o9AAAAAAAAAAAAAAAAAAAAAABg + GUpnZ2t1eXl+kHVkTEM8MSQhGRkWFhdKTDdVa1VMAAAAAAAAAAAAAAAAAAAAAAAZcGlwd3l9fn12hEMp + KSEkISQkJC0xNkN2ajEtPGdMAAAAAAAAAAAAAAAAAAAAAAB0dHZ5e3t1dXBwhVQ8Q0NKTFVaWlpnaWl5 + dFpDISQkAAAAAAAAAAAAAAAAAAAAAACLhHd1cGtrcHd5kW9na2tnZ2dqZ1pna2t3dGlpTEEkAAAAAAAA + AAAAAAAAAAAAAACEcGpra3V1fX6MlHVnamlpVUo8MS0tJCRDQ0pqWlRMAAAAAAAAAAAAAAAAAAAAAABw + aWtweX1+iIyMjFU8PDEkJCEZGRkZISRvQxktTFRMAAAAAAAAAAAAAAAAAAAAAABpdXZ7fX17eXl1fkwt + LzE8MTw8QUNUWmp9azwkGTw3AAAAAAAAAAAAAAAAAAAAAAB0eXl2dXBwdHV5jWtVZ2dnZ2tvamtrcHB9 + dWdULRkWAAAAAAAAAAAAAAAAAAAAAAB5cHBrcHB2eX6HlXRpa3Rwb29rZ1VMVUxra2dkTEw8AAAAAAAA + AAAAAAAAAAAAAAB0b3B1dn19io2QlHBaZFVMQS8kISEhJCFMQS1DVVRMAAAAAAAAAAAAAAAAAAAAAABr + dXZ5foeLi4h+h0wxMS0kLSkkJCoxPEN1VCEhNlRMAAAAAAAAAAAAAAAAAAAAAAB1eX6HhYR9dnZ2ilU8 + QUFDSlNVZGdnb3R+cEopFyQvAAAAAAAAAAAAAAAAAAAAAAB5eXt5dXV1eXt+lXBnb3Bwb29vb29wcHB9 + dFVUPDEhAAAAAAAAAAAAAAAAAAAAAAB7dHR1d3l7h4uQl3lrcHBwa2RMQTxDQzxUVEpVVExDAAAAAAAA + AAAAAAAAAAAAAAB0dXV5foWLkZGRkWdDQzwtKiQkGRkkKjxpPBktTFRMAAAAAAAAAAAAAAAAAAAAAAB1 + eX2EjYuKhH15hUotMTxMVW9wfYSRlImQh0EWJENJAAAAAAAAAAAAAAAAAAAAAAB5hISFe3t2dn2NmJCU + kZCIfnp0b2tMJENBVZSKQxIhAAAAAAAAAAAAAAAAAAAAAACEd3Z1doWRmJV7dGc8PUNJQTw3MTEMExkT + ECFUiYUvAAAAAAAAAAAAAAAAAAAAAAB3dYWRmZR7TDExVX5DQ0NKTExMTEMFmSQTEhckEFprAAAAAAAA + AAAAAAAAAAAAAAB+motwVC8hGSQ8VVVkZFpVZ2p5ZDwWljETGTxbAAAAAAAAAAAAAAAAAAAAAAAAAABr + JCkhLROQLzwWMTc8PD08PDExPCEZlDwWIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABPQRmNPEwMGSEt + N0FMWnMAAAAhjTwWIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACSLQ28pAAAAAAAAAAAAAAAhiT0Z + JAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACSKTHQkAAAAAAAAAAAAAAAZhT0hIQAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAACSKTHUkAAAAAAAAAAAAAAAbez0hJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAACmFVXYkAAAAAAAAAAAAAAAhdj0kJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACSFVXkkAAAA + AAAAAAAAAAAhdDwtJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACmEVXkkAAAAAAAAAAAAAAAhbzwv + JAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACR5VXskAAAAAAAAAAAAAAAhaTw2JAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAACF5VX0kAAAAAAAAAAAAAAAhWjxDIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAABlDVX0kAAAAAAAAAAAAAAAhVDZnIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQkQ34tAAAA + AAAAAAAAAAAZSiFwIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQkKX1MAAAAAAAAAAAAAEoZMSRr + KQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADYqD1VrNwAAAAAAAAAAADEZFlRDQAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAIEkDCFwSlwAAAAAAAAAQSEFB30hgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAqLQM3az03AAAAAAAxLQUCVUwvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCKS0DMWc9 + JCQjJCotBwEZdiGCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQDdVFyE2NjY2MS0kCwVqKV8A + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADYxdHRVWlpnaXRDE0okXwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAABfJD1wh5CRZBcWKSmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAEAxKiEWExkqYwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//+B///wAA//wAB///AAD/wAAD//8AAP8A + AAD//wAA+AAAAP//AADgAAAA//8AAIAAAAD//wAAgAAAAP//AACAAAAA//8AAIAAAAD//wAAgAAAAP// + AACAAAAA//8AAIAAAAD//wAAgAAAAP//AACAAAAA//8AAIAAAAD//wAAgAAAAP//AACAAAAA//8AAIAA + AAD//wAAgAAAAP//AACAAAAA//8AAIAAAAD//wAAgAAAAP//AACAAAAA//8AAIAAAAD//wAAgAAAB/// + AACAAAAf//8AAPAAHB///wAA/B/8H///AAD8H/wf//8AAPwf/B///wAA/B/8H///AAD8H/wf//8AAPwf + /B///wAA/B/8H///AAD8H/wf//8AAPwf/B///wAA/B/8H///AAD8H/gf//8AAPwP+B///wAA/AfwH/// + AAD+A+A///8AAP4AAD///wAA/wAAf///AAD/gAD///8AAP/AAf///wAA//AH////AAD///////8AACgA + AAAgAAAAQAAAAAEACAAAAAAAAAQAAAAAAAAAAAAAAAEAAAABAAAAAAAACjFDAAszRQANPlQADUBXAA5D + WwAUQ1gAEEtlABtPZQAQTmgAEVJtABVRbAAfUWkAElVyABRWcwASV3UAElh1AB5ZdwASWngAE1x6ABRd + ewATXXwALGB5ABRhggAVZYYAG2aFABVniQAVaYoAFmqMABZtjwAbbI0AH2yNABlvkQAacJIAHHKUAB51 + lgAzcYoAL3OSACF2mAAieJkAJHqbACR7nAAmfJ0AKH6fADJ5mQA4f54AKX+gAFd0ggBCepQAKoChACyB + ogAug6QAL4SlADCFpgAxhqgAM4ipADSJqgA2i6wAN4ytADiNrgA6j7AAO5CxADyRsgA+k7QAP5S1AEeG + owBIiqcATY6pAFCPqgBUjagAQJW2AEKXuABDmLkARJm6AEabvABHnL0ASJ2+AHGcsQB6o7cAdaK4AEqf + wABLoMEATKHCAE+kxQBQpcYAUqfIAFOoyQBUqcoAVqvMAFeszQBZrs4AW7DQAFyx0QBftNQAf67DAGC1 + 1QBmutoAab7dAGvA3wBtwuEAccblAHPI5wB1yukAecvqAIGrvQCDrL8AjK/AAIa4zgCVu8sAlcHVAIDO + 6wCQzuYAhtDsAInR7ACQ0+0Ak9XuAJrY7wCd2vAApNzxAK7f8gCy4fMAuOT0AL7m9QDF6PYAyOn2AMzr + 9wDS7fgA3PD5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8AAAAAAAAAAAAAak0WDAYCBAII + AAAAAAAAAAAAAAAAAAAAAAAAAAAACxIoMT8/PzojWCgNLwAAAAAAAAAAAAAAAAAAAABOEQllRjo/PD8/ + Pz9kVCsbJAAAAAAAAAAAAAAAAAAwDRtUYHlaU1RUSj86Ll1YWEYoAAAAAAAAAAAAAAAACzxdX2FieUo8 + NigjGhsbXVM2SlQAAAAAAAAAAAAAAAASYmBgXV12PDE6Ojw/P0pwYUYoKAAAAAAAAAAAAAAAAEZYU1NY + YXtaVFRTSkY8PGBYU1AxAAAAAAAAAAAAAAAAI0pYYWd0e0c8LigjHRsdX0ojOmIAAAAAAAAAAAAAAAAo + X2FgX113PzY8PD9HS1NxYjwjHQAAAAAAAAAAAAAAAEdYWFhfZHxdVFhVU0tGP2JYS0cxAAAAAAAAAAAA + AAAAI1hgZnV3e0Y8NjEoHR0oYEsjNmQAAAAAAAAAAAAAAAArYWZmYmF4Rjo6Pz9GS1NzYDYaIwAAAAAA + AAAAAAAAAEpfX19kcX5dWFpYVFBLU2RaSjw2AAAAAAAAAAAAAAAAKF1kcXl7fEY8NisoIyMuWEcdLmIA + AAAAAAAAAAAAAAAuZnFwZnB7X1NYXWBiZF1dYVQbIwAAAAAAAAAAAAAAAEpicHd5cWdiX2BgW1guCQkN + PGBgAAAAAAAAAAAAAAAAWnBgMRsLP0tKS1NLSjYH/wUdGh8AAAAAAAAAAAAAAAA/LjoNfBAoPEZLVVg6 + KAt/GAAAAAAAAAAAAAAAAAAAAAAAbShzG2sAAAAAAAAAEHYbAAAAAAAAAAAAAAAAAAAAAAAAI2AjAAAA + AAAAAAANZCMAAAAAAAAAAAAAAAAAAAAAAAAdWCMAAAAAAAAAAA1aIwAAAAAAAAAAAAAAAAAAAAAAACM/ + JgAAAAAAAAAADUojAAAAAAAAAAAAAAAAAAAAAAAAHTEoAAAAAAAAAAAQPCYAAAAAAAAAAAAAAAAAAAAA + AAAYKzEAAAAAAAAAABI6GwAAAAAAAAAAAAAAAAAAAAAAABIdRmkAAAAAAABpGDobAAAAAAAAAAAAAAAA + AAAAAAAAGRtLLAAAAAAAACUYMR0AAAAAAAAAAAAAAAAAAAAAAABBEigxTwAAAABPEgs6LQAAAAAAAAAA + AAAAAAAAAAAAAAAYDTwoRQAARRoEHSMAAAAAAAAAAAAAAAAAAAAAAAAAAEMoIzEdEhIdEAsxQwAAAAAA + AAAAAAAAAAAAAAAAAAAAAEImS11vXTEYGkMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF4mOiMSDRpsAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+AP//+AA//8AAH/8AA + B/+AAAf/gAAH/4AAB/+AAAf/gAAH/4AAB/+AAAf/gAAH/4AAB/+AAAf/gAAH/4AAB/+AAAf/gAA//+D+ + P//x/j//8f4///H+P//x/j//8f4///D8P//w/D//8Hg///gwf//4AH///AD///4B////////KAAAABAA + AAAgAAAAAQAIAAAAAAAAAQAAAAAAAAAAAAAAAQAAAAEAAAAAAAA+epsAAXimAEdzjwBTdo8ARXSSAE59 + lwBEe5sAVXeQAFJ6kgBWeJEAUXyXAFN9mABbfpgAF4y5AByRvgBWgJgAXYObAEiJqQBZk64AbJCnAHGT + qQB7l6oAdZmuABCd2AAglcIAIpfEACSZxgApncoAK5/MACygzQAwpNEAM6fUADOr1wA1qtYAM6zZADmu + 2gA0sdwAO7DcADS04QA/tOAATrDSAG2xyQBDuOQARbrlAEe85wBJvukAa8LeAEvA6wBMwewAUcXwAFbL + 9QBe0/0AbcfjAGTI5gBkyOgAc8vkAGDV/wBk1f8AaNb/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wAAAgICAgICAgICAgICAgAAADAwLiws + LDQoHh4eKAIAAAAgIBsbGhkiDgICAg8CAAAAMDAwLiwsOSgeHh4oAgAAACAgHh0dGyQOAgICDwIAAAAw + MDIwLi45KB4eHigCAAAAICAgHx4dJg4CAgIPAgAAADAwMzIwMDsoHh4eKAIAAAACAgYGBgICAgoKBQIC + AAAAAAAKNwoAAAAFIwcAAAAAAAAADDcKAAAABSMBAAAAAAAAAAw3CgAAAAUjAQAAAAAAAAAQNhEAAAAS + Jw0AAAAAAAAAFDUqFxYTJRgUAAAAAAAAAAAMLzgvKRgMAAAAAAAAAAAAABUKBAgUAAAAAAAAgAMAAIAD + AACAAwAAgAMAAIADAACAAwAAgAMAAIADAACAAwAA448AAOOPAADjjwAA448AAOAPAADwHwAA+D8AAIlQ + TkcNChoKAAAADUlIRFIAAAEAAAABAAgGAAAAXHKoZgAAIABJREFUeJzsvWmUZNdVJvqd6Q4xR2bkVPMo + yZIHJA/gUWay2zZienoI3K9N222wWbDo5X6NaR7dr1ysRdMNNI1pQ2stwG6zus0rUOMJgwdsS9jgSdas + kuRSlUqqqszKyinmO53h/Tj33ojIGizs0hCZ8UmxMiviRuS9J+7ZZ+9vf3sfYIIJJphgggkmmGCCCSaY + YIIJJphgggkmmGCCCSaYYIIJJphgggkmmGCCCSaYYIIJJhhfkOf6BLYajh27leH4cdbDDI3Rod/JZzgo + 63iho5aWblFHjx7VV/scJ5ggw8QAfJc4duxWVjrf5V3ZpHBdlvTom2S/9Ral9QtjFe8FCDFagxAKYy6e + y9nzhND02zBGMOc0Z+LzVMx8yqyfucfbUdTn+Yqen79R3nbbHepZv8gJtiwmBuA7wLFjtzKcPevAdRnv + iTdFuv92bczLtFZUa11IEim0UVRrQwGAEgpCLj3UxhgQQmCMgR4YCE0MUSAq1kqHlHLNOb/bc+ofElL/ + 7dlWpHa9clc8MQYTfLeYGIB/Ao4dud4BZhxVYG9RqnvUdQuNYqlaqJQrTqlcZkmSoNvtYGNtFUHQh5TJ + yKpvjBn53ZjBc9nPYUMxYjQMlDYm1lr2GRWrnls54kh86vzU2eSXf/lkTAgGHz7BBE8TEwPwbWAMyIfe + t9eNg2nX8fE7RuPHvEKhPDs/7+3df5iU578PlLkgnAMaMCpCf+UhnDpxD7rdFpSyi7TWGjKJobWBUgpS + xkgSiSSR0EbD6NQAACCUgFIGxigYYyiWSpCJBAAolUBrZbQ0odKqSxn7qvau+bcL8eNnv4lbwglnMME/ + BRMDcAV88MheT7OX7yXx43+sgBdQRmvFQoEt7NpHDr34TXDLO8GdKkaH0UDHHcTBBcTds9CyB0BDxW20 + NjaQxDFarRa63S7Onl3EeitBEGrEiQaIAgXAGIEjCCpFCuEIFItFNBrTKBZ8JFKi3+tBSoUkiaGUUlKp + PgFajjf3NrJhvn6q9KpgYggmeDqYGIBL4AtHbub3oVmqgn4wkeoHOGNlzjn1PA+1qQauf+mbUZy6FlRU + AFyK6DfQsgMVLEPLDgwMjIqhZAwje9BaQUddSBkhjmOsr61jcWkJJ06ew1PnI4ShRJRoGGMgOEGlyDFd + E9i9UMXOnTtQKvlQWqPTbiMMQ0RxDK0SI5UOjCYbrj/7Ly5smK//yu9+rj8JDSa4EiYGYAjGgPzpe19V + 8mrk7UHQ/zUu6AxlnPmeB8/zMN2Yx47dB1He8SpQUQOhzuU+CVp2ocI1GB3aZ1QAAwPAwMgIRksACkYl + kFEHMu4himI0W02cPPkE7ntoERc2YvTDBEYbEAJwzlAvc1y3v4jrrjuMSqUCpSRarRaCfh9BGMJoaRKp + AqPJqvb3v9GJv3n6HUefDJ+tMZxgvDAxACnuvv0mcffavoM0eOIzXJA5zoXjuR4pFIuYnZnFdGMeojgD + t7QTvLQPlPm43PAZHUPFGzCyB6PjwfMqGDnGaJX+LmF0ZN8XdhAEfQRBH0+eOYd77/0WnlgK0evHiBMD + SglcQTFVc/G937OAw4cOoFoto9ftYX1jHb1eD2EQQuvESGXa1OjPN4n5V+/Bj7fJJCyYYBPYc30Czwcc + O3Jz6ULE3qWT1Q8LQeddr8DLpTLZsWMn9u49gOrsAbilOXC3AsoKIJSDEG5z9yMwMDqxbr/qA1oBGJpz + Rg79roCc+af28ygHoRSOV4DnuWhMVXBg/07smCtARV30AwWpDeJYI4gkziy2EfU2UK9XUa9XUatW4bgu + jNYwBsQAngGucRXedtxZv+dHXrvr/MfvelJigglSbGsDYAzIbPjqOnjvTkPMzwjhVIrFEpluTGPfvoOY + nttjJ75Tskw/8wFoQCsYo2AnfJw/tAqhZXfT5DeDnyZd8WEAo3MDkJ4NAG1DA0JBmQDjHJ7nYapWxt7d + s5iqUDSbfcSJgpRAHBusrEd46uwKalWBeq2CSqWKSqVi04oGMAbUQJWl7P0kZfGef/byhb//5JeX4qNH + n+XBnuB5iW0bAnzhCzfz01+ZPoT4yc9QRnY6jstKpRLmZufQmNsBt9gAYT4od/JY3xoAgBAGEAbQzH4a + QGvYPCBgTJw+lbr8Wg70AEbB6MwruFjHo+VQuG6sEdFJBJkECIMAyxdW8ZWv3o9HnuigG0goCXAOTFU9 + vO57d+NFL3wByuUSjDFot1pYvnAB3U4HURRDqSSRUi8xb/8bi4foiYmQaIJtaQCOHbvVUSdXfiKIux/g + nE27jkvqU1PYsWMXytUGuFcB5UUQygAqQDYNk8ncegOoJIAxEsYAWiUwKoJRGoQRIM3tw+gBF2AUtNFW + /aclCCGglIMwB4QJUJK9RYEwNuAJVAyjE4RhhHarhUcfO4mv3HMGF9YDJBKgBKiWOV5yXRWvePlLMTsz + DcYYgiDA8vIymhtNBEEAKSOjlVlx/Pmf7tZ2fvnd7/7j5NkY8wmen9h2BuDYsVud5NTKTyZx73bKWLVQ + KKBeq2P3nj3wSw1QUQRlLkCoNQBIVXpGAgaQSQAV96FkH0kSYn19Hd12C1GcoLmxDsE5mq0mCgUfjPH8 + 72ZKP6UkwiBErVaDlAqOI1AsV1AuFVEqV+E6DgghEIKDMQ7KCAgZfA6MRBIn6HS6ePLsOXzpHx/FmfM9 + hLEBBVDwGQ7uLuGHf/AVmJttQAgOpRTWVlaxfGEZvV4PiUyMlrLpOuVfCOfx8Xe8465JlmCbYlsZgGPH + bnX0qZWfDOPe7ZyLaqFQwMxMA3PzO+CXaqCikk5+AhvfaxgdQckYKuogDPtYX1/H4rlzWDy3iPNrMQoe + Rb1MUa6UwZjINf2X0/8bY2Bg8te11gjCHtrtCP1Qg3OK2ekiGjMzaDRmUCqVIAQD5wJCOGDMfmYSS4Rh + gPPnV/D5L92P0+c66IfWyBQ8goO7y3jtq1+Mvbt3wvd9SJmg1Wzi/PIyOu024jiB0rLjcPEHknd/62ff + +2Dv2fwuJnh+YNsYgGzyxzK4nTFeLRR8zMzMYH5hAY5bAnVKoNzG+EYnMDqBDDsIwx7W1tfx+ImTOPnE + ClrdBJwZVIsEjekSXFeAMwZKGQghly36uRxsTYCGlDY0kFKh0+5iraURJQTVMsOOuQr27NmDqak6fN+F + EA5o+neCIMTy8iq++OX78MS5LoLIGgHfJZhrFPHmH7wRe/bsgO/70Eqh0+lg6fwS2u02YssLBITgf7lJ + 7z23HT3evbqjPsHzHdsiC3D77T8n+NKZ/0Omk9/3fTQa05ibX4DrlUGdAgCbj9cyQNxbQ6e1hkcfewT/ + 8OVv4B++fhJPnN1AFCeYrTLsWqhgql6C57sQXIAxBkppbgC+kwdjDIwxCM7hug6qZQeuiNHuSpw+18W3 + Hj+PU6fPIOg3QUBBiAGhBIwzeK6DmUYVzY119IIEiQSkAqIoweLSGuYaJRSLPlzPh+t68DwXUkokSQIY + IrRS1yfc2fGO185//s/vWppwAtsIW94AHDt2KyMXmq8zUfPDjIua73toNBqYnZuHXyiBCg8gBDruI4la + aG+s4fjxR/HZL9yN+x9ewtJKAG00ZmsC+3eX0JiuwPc9CCHAvsNVfzOGDQGltgCIcwbPc1Etuyj5BmGo + sNaKcWapi8ceX8T584uQMoRwXHBK4LkCM9NlrKxuIAglpLZGII4TLF9Yw8xUCeVyEVy4cB0XnudZIxAn + MADXWl2nvULzHbdcf99HPv34JDuwTbClDYAxIF//2+uuIeH5T3HBpx3XQa1Wx/zCAorFAkAsy56EXXQ6 + Gzh16jT+9nNfxwOPnMfKegglNWplgUO7fSzMVVEqFu3EZ9lqj2fokRkCbg2B76JaduALhSDU6PYk1jZi + PHmuiTNPLUImfTiuC8/lqJQEVlbbiGINqQCpgSiSWFldx84ddRQLLhh34TguPNdBkiSpJwCRyORVkcLj + v/Tr+x798IefnKgGtwG2tAHYW31JjfY7/yCEmHdch1TKFezYYSc/oRxKJgj6XSwuLuHOu76Gr917BhfW + QoSRghAEO2cdHNo3hWq1BM/zwDkDpYOVmlIKmrrumRfw3YQBl3pQSsHT0KBQcFCvuiBGohtYY9DqJji3 + 3MVTZy5AJx04DocnEqyuJ1DKZiKlMkhiiVZzDXOzNRSLPrjrQ3ABz3MQRSGUUjDauErGr28uzf31x/7u + kbWjRyeFRFsdW5YE/MCRm0sl0v0Ud9xXC8FZqVzGjvkFVKoVUMYQxxLdTgePfusE7r7vDFbXQ8TSLnql + AsX+XS5mp2tpOm8g+SWEgFAKSigcx6r1hBDo93qIomik6ceVkHUButy/LwcpFaIowtpaE6fORWh1FZS2 + OgBHUJQLDDN1itWmwvK6RCztZ3JKUPQJbjhUwxt++DWYmpoC5R6Mkmi11rG0eA7tdgdJEptEqvPc3fED + b/23f/XYpJpwa2NLegB3336TCJXz8wb4WSG4UygUMDszi3LFTv4wjLCysoKvfuUb+MaD57G6ESFRBpQC + 9QrDNXs8zDam4fveRZOfMgbPdVEoFlGv11EsFNDpdBDF8bedwJbxvxipGSZZe7ArcQqUUnDO4XkuKiWK + OFGIY41YAok0iCKN1bZCP9RQygqMCbE/jQF6gYQgARqNKbiuC8I4BGfgnCO0ngAxWhVlvP5ji/fv/tOP + fPpcdDW+kwmen9hyBsAYkDvvfOXhONn4sCNExXU9VKs11KfqVhnXD3Dm3CLu/Pv7cOKpDro9BQNAMIKp + Ksd1B6qYmqrmkz9j9ymzufhiwTbnmJqagpIK6+vrCMMoV/cBg1Ze6fRWSulYaR0qrdtKqdMS5rQ2+pwy + +pyBWVRaca10RAD7PzRJcdnsghAcruOgWnSQyBhRoqFT4k/rVJkMjC7fhkBrg2YrRLlAUKtVIIQLyh04 + DgMlBGEYQhtDYFAIE8z9yGsOfW5SQLR1seVCgD/5T68qCyW/wpm4XghBSsUSFhZ2wHUdhGGEx771OO6+ + 70lcWA8QxnZ6OIygXuO4Zl8F9VoFQvDBKkwIKKFwXQfFUgnTU9NwXBfdbgcrKysI+gFUKteFMdBaw2gj + ldGh0nrZaP0Jl/kfDdwDp2c6D6ydaDmmXnVH5mWxEhG/GJP1duEQCcO3gLIfISDXc8Z8EDiUUnI5UZFS + Gp1uHyef3MDSaoQoNlBX4PAZAxxBMD/t440/dBMO7N8DxysDIIjDDs6dPYP19XXEcYwoitvGafzEqd5L + 75x0GNqa4N/+kPHBF47czM/DvN1QcpBzTjzXxVS9DkoJOp02Hj/5JL5y92lsdGxtPWAn/1SN49oDNdSq + ZQjBUyWgXckZZfB8D7VqFfUpq6/vtFtYXV21dfdpkY8x2milpdK6pZX8hEMb/7naWD4bLIXyuHqzfN+v + HzUEV46njxzBQwuLNz3SK/Lfry/cWCXyvtdoo3+TMbaLUepR6w7kxxNCwAlBpVzAoX0EhKxjaTVCGF05 + FIkTg9WNAF//xoNoTNcxJXwwx4NwCpidnUMiJVrNFowxlShZ+bPrvU/eAKD13Xw3Ezw/saU8gA8eeUmN + C+9Rx3HmHMdFpVrBVK2Ofhji3vsfwPFvNdHuSMTKThDBCKbTyV+vVcA5yyc/JTYfXygUMDs7g2KpDEII + up02lpcvIAgCJDJJ5cJaSq3XtVa/3+I3fNic/fu1X/5v33WnXnLs2K3UP37KbYrCdSDyTxhhL2CCuoTQ + 0e/Nxhno9gI8fnodixciRMmV/zSjBJUSw2u/dz9uuvF7UCrXQCiDlgFazSbOnTtn6waSWMok/qSUhbe+ + 4+ikZmCrYcsYgA8eudnjvP8RLpxbhHB4oVDA9PQ0jNF48MFHcO/DF9DqyrxAjxCgUqS4dn8Zjek6XFeA + UEv4ERBwzlAslTE3O4tSqQQQoNvp4vzyeXQ7XSitoLUySqpuIuUdceXQ/3vuS/ddOHrH8QTfZqX/p+LI + EdAD3R/23anODycgtzNGG8zmHof5Bigp0e0GePjEGlabMZQClL78qQhGMDvl481vuAkHDgxCARl3ceH8 + Mi6srlhDFyVNxWZf9bZf/dijk6zA1sKWIAGNAXnovpcfZLr/m8JxCo4QqFTsir64tIxvPnAWG+1kZPIX + fIJr9hYxMzMFz3Otlh823heCo1wuY35hPs0cUPT7fVxYXrYdeZWEUlJrpc9TSt84VbzwoZ/513/fvOv4 + yjOioLvrLpiPfeVUcsNrDz5eKuz8cxqv7wch+yilghACgpQcpASCcxQ9gmYnRhSnZceXmbKEEEitEPSb + 2LtnBzxPgFABQhg4IwiCPpSUMEa5Otm45bEvHP6TO+56Mr70p00wjtgSBuD61vf5ymnewRk/xBknruuh + WCqh0+nioYdP4NyFEMkQj13wCA7s8DA/PwXf80ZWfiE4SuUy5ufnUS6nacN+gOULy+h2OoiTBCpJYqnk + nV2y60c/dfenT/z739t4VvTzd931pP74Zx/ufObGqb+uCOeMhrmZErjGGII8QwAwRuGwCM2uhFRp+vIS + D8BmCxKpUCqQlOD0QCgDJVZb0Ov3IZUixhAPrvfIT73zpkfuuOP4xAvYIviONq98PsEYELlzx15OnRcy + zqkQHIVCAWEQYHFxEeeWQyTx4H51HYK5aQfz81Mo+D4oZaCE5it/qVTC/NwcSuUKCOVI4girqyvodDp2 + Ew8pg0SqD0WU/eS7fu2OM3fccYm2Ps8gCGDe+18e7LVnwz+jlPxAItUKAJP6AWCMw/VcNBpTWGg4cJ3R + KG8TewBtgG5f4t4HnsT6+hpk3AeMARMeypUKyqUSHCHABfdU0v2988efKj57VzvBM42xNwAfet/Nro7O + /QllpMwoA2UMgMHa2jqeOruKbqCgYd1+wYBamWPvzjpKRT8v4QVsy+1isYi5uXmUyuVUKhyj1Wyi1W4j + iRPIOO5LFR01G73/+53/7h87347Vfybx7nffk9B9X75fubXXx4lcMRgE+4wy+J6HvTunUC1zZIkNRoFK + icERg7oDAEgksNGK8fDDjyII+jDaOjScC9TrU3AcW/XIOZua8wv/8gtHbt5S2aPtjLE3AEDTo5xfy7mg + lBIIIdBut9HpdHBhXSJJss67gO9R7Nnholz2wbnIRTa28s5Ho9GwbD91AGPQ7/ewtraGKIwgkzjQOvkN + cYH9wc/+l+dH84zbboN623v+5jHt1V6fpEYgEwpxLlAsetgz78J1bGgAYtsYzk0LeC4ZMQJBJPHYyXWs + rq5BJpbsJ9RBsVREqVS0oijO3URH/885BIXn7qonuJoYawNw7Mj1jud5vy0Yq2UreRRF6Pf7WFtvoh9a + 1p8QwEld/+mpOkTadgsAKCVwHAf1Wg3lShWMuwAI4jjA6uoq+v0+ZBLHcZz8WSlK/tttv//V4Aqn9KyD + EOi3vedvHpNO9fVKqjWdSgAJIRCOvd5GzYEjrL8ShBolj2C2LgaGAYDSQKeX4NHHTiAMQtuvkDIw5qJW + m4LrehCcwxGiJiZewJbBWBuAjl9zNWE/RijjGdudxImtgV9NoFS6ow4FaiWOXQs1+L4/UsfPOEexVEJ9 + ehrC8QAYyCRAc2MDvW4PSaJUFKsvRZz+yo8evaf/XF/zpUAI9O5W74SW+DmpZC+rKWDU9hTYvaMMz7HE + ntbAWltjx1wR01UHDs/SiECUKDx+agOra5kXYEAoQ6Hop0VRDIRSN9LBrx0HvOf2qie4GhhbA3Ds2K2s + zItv5oxXaMriG2MgZYJOt4teoOwqyAl8j2LvjgLK5QI4G8h8GaMo+Nb1d70CCBUADKIwQLPZRBSFJomT + JwJ23b9857/7x+d1u6wfOHqXXIjZp4mWH1BKx5kR4FygVCygUefgKSEYxhpKKhw+OI1SkcMR1hOQ0qDd + S/Ctb51AEAS5F8C5h1rNcgGccbjcKTfKlZcfOXJkbO+fCSzG9ws8e9ZRSfc3KKXu8NNJIrG80kOSusKc + A9M1gal6FUKMuv6u66Fer8P3CwAojFaQaadfq/STbcOrt5wL/nQRzyHh93TxA0fvChNf/Uct5cNKK5MZ + AddzsTBXhi9s1jeRBosrIUq+h4O7C/AdaygBIIoVTjyxgfX1DcgkwrAX4LqubYZCmZ9EK7+1F3debnPE + CcYEY2sANuLYAWXTjDJim3RYDyCREq2uhkrz/p7DsGuhCs/z0sYddvIzzuEX/DTut/exVjF63R663R7i + WMZKxr/Fu6dOHT2KsSmE+b9++esdzWtv1Uq1AXutggtUSkXrBXC7KVEQGURRjL17dqNRd5GpoA2AfiBx + 5qmziKIYRmvb/4ByVKuVNCPAKRf8YOx13G9zOhM8zzGWBuD2228SVeP/CmOsMpDCAlImaDZbiCJb4usI + goVpgUq5CM55fhwhBI4QqFXr1iugAgCgVIKN5gaiKDAyiZ4ogPzRbUePj5XyjRAY3j11Ssv4txKlYq2t + F+A4DuZnK3A5AwgQJwpnFzdQKBbwwuv3olzk4DwLERQee3wJvV4PWtnLZ1ygUCzlXZEoY9Wq8X/l9ttv + Es/l9U7w3WEsDUC953BF1E+TkR0zbLecZjuGMjbnXfAY5mbrtvFFqpKjNI2LSyUUij4o4zBaQasY/V4f + QT9AHMs+h3jrj/3q8zvuvxxuO3o8LlD2R1onp03apYRzjlLRx1TFyny1MthoS0gpMTszjR0NH66wBkAp + g3ZPYvHcIuIoSncnouCcoVQqgXEOCiqkke/EEiYGYIwxlgZgpe0yEF6jlOVl8iariOvbTTE5I5ib4igW + B64/YDvqOI5AvV6H4zjIhkCpBO12C1EUaSnjbygePDLOhS/R3Tv6BOS9SqsIyNKdAo0pF4xaBWCiNNbW + NuC4Lq6//jBKBZG/FoYSZ8+cQRQFqTDIgFIO3y/krdAZF7wIPkkHjjHGzgAcOXKEzlTqLxWCe4wOOuYo + pdAPAkSxAYhd/WdmanAcx7qsaRNPzgXKpTJcrwjCi9b9NzHiOEa/30eSJIGLynt+6t98daxLX2+74w5V + h/93RsunjDHGdhFyUK1X4bs2JRjFCmcXV2GMRq1exYHdJbgOBQGQKIOnzvfQancgpTUAjBH4hQIc1wHn + DIKLklso/uxEEzC+GDsDcD3+ksvowtsZiDtcCqu1QqvZg9YKggHz0zb9NdKxN139K/UpeJWdEOXd4H4D + xhB0Ox2EUayViu9N+MaWaIbZ2lsNDSPvVVpHxhgwRuE5DupVBsos49dsWd2E5zo4cOAgir4AY4DWBmGk + cP78MpKskorYNuWu64JYw+soE/3iSiWahAFjirEzABvwuCbmTSCEp002rQAokWj1FLQBXIdhulEFF/a+ + zI5j1Or9S7UdcKqHIYp7IEp7YQhHt9eDTOIokeLXj7ffuCUaYd522x2qrvy/0yo5q7VtUEgZw0yjDIen + KUGl0Wq2AQDlSglz085ISvDJp87azkdpWoUQq50QQoBSSgyhjVCVx+4+msBi7L44vzhNHC7YYHOOtKzV + GASRBiXAVIWhWPDTPfsGTT0dR6BUrkGU94DwAkAEQBi0lpBJYhIpzym2+s2t1P/udGUthtYf1MZIK4wS + KBYKlvAjlvDb2FiDUgq+52L//j3wXA5KCZQG1jZi9Pp9KGULhBij8Hw7ttx2ExbzO67bYYzZMs1lthPG + ygAYY8js3hsWCKVi6DlIKdHv92EM4DgMjYZlqrNe+1m7bs4FiuUyGC+BgIOAZpMfUmpppPpgUBFjlfb7 + dnjXu++RWh/+c61lYIVBgOAMtTKFYARKaTRbHWhtwJjA7Ow0KkUBbosqEScGa6trkFLZLdJhMwrCcUDs + Nmb+njn6dnzz5yc8wBhirAwAvvg+tndG/Ijgws1IPUIIDAyCfh8wGkWfolIuw7Euar57jxAir/8n1Lq/ + BhJGx4jjCMroUDsH//xd775nS7XAJoAJo9PnldRfMgYqI0LrNQ+UWc9prSmRaAVKCXzPw749dbiO1Qsk + UmNtfRVxnG56QhgIofA9z9ZUgPAH77vn1r+468GJARhDjJUBOA1wqsKfASEjpJNWCt2eAqcEjZqAcOzk + z0AIAWcMrueBADCyD2MiGJ1ABetoNjdMEkWn260HLzyXNf7PFIrXzySM0j81xkhjjNUElMvg6aYnUhkE + PVvn5DgeFhYW4KbFQ1JpnFtspluJK8AoMErhuvlWaSTRpr7RjichwBhirAzAZ+76JL3//vsPaKVo5tpr + raGURhgbUEpRr9ch+Cj5BwCMc3i+D8Ag6S9CRy2o4AJaFx5Gr91RSRx++mUL2JJbY//UbXdoVK79plQy + Nsak8mAO36GghEApg34vsGEApyiXK6iWKBi1r3V6Eu1O14YBAAglcD0PNM2wcMpIfWZCBI4jxu5LM9qQ + VIqa9/IzxkBqg0qJwvOctGDFPmi6sabvebYPnpFQ/TWEzQfRX30Aq6vnEcVRQlH8ny9919Zy/zMQwGws + fXOVGHOaEFhNgCNQLlEwZsev1+9CawnGGFyHY6ZRBhepSEprrK+tQckEWYERAJsJYAyO43j7Dr7yRmMm + 1YHjhrH7wgih+cRHujNOEIRghKBW8cCGyn0zUGZz1yxNfRkTQsc9hEEPURAgSeJeH4snt0Lu/3KoI5TK + qM8pZRRgG6CWClYirbRBr9ezLj4AIVw0phtwmN2HRCqDdqcFObTlECUErnDAKAOllBdJ/DJ8cfzup+2O + sfrC/OK0ndpDfa5tFiAGY0C1Vh3Z2ScDJQSO62K4b4BSClEYIo6l1kadUL2pZ7W557ON4/g/pTHux7TW + ErCSaN/3QCmBMRpBPxhkSwRDrV4H57Z7sFQaa2ttJImEUtp6AZTkBlVKJR6+/+u3feiuOydE4JhhbAxA + ngJkbCQFaIyBTBIUPALHdUbIPwBpt18HnIsRz0BrjSRJoLXUKtFfK2Jly+T+L4X3HT1qULz2NAgkYJug + up4HRq3bE8UaWTs40GNiAAAgAElEQVQxxhg8z0O5YDMBRgOtrk2X6nQfRErtuDLOQBmINnquWIkmROCY + YWwMAL74PnZg1n0DZ8zNJnmWAowThWolbfW1if23aS82ogmw5KFBHMfQ2mgh2EMzmNnSBoAA5uwTX17X + SodZSTRnFH7aJajbV1DpCBBC4QiGqSkfnA0EQ81OO/cAKKXggoNRCkooAaWlSSZg/DA+BgAApdoZie/T + nXuNITatxS/e54QyCsdxNkcFaeigQAhRuw9fe/f3v+/OLR0CAECjNKUI1ScBozPjyNIhM0AeAhBisybV + ahUOt8VBUhr02t3cAwCQ9Qi0ZOvm/QonGAuMlQEADMlW8KzpZbFUBAiB66TuvzGDB4ZcVTZqHIzRkDKB + VMpsBOX2c3E1zwWk1KFO06cgBEWfgpJ0yGDShikUjHL4XjH1sqxgKIpCKKWGDAUF5xnpOpn/44ixMQDH + V0BlqHYRAkYotZtfFHzs2rkTlZLt85+tRkOrUu6qDtcNZPoBAwBGB3jygQuEkC2bAcgQL3QUNfgiYBWB + jDIwSkBhw6MwCO0Kn0qGXc8BY8R6AEpjY2MDSSxh9EBfQVMvjNGxuZUmGMLYsLZfO34nLZb0axhzqMNs + W6qiX4DreajVa9b9N2Y0j5cuStnqn920SmnEcQwlJbTWON3b2PKTP4MmJtHaAGn+n9DByq90VvZLwAWH + 5/l5n0BjDKI4htYSBgaU2AlP2ZbYXnLbYqzMttSK2bZeFFwIeH4BUkrMzM6mvf0GFYLZQ0wa1nxb0LRd + 2nB6NesjyNKGq9oArbayJKA2+TF5m/VJBDCWGCsDMAxKKVzPBWMc5VIJwhlN8w0bgQzD0uDL7pk9QTpu + dDB2xqYCpbahkzY6HceBIWCUezsWdjx3Jz3Bd4SxMgAExLYAhyUAHSHgOAK1VP9PyZAHgGzvu0Hcn/00 + 23ryU1BqNwXMOyVd6Wg6dIABojhOOdaMZE2brbKxupUmSDE23xp1+kUC7AFAbYkvBXOroJRDiHTDimzi + X+KW1kNuKyGDCUAIwQ5sn5Ur+8I3e0fDGDaWw9DGIIojaK2gtQEBAed8ZJflCcYLY2MAAIBg0HWGUgpC + HRiYtLknvSjXn2Hz6r+9PYBLIxu7YcNwKQ/BaD0Yv2x74cncH1uMlQHIQCkBF3Y3H5koZNx/nsvO/jMX + r2I5Ui2BMQaLWHy2Tn1rIdVbTIzq+GKsKHKV8XeAZZ+5B9evA9TBlZahy92c2/HGzfTOm6/9Sov45tcu + GrNMnHU1TnCCZxVjZQCAVN+fCnwIc+FUZxCHa2l7MIzE/8MhgRX/DH4fqNm25207wpMMuf+XNIiXGCKT + ai7s8ZfnEyZ4fmPsDIAxBowz+L4PxlwQ6oIQZpt9ABiWAuX38hVW+e3mAQCDUGnwxKAP2vBEpkPsP1J1 + tdYGWm2umxqkBCcYL4wdB5A1As2XJcpBmAvhenbPOkIHD5ovbQAuJgGzgpjtlAUALIeSpUwpZWCMgNHR + dClgxT+MEoAi3VfRtgPLMOpFTTyAccRYegAAbBqPe0i7fKaTfdSFHW5fdaXP224koHXfL16xr5QazKEv + fp8xxm4jfrVOcIJnDWPnAWQcAOfsad1wZqgy8KLnJ7gI1iuwDEHuQF3h+MzIKq1CLD0bZzjB1cTYGYC8 + bRXjANim50dX/IHqd3SyG6MvT3htA2xe5XXaIGW4J4AtAd4U3ZssjLIdlYbHcTt6UlsBY2cAAAxi++8C + 23XyXxG58CeV9z5NDyB760yD+c/wGU5wlTF2HAAZYftVWqiSdanZpGAjgxVrM7a7AcjSgMMdfpCv/ukq + f8lxw0U8ywTji7EzAIC9CfM8tlG2VG04q5XflKNNQCew2JwNoUNxf4aLZMBk8NBGYZL22xoYOwOQkVQg + AsMcQH6DYtgDGE5tGQwbhKeTIdjSIBdzAZtX9kzsY58ALkr3D7Vnm2A8MZYcAKEURBQGhSgk2yhkdHW7 + kgQ4+7kdXdihUn5QOjCimyfylSb2sHHYruO4FTA2BiCMVUC08bO0ns1ZM+QyVKPtA0MFQWZ0sl9SsUaI + v50aWRit+fC01loiaxI6PDKUEHBGYLLQiVgVILnMHWNAfCHmgmfqvCd4ZjA2BgCw6Sog5QCoQNaqxuj4 + iiHp5vhfp73tt/PKla3flLI0rZex/5vCAiAnBPWQ/TTGTMZxC2DsOIDL3Wx2xTf5z8Gxo+8d/QnrNSxt + o/x1Vs9vsg1S1IjMd7hhyjCGuoMNsgXbeRy3CMbOAFiYwao/Sl3b7MAmMhCYpKsuwtOR/V4CuQGeDOeW + wNgZgOFV3Ri5+cWLPADb1tq6q8N96zJ+wFwq2b3FMewBXPR8CkovNhCUDOoFsrBqO4/jVsDYGYBLwWiV + egObhUDkspXAWcPQ7bajFTHmogvOOIBhaD1IA2YLvt6UZQG27zhuFYyVAchW/8um97QevTlzsZD9d1bH + nm1vpbXtcf+jR+/pP/Nn/9yjvhSSxHFmABA93NsPafffS4AgU1Tafw+/Z/M4TjB+GCsDAKTss9GAUTDa + Zp2MjkeNw5Ck9fKfg5FwYTughxkqIF+ONPtzpfEBRnX+l1vft+M4biWMnQEANrWzGn0h/UkuOm7zDUqu + 9DlbGIwRMbJP4mUU0hkHkNnT/JFlW7K2DNie47hVMFYGYDjVd8nX9Si5pYdKVY0ZTmXZ/wY73GwfmPSa + sxBA6XRfwFTen49dvgHoxanULH24ncdxq2CsDMDTxeZagEthu9+vmRfAhmt+h1h+gqfX6mu7j+O4Y6yU + gMAQC62TiyoAr/TQWuVuLPKV7dKlwlsVMcoUAB2MoYbKyLtc5Zcaz8ux+mbIC9s0jhMZ0Phh7DwAbbRl + +9VwHbsevDaSoiIjBFVW+26JRHsDq21UJrwWecWig0MGhupNrP1QHeBARwFADVVab1Zhbtdx3EoYOw/g + csgFKZcRtwzzBps3DN0mIHEiCWDoZkl03v1nczk1AEY3caubDOzw50wwfhg7DwApgXW514Z1AoQMeQTG + 5DsLbu9yYKKzMRzW8m/WV1wur5+HTUNjtz3HcWtgjD0AZfP/OkbupA6Rf4O97S9/I28zGEgTg5jHAejN + LcBtWcXQrkqUDLIE2TFDnMlmL2KC8cTYeQCW0LuyEjCD1sBmxVv2GdnPbyeG2UrYW2uGNmzPiNFLe1LD + 45WFAMNGAJsm/3Ybx62EsfQALpXds2Wto6taplPPMXTzbmcMj5PWmcZ/OHQajFlGAmo9Gb6tiLHyALRW + 0FpDKQ2jpS0CIoDRCbQxUEpBDWUHjCFQStv97LQeIa2Mts+bbcZe2/FTACGX9KQG3sGl13StR5uAbNdx + 3CoYSw9ggLQkVU261H6n0ObiDMnw7kBp0yVkaX9CCPRE+79lMFYeADDQo28WqJlU0jpyMyOtW79U/lrr + S/IDWx2W3MPFvryxMuGL32B7rmYvqSEtBYBtO45bBWNnALRW0GkZqjESBBzAwP0fJgINNReJg+xnmPSh + ABj6pQ/f8jJD/MS99razJVkw9f0vCRcWego4lBBCtpRva5SCBoFSdjJrY9J+f6NpPa0NlMmIVAyzgENl + 18jH8ZLGY4LnPcbGAMw0an632c6YvU0rmBkhtkaJwEtLWvNGFoAXhME3CAnRv+cPsSIlzjzAcZ+NaT/7 + t3/4/X1K2SOUeh9lQiXFG955piQLJnBnwmq1qg4dGk8jQdL2aZQQUGK7AA+PH6UELBUIUQpkGwiNqCyN + GSJah3MFE4wLxsIA3PNXP/cL/TB8LzPU73R6dgVKX7NtwawUdfNqTymFVgpKK1BF8/SBGXJblTYIw3Dk + 78Wx7S/AGHsDAMRJ/OOEhL9GY4romx/AakoorlKKJ+9kf/PZ238wpow+xJj7CQGWiBvedkbKgpmdPdQ7 + dCg2hNwQP2uD9W2hMSD5DLQyUBqQykBJNeTaWw9AaUBJ6yUoY6CUJRGzjsDZOOpJCDCWeE4NwPt/6aAL + AP/6Ayejza/d81fv+gUdb/xGoew3HJeCiYLZaApypdLTS2rV8xtUg6RSQGX0SOwqpe0tOLxjMKUUSZKA + c54/F8cxKKUwxkAIAUIIwjB6M+ccWqofNyb695pSxA/9MQDgyeMaT3zBfPbv/uQNfYeLvqDsi8x17jao + tir7frqtS9I4zqFeHMfmhhuePSOR8STGmJwGzj0iQnMPIC8UJAQUJhcKZeOZ7SeQ8SyXzM9O8LzGs2oA + 7ISfdmUSlhU3TiKIAIDf/sUXJUyS+BWve8lbXRL8anWqMs04YHQNjDMoqUAIJWaI0LMpKAVCGez+gGbo + Nfv3KB0i/Exe7jbSOix7nRACpayWgHMOpRQYY5BSgjFmS2cZgzEGnHMkSQJCCFzXzX9qrfPnjTFwHAcA + 3sCY3X2HMvZWozUobaF18o8AAIwxMEb/5ht/cWtfKx1A4/Og/L5ENJr1F/zzDgA4zuzljATZ9MhgNj1G + kBtGrbJECoxBWtAzZESHVH9ZtiCP/bMUYOpN6IkBGEs8Kwbg/b900JVJsRETPc9otNtA7eEEs1TTqYX5 + 0kt3LNRfViyXKWMa1dpcGlsSRGGEqBfCLxQQBH088cQplMtlSKUGN2dKQCklUyJwWONOU5dVg1KVx7gZ + YaiUhpQJLlxYx8zMXLryUTDGIITIDcQwj5Ct/L7v58dSSqGUAqUUruvmRgMAPNdNz8VOOkopKKXgQgzz + GG/mjEJBgQr6LwDAIeuIHvkDEEIQAzBGf/Kbd/xUX2vT14m6K6L8vpjuWF3rzsR+6/MfWmv2H9FaYqMZ + nGGCNt/zXx/8C9jprdKf5h1H7wo/+B+udQkvDpqlDH1PF3X2uYSnpbW25KGymoz8Z9wqvPs/P7Eteitu + JTzjBuD9v/SKiuT9vYSZFy40SrcdPjB/y8EXfh+dfvGvovvUJ3Dq3r/A6uoqigZwHAdhEOSuueO48AsF + KKlw/PjDOLMU47qivqhFRbaRBaX2MewBXH5RsoujTCQeeqyNf7awE5xz0LQ7ZjbxswmfPYQQeXiglMpf + dx1nxFDw1IhkLbQZY6BDnkTmOlNK4TgOpJSgjCFJwwwAoIzlE0w4zi3GGFBqYCh9O5IEJbaE668ncItv + hgzPv+Xxb51Ar9cDYwyf/sOp/0EJTXj5wEE1/9qgXn9h8LKXvaxPKAUhFDR19QdjOJpC3YwsHNCb2P7M + aNLLdRWd4HmNZ9QAZJPfd5zX7FkoHv2hn719xq1fn79utAQhBEEQYGNjA/Pz83AdB1wICCEQx1FuDDZW + nsLicoSDuwhUzOHyBB6PQaiLRCoQ1YNOujBqQGQZSkGEBoMPoll+w1OjYWQPSbiBs+dWsNFWKBaL9j3G + 5Ct7Fv/HcZy66iy/2X3Py1f5OEkAWANmjMnDAZa+n6feBGMMSinIJAFPDYkxBmFgm5tm7wEA3/chpURi + DET6nElDDK01Dl53Ixo3/od8LLtPfQJTq2sIggBra2uo1+vEGONQuXSGnftLmPW//ux9H/2Z5QceeHBa + GU1UqqrMBHw2utqULiUkTwNmvQFgDLRSkClhmJOCEyXgWOIZMwD3f/wX9we91d+liH+MMMb2Hb4Bw5Nf + 9hcRrn4DcRRBKQXP81AsFNAPgjzOBpCvLIcOX4vPfPVenF3soFAoIkkSeCrtCjyUmrpU2m9zvJ8kCYIg + QHNjHSfPJbhmtwPOOTjnQ+ktkv9913XhpBOWc56ThtlxBd8HS72HLBwQlCKOInAh8gmfpNkFkRqKHJn6 + Lv0npRS9btc+l/IOekjmPDs/PzL5ddJF0n0SSZIgiiJQSlGrVrG4tATXdVGv16G1fkNGXsoYQy2/8lPI + r+eiMRzK8BkzKvyZCIDGG1fVANz/8V/cr5L2X/o+u8nxFPG9CqLIptgeefCb+J76X0GU9kNFywjX7sfa + yjJ6/T46nQ727duHUrmCBx58EC9+8YsRhSE83wchBJ7v47rrXoDb3rSC//2Zc5BqCde/YAXGGPgFHzKR + SJIkdctHdwvinOeTQimVT/6TJ0/jG8cjTFcZXv+6GyGEgCME4iSxbrkQI5+RISMMhePkHgGAnESMY8vT + Bf2+NRZJkrv2wnFsDC0lWGpsMvESKB1kJdJJxdNzUFLmE7Pb68FttyH7iyDM8gvh6jfQXTuBTqeDIAhQ + LpdRSD2aIDWovueBc57G7QSEWA5EaZvjl/TiKj/kRjOd/yZT/hmrCDQmFRPZa5pg/HBVDcDOfbtPlXbf + AhWeR3Dhq0jCNRRVEZ12G48/fgJ79n4Bnu8hDEK0mk20Wi0sLy+jXC6jXC7jzJmn0Gg00G63USoW88kP + AMIRePWrX4tG41F88UvH8af/6+/wshfN4AUvuB6Cc7RaLYRhMFIMBAwmrDEGrVYTp59q4okl67L/8Kum + 8dKbXgqttTUUcQzOWJ7eIoTASSctYGNyz3XhOJb5lzKBlNJ6FekEB6yr7hcKVoeQ8RlpWEAIAWEsnzCE + UhBKYbSGkxKGGYk4vPFGEIbo9/sQQuC+++7D3MLvwSnthewvot/rYHlpCc1mE0mSoFatAgCOH38M11xz + EGLImBljQIaM1iDVh5wwGd0g9Okx+5OdgcYTV9UAMLcOAHCnboI380rI3iJk/yzcla/immtW8alPfgI7 + du7C9PS0dcGbTXDOUSgUcO+998BxXOzatQvFQgGu50EIB0lsJysXHFEUYt++fXjnwYNYXV3Fww8/iI9+ + 8ktYXpfgjMB1Bjc0YAtdotggkUto9zRmagxzUxxvfO1OXHftdVBaY6PZxFQ9PW/HyVd0lq6WGWOfGRHL + CUR5VZ3rujDG5CtudlxGEmqlQFLvAxjE8ZxzO/HT9KVwHMgkAUn5h8xdT9LwwYcNDaSU6Hba+MTHPoab + brwJURxDKYVms4lut4tdu3YhkRKf+fTf4PDhAygUCjk3kZ+Tplbdl6bvslVeSTkSLiEV+Fi6ND1G2SwA + UxRZNaCVB09CgXHEVTUAOu4g6TyOpHsKlJfB3Cm4Uy+EP/cafO+hZXzP9z+Ax77yITz40IMp2cYQBDZz + dOjQYfieB8/3R+JL17OrYhzFCIMArXYbhBAsLCzg5pu/H6+MY3DOsba2hk6nA5m6y9kkytj2AwcO5CWr + Ukr0gwDdbheUUngp4ZanD4aY7cxNz9h4QghYyhdk2QAAeRowSZJ80hNC4HpeTpLJVCOQZQEokBsZnXoA + w4YjO14plYchWmscvuZabGxs4PNf+CKkBIoFnhqHBOeXzqFUrmDf/oPwPA+FQgFSSsRxnKY2N2sgshUf + +ZjnSsFLxPdZViMrJbbH6wkJOKa4ugZAdqCidRgVwGgJKgqg7TqYWwMVVbj1F+PGn/wIXnJLE3HzUfQW + P4deexVxFIMLjqDfzydZtqImcYIg6Oc3WLYqZim4UqkMAKjX6zkzn016pTWklHAdB3EUIZESlBDolOnP + 0nRSyjw+BpC738N5btfzQNJz00ohSNN1WYovjqI81UfTDAJgjY2SMjcUAEZ4Ay5EfmwcxznZl2UYMgOh + lEKc8hxSShSLRbz4xS8a0e8Phz+OY4lNxhiKhYL1aJQCITSf4FlKL5Pyam3lwFIqKCVHNv9IHQJIJUeE + VMM9BT53+w9K16t8zfUb//F7b/vjT13Ne2uCZwZXPQtgdAwVtyHDFbvauQ1QXgBz64hbj4CKCpg7DebO + YOpFv4opo5B0nkC0cT/C9futQEcqhEGIKAohhAPPs2SgkjLPm3ueD8ZThR2liMIQYWQVxRnTb4xBsVi0 + EzRbqYcag2Tv9TwvT69lqb/sb8ZxlH8+kPbOoxQic9PTuF84DpIkyYnBOI5HPA4gFRGl8X6+CiuFbkrU + ZXp6JzVyGZL0ugE7sYuFQn4tjFLE6bUCyNONlFK4KVEJIE3dSQRBH4SXB98XbKiUvXcAywFkr2U7A2fH + WRHRwMNSSmGj1WKm2XwVIWf++v/7rRtRLpZUsVj/uvDqv/3qf/7hj30Ht9MEzzCuugHQcQdGxVBSIYoi + 8DCCcARYfxEgLDUI50BFGaT5EJg7De4voLjrR1De/zOQ/UUknRMILnwVKt5IXWcbm7Ieh+AcSermR2EI + 1/MgE5kTZkKI3B0HgCiK8ph75MLTVZpSCieN4zPJr1IKYRhAK2UZ/DQOB5Abic0tsbPUZa4yTCc9pRSu + 51kXPUkgUyMBDBSJMpUgG2MsCTlkiOIkyYlJwCoLc08jTTUOqxGzv5lpDKLUKKp0ovZ7Xfhla0C0lKP6 + YWMglbSqSint9acH0KzqL/MUlBzxArRW6PW66d9nMEYjSSRb29h4pdbqox9+3wvQqDcivzD1WcVKv3P8 + nhP3209ei4BL14NM8Mzj6usACIOWbcRxjCRO8klKaXqjRj1wwUEIB3VqYM4yZP9M+nsdzJ2BO/US+HM3 + Q4XLiFsnEK7dCxUug7EmgjDM3XzX86CVBhc8n7xRqisABquh73n56WVxfcbex3GMdquVS38B5G4+FyIP + SbKVLvsdQM43pJr/vJYg+xvZcclQuJCRdmLIILGU+GOpsdFaI0xTl77nWSUgpblICMZASYloKD2YGT0r + zrGGJUlJPcE5nNQw2gsc6umPAfWR7/Yz1CNQpXU+Om0YotWgkAoYVQZKKUd4higK89oIrRXarbZL6elb + uBC3VKcdlIulxPP2f2WtmfzX331P8bhI6AbQaU+MwbOHq2oACHXA3DJ0UgQhQbq6WiY/c9czQopzDsdI + qGgVhLp5mEBFGcxrgFAfzJ2CU7sO3swrYFSE4s57UHrsf2NjbQ1Tszuhki6SOEG/10cUx7mrnXkDw8Ie + bQzYUFouiuP85swmfxZvZwKebBXObnbHccAYzzmJbCWXUuYTUA+t/tlEyfiIbMXMDERGCGYTs9PtWoGT + 56Hg+7lgiKTXI1ONQhhFYKlnQQhBEsf5hNZaQyoFSghEymtkAh83TTMObwI68v2lbv/wBiF5NjAPBYay + BLBZDakkKGU5j5I3Z0mPUVLi0OFr0JieRm1qCkuLi1hbW8OFC8uCkNbrOOev2z3roeAVOl5xz7HPf/CV + v/OD7/if37qKt+YEl8FVJgEto0+ok08qmaSTUtqbXCsNymyMPHDVI3DRB43W7Ul5MyDUBfcboN0KqCiB + sCJE+TB2v/I3sYe5kN0ziNuPItp4CF4xRr/fQ6vVglIKjuPkq3FWwksIQZjG0pvVfpnhYIxBJglESqC5 + nocwCFAslTD3grfCn3s1VLSG9smP4InjX8bq6qr1RFwXXloVmMEZjr/TScgotQRkOvmzkCEe4h/KpVJu + JFRqWIZXeaUUCpnGYHiSpcThsFw5kxtnasvjjzyCKDZwpQQ48sIpbawYSGtrvGQiIaVM5b7pzsEApLIT + Xik58DakgowTrG4kcL1wpHxaJgn8QgGvfN3NuaiLEAKWhmdKKSSpcKrTbqNYKpX9oP9Ozvk7P/H7r0LB + K615pbnf8ItT/+OlP/H+9tW8VyewuOohgIo60Ekn/XItkSUcgTiK07Jeksfnw6tTGIT5CuLoZeumB+es + dyDKYG4dsn8OhDl5qODPvRrFXW9B0j6JXfgI6FOPI45sjj4ZYt6zlXaYB8g8ESfN/WcrV8YHxHGMbqeD + 6ZkZ7Hv9H45cY2bosvja9/2cgPRcN68FAAbeiJPVAwwVFmV/M3P1Xc9Dv9dDnCQQnEOkXkiWwisUi/kY + hUGQZyqyiZ8hM3zZ9VNKEcUx7vrqGTRqDF5RwmXZJMzeNHiv/Y4GtROZFPhSWh/rEQC9wODk6TauOzyd + XxOlFPv3H0C5UgFgDWyv28uzM8YYlMplTE1N44lTJ0e8Lak0wiSc1r2l9yfx6vu/9GdvATH8Kceb/k+v + uO2D//3p35ETXAlX1wCYdJXXSUqc8dxdFI4YIc6UVOBiILbJRC6cc0ShnUyWpJOgMoCKVsHcDTC3Dh03 + IYNlkI6T6w1q1/48pl5Uh+yeQeepj2Ll3GPodjqplsDLdfjDq22hUMjPLyMEpZSWrJMScZKgsf+HRi4x + bj6KoL2YK/Oq1SoqlQrOnz9vpb+cA1GUhx/ZipzxBRmByVPOQgiRZxIy4yU4z3kFALk3FYUhIgBhSmxm + 7wcGxF8WToiUvwCQX3OigDMXJKo1mXpB9vNtS3WTE64xjfJQQqcNl5UyUDw7VoMyKwWWMkY/iLDSlKiX + Bx6IMQYbvQDLy8uYn5+3Jd39AJ12C1EU5eM8tWMnZmdm8I9fewSM2UpGz/Pg+z5830e5VILjuqjV63C9 + wp61lfN/BGBiAK4SrqoB6HZaKBoJSkXKessRYo3xwc3h+d4Igz6c785IvozQAqweQPfPwHGXwbkLwlwQ + 5oM5FVBRBhUV6x24M6gceCtq15YBKHSf+hiC9ePYWFsfiHq0Rr/XG9lHYLjAJVuZFxcXUXvo49gZLoMX + diLpPomocxrLS0vope+vVavgnOPk4ydw6PA1ufu+uclI3iQknbDZxCVDrnxmiIavf3hsMkNS8H2QQsFO + 0tRoAhhZeTMvKI//HQe7ZjjuurePuYbCTJ3AEcRW+MGu7lcqqmKMpF2WVW4oEhmj2+nhyfMSTywmeNHr + HDjOQM48PV3HmTNnsXv3bvz/7L35kyTneef3yTsr6+ju6Z7umcHMYC4AMwAJCCTBQ7wl7XpJy1pZG16H + Hf4DHBv+wX+B/w7/4ghHOMJrr09ZXmutJU1S4kISKREkBRBDYDCY6Z4+qru6667KO/3De9Rb1ccMwIas + COONqKiqrKysrKp87u/zfbzBgKIsmU6nTCYTRqMhKxdWWVlZ4e133ibwBGhqaWmZlZUVWs0mzdYSzaWm + gDxP2xzu7zAejc/zkv3//TpXBdC6cJVRb4eDtnDh/SCg3mhQFjPAibpIleCpi31xeUbW2gTG5Fku8wkT + quoIPzjAdkJsN5KhQgdr9FiHCrX1b1G/+vusey2So18y3v5Tjva3ZclLXMyNZlMCYLI5BqDLlxgdq4kA + ACAASURBVC/z5pv/jq87DrXoHcajEaPhkMFwyOHhIevr69SiiL/4iz9n49Jl/R2V8KeKNUj2E5jKQQlm + nmW6MciS781lrkKFD8rFV49Ve7Jt2+RFQWIkAc1QwJGve/L4v/Otz/No76948CRlMCm5suaSZiVlUZHZ + ghotLzLcQgKhihxBtiLcfNuuyLOSJJlSlhWDwZD3N1O2DzK+/fmIGzee156HUPTif/03f/YmVzYirl67 + ThzHjEZDwrBGEAT8zU//mmlccef2ZVZXV1m/eJG19XWaF67j1tbIpx12Hv4NvV7vWJ/Hp+s3X+fawTF6 + 8n9XtrcEVU7/0f/K3vYOh0dHNOp1gjCkJZtUyqLEcR0tEOreJJVQF5E+UXWBS4ow0zJ6/qzZxXFrOMEa + lu1gOYF87GJ7S7i1y9h+A9tfIe3dZ7T1x+xufcDG5UuyT1/kDeLplHg6ZTyZcHR0xDvvvMfqapPQgPWq + c1NJwgsXVqnVariuqxOCKu9gxuJKAZhW3TzeosdgAoJMQJPCDOSGUCi8gGfAlNVzEICisiz5t997k7/4 + xRTLgkbN1n0U3/7SRW7duq2rBYPBgP/hj99iEivPAF685mNZ8HgvY/sgJ/AtvvvVJa5euz73fVQ5VnRI + JvR7XdqHBf2xUAqNms2lVYe1i2ssLS1zaWODtYvrrGzcwqs/R1UmPPn1n7O5uclU8iW4rksYhvx7//n3 + P+08Oqd1zgrgT6uqSMmne5TZBCdoYtkB44OfcXjQod/v40uyj6jewPUkXDYT4BPV+ae2uZ57HMDjuTp/ + kKXZMUCOWp4vwhAnWAOQHkKEE1zE9hrY3gq2W8etX6HMx6S9+8SHb5FNnujqxXAwEG3GacpoPGZ3d5du + 95A8y6k3Gvh+IHgM6nVCCUNWDUUmdh9msF91roqZWL2WGlgBU5CKopBcfWLVJGpR1OjLubDCVCQ6yViW + 2JalEZBJkhDVakymU37+1t/ydw9S3n6YUPNtrq67rC07+K7FxQs2ZQk/emtKkorfNsuFJ+B7Fneu+nz2 + hYgbN29RliXj8Zjl5WWd3DNLgirhqrsqbZvl5WXCMOTChQusrq0RLT2nm8l2HrzJhx9+yHA4ZDIZa9h3 + GNZYXl7mO//iB58qgHNa5/pDDj78V5VlB+STbZLBB7O41ovwGtfxGteZ7P2Y/tEBnYN9SgnVdV2XWhRp + qmk/8DX1tGoGWlx+4M8gqeX8XEBlfUEoDC0UEm+A5YgSo7eC5fjCM3CbOLV1LNsl6b5D2n9bVjR65OmQ + yXhCmiQkSSJq39KalmWpacJUPF+LIo3rV0QhKtRRjEBmLV1Bh4uyJJScgmq7+k5mnK+EH9Agp1gmEHXD + khFaufK3sCyLXr9Pnue6+qESjA8ffsBoOOSwO8PgFKUQ+qKAes3is5+5zfr6OmmacnR0xHg8oigKJuMx + fhBw+fKVudbjucYnqRQ9zyOq1QhrNS6srRI0b2B7EWU2Yefh3/Dw4UP6/T6j0ZA8y8gyUSaM6g2azRbL + y8v8+//Fjz5VAOe0zrkM6MihnbnE7hfYjk1VJqSD90n67+FFV7n00he59BIkvXc53H1A9+iQXq9HLQyp + RRGuJy6U0inn6vows3JZKqsMZaVBRqYFVPdZakJvJd7AtimzIZa9heXUyMdbcwAk22tRf+47OOEGydHP + yUaPcQ5/RlUm2G4dy6lRZkPKQvYHGMlMhXsAtDVUsGUdtjgGPZlREkWWLlUZUSkO83i5VACqkpCmqeYv + ULkSxU9oxswq36Gg0WoWwng8xrZtWq0lbt26rdGIaZoSxzGNRoPf/g/+K5zgIgBJ722e/OpPqKqK0WjI + oN+jPyyJaikXLiS6MqGSmYpoxXEc/CCg2WpRb64QLN8V5zXtsPnuD3n8+DG9Xo84lpWCtCIvBGaiFp7s + 5X26fvN1vkhAx8d2I8qsi+3WscsheSYSWraqO8dt4qynM/jrN97g6qt3yCc79Hd+wp5EiTXqdfxAkIKq + JhtAu8Zq5XlOlRzPIZgXv3JDBUOPAv0UWLaF44wpko4Q7PE2thvhRs9RZgOc+FDyDE6oSmEZ83SIbY9x + /BWNT6iKBNuLsOyAqkyw7EBgIVKJXakKgR2oCqpKeg7FzD1WIUWhefZmSEJ1/gowA8Kim9/VsSyN/FPJ + 0zTLtOtvehpmTkKtOJ4yHo/wpKCqz1xaWuLr/+l/J75Cmev/TyEffT/gC298iT/73l+SZrOcTBiGNOp1 + /Z+EtRqNZpNo+Sb+0h2oSpLu2+w8fpf33nuP6VR8/mQ8IstFi3JZgutYkrdAXl/Wp4b/vNe5KoCqSAU9 + VDKkzMfS/Xd1SdD1XF2+q4opuUzuFHGHqkypr9zi3vO/R5kNGB/8jN5Rl53tbepRRFRvUItqx9B1ZhMO + MFdCO8kjALT77jgioej5HkU2gkzw8JX5BL8Flu0yPfgrBoePmYzHuvYuqgT70oUW9XrHdXTtPwhFmOHV + nwPAa97EsmtYTohl+5TZgDIfSjxDR/x2ZSIURTakqnLdFWmGNkVeaJfYdT3yPNP5ALVU8tAzOA51b4GE + A8dxrL2LJElIkligAItCex8KSFRMD7FckZuJOz9l1LnPeDRiOp3SaDRYXl7Gc2ESi8pNvV6n2WjgyITd + 0soyQeM64eqrAEwPfsqjX/8tHz56xHg8Io5j4umUJM1Js9n3sDW0GRQO+VMFcP7rnEOAAnBFBt5yyTNh + NT3fw/NDqiqnKhIZJiSirOU1qMqUqszJp9sC/We51JZfYOn5l7hhB0zaP+Zo7wO2t7aoRRGBLC+qlee5 + vnDNEpvJ2WcqBpiVJAGqRLrUErhE0iGfRDI2HTIaDul2u0yn0zkloG7K3VXgnlni8n1sx8Fx3gTQQldv + iARo0LiO7Qb4Sy9RlSlOsCHOp0yxbJ+qEIjDMh9SxEJRFEmXMp8I5ZEn5Fk+a8xRHoMsaZq/j2IoLqRw + m2VJUXWYJQ/V93v8+BGH9/9rLXjxNKazv89oPGYymfD8888ThjX6o4orFx3W19dpNBo0Gk0arQZB8wb+ + 0gsADLf+L7YePebDR4+YTMZMJmPSJCVJc23x7QUBL+XIMteZb9/+dJ3fOlcFUCRdEQLkE6oqJ4xaWLYr + rCsiCQdQ5FMcx8d2A7ndx3JqQFO4yWVK3H9A3H+AX79GsHyPqxtf4yoQH/6MXvtddre3BemFjDfNbj7H + cbBsCbeVrjbMLIiqIqgkm2blKUos2xLhgO1SlTllEVPILPZ4PJ5r/HGM5Jp6brrbZouxUkam0nCdR/Lx + n2PZMwaiqF7H8zyCMMDxV8ByCJbvgWUTrn5x7jevyoQyn2DZLmXWl/9DjzLrkk875LEgT1VdfJ5RMVDf + JUlEh2Wj0dClTvU7/qt/+S959bXf0sjEfr/PYDBgY2ODsiz5P//kf2dj1WZ9Y4PV1VWWlpcJW8/jL92h + THsMtv6Mhw8+YH9/n16vy3g0IstSkrTCiOQoCvXfCbcfhPCL31LmV/J8Lvz7dP3m6xOhBbfdiGxakidD + HFcIgIqXi3SAZbtS4E9YloPtLePZPkXapUg6TPfb8qUAr3GN9Rf/gEuvrDDZ+yH9ziN6R0cMBwOCMBSg + HpkMMwXTrMU7rkOe5XMYeuVqC3pw4blYtovthIIYQybZlHeh+gWUZ6EAOmarrJngU5+jzkttM5WDeb7m + vfAq3tQcBo7jaM7EIAzwfA/Xb+K37lCVCX7rLlbjJrWLdbDE5ypvorH/Y6JdQckWT6f4vq/zBpcuXRK0 + 4bloBlpZXuYHP/wL/pc/+WsaNQvPtbiwZGPbDp2DfYqi4PkbtwBoNpusP/cCwYVXKdMeu+/+H2xuPubo + 6IjRaCiSitMpZVlo4VfxfVlVWLbwAJTwZ1mF41i4nlIAJWVZHBvk+un6zda5twNbTkRVCndVgXYc1yGL + D0mnHSF0vsDz2/6ySJblU1y/iWUrVt1EtwYDFMmhOL5TIxttkfYf6Kz9xkuvcdlbIRu+T3/nJxqFaDsO + tVpELappIVPW2ZwBoJKKqtJQFAVJPMH2B7hFKsqGBkRYuc3KemZZim3PoxmVglDlPLUUU1FZKuIOR7vq + 86CnWUJTKQLl4SyGNWYHoOv+VD7+E5EQ9H1NmRbVIzw/JM8TXaNfWb2A7wfU63WqsuTKjc8QLN+jzAYi + RCtSur0ed+NY5goSXnzhBaJ6nTRJBM25/M2ev3UHgMe/+J949OgRw+GQJImJ45g0SUS+IjdifCn8tg02 + s3i/LIU3IJiKKmzEcJJud0KtFnHt6lXgrz/2Nfrpml/nqgAOHr3J8to1vMYVrIn4U4NQ0lcVKa4XSVd1 + CJZDVUyxbBfHrYm8QCEsr+01KfMEy3ZEdt0JKJKu2N+p4dZErFwkXYq4o72L1Tv/jLUXXbLRhyS9+xzs + PaGzP8QPAiIjK61JO/S8qxncWICPJF4AEY8j91czBky4r+8HcwlH9VhjFCqVkJyPbwXcdxa7z8fkJVk2 + D5+GmTJQCgZUrd2lqkocZ4Y5MHMTKi/hSRCWUoiqamDZNmEU0e88ojZpz7wVf4UrV64QT6f6d7ty7Sp+ + tKEbv5xghcnRe2xvPWZvb49+vy/i+zQlTRKm8fw8U9Pyq1SJEnyVBHRd8Z/kOfSHJc26xe98+7e5dfs2 + u9vbv+FV+uky17kqgDRNGHR36D98B8u2iaI6jWZDZMj9pnb7LdvXiT8B023q7Lfjr0ihc1BkM5YT4Pgt + oCXi8jzBdgNdgqtKkT3PRg8AUY2oXfwiN69+h6pMmO7/Fe0n75HEMZ7vE9XrAgiDoQTkEi3LKZ7tCh4C + 29dCpSynSTaSSZIOVdKbEXMUeJ5vCPdMoGFGPGp28ambmU9QykocP9MeQ1XOFI3jODO2IGYTfuZDCUd7 + PkqYTe9B3atSoOqYVCVHRV229egxQdhmaXkZgO7hLznqdhkOhwyHQ+J4SiZDoVS1g7uWLu8tLtPqgxD+ + PIf+qKQW2vzON1/lhRdepN/r8v57783hIj5dv/k637kAtlOt3/mOtQ4cfvhndPb32d5+Qj2KaDSbrKxe + 0Bd0VVUivpZLWFxhdS2nphOGZT4R9Nmyjo7lYLsRRTrAdiM5HSehzBOQigEgn2yTTwT3YLj2OW5e+iZY + LtP9v2TQeZ/9vT2CMJQDSGflRUEv5stKRSpuC12LSjgVfZcCv5gsOK4rp/pI7jwRJsxe14lHA7lnMhOb + 1YwZom6mrIpy1jBUliWlxAmYOIkM5hSEEnCV9Tehx0oxmMnNRSWk3h9K667Ot9frMR6PBQVYmmLZFkUm + PITAd42QZ/56UcJfVoJ6zLHhqF/hOPDtr7/M7du32d3d5ec/f4tmU/RifLrOd52rAsizrMpGj61svElY + C3n+zsu40RWG+29z2Onw/q/fI6rVqEURUb1OWCtx3UBQiEuIrkLYueFF3NoaVRFoEIoKG0rA8VtCCYCh + BCYUaSK9BXQCLBttAkLJBCt32bj4BpeckPHO95j0t+kcHMg+9Br1Rl0kLm1X1O5tX7LgzNfkYTFunyk2 + M1Hoef6xbcobMMlRFq29WosdcNpzMNCElm3NGJcWMBAqH6JeS5JEVgJmLcjiM2eKajExaXoaSgiVQlAU + 7VVVarSjVYq24SDwdY7Ec2Vbt+EJmPfjaYXnWXz76y9z69Yt2u02b7/9tlY6vu8TBsFcX8Sn6zdf50sJ + VlVCgMuSYX8IDLCsXaJ6xNUbL2pl0O/1aO/u4roujWaT1tISXlUIQVakItmQQnoBAoIrY3LtGSR6P/IJ + li1CAstWWe9UlyTVfZlPIO5QOqJsVrv4BtHG17h4LyLp/ozB3i/Yb7fxfZ+LlyBc+yL+0l3qjUfsHxzM + uAkk+EbRX5lMNqagq+dmzkAIra2t4UwpVDqMMBt6FpGPwuIX2PbMfS/LEjf0ZLghAERmruPY/2QgDM0q + iFAawsPJ8+zYe6tKfYaBxJTgoel0opO+Cv2XJqnxmVAYMwRUzD+cVCw1LL7x1Ze5c+cOOzs7vPvuuxpR + COhGK9X+/Ok6v3WuCsC2rGMuM8B4NGYynpDnm9SiiLX1izx3+w2y8SYH7X02Hz3CtgXJ5fqlDZ1kqsqE + Mu0JZFyCnjGgkogATrgm95VJxMrFcnwsx59THCJciKiKCWXWBaDMBtj+MpY9xI2eY+2lz7D+SkSRHBAf + /pLR1p/Q2dtkOBgwGAx07dxk61FW34z/ze8+uy9VMUEL26LFV8Kt8geL3oBypdVwD/WZZTkPFYZ5noVS + 4SBsQfVl4hNMK6+Ukdl3YS4zf+E6jmYyUuekwh4V7ihFlefFnPBnWcVgXHHxgs03v/EGa2trdDodfvWr + X8nEqq89E7OJKvt0AOm5r/MtA1oWtreMWyRY1ngWD6sWXtdjPBoxGY/J2/vUakIZXL7xOlUxYXD0hPbu + HlW5w/KFCwRBgBfUocrncgJlNsT2RRJKeQxqUq56ftqyvRZlNtDHUth9yxEdaQLTX6Nx9bvYwRKW/d9Q + lu+xsbHB9vY2rutysN9mGqd4roXriphaTP4V+PhZ7d7TQguzHIJSADALI5SrW5azoaBmWKDeL+5LPYtP + uf+mZ6DLhK5DVVYSFGXPCbwZsqhlKiX1eWYIYFmZ9lKyPMdJEk16IsamC89h5oU48pwALJK0YjSt2Fj1 + +J3f+SKNRoNOp8MHH3ygz8kEdJmVkUUw16frfNYnAgQqCpEFjuMpUVSXCT9b4O49X5eusixl0BvgTx/g + OA5RPWJ54x5lPmF4tEn36Ih4uk0tikTNWuICvMZ1HQIU6UD0kcsEoeX4c+dihgCWFHa1bDcSCsCJ9Laq + ENWJInGpyoRw7XNcCVZYW3/IS/fuCtc/FJ1xk/62PMcpvX4f27Y5OjoiTVN63SMGQ0kaGlqy7CUEwnU9 + /CCgyHM8OQvRjMdhhnxTAqustlAcNhVCiVTFTIBVX4I+hmF1TeFXVv4kQVf7igqBKC+aAqhCAHVTxKj6 + nGWzlQghHFzXYxqn9IcVly4GfOMbn2dlZYV2u83R0ZEmQIFZ0lGdn3nOruvq5qZP1/mt880BlKUQKGvG + epsksW6a8fwZUaUgBHWJ5VisIAwopgXTyX0A6s0Vltbvijpz5y0GvQGT3T2iqE5Uj6i1rgjLbQcaPwA5 + lh1piy7Qb8m8ElCxt3zdVAggut4sx6fMh9heiyLukE/b9Lt9xqOhvEC3dJnMliCd69ev47oeL929KxqC + ak3c2gZlPmHrAzEERykHUTIb4DgOHz4+pKogDGZIuMAXAqFcajU2HGYcAY7rYpXFnPUX+8ohJbKzUAxh + mYcpm/+P6WWYWfZZlQCJMZiVElVCLggFr+NgMCDL0rkwyPNEAnD/MOPSxYCvfe0NTZ6qLL6ZO1FlVZVo + Nb0UX7Y3pwuNT/+A1yfhpnwiX/z8R4NlQ3FgT3SDmXGkWdZS7mYtikjimCzNsB05tSfLGfYPoX8oyk61 + OquX73Cp/hxJ9x26hx06+z/XbaYzZeBINx6NZAN0vkDlA8R5dqnKYpZclFBZ5Q3Y5vy8UnQy2o4jRmaV + M1prW7rro+Fw7nuKGFvgElSsfOXKFRzHFdBdNYOwJoagHrR3NfvQ4dERVVWxs7ONZVm024eMp4KNJwrF + cFPVIBP4lkbYee5MIaiVZ7n8TTMJy7bn4n+YKQQl9MoKz/2vZanzByoe94yyp/AWxKzEyXhEf1hyaaPJ + d7/0Ko1Gg6OjIz744AP9eTCjP1PwY7PtW92biMpAjkX7e17/UGKOs87jYyuH820GKksB360Epj53XBxn + Rk9VZbNhHEE4Y75RFk6N+fIDnyRO8AOf0XBIPI2pqg62/ZDmUpPVy3dYD1ZI+u8x7A8Z9EXWuLW0RNC4 + JHsNfB0OVEUqrb+D7a3o87VsqShKhUCUr1WlBCkF+vXJeKypxVXd3MyIm4rNXJYlxpBVZalpv2Ge3MO2 + bQ248TyPmzdv4Xour3/+c8Idlw1B6aTN/p7oi3jyRFCXHRzsE8dT0iTlSTuFakrgi4k+njvrqQ98i6Jw + NO3aYuxvTlFa9BSqalb20wjDBTBRVQna8m4/487tK3ztay9SbzTY29vj6OhoTnBNA6DKmqoPQ1Um5nsz + XK14PsFeACVgTxOm0wTx/0vXZG7E40d54/mGAEVhbb7/lh6PrWi+HNehygRzj6L9Ugg6QP/x+gIzOvXC + Wk1bMduxGY/GONOYoniE7/usrD+P17hOPu0QDx5zuPsAy7IIayHR0nMEy/dkhWAiB5a0dY+BohIv4o7A + ECQd3OiK5A8U7caCRqyux5UrYdY1eCngYIBujBq9iIuzuYYg9b5QtjAroYuThOFoRLfXm6PWUorGtm0i + KcA3btygqiru3r0nhq86DkFNfK8njx5QVRX9wYDhcEiWZXS7ghb90eYRniuuGM8TDT5BMJtwrATOccSl + IcKA2axFde6i41L8f9PplM5Bmxs3b/Pdz3yWoshpt9t0Dg/1MbTHZM/PLlDHNR8rngJ1PeR5zmg0IgxD + Ll+69Btdo8+wThPwp3kCn7Sn8A8/BLh46bK1+uJ/wvDx/8z25haPHz+mXq+zvLxMvS4EynEdkjhmOp3o + EdyVN0tCaTJQT1gkRf65SAmmLEL/cAtvsCf76y/R2HiOMhuST9sMDh+T730glEHrEpYje/xVTiAbUCWz + xKATrEnrn1IVMZXtSw8gn8XaBuTXpO1apO5SQq2so0Y/ynsTeWeiCTPX1ZN8gTmPw7YsptICqvui6Ojj + Kbc+iiIs4MKFC6yvr2sOgrIsqbfWsGyfUW+HQb9PnmUcdbs6gZllGb1el87hUNNxZXlF4AvPTOUAFKfg + XrvN6uoqX/zSlxgNh+zu7mgykUUMhEJLmrMYldehvA3FpaC+89HRIXme8+KLL7GyskKn0zmPS/UsYf24 + gv5xFcBpgr24/aTjn/TeZ/VkTj3ox14f/uBfVKs3flc/r4oJ3Sd/xd7ODv3BQLDFNJu6MpDnmah3S7ow + NTfQRKGpCyVNUm3pFCmHWUKDWdnI93382hJ+6w621yLpvk3/cIt4OqXZahGEgeak0+eq4KpuhO2JeYRO + cJGk+wuS7jv0u4IEUxF6KstvtvaKVmOXUsa2aiy5ZVnaWorPmll9MxlnlsBgvge+kJ2Evu/jGgm9qqrm + FIYZ15vhSVVVmhxUCZcrcf9Rva69Jtu2BZ2bt0yZ9ejsbeI4LkeHHXLDBfd9H891efHVbxIPHrO9tcVA + 5kHM1mnTazDPa7G92lTqSZIwnU5I05Rr166ztrrK9s4Og0GflZUL/NP/8t991Ot2cf+P8n4LPRxt7r3n + JTvPqgDO2vdjv+fck4ACg79DnicUeUG9tcYLq3dwa2v0d37C/t4ejw8OxMgn38d1PdIk0cMj8yzXXoCy + jsqlXgTaKKSZ2ldZ3KIomI6OmI5+IlzjxiXWnv8qttciPvwZ8bjL4fs/ptlqEdZXcKMrOj+gk4GywqDI + TfI8oyxms/iUonFUg1BZ4rgzGvMsy1BOvBL+QvbZ50WhR4KbQqIUirL6SlBVLGxZgghUKQTLsvSgUTNu + Vsk0dQyYVwzqs1QYNp1M5vbFsMZBEJJlKeuXLgle/qiF37opCFO77xAPHrPfbus8gsmYpARahXcK4JPK + QSbKa1JcDYJt+JAiz7lx8xatVosHD97nl798h0Z9ngXqjHWSYD6rxT9LUTxNiTyLQjCVyEmPTzpetbDN + fH6al3DWe+bWuSsA24uw/WXSsSiVpd02nneE1fuQaOk5bl+8h+016e/8hM7+Po8fP6JRr9MqS132Mtci + rBbQWWMzp2DWtWF+1Nekv4012MFxHfzaGo2Lr7B8/RJJ713y+IDB9lv4vk9QaxIs39Xxv1pibkExJ6jq + 3pYWWn12JklCTEhvLsehKzd4EXFnZsWVRTS/jzkbIDNKbUVRUBrW0/c8cul9KGVo/h62EY6YoYn6bc2y + IAgEYZokc3kObzSiMR0S1Jok06HO5JvIRLW/eo8KdzI5c1F5OubMgMGgT5amPH/jJktLS9y//y5/87dv + Y9sikSl6CvS8BfNCf1aBNy334vueRfDPw+Kf9pknKYPThPY0gV4UenP7qWHBuSsAFU/7ga/5+kFy30/a + MGlTlRVR6xK3Lr3GC8FFRrs/YvfJNu1+m0iW9gCdBxBIO09PGzYtJN48th3mMfQ6Tpc49emwTdHbwXHe + odbcwA0vsvHc75ENPyQdPmT/4V8Q1kJal9/ArV8X3kH3vv5+ug7vzHMMAlroTS9BvcdUCMpSqlHhi9bZ + /C7m82PhhgyHlMU3f2vLsnSm3sy6m8rDzEuYOQ11LgLANeMfmKsSVPMufmkk8lJZLVF8BGoUvIr/1ecP + h0P6/S5BEHLt2nUajQa//vV9fvazd8QxNTswNFtLeJ5Pnuf8h996zvrffritLuynWfeThM4+Yx/45BTA + 4qo4LqCLimpx/6edy0lewd+fB6Dq6JZlUZTzFi7PcgH4oSAZH0jMgOD9u/P5rwo48O5P2XnyhNF4rEeK + iW6wQFh8IzmmhEonm4r5OBjmZ+W53owWvKoqJoM98jxn0t8mCAP85i0uX7lJVaS40TXc6ArJ0Vt0D4/o + 9/vaagWSPUhn/A0rarb0inMq5hSSqQRsKZRKUalylx8EuDJGVkKnuP5VM4yy2Kp0qLaZeYVcVVYWoMTm + vqYSADQ3oZlsrcpShwXGHz3Xb+DYNjmzMEZ9FzMZqEKA4XBIp3NArVbj1Vd/C4D7999ld+8I27JwHEvM + BLDRN+UVGedwmpU/TWAXXzvp8eL7PorwP2spbtGFP0nIz7Lm5j7PWpI8VQmcLy24WVsuRNKqyAuCMMCy + LXzXx7JcbLuisozmkMkTzQbcWLvLKze/QxF36O/8hJ0nT+h2u0S1Gq2lJVxXuI95JV1SNQLM6KcXPPqZ + nkYMkCap7It35qyrcs3TJCWJ38XrP8CrrRNceB3LdnCj56hFEV1JmXcjwAAAIABJREFUemHbNnt7e3qI + qJqjF4YhlmVRlwk1Z+auzll/83dSyimRWXO/qnBsW+MN1Cw/AM91yYsC13FI5OtmqVGtxWpDWZY6TwDo + EEHhDrQFV8qpqnRjkUkyAifj8E1i1EVvSC2loKbTKYeHHTzP59VXX6MoCn7x87foHE0EXsESICeYlSnF + d7dkAlT81k/2p08TfhBWflGYzX1sYzsLj0+iHv4oHsBZLrop4CcJZml8VrXwPnO7vfDax1rn7gGoJBrM + LkxFnOEHPpaV48gYuyoSsBxsx9FQ3XT4kGy8TVUmtC69xoVb3yWfbItW3b09pnFMPYrwg0DTccEsDFCC + Zk4XNjPiZqxregfKtRcZ+4CkK+C7RXKA53k0Gg3NnXfnhRewLIvJeMxkPCaVk3Zs29Z19/F4TJom0vIJ + BFtd0pKZAziiWg3HtvGjSI/zVsJvsuCa30G572ZCT1lc9R3NjL/pJZkVBPP3Mn8bHSrMKfT5z7S9JnY+ + nVGrL5T8lDK2bZs4jjk8PMT3fe7dvUeW5/zyl7/g4GBIIanB0qzCsdHPHWMoSJZXeAapaytyJGnY3LJO + uMG8oJ/0+kmvLa6Pm+A7SehP8gDMbQ7HlcTivjbHhX4xt/EsCcLz7wZULbwgSkUqw5vEiXYDyfrYjo0X + rgJQpF3KHMoixnZC3aGXjbZIBx9iuxGtS6+xeucPKbMRo90fsbe9w3AwwJcUX0EYasCQ53uS2WdGu2V6 + B6JzbWahzARfmqRY1rYYJOqvMNn7Md2jIw4ODhgMBppQQwldICcBR1FEWZa0Wi0816XeaBAEIZ7vcdBu + Y9s248lEIOaShMFgQFVVPH78CN8PiKJIKwV1fFVrVxh9z3Upq4owEB2Hi73xcxZYegsaeVjME4BYMoTS + tGAmeEkmDJVSLOV7S+N3UjkA/b9X82xJRVHI5J7oebh39y5JmvLOr97h4KBLXlRa2BV40rLF1a88gTyX + sGdmeZKiKEjS0uK4hTefm4/Vvub2p4UAJyUKT1pnZeTVtkXhN/dTSsziuNCfhQM4LW/wtHM8duxzDwFU + Pd1s8jCn/CqF4JQOrp/qRp4in8qDFFiWK+7tSEzSySfkkzbZSMzyi9Ze56Ubf0SRHOoEYqfTIVTkn46t + lYHOaBvxtzovmFnWxQpCkXQ1Z6GK4V3XJU1TjVIrioLpdEpZlgyHwznvQvTDC2vYaDQFCEcOQnVdl/X1 + dXzP45VXPkMQBhx1OrpNeBrHWJbF0dERtm1zcHCgwwgVctTrdT1HUAlxEMwTlKayzOfJciEKwWfN5gZm + kt3YLBuaeQm1bNvWVQRQrdQz70tVJ6qqYjqdMhqNCIKAF+7cIc9zfvXur+gcdCjKSib2LHxPJvoshOQD + pV2RZiL+z0vhFeCgu0td1wULD8iZt+4nCbd9wvazPAD4aEKvnp8V15sCt+i12JysBBatvLmP+TkWx5XH + 4vr7SwIWWSZINP1l7DRmMp7owZ0mf76awJNO+yJOd0IcicsvihRHYfhLyXMnG3YUbXjSu086eDBTBtf/ + ACgYbP5r9nZ26fV6RLWaHic2T6ohfks/8DWNlvIITAFQvAJVkejmHzOhpcpZZrLMjMnV9qIoODwUdOjj + 8ehUCHEU1VH031Ekvu+Vy5cFsq8uoMhiZLlIRirQzWg0YjKZUJYl47EYwBKGAi6sWIBt26YmcRZLrdac + 5XYs0WJrV7OWZBBXzWLLcFkUVNKrUv0T5orjmOFwSKvV4qUXXyTPc969f59+rys9D3Fkx59nB1K6tyyh + ktyAc2ayhM5RTFRLuHPnBWBLQyyYF/7ThP207Zxwbz4+y23nhG1KKE9z/U2B54T9TxLwZ4nvT3LvT9v2 + yYUA4/GYya++J6fahIS1UAuLKlVpS+PWyZKBnPJb4Tg+TrBGFR8QT8cA+EGm8wUg404vopBNOgDp4AFJ + 9x1sf5lo7XVevP4HVOWU0fb3pGdwQFSrUW8050aKq2qA6amY8TZAVU7RgzXk9zimKNS52fMEn1U1wzUo + 1155B4pERDD7iG2TyVh7LEdHh9i2zZMnWwtEoy71eoM8z2k2m1RVxYULF/A8j1oYsrSygm3bdA8P9ajy + aRyTZRmjkVAOu7u7+liqPq+8CN/3xb3nzXXe6bKr0alnu3WsYoBVWkwmEzqdDsvLy9y9d49+r8f9+/fp + HOyLPpDquLDbtjU3J6CU5KCqAlAUUFYwnAgZ+tbXXuaVz3yGfreL61g+M4FaFP5FYbdPeGwuUxmcJnSL + Ar0oxOaxfpPE3EkhAxxXHIvnuBg6LCoSc80phnNVAJ7rVtc++4dWb+uHtHd3GY3HNBsNLqyt6RIQCIow + 10005j9LM0qnxLOG2E6IH6Ctc5YMcL1IkncUVGWBEzQ1WWgJxrDRjh62Ga7c48Vr36XMRoz3/pydrSdM + D2KiKCIMQ+womoMQA1o5AViOYAZygjV8f4cwDHWcrmYDqDwAoD2AWa1ebFdVC3UTsXEyB381X1PnkSSx + ZvNRFOC5BNIADAd9iqJg8/EjgDmF1GwJUlTfD6jVaiwtLbF+8SJBGLK8skIQBkzGEyZjoWgnY6F8xpOJ + zk+osEDRn6nEZVSrEQQhjtMnyzK2nzzB9zy+8MYbHLTb/OWbbzKdTsRv43koLgHVTlzqEFFk99WUoLKa + VwaTRCiBz792lddff500Tdnd2QEgTkpFXLAo3CcJ/2m5gbPWSRbcdMMX3e6zhP20PEG5sO1jZ/I5W2md + 5s1YQHWuCiDLsio+/JkV1upcv3kDx1/hcO9DuoeH9Pp9mo0GzVaLpZVlMcBSxumu51KVFcl0qME/fm2J + Mh/LOrs4zTLrzSi8bLFdsQGV+YxA1LID8mmHdPAhTrBGtP7b3L3xHGXWZbzzfXa3d5iMxwRhSCTjcmWd + BdmGLG3ZrgxLZlZYQV0VT78SFFVVMDPgZnJMWNHZWDATvagQgqXE+yvvAHtG/eX5voASZxme74uknZxp + oMg/1BrKBKNaWzDnrbieRxiGhGENz/NotVoEQUCr2aQWRZJnYYksjUllSXAsPYh6o8GFa1+hv/MTBv0+ + 917/XTpP/o6/+elP6XQOAMEGJOYvenOsRDOFKzgCCzn801Y5CgQ7cG9U8NmXlvnSF79EkiRsbm7OJVyB + ACFANvNCrso6ZyUEzfuThPOkpN1iac58bXH/8pTHZ91O+9zFczzpXM9SaE8NIc6fE9CNmA7bDAcDynKf + 1tISK2sb3AjW6B+8x1Gnw8HBAa1WS7AByxgVGzw7JEtjIVR5H8uWSStzTkDaoyhSvNr6jNLLdrDdgDJP + 5LQhgeN3ayKWVjMCnGCF+pXf5aWbF8kn2/Q2v097d5eyLDVVuRJEMXRzqFmOgTmhNjnzTcDOWdBl8bqD + OSNAeAKzDLyyljXZ9qtKgaXsNbBlgtFMwKnfXi2T7VctE3tQVYLKS5Vnj44O55SWqp44jsPS0jJVVbG6 + ukqjXuf51/4jGtd+n3D1i9Q+/O/58Fc/Zntnh9FopKs+ptCrc1TKzvQA0qwSV68Do2nFaFLy2bsX+PKX + v0KR5+zs7jKdTgnDUFdEqqoiL8qI+RBA3WDeC6g43eqfZeXN109apmJ4mnCfJfQl85+z+Lq57bRk32nn + +Uz5g0+AEqyQXXsuRZEwHo0YDgYUxWNaS0vcePG3cMI1hvtvc3R4yGAw0MogrIW4nosX1IknIr60LAuS + Do6/IhiALYcyPhClRsUDaEd65Lhb2xD7ZJO5c7M9oSyKuEMRd7CciNUX/jlrd0Py6S69x/9WzxVsNFv4 + kSAI8erPYdtbxHI+nplAU7h+lfCbVQBmfAGL6DxQtXmhBNSIMMXnJ1h5xL2qJKhhG4oKPM9nGfosm2cD + tu3jgm+eH4Bn8Caa5VHzHNV3EOXKkn6/RxCEFMV/y+1XHjA8fEC/19OwX4H1T3WIIgawZnr74vxEAN+z + iJOK7rDk3gsrfOUrv02eZWxvbzOdiqqQMhAqvBKeVxkiZtEvxv6m8Kt1WryMPMZJ281VLtwXHBd+cx/z + NRYeP4vl54TtJ+13lhdz0vvVmlMk508Jlg80N3xVVbNZ9GVJ9+iIfq9HGNbErIBbr+IEK/T3fklnf58s + y0SIsLyM5wvhEklCF1JB5W05EV50lSJukydHALjBBZ2sK5LuHMmnOCcxE8DxW1RFqpmC4sOfYTliDuCF + 23/E2t0W+XSb0c4PGHW3oPo++9vvMxmPieNYDwRV1nmx3VYJ0BwiMZvh4mcew3GPQu03A/ekM0y/O8M0 + gBRyF6qykszD870H6rPVMht/lAdgWTOvw8xFmNvE9hLBKCyO297fJ81+QKvVmiM/KctS/NeFGFpSyu+u + zneGyVCU4TAYl2ysOvze736ZRqPB9vY2k8lkzptZhC8DlFUVIsqAp2X4F9di7L1oVY9dxswEeVGoF7ct + 7v9xXf2TXjvte5z0vpPWSUpibp1/N6DbguxoltjKcxzXpTL+1DiekqYJw4Gg5262Wtx46fMADI8259zy + peVlXTUQ8wIlfZe/DDIcSKcdkSiUQ0JhNjgExABL5RGI8eQBMKQqJjiSTjwd/BqqAifcYOnGH+EEF2UY + 8D8C26ysrDCdivPe3d3DsS18SXIa1mqodleYb71VLr1KGKrMv9rP7I1XVs5UJma+QFlzcWwby0HW5kud + T5kRp8xPMFIsvZZlYWOT5Sml5AhUiUwzCWk2PanzBSHEk8kETxKDqHMXbMcutgwDVNLPHF7iOA6TaUJv + WHFlPeRb3/oCrVaL/f19Njc3NdZiMYTJ85wkSfS2wLMbiMlnptuv1mkJOjOWP0lYy1NuFcet/rMoABb2 + OUsBnOXWP6vL/7T3n1RuPN8kIKCTZlkmer79BXCKqn+bVms4GNDv9fA8n+ZSk9uf/TZlnnC0+y7bW1vY + tk290WBpeZlac0Nbe9tfFv98VQjMQFWQT9vY3jK2N+8FCL5/R48NsxyffJpQJF0dSoAgC82BMh/iBBvU + 1r/MpdqHrF7c5O7L94RQB2sAPHn4SwD29vawLIu9vT2qquTDD58AUI/EXDw1JSeQ/QJiUq+PmHk/z/6j + qgPquVpKgFX8byYAYeaFmKSaSjDVQNGyLDTqryxL6alVFEb+YZarUJ7IrNJhnmNeFLhGSBNFdSaT8Yzu + y/fxjP2LJOGwm7C+FvGtb75BvdHQ1ODq/BfzJ2alRU0HEoqSEJEIPC0Btmh5T3PDTxN6U+DPEv5nzQWw + 8JiP8fi07/msry0+P/8qACBGdMkLKc8yXBnDme2r5gU1w+6LwRqjwYh+96e4rkdUj3hh48sADA4fs7+3 + R7G9TaPZZGl5Gb+2JEqAatKwo2b5JRSSr15NEBY/gTs3N8CrPye5ALs4fku/puYCQkmZ9ma04OOR4AO0 + t6GqhGdTVdy+fYcsS/nMZz8LQFC/SFWmbD9+H8dx2NnZoaoqDg4OSJKYg4MuaVbhe2pegCiJKYup2HvV + ABABy5UQWynwlBxTrIu4BLVOYtJVJTpzmZ2FKjkJufZC1EQiEN1/VVWRpCme5xHHsXx9QYFkGUe9mDCw + +MbXv8CVK1fY3d2lvb9vYCbE/gpoBWjMxVxpVu5TlpUPzA+AmI+5T8u+q9dy5oX9tMdnCf5JOYDThP60 + GH9x2+I6Kww4l/WJ5AAU3NeyBB21emyHM2CQmfU2p9lUlWiHjaW77Q5HuJ5LvVFn5fJnKdKBUAbtNmWx + Q7O1RGu5heM1xMQgr6krBkIRdGfU34rzP5vo0qLtRnrcWFUWVEWCZQ9wglXApki6JFMx9jqJY4qyxFeV + AgkJnkqMf7/Xk99nmzhJCHwffJ/r168D8PIrr2BZlvZiDncfEMdTOp0OSZKwv98mjmP6/SFH/RLXsXBs + cBwIAwvHFg0yCmBkJtXMCoSy+ID+bdV0IDUmTN0rcI9y081+ffW/wfGORqVwzFKnGF8+E9p2u4vrwje+ + /kVu3rrF3s4Om5ubc6XTk5KnKmxSYCQNW1Yl2LLyQDgYHBdwM+41hb5YuJ20/bQQ4GlCf9JjjG3m+Syu + p1nqkyz3aeuYi/+095yrAoinE2vnybZODhXSnXQch8OjIy5eFBN1HMfRTLmLCEGxLdUXXpomFIWgCbN6 + b+F67twEoUl/m87+AVW1j+8HtJan+PVrOEETZfgtRwwPqcqCIhliy4nEIk8wxK2J6sLiWDFBLx4c66hT + WH2Yud6eQfIJEPiCvCLNMqaTCY7j6JxHVQlKb8/zcD2Pq1evYVmW9iD8wMd2Qob9Q7qHhwyGQ8bjMcPh + kMGgT78/pD8U0FpJpygVhaWRdsKjEG5/ksyGiaqlcwm2yKPNVyhmswfV/6Xo20xvw5aoQYX0VIp/NByQ + pBVf//qXuXHzJns7O7wr5/6p6onJHiQqBdkcWEyxBimhV6FALCYQq+TfSe69EuRFgT9JAZjPF2P9xfj/ + JFefhc9Xz9X6qK79R43vf+N1rgpgbX3DWrqwxvbmIzZ3dnAcMU662WjQkLTaGjTjunPafzadVkwRSpJ4 + rpwG0qPIhUBZnUM8zyeshVy+IXr348GHdPYPyPNdlpaXaTSX5AzBSCiBUs4CdATjUJlPZN6gg+0Kr8H2 + BCkolmwWsh09sMTzPNGFIq2csmCmtVSPFdTWTKzNg4JszZ47Go0EoYb8DXx58SuXfHV1lcuXL2tyFNdz + cf0mo/4BvaMj4jhm/+CAJElo7+0ymSbsdQqwwHfFjIDAK6nItCcRBL7hdQlMjTp/syXYRCjONzuVItMv + lTxAPJ2SpDmf+9zrvHT3Lp39fT548OAYYlL9PiYK0qxiKME3iVLGshJTr9epKp35X0y2mYKdL9w/TRks + WvyTkoSL1h+OK4RFQT8LpfdJLfW5T4M4n/d48BJ/6SVuf+4NbrzSYefBmxweHrL15AmtVouiLFlaWtIX + WFEUcmKNq8MFz/cpinwuGaYuBLPpxrIs4ZYnMYO+mBIU1SOuvvAVyjwhHjzmoL1Lmgr8QS2qianDQD7t + 4NbWREJQYgaKdCA5CRJJDjqmks1HjiOERTH+LmaogXnLaM+IONUFrJbydNRFrwg7FMmnb8TgehSWbKtV + S2T1cwLJ4x+GITdv3sTzPL78la8AENbqxNMxB+02cZKwv78PwJOtTVzP44NH4niBL8KMWig8hqgmCU5k + ZWMRz6D+D/2flyWTyYR+r8tnX32NF158keFgwNbmJmmaHpsHYAq+IiU1ORbVvfJEhkMxlfnq1WtcXFuj + Lb8Hx5N4psCr5+btNK/AjPeVC3hWPf+spN5Jgv5JWvWnhQNn9QQAn0g7cCqYdycDmq0WFy9dxas/x/7j + n9Fui7lwYRiyeuECrnSBRUY6x5KuoKoti+44X8eWZonMTCCVZUk8nRJPp/iDEY4rBo1euf0lAKa99+l3 + e+QHHcKwRqPVEOPCbdF27ARNqjKalQrjNpbjY3tL+rspgI2ygABplunynScv7FmtXZynmp9nDhNRVZA5 + 9F+ei/Hq1fwsAfW7Wpalqb1KKUBplulzMEuEZn0+kDTiN2/eBODV116TVRWh/LY3HwLQliAoVdF478Eu + ZQn1mjhH14Va6ItGryAQCivP6Xa73L51i29+61v0ez02Hz8GRLy+OOBT3VTIADMuhjn6syBgOBwyHo/Y + 2LjE+sWL7Ozu8uTJlpwlcSx+NwXd3FYgyoU5QgBM5bCY9FuM9WFeASzG+hiPf5Ns/W+yFoX8I6/zVwAy + 8C7Lksl4zGg4pBZ1aS23WHvuFWw34Gjnl3z48CFJkohZAY2GLheauHtBJDnVbbJKaFRcqOruQRBqXH5Z + lmTTlCSO8UdjMSMg8Nl4/nWcYIWk+w7DQY9B75fUohpR65JIHDqBLhXa3oqEGedURUqWxgLUUlVzgmaW + 6VLFc2CQkJjuvopvTQVSVZUWZmX5lXKwbVuz+qjSoXl8k+zDtK7KqqptE8lXMJINP0dHR1oobdumUa9j + 2Ta3b9/Bdmw++9qrWJbFd/9wHRDVl3g6pdvtiuNIyG+/32f94kW++U/+M4YH77K7va3zALOuR/dY2GAK + +2IbNYiO0m73kLW1de7de5nt7W1++KMfE/gWtahOGNZwHFsJuBLuRSVwmjJYjP0X3f3TrPyie6+2nQYk + elaBf5b9Pq6An9XerNcnQAmWUuRTwxqJbr+qqsjShxJq2+AL3/zn5NMOB9u/5smTJyRJQhRFmu5LvNfR + Qq+EwiTFmAlTSliriQajfGZZEpmsq6qKQe9tbMcmqkdcuPIqIDyDo/YjiiInrNVorb0gJgdlXZkLWDrW + Dpzlueb0V0KueP4V605eFFSGx+IyI0JRYBeNvCsFmYay5MqDAMEApD7bxOe7nkeWzpCCtm1rBmAl+Ipy + W03yMYFGpsAlkvZbJWyPOsrVfywUjycGmV66fFl0A9abuNEVgpVXABhs/mt6UjnESTJHLrIo9KbXo2DU + Cik6mUzo9bqsrV3kc69/nr12m7/+q79iPC2xQM47lB6gEN6Ek4XetPyL+YBFd/8sV19f0hxXACe9trj9 + k17PyhVw5vvOvRkIZjVoBXzRCsCI+cajv8XzPNYuXefSzTcoki7tJ++xu7vLuN2mIROHnrwA1Z+v4bHO + TDjyPNPDRGbWWXgJ8XSqpw8VecGwP2Q0+DlhLaTW3GB99RWKZMi4+5D25i/wPDFvMFj5HE64hhtdwR28 + r4Uyz3MqZ37oRmB0AZqYenWuWgCq6phVN8MJU8jLsiSR8wRs6fUAGhE3q81LQg8VTtgLLL8LMbyJMFSh + hokn0M+lV1IWBUme6/kAw8GARnNAfSqGlCaxBH5JhWfOPVh07dVvps4tyzImkwmDQZ/V1TW+8PkvsLO7 + y5tv/pjhuKQsBUeg7wpewLIsKIoc27ZSIGXeC8g4HhIoITcfn5Tke9ZMvvn8rB6Dv691mnfwzF7DuXsA + WI4ucU0nU7Is1WyuRV5QWqJe7LoeRV6QZfu4I+GWrq1f5MrtL1EkXXY377O1tYVlWTSbTWphiCuJPpU7 + rayZ7wfkeUaWpZpsQyDtFJGFLdtTXYpcNCtlWUbR2yHbf4znezSWr7B05YsaS+A1Rcwcd/6G/b02/X5f + u+eu685TaRklQXVvgp9U2GJL78BMoql43ZMuc57nQqDl+xWJp3qfya+vBFYeaFbjl9BjJWiLMwCUl2CG + MObAEY32k8fQ38dAGSochVKMZkiiFZNR71ewYXV+o9GI0WhEvV7npZfuMhgM+PM//yHTuCIvBEeALUiK + ka0TErno4Ll2hlAAKfNx/mLG/6xYnxMen5bIe1rjzSexPo5wmy3Qz1RqPPccgGXPXOaqqqhFke799wNf + WGrX00wxWZpp4czznMn4bQAuX7/L9Zf/MUnvPtubDzk4OKAoChqNBrUwJKzV5kIE1/UIQiHgswttfqqt + ek0JRFEUmp5s2N2G7ja+79PY+LxmN7a9ZX0RTyaC6GJHElMEQaDLfYAm7wzDENdxiKW1tgzrbFsWlbSI + iRyYoYSzMASokt6CEj7lLcQSfKRCEvXdzAQpzBOEmIKucgUn0Zmp0EYtFd4sMgmLCcn5MSVgWn7TC5nD + UBh8gS/fu0ecJPz61/fpdI5Is4o8R+MbihJKQyRVGdSxrQQRAiirnzHvAZyG7jsrzofTrf0/ZME/KdY/ + 6fUTFdknAAV2sW0Py8rwfE8LtwZ4+DO67rKYtalmaUZYC/VxJoM9yt4Onudx7fZr3PzMGtloi+3H79Pp + dCi7XZqNBvVGA9VyKtxDhzzPtFKYd0fF62mSaIScyhu4rqivp2nKpPMW2Xib+pXfw2tcp9F8SBLHNOXE + omVJvdXZ38d2HPr9Pq7r0uv1KIpCZ9JVrkKx6SiOvjAMtbApIYMZfbouc0q3W3kdvufpZKFSHsraq3l7 + iubLrDAoNmEzBjc7ES1rRshh5iHUTe2PIdyCh8GdkadY89ee8jRUuBHHMeOxSMrefeklSRT6LoedfbJc + Cr5l4dhSWSurX0LoQ+DPSFxrgW1afTMReFp9/6QM/1nlvJPWJyH4z2LNz3ptcZ+zlMFJzUDnrwCKdDD3 + XE3+VRcyoCf4eLKbzq5mIBQzkSUaV2yy6T6T/jau53L11qvc/tw9kt67tLfeZWdnhyAIqEcRtSjSLahJ + HBOEol1WKSIzAWZesLUomrOeeZbjhQVl1ic+/Bm9bpeDTod+v0+SJDrRuLQkOhWbzSZFUbB+8SJVVXHv + 3ss4rsPhwQGu59Hr9XBdl8FgQJqmtNttjXxTYCnHmQ02CYJgDkDkui7uQnUBZrTrJ2H9zcRbYVh59f5F + jwFrNnXIMXIC6tga06DCAbcu+zD6xz7XrHKkacpQEphev3YNx3F4+513GA76JEksY3t5rUjS0DSbyZpj + i6s1l+PZAWqBA093+z+u1f+4ybWPsj6KlT9NyM86xmnnf+y181UAFZXtRlaRdIinMbYjyl+K909dTIVd + zFk2mKHoiqIgTVKZmBIXcC2KBOV1WTEd7DDqbuEHPlfu/DbP/9Z1xjvfZ29nl3a7LQQqCMScAOkiK+7B + GRGouJB8ya8/6zIzEmZlqlmJ59BvuvTmMBwOsSxBmmEqFsv6gFKCnqqqotFokCQJS0tL1CQNmR8E9GRJ + TpXoFBffYDBgMhnjOK4ci+bPzQhQeH0z0ag8AXFu9pyiAOaUh5mcBAnAMRKSaty4wjeoY86h9cocmOrf + RiECVW6hqioNXrp16xZBEPDee++xtflYn49JCuq6oEY12HIgSCGcDpGyLyumk7EctXYmuk9cieZVeXwt + Cs9JSmBx+9N4Bp62nkW4T3rtLCv/tOdPPb9zVQCT0ahqP37LchwXz/fIs5zpZEotqglSD9B966pOrMEf + YURZinyA7QiLNZ1MSZOELM3IUhFSKLc6z3KG+28TDN7HCdZ4/uW73GncZPTkTzlo73N4eIjjOKKs6AeU + Vonne3PhgKocqDFiykPwfE9TkNteU7vilHICAAAgAElEQVTv5vjrxVhbcQSaVrXfF9ax1+vp30gkBIWC + q9cborQmCUrr9TqNuqAHrzeajMcjijxnKslIJpMJ/X5fshPFOtmpQguVhVe4Aw2mMmDXapstLb6p3HyD + 4UiBjtSa5VFyQeJaxNiVp70MZ8HdT9OUGzdu0Gg0+fWv77O5+Ui8vyiENV+gBS9LKIpKmGsp/GUJlgWT + uKI7KPnal1/g9q1bfO+nP4BnR9kpq7coGE9TAuZ71eOz9jttfVRvwoTwnqU0TnPxrYXHi57OJ1cGLAr7 + g2ar9cLB/j6Hm4cEQUAYBLoSEISBjnvF/oX2AOLpWHsI8TTGdUXTT6PZ0DmCshSsM0EYaCLMJE4g3saP + D0h693GCNa7de41bjZtM9n4kSEmPDinLkkazKUhAPVdPC1ZK4FiN3HIkEGg6l3AzCURNC1oUszFei6+r + hJiyjkoYh8OBtPx9vZ+qGJRlge8HhGFN/I5hSBiGXFhZIQxDGs0W0+mEJI4BmEyn5HnOaDQijmO63SNB + F14TTMZBEFCWpUhQSvgwzCoWZVmSZhmBP+sRMO81pNdxNFuTaJQa60TnaDQiSRIuX7rExY0Nfn3/Pg8/ + +LG+PpIkPnbNKGbgLBPCXxQzy59mFcNpydV1h//4n/0jgjDUkGZmFGDKxVcay0TxWcZ9tfB40bqfFhIs + rpOSas+y/1nPT4vdT3r8rHmDZ/IEzpsTcBw21rmx+hLXswl7m39Hp9Ohvb9Ps9lkaWmJ1tKStkJmLRuE + Qoin4iJR8WMYSp5AedGlSaq9COW6x9OYJE4IwgCv3KfMhsRHv8AJ1rh055tcja4wevKn7O+12dne1lWE + eqOhhdZ0/7M0w/Wn2G5Eng7Js2yORFMJjUi+JfL9trRk5VzibDEzr1xws7/BrM2btF95njMcDhiNZkIq + FIvgCfCDQFp8R88uXL94UXy3eoOqquh1xXSh0XhMWZYcHoqZA5vSFVdzC6MowrIsPb8wqtXmcANmUtCs + CFiWxXA4pNfrsXrhAhuXL7O1ucn3v/dviadT3UMRS44/ANd1sGUYlqSi7KfmAViWEP6jYcGVVYfv/OMv + ceHCBbrdLvZwKH8D3BOuXZXxVxz66qaeV8wUxmLJ76NWAhbXorWFpwvqWZb8WZN8i1b9tHM/aVVw3mVA + qJLxAcnRE4q8oNlqcWFtFcet0Wlv0+/1aLfbLC8t0Wy1aMopNWa+oN6oa6FU7jlIwXIdPdFHMeK4totf + +nMxsL5wp/tk031c/wHhhde4dfUKlh0wevKntHc2ebK1RS0MNRW2Sk5aliVJRlMct4YjKamTJNHEoLOk + 2rxACI4+JcT2MW9AzAkoj3kSi1l0k9ILBKWXmg+gVjydMh6NKMuC/XYb02tXvQv1RgPP86jXhYK4fPmy + nLT8WwRhwGGnQxAEGiLc6/WwLItOR8xXUJ6D7/vU63VazSaWZRHVI6ajI7Y2N2k2m1y7fp2HH3zAD/6f + 7zMejXTuxbYdPW3YPC8Qwg9iTFiWVaSZcPd9D/7pP3mdSxsbtPf3efz4sUj01utSUVYes2vXZhb/Ozy9 + s2+xy29RCSzmD9RtMQSwTnjtWYTedNM54fHi/pzw2lnbFs9xcfvc9zh/QpCyJE0SppMJ/V6hO/2arRY3 + bt/CcWsc7D2h3W6zs7PDUqtFa2mZIJz1Aqi8gBJ+QOcQbNvD9j0UP6BIEtZmsNOy0qVHx63JcpVPNt4W + hKG2S7B8lxvrX8Z2m4y3/w2d/T12trdp1OtE9YZ2fQHKIhZ8/PmMGccUQpXHUNZb5AlUydGeO5a23kbt + 3oTKqqVIVGZKZvaZ82POZo+LssK2VYkPVD7s6EjkHzy3I3+/+eEcF1YvYNs2KysXqNVqXLl8GdfzWF5Z + wfVcBr0BRZHr4SFRvc7lO1/l4NFPyLKUe6//Lk8e/IQf/fCHjMcjfWxP9mmo72p6D6qKEvhiOlCSVPRG + FaFv8Y++/So3b95ka2uLBx98oJOPKvGZZRllpQlBlHU/i9BD3eC4cuApjxdDg7MqBmdl3k+7P0nIn0Xw + 1Vr0OE7zBE5d5wsFhtJxHcJajamcV4dEmB0eHHB4IKxia2mZlz/zClVV0dk/oN3eI8syWs0mraVlGk1j + HJhj65wASOyAY+P6TWyvSRWLYRSO18AshhXZCKpCk4nabqR5AvNph+zw53jRVbzGda5tfJVrVSmmDu/s + Mhz0uXJNfJ7feB7bEeO0ZjX0GeLOfHzcipfHUHn6t1ooRyoUoBJ+tRSEGpRHUWgmYNOaeu5ZygCdcQ98 + Qb8mFEnB0eERtg29rmBdVpRkyttoLS0TBAGrq6s0Gw2ufuaPaFz7fWrr3yLu/CXv/e0fsy2BUaJ6Umls + gElWqkKBNJmRtWZ5RX9YUZQVv/ONz3Dv5ZfZ2tz8f9t7tydJzis/7Jf3rHtVd093Tw967jPADAYDDECC + XCy55HIvDi0Vlte7EXqxFQ4/O/Rk/wv2g94kvUhPCoUiZCukDYetldarXZvc5QaxwBLADAgOZoDp6e7q + W93vVXlPP3zf+fKr7KzuBjAEQXK+iIqsysrKyts53zm/c87v4OHDh5LFxBiRqQmroijwGS04UYOfxugj + JwPJVgCQ1AUg43sN8wpB/h0wP/ufhgekhRrS5zShqbz9aRjDIuV0Uihwzmp51i4AADZLmdx0JP+RWF+i + KMJoOMBw0IdhmiiVy1hd30AU+ei02mg0jnBwEKJareL8hQ3o9jkYdgjf6bCHSVUQhRFCfww1CqCqBoLA + RRyPWQKSZrEGpZrNGX0YhXcchaLcV8+twChehD/eRej2WEORyEV+5R5uvPB7AABv8AkmB3+Jj3/2EJ1O + B/1+Tyg1TWc0ZjKDDQAM+n0W4pPYgeVYulwnkEXbHceJ8Oi6IfgSaF9syRKdKFWWZnSZetsymZLyg1gg + 7KQIZk4Ay0z+m/SSqqqiQxMAKKoCXTPgeS5c18FoNIRhGPCDf4lbdx+gc/QUo+EAM8bQgzBkhUehBCpG + YcyjKiZ8z0PousKSGU0i+AHw9Tdu4uWX76DTbuHDBw/mFKllWQiCAI7jwLIskU4chDBxXAGkhX2RQohT + y6wU4bSbkIUh4ISlPBb5+Ce5AFkWgGzWRxnbZY3TwqHPuhiIzdC+58HjN5sQdDIBdcPgmXgqI4xst9Hr + dKDpOpaWl7H2wk1E/ghHB3t4/PEj5OwdVKo12DlbUGVRtWEcz3iarIUw9BCGHkDdhYnl1+tzCyBE6E5F + j0E9t8IsAt2CopmcD2CE0O1Bz1+AvfImChf+AG9e6WNy8BfoHH6KdruF4XCI4XCIyWSMfq+LwSiCaST3 + IYqGyBeYBUOhORJ8wijEHYlZx90oCqEo8xaCSHE2DDEj+5J1oOvEr8+EOQjYe2owQus1VeFptTE09fjz + kvD0c/xDVURfB0WDuI9E7tpoNOC88wOUS6W5WgRGOR4cc5FI+H3fE0k//VGEN9+4gdfu3cOw38fOzrYo + cpKLmCjDUdM0FHhhmOu6CMMorQCy0n4pISjGvAWQlSFIGlFephXCWeoH0gJ3kkAvEvxFvr68b3pYFuET + i9yAYzjHM8cADNOAYZqYTaeYOQ5MqZpPURjrj5zDL3PAdzsddNptGNwHPbe2Ct/z0Wm30Wm3WFgrn0ep + XBbpxcxPTv5fN0uIggm7klqJswSzpB4iDI3DKaMB4y4BNQtRVEYHxsJ/E4ROgFnrXXhTRpZRqVRQq9Vg + 53LCJzUtE43DI/iehx7vlNNutxEEPhqNDjw/hq4pvLouIfZUVQZoUvPPGKF4DwB+4B0DB2XKb3kJQFCA + UyZkFLEQG1kKSWote17IMqBnPopCjtonZc4iG1Cfz/X3OREJkaCQBeM6TsIxSFmI3OQfjCIMJzHeevMa + XnvtHqbTCXa2t4VSI55AGRPxPA+5XG6OXi2OY3hBZGFeAcTIdgPS30eYF+40E5D8OQ0YngYkntUK+KwK + QBbwSPqcFXlYJPQLx7NtDhr42nQyFWZfsVAQ2X0Ao6I2eUUfxcOpVj3gCTYqf5h63S6mkwnyhQJWzp1D + rrSG0OuhcXiEg709loLLowwhn/XjOEAcutztAIvhRwFUo5RcjcgV1oE8GFMwEx7WGdgSHYbiKIbjzDCd + TFiWXKcDIOHMty0LumFgY2MDcRzjpVu3WMGNyWbN5lEDvueh2+vBdV20Wk14noejoz5mHqAqLOXVMIA4 + AiyL3e+cncTk5Uo7MtXlmVbO1GPr2TUngaf3hq7we8UsgihKrAlxLbjbpvMEo2SWn2f2IYUQBAFmM+Ye + 6RorIaYEIdcLMBzH2LxQxj/6/f8Knuvi8PBgjviEXEOylOi3xWJRKBxKnWbVkrGORAEsAgFPcwVO+y7t + HmS5Dln4wWlDSb2A+Rk8Hb+XP8shTdkVOOm/vzwFoCrKarvZxHA0Yr3s83neITbiyTweAinZhvnTOiDd + eJUzBhumyUNzIXrdrghXnVtbhWEvw3c6aDWaePrpE9i5HCMBrXDWYQ76BW4XYRBCCx3oNvuOXAAAjAKc + dwYiC5w6D0XBCJqW512IEwoyheehkjlPrDtmEGDC2223221Rxw8kkYL19XVEUYTr12+IrEZVU3F0cAjX + cdDi9OCtVhO+56HRduH6MUwutMy3B/I5TxB70jHMhw1l0x6IImIQTp4lUgTs+/nIAoGLsttCVgEJqT7H + 4uxL6clUoOSjO4hwabOG3/+9r6G6tIR9TvwiVwdSghFZO3SdaWIgohjKu2DHIPoCZCmAtJmfdgcWCX76 + 91nuQRo3ALIVAqTvgNNxgJNGOhSZVhZpJZEGLk+0EJ6pArBMI/fib/wjBJM69j99Gzs7O6KEt8wr6Wj2 + Fxlo3MejdcRMA1IUSkKuEQQB2s0WoqgBXddRW1rCxuU7GHV3MR6N0Ot0YFoWazSaZyHDWOOc9U4LurUk + hB8AYwX2RyJKoKg6QncEPWeJPAAAc4VENOuSMhDHxgXDNAxEUrJMGEUIeFUfAWbUY5CsIJvzBl7Y2ICi + KLh165ZojKIbOrafbAEA9vb3AQD13W2oqoatOkuusbnFwAvmOCYRCICPsuxUlbkjlqkgiSDMKw4gUQiq + hFnEPBRHFomM0hPoGfLzGoxcVMs2/ugPfwvlSgXtZhO7/FmQ6xDo/NOlw3Rdfd8XCUoAI0MZj8cwdcUC + e3ZpRiTEPkx9pnUnfaZ1Oo67C/K+snIM6OKlLYCTZuSTYv3p355mEaT/7yRhz3RRni0IqGpBMKkj9IZY + v3gbL7z4W3D7H2Prk09R39tj6b35PErctJNpo4DExwSAmDuvqsr8ySBk/eap5DUIAnQ7HaDTgW3nsHxu + BXb5Cmb9LQz6fTSPjmBaFgrFIkrlKuI4QOSPgIDq/EucBZh9Zo1BuP+sW5xOnK/nQhsEgeDwA+ZTmamw + yefknsTyKys8ILEGCAeh3HlKc6brIEcJLF60dP3aNQDAq6++BgAoFAtQVAX1bUbftcsJVPb36oiiCJ/s + sqSlvKUyZh2DuRpBGEPXYlimIpqMyFhD0l8wiXgc2yaKoPLjZz0TPXT7U5iGgu/99rdw8dIlHOztob67 + O1f7AWCu/ZicDUn31nEcTKdTEQUIwxCTyQSrq6sol0qI4480sDyAtBBrmPf7s5SDntqGfpee+QPp/SLL + QE99f5ISOMkaQGq7k/Yh+/1ZsX95+0h6n6mUnnU1YBQFLuJwhsmwjdn+I1i2jRt3vomX3qihf3gf+3t7 + 2NndRaFQQKVSERo+CkMo5O9KDz/hAgCbYVQeTTBNUyDEvu8xy+CoIXCB89d+A9POQwwHA+zXWSZZsVRC + rriEOHQRum1EvsXzA6Q+AQCvdAMjvJA4ASk9mWYrXddhkNBLysyXagJkc1dO+KGHXibOlGdGmWTDoXz/ + KTu+Du+nR6i5zUOuN2/eRBiGuHv3VQBAoVSAoijY29kFAOxwYTzYZ70LP346hWWEsE32DNomay6Ss9ln + FsVIohPkiwsegjCE7/sYDgfo98dC8DvtNp5ubc11gKJBAi3XXdC5K4oi2o3n83m4roter4tyuYLr166h + 2+the3sbIesLQMJH6b+UEajgeDpwhERQaHt5ZifBpvcRkpBfWlGkrYxFACEylvI4LWknPdunv0u/Fu3v + y8MAaETBRPiLrG3WNoBtGIaBl+6+hVuvWWjWH2B7extHR0coFouoVasiPVbTdSBmnYVBWXMqo6Qiv5SQ + Ybnu3TAMOLMZ+89GUygDo3ARzvApBr0+el1WJFMslWDZQOi5kgvAyUh5h+E4CqCZZWjaUULUya0QGi6f + tWU3Jk2/BSSzqlxWTNaAbPbSvsnFiON4rvwXSJiDZ7MZbNsW66czVrjU6XTmAEI2a8a4ceMGFEXBG1/7 + GuI4xh/nbPiej+YRS8RqtlrwPA+dThue62L3YAJNVcCTMFEpqaJeIBTCP8Qrr9zFxUuXRCOQOUtOMu9l + t0c+H/l7+m2n04Fpmrh27TqCIMB777+H6WQCwzQRR6IzkHjkMJ9YIysGUgBqxjoSbPpt1veqtIwwD8Qt + Wi4yyU8STvl7AvhkJXAS+n9S4k/Wf4v3z14BxKG4kbl8HlEYiVjybDrFbDqDoigoVyp489v/NUK3jf3d + LexxZL9cLrMuQso8MUgkCRApAjnGTkJHcfAwDDAZjzGdTBA3WyiVy1g+twajeBGj1kO4jot+r4diqQQ7 + DGEVLwoAMJi1YRplIA54eDBp+EHknQK0lNOGI1ZSKwCzKJpzb+jhlt/7UtqvDIjJ9N70f6Qw5BbaNKPS + /6d7CgDAcMTcmCEvpqEeAKrKOg+xkmkT165dE/dNVVXkCyX4noNmo4EwCNDr9wVqDzDF8vq3/lsMjh5g + v16Hy1mJ6L7J5ylbTaS0ZXZk2u9gMIDv+9jc3EQYhnhw/wMctRzkbQXlEitcihGT6UtAhiwAWUKTBZ6R + IMu/l60EWTnIFkRWhGDREqllWgjl/dJYJOSnDdpf+v2J4+diAQAQcWFN00XBDKWymrywxmsztt3zFzZw + +da34Q6fYG9nF3v7rL8guQhkBQiTOooQ8K45tA4AFMMQdGAAhcZ0BIGP8WiE0XAIo9uBaZmorpzHavk6 + 3P7HmI776LTeQ6FYZGzB1RuIfNYgNA491o5bme/6A/C0Wekzcef5QVIaTAIvWqJJVZCyH6woiii8odRh + WUhkEk8iB5HdBo9ThAEQSoIUCL0o3CYrFQAYjcdQFAXD0Uj8p+y32xyDuHjxEizbQrG2CbN8A3E4xeDg + HfQ6nblQIZ1X+nxlIFW+jmEYot/vYzqd4vLlyzANA/cf3Mf2bh+6xjALkl9VVUks5JlZFiYgUQ5pIVBw + XKhpfVroYml7sh6AecxBxbwQy1bCIkwgvf+TrIJFSiDrN4sAwBNdgmeuAFSzBN21oOsBDMOE48xgmpbg + 3HMdB1HIwCXKE3dmDibjB9A0DZdffAM3v34D/foP0Dw6Qr1eTyjCTROQQDhFVZkzKHW0DXwfiqrOCSsx + BLOZ1YPnuRgOBgCeoLa0hGJtE9WNFXiDT9HvdjAZfYBzl9+EotmAogmhFLMmFy5a5weBoPsGmAAaui5I + PmUhJ1JQm4hNONIexazBaBiGCCNOsJHCDcitIlISIHE9iB5MFj6ZnUcG4YhmjLaV25HJVgqt4wU4CIKA + pQN7PipeH4rGFBZZa+nCJznFGUiUA7l6vu9jMplgNpthY2MDpWIRDz78ENs7R+y+MU8QtslwCcOk+zgH + fgHz5r9s0tN2cuZc1jJrnawgFvnyJPQyOCj74ulZOcs1SO+PRvq/5ffp/1iEAZz0XwCePSuwEnkjxHHA + UXN/Lr0zDEIUikU4M0dU+nmux8tG2Yw3aG9D7e7CNE1cfvENXLtbQ+/gA+zv7aHT7c5FERTwB9TzoHFM + IEqZ3cxMp/p8RrJBxBSKomA4GGA0HCJfKCCXz2Ht2m/BKN2AWbmOOArQeu9foF7fxWg0EgIg03TlOTsx + HT+NMGTNQkgJyP0AANYHgKIFpGCmvNaArAEZKCMrgqU+J/0PhGJR1bnahCxacDmXQY5k0HHblgWzUJhr + 6yWiF1SVZ5qwbEuAo+mwIF1/+p0cKaHtoyjCZDLBdDrF6uoqrly5gk8++QQ//KsfA2BhzCgCVIN+w5Q4 + FQZJT7SsCNKzsPw9Tlmm/XtSGHLIEJi3KGQ/HaljyAoLyiML4DuruZ8W6nSWYNb/LDqOZ6cA/sk/vmux + +L0mPZQsGYX4+FSN+/R6Ut7pzHhs3A9gmIbYn+u6cJxtqOouiqUKXn6DtQPvHH6Kp08Zwkx4AatCixDw + WYcEgR56IHlQfd8D9RwMwySZh+ETU+Smb6O64cKsvAjVyOPSm/8zqsv/Go2DXUEKOp1OMRqN4DgOfN9H + jqcGk9ASMEdRgiAMxZOT40w8QRgi4KYxId+mOZ/5J5v4ZDbT97Igk08uWz3y92m3RbYMCEtQFMZCTN2N + qMGIbFHIqD3iEIpWEveMFBntj0x/KuAhZcKQ/R7OnTuHK1euYGtrC+/95O8QBKFIlSa6MDpsTU06FPGR + Fuw4tQ4Z3y8y10kByOY+rdekdfQb2qesFGQeflmRAMejBcg4Fvl4geOze1q4s5RM1m9PdSGeiQL4J//4 + rsX3K07C91ijDvlBNsD6AqiqikAJ5ijDSQlYtjW3b2fmYDwaACNGvV2pVfCNy/8Q/ngX+7tbODw8hG3b + yOdysDmLDc0ywPzMxCrebPG/asDoxGl7wzDhOi6GR/ehqBbMyi1MDv4CjYNdtFotAcyVy2VBJGJZNoaD + PoIggMu74VL/gBH3qYmfz7IsIWyaxlh8iLdQNPzk3YDo2NOmvjyza5omGnJQw025LddcwhVXNhRSpG1k + LICiKTp3X8hloesX8DAs2zfLq4jCCDEXfHl/siViGgZ83ki0Vqvh7t1Xsbu7gx/+8AfwRKejEFTCrKqK + KBxidROY62PAR9rMR+pzjGwlsei3aUFMWxZZbkYWmCgLN+03vU7ef3qchBmcJvDpcSoQ+IUUQCL4AAAr + DEMEs/1jZmFSBssePsMwEXksXdSEJQg5gSQ0BCTuASX+AEyxOEcPRL+Aq3f/gAFR3a5Iwc3ncshz9hg5 + 0Yj+GwBm0yk3KxOrg2Yp3/fh9j6CapTZQx5FcF0Xw+FQ9NKj8yO3ggRa13UUi0VYpokbN25A1w2MhgPE + cSxouUajEU/5bSEMA1gWa6FGiiLH3QoqkAGOm/QC05CE3OB1FlR9SdsCEK6DfF/k86BBAqwoSTdi2Q2h + 7eM4hsIjPjHHL6hBKm2raxo838dgOIRpmrj76qtoHB7i7bd/jPF4NFcYBhCbEuNYIK5AunVkaaUrKunv + cFxYs5a0LaT18my+yDpI5wUA2cKe3jbtJiwC/c5i/mfN+idlIC4C/35+IGAcx1Hj8AiDwUDMkKZpcdMw + 6WJDRTIWLEbqGYjfz71YFpo650d6nsdi8aoiKMJz+RwuvvibuPLqGiaNv8H+bh37+/soFYuwbRsqf8jk + vnWyeUyApOe5ouOQXDAk+84yeMaOJ4TjsJTc2WwqzkNG5C3LFrx7hmGgUqnAtizkCwUUSyV0Wi2oqorR + eMz3M4Pv++h0OqIIBoCwIKgoJi/1MyDGYhp0zWS6czmvn5YyhkCYiajUlNwninAQK3CcT5SIwfP2tWie + A7Hb6yGXy+HSpUtwXRc/+uu/xmTCzjHwfVFlqEKdE34AgssAgFAC3U4b5zdeSD92aV86/V2W0C36zSJr + YNGsr0gv+t1Jx0MjSxizcgiy8gtoW6S2zdr/qaHEL6QA/pd/+sDlVoAFAGEctzdvvrW24Q2xt/UA9Xqd + 5exXqyiWWAkvzWxkipqWKeLZMmU4YQKyuUtCRWm5rDw4B0XRMWo9BPAQ+fI6brx+D1EwRf/wPppHR5hM + p8jxRqXMP9Vh2UkXIsr1j+MYs+kUhWKRVw2qghKMQmhyvzwmeNTW2wdlzUVREs+XQUl6+MlXBoA4jpDP + F6AorMGIruuoVqvI53K4fv0GLNtC8+gIuq5jMBxCVVW0Wi0oioLDQ8bEY9s5vmTnlMvlxDWTcQEC9+Sk + pjR6L6c2yzUYBCTalpUocyVpkqqqrEOx5/uYcQLQq1evIgxDPHz4EM1mQwCovu+zbs5kySDJ5aDMTipW + UlUNw5GPMBrje7/9Xdi5HIDHdLgnPeDp704STDm5h7ZdtO+0y3HWkTVTp4U+K7cgqzQ5CxdYBAKeOD63 + AkiZ/wBgxVEcDps/QxzHqFSrWN84D2fmYH9vD0eNT1AsFlGtVlGpVuceOgAis09GuylSQDMTFZ5ougbX + cYVPHoYJzdR0eATD6UDTTJRXbmDl2t+DN/gURzsfoNlsQlVVFDgJKOUJ0H9QE1PhOnh9qJo9B5gRnx2A + uXNgwh7MJfGQSS6HwtK1AYZhwfd91jF5MkIYhPiU1/RTa7NKtcaSp8plhGGI8+fPAwDu3HkFuqGjx8uT + yYLo9XpQFAWNBgun5fMFoTCjKBLFR0DCKUCKTVZuJJDUWszg90jXjblzp/szGA5FEk8ul8fjx4/QaBwJ + 60vXGcMQWVFkgWi6ztA2nj9BYdvJ1EN/5OHbb93Bq6+9htFwiFarBVNXTJw8s8sz8lnR9ayZPO06pBXK + SQCe/Dn9XZYCSFcopi2A9Pqs/aXXnTq+qAsglEAYRmYUMRBoMp5gPBpiNB6jkM/j/MYGbrx4C63GIbrd + LnY5k2y1WkUulxdNRGjk8jkxaxJgJyPMAITJS8JGQqtpGjNRjRjhpA5/ugdVs3Hh5rdx+fUrmOz/FzQO + 9wULLpnibJ/HC2MQJ+EwOeOQzGnZP1YUZc5Xp2NLC788qHSWMepoCOJA1OqTVdLttKGqKvq9LuI4xu7O + Nojyy9CZggCAcrkCwzCwtrYGQ9fx8st3YFqmsCA6/Jy7Xb6f3R02q9s5EUYkC01V1aQSjzcSCcNQRG/i + OAZilpPQ63qG6lgAACAASURBVPXQ6/XwwoULqC4t4fGjR9jn9QYy2EiEqHKyELk3nudBURUYuglnNkOr + 6+PGtVX8g3/wDURRhL3d3TkClIyRFTcH5kG6LFDvNDDuJEtA3t9J+8iqFUgL/6Lv0xbAIlcBWCz8C5XB + 51YAvaEXVktmrChQAFiGpphxHAnJURTWpWbmONjdYdVq5VIJly5fxov5MlpHezg8PITjOKhWq6wFeC6P + MAxEbwAAwm1gmYUuR/L1OYGkUFQURqKCjRQKE1AfcfQxvMGn0PMbuHj7ZVw2yhgf/pARjBwcMLyAs8/I + 4cgo8ueANLIYyGeN4/m8AxnlJ1dFVlBxHEEusGH+dmKG64YOSwIt06g6A1PZ8ZCZ3Ov24Hoxmk1WJPTo + 0WNp/8DycgVRFKFSqaJQKGBjYwOWaaL82j0AwGTMUoUHw6GIXkRRhHa7BcMwRe9Cy7KQ8zwo/B4AMzQO + D1EsFnHx0iV88vgx3v/gfX7+SZ0DwFwksuI0TRfXjZQmu74aer0RKpU8/vgPv4tCsYRG40hYLwsUQNbM + nJ6RT9teXqa3WyQ8chgwLXjpmTstuOlahCxhP+13SL0/i/AfO5fPrQD+13/1cQhg/D/98TVjdcmySwXD + iKJIBZJKN2oXRQIwnc3gcFwgl8/j9p2XAQDddod3fOmgUi6jWCoDSGZVkRxjW3M+taqqmIzHMC0L+UIe + mq5BA7MAdEOH5zJTWoCKWoAw3AZG22x/pcu4/sLv4YZqov/0/8bB3h4GgwHjMChVYBSvIHR70LT9ORdA + rvJLI+n00JPgkxuQAG6cqCOUGnDqukhmIqowYvwRCLmhi25IMu7ArgOj2KYRRkkIDQA6nQFUFej1Rsfu + o6oC5XIRpmmiUmGK+OLmJizbRrVWY/dtMmGREcdBpVrD6uWvo394H9PJBC+98few9/hH+Nu338Z4POJu + hCaSdgg/0DQm+HQvdd0UHZVUVYUzm2E6c/HNb34d165fx9HBAfb39+bSolWekszHSSa5vC4rT2CReZ9l + FaT3m7U+PSMvMt8phTi9Tl6fFnRZMWT9V3pd+jgXfQbw+RWAeNr++b9/EgDo/g/fv+TefWlDESg79ycp + PERCEKsqlDDEdDIRDSTKlQruvPoGnOkQ7WYTOzvbME0TpWJxThkAEC3E6X/sXA6+52M2ncHO2QLVp1mY + zG4ZIVc1FapqwB1tI5geQDVKKK69gZc2fwdx6KFf/wEGvS7C8E9ESzIK0clxddonYRZJzJ4e9nBOWZD5 + 7HkxFA0sOhGGcyCjTMktF97IyUCKokDTiU+AMh0T9JwsA/ocRZhTCOlIWr8/hqoCR0ddEXcHmIVh53Io + lxk1+Pnz57F+/TsoX/vvkF97jGnjB/jkgz9Hj7dFZ+etzbFA032na0bXkVU0hrz6cITXX38Ft27fxnAw + wNaTJ0kGIseFqG8hz6hMo++ngYFyqC+9fpEZf5JJn2W6y4KbFuYs4tIsvz8986dnfyB71k8f78/fBUgN + ZTT1Vd/3NFaUErBSXj4MqQtOFEXwKfzGn7RBv49Bvw/LslAqV3Dx+l0Mu3vodTp4+nQLpVIJpVKJl/s6 + IjcA4OAdjyTEUYwwYrFp0zJFw1HfS3LkKfU14Ay2MAEoUzjd+wAAzVpBdfO7MIpXEPlD7D34NxgMh9ja + eoIpR/GJgtvgPrMcX2fuQjDn69JMLocGyR2QrRkWZuNsvroGRVFFaE1O/dU01tdQTrNO6L/mhT3NAcju + +ryFsEhBaCrrPkTH1O/3EAT/FnfGu2gdfArXdQVnoFzZR3kTdK503KQAqaVaq9XB5uYL+IPv/32MRyPU + d3ePhWjlpK6s5w5nBLsW/G4Rmi/vM8u/zhLUk+jJshTASa+0BYGM9/Kxycf9mSIUz0wBHA2MfL0xcdY2 + eoi5uU/aX54V5opi+M2mtlvjyQST6RSHh8wnX1pZwaWbr6Nz+Cl63Q6msxmKhQJvd2UKs1g3GCAoM8cC + gKYlMzB1HI6jWDDv0HZzGXdxCG/wMeJwCqN0Axfu/BFWLz3F3a8NETgtHO4fYDweYzKZoNPpwPNc1Ost + KCrmWH8BpihsHpKjIYOAqqpBMRIG3bntohhQE2xBjtOTkCWsQkz4wyiJnQMQ62jQe5kPUE67JUUAsHbd + ADUU8aCqGjzXxcHBAVz3/4Vt2yiXy4AULfA8Tyg6SnCiQdZYFEXodTvQdA3f//4foFKt4mBvD65EtMKO + Z77teZ5HbcKTlUF6eZIpP3e5pW2y/uCkWTkt3CcJ/GlEpVkzvbwOmFdEJ53LmRTj51UAxzTN37y3M73/ + uPM/vvHK5B/eWJ/9N7//G8s3TUMTAFJ6pqS+834QQOFouWkYjGpKYaWpw9EIar2OarWKS1evQNNM9Lsd + HB4esFBjpSJCWmEYADZL87VsixFKOi4M0+BJKknxCj2MpDCIYMS0TET+CKpZRRRMEYcO/NFTDBofocsJ + O03TRK1Ww+rqGm6//DJLzMlV4M0GaDYaGA6H6Pf7aLdZ6vDu3hCKAtgWBB8fdeahXARqwkFNP8l0puNN + Zv+Qz9Th3Gfq+kMjipIMuiiORTovfZ7fVm4OMp9/z96TsgihajZ838d0OhUcDekEIkolpnoIsgpUVcV0 + yty+O6/cxa2Xb+Po4BD79TrCKILFawbo+KhQKo5j0RI+HUHJGFmZf8j4LAuRgmyhSs/CiwT5NAUQ4Lig + Lwr50Uv+f3mcBejL+rxwPMtMwGA8Hvd/+OMP//yHwNM/e2d648bF0r3vvjz+rZtXVzXbtkXnWRJ2TdNg + 8wIaUZjCH6h8joUCXc9Dv9/HeDSCncuhWCzh9t2vIfTHaBweodFowLYslpdv2zBM5iZoOm9xxWd3ovIK + /EBgBFEUiVbjADh4yPimFKntFrMgNDHLkcIY9Ht8izrimHXrXV5exurqKl597TXEcYx8IQ/XcdFqNDAc + jTCZTNBqNTGbTVHfZ6CcoTPrQdeT2ZmUhCz87FgWC23a95d9/WS7pOAmbQkYelLANf+7pJJRzojUDUNY + L6QIKAmJahPo2LudNm6++BLuvPIKHMfB7tNtcW3l7MqIcyrK1g65bbquz/Ev4DgGcBaXID3bp2fXrFk+ + 6xXg+IwuL4lTMK0sZOwgy9zPAviAbAW16NzOPL6oAqAbEALwAPT4QYz39g+O9vbx9J0HhbdvXiteubLS + +d733yqsViolFHjcnTQ7KQHKfQ/DUPSqz9k2HNcVeeZhEKDZbCCfz6O2tIQLV17BqLuLQb+PvXodNuf+ + ExmFgS9mWlZs46JkM2AxDMOkMIgrhyiMeD+BkHECAnMuDFkPQRAgCEMxU+mahslkkviuvR48qT4/Z9so + FYuoViq4evUqDMNEuVqG67hoN5voDwYYDoeo7zL23IOmAwWArgGaxgRR1xSO+OtzQqrx4hnqtksywows + ZW67MIpFlyDCBZiQs+1mDmMTJmVAIVgA8Dnjz1ydP1cGZOHJ3AkMfPWwvLyC73z3t6FpGpqNBiN8ReLa + yIlWBCCTqU/fq6oKRVXhJ1rtLJl+izL6TkLpF83maUHOeqVn/DRBqYwNkCIAjisA4LgikI89PT4PDgLg + iysAWeuGAKZ8OQHQBrA/mUzW3n/w6OkHivLo/a2L61c3ontvvbj35p0Xz6PIc/XJJZBZbIIwFBRT9KCE + UQSVm8bj8Zh1sjk8RLFUwtLyMjY2r6DXbmA8GqIz6yRFQWGSo85CTg7snM2BvKR9F4sOqFC0HBRVQxQk + YTO5Go9KX2WEHzxjznHduWIcEgSH06IpioJowIqDUGf7zdk2dyuY5aBpLBQ47A8xGg4EV99efReu62Dv + iHgImVJQlKRnAFGE06DuQCT4wjXg7wPE0PVEOaQSNMU5sKpJDbPZlLE0AUyQJZCP0H1N0zAej2DbOdx9 + 5Q2sX9hAu9kSbcJli2GOK5E/B1HECFE00xTlyaqqYjAYCPxGPjwcF5b0ukWoeRrgIyGVwbuslzz7L1IK + MggYpfadNeNDWsqKAKnvFn3+XOOLKIC09o340gW7QB6AMYAugIM4jneebu+sP93GznsPa29fvzy7fHW1 + /d3v/2a1XCrmBcpvmSYMw0A+l2PZYUrCxUelr8ScE8XMXJ+Mx5iMx9D1JuxcDhubL0A3Sxj2Guh1OnA9 + TxTiWJYtQDSBYNvnEDstPvtQoxDud0bMlKWa/UDyzWkZxzE8HsYDINwLCoMG3Fc2eEjL51YOAWYzxxGK + Izo6Evs0TROmYWBtdRWaruPWrdvQDR12zkav08VoOES704HjONir72LmBDhqR1AU5lYw1D+GbUKAlFGU + 4ABxxFiA2XkwK0BuNgokZCKGYXIeh6QZCdtH0iwEADyPncetW7dx7cZ1jAYjHO0n3YPpd4ZpIuDYDykC + 4iAAEu7AIAwxGo0wGAywubmJMU93znj+Fs306fdZpv1ZZve08KetgwDHFUiMbHrxNNAnH2OWQlh0Tl94 + PCsMgCwB2Z9ywS6CD2YZDAA0Aez3er1z7/Z6Ox8YxqP3t4zVzXPOm2+9uHvrlZcSq4DINMi8BJJZhlJL + NUCYjySYzmyGw719qJqGSrWKi1dvIvCnaDdbGAwGsMyZwAqiKIJh2vCmDei6hSjygTgxrRU1hzhKcvll + 85asFQBzZbP0cl13DvT0pYcdmK8jILBMDhvGcSwIR+j/iQqMlINlWTi/vg5FUXD79m1Yto1iqYhBb4BB + v4dmq4XpdIr67jZmToz9ZihCgaYBhCGQtwHPZyAlO6SAYwyBhBGQ0rN5JqPEVMSTnQBgNBrihRc2cev2 + bYRhiOZRgwk83z6WSFRDnuCj6TpCbjWQIiBCl9F4jFarhfPnz2NtbR3NZgONRuOszyMt02Bb1qx8koAv + WicLfRYguAjkyzLxz2rqP/PxrDAAecgaTtZ+HoAZmCJoADjwfX/58Sefrj3+BNsffrq6cuWBe/PiUvs7 + f/9bZbNSLiLHQz+2bYvZk7LCgjCco9kiq4Bi0mEQoNtuo91kVkG1VsOFq29g0mONQ1qNBmscUmBEoIrC + TUupDDiOZkkD0ShxI2SFRLO+SAHmDzgx4dBML9e9y8g+KQU57h3HsWihJSPrlERD/z2bzTCdTkWNhCBe + 4Uy/G+fPQ1FV3L37KlRVRb6QFzkXrXabgXE7O5jOItQbAXRNga4lNOC2yYDJQk5lCUZGNNfViF2YGP1+ + H3Ec49vf/g7snI2uRBIak9sjnS/7WQzdMIRbIJ//dDZDu91GsVjEyy+/jHa7jUePPpbutWIgOxMvC0HP + QvHl2XqRwAcZ32Vtm/bt035/2rdPux1pC+As4cpnNp4FBgBkF2KkkVQN7AK6ABww96AN4BBArdlsrjSb + zZ37tv3gZ/u58+vVzrd+915w8aVrq/NcfDwjjPIICCwE2MNFPP1ymarnMqCt1+1yevAVmPk1zIYHGPT7 + 6HbaKBSLKFcqyYlFLlSdNQxhIUJrboYPwlA0BQESRmAgUUie74vjBRgNGMXK5eo7Sh0GMKcwSNkkaHo0 + Jyg0yEqi/VJnHcqxoN8CEDn1FzY2AACvvXYPmq4hl89h0OtjNByi2WrBdV3s79UxczzUj0JoGpCfOqj6 + HkzTQo67aIPhEDdv3sTyuRUcHRyi3+uy7D1VRSiBgXEcs34P0gMjn4+m64ijCO1OB2EY4vr16/BcF48e + PWIhUykUqChzFFxpoTjNN88S5ixBX6QAFrkMsqBnKQB5LJr10+fzc7cCnqULAMyDL/SERql1dEFIGYzB + ogcNAHuO4yx/9LOHqx8BW492Xzh3aWN658b60W/+/jeXUSrmBFsORQ5I+En4ihITkJzgI5B7Lky6wRqH + rJ7fhFG8iHHrI/R7PXiui+rSEipGCUbRRej2kgxG3xfotAwCyqEpESXgLkmaK08OeSVZfGyfXmo2TGMV + pBzk97RPKjwCEgyCwpakTChDj6wHVVXR4ZWBpGwMjjkYpol7r78OVVWRy+cwHo0ZgepoBMs0MXMc5PJ5 + vPT1P8Tg4B0R1tN1HaqmiUxQVVUBRRHIv0D9+XmwRh+MJHQ4HGJtdRXFUhlbW08wGAxEPYGiKHNlzKnn + 7jQUf9Hs7eO4IghO2H7RzE9+oyz4yHhPy2cezvu84zOlDX6G/cn7JcJFeSm/dP4ywMqLcwBKAJYArAJY + L5VKq1evXH5hrex/53v3opWXrq4InMA0TVgSG64cpkr723JhCQkg9RusVKso1jahqBbc8S6s4kXouRWM + Gz8RJu10MoEfzKf5AgkOASTRArkISuQ4SKm7clowAKEkCF+Q19N+ZUafkFsTNGQXg/ZFOIWsOGTFKP9G + ztSk86PjoLJd0zBQW1pGoVhA4dwduH1mlnfbHcw4o7FBCT2UJKSyPg7U9o0UURwxHgDXcdDr97G8tISl + lRXs7+2h2WwKBUscibZtwzRNzGYz/NN//fCf/dWHozrmzfu0YGYJuCzYQeoV8e3TiiC9z7SCgbRcFM// + hc/0i8aJBdafc6SVwGl+GplMAdgNcMGwArIMup7n9RqNZmt7v/f4abv6yYMtJd7bOzq/vgTe4z6aE0ZI + +fcEGCqqyqBiUbCTkGeGQQDXcTDsHmE2bmPl0m+ifP2/h1m9Dbf9Ng7369ja2kKv18OIJ/MQQEcgnSw8 + 6fJgQ9fFLE7rKWRGuQVpUFCmGJeFUo6d05LyFNL5CrIlICuAdIGUbKbLuIOMT4iegDwpB0FfMCn5HqcR + V1laswJW6ERt4emYVf49wMDDdrsNKApu3LyJ2XSKra0tTDhvIl1HcmOoQWoYhnj7g9Y7O01viMWzPC1J + Acifs14UtUorhfQrC/zLesnPd3p8JQSfxs9DASwaWeEOYLHZRqDhGEAfQDeO42632+3s7h/V97raTx8e + lJoPHvUvWErPLOUTH5yiB3IpMX0nU3qZlsUeeiWpYAx8H5Hbgo4xABWqnkPRGuPipUtYW1tDzmL+L2UX + BjwxiQhDp9OpCF8CjAbc512FhXDy/yd/XE9VE8qsQbIAxpJiS3Lukxcwj33QkFF72kbm/ZdTeQnkI7Nb + PhZVZdyAlmXB4qm+qqoKTkQy+4UCVInjwJg79tF4DNd1cfXaNeTsHD5++BD9wUAcn3xsRBhCrk4Yhvjx + +813dppeD8cFX57508KdtS69Pr3NSYKfRvxPQvV/oWb+SePn1hoMx+O0adBGXieTLNLF1vh6FyyMOATP + KQBQ73Q6y51OZ8uyrI/qnavnl9+dvHV74/DG73xjFbUqiyBQ5ECe3eRsNSARBoCF81RNw3QyweDgHRTd + HpzhUxzu7aPRbGI0GjEug1wOlmVh9dw5GIaB1+7dgzObYcybh7ieB8dx4Loujo6OmK/LQUxyW1QpfAYk + 1Fz08BN9VzrzLp1fT/4/tQZbRJ4h039TpER2CeSZn/5TpgknS4MSc+jaqRojSaEW5wDP8OPHLHd2nvKo + xcbGBs6treLRw48xHA7Fb2SLjQYpIvr+qOOhH66pwIiEMD3L03vZnM+yBD6Lr58W8vRMf1IoL20JP2u3 + +wuNn6cCkIcs/PIFCXG8NFPu566C3SAViXswQZJTsOe67vLDhw9XADzZ2thY/aDu31rO73/3e/cU7da1 + FeR540tdQtXFQXFzWJjVUQRVyjuIpXRbgAnnbDbFcDiYEybW+zBiIUXbZq3Ii0WYhoFr164DAHpdlpAU + RRGGwyEcx8FwOOAzG+s0pOu6YP6VLQLZ/JetGhqyP0+uBvELyNiDPAvLFoLsTsj/Q4pHxlBIMclZkATq + 0aBrSeDfaDLBeDxGtVLBzdfvoXF4hJ+8+3fiugdSHgCQ9ACQ27HNHAd/+zDC3z622vXDTh/zs/gic51m + 8yyhTyuALCwhDfABJwt+lsD/QuL7Zx1ftgJYVKVFF44sADm9WO7aooEpAcIJhgA6YKHE+sHBwerBwcF2 + oVD4yV736mbtnfHvvLi2v/q73zyHWrWYUFvxGZhMVAKk0px91C1YxhPkQe4EMfP0et0kq00KC7JchpxI + 3imVSlhfW0O5UkEcxxiPRphwEK3VanFariFXBrqwFCib0bZtIRx0bKQA5HMQqcdRUp9PwkzRAeB46FEG + I2VmYblwJ0noYaW/NPtTn8Y4jhGFIbq9HgzDwO2XX8Z0MsVHH/5UFGalwcY0/kDW2V5jiv/ygYXO2Pjr + Dz5472czRjvsgYWTPRwXcHmdbAmcJPxyfgA9e8C84GeZ9qcJ+FdK4NPjy1IANBZdjLQikLeVkyMiJBzt + PhKFQJmGLQB7k8lk6cMPP3wK4PHu5ctrPz2MXq9a+2/+zusqXrq6jHw+j5xtM1OZx6zpAZRBQpEWLGEI + QIL6kxshI/9yGq2c6z4aDRkyLiUv0baFQhGWZcEwDGxsbIiCJpcDjaPxGGEYCuUwmYygqhr/DUPdqUeA + LLSypZBORJLDa4QvyKxHdIwkhHJURbai2L7YfxLDURRFmEyncBwHFy5cQLlSwZNPPxWYSRaYSccrl2hP + ZzP86IGPn+5X29s7u39er9enAI7AwOEZf8kmv6wE0sKfZfLLs75s2suxfGCx8APZz/RXWujl8WUrgPRY + dPHSloCcP0ChRNLYPth5EGg4ArMKjgDUt7e3l7e3t7eq1epfNUZXrxffHn/npY392u9+YxUryxURRkQc + A5KLoPCU2TiaR+4BCGEgvj+5oAVIcgHkCjk5G47tm8/Eriuy4QDg8HBflCcbJiPkzOcL0HUdFy9eFMqB + cejN4DgOgiBAu91GEAQYj4ZQeTzfNK05wZazBeUwpmwlED5BQkl4gtyRmM6fAacKI27l2Zej8Zg1/Tx3 + DleuXkWr0cCjw8NjRTxy1ASAsEAIz9jeH+H/ed9Gb2L9f/fv/6TuOE6X39MumBvo4Dh676fen0X40+m6 + ccb7r3Qo74uMrxQggePHk+Z9o1dWboGGJKdAByP7sgAUwPIKlgGc03V9/erVqxsry0vfqFjDO9//jQKu + X0xKlEnAiqUS8tUrcMe7OKjv4fDoCKPRiM+WTJBlSyCKIvieJ/IKFo0oChl1GTezSXnIHXE00SMv+Z3c + 4RgA7FwOtp1DLscwjpWVFVimiVK5DM91MZ3NMJvNhHKgluhEZUamPGvdnkRNiBKc1snmuKqyngrFUgmV + apWn8+rod3sYj0bo9fuolMu4sLmJbqcjlJIcfiSMQnaPCPjzPA/jyQR/dd/Dg3q5vbOz+2e7u7tjsCSx + NpjLNwUTbhnBly0AGbGXLYKsDL70rJ8O3WV9xgmff+nGV00BpIesAOR1JBoq5pWCJr0oycgAUwZ5MEVQ + BXAOwNra2trqxYsXX8qb0XfvXZnkvnVvBeeWKygVi6jUaijX1uBMejg6OECz1UK/3+cP9Hx4lwCrIPAR + R7GwHhg4OP+MkHnu8O45ct6AzPUvN8dMavY1sY1MPRZF86xAOZvhBoViEblcHqurqyjk8ygUi/A8D85s + hj4vre12Oyw0NxwK5WDbNgzDRKFQQBiGc6xOuVwO5XIZ5UpFuBJk3l+9dg1hEGJvry5AyHSylABYU6b/ + bDbDzuEUf/qugcFU/cv79+9vu67bAgN7+2DhYBfJjJ8W/DQAmIXuk2mfnvEXFenI4yST/5d2fNUVAA05 + 9zvLIsiyBui9nGloALDBlEEVQA3Aai6XW7927drFSrn07bI5uPxH31vBvVvrOLe2BmfmoNtpo9lqYTwe + w3WdOUxAzjOQk3fkTL8E3GIEJET4GceRMPfTg/nB4VwNP1F/y00zZStBHlGEY3RgAJCzWTuzQrGISqWK + 5eVl5Gwb5UpFZPONxmP4vo92u838+clYlFKXy2WUy2WcO3dORDRWV9dQKBXw9MkW+v3+3DmnU7LZMSel + 1GEYotsb4AfvzfCotdLe2dn9093d3QGYuU+z/gxJFCht7meh+1HGUhb2RXH7LJNernI9C+PQL9X4ZVEA + WVaA/F62FCjFmNZpqZeBxEXIAyiCKYIVAKubm5sbGxsbr9h68Ntv3Y7xrXvnEHhj9HpdZlL7/pyZL1f2 + 0RDKIQxZ2yst6WtIZjX9hvx/2RWQablkS0AeUQSk/pbvJ4amzVsHpAjCMEYoKQ5VRdL+O45RyKmwLBuV + ag35fB5ra2uwTBNLKyuY8ZbniqLg/OY1hP4Ys+kM1fU7ONx6B/v7+8IVIgGnEmj5GhFgSqnMT/dH+I/v + 5jH11L/84IMPPvU8rwFm8vfBzH2K+sj+fhrpl838rIKdRbM9cHz2l7ktIK3/lRy/LAqAxkmKgD6f5B7I + S9kqyIEpgzK4MiiXyxtXrly5apnGqq6F66bi3rF1Dzndw5W1ELW8C12fF0xDVwRhhpw9R0qABEhWCECC + C7iuc0zYyby3zITZB0gYfGgbAPB9AiEVBGHMTl7aVxxBKIDThqay/YRhDEVlJcG1pWV8/etv4sXv/W9Q + tDxG2/8OR1tvo9vtYjqdzvVekAFG+kyKwPM89PpD/MVPAtQH5w536/X/vLOzQ5WhXTAgl3z9rHTd08J5 + WWZ+FrCXHr+SZv5J45dNAaRHVvERfT6reyCDhyaYMigCqIC5CVUw7KBUqVTsUqmUKxaLhWKxuGLoWNMQ + XNDgnK/YLgqmj/VagGqeCYJpJGAekWvousFDjPM4AbUMC6RmGgCE60CDlAApCrIE6D0N12Ozva4dVyg0 + Qv5bss5luIJhD2wbTU04CTc313H37l3e0ZhRhXW7zDqSqxlJ0ZF7RDjJdDrF0/0R/vP7FTi++hf379// + yHXdI7AQLs36Dk728dP+/UmC/2vr359l/LIrABqLLIMsRQDMKwHZMiCFQMAh4QUWX9I6euUA5HVdz9Vq + tXy1Wi3m8/mybdtLauyvG6p3R409lGwPK0UX67UAthExv4Sb6QoAk8/uup6Y/jIhp9yRGEgAPxLmIOSI + umTO65oyt00YxkwauEDTk84JexBGIhIKjV8lUgikIExDQc5WsLS8gnK5gitXrsAwDIyGGMlv3AAABnRJ + REFUQwx5hCQd26dMv+l0itF4iv/4Yx/N2bnDnZ3d/2t3d5fM/Q7YrE+JPbLwZ6X7ZoF7i/LygeNmP87w + +ddi/KLzAJ7VyMouTH+frkcgBUDZhnLasQN2bcY4biXQZ5O/7CAIzFarZbZarbRyKNRqtXyhUMgXi5Ui + sxrUdU0JX9AxPa/DRd7wsVYNsFz0oCoR98sBIEIU+zD1ed6+rKUOZc7XjyMgkC5BHEEIv3yRFG41EA5J + wk+KCUhcBlrKHZTTCVIyuAcw68VxHEwmE/z0cQd//ckavDD/5/fvv/sen/XbYAlcMyQIfzqZJ8vMT2fs + nQTqnSVW/2sp/MCvjgKQx2k3Wa49kDMLibGI3AUfx60E2Z2QLQfZepC5Dexer2f2ej0LCdeBDaCg63q+ + Vqvla7VaaXeYr1qWtayr0Xkldu9o8JDTZyjZATZqAXJGAOrqJc/QlJBHFkUUAVCZzy4LPc3sZPLHGp/V + +Xey6S91aU8UDf/vMEw2lE37SAr1CfajIMBkMsFoNMGf/CjCOL58uLO38x/q9foemK/fB0vooZTeNKov + J3plgXq0PIuZ/3y2XzB+FRWAPOhBkMOItJ7cAtlMTLsMssAveqXBRgXJdTWQuBtyToJsNViQ3AkAuVqt + ViyVSqVCoVx82i+smqa5rsbuphI759WIuRTVnIOlYgBTi7kiSMBBXWOzOvnxijKPAyjKvHmva0AUJ4qC + vhPbqAA3QgSan84ilOv3XdfFaDTEwycj/M2TdbiB8qcPHrz7d67r7uN4aE+O64fScpGPn/bv5fucdY+f + jxPGr7oCoJH1YMSYf0iylmQdyJ/T26S3l9dnKQdZIchAJGUumr1ez+JWA4GSNoC8YRiFWq1WqlartXy+ + VrX61oqmYl2N/Vc0TKErLvK6j+VygKrtMhxA4Q1FJAsiCOcFHeBWhbSNuFgxazASRYRbJKSeBPTJJv9k + MkGvP8Sf/EiFp18+2N3f/d93d3efIEnokUG+RQw8WaW4Wdl6sgKQlzjj51/78euoAORZP40LyNuksYR0 + Qggyts9SIul1aYsiHaGQqdJkl8L0fd9qNptWs9kknIGsh0KtVquUSqVKsVguH87yq6Zpntfgb8aRe16L + XViqA0t3cb4WwtICaBzVl4/M0BV2UJIiIMURRexo5Jp/GpTJNxj08aP3+/i4/QLcAP/nh+//3Y8cx9kH + A/nkTL5FyTun1eAv8u0XjecCf8r4dVEA8jjpoVCkbdIm5KKQY/q705RD+ru01XCacqAluRcmAKvX69kS + 1kCWQ8EwjPzS0lK1Wq0u5XK1paZnrei6dl6N3FeUaAYlZuFLS3OwXg2gKjEDArnlAAVQPOZauH4MIICd + c1AosBbwYRBgMBjgydY2/tNPSvD1a/W9g71/s729/RBs1pfN/ZPQfBnQy4rhAziG5j8H9b7geO4jzY9F + 4cSz/maRAshaLvpNWpnIlkN6Kec1kLVA60xIYCQSF4M+55eWlmrlcrlSKBSW8vn8mmEYL2hqtIHQ3VBj + B2o8Q073sVL2UbJ8qApPCCorqFRK2Ny8CE3T8O/+08f4pHMhdnz8yYMHD/7Mdd09JCBfGs1fFMaT0Xzg + uJ8PLDb1F617Pk4ZzxXAyeOzKoBF67IE/KTfpF2H0xSErCRkBSG7FHI6tAhhQsIewKyGnGEYxeXl5aVq + tbqcy+WWTdNc1XX9vBq7ryByFSV2Yaku1qs+6r1i7Gvn3n3y5Mn/cXR09BgsoWeM+ay9dE5+On4vx/Gf + o/lf4niuAD7/OOnanfW6nlVRnOaKUJQjS3GkFQS5FFnWA2VDknKwkBRQ2QDyy8vLy6VSqVooFKpBEDj1 + en1rOp3ugQn+AEmdvhy6S/v0MqKfNcvLgv9c6H+O47kC+PxDxgvOuu1Z15+03RfBIuTv0tukoxRyWTVh + DjLGoIOd+xRsxid2nqwY/Vny8s8q6M8VwDMczxXAlzMWXWf5Yc4q7JWjFWfZ91kwjJNASfpeCgrOLQlj + kL+Xkfss811+ZcXt0zN9OvqCjM/PxzMazxXAV2d8lnvxee/baYDkou9Oe3+Sn55WDLQuPZ4L/S9gPFcA + v3zj8+ILZ932JOVwln1nCfJnQe+fC/6XOJ4rgF/d8awtirPu77Mm6DwX+F/g+DJbgz0fX73xZUwAz2P2 + X+Hx3AJ4Ps4yvuhz8lzgv6JjAaXk8/F8PB+/DuP/B+iMUO+tz9oHAAAAAElFTkSuQmCCKAAAADAAAABg + AAAAAQAgAAAAAACAJQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL3GPPSxtjI0l + ZYTJHFRv7RJEWvsOQFb+FUpj2BUzQiMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvcZBCLW+OkCdmhcUdV3LpFE1o/RBKY/8R + Um7/Eld1/xNce/8UZIT/FWWG/xRggP8mfJ3/L4Sl/w9JYvkYR1+UCRYdGgAAAAoAAAAFAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApaYm2Fkxm+hBOaP8SVXL/E118/xVpiv8W + bY//Fm2P/xZtj/8WbY//Fm2P/xZtj/8WbY//Fm2P/xZtj/86j7D/QZa3/yF3mP8fdpf/DDdL5RhFWmkA + AAAeAAAADwAAAAcAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvcY8tIV982w9EXP4mfJ3/l9fv/zWKq/8i + eJn/H3aX/x92l/8ieJn/LoOk/zWKq/9Blrf/Sp/A/1KnyP9Yrc7/Wq/P/2a62v9mutr/a8Df/0qfwP8p + f6D/KH6f/xBPaf8SP1bEBxIXMgAAACAAAAATAAAACAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALG2MjRdLZfYQTmj/PJGy/1KnyP9R + psf/idHs/1itzv9ar8//X7TU/2a62v9mutr/X7TU/1+01P9ar8//VqvM/1Ooyf9NosP/S6DB/0KXuP9h + ttb/b8Tj/1uw0P9bsND/Oo+w/yF3mP8UYID/Ch0mUQAAAD0AAAAwAAAAIQAAABQAAAAKAAAABAAAAAEA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACVjg8wSR1/+FWeJ/0qfwP9N + osP/U6jJ/1yx0f9yx+b/suHz/2K31/9Vqsv/Wq/P/1muz/9Vqsv/TqPE/0yhwv9Kn8D/Qpe4/zeMrf8w + hab/LIGi/x92l/85jq//WK3O/1muz/9httb/V6zN/0idvv8xhqf/ChwkVwAAAEsAAABEAAAANwAAACsA + AAAfAAAAFAAAAAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApaom0E1x6/zuQsf9M + ocL/T6TF/1arzP9mutr/bMHg/2/E4/98zOr/vub1/2K31/9Kn8D/PpO0/zKHqP8sgaL/JXuc/xZtj/8U + YoL/E118/xJaeP8SVnP/ElVy/xJaeP85jq//P5S1/yh+n/9DmLn/VarL/0Wau/8+k7T/ChwlVgAAAEoA + AABIAAAAQQAAADoAAAAxAAAAKAAAAB0AAAAFAAAAAgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAT + W3n/XLHR/1Gmx/9csdH/ar/e/27D4v95y+r/fMzq/3TJ6P9rwN//htDs/ziNrv8WbY//Fm2P/xVlhv8V + aYr/FWWG/xVniP8VZYb/FWmK/x92l/8le5z/KoCh/zSJqv9nu9v/UabH/yR6m/8bcZP/KoCh/0qfwP87 + kLH/ChwlVQAAAEkAAABHAAAAQQAAADoAAAA1AAAALwAAACkAAAAUAAAACwAAAAUAAAACAAAAAQAAAAAA + AAAAAAAAAAAAAABit9f/YbbW/2m+3f9vxOP/csfm/3PI5/9pvt3/YrfX/2C11f9ftNT/htDs/0CVtv8u + g6T/NImq/zSJqv84ja7/PpO0/0SZuv9Inb7/SJ2+/0idvv9MocL/UKXG/1Clxv9vxOP/YbbW/0mev/81 + iqv/FWWG/xZtj/8VZYb/Ch0mVAAAAEgAAABFAAAAQAAAADoAAAA0AAAALgAAACkAAAAgAAAAGAAAAA8A + AAAJAAAABQAAAAIAAAAAAAAAAAAAAACk3PH/htDs/2vA3/9nu9v/XLHR/1eszf9Wq8z/XbLS/2e72/9v + xOP/web1/1muz/9LoMH/VarL/1Spyv9PpMX/TKHC/0+kxf9Sp8j/TKHC/0qfwP9MocL/VKnK/1Wqy/9q + v97/YLXV/1Gmx/9Rpsf/PpO0/zCFpv8VZ4n/Ch0mUwAAAEgAAABDAAAAPAAAADYAAAAwAAAAKQAAACMA + AAAlAAAAHwAAABcAAAARAAAACwAAAAUAAAAAAAAAAAAAAACG0Oz/W7DQ/1KnyP9TqMn/VqvM/2C11f9n + u9v/ccbl/4DO6/+u3/L/zOv3/2a62v9Oo8T/UqfI/1Gmx/9Qpcb/RZq7/ziNrv8vhKX/KH6f/x92l/8b + cZP/Fm2P/xVlhv82i6z/Nous/zmOr/9Rpsf/R5y9/0GWt/88kbL/CyAqSwAAAD0AAAA3AAAAMAAAACcA + AAAgAAAAGQAAABQAAAAgAAAAHAAAABkAAAAUAAAADgAAAAcAAAAAAAAAAAAAAABbsND/UabH/1eszf9c + sdH/a8Df/3PI5/+Azuv/l9fv/67f8v+u3/L/rt/y/0ecvf8sgaL/LIGi/yV7nP8WbY//FWeI/xRig/8T + XHv/E1x7/xNcev8TXHv/FGKD/xZtj/9Vqsv/Moeo/xNdfP8ccpT/PJGy/0KXuP88krP/ECk1MQAAACEA + AAAdAAAAFgAAAA8AAAAMAAAABwAAAAUAAAATAAAAFAAAABQAAAASAAAADgAAAAoAAAAAAAAAAAAAAABR + psf/YrfX/2e72/9xxuX/dMno/3nL6v9zyOf/b8Tj/27D4v9mutr/fMzq/zmOr/8edJb/IXeY/yZ8nf8q + gKH/J32e/yl/oP8ug6T/MYan/zeMrf8/lLX/SZ6//1Clxv90yej/Wq/P/y2Co/8VZ4j/E1x7/yqAof8p + gKL/FjVDDAAAAAYAAAAGAAAAAgAAAAEAAAABAAAAAAAAAAAAAAAGAAAACAAAAAsAAAANAAAADAAAAAgA + AAAAAAAAAAAAAABit9f/bsPi/27D4v9sweD/Z7vb/2G21v9es9P/YLXV/2W52f9sweD/suHz/1Spyv9E + mbr/TKHC/0yhwv9MocL/T6TF/1itzv9XrM3/UabH/1Wqy/9Wq8z/XLHR/16z0/91yun/Z7vb/0yhwv9B + lrf/HHKU/xNeff8SVXL/Ey46EwAAAAgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAMA + AAAEAAAABgAAAAYAAAAAAAAAAAAAAABuw+L/YLXV/12y0v9Wq8z/Wq/P/1yx0f9mutr/bMHg/3zM6v+X + 1+//0u34/2K31/9Qpcb/WK3O/2C11f9es9P/V6zN/1itzv9XrM3/S6DB/0GWt/89krP/QZa3/zyRsv9S + p8j/UqfI/0yhwv9MocL/QZa3/z+Utf8tg6T/DycyNQAAACEAAAAXAAAADQAAAAYAAAADAAAAAQAAAAEA + AAAAAAAAAAAAAAAAAAABAAAAAQAAAAMAAAAAAAAAAAAAAABgtdX/V6zN/1qvz/9ludn/Z7vb/3LH5v95 + y+r/pNzx/7Lh8/++5vX/zuv3/1yx0f9Inb7/SJ2+/0OYuf89krP/MYan/x92l/8VZ4j/FGSE/xRig/8V + ZYb/FWeI/xVlhv88kbL/Moeo/xxylP85jq//Qpe4/0CVtv89krP/DyYyOwAAADAAAAAsAAAAIgAAABcA + AAARAAAADAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAABXrM3/YrfX/2q/3v9v + xOP/fMzq/5rY7/+k3PH/pNzx/5fX7/98zOr/k9Xu/z6TtP8ieJn/JXuc/xxylP8WbY//GW+R/xZtj/8V + aYr/FWeI/xlvkf8le5z/LoOk/zWKq/9mutr/Qpe4/xRkhP8TXn3/KH6f/0CVtv87kbP/EzA8HAAAACIA + AAApAAAAJwAAACMAAAAeAAAAGAAAABMAAAAFAAAABAAAAAIAAAADAAAAAwAAAAIAAAAAAAAAAAAAAABi + t9f/bsPi/3nL6v+T1e7/idHs/4bQ7P90yej/ar/e/2m+3f9qv97/ndrw/0SZuv8tgqP/Moeo/zKHqP82 + i6z/O5Cx/0GWt/9Embr/Sp/A/06jxP9Oo8T/Wa7P/2C11f98zOr/XbLS/ziNrv8Zb5H/Elh1/xVpiv8h + d5n/AAAAAAAAAAgAAAARAAAAFgAAABcAAAAYAAAAFgAAABQAAAAPAAAADAAAAAoAAAAIAAAABgAAAAQA + AAAAAAAAAAAAAABuw+L/ccbl/3HG5f9xxuX/ZbnZ/2a62v9nu9v/bMHg/3PI5/98zOr/0u34/1qvz/9M + ocL/W7DQ/1yx0f9Zrs//Wq/P/1eszf9Yrc7/Wa7P/1qvz/9bsND/X7TU/1yx0f90yej/YbbW/0abvP9A + lbb/LYKj/yJ4mf8WZof/AAAAAAAAAAAAAAACAAAABQAAAAYAAAAIAAAACAAAAAkAAAASAAAADwAAAAwA + AAAJAAAABwAAAAQAAAAAAAAAAAAAAABxxuX/YrfX/2K31/9nu9v/a8Df/27D4v90yej/l9fv/6Tc8f++ + 5vX/3PD5/2zB4P9Wq8z/W7DQ/1uw0P9bsND/VarL/0idvv89krP/MYan/y6DpP8yh6j/M4ip/yqAof9D + mLn/Qpe4/ziNrv9DmLn/QJW2/z+Utf81i6z/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAQAAAAEA + AAAIAAAACAAAAAYAAAAFAAAABAAAAAMAAAAAAAAAAAAAAABit9f/Zrra/2a62v9sweD/ecvq/5DT7f+k + 3PH/vub1/8Hm9f/B5vX/xej2/02iw/80iar/M4ip/yqAof8ccpT/GW+R/xZtj/8UZIT/E1x7/xNeff8V + Z4j/GW+R/y6DpP9Qpcb/LoOk/xNdfP8ccpT/PZKz/0CVtv89krP/AAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAABAAAAAQAAAAEAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAABmutr/b8Tj/3XK6f+J + 0ez/suHz/6Tc8f+d2vD/gM7r/3TJ6P9xxuX/idHs/ziNrv8bcZP/KoCh/zCFpv8/lLX/RZq7/1arzP9d + stL/dMno/4nR7P/F6Pb/zOv3/5rY7/++5vX/k9Xu/zKHqP8SVnP/Fm2P/zaLrP83ja//AAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABv + xOP/htDs/4nR7P+J0ez/ccbl/3HG5f9qv97/a8Df/3XK6f+04vP/5fX7/8Hm9f/M6/f/xej2/7jk9P+T + 1e7/fMzq/3HG5f9httb/Wq/P/1arzP85jq//Fm2P/zWKq/8xhqf/RZq7/8jp9v+d2vD/Moeo/xBPaf8U + YYH/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACG0Oz/a8Df/2q/3v9mutr/ab7d/4nR7P/B5vX/5fX7/9Lt+P9zyOf/YLXV/0+kxf8t + gqP/L4Sl/ziNrv82i6z/Moeo/y6DpP8nfZ7/JXuc/yV7nP8PR1//EE5o/xNcev8RUm7/EE5o/xVlhv9B + lrf/mtjv/5DT7f8edJb/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABrwN//Zrra/4nR7P/F6Pb/6Pb7/87r9/9zyOf/PpO0/yV7nP8i + eJn/SJ2+/3nL6v83jK3/Moeo/zWKq/84ja7/O5Cx/z2Ss/88kbL/O5Cx/zWKq/8NPlT/6Pb7/xVniP8R + Um7/EFBr/xJYdf8WbY//EEtl/0idv/9WrM3/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB5y+r/7/j8/6Tc8f9es9P/PZKz/yJ4mf8V + ZYb/E1x7/xVpiv8sgaL/QZa3/0OYuf9Jnr//Sp/A/0idvv9Inb7/TKHC/1KnyP9uw+L/SZ6//yqAof8S + VXL/2e/5/yd9nv8RUm7/E1t5/yp+ofkxf6HUL2+MDgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABTqMn/FmiJ/xZtj/8V + ZYb/G3GT/xFSbv+45PT/IniZ/yqAof8SVnP/J32e/yd9nv8vhKX/LYKj/y+Epf8tgqP/KoCh/yV7nP8i + eJn/MIWm/xVnif8TXHr/yOn2/y2Co/8SVXL/FGKD/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAC9xjz0ncpPhM4ms/hNcev+04vP/MIWm/zyRsv8QTmj/Elh1/xVniP8ccpT/KH+h/zCGqP87 + kbT9QZW49Eucv9RHj696AAAAAEqKpxUUYoL/uOT0/y2Co/8SWHX/FWWG/wAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVpiv+k3PH/Nous/1itzv8XbpD/SoqnFQAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEqKpxUUZIT/ndrw/y6DpP8TXHv/FWeI/wAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVpiv+d2vD/PJGy/2G21v8V + Zof/SoqnFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEqKpxUTXXz/idHs/y6DpP8U + YID/FWeJ/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZrjf+a + 2O//P5S1/2a62v8VZ4n/SoqnFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEqKpxUU + X3//c8jn/zCFpv8VZ4j/FWeJ/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAABZtj/+Q0+3/QZa3/2q/3v8VaYr/SoqnFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAEqKpxUUYoL/a8Df/zCFpv8WbY//FWiJ/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAABZtj/+J0ez/Q5i5/2zB4P8VaYr/SoqnFQAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAEqKpxUUZIT/YLXV/y6DpP8Zb5H/FmqM/wAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABZtj/+Azuv/RJm6/27D4v8VaYr/SoqnFQAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEqKpxUUZIT/Wa7P/y6DpP8ieJn/FWiJ/wAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVpiv9vxOP/RJm6/3LH5v8V + aYr/SoqnFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEqKpxUUZIT/UabH/yyBov8o + fp//FWmK/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABRig/9s + weD/Rpu8/3XK6f8VaYr/SoqnFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEqKpxUU + YoP/Sp/A/y2Co/80iar/FGSE/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAABRff/8ziKn/Q5i5/3nL6v8VaYr/SoqnFQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAEqKpxUUYoL/QZa3/yh+n/9LoMH/FGKC/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAABVmh/8VaYr/Nous/3zM6v8dc5X/SoqnFQAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAEqKpxUTXn3/Oo+w/xVniP9csdH/FGSE/wAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABVpiv8VZ4j/Fm2P/3XK6f9Albb/QomqfgAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADKGqvMUX3//JXuc/xVniP9TqMn/GGmK/gAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACN2mfcZb5H/EE5o/0idvv9V + qsv/JHud/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACd9nv8TXXz/EVJu/0OYuf82 + i6z/KXmb5gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADuFppsV + aYr/D0li/xNeff9ar8//Oo+w/ziIq9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMYeq/RRkhP8M + Ok//D0df/3TJ6P8VZYb/O4WmmwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAab5L+HHKU/ws1SP8qgKH/VarL/y+Epf8nep32QomoegAAAAAAAAAASoqnFUKJqHok + ep3+HHKU/w0+VP8GHCj/RZq7/0GWt/8ecJP5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9hqeTGW+R/xxylP8LNkn/IXeY/02iw/8ug6T/FWiJ/xZnif8Y + Z4n+FmiJ/xlvkf8bcZP/DkVd/wQTG/8TXHv/Z7vb/xRkhP89hqeTAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALX2g6CqAof9Gm7z/Elh1/xVlhv8o + fp//KH6f/yd9nv8ofp//JHqb/xtxk/8VaYr/D0hh/w0+VP9Vqsv/Fm2P/zN/osoAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACV4m/ck + epv/X7TU/2C11f9Embr/SZ6//0idvv9LoMH/UqfI/2a62v83jK3/EVFs/zqPsP8VaIn/MoChzAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAA1hKXOFmqM/zCFpv9ftNT/kNPt/77m9f/B5vX/SZ6//xJYdf8SV3X/Fm2P/xhqjP87 + haejAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACl7nu4jepz/GW+R/xRggP8SV3X/EVNv/xNdfP8a + bpH+NoGlwwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAP//8A///wAA//gAAP//AAD/wAAAH/8AAP4AAAAH/wAA+AAAAAD/AADgAAAAAP8AAIAAAAAA + HwAAgAAAAAAHAACAAAAAAAMAAIAAAAAAAwAAgAAAAAADAACAAAAAAAMAAIAAAAADAwAAgAAAAB8DAACA + AAAAAOMAAIAAAAAA8wAAgAAAAAADAACAAAAAgAMAAIAAAADAAwAAgAAAAPgDAACAAAAA/xMAAIAAAAD/ + /wAAgAAAAP//AACAAAAA//8AAIAAAAD//wAAgAAAA///AACAAAAf//8AAOAACB///wAA/A/4H///AAD8 + D/gf//8AAPwP+B///wAA/A/4H///AAD8D/gf//8AAPwP+B///wAA/A/4H///AAD8D/gf//8AAPwP+B// + /wAA/A/4H///AAD8D/gf//8AAPwP+B///wAA/AfwH///AAD+AYA///8AAP4AAD///wAA/wAAf///AAD/ + gAD///8AAP/AAf///wAA//AH////AAD///////8AACgAAAAgAAAAQAAAAAEAIAAAAAAAgBAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtbo2LKmuKqhxUb+wX + S2T2EkFX/AszRf8NQFf/CjFD/w5FXfEXPU91AAAACgAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC9xkEIVT2v8Elp4/yR6m/8q + gKH/PZKz/z2Ss/89krP/N4yt/x50lv9Wq8z/JHqb/xJVcv8SO060BQ8UPAAAAA8AAAAFAAAAAQAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACttjJ8ZVnT5EE5o/3PI5/9A + lbb/Nous/zuQsf87kLH/PZKz/z2Ss/89krP/O5Cx/3LH5v9Qpcb/KH6f/xVpiv8VXHneEiQtUgAAACYA + AAAUAAAACAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMHKQJiNlg9sUVnP+Fm2P/0+kxf9n + u9v/uOT0/1itzv9Qpcb/UqfI/1Clxv9Gm7z/P5S1/zaLrP8sgaL/X7TU/1Spyv9Wq8z/QJW2/yZ8nf8a + OEZ0AAAARQAAADgAAAAlAAAAFgAAAAkAAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAUUm3+OY6v/1uw0P9g + tdX/ab7d/2vA3/+45PT/RJm6/zmOr/8yh6j/Jnyd/xlvkf8VaYr/FWmK/xVpiv9csdH/TaLD/zKHqP9G + m7z/UKXG/xk2RHQAAABIAAAAQgAAADgAAAAsAAAAHQAAABEAAAAHAAAAAwAAAAEAAAAAAAAAABNcev9s + weD/ZbnZ/2W52f9gtdX/W7DQ/6Tc8f86j7D/LoOk/zOIqf82i6z/O5Cx/z2Ss/9Albb/R5y9/4bQ7P9q + v97/QJW2/yd9nv8mfJ3/GjhGcQAAAEcAAABBAAAAOQAAADAAAAAnAAAAHgAAABQAAAALAAAAAwAAAAEA + AAAAQJW2/1Wqy/9Qpcb/T6TF/1itzv9pvt3/xej2/1qvz/9Sp8j/UabH/0yhwv9HnL3/QZa3/zqPsP84 + ja7/ab7d/1KnyP9LoMH/Sp/A/y2Co/8cPEtsAAAAQAAAADgAAAAtAAAAIwAAABsAAAAZAAAAFgAAAA8A + AAAGAAAAAQAAAAAfdpf/R5y9/1eszf9pvt3/ecvq/5rY7//F6Pb/Qpe4/zeMrf8sgaL/JXuc/xxylP8Z + b5H/FmuN/xlvkf9gtdX/SJ2+/yF3mP82i6z/bsPi/zJcb0QAAAAhAAAAGgAAABIAAAALAAAACAAAAAoA + AAANAAAADAAAAAcAAAADAAAAACV7nP9httb/ab7d/2e72/9httb/X7TU/67f8v8+k7T/Moeo/zmOr/86 + j7D/PpO0/0GWt/9Gm7z/TaLD/4nR7P9rwN//OY6v/xlvkf8Zb5H/bpqvGwAAAAUAAAADAAAAAQAAAAAA + AAAAAAAAAQAAAAMAAAAEAAAABQAAAAQAAAAAQpe4/1Wqy/9Uqcr/VarL/2G21v9yx+b/yOn2/1uw0P9T + qMn/V6zN/1KnyP9PpMX/SJ2+/0CVtv8+k7T/bsPi/1arzP9Jnr//RJm6/yyBov84Y3Y7AAAAGwAAAA8A + AAAHAAAABAAAAAEAAAABAAAAAAAAAAEAAAACAAAAAgAAAAAdc5X/VKnK/2a62v95y+r/ndrw/67f8v/I + 6fb/Qpe4/zeMrf8yh6j/KoCh/yJ4mf8Zb5H/GW+R/yJ4mf9it9f/SZ6//x50lv8ug6T/bsPi/y1VaEwA + AAApAAAAJgAAABwAAAAUAAAADQAAAAgAAAAGAAAABAAAAAQAAAABAAAAACh+n/9sweD/dcrp/3XK6f9u + w+L/Z7vb/7Lh8/9Albb/NYqr/ziNrv87kLH/PZKz/0CVtv9HnL3/UKXG/5PV7v9mutr/Moeo/xVpiv8b + cZP/TnqOKQAAAA0AAAAWAAAAFgAAABUAAAASAAAADgAAAAoAAAAHAAAABAAAAAEAAAAARZq7/2C11f9h + ttb/YbbW/27D4v+J0ez/0u34/16z0/9XrM3/Wa7P/1Wqy/9Sp8j/Sp/A/0mev/9LoMH/c8jn/1muz/9E + mbr/PJGy/zKHqP+jzuITAAAAAAAAAAEAAAADAAAABAAAAAUAAAAFAAAABAAAAAIAAAABAAAAAAAAAAAj + eZr/XbLS/3HG5f+J0ez/uOT0/8Xo9v/M6/f/Qpe4/zeMrf8yh6j/KX+g/yJ4mf8bcZP/HHKU/y2Co/9T + qMn/Q5i5/xlvkf8pf6D/bsPi/6PO4hMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAcbHOByqAof91yun/idHs/4DO6/91yun/htDs/77m9f9httb/TaLD/1itzv9dstL/ar/e/27D4v9x + xuX/YLXV/12y0v9rwN//UKXG/xZtj/8ccpT/o87iEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAABxsc4HRJm6/27D4v+Azuv/suHz/77m9f+J0ez/gM7r/27D4v9gtdX/Z7vb/2a62v9a + r8//VKnK/yqAof8QTmj/EE5o/xJWc/84ja7/Zrra/2e72/+lz+IOAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAHCwzQdZrs//htDs/2a62v8qgKH/FWmK/xFRbP86j7D/SZ6//0abvP9I + nb7/S6DB/0qfwP9Gm7z/MIWm/xBLZf//////DkNb/xlvkf8XZof/HGqM+5PB1QUAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADmPsvkqgKH/M4ip/xJVcv/M6/f/Elh1/yR6m/83 + jK3/Qpe4/0qfwP9Sp8j/VKnK/ziNrv8le5z/EVJu/9zw+f8VZYb/AAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABLlriWJXuc/5DT7f8W + bY//RpK0plCWtmNVmLc5AAAAAAAAAAAAAAAAAAAAAC9vjAcSWHX/pNzx/xVpiv8AAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAa + cJL/ab7d/xxylP8vb4wHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL2+MBxJWc/9xxuX/GnCS/wAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAABtxk/9Rpsf/H3aX/y9vjAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvb4wHElZz/1qvz/8c + cpT/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAHHKU/z2Ss/8ieJn/L2+MBwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC9vjAcS + V3X/SJ2+/x1zlf8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbcZP/LIGi/yR6m/8vb4wHAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAL2+MBxJXdf84ja7/IXeY/1ubuA4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABRhgf8qgKH/LoOk/y9xkEIAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAvcZBCE1x6/zaLrP8Wa43/W5u4DgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFF17/xZtj/89krP/MHSUmAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAADB1k5sUZIT/Nous/xZqjP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaZYX9FWmK/0ecvf8k + cJLuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAImuM8BVlhv8whab/F2uN/wAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACpzldwT + XHv/IXeY/zCGqP8vdJWpAAAAAAAAAAAAAAAAAAAAAC90lakTXn3/EE5o/zSJqv8mc5XpAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAL3GQWRRig/8SVXL/Nous/yR7nf8sc5TOMXORSzFzkUssc5TOFWaH/w0+VP8Zb5H/GW+R/y9xkFkA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAALXqb2CJ4mf8fdpf/KoCh/xlvkf8TXXz/E118/xZtj/8SWHX/EVNv/y2Co/8t + eZnUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAK3iZ3CF3mP9Inb7/Wq/P/5DO5v9csdH/L4Sl/xNeff8V + Z4n/LXma1wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOIGioyF2mf41iqv/HHKU/xNce/8R + Um7/GmuN/Th/nocAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/4AH//wAAP/wAAA/gAAAB4AAAAGAAAAAgAAAAIAAAACA + AABggAAACIAAAACAAAAAgAACAYAAA/8AAAP/AAAD/wAAA/+AAD//4Dw///D8P//w/D//8Pw///D8H//w + /B//8Pw///D8P//weD//8AA///gAf//8AP///gH///////8oAAAAEAAAACAAAAABACAAAAAAAEAEAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAF4pv8BeKb/AXim/wF4pv8BeKb/AXim/wF4pv8BeKb/AXim/wF4pv8B + eKb/AXim/wF4pv8AAAAAAAAAAAAAAABLwOv/S8Dr/0m+6f9FuuX/RLnl/0O45P9e0/3/P7Tg/yygzf8s + oM3/LKDN/z+04P8BeKb/AAAAAAAAAAAAAAAAM6fU/zOn1P8kmcb/JJnG/yKXxP8glcL/NarW/xeMuf8D + eaf/A3mn/wN5p/8ckb7/AXim/wAAAAAAAAAAAAAAAEvA6/9LwOv/TcLt/0i96P9HvOf/Rrvm/2DV//8/ + tOD/LKDN/yygzf8soM3/P7Tg/wF4pv8AAAAAAAAAAAAAAAAzp9T/M6fU/yufzP8qnsv/KJzJ/yWax/85 + rtr/F4y5/wN5p/8Deaf/A3mn/xyRvv8BeKb/AAAAAAAAAAAAAAAAS8Dr/0vA6/9SxvH/TMHs/0q/6v9J + vun/ZNX//z+04P8soM3/LKDN/yygzf8/tOD/AXim/wAAAAAAAAAAAAAAADOn1P8zp9T/MaXS/zCk0f8s + oM3/Kp7L/zuw3P8XjLn/A3mn/wN5p/8Deaf/HJG+/wF4pv8AAAAAAAAAAAAAAABLwOv/S8Dr/1bL9f9R + xfD/TcLt/0q/6v9o1v//P7Tg/yygzf8soM3/LKDN/z+04P8BeKb/AAAAAAAAAAAAAAAAAXim/wF4pv9O + fZf/Tn2X/059l/8BeKb/AXim/wF4pv9SepL/Tn2X/0dzj/8BeKb/AXim/wAAAAAAAAAAAAAAAAAAAAAA + AAAAUXyX/2TI6P9Xd5D/AAAAAAAAAAAAAAAARXWT/zOr1/9Ee5v/AAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAFN9mP9lyej/VniR/wAAAAAAAAAAAAAAAEZ0kv8zrNn/Pnqb/wAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAABTfZj/Zcno/1Z4kf8AAAAAAAAAAAAAAABGdJL/M6zZ/z56m/8AAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAVoCY/2TI5v9dg5v/AAAAAAAAAAAAAAAASImp/zS04f9bfpj/AAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAFN9mNltx+P/bbHJ/3WZrv97l6r/WZOu/zSx3P8Qndj/U32Y2QAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB7l6poU32Y/2vD3/9zy+T/a8Ld/06w0v8Qndj/U32Y/3uXqmgA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHuXqmhZgZvZVniS/1N2j/9Ud5H/U32Y2XuXqmgA + AAAAAAAAAAAAAAAAAAAAAAAAAIADAACAAwAAgAMAAIADAACAAwAAgAMAAIADAACAAwAAgAMAAOOPAADj + jwAA448AAOOPAADgDwAA4A8AAPAfAAA= + + + \ No newline at end of file diff --git a/DotNetZip/Zip/Resources/WinFormsSelfExtractorStub.Designer.cs b/DotNetZip/Zip/Resources/WinFormsSelfExtractorStub.Designer.cs new file mode 100644 index 0000000..98e6de7 --- /dev/null +++ b/DotNetZip/Zip/Resources/WinFormsSelfExtractorStub.Designer.cs @@ -0,0 +1,287 @@ +namespace Ionic.Zip +{ + partial class WinFormsSelfExtractorStub + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(WinFormsSelfExtractorStub)); + this.btnExtract = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.txtExtractDirectory = new System.Windows.Forms.TextBox(); + this.lblExtractDir = new System.Windows.Forms.Label(); + this.btnDirBrowse = new System.Windows.Forms.Button(); + this.chk_OpenExplorer = new System.Windows.Forms.CheckBox(); + this.chk_ExeAfterUnpack = new System.Windows.Forms.CheckBox(); + this.txtPostUnpackCmdLine = new System.Windows.Forms.TextBox(); + this.chk_Remove = new System.Windows.Forms.CheckBox(); + this.lblComment = new System.Windows.Forms.Label(); + this.txtComment = new System.Windows.Forms.TextBox(); + this.btnContents = new System.Windows.Forms.Button(); + this.progressBar1 = new System.Windows.Forms.ProgressBar(); + this.progressBar2 = new System.Windows.Forms.ProgressBar(); + this.lblStatus = new System.Windows.Forms.Label(); + this.comboExistingFileAction = new System.Windows.Forms.ComboBox(); + this.label1 = new System.Windows.Forms.Label(); + this.SuspendLayout(); + // + // btnExtract + // + this.btnExtract.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.btnExtract.Location = new System.Drawing.Point(331, 268); + this.btnExtract.Name = "btnExtract"; + this.btnExtract.Size = new System.Drawing.Size(60, 23); + this.btnExtract.TabIndex = 0; + this.btnExtract.Text = "Extract"; + this.btnExtract.UseVisualStyleBackColor = true; + this.btnExtract.Click += new System.EventHandler(this.btnExtract_Click); + // + // btnCancel + // + this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.btnCancel.Location = new System.Drawing.Point(397, 268); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(60, 23); + this.btnCancel.TabIndex = 1; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click); + // + // txtExtractDirectory + // + this.txtExtractDirectory.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.txtExtractDirectory.Location = new System.Drawing.Point(8, 125); + this.txtExtractDirectory.Name = "txtExtractDirectory"; + this.txtExtractDirectory.Size = new System.Drawing.Size(417, 20); + this.txtExtractDirectory.TabIndex = 2; + // + // lblExtractDir + // + this.lblExtractDir.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.lblExtractDir.AutoSize = true; + this.lblExtractDir.Location = new System.Drawing.Point(5, 109); + this.lblExtractDir.Name = "lblExtractDir"; + this.lblExtractDir.Size = new System.Drawing.Size(100, 13); + this.lblExtractDir.TabIndex = 3; + this.lblExtractDir.Text = "Extract to Directory:"; + // + // btnDirBrowse + // + this.btnDirBrowse.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.btnDirBrowse.Location = new System.Drawing.Point(431, 122); + this.btnDirBrowse.Name = "btnDirBrowse"; + this.btnDirBrowse.Size = new System.Drawing.Size(25, 23); + this.btnDirBrowse.TabIndex = 4; + this.btnDirBrowse.Text = "..."; + this.btnDirBrowse.UseVisualStyleBackColor = true; + this.btnDirBrowse.Click += new System.EventHandler(this.btnDirBrowse_Click); + // + // chk_OpenExplorer + // + this.chk_OpenExplorer.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.chk_OpenExplorer.AutoSize = true; + this.chk_OpenExplorer.Checked = true; + this.chk_OpenExplorer.CheckState = System.Windows.Forms.CheckState.Checked; + this.chk_OpenExplorer.Location = new System.Drawing.Point(8, 151); + this.chk_OpenExplorer.Name = "chk_OpenExplorer"; + this.chk_OpenExplorer.Size = new System.Drawing.Size(152, 17); + this.chk_OpenExplorer.TabIndex = 13; + this.chk_OpenExplorer.Text = "Open Explorer after extract"; + this.chk_OpenExplorer.UseVisualStyleBackColor = true; + // + // chk_ExeAfterUnpack + // + this.chk_ExeAfterUnpack.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.chk_ExeAfterUnpack.AutoSize = true; + this.chk_ExeAfterUnpack.Checked = true; + this.chk_ExeAfterUnpack.CheckState = System.Windows.Forms.CheckState.Checked; + this.chk_ExeAfterUnpack.Location = new System.Drawing.Point(8, 192); + this.chk_ExeAfterUnpack.Name = "chk_ExeAfterUnpack"; + this.chk_ExeAfterUnpack.Size = new System.Drawing.Size(131, 17); + this.chk_ExeAfterUnpack.TabIndex = 15; + this.chk_ExeAfterUnpack.Text = "Execute after unpack:"; + this.chk_ExeAfterUnpack.UseVisualStyleBackColor = true; + this.chk_ExeAfterUnpack.CheckedChanged += new System.EventHandler(this.chk_ExeAfterUnpack_CheckedChanged); + // + // txtPostUnpackCmdLine + // + this.txtPostUnpackCmdLine.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.txtPostUnpackCmdLine.Location = new System.Drawing.Point(145, 190); + this.txtPostUnpackCmdLine.Name = "txtPostUnpackCmdLine"; + this.txtPostUnpackCmdLine.ReadOnly = true; + this.txtPostUnpackCmdLine.Size = new System.Drawing.Size(312, 20); + this.txtPostUnpackCmdLine.TabIndex = 16; + // + // chk_Remove + // + this.chk_Remove.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.chk_Remove.AutoSize = true; + this.chk_Remove.Location = new System.Drawing.Point(8, 212); + this.chk_Remove.Name = "chk_Remove"; + this.chk_Remove.Size = new System.Drawing.Size(271, 17); + this.chk_Remove.TabIndex = 17; + this.chk_Remove.Text = "Remove files after executing post-unpack command"; + this.chk_Remove.UseVisualStyleBackColor = true; + // + // lblComment + // + this.lblComment.AutoSize = true; + this.lblComment.Location = new System.Drawing.Point(5, 6); + this.lblComment.Name = "lblComment"; + this.lblComment.Size = new System.Drawing.Size(75, 13); + this.lblComment.TabIndex = 8; + this.lblComment.Text = "Zip Comment: "; + // + // txtComment + // + this.txtComment.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.txtComment.Location = new System.Drawing.Point(8, 22); + this.txtComment.Multiline = true; + this.txtComment.Name = "txtComment"; + this.txtComment.ReadOnly = true; + this.txtComment.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; + this.txtComment.Size = new System.Drawing.Size(448, 82); + this.txtComment.TabIndex = 9; + // + // btnContents + // + this.btnContents.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.btnContents.Location = new System.Drawing.Point(235, 268); + this.btnContents.Name = "btnContents"; + this.btnContents.Size = new System.Drawing.Size(90, 23); + this.btnContents.TabIndex = 20; + this.btnContents.Text = "Show Contents"; + this.btnContents.UseVisualStyleBackColor = true; + this.btnContents.Click += new System.EventHandler(this.btnContents_Click); + // + // progressBar1 + // + this.progressBar1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.progressBar1.Location = new System.Drawing.Point(8, 237); + this.progressBar1.Name = "progressBar1"; + this.progressBar1.Size = new System.Drawing.Size(449, 10); + this.progressBar1.TabIndex = 11; + this.progressBar1.TabStop = false; + // + // progressBar2 + // + this.progressBar2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.progressBar2.Location = new System.Drawing.Point(8, 252); + this.progressBar2.Name = "progressBar2"; + this.progressBar2.Size = new System.Drawing.Size(449, 11); + this.progressBar2.TabIndex = 12; + this.progressBar2.TabStop = false; + // + // lblStatus + // + this.lblStatus.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.lblStatus.AutoSize = true; + this.lblStatus.Location = new System.Drawing.Point(9, 273); + this.lblStatus.Name = "lblStatus"; + this.lblStatus.Size = new System.Drawing.Size(0, 13); + this.lblStatus.TabIndex = 13; + // + // comboExistingFileAction + // + this.comboExistingFileAction.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.comboExistingFileAction.FormattingEnabled = true; + this.comboExistingFileAction.Location = new System.Drawing.Point(116, 167); + this.comboExistingFileAction.Name = "comboExistingFileAction"; + this.comboExistingFileAction.Size = new System.Drawing.Size(104, 21); + this.comboExistingFileAction.TabIndex = 21; + // + // label1 + // + this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(6, 172); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(98, 13); + this.label1.TabIndex = 22; + this.label1.Text = "Existing File Action:"; + // + // WinFormsSelfExtractorStub + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(468, 300); + this.Controls.Add(this.label1); + this.Controls.Add(this.comboExistingFileAction); + this.Controls.Add(this.chk_Remove); + this.Controls.Add(this.chk_ExeAfterUnpack); + this.Controls.Add(this.txtPostUnpackCmdLine); + this.Controls.Add(this.lblStatus); + this.Controls.Add(this.progressBar2); + this.Controls.Add(this.progressBar1); + this.Controls.Add(this.btnContents); + this.Controls.Add(this.txtComment); + this.Controls.Add(this.lblComment); + this.Controls.Add(this.chk_OpenExplorer); + this.Controls.Add(this.btnDirBrowse); + this.Controls.Add(this.lblExtractDir); + this.Controls.Add(this.txtExtractDirectory); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.btnExtract); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.MaximizeBox = false; + this.MaximumSize = new System.Drawing.Size(1024, 400); + this.MinimizeBox = false; + this.MinimumSize = new System.Drawing.Size(484, 336); + this.Name = "WinFormsSelfExtractorStub"; + this.Text = "This text will be replaced at runtime"; + this.Shown += new System.EventHandler(this.Form_Shown); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button btnExtract; + private System.Windows.Forms.Button btnCancel; + private System.Windows.Forms.Button btnDirBrowse; + private System.Windows.Forms.Button btnContents; + private System.Windows.Forms.TextBox txtExtractDirectory; + private System.Windows.Forms.Label lblExtractDir; + private System.Windows.Forms.CheckBox chk_OpenExplorer; + private System.Windows.Forms.Label lblComment; + private System.Windows.Forms.TextBox txtComment; + private System.Windows.Forms.ProgressBar progressBar1; + private System.Windows.Forms.ProgressBar progressBar2; + private System.Windows.Forms.Label lblStatus; + private System.Windows.Forms.TextBox txtPostUnpackCmdLine; + private System.Windows.Forms.CheckBox chk_ExeAfterUnpack; + private System.Windows.Forms.CheckBox chk_Remove; + private System.Windows.Forms.ComboBox comboExistingFileAction; + private System.Windows.Forms.Label label1; + } +} \ No newline at end of file diff --git a/DotNetZip/Zip/Resources/WinFormsSelfExtractorStub.cs b/DotNetZip/Zip/Resources/WinFormsSelfExtractorStub.cs new file mode 100644 index 0000000..e67363a --- /dev/null +++ b/DotNetZip/Zip/Resources/WinFormsSelfExtractorStub.cs @@ -0,0 +1,1087 @@ +// WinFormsSelfExtractorStub.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2008, 2009 Dino Chiesa. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2011-July-30 15:48:47> +// +// ------------------------------------------------------------------ +// +// Implements the "stub" of a WinForms self-extracting Zip archive. This +// code is included in all GUI SFX files. It is included as a resource +// into the DotNetZip DLL, and then is compiled at runtime when a SFX is +// saved. This code runs when the SFX is run. +// +// ------------------------------------------------------------------ + +namespace Ionic.Zip +{ + // The using statements must be inside the namespace scope, because when the SFX is being + // generated, this module gets concatenated with other source code and then compiled. + + using System; + using System.Reflection; + using System.IO; + using System.Collections.Generic; + using System.Windows.Forms; + using System.Diagnostics; + using System.Threading; // ThreadPool, WaitCallback + using Ionic.Zip; + using Ionic.Zip.Forms; + + public partial class WinFormsSelfExtractorStub : Form + { + private const string DllResourceName = "Ionic.Zip.dll"; + private int entryCount; + private int Overwrite; + private bool Interactive; + private ManualResetEvent postUpackExeDone; + + delegate void ExtractEntryProgress(ExtractProgressEventArgs e); + + void _SetDefaultExtractLocation() + { + // Design Note: + + // What follows may look odd. The textbox is set to a particular value. + // Then the value is tested, and if the value begins with the first part + // of the string and ends with the last part, and if it does, then we + // change the value. When would that not get replaced? + // + + // Well, here's the thing. This module has to compile as it is, as a + // standalone sample. But then, inside DotNetZip, when generating an SFX, + // we do a text.Replace on @@EXTRACTLOCATION and insert a different value. + + // So the effect is, with a straight compile, the value gets + // SpecialFolder.Personal. If you replace @@EXTRACTLOCATION with + // something else, it stays and does not get replaced. + + this.txtExtractDirectory.Text = "@@EXTRACTLOCATION"; + + this.txtExtractDirectory.Text = ReplaceEnvVars(this.txtExtractDirectory.Text); + + + if (this.txtExtractDirectory.Text.StartsWith("@@") && + this.txtExtractDirectory.Text.EndsWith("EXTRACTLOCATION")) + { + this.txtExtractDirectory.Text = + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), + ZipName); + } + } + + + // workitem 8893 + private string ReplaceEnvVars(string v) + { + string s= v; + System.Collections.IDictionary envVars = Environment.GetEnvironmentVariables(); + foreach (System.Collections.DictionaryEntry de in envVars) + { + string t = "%" + de.Key + "%"; + s= s.Replace(t, de.Value as String); + } + + return s; + } + + + private bool SetInteractiveFlag() + { + bool result = false; + Boolean.TryParse("@@QUIET", out result); + Interactive = !result; + return Interactive; + } + + private int SetOverwriteBehavior() + { + Int32 result = 0; + Int32.TryParse("@@EXTRACT_EXISTING_FILE", out result); + Overwrite = result; + return result; + } + + + private bool PostUnpackCmdLineIsSet() + { + string s = txtPostUnpackCmdLine.Text; + bool result = !(s.StartsWith("@@") && s.EndsWith("POST_UNPACK_CMD_LINE")); + return result; + } + + + + void _SetPostUnpackCmdLine() + { + // See the design note in _SetDefaultExtractLocation() for + // an explanation of what is going on here. + + this.txtPostUnpackCmdLine.Text = "@@POST_UNPACK_CMD_LINE"; + + this.txtPostUnpackCmdLine.Text = ReplaceEnvVars(this.txtPostUnpackCmdLine.Text); + + if (this.txtPostUnpackCmdLine.Text.StartsWith("@@") && + this.txtPostUnpackCmdLine.Text.EndsWith("POST_UNPACK_CMD_LINE")) + { + // If there is nothing set for the CMD to execute after unpack, then + // disable all the UI associated to that bit. + txtPostUnpackCmdLine.Enabled = txtPostUnpackCmdLine.Visible = false; + chk_ExeAfterUnpack.Enabled = chk_ExeAfterUnpack.Visible = false; + // workitem 8925 + this.chk_Remove.Enabled = this.chk_Remove.Visible = false; + + // adjust the position of all the remaining UI + int delta = this.progressBar1.Location.Y - this.chk_ExeAfterUnpack.Location.Y ; + + this.MinimumSize = new System.Drawing.Size(this.MinimumSize.Width, this.MinimumSize.Height - (delta -4)); + + //MoveDown(this.chk_Overwrite, delta); + MoveDown(this.comboExistingFileAction, delta); + MoveDown(this.label1, delta); + MoveDown(this.chk_OpenExplorer, delta); + MoveDown(this.btnDirBrowse, delta); + MoveDown(this.txtExtractDirectory, delta); + MoveDown(this.lblExtractDir, delta); + + // finally, adjust the size of the form + this.Size = new System.Drawing.Size(this.Width, this.Height - (delta-4)); + + // Add the size to the txtComment, because it is anchored to the bottom. + // When we shrink the size of the form, the txtComment shrinks also. + // No need for that. + this.txtComment.Size = new System.Drawing.Size(this.txtComment.Width, + this.txtComment.Height + delta); + } + else + { + // workitem 8925 + // there is a comment line. Do we also want to remove files after executing it? + bool result = false; + Boolean.TryParse("@@REMOVE_AFTER_EXECUTE", out result); + this.chk_Remove.Checked = result; + } + + } + + + private void MoveDown (System.Windows.Forms.Control c, int delta) + { + c.Location = new System.Drawing.Point(c.Location.X, c.Location.Y + delta); + } + + private void FixTitle() + { + string foo = "@@SFX_EXE_WINDOW_TITLE"; + if (foo.StartsWith("@@") && foo.EndsWith("SFX_EXE_WINDOW_TITLE")) + { + this.Text = String.Format("DotNetZip v{0} Self-extractor (www.codeplex.com/DotNetZip)", + Ionic.Zip.ZipFile.LibraryVersion.ToString()); + } + else this.Text = foo; + } + + + private void HideComment() + { + int smallerHeight = this.MinimumSize.Height - (this.txtComment.Height+this.lblComment.Height+5); + + lblComment.Visible = false; + txtComment.Visible = false; + + this.MinimumSize = new System.Drawing.Size(this.MinimumSize.Width, smallerHeight); + this.MaximumSize = new System.Drawing.Size(this.MaximumSize.Width, this.MinimumSize.Height); + + this.Size = new System.Drawing.Size(this.Width, this.MinimumSize.Height); + } + + + private void InitExtractExistingFileList() + { + List _ExtractActionNames = new List(Enum.GetNames(typeof(Ionic.Zip.ExtractExistingFileAction))); + foreach (String name in _ExtractActionNames) + { + if (!name.StartsWith("Invoke")) + { + if (name.StartsWith("Throw")) + comboExistingFileAction.Items.Add("Stop"); + else + comboExistingFileAction.Items.Add(name); + } + } + + comboExistingFileAction.SelectedIndex = Overwrite; + } + + + public WinFormsSelfExtractorStub() + { + InitializeComponent(); + FixTitle(); + _setCancel = true; + entryCount= 0; + _SetDefaultExtractLocation(); + _SetPostUnpackCmdLine(); + SetInteractiveFlag(); + SetOverwriteBehavior(); + InitExtractExistingFileList(); + + try + { + if (!String.IsNullOrEmpty(zip.Comment)) + { + txtComment.Text = zip.Comment; + } + else + { + HideComment(); + } + } + catch (Exception e1) + { + // why would this ever fail? Not sure. + this.lblStatus.Text = "exception while resetting size: " + e1.ToString(); + + HideComment(); + } + } + + + + + static WinFormsSelfExtractorStub() + { + // This is important to resolve the Ionic.Zip.dll inside the extractor. + AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(Resolver); + } + + + static System.Reflection.Assembly Resolver(object sender, ResolveEventArgs args) + { + // super defensive + Assembly a1 = Assembly.GetExecutingAssembly(); + if (a1==null) + throw new Exception("GetExecutingAssembly returns null."); + + string[] tokens = args.Name.Split(','); + + String[] names = a1.GetManifestResourceNames(); + + if (names==null) + throw new Exception("GetManifestResourceNames returns null."); + + // workitem 7978 + Stream s = null; + foreach (string n in names) + { + string root = n.Substring(0,n.Length-4); + string ext = n.Substring(n.Length-3); + if (root.Equals(tokens[0]) && ext.ToLower().Equals("dll")) + { + s= a1.GetManifestResourceStream(n); + if (s!=null) break; + } + } + + if (s==null) + throw new Exception(String.Format("GetManifestResourceStream returns null. Available resources: [{0}]", + String.Join("|", names))); + + byte[] block = new byte[s.Length]; + + if (block==null) + throw new Exception(String.Format("Cannot allocated buffer of length({0}).", s.Length)); + + s.Read(block, 0, block.Length); + Assembly a2 = Assembly.Load(block); + if (a2==null) + throw new Exception("Assembly.Load(block) returns null"); + + return a2; + } + + + + + private void Form_Shown(object sender, EventArgs e) + { + if (!Interactive) + { + RemoveInteractiveComponents(); + KickoffExtract(); + } + } + + + private void RemoveInteractiveComponents() + { + if (this.btnContents.Visible) + { + txtPostUnpackCmdLine.Enabled = txtPostUnpackCmdLine.Visible = false; + chk_ExeAfterUnpack.Enabled = chk_ExeAfterUnpack.Visible = false; + chk_Remove.Enabled = chk_Remove.Visible = false; + comboExistingFileAction.Enabled = comboExistingFileAction.Visible = false; + label1.Enabled = label1.Visible = false; + chk_OpenExplorer.Checked = false; + chk_OpenExplorer.Enabled = chk_OpenExplorer.Visible = false; + btnDirBrowse.Enabled = btnDirBrowse.Visible = false; + btnContents.Enabled = btnContents.Visible = false; + btnExtract.Enabled = btnExtract.Visible = false; + + // adjust the position of all the remaining UI + int delta = this.progressBar1.Location.Y - this.chk_OpenExplorer.Location.Y ; + + this.MinimumSize = new System.Drawing.Size(this.MinimumSize.Width, this.MinimumSize.Height - (delta -4)); + MoveDown(this.txtExtractDirectory, delta); + MoveDown(this.lblExtractDir, delta); + + // finally, adjust the size of the form + this.Size = new System.Drawing.Size(this.Width, this.Height - (delta-4)); + + if (txtComment.Visible) + this.txtComment.Size = new System.Drawing.Size(this.txtComment.Width, + this.txtComment.Height + delta); + } + } + + + // workitem 8925 + private void chk_ExeAfterUnpack_CheckedChanged(object sender, EventArgs e) + { + this.chk_Remove.Enabled = (this.chk_ExeAfterUnpack.Checked); + } + + + private void btnDirBrowse_Click(object sender, EventArgs e) + { + Ionic.Utils.FolderBrowserDialogEx dlg1 = new Ionic.Utils.FolderBrowserDialogEx(); + dlg1.Description = "Select a folder for the extracted files:"; + dlg1.ShowNewFolderButton = true; + dlg1.ShowEditBox = true; + //dlg1.NewStyle = false; + if (Directory.Exists(txtExtractDirectory.Text)) + dlg1.SelectedPath = txtExtractDirectory.Text; + else + { + string d = txtExtractDirectory.Text; + while (d.Length > 2 && !Directory.Exists(d)) + { + d = Path.GetDirectoryName(d); + } + if (d.Length < 2) + dlg1.SelectedPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal); + else + dlg1.SelectedPath = d; + } + + dlg1.ShowFullPathInEditBox = true; + + //dlg1.RootFolder = System.Environment.SpecialFolder.MyComputer; + + // Show the FolderBrowserDialog. + DialogResult result = dlg1.ShowDialog(); + if (result == DialogResult.OK) + { + txtExtractDirectory.Text = dlg1.SelectedPath; + } + } + + + private void btnExtract_Click(object sender, EventArgs e) + { + KickoffExtract(); + } + + + private void KickoffExtract() + { + // disable most of the UI: + this.btnContents.Enabled = false; + this.btnExtract.Enabled = false; + this.chk_OpenExplorer.Enabled = false; + this.comboExistingFileAction.Enabled = false; + this.label1.Enabled = false; + this.chk_ExeAfterUnpack.Enabled = false; + this.chk_Remove.Enabled = false; // workitem 8925 + this.txtExtractDirectory.Enabled = false; + this.txtPostUnpackCmdLine.Enabled = false; + this.btnDirBrowse.Enabled = false; + this.btnExtract.Text = "Extracting..."; + + ThreadPool.QueueUserWorkItem(new WaitCallback(DoExtract), null); + + //System.Threading.Thread _workerThread = new System.Threading.Thread(this.DoExtract); + //_workerThread.Name = "Zip Extractor thread"; + //_workerThread.Start(null); + + this.Cursor = Cursors.WaitCursor; + } + + + + + private void DoExtract(Object obj) + { + List itemsExtracted = new List(); + string targetDirectory = txtExtractDirectory.Text; + global::Ionic.Zip.ExtractExistingFileAction WantOverwrite = (Ionic.Zip.ExtractExistingFileAction) Overwrite; + bool extractCancelled = false; + System.Collections.Generic.List didNotOverwrite = + new System.Collections.Generic.List(); + _setCancel = false; + string currentPassword = ""; + SetProgressBars(); + + try + { + // zip has already been set, when opening the exe. + + zip.ExtractProgress += ExtractProgress; + foreach (global::Ionic.Zip.ZipEntry entry in zip) + { + if (_setCancel) { extractCancelled = true; break; } + if (entry.Encryption == global::Ionic.Zip.EncryptionAlgorithm.None) + { + try + { + entry.Extract(targetDirectory, WantOverwrite); + entryCount++; + itemsExtracted.Add(entry.FileName); + } + catch (Exception ex1) + { + if (WantOverwrite != global::Ionic.Zip.ExtractExistingFileAction.OverwriteSilently + && ex1.Message.Contains("already exists.")) + { + // The file exists, but the user did not ask for overwrite. + didNotOverwrite.Add(" " + entry.FileName); + } + else if (WantOverwrite == global::Ionic.Zip.ExtractExistingFileAction.OverwriteSilently || + ex1.Message.Contains("already exists.")) + { + DialogResult result = MessageBox.Show(String.Format("Failed to extract entry {0} -- {1}", entry.FileName, ex1.Message), + String.Format("Error Extracting {0}", entry.FileName), MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1); + + if (result == DialogResult.Cancel) + { + _setCancel = true; + break; + } + } + } + } + else + { + bool done = false; + while (!done) + { + if (currentPassword == "") + { + string t = PromptForPassword(entry.FileName); + if (t == "") + { + done = true; // escape ExtractWithPassword loop + continue; + } + currentPassword = t; + } + + if (currentPassword == null) // cancel all + { + _setCancel = true; + currentPassword = ""; + break; + } + + try + { + entry.ExtractWithPassword(targetDirectory, WantOverwrite, currentPassword); + entryCount++; + itemsExtracted.Add(entry.FileName); + done= true; + } + catch (Exception ex2) + { + // Retry here in the case of bad password. + if (ex2 as Ionic.Zip.BadPasswordException != null) + { + currentPassword = ""; + continue; // loop around, ask for password again + } + else if (WantOverwrite != global::Ionic.Zip.ExtractExistingFileAction.OverwriteSilently + && ex2.Message.Contains("already exists.")) + { + // The file exists, but the user did not ask for overwrite. + didNotOverwrite.Add(" " + entry.FileName); + done = true; + } + else if (WantOverwrite == global::Ionic.Zip.ExtractExistingFileAction.OverwriteSilently + && !ex2.Message.Contains("already exists.")) + { + DialogResult result = MessageBox.Show(String.Format("Failed to extract the password-encrypted entry {0} -- {1}", entry.FileName, ex2.Message.ToString()), + String.Format("Error Extracting {0}", entry.FileName), MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1); + + done= true; + if (result == DialogResult.Cancel) + { + _setCancel = true; + break; + } + } + } // catch + } // while + } // else (encryption) + } // foreach + + } + catch (Exception) + { + MessageBox.Show("The self-extracting zip file is corrupted.", + "Error Extracting", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1); + Application.Exit(); + } + + + // optionally provide a status report + if (didNotOverwrite.Count > 0) + { + UnzipStatusReport f = new UnzipStatusReport(); + if (didNotOverwrite.Count == 1) + f.Header = "This file was not extracted because the target file already exists:"; + else + f.Header = String.Format("These {0} files were not extracted because the target files already exist:", + didNotOverwrite.Count); + f.Message = String.Join("\r\n", didNotOverwrite.ToArray()); + f.ShowDialog(); + } + + SetUiDone(); + + if (extractCancelled) return; + + + // optionally open explorer + if (chk_OpenExplorer.Checked) + { + string w = System.Environment.GetEnvironmentVariable("WINDIR"); + if (w == null) w = "c:\\windows"; + try + { + Process.Start(Path.Combine(w, "explorer.exe"), targetDirectory); + } + catch { } + } + + + // optionally execute a command + postUpackExeDone = new ManualResetEvent(false); + if (this.chk_ExeAfterUnpack.Checked && PostUnpackCmdLineIsSet()) + { + try + { + string[] cmd = SplitCommandLine(txtPostUnpackCmdLine.Text); + if (cmd != null && cmd.Length > 0) + { + object[] args = { cmd, + this.chk_Remove.Checked, + itemsExtracted, + targetDirectory + }; + ThreadPool.QueueUserWorkItem(new WaitCallback(StartPostUnpackProc), args); + } + else postUpackExeDone.Set(); + + // else, nothing. + } + catch { postUpackExeDone.Set(); } + } + else postUpackExeDone.Set(); + + // quit if this is non-interactive + if (!Interactive) + { + postUpackExeDone.WaitOne(); + Application.Exit(); + } + } + + + + + // workitem 8925 + private delegate void StatusProvider(string h, string m); + + private void ProvideStatus(string header, string message) + { + if (this.InvokeRequired) + { + this.Invoke(new StatusProvider(this.ProvideStatus), new object[] { header, message }); + } + else + { + UnzipStatusReport f = new UnzipStatusReport(); + f.Header= header; + f.Message= message; + f.ShowDialog(); + } + } + + + + // workitem 8925 + private void StartPostUnpackProc(object arg) + { + Object[] args = (object[]) arg; + String[] cmd = (String[]) args[0]; + bool removeAfter = (bool) args[1]; + + List itemsToRemove = (List) args[2]; + String basePath = (String) args[3] ; + + ProcessStartInfo startInfo = new ProcessStartInfo(cmd[0]); + startInfo.WorkingDirectory = basePath; + startInfo.CreateNoWindow = true; + if (cmd.Length > 1) + { + startInfo.Arguments = cmd[1]; + } + + try + { + // Process is IDisposable + using (Process p = Process.Start(startInfo)) + { + if (p!=null) + { + p.WaitForExit(); + if (p.ExitCode == 0) + { + if (removeAfter) + { + List failedToRemove = new List(); + foreach (string s in itemsToRemove) + { + string fullPath = Path.Combine(basePath,s); + try + { + if (File.Exists(fullPath)) + File.Delete(fullPath); + else if (Directory.Exists(fullPath)) + Directory.Delete(fullPath, true); + } + catch + { + failedToRemove.Add(s); + } + + if (failedToRemove.Count > 0) + { + string header = (failedToRemove.Count == 1) + ? "This file was not removed:" + : String.Format("These {0} files were not removed:", + failedToRemove.Count); + + string message = String.Join("\r\n", failedToRemove.ToArray()); + ProvideStatus(header, message); + } + } + } + } + else + { + ProvideStatus("Error Running Post-Unpack command", + String.Format("Post-extract command failed, exit code {0}", p.ExitCode)); + } + } + } + } + catch (Exception exc1) + { + ProvideStatus("Error Running Post-Unpack command", + String.Format("The post-extract command failed: {0}", exc1.Message)); + } + + postUpackExeDone.Set(); + } + + + + + private void SetUiDone() + { + if (this.btnExtract.InvokeRequired) + { + this.btnExtract.Invoke(new MethodInvoker(this.SetUiDone)); + } + else + { + this.lblStatus.Text = String.Format("Finished extracting {0} entries.", entryCount); + btnExtract.Text = "Extracted."; + btnExtract.Enabled = false; + btnCancel.Text = "Quit"; + _setCancel = true; + this.Cursor = Cursors.Default; + } + } + + + + private void ExtractProgress(object sender, ExtractProgressEventArgs e) + { + if (e.EventType == ZipProgressEventType.Extracting_EntryBytesWritten) + { + StepEntryProgress(e); + } + + else if (e.EventType == ZipProgressEventType.Extracting_AfterExtractEntry) + { + StepArchiveProgress(e); + } + if (_setCancel) + e.Cancel = true; + } + + + private void StepArchiveProgress(ExtractProgressEventArgs e) + { + if (this.progressBar1.InvokeRequired) + { + this.progressBar2.Invoke(new ExtractEntryProgress(this.StepArchiveProgress), new object[] { e }); + } + else + { + this.progressBar1.PerformStep(); + + // reset the progress bar for the entry: + this.progressBar2.Value = this.progressBar2.Maximum = 1; + this.lblStatus.Text = ""; + this.Update(); + } + } + + private void StepEntryProgress(ExtractProgressEventArgs e) + { + if (this.progressBar2.InvokeRequired) + { + this.progressBar2.Invoke(new ExtractEntryProgress(this.StepEntryProgress), new object[] { e }); + } + else + { + if (this.progressBar2.Maximum == 1) + { + // reset + Int64 max = e.TotalBytesToTransfer; + _progress2MaxFactor = 0; + while (max > System.Int32.MaxValue) + { + max /= 2; + _progress2MaxFactor++; + } + this.progressBar2.Maximum = (int)max; + this.lblStatus.Text = String.Format("Extracting {0}/{1}: {2} ...", + this.progressBar1.Value, zip.Entries.Count, e.CurrentEntry.FileName); + } + + int xferred = (int)(e.BytesTransferred >> _progress2MaxFactor); + + this.progressBar2.Value = (xferred >= this.progressBar2.Maximum) + ? this.progressBar2.Maximum + : xferred; + + this.Update(); + } + } + + private void SetProgressBars() + { + if (this.progressBar1.InvokeRequired) + { + this.progressBar1.Invoke(new MethodInvoker(this.SetProgressBars)); + } + else + { + this.progressBar1.Value = 0; + this.progressBar1.Maximum = zip.Entries.Count; + this.progressBar1.Minimum = 0; + this.progressBar1.Step = 1; + this.progressBar2.Value = 0; + this.progressBar2.Minimum = 0; + this.progressBar2.Maximum = 1; // will be set later, for each entry. + this.progressBar2.Step = 1; + } + } + + private String ZipName + { + get + { + return Path.GetFileNameWithoutExtension(System.Reflection.Assembly.GetExecutingAssembly().Location); + } + } + + private Stream ZipStream + { + get + { + if (_s != null) return _s; + Assembly a = Assembly.GetExecutingAssembly(); + + // workitem 7067 + _s= File.OpenRead(a.Location); + + return _s; + } + } + + private ZipFile zip + { + get + { + if (_zip == null) + _zip = global::Ionic.Zip.ZipFile.Read(ZipStream); + return _zip; + } + } + + private string PromptForPassword(string entryName) + { + PasswordDialog dlg1 = new PasswordDialog(); + dlg1.EntryName = entryName; + + // ask for password in a loop until user enters a proper one, + // or clicks skip or cancel. + bool done= false; + do { + dlg1.ShowDialog(); + done = (dlg1.Result != PasswordDialog.PasswordDialogResult.OK || + dlg1.Password != ""); + } while (!done); + + if (dlg1.Result == PasswordDialog.PasswordDialogResult.OK) + return dlg1.Password; + + else if (dlg1.Result == PasswordDialog.PasswordDialogResult.Skip) + return ""; + + // cancel + return null; + } + + private void btnCancel_Click(object sender, EventArgs e) + { + if (_setCancel == false) + _setCancel = true; + else + Application.Exit(); + } + + // workitem 6413 + private void btnContents_Click(object sender, EventArgs e) + { + ZipContentsDialog dlg1 = new ZipContentsDialog(); + dlg1.ZipFile = zip; + dlg1.ShowDialog(); + return; + } + + // workitem 8988 + private string[] SplitCommandLine(string cmdline) + { + // if the first char is NOT a double-quote, then just split the line + if (cmdline[0]!='"') + return cmdline.Split( new char[] {' '}, 2); + + // the first char is double-quote. Need to verify that there's another one. + int ix = cmdline.IndexOf('"', 1); + if (ix == -1) return null; // no double-quote - FAIL + + // if the double-quote is the last char, then just return an array of ONE string + if (ix+1 == cmdline.Length) return new string[] { cmdline.Substring(1,ix-1) }; + + if (cmdline[ix+1]!= ' ') return null; // no space following the double-quote - FAIL + + // there's definitely another double quote, followed by a space + string[] args = new string[2]; + args[0] = cmdline.Substring(1,ix-1); + while (cmdline[ix+1]==' ') ix++; // go to next non-space char + args[1] = cmdline.Substring(ix+1); + return args; + } + + + private int _progress2MaxFactor; + private bool _setCancel; + Stream _s; + global::Ionic.Zip.ZipFile _zip; + + } + + + + public class UnzipStatusReport : Form + { + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox tbMessage; + private System.Windows.Forms.Button btnOK; + + public UnzipStatusReport() + { + InitializeComponent(); + } + + + private void UnzipStatusReport_Load(object sender, EventArgs e) + { + this.Text = "DotNetZip: Unzip status report..."; + } + + + private void btnOK_Click(object sender, EventArgs e) + { + DialogResult = DialogResult.OK; + this.Close(); + } + + public string Message + { + set + { + this.tbMessage.Text = value; + this.tbMessage.Select(0,0); + } + get + { + return this.tbMessage.Text; + } + } + + public string Header + { + set + { + this.label1.Text = value; + } + get + { + return this.label1.Text; + } + } + + + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.label1 = new System.Windows.Forms.Label(); + this.tbMessage = new System.Windows.Forms.TextBox(); + this.btnOK = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 12); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(50, 13); + this.label1.TabIndex = 2; + this.label1.Text = "Status"; + // + // tbMessage + // + this.tbMessage.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right) + | System.Windows.Forms.AnchorStyles.Bottom))); + this.tbMessage.Location = new System.Drawing.Point(20, 31); + this.tbMessage.Name = "tbMessage"; + this.tbMessage.Multiline = true; + this.tbMessage.ScrollBars = ScrollBars.Vertical; + this.tbMessage.ReadOnly = true; + this.tbMessage.Size = new System.Drawing.Size(340, 110); + this.tbMessage.TabIndex = 10; + // + // btnOK + // + this.btnOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.btnOK.Location = new System.Drawing.Point(290, 156); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(82, 24); + this.btnOK.TabIndex = 20; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + this.btnOK.Click += new System.EventHandler(this.btnOK_Click); + // + // UnzipStatusReport + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(380, 190); + this.Controls.Add(this.label1); + this.Controls.Add(this.tbMessage); + this.Controls.Add(this.btnOK); + this.Name = "UnzipStatusReport"; + this.Text = "Not Unzipped"; + this.Load += new System.EventHandler(this.UnzipStatusReport_Load); + this.ResumeLayout(false); + this.PerformLayout(); + } + } + + + class WinFormsSelfExtractorStubProgram + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + // For Debugging +// if ( !AttachConsole(-1) ) // Attach to a parent process console +// AllocConsole(); // Alloc a new console + + + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new WinFormsSelfExtractorStub()); + } + + [System.Runtime.InteropServices.DllImport("kernel32.dll")] + private static extern bool AllocConsole(); + + [System.Runtime.InteropServices.DllImport("kernel32.dll")] + private static extern bool AttachConsole(int pid); + + + } +} diff --git a/DotNetZip/Zip/Resources/WinFormsSelfExtractorStub.resx b/DotNetZip/Zip/Resources/WinFormsSelfExtractorStub.resx new file mode 100644 index 0000000..e3fee67 --- /dev/null +++ b/DotNetZip/Zip/Resources/WinFormsSelfExtractorStub.resx @@ -0,0 +1,1119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + AAABAAoAMDAQAAEABABoBgAApgAAACAgEAABAAQA6AIAAA4HAAAQEBAAAQAEACgBAAD2CQAAMDAAAAEA + CACoDgAAHgsAACAgAAABAAgAqAgAAMYZAAAQEAAAAQAIAGgFAABuIgAAAAAAAAEAIABJhgAA1icAADAw + AAABACAAqCUAAB+uAAAgIAAAAQAgAKgQAADH0wAAEBAAAAEAIABoBAAAb+QAACgAAAAwAAAAYAAAAAEA + BAAAAAAAgAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAACAAAAAgIAAgAAAAIAAgACAgAAAgICAAMDA + wAAAAP8AAP8AAAD//wD/AAAA/wD/AP//AAD///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIuIAAAAAAAAAAAAAAAAAAAAAA + AAAAB4uLiLgAAAAAAAAAAAAAAAAAAAAAAAiLiHeIuIuIgAAAAAAAAAAAAAAAAAAIi4uIiHi4uLiLgAAA + AAAAAAAAAAAACIi4iL+4t4e4i4iIgAAAAAAAAAAAAAiIi4v7i4iLiHeLiLiIsAAAAAAAAAAACIi4v4iL + +Li/eIiLi4iIgAAAAAAAAAAACL+Ii4v4uIi4h3OIuLj4sAAAAAAAAAAACIuL+IuL+4v7iIi4i4j4gAAA + AAAAAAAACIiIuIiIiIiIt3iLiLj7gAAAAAAAAAAACLi/i4uLi4uLiIe4uLj4gAAAAAAAAAAACPiL+IiI + iL+IiHiIi4j7gAAAAAAAAAAACLiIuLi4v4uLh4O4uLj/gAAAAAAAAAAACPuIiIiIi/i/iIeIi4iLgAAA + AAAAAAAACIi/v7+4iL+Lh3i4uLi/gAAAAAAAAAAACL+IiIiLiIuIiIOIiIiL8AAAAAAAAAAAD4v7+4v4 + i/i4uHi4uLiAAAAAAAAAAAAACIiIiIiL+L+IiIeL+/uAAAAAAAAAAAAACIuL+/v4v4uLiIe4iIiwAAAA + AAAAAAAACIiPiIi/i/iIiHuIuLiAAAAAAAAAAAAACIv7+/iIiL+4h4V4iPvwAAAAAAAAAAAACIiIiL+/ + v4iLd397i/iwAAAAAAAAAAAACIv7+IiIiL+3R3hoiPuAAAAAAAAAAAAACIiIi/v7+IiHGHh4v4iAAAAA + AAAAAAAACIv7+PiIv7+4d3hoi/vwAAAAAAAAAAAACIiPi/v4iIiIiIgIv/iAAAAAAAAAAAAAD4v7+I+/ + v7+IiOhYiPuAAAAAAAAAAAAACIiPiL+I+Ii4iIgov/iAAAAAAAAAAAAACIv7+PiL+/iIiIhYiIuAAAAA + AAAAAAAAD4iPv7+Ij7+4+I8oiPiAAAAAAAAAAAAACIv4+Ii/iIj4j/h4v/uAAAAAAAAAAAAACIiL+/j7 + +/v4j/84+PiAAAAAAAAAAAAAD4v/j4v/j4+/j494v/vwAAAAAAAAAAAACIiIv4+L+/iIhnh4j/iwAAAA + AAAAAAAAD4v4+L+Pj7+/h3h4v/iAAAAAAAAAAAAACIiIv/i/v4+IiHeP//vwAAAAAAAAAAAAD4v4+L/4 + +Iv4+HeI+PiwAAAAAAAAAAAACIiL+Pi/v4+IiHh4i4jwAAAAAAAAAAAAD4v/i/j4+L+/iDeL/wAAAAAA + AAAAAAAACIiL/4v4v4+IgAAAAAAAAAAAAAAAAAAACIiPiI+P8AAAAAAAAAAAAAAAAAAAAAAAD4iIAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////////AAD///////8AAP// + /////wAA////////AAD///////8AAP///h///wAA///gD///AAD//gAB//8AAP/gAAH//wAA/gAAAf// + AADgAAAB//8AAIAAAAH//wAAgAAAAf//AACAAAAB//8AAIAAAAH//wAAgAAAAf//AACAAAAB//8AAIAA + AAH//wAAgAAAAf//AACAAAAB//8AAIAAAAH//wAAgAAAB///AACAAAAH//8AAIAAAAf//wAAgAAAB/// + AACAAAAH//8AAIAAAAf//wAAgAAAB///AACAAAAH//8AAIAAAAf//wAAgAAAB///AACAAAAH//8AAIAA + AAf//wAAgAAAB///AACAAAAH//8AAIAAAAf//wAAgAAAB///AACAAAAH//8AAIAAAAf//wAAgAAAB/// + AACAAAAH//8AAIAAAAf//wAAgAAAB///AACAAAA///8AAIAAH////wAAgAf/////AACD//////8AAP// + /////wAAKAAAACAAAABAAAAAAQAEAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAIAAAACA + gACAAAAAgACAAICAAACAgIAAwMDAAAAA/wAA/wAAAP//AP8AAAD/AP8A//8AAP///wAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAh4uIgAAAAAAAAAAAAAAI + i4eLi7gAAAAAAAAAAIiLi4iHeLh4gAAAAAAACIi4v4i4h7iLiIAAAAAACIuL+IuLi4h7iPuAAAAAAAiI + iLi/v4iHiLv4gAAAAAAIuL+IiIuLhziI+4AAAAAACPiLi4uIv3iLuPiAAAAAAAi4iIiIv4uHeIv4gAAA + AAAPi4uLi/uIh4uLiwAAAAAACIiIiIiIuIg4iIgAAAAAAAiL+/uLiIuHi4uAAAAAAAAIiIiIiIv4iHiL + gAAAAAAACIv7+/v4uIeL+LAAAAAAAAiIiIiIv4t4eLiAAAAAAAAIi/v7+Iv3eHv4sAAAAAAACIiIiL+I + h3d4iIAAAAAAAA+L+/iL+/iHeIuAAAAAAAAIiI+/iIiIiHv4sAAAAAAACIv4iL+/v/h4+IAAAAAAAA+I + iL/4iPj/ePuAAAAAAAAIi/j4v4v4iHv4sAAAAAAAD4iL+IiIi4Z4+IAAAAAAAAiL/7+L+I+FePuAAAAA + AAAPiIj4j7+/h4j4gAAAAAAACIv4v7///4eIi/AAAAAAAA+PiPj4iIj3ewAAAAAAAAAIi/iIiIAAAAAA + AAAAAAAAAP+AAAAAAAAAAAAAAAAAAP/////////////////8B///4AP//AAB/+AAAf+AAAH/gAAB/4AA + Af+AAAH/gAAB/4AAA/+AAAP/gAAH/4AAB/+AAAf/gAAH/4AAB/+AAAf/gAAH/4AAB/+AAAf/gAAH/4AA + B/+AAAf/gAAH/4AAB/+AAAf/gAA//4Af///H////KAAAABAAAAAgAAAAAQAEAAAAAACAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAIAAAIAAAACAgACAAAAAgACAAICAAACAgIAAwMDAAAAA/wAA/wAAAP//AP8A + AAD/AP8A//8AAP///wADMzMzMzMzAAO7u7t4s7swA7u7u4ezvDADu7u7eLO8MAO7u7uHs78wA7u7u3iz + vzADu7u7h7O/MAO7u7t4uzswA7u7u4e7swADu7u7eLswAAO7u7uHuzAAA7u7u3i7MAADu7u7d7swAAO7 + u7t3uzAAAzMzMzMzAAAAAAAAAAAAAIADAACAAQAAgAEAAIABAACAAQAAgAEAAIABAACAAQAAgAMAAIAH + AACABwAAgAcAAIAHAACABwAAgA8AAP//AAAoAAAAMAAAAGAAAAABAAgAAAAAAAAJAAAAAAAAAAAAAAAB + AAAAAQAAAAAAABssNQA5NTgAOzs+AEY+PwBJOzoAQEJGAEFESgBTUE0AXFBJAEJNVQBNTlIAaFdKAGNa + WQBoXlgARlliAExibQBecXkAWXJ/AH9qZAB/cmwAYnR+AHV0dgB8c3AAhGNZAIduYwCAeXMAjn54AH6C + fACGgH4ApIFvAP+hYwD/tX8AUXWGAFl9jwB6hYgAZI6dAFeNpQB8m6cAep+tAHmltwB2p7wAeKe7AF+n + xgBrp8QAaq7MAHKtxQB0rcQAdLDLAHqzzAB2u9UAfrnSAHK72QBtxd4AWs/sAFzQ7ABsxekAcMbhAHTJ + 5QB3z+oAcs/vAGLT7QBs1+8AdNHrAHjS7QBy2e8Acc3xAHXO8QB1zvQAeM7xAHfQ8QB31PAAdNf1AHzS + 8QB12/YAd973AHrd9QB84PcAfuD4AIGJjwCQh4MAkIiGAJ6UjACDj5QAhZKXAIyYngCSkpIAkZqeAKqR + gwCjlo4AsJWLALGcigC7n4gApZqUAKuckACgnpoAs56TALKflgC1npQApqCcAKuinQC2oJYAu6WbAIud + pQCInKgAi6CoAJehpQCZpKkAkqivAJWttwCNsbwAnbC3AKinpAChqq4AqqyrALGurwChr7QAq7e5AMCf + kgDBqpYAxKybAMuxnADStJ8AyK6iAMy2ogDOuKMAyLKqAM27qQDUt6AA1rykANO7qQDAurQAncS/ANrB + qwDHxL4A1MKzAN/ItgDXx7gA3Mq6AODHsgDgy7oAhK/BAIO6zACOu84AkbfGAJi1wQCfu8UAob7GAKG+ + yACbycYAisPaAJ/I1wCVxNgAmMnbAJvK3QCc0tMArcPLAK7IyQCpytUApsvcAKjO3ACxz9oAhcvgAIzO + 5gCEzusAhdfnAInY5ACE0OoAjtHoAJXQ4ACd1eEAldnjAJDV6wCU3O8Ags/wAIHS8gCF0vEAhtTyAIvV + 8gCD2vQAjdv1AI3e/ACS1vEAkNjzAJXY8gCQ3vAAld3wAJLY9ACV2fQAl9z1AJzc8wCR3/wAo9LkAKbd + 8gCi3fUApd71AKjf9QCM4vUAg+P4AIvm+QCO6PoAleP0AJrj9ACU4vwAm+D4AJPp+gCb7PsAuuTvAKHh + 9wCr4fUApOb5AKHu/ACu6voAs+P1ALzm9gC26PcAv+z1ALTs+gC76vgApPD9AKv0/gCy9f4AvPX9AMXC + wADWycMA0c7LAOrUxwDl2MwA6NrNAOTb1QDt3tQA8tzSAPPd2AD54dEA9eXcAMPu+gDa7vMAxfL8AM30 + /ADR9PwA1Pj9ANr4/gDs5+QA6u/pAPrn4ADz6+MA/OvmAPPu6QD/8eoA5Pr+AOn7/gD18/AA///5AP7+ + /gAAAAAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAuEWkwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAnLzg8SEpDuJoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AMGwSJFgVBI1SktDQTs6lpwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0bKvr0uzTjOYZnM/S0tD + PkGmpZoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADVuLCzs7PGxsZOTilYaC0/TkpDPYQfm6MAAAAAAAAA + AAAAAAAAAAAAAAAAANa+uLS0xcfHxsbGxsbGTpTfZB05TU1CN5Ugm6gAAAAAAAAAAAAAAAAAAAAAAADB + uMzHx8fHx8fHx8fGxsbGxi9vY59IS01DN6vzyqgAAAAAAAAAAAAAAAAAAAAAAAC+zsvLzc3Ix8fHx8bH + xsbGxm52VSE6TU1DNsn9yqgAAAAAAAAAAAAAAAAAAAAAAAC+zrXNyM3IyMfHx8fHx8fGxjGhYHI8xk1D + N8r9yagAAAAAAAAAAAAAAAAAAAAAAAC+zrXNzc3NzcjHx8fHx8bHxipWZzRAxk5DN8r9yqgAAAAAAAAA + AAAAAAAAAAAAAADBzrXNzc3Nzc3NyMfHx8fHx5DfZBY6xk5DN8r9yqgAAAAAAAAAAAAAAAAAAAAAAADB + zrXOzs3NzcjNzcjHx8fHxjCTXaBLxsZDPcr9yqwAAAAAAAAAAAAAAAAAAAAAAADC07XOzc7Nzc3NyM3N + yMfHx21gayVAx8ZFPcr9yqwAAAAAAAAAAAAAAAAAAAAAAADB07XOzs7Nzs3Nzc3IzcjHxzOgYFc7xsZF + PsnvyqwAAAAAAAAAAAAAAAAAAAAAAADR07/Ozs7Ozs7Nzc3NyM3Ix41waa6zx8ZFP0vFyawAAAAAAAAA + AAAAAAAAAAAAAADR07/Ozs7Ozs7Ozc3Nzc3NyJJ+ZBU5x8evs8rKus8AAAAAAAAAAAAAAAAAAAAAAADR + 27/TztvOzs7Ozs7Nzc3NzTSeX4+vxs2zr7O6AAAAAAAAAAAAAAAAAAAAAAAAAADR27/bztvOzs7Ozs7O + zs3NzZN7bCuvyMfHxbCsAAAAAAAAAAAAAAAAAAAAAAAAAADR28vT287bztPOzs7Nzs3NzZaXYE86yMfH + za+4AAAAAAAAAAAAAAAAAAAAAAAAAADV28vb29vO29POzs7Ozs7OyJZyaixDyMjN06+5AAAAAAAAAAAA + AAAAAAAAAAAAAADV28vb29vb09PT087Ozs7NxnVlWRkiy8jN2a+4AAAAAAAAAAAAAAAAAAAAAAAAAADV + 28zb29vb09PT087Ozs7OkBsXUOkTxc3N7a/JAAAAAAAAAAAAAAAAAAAAAAAAAADV3cvb29vb29vT09PT + zs5HHgULUYsMyc3O77DJAAAAAAAAAAAAAAAAAAAAAAAAAADV28vb3Nzb29vT09PTztOlGAExY4EJxc3T + 7rDJAAAAAAAAAAAAAAAAAAAAAAAAAADW3cvb3Nvc29vb29PT09OpdxojW4AJzc3T8LDJAAAAAAAAAAAA + AAAAAAAAAAAAAADV3cvb3Nzc3Nvb29vb09O5fYp9XHkEzc7T8bDKAAAAAAAAAAAAAAAAAAAAAAAAAADW + 3cvb3dzc3Nzb29vb29vOeX15d4ICzs7d8bLLAAAAAAAAAAAAAAAAAAAAAAAAAADW3cvc3Nzc3Nzc3Nvb + 29vNf4mDfYwDzs7d77LNAAAAAAAAAAAAAAAAAAAAAAAAAADW3czc3N3c3Nzc29zc29vNieOJieIGzs7d + 8bLNAAAAAAAAAAAAAAAAAAAAAAAAAADW3cvc3dzc3Nzc3Nzb3NvThubm5uYHzs7t+bjNAAAAAAAAAAAA + AAAAAAAAAAAAAADW3czc3d3c3dzc3Nzc3NvbnePz8vQKztPu+bjNAAAAAAAAAAAAAAAAAAAAAAAAAADW + 3cvd3d3c3dzd3Nzc3Nzbq4r7/PgP09Pw+bjOAAAAAAAAAAAAAAAAAAAAAAAAAADW3svd3d3d3N3c3Nzc + 3NzczYX1+/YQ09vu+rjOAAAAAAAAAAAAAAAAAAAAAAAAAADW3czc3d3d3dzd3dzc3Nzc3IAOCOgo29vw + +bjOAAAAAAAAAAAAAAAAAAAAAAAAAADY3c7d3d3d3d3d3N3d3Nzc3HlSGuBTztvx+bjOAAAAAAAAAAAA + AAAAAAAAAAAAAADW3czc3d3d3d3d3d3c3dzd23mDF11a2Pr9+b7OAAAAAAAAAAAAAAAAAAAAAAAAAADW + 3svd3d3d3d3d3dzd3dzd3erhDl5m7PHw3r7OAAAAAAAAAAAAAAAAAAAAAAAAAADW3czc3d3d3d3d3d3d + 3dze3t7lHHQn0NLQyszuAAAAAAAAAAAAAAAAAAAAAAAAAADW3c7d3d3d3d3d3d3d3d3d3dRxEiSO0dbu + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAADW3tPd3d3d3d3d3t3X19fX2u0AAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAADa3d3e3dje1t7W7e4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADW + 1trW7gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///////wAA////////AAD///////8AAP// + /////wAA////////AAD///4f//8AAP//4A///wAA//4AAf//AAD/4AAB//8AAP4AAAH//wAA4AAAAf// + AACAAAAB//8AAIAAAAH//wAAgAAAAf//AACAAAAB//8AAIAAAAH//wAAgAAAAf//AACAAAAB//8AAIAA + AAH//wAAgAAAAf//AACAAAAB//8AAIAAAAf//wAAgAAAB///AACAAAAH//8AAIAAAAf//wAAgAAAB/// + AACAAAAH//8AAIAAAAf//wAAgAAAB///AACAAAAH//8AAIAAAAf//wAAgAAAB///AACAAAAH//8AAIAA + AAf//wAAgAAAB///AACAAAAH//8AAIAAAAf//wAAgAAAB///AACAAAAH//8AAIAAAAf//wAAgAAAB/// + AACAAAAH//8AAIAAAAf//wAAgAAAP///AACAAB////8AAIAH/////wAAg///////AAD///////8AACgA + AAAgAAAAQAAAAAEACAAAAAAAAAQAAAAAAAAAAAAAAAEAAAABAAAAAAAAWEpEAHBPQQBxY14AaWpqAHpt + ZQBhaXAAYnB7AJNtWwCFdWwAnYF5AJeIfwC2jnIAa3yIAHF/igBpgY4AZYSWAHeIkQBmlqgAaJaoAGeZ + qwBrmqsAfpmnAHecqwBvoLEAc6S2AHujswB9q7sAarHNAH25zgBayt8AYc3fAE/N6ABnzegAZ87vAHbL + 5QBzz+oAec3tAGPU7ABl1OwAa9btAGzX7gBx1+4AeNDrAHHZ7wB22e8Acc3xAHbO8QB50PIAfdHyAHzV + 8gB81fQAdNj1AHfe9wB62fUAfdv3AHne9wB83/cAf935AH/g9wB+4PkAj4WCAIuPkQCSj5EAjZCSAI+W + mACRkJIAlZaWAJSXmQCUmZkAlpqcAKKJgQC4loYAtZqNAKOenAC9oIkAv6mIAJueoQCIo6sAn6CgAJOl + rgCkq64Ava6iALesqQDKq5UA1babAM2zpgDZu68A5r+yAOLHpADlx7MA68u0AOLNvQCAr8EAiL3PAIS+ + 0gChvMYAhMDVAIPB2ACRxdgAtsPHALjEyQClxtEApMfUAKfL1wCvydMAqc7bAL/L0QCt098AtdDZALLU + 3QCIzeYAhM/rAIfX6wCF1uwAiNbrAIzW6wCI0ewAhtjsAIXd7gCJ2u0AjNvuAI7c7gCW0eYAn9PiAJHU + 7gCf1+4AgdLxAITT8gCH1PMAitXzAI3W8gCF3/AAhtn1AITc9gCQ1/MAk97wAJLY9ACW2fQAk972AJXf + 9ACa3fUAndz0AKrb5QCg2e8ApNrvALHY4wC23egAod31AKbe9QCE4PEAgeD3AIHi+ACF4vgAheT5AIHh + /QCC5f4AiuD7AI7h+ACJ5fkAjeb5AIvg/ACN4fwAhOj+AI7o+gCR5/kAkOL8AJPk/ACV5PwAmeb9AJHo + +gCV6vsAnur6AJjs+wCa7PwAne78ALTh7QCm4fYApOX1AKrg9QCt4fYAouv7AKDv/ACl7PwArO37AKnu + /ACs7/wAseL2ALLm9wC15PYAueb3AL3m9wC27vcAuej3ALXq+QC27voAu+r5ALnt+gCi8P0ApfH9AKny + /gCu8P0AqvT+AK30/gCx8v0AtvH8ALH2/gC29P4AuvH8ALn0/wDd0coAw9HXAMLV2QDF1NoA4c7GAPbk + 3wDF2OAAxt/mANjh4wDE5vIAwej3AMHs+ADN7vgAyPT9AMD4/wDV9/0A0Pr/ANb6/gDb+/8A3Pz/APvv + 5gD/8eMA5Pr+AO/7/QDv/P8A+vPxAP//9wD1/P4A+v7/AP7//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///8AAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABtFxwlKyNpAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AH5wJUFKGjQiICJvAAAAAAAAAAAAAAAAAAAAANuQdX8xMTY2ZEMZNC4eTHJmAAAAAAAAAAAAAAAAkH2D + g4WFl5iYPDlRQ105Lh9ZcmYAAAAAAAAAAAAAvY6Lnp6YoJiYmJiYOWVDGDYuJu12aAAAAAAAAAAAAACO + taqgoKCgoJ6amJiXTUAdOS4m7XhqAAAAAAAAAAAAAJS1nqqgoKCgmqCYnphrQxQ8LinteGwAAAAAAAAA + AAAAlLWeqqqqoKCgoJqamEVAX5guLe14kgAAAAAAAAAAAACUuZ6qqqqqoKCgoKCa00USmDEt6XiTAAAA + AAAAAAAAALO5nq6uqq2tpKSgoKBDPmGYMSktegAAAAAAAAAAAAAAs7mer66uqq2kpKSgoNRFE54xNnqw + AAAAAAAAAAAAAACzuaavr6qvra2tpKSgQz5irpl/dQAAAAAAAAAAAAAAALPJpq+vr6+qra2tpK3YRRil + pTF4AAAAAAAAAAAAAAAAvcmmxq+vr6+tra2toGBHY6Wuf3UAAAAAAAAAAAAAAAC9yabGxsavr6+trapf + C0kRmLh/dgAAAAAAAAAAAAAAAL3JpsnGxsavr6+vrQIQUgUr0H92AAAAAAAAAAAAAAAAvcmuxsbGxsbG + r6+vCE9LBDbff3YAAAAAAAAAAAAAAAC+yanJycrGxsbGr69aVQwGOuGDdgAAAAAAAAAAAAAAAL7QrsnK + xsrGxsbGxtJbVAc86H+ZAAAAAAAAAAAAAAAAvtCmysrKycrGycbG2udcDZjtg3YAAAAAAAAAAAAAAAC/ + 0K7KysrKycrGysbZ7OYPmO2DlwAAAAAAAAAAAAAAAL/QrsnOysrKysrGyrBXUxaa7ot2AAAAAAAAAAAA + AAAAv9Guyc7OysrKysrGskgDDpruh5cAAAAAAAAAAAAAAADc0K7Ozs7OzsrKysqPVgEKfe6OlgAAAAAA + AAAAAAAAAL/Qqc7Ozs7Ozs7KzuvWCVht7ouWAAAAAAAAAAAAAAAAv9Guzs7Ozs7g4uTk5Nc9UGGzi9wA + AAAAAAAAAAAAAADc0MnOzs7O0dDJv7yz1E4bfAAAAAAAAAAAAAAAAAAAANzJztDQv9y83N0AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAANzd3gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/////////////////8 + B///4AP//AAB/+AAAf+AAAH/gAAB/4AAAf+AAAH/gAAB/4AAA/+AAAP/gAAH/4AAB/+AAAf/gAAH/4AA + B/+AAAf/gAAH/4AAB/+AAAf/gAAH/4AAB/+AAAf/gAAH/4AAB/+AAAf/gAA//4Af///H////KAAAABAA + AAAgAAAAAQAIAAAAAAAAAQAAAAAAAAAAAAAAAQAAAAEAAAAAAABuWFQAXmBnAIZmYQCZeHMA06VJAN25 + XQBmn8QAQKPKAESmzABJqc4ATqzRAFOv0wBZs9YAX7fZAGWixQBlpccAZKfIAHSrwAB8q8wAa7PQAGW6 + 3ABnvt4Aa77fAGy/4ABYxuUAVczqAFvP6wBvweEAcMLiAHbF5AB7yOcAY9LsAGvV7gB90usAdNnvAHXP + 9QB32/AAddzxAHjd8QB93vEAeeDyAH7g8gB94vQAg4WGAKKQgQC5lYcAqpyRAKOqpwCsrq4Ap7K1AKmy + tADKuasA78y7AIm1xwCjy98AgMvpAITO6wCE1egAh9DtAJPd8gCi0uYApt33AIDh8wCG4fIAgeL0AILl + 9QCG5PQAiOHzAIjl9ACH6PYAiej3AI3o9gCN6vgAkOf2AJDp9gCU6/cAkuv4AJHt+QCW7vkAmO74AJbw + +gCa8vsAnPH6AJ/0/AC75/cAven9AKDx+gCg9PwApPX8AKj2/ACn+P0Aqvj9AK35/QCx+v4AtPv/ALj8 + /wDu3dgA7uriAOzs7AD/+esA///+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wAACAgICAgICAgICAgHEwAAAAhLRT8o + KDEsNigIGhkTAAAKSz8/KCY+MwQmChsFBwAAC0tFQSkmMSw2JgsgBg8AAAxNRz9BKVUzBCgMIWMPAAAN + TU1GQUExLDZBDCZjDwAADlNNSUZCVjIEQQ4oYw8AAA5ZUE1HRjEsAj8ORT8RAAAYWVNRUUctJANJGDoU + NwAAHVpZU1FRNC8ESSIdAAAAAB1dWVNTUWU1BElLHgAAAAAfXl1YVFNkYQRQUB8AAAAAOF5dXVhYMAEE + U1k4AAAAADhgXl5eXWIuBFk8OAAAAAA7Ozs7Ozs9EhY7OwAAAAAAAAAAAAAAAAAAAAAAAAAAgAMAAIAB + AACAAQAAgAEAAIABAACAAQAAgAEAAIABAACAAQAAgAcAAIAHAACABwAAgAcAAIAHAACADwAA//8AAIlQ + TkcNChoKAAAADUlIRFIAAAEAAAABAAgGAAAAXHKoZgAAIABJREFUeJzsvXmsJdl93/c5td313bf3OvsM + Z4az0hyJFm2aYiTLmwItMYI4thArSBxEQGAgCUABARwgNgxE80eCAEkcw0gCZ4NlxHAEI7AVx5FMiqK4 + z8bhDGfpmeb09N79tntvbeec/FHLPVV16r73ut99I5n3h359azlrVX1/39/5nQ2WspSlLGVRcue1l3/m + zmsvP/pJl2MpS1lKU8QiE7/z2sv/vTfY+jUVT1DJRAKvAO8Cl/LfV4B3N1748u4iy7GUpSzFLgtVADtv + vKyDwAW3jwhWEd4QjUA7Hjoek05vo+IxWqU3yRTC6/nfu2SK4d1Flm8pS/lxl4UpgDuvvbzqeu7OyrkH + 0TIGfxt58A4q1eg0QckE4XTB7YM/RDsBAlAqQicRMp6g4nFhNbxNphTezv9e2Xjhy3JRZV/KUn5cxFtU + wlpnfwgH4XWgM8JNfLzBWhZAuOCuIg/eR8sDVJKgtcJJU5TW+H4Puudc4fovaSd4Sas4sxakRIZ73Hnt + 5UvMFMPrwJvA2xsvfPnmouq0lKX8qyYLUwAz0YDK/zQgyQwPAWgc30V0V/LSrKHTXZAxWoFKU3S6hwoj + lExwtEC4HbyVbfA6jyLEo0on6CRES4mKQ+689vIumVL4DjMF8e7GC19+c/F1XcpS/mjJAhWAthxr0Co/ + NlsfhTWvQKcIoRF+ByfoAKMsrHDRUqJljJYJKhmj0hiRRCipwOng9kfgBKsI5yWt05eUDEEptFTcfu1l + mDkhi+bE68Cbmy98ebqop7CUpfxhllOyALTlWm4RaDVTCloZ99I8WprfEwjhIHwP/AC3N8qKLxzQEp3G + qDRCpRE6naCTEJkm4HbB6+B1z4MQn9FafUbpCKRGpxIVTbj96suXmSmG0nLYfPHLVxb8cJaylE9UTkEB + 1MWmDOqXbJaCAJFZCODk0Yp7HsJ1cL0BLiPAzXwMQqOTEJXGaBmh0hCdhsgkAscDtwMrm2jHewihHkIl + P6OkREuFTmJuv/rylMy3ULEcNl/88usn+kiWspRPSBbnBMSE9mGdDUYTofInavcK34Gq3UvyKPEsLyEA + F+F6uG4XxACEB8IHLTM/g8ythiREJRNkmuBonSkGbwTeZg/BSzodvwROphjSlNuvvgyZYiiaEoXl8O7m + i8sxDUv5oyOLswBslv+RpQC6eak4rysG85jZsS4URWqc5+GFB8JD+AGu38cdeICfhVdJ3pwIUfEYJadI + maLSFMfrQGeA21tFC/UMKnxG4GYOyyRCxVNuv/ryNTKF8CbwBvAWmWK4dK9PYylLWZScQhOgzv7C8ms7 + NqOY1+r36+AX1evakp+OQSSzZoTKf4UP+AjPww3WcAfbQJA1PVSCTsPMWkjHqHiKUoo0GiO8Po7Xw+ms + gMM5CM85BF9QMkWlSdGckMArWusfCCHe1Zq3QL+39Zlf/3b7s1vKUhYri1cAdYI+bmRROz+21C2GlnzQ + oBMgyc6loYyED8JHuD6uv4ortkEEuZ8izf0MU1QyQaUTVAxptAOuj+N0oNfHH264muQlhHiJsoszLpoT + ZlPiTbLeibc3X/zynXuo8FKWcmRZcDfgPSO/xvpwcoMWzTLVrIVCWej8uCiDjnPlgKUp4SO8Dq43wu1t + Z9fQmdWQTlHJFBWPkckYGafIJEJrjXA6OP4g680Q6RNCOE9o7WTNjzhGJRNuv/ryHWZzJgqfw7ubL375 + 7RN6GEv5MZdTcgIaclgLAO4D/LZwtmuOcaxrYYqSO1ibFzr/FbnFoBOgGEaQV8bxybooA5zOCk53A88J + 8rgpqBgVT5DxGJkcoOKYNJmgkwThdRF+F7/TB4cN4TifA/E5naalw/L2q78BudORmfXwJvD65ou/Hh/x + YS1lKQt2At6XHJfxj6EkDg2aA7zNQihEm80LI46KQMSgJ1S0W96UQPg4wQCnu44v/DwtCTpBJgfoaEIa + H6CSMWk4QUVTnKCfNUG6a3jDNYRwXhLCe0mpNJ87ESLjMbde/Y3LzJoTxTyKt7de/PVrR3xAS/kxkk9g + HMA8lhbG3/2mWb9eNzOOKiboDYWgbb0PLcpB5HHLpkR2T6uirnn3pPBw/QEEa7giyNNJQKfoZGxYDWNU + PCaJpjhugHADHL+P1x/ieMFDQjgPKaV+TsVh3jsx4darv3FAPpEKY3LV1ou/vhwi/WMsn0AvgHldkJna + 99O+Pyb4G8EtzY3yUr15YErNxBG264UfoXAy5vdEcV5EkkCcXWIvD5aNY8gUQ4DwerjBCLe0GFLQRc/E + GJlMkOEB6WQfGYegBY7bQfgB/kof4ftDx3Ff0lK/pGSEiiNUMuVW1pwopmBXLIetF3/9oKXyS/lXRBbq + A5iJjX1Pwql3WBr1sQTHjV8TTa1p0NYlabnedsvortSN3gqZWwATNA7IIm8n64UQQdbT4J/BxYc1J1cM + MVpl4xLSeB8VjZHTCVE8RaUxrjfA8X2c3gh/uAWO+zw4z6dxiEojkukBKgm58s2//bHS6l0N78hUvSKV + ejsM03ef+7m//WGtwvfd4FvKJyML9AHoOZ/FosFva0YcMU9bt2PJyPMsgpbEtB35ujLa0TjX1MIXF4yh + 0SJTDFpPsktS5KGcrFdCdMDxcXrrdHpnQbiZIlEJWidZr0R0QBodEE+nRNMx8fQAJTokSoDjkKg+nW7/ + QhAEF4RKv+jJKb6K6fViPv7m344RvOE44vU0Ue/7gftDpfTb53/iP3uFpTL4IyV/SHwAJxnfsVw7iunf + Ito4sEYxugsb6c6xCCzJ28NY0ii7Kal1S8rMX6CnoIQRW2TNCNEBApzOOk5nG0cJdDghme5zcOs21z++ + xt1btwknd7h2S6JRjHdv0u93WVsdsL4+5OKFbdY2toON9eFnu777WaETVJLNsbj1ym+AZfEW4N2tz/z6 + ckzDH0I5RQVwGib/fYLfBLK2hakD/ZgWgajifAb+wxSFeV3NiqdrYSrntaKqFM2kdEWkqSKMUvbHKXf2 + uuxOzhBsPEHP2yPe22ezv8f7773LrbHAn6bc3pf0b+2wc/cuD5y9hr64Ra8b0B8M8btdvP4qndVzCMd9 + CnhKJtHPq2SKjCaoJOTWK79xjUwZvELmc3hl6zNLB+QnLacwDsBhZr7eixwFYDbg53HvRe9UaFm0FENb + rs9TCjpv45vJ19v8jQK0X2+1GCzh8ny01mgFaSoJo5jd/QnXr9/k+Z/4DxgMNyqpjSdTbt64zn/xN/8W + WsFdAOFy/a7CDRRrD2zTGW2gOpCKCJXEJNNxNociSfGCPk7g43YHBKMtHDc4B/w5GU//nIwnyHjCrVd+ + IwbeUEr9Xhgl/9dDn//Pf6el4ktZkJyOBVB6wmEGkkMjHTHx44J/DvtXilUH8iEWgWmW18JoW30bbF1v + 8zcC24t1WFgAVDZhSWmSJGUaRuztTbh6fZfd3S6D4UauHDTTMGR/PGFvf8yTjz9Ct9NFa41SEtf1iJVG + eg/y1puXuXN2n4sPXmR96wH6fQ/fF3iewkWSJlNUtIdKE6LJHioJkeEUN+ji+h5ud0iwshUg3M+i0s/6 + 452//qM/+Fs7H129/cYrb1z6b3/tb/zWb86r5VJORj6hgUB1J9299NG3AT9PpxX8FrO6AX5LtHnXK0na + nH3MmhdzmvZzC9EIbxvBWD8txhuAlJI4SZlMIsZTGMfbPPjEF3l27UEApmGETCVhHBFFMUmSDX0ejUYI + AXGc4Ps+QsDjn/4S1z/e4/0rH/PdV17hsUfe5Kknz7N19gyDwQrd7gCvs03Qf5Bs0lWaOSBVjEwmpOEB + Osl6G2QyRUYhXqfPaOvi2tMb21/wXPeP/W//1V/6T/fH0//41/7Gb32t/aEs5X5lgU0AbWe++xabh792 + /zjghwpYquHnZVUDdE2sl3WuELQt7SOAPz8pHXu2AGbzJTf9lVLEScrBQcje/hS3+yQbZ57k4gMPlHpp + b+8ApRVRnDANQ6ZRBMDGxgae53FwcEC328XzPHoDn7/4b/0McZTy1X/5Lb72e9/jX/7ed/jSF8/xzNNn + 2dpapdcPCHwP3+9mzkfhI5wOXmcFv/cQWsegIrRO0ESk033S6R7JeMKjjz868Dz303/wnbd/++/8rV/8 + c7/2N37r99ofzlLuR065F+B+HIGHAT8Pc1zwW9vtRyln0WVngrnWxjfTKxx9DeeizaY3rjW6Ccy6tLH+ + 7FhrTZpKJpOQuzv74D7GAw9+nuGwh9aaOE7xfY+9gwO0hiRNiaKYKMqmFAyHQ3q9Hkop+v0+vV6PO3f3 + +P5b73Nma51/7ed+ihc/8xS/9Y+3+N//wf/DF//kAV/4E+e5cGGD4UoPSHHdCaJ8f5liEk4X4QQgOghn + SDDcwh/EdNd2md69wsUHLwyf3Z/uff/tD/9L4Attb2Ep9yfz7OgTlHsFvqj9zQkn5jn8jgP+2m1rPPv1 + dqtHl973apnmtPcrt20Ox7qjb5ZPmYTWpKliEkbs7k0I4xUe+tSXSvArpSjGaxyMJ4wnE6bTkCiOieKk + TMN1XZTS5XGUSOIkYWd3n/cufYTjufzKr/4Sv/DLf56vfC3kd79ymysf3WJvd5/pNEImKVqlZPMd8inU + ah+Z3kEmV5HRh6ThD1HpbYS3Sn/r03RGZ3n8sQs94Nm/8zd/4ZdbXsBS7lM+gRWB6u19G7jrTsN5clRP + v8Gore3oIpzZt38E8FdcCKZjUNf8C4bToKFcTEC3t+0bCqbB+jNRShGnCeNxyGQScf6RX2Rl2EcphVIa + pRWJlLi+TxhmjJ8qiZKKVEqUkuzt7aGUZjzez1ZGUhq3v854EuK52dTn67fuMIqG/Mqv/gKTyZTf/Z1v + Mp7u8hf+TFY7R/ToBB6uqxFC5Iow/0B08SwEKt1FywOEO6K39jDh7Y/8rc2RunV77/PAP66/gaXcv5xC + N+D9mv1z7h05aRv4bekfYhFYxDpcACgnAzWweQjrzw1ni6atQbVWpFIxncbs749xu5/i7NmzpGmK1plT + MEklaZrS6QYk+fWi2eA6DlIq7ty5zWQy4eBgn8lkwmQywRtsMJ6GeJ6LQNDp+Nzd3cMRgn/vr/2bfO+7 + b/L2OxMG3Zif+1nwPAfXdXAdUX0OZbMo7yIVWd6ke4jeOYKVDR5/5Pzo5u295w9/GEu5FzmlJsBxpc3k + L0z947D+UcFvBDqi6W9v61O39WvpU0u/Dn6zjd9MugzbCv6Z6k2lJAwjkigE9wIASSKJ44QoSYjimGkU + Z12AGhyHDKiug5eb/Xt7e9y5c5vxeFwe7x8cMJmEjCchkzBkGkZEccKdnT0c1+GX/+LPoZTile8rPv7o + Ttb8mMakUmUAL7eNMhtNxfLwmqzbMsLtrjBa6XnAQy0PdCn3KYtTAFrPAcI8aWkOHAp6WzxmeDgU/PVb + LUBEUHy6jTS12e6Z3Wx/Ckdh/haT3zrXYnZB5V1/02kMWuP5PVzXJYxiwjhmGkaEUfarlcbzXDzPK/9c + 182shCQhDEOSJCmPp1HEeDJlPJmWiiCKEtJU8vH1m/zMn/48a+sraC34+rcS7t7ZYzyJiJPUeEYF8Ott + RZX7CmK8TtZ0AD1sfYRLuS9ZmAIoX6vZN14e19v4Ii9KXpyS5cXRnP9ljmZ62LA4P7FKu98O23LSTmXY + MDVlZ7bra+nUm/dzdaRhMRyJ9U2TWiFl1v0XpwH9QZ8kTTMFEMZMphGTMGI8CcFUAK6L73n4npv7ARRS + ytJpKGVmQRTxp2FcWgBxmjIJI5RWfO6Pv4DjCD684nLn5h77B1OSKMnSqoDfrIbKy57fcxwcxwFYm/eU + lnLvcgpNANE8beBfVM+PnO4RWP+44C+H51Yja8z2a83RNwtE/UQ3EG8LaxNDe+lc8TRYX1f/tKiUJ/Pc + Kw7GIZANAZ5GEQfTKdNpBv7JNEQBnuviuQ6e5+LkwJPSPoRbSk2cJFl3YRxnSiZJSJIUrTT7BxOee/4p + fC/AdQM+uCwZ70+YhjFp3ptQrYjpDwC0QmuJ43iI7L30DntaS7k3OWUfQAtgj51GhRLhUNafl5egOkqv + 6QSsspUwsjccfWU00YhRplIvttUEaLnWau4bZSjsk7z4jgNCOERJyN7uDq7rsn8wYTqdKYHJNEQpVYI+ + +8sSUMquAFKV9RJEUUKcpCRJSppm1kKajzg8c26TbnfIaGWDaze7jMcTwjglTVOa4DdPZ4rAMLJ8a0GW + ct+yQB8AtLvI4ejgF7W/InGos3Q7688Dv5mkqHx1VdY3kjMVRUPhmOA3EC/A3r3XYiEU93IHXTVM0+Sv + xtYIAY7r4Psurgsf/egDhHBQWnMwCQnDmPF0ysF4misAUbBtKamUsxQrloXMmxaZSS9V1tyQSpVNhdW1 + EaPRGmvr64TJiDRJieO8CVDqLhP8hRVgKrbsGTj3whNLOZIs0AdgDoox32DR1q/7AGzn9Tc/B/j1bOwX + qvEFxkcoyFb7MZOst+sNR58WTbxiwrLmRyjBf1TWh6YTtQaOoiiV2Lqsjus69Ho+vV6HaPcP+MY3vsVj + D18kTbMJQdNpVOkFyH6zSUFSKZIkxXEcXNfNaiQEjuOglCJJ06xHIZ2xv2kxuK7LysqAtbUhfjAiTiVS + KrQqCm0+m5amjQaEwHX+kHZW/Ssgp+wDuF9V3hLfuoz4PPAzAz9O7oPIGN3K+qXoyk89yYbJ37hoizhX + ixiKqM769jxNI8NzHbqdgNVRHyEUV975R7z6yqt89oWnmYRh3hWYjfiTMh8AlEoSKfPJQwlBENDpdOl0 + uvR6PfqDIUpp0iQlimMr+EW+5uFg0GUw6KCJstXMoAn2ipLTZL0Abc9rKSctpzQS8Miu/GOKzRdwSNhK + +90xWF8YYKunn1+v1Kd6aP1UtRnUFsLW1m+zekzEz9YWMO/VS+46Dt1uwOpKn63tEZcv3+TSD/5vVldX + eOHTT/C1b72OTLO9E5P8V2uNlBnDx3FMv9/H9zt88+tfwXEchOPw1E9sE4UhruuUTYDCeoBMAUip8DwP + x3WYjMc4wsERAuGIfE3Uet0L4Au00EsdcEqy0G7ATISFoe9DyrUFTHAepmAK1i9MeIEJfo1jgVDtvAX8 + VuO9uHiUapfE3gT/bN1As61vB/+saDPz2hGCwPMYDrtsb444c2YV1C5/8LXfZtDtsLG6klkAGtIkJckt + giiOy3UDhsMV1tfXuHH9Gjdv3ODWjetImTANp0RRRJIkmQVggL/b6/D1r76RNRPilF4vwQ88fD/v1msF + v1nn2dlSFienvDfgYW19S9u/0d9eHBzFqjBYH2asX14zWd9M0wb8emVqw1rnmgItUtbFeqNWjjoYDvEZ + 6OzReZ6g2/EZjXqcTVaZTCOuXfuQV179Dk898Wneu3wFgChJSvZXSpV+gsFgwGg0YmW4yd2dawRBlyic + kiqB5zoEgU+aKwHHcXA9l9/7nW/zza+9Ra874GB/n6cf26XfWyPwXdwK5ahKVXRFa1YVwVIWI6ewN6DR + 5q7/tuEfmlZDA4iHyeGsb28+GOVuAb82ew+sYW1FrJkEDfDP8tc14FdD2vwGNpO5YGTwfZfBoEOc9Nna + HLG/P+WdH3yLn/7CT/Ps008CEEUxKvfgK6mIkoTJNKTb7TEYDHng4efY2LzI5R+9STidomOJ4wgC3yPu + d0Fodu7s8tXf/joff3SDJIbhcMITD19mZdVjZdCh2/FxXSf3EdRZv/asSl/BEvyLlMVNBiotURvKzd/6 + sS2x4+RcUzgV1jcA3Mr6tnb4rBD3DX5r49ZULvNYvx6vKK+ZhG6GFdnknn6vw+qoz/pqn1u7Y3744TVW + V1cBiMouOk2Se/bHkylaZ9e2trYJV1bx+z2mB/soL8D3PCaBz3TQ57033+ODH17mYHeC0g5nNgXPf+oa + 3YHD9uaIlZUuQeDlFsBh4C+qpOevLr+U+5ZT3hloHtAtKJrrCRKWe8ICfMHxWL+9jDPwG/ErZRS1Ktby + mWvy11n8CO1f0zqopF1tQwsEruvg+w69btYtuLLi8qOPb3Lx4hkApmGYTRHOB/MkSYrnuUynIZ63x+07 + lzh/4Ul6w4dIkpAbu7tMJ2O6HZ8oDPng+5fY3x3jewGPXjzggYsxnX6Pc+fW2NocsjLs0ulkjkBKUFuU + la0XZqkBFiantCLQUU12w1JogL+dle3NhTYPf3HN3P23jfXNXGrgtw3qsYIfS13q6R/G+pYSlQxaT9sA + v5GQEJlT0M0HB3V7Iy6e3WBrdSUbzhvFpEoipS7H+2drAUYcHAh29+7gee/z5Kc/h+fCI67i22+8TRxF + 7N65y93bd/C9AK1TNjcnDEcrnD+/wfmza2ys9+n1PDzHbdbXLLMu7rXcX8qJywKbALNuoSowbE0AMyK1 + 5nIb8C29C5W19goj0jT3zfSMa0cCvwlSmxe+xTNfX/q7ErwGhaOAvz73QDSBXy9CHUCO0DjeCuujAUmS + sJ8k+YAgRZLIbDhvnCBzZ6AQcT6Sb8r+zsc889xnuXhxm42tDb7y9e+wlyiSJMERDpvrMWsbfS6cX+P8 + 2TU2N/r0ugG+67b4bbVRrSbwdaPHYCknKafUBDisi+6IHvRWpZA7+gzgH+7hPyLrwwxk2szb1vSoVWAO + +I/G+qYmrDWGNeg54G+a2Nm51ipvHQUMhz0+/PgmXU+XowGLPnqlFNNpSBzHKKWRScza+jpnLl5gbzrh + cxfO8fRTj3Pj1h2+/+oPUCollS5nz8CZrTXObq+ysd6n3+vgew7CqevrlrKWSm0J/NOQU+4GnCN1Im1Y + DTXANfKZRbQ7+cxM7sXkN/M9BPxl2Dob66bP0Nq9V38IulL0prlfDWMDv9YKTTZDUOYj/lYGPaIoAgmB + 76O0xvPKGXikSjGZTHAch3Cyz/bZ8/yHf+3fRTgu77x7ic2tTf7sn/5pvvW175CmKY7jsbUesDrqsbLS + yZlfIIpRllqXowTzqteeUV2BLpXAouWUegEMMb/tun9QWE9qESu5NK5XP/421reDf/a51U3+Iq36B1kL + V0nkEPCX7d359WlON85rWGf/er7NAqFVMc5fo6Qm8H3SOMVFMOh3gWwCUOB7xEE25z8MQ4QQfOqZF+h2 + u6yOVnAch16vy927u6yNhqRRgtYKKVOGwz79XkDH93E9B+Ham0Z28Bf1Lz+evBdgqQgWJZ/s5qBzwHjc + tJpba8/uVPOzixX82rh2GPjbcQfomtvgsO69Wv5HYP0yn1qaZRtaU87UU1LR7Q2QSuP6AVrHDAf9fMFO + TSIlcRTjuS7j8Rjf9/F9nx99+AEH04jRoMe5s9t879U36HU7nDm3yYeXrqC1ot9z8T0Xx82cjvU6zfSZ + tterogyWsmhZ8GzAXGxe+hN80U2Tty2/efFzU/5EwZ9lXIGs1eSvtX/MSxaQ6KKNXMvHWogiaD7KL05S + UuUxWt0iTlIc18V1XUYrK6ytrrA6WmFjdcTqaMjqaEiapvlftoDoD956B4DV0ZA3336X3f0x5y6cQ2uN + 40g838VxRT7gR9Osn1n22n1tezJLRbBIOX0LoEKFta6/Y1oD81m/MCXnxTfK0Qr8mmKpO6gsxF2cVLYF + O5T1a+VtAbdulNO4Vz9Xs7hKymy8f5wgGbC5vsV4GhF0AnZu7nBmew3P89AqW+2n0+ng+X7etp/xxN7+ + QeYrcF3SSJEmmjNnziCEQ8dXOPmUYYGYrbOimbX926yZenu/daWlpZykLHhBEKiC3AYqm9TiWCYT6Yay + qDsXjgL++2R9K/gL3pqBfz7rG/k1WL9qCugizGHgz8PMAJi1+5OczSWbrK2tsT+e4joOSjv8vf/uf+H/ + +F9/kySacnZ7k+3NddZHK8RxTJqmKJVNEQadLe4pBJ4b4LsdukGXb3zrt+l3pfGqVH5cKII21qcE+4zx + 6+BfaoBFyeL3BRAYu4NruxPQ9ls6wDWmr6D+yTcj2oBveJ7N8K2OPssH12by11jfZvJXxZK2uR6ekY4Z + p6lAWs5NwMyW3kHpzPMfhikJffq9Lrf3JgghOHP+DJ/9qZ/kf/67f5ev/O5XcB3NI088xLPPPkcqFTdu + 3GYwXGE4GnLh4gNEcUo38On3VlBKE/T6JElEx8+W9Z55/cmOj8D6deul9Vkt5URl8ZOBtJjh87jmfglK + 47QUYSHqOvirgC6thkb3nhnWEt/W+1c5L9K3qydLhOalNpAcAfzVYuRl0dW0sq3BJUmakKoeK8Med/an + CJEtBfbSH/8sSfJX+R//7v+AVnDl0mXefu33mU5jhOOh4j0+99IvMhgMUCpLs+N7HOxP2N7axnU9giBB + OI7h4S+U93xWb+52lL9H0VSoSzlZWfyCIEcdB2CLa5wCTZ1RN4V1PVARQBi3ToH1GwkdBvxZOmaA+cA3 + wphp6KYa0hpkmu386wlNqrv0ukG+OEe25p4QDl/40p/imeee4Z//s3/OpR9eZmPtj7O38yb7O+/y7POf + 5S/96r+DQJSr/7iey93buwg3W7ZrbUXjOCJbjLTIuPHMD2H9xgpBbXVfyknIKWwNBvOZ39IVaFyyvvoG + 67ePEajqBJuiaPm4DmX97OSTYX3j2hzWL6RYzz9JJMIJCIIuILIVeoSpBARnzp7hr/zVv0wSJ9y4dpud + Oz/LQ4+cY3NrI1MYgCwsgCBgPMnGCQS+QxBk6xDOHIDzWL9Z58rMTeyzApZysvIJzAbUluvNOIevp1+0 + 91vSEfUFO4rDlnSLNBusn5sxJ8H6s6i2k0aa7eno4h9N1q8H07kTMFu5N5EBK8MBUZIaS3RlwJ4pBAev + 5/LIYw8gHi88+JThitV/ev0gO9aabsdFSp0rEiyGmAl5G+vr2bFoxlvKYuSUtwYr2gTzBwBVgVsAMP8r + TQPd/DaEEbSeWhnfTNeMSIvJ3wS/yft2lrLUW5uBKydGuvU0bM+vAH+uhMyddMxfbey/o7NFP9M0JUo7 + DAcdplGCcDIrIOsWERtxAAAgAElEQVS6y5WBI3IzfmYdzJQDkK/3B3Dm7CaO6+J4Lt1eP7MwhKnkqYDb + zvoKs8yzZ2GsFrTUAQuTU2oC5NfErG+4gSsjzkw12NoBNeDX9EgTRDb2rkU+sba+7aquaaTmk2mdHttI + p3qsG3WZAb4SU6t8IJAm0V2Ggz7jSVQyfgHuEvCiYH0x27QpVwSg8x2HNEHg43pu3gTwiOOsu3BWTV32 + RVodfQ3g156HaeUsZSFyiqsC1+/ryuUKPqztb+OkxXhofiZ14NUSaB3Hb8u7yvpHyb1ciqxiyh7G+i1p + G2mocomzOtvaVZQm6wVQWhInLoN+l1TKKssL8ra7CfoCvwb4c2BrwA88HDdTAJ7nMo0SlFRoVez02wJg + bew4ZLMSTUttOSFooXI64wDMi4gZM5Qa3qYgbCf29rv1E5kX3+qgaot3r6wP7U0OGuna7SFRNYtLgDe7 + Vqz77ZlnSiNThaLHoN/lIExKx1+V9XPVkmuBEvp6lr/WWXqBH+C4DsIRBIFPFGV7BJQbjFC6YY3noo3i + 1e/luduIYSkLkdNbFBSoftC28MZhZWxPa//fEcFvAt+SV2u8e2V9qK4FMI/1bekcFfwZW5btZWuXpkbn + G3coqZFiwKDfZRKnJdCrrC+aJn/O+qpQAEqjtKbXC7ImBIJef4VpGCNTRSo1Mm92lNuNHcr6Zt119dJS + Fian1AQwLphWuekPNB18xddoI86KUVlrK9i+GMuMtEbYE2F9mDkm2wp/xO69kh21QZgWZaLJwV/kV9/M + MwdSrgCkUki62Q7AhtlfkG6hCCihb24Zlv2qnN1VvqW4VhrpaIKgy3S6S5pKpJJl+PK5lEW3PNlGHWf1 + b8ZZyknK6TgBS1CArlB7PaARQTeuVOPV75jhrT6CBbI+MBuyrLFV6nBHX70eNvCbQYtrdtYnZ+2saBlg + 01QyGKyQSllp92OwfyFF957WoNCgMgugWDo8VQohRLajkADf94niFJmm+eKiOmN97cyeT70SlVdiUwzG + /IelLEQWOg6g/AbLZp0or4NG1N5tyedatDO37VphGbeWwpKMzToxLn8yrF8kOacUBvhn/9fBP0tPa4VS + +d5/SjMcrhDFabnjrjAcfUVp66xfTCZSOk9Pa2QqcV0HlWYTg1zHJUxkubKwzq0AO4Mb5TtUMS7xv0g5 + nUVBjY+rfPm27cJsloA99erpkcEvoAa2o7F+y53KWIT7BX/B1tWw1aJqmneNkutqmExEvtS3QmuP0XBA + GKeVtv4s+ZnzLnP0KRSUjK6UROlsJGAiJa7rZjv+Cuh0+uzcSbMmgtIt4D8K61dfpm6ksZSTlFMbCTj7 + rmtNgPJ6PZ4N5IZCaYC4JX4F+PUP7l5Zn5lpcyKsr2u4rYG/BgI7+G2mss7XAJSkcYrwNxn2u6B1pXuv + YP3S5C/a+rmzTxW/MvvNphZLgm6AlBIBdPtrRFGaP5riWZum2RFYX2f3Gj0HS1mYLFwBZN9uc/Z+Qwwf + IEK3sPoxgF8fA/BHgvWNa2Z+1tQNa8ras5KBTylJmkiSNEW52/QHnZlnHgzGL7z9qmR5pTQ6Z31VOhKL + mYUpotdBKoUAPM8jjmXuh7Qxu+3pVIGfnZubtyzBv2hZ7KKg5Yo0NjS3fbRt4etR2j6OmpI4jPXLvQMP + SfvQtv4cZqunU4RusXp09b9a+m2sb1oks2uF8y+KUxJW6XW7JPlQ3pmjb+bg00qjKNg+7z5UZN16UpPq + rDsximW5hiBkMwPjJEVrmVezUFBm88/ybHS9Xi2PaykLkQWPA4D2Bnph6M1GtGXGn8gN0+Yot9rB/Gxb + WL88ygciHQ/81XTMOPNZPwenOS6+RZnp5k0j/QI02qLYavlq0Drf5ivf6itKXDqBTzwJS+++VlnqumR5 + cuAbrK9BSTXbOkwpJlE2kEirzFoLOj3iRCFTY8yClfVn5WwOXiruaeMxLy2BRcqpzgU4Xsiq6Wg5sQbP + ju3g0OW49HqYlvSPwPpHAr/xIbexfnu5qlOO28Ffv5TFy7b6lqhUg3DR5EyvmfXrl6DXFfAXrC+1ypsE + KrcoFGEU4zhOOfa/NzhDFKdZmjLb/FPVyz6X9Q3CMKwfiyG0lBOUUxwIdJyI9cM2a6KuJGysT/5NiVbT + up312/v1Dwe+ednG+tU4TW93AQCT9e1h6nmVMTXofHsvJRxGg1UmUZyzfWH6q9LJl+0ZoLJfna0iXLC+ + VJo0X1JcKck0hGKyUJpKXC8gSdJytSBlzvIz69PK+saDmLfi8lJOVE6hF6DtDR7C+pXbRxj8Y6LL+IAq + xvGRWb/eBDku+HXjm27Ls431K3kcC/ymUslAm6QKzxvRW1khTpKM8SsefmWcK8Pbn48ezHcTkrkCkEoR + pTFaaTodjyiOcV0362psY/a5rI9RP9v8iaUWWJQsdhxA+UJtg3rmDNGd22VgYf1KclVwVUBkS6d+qXUM + f3bejNUG/ntn/TIfa9deNZw5Z6BeOqV1vhR4inbWGAz7KFk4+xSyYP3S1J+xfQb07F6aZjP8UjVTCirN + jjvdALmzj5v3ApiLhxyL9Wv3dB5vuTnoYmWxFoDNHLcGKBxy+fG8xFrTnGmO47O+eekkWH9+Wz8rlp31 + K+VulKl2rMtefGtJsz0As+XAw3RArxdkJn7O7uUIP1WY+gbry2IMgUIqmfkBZNENmDkHYykZDPpIdQNH + uNlSYQIcZx74baxffQa6YgUs+X+RckpOQPMVWgYCNUB9BJO/RaHMZ31LvDJCu7l5pHH8hgl7PPBX8y0G + 5NjjFoVtZ/3C+64LlpcpaSKJZEA3CIiNNr5WM+deqrLzVBZLiDdZX8vsWCmN1JIklfSHPdI0QQCB75Hm + ewfow5otNgWYR8mWEjfBv1QBi5LFOgGrB1Q/9jqo54D10Pc/+0xm39whoD0R1sf4kLXFpK+d63qKNTXZ + MPmLmX5G2JL1q2lr81nlSkTl6wBqpZFiFd93Caf5dt+mw89g/fJXy5kFIHXJ+qnWaJkNEU4Sieu4SJlN + LXZdF5mqkvWzmYpu9Tm0PKPKEGhdI4cl/hcmp7QegEUJVIj+OOC3g/T+WL893fb7RV73z/oFY1elDv5Z + mdtY3wwDefteStAS6fSRWpDmnv2SyXMlkErVUARKZf36snQSSqTKn7eCOElwHEiTFADfdwijtKWu2J+5 + rr27xhyRJfoXKae4LHjt5lzwQ3U1HVvYGuuD3axspFk3TQ8Dvi2dk2P9Yvx9M66hOI08Kl7+WQYNcBUD + e9JUobWg0+kymcZ5O75w9hUOv8IKkDn7Z8Avegekzrv/8nQL51yUpARBQJomAAS+TxjGZRejuTPxXNZv + Ga1pi7WUk5VTGgegq+eViT1Y3QLVucKWj76SfBvrm6akNhi7mW5xfnTWt2V7TNav0F89fhGuCfzyvMH6 + xl2dATxNJanyGPR6TOMoH+Rjmv2GQii7+nTmEygHAEk0oFQB/mywUBSl9PpdZJpZAK4fEEVp2Vxo9QPU + Wb9SZ1Fen9VxqQYWJQu2AGb/F9dmR/VZgrPu+2pb2A7Y+eCH2YeU/zeX9W28fxjrN1OoBp/H+m3KoXau + q89vdsfO+pmoMqqS+ShA3affDXLT3vTuZ6xfnKdqNg4gzQYLZM7CvLzZ5KDZ+IEojnEdhzRNAY3ruNmi + IHkaVoWra3VoPAM9+6sHWcqJywItgOIDsEltbEDFUtB5O9AOiqpeaEs/VzCaE2T9POyhrG9ea7J+Mw1L + Pe+R9c2bWme7AaUyRfvb9Lodo51fjPKTpYMvNboBi/a+1iJn/Vk3YdEs0FqXewtkTYBsy/DpNEZK3ayn + rg1smlt/o37LcQALlcX7AKxteculVtafnR/O+sX9o7T1s2tzwb9Q1jfDmO39JvibrF/P27iRxy/b+Kkm + ZQ3P9wjDNDf7ZTmvP02LGX85wMkdfXkbXsrc5M9HByqtyvkC0zAhcJxsGTCt8FyX/f24XH2oaC4Ic4b/ + vC5ODcbyUdW6L2UhcgpDgdvEAKYBNFuYKmEeBv4izHFY3xKm0tZvt0hmRbpX1heAqgavg7/VmVYzm437 + Smee/ThOiFUHrbMx+1Jns/m0VKRqpii0bUiwUsZaAM37kzBmOApQMkVqheP5jKcRKs3CUawMJLC8Nxv4 + jbrUlOdSFiOn0ARoYVVhsjTNcMdmfaiDoC3do7N+y4KblSwPYf1KHe3lmYH/HlnfAn5lOPgS3SdMUlIp + y4E9s+m9GkUWtmB4XcwFKFlfzuYNyEwpaKWYhCHe5oBs7J7AdQOiKJlNGioV1/z3Ua1fbYi4tinrpZyU + LLQJYD/JL2g9pytQW+LPWSS0BP+8D83G+22sL06A9TmCo28O+O+B9bV5XZHv0qPB63EQxrkPIBvIk5Zm + vZwt+FHMC8i7BmdWQD61OL8mUSA10zDOvDmOg4PG8zzCcIpMVTmvoNq338b6zedTfaJLFbAoOaUmgOWj + rXcFGuFajYL6xRNnfQ4Hfi38vbG+KsusjfZ/hfVnmc3Nqwn+7Ecpme0EpDRBEJAkshzOm6qsHS+lYeIb + q//M9hGYzRPQmH6ArDdgGsaQbwsm0HQ6AXF6kPcWqFq5a8+j9SW3+VuWsghZ7JJg9Q+4/H5r5mt5JChn + EB4Z/PNZ/3jgP0QhVcIfhfWLG4XGq7N+7f8js37uNyivNssgpSaVKUJ4BEGQd/MV03nlbNGPsm2vym4+ + mbN+2Z9vtPuzAUZZnPFU4Xouge8D0OkMkPEtZkuKF4VqLgxarZdRCy0qgwGXymCxcgpDgY3T+jXjZvaZ + lF9wbfBgPZ08TAvra9O6aGMaa1u/WYNm+S31OJT1TUff/bB+Ec6y8abxXJTMhgAnicTxNwj8gP1U5l1/ + JrMXS35pqwLQhvNPF4uEFr4AdL4cmIMfeAgEQW/E3l6SzT/QxV/WAih6AyxPlgrrNyzDtm9mKSchp2AB + lFfs4YDGLkFzwV9NWM9pShwOfpOlZhk3fAVzWL8BYFt+LV7+Jutb8miAv0VxVCyLjOWTJCXWm3RdlzSc + ImXG+lGS5msEpiXDy2xxQIQjcPOt2Uz2b/zmXXxpKvH9TrY0eLfH1TAtHYymBWgHvzarRPPla6sfeSkn + JwvfGajCzMxWom8GtoO4YkUW00TnOA+bl00FYrL+/LJWM2/+zpKysVV9Bl9h39RymWvyt5TbTMFahpzV + ZdYM6PccEgRJmlkFYZRwbnuVi9vrPHBuA8dxAIFUilt3dvn45l1+eOkqd/fG+J6DEM5sFKCu/ynCKKHf + 7yKlpN/vE4YpaSrL+5mVYPerzKp9lPe4lEXI6TUBjOsVa6+4UgwOFBYGNuaHV9O3nM9l/Sp4myWphzfT + qQG/wfrF7D1bW79m8i+A9WdlyI/yYb6uK7g9DbMxAFLy4tMPsbE6JFHw1vtXGO/tE05jOoMho7UNNta3 + +DMXz7O7t8d337zExzfu4nuZkihBrWdLiodRTL/fJQwjur0eYZyQJqpcT7BBAuUuwW2TvWzvdakJFiWn + 1ASoM5gRDovFX79wBEffkVi/PG2qoEbhbeVuZf16Wib475/1zXTaWL9afFWO74+lz34YIqXiwXMbBL7L + a29d4g9+5/f44NKHJOEB57cittc1D1zYYuPMA3Q3nsddeYTnn3qUB89v8J033idJJa4jsrQ1FHP+wzim + N1whlYper0cqIU1mIwHNF6q1ys40aFRlg5Jm3esKdymLkFPuBhTGldpuQdoC4hNi/RnwC2lZ5vsw0M1T + NC3AL0OZBahqk2ZaNpO/klU7+KE2RVl0soU7PMFKv8e3X3uX/++3/gk3PvoIB8VzLz7MAxdWOLc9ZHXg + EQSC6Z1v4I5f4SP/ORL/Ai89+zhvX7rCrbv7uI4oB/horQmjhKDbxR0f0Ol08slHhaWgLeUyaqW1ZQ9Y + bZ09vpTFyMItgCoHwOz1z+vruXfwV8FmsogZf77JX7FTGuBvK9fsfgPSc03+eaxvpls/hir4Z8AvTHW0 + ZjcMSKUk8AM+vrnD1//ff8H1yx+SSM3jT2wxWhvh97fxVi4wOrvNxlqffpBy/Qf/kA2+yp3oIpfuPsXZ + zVWU0ty8u4sjRLa9OBAnKZ7rgIBev4eSNVAXz9OipKvgt1tlSwtgsbJYH0BtGGfTu47dnD6io0/XrxVx + D/14mmxsLVsloznALzMT1atmvBNl/br1UgU/eTtdo7i1O0W6A5Ik4b13r/Hx+++DgI2RwvE0d3d38YMO + vX6PbreDFg7xcMS+9wwXz66x1ttmfDnlwxt3Obe9hiPgxu1dhOcgyJYF00ITx1OCjo8CHM8B8u4/o+7V + XdhyWmg822bdllpgcXI6KwLZXqCtr78RrAm66vcyhz2toK0np9C25oBuOW6UaVbmArRHY/3ar1HWw1lf + V+I2LZ7ZsesIojhB+dkyXVcvXyZNEqSUnN2A/TBEOA47wS6O55HEKbu7u/T7PTxvmyv7K/TTDqtdyVY3 + 4dKPrvPkoxeRSrG7P0E4DqlUuI5LnMR4no/QAq0MsNfew4z1beCviW6o5aWcsCx+RaC6Q604FybwZoNk + MsVQZwFqZiTV+ybrV/9rKVidifK/uYxrybv8acn7GKxfpnMPrF9PX1MM9c26/pSTb+YRRWX7XYmA0XDE + YDhgMOjjuQ5SpozHYyaTCY7j4jh38XwXNCgpeenpT/H6hzs8/ch5vvvmBwAkUuIGnWxNgSTG9b3MD6DM + MtvXNmyVeY9+KScqi3cCmrivsWnJXroRvBLqeKxfT6WWojZ5us7C9WNbiY7A+uXN02J9jHuzwqVSZYN/ + nGybLq10vp+fINEBHb9HrzdkfX2TrY1Ngm6A7/tIKXFcjziOicKIVCZopfn0o+d45Xtf50r3RbY2Vrm9 + s4+UisBxUVISxzG+HxCG6WxkYX0kzyHg19bm31ILLEoWuzNQa9utYNzDXuycefRHYv2awmmAch7jmmGN + sfdHYn0j/crFGsjLqDalVi9rnqtVsYA2y6dnB1KmaEei0fiBj+tmy3R3XAjid7nxUY/J/gaTgwc4e/Fx + Vkcder0Br33rn7G6tskDj72A7wckSUSv22Ej+g47ew/RG6zkVoPC9RzQijSN6XQ6hFFarjGgW+tWF4tz + mFwhLPG/MDmlvQHroMvP56wM3GryH4n1a+f3xPrmh5hPay2dfJpqtrp5bM1vpkiarF+pDPfK+sU9IQSO + EPh6l6ncQAgYbm7gui6O4zANUzZWxrjuGE/d5eDGJfZvfA2tfDrdAcHwU5y98AjRlX/CuP8cq2eexnVd + 3GCVZ87s8P1bHRzHyV05Ot9kRNLpdoiiNBsMpGSmBFS9Z6Aqh07TXsrCZPFOwMa2AIe97MNM/uOxftWR + ZGH9xnlddFmRe2Z9hKXsNbA36ns4+HXNKgGdDa4R4DjZFl1nOzd5L3kUoTX90Rqdbp8kDoniiKAXsL3a + ZdgPcF2HOEoZTyLQB2w9dJELDzzCZNRlf+c6Nz/8NpNPP44aPsf5B17g9ZvXcIRAak3Q7SPTFKkknhsQ + J9k8BCnzrcL9bCSh7dnWbDQa/gJLqKWcnJzioqAtjF65YgCqMqOvxvrV/+xpHsr6NMsHzD7Apsl/PNYv + 7i+C9evgr95zBLiOg+87PLQV8+Z7B/jdHo7j8fBzz/L+976HcH3W1+DCuRFrox5B4BHHKTt7U27c2iea + 3Gb/4IBY9vAGF/Ail1defY3+aJM4Faz2O0yiFDQ4jpsPNU7wA58wlKRptvZgO3gbQ6Wqz6Gs9mEKein3 + I6fQDVhXBGB74RqMXb1NAJ0261fDFzlb89ZG+FKMse5a1cp+EqxvPptaefI4Il+koxN4REnEw4MrXJFP + opVi68EH2b1+g53rH3LthsvjjwSsr/UY9APSVNHpuKRpytXdPT788DJSKjzfQ+AT3d6h2+1w4+YNHOHg + uC5CwN7+lCSK0SiCICAMpbGlmP1d1x6icVx9LvWnu5STFWexyduAZmH+yls2gH9Pjj7TULeAv2SUOZNR + KqxvAb82z02Q5hOCNDn4Z+k0HH0NZadnOVasJxP8ala+hjKaxXEcge8J+v2AcZSy3XkftfsuwnFwXY+n + fvIn2Dj/CD+8JPjqN2KSROG6gk7Hpdfx6XUCNC4HBwckScJ0OmU6DRmPx+zc3eHjKx8TJymba0N81+Ng + OsFxBZ1Oj+FwRCLNuQCVt9NUzOa5jQBqz2ApJysLUwDVFoBNj+syXCNMbVScbUCJmUZxqOugqQOt1eSv + AmsGxaJYNtYt2L04LpKtsn7Di10p4yysnfVNVVY3+WvgN+rlCAg8j0EvYH2tT6oVT67+gGT/Cq5w8Dsd + nv/8T/HYc3+MSx/B7/7+DjduTdjdDTmYRKRJihcMCIKAlZURw8EKnivodLu4rofGwQv6nNtaw/ddfM9j + dXWdc2fPcvHig6Spajj9qmMt68Cvg7+laks5cfkEpgNn9zR1k784NNkQ44Ovp21GqbNl3fKwsX4tzRrr + N0aq5b0AVcau51H7xOea/DXgN+4XR01H3+ym/bkIwPMFw0FAnPSIkpSPruzwqf53+WDfY7T1GIHn8OSL + L/Dgo4/yzuvf4+//5gdcON/hwsVNhr1V7u5MGK10+eo//Z946LEn+fP/+q9w4/YOt3f2cIdnOXtmnV43 + IPB9EqHZ3t5idW0NIRQfvfXDSpmyiV+HLQxqKmTzvP4ul3KScgp7A1ZfeKV7zxzxZwV+PX7tXM/g07Qe + zMvzl/c208nyto1cgxnT1xl4FqgJfPO8WYYm65tH7Y4++xyK2bnjZEt1jYadbB1Aqbh6bY8nvO/w0Z2Y + 3oUXCQKHwaDH9rk/w2Rvl7feeIN/8ZV36XY8PHdMv/8B5x98gkefeIGDKNv/b3cCF85v8rnnH+cPXnuX + bsdndzoh6HTpBF20htGwg+M6OK5oWRG4pfzmM6o8kqUGWJScws5AM+a0sn5bW1/UX3rzA9KV6zoHbn1B + jnk7ExUpmDvXmGWr1MTyO0tjFvwkWF+33mt1qJrlFtlePL4roBdUwl69tscF77tMb+0Srf4kg81NPM9h + tDLg/AMX0D/3s1y+9CE7t25x9aOrdEYdrt9xSIJdti88yBe++BleeOphXnnrQ1KpEFpy9YNL9Ad9ZCK5 + 9P47/KlnNggCB891cGzbfevaeassOwAXLafTBDjCxJhCTdQCWs+tbf3KuS1udSXd7DssFFOhhIxBC/Uh + rHNYv1qm8oI9XoP1Z+k0Wb9WmUNY3/xfQLZkt+vQ7Xqs0UOTzdH/6MoOI/d93r864ubVPp96+ll6qyNc + VyCEw6df+DQd36fX8Qk8H9932NoY0Q0Cbu0c8J3vX2J3HOK5Dt/52tcZH0yI45TbN6Z46TU6g1X6/YDA + z60As8y2etWrVFkUeqkCFimnsB5A9tKFyaYVcJnONmtKlcNqx5CFTQ5l/Vmuzba+hU2Pzfo06jfLsV7m + o7J+vS5N8M8Oq+GE0NmSXl2XdfrofMOQKx/vcnE7oDc8ywfvvMr7IuDhhx/j7PnzaC2YxpJxmNINEnzP + 5aPreyRpihJki4jGEd/+2le5c/MmKI1MIm7ceIcvfrbDcKXDyiCg0/FypXIE1jfeS3OZgKUSWJSc0mQg + E7BVYGhqyqEacXbWyvpG+AZ4zGGIqhJEV/6bZ6HMY/16OTX2tu081p+Xfz1OkUc9DVvY7LiwwH3XhY5m + ddQhSQaEUcLVm2+Au8rDD5xHKfjog7f5/qvfYWP7LNtnz7F95hyxEMRJtsqP4zrcvnaNH31wiZtXrxJF + Ia7j4Hkx169+wOc+rdnY6LG50Wdl2CHwXZzKSNAWIJeKHWwjBpfwX5yc7pqANsaq3mieN1if5ofeYH0L + gLUwWJ/mR3ko68/O7axv2wlnHuvPtwjuh/XraQkECIXvufR7AWuriiSRpMkO1y7/Fh+PO+CvItwem/01 + 4t3LfPPNb9HtD7LiOV2k0niuR5rGuI6DEJpukDCe3qLn3eTf/rOb3J2MOXtmmK0q1MuaDu17AZhVann/ + xTe01AALk1PvBqwA0OrQqgK9wnFleGO4rrbFNS2O4kzVMDWP9Ss3KuU8dEbhoQCv16ker5qfWQ8z3FHB + X9wX+foLnucw6Pukaz2SVGYLfN7cZzzeQcfQJSCeuOyPV7l+8y6Dno/rBQivQycImI73ieOUfpDwS19Y + 4eObYz64OuXKnR0efmCNrY0+K8OATuCWisIq2nxG5kVRPtbZwVIDLEpOdWOQ/PXm73keE0NzHnmd9W0f + fnFsMAjGZ3YfrD8rU6WQljK0LOlVB/89sn7l/1blZUsrUwKuo/EDl+GgA2g6gctopcPefsiduxNu3x2T + ppJf+qW/wvr6Bnfu7LBz62N2rr3Jo899ge9971X+xJ/4PI8++ggeU6Jv/1MS8Trbmx7ra31GKx16gYfn + toDfyvq1cgvzmTeTWMrJyamsClwdVVccVENUzrQ2FMQ8NjTj2lg//9AOBdtRWL9m4t8T69vu2cpjz+O4 + rN+UbD0BVzh0Oi6O08X3XQZ9n+l6j/XVLivDDtdu7LF791a2a7BSrKyfpTNYY3d/zLPPPYfjety6dQff + 93jkmb/Ak5/5Jbpc49bl/xPXcXBcxz79t5X1jfoLW92WsihZeBOgOVe9Hfxlr4GVYS3nlY//OKxfL0u7 + YrA7+uplOG5bv16eeUrOZH2MZ3A01q+UIb8lhMZxRNYc6Pl0ApfBIDPbBRDHCXv7e4RxEV6UfwDy1i12 + /QDf9wiCDr7v8+LzT3L1PZXvCmxb7vsQ1m/cs7zbpZy4LL4JMJf1Z0DXmJuD0mS5e2Z9I9yhrA/l1F1b + mY9k8tev18v/CbB+JS+NVJo0yXYKStLZ0l2OEHi+Szfw6W5cQCmXJIlJ4pgkTRDCZTqZMmWaKQRH4DoO + rufx7KefJIwSkjRbBKRe1Hbwz1HqWi+bAAuWU2gCmBNeoAEq00qwytHBf8+sj6Zp8h+D9WeRjIRIeVEA + ACAASURBVDrNKftJsn4j71oatTSV0iRxShgldFc/hzy4xXRykzi8SxglTCcJnuuwsr6N1prXv/nbpGmM + 57k89uxP59N803zBD0mapsRRjJSKMEpJUmNDEF0H/rxnUBxbeg2WSmBhcopOQKhr/gJGpWNwrslvB8Hs + Wzsu6zfvnwTrV/6/X9a3pnEvrD8TpRVxIjk4iIn651m/+EU2Ox263R5+4HLr2rvcvvxtfv+1H+J5AZd+ + 8DWeee6z/PKv/Ee8994H3NoJGQ6HgM43Ic0siSAIkDn4C8vveCa/WSfjOS/Bv1BZqA+gBKXFe67FvA+5 + rj3msf7svBqmOLT3z9fzPTnWb16vFqstj5Ng/bwcbXmRWwCJ5GAcceutV+mtRXQCHz8I6AQB/UGf4cWf + ZuOtv8cHH/yIP/Uzv8BPffHnGYzO8uRTHX77v/mvefCRxxmtrtPr9un0enS7XYJOgB+4+dj/ulV3iOVj + HpY7xbdMvFrKicpiJwOVL9yAqjZfLJZ3qysfQvUDmMf6dVanEc/6IZZJtCmke2X9lvzm5NGuzI7L+vX8 + qvGllIRxyrVrH+Hu9fF9D8/36QQBnucTBD6rD/00X3x+Hd/3ubMfcv3O2zz95OPcvvp1Jne/TafTx/UH + dPvrrG1c5My6Sy/Ihv5aF3udx/rWoLNnokG2VHop9ymnMB2Y8kBr89uwzBJr/fgNy4F5QGHOh3YU1q/e + b6zl17h/v6zflsY9sH4ZvIVhjXtaZzv4IlxAM51OkQfjfBYheJ6H53lcvXoNz/PyHgB49JEHuHBuxMZa + j17Xzz39uwTBmCtvXebs9pAg8HAcp1qOVkefpbhm3WfxJi0VX8p9ygItgMIMzDzMM9DmEK7ME1e1D0Ex + W6yoSGOWbjvLmgdz2NPG+tUPjmqIOvhPmvWbac9XGrPrR2V9U4QA4QjWNs7TXz2PytvxSZKSyoQ4ionC + kDhOUEqWTr00Sdla73Ph3IjRSgffd9Fa47oOva7HyrBDr+uRbz1wLNafneRbiaMoBm8vZXGycAug1Qtc + ntaZvAmE4zn6qnHr9486gWce68+S/KPD+oUIIXAch07gkdy8ys2DBMft0u2P6HQH9Ps99Apcef81VjfO + 4PpDFC5JHOE4DsNhh7W1Hlvrfbodr0zf9Rw6gYvn5WsAzAN/K+sbddbG0O2lDliYnMpIwJkcDrzZsQl8 + jgi2w1i//jXZFcrhrD+vPItn/Wqy80DWTKBYMLTX8+j7l9i//ga378bEiYfX3STortHpjbj+4Q84e3aD + s2e2WTn3GcRwE9fL9gl0hSDwHfp9P9sq3BHl4CJXFOb/UcBvUVw69yFos9mvp7YnsZT7lwWvCHRUVlsE + 6+tK2IYlciKsXyZeK0P9uiWPSvlPkPXnxs8GWwWBx8qgw9mtIb7nMByG7B2E7B9cYffme0Rhwktf+us8 + 88yz7P3w73Pl2vdwVp5EKcU4dAkjxcEkyZSJ7xAELq7rILQAYY4CrNer9YT6s9NaIkQ+NEwQt1ZoKfcl + p7AxiA2YGNd05Vjn4NfCFtY8bGMZA1wlBu8V/Edl/Xr5mh/3/bF+nsY9sr5ZCuEIvJy9hdOn2/XYWOsx + nSbs7Yfcvjvlxs19Ll68iFIJnUf/MudvfZ8PLv2Qa9eeZuWBf4ONxy/ixu+wH36IG+4T+AmBD72uh+9b + ZgE2itRS/vJcABK0M1eZLeX+ZaEWQHaQA7U6LpjmApu6dDZpFNb59XMBUGNVK/ibrHyyrD8njfLe/bB+ + Pd7RWL9u/7iOyFjbEXQClyTJNgUZDnx8P5sPcHfnLvvjA9IkQbhnWD3j8M477zMcdJlGDv3BTxCs/Um0 + iojDy7jqLcbjywwGPsLPuwMb1ZzP+pkU8VI0HlIqlEwVS1mInI4PoLWtn50XU39FEbYgATP8XLP35Fl/ + luUnzPqzgjTu0XrPWooynMpH62mdPXPfc/FcgVIdJtOElUHA1avXcD0Xmaa4rofnBwhCpNbcuHGDILib + dRe6Hn4w4uEX/33e+8aXcb1sVyLX2s1rO7bd02ilEEIV5Vw2ARYki98efN57p/gIC9P/iGArz2vgvRfW + t7C5HXQLYP1W8Otasm2sb0tzdlPXziEbCShTSRimoLuE0RSpUrRWhHFKkiiiWBIMA9I0xfM6SCWZTCag + FePJJNsWzHFwXQfP8/F8n6c+9QR7+xGDfoDqKHCdSr7W8lvvFRuJapRKi+cwzwk4Z99hayZLMeRUtge3 + ed/LMeNAcwupMhjtH1CV9YucqkGPyvq1O0c1+VucmeX/lfKfHutXn0Q1nNKKMEzZO4h48if+E6KD69y9 + 8QbhwVWI7qCVxHN9RltbuK7Djz54mzRNGQ5HaKeXDSJKZxOBoihBa0WcxIwnCXGcrR9orbOtLpV6FfsI + ZAogTlQxqKhXiyyM3zlrjqEPuf9jL6czDqDyQeRML4oPvWn+lYf3zPrNdO+f9Wv3DjX5j8v6eRr3xfp1 + Jdisi0oVUZyyuzfl+l1Fr/cI6489TRAE2VoABx9z/YNv8N0f7gCC177+j/nU0y/y7Es/z0HocvPuHr7n + ZaCPE9I0JU1THMchjiVStqyhYD02zovuP4rlywThJMRzXIBicwNBFfRt7K+N+7oWdqkIDDmF3YHNazNl + ULT7ZypaVyNaV4axsb55v3rPyNVI/35Z35KfeaUV/EdlfXv6zbK3pGGLZ+StlCKOUw7GMa+//hpB4BN0 + OnR8n8FgQH+wQv/8l9A/+EdMpwmf/dyX+PyXfp7HnniRndvX+O4/+Ic8+sTT9Acj+oPMWSulptPplKXQ + 1gVS68dmuQrwuyA8tOMhgDu3x/idoIjoUgV/PVET4LZj270fezmlRUGr0zp1ZVIAs3C6Hq92boD/Dzfr + H5amJQ0rs9efy9wULPnWy5yFTGVmBVz/+Erm4HNdfN/H97OJQEHQYXXzPI9unGVtY4twOuW1117l8cce + 4ntf/0dcu/wo65vnWVnd/v/be/No2a76vvOz9xlrvPPwZr2neUKIwWCQ9ADZBjz0crq9ArFst0k3lpNO + YscYZwXhtbKyLLo72I693B0bMIG2jQ3dxCTGkMTGIxgxGIEQCKQn6c3TnW/dms64+49zqupU1Tl16456 + 0qvvWlV1hn323ufU+f6+vz0zPjlHeeIAhmHGqwEJZPdc4OnPo/2TJL8EqSGEzsaGQ63mUsoXAWXQ/a5m + PYws8vcaibTrrkvs7XwA0VZCfFXfC5lNjh4VTVQobln1M8m/16qflreeOPZB9ZPHlVJRRWAQks/l8X0P + z3NxHCeqkI179hmGxeJSBV0/HVX46RpHjxxkekJhiYs465epr0gWzlnk8mM0Fr/IbNHE0LUelm3yPIQA + ZDQwSeoIYdKohZz6zhXGJgoQjSPx2Vzhh91up5zYv269gn1aGKRro3u/i1zJ391W/c7x9PEAqZkerPqk + GbSsOFPi2IHq9+WjN46M5y6EQMafsfFJNC0aERgGHq7r4nkejuOiFHh+SKNRRamAMFT4nsf8bInpyWje + fyEESikMI6BknWasnMM0k8uBZak+icq+SPUROkIarK66nH1ukeLkIfKFJl5doWtGcGg2p11caGT1B9iu + Idhs/yWPvW8GpPdpDvNs91L1N4+HrHDJIy9YRd+wqp8ehwA0TWAYGvWlJ9hwcmhGDrswhmUXKdpFymOS + 5779efLFcSambgClaDSqIGBiLMeBuRJjJRtDF9GKQUJgWRqFvEnO1pEy495Ub04kSB2kjgp1zp9ZZ3lh + GduG8vgctn4VPI+cnWvcfdNY7uJCo94T6aAmwCzypz2g69YQ7EszYL9q9210h1MMJv+uqH5/PN2bWeTf + DdVPj78/Txlx9F3fm3ZKHPEhIcAwNIoFE3/5CepXa1RqIV5goKSN0GykMKmuXuDGEyeQVg1RuJHZ2XlM + w8S2dAp5g4kxi1zOQKlolmFNiqhDkRFNFDrwmbTUX2ogDVwHnj+1QL22RrEAhWKBQrGEra8TBgbFcpmp + MTNHNC/AMDX/vcfTCD7II8i67iWHfTIAid82C5LHU/bb781g0u6q6vfFn0yjdW6bqp+aj8T+QNXv1f2t + qX4niWgsgGXplEsWTcdjetbE2nBoOk2azQ1qdZeNmsfrH/wZThw/zMKTv8va2irr/isRIsQPgqh7rooG + /eiaQNNaHYNILAeepvotVomowk9E5P/uUwv4zhoT4zpWziZfzFEaH0PzF/HcgPLYeK5Y0ErASuqj6Scu + bN876DUSvXG9pIzBPkwL3iF0f005JAnfuo52qKwXPYX4XcH3WfVTkunK566pfkocg1S/HaRzTpMSy5SU + SxZSCMpFE8cJcFyfesNlZa1BveZz0x2voTw2hh78KKX1Rc6de5yFq7egjBNIPYfjOoShi6YFmEY0v4Bp + aqCBltYI0DokIOrtpwOSZ59ewm2uMjtjY1kmhUKOfMHGtm2CWoidN5icnC6XC8YB4FwiqiyCt1LPInIW + +QcRP3k3GXf34sT+NAP2PS7Vf77L5e8xFFtU/c4lu636m8WZEseeqH5v2oNVP3lQyqgIIBEYmqBYMAiD + eKbgmouuSZx8yNWFK6ysLCGNW9DKR5idh4uXF7nhrv+RuYNH0NQaXvUMoXMFN1xBOev4QZ2cpSN0rTMY + qJ2VFtdavX0ktapHrbrO/KxFLm9RKNrYtoVh6ggCICSX05iZnZt62Z23PvB937P27c99ZaFGPwkHGYQs + DDIEWUWBtLAvauzPwiBdSp34qGTA1FgGqH7/+Va69IbL6LK7fdXvTbs/7heyoq8Vd2ocKp5sTReYQsfQ + FYoQO9ARAup1E00FnDt3HsPQkVLDMEw0/Tj4TfJS4+qVc+i6iWHegFm8FcO0mShXufjt30KORUWCVmOg + 6klbQFQHIDUadRchAuxcjmIpRy5ntecTVEEFqU+ggnUm58Y5cdPNDxTyf/9honqAFgnTiL8ZYTcLn3Vu + WA/hRYX9qwPoJUSfE7Bd1e8cv7Yq+jKMTl+eMuLoS7c37eFVv3tTESqF74V4vk8Qr+IThgrfD/GDEF3X + cOoOzWaTMIxa3jRdQ9d01isVNE3D0A0M04h+DZ0Dr/4e1jcccrYeTxXWk4/WMxFRf3+BwPVCNA3snIVt + m2iJAUShcxW9eCfu+pfIF/IcPHLgDe951zve/V/++n2PsDlhNyN61nnoJ3ga2TfzEF402NeuwO0TibP9 + qk5EEBEd33XVzzi3M9WP49iR6g8g7tCqP4j80XYQhnheQL3hkcvfiFNbo9mo4Ll1ao0Ga5Umed1gbm4W + x3EIlcR1XaobGwS+i3AjYwHR9GJSSnRdo9GoUau7uJ5NEIYoZE82WjyJuCKEwPNCNE1gGloX+VEQuItI + fxXNPkbgnmd6vkgYqH/21c88kn/2zOLv/qP/7YNPZT7IwdiKR7CZBzDM/jWNfZoRKP4oOgSLCd5zUYKA + 15jqp2U3mc9NyT+s6qfEMUj120EyDF7PPYdhSNOJZv+Zv/MhxpWP7yzj1JdwNq4wMbMI3hpPrwvGxiZ5 + 8mt/jm0XeNm9r+Xq0hqu68crAvnxMmEBrusTBD6uG+AH3UuDZXpC8dyBAoXURDyNWGeSmDBo4G08hVF+ + GYb1CrzK48wfKlAcy/3jQiH3j7/86ff8+68+cebD/+y9f3ie4Vz4Qee2agheMs2H+zQpaJou9byy7eHB + 6cOH+0igOvEk4+hNd9C5dOL3XLeZ6reDD0fAjBhS0u3N83ZUv/c6RRgoHCdgfb3J82cvY9s5bHscqzBP + ceKVjB0L8RsLfPWPP87ZtQ0Wzj3JAw/+A15+zz2cPXeBJ7/zLNOTk4DC9308L1oQ1NAN/LC10Gh62t15 + j4oBKKK5BBP3Gm2FhH4Fr/J1NPsGzPH7CRqnKOQXyJ+Yolop/8uxcuFfPv5ff/nPFpc3Pvzmn/iN/8bW + KgKTrQU7NQQpN9hOY9D5Fxz7UARQKX9uL8EGqH5X8KTq98Sz66rfOk8KBuRjYNrdcQyn+hlxtI1lSrgB + BkkRLQ1Wa3gsfPc7WEY8GtC0MC2TfD6PnStw8NANTM4G3HPvqzl07FbWNpocOniQ3/voh7jzntcwf/Ao + di5HLheCUlg5G01GXYxFmgFvb3Y4J3UIwqgSsjeX0U9I6FVRwXcJ3UX0/AnMidvwq9+hWF6iND5Js8kP + TExVf+Br//WX/2J5pfoff+Chf/+Z/oe1KbGHCZ91PukFDDIK12TRYM+LAL2vgeh9BqkvcnyilwSpqt+z + P4RH0PYwMkm2Q9VPTTsljr50e9PeHdVPIgwUQaBw3YA1Z72dT6lpGLqObuhYpkUuN8nYeAnLsriysILv + X+Xld9/B1fNforbyJPnCOPnSLJOzNzBz4CaC4GVYhoaUDJgVWHXoEa8doMKWEU6p72l1JQ9CgvAKobeC + rJ9Ctw+h2yfwm2exDZ/coTLlydKD9rmFB/+///DTP/nOf/Wxz6xteGnjBtIIOoz6J0meFd9mcfdef00Y + g32pBOxXfYbwCnrObqr6g87thupH567Zir5NDVJvHhSzMzN4nofve7E77+N6HrVqHSnXUJcUUkqkFAgh + uf3WG5mftRkr6RhGA98/TXXhLM2Vx/jji5/m9hMGptFZILT/mXTzQREdUihCRc9Mwsl7CUEJVOgQOAuE + 7hJIAyktQqEhCLFyeWYOTzK3vPRL//MPH7nwm3/0/Df6H2BXjlo1kmkPbVBLAinnB5E97dw1g72fEajr + xUwor2hZfZXwCpLnaW9vTfXTz7VJc0304x+UdkYcqj+GbPL3GtvOppTE/fYljUBQKI6jaQIpogpC3/fw + fI9GvYFu5nGbNZrNBkHgEoYhc1MFZmcLFPIGmpSEocLQBYW8RrlsYdt6VL+36TMR0WjCUMVTiAW0eZL6 + H6nE7Ybg+4TCAc1AKQspJLl8kVKxdMfspPm/Av88EcGwzYaDvIAsbNcQXBPewL7UAXTebdV+WfvvuJcE + GWq7K6rfc93Q5B+WgKkxpKSbnuf0JLZm8NKjUwgEui7J5XTqC5/mu8s6PkXswgTF8hzFsSlMQ+eZr3+G + fC7HwRtfw4GDR3CdOrquUypZTE/kmBjPYcVrA7YWCMnZOpYpO016mzxzqUkCFc0AHA0qGvQ8Ettha9FW + BSEINFToomk+umFoui7ewtaJDIPL6WkEHkT+rXgEL5iHsE9dgVXPi5w4l1T9xKX7o/pdiaXknx26/Pup + +mn56t8R8VoAxYJJvVZh3KqzWnFYXwtYPKNwPYXrC6Ym5zk0VyLf/ArrV2uMz98RD/oBTZPYpkYhb8R9 + AQS6FnkVmi6jHgApyt/9tkf8UWFrCrHIzc98VmmbKkAoUMpHhD5K+a1qhqlEUlvpNjyMkUgLu1NDkNzf + VyOw98uDd5X1k+5+T7jWTir5t6b6rbT6yb1/qt9N3UFGZ5dVv2+3O5ymgW1pjJdtlFKYlsb4eDQysNbw + qFQcVteafP+PvJMjhw9w9ckPsbLyTc6d3sC59QhBOIsUBs2mBwJsU8MwohFAIl5ePP1ptDZU+7WPmv+S + x9OEsHdcRvp9Rc80KSr0rFHe5+oPQhY5k9ubNSOmbaflJy1/vXnZM+xTMyD030ePF9DFxyFUbk9UP45h + R6o/gLgDDVJ33Kr/4BZVPyVtouG6hiGjMrwmKOTNuPIvoFbzWFqtk7MMCuUZmr6JnH0rE1NN/LOPc/rc + RY7c8T9w6MgktuEQNBdxa8/TbCzhOg0KeQPL1BC6jGf27SE+oqsDmJCCEEUYRDMOCaFS89y5vnU03hZA + GCJkTP7WZKSqfba1l6W0WaTrDTtsn4HNMGwRg0S4PfUK9q0jUNd2l6FOkF+pzp87SOUGqn72+eFVf9i0 + M+LYNO39VP3u/WgMv0Ta0axA+ZxO4Cv8IKSaixfgCSUXLl7ENA1cDzStiBy7m42aQ748RT0oExg59MIx + 9PxdCL9CQV5l9cp/RwgTqQlk29dLM3qxMZKiewW4jPsr3/RegsZZ/MZZ/PoZ/MaZOIhKVBaHvfctEr+D + PIHteAS96QzjCaQRejMPIS2uXcU+TAnWr/TtbehXWxUfTJ0WvOuCThypxO+5bmjyZ8SxbdVPz3N6EsMa + na2pfta5aCYfiWYodF0QBDqWpWFbGhcuXEDXNYIgRNM0dMPE0D0cx2GjUsEwzHgWYRNNt5i97Y2cOfUn + mIaMFgjV4vQ2MXpKtboOh0DKakJKIY0JpDGBUX55fDCMDcFpguYFQucSKL/3OW1VnZMZ3ezaYT2BzeLq + NQppRN/TloN98gAgWRHYpfqt/fSLuq9POb7Tir4XVPXbQYY1OttT/a7npRR+EODHS4AFcd99paDe9Gg0 + fcIQpC5x3WjJMNd1Ec0mrVmDNU2ixesC6no0XPim48fYqDqUSxYFFYKSGXlJQ0u9FcM9d4meP4GeP9E+ + svHcv+19n7ZrALbjcm9mENK8gKzr0+JOy9euFA32vhIwMvOdg12DgVrHWhf0RtDavDZVv5u6g4zOdlS/ + 97rdUf1QhXhuNBpw6vAPUVk+Q61yFae5Qq3m0Kh7SCWYm5vFdV003cYPAq5efJ5CaaK9LJjneTjNzlTi + jtOk4fj4fkAYZOVHJZ6L6H41+u5xOxwMkzqzXQOw1UQ3qyzc7NosZU8romS1KCSPbQl7XwmYofrt1oCB + BNwL1Y9j2FT10+Lsy0F6HAOVuRN3ahybGsNBcW9uCAJf0XQC1ioOs+OvZWbytRyQIQIP36tRXb1IbeV5 + vvRcnfHxcT73J/8Bp1Hldfe/lUI5R6i0eF0BH9+PRgWGYYiQksAPCVOXBmvti8TrnqgjiMnbLgKkVfht + gpQWpr0sAmwnvjQy9x5rHSclDHRfm9xPht0S9m1loO7jPafTrsnoDdZ+aTKJMKzqZ+SrL+2MOPqu7017 + l1W/b3dY8qvun1DhuD6VDYdnnj2NaVpYloVpmlhmEbN0O5Pjt+F89bf54ldOUTAlb37LT/Da+9/KufNn + +PoTzzIzPYVpmdGQYD/AD3wMw4jiV2pAXlr7qtXrJ/qoaEARPdeqgf9lb7R9705aH4BhsNVr0gzGIM9g + K8jyKHbNI9j7KcHSCKOAzCaf+EIBfS9DK+K+axL7W1b9nnwNeG77MWFH+rmMyFJPZpMfIFQqWvDD8bhy + 6lRE/vaIQLNtEA4dfzk33/UG8vk8uXyJxeUKszMH+Px/fw+Hb7ibw8fvYHxyLlpSTDOxLCvqUjxoMFBi + v9VjoPvv3Lryd8XdXwm422qehmGa9tLCZOVtM4MyyBD0Htv0Ie55JWBbsWMCilSVS5C7j6TDdgQh43av + v4q+/nx1ECoVLRDqBTT9JtVqFRVG5Xhd1yNCm9EagZ5qUndCxFoNlOLl99xB6C2ycP5vWbr4RZQyyJXm + KE8cJWd62GY0s4/oHQyUKlg976zqFAEGT4aahWRFYldi1wKGNUStcL0kHvba5DWt7eS5PuzjugBp+0nV + 7w9zLVT0XUv9+LNPbm4MW080mpIvKovPH5jH81x8PyAIfBzHxfd9XNdlI1SE4VI837+G1CR33n4zczN5 + xsYsbFMjDCEMVzH0dZ756tPcfvM4piGRoj/l/iOJQO3hwIrB9zkIrTjayCpLb9W12EoGdurmt5CVz0Ge + wGYeQup9730/gC5Vb5/t5CdT9dPIP5zKtV/3TVU/Lc7OyeFUPyMONSCOQQap79TOVV8lvgUgBZiGpFbd + IJcvYVla1HlHyLhyL8BxHITQcJwmjtPE96PKvumJPPNzeUoFE02L1gbUpCSfMxgrm9i2Hq8NmP7+Kjpd + PBTxe9KTx+57G5KvisiL6DzbQWTcrHPQZimlKfOga9Ncoq22EOxkOyuN/VwctPdAGvGjbdV3vj/MjlQf + eClX9HVf0Z22FGDokkLepLbwKb5zJaDh2ej2OIXSHGOT8xSKY3z5cx9henqaG+96E3PzB3CaTTRdJ2fr + jJUspiZy7dl/hVAYusS29fakIGloV+10vjoZUyFKpXQEGtoAqJ5nPlQl4E4qCbdybZbRGJawWyF8UvXT + 0u+6j32cFTh5pEWi7mPtvRd1P/6MOLak+ilpb3Yu1Y70GzwhBaapUS4a1Bsuc16DysYaTecCG5e/wdXn + PSpVh5tufzN33X0366c/y3L1bmaO3osmBaHqTPppGBJdi0YDGrqMZvfVk8uDpd16vBFPCqqIRgP2jxLt + 5HkYdDUDxrc69MXdWX0hmg+ziJxVB9BL8iyPgUS41HP70BEo2ulsxq9mT1ff3vOdSBLbQ6l+ynWpcWfE + sWnaGXGo/hiyyb8bqt+br95Q6WlLKcjZOmFoAVDKG9SbPp4X0Gj6rG84LCzVeO0Db+HwkWNc0hZYW77M + ueef4M47bsacfJDJeRNdXaFeX0JKD8vSAA1dF0gl+/PS/uq8xwKZeEdaG4mBAdvoCNR5/gq2R8adqPtu + NDluZkSyjMGg4kLvflce96EfgKJdzu8erhlvtV5UtQvkH5aAqTGkpMuAPPUmMazHsZ+q33+dJgUYURFA + 1wSlgonnhwRBSK3uUVhrEIQKL4CrV5dwC68gl1PMnPs8j3/jSSbnbiY/f5xyqYCUArwVGivfwG+cot5Y + I5+L5wiIKxrT70PRXiG4i7TxO7pl8kM0n0CX7rSGAw/qcLMZ2TcLk+jMkHo8K/ywx5PYzDikhUt6Cqkv + 1L4uDBLlrPOHp6t+O2+d7dRHo3ouHZaA3XHsVPUz4xhkkPpO7a3q98ajCYEwJLpmtqfkUgps2yVUirVK + k6tXFzGMdVzXQdM0wtxt4LjkcjlWVteo1hpRk6FuoNmv5eiJN/PsV385Ir8mMIW2yX0ljySa8Aa+CwPQ + 1wFpU7KkKedWkXXtVsk6SLG3c31auNZv14PapwlBkmkOIndPuD1U/W7iDiLZLqt+3+7eq373BardQpMk + PyIqHkTzBeosLS1Hc/bFff1lvAR4vV5nfb0SLwumo+s6um5w6PAh97c2SgAAIABJREFUKhsu+ZxBztJB + T8tDQuFFssIvJn7YO5Hv8J5AdAthYi+TEGnl62ET2mkz4k6MzTBx9Kp/0kPpGy8N++UBpAifSG3+S+wP + Iv+OVH8AcVMNUn/Wdkf1U9LODDgE+YdKOyK+70cdgRwnwPHDaP0/pXDcgErVodn0mDo8hed5hGEY/QYB + gR/gOtGcAUJEy4IJKTB0A6fZpFr38LwgnuQzIw9x3Y9A0u7q2y7+7YRf8fvduXyrHsBW3PC97E8A3cQd + VBG4WSVhi/ip5Id9WRosmZ/EuXaWUwiYauOujwk7hjWGvc17g8nf2Q6VwvUCNmoud77xg9Qrl1m9+hSV + pWdouufQ5TK65jI2VkaFiqZTp1pZo7KxyMzBm+OZg6MRga1f13Xb04oHQXIeCFLzAICQiVegFT7luqF5 + 1vfibGYAej2A3rcujeS9YffKEGzHU+i1oEniZ+ZxX3oCqsR2NkE3Uf04yGB13I7qw84r+nZD9TPODVL9 + 9uaw9xYtDNJ0fFbXm3znu09TKOTJT97L4QOvxTRMLMtg9eop/vgzf4mhWXzuv/wmd77stfzE//IuTj13 + muWVGoVCHoAgCNrrAxqGEfcMbBmArPy19pMVflkGYAvcSnqULSejg80qAgeFSUOWIdhOsWKYdHqRZSBa + eQriz6Z52NMigKCrYafnbJb69oZjCJd/j1S/HWRYo7PXqj8o7Z79jGfih4qm61OtuqyfPo1pmhi6gWmZ + mKaBYZgUCgVstcSVCwv8yI/9DK/+3gfRrTFO3HCcv/zLD3PTzbczMTmJbdvIuLLPNM14INBmhjE+JmT7 + SOdc2BM25RYHQYUo2pWPu6XQg0i9GUEHGZxhkEX0tObDJPEzXf5e7GklYHtc+CCFTe0qHIXbqer3eR99 + Gcw6l0wiy+j0Xnftqn4ylFIhvhfScHwWLlyMp/uKBwHpJoahY1kmB268n+N32OiGzuWrqyhWOXJohrNP + fZorz/8V5YljTM4eZ2ruGIV8merGcXS9tYpQWh5632XRDhJVSmb1BBwWYc9/ldkTLknMLIINyngv0toy + t6rcwyLLywjoJv/Q2Kf5ALL2s6+9LvvxpxzemuqnnU/aOYWK3fQgCCiPlXFdlyAIqLs1fL/SrtzTdA1d + iwxDa2mwA3PjzM8WKOR1hLxEY+k8T5/1CChx5qnP8T136eia7HnD056vivoBiNb5LCO4VbHsMwA7JVwy + nkEJi03C7lbRoNfYhIAff4ZW/ST2tAjQ2RpEwN7rOs1S2eS/BlW/b3eL5E/Nzu6ofjJ+IaLlwaQUTIyX + UUiUUu3yfDQ60KfZqON5Hs2mQxgGQLR02OxMnumJHJapE4Qhvh+iSYFlrVAsFDANGXU2yrjPzn/b6grc + Ctd6n9PudRjE13cuG8YFH9ZF325dwWbYbpEgSfwW+beFfeoK3HO8vd39QnfPALN1VXgpTNjR2R2S/EOo + fvKoEPHKPpbO5ef+llCfRzNzWFaOfL5IPmcjpGB96TQTUwcwzCIhGp7nkbOLTJRtZqfylEsmQgh8P0QA + pikpFkxsK2M0oAIlEnLcZyPidzu+n2wjnoF2UbL94m1Wo8+A82ll7N3sV7CVIkgSisjN9+LPUBV9g7CP + 6wKojNvtJX7vda3NYVW/57rNVL8dJIOo+676g9Lu2R9S9ZNHtdbSYHmDavUpLl38MqtrIV5ooFvj2KV5 + wiCkvrFCffUMthFw4KaTFMZnovoCXWLbGqViRPYwiMRH0wSmoWHoomc+gN5sJoW0td9qw4/eky2TP75K + 0DWjwE5d/1Ycw2ai1wjsJJ20dJPE98iW2C1hD4sAyTH9naP94UiZJSixv23Vp0/9UhMfFMemypsSWerJ + XVZ92Bb5Iap8tyyNcsnC9QIQIcWSQ63uUW9cobJ4lkYz4E0/+m+46cabuPjVX2dp6Qkc5w7C8FZq9QDX + UXhuiKmH6LpE1ySaJtqf1qpAmXY9yklPFrdP/oyHsBsGoIXdbt7rjbd3P2lEFJGb78afHat+Evs3H0Df + C5lC8C4XTqSQoDuO4VQ/Iw41II5NlXdQ3C+k6vee749HkwLL0CiXDKTMUywYNB2fZtOnWvdYXmuyvNJg + fHyMZrPB+I3/gNz6M5x+7jGWl1/G7I0/ztjEBr53lvWNRUwjwDQDbEtiCQ3ZmhSw61ZTVF8IosVAk8qf + 9rJsRYC7rt8NA9Cr5oPUfadNfmkI6BDfY5sVfYOwD5OCQu8f2v2OZynsoPt8sVX0pat+Xz72SPW7PAAh + 0A1BTkQ19oW8ge+HeH7ARtXDNDSEgqWlFSqVDYqlMbTx7+XwTdOcOXeF2YM3UpiYoVAsYpg2tYWv0lh+ + nKB+njBskrN10MXAikAgWhi0fUuJ+QAG/XcDkTQAURJbjCAN2ymr70ZLgCIivBN//CGu2Rb2eU7ARPOe + yCCxgnYzUWpsA0g2lOoPIv+wqp+S9mbnUu3IVtLuv59hVb93XwBSExhE7ntgKlSoo0mB5wVsVB0WFhaQ + UpLLrcUThZbRZEBlo0rTcTEMPZ5EdBZz+ke54dg8p77wzxFCkJcSrW9WoMRzEN3HOp0Bt6P8idDdl2zX + AAzq8DNMj8Jh4ss6r4hUvklEfJc9UP0k9m1psG5eZynisKrfG3YI5VD9MXTnY7dVvzdfvaGy0u7Z37bq + p+dNKUUQRgOCPE/hhwGoqGmwNYhHCkk+n8fzPKrVKgiBIOrgYxgGmqahaTqGocVrBOocPjTL2oaDZevY + 8QQhfflokz9NUJPHtvG+9z/H7VbIDbqm1wgMc82w6SkiwrfI36ro21Psy+KgA4nb/sm+11FFX0Y8meTP + zluoFJ4XUG/65Ip3UV+7SLNZIQw9PNdno+pSa3gcuGkaASwsXMVzXer1KpZdxHGc9n8rZDSRqJSSZqNB + reHheT5haHSn3Uq+p29H5+3YvvL3Xqc6z3Q3KwFb2IknkBWXInLxm0CDTkXfvmAfJwXt/ZOHUd/rc8KO + nVb0ZZ9rDQYKWK84HH/dz3FYh9rqArXKWdavfhcWnqPpnMGprRCGggvPfo18ocTRG25EmmP4gcLzvPZI + wNYnDAJcN4hHA/bea0peUot4uyp4WQnspIdgGvkHdf3dDCGR2jeIDMC+qH4SL9y6AO2XJOt+r8WKvhen + 6icRJJYG+8bjXyWXz1PIF8nljzN/210cvSfHHfUVPvUH7+fy1VWWLj3Hy191Pz/2tp/iq195jFPPX2Js + bBxNlwTxegJBEGKaJmF7gpEQVEoRIG2/63/cqWj33XOyTLEToqbFl+YBJMNlGYtWeJeI+HUiI7CnZf0s + 7FMrQPto50dk36vqehmyVL/3XPLQi3fCjswcbLGiLzUeRXtCkHrDY+n0WXRdj0YCGka8PqCFncsxOXsD + dmmOB3/w7Rw8fIIz5y5w5PAx/uD/+SDHb7yV+UMnKJcnMfNFVBhi5/JRF+MuSqT9x4OedxqG5URfvIM8 + gJ1gJ3EoIvfeAWpEBmBHXXl3iv1fHPS6mbCjK1OD49zlir6+eBLPLQw7MwKtra9HsiiiUXy6HlXumaZJ + oXyC2dkcDU/wzLNnEITcc/cdVBa/xtMb3+bM0xPkSwcYmzhAaXyeQ/OT0bTgMpomfHjVH/w+bA8K+tU/ + K+Be1BVkpeUREb9O5PK/IKqfxL60ArTrfrpmAup9dXtd/iyCbIf8u6H6GecGqX57c5Dh2S75h1f9rLAH + 5g8SBB6O4+C6Dr4f4HkNavU6GxtVlFLRJJ9SIjXJbbfcyPxMgbGyiWm4+MEZmivPUl+2+G8LX+Le2w10 + XXbPCZDMW+uwGIZ32+dFfOWgZoa0S3ZSN7AZQiK1rxKRv9Wp5wXH3hcBhARao0BktK+6J34Yrm0/44VQ + /X5DtvJeR6qfkQcpo8FAhiEJ3AqaVaKcK6AJgSJEhSGe57N49Tx2roxC4DhNAschCAOmJ3PMz0Q9CEHh + BwopIJ/zKBejNQM7C4P057vvSGr2N7+/IZBF6L0mexocYJ2I/LvalXen2AcPQIDQ4t9oXHm0Hf2qqIWZ + wSTbZZf/OlZ9IUDXJTlL59K5P2Vpw8Qnh26WyRUmyOVLFArjXHz+7zl89GYK5VnGpqfxgwBd0ykWDCYn + bMbLJqautfsO6JqgkNfb6wzu0H3PvL9Nr+5cspN2+t7wvUZjK/EpItJve8z+XmJ/OgIJHUIVkV7qJJeC + FkIiWgNDVFxRq6C7mbQH8Quu+g8OobwZx/v2N3Fju45uJe0M0gK7VdE3KKwUAtuUlIsmtfoGnlenUnGp + VwLWL0scT+AHOgeOvoK8rVE//2eoA6+jPHs7mqYhhEKTYJsahbwRsUBEowENPR4U1DsccGj03/8O2TJM + 2X+YokFavMNmTQI5YIyod1SDa8gL2DMD8OM/9wfrH/vNh377S3/37X8yPTPO5NQG42PFiOwijL0BAVID + qSOUQggZPXEhUYkRZe2Nnah+3+5uuPz7ofqb5a03+sF5kBIsU2O8bAGKUtGgVvdoNn1qDY/VNYfFlQqv + ev2PcPToUS5/y+fqyjLL5x/Hv/tmmt4kXmDTdEKk9DENGU0FJqJhwHJT8qvEVtLA93Iy014OiDYZVx/B + WyeHyWBvmKyOP8N6AhaRITCBDaKKQJfh727PsKcewEM/97F/+rHfeOj3lxbWTwJvEEK8eWZ2nKmZMSan + CkyMLyGkjZDROnFC6FFxQRjxGvZBXGEU9yoUrbkDWs980Fpy14jLfw2ofvK8JiWGAQUBmmZTKpq4boDn + hVRqLoV8I2rO0wRLy4vYR36Y+bHTnH/6Mc6eu8DRe97BwYNjCPcsXv0svreM9CpYepOcLbGEiP7Cvo4+ + PVzJvO9e8m/JApAwppvVAaRdnFVsSJI/GWYrnoBBxDdJZ0affevxl4U9LwI89PMfewx4DPg/PvYbD2lX + r6zcv3B19SRwUkr5xtm5caZmykxMFBgf3yDyJ632bLNKBaiwU4QSsWoo4mKEgqhIkVFk2HXVT4TdlHh7 + rPrbykPkBei6JCd0TEMR2DphqLBMDaUU1ZrLpcuX45V/DHTTpnjwe7h46Qqlcpm6q2Hbd2GOvRrLsvGq + ZzCDZ6kufw4A29aQfVwaeCtdJ7dO/kz0Kn8vgQcp+E47DWXFmafTAeilbwCSeOjnPxYAfx1/+NhvPGRe + vrT8hiuXV04qpd5gGPrrZufGmZouMTlVoFS0I29ANxFooAKU8iH0AB/CaEILkPE/FRuC1F5I+6j6sAPy + 75Lqp+YhTl2peP6/ED9QbRpIDUwjWuZ7bXUNiJQ8mjnYRNcknr9KpVLBMKJpxKMRgSYvv+d/4tunPxNf + L+OxQGnpZ3EqysQukr+FrLL+Zt7BoPC9HsEgQ5KG3TYs28a+GoBePPTzH3OBP4s//N6v/cPSxYvLD1y4 + sPRGIcSbLEu/d2Z2nMmpItMzJfJ5Eyl0kHZUnR0qQuWAciOjoOJh00rFy4/Hn62QbU9Vv/f8Xql+dlxB + GBG/0fBRzNFsbOB6VVTo4QUhlQ2XpuMxcWAc1/UIAhWv/uPR8FvLgoGUWjwqUEPXNW675WaqdY9S0SSf + 0/vz1zfEW6V/p+T9Pz62wqy1yFzB58BEicOzR7OeTPJ7ENmTxIV+Eg8yBIOwmUEI6e4E9ILjBTUAvfip + d/2/G8Bn3vezhz/7nt+5oH7v1942deHcwsnzZxfuF0L8UC5n3jw9M8bMbJmJyQK5nIkQOkKzo7qDMCAM + GxC6EDpRn/S26rS8gtb/M9wiFPs9YUdmRrqi30oeuvfDEJrNgLUNhzte9zDKb+LUF6lXLrK2fAYllmnW + z2EXykxOWpw7/RQblXWECJmcPd5eGqz16zouSoDruTSa0QCh4VYG6s5vFvkBasEkz9cnea4GLADfDZkx + zjOXrzI/ZnBkZi7tcaVV/G2V8GmE3q7qe0QdgdbptAS84LimDEAL7/mdCwrgp971iWXgj9/3s4c/BfzC + kZvvmz/fcO47f3bhBxDiTfm8eWJmdpyZuTLj4za2ZSGEAYaNREMpDxW6qLCJChJzK6gQ2ivItMjf/ZJu + yeXPcFm3rvoD4tmy6qfsKwiCkKbrs77hsFyzsO0xzMljTB+4j3JjBalcbq1c5LN/8WVqtTpf/pv/xOvu + fwuvef2DVJuwuLSK1CRhqOIpxKM1AjVN4vshQRh1N+6/763eX9Jrif8p1fpILgbHuNAAtQTqWfiZA6lm + M434rf3NxgpsleBZUESqv0FE/iZRhdY1gWvSAPSiZRDg45ff97OHPwn8J4DDt9x3sHbm6skzZxbeApws + Fu0js3NjTM+UGB/PYZpW5CHoEwhDi4yBclBBE9UqMrReUtGZmXb3XP5Bqt+7vxOXfwgjkuCk7wfUGx5P + PvmNaACQZWHbNrlcnlw+Ty53A5bxGKGl8X1v/Yfc+6r7OHHLy1hauMgXH/sSt9z+MoolG0HUOhMGAZZp + dSc/RJ7aj6f9ld6DMIgWLyZsGQCSxiA63h0hsLkH0Ls/DOHTwgy6ziVS/bX495rpAtzCi8IAJBEbAwXw + vp/9/EXgD4E/fM/vXFS//+s/fry60XzT889e/QHgZLmcm5ubH2dqqsDYuB0bBBNh5BDSiA1BExU4qLAZ + vU2iNb10/HZ1eXz75/IPRfzUPPSrfvJIGIYEgcJ1A1YvXUIIia7r6IbeGRFoWZSmjnOgWOTAwRuo16s8 + +a0nOXHsCF//4ic4/8zfMjV7lOm540zPH6NUnqJQHIsWBRECmTUdWNr+IM8qhh8kyJ8gfbcBSI0ji/it + Y9up8Ou9kayyfgOoEJG/NervmsOLzgAk8Z7fudj1tvzkL/zhaeDDj/7soY8A6vCNr7+9Umk8eAreBJwc + m8hPHJgbZ3IqT7lsYxgmQloIvYAQZscg+A0Im1EfBNHyCmID0G5hyCAtbMHl31vV7yZ/95ZSimKxhO97 + +H5As9GkXqsDcXdhw2R5dYPzF660lwk7duQQ0+MeBeMyzbUFzq08zvnvWhh2kUtPf5obprR4MFDWWIC+ + TG1+D4DnR2d6Sd/nAWTGm9omuRn504xGb5i0Yx6Ru78a/7bKntckXtQGIAuP/M7FEODRh//uO8B3gP8b + UEdueeDu9dX6m4GTwP3T06Xy9GyZyckcY2M2mmZFBsEqIISBChqosIEKGhA6xK8hAKq9jFVrP/7egepD + i9e7q/pdfrGIOl7rmiQ3No1pSFQYEKoQp9nA9308z0NqJp7n02zW8X0PpRSu6zI/XWBqwiKX09tZMXUP + Xf8upWIR29LihUFS8thHm97nk8I1BV6QTvrkb49tzDYF8WNIXDFMBWEyXFqGWzloEJXz14h6+10zXX6z + 8JI0AC088oG2hxD//tE3gW8C7wf4vV97++sWlypvAu4TQrx5drbM1FSJyakcY+M2UhoILYcwygj0yCAE + 9ai4EDaiN601urFP9ffA5R/CXc5S/da+EApDl1imxvKZP6PqTaLbZfKlcUrlaYq2htQkT3zpM5h2nqM3 + vRqlAur1KpqUTIxbHJgtUC6ZGLokCKK1AU1To1QwMA2J1OhLN438baOZxZH4fj0/nfRdBmDwU0lCJH5V + yvHNigbQbwx8Ind/Nf5tzfBzzeMlbQA2w0+96+NffPThQ48BzB5+mXY1CE9evbL+euCklOJNs3NlpqeL + TE7kKY/bCGki9CJCTiCEhvIbqLBG6NciD0ERVSaqENV6Rwb1UoyxG817/UHSw2tCYOgaxYLBRvXbeCsO + CxUXx1WEoY6vdITMQeBz9MgB1s+uUph/FXNzBzEMI1pWrGgwOWZjW60BXdEkIKYh2uMCsvPdof4g7ieN + necPVv++VsfhVXenRQNFpPRrdFR/z+bw3wtc1wYAkl7CRR/4i0cfPviXj3zg0r/9vV/7R+aVS2tvuHJ5 + /QHgpKFr983MlSKDMJmLasGFidAK6MY0CIkKaii/Shi0DEL8ziTrEBJv/VCqDzty+XspITWBZUnGSiae + H2KaislxQdPxqTd9Nqp1NjZWuf+tv8CNx49y6vPvp7oMS+oBwjBu//dCwjCMOwNJpKC9LFi0lLhIyxSq + YxYHo+eZuMFg8od9dFPbUd+tthS0aviXidz+F43qJ3HdG4BePPKBSwrgp971R+6jDx/880c+cOnPHn34 + oDx44+sKFy/6Jy9eXHsAeJNl6a+cmSkxNV1geipPoWCAsBFGGcOKOqaEfg0VbBB6NVAtgxAVFzo1B4Nc + /t1R/c7haPIO29RQBRMpoVw0cN0A1wup1l2WV5s444KjN95NrlTm+Pc8zNrSOc489zdcvnQQWXwNekHS + DNYINiroWhPL1DGMqFiRsZ7LYKHvO9BtwHqLAL3bfR6U2lEnm2FaA+pE7v4qkRF4Ual+EiMDMAAtY/DI + By6Fjz78xeojH7j0p48+fPCzgDp8y8mJCxdWT164sHof8EO5nHHr9HSRmZkik1M5craJkDbSmECzDkaO + gFch8KuEXgWh3E6Rgd6FVFMKtltS/bQA0Y4QAk2X5AWYhoUfRM2CnhdSqeqoEIK8wcLCImurq2jGDLKU + Z/aIzuLyBifu+iGmpyaQagP8VZS3ite8iO+eJwyXyVk6Im4OTMtVel6Th/u9lzQDoOgtAqjkVbtFxl6X + 3yWq2V+h06nnmujRt12MDMCQSBqD6MgfrQCfevThg//5kQ9cetdHf/VtB86fd+87f371+4Dvz+eN4zMz + JebmSoyNG1EvRZlDM6fQcochDFD+BqFfIQw24rEMCkTYHvoMxN7sIPJvrvq956UANIEho7H8KPDNED8I + sW0NpdmcO38O0zCQUmKaNoYxh2q65BAsLoVYloVhzKNbRzHy9zI7HnL+6/8GKUR7UpBM8qedyCA/dFoB + +tSf/jqAZOlql9Cr+it0OvW8KFU/iZEB2CFahuGnf/ETlx99+OAngU8+8oFL6iP/548dqdecB86eWf5B + 4L5i0To6NxcVGcbHo842QubRrFl0/Rgq9FHeOqG/QehVolGPBIDWqT9Q4eYu/xBFgiDuDOT7YXtKr9ZM + wa4XUDB0vIaH67gEQdAeESg1ia5r6JqBbugYuo4Rdx46ePBVrG+42JaGZWkpL1bsQXc51YqOmej1tjtb + //rWd3Kmditnazdzunorz1dva5O/fcvdTY+7ScxkWX+Nl4DqJzEyALuIljEAeMe/+uT5Rx8+8IePfODy + xwA++qtvP75Rdb7/2eeW3gScLJet+bm5MtPTecbLJoZpIbQCmj2HXjgBoUPgrhMGFZS3EY1rIJpxtzPI + KSZUu09CK/VMiY2XBgtpOj5ox6hVF/GcCp7vUW24rK07TM4bzM7O4vseQSij1X/CgEZtg6bno1QTpVS0 + 0rCuI6WkcWeDesPH9cLOWIBkXnqr1kjWgKQrf+vchLnIhLHIveOfj+9B8nz1Np6v3sqZ2q08t3FrHHfb + gOxmH/41ItXf4CWi+kmMDMAe4pEPXG6/LD/9ix8/DXww/vDRX337rZXK4vefOhX1UpwYz03OzZeiTkll + E8OwkHoBwzgIhTwqaEaegb+O71cgjOudRIhS8cQoYvO6gzBQOG5Apepx+33/FN+LWi7Wls6wtnAawTPo + mouumxSLZb7wuT+i2WiyurLA/W9+CN2w8D2fMAzaowI9zyMIfZpuQBCqtEq5zPxk5bXLMPQUD6QIuan0 + FDeVnkJIHaGZQAEVQhgESimcAYkMg1ZvvhUit/8lpfpJjAzAC4Sf/sWPP/3ozxx45pEPXv6/AD76q2+/ + e3Wt8RaiXor3zUwXxmZmCky0DIJug15Eyx3G0IqEfpXQXyfw1gm9CtH7GYJIrs0XxmLYIU+oYgOw4XL+ + ShXDsrDMA9gz8xyafTXH7vZRXpXvfubTLCyu8dTjf8XLX/la/sm/+DWeP32GpqchpCAMIgPQGg2oG0a0 + NFjYWRR2eOK3zicrDtPJnw4BQhCGEtdzwiBQ1U0uyEJrae5WWf8lqfpJjAzAC4hHPtjlITwJPEncS/Gj + 73/79y4u1h6k3UuxyPRUnslJm7ExA03PI7QCRv4YUi8SepXIIPhrhG4FVECriKBaxgAVD+MNqTVcnjl1 + CtM0MI14RGAuh2lZmLqF51QxdcWP/eS/4LY7X8HM/DGE0Pjd3/0gt9/1GsYmJsnlLKQUKAW2acWLiMSe + +JDk720r2BL5RUR8ITQQGoHScN1GGPisDvkXJHHdqH4SIwNwjeKn3/3x1lyKfOT9b9euXN04eeXqxv3A + SU2Tb5yZKTAznWdy0mSsZCGNAkLmMfM3IkoFAm8tanb0Vgm89XaxWKlozL7rhixeutQuxxtGq0LPwrJM + Zo68koOmQak0gRtaPP3MMxw7fIDT3/4sl577a3LFKcYnjzN3+BYmZ48QKrBNiSZFT2ef7PqI9KMJ8guI + 5tCM147oLdkLEfXOFAZCGHiOpFZdd+pucG4Lj/q6U/0kRgbgRYB3vPvjAfCX8YePvP9tuStXKvdduVI5 + CTxo6PK1M7PF2CBYlIoGUishtAKmdTNoeUJvjdBbQ/hXCGkShJLxqbG2C+84Lo1GE9ggDEMMw0DTNVZW + q52a/vkZZqdNSkWBpq3hO4+z+Nw3WDqtc/brH+Q1L8th6FqnJ2AfVPfxvs0W+UXs1bfcCS3ivki6F5Hy + C2EgdBup2dRrsLRwsbq85n9ryEfrExG+VcN/zczUs18YGYAXId7x7k80gD+PP+/9yPvfVrx0qXLy0qXI + IFiW9oqZmSJTUzmmp6xoLkWtgNRL2MU7yE8dx157DnNiBk0TqDCI1wb049GAPqECz3NpNptsbGwA4Lke + 8zN5piZsCnkdIQRKRYOLbFOjXDKxTInWNx8AdDXzJSsJRZL8IiZ/3J1Y6FEln9BiN1/GUYiOYZAGUrNo + NgVra4Kzzz219M3nmt/Y5BG2VH+NiPzXleonMTIALwG8492fqAKfiT985P1vm7xwYf3khQvr9wE/nMvp + t0xPF5iZyZPPC1TDx9BcVp//Eza8CTR1SZlKAAAF4ElEQVR7hsnZo+i6hW0XkJrg1Le+THlyjplDR6hX + N2g06gghGCtbzM3kGS+Z6Lpoz/VpGRr5nIZlSkTX4iApih8vDqpU0H0itgFCiHiuRxMhTdAMUDpeIEBJ + TEtHCvBDjWZTo1GHlRXBV7/y5S9cXHIf//qpxpUBj6ul+q2y/nWn+kmMDMBLEO949ydWgE/Fn3d95N+9 + bf78ufX7zp9b/37PC9+MDI8hBecvP8PlxSoSHV23mJm7gUJpEjdQLJ55nMnJAqtTJzh0833Mzs5iGAZ5 + 22CsZDI5YWObGmEY9UGQknhpMJlYGqyH/MnOOkKkzpsikLHyGwjNwvUtlhckG1XpNBrhYqi0oFDIHxEC + 6breylpl49TFi1ef/du/+evvNl3P/dTfrH8l47EoIrK32vUrXKeqn8TIAFwHeMcvfeIK8Mn4o7/z7a+5 + qZC33qyUeqsI5Ksc35uqNZqsrD1OqBQTpRxzU0XuOFHG0tdZXvgWyFejwhA7l8eyTHK2Rd6WRAOborK9 + UIm+OKll/ZYViMrvvu8nWgxUfCou20uDEItLl0we//tvnvnmN7/5peVK8Fy9GVaCUPmOq5ymGzYrtaBa + bYROpRY6C6t+I+MRjFQ/AyMDcP0h/NDHv3zGMsTvO576z8DYPXccvOWGQxMPlArWK4Um7nKcsHxlucbK + 184wOWZTLi6zvHyZO2+eojx7N6XZGeyiiy6rKG8ZGXaPdGwPbAqhu+Ivsg5CswlDlyDw0TU65fp4+Xgh + NdBM1tcMnn7qGf/p7zz5xG99cuk3owBo8WcYBHRm6Wn14b8m1uS7VjAyANcfFOA5nqoRucD1J566tPLE + U5eeIFq80n7Ny4/ecvzI5H35nHmv5zh3r1f8omFW+dNP/jr33lECp4oszKDnDqCVb0Mpj9BZJnCXCb3V + aC6EUIHsNghRU55EMycIvXV8XyE1opp8GVf4SSNq2tMsNqomly8+q11Y9H4buED0vlqAHf/q8UfQPetK + QDQ+vzUPf4XrpF1/qxgZgOsPrV5BLh2i6ESqagD6l79x7sqXv3HuMU1iByHWg6+/6c4TRyZfK8KVe7/u + O/csL3mlQ4cbzE4vMj5moOkmmj6OXjiC1O5CBQ1CdyXqlOSsRhOsEk0HIrUcRuFGvNrzuG6AYWhIzUJo + BlJrlf1tpGbhugZOY0PcdcL66zifrTzaREtu20RGq5V/ReTuO0Sz89ToEH+k+ikYGYDrEy0yBPHHo+2D + tz9aEKIDxl/83bNX/wL+ishwOP/7L/3gyysb7gOndPm9QvCW2dkC01MrTI5bjJcNpG4hjTK6fRBZug2B + ThhUo0VbpI7vXEWFddbXHSbHLISeRxomQus06wWBpF6vEYbhufd+8KrTk98mkbJrdMjfMgCt+4n7Ro8w + CCMDcH2jtx6+NYFhCy2joCWOh//63332C8DfAOq3HrnfCIP5B65cqd0HnNQ08cbZmXxkECZMxso6CoXU + 8lGzX+ggpMHaukNlvcqhA+OE5JBGDk3TEVLih4rFq3UunjkFii8MyLvPNTrf/osFuzFscoSXNrLekT6X + +lfeOaeNzd5StAvT9wkhTyLkA5omXzMzk2d8zKJYNEBBveHx/Ol11pcWnpiaObg+NV2eKxWNY7qhbE36 + OA3fOX/ufPPpb30tAN703g9dfWJvb/H6xcgAjLCr+JV3zrUq5Awgb1jFcmnq+ANSM14hhHaLUmHOc+ve + +tKFL6xvGH8FYd3UnOWc1dQnpuZvEVK7eWXxwjRRJd+H3/uhq8+8oDf0EsfIAIywp/iVd84ZRBV2ZaJK + OwlU3vuhq1fe/dAdAsDQXC1vbSg6tfzaez90dTsj+kYYYYQRRhhhhBFGGGGEEUYYYYQRRhhhhBFGGGGE + EUYYYYQRRhhhhBFGGGGEEUYYYYQRRhhhhBFGGGGEEUYYYYQRRhhhhBFGGGGEEUYYYYQRRhhhhBFGGGGE + EUYYYYQRRhhhhBFGGGGEEUYYYYQRRhhhhBFGGGGEEa4Z/P8GlX/ISWG3BgAAAABJRU5ErkJggigAAAAw + AAAAYAAAAAEAIAAAAAAAgCUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARz87AW+NmgFx + zfEYds3ubXXM7sNzzPD1fcvq8IbE3MFxpblkAAAAHAAAABkAAAASAAAACQAAAAMAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHbO8Q9t + udZtXIqb0HGrw/hsxen/cs/v/3TX9f912/b/dc/x/5De8P+BvdTJAAAAOAAAADYAAAAwAAAAJAAAABQA + AAAHAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfdHyBn/P711+ + z++xe8/w63fQ8f+YtcH/tJ6U/4WSl/9Zcn//bcXe/3fe9/933vf/cc3x/3nc8P93zOb/dsnk/3O71NRe + ipqBAAAAOwAAAC8AAAAdAAAADAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhtHvWITR76uC + 0fDnftHy/33W9P992fX/ft33/37h+P9+udP/lcTY/7mkmf+xrq//dNHr/3je9/943vf/cc3x/2zX7/90 + 2u//idjk/4XX5/+BvdTQAAAAQAAAAD8AAAA2AAAAJgAAABQAAAAGAAAAAQAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACM0+5Ii9Tvp4nT8OWH + 1PL+hdj0/4Tb9f+D3vf/g+P4/4Li+P+B4vj/gOL4/3/h+P92p7z/qpGD/4icqP9qrsz/c9Lt/3rf9/95 + 3/f/cc3x/2LT7f+dxL///6Fj/53Q0P+Mzub/AAAAQAAAAEAAAAA/AAAAOQAAACsAAAAXAAAACAAAAAEA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJLV7j6V1/Glkdbx4pDW8v2O + 2vT/jN32/4rh9/+J5Pn/iOX5/4bk+f+F5Pn/hOP5/4Pj+P+C4/j/geL4/4Dh+P+hvsj/xsLA/66kn/+G + gH7/ccfh/3vf9/963/f/cc3x/13Q7P+bycb//7V//5vU1v+Nz+f/AAAAPwAAAD0AAAA7AAAAOQAAADQA + AAAlAAAADwAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAltfw1Jfc9f+b + 4Pj/kOL4/4/n+v+N5/r/jOf6/4vm+v+K5vn/ieX5/4jl+f+H5fn/huT5/4Xk+f+E4/n/g+P4/4Lh+P9y + rcX/nbC3/6Wgnv+my9z/d9Tw/3zg9/984Pf/cs3x/1vQ7P+V2eP/6u/p/5nk9P+Nz+f/AAAAOwAAADkA + AAA2AAAANAAAADEAAAAsAAAAGQAAAAYAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAnNv0/5jr+/+S4vz/kOj6/5Do+v+P6Pr/juj6/43n+v+M5/r/i+b5/4rm+f+I5fn/h+X5/4bk+f+F + 5Pn/hOP5/4Li+P+PsLr/wJ+S/4yYnv9RdYb/csrl/33g+P994Pf/c83x/1rP7P+Y4vP//v7+/5jk9P+O + 0Oj/AAAANgAAADQAAAAxAAAALgAAACwAAAApAAAAHwAAABEAAAAGAAAAAQAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAndz0/5vs+/+M3vz/kun7/5Lp+v+R6fr/kOj6/47o+v+N5/r/jOf6/4vm+v+K + 5vn/ieX5/4jl+f+H5Pn/huT5/4Tj+P94tM7/sc/a/7Gfl/+pqqr/d8/q/3/h+P9+4fj/c87x/1vQ7P+Z + 4vP//////5jj9P+O0ej/AAAAMQAAAC4AAAArAAAAKQAAACYAAAAiAAAAHwAAABgAAAAPAAAABwAAAAIA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAn931/5zs+/+M3vz/lOn7/5Pq+/+S6fv/ken6/5Do+v+P + 6Pr/juf6/43n+v+M5vr/iub5/4nm+f+I5fn/h+X5/4Xj+P94p7v/kpKS/4udpf9yu9n/edXx/4Di+P9/ + 4fj/dM7x/13Q7f+Z4/P//////5nk9P+P0un/AAAALAAAACkAAAAlAAAAIwAAACAAAAAcAAAAGQAAABYA + AAASAAAADAAAAAYAAAACAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAod31/53s+/+N3vz/ler7/5Xq+/+U + 6vv/k+r7/5Lp+v+Q6fr/j+j6/47o+v+N5/r/jOf6/4vm+v+K5vn/ieX5/4fk+P+Rt8b/xcPA/6ihnf91 + dHb/dcrk/4Hi+P+A4vj/dc7x/1/Q7f+b4/T//////5nk9P+P0+r/AAAAJQAAACIAAAAfAAAAHAAAABkA + AAAWAAAAEwAAABAAAAANAAAACwAAAAcAAAAEAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAo971/5/t/P+O + 3vz/l+v7/5fr+/+V6/v/lOr7/5Pq+/+S6fv/ken6/5Do+v+P6Pr/juf6/4zn+v+L5vr/iub5/4jk+P90 + sMv/ob7G/6aclv+ozd3/ftj0/4Pj+P+C4vj/ds/x/2DS7f+c4/T//////5zl9P+Q1Or/AAAAHwAAABwA + AAAZAAAAFQAAABMAAAAQAAAADQAAAAsAAAAIAAAABgAAAAQAAAACAAAAAQAAAAAAAAAAAAAAAAAAAAAA + AAAApN71/6Dt/P+O3/z/mOv8/5js+/+X6/v/luv7/5Xq+/+U6vv/k+n7/5Hp+v+Q6fr/j+j6/47o+v+N + 5/r/jOf6/4rl+f+Vrbf/s5+V/5mkqf9XjaX/eNLt/4Tj+f+D4/j/d8/x/2PT7v+e5PT//////57m9P+Q + 1ev/AAAAGQAAABYAAAASAAAAEAAAAA0AAAALAAAACAAAAAYAAAAEAAAAAgAAAAEAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAApt/1/6Lu/P+P3/z/muz8/5rt/P+Z7Pv/l+z7/5br+/+V6/v/lOr7/5Pq+/+S + 6fr/ken6/5Do+v+P6Pr/jef6/4vm+v9/udH/qc/b/7Oek/+Rmp7/d9Dr/4bk+f+F5Pn/ec/y/2bU7v+X + 4vT/0fL6/5Xj9P+R1uz/AAAAEwAAABAAAAANAAAACgAAAAgAAAAGAAAABAAAAAIAAAABAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqN/1/6Pv/P+P3/z/m+38/5vt/P+a7fz/mez8/5js+/+X + 6/v/luv7/5Xq+/+T6vv/kun7/5Hp+v+Q6Pr/j+j6/47n+v+Er8H/qKek/4ugqP+Cz/D/gdv3/4fl+f+G + 5Pn/etDy/3DY7/+A2/L/ieHy/5fl9P+R1+z/AAAADQAAAAoAAAAIAAAABgAAAAQAAAACAAAAAQAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqeD1/6Tv/P+P3/z/ne38/53u/P+c + 7fz/m+38/5rs/P+Y7Pv/l+v7/5br+/+V6/v/lOr7/5Pq+/+S6fr/ken6/4/o+v+fu8X/yLKq/6yjnf9i + dH7/cMXh/4nl+f+L5vn/e9Dy/4bd8v+a5PT/mOL0/5je8P+O0+aaAAAACAAAAAYAAAAEAAAAAgAAAAEA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAq+H2/6Xw/f+P + 3/z/nu78/57u/P+d7vz/nO78/5vt/P+a7fz/mez7/5js+/+X6/v/luv7/5Tq+/+T6vv/kun7/5Hp+v92 + u9X/qcrV/6Cemv+Ou87/fdTy/4rm+f+S6Pn/g9j0/3zQ8v+H2vT/lt3v/4vO4VwAAAAGAAAABAAAAAIA + AAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAArOH2/6fw/f+R3/z/oO/9/6Dv/P+f7/z/nu78/53u/P+c7fz/mu38/5ns/P+Y7Pv/l+v7/5br+/+V + 6vv/lOr7/5Pq+/+fvMX/yK6i/5Kor/9fp8b/fdTw/4zn+v+L5vr/jOb5/4vj+P9/0/L/lNzv/2aYpg0A + AAACAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAruL2/6jx/f+R4Pz/oe/9/6Hw/f+g7/3/n+/8/57u/P+d7vz/nO38/5vt/P+a + 7fz/mez7/5fr+/+W6/v/lev7/5Pp+/+Kwdn/n8jX/7aelP+BiY//c8jm/43n+v+M5/r/i+b6/5bp+v98 + 0fL/lNzv/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAr+L2/6ry/f+S4Pz/o/D9/6Pw/f+i8P3/oe/9/6Dv/P+f + 7/z/ne78/5zu/P+b7fz/mu38/5ns/P+Y7Pv/l+v7/5Lo+/+Lwtr/rK6t/5ehpf9rp8T/dc70/4/o+v+O + 6Pr/kej6/6Ts+/990fL/lN3w/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAseP2/6vy/f+S4fz/pPD9/6Tx/f+j + 8P3/ovD9/6Hw/f+g7/z/n+/8/57u/P+d7vz/nO38/5vt/P+a7Pz/mOz7/4fi/f+rt7n/tqCW/6OWjv+H + bmP/WX2P/5Dn+v+Q6Pr/k+n6/7Pv/P9/0vL/ld7x/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsuP2/6zz/v+S + 4fz/pfH9/6Xx/f+k8f3/o/H9/6Lw/f+h8P3/oO/9/5/v/P+e7/z/ne78/5zu/P+b7fz/l+r8/4yzvv+O + fnj/f3Js/5CHg//54dH/f2pk/4/k9/+R6fr/len6/8j0/f+A0vL/lt/y/wAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAs+P2/6zz/v+T4f3/p/H+/6fy/f+m8v3/pfH9/6Tx/f+j8P3/ovD9/6Hw/f+g7/z/n+/8/57u/P+b + 7Pz/hNDq/6SBb/9JOzr/TU5S/5CIhv/gx7L/aFdK/4/k9f+T6vv/nOv6/8/1/f+C0/L/luHz/wAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAteT2/670/v+T4f3/qPL+/6jz/v+n8v3/pvL9/6Xx/f+k8f3/o/H9/6Lw/f+h + 8P3/oO/9/5/v/P+b7Pz/hcvg/4RjWf8bLDX/fLLL/6ehm//WvKT/XlBI/5Hl9v+U6vv/ou38/8z0/f+D + 0/L/l+Lz/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtuT2/6/0/v+U4v3/qPL+/6nz/v+o8/7/p/L+/6fy/f+m + 8v3/pfH9/6Tx/f+j8P3/ovD9/6Hv/f+f7vz/ldDg/8Kolv+BenX/eoWI/7Gciv/Ut6D/WlBL/5Tm9/+W + 6/v/o+38/9H2/v+F1PP/l+P0/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAt+X2/7D0/v+U4v3/qfP+/6r0/v+q + 8/7/qfP+/6jz/v+n8v3/pvL9/6Xx/f+k8f3/o/H9/6Lw/f+h8P3/l9zw/824o//eybj/z7ik/7ufiP/K + sJ3/Rj4//5bo+P+X7Pv/pe78/9n4/v+G1PP/mOT1/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAuOX3/7H0/v+V + 4v3/qvP+/6z0/v+r9P7/qvP+/6nz/v+o8/7/p/L+/6by/f+l8v3/pfH9/6Tx/f+j8P3/leb7/8ixnP/P + u6n/wq6c/8Gslv/Tu6n/OTU4/5fq+f+Z7Pz/sfH8/9n4/f+I1fP/meX2/wAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAueb3/7L0//+V4v3/qvT+/631/v+s9P7/q/T+/6r0/v+p8/7/qfP+/6jy/v+n8v3/pvL9/6Xx/f+k + 8f3/lOn+/8y2ov/ay73/y7us/868qP/gy7r/Ozs+/5nq+v+b7fz/tfH9/9f4/f+K1fP/meb2/wAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAuub3/7P1//+W4v3/q/T+/671/v+t9f7/rPT+/6v0/v+r9P7/qvP+/6nz/v+o + 8/7/p/L9/6by/f+l8f3/kur+/9TCs//l2Mz/18rB/9fHuP/q1Mf/QEJG/5rs+v+c7vz/tfL9/9b4/f+L + 1vP/muf3/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAu+b3/7P1//+W4v3/rfX//6/1//+u9f7/rfX+/631/v+s + 9P7/q/T+/6r0/v+p8/7/qPP+/6jy/v+n8v3/ne79/8fEvv/t3tT/5NrU/+jcz//y3NL/QURK/5zs+/+e + 7vz/v/T9/+P6/v+N1vP/muj4/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvOb3/7T2//+W4v3/rvX//7D2//+v + 9v//rvX+/671/v+t9f7/rPT+/6v0/v+q9P7/qvP+/6nz/v+o8/7/p/L9/67Iyf/o2Mv/7Ofk//Pr4//6 + 5+D/Qk1V/53t/P+f7/z/zvb9/+j7/v+P1/P/m+n4/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvef3/7X2//+W + 4/3/r/X//7D2//+w9v//r/b//6/1//+u9f7/rfX+/6z0/v+s9P7/q/T+/6rz/v+p8/7/qPP+/53V4f/f + yLb/9/Pw////+f//8er/Rlli/5/u/P+h7/3/z/f9/+j7/v+Q2PP/m+r5/wAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAvuf3/7X2//+X4/3/sPb//7H2//+x9v//sPb//6/2//+v9f//rvX+/631/v+t9f7/rPT+/6v0/v+q + 9P7/qvP+/5nk9f/awav/8+7p//Tz8f/86+b/TGJt/6Du/P+i8P3/z/f+/+n7/v+S2PT/nOv6/wAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAvuf3/7X2//+X4/3/sPb//7L3//+x9///sfb//7D2//+w9v//r/b//671/v+u + 9f7/rfX+/6z0/v+r9P7/q/T+/6Xw/f/StJ//Y1pZ/1NQTf/z3dj/eaW3/6Hw/f+j8f3/1fj+/+b7/v+U + 2fT/nOz6/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvuf3/7X2//+X4/3/sPb//7L3//+y9///svf//7H2//+x + 9v//sPb//6/2//+v9f//rvX+/631/v+t9f7/rPT+/6v0/v/Pspz/npSM/4B4cv/VycX/g4+U/6Hu+/+l + 8f3/2/n+/+T7/v+W2fT/ne37/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvuf3/7X2//+X4/3/sPb//7L3//+y + 9///svf//7L3//+x9///sfb//7D2//+w9v//r/b//671/v+t9f7/rfX+/6fv+//Gqpv/wLq0/3xzcP+l + mZP/sJWL/7/s9f/s/P///////+T7/v+X2vT/ne77/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvuf3/7X2//+X + 4/3/sPb//7L3//+y9///svf//7L3//+y9///sff//7H2//+w9v//sPb//6/2//+v9f//r/X+/7fz/f/1 + 5dz/0c7L/2heWP+rnJD/vaed/9ru8//d+v//0fj+/7/2/v+Z2vT/nu/8/wAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAvuf3/7X2//+X4/3/sPb//7L3//+y9///svf//7L3//+y9///svf//7L3//+x9v//sfb//7D2//+v + 9v//uff//733/v+79Pz/5dzW/36CfP+hr7T/fJun/57h8v+k5vn/oeH3/57e9f+d4/f/nu/8lgAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAvuf3/7X2//+X4/3/sfb//7L3//+y9///svf//7P3//+z9///tPf//7P3//+y + 9///sff//7H1//+x8v3/sPD8/6/s+/+u6fn/oaqu/15xef9kjp3/f7jL9p7f896h5vm5n+j6hp7q+0Ge + 8P0PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvuf3/7T3//+l7v7/svf//7L3//+y9///svf//7L2//+0 + 9P3/tfD8/7bu+/+26/r/tuj3/7Xj9f+w5vf7ruf47Kvo+c6o6fmlpur6caDw/S0AAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvOj4/bP2/v+18/3/t/H8/7nu+v+7 + 6/n/ven4/73n9/+46fj2s+n54LDq+sGu6/qXq+v7VZ/x/g8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtOv5wbzo+P24 + 6fjyter51rLr+qyw7Pp+re37QwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAD///////8AAP///////wAA////////AAD///////8AAP///////wAA///gAH//AAD/ + /4AAH/8AAP/4AAAP/wAA/8AAAAP/AAD8AAAAAf8AAMAAAAAB/wAAgAAAAAD/AACAAAAAAH8AAIAAAAAA + PwAAgAAAAAAPAACAAAAAAA8AAIAAAAAADwAAgAAAAAA/AACAAAAAAP8AAIAAAAAD/wAAgAAAAA//AACA + AAAAP/8AAIAAAAD//wAAgAAAB///AACAAAAH//8AAIAAAAf//wAAgAAAB///AACAAAAH//8AAIAAAAf/ + /wAAgAAAB///AACAAAAH//8AAIAAAAf//wAAgAAAB///AACAAAAH//8AAIAAAAf//wAAgAAAB///AACA + AAAH//8AAIAAAAf//wAAgAAAB///AACAAAAH//8AAIAAAAf//wAAgAAAB///AACAAAAH//8AAIAAAA// + /wAAgAAH////AACAAf////8AAID//////wAA////////AAAoAAAAIAAAAEAAAAABACAAAAAAAIAQAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAFcaW5O3u0ynNSd4Y4AAAAGwAAABYA + AAAOAAAABgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIfH4DKGssKbaJGi5Wmxzfx4zu7/eNDr/3bL5f9o + mq2HIDA2QAAAACoAAAAcAAAADAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACKzOUwiMznfYTN6seAzuv2es3t/4+WmP+jnpz/e6Oz/3TY9f9n + zu//T83o/2fN6P+Izeb/IDA2TAAAADUAAAAlAAAAEwAAAAYAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAis3lMIrO54CI0OvKhdDs94LS8P990vL/fNX0/3vY9f962/X/tsPH/5eXl/9z + pLb/d973/3HN8f9ayt//v6mI/4TW7P9torWcAAAAQAAAADgAAAAoAAAAEgAAAAQAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACN0epejtLszY7T7viN1fH/itbz/4bZ9f+E3Pb/guD3/4Hi+P+A4fj/fuH4/33f9/+k + q67/kpCR/4Cvwf943vf/cc3x/2HN3//ix6T/htfs/3KpvaQAAAA8AAAAOAAAADEAAAAfAAAACwAAAAEA + AAAAAAAAAAAAAAAAAAAAmNnysJrd9f+T3vb/juH4/4zk+P+J5fn/iOX5/4bk+f+F5Pn/g+P4/4Li+P+A + 4vj/f+D3/7jEyf+UlJT/b6Cx/3re9/9wzfH/Y9Ts//X8/v+H2O3/dK3BoQAAADUAAAAxAAAALQAAACQA + AAAWAAAACAAAAAIAAAAAAAAAAAAAAACd3PT/ouv7/5Do+v+O6Pr/jef6/4vm+v+K5vn/iOX5/4fk+f+F + 5Pn/hOP5/4Lj+P+B4ff/m56h/46Sk/99uc7/fN/3/3HO8f9l1Oz/9fz+/4ja7f91scWeAAAALQAAACgA + AAAkAAAAHwAAABgAAAAPAAAABwAAAAIAAAAAAAAAAKDd9f+k7Pz/iuD7/5Hp+v+P6Pr/juf6/4zn+v+L + 5vn/ieX5/4jl+f+G5Pn/heT5/4Pi+P+/y9H/lpeX/2eZq/9+4Pj/c87x/2vW7f/1/P7/itru/3i3y5oA + AAAkAAAAHwAAABsAAAAWAAAAEgAAAA0AAAAIAAAABAAAAAEAAAAAot71/6Xs/P+L4Pz/k+r7/5Lp+v+Q + 6Pr/juj6/43n+v+L5vr/iub5/4jl+f+H5Pn/heP4/5SXmf+OkJL/hL7S/4Dh+P92zvH/cdfu//b8/v+M + 2+7/fL3QlgAAABsAAAAWAAAAEgAAAA4AAAAKAAAABgAAAAMAAAABAAAAAAAAAACl3vX/p+38/4zh/P+V + 6/v/lOr7/5Lp+/+R6fr/j+j6/47n+v+M5/r/i+b5/4nl+f+I5Pn/w9HX/5SZmf9mlqj/guL4/3fP8f92 + 2e//7/v9/47c7v9/w9iRAAAAEQAAAA0AAAAKAAAABgAAAAMAAAABAAAAAAAAAAAAAAAAAAAAAKff9f+p + 7vz/jeH8/5js+/+W6/v/ler7/5Pq+/+S6fr/kOj6/47o+v+N5/r/i+b6/4rm+f+RkZP/jZCS/4XA1v+F + 4/n/edDy/2zX7v9x2e//j9zu/4HH3HwAAAAKAAAABgAAAAMAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAqeD1/6ru/P+P4vz/mu38/5ns+/+X6/v/lev7/5Tq+/+S6fv/ken6/4/o+v+O5/r/jOf6/8XU2v+W + mpz/aJao/47m+f991PP/fNfx/5Pe8P+Fz+ObW42bEgAAAAMAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAACr4fb/rO/8/5Di/P+c7vz/m+38/5ns/P+Y7Pv/luv7/5Xq+/+T6vv/kun6/5Do+v+O + 6Pr/ko+R/4uPkf+Dwdj/n+r6/4bh+P9/0/L/jNbr/0xxewYAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAK7h9v+t8P3/keP8/5/v/P+d7vz/nO38/5rt/P+Z7Pv/l+v7/5Xr+/+U + 6vv/kun7/5Hp+v/F2OD/lZmZ/2uaq/+T6Pn/kef5/3zR8v+I1uv/AAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsOL2/6/x/f+R4/z/oe/9/5/v/P+e7vz/nO78/5vt/P+Z + 7Pz/mOz7/5br+/+V6vv/kun7/6G8xv+iiYH/kcXY/4/o+v+d6vv/ftHy/4fX6/8AAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACy4/b/sfL9/5Pk/P+j8P3/ofD9/6Dv/P+f + 7/z/ne78/5zt/P+a7fz/mez7/5fr+/+Ivc//l4h//7Wajf93iJH/huH6/6zt+/+A0vL/h9js/wAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALTk9v+x8v7/lOT8/6Xx/f+k + 8f3/ovD9/6Hv/f+f7/z/nu78/5zu/P+b7fz/mez8/3BPQf9lhJb/va6i/3ptZf9zz+r/uvH8/4LT8v+H + 2ez/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAteT2/7Py/v+V + 5f3/p/L9/6by/f+k8f3/o/D9/6Hw/f+g7/z/n+/8/53u/P+c7fz/k21b/5+goP+9oIn/aWpq/33b9//I + 9P3/hNPy/4ba7f8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC3 + 5fb/tfP+/5bl/f+p8/7/p/L+/6by/f+l8f3/pPH9/6Lw/f+h7/3/n+/8/57u/P/lx7P/1bab/7aOcv9h + aXD/f935/9X3/f+H1PP/htzu/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAALnl9/+39P7/l+X9/6r0/v+p8/7/qPP+/6fy/f+m8v3/pPH9/6Pw/f+h8P3/oO/8/93Ryv/r + y7T/yquV/2Jwe/9/4Pv/5Pr+/4nV8/+G3e7/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAuub3/7f0/v+Y5v3/rPT+/6v0/v+q8/7/qfP+/6fy/v+m8v3/pfH9/6Tx/f+i + 8P3/2OHj///x4//izb3/a3yI/4Hh/f/v/P//i9bz/4Xe7/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC85vf/uPT//5nm/f+u9f7/rfX+/6z0/v+q9P7/qfP+/6jz/v+n + 8v3/pvL9/6Tx/f/G3+b////3//vv5v9pgY7/g+T+//X9//+O1/P/hd/w/wAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL3n9/+59P//meb9/6/2//+u9f7/rfX+/6z0/v+r + 9P7/qvP+/6nz/v+n8v7/pvL9/7fe6f/Zu6//t6yp/36Zp/+E6P7/+v7//5DX8/+F4PD/AAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvuf3/7r1//+a5v3/sPb//6/2//+v + 9f//rvX+/631/v+s9P7/qvT+/6nz/v+o8/7/pOX1/7iWhv9xY17/cX+K/4Hn/v/9////ktj0/4Xg8f8A + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC+5/f/uvX//5rm/f+x + 9///sfb//7D2//+v9v//rvX+/631/v+s9P7/q/T+/6rz/v+q2+X/zbOm/1hKRP+dgXn/ltHm//3///+V + 2fT/hOHx/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAL7n9/+6 + 9f//mub9/7L3//+y9///sfb//7D2//+v9v//r/X//671/v+t9f7/rPT+//rz8f/hzsb/hXVs/+a/sv+y + 1N3//f///5fa9P+E4vL/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAvuf3/7r1//+a5v3/svf//7L3//+y9///sff//7b3///A+P//0Pr//9b6/v/b+///3Pz///bk3/+P + hYL/k6Wu/4TB1f+m4fb/ld/0/4Tj8pYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAC+5/f/ufX//6vy//+y9///svf//7L3//+z9///tfT+/7fx/P+27vr/ter5/7Lm9/+s + 4fb/wtXZ/Yijq/94qLn1fMPYuYzh80SE4/IPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAL3n9+2z9f7/tfP9/7fw/P+57fr/u+r5/7vm9/+15vfyreX22Kfk9bKl + 5PV2oeP1OQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvuf3fr3n9+225/bWtef2q7Xn9nW+5/czvef3AwAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP////////////4Af//4AD//gAAP+AAAD8AAAAeA + AAADgAAAAYAAAACAAAABgAAAB4AAAB+AAAB/gAAB/4AAB/+AAAf/gAAH/4AAB/+AAAf/gAAH/4AAB/+A + AAf/gAAH/4AAB/+AAAf/gAAH/4AAB/+AAAf/gAAP/4AH//+A////KAAAABAAAAAgAAAAAQAgAAAAAABA + BAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAo8r/QKPK/0Cjyv9Ao8r/QKPK/0Cjyv9Ao8r/QKPK/0Cjyv9A + o8r/QKPK/2efxP9nnsTbZ53DcgAAAAAAAAAARKbM/5Dn9v+H5PT/gOHz/3zf8v943fH/rK6u/4OFhv+J + tcf/edzw/0SmzP9VzOr/WMbl/2eexNsAAAAAAAAAAEmpzv+P6Pb/huX1/3/h8/953vL/ddzx/6bd9/+p + srT/mXhz/3fb8P9Jqc7/W8/r/9OlSf9mn8T/AAAAAAAAAABOrNH/kOn2/4fl9f9/4vT/eeDy/3Xe8v+s + rq7/g4WG/4m1x/923PH/TqzR/2PS7P/duV3/ZqHF/wAAAAAAAAAAU6/T/5Pr+P+K6Pf/guX1/3zi9P95 + 4PP/u+f3/6mytP+ZeHP/ed3x/1Ov0/9r1e7/7Ozs/2aixv8AAAAAAAAAAFmz1v+X7vn/j+r4/4fo9v+B + 5fX/fuP0/6yurv+DhYb/ibXH/33g8v9Zs9b/dNnv/+zs7P9lo8b/AAAAAAAAAABft9n/nPD6/5Tt+P+M + 6vf/h+j2/4Pm9f+96f3/p7K1/5l4c/+B4vT/X7fZ/37d8f/s7Oz/ZaXH/wAAAAAAAAAAZbrc/6Dy+/+Y + 7/n/ke35/4zr+P+I6ff/rK6u/4OFhv9eYGf/heX0/2W63P+I4fP/huHy/2SnyP8AAAAAAAAAAGu+3/+k + 9fz/nPL7/5bv+v+R7fn/jev4/6KQgf91z/X/hmZh/4jl9P9sv+D/hNXo/2uz0P9kqMmWAAAAAAAAAABw + wuL/qPb8/6H0/P+b8vv/lfD6/5Lu+f/Kuav/qpyR/5l4c/+N6Pf/fdLr/2/B4f9jrMtLAAAAAAAAAAAA + AAAAdsXk/6v4/f+l9v3/n/T8/5ry+/+X8fr////8/+/Mu/+ZeHP/kev4/5Tr9/92xeT/AAAAAAAAAAAA + AAAAAAAAAHvI5/+v+v7/qfj+/6T2/f+g9fz/nfP7///56//u3dj/mXhz/5fu+f+Z7vj/e8jn/wAAAAAA + AAAAAAAAAAAAAACAy+n/tPv//676/v+q+f7/p/j9/6T2/P+jqqf/blhU/5l4c/+e8Pr/oPH6/4DL6f8A + AAAAAAAAAAAAAAAAAAAAhM7r/7j8//+1+///sfr+/675/v+s+P3/7uri/7mVh/+ZeHP/pvT8/5Pd8v+E + zuv/AAAAAAAAAAAAAAAAAAAAAIfQ7f+H0O3/h9Dt/4fQ7f+H0O3/h9Dt/6LS5v90q8D/Z77e/4fQ7f+H + 0O3/X7rSdQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAAgAEAAIABAACAAQAAgAEAAIABAACAAQAAgAEAAIABAACA + AwAAgAcAAIAHAACABwAAgAcAAIAHAAD//wAA + + + \ No newline at end of file diff --git a/DotNetZip/Zip/Resources/ZipContentsDialog.Designer.cs b/DotNetZip/Zip/Resources/ZipContentsDialog.Designer.cs new file mode 100644 index 0000000..b6c59d7 --- /dev/null +++ b/DotNetZip/Zip/Resources/ZipContentsDialog.Designer.cs @@ -0,0 +1,79 @@ +namespace Ionic.Zip.Forms +{ + partial class ZipContentsDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(WinFormsSelfExtractorStub)); + this.listView1 = new System.Windows.Forms.ListView(); + this.button1 = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // listView1 + // + this.listView1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.listView1.Location = new System.Drawing.Point(12, 12); + this.listView1.Name = "listView1"; + this.listView1.Size = new System.Drawing.Size(700, 287); + this.listView1.TabIndex = 0; + this.listView1.UseCompatibleStateImageBehavior = false; + this.listView1.View = System.Windows.Forms.View.Details; + this.listView1.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.listView1_ColumnClick); + // + // button1 + // + this.button1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.button1.Location = new System.Drawing.Point(637, 305); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(75, 23); + this.button1.TabIndex = 1; + this.button1.Text = "Close"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // ZipContentsDialog + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(724, 340); + this.Controls.Add(this.button1); + this.Controls.Add(this.listView1); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.Name = "ZipContentsDialog"; + this.Text = "Contents of the zip archive"; + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.ListView listView1; + private System.Windows.Forms.Button button1; + } +} \ No newline at end of file diff --git a/DotNetZip/Zip/Resources/ZipContentsDialog.cs b/DotNetZip/Zip/Resources/ZipContentsDialog.cs new file mode 100644 index 0000000..7197a71 --- /dev/null +++ b/DotNetZip/Zip/Resources/ZipContentsDialog.cs @@ -0,0 +1,226 @@ +// ZipContentsDialog.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2009 Dino Chiesa +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// + +namespace Ionic.Zip.Forms +{ + using System; + using System.Collections.Generic; + using System.Windows.Forms; + using Ionic.Zip; + + public partial class ZipContentsDialog : Form + { + public ZipContentsDialog() + { + InitializeComponent(); + FixTitle(); + } + + private void FixTitle() + { + this.Text = String.Format("Contents of the zip archive (DotNetZip v{0})", + Ionic.Zip.ZipFile.LibraryVersion.ToString()); + } + + public ZipFile ZipFile + { + set + { + listView1.Clear(); + listView1.BeginUpdate(); + + string[] columnHeaders = new string[] { "name", "lastmod", "original", "ratio", "compressed", "enc?", "CRC" }; + foreach (string label in columnHeaders) + { + SortableColumnHeader ch = new SortableColumnHeader(label); + if (label != "name" && label != "lastmod") + ch.TextAlign = HorizontalAlignment.Right; + listView1.Columns.Add(ch); + } + + foreach (ZipEntry e in value) + { + ListViewItem item = new ListViewItem(e.FileName); + + string[] subitems = new string[] { + e.LastModified.ToString("yyyy-MM-dd HH:mm:ss"), + e.UncompressedSize.ToString(), + String.Format("{0,5:F0}%", e.CompressionRatio), + e.CompressedSize.ToString(), + (e.UsesEncryption) ? "Y" : "N", + String.Format("{0:X8}", e.Crc)}; + + foreach (String s in subitems) + { + ListViewItem.ListViewSubItem subitem = new ListViewItem.ListViewSubItem(); + subitem.Text = s; + item.SubItems.Add(subitem); + } + + this.listView1.Items.Add(item); + } + + listView1.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent); + + // adjust size of the entire form + int aggWidth= 0; + for (int i = 0; i < this.listView1.Columns.Count; i++) + { + aggWidth += this.listView1.Columns[i].Width + 1; + } + // plus a fudge factor + //aggWidth += this.listView1.Columns[this.listView1.Columns.Count - 1].Width / 2 + this.listView1.Location.X * 4; + aggWidth += this.listView1.Location.X * 4 + 4; + this.Size = new System.Drawing.Size(aggWidth, this.Height); + + this.listView1.EndUpdate(); + } + } + + + + + private void button1_Click(object sender, EventArgs e) + { + this.Close(); + } + + private void listView1_ColumnClick(object sender, ColumnClickEventArgs e) + { + // Create an instance of the ColHeader class. + SortableColumnHeader clickedCol = (SortableColumnHeader)this.listView1.Columns[e.Column]; + + // Set the ascending property to sort in the opposite order. + clickedCol.SortAscending = !clickedCol.SortAscending; + + // Get the number of items in the list. + int numItems = this.listView1.Items.Count; + + // Turn off display while data is repoplulated. + this.listView1.BeginUpdate(); + + // Populate an ArrayList with a SortWrapper of each list item. + List list = new List(); + for (int i = 0; i < numItems; i++) + { + list.Add(new ItemWrapper(this.listView1.Items[i], e.Column)); + } + + if (e.Column == 2 || e.Column == 4) + list.Sort(new ItemWrapper.NumericComparer(clickedCol.SortAscending)); + else + list.Sort(new ItemWrapper.StringComparer(clickedCol.SortAscending)); + + // Clear the list, and repopulate with the sorted items. + this.listView1.Items.Clear(); + for (int i = 0; i < numItems; i++) + this.listView1.Items.Add(list[i].Item); + + // Turn display back on. + this.listView1.EndUpdate(); + } + + } + + + + // The ColHeader class is a ColumnHeader object with an + // added property for determining an ascending or descending sort. + // True specifies an ascending order, false specifies a descending order. + public class SortableColumnHeader : ColumnHeader + { + public bool SortAscending; + public SortableColumnHeader(string text) + { + this.Text = text; + this.SortAscending = true; + } + } + + + // An instance of the SortWrapper class is created for + // each item and added to the ArrayList for sorting. + public class ItemWrapper + { + internal ListViewItem Item; + internal int Column; + + // A SortWrapper requires the item and the index of the clicked column. + public ItemWrapper(ListViewItem item, int column) + { + Item = item; + Column = column; + } + + // Text property for getting the text of an item. + public string Text + { + get { return Item.SubItems[Column].Text; } + } + + // Implementation of the IComparer + public class StringComparer : IComparer + { + bool ascending; + + // Constructor requires the sort order; + // true if ascending, otherwise descending. + public StringComparer(bool asc) + { + this.ascending = asc; + } + + // Implemnentation of the IComparer:Compare + // method for comparing two objects. + public int Compare(ItemWrapper xItem, ItemWrapper yItem) + { + string xText = xItem.Item.SubItems[xItem.Column].Text; + string yText = yItem.Item.SubItems[yItem.Column].Text; + return xText.CompareTo(yText) * (this.ascending ? 1 : -1); + } + } + + public class NumericComparer : IComparer + { + bool ascending; + + // Constructor requires the sort order; + // true if ascending, otherwise descending. + public NumericComparer(bool asc) + { + this.ascending = asc; + } + + // Implemnentation of the IComparer:Compare + // method for comparing two objects. + public int Compare(ItemWrapper xItem, ItemWrapper yItem) + { + int x = 0, y = 0; + try + { + x = Int32.Parse(xItem.Item.SubItems[xItem.Column].Text); + y = Int32.Parse(yItem.Item.SubItems[yItem.Column].Text); + } + catch + { + } + return (x - y) * (this.ascending ? 1 : -1); + } + } + } + +} diff --git a/DotNetZip/Zip/Resources/ZipContentsDialog.resx b/DotNetZip/Zip/Resources/ZipContentsDialog.resx new file mode 100644 index 0000000..19dc0dd --- /dev/null +++ b/DotNetZip/Zip/Resources/ZipContentsDialog.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/DotNetZip/Zip/Resources/zippedFile.ico b/DotNetZip/Zip/Resources/zippedFile.ico new file mode 100644 index 0000000..b39623c Binary files /dev/null and b/DotNetZip/Zip/Resources/zippedFile.ico differ diff --git a/DotNetZip/Zip/Resources/zippedResources.zip b/DotNetZip/Zip/Resources/zippedResources.zip new file mode 100644 index 0000000..da03d18 Binary files /dev/null and b/DotNetZip/Zip/Resources/zippedResources.zip differ diff --git a/DotNetZip/Zip/Shared.cs b/DotNetZip/Zip/Shared.cs new file mode 100644 index 0000000..cdc7161 --- /dev/null +++ b/DotNetZip/Zip/Shared.cs @@ -0,0 +1,901 @@ +// Shared.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2006-2011 Dino Chiesa. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// Last Saved: <2011-August-02 19:41:01> +// +// ------------------------------------------------------------------ +// +// This module defines some shared utility classes and methods. +// +// Created: Tue, 27 Mar 2007 15:30 +// + +using System; +using System.IO; +using System.Security.Permissions; + +namespace Ionic.Zip +{ + /// + /// Collects general purpose utility methods. + /// + internal static class SharedUtilities + { + /// private null constructor + //private SharedUtilities() { } + + // workitem 8423 + public static Int64 GetFileLength(string fileName) + { + if (!File.Exists(fileName)) + throw new System.IO.FileNotFoundException(fileName); + + long fileLength = 0L; + FileShare fs = FileShare.ReadWrite; +#if !NETCF + // FileShare.Delete is not defined for the Compact Framework + fs |= FileShare.Delete; +#endif + using (var s = File.Open(fileName, FileMode.Open, FileAccess.Read, fs)) + { + fileLength = s.Length; + } + return fileLength; + } + + + [System.Diagnostics.Conditional("NETCF")] + public static void Workaround_Ladybug318918(Stream s) + { + // This is a workaround for this issue: + // https://connect.microsoft.com/VisualStudio/feedback/details/318918 + // It's required only on NETCF. + s.Flush(); + } + + +#if LEGACY + /// + /// Round the given DateTime value to an even second value. + /// + /// + /// + /// + /// Round up in the case of an odd second value. The rounding does not consider + /// fractional seconds. + /// + /// + /// This is useful because the Zip spec allows storage of time only to the nearest + /// even second. So if you want to compare the time of an entry in the archive with + /// it's actual time in the filesystem, you need to round the actual filesystem + /// time, or use a 2-second threshold for the comparison. + /// + /// + /// This is most nautrally an extension method for the DateTime class but this + /// library is built for .NET 2.0, not for .NET 3.5; This means extension methods + /// are a no-no. + /// + /// + /// The DateTime value to round + /// The ruonded DateTime value + public static DateTime RoundToEvenSecond(DateTime source) + { + // round to nearest second: + if ((source.Second % 2) == 1) + source += new TimeSpan(0, 0, 1); + + DateTime dtRounded = new DateTime(source.Year, source.Month, source.Day, source.Hour, source.Minute, source.Second); + //if (source.Millisecond >= 500) dtRounded = dtRounded.AddSeconds(1); + return dtRounded; + } +#endif + +#if YOU_LIKE_REDUNDANT_CODE + internal static string NormalizePath(string path) + { + // remove leading single dot slash + if (path.StartsWith(".\\")) path = path.Substring(2); + + // remove intervening dot-slash + path = path.Replace("\\.\\", "\\"); + + // remove double dot when preceded by a directory name + var re = new System.Text.RegularExpressions.Regex(@"^(.*\\)?([^\\\.]+\\\.\.\\)(.+)$"); + path = re.Replace(path, "$1$3"); + return path; + } +#endif + + private static System.Text.RegularExpressions.Regex doubleDotRegex1 = + new System.Text.RegularExpressions.Regex(@"^(.*/)?([^/\\.]+/\\.\\./)(.+)$"); + + private static string SimplifyFwdSlashPath(string path) + { + if (path.StartsWith("./")) path = path.Substring(2); + path = path.Replace("/./", "/"); + + // Replace foo/anything/../bar with foo/bar + path = doubleDotRegex1.Replace(path, "$1$3"); + return path; + } + + + /// + /// Utility routine for transforming path names from filesystem format (on Windows that means backslashes) to + /// a format suitable for use within zipfiles. This means trimming the volume letter and colon (if any) And + /// swapping backslashes for forward slashes. + /// + /// source path. + /// transformed path + public static string NormalizePathForUseInZipFile(string pathName) + { + // boundary case + if (String.IsNullOrEmpty(pathName)) return pathName; + + // trim volume if necessary + if ((pathName.Length >= 2) && ((pathName[1] == ':') && (pathName[2] == '\\'))) + pathName = pathName.Substring(3); + + // swap slashes + pathName = pathName.Replace('\\', '/'); + + // trim all leading slashes + while (pathName.StartsWith("/")) pathName = pathName.Substring(1); + + return SimplifyFwdSlashPath(pathName); + } + + + static System.Text.Encoding ibm437 = System.Text.Encoding.GetEncoding("IBM437"); + static System.Text.Encoding utf8 = System.Text.Encoding.GetEncoding("UTF-8"); + + internal static byte[] StringToByteArray(string value, System.Text.Encoding encoding) + { + byte[] a = encoding.GetBytes(value); + return a; + } + internal static byte[] StringToByteArray(string value) + { + return StringToByteArray(value, ibm437); + } + + //internal static byte[] Utf8StringToByteArray(string value) + //{ + // return StringToByteArray(value, utf8); + //} + + //internal static string StringFromBuffer(byte[] buf, int maxlength) + //{ + // return StringFromBuffer(buf, maxlength, ibm437); + //} + + internal static string Utf8StringFromBuffer(byte[] buf) + { + return StringFromBuffer(buf, utf8); + } + + internal static string StringFromBuffer(byte[] buf, System.Text.Encoding encoding) + { + // this form of the GetString() method is required for .NET CF compatibility + string s = encoding.GetString(buf, 0, buf.Length); + return s; + } + + + internal static int ReadSignature(System.IO.Stream s) + { + int x = 0; + try { x = _ReadFourBytes(s, "n/a"); } + catch (BadReadException) { } + return x; + } + + + internal static int ReadEntrySignature(System.IO.Stream s) + { + // handle the case of ill-formatted zip archives - includes a data descriptor + // when none is expected. + int x = 0; + try + { + x = _ReadFourBytes(s, "n/a"); + if (x == ZipConstants.ZipEntryDataDescriptorSignature) + { + // advance past data descriptor - 12 bytes if not zip64 + s.Seek(12, SeekOrigin.Current); + // workitem 10178 + Workaround_Ladybug318918(s); + x = _ReadFourBytes(s, "n/a"); + if (x != ZipConstants.ZipEntrySignature) + { + // Maybe zip64 was in use for the prior entry. + // Therefore, skip another 8 bytes. + s.Seek(8, SeekOrigin.Current); + // workitem 10178 + Workaround_Ladybug318918(s); + x = _ReadFourBytes(s, "n/a"); + if (x != ZipConstants.ZipEntrySignature) + { + // seek back to the first spot + s.Seek(-24, SeekOrigin.Current); + // workitem 10178 + Workaround_Ladybug318918(s); + x = _ReadFourBytes(s, "n/a"); + } + } + } + } + catch (BadReadException) { } + return x; + } + + + internal static int ReadInt(System.IO.Stream s) + { + return _ReadFourBytes(s, "Could not read block - no data! (position 0x{0:X8})"); + } + + private static int _ReadFourBytes(System.IO.Stream s, string message) + { + int n = 0; + byte[] block = new byte[4]; +#if NETCF + // workitem 9181 + // Reading here in NETCF sometimes reads "backwards". Seems to happen for + // larger files. Not sure why. Maybe an error in caching. If the data is: + // + // 00100210: 9efa 0f00 7072 6f6a 6563 742e 6963 7750 ....project.icwP + // 00100220: 4b05 0600 0000 0006 0006 0091 0100 008e K............... + // 00100230: 0010 0000 00 ..... + // + // ...and the stream Position is 10021F, then a Read of 4 bytes is returning + // 50776369, instead of 06054b50. This seems to happen the 2nd time Read() + // is called from that Position.. + // + // submitted to connect.microsoft.com + // https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=318918#tabs + // + for (int i = 0; i < block.Length; i++) + { + n+= s.Read(block, i, 1); + } +#else + n = s.Read(block, 0, block.Length); +#endif + if (n != block.Length) throw new BadReadException(String.Format(message, s.Position)); + int data = unchecked((((block[3] * 256 + block[2]) * 256) + block[1]) * 256 + block[0]); + return data; + } + + + + /// + /// Finds a signature in the zip stream. This is useful for finding + /// the end of a zip entry, for example, or the beginning of the next ZipEntry. + /// + /// + /// + /// + /// Scans through 64k at a time. + /// + /// + /// + /// If the method fails to find the requested signature, the stream Position + /// after completion of this method is unchanged. If the method succeeds in + /// finding the requested signature, the stream position after completion is + /// direct AFTER the signature found in the stream. + /// + /// + /// + /// The stream to search + /// The 4-byte signature to find + /// The number of bytes read + internal static long FindSignature(System.IO.Stream stream, int SignatureToFind) + { + long startingPosition = stream.Position; + + int BATCH_SIZE = 65536; // 8192; + byte[] targetBytes = new byte[4]; + targetBytes[0] = (byte)(SignatureToFind >> 24); + targetBytes[1] = (byte)((SignatureToFind & 0x00FF0000) >> 16); + targetBytes[2] = (byte)((SignatureToFind & 0x0000FF00) >> 8); + targetBytes[3] = (byte)(SignatureToFind & 0x000000FF); + byte[] batch = new byte[BATCH_SIZE]; + int n = 0; + bool success = false; + do + { + n = stream.Read(batch, 0, batch.Length); + if (n != 0) + { + for (int i = 0; i < n; i++) + { + if (batch[i] == targetBytes[3]) + { + long curPosition = stream.Position; + stream.Seek(i - n, System.IO.SeekOrigin.Current); + // workitem 10178 + Workaround_Ladybug318918(stream); + + // workitem 7711 + int sig = ReadSignature(stream); + + success = (sig == SignatureToFind); + if (!success) + { + stream.Seek(curPosition, System.IO.SeekOrigin.Begin); + // workitem 10178 + Workaround_Ladybug318918(stream); + } + else + break; // out of for loop + } + } + } + else break; + if (success) break; + + } while (true); + + if (!success) + { + stream.Seek(startingPosition, System.IO.SeekOrigin.Begin); + // workitem 10178 + Workaround_Ladybug318918(stream); + return -1; // or throw? + } + + // subtract 4 for the signature. + long bytesRead = (stream.Position - startingPosition) - 4; + + return bytesRead; + } + + + // If I have a time in the .NET environment, and I want to use it for + // SetWastWriteTime() etc, then I need to adjust it for Win32. + internal static DateTime AdjustTime_Reverse(DateTime time) + { + if (time.Kind == DateTimeKind.Utc) return time; + DateTime adjusted = time; + if (DateTime.Now.IsDaylightSavingTime() && !time.IsDaylightSavingTime()) + adjusted = time - new System.TimeSpan(1, 0, 0); + + else if (!DateTime.Now.IsDaylightSavingTime() && time.IsDaylightSavingTime()) + adjusted = time + new System.TimeSpan(1, 0, 0); + + return adjusted; + } + +#if NECESSARY + // If I read a time from a file with GetLastWriteTime() (etc), I need + // to adjust it for display in the .NET environment. + internal static DateTime AdjustTime_Forward(DateTime time) + { + if (time.Kind == DateTimeKind.Utc) return time; + DateTime adjusted = time; + if (DateTime.Now.IsDaylightSavingTime() && !time.IsDaylightSavingTime()) + adjusted = time + new System.TimeSpan(1, 0, 0); + + else if (!DateTime.Now.IsDaylightSavingTime() && time.IsDaylightSavingTime()) + adjusted = time - new System.TimeSpan(1, 0, 0); + + return adjusted; + } +#endif + + + internal static DateTime PackedToDateTime(Int32 packedDateTime) + { + // workitem 7074 & workitem 7170 + if (packedDateTime == 0xFFFF || packedDateTime == 0) + return new System.DateTime(1995, 1, 1, 0, 0, 0, 0); // return a fixed date when none is supplied. + + Int16 packedTime = unchecked((Int16)(packedDateTime & 0x0000ffff)); + Int16 packedDate = unchecked((Int16)((packedDateTime & 0xffff0000) >> 16)); + + int year = 1980 + ((packedDate & 0xFE00) >> 9); + int month = (packedDate & 0x01E0) >> 5; + int day = packedDate & 0x001F; + + int hour = (packedTime & 0xF800) >> 11; + int minute = (packedTime & 0x07E0) >> 5; + //int second = packedTime & 0x001F; + int second = (packedTime & 0x001F) * 2; + + // validation and error checking. + // this is not foolproof but will catch most errors. + if (second >= 60) { minute++; second = 0; } + if (minute >= 60) { hour++; minute = 0; } + if (hour >= 24) { day++; hour = 0; } + + DateTime d = System.DateTime.Now; + bool success= false; + try + { + d = new System.DateTime(year, month, day, hour, minute, second, 0); + success= true; + } + catch (System.ArgumentOutOfRangeException) + { + if (year == 1980 && (month == 0 || day == 0)) + { + try + { + d = new System.DateTime(1980, 1, 1, hour, minute, second, 0); + success= true; + } + catch (System.ArgumentOutOfRangeException) + { + try + { + d = new System.DateTime(1980, 1, 1, 0, 0, 0, 0); + success= true; + } + catch (System.ArgumentOutOfRangeException) { } + + } + } + // workitem 8814 + // my god, I can't believe how many different ways applications + // can mess up a simple date format. + else + { + try + { + while (year < 1980) year++; + while (year > 2030) year--; + while (month < 1) month++; + while (month > 12) month--; + while (day < 1) day++; + while (day > 28) day--; + while (minute < 0) minute++; + while (minute > 59) minute--; + while (second < 0) second++; + while (second > 59) second--; + d = new System.DateTime(year, month, day, hour, minute, second, 0); + success= true; + } + catch (System.ArgumentOutOfRangeException) { } + } + } + if (!success) + { + string msg = String.Format("y({0}) m({1}) d({2}) h({3}) m({4}) s({5})", year, month, day, hour, minute, second); + throw new ZipException(String.Format("Bad date/time format in the zip file. ({0})", msg)); + + } + // workitem 6191 + //d = AdjustTime_Reverse(d); + d = DateTime.SpecifyKind(d, DateTimeKind.Local); + return d; + } + + + internal + static Int32 DateTimeToPacked(DateTime time) + { + // The time is passed in here only for purposes of writing LastModified to the + // zip archive. It should always be LocalTime, but we convert anyway. And, + // since the time is being written out, it needs to be adjusted. + + time = time.ToLocalTime(); + // workitem 7966 + //time = AdjustTime_Forward(time); + + // see http://www.vsft.com/hal/dostime.htm for the format + UInt16 packedDate = (UInt16)((time.Day & 0x0000001F) | ((time.Month << 5) & 0x000001E0) | (((time.Year - 1980) << 9) & 0x0000FE00)); + UInt16 packedTime = (UInt16)((time.Second / 2 & 0x0000001F) | ((time.Minute << 5) & 0x000007E0) | ((time.Hour << 11) & 0x0000F800)); + + Int32 result = (Int32)(((UInt32)(packedDate << 16)) | packedTime); + return result; + } + + + /// + /// Create a pseudo-random filename, suitable for use as a temporary + /// file, and open it. + /// + /// + /// + /// The System.IO.Path.GetRandomFileName() method is not available on + /// the Compact Framework, so this library provides its own substitute + /// on NETCF. + /// + /// + /// This method produces a filename of the form + /// DotNetZip-xxxxxxxx.tmp, where xxxxxxxx is replaced by randomly + /// chosen characters, and creates that file. + /// + /// + public static void CreateAndOpenUniqueTempFile(string dir, + out Stream fs, + out string filename) + { + // workitem 9763 + // http://dotnet.org.za/markn/archive/2006/04/15/51594.aspx + // try 3 times: + for (int i = 0; i < 3; i++) + { + try + { + filename = Path.Combine(dir, InternalGetTempFileName()); + fs = new FileStream(filename, FileMode.CreateNew); + return; + } + catch (IOException) + { + if (i == 2) throw; + } + } + throw new IOException(); + } + +#if NETCF || SILVERLIGHT + public static string InternalGetTempFileName() + { + return "DotNetZip-" + GenerateRandomStringImpl(8,0) + ".tmp"; + } + + internal static string GenerateRandomStringImpl(int length, int delta) + { + bool WantMixedCase = (delta == 0); + System.Random rnd = new System.Random(); + + string result = ""; + char[] a = new char[length]; + + for (int i = 0; i < length; i++) + { + // delta == 65 means uppercase + // delta == 97 means lowercase + if (WantMixedCase) + delta = (rnd.Next(2) == 0) ? 65 : 97; + a[i] = (char)(rnd.Next(26) + delta); + } + + result = new System.String(a); + return result; + } +#else + public static string InternalGetTempFileName() + { + return "DotNetZip-" + Path.GetRandomFileName().Substring(0, 8) + ".tmp"; + } + +#endif + + + /// + /// Workitem 7889: handle ERROR_LOCK_VIOLATION during read + /// + /// + /// This could be gracefully handled with an extension attribute, but + /// This assembly is built for .NET 2.0, so I cannot use them. + /// + internal static int ReadWithRetry(System.IO.Stream s, byte[] buffer, int offset, int count, string FileName) + { + int n = 0; + bool done = false; +#if !NETCF && !SILVERLIGHT + int retries = 0; +#endif + do + { + try + { + n = s.Read(buffer, offset, count); + done = true; + } +#if NETCF || SILVERLIGHT + catch (System.IO.IOException) + { + throw; + } +#else + catch (System.IO.IOException ioexc1) + { + // Check if we can call GetHRForException, + // which makes unmanaged code calls. + var p = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode); + if (p.IsUnrestricted()) + { + uint hresult = _HRForException(ioexc1); + if (hresult != 0x80070021) // ERROR_LOCK_VIOLATION + throw new System.IO.IOException(String.Format("Cannot read file {0}", FileName), ioexc1); + retries++; + if (retries > 10) + throw new System.IO.IOException(String.Format("Cannot read file {0}, at offset 0x{1:X8} after 10 retries", FileName, offset), ioexc1); + + // max time waited on last retry = 250 + 10*550 = 5.75s + // aggregate time waited after 10 retries: 250 + 55*550 = 30.5s + System.Threading.Thread.Sleep(250 + retries * 550); + } + else + { + // The permission.Demand() failed. Therefore, we cannot call + // GetHRForException, and cannot do the subtle handling of + // ERROR_LOCK_VIOLATION. Just bail. + throw; + } + } +#endif + } + while (!done); + + return n; + } + + +#if !NETCF + // workitem 8009 + // + // This method must remain separate. + // + // Marshal.GetHRForException() is needed to do special exception handling for + // the read. But, that method requires UnmanagedCode permissions, and is marked + // with LinkDemand for UnmanagedCode. In an ASP.NET medium trust environment, + // where UnmanagedCode is restricted, will generate a SecurityException at the + // time of JIT of the method that calls a method that is marked with LinkDemand + // for UnmanagedCode. The SecurityException, if it is restricted, will occur + // when this method is JITed. + // + // The Marshal.GetHRForException() is factored out of ReadWithRetry in order to + // avoid the SecurityException at JIT compile time. Because _HRForException is + // called only when the UnmanagedCode is allowed. This means .NET never + // JIT-compiles this method when UnmanagedCode is disallowed, and thus never + // generates the JIT-compile time exception. + // +#endif + private static uint _HRForException(System.Exception ex1) + { + return unchecked((uint)System.Runtime.InteropServices.Marshal.GetHRForException(ex1)); + } + + } + + + + /// + /// A decorator stream. It wraps another stream, and performs bookkeeping + /// to keep track of the stream Position. + /// + /// + /// + /// In some cases, it is not possible to get the Position of a stream, let's + /// say, on a write-only output stream like ASP.NET's + /// Response.OutputStream, or on a different write-only stream + /// provided as the destination for the zip by the application. In this + /// case, programmers can use this counting stream to count the bytes read + /// or written. + /// + /// + /// Consider the scenario of an application that saves a self-extracting + /// archive (SFX), that uses a custom SFX stub. + /// + /// + /// Saving to a filesystem file, the application would open the + /// filesystem file (getting a FileStream), save the custom sfx stub + /// into it, and then call ZipFile.Save(), specifying the same + /// FileStream. ZipFile.Save() does the right thing for the zipentry + /// offsets, by inquiring the Position of the FileStream before writing + /// any data, and then adding that initial offset into any ZipEntry + /// offsets in the zip directory. Everything works fine. + /// + /// + /// Now suppose the application is an ASPNET application and it saves + /// directly to Response.OutputStream. It's not possible for DotNetZip to + /// inquire the Position, so the offsets for the SFX will be wrong. + /// + /// + /// The workaround is for the application to use this class to wrap + /// HttpResponse.OutputStream, then write the SFX stub and the ZipFile + /// into that wrapper stream. Because ZipFile.Save() can inquire the + /// Position, it will then do the right thing with the offsets. + /// + /// + public class CountingStream : System.IO.Stream + { + // workitem 12374: this class is now public + private System.IO.Stream _s; + private Int64 _bytesWritten; + private Int64 _bytesRead; + private Int64 _initialOffset; + + /// + /// The constructor. + /// + /// The underlying stream + public CountingStream(System.IO.Stream stream) + : base() + { + _s = stream; + try + { + _initialOffset = _s.Position; + } + catch + { + _initialOffset = 0L; + } + } + + /// + /// Gets the wrapped stream. + /// + public Stream WrappedStream + { + get + { + return _s; + } + } + + /// + /// The count of bytes written out to the stream. + /// + public Int64 BytesWritten + { + get { return _bytesWritten; } + } + + /// + /// the count of bytes that have been read from the stream. + /// + public Int64 BytesRead + { + get { return _bytesRead; } + } + + /// + /// Adjust the byte count on the stream. + /// + /// + /// + /// the number of bytes to subtract from the count. + /// + /// + /// + /// + /// Subtract delta from the count of bytes written to the stream. + /// This is necessary when seeking back, and writing additional data, + /// as happens in some cases when saving Zip files. + /// + /// + public void Adjust(Int64 delta) + { + _bytesWritten -= delta; + if (_bytesWritten < 0) + throw new InvalidOperationException(); + if (_s as CountingStream != null) + ((CountingStream)_s).Adjust(delta); + } + + /// + /// The read method. + /// + /// The buffer to hold the data read from the stream. + /// the offset within the buffer to copy the first byte read. + /// the number of bytes to read. + /// the number of bytes read, after decryption and decompression. + public override int Read(byte[] buffer, int offset, int count) + { + int n = _s.Read(buffer, offset, count); + _bytesRead += n; + return n; + } + + /// + /// Write data into the stream. + /// + /// The buffer holding data to write to the stream. + /// the offset within that data array to find the first byte to write. + /// the number of bytes to write. + public override void Write(byte[] buffer, int offset, int count) + { + if (count == 0) return; + _s.Write(buffer, offset, count); + _bytesWritten += count; + } + + /// + /// Whether the stream can be read. + /// + public override bool CanRead + { + get { return _s.CanRead; } + } + + /// + /// Whether it is possible to call Seek() on the stream. + /// + public override bool CanSeek + { + get { return _s.CanSeek; } + } + + /// + /// Whether it is possible to call Write() on the stream. + /// + public override bool CanWrite + { + get { return _s.CanWrite; } + } + + /// + /// Flushes the underlying stream. + /// + public override void Flush() + { + _s.Flush(); + } + + /// + /// The length of the underlying stream. + /// + public override long Length + { + get { return _s.Length; } // bytesWritten?? + } + + /// + /// Returns the sum of number of bytes written, plus the initial + /// offset before writing. + /// + public long ComputedPosition + { + get { return _initialOffset + _bytesWritten; } + } + + + /// + /// The Position of the stream. + /// + public override long Position + { + get { return _s.Position; } + set + { + _s.Seek(value, System.IO.SeekOrigin.Begin); + // workitem 10178 + Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(_s); + } + } + + /// + /// Seek in the stream. + /// + /// the offset point to seek to + /// the reference point from which to seek + /// The new position + public override long Seek(long offset, System.IO.SeekOrigin origin) + { + return _s.Seek(offset, origin); + } + + /// + /// Set the length of the underlying stream. Be careful with this! + /// + /// + /// the length to set on the underlying stream. + public override void SetLength(long value) + { + _s.SetLength(value); + } + } + + +} diff --git a/DotNetZip/Zip/WinZipAes.cs b/DotNetZip/Zip/WinZipAes.cs new file mode 100644 index 0000000..a316b91 --- /dev/null +++ b/DotNetZip/Zip/WinZipAes.cs @@ -0,0 +1,941 @@ +//#define Trace + +// WinZipAes.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2009-2011 Dino Chiesa. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2011-July-12 13:42:06> +// +// ------------------------------------------------------------------ +// +// This module defines the classes for dealing with WinZip's AES encryption, +// according to the specifications for the format available on WinZip's website. +// +// Created: January 2009 +// +// ------------------------------------------------------------------ + +using System; +using System.IO; +using System.Collections.Generic; +using System.Security.Cryptography; + +#if AESCRYPTO +namespace Ionic.Zip +{ + /// + /// This is a helper class supporting WinZip AES encryption. + /// This class is intended for use only by the DotNetZip library. + /// + /// + /// + /// Most uses of the DotNetZip library will not involve direct calls into + /// the WinZipAesCrypto class. Instead, the WinZipAesCrypto class is + /// instantiated and used by the ZipEntry() class when WinZip AES + /// encryption or decryption on an entry is employed. + /// + internal class WinZipAesCrypto + { + internal byte[] _Salt; + internal byte[] _providedPv; + internal byte[] _generatedPv; + internal int _KeyStrengthInBits; + private byte[] _MacInitializationVector; + private byte[] _StoredMac; + private byte[] _keyBytes; + private Int16 PasswordVerificationStored; + private Int16 PasswordVerificationGenerated; + private int Rfc2898KeygenIterations = 1000; + private string _Password; + private bool _cryptoGenerated ; + + private WinZipAesCrypto(string password, int KeyStrengthInBits) + { + _Password = password; + _KeyStrengthInBits = KeyStrengthInBits; + } + + public static WinZipAesCrypto Generate(string password, int KeyStrengthInBits) + { + WinZipAesCrypto c = new WinZipAesCrypto(password, KeyStrengthInBits); + + int saltSizeInBytes = c._KeyStrengthInBytes / 2; + c._Salt = new byte[saltSizeInBytes]; + Random rnd = new Random(); + rnd.NextBytes(c._Salt); + return c; + } + + + + public static WinZipAesCrypto ReadFromStream(string password, int KeyStrengthInBits, Stream s) + { + // from http://www.winzip.com/aes_info.htm + // + // Size(bytes) Content + // ----------------------------------- + // Variable Salt value + // 2 Password verification value + // Variable Encrypted file data + // 10 Authentication code + // + // ZipEntry.CompressedSize represents the size of all of those elements. + + // salt size varies with key length: + // 128 bit key => 8 bytes salt + // 192 bits => 12 bytes salt + // 256 bits => 16 bytes salt + + WinZipAesCrypto c = new WinZipAesCrypto(password, KeyStrengthInBits); + + int saltSizeInBytes = c._KeyStrengthInBytes / 2; + c._Salt = new byte[saltSizeInBytes]; + c._providedPv = new byte[2]; + + s.Read(c._Salt, 0, c._Salt.Length); + s.Read(c._providedPv, 0, c._providedPv.Length); + + c.PasswordVerificationStored = (Int16)(c._providedPv[0] + c._providedPv[1] * 256); + if (password != null) + { + c.PasswordVerificationGenerated = (Int16)(c.GeneratedPV[0] + c.GeneratedPV[1] * 256); + if (c.PasswordVerificationGenerated != c.PasswordVerificationStored) + throw new BadPasswordException("bad password"); + } + + return c; + } + + public byte[] GeneratedPV + { + get + { + if (!_cryptoGenerated) _GenerateCryptoBytes(); + return _generatedPv; + } + } + + + public byte[] Salt + { + get + { + return _Salt; + } + } + + + private int _KeyStrengthInBytes + { + get + { + return _KeyStrengthInBits / 8; + + } + } + + public int SizeOfEncryptionMetadata + { + get + { + // 10 bytes after, (n-10) before the compressed data + return _KeyStrengthInBytes / 2 + 10 + 2; + } + } + + public string Password + { + set + { + _Password = value; + if (_Password != null) + { + PasswordVerificationGenerated = (Int16)(GeneratedPV[0] + GeneratedPV[1] * 256); + if (PasswordVerificationGenerated != PasswordVerificationStored) + throw new Ionic.Zip.BadPasswordException(); + } + } + private get + { + return _Password; + } + } + + + private void _GenerateCryptoBytes() + { + //Console.WriteLine(" provided password: '{0}'", _Password); + + System.Security.Cryptography.Rfc2898DeriveBytes rfc2898 = + new System.Security.Cryptography.Rfc2898DeriveBytes(_Password, Salt, Rfc2898KeygenIterations); + + _keyBytes = rfc2898.GetBytes(_KeyStrengthInBytes); // 16 or 24 or 32 ??? + _MacInitializationVector = rfc2898.GetBytes(_KeyStrengthInBytes); + _generatedPv = rfc2898.GetBytes(2); + + _cryptoGenerated = true; + } + + + public byte[] KeyBytes + { + get + { + if (!_cryptoGenerated) _GenerateCryptoBytes(); + return _keyBytes; + } + } + + + public byte[] MacIv + { + get + { + if (!_cryptoGenerated) _GenerateCryptoBytes(); + return _MacInitializationVector; + } + } + + public byte[] CalculatedMac; + + + public void ReadAndVerifyMac(System.IO.Stream s) + { + bool invalid = false; + + // read integrityCheckVector. + // caller must ensure that the file pointer is in the right spot! + _StoredMac = new byte[10]; // aka "authentication code" + s.Read(_StoredMac, 0, _StoredMac.Length); + + if (_StoredMac.Length != CalculatedMac.Length) + invalid = true; + + if (!invalid) + { + for (int i = 0; i < _StoredMac.Length; i++) + { + if (_StoredMac[i] != CalculatedMac[i]) + invalid = true; + } + } + + if (invalid) + throw new Ionic.Zip.BadStateException("The MAC does not match."); + } + + } + + + #region DONT_COMPILE_BUT_KEEP_FOR_POTENTIAL_FUTURE_USE +#if NO + internal class Util + { + private static void _Format(System.Text.StringBuilder sb1, + byte[] b, + int offset, + int length) + { + + System.Text.StringBuilder sb2 = new System.Text.StringBuilder(); + sb1.Append("0000 "); + int i; + for (i = 0; i < length; i++) + { + int x = offset+i; + if (i != 0 && i % 16 == 0) + { + sb1.Append(" ") + .Append(sb2) + .Append("\n") + .Append(String.Format("{0:X4} ", i)); + sb2.Remove(0,sb2.Length); + } + sb1.Append(System.String.Format("{0:X2} ", b[x])); + if (b[x] >=32 && b[x] <= 126) + sb2.Append((char)b[x]); + else + sb2.Append("."); + } + if (sb2.Length > 0) + { + sb1.Append(new String(' ', ((16 - i%16) * 3) + 4)) + .Append(sb2); + } + } + + + + internal static string FormatByteArray(byte[] b, int limit) + { + System.Text.StringBuilder sb1 = new System.Text.StringBuilder(); + + if ((limit * 2 > b.Length) || limit == 0) + { + _Format(sb1, b, 0, b.Length); + } + else + { + // first N bytes of the buffer + _Format(sb1, b, 0, limit); + + if (b.Length > limit * 2) + sb1.Append(String.Format("\n ...({0} other bytes here)....\n", b.Length - limit * 2)); + + // last N bytes of the buffer + _Format(sb1, b, b.Length - limit, limit); + } + + return sb1.ToString(); + } + + + internal static string FormatByteArray(byte[] b) + { + return FormatByteArray(b, 0); + } + } + +#endif + #endregion + + + + + /// + /// A stream that encrypts as it writes, or decrypts as it reads. The + /// Crypto is AES in CTR (counter) mode, which is compatible with the AES + /// encryption employed by WinZip 12.0. + /// + /// + /// + /// The AES/CTR encryption protocol used by WinZip works like this: + /// + /// - start with a counter, initialized to zero. + /// + /// - to encrypt, take the data by 16-byte blocks. For each block: + /// - apply the transform to the counter + /// - increement the counter + /// - XOR the result of the transform with the plaintext to + /// get the ciphertext. + /// - compute the mac on the encrypted bytes + /// - when finished with all blocks, store the computed MAC. + /// + /// - to decrypt, take the data by 16-byte blocks. For each block: + /// - compute the mac on the encrypted bytes, + /// - apply the transform to the counter + /// - increement the counter + /// - XOR the result of the transform with the ciphertext to + /// get the plaintext. + /// - when finished with all blocks, compare the computed MAC against + /// the stored MAC + /// + /// + /// + // + internal class WinZipAesCipherStream : Stream + { + private WinZipAesCrypto _params; + private System.IO.Stream _s; + private CryptoMode _mode; + private int _nonce; + private bool _finalBlock; + + internal HMACSHA1 _mac; + + // Use RijndaelManaged from .NET 2.0. + // AesManaged came in .NET 3.5, but we want to limit + // dependency to .NET 2.0. AES is just a restricted form + // of Rijndael (fixed block size of 128, some crypto modes not supported). + + internal RijndaelManaged _aesCipher; + internal ICryptoTransform _xform; + + private const int BLOCK_SIZE_IN_BYTES = 16; + + private byte[] counter = new byte[BLOCK_SIZE_IN_BYTES]; + private byte[] counterOut = new byte[BLOCK_SIZE_IN_BYTES]; + + // I've had a problem when wrapping a WinZipAesCipherStream inside + // a DeflateStream. Calling Read() on the DeflateStream results in + // a Read() on the WinZipAesCipherStream, but the buffer is larger + // than the total size of the encrypted data, and larger than the + // initial Read() on the DeflateStream! When the encrypted + // bytestream is embedded within a larger stream (As in a zip + // archive), the Read() doesn't fail with EOF. This causes bad + // data to be returned, and it messes up the MAC. + + // This field is used to provide a hard-stop to the size of + // data that can be read from the stream. In Read(), if the buffer or + // read request goes beyond the stop, we truncate it. + + private long _length; + private long _totalBytesXferred; + private byte[] _PendingWriteBlock; + private int _pendingCount; + private byte[] _iobuf; + + /// + /// The constructor. + /// + /// The underlying stream + /// To either encrypt or decrypt. + /// The pre-initialized WinZipAesCrypto object. + /// The maximum number of bytes to read from the stream. + internal WinZipAesCipherStream(System.IO.Stream s, WinZipAesCrypto cryptoParams, long length, CryptoMode mode) + : this(s, cryptoParams, mode) + { + // don't read beyond this limit! + _length = length; + //Console.WriteLine("max length of AES stream: {0}", _length); + } + + +#if WANT_TRACE + Stream untransformed; + String traceFileUntransformed; + Stream transformed; + String traceFileTransformed; +#endif + + + internal WinZipAesCipherStream(System.IO.Stream s, WinZipAesCrypto cryptoParams, CryptoMode mode) + : base() + { + TraceOutput("-------------------------------------------------------"); + TraceOutput("Create {0:X8}", this.GetHashCode()); + + _params = cryptoParams; + _s = s; + _mode = mode; + _nonce = 1; + + if (_params == null) + throw new BadPasswordException("Supply a password to use AES encryption."); + + int keySizeInBits = _params.KeyBytes.Length * 8; + if (keySizeInBits != 256 && keySizeInBits != 128 && keySizeInBits != 192) + throw new ArgumentOutOfRangeException("keysize", + "size of key must be 128, 192, or 256"); + + _mac = new HMACSHA1(_params.MacIv); + + _aesCipher = new System.Security.Cryptography.RijndaelManaged(); + _aesCipher.BlockSize = 128; + _aesCipher.KeySize = keySizeInBits; // 128, 192, 256 + _aesCipher.Mode = CipherMode.ECB; + _aesCipher.Padding = PaddingMode.None; + + byte[] iv = new byte[BLOCK_SIZE_IN_BYTES]; // all zeroes + + // Create an ENCRYPTOR, regardless whether doing decryption or encryption. + // It is reflexive. + _xform = _aesCipher.CreateEncryptor(_params.KeyBytes, iv); + + if (_mode == CryptoMode.Encrypt) + { + _iobuf = new byte[2048]; + _PendingWriteBlock = new byte[BLOCK_SIZE_IN_BYTES]; + } + + +#if WANT_TRACE + traceFileUntransformed = "unpack\\WinZipAesCipherStream.trace.untransformed.out"; + traceFileTransformed = "unpack\\WinZipAesCipherStream.trace.transformed.out"; + + untransformed = System.IO.File.Create(traceFileUntransformed); + transformed = System.IO.File.Create(traceFileTransformed); +#endif + } + + private void XorInPlace(byte[] buffer, int offset, int count) + { + for (int i = 0; i < count; i++) + { + buffer[offset + i] = (byte)(counterOut[i] ^ buffer[offset + i]); + } + } + + private void WriteTransformOneBlock(byte[] buffer, int offset) + { + System.Array.Copy(BitConverter.GetBytes(_nonce++), 0, counter, 0, 4); + _xform.TransformBlock(counter, + 0, + BLOCK_SIZE_IN_BYTES, + counterOut, + 0); + XorInPlace(buffer, offset, BLOCK_SIZE_IN_BYTES); + _mac.TransformBlock(buffer, offset, BLOCK_SIZE_IN_BYTES, null, 0); + } + + + private void WriteTransformBlocks(byte[] buffer, int offset, int count) + { + int posn = offset; + int last = count + offset; + + while (posn < buffer.Length && posn < last) + { + WriteTransformOneBlock (buffer, posn); + posn += BLOCK_SIZE_IN_BYTES; + } + } + + + private void WriteTransformFinalBlock() + { + if (_pendingCount == 0) + throw new InvalidOperationException("No bytes available."); + + if (_finalBlock) + throw new InvalidOperationException("The final block has already been transformed."); + + System.Array.Copy(BitConverter.GetBytes(_nonce++), 0, counter, 0, 4); + counterOut = _xform.TransformFinalBlock(counter, + 0, + BLOCK_SIZE_IN_BYTES); + XorInPlace(_PendingWriteBlock, 0, _pendingCount); + _mac.TransformFinalBlock(_PendingWriteBlock, 0, _pendingCount); + _finalBlock = true; + } + + + + + + private int ReadTransformOneBlock(byte[] buffer, int offset, int last) + { + if (_finalBlock) + throw new NotSupportedException(); + + int bytesRemaining = last - offset; + int bytesToRead = (bytesRemaining > BLOCK_SIZE_IN_BYTES) + ? BLOCK_SIZE_IN_BYTES + : bytesRemaining; + + // update the counter + System.Array.Copy(BitConverter.GetBytes(_nonce++), 0, counter, 0, 4); + + // Determine if this is the final block + if ((bytesToRead == bytesRemaining) && + (_length > 0) && + (_totalBytesXferred + last == _length)) + { + _mac.TransformFinalBlock(buffer, offset, bytesToRead); + counterOut = _xform.TransformFinalBlock(counter, + 0, + BLOCK_SIZE_IN_BYTES); + _finalBlock = true; + } + else + { + _mac.TransformBlock(buffer, offset, bytesToRead, null, 0); + _xform.TransformBlock(counter, + 0, // offset + BLOCK_SIZE_IN_BYTES, + counterOut, + 0); // offset + } + + XorInPlace(buffer, offset, bytesToRead); + return bytesToRead; + } + + + + private void ReadTransformBlocks(byte[] buffer, int offset, int count) + { + int posn = offset; + int last = count + offset; + + while (posn < buffer.Length && posn < last ) + { + int n = ReadTransformOneBlock (buffer, posn, last); + posn += n; + } + } + + + + public override int Read(byte[] buffer, int offset, int count) + { + if (_mode == CryptoMode.Encrypt) + throw new NotSupportedException(); + + if (buffer == null) + throw new ArgumentNullException("buffer"); + + if (offset < 0) + throw new ArgumentOutOfRangeException("offset", + "Must not be less than zero."); + if (count < 0) + throw new ArgumentOutOfRangeException("count", + "Must not be less than zero."); + + if (buffer.Length < offset + count) + throw new ArgumentException("The buffer is too small"); + + // When I wrap a WinZipAesStream in a DeflateStream, the + // DeflateStream asks its captive to read 4k blocks, even if the + // encrypted bytestream is smaller than that. This is a way to + // limit the number of bytes read. + + int bytesToRead = count; + + if (_totalBytesXferred >= _length) + { + return 0; // EOF + } + + long bytesRemaining = _length - _totalBytesXferred; + if (bytesRemaining < count) bytesToRead = (int)bytesRemaining; + + int n = _s.Read(buffer, offset, bytesToRead); + + +#if WANT_TRACE + untransformed.Write(buffer, offset, bytesToRead); +#endif + + ReadTransformBlocks(buffer, offset, bytesToRead); + +#if WANT_TRACE + transformed.Write(buffer, offset, bytesToRead); +#endif + _totalBytesXferred += n; + return n; + } + + + + /// + /// Returns the final HMAC-SHA1-80 for the data that was encrypted. + /// + public byte[] FinalAuthentication + { + get + { + if (!_finalBlock) + { + // special-case zero-byte files + if ( _totalBytesXferred != 0) + throw new BadStateException("The final hash has not been computed."); + + // Must call ComputeHash on an empty byte array when no data + // has run through the MAC. + + byte[] b = { }; + _mac.ComputeHash(b); + // fall through + } + byte[] macBytes10 = new byte[10]; + System.Array.Copy(_mac.Hash, 0, macBytes10, 0, 10); + return macBytes10; + } + } + + + public override void Write(byte[] buffer, int offset, int count) + { + if (_finalBlock) + throw new InvalidOperationException("The final block has already been transformed."); + + if (_mode == CryptoMode.Decrypt) + throw new NotSupportedException(); + + if (buffer == null) + throw new ArgumentNullException("buffer"); + + if (offset < 0) + throw new ArgumentOutOfRangeException("offset", + "Must not be less than zero."); + if (count < 0) + throw new ArgumentOutOfRangeException("count", + "Must not be less than zero."); + if (buffer.Length < offset + count) + throw new ArgumentException("The offset and count are too large"); + + if (count == 0) + return; + + TraceOutput("Write off({0}) count({1})", offset, count); + +#if WANT_TRACE + untransformed.Write(buffer, offset, count); +#endif + + // For proper AES encryption, an AES encryptor application calls + // TransformBlock repeatedly for all 16-byte blocks except the + // last. For the last block, it then calls TransformFinalBlock(). + // + // This class is a stream that encrypts via Write(). But, it's not + // possible to recognize which are the "last" bytes from within the call + // to Write(). The caller can call Write() several times in succession, + // with varying buffers. This class only "knows" that the last bytes + // have been written when the app calls Close(). + // + // Therefore, this class buffers writes: After completion every Write(), + // a 16-byte "pending" block (_PendingWriteBlock) must hold between 1 + // and 16 bytes, which will be used in TransformFinalBlock if the app + // calls Close() immediately thereafter. Also, every write must + // transform any pending bytes, before transforming the data passed in + // to the current call. + // + // In operation, after the first call to Write() and before the call to + // Close(), one full or partial block of bytes is always available, + // pending. At time of Close(), this class calls + // WriteTransformFinalBlock() to flush the pending bytes. + // + // This approach works whether the caller writes in odd-sized batches, + // for example 5000 bytes, or in batches that are neat multiples of the + // blocksize (16). + // + // Logicaly, what we do is this: + // + // 1. if there are fewer than 16 bytes (pending + current), then + // just copy them into th pending buffer and return. + // + // 2. there are more than 16 bytes to write. So, take the leading slice + // of bytes from the current buffer, enough to fill the pending + // buffer. Transform the pending block, and write it out. + // + // 3. Take the trailing slice of bytes (a full block or a partial block), + // and copy it to the pending block for next time. + // + // 4. transform and write all the other blocks, the middle slice. + // + + // There are 16 or fewer bytes, so just buffer the bytes. + if (count + _pendingCount <= BLOCK_SIZE_IN_BYTES) + { + Buffer.BlockCopy(buffer, + offset, + _PendingWriteBlock, + _pendingCount, + count); + _pendingCount += count; + + // At this point, _PendingWriteBlock contains up to + // BLOCK_SIZE_IN_BYTES bytes, and _pendingCount ranges from 0 to + // BLOCK_SIZE_IN_BYTES. We don't want to xform+write them yet, + // because this may have been the last block. The last block gets + // written at Close(). + return; + } + + // We know there are at least 17 bytes, counting those in the current + // buffer, along with the (possibly empty) pending block. + + int bytesRemaining = count; + int curOffset = offset; + + // workitem 12815 + // + // xform chunkwise ... Cannot transform in place using the original + // buffer because that is user-maintained. + + if (_pendingCount != 0) + { + // We have more than one block of data to write, therefore it is safe + // to xform+write. + int fillCount = BLOCK_SIZE_IN_BYTES - _pendingCount; + + // fillCount is possibly zero here. That happens when the pending + // buffer held 16 bytes (one complete block) before this call to + // Write. + if (fillCount > 0) + { + Buffer.BlockCopy(buffer, + offset, + _PendingWriteBlock, + _pendingCount, + fillCount); + + // adjust counts: + bytesRemaining -= fillCount; + curOffset += fillCount; + } + + // xform and write: + WriteTransformOneBlock(_PendingWriteBlock, 0); + _s.Write(_PendingWriteBlock, 0, BLOCK_SIZE_IN_BYTES); + _totalBytesXferred += BLOCK_SIZE_IN_BYTES; + _pendingCount = 0; + } + + // At this point _PendingWriteBlock is empty, and bytesRemaining is + // always greater than 0. + + // Now, xform N blocks, where N = floor((bytesRemaining-1)/16). If + // writing 32 bytes, then xform 1 block, and stage the remaining 16. If + // writing 10037 bytes, xform 627 blocks of 16 bytes, then stage the + // remaining 5 bytes. + + int blocksToXform = (bytesRemaining-1)/BLOCK_SIZE_IN_BYTES; + _pendingCount = bytesRemaining - (blocksToXform * BLOCK_SIZE_IN_BYTES); + + // _pendingCount is ALWAYS between 1 and 16. + // Put the last _pendingCount bytes into the pending block. + Buffer.BlockCopy(buffer, + curOffset + bytesRemaining - _pendingCount, + _PendingWriteBlock, + 0, + _pendingCount); + bytesRemaining -= _pendingCount; + _totalBytesXferred += bytesRemaining; // will be true after the loop + + // now, transform all the full blocks preceding that. + // bytesRemaining is always a multiple of 16 . + if (blocksToXform > 0) + { + do + { + int c = _iobuf.Length; + if (c > bytesRemaining) c = bytesRemaining; + Buffer.BlockCopy(buffer, + curOffset, + _iobuf, + 0, + c); + + WriteTransformBlocks(_iobuf, 0, c); + _s.Write(_iobuf, 0, c); + bytesRemaining -= c; + curOffset += c; + } while(bytesRemaining > 0); + } + } + + + + /// + /// Close the stream. + /// + public override void Close() + { + TraceOutput("Close {0:X8}", this.GetHashCode()); + + // In the degenerate case, no bytes have been written to the + // stream at all. Need to check here, and NOT emit the + // final block if Write has not been called. + if (_pendingCount > 0) + { + WriteTransformFinalBlock(); + _s.Write(_PendingWriteBlock, 0, _pendingCount); + _totalBytesXferred += _pendingCount; + _pendingCount = 0; + } + _s.Close(); + +#if WANT_TRACE + untransformed.Close(); + transformed.Close(); + Console.WriteLine("\nuntransformed bytestream is in {0}", traceFileUntransformed); + Console.WriteLine("\ntransformed bytestream is in {0}", traceFileTransformed); +#endif + TraceOutput("-------------------------------------------------------"); + } + + + /// + /// Returns true if the stream can be read. + /// + public override bool CanRead + { + get + { + if (_mode != CryptoMode.Decrypt) return false; + return true; + } + } + + + /// + /// Always returns false. + /// + public override bool CanSeek + { + get { return false; } + } + + /// + /// Returns true if the CryptoMode is Encrypt. + /// + public override bool CanWrite + { + get { return (_mode == CryptoMode.Encrypt); } + } + + /// + /// Flush the content in the stream. + /// + public override void Flush() + { + _s.Flush(); + } + + /// + /// Getting this property throws a NotImplementedException. + /// + public override long Length + { + get { throw new NotImplementedException(); } + } + + /// + /// Getting or Setting this property throws a NotImplementedException. + /// + public override long Position + { + get { throw new NotImplementedException(); } + set { throw new NotImplementedException(); } + } + + /// + /// This method throws a NotImplementedException. + /// + public override long Seek(long offset, System.IO.SeekOrigin origin) + { + throw new NotImplementedException(); + } + + /// + /// This method throws a NotImplementedException. + /// + public override void SetLength(long value) + { + throw new NotImplementedException(); + } + + + + [System.Diagnostics.ConditionalAttribute("Trace")] + private void TraceOutput(string format, params object[] varParams) + { + lock(_outputLock) + { + int tid = System.Threading.Thread.CurrentThread.GetHashCode(); + Console.ForegroundColor = (ConsoleColor) (tid % 8 + 8); + Console.Write("{0:000} WZACS ", tid); + Console.WriteLine(format, varParams); + Console.ResetColor(); + } + } + + private object _outputLock = new Object(); + } +} +#endif diff --git a/DotNetZip/Zip/Zip DLL.csproj b/DotNetZip/Zip/Zip DLL.csproj new file mode 100644 index 0000000..f0a7a27 --- /dev/null +++ b/DotNetZip/Zip/Zip DLL.csproj @@ -0,0 +1,231 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {D3B0AD67-44D8-4B3D-BED9-CE1FD6DE2C5A} + Library + Properties + Ionic.Zip + Ionic.Zip + false + ..\Ionic.snk + + + + + 3.5 + false + SAK + SAK + SAK + SAK + v4.0 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + true + + + + + + true + full + false + bin\Debug\ + TRACE;DEBUG;AESCRYPTO;BZIP + prompt + 4 + bin\Debug\Ionic.Zip.xml + false + + + AllRules.ruleset + + + pdbonly + true + bin\Release\ + TRACE;AESCRYPTO;BZIP + prompt + 4 + bin\Release\Ionic.Zip.xml + AllRules.ruleset + + + + ..\..\packages\Newtonsoft.Json.12.0.1\lib\net40\Newtonsoft.Json.dll + True + + + + + ..\..\packages\System.Data.SQLite.Core.1.0.110.0\lib\net40\System.Data.SQLite.dll + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BZip2\BitWriter + + + BZip2\BZip2Compressor + + + BZip2\BZip2InputStream + + + BZip2\BZip2OutputStream + + + BZip2\ParallelBZip2OutputStream + + + BZip2\Rand + + + CRC32.cs + + + Properties\SolutionInfo.cs + + + + + + + + + PasswordDialog.cs + + + + WinFormsSelfExtractorStub.cs + + + + ZipContentsDialog.cs + + + + + + + + Ionic.snk + + + + + PasswordDialog.cs + + + WinFormsSelfExtractorStub.cs + + + ZipContentsDialog.cs + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 2.0 %28x86%29 + true + + + False + .NET Framework 3.0 %28x86%29 + false + + + False + .NET Framework 3.5 + false + + + False + .NET Framework 3.5 SP1 + false + + + + + {9816ba86-9250-4c00-a912-25f07f8677d1} + Zlib DLL + + + + + + + + + + + + + + Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}". + + + + \ No newline at end of file diff --git a/DotNetZip/Zip/ZipConstants.cs b/DotNetZip/Zip/ZipConstants.cs new file mode 100644 index 0000000..cc5eec1 --- /dev/null +++ b/DotNetZip/Zip/ZipConstants.cs @@ -0,0 +1,51 @@ +// ZipConstants.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2006, 2007, 2008, 2009 Dino Chiesa and Microsoft Corporation. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2009-August-27 23:22:32> +// +// ------------------------------------------------------------------ +// +// This module defines a few constants that are used in the project. +// +// ------------------------------------------------------------------ + +using System; + +namespace Ionic.Zip +{ + static class ZipConstants + { + public const UInt32 PackedToRemovableMedia = 0x30304b50; + public const UInt32 Zip64EndOfCentralDirectoryRecordSignature = 0x06064b50; + public const UInt32 Zip64EndOfCentralDirectoryLocatorSignature = 0x07064b50; + public const UInt32 EndOfCentralDirectorySignature = 0x06054b50; + public const int ZipEntrySignature = 0x04034b50; + public const int ZipEntryDataDescriptorSignature = 0x08074b50; + public const int SplitArchiveSignature = 0x08074b50; + public const int ZipDirEntrySignature = 0x02014b50; + + + // These are dictated by the Zip Spec.See APPNOTE.txt + public const int AesKeySize = 192; // 128, 192, 256 + public const int AesBlockSize = 128; // ??? + + public const UInt16 AesAlgId128 = 0x660E; + public const UInt16 AesAlgId192 = 0x660F; + public const UInt16 AesAlgId256 = 0x6610; + + } +} diff --git a/DotNetZip/Zip/ZipCrypto.cs b/DotNetZip/Zip/ZipCrypto.cs new file mode 100644 index 0000000..a8c0b5f --- /dev/null +++ b/DotNetZip/Zip/ZipCrypto.cs @@ -0,0 +1,455 @@ +// ZipCrypto.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2008, 2009, 2011 Dino Chiesa +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2011-July-28 06:30:59> +// +// ------------------------------------------------------------------ +// +// This module provides the implementation for "traditional" Zip encryption. +// +// Created Tue Apr 15 17:39:56 2008 +// +// ------------------------------------------------------------------ + +using System; + +namespace Ionic.Zip +{ + /// + /// This class implements the "traditional" or "classic" PKZip encryption, + /// which today is considered to be weak. On the other hand it is + /// ubiquitous. This class is intended for use only by the DotNetZip + /// library. + /// + /// + /// + /// Most uses of the DotNetZip library will not involve direct calls into + /// the ZipCrypto class. Instead, the ZipCrypto class is instantiated and + /// used by the ZipEntry() class when encryption or decryption on an entry + /// is employed. If for some reason you really wanted to use a weak + /// encryption algorithm in some other application, you might use this + /// library. But you would be much better off using one of the built-in + /// strong encryption libraries in the .NET Framework, like the AES + /// algorithm or SHA. + /// + internal class ZipCrypto + { + /// + /// The default constructor for ZipCrypto. + /// + /// + /// + /// This class is intended for internal use by the library only. It's + /// probably not useful to you. Seriously. Stop reading this + /// documentation. It's a waste of your time. Go do something else. + /// Check the football scores. Go get an ice cream with a friend. + /// Seriously. + /// + /// + private ZipCrypto() { } + + public static ZipCrypto ForWrite(string password) + { + ZipCrypto z = new ZipCrypto(); + if (password == null) + throw new BadPasswordException("This entry requires a password."); + z.InitCipher(password); + return z; + } + + + public static ZipCrypto ForRead(string password, ZipEntry e) + { + System.IO.Stream s = e._archiveStream; + e._WeakEncryptionHeader = new byte[12]; + byte[] eh = e._WeakEncryptionHeader; + ZipCrypto z = new ZipCrypto(); + + if (password == null) + throw new BadPasswordException("This entry requires a password."); + + z.InitCipher(password); + + ZipEntry.ReadWeakEncryptionHeader(s, eh); + + // Decrypt the header. This has a side effect of "further initializing the + // encryption keys" in the traditional zip encryption. + byte[] DecryptedHeader = z.DecryptMessage(eh, eh.Length); + + // CRC check + // According to the pkzip spec, the final byte in the decrypted header + // is the highest-order byte in the CRC. We check it here. + if (DecryptedHeader[11] != (byte)((e._Crc32 >> 24) & 0xff)) + { + // In the case that bit 3 of the general purpose bit flag is set to + // indicate the presence of an 'Extended File Header' or a 'data + // descriptor' (signature 0x08074b50), the last byte of the decrypted + // header is sometimes compared with the high-order byte of the + // lastmodified time, rather than the high-order byte of the CRC, to + // verify the password. + // + // This is not documented in the PKWare Appnote.txt. It was + // discovered this by analysis of the Crypt.c source file in the + // InfoZip library http://www.info-zip.org/pub/infozip/ + // + // The reason for this is that the CRC for a file cannot be known + // until the entire contents of the file have been streamed. This + // means a tool would have to read the file content TWICE in its + // entirety in order to perform PKZIP encryption - once to compute + // the CRC, and again to actually encrypt. + // + // This is so important for performance that using the timeblob as + // the verification should be the standard practice for DotNetZip + // when using PKZIP encryption. This implies that bit 3 must be + // set. The downside is that some tools still cannot cope with ZIP + // files that use bit 3. Therefore, DotNetZip DOES NOT force bit 3 + // when PKZIP encryption is in use, and instead, reads the stream + // twice. + // + + if ((e._BitField & 0x0008) != 0x0008) + { + throw new BadPasswordException("The password did not match."); + } + else if (DecryptedHeader[11] != (byte)((e._TimeBlob >> 8) & 0xff)) + { + throw new BadPasswordException("The password did not match."); + } + + // We have a good password. + } + else + { + // A-OK + } + return z; + } + + + + + /// + /// From AppNote.txt: + /// unsigned char decrypt_byte() + /// local unsigned short temp + /// temp :=- Key(2) | 2 + /// decrypt_byte := (temp * (temp ^ 1)) bitshift-right 8 + /// end decrypt_byte + /// + private byte MagicByte + { + get + { + UInt16 t = (UInt16)((UInt16)(_Keys[2] & 0xFFFF) | 2); + return (byte)((t * (t ^ 1)) >> 8); + } + } + + // Decrypting: + // From AppNote.txt: + // loop for i from 0 to 11 + // C := buffer(i) ^ decrypt_byte() + // update_keys(C) + // buffer(i) := C + // end loop + + + /// + /// Call this method on a cipher text to render the plaintext. You must + /// first initialize the cipher with a call to InitCipher. + /// + /// + /// + /// + /// var cipher = new ZipCrypto(); + /// cipher.InitCipher(Password); + /// // Decrypt the header. This has a side effect of "further initializing the + /// // encryption keys" in the traditional zip encryption. + /// byte[] DecryptedMessage = cipher.DecryptMessage(EncryptedMessage); + /// + /// + /// + /// The encrypted buffer. + /// + /// The number of bytes to encrypt. + /// Should be less than or equal to CipherText.Length. + /// + /// + /// The plaintext. + public byte[] DecryptMessage(byte[] cipherText, int length) + { + if (cipherText == null) + throw new ArgumentNullException("cipherText"); + + if (length > cipherText.Length) + throw new ArgumentOutOfRangeException("length", + "Bad length during Decryption: the length parameter must be smaller than or equal to the size of the destination array."); + + byte[] plainText = new byte[length]; + for (int i = 0; i < length; i++) + { + byte C = (byte)(cipherText[i] ^ MagicByte); + UpdateKeys(C); + plainText[i] = C; + } + return plainText; + } + + /// + /// This is the converse of DecryptMessage. It encrypts the plaintext + /// and produces a ciphertext. + /// + /// + /// The plain text buffer. + /// + /// + /// The number of bytes to encrypt. + /// Should be less than or equal to plainText.Length. + /// + /// + /// The ciphertext. + public byte[] EncryptMessage(byte[] plainText, int length) + { + if (plainText == null) + throw new ArgumentNullException("plaintext"); + + if (length > plainText.Length) + throw new ArgumentOutOfRangeException("length", + "Bad length during Encryption: The length parameter must be smaller than or equal to the size of the destination array."); + + byte[] cipherText = new byte[length]; + for (int i = 0; i < length; i++) + { + byte C = plainText[i]; + cipherText[i] = (byte)(plainText[i] ^ MagicByte); + UpdateKeys(C); + } + return cipherText; + } + + + /// + /// This initializes the cipher with the given password. + /// See AppNote.txt for details. + /// + /// + /// + /// The passphrase for encrypting or decrypting with this cipher. + /// + /// + /// + /// + /// Step 1 - Initializing the encryption keys + /// ----------------------------------------- + /// Start with these keys: + /// Key(0) := 305419896 (0x12345678) + /// Key(1) := 591751049 (0x23456789) + /// Key(2) := 878082192 (0x34567890) + /// + /// Then, initialize the keys with a password: + /// + /// loop for i from 0 to length(password)-1 + /// update_keys(password(i)) + /// end loop + /// + /// Where update_keys() is defined as: + /// + /// update_keys(char): + /// Key(0) := crc32(key(0),char) + /// Key(1) := Key(1) + (Key(0) bitwiseAND 000000ffH) + /// Key(1) := Key(1) * 134775813 + 1 + /// Key(2) := crc32(key(2),key(1) rightshift 24) + /// end update_keys + /// + /// Where crc32(old_crc,char) is a routine that given a CRC value and a + /// character, returns an updated CRC value after applying the CRC-32 + /// algorithm described elsewhere in this document. + /// + /// + /// + /// + /// After the keys are initialized, then you can use the cipher to + /// encrypt the plaintext. + /// + /// + /// + /// Essentially we encrypt the password with the keys, then discard the + /// ciphertext for the password. This initializes the keys for later use. + /// + /// + /// + public void InitCipher(string passphrase) + { + byte[] p = SharedUtilities.StringToByteArray(passphrase); + for (int i = 0; i < passphrase.Length; i++) + UpdateKeys(p[i]); + } + + + private void UpdateKeys(byte byteValue) + { + _Keys[0] = (UInt32)crc32.ComputeCrc32((int)_Keys[0], byteValue); + _Keys[1] = _Keys[1] + (byte)_Keys[0]; + _Keys[1] = _Keys[1] * 0x08088405 + 1; + _Keys[2] = (UInt32)crc32.ComputeCrc32((int)_Keys[2], (byte)(_Keys[1] >> 24)); + } + + ///// + ///// The byte array representing the seed keys used. + ///// Get this after calling InitCipher. The 12 bytes represents + ///// what the zip spec calls the "EncryptionHeader". + ///// + //public byte[] KeyHeader + //{ + // get + // { + // byte[] result = new byte[12]; + // result[0] = (byte)(_Keys[0] & 0xff); + // result[1] = (byte)((_Keys[0] >> 8) & 0xff); + // result[2] = (byte)((_Keys[0] >> 16) & 0xff); + // result[3] = (byte)((_Keys[0] >> 24) & 0xff); + // result[4] = (byte)(_Keys[1] & 0xff); + // result[5] = (byte)((_Keys[1] >> 8) & 0xff); + // result[6] = (byte)((_Keys[1] >> 16) & 0xff); + // result[7] = (byte)((_Keys[1] >> 24) & 0xff); + // result[8] = (byte)(_Keys[2] & 0xff); + // result[9] = (byte)((_Keys[2] >> 8) & 0xff); + // result[10] = (byte)((_Keys[2] >> 16) & 0xff); + // result[11] = (byte)((_Keys[2] >> 24) & 0xff); + // return result; + // } + //} + + // private fields for the crypto stuff: + private UInt32[] _Keys = { 0x12345678, 0x23456789, 0x34567890 }; + private Ionic.Crc.CRC32 crc32 = new Ionic.Crc.CRC32(); + + } + + internal enum CryptoMode + { + Encrypt, + Decrypt + } + + /// + /// A Stream for reading and concurrently decrypting data from a zip file, + /// or for writing and concurrently encrypting data to a zip file. + /// + internal class ZipCipherStream : System.IO.Stream + { + private ZipCrypto _cipher; + private System.IO.Stream _s; + private CryptoMode _mode; + + /// The constructor. + /// The underlying stream + /// To either encrypt or decrypt. + /// The pre-initialized ZipCrypto object. + public ZipCipherStream(System.IO.Stream s, ZipCrypto cipher, CryptoMode mode) + : base() + { + _cipher = cipher; + _s = s; + _mode = mode; + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (_mode == CryptoMode.Encrypt) + throw new NotSupportedException("This stream does not encrypt via Read()"); + + if (buffer == null) + throw new ArgumentNullException("buffer"); + + byte[] db = new byte[count]; + int n = _s.Read(db, 0, count); + byte[] decrypted = _cipher.DecryptMessage(db, n); + for (int i = 0; i < n; i++) + { + buffer[offset + i] = decrypted[i]; + } + return n; + } + + public override void Write(byte[] buffer, int offset, int count) + { + if (_mode == CryptoMode.Decrypt) + throw new NotSupportedException("This stream does not Decrypt via Write()"); + + if (buffer == null) + throw new ArgumentNullException("buffer"); + + // workitem 7696 + if (count == 0) return; + + byte[] plaintext = null; + if (offset != 0) + { + plaintext = new byte[count]; + for (int i = 0; i < count; i++) + { + plaintext[i] = buffer[offset + i]; + } + } + else plaintext = buffer; + + byte[] encrypted = _cipher.EncryptMessage(plaintext, count); + _s.Write(encrypted, 0, encrypted.Length); + } + + + public override bool CanRead + { + get { return (_mode == CryptoMode.Decrypt); } + } + public override bool CanSeek + { + get { return false; } + } + + public override bool CanWrite + { + get { return (_mode == CryptoMode.Encrypt); } + } + + public override void Flush() + { + //throw new NotSupportedException(); + } + + public override long Length + { + get { throw new NotSupportedException(); } + } + + public override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + public override long Seek(long offset, System.IO.SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + } +} diff --git a/DotNetZip/Zip/ZipDirEntry.cs b/DotNetZip/Zip/ZipDirEntry.cs new file mode 100644 index 0000000..882c694 --- /dev/null +++ b/DotNetZip/Zip/ZipDirEntry.cs @@ -0,0 +1,381 @@ +// ZipDirEntry.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2006-2011 Dino Chiesa . +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2011-July-11 12:03:03> +// +// ------------------------------------------------------------------ +// +// This module defines members of the ZipEntry class for reading the +// Zip file central directory. +// +// Created: Tue, 27 Mar 2007 15:30 +// +// ------------------------------------------------------------------ + + +using System; +using System.Collections.Generic; + +namespace Ionic.Zip +{ + + partial class ZipEntry + { + /// + /// True if the referenced entry is a directory. + /// + internal bool AttributesIndicateDirectory + { + get { return ((_InternalFileAttrs == 0) && ((_ExternalFileAttrs & 0x0010) == 0x0010)); } + } + + + internal void ResetDirEntry() + { + // __FileDataPosition is the position of the file data for an entry. + // It is _RelativeOffsetOfLocalHeader + size of local header. + + // We cannot know the __FileDataPosition until we read the local + // header. + + // The local header is not necessarily the same length as the record + // in the central directory. + + // Set to -1, to indicate we need to read this later. + this.__FileDataPosition = -1; + + // set _LengthOfHeader to 0, to indicate we need to read later. + this._LengthOfHeader = 0; + } + + /// + /// Provides a human-readable string with information about the ZipEntry. + /// + public string Info + { + get + { + var builder = new System.Text.StringBuilder(); + builder + .Append(string.Format(" ZipEntry: {0}\n", this.FileName)) + .Append(string.Format(" Version Made By: {0}\n", this._VersionMadeBy)) + .Append(string.Format(" Needed to extract: {0}\n", this.VersionNeeded)); + + if (this._IsDirectory) + builder.Append(" Entry type: directory\n"); + else + { + builder.Append(string.Format(" File type: {0}\n", this._IsText? "text":"binary")) + .Append(string.Format(" Compression: {0}\n", this.CompressionMethod)) + .Append(string.Format(" Compressed: 0x{0:X}\n", this.CompressedSize)) + .Append(string.Format(" Uncompressed: 0x{0:X}\n", this.UncompressedSize)) + .Append(string.Format(" CRC32: 0x{0:X8}\n", this._Crc32)); + } + builder.Append(string.Format(" Disk Number: {0}\n", this._diskNumber)); + if (this._RelativeOffsetOfLocalHeader > 0xFFFFFFFF) + builder + .Append(string.Format(" Relative Offset: 0x{0:X16}\n", this._RelativeOffsetOfLocalHeader)); + else + builder + .Append(string.Format(" Relative Offset: 0x{0:X8}\n", this._RelativeOffsetOfLocalHeader)); + + builder + .Append(string.Format(" Bit Field: 0x{0:X4}\n", this._BitField)) + .Append(string.Format(" Encrypted?: {0}\n", this._sourceIsEncrypted)) + .Append(string.Format(" Timeblob: 0x{0:X8}\n", this._TimeBlob)) + .Append(string.Format(" Time: {0}\n", Ionic.Zip.SharedUtilities.PackedToDateTime(this._TimeBlob))); + + builder.Append(string.Format(" Is Zip64?: {0}\n", this._InputUsesZip64)); + if (!string.IsNullOrEmpty(this._Comment)) + { + builder.Append(string.Format(" Comment: {0}\n", this._Comment)); + } + builder.Append("\n"); + return builder.ToString(); + } + } + + + // workitem 10330 + private class CopyHelper + { + private static System.Text.RegularExpressions.Regex re = + new System.Text.RegularExpressions.Regex(" \\(copy (\\d+)\\)$"); + + private static int callCount = 0; + + internal static string AppendCopyToFileName(string f) + { + callCount++; + if (callCount > 25) + throw new OverflowException("overflow while creating filename"); + + int n = 1; + int r = f.LastIndexOf("."); + + if (r == -1) + { + // there is no extension + System.Text.RegularExpressions.Match m = re.Match(f); + if (m.Success) + { + n = Int32.Parse(m.Groups[1].Value) + 1; + string copy = String.Format(" (copy {0})", n); + f = f.Substring(0, m.Index) + copy; + } + else + { + string copy = String.Format(" (copy {0})", n); + f = f + copy; + } + } + else + { + //System.Console.WriteLine("HasExtension"); + System.Text.RegularExpressions.Match m = re.Match(f.Substring(0, r)); + if (m.Success) + { + n = Int32.Parse(m.Groups[1].Value) + 1; + string copy = String.Format(" (copy {0})", n); + f = f.Substring(0, m.Index) + copy + f.Substring(r); + } + else + { + string copy = String.Format(" (copy {0})", n); + f = f.Substring(0, r) + copy + f.Substring(r); + } + + //System.Console.WriteLine("returning f({0})", f); + } + return f; + } + } + + + + /// + /// Reads one entry from the zip directory structure in the zip file. + /// + /// + /// + /// The zipfile for which a directory entry will be read. From this param, the + /// method gets the ReadStream and the expected text encoding + /// (ProvisionalAlternateEncoding) which is used if the entry is not marked + /// UTF-8. + /// + /// + /// + /// a list of previously seen entry names; used to prevent duplicates. + /// + /// + /// the entry read from the archive. + internal static ZipEntry ReadDirEntry(ZipFile zf, + Dictionary previouslySeen) + { + System.IO.Stream s = zf.ReadStream; + System.Text.Encoding expectedEncoding = (zf.AlternateEncodingUsage == ZipOption.Always) + ? zf.AlternateEncoding + : ZipFile.DefaultEncoding; + + int signature = Ionic.Zip.SharedUtilities.ReadSignature(s); + // return null if this is not a local file header signature + if (IsNotValidZipDirEntrySig(signature)) + { + s.Seek(-4, System.IO.SeekOrigin.Current); + // workitem 10178 + Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s); + + // Getting "not a ZipDirEntry signature" here is not always wrong or an + // error. This can happen when walking through a zipfile. After the + // last ZipDirEntry, we expect to read an + // EndOfCentralDirectorySignature. When we get this is how we know + // we've reached the end of the central directory. + if (signature != ZipConstants.EndOfCentralDirectorySignature && + signature != ZipConstants.Zip64EndOfCentralDirectoryRecordSignature && + signature != ZipConstants.ZipEntrySignature // workitem 8299 + ) + { + throw new BadReadException(String.Format(" Bad signature (0x{0:X8}) at position 0x{1:X8}", signature, s.Position)); + } + return null; + } + + int bytesRead = 42 + 4; + byte[] block = new byte[42]; + int n = s.Read(block, 0, block.Length); + if (n != block.Length) return null; + + int i = 0; + ZipEntry zde = new ZipEntry(); + zde.AlternateEncoding = expectedEncoding; + zde._Source = ZipEntrySource.ZipFile; + zde._container = new ZipContainer(zf); + + unchecked + { + zde._VersionMadeBy = (short)(block[i++] + block[i++] * 256); + zde._VersionNeeded = (short)(block[i++] + block[i++] * 256); + zde._BitField = (short)(block[i++] + block[i++] * 256); + zde._CompressionMethod = (Int16)(block[i++] + block[i++] * 256); + zde._TimeBlob = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256; + zde._LastModified = Ionic.Zip.SharedUtilities.PackedToDateTime(zde._TimeBlob); + zde._timestamp |= ZipEntryTimestamp.DOS; + + zde._Crc32 = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256; + zde._CompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); + zde._UncompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); + } + + // preserve + zde._CompressionMethod_FromZipFile = zde._CompressionMethod; + + zde._filenameLength = (short)(block[i++] + block[i++] * 256); + zde._extraFieldLength = (short)(block[i++] + block[i++] * 256); + zde._commentLength = (short)(block[i++] + block[i++] * 256); + zde._diskNumber = (UInt32)(block[i++] + block[i++] * 256); + + zde._InternalFileAttrs = (short)(block[i++] + block[i++] * 256); + zde._ExternalFileAttrs = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256; + + zde._RelativeOffsetOfLocalHeader = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); + + // workitem 7801 + zde.IsText = ((zde._InternalFileAttrs & 0x01) == 0x01); + + block = new byte[zde._filenameLength]; + n = s.Read(block, 0, block.Length); + bytesRead += n; + if ((zde._BitField & 0x0800) == 0x0800) + { + // UTF-8 is in use + zde._FileNameInArchive = Ionic.Zip.SharedUtilities.Utf8StringFromBuffer(block); + } + else + { + zde._FileNameInArchive = Ionic.Zip.SharedUtilities.StringFromBuffer(block, expectedEncoding); + } + + // workitem 10330 + // insure unique entry names + while (previouslySeen.ContainsKey(zde._FileNameInArchive)) + { + zde._FileNameInArchive = CopyHelper.AppendCopyToFileName(zde._FileNameInArchive); + zde._metadataChanged = true; + } + + if (zde.AttributesIndicateDirectory) + zde.MarkAsDirectory(); // may append a slash to filename if nec. + // workitem 6898 + else if (zde._FileNameInArchive.EndsWith("/")) zde.MarkAsDirectory(); + + zde._CompressedFileDataSize = zde._CompressedSize; + if ((zde._BitField & 0x01) == 0x01) + { + // this may change after processing the Extra field + zde._Encryption_FromZipFile = zde._Encryption = + EncryptionAlgorithm.PkzipWeak; + zde._sourceIsEncrypted = true; + } + + if (zde._extraFieldLength > 0) + { + zde._InputUsesZip64 = (zde._CompressedSize == 0xFFFFFFFF || + zde._UncompressedSize == 0xFFFFFFFF || + zde._RelativeOffsetOfLocalHeader == 0xFFFFFFFF); + + // Console.WriteLine(" Input uses Z64?: {0}", zde._InputUsesZip64); + + bytesRead += zde.ProcessExtraField(s, zde._extraFieldLength); + zde._CompressedFileDataSize = zde._CompressedSize; + } + + // we've processed the extra field, so we know the encryption method is set now. + if (zde._Encryption == EncryptionAlgorithm.PkzipWeak) + { + // the "encryption header" of 12 bytes precedes the file data + zde._CompressedFileDataSize -= 12; + } +#if AESCRYPTO + else if (zde.Encryption == EncryptionAlgorithm.WinZipAes128 || + zde.Encryption == EncryptionAlgorithm.WinZipAes256) + { + zde._CompressedFileDataSize = zde.CompressedSize - + (ZipEntry.GetLengthOfCryptoHeaderBytes(zde.Encryption) + 10); + zde._LengthOfTrailer = 10; + } +#endif + + // tally the trailing descriptor + if ((zde._BitField & 0x0008) == 0x0008) + { + // sig, CRC, Comp and Uncomp sizes + if (zde._InputUsesZip64) + zde._LengthOfTrailer += 24; + else + zde._LengthOfTrailer += 16; + } + + // workitem 12744 + zde.AlternateEncoding = ((zde._BitField & 0x0800) == 0x0800) + ? System.Text.Encoding.UTF8 + :expectedEncoding; + + zde.AlternateEncodingUsage = ZipOption.Always; + + if (zde._commentLength > 0) + { + block = new byte[zde._commentLength]; + n = s.Read(block, 0, block.Length); + bytesRead += n; + if ((zde._BitField & 0x0800) == 0x0800) + { + // UTF-8 is in use + zde._Comment = Ionic.Zip.SharedUtilities.Utf8StringFromBuffer(block); + } + else + { + zde._Comment = Ionic.Zip.SharedUtilities.StringFromBuffer(block, expectedEncoding); + } + } + //zde._LengthOfDirEntry = bytesRead; + return zde; + } + + + /// + /// Returns true if the passed-in value is a valid signature for a ZipDirEntry. + /// + /// the candidate 4-byte signature value. + /// true, if the signature is valid according to the PKWare spec. + internal static bool IsNotValidZipDirEntrySig(int signature) + { + return (signature != ZipConstants.ZipDirEntrySignature); + } + + + private Int16 _VersionMadeBy; + private Int16 _InternalFileAttrs; + private Int32 _ExternalFileAttrs; + + //private Int32 _LengthOfDirEntry; + private Int16 _filenameLength; + private Int16 _extraFieldLength; + private Int16 _commentLength; + } + + +} diff --git a/DotNetZip/Zip/ZipEntry.Extract.cs b/DotNetZip/Zip/ZipEntry.Extract.cs new file mode 100644 index 0000000..436d132 --- /dev/null +++ b/DotNetZip/Zip/ZipEntry.Extract.cs @@ -0,0 +1,1466 @@ +// ZipEntry.Extract.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2009-2011 Dino Chiesa +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2011-August-06 18:08:21> +// +// ------------------------------------------------------------------ +// +// This module defines logic for Extract methods on the ZipEntry class. +// +// ------------------------------------------------------------------ + + +using System; +using System.IO; + +namespace Ionic.Zip +{ + + public partial class ZipEntry + { + /// + /// Extract the entry to the filesystem, starting at the current + /// working directory. + /// + /// + /// + /// This method has a bunch of overloads! One of them is sure to + /// be the right one for you... If you don't like these, check + /// out the ExtractWithPassword() methods. + /// + /// + /// + /// + /// + /// + /// + /// + /// This method extracts an entry from a zip file into the current + /// working directory. The path of the entry as extracted is the full + /// path as specified in the zip archive, relative to the current + /// working directory. After the file is extracted successfully, the + /// file attributes and timestamps are set. + /// + /// + /// + /// The action taken when extraction an entry would overwrite an + /// existing file is determined by the property. + /// + /// + /// + /// Within the call to Extract(), the content for the entry is + /// written into a filesystem file, and then the last modified time of the + /// file is set according to the property on + /// the entry. See the remarks the property for + /// some details about the last modified time. + /// + /// + /// + public void Extract() + { + InternalExtract(".", null, null); + } + + + /// + /// Extract the entry to a file in the filesystem, using the specified + /// behavior when extraction would overwrite an existing file. + /// + /// + /// + /// + /// See the remarks on the property, for some + /// details about how the last modified time of the file is set after + /// extraction. + /// + /// + /// + /// + /// The action to take if extraction would overwrite an existing file. + /// + public void Extract(ExtractExistingFileAction extractExistingFile) + { + ExtractExistingFile = extractExistingFile; + InternalExtract(".", null, null); + } + + /// + /// Extracts the entry to the specified stream. + /// + /// + /// + /// + /// The caller can specify any write-able stream, for example a , a , or ASP.NET's + /// Response.OutputStream. The content will be decrypted and + /// decompressed as necessary. If the entry is encrypted and no password + /// is provided, this method will throw. + /// + /// + /// The position on the stream is not reset by this method before it extracts. + /// You may want to call stream.Seek() before calling ZipEntry.Extract(). + /// + /// + /// + /// + /// the stream to which the entry should be extracted. + /// + /// + public void Extract(Stream stream) + { + InternalExtract(null, stream, null); + } + + /// + /// Extract the entry to the filesystem, starting at the specified base + /// directory. + /// + /// + /// the pathname of the base directory + /// + /// + /// + /// + /// + /// This example extracts only the entries in a zip file that are .txt files, + /// into a directory called "textfiles". + /// + /// using (ZipFile zip = ZipFile.Read("PackedDocuments.zip")) + /// { + /// foreach (string s1 in zip.EntryFilenames) + /// { + /// if (s1.EndsWith(".txt")) + /// { + /// zip[s1].Extract("textfiles"); + /// } + /// } + /// } + /// + /// + /// Using zip As ZipFile = ZipFile.Read("PackedDocuments.zip") + /// Dim s1 As String + /// For Each s1 In zip.EntryFilenames + /// If s1.EndsWith(".txt") Then + /// zip(s1).Extract("textfiles") + /// End If + /// Next + /// End Using + /// + /// + /// + /// + /// + /// + /// Using this method, existing entries in the filesystem will not be + /// overwritten. If you would like to force the overwrite of existing + /// files, see the property, or call + /// . + /// + /// + /// + /// See the remarks on the property, for some + /// details about how the last modified time of the created file is set. + /// + /// + public void Extract(string baseDirectory) + { + InternalExtract(baseDirectory, null, null); + } + + + + + + /// + /// Extract the entry to the filesystem, starting at the specified base + /// directory, and using the specified behavior when extraction would + /// overwrite an existing file. + /// + /// + /// + /// + /// See the remarks on the property, for some + /// details about how the last modified time of the created file is set. + /// + /// + /// + /// + /// + /// String sZipPath = "Airborne.zip"; + /// String sFilePath = "Readme.txt"; + /// String sRootFolder = "Digado"; + /// using (ZipFile zip = ZipFile.Read(sZipPath)) + /// { + /// if (zip.EntryFileNames.Contains(sFilePath)) + /// { + /// // use the string indexer on the zip file + /// zip[sFileName].Extract(sRootFolder, + /// ExtractExistingFileAction.OverwriteSilently); + /// } + /// } + /// + /// + /// + /// Dim sZipPath as String = "Airborne.zip" + /// Dim sFilePath As String = "Readme.txt" + /// Dim sRootFolder As String = "Digado" + /// Using zip As ZipFile = ZipFile.Read(sZipPath) + /// If zip.EntryFileNames.Contains(sFilePath) + /// ' use the string indexer on the zip file + /// zip(sFilePath).Extract(sRootFolder, _ + /// ExtractExistingFileAction.OverwriteSilently) + /// End If + /// End Using + /// + /// + /// + /// the pathname of the base directory + /// + /// The action to take if extraction would overwrite an existing file. + /// + public void Extract(string baseDirectory, ExtractExistingFileAction extractExistingFile) + { + ExtractExistingFile = extractExistingFile; + InternalExtract(baseDirectory, null, null); + } + + + /// + /// Extract the entry to the filesystem, using the current working directory + /// and the specified password. + /// + /// + /// + /// This method has a bunch of overloads! One of them is sure to be + /// the right one for you... + /// + /// + /// + /// + /// + /// + /// + /// + /// Existing entries in the filesystem will not be overwritten. If you + /// would like to force the overwrite of existing files, see the property, or call + /// . + /// + /// + /// + /// See the remarks on the property for some + /// details about how the "last modified" time of the created file is + /// set. + /// + /// + /// + /// + /// In this example, entries that use encryption are extracted using a + /// particular password. + /// + /// using (var zip = ZipFile.Read(FilePath)) + /// { + /// foreach (ZipEntry e in zip) + /// { + /// if (e.UsesEncryption) + /// e.ExtractWithPassword("Secret!"); + /// else + /// e.Extract(); + /// } + /// } + /// + /// + /// Using zip As ZipFile = ZipFile.Read(FilePath) + /// Dim e As ZipEntry + /// For Each e In zip + /// If (e.UsesEncryption) + /// e.ExtractWithPassword("Secret!") + /// Else + /// e.Extract + /// End If + /// Next + /// End Using + /// + /// + /// The Password to use for decrypting the entry. + public void ExtractWithPassword(string password) + { + InternalExtract(".", null, password); + } + + /// + /// Extract the entry to the filesystem, starting at the specified base + /// directory, and using the specified password. + /// + /// + /// + /// + /// + /// + /// + /// Existing entries in the filesystem will not be overwritten. If you + /// would like to force the overwrite of existing files, see the property, or call + /// . + /// + /// + /// + /// See the remarks on the property, for some + /// details about how the last modified time of the created file is set. + /// + /// + /// + /// The pathname of the base directory. + /// The Password to use for decrypting the entry. + public void ExtractWithPassword(string baseDirectory, string password) + { + InternalExtract(baseDirectory, null, password); + } + + + + + /// + /// Extract the entry to a file in the filesystem, relative to the + /// current directory, using the specified behavior when extraction + /// would overwrite an existing file. + /// + /// + /// + /// + /// See the remarks on the property, for some + /// details about how the last modified time of the created file is set. + /// + /// + /// + /// The Password to use for decrypting the entry. + /// + /// + /// The action to take if extraction would overwrite an existing file. + /// + public void ExtractWithPassword(ExtractExistingFileAction extractExistingFile, string password) + { + ExtractExistingFile = extractExistingFile; + InternalExtract(".", null, password); + } + + + + /// + /// Extract the entry to the filesystem, starting at the specified base + /// directory, and using the specified behavior when extraction would + /// overwrite an existing file. + /// + /// + /// + /// See the remarks on the property, for some + /// details about how the last modified time of the created file is set. + /// + /// + /// the pathname of the base directory + /// + /// The action to take if extraction would + /// overwrite an existing file. + /// + /// The Password to use for decrypting the entry. + public void ExtractWithPassword(string baseDirectory, ExtractExistingFileAction extractExistingFile, string password) + { + ExtractExistingFile = extractExistingFile; + InternalExtract(baseDirectory, null, password); + } + + /// + /// Extracts the entry to the specified stream, using the specified + /// Password. For example, the caller could extract to Console.Out, or + /// to a MemoryStream. + /// + /// + /// + /// + /// The caller can specify any write-able stream, for example a , a , or ASP.NET's + /// Response.OutputStream. The content will be decrypted and + /// decompressed as necessary. If the entry is encrypted and no password + /// is provided, this method will throw. + /// + /// + /// The position on the stream is not reset by this method before it extracts. + /// You may want to call stream.Seek() before calling ZipEntry.Extract(). + /// + /// + /// + /// + /// + /// the stream to which the entry should be extracted. + /// + /// + /// The password to use for decrypting the entry. + /// + public void ExtractWithPassword(Stream stream, string password) + { + InternalExtract(null, stream, password); + } + + + /// + /// Opens a readable stream corresponding to the zip entry in the + /// archive. The stream decompresses and decrypts as necessary, as it + /// is read. + /// + /// + /// + /// + /// + /// DotNetZip offers a variety of ways to extract entries from a zip + /// file. This method allows an application to extract an entry by + /// reading a . + /// + /// + /// + /// The return value is of type . Use it as you would any + /// stream for reading. When an application calls on that stream, it will + /// receive data from the zip entry that is decrypted and decompressed + /// as necessary. + /// + /// + /// + /// CrcCalculatorStream adds one additional feature: it keeps a + /// CRC32 checksum on the bytes of the stream as it is read. The CRC + /// value is available in the property on the + /// CrcCalculatorStream. When the read is complete, your + /// application + /// should check this CRC against the + /// property on the ZipEntry to validate the content of the + /// ZipEntry. You don't have to validate the entry using the CRC, but + /// you should, to verify integrity. Check the example for how to do + /// this. + /// + /// + /// + /// If the entry is protected with a password, then you need to provide + /// a password prior to calling , either by + /// setting the property on the entry, or the + /// property on the ZipFile + /// itself. Or, you can use , the + /// overload of OpenReader that accepts a password parameter. + /// + /// + /// + /// If you want to extract entry data into a write-able stream that is + /// already opened, like a , do not + /// use this method. Instead, use . + /// + /// + /// + /// Your application may use only one stream created by OpenReader() at + /// a time, and you should not call other Extract methods before + /// completing your reads on a stream obtained from OpenReader(). This + /// is because there is really only one source stream for the compressed + /// content. A call to OpenReader() seeks in the source stream, to the + /// beginning of the compressed content. A subsequent call to + /// OpenReader() on a different entry will seek to a different position + /// in the source stream, as will a call to Extract() or one of its + /// overloads. This will corrupt the state for the decompressing stream + /// from the original call to OpenReader(). + /// + /// + /// + /// The OpenReader() method works only when the ZipEntry is + /// obtained from an instance of ZipFile. This method will throw + /// an exception if the ZipEntry is obtained from a . + /// + /// + /// + /// + /// This example shows how to open a zip archive, then read in a named + /// entry via a stream. After the read loop is complete, the code + /// compares the calculated during the read loop with the expected CRC + /// on the ZipEntry, to verify the extraction. + /// + /// using (ZipFile zip = new ZipFile(ZipFileToRead)) + /// { + /// ZipEntry e1= zip["Elevation.mp3"]; + /// using (Ionic.Zlib.CrcCalculatorStream s = e1.OpenReader()) + /// { + /// byte[] buffer = new byte[4096]; + /// int n, totalBytesRead= 0; + /// do { + /// n = s.Read(buffer,0, buffer.Length); + /// totalBytesRead+=n; + /// } while (n>0); + /// if (s.Crc32 != e1.Crc32) + /// throw new Exception(string.Format("The Zip Entry failed the CRC Check. (0x{0:X8}!=0x{1:X8})", s.Crc32, e1.Crc32)); + /// if (totalBytesRead != e1.UncompressedSize) + /// throw new Exception(string.Format("We read an unexpected number of bytes. ({0}!={1})", totalBytesRead, e1.UncompressedSize)); + /// } + /// } + /// + /// + /// Using zip As New ZipFile(ZipFileToRead) + /// Dim e1 As ZipEntry = zip.Item("Elevation.mp3") + /// Using s As Ionic.Zlib.CrcCalculatorStream = e1.OpenReader + /// Dim n As Integer + /// Dim buffer As Byte() = New Byte(4096) {} + /// Dim totalBytesRead As Integer = 0 + /// Do + /// n = s.Read(buffer, 0, buffer.Length) + /// totalBytesRead = (totalBytesRead + n) + /// Loop While (n > 0) + /// If (s.Crc32 <> e1.Crc32) Then + /// Throw New Exception(String.Format("The Zip Entry failed the CRC Check. (0x{0:X8}!=0x{1:X8})", s.Crc32, e1.Crc32)) + /// End If + /// If (totalBytesRead <> e1.UncompressedSize) Then + /// Throw New Exception(String.Format("We read an unexpected number of bytes. ({0}!={1})", totalBytesRead, e1.UncompressedSize)) + /// End If + /// End Using + /// End Using + /// + /// + /// + /// The Stream for reading. + public Ionic.Crc.CrcCalculatorStream OpenReader() + { + // workitem 10923 + if (_container.ZipFile == null) + throw new InvalidOperationException("Use OpenReader() only with ZipFile."); + + // use the entry password if it is non-null, + // else use the zipfile password, which is possibly null + return InternalOpenReader(this._Password ?? this._container.Password); + } + + /// + /// Opens a readable stream for an encrypted zip entry in the archive. + /// The stream decompresses and decrypts as necessary, as it is read. + /// + /// + /// + /// + /// See the documentation on the method for + /// full details. This overload allows the application to specify a + /// password for the ZipEntry to be read. + /// + /// + /// + /// The password to use for decrypting the entry. + /// The Stream for reading. + public Ionic.Crc.CrcCalculatorStream OpenReader(string password) + { + // workitem 10923 + if (_container.ZipFile == null) + throw new InvalidOperationException("Use OpenReader() only with ZipFile."); + + return InternalOpenReader(password); + } + + + + internal Ionic.Crc.CrcCalculatorStream InternalOpenReader(string password) + { + ValidateCompression(); + ValidateEncryption(); + SetupCryptoForExtract(password); + + // workitem 7958 + if (this._Source != ZipEntrySource.ZipFile) + throw new BadStateException("You must call ZipFile.Save before calling OpenReader"); + + // LeftToRead is a count of bytes remaining to be read (out) + // from the stream AFTER decompression and decryption. + // It is the uncompressed size, unless ... there is no compression in which + // case ...? :< I'm not sure why it's not always UncompressedSize + Int64 LeftToRead = (_CompressionMethod_FromZipFile == (short)CompressionMethod.None) + ? this._CompressedFileDataSize + : this.UncompressedSize; + + Stream input = this.ArchiveStream; + + this.ArchiveStream.Seek(this.FileDataPosition, SeekOrigin.Begin); + // workitem 10178 + Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(this.ArchiveStream); + + _inputDecryptorStream = GetExtractDecryptor(input); + Stream input3 = GetExtractDecompressor(_inputDecryptorStream); + + return new Ionic.Crc.CrcCalculatorStream(input3, LeftToRead); + } + + + + private void OnExtractProgress(Int64 bytesWritten, Int64 totalBytesToWrite) + { + if (_container.ZipFile != null) + _ioOperationCanceled = _container.ZipFile.OnExtractBlock(this, bytesWritten, totalBytesToWrite); + } + + + private void OnBeforeExtract(string path) + { + // When in the context of a ZipFile.ExtractAll, the events are generated from + // the ZipFile method, not from within the ZipEntry instance. (why?) + // Therefore we suppress the events originating from the ZipEntry method. + if (_container.ZipFile != null) + { + if (!_container.ZipFile._inExtractAll) + { + _ioOperationCanceled = _container.ZipFile.OnSingleEntryExtract(this, path, true); + } + } + } + + private void OnAfterExtract(string path) + { + // When in the context of a ZipFile.ExtractAll, the events are generated from + // the ZipFile method, not from within the ZipEntry instance. (why?) + // Therefore we suppress the events originating from the ZipEntry method. + if (_container.ZipFile != null) + { + if (!_container.ZipFile._inExtractAll) + { + _container.ZipFile.OnSingleEntryExtract(this, path, false); + } + } + } + + private void OnExtractExisting(string path) + { + if (_container.ZipFile != null) + _ioOperationCanceled = _container.ZipFile.OnExtractExisting(this, path); + } + + private static void ReallyDelete(string fileName) + { + // workitem 7881 + // reset ReadOnly bit if necessary +#if NETCF + if ( (NetCfFile.GetAttributes(fileName) & (uint)FileAttributes.ReadOnly) == (uint)FileAttributes.ReadOnly) + NetCfFile.SetAttributes(fileName, (uint)FileAttributes.Normal); +#elif SILVERLIGHT +#else + if ((File.GetAttributes(fileName) & FileAttributes.ReadOnly) == FileAttributes.ReadOnly) + File.SetAttributes(fileName, FileAttributes.Normal); +#endif + File.Delete(fileName); + } + + + private void WriteStatus(string format, params Object[] args) + { + if (_container.ZipFile != null && _container.ZipFile.Verbose) _container.ZipFile.StatusMessageTextWriter.WriteLine(format, args); + } + + + // Pass in either basedir or s, but not both. + // In other words, you can extract to a stream or to a directory (filesystem), but not both! + // The Password param is required for encrypted entries. + private void InternalExtract(string baseDir, Stream outstream, string password) + { + // workitem 7958 + if (_container == null) + throw new BadStateException("This entry is an orphan"); + + // workitem 10355 + if (_container.ZipFile == null) + throw new InvalidOperationException("Use Extract() only with ZipFile."); + + _container.ZipFile.Reset(false); + + if (this._Source != ZipEntrySource.ZipFile) + throw new BadStateException("You must call ZipFile.Save before calling any Extract method"); + + OnBeforeExtract(baseDir); + _ioOperationCanceled = false; + string targetFileName = null; + Stream output = null; + bool fileExistsBeforeExtraction = false; + bool checkLaterForResetDirTimes = false; + try + { + ValidateCompression(); + ValidateEncryption(); + + if (ValidateOutput(baseDir, outstream, out targetFileName)) + { + WriteStatus("extract dir {0}...", targetFileName); + // if true, then the entry was a directory and has been created. + // We need to fire the Extract Event. + OnAfterExtract(baseDir); + return; + } + + // workitem 10639 + // do we want to extract to a regular filesystem file? + if (targetFileName != null) + { + // Check for extracting to a previously extant file. The user + // can specify bejavior for that case: overwrite, don't + // overwrite, and throw. Also, if the file exists prior to + // extraction, it affects exception handling: whether to delete + // the target of extraction or not. This check needs to be done + // before the password check is done, because password check may + // throw a BadPasswordException, which triggers the catch, + // wherein the extant file may be deleted if not flagged as + // pre-existing. + if (File.Exists(targetFileName)) + { + fileExistsBeforeExtraction = true; + int rc = CheckExtractExistingFile(baseDir, targetFileName); + if (rc == 2) goto ExitTry; // cancel + if (rc == 1) return; // do not overwrite + } + } + + // If no password explicitly specified, use the password on the entry itself, + // or on the zipfile itself. + string p = password ?? this._Password ?? this._container.Password; + if (_Encryption_FromZipFile != EncryptionAlgorithm.None) + { + if (p == null) + throw new BadPasswordException(); + SetupCryptoForExtract(p); + } + + + // set up the output stream + if (targetFileName != null) + { + WriteStatus("extract file {0}...", targetFileName); + targetFileName += ".tmp"; + // var dirName = Path.GetDirectoryName(targetFileName); + // modified 2018-04-02 DL2ALF + // Path.GetDirectory does not work properly on Linux/Mono + var dirName = String.Empty; + if (targetFileName.IndexOf("\\") >= 0) + dirName = targetFileName.Substring(0, targetFileName.LastIndexOf("\\")); + else if (targetFileName.IndexOf("/") >= 0) + dirName = targetFileName.Substring(0, targetFileName.LastIndexOf("/")); + // ensure the target path exists + if (!Directory.Exists(dirName)) + { + // we create the directory here, but we do not set the + // create/modified/accessed times on it because it is being + // created implicitly, not explcitly. There's no entry in the + // zip archive for the directory. + Directory.CreateDirectory(dirName); + } + else + { + // workitem 8264 + if (_container.ZipFile != null) + checkLaterForResetDirTimes = _container.ZipFile._inExtractAll; + } + + // File.Create(CreateNew) will overwrite any existing file. + output = new FileStream(targetFileName, FileMode.CreateNew); + } + else + { + WriteStatus("extract entry {0} to stream...", FileName); + output = outstream; + } + + + if (_ioOperationCanceled) + goto ExitTry; + + Int32 ActualCrc32 = ExtractOne(output); + + if (_ioOperationCanceled) + goto ExitTry; + + VerifyCrcAfterExtract(ActualCrc32); + + if (targetFileName != null) + { + output.Close(); + output = null; + + // workitem 10639 + // move file to permanent home + string tmpName = targetFileName; + string zombie = null; + targetFileName = tmpName.Substring(0,tmpName.Length-4); + + if (fileExistsBeforeExtraction) + { + // An AV program may hold the target file open, which means + // File.Delete() will succeed, though the actual deletion + // remains pending. This will prevent a subsequent + // File.Move() from succeeding. To avoid this, when the file + // already exists, we need to replace it in 3 steps: + // + // 1. rename the existing file to a zombie name; + // 2. rename the extracted file from the temp name to + // the target file name; + // 3. delete the zombie. + // + zombie = targetFileName + ".PendingOverwrite"; + File.Move(targetFileName, zombie); + } + + File.Move(tmpName, targetFileName); + _SetTimes(targetFileName, true); + + if (zombie != null && File.Exists(zombie)) + ReallyDelete(zombie); + + // workitem 8264 + if (checkLaterForResetDirTimes) + { + // This is sort of a hack. What I do here is set the time on + // the parent directory, every time a file is extracted into + // it. If there is a directory with 1000 files, then I set + // the time on the dir, 1000 times. This allows the directory + // to have times that reflect the actual time on the entry in + // the zip archive. + + // String.Contains is not available on .NET CF 2.0 + if (this.FileName.IndexOf('/') != -1) + { + string dirname = Path.GetDirectoryName(this.FileName); + if (this._container.ZipFile[dirname] == null) + { + _SetTimes(Path.GetDirectoryName(targetFileName), false); + } + } + } + +#if NETCF + // workitem 7926 - version made by OS can be zero or 10 + if ((_VersionMadeBy & 0xFF00) == 0x0a00 || (_VersionMadeBy & 0xFF00) == 0x0000) + NetCfFile.SetAttributes(targetFileName, (uint)_ExternalFileAttrs); + +#else + // workitem 7071 + // + // We can only apply attributes if they are relevant to the NTFS + // OS. Must do this LAST because it may involve a ReadOnly bit, + // which would prevent us from setting the time, etc. + // + // workitem 7926 - version made by OS can be zero (FAT) or 10 + // (NTFS) + if ((_VersionMadeBy & 0xFF00) == 0x0a00 || (_VersionMadeBy & 0xFF00) == 0x0000) + File.SetAttributes(targetFileName, (FileAttributes)_ExternalFileAttrs); +#endif + } + + OnAfterExtract(baseDir); + + ExitTry: ; + } + catch (Exception) + { + _ioOperationCanceled = true; + throw; + } + finally + { + if (_ioOperationCanceled) + { + if (targetFileName != null) + { + try + { + if (output != null) output.Close(); + // An exception has occurred. If the file exists, check + // to see if it existed before we tried extracting. If + // it did not, attempt to remove the target file. There + // is a small possibility that the existing file has + // been extracted successfully, overwriting a previously + // existing file, and an exception was thrown after that + // but before final completion (setting times, etc). In + // that case the file will remain, even though some + // error occurred. Nothing to be done about it. + if (File.Exists(targetFileName) && !fileExistsBeforeExtraction) + File.Delete(targetFileName); + + } + finally { } + } + } + } + } + + +#if NOT + internal void CalcWinZipAesMac(Stream input) + { + if (Encryption == EncryptionAlgorithm.WinZipAes128 || + Encryption == EncryptionAlgorithm.WinZipAes256) + { + if (input is WinZipAesCipherStream) + wzs = input as WinZipAesCipherStream; + + else if (input is Ionic.Zlib.CrcCalculatorStream) + { + xxx; + } + + } + } +#endif + + + internal void VerifyCrcAfterExtract(Int32 actualCrc32) + { + +#if AESCRYPTO + // After extracting, Validate the CRC32 + if (actualCrc32 != _Crc32) + { + // CRC is not meaningful with WinZipAES and AES method 2 (AE-2) + if ((Encryption != EncryptionAlgorithm.WinZipAes128 && + Encryption != EncryptionAlgorithm.WinZipAes256) + || _WinZipAesMethod != 0x02) + throw new BadCrcException("CRC error: the file being extracted appears to be corrupted. " + + String.Format("Expected 0x{0:X8}, Actual 0x{1:X8}", _Crc32, actualCrc32)); + } + + // ignore MAC if the size of the file is zero + if (this.UncompressedSize == 0) + return; + + // calculate the MAC + if (Encryption == EncryptionAlgorithm.WinZipAes128 || + Encryption == EncryptionAlgorithm.WinZipAes256) + { + WinZipAesCipherStream wzs = _inputDecryptorStream as WinZipAesCipherStream; + _aesCrypto_forExtract.CalculatedMac = wzs.FinalAuthentication; + + _aesCrypto_forExtract.ReadAndVerifyMac(this.ArchiveStream); // throws if MAC is bad + // side effect: advances file position. + } + + + + +#else + if (actualCrc32 != _Crc32) + throw new BadCrcException("CRC error: the file being extracted appears to be corrupted. " + + String.Format("Expected 0x{0:X8}, Actual 0x{1:X8}", _Crc32, actualCrc32)); +#endif + } + + + + + private int CheckExtractExistingFile(string baseDir, string targetFileName) + { + int loop = 0; + // returns: 0 == extract, 1 = don't, 2 = cancel + do + { + switch (ExtractExistingFile) + { + case ExtractExistingFileAction.OverwriteSilently: + WriteStatus("the file {0} exists; will overwrite it...", targetFileName); + return 0; + + case ExtractExistingFileAction.DoNotOverwrite: + WriteStatus("the file {0} exists; not extracting entry...", FileName); + OnAfterExtract(baseDir); + return 1; + + case ExtractExistingFileAction.InvokeExtractProgressEvent: + if (loop>0) + throw new ZipException(String.Format("The file {0} already exists.", targetFileName)); + OnExtractExisting(baseDir); + if (_ioOperationCanceled) + return 2; + + // loop around + break; + + case ExtractExistingFileAction.Throw: + default: + throw new ZipException(String.Format("The file {0} already exists.", targetFileName)); + } + loop++; + } + while (true); + } + + + + + private void _CheckRead(int nbytes) + { + if (nbytes == 0) + throw new BadReadException(String.Format("bad read of entry {0} from compressed archive.", + this.FileName)); + } + + + private Stream _inputDecryptorStream; + + private Int32 ExtractOne(Stream output) + { + Int32 CrcResult = 0; + Stream input = this.ArchiveStream; + + try + { + // change for workitem 8098 + input.Seek(this.FileDataPosition, SeekOrigin.Begin); + // workitem 10178 + Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(input); + + byte[] bytes = new byte[BufferSize]; + + // The extraction process varies depending on how the entry was + // stored. It could have been encrypted, and it coould have + // been compressed, or both, or neither. So we need to check + // both the encryption flag and the compression flag, and take + // the proper action in all cases. + + Int64 LeftToRead = (_CompressionMethod_FromZipFile != (short)CompressionMethod.None) + ? this.UncompressedSize + : this._CompressedFileDataSize; + + // Get a stream that either decrypts or not. + _inputDecryptorStream = GetExtractDecryptor(input); + + Stream input3 = GetExtractDecompressor( _inputDecryptorStream ); + + Int64 bytesWritten = 0; + // As we read, we maybe decrypt, and then we maybe decompress. Then we write. + using (var s1 = new Ionic.Crc.CrcCalculatorStream(input3)) + { + while (LeftToRead > 0) + { + //Console.WriteLine("ExtractOne: LeftToRead {0}", LeftToRead); + + // Casting LeftToRead down to an int is ok here in the else clause, + // because that only happens when it is less than bytes.Length, + // which is much less than MAX_INT. + int len = (LeftToRead > bytes.Length) ? bytes.Length : (int)LeftToRead; + int n = s1.Read(bytes, 0, len); + + // must check data read - essential for detecting corrupt zip files + _CheckRead(n); + + output.Write(bytes, 0, n); + LeftToRead -= n; + bytesWritten += n; + + // fire the progress event, check for cancels + OnExtractProgress(bytesWritten, UncompressedSize); + if (_ioOperationCanceled) + { + break; + } + } + + CrcResult = s1.Crc; + } + } + finally + { + var zss = input as ZipSegmentedStream; + if (zss != null) + { +#if NETCF + zss.Close(); +#else + // need to dispose it + zss.Dispose(); +#endif + _archiveStream = null; + } + } + + return CrcResult; + } + + + + internal Stream GetExtractDecompressor(Stream input2) + { + // get a stream that either decompresses or not. + switch (_CompressionMethod_FromZipFile) + { + case (short)CompressionMethod.None: + return input2; + case (short)CompressionMethod.Deflate: + return new Ionic.Zlib.DeflateStream(input2, Ionic.Zlib.CompressionMode.Decompress, true); +#if BZIP + case (short)CompressionMethod.BZip2: + return new Ionic.BZip2.BZip2InputStream(input2, true); +#endif + } + + return null; + } + + + + internal Stream GetExtractDecryptor(Stream input) + { + Stream input2 = null; + if (_Encryption_FromZipFile == EncryptionAlgorithm.PkzipWeak) + input2 = new ZipCipherStream(input, _zipCrypto_forExtract, CryptoMode.Decrypt); + +#if AESCRYPTO + else if (_Encryption_FromZipFile == EncryptionAlgorithm.WinZipAes128 || + _Encryption_FromZipFile == EncryptionAlgorithm.WinZipAes256) + input2 = new WinZipAesCipherStream(input, _aesCrypto_forExtract, _CompressedFileDataSize, CryptoMode.Decrypt); +#endif + + else + input2 = input; + + return input2; + } + + + + + internal void _SetTimes(string fileOrDirectory, bool isFile) + { +#if SILVERLIGHT + // punt on setting file times +#else + // workitem 8807: + // Because setting the time is not considered to be a fatal error, + // and because other applications can interfere with the setting + // of a time on a directory, we're going to swallow IO exceptions + // in this method. + + try + { + if (_ntfsTimesAreSet) + { +#if NETCF + // workitem 7944: set time should not be a fatal error on CF + int rc = NetCfFile.SetTimes(fileOrDirectory, _Ctime, _Atime, _Mtime); + if ( rc != 0) + { + WriteStatus("Warning: SetTimes failed. entry({0}) file({1}) rc({2})", + FileName, fileOrDirectory, rc); + } +#else + if (isFile) + { + // It's possible that the extract was cancelled, in which case, + // the file does not exist. + if (File.Exists(fileOrDirectory)) + { + File.SetCreationTimeUtc(fileOrDirectory, _Ctime); + File.SetLastAccessTimeUtc(fileOrDirectory, _Atime); + File.SetLastWriteTimeUtc(fileOrDirectory, _Mtime); + } + } + else + { + // It's possible that the extract was cancelled, in which case, + // the directory does not exist. + if (Directory.Exists(fileOrDirectory)) + { + Directory.SetCreationTimeUtc(fileOrDirectory, _Ctime); + Directory.SetLastAccessTimeUtc(fileOrDirectory, _Atime); + Directory.SetLastWriteTimeUtc(fileOrDirectory, _Mtime); + } + } +#endif + } + else + { + // workitem 6191 + DateTime AdjustedLastModified = Ionic.Zip.SharedUtilities.AdjustTime_Reverse(LastModified); + +#if NETCF + int rc = NetCfFile.SetLastWriteTime(fileOrDirectory, AdjustedLastModified); + + if ( rc != 0) + { + WriteStatus("Warning: SetLastWriteTime failed. entry({0}) file({1}) rc({2})", + FileName, fileOrDirectory, rc); + } +#else + if (isFile) + File.SetLastWriteTime(fileOrDirectory, AdjustedLastModified); + else + Directory.SetLastWriteTime(fileOrDirectory, AdjustedLastModified); +#endif + } + } + catch (System.IO.IOException ioexc1) + { + WriteStatus("failed to set time on {0}: {1}", fileOrDirectory, ioexc1.Message); + } +#endif + } + + + #region Support methods + + + // workitem 7968 + private string UnsupportedAlgorithm + { + get + { + string alg = String.Empty; + switch (_UnsupportedAlgorithmId) + { + case 0: + alg = "--"; + break; + case 0x6601: + alg = "DES"; + break; + case 0x6602: // - RC2 (version needed to extract < 5.2) + alg = "RC2"; + break; + case 0x6603: // - 3DES 168 + alg = "3DES-168"; + break; + case 0x6609: // - 3DES 112 + alg = "3DES-112"; + break; + case 0x660E: // - AES 128 + alg = "PKWare AES128"; + break; + case 0x660F: // - AES 192 + alg = "PKWare AES192"; + break; + case 0x6610: // - AES 256 + alg = "PKWare AES256"; + break; + case 0x6702: // - RC2 (version needed to extract >= 5.2) + alg = "RC2"; + break; + case 0x6720: // - Blowfish + alg = "Blowfish"; + break; + case 0x6721: // - Twofish + alg = "Twofish"; + break; + case 0x6801: // - RC4 + alg = "RC4"; + break; + case 0xFFFF: // - Unknown algorithm + default: + alg = String.Format("Unknown (0x{0:X4})", _UnsupportedAlgorithmId); + break; + } + return alg; + } + } + + // workitem 7968 + private string UnsupportedCompressionMethod + { + get + { + string meth = String.Empty; + switch ((int)_CompressionMethod) + { + case 0: + meth = "Store"; + break; + case 1: + meth = "Shrink"; + break; + case 8: + meth = "DEFLATE"; + break; + case 9: + meth = "Deflate64"; + break; + case 12: + meth = "BZIP2"; // only if BZIP not compiled in + break; + case 14: + meth = "LZMA"; + break; + case 19: + meth = "LZ77"; + break; + case 98: + meth = "PPMd"; + break; + default: + meth = String.Format("Unknown (0x{0:X4})", _CompressionMethod); + break; + } + return meth; + } + } + + + internal void ValidateEncryption() + { + if (Encryption != EncryptionAlgorithm.PkzipWeak && +#if AESCRYPTO + Encryption != EncryptionAlgorithm.WinZipAes128 && + Encryption != EncryptionAlgorithm.WinZipAes256 && +#endif + Encryption != EncryptionAlgorithm.None) + { + // workitem 7968 + if (_UnsupportedAlgorithmId != 0) + throw new ZipException(String.Format("Cannot extract: Entry {0} is encrypted with an algorithm not supported by DotNetZip: {1}", + FileName, UnsupportedAlgorithm)); + else + throw new ZipException(String.Format("Cannot extract: Entry {0} uses an unsupported encryption algorithm ({1:X2})", + FileName, (int)Encryption)); + } + } + + + private void ValidateCompression() + { + if ((_CompressionMethod_FromZipFile != (short)CompressionMethod.None) && + (_CompressionMethod_FromZipFile != (short)CompressionMethod.Deflate) +#if BZIP + && (_CompressionMethod_FromZipFile != (short)CompressionMethod.BZip2) +#endif + ) + throw new ZipException(String.Format("Entry {0} uses an unsupported compression method (0x{1:X2}, {2})", + FileName, _CompressionMethod_FromZipFile, UnsupportedCompressionMethod)); + } + + + private void SetupCryptoForExtract(string password) + { + //if (password == null) return; + if (_Encryption_FromZipFile == EncryptionAlgorithm.None) return; + + if (_Encryption_FromZipFile == EncryptionAlgorithm.PkzipWeak) + { + if (password == null) + throw new ZipException("Missing password."); + + this.ArchiveStream.Seek(this.FileDataPosition - 12, SeekOrigin.Begin); + // workitem 10178 + Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(this.ArchiveStream); + _zipCrypto_forExtract = ZipCrypto.ForRead(password, this); + } + +#if AESCRYPTO + else if (_Encryption_FromZipFile == EncryptionAlgorithm.WinZipAes128 || + _Encryption_FromZipFile == EncryptionAlgorithm.WinZipAes256) + { + if (password == null) + throw new ZipException("Missing password."); + + // If we already have a WinZipAesCrypto object in place, use it. + // It can be set up in the ReadDirEntry(), or during a previous Extract. + if (_aesCrypto_forExtract != null) + { + _aesCrypto_forExtract.Password = password; + } + else + { + int sizeOfSaltAndPv = GetLengthOfCryptoHeaderBytes(_Encryption_FromZipFile); + this.ArchiveStream.Seek(this.FileDataPosition - sizeOfSaltAndPv, SeekOrigin.Begin); + // workitem 10178 + Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(this.ArchiveStream); + int keystrength = GetKeyStrengthInBits(_Encryption_FromZipFile); + _aesCrypto_forExtract = WinZipAesCrypto.ReadFromStream(password, keystrength, this.ArchiveStream); + } + } +#endif + } + + + + /// + /// Validates that the args are consistent. + /// + /// + /// Only one of {baseDir, outStream} can be non-null. + /// If baseDir is non-null, then the outputFile is created. + /// + private bool ValidateOutput(string basedir, Stream outstream, out string outFileName) + { + if (basedir != null) + { + // Sometimes the name on the entry starts with a slash. + // Rather than unpack to the root of the volume, we're going to + // drop the slash and unpack to the specified base directory. + string f = this.FileName.Replace("\\","/"); + + // workitem 11772: remove drive letter with separator + if (f.IndexOf(':') == 1) + f= f.Substring(2); + + if (f.StartsWith("/")) + f= f.Substring(1); + + // String.Contains is not available on .NET CF 2.0 + + if (_container.ZipFile.FlattenFoldersOnExtract) + outFileName = Path.Combine(basedir, + (f.IndexOf('/') != -1) ? Path.GetFileName(f) : f); + else + outFileName = Path.Combine(basedir, f); + + // workitem 10639 + // 2018-04-02 DL2ALF workaround + // determine when working on a Linux system --> no drive letter found + // change back slashes to backslash on Windows + if (outFileName.Contains(":")) + outFileName = outFileName.Replace("/","\\"); + // check if it is a directory + if ((IsDirectory) || (FileName.EndsWith("/"))) + { + if (!Directory.Exists(outFileName)) + { + Directory.CreateDirectory(outFileName); + _SetTimes(outFileName, false); + } + else + { + // the dir exists, maybe we want to overwrite times. + if (ExtractExistingFile == ExtractExistingFileAction.OverwriteSilently) + _SetTimes(outFileName, false); + } + return true; // true == all done, caller will return + } + return false; // false == work to do by caller. + } + + if (outstream != null) + { + outFileName = null; + if ((IsDirectory) || (FileName.EndsWith("/"))) + { + // extract a directory to streamwriter? nothing to do! + return true; // true == all done! caller can return + } + return false; + } + + throw new ArgumentNullException("outstream"); + } + + + #endregion + + } +} diff --git a/DotNetZip/Zip/ZipEntry.Read.cs b/DotNetZip/Zip/ZipEntry.Read.cs new file mode 100644 index 0000000..09d1385 --- /dev/null +++ b/DotNetZip/Zip/ZipEntry.Read.cs @@ -0,0 +1,798 @@ +// ZipEntry.Read.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2009-2011 Dino Chiesa +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2011-July-09 21:31:28> +// +// ------------------------------------------------------------------ +// +// This module defines logic for Reading the ZipEntry from a +// zip file. +// +// ------------------------------------------------------------------ + + +using System; +using System.IO; +using System.Threading; + +namespace Ionic.Zip +{ + public partial class ZipEntry + { + private int _readExtraDepth; + private void ReadExtraField() + { + _readExtraDepth++; + // workitem 8098: ok (restore) + long posn = this.ArchiveStream.Position; + this.ArchiveStream.Seek(this._RelativeOffsetOfLocalHeader, SeekOrigin.Begin); + // workitem 10178 + Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(this.ArchiveStream); + + byte[] block = new byte[30]; + this.ArchiveStream.Read(block, 0, block.Length); + int i = 26; + Int16 filenameLength = (short)(block[i++] + block[i++] * 256); + Int16 extraFieldLength = (short)(block[i++] + block[i++] * 256); + + // workitem 8098: ok (relative) + this.ArchiveStream.Seek(filenameLength, SeekOrigin.Current); + // workitem 10178 + Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(this.ArchiveStream); + + ProcessExtraField(this.ArchiveStream, extraFieldLength); + + // workitem 8098: ok (restore) + this.ArchiveStream.Seek(posn, SeekOrigin.Begin); + // workitem 10178 + Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(this.ArchiveStream); + _readExtraDepth--; + } + + + private static bool ReadHeader(ZipEntry ze, System.Text.Encoding defaultEncoding) + { + int bytesRead = 0; + + // change for workitem 8098 + ze._RelativeOffsetOfLocalHeader = ze.ArchiveStream.Position; + + int signature = Ionic.Zip.SharedUtilities.ReadEntrySignature(ze.ArchiveStream); + bytesRead += 4; + + // Return false if this is not a local file header signature. + if (ZipEntry.IsNotValidSig(signature)) + { + // Getting "not a ZipEntry signature" is not always wrong or an error. + // This will happen after the last entry in a zipfile. In that case, we + // expect to read : + // a ZipDirEntry signature (if a non-empty zip file) or + // a ZipConstants.EndOfCentralDirectorySignature. + // + // Anything else is a surprise. + + ze.ArchiveStream.Seek(-4, SeekOrigin.Current); // unread the signature + // workitem 10178 + Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(ze.ArchiveStream); + if (ZipEntry.IsNotValidZipDirEntrySig(signature) && (signature != ZipConstants.EndOfCentralDirectorySignature)) + { + throw new BadReadException(String.Format(" Bad signature (0x{0:X8}) at position 0x{1:X8}", signature, ze.ArchiveStream.Position)); + } + return false; + } + + byte[] block = new byte[26]; + int n = ze.ArchiveStream.Read(block, 0, block.Length); + if (n != block.Length) return false; + bytesRead += n; + + int i = 0; + ze._VersionNeeded = (Int16)(block[i++] + block[i++] * 256); + ze._BitField = (Int16)(block[i++] + block[i++] * 256); + ze._CompressionMethod_FromZipFile = ze._CompressionMethod = (Int16)(block[i++] + block[i++] * 256); + ze._TimeBlob = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256; + // transform the time data into something usable (a DateTime) + ze._LastModified = Ionic.Zip.SharedUtilities.PackedToDateTime(ze._TimeBlob); + ze._timestamp |= ZipEntryTimestamp.DOS; + + if ((ze._BitField & 0x01) == 0x01) + { + ze._Encryption_FromZipFile = ze._Encryption = EncryptionAlgorithm.PkzipWeak; // this *may* change after processing the Extra field + ze._sourceIsEncrypted = true; + } + + // NB: if ((ze._BitField & 0x0008) != 0x0008), then the Compressed, uncompressed and + // CRC values are not true values; the true values will follow the entry data. + // But, regardless of the status of bit 3 in the bitfield, the slots for + // the three amigos may contain marker values for ZIP64. So we must read them. + { + ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); + ze._CompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); + ze._UncompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); + + if ((uint)ze._CompressedSize == 0xFFFFFFFF || + (uint)ze._UncompressedSize == 0xFFFFFFFF) + + ze._InputUsesZip64 = true; + } + + Int16 filenameLength = (short)(block[i++] + block[i++] * 256); + Int16 extraFieldLength = (short)(block[i++] + block[i++] * 256); + + block = new byte[filenameLength]; + n = ze.ArchiveStream.Read(block, 0, block.Length); + bytesRead += n; + + // if the UTF8 bit is set for this entry, override the + // encoding the application requested. + + if ((ze._BitField & 0x0800) == 0x0800) + { + // workitem 12744 + ze.AlternateEncoding = System.Text.Encoding.UTF8; + ze.AlternateEncodingUsage = ZipOption.Always; + } + + // need to use this form of GetString() for .NET CF + ze._FileNameInArchive = ze.AlternateEncoding.GetString(block, 0, block.Length); + + // workitem 6898 + if (ze._FileNameInArchive.EndsWith("/")) ze.MarkAsDirectory(); + + bytesRead += ze.ProcessExtraField(ze.ArchiveStream, extraFieldLength); + + ze._LengthOfTrailer = 0; + + // workitem 6607 - don't read for directories + // actually get the compressed size and CRC if necessary + if (!ze._FileNameInArchive.EndsWith("/") && (ze._BitField & 0x0008) == 0x0008) + { + // This descriptor exists only if bit 3 of the general + // purpose bit flag is set (see below). It is byte aligned + // and immediately follows the last byte of compressed data, + // as well as any encryption trailer, as with AES. + // This descriptor is used only when it was not possible to + // seek in the output .ZIP file, e.g., when the output .ZIP file + // was standard output or a non-seekable device. For ZIP64(tm) format + // archives, the compressed and uncompressed sizes are 8 bytes each. + + // workitem 8098: ok (restore) + long posn = ze.ArchiveStream.Position; + + // Here, we're going to loop until we find a ZipEntryDataDescriptorSignature and + // a consistent data record after that. To be consistent, the data record must + // indicate the length of the entry data. + bool wantMore = true; + long SizeOfDataRead = 0; + int tries = 0; + while (wantMore) + { + tries++; + // We call the FindSignature shared routine to find the specified signature + // in the already-opened zip archive, starting from the current cursor + // position in that filestream. If we cannot find the signature, then the + // routine returns -1, and the ReadHeader() method returns false, + // indicating we cannot read a legal entry header. If we have found it, + // then the FindSignature() method returns the number of bytes in the + // stream we had to seek forward, to find the sig. We need this to + // determine if the zip entry is valid, later. + + if (ze._container.ZipFile != null) + ze._container.ZipFile.OnReadBytes(ze); + + long d = Ionic.Zip.SharedUtilities.FindSignature(ze.ArchiveStream, ZipConstants.ZipEntryDataDescriptorSignature); + if (d == -1) return false; + + // total size of data read (through all loops of this). + SizeOfDataRead += d; + + if (ze._InputUsesZip64) + { + // read 1x 4-byte (CRC) and 2x 8-bytes (Compressed Size, Uncompressed Size) + block = new byte[20]; + n = ze.ArchiveStream.Read(block, 0, block.Length); + if (n != 20) return false; + + // do not increment bytesRead - it is for entry header only. + // the data we have just read is a footer (falls after the file data) + //bytesRead += n; + + i = 0; + ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); + ze._CompressedSize = BitConverter.ToInt64(block, i); + i += 8; + ze._UncompressedSize = BitConverter.ToInt64(block, i); + i += 8; + + ze._LengthOfTrailer += 24; // bytes including sig, CRC, Comp and Uncomp sizes + } + else + { + // read 3x 4-byte fields (CRC, Compressed Size, Uncompressed Size) + block = new byte[12]; + n = ze.ArchiveStream.Read(block, 0, block.Length); + if (n != 12) return false; + + // do not increment bytesRead - it is for entry header only. + // the data we have just read is a footer (falls after the file data) + //bytesRead += n; + + i = 0; + ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); + ze._CompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); + ze._UncompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); + + ze._LengthOfTrailer += 16; // bytes including sig, CRC, Comp and Uncomp sizes + + } + + wantMore = (SizeOfDataRead != ze._CompressedSize); + + if (wantMore) + { + // Seek back to un-read the last 12 bytes - maybe THEY contain + // the ZipEntryDataDescriptorSignature. + // (12 bytes for the CRC, Comp and Uncomp size.) + ze.ArchiveStream.Seek(-12, SeekOrigin.Current); + // workitem 10178 + Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(ze.ArchiveStream); + + // Adjust the size to account for the false signature read in + // FindSignature(). + SizeOfDataRead += 4; + } + } + + // seek back to previous position, to prepare to read file data + // workitem 8098: ok (restore) + ze.ArchiveStream.Seek(posn, SeekOrigin.Begin); + // workitem 10178 + Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(ze.ArchiveStream); + } + + ze._CompressedFileDataSize = ze._CompressedSize; + + + // bit 0 set indicates that some kind of encryption is in use + if ((ze._BitField & 0x01) == 0x01) + { +#if AESCRYPTO + if (ze.Encryption == EncryptionAlgorithm.WinZipAes128 || + ze.Encryption == EncryptionAlgorithm.WinZipAes256) + { + int bits = ZipEntry.GetKeyStrengthInBits(ze._Encryption_FromZipFile); + // read in the WinZip AES metadata: salt + PV. 18 bytes for AES256. 10 bytes for AES128. + ze._aesCrypto_forExtract = WinZipAesCrypto.ReadFromStream(null, bits, ze.ArchiveStream); + bytesRead += ze._aesCrypto_forExtract.SizeOfEncryptionMetadata - 10; // MAC (follows crypto bytes) + // according to WinZip, the CompressedSize includes the AES Crypto framing data. + ze._CompressedFileDataSize -= ze._aesCrypto_forExtract.SizeOfEncryptionMetadata; + ze._LengthOfTrailer += 10; // MAC + } + else +#endif + { + // read in the header data for "weak" encryption + ze._WeakEncryptionHeader = new byte[12]; + bytesRead += ZipEntry.ReadWeakEncryptionHeader(ze._archiveStream, ze._WeakEncryptionHeader); + // decrease the filedata size by 12 bytes + ze._CompressedFileDataSize -= 12; + } + } + + // Remember the size of the blob for this entry. + // We also have the starting position in the stream for this entry. + ze._LengthOfHeader = bytesRead; + ze._TotalEntrySize = ze._LengthOfHeader + ze._CompressedFileDataSize + ze._LengthOfTrailer; + + + // We've read in the regular entry header, the extra field, and any + // encryption header. The pointer in the file is now at the start of the + // filedata, which is potentially compressed and encrypted. Just ahead in + // the file, there are _CompressedFileDataSize bytes of data, followed by + // potentially a non-zero length trailer, consisting of optionally, some + // encryption stuff (10 byte MAC for AES), and the bit-3 trailer (16 or 24 + // bytes). + + return true; + } + + + + internal static int ReadWeakEncryptionHeader(Stream s, byte[] buffer) + { + // PKZIP encrypts the compressed data stream. Encrypted files must + // be decrypted before they can be extracted. + + // Each PKZIP-encrypted file has an extra 12 bytes stored at the start of the data + // area defining the encryption header for that file. The encryption header is + // originally set to random values, and then itself encrypted, using three, 32-bit + // keys. The key values are initialized using the supplied encryption password. + // After each byte is encrypted, the keys are then updated using pseudo-random + // number generation techniques in combination with the same CRC-32 algorithm used + // in PKZIP and implemented in the CRC32.cs module in this project. + + // read the 12-byte encryption header + int additionalBytesRead = s.Read(buffer, 0, 12); + if (additionalBytesRead != 12) + throw new ZipException(String.Format("Unexpected end of data at position 0x{0:X8}", s.Position)); + + return additionalBytesRead; + } + + + + private static bool IsNotValidSig(int signature) + { + return (signature != ZipConstants.ZipEntrySignature); + } + + + /// + /// Reads one ZipEntry from the given stream. The content for + /// the entry does not get decompressed or decrypted. This method + /// basically reads metadata, and seeks. + /// + /// the ZipContainer this entry belongs to. + /// + /// true of this is the first entry being read from the stream. + /// + /// the ZipEntry read from the stream. + internal static ZipEntry ReadEntry(ZipContainer zc, bool first) + { + ZipFile zf = zc.ZipFile; + Stream s = zc.ReadStream; + System.Text.Encoding defaultEncoding = zc.AlternateEncoding; + ZipEntry entry = new ZipEntry(); + entry._Source = ZipEntrySource.ZipFile; + entry._container = zc; + entry._archiveStream = s; + if (zf != null) + zf.OnReadEntry(true, null); + + if (first) HandlePK00Prefix(s); + + // Read entry header, including any encryption header + if (!ReadHeader(entry, defaultEncoding)) return null; + + // Store the position in the stream for this entry + // change for workitem 8098 + entry.__FileDataPosition = entry.ArchiveStream.Position; + + // seek past the data without reading it. We will read on Extract() + s.Seek(entry._CompressedFileDataSize + entry._LengthOfTrailer, SeekOrigin.Current); + // workitem 10178 + Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s); + + // ReadHeader moves the file pointer to the end of the entry header, + // as well as any encryption header. + + // CompressedFileDataSize includes: + // the maybe compressed, maybe encrypted file data + // the encryption trailer, if any + // the bit 3 descriptor, if any + + // workitem 5306 + // http://www.codeplex.com/DotNetZip/WorkItem/View.aspx?WorkItemId=5306 + HandleUnexpectedDataDescriptor(entry); + + if (zf != null) + { + zf.OnReadBytes(entry); + zf.OnReadEntry(false, entry); + } + + return entry; + } + + + internal static void HandlePK00Prefix(Stream s) + { + // in some cases, the zip file begins with "PK00". This is a throwback and is rare, + // but we handle it anyway. We do not change behavior based on it. + uint datum = (uint)Ionic.Zip.SharedUtilities.ReadInt(s); + if (datum != ZipConstants.PackedToRemovableMedia) + { + s.Seek(-4, SeekOrigin.Current); // unread the block + // workitem 10178 + Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s); + } + } + + + + private static void HandleUnexpectedDataDescriptor(ZipEntry entry) + { + Stream s = entry.ArchiveStream; + + // In some cases, the "data descriptor" is present, without a signature, even when + // bit 3 of the BitField is NOT SET. This is the CRC, followed + // by the compressed length and the uncompressed length (4 bytes for each + // of those three elements). Need to check that here. + // + uint datum = (uint)Ionic.Zip.SharedUtilities.ReadInt(s); + if (datum == entry._Crc32) + { + int sz = Ionic.Zip.SharedUtilities.ReadInt(s); + if (sz == entry._CompressedSize) + { + sz = Ionic.Zip.SharedUtilities.ReadInt(s); + if (sz == entry._UncompressedSize) + { + // ignore everything and discard it. + } + else + { + s.Seek(-12, SeekOrigin.Current); // unread the three blocks + + // workitem 10178 + Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s); + } + } + else + { + s.Seek(-8, SeekOrigin.Current); // unread the two blocks + + // workitem 10178 + Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s); + } + } + else + { + s.Seek(-4, SeekOrigin.Current); // unread the block + + // workitem 10178 + Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s); + } + } + + + /// + /// Finds a particular segment in the given extra field. + /// This is used when modifying a previously-generated + /// extra field, in particular when removing the AES crypto + /// segment in the extra field. + /// + static internal int FindExtraFieldSegment(byte[] extra, int offx, UInt16 targetHeaderId) + { + int j = offx; + while (j + 3 < extra.Length) + { + UInt16 headerId = (UInt16)(extra[j++] + extra[j++] * 256); + if (headerId == targetHeaderId) return j-2; + + // else advance to next segment + Int16 dataSize = (short)(extra[j++] + extra[j++] * 256); + j+= dataSize; + } + + return -1; + } + + + /// + /// At current cursor position in the stream, read the extra + /// field, and set the properties on the ZipEntry instance + /// appropriately. This can be called when processing the + /// Extra field in the Central Directory, or in the local + /// header. + /// + internal int ProcessExtraField(Stream s, Int16 extraFieldLength) + { + int additionalBytesRead = 0; + if (extraFieldLength > 0) + { + byte[] buffer = this._Extra = new byte[extraFieldLength]; + additionalBytesRead = s.Read(buffer, 0, buffer.Length); + long posn = s.Position - additionalBytesRead; + int j = 0; + while (j + 3 < buffer.Length) + { + int start = j; + UInt16 headerId = (UInt16)(buffer[j++] + buffer[j++] * 256); + Int16 dataSize = (short)(buffer[j++] + buffer[j++] * 256); + switch (headerId) + { + case 0x000a: // NTFS ctime, atime, mtime + j = ProcessExtraFieldWindowsTimes(buffer, j, dataSize, posn); + break; + + case 0x5455: // Unix ctime, atime, mtime + j = ProcessExtraFieldUnixTimes(buffer, j, dataSize, posn); + break; + + case 0x5855: // Info-zip Extra field (outdated) + // This is outdated, so the field is supported on + // read only. + j = ProcessExtraFieldInfoZipTimes(buffer, j, dataSize, posn); + break; + + case 0x7855: // Unix uid/gid + // ignored. DotNetZip does not handle this field. + break; + + case 0x7875: // ?? + // ignored. I could not find documentation on this field, + // though it appears in some zip files. + break; + + case 0x0001: // ZIP64 + j = ProcessExtraFieldZip64(buffer, j, dataSize, posn); + break; + +#if AESCRYPTO + case 0x9901: // WinZip AES encryption is in use. (workitem 6834) + // we will handle this extra field only if compressionmethod is 0x63 + j = ProcessExtraFieldWinZipAes(buffer, j, dataSize, posn); + break; +#endif + case 0x0017: // workitem 7968: handle PKWare Strong encryption header + j = ProcessExtraFieldPkwareStrongEncryption(buffer, j); + break; + } + // move to the next Header in the extra field + j = start + dataSize + 4; + } + } + return additionalBytesRead; + } + + private int ProcessExtraFieldPkwareStrongEncryption(byte[] Buffer, int j) + { + // Value Size Description + // ----- ---- ----------- + // 0x0017 2 bytes Tag for this "extra" block type + // TSize 2 bytes Size of data that follows + // Format 2 bytes Format definition for this record + // AlgID 2 bytes Encryption algorithm identifier + // Bitlen 2 bytes Bit length of encryption key + // Flags 2 bytes Processing flags + // CertData TSize-8 Certificate decryption extra field data + // (refer to the explanation for CertData + // in the section describing the + // Certificate Processing Method under + // the Strong Encryption Specification) + + j += 2; + _UnsupportedAlgorithmId = (UInt16)(Buffer[j++] + Buffer[j++] * 256); + _Encryption_FromZipFile = _Encryption = EncryptionAlgorithm.Unsupported; + + // DotNetZip doesn't support this algorithm, but we don't need to throw + // here. we might just be reading the archive, which is fine. We'll + // need to throw if Extract() is called. + + return j; + } + + +#if AESCRYPTO + private int ProcessExtraFieldWinZipAes(byte[] buffer, int j, Int16 dataSize, long posn) + { + if (this._CompressionMethod == 0x0063) + { + if ((this._BitField & 0x01) != 0x01) + throw new BadReadException(String.Format(" Inconsistent metadata at position 0x{0:X16}", posn)); + + this._sourceIsEncrypted = true; + + //this._aesCrypto = new WinZipAesCrypto(this); + // see spec at http://www.winzip.com/aes_info.htm + if (dataSize != 7) + throw new BadReadException(String.Format(" Inconsistent size (0x{0:X4}) in WinZip AES field at position 0x{1:X16}", dataSize, posn)); + + this._WinZipAesMethod = BitConverter.ToInt16(buffer, j); + j += 2; + if (this._WinZipAesMethod != 0x01 && this._WinZipAesMethod != 0x02) + throw new BadReadException(String.Format(" Unexpected vendor version number (0x{0:X4}) for WinZip AES metadata at position 0x{1:X16}", + this._WinZipAesMethod, posn)); + + Int16 vendorId = BitConverter.ToInt16(buffer, j); + j += 2; + if (vendorId != 0x4541) + throw new BadReadException(String.Format(" Unexpected vendor ID (0x{0:X4}) for WinZip AES metadata at position 0x{1:X16}", vendorId, posn)); + + int keystrength = (buffer[j] == 1) ? 128 : (buffer[j] == 3) ? 256 : -1; + if (keystrength < 0) + throw new BadReadException(String.Format("Invalid key strength ({0})", keystrength)); + + _Encryption_FromZipFile = this._Encryption = (keystrength == 128) + ? EncryptionAlgorithm.WinZipAes128 + : EncryptionAlgorithm.WinZipAes256; + + j++; + + // set the actual compression method + this._CompressionMethod_FromZipFile = + this._CompressionMethod = BitConverter.ToInt16(buffer, j); + j += 2; // for the next segment of the extra field + } + return j; + } + +#endif + + private delegate T Func(); + + private int ProcessExtraFieldZip64(byte[] buffer, int j, Int16 dataSize, long posn) + { + // The PKWare spec says that any of {UncompressedSize, CompressedSize, + // RelativeOffset} exceeding 0xFFFFFFFF can lead to the ZIP64 header, + // and the ZIP64 header may contain one or more of those. If the + // values are present, they will be found in the prescribed order. + // There may also be a 4-byte "disk start number." + // This means that the DataSize must be 28 bytes or less. + + this._InputUsesZip64 = true; + + // workitem 7941: check datasize before reading. + if (dataSize > 28) + throw new BadReadException(String.Format(" Inconsistent size (0x{0:X4}) for ZIP64 extra field at position 0x{1:X16}", + dataSize, posn)); + int remainingData = dataSize; + + var slurp = new Func( () => { + if (remainingData < 8) + throw new BadReadException(String.Format(" Missing data for ZIP64 extra field, position 0x{0:X16}", posn)); + var x = BitConverter.ToInt64(buffer, j); + j+= 8; + remainingData -= 8; + return x; + }); + + if (this._UncompressedSize == 0xFFFFFFFF) + this._UncompressedSize = slurp(); + + if (this._CompressedSize == 0xFFFFFFFF) + this._CompressedSize = slurp(); + + if (this._RelativeOffsetOfLocalHeader == 0xFFFFFFFF) + this._RelativeOffsetOfLocalHeader = slurp(); + + // Ignore anything else. Potentially there are 4 more bytes for the + // disk start number. DotNetZip currently doesn't handle multi-disk + // archives. + return j; + } + + + private int ProcessExtraFieldInfoZipTimes(byte[] buffer, int j, Int16 dataSize, long posn) + { + if (dataSize != 12 && dataSize != 8) + throw new BadReadException(String.Format(" Unexpected size (0x{0:X4}) for InfoZip v1 extra field at position 0x{1:X16}", dataSize, posn)); + + Int32 timet = BitConverter.ToInt32(buffer, j); + this._Mtime = _unixEpoch.AddSeconds(timet); + j += 4; + + timet = BitConverter.ToInt32(buffer, j); + this._Atime = _unixEpoch.AddSeconds(timet); + j += 4; + + this._Ctime = DateTime.UtcNow; + + _ntfsTimesAreSet = true; + _timestamp |= ZipEntryTimestamp.InfoZip1; return j; + } + + + + private int ProcessExtraFieldUnixTimes(byte[] buffer, int j, Int16 dataSize, long posn) + { + // Console.WriteLine(_readExtraDepth + ":" + BitConverter.ToString(buffer) + " [" + buffer.Length + "<>" + dataSize + "]" ); + string s = BitConverter.ToString(buffer); + // The Unix filetimes are 32-bit unsigned integers, + // storing seconds since Unix epoch. + + if (dataSize != 13 && dataSize != 9 && dataSize != 5) + throw new BadReadException(String.Format(" Unexpected size (0x{0:X4}) for Extended Timestamp extra field at position 0x{1:X16}", dataSize, posn)); + + int remainingData = dataSize; + + var slurp = new Func( () => { + Int32 timet = BitConverter.ToInt32(buffer, j); + j += 4; + remainingData -= 4; + return _unixEpoch.AddSeconds(timet); + }); + + if (dataSize == 13 || _readExtraDepth > 0) + { + byte flag = buffer[j++]; + remainingData--; + + if ((flag & 0x0001) != 0 && remainingData >= 4) + this._Mtime = slurp(); + + this._Atime = ((flag & 0x0002) != 0 && remainingData >= 4) + ? slurp() + : DateTime.UtcNow; + + this._Ctime = ((flag & 0x0004) != 0 && remainingData >= 4) + ? slurp() + :DateTime.UtcNow; + + _timestamp |= ZipEntryTimestamp.Unix; + _ntfsTimesAreSet = true; + _emitUnixTimes = true; + } + else + ReadExtraField(); // will recurse + return j; + } + + + private int ProcessExtraFieldWindowsTimes(byte[] buffer, int j, Int16 dataSize, long posn) + { + // The NTFS filetimes are 64-bit unsigned integers, stored in Intel + // (least significant byte first) byte order. They are expressed as the + // number of 1.0E-07 seconds (1/10th microseconds!) past WinNT "epoch", + // which is "01-Jan-1601 00:00:00 UTC". + // + // HeaderId 2 bytes 0x000a == NTFS stuff + // Datasize 2 bytes ?? (usually 32) + // reserved 4 bytes ?? + // timetag 2 bytes 0x0001 == time + // size 2 bytes 24 == 8 bytes each for ctime, mtime, atime + // mtime 8 bytes win32 ticks since win32epoch + // atime 8 bytes win32 ticks since win32epoch + // ctime 8 bytes win32 ticks since win32epoch + + if (dataSize != 32) + throw new BadReadException(String.Format(" Unexpected size (0x{0:X4}) for NTFS times extra field at position 0x{1:X16}", dataSize, posn)); + + j += 4; // reserved + Int16 timetag = (Int16)(buffer[j] + buffer[j + 1] * 256); + Int16 addlsize = (Int16)(buffer[j + 2] + buffer[j + 3] * 256); + j += 4; // tag and size + + if (timetag == 0x0001 && addlsize == 24) + { + Int64 z = BitConverter.ToInt64(buffer, j); + this._Mtime = DateTime.FromFileTimeUtc(z); + j += 8; + + // At this point the library *could* set the LastModified value + // to coincide with the Mtime value. In theory, they refer to + // the same property of the file, and should be the same anyway, + // allowing for differences in precision. But they are + // independent quantities in the zip archive, and this library + // will keep them separate in the object model. There is no ill + // effect from this, because as files are extracted, the + // higher-precision value (Mtime) is used if it is present. + // Apps may wish to compare the Mtime versus LastModified + // values, but any difference when both are present is not + // germaine to the correctness of the library. but note: when + // explicitly setting either value, both are set. See the setter + // for LastModified or the SetNtfsTimes() method. + + z = BitConverter.ToInt64(buffer, j); + this._Atime = DateTime.FromFileTimeUtc(z); + j += 8; + + z = BitConverter.ToInt64(buffer, j); + this._Ctime = DateTime.FromFileTimeUtc(z); + j += 8; + + _ntfsTimesAreSet = true; + _timestamp |= ZipEntryTimestamp.Windows; + _emitNtfsTimes = true; + } + return j; + } + + + } +} diff --git a/DotNetZip/Zip/ZipEntry.Write.cs b/DotNetZip/Zip/ZipEntry.Write.cs new file mode 100644 index 0000000..b4e87f6 --- /dev/null +++ b/DotNetZip/Zip/ZipEntry.Write.cs @@ -0,0 +1,2582 @@ +//#define Trace + +// ZipEntry.Write.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2009-2011 Dino Chiesa +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// Last Saved: <2011-July-30 14:55:47> +// +// ------------------------------------------------------------------ +// +// This module defines logic for writing (saving) the ZipEntry into a +// zip file. +// +// ------------------------------------------------------------------ + + +using System; +using System.IO; +using RE = System.Text.RegularExpressions; + +namespace Ionic.Zip +{ + public partial class ZipEntry + { + internal void WriteCentralDirectoryEntry(Stream s) + { + byte[] bytes = new byte[4096]; + int i = 0; + // signature + bytes[i++] = (byte)(ZipConstants.ZipDirEntrySignature & 0x000000FF); + bytes[i++] = (byte)((ZipConstants.ZipDirEntrySignature & 0x0000FF00) >> 8); + bytes[i++] = (byte)((ZipConstants.ZipDirEntrySignature & 0x00FF0000) >> 16); + bytes[i++] = (byte)((ZipConstants.ZipDirEntrySignature & 0xFF000000) >> 24); + + // Version Made By + // workitem 7071 + // We must not overwrite the VersionMadeBy field when writing out a zip + // archive. The VersionMadeBy tells the zip reader the meaning of the + // File attributes. Overwriting the VersionMadeBy will result in + // inconsistent metadata. Consider the scenario where the application + // opens and reads a zip file that had been created on Linux. Then the + // app adds one file to the Zip archive, and saves it. The file + // attributes for all the entries added on Linux will be significant for + // Linux. Therefore the VersionMadeBy for those entries must not be + // changed. Only the entries that are actually created on Windows NTFS + // should get the VersionMadeBy indicating Windows/NTFS. + bytes[i++] = (byte)(_VersionMadeBy & 0x00FF); + bytes[i++] = (byte)((_VersionMadeBy & 0xFF00) >> 8); + + // Apparently we want to duplicate the extra field here; we cannot + // simply zero it out and assume tools and apps will use the right one. + + ////Int16 extraFieldLengthSave = (short)(_EntryHeader[28] + _EntryHeader[29] * 256); + ////_EntryHeader[28] = 0; + ////_EntryHeader[29] = 0; + + // Version Needed, Bitfield, compression method, lastmod, + // crc, compressed and uncompressed sizes, filename length and extra field length. + // These are all present in the local file header, but they may be zero values there. + // So we cannot just copy them. + + // workitem 11969: Version Needed To Extract in central directory must be + // the same as the local entry or MS .NET System.IO.Zip fails read. + Int16 vNeeded = (Int16)(VersionNeeded != 0 ? VersionNeeded : 20); + // workitem 12964 + if (_OutputUsesZip64==null) + { + // a zipentry in a zipoutputstream, with zero bytes written + _OutputUsesZip64 = new Nullable(_container.Zip64 == Zip64Option.Always); + } + + Int16 versionNeededToExtract = (Int16)(_OutputUsesZip64.Value ? 45 : vNeeded); +#if BZIP + if (this.CompressionMethod == Ionic.Zip.CompressionMethod.BZip2) + versionNeededToExtract = 46; +#endif + + bytes[i++] = (byte)(versionNeededToExtract & 0x00FF); + bytes[i++] = (byte)((versionNeededToExtract & 0xFF00) >> 8); + + bytes[i++] = (byte)(_BitField & 0x00FF); + bytes[i++] = (byte)((_BitField & 0xFF00) >> 8); + + bytes[i++] = (byte)(_CompressionMethod & 0x00FF); + bytes[i++] = (byte)((_CompressionMethod & 0xFF00) >> 8); + +#if AESCRYPTO + if (Encryption == EncryptionAlgorithm.WinZipAes128 || + Encryption == EncryptionAlgorithm.WinZipAes256) + { + i -= 2; + bytes[i++] = 0x63; + bytes[i++] = 0; + } +#endif + + bytes[i++] = (byte)(_TimeBlob & 0x000000FF); + bytes[i++] = (byte)((_TimeBlob & 0x0000FF00) >> 8); + bytes[i++] = (byte)((_TimeBlob & 0x00FF0000) >> 16); + bytes[i++] = (byte)((_TimeBlob & 0xFF000000) >> 24); + bytes[i++] = (byte)(_Crc32 & 0x000000FF); + bytes[i++] = (byte)((_Crc32 & 0x0000FF00) >> 8); + bytes[i++] = (byte)((_Crc32 & 0x00FF0000) >> 16); + bytes[i++] = (byte)((_Crc32 & 0xFF000000) >> 24); + + int j = 0; + if (_OutputUsesZip64.Value) + { + // CompressedSize (Int32) and UncompressedSize - all 0xFF + for (j = 0; j < 8; j++) + bytes[i++] = 0xFF; + } + else + { + bytes[i++] = (byte)(_CompressedSize & 0x000000FF); + bytes[i++] = (byte)((_CompressedSize & 0x0000FF00) >> 8); + bytes[i++] = (byte)((_CompressedSize & 0x00FF0000) >> 16); + bytes[i++] = (byte)((_CompressedSize & 0xFF000000) >> 24); + + bytes[i++] = (byte)(_UncompressedSize & 0x000000FF); + bytes[i++] = (byte)((_UncompressedSize & 0x0000FF00) >> 8); + bytes[i++] = (byte)((_UncompressedSize & 0x00FF0000) >> 16); + bytes[i++] = (byte)((_UncompressedSize & 0xFF000000) >> 24); + } + + byte[] fileNameBytes = GetEncodedFileNameBytes(); + Int16 filenameLength = (Int16)fileNameBytes.Length; + bytes[i++] = (byte)(filenameLength & 0x00FF); + bytes[i++] = (byte)((filenameLength & 0xFF00) >> 8); + + // do this again because now we have real data + _presumeZip64 = _OutputUsesZip64.Value; + + // workitem 11131 + // + // cannot generate the extra field again, here's why: In the case of a + // zero-byte entry, which uses encryption, DotNetZip will "remove" the + // encryption from the entry. It does this in PostProcessOutput; it + // modifies the entry header, and rewrites it, resetting the Bitfield + // (one bit indicates encryption), and potentially resetting the + // compression method - for AES the Compression method is 0x63, and it + // would get reset to zero (no compression). It then calls SetLength() + // to truncate the stream to remove the encryption header (12 bytes for + // AES256). But, it leaves the previously-generated "Extra Field" + // metadata (11 bytes) for AES in the entry header. This extra field + // data is now "orphaned" - it refers to AES encryption when in fact no + // AES encryption is used. But no problem, the PKWARE spec says that + // unrecognized extra fields can just be ignored. ok. After "removal" + // of AES encryption, the length of the Extra Field can remains the + // same; it's just that there will be 11 bytes in there that previously + // pertained to AES which are now unused. Even the field code is still + // there, but it will be unused by readers, as the encryption bit is not + // set. + // + // Re-calculating the Extra field now would produce a block that is 11 + // bytes shorter, and that mismatch - between the extra field in the + // local header and the extra field in the Central Directory - would + // cause problems. (where? why? what problems?) So we can't do + // that. It's all good though, because though the content may have + // changed, the length definitely has not. Also, the _EntryHeader + // contains the "updated" extra field (after PostProcessOutput) at + // offset (30 + filenameLength). + + _Extra = ConstructExtraField(true); + + Int16 extraFieldLength = (Int16)((_Extra == null) ? 0 : _Extra.Length); + bytes[i++] = (byte)(extraFieldLength & 0x00FF); + bytes[i++] = (byte)((extraFieldLength & 0xFF00) >> 8); + + // File (entry) Comment Length + // the _CommentBytes private field was set during WriteHeader() + int commentLength = (_CommentBytes == null) ? 0 : _CommentBytes.Length; + + // the size of our buffer defines the max length of the comment we can write + if (commentLength + i > bytes.Length) commentLength = bytes.Length - i; + bytes[i++] = (byte)(commentLength & 0x00FF); + bytes[i++] = (byte)((commentLength & 0xFF00) >> 8); + + // Disk number start + bool segmented = (this._container.ZipFile != null) && + (this._container.ZipFile.MaxOutputSegmentSize != 0); + if (segmented) // workitem 13915 + { + // Emit nonzero disknumber only if saving segmented archive. + bytes[i++] = (byte)(_diskNumber & 0x00FF); + bytes[i++] = (byte)((_diskNumber & 0xFF00) >> 8); + } + else + { + // If reading a segmneted archive and saving to a regular archive, + // ZipEntry._diskNumber will be non-zero but it should be saved as + // zero. + bytes[i++] = 0; + bytes[i++] = 0; + } + + // internal file attrs + // workitem 7801 + bytes[i++] = (byte)((_IsText) ? 1 : 0); // lo bit: filetype hint. 0=bin, 1=txt. + bytes[i++] = 0; + + // external file attrs + // workitem 7071 + bytes[i++] = (byte)(_ExternalFileAttrs & 0x000000FF); + bytes[i++] = (byte)((_ExternalFileAttrs & 0x0000FF00) >> 8); + bytes[i++] = (byte)((_ExternalFileAttrs & 0x00FF0000) >> 16); + bytes[i++] = (byte)((_ExternalFileAttrs & 0xFF000000) >> 24); + + // workitem 11131 + // relative offset of local header. + // + // If necessary to go to 64-bit value, then emit 0xFFFFFFFF, + // else write out the value. + // + // Even if zip64 is required for other reasons - number of the entry + // > 65534, or uncompressed size of the entry > MAX_INT32, the ROLH + // need not be stored in a 64-bit field . + if (_RelativeOffsetOfLocalHeader > 0xFFFFFFFFL) // _OutputUsesZip64.Value + { + bytes[i++] = 0xFF; + bytes[i++] = 0xFF; + bytes[i++] = 0xFF; + bytes[i++] = 0xFF; + } + else + { + bytes[i++] = (byte)(_RelativeOffsetOfLocalHeader & 0x000000FF); + bytes[i++] = (byte)((_RelativeOffsetOfLocalHeader & 0x0000FF00) >> 8); + bytes[i++] = (byte)((_RelativeOffsetOfLocalHeader & 0x00FF0000) >> 16); + bytes[i++] = (byte)((_RelativeOffsetOfLocalHeader & 0xFF000000) >> 24); + } + + // actual filename + Buffer.BlockCopy(fileNameBytes, 0, bytes, i, filenameLength); + i += filenameLength; + + // "Extra field" + if (_Extra != null) + { + // workitem 11131 + // + // copy from EntryHeader if available - it may have been updated. + // if not, copy from Extra. This would be unnecessary if I just + // updated the Extra field when updating EntryHeader, in + // PostProcessOutput. + + //?? I don't understand why I wouldn't want to just use + // the recalculated Extra field. ?? + + // byte[] h = _EntryHeader ?? _Extra; + // int offx = (h == _EntryHeader) ? 30 + filenameLength : 0; + // Buffer.BlockCopy(h, offx, bytes, i, extraFieldLength); + // i += extraFieldLength; + + byte[] h = _Extra; + int offx = 0; + Buffer.BlockCopy(h, offx, bytes, i, extraFieldLength); + i += extraFieldLength; + } + + // file (entry) comment + if (commentLength != 0) + { + // now actually write the comment itself into the byte buffer + Buffer.BlockCopy(_CommentBytes, 0, bytes, i, commentLength); + // for (j = 0; (j < commentLength) && (i + j < bytes.Length); j++) + // bytes[i + j] = _CommentBytes[j]; + i += commentLength; + } + + s.Write(bytes, 0, i); + } + + +#if INFOZIP_UTF8 + static private bool FileNameIsUtf8(char[] FileNameChars) + { + bool isUTF8 = false; + bool isUnicode = false; + for (int j = 0; j < FileNameChars.Length; j++) + { + byte[] b = System.BitConverter.GetBytes(FileNameChars[j]); + isUnicode |= (b.Length != 2); + isUnicode |= (b[1] != 0); + isUTF8 |= ((b[0] & 0x80) != 0); + } + + return isUTF8; + } +#endif + + + private byte[] ConstructExtraField(bool forCentralDirectory) + { + var listOfBlocks = new System.Collections.Generic.List(); + byte[] block; + + // Conditionally emit an extra field with Zip64 information. If the + // Zip64 option is Always, we emit the field, before knowing that it's + // necessary. Later, if it turns out this entry does not need zip64, + // we'll set the header ID to rubbish and the data will be ignored. + // This results in additional overhead metadata in the zip file, but + // it will be small in comparison to the entry data. + // + // On the other hand if the Zip64 option is AsNecessary and it's NOT + // for the central directory, then we do the same thing. Or, if the + // Zip64 option is AsNecessary and it IS for the central directory, + // and the entry requires zip64, then emit the header. + if (_container.Zip64 == Zip64Option.Always || + (_container.Zip64 == Zip64Option.AsNecessary && + (!forCentralDirectory || _entryRequiresZip64.Value))) + { + // add extra field for zip64 here + // workitem 7924 + int sz = 4 + (forCentralDirectory ? 28 : 16); + block = new byte[sz]; + int i = 0; + + if (_presumeZip64 || forCentralDirectory) + { + // HeaderId = always use zip64 extensions. + block[i++] = 0x01; + block[i++] = 0x00; + } + else + { + // HeaderId = dummy data now, maybe set to 0x0001 (ZIP64) later. + block[i++] = 0x99; + block[i++] = 0x99; + } + + // DataSize + block[i++] = (byte)(sz - 4); // decimal 28 or 16 (workitem 7924) + block[i++] = 0x00; + + // The actual metadata - we may or may not have real values yet... + + // uncompressed size + Array.Copy(BitConverter.GetBytes(_UncompressedSize), 0, block, i, 8); + i += 8; + // compressed size + Array.Copy(BitConverter.GetBytes(_CompressedSize), 0, block, i, 8); + i += 8; + + // workitem 7924 - only include this if the "extra" field is for + // use in the central directory. It is unnecessary and not useful + // for local header; makes WinZip choke. + if (forCentralDirectory) + { + // relative offset + Array.Copy(BitConverter.GetBytes(_RelativeOffsetOfLocalHeader), 0, block, i, 8); + i += 8; + + // starting disk number + Array.Copy(BitConverter.GetBytes(0), 0, block, i, 4); + } + listOfBlocks.Add(block); + } + + +#if AESCRYPTO + if (Encryption == EncryptionAlgorithm.WinZipAes128 || + Encryption == EncryptionAlgorithm.WinZipAes256) + { + block = new byte[4 + 7]; + int i = 0; + // extra field for WinZip AES + // header id + block[i++] = 0x01; + block[i++] = 0x99; + + // data size + block[i++] = 0x07; + block[i++] = 0x00; + + // vendor number + block[i++] = 0x01; // AE-1 - means "Verify CRC" + block[i++] = 0x00; + + // vendor id "AE" + block[i++] = 0x41; + block[i++] = 0x45; + + // key strength + int keystrength = GetKeyStrengthInBits(Encryption); + if (keystrength == 128) + block[i] = 1; + else if (keystrength == 256) + block[i] = 3; + else + block[i] = 0xFF; + i++; + + // actual compression method + block[i++] = (byte)(_CompressionMethod & 0x00FF); + block[i++] = (byte)(_CompressionMethod & 0xFF00); + + listOfBlocks.Add(block); + } +#endif + + if (_ntfsTimesAreSet && _emitNtfsTimes) + { + block = new byte[32 + 4]; + // HeaderId 2 bytes 0x000a == NTFS times + // Datasize 2 bytes 32 + // reserved 4 bytes ?? don't care + // timetag 2 bytes 0x0001 == NTFS time + // size 2 bytes 24 == 8 bytes each for ctime, mtime, atime + // mtime 8 bytes win32 ticks since win32epoch + // atime 8 bytes win32 ticks since win32epoch + // ctime 8 bytes win32 ticks since win32epoch + int i = 0; + // extra field for NTFS times + // header id + block[i++] = 0x0a; + block[i++] = 0x00; + + // data size + block[i++] = 32; + block[i++] = 0; + + i += 4; // reserved + + // time tag + block[i++] = 0x01; + block[i++] = 0x00; + + // data size (again) + block[i++] = 24; + block[i++] = 0; + + Int64 z = _Mtime.ToFileTime(); + Array.Copy(BitConverter.GetBytes(z), 0, block, i, 8); + i += 8; + z = _Atime.ToFileTime(); + Array.Copy(BitConverter.GetBytes(z), 0, block, i, 8); + i += 8; + z = _Ctime.ToFileTime(); + Array.Copy(BitConverter.GetBytes(z), 0, block, i, 8); + i += 8; + + listOfBlocks.Add(block); + } + + if (_ntfsTimesAreSet && _emitUnixTimes) + { + int len = 5 + 4; + if (!forCentralDirectory) len += 8; + + block = new byte[len]; + // local form: + // -------------- + // HeaderId 2 bytes 0x5455 == unix timestamp + // Datasize 2 bytes 13 + // flags 1 byte 7 (low three bits all set) + // mtime 4 bytes seconds since unix epoch + // atime 4 bytes seconds since unix epoch + // ctime 4 bytes seconds since unix epoch + // + // central directory form: + //--------------------------------- + // HeaderId 2 bytes 0x5455 == unix timestamp + // Datasize 2 bytes 5 + // flags 1 byte 7 (low three bits all set) + // mtime 4 bytes seconds since unix epoch + // + int i = 0; + // extra field for "unix" times + // header id + block[i++] = 0x55; + block[i++] = 0x54; + + // data size + block[i++] = unchecked((byte)(len - 4)); + block[i++] = 0; + + // flags + block[i++] = 0x07; + + Int32 z = unchecked((int)((_Mtime - _unixEpoch).TotalSeconds)); + Array.Copy(BitConverter.GetBytes(z), 0, block, i, 4); + i += 4; + if (!forCentralDirectory) + { + z = unchecked((int)((_Atime - _unixEpoch).TotalSeconds)); + Array.Copy(BitConverter.GetBytes(z), 0, block, i, 4); + i += 4; + z = unchecked((int)((_Ctime - _unixEpoch).TotalSeconds)); + Array.Copy(BitConverter.GetBytes(z), 0, block, i, 4); + i += 4; + } + listOfBlocks.Add(block); + } + + + // inject other blocks here... + + + // concatenate any blocks we've got: + byte[] aggregateBlock = null; + if (listOfBlocks.Count > 0) + { + int totalLength = 0; + int i, current = 0; + for (i = 0; i < listOfBlocks.Count; i++) + totalLength += listOfBlocks[i].Length; + aggregateBlock = new byte[totalLength]; + for (i = 0; i < listOfBlocks.Count; i++) + { + System.Array.Copy(listOfBlocks[i], 0, aggregateBlock, current, listOfBlocks[i].Length); + current += listOfBlocks[i].Length; + } + } + + return aggregateBlock; + } + + + + // private System.Text.Encoding GenerateCommentBytes() + // { + // var getEncoding = new Func({ + // switch (AlternateEncodingUsage) + // { + // case ZipOption.Always: + // return AlternateEncoding; + // case ZipOption.Never: + // return ibm437; + // } + // var cb = ibm437.GetBytes(_Comment); + // // need to use this form of GetString() for .NET CF + // string s1 = ibm437.GetString(cb, 0, cb.Length); + // if (s1 == _Comment) + // return ibm437; + // return AlternateEncoding; + // }); + // + // var encoding = getEncoding(); + // _CommentBytes = encoding.GetBytes(_Comment); + // return encoding; + // } + + + private string NormalizeFileName() + { + // here, we need to flip the backslashes to forward-slashes, + // also, we need to trim the \\server\share syntax from any UNC path. + // and finally, we need to remove any leading .\ + + string SlashFixed = FileName.Replace("\\", "/"); + string s1 = null; + if ((_TrimVolumeFromFullyQualifiedPaths) && (FileName.Length >= 3) + && (FileName[1] == ':') && (SlashFixed[2] == '/')) + { + // trim off volume letter, colon, and slash + s1 = SlashFixed.Substring(3); + } + else if ((FileName.Length >= 4) + && ((SlashFixed[0] == '/') && (SlashFixed[1] == '/'))) + { + int n = SlashFixed.IndexOf('/', 2); + if (n == -1) + throw new ArgumentException("The path for that entry appears to be badly formatted"); + s1 = SlashFixed.Substring(n + 1); + } + else if ((FileName.Length >= 3) + && ((SlashFixed[0] == '.') && (SlashFixed[1] == '/'))) + { + // trim off dot and slash + s1 = SlashFixed.Substring(2); + } + else + { + s1 = SlashFixed; + } + return s1; + } + + + /// + /// generate and return a byte array that encodes the filename + /// for the entry. + /// + /// + /// + /// side effects: generate and store into _CommentBytes the + /// byte array for any comment attached to the entry. Also + /// sets _actualEncoding to indicate the actual encoding + /// used. The same encoding is used for both filename and + /// comment. + /// + /// + private byte[] GetEncodedFileNameBytes() + { + // workitem 6513 + var s1 = NormalizeFileName(); + + switch(AlternateEncodingUsage) + { + case ZipOption.Always: + if (!(_Comment == null || _Comment.Length == 0)) + _CommentBytes = AlternateEncoding.GetBytes(_Comment); + _actualEncoding = AlternateEncoding; + return AlternateEncoding.GetBytes(s1); + + case ZipOption.Never: + if (!(_Comment == null || _Comment.Length == 0)) + _CommentBytes = ibm437.GetBytes(_Comment); + _actualEncoding = ibm437; + return ibm437.GetBytes(s1); + } + + // arriving here means AlternateEncodingUsage is "AsNecessary" + + // case ZipOption.AsNecessary: + // workitem 6513: when writing, use the alternative encoding + // only when _actualEncoding is not yet set (it can be set + // during Read), and when ibm437 will not do. + + byte[] result = ibm437.GetBytes(s1); + // need to use this form of GetString() for .NET CF + string s2 = ibm437.GetString(result, 0, result.Length); + _CommentBytes = null; + if (s2 != s1) + { + // Encoding the filename with ibm437 does not allow round-trips. + // Therefore, use the alternate encoding. Assume it will work, + // no checking of round trips here. + result = AlternateEncoding.GetBytes(s1); + if (_Comment != null && _Comment.Length != 0) + _CommentBytes = AlternateEncoding.GetBytes(_Comment); + _actualEncoding = AlternateEncoding; + return result; + } + + _actualEncoding = ibm437; + + // Using ibm437, FileName can be encoded without information + // loss; now try the Comment. + + // if there is no comment, use ibm437. + if (_Comment == null || _Comment.Length == 0) + return result; + + // there is a comment. Get the encoded form. + byte[] cbytes = ibm437.GetBytes(_Comment); + string c2 = ibm437.GetString(cbytes,0,cbytes.Length); + + // Check for round-trip. + if (c2 != Comment) + { + // Comment cannot correctly be encoded with ibm437. Use + // the alternate encoding. + + result = AlternateEncoding.GetBytes(s1); + _CommentBytes = AlternateEncoding.GetBytes(_Comment); + _actualEncoding = AlternateEncoding; + return result; + } + + // use IBM437 + _CommentBytes = cbytes; + return result; + } + + + + private bool WantReadAgain() + { + if (_UncompressedSize < 0x10) return false; + if (_CompressionMethod == 0x00) return false; + if (CompressionLevel == Ionic.Zlib.CompressionLevel.None) return false; + if (_CompressedSize < _UncompressedSize) return false; + + if (this._Source == ZipEntrySource.Stream && !this._sourceStream.CanSeek) return false; + +#if AESCRYPTO + if (_aesCrypto_forWrite != null && (CompressedSize - _aesCrypto_forWrite.SizeOfEncryptionMetadata) <= UncompressedSize + 0x10) return false; +#endif + + if (_zipCrypto_forWrite != null && (CompressedSize - 12) <= UncompressedSize) return false; + + return true; + } + + + + private void MaybeUnsetCompressionMethodForWriting(int cycle) + { + // if we've already tried with compression... turn it off this time + if (cycle > 1) + { + _CompressionMethod = 0x0; + return; + } + // compression for directories = 0x00 (No Compression) + if (IsDirectory) + { + _CompressionMethod = 0x0; + return; + } + + if (this._Source == ZipEntrySource.ZipFile) + { + return; // do nothing + } + + // If __FileDataPosition is zero, then that means we will get the data + // from a file or stream. + + // It is never possible to compress a zero-length file, so we check for + // this condition. + + if (this._Source == ZipEntrySource.Stream) + { + // workitem 7742 + if (_sourceStream != null && _sourceStream.CanSeek) + { + // Length prop will throw if CanSeek is false + long fileLength = _sourceStream.Length; + if (fileLength == 0) + { + _CompressionMethod = 0x00; + return; + } + } + } + else if ((this._Source == ZipEntrySource.FileSystem) && (SharedUtilities.GetFileLength(LocalFileName) == 0L)) + { + _CompressionMethod = 0x00; + return; + } + + // Ok, we're getting the data to be compressed from a + // non-zero-length file or stream, or a file or stream of + // unknown length, and we presume that it is non-zero. In + // that case we check the callback to see if the app wants + // to tell us whether to compress or not. + if (SetCompression != null) + CompressionLevel = SetCompression(LocalFileName, _FileNameInArchive); + + // finally, set CompressionMethod to None if CompressionLevel is None + if (CompressionLevel == (short)Ionic.Zlib.CompressionLevel.None && + CompressionMethod == Ionic.Zip.CompressionMethod.Deflate) + _CompressionMethod = 0x00; + + return; + } + + + + // write the header info for an entry + internal void WriteHeader(Stream s, int cycle) + { + // Must remember the offset, within the output stream, of this particular + // entry header. + // + // This is for 2 reasons: + // + // 1. so we can determine the RelativeOffsetOfLocalHeader (ROLH) for + // use in the central directory. + // 2. so we can seek backward in case there is an error opening or reading + // the file, and the application decides to skip the file. In this case, + // we need to seek backward in the output stream to allow the next entry + // to be added to the zipfile output stream. + // + // Normally you would just store the offset before writing to the output + // stream and be done with it. But the possibility to use split archives + // makes this approach ineffective. In split archives, each file or segment + // is bound to a max size limit, and each local file header must not span a + // segment boundary; it must be written contiguously. If it will fit in the + // current segment, then the ROLH is just the current Position in the output + // stream. If it won't fit, then we need a new file (segment) and the ROLH + // is zero. + // + // But we only can know if it is possible to write a header contiguously + // after we know the size of the local header, a size that varies with + // things like filename length, comments, and extra fields. We have to + // compute the header fully before knowing whether it will fit. + // + // That takes care of item #1 above. Now, regarding #2. If an error occurs + // while computing the local header, we want to just seek backward. The + // exception handling logic (in the caller of WriteHeader) uses ROLH to + // scroll back. + // + // All this means we have to preserve the starting offset before computing + // the header, and also we have to compute the offset later, to handle the + // case of split archives. + + var counter = s as CountingStream; + + // workitem 8098: ok (output) + // This may change later, for split archives + + // Don't set _RelativeOffsetOfLocalHeader. Instead, set a temp variable. + // This allows for re-streaming, where a zip entry might be read from a + // zip archive (and maybe decrypted, and maybe decompressed) and then + // written to another zip archive, with different settings for + // compression method, compression level, or encryption algorithm. + _future_ROLH = (counter != null) + ? counter.ComputedPosition + : s.Position; + + int j = 0, i = 0; + + byte[] block = new byte[30]; + + // signature + block[i++] = (byte)(ZipConstants.ZipEntrySignature & 0x000000FF); + block[i++] = (byte)((ZipConstants.ZipEntrySignature & 0x0000FF00) >> 8); + block[i++] = (byte)((ZipConstants.ZipEntrySignature & 0x00FF0000) >> 16); + block[i++] = (byte)((ZipConstants.ZipEntrySignature & 0xFF000000) >> 24); + + // Design notes for ZIP64: + // + // The specification says that the header must include the Compressed + // and Uncompressed sizes, as well as the CRC32 value. When creating + // a zip via streamed processing, these quantities are not known until + // after the compression is done. Thus, a typical way to do it is to + // insert zeroes for these quantities, then do the compression, then + // seek back to insert the appropriate values, then seek forward to + // the end of the file data. + // + // There is also the option of using bit 3 in the GP bitfield - to + // specify that there is a data descriptor after the file data + // containing these three quantities. + // + // This works when the size of the quantities is known, either 32-bits + // or 64 bits as with the ZIP64 extensions. + // + // With Zip64, the 4-byte fields are set to 0xffffffff, and there is a + // corresponding data block in the "extra field" that contains the + // actual Compressed, uncompressed sizes. (As well as an additional + // field, the "Relative Offset of Local Header") + // + // The problem is when the app desires to use ZIP64 extensions + // optionally, only when necessary. Suppose the library assumes no + // zip64 extensions when writing the header, then after compression + // finds that the size of the data requires zip64. At this point, the + // header, already written to the file, won't have the necessary data + // block in the "extra field". The size of the entry header is fixed, + // so it is not possible to just "add on" the zip64 data block after + // compressing the file. On the other hand, always using zip64 will + // break interoperability with many other systems and apps. + // + // The approach we take is to insert a 32-byte dummy data block in the + // extra field, whenever zip64 is to be used "as necessary". This data + // block will get the actual zip64 HeaderId and zip64 metadata if + // necessary. If not necessary, the data block will get a meaningless + // HeaderId (0x1111), and will be filled with zeroes. + // + // When zip64 is actually in use, we also need to set the + // VersionNeededToExtract field to 45. + // + // There is one additional wrinkle: using zip64 as necessary conflicts + // with output to non-seekable devices. The header is emitted and + // must indicate whether zip64 is in use, before we know if zip64 is + // necessary. Because there is no seeking, the header can never be + // changed. Therefore, on non-seekable devices, + // Zip64Option.AsNecessary is the same as Zip64Option.Always. + // + + + // version needed- see AppNote.txt. + // + // need v5.1 for PKZIP strong encryption, or v2.0 for no encryption or + // for PK encryption, 4.5 for zip64. We may reset this later, as + // necessary or zip64. + + _presumeZip64 = (_container.Zip64 == Zip64Option.Always || + (_container.Zip64 == Zip64Option.AsNecessary && !s.CanSeek)); + Int16 VersionNeededToExtract = (Int16)(_presumeZip64 ? 45 : 20); +#if BZIP + if (this.CompressionMethod == Ionic.Zip.CompressionMethod.BZip2) + VersionNeededToExtract = 46; +#endif + + // (i==4) + block[i++] = (byte)(VersionNeededToExtract & 0x00FF); + block[i++] = (byte)((VersionNeededToExtract & 0xFF00) >> 8); + + // Get byte array. Side effect: sets ActualEncoding. + // Must determine encoding before setting the bitfield. + // workitem 6513 + byte[] fileNameBytes = GetEncodedFileNameBytes(); + Int16 filenameLength = (Int16)fileNameBytes.Length; + + // general purpose bitfield + // In the current implementation, this library uses only these bits + // in the GP bitfield: + // bit 0 = if set, indicates the entry is encrypted + // bit 3 = if set, indicates the CRC, C and UC sizes follow the file data. + // bit 6 = strong encryption - for pkware's meaning of strong encryption + // bit 11 = UTF-8 encoding is used in the comment and filename + + + // Here we set or unset the encryption bit. + // _BitField may already be set, as with a ZipEntry added into ZipOutputStream, which + // has bit 3 always set. We only want to set one bit + if (_Encryption == EncryptionAlgorithm.None) + _BitField &= ~1; // encryption bit OFF + else + _BitField |= 1; // encryption bit ON + + + // workitem 7941: WinZip does not the "strong encryption" bit when using AES. + // This "Strong Encryption" is a PKWare Strong encryption thing. + // _BitField |= 0x0020; + + // set the UTF8 bit if necessary +#if SILVERLIGHT + if (_actualEncoding.WebName == "utf-8") +#else + if (_actualEncoding.CodePage == System.Text.Encoding.UTF8.CodePage) +#endif + _BitField |= 0x0800; + + // The PKZIP spec says that if bit 3 is set (0x0008) in the General + // Purpose BitField, then the CRC, Compressed size, and uncompressed + // size are written directly after the file data. + // + // These 3 quantities are normally present in the regular zip entry + // header. But, they are not knowable until after the compression is + // done. So, in the normal case, we + // + // - write the header, using zeros for these quantities + // - compress the data, and incidentally compute these quantities. + // - seek back and write the correct values them into the header. + // + // This is nice because, while it is more complicated to write the zip + // file, it is simpler and less error prone to read the zip file, and + // as a result more applications can read zip files produced this way, + // with those 3 quantities in the header. + // + // But if seeking in the output stream is not possible, then we need + // to set the appropriate bitfield and emit these quantities after the + // compressed file data in the output. + // + // workitem 7216 - having trouble formatting a zip64 file that is + // readable by WinZip. not sure why! What I found is that setting + // bit 3 and following all the implications, the zip64 file is + // readable by WinZip 12. and Perl's IO::Compress::Zip . Perl takes + // an interesting approach - it always sets bit 3 if ZIP64 in use. + // DotNetZip now does the same; this gives better compatibility with + // WinZip 12. + + if (IsDirectory || cycle == 99) + { + // (cycle == 99) indicates a zero-length entry written by ZipOutputStream + + _BitField &= ~0x0008; // unset bit 3 - no "data descriptor" - ever + _BitField &= ~0x0001; // unset bit 1 - no encryption - ever + Encryption = EncryptionAlgorithm.None; + Password = null; + } + else if (!s.CanSeek) + _BitField |= 0x0008; + +#if DONT_GO_THERE + else if (this.Encryption == EncryptionAlgorithm.PkzipWeak && + this._Source != ZipEntrySource.ZipFile) + { + // Set bit 3 to avoid the double-read perf issue. + // + // When PKZIP encryption is used, byte 11 of the encryption header is + // used as a consistency check. It is normally set to the MSByte of the + // CRC. But this means the cRC must be known ebfore compression and + // encryption, which means the entire stream has to be read twice. To + // avoid that, the high-byte of the time blob (when in DOS format) can + // be used for the consistency check (byte 11 in the encryption header). + // But this means the entry must have bit 3 set. + // + // Previously I used a more complex arrangement - using the methods like + // FigureCrc32(), PrepOutputStream() and others, in order to manage the + // seek-back in the source stream. Why? Because bit 3 is not always + // friendly with third-party zip tools, like those on the Mac. + // + // This is why this code is still ifdef'd out. + // + // Might consider making this yet another programmable option - + // AlwaysUseBit3ForPkzip. But that's for another day. + // + _BitField |= 0x0008; + } +#endif + + // (i==6) + block[i++] = (byte)(_BitField & 0x00FF); + block[i++] = (byte)((_BitField & 0xFF00) >> 8); + + // Here, we want to set values for Compressed Size, Uncompressed Size, + // and CRC. If we have __FileDataPosition as not -1 (zero is a valid + // FDP), then that means we are reading this zip entry from a zip + // file, and we have good values for those quantities. + // + // If _FileDataPosition is -1, then we are constructing this Entry + // from nothing. We zero those quantities now, and we will compute + // actual values for the three quantities later, when we do the + // compression, and then seek back to write them into the appropriate + // place in the header. + if (this.__FileDataPosition == -1) + { + //_UncompressedSize = 0; // do not unset - may need this value for restream + // _Crc32 = 0; // ditto + _CompressedSize = 0; + _crcCalculated = false; + } + + // set compression method here + MaybeUnsetCompressionMethodForWriting(cycle); + + // (i==8) compression method + block[i++] = (byte)(_CompressionMethod & 0x00FF); + block[i++] = (byte)((_CompressionMethod & 0xFF00) >> 8); + + if (cycle == 99) + { + // (cycle == 99) indicates a zero-length entry written by ZipOutputStream + SetZip64Flags(); + } + +#if AESCRYPTO + else if (Encryption == EncryptionAlgorithm.WinZipAes128 || Encryption == EncryptionAlgorithm.WinZipAes256) + { + i -= 2; + block[i++] = 0x63; + block[i++] = 0; + } +#endif + + // LastMod + _TimeBlob = Ionic.Zip.SharedUtilities.DateTimeToPacked(LastModified); + + // (i==10) time blob + block[i++] = (byte)(_TimeBlob & 0x000000FF); + block[i++] = (byte)((_TimeBlob & 0x0000FF00) >> 8); + block[i++] = (byte)((_TimeBlob & 0x00FF0000) >> 16); + block[i++] = (byte)((_TimeBlob & 0xFF000000) >> 24); + + // (i==14) CRC - if source==filesystem, this is zero now, actual value + // will be calculated later. if source==archive, this is a bonafide + // value. + block[i++] = (byte)(_Crc32 & 0x000000FF); + block[i++] = (byte)((_Crc32 & 0x0000FF00) >> 8); + block[i++] = (byte)((_Crc32 & 0x00FF0000) >> 16); + block[i++] = (byte)((_Crc32 & 0xFF000000) >> 24); + + if (_presumeZip64) + { + // (i==18) CompressedSize (Int32) and UncompressedSize - all 0xFF for now + for (j = 0; j < 8; j++) + block[i++] = 0xFF; + } + else + { + // (i==18) CompressedSize (Int32) - this value may or may not be + // bonafide. if source == filesystem, then it is zero, and we'll + // learn it after we compress. if source == archive, then it is + // bonafide data. + block[i++] = (byte)(_CompressedSize & 0x000000FF); + block[i++] = (byte)((_CompressedSize & 0x0000FF00) >> 8); + block[i++] = (byte)((_CompressedSize & 0x00FF0000) >> 16); + block[i++] = (byte)((_CompressedSize & 0xFF000000) >> 24); + + // (i==22) UncompressedSize (Int32) - this value may or may not be + // bonafide. + block[i++] = (byte)(_UncompressedSize & 0x000000FF); + block[i++] = (byte)((_UncompressedSize & 0x0000FF00) >> 8); + block[i++] = (byte)((_UncompressedSize & 0x00FF0000) >> 16); + block[i++] = (byte)((_UncompressedSize & 0xFF000000) >> 24); + } + + // (i==26) filename length (Int16) + block[i++] = (byte)(filenameLength & 0x00FF); + block[i++] = (byte)((filenameLength & 0xFF00) >> 8); + + _Extra = ConstructExtraField(false); + + // (i==28) extra field length (short) + Int16 extraFieldLength = (Int16)((_Extra == null) ? 0 : _Extra.Length); + block[i++] = (byte)(extraFieldLength & 0x00FF); + block[i++] = (byte)((extraFieldLength & 0xFF00) >> 8); + + // workitem 13542 + byte[] bytes = new byte[i + filenameLength + extraFieldLength]; + + // get the fixed portion + Buffer.BlockCopy(block, 0, bytes, 0, i); + //for (j = 0; j < i; j++) bytes[j] = block[j]; + + // The filename written to the archive. + Buffer.BlockCopy(fileNameBytes, 0, bytes, i, fileNameBytes.Length); + // for (j = 0; j < fileNameBytes.Length; j++) + // bytes[i + j] = fileNameBytes[j]; + + i += fileNameBytes.Length; + + // "Extra field" + if (_Extra != null) + { + Buffer.BlockCopy(_Extra, 0, bytes, i, _Extra.Length); + // for (j = 0; j < _Extra.Length; j++) + // bytes[i + j] = _Extra[j]; + i += _Extra.Length; + } + + _LengthOfHeader = i; + + // handle split archives + var zss = s as ZipSegmentedStream; + if (zss != null) + { + zss.ContiguousWrite = true; + UInt32 requiredSegment = zss.ComputeSegment(i); + if (requiredSegment != zss.CurrentSegment) + _future_ROLH = 0; // rollover! + else + _future_ROLH = zss.Position; + + _diskNumber = requiredSegment; + } + + // validate the ZIP64 usage + if (_container.Zip64 == Zip64Option.Never && (uint)_RelativeOffsetOfLocalHeader >= 0xFFFFFFFF) + throw new ZipException("Offset within the zip archive exceeds 0xFFFFFFFF. Consider setting the UseZip64WhenSaving property on the ZipFile instance."); + + + // finally, write the header to the stream + s.Write(bytes, 0, i); + + // now that the header is written, we can turn off the contiguous write restriction. + if (zss != null) + zss.ContiguousWrite = false; + + // Preserve this header data, we'll use it again later. + // ..when seeking backward, to write again, after we have the Crc, compressed + // and uncompressed sizes. + // ..and when writing the central directory structure. + _EntryHeader = bytes; + } + + + + + private Int32 FigureCrc32() + { + if (_crcCalculated == false) + { + Stream input = null; + // get the original stream: + if (this._Source == ZipEntrySource.WriteDelegate) + { + var output = new Ionic.Crc.CrcCalculatorStream(Stream.Null); + // allow the application to write the data + this._WriteDelegate(this.FileName, output); + _Crc32 = output.Crc; + } + else if (this._Source == ZipEntrySource.ZipFile) + { + // nothing to do - the CRC is already set + } + else + { + if (this._Source == ZipEntrySource.Stream) + { + PrepSourceStream(); + input = this._sourceStream; + } + else if (this._Source == ZipEntrySource.JitStream) + { + // allow the application to open the stream + if (this._sourceStream == null) + _sourceStream = this._OpenDelegate(this.FileName); + PrepSourceStream(); + input = this._sourceStream; + } + else if (this._Source == ZipEntrySource.ZipOutputStream) + { + } + else + { + //input = File.OpenRead(LocalFileName); + input = File.Open(LocalFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + } + + var crc32 = new Ionic.Crc.CRC32(); + _Crc32 = crc32.GetCrc32(input); + + if (_sourceStream == null) + { +#if NETCF + input.Close(); +#else + input.Dispose(); +#endif + } + } + _crcCalculated = true; + } + return _Crc32; + } + + + /// + /// Stores the position of the entry source stream, or, if the position is + /// already stored, seeks to that position. + /// + /// + /// + /// + /// This method is called in prep for reading the source stream. If PKZIP + /// encryption is used, then we need to calc the CRC32 before doing the + /// encryption, because the CRC is used in the 12th byte of the PKZIP + /// encryption header. So, we need to be able to seek backward in the source + /// when saving the ZipEntry. This method is called from the place that + /// calculates the CRC, and also from the method that does the encryption of + /// the file data. + /// + /// + /// + /// The first time through, this method sets the _sourceStreamOriginalPosition + /// field. Subsequent calls to this method seek to that position. + /// + /// + private void PrepSourceStream() + { + if (_sourceStream == null) + throw new ZipException(String.Format("The input stream is null for entry '{0}'.", FileName)); + + if (this._sourceStreamOriginalPosition != null) + { + // this will happen the 2nd cycle through, if the stream is seekable + this._sourceStream.Position = this._sourceStreamOriginalPosition.Value; + } + else if (this._sourceStream.CanSeek) + { + // this will happen the first cycle through, if seekable + this._sourceStreamOriginalPosition = new Nullable(this._sourceStream.Position); + } + else if (this.Encryption == EncryptionAlgorithm.PkzipWeak) + { + // In general, using PKZIP encryption on a a zip entry whose input + // comes from a non-seekable stream, is tricky. Here's why: + // + // Byte 11 of the PKZIP encryption header is used for password + // validation and consistency checknig. + // + // Normally, the highest byte of the CRC is used as the 11th (last) byte + // in the PKZIP encryption header. This means the CRC must be known + // before encryption is performed. Normally that means we read the full + // data stream, compute the CRC, then seek back and read it again for + // the compression+encryption phase. Obviously this is bad for + // performance with a large input file. + // + // There's a twist in the ZIP spec (actually documented only in infozip + // code, not in the spec itself) that allows the high-order byte of the + // last modified time for the entry, when the lastmod time is in packed + // (DOS) format, to be used for Byte 11 in the encryption header. In + // this case, the bit 3 "data descriptor" must be used. + // + // An intelligent implementation would therefore force the use of the + // bit 3 data descriptor when PKZIP encryption is in use, regardless. + // This avoids the double-read of the stream to be encrypted. So far, + // DotNetZip doesn't do that; it just punts when the input stream is + // non-seekable, and the output does not use Bit 3. + // + // The other option is to use the CRC when it is already available, eg, + // when the source for the data is a ZipEntry (when the zip file is + // being updated). In this case we already know the CRC and can just use + // what we know. + + if (this._Source != ZipEntrySource.ZipFile && ((this._BitField & 0x0008) != 0x0008)) + throw new ZipException("It is not possible to use PKZIP encryption on a non-seekable input stream"); + } + } + + + /// + /// Copy metadata that may have been changed by the app. We do this when + /// resetting the zipFile instance. If the app calls Save() on a ZipFile, then + /// tries to party on that file some more, we may need to Reset() it , which + /// means re-reading the entries and then copying the metadata. I think. + /// + internal void CopyMetaData(ZipEntry source) + { + this.__FileDataPosition = source.__FileDataPosition; + this.CompressionMethod = source.CompressionMethod; + this._CompressionMethod_FromZipFile = source._CompressionMethod_FromZipFile; + this._CompressedFileDataSize = source._CompressedFileDataSize; + this._UncompressedSize = source._UncompressedSize; + this._BitField = source._BitField; + this._Source = source._Source; + this._LastModified = source._LastModified; + this._Mtime = source._Mtime; + this._Atime = source._Atime; + this._Ctime = source._Ctime; + this._ntfsTimesAreSet = source._ntfsTimesAreSet; + this._emitUnixTimes = source._emitUnixTimes; + this._emitNtfsTimes = source._emitNtfsTimes; + } + + + private void OnWriteBlock(Int64 bytesXferred, Int64 totalBytesToXfer) + { + if (_container.ZipFile != null) + _ioOperationCanceled = _container.ZipFile.OnSaveBlock(this, bytesXferred, totalBytesToXfer); + } + + + + private void _WriteEntryData(Stream s) + { + // Read in the data from the input stream (often a file in the filesystem), + // and write it to the output stream, calculating a CRC on it as we go. + // We will also compress and encrypt as necessary. + + Stream input = null; + long fdp = -1L; + try + { + // Want to record the position in the zip file of the zip entry + // data (as opposed to the metadata). s.Position may fail on some + // write-only streams, eg stdout or System.Web.HttpResponseStream. + // We swallow that exception, because we don't care, in that case. + // But, don't set __FileDataPosition directly. It may be needed + // to READ the zip entry from the zip file, if this is a + // "re-stream" situation. In other words if the zip entry has + // changed compression level, or compression method, or (maybe?) + // encryption algorithm. In that case if the original entry is + // encrypted, we need __FileDataPosition to be the value for the + // input zip file. This s.Position is for the output zipfile. So + // we copy fdp to __FileDataPosition after this entry has been + // (maybe) restreamed. + fdp = s.Position; + } + catch (Exception) { } + + try + { + // Use fileLength for progress updates, and to decide whether we can + // skip encryption and compression altogether (in case of length==zero) + long fileLength = SetInputAndFigureFileLength(ref input); + + // Wrap a counting stream around the raw output stream: + // This is the last thing that happens before the bits go to the + // application-provided stream. + // + // Sometimes s is a CountingStream. Doesn't matter. Wrap it with a + // counter anyway. We need to count at both levels. + + CountingStream entryCounter = new CountingStream(s); + + Stream encryptor; + Stream compressor; + + if (fileLength != 0L) + { + // Maybe wrap an encrypting stream around the counter: This will + // happen BEFORE output counting, and AFTER compression, if encryption + // is used. + encryptor = MaybeApplyEncryption(entryCounter); + + // Maybe wrap a compressing Stream around that. + // This will happen BEFORE encryption (if any) as we write data out. + compressor = MaybeApplyCompression(encryptor, fileLength); + } + else + { + encryptor = compressor = entryCounter; + } + + // Wrap a CrcCalculatorStream around that. + // This will happen BEFORE compression (if any) as we write data out. + var output = new Ionic.Crc.CrcCalculatorStream(compressor, true); + + // output.Write() causes this flow: + // calc-crc -> compress -> encrypt -> count -> actually write + + if (this._Source == ZipEntrySource.WriteDelegate) + { + // allow the application to write the data + this._WriteDelegate(this.FileName, output); + } + else + { + // synchronously copy the input stream to the output stream-chain + byte[] buffer = new byte[BufferSize]; + int n; + while ((n = SharedUtilities.ReadWithRetry(input, buffer, 0, buffer.Length, FileName)) != 0) + { + output.Write(buffer, 0, n); + OnWriteBlock(output.TotalBytesSlurped, fileLength); + if (_ioOperationCanceled) + break; + } + } + + FinishOutputStream(s, entryCounter, encryptor, compressor, output); + } + finally + { + if (this._Source == ZipEntrySource.JitStream) + { + // allow the application to close the stream + if (this._CloseDelegate != null) + this._CloseDelegate(this.FileName, input); + } + else if ((input as FileStream) != null) + { +#if NETCF + input.Close(); +#else + input.Dispose(); +#endif + } + } + + if (_ioOperationCanceled) + return; + + // set FDP now, to allow for re-streaming + this.__FileDataPosition = fdp; + PostProcessOutput(s); + } + + + /// + /// Set the input stream and get its length, if possible. The length is + /// used for progress updates, AND, to allow an optimization in case of + /// a stream/file of zero length. In that case we skip the Encrypt and + /// compression Stream. (like DeflateStream or BZip2OutputStream) + /// + private long SetInputAndFigureFileLength(ref Stream input) + { + long fileLength = -1L; + // get the original stream: + if (this._Source == ZipEntrySource.Stream) + { + PrepSourceStream(); + input = this._sourceStream; + + // Try to get the length, no big deal if not available. + try { fileLength = this._sourceStream.Length; } + catch (NotSupportedException) { } + } + else if (this._Source == ZipEntrySource.ZipFile) + { + // we are "re-streaming" the zip entry. + string pwd = (_Encryption_FromZipFile == EncryptionAlgorithm.None) ? null : (this._Password ?? this._container.Password); + this._sourceStream = InternalOpenReader(pwd); + PrepSourceStream(); + input = this._sourceStream; + fileLength = this._sourceStream.Length; + } + else if (this._Source == ZipEntrySource.JitStream) + { + // allow the application to open the stream + if (this._sourceStream == null) _sourceStream = this._OpenDelegate(this.FileName); + PrepSourceStream(); + input = this._sourceStream; + try { fileLength = this._sourceStream.Length; } + catch (NotSupportedException) { } + } + else if (this._Source == ZipEntrySource.FileSystem) + { + // workitem 7145 + FileShare fs = FileShare.ReadWrite; +#if !NETCF + // FileShare.Delete is not defined for the Compact Framework + fs |= FileShare.Delete; +#endif + // workitem 8423 + input = File.Open(LocalFileName, FileMode.Open, FileAccess.Read, fs); + fileLength = input.Length; + } + + return fileLength; + } + + + + internal void FinishOutputStream(Stream s, + CountingStream entryCounter, + Stream encryptor, + Stream compressor, + Ionic.Crc.CrcCalculatorStream output) + { + if (output == null) return; + + output.Close(); + + // by calling Close() on the deflate stream, we write the footer bytes, as necessary. + if ((compressor as Ionic.Zlib.DeflateStream) != null) + compressor.Close(); +#if BZIP + else if ((compressor as Ionic.BZip2.BZip2OutputStream) != null) + compressor.Close(); +#if !NETCF + else if ((compressor as Ionic.BZip2.ParallelBZip2OutputStream) != null) + compressor.Close(); +#endif +#endif + +#if !NETCF + else if ((compressor as Ionic.Zlib.ParallelDeflateOutputStream) != null) + compressor.Close(); +#endif + + encryptor.Flush(); + encryptor.Close(); + + _LengthOfTrailer = 0; + + _UncompressedSize = output.TotalBytesSlurped; + +#if AESCRYPTO + WinZipAesCipherStream wzacs = encryptor as WinZipAesCipherStream; + if (wzacs != null && _UncompressedSize > 0) + { + s.Write(wzacs.FinalAuthentication, 0, 10); + _LengthOfTrailer += 10; + } +#endif + _CompressedFileDataSize = entryCounter.BytesWritten; + _CompressedSize = _CompressedFileDataSize; // may be adjusted + _Crc32 = output.Crc; + + // Set _RelativeOffsetOfLocalHeader now, to allow for re-streaming + StoreRelativeOffset(); + } + + + + + internal void PostProcessOutput(Stream s) + { + var s1 = s as CountingStream; + + // workitem 8931 - for WriteDelegate. + // The WriteDelegate changes things because there can be a zero-byte stream + // written. In all other cases DotNetZip knows the length of the stream + // before compressing and encrypting. In this case we have to circle back, + // and omit all the crypto stuff - the GP bitfield, and the crypto header. + if (_UncompressedSize == 0 && _CompressedSize == 0) + { + if (this._Source == ZipEntrySource.ZipOutputStream) return; // nothing to do... + + if (_Password != null) + { + int headerBytesToRetract = 0; + if (Encryption == EncryptionAlgorithm.PkzipWeak) + headerBytesToRetract = 12; +#if AESCRYPTO + else if (Encryption == EncryptionAlgorithm.WinZipAes128 || + Encryption == EncryptionAlgorithm.WinZipAes256) + { + headerBytesToRetract = _aesCrypto_forWrite._Salt.Length + _aesCrypto_forWrite.GeneratedPV.Length; + } +#endif + if (this._Source == ZipEntrySource.ZipOutputStream && !s.CanSeek) + throw new ZipException("Zero bytes written, encryption in use, and non-seekable output."); + + if (Encryption != EncryptionAlgorithm.None) + { + // seek back in the stream to un-output the security metadata + s.Seek(-1 * headerBytesToRetract, SeekOrigin.Current); + s.SetLength(s.Position); + // workitem 10178 + Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s); + + // workitem 11131 + // adjust the count on the CountingStream as necessary + if (s1 != null) s1.Adjust(headerBytesToRetract); + + // subtract the size of the security header from the _LengthOfHeader + _LengthOfHeader -= headerBytesToRetract; + __FileDataPosition -= headerBytesToRetract; + } + _Password = null; + + // turn off the encryption bit + _BitField &= ~(0x0001); + + // copy the updated bitfield value into the header + int j = 6; + _EntryHeader[j++] = (byte)(_BitField & 0x00FF); + _EntryHeader[j++] = (byte)((_BitField & 0xFF00) >> 8); + +#if AESCRYPTO + if (Encryption == EncryptionAlgorithm.WinZipAes128 || + Encryption == EncryptionAlgorithm.WinZipAes256) + { + // Fix the extra field - overwrite the 0x9901 headerId + // with dummy data. (arbitrarily, 0x9999) + Int16 fnLength = (short)(_EntryHeader[26] + _EntryHeader[27] * 256); + int offx = 30 + fnLength; + int aesIndex = FindExtraFieldSegment(_EntryHeader, offx, 0x9901); + if (aesIndex >= 0) + { + _EntryHeader[aesIndex++] = 0x99; + _EntryHeader[aesIndex++] = 0x99; + } + } +#endif + } + + CompressionMethod = 0; + Encryption = EncryptionAlgorithm.None; + } + else if (_zipCrypto_forWrite != null +#if AESCRYPTO + || _aesCrypto_forWrite != null +#endif + ) + + { + if (Encryption == EncryptionAlgorithm.PkzipWeak) + { + _CompressedSize += 12; // 12 extra bytes for the encryption header + } +#if AESCRYPTO + else if (Encryption == EncryptionAlgorithm.WinZipAes128 || + Encryption == EncryptionAlgorithm.WinZipAes256) + { + // adjust the compressed size to include the variable (salt+pv) + // security header and 10-byte trailer. According to the winzip AES + // spec, that metadata is included in the "Compressed Size" figure + // when encoding the zip archive. + _CompressedSize += _aesCrypto_forWrite.SizeOfEncryptionMetadata; + } +#endif + } + + int i = 8; + _EntryHeader[i++] = (byte)(_CompressionMethod & 0x00FF); + _EntryHeader[i++] = (byte)((_CompressionMethod & 0xFF00) >> 8); + + i = 14; + // CRC - the correct value now + _EntryHeader[i++] = (byte)(_Crc32 & 0x000000FF); + _EntryHeader[i++] = (byte)((_Crc32 & 0x0000FF00) >> 8); + _EntryHeader[i++] = (byte)((_Crc32 & 0x00FF0000) >> 16); + _EntryHeader[i++] = (byte)((_Crc32 & 0xFF000000) >> 24); + + SetZip64Flags(); + + // (i==26) filename length (Int16) + Int16 filenameLength = (short)(_EntryHeader[26] + _EntryHeader[27] * 256); + Int16 extraFieldLength = (short)(_EntryHeader[28] + _EntryHeader[29] * 256); + + if (_OutputUsesZip64.Value) + { + // VersionNeededToExtract - set to 45 to indicate zip64 + _EntryHeader[4] = (byte)(45 & 0x00FF); + _EntryHeader[5] = 0x00; + + // workitem 7924 - don't need bit 3 + // // workitem 7917 + // // set bit 3 for ZIP64 compatibility with WinZip12 + // _BitField |= 0x0008; + // _EntryHeader[6] = (byte)(_BitField & 0x00FF); + + // CompressedSize and UncompressedSize - 0xFF + for (int j = 0; j < 8; j++) + _EntryHeader[i++] = 0xff; + + // At this point we need to find the "Extra field" that follows the + // filename. We had already emitted it, but the data (uncomp, comp, + // ROLH) was not available at the time we did so. Here, we emit it + // again, with final values. + + i = 30 + filenameLength; + _EntryHeader[i++] = 0x01; // zip64 + _EntryHeader[i++] = 0x00; + + i += 2; // skip over data size, which is 16+4 + + Array.Copy(BitConverter.GetBytes(_UncompressedSize), 0, _EntryHeader, i, 8); + i += 8; + Array.Copy(BitConverter.GetBytes(_CompressedSize), 0, _EntryHeader, i, 8); + } + else + { + // VersionNeededToExtract - reset to 20 since no zip64 + _EntryHeader[4] = (byte)(20 & 0x00FF); + _EntryHeader[5] = 0x00; + + // CompressedSize - the correct value now + i = 18; + _EntryHeader[i++] = (byte)(_CompressedSize & 0x000000FF); + _EntryHeader[i++] = (byte)((_CompressedSize & 0x0000FF00) >> 8); + _EntryHeader[i++] = (byte)((_CompressedSize & 0x00FF0000) >> 16); + _EntryHeader[i++] = (byte)((_CompressedSize & 0xFF000000) >> 24); + + // UncompressedSize - the correct value now + _EntryHeader[i++] = (byte)(_UncompressedSize & 0x000000FF); + _EntryHeader[i++] = (byte)((_UncompressedSize & 0x0000FF00) >> 8); + _EntryHeader[i++] = (byte)((_UncompressedSize & 0x00FF0000) >> 16); + _EntryHeader[i++] = (byte)((_UncompressedSize & 0xFF000000) >> 24); + + // The HeaderId in the extra field header, is already dummied out. + if (extraFieldLength != 0) + { + i = 30 + filenameLength; + // For zip archives written by this library, if the zip64 + // header exists, it is the first header. Because of the logic + // used when first writing the _EntryHeader bytes, the + // HeaderId is not guaranteed to be any particular value. So + // we determine if the first header is a putative zip64 header + // by examining the datasize. UInt16 HeaderId = + // (UInt16)(_EntryHeader[i] + _EntryHeader[i + 1] * 256); + Int16 DataSize = (short)(_EntryHeader[i + 2] + _EntryHeader[i + 3] * 256); + if (DataSize == 16) + { + // reset to Header Id to dummy value, effectively dummy-ing out the zip64 metadata + _EntryHeader[i++] = 0x99; + _EntryHeader[i++] = 0x99; + } + } + } + + +#if AESCRYPTO + + if (Encryption == EncryptionAlgorithm.WinZipAes128 || + Encryption == EncryptionAlgorithm.WinZipAes256) + { + // Must set compressionmethod to 0x0063 (decimal 99) + // + // and then set the compression method bytes inside the extra + // field to the actual compression method value. + + i = 8; + _EntryHeader[i++] = 0x63; + _EntryHeader[i++] = 0; + + i = 30 + filenameLength; + do + { + UInt16 HeaderId = (UInt16)(_EntryHeader[i] + _EntryHeader[i + 1] * 256); + Int16 DataSize = (short)(_EntryHeader[i + 2] + _EntryHeader[i + 3] * 256); + if (HeaderId != 0x9901) + { + // skip this header + i += DataSize + 4; + } + else + { + i += 9; + // actual compression method + _EntryHeader[i++] = (byte)(_CompressionMethod & 0x00FF); + _EntryHeader[i++] = (byte)(_CompressionMethod & 0xFF00); + } + } while (i < (extraFieldLength - 30 - filenameLength)); + } +#endif + + // finally, write the data. + + // workitem 7216 - sometimes we don't seek even if we CAN. ASP.NET + // Response.OutputStream, or stdout are non-seekable. But we may also want + // to NOT seek in other cases, eg zip64. For all cases, we just check bit 3 + // to see if we want to seek. There's one exception - if using a + // ZipOutputStream, and PKZip encryption is in use, then we set bit 3 even + // if the out is seekable. This is so the check on the last byte of the + // PKZip Encryption Header can be done on the current time, as opposed to + // the CRC, to prevent streaming the file twice. So, test for + // ZipOutputStream and seekable, and if so, seek back, even if bit 3 is set. + + if ((_BitField & 0x0008) != 0x0008 || + (this._Source == ZipEntrySource.ZipOutputStream && s.CanSeek)) + { + // seek back and rewrite the entry header + var zss = s as ZipSegmentedStream; + if (zss != null && _diskNumber != zss.CurrentSegment) + { + // In this case the entry header is in a different file, + // which has already been closed. Need to re-open it. + using (Stream hseg = ZipSegmentedStream.ForUpdate(this._container.ZipFile.Name, _diskNumber)) + { + hseg.Seek(this._RelativeOffsetOfLocalHeader, SeekOrigin.Begin); + hseg.Write(_EntryHeader, 0, _EntryHeader.Length); + } + } + else + { + // seek in the raw output stream, to the beginning of the header for + // this entry. + // workitem 8098: ok (output) + s.Seek(this._RelativeOffsetOfLocalHeader, SeekOrigin.Begin); + + // write the updated header to the output stream + s.Write(_EntryHeader, 0, _EntryHeader.Length); + + // adjust the count on the CountingStream as necessary + if (s1 != null) s1.Adjust(_EntryHeader.Length); + + // seek in the raw output stream, to the end of the file data + // for this entry + s.Seek(_CompressedSize, SeekOrigin.Current); + } + } + + // emit the descriptor - only if not a directory. + if (((_BitField & 0x0008) == 0x0008) && !IsDirectory) + { + byte[] Descriptor = new byte[16 + (_OutputUsesZip64.Value ? 8 : 0)]; + i = 0; + + // signature + Array.Copy(BitConverter.GetBytes(ZipConstants.ZipEntryDataDescriptorSignature), 0, Descriptor, i, 4); + i += 4; + + // CRC - the correct value now + Array.Copy(BitConverter.GetBytes(_Crc32), 0, Descriptor, i, 4); + i += 4; + + // workitem 7917 + if (_OutputUsesZip64.Value) + { + // CompressedSize - the correct value now + Array.Copy(BitConverter.GetBytes(_CompressedSize), 0, Descriptor, i, 8); + i += 8; + + // UncompressedSize - the correct value now + Array.Copy(BitConverter.GetBytes(_UncompressedSize), 0, Descriptor, i, 8); + i += 8; + } + else + { + // CompressedSize - (lower 32 bits) the correct value now + Descriptor[i++] = (byte)(_CompressedSize & 0x000000FF); + Descriptor[i++] = (byte)((_CompressedSize & 0x0000FF00) >> 8); + Descriptor[i++] = (byte)((_CompressedSize & 0x00FF0000) >> 16); + Descriptor[i++] = (byte)((_CompressedSize & 0xFF000000) >> 24); + + // UncompressedSize - (lower 32 bits) the correct value now + Descriptor[i++] = (byte)(_UncompressedSize & 0x000000FF); + Descriptor[i++] = (byte)((_UncompressedSize & 0x0000FF00) >> 8); + Descriptor[i++] = (byte)((_UncompressedSize & 0x00FF0000) >> 16); + Descriptor[i++] = (byte)((_UncompressedSize & 0xFF000000) >> 24); + } + + // finally, write the trailing descriptor to the output stream + s.Write(Descriptor, 0, Descriptor.Length); + + _LengthOfTrailer += Descriptor.Length; + } + } + + + + private void SetZip64Flags() + { + // zip64 housekeeping + _entryRequiresZip64 = new Nullable + (_CompressedSize >= 0xFFFFFFFF || _UncompressedSize >= 0xFFFFFFFF || _RelativeOffsetOfLocalHeader >= 0xFFFFFFFF); + + // validate the ZIP64 usage + if (_container.Zip64 == Zip64Option.Never && _entryRequiresZip64.Value) + throw new ZipException("Compressed or Uncompressed size, or offset exceeds the maximum value. Consider setting the UseZip64WhenSaving property on the ZipFile instance."); + + _OutputUsesZip64 = new Nullable(_container.Zip64 == Zip64Option.Always || _entryRequiresZip64.Value); + } + + + + /// + /// Prepare the given stream for output - wrap it in a CountingStream, and + /// then in a CRC stream, and an encryptor and deflator as appropriate. + /// + /// + /// + /// Previously this was used in ZipEntry.Write(), but in an effort to + /// introduce some efficiencies in that method I've refactored to put the + /// code inline. This method still gets called by ZipOutputStream. + /// + /// + internal void PrepOutputStream(Stream s, + long streamLength, + out CountingStream outputCounter, + out Stream encryptor, + out Stream compressor, + out Ionic.Crc.CrcCalculatorStream output) + { + TraceWriteLine("PrepOutputStream: e({0}) comp({1}) crypto({2}) zf({3})", + FileName, + CompressionLevel, + Encryption, + _container.Name); + + // Wrap a counting stream around the raw output stream: + // This is the last thing that happens before the bits go to the + // application-provided stream. + outputCounter = new CountingStream(s); + + // Sometimes the incoming "raw" output stream is already a CountingStream. + // Doesn't matter. Wrap it with a counter anyway. We need to count at both + // levels. + + if (streamLength != 0L) + { + // Maybe wrap an encrypting stream around that: + // This will happen BEFORE output counting, and AFTER deflation, if encryption + // is used. + encryptor = MaybeApplyEncryption(outputCounter); + + // Maybe wrap a compressing Stream around that. + // This will happen BEFORE encryption (if any) as we write data out. + compressor = MaybeApplyCompression(encryptor, streamLength); + } + else + { + encryptor = compressor = outputCounter; + } + // Wrap a CrcCalculatorStream around that. + // This will happen BEFORE compression (if any) as we write data out. + output = new Ionic.Crc.CrcCalculatorStream(compressor, true); + } + + + + private Stream MaybeApplyCompression(Stream s, long streamLength) + { + if (_CompressionMethod == 0x08 && CompressionLevel != Ionic.Zlib.CompressionLevel.None) + { +#if !NETCF + // ParallelDeflateThreshold == 0 means ALWAYS use parallel deflate + // ParallelDeflateThreshold == -1L means NEVER use parallel deflate + // Other values specify the actual threshold. + if (_container.ParallelDeflateThreshold == 0L || + (streamLength > _container.ParallelDeflateThreshold && + _container.ParallelDeflateThreshold > 0L)) + { + // This is sort of hacky. + // + // It's expensive to create a ParallelDeflateOutputStream, because + // of the large memory buffers. But the class is unlike most Stream + // classes in that it can be re-used, so the caller can compress + // multiple files with it, one file at a time. The key is to call + // Reset() on it, in between uses. + // + // The ParallelDeflateOutputStream is attached to the container + // itself - there is just one for the entire ZipFile or + // ZipOutputStream. So it gets created once, per save, and then + // re-used many times. + // + // This approach will break when we go to a "parallel save" + // approach, where multiple entries within the zip file are being + // compressed and saved at the same time. But for now it's ok. + // + + // instantiate the ParallelDeflateOutputStream + if (_container.ParallelDeflater == null) + { + _container.ParallelDeflater = + new Ionic.Zlib.ParallelDeflateOutputStream(s, + CompressionLevel, + _container.Strategy, + true); + // can set the codec buffer size only before the first call to Write(). + if (_container.CodecBufferSize > 0) + _container.ParallelDeflater.BufferSize = _container.CodecBufferSize; + if (_container.ParallelDeflateMaxBufferPairs > 0) + _container.ParallelDeflater.MaxBufferPairs = + _container.ParallelDeflateMaxBufferPairs; + } + // reset it with the new stream + Ionic.Zlib.ParallelDeflateOutputStream o1 = _container.ParallelDeflater; + o1.Reset(s); + return o1; + } +#endif + var o = new Ionic.Zlib.DeflateStream(s, Ionic.Zlib.CompressionMode.Compress, + CompressionLevel, + true); + if (_container.CodecBufferSize > 0) + o.BufferSize = _container.CodecBufferSize; + o.Strategy = _container.Strategy; + return o; + } + + +#if BZIP + if (_CompressionMethod == 0x0c) + { +#if !NETCF + if (_container.ParallelDeflateThreshold == 0L || + (streamLength > _container.ParallelDeflateThreshold && + _container.ParallelDeflateThreshold > 0L)) + { + + var o1 = new Ionic.BZip2.ParallelBZip2OutputStream(s, true); + return o1; + } +#endif + var o = new Ionic.BZip2.BZip2OutputStream(s, true); + return o; + } +#endif + + return s; + } + + + + private Stream MaybeApplyEncryption(Stream s) + { + if (Encryption == EncryptionAlgorithm.PkzipWeak) + { + TraceWriteLine("MaybeApplyEncryption: e({0}) PKZIP", FileName); + + return new ZipCipherStream(s, _zipCrypto_forWrite, CryptoMode.Encrypt); + } +#if AESCRYPTO + if (Encryption == EncryptionAlgorithm.WinZipAes128 || + Encryption == EncryptionAlgorithm.WinZipAes256) + { + TraceWriteLine("MaybeApplyEncryption: e({0}) AES", FileName); + + return new WinZipAesCipherStream(s, _aesCrypto_forWrite, CryptoMode.Encrypt); + } +#endif + TraceWriteLine("MaybeApplyEncryption: e({0}) None", FileName); + + return s; + } + + + + private void OnZipErrorWhileSaving(Exception e) + { + if (_container.ZipFile != null) + _ioOperationCanceled = _container.ZipFile.OnZipErrorSaving(this, e); + } + + + + internal void Write(Stream s) + { + var cs1 = s as CountingStream; + var zss1 = s as ZipSegmentedStream; + + bool done = false; + do + { + try + { + // When the app is updating a zip file, it may be possible to + // just copy data for a ZipEntry from the source zipfile to the + // destination, as a block, without decompressing and + // recompressing, etc. But, in some cases the app modifies the + // properties on a ZipEntry prior to calling Save(). A change to + // any of the metadata - the FileName, CompressioLeve and so on, + // means DotNetZip cannot simply copy through the existing + // ZipEntry data unchanged. + // + // There are two cases: + // + // 1. Changes to only metadata, which means the header and + // central directory must be changed. + // + // 2. Changes to the properties that affect the compressed + // stream, such as CompressionMethod, CompressionLevel, or + // EncryptionAlgorithm. In this case, DotNetZip must + // "re-stream" the data: the old entry data must be maybe + // decrypted, maybe decompressed, then maybe re-compressed + // and maybe re-encrypted. + // + // This test checks if the source for the entry data is a zip file, and + // if a restream is necessary. If NOT, then it just copies through + // one entry, potentially changing the metadata. + + if (_Source == ZipEntrySource.ZipFile && !_restreamRequiredOnSave) + { + CopyThroughOneEntry(s); + return; + } + + // Is the entry a directory? If so, the write is relatively simple. + if (IsDirectory) + { + WriteHeader(s, 1); + StoreRelativeOffset(); + _entryRequiresZip64 = new Nullable(_RelativeOffsetOfLocalHeader >= 0xFFFFFFFF); + _OutputUsesZip64 = new Nullable(_container.Zip64 == Zip64Option.Always || _entryRequiresZip64.Value); + // handle case for split archives + if (zss1 != null) + _diskNumber = zss1.CurrentSegment; + + return; + } + + // At this point, the source for this entry is not a directory, and + // not a previously created zip file, or the source for the entry IS + // a previously created zip but the settings whave changed in + // important ways and therefore we will need to process the + // bytestream (compute crc, maybe compress, maybe encrypt) in order + // to write the content into the new zip. + // + // We do this in potentially 2 passes: The first time we do it as + // requested, maybe with compression and maybe encryption. If that + // causes the bytestream to inflate in size, and if compression was + // on, then we turn off compression and do it again. + + + bool readAgain = true; + int nCycles = 0; + do + { + nCycles++; + + WriteHeader(s, nCycles); + + // write the encrypted header + WriteSecurityMetadata(s); + + // write the (potentially compressed, potentially encrypted) file data + _WriteEntryData(s); + + // track total entry size (including the trailing descriptor and MAC) + _TotalEntrySize = _LengthOfHeader + _CompressedFileDataSize + _LengthOfTrailer; + + // The file data has now been written to the stream, and + // the file pointer is positioned directly after file data. + + if (nCycles > 1) readAgain = false; + else if (!s.CanSeek) readAgain = false; + else readAgain = WantReadAgain(); + + if (readAgain) + { + // Seek back in the raw output stream, to the beginning of the file + // data for this entry. + + // handle case for split archives + if (zss1 != null) + { + // Console.WriteLine("***_diskNumber/first: {0}", _diskNumber); + // Console.WriteLine("***_diskNumber/current: {0}", zss.CurrentSegment); + zss1.TruncateBackward(_diskNumber, _RelativeOffsetOfLocalHeader); + } + else + // workitem 8098: ok (output). + s.Seek(_RelativeOffsetOfLocalHeader, SeekOrigin.Begin); + + // If the last entry expands, we read again; but here, we must + // truncate the stream to prevent garbage data after the + // end-of-central-directory. + + // workitem 8098: ok (output). + s.SetLength(s.Position); + + // Adjust the count on the CountingStream as necessary. + if (cs1 != null) cs1.Adjust(_TotalEntrySize); + } + } + while (readAgain); + _skippedDuringSave = false; + done = true; + } + catch (System.Exception exc1) + { + ZipErrorAction orig = this.ZipErrorAction; + int loop = 0; + do + { + if (ZipErrorAction == ZipErrorAction.Throw) + throw; + + if (ZipErrorAction == ZipErrorAction.Skip || + ZipErrorAction == ZipErrorAction.Retry) + { + // must reset file pointer here. + // workitem 13903 - seek back only when necessary + long p1 = (cs1 != null) + ? cs1.ComputedPosition + : s.Position; + long delta = p1 - _future_ROLH; + if (delta > 0) + { + s.Seek(delta, SeekOrigin.Current); // may throw + long p2 = s.Position; + s.SetLength(s.Position); // to prevent garbage if this is the last entry + if (cs1 != null) cs1.Adjust(p1 - p2); + } + if (ZipErrorAction == ZipErrorAction.Skip) + { + WriteStatus("Skipping file {0} (exception: {1})", LocalFileName, exc1.ToString()); + + _skippedDuringSave = true; + done = true; + } + else + this.ZipErrorAction = orig; + break; + } + + if (loop > 0) throw; + + if (ZipErrorAction == ZipErrorAction.InvokeErrorEvent) + { + OnZipErrorWhileSaving(exc1); + if (_ioOperationCanceled) + { + done = true; + break; + } + } + loop++; + } + while (true); + } + } + while (!done); + } + + + internal void StoreRelativeOffset() + { + _RelativeOffsetOfLocalHeader = _future_ROLH; + } + + + + internal void NotifySaveComplete() + { + // When updating a zip file, there are two contexts for properties + // like Encryption or CompressionMethod - the values read from the + // original zip file, and the values used in the updated zip file. + // The _FromZipFile versions are the originals. At the end of a save, + // these values are the same. So we need to update them. This takes + // care of the boundary case where a single zipfile instance can be + // saved multiple times, with distinct changes to the properties on + // the entries, in between each Save(). + _Encryption_FromZipFile = _Encryption; + _CompressionMethod_FromZipFile = _CompressionMethod; + _restreamRequiredOnSave = false; + _metadataChanged = false; + //_Source = ZipEntrySource.None; + _Source = ZipEntrySource.ZipFile; // workitem 10694 + } + + + internal void WriteSecurityMetadata(Stream outstream) + { + if (Encryption == EncryptionAlgorithm.None) + return; + + string pwd = this._Password; + + // special handling for source == ZipFile. + // Want to support the case where we re-stream an encrypted entry. This will involve, + // at runtime, reading, decrypting, and decompressing from the original zip file, then + // compressing, encrypting, and writing to the output zip file. + + // If that's what we're doing, and the password hasn't been set on the entry, + // we use the container (ZipFile/ZipOutputStream) password to decrypt. + // This test here says to use the container password to re-encrypt, as well, + // with that password, if the entry password is null. + + if (this._Source == ZipEntrySource.ZipFile && pwd == null) + pwd = this._container.Password; + + if (pwd == null) + { + _zipCrypto_forWrite = null; +#if AESCRYPTO + _aesCrypto_forWrite = null; +#endif + return; + } + + TraceWriteLine("WriteSecurityMetadata: e({0}) crypto({1}) pw({2})", + FileName, Encryption.ToString(), pwd); + + if (Encryption == EncryptionAlgorithm.PkzipWeak) + { + // If PKZip (weak) encryption is in use, then the encrypted entry data + // is preceded by 12-byte "encryption header" for the entry. + + _zipCrypto_forWrite = ZipCrypto.ForWrite(pwd); + + // generate the random 12-byte header: + var rnd = new System.Random(); + byte[] encryptionHeader = new byte[12]; + rnd.NextBytes(encryptionHeader); + + // workitem 8271 + if ((this._BitField & 0x0008) == 0x0008) + { + // In the case that bit 3 of the general purpose bit flag is set to + // indicate the presence of a 'data descriptor' (signature + // 0x08074b50), the last byte of the decrypted header is sometimes + // compared with the high-order byte of the lastmodified time, + // rather than the high-order byte of the CRC, to verify the + // password. + // + // This is not documented in the PKWare Appnote.txt. + // This was discovered this by analysis of the Crypt.c source file in the + // InfoZip library + // http://www.info-zip.org/pub/infozip/ + + // Also, winzip insists on this! + _TimeBlob = Ionic.Zip.SharedUtilities.DateTimeToPacked(LastModified); + encryptionHeader[11] = (byte)((this._TimeBlob >> 8) & 0xff); + } + else + { + // When bit 3 is not set, the CRC value is required before + // encryption of the file data begins. In this case there is no way + // around it: must read the stream in its entirety to compute the + // actual CRC before proceeding. + FigureCrc32(); + encryptionHeader[11] = (byte)((this._Crc32 >> 24) & 0xff); + } + + // Encrypt the random header, INCLUDING the final byte which is either + // the high-order byte of the CRC32, or the high-order byte of the + // _TimeBlob. Must do this BEFORE encrypting the file data. This + // step changes the state of the cipher, or in the words of the PKZIP + // spec, it "further initializes" the cipher keys. + + byte[] cipherText = _zipCrypto_forWrite.EncryptMessage(encryptionHeader, encryptionHeader.Length); + + // Write the ciphered bonafide encryption header. + outstream.Write(cipherText, 0, cipherText.Length); + _LengthOfHeader += cipherText.Length; // 12 bytes + } + +#if AESCRYPTO + else if (Encryption == EncryptionAlgorithm.WinZipAes128 || + Encryption == EncryptionAlgorithm.WinZipAes256) + { + // If WinZip AES encryption is in use, then the encrypted entry data is + // preceded by a variable-sized Salt and a 2-byte "password + // verification" value for the entry. + + int keystrength = GetKeyStrengthInBits(Encryption); + _aesCrypto_forWrite = WinZipAesCrypto.Generate(pwd, keystrength); + outstream.Write(_aesCrypto_forWrite.Salt, 0, _aesCrypto_forWrite._Salt.Length); + outstream.Write(_aesCrypto_forWrite.GeneratedPV, 0, _aesCrypto_forWrite.GeneratedPV.Length); + _LengthOfHeader += _aesCrypto_forWrite._Salt.Length + _aesCrypto_forWrite.GeneratedPV.Length; + + TraceWriteLine("WriteSecurityMetadata: AES e({0}) keybits({1}) _LOH({2})", + FileName, keystrength, _LengthOfHeader); + + } +#endif + + } + + + + private void CopyThroughOneEntry(Stream outStream) + { + // Just read the entry from the existing input zipfile and write to the output. + // But, if metadata has changed (like file times or attributes), or if the ZIP64 + // option has changed, we can re-stream the entry data but must recompute the + // metadata. + if (this.LengthOfHeader == 0) + throw new BadStateException("Bad header length."); + + // is it necessary to re-constitute new metadata for this entry? + bool needRecompute = _metadataChanged || + (this.ArchiveStream is ZipSegmentedStream) || + (outStream is ZipSegmentedStream) || + (_InputUsesZip64 && _container.UseZip64WhenSaving == Zip64Option.Never) || + (!_InputUsesZip64 && _container.UseZip64WhenSaving == Zip64Option.Always); + + if (needRecompute) + CopyThroughWithRecompute(outStream); + else + CopyThroughWithNoChange(outStream); + + // zip64 housekeeping + _entryRequiresZip64 = new Nullable + (_CompressedSize >= 0xFFFFFFFF || _UncompressedSize >= 0xFFFFFFFF || + _RelativeOffsetOfLocalHeader >= 0xFFFFFFFF + ); + + _OutputUsesZip64 = new Nullable(_container.Zip64 == Zip64Option.Always || _entryRequiresZip64.Value); + } + + + + private void CopyThroughWithRecompute(Stream outstream) + { + int n; + byte[] bytes = new byte[BufferSize]; + var input = new CountingStream(this.ArchiveStream); + + long origRelativeOffsetOfHeader = _RelativeOffsetOfLocalHeader; + + // The header length may change due to rename of file, add a comment, etc. + // We need to retain the original. + int origLengthOfHeader = LengthOfHeader; // including crypto bytes! + + // WriteHeader() has the side effect of changing _RelativeOffsetOfLocalHeader + // and setting _LengthOfHeader. While ReadHeader() reads the crypto header if + // present, WriteHeader() does not write the crypto header. + WriteHeader(outstream, 0); + StoreRelativeOffset(); + + if (!this.FileName.EndsWith("/")) + { + // Not a directory; there is file data. + // Seek to the beginning of the entry data in the input stream. + + long pos = origRelativeOffsetOfHeader + origLengthOfHeader; + int len = GetLengthOfCryptoHeaderBytes(_Encryption_FromZipFile); + pos -= len; // want to keep the crypto header + _LengthOfHeader += len; + + input.Seek(pos, SeekOrigin.Begin); + + // copy through everything after the header to the output stream + long remaining = this._CompressedSize; + + while (remaining > 0) + { + len = (remaining > bytes.Length) ? bytes.Length : (int)remaining; + + // read + n = input.Read(bytes, 0, len); + //_CheckRead(n); + + // write + outstream.Write(bytes, 0, n); + remaining -= n; + OnWriteBlock(input.BytesRead, this._CompressedSize); + if (_ioOperationCanceled) + break; + } + + // bit 3 descriptor + if ((this._BitField & 0x0008) == 0x0008) + { + int size = 16; + if (_InputUsesZip64) size += 8; + byte[] Descriptor = new byte[size]; + input.Read(Descriptor, 0, size); + + if (_InputUsesZip64 && _container.UseZip64WhenSaving == Zip64Option.Never) + { + // original descriptor was 24 bytes, now we need 16. + // Must check for underflow here. + // signature + CRC. + outstream.Write(Descriptor, 0, 8); + + // Compressed + if (_CompressedSize > 0xFFFFFFFF) + throw new InvalidOperationException("ZIP64 is required"); + outstream.Write(Descriptor, 8, 4); + + // UnCompressed + if (_UncompressedSize > 0xFFFFFFFF) + throw new InvalidOperationException("ZIP64 is required"); + outstream.Write(Descriptor, 16, 4); + _LengthOfTrailer -= 8; + } + else if (!_InputUsesZip64 && _container.UseZip64WhenSaving == Zip64Option.Always) + { + // original descriptor was 16 bytes, now we need 24 + // signature + CRC + byte[] pad = new byte[4]; + outstream.Write(Descriptor, 0, 8); + // Compressed + outstream.Write(Descriptor, 8, 4); + outstream.Write(pad, 0, 4); + // UnCompressed + outstream.Write(Descriptor, 12, 4); + outstream.Write(pad, 0, 4); + _LengthOfTrailer += 8; + } + else + { + // same descriptor on input and output. Copy it through. + outstream.Write(Descriptor, 0, size); + //_LengthOfTrailer += size; + } + } + } + + _TotalEntrySize = _LengthOfHeader + _CompressedFileDataSize + _LengthOfTrailer; + } + + + private void CopyThroughWithNoChange(Stream outstream) + { + int n; + byte[] bytes = new byte[BufferSize]; + var input = new CountingStream(this.ArchiveStream); + + // seek to the beginning of the entry data in the input stream + input.Seek(this._RelativeOffsetOfLocalHeader, SeekOrigin.Begin); + + if (this._TotalEntrySize == 0) + { + // We've never set the length of the entry. + // Set it here. + this._TotalEntrySize = this._LengthOfHeader + this._CompressedFileDataSize + _LengthOfTrailer; + + // The CompressedSize includes all the leading metadata associated + // to encryption, if any, as well as the compressed data, or + // compressed-then-encrypted data, and the trailer in case of AES. + + // The CompressedFileData size is the same, less the encryption + // framing data (12 bytes header for PKZip; 10/18 bytes header and + // 10 byte trailer for AES). + + // The _LengthOfHeader includes all the zip entry header plus the + // crypto header, if any. The _LengthOfTrailer includes the + // 10-byte MAC for AES, where appropriate, and the bit-3 + // Descriptor, where applicable. + } + + + // workitem 5616 + // remember the offset, within the output stream, of this particular entry header. + // This may have changed if any of the other entries changed (eg, if a different + // entry was removed or added.) + var counter = outstream as CountingStream; + _RelativeOffsetOfLocalHeader = (counter != null) + ? counter.ComputedPosition + : outstream.Position; // BytesWritten + + // copy through the header, filedata, trailer, everything... + long remaining = this._TotalEntrySize; + while (remaining > 0) + { + int len = (remaining > bytes.Length) ? bytes.Length : (int)remaining; + + // read + n = input.Read(bytes, 0, len); + //_CheckRead(n); + + // write + outstream.Write(bytes, 0, n); + remaining -= n; + OnWriteBlock(input.BytesRead, this._TotalEntrySize); + if (_ioOperationCanceled) + break; + } + } + + + + + [System.Diagnostics.ConditionalAttribute("Trace")] + private void TraceWriteLine(string format, params object[] varParams) + { + lock (_outputLock) + { + int tid = System.Threading.Thread.CurrentThread.GetHashCode(); +#if ! (NETCF || SILVERLIGHT) + Console.ForegroundColor = (ConsoleColor)(tid % 8 + 8); +#endif + Console.Write("{0:000} ZipEntry.Write ", tid); + Console.WriteLine(format, varParams); +#if ! (NETCF || SILVERLIGHT) + Console.ResetColor(); +#endif + } + } + + private object _outputLock = new Object(); + } +} diff --git a/DotNetZip/Zip/ZipEntry.cs b/DotNetZip/Zip/ZipEntry.cs new file mode 100644 index 0000000..02d3f5b --- /dev/null +++ b/DotNetZip/Zip/ZipEntry.cs @@ -0,0 +1,2968 @@ +// ZipEntry.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2006-2010 Dino Chiesa. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2011-August-06 17:25:53> +// +// ------------------------------------------------------------------ +// +// This module defines the ZipEntry class, which models the entries within a zip file. +// +// Created: Tue, 27 Mar 2007 15:30 +// +// ------------------------------------------------------------------ + + +using System; +using System.IO; +using Interop = System.Runtime.InteropServices; + +namespace Ionic.Zip +{ + /// + /// Represents a single entry in a ZipFile. Typically, applications get a ZipEntry + /// by enumerating the entries within a ZipFile, or by adding an entry to a ZipFile. + /// + + [Interop.GuidAttribute("ebc25cf6-9120-4283-b972-0e5520d00004")] + [Interop.ComVisible(true)] +#if !NETCF + [Interop.ClassInterface(Interop.ClassInterfaceType.AutoDispatch)] // AutoDual +#endif + public partial class ZipEntry + { + /// + /// Default constructor. + /// + /// + /// Applications should never need to call this directly. It is exposed to + /// support COM Automation environments. + /// + public ZipEntry() + { + _CompressionMethod = (Int16)CompressionMethod.Deflate; + _CompressionLevel = Ionic.Zlib.CompressionLevel.Default; + _Encryption = EncryptionAlgorithm.None; + _Source = ZipEntrySource.None; + AlternateEncoding = System.Text.Encoding.GetEncoding("IBM437"); + AlternateEncodingUsage = ZipOption.Never; + } + + /// + /// The time and date at which the file indicated by the ZipEntry was + /// last modified. + /// + /// + /// + /// + /// The DotNetZip library sets the LastModified value for an entry, equal to + /// the Last Modified time of the file in the filesystem. If an entry is + /// added from a stream, the library uses System.DateTime.Now for this + /// value, for the given entry. + /// + /// + /// + /// This property allows the application to retrieve and possibly set the + /// LastModified value on an entry, to an arbitrary value. values with a + /// setting of DateTimeKind.Unspecified are taken to be expressed as + /// DateTimeKind.Local. + /// + /// + /// + /// Be aware that because of the way PKWare's + /// Zip specification describes how times are stored in the zip file, + /// the full precision of the System.DateTime datatype is not stored + /// for the last modified time when saving zip files. For more information on + /// how times are formatted, see the PKZip specification. + /// + /// + /// + /// The actual last modified time of a file can be stored in multiple ways in + /// the zip file, and they are not mutually exclusive: + /// + /// + /// + /// + /// In the so-called "DOS" format, which has a 2-second precision. Values + /// are rounded to the nearest even second. For example, if the time on the + /// file is 12:34:43, then it will be stored as 12:34:44. This first value + /// is accessible via the LastModified property. This value is always + /// present in the metadata for each zip entry. In some cases the value is + /// invalid, or zero. + /// + /// + /// + /// In the so-called "Windows" or "NTFS" format, as an 8-byte integer + /// quantity expressed as the number of 1/10 milliseconds (in other words + /// the number of 100 nanosecond units) since January 1, 1601 (UTC). This + /// format is how Windows represents file times. This time is accessible + /// via the ModifiedTime property. + /// + /// + /// + /// In the "Unix" format, a 4-byte quantity specifying the number of seconds since + /// January 1, 1970 UTC. + /// + /// + /// + /// In an older format, now deprecated but still used by some current + /// tools. This format is also a 4-byte quantity specifying the number of + /// seconds since January 1, 1970 UTC. + /// + /// + /// + /// + /// + /// Zip tools and libraries will always at least handle (read or write) the + /// DOS time, and may also handle the other time formats. Keep in mind that + /// while the names refer to particular operating systems, there is nothing in + /// the time formats themselves that prevents their use on other operating + /// systems. + /// + /// + /// + /// When reading ZIP files, the DotNetZip library reads the Windows-formatted + /// time, if it is stored in the entry, and sets both LastModified and + /// ModifiedTime to that value. When writing ZIP files, the DotNetZip + /// library by default will write both time quantities. It can also emit the + /// Unix-formatted time if desired (See .) + /// + /// + /// + /// The last modified time of the file created upon a call to + /// ZipEntry.Extract() may be adjusted during extraction to compensate + /// for differences in how the .NET Base Class Library deals with daylight + /// saving time (DST) versus how the Windows filesystem deals with daylight + /// saving time. Raymond Chen provides + /// some good context. + /// + /// + /// + /// In a nutshell: Daylight savings time rules change regularly. In 2007, for + /// example, the inception week of DST changed. In 1977, DST was in place all + /// year round. In 1945, likewise. And so on. Win32 does not attempt to + /// guess which time zone rules were in effect at the time in question. It + /// will render a time as "standard time" and allow the app to change to DST + /// as necessary. .NET makes a different choice. + /// + /// + /// + /// Compare the output of FileInfo.LastWriteTime.ToString("f") with what you + /// see in the Windows Explorer property sheet for a file that was last + /// written to on the other side of the DST transition. For example, suppose + /// the file was last modified on October 17, 2003, during DST but DST is not + /// currently in effect. Explorer's file properties reports Thursday, October + /// 17, 2003, 8:45:38 AM, but .NETs FileInfo reports Thursday, October 17, + /// 2003, 9:45 AM. + /// + /// + /// + /// Win32 says, "Thursday, October 17, 2002 8:45:38 AM PST". Note: Pacific + /// STANDARD Time. Even though October 17 of that year occurred during Pacific + /// Daylight Time, Win32 displays the time as standard time because that's + /// what time it is NOW. + /// + /// + /// + /// .NET BCL assumes that the current DST rules were in place at the time in + /// question. So, .NET says, "Well, if the rules in effect now were also in + /// effect on October 17, 2003, then that would be daylight time" so it + /// displays "Thursday, October 17, 2003, 9:45 AM PDT" - daylight time. + /// + /// + /// + /// So .NET gives a value which is more intuitively correct, but is also + /// potentially incorrect, and which is not invertible. Win32 gives a value + /// which is intuitively incorrect, but is strictly correct. + /// + /// + /// + /// Because of this funkiness, this library adds one hour to the LastModified + /// time on the extracted file, if necessary. That is to say, if the time in + /// question had occurred in what the .NET Base Class Library assumed to be + /// DST. This assumption may be wrong given the constantly changing DST rules, + /// but it is the best we can do. + /// + /// + /// + /// + public DateTime LastModified + { + get { return _LastModified.ToLocalTime(); } + set + { + _LastModified = (value.Kind == DateTimeKind.Unspecified) + ? DateTime.SpecifyKind(value, DateTimeKind.Local) + : value.ToLocalTime(); + _Mtime = Ionic.Zip.SharedUtilities.AdjustTime_Reverse(_LastModified).ToUniversalTime(); + _metadataChanged = true; + } + } + + + private int BufferSize + { + get + { + return this._container.BufferSize; + } + } + + /// + /// Last Modified time for the file represented by the entry. + /// + /// + /// + /// + /// + /// This value corresponds to the "last modified" time in the NTFS file times + /// as described in the Zip + /// specification. When getting this property, the value may be + /// different from . When setting the property, + /// the property also gets set, but with a lower + /// precision. + /// + /// + /// + /// Let me explain. It's going to take a while, so get + /// comfortable. Originally, waaaaay back in 1989 when the ZIP specification + /// was originally described by the esteemed Mr. Phil Katz, the dominant + /// operating system of the time was MS-DOS. MSDOS stored file times with a + /// 2-second precision, because, c'mon, who is ever going to need better + /// resolution than THAT? And so ZIP files, regardless of the platform on + /// which the zip file was created, store file times in exactly the same format that DOS used + /// in 1989. + /// + /// + /// + /// Since then, the ZIP spec has evolved, but the internal format for file + /// timestamps remains the same. Despite the fact that the way times are + /// stored in a zip file is rooted in DOS heritage, any program on any + /// operating system can format a time in this way, and most zip tools and + /// libraries DO - they round file times to the nearest even second and store + /// it just like DOS did 25+ years ago. + /// + /// + /// + /// PKWare extended the ZIP specification to allow a zip file to store what + /// are called "NTFS Times" and "Unix(tm) times" for a file. These are the + /// last write, last access, and file creation + /// times of a particular file. These metadata are not actually specific + /// to NTFS or Unix. They are tracked for each file by NTFS and by various + /// Unix filesystems, but they are also tracked by other filesystems, too. + /// The key point is that the times are formatted in the zip file + /// in the same way that NTFS formats the time (ticks since win32 epoch), + /// or in the same way that Unix formats the time (seconds since Unix + /// epoch). As with the DOS time, any tool or library running on any + /// operating system is capable of formatting a time in one of these ways + /// and embedding it into the zip file. + /// + /// + /// + /// These extended times are higher precision quantities than the DOS time. + /// As described above, the (DOS) LastModified has a precision of 2 seconds. + /// The Unix time is stored with a precision of 1 second. The NTFS time is + /// stored with a precision of 0.0000001 seconds. The quantities are easily + /// convertible, except for the loss of precision you may incur. + /// + /// + /// + /// A zip archive can store the {C,A,M} times in NTFS format, in Unix format, + /// or not at all. Often a tool running on Unix or Mac will embed the times + /// in Unix format (1 second precision), while WinZip running on Windows might + /// embed the times in NTFS format (precision of of 0.0000001 seconds). When + /// reading a zip file with these "extended" times, in either format, + /// DotNetZip represents the values with the + /// ModifiedTime, AccessedTime and CreationTime + /// properties on the ZipEntry. + /// + /// + /// + /// While any zip application or library, regardless of the platform it + /// runs on, could use any of the time formats allowed by the ZIP + /// specification, not all zip tools or libraries do support all these + /// formats. Storing the higher-precision times for each entry is + /// optional for zip files, and many tools and libraries don't use the + /// higher precision quantities at all. The old DOS time, represented by + /// , is guaranteed to be present, though it + /// sometimes unset. + /// + /// + /// + /// Ok, getting back to the question about how the LastModified + /// property relates to this ModifiedTime + /// property... LastModified is always set, while + /// ModifiedTime is not. (The other times stored in the NTFS + /// times extension, CreationTime and AccessedTime also + /// may not be set on an entry that is read from an existing zip file.) + /// When reading a zip file, then LastModified takes the DOS time + /// that is stored with the file. If the DOS time has been stored as zero + /// in the zipfile, then this library will use DateTime.Now for the + /// LastModified value. If the ZIP file was created by an evolved + /// tool, then there will also be higher precision NTFS or Unix times in + /// the zip file. In that case, this library will read those times, and + /// set LastModified and ModifiedTime to the same value, the + /// one corresponding to the last write time of the file. If there are no + /// higher precision times stored for the entry, then ModifiedTime + /// remains unset (likewise AccessedTime and CreationTime), + /// and LastModified keeps its DOS time. + /// + /// + /// + /// When creating zip files with this library, by default the extended time + /// properties (ModifiedTime, AccessedTime, and + /// CreationTime) are set on the ZipEntry instance, and these data are + /// stored in the zip archive for each entry, in NTFS format. If you add an + /// entry from an actual filesystem file, then the entry gets the actual file + /// times for that file, to NTFS-level precision. If you add an entry from a + /// stream, or a string, then the times get the value DateTime.Now. In + /// this case LastModified and ModifiedTime will be identical, + /// to 2 seconds of precision. You can explicitly set the + /// CreationTime, AccessedTime, and ModifiedTime of an + /// entry using the property setters. If you want to set all of those + /// quantities, it's more efficient to use the method. Those + /// changes are not made permanent in the zip file until you call or one of its cousins. + /// + /// + /// + /// When creating a zip file, you can override the default behavior of + /// this library for formatting times in the zip file, disabling the + /// embedding of file times in NTFS format or enabling the storage of file + /// times in Unix format, or both. You may want to do this, for example, + /// when creating a zip file on Windows, that will be consumed on a Mac, + /// by an application that is not hip to the "NTFS times" format. To do + /// this, use the and + /// properties. A valid zip + /// file may store the file times in both formats. But, there are no + /// guarantees that a program running on Mac or Linux will gracefully + /// handle the NTFS-formatted times when Unix times are present, or that a + /// non-DotNetZip-powered application running on Windows will be able to + /// handle file times in Unix format. DotNetZip will always do something + /// reasonable; other libraries or tools may not. When in doubt, test. + /// + /// + /// + /// I'll bet you didn't think one person could type so much about time, eh? + /// And reading it was so enjoyable, too! Well, in appreciation, maybe you + /// should donate? + /// + /// + /// + /// + /// + /// + /// + public DateTime ModifiedTime + { + get { return _Mtime; } + set + { + SetEntryTimes(_Ctime, _Atime, value); + } + } + + /// + /// Last Access time for the file represented by the entry. + /// + /// + /// This value may or may not be meaningful. If the ZipEntry was read from an existing + /// Zip archive, this information may not be available. For an explanation of why, see + /// . + /// + /// + /// + /// + public DateTime AccessedTime + { + get { return _Atime; } + set + { + SetEntryTimes(_Ctime, value, _Mtime); + } + } + + /// + /// The file creation time for the file represented by the entry. + /// + /// + /// + /// This value may or may not be meaningful. If the ZipEntry was read + /// from an existing zip archive, and the creation time was not set on the entry + /// when the zip file was created, then this property may be meaningless. For an + /// explanation of why, see . + /// + /// + /// + /// + public DateTime CreationTime + { + get { return _Ctime; } + set + { + SetEntryTimes(value, _Atime, _Mtime); + } + } + + /// + /// Sets the NTFS Creation, Access, and Modified times for the given entry. + /// + /// + /// + /// + /// When adding an entry from a file or directory, the Creation, Access, and + /// Modified times for the given entry are automatically set from the + /// filesystem values. When adding an entry from a stream or string, the + /// values are implicitly set to DateTime.Now. The application may wish to + /// set these values to some arbitrary value, before saving the archive, and + /// can do so using the various setters. If you want to set all of the times, + /// this method is more efficient. + /// + /// + /// + /// The values you set here will be retrievable with the , and properties. + /// + /// + /// + /// When this method is called, if both and are false, then the + /// EmitTimesInWindowsFormatWhenSaving flag is automatically set. + /// + /// + /// + /// DateTime values provided here without a DateTimeKind are assumed to be Local Time. + /// + /// + /// + /// the creation time of the entry. + /// the last access time of the entry. + /// the last modified time of the entry. + /// + /// + /// + /// + /// + /// + public void SetEntryTimes(DateTime created, DateTime accessed, DateTime modified) + { + _ntfsTimesAreSet = true; + if (created == _zeroHour && created.Kind == _zeroHour.Kind) created = _win32Epoch; + if (accessed == _zeroHour && accessed.Kind == _zeroHour.Kind) accessed = _win32Epoch; + if (modified == _zeroHour && modified.Kind == _zeroHour.Kind) modified = _win32Epoch; + _Ctime = created.ToUniversalTime(); + _Atime = accessed.ToUniversalTime(); + _Mtime = modified.ToUniversalTime(); + _LastModified = _Mtime; + if (!_emitUnixTimes && !_emitNtfsTimes) + _emitNtfsTimes = true; + _metadataChanged = true; + } + + + + /// + /// Specifies whether the Creation, Access, and Modified times for the given + /// entry will be emitted in "Windows format" when the zip archive is saved. + /// + /// + /// + /// + /// An application creating a zip archive can use this flag to explicitly + /// specify that the file times for the entry should or should not be stored + /// in the zip archive in the format used by Windows. The default value of + /// this property is true. + /// + /// + /// + /// When adding an entry from a file or directory, the Creation (), Access (), and Modified + /// () times for the given entry are automatically + /// set from the filesystem values. When adding an entry from a stream or + /// string, all three values are implicitly set to DateTime.Now. Applications + /// can also explicitly set those times by calling . + /// + /// + /// + /// PKWARE's + /// zip specification describes multiple ways to format these times in a + /// zip file. One is the format Windows applications normally use: 100ns ticks + /// since Jan 1, 1601 UTC. The other is a format Unix applications typically + /// use: seconds since January 1, 1970 UTC. Each format can be stored in an + /// "extra field" in the zip entry when saving the zip archive. The former + /// uses an extra field with a Header Id of 0x000A, while the latter uses a + /// header ID of 0x5455. + /// + /// + /// + /// Not all zip tools and libraries can interpret these fields. Windows + /// compressed folders is one that can read the Windows Format timestamps, + /// while I believe the Infozip + /// tools can read the Unix format timestamps. Although the time values are + /// easily convertible, subject to a loss of precision, some tools and + /// libraries may be able to read only one or the other. DotNetZip can read or + /// write times in either or both formats. + /// + /// + /// + /// The times stored are taken from , , and . + /// + /// + /// + /// This property is not mutually exclusive from the property. It is + /// possible that a zip entry can embed the timestamps in both forms, one + /// form, or neither. But, there are no guarantees that a program running on + /// Mac or Linux will gracefully handle NTFS Formatted times, or that a + /// non-DotNetZip-powered application running on Windows will be able to + /// handle file times in Unix format. When in doubt, test. + /// + /// + /// + /// Normally you will use the ZipFile.EmitTimesInWindowsFormatWhenSaving + /// property, to specify the behavior for all entries in a zip, rather than + /// the property on each individual entry. + /// + /// + /// + /// + /// + /// + /// + /// + /// + public bool EmitTimesInWindowsFormatWhenSaving + { + get + { + return _emitNtfsTimes; + } + set + { + _emitNtfsTimes = value; + _metadataChanged = true; + } + } + + /// + /// Specifies whether the Creation, Access, and Modified times for the given + /// entry will be emitted in "Unix(tm) format" when the zip archive is saved. + /// + /// + /// + /// + /// An application creating a zip archive can use this flag to explicitly + /// specify that the file times for the entry should or should not be stored + /// in the zip archive in the format used by Unix. By default this flag is + /// false, meaning the Unix-format times are not stored in the zip + /// archive. + /// + /// + /// + /// When adding an entry from a file or directory, the Creation (), Access (), and Modified + /// () times for the given entry are automatically + /// set from the filesystem values. When adding an entry from a stream or + /// string, all three values are implicitly set to DateTime.Now. Applications + /// can also explicitly set those times by calling . + /// + /// + /// + /// PKWARE's + /// zip specification describes multiple ways to format these times in a + /// zip file. One is the format Windows applications normally use: 100ns ticks + /// since Jan 1, 1601 UTC. The other is a format Unix applications typically + /// use: seconds since Jan 1, 1970 UTC. Each format can be stored in an + /// "extra field" in the zip entry when saving the zip archive. The former + /// uses an extra field with a Header Id of 0x000A, while the latter uses a + /// header ID of 0x5455. + /// + /// + /// + /// Not all tools and libraries can interpret these fields. Windows + /// compressed folders is one that can read the Windows Format timestamps, + /// while I believe the Infozip + /// tools can read the Unix format timestamps. Although the time values are + /// easily convertible, subject to a loss of precision, some tools and + /// libraries may be able to read only one or the other. DotNetZip can read or + /// write times in either or both formats. + /// + /// + /// + /// The times stored are taken from , , and . + /// + /// + /// + /// This property is not mutually exclusive from the property. It is + /// possible that a zip entry can embed the timestamps in both forms, one + /// form, or neither. But, there are no guarantees that a program running on + /// Mac or Linux will gracefully handle NTFS Formatted times, or that a + /// non-DotNetZip-powered application running on Windows will be able to + /// handle file times in Unix format. When in doubt, test. + /// + /// + /// + /// Normally you will use the ZipFile.EmitTimesInUnixFormatWhenSaving + /// property, to specify the behavior for all entries, rather than the + /// property on each individual entry. + /// + /// + /// + /// + /// + /// + /// + /// + /// + public bool EmitTimesInUnixFormatWhenSaving + { + get + { + return _emitUnixTimes; + } + set + { + _emitUnixTimes = value; + _metadataChanged = true; + } + } + + + /// + /// The type of timestamp attached to the ZipEntry. + /// + /// + /// + /// This property is valid only for a ZipEntry that was read from a zip archive. + /// It indicates the type of timestamp attached to the entry. + /// + /// + /// + /// + public ZipEntryTimestamp Timestamp + { + get + { + return _timestamp; + } + } + + /// + /// The file attributes for the entry. + /// + /// + /// + /// + /// + /// The attributes in NTFS include + /// ReadOnly, Archive, Hidden, System, and Indexed. When adding a + /// ZipEntry to a ZipFile, these attributes are set implicitly when + /// adding an entry from the filesystem. When adding an entry from a stream + /// or string, the Attributes are not set implicitly. Regardless of the way + /// an entry was added to a ZipFile, you can set the attributes + /// explicitly if you like. + /// + /// + /// + /// When reading a ZipEntry from a ZipFile, the attributes are + /// set according to the data stored in the ZipFile. If you extract the + /// entry from the archive to a filesystem file, DotNetZip will set the + /// attributes on the resulting file accordingly. + /// + /// + /// + /// The attributes can be set explicitly by the application. For example the + /// application may wish to set the FileAttributes.ReadOnly bit for all + /// entries added to an archive, so that on unpack, this attribute will be set + /// on the extracted file. Any changes you make to this property are made + /// permanent only when you call a Save() method on the ZipFile + /// instance that contains the ZipEntry. + /// + /// + /// + /// For example, an application may wish to zip up a directory and set the + /// ReadOnly bit on every file in the archive, so that upon later extraction, + /// the resulting files will be marked as ReadOnly. Not every extraction tool + /// respects these attributes, but if you unpack with DotNetZip, as for + /// example in a self-extracting archive, then the attributes will be set as + /// they are stored in the ZipFile. + /// + /// + /// + /// These attributes may not be interesting or useful if the resulting archive + /// is extracted on a non-Windows platform. How these attributes get used + /// upon extraction depends on the platform and tool used. + /// + /// + /// + /// This property is only partially supported in the Silverlight version + /// of the library: applications can read attributes on entries within + /// ZipFiles. But extracting entries within Silverlight will not set the + /// attributes on the extracted files. + /// + /// + /// + public System.IO.FileAttributes Attributes + { + // workitem 7071 + get { return (System.IO.FileAttributes)_ExternalFileAttrs; } + set + { + _ExternalFileAttrs = (int)value; + // Since the application is explicitly setting the attributes, overwriting + // whatever was there, we will explicitly set the Version made by field. + // workitem 7926 - "version made by" OS should be zero for compat with WinZip + _VersionMadeBy = (0 << 8) + 45; // v4.5 of the spec + _metadataChanged = true; + } + } + + + /// + /// The name of the filesystem file, referred to by the ZipEntry. + /// + /// + /// + /// + /// This property specifies the thing-to-be-zipped on disk, and is set only + /// when the ZipEntry is being created from a filesystem file. If the + /// ZipFile is instantiated by reading an existing .zip archive, then + /// the LocalFileName will be null (Nothing in VB). + /// + /// + /// + /// When it is set, the value of this property may be different than , which is the path used in the archive itself. If you + /// call Zip.AddFile("foop.txt", AlternativeDirectory), then the path + /// used for the ZipEntry within the zip archive will be different + /// than this path. + /// + /// + /// + /// If the entry is being added from a stream, then this is null (Nothing in VB). + /// + /// + /// + /// + internal string LocalFileName + { + get { return _LocalFileName; } + } + + /// + /// The name of the file contained in the ZipEntry. + /// + /// + /// + /// + /// + /// This is the name of the entry in the ZipFile itself. When creating + /// a zip archive, if the ZipEntry has been created from a filesystem + /// file, via a call to or , or a related overload, the value + /// of this property is derived from the name of that file. The + /// FileName property does not include drive letters, and may include a + /// different directory path, depending on the value of the + /// directoryPathInArchive parameter used when adding the entry into + /// the ZipFile. + /// + /// + /// + /// In some cases there is no related filesystem file - for example when a + /// ZipEntry is created using or one of the similar overloads. In this case, the value of + /// this property is derived from the fileName and the directory path passed + /// to that method. + /// + /// + /// + /// When reading a zip file, this property takes the value of the entry name + /// as stored in the zip file. If you extract such an entry, the extracted + /// file will take the name given by this property. + /// + /// + /// + /// Applications can set this property when creating new zip archives or when + /// reading existing archives. When setting this property, the actual value + /// that is set will replace backslashes with forward slashes, in accordance + /// with the Zip + /// specification, for compatibility with Unix(tm) and ... get + /// this.... Amiga! + /// + /// + /// + /// If an application reads a ZipFile via or a related overload, and then explicitly + /// sets the FileName on an entry contained within the ZipFile, and + /// then calls , the application will effectively + /// rename the entry within the zip archive. + /// + /// + /// + /// If an application sets the value of FileName, then calls + /// Extract() on the entry, the entry is extracted to a file using the + /// newly set value as the filename. The FileName value is made + /// permanent in the zip archive only after a call to one of the + /// ZipFile.Save() methods on the ZipFile that contains the + /// ZipEntry. + /// + /// + /// + /// If an application attempts to set the FileName to a value that + /// would result in a duplicate entry in the ZipFile, an exception is + /// thrown. + /// + /// + /// + /// When a ZipEntry is contained within a ZipFile, applications + /// cannot rename the entry within the context of a foreach (For + /// Each in VB) loop, because of the way the ZipFile stores + /// entries. If you need to enumerate through all the entries and rename one + /// or more of them, use ZipFile.EntriesSorted as the + /// collection. See also, ZipFile.GetEnumerator(). + /// + /// + /// + public string FileName + { + get { return _FileNameInArchive; } + set + { + if (_container.ZipFile == null) + throw new ZipException("Cannot rename; this is not supported in ZipOutputStream/ZipInputStream."); + + // rename the entry! + if (String.IsNullOrEmpty(value)) throw new ZipException("The FileName must be non empty and non-null."); + + var filename = ZipEntry.NameInArchive(value, null); + // workitem 8180 + if (_FileNameInArchive == filename) return; // nothing to do + + // workitem 8047 - when renaming, must remove old and then add a new entry + this._container.ZipFile.RemoveEntry(this); + this._container.ZipFile.InternalAddEntry(filename, this); + + _FileNameInArchive = filename; + _container.ZipFile.NotifyEntryChanged(); + _metadataChanged = true; + } + } + + + /// + /// The stream that provides content for the ZipEntry. + /// + /// + /// + /// + /// + /// The application can use this property to set the input stream for an + /// entry on a just-in-time basis. Imagine a scenario where the application + /// creates a ZipFile comprised of content obtained from hundreds of + /// files, via calls to AddFile(). The DotNetZip library opens streams + /// on these files on a just-in-time basis, only when writing the entry out to + /// an external store within the scope of a ZipFile.Save() call. Only + /// one input stream is opened at a time, as each entry is being written out. + /// + /// + /// + /// Now imagine a different application that creates a ZipFile + /// with content obtained from hundreds of streams, added through . Normally the + /// application would supply an open stream to that call. But when large + /// numbers of streams are being added, this can mean many open streams at one + /// time, unnecessarily. + /// + /// + /// + /// To avoid this, call and specify delegates that open and close the stream at + /// the time of Save. + /// + /// + /// + /// + /// Setting the value of this property when the entry was not added from a + /// stream (for example, when the ZipEntry was added with or , or when the entry was added by + /// reading an existing zip archive) will throw an exception. + /// + /// + /// + /// + public Stream InputStream + { + get { return _sourceStream; } + + set + { + if (this._Source != ZipEntrySource.Stream) + throw new ZipException("You must not set the input stream for this entry."); + + _sourceWasJitProvided = true; + _sourceStream = value; + } + } + + + /// + /// A flag indicating whether the InputStream was provided Just-in-time. + /// + /// + /// + /// + /// + /// When creating a zip archive, an application can obtain content for one or + /// more of the ZipEntry instances from streams, using the method. At the time + /// of calling that method, the application can supply null as the value of + /// the stream parameter. By doing so, the application indicates to the + /// library that it will provide a stream for the entry on a just-in-time + /// basis, at the time one of the ZipFile.Save() methods is called and + /// the data for the various entries are being compressed and written out. + /// + /// + /// + /// In this case, the application can set the + /// property, typically within the SaveProgress event (event type: ) for that entry. + /// + /// + /// + /// The application will later want to call Close() and Dispose() on that + /// stream. In the SaveProgress event, when the event type is , the application can + /// do so. This flag indicates that the stream has been provided by the + /// application on a just-in-time basis and that it is the application's + /// responsibility to call Close/Dispose on that stream. + /// + /// + /// + /// + public bool InputStreamWasJitProvided + { + get { return _sourceWasJitProvided; } + } + + + + /// + /// An enum indicating the source of the ZipEntry. + /// + public ZipEntrySource Source + { + get { return _Source; } + } + + + /// + /// The version of the zip engine needed to read the ZipEntry. + /// + /// + /// + /// + /// This is a readonly property, indicating the version of the Zip + /// specification that the extracting tool or library must support to + /// extract the given entry. Generally higher versions indicate newer + /// features. Older zip engines obviously won't know about new features, and + /// won't be able to extract entries that depend on those newer features. + /// + /// + /// + /// + /// value + /// Features + /// + /// + /// + /// 20 + /// a basic Zip Entry, potentially using PKZIP encryption. + /// + /// + /// + /// + /// 45 + /// The ZIP64 extension is used on the entry. + /// + /// + /// + /// + /// 46 + /// File is compressed using BZIP2 compression* + /// + /// + /// + /// 50 + /// File is encrypted using PkWare's DES, 3DES, (broken) RC2 or RC4 + /// + /// + /// + /// 51 + /// File is encrypted using PKWare's AES encryption or corrected RC2 encryption. + /// + /// + /// + /// 52 + /// File is encrypted using corrected RC2-64 encryption** + /// + /// + /// + /// 61 + /// File is encrypted using non-OAEP key wrapping*** + /// + /// + /// + /// 63 + /// File is compressed using LZMA, PPMd+, Blowfish, or Twofish + /// + /// + /// + /// + /// + /// There are other values possible, not listed here. DotNetZip supports + /// regular PKZip encryption, and ZIP64 extensions. DotNetZip cannot extract + /// entries that require a zip engine higher than 45. + /// + /// + /// + /// This value is set upon reading an existing zip file, or after saving a zip + /// archive. + /// + /// + public Int16 VersionNeeded + { + get { return _VersionNeeded; } + } + + /// + /// The comment attached to the ZipEntry. + /// + /// + /// + /// + /// Each entry in a zip file can optionally have a comment associated to + /// it. The comment might be displayed by a zip tool during extraction, for + /// example. + /// + /// + /// + /// By default, the Comment is encoded in IBM437 code page. You can + /// specify an alternative with and + /// . + /// + /// + /// + /// + public string Comment + { + get { return _Comment; } + set + { + _Comment = value; + _metadataChanged = true; + } + } + + + /// + /// Indicates whether the entry requires ZIP64 extensions. + /// + /// + /// + /// + /// + /// This property is null (Nothing in VB) until a Save() method on the + /// containing instance has been called. The property is + /// non-null (HasValue is true) only after a Save() method has + /// been called. + /// + /// + /// + /// After the containing ZipFile has been saved, the Value of this + /// property is true if any of the following three conditions holds: the + /// uncompressed size of the entry is larger than 0xFFFFFFFF; the compressed + /// size of the entry is larger than 0xFFFFFFFF; the relative offset of the + /// entry within the zip archive is larger than 0xFFFFFFFF. These quantities + /// are not known until a Save() is attempted on the zip archive and + /// the compression is applied. + /// + /// + /// + /// If none of the three conditions holds, then the Value is false. + /// + /// + /// + /// A Value of false does not indicate that the entry, as saved in the + /// zip archive, does not use ZIP64. It merely indicates that ZIP64 is + /// not required. An entry may use ZIP64 even when not required if + /// the property on the containing + /// ZipFile instance is set to , or if + /// the property on the containing + /// ZipFile instance is set to + /// and the output stream was not seekable. + /// + /// + /// + /// + public Nullable RequiresZip64 + { + get + { + return _entryRequiresZip64; + } + } + + /// + /// Indicates whether the entry actually used ZIP64 extensions, as it was most + /// recently written to the output file or stream. + /// + /// + /// + /// + /// + /// This Nullable property is null (Nothing in VB) until a Save() + /// method on the containing instance has been + /// called. HasValue is true only after a Save() method has been + /// called. + /// + /// + /// + /// The value of this property for a particular ZipEntry may change + /// over successive calls to Save() methods on the containing ZipFile, + /// even if the file that corresponds to the ZipEntry does not. This + /// may happen if other entries contained in the ZipFile expand, + /// causing the offset for this particular entry to exceed 0xFFFFFFFF. + /// + /// + /// + public Nullable OutputUsedZip64 + { + get { return _OutputUsesZip64; } + } + + + /// + /// The bitfield for the entry as defined in the zip spec. You probably + /// never need to look at this. + /// + /// + /// + /// + /// You probably do not need to concern yourself with the contents of this + /// property, but in case you do: + /// + /// + /// + /// + /// bit + /// meaning + /// + /// + /// + /// 0 + /// set if encryption is used. + /// + /// + /// + /// 1-2 + /// + /// set to determine whether normal, max, fast deflation. DotNetZip library + /// always leaves these bits unset when writing (indicating "normal" + /// deflation"), but can read an entry with any value here. + /// + /// + /// + /// + /// 3 + /// + /// Indicates that the Crc32, Compressed and Uncompressed sizes are zero in the + /// local header. This bit gets set on an entry during writing a zip file, when + /// it is saved to a non-seekable output stream. + /// + /// + /// + /// + /// + /// 4 + /// reserved for "enhanced deflating". This library doesn't do enhanced deflating. + /// + /// + /// + /// 5 + /// set to indicate the zip is compressed patched data. This library doesn't do that. + /// + /// + /// + /// 6 + /// + /// set if PKWare's strong encryption is used (must also set bit 1 if bit 6 is + /// set). This bit is not set if WinZip's AES encryption is set. + /// + /// + /// + /// 7 + /// not used + /// + /// + /// + /// 8 + /// not used + /// + /// + /// + /// 9 + /// not used + /// + /// + /// + /// 10 + /// not used + /// + /// + /// + /// 11 + /// + /// Language encoding flag (EFS). If this bit is set, the filename and comment + /// fields for this file must be encoded using UTF-8. This library currently + /// does not support UTF-8. + /// + /// + /// + /// + /// 12 + /// Reserved by PKWARE for enhanced compression. + /// + /// + /// + /// 13 + /// + /// Used when encrypting the Central Directory to indicate selected data + /// values in the Local Header are masked to hide their actual values. See + /// the section in the Zip + /// specification describing the Strong Encryption Specification for + /// details. + /// + /// + /// + /// + /// 14 + /// Reserved by PKWARE. + /// + /// + /// + /// 15 + /// Reserved by PKWARE. + /// + /// + /// + /// + /// + public Int16 BitField + { + get { return _BitField; } + } + + /// + /// The compression method employed for this ZipEntry. + /// + /// + /// + /// + /// + /// The + /// Zip specification allows a variety of compression methods. This + /// library supports just two: 0x08 = Deflate. 0x00 = Store (no compression), + /// for reading or writing. + /// + /// + /// + /// When reading an entry from an existing zipfile, the value you retrieve + /// here indicates the compression method used on the entry by the original + /// creator of the zip. When writing a zipfile, you can specify either 0x08 + /// (Deflate) or 0x00 (None). If you try setting something else, you will get + /// an exception. + /// + /// + /// + /// You may wish to set CompressionMethod to CompressionMethod.None (0) + /// when zipping already-compressed data like a jpg, png, or mp3 file. + /// This can save time and cpu cycles. + /// + /// + /// + /// When setting this property on a ZipEntry that is read from an + /// existing zip file, calling ZipFile.Save() will cause the new + /// CompressionMethod to be used on the entry in the newly saved zip file. + /// + /// + /// + /// Setting this property may have the side effect of modifying the + /// CompressionLevel property. If you set the CompressionMethod to a + /// value other than None, and CompressionLevel is previously + /// set to None, then CompressionLevel will be set to + /// Default. + /// + /// + /// + /// + /// + /// + /// In this example, the first entry added to the zip archive uses the default + /// behavior - compression is used where it makes sense. The second entry, + /// the MP3 file, is added to the archive without being compressed. + /// + /// using (ZipFile zip = new ZipFile(ZipFileToCreate)) + /// { + /// ZipEntry e1= zip.AddFile(@"notes\Readme.txt"); + /// ZipEntry e2= zip.AddFile(@"music\StopThisTrain.mp3"); + /// e2.CompressionMethod = CompressionMethod.None; + /// zip.Save(); + /// } + /// + /// + /// + /// Using zip As New ZipFile(ZipFileToCreate) + /// zip.AddFile("notes\Readme.txt") + /// Dim e2 as ZipEntry = zip.AddFile("music\StopThisTrain.mp3") + /// e2.CompressionMethod = CompressionMethod.None + /// zip.Save + /// End Using + /// + /// + public CompressionMethod CompressionMethod + { + get { return (CompressionMethod)_CompressionMethod; } + set + { + if (value == (CompressionMethod)_CompressionMethod) return; // nothing to do. + + if (value != CompressionMethod.None && value != CompressionMethod.Deflate +#if BZIP + && value != CompressionMethod.BZip2 +#endif + ) + throw new InvalidOperationException("Unsupported compression method."); + + // If the source is a zip archive and there was encryption on the + // entry, changing the compression method is not supported. + // if (this._Source == ZipEntrySource.ZipFile && _sourceIsEncrypted) + // throw new InvalidOperationException("Cannot change compression method on encrypted entries read from archives."); + + _CompressionMethod = (Int16)value; + + if (_CompressionMethod == (Int16)Ionic.Zip.CompressionMethod.None) + _CompressionLevel = Ionic.Zlib.CompressionLevel.None; + else if (CompressionLevel == Ionic.Zlib.CompressionLevel.None) + _CompressionLevel = Ionic.Zlib.CompressionLevel.Default; + + if (_container.ZipFile != null) _container.ZipFile.NotifyEntryChanged(); + _restreamRequiredOnSave = true; + } + } + + + /// + /// Sets the compression level to be used for the entry when saving the zip + /// archive. This applies only for CompressionMethod = DEFLATE. + /// + /// + /// + /// + /// When using the DEFLATE compression method, Varying the compression + /// level used on entries can affect the size-vs-speed tradeoff when + /// compression and decompressing data streams or files. + /// + /// + /// + /// If you do not set this property, the default compression level is used, + /// which normally gives a good balance of compression efficiency and + /// compression speed. In some tests, using BestCompression can + /// double the time it takes to compress, while delivering just a small + /// increase in compression efficiency. This behavior will vary with the + /// type of data you compress. If you are in doubt, just leave this setting + /// alone, and accept the default. + /// + /// + /// + /// When setting this property on a ZipEntry that is read from an + /// existing zip file, calling ZipFile.Save() will cause the new + /// CompressionLevel to be used on the entry in the newly saved zip file. + /// + /// + /// + /// Setting this property may have the side effect of modifying the + /// CompressionMethod property. If you set the CompressionLevel + /// to a value other than None, CompressionMethod will be set + /// to Deflate, if it was previously None. + /// + /// + /// + /// Setting this property has no effect if the CompressionMethod is something + /// other than Deflate or None. + /// + /// + /// + /// + public Ionic.Zlib.CompressionLevel CompressionLevel + { + get + { + return _CompressionLevel; + } + set + { + if (_CompressionMethod != (short)CompressionMethod.Deflate && + _CompressionMethod != (short)CompressionMethod.None) + return ; // no effect + + if (value == Ionic.Zlib.CompressionLevel.Default && + _CompressionMethod == (short)CompressionMethod.Deflate) return; // nothing to do + _CompressionLevel = value; + + if (value == Ionic.Zlib.CompressionLevel.None && + _CompressionMethod == (short)CompressionMethod.None) + return; // nothing more to do + + if (_CompressionLevel == Ionic.Zlib.CompressionLevel.None) + _CompressionMethod = (short) Ionic.Zip.CompressionMethod.None; + else + _CompressionMethod = (short) Ionic.Zip.CompressionMethod.Deflate; + + if (_container.ZipFile != null) _container.ZipFile.NotifyEntryChanged(); + _restreamRequiredOnSave = true; + } + } + + + + /// + /// The compressed size of the file, in bytes, within the zip archive. + /// + /// + /// + /// When reading a ZipFile, this value is read in from the existing + /// zip file. When creating or updating a ZipFile, the compressed + /// size is computed during compression. Therefore the value on a + /// ZipEntry is valid after a call to Save() (or one of its + /// overloads) in that case. + /// + /// + /// + public Int64 CompressedSize + { + get { return _CompressedSize; } + } + + /// + /// The size of the file, in bytes, before compression, or after extraction. + /// + /// + /// + /// When reading a ZipFile, this value is read in from the existing + /// zip file. When creating or updating a ZipFile, the uncompressed + /// size is computed during compression. Therefore the value on a + /// ZipEntry is valid after a call to Save() (or one of its + /// overloads) in that case. + /// + /// + /// + public Int64 UncompressedSize + { + get { return _UncompressedSize; } + } + + /// + /// The ratio of compressed size to uncompressed size of the ZipEntry. + /// + /// + /// + /// + /// This is a ratio of the compressed size to the uncompressed size of the + /// entry, expressed as a double in the range of 0 to 100+. A value of 100 + /// indicates no compression at all. It could be higher than 100 when the + /// compression algorithm actually inflates the data, as may occur for small + /// files, or uncompressible data that is encrypted. + /// + /// + /// + /// You could format it for presentation to a user via a format string of + /// "{3,5:F0}%" to see it as a percentage. + /// + /// + /// + /// If the size of the original uncompressed file is 0, implying a + /// denominator of 0, the return value will be zero. + /// + /// + /// + /// This property is valid after reading in an existing zip file, or after + /// saving the ZipFile that contains the ZipEntry. You cannot know the + /// effect of a compression transform until you try it. + /// + /// + /// + public Double CompressionRatio + { + get + { + if (UncompressedSize == 0) return 0; + return 100 * (1.0 - (1.0 * CompressedSize) / (1.0 * UncompressedSize)); + } + } + + /// + /// The 32-bit CRC (Cyclic Redundancy Check) on the contents of the ZipEntry. + /// + /// + /// + /// + /// You probably don't need to concern yourself with this. It is used + /// internally by DotNetZip to verify files or streams upon extraction. + /// + /// The value is a 32-bit + /// CRC using 0xEDB88320 for the polynomial. This is the same CRC-32 used in + /// PNG, MPEG-2, and other protocols and formats. It is a read-only property; when + /// creating a Zip archive, the CRC for each entry is set only after a call to + /// Save() on the containing ZipFile. When reading an existing zip file, the value + /// of this property reflects the stored CRC for the entry. + /// + /// + public Int32 Crc + { + get { return _Crc32; } + } + + /// + /// True if the entry is a directory (not a file). + /// This is a readonly property on the entry. + /// + public bool IsDirectory + { + get { return _IsDirectory; } + } + + /// + /// A derived property that is true if the entry uses encryption. + /// + /// + /// + /// + /// This is a readonly property on the entry. When reading a zip file, + /// the value for the ZipEntry is determined by the data read + /// from the zip file. After saving a ZipFile, the value of this + /// property for each ZipEntry indicates whether encryption was + /// actually used (which will have been true if the was set and the property + /// was something other than . + /// + /// + public bool UsesEncryption + { + get { return (_Encryption_FromZipFile != EncryptionAlgorithm.None); } + } + + + /// + /// Set this to specify which encryption algorithm to use for the entry when + /// saving it to a zip archive. + /// + /// + /// + /// + /// + /// Set this property in order to encrypt the entry when the ZipFile is + /// saved. When setting this property, you must also set a on the entry. If you set a value other than on this property and do not set a + /// Password then the entry will not be encrypted. The ZipEntry + /// data is encrypted as the ZipFile is saved, when you call or one of its cousins on the containing + /// ZipFile instance. You do not need to specify the Encryption + /// when extracting entries from an archive. + /// + /// + /// + /// The Zip specification from PKWare defines a set of encryption algorithms, + /// and the data formats for the zip archive that support them, and PKWare + /// supports those algorithms in the tools it produces. Other vendors of tools + /// and libraries, such as WinZip or Xceed, typically support a + /// subset of the algorithms specified by PKWare. These tools can + /// sometimes support additional different encryption algorithms and data + /// formats, not specified by PKWare. The AES Encryption specified and + /// supported by WinZip is the most popular example. This library supports a + /// subset of the complete set of algorithms specified by PKWare and other + /// vendors. + /// + /// + /// + /// There is no common, ubiquitous multi-vendor standard for strong encryption + /// within zip files. There is broad support for so-called "traditional" Zip + /// encryption, sometimes called Zip 2.0 encryption, as specified + /// by PKWare, but this encryption is considered weak and + /// breakable. This library currently supports the Zip 2.0 "weak" encryption, + /// and also a stronger WinZip-compatible AES encryption, using either 128-bit + /// or 256-bit key strength. If you want DotNetZip to support an algorithm + /// that is not currently supported, call the author of this library and maybe + /// we can talk business. + /// + /// + /// + /// The class also has a property. In most cases you will use + /// that property when setting encryption. This property takes + /// precedence over any Encryption set on the ZipFile itself. + /// Typically, you would use the per-entry Encryption when most entries in the + /// zip archive use one encryption algorithm, and a few entries use a + /// different one. If all entries in the zip file use the same Encryption, + /// then it is simpler to just set this property on the ZipFile itself, when + /// creating a zip archive. + /// + /// + /// + /// Some comments on updating archives: If you read a ZipFile, you can + /// modify the Encryption on an encrypted entry: you can remove encryption + /// from an entry that was encrypted; you can encrypt an entry that was not + /// encrypted previously; or, you can change the encryption algorithm. The + /// changes in encryption are not made permanent until you call Save() on the + /// ZipFile. To effect changes in encryption, the entry content is + /// streamed through several transformations, depending on the modification + /// the application has requested. For example if the entry is not encrypted + /// and the application sets Encryption to PkzipWeak, then at + /// the time of Save(), the original entry is read and decompressed, + /// then re-compressed and encrypted. Conversely, if the original entry is + /// encrypted with PkzipWeak encryption, and the application sets the + /// Encryption property to WinZipAes128, then at the time of + /// Save(), the original entry is decrypted via PKZIP encryption and + /// decompressed, then re-compressed and re-encrypted with AES. This all + /// happens automatically within the library, but it can be time-consuming for + /// large entries. + /// + /// + /// + /// Additionally, when updating archives, it is not possible to change the + /// password when changing the encryption algorithm. To change both the + /// algorithm and the password, you need to Save() the zipfile twice. First + /// set the Encryption to None, then call Save(). Then set the + /// Encryption to the new value (not "None"), then call Save() + /// once again. + /// + /// + /// + /// The WinZip AES encryption algorithms are not supported on the .NET Compact + /// Framework. + /// + /// + /// + /// + /// + /// This example creates a zip archive that uses encryption, and then extracts + /// entries from the archive. When creating the zip archive, the ReadMe.txt + /// file is zipped without using a password or encryption. The other file + /// uses encryption. + /// + /// + /// // Create a zip archive with AES Encryption. + /// using (ZipFile zip = new ZipFile()) + /// { + /// zip.AddFile("ReadMe.txt") + /// ZipEntry e1= zip.AddFile("2008-Regional-Sales-Report.pdf"); + /// e1.Encryption= EncryptionAlgorithm.WinZipAes256; + /// e1.Password= "Top.Secret.No.Peeking!"; + /// zip.Save("EncryptedArchive.zip"); + /// } + /// + /// // Extract a zip archive that uses AES Encryption. + /// // You do not need to specify the algorithm during extraction. + /// using (ZipFile zip = ZipFile.Read("EncryptedArchive.zip")) + /// { + /// // Specify the password that is used during extraction, for + /// // all entries that require a password: + /// zip.Password= "Top.Secret.No.Peeking!"; + /// zip.ExtractAll("extractDirectory"); + /// } + /// + /// + /// + /// ' Create a zip that uses Encryption. + /// Using zip As New ZipFile() + /// zip.AddFile("ReadMe.txt") + /// Dim e1 as ZipEntry + /// e1= zip.AddFile("2008-Regional-Sales-Report.pdf") + /// e1.Encryption= EncryptionAlgorithm.WinZipAes256 + /// e1.Password= "Top.Secret.No.Peeking!" + /// zip.Save("EncryptedArchive.zip") + /// End Using + /// + /// ' Extract a zip archive that uses AES Encryption. + /// ' You do not need to specify the algorithm during extraction. + /// Using (zip as ZipFile = ZipFile.Read("EncryptedArchive.zip")) + /// ' Specify the password that is used during extraction, for + /// ' all entries that require a password: + /// zip.Password= "Top.Secret.No.Peeking!" + /// zip.ExtractAll("extractDirectory") + /// End Using + /// + /// + /// + /// + /// + /// Thrown in the setter if EncryptionAlgorithm.Unsupported is specified. + /// + /// + /// ZipEntry.Password + /// ZipFile.Encryption + public EncryptionAlgorithm Encryption + { + get + { + return _Encryption; + } + set + { + if (value == _Encryption) return; // no change + + if (value == EncryptionAlgorithm.Unsupported) + throw new InvalidOperationException("You may not set Encryption to that value."); + + // If the source is a zip archive and there was encryption + // on the entry, this will not work. + //if (this._Source == ZipEntrySource.ZipFile && _sourceIsEncrypted) + // throw new InvalidOperationException("You cannot change the encryption method on encrypted entries read from archives."); + + _Encryption = value; + _restreamRequiredOnSave = true; + if (_container.ZipFile!=null) + _container.ZipFile.NotifyEntryChanged(); + } + } + + + /// + /// The Password to be used when encrypting a ZipEntry upon + /// ZipFile.Save(), or when decrypting an entry upon Extract(). + /// + /// + /// + /// + /// This is a write-only property on the entry. Set this to request that the + /// entry be encrypted when writing the zip archive, or set it to specify the + /// password to be used when extracting an existing entry that is encrypted. + /// + /// + /// + /// The password set here is implicitly used to encrypt the entry during the + /// operation, or to decrypt during the or operation. If you set + /// the Password on a ZipEntry after calling Save(), there is no + /// effect. + /// + /// + /// + /// Consider setting the property when using a + /// password. Answering concerns that the standard password protection + /// supported by all zip tools is weak, WinZip has extended the ZIP + /// specification with a way to use AES Encryption to protect entries in the + /// Zip file. Unlike the "PKZIP 2.0" encryption specified in the PKZIP + /// specification, AES + /// Encryption uses a standard, strong, tested, encryption + /// algorithm. DotNetZip can create zip archives that use WinZip-compatible + /// AES encryption, if you set the property. But, + /// archives created that use AES encryption may not be readable by all other + /// tools and libraries. For example, Windows Explorer cannot read a + /// "compressed folder" (a zip file) that uses AES encryption, though it can + /// read a zip file that uses "PKZIP encryption." + /// + /// + /// + /// The class also has a + /// property. This property takes precedence over any password set on the + /// ZipFile itself. Typically, you would use the per-entry Password when most + /// entries in the zip archive use one password, and a few entries use a + /// different password. If all entries in the zip file use the same password, + /// then it is simpler to just set this property on the ZipFile itself, + /// whether creating a zip archive or extracting a zip archive. + /// + /// + /// + /// Some comments on updating archives: If you read a ZipFile, you + /// cannot modify the password on any encrypted entry, except by extracting + /// the entry with the original password (if any), removing the original entry + /// via , and then adding a new + /// entry with a new Password. + /// + /// + /// + /// For example, suppose you read a ZipFile, and there is an encrypted + /// entry. Setting the Password property on that ZipEntry and then + /// calling Save() on the ZipFile does not update the password + /// on that entry in the archive. Neither is an exception thrown. Instead, + /// what happens during the Save() is the existing entry is copied + /// through to the new zip archive, in its original encrypted form. Upon + /// re-reading that archive, the entry can be decrypted with its original + /// password. + /// + /// + /// + /// If you read a ZipFile, and there is an un-encrypted entry, you can set the + /// Password on the entry and then call Save() on the ZipFile, and get + /// encryption on that entry. + /// + /// + /// + /// + /// + /// + /// This example creates a zip file with two entries, and then extracts the + /// entries from the zip file. When creating the zip file, the two files are + /// added to the zip file using password protection. Each entry uses a + /// different password. During extraction, each file is extracted with the + /// appropriate password. + /// + /// + /// // create a file with encryption + /// using (ZipFile zip = new ZipFile()) + /// { + /// ZipEntry entry; + /// entry= zip.AddFile("Declaration.txt"); + /// entry.Password= "123456!"; + /// entry = zip.AddFile("Report.xls"); + /// entry.Password= "1Secret!"; + /// zip.Save("EncryptedArchive.zip"); + /// } + /// + /// // extract entries that use encryption + /// using (ZipFile zip = ZipFile.Read("EncryptedArchive.zip")) + /// { + /// ZipEntry entry; + /// entry = zip["Declaration.txt"]; + /// entry.Password = "123456!"; + /// entry.Extract("extractDir"); + /// entry = zip["Report.xls"]; + /// entry.Password = "1Secret!"; + /// entry.Extract("extractDir"); + /// } + /// + /// + /// + /// + /// Using zip As New ZipFile + /// Dim entry as ZipEntry + /// entry= zip.AddFile("Declaration.txt") + /// entry.Password= "123456!" + /// entry = zip.AddFile("Report.xls") + /// entry.Password= "1Secret!" + /// zip.Save("EncryptedArchive.zip") + /// End Using + /// + /// + /// ' extract entries that use encryption + /// Using (zip as ZipFile = ZipFile.Read("EncryptedArchive.zip")) + /// Dim entry as ZipEntry + /// entry = zip("Declaration.txt") + /// entry.Password = "123456!" + /// entry.Extract("extractDir") + /// entry = zip("Report.xls") + /// entry.Password = "1Secret!" + /// entry.Extract("extractDir") + /// End Using + /// + /// + /// + /// + /// + /// + /// ZipFile.Password + public string Password + { + set + { + _Password = value; + if (_Password == null) + { + _Encryption = EncryptionAlgorithm.None; + } + else + { + // We're setting a non-null password. + + // For entries obtained from a zip file that are encrypted, we cannot + // simply restream (recompress, re-encrypt) the file data, because we + // need the old password in order to decrypt the data, and then we + // need the new password to encrypt. So, setting the password is + // never going to work on an entry that is stored encrypted in a zipfile. + + // But it is not en error to set the password, obviously: callers will + // set the password in order to Extract encrypted archives. + + // If the source is a zip archive and there was previously no encryption + // on the entry, then we must re-stream the entry in order to encrypt it. + if (this._Source == ZipEntrySource.ZipFile && !_sourceIsEncrypted) + _restreamRequiredOnSave = true; + + if (Encryption == EncryptionAlgorithm.None) + { + _Encryption = EncryptionAlgorithm.PkzipWeak; + } + } + } + private get { return _Password; } + } + + + + internal bool IsChanged + { + get + { + return _restreamRequiredOnSave | _metadataChanged; + } + } + + + /// + /// The action the library should take when extracting a file that already exists. + /// + /// + /// + /// + /// This property affects the behavior of the Extract methods (one of the + /// Extract() or ExtractWithPassword() overloads), when + /// extraction would would overwrite an existing filesystem file. If you do + /// not set this property, the library throws an exception when extracting + /// an entry would overwrite an existing file. + /// + /// + /// + /// This property has no effect when extracting to a stream, or when the file to be + /// extracted does not already exist. + /// + /// + /// + /// + /// + /// + /// This example shows how to set the ExtractExistingFile property in + /// an ExtractProgress event, in response to user input. The + /// ExtractProgress event is invoked if and only if the + /// ExtractExistingFile property was previously set to + /// ExtractExistingFileAction.InvokeExtractProgressEvent. + /// + /// public static void ExtractProgress(object sender, ExtractProgressEventArgs e) + /// { + /// if (e.EventType == ZipProgressEventType.Extracting_BeforeExtractEntry) + /// Console.WriteLine("extract {0} ", e.CurrentEntry.FileName); + /// + /// else if (e.EventType == ZipProgressEventType.Extracting_ExtractEntryWouldOverwrite) + /// { + /// ZipEntry entry = e.CurrentEntry; + /// string response = null; + /// // Ask the user if he wants overwrite the file + /// do + /// { + /// Console.Write("Overwrite {0} in {1} ? (y/n/C) ", entry.FileName, e.ExtractLocation); + /// response = Console.ReadLine(); + /// Console.WriteLine(); + /// + /// } while (response != null && response[0]!='Y' && + /// response[0]!='N' && response[0]!='C'); + /// + /// if (response[0]=='C') + /// e.Cancel = true; + /// else if (response[0]=='Y') + /// entry.ExtractExistingFile = ExtractExistingFileAction.OverwriteSilently; + /// else + /// entry.ExtractExistingFile= ExtractExistingFileAction.DoNotOverwrite; + /// } + /// } + /// + /// + public ExtractExistingFileAction ExtractExistingFile + { + get; + set; + } + + + /// + /// The action to take when an error is encountered while + /// opening or reading files as they are saved into a zip archive. + /// + /// + /// + /// + /// Errors can occur within a call to ZipFile.Save, as the various files contained + /// in a ZipFile are being saved into the zip archive. During the + /// Save, DotNetZip will perform a File.Open on the file + /// associated to the ZipEntry, and then will read the entire contents of + /// the file as it is zipped. Either the open or the Read may fail, because + /// of lock conflicts or other reasons. Using this property, you can + /// specify the action to take when such errors occur. + /// + /// + /// + /// Typically you will NOT set this property on individual ZipEntry + /// instances. Instead, you will set the ZipFile.ZipErrorAction property on + /// the ZipFile instance, before adding any entries to the + /// ZipFile. If you do this, errors encountered on behalf of any of + /// the entries in the ZipFile will be handled the same way. + /// + /// + /// + /// But, if you use a handler, you will want + /// to set this property on the ZipEntry within the handler, to + /// communicate back to DotNetZip what you would like to do with the + /// particular error. + /// + /// + /// + /// + /// + public ZipErrorAction ZipErrorAction + { + get; + set; + } + + + /// + /// Indicates whether the entry was included in the most recent save. + /// + /// + /// An entry can be excluded or skipped from a save if there is an error + /// opening or reading the entry. + /// + /// + public bool IncludedInMostRecentSave + { + get + { + return !_skippedDuringSave; + } + } + + + /// + /// A callback that allows the application to specify the compression to use + /// for a given entry that is about to be added to the zip archive. + /// + /// + /// + /// + /// See + /// + /// + public SetCompressionCallback SetCompression + { + get; + set; + } + + + + /// + /// Set to indicate whether to use UTF-8 encoding for filenames and comments. + /// + /// + /// + /// + /// + /// If this flag is set, the comment and filename for the entry will be + /// encoded with UTF-8, as described in the Zip + /// specification, if necessary. "Necessary" means, the filename or + /// entry comment (if any) cannot be reflexively encoded and decoded using the + /// default code page, IBM437. + /// + /// + /// + /// Setting this flag to true is equivalent to setting to System.Text.Encoding.UTF8. + /// + /// + /// + /// This flag has no effect or relation to the text encoding used within the + /// file itself. + /// + /// + /// + [Obsolete("Beginning with v1.9.1.6 of DotNetZip, this property is obsolete. It will be removed in a future version of the library. Your applications should use AlternateEncoding and AlternateEncodingUsage instead.")] + public bool UseUnicodeAsNecessary + { + get + { + return (AlternateEncoding == System.Text.Encoding.GetEncoding("UTF-8")) && + (AlternateEncodingUsage == ZipOption.AsNecessary); + } + set + { + if (value) + { + AlternateEncoding = System.Text.Encoding.GetEncoding("UTF-8"); + AlternateEncodingUsage = ZipOption.AsNecessary; + + } + else + { + AlternateEncoding = Ionic.Zip.ZipFile.DefaultEncoding; + AlternateEncodingUsage = ZipOption.Never; + } + } + } + + /// + /// The text encoding to use for the FileName and Comment on this ZipEntry, + /// when the default encoding is insufficient. + /// + /// + /// + /// + /// + /// Don't use this property. See . + /// + /// + /// + [Obsolete("This property is obsolete since v1.9.1.6. Use AlternateEncoding and AlternateEncodingUsage instead.", true)] + public System.Text.Encoding ProvisionalAlternateEncoding + { + get; set; + } + + /// + /// Specifies the alternate text encoding used by this ZipEntry + /// + /// + /// + /// The default text encoding used in Zip files for encoding filenames and + /// comments is IBM437, which is something like a superset of ASCII. In + /// cases where this is insufficient, applications can specify an + /// alternate encoding. + /// + /// + /// When creating a zip file, the usage of the alternate encoding is + /// governed by the property. + /// Typically you would set both properties to tell DotNetZip to employ an + /// encoding that is not IBM437 in the zipfile you are creating. + /// + /// + /// Keep in mind that because the ZIP specification states that the only + /// valid encodings to use are IBM437 and UTF-8, if you use something + /// other than that, then zip tools and libraries may not be able to + /// successfully read the zip archive you generate. + /// + /// + /// The zip specification states that applications should presume that + /// IBM437 is in use, except when a special bit is set, which indicates + /// UTF-8. There is no way to specify an arbitrary code page, within the + /// zip file itself. When you create a zip file encoded with gb2312 or + /// ibm861 or anything other than IBM437 or UTF-8, then the application + /// that reads the zip file needs to "know" which code page to use. In + /// some cases, the code page used when reading is chosen implicitly. For + /// example, WinRar uses the ambient code page for the host desktop + /// operating system. The pitfall here is that if you create a zip in + /// Copenhagen and send it to Tokyo, the reader of the zipfile may not be + /// able to decode successfully. + /// + /// + /// + /// This example shows how to create a zipfile encoded with a + /// language-specific encoding: + /// + /// using (var zip = new ZipFile()) + /// { + /// zip.AlternateEnoding = System.Text.Encoding.GetEncoding("ibm861"); + /// zip.AlternateEnodingUsage = ZipOption.Always; + /// zip.AddFileS(arrayOfFiles); + /// zip.Save("Myarchive-Encoded-in-IBM861.zip"); + /// } + /// + /// + /// + public System.Text.Encoding AlternateEncoding + { + get; set; + } + + + /// + /// Describes if and when this instance should apply + /// AlternateEncoding to encode the FileName and Comment, when + /// saving. + /// + /// + public ZipOption AlternateEncodingUsage + { + get; set; + } + + + // /// + // /// The text encoding actually used for this ZipEntry. + // /// + // /// + // /// + // /// + // /// + // /// This read-only property describes the encoding used by the + // /// ZipEntry. If the entry has been read in from an existing ZipFile, + // /// then it may take the value UTF-8, if the entry is coded to specify UTF-8. + // /// If the entry does not specify UTF-8, the typical case, then the encoding + // /// used is whatever the application specified in the call to + // /// ZipFile.Read(). If the application has used one of the overloads of + // /// ZipFile.Read() that does not accept an encoding parameter, then the + // /// encoding used is IBM437, which is the default encoding described in the + // /// ZIP specification. + // /// + // /// + // /// If the entry is being created, then the value of ActualEncoding is taken + // /// according to the logic described in the documentation for . + // /// + // /// + // /// An application might be interested in retrieving this property to see if + // /// an entry read in from a file has used Unicode (UTF-8). + // /// + // /// + // /// + // /// + // public System.Text.Encoding ActualEncoding + // { + // get + // { + // return _actualEncoding; + // } + // } + + + + + internal static string NameInArchive(String filename, string directoryPathInArchive) + { + string result = null; + if (directoryPathInArchive == null) + result = filename; + + else + { + if (String.IsNullOrEmpty(directoryPathInArchive)) + { + result = Path.GetFileName(filename); + } + else + { + // explicitly specify a pathname for this file + result = Path.Combine(directoryPathInArchive, Path.GetFileName(filename)); + } + } + + //result = Path.GetFullPath(result); + result = SharedUtilities.NormalizePathForUseInZipFile(result); + + return result; + } + + // workitem 9073 + internal static ZipEntry CreateFromNothing(String nameInArchive) + { + return Create(nameInArchive, ZipEntrySource.None, null, null); + } + + internal static ZipEntry CreateFromFile(String filename, string nameInArchive) + { + return Create(nameInArchive, ZipEntrySource.FileSystem, filename, null); + } + + internal static ZipEntry CreateForStream(String entryName, Stream s) + { + return Create(entryName, ZipEntrySource.Stream, s, null); + } + + internal static ZipEntry CreateForWriter(String entryName, WriteDelegate d) + { + return Create(entryName, ZipEntrySource.WriteDelegate, d, null); + } + + internal static ZipEntry CreateForJitStreamProvider(string nameInArchive, OpenDelegate opener, CloseDelegate closer) + { + return Create(nameInArchive, ZipEntrySource.JitStream, opener, closer); + } + + internal static ZipEntry CreateForZipOutputStream(string nameInArchive) + { + return Create(nameInArchive, ZipEntrySource.ZipOutputStream, null, null); + } + + + private static ZipEntry Create(string nameInArchive, ZipEntrySource source, Object arg1, Object arg2) + { + if (String.IsNullOrEmpty(nameInArchive)) + throw new Ionic.Zip.ZipException("The entry name must be non-null and non-empty."); + + ZipEntry entry = new ZipEntry(); + + // workitem 7071 + // workitem 7926 - "version made by" OS should be zero for compat with WinZip + entry._VersionMadeBy = (0 << 8) + 45; // indicates the attributes are FAT Attributes, and v4.5 of the spec + entry._Source = source; + entry._Mtime = entry._Atime = entry._Ctime = DateTime.UtcNow; + + if (source == ZipEntrySource.Stream) + { + entry._sourceStream = (arg1 as Stream); // may or may not be null + } + else if (source == ZipEntrySource.WriteDelegate) + { + entry._WriteDelegate = (arg1 as WriteDelegate); // may or may not be null + } + else if (source == ZipEntrySource.JitStream) + { + entry._OpenDelegate = (arg1 as OpenDelegate); // may or may not be null + entry._CloseDelegate = (arg2 as CloseDelegate); // may or may not be null + } + else if (source == ZipEntrySource.ZipOutputStream) + { + } + // workitem 9073 + else if (source == ZipEntrySource.None) + { + // make this a valid value, for later. + entry._Source = ZipEntrySource.FileSystem; + } + else + { + String filename = (arg1 as String); // must not be null + + if (String.IsNullOrEmpty(filename)) + throw new Ionic.Zip.ZipException("The filename must be non-null and non-empty."); + + try + { + // The named file may or may not exist at this time. For + // example, when adding a directory by name. We test existence + // when necessary: when saving the ZipFile, or when getting the + // attributes, and so on. + +#if NETCF + // workitem 6878 + // Ionic.Zip.SharedUtilities.AdjustTime_Win32ToDotNet + entry._Mtime = File.GetLastWriteTime(filename).ToUniversalTime(); + entry._Ctime = File.GetCreationTime(filename).ToUniversalTime(); + entry._Atime = File.GetLastAccessTime(filename).ToUniversalTime(); + + // workitem 7071 + // can only get attributes of files that exist. + if (File.Exists(filename) || Directory.Exists(filename)) + entry._ExternalFileAttrs = (int)NetCfFile.GetAttributes(filename); + +#elif SILVERLIGHT + entry._Mtime = + entry._Ctime = + entry._Atime = System.DateTime.UtcNow; + entry._ExternalFileAttrs = (int)0; +#else + // workitem 6878?? + entry._Mtime = File.GetLastWriteTime(filename).ToUniversalTime(); + entry._Ctime = File.GetCreationTime(filename).ToUniversalTime(); + entry._Atime = File.GetLastAccessTime(filename).ToUniversalTime(); + + // workitem 7071 + // can only get attributes on files that exist. + if (File.Exists(filename) || Directory.Exists(filename)) + entry._ExternalFileAttrs = (int)File.GetAttributes(filename); + +#endif + entry._ntfsTimesAreSet = true; + + entry._LocalFileName = Path.GetFullPath(filename); // workitem 8813 + + } + catch (System.IO.PathTooLongException ptle) + { + // workitem 14035 + var msg = String.Format("The path is too long, filename={0}", + filename); + throw new ZipException(msg, ptle); + } + + } + + entry._LastModified = entry._Mtime; + entry._FileNameInArchive = SharedUtilities.NormalizePathForUseInZipFile(nameInArchive); + // We don't actually slurp in the file data until the caller invokes Write on this entry. + + return entry; + } + + + + + internal void MarkAsDirectory() + { + _IsDirectory = true; + // workitem 6279 + if (!_FileNameInArchive.EndsWith("/")) + _FileNameInArchive += "/"; + } + + + + /// + /// Indicates whether an entry is marked as a text file. Be careful when + /// using on this property. Unless you have a good reason, you should + /// probably ignore this property. + /// + /// + /// + /// + /// The ZIP format includes a provision for specifying whether an entry in + /// the zip archive is a text or binary file. This property exposes that + /// metadata item. Be careful when using this property: It's not clear + /// that this property as a firm meaning, across tools and libraries. + /// + /// + /// + /// To be clear, when reading a zip file, the property value may or may + /// not be set, and its value may or may not be valid. Not all entries + /// that you may think of as "text" entries will be so marked, and entries + /// marked as "text" are not guaranteed in any way to be text entries. + /// Whether the value is set and set correctly depends entirely on the + /// application that produced the zip file. + /// + /// + /// + /// There are many zip tools available, and when creating zip files, some + /// of them "respect" the IsText metadata field, and some of them do not. + /// Unfortunately, even when an application tries to do "the right thing", + /// it's not always clear what "the right thing" is. + /// + /// + /// + /// There's no firm definition of just what it means to be "a text file", + /// and the zip specification does not help in this regard. Twenty years + /// ago, text was ASCII, each byte was less than 127. IsText meant, all + /// bytes in the file were less than 127. These days, it is not the case + /// that all text files have all bytes less than 127. Any unicode file + /// may have bytes that are above 0x7f. The zip specification has nothing + /// to say on this topic. Therefore, it's not clear what IsText really + /// means. + /// + /// + /// + /// This property merely tells a reading application what is stored in the + /// metadata for an entry, without guaranteeing its validity or its + /// meaning. + /// + /// + /// + /// When DotNetZip is used to create a zipfile, it attempts to set this + /// field "correctly." For example, if a file ends in ".txt", this field + /// will be set. Your application may override that default setting. When + /// writing a zip file, you must set the property before calling + /// Save() on the ZipFile. + /// + /// + /// + /// When reading a zip file, a more general way to decide just what kind + /// of file is contained in a particular entry is to use the file type + /// database stored in the operating system. The operating system stores + /// a table that says, a file with .jpg extension is a JPG image file, a + /// file with a .xml extension is an XML document, a file with a .txt is a + /// pure ASCII text document, and so on. To get this information on + /// Windows, you + /// need to read and parse the registry. + /// + /// + /// + /// + /// using (var zip = new ZipFile()) + /// { + /// var e = zip.UpdateFile("Descriptions.mme", ""); + /// e.IsText = true; + /// zip.Save(zipPath); + /// } + /// + /// + /// + /// Using zip As New ZipFile + /// Dim e2 as ZipEntry = zip.AddFile("Descriptions.mme", "") + /// e.IsText= True + /// zip.Save(zipPath) + /// End Using + /// + /// + public bool IsText + { + // workitem 7801 + get { return _IsText; } + set { _IsText = value; } + } + + + + /// Provides a string representation of the instance. + /// a string representation of the instance. + public override String ToString() + { + return String.Format("ZipEntry::{0}", FileName); + } + + + internal Stream ArchiveStream + { + get + { + if (_archiveStream == null) + { + if (_container.ZipFile != null) + { + var zf = _container.ZipFile; + zf.Reset(false); + _archiveStream = zf.StreamForDiskNumber(_diskNumber); + } + else + { + _archiveStream = _container.ZipOutputStream.OutputStream; + } + } + return _archiveStream; + } + } + + + private void SetFdpLoh() + { + // The value for FileDataPosition has not yet been set. + // Therefore, seek to the local header, and figure the start of file data. + // workitem 8098: ok (restore) + long origPosition = this.ArchiveStream.Position; + try + { + this.ArchiveStream.Seek(this._RelativeOffsetOfLocalHeader, SeekOrigin.Begin); + + // workitem 10178 + Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(this.ArchiveStream); + } + catch (System.IO.IOException exc1) + { + string description = String.Format("Exception seeking entry({0}) offset(0x{1:X8}) len(0x{2:X8})", + this.FileName, this._RelativeOffsetOfLocalHeader, + this.ArchiveStream.Length); + throw new BadStateException(description, exc1); + } + + byte[] block = new byte[30]; + this.ArchiveStream.Read(block, 0, block.Length); + + // At this point we could verify the contents read from the local header + // with the contents read from the central header. We could, but don't need to. + // So we won't. + + Int16 filenameLength = (short)(block[26] + block[27] * 256); + Int16 extraFieldLength = (short)(block[28] + block[29] * 256); + + // Console.WriteLine(" pos 0x{0:X8} ({0})", this.ArchiveStream.Position); + // Console.WriteLine(" seek 0x{0:X8} ({0})", filenameLength + extraFieldLength); + + this.ArchiveStream.Seek(filenameLength + extraFieldLength, SeekOrigin.Current); + // workitem 10178 + Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(this.ArchiveStream); + + this._LengthOfHeader = 30 + extraFieldLength + filenameLength + + GetLengthOfCryptoHeaderBytes(_Encryption_FromZipFile); + + // Console.WriteLine(" ROLH 0x{0:X8} ({0})", _RelativeOffsetOfLocalHeader); + // Console.WriteLine(" LOH 0x{0:X8} ({0})", _LengthOfHeader); + // workitem 8098: ok (arithmetic) + this.__FileDataPosition = _RelativeOffsetOfLocalHeader + _LengthOfHeader; + // Console.WriteLine(" FDP 0x{0:X8} ({0})", __FileDataPosition); + + // restore file position: + // workitem 8098: ok (restore) + this.ArchiveStream.Seek(origPosition, SeekOrigin.Begin); + // workitem 10178 + Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(this.ArchiveStream); + } + + + +#if AESCRYPTO + private static int GetKeyStrengthInBits(EncryptionAlgorithm a) + { + if (a == EncryptionAlgorithm.WinZipAes256) return 256; + else if (a == EncryptionAlgorithm.WinZipAes128) return 128; + return -1; + } +#endif + + internal static int GetLengthOfCryptoHeaderBytes(EncryptionAlgorithm a) + { + //if ((_BitField & 0x01) != 0x01) return 0; + if (a == EncryptionAlgorithm.None) return 0; + +#if AESCRYPTO + if (a == EncryptionAlgorithm.WinZipAes128 || + a == EncryptionAlgorithm.WinZipAes256) + { + int KeyStrengthInBits = GetKeyStrengthInBits(a); + int sizeOfSaltAndPv = ((KeyStrengthInBits / 8 / 2) + 2); + return sizeOfSaltAndPv; + } +#endif + if (a == EncryptionAlgorithm.PkzipWeak) + return 12; + throw new ZipException("internal error"); + } + + + internal long FileDataPosition + { + get + { + if (__FileDataPosition == -1) + SetFdpLoh(); + + return __FileDataPosition; + } + } + + private int LengthOfHeader + { + get + { + if (_LengthOfHeader == 0) + SetFdpLoh(); + + return _LengthOfHeader; + } + } + + + + private ZipCrypto _zipCrypto_forExtract; + private ZipCrypto _zipCrypto_forWrite; +#if AESCRYPTO + private WinZipAesCrypto _aesCrypto_forExtract; + private WinZipAesCrypto _aesCrypto_forWrite; + private Int16 _WinZipAesMethod; +#endif + + internal DateTime _LastModified; + private DateTime _Mtime, _Atime, _Ctime; // workitem 6878: NTFS quantities + private bool _ntfsTimesAreSet; + private bool _emitNtfsTimes = true; + private bool _emitUnixTimes; // by default, false + private bool _TrimVolumeFromFullyQualifiedPaths = true; // by default, trim them. + internal string _LocalFileName; + private string _FileNameInArchive; + internal Int16 _VersionNeeded; + internal Int16 _BitField; + internal Int16 _CompressionMethod; + private Int16 _CompressionMethod_FromZipFile; + private Ionic.Zlib.CompressionLevel _CompressionLevel; + internal string _Comment; + private bool _IsDirectory; + private byte[] _CommentBytes; + internal Int64 _CompressedSize; + internal Int64 _CompressedFileDataSize; // CompressedSize less 12 bytes for the encryption header, if any + internal Int64 _UncompressedSize; + internal Int32 _TimeBlob; + private bool _crcCalculated; + internal Int32 _Crc32; + internal byte[] _Extra; + private bool _metadataChanged; + private bool _restreamRequiredOnSave; + private bool _sourceIsEncrypted; + private bool _skippedDuringSave; + private UInt32 _diskNumber; + + private static System.Text.Encoding ibm437 = System.Text.Encoding.GetEncoding("IBM437"); + //private System.Text.Encoding _provisionalAlternateEncoding = System.Text.Encoding.GetEncoding("IBM437"); + private System.Text.Encoding _actualEncoding; + + internal ZipContainer _container; + + private long __FileDataPosition = -1; + private byte[] _EntryHeader; + internal Int64 _RelativeOffsetOfLocalHeader; + private Int64 _future_ROLH; + private Int64 _TotalEntrySize; + private int _LengthOfHeader; + private int _LengthOfTrailer; + internal bool _InputUsesZip64; + private UInt32 _UnsupportedAlgorithmId; + + internal string _Password; + internal ZipEntrySource _Source; + internal EncryptionAlgorithm _Encryption; + internal EncryptionAlgorithm _Encryption_FromZipFile; + internal byte[] _WeakEncryptionHeader; + internal Stream _archiveStream; + private Stream _sourceStream; + private Nullable _sourceStreamOriginalPosition; + private bool _sourceWasJitProvided; + private bool _ioOperationCanceled; + private bool _presumeZip64; + private Nullable _entryRequiresZip64; + private Nullable _OutputUsesZip64; + private bool _IsText; // workitem 7801 + private ZipEntryTimestamp _timestamp; + + private static System.DateTime _unixEpoch = new System.DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); + private static System.DateTime _win32Epoch = System.DateTime.FromFileTimeUtc(0L); + private static System.DateTime _zeroHour = new System.DateTime(1, 1, 1, 0, 0, 0, DateTimeKind.Utc); + + private WriteDelegate _WriteDelegate; + private OpenDelegate _OpenDelegate; + private CloseDelegate _CloseDelegate; + + + // summary + // The default size of the IO buffer for ZipEntry instances. Currently it is 8192 bytes. + // summary + //public const int IO_BUFFER_SIZE_DEFAULT = 8192; // 0x8000; // 0x4400 + + } + + + + /// + /// An enum that specifies the type of timestamp available on the ZipEntry. + /// + /// + /// + /// + /// + /// The last modified time of a file can be stored in multiple ways in + /// a zip file, and they are not mutually exclusive: + /// + /// + /// + /// + /// In the so-called "DOS" format, which has a 2-second precision. Values + /// are rounded to the nearest even second. For example, if the time on the + /// file is 12:34:43, then it will be stored as 12:34:44. This first value + /// is accessible via the LastModified property. This value is always + /// present in the metadata for each zip entry. In some cases the value is + /// invalid, or zero. + /// + /// + /// + /// In the so-called "Windows" or "NTFS" format, as an 8-byte integer + /// quantity expressed as the number of 1/10 milliseconds (in other words + /// the number of 100 nanosecond units) since January 1, 1601 (UTC). This + /// format is how Windows represents file times. This time is accessible + /// via the ModifiedTime property. + /// + /// + /// + /// In the "Unix" format, a 4-byte quantity specifying the number of seconds since + /// January 1, 1970 UTC. + /// + /// + /// + /// In an older format, now deprecated but still used by some current + /// tools. This format is also a 4-byte quantity specifying the number of + /// seconds since January 1, 1970 UTC. + /// + /// + /// + /// + /// + /// This bit field describes which of the formats were found in a ZipEntry that was read. + /// + /// + /// + [Flags] + public enum ZipEntryTimestamp + { + /// + /// Default value. + /// + None = 0, + + /// + /// A DOS timestamp with 2-second precision. + /// + DOS = 1, + + /// + /// A Windows timestamp with 100-ns precision. + /// + Windows = 2, + + /// + /// A Unix timestamp with 1-second precision. + /// + Unix = 4, + + /// + /// A Unix timestamp with 1-second precision, stored in InfoZip v1 format. This + /// format is outdated and is supported for reading archives only. + /// + InfoZip1 = 8, + } + + + + /// + /// The method of compression to use for a particular ZipEntry. + /// + /// + /// + /// PKWare's + /// ZIP Specification describes a number of distinct + /// cmopression methods that can be used within a zip + /// file. DotNetZip supports a subset of them. + /// + public enum CompressionMethod + { + /// + /// No compression at all. For COM environments, the value is 0 (zero). + /// + None = 0, + + /// + /// DEFLATE compression, as described in IETF RFC + /// 1951. This is the "normal" compression used in zip + /// files. For COM environments, the value is 8. + /// + Deflate = 8, + +#if BZIP + /// + /// BZip2 compression, a compression algorithm developed by Julian Seward. + /// For COM environments, the value is 12. + /// + BZip2 = 12, +#endif + } + + +#if NETCF + internal class NetCfFile + { + public static int SetTimes(string filename, DateTime ctime, DateTime atime, DateTime mtime) + { + IntPtr hFile = (IntPtr) CreateFileCE(filename, + (uint)0x40000000L, // (uint)FileAccess.Write, + (uint)0x00000002L, // (uint)FileShare.Write, + 0, + (uint) 3, // == open existing + (uint)0, // flagsAndAttributes + 0); + + if((int)hFile == -1) + { + // workitem 7944: don't throw on failure to set file times + // throw new ZipException("CreateFileCE Failed"); + return Interop.Marshal.GetLastWin32Error(); + } + + SetFileTime(hFile, + BitConverter.GetBytes(ctime.ToFileTime()), + BitConverter.GetBytes(atime.ToFileTime()), + BitConverter.GetBytes(mtime.ToFileTime())); + + CloseHandle(hFile); + return 0; + } + + + public static int SetLastWriteTime(string filename, DateTime mtime) + { + IntPtr hFile = (IntPtr) CreateFileCE(filename, + (uint)0x40000000L, // (uint)FileAccess.Write, + (uint)0x00000002L, // (uint)FileShare.Write, + 0, + (uint) 3, // == open existing + (uint)0, // flagsAndAttributes + 0); + + if((int)hFile == -1) + { + // workitem 7944: don't throw on failure to set file time + // throw new ZipException(String.Format("CreateFileCE Failed ({0})", + // Interop.Marshal.GetLastWin32Error())); + return Interop.Marshal.GetLastWin32Error(); + } + + SetFileTime(hFile, null, null, + BitConverter.GetBytes(mtime.ToFileTime())); + + CloseHandle(hFile); + return 0; + } + + + [Interop.DllImport("coredll.dll", EntryPoint="CreateFile", SetLastError=true)] + internal static extern int CreateFileCE(string lpFileName, + uint dwDesiredAccess, + uint dwShareMode, + int lpSecurityAttributes, + uint dwCreationDisposition, + uint dwFlagsAndAttributes, + int hTemplateFile); + + + [Interop.DllImport("coredll", EntryPoint="GetFileAttributes", SetLastError=true)] + internal static extern uint GetAttributes(string lpFileName); + + [Interop.DllImport("coredll", EntryPoint="SetFileAttributes", SetLastError=true)] + internal static extern bool SetAttributes(string lpFileName, uint dwFileAttributes); + + [Interop.DllImport("coredll", EntryPoint="SetFileTime", SetLastError=true)] + internal static extern bool SetFileTime(IntPtr hFile, byte[] lpCreationTime, byte[] lpLastAccessTime, byte[] lpLastWriteTime); + + [Interop.DllImport("coredll.dll", SetLastError=true)] + internal static extern bool CloseHandle(IntPtr hObject); + + } +#endif + + + +} diff --git a/DotNetZip/Zip/ZipEntrySource.cs b/DotNetZip/Zip/ZipEntrySource.cs new file mode 100644 index 0000000..b64380d --- /dev/null +++ b/DotNetZip/Zip/ZipEntrySource.cs @@ -0,0 +1,69 @@ +// ZipEntrySource.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2009 Dino Chiesa +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2009-November-19 11:18:42> +// +// ------------------------------------------------------------------ +// + +namespace Ionic.Zip +{ + /// + /// An enum that specifies the source of the ZipEntry. + /// + public enum ZipEntrySource + { + /// + /// Default value. Invalid on a bonafide ZipEntry. + /// + None = 0, + + /// + /// The entry was instantiated by calling AddFile() or another method that + /// added an entry from the filesystem. + /// + FileSystem, + + /// + /// The entry was instantiated via or + /// . + /// + Stream, + + /// + /// The ZipEntry was instantiated by reading a zipfile. + /// + ZipFile, + + /// + /// The content for the ZipEntry will be or was provided by the WriteDelegate. + /// + WriteDelegate, + + /// + /// The content for the ZipEntry will be obtained from the stream dispensed by the OpenDelegate. + /// The entry was instantiated via . + /// + JitStream, + + /// + /// The content for the ZipEntry will be or was obtained from a ZipOutputStream. + /// + ZipOutputStream, + } + +} \ No newline at end of file diff --git a/DotNetZip/Zip/ZipErrorAction.cs b/DotNetZip/Zip/ZipErrorAction.cs new file mode 100644 index 0000000..3a394cd --- /dev/null +++ b/DotNetZip/Zip/ZipErrorAction.cs @@ -0,0 +1,97 @@ +// ZipErrorAction.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2009 Dino Chiesa +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2009-September-01 18:43:20> +// +// ------------------------------------------------------------------ +// +// This module defines the ZipErrorAction enum, which provides +// an action to take when errors occur when opening or reading +// files to be added to a zip file. +// +// ------------------------------------------------------------------ + + +namespace Ionic.Zip +{ + /// + /// An enum providing the options when an error occurs during opening or reading + /// of a file or directory that is being saved to a zip file. + /// + /// + /// + /// + /// This enum describes the actions that the library can take when an error occurs + /// opening or reading a file, as it is being saved into a Zip archive. + /// + /// + /// + /// In some cases an error will occur when DotNetZip tries to open a file to be + /// added to the zip archive. In other cases, an error might occur after the + /// file has been successfully opened, while DotNetZip is reading the file. + /// + /// + /// + /// The first problem might occur when calling AddDirectory() on a directory + /// that contains a Clipper .dbf file; the file is locked by Clipper and + /// cannot be opened by another process. An example of the second problem is + /// the ERROR_LOCK_VIOLATION that results when a file is opened by another + /// process, but not locked, and a range lock has been taken on the file. + /// Microsoft Outlook takes range locks on .PST files. + /// + /// + public enum ZipErrorAction + { + /// + /// Throw an exception when an error occurs while zipping. This is the default + /// behavior. (For COM clients, this is a 0 (zero).) + /// + Throw, + + /// + /// When an error occurs during zipping, for example a file cannot be opened, + /// skip the file causing the error, and continue zipping. (For COM clients, + /// this is a 1.) + /// + Skip, + + /// + /// When an error occurs during zipping, for example a file cannot be opened, + /// retry the operation that caused the error. Be careful with this option. If + /// the error is not temporary, the library will retry forever. (For COM + /// clients, this is a 2.) + /// + Retry, + + /// + /// When an error occurs, invoke the zipError event. The event type used is + /// . A typical use of this option: + /// a GUI application may wish to pop up a dialog to allow the user to view the + /// error that occurred, and choose an appropriate action. After your + /// processing in the error event, if you want to skip the file, set on the + /// ZipProgressEventArgs.CurrentEntry to Skip. If you want the + /// exception to be thrown, set ZipErrorAction on the CurrentEntry + /// to Throw. If you want to cancel the zip, set + /// ZipProgressEventArgs.Cancel to true. Cancelling differs from using + /// Skip in that a cancel will not save any further entries, if there are any. + /// (For COM clients, the value of this enum is a 3.) + /// + InvokeErrorEvent, + } + +} diff --git a/DotNetZip/Zip/ZipFile.AddUpdate.cs b/DotNetZip/Zip/ZipFile.AddUpdate.cs new file mode 100644 index 0000000..d5da4fc --- /dev/null +++ b/DotNetZip/Zip/ZipFile.AddUpdate.cs @@ -0,0 +1,2182 @@ +// ZipFile.AddUpdate.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2009-2011 Dino Chiesa. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2011-November-01 13:56:58> +// +// ------------------------------------------------------------------ +// +// This module defines the methods for Adding and Updating entries in +// the ZipFile. +// +// ------------------------------------------------------------------ +// + + +using System; +using System.IO; +using System.Collections.Generic; + +namespace Ionic.Zip +{ + public partial class ZipFile + { + /// + /// Adds an item, either a file or a directory, to a zip file archive. + /// + /// + /// + /// + /// This method is handy if you are adding things to zip archive and don't + /// want to bother distinguishing between directories or files. Any files are + /// added as single entries. A directory added through this method is added + /// recursively: all files and subdirectories contained within the directory + /// are added to the ZipFile. + /// + /// + /// + /// The name of the item may be a relative path or a fully-qualified + /// path. Remember, the items contained in ZipFile instance get written + /// to the disk only when you call or a similar + /// save method. + /// + /// + /// + /// The directory name used for the file within the archive is the same + /// as the directory name (potentially a relative path) specified in the + /// . + /// + /// + /// + /// For ZipFile properties including , , , , , + /// , and , their + /// respective values at the time of this call will be applied to the + /// ZipEntry added. + /// + /// + /// + /// + /// + /// + /// + /// + /// This method has two overloads. + /// + /// the name of the file or directory to add. + /// + /// The ZipEntry added. + public ZipEntry AddItem(string fileOrDirectoryName) + { + return AddItem(fileOrDirectoryName, null); + } + + + /// + /// Adds an item, either a file or a directory, to a zip file archive, + /// explicitly specifying the directory path to be used in the archive. + /// + /// + /// + /// + /// If adding a directory, the add is recursive on all files and + /// subdirectories contained within it. + /// + /// + /// The name of the item may be a relative path or a fully-qualified path. + /// The item added by this call to the ZipFile is not read from the + /// disk nor written to the zip file archive until the application calls + /// Save() on the ZipFile. + /// + /// + /// + /// This version of the method allows the caller to explicitly specify the + /// directory path to be used in the archive, which would override the + /// "natural" path of the filesystem file. + /// + /// + /// + /// Encryption will be used on the file data if the Password has + /// been set on the ZipFile object, prior to calling this method. + /// + /// + /// + /// For ZipFile properties including , , , , , + /// , and , their + /// respective values at the time of this call will be applied to the + /// ZipEntry added. + /// + /// + /// + /// + /// + /// Thrown if the file or directory passed in does not exist. + /// + /// + /// the name of the file or directory to add. + /// + /// + /// + /// The name of the directory path to use within the zip archive. This path + /// need not refer to an extant directory in the current filesystem. If the + /// files within the zip are later extracted, this is the path used for the + /// extracted file. Passing null (Nothing in VB) will use the + /// path on the fileOrDirectoryName. Passing the empty string ("") will + /// insert the item at the root path within the archive. + /// + /// + /// + /// + /// + /// + /// + /// This example shows how to zip up a set of files into a flat hierarchy, + /// regardless of where in the filesystem the files originated. The resulting + /// zip archive will contain a toplevel directory named "flat", which itself + /// will contain files Readme.txt, MyProposal.docx, and Image1.jpg. A + /// subdirectory under "flat" called SupportFiles will contain all the files + /// in the "c:\SupportFiles" directory on disk. + /// + /// + /// String[] itemnames= { + /// "c:\\fixedContent\\Readme.txt", + /// "MyProposal.docx", + /// "c:\\SupportFiles", // a directory + /// "images\\Image1.jpg" + /// }; + /// + /// try + /// { + /// using (ZipFile zip = new ZipFile()) + /// { + /// for (int i = 1; i < itemnames.Length; i++) + /// { + /// // will add Files or Dirs, recurses and flattens subdirectories + /// zip.AddItem(itemnames[i],"flat"); + /// } + /// zip.Save(ZipToCreate); + /// } + /// } + /// catch (System.Exception ex1) + /// { + /// System.Console.Error.WriteLine("exception: {0}", ex1); + /// } + /// + /// + /// + /// Dim itemnames As String() = _ + /// New String() { "c:\fixedContent\Readme.txt", _ + /// "MyProposal.docx", _ + /// "SupportFiles", _ + /// "images\Image1.jpg" } + /// Try + /// Using zip As New ZipFile + /// Dim i As Integer + /// For i = 1 To itemnames.Length - 1 + /// ' will add Files or Dirs, recursing and flattening subdirectories. + /// zip.AddItem(itemnames(i), "flat") + /// Next i + /// zip.Save(ZipToCreate) + /// End Using + /// Catch ex1 As Exception + /// Console.Error.WriteLine("exception: {0}", ex1.ToString()) + /// End Try + /// + /// + /// The ZipEntry added. + public ZipEntry AddItem(String fileOrDirectoryName, String directoryPathInArchive) + { + if (File.Exists(fileOrDirectoryName)) + return AddFile(fileOrDirectoryName, directoryPathInArchive); + + if (Directory.Exists(fileOrDirectoryName)) + return AddDirectory(fileOrDirectoryName, directoryPathInArchive); + + throw new FileNotFoundException(String.Format("That file or directory ({0}) does not exist!", + fileOrDirectoryName)); + } + + /// + /// Adds a File to a Zip file archive. + /// + /// + /// + /// + /// This call collects metadata for the named file in the filesystem, + /// including the file attributes and the timestamp, and inserts that metadata + /// into the resulting ZipEntry. Only when the application calls Save() on + /// the ZipFile, does DotNetZip read the file from the filesystem and + /// then write the content to the zip file archive. + /// + /// + /// + /// This method will throw an exception if an entry with the same name already + /// exists in the ZipFile. + /// + /// + /// + /// For ZipFile properties including , , , , , + /// , and , their + /// respective values at the time of this call will be applied to the + /// ZipEntry added. + /// + /// + /// + /// + /// + /// + /// In this example, three files are added to a Zip archive. The ReadMe.txt + /// file will be placed in the root of the archive. The .png file will be + /// placed in a folder within the zip called photos\personal. The pdf file + /// will be included into a folder within the zip called Desktop. + /// + /// + /// try + /// { + /// using (ZipFile zip = new ZipFile()) + /// { + /// zip.AddFile("c:\\photos\\personal\\7440-N49th.png"); + /// zip.AddFile("c:\\Desktop\\2008-Regional-Sales-Report.pdf"); + /// zip.AddFile("ReadMe.txt"); + /// + /// zip.Save("Package.zip"); + /// } + /// } + /// catch (System.Exception ex1) + /// { + /// System.Console.Error.WriteLine("exception: " + ex1); + /// } + /// + /// + /// + /// Try + /// Using zip As ZipFile = New ZipFile + /// zip.AddFile("c:\photos\personal\7440-N49th.png") + /// zip.AddFile("c:\Desktop\2008-Regional-Sales-Report.pdf") + /// zip.AddFile("ReadMe.txt") + /// zip.Save("Package.zip") + /// End Using + /// Catch ex1 As Exception + /// Console.Error.WriteLine("exception: {0}", ex1.ToString) + /// End Try + /// + /// + /// + /// This method has two overloads. + /// + /// + /// + /// + /// + /// + /// The name of the file to add. It should refer to a file in the filesystem. + /// The name of the file may be a relative path or a fully-qualified path. + /// + /// The ZipEntry corresponding to the File added. + public ZipEntry AddFile(string fileName) + { + return AddFile(fileName, null); + } + + + + + + /// + /// Adds a File to a Zip file archive, potentially overriding the path to be + /// used within the zip archive. + /// + /// + /// + /// + /// The file added by this call to the ZipFile is not written to the + /// zip file archive until the application calls Save() on the ZipFile. + /// + /// + /// + /// This method will throw an exception if an entry with the same name already + /// exists in the ZipFile. + /// + /// + /// + /// This version of the method allows the caller to explicitly specify the + /// directory path to be used in the archive. + /// + /// + /// + /// For ZipFile properties including , , , , , + /// , and , their + /// respective values at the time of this call will be applied to the + /// ZipEntry added. + /// + /// + /// + /// + /// + /// + /// In this example, three files are added to a Zip archive. The ReadMe.txt + /// file will be placed in the root of the archive. The .png file will be + /// placed in a folder within the zip called images. The pdf file will be + /// included into a folder within the zip called files\docs, and will be + /// encrypted with the given password. + /// + /// + /// try + /// { + /// using (ZipFile zip = new ZipFile()) + /// { + /// // the following entry will be inserted at the root in the archive. + /// zip.AddFile("c:\\datafiles\\ReadMe.txt", ""); + /// // this image file will be inserted into the "images" directory in the archive. + /// zip.AddFile("c:\\photos\\personal\\7440-N49th.png", "images"); + /// // the following will result in a password-protected file called + /// // files\\docs\\2008-Regional-Sales-Report.pdf in the archive. + /// zip.Password = "EncryptMe!"; + /// zip.AddFile("c:\\Desktop\\2008-Regional-Sales-Report.pdf", "files\\docs"); + /// zip.Save("Archive.zip"); + /// } + /// } + /// catch (System.Exception ex1) + /// { + /// System.Console.Error.WriteLine("exception: {0}", ex1); + /// } + /// + /// + /// + /// Try + /// Using zip As ZipFile = New ZipFile + /// ' the following entry will be inserted at the root in the archive. + /// zip.AddFile("c:\datafiles\ReadMe.txt", "") + /// ' this image file will be inserted into the "images" directory in the archive. + /// zip.AddFile("c:\photos\personal\7440-N49th.png", "images") + /// ' the following will result in a password-protected file called + /// ' files\\docs\\2008-Regional-Sales-Report.pdf in the archive. + /// zip.Password = "EncryptMe!" + /// zip.AddFile("c:\Desktop\2008-Regional-Sales-Report.pdf", "files\documents") + /// zip.Save("Archive.zip") + /// End Using + /// Catch ex1 As Exception + /// Console.Error.WriteLine("exception: {0}", ex1) + /// End Try + /// + /// + /// + /// + /// + /// + /// + /// + /// The name of the file to add. The name of the file may be a relative path + /// or a fully-qualified path. + /// + /// + /// + /// Specifies a directory path to use to override any path in the fileName. + /// This path may, or may not, correspond to a real directory in the current + /// filesystem. If the files within the zip are later extracted, this is the + /// path used for the extracted file. Passing null (Nothing in + /// VB) will use the path on the fileName, if any. Passing the empty string + /// ("") will insert the item at the root path within the archive. + /// + /// + /// The ZipEntry corresponding to the file added. + public ZipEntry AddFile(string fileName, String directoryPathInArchive) + { + string nameInArchive = ZipEntry.NameInArchive(fileName, directoryPathInArchive); + ZipEntry ze = ZipEntry.CreateFromFile(fileName, nameInArchive); + if (Verbose) StatusMessageTextWriter.WriteLine("adding {0}...", fileName); + return _InternalAddEntry(ze); + } + + + /// + /// This method removes a collection of entries from the ZipFile. + /// + /// + /// + /// A collection of ZipEntry instances from this zip file to be removed. For + /// example, you can pass in an array of ZipEntry instances; or you can call + /// SelectEntries(), and then add or remove entries from that + /// ICollection<ZipEntry> (ICollection(Of ZipEntry) in VB), and pass + /// that ICollection to this method. + /// + /// + /// + /// + public void RemoveEntries(System.Collections.Generic.ICollection entriesToRemove) + { + if (entriesToRemove == null) + throw new ArgumentNullException("entriesToRemove"); + + foreach (ZipEntry e in entriesToRemove) + { + this.RemoveEntry(e); + } + } + + + /// + /// This method removes a collection of entries from the ZipFile, by name. + /// + /// + /// + /// A collection of strings that refer to names of entries to be removed + /// from the ZipFile. For example, you can pass in an array or a + /// List of Strings that provide the names of entries to be removed. + /// + /// + /// + /// + public void RemoveEntries(System.Collections.Generic.ICollection entriesToRemove) + { + if (entriesToRemove == null) + throw new ArgumentNullException("entriesToRemove"); + + foreach (String e in entriesToRemove) + { + this.RemoveEntry(e); + } + } + + + /// + /// This method adds a set of files to the ZipFile. + /// + /// + /// + /// + /// Use this method to add a set of files to the zip archive, in one call. + /// For example, a list of files received from + /// System.IO.Directory.GetFiles() can be added to a zip archive in one + /// call. + /// + /// + /// + /// For ZipFile properties including , , , , , + /// , and , their + /// respective values at the time of this call will be applied to each + /// ZipEntry added. + /// + /// + /// + /// + /// The collection of names of the files to add. Each string should refer to a + /// file in the filesystem. The name of the file may be a relative path or a + /// fully-qualified path. + /// + /// + /// + /// This example shows how to create a zip file, and add a few files into it. + /// + /// String ZipFileToCreate = "archive1.zip"; + /// String DirectoryToZip = "c:\\reports"; + /// using (ZipFile zip = new ZipFile()) + /// { + /// // Store all files found in the top level directory, into the zip archive. + /// String[] filenames = System.IO.Directory.GetFiles(DirectoryToZip); + /// zip.AddFiles(filenames); + /// zip.Save(ZipFileToCreate); + /// } + /// + /// + /// + /// Dim ZipFileToCreate As String = "archive1.zip" + /// Dim DirectoryToZip As String = "c:\reports" + /// Using zip As ZipFile = New ZipFile + /// ' Store all files found in the top level directory, into the zip archive. + /// Dim filenames As String() = System.IO.Directory.GetFiles(DirectoryToZip) + /// zip.AddFiles(filenames) + /// zip.Save(ZipFileToCreate) + /// End Using + /// + /// + /// + /// + public void AddFiles(System.Collections.Generic.IEnumerable fileNames) + { + this.AddFiles(fileNames, null); + } + + + /// + /// Adds or updates a set of files in the ZipFile. + /// + /// + /// + /// + /// Any files that already exist in the archive are updated. Any files that + /// don't yet exist in the archive are added. + /// + /// + /// + /// For ZipFile properties including , , , , , + /// , and , their + /// respective values at the time of this call will be applied to each + /// ZipEntry added. + /// + /// + /// + /// + /// The collection of names of the files to update. Each string should refer to a file in + /// the filesystem. The name of the file may be a relative path or a fully-qualified path. + /// + /// + public void UpdateFiles(System.Collections.Generic.IEnumerable fileNames) + { + this.UpdateFiles(fileNames, null); + } + + + /// + /// Adds a set of files to the ZipFile, using the + /// specified directory path in the archive. + /// + /// + /// + /// + /// Any directory structure that may be present in the + /// filenames contained in the list is "flattened" in the + /// archive. Each file in the list is added to the archive in + /// the specified top-level directory. + /// + /// + /// + /// For ZipFile properties including , , , , , , and , their respective values at the + /// time of this call will be applied to each ZipEntry added. + /// + /// + /// + /// + /// The names of the files to add. Each string should refer to + /// a file in the filesystem. The name of the file may be a + /// relative path or a fully-qualified path. + /// + /// + /// + /// Specifies a directory path to use to override any path in the file name. + /// Th is path may, or may not, correspond to a real directory in the current + /// filesystem. If the files within the zip are later extracted, this is the + /// path used for the extracted file. Passing null (Nothing in + /// VB) will use the path on each of the fileNames, if any. Passing + /// the empty string ("") will insert the item at the root path within the + /// archive. + /// + /// + /// + public void AddFiles(System.Collections.Generic.IEnumerable fileNames, String directoryPathInArchive) + { + AddFiles(fileNames, false, directoryPathInArchive); + } + + + + /// + /// Adds a set of files to the ZipFile, using the specified directory + /// path in the archive, and preserving the full directory structure in the + /// filenames. + /// + /// + /// + /// + /// + /// Think of the as a "root" or + /// base directory used in the archive for the files that get added. when + /// is true, the hierarchy of files + /// found in the filesystem will be placed, with the hierarchy intact, + /// starting at that root in the archive. When preserveDirHierarchy + /// is false, the path hierarchy of files is flattned, and the flattened + /// set of files gets placed in the root within the archive as specified in + /// directoryPathInArchive. + /// + /// + /// + /// For ZipFile properties including , , , , , + /// , and , their + /// respective values at the time of this call will be applied to each + /// ZipEntry added. + /// + /// + /// + /// + /// + /// The names of the files to add. Each string should refer to a file in the + /// filesystem. The name of the file may be a relative path or a + /// fully-qualified path. + /// + /// + /// + /// Specifies a directory path to use as a prefix for each entry name. + /// This path may, or may not, correspond to a real directory in the current + /// filesystem. If the files within the zip are later extracted, this is the + /// path used for the extracted file. Passing null (Nothing in + /// VB) will use the path on each of the fileNames, if any. Passing + /// the empty string ("") will insert the item at the root path within the + /// archive. + /// + /// + /// + /// whether the entries in the zip archive will reflect the directory + /// hierarchy that is present in the various filenames. For example, if + /// includes two paths, + /// \Animalia\Chordata\Mammalia\Info.txt and + /// \Plantae\Magnoliophyta\Dicotyledon\Info.txt, then calling this method + /// with = false will + /// result in an exception because of a duplicate entry name, while + /// calling this method with = + /// true will result in the full direcory paths being included in + /// the entries added to the ZipFile. + /// + /// + public void AddFiles(System.Collections.Generic.IEnumerable fileNames, + bool preserveDirHierarchy, + String directoryPathInArchive) + { + if (fileNames == null) + throw new ArgumentNullException("fileNames"); + + _addOperationCanceled = false; + OnAddStarted(); + if (preserveDirHierarchy) + { + foreach (var f in fileNames) + { + if (_addOperationCanceled) break; + if (directoryPathInArchive != null) + { + //string s = SharedUtilities.NormalizePath(Path.Combine(directoryPathInArchive, Path.GetDirectoryName(f))); + string s = Path.GetFullPath(Path.Combine(directoryPathInArchive, Path.GetDirectoryName(f))); + this.AddFile(f, s); + } + else + this.AddFile(f, null); + } + } + else + { + foreach (var f in fileNames) + { + if (_addOperationCanceled) break; + this.AddFile(f, directoryPathInArchive); + } + } + if (!_addOperationCanceled) + OnAddCompleted(); + } + + + /// + /// Adds or updates a set of files to the ZipFile, using the specified + /// directory path in the archive. + /// + /// + /// + /// + /// + /// Any files that already exist in the archive are updated. Any files that + /// don't yet exist in the archive are added. + /// + /// + /// + /// For ZipFile properties including , , , , , + /// , and , their + /// respective values at the time of this call will be applied to each + /// ZipEntry added. + /// + /// + /// + /// + /// The names of the files to add or update. Each string should refer to a + /// file in the filesystem. The name of the file may be a relative path or a + /// fully-qualified path. + /// + /// + /// + /// Specifies a directory path to use to override any path in the file name. + /// This path may, or may not, correspond to a real directory in the current + /// filesystem. If the files within the zip are later extracted, this is the + /// path used for the extracted file. Passing null (Nothing in + /// VB) will use the path on each of the fileNames, if any. Passing + /// the empty string ("") will insert the item at the root path within the + /// archive. + /// + /// + /// + public void UpdateFiles(System.Collections.Generic.IEnumerable fileNames, String directoryPathInArchive) + { + if (fileNames == null) + throw new ArgumentNullException("fileNames"); + + OnAddStarted(); + foreach (var f in fileNames) + this.UpdateFile(f, directoryPathInArchive); + OnAddCompleted(); + } + + + + + /// + /// Adds or Updates a File in a Zip file archive. + /// + /// + /// + /// + /// This method adds a file to a zip archive, or, if the file already exists + /// in the zip archive, this method Updates the content of that given filename + /// in the zip archive. The UpdateFile method might more accurately be + /// called "AddOrUpdateFile". + /// + /// + /// + /// Upon success, there is no way for the application to learn whether the file + /// was added versus updated. + /// + /// + /// + /// For ZipFile properties including , , , , , + /// , and , their + /// respective values at the time of this call will be applied to the + /// ZipEntry added. + /// + /// + /// + /// + /// + /// This example shows how to Update an existing entry in a zipfile. The first + /// call to UpdateFile adds the file to the newly-created zip archive. The + /// second call to UpdateFile updates the content for that file in the zip + /// archive. + /// + /// + /// using (ZipFile zip1 = new ZipFile()) + /// { + /// // UpdateFile might more accurately be called "AddOrUpdateFile" + /// zip1.UpdateFile("MyDocuments\\Readme.txt"); + /// zip1.UpdateFile("CustomerList.csv"); + /// zip1.Comment = "This zip archive has been created."; + /// zip1.Save("Content.zip"); + /// } + /// + /// using (ZipFile zip2 = ZipFile.Read("Content.zip")) + /// { + /// zip2.UpdateFile("Updates\\Readme.txt"); + /// zip2.Comment = "This zip archive has been updated: The Readme.txt file has been changed."; + /// zip2.Save(); + /// } + /// + /// + /// + /// Using zip1 As New ZipFile + /// ' UpdateFile might more accurately be called "AddOrUpdateFile" + /// zip1.UpdateFile("MyDocuments\Readme.txt") + /// zip1.UpdateFile("CustomerList.csv") + /// zip1.Comment = "This zip archive has been created." + /// zip1.Save("Content.zip") + /// End Using + /// + /// Using zip2 As ZipFile = ZipFile.Read("Content.zip") + /// zip2.UpdateFile("Updates\Readme.txt") + /// zip2.Comment = "This zip archive has been updated: The Readme.txt file has been changed." + /// zip2.Save + /// End Using + /// + /// + /// + /// + /// + /// + /// + /// + /// The name of the file to add or update. It should refer to a file in the + /// filesystem. The name of the file may be a relative path or a + /// fully-qualified path. + /// + /// + /// + /// The ZipEntry corresponding to the File that was added or updated. + /// + public ZipEntry UpdateFile(string fileName) + { + return UpdateFile(fileName, null); + } + + + + /// + /// Adds or Updates a File in a Zip file archive. + /// + /// + /// + /// + /// This method adds a file to a zip archive, or, if the file already exists + /// in the zip archive, this method Updates the content of that given filename + /// in the zip archive. + /// + /// + /// + /// This version of the method allows the caller to explicitly specify the + /// directory path to be used in the archive. The entry to be added or + /// updated is found by using the specified directory path, combined with the + /// basename of the specified filename. + /// + /// + /// + /// Upon success, there is no way for the application to learn if the file was + /// added versus updated. + /// + /// + /// + /// For ZipFile properties including , , , , , + /// , and , their + /// respective values at the time of this call will be applied to the + /// ZipEntry added. + /// + /// + /// + /// + /// + /// + /// + /// + /// The name of the file to add or update. It should refer to a file in the + /// filesystem. The name of the file may be a relative path or a + /// fully-qualified path. + /// + /// + /// + /// Specifies a directory path to use to override any path in the + /// fileName. This path may, or may not, correspond to a real + /// directory in the current filesystem. If the files within the zip are + /// later extracted, this is the path used for the extracted file. Passing + /// null (Nothing in VB) will use the path on the + /// fileName, if any. Passing the empty string ("") will insert the + /// item at the root path within the archive. + /// + /// + /// + /// The ZipEntry corresponding to the File that was added or updated. + /// + public ZipEntry UpdateFile(string fileName, String directoryPathInArchive) + { + // ideally this would all be transactional! + var key = ZipEntry.NameInArchive(fileName, directoryPathInArchive); + if (this[key] != null) + this.RemoveEntry(key); + return this.AddFile(fileName, directoryPathInArchive); + } + + + + + + /// + /// Add or update a directory in a zip archive. + /// + /// + /// + /// If the specified directory does not exist in the archive, then this method + /// is equivalent to calling AddDirectory(). If the specified + /// directory already exists in the archive, then this method updates any + /// existing entries, and adds any new entries. Any entries that are in the + /// zip archive but not in the specified directory, are left alone. In other + /// words, the contents of the zip file will be a union of the previous + /// contents and the new files. + /// + /// + /// + /// + /// + /// + /// + /// The path to the directory to be added to the zip archive, or updated in + /// the zip archive. + /// + /// + /// + /// The ZipEntry corresponding to the Directory that was added or updated. + /// + public ZipEntry UpdateDirectory(string directoryName) + { + return UpdateDirectory(directoryName, null); + } + + + /// + /// Add or update a directory in the zip archive at the specified root + /// directory in the archive. + /// + /// + /// + /// If the specified directory does not exist in the archive, then this method + /// is equivalent to calling AddDirectory(). If the specified + /// directory already exists in the archive, then this method updates any + /// existing entries, and adds any new entries. Any entries that are in the + /// zip archive but not in the specified directory, are left alone. In other + /// words, the contents of the zip file will be a union of the previous + /// contents and the new files. + /// + /// + /// + /// + /// + /// + /// + /// The path to the directory to be added to the zip archive, or updated + /// in the zip archive. + /// + /// + /// + /// Specifies a directory path to use to override any path in the + /// directoryName. This path may, or may not, correspond to a real + /// directory in the current filesystem. If the files within the zip are + /// later extracted, this is the path used for the extracted file. Passing + /// null (Nothing in VB) will use the path on the + /// directoryName, if any. Passing the empty string ("") will insert + /// the item at the root path within the archive. + /// + /// + /// + /// The ZipEntry corresponding to the Directory that was added or updated. + /// + public ZipEntry UpdateDirectory(string directoryName, String directoryPathInArchive) + { + return this.AddOrUpdateDirectoryImpl(directoryName, directoryPathInArchive, AddOrUpdateAction.AddOrUpdate); + } + + + + + + /// + /// Add or update a file or directory in the zip archive. + /// + /// + /// + /// + /// This is useful when the application is not sure or does not care if the + /// item to be added is a file or directory, and does not know or does not + /// care if the item already exists in the ZipFile. Calling this method + /// is equivalent to calling RemoveEntry() if an entry by the same name + /// already exists, followed calling by AddItem(). + /// + /// + /// + /// For ZipFile properties including , , , , , + /// , and , their + /// respective values at the time of this call will be applied to the + /// ZipEntry added. + /// + /// + /// + /// + /// + /// + /// + /// + /// the path to the file or directory to be added or updated. + /// + public void UpdateItem(string itemName) + { + UpdateItem(itemName, null); + } + + + /// + /// Add or update a file or directory. + /// + /// + /// + /// + /// This method is useful when the application is not sure or does not care if + /// the item to be added is a file or directory, and does not know or does not + /// care if the item already exists in the ZipFile. Calling this method + /// is equivalent to calling RemoveEntry(), if an entry by that name + /// exists, and then calling AddItem(). + /// + /// + /// + /// This version of the method allows the caller to explicitly specify the + /// directory path to be used for the item being added to the archive. The + /// entry or entries that are added or updated will use the specified + /// DirectoryPathInArchive. Extracting the entry from the archive will + /// result in a file stored in that directory path. + /// + /// + /// + /// For ZipFile properties including , , , , , + /// , and , their + /// respective values at the time of this call will be applied to the + /// ZipEntry added. + /// + /// + /// + /// + /// + /// + /// + /// + /// The path for the File or Directory to be added or updated. + /// + /// + /// Specifies a directory path to use to override any path in the + /// itemName. This path may, or may not, correspond to a real + /// directory in the current filesystem. If the files within the zip are + /// later extracted, this is the path used for the extracted file. Passing + /// null (Nothing in VB) will use the path on the + /// itemName, if any. Passing the empty string ("") will insert the + /// item at the root path within the archive. + /// + public void UpdateItem(string itemName, string directoryPathInArchive) + { + if (File.Exists(itemName)) + UpdateFile(itemName, directoryPathInArchive); + + else if (Directory.Exists(itemName)) + UpdateDirectory(itemName, directoryPathInArchive); + + else + throw new FileNotFoundException(String.Format("That file or directory ({0}) does not exist!", itemName)); + } + + + + + /// + /// Adds a named entry into the zip archive, taking content for the entry + /// from a string. + /// + /// + /// + /// Calling this method creates an entry using the given fileName and + /// directory path within the archive. There is no need for a file by the + /// given name to exist in the filesystem; the name is used within the zip + /// archive only. The content for the entry is encoded using the default text + /// encoding for the machine, or on Silverlight, using UTF-8. + /// + /// + /// + /// The content of the file, should it be extracted from the zip. + /// + /// + /// + /// The name, including any path, to use for the entry within the archive. + /// + /// + /// The ZipEntry added. + /// + /// + /// + /// This example shows how to add an entry to the zipfile, using a string as + /// content for that entry. + /// + /// + /// string Content = "This string will be the content of the Readme.txt file in the zip archive."; + /// using (ZipFile zip1 = new ZipFile()) + /// { + /// zip1.AddFile("MyDocuments\\Resume.doc", "files"); + /// zip1.AddEntry("Readme.txt", Content); + /// zip1.Comment = "This zip file was created at " + System.DateTime.Now.ToString("G"); + /// zip1.Save("Content.zip"); + /// } + /// + /// + /// + /// Public Sub Run() + /// Dim Content As String = "This string will be the content of the Readme.txt file in the zip archive." + /// Using zip1 As ZipFile = New ZipFile + /// zip1.AddEntry("Readme.txt", Content) + /// zip1.AddFile("MyDocuments\Resume.doc", "files") + /// zip1.Comment = ("This zip file was created at " & DateTime.Now.ToString("G")) + /// zip1.Save("Content.zip") + /// End Using + /// End Sub + /// + /// + public ZipEntry AddEntry(string entryName, string content) + { +#if SILVERLIGHT + return AddEntry(entryName, content, System.Text.Encoding.UTF8); +#else + return AddEntry(entryName, content, System.Text.Encoding.Default); +#endif + } + + + + /// + /// Adds a named entry into the zip archive, taking content for the entry + /// from a string, and using the specified text encoding. + /// + /// + /// + /// + /// + /// Calling this method creates an entry using the given fileName and + /// directory path within the archive. There is no need for a file by the + /// given name to exist in the filesystem; the name is used within the zip + /// archive only. + /// + /// + /// + /// The content for the entry, a string value, is encoded using the given + /// text encoding. A BOM (byte-order-mark) is emitted into the file, if the + /// Encoding parameter is set for that. + /// + /// + /// + /// Most Encoding classes support a constructor that accepts a boolean, + /// indicating whether to emit a BOM or not. For example see . + /// + /// + /// + /// + /// + /// The name, including any path, to use within the archive for the entry. + /// + /// + /// + /// The content of the file, should it be extracted from the zip. + /// + /// + /// + /// The text encoding to use when encoding the string. Be aware: This is + /// distinct from the text encoding used to encode the fileName, as specified + /// in . + /// + /// + /// The ZipEntry added. + /// + public ZipEntry AddEntry(string entryName, string content, System.Text.Encoding encoding) + { + // cannot employ a using clause here. We need the stream to + // persist after exit from this method. + var ms = new MemoryStream(); + + // cannot use a using clause here; StreamWriter takes + // ownership of the stream and Disposes it before we are ready. + var sw = new StreamWriter(ms, encoding); + sw.Write(content); + sw.Flush(); + + // reset to allow reading later + ms.Seek(0, SeekOrigin.Begin); + + return AddEntry(entryName, ms); + + // must not dispose the MemoryStream - it will be used later. + } + + + /// + /// Create an entry in the ZipFile using the given Stream + /// as input. The entry will have the given filename. + /// + /// + /// + /// + /// + /// The application should provide an open, readable stream; in this case it + /// will be read during the call to or one of + /// its overloads. + /// + /// + /// + /// The passed stream will be read from its current position. If + /// necessary, callers should set the position in the stream before + /// calling AddEntry(). This might be appropriate when using this method + /// with a MemoryStream, for example. + /// + /// + /// + /// In cases where a large number of streams will be added to the + /// ZipFile, the application may wish to avoid maintaining all of the + /// streams open simultaneously. To handle this situation, the application + /// should use the + /// overload. + /// + /// + /// + /// For ZipFile properties including , , , , , + /// , and , their + /// respective values at the time of this call will be applied to the + /// ZipEntry added. + /// + /// + /// + /// + /// + /// + /// This example adds a single entry to a ZipFile via a Stream. + /// + /// + /// + /// String zipToCreate = "Content.zip"; + /// String fileNameInArchive = "Content-From-Stream.bin"; + /// using (System.IO.Stream streamToRead = MyStreamOpener()) + /// { + /// using (ZipFile zip = new ZipFile()) + /// { + /// ZipEntry entry= zip.AddEntry(fileNameInArchive, streamToRead); + /// zip.AddFile("Readme.txt"); + /// zip.Save(zipToCreate); // the stream is read implicitly here + /// } + /// } + /// + /// + /// + /// Dim zipToCreate As String = "Content.zip" + /// Dim fileNameInArchive As String = "Content-From-Stream.bin" + /// Using streamToRead as System.IO.Stream = MyStreamOpener() + /// Using zip As ZipFile = New ZipFile() + /// Dim entry as ZipEntry = zip.AddEntry(fileNameInArchive, streamToRead) + /// zip.AddFile("Readme.txt") + /// zip.Save(zipToCreate) '' the stream is read implicitly, here + /// End Using + /// End Using + /// + /// + /// + /// + /// + /// + /// The name, including any path, which is shown in the zip file for the added + /// entry. + /// + /// + /// The input stream from which to grab content for the file + /// + /// The ZipEntry added. + public ZipEntry AddEntry(string entryName, Stream stream) + { + ZipEntry ze = ZipEntry.CreateForStream(entryName, stream); + ze.SetEntryTimes(DateTime.Now,DateTime.Now,DateTime.Now); + if (Verbose) StatusMessageTextWriter.WriteLine("adding {0}...", entryName); + return _InternalAddEntry(ze); + } + + + + /// + /// Add a ZipEntry for which content is written directly by the application. + /// + /// + /// + /// + /// When the application needs to write the zip entry data, use this + /// method to add the ZipEntry. For example, in the case that the + /// application wishes to write the XML representation of a DataSet into + /// a ZipEntry, the application can use this method to do so. + /// + /// + /// + /// For ZipFile properties including , , , , , + /// , and , their + /// respective values at the time of this call will be applied to the + /// ZipEntry added. + /// + /// + /// + /// About progress events: When using the WriteDelegate, DotNetZip does + /// not issue any SaveProgress events with EventType = + /// Saving_EntryBytesRead. (This is because it is the + /// application's code that runs in WriteDelegate - there's no way for + /// DotNetZip to know when to issue a EntryBytesRead event.) + /// Applications that want to update a progress bar or similar status + /// indicator should do so from within the WriteDelegate + /// itself. DotNetZip will issue the other SaveProgress events, + /// including + /// Saving_Started, + /// + /// Saving_BeforeWriteEntry, and + /// Saving_AfterWriteEntry. + /// + /// + /// + /// Note: When you use PKZip encryption, it's normally necessary to + /// compute the CRC of the content to be encrypted, before compressing or + /// encrypting it. Therefore, when using PKZip encryption with a + /// WriteDelegate, the WriteDelegate CAN BE called twice: once to compute + /// the CRC, and the second time to potentially compress and + /// encrypt. Surprising, but true. This is because PKWARE specified that + /// the encryption initialization data depends on the CRC. + /// If this happens, for each call of the delegate, your + /// application must stream the same entry data in its entirety. If your + /// application writes different data during the second call, it will + /// result in a corrupt zip file. + /// + /// + /// + /// The double-read behavior happens with all types of entries, not only + /// those that use WriteDelegate. It happens if you add an entry from a + /// filesystem file, or using a string, or a stream, or an opener/closer + /// pair. But in those cases, DotNetZip takes care of reading twice; in + /// the case of the WriteDelegate, the application code gets invoked + /// twice. Be aware. + /// + /// + /// + /// As you can imagine, this can cause performance problems for large + /// streams, and it can lead to correctness problems when you use a + /// WriteDelegate. This is a pretty big pitfall. There are two + /// ways to avoid it. First, and most preferred: don't use PKZIP + /// encryption. If you use the WinZip AES encryption, this problem + /// doesn't occur, because the encryption protocol doesn't require the CRC + /// up front. Second: if you do choose to use PKZIP encryption, write out + /// to a non-seekable stream (like standard output, or the + /// Response.OutputStream in an ASP.NET application). In this case, + /// DotNetZip will use an alternative encryption protocol that does not + /// rely on the CRC of the content. This also implies setting bit 3 in + /// the zip entry, which still presents problems for some zip tools. + /// + /// + /// + /// In the future I may modify DotNetZip to *always* use bit 3 when PKZIP + /// encryption is in use. This seems like a win overall, but there will + /// be some work involved. If you feel strongly about it, visit the + /// DotNetZip forums and vote up the Workitem + /// tracking this issue. + /// + /// + /// + /// + /// the name of the entry to add + /// the delegate which will write the entry content + /// the ZipEntry added + /// + /// + /// + /// This example shows an application filling a DataSet, then saving the + /// contents of that DataSet as XML, into a ZipEntry in a ZipFile, using an + /// anonymous delegate in C#. The DataSet XML is never saved to a disk file. + /// + /// + /// var c1= new System.Data.SqlClient.SqlConnection(connstring1); + /// var da = new System.Data.SqlClient.SqlDataAdapter() + /// { + /// SelectCommand= new System.Data.SqlClient.SqlCommand(strSelect, c1) + /// }; + /// + /// DataSet ds1 = new DataSet(); + /// da.Fill(ds1, "Invoices"); + /// + /// using(Ionic.Zip.ZipFile zip = new Ionic.Zip.ZipFile()) + /// { + /// zip.AddEntry(zipEntryName, (name,stream) => ds1.WriteXml(stream) ); + /// zip.Save(zipFileName); + /// } + /// + /// + /// + /// + /// + /// This example uses an anonymous method in C# as the WriteDelegate to provide + /// the data for the ZipEntry. The example is a bit contrived - the + /// AddFile() method is a simpler way to insert the contents of a file + /// into an entry in a zip file. On the other hand, if there is some sort of + /// processing or transformation of the file contents required before writing, + /// the application could use the WriteDelegate to do it, in this way. + /// + /// + /// using (var input = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite )) + /// { + /// using(Ionic.Zip.ZipFile zip = new Ionic.Zip.ZipFile()) + /// { + /// zip.AddEntry(zipEntryName, (name,output) => + /// { + /// byte[] buffer = new byte[BufferSize]; + /// int n; + /// while ((n = input.Read(buffer, 0, buffer.Length)) != 0) + /// { + /// // could transform the data here... + /// output.Write(buffer, 0, n); + /// // could update a progress bar here + /// } + /// }); + /// + /// zip.Save(zipFileName); + /// } + /// } + /// + /// + /// + /// + /// + /// This example uses a named delegate in VB to write data for the given + /// ZipEntry (VB9 does not have anonymous delegates). The example here is a bit + /// contrived - a simpler way to add the contents of a file to a ZipEntry is to + /// simply use the appropriate AddFile() method. The key scenario for + /// which the WriteDelegate makes sense is saving a DataSet, in XML + /// format, to the zip file. The DataSet can write XML to a stream, and the + /// WriteDelegate is the perfect place to write into the zip file. There may be + /// other data structures that can write to a stream, but cannot be read as a + /// stream. The WriteDelegate would be appropriate for those cases as + /// well. + /// + /// + /// Private Sub WriteEntry (ByVal name As String, ByVal output As Stream) + /// Using input As FileStream = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite) + /// Dim n As Integer = -1 + /// Dim buffer As Byte() = New Byte(BufferSize){} + /// Do While n <> 0 + /// n = input.Read(buffer, 0, buffer.Length) + /// output.Write(buffer, 0, n) + /// Loop + /// End Using + /// End Sub + /// + /// Public Sub Run() + /// Using zip = New ZipFile + /// zip.AddEntry(zipEntryName, New WriteDelegate(AddressOf WriteEntry)) + /// zip.Save(zipFileName) + /// End Using + /// End Sub + /// + /// + public ZipEntry AddEntry(string entryName, WriteDelegate writer) + { + ZipEntry ze = ZipEntry.CreateForWriter(entryName, writer); + if (Verbose) StatusMessageTextWriter.WriteLine("adding {0}...", entryName); + return _InternalAddEntry(ze); + } + + + /// + /// Add an entry, for which the application will provide a stream + /// containing the entry data, on a just-in-time basis. + /// + /// + /// + /// + /// In cases where the application wishes to open the stream that + /// holds the content for the ZipEntry, on a just-in-time basis, the + /// application can use this method. The application provides an + /// opener delegate that will be called by the DotNetZip library to + /// obtain a readable stream that can be read to get the bytes for + /// the given entry. Typically, this delegate opens a stream. + /// Optionally, the application can provide a closer delegate as + /// well, which will be called by DotNetZip when all bytes have been + /// read from the entry. + /// + /// + /// + /// These delegates are called from within the scope of the call to + /// ZipFile.Save(). + /// + /// + /// + /// For ZipFile properties including , , , , , + /// , and , their + /// respective values at the time of this call will be applied to the + /// ZipEntry added. + /// + /// + /// + /// + /// + /// + /// This example uses anonymous methods in C# to open and close the + /// source stream for the content for a zip entry. + /// + /// + /// using(Ionic.Zip.ZipFile zip = new Ionic.Zip.ZipFile()) + /// { + /// zip.AddEntry(zipEntryName, + /// (name) => File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite ), + /// (name, stream) => stream.Close() + /// ); + /// + /// zip.Save(zipFileName); + /// } + /// + /// + /// + /// + /// + /// + /// This example uses delegates in VB.NET to open and close the + /// the source stream for the content for a zip entry. VB 9.0 lacks + /// support for "Sub" lambda expressions, and so the CloseDelegate must + /// be an actual, named Sub. + /// + /// + /// + /// Function MyStreamOpener(ByVal entryName As String) As Stream + /// '' This simply opens a file. You probably want to do somethinig + /// '' more involved here: open a stream to read from a database, + /// '' open a stream on an HTTP connection, and so on. + /// Return File.OpenRead(entryName) + /// End Function + /// + /// Sub MyStreamCloser(entryName As String, stream As Stream) + /// stream.Close() + /// End Sub + /// + /// Public Sub Run() + /// Dim dirToZip As String = "fodder" + /// Dim zipFileToCreate As String = "Archive.zip" + /// Dim opener As OpenDelegate = AddressOf MyStreamOpener + /// Dim closer As CloseDelegate = AddressOf MyStreamCloser + /// Dim numFilestoAdd As Int32 = 4 + /// Using zip As ZipFile = New ZipFile + /// Dim i As Integer + /// For i = 0 To numFilesToAdd - 1 + /// zip.AddEntry(String.Format("content-{0:000}.txt"), opener, closer) + /// Next i + /// zip.Save(zipFileToCreate) + /// End Using + /// End Sub + /// + /// + /// + /// + /// the name of the entry to add + /// + /// the delegate that will be invoked by ZipFile.Save() to get the + /// readable stream for the given entry. ZipFile.Save() will call + /// read on this stream to obtain the data for the entry. This data + /// will then be compressed and written to the newly created zip + /// file. + /// + /// + /// the delegate that will be invoked to close the stream. This may + /// be null (Nothing in VB), in which case no call is makde to close + /// the stream. + /// + /// the ZipEntry added + /// + public ZipEntry AddEntry(string entryName, OpenDelegate opener, CloseDelegate closer) + { + ZipEntry ze = ZipEntry.CreateForJitStreamProvider(entryName, opener, closer); + ze.SetEntryTimes(DateTime.Now,DateTime.Now,DateTime.Now); + if (Verbose) StatusMessageTextWriter.WriteLine("adding {0}...", entryName); + return _InternalAddEntry(ze); + } + + + + private ZipEntry _InternalAddEntry(ZipEntry ze) + { + // stamp all the props onto the entry + ze._container = new ZipContainer(this); + ze.CompressionMethod = this.CompressionMethod; + ze.CompressionLevel = this.CompressionLevel; + ze.ExtractExistingFile = this.ExtractExistingFile; + ze.ZipErrorAction = this.ZipErrorAction; + ze.SetCompression = this.SetCompression; + ze.AlternateEncoding = this.AlternateEncoding; + ze.AlternateEncodingUsage = this.AlternateEncodingUsage; + ze.Password = this._Password; + ze.Encryption = this.Encryption; + ze.EmitTimesInWindowsFormatWhenSaving = this._emitNtfsTimes; + ze.EmitTimesInUnixFormatWhenSaving = this._emitUnixTimes; + //string key = DictionaryKeyForEntry(ze); + InternalAddEntry(ze.FileName,ze); + AfterAddEntry(ze); + return ze; + } + + + + + /// + /// Updates the given entry in the ZipFile, using the given + /// string as content for the ZipEntry. + /// + /// + /// + /// + /// + /// Calling this method is equivalent to removing the ZipEntry for + /// the given file name and directory path, if it exists, and then calling + /// . See the documentation for + /// that method for further explanation. The string content is encoded + /// using the default encoding for the machine, or on Silverlight, using + /// UTF-8. This encoding is distinct from the encoding used for the + /// filename itself. See . + /// + /// + /// + /// + /// + /// The name, including any path, to use within the archive for the entry. + /// + /// + /// + /// The content of the file, should it be extracted from the zip. + /// + /// + /// The ZipEntry added. + /// + public ZipEntry UpdateEntry(string entryName, string content) + { +#if SILVERLIGHT + return UpdateEntry(entryName, content, System.Text.Encoding.UTF8); +#else + return UpdateEntry(entryName, content, System.Text.Encoding.Default); +#endif + } + + + /// + /// Updates the given entry in the ZipFile, using the given string as + /// content for the ZipEntry. + /// + /// + /// + /// Calling this method is equivalent to removing the ZipEntry for the + /// given file name and directory path, if it exists, and then calling . See the + /// documentation for that method for further explanation. + /// + /// + /// + /// The name, including any path, to use within the archive for the entry. + /// + /// + /// + /// The content of the file, should it be extracted from the zip. + /// + /// + /// + /// The text encoding to use when encoding the string. Be aware: This is + /// distinct from the text encoding used to encode the filename. See . + /// + /// + /// The ZipEntry added. + /// + public ZipEntry UpdateEntry(string entryName, string content, System.Text.Encoding encoding) + { + RemoveEntryForUpdate(entryName); + return AddEntry(entryName, content, encoding); + } + + + + /// + /// Updates the given entry in the ZipFile, using the given delegate + /// as the source for content for the ZipEntry. + /// + /// + /// + /// Calling this method is equivalent to removing the ZipEntry for the + /// given file name and directory path, if it exists, and then calling . See the + /// documentation for that method for further explanation. + /// + /// + /// + /// The name, including any path, to use within the archive for the entry. + /// + /// + /// the delegate which will write the entry content. + /// + /// The ZipEntry added. + /// + public ZipEntry UpdateEntry(string entryName, WriteDelegate writer) + { + RemoveEntryForUpdate(entryName); + return AddEntry(entryName, writer); + } + + + + /// + /// Updates the given entry in the ZipFile, using the given delegates + /// to open and close the stream that provides the content for the ZipEntry. + /// + /// + /// + /// Calling this method is equivalent to removing the ZipEntry for the + /// given file name and directory path, if it exists, and then calling . See the + /// documentation for that method for further explanation. + /// + /// + /// + /// The name, including any path, to use within the archive for the entry. + /// + /// + /// + /// the delegate that will be invoked to open the stream + /// + /// + /// the delegate that will be invoked to close the stream + /// + /// + /// The ZipEntry added or updated. + /// + public ZipEntry UpdateEntry(string entryName, OpenDelegate opener, CloseDelegate closer) + { + RemoveEntryForUpdate(entryName); + return AddEntry(entryName, opener, closer); + } + + + /// + /// Updates the given entry in the ZipFile, using the given stream as + /// input, and the given filename and given directory Path. + /// + /// + /// + /// + /// Calling the method is equivalent to calling RemoveEntry() if an + /// entry by the same name already exists, and then calling AddEntry() + /// with the given fileName and stream. + /// + /// + /// + /// The stream must be open and readable during the call to + /// ZipFile.Save. You can dispense the stream on a just-in-time basis + /// using the property. Check the + /// documentation of that property for more information. + /// + /// + /// + /// For ZipFile properties including , , , , , + /// , and , their + /// respective values at the time of this call will be applied to the + /// ZipEntry added. + /// + /// + /// + /// + /// + /// + /// + /// + /// The name, including any path, to use within the archive for the entry. + /// + /// + /// The input stream from which to read file data. + /// The ZipEntry added. + public ZipEntry UpdateEntry(string entryName, Stream stream) + { + RemoveEntryForUpdate(entryName); + return AddEntry(entryName, stream); + } + + + private void RemoveEntryForUpdate(string entryName) + { + if (String.IsNullOrEmpty(entryName)) + throw new ArgumentNullException("entryName"); + + string directoryPathInArchive = null; + if (entryName.IndexOf('\\') != -1) + { + directoryPathInArchive = Path.GetDirectoryName(entryName); + entryName = Path.GetFileName(entryName); + } + var key = ZipEntry.NameInArchive(entryName, directoryPathInArchive); + if (this[key] != null) + this.RemoveEntry(key); + } + + + + + /// + /// Add an entry into the zip archive using the given filename and + /// directory path within the archive, and the given content for the + /// file. No file is created in the filesystem. + /// + /// + /// The data to use for the entry. + /// + /// + /// The name, including any path, to use within the archive for the entry. + /// + /// + /// The ZipEntry added. + public ZipEntry AddEntry(string entryName, byte[] byteContent) + { + if (byteContent == null) throw new ArgumentException("bad argument", "byteContent"); + var ms = new MemoryStream(byteContent); + return AddEntry(entryName, ms); + } + + + /// + /// Updates the given entry in the ZipFile, using the given byte + /// array as content for the entry. + /// + /// + /// + /// Calling this method is equivalent to removing the ZipEntry + /// for the given filename and directory path, if it exists, and then + /// calling . See the + /// documentation for that method for further explanation. + /// + /// + /// + /// The name, including any path, to use within the archive for the entry. + /// + /// + /// The content to use for the ZipEntry. + /// + /// The ZipEntry added. + /// + public ZipEntry UpdateEntry(string entryName, byte[] byteContent) + { + RemoveEntryForUpdate(entryName); + return AddEntry(entryName, byteContent); + } + + +// private string DictionaryKeyForEntry(ZipEntry ze1) +// { +// var filename = SharedUtilities.NormalizePathForUseInZipFile(ze1.FileName); +// return filename; +// } + + + /// + /// Adds the contents of a filesystem directory to a Zip file archive. + /// + /// + /// + /// + /// + /// The name of the directory may be a relative path or a fully-qualified + /// path. Any files within the named directory are added to the archive. Any + /// subdirectories within the named directory are also added to the archive, + /// recursively. + /// + /// + /// + /// Top-level entries in the named directory will appear as top-level entries + /// in the zip archive. Entries in subdirectories in the named directory will + /// result in entries in subdirectories in the zip archive. + /// + /// + /// + /// If you want the entries to appear in a containing directory in the zip + /// archive itself, then you should call the AddDirectory() overload that + /// allows you to explicitly specify a directory path for use in the archive. + /// + /// + /// + /// For ZipFile properties including , , , , , + /// , and , their + /// respective values at the time of this call will be applied to each + /// ZipEntry added. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// This method has 2 overloads. + /// + /// The name of the directory to add. + /// The ZipEntry added. + public ZipEntry AddDirectory(string directoryName) + { + return AddDirectory(directoryName, null); + } + + + /// + /// Adds the contents of a filesystem directory to a Zip file archive, + /// overriding the path to be used for entries in the archive. + /// + /// + /// + /// + /// The name of the directory may be a relative path or a fully-qualified + /// path. The add operation is recursive, so that any files or subdirectories + /// within the name directory are also added to the archive. + /// + /// + /// + /// Top-level entries in the named directory will appear as top-level entries + /// in the zip archive. Entries in subdirectories in the named directory will + /// result in entries in subdirectories in the zip archive. + /// + /// + /// + /// For ZipFile properties including , , , , , + /// , and , their + /// respective values at the time of this call will be applied to each + /// ZipEntry added. + /// + /// + /// + /// + /// + /// + /// In this code, calling the ZipUp() method with a value of "c:\reports" for + /// the directory parameter will result in a zip file structure in which all + /// entries are contained in a toplevel "reports" directory. + /// + /// + /// + /// public void ZipUp(string targetZip, string directory) + /// { + /// using (var zip = new ZipFile()) + /// { + /// zip.AddDirectory(directory, System.IO.Path.GetFileName(directory)); + /// zip.Save(targetZip); + /// } + /// } + /// + /// + /// + /// + /// + /// + /// + /// The name of the directory to add. + /// + /// + /// Specifies a directory path to use to override any path in the + /// DirectoryName. This path may, or may not, correspond to a real directory + /// in the current filesystem. If the zip is later extracted, this is the + /// path used for the extracted file or directory. Passing null + /// (Nothing in VB) or the empty string ("") will insert the items at + /// the root path within the archive. + /// + /// + /// The ZipEntry added. + public ZipEntry AddDirectory(string directoryName, string directoryPathInArchive) + { + return AddOrUpdateDirectoryImpl(directoryName, directoryPathInArchive, AddOrUpdateAction.AddOnly); + } + + + /// + /// Creates a directory in the zip archive. + /// + /// + /// + /// + /// + /// Use this when you want to create a directory in the archive but there is + /// no corresponding filesystem representation for that directory. + /// + /// + /// + /// You will probably not need to do this in your code. One of the only times + /// you will want to do this is if you want an empty directory in the zip + /// archive. The reason: if you add a file to a zip archive that is stored + /// within a multi-level directory, all of the directory tree is implicitly + /// created in the zip archive. + /// + /// + /// + /// + /// + /// The name of the directory to create in the archive. + /// + /// The ZipEntry added. + public ZipEntry AddDirectoryByName(string directoryNameInArchive) + { + // workitem 9073 + ZipEntry dir = ZipEntry.CreateFromNothing(directoryNameInArchive); + dir._container = new ZipContainer(this); + dir.MarkAsDirectory(); + dir.AlternateEncoding = this.AlternateEncoding; // workitem 8984 + dir.AlternateEncodingUsage = this.AlternateEncodingUsage; + dir.SetEntryTimes(DateTime.Now,DateTime.Now,DateTime.Now); + dir.EmitTimesInWindowsFormatWhenSaving = _emitNtfsTimes; + dir.EmitTimesInUnixFormatWhenSaving = _emitUnixTimes; + dir._Source = ZipEntrySource.Stream; + //string key = DictionaryKeyForEntry(dir); + InternalAddEntry(dir.FileName,dir); + AfterAddEntry(dir); + return dir; + } + + + + private ZipEntry AddOrUpdateDirectoryImpl(string directoryName, + string rootDirectoryPathInArchive, + AddOrUpdateAction action) + { + if (rootDirectoryPathInArchive == null) + { + rootDirectoryPathInArchive = ""; + } + + return AddOrUpdateDirectoryImpl(directoryName, rootDirectoryPathInArchive, action, true, 0); + } + + + internal void InternalAddEntry(String name, ZipEntry entry) + { + _entries.Add(name, entry); + _zipEntriesAsList = null; + _contentsChanged = true; + } + + + + private ZipEntry AddOrUpdateDirectoryImpl(string directoryName, + string rootDirectoryPathInArchive, + AddOrUpdateAction action, + bool recurse, + int level) + { + if (Verbose) + StatusMessageTextWriter.WriteLine("{0} {1}...", + (action == AddOrUpdateAction.AddOnly) ? "adding" : "Adding or updating", + directoryName); + + if (level == 0) + { + _addOperationCanceled = false; + OnAddStarted(); + } + + // workitem 13371 + if (_addOperationCanceled) + return null; + + string dirForEntries = rootDirectoryPathInArchive; + ZipEntry baseDir = null; + + if (level > 0) + { + int f = directoryName.Length; + for (int i = level; i > 0; i--) + f = directoryName.LastIndexOfAny("/\\".ToCharArray(), f - 1, f - 1); + + dirForEntries = directoryName.Substring(f + 1); + dirForEntries = Path.Combine(rootDirectoryPathInArchive, dirForEntries); + } + + // if not top level, or if the root is non-empty, then explicitly add the directory + if (level > 0 || rootDirectoryPathInArchive != "") + { + baseDir = ZipEntry.CreateFromFile(directoryName, dirForEntries); + baseDir._container = new ZipContainer(this); + baseDir.AlternateEncoding = this.AlternateEncoding; // workitem 6410 + baseDir.AlternateEncodingUsage = this.AlternateEncodingUsage; + baseDir.MarkAsDirectory(); + baseDir.EmitTimesInWindowsFormatWhenSaving = _emitNtfsTimes; + baseDir.EmitTimesInUnixFormatWhenSaving = _emitUnixTimes; + + // add the directory only if it does not exist. + // It's not an error if it already exists. + if (!_entries.ContainsKey(baseDir.FileName)) + { + InternalAddEntry(baseDir.FileName,baseDir); + AfterAddEntry(baseDir); + } + dirForEntries = baseDir.FileName; + } + + if (!_addOperationCanceled) + { + + String[] filenames = Directory.GetFiles(directoryName); + + if (recurse) + { + // add the files: + foreach (String filename in filenames) + { + if (_addOperationCanceled) break; + if (action == AddOrUpdateAction.AddOnly) + AddFile(filename, dirForEntries); + else + UpdateFile(filename, dirForEntries); + } + + if (!_addOperationCanceled) + { + // add the subdirectories: + String[] dirnames = Directory.GetDirectories(directoryName); + foreach (String dir in dirnames) + { + // workitem 8617: Optionally traverse reparse points +#if SILVERLIGHT +#elif NETCF + FileAttributes fileAttrs = (FileAttributes) NetCfFile.GetAttributes(dir); +#else + FileAttributes fileAttrs = System.IO.File.GetAttributes(dir); +#endif + if (this.AddDirectoryWillTraverseReparsePoints +#if !SILVERLIGHT + || ((fileAttrs & FileAttributes.ReparsePoint) == 0) +#endif + ) + AddOrUpdateDirectoryImpl(dir, rootDirectoryPathInArchive, action, recurse, level + 1); + + } + + } + } + } + + if (level == 0) + OnAddCompleted(); + + return baseDir; + } + + } + +} diff --git a/DotNetZip/Zip/ZipFile.Check.cs b/DotNetZip/Zip/ZipFile.Check.cs new file mode 100644 index 0000000..b8307d5 --- /dev/null +++ b/DotNetZip/Zip/ZipFile.Check.cs @@ -0,0 +1,352 @@ +// ZipFile.Check.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2009-2011 Dino Chiesa. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2011-July-31 14:40:50> +// +// ------------------------------------------------------------------ +// +// This module defines the methods for doing Checks on zip files. +// These are not necessary to include in the Reduced or CF +// version of the library. +// +// ------------------------------------------------------------------ +// + + +using System; +using System.IO; +using System.Collections.Generic; + +namespace Ionic.Zip +{ + public partial class ZipFile + { + /// + /// Checks a zip file to see if its directory is consistent. + /// + /// + /// + /// + /// + /// In cases of data error, the directory within a zip file can get out + /// of synch with the entries in the zip file. This method checks the + /// given zip file and returns true if this has occurred. + /// + /// + /// This method may take a long time to run for large zip files. + /// + /// + /// This method is not supported in the Reduced or Compact Framework + /// versions of DotNetZip. + /// + /// + /// + /// Developers using COM can use the ComHelper.CheckZip(String) + /// method. + /// + /// + /// + /// + /// The filename to of the zip file to check. + /// + /// true if the named zip file checks OK. Otherwise, false. + /// + /// + /// + public static bool CheckZip(string zipFileName) + { + return CheckZip(zipFileName, false, null); + } + + + /// + /// Checks a zip file to see if its directory is consistent, + /// and optionally fixes the directory if necessary. + /// + /// + /// + /// + /// + /// In cases of data error, the directory within a zip file can get out of + /// synch with the entries in the zip file. This method checks the given + /// zip file, and returns true if this has occurred. It also optionally + /// fixes the zipfile, saving the fixed copy in Name_Fixed.zip. + /// + /// + /// + /// This method may take a long time to run for large zip files. It + /// will take even longer if the file actually needs to be fixed, and if + /// fixIfNecessary is true. + /// + /// + /// + /// This method is not supported in the Reduced or Compact + /// Framework versions of DotNetZip. + /// + /// + /// + /// + /// The filename to of the zip file to check. + /// + /// If true, the method will fix the zip file if + /// necessary. + /// + /// + /// a TextWriter in which messages generated while checking will be written. + /// + /// + /// true if the named zip is OK; false if the file needs to be fixed. + /// + /// + /// + public static bool CheckZip(string zipFileName, bool fixIfNecessary, + TextWriter writer) + + { + ZipFile zip1 = null, zip2 = null; + bool isOk = true; + try + { + zip1 = new ZipFile(); + zip1.FullScan = true; + zip1.Initialize(zipFileName); + + zip2 = ZipFile.Read(zipFileName); + + foreach (var e1 in zip1) + { + foreach (var e2 in zip2) + { + if (e1.FileName == e2.FileName) + { + if (e1._RelativeOffsetOfLocalHeader != e2._RelativeOffsetOfLocalHeader) + { + isOk = false; + if (writer != null) + writer.WriteLine("{0}: mismatch in RelativeOffsetOfLocalHeader (0x{1:X16} != 0x{2:X16})", + e1.FileName, e1._RelativeOffsetOfLocalHeader, + e2._RelativeOffsetOfLocalHeader); + } + if (e1._CompressedSize != e2._CompressedSize) + { + isOk = false; + if (writer != null) + writer.WriteLine("{0}: mismatch in CompressedSize (0x{1:X16} != 0x{2:X16})", + e1.FileName, e1._CompressedSize, + e2._CompressedSize); + } + if (e1._UncompressedSize != e2._UncompressedSize) + { + isOk = false; + if (writer != null) + writer.WriteLine("{0}: mismatch in UncompressedSize (0x{1:X16} != 0x{2:X16})", + e1.FileName, e1._UncompressedSize, + e2._UncompressedSize); + } + if (e1.CompressionMethod != e2.CompressionMethod) + { + isOk = false; + if (writer != null) + writer.WriteLine("{0}: mismatch in CompressionMethod (0x{1:X4} != 0x{2:X4})", + e1.FileName, e1.CompressionMethod, + e2.CompressionMethod); + } + if (e1.Crc != e2.Crc) + { + isOk = false; + if (writer != null) + writer.WriteLine("{0}: mismatch in Crc32 (0x{1:X4} != 0x{2:X4})", + e1.FileName, e1.Crc, + e2.Crc); + } + + // found a match, so stop the inside loop + break; + } + } + } + + zip2.Dispose(); + zip2 = null; + + if (!isOk && fixIfNecessary) + { + string newFileName = Path.GetFileNameWithoutExtension(zipFileName); + newFileName = System.String.Format("{0}_fixed.zip", newFileName); + zip1.Save(newFileName); + } + } + finally + { + if (zip1 != null) zip1.Dispose(); + if (zip2 != null) zip2.Dispose(); + } + return isOk; + } + + + + /// + /// Rewrite the directory within a zipfile. + /// + /// + /// + /// + /// + /// In cases of data error, the directory in a zip file can get out of + /// synch with the entries in the zip file. This method attempts to fix + /// the zip file if this has occurred. + /// + /// + /// This can take a long time for large zip files. + /// + /// This won't work if the zip file uses a non-standard + /// code page - neither IBM437 nor UTF-8. + /// + /// + /// This method is not supported in the Reduced or Compact Framework + /// versions of DotNetZip. + /// + /// + /// + /// Developers using COM can use the ComHelper.FixZipDirectory(String) + /// method. + /// + /// + /// + /// + /// The filename to of the zip file to fix. + /// + /// + /// + public static void FixZipDirectory(string zipFileName) + { + using (var zip = new ZipFile()) + { + zip.FullScan = true; + zip.Initialize(zipFileName); + zip.Save(zipFileName); + } + } + + + + /// + /// Verify the password on a zip file. + /// + /// + /// + /// + /// Keep in mind that passwords in zipfiles are applied to + /// zip entries, not to the entire zip file. So testing a + /// zipfile for a particular password doesn't work in the + /// general case. On the other hand, it's often the case + /// that a single password will be used on all entries in a + /// zip file. This method works for that case. + /// + /// + /// There is no way to check a password without doing the + /// decryption. So this code decrypts and extracts the given + /// zipfile into + /// + /// + /// + /// The filename to of the zip file to fix. + /// + /// The password to check. + /// + /// a bool indicating whether the password matches. + public static bool CheckZipPassword(string zipFileName, string password) + { + // workitem 13664 + bool success = false; + try + { + using (ZipFile zip1 = ZipFile.Read(zipFileName)) + { + foreach (var e in zip1) + { + if (!e.IsDirectory && e.UsesEncryption) + { + e.ExtractWithPassword(System.IO.Stream.Null, password); + } + } + } + success = true; + } + catch(Ionic.Zip.BadPasswordException) { } + return success; + } + + + /// + /// Provides a human-readable string with information about the ZipFile. + /// + /// + /// + /// + /// The information string contains 10 lines or so, about each ZipEntry, + /// describing whether encryption is in use, the compressed and uncompressed + /// length of the entry, the offset of the entry, and so on. As a result the + /// information string can be very long for zip files that contain many + /// entries. + /// + /// + /// This information is mostly useful for diagnostic purposes. + /// + /// + public string Info + { + get + { + var builder = new System.Text.StringBuilder(); + builder.Append(string.Format(" ZipFile: {0}\n", this.Name)); + if (!string.IsNullOrEmpty(this._Comment)) + { + builder.Append(string.Format(" Comment: {0}\n", this._Comment)); + } + if (this._versionMadeBy != 0) + { + builder.Append(string.Format(" version made by: 0x{0:X4}\n", this._versionMadeBy)); + } + if (this._versionNeededToExtract != 0) + { + builder.Append(string.Format("needed to extract: 0x{0:X4}\n", this._versionNeededToExtract)); + } + + builder.Append(string.Format(" uses ZIP64: {0}\n", this.InputUsesZip64)); + + builder.Append(string.Format(" disk with CD: {0}\n", this._diskNumberWithCd)); + if (this._OffsetOfCentralDirectory == 0xFFFFFFFF) + builder.Append(string.Format(" CD64 offset: 0x{0:X16}\n", this._OffsetOfCentralDirectory64)); + else + builder.Append(string.Format(" CD offset: 0x{0:X8}\n", this._OffsetOfCentralDirectory)); + builder.Append("\n"); + foreach (ZipEntry entry in this._entries.Values) + { + builder.Append(entry.Info); + } + return builder.ToString(); + } + } + + + } + +} \ No newline at end of file diff --git a/DotNetZip/Zip/ZipFile.Events.cs b/DotNetZip/Zip/ZipFile.Events.cs new file mode 100644 index 0000000..110ee6d --- /dev/null +++ b/DotNetZip/Zip/ZipFile.Events.cs @@ -0,0 +1,1219 @@ +// ZipFile.Events.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2008, 2009, 2011 Dino Chiesa . +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2011-July-09 08:42:35> +// +// ------------------------------------------------------------------ +// +// This module defines the methods for issuing events from the ZipFile class. +// +// ------------------------------------------------------------------ +// + +using System; +using System.IO; + +namespace Ionic.Zip +{ + public partial class ZipFile + { + private string ArchiveNameForEvent + { + get + { + return (_name != null) ? _name : "(stream)"; + } + } + + #region Save + + /// + /// An event handler invoked when a Save() starts, before and after each + /// entry has been written to the archive, when a Save() completes, and + /// during other Save events. + /// + /// + /// + /// + /// Depending on the particular event, different properties on the parameter are set. The following + /// table summarizes the available EventTypes and the conditions under + /// which this event handler is invoked with a + /// SaveProgressEventArgs with the given EventType. + /// + /// + /// + /// + /// value of EntryType + /// Meaning and conditions + /// + /// + /// + /// ZipProgressEventType.Saving_Started + /// Fired when ZipFile.Save() begins. + /// + /// + /// + /// + /// ZipProgressEventType.Saving_BeforeSaveEntry + /// + /// Fired within ZipFile.Save(), just before writing data for each + /// particular entry. + /// + /// + /// + /// + /// ZipProgressEventType.Saving_AfterSaveEntry + /// + /// Fired within ZipFile.Save(), just after having finished writing data + /// for each particular entry. + /// + /// + /// + /// + /// ZipProgressEventType.Saving_Completed + /// Fired when ZipFile.Save() has completed. + /// + /// + /// + /// + /// ZipProgressEventType.Saving_AfterSaveTempArchive + /// + /// Fired after the temporary file has been created. This happens only + /// when saving to a disk file. This event will not be invoked when + /// saving to a stream. + /// + /// + /// + /// + /// ZipProgressEventType.Saving_BeforeRenameTempArchive + /// + /// Fired just before renaming the temporary file to the permanent + /// location. This happens only when saving to a disk file. This event + /// will not be invoked when saving to a stream. + /// + /// + /// + /// + /// ZipProgressEventType.Saving_AfterRenameTempArchive + /// + /// Fired just after renaming the temporary file to the permanent + /// location. This happens only when saving to a disk file. This event + /// will not be invoked when saving to a stream. + /// + /// + /// + /// + /// ZipProgressEventType.Saving_AfterCompileSelfExtractor + /// + /// Fired after a self-extracting archive has finished compiling. This + /// EventType is used only within SaveSelfExtractor(). + /// + /// + /// + /// + /// ZipProgressEventType.Saving_BytesRead + /// + /// Set during the save of a particular entry, to update progress of the + /// Save(). When this EventType is set, the BytesTransferred is the + /// number of bytes that have been read from the source stream. The + /// TotalBytesToTransfer is the number of bytes in the uncompressed + /// file. + /// + /// + /// + /// + /// + /// + /// + /// + /// This example uses an anonymous method to handle the + /// SaveProgress event, by updating a progress bar. + /// + /// + /// progressBar1.Value = 0; + /// progressBar1.Max = listbox1.Items.Count; + /// using (ZipFile zip = new ZipFile()) + /// { + /// // listbox1 contains a list of filenames + /// zip.AddFiles(listbox1.Items); + /// + /// // do the progress bar: + /// zip.SaveProgress += (sender, e) => { + /// if (e.EventType == ZipProgressEventType.Saving_BeforeWriteEntry) { + /// progressBar1.PerformStep(); + /// } + /// }; + /// + /// zip.Save(fs); + /// } + /// + /// + /// + /// + /// This example uses a named method as the + /// SaveProgress event handler, to update the user, in a + /// console-based application. + /// + /// + /// static bool justHadByteUpdate= false; + /// public static void SaveProgress(object sender, SaveProgressEventArgs e) + /// { + /// if (e.EventType == ZipProgressEventType.Saving_Started) + /// Console.WriteLine("Saving: {0}", e.ArchiveName); + /// + /// else if (e.EventType == ZipProgressEventType.Saving_Completed) + /// { + /// justHadByteUpdate= false; + /// Console.WriteLine(); + /// Console.WriteLine("Done: {0}", e.ArchiveName); + /// } + /// + /// else if (e.EventType == ZipProgressEventType.Saving_BeforeWriteEntry) + /// { + /// if (justHadByteUpdate) + /// Console.WriteLine(); + /// Console.WriteLine(" Writing: {0} ({1}/{2})", + /// e.CurrentEntry.FileName, e.EntriesSaved, e.EntriesTotal); + /// justHadByteUpdate= false; + /// } + /// + /// else if (e.EventType == ZipProgressEventType.Saving_EntryBytesRead) + /// { + /// if (justHadByteUpdate) + /// Console.SetCursorPosition(0, Console.CursorTop); + /// Console.Write(" {0}/{1} ({2:N0}%)", e.BytesTransferred, e.TotalBytesToTransfer, + /// e.BytesTransferred / (0.01 * e.TotalBytesToTransfer )); + /// justHadByteUpdate= true; + /// } + /// } + /// + /// public static ZipUp(string targetZip, string directory) + /// { + /// using (var zip = new ZipFile()) { + /// zip.SaveProgress += SaveProgress; + /// zip.AddDirectory(directory); + /// zip.Save(targetZip); + /// } + /// } + /// + /// + /// + /// + /// Public Sub ZipUp(ByVal targetZip As String, ByVal directory As String) + /// Using zip As ZipFile = New ZipFile + /// AddHandler zip.SaveProgress, AddressOf MySaveProgress + /// zip.AddDirectory(directory) + /// zip.Save(targetZip) + /// End Using + /// End Sub + /// + /// Private Shared justHadByteUpdate As Boolean = False + /// + /// Public Shared Sub MySaveProgress(ByVal sender As Object, ByVal e As SaveProgressEventArgs) + /// If (e.EventType Is ZipProgressEventType.Saving_Started) Then + /// Console.WriteLine("Saving: {0}", e.ArchiveName) + /// + /// ElseIf (e.EventType Is ZipProgressEventType.Saving_Completed) Then + /// justHadByteUpdate = False + /// Console.WriteLine + /// Console.WriteLine("Done: {0}", e.ArchiveName) + /// + /// ElseIf (e.EventType Is ZipProgressEventType.Saving_BeforeWriteEntry) Then + /// If justHadByteUpdate Then + /// Console.WriteLine + /// End If + /// Console.WriteLine(" Writing: {0} ({1}/{2})", e.CurrentEntry.FileName, e.EntriesSaved, e.EntriesTotal) + /// justHadByteUpdate = False + /// + /// ElseIf (e.EventType Is ZipProgressEventType.Saving_EntryBytesRead) Then + /// If justHadByteUpdate Then + /// Console.SetCursorPosition(0, Console.CursorTop) + /// End If + /// Console.Write(" {0}/{1} ({2:N0}%)", e.BytesTransferred, _ + /// e.TotalBytesToTransfer, _ + /// (CDbl(e.BytesTransferred) / (0.01 * e.TotalBytesToTransfer))) + /// justHadByteUpdate = True + /// End If + /// End Sub + /// + /// + /// + /// + /// + /// This is a more complete example of using the SaveProgress + /// events in a Windows Forms application, with a + /// Thread object. + /// + /// + /// delegate void SaveEntryProgress(SaveProgressEventArgs e); + /// delegate void ButtonClick(object sender, EventArgs e); + /// + /// public class WorkerOptions + /// { + /// public string ZipName; + /// public string Folder; + /// public string Encoding; + /// public string Comment; + /// public int ZipFlavor; + /// public Zip64Option Zip64; + /// } + /// + /// private int _progress2MaxFactor; + /// private bool _saveCanceled; + /// private long _totalBytesBeforeCompress; + /// private long _totalBytesAfterCompress; + /// private Thread _workerThread; + /// + /// + /// private void btnZipup_Click(object sender, EventArgs e) + /// { + /// KickoffZipup(); + /// } + /// + /// private void btnCancel_Click(object sender, EventArgs e) + /// { + /// if (this.lblStatus.InvokeRequired) + /// { + /// this.lblStatus.Invoke(new ButtonClick(this.btnCancel_Click), new object[] { sender, e }); + /// } + /// else + /// { + /// _saveCanceled = true; + /// lblStatus.Text = "Canceled..."; + /// ResetState(); + /// } + /// } + /// + /// private void KickoffZipup() + /// { + /// _folderName = tbDirName.Text; + /// + /// if (_folderName == null || _folderName == "") return; + /// if (this.tbZipName.Text == null || this.tbZipName.Text == "") return; + /// + /// // check for existence of the zip file: + /// if (System.IO.File.Exists(this.tbZipName.Text)) + /// { + /// var dlgResult = MessageBox.Show(String.Format("The file you have specified ({0}) already exists." + + /// " Do you want to overwrite this file?", this.tbZipName.Text), + /// "Confirmation is Required", MessageBoxButtons.YesNo, MessageBoxIcon.Question); + /// if (dlgResult != DialogResult.Yes) return; + /// System.IO.File.Delete(this.tbZipName.Text); + /// } + /// + /// _saveCanceled = false; + /// _nFilesCompleted = 0; + /// _totalBytesAfterCompress = 0; + /// _totalBytesBeforeCompress = 0; + /// this.btnOk.Enabled = false; + /// this.btnOk.Text = "Zipping..."; + /// this.btnCancel.Enabled = true; + /// lblStatus.Text = "Zipping..."; + /// + /// var options = new WorkerOptions + /// { + /// ZipName = this.tbZipName.Text, + /// Folder = _folderName, + /// Encoding = "ibm437" + /// }; + /// + /// if (this.comboBox1.SelectedIndex != 0) + /// { + /// options.Encoding = this.comboBox1.SelectedItem.ToString(); + /// } + /// + /// if (this.radioFlavorSfxCmd.Checked) + /// options.ZipFlavor = 2; + /// else if (this.radioFlavorSfxGui.Checked) + /// options.ZipFlavor = 1; + /// else options.ZipFlavor = 0; + /// + /// if (this.radioZip64AsNecessary.Checked) + /// options.Zip64 = Zip64Option.AsNecessary; + /// else if (this.radioZip64Always.Checked) + /// options.Zip64 = Zip64Option.Always; + /// else options.Zip64 = Zip64Option.Never; + /// + /// options.Comment = String.Format("Encoding:{0} || Flavor:{1} || ZIP64:{2}\r\nCreated at {3} || {4}\r\n", + /// options.Encoding, + /// FlavorToString(options.ZipFlavor), + /// options.Zip64.ToString(), + /// System.DateTime.Now.ToString("yyyy-MMM-dd HH:mm:ss"), + /// this.Text); + /// + /// if (this.tbComment.Text != TB_COMMENT_NOTE) + /// options.Comment += this.tbComment.Text; + /// + /// _workerThread = new Thread(this.DoSave); + /// _workerThread.Name = "Zip Saver thread"; + /// _workerThread.Start(options); + /// this.Cursor = Cursors.WaitCursor; + /// } + /// + /// + /// private void DoSave(Object p) + /// { + /// WorkerOptions options = p as WorkerOptions; + /// try + /// { + /// using (var zip1 = new ZipFile()) + /// { + /// zip1.ProvisionalAlternateEncoding = System.Text.Encoding.GetEncoding(options.Encoding); + /// zip1.Comment = options.Comment; + /// zip1.AddDirectory(options.Folder); + /// _entriesToZip = zip1.EntryFileNames.Count; + /// SetProgressBars(); + /// zip1.SaveProgress += this.zip1_SaveProgress; + /// + /// zip1.UseZip64WhenSaving = options.Zip64; + /// + /// if (options.ZipFlavor == 1) + /// zip1.SaveSelfExtractor(options.ZipName, SelfExtractorFlavor.WinFormsApplication); + /// else if (options.ZipFlavor == 2) + /// zip1.SaveSelfExtractor(options.ZipName, SelfExtractorFlavor.ConsoleApplication); + /// else + /// zip1.Save(options.ZipName); + /// } + /// } + /// catch (System.Exception exc1) + /// { + /// MessageBox.Show(String.Format("Exception while zipping: {0}", exc1.Message)); + /// btnCancel_Click(null, null); + /// } + /// } + /// + /// + /// + /// void zip1_SaveProgress(object sender, SaveProgressEventArgs e) + /// { + /// switch (e.EventType) + /// { + /// case ZipProgressEventType.Saving_AfterWriteEntry: + /// StepArchiveProgress(e); + /// break; + /// case ZipProgressEventType.Saving_EntryBytesRead: + /// StepEntryProgress(e); + /// break; + /// case ZipProgressEventType.Saving_Completed: + /// SaveCompleted(); + /// break; + /// case ZipProgressEventType.Saving_AfterSaveTempArchive: + /// // this event only occurs when saving an SFX file + /// TempArchiveSaved(); + /// break; + /// } + /// if (_saveCanceled) + /// e.Cancel = true; + /// } + /// + /// + /// + /// private void StepArchiveProgress(SaveProgressEventArgs e) + /// { + /// if (this.progressBar1.InvokeRequired) + /// { + /// this.progressBar1.Invoke(new SaveEntryProgress(this.StepArchiveProgress), new object[] { e }); + /// } + /// else + /// { + /// if (!_saveCanceled) + /// { + /// _nFilesCompleted++; + /// this.progressBar1.PerformStep(); + /// _totalBytesAfterCompress += e.CurrentEntry.CompressedSize; + /// _totalBytesBeforeCompress += e.CurrentEntry.UncompressedSize; + /// + /// // reset the progress bar for the entry: + /// this.progressBar2.Value = this.progressBar2.Maximum = 1; + /// + /// this.Update(); + /// } + /// } + /// } + /// + /// + /// private void StepEntryProgress(SaveProgressEventArgs e) + /// { + /// if (this.progressBar2.InvokeRequired) + /// { + /// this.progressBar2.Invoke(new SaveEntryProgress(this.StepEntryProgress), new object[] { e }); + /// } + /// else + /// { + /// if (!_saveCanceled) + /// { + /// if (this.progressBar2.Maximum == 1) + /// { + /// // reset + /// Int64 max = e.TotalBytesToTransfer; + /// _progress2MaxFactor = 0; + /// while (max > System.Int32.MaxValue) + /// { + /// max /= 2; + /// _progress2MaxFactor++; + /// } + /// this.progressBar2.Maximum = (int)max; + /// lblStatus.Text = String.Format("{0} of {1} files...({2})", + /// _nFilesCompleted + 1, _entriesToZip, e.CurrentEntry.FileName); + /// } + /// + /// int xferred = e.BytesTransferred >> _progress2MaxFactor; + /// + /// this.progressBar2.Value = (xferred >= this.progressBar2.Maximum) + /// ? this.progressBar2.Maximum + /// : xferred; + /// + /// this.Update(); + /// } + /// } + /// } + /// + /// private void SaveCompleted() + /// { + /// if (this.lblStatus.InvokeRequired) + /// { + /// this.lblStatus.Invoke(new MethodInvoker(this.SaveCompleted)); + /// } + /// else + /// { + /// lblStatus.Text = String.Format("Done, Compressed {0} files, {1:N0}% of original.", + /// _nFilesCompleted, (100.00 * _totalBytesAfterCompress) / _totalBytesBeforeCompress); + /// ResetState(); + /// } + /// } + /// + /// private void ResetState() + /// { + /// this.btnCancel.Enabled = false; + /// this.btnOk.Enabled = true; + /// this.btnOk.Text = "Zip it!"; + /// this.progressBar1.Value = 0; + /// this.progressBar2.Value = 0; + /// this.Cursor = Cursors.Default; + /// if (!_workerThread.IsAlive) + /// _workerThread.Join(); + /// } + /// + /// + /// + /// + /// + /// + /// + public event EventHandler SaveProgress; + + + internal bool OnSaveBlock(ZipEntry entry, Int64 bytesXferred, Int64 totalBytesToXfer) + { + EventHandler sp = SaveProgress; + if (sp != null) + { + var e = SaveProgressEventArgs.ByteUpdate(ArchiveNameForEvent, entry, + bytesXferred, totalBytesToXfer); + sp(this, e); + if (e.Cancel) + _saveOperationCanceled = true; + } + return _saveOperationCanceled; + } + + private void OnSaveEntry(int current, ZipEntry entry, bool before) + { + EventHandler sp = SaveProgress; + if (sp != null) + { + var e = new SaveProgressEventArgs(ArchiveNameForEvent, before, _entries.Count, current, entry); + sp(this, e); + if (e.Cancel) + _saveOperationCanceled = true; + } + } + + private void OnSaveEvent(ZipProgressEventType eventFlavor) + { + EventHandler sp = SaveProgress; + if (sp != null) + { + var e = new SaveProgressEventArgs(ArchiveNameForEvent, eventFlavor); + sp(this, e); + if (e.Cancel) + _saveOperationCanceled = true; + } + } + + private void OnSaveStarted() + { + EventHandler sp = SaveProgress; + if (sp != null) + { + var e = SaveProgressEventArgs.Started(ArchiveNameForEvent); + sp(this, e); + if (e.Cancel) + _saveOperationCanceled = true; + } + } + private void OnSaveCompleted() + { + EventHandler sp = SaveProgress; + if (sp != null) + { + var e = SaveProgressEventArgs.Completed(ArchiveNameForEvent); + sp(this, e); + } + } + #endregion + + + #region Read + /// + /// An event handler invoked before, during, and after the reading of a zip archive. + /// + /// + /// + /// + /// Depending on the particular event being signaled, different properties on the + /// parameter are set. The following table + /// summarizes the available EventTypes and the conditions under which this + /// event handler is invoked with a ReadProgressEventArgs with the given EventType. + /// + /// + /// + /// + /// value of EntryType + /// Meaning and conditions + /// + /// + /// + /// ZipProgressEventType.Reading_Started + /// Fired just as ZipFile.Read() begins. Meaningful properties: ArchiveName. + /// + /// + /// + /// + /// ZipProgressEventType.Reading_Completed + /// Fired when ZipFile.Read() has completed. Meaningful properties: ArchiveName. + /// + /// + /// + /// + /// ZipProgressEventType.Reading_ArchiveBytesRead + /// Fired while reading, updates the number of bytes read for the entire archive. + /// Meaningful properties: ArchiveName, CurrentEntry, BytesTransferred, TotalBytesToTransfer. + /// + /// + /// + /// + /// ZipProgressEventType.Reading_BeforeReadEntry + /// Indicates an entry is about to be read from the archive. + /// Meaningful properties: ArchiveName, EntriesTotal. + /// + /// + /// + /// + /// ZipProgressEventType.Reading_AfterReadEntry + /// Indicates an entry has just been read from the archive. + /// Meaningful properties: ArchiveName, EntriesTotal, CurrentEntry. + /// + /// + /// + /// + /// + /// + /// + /// + /// + public event EventHandler ReadProgress; + + private void OnReadStarted() + { + EventHandler rp = ReadProgress; + if (rp != null) + { + var e = ReadProgressEventArgs.Started(ArchiveNameForEvent); + rp(this, e); + } + } + + private void OnReadCompleted() + { + EventHandler rp = ReadProgress; + if (rp != null) + { + var e = ReadProgressEventArgs.Completed(ArchiveNameForEvent); + rp(this, e); + } + } + + internal void OnReadBytes(ZipEntry entry) + { + EventHandler rp = ReadProgress; + if (rp != null) + { + var e = ReadProgressEventArgs.ByteUpdate(ArchiveNameForEvent, + entry, + ReadStream.Position, + LengthOfReadStream); + rp(this, e); + } + } + + internal void OnReadEntry(bool before, ZipEntry entry) + { + EventHandler rp = ReadProgress; + if (rp != null) + { + ReadProgressEventArgs e = (before) + ? ReadProgressEventArgs.Before(ArchiveNameForEvent, _entries.Count) + : ReadProgressEventArgs.After(ArchiveNameForEvent, entry, _entries.Count); + rp(this, e); + } + } + + private Int64 _lengthOfReadStream = -99; + private Int64 LengthOfReadStream + { + get + { + if (_lengthOfReadStream == -99) + { + _lengthOfReadStream = (_ReadStreamIsOurs) + ? SharedUtilities.GetFileLength(_name) + : -1L; + } + return _lengthOfReadStream; + } + } + #endregion + + + #region Extract + /// + /// An event handler invoked before, during, and after extraction of + /// entries in the zip archive. + /// + /// + /// + /// + /// Depending on the particular event, different properties on the parameter are set. The following + /// table summarizes the available EventTypes and the conditions under + /// which this event handler is invoked with a + /// ExtractProgressEventArgs with the given EventType. + /// + /// + /// + /// + /// value of EntryType + /// Meaning and conditions + /// + /// + /// + /// ZipProgressEventType.Extracting_BeforeExtractAll + /// + /// Set when ExtractAll() begins. The ArchiveName, Overwrite, and + /// ExtractLocation properties are meaningful. + /// + /// + /// + /// ZipProgressEventType.Extracting_AfterExtractAll + /// + /// Set when ExtractAll() has completed. The ArchiveName, Overwrite, + /// and ExtractLocation properties are meaningful. + /// + /// + /// + /// + /// ZipProgressEventType.Extracting_BeforeExtractEntry + /// + /// Set when an Extract() on an entry in the ZipFile has begun. + /// Properties that are meaningful: ArchiveName, EntriesTotal, + /// CurrentEntry, Overwrite, ExtractLocation, EntriesExtracted. + /// + /// + /// + /// + /// ZipProgressEventType.Extracting_AfterExtractEntry + /// + /// Set when an Extract() on an entry in the ZipFile has completed. + /// Properties that are meaningful: ArchiveName, EntriesTotal, + /// CurrentEntry, Overwrite, ExtractLocation, EntriesExtracted. + /// + /// + /// + /// + /// ZipProgressEventType.Extracting_EntryBytesWritten + /// + /// Set within a call to Extract() on an entry in the ZipFile, as data + /// is extracted for the entry. Properties that are meaningful: + /// ArchiveName, CurrentEntry, BytesTransferred, TotalBytesToTransfer. + /// + /// + /// + /// + /// ZipProgressEventType.Extracting_ExtractEntryWouldOverwrite + /// + /// Set within a call to Extract() on an entry in the ZipFile, when the + /// extraction would overwrite an existing file. This event type is used + /// only when ExtractExistingFileAction on the ZipFile or + /// ZipEntry is set to InvokeExtractProgressEvent. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// private static bool justHadByteUpdate = false; + /// public static void ExtractProgress(object sender, ExtractProgressEventArgs e) + /// { + /// if(e.EventType == ZipProgressEventType.Extracting_EntryBytesWritten) + /// { + /// if (justHadByteUpdate) + /// Console.SetCursorPosition(0, Console.CursorTop); + /// + /// Console.Write(" {0}/{1} ({2:N0}%)", e.BytesTransferred, e.TotalBytesToTransfer, + /// e.BytesTransferred / (0.01 * e.TotalBytesToTransfer )); + /// justHadByteUpdate = true; + /// } + /// else if(e.EventType == ZipProgressEventType.Extracting_BeforeExtractEntry) + /// { + /// if (justHadByteUpdate) + /// Console.WriteLine(); + /// Console.WriteLine("Extracting: {0}", e.CurrentEntry.FileName); + /// justHadByteUpdate= false; + /// } + /// } + /// + /// public static ExtractZip(string zipToExtract, string directory) + /// { + /// string TargetDirectory= "extract"; + /// using (var zip = ZipFile.Read(zipToExtract)) { + /// zip.ExtractProgress += ExtractProgress; + /// foreach (var e in zip1) + /// { + /// e.Extract(TargetDirectory, true); + /// } + /// } + /// } + /// + /// + /// + /// Public Shared Sub Main(ByVal args As String()) + /// Dim ZipToUnpack As String = "C1P3SML.zip" + /// Dim TargetDir As String = "ExtractTest_Extract" + /// Console.WriteLine("Extracting file {0} to {1}", ZipToUnpack, TargetDir) + /// Using zip1 As ZipFile = ZipFile.Read(ZipToUnpack) + /// AddHandler zip1.ExtractProgress, AddressOf MyExtractProgress + /// Dim e As ZipEntry + /// For Each e In zip1 + /// e.Extract(TargetDir, True) + /// Next + /// End Using + /// End Sub + /// + /// Private Shared justHadByteUpdate As Boolean = False + /// + /// Public Shared Sub MyExtractProgress(ByVal sender As Object, ByVal e As ExtractProgressEventArgs) + /// If (e.EventType = ZipProgressEventType.Extracting_EntryBytesWritten) Then + /// If ExtractTest.justHadByteUpdate Then + /// Console.SetCursorPosition(0, Console.CursorTop) + /// End If + /// Console.Write(" {0}/{1} ({2:N0}%)", e.BytesTransferred, e.TotalBytesToTransfer, (CDbl(e.BytesTransferred) / (0.01 * e.TotalBytesToTransfer))) + /// ExtractTest.justHadByteUpdate = True + /// ElseIf (e.EventType = ZipProgressEventType.Extracting_BeforeExtractEntry) Then + /// If ExtractTest.justHadByteUpdate Then + /// Console.WriteLine + /// End If + /// Console.WriteLine("Extracting: {0}", e.CurrentEntry.FileName) + /// ExtractTest.justHadByteUpdate = False + /// End If + /// End Sub + /// + /// + /// + /// + /// + /// + public event EventHandler ExtractProgress; + + + + private void OnExtractEntry(int current, bool before, ZipEntry currentEntry, string path) + { + EventHandler ep = ExtractProgress; + if (ep != null) + { + var e = new ExtractProgressEventArgs(ArchiveNameForEvent, before, _entries.Count, current, currentEntry, path); + ep(this, e); + if (e.Cancel) + _extractOperationCanceled = true; + } + } + + + // Can be called from within ZipEntry._ExtractOne. + internal bool OnExtractBlock(ZipEntry entry, Int64 bytesWritten, Int64 totalBytesToWrite) + { + EventHandler ep = ExtractProgress; + if (ep != null) + { + var e = ExtractProgressEventArgs.ByteUpdate(ArchiveNameForEvent, entry, + bytesWritten, totalBytesToWrite); + ep(this, e); + if (e.Cancel) + _extractOperationCanceled = true; + } + return _extractOperationCanceled; + } + + + // Can be called from within ZipEntry.InternalExtract. + internal bool OnSingleEntryExtract(ZipEntry entry, string path, bool before) + { + EventHandler ep = ExtractProgress; + if (ep != null) + { + var e = (before) + ? ExtractProgressEventArgs.BeforeExtractEntry(ArchiveNameForEvent, entry, path) + : ExtractProgressEventArgs.AfterExtractEntry(ArchiveNameForEvent, entry, path); + ep(this, e); + if (e.Cancel) + _extractOperationCanceled = true; + } + return _extractOperationCanceled; + } + + internal bool OnExtractExisting(ZipEntry entry, string path) + { + EventHandler ep = ExtractProgress; + if (ep != null) + { + var e = ExtractProgressEventArgs.ExtractExisting(ArchiveNameForEvent, entry, path); + ep(this, e); + if (e.Cancel) + _extractOperationCanceled = true; + } + return _extractOperationCanceled; + } + + + private void OnExtractAllCompleted(string path) + { + EventHandler ep = ExtractProgress; + if (ep != null) + { + var e = ExtractProgressEventArgs.ExtractAllCompleted(ArchiveNameForEvent, + path ); + ep(this, e); + } + } + + + private void OnExtractAllStarted(string path) + { + EventHandler ep = ExtractProgress; + if (ep != null) + { + var e = ExtractProgressEventArgs.ExtractAllStarted(ArchiveNameForEvent, + path ); + ep(this, e); + } + } + + + #endregion + + + + #region Add + /// + /// An event handler invoked before, during, and after Adding entries to a zip archive. + /// + /// + /// + /// Adding a large number of entries to a zip file can take a long + /// time. For example, when calling on a + /// directory that contains 50,000 files, it could take 3 minutes or so. + /// This event handler allws an application to track the progress of the Add + /// operation, and to optionally cancel a lengthy Add operation. + /// + /// + /// + /// + /// + /// int _numEntriesToAdd= 0; + /// int _numEntriesAdded= 0; + /// void AddProgressHandler(object sender, AddProgressEventArgs e) + /// { + /// switch (e.EventType) + /// { + /// case ZipProgressEventType.Adding_Started: + /// Console.WriteLine("Adding files to the zip..."); + /// break; + /// case ZipProgressEventType.Adding_AfterAddEntry: + /// _numEntriesAdded++; + /// Console.WriteLine(String.Format("Adding file {0}/{1} :: {2}", + /// _numEntriesAdded, _numEntriesToAdd, e.CurrentEntry.FileName)); + /// break; + /// case ZipProgressEventType.Adding_Completed: + /// Console.WriteLine("Added all files"); + /// break; + /// } + /// } + /// + /// void CreateTheZip() + /// { + /// using (ZipFile zip = new ZipFile()) + /// { + /// zip.AddProgress += AddProgressHandler; + /// zip.AddDirectory(System.IO.Path.GetFileName(DirToZip)); + /// zip.Save(ZipFileToCreate); + /// } + /// } + /// + /// + /// + /// + /// + /// Private Sub AddProgressHandler(ByVal sender As Object, ByVal e As AddProgressEventArgs) + /// Select Case e.EventType + /// Case ZipProgressEventType.Adding_Started + /// Console.WriteLine("Adding files to the zip...") + /// Exit Select + /// Case ZipProgressEventType.Adding_AfterAddEntry + /// Console.WriteLine(String.Format("Adding file {0}", e.CurrentEntry.FileName)) + /// Exit Select + /// Case ZipProgressEventType.Adding_Completed + /// Console.WriteLine("Added all files") + /// Exit Select + /// End Select + /// End Sub + /// + /// Sub CreateTheZip() + /// Using zip as ZipFile = New ZipFile + /// AddHandler zip.AddProgress, AddressOf AddProgressHandler + /// zip.AddDirectory(System.IO.Path.GetFileName(DirToZip)) + /// zip.Save(ZipFileToCreate); + /// End Using + /// End Sub + /// + /// + /// + /// + /// + /// + /// + /// + public event EventHandler AddProgress; + + private void OnAddStarted() + { + EventHandler ap = AddProgress; + if (ap != null) + { + var e = AddProgressEventArgs.Started(ArchiveNameForEvent); + ap(this, e); + if (e.Cancel) // workitem 13371 + _addOperationCanceled = true; + } + } + + private void OnAddCompleted() + { + EventHandler ap = AddProgress; + if (ap != null) + { + var e = AddProgressEventArgs.Completed(ArchiveNameForEvent); + ap(this, e); + } + } + + internal void AfterAddEntry(ZipEntry entry) + { + EventHandler ap = AddProgress; + if (ap != null) + { + var e = AddProgressEventArgs.AfterEntry(ArchiveNameForEvent, entry, _entries.Count); + ap(this, e); + if (e.Cancel) // workitem 13371 + _addOperationCanceled = true; + } + } + + #endregion + + + + #region Error + /// + /// An event that is raised when an error occurs during open or read of files + /// while saving a zip archive. + /// + /// + /// + /// + /// Errors can occur as a file is being saved to the zip archive. For + /// example, the File.Open may fail, or a File.Read may fail, because of + /// lock conflicts or other reasons. If you add a handler to this event, + /// you can handle such errors in your own code. If you don't add a + /// handler, the library will throw an exception if it encounters an I/O + /// error during a call to Save(). + /// + /// + /// + /// Setting a handler implicitly sets to + /// ZipErrorAction.InvokeErrorEvent. + /// + /// + /// + /// The handler you add applies to all items that are + /// subsequently added to the ZipFile instance. If you set this + /// property after you have added items to the ZipFile, but before you + /// have called Save(), errors that occur while saving those items + /// will not cause the error handler to be invoked. + /// + /// + /// + /// If you want to handle any errors that occur with any entry in the zip + /// file using the same error handler, then add your error handler once, + /// before adding any entries to the zip archive. + /// + /// + /// + /// In the error handler method, you need to set the property on the + /// ZipErrorEventArgs.CurrentEntry. This communicates back to + /// DotNetZip what you would like to do with this particular error. Within + /// an error handler, if you set the ZipEntry.ZipErrorAction property + /// on the ZipEntry to ZipErrorAction.InvokeErrorEvent or if + /// you don't set it at all, the library will throw the exception. (It is the + /// same as if you had set the ZipEntry.ZipErrorAction property on the + /// ZipEntry to ZipErrorAction.Throw.) If you set the + /// ZipErrorEventArgs.Cancel to true, the entire Save() will be + /// canceled. + /// + /// + /// + /// In the case that you use ZipErrorAction.Skip, implying that + /// you want to skip the entry for which there's been an error, DotNetZip + /// tries to seek backwards in the output stream, and truncate all bytes + /// written on behalf of that particular entry. This works only if the + /// output stream is seekable. It will not work, for example, when using + /// ASPNET's Response.OutputStream. + /// + /// + /// + /// + /// + /// + /// This example shows how to use an event handler to handle + /// errors during save of the zip file. + /// + /// + /// public static void MyZipError(object sender, ZipErrorEventArgs e) + /// { + /// Console.WriteLine("Error saving {0}...", e.FileName); + /// Console.WriteLine(" Exception: {0}", e.exception); + /// ZipEntry entry = e.CurrentEntry; + /// string response = null; + /// // Ask the user whether he wants to skip this error or not + /// do + /// { + /// Console.Write("Retry, Skip, Throw, or Cancel ? (R/S/T/C) "); + /// response = Console.ReadLine(); + /// Console.WriteLine(); + /// + /// } while (response != null && + /// response[0]!='S' && response[0]!='s' && + /// response[0]!='R' && response[0]!='r' && + /// response[0]!='T' && response[0]!='t' && + /// response[0]!='C' && response[0]!='c'); + /// + /// e.Cancel = (response[0]=='C' || response[0]=='c'); + /// + /// if (response[0]=='S' || response[0]=='s') + /// entry.ZipErrorAction = ZipErrorAction.Skip; + /// else if (response[0]=='R' || response[0]=='r') + /// entry.ZipErrorAction = ZipErrorAction.Retry; + /// else if (response[0]=='T' || response[0]=='t') + /// entry.ZipErrorAction = ZipErrorAction.Throw; + /// } + /// + /// public void SaveTheFile() + /// { + /// string directoryToZip = "fodder"; + /// string directoryInArchive = "files"; + /// string zipFileToCreate = "Archive.zip"; + /// using (var zip = new ZipFile()) + /// { + /// // set the event handler before adding any entries + /// zip.ZipError += MyZipError; + /// zip.AddDirectory(directoryToZip, directoryInArchive); + /// zip.Save(zipFileToCreate); + /// } + /// } + /// + /// + /// + /// Private Sub MyZipError(ByVal sender As Object, ByVal e As Ionic.Zip.ZipErrorEventArgs) + /// ' At this point, the application could prompt the user for an action to take. + /// ' But in this case, this application will simply automatically skip the file, in case of error. + /// Console.WriteLine("Zip Error, entry {0}", e.CurrentEntry.FileName) + /// Console.WriteLine(" Exception: {0}", e.exception) + /// ' set the desired ZipErrorAction on the CurrentEntry to communicate that to DotNetZip + /// e.CurrentEntry.ZipErrorAction = Zip.ZipErrorAction.Skip + /// End Sub + /// + /// Public Sub SaveTheFile() + /// Dim directoryToZip As String = "fodder" + /// Dim directoryInArchive As String = "files" + /// Dim zipFileToCreate as String = "Archive.zip" + /// Using zipArchive As ZipFile = New ZipFile + /// ' set the event handler before adding any entries + /// AddHandler zipArchive.ZipError, AddressOf MyZipError + /// zipArchive.AddDirectory(directoryToZip, directoryInArchive) + /// zipArchive.Save(zipFileToCreate) + /// End Using + /// End Sub + /// + /// + /// + /// + /// + public event EventHandler ZipError; + + internal bool OnZipErrorSaving(ZipEntry entry, Exception exc) + { + if (ZipError != null) + { + lock (LOCK) + { + var e = ZipErrorEventArgs.Saving(this.Name, entry, exc); + ZipError(this, e); + if (e.Cancel) + _saveOperationCanceled = true; + } + } + return _saveOperationCanceled; + } + #endregion + + } +} diff --git a/DotNetZip/Zip/ZipFile.Extract.cs b/DotNetZip/Zip/ZipFile.Extract.cs new file mode 100644 index 0000000..531c0f3 --- /dev/null +++ b/DotNetZip/Zip/ZipFile.Extract.cs @@ -0,0 +1,298 @@ +// ZipFile.Extract.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2009 Dino Chiesa. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2011-July-31 14:45:18> +// +// ------------------------------------------------------------------ +// +// This module defines the methods for Extract operations on zip files. +// +// ------------------------------------------------------------------ +// + + +using System; +using System.IO; +using System.Collections.Generic; + +namespace Ionic.Zip +{ + + public partial class ZipFile + { + + /// + /// Extracts all of the items in the zip archive, to the specified path in the + /// filesystem. The path can be relative or fully-qualified. + /// + /// + /// + /// + /// This method will extract all entries in the ZipFile to the + /// specified path. + /// + /// + /// + /// If an extraction of a file from the zip archive would overwrite an + /// existing file in the filesystem, the action taken is dictated by the + /// ExtractExistingFile property, which overrides any setting you may have + /// made on individual ZipEntry instances. By default, if you have not + /// set that property on the ZipFile instance, the entry will not + /// be extracted, the existing file will not be overwritten and an + /// exception will be thrown. To change this, set the property, or use the + /// overload that allows you to + /// specify an ExtractExistingFileAction parameter. + /// + /// + /// + /// The action to take when an extract would overwrite an existing file + /// applies to all entries. If you want to set this on a per-entry basis, + /// then you must use one of the ZipEntry.Extract methods. + /// + /// + /// + /// This method will send verbose output messages to the , if it is set on the ZipFile + /// instance. + /// + /// + /// + /// You may wish to take advantage of the ExtractProgress event. + /// + /// + /// + /// About timestamps: When extracting a file entry from a zip archive, the + /// extracted file gets the last modified time of the entry as stored in + /// the archive. The archive may also store extended file timestamp + /// information, including last accessed and created times. If these are + /// present in the ZipEntry, then the extracted file will also get + /// these times. + /// + /// + /// + /// A Directory entry is somewhat different. It will get the times as + /// described for a file entry, but, if there are file entries in the zip + /// archive that, when extracted, appear in the just-created directory, + /// then when those file entries are extracted, the last modified and last + /// accessed times of the directory will change, as a side effect. The + /// result is that after an extraction of a directory and a number of + /// files within the directory, the last modified and last accessed + /// timestamps on the directory will reflect the time that the last file + /// was extracted into the directory, rather than the time stored in the + /// zip archive for the directory. + /// + /// + /// + /// To compensate, when extracting an archive with ExtractAll, + /// DotNetZip will extract all the file and directory entries as described + /// above, but it will then make a second pass on the directories, and + /// reset the times on the directories to reflect what is stored in the + /// zip archive. + /// + /// + /// + /// This compensation is performed only within the context of an + /// ExtractAll. If you call ZipEntry.Extract on a directory + /// entry, the timestamps on directory in the filesystem will reflect the + /// times stored in the zip. If you then call ZipEntry.Extract on + /// a file entry, which is extracted into the directory, the timestamps on + /// the directory will be updated to the current time. + /// + /// + /// + /// + /// This example extracts all the entries in a zip archive file, to the + /// specified target directory. The extraction will overwrite any + /// existing files silently. + /// + /// + /// String TargetDirectory= "unpack"; + /// using(ZipFile zip= ZipFile.Read(ZipFileToExtract)) + /// { + /// zip.ExtractExistingFile= ExtractExistingFileAction.OverwriteSilently; + /// zip.ExtractAll(TargetDirectory); + /// } + /// + /// + /// + /// Dim TargetDirectory As String = "unpack" + /// Using zip As ZipFile = ZipFile.Read(ZipFileToExtract) + /// zip.ExtractExistingFile= ExtractExistingFileAction.OverwriteSilently + /// zip.ExtractAll(TargetDirectory) + /// End Using + /// + /// + /// + /// + /// + /// + /// + /// The path to which the contents of the zipfile will be extracted. + /// The path can be relative or fully-qualified. + /// + /// + public void ExtractAll(string path) + { + _InternalExtractAll(path, true); + } + + + + /// + /// Extracts all of the items in the zip archive, to the specified path in the + /// filesystem, using the specified behavior when extraction would overwrite an + /// existing file. + /// + /// + /// + /// + /// + /// This method will extract all entries in the ZipFile to the specified + /// path. For an extraction that would overwrite an existing file, the behavior + /// is dictated by , which overrides any + /// setting you may have made on individual ZipEntry instances. + /// + /// + /// + /// The action to take when an extract would overwrite an existing file + /// applies to all entries. If you want to set this on a per-entry basis, + /// then you must use or one of the similar methods. + /// + /// + /// + /// Calling this method is equivalent to setting the property and then calling . + /// + /// + /// + /// This method will send verbose output messages to the + /// , if it is set on the ZipFile instance. + /// + /// + /// + /// + /// This example extracts all the entries in a zip archive file, to the + /// specified target directory. It does not overwrite any existing files. + /// + /// String TargetDirectory= "c:\\unpack"; + /// using(ZipFile zip= ZipFile.Read(ZipFileToExtract)) + /// { + /// zip.ExtractAll(TargetDirectory, ExtractExistingFileAction.DontOverwrite); + /// } + /// + /// + /// + /// Dim TargetDirectory As String = "c:\unpack" + /// Using zip As ZipFile = ZipFile.Read(ZipFileToExtract) + /// zip.ExtractAll(TargetDirectory, ExtractExistingFileAction.DontOverwrite) + /// End Using + /// + /// + /// + /// + /// The path to which the contents of the zipfile will be extracted. + /// The path can be relative or fully-qualified. + /// + /// + /// + /// The action to take if extraction would overwrite an existing file. + /// + /// + public void ExtractAll(string path, ExtractExistingFileAction extractExistingFile) + { + ExtractExistingFile = extractExistingFile; + _InternalExtractAll(path, true); + } + + + private void _InternalExtractAll(string path, bool overrideExtractExistingProperty) + { + bool header = Verbose; + _inExtractAll = true; + try + { + OnExtractAllStarted(path); + + int n = 0; + foreach (ZipEntry e in _entries.Values) + { + if (header) + { + StatusMessageTextWriter.WriteLine("\n{1,-22} {2,-8} {3,4} {4,-8} {0}", + "Name", "Modified", "Size", "Ratio", "Packed"); + StatusMessageTextWriter.WriteLine(new System.String('-', 72)); + header = false; + } + if (Verbose) + { + StatusMessageTextWriter.WriteLine("{1,-22} {2,-8} {3,4:F0}% {4,-8} {0}", + e.FileName, + e.LastModified.ToString("yyyy-MM-dd HH:mm:ss"), + e.UncompressedSize, + e.CompressionRatio, + e.CompressedSize); + if (!String.IsNullOrEmpty(e.Comment)) + StatusMessageTextWriter.WriteLine(" Comment: {0}", e.Comment); + } + e.Password = _Password; // this may be null + OnExtractEntry(n, true, e, path); + if (overrideExtractExistingProperty) + e.ExtractExistingFile = this.ExtractExistingFile; + e.Extract(path); + n++; + OnExtractEntry(n, false, e, path); + if (_extractOperationCanceled) + break; + } + + if (!_extractOperationCanceled) + { + // workitem 8264: + // now, set times on directory entries, again. + // The problem is, extracting a file changes the times on the parent + // directory. So after all files have been extracted, we have to + // run through the directories again. + foreach (ZipEntry e in _entries.Values) + { + // check if it is a directory + if ((e.IsDirectory) || (e.FileName.EndsWith("/"))) + { + string outputFile = (e.FileName.StartsWith("/")) + ? Path.Combine(path, e.FileName.Substring(1)) + : Path.Combine(path, e.FileName); + + e._SetTimes(outputFile, false); + } + } + OnExtractAllCompleted(path); + } + + } + finally + { + + _inExtractAll = false; + } + } + + + } +} diff --git a/DotNetZip/Zip/ZipFile.Read.cs b/DotNetZip/Zip/ZipFile.Read.cs new file mode 100644 index 0000000..3ee6e1d --- /dev/null +++ b/DotNetZip/Zip/ZipFile.Read.cs @@ -0,0 +1,1115 @@ +// ZipFile.Read.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2009-2011 Dino Chiesa. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2011-August-05 11:38:59> +// +// ------------------------------------------------------------------ +// +// This module defines the methods for Reading zip files. +// +// ------------------------------------------------------------------ +// + + +using System; +using System.IO; +using System.Collections.Generic; + +namespace Ionic.Zip +{ + /// + /// A class for collecting the various options that can be used when + /// Reading zip files for extraction or update. + /// + /// + /// + /// + /// When reading a zip file, there are several options an + /// application can set, to modify how the file is read, or what + /// the library does while reading. This class collects those + /// options into one container. + /// + /// + /// + /// Pass an instance of the ReadOptions class into the + /// ZipFile.Read() method. + /// + /// + /// . + /// . + /// + public class ReadOptions + { + /// + /// An event handler for Read operations. When opening large zip + /// archives, you may want to display a progress bar or other + /// indicator of status progress while reading. This parameter + /// allows you to specify a ReadProgress Event Handler directly. + /// When you call Read(), the progress event is invoked as + /// necessary. + /// + public EventHandler ReadProgress { get; set; } + + /// + /// The System.IO.TextWriter to use for writing verbose status messages + /// during operations on the zip archive. A console application may wish to + /// pass System.Console.Out to get messages on the Console. A graphical + /// or headless application may wish to capture the messages in a different + /// TextWriter, such as a System.IO.StringWriter. + /// + public TextWriter StatusMessageWriter { get; set; } + + /// + /// The System.Text.Encoding to use when reading in the zip archive. Be + /// careful specifying the encoding. If the value you use here is not the same + /// as the Encoding used when the zip archive was created (possibly by a + /// different archiver) you will get unexpected results and possibly exceptions. + /// + /// + /// + /// + public System.Text.Encoding @Encoding { get; set; } + } + + + public partial class ZipFile + { + /// + /// Reads a zip file archive and returns the instance. + /// + /// + /// + /// + /// The stream is read using the default System.Text.Encoding, which is the + /// IBM437 codepage. + /// + /// + /// + /// + /// Thrown if the ZipFile cannot be read. The implementation of this method + /// relies on System.IO.File.OpenRead, which can throw a variety of exceptions, + /// including specific exceptions if a file is not found, an unauthorized access + /// exception, exceptions for poorly formatted filenames, and so on. + /// + /// + /// + /// The name of the zip archive to open. This can be a fully-qualified or relative + /// pathname. + /// + /// + /// . + /// + /// The instance read from the zip archive. + /// + public static ZipFile Read(string fileName) + { + return ZipFile.Read(fileName, null, null, null); + } + + + /// + /// Reads a zip file archive from the named filesystem file using the + /// specified options. + /// + /// + /// + /// + /// This version of the Read() method allows the caller to pass + /// in a TextWriter an Encoding, via an instance of the + /// ReadOptions class. The ZipFile is read in using the + /// specified encoding for entries where UTF-8 encoding is not + /// explicitly specified. + /// + /// + /// + /// + /// + /// + /// This example shows how to read a zip file using the Big-5 Chinese + /// code page (950), and extract each entry in the zip file, while + /// sending status messages out to the Console. + /// + /// + /// + /// For this code to work as intended, the zipfile must have been + /// created using the big5 code page (CP950). This is typical, for + /// example, when using WinRar on a machine with CP950 set as the + /// default code page. In that case, the names of entries within the + /// Zip archive will be stored in that code page, and reading the zip + /// archive must be done using that code page. If the application did + /// not use the correct code page in ZipFile.Read(), then names of + /// entries within the zip archive would not be correctly retrieved. + /// + /// + /// + /// string zipToExtract = "MyArchive.zip"; + /// string extractDirectory = "extract"; + /// var options = new ReadOptions + /// { + /// StatusMessageWriter = System.Console.Out, + /// Encoding = System.Text.Encoding.GetEncoding(950) + /// }; + /// using (ZipFile zip = ZipFile.Read(zipToExtract, options)) + /// { + /// foreach (ZipEntry e in zip) + /// { + /// e.Extract(extractDirectory); + /// } + /// } + /// + /// + /// + /// + /// Dim zipToExtract as String = "MyArchive.zip" + /// Dim extractDirectory as String = "extract" + /// Dim options as New ReadOptions + /// options.Encoding = System.Text.Encoding.GetEncoding(950) + /// options.StatusMessageWriter = System.Console.Out + /// Using zip As ZipFile = ZipFile.Read(zipToExtract, options) + /// Dim e As ZipEntry + /// For Each e In zip + /// e.Extract(extractDirectory) + /// Next + /// End Using + /// + /// + /// + /// + /// + /// + /// + /// This example shows how to read a zip file using the default + /// code page, to remove entries that have a modified date before a given threshold, + /// sending status messages out to a StringWriter. + /// + /// + /// + /// var options = new ReadOptions + /// { + /// StatusMessageWriter = new System.IO.StringWriter() + /// }; + /// using (ZipFile zip = ZipFile.Read("PackedDocuments.zip", options)) + /// { + /// var Threshold = new DateTime(2007,7,4); + /// // We cannot remove the entry from the list, within the context of + /// // an enumeration of said list. + /// // So we add the doomed entry to a list to be removed later. + /// // pass 1: mark the entries for removal + /// var MarkedEntries = new System.Collections.Generic.List<ZipEntry>(); + /// foreach (ZipEntry e in zip) + /// { + /// if (e.LastModified < Threshold) + /// MarkedEntries.Add(e); + /// } + /// // pass 2: actually remove the entry. + /// foreach (ZipEntry zombie in MarkedEntries) + /// zip.RemoveEntry(zombie); + /// zip.Comment = "This archive has been updated."; + /// zip.Save(); + /// } + /// // can now use contents of sw, eg store in an audit log + /// + /// + /// + /// Dim options as New ReadOptions + /// options.StatusMessageWriter = New System.IO.StringWriter + /// Using zip As ZipFile = ZipFile.Read("PackedDocuments.zip", options) + /// Dim Threshold As New DateTime(2007, 7, 4) + /// ' We cannot remove the entry from the list, within the context of + /// ' an enumeration of said list. + /// ' So we add the doomed entry to a list to be removed later. + /// ' pass 1: mark the entries for removal + /// Dim MarkedEntries As New System.Collections.Generic.List(Of ZipEntry) + /// Dim e As ZipEntry + /// For Each e In zip + /// If (e.LastModified < Threshold) Then + /// MarkedEntries.Add(e) + /// End If + /// Next + /// ' pass 2: actually remove the entry. + /// Dim zombie As ZipEntry + /// For Each zombie In MarkedEntries + /// zip.RemoveEntry(zombie) + /// Next + /// zip.Comment = "This archive has been updated." + /// zip.Save + /// End Using + /// ' can now use contents of sw, eg store in an audit log + /// + /// + /// + /// + /// Thrown if the zipfile cannot be read. The implementation of + /// this method relies on System.IO.File.OpenRead, which + /// can throw a variety of exceptions, including specific + /// exceptions if a file is not found, an unauthorized access + /// exception, exceptions for poorly formatted filenames, and so + /// on. + /// + /// + /// + /// The name of the zip archive to open. + /// This can be a fully-qualified or relative pathname. + /// + /// + /// + /// The set of options to use when reading the zip file. + /// + /// + /// The ZipFile instance read from the zip archive. + /// + /// + /// + public static ZipFile Read(string fileName, + ReadOptions options) + { + if (options == null) + throw new ArgumentNullException("options"); + return Read(fileName, + options.StatusMessageWriter, + options.Encoding, + options.ReadProgress); + } + + /// + /// Reads a zip file archive using the specified text encoding, the specified + /// TextWriter for status messages, and the specified ReadProgress event handler, + /// and returns the instance. + /// + /// + /// + /// The name of the zip archive to open. + /// This can be a fully-qualified or relative pathname. + /// + /// + /// + /// An event handler for Read operations. + /// + /// + /// + /// The System.IO.TextWriter to use for writing verbose status messages + /// during operations on the zip archive. A console application may wish to + /// pass System.Console.Out to get messages on the Console. A graphical + /// or headless application may wish to capture the messages in a different + /// TextWriter, such as a System.IO.StringWriter. + /// + /// + /// + /// The System.Text.Encoding to use when reading in the zip archive. Be + /// careful specifying the encoding. If the value you use here is not the same + /// as the Encoding used when the zip archive was created (possibly by a + /// different archiver) you will get unexpected results and possibly exceptions. + /// + /// + /// The instance read from the zip archive. + /// + private static ZipFile Read(string fileName, + TextWriter statusMessageWriter, + System.Text.Encoding encoding, + EventHandler readProgress) + { + ZipFile zf = new ZipFile(); + zf.AlternateEncoding = encoding ?? DefaultEncoding; + zf.AlternateEncodingUsage = ZipOption.Always; + zf._StatusMessageTextWriter = statusMessageWriter; + zf._name = fileName; + if (readProgress != null) + zf.ReadProgress = readProgress; + + if (zf.Verbose) zf._StatusMessageTextWriter.WriteLine("reading from {0}...", fileName); + + ReadIntoInstance(zf); + zf._fileAlreadyExists = true; + + return zf; + } + + /// + /// Reads a zip archive from a stream. + /// + /// + /// + /// + /// + /// When reading from a file, it's probably easier to just use + /// ZipFile.Read(String, ReadOptions). This + /// overload is useful when when the zip archive content is + /// available from an already-open stream. The stream must be + /// open and readable and seekable when calling this method. The + /// stream is left open when the reading is completed. + /// + /// + /// + /// Using this overload, the stream is read using the default + /// System.Text.Encoding, which is the IBM437 + /// codepage. If you want to specify the encoding to use when + /// reading the zipfile content, see + /// ZipFile.Read(Stream, ReadOptions). This + /// + /// + /// + /// Reading of zip content begins at the current position in the + /// stream. This means if you have a stream that concatenates + /// regular data and zip data, if you position the open, readable + /// stream at the start of the zip data, you will be able to read + /// the zip archive using this constructor, or any of the ZipFile + /// constructors that accept a as + /// input. Some examples of where this might be useful: the zip + /// content is concatenated at the end of a regular EXE file, as + /// some self-extracting archives do. (Note: SFX files produced + /// by DotNetZip do not work this way; they can be read as normal + /// ZIP files). Another example might be a stream being read from + /// a database, where the zip content is embedded within an + /// aggregate stream of data. + /// + /// + /// + /// + /// + /// + /// This example shows how to Read zip content from a stream, and + /// extract one entry into a different stream. In this example, + /// the filename "NameOfEntryInArchive.doc", refers only to the + /// name of the entry within the zip archive. A file by that + /// name is not created in the filesystem. The I/O is done + /// strictly with the given streams. + /// + /// + /// + /// using (ZipFile zip = ZipFile.Read(InputStream)) + /// { + /// zip.Extract("NameOfEntryInArchive.doc", OutputStream); + /// } + /// + /// + /// + /// Using zip as ZipFile = ZipFile.Read(InputStream) + /// zip.Extract("NameOfEntryInArchive.doc", OutputStream) + /// End Using + /// + /// + /// + /// the stream containing the zip data. + /// + /// The ZipFile instance read from the stream + /// + public static ZipFile Read(Stream zipStream) + { + return Read(zipStream, null, null, null); + } + + /// + /// Reads a zip file archive from the given stream using the + /// specified options. + /// + /// + /// + /// + /// + /// When reading from a file, it's probably easier to just use + /// ZipFile.Read(String, ReadOptions). This + /// overload is useful when when the zip archive content is + /// available from an already-open stream. The stream must be + /// open and readable and seekable when calling this method. The + /// stream is left open when the reading is completed. + /// + /// + /// + /// Reading of zip content begins at the current position in the + /// stream. This means if you have a stream that concatenates + /// regular data and zip data, if you position the open, readable + /// stream at the start of the zip data, you will be able to read + /// the zip archive using this constructor, or any of the ZipFile + /// constructors that accept a as + /// input. Some examples of where this might be useful: the zip + /// content is concatenated at the end of a regular EXE file, as + /// some self-extracting archives do. (Note: SFX files produced + /// by DotNetZip do not work this way; they can be read as normal + /// ZIP files). Another example might be a stream being read from + /// a database, where the zip content is embedded within an + /// aggregate stream of data. + /// + /// + /// + /// the stream containing the zip data. + /// + /// + /// The set of options to use when reading the zip file. + /// + /// + /// + /// Thrown if the zip archive cannot be read. + /// + /// + /// The ZipFile instance read from the stream. + /// + /// + /// + public static ZipFile Read(Stream zipStream, ReadOptions options) + { + if (options == null) + throw new ArgumentNullException("options"); + + return Read(zipStream, + options.StatusMessageWriter, + options.Encoding, + options.ReadProgress); + } + + + + /// + /// Reads a zip archive from a stream, using the specified text Encoding, the + /// specified TextWriter for status messages, + /// and the specified ReadProgress event handler. + /// + /// + /// + /// + /// Reading of zip content begins at the current position in the stream. This + /// means if you have a stream that concatenates regular data and zip data, if + /// you position the open, readable stream at the start of the zip data, you + /// will be able to read the zip archive using this constructor, or any of the + /// ZipFile constructors that accept a as + /// input. Some examples of where this might be useful: the zip content is + /// concatenated at the end of a regular EXE file, as some self-extracting + /// archives do. (Note: SFX files produced by DotNetZip do not work this + /// way). Another example might be a stream being read from a database, where + /// the zip content is embedded within an aggregate stream of data. + /// + /// + /// + /// the stream containing the zip data. + /// + /// + /// The System.IO.TextWriter to which verbose status messages are written + /// during operations on the ZipFile. For example, in a console + /// application, System.Console.Out works, and will get a message for each entry + /// added to the ZipFile. If the TextWriter is null, no verbose messages + /// are written. + /// + /// + /// + /// The text encoding to use when reading entries that do not have the UTF-8 + /// encoding bit set. Be careful specifying the encoding. If the value you use + /// here is not the same as the Encoding used when the zip archive was created + /// (possibly by a different archiver) you will get unexpected results and + /// possibly exceptions. See the + /// property for more information. + /// + /// + /// + /// An event handler for Read operations. + /// + /// + /// an instance of ZipFile + private static ZipFile Read(Stream zipStream, + TextWriter statusMessageWriter, + System.Text.Encoding encoding, + EventHandler readProgress) + { + if (zipStream == null) + throw new ArgumentNullException("zipStream"); + + ZipFile zf = new ZipFile(); + zf._StatusMessageTextWriter = statusMessageWriter; + zf._alternateEncoding = encoding ?? ZipFile.DefaultEncoding; + zf._alternateEncodingUsage = ZipOption.Always; + if (readProgress != null) + zf.ReadProgress += readProgress; + zf._readstream = (zipStream.Position == 0L) + ? zipStream + : new OffsetStream(zipStream); + zf._ReadStreamIsOurs = false; + if (zf.Verbose) zf._StatusMessageTextWriter.WriteLine("reading from stream..."); + + ReadIntoInstance(zf); + return zf; + } + + + + private static void ReadIntoInstance(ZipFile zf) + { + Stream s = zf.ReadStream; + try + { + zf._readName = zf._name; // workitem 13915 + if (!s.CanSeek) + { + ReadIntoInstance_Orig(zf); + return; + } + + zf.OnReadStarted(); + + // change for workitem 8098 + //zf._originPosition = s.Position; + + // Try reading the central directory, rather than scanning the file. + + uint datum = ReadFirstFourBytes(s); + + if (datum == ZipConstants.EndOfCentralDirectorySignature) + return; + + + // start at the end of the file... + // seek backwards a bit, then look for the EoCD signature. + int nTries = 0; + bool success = false; + + // The size of the end-of-central-directory-footer plus 2 bytes is 18. + // This implies an archive comment length of 0. We'll add a margin of + // safety and start "in front" of that, when looking for the + // EndOfCentralDirectorySignature + long posn = s.Length - 64; + long maxSeekback = Math.Max(s.Length - 0x4000, 10); + do + { + if (posn < 0) posn = 0; // BOF + s.Seek(posn, SeekOrigin.Begin); + long bytesRead = SharedUtilities.FindSignature(s, (int)ZipConstants.EndOfCentralDirectorySignature); + if (bytesRead != -1) + success = true; + else + { + if (posn==0) break; // started at the BOF and found nothing + nTries++; + // Weird: with NETCF, negative offsets from SeekOrigin.End DO + // NOT WORK. So rather than seek a negative offset, we seek + // from SeekOrigin.Begin using a smaller number. + posn -= (32 * (nTries + 1) * nTries); + } + } + while (!success && posn > maxSeekback); + + if (success) + { + // workitem 8299 + zf._locEndOfCDS = s.Position - 4; + + byte[] block = new byte[16]; + s.Read(block, 0, block.Length); + + zf._diskNumberWithCd = BitConverter.ToUInt16(block, 2); + + if (zf._diskNumberWithCd == 0xFFFF) + throw new ZipException("Spanned archives with more than 65534 segments are not supported at this time."); + + zf._diskNumberWithCd++; // I think the number in the file differs from reality by 1 + + int i = 12; + + uint offset32 = (uint) BitConverter.ToUInt32(block, i); + if (offset32 == 0xFFFFFFFF) + { + Zip64SeekToCentralDirectory(zf); + } + else + { + zf._OffsetOfCentralDirectory = offset32; + // change for workitem 8098 + s.Seek(offset32, SeekOrigin.Begin); + } + + ReadCentralDirectory(zf); + } + else + { + // Could not find the central directory. + // Fallback to the old method. + // workitem 8098: ok + //s.Seek(zf._originPosition, SeekOrigin.Begin); + s.Seek(0L, SeekOrigin.Begin); + ReadIntoInstance_Orig(zf); + } + } + catch (Exception ex1) + { + if (zf._ReadStreamIsOurs && zf._readstream != null) + { + try + { +#if NETCF + zf._readstream.Close(); +#else + zf._readstream.Dispose(); +#endif + zf._readstream = null; + } + finally { } + } + + throw new ZipException("Cannot read that as a ZipFile", ex1); + } + + // the instance has been read in + zf._contentsChanged = false; + } + + + + private static void Zip64SeekToCentralDirectory(ZipFile zf) + { + Stream s = zf.ReadStream; + byte[] block = new byte[16]; + + // seek back to find the ZIP64 EoCD. + // I think this might not work for .NET CF ? + s.Seek(-40, SeekOrigin.Current); + s.Read(block, 0, 16); + + Int64 offset64 = BitConverter.ToInt64(block, 8); + zf._OffsetOfCentralDirectory = 0xFFFFFFFF; + zf._OffsetOfCentralDirectory64 = offset64; + // change for workitem 8098 + s.Seek(offset64, SeekOrigin.Begin); + //zf.SeekFromOrigin(Offset64); + + uint datum = (uint)Ionic.Zip.SharedUtilities.ReadInt(s); + if (datum != ZipConstants.Zip64EndOfCentralDirectoryRecordSignature) + throw new BadReadException(String.Format(" Bad signature (0x{0:X8}) looking for ZIP64 EoCD Record at position 0x{1:X8}", datum, s.Position)); + + s.Read(block, 0, 8); + Int64 Size = BitConverter.ToInt64(block, 0); + + block = new byte[Size]; + s.Read(block, 0, block.Length); + + offset64 = BitConverter.ToInt64(block, 36); + // change for workitem 8098 + s.Seek(offset64, SeekOrigin.Begin); + //zf.SeekFromOrigin(Offset64); + } + + + private static uint ReadFirstFourBytes(Stream s) + { + uint datum = (uint)Ionic.Zip.SharedUtilities.ReadInt(s); + return datum; + } + + + + private static void ReadCentralDirectory(ZipFile zf) + { + // We must have the central directory footer record, in order to properly + // read zip dir entries from the central directory. This because the logic + // knows when to open a spanned file when the volume number for the central + // directory differs from the volume number for the zip entry. The + // _diskNumberWithCd was set when originally finding the offset for the + // start of the Central Directory. + + // workitem 9214 + bool inputUsesZip64 = false; + ZipEntry de; + // in lieu of hashset, use a dictionary + var previouslySeen = new Dictionary(); + while ((de = ZipEntry.ReadDirEntry(zf, previouslySeen)) != null) + { + try + { + de.ResetDirEntry(); + zf.OnReadEntry(true, null); + + if (zf.Verbose) + zf.StatusMessageTextWriter.WriteLine("entry {0}", de.FileName); + zf._entries.Add(de.FileName, de); + // workitem 9214 + if (de._InputUsesZip64) inputUsesZip64 = true; + previouslySeen.Add(de.FileName, null); // to prevent dupes + } + catch (Exception ex) + { + + } + } + + // workitem 9214; auto-set the zip64 flag + if (inputUsesZip64) zf.UseZip64WhenSaving = Zip64Option.Always; + + // workitem 8299 + if (zf._locEndOfCDS > 0) + zf.ReadStream.Seek(zf._locEndOfCDS, SeekOrigin.Begin); + + ReadCentralDirectoryFooter(zf); + + if (zf.Verbose && !String.IsNullOrEmpty(zf.Comment)) + zf.StatusMessageTextWriter.WriteLine("Zip file Comment: {0}", zf.Comment); + + // We keep the read stream open after reading. + + if (zf.Verbose) + zf.StatusMessageTextWriter.WriteLine("read in {0} entries.", zf._entries.Count); + + zf.OnReadCompleted(); + } + + + + + // build the TOC by reading each entry in the file. + private static void ReadIntoInstance_Orig(ZipFile zf) + { + zf.OnReadStarted(); + //zf._entries = new System.Collections.Generic.List(); + zf._entries = new System.Collections.Generic.Dictionary(); + + ZipEntry e; + if (zf.Verbose) + if (zf.Name == null) + zf.StatusMessageTextWriter.WriteLine("Reading zip from stream..."); + else + zf.StatusMessageTextWriter.WriteLine("Reading zip {0}...", zf.Name); + + // work item 6647: PK00 (packed to removable disk) + bool firstEntry = true; + ZipContainer zc = new ZipContainer(zf); + while ((e = ZipEntry.ReadEntry(zc, firstEntry)) != null) + { + if (zf.Verbose) + zf.StatusMessageTextWriter.WriteLine(" {0}", e.FileName); + + zf._entries.Add(e.FileName,e); + firstEntry = false; + } + + // read the zipfile's central directory structure here. + // workitem 9912 + // But, because it may be corrupted, ignore errors. + try + { + ZipEntry de; + // in lieu of hashset, use a dictionary + var previouslySeen = new Dictionary(); + while ((de = ZipEntry.ReadDirEntry(zf, previouslySeen)) != null) + { + // Housekeeping: Since ZipFile exposes ZipEntry elements in the enumerator, + // we need to copy the comment that we grab from the ZipDirEntry + // into the ZipEntry, so the application can access the comment. + // Also since ZipEntry is used to Write zip files, we need to copy the + // file attributes to the ZipEntry as appropriate. + ZipEntry e1 = zf._entries[de.FileName]; + if (e1 != null) + { + e1._Comment = de.Comment; + if (de.IsDirectory) e1.MarkAsDirectory(); + } + previouslySeen.Add(de.FileName,null); // to prevent dupes + } + + // workitem 8299 + if (zf._locEndOfCDS > 0) + zf.ReadStream.Seek(zf._locEndOfCDS, SeekOrigin.Begin); + + ReadCentralDirectoryFooter(zf); + + if (zf.Verbose && !String.IsNullOrEmpty(zf.Comment)) + zf.StatusMessageTextWriter.WriteLine("Zip file Comment: {0}", zf.Comment); + } + catch (ZipException) { } + catch (IOException) { } + + zf.OnReadCompleted(); + } + + + + + private static void ReadCentralDirectoryFooter(ZipFile zf) + { + Stream s = zf.ReadStream; + int signature = Ionic.Zip.SharedUtilities.ReadSignature(s); + + byte[] block = null; + int j = 0; + if (signature == ZipConstants.Zip64EndOfCentralDirectoryRecordSignature) + { + // We have a ZIP64 EOCD + // This data block is 4 bytes sig, 8 bytes size, 44 bytes fixed data, + // followed by a variable-sized extension block. We have read the sig already. + // 8 - datasize (64 bits) + // 2 - version made by + // 2 - version needed to extract + // 4 - number of this disk + // 4 - number of the disk with the start of the CD + // 8 - total number of entries in the CD on this disk + // 8 - total number of entries in the CD + // 8 - size of the CD + // 8 - offset of the CD + // ----------------------- + // 52 bytes + + block = new byte[8 + 44]; + s.Read(block, 0, block.Length); + + Int64 DataSize = BitConverter.ToInt64(block, 0); // == 44 + the variable length + + if (DataSize < 44) + throw new ZipException("Bad size in the ZIP64 Central Directory."); + + zf._versionMadeBy = BitConverter.ToUInt16(block, j); + j += 2; + zf._versionNeededToExtract = BitConverter.ToUInt16(block, j); + j += 2; + zf._diskNumberWithCd = BitConverter.ToUInt32(block, j); + j += 2; + + //zf._diskNumberWithCd++; // hack!! + + // read the extended block + block = new byte[DataSize - 44]; + s.Read(block, 0, block.Length); + // discard the result + + signature = Ionic.Zip.SharedUtilities.ReadSignature(s); + if (signature != ZipConstants.Zip64EndOfCentralDirectoryLocatorSignature) + throw new ZipException("Inconsistent metadata in the ZIP64 Central Directory."); + + block = new byte[16]; + s.Read(block, 0, block.Length); + // discard the result + + signature = Ionic.Zip.SharedUtilities.ReadSignature(s); + } + + // Throw if this is not a signature for "end of central directory record" + // This is a sanity check. + if (signature != ZipConstants.EndOfCentralDirectorySignature) + { + s.Seek(-4, SeekOrigin.Current); + throw new BadReadException(String.Format("Bad signature ({0:X8}) at position 0x{1:X8}", + signature, s.Position)); + } + + // read the End-of-Central-Directory-Record + block = new byte[16]; + zf.ReadStream.Read(block, 0, block.Length); + + // off sz data + // ------------------------------------------------------- + // 0 4 end of central dir signature (0x06054b50) + // 4 2 number of this disk + // 6 2 number of the disk with start of the central directory + // 8 2 total number of entries in the central directory on this disk + // 10 2 total number of entries in the central directory + // 12 4 size of the central directory + // 16 4 offset of start of central directory with respect to the starting disk number + // 20 2 ZIP file comment length + // 22 ?? ZIP file comment + + if (zf._diskNumberWithCd == 0) + { + zf._diskNumberWithCd = BitConverter.ToUInt16(block, 2); + //zf._diskNumberWithCd++; // hack!! + } + + // read the comment here + ReadZipFileComment(zf); + } + + + + private static void ReadZipFileComment(ZipFile zf) + { + // read the comment here + byte[] block = new byte[2]; + zf.ReadStream.Read(block, 0, block.Length); + + Int16 commentLength = (short)(block[0] + block[1] * 256); + if (commentLength > 0) + { + block = new byte[commentLength]; + zf.ReadStream.Read(block, 0, block.Length); + + // workitem 10392 - prefer ProvisionalAlternateEncoding, + // first. The fix for workitem 6513 tried to use UTF8 + // only as necessary, but that is impossible to test + // for, in this direction. There's no way to know what + // characters the already-encoded bytes refer + // to. Therefore, must do what the user tells us. + + string s1 = zf.AlternateEncoding.GetString(block, 0, block.Length); + zf.Comment = s1; + } + } + + + // private static bool BlocksAreEqual(byte[] a, byte[] b) + // { + // if (a.Length != b.Length) return false; + // for (int i = 0; i < a.Length; i++) + // { + // if (a[i] != b[i]) return false; + // } + // return true; + // } + + + + /// + /// Checks the given file to see if it appears to be a valid zip file. + /// + /// + /// + /// + /// Calling this method is equivalent to calling with the testExtract parameter set to false. + /// + /// + /// + /// The file to check. + /// true if the file appears to be a zip file. + public static bool IsZipFile(string fileName) + { + return IsZipFile(fileName, false); + } + + + /// + /// Checks a file to see if it is a valid zip file. + /// + /// + /// + /// + /// This method opens the specified zip file, reads in the zip archive, + /// verifying the ZIP metadata as it reads. + /// + /// + /// + /// If everything succeeds, then the method returns true. If anything fails - + /// for example if an incorrect signature or CRC is found, indicating a + /// corrupt file, the the method returns false. This method also returns + /// false for a file that does not exist. + /// + /// + /// + /// If is true, as part of its check, this + /// method reads in the content for each entry, expands it, and checks CRCs. + /// This provides an additional check beyond verifying the zip header and + /// directory data. + /// + /// + /// + /// If is true, and if any of the zip entries + /// are protected with a password, this method will return false. If you want + /// to verify a ZipFile that has entries which are protected with a + /// password, you will need to do that manually. + /// + /// + /// + /// + /// The zip file to check. + /// true if the caller wants to extract each entry. + /// true if the file contains a valid zip file. + public static bool IsZipFile(string fileName, bool testExtract) + { + bool result = false; + try + { + if (!File.Exists(fileName)) return false; + + using (var s = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + result = IsZipFile(s, testExtract); + } + } + catch (IOException) { } + catch (ZipException) { } + return result; + } + + + /// + /// Checks a stream to see if it contains a valid zip archive. + /// + /// + /// + /// + /// This method reads the zip archive contained in the specified stream, verifying + /// the ZIP metadata as it reads. If testExtract is true, this method also extracts + /// each entry in the archive, dumping all the bits into . + /// + /// + /// + /// If everything succeeds, then the method returns true. If anything fails - + /// for example if an incorrect signature or CRC is found, indicating a corrupt + /// file, the the method returns false. This method also returns false for a + /// file that does not exist. + /// + /// + /// + /// If testExtract is true, this method reads in the content for each + /// entry, expands it, and checks CRCs. This provides an additional check + /// beyond verifying the zip header data. + /// + /// + /// + /// If testExtract is true, and if any of the zip entries are protected + /// with a password, this method will return false. If you want to verify a + /// ZipFile that has entries which are protected with a password, you will need + /// to do that manually. + /// + /// + /// + /// + /// + /// The stream to check. + /// true if the caller wants to extract each entry. + /// true if the stream contains a valid zip archive. + public static bool IsZipFile(Stream stream, bool testExtract) + { + if (stream == null) + throw new ArgumentNullException("stream"); + + bool result = false; + try + { + if (!stream.CanRead) return false; + + var bitBucket = Stream.Null; + + using (ZipFile zip1 = ZipFile.Read(stream, null, null, null)) + { + if (testExtract) + { + foreach (var e in zip1) + { + if (!e.IsDirectory) + { + e.Extract(bitBucket); + } + } + } + } + result = true; + } + catch (IOException) { } + catch (ZipException) { } + return result; + } + + + + + } + +} diff --git a/DotNetZip/Zip/ZipFile.Save.cs b/DotNetZip/Zip/ZipFile.Save.cs new file mode 100644 index 0000000..9b07751 --- /dev/null +++ b/DotNetZip/Zip/ZipFile.Save.cs @@ -0,0 +1,964 @@ +// ZipFile.Save.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2009 Dino Chiesa. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2011-August-05 13:31:23> +// +// ------------------------------------------------------------------ +// +// This module defines the methods for Save operations on zip files. +// +// ------------------------------------------------------------------ +// + + +using System; +using System.IO; +using System.Collections.Generic; + +namespace Ionic.Zip +{ + + public partial class ZipFile + { + + /// + /// Delete file with retry on UnauthorizedAccessException. + /// + /// + /// + /// + /// When calling File.Delete() on a file that has been "recently" + /// created, the call sometimes fails with + /// UnauthorizedAccessException. This method simply retries the Delete 3 + /// times with a sleep between tries. + /// + /// + /// + /// the name of the file to be deleted + private void DeleteFileWithRetry(string filename) + { + bool done = false; + int nRetries = 3; + for (int i=0; i < nRetries && !done; i++) + { + try + { + File.Delete(filename); + done = true; + } + catch (System.UnauthorizedAccessException) + { + Console.WriteLine("************************************************** Retry delete."); + System.Threading.Thread.Sleep(200+i*200); + } + } + } + + + /// + /// Saves the Zip archive to a file, specified by the Name property of the + /// ZipFile. + /// + /// + /// + /// + /// The ZipFile instance is written to storage, typically a zip file + /// in a filesystem, only when the caller calls Save. In the typical + /// case, the Save operation writes the zip content to a temporary file, and + /// then renames the temporary file to the desired name. If necessary, this + /// method will delete a pre-existing file before the rename. + /// + /// + /// + /// The property is specified either explicitly, + /// or implicitly using one of the parameterized ZipFile constructors. For + /// COM Automation clients, the Name property must be set explicitly, + /// because COM Automation clients cannot call parameterized constructors. + /// + /// + /// + /// When using a filesystem file for the Zip output, it is possible to call + /// Save multiple times on the ZipFile instance. With each + /// call the zip content is re-written to the same output file. + /// + /// + /// + /// Data for entries that have been added to the ZipFile instance is + /// written to the output when the Save method is called. This means + /// that the input streams for those entries must be available at the time + /// the application calls Save. If, for example, the application + /// adds entries with AddEntry using a dynamically-allocated + /// MemoryStream, the memory stream must not have been disposed + /// before the call to Save. See the property for more discussion of the + /// availability requirements of the input stream for an entry, and an + /// approach for providing just-in-time stream lifecycle management. + /// + /// + /// + /// + /// + /// + /// + /// Thrown if you haven't specified a location or stream for saving the zip, + /// either in the constructor or by setting the Name property, or if you try + /// to save a regular zip archive to a filename with a .exe extension. + /// + /// + /// + /// Thrown if is non-zero, and the number + /// of segments that would be generated for the spanned zip file during the + /// save operation exceeds 99. If this happens, you need to increase the + /// segment size. + /// + /// + public void Save() + { + try + { + bool thisSaveUsedZip64 = false; + _saveOperationCanceled = false; + _numberOfSegmentsForMostRecentSave = 0; + OnSaveStarted(); + + if (WriteStream == null) + throw new BadStateException("You haven't specified where to save the zip."); + + if (_name != null && _name.EndsWith(".exe") && !_SavingSfx) + throw new BadStateException("You specified an EXE for a plain zip file."); + + // check if modified, before saving. + if (!_contentsChanged) + { + OnSaveCompleted(); + if (Verbose) StatusMessageTextWriter.WriteLine("No save is necessary...."); + return; + } + + Reset(true); + + if (Verbose) StatusMessageTextWriter.WriteLine("saving...."); + + // validate the number of entries + if (_entries.Count >= 0xFFFF && _zip64 == Zip64Option.Never) + throw new ZipException("The number of entries is 65535 or greater. Consider setting the UseZip64WhenSaving property on the ZipFile instance."); + + + // write an entry in the zip for each file + int n = 0; + // workitem 9831 + ICollection c = (SortEntriesBeforeSaving) ? EntriesSorted : Entries; + foreach (ZipEntry e in c) // _entries.Values + { + OnSaveEntry(n, e, true); + e.Write(WriteStream); + if (_saveOperationCanceled) + break; + + n++; + OnSaveEntry(n, e, false); + if (_saveOperationCanceled) + break; + + // Some entries can be skipped during the save. + if (e.IncludedInMostRecentSave) + thisSaveUsedZip64 |= e.OutputUsedZip64.Value; + } + + + + if (_saveOperationCanceled) + return; + + var zss = WriteStream as ZipSegmentedStream; + + _numberOfSegmentsForMostRecentSave = (zss!=null) + ? zss.CurrentSegment + : 1; + + bool directoryNeededZip64 = + ZipOutput.WriteCentralDirectoryStructure + (WriteStream, + c, + _numberOfSegmentsForMostRecentSave, + _zip64, + Comment, + new ZipContainer(this)); + + OnSaveEvent(ZipProgressEventType.Saving_AfterSaveTempArchive); + + _hasBeenSaved = true; + _contentsChanged = false; + + thisSaveUsedZip64 |= directoryNeededZip64; + _OutputUsesZip64 = new Nullable(thisSaveUsedZip64); + + + // do the rename as necessary + if (_name != null && + (_temporaryFileName!=null || zss != null)) + { + // _temporaryFileName may remain null if we are writing to a stream. + // only close the stream if there is a file behind it. +#if NETCF + WriteStream.Close(); +#else + WriteStream.Dispose(); +#endif + if (_saveOperationCanceled) + return; + + if (_fileAlreadyExists && this._readstream != null) + { + // This means we opened and read a zip file. + // If we are now saving to the same file, we need to close the + // orig file, first. + this._readstream.Close(); + this._readstream = null; + // the archiveStream for each entry needs to be null + foreach (var e in c) + { + var zss1 = e._archiveStream as ZipSegmentedStream; + if (zss1 != null) +#if NETCF + zss1.Close(); +#else + zss1.Dispose(); +#endif + e._archiveStream = null; + } + } + + string tmpName = null; + if (File.Exists(_name)) + { + // the steps: + // + // 1. Delete tmpName + // 2. move existing zip to tmpName + // 3. rename (File.Move) working file to name of existing zip + // 4. delete tmpName + // + // This series of steps avoids the exception, + // System.IO.IOException: + // "Cannot create a file when that file already exists." + // + // Cannot just call File.Replace() here because + // there is a possibility that the TEMP volume is different + // that the volume for the final file (c:\ vs d:\). + // So we need to do a Delete+Move pair. + // + // But, when doing the delete, Windows allows a process to + // delete the file, even though it is held open by, say, a + // virus scanner. It gets internally marked as "delete + // pending". The file does not actually get removed from the + // file system, it is still there after the File.Delete + // call. + // + // Therefore, we need to move the existing zip, which may be + // held open, to some other name. Then rename our working + // file to the desired name, then delete (possibly delete + // pending) the "other name". + // + // Ideally this would be transactional. It's possible that the + // delete succeeds and the move fails. Lacking transactions, if + // this kind of failure happens, we're hosed, and this logic will + // throw on the next File.Move(). + // + //File.Delete(_name); + // workitem 10447 +#if NETCF || SILVERLIGHT + tmpName = _name + "." + SharedUtilities.GenerateRandomStringImpl(8,0) + ".tmp"; +#else + tmpName = _name + "." + Path.GetRandomFileName(); +#endif + if (File.Exists(tmpName)) + DeleteFileWithRetry(tmpName); + File.Move(_name, tmpName); + } + + OnSaveEvent(ZipProgressEventType.Saving_BeforeRenameTempArchive); + File.Move((zss != null) ? zss.CurrentTempName : _temporaryFileName, + _name); + + OnSaveEvent(ZipProgressEventType.Saving_AfterRenameTempArchive); + + if (tmpName != null) + { + try + { + // not critical + if (File.Exists(tmpName)) + File.Delete(tmpName); + } + catch + { + // don't care about exceptions here. + } + + } + _fileAlreadyExists = true; + } + + NotifyEntriesSaveComplete(c); + OnSaveCompleted(); + _JustSaved = true; + } + + // workitem 5043 + finally + { + CleanupAfterSaveOperation(); + } + + return; + } + + + + private static void NotifyEntriesSaveComplete(ICollection c) + { + foreach (ZipEntry e in c) + { + e.NotifySaveComplete(); + } + } + + + private void RemoveTempFile() + { + try + { + if (File.Exists(_temporaryFileName)) + { + File.Delete(_temporaryFileName); + } + } + catch (IOException ex1) + { + if (Verbose) + StatusMessageTextWriter.WriteLine("ZipFile::Save: could not delete temp file: {0}.", ex1.Message); + } + } + + + private void CleanupAfterSaveOperation() + { + if (_name != null) + { + // close the stream if there is a file behind it. + if (_writestream != null) + { + try + { + // workitem 7704 +#if NETCF + _writestream.Close(); +#else + _writestream.Dispose(); +#endif + } + catch (System.IO.IOException) { } + } + _writestream = null; + + if (_temporaryFileName != null) + { + RemoveTempFile(); + _temporaryFileName = null; + } + } + } + + + /// + /// Save the file to a new zipfile, with the given name. + /// + /// + /// + /// + /// This method allows the application to explicitly specify the name of the zip + /// file when saving. Use this when creating a new zip file, or when + /// updating a zip archive. + /// + /// + /// + /// An application can also save a zip archive in several places by calling this + /// method multiple times in succession, with different filenames. + /// + /// + /// + /// The ZipFile instance is written to storage, typically a zip file in a + /// filesystem, only when the caller calls Save. The Save operation writes + /// the zip content to a temporary file, and then renames the temporary file + /// to the desired name. If necessary, this method will delete a pre-existing file + /// before the rename. + /// + /// + /// + /// + /// + /// Thrown if you specify a directory for the filename. + /// + /// + /// + /// The name of the zip archive to save to. Existing files will + /// be overwritten with great prejudice. + /// + /// + /// + /// This example shows how to create and Save a zip file. + /// + /// using (ZipFile zip = new ZipFile()) + /// { + /// zip.AddDirectory(@"c:\reports\January"); + /// zip.Save("January.zip"); + /// } + /// + /// + /// + /// Using zip As New ZipFile() + /// zip.AddDirectory("c:\reports\January") + /// zip.Save("January.zip") + /// End Using + /// + /// + /// + /// + /// + /// This example shows how to update a zip file. + /// + /// using (ZipFile zip = ZipFile.Read("ExistingArchive.zip")) + /// { + /// zip.AddFile("NewData.csv"); + /// zip.Save("UpdatedArchive.zip"); + /// } + /// + /// + /// + /// Using zip As ZipFile = ZipFile.Read("ExistingArchive.zip") + /// zip.AddFile("NewData.csv") + /// zip.Save("UpdatedArchive.zip") + /// End Using + /// + /// + /// + public void Save(String fileName) + { + // Check for the case where we are re-saving a zip archive + // that was originally instantiated with a stream. In that case, + // the _name will be null. If so, we set _writestream to null, + // which insures that we'll cons up a new WriteStream (with a filesystem + // file backing it) in the Save() method. + if (_name == null) + _writestream = null; + + else _readName = _name; // workitem 13915 + + _name = fileName; + if (Directory.Exists(_name)) + throw new ZipException("Bad Directory", new System.ArgumentException("That name specifies an existing directory. Please specify a filename.", "fileName")); + _contentsChanged = true; + _fileAlreadyExists = File.Exists(_name); + Save(); + } + + + /// + /// Save the zip archive to the specified stream. + /// + /// + /// + /// + /// The ZipFile instance is written to storage - typically a zip file + /// in a filesystem, but using this overload, the storage can be anything + /// accessible via a writable stream - only when the caller calls Save. + /// + /// + /// + /// Use this method to save the zip content to a stream directly. A common + /// scenario is an ASP.NET application that dynamically generates a zip file + /// and allows the browser to download it. The application can call + /// Save(Response.OutputStream) to write a zipfile directly to the + /// output stream, without creating a zip file on the disk on the ASP.NET + /// server. + /// + /// + /// + /// Be careful when saving a file to a non-seekable stream, including + /// Response.OutputStream. When DotNetZip writes to a non-seekable + /// stream, the zip archive is formatted in such a way that may not be + /// compatible with all zip tools on all platforms. It's a perfectly legal + /// and compliant zip file, but some people have reported problems opening + /// files produced this way using the Mac OS archive utility. + /// + /// + /// + /// + /// + /// + /// This example saves the zipfile content into a MemoryStream, and + /// then gets the array of bytes from that MemoryStream. + /// + /// + /// using (var zip = new Ionic.Zip.ZipFile()) + /// { + /// zip.CompressionLevel= Ionic.Zlib.CompressionLevel.BestCompression; + /// zip.Password = "VerySecret."; + /// zip.Encryption = EncryptionAlgorithm.WinZipAes128; + /// zip.AddFile(sourceFileName); + /// MemoryStream output = new MemoryStream(); + /// zip.Save(output); + /// + /// byte[] zipbytes = output.ToArray(); + /// } + /// + /// + /// + /// + /// + /// This example shows a pitfall you should avoid. DO NOT read + /// from a stream, then try to save to the same stream. DO + /// NOT DO THIS: + /// + /// + /// + /// using (var fs = new FileSteeam(filename, FileMode.Open)) + /// { + /// using (var zip = Ionic.Zip.ZipFile.Read(inputStream)) + /// { + /// zip.AddEntry("Name1.txt", "this is the content"); + /// zip.Save(inputStream); // NO NO NO!! + /// } + /// } + /// + /// + /// + /// Better like this: + /// + /// + /// + /// using (var zip = Ionic.Zip.ZipFile.Read(filename)) + /// { + /// zip.AddEntry("Name1.txt", "this is the content"); + /// zip.Save(); // YES! + /// } + /// + /// + /// + /// + /// + /// The System.IO.Stream to write to. It must be + /// writable. If you created the ZipFile instanct by calling + /// ZipFile.Read(), this stream must not be the same stream + /// you passed to ZipFile.Read(). + /// + public void Save(Stream outputStream) + { + if (outputStream == null) + throw new ArgumentNullException("outputStream"); + if (!outputStream.CanWrite) + throw new ArgumentException("Must be a writable stream.", "outputStream"); + + // if we had a filename to save to, we are now obliterating it. + _name = null; + + _writestream = new CountingStream(outputStream); + + _contentsChanged = true; + _fileAlreadyExists = false; + Save(); + } + + + } + + + + internal static class ZipOutput + { + public static bool WriteCentralDirectoryStructure(Stream s, + ICollection entries, + uint numSegments, + Zip64Option zip64, + String comment, + ZipContainer container) + { + var zss = s as ZipSegmentedStream; + if (zss != null) + zss.ContiguousWrite = true; + + // write to a memory stream in order to keep the + // CDR contiguous + Int64 aLength = 0; + using (var ms = new MemoryStream()) + { + foreach (ZipEntry e in entries) + { + if (e.IncludedInMostRecentSave) + { + // this writes a ZipDirEntry corresponding to the ZipEntry + e.WriteCentralDirectoryEntry(ms); + } + } + var a = ms.ToArray(); + s.Write(a, 0, a.Length); + aLength = a.Length; + } + + + // We need to keep track of the start and + // Finish of the Central Directory Structure. + + // Cannot always use WriteStream.Length or Position; some streams do + // not support these. (eg, ASP.NET Response.OutputStream) In those + // cases we have a CountingStream. + + // Also, we cannot just set Start as s.Position bfore the write, and Finish + // as s.Position after the write. In a split zip, the write may actually + // flip to the next segment. In that case, Start will be zero. But we + // don't know that til after we know the size of the thing to write. So the + // answer is to compute the directory, then ask the ZipSegmentedStream which + // segment that directory would fall in, it it were written. Then, include + // that data into the directory, and finally, write the directory to the + // output stream. + + var output = s as CountingStream; + long Finish = (output != null) ? output.ComputedPosition : s.Position; // BytesWritten + long Start = Finish - aLength; + + // need to know which segment the EOCD record starts in + UInt32 startSegment = (zss != null) + ? zss.CurrentSegment + : 0; + + Int64 SizeOfCentralDirectory = Finish - Start; + + int countOfEntries = CountEntries(entries); + + bool needZip64CentralDirectory = + zip64 == Zip64Option.Always || + countOfEntries >= 0xFFFF || + SizeOfCentralDirectory > 0xFFFFFFFF || + Start > 0xFFFFFFFF; + + byte[] a2 = null; + + // emit ZIP64 extensions as required + if (needZip64CentralDirectory) + { + if (zip64 == Zip64Option.Never) + { +#if NETCF + throw new ZipException("The archive requires a ZIP64 Central Directory. Consider enabling ZIP64 extensions."); +#else + System.Diagnostics.StackFrame sf = new System.Diagnostics.StackFrame(1); + if (sf.GetMethod().DeclaringType == typeof(ZipFile)) + throw new ZipException("The archive requires a ZIP64 Central Directory. Consider setting the ZipFile.UseZip64WhenSaving property."); + else + throw new ZipException("The archive requires a ZIP64 Central Directory. Consider setting the ZipOutputStream.EnableZip64 property."); +#endif + + } + + var a = GenZip64EndOfCentralDirectory(Start, Finish, countOfEntries, numSegments); + a2 = GenCentralDirectoryFooter(Start, Finish, zip64, countOfEntries, comment, container); + if (startSegment != 0) + { + UInt32 thisSegment = zss.ComputeSegment(a.Length + a2.Length); + int i = 16; + // number of this disk + Array.Copy(BitConverter.GetBytes(thisSegment), 0, a, i, 4); + i += 4; + // number of the disk with the start of the central directory + //Array.Copy(BitConverter.GetBytes(startSegment), 0, a, i, 4); + Array.Copy(BitConverter.GetBytes(thisSegment), 0, a, i, 4); + + i = 60; + // offset 60 + // number of the disk with the start of the zip64 eocd + Array.Copy(BitConverter.GetBytes(thisSegment), 0, a, i, 4); + i += 4; + i += 8; + + // offset 72 + // total number of disks + Array.Copy(BitConverter.GetBytes(thisSegment), 0, a, i, 4); + } + s.Write(a, 0, a.Length); + } + else + a2 = GenCentralDirectoryFooter(Start, Finish, zip64, countOfEntries, comment, container); + + + // now, the regular footer + if (startSegment != 0) + { + // The assumption is the central directory is never split across + // segment boundaries. + + UInt16 thisSegment = (UInt16) zss.ComputeSegment(a2.Length); + int i = 4; + // number of this disk + Array.Copy(BitConverter.GetBytes(thisSegment), 0, a2, i, 2); + i += 2; + // number of the disk with the start of the central directory + //Array.Copy(BitConverter.GetBytes((UInt16)startSegment), 0, a2, i, 2); + Array.Copy(BitConverter.GetBytes(thisSegment), 0, a2, i, 2); + i += 2; + } + + s.Write(a2, 0, a2.Length); + + // reset the contiguous write property if necessary + if (zss != null) + zss.ContiguousWrite = false; + + return needZip64CentralDirectory; + } + + + private static System.Text.Encoding GetEncoding(ZipContainer container, string t) + { + switch (container.AlternateEncodingUsage) + { + case ZipOption.Always: + return container.AlternateEncoding; + case ZipOption.Never: + return container.DefaultEncoding; + } + + // AsNecessary is in force + var e = container.DefaultEncoding; + if (t == null) return e; + + var bytes = e.GetBytes(t); + var t2 = e.GetString(bytes,0,bytes.Length); + if (t2.Equals(t)) return e; + return container.AlternateEncoding; + } + + + + private static byte[] GenCentralDirectoryFooter(long StartOfCentralDirectory, + long EndOfCentralDirectory, + Zip64Option zip64, + int entryCount, + string comment, + ZipContainer container) + { + System.Text.Encoding encoding = GetEncoding(container, comment); + int j = 0; + int bufferLength = 22; + byte[] block = null; + Int16 commentLength = 0; + if ((comment != null) && (comment.Length != 0)) + { + block = encoding.GetBytes(comment); + commentLength = (Int16)block.Length; + } + bufferLength += commentLength; + byte[] bytes = new byte[bufferLength]; + + int i = 0; + // signature + byte[] sig = BitConverter.GetBytes(ZipConstants.EndOfCentralDirectorySignature); + Array.Copy(sig, 0, bytes, i, 4); + i+=4; + + // number of this disk + // (this number may change later) + bytes[i++] = 0; + bytes[i++] = 0; + + // number of the disk with the start of the central directory + // (this number may change later) + bytes[i++] = 0; + bytes[i++] = 0; + + // handle ZIP64 extensions for the end-of-central-directory + if (entryCount >= 0xFFFF || zip64 == Zip64Option.Always) + { + // the ZIP64 version. + for (j = 0; j < 4; j++) + bytes[i++] = 0xFF; + } + else + { + // the standard version. + // total number of entries in the central dir on this disk + bytes[i++] = (byte)(entryCount & 0x00FF); + bytes[i++] = (byte)((entryCount & 0xFF00) >> 8); + + // total number of entries in the central directory + bytes[i++] = (byte)(entryCount & 0x00FF); + bytes[i++] = (byte)((entryCount & 0xFF00) >> 8); + } + + // size of the central directory + Int64 SizeOfCentralDirectory = EndOfCentralDirectory - StartOfCentralDirectory; + + if (SizeOfCentralDirectory >= 0xFFFFFFFF || StartOfCentralDirectory >= 0xFFFFFFFF) + { + // The actual data is in the ZIP64 central directory structure + for (j = 0; j < 8; j++) + bytes[i++] = 0xFF; + } + else + { + // size of the central directory (we just get the low 4 bytes) + bytes[i++] = (byte)(SizeOfCentralDirectory & 0x000000FF); + bytes[i++] = (byte)((SizeOfCentralDirectory & 0x0000FF00) >> 8); + bytes[i++] = (byte)((SizeOfCentralDirectory & 0x00FF0000) >> 16); + bytes[i++] = (byte)((SizeOfCentralDirectory & 0xFF000000) >> 24); + + // offset of the start of the central directory (we just get the low 4 bytes) + bytes[i++] = (byte)(StartOfCentralDirectory & 0x000000FF); + bytes[i++] = (byte)((StartOfCentralDirectory & 0x0000FF00) >> 8); + bytes[i++] = (byte)((StartOfCentralDirectory & 0x00FF0000) >> 16); + bytes[i++] = (byte)((StartOfCentralDirectory & 0xFF000000) >> 24); + } + + + // zip archive comment + if ((comment == null) || (comment.Length == 0)) + { + // no comment! + bytes[i++] = (byte)0; + bytes[i++] = (byte)0; + } + else + { + // the size of our buffer defines the max length of the comment we can write + if (commentLength + i + 2 > bytes.Length) commentLength = (Int16)(bytes.Length - i - 2); + bytes[i++] = (byte)(commentLength & 0x00FF); + bytes[i++] = (byte)((commentLength & 0xFF00) >> 8); + + if (commentLength != 0) + { + // now actually write the comment itself into the byte buffer + for (j = 0; (j < commentLength) && (i + j < bytes.Length); j++) + { + bytes[i + j] = block[j]; + } + i += j; + } + } + + // s.Write(bytes, 0, i); + return bytes; + } + + + + private static byte[] GenZip64EndOfCentralDirectory(long StartOfCentralDirectory, + long EndOfCentralDirectory, + int entryCount, + uint numSegments) + { + const int bufferLength = 12 + 44 + 20; + + byte[] bytes = new byte[bufferLength]; + + int i = 0; + // signature + byte[] sig = BitConverter.GetBytes(ZipConstants.Zip64EndOfCentralDirectoryRecordSignature); + Array.Copy(sig, 0, bytes, i, 4); + i+=4; + + // There is a possibility to include "Extensible" data in the zip64 + // end-of-central-dir record. I cannot figure out what it might be used to + // store, so the size of this record is always fixed. Maybe it is used for + // strong encryption data? That is for another day. + long DataSize = 44; + Array.Copy(BitConverter.GetBytes(DataSize), 0, bytes, i, 8); + i += 8; + + // offset 12 + // VersionMadeBy = 45; + bytes[i++] = 45; + bytes[i++] = 0x00; + + // VersionNeededToExtract = 45; + bytes[i++] = 45; + bytes[i++] = 0x00; + + // offset 16 + // number of the disk, and the disk with the start of the central dir. + // (this may change later) + for (int j = 0; j < 8; j++) + bytes[i++] = 0x00; + + // offset 24 + long numberOfEntries = entryCount; + Array.Copy(BitConverter.GetBytes(numberOfEntries), 0, bytes, i, 8); + i += 8; + Array.Copy(BitConverter.GetBytes(numberOfEntries), 0, bytes, i, 8); + i += 8; + + // offset 40 + Int64 SizeofCentraldirectory = EndOfCentralDirectory - StartOfCentralDirectory; + Array.Copy(BitConverter.GetBytes(SizeofCentraldirectory), 0, bytes, i, 8); + i += 8; + Array.Copy(BitConverter.GetBytes(StartOfCentralDirectory), 0, bytes, i, 8); + i += 8; + + // offset 56 + // now, the locator + // signature + sig = BitConverter.GetBytes(ZipConstants.Zip64EndOfCentralDirectoryLocatorSignature); + Array.Copy(sig, 0, bytes, i, 4); + i+=4; + + // offset 60 + // number of the disk with the start of the zip64 eocd + // (this will change later) (it will?) + uint x2 = (numSegments==0)?0:(uint)(numSegments-1); + Array.Copy(BitConverter.GetBytes(x2), 0, bytes, i, 4); + i+=4; + + // offset 64 + // relative offset of the zip64 eocd + Array.Copy(BitConverter.GetBytes(EndOfCentralDirectory), 0, bytes, i, 8); + i += 8; + + // offset 72 + // total number of disks + // (this will change later) + Array.Copy(BitConverter.GetBytes(numSegments), 0, bytes, i, 4); + i+=4; + + return bytes; + } + + + + private static int CountEntries(ICollection _entries) + { + // Cannot just emit _entries.Count, because some of the entries + // may have been skipped. + int count = 0; + foreach (var entry in _entries) + if (entry.IncludedInMostRecentSave) count++; + return count; + } + + + + + } +} diff --git a/DotNetZip/Zip/ZipFile.SaveSelfExtractor.cs b/DotNetZip/Zip/ZipFile.SaveSelfExtractor.cs new file mode 100644 index 0000000..9de5dc8 --- /dev/null +++ b/DotNetZip/Zip/ZipFile.SaveSelfExtractor.cs @@ -0,0 +1,1101 @@ +// ZipFile.saveSelfExtractor.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2008-2011 Dino Chiesa. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2011-August-10 19:22:46> +// +// ------------------------------------------------------------------ +// +// This is a the source module that implements the stuff for saving to a +// self-extracting Zip archive. +// +// ZipFile is set up as a "partial class" - defined in multiple .cs source modules. +// This is one of the source modules for the ZipFile class. +// +// Here's the design: The self-extracting zip file is just a regular managed EXE +// file, with embedded resources. The managed code logic instantiates a ZipFile, and +// then extracts each entry. The embedded resources include the zip archive content, +// as well as the Zip library itself. The latter is required so that self-extracting +// can work on any machine, whether or not it has the DotNetZip library installed on +// it. +// +// What we need to do is create the animal I just described, within a method on the +// ZipFile class. This source module provides that capability. The method is +// SaveSelfExtractor(). +// +// The way the method works: it uses the programmatic interface to the csc.exe +// compiler, Microsoft.CSharp.CSharpCodeProvider, to compile "boilerplate" +// extraction logic into a new assembly. As part of that compile, we embed within +// that assembly the zip archive itself, as well as the Zip library. +// +// Therefore we need to first save to a temporary zip file, then produce the exe. +// +// There are a few twists. +// +// The Visual Studio Project structure is a little weird. There are code files +// that ARE NOT compiled during a normal build of the VS Solution. They are +// marked as embedded resources. These are the various "boilerplate" modules that +// are used in the self-extractor. These modules are: WinFormsSelfExtractorStub.cs +// WinFormsSelfExtractorStub.Designer.cs CommandLineSelfExtractorStub.cs +// PasswordDialog.cs PasswordDialog.Designer.cs +// +// At design time, if you want to modify the way the GUI looks, you have to +// mark those modules to have a "compile" build action. Then tweak em, test, +// etc. Then again mark them as "Embedded resource". +// +// ------------------------------------------------------------------ + +using System; +using System.Reflection; +using System.IO; +using System.Collections.Generic; + + +namespace Ionic.Zip +{ +#if !NO_SFX + /// + /// An enum that provides the different self-extractor flavors + /// + public enum SelfExtractorFlavor + { + /// + /// A self-extracting zip archive that runs from the console or + /// command line. + /// + ConsoleApplication = 0, + + /// + /// A self-extracting zip archive that presents a graphical user + /// interface when it is executed. + /// + WinFormsApplication, + } + + /// + /// The options for generating a self-extracting archive. + /// + public class SelfExtractorSaveOptions + { + /// + /// The type of SFX to create. + /// + public SelfExtractorFlavor Flavor + { + get; + set; + } + + /// + /// The command to run after extraction. + /// + /// + /// + /// + /// This is optional. Leave it empty (null in C# or Nothing in + /// VB) to run no command after extraction. + /// + /// + /// + /// If it is non-empty, the SFX will execute the command specified in this + /// string on the user's machine, and using the extract directory as the + /// working directory for the process, after unpacking the archive. The + /// program to execute can include a path, if you like. If you want to execute + /// a program that accepts arguments, specify the program name, followed by a + /// space, and then the arguments for the program, each separated by a space, + /// just as you would on a normal command line. Example: program.exe arg1 + /// arg2. The string prior to the first space will be taken as the + /// program name, and the string following the first space specifies the + /// arguments to the program. + /// + /// + /// + /// If you want to execute a program that has a space in the name or path of + /// the file, surround the program name in double-quotes. The first character + /// of the command line should be a double-quote character, and there must be + /// a matching double-quote following the end of the program file name. Any + /// optional arguments to the program follow that, separated by + /// spaces. Example: "c:\project files\program name.exe" arg1 arg2. + /// + /// + /// + /// If the flavor of the SFX is SelfExtractorFlavor.ConsoleApplication, + /// then the SFX starts a new process, using this string as the post-extract + /// command line. The SFX waits for the process to exit. The exit code of + /// the post-extract command line is returned as the exit code of the + /// command-line self-extractor exe. A non-zero exit code is typically used to + /// indicated a failure by the program. In the case of an SFX, a non-zero exit + /// code may indicate a failure during extraction, OR, it may indicate a + /// failure of the run-after-extract program if specified, OR, it may indicate + /// the run-after-extract program could not be fuond. There is no way to + /// distinguish these conditions from the calling shell, aside from parsing + /// the output of the SFX. If you have Quiet set to true, you may not + /// see error messages, if a problem occurs. + /// + /// + /// + /// If the flavor of the SFX is + /// SelfExtractorFlavor.WinFormsApplication, then the SFX starts a new + /// process, using this string as the post-extract command line, and using the + /// extract directory as the working directory for the process. The SFX does + /// not wait for the command to complete, and does not check the exit code of + /// the program. If the run-after-extract program cannot be fuond, a message + /// box is displayed indicating that fact. + /// + /// + /// + /// You can specify environment variables within this string, with a format like + /// %NAME%. The value of these variables will be expanded at the time + /// the SFX is run. Example: %WINDIR%\system32\xcopy.exe may expand at + /// runtime to c:\Windows\System32\xcopy.exe. + /// + /// + /// + /// By combining this with the RemoveUnpackedFilesAfterExecute + /// flag, you can create an SFX that extracts itself, runs a file that + /// was extracted, then deletes all the files that were extracted. If + /// you want it to run "invisibly" then set Flavor to + /// SelfExtractorFlavor.ConsoleApplication, and set Quiet + /// to true. The user running such an EXE will see a console window + /// appear, then disappear quickly. You may also want to specify the + /// default extract location, with DefaultExtractDirectory. + /// + /// + /// + /// If you set Flavor to + /// SelfExtractorFlavor.WinFormsApplication, and set Quiet to + /// true, then a GUI with progressbars is displayed, but it is + /// "non-interactive" - it accepts no input from the user. Instead the SFX + /// just automatically unpacks and exits. + /// + /// + /// + public String PostExtractCommandLine + { + get; + set; + } + + /// + /// The default extract directory the user will see when + /// running the self-extracting archive. + /// + /// + /// + /// + /// Passing null (or Nothing in VB) here will cause the Self Extractor to use + /// the the user's personal directory () for the default extract + /// location. + /// + /// + /// + /// This is only a default location. The actual extract location will be + /// settable on the command line when the SFX is executed. + /// + /// + /// + /// You can specify environment variables within this string, + /// with %NAME%. The value of these variables will be + /// expanded at the time the SFX is run. Example: + /// %USERPROFILE%\Documents\unpack may expand at runtime to + /// c:\users\melvin\Documents\unpack. + /// + /// + public String DefaultExtractDirectory + { + get; + set; + } + + /// + /// The name of an .ico file in the filesystem to use for the application icon + /// for the generated SFX. + /// + /// + /// + /// + /// Normally, DotNetZip will embed an "zipped folder" icon into the generated + /// SFX. If you prefer to use a different icon, you can specify it here. It + /// should be a .ico file. This file is passed as the /win32icon + /// option to the csc.exe compiler when constructing the SFX file. + /// + /// + /// + public string IconFile + { + get; + set; + } + + /// + /// Whether the ConsoleApplication SFX will be quiet during extraction. + /// + /// + /// + /// + /// This option affects the way the generated SFX runs. By default it is + /// false. When you set it to true,... + /// + /// + /// + /// + /// Flavor + /// Behavior + /// + /// + /// + /// ConsoleApplication + /// no messages will be emitted during successful + /// operation. Double-clicking the SFX in Windows + /// Explorer or as an attachment in an email will cause a console + /// window to appear briefly, before it disappears. If you run the + /// ConsoleApplication SFX from the cmd.exe prompt, it runs as a + /// normal console app; by default, because it is quiet, it displays + /// no messages to the console. If you pass the -v+ command line + /// argument to the Console SFX when you run it, you will get verbose + /// messages to the console. + /// + /// + /// + /// + /// WinFormsApplication + /// the SFX extracts automatically when the application + /// is launched, with no additional user input. + /// + /// + /// + /// + /// + /// + /// When you set it to false,... + /// + /// + /// + /// + /// Flavor + /// Behavior + /// + /// + /// + /// ConsoleApplication + /// the extractor will emit a + /// message to the console for each entry extracted. + /// + /// When double-clicking to launch the SFX, the console window will + /// remain, and the SFX will emit a message for each file as it + /// extracts. The messages fly by quickly, they won't be easily + /// readable, unless the extracted files are fairly large. + /// + /// + /// + /// + /// + /// WinFormsApplication + /// the SFX presents a forms UI and allows the user to select + /// options before extracting. + /// + /// + /// + /// + /// + /// + public bool Quiet + { + get; + set; + } + + + /// + /// Specify what the self-extractor will do when extracting an entry + /// would overwrite an existing file. + /// + /// + /// + /// The default behavvior is to Throw. + /// + /// + public Ionic.Zip.ExtractExistingFileAction ExtractExistingFile + { + get; + set; + } + + + /// + /// Whether to remove the files that have been unpacked, after executing the + /// PostExtractCommandLine. + /// + /// + /// + /// + /// If true, and if there is a + /// PostExtractCommandLine, and if the command runs successfully, + /// then the files that the SFX unpacked will be removed, afterwards. If + /// the command does not complete successfully (non-zero return code), + /// that is interpreted as a failure, and the extracted files will not be + /// removed. + /// + /// + /// + /// Setting this flag, and setting Flavor to + /// SelfExtractorFlavor.ConsoleApplication, and setting Quiet to + /// true, results in an SFX that extracts itself, runs a file that was + /// extracted, then deletes all the files that were extracted, with no + /// intervention by the user. You may also want to specify the default + /// extract location, with DefaultExtractDirectory. + /// + /// + /// + public bool RemoveUnpackedFilesAfterExecute + { + get; + set; + } + + + /// + /// The file version number to embed into the generated EXE. It will show up, for + /// example, during a mouseover in Windows Explorer. + /// + /// + public Version FileVersion + { + get; + set; + } + + /// + /// The product version to embed into the generated EXE. It will show up, for + /// example, during a mouseover in Windows Explorer. + /// + /// + /// + /// You can use any arbitrary string, but a human-readable version number is + /// recommended. For example "v1.2 alpha" or "v4.2 RC2". If you specify nothing, + /// then there is no product version embedded into the EXE. + /// + /// + public String ProductVersion + { + get; + set; + } + + /// + /// The copyright notice, if any, to embed into the generated EXE. + /// + /// + /// + /// It will show up, for example, while viewing properties of the file in + /// Windows Explorer. You can use any arbitrary string, but typically you + /// want something like "Copyright Dino Chiesa 2011". + /// + /// + public String Copyright + { + get; + set; + } + + + /// + /// The description to embed into the generated EXE. + /// + /// + /// + /// Use any arbitrary string. This text will be displayed during a + /// mouseover in Windows Explorer. If you specify nothing, then the string + /// "DotNetZip SFX Archive" is embedded into the EXE as the description. + /// + /// + public String Description + { + get; + set; + } + + /// + /// The product name to embed into the generated EXE. + /// + /// + /// + /// Use any arbitrary string. This text will be displayed + /// while viewing properties of the EXE file in + /// Windows Explorer. + /// + /// + public String ProductName + { + get; + set; + } + + /// + /// The title to display in the Window of a GUI SFX, while it extracts. + /// + /// + /// + /// + /// By default the title show in the GUI window of a self-extractor + /// is "DotNetZip Self-extractor (http://DotNetZip.codeplex.com/)". + /// You can change that by setting this property before saving the SFX. + /// + /// + /// + /// This property has an effect only when producing a Self-extractor + /// of flavor SelfExtractorFlavor.WinFormsApplication. + /// + /// + /// + public String SfxExeWindowTitle + { + // workitem 12608 + get; + set; + } + + /// + /// Additional options for the csc.exe compiler, when producing the SFX + /// EXE. + /// + /// + public string AdditionalCompilerSwitches + { + get; set; + } + } + + + + + partial class ZipFile + { + class ExtractorSettings + { + public SelfExtractorFlavor Flavor; + public List ReferencedAssemblies; + public List CopyThroughResources; + public List ResourcesToCompile; + } + + + private static ExtractorSettings[] SettingsList = { + new ExtractorSettings() { + Flavor = SelfExtractorFlavor.WinFormsApplication, + ReferencedAssemblies= new List{ + "System.dll", "System.Windows.Forms.dll", "System.Drawing.dll"}, + CopyThroughResources = new List{ + "Ionic.Zip.WinFormsSelfExtractorStub.resources", + "Ionic.Zip.Forms.PasswordDialog.resources", + "Ionic.Zip.Forms.ZipContentsDialog.resources"}, + ResourcesToCompile = new List{ + "WinFormsSelfExtractorStub.cs", + "WinFormsSelfExtractorStub.Designer.cs", // .Designer.cs? + "PasswordDialog.cs", + "PasswordDialog.Designer.cs", //.Designer.cs" + "ZipContentsDialog.cs", + "ZipContentsDialog.Designer.cs", //.Designer.cs" + "FolderBrowserDialogEx.cs", + } + }, + new ExtractorSettings() { + Flavor = SelfExtractorFlavor.ConsoleApplication, + ReferencedAssemblies= new List { "System.dll", }, + CopyThroughResources = null, + ResourcesToCompile = new List{"CommandLineSelfExtractorStub.cs"} + } + }; + + + + //string _defaultExtractLocation; + //string _postExtractCmdLine; + // string _SetDefaultLocationCode = + // "namespace Ionic.Zip { public partial class WinFormsSelfExtractorStub { partial void _SetDefaultExtractLocation() {" + + // " txtExtractDirectory.Text = \"@@VALUE\"; } }}"; + + + + /// + /// Saves the ZipFile instance to a self-extracting zip archive. + /// + /// + /// + /// + /// + /// The generated exe image will execute on any machine that has the .NET + /// Framework 2.0 installed on it. The generated exe image is also a + /// valid ZIP file, readable with DotNetZip or another Zip library or tool + /// such as WinZip. + /// + /// + /// + /// There are two "flavors" of self-extracting archive. The + /// WinFormsApplication version will pop up a GUI and allow the + /// user to select a target directory into which to extract. There's also + /// a checkbox allowing the user to specify to overwrite existing files, + /// and another checkbox to allow the user to request that Explorer be + /// opened to see the extracted files after extraction. The other flavor + /// is ConsoleApplication. A self-extractor generated with that + /// flavor setting will run from the command line. It accepts command-line + /// options to set the overwrite behavior, and to specify the target + /// extraction directory. + /// + /// + /// + /// There are a few temporary files created during the saving to a + /// self-extracting zip. These files are created in the directory pointed + /// to by , which defaults to . These temporary files are + /// removed upon successful completion of this method. + /// + /// + /// + /// When a user runs the WinForms SFX, the user's personal directory (Environment.SpecialFolder.Personal) + /// will be used as the default extract location. If you want to set the + /// default extract location, you should use the other overload of + /// SaveSelfExtractor()/ The user who runs the SFX will have the + /// opportunity to change the extract directory before extracting. When + /// the user runs the Command-Line SFX, the user must explicitly specify + /// the directory to which to extract. The .NET Framework 2.0 is required + /// on the computer when the self-extracting archive is run. + /// + /// + /// + /// NB: This method is not available in the version of DotNetZip build for + /// the .NET Compact Framework, nor in the "Reduced" DotNetZip library. + /// + /// + /// + /// + /// + /// + /// string DirectoryPath = "c:\\Documents\\Project7"; + /// using (ZipFile zip = new ZipFile()) + /// { + /// zip.AddDirectory(DirectoryPath, System.IO.Path.GetFileName(DirectoryPath)); + /// zip.Comment = "This will be embedded into a self-extracting console-based exe"; + /// zip.SaveSelfExtractor("archive.exe", SelfExtractorFlavor.ConsoleApplication); + /// } + /// + /// + /// Dim DirectoryPath As String = "c:\Documents\Project7" + /// Using zip As New ZipFile() + /// zip.AddDirectory(DirectoryPath, System.IO.Path.GetFileName(DirectoryPath)) + /// zip.Comment = "This will be embedded into a self-extracting console-based exe" + /// zip.SaveSelfExtractor("archive.exe", SelfExtractorFlavor.ConsoleApplication) + /// End Using + /// + /// + /// + /// + /// a pathname, possibly fully qualified, to be created. Typically it + /// will end in an .exe extension. + /// + /// Indicates whether a Winforms or Console self-extractor is + /// desired. + public void SaveSelfExtractor(string exeToGenerate, SelfExtractorFlavor flavor) + { + SelfExtractorSaveOptions options = new SelfExtractorSaveOptions(); + options.Flavor = flavor; + SaveSelfExtractor(exeToGenerate, options); + } + + + + /// + /// Saves the ZipFile instance to a self-extracting zip archive, using + /// the specified save options. + /// + /// + /// + /// + /// This method saves a self extracting archive, using the specified save + /// options. These options include the flavor of the SFX, the default extract + /// directory, the icon file, and so on. See the documentation + /// for for more + /// details. + /// + /// + /// + /// The user who runs the SFX will have the opportunity to change the extract + /// directory before extracting. If at the time of extraction, the specified + /// directory does not exist, the SFX will create the directory before + /// extracting the files. + /// + /// + /// + /// + /// + /// This example saves a WinForms-based self-extracting archive EXE that + /// will use c:\ExtractHere as the default extract location. The C# code + /// shows syntax for .NET 3.0, which uses an object initializer for + /// the SelfExtractorOptions object. + /// + /// string DirectoryPath = "c:\\Documents\\Project7"; + /// using (ZipFile zip = new ZipFile()) + /// { + /// zip.AddDirectory(DirectoryPath, System.IO.Path.GetFileName(DirectoryPath)); + /// zip.Comment = "This will be embedded into a self-extracting WinForms-based exe"; + /// var options = new SelfExtractorOptions + /// { + /// Flavor = SelfExtractorFlavor.WinFormsApplication, + /// DefaultExtractDirectory = "%USERPROFILE%\\ExtractHere", + /// PostExtractCommandLine = ExeToRunAfterExtract, + /// SfxExeWindowTitle = "My Custom Window Title", + /// RemoveUnpackedFilesAfterExecute = true + /// }; + /// zip.SaveSelfExtractor("archive.exe", options); + /// } + /// + /// + /// Dim DirectoryPath As String = "c:\Documents\Project7" + /// Using zip As New ZipFile() + /// zip.AddDirectory(DirectoryPath, System.IO.Path.GetFileName(DirectoryPath)) + /// zip.Comment = "This will be embedded into a self-extracting console-based exe" + /// Dim options As New SelfExtractorOptions() + /// options.Flavor = SelfExtractorFlavor.WinFormsApplication + /// options.DefaultExtractDirectory = "%USERPROFILE%\\ExtractHere" + /// options.PostExtractCommandLine = ExeToRunAfterExtract + /// options.SfxExeWindowTitle = "My Custom Window Title" + /// options.RemoveUnpackedFilesAfterExecute = True + /// zip.SaveSelfExtractor("archive.exe", options) + /// End Using + /// + /// + /// + /// The name of the EXE to generate. + /// provides the options for creating the + /// Self-extracting archive. + public void SaveSelfExtractor(string exeToGenerate, SelfExtractorSaveOptions options) + { + // Save an SFX that is both an EXE and a ZIP. + + // Check for the case where we are re-saving a zip archive + // that was originally instantiated with a stream. In that case, + // the _name will be null. If so, we set _writestream to null, + // which insures that we'll cons up a new WriteStream (with a filesystem + // file backing it) in the Save() method. + if (_name == null) + _writestream = null; + + _SavingSfx = true; + _name = exeToGenerate; + if (Directory.Exists(_name)) + throw new ZipException("Bad Directory", new System.ArgumentException("That name specifies an existing directory. Please specify a filename.", "exeToGenerate")); + _contentsChanged = true; + _fileAlreadyExists = File.Exists(_name); + + _SaveSfxStub(exeToGenerate, options); + + Save(); + _SavingSfx = false; + } + + + + + private static void ExtractResourceToFile(Assembly a, string resourceName, string filename) + { + int n = 0; + byte[] bytes = new byte[1024]; + using (Stream instream = a.GetManifestResourceStream(resourceName)) + { + if (instream == null) + throw new ZipException(String.Format("missing resource '{0}'", resourceName)); + + using (FileStream outstream = File.OpenWrite(filename)) + { + do + { + n = instream.Read(bytes, 0, bytes.Length); + outstream.Write(bytes, 0, n); + } while (n > 0); + } + } + } + + + private void _SaveSfxStub(string exeToGenerate, SelfExtractorSaveOptions options) + { + string nameOfIconFile = null; + string stubExe = null; + string unpackedResourceDir = null; + string tmpDir = null; + try + { + if (File.Exists(exeToGenerate)) + { + if (Verbose) StatusMessageTextWriter.WriteLine("The existing file ({0}) will be overwritten.", exeToGenerate); + } + if (!exeToGenerate.EndsWith(".exe")) + { + if (Verbose) StatusMessageTextWriter.WriteLine("Warning: The generated self-extracting file will not have an .exe extension."); + } + + // workitem 10553 + tmpDir = TempFileFolder ?? Path.GetDirectoryName(exeToGenerate); + stubExe = GenerateTempPathname(tmpDir, "exe"); + + // get the Ionic.Zip assembly + Assembly a1 = typeof(ZipFile).Assembly; + + using (var csharp = new Microsoft.CSharp.CSharpCodeProvider + (new Dictionary() { { "CompilerVersion", "v2.0" } })) { + + // The following is a perfect opportunity for a linq query, but + // I cannot use it. DotNetZip needs to run on .NET 2.0, + // and using LINQ would break that. Here's what it would look + // like: + // + // var settings = (from x in SettingsList + // where x.Flavor == flavor + // select x).First(); + + ExtractorSettings settings = null; + foreach (var x in SettingsList) + { + if (x.Flavor == options.Flavor) + { + settings = x; + break; + } + } + + // sanity check; should never happen + if (settings == null) + throw new BadStateException(String.Format("While saving a Self-Extracting Zip, Cannot find that flavor ({0})?", options.Flavor)); + + // This is the list of referenced assemblies. Ionic.Zip is + // needed here. Also if it is the winforms (gui) extractor, we + // need other referenced assemblies, like + // System.Windows.Forms.dll, etc. + var cp = new System.CodeDom.Compiler.CompilerParameters(); + cp.ReferencedAssemblies.Add(a1.Location); + if (settings.ReferencedAssemblies != null) + foreach (string ra in settings.ReferencedAssemblies) + cp.ReferencedAssemblies.Add(ra); + + cp.GenerateInMemory = false; + cp.GenerateExecutable = true; + cp.IncludeDebugInformation = false; + cp.CompilerOptions = ""; + + Assembly a2 = Assembly.GetExecutingAssembly(); + + // Use this to concatenate all the source code resources into a + // single module. + var sb = new System.Text.StringBuilder(); + + // In case there are compiler errors later, we allocate a source + // file name now. If errors are detected, we'll spool the source + // code as well as the errors (in comments) into that filename, + // and throw an exception with the filename. Makes it easier to + // diagnose. This should be rare; most errors happen only + // during devlpmt of DotNetZip itself, but there are rare + // occasions when they occur in other cases. + string sourceFile = GenerateTempPathname(tmpDir, "cs"); + + + // // debugging: enumerate the resources in this assembly + // Console.WriteLine("Resources in this assembly:"); + // foreach (string rsrc in a2.GetManifestResourceNames()) + // { + // Console.WriteLine(rsrc); + // } + // Console.WriteLine(); + + + // all the source code is embedded in the DLL as a zip file. + using (ZipFile zip = ZipFile.Read(a2.GetManifestResourceStream("Ionic.Zip.Resources.ZippedResources.zip"))) + { + // // debugging: enumerate the files in the embedded zip + // Console.WriteLine("Entries in the embbedded zip:"); + // foreach (ZipEntry entry in zip) + // { + // Console.WriteLine(entry.FileName); + // } + // Console.WriteLine(); + + unpackedResourceDir = GenerateTempPathname(tmpDir, "tmp"); + + if (String.IsNullOrEmpty(options.IconFile)) + { + // Use the ico file that is embedded into the Ionic.Zip + // DLL itself. To do this we must unpack the icon to + // the filesystem, in order to specify it on the cmdline + // of csc.exe. This method will remove the unpacked + // file later. + System.IO.Directory.CreateDirectory(unpackedResourceDir); + ZipEntry e = zip["zippedFile.ico"]; + // Must not extract a readonly file - it will be impossible to + // delete later. + if ((e.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly) + e.Attributes ^= FileAttributes.ReadOnly; + e.Extract(unpackedResourceDir); + nameOfIconFile = Path.Combine(unpackedResourceDir, "zippedFile.ico"); + cp.CompilerOptions += String.Format("/win32icon:\"{0}\"", nameOfIconFile); + } + else + cp.CompilerOptions += String.Format("/win32icon:\"{0}\"", options.IconFile); + + cp.OutputAssembly = stubExe; + + if (options.Flavor == SelfExtractorFlavor.WinFormsApplication) + cp.CompilerOptions += " /target:winexe"; + + if (!String.IsNullOrEmpty(options.AdditionalCompilerSwitches)) + cp.CompilerOptions += " " + options.AdditionalCompilerSwitches; + + if (String.IsNullOrEmpty(cp.CompilerOptions)) + cp.CompilerOptions = null; + + if ((settings.CopyThroughResources != null) && (settings.CopyThroughResources.Count != 0)) + { + if (!Directory.Exists(unpackedResourceDir)) System.IO.Directory.CreateDirectory(unpackedResourceDir); + foreach (string re in settings.CopyThroughResources) + { + string filename = Path.Combine(unpackedResourceDir, re); + + ExtractResourceToFile(a2, re, filename); + // add the file into the target assembly as an embedded resource + cp.EmbeddedResources.Add(filename); + } + } + + // add the Ionic.Utils.Zip DLL as an embedded resource + cp.EmbeddedResources.Add(a1.Location); + + // file header + sb.Append("// " + Path.GetFileName(sourceFile) + "\n") + .Append("// --------------------------------------------\n//\n") + .Append("// This SFX source file was generated by DotNetZip ") + .Append(ZipFile.LibraryVersion.ToString()) + .Append("\n// at ") + .Append(System.DateTime.Now.ToString("yyyy MMMM dd HH:mm:ss")) + .Append("\n//\n// --------------------------------------------\n\n\n"); + + // assembly attributes + if (!String.IsNullOrEmpty(options.Description)) + sb.Append("[assembly: System.Reflection.AssemblyTitle(\"" + + options.Description.Replace("\"", "") + + "\")]\n"); + else + sb.Append("[assembly: System.Reflection.AssemblyTitle(\"DotNetZip SFX Archive\")]\n"); + + if (!String.IsNullOrEmpty(options.ProductVersion)) + sb.Append("[assembly: System.Reflection.AssemblyInformationalVersion(\"" + + options.ProductVersion.Replace("\"", "") + + "\")]\n"); + + // workitem + string copyright = + (String.IsNullOrEmpty(options.Copyright)) + ? "Extractor: Copyright Dino Chiesa 2008-2011" + : options.Copyright.Replace("\"", ""); + + if (!String.IsNullOrEmpty(options.ProductName)) + sb.Append("[assembly: System.Reflection.AssemblyProduct(\"") + .Append(options.ProductName.Replace("\"", "")) + .Append("\")]\n"); + else + sb.Append("[assembly: System.Reflection.AssemblyProduct(\"DotNetZip\")]\n"); + + + sb.Append("[assembly: System.Reflection.AssemblyCopyright(\"" + copyright + "\")]\n") + .Append(String.Format("[assembly: System.Reflection.AssemblyVersion(\"{0}\")]\n", ZipFile.LibraryVersion.ToString())); + if (options.FileVersion != null) + sb.Append(String.Format("[assembly: System.Reflection.AssemblyFileVersion(\"{0}\")]\n", + options.FileVersion.ToString())); + + sb.Append("\n\n\n"); + + // Set the default extract location if it is available + string extractLoc = options.DefaultExtractDirectory; + if (extractLoc != null) + { + // remove double-quotes and replace slash with double-slash. + // This, because the value is going to be embedded into a + // cs file as a quoted string, and it needs to be escaped. + extractLoc = extractLoc.Replace("\"", "").Replace("\\", "\\\\"); + } + + string postExCmdLine = options.PostExtractCommandLine; + if (postExCmdLine != null) + { + postExCmdLine = postExCmdLine.Replace("\\", "\\\\"); + postExCmdLine = postExCmdLine.Replace("\"", "\\\""); + } + + + foreach (string rc in settings.ResourcesToCompile) + { + using (Stream s = zip[rc].OpenReader()) + { + if (s == null) + throw new ZipException(String.Format("missing resource '{0}'", rc)); + using (StreamReader sr = new StreamReader(s)) + { + while (sr.Peek() >= 0) + { + string line = sr.ReadLine(); + if (extractLoc != null) + line = line.Replace("@@EXTRACTLOCATION", extractLoc); + + line = line.Replace("@@REMOVE_AFTER_EXECUTE", options.RemoveUnpackedFilesAfterExecute.ToString()); + line = line.Replace("@@QUIET", options.Quiet.ToString()); + if (!String.IsNullOrEmpty(options.SfxExeWindowTitle)) + + line = line.Replace("@@SFX_EXE_WINDOW_TITLE", options.SfxExeWindowTitle); + + line = line.Replace("@@EXTRACT_EXISTING_FILE", ((int)options.ExtractExistingFile).ToString()); + + if (postExCmdLine != null) + line = line.Replace("@@POST_UNPACK_CMD_LINE", postExCmdLine); + + sb.Append(line).Append("\n"); + } + } + sb.Append("\n\n"); + } + } + } + + string LiteralSource = sb.ToString(); + +#if DEBUGSFX + // for debugging only + string sourceModule = GenerateTempPathname(tmpDir, "cs"); + using (StreamWriter sw = File.CreateText(sourceModule)) + { + sw.Write(LiteralSource); + } + Console.WriteLine("source: {0}", sourceModule); +#endif + + var cr = csharp.CompileAssemblyFromSource(cp, LiteralSource); + + + if (cr == null) + throw new SfxGenerationException("Cannot compile the extraction logic!"); + + if (Verbose) + foreach (string output in cr.Output) + StatusMessageTextWriter.WriteLine(output); + + if (cr.Errors.Count != 0) + { + using (TextWriter tw = new StreamWriter(sourceFile)) + { + // first, the source we compiled + tw.Write(LiteralSource); + + // now, append the compile errors + tw.Write("\n\n\n// ------------------------------------------------------------------\n"); + tw.Write("// Errors during compilation: \n//\n"); + string p = Path.GetFileName(sourceFile); + + foreach (System.CodeDom.Compiler.CompilerError error in cr.Errors) + { + tw.Write(String.Format("// {0}({1},{2}): {3} {4}: {5}\n//\n", + p, // 0 + error.Line, // 1 + error.Column, // 2 + error.IsWarning ? "Warning" : "error", // 3 + error.ErrorNumber, // 4 + error.ErrorText)); // 5 + } + } + throw new SfxGenerationException(String.Format("Errors compiling the extraction logic! {0}", sourceFile)); + } + + OnSaveEvent(ZipProgressEventType.Saving_AfterCompileSelfExtractor); + + // Now, copy the resulting EXE image to the _writestream. + // Because this stub exe is being saved first, the effect will be to + // concatenate the exe and the zip data together. + using (System.IO.Stream input = System.IO.File.OpenRead(stubExe)) + { + byte[] buffer = new byte[4000]; + int n = 1; + while (n != 0) + { + n = input.Read(buffer, 0, buffer.Length); + if (n != 0) + WriteStream.Write(buffer, 0, n); + } + } + } + + OnSaveEvent(ZipProgressEventType.Saving_AfterSaveTempArchive); + } + finally + { + try + { + if (Directory.Exists(unpackedResourceDir)) + { + try { Directory.Delete(unpackedResourceDir, true); } + catch (System.IO.IOException exc1) + { + StatusMessageTextWriter.WriteLine("Warning: Exception: {0}", exc1); + } + } + if (File.Exists(stubExe)) + { + try { File.Delete(stubExe); } + catch (System.IO.IOException exc1) + { + StatusMessageTextWriter.WriteLine("Warning: Exception: {0}", exc1); + } + } + } + catch (System.IO.IOException) { } + } + + return; + + } + + + + internal static string GenerateTempPathname(string dir, string extension) + { + string candidate = null; + String AppName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name; + do + { + // workitem 13475 + string uuid = System.Guid.NewGuid().ToString(); + + string Name = String.Format("{0}-{1}-{2}.{3}", + AppName, System.DateTime.Now.ToString("yyyyMMMdd-HHmmss"), + uuid, extension); + candidate = System.IO.Path.Combine(dir, Name); + } while (System.IO.File.Exists(candidate) || System.IO.Directory.Exists(candidate)); + + // The candidate path does not exist as a file or directory. + // It can now be created, as a file or directory. + return candidate; + } + + } +#endif +} diff --git a/DotNetZip/Zip/ZipFile.Selector.cs b/DotNetZip/Zip/ZipFile.Selector.cs new file mode 100644 index 0000000..31c3fd8 --- /dev/null +++ b/DotNetZip/Zip/ZipFile.Selector.cs @@ -0,0 +1,1464 @@ +// ZipFile.Selector.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2009-2010 Dino Chiesa. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2011-August-06 09:35:58> +// +// ------------------------------------------------------------------ +// +// This module defines methods in the ZipFile class associated to the FileFilter +// capability - selecting files to add into the archive, or selecting entries to +// retrieve from the archive based on criteria including the filename, size, date, or +// attributes. It is something like a "poor man's LINQ". I included it into DotNetZip +// because not everyone has .NET 3.5 yet. When using DotNetZip on .NET 3.5, the LINQ +// query/selection will be superior. +// +// These methods are segregated into a different module to facilitate easy exclusion for +// those people who wish to have a smaller library without this function. +// +// ------------------------------------------------------------------ + + +using System; +using System.IO; +using System.Collections.Generic; + +namespace Ionic.Zip +{ + + partial class ZipFile + { + /// + /// Adds to the ZipFile a set of files from the current working directory on + /// disk, that conform to the specified criteria. + /// + /// + /// + /// + /// This method selects files from the the current working directory matching + /// the specified criteria, and adds them to the ZipFile. + /// + /// + /// + /// Specify the criteria in statements of 3 elements: a noun, an operator, and + /// a value. Consider the string "name != *.doc" . The noun is "name". The + /// operator is "!=", implying "Not Equal". The value is "*.doc". That + /// criterion, in English, says "all files with a name that does not end in + /// the .doc extension." + /// + /// + /// + /// Supported nouns include "name" (or "filename") for the filename; "atime", + /// "mtime", and "ctime" for last access time, last modfied time, and created + /// time of the file, respectively; "attributes" (or "attrs") for the file + /// attributes; "size" (or "length") for the file length (uncompressed), and + /// "type" for the type of object, either a file or a directory. The + /// "attributes", "name" and "type" nouns both support = and != as operators. + /// The "size", "atime", "mtime", and "ctime" nouns support = and !=, and + /// >, >=, <, <= as well. The times are taken to be expressed in + /// local time. + /// + /// + /// + /// Specify values for the file attributes as a string with one or more of the + /// characters H,R,S,A,I,L in any order, implying file attributes of Hidden, + /// ReadOnly, System, Archive, NotContextIndexed, and ReparsePoint (symbolic + /// link) respectively. + /// + /// + /// + /// To specify a time, use YYYY-MM-DD-HH:mm:ss or YYYY/MM/DD-HH:mm:ss as the + /// format. If you omit the HH:mm:ss portion, it is assumed to be 00:00:00 + /// (midnight). + /// + /// + /// + /// The value for a size criterion is expressed in integer quantities of bytes, + /// kilobytes (use k or kb after the number), megabytes (m or mb), or gigabytes + /// (g or gb). + /// + /// + /// + /// The value for a name is a pattern to match against the filename, potentially + /// including wildcards. The pattern follows CMD.exe glob rules: * implies one + /// or more of any character, while ? implies one character. If the name + /// pattern contains any slashes, it is matched to the entire filename, + /// including the path; otherwise, it is matched against only the filename + /// without the path. This means a pattern of "*\*.*" matches all files one + /// directory level deep, while a pattern of "*.*" matches all files in all + /// directories. + /// + /// + /// + /// To specify a name pattern that includes spaces, use single quotes around the + /// pattern. A pattern of "'* *.*'" will match all files that have spaces in + /// the filename. The full criteria string for that would be "name = '* *.*'" . + /// + /// + /// + /// The value for a type criterion is either F (implying a file) or D (implying + /// a directory). + /// + /// + /// + /// Some examples: + /// + /// + /// + /// + /// criteria + /// Files retrieved + /// + /// + /// + /// name != *.xls + /// any file with an extension that is not .xls + /// + /// + /// + /// + /// name = *.mp3 + /// any file with a .mp3 extension. + /// + /// + /// + /// + /// *.mp3 + /// (same as above) any file with a .mp3 extension. + /// + /// + /// + /// + /// attributes = A + /// all files whose attributes include the Archive bit. + /// + /// + /// + /// + /// attributes != H + /// all files whose attributes do not include the Hidden bit. + /// + /// + /// + /// + /// mtime > 2009-01-01 + /// all files with a last modified time after January 1st, 2009. + /// + /// + /// + /// + /// size > 2gb + /// all files whose uncompressed size is greater than 2gb. + /// + /// + /// + /// + /// type = D + /// all directories in the filesystem. + /// + /// + /// + /// + /// + /// You can combine criteria with the conjunctions AND or OR. Using a string + /// like "name = *.txt AND size >= 100k" for the selectionCriteria retrieves + /// entries whose names end in .txt, and whose uncompressed size is greater than + /// or equal to 100 kilobytes. + /// + /// + /// + /// For more complex combinations of criteria, you can use parenthesis to group + /// clauses in the boolean logic. Without parenthesis, the precedence of the + /// criterion atoms is determined by order of appearance. Unlike the C# + /// language, the AND conjunction does not take precendence over the logical OR. + /// This is important only in strings that contain 3 or more criterion atoms. + /// In other words, "name = *.txt and size > 1000 or attributes = H" implies + /// "((name = *.txt AND size > 1000) OR attributes = H)" while "attributes = + /// H OR name = *.txt and size > 1000" evaluates to "((attributes = H OR name + /// = *.txt) AND size > 1000)". When in doubt, use parenthesis. + /// + /// + /// + /// Using time properties requires some extra care. If you want to retrieve all + /// entries that were last updated on 2009 February 14, specify a time range + /// like so:"mtime >= 2009-02-14 AND mtime < 2009-02-15". Read this to + /// say: all files updated after 12:00am on February 14th, until 12:00am on + /// February 15th. You can use the same bracketing approach to specify any time + /// period - a year, a month, a week, and so on. + /// + /// + /// + /// The syntax allows one special case: if you provide a string with no spaces, it is + /// treated as a pattern to match for the filename. Therefore a string like "*.xls" + /// will be equivalent to specifying "name = *.xls". + /// + /// + /// + /// There is no logic in this method that insures that the file inclusion + /// criteria are internally consistent. For example, it's possible to specify + /// criteria that says the file must have a size of less than 100 bytes, as well + /// as a size that is greater than 1000 bytes. Obviously no file will ever + /// satisfy such criteria, but this method does not detect such logical + /// inconsistencies. The caller is responsible for insuring the criteria are + /// sensible. + /// + /// + /// + /// Using this method, the file selection does not recurse into + /// subdirectories, and the full path of the selected files is included in the + /// entries added into the zip archive. If you don't like these behaviors, + /// see the other overloads of this method. + /// + /// + /// + /// + /// This example zips up all *.csv files in the current working directory. + /// + /// using (ZipFile zip = new ZipFile()) + /// { + /// // To just match on filename wildcards, + /// // use the shorthand form of the selectionCriteria string. + /// zip.AddSelectedFiles("*.csv"); + /// zip.Save(PathToZipArchive); + /// } + /// + /// + /// Using zip As ZipFile = New ZipFile() + /// zip.AddSelectedFiles("*.csv") + /// zip.Save(PathToZipArchive) + /// End Using + /// + /// + /// + /// The criteria for file selection + public void AddSelectedFiles(String selectionCriteria) + { + this.AddSelectedFiles(selectionCriteria, ".", null, false); + } + + /// + /// Adds to the ZipFile a set of files from the disk that conform to the + /// specified criteria, optionally recursing into subdirectories. + /// + /// + /// + /// + /// This method selects files from the the current working directory matching + /// the specified criteria, and adds them to the ZipFile. If + /// recurseDirectories is true, files are also selected from + /// subdirectories, and the directory structure in the filesystem is + /// reproduced in the zip archive, rooted at the current working directory. + /// + /// + /// + /// Using this method, the full path of the selected files is included in the + /// entries added into the zip archive. If you don't want this behavior, use + /// one of the overloads of this method that allows the specification of a + /// directoryInArchive. + /// + /// + /// + /// For details on the syntax for the selectionCriteria parameter, see . + /// + /// + /// + /// + /// + /// + /// This example zips up all *.xml files in the current working directory, or any + /// subdirectory, that are larger than 1mb. + /// + /// + /// using (ZipFile zip = new ZipFile()) + /// { + /// // Use a compound expression in the selectionCriteria string. + /// zip.AddSelectedFiles("name = *.xml and size > 1024kb", true); + /// zip.Save(PathToZipArchive); + /// } + /// + /// + /// Using zip As ZipFile = New ZipFile() + /// ' Use a compound expression in the selectionCriteria string. + /// zip.AddSelectedFiles("name = *.xml and size > 1024kb", true) + /// zip.Save(PathToZipArchive) + /// End Using + /// + /// + /// + /// The criteria for file selection + /// + /// + /// If true, the file selection will recurse into subdirectories. + /// + public void AddSelectedFiles(String selectionCriteria, bool recurseDirectories) + { + this.AddSelectedFiles(selectionCriteria, ".", null, recurseDirectories); + } + + /// + /// Adds to the ZipFile a set of files from a specified directory in the + /// filesystem, that conform to the specified criteria. + /// + /// + /// + /// + /// This method selects files that conform to the specified criteria, from the + /// the specified directory on disk, and adds them to the ZipFile. The search + /// does not recurse into subdirectores. + /// + /// + /// + /// Using this method, the full filesystem path of the files on disk is + /// reproduced on the entries added to the zip file. If you don't want this + /// behavior, use one of the other overloads of this method. + /// + /// + /// + /// For details on the syntax for the selectionCriteria parameter, see . + /// + /// + /// + /// + /// + /// + /// This example zips up all *.xml files larger than 1mb in the directory + /// given by "d:\rawdata". + /// + /// + /// using (ZipFile zip = new ZipFile()) + /// { + /// // Use a compound expression in the selectionCriteria string. + /// zip.AddSelectedFiles("name = *.xml and size > 1024kb", "d:\\rawdata"); + /// zip.Save(PathToZipArchive); + /// } + /// + /// + /// + /// Using zip As ZipFile = New ZipFile() + /// ' Use a compound expression in the selectionCriteria string. + /// zip.AddSelectedFiles("name = *.xml and size > 1024kb", "d:\rawdata) + /// zip.Save(PathToZipArchive) + /// End Using + /// + /// + /// + /// The criteria for file selection + /// + /// + /// The name of the directory on the disk from which to select files. + /// + public void AddSelectedFiles(String selectionCriteria, String directoryOnDisk) + { + this.AddSelectedFiles(selectionCriteria, directoryOnDisk, null, false); + } + + + /// + /// Adds to the ZipFile a set of files from the specified directory on disk, + /// that conform to the specified criteria. + /// + /// + /// + /// + /// + /// This method selects files from the the specified disk directory matching + /// the specified selection criteria, and adds them to the ZipFile. If + /// recurseDirectories is true, files are also selected from + /// subdirectories. + /// + /// + /// + /// The full directory structure in the filesystem is reproduced on the + /// entries added to the zip archive. If you don't want this behavior, use + /// one of the overloads of this method that allows the specification of a + /// directoryInArchive. + /// + /// + /// + /// For details on the syntax for the selectionCriteria parameter, see . + /// + /// + /// + /// + /// + /// This example zips up all *.csv files in the "files" directory, or any + /// subdirectory, that have been saved since 2009 February 14th. + /// + /// + /// using (ZipFile zip = new ZipFile()) + /// { + /// // Use a compound expression in the selectionCriteria string. + /// zip.AddSelectedFiles("name = *.csv and mtime > 2009-02-14", "files", true); + /// zip.Save(PathToZipArchive); + /// } + /// + /// + /// Using zip As ZipFile = New ZipFile() + /// ' Use a compound expression in the selectionCriteria string. + /// zip.AddSelectedFiles("name = *.csv and mtime > 2009-02-14", "files", true) + /// zip.Save(PathToZipArchive) + /// End Using + /// + /// + /// + /// + /// This example zips up all files in the current working + /// directory, and all its child directories, except those in + /// the excludethis subdirectory. + /// + /// Using Zip As ZipFile = New ZipFile(zipfile) + /// Zip.AddSelectedFfiles("name != 'excludethis\*.*'", datapath, True) + /// Zip.Save() + /// End Using + /// + /// + /// + /// The criteria for file selection + /// + /// + /// The filesystem path from which to select files. + /// + /// + /// + /// If true, the file selection will recurse into subdirectories. + /// + public void AddSelectedFiles(String selectionCriteria, String directoryOnDisk, bool recurseDirectories) + { + this.AddSelectedFiles(selectionCriteria, directoryOnDisk, null, recurseDirectories); + } + + + /// + /// Adds to the ZipFile a selection of files from the specified directory on + /// disk, that conform to the specified criteria, and using a specified root + /// path for entries added to the zip archive. + /// + /// + /// + /// + /// This method selects files from the specified disk directory matching the + /// specified selection criteria, and adds those files to the ZipFile, using + /// the specified directory path in the archive. The search does not recurse + /// into subdirectories. For details on the syntax for the selectionCriteria + /// parameter, see . + /// + /// + /// + /// + /// + /// + /// This example zips up all *.psd files in the "photos" directory that have + /// been saved since 2009 February 14th, and puts them all in a zip file, + /// using the directory name of "content" in the zip archive itself. When the + /// zip archive is unzipped, the folder containing the .psd files will be + /// named "content". + /// + /// + /// using (ZipFile zip = new ZipFile()) + /// { + /// // Use a compound expression in the selectionCriteria string. + /// zip.AddSelectedFiles("name = *.psd and mtime > 2009-02-14", "photos", "content"); + /// zip.Save(PathToZipArchive); + /// } + /// + /// + /// Using zip As ZipFile = New ZipFile + /// zip.AddSelectedFiles("name = *.psd and mtime > 2009-02-14", "photos", "content") + /// zip.Save(PathToZipArchive) + /// End Using + /// + /// + /// + /// + /// The criteria for selection of files to add to the ZipFile. + /// + /// + /// + /// The path to the directory in the filesystem from which to select files. + /// + /// + /// + /// Specifies a directory path to use to in place of the + /// directoryOnDisk. This path may, or may not, correspond to a real + /// directory in the current filesystem. If the files within the zip are + /// later extracted, this is the path used for the extracted file. Passing + /// null (nothing in VB) will use the path on the file name, if any; in other + /// words it would use directoryOnDisk, plus any subdirectory. Passing + /// the empty string ("") will insert the item at the root path within the + /// archive. + /// + public void AddSelectedFiles(String selectionCriteria, + String directoryOnDisk, + String directoryPathInArchive) + { + this.AddSelectedFiles(selectionCriteria, directoryOnDisk, directoryPathInArchive, false); + } + + /// + /// Adds to the ZipFile a selection of files from the specified directory on + /// disk, that conform to the specified criteria, optionally recursing through + /// subdirectories, and using a specified root path for entries added to the + /// zip archive. + /// + /// + /// + /// This method selects files from the specified disk directory that match the + /// specified selection criteria, and adds those files to the ZipFile, using + /// the specified directory path in the archive. If recurseDirectories + /// is true, files are also selected from subdirectories, and the directory + /// structure in the filesystem is reproduced in the zip archive, rooted at + /// the directory specified by directoryOnDisk. For details on the + /// syntax for the selectionCriteria parameter, see . + /// + /// + /// + /// + /// This example zips up all files that are NOT *.pst files, in the current + /// working directory and any subdirectories. + /// + /// + /// using (ZipFile zip = new ZipFile()) + /// { + /// zip.AddSelectedFiles("name != *.pst", SourceDirectory, "backup", true); + /// zip.Save(PathToZipArchive); + /// } + /// + /// + /// Using zip As ZipFile = New ZipFile + /// zip.AddSelectedFiles("name != *.pst", SourceDirectory, "backup", true) + /// zip.Save(PathToZipArchive) + /// End Using + /// + /// + /// + /// + /// The criteria for selection of files to add to the ZipFile. + /// + /// + /// + /// The path to the directory in the filesystem from which to select files. + /// + /// + /// + /// Specifies a directory path to use to in place of the + /// directoryOnDisk. This path may, or may not, correspond to a real + /// directory in the current filesystem. If the files within the zip are + /// later extracted, this is the path used for the extracted file. Passing + /// null (nothing in VB) will use the path on the file name, if any; in other + /// words it would use directoryOnDisk, plus any subdirectory. Passing + /// the empty string ("") will insert the item at the root path within the + /// archive. + /// + /// + /// + /// If true, the method also scans subdirectories for files matching the + /// criteria. + /// + public void AddSelectedFiles(String selectionCriteria, + String directoryOnDisk, + String directoryPathInArchive, + bool recurseDirectories) + { + _AddOrUpdateSelectedFiles(selectionCriteria, + directoryOnDisk, + directoryPathInArchive, + recurseDirectories, + false); + } + + /// + /// Updates the ZipFile with a selection of files from the disk that conform + /// to the specified criteria. + /// + /// + /// + /// This method selects files from the specified disk directory that match the + /// specified selection criteria, and Updates the ZipFile with those + /// files, using the specified directory path in the archive. If + /// recurseDirectories is true, files are also selected from + /// subdirectories, and the directory structure in the filesystem is + /// reproduced in the zip archive, rooted at the directory specified by + /// directoryOnDisk. For details on the syntax for the + /// selectionCriteria parameter, see . + /// + /// + /// + /// The criteria for selection of files to add to the ZipFile. + /// + /// + /// + /// The path to the directory in the filesystem from which to select files. + /// + /// + /// + /// Specifies a directory path to use to in place of the + /// directoryOnDisk. This path may, or may not, correspond to a + /// real directory in the current filesystem. If the files within the zip + /// are later extracted, this is the path used for the extracted file. + /// Passing null (nothing in VB) will use the path on the file name, if + /// any; in other words it would use directoryOnDisk, plus any + /// subdirectory. Passing the empty string ("") will insert the item at + /// the root path within the archive. + /// + /// + /// + /// If true, the method also scans subdirectories for files matching the criteria. + /// + /// + /// + public void UpdateSelectedFiles(String selectionCriteria, + String directoryOnDisk, + String directoryPathInArchive, + bool recurseDirectories) + { + _AddOrUpdateSelectedFiles(selectionCriteria, + directoryOnDisk, + directoryPathInArchive, + recurseDirectories, + true); + } + + + private string EnsureendInSlash(string s) + { + if (s.EndsWith("\\")) return s; + return s + "\\"; + } + + private void _AddOrUpdateSelectedFiles(String selectionCriteria, + String directoryOnDisk, + String directoryPathInArchive, + bool recurseDirectories, + bool wantUpdate) + { + if (directoryOnDisk == null && (Directory.Exists(selectionCriteria))) + { + directoryOnDisk = selectionCriteria; + selectionCriteria = "*.*"; + } + else if (String.IsNullOrEmpty(directoryOnDisk)) + { + directoryOnDisk = "."; + } + + // workitem 9176 + while (directoryOnDisk.EndsWith("\\")) directoryOnDisk = directoryOnDisk.Substring(0, directoryOnDisk.Length - 1); + if (Verbose) StatusMessageTextWriter.WriteLine("adding selection '{0}' from dir '{1}'...", + selectionCriteria, directoryOnDisk); + Ionic.FileSelector ff = new Ionic.FileSelector(selectionCriteria, + AddDirectoryWillTraverseReparsePoints); + var itemsToAdd = ff.SelectFiles(directoryOnDisk, recurseDirectories); + + if (Verbose) StatusMessageTextWriter.WriteLine("found {0} files...", itemsToAdd.Count); + + OnAddStarted(); + + AddOrUpdateAction action = (wantUpdate) ? AddOrUpdateAction.AddOrUpdate : AddOrUpdateAction.AddOnly; + foreach (var item in itemsToAdd) + { + // workitem 10153 + string dirInArchive = (directoryPathInArchive == null) + ? null + // workitem 12260 + : ReplaceLeadingDirectory(Path.GetDirectoryName(item), + directoryOnDisk, + directoryPathInArchive); + + if (File.Exists(item)) + { + if (wantUpdate) + this.UpdateFile(item, dirInArchive); + else + this.AddFile(item, dirInArchive); + } + else + { + // this adds "just" the directory, without recursing to the contained files + AddOrUpdateDirectoryImpl(item, dirInArchive, action, false, 0); + } + } + + OnAddCompleted(); + } + + + // workitem 12260 + private static string ReplaceLeadingDirectory(string original, + string pattern, + string replacement) + { + string upperString = original.ToUpper(); + string upperPattern = pattern.ToUpper(); + int p1 = upperString.IndexOf(upperPattern); + if (p1 != 0) return original; + return replacement + original.Substring(upperPattern.Length); + } + +#if NOT + private static string ReplaceEx(string original, + string pattern, + string replacement) + { + int count, position0, position1; + count = position0 = position1 = 0; + string upperString = original.ToUpper(); + string upperPattern = pattern.ToUpper(); + int inc = (original.Length/pattern.Length) * + (replacement.Length-pattern.Length); + char [] chars = new char[original.Length + Math.Max(0, inc)]; + while( (position1 = upperString.IndexOf(upperPattern, + position0)) != -1 ) + { + for ( int i=position0 ; i < position1 ; ++i ) + chars[count++] = original[i]; + for ( int i=0 ; i < replacement.Length ; ++i ) + chars[count++] = replacement[i]; + position0 = position1+pattern.Length; + } + if ( position0 == 0 ) return original; + for ( int i=position0 ; i < original.Length ; ++i ) + chars[count++] = original[i]; + return new string(chars, 0, count); + } +#endif + + /// + /// Retrieve entries from the zipfile by specified criteria. + /// + /// + /// + /// + /// This method allows callers to retrieve the collection of entries from the zipfile + /// that fit the specified criteria. The criteria are described in a string format, and + /// can include patterns for the filename; constraints on the size of the entry; + /// constraints on the last modified, created, or last accessed time for the file + /// described by the entry; or the attributes of the entry. + /// + /// + /// + /// For details on the syntax for the selectionCriteria parameter, see . + /// + /// + /// + /// This method is intended for use with a ZipFile that has been read from storage. + /// When creating a new ZipFile, this method will work only after the ZipArchive has + /// been Saved to the disk (the ZipFile class subsequently and implicitly reads the Zip + /// archive from storage.) Calling SelectEntries on a ZipFile that has not yet been + /// saved will deliver undefined results. + /// + /// + /// + /// + /// Thrown if selectionCriteria has an invalid syntax. + /// + /// + /// + /// This example selects all the PhotoShop files from within an archive, and extracts them + /// to the current working directory. + /// + /// using (ZipFile zip1 = ZipFile.Read(ZipFileName)) + /// { + /// var PhotoShopFiles = zip1.SelectEntries("*.psd"); + /// foreach (ZipEntry psd in PhotoShopFiles) + /// { + /// psd.Extract(); + /// } + /// } + /// + /// + /// Using zip1 As ZipFile = ZipFile.Read(ZipFileName) + /// Dim PhotoShopFiles as ICollection(Of ZipEntry) + /// PhotoShopFiles = zip1.SelectEntries("*.psd") + /// Dim psd As ZipEntry + /// For Each psd In PhotoShopFiles + /// psd.Extract + /// Next + /// End Using + /// + /// + /// the string that specifies which entries to select + /// a collection of ZipEntry objects that conform to the inclusion spec + public ICollection SelectEntries(String selectionCriteria) + { + Ionic.FileSelector ff = new Ionic.FileSelector(selectionCriteria, + AddDirectoryWillTraverseReparsePoints); + return ff.SelectEntries(this); + } + + + /// + /// Retrieve entries from the zipfile by specified criteria. + /// + /// + /// + /// + /// This method allows callers to retrieve the collection of entries from the zipfile + /// that fit the specified criteria. The criteria are described in a string format, and + /// can include patterns for the filename; constraints on the size of the entry; + /// constraints on the last modified, created, or last accessed time for the file + /// described by the entry; or the attributes of the entry. + /// + /// + /// + /// For details on the syntax for the selectionCriteria parameter, see . + /// + /// + /// + /// This method is intended for use with a ZipFile that has been read from storage. + /// When creating a new ZipFile, this method will work only after the ZipArchive has + /// been Saved to the disk (the ZipFile class subsequently and implicitly reads the Zip + /// archive from storage.) Calling SelectEntries on a ZipFile that has not yet been + /// saved will deliver undefined results. + /// + /// + /// + /// + /// Thrown if selectionCriteria has an invalid syntax. + /// + /// + /// + /// + /// using (ZipFile zip1 = ZipFile.Read(ZipFileName)) + /// { + /// var UpdatedPhotoShopFiles = zip1.SelectEntries("*.psd", "UpdatedFiles"); + /// foreach (ZipEntry e in UpdatedPhotoShopFiles) + /// { + /// // prompt for extract here + /// if (WantExtract(e.FileName)) + /// e.Extract(); + /// } + /// } + /// + /// + /// Using zip1 As ZipFile = ZipFile.Read(ZipFileName) + /// Dim UpdatedPhotoShopFiles As ICollection(Of ZipEntry) = zip1.SelectEntries("*.psd", "UpdatedFiles") + /// Dim e As ZipEntry + /// For Each e In UpdatedPhotoShopFiles + /// ' prompt for extract here + /// If Me.WantExtract(e.FileName) Then + /// e.Extract + /// End If + /// Next + /// End Using + /// + /// + /// the string that specifies which entries to select + /// + /// + /// the directory in the archive from which to select entries. If null, then + /// all directories in the archive are used. + /// + /// + /// a collection of ZipEntry objects that conform to the inclusion spec + public ICollection SelectEntries(String selectionCriteria, string directoryPathInArchive) + { + Ionic.FileSelector ff = new Ionic.FileSelector(selectionCriteria, + AddDirectoryWillTraverseReparsePoints); + return ff.SelectEntries(this, directoryPathInArchive); + } + + + + /// + /// Remove entries from the zipfile by specified criteria. + /// + /// + /// + /// + /// This method allows callers to remove the collection of entries from the zipfile + /// that fit the specified criteria. The criteria are described in a string format, and + /// can include patterns for the filename; constraints on the size of the entry; + /// constraints on the last modified, created, or last accessed time for the file + /// described by the entry; or the attributes of the entry. + /// + /// + /// + /// For details on the syntax for the selectionCriteria parameter, see . + /// + /// + /// + /// This method is intended for use with a ZipFile that has been read from storage. + /// When creating a new ZipFile, this method will work only after the ZipArchive has + /// been Saved to the disk (the ZipFile class subsequently and implicitly reads the Zip + /// archive from storage.) Calling SelectEntries on a ZipFile that has not yet been + /// saved will deliver undefined results. + /// + /// + /// + /// + /// Thrown if selectionCriteria has an invalid syntax. + /// + /// + /// + /// This example removes all entries in a zip file that were modified prior to January 1st, 2008. + /// + /// using (ZipFile zip1 = ZipFile.Read(ZipFileName)) + /// { + /// // remove all entries from prior to Jan 1, 2008 + /// zip1.RemoveEntries("mtime < 2008-01-01"); + /// // don't forget to save the archive! + /// zip1.Save(); + /// } + /// + /// + /// Using zip As ZipFile = ZipFile.Read(ZipFileName) + /// ' remove all entries from prior to Jan 1, 2008 + /// zip1.RemoveEntries("mtime < 2008-01-01") + /// ' do not forget to save the archive! + /// zip1.Save + /// End Using + /// + /// + /// the string that specifies which entries to select + /// the number of entries removed + public int RemoveSelectedEntries(String selectionCriteria) + { + var selection = this.SelectEntries(selectionCriteria); + this.RemoveEntries(selection); + return selection.Count; + } + + + /// + /// Remove entries from the zipfile by specified criteria, and within the specified + /// path in the archive. + /// + /// + /// + /// + /// This method allows callers to remove the collection of entries from the zipfile + /// that fit the specified criteria. The criteria are described in a string format, and + /// can include patterns for the filename; constraints on the size of the entry; + /// constraints on the last modified, created, or last accessed time for the file + /// described by the entry; or the attributes of the entry. + /// + /// + /// + /// For details on the syntax for the selectionCriteria parameter, see . + /// + /// + /// + /// This method is intended for use with a ZipFile that has been read from storage. + /// When creating a new ZipFile, this method will work only after the ZipArchive has + /// been Saved to the disk (the ZipFile class subsequently and implicitly reads the Zip + /// archive from storage.) Calling SelectEntries on a ZipFile that has not yet been + /// saved will deliver undefined results. + /// + /// + /// + /// + /// Thrown if selectionCriteria has an invalid syntax. + /// + /// + /// + /// + /// using (ZipFile zip1 = ZipFile.Read(ZipFileName)) + /// { + /// // remove all entries from prior to Jan 1, 2008 + /// zip1.RemoveEntries("mtime < 2008-01-01", "documents"); + /// // a call to ZipFile.Save will make the modifications permanent + /// zip1.Save(); + /// } + /// + /// + /// Using zip As ZipFile = ZipFile.Read(ZipFileName) + /// ' remove all entries from prior to Jan 1, 2008 + /// zip1.RemoveEntries("mtime < 2008-01-01", "documents") + /// ' a call to ZipFile.Save will make the modifications permanent + /// zip1.Save + /// End Using + /// + /// + /// + /// the string that specifies which entries to select + /// + /// the directory in the archive from which to select entries. If null, then + /// all directories in the archive are used. + /// + /// the number of entries removed + public int RemoveSelectedEntries(String selectionCriteria, string directoryPathInArchive) + { + var selection = this.SelectEntries(selectionCriteria, directoryPathInArchive); + this.RemoveEntries(selection); + return selection.Count; + } + + + /// + /// Selects and Extracts a set of Entries from the ZipFile. + /// + /// + /// + /// + /// The entries are extracted into the current working directory. + /// + /// + /// + /// If any of the files to be extracted already exist, then the action taken is as + /// specified in the property on the + /// corresponding ZipEntry instance. By default, the action taken in this case is to + /// throw an exception. + /// + /// + /// + /// For information on the syntax of the selectionCriteria string, + /// see . + /// + /// + /// + /// + /// This example shows how extract all XML files modified after 15 January 2009. + /// + /// using (ZipFile zip = ZipFile.Read(zipArchiveName)) + /// { + /// zip.ExtractSelectedEntries("name = *.xml and mtime > 2009-01-15"); + /// } + /// + /// + /// the selection criteria for entries to extract. + /// + /// + public void ExtractSelectedEntries(String selectionCriteria) + { + foreach (ZipEntry e in SelectEntries(selectionCriteria)) + { + e.Password = _Password; // possibly null + e.Extract(); + } + } + + + /// + /// Selects and Extracts a set of Entries from the ZipFile. + /// + /// + /// + /// + /// The entries are extracted into the current working directory. When extraction would would + /// overwrite an existing filesystem file, the action taken is as specified in the + /// parameter. + /// + /// + /// + /// For information on the syntax of the string describing the entry selection criteria, + /// see . + /// + /// + /// + /// + /// This example shows how extract all XML files modified after 15 January 2009, + /// overwriting any existing files. + /// + /// using (ZipFile zip = ZipFile.Read(zipArchiveName)) + /// { + /// zip.ExtractSelectedEntries("name = *.xml and mtime > 2009-01-15", + /// ExtractExistingFileAction.OverwriteSilently); + /// } + /// + /// + /// + /// the selection criteria for entries to extract. + /// + /// + /// The action to take if extraction would overwrite an existing file. + /// + public void ExtractSelectedEntries(String selectionCriteria, ExtractExistingFileAction extractExistingFile) + { + foreach (ZipEntry e in SelectEntries(selectionCriteria)) + { + e.Password = _Password; // possibly null + e.Extract(extractExistingFile); + } + } + + + /// + /// Selects and Extracts a set of Entries from the ZipFile. + /// + /// + /// + /// + /// The entries are selected from the specified directory within the archive, and then + /// extracted into the current working directory. + /// + /// + /// + /// If any of the files to be extracted already exist, then the action taken is as + /// specified in the property on the + /// corresponding ZipEntry instance. By default, the action taken in this case is to + /// throw an exception. + /// + /// + /// + /// For information on the syntax of the string describing the entry selection criteria, + /// see . + /// + /// + /// + /// + /// This example shows how extract all XML files modified after 15 January 2009, + /// and writes them to the "unpack" directory. + /// + /// using (ZipFile zip = ZipFile.Read(zipArchiveName)) + /// { + /// zip.ExtractSelectedEntries("name = *.xml and mtime > 2009-01-15","unpack"); + /// } + /// + /// + /// + /// the selection criteria for entries to extract. + /// + /// + /// the directory in the archive from which to select entries. If null, then + /// all directories in the archive are used. + /// + /// + /// + public void ExtractSelectedEntries(String selectionCriteria, String directoryPathInArchive) + { + foreach (ZipEntry e in SelectEntries(selectionCriteria, directoryPathInArchive)) + { + e.Password = _Password; // possibly null + e.Extract(); + } + } + + + /// + /// Selects and Extracts a set of Entries from the ZipFile. + /// + /// + /// + /// + /// The entries are extracted into the specified directory. If any of the files to be + /// extracted already exist, an exception will be thrown. + /// + /// + /// For information on the syntax of the string describing the entry selection criteria, + /// see . + /// + /// + /// + /// the selection criteria for entries to extract. + /// + /// + /// the directory in the archive from which to select entries. If null, then + /// all directories in the archive are used. + /// + /// + /// + /// the directory on the disk into which to extract. It will be created + /// if it does not exist. + /// + public void ExtractSelectedEntries(String selectionCriteria, string directoryInArchive, string extractDirectory) + { + foreach (ZipEntry e in SelectEntries(selectionCriteria, directoryInArchive)) + { + e.Password = _Password; // possibly null + e.Extract(extractDirectory); + } + } + + + /// + /// Selects and Extracts a set of Entries from the ZipFile. + /// + /// + /// + /// + /// The entries are extracted into the specified directory. When extraction would would + /// overwrite an existing filesystem file, the action taken is as specified in the + /// parameter. + /// + /// + /// + /// For information on the syntax of the string describing the entry selection criteria, + /// see . + /// + /// + /// + /// + /// This example shows how extract all files with an XML extension or with a size larger than 100,000 bytes, + /// and puts them in the unpack directory. For any files that already exist in + /// that destination directory, they will not be overwritten. + /// + /// using (ZipFile zip = ZipFile.Read(zipArchiveName)) + /// { + /// zip.ExtractSelectedEntries("name = *.xml or size > 100000", + /// null, + /// "unpack", + /// ExtractExistingFileAction.DontOverwrite); + /// } + /// + /// + /// + /// the selection criteria for entries to extract. + /// + /// + /// The directory on the disk into which to extract. It will be created if it does not exist. + /// + /// + /// + /// The directory in the archive from which to select entries. If null, then + /// all directories in the archive are used. + /// + /// + /// + /// The action to take if extraction would overwrite an existing file. + /// + /// + public void ExtractSelectedEntries(String selectionCriteria, string directoryPathInArchive, string extractDirectory, ExtractExistingFileAction extractExistingFile) + { + foreach (ZipEntry e in SelectEntries(selectionCriteria, directoryPathInArchive)) + { + e.Password = _Password; // possibly null + e.Extract(extractDirectory, extractExistingFile); + } + } + + } + +} + + + +namespace Ionic +{ + internal abstract partial class SelectionCriterion + { + internal abstract bool Evaluate(Ionic.Zip.ZipEntry entry); + } + + + internal partial class NameCriterion : SelectionCriterion + { + internal override bool Evaluate(Ionic.Zip.ZipEntry entry) + { + // swap forward slashes in the entry.FileName for backslashes + string transformedFileName = entry.FileName.Replace("/", "\\"); + + return _Evaluate(transformedFileName); + } + } + + + internal partial class SizeCriterion : SelectionCriterion + { + internal override bool Evaluate(Ionic.Zip.ZipEntry entry) + { + return _Evaluate(entry.UncompressedSize); + } + } + + internal partial class TimeCriterion : SelectionCriterion + { + internal override bool Evaluate(Ionic.Zip.ZipEntry entry) + { + DateTime x; + switch (Which) + { + case WhichTime.atime: + x = entry.AccessedTime; + break; + case WhichTime.mtime: + x = entry.ModifiedTime; + break; + case WhichTime.ctime: + x = entry.CreationTime; + break; + default: throw new ArgumentException("??time"); + } + return _Evaluate(x); + } + } + + + internal partial class TypeCriterion : SelectionCriterion + { + internal override bool Evaluate(Ionic.Zip.ZipEntry entry) + { + bool result = (ObjectType == 'D') + ? entry.IsDirectory + : !entry.IsDirectory; + + if (Operator != ComparisonOperator.EqualTo) + result = !result; + return result; + } + } + +#if !SILVERLIGHT + internal partial class AttributesCriterion : SelectionCriterion + { + internal override bool Evaluate(Ionic.Zip.ZipEntry entry) + { + FileAttributes fileAttrs = entry.Attributes; + return _Evaluate(fileAttrs); + } + } +#endif + + internal partial class CompoundCriterion : SelectionCriterion + { + internal override bool Evaluate(Ionic.Zip.ZipEntry entry) + { + bool result = Left.Evaluate(entry); + switch (Conjunction) + { + case LogicalConjunction.AND: + if (result) + result = Right.Evaluate(entry); + break; + case LogicalConjunction.OR: + if (!result) + result = Right.Evaluate(entry); + break; + case LogicalConjunction.XOR: + result ^= Right.Evaluate(entry); + break; + } + return result; + } + } + + + + public partial class FileSelector + { + private bool Evaluate(Ionic.Zip.ZipEntry entry) + { + bool result = _Criterion.Evaluate(entry); + return result; + } + + /// + /// Retrieve the ZipEntry items in the ZipFile that conform to the specified criteria. + /// + /// + /// + /// + /// This method applies the criteria set in the FileSelector instance (as described in + /// the ) to the specified ZipFile. Using this + /// method, for example, you can retrieve all entries from the given ZipFile that + /// have filenames ending in .txt. + /// + /// + /// + /// Normally, applications would not call this method directly. This method is used + /// by the ZipFile class. + /// + /// + /// + /// Using the appropriate SelectionCriteria, you can retrieve entries based on size, + /// time, and attributes. See for a + /// description of the syntax of the SelectionCriteria string. + /// + /// + /// + /// + /// The ZipFile from which to retrieve entries. + /// + /// a collection of ZipEntry objects that conform to the criteria. + public ICollection SelectEntries(Ionic.Zip.ZipFile zip) + { + if (zip == null) + throw new ArgumentNullException("zip"); + + var list = new List(); + + foreach (Ionic.Zip.ZipEntry e in zip) + { + if (this.Evaluate(e)) + list.Add(e); + } + + return list; + } + + + /// + /// Retrieve the ZipEntry items in the ZipFile that conform to the specified criteria. + /// + /// + /// + /// + /// This method applies the criteria set in the FileSelector instance (as described in + /// the ) to the specified ZipFile. Using this + /// method, for example, you can retrieve all entries from the given ZipFile that + /// have filenames ending in .txt. + /// + /// + /// + /// Normally, applications would not call this method directly. This method is used + /// by the ZipFile class. + /// + /// + /// + /// This overload allows the selection of ZipEntry instances from the ZipFile to be restricted + /// to entries contained within a particular directory in the ZipFile. + /// + /// + /// + /// Using the appropriate SelectionCriteria, you can retrieve entries based on size, + /// time, and attributes. See for a + /// description of the syntax of the SelectionCriteria string. + /// + /// + /// + /// + /// The ZipFile from which to retrieve entries. + /// + /// + /// the directory in the archive from which to select entries. If null, then + /// all directories in the archive are used. + /// + /// + /// a collection of ZipEntry objects that conform to the criteria. + public ICollection SelectEntries(Ionic.Zip.ZipFile zip, string directoryPathInArchive) + { + if (zip == null) + throw new ArgumentNullException("zip"); + + var list = new List(); + // workitem 8559 + string slashSwapped = (directoryPathInArchive == null) ? null : directoryPathInArchive.Replace("/", "\\"); + // workitem 9174 + if (slashSwapped != null) + { + while (slashSwapped.EndsWith("\\")) + slashSwapped = slashSwapped.Substring(0, slashSwapped.Length - 1); + } + foreach (Ionic.Zip.ZipEntry e in zip) + { + if (directoryPathInArchive == null || (Path.GetDirectoryName(e.FileName) == directoryPathInArchive) + || (Path.GetDirectoryName(e.FileName) == slashSwapped)) // workitem 8559 + if (this.Evaluate(e)) + list.Add(e); + } + + return list; + } + + } +} diff --git a/DotNetZip/Zip/ZipFile.cs b/DotNetZip/Zip/ZipFile.cs new file mode 100644 index 0000000..cd49a88 --- /dev/null +++ b/DotNetZip/Zip/ZipFile.cs @@ -0,0 +1,3910 @@ +// ZipFile.cs +// +// Copyright (c) 2006-2010 Dino Chiesa +// All rights reserved. +// +// This module is part of DotNetZip, a zipfile class library. +// The class library reads and writes zip files, according to the format +// described by PKware, at: +// http://www.pkware.com/business_and_developers/developer/popups/appnote.txt +// +// +// There are other Zip class libraries available. +// +// - it is possible to read and write zip files within .NET via the J# runtime. +// But some people don't like to install the extra DLL, which is no longer +// supported by MS. And also, the J# libraries don't support advanced zip +// features, like ZIP64, spanned archives, or AES encryption. +// +// - There are third-party GPL and LGPL libraries available. Some people don't +// like the license, and some of them don't support all the ZIP features, like AES. +// +// - Finally, there are commercial tools (From ComponentOne, XCeed, etc). But +// some people don't want to incur the cost. +// +// This alternative implementation is **not** GPL licensed. It is free of cost, and +// does not require J#. It does require .NET 2.0. It balances a good set of +// features, with ease of use and speed of performance. +// +// This code is released under the Microsoft Public License . +// See the License.txt for details. +// +// +// NB: This implementation originally relied on the +// System.IO.Compression.DeflateStream base class in the .NET Framework +// v2.0 base class library, but now includes a managed-code port of Zlib. +// +// Thu, 08 Oct 2009 17:04 +// + + +using System; +using System.IO; +using System.Collections.Generic; +using Interop = System.Runtime.InteropServices; + + +namespace Ionic.Zip +{ + /// + /// The ZipFile type represents a zip archive file. + /// + /// + /// + /// + /// This is the main type in the DotNetZip class library. This class reads and + /// writes zip files, as defined in the specification + /// for zip files described by PKWare. The compression for this + /// implementation is provided by a managed-code version of Zlib, included with + /// DotNetZip in the classes in the Ionic.Zlib namespace. + /// + /// + /// + /// This class provides a general purpose zip file capability. Use it to read, + /// create, or update zip files. When you want to create zip files using a + /// Stream type to write the zip file, you may want to consider the class. + /// + /// + /// + /// Both the ZipOutputStream class and the ZipFile class can + /// be used to create zip files. Both of them support many of the common zip + /// features, including Unicode, different compression methods and levels, + /// and ZIP64. They provide very similar performance when creating zip + /// files. + /// + /// + /// + /// The ZipFile class is generally easier to use than + /// ZipOutputStream and should be considered a higher-level interface. For + /// example, when creating a zip file via calls to the PutNextEntry() and + /// Write() methods on the ZipOutputStream class, the caller is + /// responsible for opening the file, reading the bytes from the file, writing + /// those bytes into the ZipOutputStream, setting the attributes on the + /// ZipEntry, and setting the created, last modified, and last accessed + /// timestamps on the zip entry. All of these things are done automatically by a + /// call to ZipFile.AddFile(). + /// For this reason, the ZipOutputStream is generally recommended for use + /// only when your application emits arbitrary data, not necessarily data from a + /// filesystem file, directly into a zip file, and does so using a Stream + /// metaphor. + /// + /// + /// + /// Aside from the differences in programming model, there are other + /// differences in capability between the two classes. + /// + /// + /// + /// + /// ZipFile can be used to read and extract zip files, in addition to + /// creating zip files. ZipOutputStream cannot read zip files. If you want + /// to use a stream to read zip files, check out the class. + /// + /// + /// + /// ZipOutputStream does not support the creation of segmented or spanned + /// zip files. + /// + /// + /// + /// ZipOutputStream cannot produce a self-extracting archive. + /// + /// + /// + /// + /// Be aware that the ZipFile class implements the interface. In order for ZipFile to + /// produce a valid zip file, you use use it within a using clause (Using + /// in VB), or call the Dispose() method explicitly. See the examples + /// for how to employ a using clause. + /// + /// + /// + [Interop.GuidAttribute("ebc25cf6-9120-4283-b972-0e5520d00005")] + [Interop.ComVisible(true)] +#if !NETCF + [Interop.ClassInterface(Interop.ClassInterfaceType.AutoDispatch)] +#endif + public partial class ZipFile : + System.Collections.IEnumerable, + System.Collections.Generic.IEnumerable, + IDisposable + { + + #region public properties + + /// + /// Indicates whether to perform a full scan of the zip file when reading it. + /// + /// + /// + /// + /// + /// You almost never want to use this property. + /// + /// + /// + /// When reading a zip file, if this flag is true (True in + /// VB), the entire zip archive will be scanned and searched for entries. + /// For large archives, this can take a very, long time. The much more + /// efficient default behavior is to read the zip directory, which is + /// stored at the end of the zip file. But, in some cases the directory is + /// corrupted and you need to perform a full scan of the zip file to + /// determine the contents of the zip file. This property lets you do + /// that, when necessary. + /// + /// + /// + /// This flag is effective only when calling . Normally you would read a ZipFile with the + /// static ZipFile.Read + /// method. But you can't set the FullScan property on the + /// ZipFile instance when you use a static factory method like + /// ZipFile.Read. + /// + /// + /// + /// + /// + /// + /// This example shows how to read a zip file using the full scan approach, + /// and then save it, thereby producing a corrected zip file. + /// + /// + /// using (var zip = new ZipFile()) + /// { + /// zip.FullScan = true; + /// zip.Initialize(zipFileName); + /// zip.Save(newName); + /// } + /// + /// + /// + /// Using zip As New ZipFile + /// zip.FullScan = True + /// zip.Initialize(zipFileName) + /// zip.Save(newName) + /// End Using + /// + /// + /// + public bool FullScan + { + get; + set; + } + + + /// + /// Whether to sort the ZipEntries before saving the file. + /// + /// + /// + /// The default is false. If you have a large number of zip entries, the sort + /// alone can consume significant time. + /// + /// + /// + /// + /// using (var zip = new ZipFile()) + /// { + /// zip.AddFiles(filesToAdd); + /// zip.SortEntriesBeforeSaving = true; + /// zip.Save(name); + /// } + /// + /// + /// + /// Using zip As New ZipFile + /// zip.AddFiles(filesToAdd) + /// zip.SortEntriesBeforeSaving = True + /// zip.Save(name) + /// End Using + /// + /// + /// + public bool SortEntriesBeforeSaving + { + get; + set; + } + + + + /// + /// Indicates whether NTFS Reparse Points, like junctions, should be + /// traversed during calls to AddDirectory(). + /// + /// + /// + /// By default, calls to AddDirectory() will traverse NTFS reparse + /// points, like mounted volumes, and directory junctions. An example + /// of a junction is the "My Music" directory in Windows Vista. In some + /// cases you may not want DotNetZip to traverse those directories. In + /// that case, set this property to false. + /// + /// + /// + /// + /// using (var zip = new ZipFile()) + /// { + /// zip.AddDirectoryWillTraverseReparsePoints = false; + /// zip.AddDirectory(dirToZip,"fodder"); + /// zip.Save(zipFileToCreate); + /// } + /// + /// + public bool AddDirectoryWillTraverseReparsePoints { get; set; } + + + /// + /// Size of the IO buffer used while saving. + /// + /// + /// + /// + /// + /// First, let me say that you really don't need to bother with this. It is + /// here to allow for optimizations that you probably won't make! It will work + /// fine if you don't set or get this property at all. Ok? + /// + /// + /// + /// Now that we have that out of the way, the fine print: This + /// property affects the size of the buffer that is used for I/O for each + /// entry contained in the zip file. When a file is read in to be compressed, + /// it uses a buffer given by the size here. When you update a zip file, the + /// data for unmodified entries is copied from the first zip file to the + /// other, through a buffer given by the size here. + /// + /// + /// + /// Changing the buffer size affects a few things: first, for larger buffer + /// sizes, the memory used by the ZipFile, obviously, will be larger + /// during I/O operations. This may make operations faster for very much + /// larger files. Last, for any given entry, when you use a larger buffer + /// there will be fewer progress events during I/O operations, because there's + /// one progress event generated for each time the buffer is filled and then + /// emptied. + /// + /// + /// + /// The default buffer size is 8k. Increasing the buffer size may speed + /// things up as you compress larger files. But there are no hard-and-fast + /// rules here, eh? You won't know til you test it. And there will be a + /// limit where ever larger buffers actually slow things down. So as I said + /// in the beginning, it's probably best if you don't set or get this property + /// at all. + /// + /// + /// + /// + /// + /// This example shows how you might set a large buffer size for efficiency when + /// dealing with zip entries that are larger than 1gb. + /// + /// using (ZipFile zip = new ZipFile()) + /// { + /// zip.SaveProgress += this.zip1_SaveProgress; + /// zip.AddDirectory(directoryToZip, ""); + /// zip.UseZip64WhenSaving = Zip64Option.Always; + /// zip.BufferSize = 65536*8; // 65536 * 8 = 512k + /// zip.Save(ZipFileToCreate); + /// } + /// + /// + + public int BufferSize + { + get { return _BufferSize; } + set { _BufferSize = value; } + } + + /// + /// Size of the work buffer to use for the ZLIB codec during compression. + /// + /// + /// + /// + /// When doing ZLIB or Deflate compression, the library fills a buffer, + /// then passes it to the compressor for compression. Then the library + /// reads out the compressed bytes. This happens repeatedly until there + /// is no more uncompressed data to compress. This property sets the + /// size of the buffer that will be used for chunk-wise compression. In + /// order for the setting to take effect, your application needs to set + /// this property before calling one of the ZipFile.Save() + /// overloads. + /// + /// + /// Setting this affects the performance and memory efficiency of + /// compression and decompression. For larger files, setting this to a + /// larger size may improve compression performance, but the exact + /// numbers vary depending on available memory, the size of the streams + /// you are compressing, and a bunch of other variables. I don't have + /// good firm recommendations on how to set it. You'll have to test it + /// yourself. Or just leave it alone and accept the default. + /// + /// + public int CodecBufferSize + { + get; + set; + } + + /// + /// Indicates whether extracted files should keep their paths as + /// stored in the zip archive. + /// + /// + /// + /// + /// This property affects Extraction. It is not used when creating zip + /// archives. + /// + /// + /// + /// With this property set to false, the default, extracting entries + /// from a zip file will create files in the filesystem that have the full + /// path associated to the entry within the zip file. With this property set + /// to true, extracting entries from the zip file results in files + /// with no path: the folders are "flattened." + /// + /// + /// + /// An example: suppose the zip file contains entries /directory1/file1.txt and + /// /directory2/file2.txt. With FlattenFoldersOnExtract set to false, + /// the files created will be \directory1\file1.txt and \directory2\file2.txt. + /// With the property set to true, the files created are file1.txt and file2.txt. + /// + /// + /// + public bool FlattenFoldersOnExtract + { + get; + set; + } + + + /// + /// The compression strategy to use for all entries. + /// + /// + /// + /// Set the Strategy used by the ZLIB-compatible compressor, when + /// compressing entries using the DEFLATE method. Different compression + /// strategies work better on different sorts of data. The strategy + /// parameter can affect the compression ratio and the speed of + /// compression but not the correctness of the compresssion. For more + /// information see Ionic.Zlib.CompressionStrategy. + /// + public Ionic.Zlib.CompressionStrategy Strategy + { + get { return _Strategy; } + set { _Strategy = value; } + } + + + /// + /// The name of the ZipFile, on disk. + /// + /// + /// + /// + /// + /// When the ZipFile instance was created by reading an archive using + /// one of the ZipFile.Read methods, this property represents the name + /// of the zip file that was read. When the ZipFile instance was + /// created by using the no-argument constructor, this value is null + /// (Nothing in VB). + /// + /// + /// + /// If you use the no-argument constructor, and you then explicitly set this + /// property, when you call , this name will + /// specify the name of the zip file created. Doing so is equivalent to + /// calling . When instantiating a + /// ZipFile by reading from a stream or byte array, the Name + /// property remains null. When saving to a stream, the Name + /// property is implicitly set to null. + /// + /// + public string Name + { + get { return _name; } + set { _name = value; } + } + + + /// + /// Sets the compression level to be used for entries subsequently added to + /// the zip archive. + /// + /// + /// + /// + /// Varying the compression level used on entries can affect the + /// size-vs-speed tradeoff when compression and decompressing data streams + /// or files. + /// + /// + /// + /// As with some other properties on the ZipFile class, like , , and , setting this property on a ZipFile + /// instance will cause the specified CompressionLevel to be used on all + /// items that are subsequently added to the + /// ZipFile instance. If you set this property after you have added + /// items to the ZipFile, but before you have called Save(), + /// those items will not use the specified compression level. + /// + /// + /// + /// If you do not set this property, the default compression level is used, + /// which normally gives a good balance of compression efficiency and + /// compression speed. In some tests, using BestCompression can + /// double the time it takes to compress, while delivering just a small + /// increase in compression efficiency. This behavior will vary with the + /// type of data you compress. If you are in doubt, just leave this setting + /// alone, and accept the default. + /// + /// + public Ionic.Zlib.CompressionLevel CompressionLevel + { + get; + set; + } + + /// + /// The compression method for the zipfile. + /// + /// + /// + /// By default, the compression method is CompressionMethod.Deflate. + /// + /// + /// + public Ionic.Zip.CompressionMethod CompressionMethod + { + get + { + return _compressionMethod; + } + set + { + _compressionMethod = value; + } + } + + + + /// + /// A comment attached to the zip archive. + /// + /// + /// + /// + /// + /// This property is read/write. It allows the application to specify a + /// comment for the ZipFile, or read the comment for the + /// ZipFile. After setting this property, changes are only made + /// permanent when you call a Save() method. + /// + /// + /// + /// According to PKWARE's + /// zip specification, the comment is not encrypted, even if there is a + /// password set on the zip file. + /// + /// + /// + /// The specification does not describe how to indicate the encoding used + /// on a comment string. Many "compliant" zip tools and libraries use + /// IBM437 as the code page for comments; DotNetZip, too, follows that + /// practice. On the other hand, there are situations where you want a + /// Comment to be encoded with something else, for example using code page + /// 950 "Big-5 Chinese". To fill that need, DotNetZip will encode the + /// comment following the same procedure it follows for encoding + /// filenames: (a) if is + /// Never, it uses the default encoding (IBM437). (b) if is Always, it always uses the + /// alternate encoding (). (c) if is AsNecessary, it uses the + /// alternate encoding only if the default encoding is not sufficient for + /// encoding the comment - in other words if decoding the result does not + /// produce the original string. This decision is taken at the time of + /// the call to ZipFile.Save(). + /// + /// + /// + /// When creating a zip archive using this library, it is possible to change + /// the value of between each + /// entry you add, and between adding entries and the call to + /// Save(). Don't do this. It will likely result in a zip file that is + /// not readable by any tool or application. For best interoperability, leave + /// alone, or specify it only + /// once, before adding any entries to the ZipFile instance. + /// + /// + /// + public string Comment + { + get { return _Comment; } + set + { + _Comment = value; + _contentsChanged = true; + } + } + + + + + /// + /// Specifies whether the Creation, Access, and Modified times for entries + /// added to the zip file will be emitted in “Windows format” + /// when the zip archive is saved. + /// + /// + /// + /// + /// An application creating a zip archive can use this flag to explicitly + /// specify that the file times for the entries should or should not be stored + /// in the zip archive in the format used by Windows. By default this flag is + /// true, meaning the Windows-format times are stored in the zip + /// archive. + /// + /// + /// + /// When adding an entry from a file or directory, the Creation (), Access (), and Modified () times for the given entry are + /// automatically set from the filesystem values. When adding an entry from a + /// stream or string, all three values are implicitly set to + /// DateTime.Now. Applications can also explicitly set those times by + /// calling . + /// + /// + /// + /// PKWARE's + /// zip specification describes multiple ways to format these times in a + /// zip file. One is the format Windows applications normally use: 100ns ticks + /// since January 1, 1601 UTC. The other is a format Unix applications typically + /// use: seconds since January 1, 1970 UTC. Each format can be stored in an + /// "extra field" in the zip entry when saving the zip archive. The former + /// uses an extra field with a Header Id of 0x000A, while the latter uses a + /// header ID of 0x5455, although you probably don't need to know that. + /// + /// + /// + /// Not all tools and libraries can interpret these fields. Windows + /// compressed folders is one that can read the Windows Format timestamps, + /// while I believe the Infozip + /// tools can read the Unix format timestamps. Some tools and libraries + /// may be able to read only one or the other. DotNetZip can read or write + /// times in either or both formats. + /// + /// + /// + /// The times stored are taken from , , and . + /// + /// + /// + /// The value set here applies to all entries subsequently added to the + /// ZipFile. + /// + /// + /// + /// This property is not mutually exclusive of the property. It is possible and + /// legal and valid to produce a zip file that contains timestamps encoded in + /// the Unix format as well as in the Windows format, in addition to the LastModified time attached to each + /// entry in the archive, a time that is always stored in "DOS format". And, + /// notwithstanding the names PKWare uses for these time formats, any of them + /// can be read and written by any computer, on any operating system. But, + /// there are no guarantees that a program running on Mac or Linux will + /// gracefully handle a zip file with "Windows" formatted times, or that an + /// application that does not use DotNetZip but runs on Windows will be able to + /// handle file times in Unix format. + /// + /// + /// + /// When in doubt, test. Sorry, I haven't got a complete list of tools and + /// which sort of timestamps they can use and will tolerate. If you get any + /// good information and would like to pass it on, please do so and I will + /// include that information in this documentation. + /// + /// + /// + /// + /// This example shows how to save a zip file that contains file timestamps + /// in a format normally used by Unix. + /// + /// using (var zip = new ZipFile()) + /// { + /// // produce a zip file the Mac will like + /// zip.EmitTimesInWindowsFormatWhenSaving = false; + /// zip.EmitTimesInUnixFormatWhenSaving = true; + /// zip.AddDirectory(directoryToZip, "files"); + /// zip.Save(outputFile); + /// } + /// + /// + /// + /// Using zip As New ZipFile + /// '' produce a zip file the Mac will like + /// zip.EmitTimesInWindowsFormatWhenSaving = False + /// zip.EmitTimesInUnixFormatWhenSaving = True + /// zip.AddDirectory(directoryToZip, "files") + /// zip.Save(outputFile) + /// End Using + /// + /// + /// + /// + /// + public bool EmitTimesInWindowsFormatWhenSaving + { + get + { + return _emitNtfsTimes; + } + set + { + _emitNtfsTimes = value; + } + } + + + /// + /// Specifies whether the Creation, Access, and Modified times + /// for entries added to the zip file will be emitted in "Unix(tm) + /// format" when the zip archive is saved. + /// + /// + /// + /// + /// An application creating a zip archive can use this flag to explicitly + /// specify that the file times for the entries should or should not be stored + /// in the zip archive in the format used by Unix. By default this flag is + /// false, meaning the Unix-format times are not stored in the zip + /// archive. + /// + /// + /// + /// When adding an entry from a file or directory, the Creation (), Access (), and Modified () times for the given entry are + /// automatically set from the filesystem values. When adding an entry from a + /// stream or string, all three values are implicitly set to DateTime.Now. + /// Applications can also explicitly set those times by calling . + /// + /// + /// + /// PKWARE's + /// zip specification describes multiple ways to format these times in a + /// zip file. One is the format Windows applications normally use: 100ns ticks + /// since January 1, 1601 UTC. The other is a format Unix applications + /// typically use: seconds since January 1, 1970 UTC. Each format can be + /// stored in an "extra field" in the zip entry when saving the zip + /// archive. The former uses an extra field with a Header Id of 0x000A, while + /// the latter uses a header ID of 0x5455, although you probably don't need to + /// know that. + /// + /// + /// + /// Not all tools and libraries can interpret these fields. Windows + /// compressed folders is one that can read the Windows Format timestamps, + /// while I believe the Infozip + /// tools can read the Unix format timestamps. Some tools and libraries may be + /// able to read only one or the other. DotNetZip can read or write times in + /// either or both formats. + /// + /// + /// + /// The times stored are taken from , , and . + /// + /// + /// + /// This property is not mutually exclusive of the property. It is possible and + /// legal and valid to produce a zip file that contains timestamps encoded in + /// the Unix format as well as in the Windows format, in addition to the LastModified time attached to each + /// entry in the zip archive, a time that is always stored in "DOS + /// format". And, notwithstanding the names PKWare uses for these time + /// formats, any of them can be read and written by any computer, on any + /// operating system. But, there are no guarantees that a program running on + /// Mac or Linux will gracefully handle a zip file with "Windows" formatted + /// times, or that an application that does not use DotNetZip but runs on + /// Windows will be able to handle file times in Unix format. + /// + /// + /// + /// When in doubt, test. Sorry, I haven't got a complete list of tools and + /// which sort of timestamps they can use and will tolerate. If you get any + /// good information and would like to pass it on, please do so and I will + /// include that information in this documentation. + /// + /// + /// + /// + /// + public bool EmitTimesInUnixFormatWhenSaving + { + get + { + return _emitUnixTimes; + } + set + { + _emitUnixTimes = value; + } + } + + + + /// + /// Indicates whether verbose output is sent to the during AddXxx() and + /// ReadXxx() operations. + /// + /// + /// + /// This is a synthetic property. It returns true if the is non-null. + /// + internal bool Verbose + { + get { return (_StatusMessageTextWriter != null); } + } + + + /// + /// Returns true if an entry by the given name exists in the ZipFile. + /// + /// + /// the name of the entry to find + /// true if an entry with the given name exists; otherwise false. + /// + public bool ContainsEntry(string name) + { + // workitem 12534 + return _entries.ContainsKey(SharedUtilities.NormalizePathForUseInZipFile(name)); + } + + + + /// + /// Indicates whether to perform case-sensitive matching on the filename when + /// retrieving entries in the zipfile via the string-based indexer. + /// + /// + /// + /// The default value is false, which means don't do case-sensitive + /// matching. In other words, retrieving zip["ReadMe.Txt"] is the same as + /// zip["readme.txt"]. It really makes sense to set this to true only + /// if you are not running on Windows, which has case-insensitive + /// filenames. But since this library is not built for non-Windows platforms, + /// in most cases you should just leave this property alone. + /// + public bool CaseSensitiveRetrieval + { + get + { + return _CaseSensitiveRetrieval; + } + + set + { + // workitem 9868 + if (value != _CaseSensitiveRetrieval) + { + _CaseSensitiveRetrieval = value; + _initEntriesDictionary(); + } + } + } + + + /// + /// Indicates whether to encode entry filenames and entry comments using Unicode + /// (UTF-8). + /// + /// + /// + /// + /// The + /// PKWare zip specification provides for encoding file names and file + /// comments in either the IBM437 code page, or in UTF-8. This flag selects + /// the encoding according to that specification. By default, this flag is + /// false, and filenames and comments are encoded into the zip file in the + /// IBM437 codepage. Setting this flag to true will specify that filenames + /// and comments that cannot be encoded with IBM437 will be encoded with + /// UTF-8. + /// + /// + /// + /// Zip files created with strict adherence to the PKWare specification with + /// respect to UTF-8 encoding can contain entries with filenames containing + /// any combination of Unicode characters, including the full range of + /// characters from Chinese, Latin, Hebrew, Greek, Cyrillic, and many other + /// alphabets. However, because at this time, the UTF-8 portion of the PKWare + /// specification is not broadly supported by other zip libraries and + /// utilities, such zip files may not be readable by your favorite zip tool or + /// archiver. In other words, interoperability will decrease if you set this + /// flag to true. + /// + /// + /// + /// In particular, Zip files created with strict adherence to the PKWare + /// specification with respect to UTF-8 encoding will not work well with + /// Explorer in Windows XP or Windows Vista, because Windows compressed + /// folders, as far as I know, do not support UTF-8 in zip files. Vista can + /// read the zip files, but shows the filenames incorrectly. Unpacking from + /// Windows Vista Explorer will result in filenames that have rubbish + /// characters in place of the high-order UTF-8 bytes. + /// + /// + /// + /// Also, zip files that use UTF-8 encoding will not work well with Java + /// applications that use the java.util.zip classes, as of v5.0 of the Java + /// runtime. The Java runtime does not correctly implement the PKWare + /// specification in this regard. + /// + /// + /// + /// As a result, we have the unfortunate situation that "correct" behavior by + /// the DotNetZip library with regard to Unicode encoding of filenames during + /// zip creation will result in zip files that are readable by strictly + /// compliant and current tools (for example the most recent release of the + /// commercial WinZip tool); but these zip files will not be readable by + /// various other tools or libraries, including Windows Explorer. + /// + /// + /// + /// The DotNetZip library can read and write zip files with UTF8-encoded + /// entries, according to the PKware spec. If you use DotNetZip for both + /// creating and reading the zip file, and you use UTF-8, there will be no + /// loss of information in the filenames. For example, using a self-extractor + /// created by this library will allow you to unpack files correctly with no + /// loss of information in the filenames. + /// + /// + /// + /// If you do not set this flag, it will remain false. If this flag is false, + /// your ZipFile will encode all filenames and comments using the + /// IBM437 codepage. This can cause "loss of information" on some filenames, + /// but the resulting zipfile will be more interoperable with other + /// utilities. As an example of the loss of information, diacritics can be + /// lost. The o-tilde character will be down-coded to plain o. The c with a + /// cedilla (Unicode 0xE7) used in Portugese will be downcoded to a c. + /// Likewise, the O-stroke character (Unicode 248), used in Danish and + /// Norwegian, will be down-coded to plain o. Chinese characters cannot be + /// represented in codepage IBM437; when using the default encoding, Chinese + /// characters in filenames will be represented as ?. These are all examples + /// of "information loss". + /// + /// + /// + /// The loss of information associated to the use of the IBM437 encoding is + /// inconvenient, and can also lead to runtime errors. For example, using + /// IBM437, any sequence of 4 Chinese characters will be encoded as ????. If + /// your application creates a ZipFile, then adds two files, each with + /// names of four Chinese characters each, this will result in a duplicate + /// filename exception. In the case where you add a single file with a name + /// containing four Chinese characters, calling Extract() on the entry that + /// has question marks in the filename will result in an exception, because + /// the question mark is not legal for use within filenames on Windows. These + /// are just a few examples of the problems associated to loss of information. + /// + /// + /// + /// This flag is independent of the encoding of the content within the entries + /// in the zip file. Think of the zip file as a container - it supports an + /// encoding. Within the container are other "containers" - the file entries + /// themselves. The encoding within those entries is independent of the + /// encoding of the zip archive container for those entries. + /// + /// + /// + /// Rather than specify the encoding in a binary fashion using this flag, an + /// application can specify an arbitrary encoding via the property. Setting the encoding + /// explicitly when creating zip archives will result in non-compliant zip + /// files that, curiously, are fairly interoperable. The challenge is, the + /// PKWare specification does not provide for a way to specify that an entry + /// in a zip archive uses a code page that is neither IBM437 nor UTF-8. + /// Therefore if you set the encoding explicitly when creating a zip archive, + /// you must take care upon reading the zip archive to use the same code page. + /// If you get it wrong, the behavior is undefined and may result in incorrect + /// filenames, exceptions, stomach upset, hair loss, and acne. + /// + /// + /// + [Obsolete("Beginning with v1.9.1.6 of DotNetZip, this property is obsolete. It will be removed in a future version of the library. Your applications should use AlternateEncoding and AlternateEncodingUsage instead.")] + public bool UseUnicodeAsNecessary + { + get + { + return (_alternateEncoding == System.Text.Encoding.GetEncoding("UTF-8")) && + (_alternateEncodingUsage == ZipOption.AsNecessary); + } + set + { + if (value) + { + _alternateEncoding = System.Text.Encoding.GetEncoding("UTF-8"); + _alternateEncodingUsage = ZipOption.AsNecessary; + + } + else + { + _alternateEncoding = Ionic.Zip.ZipFile.DefaultEncoding; + _alternateEncodingUsage = ZipOption.Never; + } + } + } + + + /// + /// Specify whether to use ZIP64 extensions when saving a zip archive. + /// + /// + /// + /// + /// + /// When creating a zip file, the default value for the property is . is + /// safest, in the sense that you will not get an Exception if a pre-ZIP64 + /// limit is exceeded. + /// + /// + /// + /// You may set the property at any time before calling Save(). + /// + /// + /// + /// When reading a zip file via the Zipfile.Read() method, DotNetZip + /// will properly read ZIP64-endowed zip archives, regardless of the value of + /// this property. DotNetZip will always read ZIP64 archives. This property + /// governs only whether DotNetZip will write them. Therefore, when updating + /// archives, be careful about setting this property after reading an archive + /// that may use ZIP64 extensions. + /// + /// + /// + /// An interesting question is, if you have set this property to + /// AsNecessary, and then successfully saved, does the resulting + /// archive use ZIP64 extensions or not? To learn this, check the property, after calling Save(). + /// + /// + /// + /// Have you thought about + /// donating? + /// + /// + /// + /// + public Zip64Option UseZip64WhenSaving + { + get + { + return _zip64; + } + set + { + _zip64 = value; + } + } + + + + /// + /// Indicates whether the archive requires ZIP64 extensions. + /// + /// + /// + /// + /// + /// This property is null (or Nothing in VB) if the archive has + /// not been saved, and there are fewer than 65334 ZipEntry items + /// contained in the archive. + /// + /// + /// + /// The Value is true if any of the following four conditions holds: + /// the uncompressed size of any entry is larger than 0xFFFFFFFF; the + /// compressed size of any entry is larger than 0xFFFFFFFF; the relative + /// offset of any entry within the zip archive is larger than 0xFFFFFFFF; or + /// there are more than 65534 entries in the archive. (0xFFFFFFFF = + /// 4,294,967,295). The result may not be known until a Save() is attempted + /// on the zip archive. The Value of this + /// property may be set only AFTER one of the Save() methods has been called. + /// + /// + /// + /// If none of the four conditions holds, and the archive has been saved, then + /// the Value is false. + /// + /// + /// + /// A Value of false does not indicate that the zip archive, as saved, + /// does not use ZIP64. It merely indicates that ZIP64 is not required. An + /// archive may use ZIP64 even when not required if the property is set to , or if the property is set to and the output stream was not + /// seekable. Use the property to determine if + /// the most recent Save() method resulted in an archive that utilized + /// the ZIP64 extensions. + /// + /// + /// + /// + /// + public Nullable RequiresZip64 + { + get + { + if (_entries.Count > 65534) + return new Nullable(true); + + // If the ZipFile has not been saved or if the contents have changed, then + // it is not known if ZIP64 is required. + if (!_hasBeenSaved || _contentsChanged) return null; + + // Whether ZIP64 is required is knowable. + foreach (ZipEntry e in _entries.Values) + { + if (e.RequiresZip64.Value) return new Nullable(true); + } + + return new Nullable(false); + } + } + + + /// + /// Indicates whether the most recent Save() operation used ZIP64 extensions. + /// + /// + /// + /// + /// The use of ZIP64 extensions within an archive is not always necessary, and + /// for interoperability concerns, it may be desired to NOT use ZIP64 if + /// possible. The property can be + /// set to use ZIP64 extensions only when necessary. In those cases, + /// Sometimes applications want to know whether a Save() actually used ZIP64 + /// extensions. Applications can query this read-only property to learn + /// whether ZIP64 has been used in a just-saved ZipFile. + /// + /// + /// + /// The value is null (or Nothing in VB) if the archive has not + /// been saved. + /// + /// + /// + /// Non-null values (HasValue is true) indicate whether ZIP64 + /// extensions were used during the most recent Save() operation. The + /// ZIP64 extensions may have been used as required by any particular entry + /// because of its uncompressed or compressed size, or because the archive is + /// larger than 4294967295 bytes, or because there are more than 65534 entries + /// in the archive, or because the UseZip64WhenSaving property was set + /// to , or because the + /// UseZip64WhenSaving property was set to and the output stream was not seekable. + /// The value of this property does not indicate the reason the ZIP64 + /// extensions were used. + /// + /// + /// + /// + /// + public Nullable OutputUsedZip64 + { + get + { + return _OutputUsesZip64; + } + } + + + /// + /// Indicates whether the most recent Read() operation read a zip file that uses + /// ZIP64 extensions. + /// + /// + /// + /// This property will return null (Nothing in VB) if you've added an entry after reading + /// the zip file. + /// + public Nullable InputUsesZip64 + { + get + { + if (_entries.Count > 65534) + return true; + + foreach (ZipEntry e in this) + { + // if any entry was added after reading the zip file, then the result is null + if (e.Source != ZipEntrySource.ZipFile) return null; + + // if any entry read from the zip used zip64, then the result is true + if (e._InputUsesZip64) return true; + } + return false; + } + } + + + /// + /// The text encoding to use when writing new entries to the ZipFile, + /// for those entries that cannot be encoded with the default (IBM437) + /// encoding; or, the text encoding that was used when reading the entries + /// from the ZipFile. + /// + /// + /// + /// + /// In its + /// zip specification, PKWare describes two options for encoding + /// filenames and comments: using IBM437 or UTF-8. But, some archiving tools + /// or libraries do not follow the specification, and instead encode + /// characters using the system default code page. For example, WinRAR when + /// run on a machine in Shanghai may encode filenames with the Big-5 Chinese + /// (950) code page. This behavior is contrary to the Zip specification, but + /// it occurs anyway. + /// + /// + /// + /// When using DotNetZip to write zip archives that will be read by one of + /// these other archivers, set this property to specify the code page to use + /// when encoding the and for each ZipEntry in the zip file, for + /// values that cannot be encoded with the default codepage for zip files, + /// IBM437. This is why this property is "provisional". In all cases, IBM437 + /// is used where possible, in other words, where no loss of data would + /// result. It is possible, therefore, to have a given entry with a + /// Comment encoded in IBM437 and a FileName encoded with the + /// specified "provisional" codepage. + /// + /// + /// + /// Be aware that a zip file created after you've explicitly set the property to a value other than + /// IBM437 may not be compliant to the PKWare specification, and may not be + /// readable by compliant archivers. On the other hand, many (most?) + /// archivers are non-compliant and can read zip files created in arbitrary + /// code pages. The trick is to use or specify the proper codepage when + /// reading the zip. + /// + /// + /// + /// When creating a zip archive using this library, it is possible to change + /// the value of between each + /// entry you add, and between adding entries and the call to + /// Save(). Don't do this. It will likely result in a zipfile that is + /// not readable. For best interoperability, either leave alone, or specify it only once, + /// before adding any entries to the ZipFile instance. There is one + /// exception to this recommendation, described later. + /// + /// + /// + /// When using an arbitrary, non-UTF8 code page for encoding, there is no + /// standard way for the creator application - whether DotNetZip, WinZip, + /// WinRar, or something else - to formally specify in the zip file which + /// codepage has been used for the entries. As a result, readers of zip files + /// are not able to inspect the zip file and determine the codepage that was + /// used for the entries contained within it. It is left to the application + /// or user to determine the necessary codepage when reading zip files encoded + /// this way. In other words, if you explicitly specify the codepage when you + /// create the zipfile, you must explicitly specify the same codepage when + /// reading the zipfile. + /// + /// + /// + /// The way you specify the code page to use when reading a zip file varies + /// depending on the tool or library you use to read the zip. In DotNetZip, + /// you use a ZipFile.Read() method that accepts an encoding parameter. It + /// isn't possible with Windows Explorer, as far as I know, to specify an + /// explicit codepage to use when reading a zip. If you use an incorrect + /// codepage when reading a zipfile, you will get entries with filenames that + /// are incorrect, and the incorrect filenames may even contain characters + /// that are not legal for use within filenames in Windows. Extracting entries + /// with illegal characters in the filenames will lead to exceptions. It's too + /// bad, but this is just the way things are with code pages in zip + /// files. Caveat Emptor. + /// + /// + /// + /// Example: Suppose you create a zipfile that contains entries with + /// filenames that have Danish characters. If you use equal to "iso-8859-1" (cp 28591), + /// the filenames will be correctly encoded in the zip. But, to read that + /// zipfile correctly, you have to specify the same codepage at the time you + /// read it. If try to read that zip file with Windows Explorer or another + /// application that is not flexible with respect to the codepage used to + /// decode filenames in zipfiles, you will get a filename like "Inf.txt". + /// + /// + /// + /// When using DotNetZip to read a zip archive, and the zip archive uses an + /// arbitrary code page, you must specify the encoding to use before or when + /// the Zipfile is READ. This means you must use a ZipFile.Read() + /// method that allows you to specify a System.Text.Encoding parameter. Setting + /// the ProvisionalAlternateEncoding property after your application has read in + /// the zip archive will not affect the entry names of entries that have already + /// been read in. + /// + /// + /// + /// And now, the exception to the rule described above. One strategy for + /// specifying the code page for a given zip file is to describe the code page + /// in a human-readable form in the Zip comment. For example, the comment may + /// read "Entries in this archive are encoded in the Big5 code page". For + /// maximum interoperability, the zip comment in this case should be encoded + /// in the default, IBM437 code page. In this case, the zip comment is + /// encoded using a different page than the filenames. To do this, Specify + /// ProvisionalAlternateEncoding to your desired region-specific code + /// page, once before adding any entries, and then reset + /// ProvisionalAlternateEncoding to IBM437 before setting the property and calling Save(). + /// + /// + /// + /// + /// This example shows how to read a zip file using the Big-5 Chinese code page + /// (950), and extract each entry in the zip file. For this code to work as + /// desired, the Zipfile must have been created using the big5 code page + /// (CP950). This is typical, for example, when using WinRar on a machine with + /// CP950 set as the default code page. In that case, the names of entries + /// within the Zip archive will be stored in that code page, and reading the zip + /// archive must be done using that code page. If the application did not use + /// the correct code page in ZipFile.Read(), then names of entries within the + /// zip archive would not be correctly retrieved. + /// + /// using (var zip = ZipFile.Read(zipFileName, System.Text.Encoding.GetEncoding("big5"))) + /// { + /// // retrieve and extract an entry using a name encoded with CP950 + /// zip[MyDesiredEntry].Extract("unpack"); + /// } + /// + /// + /// + /// Using zip As ZipFile = ZipFile.Read(ZipToExtract, System.Text.Encoding.GetEncoding("big5")) + /// ' retrieve and extract an entry using a name encoded with CP950 + /// zip(MyDesiredEntry).Extract("unpack") + /// End Using + /// + /// + /// + /// DefaultEncoding + [Obsolete("use AlternateEncoding instead.")] + public System.Text.Encoding ProvisionalAlternateEncoding + { + get + { + if (_alternateEncodingUsage == ZipOption.AsNecessary) + return _alternateEncoding; + return null; + } + set + { + _alternateEncoding = value; + _alternateEncodingUsage = ZipOption.AsNecessary; + } + } + + + /// + /// A Text Encoding to use when encoding the filenames and comments for + /// all the ZipEntry items, during a ZipFile.Save() operation. + /// + /// + /// + /// Whether the encoding specified here is used during the save depends + /// on . + /// + /// + public System.Text.Encoding AlternateEncoding + { + get + { + return _alternateEncoding; + } + set + { + _alternateEncoding = value; + } + } + + + /// + /// A flag that tells if and when this instance should apply + /// AlternateEncoding to encode the filenames and comments associated to + /// of ZipEntry objects contained within this instance. + /// + public ZipOption AlternateEncodingUsage + { + get + { + return _alternateEncodingUsage; + } + set + { + _alternateEncodingUsage = value; + } + } + + + /// + /// The default text encoding used in zip archives. It is numeric 437, also + /// known as IBM437. + /// + /// + public static System.Text.Encoding DefaultEncoding + { + get + { + return _defaultEncoding; + } + } + + + /// + /// Gets or sets the TextWriter to which status messages are delivered + /// for the instance. + /// + /// + /// + /// If the TextWriter is set to a non-null value, then verbose output is sent + /// to the TextWriter during Add, Read, Save and + /// Extract operations. Typically, console applications might use + /// Console.Out and graphical or headless applications might use a + /// System.IO.StringWriter. The output of this is suitable for viewing + /// by humans. + /// + /// + /// + /// + /// In this example, a console application instantiates a ZipFile, then + /// sets the StatusMessageTextWriter to Console.Out. At that + /// point, all verbose status messages for that ZipFile are sent to the + /// console. + /// + /// + /// + /// using (ZipFile zip= ZipFile.Read(FilePath)) + /// { + /// zip.StatusMessageTextWriter= System.Console.Out; + /// // messages are sent to the console during extraction + /// zip.ExtractAll(); + /// } + /// + /// + /// + /// Using zip As ZipFile = ZipFile.Read(FilePath) + /// zip.StatusMessageTextWriter= System.Console.Out + /// 'Status Messages will be sent to the console during extraction + /// zip.ExtractAll() + /// End Using + /// + /// + /// + /// In this example, a Windows Forms application instantiates a + /// ZipFile, then sets the StatusMessageTextWriter to a + /// StringWriter. At that point, all verbose status messages for that + /// ZipFile are sent to the StringWriter. + /// + /// + /// + /// var sw = new System.IO.StringWriter(); + /// using (ZipFile zip= ZipFile.Read(FilePath)) + /// { + /// zip.StatusMessageTextWriter= sw; + /// zip.ExtractAll(); + /// } + /// Console.WriteLine("{0}", sw.ToString()); + /// + /// + /// + /// Dim sw as New System.IO.StringWriter + /// Using zip As ZipFile = ZipFile.Read(FilePath) + /// zip.StatusMessageTextWriter= sw + /// zip.ExtractAll() + /// End Using + /// 'Status Messages are now available in sw + /// + /// + /// + public TextWriter StatusMessageTextWriter + { + get { return _StatusMessageTextWriter; } + set { _StatusMessageTextWriter = value; } + } + + + + + /// + /// Gets or sets the name for the folder to store the temporary file + /// this library writes when saving a zip archive. + /// + /// + /// + /// + /// This library will create a temporary file when saving a Zip archive to a + /// file. This file is written when calling one of the Save() methods + /// that does not save to a stream, or one of the SaveSelfExtractor() + /// methods. + /// + /// + /// + /// By default, the library will create the temporary file in the directory + /// specified for the file itself, via the property or via + /// the method. + /// + /// + /// + /// Setting this property allows applications to override this default + /// behavior, so that the library will create the temporary file in the + /// specified folder. For example, to have the library create the temporary + /// file in the current working directory, regardless where the ZipFile + /// is saved, specfy ".". To revert to the default behavior, set this + /// property to null (Nothing in VB). + /// + /// + /// + /// When setting the property to a non-null value, the folder specified must + /// exist; if it does not an exception is thrown. The application should have + /// write and delete permissions on the folder. The permissions are not + /// explicitly checked ahead of time; if the application does not have the + /// appropriate rights, an exception will be thrown at the time Save() + /// is called. + /// + /// + /// + /// There is no temporary file created when reading a zip archive. When + /// saving to a Stream, there is no temporary file created. For example, if + /// the application is an ASP.NET application and calls Save() + /// specifying the Response.OutputStream as the output stream, there is + /// no temporary file created. + /// + /// + /// + /// + /// Thrown when setting the property if the directory does not exist. + /// + /// + public String TempFileFolder + { + get { return _TempFileFolder; } + + set + { + _TempFileFolder = value; + if (value == null) return; + + if (!Directory.Exists(value)) + throw new FileNotFoundException(String.Format("That directory ({0}) does not exist.", value)); + + } + } + + /// + /// Sets the password to be used on the ZipFile instance. + /// + /// + /// + /// + /// + /// When writing a zip archive, this password is applied to the entries, not + /// to the zip archive itself. It applies to any ZipEntry subsequently + /// added to the ZipFile, using one of the AddFile, + /// AddDirectory, AddEntry, or AddItem methods, etc. + /// When reading a zip archive, this property applies to any entry + /// subsequently extracted from the ZipFile using one of the Extract + /// methods on the ZipFile class. + /// + /// + /// + /// When writing a zip archive, keep this in mind: though the password is set + /// on the ZipFile object, according to the Zip spec, the "directory" of the + /// archive - in other words the list of entries or files contained in the archive - is + /// not encrypted with the password, or protected in any way. If you set the + /// Password property, the password actually applies to individual entries + /// that are added to the archive, subsequent to the setting of this property. + /// The list of filenames in the archive that is eventually created will + /// appear in clear text, but the contents of the individual files are + /// encrypted. This is how Zip encryption works. + /// + /// + /// + /// One simple way around this limitation is to simply double-wrap sensitive + /// filenames: Store the files in a zip file, and then store that zip file + /// within a second, "outer" zip file. If you apply a password to the outer + /// zip file, then readers will be able to see that the outer zip file + /// contains an inner zip file. But readers will not be able to read the + /// directory or file list of the inner zip file. + /// + /// + /// + /// If you set the password on the ZipFile, and then add a set of files + /// to the archive, then each entry is encrypted with that password. You may + /// also want to change the password between adding different entries. If you + /// set the password, add an entry, then set the password to null + /// (Nothing in VB), and add another entry, the first entry is + /// encrypted and the second is not. If you call AddFile(), then set + /// the Password property, then call ZipFile.Save, the file + /// added will not be password-protected, and no warning will be generated. + /// + /// + /// + /// When setting the Password, you may also want to explicitly set the property, to specify how to encrypt the entries added + /// to the ZipFile. If you set the Password to a non-null value and do not + /// set , then PKZip 2.0 ("Weak") encryption is used. + /// This encryption is relatively weak but is very interoperable. If you set + /// the password to a null value (Nothing in VB), Encryption is + /// reset to None. + /// + /// + /// + /// All of the preceding applies to writing zip archives, in other words when + /// you use one of the Save methods. To use this property when reading or an + /// existing ZipFile, do the following: set the Password property on the + /// ZipFile, then call one of the Extract() overloads on the . In this case, the entry is extracted using the + /// Password that is specified on the ZipFile instance. If you + /// have not set the Password property, then the password is + /// null, and the entry is extracted with no password. + /// + /// + /// + /// If you set the Password property on the ZipFile, then call + /// Extract() an entry that has not been encrypted with a password, the + /// password is not used for that entry, and the ZipEntry is extracted + /// as normal. In other words, the password is used only if necessary. + /// + /// + /// + /// The class also has a Password property. It takes precedence + /// over this property on the ZipFile. Typically, you would use the + /// per-entry Password when most entries in the zip archive use one password, + /// and a few entries use a different password. If all entries in the zip + /// file use the same password, then it is simpler to just set this property + /// on the ZipFile itself, whether creating a zip archive or extracting + /// a zip archive. + /// + /// + /// + /// + /// + /// + /// This example creates a zip file, using password protection for the + /// entries, and then extracts the entries from the zip file. When creating + /// the zip file, the Readme.txt file is not protected with a password, but + /// the other two are password-protected as they are saved. During extraction, + /// each file is extracted with the appropriate password. + /// + /// + /// // create a file with encryption + /// using (ZipFile zip = new ZipFile()) + /// { + /// zip.AddFile("ReadMe.txt"); + /// zip.Password= "!Secret1"; + /// zip.AddFile("MapToTheSite-7440-N49th.png"); + /// zip.AddFile("2008-Regional-Sales-Report.pdf"); + /// zip.Save("EncryptedArchive.zip"); + /// } + /// + /// // extract entries that use encryption + /// using (ZipFile zip = ZipFile.Read("EncryptedArchive.zip")) + /// { + /// zip.Password= "!Secret1"; + /// zip.ExtractAll("extractDir"); + /// } + /// + /// + /// + /// + /// Using zip As New ZipFile + /// zip.AddFile("ReadMe.txt") + /// zip.Password = "123456!" + /// zip.AddFile("MapToTheSite-7440-N49th.png") + /// zip.Password= "!Secret1"; + /// zip.AddFile("2008-Regional-Sales-Report.pdf") + /// zip.Save("EncryptedArchive.zip") + /// End Using + /// + /// + /// ' extract entries that use encryption + /// Using (zip as ZipFile = ZipFile.Read("EncryptedArchive.zip")) + /// zip.Password= "!Secret1" + /// zip.ExtractAll("extractDir") + /// End Using + /// + /// + /// + /// + /// + /// ZipFile.Encryption + /// ZipEntry.Password + public String Password + { + set + { + _Password = value; + if (_Password == null) + { + Encryption = EncryptionAlgorithm.None; + } + else if (Encryption == EncryptionAlgorithm.None) + { + Encryption = EncryptionAlgorithm.PkzipWeak; + } + } + private get + { + return _Password; + } + } + + + + + + /// + /// The action the library should take when extracting a file that already + /// exists. + /// + /// + /// + /// + /// This property affects the behavior of the Extract methods (one of the + /// Extract() or ExtractWithPassword() overloads), when + /// extraction would would overwrite an existing filesystem file. If you do + /// not set this property, the library throws an exception when extracting an + /// entry would overwrite an existing file. + /// + /// + /// + /// This property has no effect when extracting to a stream, or when the file + /// to be extracted does not already exist. + /// + /// + /// + public ExtractExistingFileAction ExtractExistingFile + { + get; + set; + } + + + /// + /// The action the library should take when an error is encountered while + /// opening or reading files as they are saved into a zip archive. + /// + /// + /// + /// + /// Errors can occur as a file is being saved to the zip archive. For + /// example, the File.Open may fail, or a File.Read may fail, because of + /// lock conflicts or other reasons. + /// + /// + /// + /// The first problem might occur after having called AddDirectory() on a + /// directory that contains a Clipper .dbf file; the file is locked by + /// Clipper and cannot be opened for read by another process. An example of + /// the second problem might occur when trying to zip a .pst file that is in + /// use by Microsoft Outlook. Outlook locks a range on the file, which allows + /// other processes to open the file, but not read it in its entirety. + /// + /// + /// + /// This property tells DotNetZip what you would like to do in the case of + /// these errors. The primary options are: ZipErrorAction.Throw to + /// throw an exception (this is the default behavior if you don't set this + /// property); ZipErrorAction.Skip to Skip the file for which there + /// was an error and continue saving; ZipErrorAction.Retry to Retry + /// the entry that caused the problem; or + /// ZipErrorAction.InvokeErrorEvent to invoke an event handler. + /// + /// + /// + /// This property is implicitly set to ZipErrorAction.InvokeErrorEvent + /// if you add a handler to the event. If you set + /// this property to something other than + /// ZipErrorAction.InvokeErrorEvent, then the ZipError + /// event is implicitly cleared. What it means is you can set one or the + /// other (or neither), depending on what you want, but you never need to set + /// both. + /// + /// + /// + /// As with some other properties on the ZipFile class, like , , and , setting this property on a ZipFile + /// instance will cause the specified ZipErrorAction to be used on all + /// items that are subsequently added to the + /// ZipFile instance. If you set this property after you have added + /// items to the ZipFile, but before you have called Save(), + /// those items will not use the specified error handling action. + /// + /// + /// + /// If you want to handle any errors that occur with any entry in the zip + /// file in the same way, then set this property once, before adding any + /// entries to the zip archive. + /// + /// + /// + /// If you set this property to ZipErrorAction.Skip and you'd like to + /// learn which files may have been skipped after a Save(), you can + /// set the on the ZipFile before + /// calling Save(). A message will be emitted into that writer for + /// each skipped file, if any. + /// + /// + /// + /// + /// + /// This example shows how to tell DotNetZip to skip any files for which an + /// error is generated during the Save(). + /// + /// Public Sub SaveZipFile() + /// Dim SourceFolder As String = "fodder" + /// Dim DestFile As String = "eHandler.zip" + /// Dim sw as New StringWriter + /// Using zipArchive As ZipFile = New ZipFile + /// ' Tell DotNetZip to skip any files for which it encounters an error + /// zipArchive.ZipErrorAction = ZipErrorAction.Skip + /// zipArchive.StatusMessageTextWriter = sw + /// zipArchive.AddDirectory(SourceFolder) + /// zipArchive.Save(DestFile) + /// End Using + /// ' examine sw here to see any messages + /// End Sub + /// + /// + /// + /// + /// + /// + + public ZipErrorAction ZipErrorAction + { + get + { + if (ZipError != null) + _zipErrorAction = ZipErrorAction.InvokeErrorEvent; + return _zipErrorAction; + } + set + { + _zipErrorAction = value; + if (_zipErrorAction != ZipErrorAction.InvokeErrorEvent && ZipError != null) + ZipError = null; + } + } + + + /// + /// The Encryption to use for entries added to the ZipFile. + /// + /// + /// + /// + /// Set this when creating a zip archive, or when updating a zip archive. The + /// specified Encryption is applied to the entries subsequently added to the + /// ZipFile instance. Applications do not need to set the + /// Encryption property when reading or extracting a zip archive. + /// + /// + /// + /// If you set this to something other than EncryptionAlgorithm.None, you + /// will also need to set the . + /// + /// + /// + /// As with some other properties on the ZipFile class, like and , setting this + /// property on a ZipFile instance will cause the specified + /// EncryptionAlgorithm to be used on all items + /// that are subsequently added to the ZipFile instance. In other + /// words, if you set this property after you have added items to the + /// ZipFile, but before you have called Save(), those items will + /// not be encrypted or protected with a password in the resulting zip + /// archive. To get a zip archive with encrypted entries, set this property, + /// along with the property, before calling + /// AddFile, AddItem, or AddDirectory (etc.) on the + /// ZipFile instance. + /// + /// + /// + /// If you read a ZipFile, you can modify the Encryption on an + /// encrypted entry, only by setting the Encryption property on the + /// ZipEntry itself. Setting the Encryption property on the + /// ZipFile, once it has been created via a call to + /// ZipFile.Read(), does not affect entries that were previously read. + /// + /// + /// + /// For example, suppose you read a ZipFile, and there is an encrypted + /// entry. Setting the Encryption property on that ZipFile and + /// then calling Save() on the ZipFile does not update the + /// Encryption used for the entries in the archive. Neither is an + /// exception thrown. Instead, what happens during the Save() is that + /// all previously existing entries are copied through to the new zip archive, + /// with whatever encryption and password that was used when originally + /// creating the zip archive. Upon re-reading that archive, to extract + /// entries, applications should use the original password or passwords, if + /// any. + /// + /// + /// + /// Suppose an application reads a ZipFile, and there is an encrypted + /// entry. Setting the Encryption property on that ZipFile and + /// then adding new entries (via AddFile(), AddEntry(), etc) + /// and then calling Save() on the ZipFile does not update the + /// Encryption on any of the entries that had previously been in the + /// ZipFile. The Encryption property applies only to the + /// newly-added entries. + /// + /// + /// + /// + /// + /// + /// This example creates a zip archive that uses encryption, and then extracts + /// entries from the archive. When creating the zip archive, the ReadMe.txt + /// file is zipped without using a password or encryption. The other files + /// use encryption. + /// + /// + /// + /// // Create a zip archive with AES Encryption. + /// using (ZipFile zip = new ZipFile()) + /// { + /// zip.AddFile("ReadMe.txt"); + /// zip.Encryption= EncryptionAlgorithm.WinZipAes256; + /// zip.Password= "Top.Secret.No.Peeking!"; + /// zip.AddFile("7440-N49th.png"); + /// zip.AddFile("2008-Regional-Sales-Report.pdf"); + /// zip.Save("EncryptedArchive.zip"); + /// } + /// + /// // Extract a zip archive that uses AES Encryption. + /// // You do not need to specify the algorithm during extraction. + /// using (ZipFile zip = ZipFile.Read("EncryptedArchive.zip")) + /// { + /// zip.Password= "Top.Secret.No.Peeking!"; + /// zip.ExtractAll("extractDirectory"); + /// } + /// + /// + /// + /// ' Create a zip that uses Encryption. + /// Using zip As New ZipFile() + /// zip.Encryption= EncryptionAlgorithm.WinZipAes256 + /// zip.Password= "Top.Secret.No.Peeking!" + /// zip.AddFile("ReadMe.txt") + /// zip.AddFile("7440-N49th.png") + /// zip.AddFile("2008-Regional-Sales-Report.pdf") + /// zip.Save("EncryptedArchive.zip") + /// End Using + /// + /// ' Extract a zip archive that uses AES Encryption. + /// ' You do not need to specify the algorithm during extraction. + /// Using (zip as ZipFile = ZipFile.Read("EncryptedArchive.zip")) + /// zip.Password= "Top.Secret.No.Peeking!" + /// zip.ExtractAll("extractDirectory") + /// End Using + /// + /// + /// + /// + /// ZipFile.Password + /// ZipEntry.Encryption + public EncryptionAlgorithm Encryption + { + get + { + return _Encryption; + } + set + { + if (value == EncryptionAlgorithm.Unsupported) + throw new InvalidOperationException("You may not set Encryption to that value."); + _Encryption = value; + } + } + + + + /// + /// A callback that allows the application to specify the compression level + /// to use for entries subsequently added to the zip archive. + /// + /// + /// + /// + /// + /// With this callback, the DotNetZip library allows the application to + /// determine whether compression will be used, at the time of the + /// Save. This may be useful if the application wants to favor + /// speed over size, and wants to defer the decision until the time of + /// Save. + /// + /// + /// + /// Typically applications set the property on + /// the ZipFile or on each ZipEntry to determine the level of + /// compression used. This is done at the time the entry is added to the + /// ZipFile. Setting the property to + /// Ionic.Zlib.CompressionLevel.None means no compression will be used. + /// + /// + /// + /// This callback allows the application to defer the decision on the + /// CompressionLevel to use, until the time of the call to + /// ZipFile.Save(). The callback is invoked once per ZipEntry, + /// at the time the data for the entry is being written out as part of a + /// Save() operation. The application can use whatever criteria it + /// likes in determining the level to return. For example, an application may + /// wish that no .mp3 files should be compressed, because they are already + /// compressed and the extra compression is not worth the CPU time incurred, + /// and so can return None for all .mp3 entries. + /// + /// + /// + /// The library determines whether compression will be attempted for an entry + /// this way: If the entry is a zero length file, or a directory, no + /// compression is used. Otherwise, if this callback is set, it is invoked + /// and the CompressionLevel is set to the return value. If this + /// callback has not been set, then the previously set value for + /// CompressionLevel is used. + /// + /// + /// + public SetCompressionCallback SetCompression + { + get; + set; + } + + + /// + /// The maximum size of an output segment, when saving a split Zip file. + /// + /// + /// + /// Set this to a non-zero value before calling or to specify that the ZipFile should be saved as a + /// split archive, also sometimes called a spanned archive. Some also + /// call them multi-file archives. + /// + /// + /// + /// A split zip archive is saved in a set of discrete filesystem files, + /// rather than in a single file. This is handy when transmitting the + /// archive in email or some other mechanism that has a limit to the size of + /// each file. The first file in a split archive will be named + /// basename.z01, the second will be named basename.z02, and + /// so on. The final file is named basename.zip. According to the zip + /// specification from PKWare, the minimum value is 65536, for a 64k segment + /// size. The maximum number of segments allows in a split archive is 99. + /// + /// + /// + /// The value of this property determines the maximum size of a split + /// segment when writing a split archive. For example, suppose you have a + /// ZipFile that would save to a single file of 200k. If you set the + /// MaxOutputSegmentSize to 65536 before calling Save(), you + /// will get four distinct output files. On the other hand if you set this + /// property to 256k, then you will get a single-file archive for that + /// ZipFile. + /// + /// + /// + /// The size of each split output file will be as large as possible, up to + /// the maximum size set here. The zip specification requires that some data + /// fields in a zip archive may not span a split boundary, and an output + /// segment may be smaller than the maximum if necessary to avoid that + /// problem. Also, obviously the final segment of the archive may be smaller + /// than the maximum segment size. Segments will never be larger than the + /// value set with this property. + /// + /// + /// + /// You can save a split Zip file only when saving to a regular filesystem + /// file. It's not possible to save a split zip file as a self-extracting + /// archive, nor is it possible to save a split zip file to a stream. When + /// saving to a SFX or to a Stream, this property is ignored. + /// + /// + /// + /// About interoperability: Split or spanned zip files produced by DotNetZip + /// can be read by WinZip or PKZip, and vice-versa. Segmented zip files may + /// not be readable by other tools, if those other tools don't support zip + /// spanning or splitting. When in doubt, test. I don't believe Windows + /// Explorer can extract a split archive. + /// + /// + /// + /// This property has no effect when reading a split archive. You can read + /// a split archive in the normal way with DotNetZip. + /// + /// + /// + /// When saving a zip file, if you want a regular zip file rather than a + /// split zip file, don't set this property, or set it to Zero. + /// + /// + /// + /// If you read a split archive, with and + /// then subsequently call ZipFile.Save(), unless you set this + /// property before calling Save(), you will get a normal, + /// single-file archive. + /// + /// + /// + /// + public Int32 MaxOutputSegmentSize + { + get + { + return _maxOutputSegmentSize; + } + set + { + if (value < 65536 && value != 0) + throw new ZipException("The minimum acceptable segment size is 65536."); + _maxOutputSegmentSize = value; + } + } + + + /// + /// Returns the number of segments used in the most recent Save() operation. + /// + /// + /// + /// This is normally zero, unless you have set the property. If you have set , and then you save a file, after the call to + /// Save() completes, you can read this value to learn the number of segments that + /// were created. + /// + /// + /// If you call Save("Archive.zip"), and it creates 5 segments, then you + /// will have filesystem files named Archive.z01, Archive.z02, Archive.z03, + /// Archive.z04, and Archive.zip, and the value of this property will be 5. + /// + /// + /// + public Int32 NumberOfSegmentsForMostRecentSave + { + get + { + return unchecked((Int32)_numberOfSegmentsForMostRecentSave + 1); + } + } + + +#if !NETCF + /// + /// The size threshold for an entry, above which a parallel deflate is used. + /// + /// + /// + /// + /// + /// DotNetZip will use multiple threads to compress any ZipEntry, + /// if the entry is larger than the given size. Zero means "always + /// use parallel deflate", while -1 means "never use parallel + /// deflate". The default value for this property is 512k. Aside + /// from the special values of 0 and 1, the minimum value is 65536. + /// + /// + /// + /// If the entry size cannot be known before compression, as with a + /// read-forward stream, then Parallel deflate will never be + /// performed, unless the value of this property is zero. + /// + /// + /// + /// A parallel deflate operations will speed up the compression of + /// large files, on computers with multiple CPUs or multiple CPU + /// cores. For files above 1mb, on a dual core or dual-cpu (2p) + /// machine, the time required to compress the file can be 70% of the + /// single-threaded deflate. For very large files on 4p machines the + /// compression can be done in 30% of the normal time. The downside + /// is that parallel deflate consumes extra memory during the deflate, + /// and the deflation is not as effective. + /// + /// + /// + /// Parallel deflate tends to yield slightly less compression when + /// compared to as single-threaded deflate; this is because the original + /// data stream is split into multiple independent buffers, each of which + /// is compressed in parallel. But because they are treated + /// independently, there is no opportunity to share compression + /// dictionaries. For that reason, a deflated stream may be slightly + /// larger when compressed using parallel deflate, as compared to a + /// traditional single-threaded deflate. Sometimes the increase over the + /// normal deflate is as much as 5% of the total compressed size. For + /// larger files it can be as small as 0.1%. + /// + /// + /// + /// Multi-threaded compression does not give as much an advantage when + /// using Encryption. This is primarily because encryption tends to slow + /// down the entire pipeline. Also, multi-threaded compression gives less + /// of an advantage when using lower compression levels, for example . You may have to + /// perform some tests to determine the best approach for your situation. + /// + /// + /// + /// + /// + /// + public long ParallelDeflateThreshold + { + set + { + if ((value != 0) && (value != -1) && (value < 64 * 1024)) + throw new ArgumentOutOfRangeException("ParallelDeflateThreshold should be -1, 0, or > 65536"); + _ParallelDeflateThreshold = value; + } + get + { + return _ParallelDeflateThreshold; + } + } + + /// + /// The maximum number of buffer pairs to use when performing + /// parallel compression. + /// + /// + /// + /// + /// This property sets an upper limit on the number of memory + /// buffer pairs to create when performing parallel + /// compression. The implementation of the parallel + /// compression stream allocates multiple buffers to + /// facilitate parallel compression. As each buffer fills up, + /// the stream uses + /// ThreadPool.QueueUserWorkItem() to compress those + /// buffers in a background threadpool thread. After a buffer + /// is compressed, it is re-ordered and written to the output + /// stream. + /// + /// + /// + /// A higher number of buffer pairs enables a higher degree of + /// parallelism, which tends to increase the speed of compression on + /// multi-cpu computers. On the other hand, a higher number of buffer + /// pairs also implies a larger memory consumption, more active worker + /// threads, and a higher cpu utilization for any compression. This + /// property enables the application to limit its memory consumption and + /// CPU utilization behavior depending on requirements. + /// + /// + /// + /// For each compression "task" that occurs in parallel, there are 2 + /// buffers allocated: one for input and one for output. This property + /// sets a limit for the number of pairs. The total amount of storage + /// space allocated for buffering will then be (N*S*2), where N is the + /// number of buffer pairs, S is the size of each buffer (). By default, DotNetZip allocates 4 buffer + /// pairs per CPU core, so if your machine has 4 cores, and you retain + /// the default buffer size of 128k, then the + /// ParallelDeflateOutputStream will use 4 * 4 * 2 * 128kb of buffer + /// memory in total, or 4mb, in blocks of 128kb. If you then set this + /// property to 8, then the number will be 8 * 2 * 128kb of buffer + /// memory, or 2mb. + /// + /// + /// + /// CPU utilization will also go up with additional buffers, because a + /// larger number of buffer pairs allows a larger number of background + /// threads to compress in parallel. If you find that parallel + /// compression is consuming too much memory or CPU, you can adjust this + /// value downward. + /// + /// + /// + /// The default value is 16. Different values may deliver better or + /// worse results, depending on your priorities and the dynamic + /// performance characteristics of your storage and compute resources. + /// + /// + /// + /// This property is not the number of buffer pairs to use; it is an + /// upper limit. An illustration: Suppose you have an application that + /// uses the default value of this property (which is 16), and it runs + /// on a machine with 2 CPU cores. In that case, DotNetZip will allocate + /// 4 buffer pairs per CPU core, for a total of 8 pairs. The upper + /// limit specified by this property has no effect. + /// + /// + /// + /// The application can set this value at any time + /// before calling ZipFile.Save(). + /// + /// + /// + /// + /// + public int ParallelDeflateMaxBufferPairs + { + get + { + return _maxBufferPairs; + } + set + { + if (value < 4) + throw new ArgumentOutOfRangeException("ParallelDeflateMaxBufferPairs", + "Value must be 4 or greater."); + _maxBufferPairs = value; + } + } +#endif + + + /// Provides a string representation of the instance. + /// a string representation of the instance. + public override String ToString() + { + return String.Format("ZipFile::{0}", Name); + } + + + /// + /// Returns the version number on the DotNetZip assembly. + /// + /// + /// + /// + /// This property is exposed as a convenience. Callers could also get the + /// version value by retrieving GetName().Version on the + /// System.Reflection.Assembly object pointing to the DotNetZip + /// assembly. But sometimes it is not clear which assembly is being loaded. + /// This property makes it clear. + /// + /// + /// This static property is primarily useful for diagnostic purposes. + /// + /// + public static System.Version LibraryVersion + { + get + { + return System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; + } + } + + internal void NotifyEntryChanged() + { + _contentsChanged = true; + } + + + internal Stream StreamForDiskNumber(uint diskNumber) + { + if (diskNumber + 1 == this._diskNumberWithCd || + (diskNumber == 0 && this._diskNumberWithCd == 0)) + { + //return (this.ReadStream as FileStream); + return this.ReadStream; + } + return ZipSegmentedStream.ForReading(this._readName ?? this._name, + diskNumber, _diskNumberWithCd); + } + + + + // called by ZipEntry in ZipEntry.Extract(), when there is no stream set for the + // ZipEntry. + internal void Reset(bool whileSaving) + { + if (_JustSaved) + { + // read in the just-saved zip archive + using (ZipFile x = new ZipFile()) + { + // workitem 10735 + x._readName = x._name = whileSaving + ? (this._readName ?? this._name) + : this._name; + x.AlternateEncoding = this.AlternateEncoding; + x.AlternateEncodingUsage = this.AlternateEncodingUsage; + ReadIntoInstance(x); + // copy the contents of the entries. + // cannot just replace the entries - the app may be holding them + foreach (ZipEntry e1 in x) + { + foreach (ZipEntry e2 in this) + { + if (e1.FileName == e2.FileName) + { + e2.CopyMetaData(e1); + break; + } + } + } + } + _JustSaved = false; + } + } + + + #endregion + + #region Constructors + + /// + /// Creates a new ZipFile instance, using the specified filename. + /// + /// + /// + /// + /// Applications can use this constructor to create a new ZipFile for writing, + /// or to slurp in an existing zip archive for read and update purposes. + /// + /// + /// + /// To create a new zip archive, an application can call this constructor, + /// passing the name of a file that does not exist. The name may be a fully + /// qualified path. Then the application can add directories or files to the + /// ZipFile via AddDirectory(), AddFile(), AddItem() + /// and then write the zip archive to the disk by calling Save(). The + /// zip file is not actually opened and written to the disk until the + /// application calls ZipFile.Save(). At that point the new zip file + /// with the given name is created. + /// + /// + /// + /// If you won't know the name of the Zipfile until the time you call + /// ZipFile.Save(), or if you plan to save to a stream (which has no + /// name), then you should use the no-argument constructor. + /// + /// + /// + /// The application can also call this constructor to read an existing zip + /// archive. passing the name of a valid zip file that does exist. But, it's + /// better form to use the static method, + /// passing the name of the zip file, because using ZipFile.Read() in + /// your code communicates very clearly what you are doing. In either case, + /// the file is then read into the ZipFile instance. The app can then + /// enumerate the entries or can modify the zip file, for example adding + /// entries, removing entries, changing comments, and so on. + /// + /// + /// + /// One advantage to this parameterized constructor: it allows applications to + /// use the same code to add items to a zip archive, regardless of whether the + /// zip file exists. + /// + /// + /// + /// Instances of the ZipFile class are not multi-thread safe. You may + /// not party on a single instance with multiple threads. You may have + /// multiple threads that each use a distinct ZipFile instance, or you + /// can synchronize multi-thread access to a single instance. + /// + /// + /// + /// By the way, since DotNetZip is so easy to use, don't you think you should + /// donate $5 or $10? + /// + /// + /// + /// + /// + /// Thrown if name refers to an existing file that is not a valid zip file. + /// + /// + /// + /// This example shows how to create a zipfile, and add a few files into it. + /// + /// String ZipFileToCreate = "archive1.zip"; + /// String DirectoryToZip = "c:\\reports"; + /// using (ZipFile zip = new ZipFile()) + /// { + /// // Store all files found in the top level directory, into the zip archive. + /// String[] filenames = System.IO.Directory.GetFiles(DirectoryToZip); + /// zip.AddFiles(filenames, "files"); + /// zip.Save(ZipFileToCreate); + /// } + /// + /// + /// + /// Dim ZipFileToCreate As String = "archive1.zip" + /// Dim DirectoryToZip As String = "c:\reports" + /// Using zip As ZipFile = New ZipFile() + /// Dim filenames As String() = System.IO.Directory.GetFiles(DirectoryToZip) + /// zip.AddFiles(filenames, "files") + /// zip.Save(ZipFileToCreate) + /// End Using + /// + /// + /// + /// The filename to use for the new zip archive. + /// + public ZipFile(string fileName) + { + try + { + _InitInstance(fileName, null); + } + catch (Exception e1) + { + throw new ZipException(String.Format("Could not read {0} as a zip file", fileName), e1); + } + } + + + /// + /// Creates a new ZipFile instance, using the specified name for the + /// filename, and the specified Encoding. + /// + /// + /// + /// + /// See the documentation on the ZipFile + /// constructor that accepts a single string argument for basic + /// information on all the ZipFile constructors. + /// + /// + /// + /// The Encoding is used as the default alternate encoding for entries with + /// filenames or comments that cannot be encoded with the IBM437 code page. + /// This is equivalent to setting the property on the ZipFile + /// instance after construction. + /// + /// + /// + /// Instances of the ZipFile class are not multi-thread safe. You may + /// not party on a single instance with multiple threads. You may have + /// multiple threads that each use a distinct ZipFile instance, or you + /// can synchronize multi-thread access to a single instance. + /// + /// + /// + /// + /// + /// Thrown if name refers to an existing file that is not a valid zip file. + /// + /// + /// The filename to use for the new zip archive. + /// The Encoding is used as the default alternate + /// encoding for entries with filenames or comments that cannot be encoded + /// with the IBM437 code page. + public ZipFile(string fileName, System.Text.Encoding encoding) + { + try + { + AlternateEncoding = encoding; + AlternateEncodingUsage = ZipOption.Always; + _InitInstance(fileName, null); + } + catch (Exception e1) + { + throw new ZipException(String.Format("{0} is not a valid zip file", fileName), e1); + } + } + + + + /// + /// Create a zip file, without specifying a target filename or stream to save to. + /// + /// + /// + /// + /// See the documentation on the ZipFile + /// constructor that accepts a single string argument for basic + /// information on all the ZipFile constructors. + /// + /// + /// + /// After instantiating with this constructor and adding entries to the + /// archive, the application should call or + /// to save to a file or a + /// stream, respectively. The application can also set the + /// property and then call the no-argument method. (This + /// is the preferred approach for applications that use the library through + /// COM interop.) If you call the no-argument method + /// without having set the Name of the ZipFile, either through + /// the parameterized constructor or through the explicit property , the + /// Save() will throw, because there is no place to save the file. + /// + /// + /// Instances of the ZipFile class are not multi-thread safe. You may + /// have multiple threads that each use a distinct ZipFile instance, or + /// you can synchronize multi-thread access to a single instance. + /// + /// + /// + /// + /// This example creates a Zip archive called Backup.zip, containing all the files + /// in the directory DirectoryToZip. Files within subdirectories are not zipped up. + /// + /// using (ZipFile zip = new ZipFile()) + /// { + /// // Store all files found in the top level directory, into the zip archive. + /// // note: this code does not recurse subdirectories! + /// String[] filenames = System.IO.Directory.GetFiles(DirectoryToZip); + /// zip.AddFiles(filenames, "files"); + /// zip.Save("Backup.zip"); + /// } + /// + /// + /// + /// Using zip As New ZipFile + /// ' Store all files found in the top level directory, into the zip archive. + /// ' note: this code does not recurse subdirectories! + /// Dim filenames As String() = System.IO.Directory.GetFiles(DirectoryToZip) + /// zip.AddFiles(filenames, "files") + /// zip.Save("Backup.zip") + /// End Using + /// + /// + public ZipFile() + { + _InitInstance(null, null); + } + + + /// + /// Create a zip file, specifying a text Encoding, but without specifying a + /// target filename or stream to save to. + /// + /// + /// + /// + /// See the documentation on the ZipFile + /// constructor that accepts a single string argument for basic + /// information on all the ZipFile constructors. + /// + /// + /// + /// + /// + /// The Encoding is used as the default alternate encoding for entries with + /// filenames or comments that cannot be encoded with the IBM437 code page. + /// + public ZipFile(System.Text.Encoding encoding) + { + AlternateEncoding = encoding; + AlternateEncodingUsage = ZipOption.Always; + _InitInstance(null, null); + } + + + /// + /// Creates a new ZipFile instance, using the specified name for the + /// filename, and the specified status message writer. + /// + /// + /// + /// + /// See the documentation on the ZipFile + /// constructor that accepts a single string argument for basic + /// information on all the ZipFile constructors. + /// + /// + /// + /// This version of the constructor allows the caller to pass in a TextWriter, + /// to which verbose messages will be written during extraction or creation of + /// the zip archive. A console application may wish to pass + /// System.Console.Out to get messages on the Console. A graphical or headless + /// application may wish to capture the messages in a different + /// TextWriter, for example, a StringWriter, and then display + /// the messages in a TextBox, or generate an audit log of ZipFile operations. + /// + /// + /// + /// To encrypt the data for the files added to the ZipFile instance, + /// set the Password property after creating the ZipFile instance. + /// + /// + /// + /// Instances of the ZipFile class are not multi-thread safe. You may + /// not party on a single instance with multiple threads. You may have + /// multiple threads that each use a distinct ZipFile instance, or you + /// can synchronize multi-thread access to a single instance. + /// + /// + /// + /// + /// + /// Thrown if name refers to an existing file that is not a valid zip file. + /// + /// + /// + /// + /// using (ZipFile zip = new ZipFile("Backup.zip", Console.Out)) + /// { + /// // Store all files found in the top level directory, into the zip archive. + /// // note: this code does not recurse subdirectories! + /// // Status messages will be written to Console.Out + /// String[] filenames = System.IO.Directory.GetFiles(DirectoryToZip); + /// zip.AddFiles(filenames); + /// zip.Save(); + /// } + /// + /// + /// + /// Using zip As New ZipFile("Backup.zip", Console.Out) + /// ' Store all files found in the top level directory, into the zip archive. + /// ' note: this code does not recurse subdirectories! + /// ' Status messages will be written to Console.Out + /// Dim filenames As String() = System.IO.Directory.GetFiles(DirectoryToZip) + /// zip.AddFiles(filenames) + /// zip.Save() + /// End Using + /// + /// + /// + /// The filename to use for the new zip archive. + /// A TextWriter to use for writing + /// verbose status messages. + public ZipFile(string fileName, TextWriter statusMessageWriter) + { + try + { + _InitInstance(fileName, statusMessageWriter); + } + catch (Exception e1) + { + throw new ZipException(String.Format("{0} is not a valid zip file", fileName), e1); + } + } + + + /// + /// Creates a new ZipFile instance, using the specified name for the + /// filename, the specified status message writer, and the specified Encoding. + /// + /// + /// + /// + /// This constructor works like the ZipFile + /// constructor that accepts a single string argument. See that + /// reference for detail on what this constructor does. + /// + /// + /// + /// This version of the constructor allows the caller to pass in a + /// TextWriter, and an Encoding. The TextWriter will collect + /// verbose messages that are generated by the library during extraction or + /// creation of the zip archive. A console application may wish to pass + /// System.Console.Out to get messages on the Console. A graphical or + /// headless application may wish to capture the messages in a different + /// TextWriter, for example, a StringWriter, and then display + /// the messages in a TextBox, or generate an audit log of + /// ZipFile operations. + /// + /// + /// + /// The Encoding is used as the default alternate encoding for entries + /// with filenames or comments that cannot be encoded with the IBM437 code + /// page. This is a equivalent to setting the property on the ZipFile + /// instance after construction. + /// + /// + /// + /// To encrypt the data for the files added to the ZipFile instance, + /// set the Password property after creating the ZipFile + /// instance. + /// + /// + /// + /// Instances of the ZipFile class are not multi-thread safe. You may + /// not party on a single instance with multiple threads. You may have + /// multiple threads that each use a distinct ZipFile instance, or you + /// can synchronize multi-thread access to a single instance. + /// + /// + /// + /// + /// + /// Thrown if fileName refers to an existing file that is not a valid zip file. + /// + /// + /// The filename to use for the new zip archive. + /// A TextWriter to use for writing verbose + /// status messages. + /// + /// The Encoding is used as the default alternate encoding for entries with + /// filenames or comments that cannot be encoded with the IBM437 code page. + /// + public ZipFile(string fileName, TextWriter statusMessageWriter, + System.Text.Encoding encoding) + { + try + { + AlternateEncoding = encoding; + AlternateEncodingUsage = ZipOption.Always; + _InitInstance(fileName, statusMessageWriter); + } + catch (Exception e1) + { + throw new ZipException(String.Format("{0} is not a valid zip file", fileName), e1); + } + } + + + + + /// + /// Initialize a ZipFile instance by reading in a zip file. + /// + /// + /// + /// + /// + /// This method is primarily useful from COM Automation environments, when + /// reading or extracting zip files. In COM, it is not possible to invoke + /// parameterized constructors for a class. A COM Automation application can + /// update a zip file by using the default (no argument) + /// constructor, then calling Initialize() to read the contents + /// of an on-disk zip archive into the ZipFile instance. + /// + /// + /// + /// .NET applications are encouraged to use the ZipFile.Read() methods + /// for better clarity. + /// + /// + /// + /// the name of the existing zip file to read in. + public void Initialize(string fileName) + { + try + { + _InitInstance(fileName, null); + } + catch (Exception e1) + { + throw new ZipException(String.Format("{0} is not a valid zip file", fileName), e1); + } + } + + + + private void _initEntriesDictionary() + { + // workitem 9868 + StringComparer sc = (CaseSensitiveRetrieval) ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase; + _entries = (_entries == null) + ? new Dictionary(sc) + : new Dictionary(_entries, sc); + } + + + private void _InitInstance(string zipFileName, TextWriter statusMessageWriter) + { + // create a new zipfile + _name = zipFileName; + _StatusMessageTextWriter = statusMessageWriter; + _contentsChanged = true; + AddDirectoryWillTraverseReparsePoints = true; // workitem 8617 + CompressionLevel = Ionic.Zlib.CompressionLevel.Default; +#if !NETCF + ParallelDeflateThreshold = 512 * 1024; +#endif + // workitem 7685, 9868 + _initEntriesDictionary(); + + if (File.Exists(_name)) + { + if (FullScan) + ReadIntoInstance_Orig(this); + else + ReadIntoInstance(this); + this._fileAlreadyExists = true; + } + + return; + } + #endregion + + + + #region Indexers and Collections + + private List ZipEntriesAsList + { + get + { + if (_zipEntriesAsList == null) + _zipEntriesAsList = new List(_entries.Values); + return _zipEntriesAsList; + } + } + + /// + /// This is an integer indexer into the Zip archive. + /// + /// + /// + /// + /// This property is read-only. + /// + /// + /// + /// Internally, the ZipEntry instances that belong to the + /// ZipFile are stored in a Dictionary. When you use this + /// indexer the first time, it creates a read-only + /// List<ZipEntry> from the Dictionary.Values Collection. + /// If at any time you modify the set of entries in the ZipFile, + /// either by adding an entry, removing an entry, or renaming an + /// entry, a new List will be created, and the numeric indexes for the + /// remaining entries may be different. + /// + /// + /// + /// This means you cannot rename any ZipEntry from + /// inside an enumeration of the zip file. + /// + /// + /// + /// The index value. + /// + /// + /// + /// + /// + /// The ZipEntry within the Zip archive at the specified index. If the + /// entry does not exist in the archive, this indexer throws. + /// + /// + public ZipEntry this[int ix] + { + // workitem 6402 + get + { + return ZipEntriesAsList[ix]; + } + } + + + /// + /// This is a name-based indexer into the Zip archive. + /// + /// + /// + /// + /// This property is read-only. + /// + /// + /// + /// The property on the ZipFile + /// determines whether retrieval via this indexer is done via case-sensitive + /// comparisons. By default, retrieval is not case sensitive. This makes + /// sense on Windows, in which filesystems are not case sensitive. + /// + /// + /// + /// Regardless of case-sensitivity, it is not always the case that + /// this[value].FileName == value. In other words, the FileName + /// property of the ZipEntry retrieved with this indexer, may or may + /// not be equal to the index value. + /// + /// + /// + /// This is because DotNetZip performs a normalization of filenames passed to + /// this indexer, before attempting to retrieve the item. That normalization + /// includes: removal of a volume letter and colon, swapping backward slashes + /// for forward slashes. So, zip["dir1\\entry1.txt"].FileName == + /// "dir1/entry.txt". + /// + /// + /// + /// Directory entries in the zip file may be retrieved via this indexer only + /// with names that have a trailing slash. DotNetZip automatically appends a + /// trailing slash to the names of any directory entries added to a zip. + /// + /// + /// + /// + /// + /// This example extracts only the entries in a zip file that are .txt files. + /// + /// using (ZipFile zip = ZipFile.Read("PackedDocuments.zip")) + /// { + /// foreach (string s1 in zip.EntryFilenames) + /// { + /// if (s1.EndsWith(".txt")) + /// zip[s1].Extract("textfiles"); + /// } + /// } + /// + /// + /// Using zip As ZipFile = ZipFile.Read("PackedDocuments.zip") + /// Dim s1 As String + /// For Each s1 In zip.EntryFilenames + /// If s1.EndsWith(".txt") Then + /// zip(s1).Extract("textfiles") + /// End If + /// Next + /// End Using + /// + /// + /// + /// + /// + /// Thrown if the caller attempts to assign a non-null value to the indexer. + /// + /// + /// + /// The name of the file, including any directory path, to retrieve from the + /// zip. The filename match is not case-sensitive by default; you can use the + /// property to change this behavior. The + /// pathname can use forward-slashes or backward slashes. + /// + /// + /// + /// The ZipEntry within the Zip archive, given by the specified + /// filename. If the named entry does not exist in the archive, this indexer + /// returns null (Nothing in VB). + /// + /// + public ZipEntry this[String fileName] + { + get + { + var key = SharedUtilities.NormalizePathForUseInZipFile(fileName); + if (_entries.ContainsKey(key)) + return _entries[key]; + // workitem 11056 + key = key.Replace("/", "\\"); + if (_entries.ContainsKey(key)) + return _entries[key]; + return null; + +#if MESSY + foreach (ZipEntry e in _entries.Values) + { + if (this.CaseSensitiveRetrieval) + { + // check for the file match with a case-sensitive comparison. + if (e.FileName == fileName) return e; + // also check for equivalence + if (fileName.Replace("\\", "/") == e.FileName) return e; + if (e.FileName.Replace("\\", "/") == fileName) return e; + + // check for a difference only in trailing slash + if (e.FileName.EndsWith("/")) + { + var fileNameNoSlash = e.FileName.Trim("/".ToCharArray()); + if (fileNameNoSlash == fileName) return e; + // also check for equivalence + if (fileName.Replace("\\", "/") == fileNameNoSlash) return e; + if (fileNameNoSlash.Replace("\\", "/") == fileName) return e; + } + + } + else + { + // check for the file match in a case-insensitive manner. + if (String.Compare(e.FileName, fileName, StringComparison.CurrentCultureIgnoreCase) == 0) return e; + // also check for equivalence + if (String.Compare(fileName.Replace("\\", "/"), e.FileName, StringComparison.CurrentCultureIgnoreCase) == 0) return e; + if (String.Compare(e.FileName.Replace("\\", "/"), fileName, StringComparison.CurrentCultureIgnoreCase) == 0) return e; + + // check for a difference only in trailing slash + if (e.FileName.EndsWith("/")) + { + var fileNameNoSlash = e.FileName.Trim("/".ToCharArray()); + + if (String.Compare(fileNameNoSlash, fileName, StringComparison.CurrentCultureIgnoreCase) == 0) return e; + // also check for equivalence + if (String.Compare(fileName.Replace("\\", "/"), fileNameNoSlash, StringComparison.CurrentCultureIgnoreCase) == 0) return e; + if (String.Compare(fileNameNoSlash.Replace("\\", "/"), fileName, StringComparison.CurrentCultureIgnoreCase) == 0) return e; + + } + + } + + } + return null; + +#endif + } + } + + + /// + /// The list of filenames for the entries contained within the zip archive. + /// + /// + /// + /// According to the ZIP specification, the names of the entries use forward + /// slashes in pathnames. If you are scanning through the list, you may have + /// to swap forward slashes for backslashes. + /// + /// + /// + /// + /// + /// This example shows one way to test if a filename is already contained + /// within a zip archive. + /// + /// String zipFileToRead= "PackedDocuments.zip"; + /// string candidate = "DatedMaterial.xps"; + /// using (ZipFile zip = new ZipFile(zipFileToRead)) + /// { + /// if (zip.EntryFilenames.Contains(candidate)) + /// Console.WriteLine("The file '{0}' exists in the zip archive '{1}'", + /// candidate, + /// zipFileName); + /// else + /// Console.WriteLine("The file, '{0}', does not exist in the zip archive '{1}'", + /// candidate, + /// zipFileName); + /// Console.WriteLine(); + /// } + /// + /// + /// Dim zipFileToRead As String = "PackedDocuments.zip" + /// Dim candidate As String = "DatedMaterial.xps" + /// Using zip As ZipFile.Read(ZipFileToRead) + /// If zip.EntryFilenames.Contains(candidate) Then + /// Console.WriteLine("The file '{0}' exists in the zip archive '{1}'", _ + /// candidate, _ + /// zipFileName) + /// Else + /// Console.WriteLine("The file, '{0}', does not exist in the zip archive '{1}'", _ + /// candidate, _ + /// zipFileName) + /// End If + /// Console.WriteLine + /// End Using + /// + /// + /// + /// + /// The list of strings for the filenames contained within the Zip archive. + /// + /// + public System.Collections.Generic.ICollection EntryFileNames + { + get + { + return _entries.Keys; + } + } + + + /// + /// Returns the readonly collection of entries in the Zip archive. + /// + /// + /// + /// + /// + /// If there are no entries in the current ZipFile, the value returned is a + /// non-null zero-element collection. If there are entries in the zip file, + /// the elements are returned in no particular order. + /// + /// + /// This is the implied enumerator on the ZipFile class. If you use a + /// ZipFile instance in a context that expects an enumerator, you will + /// get this collection. + /// + /// + /// + public System.Collections.Generic.ICollection Entries + { + get + { + return _entries.Values; + } + } + + + /// + /// Returns a readonly collection of entries in the Zip archive, sorted by FileName. + /// + /// + /// + /// If there are no entries in the current ZipFile, the value returned + /// is a non-null zero-element collection. If there are entries in the zip + /// file, the elements are returned sorted by the name of the entry. + /// + /// + /// + /// + /// This example fills a Windows Forms ListView with the entries in a zip file. + /// + /// + /// using (ZipFile zip = ZipFile.Read(zipFile)) + /// { + /// foreach (ZipEntry entry in zip.EntriesSorted) + /// { + /// ListViewItem item = new ListViewItem(n.ToString()); + /// n++; + /// string[] subitems = new string[] { + /// entry.FileName.Replace("/","\\"), + /// entry.LastModified.ToString("yyyy-MM-dd HH:mm:ss"), + /// entry.UncompressedSize.ToString(), + /// String.Format("{0,5:F0}%", entry.CompressionRatio), + /// entry.CompressedSize.ToString(), + /// (entry.UsesEncryption) ? "Y" : "N", + /// String.Format("{0:X8}", entry.Crc)}; + /// + /// foreach (String s in subitems) + /// { + /// ListViewItem.ListViewSubItem subitem = new ListViewItem.ListViewSubItem(); + /// subitem.Text = s; + /// item.SubItems.Add(subitem); + /// } + /// + /// this.listView1.Items.Add(item); + /// } + /// } + /// + /// + /// + /// + public System.Collections.Generic.ICollection EntriesSorted + { + get + { + var coll = new System.Collections.Generic.List(); + foreach (var e in this.Entries) + { + coll.Add(e); + } + StringComparison sc = (CaseSensitiveRetrieval) ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; + + coll.Sort((x, y) => { return String.Compare(x.FileName, y.FileName, sc); }); + return coll.AsReadOnly(); + } + } + + + /// + /// Returns the number of entries in the Zip archive. + /// + public int Count + { + get + { + return _entries.Count; + } + } + + + + /// + /// Removes the given ZipEntry from the zip archive. + /// + /// + /// + /// + /// After calling RemoveEntry, the application must call Save to + /// make the changes permanent. + /// + /// + /// + /// + /// Thrown if the specified ZipEntry does not exist in the ZipFile. + /// + /// + /// + /// In this example, all entries in the zip archive dating from before + /// December 31st, 2007, are removed from the archive. This is actually much + /// easier if you use the RemoveSelectedEntries method. But I needed an + /// example for RemoveEntry, so here it is. + /// + /// String ZipFileToRead = "ArchiveToModify.zip"; + /// System.DateTime Threshold = new System.DateTime(2007,12,31); + /// using (ZipFile zip = ZipFile.Read(ZipFileToRead)) + /// { + /// var EntriesToRemove = new System.Collections.Generic.List<ZipEntry>(); + /// foreach (ZipEntry e in zip) + /// { + /// if (e.LastModified < Threshold) + /// { + /// // We cannot remove the entry from the list, within the context of + /// // an enumeration of said list. + /// // So we add the doomed entry to a list to be removed later. + /// EntriesToRemove.Add(e); + /// } + /// } + /// + /// // actually remove the doomed entries. + /// foreach (ZipEntry zombie in EntriesToRemove) + /// zip.RemoveEntry(zombie); + /// + /// zip.Comment= String.Format("This zip archive was updated at {0}.", + /// System.DateTime.Now.ToString("G")); + /// + /// // save with a different name + /// zip.Save("Archive-Updated.zip"); + /// } + /// + /// + /// + /// Dim ZipFileToRead As String = "ArchiveToModify.zip" + /// Dim Threshold As New DateTime(2007, 12, 31) + /// Using zip As ZipFile = ZipFile.Read(ZipFileToRead) + /// Dim EntriesToRemove As New System.Collections.Generic.List(Of ZipEntry) + /// Dim e As ZipEntry + /// For Each e In zip + /// If (e.LastModified < Threshold) Then + /// ' We cannot remove the entry from the list, within the context of + /// ' an enumeration of said list. + /// ' So we add the doomed entry to a list to be removed later. + /// EntriesToRemove.Add(e) + /// End If + /// Next + /// + /// ' actually remove the doomed entries. + /// Dim zombie As ZipEntry + /// For Each zombie In EntriesToRemove + /// zip.RemoveEntry(zombie) + /// Next + /// zip.Comment = String.Format("This zip archive was updated at {0}.", DateTime.Now.ToString("G")) + /// 'save as a different name + /// zip.Save("Archive-Updated.zip") + /// End Using + /// + /// + /// + /// + /// The ZipEntry to remove from the zip. + /// + /// + /// + /// + public void RemoveEntry(ZipEntry entry) + { + //if (!_entries.Values.Contains(entry)) + // throw new ArgumentException("The entry you specified does not exist in the zip archive."); + if (entry == null) + throw new ArgumentNullException("entry"); + + _entries.Remove(SharedUtilities.NormalizePathForUseInZipFile(entry.FileName)); + _zipEntriesAsList = null; + +#if NOTNEEDED + if (_direntries != null) + { + bool FoundAndRemovedDirEntry = false; + foreach (ZipDirEntry de1 in _direntries) + { + if (entry.FileName == de1.FileName) + { + _direntries.Remove(de1); + FoundAndRemovedDirEntry = true; + break; + } + } + + if (!FoundAndRemovedDirEntry) + throw new BadStateException("The entry to be removed was not found in the directory."); + } +#endif + _contentsChanged = true; + } + + + + + /// + /// Removes the ZipEntry with the given filename from the zip archive. + /// + /// + /// + /// + /// After calling RemoveEntry, the application must call Save to + /// make the changes permanent. + /// + /// + /// + /// + /// + /// Thrown if the ZipFile is not updatable. + /// + /// + /// + /// Thrown if a ZipEntry with the specified filename does not exist in + /// the ZipFile. + /// + /// + /// + /// + /// This example shows one way to remove an entry with a given filename from + /// an existing zip archive. + /// + /// + /// String zipFileToRead= "PackedDocuments.zip"; + /// string candidate = "DatedMaterial.xps"; + /// using (ZipFile zip = ZipFile.Read(zipFileToRead)) + /// { + /// if (zip.EntryFilenames.Contains(candidate)) + /// { + /// zip.RemoveEntry(candidate); + /// zip.Comment= String.Format("The file '{0}' has been removed from this archive.", + /// Candidate); + /// zip.Save(); + /// } + /// } + /// + /// + /// Dim zipFileToRead As String = "PackedDocuments.zip" + /// Dim candidate As String = "DatedMaterial.xps" + /// Using zip As ZipFile = ZipFile.Read(zipFileToRead) + /// If zip.EntryFilenames.Contains(candidate) Then + /// zip.RemoveEntry(candidate) + /// zip.Comment = String.Format("The file '{0}' has been removed from this archive.", Candidate) + /// zip.Save + /// End If + /// End Using + /// + /// + /// + /// + /// The name of the file, including any directory path, to remove from the zip. + /// The filename match is not case-sensitive by default; you can use the + /// CaseSensitiveRetrieval property to change this behavior. The + /// pathname can use forward-slashes or backward slashes. + /// + /// + public void RemoveEntry(String fileName) + { + string modifiedName = ZipEntry.NameInArchive(fileName, null); + ZipEntry e = this[modifiedName]; + if (e == null) + throw new ArgumentException("The entry you specified was not found in the zip archive."); + + RemoveEntry(e); + } + + + #endregion + + #region Destructors and Disposers + + // /// + // /// This is the class Destructor, which gets called implicitly when the instance + // /// is destroyed. Because the ZipFile type implements IDisposable, this + // /// method calls Dispose(false). + // /// + // ~ZipFile() + // { + // // call Dispose with false. Since we're in the + // // destructor call, the managed resources will be + // // disposed of anyways. + // Dispose(false); + // } + + /// + /// Closes the read and write streams associated + /// to the ZipFile, if necessary. + /// + /// + /// + /// The Dispose() method is generally employed implicitly, via a using(..) {..} + /// statement. (Using...End Using in VB) If you do not employ a using + /// statement, insure that your application calls Dispose() explicitly. For + /// example, in a Powershell application, or an application that uses the COM + /// interop interface, you must call Dispose() explicitly. + /// + /// + /// + /// This example extracts an entry selected by name, from the Zip file to the + /// Console. + /// + /// using (ZipFile zip = ZipFile.Read(zipfile)) + /// { + /// foreach (ZipEntry e in zip) + /// { + /// if (WantThisEntry(e.FileName)) + /// zip.Extract(e.FileName, Console.OpenStandardOutput()); + /// } + /// } // Dispose() is called implicitly here. + /// + /// + /// + /// Using zip As ZipFile = ZipFile.Read(zipfile) + /// Dim e As ZipEntry + /// For Each e In zip + /// If WantThisEntry(e.FileName) Then + /// zip.Extract(e.FileName, Console.OpenStandardOutput()) + /// End If + /// Next + /// End Using ' Dispose is implicity called here + /// + /// + public void Dispose() + { + // dispose of the managed and unmanaged resources + Dispose(true); + + // tell the GC that the Finalize process no longer needs + // to be run for this object. + GC.SuppressFinalize(this); + } + + /// + /// Disposes any managed resources, if the flag is set, then marks the + /// instance disposed. This method is typically not called explicitly from + /// application code. + /// + /// + /// + /// Applications should call the no-arg Dispose method. + /// + /// + /// + /// indicates whether the method should dispose streams or not. + /// + protected virtual void Dispose(bool disposeManagedResources) + { + if (!this._disposed) + { + if (disposeManagedResources) + { + // dispose managed resources + if (_ReadStreamIsOurs) + { + if (_readstream != null) + { + // workitem 7704 +#if NETCF + _readstream.Close(); +#else + _readstream.Dispose(); +#endif + _readstream = null; + } + } + // only dispose the writestream if there is a backing file + if ((_temporaryFileName != null) && (_name != null)) + if (_writestream != null) + { + // workitem 7704 +#if NETCF + _writestream.Close(); +#else + _writestream.Dispose(); +#endif + _writestream = null; + } + +#if !NETCF + // workitem 10030 + if (this.ParallelDeflater != null) + { + this.ParallelDeflater.Dispose(); + this.ParallelDeflater = null; + } +#endif + } + this._disposed = true; + } + } + #endregion + + + #region private properties + + internal Stream ReadStream + { + get + { + if (_readstream == null) + { + if (_readName != null || _name !=null) + { + _readstream = File.Open(_readName ?? _name, + FileMode.Open, + FileAccess.Read, + FileShare.Read | FileShare.Write); + _ReadStreamIsOurs = true; + } + } + return _readstream; + } + } + + + + private Stream WriteStream + { + // workitem 9763 + get + { + if (_writestream != null) return _writestream; + if (_name == null) return _writestream; + + if (_maxOutputSegmentSize != 0) + { + _writestream = ZipSegmentedStream.ForWriting(this._name, _maxOutputSegmentSize); + return _writestream; + } + + SharedUtilities.CreateAndOpenUniqueTempFile(TempFileFolder ?? Path.GetDirectoryName(_name), + out _writestream, + out _temporaryFileName); + return _writestream; + } + set + { + if (value != null) + throw new ZipException("Cannot set the stream to a non-null value."); + _writestream = null; + } + } + #endregion + + #region private fields + private TextWriter _StatusMessageTextWriter; + private bool _CaseSensitiveRetrieval; + private Stream _readstream; + private Stream _writestream; + private UInt16 _versionMadeBy; + private UInt16 _versionNeededToExtract; + private UInt32 _diskNumberWithCd; + private Int32 _maxOutputSegmentSize; + private UInt32 _numberOfSegmentsForMostRecentSave; + private ZipErrorAction _zipErrorAction; + private bool _disposed; + //private System.Collections.Generic.List _entries; + private System.Collections.Generic.Dictionary _entries; + private List _zipEntriesAsList; + private string _name; + private string _readName; + private string _Comment; + internal string _Password; + private bool _emitNtfsTimes = true; + private bool _emitUnixTimes; + private Ionic.Zlib.CompressionStrategy _Strategy = Ionic.Zlib.CompressionStrategy.Default; + private Ionic.Zip.CompressionMethod _compressionMethod = Ionic.Zip.CompressionMethod.Deflate; + private bool _fileAlreadyExists; + private string _temporaryFileName; + private bool _contentsChanged; + private bool _hasBeenSaved; + private String _TempFileFolder; + private bool _ReadStreamIsOurs = true; + private object LOCK = new object(); + private bool _saveOperationCanceled; + private bool _extractOperationCanceled; + private bool _addOperationCanceled; + private EncryptionAlgorithm _Encryption; + private bool _JustSaved; + private long _locEndOfCDS = -1; + private uint _OffsetOfCentralDirectory; + private Int64 _OffsetOfCentralDirectory64; + private Nullable _OutputUsesZip64; + internal bool _inExtractAll; + private System.Text.Encoding _alternateEncoding = System.Text.Encoding.GetEncoding("IBM437"); // UTF-8 + private ZipOption _alternateEncodingUsage = ZipOption.Never; + private static System.Text.Encoding _defaultEncoding = System.Text.Encoding.GetEncoding("IBM437"); + + private int _BufferSize = BufferSizeDefault; + +#if !NETCF + internal Ionic.Zlib.ParallelDeflateOutputStream ParallelDeflater; + private long _ParallelDeflateThreshold; + private int _maxBufferPairs = 16; +#endif + + internal Zip64Option _zip64 = Zip64Option.Default; +#pragma warning disable 649 + private bool _SavingSfx; +#pragma warning restore 649 + + /// + /// Default size of the buffer used for IO. + /// + public static readonly int BufferSizeDefault = 32768; + + #endregion + } + + /// + /// Options for using ZIP64 extensions when saving zip archives. + /// + /// + /// + /// + /// + /// Designed many years ago, the original zip + /// specification from PKWARE allowed for 32-bit quantities for the + /// compressed and uncompressed sizes of zip entries, as well as a 32-bit quantity + /// for specifying the length of the zip archive itself, and a maximum of 65535 + /// entries. These limits are now regularly exceeded in many backup and archival + /// scenarios. Recently, PKWare added extensions to the original zip spec, called + /// "ZIP64 extensions", to raise those limitations. This property governs whether + /// DotNetZip will use those extensions when writing zip archives. The use of + /// these extensions is optional and explicit in DotNetZip because, despite the + /// status of ZIP64 as a bona fide standard, many other zip tools and libraries do + /// not support ZIP64, and therefore a zip file with ZIP64 extensions may be + /// unreadable by some of those other tools. + /// + /// + /// + /// Set this property to to always use ZIP64 + /// extensions when saving, regardless of whether your zip archive needs it. + /// Suppose you add 5 files, each under 100k, to a ZipFile. If you specify Always + /// for this flag, you will get a ZIP64 archive, though the archive does not need + /// to use ZIP64 because none of the original zip limits had been exceeded. + /// + /// + /// + /// Set this property to to tell the DotNetZip + /// library to never use ZIP64 extensions. This is useful for maximum + /// compatibility and interoperability, at the expense of the capability of + /// handling large files or large archives. NB: Windows Explorer in Windows XP + /// and Windows Vista cannot currently extract files from a zip64 archive, so if + /// you want to guarantee that a zip archive produced by this library will work in + /// Windows Explorer, use Never. If you set this property to , and your application creates a zip that would + /// exceed one of the Zip limits, the library will throw an exception while saving + /// the zip file. + /// + /// + /// + /// Set this property to to tell the + /// DotNetZip library to use the ZIP64 extensions when required by the + /// entry. After the file is compressed, the original and compressed sizes are + /// checked, and if they exceed the limits described above, then zip64 can be + /// used. That is the general idea, but there is an additional wrinkle when saving + /// to a non-seekable device, like the ASP.NET Response.OutputStream, or + /// Console.Out. When using non-seekable streams for output, the entry + /// header - which indicates whether zip64 is in use - is emitted before it is + /// known if zip64 is necessary. It is only after all entries have been saved + /// that it can be known if ZIP64 will be required. On seekable output streams, + /// after saving all entries, the library can seek backward and re-emit the zip + /// file header to be consistent with the actual ZIP64 requirement. But using a + /// non-seekable output stream, the library cannot seek backward, so the header + /// can never be changed. In other words, the archive's use of ZIP64 extensions is + /// not alterable after the header is emitted. Therefore, when saving to + /// non-seekable streams, using is the same + /// as using : it will always produce a zip + /// archive that uses ZIP64 extensions. + /// + /// + /// + public enum Zip64Option + { + /// + /// The default behavior, which is "Never". + /// (For COM clients, this is a 0 (zero).) + /// + Default = 0, + /// + /// Do not use ZIP64 extensions when writing zip archives. + /// (For COM clients, this is a 0 (zero).) + /// + Never = 0, + /// + /// Use ZIP64 extensions when writing zip archives, as necessary. + /// For example, when a single entry exceeds 0xFFFFFFFF in size, or when the archive as a whole + /// exceeds 0xFFFFFFFF in size, or when there are more than 65535 entries in an archive. + /// (For COM clients, this is a 1.) + /// + AsNecessary = 1, + /// + /// Always use ZIP64 extensions when writing zip archives, even when unnecessary. + /// (For COM clients, this is a 2.) + /// + Always + } + + + /// + /// An enum representing the values on a three-way toggle switch + /// for various options in the library. This might be used to + /// specify whether to employ a particular text encoding, or to use + /// ZIP64 extensions, or some other option. + /// + public enum ZipOption + { + /// + /// The default behavior. This is the same as "Never". + /// (For COM clients, this is a 0 (zero).) + /// + Default = 0, + /// + /// Never use the associated option. + /// (For COM clients, this is a 0 (zero).) + /// + Never = 0, + /// + /// Use the associated behavior "as necessary." + /// (For COM clients, this is a 1.) + /// + AsNecessary = 1, + /// + /// Use the associated behavior Always, whether necessary or not. + /// (For COM clients, this is a 2.) + /// + Always + } + + + enum AddOrUpdateAction + { + AddOnly = 0, + AddOrUpdate + } + +} + + + +// ================================================================== +// +// Information on the ZIP format: +// +// From +// http://www.pkware.com/documents/casestudies/APPNOTE.TXT +// +// Overall .ZIP file format: +// +// [local file header 1] +// [file data 1] +// [data descriptor 1] ** sometimes +// . +// . +// . +// [local file header n] +// [file data n] +// [data descriptor n] ** sometimes +// [archive decryption header] +// [archive extra data record] +// [central directory] +// [zip64 end of central directory record] +// [zip64 end of central directory locator] +// [end of central directory record] +// +// Local File Header format: +// local file header signature ... 4 bytes (0x04034b50) +// version needed to extract ..... 2 bytes +// general purpose bit field ..... 2 bytes +// compression method ............ 2 bytes +// last mod file time ............ 2 bytes +// last mod file date............. 2 bytes +// crc-32 ........................ 4 bytes +// compressed size................ 4 bytes +// uncompressed size.............. 4 bytes +// file name length............... 2 bytes +// extra field length ............ 2 bytes +// file name varies +// extra field varies +// +// +// Data descriptor: (used only when bit 3 of the general purpose bitfield is set) +// (although, I have found zip files where bit 3 is not set, yet this descriptor is present!) +// local file header signature 4 bytes (0x08074b50) ** sometimes!!! Not always +// crc-32 4 bytes +// compressed size 4 bytes +// uncompressed size 4 bytes +// +// +// Central directory structure: +// +// [file header 1] +// . +// . +// . +// [file header n] +// [digital signature] +// +// +// File header: (This is a ZipDirEntry) +// central file header signature 4 bytes (0x02014b50) +// version made by 2 bytes +// version needed to extract 2 bytes +// general purpose bit flag 2 bytes +// compression method 2 bytes +// last mod file time 2 bytes +// last mod file date 2 bytes +// crc-32 4 bytes +// compressed size 4 bytes +// uncompressed size 4 bytes +// file name length 2 bytes +// extra field length 2 bytes +// file comment length 2 bytes +// disk number start 2 bytes +// internal file attributes ** 2 bytes +// external file attributes *** 4 bytes +// relative offset of local header 4 bytes +// file name (variable size) +// extra field (variable size) +// file comment (variable size) +// +// ** The internal file attributes, near as I can tell, +// uses 0x01 for a file and a 0x00 for a directory. +// +// ***The external file attributes follows the MS-DOS file attribute byte, described here: +// at http://support.microsoft.com/kb/q125019/ +// 0x0010 => directory +// 0x0020 => file +// +// +// End of central directory record: +// +// end of central dir signature 4 bytes (0x06054b50) +// number of this disk 2 bytes +// number of the disk with the +// start of the central directory 2 bytes +// total number of entries in the +// central directory on this disk 2 bytes +// total number of entries in +// the central directory 2 bytes +// size of the central directory 4 bytes +// offset of start of central +// directory with respect to +// the starting disk number 4 bytes +// .ZIP file comment length 2 bytes +// .ZIP file comment (variable size) +// +// date and time are packed values, as MSDOS did them +// time: bits 0-4 : seconds (divided by 2) +// 5-10: minute +// 11-15: hour +// date bits 0-4 : day +// 5-8: month +// 9-15 year (since 1980) +// +// see http://msdn.microsoft.com/en-us/library/ms724274(VS.85).aspx + diff --git a/DotNetZip/Zip/ZipFile.x-IEnumerable.cs b/DotNetZip/Zip/ZipFile.x-IEnumerable.cs new file mode 100644 index 0000000..b6cd816 --- /dev/null +++ b/DotNetZip/Zip/ZipFile.x-IEnumerable.cs @@ -0,0 +1,154 @@ +// ZipFile.x-IEnumerable.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2006, 2007, 2008, 2009 Dino Chiesa and Microsoft Corporation. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2009-December-26 15:13:26> +// +// ------------------------------------------------------------------ +// +// This module defines smoe methods for IEnumerable support. It is +// particularly important for COM to have these things in a separate module. +// +// ------------------------------------------------------------------ + + +namespace Ionic.Zip +{ + + // For some weird reason, the method with the DispId(-4) attribute, which is used as + // the _NewEnum() method, and which is required to get enumeration to work from COM + // environments like VBScript and Javascript (etc) must be the LAST MEMBER in the + // source. In the event of Partial classes, it needs to be the last member defined + // in the last source module. The source modules are ordered alphabetically by + // filename. Not sure why this is true. In any case, we put the enumeration stuff + // here in this oddly-named module, for this reason. + // + + + + public partial class ZipFile + { + + + + + /// + /// Generic IEnumerator support, for use of a ZipFile in an enumeration. + /// + /// + /// + /// You probably do not want to call GetEnumerator explicitly. Instead + /// it is implicitly called when you use a loop in C#, or a + /// For Each loop in VB.NET. + /// + /// + /// + /// This example reads a zipfile of a given name, then enumerates the + /// entries in that zip file, and displays the information about each + /// entry on the Console. + /// + /// using (ZipFile zip = ZipFile.Read(zipfile)) + /// { + /// bool header = true; + /// foreach (ZipEntry e in zip) + /// { + /// if (header) + /// { + /// System.Console.WriteLine("Zipfile: {0}", zip.Name); + /// System.Console.WriteLine("Version Needed: 0x{0:X2}", e.VersionNeeded); + /// System.Console.WriteLine("BitField: 0x{0:X2}", e.BitField); + /// System.Console.WriteLine("Compression Method: 0x{0:X2}", e.CompressionMethod); + /// System.Console.WriteLine("\n{1,-22} {2,-6} {3,4} {4,-8} {0}", + /// "Filename", "Modified", "Size", "Ratio", "Packed"); + /// System.Console.WriteLine(new System.String('-', 72)); + /// header = false; + /// } + /// + /// System.Console.WriteLine("{1,-22} {2,-6} {3,4:F0}% {4,-8} {0}", + /// e.FileName, + /// e.LastModified.ToString("yyyy-MM-dd HH:mm:ss"), + /// e.UncompressedSize, + /// e.CompressionRatio, + /// e.CompressedSize); + /// + /// e.Extract(); + /// } + /// } + /// + /// + /// + /// Dim ZipFileToExtract As String = "c:\foo.zip" + /// Using zip As ZipFile = ZipFile.Read(ZipFileToExtract) + /// Dim header As Boolean = True + /// Dim e As ZipEntry + /// For Each e In zip + /// If header Then + /// Console.WriteLine("Zipfile: {0}", zip.Name) + /// Console.WriteLine("Version Needed: 0x{0:X2}", e.VersionNeeded) + /// Console.WriteLine("BitField: 0x{0:X2}", e.BitField) + /// Console.WriteLine("Compression Method: 0x{0:X2}", e.CompressionMethod) + /// Console.WriteLine(ChrW(10) & "{1,-22} {2,-6} {3,4} {4,-8} {0}", _ + /// "Filename", "Modified", "Size", "Ratio", "Packed" ) + /// Console.WriteLine(New String("-"c, 72)) + /// header = False + /// End If + /// Console.WriteLine("{1,-22} {2,-6} {3,4:F0}% {4,-8} {0}", _ + /// e.FileName, _ + /// e.LastModified.ToString("yyyy-MM-dd HH:mm:ss"), _ + /// e.UncompressedSize, _ + /// e.CompressionRatio, _ + /// e.CompressedSize ) + /// e.Extract + /// Next + /// End Using + /// + /// + /// + /// A generic enumerator suitable for use within a foreach loop. + public System.Collections.Generic.IEnumerator GetEnumerator() + { + foreach (ZipEntry e in _entries.Values) + yield return e; + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + + /// + /// An IEnumerator, for use of a ZipFile in a foreach construct. + /// + /// + /// + /// This method is included for COM support. An application generally does not call + /// this method directly. It is called implicitly by COM clients when enumerating + /// the entries in the ZipFile instance. In VBScript, this is done with a For Each + /// statement. In Javascript, this is done with new Enumerator(zipfile). + /// + /// + /// + /// The IEnumerator over the entries in the ZipFile. + /// + [System.Runtime.InteropServices.DispId(-4)] + public System.Collections.IEnumerator GetNewEnum() // the name of this method is not significant + { + return GetEnumerator(); + } + + } +} diff --git a/DotNetZip/Zip/ZipInputStream.cs b/DotNetZip/Zip/ZipInputStream.cs new file mode 100644 index 0000000..25622f6 --- /dev/null +++ b/DotNetZip/Zip/ZipInputStream.cs @@ -0,0 +1,827 @@ +// ZipInputStream.cs +// +// ------------------------------------------------------------------ +// +// Copyright (c) 2009-2010 Dino Chiesa. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2011-July-31 14:48:30> +// +// ------------------------------------------------------------------ +// +// This module defines the ZipInputStream class, which is a stream metaphor for +// reading zip files. This class does not depend on Ionic.Zip.ZipFile, but rather +// stands alongside it as an alternative "container" for ZipEntry, when reading zips. +// +// It adds one interesting method to the normal "stream" interface: GetNextEntry. +// +// ------------------------------------------------------------------ +// + +using System; +using System.Threading; +using System.Collections.Generic; +using System.IO; +using Ionic.Zip; + +namespace Ionic.Zip +{ + /// + /// Provides a stream metaphor for reading zip files. + /// + /// + /// + /// + /// This class provides an alternative programming model for reading zip files to + /// the one enabled by the class. Use this when reading zip + /// files, as an alternative to the class, when you would + /// like to use a Stream class to read the file. + /// + /// + /// + /// Some application designs require a readable stream for input. This stream can + /// be used to read a zip file, and extract entries. + /// + /// + /// + /// Both the ZipInputStream class and the ZipFile class can be used + /// to read and extract zip files. Both of them support many of the common zip + /// features, including Unicode, different compression levels, and ZIP64. The + /// programming models differ. For example, when extracting entries via calls to + /// the GetNextEntry() and Read() methods on the + /// ZipInputStream class, the caller is responsible for creating the file, + /// writing the bytes into the file, setting the attributes on the file, and + /// setting the created, last modified, and last accessed timestamps on the + /// file. All of these things are done automatically by a call to ZipEntry.Extract(). For this reason, the + /// ZipInputStream is generally recommended for when your application wants + /// to extract the data, without storing that data into a file. + /// + /// + /// + /// Aside from the obvious differences in programming model, there are some + /// differences in capability between the ZipFile class and the + /// ZipInputStream class. + /// + /// + /// + /// + /// ZipFile can be used to create or update zip files, or read and + /// extract zip files. ZipInputStream can be used only to read and + /// extract zip files. If you want to use a stream to create zip files, check + /// out the . + /// + /// + /// + /// ZipInputStream cannot read segmented or spanned + /// zip files. + /// + /// + /// + /// ZipInputStream will not read Zip file comments. + /// + /// + /// + /// When reading larger files, ZipInputStream will always underperform + /// ZipFile. This is because the ZipInputStream does a full scan on the + /// zip file, while the ZipFile class reads the central directory of the + /// zip file. + /// + /// + /// + /// + /// + public class ZipInputStream : Stream + { + /// + /// Create a ZipInputStream, wrapping it around an existing stream. + /// + /// + /// + /// + /// + /// While the class is generally easier + /// to use, this class provides an alternative to those + /// applications that want to read from a zipfile directly, + /// using a . + /// + /// + /// + /// Both the ZipInputStream class and the ZipFile class can be used + /// to read and extract zip files. Both of them support many of the common zip + /// features, including Unicode, different compression levels, and ZIP64. The + /// programming models differ. For example, when extracting entries via calls to + /// the GetNextEntry() and Read() methods on the + /// ZipInputStream class, the caller is responsible for creating the file, + /// writing the bytes into the file, setting the attributes on the file, and + /// setting the created, last modified, and last accessed timestamps on the + /// file. All of these things are done automatically by a call to ZipEntry.Extract(). For this reason, the + /// ZipInputStream is generally recommended for when your application wants + /// to extract the data, without storing that data into a file. + /// + /// + /// + /// Aside from the obvious differences in programming model, there are some + /// differences in capability between the ZipFile class and the + /// ZipInputStream class. + /// + /// + /// + /// + /// ZipFile can be used to create or update zip files, or read and extract + /// zip files. ZipInputStream can be used only to read and extract zip + /// files. If you want to use a stream to create zip files, check out the . + /// + /// + /// + /// ZipInputStream cannot read segmented or spanned + /// zip files. + /// + /// + /// + /// ZipInputStream will not read Zip file comments. + /// + /// + /// + /// When reading larger files, ZipInputStream will always underperform + /// ZipFile. This is because the ZipInputStream does a full scan on the + /// zip file, while the ZipFile class reads the central directory of the + /// zip file. + /// + /// + /// + /// + /// + /// + /// + /// The stream to read. It must be readable. This stream will be closed at + /// the time the ZipInputStream is closed. + /// + /// + /// + /// + /// This example shows how to read a zip file, and extract entries, using the + /// ZipInputStream class. + /// + /// + /// private void Unzip() + /// { + /// byte[] buffer= new byte[2048]; + /// int n; + /// using (var raw = File.Open(inputFileName, FileMode.Open, FileAccess.Read)) + /// { + /// using (var input= new ZipInputStream(raw)) + /// { + /// ZipEntry e; + /// while (( e = input.GetNextEntry()) != null) + /// { + /// if (e.IsDirectory) continue; + /// string outputPath = Path.Combine(extractDir, e.FileName); + /// using (var output = File.Open(outputPath, FileMode.Create, FileAccess.ReadWrite)) + /// { + /// while ((n= input.Read(buffer, 0, buffer.Length)) > 0) + /// { + /// output.Write(buffer,0,n); + /// } + /// } + /// } + /// } + /// } + /// } + /// + /// + /// + /// Private Sub UnZip() + /// Dim inputFileName As String = "MyArchive.zip" + /// Dim extractDir As String = "extract" + /// Dim buffer As Byte() = New Byte(2048) {} + /// Using raw As FileStream = File.Open(inputFileName, FileMode.Open, FileAccess.Read) + /// Using input As ZipInputStream = New ZipInputStream(raw) + /// Dim e As ZipEntry + /// Do While (Not e = input.GetNextEntry Is Nothing) + /// If Not e.IsDirectory Then + /// Using output As FileStream = File.Open(Path.Combine(extractDir, e.FileName), _ + /// FileMode.Create, FileAccess.ReadWrite) + /// Dim n As Integer + /// Do While (n = input.Read(buffer, 0, buffer.Length) > 0) + /// output.Write(buffer, 0, n) + /// Loop + /// End Using + /// End If + /// Loop + /// End Using + /// End Using + /// End Sub + /// + /// + public ZipInputStream(Stream stream) : this (stream, false) { } + + + + /// + /// Create a ZipInputStream, given the name of an existing zip file. + /// + /// + /// + /// + /// + /// This constructor opens a FileStream for the given zipfile, and + /// wraps a ZipInputStream around that. See the documentation for the + /// constructor for full details. + /// + /// + /// + /// While the class is generally easier + /// to use, this class provides an alternative to those + /// applications that want to read from a zipfile directly, + /// using a . + /// + /// + /// + /// + /// + /// The name of the filesystem file to read. + /// + /// + /// + /// + /// This example shows how to read a zip file, and extract entries, using the + /// ZipInputStream class. + /// + /// + /// private void Unzip() + /// { + /// byte[] buffer= new byte[2048]; + /// int n; + /// using (var input= new ZipInputStream(inputFileName)) + /// { + /// ZipEntry e; + /// while (( e = input.GetNextEntry()) != null) + /// { + /// if (e.IsDirectory) continue; + /// string outputPath = Path.Combine(extractDir, e.FileName); + /// using (var output = File.Open(outputPath, FileMode.Create, FileAccess.ReadWrite)) + /// { + /// while ((n= input.Read(buffer, 0, buffer.Length)) > 0) + /// { + /// output.Write(buffer,0,n); + /// } + /// } + /// } + /// } + /// } + /// + /// + /// + /// Private Sub UnZip() + /// Dim inputFileName As String = "MyArchive.zip" + /// Dim extractDir As String = "extract" + /// Dim buffer As Byte() = New Byte(2048) {} + /// Using input As ZipInputStream = New ZipInputStream(inputFileName) + /// Dim e As ZipEntry + /// Do While (Not e = input.GetNextEntry Is Nothing) + /// If Not e.IsDirectory Then + /// Using output As FileStream = File.Open(Path.Combine(extractDir, e.FileName), _ + /// FileMode.Create, FileAccess.ReadWrite) + /// Dim n As Integer + /// Do While (n = input.Read(buffer, 0, buffer.Length) > 0) + /// output.Write(buffer, 0, n) + /// Loop + /// End Using + /// End If + /// Loop + /// End Using + /// End Sub + /// + /// + public ZipInputStream(String fileName) + { + Stream stream = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.Read ); + _Init(stream, false, fileName); + } + + + /// + /// Create a ZipInputStream, explicitly specifying whether to + /// keep the underlying stream open. + /// + /// + /// + /// See the documentation for the ZipInputStream(Stream) + /// constructor for a discussion of the class, and an example of how to use the class. + /// + /// + /// + /// The stream to read from. It must be readable. + /// + /// + /// + /// true if the application would like the stream + /// to remain open after the ZipInputStream has been closed. + /// + public ZipInputStream(Stream stream, bool leaveOpen) + { + _Init(stream, leaveOpen, null); + } + + private void _Init(Stream stream, bool leaveOpen, string name) + { + _inputStream = stream; + if (!_inputStream.CanRead) + throw new ZipException("The stream must be readable."); + _container= new ZipContainer(this); + _provisionalAlternateEncoding = System.Text.Encoding.GetEncoding("IBM437"); + _leaveUnderlyingStreamOpen = leaveOpen; + _findRequired= true; + _name = name ?? "(stream)"; + } + + + /// Provides a string representation of the instance. + /// + /// + /// This can be useful for debugging purposes. + /// + /// + /// a string representation of the instance. + public override String ToString() + { + return String.Format ("ZipInputStream::{0}(leaveOpen({1})))", _name, _leaveUnderlyingStreamOpen); + } + + + /// + /// The text encoding to use when reading entries into the zip archive, for + /// those entries whose filenames or comments cannot be encoded with the + /// default (IBM437) encoding. + /// + /// + /// + /// + /// In its + /// zip specification, PKWare describes two options for encoding + /// filenames and comments: using IBM437 or UTF-8. But, some archiving tools + /// or libraries do not follow the specification, and instead encode + /// characters using the system default code page. For example, WinRAR when + /// run on a machine in Shanghai may encode filenames with the Big-5 Chinese + /// (950) code page. This behavior is contrary to the Zip specification, but + /// it occurs anyway. + /// + /// + /// + /// When using DotNetZip to read zip archives that use something other than + /// UTF-8 or IBM437, set this property to specify the code page to use when + /// reading encoded filenames and comments for each ZipEntry in the zip + /// file. + /// + /// + /// + /// This property is "provisional". When the entry in the zip archive is not + /// explicitly marked as using UTF-8, then IBM437 is used to decode filenames + /// and comments. If a loss of data would result from using IBM436 - + /// specifically when encoding and decoding is not reflexive - the codepage + /// specified here is used. It is possible, therefore, to have a given entry + /// with a Comment encoded in IBM437 and a FileName encoded with + /// the specified "provisional" codepage. + /// + /// + /// + /// When a zip file uses an arbitrary, non-UTF8 code page for encoding, there + /// is no standard way for the reader application - whether DotNetZip, WinZip, + /// WinRar, or something else - to know which codepage has been used for the + /// entries. Readers of zip files are not able to inspect the zip file and + /// determine the codepage that was used for the entries contained within it. + /// It is left to the application or user to determine the necessary codepage + /// when reading zip files encoded this way. If you use an incorrect codepage + /// when reading a zipfile, you will get entries with filenames that are + /// incorrect, and the incorrect filenames may even contain characters that + /// are not legal for use within filenames in Windows. Extracting entries with + /// illegal characters in the filenames will lead to exceptions. It's too bad, + /// but this is just the way things are with code pages in zip files. Caveat + /// Emptor. + /// + /// + /// + public System.Text.Encoding ProvisionalAlternateEncoding + { + get + { + return _provisionalAlternateEncoding; + } + set + { + _provisionalAlternateEncoding = value; + } + } + + + /// + /// Size of the work buffer to use for the ZLIB codec during decompression. + /// + /// + /// + /// Setting this affects the performance and memory efficiency of compression + /// and decompression. For larger files, setting this to a larger size may + /// improve performance, but the exact numbers vary depending on available + /// memory, and a bunch of other variables. I don't have good firm + /// recommendations on how to set it. You'll have to test it yourself. Or + /// just leave it alone and accept the default. + /// + public int CodecBufferSize + { + get; + set; + } + + + /// + /// Sets the password to be used on the ZipInputStream instance. + /// + /// + /// + /// + /// + /// When reading a zip archive, this password is used to read and decrypt the + /// entries that are encrypted within the zip file. When entries within a zip + /// file use different passwords, set the appropriate password for the entry + /// before the first call to Read() for each entry. + /// + /// + /// + /// When reading an entry that is not encrypted, the value of this property is + /// ignored. + /// + /// + /// + /// + /// + /// + /// This example uses the ZipInputStream to read and extract entries from a + /// zip file, using a potentially different password for each entry. + /// + /// + /// byte[] buffer= new byte[2048]; + /// int n; + /// using (var raw = File.Open(_inputFileName, FileMode.Open, FileAccess.Read )) + /// { + /// using (var input= new ZipInputStream(raw)) + /// { + /// ZipEntry e; + /// while (( e = input.GetNextEntry()) != null) + /// { + /// input.Password = PasswordForEntry(e.FileName); + /// if (e.IsDirectory) continue; + /// string outputPath = Path.Combine(_extractDir, e.FileName); + /// using (var output = File.Open(outputPath, FileMode.Create, FileAccess.ReadWrite)) + /// { + /// while ((n= input.Read(buffer,0,buffer.Length)) > 0) + /// { + /// output.Write(buffer,0,n); + /// } + /// } + /// } + /// } + /// } + /// + /// + /// + public String Password + { + set + { + if (_closed) + { + _exceptionPending = true; + throw new System.InvalidOperationException("The stream has been closed."); + } + _Password = value; + } + } + + + private void SetupStream() + { + // Seek to the correct posn in the file, and open a + // stream that can be read. + _crcStream= _currentEntry.InternalOpenReader(_Password); + _LeftToRead = _crcStream.Length; + _needSetup = false; + } + + + + internal Stream ReadStream + { + get + { + return _inputStream; + } + } + + + /// + /// Read the data from the stream into the buffer. + /// + /// + /// + /// + /// The data for the zipentry will be decrypted and uncompressed, as + /// necessary, before being copied into the buffer. + /// + /// + /// + /// You must set the property before calling + /// Read() the first time for an encrypted entry. To determine if an + /// entry is encrypted and requires a password, check the ZipEntry.Encryption property. + /// + /// + /// + /// The buffer to hold the data read from the stream. + /// the offset within the buffer to copy the first byte read. + /// the number of bytes to read. + /// the number of bytes read, after decryption and decompression. + public override int Read(byte[] buffer, int offset, int count) + { + if (_closed) + { + _exceptionPending = true; + throw new System.InvalidOperationException("The stream has been closed."); + } + + if (_needSetup) + SetupStream(); + + if (_LeftToRead == 0) return 0; + + int len = (_LeftToRead > count) ? count : (int)_LeftToRead; + int n = _crcStream.Read(buffer, offset, len); + + _LeftToRead -= n; + + if (_LeftToRead == 0) + { + int CrcResult = _crcStream.Crc; + _currentEntry.VerifyCrcAfterExtract(CrcResult); + _inputStream.Seek(_endOfEntry, SeekOrigin.Begin); + // workitem 10178 + Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(_inputStream); + } + + return n; + } + + + + /// + /// Read the next entry from the zip file. + /// + /// + /// + /// + /// Call this method just before calling , + /// to position the pointer in the zip file to the next entry that can be + /// read. Subsequent calls to Read(), will decrypt and decompress the + /// data in the zip file, until Read() returns 0. + /// + /// + /// + /// Each time you call GetNextEntry(), the pointer in the wrapped + /// stream is moved to the next entry in the zip file. If you call , and thus re-position the pointer within + /// the file, you will need to call GetNextEntry() again, to insure + /// that the file pointer is positioned at the beginning of a zip entry. + /// + /// + /// + /// This method returns the ZipEntry. Using a stream approach, you will + /// read the raw bytes for an entry in a zip file via calls to Read(). + /// Alternatively, you can extract an entry into a file, or a stream, by + /// calling , or one of its siblings. + /// + /// + /// + /// + /// + /// The ZipEntry read. Returns null (or Nothing in VB) if there are no more + /// entries in the zip file. + /// + /// + public ZipEntry GetNextEntry() + { + if (_findRequired) + { + // find the next signature + long d = SharedUtilities.FindSignature(_inputStream, ZipConstants.ZipEntrySignature); + if (d == -1) return null; + // back up 4 bytes: ReadEntry assumes the file pointer is positioned before the entry signature + _inputStream.Seek(-4, SeekOrigin.Current); + // workitem 10178 + Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(_inputStream); + } + // workitem 10923 + else if (_firstEntry) + { + // we've already read one entry. + // Seek to the end of it. + _inputStream.Seek(_endOfEntry, SeekOrigin.Begin); + Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(_inputStream); + } + + _currentEntry = ZipEntry.ReadEntry(_container, !_firstEntry); + // ReadEntry leaves the file position after all the entry + // data and the optional bit-3 data descriptpr. This is + // where the next entry would normally start. + _endOfEntry = _inputStream.Position; + _firstEntry = true; + _needSetup = true; + _findRequired= false; + return _currentEntry; + } + + + /// + /// Dispose the stream. + /// + /// + /// + /// + /// This method disposes the ZipInputStream. It may also close the + /// underlying stream, depending on which constructor was used. + /// + /// + /// + /// Typically the application will call Dispose() implicitly, via + /// a using statement in C#, or a Using statement in VB. + /// + /// + /// + /// Application code won't call this code directly. This method may + /// be invoked in two distinct scenarios. If disposing == true, the + /// method has been called directly or indirectly by a user's code, + /// for example via the public Dispose() method. In this case, both + /// managed and unmanaged resources can be referenced and disposed. + /// If disposing == false, the method has been called by the runtime + /// from inside the object finalizer and this method should not + /// reference other objects; in that case only unmanaged resources + /// must be referenced or disposed. + /// + /// + /// + /// + /// true if the Dispose method was invoked by user code. + /// + protected override void Dispose(bool disposing) + { + if (_closed) return; + + if (disposing) // not called from finalizer + { + // When ZipInputStream is used within a using clause, and an + // exception is thrown, Close() is invoked. But we don't want to + // try to write anything in that case. Eventually the exception + // will be propagated to the application. + if (_exceptionPending) return; + + if (!_leaveUnderlyingStreamOpen) + { +#if NETCF + _inputStream.Close(); +#else + _inputStream.Dispose(); +#endif + } + } + _closed= true; + } + + + /// + /// Always returns true. + /// + public override bool CanRead { get { return true; }} + + /// + /// Returns the value of CanSeek for the underlying (wrapped) stream. + /// + public override bool CanSeek { get { return _inputStream.CanSeek; } } + + /// + /// Always returns false. + /// + public override bool CanWrite { get { return false; } } + + /// + /// Returns the length of the underlying stream. + /// + public override long Length { get { return _inputStream.Length; }} + + /// + /// Gets or sets the position of the underlying stream. + /// + /// + /// Setting the position is equivalent to calling Seek(value, SeekOrigin.Begin). + /// + public override long Position + { + get { return _inputStream.Position;} + set { Seek(value, SeekOrigin.Begin); } + } + + /// + /// This is a no-op. + /// + public override void Flush() + { + throw new NotSupportedException("Flush"); + } + + + /// + /// This method always throws a NotSupportedException. + /// + /// ignored + /// ignored + /// ignored + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException("Write"); + } + + + /// + /// This method seeks in the underlying stream. + /// + /// + /// + /// + /// Call this method if you want to seek around within the zip file for random access. + /// + /// + /// + /// Applications can intermix calls to Seek() with calls to . After a call to Seek(), + /// GetNextEntry() will get the next ZipEntry that falls after + /// the current position in the input stream. You're on your own for finding + /// out just where to seek in the stream, to get to the various entries. + /// + /// + /// + /// + /// the offset point to seek to + /// the reference point from which to seek + /// The new position + public override long Seek(long offset, SeekOrigin origin) + { + _findRequired= true; + var x = _inputStream.Seek(offset, origin); + // workitem 10178 + Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(_inputStream); + return x; + } + + /// + /// This method always throws a NotSupportedException. + /// + /// ignored + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + + private Stream _inputStream; + private System.Text.Encoding _provisionalAlternateEncoding; + private ZipEntry _currentEntry; + private bool _firstEntry; + private bool _needSetup; + private ZipContainer _container; + private Ionic.Crc.CrcCalculatorStream _crcStream; + private Int64 _LeftToRead; + internal String _Password; + private Int64 _endOfEntry; + private string _name; + + private bool _leaveUnderlyingStreamOpen; + private bool _closed; + private bool _findRequired; + private bool _exceptionPending; + } + + + +} \ No newline at end of file diff --git a/DotNetZip/Zip/ZipOutputStream.cs b/DotNetZip/Zip/ZipOutputStream.cs new file mode 100644 index 0000000..71f7633 --- /dev/null +++ b/DotNetZip/Zip/ZipOutputStream.cs @@ -0,0 +1,1817 @@ +// ZipOutputStream.cs +// +// ------------------------------------------------------------------ +// +// Copyright (c) 2009 Dino Chiesa. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2011-July-28 06:34:30> +// +// ------------------------------------------------------------------ +// +// This module defines the ZipOutputStream class, which is a stream metaphor for +// generating zip files. This class does not depend on Ionic.Zip.ZipFile, but rather +// stands alongside it as an alternative "container" for ZipEntry. It replicates a +// subset of the properties, including these: +// +// - Comment +// - Encryption +// - Password +// - CodecBufferSize +// - CompressionLevel +// - CompressionMethod +// - EnableZip64 (UseZip64WhenSaving) +// - IgnoreCase (!CaseSensitiveRetrieval) +// +// It adds these novel methods: +// +// - PutNextEntry +// +// +// ------------------------------------------------------------------ +// + +using System; +using System.Threading; +using System.Collections.Generic; +using System.IO; +using Ionic.Zip; + +namespace Ionic.Zip +{ + /// + /// Provides a stream metaphor for generating zip files. + /// + /// + /// + /// + /// This class writes zip files, as defined in the specification + /// for zip files described by PKWare. The compression for this + /// implementation is provided by a managed-code version of Zlib, included with + /// DotNetZip in the classes in the Ionic.Zlib namespace. + /// + /// + /// + /// This class provides an alternative programming model to the one enabled by the + /// class. Use this when creating zip files, as an + /// alternative to the class, when you would like to use a + /// Stream type to write the zip file. + /// + /// + /// + /// Both the ZipOutputStream class and the ZipFile class can be used + /// to create zip files. Both of them support many of the common zip features, + /// including Unicode, different compression levels, and ZIP64. They provide + /// very similar performance when creating zip files. + /// + /// + /// + /// The ZipFile class is generally easier to use than + /// ZipOutputStream and should be considered a higher-level interface. For + /// example, when creating a zip file via calls to the PutNextEntry() and + /// Write() methods on the ZipOutputStream class, the caller is + /// responsible for opening the file, reading the bytes from the file, writing + /// those bytes into the ZipOutputStream, setting the attributes on the + /// ZipEntry, and setting the created, last modified, and last accessed + /// timestamps on the zip entry. All of these things are done automatically by a + /// call to ZipFile.AddFile(). + /// For this reason, the ZipOutputStream is generally recommended for use + /// only when your application emits arbitrary data, not necessarily data from a + /// filesystem file, directly into a zip file, and does so using a Stream + /// metaphor. + /// + /// + /// + /// Aside from the differences in programming model, there are other + /// differences in capability between the two classes. + /// + /// + /// + /// + /// ZipFile can be used to read and extract zip files, in addition to + /// creating zip files. ZipOutputStream cannot read zip files. If you want + /// to use a stream to read zip files, check out the class. + /// + /// + /// + /// ZipOutputStream does not support the creation of segmented or spanned + /// zip files. + /// + /// + /// + /// ZipOutputStream cannot produce a self-extracting archive. + /// + /// + /// + /// + /// Be aware that the ZipOutputStream class implements the interface. In order for + /// ZipOutputStream to produce a valid zip file, you use use it within + /// a using clause (Using in VB), or call the Dispose() method + /// explicitly. See the examples for how to employ a using clause. + /// + /// + /// + /// Also, a note regarding compression performance: On the desktop .NET + /// Framework, DotNetZip can use a multi-threaded compression implementation + /// that provides significant speed increases on large files, over 300k or so, + /// at the cost of increased memory use at runtime. (The output of the + /// compression is almost exactly the same size). But, the multi-threaded + /// approach incurs a performance hit on smaller files. There's no way for the + /// ZipOutputStream to know whether parallel compression will be beneficial, + /// because the ZipOutputStream does not know how much data you will write + /// through the stream. You may wish to set the property to zero, if you are compressing + /// large files through ZipOutputStream. This will cause parallel + /// compression to be used, always. + /// + /// + public class ZipOutputStream : Stream + { + /// + /// Create a ZipOutputStream, wrapping an existing stream. + /// + /// + /// + /// + /// The class is generally easier to use when creating + /// zip files. The ZipOutputStream offers a different metaphor for creating a + /// zip file, based on the class. + /// + /// + /// + /// + /// + /// The stream to wrap. It must be writable. This stream will be closed at + /// the time the ZipOutputStream is closed. + /// + /// + /// + /// + /// This example shows how to create a zip file, using the + /// ZipOutputStream class. + /// + /// + /// private void Zipup() + /// { + /// if (filesToZip.Count == 0) + /// { + /// System.Console.WriteLine("Nothing to do."); + /// return; + /// } + /// + /// using (var raw = File.Open(_outputFileName, FileMode.Create, FileAccess.ReadWrite )) + /// { + /// using (var output= new ZipOutputStream(raw)) + /// { + /// output.Password = "VerySecret!"; + /// output.Encryption = EncryptionAlgorithm.WinZipAes256; + /// + /// foreach (string inputFileName in filesToZip) + /// { + /// System.Console.WriteLine("file: {0}", inputFileName); + /// + /// output.PutNextEntry(inputFileName); + /// using (var input = File.Open(inputFileName, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Write )) + /// { + /// byte[] buffer= new byte[2048]; + /// int n; + /// while ((n= input.Read(buffer,0,buffer.Length)) > 0) + /// { + /// output.Write(buffer,0,n); + /// } + /// } + /// } + /// } + /// } + /// } + /// + /// + /// + /// Private Sub Zipup() + /// Dim outputFileName As String = "XmlData.zip" + /// Dim filesToZip As String() = Directory.GetFiles(".", "*.xml") + /// If (filesToZip.Length = 0) Then + /// Console.WriteLine("Nothing to do.") + /// Else + /// Using raw As FileStream = File.Open(outputFileName, FileMode.Create, FileAccess.ReadWrite) + /// Using output As ZipOutputStream = New ZipOutputStream(raw) + /// output.Password = "VerySecret!" + /// output.Encryption = EncryptionAlgorithm.WinZipAes256 + /// Dim inputFileName As String + /// For Each inputFileName In filesToZip + /// Console.WriteLine("file: {0}", inputFileName) + /// output.PutNextEntry(inputFileName) + /// Using input As FileStream = File.Open(inputFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite) + /// Dim n As Integer + /// Dim buffer As Byte() = New Byte(2048) {} + /// Do While (n = input.Read(buffer, 0, buffer.Length) > 0) + /// output.Write(buffer, 0, n) + /// Loop + /// End Using + /// Next + /// End Using + /// End Using + /// End If + /// End Sub + /// + /// + public ZipOutputStream(Stream stream) : this(stream, false) { } + + + /// + /// Create a ZipOutputStream that writes to a filesystem file. + /// + /// + /// + /// The class is generally easier to use when creating + /// zip files. The ZipOutputStream offers a different metaphor for creating a + /// zip file, based on the class. + /// + /// + /// + /// The name of the zip file to create. + /// + /// + /// + /// + /// This example shows how to create a zip file, using the + /// ZipOutputStream class. + /// + /// + /// private void Zipup() + /// { + /// if (filesToZip.Count == 0) + /// { + /// System.Console.WriteLine("Nothing to do."); + /// return; + /// } + /// + /// using (var output= new ZipOutputStream(outputFileName)) + /// { + /// output.Password = "VerySecret!"; + /// output.Encryption = EncryptionAlgorithm.WinZipAes256; + /// + /// foreach (string inputFileName in filesToZip) + /// { + /// System.Console.WriteLine("file: {0}", inputFileName); + /// + /// output.PutNextEntry(inputFileName); + /// using (var input = File.Open(inputFileName, FileMode.Open, FileAccess.Read, + /// FileShare.Read | FileShare.Write )) + /// { + /// byte[] buffer= new byte[2048]; + /// int n; + /// while ((n= input.Read(buffer,0,buffer.Length)) > 0) + /// { + /// output.Write(buffer,0,n); + /// } + /// } + /// } + /// } + /// } + /// + /// + /// + /// Private Sub Zipup() + /// Dim outputFileName As String = "XmlData.zip" + /// Dim filesToZip As String() = Directory.GetFiles(".", "*.xml") + /// If (filesToZip.Length = 0) Then + /// Console.WriteLine("Nothing to do.") + /// Else + /// Using output As ZipOutputStream = New ZipOutputStream(outputFileName) + /// output.Password = "VerySecret!" + /// output.Encryption = EncryptionAlgorithm.WinZipAes256 + /// Dim inputFileName As String + /// For Each inputFileName In filesToZip + /// Console.WriteLine("file: {0}", inputFileName) + /// output.PutNextEntry(inputFileName) + /// Using input As FileStream = File.Open(inputFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite) + /// Dim n As Integer + /// Dim buffer As Byte() = New Byte(2048) {} + /// Do While (n = input.Read(buffer, 0, buffer.Length) > 0) + /// output.Write(buffer, 0, n) + /// Loop + /// End Using + /// Next + /// End Using + /// End If + /// End Sub + /// + /// + public ZipOutputStream(String fileName) + { + Stream stream = File.Open(fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.None); + _Init(stream, false, fileName); + } + + + /// + /// Create a ZipOutputStream. + /// + /// + /// + /// See the documentation for the ZipOutputStream(Stream) + /// constructor for an example. + /// + /// + /// + /// The stream to wrap. It must be writable. + /// + /// + /// + /// true if the application would like the stream + /// to remain open after the ZipOutputStream has been closed. + /// + public ZipOutputStream(Stream stream, bool leaveOpen) + { + _Init(stream, leaveOpen, null); + } + + private void _Init(Stream stream, bool leaveOpen, string name) + { + // workitem 9307 + _outputStream = stream.CanRead ? stream : new CountingStream(stream); + CompressionLevel = Ionic.Zlib.CompressionLevel.Default; + CompressionMethod = Ionic.Zip.CompressionMethod.Deflate; + _encryption = EncryptionAlgorithm.None; + _entriesWritten = new Dictionary(StringComparer.Ordinal); + _zip64 = Zip64Option.Never; + _leaveUnderlyingStreamOpen = leaveOpen; + Strategy = Ionic.Zlib.CompressionStrategy.Default; + _name = name ?? "(stream)"; +#if !NETCF + ParallelDeflateThreshold = -1L; +#endif + } + + + /// Provides a string representation of the instance. + /// + /// + /// This can be useful for debugging purposes. + /// + /// + /// a string representation of the instance. + public override String ToString() + { + return String.Format ("ZipOutputStream::{0}(leaveOpen({1})))", _name, _leaveUnderlyingStreamOpen); + } + + + /// + /// Sets the password to be used on the ZipOutputStream instance. + /// + /// + /// + /// + /// + /// When writing a zip archive, this password is applied to the entries, not + /// to the zip archive itself. It applies to any ZipEntry subsequently + /// written to the ZipOutputStream. + /// + /// + /// + /// Using a password does not encrypt or protect the "directory" of the + /// archive - the list of entries contained in the archive. If you set the + /// Password property, the password actually applies to individual + /// entries that are added to the archive, subsequent to the setting of this + /// property. The list of filenames in the archive that is eventually created + /// will appear in clear text, but the contents of the individual files are + /// encrypted. This is how Zip encryption works. + /// + /// + /// + /// If you set this property, and then add a set of entries to the archive via + /// calls to PutNextEntry, then each entry is encrypted with that + /// password. You may also want to change the password between adding + /// different entries. If you set the password, add an entry, then set the + /// password to null (Nothing in VB), and add another entry, the + /// first entry is encrypted and the second is not. + /// + /// + /// + /// When setting the Password, you may also want to explicitly set the property, to specify how to encrypt the entries added + /// to the ZipFile. If you set the Password to a non-null value and do not + /// set , then PKZip 2.0 ("Weak") encryption is used. + /// This encryption is relatively weak but is very interoperable. If + /// you set the password to a null value (Nothing in VB), + /// Encryption is reset to None. + /// + /// + /// + /// Special case: if you wrap a ZipOutputStream around a non-seekable stream, + /// and use encryption, and emit an entry of zero bytes, the Close() or + /// PutNextEntry() following the entry will throw an exception. + /// + /// + /// + public String Password + { + set + { + if (_disposed) + { + _exceptionPending = true; + throw new System.InvalidOperationException("The stream has been closed."); + } + + _password = value; + if (_password == null) + { + _encryption = EncryptionAlgorithm.None; + } + else if (_encryption == EncryptionAlgorithm.None) + { + _encryption = EncryptionAlgorithm.PkzipWeak; + } + } + } + + + /// + /// The Encryption to use for entries added to the ZipOutputStream. + /// + /// + /// + /// + /// The specified Encryption is applied to the entries subsequently + /// written to the ZipOutputStream instance. + /// + /// + /// + /// If you set this to something other than + /// EncryptionAlgorithm.None, you will also need to set the + /// to a non-null, non-empty value in + /// order to actually get encryption on the entry. + /// + /// + /// + /// + /// ZipOutputStream.Password + /// ZipEntry.Encryption + public EncryptionAlgorithm Encryption + { + get + { + return _encryption; + } + set + { + if (_disposed) + { + _exceptionPending = true; + throw new System.InvalidOperationException("The stream has been closed."); + } + if (value == EncryptionAlgorithm.Unsupported) + { + _exceptionPending = true; + throw new InvalidOperationException("You may not set Encryption to that value."); + } + _encryption = value; + } + } + + + /// + /// Size of the work buffer to use for the ZLIB codec during compression. + /// + /// + /// + /// Setting this may affect performance. For larger files, setting this to a + /// larger size may improve performance, but I'm not sure. Sorry, I don't + /// currently have good recommendations on how to set it. You can test it if + /// you like. + /// + public int CodecBufferSize + { + get; + set; + } + + + /// + /// The compression strategy to use for all entries. + /// + /// + /// + /// Set the Strategy used by the ZLIB-compatible compressor, when compressing + /// data for the entries in the zip archive. Different compression strategies + /// work better on different sorts of data. The strategy parameter can affect + /// the compression ratio and the speed of compression but not the correctness + /// of the compresssion. For more information see . + /// + public Ionic.Zlib.CompressionStrategy Strategy + { + get; + set; + } + + + /// + /// The type of timestamp attached to the ZipEntry. + /// + /// + /// + /// Set this in order to specify the kind of timestamp that should be emitted + /// into the zip file for each entry. + /// + public ZipEntryTimestamp Timestamp + { + get + { + return _timestamp; + } + set + { + if (_disposed) + { + _exceptionPending = true; + throw new System.InvalidOperationException("The stream has been closed."); + } + _timestamp = value; + } + } + + + /// + /// Sets the compression level to be used for entries subsequently added to + /// the zip archive. + /// + /// + /// + /// + /// Varying the compression level used on entries can affect the + /// size-vs-speed tradeoff when compression and decompressing data streams + /// or files. + /// + /// + /// + /// As with some other properties on the ZipOutputStream class, like , and , + /// setting this property on a ZipOutputStream + /// instance will cause the specified CompressionLevel to be used on all + /// items that are subsequently added to the + /// ZipOutputStream instance. + /// + /// + /// + /// If you do not set this property, the default compression level is used, + /// which normally gives a good balance of compression efficiency and + /// compression speed. In some tests, using BestCompression can + /// double the time it takes to compress, while delivering just a small + /// increase in compression efficiency. This behavior will vary with the + /// type of data you compress. If you are in doubt, just leave this setting + /// alone, and accept the default. + /// + /// + public Ionic.Zlib.CompressionLevel CompressionLevel + { + get; + set; + } + + /// + /// The compression method used on each entry added to the ZipOutputStream. + /// + public Ionic.Zip.CompressionMethod CompressionMethod + { + get; + set; + } + + + /// + /// A comment attached to the zip archive. + /// + /// + /// + /// + /// + /// The application sets this property to specify a comment to be embedded + /// into the generated zip archive. + /// + /// + /// + /// According to PKWARE's + /// zip specification, the comment is not encrypted, even if there is a + /// password set on the zip file. + /// + /// + /// + /// The specification does not describe how to indicate the encoding used + /// on a comment string. Many "compliant" zip tools and libraries use + /// IBM437 as the code page for comments; DotNetZip, too, follows that + /// practice. On the other hand, there are situations where you want a + /// Comment to be encoded with something else, for example using code page + /// 950 "Big-5 Chinese". To fill that need, DotNetZip will encode the + /// comment following the same procedure it follows for encoding + /// filenames: (a) if is + /// Never, it uses the default encoding (IBM437). (b) if is Always, it always uses the + /// alternate encoding (). (c) if is AsNecessary, it uses the + /// alternate encoding only if the default encoding is not sufficient for + /// encoding the comment - in other words if decoding the result does not + /// produce the original string. This decision is taken at the time of + /// the call to ZipFile.Save(). + /// + /// + /// + public string Comment + { + get { return _comment; } + set + { + if (_disposed) + { + _exceptionPending = true; + throw new System.InvalidOperationException("The stream has been closed."); + } + _comment = value; + } + } + + + + /// + /// Specify whether to use ZIP64 extensions when saving a zip archive. + /// + /// + /// + /// + /// The default value for the property is . is + /// safest, in the sense that you will not get an Exception if a + /// pre-ZIP64 limit is exceeded. + /// + /// + /// + /// You must set this property before calling Write(). + /// + /// + /// + public Zip64Option EnableZip64 + { + get + { + return _zip64; + } + set + { + if (_disposed) + { + _exceptionPending = true; + throw new System.InvalidOperationException("The stream has been closed."); + } + _zip64 = value; + } + } + + + /// + /// Indicates whether ZIP64 extensions were used when saving the zip archive. + /// + /// + /// + /// The value is defined only after the ZipOutputStream has been closed. + /// + public bool OutputUsedZip64 + { + get + { + return _anyEntriesUsedZip64 || _directoryNeededZip64; + } + } + + + /// + /// Whether the ZipOutputStream should use case-insensitive comparisons when + /// checking for uniqueness of zip entries. + /// + /// + /// + /// + /// Though the zip specification doesn't prohibit zipfiles with duplicate + /// entries, Sane zip files have no duplicates, and the DotNetZip library + /// cannot create zip files with duplicate entries. If an application attempts + /// to call with a name that duplicates one + /// already used within the archive, the library will throw an Exception. + /// + /// + /// This property allows the application to specify whether the + /// ZipOutputStream instance considers ordinal case when checking for + /// uniqueness of zip entries. + /// + /// + public bool IgnoreCase + { + get + { + return !_DontIgnoreCase; + } + + set + { + _DontIgnoreCase = !value; + } + + } + + + /// + /// Indicates whether to encode entry filenames and entry comments using + /// Unicode (UTF-8). + /// + /// + /// + /// + /// The + /// PKWare zip specification provides for encoding file names and file + /// comments in either the IBM437 code page, or in UTF-8. This flag selects + /// the encoding according to that specification. By default, this flag is + /// false, and filenames and comments are encoded into the zip file in the + /// IBM437 codepage. Setting this flag to true will specify that filenames + /// and comments that cannot be encoded with IBM437 will be encoded with + /// UTF-8. + /// + /// + /// + /// Zip files created with strict adherence to the PKWare specification with + /// respect to UTF-8 encoding can contain entries with filenames containing + /// any combination of Unicode characters, including the full range of + /// characters from Chinese, Latin, Hebrew, Greek, Cyrillic, and many other + /// alphabets. However, because at this time, the UTF-8 portion of the PKWare + /// specification is not broadly supported by other zip libraries and + /// utilities, such zip files may not be readable by your favorite zip tool or + /// archiver. In other words, interoperability will decrease if you set this + /// flag to true. + /// + /// + /// + /// In particular, Zip files created with strict adherence to the PKWare + /// specification with respect to UTF-8 encoding will not work well with + /// Explorer in Windows XP or Windows Vista, because Windows compressed + /// folders, as far as I know, do not support UTF-8 in zip files. Vista can + /// read the zip files, but shows the filenames incorrectly. Unpacking from + /// Windows Vista Explorer will result in filenames that have rubbish + /// characters in place of the high-order UTF-8 bytes. + /// + /// + /// + /// Also, zip files that use UTF-8 encoding will not work well with Java + /// applications that use the java.util.zip classes, as of v5.0 of the Java + /// runtime. The Java runtime does not correctly implement the PKWare + /// specification in this regard. + /// + /// + /// + /// As a result, we have the unfortunate situation that "correct" behavior by + /// the DotNetZip library with regard to Unicode encoding of filenames during + /// zip creation will result in zip files that are readable by strictly + /// compliant and current tools (for example the most recent release of the + /// commercial WinZip tool); but these zip files will not be readable by + /// various other tools or libraries, including Windows Explorer. + /// + /// + /// + /// The DotNetZip library can read and write zip files with UTF8-encoded + /// entries, according to the PKware spec. If you use DotNetZip for both + /// creating and reading the zip file, and you use UTF-8, there will be no + /// loss of information in the filenames. For example, using a self-extractor + /// created by this library will allow you to unpack files correctly with no + /// loss of information in the filenames. + /// + /// + /// + /// If you do not set this flag, it will remain false. If this flag is false, + /// the ZipOutputStream will encode all filenames and comments using + /// the IBM437 codepage. This can cause "loss of information" on some + /// filenames, but the resulting zipfile will be more interoperable with other + /// utilities. As an example of the loss of information, diacritics can be + /// lost. The o-tilde character will be down-coded to plain o. The c with a + /// cedilla (Unicode 0xE7) used in Portugese will be downcoded to a c. + /// Likewise, the O-stroke character (Unicode 248), used in Danish and + /// Norwegian, will be down-coded to plain o. Chinese characters cannot be + /// represented in codepage IBM437; when using the default encoding, Chinese + /// characters in filenames will be represented as ?. These are all examples + /// of "information loss". + /// + /// + /// + /// The loss of information associated to the use of the IBM437 encoding is + /// inconvenient, and can also lead to runtime errors. For example, using + /// IBM437, any sequence of 4 Chinese characters will be encoded as ????. If + /// your application creates a ZipOutputStream, does not set the + /// encoding, then adds two files, each with names of four Chinese characters + /// each, this will result in a duplicate filename exception. In the case + /// where you add a single file with a name containing four Chinese + /// characters, the zipfile will save properly, but extracting that file + /// later, with any zip tool, will result in an error, because the question + /// mark is not legal for use within filenames on Windows. These are just a + /// few examples of the problems associated to loss of information. + /// + /// + /// + /// This flag is independent of the encoding of the content within the entries + /// in the zip file. Think of the zip file as a container - it supports an + /// encoding. Within the container are other "containers" - the file entries + /// themselves. The encoding within those entries is independent of the + /// encoding of the zip archive container for those entries. + /// + /// + /// + /// Rather than specify the encoding in a binary fashion using this flag, an + /// application can specify an arbitrary encoding via the property. Setting the encoding + /// explicitly when creating zip archives will result in non-compliant zip + /// files that, curiously, are fairly interoperable. The challenge is, the + /// PKWare specification does not provide for a way to specify that an entry + /// in a zip archive uses a code page that is neither IBM437 nor UTF-8. + /// Therefore if you set the encoding explicitly when creating a zip archive, + /// you must take care upon reading the zip archive to use the same code page. + /// If you get it wrong, the behavior is undefined and may result in incorrect + /// filenames, exceptions, stomach upset, hair loss, and acne. + /// + /// + /// + [Obsolete("Beginning with v1.9.1.6 of DotNetZip, this property is obsolete. It will be removed in a future version of the library. Use AlternateEncoding and AlternateEncodingUsage instead.")] + public bool UseUnicodeAsNecessary + { + get + { + return (_alternateEncoding == System.Text.Encoding.UTF8) && + (AlternateEncodingUsage == ZipOption.AsNecessary); + } + set + { + if (value) + { + _alternateEncoding = System.Text.Encoding.UTF8; + _alternateEncodingUsage = ZipOption.AsNecessary; + + } + else + { + _alternateEncoding = Ionic.Zip.ZipOutputStream.DefaultEncoding; + _alternateEncodingUsage = ZipOption.Never; + } + } + } + + + /// + /// The text encoding to use when emitting entries into the zip archive, for + /// those entries whose filenames or comments cannot be encoded with the + /// default (IBM437) encoding. + /// + /// + /// + /// + /// In its + /// zip specification, PKWare describes two options for encoding + /// filenames and comments: using IBM437 or UTF-8. But, some archiving tools + /// or libraries do not follow the specification, and instead encode + /// characters using the system default code page. For example, WinRAR when + /// run on a machine in Shanghai may encode filenames with the Big-5 Chinese + /// (950) code page. This behavior is contrary to the Zip specification, but + /// it occurs anyway. + /// + /// + /// + /// When using DotNetZip to write zip archives that will be read by one of + /// these other archivers, set this property to specify the code page to use + /// when encoding the and for each ZipEntry in the zip file, for + /// values that cannot be encoded with the default codepage for zip files, + /// IBM437. This is why this property is "provisional". In all cases, IBM437 + /// is used where possible, in other words, where no loss of data would + /// result. It is possible, therefore, to have a given entry with a + /// Comment encoded in IBM437 and a FileName encoded with the + /// specified "provisional" codepage. + /// + /// + /// + /// Be aware that a zip file created after you've explicitly set the + /// ProvisionalAlternateEncoding property to a value other than + /// IBM437 may not be compliant to the PKWare specification, and may not be + /// readable by compliant archivers. On the other hand, many (most?) + /// archivers are non-compliant and can read zip files created in arbitrary + /// code pages. The trick is to use or specify the proper codepage when + /// reading the zip. + /// + /// + /// + /// When creating a zip archive using this library, it is possible to change + /// the value of ProvisionalAlternateEncoding between each entry you + /// add, and between adding entries and the call to Close(). Don't do + /// this. It will likely result in a zipfile that is not readable. For best + /// interoperability, either leave ProvisionalAlternateEncoding + /// alone, or specify it only once, before adding any entries to the + /// ZipOutputStream instance. There is one exception to this + /// recommendation, described later. + /// + /// + /// + /// When using an arbitrary, non-UTF8 code page for encoding, there is no + /// standard way for the creator application - whether DotNetZip, WinZip, + /// WinRar, or something else - to formally specify in the zip file which + /// codepage has been used for the entries. As a result, readers of zip files + /// are not able to inspect the zip file and determine the codepage that was + /// used for the entries contained within it. It is left to the application + /// or user to determine the necessary codepage when reading zip files encoded + /// this way. If you use an incorrect codepage when reading a zipfile, you + /// will get entries with filenames that are incorrect, and the incorrect + /// filenames may even contain characters that are not legal for use within + /// filenames in Windows. Extracting entries with illegal characters in the + /// filenames will lead to exceptions. It's too bad, but this is just the way + /// things are with code pages in zip files. Caveat Emptor. + /// + /// + /// + /// One possible approach for specifying the code page for a given zip file is + /// to describe the code page in a human-readable form in the Zip comment. For + /// example, the comment may read "Entries in this archive are encoded in the + /// Big5 code page". For maximum interoperability, the zip comment in this + /// case should be encoded in the default, IBM437 code page. In this case, + /// the zip comment is encoded using a different page than the filenames. To + /// do this, Specify ProvisionalAlternateEncoding to your desired + /// region-specific code page, once before adding any entries, and then set + /// the property and reset + /// ProvisionalAlternateEncoding to IBM437 before calling Close(). + /// + /// + [Obsolete("use AlternateEncoding and AlternateEncodingUsage instead.")] + public System.Text.Encoding ProvisionalAlternateEncoding + { + get + { + if (_alternateEncodingUsage == ZipOption.AsNecessary) + return _alternateEncoding; + return null; + } + set + { + _alternateEncoding = value; + _alternateEncodingUsage = ZipOption.AsNecessary; + } + } + + /// + /// A Text Encoding to use when encoding the filenames and comments for + /// all the ZipEntry items, during a ZipFile.Save() operation. + /// + /// + /// + /// Whether the encoding specified here is used during the save depends + /// on . + /// + /// + public System.Text.Encoding AlternateEncoding + { + get + { + return _alternateEncoding; + } + set + { + _alternateEncoding = value; + } + } + + /// + /// A flag that tells if and when this instance should apply + /// AlternateEncoding to encode the filenames and comments associated to + /// of ZipEntry objects contained within this instance. + /// + public ZipOption AlternateEncodingUsage + { + get + { + return _alternateEncodingUsage; + } + set + { + _alternateEncodingUsage = value; + } + } + + /// + /// The default text encoding used in zip archives. It is numeric 437, also + /// known as IBM437. + /// + /// + public static System.Text.Encoding DefaultEncoding + { + get + { + return System.Text.Encoding.GetEncoding("IBM437"); + } + } + + +#if !NETCF + /// + /// The size threshold for an entry, above which a parallel deflate is used. + /// + /// + /// + /// + /// + /// DotNetZip will use multiple threads to compress any ZipEntry, when + /// the CompressionMethod is Deflate, and if the entry is + /// larger than the given size. Zero means "always use parallel + /// deflate", while -1 means "never use parallel deflate". + /// + /// + /// + /// If the entry size cannot be known before compression, as with any entry + /// added via a ZipOutputStream, then Parallel deflate will never be + /// performed, unless the value of this property is zero. + /// + /// + /// + /// A parallel deflate operations will speed up the compression of + /// large files, on computers with multiple CPUs or multiple CPU + /// cores. For files above 1mb, on a dual core or dual-cpu (2p) + /// machine, the time required to compress the file can be 70% of the + /// single-threaded deflate. For very large files on 4p machines the + /// compression can be done in 30% of the normal time. The downside + /// is that parallel deflate consumes extra memory during the deflate, + /// and the deflation is slightly less effective. + /// + /// + /// + /// Parallel deflate tends to not be as effective as single-threaded deflate + /// because the original data stream is split into multiple independent + /// buffers, each of which is compressed in parallel. But because they are + /// treated independently, there is no opportunity to share compression + /// dictionaries, and additional framing bytes must be added to the output + /// stream. For that reason, a deflated stream may be slightly larger when + /// compressed using parallel deflate, as compared to a traditional + /// single-threaded deflate. For files of about 512k, the increase over the + /// normal deflate is as much as 5% of the total compressed size. For larger + /// files, the difference can be as small as 0.1%. + /// + /// + /// + /// Multi-threaded compression does not give as much an advantage when using + /// Encryption. This is primarily because encryption tends to slow down + /// the entire pipeline. Also, multi-threaded compression gives less of an + /// advantage when using lower compression levels, for example . You may have to perform + /// some tests to determine the best approach for your situation. + /// + /// + /// + /// The default value for this property is -1, which means parallel + /// compression will not be performed unless you set it to zero. + /// + /// + /// + public long ParallelDeflateThreshold + { + set + { + if ((value != 0) && (value != -1) && (value < 64 * 1024)) + throw new ArgumentOutOfRangeException("value must be greater than 64k, or 0, or -1"); + _ParallelDeflateThreshold = value; + } + get + { + return _ParallelDeflateThreshold; + } + } + + + /// + /// The maximum number of buffer pairs to use when performing + /// parallel compression. + /// + /// + /// + /// + /// This property sets an upper limit on the number of memory + /// buffer pairs to create when performing parallel + /// compression. The implementation of the parallel + /// compression stream allocates multiple buffers to + /// facilitate parallel compression. As each buffer fills up, + /// the stream uses + /// ThreadPool.QueueUserWorkItem() to compress those + /// buffers in a background threadpool thread. After a buffer + /// is compressed, it is re-ordered and written to the output + /// stream. + /// + /// + /// + /// A higher number of buffer pairs enables a higher degree of + /// parallelism, which tends to increase the speed of compression on + /// multi-cpu computers. On the other hand, a higher number of buffer + /// pairs also implies a larger memory consumption, more active worker + /// threads, and a higher cpu utilization for any compression. This + /// property enables the application to limit its memory consumption and + /// CPU utilization behavior depending on requirements. + /// + /// + /// + /// For each compression "task" that occurs in parallel, there are 2 + /// buffers allocated: one for input and one for output. This property + /// sets a limit for the number of pairs. The total amount of storage + /// space allocated for buffering will then be (N*S*2), where N is the + /// number of buffer pairs, S is the size of each buffer (). By default, DotNetZip allocates 4 buffer + /// pairs per CPU core, so if your machine has 4 cores, and you retain + /// the default buffer size of 128k, then the + /// ParallelDeflateOutputStream will use 4 * 4 * 2 * 128kb of buffer + /// memory in total, or 4mb, in blocks of 128kb. If you then set this + /// property to 8, then the number will be 8 * 2 * 128kb of buffer + /// memory, or 2mb. + /// + /// + /// + /// CPU utilization will also go up with additional buffers, because a + /// larger number of buffer pairs allows a larger number of background + /// threads to compress in parallel. If you find that parallel + /// compression is consuming too much memory or CPU, you can adjust this + /// value downward. + /// + /// + /// + /// The default value is 16. Different values may deliver better or + /// worse results, depending on your priorities and the dynamic + /// performance characteristics of your storage and compute resources. + /// + /// + /// + /// This property is not the number of buffer pairs to use; it is an + /// upper limit. An illustration: Suppose you have an application that + /// uses the default value of this property (which is 16), and it runs + /// on a machine with 2 CPU cores. In that case, DotNetZip will allocate + /// 4 buffer pairs per CPU core, for a total of 8 pairs. The upper + /// limit specified by this property has no effect. + /// + /// + /// + /// The application can set this value at any time, but it is + /// effective only if set before calling + /// ZipOutputStream.Write() for the first time. + /// + /// + /// + /// + /// + public int ParallelDeflateMaxBufferPairs + { + get + { + return _maxBufferPairs; + } + set + { + if (value < 4) + throw new ArgumentOutOfRangeException("ParallelDeflateMaxBufferPairs", + "Value must be 4 or greater."); + _maxBufferPairs = value; + } + } +#endif + + + private void InsureUniqueEntry(ZipEntry ze1) + { + if (_entriesWritten.ContainsKey(ze1.FileName)) + { + _exceptionPending = true; + throw new ArgumentException(String.Format("The entry '{0}' already exists in the zip archive.", ze1.FileName)); + } + } + + + internal Stream OutputStream + { + get + { + return _outputStream; + } + } + + internal String Name + { + get + { + return _name; + } + } + + /// + /// Returns true if an entry by the given name has already been written + /// to the ZipOutputStream. + /// + /// + /// + /// The name of the entry to scan for. + /// + /// + /// + /// true if an entry by the given name has already been written. + /// + public bool ContainsEntry(string name) + { + return _entriesWritten.ContainsKey(SharedUtilities.NormalizePathForUseInZipFile(name)); + } + + + /// + /// Write the data from the buffer to the stream. + /// + /// + /// + /// As the application writes data into this stream, the data may be + /// compressed and encrypted before being written out to the underlying + /// stream, depending on the settings of the + /// and the properties. + /// + /// + /// The buffer holding data to write to the stream. + /// the offset within that data array to find the first byte to write. + /// the number of bytes to write. + public override void Write(byte[] buffer, int offset, int count) + { + if (_disposed) + { + _exceptionPending = true; + throw new System.InvalidOperationException("The stream has been closed."); + } + + if (buffer==null) + { + _exceptionPending = true; + throw new System.ArgumentNullException("buffer"); + } + + if (_currentEntry == null) + { + _exceptionPending = true; + throw new System.InvalidOperationException("You must call PutNextEntry() before calling Write()."); + } + + if (_currentEntry.IsDirectory) + { + _exceptionPending = true; + throw new System.InvalidOperationException("You cannot Write() data for an entry that is a directory."); + } + + if (_needToWriteEntryHeader) + _InitiateCurrentEntry(false); + + if (count != 0) + _entryOutputStream.Write(buffer, offset, count); + } + + + + /// + /// Specify the name of the next entry that will be written to the zip file. + /// + /// + /// + /// + /// Call this method just before calling , to + /// specify the name of the entry that the next set of bytes written to + /// the ZipOutputStream belongs to. All subsequent calls to Write, + /// until the next call to PutNextEntry, + /// will be inserted into the named entry in the zip file. + /// + /// + /// + /// If the used in PutNextEntry() ends in + /// a slash, then the entry added is marked as a directory. Because directory + /// entries do not contain data, a call to Write(), before an + /// intervening additional call to PutNextEntry(), will throw an + /// exception. + /// + /// + /// + /// If you don't call Write() between two calls to + /// PutNextEntry(), the first entry is inserted into the zip file as a + /// file of zero size. This may be what you want. + /// + /// + /// + /// Because PutNextEntry() closes out the prior entry, if any, this + /// method may throw if there is a problem with the prior entry. + /// + /// + /// + /// This method returns the ZipEntry. You can modify public properties + /// on the ZipEntry, such as , , and so on, until the first call to + /// ZipOutputStream.Write(), or until the next call to + /// PutNextEntry(). If you modify the ZipEntry after + /// having called Write(), you may get a runtime exception, or you may + /// silently get an invalid zip archive. + /// + /// + /// + /// + /// + /// + /// This example shows how to create a zip file, using the + /// ZipOutputStream class. + /// + /// + /// private void Zipup() + /// { + /// using (FileStream fs raw = File.Open(_outputFileName, FileMode.Create, FileAccess.ReadWrite )) + /// { + /// using (var output= new ZipOutputStream(fs)) + /// { + /// output.Password = "VerySecret!"; + /// output.Encryption = EncryptionAlgorithm.WinZipAes256; + /// output.PutNextEntry("entry1.txt"); + /// byte[] buffer= System.Text.Encoding.ASCII.GetBytes("This is the content for entry #1."); + /// output.Write(buffer,0,buffer.Length); + /// output.PutNextEntry("entry2.txt"); // this will be zero length + /// output.PutNextEntry("entry3.txt"); + /// buffer= System.Text.Encoding.ASCII.GetBytes("This is the content for entry #3."); + /// output.Write(buffer,0,buffer.Length); + /// } + /// } + /// } + /// + /// + /// + /// + /// The name of the entry to be added, including any path to be used + /// within the zip file. + /// + /// + /// + /// The ZipEntry created. + /// + /// + public ZipEntry PutNextEntry(String entryName) + { + if (String.IsNullOrEmpty(entryName)) + throw new ArgumentNullException("entryName"); + + if (_disposed) + { + _exceptionPending = true; + throw new System.InvalidOperationException("The stream has been closed."); + } + + _FinishCurrentEntry(); + _currentEntry = ZipEntry.CreateForZipOutputStream(entryName); + _currentEntry._container = new ZipContainer(this); + _currentEntry._BitField |= 0x0008; // workitem 8932 + _currentEntry.SetEntryTimes(DateTime.Now, DateTime.Now, DateTime.Now); + _currentEntry.CompressionLevel = this.CompressionLevel; + _currentEntry.CompressionMethod = this.CompressionMethod; + _currentEntry.Password = _password; // workitem 13909 + _currentEntry.Encryption = this.Encryption; + // workitem 12634 + _currentEntry.AlternateEncoding = this.AlternateEncoding; + _currentEntry.AlternateEncodingUsage = this.AlternateEncodingUsage; + + if (entryName.EndsWith("/")) _currentEntry.MarkAsDirectory(); + + _currentEntry.EmitTimesInWindowsFormatWhenSaving = ((_timestamp & ZipEntryTimestamp.Windows) != 0); + _currentEntry.EmitTimesInUnixFormatWhenSaving = ((_timestamp & ZipEntryTimestamp.Unix) != 0); + InsureUniqueEntry(_currentEntry); + _needToWriteEntryHeader = true; + + return _currentEntry; + } + + + + private void _InitiateCurrentEntry(bool finishing) + { + // If finishing==true, this means we're initiating the entry at the time of + // Close() or PutNextEntry(). If this happens, it means no data was written + // for the entry - Write() was never called. (The usual case us to call + // _InitiateCurrentEntry(bool) from within Write().) If finishing==true, + // the entry could be either a zero-byte file or a directory. + + _entriesWritten.Add(_currentEntry.FileName,_currentEntry); + _entryCount++; // could use _entriesWritten.Count, but I don't want to incur + // the cost. + + if (_entryCount > 65534 && _zip64 == Zip64Option.Never) + { + _exceptionPending = true; + throw new System.InvalidOperationException("Too many entries. Consider setting ZipOutputStream.EnableZip64."); + } + + // Write out the header. + // + // If finishing, and encryption is in use, then we don't want to emit the + // normal encryption header. Signal that with a cycle=99 to turn off + // encryption for zero-byte entries or directories. + // + // If finishing, then we know the stream length is zero. Else, unknown + // stream length. Passing stream length == 0 allows an optimization so as + // not to setup an encryption or deflation stream, when stream length is + // zero. + + _currentEntry.WriteHeader(_outputStream, finishing ? 99 : 0); + _currentEntry.StoreRelativeOffset(); + + if (!_currentEntry.IsDirectory) + { + _currentEntry.WriteSecurityMetadata(_outputStream); + _currentEntry.PrepOutputStream(_outputStream, + finishing ? 0 : -1, + out _outputCounter, + out _encryptor, + out _deflater, + out _entryOutputStream); + } + _needToWriteEntryHeader = false; + } + + + + private void _FinishCurrentEntry() + { + if (_currentEntry != null) + { + if (_needToWriteEntryHeader) + _InitiateCurrentEntry(true); // an empty entry - no writes + + _currentEntry.FinishOutputStream(_outputStream, _outputCounter, _encryptor, _deflater, _entryOutputStream); + _currentEntry.PostProcessOutput(_outputStream); + // workitem 12964 + if (_currentEntry.OutputUsedZip64!=null) + _anyEntriesUsedZip64 |= _currentEntry.OutputUsedZip64.Value; + + // reset all the streams + _outputCounter = null; _encryptor = _deflater = null; _entryOutputStream = null; + } + } + + + + /// + /// Dispose the stream + /// + /// + /// + /// + /// This method writes the Zip Central directory, then closes the stream. The + /// application must call Dispose() (or Close) in order to produce a valid zip file. + /// + /// + /// + /// Typically the application will call Dispose() implicitly, via a using + /// statement in C#, or a Using statement in VB. + /// + /// + /// + /// + /// set this to true, always. + protected override void Dispose(bool disposing) + { + if (_disposed) return; + + if (disposing) // not called from finalizer + { + // handle pending exceptions + if (!_exceptionPending) + { + _FinishCurrentEntry(); + _directoryNeededZip64 = ZipOutput.WriteCentralDirectoryStructure(_outputStream, + _entriesWritten.Values, + 1, // _numberOfSegmentsForMostRecentSave, + _zip64, + Comment, + new ZipContainer(this)); + Stream wrappedStream = null; + CountingStream cs = _outputStream as CountingStream; + if (cs != null) + { + wrappedStream = cs.WrappedStream; +#if NETCF + cs.Close(); +#else + cs.Dispose(); +#endif + } + else + { + wrappedStream = _outputStream; + } + + if (!_leaveUnderlyingStreamOpen) + { +#if NETCF + wrappedStream.Close(); +#else + wrappedStream.Dispose(); +#endif + } + _outputStream = null; + } + } + _disposed = true; + } + + + + /// + /// Always returns false. + /// + public override bool CanRead { get { return false; } } + + /// + /// Always returns false. + /// + public override bool CanSeek { get { return false; } } + + /// + /// Always returns true. + /// + public override bool CanWrite { get { return true; } } + + /// + /// Always returns a NotSupportedException. + /// + public override long Length { get { throw new NotSupportedException(); } } + + /// + /// Setting this property always returns a NotSupportedException. Getting it + /// returns the value of the Position on the underlying stream. + /// + public override long Position + { + get { return _outputStream.Position; } + set { throw new NotSupportedException(); } + } + + /// + /// This is a no-op. + /// + public override void Flush() { } + + /// + /// This method always throws a NotSupportedException. + /// + /// ignored + /// ignored + /// ignored + /// nothing + public override int Read(byte[] buffer, int offset, int count) + { + throw new NotSupportedException("Read"); + } + + /// + /// This method always throws a NotSupportedException. + /// + /// ignored + /// ignored + /// nothing + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException("Seek"); + } + + /// + /// This method always throws a NotSupportedException. + /// + /// ignored + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + + private EncryptionAlgorithm _encryption; + private ZipEntryTimestamp _timestamp; + internal String _password; + private String _comment; + private Stream _outputStream; + private ZipEntry _currentEntry; + internal Zip64Option _zip64; + private Dictionary _entriesWritten; + private int _entryCount; + private ZipOption _alternateEncodingUsage = ZipOption.Never; + private System.Text.Encoding _alternateEncoding + = System.Text.Encoding.GetEncoding("IBM437"); // default = IBM437 + + private bool _leaveUnderlyingStreamOpen; + private bool _disposed; + private bool _exceptionPending; // **see note below + private bool _anyEntriesUsedZip64, _directoryNeededZip64; + private CountingStream _outputCounter; + private Stream _encryptor; + private Stream _deflater; + private Ionic.Crc.CrcCalculatorStream _entryOutputStream; + private bool _needToWriteEntryHeader; + private string _name; + private bool _DontIgnoreCase; +#if !NETCF + internal Ionic.Zlib.ParallelDeflateOutputStream ParallelDeflater; + private long _ParallelDeflateThreshold; + private int _maxBufferPairs = 16; +#endif + + // **Note regarding exceptions: + + // When ZipOutputStream is employed within a using clause, which + // is the typical scenario, and an exception is thrown within + // the scope of the using, Close()/Dispose() is invoked + // implicitly before processing the initial exception. In that + // case, _exceptionPending is true, and we don't want to try to + // write anything in the Close/Dispose logic. Doing so can + // cause additional exceptions that mask the original one. So, + // the _exceptionPending flag is used to track that, and to + // allow the original exception to be propagated to the + // application without extra "noise." + + } + + + + internal class ZipContainer + { + private ZipFile _zf; + private ZipOutputStream _zos; + private ZipInputStream _zis; + + public ZipContainer(Object o) + { + _zf = (o as ZipFile); + _zos = (o as ZipOutputStream); + _zis = (o as ZipInputStream); + } + + public ZipFile ZipFile + { + get { return _zf; } + } + + public ZipOutputStream ZipOutputStream + { + get { return _zos; } + } + + public string Name + { + get + { + if (_zf != null) return _zf.Name; + if (_zis != null) throw new NotSupportedException(); + return _zos.Name; + } + } + + public string Password + { + get + { + if (_zf != null) return _zf._Password; + if (_zis != null) return _zis._Password; + return _zos._password; + } + } + + public Zip64Option Zip64 + { + get + { + if (_zf != null) return _zf._zip64; + if (_zis != null) throw new NotSupportedException(); + return _zos._zip64; + } + } + + public int BufferSize + { + get + { + if (_zf != null) return _zf.BufferSize; + if (_zis != null) throw new NotSupportedException(); + return 0; + } + } + +#if !NETCF + public Ionic.Zlib.ParallelDeflateOutputStream ParallelDeflater + { + get + { + if (_zf != null) return _zf.ParallelDeflater; + if (_zis != null) return null; + return _zos.ParallelDeflater; + } + set + { + if (_zf != null) _zf.ParallelDeflater = value; + else if (_zos != null) _zos.ParallelDeflater = value; + } + } + + public long ParallelDeflateThreshold + { + get + { + if (_zf != null) return _zf.ParallelDeflateThreshold; + return _zos.ParallelDeflateThreshold; + } + } + public int ParallelDeflateMaxBufferPairs + { + get + { + if (_zf != null) return _zf.ParallelDeflateMaxBufferPairs; + return _zos.ParallelDeflateMaxBufferPairs; + } + } +#endif + + public int CodecBufferSize + { + get + { + if (_zf != null) return _zf.CodecBufferSize; + if (_zis != null) return _zis.CodecBufferSize; + return _zos.CodecBufferSize; + } + } + + public Ionic.Zlib.CompressionStrategy Strategy + { + get + { + if (_zf != null) return _zf.Strategy; + return _zos.Strategy; + } + } + + public Zip64Option UseZip64WhenSaving + { + get + { + if (_zf != null) return _zf.UseZip64WhenSaving; + return _zos.EnableZip64; + } + } + + public System.Text.Encoding AlternateEncoding + { + get + { + if (_zf != null) return _zf.AlternateEncoding; + if (_zos!=null) return _zos.AlternateEncoding; + return null; + } + } + public System.Text.Encoding DefaultEncoding + { + get + { + if (_zf != null) return ZipFile.DefaultEncoding; + if (_zos!=null) return ZipOutputStream.DefaultEncoding; + return null; + } + } + public ZipOption AlternateEncodingUsage + { + get + { + if (_zf != null) return _zf.AlternateEncodingUsage; + if (_zos!=null) return _zos.AlternateEncodingUsage; + return ZipOption.Never; // n/a + } + } + + public Stream ReadStream + { + get + { + if (_zf != null) return _zf.ReadStream; + return _zis.ReadStream; + } + } + } + +} \ No newline at end of file diff --git a/DotNetZip/Zip/ZipSegmentedStream.cs b/DotNetZip/Zip/ZipSegmentedStream.cs new file mode 100644 index 0000000..7fe4f11 --- /dev/null +++ b/DotNetZip/Zip/ZipSegmentedStream.cs @@ -0,0 +1,571 @@ +// ZipSegmentedStream.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2009-2011 Dino Chiesa. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2011-July-13 22:25:45> +// +// ------------------------------------------------------------------ +// +// This module defines logic for zip streams that span disk files. +// +// ------------------------------------------------------------------ + + +using System; +using System.Collections.Generic; +using System.IO; + +namespace Ionic.Zip +{ + internal class ZipSegmentedStream : System.IO.Stream + { + enum RwMode + { + None = 0, + ReadOnly = 1, + Write = 2, + //Update = 3 + } + + private RwMode rwMode; + private bool _exceptionPending; // **see note below + private string _baseName; + private string _baseDir; + //private bool _isDisposed; + private string _currentName; + private string _currentTempName; + private uint _currentDiskNumber; + private uint _maxDiskNumber; + private int _maxSegmentSize; + private System.IO.Stream _innerStream; + + // **Note regarding exceptions: + // + // When ZipSegmentedStream is employed within a using clause, + // which is the typical scenario, and an exception is thrown + // within the scope of the using, Dispose() is invoked + // implicitly before processing the initial exception. If that + // happens, this class sets _exceptionPending to true, and then + // within the Dispose(bool), takes special action as + // appropriate. Need to be careful: any additional exceptions + // will mask the original one. + + private ZipSegmentedStream() : base() + { + _exceptionPending = false; + } + + public static ZipSegmentedStream ForReading(string name, + uint initialDiskNumber, + uint maxDiskNumber) + { + ZipSegmentedStream zss = new ZipSegmentedStream() + { + rwMode = RwMode.ReadOnly, + CurrentSegment = initialDiskNumber, + _maxDiskNumber = maxDiskNumber, + _baseName = name, + }; + + // Console.WriteLine("ZSS: ForReading ({0})", + // Path.GetFileName(zss.CurrentName)); + + zss._SetReadStream(); + + return zss; + } + + + public static ZipSegmentedStream ForWriting(string name, int maxSegmentSize) + { + ZipSegmentedStream zss = new ZipSegmentedStream() + { + rwMode = RwMode.Write, + CurrentSegment = 0, + _baseName = name, + _maxSegmentSize = maxSegmentSize, + _baseDir = Path.GetDirectoryName(name) + }; + + // workitem 9522 + if (zss._baseDir=="") zss._baseDir="."; + + zss._SetWriteStream(0); + + // Console.WriteLine("ZSS: ForWriting ({0})", + // Path.GetFileName(zss.CurrentName)); + + return zss; + } + + + /// + /// Sort-of like a factory method, ForUpdate is used only when + /// the application needs to update the zip entry metadata for + /// a segmented zip file, when the starting segment is earlier + /// than the ending segment, for a particular entry. + /// + /// + /// + /// The update is always contiguous, never rolls over. As a + /// result, this method doesn't need to return a ZSS; it can + /// simply return a FileStream. That's why it's "sort of" + /// like a Factory method. + /// + /// + /// Caller must Close/Dispose the stream object returned by + /// this method. + /// + /// + public static Stream ForUpdate(string name, uint diskNumber) + { + if (diskNumber >= 99) + throw new ArgumentOutOfRangeException("diskNumber"); + + string fname = + String.Format("{0}.z{1:D2}", + Path.Combine(Path.GetDirectoryName(name), + Path.GetFileNameWithoutExtension(name)), + diskNumber + 1); + + // Console.WriteLine("ZSS: ForUpdate ({0})", + // Path.GetFileName(fname)); + + // This class assumes that the update will not expand the + // size of the segment. Update is used only for an in-place + // update of zip metadata. It never will try to write beyond + // the end of a segment. + + return File.Open(fname, + FileMode.Open, + FileAccess.ReadWrite, + FileShare.None); + } + + public bool ContiguousWrite + { + get; + set; + } + + + public UInt32 CurrentSegment + { + get + { + return _currentDiskNumber; + } + private set + { + _currentDiskNumber = value; + _currentName = null; // it will get updated next time referenced + } + } + + /// + /// Name of the filesystem file corresponding to the current segment. + /// + /// + /// + /// The name is not always the name currently being used in the + /// filesystem. When rwMode is RwMode.Write, the filesystem file has a + /// temporary name until the stream is closed or until the next segment is + /// started. + /// + /// + public String CurrentName + { + get + { + if (_currentName==null) + _currentName = _NameForSegment(CurrentSegment); + + return _currentName; + } + } + + + public String CurrentTempName + { + get + { + return _currentTempName; + } + } + + private string _NameForSegment(uint diskNumber) + { + if (diskNumber >= 99) + { + _exceptionPending = true; + throw new OverflowException("The number of zip segments would exceed 99."); + } + + return String.Format("{0}.z{1:D2}", + Path.Combine(Path.GetDirectoryName(_baseName), + Path.GetFileNameWithoutExtension(_baseName)), + diskNumber + 1); + } + + + // Returns the segment that WILL be current if writing + // a block of the given length. + // This isn't exactly true. It could roll over beyond + // this number. + public UInt32 ComputeSegment(int length) + { + if (_innerStream.Position + length > _maxSegmentSize) + // the block will go AT LEAST into the next segment + return CurrentSegment + 1; + + // it will fit in the current segment + return CurrentSegment; + } + + + public override String ToString() + { + return String.Format("{0}[{1}][{2}], pos=0x{3:X})", + "ZipSegmentedStream", CurrentName, + rwMode.ToString(), + this.Position); + } + + + private void _SetReadStream() + { + if (_innerStream != null) + { +#if NETCF + _innerStream.Close(); +#else + _innerStream.Dispose(); +#endif + } + + if (CurrentSegment + 1 == _maxDiskNumber) + _currentName = _baseName; + + // Console.WriteLine("ZSS: SRS ({0})", + // Path.GetFileName(CurrentName)); + + _innerStream = File.OpenRead(CurrentName); + } + + + /// + /// Read from the stream + /// + /// the buffer to read + /// the offset at which to start + /// the number of bytes to read + /// the number of bytes actually read + public override int Read(byte[] buffer, int offset, int count) + { + if (rwMode != RwMode.ReadOnly) + { + _exceptionPending = true; + throw new InvalidOperationException("Stream Error: Cannot Read."); + } + + int r = _innerStream.Read(buffer, offset, count); + int r1 = r; + + while (r1 != count) + { + if (_innerStream.Position != _innerStream.Length) + { + _exceptionPending = true; + throw new ZipException(String.Format("Read error in file {0}", CurrentName)); + + } + + if (CurrentSegment + 1 == _maxDiskNumber) + return r; // no more to read + + CurrentSegment++; + _SetReadStream(); + offset += r1; + count -= r1; + r1 = _innerStream.Read(buffer, offset, count); + r += r1; + } + return r; + } + + + + private void _SetWriteStream(uint increment) + { + if (_innerStream != null) + { +#if NETCF + _innerStream.Close(); +#else + _innerStream.Dispose(); +#endif + if (File.Exists(CurrentName)) + File.Delete(CurrentName); + File.Move(_currentTempName, CurrentName); + // Console.WriteLine("ZSS: SWS close ({0})", + // Path.GetFileName(CurrentName)); + } + + if (increment > 0) + CurrentSegment += increment; + + SharedUtilities.CreateAndOpenUniqueTempFile(_baseDir, + out _innerStream, + out _currentTempName); + + // Console.WriteLine("ZSS: SWS open ({0})", + // Path.GetFileName(_currentTempName)); + + if (CurrentSegment == 0) + _innerStream.Write(BitConverter.GetBytes(ZipConstants.SplitArchiveSignature), 0, 4); + } + + + /// + /// Write to the stream. + /// + /// the buffer from which to write + /// the offset at which to start writing + /// the number of bytes to write + public override void Write(byte[] buffer, int offset, int count) + { + if (rwMode != RwMode.Write) + { + _exceptionPending = true; + throw new InvalidOperationException("Stream Error: Cannot Write."); + } + + + if (ContiguousWrite) + { + // enough space for a contiguous write? + if (_innerStream.Position + count > _maxSegmentSize) + _SetWriteStream(1); + } + else + { + while (_innerStream.Position + count > _maxSegmentSize) + { + int c = unchecked(_maxSegmentSize - (int)_innerStream.Position); + _innerStream.Write(buffer, offset, c); + _SetWriteStream(1); + count -= c; + offset += c; + } + } + + _innerStream.Write(buffer, offset, count); + } + + + public long TruncateBackward(uint diskNumber, long offset) + { + // Console.WriteLine("***ZSS.Trunc to disk {0}", diskNumber); + // Console.WriteLine("***ZSS.Trunc: current disk {0}", CurrentSegment); + if (diskNumber >= 99) + throw new ArgumentOutOfRangeException("diskNumber"); + + if (rwMode != RwMode.Write) + { + _exceptionPending = true; + throw new ZipException("bad state."); + } + + // Seek back in the segmented stream to a (maybe) prior segment. + + // Check if it is the same segment. If it is, very simple. + if (diskNumber == CurrentSegment) + { + var x =_innerStream.Seek(offset, SeekOrigin.Begin); + // workitem 10178 + Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(_innerStream); + return x; + } + + // Seeking back to a prior segment. + // The current segment and any intervening segments must be removed. + // First, close the current segment, and then remove it. + if (_innerStream != null) + { +#if NETCF + _innerStream.Close(); +#else + _innerStream.Dispose(); +#endif + if (File.Exists(_currentTempName)) + File.Delete(_currentTempName); + } + + // Now, remove intervening segments. + for (uint j= CurrentSegment-1; j > diskNumber; j--) + { + string s = _NameForSegment(j); + // Console.WriteLine("***ZSS.Trunc: removing file {0}", s); + if (File.Exists(s)) + File.Delete(s); + } + + // now, open the desired segment. It must exist. + CurrentSegment = diskNumber; + + // get a new temp file, try 3 times: + for (int i = 0; i < 3; i++) + { + try + { + _currentTempName = SharedUtilities.InternalGetTempFileName(); + // move the .z0x file back to a temp name + File.Move(CurrentName, _currentTempName); + break; // workitem 12403 + } + catch(IOException) + { + if (i == 2) throw; + } + } + + // open it + _innerStream = new FileStream(_currentTempName, FileMode.Open); + + var r = _innerStream.Seek(offset, SeekOrigin.Begin); + + // workitem 10178 + Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(_innerStream); + + return r; + } + + + + public override bool CanRead + { + get + { + return (rwMode == RwMode.ReadOnly && + (_innerStream != null) && + _innerStream.CanRead); + } + } + + + public override bool CanSeek + { + get + { + return (_innerStream != null) && + _innerStream.CanSeek; + } + } + + + public override bool CanWrite + { + get + { + return (rwMode == RwMode.Write) && + (_innerStream != null) && + _innerStream.CanWrite; + } + } + + public override void Flush() + { + _innerStream.Flush(); + } + + public override long Length + { + get + { + return _innerStream.Length; + } + } + + public override long Position + { + get { return _innerStream.Position; } + set { _innerStream.Position = value; } + } + + public override long Seek(long offset, System.IO.SeekOrigin origin) + { + var x = _innerStream.Seek(offset, origin); + // workitem 10178 + Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(_innerStream); + return x; + } + + public override void SetLength(long value) + { + if (rwMode != RwMode.Write) + { + _exceptionPending = true; + throw new InvalidOperationException(); + } + _innerStream.SetLength(value); + } + + + protected override void Dispose(bool disposing) + { + // this gets called by Stream.Close() + + // if (_isDisposed) return; + // _isDisposed = true; + //Console.WriteLine("Dispose (mode={0})\n", rwMode.ToString()); + + try + { + if (_innerStream != null) + { +#if NETCF + _innerStream.Close(); +#else + _innerStream.Dispose(); +#endif + //_innerStream = null; + if (rwMode == RwMode.Write) + { + if (_exceptionPending) + { + // possibly could try to clean up all the + // temp files created so far... + } + else + { + // // move the final temp file to the .zNN name + // if (File.Exists(CurrentName)) + // File.Delete(CurrentName); + // if (File.Exists(_currentTempName)) + // File.Move(_currentTempName, CurrentName); + } + } + } + } + finally + { + base.Dispose(disposing); + } + } + + } + +} \ No newline at end of file diff --git a/DotNetZip/Zip/msbuild.flymake.xml b/DotNetZip/Zip/msbuild.flymake.xml new file mode 100644 index 0000000..10ed060 --- /dev/null +++ b/DotNetZip/Zip/msbuild.flymake.xml @@ -0,0 +1,64 @@ + + + + + + false + true + + .\ + .\ + .\obj\ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DotNetZip/Zip/packages.config b/DotNetZip/Zip/packages.config new file mode 100644 index 0000000..0aee17e --- /dev/null +++ b/DotNetZip/Zip/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/DotNetZip/Zlib/Deflate.cs b/DotNetZip/Zlib/Deflate.cs new file mode 100644 index 0000000..9672250 --- /dev/null +++ b/DotNetZip/Zlib/Deflate.cs @@ -0,0 +1,1879 @@ +// Deflate.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2011-August-03 19:52:15> +// +// ------------------------------------------------------------------ +// +// This module defines logic for handling the Deflate or compression. +// +// This code is based on multiple sources: +// - the original zlib v1.2.3 source, which is Copyright (C) 1995-2005 Jean-loup Gailly. +// - the original jzlib, which is Copyright (c) 2000-2003 ymnk, JCraft,Inc. +// +// However, this code is significantly different from both. +// The object model is not the same, and many of the behaviors are different. +// +// In keeping with the license for these other works, the copyrights for +// jzlib and zlib are here. +// +// ----------------------------------------------------------------------- +// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in +// the documentation and/or other materials provided with the distribution. +// +// 3. The names of the authors may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// ----------------------------------------------------------------------- +// +// This program is based on zlib-1.1.3; credit to authors +// Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) +// and contributors of zlib. +// +// ----------------------------------------------------------------------- + + +using System; + +namespace Ionic.Zlib +{ + + internal enum BlockState + { + NeedMore = 0, // block not completed, need more input or more output + BlockDone, // block flush performed + FinishStarted, // finish started, need only more output at next deflate + FinishDone // finish done, accept no more input or output + } + + internal enum DeflateFlavor + { + Store, + Fast, + Slow + } + + internal sealed class DeflateManager + { + private static readonly int MEM_LEVEL_MAX = 9; + private static readonly int MEM_LEVEL_DEFAULT = 8; + + internal delegate BlockState CompressFunc(FlushType flush); + + internal class Config + { + // Use a faster search when the previous match is longer than this + internal int GoodLength; // reduce lazy search above this match length + + // Attempt to find a better match only when the current match is + // strictly smaller than this value. This mechanism is used only for + // compression levels >= 4. For levels 1,2,3: MaxLazy is actually + // MaxInsertLength. (See DeflateFast) + + internal int MaxLazy; // do not perform lazy search above this match length + + internal int NiceLength; // quit search above this match length + + // To speed up deflation, hash chains are never searched beyond this + // length. A higher limit improves compression ratio but degrades the speed. + + internal int MaxChainLength; + + internal DeflateFlavor Flavor; + + private Config(int goodLength, int maxLazy, int niceLength, int maxChainLength, DeflateFlavor flavor) + { + this.GoodLength = goodLength; + this.MaxLazy = maxLazy; + this.NiceLength = niceLength; + this.MaxChainLength = maxChainLength; + this.Flavor = flavor; + } + + public static Config Lookup(CompressionLevel level) + { + return Table[(int)level]; + } + + + static Config() + { + Table = new Config[] { + new Config(0, 0, 0, 0, DeflateFlavor.Store), + new Config(4, 4, 8, 4, DeflateFlavor.Fast), + new Config(4, 5, 16, 8, DeflateFlavor.Fast), + new Config(4, 6, 32, 32, DeflateFlavor.Fast), + + new Config(4, 4, 16, 16, DeflateFlavor.Slow), + new Config(8, 16, 32, 32, DeflateFlavor.Slow), + new Config(8, 16, 128, 128, DeflateFlavor.Slow), + new Config(8, 32, 128, 256, DeflateFlavor.Slow), + new Config(32, 128, 258, 1024, DeflateFlavor.Slow), + new Config(32, 258, 258, 4096, DeflateFlavor.Slow), + }; + } + + private static readonly Config[] Table; + } + + + private CompressFunc DeflateFunction; + + private static readonly System.String[] _ErrorMessage = new System.String[] + { + "need dictionary", + "stream end", + "", + "file error", + "stream error", + "data error", + "insufficient memory", + "buffer error", + "incompatible version", + "" + }; + + // preset dictionary flag in zlib header + private static readonly int PRESET_DICT = 0x20; + + private static readonly int INIT_STATE = 42; + private static readonly int BUSY_STATE = 113; + private static readonly int FINISH_STATE = 666; + + // The deflate compression method + private static readonly int Z_DEFLATED = 8; + + private static readonly int STORED_BLOCK = 0; + private static readonly int STATIC_TREES = 1; + private static readonly int DYN_TREES = 2; + + // The three kinds of block type + private static readonly int Z_BINARY = 0; + private static readonly int Z_ASCII = 1; + private static readonly int Z_UNKNOWN = 2; + + private static readonly int Buf_size = 8 * 2; + + private static readonly int MIN_MATCH = 3; + private static readonly int MAX_MATCH = 258; + + private static readonly int MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1); + + private static readonly int HEAP_SIZE = (2 * InternalConstants.L_CODES + 1); + + private static readonly int END_BLOCK = 256; + + internal ZlibCodec _codec; // the zlib encoder/decoder + internal int status; // as the name implies + internal byte[] pending; // output still pending - waiting to be compressed + internal int nextPending; // index of next pending byte to output to the stream + internal int pendingCount; // number of bytes in the pending buffer + + internal sbyte data_type; // UNKNOWN, BINARY or ASCII + internal int last_flush; // value of flush param for previous deflate call + + internal int w_size; // LZ77 window size (32K by default) + internal int w_bits; // log2(w_size) (8..16) + internal int w_mask; // w_size - 1 + + //internal byte[] dictionary; + internal byte[] window; + + // Sliding window. Input bytes are read into the second half of the window, + // and move to the first half later to keep a dictionary of at least wSize + // bytes. With this organization, matches are limited to a distance of + // wSize-MAX_MATCH bytes, but this ensures that IO is always + // performed with a length multiple of the block size. + // + // To do: use the user input buffer as sliding window. + + internal int window_size; + // Actual size of window: 2*wSize, except when the user input buffer + // is directly used as sliding window. + + internal short[] prev; + // Link to older string with same hash index. To limit the size of this + // array to 64K, this link is maintained only for the last 32K strings. + // An index in this array is thus a window index modulo 32K. + + internal short[] head; // Heads of the hash chains or NIL. + + internal int ins_h; // hash index of string to be inserted + internal int hash_size; // number of elements in hash table + internal int hash_bits; // log2(hash_size) + internal int hash_mask; // hash_size-1 + + // Number of bits by which ins_h must be shifted at each input + // step. It must be such that after MIN_MATCH steps, the oldest + // byte no longer takes part in the hash key, that is: + // hash_shift * MIN_MATCH >= hash_bits + internal int hash_shift; + + // Window position at the beginning of the current output block. Gets + // negative when the window is moved backwards. + + internal int block_start; + + Config config; + internal int match_length; // length of best match + internal int prev_match; // previous match + internal int match_available; // set if previous match exists + internal int strstart; // start of string to insert into.....???? + internal int match_start; // start of matching string + internal int lookahead; // number of valid bytes ahead in window + + // Length of the best match at previous step. Matches not greater than this + // are discarded. This is used in the lazy match evaluation. + internal int prev_length; + + // Insert new strings in the hash table only if the match length is not + // greater than this length. This saves time but degrades compression. + // max_insert_length is used only for compression levels <= 3. + + internal CompressionLevel compressionLevel; // compression level (1..9) + internal CompressionStrategy compressionStrategy; // favor or force Huffman coding + + + internal short[] dyn_ltree; // literal and length tree + internal short[] dyn_dtree; // distance tree + internal short[] bl_tree; // Huffman tree for bit lengths + + internal Tree treeLiterals = new Tree(); // desc for literal tree + internal Tree treeDistances = new Tree(); // desc for distance tree + internal Tree treeBitLengths = new Tree(); // desc for bit length tree + + // number of codes at each bit length for an optimal tree + internal short[] bl_count = new short[InternalConstants.MAX_BITS + 1]; + + // heap used to build the Huffman trees + internal int[] heap = new int[2 * InternalConstants.L_CODES + 1]; + + internal int heap_len; // number of elements in the heap + internal int heap_max; // element of largest frequency + + // The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + // The same heap array is used to build all trees. + + // Depth of each subtree used as tie breaker for trees of equal frequency + internal sbyte[] depth = new sbyte[2 * InternalConstants.L_CODES + 1]; + + internal int _lengthOffset; // index for literals or lengths + + + // Size of match buffer for literals/lengths. There are 4 reasons for + // limiting lit_bufsize to 64K: + // - frequencies can be kept in 16 bit counters + // - if compression is not successful for the first block, all input + // data is still in the window so we can still emit a stored block even + // when input comes from standard input. (This can also be done for + // all blocks if lit_bufsize is not greater than 32K.) + // - if compression is not successful for a file smaller than 64K, we can + // even emit a stored file instead of a stored block (saving 5 bytes). + // This is applicable only for zip (not gzip or zlib). + // - creating new Huffman trees less frequently may not provide fast + // adaptation to changes in the input data statistics. (Take for + // example a binary file with poorly compressible code followed by + // a highly compressible string table.) Smaller buffer sizes give + // fast adaptation but have of course the overhead of transmitting + // trees more frequently. + + internal int lit_bufsize; + + internal int last_lit; // running index in l_buf + + // Buffer for distances. To simplify the code, d_buf and l_buf have + // the same number of elements. To use different lengths, an extra flag + // array would be necessary. + + internal int _distanceOffset; // index into pending; points to distance data?? + + internal int opt_len; // bit length of current block with optimal trees + internal int static_len; // bit length of current block with static trees + internal int matches; // number of string matches in current block + internal int last_eob_len; // bit length of EOB code for last block + + // Output buffer. bits are inserted starting at the bottom (least + // significant bits). + internal short bi_buf; + + // Number of valid bits in bi_buf. All bits above the last valid bit + // are always zero. + internal int bi_valid; + + + internal DeflateManager() + { + dyn_ltree = new short[HEAP_SIZE * 2]; + dyn_dtree = new short[(2 * InternalConstants.D_CODES + 1) * 2]; // distance tree + bl_tree = new short[(2 * InternalConstants.BL_CODES + 1) * 2]; // Huffman tree for bit lengths + } + + + // lm_init + private void _InitializeLazyMatch() + { + window_size = 2 * w_size; + + // clear the hash - workitem 9063 + Array.Clear(head, 0, hash_size); + //for (int i = 0; i < hash_size; i++) head[i] = 0; + + config = Config.Lookup(compressionLevel); + SetDeflater(); + + strstart = 0; + block_start = 0; + lookahead = 0; + match_length = prev_length = MIN_MATCH - 1; + match_available = 0; + ins_h = 0; + } + + // Initialize the tree data structures for a new zlib stream. + private void _InitializeTreeData() + { + treeLiterals.dyn_tree = dyn_ltree; + treeLiterals.staticTree = StaticTree.Literals; + + treeDistances.dyn_tree = dyn_dtree; + treeDistances.staticTree = StaticTree.Distances; + + treeBitLengths.dyn_tree = bl_tree; + treeBitLengths.staticTree = StaticTree.BitLengths; + + bi_buf = 0; + bi_valid = 0; + last_eob_len = 8; // enough lookahead for inflate + + // Initialize the first block of the first file: + _InitializeBlocks(); + } + + internal void _InitializeBlocks() + { + // Initialize the trees. + for (int i = 0; i < InternalConstants.L_CODES; i++) + dyn_ltree[i * 2] = 0; + for (int i = 0; i < InternalConstants.D_CODES; i++) + dyn_dtree[i * 2] = 0; + for (int i = 0; i < InternalConstants.BL_CODES; i++) + bl_tree[i * 2] = 0; + + dyn_ltree[END_BLOCK * 2] = 1; + opt_len = static_len = 0; + last_lit = matches = 0; + } + + // Restore the heap property by moving down the tree starting at node k, + // exchanging a node with the smallest of its two sons if necessary, stopping + // when the heap property is re-established (each father smaller than its + // two sons). + internal void pqdownheap(short[] tree, int k) + { + int v = heap[k]; + int j = k << 1; // left son of k + while (j <= heap_len) + { + // Set j to the smallest of the two sons: + if (j < heap_len && _IsSmaller(tree, heap[j + 1], heap[j], depth)) + { + j++; + } + // Exit if v is smaller than both sons + if (_IsSmaller(tree, v, heap[j], depth)) + break; + + // Exchange v with the smallest son + heap[k] = heap[j]; k = j; + // And continue down the tree, setting j to the left son of k + j <<= 1; + } + heap[k] = v; + } + + internal static bool _IsSmaller(short[] tree, int n, int m, sbyte[] depth) + { + short tn2 = tree[n * 2]; + short tm2 = tree[m * 2]; + return (tn2 < tm2 || (tn2 == tm2 && depth[n] <= depth[m])); + } + + + // Scan a literal or distance tree to determine the frequencies of the codes + // in the bit length tree. + internal void scan_tree(short[] tree, int max_code) + { + int n; // iterates over all tree elements + int prevlen = -1; // last emitted length + int curlen; // length of current code + int nextlen = (int)tree[0 * 2 + 1]; // length of next code + int count = 0; // repeat count of the current code + int max_count = 7; // max repeat count + int min_count = 4; // min repeat count + + if (nextlen == 0) + { + max_count = 138; min_count = 3; + } + tree[(max_code + 1) * 2 + 1] = (short)0x7fff; // guard //?? + + for (n = 0; n <= max_code; n++) + { + curlen = nextlen; nextlen = (int)tree[(n + 1) * 2 + 1]; + if (++count < max_count && curlen == nextlen) + { + continue; + } + else if (count < min_count) + { + bl_tree[curlen * 2] = (short)(bl_tree[curlen * 2] + count); + } + else if (curlen != 0) + { + if (curlen != prevlen) + bl_tree[curlen * 2]++; + bl_tree[InternalConstants.REP_3_6 * 2]++; + } + else if (count <= 10) + { + bl_tree[InternalConstants.REPZ_3_10 * 2]++; + } + else + { + bl_tree[InternalConstants.REPZ_11_138 * 2]++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) + { + max_count = 138; min_count = 3; + } + else if (curlen == nextlen) + { + max_count = 6; min_count = 3; + } + else + { + max_count = 7; min_count = 4; + } + } + } + + // Construct the Huffman tree for the bit lengths and return the index in + // bl_order of the last bit length code to send. + internal int build_bl_tree() + { + int max_blindex; // index of last bit length code of non zero freq + + // Determine the bit length frequencies for literal and distance trees + scan_tree(dyn_ltree, treeLiterals.max_code); + scan_tree(dyn_dtree, treeDistances.max_code); + + // Build the bit length tree: + treeBitLengths.build_tree(this); + // opt_len now includes the length of the tree representations, except + // the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + + // Determine the number of bit length codes to send. The pkzip format + // requires that at least 4 bit length codes be sent. (appnote.txt says + // 3 but the actual value used is 4.) + for (max_blindex = InternalConstants.BL_CODES - 1; max_blindex >= 3; max_blindex--) + { + if (bl_tree[Tree.bl_order[max_blindex] * 2 + 1] != 0) + break; + } + // Update opt_len to include the bit length tree and counts + opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; + + return max_blindex; + } + + + // Send the header for a block using dynamic Huffman trees: the counts, the + // lengths of the bit length codes, the literal tree and the distance tree. + // IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + internal void send_all_trees(int lcodes, int dcodes, int blcodes) + { + int rank; // index in bl_order + + send_bits(lcodes - 257, 5); // not +255 as stated in appnote.txt + send_bits(dcodes - 1, 5); + send_bits(blcodes - 4, 4); // not -3 as stated in appnote.txt + for (rank = 0; rank < blcodes; rank++) + { + send_bits(bl_tree[Tree.bl_order[rank] * 2 + 1], 3); + } + send_tree(dyn_ltree, lcodes - 1); // literal tree + send_tree(dyn_dtree, dcodes - 1); // distance tree + } + + // Send a literal or distance tree in compressed form, using the codes in + // bl_tree. + internal void send_tree(short[] tree, int max_code) + { + int n; // iterates over all tree elements + int prevlen = -1; // last emitted length + int curlen; // length of current code + int nextlen = tree[0 * 2 + 1]; // length of next code + int count = 0; // repeat count of the current code + int max_count = 7; // max repeat count + int min_count = 4; // min repeat count + + if (nextlen == 0) + { + max_count = 138; min_count = 3; + } + + for (n = 0; n <= max_code; n++) + { + curlen = nextlen; nextlen = tree[(n + 1) * 2 + 1]; + if (++count < max_count && curlen == nextlen) + { + continue; + } + else if (count < min_count) + { + do + { + send_code(curlen, bl_tree); + } + while (--count != 0); + } + else if (curlen != 0) + { + if (curlen != prevlen) + { + send_code(curlen, bl_tree); count--; + } + send_code(InternalConstants.REP_3_6, bl_tree); + send_bits(count - 3, 2); + } + else if (count <= 10) + { + send_code(InternalConstants.REPZ_3_10, bl_tree); + send_bits(count - 3, 3); + } + else + { + send_code(InternalConstants.REPZ_11_138, bl_tree); + send_bits(count - 11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) + { + max_count = 138; min_count = 3; + } + else if (curlen == nextlen) + { + max_count = 6; min_count = 3; + } + else + { + max_count = 7; min_count = 4; + } + } + } + + // Output a block of bytes on the stream. + // IN assertion: there is enough room in pending_buf. + private void put_bytes(byte[] p, int start, int len) + { + Array.Copy(p, start, pending, pendingCount, len); + pendingCount += len; + } + +#if NOTNEEDED + private void put_byte(byte c) + { + pending[pendingCount++] = c; + } + internal void put_short(int b) + { + unchecked + { + pending[pendingCount++] = (byte)b; + pending[pendingCount++] = (byte)(b >> 8); + } + } + internal void putShortMSB(int b) + { + unchecked + { + pending[pendingCount++] = (byte)(b >> 8); + pending[pendingCount++] = (byte)b; + } + } +#endif + + internal void send_code(int c, short[] tree) + { + int c2 = c * 2; + send_bits((tree[c2] & 0xffff), (tree[c2 + 1] & 0xffff)); + } + + internal void send_bits(int value, int length) + { + int len = length; + unchecked + { + if (bi_valid > (int)Buf_size - len) + { + //int val = value; + // bi_buf |= (val << bi_valid); + + bi_buf |= (short)((value << bi_valid) & 0xffff); + //put_short(bi_buf); + pending[pendingCount++] = (byte)bi_buf; + pending[pendingCount++] = (byte)(bi_buf >> 8); + + + bi_buf = (short)((uint)value >> (Buf_size - bi_valid)); + bi_valid += len - Buf_size; + } + else + { + // bi_buf |= (value) << bi_valid; + bi_buf |= (short)((value << bi_valid) & 0xffff); + bi_valid += len; + } + } + } + + // Send one empty static block to give enough lookahead for inflate. + // This takes 10 bits, of which 7 may remain in the bit buffer. + // The current inflate code requires 9 bits of lookahead. If the + // last two codes for the previous block (real code plus EOB) were coded + // on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + // the last real code. In this case we send two empty static blocks instead + // of one. (There are no problems if the previous block is stored or fixed.) + // To simplify the code, we assume the worst case of last real code encoded + // on one bit only. + internal void _tr_align() + { + send_bits(STATIC_TREES << 1, 3); + send_code(END_BLOCK, StaticTree.lengthAndLiteralsTreeCodes); + + bi_flush(); + + // Of the 10 bits for the empty block, we have already sent + // (10 - bi_valid) bits. The lookahead for the last real code (before + // the EOB of the previous block) was thus at least one plus the length + // of the EOB plus what we have just sent of the empty static block. + if (1 + last_eob_len + 10 - bi_valid < 9) + { + send_bits(STATIC_TREES << 1, 3); + send_code(END_BLOCK, StaticTree.lengthAndLiteralsTreeCodes); + bi_flush(); + } + last_eob_len = 7; + } + + + // Save the match info and tally the frequency counts. Return true if + // the current block must be flushed. + internal bool _tr_tally(int dist, int lc) + { + pending[_distanceOffset + last_lit * 2] = unchecked((byte) ( (uint)dist >> 8 ) ); + pending[_distanceOffset + last_lit * 2 + 1] = unchecked((byte)dist); + pending[_lengthOffset + last_lit] = unchecked((byte)lc); + last_lit++; + + if (dist == 0) + { + // lc is the unmatched char + dyn_ltree[lc * 2]++; + } + else + { + matches++; + // Here, lc is the match length - MIN_MATCH + dist--; // dist = match distance - 1 + dyn_ltree[(Tree.LengthCode[lc] + InternalConstants.LITERALS + 1) * 2]++; + dyn_dtree[Tree.DistanceCode(dist) * 2]++; + } + + if ((last_lit & 0x1fff) == 0 && (int)compressionLevel > 2) + { + // Compute an upper bound for the compressed length + int out_length = last_lit << 3; + int in_length = strstart - block_start; + int dcode; + for (dcode = 0; dcode < InternalConstants.D_CODES; dcode++) + { + out_length = (int)(out_length + (int)dyn_dtree[dcode * 2] * (5L + Tree.ExtraDistanceBits[dcode])); + } + out_length >>= 3; + if ((matches < (last_lit / 2)) && out_length < in_length / 2) + return true; + } + + return (last_lit == lit_bufsize - 1) || (last_lit == lit_bufsize); + // dinoch - wraparound? + // We avoid equality with lit_bufsize because of wraparound at 64K + // on 16 bit machines and because stored blocks are restricted to + // 64K-1 bytes. + } + + + + // Send the block data compressed using the given Huffman trees + internal void send_compressed_block(short[] ltree, short[] dtree) + { + int distance; // distance of matched string + int lc; // match length or unmatched char (if dist == 0) + int lx = 0; // running index in l_buf + int code; // the code to send + int extra; // number of extra bits to send + + if (last_lit != 0) + { + do + { + int ix = _distanceOffset + lx * 2; + distance = ((pending[ix] << 8) & 0xff00) | + (pending[ix + 1] & 0xff); + lc = (pending[_lengthOffset + lx]) & 0xff; + lx++; + + if (distance == 0) + { + send_code(lc, ltree); // send a literal byte + } + else + { + // literal or match pair + // Here, lc is the match length - MIN_MATCH + code = Tree.LengthCode[lc]; + + // send the length code + send_code(code + InternalConstants.LITERALS + 1, ltree); + extra = Tree.ExtraLengthBits[code]; + if (extra != 0) + { + // send the extra length bits + lc -= Tree.LengthBase[code]; + send_bits(lc, extra); + } + distance--; // dist is now the match distance - 1 + code = Tree.DistanceCode(distance); + + // send the distance code + send_code(code, dtree); + + extra = Tree.ExtraDistanceBits[code]; + if (extra != 0) + { + // send the extra distance bits + distance -= Tree.DistanceBase[code]; + send_bits(distance, extra); + } + } + + // Check that the overlay between pending and d_buf+l_buf is ok: + } + while (lx < last_lit); + } + + send_code(END_BLOCK, ltree); + last_eob_len = ltree[END_BLOCK * 2 + 1]; + } + + + + // Set the data type to ASCII or BINARY, using a crude approximation: + // binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. + // IN assertion: the fields freq of dyn_ltree are set and the total of all + // frequencies does not exceed 64K (to fit in an int on 16 bit machines). + internal void set_data_type() + { + int n = 0; + int ascii_freq = 0; + int bin_freq = 0; + while (n < 7) + { + bin_freq += dyn_ltree[n * 2]; n++; + } + while (n < 128) + { + ascii_freq += dyn_ltree[n * 2]; n++; + } + while (n < InternalConstants.LITERALS) + { + bin_freq += dyn_ltree[n * 2]; n++; + } + data_type = (sbyte)(bin_freq > (ascii_freq >> 2) ? Z_BINARY : Z_ASCII); + } + + + + // Flush the bit buffer, keeping at most 7 bits in it. + internal void bi_flush() + { + if (bi_valid == 16) + { + pending[pendingCount++] = (byte)bi_buf; + pending[pendingCount++] = (byte)(bi_buf >> 8); + bi_buf = 0; + bi_valid = 0; + } + else if (bi_valid >= 8) + { + //put_byte((byte)bi_buf); + pending[pendingCount++] = (byte)bi_buf; + bi_buf >>= 8; + bi_valid -= 8; + } + } + + // Flush the bit buffer and align the output on a byte boundary + internal void bi_windup() + { + if (bi_valid > 8) + { + pending[pendingCount++] = (byte)bi_buf; + pending[pendingCount++] = (byte)(bi_buf >> 8); + } + else if (bi_valid > 0) + { + //put_byte((byte)bi_buf); + pending[pendingCount++] = (byte)bi_buf; + } + bi_buf = 0; + bi_valid = 0; + } + + // Copy a stored block, storing first the length and its + // one's complement if requested. + internal void copy_block(int buf, int len, bool header) + { + bi_windup(); // align on byte boundary + last_eob_len = 8; // enough lookahead for inflate + + if (header) + unchecked + { + //put_short((short)len); + pending[pendingCount++] = (byte)len; + pending[pendingCount++] = (byte)(len >> 8); + //put_short((short)~len); + pending[pendingCount++] = (byte)~len; + pending[pendingCount++] = (byte)(~len >> 8); + } + + put_bytes(window, buf, len); + } + + internal void flush_block_only(bool eof) + { + _tr_flush_block(block_start >= 0 ? block_start : -1, strstart - block_start, eof); + block_start = strstart; + _codec.flush_pending(); + } + + // Copy without compression as much as possible from the input stream, return + // the current block state. + // This function does not insert new strings in the dictionary since + // uncompressible data is probably not useful. This function is used + // only for the level=0 compression option. + // NOTE: this function should be optimized to avoid extra copying from + // window to pending_buf. + internal BlockState DeflateNone(FlushType flush) + { + // Stored blocks are limited to 0xffff bytes, pending is limited + // to pending_buf_size, and each stored block has a 5 byte header: + + int max_block_size = 0xffff; + int max_start; + + if (max_block_size > pending.Length - 5) + { + max_block_size = pending.Length - 5; + } + + // Copy as much as possible from input to output: + while (true) + { + // Fill the window as much as possible: + if (lookahead <= 1) + { + _fillWindow(); + if (lookahead == 0 && flush == FlushType.None) + return BlockState.NeedMore; + if (lookahead == 0) + break; // flush the current block + } + + strstart += lookahead; + lookahead = 0; + + // Emit a stored block if pending will be full: + max_start = block_start + max_block_size; + if (strstart == 0 || strstart >= max_start) + { + // strstart == 0 is possible when wraparound on 16-bit machine + lookahead = (int)(strstart - max_start); + strstart = (int)max_start; + + flush_block_only(false); + if (_codec.AvailableBytesOut == 0) + return BlockState.NeedMore; + } + + // Flush if we may have to slide, otherwise block_start may become + // negative and the data will be gone: + if (strstart - block_start >= w_size - MIN_LOOKAHEAD) + { + flush_block_only(false); + if (_codec.AvailableBytesOut == 0) + return BlockState.NeedMore; + } + } + + flush_block_only(flush == FlushType.Finish); + if (_codec.AvailableBytesOut == 0) + return (flush == FlushType.Finish) ? BlockState.FinishStarted : BlockState.NeedMore; + + return flush == FlushType.Finish ? BlockState.FinishDone : BlockState.BlockDone; + } + + + // Send a stored block + internal void _tr_stored_block(int buf, int stored_len, bool eof) + { + send_bits((STORED_BLOCK << 1) + (eof ? 1 : 0), 3); // send block type + copy_block(buf, stored_len, true); // with header + } + + // Determine the best encoding for the current block: dynamic trees, static + // trees or store, and output the encoded block to the zip file. + internal void _tr_flush_block(int buf, int stored_len, bool eof) + { + int opt_lenb, static_lenb; // opt_len and static_len in bytes + int max_blindex = 0; // index of last bit length code of non zero freq + + // Build the Huffman trees unless a stored block is forced + if (compressionLevel > 0) + { + // Check if the file is ascii or binary + if (data_type == Z_UNKNOWN) + set_data_type(); + + // Construct the literal and distance trees + treeLiterals.build_tree(this); + + treeDistances.build_tree(this); + + // At this point, opt_len and static_len are the total bit lengths of + // the compressed block data, excluding the tree representations. + + // Build the bit length tree for the above two trees, and get the index + // in bl_order of the last bit length code to send. + max_blindex = build_bl_tree(); + + // Determine the best encoding. Compute first the block length in bytes + opt_lenb = (opt_len + 3 + 7) >> 3; + static_lenb = (static_len + 3 + 7) >> 3; + + if (static_lenb <= opt_lenb) + opt_lenb = static_lenb; + } + else + { + opt_lenb = static_lenb = stored_len + 5; // force a stored block + } + + if (stored_len + 4 <= opt_lenb && buf != -1) + { + // 4: two words for the lengths + // The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + // Otherwise we can't have processed more than WSIZE input bytes since + // the last block flush, because compression would have been + // successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + // transform a block into a stored block. + _tr_stored_block(buf, stored_len, eof); + } + else if (static_lenb == opt_lenb) + { + send_bits((STATIC_TREES << 1) + (eof ? 1 : 0), 3); + send_compressed_block(StaticTree.lengthAndLiteralsTreeCodes, StaticTree.distTreeCodes); + } + else + { + send_bits((DYN_TREES << 1) + (eof ? 1 : 0), 3); + send_all_trees(treeLiterals.max_code + 1, treeDistances.max_code + 1, max_blindex + 1); + send_compressed_block(dyn_ltree, dyn_dtree); + } + + // The above check is made mod 2^32, for files larger than 512 MB + // and uLong implemented on 32 bits. + + _InitializeBlocks(); + + if (eof) + { + bi_windup(); + } + } + + // Fill the window when the lookahead becomes insufficient. + // Updates strstart and lookahead. + // + // IN assertion: lookahead < MIN_LOOKAHEAD + // OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + // At least one byte has been read, or avail_in == 0; reads are + // performed for at least two bytes (required for the zip translate_eol + // option -- not supported here). + private void _fillWindow() + { + int n, m; + int p; + int more; // Amount of free space at the end of the window. + + do + { + more = (window_size - lookahead - strstart); + + // Deal with !@#$% 64K limit: + if (more == 0 && strstart == 0 && lookahead == 0) + { + more = w_size; + } + else if (more == -1) + { + // Very unlikely, but possible on 16 bit machine if strstart == 0 + // and lookahead == 1 (input done one byte at time) + more--; + + // If the window is almost full and there is insufficient lookahead, + // move the upper half to the lower one to make room in the upper half. + } + else if (strstart >= w_size + w_size - MIN_LOOKAHEAD) + { + Array.Copy(window, w_size, window, 0, w_size); + match_start -= w_size; + strstart -= w_size; // we now have strstart >= MAX_DIST + block_start -= w_size; + + // Slide the hash table (could be avoided with 32 bit values + // at the expense of memory usage). We slide even when level == 0 + // to keep the hash table consistent if we switch back to level > 0 + // later. (Using level 0 permanently is not an optimal usage of + // zlib, so we don't care about this pathological case.) + + n = hash_size; + p = n; + do + { + m = (head[--p] & 0xffff); + head[p] = (short)((m >= w_size) ? (m - w_size) : 0); + } + while (--n != 0); + + n = w_size; + p = n; + do + { + m = (prev[--p] & 0xffff); + prev[p] = (short)((m >= w_size) ? (m - w_size) : 0); + // If n is not on any hash chain, prev[n] is garbage but + // its value will never be used. + } + while (--n != 0); + more += w_size; + } + + if (_codec.AvailableBytesIn == 0) + return; + + // If there was no sliding: + // strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + // more == window_size - lookahead - strstart + // => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + // => more >= window_size - 2*WSIZE + 2 + // In the BIG_MEM or MMAP case (not yet supported), + // window_size == input_size + MIN_LOOKAHEAD && + // strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + // Otherwise, window_size == 2*WSIZE so more >= 2. + // If there was sliding, more >= WSIZE. So in all cases, more >= 2. + + n = _codec.read_buf(window, strstart + lookahead, more); + lookahead += n; + + // Initialize the hash value now that we have some input: + if (lookahead >= MIN_MATCH) + { + ins_h = window[strstart] & 0xff; + ins_h = (((ins_h) << hash_shift) ^ (window[strstart + 1] & 0xff)) & hash_mask; + } + // If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + // but this is not important since only literal bytes will be emitted. + } + while (lookahead < MIN_LOOKAHEAD && _codec.AvailableBytesIn != 0); + } + + // Compress as much as possible from the input stream, return the current + // block state. + // This function does not perform lazy evaluation of matches and inserts + // new strings in the dictionary only for unmatched strings or for short + // matches. It is used only for the fast compression options. + internal BlockState DeflateFast(FlushType flush) + { + // short hash_head = 0; // head of the hash chain + int hash_head = 0; // head of the hash chain + bool bflush; // set if current block must be flushed + + while (true) + { + // Make sure that we always have enough lookahead, except + // at the end of the input file. We need MAX_MATCH bytes + // for the next match, plus MIN_MATCH bytes to insert the + // string following the next match. + if (lookahead < MIN_LOOKAHEAD) + { + _fillWindow(); + if (lookahead < MIN_LOOKAHEAD && flush == FlushType.None) + { + return BlockState.NeedMore; + } + if (lookahead == 0) + break; // flush the current block + } + + // Insert the string window[strstart .. strstart+2] in the + // dictionary, and set hash_head to the head of the hash chain: + if (lookahead >= MIN_MATCH) + { + ins_h = (((ins_h) << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask; + + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (head[ins_h] & 0xffff); + prev[strstart & w_mask] = head[ins_h]; + head[ins_h] = unchecked((short)strstart); + } + + // Find the longest match, discarding those <= prev_length. + // At this point we have always match_length < MIN_MATCH + + if (hash_head != 0L && ((strstart - hash_head) & 0xffff) <= w_size - MIN_LOOKAHEAD) + { + // To simplify the code, we prevent matches with the string + // of window index 0 (in particular we have to avoid a match + // of the string with itself at the start of the input file). + if (compressionStrategy != CompressionStrategy.HuffmanOnly) + { + match_length = longest_match(hash_head); + } + // longest_match() sets match_start + } + if (match_length >= MIN_MATCH) + { + // check_match(strstart, match_start, match_length); + + bflush = _tr_tally(strstart - match_start, match_length - MIN_MATCH); + + lookahead -= match_length; + + // Insert new strings in the hash table only if the match length + // is not too large. This saves time but degrades compression. + if (match_length <= config.MaxLazy && lookahead >= MIN_MATCH) + { + match_length--; // string at strstart already in hash table + do + { + strstart++; + + ins_h = ((ins_h << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask; + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (head[ins_h] & 0xffff); + prev[strstart & w_mask] = head[ins_h]; + head[ins_h] = unchecked((short)strstart); + + // strstart never exceeds WSIZE-MAX_MATCH, so there are + // always MIN_MATCH bytes ahead. + } + while (--match_length != 0); + strstart++; + } + else + { + strstart += match_length; + match_length = 0; + ins_h = window[strstart] & 0xff; + + ins_h = (((ins_h) << hash_shift) ^ (window[strstart + 1] & 0xff)) & hash_mask; + // If lookahead < MIN_MATCH, ins_h is garbage, but it does not + // matter since it will be recomputed at next deflate call. + } + } + else + { + // No match, output a literal byte + + bflush = _tr_tally(0, window[strstart] & 0xff); + lookahead--; + strstart++; + } + if (bflush) + { + flush_block_only(false); + if (_codec.AvailableBytesOut == 0) + return BlockState.NeedMore; + } + } + + flush_block_only(flush == FlushType.Finish); + if (_codec.AvailableBytesOut == 0) + { + if (flush == FlushType.Finish) + return BlockState.FinishStarted; + else + return BlockState.NeedMore; + } + return flush == FlushType.Finish ? BlockState.FinishDone : BlockState.BlockDone; + } + + // Same as above, but achieves better compression. We use a lazy + // evaluation for matches: a match is finally adopted only if there is + // no better match at the next window position. + internal BlockState DeflateSlow(FlushType flush) + { + // short hash_head = 0; // head of hash chain + int hash_head = 0; // head of hash chain + bool bflush; // set if current block must be flushed + + // Process the input block. + while (true) + { + // Make sure that we always have enough lookahead, except + // at the end of the input file. We need MAX_MATCH bytes + // for the next match, plus MIN_MATCH bytes to insert the + // string following the next match. + + if (lookahead < MIN_LOOKAHEAD) + { + _fillWindow(); + if (lookahead < MIN_LOOKAHEAD && flush == FlushType.None) + return BlockState.NeedMore; + + if (lookahead == 0) + break; // flush the current block + } + + // Insert the string window[strstart .. strstart+2] in the + // dictionary, and set hash_head to the head of the hash chain: + + if (lookahead >= MIN_MATCH) + { + ins_h = (((ins_h) << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask; + // prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (head[ins_h] & 0xffff); + prev[strstart & w_mask] = head[ins_h]; + head[ins_h] = unchecked((short)strstart); + } + + // Find the longest match, discarding those <= prev_length. + prev_length = match_length; + prev_match = match_start; + match_length = MIN_MATCH - 1; + + if (hash_head != 0 && prev_length < config.MaxLazy && + ((strstart - hash_head) & 0xffff) <= w_size - MIN_LOOKAHEAD) + { + // To simplify the code, we prevent matches with the string + // of window index 0 (in particular we have to avoid a match + // of the string with itself at the start of the input file). + + if (compressionStrategy != CompressionStrategy.HuffmanOnly) + { + match_length = longest_match(hash_head); + } + // longest_match() sets match_start + + if (match_length <= 5 && (compressionStrategy == CompressionStrategy.Filtered || + (match_length == MIN_MATCH && strstart - match_start > 4096))) + { + + // If prev_match is also MIN_MATCH, match_start is garbage + // but we will ignore the current match anyway. + match_length = MIN_MATCH - 1; + } + } + + // If there was a match at the previous step and the current + // match is not better, output the previous match: + if (prev_length >= MIN_MATCH && match_length <= prev_length) + { + int max_insert = strstart + lookahead - MIN_MATCH; + // Do not insert strings in hash table beyond this. + + // check_match(strstart-1, prev_match, prev_length); + + bflush = _tr_tally(strstart - 1 - prev_match, prev_length - MIN_MATCH); + + // Insert in hash table all strings up to the end of the match. + // strstart-1 and strstart are already inserted. If there is not + // enough lookahead, the last two strings are not inserted in + // the hash table. + lookahead -= (prev_length - 1); + prev_length -= 2; + do + { + if (++strstart <= max_insert) + { + ins_h = (((ins_h) << hash_shift) ^ (window[(strstart) + (MIN_MATCH - 1)] & 0xff)) & hash_mask; + //prev[strstart&w_mask]=hash_head=head[ins_h]; + hash_head = (head[ins_h] & 0xffff); + prev[strstart & w_mask] = head[ins_h]; + head[ins_h] = unchecked((short)strstart); + } + } + while (--prev_length != 0); + match_available = 0; + match_length = MIN_MATCH - 1; + strstart++; + + if (bflush) + { + flush_block_only(false); + if (_codec.AvailableBytesOut == 0) + return BlockState.NeedMore; + } + } + else if (match_available != 0) + { + + // If there was no match at the previous position, output a + // single literal. If there was a match but the current match + // is longer, truncate the previous match to a single literal. + + bflush = _tr_tally(0, window[strstart - 1] & 0xff); + + if (bflush) + { + flush_block_only(false); + } + strstart++; + lookahead--; + if (_codec.AvailableBytesOut == 0) + return BlockState.NeedMore; + } + else + { + // There is no previous match to compare with, wait for + // the next step to decide. + + match_available = 1; + strstart++; + lookahead--; + } + } + + if (match_available != 0) + { + bflush = _tr_tally(0, window[strstart - 1] & 0xff); + match_available = 0; + } + flush_block_only(flush == FlushType.Finish); + + if (_codec.AvailableBytesOut == 0) + { + if (flush == FlushType.Finish) + return BlockState.FinishStarted; + else + return BlockState.NeedMore; + } + + return flush == FlushType.Finish ? BlockState.FinishDone : BlockState.BlockDone; + } + + + internal int longest_match(int cur_match) + { + int chain_length = config.MaxChainLength; // max hash chain length + int scan = strstart; // current string + int match; // matched string + int len; // length of current match + int best_len = prev_length; // best match length so far + int limit = strstart > (w_size - MIN_LOOKAHEAD) ? strstart - (w_size - MIN_LOOKAHEAD) : 0; + + int niceLength = config.NiceLength; + + // Stop when cur_match becomes <= limit. To simplify the code, + // we prevent matches with the string of window index 0. + + int wmask = w_mask; + + int strend = strstart + MAX_MATCH; + byte scan_end1 = window[scan + best_len - 1]; + byte scan_end = window[scan + best_len]; + + // The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + // It is easy to get rid of this optimization if necessary. + + // Do not waste too much time if we already have a good match: + if (prev_length >= config.GoodLength) + { + chain_length >>= 2; + } + + // Do not look for matches beyond the end of the input. This is necessary + // to make deflate deterministic. + if (niceLength > lookahead) + niceLength = lookahead; + + do + { + match = cur_match; + + // Skip to next match if the match length cannot increase + // or if the match length is less than 2: + if (window[match + best_len] != scan_end || + window[match + best_len - 1] != scan_end1 || + window[match] != window[scan] || + window[++match] != window[scan + 1]) + continue; + + // The check at best_len-1 can be removed because it will be made + // again later. (This heuristic is not always a win.) + // It is not necessary to compare scan[2] and match[2] since they + // are always equal when the other bytes match, given that + // the hash keys are equal and that HASH_BITS >= 8. + scan += 2; match++; + + // We check for insufficient lookahead only every 8th comparison; + // the 256th check will be made at strstart+258. + do + { + } + while (window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && + window[++scan] == window[++match] && scan < strend); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + + if (len > best_len) + { + match_start = cur_match; + best_len = len; + if (len >= niceLength) + break; + scan_end1 = window[scan + best_len - 1]; + scan_end = window[scan + best_len]; + } + } + while ((cur_match = (prev[cur_match & wmask] & 0xffff)) > limit && --chain_length != 0); + + if (best_len <= lookahead) + return best_len; + return lookahead; + } + + + private bool Rfc1950BytesEmitted = false; + private bool _WantRfc1950HeaderBytes = true; + internal bool WantRfc1950HeaderBytes + { + get { return _WantRfc1950HeaderBytes; } + set { _WantRfc1950HeaderBytes = value; } + } + + + internal int Initialize(ZlibCodec codec, CompressionLevel level) + { + return Initialize(codec, level, ZlibConstants.WindowBitsMax); + } + + internal int Initialize(ZlibCodec codec, CompressionLevel level, int bits) + { + return Initialize(codec, level, bits, MEM_LEVEL_DEFAULT, CompressionStrategy.Default); + } + + internal int Initialize(ZlibCodec codec, CompressionLevel level, int bits, CompressionStrategy compressionStrategy) + { + return Initialize(codec, level, bits, MEM_LEVEL_DEFAULT, compressionStrategy); + } + + internal int Initialize(ZlibCodec codec, CompressionLevel level, int windowBits, int memLevel, CompressionStrategy strategy) + { + _codec = codec; + _codec.Message = null; + + // validation + if (windowBits < 9 || windowBits > 15) + throw new ZlibException("windowBits must be in the range 9..15."); + + if (memLevel < 1 || memLevel > MEM_LEVEL_MAX) + throw new ZlibException(String.Format("memLevel must be in the range 1.. {0}", MEM_LEVEL_MAX)); + + _codec.dstate = this; + + w_bits = windowBits; + w_size = 1 << w_bits; + w_mask = w_size - 1; + + hash_bits = memLevel + 7; + hash_size = 1 << hash_bits; + hash_mask = hash_size - 1; + hash_shift = ((hash_bits + MIN_MATCH - 1) / MIN_MATCH); + + window = new byte[w_size * 2]; + prev = new short[w_size]; + head = new short[hash_size]; + + // for memLevel==8, this will be 16384, 16k + lit_bufsize = 1 << (memLevel + 6); + + // Use a single array as the buffer for data pending compression, + // the output distance codes, and the output length codes (aka tree). + // orig comment: This works just fine since the average + // output size for (length,distance) codes is <= 24 bits. + pending = new byte[lit_bufsize * 4]; + _distanceOffset = lit_bufsize; + _lengthOffset = (1 + 2) * lit_bufsize; + + // So, for memLevel 8, the length of the pending buffer is 65536. 64k. + // The first 16k are pending bytes. + // The middle slice, of 32k, is used for distance codes. + // The final 16k are length codes. + + this.compressionLevel = level; + this.compressionStrategy = strategy; + + Reset(); + return ZlibConstants.Z_OK; + } + + + internal void Reset() + { + _codec.TotalBytesIn = _codec.TotalBytesOut = 0; + _codec.Message = null; + //strm.data_type = Z_UNKNOWN; + + pendingCount = 0; + nextPending = 0; + + Rfc1950BytesEmitted = false; + + status = (WantRfc1950HeaderBytes) ? INIT_STATE : BUSY_STATE; + _codec._Adler32 = Adler.Adler32(0, null, 0, 0); + + last_flush = (int)FlushType.None; + + _InitializeTreeData(); + _InitializeLazyMatch(); + } + + + internal int End() + { + if (status != INIT_STATE && status != BUSY_STATE && status != FINISH_STATE) + { + return ZlibConstants.Z_STREAM_ERROR; + } + // Deallocate in reverse order of allocations: + pending = null; + head = null; + prev = null; + window = null; + // free + // dstate=null; + return status == BUSY_STATE ? ZlibConstants.Z_DATA_ERROR : ZlibConstants.Z_OK; + } + + + private void SetDeflater() + { + switch (config.Flavor) + { + case DeflateFlavor.Store: + DeflateFunction = DeflateNone; + break; + case DeflateFlavor.Fast: + DeflateFunction = DeflateFast; + break; + case DeflateFlavor.Slow: + DeflateFunction = DeflateSlow; + break; + } + } + + + internal int SetParams(CompressionLevel level, CompressionStrategy strategy) + { + int result = ZlibConstants.Z_OK; + + if (compressionLevel != level) + { + Config newConfig = Config.Lookup(level); + + // change in the deflate flavor (Fast vs slow vs none)? + if (newConfig.Flavor != config.Flavor && _codec.TotalBytesIn != 0) + { + // Flush the last buffer: + result = _codec.Deflate(FlushType.Partial); + } + + compressionLevel = level; + config = newConfig; + SetDeflater(); + } + + // no need to flush with change in strategy? Really? + compressionStrategy = strategy; + + return result; + } + + + internal int SetDictionary(byte[] dictionary) + { + int length = dictionary.Length; + int index = 0; + + if (dictionary == null || status != INIT_STATE) + throw new ZlibException("Stream error."); + + _codec._Adler32 = Adler.Adler32(_codec._Adler32, dictionary, 0, dictionary.Length); + + if (length < MIN_MATCH) + return ZlibConstants.Z_OK; + if (length > w_size - MIN_LOOKAHEAD) + { + length = w_size - MIN_LOOKAHEAD; + index = dictionary.Length - length; // use the tail of the dictionary + } + Array.Copy(dictionary, index, window, 0, length); + strstart = length; + block_start = length; + + // Insert all strings in the hash table (except for the last two bytes). + // s->lookahead stays null, so s->ins_h will be recomputed at the next + // call of fill_window. + + ins_h = window[0] & 0xff; + ins_h = (((ins_h) << hash_shift) ^ (window[1] & 0xff)) & hash_mask; + + for (int n = 0; n <= length - MIN_MATCH; n++) + { + ins_h = (((ins_h) << hash_shift) ^ (window[(n) + (MIN_MATCH - 1)] & 0xff)) & hash_mask; + prev[n & w_mask] = head[ins_h]; + head[ins_h] = (short)n; + } + return ZlibConstants.Z_OK; + } + + + + internal int Deflate(FlushType flush) + { + int old_flush; + + if (_codec.OutputBuffer == null || + (_codec.InputBuffer == null && _codec.AvailableBytesIn != 0) || + (status == FINISH_STATE && flush != FlushType.Finish)) + { + _codec.Message = _ErrorMessage[ZlibConstants.Z_NEED_DICT - (ZlibConstants.Z_STREAM_ERROR)]; + throw new ZlibException(String.Format("Something is fishy. [{0}]", _codec.Message)); + } + if (_codec.AvailableBytesOut == 0) + { + _codec.Message = _ErrorMessage[ZlibConstants.Z_NEED_DICT - (ZlibConstants.Z_BUF_ERROR)]; + throw new ZlibException("OutputBuffer is full (AvailableBytesOut == 0)"); + } + + old_flush = last_flush; + last_flush = (int)flush; + + // Write the zlib (rfc1950) header bytes + if (status == INIT_STATE) + { + int header = (Z_DEFLATED + ((w_bits - 8) << 4)) << 8; + int level_flags = (((int)compressionLevel - 1) & 0xff) >> 1; + + if (level_flags > 3) + level_flags = 3; + header |= (level_flags << 6); + if (strstart != 0) + header |= PRESET_DICT; + header += 31 - (header % 31); + + status = BUSY_STATE; + //putShortMSB(header); + unchecked + { + pending[pendingCount++] = (byte)(header >> 8); + pending[pendingCount++] = (byte)header; + } + // Save the adler32 of the preset dictionary: + if (strstart != 0) + { + pending[pendingCount++] = (byte)((_codec._Adler32 & 0xFF000000) >> 24); + pending[pendingCount++] = (byte)((_codec._Adler32 & 0x00FF0000) >> 16); + pending[pendingCount++] = (byte)((_codec._Adler32 & 0x0000FF00) >> 8); + pending[pendingCount++] = (byte)(_codec._Adler32 & 0x000000FF); + } + _codec._Adler32 = Adler.Adler32(0, null, 0, 0); + } + + // Flush as much pending output as possible + if (pendingCount != 0) + { + _codec.flush_pending(); + if (_codec.AvailableBytesOut == 0) + { + //System.out.println(" avail_out==0"); + // Since avail_out is 0, deflate will be called again with + // more output space, but possibly with both pending and + // avail_in equal to zero. There won't be anything to do, + // but this is not an error situation so make sure we + // return OK instead of BUF_ERROR at next call of deflate: + last_flush = -1; + return ZlibConstants.Z_OK; + } + + // Make sure there is something to do and avoid duplicate consecutive + // flushes. For repeated and useless calls with Z_FINISH, we keep + // returning Z_STREAM_END instead of Z_BUFF_ERROR. + } + else if (_codec.AvailableBytesIn == 0 && + (int)flush <= old_flush && + flush != FlushType.Finish) + { + // workitem 8557 + // + // Not sure why this needs to be an error. pendingCount == 0, which + // means there's nothing to deflate. And the caller has not asked + // for a FlushType.Finish, but... that seems very non-fatal. We + // can just say "OK" and do nothing. + + // _codec.Message = z_errmsg[ZlibConstants.Z_NEED_DICT - (ZlibConstants.Z_BUF_ERROR)]; + // throw new ZlibException("AvailableBytesIn == 0 && flush<=old_flush && flush != FlushType.Finish"); + + return ZlibConstants.Z_OK; + } + + // User must not provide more input after the first FINISH: + if (status == FINISH_STATE && _codec.AvailableBytesIn != 0) + { + _codec.Message = _ErrorMessage[ZlibConstants.Z_NEED_DICT - (ZlibConstants.Z_BUF_ERROR)]; + throw new ZlibException("status == FINISH_STATE && _codec.AvailableBytesIn != 0"); + } + + // Start a new block or continue the current one. + if (_codec.AvailableBytesIn != 0 || lookahead != 0 || (flush != FlushType.None && status != FINISH_STATE)) + { + BlockState bstate = DeflateFunction(flush); + + if (bstate == BlockState.FinishStarted || bstate == BlockState.FinishDone) + { + status = FINISH_STATE; + } + if (bstate == BlockState.NeedMore || bstate == BlockState.FinishStarted) + { + if (_codec.AvailableBytesOut == 0) + { + last_flush = -1; // avoid BUF_ERROR next call, see above + } + return ZlibConstants.Z_OK; + // If flush != Z_NO_FLUSH && avail_out == 0, the next call + // of deflate should use the same flush parameter to make sure + // that the flush is complete. So we don't have to output an + // empty block here, this will be done at next call. This also + // ensures that for a very small output buffer, we emit at most + // one empty block. + } + + if (bstate == BlockState.BlockDone) + { + if (flush == FlushType.Partial) + { + _tr_align(); + } + else + { + // FlushType.Full or FlushType.Sync + _tr_stored_block(0, 0, false); + // For a full flush, this empty block will be recognized + // as a special marker by inflate_sync(). + if (flush == FlushType.Full) + { + // clear hash (forget the history) + for (int i = 0; i < hash_size; i++) + head[i] = 0; + } + } + _codec.flush_pending(); + if (_codec.AvailableBytesOut == 0) + { + last_flush = -1; // avoid BUF_ERROR at next call, see above + return ZlibConstants.Z_OK; + } + } + } + + if (flush != FlushType.Finish) + return ZlibConstants.Z_OK; + + if (!WantRfc1950HeaderBytes || Rfc1950BytesEmitted) + return ZlibConstants.Z_STREAM_END; + + // Write the zlib trailer (adler32) + pending[pendingCount++] = (byte)((_codec._Adler32 & 0xFF000000) >> 24); + pending[pendingCount++] = (byte)((_codec._Adler32 & 0x00FF0000) >> 16); + pending[pendingCount++] = (byte)((_codec._Adler32 & 0x0000FF00) >> 8); + pending[pendingCount++] = (byte)(_codec._Adler32 & 0x000000FF); + //putShortMSB((int)(SharedUtils.URShift(_codec._Adler32, 16))); + //putShortMSB((int)(_codec._Adler32 & 0xffff)); + + _codec.flush_pending(); + + // If avail_out is zero, the application will call deflate again + // to flush the rest. + + Rfc1950BytesEmitted = true; // write the trailer only once! + + return pendingCount != 0 ? ZlibConstants.Z_OK : ZlibConstants.Z_STREAM_END; + } + + } +} \ No newline at end of file diff --git a/DotNetZip/Zlib/DeflateStream.cs b/DotNetZip/Zlib/DeflateStream.cs new file mode 100644 index 0000000..05362d4 --- /dev/null +++ b/DotNetZip/Zlib/DeflateStream.cs @@ -0,0 +1,740 @@ +// DeflateStream.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2009-2010 Dino Chiesa. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2011-July-31 14:48:11> +// +// ------------------------------------------------------------------ +// +// This module defines the DeflateStream class, which can be used as a replacement for +// the System.IO.Compression.DeflateStream class in the .NET BCL. +// +// ------------------------------------------------------------------ + + +using System; + +namespace Ionic.Zlib +{ + /// + /// A class for compressing and decompressing streams using the Deflate algorithm. + /// + /// + /// + /// + /// + /// The DeflateStream is a Decorator on a . It adds DEFLATE compression or decompression to any + /// stream. + /// + /// + /// + /// Using this stream, applications can compress or decompress data via stream + /// Read and Write operations. Either compresssion or decompression + /// can occur through either reading or writing. The compression format used is + /// DEFLATE, which is documented in IETF RFC 1951, "DEFLATE + /// Compressed Data Format Specification version 1.3.". + /// + /// + /// + /// This class is similar to , except that + /// ZlibStream adds the RFC + /// 1950 - ZLIB framing bytes to a compressed stream when compressing, or + /// expects the RFC1950 framing bytes when decompressing. The DeflateStream + /// does not. + /// + /// + /// + /// + /// + /// + public class DeflateStream : System.IO.Stream + { + internal ZlibBaseStream _baseStream; + internal System.IO.Stream _innerStream; + bool _disposed; + + /// + /// Create a DeflateStream using the specified CompressionMode. + /// + /// + /// + /// When mode is CompressionMode.Compress, the DeflateStream will use + /// the default compression level. The "captive" stream will be closed when + /// the DeflateStream is closed. + /// + /// + /// + /// This example uses a DeflateStream to compress data from a file, and writes + /// the compressed data to another file. + /// + /// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress)) + /// { + /// using (var raw = System.IO.File.Create(fileToCompress + ".deflated")) + /// { + /// using (Stream compressor = new DeflateStream(raw, CompressionMode.Compress)) + /// { + /// byte[] buffer = new byte[WORKING_BUFFER_SIZE]; + /// int n; + /// while ((n= input.Read(buffer, 0, buffer.Length)) != 0) + /// { + /// compressor.Write(buffer, 0, n); + /// } + /// } + /// } + /// } + /// + /// + /// + /// Using input As Stream = File.OpenRead(fileToCompress) + /// Using raw As FileStream = File.Create(fileToCompress & ".deflated") + /// Using compressor As Stream = New DeflateStream(raw, CompressionMode.Compress) + /// Dim buffer As Byte() = New Byte(4096) {} + /// Dim n As Integer = -1 + /// Do While (n <> 0) + /// If (n > 0) Then + /// compressor.Write(buffer, 0, n) + /// End If + /// n = input.Read(buffer, 0, buffer.Length) + /// Loop + /// End Using + /// End Using + /// End Using + /// + /// + /// The stream which will be read or written. + /// Indicates whether the DeflateStream will compress or decompress. + public DeflateStream(System.IO.Stream stream, CompressionMode mode) + : this(stream, mode, CompressionLevel.Default, false) + { + } + + /// + /// Create a DeflateStream using the specified CompressionMode and the specified CompressionLevel. + /// + /// + /// + /// + /// + /// When mode is CompressionMode.Decompress, the level parameter is + /// ignored. The "captive" stream will be closed when the DeflateStream is + /// closed. + /// + /// + /// + /// + /// + /// + /// This example uses a DeflateStream to compress data from a file, and writes + /// the compressed data to another file. + /// + /// + /// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress)) + /// { + /// using (var raw = System.IO.File.Create(fileToCompress + ".deflated")) + /// { + /// using (Stream compressor = new DeflateStream(raw, + /// CompressionMode.Compress, + /// CompressionLevel.BestCompression)) + /// { + /// byte[] buffer = new byte[WORKING_BUFFER_SIZE]; + /// int n= -1; + /// while (n != 0) + /// { + /// if (n > 0) + /// compressor.Write(buffer, 0, n); + /// n= input.Read(buffer, 0, buffer.Length); + /// } + /// } + /// } + /// } + /// + /// + /// + /// Using input As Stream = File.OpenRead(fileToCompress) + /// Using raw As FileStream = File.Create(fileToCompress & ".deflated") + /// Using compressor As Stream = New DeflateStream(raw, CompressionMode.Compress, CompressionLevel.BestCompression) + /// Dim buffer As Byte() = New Byte(4096) {} + /// Dim n As Integer = -1 + /// Do While (n <> 0) + /// If (n > 0) Then + /// compressor.Write(buffer, 0, n) + /// End If + /// n = input.Read(buffer, 0, buffer.Length) + /// Loop + /// End Using + /// End Using + /// End Using + /// + /// + /// The stream to be read or written while deflating or inflating. + /// Indicates whether the DeflateStream will compress or decompress. + /// A tuning knob to trade speed for effectiveness. + public DeflateStream(System.IO.Stream stream, CompressionMode mode, CompressionLevel level) + : this(stream, mode, level, false) + { + } + + /// + /// Create a DeflateStream using the specified + /// CompressionMode, and explicitly specify whether the + /// stream should be left open after Deflation or Inflation. + /// + /// + /// + /// + /// + /// This constructor allows the application to request that the captive stream + /// remain open after the deflation or inflation occurs. By default, after + /// Close() is called on the stream, the captive stream is also + /// closed. In some cases this is not desired, for example if the stream is a + /// memory stream that will be re-read after compression. Specify true for + /// the parameter to leave the stream open. + /// + /// + /// + /// The DeflateStream will use the default compression level. + /// + /// + /// + /// See the other overloads of this constructor for example code. + /// + /// + /// + /// + /// The stream which will be read or written. This is called the + /// "captive" stream in other places in this documentation. + /// + /// + /// + /// Indicates whether the DeflateStream will compress or decompress. + /// + /// + /// true if the application would like the stream to + /// remain open after inflation/deflation. + public DeflateStream(System.IO.Stream stream, CompressionMode mode, bool leaveOpen) + : this(stream, mode, CompressionLevel.Default, leaveOpen) + { + } + + /// + /// Create a DeflateStream using the specified CompressionMode + /// and the specified CompressionLevel, and explicitly specify whether + /// the stream should be left open after Deflation or Inflation. + /// + /// + /// + /// + /// + /// When mode is CompressionMode.Decompress, the level parameter is ignored. + /// + /// + /// + /// This constructor allows the application to request that the captive stream + /// remain open after the deflation or inflation occurs. By default, after + /// Close() is called on the stream, the captive stream is also + /// closed. In some cases this is not desired, for example if the stream is a + /// that will be re-read after + /// compression. Specify true for the parameter + /// to leave the stream open. + /// + /// + /// + /// + /// + /// + /// This example shows how to use a DeflateStream to compress data from + /// a file, and store the compressed data into another file. + /// + /// + /// using (var output = System.IO.File.Create(fileToCompress + ".deflated")) + /// { + /// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress)) + /// { + /// using (Stream compressor = new DeflateStream(output, CompressionMode.Compress, CompressionLevel.BestCompression, true)) + /// { + /// byte[] buffer = new byte[WORKING_BUFFER_SIZE]; + /// int n= -1; + /// while (n != 0) + /// { + /// if (n > 0) + /// compressor.Write(buffer, 0, n); + /// n= input.Read(buffer, 0, buffer.Length); + /// } + /// } + /// } + /// // can write additional data to the output stream here + /// } + /// + /// + /// + /// Using output As FileStream = File.Create(fileToCompress & ".deflated") + /// Using input As Stream = File.OpenRead(fileToCompress) + /// Using compressor As Stream = New DeflateStream(output, CompressionMode.Compress, CompressionLevel.BestCompression, True) + /// Dim buffer As Byte() = New Byte(4096) {} + /// Dim n As Integer = -1 + /// Do While (n <> 0) + /// If (n > 0) Then + /// compressor.Write(buffer, 0, n) + /// End If + /// n = input.Read(buffer, 0, buffer.Length) + /// Loop + /// End Using + /// End Using + /// ' can write additional data to the output stream here. + /// End Using + /// + /// + /// The stream which will be read or written. + /// Indicates whether the DeflateStream will compress or decompress. + /// true if the application would like the stream to remain open after inflation/deflation. + /// A tuning knob to trade speed for effectiveness. + public DeflateStream(System.IO.Stream stream, CompressionMode mode, CompressionLevel level, bool leaveOpen) + { + _innerStream = stream; + _baseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.DEFLATE, leaveOpen); + } + + #region Zlib properties + + /// + /// This property sets the flush behavior on the stream. + /// + /// See the ZLIB documentation for the meaning of the flush behavior. + /// + virtual public FlushType FlushMode + { + get { return (this._baseStream._flushMode); } + set + { + if (_disposed) throw new ObjectDisposedException("DeflateStream"); + this._baseStream._flushMode = value; + } + } + + /// + /// The size of the working buffer for the compression codec. + /// + /// + /// + /// + /// The working buffer is used for all stream operations. The default size is + /// 1024 bytes. The minimum size is 128 bytes. You may get better performance + /// with a larger buffer. Then again, you might not. You would have to test + /// it. + /// + /// + /// + /// Set this before the first call to Read() or Write() on the + /// stream. If you try to set it afterwards, it will throw. + /// + /// + public int BufferSize + { + get + { + return this._baseStream._bufferSize; + } + set + { + if (_disposed) throw new ObjectDisposedException("DeflateStream"); + if (this._baseStream._workingBuffer != null) + throw new ZlibException("The working buffer is already set."); + if (value < ZlibConstants.WorkingBufferSizeMin) + throw new ZlibException(String.Format("Don't be silly. {0} bytes?? Use a bigger buffer, at least {1}.", value, ZlibConstants.WorkingBufferSizeMin)); + this._baseStream._bufferSize = value; + } + } + + /// + /// The ZLIB strategy to be used during compression. + /// + /// + /// + /// By tweaking this parameter, you may be able to optimize the compression for + /// data with particular characteristics. + /// + public CompressionStrategy Strategy + { + get + { + return this._baseStream.Strategy; + } + set + { + if (_disposed) throw new ObjectDisposedException("DeflateStream"); + this._baseStream.Strategy = value; + } + } + + /// Returns the total number of bytes input so far. + virtual public long TotalIn + { + get + { + return this._baseStream._z.TotalBytesIn; + } + } + + /// Returns the total number of bytes output so far. + virtual public long TotalOut + { + get + { + return this._baseStream._z.TotalBytesOut; + } + } + + #endregion + + #region System.IO.Stream methods + /// + /// Dispose the stream. + /// + /// + /// + /// This may or may not result in a Close() call on the captive + /// stream. See the constructors that have a leaveOpen parameter + /// for more information. + /// + /// + /// Application code won't call this code directly. This method may be + /// invoked in two distinct scenarios. If disposing == true, the method + /// has been called directly or indirectly by a user's code, for example + /// via the public Dispose() method. In this case, both managed and + /// unmanaged resources can be referenced and disposed. If disposing == + /// false, the method has been called by the runtime from inside the + /// object finalizer and this method should not reference other objects; + /// in that case only unmanaged resources must be referenced or + /// disposed. + /// + /// + /// + /// true if the Dispose method was invoked by user code. + /// + protected override void Dispose(bool disposing) + { + try + { + if (!_disposed) + { + if (disposing && (this._baseStream != null)) + this._baseStream.Close(); + _disposed = true; + } + } + finally + { + base.Dispose(disposing); + } + } + + + + /// + /// Indicates whether the stream can be read. + /// + /// + /// The return value depends on whether the captive stream supports reading. + /// + public override bool CanRead + { + get + { + if (_disposed) throw new ObjectDisposedException("DeflateStream"); + return _baseStream._stream.CanRead; + } + } + + /// + /// Indicates whether the stream supports Seek operations. + /// + /// + /// Always returns false. + /// + public override bool CanSeek + { + get { return false; } + } + + + /// + /// Indicates whether the stream can be written. + /// + /// + /// The return value depends on whether the captive stream supports writing. + /// + public override bool CanWrite + { + get + { + if (_disposed) throw new ObjectDisposedException("DeflateStream"); + return _baseStream._stream.CanWrite; + } + } + + /// + /// Flush the stream. + /// + public override void Flush() + { + if (_disposed) throw new ObjectDisposedException("DeflateStream"); + _baseStream.Flush(); + } + + /// + /// Reading this property always throws a . + /// + public override long Length + { + get { throw new NotImplementedException(); } + } + + /// + /// The position of the stream pointer. + /// + /// + /// + /// Setting this property always throws a . Reading will return the total bytes + /// written out, if used in writing, or the total bytes read in, if used in + /// reading. The count may refer to compressed bytes or uncompressed bytes, + /// depending on how you've used the stream. + /// + public override long Position + { + get + { + if (this._baseStream._streamMode == Ionic.Zlib.ZlibBaseStream.StreamMode.Writer) + return this._baseStream._z.TotalBytesOut; + if (this._baseStream._streamMode == Ionic.Zlib.ZlibBaseStream.StreamMode.Reader) + return this._baseStream._z.TotalBytesIn; + return 0; + } + set { throw new NotImplementedException(); } + } + + /// + /// Read data from the stream. + /// + /// + /// + /// + /// If you wish to use the DeflateStream to compress data while + /// reading, you can create a DeflateStream with + /// CompressionMode.Compress, providing an uncompressed data stream. + /// Then call Read() on that DeflateStream, and the data read will be + /// compressed as you read. If you wish to use the DeflateStream to + /// decompress data while reading, you can create a DeflateStream with + /// CompressionMode.Decompress, providing a readable compressed data + /// stream. Then call Read() on that DeflateStream, and the data read + /// will be decompressed as you read. + /// + /// + /// + /// A DeflateStream can be used for Read() or Write(), but not both. + /// + /// + /// + /// The buffer into which the read data should be placed. + /// the offset within that data array to put the first byte read. + /// the number of bytes to read. + /// the number of bytes actually read + public override int Read(byte[] buffer, int offset, int count) + { + if (_disposed) throw new ObjectDisposedException("DeflateStream"); + return _baseStream.Read(buffer, offset, count); + } + + + /// + /// Calling this method always throws a . + /// + /// this is irrelevant, since it will always throw! + /// this is irrelevant, since it will always throw! + /// irrelevant! + public override long Seek(long offset, System.IO.SeekOrigin origin) + { + throw new NotImplementedException(); + } + + /// + /// Calling this method always throws a . + /// + /// this is irrelevant, since it will always throw! + public override void SetLength(long value) + { + throw new NotImplementedException(); + } + + /// + /// Write data to the stream. + /// + /// + /// + /// + /// If you wish to use the DeflateStream to compress data while + /// writing, you can create a DeflateStream with + /// CompressionMode.Compress, and a writable output stream. Then call + /// Write() on that DeflateStream, providing uncompressed data + /// as input. The data sent to the output stream will be the compressed form + /// of the data written. If you wish to use the DeflateStream to + /// decompress data while writing, you can create a DeflateStream with + /// CompressionMode.Decompress, and a writable output stream. Then + /// call Write() on that stream, providing previously compressed + /// data. The data sent to the output stream will be the decompressed form of + /// the data written. + /// + /// + /// + /// A DeflateStream can be used for Read() or Write(), + /// but not both. + /// + /// + /// + /// + /// The buffer holding data to write to the stream. + /// the offset within that data array to find the first byte to write. + /// the number of bytes to write. + public override void Write(byte[] buffer, int offset, int count) + { + if (_disposed) throw new ObjectDisposedException("DeflateStream"); + _baseStream.Write(buffer, offset, count); + } + #endregion + + + + + /// + /// Compress a string into a byte array using DEFLATE (RFC 1951). + /// + /// + /// + /// Uncompress it with . + /// + /// + /// DeflateStream.UncompressString(byte[]) + /// DeflateStream.CompressBuffer(byte[]) + /// GZipStream.CompressString(string) + /// ZlibStream.CompressString(string) + /// + /// + /// A string to compress. The string will first be encoded + /// using UTF8, then compressed. + /// + /// + /// The string in compressed form + public static byte[] CompressString(String s) + { + using (var ms = new System.IO.MemoryStream()) + { + System.IO.Stream compressor = + new DeflateStream(ms, CompressionMode.Compress, CompressionLevel.BestCompression); + ZlibBaseStream.CompressString(s, compressor); + return ms.ToArray(); + } + } + + + /// + /// Compress a byte array into a new byte array using DEFLATE. + /// + /// + /// + /// Uncompress it with . + /// + /// + /// DeflateStream.CompressString(string) + /// DeflateStream.UncompressBuffer(byte[]) + /// GZipStream.CompressBuffer(byte[]) + /// ZlibStream.CompressBuffer(byte[]) + /// + /// + /// A buffer to compress. + /// + /// + /// The data in compressed form + public static byte[] CompressBuffer(byte[] b) + { + using (var ms = new System.IO.MemoryStream()) + { + System.IO.Stream compressor = + new DeflateStream( ms, CompressionMode.Compress, CompressionLevel.BestCompression ); + + ZlibBaseStream.CompressBuffer(b, compressor); + return ms.ToArray(); + } + } + + + /// + /// Uncompress a DEFLATE'd byte array into a single string. + /// + /// + /// DeflateStream.CompressString(String) + /// DeflateStream.UncompressBuffer(byte[]) + /// GZipStream.UncompressString(byte[]) + /// ZlibStream.UncompressString(byte[]) + /// + /// + /// A buffer containing DEFLATE-compressed data. + /// + /// + /// The uncompressed string + public static String UncompressString(byte[] compressed) + { + using (var input = new System.IO.MemoryStream(compressed)) + { + System.IO.Stream decompressor = + new DeflateStream(input, CompressionMode.Decompress); + + return ZlibBaseStream.UncompressString(compressed, decompressor); + } + } + + + /// + /// Uncompress a DEFLATE'd byte array into a byte array. + /// + /// + /// DeflateStream.CompressBuffer(byte[]) + /// DeflateStream.UncompressString(byte[]) + /// GZipStream.UncompressBuffer(byte[]) + /// ZlibStream.UncompressBuffer(byte[]) + /// + /// + /// A buffer containing data that has been compressed with DEFLATE. + /// + /// + /// The data in uncompressed form + public static byte[] UncompressBuffer(byte[] compressed) + { + using (var input = new System.IO.MemoryStream(compressed)) + { + System.IO.Stream decompressor = + new DeflateStream( input, CompressionMode.Decompress ); + + return ZlibBaseStream.UncompressBuffer(compressed, decompressor); + } + } + + } + +} + diff --git a/DotNetZip/Zlib/GZipStream.cs b/DotNetZip/Zlib/GZipStream.cs new file mode 100644 index 0000000..745e096 --- /dev/null +++ b/DotNetZip/Zlib/GZipStream.cs @@ -0,0 +1,1033 @@ +// GZipStream.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2011-August-08 18:14:39> +// +// ------------------------------------------------------------------ +// +// This module defines the GZipStream class, which can be used as a replacement for +// the System.IO.Compression.GZipStream class in the .NET BCL. NB: The design is not +// completely OO clean: there is some intelligence in the ZlibBaseStream that reads the +// GZip header. +// +// ------------------------------------------------------------------ + + +using System; +using System.IO; + +namespace Ionic.Zlib +{ + /// + /// A class for compressing and decompressing GZIP streams. + /// + /// + /// + /// + /// The GZipStream is a Decorator on a + /// . It adds GZIP compression or decompression to any + /// stream. + /// + /// + /// + /// Like the System.IO.Compression.GZipStream in the .NET Base Class Library, the + /// Ionic.Zlib.GZipStream can compress while writing, or decompress while + /// reading, but not vice versa. The compression method used is GZIP, which is + /// documented in IETF RFC + /// 1952, "GZIP file format specification version 4.3". + /// + /// + /// A GZipStream can be used to decompress data (through Read()) or + /// to compress data (through Write()), but not both. + /// + /// + /// + /// If you wish to use the GZipStream to compress data, you must wrap it + /// around a write-able stream. As you call Write() on the GZipStream, the + /// data will be compressed into the GZIP format. If you want to decompress data, + /// you must wrap the GZipStream around a readable stream that contains an + /// IETF RFC 1952-compliant stream. The data will be decompressed as you call + /// Read() on the GZipStream. + /// + /// + /// + /// Though the GZIP format allows data from multiple files to be concatenated + /// together, this stream handles only a single segment of GZIP format, typically + /// representing a single file. + /// + /// + /// + /// This class is similar to and . + /// ZlibStream handles RFC1950-compliant streams. + /// handles RFC1951-compliant streams. This class handles RFC1952-compliant streams. + /// + /// + /// + /// + /// + /// + public class GZipStream : System.IO.Stream + { + // GZip format + // source: http://tools.ietf.org/html/rfc1952 + // + // header id: 2 bytes 1F 8B + // compress method 1 byte 8= DEFLATE (none other supported) + // flag 1 byte bitfield (See below) + // mtime 4 bytes time_t (seconds since jan 1, 1970 UTC of the file. + // xflg 1 byte 2 = max compress used , 4 = max speed (can be ignored) + // OS 1 byte OS for originating archive. set to 0xFF in compression. + // extra field length 2 bytes optional - only if FEXTRA is set. + // extra field varies + // filename varies optional - if FNAME is set. zero terminated. ISO-8859-1. + // file comment varies optional - if FCOMMENT is set. zero terminated. ISO-8859-1. + // crc16 1 byte optional - present only if FHCRC bit is set + // compressed data varies + // CRC32 4 bytes + // isize 4 bytes data size modulo 2^32 + // + // FLG (FLaGs) + // bit 0 FTEXT - indicates file is ASCII text (can be safely ignored) + // bit 1 FHCRC - there is a CRC16 for the header immediately following the header + // bit 2 FEXTRA - extra fields are present + // bit 3 FNAME - the zero-terminated filename is present. encoding; ISO-8859-1. + // bit 4 FCOMMENT - a zero-terminated file comment is present. encoding: ISO-8859-1 + // bit 5 reserved + // bit 6 reserved + // bit 7 reserved + // + // On consumption: + // Extra field is a bunch of nonsense and can be safely ignored. + // Header CRC and OS, likewise. + // + // on generation: + // all optional fields get 0, except for the OS, which gets 255. + // + + + + /// + /// The comment on the GZIP stream. + /// + /// + /// + /// + /// The GZIP format allows for each file to optionally have an associated + /// comment stored with the file. The comment is encoded with the ISO-8859-1 + /// code page. To include a comment in a GZIP stream you create, set this + /// property before calling Write() for the first time on the + /// GZipStream. + /// + /// + /// + /// When using GZipStream to decompress, you can retrieve this property + /// after the first call to Read(). If no comment has been set in the + /// GZIP bytestream, the Comment property will return null + /// (Nothing in VB). + /// + /// + public String Comment + { + get + { + return _Comment; + } + set + { + if (_disposed) throw new ObjectDisposedException("GZipStream"); + _Comment = value; + } + } + + /// + /// The FileName for the GZIP stream. + /// + /// + /// + /// + /// + /// The GZIP format optionally allows each file to have an associated + /// filename. When compressing data (through Write()), set this + /// FileName before calling Write() the first time on the GZipStream. + /// The actual filename is encoded into the GZIP bytestream with the + /// ISO-8859-1 code page, according to RFC 1952. It is the application's + /// responsibility to insure that the FileName can be encoded and decoded + /// correctly with this code page. + /// + /// + /// + /// When decompressing (through Read()), you can retrieve this value + /// any time after the first Read(). In the case where there was no filename + /// encoded into the GZIP bytestream, the property will return null (Nothing + /// in VB). + /// + /// + public String FileName + { + get { return _FileName; } + set + { + if (_disposed) throw new ObjectDisposedException("GZipStream"); + _FileName = value; + if (_FileName == null) return; + if (_FileName.IndexOf("/") != -1) + { + _FileName = _FileName.Replace("/", "\\"); + } + if (_FileName.EndsWith("\\")) + throw new Exception("Illegal filename"); + if (_FileName.IndexOf("\\") != -1) + { + // trim any leading path + _FileName = Path.GetFileName(_FileName); + } + } + } + + /// + /// The last modified time for the GZIP stream. + /// + /// + /// + /// GZIP allows the storage of a last modified time with each GZIP entry. + /// When compressing data, you can set this before the first call to + /// Write(). When decompressing, you can retrieve this value any time + /// after the first call to Read(). + /// + public DateTime? LastModified; + + /// + /// The CRC on the GZIP stream. + /// + /// + /// This is used for internal error checking. You probably don't need to look at this property. + /// + public int Crc32 { get { return _Crc32; } } + + private int _headerByteCount; + internal ZlibBaseStream _baseStream; + bool _disposed; + bool _firstReadDone; + string _FileName; + string _Comment; + int _Crc32; + + + /// + /// Create a GZipStream using the specified CompressionMode. + /// + /// + /// + /// + /// When mode is CompressionMode.Compress, the GZipStream will use the + /// default compression level. + /// + /// + /// + /// As noted in the class documentation, the CompressionMode (Compress + /// or Decompress) also establishes the "direction" of the stream. A + /// GZipStream with CompressionMode.Compress works only through + /// Write(). A GZipStream with + /// CompressionMode.Decompress works only through Read(). + /// + /// + /// + /// + /// + /// This example shows how to use a GZipStream to compress data. + /// + /// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress)) + /// { + /// using (var raw = System.IO.File.Create(outputFile)) + /// { + /// using (Stream compressor = new GZipStream(raw, CompressionMode.Compress)) + /// { + /// byte[] buffer = new byte[WORKING_BUFFER_SIZE]; + /// int n; + /// while ((n= input.Read(buffer, 0, buffer.Length)) != 0) + /// { + /// compressor.Write(buffer, 0, n); + /// } + /// } + /// } + /// } + /// + /// + /// Dim outputFile As String = (fileToCompress & ".compressed") + /// Using input As Stream = File.OpenRead(fileToCompress) + /// Using raw As FileStream = File.Create(outputFile) + /// Using compressor As Stream = New GZipStream(raw, CompressionMode.Compress) + /// Dim buffer As Byte() = New Byte(4096) {} + /// Dim n As Integer = -1 + /// Do While (n <> 0) + /// If (n > 0) Then + /// compressor.Write(buffer, 0, n) + /// End If + /// n = input.Read(buffer, 0, buffer.Length) + /// Loop + /// End Using + /// End Using + /// End Using + /// + /// + /// + /// + /// This example shows how to use a GZipStream to uncompress a file. + /// + /// private void GunZipFile(string filename) + /// { + /// if (!filename.EndsWith(".gz)) + /// throw new ArgumentException("filename"); + /// var DecompressedFile = filename.Substring(0,filename.Length-3); + /// byte[] working = new byte[WORKING_BUFFER_SIZE]; + /// int n= 1; + /// using (System.IO.Stream input = System.IO.File.OpenRead(filename)) + /// { + /// using (Stream decompressor= new Ionic.Zlib.GZipStream(input, CompressionMode.Decompress, true)) + /// { + /// using (var output = System.IO.File.Create(DecompressedFile)) + /// { + /// while (n !=0) + /// { + /// n= decompressor.Read(working, 0, working.Length); + /// if (n > 0) + /// { + /// output.Write(working, 0, n); + /// } + /// } + /// } + /// } + /// } + /// } + /// + /// + /// + /// Private Sub GunZipFile(ByVal filename as String) + /// If Not (filename.EndsWith(".gz)) Then + /// Throw New ArgumentException("filename") + /// End If + /// Dim DecompressedFile as String = filename.Substring(0,filename.Length-3) + /// Dim working(WORKING_BUFFER_SIZE) as Byte + /// Dim n As Integer = 1 + /// Using input As Stream = File.OpenRead(filename) + /// Using decompressor As Stream = new Ionic.Zlib.GZipStream(input, CompressionMode.Decompress, True) + /// Using output As Stream = File.Create(UncompressedFile) + /// Do + /// n= decompressor.Read(working, 0, working.Length) + /// If n > 0 Then + /// output.Write(working, 0, n) + /// End IF + /// Loop While (n > 0) + /// End Using + /// End Using + /// End Using + /// End Sub + /// + /// + /// + /// The stream which will be read or written. + /// Indicates whether the GZipStream will compress or decompress. + public GZipStream(Stream stream, CompressionMode mode) + : this(stream, mode, CompressionLevel.Default, false) + { + } + + /// + /// Create a GZipStream using the specified CompressionMode and + /// the specified CompressionLevel. + /// + /// + /// + /// + /// The CompressionMode (Compress or Decompress) also establishes the + /// "direction" of the stream. A GZipStream with + /// CompressionMode.Compress works only through Write(). A + /// GZipStream with CompressionMode.Decompress works only + /// through Read(). + /// + /// + /// + /// + /// + /// + /// This example shows how to use a GZipStream to compress a file into a .gz file. + /// + /// + /// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress)) + /// { + /// using (var raw = System.IO.File.Create(fileToCompress + ".gz")) + /// { + /// using (Stream compressor = new GZipStream(raw, + /// CompressionMode.Compress, + /// CompressionLevel.BestCompression)) + /// { + /// byte[] buffer = new byte[WORKING_BUFFER_SIZE]; + /// int n; + /// while ((n= input.Read(buffer, 0, buffer.Length)) != 0) + /// { + /// compressor.Write(buffer, 0, n); + /// } + /// } + /// } + /// } + /// + /// + /// + /// Using input As Stream = File.OpenRead(fileToCompress) + /// Using raw As FileStream = File.Create(fileToCompress & ".gz") + /// Using compressor As Stream = New GZipStream(raw, CompressionMode.Compress, CompressionLevel.BestCompression) + /// Dim buffer As Byte() = New Byte(4096) {} + /// Dim n As Integer = -1 + /// Do While (n <> 0) + /// If (n > 0) Then + /// compressor.Write(buffer, 0, n) + /// End If + /// n = input.Read(buffer, 0, buffer.Length) + /// Loop + /// End Using + /// End Using + /// End Using + /// + /// + /// The stream to be read or written while deflating or inflating. + /// Indicates whether the GZipStream will compress or decompress. + /// A tuning knob to trade speed for effectiveness. + public GZipStream(Stream stream, CompressionMode mode, CompressionLevel level) + : this(stream, mode, level, false) + { + } + + /// + /// Create a GZipStream using the specified CompressionMode, and + /// explicitly specify whether the stream should be left open after Deflation + /// or Inflation. + /// + /// + /// + /// + /// This constructor allows the application to request that the captive stream + /// remain open after the deflation or inflation occurs. By default, after + /// Close() is called on the stream, the captive stream is also + /// closed. In some cases this is not desired, for example if the stream is a + /// memory stream that will be re-read after compressed data has been written + /// to it. Specify true for the parameter to leave + /// the stream open. + /// + /// + /// + /// The (Compress or Decompress) also + /// establishes the "direction" of the stream. A GZipStream with + /// CompressionMode.Compress works only through Write(). A GZipStream + /// with CompressionMode.Decompress works only through Read(). + /// + /// + /// + /// The GZipStream will use the default compression level. If you want + /// to specify the compression level, see . + /// + /// + /// + /// See the other overloads of this constructor for example code. + /// + /// + /// + /// + /// + /// The stream which will be read or written. This is called the "captive" + /// stream in other places in this documentation. + /// + /// + /// Indicates whether the GZipStream will compress or decompress. + /// + /// + /// + /// true if the application would like the base stream to remain open after + /// inflation/deflation. + /// + public GZipStream(Stream stream, CompressionMode mode, bool leaveOpen) + : this(stream, mode, CompressionLevel.Default, leaveOpen) + { + } + + /// + /// Create a GZipStream using the specified CompressionMode and the + /// specified CompressionLevel, and explicitly specify whether the + /// stream should be left open after Deflation or Inflation. + /// + /// + /// + /// + /// + /// This constructor allows the application to request that the captive stream + /// remain open after the deflation or inflation occurs. By default, after + /// Close() is called on the stream, the captive stream is also + /// closed. In some cases this is not desired, for example if the stream is a + /// memory stream that will be re-read after compressed data has been written + /// to it. Specify true for the parameter to + /// leave the stream open. + /// + /// + /// + /// As noted in the class documentation, the CompressionMode (Compress + /// or Decompress) also establishes the "direction" of the stream. A + /// GZipStream with CompressionMode.Compress works only through + /// Write(). A GZipStream with CompressionMode.Decompress works only + /// through Read(). + /// + /// + /// + /// + /// + /// This example shows how to use a GZipStream to compress data. + /// + /// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress)) + /// { + /// using (var raw = System.IO.File.Create(outputFile)) + /// { + /// using (Stream compressor = new GZipStream(raw, CompressionMode.Compress, CompressionLevel.BestCompression, true)) + /// { + /// byte[] buffer = new byte[WORKING_BUFFER_SIZE]; + /// int n; + /// while ((n= input.Read(buffer, 0, buffer.Length)) != 0) + /// { + /// compressor.Write(buffer, 0, n); + /// } + /// } + /// } + /// } + /// + /// + /// Dim outputFile As String = (fileToCompress & ".compressed") + /// Using input As Stream = File.OpenRead(fileToCompress) + /// Using raw As FileStream = File.Create(outputFile) + /// Using compressor As Stream = New GZipStream(raw, CompressionMode.Compress, CompressionLevel.BestCompression, True) + /// Dim buffer As Byte() = New Byte(4096) {} + /// Dim n As Integer = -1 + /// Do While (n <> 0) + /// If (n > 0) Then + /// compressor.Write(buffer, 0, n) + /// End If + /// n = input.Read(buffer, 0, buffer.Length) + /// Loop + /// End Using + /// End Using + /// End Using + /// + /// + /// The stream which will be read or written. + /// Indicates whether the GZipStream will compress or decompress. + /// true if the application would like the stream to remain open after inflation/deflation. + /// A tuning knob to trade speed for effectiveness. + public GZipStream(Stream stream, CompressionMode mode, CompressionLevel level, bool leaveOpen) + { + _baseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.GZIP, leaveOpen); + } + + #region Zlib properties + + /// + /// This property sets the flush behavior on the stream. + /// + virtual public FlushType FlushMode + { + get { return (this._baseStream._flushMode); } + set { + if (_disposed) throw new ObjectDisposedException("GZipStream"); + this._baseStream._flushMode = value; + } + } + + /// + /// The size of the working buffer for the compression codec. + /// + /// + /// + /// + /// The working buffer is used for all stream operations. The default size is + /// 1024 bytes. The minimum size is 128 bytes. You may get better performance + /// with a larger buffer. Then again, you might not. You would have to test + /// it. + /// + /// + /// + /// Set this before the first call to Read() or Write() on the + /// stream. If you try to set it afterwards, it will throw. + /// + /// + public int BufferSize + { + get + { + return this._baseStream._bufferSize; + } + set + { + if (_disposed) throw new ObjectDisposedException("GZipStream"); + if (this._baseStream._workingBuffer != null) + throw new ZlibException("The working buffer is already set."); + if (value < ZlibConstants.WorkingBufferSizeMin) + throw new ZlibException(String.Format("Don't be silly. {0} bytes?? Use a bigger buffer, at least {1}.", value, ZlibConstants.WorkingBufferSizeMin)); + this._baseStream._bufferSize = value; + } + } + + + /// Returns the total number of bytes input so far. + virtual public long TotalIn + { + get + { + return this._baseStream._z.TotalBytesIn; + } + } + + /// Returns the total number of bytes output so far. + virtual public long TotalOut + { + get + { + return this._baseStream._z.TotalBytesOut; + } + } + + #endregion + + #region Stream methods + + /// + /// Dispose the stream. + /// + /// + /// + /// This may or may not result in a Close() call on the captive + /// stream. See the constructors that have a leaveOpen parameter + /// for more information. + /// + /// + /// This method may be invoked in two distinct scenarios. If disposing + /// == true, the method has been called directly or indirectly by a + /// user's code, for example via the public Dispose() method. In this + /// case, both managed and unmanaged resources can be referenced and + /// disposed. If disposing == false, the method has been called by the + /// runtime from inside the object finalizer and this method should not + /// reference other objects; in that case only unmanaged resources must + /// be referenced or disposed. + /// + /// + /// + /// indicates whether the Dispose method was invoked by user code. + /// + protected override void Dispose(bool disposing) + { + try + { + if (!_disposed) + { + if (disposing && (this._baseStream != null)) + { + this._baseStream.Close(); + this._Crc32 = _baseStream.Crc32; + } + _disposed = true; + } + } + finally + { + base.Dispose(disposing); + } + } + + + /// + /// Indicates whether the stream can be read. + /// + /// + /// The return value depends on whether the captive stream supports reading. + /// + public override bool CanRead + { + get + { + if (_disposed) throw new ObjectDisposedException("GZipStream"); + return _baseStream._stream.CanRead; + } + } + + /// + /// Indicates whether the stream supports Seek operations. + /// + /// + /// Always returns false. + /// + public override bool CanSeek + { + get { return false; } + } + + + /// + /// Indicates whether the stream can be written. + /// + /// + /// The return value depends on whether the captive stream supports writing. + /// + public override bool CanWrite + { + get + { + if (_disposed) throw new ObjectDisposedException("GZipStream"); + return _baseStream._stream.CanWrite; + } + } + + /// + /// Flush the stream. + /// + public override void Flush() + { + if (_disposed) throw new ObjectDisposedException("GZipStream"); + _baseStream.Flush(); + } + + /// + /// Reading this property always throws a . + /// + public override long Length + { + get { throw new NotImplementedException(); } + } + + /// + /// The position of the stream pointer. + /// + /// + /// + /// Setting this property always throws a . Reading will return the total bytes + /// written out, if used in writing, or the total bytes read in, if used in + /// reading. The count may refer to compressed bytes or uncompressed bytes, + /// depending on how you've used the stream. + /// + public override long Position + { + get + { + if (this._baseStream._streamMode == Ionic.Zlib.ZlibBaseStream.StreamMode.Writer) + return this._baseStream._z.TotalBytesOut + _headerByteCount; + if (this._baseStream._streamMode == Ionic.Zlib.ZlibBaseStream.StreamMode.Reader) + return this._baseStream._z.TotalBytesIn + this._baseStream._gzipHeaderByteCount; + return 0; + } + + set { throw new NotImplementedException(); } + } + + /// + /// Read and decompress data from the source stream. + /// + /// + /// + /// With a GZipStream, decompression is done through reading. + /// + /// + /// + /// + /// byte[] working = new byte[WORKING_BUFFER_SIZE]; + /// using (System.IO.Stream input = System.IO.File.OpenRead(_CompressedFile)) + /// { + /// using (Stream decompressor= new Ionic.Zlib.GZipStream(input, CompressionMode.Decompress, true)) + /// { + /// using (var output = System.IO.File.Create(_DecompressedFile)) + /// { + /// int n; + /// while ((n= decompressor.Read(working, 0, working.Length)) !=0) + /// { + /// output.Write(working, 0, n); + /// } + /// } + /// } + /// } + /// + /// + /// The buffer into which the decompressed data should be placed. + /// the offset within that data array to put the first byte read. + /// the number of bytes to read. + /// the number of bytes actually read + public override int Read(byte[] buffer, int offset, int count) + { + if (_disposed) throw new ObjectDisposedException("GZipStream"); + int n = _baseStream.Read(buffer, offset, count); + + // Console.WriteLine("GZipStream::Read(buffer, off({0}), c({1}) = {2}", offset, count, n); + // Console.WriteLine( Util.FormatByteArray(buffer, offset, n) ); + + if (!_firstReadDone) + { + _firstReadDone = true; + FileName = _baseStream._GzipFileName; + Comment = _baseStream._GzipComment; + } + return n; + } + + + + /// + /// Calling this method always throws a . + /// + /// irrelevant; it will always throw! + /// irrelevant; it will always throw! + /// irrelevant! + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotImplementedException(); + } + + /// + /// Calling this method always throws a . + /// + /// irrelevant; this method will always throw! + public override void SetLength(long value) + { + throw new NotImplementedException(); + } + + /// + /// Write data to the stream. + /// + /// + /// + /// + /// If you wish to use the GZipStream to compress data while writing, + /// you can create a GZipStream with CompressionMode.Compress, and a + /// writable output stream. Then call Write() on that GZipStream, + /// providing uncompressed data as input. The data sent to the output stream + /// will be the compressed form of the data written. + /// + /// + /// + /// A GZipStream can be used for Read() or Write(), but not + /// both. Writing implies compression. Reading implies decompression. + /// + /// + /// + /// The buffer holding data to write to the stream. + /// the offset within that data array to find the first byte to write. + /// the number of bytes to write. + public override void Write(byte[] buffer, int offset, int count) + { + if (_disposed) throw new ObjectDisposedException("GZipStream"); + if (_baseStream._streamMode == Ionic.Zlib.ZlibBaseStream.StreamMode.Undefined) + { + //Console.WriteLine("GZipStream: First write"); + if (_baseStream._wantCompress) + { + // first write in compression, therefore, emit the GZIP header + _headerByteCount = EmitHeader(); + } + else + { + throw new InvalidOperationException(); + } + } + + _baseStream.Write(buffer, offset, count); + } + #endregion + + + internal static readonly System.DateTime _unixEpoch = new System.DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); +#if SILVERLIGHT || NETCF + internal static readonly System.Text.Encoding iso8859dash1 = new Ionic.Encoding.Iso8859Dash1Encoding(); +#else + internal static readonly System.Text.Encoding iso8859dash1 = System.Text.Encoding.GetEncoding("iso-8859-1"); +#endif + + + private int EmitHeader() + { + byte[] commentBytes = (Comment == null) ? null : iso8859dash1.GetBytes(Comment); + byte[] filenameBytes = (FileName == null) ? null : iso8859dash1.GetBytes(FileName); + + int cbLength = (Comment == null) ? 0 : commentBytes.Length + 1; + int fnLength = (FileName == null) ? 0 : filenameBytes.Length + 1; + + int bufferLength = 10 + cbLength + fnLength; + byte[] header = new byte[bufferLength]; + int i = 0; + // ID + header[i++] = 0x1F; + header[i++] = 0x8B; + + // compression method + header[i++] = 8; + byte flag = 0; + if (Comment != null) + flag ^= 0x10; + if (FileName != null) + flag ^= 0x8; + + // flag + header[i++] = flag; + + // mtime + if (!LastModified.HasValue) LastModified = DateTime.Now; + System.TimeSpan delta = LastModified.Value - _unixEpoch; + Int32 timet = (Int32)delta.TotalSeconds; + Array.Copy(BitConverter.GetBytes(timet), 0, header, i, 4); + i += 4; + + // xflg + header[i++] = 0; // this field is totally useless + // OS + header[i++] = 0xFF; // 0xFF == unspecified + + // extra field length - only if FEXTRA is set, which it is not. + //header[i++]= 0; + //header[i++]= 0; + + // filename + if (fnLength != 0) + { + Array.Copy(filenameBytes, 0, header, i, fnLength - 1); + i += fnLength - 1; + header[i++] = 0; // terminate + } + + // comment + if (cbLength != 0) + { + Array.Copy(commentBytes, 0, header, i, cbLength - 1); + i += cbLength - 1; + header[i++] = 0; // terminate + } + + _baseStream._stream.Write(header, 0, header.Length); + + return header.Length; // bytes written + } + + + + /// + /// Compress a string into a byte array using GZip. + /// + /// + /// + /// Uncompress it with . + /// + /// + /// + /// + /// + /// + /// A string to compress. The string will first be encoded + /// using UTF8, then compressed. + /// + /// + /// The string in compressed form + public static byte[] CompressString(String s) + { + using (var ms = new MemoryStream()) + { + System.IO.Stream compressor = + new GZipStream(ms, CompressionMode.Compress, CompressionLevel.BestCompression); + ZlibBaseStream.CompressString(s, compressor); + return ms.ToArray(); + } + } + + + /// + /// Compress a byte array into a new byte array using GZip. + /// + /// + /// + /// Uncompress it with . + /// + /// + /// + /// + /// + /// + /// A buffer to compress. + /// + /// + /// The data in compressed form + public static byte[] CompressBuffer(byte[] b) + { + using (var ms = new MemoryStream()) + { + System.IO.Stream compressor = + new GZipStream( ms, CompressionMode.Compress, CompressionLevel.BestCompression ); + + ZlibBaseStream.CompressBuffer(b, compressor); + return ms.ToArray(); + } + } + + + /// + /// Uncompress a GZip'ed byte array into a single string. + /// + /// + /// + /// + /// + /// + /// A buffer containing GZIP-compressed data. + /// + /// + /// The uncompressed string + public static String UncompressString(byte[] compressed) + { + using (var input = new MemoryStream(compressed)) + { + Stream decompressor = new GZipStream(input, CompressionMode.Decompress); + return ZlibBaseStream.UncompressString(compressed, decompressor); + } + } + + + /// + /// Uncompress a GZip'ed byte array into a byte array. + /// + /// + /// + /// + /// + /// + /// A buffer containing data that has been compressed with GZip. + /// + /// + /// The data in uncompressed form + public static byte[] UncompressBuffer(byte[] compressed) + { + using (var input = new System.IO.MemoryStream(compressed)) + { + System.IO.Stream decompressor = + new GZipStream( input, CompressionMode.Decompress ); + + return ZlibBaseStream.UncompressBuffer(compressed, decompressor); + } + } + + + } +} diff --git a/DotNetZip/Zlib/InfTree.cs b/DotNetZip/Zlib/InfTree.cs new file mode 100644 index 0000000..416b143 --- /dev/null +++ b/DotNetZip/Zlib/InfTree.cs @@ -0,0 +1,436 @@ +// Inftree.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2009-October-28 12:43:54> +// +// ------------------------------------------------------------------ +// +// This module defines classes used in decompression. This code is derived +// from the jzlib implementation of zlib. In keeping with the license for jzlib, +// the copyright to that code is below. +// +// ------------------------------------------------------------------ +// +// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in +// the documentation and/or other materials provided with the distribution. +// +// 3. The names of the authors may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// ----------------------------------------------------------------------- +// +// This program is based on zlib-1.1.3; credit to authors +// Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) +// and contributors of zlib. +// +// ----------------------------------------------------------------------- + + + +using System; +namespace Ionic.Zlib +{ + + sealed class InfTree + { + + private const int MANY = 1440; + + private const int Z_OK = 0; + private const int Z_STREAM_END = 1; + private const int Z_NEED_DICT = 2; + private const int Z_ERRNO = - 1; + private const int Z_STREAM_ERROR = - 2; + private const int Z_DATA_ERROR = - 3; + private const int Z_MEM_ERROR = - 4; + private const int Z_BUF_ERROR = - 5; + private const int Z_VERSION_ERROR = - 6; + + internal const int fixed_bl = 9; + internal const int fixed_bd = 5; + + //UPGRADE_NOTE: Final was removed from the declaration of 'fixed_tl'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + internal static readonly int[] fixed_tl = new int[]{96, 7, 256, 0, 8, 80, 0, 8, 16, 84, 8, 115, 82, 7, 31, 0, 8, 112, 0, 8, 48, 0, 9, 192, 80, 7, 10, 0, 8, 96, 0, 8, 32, 0, 9, 160, 0, 8, 0, 0, 8, 128, 0, 8, 64, 0, 9, 224, 80, 7, 6, 0, 8, 88, 0, 8, 24, 0, 9, 144, 83, 7, 59, 0, 8, 120, 0, 8, 56, 0, 9, 208, 81, 7, 17, 0, 8, 104, 0, 8, 40, 0, 9, 176, 0, 8, 8, 0, 8, 136, 0, 8, 72, 0, 9, 240, 80, 7, 4, 0, 8, 84, 0, 8, 20, 85, 8, 227, 83, 7, 43, 0, 8, 116, 0, 8, 52, 0, 9, 200, 81, 7, 13, 0, 8, 100, 0, 8, 36, 0, 9, 168, 0, 8, 4, 0, 8, 132, 0, 8, 68, 0, 9, 232, 80, 7, 8, 0, 8, 92, 0, 8, 28, 0, 9, 152, 84, 7, 83, 0, 8, 124, 0, 8, 60, 0, 9, 216, 82, 7, 23, 0, 8, 108, 0, 8, 44, 0, 9, 184, 0, 8, 12, 0, 8, 140, 0, 8, 76, 0, 9, 248, 80, 7, 3, 0, 8, 82, 0, 8, 18, 85, 8, 163, 83, 7, 35, 0, 8, 114, 0, 8, 50, 0, 9, 196, 81, 7, 11, 0, 8, 98, 0, 8, 34, 0, 9, 164, 0, 8, 2, 0, 8, 130, 0, 8, 66, 0, 9, 228, 80, 7, 7, 0, 8, 90, 0, 8, 26, 0, 9, 148, 84, 7, 67, 0, 8, 122, 0, 8, 58, 0, 9, 212, 82, 7, 19, 0, 8, 106, 0, 8, 42, 0, 9, 180, 0, 8, 10, 0, 8, 138, 0, 8, 74, 0, 9, 244, 80, 7, 5, 0, 8, 86, 0, 8, 22, 192, 8, 0, 83, 7, 51, 0, 8, 118, 0, 8, 54, 0, 9, 204, 81, 7, 15, 0, 8, 102, 0, 8, 38, 0, 9, 172, 0, 8, 6, 0, 8, 134, 0, 8, 70, 0, 9, 236, 80, 7, 9, 0, 8, 94, 0, 8, 30, 0, 9, 156, 84, 7, 99, 0, 8, 126, 0, 8, 62, 0, 9, 220, 82, 7, 27, 0, 8, 110, 0, 8, 46, 0, 9, 188, 0, 8, 14, 0, 8, 142, 0, 8, 78, 0, 9, 252, 96, 7, 256, 0, 8, 81, 0, 8, 17, 85, 8, 131, 82, 7, 31, 0, 8, 113, 0, 8, 49, 0, 9, 194, 80, 7, 10, 0, 8, 97, 0, 8, 33, 0, 9, 162, 0, 8, 1, 0, 8, 129, 0, 8, 65, 0, 9, 226, 80, 7, 6, 0, 8, 89, 0, 8, 25, 0, 9, 146, 83, 7, 59, 0, 8, 121, 0, 8, 57, 0, 9, 210, 81, 7, 17, 0, 8, 105, 0, 8, 41, 0, 9, 178, 0, 8, 9, 0, 8, 137, 0, 8, 73, 0, 9, 242, 80, 7, 4, 0, 8, 85, 0, 8, 21, 80, 8, 258, 83, 7, 43, 0, 8, 117, 0, 8, 53, 0, 9, 202, 81, 7, 13, 0, 8, 101, 0, 8, 37, 0, 9, 170, 0, 8, 5, 0, 8, 133, 0, 8, 69, 0, 9, 234, 80, 7, 8, 0, 8, 93, 0, 8, 29, 0, 9, 154, 84, 7, 83, 0, 8, 125, 0, 8, 61, 0, 9, 218, 82, 7, 23, 0, 8, 109, 0, 8, 45, 0, 9, 186, + 0, 8, 13, 0, 8, 141, 0, 8, 77, 0, 9, 250, 80, 7, 3, 0, 8, 83, 0, 8, 19, 85, 8, 195, 83, 7, 35, 0, 8, 115, 0, 8, 51, 0, 9, 198, 81, 7, 11, 0, 8, 99, 0, 8, 35, 0, 9, 166, 0, 8, 3, 0, 8, 131, 0, 8, 67, 0, 9, 230, 80, 7, 7, 0, 8, 91, 0, 8, 27, 0, 9, 150, 84, 7, 67, 0, 8, 123, 0, 8, 59, 0, 9, 214, 82, 7, 19, 0, 8, 107, 0, 8, 43, 0, 9, 182, 0, 8, 11, 0, 8, 139, 0, 8, 75, 0, 9, 246, 80, 7, 5, 0, 8, 87, 0, 8, 23, 192, 8, 0, 83, 7, 51, 0, 8, 119, 0, 8, 55, 0, 9, 206, 81, 7, 15, 0, 8, 103, 0, 8, 39, 0, 9, 174, 0, 8, 7, 0, 8, 135, 0, 8, 71, 0, 9, 238, 80, 7, 9, 0, 8, 95, 0, 8, 31, 0, 9, 158, 84, 7, 99, 0, 8, 127, 0, 8, 63, 0, 9, 222, 82, 7, 27, 0, 8, 111, 0, 8, 47, 0, 9, 190, 0, 8, 15, 0, 8, 143, 0, 8, 79, 0, 9, 254, 96, 7, 256, 0, 8, 80, 0, 8, 16, 84, 8, 115, 82, 7, 31, 0, 8, 112, 0, 8, 48, 0, 9, 193, 80, 7, 10, 0, 8, 96, 0, 8, 32, 0, 9, 161, 0, 8, 0, 0, 8, 128, 0, 8, 64, 0, 9, 225, 80, 7, 6, 0, 8, 88, 0, 8, 24, 0, 9, 145, 83, 7, 59, 0, 8, 120, 0, 8, 56, 0, 9, 209, 81, 7, 17, 0, 8, 104, 0, 8, 40, 0, 9, 177, 0, 8, 8, 0, 8, 136, 0, 8, 72, 0, 9, 241, 80, 7, 4, 0, 8, 84, 0, 8, 20, 85, 8, 227, 83, 7, 43, 0, 8, 116, 0, 8, 52, 0, 9, 201, 81, 7, 13, 0, 8, 100, 0, 8, 36, 0, 9, 169, 0, 8, 4, 0, 8, 132, 0, 8, 68, 0, 9, 233, 80, 7, 8, 0, 8, 92, 0, 8, 28, 0, 9, 153, 84, 7, 83, 0, 8, 124, 0, 8, 60, 0, 9, 217, 82, 7, 23, 0, 8, 108, 0, 8, 44, 0, 9, 185, 0, 8, 12, 0, 8, 140, 0, 8, 76, 0, 9, 249, 80, 7, 3, 0, 8, 82, 0, 8, 18, 85, 8, 163, 83, 7, 35, 0, 8, 114, 0, 8, 50, 0, 9, 197, 81, 7, 11, 0, 8, 98, 0, 8, 34, 0, 9, 165, 0, 8, 2, 0, 8, 130, 0, 8, 66, 0, 9, 229, 80, 7, 7, 0, 8, 90, 0, 8, 26, 0, 9, 149, 84, 7, 67, 0, 8, 122, 0, 8, 58, 0, 9, 213, 82, 7, 19, 0, 8, 106, 0, 8, 42, 0, 9, 181, 0, 8, 10, 0, 8, 138, 0, 8, 74, 0, 9, 245, 80, 7, 5, 0, 8, 86, 0, 8, 22, 192, 8, 0, 83, 7, 51, 0, 8, 118, 0, 8, 54, 0, 9, 205, 81, 7, 15, 0, 8, 102, 0, 8, 38, 0, 9, 173, 0, 8, 6, 0, 8, 134, 0, 8, 70, 0, 9, 237, 80, 7, 9, 0, 8, 94, 0, 8, 30, 0, 9, 157, 84, 7, 99, 0, 8, 126, 0, 8, 62, 0, 9, 221, 82, 7, 27, 0, 8, 110, 0, 8, 46, 0, 9, 189, 0, 8, + 14, 0, 8, 142, 0, 8, 78, 0, 9, 253, 96, 7, 256, 0, 8, 81, 0, 8, 17, 85, 8, 131, 82, 7, 31, 0, 8, 113, 0, 8, 49, 0, 9, 195, 80, 7, 10, 0, 8, 97, 0, 8, 33, 0, 9, 163, 0, 8, 1, 0, 8, 129, 0, 8, 65, 0, 9, 227, 80, 7, 6, 0, 8, 89, 0, 8, 25, 0, 9, 147, 83, 7, 59, 0, 8, 121, 0, 8, 57, 0, 9, 211, 81, 7, 17, 0, 8, 105, 0, 8, 41, 0, 9, 179, 0, 8, 9, 0, 8, 137, 0, 8, 73, 0, 9, 243, 80, 7, 4, 0, 8, 85, 0, 8, 21, 80, 8, 258, 83, 7, 43, 0, 8, 117, 0, 8, 53, 0, 9, 203, 81, 7, 13, 0, 8, 101, 0, 8, 37, 0, 9, 171, 0, 8, 5, 0, 8, 133, 0, 8, 69, 0, 9, 235, 80, 7, 8, 0, 8, 93, 0, 8, 29, 0, 9, 155, 84, 7, 83, 0, 8, 125, 0, 8, 61, 0, 9, 219, 82, 7, 23, 0, 8, 109, 0, 8, 45, 0, 9, 187, 0, 8, 13, 0, 8, 141, 0, 8, 77, 0, 9, 251, 80, 7, 3, 0, 8, 83, 0, 8, 19, 85, 8, 195, 83, 7, 35, 0, 8, 115, 0, 8, 51, 0, 9, 199, 81, 7, 11, 0, 8, 99, 0, 8, 35, 0, 9, 167, 0, 8, 3, 0, 8, 131, 0, 8, 67, 0, 9, 231, 80, 7, 7, 0, 8, 91, 0, 8, 27, 0, 9, 151, 84, 7, 67, 0, 8, 123, 0, 8, 59, 0, 9, 215, 82, 7, 19, 0, 8, 107, 0, 8, 43, 0, 9, 183, 0, 8, 11, 0, 8, 139, 0, 8, 75, 0, 9, 247, 80, 7, 5, 0, 8, 87, 0, 8, 23, 192, 8, 0, 83, 7, 51, 0, 8, 119, 0, 8, 55, 0, 9, 207, 81, 7, 15, 0, 8, 103, 0, 8, 39, 0, 9, 175, 0, 8, 7, 0, 8, 135, 0, 8, 71, 0, 9, 239, 80, 7, 9, 0, 8, 95, 0, 8, 31, 0, 9, 159, 84, 7, 99, 0, 8, 127, 0, 8, 63, 0, 9, 223, 82, 7, 27, 0, 8, 111, 0, 8, 47, 0, 9, 191, 0, 8, 15, 0, 8, 143, 0, 8, 79, 0, 9, 255}; + //UPGRADE_NOTE: Final was removed from the declaration of 'fixed_td'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + internal static readonly int[] fixed_td = new int[]{80, 5, 1, 87, 5, 257, 83, 5, 17, 91, 5, 4097, 81, 5, 5, 89, 5, 1025, 85, 5, 65, 93, 5, 16385, 80, 5, 3, 88, 5, 513, 84, 5, 33, 92, 5, 8193, 82, 5, 9, 90, 5, 2049, 86, 5, 129, 192, 5, 24577, 80, 5, 2, 87, 5, 385, 83, 5, 25, 91, 5, 6145, 81, 5, 7, 89, 5, 1537, 85, 5, 97, 93, 5, 24577, 80, 5, 4, 88, 5, 769, 84, 5, 49, 92, 5, 12289, 82, 5, 13, 90, 5, 3073, 86, 5, 193, 192, 5, 24577}; + + // Tables for deflate from PKZIP's appnote.txt. + //UPGRADE_NOTE: Final was removed from the declaration of 'cplens'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + internal static readonly int[] cplens = new int[]{3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + + // see note #13 above about 258 + //UPGRADE_NOTE: Final was removed from the declaration of 'cplext'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + internal static readonly int[] cplext = new int[]{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 112, 112}; + + //UPGRADE_NOTE: Final was removed from the declaration of 'cpdist'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + internal static readonly int[] cpdist = new int[]{1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577}; + + //UPGRADE_NOTE: Final was removed from the declaration of 'cpdext'. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'" + internal static readonly int[] cpdext = new int[]{0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}; + + // If BMAX needs to be larger than 16, then h and x[] should be uLong. + internal const int BMAX = 15; // maximum bit length of any code + + internal int[] hn = null; // hufts used in space + internal int[] v = null; // work area for huft_build + internal int[] c = null; // bit length count table + internal int[] r = null; // table entry for structure assignment + internal int[] u = null; // table stack + internal int[] x = null; // bit offsets, then code stack + + private int huft_build(int[] b, int bindex, int n, int s, int[] d, int[] e, int[] t, int[] m, int[] hp, int[] hn, int[] v) + { + // Given a list of code lengths and a maximum table size, make a set of + // tables to decode that set of codes. Return Z_OK on success, Z_BUF_ERROR + // if the given code set is incomplete (the tables are still built in this + // case), Z_DATA_ERROR if the input is invalid (an over-subscribed set of + // lengths), or Z_MEM_ERROR if not enough memory. + + int a; // counter for codes of length k + int f; // i repeats in table every f entries + int g; // maximum code length + int h; // table level + int i; // counter, current code + int j; // counter + int k; // number of bits in current code + int l; // bits per table (returned in m) + int mask; // (1 << w) - 1, to avoid cc -O bug on HP + int p; // pointer into c[], b[], or v[] + int q; // points to current table + int w; // bits before this table == (l * h) + int xp; // pointer into x + int y; // number of dummy codes added + int z; // number of entries in current table + + // Generate counts for each bit length + + p = 0; i = n; + do + { + c[b[bindex + p]]++; p++; i--; // assume all entries <= BMAX + } + while (i != 0); + + if (c[0] == n) + { + // null input--all zero length codes + t[0] = - 1; + m[0] = 0; + return Z_OK; + } + + // Find minimum and maximum length, bound *m by those + l = m[0]; + for (j = 1; j <= BMAX; j++) + if (c[j] != 0) + break; + k = j; // minimum code length + if (l < j) + { + l = j; + } + for (i = BMAX; i != 0; i--) + { + if (c[i] != 0) + break; + } + g = i; // maximum code length + if (l > i) + { + l = i; + } + m[0] = l; + + // Adjust last length count to fill out codes, if needed + for (y = 1 << j; j < i; j++, y <<= 1) + { + if ((y -= c[j]) < 0) + { + return Z_DATA_ERROR; + } + } + if ((y -= c[i]) < 0) + { + return Z_DATA_ERROR; + } + c[i] += y; + + // Generate starting offsets into the value table for each length + x[1] = j = 0; + p = 1; xp = 2; + while (--i != 0) + { + // note that i == g from above + x[xp] = (j += c[p]); + xp++; + p++; + } + + // Make a table of values in order of bit lengths + i = 0; p = 0; + do + { + if ((j = b[bindex + p]) != 0) + { + v[x[j]++] = i; + } + p++; + } + while (++i < n); + n = x[g]; // set n to length of v + + // Generate the Huffman codes and for each, make the table entries + x[0] = i = 0; // first Huffman code is zero + p = 0; // grab values in bit order + h = - 1; // no tables yet--level -1 + w = - l; // bits decoded == (l * h) + u[0] = 0; // just to keep compilers happy + q = 0; // ditto + z = 0; // ditto + + // go through the bit lengths (k already is bits in shortest code) + for (; k <= g; k++) + { + a = c[k]; + while (a-- != 0) + { + // here i is the Huffman code of length k bits for value *p + // make tables up to required level + while (k > w + l) + { + h++; + w += l; // previous table always l bits + // compute minimum size table less than or equal to l bits + z = g - w; + z = (z > l)?l:z; // table size upper limit + if ((f = 1 << (j = k - w)) > a + 1) + { + // try a k-w bit table + // too few codes for k-w bit table + f -= (a + 1); // deduct codes from patterns left + xp = k; + if (j < z) + { + while (++j < z) + { + // try smaller tables up to z bits + if ((f <<= 1) <= c[++xp]) + break; // enough codes to use up j bits + f -= c[xp]; // else deduct codes from patterns + } + } + } + z = 1 << j; // table entries for j-bit table + + // allocate new table + if (hn[0] + z > MANY) + { + // (note: doesn't matter for fixed) + return Z_DATA_ERROR; // overflow of MANY + } + u[h] = q = hn[0]; // DEBUG + hn[0] += z; + + // connect to last table, if there is one + if (h != 0) + { + x[h] = i; // save pattern for backing up + r[0] = (sbyte) j; // bits in this table + r[1] = (sbyte) l; // bits to dump before this table + j = SharedUtils.URShift(i, (w - l)); + r[2] = (int) (q - u[h - 1] - j); // offset to this table + Array.Copy(r, 0, hp, (u[h - 1] + j) * 3, 3); // connect to last table + } + else + { + t[0] = q; // first table is returned result + } + } + + // set up table entry in r + r[1] = (sbyte) (k - w); + if (p >= n) + { + r[0] = 128 + 64; // out of values--invalid code + } + else if (v[p] < s) + { + r[0] = (sbyte) (v[p] < 256?0:32 + 64); // 256 is end-of-block + r[2] = v[p++]; // simple code is just the value + } + else + { + r[0] = (sbyte) (e[v[p] - s] + 16 + 64); // non-simple--look up in lists + r[2] = d[v[p++] - s]; + } + + // fill code-like entries with r + f = 1 << (k - w); + for (j = SharedUtils.URShift(i, w); j < z; j += f) + { + Array.Copy(r, 0, hp, (q + j) * 3, 3); + } + + // backwards increment the k-bit code i + for (j = 1 << (k - 1); (i & j) != 0; j = SharedUtils.URShift(j, 1)) + { + i ^= j; + } + i ^= j; + + // backup over finished tables + mask = (1 << w) - 1; // needed on HP, cc -O bug + while ((i & mask) != x[h]) + { + h--; // don't need to update q + w -= l; + mask = (1 << w) - 1; + } + } + } + // Return Z_BUF_ERROR if we were given an incomplete table + return y != 0 && g != 1?Z_BUF_ERROR:Z_OK; + } + + internal int inflate_trees_bits(int[] c, int[] bb, int[] tb, int[] hp, ZlibCodec z) + { + int result; + initWorkArea(19); + hn[0] = 0; + result = huft_build(c, 0, 19, 19, null, null, tb, bb, hp, hn, v); + + if (result == Z_DATA_ERROR) + { + z.Message = "oversubscribed dynamic bit lengths tree"; + } + else if (result == Z_BUF_ERROR || bb[0] == 0) + { + z.Message = "incomplete dynamic bit lengths tree"; + result = Z_DATA_ERROR; + } + return result; + } + + internal int inflate_trees_dynamic(int nl, int nd, int[] c, int[] bl, int[] bd, int[] tl, int[] td, int[] hp, ZlibCodec z) + { + int result; + + // build literal/length tree + initWorkArea(288); + hn[0] = 0; + result = huft_build(c, 0, nl, 257, cplens, cplext, tl, bl, hp, hn, v); + if (result != Z_OK || bl[0] == 0) + { + if (result == Z_DATA_ERROR) + { + z.Message = "oversubscribed literal/length tree"; + } + else if (result != Z_MEM_ERROR) + { + z.Message = "incomplete literal/length tree"; + result = Z_DATA_ERROR; + } + return result; + } + + // build distance tree + initWorkArea(288); + result = huft_build(c, nl, nd, 0, cpdist, cpdext, td, bd, hp, hn, v); + + if (result != Z_OK || (bd[0] == 0 && nl > 257)) + { + if (result == Z_DATA_ERROR) + { + z.Message = "oversubscribed distance tree"; + } + else if (result == Z_BUF_ERROR) + { + z.Message = "incomplete distance tree"; + result = Z_DATA_ERROR; + } + else if (result != Z_MEM_ERROR) + { + z.Message = "empty distance tree with lengths"; + result = Z_DATA_ERROR; + } + return result; + } + + return Z_OK; + } + + internal static int inflate_trees_fixed(int[] bl, int[] bd, int[][] tl, int[][] td, ZlibCodec z) + { + bl[0] = fixed_bl; + bd[0] = fixed_bd; + tl[0] = fixed_tl; + td[0] = fixed_td; + return Z_OK; + } + + private void initWorkArea(int vsize) + { + if (hn == null) + { + hn = new int[1]; + v = new int[vsize]; + c = new int[BMAX + 1]; + r = new int[3]; + u = new int[BMAX]; + x = new int[BMAX + 1]; + } + else + { + if (v.Length < vsize) + { + v = new int[vsize]; + } + Array.Clear(v,0,vsize); + Array.Clear(c,0,BMAX+1); + r[0]=0; r[1]=0; r[2]=0; + // for(int i=0; i +// +// ------------------------------------------------------------------ +// +// This module defines classes for decompression. This code is derived +// from the jzlib implementation of zlib, but significantly modified. +// The object model is not the same, and many of the behaviors are +// different. Nonetheless, in keeping with the license for jzlib, I am +// reproducing the copyright to that code here. +// +// ------------------------------------------------------------------ +// +// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in +// the documentation and/or other materials provided with the distribution. +// +// 3. The names of the authors may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// ----------------------------------------------------------------------- +// +// This program is based on zlib-1.1.3; credit to authors +// Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) +// and contributors of zlib. +// +// ----------------------------------------------------------------------- + + +using System; +namespace Ionic.Zlib +{ + sealed class InflateBlocks + { + private const int MANY = 1440; + + // Table for deflate from PKZIP's appnote.txt. + internal static readonly int[] border = new int[] + { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; + + private enum InflateBlockMode + { + TYPE = 0, // get type bits (3, including end bit) + LENS = 1, // get lengths for stored + STORED = 2, // processing stored block + TABLE = 3, // get table lengths + BTREE = 4, // get bit lengths tree for a dynamic block + DTREE = 5, // get length, distance trees for a dynamic block + CODES = 6, // processing fixed or dynamic block + DRY = 7, // output remaining window bytes + DONE = 8, // finished last block, done + BAD = 9, // ot a data error--stuck here + } + + private InflateBlockMode mode; // current inflate_block mode + + internal int left; // if STORED, bytes left to copy + + internal int table; // table lengths (14 bits) + internal int index; // index into blens (or border) + internal int[] blens; // bit lengths of codes + internal int[] bb = new int[1]; // bit length tree depth + internal int[] tb = new int[1]; // bit length decoding tree + + internal InflateCodes codes = new InflateCodes(); // if CODES, current state + + internal int last; // true if this block is the last block + + internal ZlibCodec _codec; // pointer back to this zlib stream + + // mode independent information + internal int bitk; // bits in bit buffer + internal int bitb; // bit buffer + internal int[] hufts; // single malloc for tree space + internal byte[] window; // sliding window + internal int end; // one byte after sliding window + internal int readAt; // window read pointer + internal int writeAt; // window write pointer + internal System.Object checkfn; // check function + internal uint check; // check on output + + internal InfTree inftree = new InfTree(); + + internal InflateBlocks(ZlibCodec codec, System.Object checkfn, int w) + { + _codec = codec; + hufts = new int[MANY * 3]; + window = new byte[w]; + end = w; + this.checkfn = checkfn; + mode = InflateBlockMode.TYPE; + Reset(); + } + + internal uint Reset() + { + uint oldCheck = check; + mode = InflateBlockMode.TYPE; + bitk = 0; + bitb = 0; + readAt = writeAt = 0; + + if (checkfn != null) + _codec._Adler32 = check = Adler.Adler32(0, null, 0, 0); + return oldCheck; + } + + + internal int Process(int r) + { + int t; // temporary storage + int b; // bit buffer + int k; // bits in bit buffer + int p; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + + // copy input/output information to locals (UPDATE macro restores) + + p = _codec.NextIn; + n = _codec.AvailableBytesIn; + b = bitb; + k = bitk; + + q = writeAt; + m = (int)(q < readAt ? readAt - q - 1 : end - q); + + + // process input based on current state + while (true) + { + switch (mode) + { + case InflateBlockMode.TYPE: + + while (k < (3)) + { + if (n != 0) + { + r = ZlibConstants.Z_OK; + } + else + { + bitb = b; bitk = k; + _codec.AvailableBytesIn = n; + _codec.TotalBytesIn += p - _codec.NextIn; + _codec.NextIn = p; + writeAt = q; + return Flush(r); + } + + n--; + b |= (_codec.InputBuffer[p++] & 0xff) << k; + k += 8; + } + t = (int)(b & 7); + last = t & 1; + + switch ((uint)t >> 1) + { + case 0: // stored + b >>= 3; k -= (3); + t = k & 7; // go to byte boundary + b >>= t; k -= t; + mode = InflateBlockMode.LENS; // get length of stored block + break; + + case 1: // fixed + int[] bl = new int[1]; + int[] bd = new int[1]; + int[][] tl = new int[1][]; + int[][] td = new int[1][]; + InfTree.inflate_trees_fixed(bl, bd, tl, td, _codec); + codes.Init(bl[0], bd[0], tl[0], 0, td[0], 0); + b >>= 3; k -= 3; + mode = InflateBlockMode.CODES; + break; + + case 2: // dynamic + b >>= 3; k -= 3; + mode = InflateBlockMode.TABLE; + break; + + case 3: // illegal + b >>= 3; k -= 3; + mode = InflateBlockMode.BAD; + _codec.Message = "invalid block type"; + r = ZlibConstants.Z_DATA_ERROR; + bitb = b; bitk = k; + _codec.AvailableBytesIn = n; + _codec.TotalBytesIn += p - _codec.NextIn; + _codec.NextIn = p; + writeAt = q; + return Flush(r); + } + break; + + case InflateBlockMode.LENS: + + while (k < (32)) + { + if (n != 0) + { + r = ZlibConstants.Z_OK; + } + else + { + bitb = b; bitk = k; + _codec.AvailableBytesIn = n; + _codec.TotalBytesIn += p - _codec.NextIn; + _codec.NextIn = p; + writeAt = q; + return Flush(r); + } + ; + n--; + b |= (_codec.InputBuffer[p++] & 0xff) << k; + k += 8; + } + + if ( ( ((~b)>>16) & 0xffff) != (b & 0xffff)) + { + mode = InflateBlockMode.BAD; + _codec.Message = "invalid stored block lengths"; + r = ZlibConstants.Z_DATA_ERROR; + + bitb = b; bitk = k; + _codec.AvailableBytesIn = n; + _codec.TotalBytesIn += p - _codec.NextIn; + _codec.NextIn = p; + writeAt = q; + return Flush(r); + } + left = (b & 0xffff); + b = k = 0; // dump bits + mode = left != 0 ? InflateBlockMode.STORED : (last != 0 ? InflateBlockMode.DRY : InflateBlockMode.TYPE); + break; + + case InflateBlockMode.STORED: + if (n == 0) + { + bitb = b; bitk = k; + _codec.AvailableBytesIn = n; + _codec.TotalBytesIn += p - _codec.NextIn; + _codec.NextIn = p; + writeAt = q; + return Flush(r); + } + + if (m == 0) + { + if (q == end && readAt != 0) + { + q = 0; m = (int)(q < readAt ? readAt - q - 1 : end - q); + } + if (m == 0) + { + writeAt = q; + r = Flush(r); + q = writeAt; m = (int)(q < readAt ? readAt - q - 1 : end - q); + if (q == end && readAt != 0) + { + q = 0; m = (int)(q < readAt ? readAt - q - 1 : end - q); + } + if (m == 0) + { + bitb = b; bitk = k; + _codec.AvailableBytesIn = n; + _codec.TotalBytesIn += p - _codec.NextIn; + _codec.NextIn = p; + writeAt = q; + return Flush(r); + } + } + } + r = ZlibConstants.Z_OK; + + t = left; + if (t > n) + t = n; + if (t > m) + t = m; + Array.Copy(_codec.InputBuffer, p, window, q, t); + p += t; n -= t; + q += t; m -= t; + if ((left -= t) != 0) + break; + mode = last != 0 ? InflateBlockMode.DRY : InflateBlockMode.TYPE; + break; + + case InflateBlockMode.TABLE: + + while (k < (14)) + { + if (n != 0) + { + r = ZlibConstants.Z_OK; + } + else + { + bitb = b; bitk = k; + _codec.AvailableBytesIn = n; + _codec.TotalBytesIn += p - _codec.NextIn; + _codec.NextIn = p; + writeAt = q; + return Flush(r); + } + + n--; + b |= (_codec.InputBuffer[p++] & 0xff) << k; + k += 8; + } + + table = t = (b & 0x3fff); + if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29) + { + mode = InflateBlockMode.BAD; + _codec.Message = "too many length or distance symbols"; + r = ZlibConstants.Z_DATA_ERROR; + + bitb = b; bitk = k; + _codec.AvailableBytesIn = n; + _codec.TotalBytesIn += p - _codec.NextIn; + _codec.NextIn = p; + writeAt = q; + return Flush(r); + } + t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f); + if (blens == null || blens.Length < t) + { + blens = new int[t]; + } + else + { + Array.Clear(blens, 0, t); + // for (int i = 0; i < t; i++) + // { + // blens[i] = 0; + // } + } + + b >>= 14; + k -= 14; + + + index = 0; + mode = InflateBlockMode.BTREE; + goto case InflateBlockMode.BTREE; + + case InflateBlockMode.BTREE: + while (index < 4 + (table >> 10)) + { + while (k < (3)) + { + if (n != 0) + { + r = ZlibConstants.Z_OK; + } + else + { + bitb = b; bitk = k; + _codec.AvailableBytesIn = n; + _codec.TotalBytesIn += p - _codec.NextIn; + _codec.NextIn = p; + writeAt = q; + return Flush(r); + } + + n--; + b |= (_codec.InputBuffer[p++] & 0xff) << k; + k += 8; + } + + blens[border[index++]] = b & 7; + + b >>= 3; k -= 3; + } + + while (index < 19) + { + blens[border[index++]] = 0; + } + + bb[0] = 7; + t = inftree.inflate_trees_bits(blens, bb, tb, hufts, _codec); + if (t != ZlibConstants.Z_OK) + { + r = t; + if (r == ZlibConstants.Z_DATA_ERROR) + { + blens = null; + mode = InflateBlockMode.BAD; + } + + bitb = b; bitk = k; + _codec.AvailableBytesIn = n; + _codec.TotalBytesIn += p - _codec.NextIn; + _codec.NextIn = p; + writeAt = q; + return Flush(r); + } + + index = 0; + mode = InflateBlockMode.DTREE; + goto case InflateBlockMode.DTREE; + + case InflateBlockMode.DTREE: + while (true) + { + t = table; + if (!(index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))) + { + break; + } + + int i, j, c; + + t = bb[0]; + + while (k < t) + { + if (n != 0) + { + r = ZlibConstants.Z_OK; + } + else + { + bitb = b; bitk = k; + _codec.AvailableBytesIn = n; + _codec.TotalBytesIn += p - _codec.NextIn; + _codec.NextIn = p; + writeAt = q; + return Flush(r); + } + + n--; + b |= (_codec.InputBuffer[p++] & 0xff) << k; + k += 8; + } + + t = hufts[(tb[0] + (b & InternalInflateConstants.InflateMask[t])) * 3 + 1]; + c = hufts[(tb[0] + (b & InternalInflateConstants.InflateMask[t])) * 3 + 2]; + + if (c < 16) + { + b >>= t; k -= t; + blens[index++] = c; + } + else + { + // c == 16..18 + i = c == 18 ? 7 : c - 14; + j = c == 18 ? 11 : 3; + + while (k < (t + i)) + { + if (n != 0) + { + r = ZlibConstants.Z_OK; + } + else + { + bitb = b; bitk = k; + _codec.AvailableBytesIn = n; + _codec.TotalBytesIn += p - _codec.NextIn; + _codec.NextIn = p; + writeAt = q; + return Flush(r); + } + + n--; + b |= (_codec.InputBuffer[p++] & 0xff) << k; + k += 8; + } + + b >>= t; k -= t; + + j += (b & InternalInflateConstants.InflateMask[i]); + + b >>= i; k -= i; + + i = index; + t = table; + if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) || (c == 16 && i < 1)) + { + blens = null; + mode = InflateBlockMode.BAD; + _codec.Message = "invalid bit length repeat"; + r = ZlibConstants.Z_DATA_ERROR; + + bitb = b; bitk = k; + _codec.AvailableBytesIn = n; + _codec.TotalBytesIn += p - _codec.NextIn; + _codec.NextIn = p; + writeAt = q; + return Flush(r); + } + + c = (c == 16) ? blens[i-1] : 0; + do + { + blens[i++] = c; + } + while (--j != 0); + index = i; + } + } + + tb[0] = -1; + { + int[] bl = new int[] { 9 }; // must be <= 9 for lookahead assumptions + int[] bd = new int[] { 6 }; // must be <= 9 for lookahead assumptions + int[] tl = new int[1]; + int[] td = new int[1]; + + t = table; + t = inftree.inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f), blens, bl, bd, tl, td, hufts, _codec); + + if (t != ZlibConstants.Z_OK) + { + if (t == ZlibConstants.Z_DATA_ERROR) + { + blens = null; + mode = InflateBlockMode.BAD; + } + r = t; + + bitb = b; bitk = k; + _codec.AvailableBytesIn = n; + _codec.TotalBytesIn += p - _codec.NextIn; + _codec.NextIn = p; + writeAt = q; + return Flush(r); + } + codes.Init(bl[0], bd[0], hufts, tl[0], hufts, td[0]); + } + mode = InflateBlockMode.CODES; + goto case InflateBlockMode.CODES; + + case InflateBlockMode.CODES: + bitb = b; bitk = k; + _codec.AvailableBytesIn = n; + _codec.TotalBytesIn += p - _codec.NextIn; + _codec.NextIn = p; + writeAt = q; + + r = codes.Process(this, r); + if (r != ZlibConstants.Z_STREAM_END) + { + return Flush(r); + } + + r = ZlibConstants.Z_OK; + p = _codec.NextIn; + n = _codec.AvailableBytesIn; + b = bitb; + k = bitk; + q = writeAt; + m = (int)(q < readAt ? readAt - q - 1 : end - q); + + if (last == 0) + { + mode = InflateBlockMode.TYPE; + break; + } + mode = InflateBlockMode.DRY; + goto case InflateBlockMode.DRY; + + case InflateBlockMode.DRY: + writeAt = q; + r = Flush(r); + q = writeAt; m = (int)(q < readAt ? readAt - q - 1 : end - q); + if (readAt != writeAt) + { + bitb = b; bitk = k; + _codec.AvailableBytesIn = n; + _codec.TotalBytesIn += p - _codec.NextIn; + _codec.NextIn = p; + writeAt = q; + return Flush(r); + } + mode = InflateBlockMode.DONE; + goto case InflateBlockMode.DONE; + + case InflateBlockMode.DONE: + r = ZlibConstants.Z_STREAM_END; + bitb = b; + bitk = k; + _codec.AvailableBytesIn = n; + _codec.TotalBytesIn += p - _codec.NextIn; + _codec.NextIn = p; + writeAt = q; + return Flush(r); + + case InflateBlockMode.BAD: + r = ZlibConstants.Z_DATA_ERROR; + + bitb = b; bitk = k; + _codec.AvailableBytesIn = n; + _codec.TotalBytesIn += p - _codec.NextIn; + _codec.NextIn = p; + writeAt = q; + return Flush(r); + + + default: + r = ZlibConstants.Z_STREAM_ERROR; + + bitb = b; bitk = k; + _codec.AvailableBytesIn = n; + _codec.TotalBytesIn += p - _codec.NextIn; + _codec.NextIn = p; + writeAt = q; + return Flush(r); + } + } + } + + + internal void Free() + { + Reset(); + window = null; + hufts = null; + } + + internal void SetDictionary(byte[] d, int start, int n) + { + Array.Copy(d, start, window, 0, n); + readAt = writeAt = n; + } + + // Returns true if inflate is currently at the end of a block generated + // by Z_SYNC_FLUSH or Z_FULL_FLUSH. + internal int SyncPoint() + { + return mode == InflateBlockMode.LENS ? 1 : 0; + } + + // copy as much as possible from the sliding window to the output area + internal int Flush(int r) + { + int nBytes; + + for (int pass=0; pass < 2; pass++) + { + if (pass==0) + { + // compute number of bytes to copy as far as end of window + nBytes = (int)((readAt <= writeAt ? writeAt : end) - readAt); + } + else + { + // compute bytes to copy + nBytes = writeAt - readAt; + } + + // workitem 8870 + if (nBytes == 0) + { + if (r == ZlibConstants.Z_BUF_ERROR) + r = ZlibConstants.Z_OK; + return r; + } + + if (nBytes > _codec.AvailableBytesOut) + nBytes = _codec.AvailableBytesOut; + + if (nBytes != 0 && r == ZlibConstants.Z_BUF_ERROR) + r = ZlibConstants.Z_OK; + + // update counters + _codec.AvailableBytesOut -= nBytes; + _codec.TotalBytesOut += nBytes; + + // update check information + if (checkfn != null) + _codec._Adler32 = check = Adler.Adler32(check, window, readAt, nBytes); + + // copy as far as end of window + Array.Copy(window, readAt, _codec.OutputBuffer, _codec.NextOut, nBytes); + _codec.NextOut += nBytes; + readAt += nBytes; + + // see if more to copy at beginning of window + if (readAt == end && pass == 0) + { + // wrap pointers + readAt = 0; + if (writeAt == end) + writeAt = 0; + } + else pass++; + } + + // done + return r; + } + } + + + internal static class InternalInflateConstants + { + // And'ing with mask[n] masks the lower n bits + internal static readonly int[] InflateMask = new int[] { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, + 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, + 0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff, + 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff }; + } + + + sealed class InflateCodes + { + // waiting for "i:"=input, + // "o:"=output, + // "x:"=nothing + private const int START = 0; // x: set up for LEN + private const int LEN = 1; // i: get length/literal/eob next + private const int LENEXT = 2; // i: getting length extra (have base) + private const int DIST = 3; // i: get distance next + private const int DISTEXT = 4; // i: getting distance extra + private const int COPY = 5; // o: copying bytes in window, waiting for space + private const int LIT = 6; // o: got literal, waiting for output space + private const int WASH = 7; // o: got eob, possibly still output waiting + private const int END = 8; // x: got eob and all data flushed + private const int BADCODE = 9; // x: got error + + internal int mode; // current inflate_codes mode + + // mode dependent information + internal int len; + + internal int[] tree; // pointer into tree + internal int tree_index = 0; + internal int need; // bits needed + + internal int lit; + + // if EXT or COPY, where and how much + internal int bitsToGet; // bits to get for extra + internal int dist; // distance back to copy from + + internal byte lbits; // ltree bits decoded per branch + internal byte dbits; // dtree bits decoder per branch + internal int[] ltree; // literal/length/eob tree + internal int ltree_index; // literal/length/eob tree + internal int[] dtree; // distance tree + internal int dtree_index; // distance tree + + internal InflateCodes() + { + } + + internal void Init(int bl, int bd, int[] tl, int tl_index, int[] td, int td_index) + { + mode = START; + lbits = (byte)bl; + dbits = (byte)bd; + ltree = tl; + ltree_index = tl_index; + dtree = td; + dtree_index = td_index; + tree = null; + } + + internal int Process(InflateBlocks blocks, int r) + { + int j; // temporary storage + int tindex; // temporary pointer + int e; // extra bits or operation + int b = 0; // bit buffer + int k = 0; // bits in bit buffer + int p = 0; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int f; // pointer to copy strings from + + ZlibCodec z = blocks._codec; + + // copy input/output information to locals (UPDATE macro restores) + p = z.NextIn; + n = z.AvailableBytesIn; + b = blocks.bitb; + k = blocks.bitk; + q = blocks.writeAt; m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q; + + // process input and output based on current state + while (true) + { + switch (mode) + { + // waiting for "i:"=input, "o:"=output, "x:"=nothing + case START: // x: set up for LEN + if (m >= 258 && n >= 10) + { + blocks.bitb = b; blocks.bitk = k; + z.AvailableBytesIn = n; + z.TotalBytesIn += p - z.NextIn; + z.NextIn = p; + blocks.writeAt = q; + r = InflateFast(lbits, dbits, ltree, ltree_index, dtree, dtree_index, blocks, z); + + p = z.NextIn; + n = z.AvailableBytesIn; + b = blocks.bitb; + k = blocks.bitk; + q = blocks.writeAt; m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q; + + if (r != ZlibConstants.Z_OK) + { + mode = (r == ZlibConstants.Z_STREAM_END) ? WASH : BADCODE; + break; + } + } + need = lbits; + tree = ltree; + tree_index = ltree_index; + + mode = LEN; + goto case LEN; + + case LEN: // i: get length/literal/eob next + j = need; + + while (k < j) + { + if (n != 0) + r = ZlibConstants.Z_OK; + else + { + blocks.bitb = b; blocks.bitk = k; + z.AvailableBytesIn = n; + z.TotalBytesIn += p - z.NextIn; + z.NextIn = p; + blocks.writeAt = q; + return blocks.Flush(r); + } + n--; + b |= (z.InputBuffer[p++] & 0xff) << k; + k += 8; + } + + tindex = (tree_index + (b & InternalInflateConstants.InflateMask[j])) * 3; + + b >>= (tree[tindex + 1]); + k -= (tree[tindex + 1]); + + e = tree[tindex]; + + if (e == 0) + { + // literal + lit = tree[tindex + 2]; + mode = LIT; + break; + } + if ((e & 16) != 0) + { + // length + bitsToGet = e & 15; + len = tree[tindex + 2]; + mode = LENEXT; + break; + } + if ((e & 64) == 0) + { + // next table + need = e; + tree_index = tindex / 3 + tree[tindex + 2]; + break; + } + if ((e & 32) != 0) + { + // end of block + mode = WASH; + break; + } + mode = BADCODE; // invalid code + z.Message = "invalid literal/length code"; + r = ZlibConstants.Z_DATA_ERROR; + + blocks.bitb = b; blocks.bitk = k; + z.AvailableBytesIn = n; + z.TotalBytesIn += p - z.NextIn; + z.NextIn = p; + blocks.writeAt = q; + return blocks.Flush(r); + + + case LENEXT: // i: getting length extra (have base) + j = bitsToGet; + + while (k < j) + { + if (n != 0) + r = ZlibConstants.Z_OK; + else + { + blocks.bitb = b; blocks.bitk = k; + z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p; + blocks.writeAt = q; + return blocks.Flush(r); + } + n--; b |= (z.InputBuffer[p++] & 0xff) << k; + k += 8; + } + + len += (b & InternalInflateConstants.InflateMask[j]); + + b >>= j; + k -= j; + + need = dbits; + tree = dtree; + tree_index = dtree_index; + mode = DIST; + goto case DIST; + + case DIST: // i: get distance next + j = need; + + while (k < j) + { + if (n != 0) + r = ZlibConstants.Z_OK; + else + { + blocks.bitb = b; blocks.bitk = k; + z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p; + blocks.writeAt = q; + return blocks.Flush(r); + } + n--; b |= (z.InputBuffer[p++] & 0xff) << k; + k += 8; + } + + tindex = (tree_index + (b & InternalInflateConstants.InflateMask[j])) * 3; + + b >>= tree[tindex + 1]; + k -= tree[tindex + 1]; + + e = (tree[tindex]); + if ((e & 0x10) != 0) + { + // distance + bitsToGet = e & 15; + dist = tree[tindex + 2]; + mode = DISTEXT; + break; + } + if ((e & 64) == 0) + { + // next table + need = e; + tree_index = tindex / 3 + tree[tindex + 2]; + break; + } + mode = BADCODE; // invalid code + z.Message = "invalid distance code"; + r = ZlibConstants.Z_DATA_ERROR; + + blocks.bitb = b; blocks.bitk = k; + z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p; + blocks.writeAt = q; + return blocks.Flush(r); + + + case DISTEXT: // i: getting distance extra + j = bitsToGet; + + while (k < j) + { + if (n != 0) + r = ZlibConstants.Z_OK; + else + { + blocks.bitb = b; blocks.bitk = k; + z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p; + blocks.writeAt = q; + return blocks.Flush(r); + } + n--; b |= (z.InputBuffer[p++] & 0xff) << k; + k += 8; + } + + dist += (b & InternalInflateConstants.InflateMask[j]); + + b >>= j; + k -= j; + + mode = COPY; + goto case COPY; + + case COPY: // o: copying bytes in window, waiting for space + f = q - dist; + while (f < 0) + { + // modulo window size-"while" instead + f += blocks.end; // of "if" handles invalid distances + } + while (len != 0) + { + if (m == 0) + { + if (q == blocks.end && blocks.readAt != 0) + { + q = 0; m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q; + } + if (m == 0) + { + blocks.writeAt = q; r = blocks.Flush(r); + q = blocks.writeAt; m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q; + + if (q == blocks.end && blocks.readAt != 0) + { + q = 0; m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q; + } + + if (m == 0) + { + blocks.bitb = b; blocks.bitk = k; + z.AvailableBytesIn = n; + z.TotalBytesIn += p - z.NextIn; + z.NextIn = p; + blocks.writeAt = q; + return blocks.Flush(r); + } + } + } + + blocks.window[q++] = blocks.window[f++]; m--; + + if (f == blocks.end) + f = 0; + len--; + } + mode = START; + break; + + case LIT: // o: got literal, waiting for output space + if (m == 0) + { + if (q == blocks.end && blocks.readAt != 0) + { + q = 0; m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q; + } + if (m == 0) + { + blocks.writeAt = q; r = blocks.Flush(r); + q = blocks.writeAt; m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q; + + if (q == blocks.end && blocks.readAt != 0) + { + q = 0; m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q; + } + if (m == 0) + { + blocks.bitb = b; blocks.bitk = k; + z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p; + blocks.writeAt = q; + return blocks.Flush(r); + } + } + } + r = ZlibConstants.Z_OK; + + blocks.window[q++] = (byte)lit; m--; + + mode = START; + break; + + case WASH: // o: got eob, possibly more output + if (k > 7) + { + // return unused byte, if any + k -= 8; + n++; + p--; // can always return one + } + + blocks.writeAt = q; r = blocks.Flush(r); + q = blocks.writeAt; m = q < blocks.readAt ? blocks.readAt - q - 1 : blocks.end - q; + + if (blocks.readAt != blocks.writeAt) + { + blocks.bitb = b; blocks.bitk = k; + z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p; + blocks.writeAt = q; + return blocks.Flush(r); + } + mode = END; + goto case END; + + case END: + r = ZlibConstants.Z_STREAM_END; + blocks.bitb = b; blocks.bitk = k; + z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p; + blocks.writeAt = q; + return blocks.Flush(r); + + case BADCODE: // x: got error + + r = ZlibConstants.Z_DATA_ERROR; + + blocks.bitb = b; blocks.bitk = k; + z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p; + blocks.writeAt = q; + return blocks.Flush(r); + + default: + r = ZlibConstants.Z_STREAM_ERROR; + + blocks.bitb = b; blocks.bitk = k; + z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p; + blocks.writeAt = q; + return blocks.Flush(r); + } + } + } + + + // Called with number of bytes left to write in window at least 258 + // (the maximum string length) and number of input bytes available + // at least ten. The ten bytes are six bytes for the longest length/ + // distance pair plus four bytes for overloading the bit buffer. + + internal int InflateFast(int bl, int bd, int[] tl, int tl_index, int[] td, int td_index, InflateBlocks s, ZlibCodec z) + { + int t; // temporary pointer + int[] tp; // temporary pointer + int tp_index; // temporary pointer + int e; // extra bits or operation + int b; // bit buffer + int k; // bits in bit buffer + int p; // input data pointer + int n; // bytes available there + int q; // output window write pointer + int m; // bytes to end of window or read pointer + int ml; // mask for literal/length tree + int md; // mask for distance tree + int c; // bytes to copy + int d; // distance back to copy from + int r; // copy source pointer + + int tp_index_t_3; // (tp_index+t)*3 + + // load input, output, bit values + p = z.NextIn; n = z.AvailableBytesIn; b = s.bitb; k = s.bitk; + q = s.writeAt; m = q < s.readAt ? s.readAt - q - 1 : s.end - q; + + // initialize masks + ml = InternalInflateConstants.InflateMask[bl]; + md = InternalInflateConstants.InflateMask[bd]; + + // do until not enough input or output space for fast loop + do + { + // assume called with m >= 258 && n >= 10 + // get literal/length code + while (k < (20)) + { + // max bits for literal/length code + n--; + b |= (z.InputBuffer[p++] & 0xff) << k; k += 8; + } + + t = b & ml; + tp = tl; + tp_index = tl_index; + tp_index_t_3 = (tp_index + t) * 3; + if ((e = tp[tp_index_t_3]) == 0) + { + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + s.window[q++] = (byte)tp[tp_index_t_3 + 2]; + m--; + continue; + } + do + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + if ((e & 16) != 0) + { + e &= 15; + c = tp[tp_index_t_3 + 2] + ((int)b & InternalInflateConstants.InflateMask[e]); + + b >>= e; k -= e; + + // decode distance base of block to copy + while (k < 15) + { + // max bits for distance code + n--; + b |= (z.InputBuffer[p++] & 0xff) << k; k += 8; + } + + t = b & md; + tp = td; + tp_index = td_index; + tp_index_t_3 = (tp_index + t) * 3; + e = tp[tp_index_t_3]; + + do + { + + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + + if ((e & 16) != 0) + { + // get extra bits to add to distance base + e &= 15; + while (k < e) + { + // get extra bits (up to 13) + n--; + b |= (z.InputBuffer[p++] & 0xff) << k; k += 8; + } + + d = tp[tp_index_t_3 + 2] + (b & InternalInflateConstants.InflateMask[e]); + + b >>= e; k -= e; + + // do the copy + m -= c; + if (q >= d) + { + // offset before dest + // just copy + r = q - d; + if (q - r > 0 && 2 > (q - r)) + { + s.window[q++] = s.window[r++]; // minimum count is three, + s.window[q++] = s.window[r++]; // so unroll loop a little + c -= 2; + } + else + { + Array.Copy(s.window, r, s.window, q, 2); + q += 2; r += 2; c -= 2; + } + } + else + { + // else offset after destination + r = q - d; + do + { + r += s.end; // force pointer in window + } + while (r < 0); // covers invalid distances + e = s.end - r; + if (c > e) + { + // if source crosses, + c -= e; // wrapped copy + if (q - r > 0 && e > (q - r)) + { + do + { + s.window[q++] = s.window[r++]; + } + while (--e != 0); + } + else + { + Array.Copy(s.window, r, s.window, q, e); + q += e; r += e; e = 0; + } + r = 0; // copy rest from start of window + } + } + + // copy all or what's left + if (q - r > 0 && c > (q - r)) + { + do + { + s.window[q++] = s.window[r++]; + } + while (--c != 0); + } + else + { + Array.Copy(s.window, r, s.window, q, c); + q += c; r += c; c = 0; + } + break; + } + else if ((e & 64) == 0) + { + t += tp[tp_index_t_3 + 2]; + t += (b & InternalInflateConstants.InflateMask[e]); + tp_index_t_3 = (tp_index + t) * 3; + e = tp[tp_index_t_3]; + } + else + { + z.Message = "invalid distance code"; + + c = z.AvailableBytesIn - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= (c << 3); + + s.bitb = b; s.bitk = k; + z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p; + s.writeAt = q; + + return ZlibConstants.Z_DATA_ERROR; + } + } + while (true); + break; + } + + if ((e & 64) == 0) + { + t += tp[tp_index_t_3 + 2]; + t += (b & InternalInflateConstants.InflateMask[e]); + tp_index_t_3 = (tp_index + t) * 3; + if ((e = tp[tp_index_t_3]) == 0) + { + b >>= (tp[tp_index_t_3 + 1]); k -= (tp[tp_index_t_3 + 1]); + s.window[q++] = (byte)tp[tp_index_t_3 + 2]; + m--; + break; + } + } + else if ((e & 32) != 0) + { + c = z.AvailableBytesIn - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= (c << 3); + + s.bitb = b; s.bitk = k; + z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p; + s.writeAt = q; + + return ZlibConstants.Z_STREAM_END; + } + else + { + z.Message = "invalid literal/length code"; + + c = z.AvailableBytesIn - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= (c << 3); + + s.bitb = b; s.bitk = k; + z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p; + s.writeAt = q; + + return ZlibConstants.Z_DATA_ERROR; + } + } + while (true); + } + while (m >= 258 && n >= 10); + + // not enough input or output--restore pointers and return + c = z.AvailableBytesIn - n; c = (k >> 3) < c ? k >> 3 : c; n += c; p -= c; k -= (c << 3); + + s.bitb = b; s.bitk = k; + z.AvailableBytesIn = n; z.TotalBytesIn += p - z.NextIn; z.NextIn = p; + s.writeAt = q; + + return ZlibConstants.Z_OK; + } + } + + + internal sealed class InflateManager + { + // preset dictionary flag in zlib header + private const int PRESET_DICT = 0x20; + + private const int Z_DEFLATED = 8; + + private enum InflateManagerMode + { + METHOD = 0, // waiting for method byte + FLAG = 1, // waiting for flag byte + DICT4 = 2, // four dictionary check bytes to go + DICT3 = 3, // three dictionary check bytes to go + DICT2 = 4, // two dictionary check bytes to go + DICT1 = 5, // one dictionary check byte to go + DICT0 = 6, // waiting for inflateSetDictionary + BLOCKS = 7, // decompressing blocks + CHECK4 = 8, // four check bytes to go + CHECK3 = 9, // three check bytes to go + CHECK2 = 10, // two check bytes to go + CHECK1 = 11, // one check byte to go + DONE = 12, // finished check, done + BAD = 13, // got an error--stay here + } + + private InflateManagerMode mode; // current inflate mode + internal ZlibCodec _codec; // pointer back to this zlib stream + + // mode dependent information + internal int method; // if FLAGS, method byte + + // if CHECK, check values to compare + internal uint computedCheck; // computed check value + internal uint expectedCheck; // stream check value + + // if BAD, inflateSync's marker bytes count + internal int marker; + + // mode independent information + //internal int nowrap; // flag for no wrapper + private bool _handleRfc1950HeaderBytes = true; + internal bool HandleRfc1950HeaderBytes + { + get { return _handleRfc1950HeaderBytes; } + set { _handleRfc1950HeaderBytes = value; } + } + internal int wbits; // log2(window size) (8..15, defaults to 15) + + internal InflateBlocks blocks; // current inflate_blocks state + + public InflateManager() { } + + public InflateManager(bool expectRfc1950HeaderBytes) + { + _handleRfc1950HeaderBytes = expectRfc1950HeaderBytes; + } + + internal int Reset() + { + _codec.TotalBytesIn = _codec.TotalBytesOut = 0; + _codec.Message = null; + mode = HandleRfc1950HeaderBytes ? InflateManagerMode.METHOD : InflateManagerMode.BLOCKS; + blocks.Reset(); + return ZlibConstants.Z_OK; + } + + internal int End() + { + if (blocks != null) + blocks.Free(); + blocks = null; + return ZlibConstants.Z_OK; + } + + internal int Initialize(ZlibCodec codec, int w) + { + _codec = codec; + _codec.Message = null; + blocks = null; + + // handle undocumented nowrap option (no zlib header or check) + //nowrap = 0; + //if (w < 0) + //{ + // w = - w; + // nowrap = 1; + //} + + // set window size + if (w < 8 || w > 15) + { + End(); + throw new ZlibException("Bad window size."); + + //return ZlibConstants.Z_STREAM_ERROR; + } + wbits = w; + + blocks = new InflateBlocks(codec, + HandleRfc1950HeaderBytes ? this : null, + 1 << w); + + // reset state + Reset(); + return ZlibConstants.Z_OK; + } + + + internal int Inflate(FlushType flush) + { + int b; + + if (_codec.InputBuffer == null) + throw new ZlibException("InputBuffer is null. "); + +// int f = (flush == FlushType.Finish) +// ? ZlibConstants.Z_BUF_ERROR +// : ZlibConstants.Z_OK; + + // workitem 8870 + int f = ZlibConstants.Z_OK; + int r = ZlibConstants.Z_BUF_ERROR; + + while (true) + { + switch (mode) + { + case InflateManagerMode.METHOD: + if (_codec.AvailableBytesIn == 0) return r; + r = f; + _codec.AvailableBytesIn--; + _codec.TotalBytesIn++; + if (((method = _codec.InputBuffer[_codec.NextIn++]) & 0xf) != Z_DEFLATED) + { + mode = InflateManagerMode.BAD; + _codec.Message = String.Format("unknown compression method (0x{0:X2})", method); + marker = 5; // can't try inflateSync + break; + } + if ((method >> 4) + 8 > wbits) + { + mode = InflateManagerMode.BAD; + _codec.Message = String.Format("invalid window size ({0})", (method >> 4) + 8); + marker = 5; // can't try inflateSync + break; + } + mode = InflateManagerMode.FLAG; + break; + + + case InflateManagerMode.FLAG: + if (_codec.AvailableBytesIn == 0) return r; + r = f; + _codec.AvailableBytesIn--; + _codec.TotalBytesIn++; + b = (_codec.InputBuffer[_codec.NextIn++]) & 0xff; + + if ((((method << 8) + b) % 31) != 0) + { + mode = InflateManagerMode.BAD; + _codec.Message = "incorrect header check"; + marker = 5; // can't try inflateSync + break; + } + + mode = ((b & PRESET_DICT) == 0) + ? InflateManagerMode.BLOCKS + : InflateManagerMode.DICT4; + break; + + case InflateManagerMode.DICT4: + if (_codec.AvailableBytesIn == 0) return r; + r = f; + _codec.AvailableBytesIn--; + _codec.TotalBytesIn++; + expectedCheck = (uint)((_codec.InputBuffer[_codec.NextIn++] << 24) & 0xff000000); + mode = InflateManagerMode.DICT3; + break; + + case InflateManagerMode.DICT3: + if (_codec.AvailableBytesIn == 0) return r; + r = f; + _codec.AvailableBytesIn--; + _codec.TotalBytesIn++; + expectedCheck += (uint)((_codec.InputBuffer[_codec.NextIn++] << 16) & 0x00ff0000); + mode = InflateManagerMode.DICT2; + break; + + case InflateManagerMode.DICT2: + + if (_codec.AvailableBytesIn == 0) return r; + r = f; + _codec.AvailableBytesIn--; + _codec.TotalBytesIn++; + expectedCheck += (uint)((_codec.InputBuffer[_codec.NextIn++] << 8) & 0x0000ff00); + mode = InflateManagerMode.DICT1; + break; + + + case InflateManagerMode.DICT1: + if (_codec.AvailableBytesIn == 0) return r; + r = f; + _codec.AvailableBytesIn--; _codec.TotalBytesIn++; + expectedCheck += (uint)(_codec.InputBuffer[_codec.NextIn++] & 0x000000ff); + _codec._Adler32 = expectedCheck; + mode = InflateManagerMode.DICT0; + return ZlibConstants.Z_NEED_DICT; + + + case InflateManagerMode.DICT0: + mode = InflateManagerMode.BAD; + _codec.Message = "need dictionary"; + marker = 0; // can try inflateSync + return ZlibConstants.Z_STREAM_ERROR; + + + case InflateManagerMode.BLOCKS: + r = blocks.Process(r); + if (r == ZlibConstants.Z_DATA_ERROR) + { + mode = InflateManagerMode.BAD; + marker = 0; // can try inflateSync + break; + } + + if (r == ZlibConstants.Z_OK) r = f; + + if (r != ZlibConstants.Z_STREAM_END) + return r; + + r = f; + computedCheck = blocks.Reset(); + if (!HandleRfc1950HeaderBytes) + { + mode = InflateManagerMode.DONE; + return ZlibConstants.Z_STREAM_END; + } + mode = InflateManagerMode.CHECK4; + break; + + case InflateManagerMode.CHECK4: + if (_codec.AvailableBytesIn == 0) return r; + r = f; + _codec.AvailableBytesIn--; + _codec.TotalBytesIn++; + expectedCheck = (uint)((_codec.InputBuffer[_codec.NextIn++] << 24) & 0xff000000); + mode = InflateManagerMode.CHECK3; + break; + + case InflateManagerMode.CHECK3: + if (_codec.AvailableBytesIn == 0) return r; + r = f; + _codec.AvailableBytesIn--; _codec.TotalBytesIn++; + expectedCheck += (uint)((_codec.InputBuffer[_codec.NextIn++] << 16) & 0x00ff0000); + mode = InflateManagerMode.CHECK2; + break; + + case InflateManagerMode.CHECK2: + if (_codec.AvailableBytesIn == 0) return r; + r = f; + _codec.AvailableBytesIn--; + _codec.TotalBytesIn++; + expectedCheck += (uint)((_codec.InputBuffer[_codec.NextIn++] << 8) & 0x0000ff00); + mode = InflateManagerMode.CHECK1; + break; + + case InflateManagerMode.CHECK1: + if (_codec.AvailableBytesIn == 0) return r; + r = f; + _codec.AvailableBytesIn--; _codec.TotalBytesIn++; + expectedCheck += (uint)(_codec.InputBuffer[_codec.NextIn++] & 0x000000ff); + if (computedCheck != expectedCheck) + { + mode = InflateManagerMode.BAD; + _codec.Message = "incorrect data check"; + marker = 5; // can't try inflateSync + break; + } + mode = InflateManagerMode.DONE; + return ZlibConstants.Z_STREAM_END; + + case InflateManagerMode.DONE: + return ZlibConstants.Z_STREAM_END; + + case InflateManagerMode.BAD: + throw new ZlibException(String.Format("Bad state ({0})", _codec.Message)); + + default: + throw new ZlibException("Stream error."); + + } + } + } + + + + internal int SetDictionary(byte[] dictionary) + { + int index = 0; + int length = dictionary.Length; + if (mode != InflateManagerMode.DICT0) + throw new ZlibException("Stream error."); + + if (Adler.Adler32(1, dictionary, 0, dictionary.Length) != _codec._Adler32) + { + return ZlibConstants.Z_DATA_ERROR; + } + + _codec._Adler32 = Adler.Adler32(0, null, 0, 0); + + if (length >= (1 << wbits)) + { + length = (1 << wbits) - 1; + index = dictionary.Length - length; + } + blocks.SetDictionary(dictionary, index, length); + mode = InflateManagerMode.BLOCKS; + return ZlibConstants.Z_OK; + } + + + private static readonly byte[] mark = new byte[] { 0, 0, 0xff, 0xff }; + + internal int Sync() + { + int n; // number of bytes to look at + int p; // pointer to bytes + int m; // number of marker bytes found in a row + long r, w; // temporaries to save total_in and total_out + + // set up + if (mode != InflateManagerMode.BAD) + { + mode = InflateManagerMode.BAD; + marker = 0; + } + if ((n = _codec.AvailableBytesIn) == 0) + return ZlibConstants.Z_BUF_ERROR; + p = _codec.NextIn; + m = marker; + + // search + while (n != 0 && m < 4) + { + if (_codec.InputBuffer[p] == mark[m]) + { + m++; + } + else if (_codec.InputBuffer[p] != 0) + { + m = 0; + } + else + { + m = 4 - m; + } + p++; n--; + } + + // restore + _codec.TotalBytesIn += p - _codec.NextIn; + _codec.NextIn = p; + _codec.AvailableBytesIn = n; + marker = m; + + // return no joy or set up to restart on a new block + if (m != 4) + { + return ZlibConstants.Z_DATA_ERROR; + } + r = _codec.TotalBytesIn; + w = _codec.TotalBytesOut; + Reset(); + _codec.TotalBytesIn = r; + _codec.TotalBytesOut = w; + mode = InflateManagerMode.BLOCKS; + return ZlibConstants.Z_OK; + } + + + // Returns true if inflate is currently at the end of a block generated + // by Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + // implementation to provide an additional safety check. PPP uses Z_SYNC_FLUSH + // but removes the length bytes of the resulting empty stored block. When + // decompressing, PPP checks that at the end of input packet, inflate is + // waiting for these length bytes. + internal int SyncPoint(ZlibCodec z) + { + return blocks.SyncPoint(); + } + } +} \ No newline at end of file diff --git a/DotNetZip/Zlib/LICENSE.jzlib.txt b/DotNetZip/Zlib/LICENSE.jzlib.txt new file mode 100644 index 0000000..19414a2 --- /dev/null +++ b/DotNetZip/Zlib/LICENSE.jzlib.txt @@ -0,0 +1,32 @@ +The ZLIB library, available as Ionic.Zlib.dll or as part of DotNetZip, +is a ported-then-modified version of jzlib. The following applies to jzlib: + +JZlib 0.0.* were released under the GNU LGPL license. Later, we have switched +over to a BSD-style license. + +------------------------------------------------------------------------------ +Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/DotNetZip/Zlib/License.zlib.txt b/DotNetZip/Zlib/License.zlib.txt new file mode 100644 index 0000000..de6d57a --- /dev/null +++ b/DotNetZip/Zlib/License.zlib.txt @@ -0,0 +1,31 @@ +The ZLIB library, available as Ionic.Zlib.dll or as part of DotNetZip, +is a ported-then-modified version of jzlib, which itself is based on +zlib-1.1.3, the well-known C-language compression library. + +The following notice applies to zlib: + +----------------------------------------------------------------------- + +Copyright (C) 1995-2004 Jean-loup Gailly and Mark Adler + + The ZLIB software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly jloup@gzip.org + Mark Adler madler@alumni.caltech.edu + + +----------------------------------------------------------------------- diff --git a/DotNetZip/Zlib/LocalTestRun.testrunconfig b/DotNetZip/Zlib/LocalTestRun.testrunconfig new file mode 100644 index 0000000..09d5c0b --- /dev/null +++ b/DotNetZip/Zlib/LocalTestRun.testrunconfig @@ -0,0 +1,5 @@ + + + This is a default test run configuration for a local test run. + + \ No newline at end of file diff --git a/DotNetZip/Zlib/ParallelDeflateOutputStream.cs b/DotNetZip/Zlib/ParallelDeflateOutputStream.cs new file mode 100644 index 0000000..f751415 --- /dev/null +++ b/DotNetZip/Zlib/ParallelDeflateOutputStream.cs @@ -0,0 +1,1386 @@ +//#define Trace + +// ParallelDeflateOutputStream.cs +// ------------------------------------------------------------------ +// +// A DeflateStream that does compression only, it uses a +// divide-and-conquer approach with multiple threads to exploit multiple +// CPUs for the DEFLATE computation. +// +// last saved: <2011-July-31 14:49:40> +// +// ------------------------------------------------------------------ +// +// Copyright (c) 2009-2011 by Dino Chiesa +// All rights reserved! +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Threading; +using Ionic.Zlib; +using System.IO; + + +namespace Ionic.Zlib +{ + internal class WorkItem + { + public byte[] buffer; + public byte[] compressed; + public int crc; + public int index; + public int ordinal; + public int inputBytesAvailable; + public int compressedBytesAvailable; + public ZlibCodec compressor; + + public WorkItem(int size, + Ionic.Zlib.CompressionLevel compressLevel, + CompressionStrategy strategy, + int ix) + { + this.buffer= new byte[size]; + // alloc 5 bytes overhead for every block (margin of safety= 2) + int n = size + ((size / 32768)+1) * 5 * 2; + this.compressed = new byte[n]; + this.compressor = new ZlibCodec(); + this.compressor.InitializeDeflate(compressLevel, false); + this.compressor.OutputBuffer = this.compressed; + this.compressor.InputBuffer = this.buffer; + this.index = ix; + } + } + + /// + /// A class for compressing streams using the + /// Deflate algorithm with multiple threads. + /// + /// + /// + /// + /// This class performs DEFLATE compression through writing. For + /// more information on the Deflate algorithm, see IETF RFC 1951, + /// "DEFLATE Compressed Data Format Specification version 1.3." + /// + /// + /// + /// This class is similar to , except + /// that this class is for compression only, and this implementation uses an + /// approach that employs multiple worker threads to perform the DEFLATE. On + /// a multi-cpu or multi-core computer, the performance of this class can be + /// significantly higher than the single-threaded DeflateStream, particularly + /// for larger streams. How large? Anything over 10mb is a good candidate + /// for parallel compression. + /// + /// + /// + /// The tradeoff is that this class uses more memory and more CPU than the + /// vanilla DeflateStream, and also is less efficient as a compressor. For + /// large files the size of the compressed data stream can be less than 1% + /// larger than the size of a compressed data stream from the vanialla + /// DeflateStream. For smaller files the difference can be larger. The + /// difference will also be larger if you set the BufferSize to be lower than + /// the default value. Your mileage may vary. Finally, for small files, the + /// ParallelDeflateOutputStream can be much slower than the vanilla + /// DeflateStream, because of the overhead associated to using the thread + /// pool. + /// + /// + /// + /// + public class ParallelDeflateOutputStream : System.IO.Stream + { + + private static readonly int IO_BUFFER_SIZE_DEFAULT = 64 * 1024; // 128k + private static readonly int BufferPairsPerCore = 4; + + private System.Collections.Generic.List _pool; + private bool _leaveOpen; + private bool emitting; + private System.IO.Stream _outStream; + private int _maxBufferPairs; + private int _bufferSize = IO_BUFFER_SIZE_DEFAULT; + private AutoResetEvent _newlyCompressedBlob; + //private ManualResetEvent _writingDone; + //private ManualResetEvent _sessionReset; + private object _outputLock = new object(); + private bool _isClosed; + private bool _firstWriteDone; + private int _currentlyFilling; + private int _lastFilled; + private int _lastWritten; + private int _latestCompressed; + private int _Crc32; + private Ionic.Crc.CRC32 _runningCrc; + private object _latestLock = new object(); + private System.Collections.Generic.Queue _toWrite; + private System.Collections.Generic.Queue _toFill; + private Int64 _totalBytesProcessed; + private Ionic.Zlib.CompressionLevel _compressLevel; + private volatile Exception _pendingException; + private bool _handlingException; + private object _eLock = new Object(); // protects _pendingException + + // This bitfield is used only when Trace is defined. + //private TraceBits _DesiredTrace = TraceBits.Write | TraceBits.WriteBegin | + //TraceBits.WriteDone | TraceBits.Lifecycle | TraceBits.Fill | TraceBits.Flush | + //TraceBits.Session; + + //private TraceBits _DesiredTrace = TraceBits.WriteBegin | TraceBits.WriteDone | TraceBits.Synch | TraceBits.Lifecycle | TraceBits.Session ; + + private TraceBits _DesiredTrace = + TraceBits.Session | + TraceBits.Compress | + TraceBits.WriteTake | + TraceBits.WriteEnter | + TraceBits.EmitEnter | + TraceBits.EmitDone | + TraceBits.EmitLock | + TraceBits.EmitSkip | + TraceBits.EmitBegin; + + /// + /// Create a ParallelDeflateOutputStream. + /// + /// + /// + /// + /// This stream compresses data written into it via the DEFLATE + /// algorithm (see RFC 1951), and writes out the compressed byte stream. + /// + /// + /// + /// The instance will use the default compression level, the default + /// buffer sizes and the default number of threads and buffers per + /// thread. + /// + /// + /// + /// This class is similar to , + /// except that this implementation uses an approach that employs + /// multiple worker threads to perform the DEFLATE. On a multi-cpu or + /// multi-core computer, the performance of this class can be + /// significantly higher than the single-threaded DeflateStream, + /// particularly for larger streams. How large? Anything over 10mb is + /// a good candidate for parallel compression. + /// + /// + /// + /// + /// + /// + /// This example shows how to use a ParallelDeflateOutputStream to compress + /// data. It reads a file, compresses it, and writes the compressed data to + /// a second, output file. + /// + /// + /// byte[] buffer = new byte[WORKING_BUFFER_SIZE]; + /// int n= -1; + /// String outputFile = fileToCompress + ".compressed"; + /// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress)) + /// { + /// using (var raw = System.IO.File.Create(outputFile)) + /// { + /// using (Stream compressor = new ParallelDeflateOutputStream(raw)) + /// { + /// while ((n= input.Read(buffer, 0, buffer.Length)) != 0) + /// { + /// compressor.Write(buffer, 0, n); + /// } + /// } + /// } + /// } + /// + /// + /// Dim buffer As Byte() = New Byte(4096) {} + /// Dim n As Integer = -1 + /// Dim outputFile As String = (fileToCompress & ".compressed") + /// Using input As Stream = File.OpenRead(fileToCompress) + /// Using raw As FileStream = File.Create(outputFile) + /// Using compressor As Stream = New ParallelDeflateOutputStream(raw) + /// Do While (n <> 0) + /// If (n > 0) Then + /// compressor.Write(buffer, 0, n) + /// End If + /// n = input.Read(buffer, 0, buffer.Length) + /// Loop + /// End Using + /// End Using + /// End Using + /// + /// + /// The stream to which compressed data will be written. + public ParallelDeflateOutputStream(System.IO.Stream stream) + : this(stream, CompressionLevel.Default, CompressionStrategy.Default, false) + { + } + + /// + /// Create a ParallelDeflateOutputStream using the specified CompressionLevel. + /// + /// + /// See the + /// constructor for example code. + /// + /// The stream to which compressed data will be written. + /// A tuning knob to trade speed for effectiveness. + public ParallelDeflateOutputStream(System.IO.Stream stream, CompressionLevel level) + : this(stream, level, CompressionStrategy.Default, false) + { + } + + /// + /// Create a ParallelDeflateOutputStream and specify whether to leave the captive stream open + /// when the ParallelDeflateOutputStream is closed. + /// + /// + /// See the + /// constructor for example code. + /// + /// The stream to which compressed data will be written. + /// + /// true if the application would like the stream to remain open after inflation/deflation. + /// + public ParallelDeflateOutputStream(System.IO.Stream stream, bool leaveOpen) + : this(stream, CompressionLevel.Default, CompressionStrategy.Default, leaveOpen) + { + } + + /// + /// Create a ParallelDeflateOutputStream and specify whether to leave the captive stream open + /// when the ParallelDeflateOutputStream is closed. + /// + /// + /// See the + /// constructor for example code. + /// + /// The stream to which compressed data will be written. + /// A tuning knob to trade speed for effectiveness. + /// + /// true if the application would like the stream to remain open after inflation/deflation. + /// + public ParallelDeflateOutputStream(System.IO.Stream stream, CompressionLevel level, bool leaveOpen) + : this(stream, CompressionLevel.Default, CompressionStrategy.Default, leaveOpen) + { + } + + /// + /// Create a ParallelDeflateOutputStream using the specified + /// CompressionLevel and CompressionStrategy, and specifying whether to + /// leave the captive stream open when the ParallelDeflateOutputStream is + /// closed. + /// + /// + /// See the + /// constructor for example code. + /// + /// The stream to which compressed data will be written. + /// A tuning knob to trade speed for effectiveness. + /// + /// By tweaking this parameter, you may be able to optimize the compression for + /// data with particular characteristics. + /// + /// + /// true if the application would like the stream to remain open after inflation/deflation. + /// + public ParallelDeflateOutputStream(System.IO.Stream stream, + CompressionLevel level, + CompressionStrategy strategy, + bool leaveOpen) + { + TraceOutput(TraceBits.Lifecycle | TraceBits.Session, "-------------------------------------------------------"); + TraceOutput(TraceBits.Lifecycle | TraceBits.Session, "Create {0:X8}", this.GetHashCode()); + _outStream = stream; + _compressLevel= level; + Strategy = strategy; + _leaveOpen = leaveOpen; + this.MaxBufferPairs = 16; // default + } + + + /// + /// The ZLIB strategy to be used during compression. + /// + /// + public CompressionStrategy Strategy + { + get; + private set; + } + + /// + /// The maximum number of buffer pairs to use. + /// + /// + /// + /// + /// This property sets an upper limit on the number of memory buffer + /// pairs to create. The implementation of this stream allocates + /// multiple buffers to facilitate parallel compression. As each buffer + /// fills up, this stream uses + /// ThreadPool.QueueUserWorkItem() + /// to compress those buffers in a background threadpool thread. After a + /// buffer is compressed, it is re-ordered and written to the output + /// stream. + /// + /// + /// + /// A higher number of buffer pairs enables a higher degree of + /// parallelism, which tends to increase the speed of compression on + /// multi-cpu computers. On the other hand, a higher number of buffer + /// pairs also implies a larger memory consumption, more active worker + /// threads, and a higher cpu utilization for any compression. This + /// property enables the application to limit its memory consumption and + /// CPU utilization behavior depending on requirements. + /// + /// + /// + /// For each compression "task" that occurs in parallel, there are 2 + /// buffers allocated: one for input and one for output. This property + /// sets a limit for the number of pairs. The total amount of storage + /// space allocated for buffering will then be (N*S*2), where N is the + /// number of buffer pairs, S is the size of each buffer (). By default, DotNetZip allocates 4 buffer + /// pairs per CPU core, so if your machine has 4 cores, and you retain + /// the default buffer size of 128k, then the + /// ParallelDeflateOutputStream will use 4 * 4 * 2 * 128kb of buffer + /// memory in total, or 4mb, in blocks of 128kb. If you then set this + /// property to 8, then the number will be 8 * 2 * 128kb of buffer + /// memory, or 2mb. + /// + /// + /// + /// CPU utilization will also go up with additional buffers, because a + /// larger number of buffer pairs allows a larger number of background + /// threads to compress in parallel. If you find that parallel + /// compression is consuming too much memory or CPU, you can adjust this + /// value downward. + /// + /// + /// + /// The default value is 16. Different values may deliver better or + /// worse results, depending on your priorities and the dynamic + /// performance characteristics of your storage and compute resources. + /// + /// + /// + /// This property is not the number of buffer pairs to use; it is an + /// upper limit. An illustration: Suppose you have an application that + /// uses the default value of this property (which is 16), and it runs + /// on a machine with 2 CPU cores. In that case, DotNetZip will allocate + /// 4 buffer pairs per CPU core, for a total of 8 pairs. The upper + /// limit specified by this property has no effect. + /// + /// + /// + /// The application can set this value at any time, but it is effective + /// only before the first call to Write(), which is when the buffers are + /// allocated. + /// + /// + public int MaxBufferPairs + { + get + { + return _maxBufferPairs; + } + set + { + if (value < 4) + throw new ArgumentException("MaxBufferPairs", + "Value must be 4 or greater."); + _maxBufferPairs = value; + } + } + + /// + /// The size of the buffers used by the compressor threads. + /// + /// + /// + /// + /// The default buffer size is 128k. The application can set this value + /// at any time, but it is effective only before the first Write(). + /// + /// + /// + /// Larger buffer sizes implies larger memory consumption but allows + /// more efficient compression. Using smaller buffer sizes consumes less + /// memory but may result in less effective compression. For example, + /// using the default buffer size of 128k, the compression delivered is + /// within 1% of the compression delivered by the single-threaded . On the other hand, using a + /// BufferSize of 8k can result in a compressed data stream that is 5% + /// larger than that delivered by the single-threaded + /// DeflateStream. Excessively small buffer sizes can also cause + /// the speed of the ParallelDeflateOutputStream to drop, because of + /// larger thread scheduling overhead dealing with many many small + /// buffers. + /// + /// + /// + /// The total amount of storage space allocated for buffering will be + /// (N*S*2), where N is the number of buffer pairs, and S is the size of + /// each buffer (this property). There are 2 buffers used by the + /// compressor, one for input and one for output. By default, DotNetZip + /// allocates 4 buffer pairs per CPU core, so if your machine has 4 + /// cores, then the number of buffer pairs used will be 16. If you + /// accept the default value of this property, 128k, then the + /// ParallelDeflateOutputStream will use 16 * 2 * 128kb of buffer memory + /// in total, or 4mb, in blocks of 128kb. If you set this property to + /// 64kb, then the number will be 16 * 2 * 64kb of buffer memory, or + /// 2mb. + /// + /// + /// + public int BufferSize + { + get { return _bufferSize;} + set + { + if (value < 1024) + throw new ArgumentOutOfRangeException("BufferSize", + "BufferSize must be greater than 1024 bytes"); + _bufferSize = value; + } + } + + /// + /// The CRC32 for the data that was written out, prior to compression. + /// + /// + /// This value is meaningful only after a call to Close(). + /// + public int Crc32 { get { return _Crc32; } } + + + /// + /// The total number of uncompressed bytes processed by the ParallelDeflateOutputStream. + /// + /// + /// This value is meaningful only after a call to Close(). + /// + public Int64 BytesProcessed { get { return _totalBytesProcessed; } } + + + private void _InitializePoolOfWorkItems() + { + _toWrite = new Queue(); + _toFill = new Queue(); + _pool = new System.Collections.Generic.List(); + int nTasks = BufferPairsPerCore * Environment.ProcessorCount; + nTasks = Math.Min(nTasks, _maxBufferPairs); + for(int i=0; i < nTasks; i++) + { + _pool.Add(new WorkItem(_bufferSize, _compressLevel, Strategy, i)); + _toFill.Enqueue(i); + } + + _newlyCompressedBlob = new AutoResetEvent(false); + _runningCrc = new Ionic.Crc.CRC32(); + _currentlyFilling = -1; + _lastFilled = -1; + _lastWritten = -1; + _latestCompressed = -1; + } + + + + + /// + /// Write data to the stream. + /// + /// + /// + /// + /// + /// To use the ParallelDeflateOutputStream to compress data, create a + /// ParallelDeflateOutputStream with CompressionMode.Compress, passing a + /// writable output stream. Then call Write() on that + /// ParallelDeflateOutputStream, providing uncompressed data as input. The + /// data sent to the output stream will be the compressed form of the data + /// written. + /// + /// + /// + /// To decompress data, use the class. + /// + /// + /// + /// The buffer holding data to write to the stream. + /// the offset within that data array to find the first byte to write. + /// the number of bytes to write. + public override void Write(byte[] buffer, int offset, int count) + { + bool mustWait = false; + + // This method does this: + // 0. handles any pending exceptions + // 1. write any buffers that are ready to be written, + // 2. fills a work buffer; when full, flip state to 'Filled', + // 3. if more data to be written, goto step 1 + + if (_isClosed) + throw new InvalidOperationException(); + + // dispense any exceptions that occurred on the BG threads + if (_pendingException != null) + { + _handlingException = true; + var pe = _pendingException; + _pendingException = null; + throw pe; + } + + if (count == 0) return; + + if (!_firstWriteDone) + { + // Want to do this on first Write, first session, and not in the + // constructor. We want to allow MaxBufferPairs to + // change after construction, but before first Write. + _InitializePoolOfWorkItems(); + _firstWriteDone = true; + } + + + do + { + // may need to make buffers available + EmitPendingBuffers(false, mustWait); + + mustWait = false; + // use current buffer, or get a new buffer to fill + int ix = -1; + if (_currentlyFilling >= 0) + { + ix = _currentlyFilling; + TraceOutput(TraceBits.WriteTake, + "Write notake wi({0}) lf({1})", + ix, + _lastFilled); + } + else + { + TraceOutput(TraceBits.WriteTake, "Write take?"); + if (_toFill.Count == 0) + { + // no available buffers, so... need to emit + // compressed buffers. + mustWait = true; + continue; + } + + ix = _toFill.Dequeue(); + TraceOutput(TraceBits.WriteTake, + "Write take wi({0}) lf({1})", + ix, + _lastFilled); + ++_lastFilled; // TODO: consider rollover? + } + + WorkItem workitem = _pool[ix]; + + int limit = ((workitem.buffer.Length - workitem.inputBytesAvailable) > count) + ? count + : (workitem.buffer.Length - workitem.inputBytesAvailable); + + workitem.ordinal = _lastFilled; + + TraceOutput(TraceBits.Write, + "Write lock wi({0}) ord({1}) iba({2})", + workitem.index, + workitem.ordinal, + workitem.inputBytesAvailable + ); + + // copy from the provided buffer to our workitem, starting at + // the tail end of whatever data we might have in there currently. + Buffer.BlockCopy(buffer, + offset, + workitem.buffer, + workitem.inputBytesAvailable, + limit); + + count -= limit; + offset += limit; + workitem.inputBytesAvailable += limit; + if (workitem.inputBytesAvailable == workitem.buffer.Length) + { + // No need for interlocked.increment: the Write() + // method is documented as not multi-thread safe, so + // we can assume Write() calls come in from only one + // thread. + TraceOutput(TraceBits.Write, + "Write QUWI wi({0}) ord({1}) iba({2}) nf({3})", + workitem.index, + workitem.ordinal, + workitem.inputBytesAvailable ); + + if (!ThreadPool.QueueUserWorkItem( _DeflateOne, workitem )) + throw new Exception("Cannot enqueue workitem"); + + _currentlyFilling = -1; // will get a new buffer next time + } + else + _currentlyFilling = ix; + + if (count > 0) + TraceOutput(TraceBits.WriteEnter, "Write more"); + } + while (count > 0); // until no more to write + + TraceOutput(TraceBits.WriteEnter, "Write exit"); + return; + } + + + + private void _FlushFinish() + { + // After writing a series of compressed buffers, each one closed + // with Flush.Sync, we now write the final one as Flush.Finish, + // and then stop. + byte[] buffer = new byte[128]; + var compressor = new ZlibCodec(); + int rc = compressor.InitializeDeflate(_compressLevel, false); + compressor.InputBuffer = null; + compressor.NextIn = 0; + compressor.AvailableBytesIn = 0; + compressor.OutputBuffer = buffer; + compressor.NextOut = 0; + compressor.AvailableBytesOut = buffer.Length; + rc = compressor.Deflate(FlushType.Finish); + + if (rc != ZlibConstants.Z_STREAM_END && rc != ZlibConstants.Z_OK) + throw new Exception("deflating: " + compressor.Message); + + if (buffer.Length - compressor.AvailableBytesOut > 0) + { + TraceOutput(TraceBits.EmitBegin, + "Emit begin flush bytes({0})", + buffer.Length - compressor.AvailableBytesOut); + + _outStream.Write(buffer, 0, buffer.Length - compressor.AvailableBytesOut); + + TraceOutput(TraceBits.EmitDone, + "Emit done flush"); + } + + compressor.EndDeflate(); + + _Crc32 = _runningCrc.Crc32Result; + } + + + private void _Flush(bool lastInput) + { + if (_isClosed) + throw new InvalidOperationException(); + + if (emitting) return; + + // compress any partial buffer + if (_currentlyFilling >= 0) + { + WorkItem workitem = _pool[_currentlyFilling]; + _DeflateOne(workitem); + _currentlyFilling = -1; // get a new buffer next Write() + } + + if (lastInput) + { + EmitPendingBuffers(true, false); + _FlushFinish(); + } + else + { + EmitPendingBuffers(false, false); + } + } + + + + /// + /// Flush the stream. + /// + public override void Flush() + { + if (_pendingException != null) + { + _handlingException = true; + var pe = _pendingException; + _pendingException = null; + throw pe; + } + if (_handlingException) + return; + + _Flush(false); + } + + + /// + /// Close the stream. + /// + /// + /// You must call Close on the stream to guarantee that all of the data written in has + /// been compressed, and the compressed data has been written out. + /// + public override void Close() + { + TraceOutput(TraceBits.Session, "Close {0:X8}", this.GetHashCode()); + + if (_pendingException != null) + { + _handlingException = true; + var pe = _pendingException; + _pendingException = null; + throw pe; + } + + if (_handlingException) + return; + + if (_isClosed) return; + + _Flush(true); + + if (!_leaveOpen) + _outStream.Close(); + + _isClosed= true; + } + + + + // workitem 10030 - implement a new Dispose method + + /// Dispose the object + /// + /// + /// Because ParallelDeflateOutputStream is IDisposable, the + /// application must call this method when finished using the instance. + /// + /// + /// This method is generally called implicitly upon exit from + /// a using scope in C# (Using in VB). + /// + /// + new public void Dispose() + { + TraceOutput(TraceBits.Lifecycle, "Dispose {0:X8}", this.GetHashCode()); + Close(); + _pool = null; + Dispose(true); + } + + + + /// The Dispose method + /// + /// indicates whether the Dispose method was invoked by user code. + /// + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + } + + + /// + /// Resets the stream for use with another stream. + /// + /// + /// Because the ParallelDeflateOutputStream is expensive to create, it + /// has been designed so that it can be recycled and re-used. You have + /// to call Close() on the stream first, then you can call Reset() on + /// it, to use it again on another stream. + /// + /// + /// + /// The new output stream for this era. + /// + /// + /// + /// + /// ParallelDeflateOutputStream deflater = null; + /// foreach (var inputFile in listOfFiles) + /// { + /// string outputFile = inputFile + ".compressed"; + /// using (System.IO.Stream input = System.IO.File.OpenRead(inputFile)) + /// { + /// using (var outStream = System.IO.File.Create(outputFile)) + /// { + /// if (deflater == null) + /// deflater = new ParallelDeflateOutputStream(outStream, + /// CompressionLevel.Best, + /// CompressionStrategy.Default, + /// true); + /// deflater.Reset(outStream); + /// + /// while ((n= input.Read(buffer, 0, buffer.Length)) != 0) + /// { + /// deflater.Write(buffer, 0, n); + /// } + /// } + /// } + /// } + /// + /// + public void Reset(Stream stream) + { + TraceOutput(TraceBits.Session, "-------------------------------------------------------"); + TraceOutput(TraceBits.Session, "Reset {0:X8} firstDone({1})", this.GetHashCode(), _firstWriteDone); + + if (!_firstWriteDone) return; + + // reset all status + _toWrite.Clear(); + _toFill.Clear(); + foreach (var workitem in _pool) + { + _toFill.Enqueue(workitem.index); + workitem.ordinal = -1; + } + + _firstWriteDone = false; + _totalBytesProcessed = 0L; + _runningCrc = new Ionic.Crc.CRC32(); + _isClosed= false; + _currentlyFilling = -1; + _lastFilled = -1; + _lastWritten = -1; + _latestCompressed = -1; + _outStream = stream; + } + + + + + private void EmitPendingBuffers(bool doAll, bool mustWait) + { + // When combining parallel deflation with a ZipSegmentedStream, it's + // possible for the ZSS to throw from within this method. In that + // case, Close/Dispose will be called on this stream, if this stream + // is employed within a using or try/finally pair as required. But + // this stream is unaware of the pending exception, so the Close() + // method invokes this method AGAIN. This can lead to a deadlock. + // Therefore, failfast if re-entering. + + if (emitting) return; + emitting = true; + if (doAll || mustWait) + _newlyCompressedBlob.WaitOne(); + + do + { + int firstSkip = -1; + int millisecondsToWait = doAll ? 200 : (mustWait ? -1 : 0); + int nextToWrite = -1; + + do + { + if (Monitor.TryEnter(_toWrite, millisecondsToWait)) + { + nextToWrite = -1; + try + { + if (_toWrite.Count > 0) + nextToWrite = _toWrite.Dequeue(); + } + finally + { + Monitor.Exit(_toWrite); + } + + if (nextToWrite >= 0) + { + WorkItem workitem = _pool[nextToWrite]; + if (workitem.ordinal != _lastWritten + 1) + { + // out of order. requeue and try again. + TraceOutput(TraceBits.EmitSkip, + "Emit skip wi({0}) ord({1}) lw({2}) fs({3})", + workitem.index, + workitem.ordinal, + _lastWritten, + firstSkip); + + lock(_toWrite) + { + _toWrite.Enqueue(nextToWrite); + } + + if (firstSkip == nextToWrite) + { + // We went around the list once. + // None of the items in the list is the one we want. + // Now wait for a compressor to signal again. + _newlyCompressedBlob.WaitOne(); + firstSkip = -1; + } + else if (firstSkip == -1) + firstSkip = nextToWrite; + + continue; + } + + firstSkip = -1; + + TraceOutput(TraceBits.EmitBegin, + "Emit begin wi({0}) ord({1}) cba({2})", + workitem.index, + workitem.ordinal, + workitem.compressedBytesAvailable); + + _outStream.Write(workitem.compressed, 0, workitem.compressedBytesAvailable); + _runningCrc.Combine(workitem.crc, workitem.inputBytesAvailable); + _totalBytesProcessed += workitem.inputBytesAvailable; + workitem.inputBytesAvailable = 0; + + TraceOutput(TraceBits.EmitDone, + "Emit done wi({0}) ord({1}) cba({2}) mtw({3})", + workitem.index, + workitem.ordinal, + workitem.compressedBytesAvailable, + millisecondsToWait); + + _lastWritten = workitem.ordinal; + _toFill.Enqueue(workitem.index); + + // don't wait next time through + if (millisecondsToWait == -1) millisecondsToWait = 0; + } + } + else + nextToWrite = -1; + + } while (nextToWrite >= 0); + + } while (doAll && (_lastWritten != _latestCompressed)); + + emitting = false; + } + + + +#if OLD + private void _PerpetualWriterMethod(object state) + { + TraceOutput(TraceBits.WriterThread, "_PerpetualWriterMethod START"); + + try + { + do + { + // wait for the next session + TraceOutput(TraceBits.Synch | TraceBits.WriterThread, "Synch _sessionReset.WaitOne(begin) PWM"); + _sessionReset.WaitOne(); + TraceOutput(TraceBits.Synch | TraceBits.WriterThread, "Synch _sessionReset.WaitOne(done) PWM"); + + if (_isDisposed) break; + + TraceOutput(TraceBits.Synch | TraceBits.WriterThread, "Synch _sessionReset.Reset() PWM"); + _sessionReset.Reset(); + + // repeatedly write buffers as they become ready + WorkItem workitem = null; + Ionic.Zlib.CRC32 c= new Ionic.Zlib.CRC32(); + do + { + workitem = _pool[_nextToWrite % _pc]; + lock(workitem) + { + if (_noMoreInputForThisSegment) + TraceOutput(TraceBits.Write, + "Write drain wi({0}) stat({1}) canuse({2}) cba({3})", + workitem.index, + workitem.status, + (workitem.status == (int)WorkItem.Status.Compressed), + workitem.compressedBytesAvailable); + + do + { + if (workitem.status == (int)WorkItem.Status.Compressed) + { + TraceOutput(TraceBits.WriteBegin, + "Write begin wi({0}) stat({1}) cba({2})", + workitem.index, + workitem.status, + workitem.compressedBytesAvailable); + + workitem.status = (int)WorkItem.Status.Writing; + _outStream.Write(workitem.compressed, 0, workitem.compressedBytesAvailable); + c.Combine(workitem.crc, workitem.inputBytesAvailable); + _totalBytesProcessed += workitem.inputBytesAvailable; + _nextToWrite++; + workitem.inputBytesAvailable= 0; + workitem.status = (int)WorkItem.Status.Done; + + TraceOutput(TraceBits.WriteDone, + "Write done wi({0}) stat({1}) cba({2})", + workitem.index, + workitem.status, + workitem.compressedBytesAvailable); + + + Monitor.Pulse(workitem); + break; + } + else + { + int wcycles = 0; + // I've locked a workitem I cannot use. + // Therefore, wake someone else up, and then release the lock. + while (workitem.status != (int)WorkItem.Status.Compressed) + { + TraceOutput(TraceBits.WriteWait, + "Write waiting wi({0}) stat({1}) nw({2}) nf({3}) nomore({4})", + workitem.index, + workitem.status, + _nextToWrite, _nextToFill, + _noMoreInputForThisSegment ); + + if (_noMoreInputForThisSegment && _nextToWrite == _nextToFill) + break; + + wcycles++; + + // wake up someone else + Monitor.Pulse(workitem); + // release and wait + Monitor.Wait(workitem); + + if (workitem.status == (int)WorkItem.Status.Compressed) + TraceOutput(TraceBits.WriteWait, + "Write A-OK wi({0}) stat({1}) iba({2}) cba({3}) cyc({4})", + workitem.index, + workitem.status, + workitem.inputBytesAvailable, + workitem.compressedBytesAvailable, + wcycles); + } + + if (_noMoreInputForThisSegment && _nextToWrite == _nextToFill) + break; + + } + } + while (true); + } + + if (_noMoreInputForThisSegment) + TraceOutput(TraceBits.Write, + "Write nomore nw({0}) nf({1}) break({2})", + _nextToWrite, _nextToFill, (_nextToWrite == _nextToFill)); + + if (_noMoreInputForThisSegment && _nextToWrite == _nextToFill) + break; + + } while (true); + + + // Finish: + // After writing a series of buffers, closing each one with + // Flush.Sync, we now write the final one as Flush.Finish, and + // then stop. + byte[] buffer = new byte[128]; + ZlibCodec compressor = new ZlibCodec(); + int rc = compressor.InitializeDeflate(_compressLevel, false); + compressor.InputBuffer = null; + compressor.NextIn = 0; + compressor.AvailableBytesIn = 0; + compressor.OutputBuffer = buffer; + compressor.NextOut = 0; + compressor.AvailableBytesOut = buffer.Length; + rc = compressor.Deflate(FlushType.Finish); + + if (rc != ZlibConstants.Z_STREAM_END && rc != ZlibConstants.Z_OK) + throw new Exception("deflating: " + compressor.Message); + + if (buffer.Length - compressor.AvailableBytesOut > 0) + { + TraceOutput(TraceBits.WriteBegin, + "Write begin flush bytes({0})", + buffer.Length - compressor.AvailableBytesOut); + + _outStream.Write(buffer, 0, buffer.Length - compressor.AvailableBytesOut); + + TraceOutput(TraceBits.WriteBegin, + "Write done flush"); + } + + compressor.EndDeflate(); + + _Crc32 = c.Crc32Result; + + // signal that writing is complete: + TraceOutput(TraceBits.Synch, "Synch _writingDone.Set() PWM"); + _writingDone.Set(); + } + while (true); + } + catch (System.Exception exc1) + { + lock(_eLock) + { + // expose the exception to the main thread + if (_pendingException!=null) + _pendingException = exc1; + } + } + + TraceOutput(TraceBits.WriterThread, "_PerpetualWriterMethod FINIS"); + } +#endif + + + + + private void _DeflateOne(Object wi) + { + // compress one buffer + WorkItem workitem = (WorkItem) wi; + try + { + int myItem = workitem.index; + Ionic.Crc.CRC32 crc = new Ionic.Crc.CRC32(); + + // calc CRC on the buffer + crc.SlurpBlock(workitem.buffer, 0, workitem.inputBytesAvailable); + + // deflate it + DeflateOneSegment(workitem); + + // update status + workitem.crc = crc.Crc32Result; + TraceOutput(TraceBits.Compress, + "Compress wi({0}) ord({1}) len({2})", + workitem.index, + workitem.ordinal, + workitem.compressedBytesAvailable + ); + + lock(_latestLock) + { + if (workitem.ordinal > _latestCompressed) + _latestCompressed = workitem.ordinal; + } + lock (_toWrite) + { + _toWrite.Enqueue(workitem.index); + } + _newlyCompressedBlob.Set(); + } + catch (System.Exception exc1) + { + lock(_eLock) + { + // expose the exception to the main thread + if (_pendingException!=null) + _pendingException = exc1; + } + } + } + + + + + private bool DeflateOneSegment(WorkItem workitem) + { + ZlibCodec compressor = workitem.compressor; + int rc= 0; + compressor.ResetDeflate(); + compressor.NextIn = 0; + + compressor.AvailableBytesIn = workitem.inputBytesAvailable; + + // step 1: deflate the buffer + compressor.NextOut = 0; + compressor.AvailableBytesOut = workitem.compressed.Length; + do + { + compressor.Deflate(FlushType.None); + } + while (compressor.AvailableBytesIn > 0 || compressor.AvailableBytesOut == 0); + + // step 2: flush (sync) + rc = compressor.Deflate(FlushType.Sync); + + workitem.compressedBytesAvailable= (int) compressor.TotalBytesOut; + return true; + } + + + [System.Diagnostics.ConditionalAttribute("Trace")] + private void TraceOutput(TraceBits bits, string format, params object[] varParams) + { + if ((bits & _DesiredTrace) != 0) + { + lock(_outputLock) + { + int tid = Thread.CurrentThread.GetHashCode(); +#if !SILVERLIGHT + Console.ForegroundColor = (ConsoleColor) (tid % 8 + 8); +#endif + Console.Write("{0:000} PDOS ", tid); + Console.WriteLine(format, varParams); +#if !SILVERLIGHT + Console.ResetColor(); +#endif + } + } + } + + + // used only when Trace is defined + [Flags] + enum TraceBits : uint + { + None = 0, + NotUsed1 = 1, + EmitLock = 2, + EmitEnter = 4, // enter _EmitPending + EmitBegin = 8, // begin to write out + EmitDone = 16, // done writing out + EmitSkip = 32, // writer skipping a workitem + EmitAll = 58, // All Emit flags + Flush = 64, + Lifecycle = 128, // constructor/disposer + Session = 256, // Close/Reset + Synch = 512, // thread synchronization + Instance = 1024, // instance settings + Compress = 2048, // compress task + Write = 4096, // filling buffers, when caller invokes Write() + WriteEnter = 8192, // upon entry to Write() + WriteTake = 16384, // on _toFill.Take() + All = 0xffffffff, + } + + + + /// + /// Indicates whether the stream supports Seek operations. + /// + /// + /// Always returns false. + /// + public override bool CanSeek + { + get { return false; } + } + + + /// + /// Indicates whether the stream supports Read operations. + /// + /// + /// Always returns false. + /// + public override bool CanRead + { + get {return false;} + } + + /// + /// Indicates whether the stream supports Write operations. + /// + /// + /// Returns true if the provided stream is writable. + /// + public override bool CanWrite + { + get { return _outStream.CanWrite; } + } + + /// + /// Reading this property always throws a NotSupportedException. + /// + public override long Length + { + get { throw new NotSupportedException(); } + } + + /// + /// Returns the current position of the output stream. + /// + /// + /// + /// Because the output gets written by a background thread, + /// the value may change asynchronously. Setting this + /// property always throws a NotSupportedException. + /// + /// + public override long Position + { + get { return _outStream.Position; } + set { throw new NotSupportedException(); } + } + + /// + /// This method always throws a NotSupportedException. + /// + /// + /// The buffer into which data would be read, IF THIS METHOD + /// ACTUALLY DID ANYTHING. + /// + /// + /// The offset within that data array at which to insert the + /// data that is read, IF THIS METHOD ACTUALLY DID + /// ANYTHING. + /// + /// + /// The number of bytes to write, IF THIS METHOD ACTUALLY DID + /// ANYTHING. + /// + /// nothing. + public override int Read(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } + + /// + /// This method always throws a NotSupportedException. + /// + /// + /// The offset to seek to.... + /// IF THIS METHOD ACTUALLY DID ANYTHING. + /// + /// + /// The reference specifying how to apply the offset.... IF + /// THIS METHOD ACTUALLY DID ANYTHING. + /// + /// nothing. It always throws. + public override long Seek(long offset, System.IO.SeekOrigin origin) + { + throw new NotSupportedException(); + } + + /// + /// This method always throws a NotSupportedException. + /// + /// + /// The new value for the stream length.... IF + /// THIS METHOD ACTUALLY DID ANYTHING. + /// + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + } + +} + + diff --git a/DotNetZip/Zlib/Tree.cs b/DotNetZip/Zlib/Tree.cs new file mode 100644 index 0000000..1db8c4f --- /dev/null +++ b/DotNetZip/Zlib/Tree.cs @@ -0,0 +1,423 @@ +// Tree.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2009-October-28 13:29:50> +// +// ------------------------------------------------------------------ +// +// This module defines classes for zlib compression and +// decompression. This code is derived from the jzlib implementation of +// zlib. In keeping with the license for jzlib, the copyright to that +// code is below. +// +// ------------------------------------------------------------------ +// +// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in +// the documentation and/or other materials provided with the distribution. +// +// 3. The names of the authors may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// ----------------------------------------------------------------------- +// +// This program is based on zlib-1.1.3; credit to authors +// Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) +// and contributors of zlib. +// +// ----------------------------------------------------------------------- + + +using System; + +namespace Ionic.Zlib +{ + sealed class Tree + { + private static readonly int HEAP_SIZE = (2 * InternalConstants.L_CODES + 1); + + // extra bits for each length code + internal static readonly int[] ExtraLengthBits = new int[] + { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 + }; + + // extra bits for each distance code + internal static readonly int[] ExtraDistanceBits = new int[] + { + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 + }; + + // extra bits for each bit length code + internal static readonly int[] extra_blbits = new int[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7}; + + internal static readonly sbyte[] bl_order = new sbyte[]{16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + + // The lengths of the bit length codes are sent in order of decreasing + // probability, to avoid transmitting the lengths for unused bit + // length codes. + + internal const int Buf_size = 8 * 2; + + // see definition of array dist_code below + //internal const int DIST_CODE_LEN = 512; + + private static readonly sbyte[] _dist_code = new sbyte[] + { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 0, 0, 16, 17, 18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, + 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 + }; + + internal static readonly sbyte[] LengthCode = new sbyte[] + { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, + 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, + 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 + }; + + + internal static readonly int[] LengthBase = new int[] + { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, + 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 0 + }; + + + internal static readonly int[] DistanceBase = new int[] + { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, + 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 + }; + + + /// + /// Map from a distance to a distance code. + /// + /// + /// No side effects. _dist_code[256] and _dist_code[257] are never used. + /// + internal static int DistanceCode(int dist) + { + return (dist < 256) + ? _dist_code[dist] + : _dist_code[256 + SharedUtils.URShift(dist, 7)]; + } + + internal short[] dyn_tree; // the dynamic tree + internal int max_code; // largest code with non zero frequency + internal StaticTree staticTree; // the corresponding static tree + + // Compute the optimal bit lengths for a tree and update the total bit length + // for the current block. + // IN assertion: the fields freq and dad are set, heap[heap_max] and + // above are the tree nodes sorted by increasing frequency. + // OUT assertions: the field len is set to the optimal bit length, the + // array bl_count contains the frequencies for each bit length. + // The length opt_len is updated; static_len is also updated if stree is + // not null. + internal void gen_bitlen(DeflateManager s) + { + short[] tree = dyn_tree; + short[] stree = staticTree.treeCodes; + int[] extra = staticTree.extraBits; + int base_Renamed = staticTree.extraBase; + int max_length = staticTree.maxLength; + int h; // heap index + int n, m; // iterate over the tree elements + int bits; // bit length + int xbits; // extra bits + short f; // frequency + int overflow = 0; // number of elements with bit length too large + + for (bits = 0; bits <= InternalConstants.MAX_BITS; bits++) + s.bl_count[bits] = 0; + + // In a first pass, compute the optimal bit lengths (which may + // overflow in the case of the bit length tree). + tree[s.heap[s.heap_max] * 2 + 1] = 0; // root of the heap + + for (h = s.heap_max + 1; h < HEAP_SIZE; h++) + { + n = s.heap[h]; + bits = tree[tree[n * 2 + 1] * 2 + 1] + 1; + if (bits > max_length) + { + bits = max_length; overflow++; + } + tree[n * 2 + 1] = (short) bits; + // We overwrite tree[n*2+1] which is no longer needed + + if (n > max_code) + continue; // not a leaf node + + s.bl_count[bits]++; + xbits = 0; + if (n >= base_Renamed) + xbits = extra[n - base_Renamed]; + f = tree[n * 2]; + s.opt_len += f * (bits + xbits); + if (stree != null) + s.static_len += f * (stree[n * 2 + 1] + xbits); + } + if (overflow == 0) + return ; + + // This happens for example on obj2 and pic of the Calgary corpus + // Find the first bit length which could increase: + do + { + bits = max_length - 1; + while (s.bl_count[bits] == 0) + bits--; + s.bl_count[bits]--; // move one leaf down the tree + s.bl_count[bits + 1] = (short) (s.bl_count[bits + 1] + 2); // move one overflow item as its brother + s.bl_count[max_length]--; + // The brother of the overflow item also moves one step up, + // but this does not affect bl_count[max_length] + overflow -= 2; + } + while (overflow > 0); + + for (bits = max_length; bits != 0; bits--) + { + n = s.bl_count[bits]; + while (n != 0) + { + m = s.heap[--h]; + if (m > max_code) + continue; + if (tree[m * 2 + 1] != bits) + { + s.opt_len = (int) (s.opt_len + ((long) bits - (long) tree[m * 2 + 1]) * (long) tree[m * 2]); + tree[m * 2 + 1] = (short) bits; + } + n--; + } + } + } + + // Construct one Huffman tree and assigns the code bit strings and lengths. + // Update the total bit length for the current block. + // IN assertion: the field freq is set for all tree elements. + // OUT assertions: the fields len and code are set to the optimal bit length + // and corresponding code. The length opt_len is updated; static_len is + // also updated if stree is not null. The field max_code is set. + internal void build_tree(DeflateManager s) + { + short[] tree = dyn_tree; + short[] stree = staticTree.treeCodes; + int elems = staticTree.elems; + int n, m; // iterate over heap elements + int max_code = -1; // largest code with non zero frequency + int node; // new node being created + + // Construct the initial heap, with least frequent element in + // heap[1]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + // heap[0] is not used. + s.heap_len = 0; + s.heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) + { + if (tree[n * 2] != 0) + { + s.heap[++s.heap_len] = max_code = n; + s.depth[n] = 0; + } + else + { + tree[n * 2 + 1] = 0; + } + } + + // The pkzip format requires that at least one distance code exists, + // and that at least one bit should be sent even if there is only one + // possible code. So to avoid special checks later on we force at least + // two codes of non zero frequency. + while (s.heap_len < 2) + { + node = s.heap[++s.heap_len] = (max_code < 2?++max_code:0); + tree[node * 2] = 1; + s.depth[node] = 0; + s.opt_len--; + if (stree != null) + s.static_len -= stree[node * 2 + 1]; + // node is 0 or 1 so it does not have extra bits + } + this.max_code = max_code; + + // The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + // establish sub-heaps of increasing lengths: + + for (n = s.heap_len / 2; n >= 1; n--) + s.pqdownheap(tree, n); + + // Construct the Huffman tree by repeatedly combining the least two + // frequent nodes. + + node = elems; // next internal node of the tree + do + { + // n = node of least frequency + n = s.heap[1]; + s.heap[1] = s.heap[s.heap_len--]; + s.pqdownheap(tree, 1); + m = s.heap[1]; // m = node of next least frequency + + s.heap[--s.heap_max] = n; // keep the nodes sorted by frequency + s.heap[--s.heap_max] = m; + + // Create a new node father of n and m + tree[node * 2] = unchecked((short) (tree[n * 2] + tree[m * 2])); + s.depth[node] = (sbyte) (System.Math.Max((byte) s.depth[n], (byte) s.depth[m]) + 1); + tree[n * 2 + 1] = tree[m * 2 + 1] = (short) node; + + // and insert the new node in the heap + s.heap[1] = node++; + s.pqdownheap(tree, 1); + } + while (s.heap_len >= 2); + + s.heap[--s.heap_max] = s.heap[1]; + + // At this point, the fields freq and dad are set. We can now + // generate the bit lengths. + + gen_bitlen(s); + + // The field len is now set, we can generate the bit codes + gen_codes(tree, max_code, s.bl_count); + } + + // Generate the codes for a given tree and bit counts (which need not be + // optimal). + // IN assertion: the array bl_count contains the bit length statistics for + // the given tree and the field len is set for all tree elements. + // OUT assertion: the field code is set for all tree elements of non + // zero code length. + internal static void gen_codes(short[] tree, int max_code, short[] bl_count) + { + short[] next_code = new short[InternalConstants.MAX_BITS + 1]; // next code value for each bit length + short code = 0; // running code value + int bits; // bit index + int n; // code index + + // The distribution counts are first used to generate the code values + // without bit reversal. + for (bits = 1; bits <= InternalConstants.MAX_BITS; bits++) + unchecked { + next_code[bits] = code = (short) ((code + bl_count[bits - 1]) << 1); + } + + // Check that the bit counts in bl_count are consistent. The last code + // must be all ones. + //Assert (code + bl_count[MAX_BITS]-1 == (1<>= 1; //SharedUtils.URShift(code, 1); + res <<= 1; + } + while (--len > 0); + return res >> 1; + } + } +} \ No newline at end of file diff --git a/DotNetZip/Zlib/Zlib DLL.csproj b/DotNetZip/Zlib/Zlib DLL.csproj new file mode 100644 index 0000000..b9d4f61 --- /dev/null +++ b/DotNetZip/Zlib/Zlib DLL.csproj @@ -0,0 +1,185 @@ + + + + Local + 2.0 + Debug + AnyCPU + + + + + Ionic.Zlib + ..\Ionic.snk + JScript + Grid + IE50 + false + Library + Ionic.Zlib + false + OnBuildSuccess + + + + + 3.5 + + + {9816BA86-9250-4C00-A912-25F07F8677D1} + false + false + SAK + SAK + SAK + SAK + v2.0 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + true + + + bin\Debug\ + false + 285212672 + false + + + + + bin\Debug\Ionic.Zlib.XML + true + 4096 + false + + + false + false + false + 4 + full + prompt + AllRules.ruleset + + + bin\Release\ + false + 285212672 + false + + + + + + + true + 4096 + false + + + false + false + false + 4 + full + prompt + AllRules.ruleset + + + + mscorlib + + + System + + + System.Data + + + System.Design + + + System.Drawing + + + System.Management + + + System.Windows.Forms + + + System.Xml + + + + + + + + + + + + + + + + + + CRC32.cs + + + Properties\SolutionInfo.cs + + + + + Ionic.snk + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 2.0 %28x86%29 + true + + + False + .NET Framework 3.0 %28x86%29 + false + + + False + .NET Framework 3.5 + false + + + False + .NET Framework 3.5 SP1 + false + + + + + + + + + + \ No newline at end of file diff --git a/DotNetZip/Zlib/Zlib.cs b/DotNetZip/Zlib/Zlib.cs new file mode 100644 index 0000000..dcfe725 --- /dev/null +++ b/DotNetZip/Zlib/Zlib.cs @@ -0,0 +1,546 @@ +// Zlib.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2009-2011 Dino Chiesa and Microsoft Corporation. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// Last Saved: <2011-August-03 19:52:28> +// +// ------------------------------------------------------------------ +// +// This module defines classes for ZLIB compression and +// decompression. This code is derived from the jzlib implementation of +// zlib, but significantly modified. The object model is not the same, +// and many of the behaviors are new or different. Nonetheless, in +// keeping with the license for jzlib, the copyright to that code is +// included below. +// +// ------------------------------------------------------------------ +// +// The following notice applies to jzlib: +// +// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in +// the documentation and/or other materials provided with the distribution. +// +// 3. The names of the authors may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// ----------------------------------------------------------------------- +// +// jzlib is based on zlib-1.1.3. +// +// The following notice applies to zlib: +// +// ----------------------------------------------------------------------- +// +// Copyright (C) 1995-2004 Jean-loup Gailly and Mark Adler +// +// The ZLIB software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// +// Jean-loup Gailly jloup@gzip.org +// Mark Adler madler@alumni.caltech.edu +// +// ----------------------------------------------------------------------- + + + +using System; +using Interop=System.Runtime.InteropServices; + +namespace Ionic.Zlib +{ + + /// + /// Describes how to flush the current deflate operation. + /// + /// + /// The different FlushType values are useful when using a Deflate in a streaming application. + /// + public enum FlushType + { + /// No flush at all. + None = 0, + + /// Closes the current block, but doesn't flush it to + /// the output. Used internally only in hypothetical + /// scenarios. This was supposed to be removed by Zlib, but it is + /// still in use in some edge cases. + /// + Partial, + + /// + /// Use this during compression to specify that all pending output should be + /// flushed to the output buffer and the output should be aligned on a byte + /// boundary. You might use this in a streaming communication scenario, so that + /// the decompressor can get all input data available so far. When using this + /// with a ZlibCodec, AvailableBytesIn will be zero after the call if + /// enough output space has been provided before the call. Flushing will + /// degrade compression and so it should be used only when necessary. + /// + Sync, + + /// + /// Use this during compression to specify that all output should be flushed, as + /// with FlushType.Sync, but also, the compression state should be reset + /// so that decompression can restart from this point if previous compressed + /// data has been damaged or if random access is desired. Using + /// FlushType.Full too often can significantly degrade the compression. + /// + Full, + + /// Signals the end of the compression/decompression stream. + Finish, + } + + + /// + /// The compression level to be used when using a DeflateStream or ZlibStream with CompressionMode.Compress. + /// + public enum CompressionLevel + { + /// + /// None means that the data will be simply stored, with no change at all. + /// If you are producing ZIPs for use on Mac OSX, be aware that archives produced with CompressionLevel.None + /// cannot be opened with the default zip reader. Use a different CompressionLevel. + /// + None= 0, + /// + /// Same as None. + /// + Level0 = 0, + + /// + /// The fastest but least effective compression. + /// + BestSpeed = 1, + + /// + /// A synonym for BestSpeed. + /// + Level1 = 1, + + /// + /// A little slower, but better, than level 1. + /// + Level2 = 2, + + /// + /// A little slower, but better, than level 2. + /// + Level3 = 3, + + /// + /// A little slower, but better, than level 3. + /// + Level4 = 4, + + /// + /// A little slower than level 4, but with better compression. + /// + Level5 = 5, + + /// + /// The default compression level, with a good balance of speed and compression efficiency. + /// + Default = 6, + /// + /// A synonym for Default. + /// + Level6 = 6, + + /// + /// Pretty good compression! + /// + Level7 = 7, + + /// + /// Better compression than Level7! + /// + Level8 = 8, + + /// + /// The "best" compression, where best means greatest reduction in size of the input data stream. + /// This is also the slowest compression. + /// + BestCompression = 9, + + /// + /// A synonym for BestCompression. + /// + Level9 = 9, + } + + /// + /// Describes options for how the compression algorithm is executed. Different strategies + /// work better on different sorts of data. The strategy parameter can affect the compression + /// ratio and the speed of compression but not the correctness of the compresssion. + /// + public enum CompressionStrategy + { + /// + /// The default strategy is probably the best for normal data. + /// + Default = 0, + + /// + /// The Filtered strategy is intended to be used most effectively with data produced by a + /// filter or predictor. By this definition, filtered data consists mostly of small + /// values with a somewhat random distribution. In this case, the compression algorithm + /// is tuned to compress them better. The effect of Filtered is to force more Huffman + /// coding and less string matching; it is a half-step between Default and HuffmanOnly. + /// + Filtered = 1, + + /// + /// Using HuffmanOnly will force the compressor to do Huffman encoding only, with no + /// string matching. + /// + HuffmanOnly = 2, + } + + + /// + /// An enum to specify the direction of transcoding - whether to compress or decompress. + /// + public enum CompressionMode + { + /// + /// Used to specify that the stream should compress the data. + /// + Compress= 0, + /// + /// Used to specify that the stream should decompress the data. + /// + Decompress = 1, + } + + + /// + /// A general purpose exception class for exceptions in the Zlib library. + /// + [Interop.GuidAttribute("ebc25cf6-9120-4283-b972-0e5520d0000E")] + public class ZlibException : System.Exception + { + /// + /// The ZlibException class captures exception information generated + /// by the Zlib library. + /// + public ZlibException() + : base() + { + } + + /// + /// This ctor collects a message attached to the exception. + /// + /// the message for the exception. + public ZlibException(System.String s) + : base(s) + { + } + } + + + internal class SharedUtils + { + /// + /// Performs an unsigned bitwise right shift with the specified number + /// + /// Number to operate on + /// Ammount of bits to shift + /// The resulting number from the shift operation + public static int URShift(int number, int bits) + { + return (int)((uint)number >> bits); + } + +#if NOT + /// + /// Performs an unsigned bitwise right shift with the specified number + /// + /// Number to operate on + /// Ammount of bits to shift + /// The resulting number from the shift operation + public static long URShift(long number, int bits) + { + return (long) ((UInt64)number >> bits); + } +#endif + + /// + /// Reads a number of characters from the current source TextReader and writes + /// the data to the target array at the specified index. + /// + /// + /// The source TextReader to read from + /// Contains the array of characteres read from the source TextReader. + /// The starting index of the target array. + /// The maximum number of characters to read from the source TextReader. + /// + /// + /// The number of characters read. The number will be less than or equal to + /// count depending on the data available in the source TextReader. Returns -1 + /// if the end of the stream is reached. + /// + public static System.Int32 ReadInput(System.IO.TextReader sourceTextReader, byte[] target, int start, int count) + { + // Returns 0 bytes if not enough space in target + if (target.Length == 0) return 0; + + char[] charArray = new char[target.Length]; + int bytesRead = sourceTextReader.Read(charArray, start, count); + + // Returns -1 if EOF + if (bytesRead == 0) return -1; + + for (int index = start; index < start + bytesRead; index++) + target[index] = (byte)charArray[index]; + + return bytesRead; + } + + + internal static byte[] ToByteArray(System.String sourceString) + { + return System.Text.UTF8Encoding.UTF8.GetBytes(sourceString); + } + + + internal static char[] ToCharArray(byte[] byteArray) + { + return System.Text.UTF8Encoding.UTF8.GetChars(byteArray); + } + } + + internal static class InternalConstants + { + internal static readonly int MAX_BITS = 15; + internal static readonly int BL_CODES = 19; + internal static readonly int D_CODES = 30; + internal static readonly int LITERALS = 256; + internal static readonly int LENGTH_CODES = 29; + internal static readonly int L_CODES = (LITERALS + 1 + LENGTH_CODES); + + // Bit length codes must not exceed MAX_BL_BITS bits + internal static readonly int MAX_BL_BITS = 7; + + // repeat previous bit length 3-6 times (2 bits of repeat count) + internal static readonly int REP_3_6 = 16; + + // repeat a zero length 3-10 times (3 bits of repeat count) + internal static readonly int REPZ_3_10 = 17; + + // repeat a zero length 11-138 times (7 bits of repeat count) + internal static readonly int REPZ_11_138 = 18; + + } + + internal sealed class StaticTree + { + internal static readonly short[] lengthAndLiteralsTreeCodes = new short[] { + 12, 8, 140, 8, 76, 8, 204, 8, 44, 8, 172, 8, 108, 8, 236, 8, + 28, 8, 156, 8, 92, 8, 220, 8, 60, 8, 188, 8, 124, 8, 252, 8, + 2, 8, 130, 8, 66, 8, 194, 8, 34, 8, 162, 8, 98, 8, 226, 8, + 18, 8, 146, 8, 82, 8, 210, 8, 50, 8, 178, 8, 114, 8, 242, 8, + 10, 8, 138, 8, 74, 8, 202, 8, 42, 8, 170, 8, 106, 8, 234, 8, + 26, 8, 154, 8, 90, 8, 218, 8, 58, 8, 186, 8, 122, 8, 250, 8, + 6, 8, 134, 8, 70, 8, 198, 8, 38, 8, 166, 8, 102, 8, 230, 8, + 22, 8, 150, 8, 86, 8, 214, 8, 54, 8, 182, 8, 118, 8, 246, 8, + 14, 8, 142, 8, 78, 8, 206, 8, 46, 8, 174, 8, 110, 8, 238, 8, + 30, 8, 158, 8, 94, 8, 222, 8, 62, 8, 190, 8, 126, 8, 254, 8, + 1, 8, 129, 8, 65, 8, 193, 8, 33, 8, 161, 8, 97, 8, 225, 8, + 17, 8, 145, 8, 81, 8, 209, 8, 49, 8, 177, 8, 113, 8, 241, 8, + 9, 8, 137, 8, 73, 8, 201, 8, 41, 8, 169, 8, 105, 8, 233, 8, + 25, 8, 153, 8, 89, 8, 217, 8, 57, 8, 185, 8, 121, 8, 249, 8, + 5, 8, 133, 8, 69, 8, 197, 8, 37, 8, 165, 8, 101, 8, 229, 8, + 21, 8, 149, 8, 85, 8, 213, 8, 53, 8, 181, 8, 117, 8, 245, 8, + 13, 8, 141, 8, 77, 8, 205, 8, 45, 8, 173, 8, 109, 8, 237, 8, + 29, 8, 157, 8, 93, 8, 221, 8, 61, 8, 189, 8, 125, 8, 253, 8, + 19, 9, 275, 9, 147, 9, 403, 9, 83, 9, 339, 9, 211, 9, 467, 9, + 51, 9, 307, 9, 179, 9, 435, 9, 115, 9, 371, 9, 243, 9, 499, 9, + 11, 9, 267, 9, 139, 9, 395, 9, 75, 9, 331, 9, 203, 9, 459, 9, + 43, 9, 299, 9, 171, 9, 427, 9, 107, 9, 363, 9, 235, 9, 491, 9, + 27, 9, 283, 9, 155, 9, 411, 9, 91, 9, 347, 9, 219, 9, 475, 9, + 59, 9, 315, 9, 187, 9, 443, 9, 123, 9, 379, 9, 251, 9, 507, 9, + 7, 9, 263, 9, 135, 9, 391, 9, 71, 9, 327, 9, 199, 9, 455, 9, + 39, 9, 295, 9, 167, 9, 423, 9, 103, 9, 359, 9, 231, 9, 487, 9, + 23, 9, 279, 9, 151, 9, 407, 9, 87, 9, 343, 9, 215, 9, 471, 9, + 55, 9, 311, 9, 183, 9, 439, 9, 119, 9, 375, 9, 247, 9, 503, 9, + 15, 9, 271, 9, 143, 9, 399, 9, 79, 9, 335, 9, 207, 9, 463, 9, + 47, 9, 303, 9, 175, 9, 431, 9, 111, 9, 367, 9, 239, 9, 495, 9, + 31, 9, 287, 9, 159, 9, 415, 9, 95, 9, 351, 9, 223, 9, 479, 9, + 63, 9, 319, 9, 191, 9, 447, 9, 127, 9, 383, 9, 255, 9, 511, 9, + 0, 7, 64, 7, 32, 7, 96, 7, 16, 7, 80, 7, 48, 7, 112, 7, + 8, 7, 72, 7, 40, 7, 104, 7, 24, 7, 88, 7, 56, 7, 120, 7, + 4, 7, 68, 7, 36, 7, 100, 7, 20, 7, 84, 7, 52, 7, 116, 7, + 3, 8, 131, 8, 67, 8, 195, 8, 35, 8, 163, 8, 99, 8, 227, 8 + }; + + internal static readonly short[] distTreeCodes = new short[] { + 0, 5, 16, 5, 8, 5, 24, 5, 4, 5, 20, 5, 12, 5, 28, 5, + 2, 5, 18, 5, 10, 5, 26, 5, 6, 5, 22, 5, 14, 5, 30, 5, + 1, 5, 17, 5, 9, 5, 25, 5, 5, 5, 21, 5, 13, 5, 29, 5, + 3, 5, 19, 5, 11, 5, 27, 5, 7, 5, 23, 5 }; + + internal static readonly StaticTree Literals; + internal static readonly StaticTree Distances; + internal static readonly StaticTree BitLengths; + + internal short[] treeCodes; // static tree or null + internal int[] extraBits; // extra bits for each code or null + internal int extraBase; // base index for extra_bits + internal int elems; // max number of elements in the tree + internal int maxLength; // max bit length for the codes + + private StaticTree(short[] treeCodes, int[] extraBits, int extraBase, int elems, int maxLength) + { + this.treeCodes = treeCodes; + this.extraBits = extraBits; + this.extraBase = extraBase; + this.elems = elems; + this.maxLength = maxLength; + } + static StaticTree() + { + Literals = new StaticTree(lengthAndLiteralsTreeCodes, Tree.ExtraLengthBits, InternalConstants.LITERALS + 1, InternalConstants.L_CODES, InternalConstants.MAX_BITS); + Distances = new StaticTree(distTreeCodes, Tree.ExtraDistanceBits, 0, InternalConstants.D_CODES, InternalConstants.MAX_BITS); + BitLengths = new StaticTree(null, Tree.extra_blbits, 0, InternalConstants.BL_CODES, InternalConstants.MAX_BL_BITS); + } + } + + + + /// + /// Computes an Adler-32 checksum. + /// + /// + /// The Adler checksum is similar to a CRC checksum, but faster to compute, though less + /// reliable. It is used in producing RFC1950 compressed streams. The Adler checksum + /// is a required part of the "ZLIB" standard. Applications will almost never need to + /// use this class directly. + /// + /// + /// + public sealed class Adler + { + // largest prime smaller than 65536 + private static readonly uint BASE = 65521; + // NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 + private static readonly int NMAX = 5552; + + +#pragma warning disable 3001 +#pragma warning disable 3002 + + /// + /// Calculates the Adler32 checksum. + /// + /// + /// + /// This is used within ZLIB. You probably don't need to use this directly. + /// + /// + /// + /// To compute an Adler32 checksum on a byte array: + /// + /// var adler = Adler.Adler32(0, null, 0, 0); + /// adler = Adler.Adler32(adler, buffer, index, length); + /// + /// + public static uint Adler32(uint adler, byte[] buf, int index, int len) + { + if (buf == null) + return 1; + + uint s1 = (uint) (adler & 0xffff); + uint s2 = (uint) ((adler >> 16) & 0xffff); + + while (len > 0) + { + int k = len < NMAX ? len : NMAX; + len -= k; + while (k >= 16) + { + //s1 += (buf[index++] & 0xff); s2 += s1; + s1 += buf[index++]; s2 += s1; + s1 += buf[index++]; s2 += s1; + s1 += buf[index++]; s2 += s1; + s1 += buf[index++]; s2 += s1; + s1 += buf[index++]; s2 += s1; + s1 += buf[index++]; s2 += s1; + s1 += buf[index++]; s2 += s1; + s1 += buf[index++]; s2 += s1; + s1 += buf[index++]; s2 += s1; + s1 += buf[index++]; s2 += s1; + s1 += buf[index++]; s2 += s1; + s1 += buf[index++]; s2 += s1; + s1 += buf[index++]; s2 += s1; + s1 += buf[index++]; s2 += s1; + s1 += buf[index++]; s2 += s1; + s1 += buf[index++]; s2 += s1; + k -= 16; + } + if (k != 0) + { + do + { + s1 += buf[index++]; + s2 += s1; + } + while (--k != 0); + } + s1 %= BASE; + s2 %= BASE; + } + return (uint)((s2 << 16) | s1); + } +#pragma warning restore 3001 +#pragma warning restore 3002 + + } + +} \ No newline at end of file diff --git a/DotNetZip/Zlib/ZlibBaseStream.cs b/DotNetZip/Zlib/ZlibBaseStream.cs new file mode 100644 index 0000000..700ab7b --- /dev/null +++ b/DotNetZip/Zlib/ZlibBaseStream.cs @@ -0,0 +1,627 @@ +// ZlibBaseStream.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2011-August-06 21:22:38> +// +// ------------------------------------------------------------------ +// +// This module defines the ZlibBaseStream class, which is an intnernal +// base class for DeflateStream, ZlibStream and GZipStream. +// +// ------------------------------------------------------------------ + +using System; +using System.IO; + +namespace Ionic.Zlib +{ + + internal enum ZlibStreamFlavor { ZLIB = 1950, DEFLATE = 1951, GZIP = 1952 } + + internal class ZlibBaseStream : System.IO.Stream + { + protected internal ZlibCodec _z = null; // deferred init... new ZlibCodec(); + + protected internal StreamMode _streamMode = StreamMode.Undefined; + protected internal FlushType _flushMode; + protected internal ZlibStreamFlavor _flavor; + protected internal CompressionMode _compressionMode; + protected internal CompressionLevel _level; + protected internal bool _leaveOpen; + protected internal byte[] _workingBuffer; + protected internal int _bufferSize = ZlibConstants.WorkingBufferSizeDefault; + protected internal byte[] _buf1 = new byte[1]; + + protected internal System.IO.Stream _stream; + protected internal CompressionStrategy Strategy = CompressionStrategy.Default; + + // workitem 7159 + Ionic.Crc.CRC32 crc; + protected internal string _GzipFileName; + protected internal string _GzipComment; + protected internal DateTime _GzipMtime; + protected internal int _gzipHeaderByteCount; + + internal int Crc32 { get { if (crc == null) return 0; return crc.Crc32Result; } } + + public ZlibBaseStream(System.IO.Stream stream, + CompressionMode compressionMode, + CompressionLevel level, + ZlibStreamFlavor flavor, + bool leaveOpen) + : base() + { + this._flushMode = FlushType.None; + //this._workingBuffer = new byte[WORKING_BUFFER_SIZE_DEFAULT]; + this._stream = stream; + this._leaveOpen = leaveOpen; + this._compressionMode = compressionMode; + this._flavor = flavor; + this._level = level; + // workitem 7159 + if (flavor == ZlibStreamFlavor.GZIP) + { + this.crc = new Ionic.Crc.CRC32(); + } + } + + + protected internal bool _wantCompress + { + get + { + return (this._compressionMode == CompressionMode.Compress); + } + } + + private ZlibCodec z + { + get + { + if (_z == null) + { + bool wantRfc1950Header = (this._flavor == ZlibStreamFlavor.ZLIB); + _z = new ZlibCodec(); + if (this._compressionMode == CompressionMode.Decompress) + { + _z.InitializeInflate(wantRfc1950Header); + } + else + { + _z.Strategy = Strategy; + _z.InitializeDeflate(this._level, wantRfc1950Header); + } + } + return _z; + } + } + + + + private byte[] workingBuffer + { + get + { + if (_workingBuffer == null) + _workingBuffer = new byte[_bufferSize]; + return _workingBuffer; + } + } + + + + public override void Write(System.Byte[] buffer, int offset, int count) + { + // workitem 7159 + // calculate the CRC on the unccompressed data (before writing) + if (crc != null) + crc.SlurpBlock(buffer, offset, count); + + if (_streamMode == StreamMode.Undefined) + _streamMode = StreamMode.Writer; + else if (_streamMode != StreamMode.Writer) + throw new ZlibException("Cannot Write after Reading."); + + if (count == 0) + return; + + // first reference of z property will initialize the private var _z + z.InputBuffer = buffer; + _z.NextIn = offset; + _z.AvailableBytesIn = count; + bool done = false; + do + { + _z.OutputBuffer = workingBuffer; + _z.NextOut = 0; + _z.AvailableBytesOut = _workingBuffer.Length; + int rc = (_wantCompress) + ? _z.Deflate(_flushMode) + : _z.Inflate(_flushMode); + if (rc != ZlibConstants.Z_OK && rc != ZlibConstants.Z_STREAM_END) + throw new ZlibException((_wantCompress ? "de" : "in") + "flating: " + _z.Message); + + //if (_workingBuffer.Length - _z.AvailableBytesOut > 0) + _stream.Write(_workingBuffer, 0, _workingBuffer.Length - _z.AvailableBytesOut); + + done = _z.AvailableBytesIn == 0 && _z.AvailableBytesOut != 0; + + // If GZIP and de-compress, we're done when 8 bytes remain. + if (_flavor == ZlibStreamFlavor.GZIP && !_wantCompress) + done = (_z.AvailableBytesIn == 8 && _z.AvailableBytesOut != 0); + + } + while (!done); + } + + + + private void finish() + { + if (_z == null) return; + + if (_streamMode == StreamMode.Writer) + { + bool done = false; + do + { + _z.OutputBuffer = workingBuffer; + _z.NextOut = 0; + _z.AvailableBytesOut = _workingBuffer.Length; + int rc = (_wantCompress) + ? _z.Deflate(FlushType.Finish) + : _z.Inflate(FlushType.Finish); + + if (rc != ZlibConstants.Z_STREAM_END && rc != ZlibConstants.Z_OK) + { + string verb = (_wantCompress ? "de" : "in") + "flating"; + if (_z.Message == null) + throw new ZlibException(String.Format("{0}: (rc = {1})", verb, rc)); + else + throw new ZlibException(verb + ": " + _z.Message); + } + + if (_workingBuffer.Length - _z.AvailableBytesOut > 0) + { + _stream.Write(_workingBuffer, 0, _workingBuffer.Length - _z.AvailableBytesOut); + } + + done = _z.AvailableBytesIn == 0 && _z.AvailableBytesOut != 0; + // If GZIP and de-compress, we're done when 8 bytes remain. + if (_flavor == ZlibStreamFlavor.GZIP && !_wantCompress) + done = (_z.AvailableBytesIn == 8 && _z.AvailableBytesOut != 0); + + } + while (!done); + + Flush(); + + // workitem 7159 + if (_flavor == ZlibStreamFlavor.GZIP) + { + if (_wantCompress) + { + // Emit the GZIP trailer: CRC32 and size mod 2^32 + int c1 = crc.Crc32Result; + _stream.Write(BitConverter.GetBytes(c1), 0, 4); + int c2 = (Int32)(crc.TotalBytesRead & 0x00000000FFFFFFFF); + _stream.Write(BitConverter.GetBytes(c2), 0, 4); + } + else + { + throw new ZlibException("Writing with decompression is not supported."); + } + } + } + // workitem 7159 + else if (_streamMode == StreamMode.Reader) + { + if (_flavor == ZlibStreamFlavor.GZIP) + { + if (!_wantCompress) + { + // workitem 8501: handle edge case (decompress empty stream) + if (_z.TotalBytesOut == 0L) + return; + + // Read and potentially verify the GZIP trailer: + // CRC32 and size mod 2^32 + byte[] trailer = new byte[8]; + + // workitems 8679 & 12554 + if (_z.AvailableBytesIn < 8) + { + // Make sure we have read to the end of the stream + Array.Copy(_z.InputBuffer, _z.NextIn, trailer, 0, _z.AvailableBytesIn); + int bytesNeeded = 8 - _z.AvailableBytesIn; + int bytesRead = _stream.Read(trailer, + _z.AvailableBytesIn, + bytesNeeded); + if (bytesNeeded != bytesRead) + { + throw new ZlibException(String.Format("Missing or incomplete GZIP trailer. Expected 8 bytes, got {0}.", + _z.AvailableBytesIn + bytesRead)); + } + } + else + { + Array.Copy(_z.InputBuffer, _z.NextIn, trailer, 0, trailer.Length); + } + + Int32 crc32_expected = BitConverter.ToInt32(trailer, 0); + Int32 crc32_actual = crc.Crc32Result; + Int32 isize_expected = BitConverter.ToInt32(trailer, 4); + Int32 isize_actual = (Int32)(_z.TotalBytesOut & 0x00000000FFFFFFFF); + + if (crc32_actual != crc32_expected) + throw new ZlibException(String.Format("Bad CRC32 in GZIP trailer. (actual({0:X8})!=expected({1:X8}))", crc32_actual, crc32_expected)); + + if (isize_actual != isize_expected) + throw new ZlibException(String.Format("Bad size in GZIP trailer. (actual({0})!=expected({1}))", isize_actual, isize_expected)); + + } + else + { + throw new ZlibException("Reading with compression is not supported."); + } + } + } + } + + + private void end() + { + if (z == null) + return; + if (_wantCompress) + { + _z.EndDeflate(); + } + else + { + _z.EndInflate(); + } + _z = null; + } + + + public override void Close() + { + if (_stream == null) return; + try + { + finish(); + } + finally + { + end(); + if (!_leaveOpen) _stream.Close(); + _stream = null; + } + } + + public override void Flush() + { + _stream.Flush(); + } + + public override System.Int64 Seek(System.Int64 offset, System.IO.SeekOrigin origin) + { + throw new NotImplementedException(); + //_outStream.Seek(offset, origin); + } + public override void SetLength(System.Int64 value) + { + _stream.SetLength(value); + } + + +#if NOT + public int Read() + { + if (Read(_buf1, 0, 1) == 0) + return 0; + // calculate CRC after reading + if (crc!=null) + crc.SlurpBlock(_buf1,0,1); + return (_buf1[0] & 0xFF); + } +#endif + + private bool nomoreinput = false; + + + + private string ReadZeroTerminatedString() + { + var list = new System.Collections.Generic.List(); + bool done = false; + do + { + // workitem 7740 + int n = _stream.Read(_buf1, 0, 1); + if (n != 1) + throw new ZlibException("Unexpected EOF reading GZIP header."); + else + { + if (_buf1[0] == 0) + done = true; + else + list.Add(_buf1[0]); + } + } while (!done); + byte[] a = list.ToArray(); + return GZipStream.iso8859dash1.GetString(a, 0, a.Length); + } + + + private int _ReadAndValidateGzipHeader() + { + int totalBytesRead = 0; + // read the header on the first read + byte[] header = new byte[10]; + int n = _stream.Read(header, 0, header.Length); + + // workitem 8501: handle edge case (decompress empty stream) + if (n == 0) + return 0; + + if (n != 10) + throw new ZlibException("Not a valid GZIP stream."); + + if (header[0] != 0x1F || header[1] != 0x8B || header[2] != 8) + throw new ZlibException("Bad GZIP header."); + + Int32 timet = BitConverter.ToInt32(header, 4); + _GzipMtime = GZipStream._unixEpoch.AddSeconds(timet); + totalBytesRead += n; + if ((header[3] & 0x04) == 0x04) + { + // read and discard extra field + n = _stream.Read(header, 0, 2); // 2-byte length field + totalBytesRead += n; + + Int16 extraLength = (Int16)(header[0] + header[1] * 256); + byte[] extra = new byte[extraLength]; + n = _stream.Read(extra, 0, extra.Length); + if (n != extraLength) + throw new ZlibException("Unexpected end-of-file reading GZIP header."); + totalBytesRead += n; + } + if ((header[3] & 0x08) == 0x08) + _GzipFileName = ReadZeroTerminatedString(); + if ((header[3] & 0x10) == 0x010) + _GzipComment = ReadZeroTerminatedString(); + if ((header[3] & 0x02) == 0x02) + Read(_buf1, 0, 1); // CRC16, ignore + + return totalBytesRead; + } + + + + public override System.Int32 Read(System.Byte[] buffer, System.Int32 offset, System.Int32 count) + { + // According to MS documentation, any implementation of the IO.Stream.Read function must: + // (a) throw an exception if offset & count reference an invalid part of the buffer, + // or if count < 0, or if buffer is null + // (b) return 0 only upon EOF, or if count = 0 + // (c) if not EOF, then return at least 1 byte, up to bytes + + if (_streamMode == StreamMode.Undefined) + { + if (!this._stream.CanRead) throw new ZlibException("The stream is not readable."); + // for the first read, set up some controls. + _streamMode = StreamMode.Reader; + // (The first reference to _z goes through the private accessor which + // may initialize it.) + z.AvailableBytesIn = 0; + if (_flavor == ZlibStreamFlavor.GZIP) + { + _gzipHeaderByteCount = _ReadAndValidateGzipHeader(); + // workitem 8501: handle edge case (decompress empty stream) + if (_gzipHeaderByteCount == 0) + return 0; + } + } + + if (_streamMode != StreamMode.Reader) + throw new ZlibException("Cannot Read after Writing."); + + if (count == 0) return 0; + if (nomoreinput && _wantCompress) return 0; // workitem 8557 + if (buffer == null) throw new ArgumentNullException("buffer"); + if (count < 0) throw new ArgumentOutOfRangeException("count"); + if (offset < buffer.GetLowerBound(0)) throw new ArgumentOutOfRangeException("offset"); + if ((offset + count) > buffer.GetLength(0)) throw new ArgumentOutOfRangeException("count"); + + int rc = 0; + + // set up the output of the deflate/inflate codec: + _z.OutputBuffer = buffer; + _z.NextOut = offset; + _z.AvailableBytesOut = count; + + // This is necessary in case _workingBuffer has been resized. (new byte[]) + // (The first reference to _workingBuffer goes through the private accessor which + // may initialize it.) + _z.InputBuffer = workingBuffer; + + do + { + // need data in _workingBuffer in order to deflate/inflate. Here, we check if we have any. + if ((_z.AvailableBytesIn == 0) && (!nomoreinput)) + { + // No data available, so try to Read data from the captive stream. + _z.NextIn = 0; + _z.AvailableBytesIn = _stream.Read(_workingBuffer, 0, _workingBuffer.Length); + if (_z.AvailableBytesIn == 0) + nomoreinput = true; + + } + // we have data in InputBuffer; now compress or decompress as appropriate + rc = (_wantCompress) + ? _z.Deflate(_flushMode) + : _z.Inflate(_flushMode); + + if (nomoreinput && (rc == ZlibConstants.Z_BUF_ERROR)) + return 0; + + if (rc != ZlibConstants.Z_OK && rc != ZlibConstants.Z_STREAM_END) + throw new ZlibException(String.Format("{0}flating: rc={1} msg={2}", (_wantCompress ? "de" : "in"), rc, _z.Message)); + + if ((nomoreinput || rc == ZlibConstants.Z_STREAM_END) && (_z.AvailableBytesOut == count)) + break; // nothing more to read + } + //while (_z.AvailableBytesOut == count && rc == ZlibConstants.Z_OK); + while (_z.AvailableBytesOut > 0 && !nomoreinput && rc == ZlibConstants.Z_OK); + + + // workitem 8557 + // is there more room in output? + if (_z.AvailableBytesOut > 0) + { + if (rc == ZlibConstants.Z_OK && _z.AvailableBytesIn == 0) + { + // deferred + } + + // are we completely done reading? + if (nomoreinput) + { + // and in compression? + if (_wantCompress) + { + // no more input data available; therefore we flush to + // try to complete the read + rc = _z.Deflate(FlushType.Finish); + + if (rc != ZlibConstants.Z_OK && rc != ZlibConstants.Z_STREAM_END) + throw new ZlibException(String.Format("Deflating: rc={0} msg={1}", rc, _z.Message)); + } + } + } + + + rc = (count - _z.AvailableBytesOut); + + // calculate CRC after reading + if (crc != null) + crc.SlurpBlock(buffer, offset, rc); + + return rc; + } + + + + public override System.Boolean CanRead + { + get { return this._stream.CanRead; } + } + + public override System.Boolean CanSeek + { + get { return this._stream.CanSeek; } + } + + public override System.Boolean CanWrite + { + get { return this._stream.CanWrite; } + } + + public override System.Int64 Length + { + get { return _stream.Length; } + } + + public override long Position + { + get { throw new NotImplementedException(); } + set { throw new NotImplementedException(); } + } + + internal enum StreamMode + { + Writer, + Reader, + Undefined, + } + + + public static void CompressString(String s, Stream compressor) + { + byte[] uncompressed = System.Text.Encoding.UTF8.GetBytes(s); + using (compressor) + { + compressor.Write(uncompressed, 0, uncompressed.Length); + } + } + + public static void CompressBuffer(byte[] b, Stream compressor) + { + // workitem 8460 + using (compressor) + { + compressor.Write(b, 0, b.Length); + } + } + + public static String UncompressString(byte[] compressed, Stream decompressor) + { + // workitem 8460 + byte[] working = new byte[1024]; + var encoding = System.Text.Encoding.UTF8; + using (var output = new MemoryStream()) + { + using (decompressor) + { + int n; + while ((n = decompressor.Read(working, 0, working.Length)) != 0) + { + output.Write(working, 0, n); + } + } + + // reset to allow read from start + output.Seek(0, SeekOrigin.Begin); + var sr = new StreamReader(output, encoding); + return sr.ReadToEnd(); + } + } + + public static byte[] UncompressBuffer(byte[] compressed, Stream decompressor) + { + // workitem 8460 + byte[] working = new byte[1024]; + using (var output = new MemoryStream()) + { + using (decompressor) + { + int n; + while ((n = decompressor.Read(working, 0, working.Length)) != 0) + { + output.Write(working, 0, n); + } + } + return output.ToArray(); + } + } + + } + + +} diff --git a/DotNetZip/Zlib/ZlibCodec.cs b/DotNetZip/Zlib/ZlibCodec.cs new file mode 100644 index 0000000..ab0abcf --- /dev/null +++ b/DotNetZip/Zlib/ZlibCodec.cs @@ -0,0 +1,717 @@ +// ZlibCodec.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2009-November-03 15:40:51> +// +// ------------------------------------------------------------------ +// +// This module defines a Codec for ZLIB compression and +// decompression. This code extends code that was based the jzlib +// implementation of zlib, but this code is completely novel. The codec +// class is new, and encapsulates some behaviors that are new, and some +// that were present in other classes in the jzlib code base. In +// keeping with the license for jzlib, the copyright to the jzlib code +// is included below. +// +// ------------------------------------------------------------------ +// +// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in +// the documentation and/or other materials provided with the distribution. +// +// 3. The names of the authors may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// ----------------------------------------------------------------------- +// +// This program is based on zlib-1.1.3; credit to authors +// Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) +// and contributors of zlib. +// +// ----------------------------------------------------------------------- + + +using System; +using Interop=System.Runtime.InteropServices; + +namespace Ionic.Zlib +{ + /// + /// Encoder and Decoder for ZLIB and DEFLATE (IETF RFC1950 and RFC1951). + /// + /// + /// + /// This class compresses and decompresses data according to the Deflate algorithm + /// and optionally, the ZLIB format, as documented in RFC 1950 - ZLIB and RFC 1951 - DEFLATE. + /// + [Interop.GuidAttribute("ebc25cf6-9120-4283-b972-0e5520d0000D")] + [Interop.ComVisible(true)] +#if !NETCF + [Interop.ClassInterface(Interop.ClassInterfaceType.AutoDispatch)] +#endif + sealed public class ZlibCodec + { + /// + /// The buffer from which data is taken. + /// + public byte[] InputBuffer; + + /// + /// An index into the InputBuffer array, indicating where to start reading. + /// + public int NextIn; + + /// + /// The number of bytes available in the InputBuffer, starting at NextIn. + /// + /// + /// Generally you should set this to InputBuffer.Length before the first Inflate() or Deflate() call. + /// The class will update this number as calls to Inflate/Deflate are made. + /// + public int AvailableBytesIn; + + /// + /// Total number of bytes read so far, through all calls to Inflate()/Deflate(). + /// + public long TotalBytesIn; + + /// + /// Buffer to store output data. + /// + public byte[] OutputBuffer; + + /// + /// An index into the OutputBuffer array, indicating where to start writing. + /// + public int NextOut; + + /// + /// The number of bytes available in the OutputBuffer, starting at NextOut. + /// + /// + /// Generally you should set this to OutputBuffer.Length before the first Inflate() or Deflate() call. + /// The class will update this number as calls to Inflate/Deflate are made. + /// + public int AvailableBytesOut; + + /// + /// Total number of bytes written to the output so far, through all calls to Inflate()/Deflate(). + /// + public long TotalBytesOut; + + /// + /// used for diagnostics, when something goes wrong! + /// + public System.String Message; + + internal DeflateManager dstate; + internal InflateManager istate; + + internal uint _Adler32; + + /// + /// The compression level to use in this codec. Useful only in compression mode. + /// + public CompressionLevel CompressLevel = CompressionLevel.Default; + + /// + /// The number of Window Bits to use. + /// + /// + /// This gauges the size of the sliding window, and hence the + /// compression effectiveness as well as memory consumption. It's best to just leave this + /// setting alone if you don't know what it is. The maximum value is 15 bits, which implies + /// a 32k window. + /// + public int WindowBits = ZlibConstants.WindowBitsDefault; + + /// + /// The compression strategy to use. + /// + /// + /// This is only effective in compression. The theory offered by ZLIB is that different + /// strategies could potentially produce significant differences in compression behavior + /// for different data sets. Unfortunately I don't have any good recommendations for how + /// to set it differently. When I tested changing the strategy I got minimally different + /// compression performance. It's best to leave this property alone if you don't have a + /// good feel for it. Or, you may want to produce a test harness that runs through the + /// different strategy options and evaluates them on different file types. If you do that, + /// let me know your results. + /// + public CompressionStrategy Strategy = CompressionStrategy.Default; + + + /// + /// The Adler32 checksum on the data transferred through the codec so far. You probably don't need to look at this. + /// + public int Adler32 { get { return (int)_Adler32; } } + + + /// + /// Create a ZlibCodec. + /// + /// + /// If you use this default constructor, you will later have to explicitly call + /// InitializeInflate() or InitializeDeflate() before using the ZlibCodec to compress + /// or decompress. + /// + public ZlibCodec() { } + + /// + /// Create a ZlibCodec that either compresses or decompresses. + /// + /// + /// Indicates whether the codec should compress (deflate) or decompress (inflate). + /// + public ZlibCodec(CompressionMode mode) + { + if (mode == CompressionMode.Compress) + { + int rc = InitializeDeflate(); + if (rc != ZlibConstants.Z_OK) throw new ZlibException("Cannot initialize for deflate."); + } + else if (mode == CompressionMode.Decompress) + { + int rc = InitializeInflate(); + if (rc != ZlibConstants.Z_OK) throw new ZlibException("Cannot initialize for inflate."); + } + else throw new ZlibException("Invalid ZlibStreamFlavor."); + } + + /// + /// Initialize the inflation state. + /// + /// + /// It is not necessary to call this before using the ZlibCodec to inflate data; + /// It is implicitly called when you call the constructor. + /// + /// Z_OK if everything goes well. + public int InitializeInflate() + { + return InitializeInflate(this.WindowBits); + } + + /// + /// Initialize the inflation state with an explicit flag to + /// govern the handling of RFC1950 header bytes. + /// + /// + /// + /// By default, the ZLIB header defined in RFC 1950 is expected. If + /// you want to read a zlib stream you should specify true for + /// expectRfc1950Header. If you have a deflate stream, you will want to specify + /// false. It is only necessary to invoke this initializer explicitly if you + /// want to specify false. + /// + /// + /// whether to expect an RFC1950 header byte + /// pair when reading the stream of data to be inflated. + /// + /// Z_OK if everything goes well. + public int InitializeInflate(bool expectRfc1950Header) + { + return InitializeInflate(this.WindowBits, expectRfc1950Header); + } + + /// + /// Initialize the ZlibCodec for inflation, with the specified number of window bits. + /// + /// The number of window bits to use. If you need to ask what that is, + /// then you shouldn't be calling this initializer. + /// Z_OK if all goes well. + public int InitializeInflate(int windowBits) + { + this.WindowBits = windowBits; + return InitializeInflate(windowBits, true); + } + + /// + /// Initialize the inflation state with an explicit flag to govern the handling of + /// RFC1950 header bytes. + /// + /// + /// + /// If you want to read a zlib stream you should specify true for + /// expectRfc1950Header. In this case, the library will expect to find a ZLIB + /// header, as defined in RFC + /// 1950, in the compressed stream. If you will be reading a DEFLATE or + /// GZIP stream, which does not have such a header, you will want to specify + /// false. + /// + /// + /// whether to expect an RFC1950 header byte pair when reading + /// the stream of data to be inflated. + /// The number of window bits to use. If you need to ask what that is, + /// then you shouldn't be calling this initializer. + /// Z_OK if everything goes well. + public int InitializeInflate(int windowBits, bool expectRfc1950Header) + { + this.WindowBits = windowBits; + if (dstate != null) throw new ZlibException("You may not call InitializeInflate() after calling InitializeDeflate()."); + istate = new InflateManager(expectRfc1950Header); + return istate.Initialize(this, windowBits); + } + + /// + /// Inflate the data in the InputBuffer, placing the result in the OutputBuffer. + /// + /// + /// You must have set InputBuffer and OutputBuffer, NextIn and NextOut, and AvailableBytesIn and + /// AvailableBytesOut before calling this method. + /// + /// + /// + /// private void InflateBuffer() + /// { + /// int bufferSize = 1024; + /// byte[] buffer = new byte[bufferSize]; + /// ZlibCodec decompressor = new ZlibCodec(); + /// + /// Console.WriteLine("\n============================================"); + /// Console.WriteLine("Size of Buffer to Inflate: {0} bytes.", CompressedBytes.Length); + /// MemoryStream ms = new MemoryStream(DecompressedBytes); + /// + /// int rc = decompressor.InitializeInflate(); + /// + /// decompressor.InputBuffer = CompressedBytes; + /// decompressor.NextIn = 0; + /// decompressor.AvailableBytesIn = CompressedBytes.Length; + /// + /// decompressor.OutputBuffer = buffer; + /// + /// // pass 1: inflate + /// do + /// { + /// decompressor.NextOut = 0; + /// decompressor.AvailableBytesOut = buffer.Length; + /// rc = decompressor.Inflate(FlushType.None); + /// + /// if (rc != ZlibConstants.Z_OK && rc != ZlibConstants.Z_STREAM_END) + /// throw new Exception("inflating: " + decompressor.Message); + /// + /// ms.Write(decompressor.OutputBuffer, 0, buffer.Length - decompressor.AvailableBytesOut); + /// } + /// while (decompressor.AvailableBytesIn > 0 || decompressor.AvailableBytesOut == 0); + /// + /// // pass 2: finish and flush + /// do + /// { + /// decompressor.NextOut = 0; + /// decompressor.AvailableBytesOut = buffer.Length; + /// rc = decompressor.Inflate(FlushType.Finish); + /// + /// if (rc != ZlibConstants.Z_STREAM_END && rc != ZlibConstants.Z_OK) + /// throw new Exception("inflating: " + decompressor.Message); + /// + /// if (buffer.Length - decompressor.AvailableBytesOut > 0) + /// ms.Write(buffer, 0, buffer.Length - decompressor.AvailableBytesOut); + /// } + /// while (decompressor.AvailableBytesIn > 0 || decompressor.AvailableBytesOut == 0); + /// + /// decompressor.EndInflate(); + /// } + /// + /// + /// + /// The flush to use when inflating. + /// Z_OK if everything goes well. + public int Inflate(FlushType flush) + { + if (istate == null) + throw new ZlibException("No Inflate State!"); + return istate.Inflate(flush); + } + + + /// + /// Ends an inflation session. + /// + /// + /// Call this after successively calling Inflate(). This will cause all buffers to be flushed. + /// After calling this you cannot call Inflate() without a intervening call to one of the + /// InitializeInflate() overloads. + /// + /// Z_OK if everything goes well. + public int EndInflate() + { + if (istate == null) + throw new ZlibException("No Inflate State!"); + int ret = istate.End(); + istate = null; + return ret; + } + + /// + /// I don't know what this does! + /// + /// Z_OK if everything goes well. + public int SyncInflate() + { + if (istate == null) + throw new ZlibException("No Inflate State!"); + return istate.Sync(); + } + + /// + /// Initialize the ZlibCodec for deflation operation. + /// + /// + /// The codec will use the MAX window bits and the default level of compression. + /// + /// + /// + /// int bufferSize = 40000; + /// byte[] CompressedBytes = new byte[bufferSize]; + /// byte[] DecompressedBytes = new byte[bufferSize]; + /// + /// ZlibCodec compressor = new ZlibCodec(); + /// + /// compressor.InitializeDeflate(CompressionLevel.Default); + /// + /// compressor.InputBuffer = System.Text.ASCIIEncoding.ASCII.GetBytes(TextToCompress); + /// compressor.NextIn = 0; + /// compressor.AvailableBytesIn = compressor.InputBuffer.Length; + /// + /// compressor.OutputBuffer = CompressedBytes; + /// compressor.NextOut = 0; + /// compressor.AvailableBytesOut = CompressedBytes.Length; + /// + /// while (compressor.TotalBytesIn != TextToCompress.Length && compressor.TotalBytesOut < bufferSize) + /// { + /// compressor.Deflate(FlushType.None); + /// } + /// + /// while (true) + /// { + /// int rc= compressor.Deflate(FlushType.Finish); + /// if (rc == ZlibConstants.Z_STREAM_END) break; + /// } + /// + /// compressor.EndDeflate(); + /// + /// + /// + /// Z_OK if all goes well. You generally don't need to check the return code. + public int InitializeDeflate() + { + return _InternalInitializeDeflate(true); + } + + /// + /// Initialize the ZlibCodec for deflation operation, using the specified CompressionLevel. + /// + /// + /// The codec will use the maximum window bits (15) and the specified + /// CompressionLevel. It will emit a ZLIB stream as it compresses. + /// + /// The compression level for the codec. + /// Z_OK if all goes well. + public int InitializeDeflate(CompressionLevel level) + { + this.CompressLevel = level; + return _InternalInitializeDeflate(true); + } + + + /// + /// Initialize the ZlibCodec for deflation operation, using the specified CompressionLevel, + /// and the explicit flag governing whether to emit an RFC1950 header byte pair. + /// + /// + /// The codec will use the maximum window bits (15) and the specified CompressionLevel. + /// If you want to generate a zlib stream, you should specify true for + /// wantRfc1950Header. In this case, the library will emit a ZLIB + /// header, as defined in RFC + /// 1950, in the compressed stream. + /// + /// The compression level for the codec. + /// whether to emit an initial RFC1950 byte pair in the compressed stream. + /// Z_OK if all goes well. + public int InitializeDeflate(CompressionLevel level, bool wantRfc1950Header) + { + this.CompressLevel = level; + return _InternalInitializeDeflate(wantRfc1950Header); + } + + + /// + /// Initialize the ZlibCodec for deflation operation, using the specified CompressionLevel, + /// and the specified number of window bits. + /// + /// + /// The codec will use the specified number of window bits and the specified CompressionLevel. + /// + /// The compression level for the codec. + /// the number of window bits to use. If you don't know what this means, don't use this method. + /// Z_OK if all goes well. + public int InitializeDeflate(CompressionLevel level, int bits) + { + this.CompressLevel = level; + this.WindowBits = bits; + return _InternalInitializeDeflate(true); + } + + /// + /// Initialize the ZlibCodec for deflation operation, using the specified + /// CompressionLevel, the specified number of window bits, and the explicit flag + /// governing whether to emit an RFC1950 header byte pair. + /// + /// + /// The compression level for the codec. + /// whether to emit an initial RFC1950 byte pair in the compressed stream. + /// the number of window bits to use. If you don't know what this means, don't use this method. + /// Z_OK if all goes well. + public int InitializeDeflate(CompressionLevel level, int bits, bool wantRfc1950Header) + { + this.CompressLevel = level; + this.WindowBits = bits; + return _InternalInitializeDeflate(wantRfc1950Header); + } + + private int _InternalInitializeDeflate(bool wantRfc1950Header) + { + if (istate != null) throw new ZlibException("You may not call InitializeDeflate() after calling InitializeInflate()."); + dstate = new DeflateManager(); + dstate.WantRfc1950HeaderBytes = wantRfc1950Header; + + return dstate.Initialize(this, this.CompressLevel, this.WindowBits, this.Strategy); + } + + /// + /// Deflate one batch of data. + /// + /// + /// You must have set InputBuffer and OutputBuffer before calling this method. + /// + /// + /// + /// private void DeflateBuffer(CompressionLevel level) + /// { + /// int bufferSize = 1024; + /// byte[] buffer = new byte[bufferSize]; + /// ZlibCodec compressor = new ZlibCodec(); + /// + /// Console.WriteLine("\n============================================"); + /// Console.WriteLine("Size of Buffer to Deflate: {0} bytes.", UncompressedBytes.Length); + /// MemoryStream ms = new MemoryStream(); + /// + /// int rc = compressor.InitializeDeflate(level); + /// + /// compressor.InputBuffer = UncompressedBytes; + /// compressor.NextIn = 0; + /// compressor.AvailableBytesIn = UncompressedBytes.Length; + /// + /// compressor.OutputBuffer = buffer; + /// + /// // pass 1: deflate + /// do + /// { + /// compressor.NextOut = 0; + /// compressor.AvailableBytesOut = buffer.Length; + /// rc = compressor.Deflate(FlushType.None); + /// + /// if (rc != ZlibConstants.Z_OK && rc != ZlibConstants.Z_STREAM_END) + /// throw new Exception("deflating: " + compressor.Message); + /// + /// ms.Write(compressor.OutputBuffer, 0, buffer.Length - compressor.AvailableBytesOut); + /// } + /// while (compressor.AvailableBytesIn > 0 || compressor.AvailableBytesOut == 0); + /// + /// // pass 2: finish and flush + /// do + /// { + /// compressor.NextOut = 0; + /// compressor.AvailableBytesOut = buffer.Length; + /// rc = compressor.Deflate(FlushType.Finish); + /// + /// if (rc != ZlibConstants.Z_STREAM_END && rc != ZlibConstants.Z_OK) + /// throw new Exception("deflating: " + compressor.Message); + /// + /// if (buffer.Length - compressor.AvailableBytesOut > 0) + /// ms.Write(buffer, 0, buffer.Length - compressor.AvailableBytesOut); + /// } + /// while (compressor.AvailableBytesIn > 0 || compressor.AvailableBytesOut == 0); + /// + /// compressor.EndDeflate(); + /// + /// ms.Seek(0, SeekOrigin.Begin); + /// CompressedBytes = new byte[compressor.TotalBytesOut]; + /// ms.Read(CompressedBytes, 0, CompressedBytes.Length); + /// } + /// + /// + /// whether to flush all data as you deflate. Generally you will want to + /// use Z_NO_FLUSH here, in a series of calls to Deflate(), and then call EndDeflate() to + /// flush everything. + /// + /// Z_OK if all goes well. + public int Deflate(FlushType flush) + { + if (dstate == null) + throw new ZlibException("No Deflate State!"); + return dstate.Deflate(flush); + } + + /// + /// End a deflation session. + /// + /// + /// Call this after making a series of one or more calls to Deflate(). All buffers are flushed. + /// + /// Z_OK if all goes well. + public int EndDeflate() + { + if (dstate == null) + throw new ZlibException("No Deflate State!"); + // TODO: dinoch Tue, 03 Nov 2009 15:39 (test this) + //int ret = dstate.End(); + dstate = null; + return ZlibConstants.Z_OK; //ret; + } + + /// + /// Reset a codec for another deflation session. + /// + /// + /// Call this to reset the deflation state. For example if a thread is deflating + /// non-consecutive blocks, you can call Reset() after the Deflate(Sync) of the first + /// block and before the next Deflate(None) of the second block. + /// + /// Z_OK if all goes well. + public void ResetDeflate() + { + if (dstate == null) + throw new ZlibException("No Deflate State!"); + dstate.Reset(); + } + + + /// + /// Set the CompressionStrategy and CompressionLevel for a deflation session. + /// + /// the level of compression to use. + /// the strategy to use for compression. + /// Z_OK if all goes well. + public int SetDeflateParams(CompressionLevel level, CompressionStrategy strategy) + { + if (dstate == null) + throw new ZlibException("No Deflate State!"); + return dstate.SetParams(level, strategy); + } + + + /// + /// Set the dictionary to be used for either Inflation or Deflation. + /// + /// The dictionary bytes to use. + /// Z_OK if all goes well. + public int SetDictionary(byte[] dictionary) + { + if (istate != null) + return istate.SetDictionary(dictionary); + + if (dstate != null) + return dstate.SetDictionary(dictionary); + + throw new ZlibException("No Inflate or Deflate state!"); + } + + // Flush as much pending output as possible. All deflate() output goes + // through this function so some applications may wish to modify it + // to avoid allocating a large strm->next_out buffer and copying into it. + // (See also read_buf()). + internal void flush_pending() + { + int len = dstate.pendingCount; + + if (len > AvailableBytesOut) + len = AvailableBytesOut; + if (len == 0) + return; + + if (dstate.pending.Length <= dstate.nextPending || + OutputBuffer.Length <= NextOut || + dstate.pending.Length < (dstate.nextPending + len) || + OutputBuffer.Length < (NextOut + len)) + { + throw new ZlibException(String.Format("Invalid State. (pending.Length={0}, pendingCount={1})", + dstate.pending.Length, dstate.pendingCount)); + } + + Array.Copy(dstate.pending, dstate.nextPending, OutputBuffer, NextOut, len); + + NextOut += len; + dstate.nextPending += len; + TotalBytesOut += len; + AvailableBytesOut -= len; + dstate.pendingCount -= len; + if (dstate.pendingCount == 0) + { + dstate.nextPending = 0; + } + } + + // Read a new buffer from the current input stream, update the adler32 + // and total number of bytes read. All deflate() input goes through + // this function so some applications may wish to modify it to avoid + // allocating a large strm->next_in buffer and copying from it. + // (See also flush_pending()). + internal int read_buf(byte[] buf, int start, int size) + { + int len = AvailableBytesIn; + + if (len > size) + len = size; + if (len == 0) + return 0; + + AvailableBytesIn -= len; + + if (dstate.WantRfc1950HeaderBytes) + { + _Adler32 = Adler.Adler32(_Adler32, InputBuffer, NextIn, len); + } + Array.Copy(InputBuffer, NextIn, buf, start, len); + NextIn += len; + TotalBytesIn += len; + return len; + } + + } +} \ No newline at end of file diff --git a/DotNetZip/Zlib/ZlibConstants.cs b/DotNetZip/Zlib/ZlibConstants.cs new file mode 100644 index 0000000..59ae730 --- /dev/null +++ b/DotNetZip/Zlib/ZlibConstants.cs @@ -0,0 +1,128 @@ +// ZlibConstants.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2009-November-03 18:50:19> +// +// ------------------------------------------------------------------ +// +// This module defines constants used by the zlib class library. This +// code is derived from the jzlib implementation of zlib, but +// significantly modified. In keeping with the license for jzlib, the +// copyright to that code is included here. +// +// ------------------------------------------------------------------ +// +// Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in +// the documentation and/or other materials provided with the distribution. +// +// 3. The names of the authors may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, +// INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// ----------------------------------------------------------------------- +// +// This program is based on zlib-1.1.3; credit to authors +// Jean-loup Gailly(jloup@gzip.org) and Mark Adler(madler@alumni.caltech.edu) +// and contributors of zlib. +// +// ----------------------------------------------------------------------- + + +using System; + +namespace Ionic.Zlib +{ + /// + /// A bunch of constants used in the Zlib interface. + /// + public static class ZlibConstants + { + /// + /// The maximum number of window bits for the Deflate algorithm. + /// + public const int WindowBitsMax = 15; // 32K LZ77 window + + /// + /// The default number of window bits for the Deflate algorithm. + /// + public const int WindowBitsDefault = WindowBitsMax; + + /// + /// indicates everything is A-OK + /// + public const int Z_OK = 0; + + /// + /// Indicates that the last operation reached the end of the stream. + /// + public const int Z_STREAM_END = 1; + + /// + /// The operation ended in need of a dictionary. + /// + public const int Z_NEED_DICT = 2; + + /// + /// There was an error with the stream - not enough data, not open and readable, etc. + /// + public const int Z_STREAM_ERROR = -2; + + /// + /// There was an error with the data - not enough data, bad data, etc. + /// + public const int Z_DATA_ERROR = -3; + + /// + /// There was an error with the working buffer. + /// + public const int Z_BUF_ERROR = -5; + + /// + /// The size of the working buffer used in the ZlibCodec class. Defaults to 8192 bytes. + /// +#if NETCF + public const int WorkingBufferSizeDefault = 8192; +#else + public const int WorkingBufferSizeDefault = 16384; +#endif + /// + /// The minimum size of the working buffer used in the ZlibCodec class. Currently it is 128 bytes. + /// + public const int WorkingBufferSizeMin = 1024; + } + +} + diff --git a/DotNetZip/Zlib/ZlibStream.cs b/DotNetZip/Zlib/ZlibStream.cs new file mode 100644 index 0000000..88ddca9 --- /dev/null +++ b/DotNetZip/Zlib/ZlibStream.cs @@ -0,0 +1,725 @@ +// ZlibStream.cs +// ------------------------------------------------------------------ +// +// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation. +// All rights reserved. +// +// This code module is part of DotNetZip, a zipfile class library. +// +// ------------------------------------------------------------------ +// +// This code is licensed under the Microsoft Public License. +// See the file License.txt for the license details. +// More info on: http://dotnetzip.codeplex.com +// +// ------------------------------------------------------------------ +// +// last saved (in emacs): +// Time-stamp: <2011-July-31 14:53:33> +// +// ------------------------------------------------------------------ +// +// This module defines the ZlibStream class, which is similar in idea to +// the System.IO.Compression.DeflateStream and +// System.IO.Compression.GZipStream classes in the .NET BCL. +// +// ------------------------------------------------------------------ + +using System; +using System.IO; + +namespace Ionic.Zlib +{ + + /// + /// Represents a Zlib stream for compression or decompression. + /// + /// + /// + /// + /// The ZlibStream is a Decorator on a . It adds ZLIB compression or decompression to any + /// stream. + /// + /// + /// Using this stream, applications can compress or decompress data via + /// stream Read() and Write() operations. Either compresssion or + /// decompression can occur through either reading or writing. The compression + /// format used is ZLIB, which is documented in IETF RFC 1950, "ZLIB Compressed + /// Data Format Specification version 3.3". This implementation of ZLIB always uses + /// DEFLATE as the compression method. (see IETF RFC 1951, "DEFLATE + /// Compressed Data Format Specification version 1.3.") + /// + /// + /// The ZLIB format allows for varying compression methods, window sizes, and dictionaries. + /// This implementation always uses the DEFLATE compression method, a preset dictionary, + /// and 15 window bits by default. + /// + /// + /// + /// This class is similar to , except that it adds the + /// RFC1950 header and trailer bytes to a compressed stream when compressing, or expects + /// the RFC1950 header and trailer bytes when decompressing. It is also similar to the + /// . + /// + /// + /// + /// + public class ZlibStream : System.IO.Stream + { + internal ZlibBaseStream _baseStream; + bool _disposed; + + /// + /// Create a ZlibStream using the specified CompressionMode. + /// + /// + /// + /// + /// When mode is CompressionMode.Compress, the ZlibStream + /// will use the default compression level. The "captive" stream will be + /// closed when the ZlibStream is closed. + /// + /// + /// + /// + /// + /// This example uses a ZlibStream to compress a file, and writes the + /// compressed data to another file. + /// + /// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress)) + /// { + /// using (var raw = System.IO.File.Create(fileToCompress + ".zlib")) + /// { + /// using (Stream compressor = new ZlibStream(raw, CompressionMode.Compress)) + /// { + /// byte[] buffer = new byte[WORKING_BUFFER_SIZE]; + /// int n; + /// while ((n= input.Read(buffer, 0, buffer.Length)) != 0) + /// { + /// compressor.Write(buffer, 0, n); + /// } + /// } + /// } + /// } + /// + /// + /// Using input As Stream = File.OpenRead(fileToCompress) + /// Using raw As FileStream = File.Create(fileToCompress & ".zlib") + /// Using compressor As Stream = New ZlibStream(raw, CompressionMode.Compress) + /// Dim buffer As Byte() = New Byte(4096) {} + /// Dim n As Integer = -1 + /// Do While (n <> 0) + /// If (n > 0) Then + /// compressor.Write(buffer, 0, n) + /// End If + /// n = input.Read(buffer, 0, buffer.Length) + /// Loop + /// End Using + /// End Using + /// End Using + /// + /// + /// + /// The stream which will be read or written. + /// Indicates whether the ZlibStream will compress or decompress. + public ZlibStream(System.IO.Stream stream, CompressionMode mode) + : this(stream, mode, CompressionLevel.Default, false) + { + } + + /// + /// Create a ZlibStream using the specified CompressionMode and + /// the specified CompressionLevel. + /// + /// + /// + /// + /// + /// When mode is CompressionMode.Decompress, the level parameter is ignored. + /// The "captive" stream will be closed when the ZlibStream is closed. + /// + /// + /// + /// + /// + /// This example uses a ZlibStream to compress data from a file, and writes the + /// compressed data to another file. + /// + /// + /// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress)) + /// { + /// using (var raw = System.IO.File.Create(fileToCompress + ".zlib")) + /// { + /// using (Stream compressor = new ZlibStream(raw, + /// CompressionMode.Compress, + /// CompressionLevel.BestCompression)) + /// { + /// byte[] buffer = new byte[WORKING_BUFFER_SIZE]; + /// int n; + /// while ((n= input.Read(buffer, 0, buffer.Length)) != 0) + /// { + /// compressor.Write(buffer, 0, n); + /// } + /// } + /// } + /// } + /// + /// + /// + /// Using input As Stream = File.OpenRead(fileToCompress) + /// Using raw As FileStream = File.Create(fileToCompress & ".zlib") + /// Using compressor As Stream = New ZlibStream(raw, CompressionMode.Compress, CompressionLevel.BestCompression) + /// Dim buffer As Byte() = New Byte(4096) {} + /// Dim n As Integer = -1 + /// Do While (n <> 0) + /// If (n > 0) Then + /// compressor.Write(buffer, 0, n) + /// End If + /// n = input.Read(buffer, 0, buffer.Length) + /// Loop + /// End Using + /// End Using + /// End Using + /// + /// + /// + /// The stream to be read or written while deflating or inflating. + /// Indicates whether the ZlibStream will compress or decompress. + /// A tuning knob to trade speed for effectiveness. + public ZlibStream(System.IO.Stream stream, CompressionMode mode, CompressionLevel level) + : this(stream, mode, level, false) + { + } + + /// + /// Create a ZlibStream using the specified CompressionMode, and + /// explicitly specify whether the captive stream should be left open after + /// Deflation or Inflation. + /// + /// + /// + /// + /// + /// When mode is CompressionMode.Compress, the ZlibStream will use + /// the default compression level. + /// + /// + /// + /// This constructor allows the application to request that the captive stream + /// remain open after the deflation or inflation occurs. By default, after + /// Close() is called on the stream, the captive stream is also + /// closed. In some cases this is not desired, for example if the stream is a + /// that will be re-read after + /// compression. Specify true for the parameter to leave the stream + /// open. + /// + /// + /// + /// See the other overloads of this constructor for example code. + /// + /// + /// + /// + /// The stream which will be read or written. This is called the + /// "captive" stream in other places in this documentation. + /// Indicates whether the ZlibStream will compress or decompress. + /// true if the application would like the stream to remain + /// open after inflation/deflation. + public ZlibStream(System.IO.Stream stream, CompressionMode mode, bool leaveOpen) + : this(stream, mode, CompressionLevel.Default, leaveOpen) + { + } + + /// + /// Create a ZlibStream using the specified CompressionMode + /// and the specified CompressionLevel, and explicitly specify + /// whether the stream should be left open after Deflation or Inflation. + /// + /// + /// + /// + /// + /// This constructor allows the application to request that the captive + /// stream remain open after the deflation or inflation occurs. By + /// default, after Close() is called on the stream, the captive + /// stream is also closed. In some cases this is not desired, for example + /// if the stream is a that will be + /// re-read after compression. Specify true for the parameter to leave the stream open. + /// + /// + /// + /// When mode is CompressionMode.Decompress, the level parameter is + /// ignored. + /// + /// + /// + /// + /// + /// + /// This example shows how to use a ZlibStream to compress the data from a file, + /// and store the result into another file. The filestream remains open to allow + /// additional data to be written to it. + /// + /// + /// using (var output = System.IO.File.Create(fileToCompress + ".zlib")) + /// { + /// using (System.IO.Stream input = System.IO.File.OpenRead(fileToCompress)) + /// { + /// using (Stream compressor = new ZlibStream(output, CompressionMode.Compress, CompressionLevel.BestCompression, true)) + /// { + /// byte[] buffer = new byte[WORKING_BUFFER_SIZE]; + /// int n; + /// while ((n= input.Read(buffer, 0, buffer.Length)) != 0) + /// { + /// compressor.Write(buffer, 0, n); + /// } + /// } + /// } + /// // can write additional data to the output stream here + /// } + /// + /// + /// Using output As FileStream = File.Create(fileToCompress & ".zlib") + /// Using input As Stream = File.OpenRead(fileToCompress) + /// Using compressor As Stream = New ZlibStream(output, CompressionMode.Compress, CompressionLevel.BestCompression, True) + /// Dim buffer As Byte() = New Byte(4096) {} + /// Dim n As Integer = -1 + /// Do While (n <> 0) + /// If (n > 0) Then + /// compressor.Write(buffer, 0, n) + /// End If + /// n = input.Read(buffer, 0, buffer.Length) + /// Loop + /// End Using + /// End Using + /// ' can write additional data to the output stream here. + /// End Using + /// + /// + /// + /// The stream which will be read or written. + /// + /// Indicates whether the ZlibStream will compress or decompress. + /// + /// + /// true if the application would like the stream to remain open after + /// inflation/deflation. + /// + /// + /// + /// A tuning knob to trade speed for effectiveness. This parameter is + /// effective only when mode is CompressionMode.Compress. + /// + public ZlibStream(System.IO.Stream stream, CompressionMode mode, CompressionLevel level, bool leaveOpen) + { + _baseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.ZLIB, leaveOpen); + } + + #region Zlib properties + + /// + /// This property sets the flush behavior on the stream. + /// Sorry, though, not sure exactly how to describe all the various settings. + /// + virtual public FlushType FlushMode + { + get { return (this._baseStream._flushMode); } + set + { + if (_disposed) throw new ObjectDisposedException("ZlibStream"); + this._baseStream._flushMode = value; + } + } + + /// + /// The size of the working buffer for the compression codec. + /// + /// + /// + /// + /// The working buffer is used for all stream operations. The default size is + /// 1024 bytes. The minimum size is 128 bytes. You may get better performance + /// with a larger buffer. Then again, you might not. You would have to test + /// it. + /// + /// + /// + /// Set this before the first call to Read() or Write() on the + /// stream. If you try to set it afterwards, it will throw. + /// + /// + public int BufferSize + { + get + { + return this._baseStream._bufferSize; + } + set + { + if (_disposed) throw new ObjectDisposedException("ZlibStream"); + if (this._baseStream._workingBuffer != null) + throw new ZlibException("The working buffer is already set."); + if (value < ZlibConstants.WorkingBufferSizeMin) + throw new ZlibException(String.Format("Don't be silly. {0} bytes?? Use a bigger buffer, at least {1}.", value, ZlibConstants.WorkingBufferSizeMin)); + this._baseStream._bufferSize = value; + } + } + + /// Returns the total number of bytes input so far. + virtual public long TotalIn + { + get { return this._baseStream._z.TotalBytesIn; } + } + + /// Returns the total number of bytes output so far. + virtual public long TotalOut + { + get { return this._baseStream._z.TotalBytesOut; } + } + + #endregion + + #region System.IO.Stream methods + + /// + /// Dispose the stream. + /// + /// + /// + /// This may or may not result in a Close() call on the captive + /// stream. See the constructors that have a leaveOpen parameter + /// for more information. + /// + /// + /// This method may be invoked in two distinct scenarios. If disposing + /// == true, the method has been called directly or indirectly by a + /// user's code, for example via the public Dispose() method. In this + /// case, both managed and unmanaged resources can be referenced and + /// disposed. If disposing == false, the method has been called by the + /// runtime from inside the object finalizer and this method should not + /// reference other objects; in that case only unmanaged resources must + /// be referenced or disposed. + /// + /// + /// + /// indicates whether the Dispose method was invoked by user code. + /// + protected override void Dispose(bool disposing) + { + try + { + if (!_disposed) + { + if (disposing && (this._baseStream != null)) + this._baseStream.Close(); + _disposed = true; + } + } + finally + { + base.Dispose(disposing); + } + } + + + /// + /// Indicates whether the stream can be read. + /// + /// + /// The return value depends on whether the captive stream supports reading. + /// + public override bool CanRead + { + get + { + if (_disposed) throw new ObjectDisposedException("ZlibStream"); + return _baseStream._stream.CanRead; + } + } + + /// + /// Indicates whether the stream supports Seek operations. + /// + /// + /// Always returns false. + /// + public override bool CanSeek + { + get { return false; } + } + + /// + /// Indicates whether the stream can be written. + /// + /// + /// The return value depends on whether the captive stream supports writing. + /// + public override bool CanWrite + { + get + { + if (_disposed) throw new ObjectDisposedException("ZlibStream"); + return _baseStream._stream.CanWrite; + } + } + + /// + /// Flush the stream. + /// + public override void Flush() + { + if (_disposed) throw new ObjectDisposedException("ZlibStream"); + _baseStream.Flush(); + } + + /// + /// Reading this property always throws a . + /// + public override long Length + { + get { throw new NotSupportedException(); } + } + + /// + /// The position of the stream pointer. + /// + /// + /// + /// Setting this property always throws a . Reading will return the total bytes + /// written out, if used in writing, or the total bytes read in, if used in + /// reading. The count may refer to compressed bytes or uncompressed bytes, + /// depending on how you've used the stream. + /// + public override long Position + { + get + { + if (this._baseStream._streamMode == Ionic.Zlib.ZlibBaseStream.StreamMode.Writer) + return this._baseStream._z.TotalBytesOut; + if (this._baseStream._streamMode == Ionic.Zlib.ZlibBaseStream.StreamMode.Reader) + return this._baseStream._z.TotalBytesIn; + return 0; + } + + set { throw new NotSupportedException(); } + } + + /// + /// Read data from the stream. + /// + /// + /// + /// + /// + /// If you wish to use the ZlibStream to compress data while reading, + /// you can create a ZlibStream with CompressionMode.Compress, + /// providing an uncompressed data stream. Then call Read() on that + /// ZlibStream, and the data read will be compressed. If you wish to + /// use the ZlibStream to decompress data while reading, you can create + /// a ZlibStream with CompressionMode.Decompress, providing a + /// readable compressed data stream. Then call Read() on that + /// ZlibStream, and the data will be decompressed as it is read. + /// + /// + /// + /// A ZlibStream can be used for Read() or Write(), but + /// not both. + /// + /// + /// + /// + /// + /// The buffer into which the read data should be placed. + /// + /// + /// the offset within that data array to put the first byte read. + /// + /// the number of bytes to read. + /// + /// the number of bytes read + public override int Read(byte[] buffer, int offset, int count) + { + if (_disposed) throw new ObjectDisposedException("ZlibStream"); + return _baseStream.Read(buffer, offset, count); + } + + /// + /// Calling this method always throws a . + /// + /// + /// The offset to seek to.... + /// IF THIS METHOD ACTUALLY DID ANYTHING. + /// + /// + /// The reference specifying how to apply the offset.... IF + /// THIS METHOD ACTUALLY DID ANYTHING. + /// + /// + /// nothing. This method always throws. + public override long Seek(long offset, System.IO.SeekOrigin origin) + { + throw new NotSupportedException(); + } + + /// + /// Calling this method always throws a . + /// + /// + /// The new value for the stream length.... IF + /// THIS METHOD ACTUALLY DID ANYTHING. + /// + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + /// + /// Write data to the stream. + /// + /// + /// + /// + /// + /// If you wish to use the ZlibStream to compress data while writing, + /// you can create a ZlibStream with CompressionMode.Compress, + /// and a writable output stream. Then call Write() on that + /// ZlibStream, providing uncompressed data as input. The data sent to + /// the output stream will be the compressed form of the data written. If you + /// wish to use the ZlibStream to decompress data while writing, you + /// can create a ZlibStream with CompressionMode.Decompress, and a + /// writable output stream. Then call Write() on that stream, + /// providing previously compressed data. The data sent to the output stream + /// will be the decompressed form of the data written. + /// + /// + /// + /// A ZlibStream can be used for Read() or Write(), but not both. + /// + /// + /// The buffer holding data to write to the stream. + /// the offset within that data array to find the first byte to write. + /// the number of bytes to write. + public override void Write(byte[] buffer, int offset, int count) + { + if (_disposed) throw new ObjectDisposedException("ZlibStream"); + _baseStream.Write(buffer, offset, count); + } + #endregion + + + /// + /// Compress a string into a byte array using ZLIB. + /// + /// + /// + /// Uncompress it with . + /// + /// + /// + /// + /// + /// + /// + /// A string to compress. The string will first be encoded + /// using UTF8, then compressed. + /// + /// + /// The string in compressed form + public static byte[] CompressString(String s) + { + using (var ms = new MemoryStream()) + { + Stream compressor = + new ZlibStream(ms, CompressionMode.Compress, CompressionLevel.BestCompression); + ZlibBaseStream.CompressString(s, compressor); + return ms.ToArray(); + } + } + + + /// + /// Compress a byte array into a new byte array using ZLIB. + /// + /// + /// + /// Uncompress it with . + /// + /// + /// + /// + /// + /// + /// A buffer to compress. + /// + /// + /// The data in compressed form + public static byte[] CompressBuffer(byte[] b) + { + using (var ms = new MemoryStream()) + { + Stream compressor = + new ZlibStream( ms, CompressionMode.Compress, CompressionLevel.BestCompression ); + + ZlibBaseStream.CompressBuffer(b, compressor); + return ms.ToArray(); + } + } + + + /// + /// Uncompress a ZLIB-compressed byte array into a single string. + /// + /// + /// + /// + /// + /// + /// A buffer containing ZLIB-compressed data. + /// + /// + /// The uncompressed string + public static String UncompressString(byte[] compressed) + { + using (var input = new MemoryStream(compressed)) + { + Stream decompressor = + new ZlibStream(input, CompressionMode.Decompress); + + return ZlibBaseStream.UncompressString(compressed, decompressor); + } + } + + + /// + /// Uncompress a ZLIB-compressed byte array into a byte array. + /// + /// + /// + /// + /// + /// + /// A buffer containing ZLIB-compressed data. + /// + /// + /// The data in uncompressed form + public static byte[] UncompressBuffer(byte[] compressed) + { + using (var input = new MemoryStream(compressed)) + { + Stream decompressor = + new ZlibStream( input, CompressionMode.Decompress ); + + return ZlibBaseStream.UncompressBuffer(compressed, decompressor); + } + } + + } + + +} \ No newline at end of file diff --git a/DotNetZip/Zlib/properties/AssemblyInfo.cs b/DotNetZip/Zlib/properties/AssemblyInfo.cs new file mode 100644 index 0000000..72b263c Binary files /dev/null and b/DotNetZip/Zlib/properties/AssemblyInfo.cs differ diff --git a/DotNetZip/Zlib/zlib-10.vsmdi b/DotNetZip/Zlib/zlib-10.vsmdi new file mode 100644 index 0000000..8aa6a31 --- /dev/null +++ b/DotNetZip/Zlib/zlib-10.vsmdi @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/ElevationTileGenerator/App.config b/ElevationTileGenerator/App.config new file mode 100644 index 0000000..98a0519 --- /dev/null +++ b/ElevationTileGenerator/App.config @@ -0,0 +1,24 @@ + + + + +
+ + + + + + + + + + + + + + + False + + + + diff --git a/ElevationTileGenerator/ElevationTileGenerator.csproj b/ElevationTileGenerator/ElevationTileGenerator.csproj new file mode 100644 index 0000000..ce50eb4 --- /dev/null +++ b/ElevationTileGenerator/ElevationTileGenerator.csproj @@ -0,0 +1,145 @@ + + + + + Debug + AnyCPU + {955CEA7D-37F6-4122-B4CA-B08C2B73969F} + WinExe + Properties + ElevationTileGenerator + ElevationTileGenerator + v4.0 + 512 + true + + + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + prompt + MinimumRecommendedRules.ruleset + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + prompt + MinimumRecommendedRules.ruleset + + + + False + ..\..\..\ScoutBase\DotNetZip\zip-v1.9\Debug\Ionic.Zip.dll + + + ..\packages\Newtonsoft.Json.12.0.1\lib\net40\Newtonsoft.Json.dll + True + + + + + ..\packages\System.Data.SQLite.Core.1.0.110.0\lib\net40\System.Data.SQLite.dll + True + + + + + + + + + + + + + Form + + + MainDlg.cs + + + + + MainDlg.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + + {ee86e933-d883-4b18-80eb-0fba55ec67c6} + ScoutBase.Core + + + {009cabfd-726d-481f-972d-0a218e0ad9b9} + ScoutBase.Elevation + + + {6056d3be-7002-4a6a-a9ea-6ff45122a3c7} + SQLiteDatabase + + + + + + + Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}". + + + + + \ No newline at end of file diff --git a/ElevationTileGenerator/MainDlg.Designer.cs b/ElevationTileGenerator/MainDlg.Designer.cs new file mode 100644 index 0000000..1a53b52 --- /dev/null +++ b/ElevationTileGenerator/MainDlg.Designer.cs @@ -0,0 +1,281 @@ +namespace ElevationTileGenerator +{ + partial class MainDlg + { + /// + /// Erforderliche Designervariable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Verwendete Ressourcen bereinigen. + /// + /// True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Vom Windows Form-Designer generierter Code + + /// + /// Erforderliche Methode für die Designerunterstützung. + /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden. + /// + private void InitializeComponent() + { + this.ss_Main = new System.Windows.Forms.StatusStrip(); + this.tsl_Status = new System.Windows.Forms.ToolStripStatusLabel(); + this.btn_Start = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.btn_SourceDir = new System.Windows.Forms.Button(); + this.btn_DestDir = new System.Windows.Forms.Button(); + this.label2 = new System.Windows.Forms.Label(); + this.btn_Stop = new System.Windows.Forms.Button(); + this.pb_Picture = new System.Windows.Forms.PictureBox(); + this.btn_Picture = new System.Windows.Forms.Button(); + this.btn_ZIP = new System.Windows.Forms.Button(); + this.btn_CAT = new System.Windows.Forms.Button(); + this.btn_TestCAT = new System.Windows.Forms.Button(); + this.btn_CATFromZIP = new System.Windows.Forms.Button(); + this.btn_PictureFromTiles = new System.Windows.Forms.Button(); + this.tb_DestDir = new System.Windows.Forms.TextBox(); + this.tb_SourceDir = new System.Windows.Forms.TextBox(); + this.btn_Delete = new System.Windows.Forms.Button(); + this.ss_Main.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pb_Picture)).BeginInit(); + this.SuspendLayout(); + // + // ss_Main + // + this.ss_Main.ImageScalingSize = new System.Drawing.Size(20, 20); + this.ss_Main.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.tsl_Status}); + this.ss_Main.Location = new System.Drawing.Point(0, 534); + this.ss_Main.Name = "ss_Main"; + this.ss_Main.Size = new System.Drawing.Size(1001, 22); + this.ss_Main.TabIndex = 0; + this.ss_Main.Text = "statusStrip1"; + // + // tsl_Status + // + this.tsl_Status.Name = "tsl_Status"; + this.tsl_Status.Size = new System.Drawing.Size(39, 17); + this.tsl_Status.Text = "Status"; + // + // btn_Start + // + this.btn_Start.Location = new System.Drawing.Point(640, 478); + this.btn_Start.Name = "btn_Start"; + this.btn_Start.Size = new System.Drawing.Size(75, 23); + this.btn_Start.TabIndex = 1; + this.btn_Start.Text = "Start"; + this.btn_Start.UseVisualStyleBackColor = true; + this.btn_Start.Click += new System.EventHandler(this.btn_Start_Click); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(33, 45); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(89, 13); + this.label1.TabIndex = 2; + this.label1.Text = "Source Directory:"; + // + // btn_SourceDir + // + this.btn_SourceDir.Location = new System.Drawing.Point(558, 40); + this.btn_SourceDir.Name = "btn_SourceDir"; + this.btn_SourceDir.Size = new System.Drawing.Size(75, 23); + this.btn_SourceDir.TabIndex = 4; + this.btn_SourceDir.Text = "Select"; + this.btn_SourceDir.UseVisualStyleBackColor = true; + this.btn_SourceDir.Click += new System.EventHandler(this.btn_SourceDir_Click); + // + // btn_DestDir + // + this.btn_DestDir.Location = new System.Drawing.Point(558, 66); + this.btn_DestDir.Name = "btn_DestDir"; + this.btn_DestDir.Size = new System.Drawing.Size(75, 23); + this.btn_DestDir.TabIndex = 7; + this.btn_DestDir.Text = "Select"; + this.btn_DestDir.UseVisualStyleBackColor = true; + this.btn_DestDir.Click += new System.EventHandler(this.btn_DestDir_Click); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(33, 71); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(108, 13); + this.label2.TabIndex = 5; + this.label2.Text = "Destination Directory:"; + // + // btn_Stop + // + this.btn_Stop.Location = new System.Drawing.Point(721, 478); + this.btn_Stop.Name = "btn_Stop"; + this.btn_Stop.Size = new System.Drawing.Size(75, 23); + this.btn_Stop.TabIndex = 8; + this.btn_Stop.Text = "Stop"; + this.btn_Stop.UseVisualStyleBackColor = true; + this.btn_Stop.Click += new System.EventHandler(this.btn_Stop_Click); + // + // pb_Picture + // + this.pb_Picture.BackColor = System.Drawing.SystemColors.AppWorkspace; + this.pb_Picture.Location = new System.Drawing.Point(36, 127); + this.pb_Picture.Name = "pb_Picture"; + this.pb_Picture.Size = new System.Drawing.Size(586, 326); + this.pb_Picture.TabIndex = 9; + this.pb_Picture.TabStop = false; + // + // btn_Picture + // + this.btn_Picture.Location = new System.Drawing.Point(537, 478); + this.btn_Picture.Name = "btn_Picture"; + this.btn_Picture.Size = new System.Drawing.Size(75, 23); + this.btn_Picture.TabIndex = 10; + this.btn_Picture.Text = "Picture"; + this.btn_Picture.UseVisualStyleBackColor = true; + this.btn_Picture.Click += new System.EventHandler(this.btn_Picture_Click); + // + // btn_ZIP + // + this.btn_ZIP.Location = new System.Drawing.Point(802, 478); + this.btn_ZIP.Name = "btn_ZIP"; + this.btn_ZIP.Size = new System.Drawing.Size(75, 23); + this.btn_ZIP.TabIndex = 11; + this.btn_ZIP.Text = "ZIP"; + this.btn_ZIP.UseVisualStyleBackColor = true; + this.btn_ZIP.Click += new System.EventHandler(this.btn_ZIP_Click); + // + // btn_CAT + // + this.btn_CAT.Location = new System.Drawing.Point(883, 478); + this.btn_CAT.Name = "btn_CAT"; + this.btn_CAT.Size = new System.Drawing.Size(75, 23); + this.btn_CAT.TabIndex = 12; + this.btn_CAT.Text = "CAT"; + this.btn_CAT.UseVisualStyleBackColor = true; + this.btn_CAT.Click += new System.EventHandler(this.btn_CAT_Click); + // + // btn_TestCAT + // + this.btn_TestCAT.Location = new System.Drawing.Point(883, 390); + this.btn_TestCAT.Name = "btn_TestCAT"; + this.btn_TestCAT.Size = new System.Drawing.Size(75, 23); + this.btn_TestCAT.TabIndex = 13; + this.btn_TestCAT.Text = "TestCAT"; + this.btn_TestCAT.UseVisualStyleBackColor = true; + this.btn_TestCAT.Click += new System.EventHandler(this.btn_TestCAT_Click); + // + // btn_CATFromZIP + // + this.btn_CATFromZIP.Location = new System.Drawing.Point(883, 449); + this.btn_CATFromZIP.Name = "btn_CATFromZIP"; + this.btn_CATFromZIP.Size = new System.Drawing.Size(75, 23); + this.btn_CATFromZIP.TabIndex = 14; + this.btn_CATFromZIP.Text = "CAT fm ZIP"; + this.btn_CATFromZIP.UseVisualStyleBackColor = true; + this.btn_CATFromZIP.Click += new System.EventHandler(this.btn_CATFromZIP_Click); + // + // btn_PictureFromTiles + // + this.btn_PictureFromTiles.Location = new System.Drawing.Point(451, 478); + this.btn_PictureFromTiles.Name = "btn_PictureFromTiles"; + this.btn_PictureFromTiles.Size = new System.Drawing.Size(75, 23); + this.btn_PictureFromTiles.TabIndex = 15; + this.btn_PictureFromTiles.Text = "Pic fm Tiles"; + this.btn_PictureFromTiles.UseVisualStyleBackColor = true; + this.btn_PictureFromTiles.Click += new System.EventHandler(this.btn_PictureFromTiles_Click); + // + // tb_DestDir + // + this.tb_DestDir.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::ElevationTileGenerator.Properties.Settings.Default, "DestDir", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_DestDir.Location = new System.Drawing.Point(156, 68); + this.tb_DestDir.Name = "tb_DestDir"; + this.tb_DestDir.Size = new System.Drawing.Size(370, 20); + this.tb_DestDir.TabIndex = 6; + this.tb_DestDir.Text = global::ElevationTileGenerator.Properties.Settings.Default.DestDir; + // + // tb_SourceDir + // + this.tb_SourceDir.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::ElevationTileGenerator.Properties.Settings.Default, "SourceDir", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.tb_SourceDir.Location = new System.Drawing.Point(156, 42); + this.tb_SourceDir.Name = "tb_SourceDir"; + this.tb_SourceDir.Size = new System.Drawing.Size(370, 20); + this.tb_SourceDir.TabIndex = 3; + this.tb_SourceDir.Text = global::ElevationTileGenerator.Properties.Settings.Default.SourceDir; + // + // btn_Delete + // + this.btn_Delete.Location = new System.Drawing.Point(640, 449); + this.btn_Delete.Name = "btn_Delete"; + this.btn_Delete.Size = new System.Drawing.Size(75, 23); + this.btn_Delete.TabIndex = 16; + this.btn_Delete.Text = "Delete"; + this.btn_Delete.UseVisualStyleBackColor = true; + this.btn_Delete.Click += new System.EventHandler(this.btn_Delete_Click); + // + // MainDlg + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(1001, 556); + this.Controls.Add(this.btn_Delete); + this.Controls.Add(this.btn_PictureFromTiles); + this.Controls.Add(this.btn_CATFromZIP); + this.Controls.Add(this.btn_TestCAT); + this.Controls.Add(this.btn_CAT); + this.Controls.Add(this.btn_ZIP); + this.Controls.Add(this.btn_Picture); + this.Controls.Add(this.pb_Picture); + this.Controls.Add(this.btn_Stop); + this.Controls.Add(this.btn_DestDir); + this.Controls.Add(this.tb_DestDir); + this.Controls.Add(this.label2); + this.Controls.Add(this.btn_SourceDir); + this.Controls.Add(this.tb_SourceDir); + this.Controls.Add(this.label1); + this.Controls.Add(this.btn_Start); + this.Controls.Add(this.ss_Main); + this.Name = "MainDlg"; + this.Text = "ElevationTileGenerator"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainDlg_FormClosing); + this.Load += new System.EventHandler(this.MainDlg_Load); + this.ss_Main.ResumeLayout(false); + this.ss_Main.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pb_Picture)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.StatusStrip ss_Main; + private System.Windows.Forms.ToolStripStatusLabel tsl_Status; + private System.Windows.Forms.Button btn_Start; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TextBox tb_SourceDir; + private System.Windows.Forms.Button btn_SourceDir; + private System.Windows.Forms.Button btn_DestDir; + private System.Windows.Forms.TextBox tb_DestDir; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Button btn_Stop; + private System.Windows.Forms.PictureBox pb_Picture; + private System.Windows.Forms.Button btn_Picture; + private System.Windows.Forms.Button btn_ZIP; + private System.Windows.Forms.Button btn_CAT; + private System.Windows.Forms.Button btn_TestCAT; + private System.Windows.Forms.Button btn_CATFromZIP; + private System.Windows.Forms.Button btn_PictureFromTiles; + private System.Windows.Forms.Button btn_Delete; + } +} + diff --git a/ElevationTileGenerator/MainDlg.cs b/ElevationTileGenerator/MainDlg.cs new file mode 100644 index 0000000..b927063 --- /dev/null +++ b/ElevationTileGenerator/MainDlg.cs @@ -0,0 +1,887 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using System.Diagnostics; +using System.IO; +using System.Globalization; +using System.Threading; +using ScoutBase; +using ScoutBase.Core; +using ScoutBase.Elevation; +using SQLiteDatabase; +using Ionic.Zip; +using Newtonsoft.Json; + +namespace ElevationTileGenerator +{ + public partial class MainDlg : Form + { + DataTableSRTM3 srtm3 = new DataTableSRTM3(); + + ElevationCatalogue Tiles = new ElevationCatalogue(); + + public MainDlg() + { + InitializeComponent(); + } + + private void MainDlg_Load(object sender, EventArgs e) + { +// string[] a = ElevationModel.Database.GetLocsFromRect(50, 0, 51, 2); + } + + private void btn_Start_Click(object sender, EventArgs e) + { + Scan(); + } + + public bool IsGLOBEFile(string filename) + { + // checks for a valid filename with/witout extension + // filename must have a xxxx. notation + // [0]: 'A'..'P' + // [1],[2]: nn = version + // [3]: 'G','B','S','T' = status + // 'S' and 'T' are not used + if (String.IsNullOrEmpty(filename)) + return false; + string upperfilename = Path.GetFileNameWithoutExtension(filename).ToUpper(); + // return on extension not empty + string extension = Path.GetExtension(filename); + if (!String.IsNullOrEmpty(extension)) + return false; + char index = upperfilename[0]; + char status = upperfilename[3]; + // check length of filename + if (upperfilename.Length != 4) + return false; + // check allowed chars + if ((index < 'A') + || (index > 'P') + || !Char.IsDigit(upperfilename[1]) + || !Char.IsDigit(upperfilename[2]) + || ((status != 'G') && (status != 'B')) + ) + return false; + //check length + long l = 0; + switch (index) + { + case 'A': + case 'B': + case 'C': + case 'D': + case 'M': + case 'N': + case 'O': + case 'P': + l = 4800 * 10800 * 2; + break; + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + l = 6000 * 10800 * 2; + break; + } + if (l != new System.IO.FileInfo(filename).Length) + return false; + return true; + } + + public bool IsSRTM3File(string filename) + { + // checks for a valid SRTM filename with extension + // filename must have a XnnYnnn.hgt notation + // [0]: 'N' or 'S' + // [1],[2]: nn = Latitude + // [3]: 'E' or 'W' + // [4],[5],[6]: nnn = Longitude + if (String.IsNullOrEmpty(filename)) + return false; + if (Path.GetExtension(filename).ToUpper() != ".HGT") + return false; + string upperfilename = Path.GetFileNameWithoutExtension(filename).ToUpper(); + if ((upperfilename[0] != 'N') && (upperfilename[0] != 'S')) + return false; + if ((upperfilename[3] != 'W') && (upperfilename[3] != 'E')) + return false; + if (!Char.IsDigit(upperfilename[1])) + return false; + if (!Char.IsDigit(upperfilename[2])) + return false; + if (!Char.IsDigit(upperfilename[4])) + return false; + if (!Char.IsDigit(upperfilename[5])) + return false; + if (!Char.IsDigit(upperfilename[6])) + return false; + // check file size + long l = 1201 * 1201 * 2; + if (l != new System.IO.FileInfo(filename).Length) + return false; + return true; + } + + public bool IsSRTM1File(string filename) + { + // checks for a valid SRTM filename with extension + // filename must have a XnnYnnn.hgt notation + // [0]: 'N' or 'S' + // [1],[2]: nn = Latitude + // [3]: 'E' or 'W' + // [4],[5],[6]: nnn = Longitude + if (String.IsNullOrEmpty(filename)) + return false; + if (Path.GetExtension(filename).ToUpper() != ".HGT") + return false; + string upperfilename = Path.GetFileNameWithoutExtension(filename).ToUpper(); + if ((upperfilename[0] != 'N') && (upperfilename[0] != 'S')) + return false; + if ((upperfilename[3] != 'W') && (upperfilename[3] != 'E')) + return false; + if (!Char.IsDigit(upperfilename[1])) + return false; + if (!Char.IsDigit(upperfilename[2])) + return false; + if (!Char.IsDigit(upperfilename[4])) + return false; + if (!Char.IsDigit(upperfilename[5])) + return false; + if (!Char.IsDigit(upperfilename[6])) + return false; + // check file size + long l = 3601 * 3601 * 2; + if (l != new System.IO.FileInfo(filename).Length) + return false; + return true; + } + + + private void ProcessTile(string tilename, int current, int count) + { + if (IsSRTM3File(tilename) || IsSRTM1File(tilename)) + { + string srtmindex = Path.GetFileNameWithoutExtension(tilename).ToUpper(); + int latbase = (srtmindex[0] == 'S') ? -System.Convert.ToInt32(srtmindex.Substring(1, 2)) : System.Convert.ToInt32(srtmindex.Substring(1, 2)); + int lonbase = (srtmindex[3] == 'W') ? -System.Convert.ToInt32(srtmindex.Substring(4, 3)) : System.Convert.ToInt32(srtmindex.Substring(4, 3)); + string tilebase = MaidenheadLocator.LocFromLatLon(latbase, lonbase, false, 3).Substring(0,4); + + // creating dir + string dir = Path.Combine(Properties.Settings.Default.DestDir, tilebase.Substring(0, 2), tilebase.Substring(2, 2)); + int rows; + int cols; + if (IsSRTM3File(tilename)) + { + rows = 1201; + cols = 1201; + } + else + { + rows = 3601; + cols = 3601; + } + FileStream fs = File.OpenRead(tilename); + MemoryStream ms = new MemoryStream(); + ms.SetLength(fs.Length); + fs.Read(ms.GetBuffer(), 0, (int)fs.Length); + using (BinaryReader br = new BinaryReader(ms)) + { + bool all_void = true; + ElevationTileDesignator tile; + char x = (lonbase % 2 == 0) ? 'A' : 'M'; + for (int i = 0; i < 12; i++) + { + char y = 'A'; + for (int j = 0; j < 24; j++) + { + Stopwatch st = new Stopwatch(); + st.Start(); + string filename = tilebase + x + y + ".loc"; + if (!File.Exists(Path.Combine(dir, filename))) + { + tile = new ElevationTileDesignator(); + tile.TileIndex = tilebase + x + y; + tile.Rows = rows / 24; + tile.Columns = cols / 12; + short[,] b = new short[tile.Columns, tile.Rows]; + byte[] a16 = new byte[2]; + for (int k = 0; k < tile.Columns; k++) + { + for (int l = 0; l < tile.Rows; l++) + { + br.BaseStream.Position = ((rows - 2 - j * tile.Rows - l) * cols + i * tile.Columns + k) * 2; + br.Read(a16, 0, 2); + Array.Reverse(a16); + short e = BitConverter.ToInt16(a16, 0); + b[k, l] = e; + if ((e != ElevationData.Database.ElvMissingFlag) && (e < tile.MinElv)) + { + tile.MinElv = e; + tile.MinLon = tile.BaseLon + k * tile.StepWidthLon; + tile.MinLat = tile.BaseLat + l * tile.StepWidthLat; + } + if ((e != ElevationData.Database.ElvMissingFlag) && (e > tile.MaxElv)) + { + tile.MaxElv = e; + tile.MaxLon = tile.BaseLon + k * tile.StepWidthLon; + tile.MaxLat = tile.BaseLat + l * tile.StepWidthLat; + } + if (e != ElevationData.Database.ElvMissingFlag) + all_void = false; + } + } + tile.Elv = b; + tile.LastUpdated = DateTime.UtcNow; + if (!all_void) + { + // ElevationModel.Database.ElevationTileInsertOrUpdateIfNewer(tile); + // create dir if not exists + if (!Directory.Exists(dir)) + Directory.CreateDirectory(dir); + // write json file + ms = new MemoryStream(); + using (StreamWriter sw = new StreamWriter(ms)) + { + string json = tile.ToJSON(); + sw.WriteLine(json); + sw.Flush(); + ms.Position = 0; + File.WriteAllBytes(Path.Combine(dir, filename), ms.ToArray()); + } + File.SetCreationTimeUtc(Path.Combine(dir, filename), tile.LastUpdated); + File.SetLastWriteTimeUtc(Path.Combine(dir, filename), tile.LastUpdated); + } + } + st.Stop(); + tsl_Status.Text = "[" + Path.GetFileNameWithoutExtension(tilename) + ", " + current.ToString() + " of " + count.ToString() + "] Processing tile: " + tilebase + x + y + ", " + st.ElapsedMilliseconds.ToString() + "ms"; + Application.DoEvents(); + y++; + } + x++; + } + } + } + else if (IsGLOBEFile(tilename)) + { + char index = Path.GetFileName(tilename.ToUpper())[0]; + int latbase; + int lonbase; + int rows; + int cols; + switch (index) + { + case 'A': + latbase = 50; + lonbase = -180; + cols = 10800; + rows = 4800; + break; + case 'B': + latbase = 50; + lonbase = -90; + cols = 10800; + rows = 4800; + break; + case 'C': + latbase = 50; + lonbase = 0; + cols = 10800; + rows = 4800; + break; + case 'D': + latbase = 50; + lonbase = 90; + cols = 10800; + rows = 4800; + break; + case 'E': + latbase = 0; + lonbase = -180; + cols = 10800; + rows = 6000; + break; + case 'F': + latbase = 0; + lonbase = -90; + cols = 10800; + rows = 6000; + break; + case 'G': + latbase = 0; + lonbase = 0; + cols = 10800; + rows = 6000; + break; + case 'H': + latbase = 0; + lonbase = 90; + cols = 10800; + rows = 6000; + break; + case 'I': + latbase = -50; + lonbase = -180; + cols = 10800; + rows = 6000; + break; + case 'J': + latbase = -50; + lonbase = -90; + cols = 10800; + rows = 6000; + break; + case 'K': + latbase = -50; + lonbase = 0; + cols = 10800; + rows = 6000; + break; + case 'L': + latbase = -50; + lonbase = 90; + cols = 10800; + rows = 6000; + break; + case 'M': + latbase = -90; + lonbase = -180; + cols = 10800; + rows = 4800; + break; + case 'N': + latbase = -90; + lonbase = -90; + cols = 10800; + rows = 4800; + break; + case 'O': + latbase = -90; + lonbase = -0; + cols = 10800; + rows = 4800; + break; + case 'P': + latbase = -90; + lonbase = 90; + cols = 10800; + rows = 4800; + break; + default: + latbase = 0; + lonbase = 0; + cols = 0; + rows = 0; + break; + } + + string tilebase = MaidenheadLocator.LocFromLatLon(latbase, lonbase, false, 3).Substring(0, 4); + FileStream fs = File.OpenRead(tilename); + MemoryStream ms = new MemoryStream(); + ms.SetLength(fs.Length); + fs.Read(ms.GetBuffer(), 0, (int)fs.Length); + using (BinaryReader br = new BinaryReader(ms)) + { + ElevationTileDesignator tile; + for (int i0 = 0; i0 <= 4; i0++) + { + int j0max = (rows == 6000) ? 4 : 3; + for (int j0 = 0; j0 <= j0max; j0++) + { + int i1max = (i0 < 4) ? 9 : 4; + for (int i1 = 0; i1 <= i1max; i1++) + { + for (int j1 = 0; j1 <= 9; j1++) + { + for (int i2 = 0; i2 < 24; i2++) + { + for (int j2 = 0; j2 < 24; j2++) + { + Stopwatch st = new Stopwatch(); + st.Start(); + // generate lat/lon from tile's midpoint + double lat = latbase + j0 * 10.0 + j1 * 1.0 + j2 * 1.0 / 24.0 + 0.5 / 24.0; + double lon = lonbase + i0 * 20.0 + i1 * 2.0 + i2 * 2.0 / 24.0 + 1.0 / 24.0; + // generate filename from lat/lon + string tileindex = MaidenheadLocator.LocFromLatLon(lat, lon, false, 3); + string filename = tileindex + ".loc"; + // creating dir + string dir = Path.Combine(Properties.Settings.Default.DestDir, filename.Substring(0, 2), filename.Substring(2,2)); + if (!File.Exists(Path.Combine(dir, filename))) + { + bool all_void = true; + tile = new ElevationTileDesignator(); + tile.TileIndex = tileindex; + tile.Rows = (rows == 6000) ? rows / 50 / 24 : rows / 40 / 24; + tile.Columns = cols / 90 / 24 * 2; + short[,] b = new short[tile.Columns, tile.Rows]; + for (int k = 0; k < tile.Columns; k++) + { + for (int l = 0; l < tile.Rows; l++) + { + br.BaseStream.Position = ((rows - 1 - j0 * 10 * 24 * tile.Rows - j1 * 24 * tile.Rows - j2 * tile.Rows - l) * cols + i0 * 10 * 24 * tile.Columns + i1 * 24 * tile.Columns + i2 * tile.Columns + k) * 2; + short e = br.ReadInt16(); + if (e <= -500) + e = ElevationData.Database.ElvMissingFlag; + b[k, l] = e; + if ((e != ElevationData.Database.ElvMissingFlag) && (e < tile.MinElv)) + { + tile.MinElv = e; + tile.MinLon = tile.BaseLon + k * tile.StepWidthLon; + tile.MinLat = tile.BaseLat + l * tile.StepWidthLat; + } + if ((e != ElevationData.Database.ElvMissingFlag) && (e > tile.MaxElv)) + { + tile.MaxElv = e; + tile.MaxLon = tile.BaseLon + k * tile.StepWidthLon; + tile.MaxLat = tile.BaseLat + l * tile.StepWidthLat; + } + if (e != ElevationData.Database.ElvMissingFlag) + all_void = false; + } + } + tile.Elv = b; + tile.LastUpdated = DateTime.UtcNow; + if (!all_void) + { + // ElevationModel.Database.ElevationTileInsertOrUpdateIfNewer(tile); + // create dir if not exists + try + { + if (!Directory.Exists(dir)) + Directory.CreateDirectory(dir); + } + catch (Exception ex) + { + + } + // write json file + ms = new MemoryStream(); + using (StreamWriter sw = new StreamWriter(ms)) + { + string json = tile.ToJSON(); + sw.WriteLine(json); + sw.Flush(); + ms.Position = 0; + File.WriteAllBytes(Path.Combine(dir, filename), ms.ToArray()); + } + File.SetCreationTimeUtc(Path.Combine(dir, filename), tile.LastUpdated); + File.SetLastWriteTimeUtc(Path.Combine(dir, filename), tile.LastUpdated); + } + } + st.Stop(); + tsl_Status.Text = "[" + Path.GetFileNameWithoutExtension(tilename) + ", " + current.ToString() + " of " + count.ToString() + "] Processing tile: " + filename + ", " + st.ElapsedMilliseconds.ToString() + "ms"; + ss_Main.Refresh(); + Application.DoEvents(); + } + } + } + } + } + } + } + } + } + + private void Scan() + { + // string[] files = Directory.GetFiles(Properties.Settings.Default.SourceDir); + // foreach (string file in files) + // ProcessTile(file); + OpenFileDialog Dlg = new OpenFileDialog(); + Dlg.DefaultExt = "*.loc"; + Dlg.Multiselect = true; + Dlg.InitialDirectory = Properties.Settings.Default.SourceDir; + if (Dlg.ShowDialog() == DialogResult.OK) + { + int i = 1; + foreach (string file in Dlg.FileNames) + { + ProcessTile(file, i, Dlg.FileNames.Count()); + i++; + } + } + } + + private void ZIP() + { + int i = 1; + char[] a = new char[2]; + char[] b = new char[2]; + for (a[0] = 'A'; a[0] <= 'R'; a[0]++) + { + for (a[1] = 'A'; a[1] <= 'R'; a[1]++) + { + for (b[0] = '0'; b[0] <= '9'; b[0]++) + { + for (b[1] = '0'; b[1] <= '9'; b[1]++) + { + Stopwatch st = new Stopwatch(); + st.Start(); + string dira = Path.Combine(Properties.Settings.Default.DestDir, new string(a), new string(b)); + string dirb = Path.Combine(Properties.Settings.Default.DestDir, new string(a)); + if (Directory.Exists(dira)) + { + string[] files = Directory.GetFiles(dira, "*.loc"); + if (files.Length > 0) + { + // create ZIP file and add all loc files + string zipfilename = Path.Combine(dirb, new string(a) + new string(b) + ".zip"); + if (File.Exists(zipfilename)) + File.Delete(zipfilename); + ZipFile zip = new ZipFile(zipfilename); + zip.AddFiles(files, ""); + zip.Save(); + // sleep 5s to ensure that file is written + Thread.Sleep(1000); + } + foreach (string file in files) + File.Delete(file); + Directory.Delete(dira); + } + + st.Stop(); + tsl_Status.Text = "Processing dir: " + dira + ", " + st.ElapsedMilliseconds.ToString() + "ms."; + Application.DoEvents(); + i++; + } + } + } + } + } + + private void MainDlg_FormClosing(object sender, FormClosingEventArgs e) + { + Properties.Settings.Default.Save(); + } + + private void btn_SourceDir_Click(object sender, EventArgs e) + { + FolderBrowserDialog Dlg = new FolderBrowserDialog(); + Dlg.SelectedPath = Properties.Settings.Default.SourceDir; + if (Dlg.ShowDialog() == DialogResult.OK) + { + Properties.Settings.Default.SourceDir = Dlg.SelectedPath; + } + } + + private void btn_DestDir_Click(object sender, EventArgs e) + { + FolderBrowserDialog Dlg = new FolderBrowserDialog(); + Dlg.SelectedPath = Properties.Settings.Default.DestDir; + if (Dlg.ShowDialog() == DialogResult.OK) + { + Properties.Settings.Default.DestDir = Dlg.SelectedPath; + } + } + + private void btn_Stop_Click(object sender, EventArgs e) + { + this.Close(); + } + + private void btn_Picture_Click(object sender, EventArgs e) + { + Stopwatch st = new Stopwatch(); + st.Start(); + Bitmap bm = ElevationData.Database.DrawElevationBitmap(50, 0, 60, 20,3200, 3200, ELEVATIONMODEL.SRTM1); + bm.Save("Elevation.tif", System.Drawing.Imaging.ImageFormat.Tiff); + pb_Picture.Image = bm; + st.Stop(); + tsl_Status.Text = "Drawing picture: " + st.ElapsedMilliseconds.ToString() + "ms."; + this.Refresh(); + } + + private void btn_ZIP_Click(object sender, EventArgs e) + { + ZIP(); + } + + public T GetFirstInstance(string propertyName, string json) + { + using (var stringReader = new StringReader(json)) + using (var jsonReader = new JsonTextReader(stringReader)) + { + int level = 0; + + while (jsonReader.Read()) + { + switch (jsonReader.TokenType) + { + case JsonToken.PropertyName: + if (level != 1) + break; + if ((string)jsonReader.Value == propertyName) + { + jsonReader.Read(); + + return (T)jsonReader.Value; + } + break; + + case JsonToken.StartArray: + case JsonToken.StartConstructor: + case JsonToken.StartObject: + level++; + break; + + case JsonToken.EndArray: + case JsonToken.EndConstructor: + case JsonToken.EndObject: + level--; + break; + } + + } + return default(T); + } + } + private void ScanDir(string dir) + { + tsl_Status.Text = "Scanning directory: " + dir; + this.Refresh(); + Application.DoEvents(); + string[] dirs = Directory.GetDirectories(dir); + foreach (string d in dirs) + ScanDir(d); + string[] files = Directory.GetFiles(dir, "*.loc"); + foreach (string file in files) + { + try + { + DateTime lastupdated = File.GetCreationTimeUtc(file); + Tiles.Files.Add(Path.GetFileName(file), lastupdated); + /* + using (StreamReader sr = new StreamReader(File.OpenRead(file))) + { + string json = sr.ReadToEnd(); + // ElevationTileDesignator tile = ElevationTileDesignator.FromJSON(json); + json = "{\"" + json.Remove(0, json.LastIndexOf("LastUpdated")); + lastupdated = GetFirstInstance("LastUpdated", json); + Tiles.Files.Add(Path.GetFileName(file), lastupdated); + } + */ + } + catch (Exception ex) + { + MessageBox.Show(ex.Message); + } + } + } + + private void ScanDirZIP(string dir) + { + tsl_Status.Text = "Scanning directory: " + dir; + this.Refresh(); + Application.DoEvents(); + string[] dirs = Directory.GetDirectories(dir); + foreach (string d in dirs) + ScanDirZIP(d); + string[] zipfiles = Directory.GetFiles(dir, "*.zip"); + foreach (string zipfile in zipfiles) + { + ZipFile zip = new ZipFile(zipfile); + foreach (ZipEntry file in zip.Entries) + { + Tiles.Files.Add(Path.GetFileName(file.FileName), file.CreationTime.ToUniversalTime()); + } + } + } + + private void CAT() + { + Tiles.Files.Clear(); + ScanDir(Properties.Settings.Default.DestDir); + tsl_Status.Text = "Scanned: " + Tiles.Files.Count + " files."; + this.Refresh(); + string filename = Path.Combine(Properties.Settings.Default.DestDir, "files.cat"); + using (StreamWriter sw = new StreamWriter(filename)) + { + sw.WriteLine("// (D)igital (E)levation (Model) tile catalogue"); + sw.WriteLine("// created " + DateTime.UtcNow.ToString("yyyy-MM-dd") + " (c) DL2ALF"); + sw.WriteLine("// using elevation data from the GLOBE and SRTM (V3) projects, voids filled with the help of ASTER data"); + sw.WriteLine("// GLOBE data homepage: https://www.ngdc.noaa.gov/mgg/topo/globe.html"); + sw.WriteLine("// SRTM3 data homepage: https://lpdaac.usgs.gov/dataset_discovery/measures/measures_products_table/srtmgl3_v003"); + sw.WriteLine("// SRTM1 data homepage: https://lpdaac.usgs.gov/dataset_discovery/measures/measures_products_table/srtmgl1_v003"); + sw.WriteLine("// ASTER GDEM is a product of NASA and METI. See links above for details."); + sw.WriteLine(); + foreach (KeyValuePair tile in Tiles.Files) + { + sw.WriteLine(tile.Key + ";" + tile.Value.ToString("yyyy-MM-dd HH:mm:ssZ", CultureInfo.InvariantCulture)); + } + } + string zipfilename = filename.Replace(".cat", ".zip"); + if (File.Exists(zipfilename)) + File.Delete(zipfilename); + ZipFile zip = new ZipFile(zipfilename); + zip.AddFile(filename, ""); + zip.Save(); + } + + private void CATFromZIP() + { + Tiles.Files.Clear(); + ScanDirZIP(Properties.Settings.Default.DestDir); + tsl_Status.Text = "Scanned: " + Tiles.Files.Count + " files."; + this.Refresh(); + Application.DoEvents(); + // string json = Tiles.ToJSON(); + string filename = Path.Combine(Properties.Settings.Default.DestDir, "files.cat"); + using (StreamWriter sw = new StreamWriter(filename)) + { + sw.WriteLine("// (D)igital (E)levation (Model) tile catalogue"); + sw.WriteLine("// created " + DateTime.UtcNow.ToString("yyyy-MM-dd") + " (c) DL2ALF"); + sw.WriteLine("// using elevation data from the GLOBE and SRTM (V3) projects, voids filled with the help of ASTER data"); + sw.WriteLine("// GLOBE data homepage: https://www.ngdc.noaa.gov/mgg/topo/globe.html"); + sw.WriteLine("// SRTM3 data homepage: https://lpdaac.usgs.gov/dataset_discovery/measures/measures_products_table/srtmgl3_v003"); + sw.WriteLine("// SRTM1 data homepage: https://lpdaac.usgs.gov/dataset_discovery/measures/measures_products_table/srtmgl1_v003"); + sw.WriteLine("// ASTER GDEM is a product of NASA and METI. See links above for details."); + sw.WriteLine(); + foreach (KeyValuePair tile in Tiles.Files) + { + sw.WriteLine(tile.Key + ";" + tile.Value.ToString("yyyy-MM-dd HH:mm:ssZ", CultureInfo.InvariantCulture)); + } + } + string zipfilename = filename.Replace(".cat", ".zip"); + if (File.Exists(zipfilename)) + File.Delete(zipfilename); + ZipFile zip = new ZipFile(zipfilename); + zip.AddFile(filename, ""); + zip.Save(); + } + + private void btn_CAT_Click(object sender, EventArgs e) + { + CAT(); + } + + private void TestCAT() + { + // ElevationCatalogue cat = new ElevationCatalogue("http://www.airscout.eu/downloads/ElevationData/SRTM3", Application.StartupPath, 35, -15, 60, 30); + } + + private void btn_TestCAT_Click(object sender, EventArgs e) + { + TestCAT(); + } + + private void btn_CATFromZIP_Click(object sender, EventArgs e) + { + CATFromZIP(); + } + + private void btn_PictureFromTiles_Click(object sender, EventArgs e) + { + FolderBrowserDialog Dlg = new FolderBrowserDialog(); + Dlg.SelectedPath = Properties.Settings.Default.DestDir; + if (Dlg.ShowDialog() == DialogResult.OK) + { + string[] files = Directory.GetFiles(Dlg.SelectedPath, "*.loc"); + Bitmap bm = null; + double baselat, baselon; + string dir = Directory.GetParent(Dlg.SelectedPath).Name + Dlg.SelectedPath.Substring(Dlg.SelectedPath.LastIndexOf('\\')).Replace("\\", ""); + MaidenheadLocator.LatLonFromLoc(dir, PositionInRectangle.BottomLeft, out baselat, out baselon); + foreach (string file in files) + { + tsl_Status.Text = "Processing " + Path.GetFileName(file) + "..."; + Application.DoEvents(); + using (StreamReader sr = new StreamReader(File.OpenRead(file))) + { + string json = sr.ReadToEnd(); + ElevationTileDesignator tile = ElevationTileDesignator.FromJSON(json); + // create bitmap if not created + if (bm == null) + { + bm = new Bitmap(tile.Columns * 24, tile.Rows * 24); + } + Bitmap bt = tile.ToBitmap(); + // bt.Save(Path.Combine(Dlg.SelectedPath, tile.TileIndex) + ".tif", System.Drawing.Imaging.ImageFormat.Tiff); + int col = (int)(tile.TileIndex[4] - 'A') * tile.Columns; + int row = (int)(tile.TileIndex[5] - 'A') * tile.Rows; + for (int i = 0; i < tile.Columns; i++) + { + for (int j = 0; j < tile.Rows; j++) + { + bm.SetPixel(col + i, bm.Height - row - bt.Height + j, bt.GetPixel(i, j)); + } + } + } + } + if (bm != null) + bm.Save("ElevationTiles.tif", System.Drawing.Imaging.ImageFormat.Tiff); + } + } + + private void ScanDirDelete(string dir) + { + tsl_Status.Text = "Scanning directory: " + dir; + this.Refresh(); + string[] dirs = Directory.GetDirectories(dir); + foreach (string d in dirs) + ScanDirZIP(d); + string[] delfiles = Directory.GetFiles(dir, "*.*"); + foreach (string delfile in delfiles) + { + try + { + File.Delete(delfile); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + } + } + + private void Delete() + { + FolderBrowserDialog Dlg = new FolderBrowserDialog(); + Dlg.SelectedPath = Properties.Settings.Default.DestDir; + if (Dlg.ShowDialog() == DialogResult.OK) + { + ScanDirDelete(Dlg.SelectedPath); + } + } + + private void btn_Delete_Click(object sender, EventArgs e) + { + Delete(); + } + } + + [System.ComponentModel.DesignerCategory("")] + public class DataTableSRTM3 : DataTable + { + public DataTableSRTM3() + : base() + { + // set table name + TableName = "SRTM3Tiles"; + // create all specific columns + DataColumn FileIndex = this.Columns.Add("FileIndex", typeof(string)); + DataColumn MinLat = this.Columns.Add("MinLat", typeof(double)); + DataColumn MaxLat = this.Columns.Add("MaxLat", typeof(double)); + DataColumn MinLon = this.Columns.Add("MinLon", typeof(double)); + DataColumn MaxLon = this.Columns.Add("MaxLon", typeof(double)); + DataColumn MinElv = this.Columns.Add("MinElv", typeof(int)); + DataColumn MaxElv = this.Columns.Add("MaxElv", typeof(int)); + DataColumn LastUpdated = this.Columns.Add("LastUpdated", typeof(string)); + // create primary key + DataColumn[] keys = new DataColumn[1]; + keys[0] = FileIndex; + this.PrimaryKey = keys; + } + + } + +} diff --git a/ElevationTileGenerator/MainDlg.resx b/ElevationTileGenerator/MainDlg.resx new file mode 100644 index 0000000..05efec9 --- /dev/null +++ b/ElevationTileGenerator/MainDlg.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/ElevationTileGenerator/Program.cs b/ElevationTileGenerator/Program.cs new file mode 100644 index 0000000..a7b2372 --- /dev/null +++ b/ElevationTileGenerator/Program.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace ElevationTileGenerator +{ + static class Program + { + /// + /// Der Haupteinstiegspunkt für die Anwendung. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new MainDlg()); + } + } +} diff --git a/ElevationTileGenerator/Properties/AssemblyInfo.cs b/ElevationTileGenerator/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..0d904b7 --- /dev/null +++ b/ElevationTileGenerator/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Allgemeine Informationen über eine Assembly werden über die folgenden +// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, +// die einer Assembly zugeordnet sind. +[assembly: AssemblyTitle("ElevationTileGenerator")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ElevationTileGenerator")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar +// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von +// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen. +[assembly: ComVisible(false)] + +// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird +[assembly: Guid("955cea7d-37f6-4122-b4ca-b08c2b73969f")] + +// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: +// +// Hauptversion +// Nebenversion +// Buildnummer +// Revision +// +// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern +// übernehmen, indem Sie "*" eingeben: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/ElevationTileGenerator/Properties/Resources.Designer.cs b/ElevationTileGenerator/Properties/Resources.Designer.cs new file mode 100644 index 0000000..97fc2be --- /dev/null +++ b/ElevationTileGenerator/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.42000 +// +// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn +// der Code erneut generiert wird. +// +//------------------------------------------------------------------------------ + +namespace ElevationTileGenerator.Properties { + using System; + + + /// + /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw. + /// + // Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert + // -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert. + // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen + // mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ElevationTileGenerator.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle + /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/ElevationTileGenerator/Properties/Resources.resx b/ElevationTileGenerator/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/ElevationTileGenerator/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/ElevationTileGenerator/Properties/Settings.Designer.cs b/ElevationTileGenerator/Properties/Settings.Designer.cs new file mode 100644 index 0000000..80b4a93 --- /dev/null +++ b/ElevationTileGenerator/Properties/Settings.Designer.cs @@ -0,0 +1,62 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.42000 +// +// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn +// der Code erneut generiert wird. +// +//------------------------------------------------------------------------------ + +namespace ElevationTileGenerator.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string SourceDir { + get { + return ((string)(this["SourceDir"])); + } + set { + this["SourceDir"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string DestDir { + get { + return ((string)(this["DestDir"])); + } + set { + this["DestDir"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool Overwrite { + get { + return ((bool)(this["Overwrite"])); + } + set { + this["Overwrite"] = value; + } + } + } +} diff --git a/ElevationTileGenerator/Properties/Settings.settings b/ElevationTileGenerator/Properties/Settings.settings new file mode 100644 index 0000000..c86beba --- /dev/null +++ b/ElevationTileGenerator/Properties/Settings.settings @@ -0,0 +1,15 @@ + + + + + + + + + + + + False + + + \ No newline at end of file diff --git a/ElevationTileGenerator/packages.config b/ElevationTileGenerator/packages.config new file mode 100644 index 0000000..0aee17e --- /dev/null +++ b/ElevationTileGenerator/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/GreatMaps/.hg_archival.txt b/GreatMaps/.hg_archival.txt new file mode 100644 index 0000000..179c922 --- /dev/null +++ b/GreatMaps/.hg_archival.txt @@ -0,0 +1,5 @@ +repo: 4535e32c8607b09918d4b3415247cd7063d4a1a1 +node: 871ebb856b1d3bc515193fc75393ad3e12b29905 +branch: default +latesttag: 1.6 +latesttagdistance: 112 diff --git a/GreatMaps/.hgtags b/GreatMaps/.hgtags new file mode 100644 index 0000000..623b4e1 --- /dev/null +++ b/GreatMaps/.hgtags @@ -0,0 +1,24 @@ +a41a181da72e4724a7e3442ed6260f174a47e639 1.5 beta +a41a181da72e4724a7e3442ed6260f174a47e639 1.5.0.1 +a41a181da72e4724a7e3442ed6260f174a47e639 1.5.0.1 +0000000000000000000000000000000000000000 1.5.0.1 +0000000000000000000000000000000000000000 1.5.0.1 +d7c8a823dcfa8320d90e26490ef0df894d07beec 1.5.0.1 +d7c8a823dcfa8320d90e26490ef0df894d07beec 1.5.0.1 +0000000000000000000000000000000000000000 1.5.0.1 +0000000000000000000000000000000000000000 1.5.0.1 +9283af54b20b47c60ec16730c449468047a773d0 1.5.0.1 +2b589a98ebb5da85bd4df8ea97a05dce10065376 1.5.1.1 +5aa1baf7897fbe83ffc414f83daad717eeb7073a 1.5.1.2 +2d0d554965044e588be7963c76f1ae01beaab029 1.5.1.3 +2d0d554965044e588be7963c76f1ae01beaab029 1.5.1.3 +0000000000000000000000000000000000000000 1.5.1.3 +0000000000000000000000000000000000000000 1.5.1.3 +940011f42d467102e820969c952fa78525724c45 1.5.1.3 +940011f42d467102e820969c952fa78525724c45 1.5.1.3 +0000000000000000000000000000000000000000 1.5.1.3 +258a79e50af78a6586292b98ba7925144d493d32 1.5.3.1 +3eb3a2e66d886e999ee6e39c747896df9cb47de3 1.5.3.2 +4dd6c1c7428b74731d67e713f023f9714a45a369 1.5.3.3 +32f7138ff593910cf788e8c9dd766716e8340042 1.5.5.5 +dbfffa5b4adced279df1e089a153b51dbbc34d92 1.6 diff --git a/GreatMaps/GMap.NET vs10.sln b/GreatMaps/GMap.NET vs10.sln new file mode 100644 index 0000000..cce9f3a --- /dev/null +++ b/GreatMaps/GMap.NET vs10.sln @@ -0,0 +1,105 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GMap.NET", "GMap.NET", "{775C3C1A-44D3-41F4-9FC6-F83E39D3ABB8}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Demo", "Demo", "{4071E6E4-D401-4EF2-94B0-6F5257088116}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Testing", "Testing", "{A24009F2-84ED-4F0A-BC1C-8B05F474986A}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Info", "Info", "{9EB13FA2-ADB7-4C90-A6DA-18EDFE256273}" + ProjectSection(SolutionItems) = preProject + Info\google maps.pdf = Info\google maps.pdf + Info\License.txt = Info\License.txt + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Demo.WindowsForms", "Demo.WindowsForms\Demo.WindowsForms.csproj", "{A2E07A76-8B2C-41A2-B23E-EA31AE94D706}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Demo.WindowsPresentation", "Demo.WindowsPresentation\Demo.WindowsPresentation.csproj", "{83195AEF-0071-471C-9E8B-E67211F5D028}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GMap.NET.Core", "GMap.NET.Core\GMap.NET.Core.csproj", "{D0C39D9D-BED0-418B-9A5E-713176CAF40C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GMap.NET.WindowsForms", "GMap.NET.WindowsForms\GMap.NET.WindowsForms.csproj", "{E06DEF77-F933-42FB-AFD7-DB2D0D8D6A98}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GMap.NET.WindowsPresentation", "GMap.NET.WindowsPresentation\GMap.NET.WindowsPresentation.csproj", "{644FE7D4-0184-400F-B2D7-99CB41360658}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TemplatedBinding", "Testing\TemplatedBinding\TemplatedBinding.csproj", "{C7CD6A76-D941-493F-91F8-6222AB87BECA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WPF-GMapControlNew", "Testing\WPF-GMapControlNew\WPF-GMapControlNew.csproj", "{B5A673B4-6286-4150-A536-1C16F3B8DC8B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Demo.StreetView", "Testing\Demo.StreetView\Demo.StreetView.csproj", "{B6E411A2-DFD8-461E-8207-BDBA405264CA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BigMapMaker", "Testing\BigMapMaker\BigMapMaker.csproj", "{A6E9D42C-935B-44D0-9FB0-E2E0319627D1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Demo.Clouds", "Testing\Demo.Clouds\Demo.Clouds.csproj", "{097FA134-51A5-4801-AADD-A1914EAA32FF}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Setup", "Setup", "{BA76BAF9-B8BB-46B3-89D2-3087524D80B0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GMap.NET - Hot Build", "Setup\GMap.NET - Hot Build\GMap.NET - Hot Build.csproj", "{C5E9D4B5-F9A8-4414-9B17-5A70DC2705C0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Demo.Docking", "Testing\Demo.Docking\Demo.Docking.csproj", "{F729FF99-2991-4819-9855-7CD7A1199089}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleApplication", "Testing\ConsoleApplication\ConsoleApplication.csproj", "{448B761F-C033-447D-93D5-F4380A7DB0E2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A2E07A76-8B2C-41A2-B23E-EA31AE94D706}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A2E07A76-8B2C-41A2-B23E-EA31AE94D706}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A2E07A76-8B2C-41A2-B23E-EA31AE94D706}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A2E07A76-8B2C-41A2-B23E-EA31AE94D706}.Release|Any CPU.Build.0 = Release|Any CPU + {83195AEF-0071-471C-9E8B-E67211F5D028}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {83195AEF-0071-471C-9E8B-E67211F5D028}.Debug|Any CPU.Build.0 = Debug|Any CPU + {83195AEF-0071-471C-9E8B-E67211F5D028}.Release|Any CPU.ActiveCfg = Release|Any CPU + {83195AEF-0071-471C-9E8B-E67211F5D028}.Release|Any CPU.Build.0 = Release|Any CPU + {D0C39D9D-BED0-418B-9A5E-713176CAF40C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D0C39D9D-BED0-418B-9A5E-713176CAF40C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D0C39D9D-BED0-418B-9A5E-713176CAF40C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D0C39D9D-BED0-418B-9A5E-713176CAF40C}.Release|Any CPU.Build.0 = Release|Any CPU + {E06DEF77-F933-42FB-AFD7-DB2D0D8D6A98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E06DEF77-F933-42FB-AFD7-DB2D0D8D6A98}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E06DEF77-F933-42FB-AFD7-DB2D0D8D6A98}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E06DEF77-F933-42FB-AFD7-DB2D0D8D6A98}.Release|Any CPU.Build.0 = Release|Any CPU + {644FE7D4-0184-400F-B2D7-99CB41360658}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {644FE7D4-0184-400F-B2D7-99CB41360658}.Debug|Any CPU.Build.0 = Debug|Any CPU + {644FE7D4-0184-400F-B2D7-99CB41360658}.Release|Any CPU.ActiveCfg = Release|Any CPU + {644FE7D4-0184-400F-B2D7-99CB41360658}.Release|Any CPU.Build.0 = Release|Any CPU + {C7CD6A76-D941-493F-91F8-6222AB87BECA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C7CD6A76-D941-493F-91F8-6222AB87BECA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B5A673B4-6286-4150-A536-1C16F3B8DC8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B5A673B4-6286-4150-A536-1C16F3B8DC8B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B6E411A2-DFD8-461E-8207-BDBA405264CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B6E411A2-DFD8-461E-8207-BDBA405264CA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A6E9D42C-935B-44D0-9FB0-E2E0319627D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A6E9D42C-935B-44D0-9FB0-E2E0319627D1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {097FA134-51A5-4801-AADD-A1914EAA32FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {097FA134-51A5-4801-AADD-A1914EAA32FF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C5E9D4B5-F9A8-4414-9B17-5A70DC2705C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C5E9D4B5-F9A8-4414-9B17-5A70DC2705C0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F729FF99-2991-4819-9855-7CD7A1199089}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F729FF99-2991-4819-9855-7CD7A1199089}.Release|Any CPU.ActiveCfg = Release|Any CPU + {448B761F-C033-447D-93D5-F4380A7DB0E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {448B761F-C033-447D-93D5-F4380A7DB0E2}.Release|Any CPU.ActiveCfg = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {D0C39D9D-BED0-418B-9A5E-713176CAF40C} = {775C3C1A-44D3-41F4-9FC6-F83E39D3ABB8} + {E06DEF77-F933-42FB-AFD7-DB2D0D8D6A98} = {775C3C1A-44D3-41F4-9FC6-F83E39D3ABB8} + {644FE7D4-0184-400F-B2D7-99CB41360658} = {775C3C1A-44D3-41F4-9FC6-F83E39D3ABB8} + {A2E07A76-8B2C-41A2-B23E-EA31AE94D706} = {4071E6E4-D401-4EF2-94B0-6F5257088116} + {83195AEF-0071-471C-9E8B-E67211F5D028} = {4071E6E4-D401-4EF2-94B0-6F5257088116} + {C7CD6A76-D941-493F-91F8-6222AB87BECA} = {A24009F2-84ED-4F0A-BC1C-8B05F474986A} + {B5A673B4-6286-4150-A536-1C16F3B8DC8B} = {A24009F2-84ED-4F0A-BC1C-8B05F474986A} + {B6E411A2-DFD8-461E-8207-BDBA405264CA} = {A24009F2-84ED-4F0A-BC1C-8B05F474986A} + {A6E9D42C-935B-44D0-9FB0-E2E0319627D1} = {A24009F2-84ED-4F0A-BC1C-8B05F474986A} + {097FA134-51A5-4801-AADD-A1914EAA32FF} = {A24009F2-84ED-4F0A-BC1C-8B05F474986A} + {F729FF99-2991-4819-9855-7CD7A1199089} = {A24009F2-84ED-4F0A-BC1C-8B05F474986A} + {448B761F-C033-447D-93D5-F4380A7DB0E2} = {A24009F2-84ED-4F0A-BC1C-8B05F474986A} + {C5E9D4B5-F9A8-4414-9B17-5A70DC2705C0} = {BA76BAF9-B8BB-46B3-89D2-3087524D80B0} + EndGlobalSection +EndGlobal diff --git a/GreatMaps/GMap.NET.Core/App.config b/GreatMaps/GMap.NET.Core/App.config new file mode 100644 index 0000000..22222c2 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/App.config @@ -0,0 +1,19 @@ + + + +
+ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.CacheProviders/MSSQLCEPureImageCache.cs b/GreatMaps/GMap.NET.Core/GMap.NET.CacheProviders/MSSQLCEPureImageCache.cs new file mode 100644 index 0000000..9f968da --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.CacheProviders/MSSQLCEPureImageCache.cs @@ -0,0 +1,255 @@ + +namespace GMap.NET.CacheProviders +{ +#if !SQLite + using System; + using System.Data; + using System.Diagnostics; + using System.IO; + using SqlCommand = System.Data.SqlServerCe.SqlCeCommand; + using SqlConnection = System.Data.SqlServerCe.SqlCeConnection; + using GMap.NET.MapProviders; + + /// + /// image cache for ms sql server + /// + internal class MsSQLCePureImageCache : PureImageCache, IDisposable + { + string cache; + string gtileCache; + + /// + /// local cache location + /// + public string CacheLocation + { + get + { + return cache; + } + set + { + cache = value; + gtileCache = Path.Combine(cache, "TileDBv3") + Path.DirectorySeparatorChar + GMapProvider.LanguageStr + Path.DirectorySeparatorChar; + } + } + + SqlCommand cmdInsert; + SqlCommand cmdFetch; + SqlConnection cnGet; + SqlConnection cnSet; + + /// + /// is cache initialized + /// + public volatile bool Initialized = false; + + /// + /// inits connection to server + /// + /// + public bool Initialize() + { + if(!Initialized) + { + #region prepare mssql & cache table + try + { + // precrete dir + if(!Directory.Exists(gtileCache)) + { + Directory.CreateDirectory(gtileCache); + } + + string connectionString = string.Format("Data Source={0}Data.sdf", gtileCache); + + if(!File.Exists(gtileCache + "Data.sdf")) + { + using(System.Data.SqlServerCe.SqlCeEngine engine = new System.Data.SqlServerCe.SqlCeEngine(connectionString)) + { + engine.CreateDatabase(); + } + + try + { + using(SqlConnection c = new SqlConnection(connectionString)) + { + c.Open(); + + using(SqlCommand cmd = new SqlCommand( + "CREATE TABLE [GMapNETcache] ( \n" + + " [Type] [int] NOT NULL, \n" + + " [Zoom] [int] NOT NULL, \n" + + " [X] [int] NOT NULL, \n" + + " [Y] [int] NOT NULL, \n" + + " [Tile] [image] NOT NULL, \n" + + " CONSTRAINT [PK_GMapNETcache] PRIMARY KEY (Type, Zoom, X, Y) \n" + + ")", c)) + { + cmd.ExecuteNonQuery(); + } + } + } + catch(Exception ex) + { + try + { + File.Delete(gtileCache + "Data.sdf"); + } + catch + { + } + + throw ex; + } + } + + // different connections so the multi-thread inserts and selects don't collide on open readers. + this.cnGet = new SqlConnection(connectionString); + this.cnGet.Open(); + this.cnSet = new SqlConnection(connectionString); + this.cnSet.Open(); + + this.cmdFetch = new SqlCommand("SELECT [Tile] FROM [GMapNETcache] WITH (NOLOCK) WHERE [X]=@x AND [Y]=@y AND [Zoom]=@zoom AND [Type]=@type", cnGet); + this.cmdFetch.Parameters.Add("@x", System.Data.SqlDbType.Int); + this.cmdFetch.Parameters.Add("@y", System.Data.SqlDbType.Int); + this.cmdFetch.Parameters.Add("@zoom", System.Data.SqlDbType.Int); + this.cmdFetch.Parameters.Add("@type", System.Data.SqlDbType.Int); + this.cmdFetch.Prepare(); + + this.cmdInsert = new SqlCommand("INSERT INTO [GMapNETcache] ( [X], [Y], [Zoom], [Type], [Tile] ) VALUES ( @x, @y, @zoom, @type, @tile )", cnSet); + this.cmdInsert.Parameters.Add("@x", System.Data.SqlDbType.Int); + this.cmdInsert.Parameters.Add("@y", System.Data.SqlDbType.Int); + this.cmdInsert.Parameters.Add("@zoom", System.Data.SqlDbType.Int); + this.cmdInsert.Parameters.Add("@type", System.Data.SqlDbType.Int); + this.cmdInsert.Parameters.Add("@tile", System.Data.SqlDbType.Image); //, calcmaximgsize); + //can't prepare insert because of the IMAGE field having a variable size. Could set it to some 'maximum' size? + + Initialized = true; + } + catch(Exception ex) + { + Initialized = false; + Debug.WriteLine(ex.Message); + } + #endregion + } + return Initialized; + } + + #region IDisposable Members + public void Dispose() + { + lock(cmdInsert) + { + if(cmdInsert != null) + { + cmdInsert.Dispose(); + cmdInsert = null; + } + + if(cnSet != null) + { + cnSet.Dispose(); + cnSet = null; + } + } + + lock(cmdFetch) + { + if(cmdFetch != null) + { + cmdFetch.Dispose(); + cmdFetch = null; + } + + if(cnGet != null) + { + cnGet.Dispose(); + cnGet = null; + } + } + Initialized = false; + } + #endregion + + #region PureImageCache Members + public bool PutImageToCache(byte[] tile, int type, GPoint pos, int zoom) + { + bool ret = true; + { + if(Initialize()) + { + try + { + lock(cmdInsert) + { + cmdInsert.Parameters["@x"].Value = pos.X; + cmdInsert.Parameters["@y"].Value = pos.Y; + cmdInsert.Parameters["@zoom"].Value = zoom; + cmdInsert.Parameters["@type"].Value = type; + cmdInsert.Parameters["@tile"].Value = tile; + cmdInsert.ExecuteNonQuery(); + } + } + catch(Exception ex) + { + Debug.WriteLine(ex.ToString()); + ret = false; + Dispose(); + } + } + } + return ret; + } + + public PureImage GetImageFromCache(int type, GPoint pos, int zoom) + { + PureImage ret = null; + { + if(Initialize()) + { + try + { + object odata = null; + lock(cmdFetch) + { + cmdFetch.Parameters["@x"].Value = pos.X; + cmdFetch.Parameters["@y"].Value = pos.Y; + cmdFetch.Parameters["@zoom"].Value = zoom; + cmdFetch.Parameters["@type"].Value = type; + odata = cmdFetch.ExecuteScalar(); + } + + if(odata != null && odata != DBNull.Value) + { + byte[] tile = (byte[])odata; + if(tile != null && tile.Length > 0) + { + if(GMapProvider.TileImageProxy != null) + { + ret = GMapProvider.TileImageProxy.FromArray(tile); + } + } + tile = null; + } + } + catch(Exception ex) + { + Debug.WriteLine(ex.ToString()); + ret = null; + Dispose(); + } + } + } + return ret; + } + + int PureImageCache.DeleteOlderThan(DateTime date, int ? type) + { + throw new NotImplementedException(); + } + #endregion + } +#endif +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.CacheProviders/MSSQLPureImageCache.cs b/GreatMaps/GMap.NET.Core/GMap.NET.CacheProviders/MSSQLPureImageCache.cs new file mode 100644 index 0000000..e39de4b --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.CacheProviders/MSSQLPureImageCache.cs @@ -0,0 +1,255 @@ + +namespace GMap.NET.CacheProviders +{ + using System; + using System.Data.SqlClient; + using System.Diagnostics; + using System.IO; + using GMap.NET.MapProviders; + + /// + /// image cache for ms sql server + /// optimized by mmurfinsimmons@gmail.com + /// + public class MsSQLPureImageCache : PureImageCache, IDisposable + { + string connectionString = string.Empty; + public string ConnectionString + { + get + { + return connectionString; + } + set + { + if(connectionString != value) + { + connectionString = value; + + if(Initialized) + { + Dispose(); + Initialize(); + } + } + } + } + + SqlCommand cmdInsert; + SqlCommand cmdFetch; + SqlConnection cnGet; + SqlConnection cnSet; + + bool initialized = false; + + /// + /// is cache initialized + /// + public bool Initialized + { + get + { + lock(this) + { + return initialized; + } + } + private set + { + lock(this) + { + initialized = value; + } + } + } + + /// + /// inits connection to server + /// + /// + public bool Initialize() + { + lock(this) + { + if(!Initialized) + { + #region prepare mssql & cache table + try + { + // different connections so the multi-thread inserts and selects don't collide on open readers. + this.cnGet = new SqlConnection(connectionString); + this.cnGet.Open(); + this.cnSet = new SqlConnection(connectionString); + this.cnSet.Open(); + + bool tableexists = false; + using(SqlCommand cmd = new SqlCommand("select object_id('GMapNETcache')", cnGet)) + { + object objid = cmd.ExecuteScalar(); + tableexists = (objid != null && objid != DBNull.Value); + } + if(!tableexists) + { + using(SqlCommand cmd = new SqlCommand( + "CREATE TABLE [GMapNETcache] ( \n" + + " [Type] [int] NOT NULL, \n" + + " [Zoom] [int] NOT NULL, \n" + + " [X] [int] NOT NULL, \n" + + " [Y] [int] NOT NULL, \n" + + " [Tile] [image] NOT NULL, \n" + + " CONSTRAINT [PK_GMapNETcache] PRIMARY KEY CLUSTERED (Type, Zoom, X, Y) \n" + + ")", cnGet)) + { + cmd.ExecuteNonQuery(); + } + } + + this.cmdFetch = new SqlCommand("SELECT [Tile] FROM [GMapNETcache] WITH (NOLOCK) WHERE [X]=@x AND [Y]=@y AND [Zoom]=@zoom AND [Type]=@type", cnGet); + this.cmdFetch.Parameters.Add("@x", System.Data.SqlDbType.Int); + this.cmdFetch.Parameters.Add("@y", System.Data.SqlDbType.Int); + this.cmdFetch.Parameters.Add("@zoom", System.Data.SqlDbType.Int); + this.cmdFetch.Parameters.Add("@type", System.Data.SqlDbType.Int); + this.cmdFetch.Prepare(); + + this.cmdInsert = new SqlCommand("INSERT INTO [GMapNETcache] ( [X], [Y], [Zoom], [Type], [Tile] ) VALUES ( @x, @y, @zoom, @type, @tile )", cnSet); + this.cmdInsert.Parameters.Add("@x", System.Data.SqlDbType.Int); + this.cmdInsert.Parameters.Add("@y", System.Data.SqlDbType.Int); + this.cmdInsert.Parameters.Add("@zoom", System.Data.SqlDbType.Int); + this.cmdInsert.Parameters.Add("@type", System.Data.SqlDbType.Int); + this.cmdInsert.Parameters.Add("@tile", System.Data.SqlDbType.Image); //, calcmaximgsize); + //can't prepare insert because of the IMAGE field having a variable size. Could set it to some 'maximum' size? + + Initialized = true; + } + catch(Exception ex) + { + this.initialized = false; + Debug.WriteLine(ex.Message); + } + #endregion + } + return Initialized; + } + } + + #region IDisposable Members + public void Dispose() + { + lock(cmdInsert) + { + if(cmdInsert != null) + { + cmdInsert.Dispose(); + cmdInsert = null; + } + + if(cnSet != null) + { + cnSet.Dispose(); + cnSet = null; + } + } + + lock(cmdFetch) + { + if(cmdFetch != null) + { + cmdFetch.Dispose(); + cmdFetch = null; + } + + if(cnGet != null) + { + cnGet.Dispose(); + cnGet = null; + } + } + Initialized = false; + } + #endregion + + #region PureImageCache Members + public bool PutImageToCache(byte[] tile, int type, GPoint pos, int zoom) + { + bool ret = true; + { + if(Initialize()) + { + try + { + lock(cmdInsert) + { + cmdInsert.Parameters["@x"].Value = pos.X; + cmdInsert.Parameters["@y"].Value = pos.Y; + cmdInsert.Parameters["@zoom"].Value = zoom; + cmdInsert.Parameters["@type"].Value = type; + cmdInsert.Parameters["@tile"].Value = tile; + cmdInsert.ExecuteNonQuery(); + } + } + catch(Exception ex) + { + Debug.WriteLine(ex.ToString()); + ret = false; + Dispose(); + } + } + } + return ret; + } + + public PureImage GetImageFromCache(int type, GPoint pos, int zoom) + { + PureImage ret = null; + { + if(Initialize()) + { + try + { + object odata = null; + lock(cmdFetch) + { + cmdFetch.Parameters["@x"].Value = pos.X; + cmdFetch.Parameters["@y"].Value = pos.Y; + cmdFetch.Parameters["@zoom"].Value = zoom; + cmdFetch.Parameters["@type"].Value = type; + odata = cmdFetch.ExecuteScalar(); + } + + if(odata != null && odata != DBNull.Value) + { + byte[] tile = (byte[])odata; + if(tile != null && tile.Length > 0) + { + if(GMapProvider.TileImageProxy != null) + { + ret = GMapProvider.TileImageProxy.FromArray(tile); + } + } + tile = null; + } + } + catch(Exception ex) + { + Debug.WriteLine(ex.ToString()); + ret = null; + Dispose(); + } + } + } + return ret; + } + + /// + /// NotImplemented + /// + /// + /// + /// + int PureImageCache.DeleteOlderThan(DateTime date, int ? type) + { + throw new NotImplementedException(); + } + #endregion + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.CacheProviders/MemoryCache.cs b/GreatMaps/GMap.NET.Core/GMap.NET.CacheProviders/MemoryCache.cs new file mode 100644 index 0000000..376b90a --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.CacheProviders/MemoryCache.cs @@ -0,0 +1,168 @@ + +namespace GMap.NET.CacheProviders +{ + using System.Diagnostics; + using GMap.NET.Internals; + using System; + + public class MemoryCache : IDisposable + { + readonly KiberTileCache TilesInMemory = new KiberTileCache(); + + FastReaderWriterLock kiberCacheLock = new FastReaderWriterLock(); + + /// + /// the amount of tiles in MB to keep in memmory, default: 22MB, if each ~100Kb it's ~222 tiles + /// + public int Capacity + { + get + { + kiberCacheLock.AcquireReaderLock(); + try + { + return TilesInMemory.MemoryCacheCapacity; + } + finally + { + kiberCacheLock.ReleaseReaderLock(); + } + } + set + { + kiberCacheLock.AcquireWriterLock(); + try + { + TilesInMemory.MemoryCacheCapacity = value; + } + finally + { + kiberCacheLock.ReleaseWriterLock(); + } + } + } + + /// + /// current memmory cache size in MB + /// + public double Size + { + get + { + kiberCacheLock.AcquireReaderLock(); + try + { + return TilesInMemory.MemoryCacheSize; + } + finally + { + kiberCacheLock.ReleaseReaderLock(); + } + } + } + + public void Clear() + { + kiberCacheLock.AcquireWriterLock(); + try + { + TilesInMemory.Clear(); + } + finally + { + kiberCacheLock.ReleaseWriterLock(); + } + } + + // ... + + internal byte[] GetTileFromMemoryCache(RawTile tile) + { + kiberCacheLock.AcquireReaderLock(); + try + { + byte[] ret = null; + if(TilesInMemory.TryGetValue(tile, out ret)) + { + return ret; + } + } + finally + { + kiberCacheLock.ReleaseReaderLock(); + } + return null; + } + + internal void AddTileToMemoryCache(RawTile tile, byte[] data) + { + if(data != null) + { + kiberCacheLock.AcquireWriterLock(); + try + { + if(!TilesInMemory.ContainsKey(tile)) + { + TilesInMemory.Add(tile, data); + } + } + finally + { + kiberCacheLock.ReleaseWriterLock(); + } + } +#if DEBUG + else + { + Debug.WriteLine("adding empty data to MemoryCache ;} "); + if(Debugger.IsAttached) + { + Debugger.Break(); + } + } +#endif + } + + internal void RemoveOverload() + { + kiberCacheLock.AcquireWriterLock(); + try + { + TilesInMemory.RemoveMemoryOverload(); + } + finally + { + kiberCacheLock.ReleaseWriterLock(); + } + } + + #region IDisposable Members + + ~MemoryCache() + { + Dispose(false); + } + + void Dispose(bool disposing) + { + if(kiberCacheLock != null) + { + if(disposing) + { + Clear(); + } + + kiberCacheLock.Dispose(); + kiberCacheLock = null; + } + } + + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + #endregion + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.CacheProviders/MySQLPureImageCache.cs b/GreatMaps/GMap.NET.Core/GMap.NET.CacheProviders/MySQLPureImageCache.cs new file mode 100644 index 0000000..e37a507 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.CacheProviders/MySQLPureImageCache.cs @@ -0,0 +1,259 @@ + +namespace GMap.NET.CacheProviders +{ +#if MySQL + using System; + using System.Data; + using System.Diagnostics; + using System.IO; + using GMap.NET; + using MySql.Data.MySqlClient; + + /// + /// image cache for mysql server + /// + public class MySQLPureImageCache : PureImageCache, IDisposable + { + string connectionString = string.Empty; + public string ConnectionString + { + get + { + return connectionString; + } + set + { + if(connectionString != value) + { + connectionString = value; + + if(Initialized) + { + Dispose(); + Initialize(); + } + } + } + } + + MySqlCommand cmdInsert; + MySqlCommand cmdFetch; + MySqlConnection cnGet; + MySqlConnection cnSet; + + bool initialized = false; + + /// + /// is cache initialized + /// + public bool Initialized + { + get + { + lock(this) + { + return initialized; + } + } + private set + { + lock(this) + { + initialized = value; + } + } + } + + /// + /// inits connection to server + /// + /// + public bool Initialize() + { + lock(this) + { + if(!initialized) + { + #region prepare mssql & cache table + try + { + // different connections so the multi-thread inserts and selects don't collide on open readers. + this.cnGet = new MySqlConnection(connectionString); + this.cnGet.Open(); + this.cnSet = new MySqlConnection(connectionString); + this.cnSet.Open(); + + { + using(MySqlCommand cmd = new MySqlCommand( + @" CREATE TABLE IF NOT EXISTS `gmapnetcache` ( + `Type` int(10) NOT NULL, + `Zoom` int(10) NOT NULL, + `X` int(10) NOT NULL, + `Y` int(10) NOT NULL, + `Tile` longblob NOT NULL, + PRIMARY KEY (`Type`,`Zoom`,`X`,`Y`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8;", cnGet)) + { + cmd.ExecuteNonQuery(); + } + } + + this.cmdFetch = new MySqlCommand("SELECT Tile FROM `gmapnetcache` WHERE Type=@type AND Zoom=@zoom AND X=@x AND Y=@y", cnGet); + this.cmdFetch.Parameters.Add("@type", MySqlDbType.Int32); + this.cmdFetch.Parameters.Add("@zoom", MySqlDbType.Int32); + this.cmdFetch.Parameters.Add("@x", MySqlDbType.Int32); + this.cmdFetch.Parameters.Add("@y", MySqlDbType.Int32); + this.cmdFetch.Prepare(); + + this.cmdInsert = new MySqlCommand("INSERT INTO `gmapnetcache` ( Type, Zoom, X, Y, Tile ) VALUES ( @type, @zoom, @x, @y, @tile )", cnSet); + this.cmdInsert.Parameters.Add("@type", MySqlDbType.Int32); + this.cmdInsert.Parameters.Add("@zoom", MySqlDbType.Int32); + this.cmdInsert.Parameters.Add("@x", MySqlDbType.Int32); + this.cmdInsert.Parameters.Add("@y", MySqlDbType.Int32); + this.cmdInsert.Parameters.Add("@tile", MySqlDbType.Blob); //, calcmaximgsize); + //can't prepare insert because of the IMAGE field having a variable size. Could set it to some 'maximum' size? + + Initialized = true; + } + catch(Exception ex) + { + this.initialized = false; + Debug.WriteLine(ex.Message); + } + #endregion + } + return initialized; + } + } + + #region IDisposable Members + + public void Dispose() + { + lock(cmdInsert) + { + if(cmdInsert != null) + { + cmdInsert.Dispose(); + cmdInsert = null; + } + + if(cnSet != null) + { + cnSet.Dispose(); + cnSet = null; + } + } + + lock(cmdFetch) + { + if(cmdFetch != null) + { + cmdFetch.Dispose(); + cmdFetch = null; + } + + if(cnGet != null) + { + cnGet.Dispose(); + cnGet = null; + } + } + Initialized = false; + } + #endregion + + #region PureImageCache Members + public bool PutImageToCache(MemoryStream tile, MapType type, GPoint pos, int zoom) + { + bool ret = true; + { + if(Initialize()) + { + try + { + lock(cmdInsert) + { + cnSet.Ping(); + + if(cnSet.State != ConnectionState.Open) + { + cnSet.Open(); + } + + cmdInsert.Parameters["@type"].Value = (int)type; + cmdInsert.Parameters["@zoom"].Value = zoom; + cmdInsert.Parameters["@x"].Value = pos.X; + cmdInsert.Parameters["@y"].Value = pos.Y; + cmdInsert.Parameters["@tile"].Value = tile.GetBuffer(); + cmdInsert.ExecuteNonQuery(); + } + } + catch(Exception ex) + { + Debug.WriteLine(ex.ToString()); + ret = false; + Dispose(); + } + } + } + return ret; + } + + public PureImage GetImageFromCache(MapType type, GPoint pos, int zoom) + { + PureImage ret = null; + { + if(Initialize()) + { + try + { + object odata = null; + lock(cmdFetch) + { + cnGet.Ping(); + + if(cnGet.State != ConnectionState.Open) + { + cnGet.Open(); + } + + cmdFetch.Parameters["@type"].Value = (int)type; + cmdFetch.Parameters["@zoom"].Value = zoom; + cmdFetch.Parameters["@x"].Value = pos.X; + cmdFetch.Parameters["@y"].Value = pos.Y; + odata = cmdFetch.ExecuteScalar(); + } + + if(odata != null && odata != DBNull.Value) + { + byte[] tile = (byte[])odata; + if(tile != null && tile.Length > 0) + { + if(GMapProvider.TileImageProxy != null) + { + ret = GMapProvider.TileImageProxy.FromArray(tile); + } + } + tile = null; + } + } + catch(Exception ex) + { + Debug.WriteLine(ex.ToString()); + ret = null; + Dispose(); + } + } + } + return ret; + } + + int PureImageCache.DeleteOlderThan(DateTime date, int ? type) + { + throw new NotImplementedException(); + } + #endregion + } +#endif +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.CacheProviders/PostgreSQLPureImageCache.cs b/GreatMaps/GMap.NET.Core/GMap.NET.CacheProviders/PostgreSQLPureImageCache.cs new file mode 100644 index 0000000..c22e669 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.CacheProviders/PostgreSQLPureImageCache.cs @@ -0,0 +1,275 @@ + +namespace GMap.NET.CacheProviders +{ +#if PostgreSQL + using System; + using System.Diagnostics; + using System.IO; + using Npgsql; + using NpgsqlTypes; + using GMap.NET.MapProviders; + + /// + /// image cache for postgresql server + /// + public class PostgreSQLPureImageCache : PureImageCache, IDisposable + { + string connectionString = string.Empty; + public string ConnectionString + { + get + { + return connectionString; + } + set + { + if(connectionString != value) + { + connectionString = value; + + if(Initialized) + { + Dispose(); + Initialize(); + } + } + } + } + + NpgsqlCommand cmdInsert; + NpgsqlCommand cmdFetch; + NpgsqlConnection cnGet; + NpgsqlConnection cnSet; + + bool initialized = false; + + /// + /// is cache initialized + /// + public bool Initialized + { + get + { + lock(this) + { + return initialized; + } + } + private set + { + lock(this) + { + initialized = value; + } + } + } + + /// + /// inits connection to server + /// + /// + public bool Initialize() + { + lock(this) + { + if(!Initialized) + { + #region prepare postgresql & cache table + + try + { + // different connections so the multi-thread inserts and selects don't collide on open readers. + this.cnGet = new NpgsqlConnection(connectionString); + this.cnGet.Open(); + this.cnSet = new NpgsqlConnection(connectionString); + this.cnSet.Open(); + + bool tableexists = false; + using(NpgsqlCommand cmd = new NpgsqlCommand()) + { + cmd.CommandText = "SELECT COUNT(*) FROM information_schema.tables WHERE table_name='GMapNETcache'"; + cmd.Connection = cnGet; + object cnt = cmd.ExecuteScalar(); + tableexists = ((long)cnt == 1); + } + + if(!tableexists) + { + using(NpgsqlCommand cmd = new NpgsqlCommand()) + { + cmd.Connection = cnGet; + + // create tile-cache table + cmd.CommandText = "CREATE TABLE \"GMapNETcache\" ( \n" + + " \"Type\" integer NOT NULL, \n" + + " \"Zoom\" integer NOT NULL, \n" + + " \"X\" integer NOT NULL, \n" + + " \"Y\" integer NOT NULL, \n" + + " \"Tile\" bytea NOT NULL, \n" + + " CONSTRAINT \"PK_GMapNETcache\" PRIMARY KEY ( \"Type\", \"Zoom\", \"X\", \"Y\" ) )"; + cmd.ExecuteNonQuery(); + + // allows out-of-line storage but not compression of tile data + // see http://www.postgresql.org/docs/9.0/static/storage-toast.html + cmd.CommandText = "ALTER TABLE \"GMapNETcache\" \n" + + " ALTER COLUMN \"Tile\" SET STORAGE EXTERNAL"; + cmd.ExecuteNonQuery(); + + // select pk index for cluster operations + cmd.CommandText = "ALTER TABLE \"GMapNETcache\" \n" + + " CLUSTER ON \"PK_GMapNETcache\""; + cmd.ExecuteNonQuery(); + } + } + + this.cmdFetch = new NpgsqlCommand("SELECT \"Tile\" FROM \"GMapNETcache\" WHERE \"X\"=@x AND \"Y\"=@y AND \"Zoom\"=@zoom AND \"Type\"=@type", cnGet); + this.cmdFetch.Parameters.Add("@x", NpgsqlDbType.Integer); + this.cmdFetch.Parameters.Add("@y", NpgsqlDbType.Integer); + this.cmdFetch.Parameters.Add("@zoom", NpgsqlDbType.Integer); + this.cmdFetch.Parameters.Add("@type", NpgsqlDbType.Integer); + this.cmdFetch.Prepare(); + + this.cmdInsert = new NpgsqlCommand("INSERT INTO \"GMapNETcache\" ( \"X\", \"Y\", \"Zoom\", \"Type\", \"Tile\" ) VALUES ( @x, @y, @zoom, @type, @tile )", cnSet); + this.cmdInsert.Parameters.Add("@x", NpgsqlDbType.Integer); + this.cmdInsert.Parameters.Add("@y", NpgsqlDbType.Integer); + this.cmdInsert.Parameters.Add("@zoom", NpgsqlDbType.Integer); + this.cmdInsert.Parameters.Add("@type", NpgsqlDbType.Integer); + this.cmdInsert.Parameters.Add("@tile", NpgsqlDbType.Bytea); + this.cmdInsert.Prepare(); + + Initialized = true; + } + catch(Exception ex) + { + this.initialized = false; + Debug.WriteLine(ex.Message); + } + + #endregion + } + + return Initialized; + } + } + + #region IDisposable Members + + public void Dispose() + { + lock(cmdInsert) + { + if(cmdInsert != null) + { + cmdInsert.Dispose(); + cmdInsert = null; + } + + if(cnSet != null) + { + cnSet.Dispose(); + cnSet = null; + } + } + + lock(cmdFetch) + { + if(cmdFetch != null) + { + cmdFetch.Dispose(); + cmdFetch = null; + } + + if(cnGet != null) + { + cnGet.Dispose(); + cnGet = null; + } + } + + Initialized = false; + } + + #endregion + + #region PureImageCache Members + + public bool PutImageToCache(byte[] tile, int type, GPoint pos, int zoom) + { + bool ret = true; + + if(Initialize()) + { + try + { + lock(cmdInsert) + { + cmdInsert.Parameters["@x"].Value = pos.X; + cmdInsert.Parameters["@y"].Value = pos.Y; + cmdInsert.Parameters["@zoom"].Value = zoom; + cmdInsert.Parameters["@type"].Value = type; + cmdInsert.Parameters["@tile"].Value = tile; + cmdInsert.ExecuteNonQuery(); + } + } + catch(Exception ex) + { + Debug.WriteLine(ex.ToString()); + ret = false; + Dispose(); + } + } + + return ret; + } + + public PureImage GetImageFromCache(int type, GPoint pos, int zoom) + { + PureImage ret = null; + + if(Initialize()) + { + try + { + object odata = null; + lock(cmdFetch) + { + cmdFetch.Parameters["@x"].Value = pos.X; + cmdFetch.Parameters["@y"].Value = pos.Y; + cmdFetch.Parameters["@zoom"].Value = zoom; + cmdFetch.Parameters["@type"].Value = type; + odata = cmdFetch.ExecuteScalar(); + } + + if(odata != null && odata != DBNull.Value) + { + byte[] tile = (byte[])odata; + if(tile != null && tile.Length > 0) + { + if(GMapProvider.TileImageProxy != null) + { + ret = GMapProvider.TileImageProxy.FromArray(tile); + } + } + tile = null; + } + } + catch(Exception ex) + { + Debug.WriteLine(ex.ToString()); + ret = null; + Dispose(); + } + } + + return ret; + } + + int PureImageCache.DeleteOlderThan(DateTime date, int ? type) + { + throw new NotImplementedException(); + } + + #endregion + } +#endif +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.CacheProviders/SQLitePureImageCache.cs b/GreatMaps/GMap.NET.Core/GMap.NET.CacheProviders/SQLitePureImageCache.cs new file mode 100644 index 0000000..a7bac96 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.CacheProviders/SQLitePureImageCache.cs @@ -0,0 +1,878 @@ + +namespace GMap.NET.CacheProviders +{ +#if SQLite + + using System.Collections.Generic; + using System.Data.Common; + using System.IO; + using System.Text; + using System; + using System.Diagnostics; + using System.Globalization; + using GMap.NET.MapProviders; + using System.Threading; + + using System.Data.SQLite; + /* + #if !MONO + using System.Data.SQLite; + #else + using SQLiteConnection = Mono.Data.SqliteClient.SqliteConnection; + using SQLiteTransaction = Mono.Data.SqliteClient.SqliteTransaction; + using SQLiteCommand = Mono.Data.SqliteClient.SqliteCommand; + using SQLiteDataReader = Mono.Data.SqliteClient.SqliteDataReader; + using SQLiteParameter = Mono.Data.SqliteClient.SqliteParameter; + #endif + + */ + /// + /// ultra fast cache system for tiles + /// + internal class SQLitePureImageCache : PureImageCache + { +#if !PocketPC +#if !MONO + static SQLitePureImageCache() + { + AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve); + } + + static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) + { + if(args.Name.StartsWith("System.Data.SQLite", StringComparison.OrdinalIgnoreCase)) + { + string rootDir = System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData) + Path.DirectorySeparatorChar + "GMap.NET" + Path.DirectorySeparatorChar; + string dllDir = rootDir + "DllCache" + Path.DirectorySeparatorChar; + string dll = dllDir + "SQLite_v81_NET" + Environment.Version.Major + "_" + (IntPtr.Size == 8 ? "x64" : "x86") + Path.DirectorySeparatorChar + "System.Data.SQLite.DLL"; + if(!File.Exists(dll)) + { + string dir = Path.GetDirectoryName(dll); + if(!Directory.Exists(dir)) + { + Directory.CreateDirectory(dir); + } + + Debug.WriteLine("Saving to DllCache: " + dll); + + if(Environment.Version.Major == 2) + { + using(MemoryStream gzipDll = new MemoryStream((IntPtr.Size == 8 ? Properties.Resources.System_Data_SQLite_x64_NET2_dll : Properties.Resources.System_Data_SQLite_x86_NET2_dll))) + { + using(var gs = new System.IO.Compression.GZipStream(gzipDll, System.IO.Compression.CompressionMode.Decompress)) + { + using(MemoryStream exctDll = new MemoryStream()) + { + byte[] tmp = new byte[1024 * 256]; + int r = 0; + while((r = gs.Read(tmp, 0, tmp.Length)) > 0) + { + exctDll.Write(tmp, 0, r); + } + File.WriteAllBytes(dll, exctDll.ToArray()); + } + } + } + } + else if(Environment.Version.Major == 4) + { + using(MemoryStream gzipDll = new MemoryStream((IntPtr.Size == 8 ? Properties.Resources.System_Data_SQLite_x64_NET4_dll : Properties.Resources.System_Data_SQLite_x86_NET4_dll))) + { + using(var gs = new System.IO.Compression.GZipStream(gzipDll, System.IO.Compression.CompressionMode.Decompress)) + { + using(MemoryStream exctDll = new MemoryStream()) + { + byte[] tmp = new byte[1024 * 256]; + int r = 0; + while((r = gs.Read(tmp, 0, tmp.Length)) > 0) + { + exctDll.Write(tmp, 0, r); + } + File.WriteAllBytes(dll, exctDll.ToArray()); + } + } + } + } + } + + Debug.WriteLine("Assembly.LoadFile: " + dll); + + return System.Reflection.Assembly.LoadFile(dll); + } + return null; + } + + static int ping = 0; + + /// + /// triggers dynamic sqlite loading + /// + public static void Ping() + { + ping++; + } +#endif +#endif + + string cache; + string gtileCache; + string dir; + string db; + bool Created = false; + + public string GtileCache + { + get + { + return gtileCache; + } + } + + /// + /// local cache location + /// + public string CacheLocation + { + get + { + return cache; + } + set + { + cache = value; + + gtileCache = Path.Combine(cache, "TileDBv5") + Path.DirectorySeparatorChar; + + dir = gtileCache + GMapProvider.LanguageStr + Path.DirectorySeparatorChar; + + // precreate dir + if(!Directory.Exists(dir)) + { + Directory.CreateDirectory(dir); + } + +#if !MONO + SQLiteConnection.ClearAllPools(); +#endif + // make empty db + { + db = dir + "Data.gmdb"; + + if(!File.Exists(db)) + { + Created = CreateEmptyDB(db); + } + else + { + Created = AlterDBAddTimeColumn(db); + } + + CheckPreAllocation(); + + //var connBuilder = new SQLiteConnectionStringBuilder(); + //connBuilder.DataSource = "c:\filePath.db"; + //connBuilder.Version = 3; + //connBuilder.PageSize = 4096; + //connBuilder.JournalMode = SQLiteJournalModeEnum.Wal; + //connBuilder.Pooling = true; + //var x = connBuilder.ToString(); + + ConnectionString = string.Format("Data Source=\"{0}\";Page Size=32768;Pooling=True", db); //;Journal Mode=Wal +/* +#if !MONO + ConnectionString = string.Format("Data Source=\"{0}\";Page Size=32768;Pooling=True", db); //;Journal Mode=Wal +#else + ConnectionString = string.Format("Version=3,URI=file://{0},FailIfMissing=True,Page Size=32768,Pooling=True", db); +#endif +*/ + } + + // clear old attachments + AttachedCaches.Clear(); + RebuildFinnalSelect(); + + // attach all databases from main cache location +#if !PocketPC + var dbs = Directory.GetFiles(dir, "*.gmdb", SearchOption.AllDirectories); +#else + var dbs = Directory.GetFiles(dir, "*.gmdb"); +#endif + foreach(var d in dbs) + { + if(d != db) + { + Attach(d); + } + } + } + } + + /// + /// pre-allocate 32MB free space 'ahead' if needed, + /// decreases fragmentation + /// + void CheckPreAllocation() + { + { + byte[] pageSizeBytes = new byte[2]; + byte[] freePagesBytes = new byte[4]; + + lock(this) + { + using(var dbf = File.Open(db, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + dbf.Seek(16, SeekOrigin.Begin); + +#if (!PocketPC && !MONO) + dbf.Lock(16, 2); + dbf.Read(pageSizeBytes, 0, 2); + dbf.Unlock(16, 2); + + dbf.Seek(36, SeekOrigin.Begin); + + dbf.Lock(36, 4); + dbf.Read(freePagesBytes, 0, 4); + dbf.Unlock(36, 4); +#else + dbf.Read(pageSizeBytes, 0, 2); + dbf.Seek(36, SeekOrigin.Begin); + dbf.Read(freePagesBytes, 0, 4); +#endif + + dbf.Close(); + } + } + + if(BitConverter.IsLittleEndian) + { + Array.Reverse(pageSizeBytes); + Array.Reverse(freePagesBytes); + } + UInt16 pageSize = BitConverter.ToUInt16(pageSizeBytes, 0); + UInt32 freePages = BitConverter.ToUInt32(freePagesBytes, 0); + + var freeMB = (pageSize * freePages) / (1024.0 * 1024.0); + +#if !PocketPC + int addSizeMB = 32; + int waitUntilMB = 4; +#else + int addSizeMB = 4; // reduce due to test in emulator + int waitUntilMB = 2; +#endif + + Debug.WriteLine("FreePageSpace in cache: " + freeMB + "MB | " + freePages + " pages"); + + if(freeMB <= waitUntilMB) + { + PreAllocateDB(db, addSizeMB); + } + } + } + + #region -- import / export -- + public static bool CreateEmptyDB(string file) + { + bool ret = true; + + try + { + string dir = Path.GetDirectoryName(file); + if(!Directory.Exists(dir)) + { + Directory.CreateDirectory(dir); + } + + using(SQLiteConnection cn = new SQLiteConnection()) + { + /* + #if !MONO + cn.ConnectionString = string.Format("Data Source=\"{0}\";FailIfMissing=False;Page Size=32768", file); + #else + cn.ConnectionString = string.Format("Version=3,URI=file://{0},FailIfMissing=False,Page Size=32768", file); + #endif + */ + cn.ConnectionString = string.Format("Data Source=\"{0}\";FailIfMissing=False;Page Size=32768", file); + cn.Open(); + { + using(DbTransaction tr = cn.BeginTransaction()) + { + try + { + using(DbCommand cmd = cn.CreateCommand()) + { + cmd.Transaction = tr; +#if !PocketPC + cmd.CommandText = Properties.Resources.CreateTileDb; +#else + cmd.CommandText = GMap.NET.WindowsMobile.Properties.Resources.CreateTileDb; +#endif + cmd.ExecuteNonQuery(); + } + tr.Commit(); + } + catch(Exception exx) + { +#if MONO + Console.WriteLine("CreateEmptyDB: " + exx.ToString()); +#endif + Debug.WriteLine("CreateEmptyDB: " + exx.ToString()); + + tr.Rollback(); + ret = false; + } + } + cn.Close(); + } + } + } + catch(Exception ex) + { +#if MONO + Console.WriteLine("CreateEmptyDB: " + ex.ToString()); +#endif + Debug.WriteLine("CreateEmptyDB: " + ex.ToString()); + ret = false; + } + return ret; + } + + public static bool PreAllocateDB(string file, int addSizeInMBytes) + { + bool ret = true; + + try + { + Debug.WriteLine("PreAllocateDB: " + file + ", +" + addSizeInMBytes + "MB"); + + using(SQLiteConnection cn = new SQLiteConnection()) + { + /* + #if !MONO + cn.ConnectionString = string.Format("Data Source=\"{0}\";FailIfMissing=False;Page Size=32768", file); + #else + cn.ConnectionString = string.Format("Version=3,URI=file://{0},FailIfMissing=False,Page Size=32768", file); + #endif + */ + cn.ConnectionString = string.Format("Data Source=\"{0}\";FailIfMissing=False;Page Size=32768", file); + cn.Open(); + { + using(DbTransaction tr = cn.BeginTransaction()) + { + try + { + using(DbCommand cmd = cn.CreateCommand()) + { + cmd.Transaction = tr; + cmd.CommandText = string.Format("create table large (a); insert into large values (zeroblob({0})); drop table large;", addSizeInMBytes * 1024 * 1024); + cmd.ExecuteNonQuery(); + } + tr.Commit(); + } + catch(Exception exx) + { +#if MONO + Console.WriteLine("PreAllocateDB: " + exx.ToString()); +#endif + Debug.WriteLine("PreAllocateDB: " + exx.ToString()); + + tr.Rollback(); + ret = false; + } + } + cn.Close(); + } + } + } + catch(Exception ex) + { +#if MONO + Console.WriteLine("PreAllocateDB: " + ex.ToString()); +#endif + Debug.WriteLine("PreAllocateDB: " + ex.ToString()); + ret = false; + } + return ret; + } + + private static bool AlterDBAddTimeColumn(string file) + { + bool ret = true; + + try + { + if(File.Exists(file)) + { + using(SQLiteConnection cn = new SQLiteConnection()) + { + /* + #if !MONO + cn.ConnectionString = string.Format("Data Source=\"{0}\";FailIfMissing=False;Page Size=32768;Pooling=True", file); + #else + cn.ConnectionString = string.Format("Version=3,URI=file://{0},FailIfMissing=False,Page Size=32768,Pooling=True", file); + #endif + */ + cn.ConnectionString = string.Format("Data Source=\"{0}\";FailIfMissing=False;Page Size=32768;Pooling=True", file); + cn.Open(); + { + using(DbTransaction tr = cn.BeginTransaction()) + { + bool? NoCacheTimeColumn = null; + + try + { + using(DbCommand cmd = new SQLiteCommand("SELECT CacheTime FROM Tiles", cn)) + { + cmd.Transaction = tr; + + using(DbDataReader rd = cmd.ExecuteReader()) + { + rd.Close(); + } + NoCacheTimeColumn = false; + } + } + catch(Exception ex) + { + if(ex.Message.Contains("no such column: CacheTime")) + { + NoCacheTimeColumn = true; + } + else + { + throw ex; + } + } + + try + { + if(NoCacheTimeColumn.HasValue && NoCacheTimeColumn.Value) + { + using(DbCommand cmd = cn.CreateCommand()) + { + cmd.Transaction = tr; + + cmd.CommandText = "ALTER TABLE Tiles ADD CacheTime DATETIME"; + + cmd.ExecuteNonQuery(); + } + tr.Commit(); + NoCacheTimeColumn = false; + } + } + catch(Exception exx) + { +#if MONO + Console.WriteLine("AlterDBAddTimeColumn: " + exx.ToString()); +#endif + Debug.WriteLine("AlterDBAddTimeColumn: " + exx.ToString()); + + tr.Rollback(); + ret = false; + } + } + cn.Close(); + } + } + } + else + { + ret = false; + } + } + catch(Exception ex) + { +#if MONO + Console.WriteLine("AlterDBAddTimeColumn: " + ex.ToString()); +#endif + Debug.WriteLine("AlterDBAddTimeColumn: " + ex.ToString()); + ret = false; + } + return ret; + } + + public static bool VacuumDb(string file) + { + bool ret = true; + + try + { + using(SQLiteConnection cn = new SQLiteConnection()) + { + /* + #if !MONO + cn.ConnectionString = string.Format("Data Source=\"{0}\";FailIfMissing=True;Page Size=32768", file); + #else + cn.ConnectionString = string.Format("Version=3,URI=file://{0},FailIfMissing=True,Page Size=32768", file); + #endif + */ + cn.ConnectionString = string.Format("Data Source=\"{0}\";FailIfMissing=True;Page Size=32768", file); + cn.Open(); + { + using(DbCommand cmd = cn.CreateCommand()) + { + cmd.CommandText = "vacuum;"; + cmd.ExecuteNonQuery(); + } + cn.Close(); + } + } + } + catch(Exception ex) + { + Debug.WriteLine("VacuumDb: " + ex.ToString()); + ret = false; + } + return ret; + } + + public static bool ExportMapDataToDB(string sourceFile, string destFile) + { + bool ret = true; + + try + { + if(!File.Exists(destFile)) + { + ret = CreateEmptyDB(destFile); + } + + if(ret) + { + using(SQLiteConnection cn1 = new SQLiteConnection()) + { + /* + #if !MONO + cn1.ConnectionString = string.Format("Data Source=\"{0}\";Page Size=32768", sourceFile); + #else + cn1.ConnectionString = string.Format("Version=3,URI=file://{0},FailIfMissing=True,Page Size=32768", sourceFile); + #endif + */ + cn1.ConnectionString = string.Format("Data Source=\"{0}\";Page Size=32768", sourceFile); + cn1.Open(); + if(cn1.State == System.Data.ConnectionState.Open) + { + using(SQLiteConnection cn2 = new SQLiteConnection()) + { + /* + #if !MONO + cn2.ConnectionString = string.Format("Data Source=\"{0}\";Page Size=32768", destFile); + #else + cn2.ConnectionString = string.Format("Version=3,URI=file://{0},FailIfMissing=True,Page Size=32768", destFile); + #endif + */ + cn2.ConnectionString = string.Format("Data Source=\"{0}\";Page Size=32768", destFile); + cn2.Open(); + if(cn2.State == System.Data.ConnectionState.Open) + { + using(SQLiteCommand cmd = new SQLiteCommand(string.Format("ATTACH DATABASE \"{0}\" AS Source", sourceFile), cn2)) + { + cmd.ExecuteNonQuery(); + } + using (SQLiteTransaction tr = cn2.BeginTransaction()) + + /* + #if !MONO + using(SQLiteTransaction tr = cn2.BeginTransaction()) + #else + using(DbTransaction tr = cn2.BeginTransaction()) + #endif + */ + { + try + { + List add = new List(); + using(SQLiteCommand cmd = new SQLiteCommand("SELECT id, X, Y, Zoom, Type FROM Tiles;", cn1)) + { + using(SQLiteDataReader rd = cmd.ExecuteReader()) + { + while(rd.Read()) + { + long id = rd.GetInt64(0); + using(SQLiteCommand cmd2 = new SQLiteCommand(string.Format("SELECT id FROM Tiles WHERE X={0} AND Y={1} AND Zoom={2} AND Type={3};", rd.GetInt32(1), rd.GetInt32(2), rd.GetInt32(3), rd.GetInt32(4)), cn2)) + { + using(SQLiteDataReader rd2 = cmd2.ExecuteReader()) + { + if(!rd2.Read()) + { + add.Add(id); + } + } + } + } + } + } + + foreach(long id in add) + { + using(SQLiteCommand cmd = new SQLiteCommand(string.Format("INSERT INTO Tiles(X, Y, Zoom, Type, CacheTime) SELECT X, Y, Zoom, Type, CacheTime FROM Source.Tiles WHERE id={0}; INSERT INTO TilesData(id, Tile) Values((SELECT last_insert_rowid()), (SELECT Tile FROM Source.TilesData WHERE id={0}));", id), cn2)) + { + cmd.Transaction = tr; + cmd.ExecuteNonQuery(); + } + } + add.Clear(); + + tr.Commit(); + } + catch(Exception exx) + { + Debug.WriteLine("ExportMapDataToDB: " + exx.ToString()); + tr.Rollback(); + ret = false; + } + } + + using(SQLiteCommand cmd = new SQLiteCommand("DETACH DATABASE Source;", cn2)) + { + cmd.ExecuteNonQuery(); + } + } + } + } + } + } + } + catch(Exception ex) + { + Debug.WriteLine("ExportMapDataToDB: " + ex.ToString()); + ret = false; + } + return ret; + } + #endregion + + static readonly string singleSqlSelect = "SELECT Tile FROM main.TilesData WHERE id = (SELECT id FROM main.Tiles WHERE X={0} AND Y={1} AND Zoom={2} AND Type={3})"; + static readonly string singleSqlInsert = "INSERT INTO main.Tiles(X, Y, Zoom, Type, CacheTime) VALUES(@p1, @p2, @p3, @p4, @p5)"; + static readonly string singleSqlInsertLast = "INSERT INTO main.TilesData(id, Tile) VALUES((SELECT last_insert_rowid()), @p1)"; + + string ConnectionString; + + readonly List AttachedCaches = new List(); + string finnalSqlSelect = singleSqlSelect; + string attachSqlQuery = string.Empty; + string detachSqlQuery = string.Empty; + + void RebuildFinnalSelect() + { + finnalSqlSelect = null; + finnalSqlSelect = singleSqlSelect; + + attachSqlQuery = null; + attachSqlQuery = string.Empty; + + detachSqlQuery = null; + detachSqlQuery = string.Empty; + + int i = 1; + foreach(var c in AttachedCaches) + { + finnalSqlSelect += string.Format("\nUNION SELECT Tile FROM db{0}.TilesData WHERE id = (SELECT id FROM db{0}.Tiles WHERE X={{0}} AND Y={{1}} AND Zoom={{2}} AND Type={{3}})", i); + attachSqlQuery += string.Format("\nATTACH '{0}' as db{1};", c, i); + detachSqlQuery += string.Format("\nDETACH DATABASE db{0};", i); + + i++; + } + } + + public void Attach(string db) + { + if(!AttachedCaches.Contains(db)) + { + AttachedCaches.Add(db); + RebuildFinnalSelect(); + } + } + + public void Detach(string db) + { + if(AttachedCaches.Contains(db)) + { + AttachedCaches.Remove(db); + RebuildFinnalSelect(); + } + } + + #region PureImageCache Members + + int preAllocationPing = 0; + + bool PureImageCache.PutImageToCache(byte[] tile, int type, GPoint pos, int zoom) + { + bool ret = true; + if(Created) + { + try + { + using(SQLiteConnection cn = new SQLiteConnection()) + { + cn.ConnectionString = ConnectionString; + cn.Open(); + { + using(DbTransaction tr = cn.BeginTransaction()) + { + try + { + using(DbCommand cmd = cn.CreateCommand()) + { + cmd.Transaction = tr; + cmd.CommandText = singleSqlInsert; + + cmd.Parameters.Add(new SQLiteParameter("@p1", pos.X)); + cmd.Parameters.Add(new SQLiteParameter("@p2", pos.Y)); + cmd.Parameters.Add(new SQLiteParameter("@p3", zoom)); + cmd.Parameters.Add(new SQLiteParameter("@p4", type)); + cmd.Parameters.Add(new SQLiteParameter("@p5", DateTime.Now)); + + cmd.ExecuteNonQuery(); + } + + using(DbCommand cmd = cn.CreateCommand()) + { + cmd.Transaction = tr; + + cmd.CommandText = singleSqlInsertLast; + cmd.Parameters.Add(new SQLiteParameter("@p1", tile)); + + cmd.ExecuteNonQuery(); + } + tr.Commit(); + } + catch(Exception ex) + { +#if MONO + Console.WriteLine("PutImageToCache: " + ex.ToString()); +#endif + Debug.WriteLine("PutImageToCache: " + ex.ToString()); + + tr.Rollback(); + ret = false; + } + } + } + cn.Close(); + } + + if(Interlocked.Increment(ref preAllocationPing) % 22 == 0) + { + CheckPreAllocation(); + } + } + catch(Exception ex) + { +#if MONO + Console.WriteLine("PutImageToCache: " + ex.ToString()); +#endif + Debug.WriteLine("PutImageToCache: " + ex.ToString()); + ret = false; + } + } + return ret; + } + + PureImage PureImageCache.GetImageFromCache(int type, GPoint pos, int zoom) + { + PureImage ret = null; + try + { + using(SQLiteConnection cn = new SQLiteConnection()) + { + cn.ConnectionString = ConnectionString; + cn.Open(); + { + if(!string.IsNullOrEmpty(attachSqlQuery)) + { + using(DbCommand com = cn.CreateCommand()) + { + com.CommandText = attachSqlQuery; + int x = com.ExecuteNonQuery(); + //Debug.WriteLine("Attach: " + x); + } + } + + using(DbCommand com = cn.CreateCommand()) + { + com.CommandText = string.Format(finnalSqlSelect, pos.X, pos.Y, zoom, type); + + using(DbDataReader rd = com.ExecuteReader(System.Data.CommandBehavior.SequentialAccess)) + { + if(rd.Read()) + { + long length = rd.GetBytes(0, 0, null, 0, 0); + byte[] tile = new byte[length]; + rd.GetBytes(0, 0, tile, 0, tile.Length); + { + if(GMapProvider.TileImageProxy != null) + { + ret = GMapProvider.TileImageProxy.FromArray(tile); + } + } + tile = null; + } + rd.Close(); + } + } + + if(!string.IsNullOrEmpty(detachSqlQuery)) + { + using(DbCommand com = cn.CreateCommand()) + { + com.CommandText = detachSqlQuery; + int x = com.ExecuteNonQuery(); + //Debug.WriteLine("Detach: " + x); + } + } + } + cn.Close(); + } + } + catch(Exception ex) + { +#if MONO + Console.WriteLine("GetImageFromCache: " + ex.ToString()); +#endif + Debug.WriteLine("GetImageFromCache: " + ex.ToString()); + ret = null; + } + + return ret; + } + + int PureImageCache.DeleteOlderThan(DateTime date, int? type) + { + int affectedRows = 0; + + try + { + using(SQLiteConnection cn = new SQLiteConnection()) + { + cn.ConnectionString = ConnectionString; + cn.Open(); + { + using(DbCommand com = cn.CreateCommand()) + { + com.CommandText = string.Format("DELETE FROM Tiles WHERE CacheTime is not NULL and CacheTime < datetime('{0}')", date.ToString("s")); + if(type.HasValue) + { + com.CommandText += " and Type = " + type; + } + affectedRows = com.ExecuteNonQuery(); + } + } + } + } + catch(Exception ex) + { +#if MONO + Console.WriteLine("DeleteOlderThan: " + ex); +#endif + Debug.WriteLine("DeleteOlderThan: " + ex); + } + + return affectedRows; + } + + #endregion + } +#endif +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.Core.csproj b/GreatMaps/GMap.NET.Core/GMap.NET.Core.csproj new file mode 100644 index 0000000..7e83420 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.Core.csproj @@ -0,0 +1,322 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {D0C39D9D-BED0-418B-9A5E-713176CAF40C} + Library + Properties + GMap.NET + GMap.NET.Core + v4.0 + 512 + true + sn.snk + + + 3.5 + + false + + + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + true + + + + + true + bin\Debug\ + TRACE;DEBUG;MONO;MySQL_disabled; PostgreSQL_disabled; SQLite + full + AnyCPU + bin\x86\Debug\GMap.NET.Core.dll.CodeAnalysisLog.xml + true + GlobalSuppressions.cs + Off + prompt + MinimumRecommendedRules.ruleset + ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets + true + ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules + true + + + bin\Release\ + TRACE;MONO_disabled; MySQL_disabled; PostgreSQL_disabled; SQLite + bin\x86\Release\GMap.NET.Core.XML + true + AnyCPU + bin\x86\Release\GMap.NET.Core.dll.CodeAnalysisLog.xml + true + GlobalSuppressions.cs + Off + prompt + MinimumRecommendedRules.ruleset + ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets + false + ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules + false + + + true + bin\x86\Debug\ + TRACE;DEBUG;MySQL_disabled; PostgreSQL_disabled; SQLite;MONO + full + x86 + bin\x86\Debug\GMap.NET.Core.dll.CodeAnalysisLog.xml + true + GlobalSuppressions.cs + Off + prompt + MinimumRecommendedRules.ruleset + ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets + true + ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules + true + + + bin\x86\Release\ + TRACE;MONO_disabled; MySQL_disabled; PostgreSQL_disabled; SQLite + bin\x86\Release\GMap.NET.Core.XML + true + x86 + bin\x86\Release\GMap.NET.Core.dll.CodeAnalysisLog.xml + true + GlobalSuppressions.cs + Off + prompt + MinimumRecommendedRules.ruleset + ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets + false + ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules + + + + False + ..\References\Windows\MySql.Data.dll + True + + + ..\..\packages\Newtonsoft.Json.12.0.1\lib\net40\Newtonsoft.Json.dll + True + + + False + ..\References\Windows\Npgsql\Npgsql.dll + + + + + + ..\..\packages\System.Data.SQLite.Core.1.0.110.0\lib\net40\System.Data.SQLite.dll + True + + + False + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Code + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Resources.resx + True + True + + + + + + + + + + + + + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + + + + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + + + + + + + Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}". + + + + + \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.Internals/Cache.cs b/GreatMaps/GMap.NET.Core/GMap.NET.Internals/Cache.cs new file mode 100644 index 0000000..77d9d5c --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.Internals/Cache.cs @@ -0,0 +1,298 @@ + +namespace GMap.NET.Internals +{ + using System; + using System.Diagnostics; + using System.IO; + using System.Text; + using GMap.NET.CacheProviders; + + internal class CacheLocator + { + private static string location; + public static string Location + { + get + { + if(string.IsNullOrEmpty(location)) + { + Reset(); + } + + return location; + } + set + { + if(string.IsNullOrEmpty(value)) // setting to null resets to default + { + Reset(); + } + else + { + location = value; + } + + if(Delay) + { + Cache.Instance.CacheLocation = location; + } + } + } + + static void Reset() + { +#if !PocketPC + location = System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData) + Path.DirectorySeparatorChar + "GMap.NET" + Path.DirectorySeparatorChar; + + // http://greatmaps.codeplex.com/discussions/403151 + if(string.IsNullOrEmpty(location)) + { + GMaps.Instance.Mode = AccessMode.ServerOnly; + GMaps.Instance.UseDirectionsCache = false; + GMaps.Instance.UseGeocoderCache = false; + GMaps.Instance.UsePlacemarkCache = false; + GMaps.Instance.UseRouteCache = false; + GMaps.Instance.UseUrlCache = false; + } +#else + location = System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData) + Path.DirectorySeparatorChar + "GMap.NET" + Path.DirectorySeparatorChar; +#endif + } + + public static bool Delay = false; + } + + /// + /// cache system for tiles, geocoding, etc... + /// + internal class Cache : Singleton + { + /// + /// abstract image cache + /// + public PureImageCache ImageCache; + + /// + /// second level abstract image cache + /// + public PureImageCache ImageCacheSecond; + + string cache; + + /// + /// local cache location + /// + public string CacheLocation + { + get + { + return cache; + } + set + { + cache = value; +#if SQLite + if(ImageCache is SQLitePureImageCache) + { + (ImageCache as SQLitePureImageCache).CacheLocation = value; + } +#else + if(ImageCache is MsSQLCePureImageCache) + { + (ImageCache as MsSQLCePureImageCache).CacheLocation = value; + } +#endif + CacheLocator.Delay = true; + } + } + + public Cache() + { + #region singleton check + if(Instance != null) + { + throw (new System.Exception("You have tried to create a new singleton class where you should have instanced it. Replace your \"new class()\" with \"class.Instance\"")); + } + #endregion + +#if SQLite + ImageCache = new SQLitePureImageCache(); +#else + // you can use $ms stuff if you like too ;} + ImageCache = new MsSQLCePureImageCache(); +#endif + +#if PocketPC + // use sd card if exist for cache + string sd = Native.GetRemovableStorageDirectory(); + if(!string.IsNullOrEmpty(sd)) + { + CacheLocation = sd + Path.DirectorySeparatorChar + "GMap.NET" + Path.DirectorySeparatorChar; + } + else +#endif + { +#if PocketPC + CacheLocation = CacheLocator.Location; +#else + string newCache = CacheLocator.Location; + + string oldCache = System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData) + Path.DirectorySeparatorChar + "GMap.NET" + Path.DirectorySeparatorChar; + + // move database to non-roaming user directory + if(Directory.Exists(oldCache)) + { + try + { + if(Directory.Exists(newCache)) + { + Directory.Delete(oldCache, true); + } + else + { + Directory.Move(oldCache, newCache); + } + CacheLocation = newCache; + } + catch(Exception ex) + { + CacheLocation = oldCache; + Trace.WriteLine("SQLitePureImageCache, moving data: " + ex.ToString()); + } + } + else + { + CacheLocation = newCache; + } +#endif + } + } + + #region -- etc cache -- + + public void SaveContent(string urlEnd, CacheType type, string content) + { + try + { + Stuff.RemoveInvalidPathSymbols(ref urlEnd); + + string dir = Path.Combine(cache, type.ToString()) + Path.DirectorySeparatorChar; + + // precrete dir + if(!Directory.Exists(dir)) + { + Directory.CreateDirectory(dir); + } + + string file = dir + urlEnd; + + switch(type) + { + case CacheType.GeocoderCache: + file += ".geo"; + break; + + case CacheType.PlacemarkCache: + file += ".plc"; + break; + + case CacheType.RouteCache: + file += ".dragdir"; + break; + + case CacheType.UrlCache: + file += ".url"; + break; + + case CacheType.DirectionsCache: + file += ".dir"; + break; + + default: + file += ".txt"; + break; + } + + using(StreamWriter writer = new StreamWriter(file, false, Encoding.UTF8)) + { + writer.Write(content); + } + } + catch(Exception ex) + { + Debug.WriteLine("SaveContent: " + ex); + } + } + + public string GetContent(string urlEnd, CacheType type, TimeSpan stayInCache) + { + string ret = null; + + try + { + Stuff.RemoveInvalidPathSymbols(ref urlEnd); + + string dir = Path.Combine(cache, type.ToString()) + Path.DirectorySeparatorChar; + string file = dir + urlEnd; + + switch(type) + { + case CacheType.GeocoderCache: + file += ".geo"; + break; + + case CacheType.PlacemarkCache: + file += ".plc"; + break; + + case CacheType.RouteCache: + file += ".dragdir"; + break; + + case CacheType.UrlCache: + file += ".url"; + break; + + default: + file += ".txt"; + break; + } + + if(File.Exists(file)) + { + var writeTime = File.GetLastWriteTime(file); + if(DateTime.Now - writeTime < stayInCache) + { + using(StreamReader r = new StreamReader(file, Encoding.UTF8)) + { + ret = r.ReadToEnd(); + } + } + } + } + catch(Exception ex) + { + ret = null; + Debug.WriteLine("GetContent: " + ex); + } + + return ret; + } + + public string GetContent(string urlEnd, CacheType type) + { + return GetContent(urlEnd, type, TimeSpan.FromDays(88)); + } + + #endregion + } + + internal enum CacheType + { + GeocoderCache, + PlacemarkCache, + RouteCache, + UrlCache, + DirectionsCache, + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.Internals/CacheQueueItem.cs b/GreatMaps/GMap.NET.Core/GMap.NET.Internals/CacheQueueItem.cs new file mode 100644 index 0000000..6a17b1b --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.Internals/CacheQueueItem.cs @@ -0,0 +1,40 @@ + +namespace GMap.NET.Internals +{ + using System.IO; + using System; + + /// + /// cache queue item + /// + internal struct CacheQueueItem + { + public RawTile Tile; + public byte[] Img; + public CacheUsage CacheType; + + public CacheQueueItem(RawTile tile, byte[] Img, CacheUsage cacheType) + { + this.Tile = tile; + this.Img = Img; + this.CacheType = cacheType; + } + + public override string ToString() + { + return Tile + ", CacheType:" + CacheType; + } + + public void Clear() + { + Img = null; + } + } + + internal enum CacheUsage + { + First = 2, + Second = 4, + Both = First | Second + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.Internals/Core.cs b/GreatMaps/GMap.NET.Core/GMap.NET.Internals/Core.cs new file mode 100644 index 0000000..43cd7bd --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.Internals/Core.cs @@ -0,0 +1,1313 @@ + +namespace GMap.NET.Internals +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Threading; + using GMap.NET.Projections; + using System.IO; + using GMap.NET.MapProviders; + using System.ComponentModel; + +#if PocketPC + using OpenNETCF.ComponentModel; + using OpenNETCF.Threading; + using Thread=OpenNETCF.Threading.Thread2; +#endif + + /// + /// internal map control core + /// + internal class Core : IDisposable + { + public PointLatLng position; + public GPoint positionPixel; + + public GPoint renderOffset; + public GPoint centerTileXYLocation; + public GPoint centerTileXYLocationLast; + public GPoint dragPoint; + public GPoint compensationOffset; + + public GPoint mouseDown; + public GPoint mouseCurrent; + public GPoint mouseLastZoom; + + public MouseWheelZoomType MouseWheelZoomType = MouseWheelZoomType.MousePositionAndCenter; + + public PointLatLng? LastLocationInBounds = null; + public bool VirtualSizeEnabled = false; + + public GSize sizeOfMapArea; + public GSize minOfTiles; + public GSize maxOfTiles; + + public GRect tileRect; + public GRect tileRectBearing; + //public GRect currentRegion; + public float bearing = 0; + public bool IsRotated = false; + + public bool fillEmptyTiles = true; + + public TileMatrix Matrix = new TileMatrix(); + + public List tileDrawingList = new List(); + public FastReaderWriterLock tileDrawingListLock = new FastReaderWriterLock(); + + public readonly Stack tileLoadQueue = new Stack(); + +#if !PocketPC + static readonly int GThreadPoolSize = 5; +#else + static readonly int GThreadPoolSize = 2; +#endif + + DateTime LastTileLoadStart = DateTime.Now; + DateTime LastTileLoadEnd = DateTime.Now; + internal volatile bool IsStarted = false; + int zoom; + internal int maxZoom = 2; + internal int minZoom = 2; + internal int Width; + internal int Height; + + internal int pxRes100m; // 100 meters + internal int pxRes1000m; // 1km + internal int pxRes10km; // 10km + internal int pxRes100km; // 100km + internal int pxRes1000km; // 1000km + internal int pxRes5000km; // 5000km + + /// + /// is user dragging map + /// + public bool IsDragging = false; + + public Core() + { + Provider = EmptyProvider.Instance; + } + + /// + /// map zoom + /// + public int Zoom + { + get + { + return zoom; + } + set + { + if(zoom != value && !IsDragging) + { + zoom = value; + + minOfTiles = Provider.Projection.GetTileMatrixMinXY(value); + maxOfTiles = Provider.Projection.GetTileMatrixMaxXY(value); + + positionPixel = Provider.Projection.FromLatLngToPixel(Position, value); + + if(IsStarted) + { + Monitor.Enter(tileLoadQueue); + try + { + tileLoadQueue.Clear(); + } + finally + { + Monitor.Exit(tileLoadQueue); + } + + Matrix.ClearLevelsBelove(zoom - LevelsKeepInMemmory); + Matrix.ClearLevelsAbove(zoom + LevelsKeepInMemmory); + + lock(FailedLoads) + { + FailedLoads.Clear(); + RaiseEmptyTileError = true; + } + + GoToCurrentPositionOnZoom(); + UpdateBounds(); + + if(OnMapZoomChanged != null) + { + OnMapZoomChanged(); + } + } + } + } + } + + /// + /// current marker position in pixel coordinates + /// + public GPoint PositionPixel + { + get + { + return positionPixel; + } + } + + /// + /// current marker position + /// + public PointLatLng Position + { + get + { + + return position; + } + set + { + position = value; + positionPixel = Provider.Projection.FromLatLngToPixel(value, Zoom); + + if(IsStarted) + { + if(!IsDragging) + { + GoToCurrentPosition(); + } + + if(OnCurrentPositionChanged != null) + OnCurrentPositionChanged(position); + } + } + } + + public GMapProvider provider; + public GMapProvider Provider + { + get + { + return provider; + } + set + { + if(provider == null || !provider.Equals(value)) + { + provider = value; + + if(!provider.IsInitialized) + { + provider.IsInitialized = true; + provider.OnInitialized(); + } + + if(Provider.Projection != null) + { + tileRect = new GRect(GPoint.Empty, Provider.Projection.TileSize); + tileRectBearing = tileRect; + if(IsRotated) + { + tileRectBearing.Inflate(1, 1); + } + + minOfTiles = Provider.Projection.GetTileMatrixMinXY(Zoom); + maxOfTiles = Provider.Projection.GetTileMatrixMaxXY(Zoom); + positionPixel = Provider.Projection.FromLatLngToPixel(Position, Zoom); + } + + if(IsStarted) + { + CancelAsyncTasks(); + OnMapSizeChanged(Width, Height); + ReloadMap(); + + if(minZoom < provider.MinZoom) + { + minZoom = provider.MinZoom; + } + + //if(provider.MaxZoom.HasValue && maxZoom > provider.MaxZoom) + //{ + // maxZoom = provider.MaxZoom.Value; + //} + + zoomToArea = true; + + if(provider.Area.HasValue && !provider.Area.Value.Contains(Position)) + { + SetZoomToFitRect(provider.Area.Value); + zoomToArea = false; + } + + if(OnMapTypeChanged != null) + { + OnMapTypeChanged(value); + } + } + } + } + } + + internal bool zoomToArea = true; + + /// + /// sets zoom to max to fit rect + /// + /// + /// + public bool SetZoomToFitRect(RectLatLng rect) + { + int mmaxZoom = GetMaxZoomToFitRect(rect); + if(mmaxZoom > 0) + { + PointLatLng center = new PointLatLng(rect.Lat - (rect.HeightLat / 2), rect.Lng + (rect.WidthLng / 2)); + Position = center; + + if(mmaxZoom > maxZoom) + { + mmaxZoom = maxZoom; + } + + if(Zoom != mmaxZoom) + { + Zoom = (int)mmaxZoom; + } + + return true; + } + return false; + } + + /// + /// is polygons enabled + /// + public bool PolygonsEnabled = true; + + /// + /// is routes enabled + /// + public bool RoutesEnabled = true; + + /// + /// is markers enabled + /// + public bool MarkersEnabled = true; + + /// + /// can user drag map + /// + public bool CanDragMap = true; + + /// + /// retry count to get tile + /// +#if !PocketPC + public int RetryLoadTile = 0; +#else + public int RetryLoadTile = 1; +#endif + + /// + /// how many levels of tiles are staying decompresed in memory + /// +#if !PocketPC + public int LevelsKeepInMemmory = 5; +#else + public int LevelsKeepInMemmory = 1; +#endif + + /// + /// map render mode + /// + public RenderMode RenderMode = RenderMode.GDI_PLUS; + + /// + /// occurs when current position is changed + /// + public event PositionChanged OnCurrentPositionChanged; + + /// + /// occurs when tile set load is complete + /// + public event TileLoadComplete OnTileLoadComplete; + + /// + /// occurs when tile set is starting to load + /// + public event TileLoadStart OnTileLoadStart; + + /// + /// occurs on empty tile displayed + /// + public event EmptyTileError OnEmptyTileError; + + /// + /// occurs on map drag + /// + public event MapDrag OnMapDrag; + + /// + /// occurs on map zoom changed + /// + public event MapZoomChanged OnMapZoomChanged; + + /// + /// occurs on map type changed + /// + public event MapTypeChanged OnMapTypeChanged; + + readonly List GThreadPool = new List(); + // ^ + // should be only one pool for multiply controls, any ideas how to fix? + //static readonly List GThreadPool = new List(); + + // windows forms or wpf + internal string SystemType; + + internal static int instances = 0; + + BackgroundWorker invalidator; + + public BackgroundWorker OnMapOpen() + { + if(!IsStarted) + { + int x = Interlocked.Increment(ref instances); + Debug.WriteLine("OnMapOpen: " + x); + + IsStarted = true; + + if(x == 1) + { + GMaps.Instance.noMapInstances = false; + } + + GoToCurrentPosition(); + + invalidator = new BackgroundWorker(); + invalidator.WorkerSupportsCancellation = true; + invalidator.WorkerReportsProgress = true; + invalidator.DoWork += new DoWorkEventHandler(invalidatorWatch); + invalidator.RunWorkerAsync(); + + //if(x == 1) + //{ + // first control shown + //} + } + return invalidator; + } + + public void OnMapClose() + { + Dispose(); + } + + internal readonly object invalidationLock = new object(); + internal DateTime lastInvalidation = DateTime.Now; + + void invalidatorWatch(object sender, DoWorkEventArgs e) + { + var w = sender as BackgroundWorker; + + TimeSpan span = TimeSpan.FromMilliseconds(111); + int spanMs = (int)span.TotalMilliseconds; + bool skiped = false; + TimeSpan delta; + DateTime now = DateTime.Now; + + while(Refresh != null && (!skiped && Refresh.WaitOne() || (Refresh.WaitOne(spanMs, false) || true))) + { + if(w.CancellationPending) + break; + + now = DateTime.Now; + lock(invalidationLock) + { + delta = now - lastInvalidation; + } + + if(delta > span) + { + lock(invalidationLock) + { + lastInvalidation = now; + } + skiped = false; + + w.ReportProgress(1); + // Debug.WriteLine("Invalidate delta: " + (int)delta.TotalMilliseconds + "ms"); + } + else + { + skiped = true; + } + } + } + + public void UpdateCenterTileXYLocation() + { + PointLatLng center = FromLocalToLatLng(Width / 2, Height / 2); + GPoint centerPixel = Provider.Projection.FromLatLngToPixel(center, Zoom); + centerTileXYLocation = Provider.Projection.FromPixelToTileXY(centerPixel); + } + + public int vWidth = 800; + public int vHeight = 400; + + public void OnMapSizeChanged(int width, int height) + { + this.Width = width; + this.Height = height; + + if(IsRotated) + { +#if !PocketPC + int diag = (int)Math.Round(Math.Sqrt(Width * Width + Height * Height) / Provider.Projection.TileSize.Width, MidpointRounding.AwayFromZero); +#else + int diag = (int) Math.Round(Math.Sqrt(Width * Width + Height * Height) / Provider.Projection.TileSize.Width); +#endif + sizeOfMapArea.Width = 1 + (diag / 2); + sizeOfMapArea.Height = 1 + (diag / 2); + } + else + { + sizeOfMapArea.Width = 1 + (Width / Provider.Projection.TileSize.Width) / 2; + sizeOfMapArea.Height = 1 + (Height / Provider.Projection.TileSize.Height) / 2; + } + + Debug.WriteLine("OnMapSizeChanged, w: " + width + ", h: " + height + ", size: " + sizeOfMapArea); + + if(IsStarted) + { + UpdateBounds(); + GoToCurrentPosition(); + } + } + + /// + /// gets current map view top/left coordinate, width in Lng, height in Lat + /// + /// + public RectLatLng ViewArea + { + get + { + if(Provider.Projection != null) + { + var p = FromLocalToLatLng(0, 0); + var p2 = FromLocalToLatLng(Width, Height); + + return RectLatLng.FromLTRB(p.Lng, p.Lat, p2.Lng, p2.Lat); + } + return RectLatLng.Empty; + } + } + + /// + /// gets lat/lng from local control coordinates + /// + /// + /// + /// + public PointLatLng FromLocalToLatLng(long x, long y) + { + GPoint p = new GPoint(x, y); + p.OffsetNegative(renderOffset); + p.Offset(compensationOffset); + + return Provider.Projection.FromPixelToLatLng(p, Zoom); + } + + /// + /// return local coordinates from lat/lng + /// + /// + /// + public GPoint FromLatLngToLocal(PointLatLng latlng) + { + GPoint pLocal = Provider.Projection.FromLatLngToPixel(latlng, Zoom); + pLocal.Offset(renderOffset); + pLocal.OffsetNegative(compensationOffset); + return pLocal; + } + + /// + /// gets max zoom level to fit rectangle + /// + /// + /// + public int GetMaxZoomToFitRect(RectLatLng rect) + { + int zoom = minZoom; + + if(rect.HeightLat == 0 || rect.WidthLng == 0) + { + zoom = maxZoom / 2; + } + else + { + for(int i = (int)zoom; i <= maxZoom; i++) + { + GPoint p1 = Provider.Projection.FromLatLngToPixel(rect.LocationTopLeft, i); + GPoint p2 = Provider.Projection.FromLatLngToPixel(rect.LocationRightBottom, i); + + if(((p2.X - p1.X) <= Width + 10) && (p2.Y - p1.Y) <= Height + 10) + { + zoom = i; + } + else + { + break; + } + } + } + + return zoom; + } + + /// + /// initiates map dragging + /// + /// + public void BeginDrag(GPoint pt) + { + dragPoint.X = pt.X - renderOffset.X; + dragPoint.Y = pt.Y - renderOffset.Y; + IsDragging = true; + } + + /// + /// ends map dragging + /// + public void EndDrag() + { + IsDragging = false; + mouseDown = GPoint.Empty; + + Refresh.Set(); + } + + /// + /// reloads map + /// + public void ReloadMap() + { + if(IsStarted) + { + Debug.WriteLine("------------------"); + + okZoom = 0; + skipOverZoom = 0; + + Monitor.Enter(tileLoadQueue); + try + { + tileLoadQueue.Clear(); + } + finally + { + Monitor.Exit(tileLoadQueue); + } + + Matrix.ClearAllLevels(); + + lock(FailedLoads) + { + FailedLoads.Clear(); + RaiseEmptyTileError = true; + } + + Refresh.Set(); + + UpdateBounds(); + } + else + { + throw new Exception("Please, do not call ReloadMap before form is loaded, it's useless"); + } + } + + /// + /// moves current position into map center + /// + public void GoToCurrentPosition() + { + compensationOffset = positionPixel; // TODO: fix + + // reset stuff + renderOffset = GPoint.Empty; + dragPoint = GPoint.Empty; + + //var dd = new GPoint(-(CurrentPositionGPixel.X - Width / 2), -(CurrentPositionGPixel.Y - Height / 2)); + //dd.Offset(compensationOffset); + + var d = new GPoint(Width / 2, Height / 2); + + this.Drag(d); + } + + public bool MouseWheelZooming = false; + + /// + /// moves current position into map center + /// + internal void GoToCurrentPositionOnZoom() + { + compensationOffset = positionPixel; // TODO: fix + + // reset stuff + renderOffset = GPoint.Empty; + dragPoint = GPoint.Empty; + + // goto location and centering + if(MouseWheelZooming) + { + if(MouseWheelZoomType != MouseWheelZoomType.MousePositionWithoutCenter) + { + GPoint pt = new GPoint(-(positionPixel.X - Width / 2), -(positionPixel.Y - Height / 2)); + pt.Offset(compensationOffset); + renderOffset.X = pt.X - dragPoint.X; + renderOffset.Y = pt.Y - dragPoint.Y; + } + else // without centering + { + renderOffset.X = -positionPixel.X - dragPoint.X; + renderOffset.Y = -positionPixel.Y - dragPoint.Y; + renderOffset.Offset(mouseLastZoom); + renderOffset.Offset(compensationOffset); + } + } + else // use current map center + { + mouseLastZoom = GPoint.Empty; + + GPoint pt = new GPoint(-(positionPixel.X - Width / 2), -(positionPixel.Y - Height / 2)); + pt.Offset(compensationOffset); + renderOffset.X = pt.X - dragPoint.X; + renderOffset.Y = pt.Y - dragPoint.Y; + } + + UpdateCenterTileXYLocation(); + } + + /// + /// darg map by offset in pixels + /// + /// + public void DragOffset(GPoint offset) + { + renderOffset.Offset(offset); + + UpdateCenterTileXYLocation(); + + if(centerTileXYLocation != centerTileXYLocationLast) + { + centerTileXYLocationLast = centerTileXYLocation; + UpdateBounds(); + } + + { + LastLocationInBounds = Position; + + IsDragging = true; + Position = FromLocalToLatLng((int)Width / 2, (int)Height / 2); + IsDragging = false; + } + + if(OnMapDrag != null) + { + OnMapDrag(); + } + } + + /// + /// drag map + /// + /// + public void Drag(GPoint pt) + { + renderOffset.X = pt.X - dragPoint.X; + renderOffset.Y = pt.Y - dragPoint.Y; + + UpdateCenterTileXYLocation(); + + if(centerTileXYLocation != centerTileXYLocationLast) + { + centerTileXYLocationLast = centerTileXYLocation; + UpdateBounds(); + } + + if(IsDragging) + { + LastLocationInBounds = Position; + Position = FromLocalToLatLng((int)Width / 2, (int)Height / 2); + + if(OnMapDrag != null) + { + OnMapDrag(); + } + } + } + + /// + /// cancels tile loaders and bounds checker + /// + public void CancelAsyncTasks() + { + if(IsStarted) + { + Monitor.Enter(tileLoadQueue); + try + { + tileLoadQueue.Clear(); + } + finally + { + Monitor.Exit(tileLoadQueue); + } + } + } + + bool RaiseEmptyTileError = false; + internal Dictionary FailedLoads = new Dictionary(); + + internal static readonly int WaitForTileLoadThreadTimeout = 5 * 1000 * 60; // 5 min. + + byte loadWaitCount = 0; + volatile int okZoom = 0; + volatile int skipOverZoom = 0; + + // tile consumer thread + void ProcessLoadTask() + { + LoadTask? task = null; + long lastTileLoadTimeMs; + bool stop = false; + +#if !PocketPC + Thread ct = Thread.CurrentThread; + string ctid = "Thread[" + ct.ManagedThreadId + "]"; +#else + int ctid = 0; +#endif + while(!stop && IsStarted) + { + task = null; + + Monitor.Enter(tileLoadQueue); + try + { + while(tileLoadQueue.Count == 0) + { + Debug.WriteLine(ctid + " - Wait " + loadWaitCount + " - " + DateTime.Now.TimeOfDay); + + if(++loadWaitCount >= GThreadPoolSize) + { + loadWaitCount = 0; + + #region -- last thread takes action -- + + { + LastTileLoadEnd = DateTime.Now; + lastTileLoadTimeMs = (long)(LastTileLoadEnd - LastTileLoadStart).TotalMilliseconds; + } + + #region -- clear stuff-- + if(IsStarted) + { + GMaps.Instance.MemoryCache.RemoveOverload(); + + tileDrawingListLock.AcquireReaderLock(); + try + { + Matrix.ClearLevelAndPointsNotIn(Zoom, tileDrawingList); + } + finally + { + tileDrawingListLock.ReleaseReaderLock(); + } + } + #endregion + + UpdateGroundResolution(); +#if UseGC + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); +#endif + + Debug.WriteLine(ctid + " - OnTileLoadComplete: " + lastTileLoadTimeMs + "ms, MemoryCacheSize: " + GMaps.Instance.MemoryCache.Size + "MB"); + + if(OnTileLoadComplete != null) + { + OnTileLoadComplete(lastTileLoadTimeMs); + } + #endregion + } + + if(!IsStarted || false == Monitor.Wait(tileLoadQueue, WaitForTileLoadThreadTimeout, false) || !IsStarted) + { + stop = true; + break; + } + } + + if(IsStarted && !stop || tileLoadQueue.Count > 0) + { + task = tileLoadQueue.Pop(); + } + } + finally + { + Monitor.Exit(tileLoadQueue); + } + + if(task.HasValue && IsStarted) + { + try + { + #region -- execute -- + + var m = Matrix.GetTileWithReadLock(task.Value.Zoom, task.Value.Pos); + if(!m.NotEmpty) + { + Debug.WriteLine(ctid + " - try load: " + task); + + Tile t = new Tile(task.Value.Zoom, task.Value.Pos); + + foreach(var tl in provider.Overlays) + { + int retry = 0; + do + { + PureImage img = null; + Exception ex = null; + + if(!provider.MaxZoom.HasValue || task.Value.Zoom <= provider.MaxZoom) + { + if(skipOverZoom == 0 || task.Value.Zoom <= skipOverZoom) + { + // tile number inversion(BottomLeft -> TopLeft) + if(tl.InvertedAxisY) + { + img = GMaps.Instance.GetImageFrom(tl, new GPoint(task.Value.Pos.X, maxOfTiles.Height - task.Value.Pos.Y), task.Value.Zoom, out ex); + } + else // ok + { + img = GMaps.Instance.GetImageFrom(tl, task.Value.Pos, task.Value.Zoom, out ex); + } + } + } + + if(img != null && ex == null) + { + if(okZoom < task.Value.Zoom) + { + okZoom = task.Value.Zoom; + skipOverZoom = 0; + Debug.WriteLine("skipOverZoom disabled, okZoom: " + okZoom); + } + } + else if(ex != null) + { + if(skipOverZoom != okZoom) + { + if(ex.Message.Contains("(404) Not Found")) + { + skipOverZoom = okZoom; + Debug.WriteLine("skipOverZoom enabled: " + skipOverZoom); + } + } + } + + // check for parent tiles if not found + if(img == null && okZoom > 0 && fillEmptyTiles && Provider.Projection is MercatorProjection) + { + int zoomOffset = task.Value.Zoom > okZoom ? task.Value.Zoom - okZoom : 1; + long Ix = 0; + GPoint parentTile = GPoint.Empty; + + while(img == null && zoomOffset < task.Value.Zoom) + { + Ix = (long)Math.Pow(2, zoomOffset); + parentTile = new GMap.NET.GPoint((task.Value.Pos.X / Ix), (task.Value.Pos.Y / Ix)); + img = GMaps.Instance.GetImageFrom(tl, parentTile, task.Value.Zoom - zoomOffset++, out ex); + } + + if(img != null) + { + // offsets in quadrant + long Xoff = Math.Abs(task.Value.Pos.X - (parentTile.X * Ix)); + long Yoff = Math.Abs(task.Value.Pos.Y - (parentTile.Y * Ix)); + + img.IsParent = true; + img.Ix = Ix; + img.Xoff = Xoff; + img.Yoff = Yoff; + + // wpf + //var geometry = new RectangleGeometry(new Rect(Core.tileRect.X + 0.6, Core.tileRect.Y + 0.6, Core.tileRect.Width + 0.6, Core.tileRect.Height + 0.6)); + //var parentImgRect = new Rect(Core.tileRect.X - Core.tileRect.Width * Xoff + 0.6, Core.tileRect.Y - Core.tileRect.Height * Yoff + 0.6, Core.tileRect.Width * Ix + 0.6, Core.tileRect.Height * Ix + 0.6); + + // gdi+ + //System.Drawing.Rectangle dst = new System.Drawing.Rectangle((int)Core.tileRect.X, (int)Core.tileRect.Y, (int)Core.tileRect.Width, (int)Core.tileRect.Height); + //System.Drawing.RectangleF srcRect = new System.Drawing.RectangleF((float)(Xoff * (img.Img.Width / Ix)), (float)(Yoff * (img.Img.Height / Ix)), (img.Img.Width / Ix), (img.Img.Height / Ix)); + } + } + + if(img != null) + { + Debug.WriteLine(ctid + " - tile loaded: " + img.Data.Length / 1024 + "KB, " + task); + { + t.AddOverlay(img); + } + break; + } + else + { + if(ex != null) + { + lock(FailedLoads) + { + if(!FailedLoads.ContainsKey(task.Value)) + { + FailedLoads.Add(task.Value, ex); + + if(OnEmptyTileError != null) + { + if(!RaiseEmptyTileError) + { + RaiseEmptyTileError = true; + OnEmptyTileError(task.Value.Zoom, task.Value.Pos); + } + } + } + } + } + + if(RetryLoadTile > 0) + { + Debug.WriteLine(ctid + " - ProcessLoadTask: " + task + " -> empty tile, retry " + retry); + { + Thread.Sleep(1111); + } + } + } + } + while(++retry < RetryLoadTile); + } + + if(t.HasAnyOverlays && IsStarted) + { + Matrix.SetTile(t); + } + else + { + t.Dispose(); + } + } + + #endregion + } + catch(Exception ex) + { + Debug.WriteLine(ctid + " - ProcessLoadTask: " + ex.ToString()); + } + finally + { + if(Refresh != null) + { + Refresh.Set(); + } + } + } + } + +#if !PocketPC + Monitor.Enter(tileLoadQueue); + try + { + Debug.WriteLine("Quit - " + ct.Name); + lock(GThreadPool) + { + GThreadPool.Remove(ct); + } + } + finally + { + Monitor.Exit(tileLoadQueue); + } +#endif + } + + public AutoResetEvent Refresh = new AutoResetEvent(false); + + public bool updatingBounds = false; + + /// + /// updates map bounds + /// + void UpdateBounds() + { + if(!IsStarted || Provider.Equals(EmptyProvider.Instance)) + { + return; + } + + updatingBounds = true; + + tileDrawingListLock.AcquireWriterLock(); + try + { + #region -- find tiles around -- + tileDrawingList.Clear(); + + for(long i = -sizeOfMapArea.Width; i <= sizeOfMapArea.Width; i++) + { + for(long j = -sizeOfMapArea.Height; j <= sizeOfMapArea.Height; j++) + { + GPoint p = centerTileXYLocation; + p.X += i; + p.Y += j; + +#if ContinuesMap + // ---------------------------- + if(p.X < minOfTiles.Width) + { + p.X += (maxOfTiles.Width + 1); + } + + if(p.X > maxOfTiles.Width) + { + p.X -= (maxOfTiles.Width + 1); + } + // ---------------------------- +#endif + + if(p.X >= minOfTiles.Width && p.Y >= minOfTiles.Height && p.X <= maxOfTiles.Width && p.Y <= maxOfTiles.Height) + { + DrawTile dt = new DrawTile(p, new GPoint(p.X * tileRect.Width, p.Y * tileRect.Height)); + + if(!tileDrawingList.Contains(dt)) + { + tileDrawingList.Add(dt); + + //Debug.WriteLine("draw: " + dt); + } + } + } + } + + if(GMaps.Instance.ShuffleTilesOnLoad) + { + Stuff.Shuffle(tileDrawingList); + } + #endregion + } + finally + { + tileDrawingListLock.ReleaseWriterLock(); + } + + Monitor.Enter(tileLoadQueue); + try + { + tileDrawingListLock.AcquireReaderLock(); + try + { + foreach(DrawTile p in tileDrawingList) + { + LoadTask task = new LoadTask(p.PosXY, Zoom); + { + if(!tileLoadQueue.Contains(task)) + { + tileLoadQueue.Push(task); + } + } + } + } + finally + { + tileDrawingListLock.ReleaseReaderLock(); + } + + #region -- starts loader threads if needed -- + + lock(GThreadPool) + { + while(GThreadPool.Count < GThreadPoolSize) + { + Thread t = new Thread(new ThreadStart(ProcessLoadTask)); + { + t.Name = "TileLoader: " + GThreadPool.Count; + t.IsBackground = true; + t.Priority = ThreadPriority.BelowNormal; + } + GThreadPool.Add(t); + + Debug.WriteLine("add " + t.Name + " to GThreadPool"); + + t.Start(); + } + } + #endregion + + { + LastTileLoadStart = DateTime.Now; + Debug.WriteLine("OnTileLoadStart - at zoom " + Zoom + ", time: " + LastTileLoadStart.TimeOfDay); + } + + loadWaitCount = 0; + Monitor.PulseAll(tileLoadQueue); + } + finally + { + Monitor.Exit(tileLoadQueue); + } + + updatingBounds = false; + + if(OnTileLoadStart != null) + { + OnTileLoadStart(); + } + } + + /// + /// updates ground resolution info + /// + void UpdateGroundResolution() + { + double rez = Provider.Projection.GetGroundResolution(Zoom, Position.Lat); + pxRes100m = (int)(100.0 / rez); // 100 meters + pxRes1000m = (int)(1000.0 / rez); // 1km + pxRes10km = (int)(10000.0 / rez); // 10km + pxRes100km = (int)(100000.0 / rez); // 100km + pxRes1000km = (int)(1000000.0 / rez); // 1000km + pxRes5000km = (int)(5000000.0 / rez); // 5000km + } + + #region IDisposable Members + + ~Core() + { + Dispose(false); + } + + void Dispose(bool disposing) + { + if(IsStarted) + { + if(invalidator != null) + { + invalidator.CancelAsync(); + invalidator.DoWork -= new DoWorkEventHandler(invalidatorWatch); + invalidator.Dispose(); + invalidator = null; + } + + if(Refresh != null) + { + Refresh.Set(); + Refresh.Close(); + Refresh = null; + } + + int x = Interlocked.Decrement(ref instances); + Debug.WriteLine("OnMapClose: " + x); + + CancelAsyncTasks(); + IsStarted = false; + + if(Matrix != null) + { + Matrix.Dispose(); + Matrix = null; + } + + if(FailedLoads != null) + { + lock(FailedLoads) + { + FailedLoads.Clear(); + RaiseEmptyTileError = false; + } + FailedLoads = null; + } + + // cancel waiting loaders + Monitor.Enter(tileLoadQueue); + try + { + Monitor.PulseAll(tileLoadQueue); + tileDrawingList.Clear(); + } + finally + { + Monitor.Exit(tileLoadQueue); + } + + lock(GThreadPool) + { +#if PocketPC + Debug.WriteLine("waiting until loaders are stopped..."); + while(GThreadPool.Count > 0) + { + var t = GThreadPool[0]; + + if (t.State != ThreadState.Stopped) + { + var tr = t.Join(1111); + + Debug.WriteLine(t.Name + ", " + t.State); + + if (!tr) + { + continue; + } + else + { + GThreadPool.Remove(t); + } + } + else + { + GThreadPool.Remove(t); + } + } + Thread.Sleep(1111); +#endif + } + + if(tileDrawingListLock != null) + { + tileDrawingListLock.Dispose(); + tileDrawingListLock = null; + tileDrawingList = null; + } + + if(x == 0) + { +#if DEBUG + GMaps.Instance.CancelTileCaching(); +#endif + GMaps.Instance.noMapInstances = true; + GMaps.Instance.WaitForCache.Set(); + if(disposing) + { + GMaps.Instance.MemoryCache.Clear(); + } + } + } + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + #endregion + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.Internals/DrawTile.cs b/GreatMaps/GMap.NET.Core/GMap.NET.Internals/DrawTile.cs new file mode 100644 index 0000000..ca7f84b --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.Internals/DrawTile.cs @@ -0,0 +1,23 @@ + +namespace GMap.NET.Internals +{ + /// + /// struct for drawing tile + /// + internal struct DrawTile + { + public GPoint PosXY; + public GPoint PosPixel; + + public DrawTile(GPoint Pos, GPoint PosPixel) + { + this.PosXY = Pos; + this.PosPixel = PosPixel; + } + + public override string ToString() + { + return PosXY + ", px: " + PosPixel; + } + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.Internals/FastReaderWriterLock.cs b/GreatMaps/GMap.NET.Core/GMap.NET.Internals/FastReaderWriterLock.cs new file mode 100644 index 0000000..e6acd1a --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.Internals/FastReaderWriterLock.cs @@ -0,0 +1,194 @@ +#if !MONO && !PocketPC +#define UseFastResourceLock +#endif + +namespace GMap.NET.Internals +{ + using System; + using System.Threading; +#if !MONO + using System.Runtime.InteropServices; +#endif + + /// + /// custom ReaderWriterLock + /// in Vista and later uses integrated Slim Reader/Writer (SRW) Lock + /// http://msdn.microsoft.com/en-us/library/aa904937(VS.85).aspx + /// http://msdn.microsoft.com/en-us/magazine/cc163405.aspx#S2 + /// + public sealed class FastReaderWriterLock : IDisposable + { +#if !MONO && !PocketPC + private static class NativeMethods + { + // Methods + [DllImport("Kernel32", ExactSpelling = true)] + internal static extern void AcquireSRWLockExclusive(ref IntPtr srw); + [DllImport("Kernel32", ExactSpelling = true)] + internal static extern void AcquireSRWLockShared(ref IntPtr srw); + [DllImport("Kernel32", ExactSpelling = true)] + internal static extern void InitializeSRWLock(out IntPtr srw); + [DllImport("Kernel32", ExactSpelling = true)] + internal static extern void ReleaseSRWLockExclusive(ref IntPtr srw); + [DllImport("Kernel32", ExactSpelling = true)] + internal static extern void ReleaseSRWLockShared(ref IntPtr srw); + } + + IntPtr LockSRW = IntPtr.Zero; + + public FastReaderWriterLock() + { + if (UseNativeSRWLock) + { + NativeMethods.InitializeSRWLock(out this.LockSRW); + } + else + { +#if UseFastResourceLock + pLock = new FastResourceLock(); +#endif + } + } + +#if UseFastResourceLock + ~FastReaderWriterLock() + { + Dispose(false); + } + + void Dispose(bool disposing) + { + if (pLock != null) + { + pLock.Dispose(); + pLock = null; + } + } + + FastResourceLock pLock; +#endif + + static readonly bool UseNativeSRWLock = Stuff.IsRunningOnVistaOrLater() && IntPtr.Size == 4; // works only in 32-bit mode, any ideas on native 64-bit support? + +#endif + +#if !UseFastResourceLock + Int32 busy = 0; + Int32 readCount = 0; +#endif + + public void AcquireReaderLock() + { +#if !MONO && !PocketPC + if (UseNativeSRWLock) + { + NativeMethods.AcquireSRWLockShared(ref LockSRW); + } + else +#endif + { +#if UseFastResourceLock + pLock.AcquireShared(); +#else + Thread.BeginCriticalRegion(); + + while(Interlocked.CompareExchange(ref busy, 1, 0) != 0) + { + Thread.Sleep(1); + } + + Interlocked.Increment(ref readCount); + + // somehow this fix deadlock on heavy reads + Thread.Sleep(0); + Thread.Sleep(0); + Thread.Sleep(0); + Thread.Sleep(0); + Thread.Sleep(0); + Thread.Sleep(0); + Thread.Sleep(0); + + Interlocked.Exchange(ref busy, 0); +#endif + } + } + + public void ReleaseReaderLock() + { +#if !MONO && !PocketPC + if (UseNativeSRWLock) + { + NativeMethods.ReleaseSRWLockShared(ref LockSRW); + } + else +#endif + { +#if UseFastResourceLock + pLock.ReleaseShared(); +#else + Interlocked.Decrement(ref readCount); + Thread.EndCriticalRegion(); +#endif + } + } + + public void AcquireWriterLock() + { +#if !MONO && !PocketPC + if (UseNativeSRWLock) + { + NativeMethods.AcquireSRWLockExclusive(ref LockSRW); + } + else +#endif + { +#if UseFastResourceLock + pLock.AcquireExclusive(); +#else + Thread.BeginCriticalRegion(); + + while(Interlocked.CompareExchange(ref busy, 1, 0) != 0) + { + Thread.Sleep(1); + } + + while(Interlocked.CompareExchange(ref readCount, 0, 0) != 0) + { + Thread.Sleep(1); + } +#endif + } + } + + public void ReleaseWriterLock() + { +#if !MONO && !PocketPC + if (UseNativeSRWLock) + { + NativeMethods.ReleaseSRWLockExclusive(ref LockSRW); + } + else +#endif + { +#if UseFastResourceLock + pLock.ReleaseExclusive(); +#else + Interlocked.Exchange(ref busy, 0); + Thread.EndCriticalRegion(); +#endif + } + } + + #region IDisposable Members + + public void Dispose() + { +#if UseFastResourceLock + this.Dispose(true); + GC.SuppressFinalize(this); +#endif + } + + #endregion + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.Internals/FastResourceLock.cs b/GreatMaps/GMap.NET.Core/GMap.NET.Internals/FastResourceLock.cs new file mode 100644 index 0000000..c8798ef --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.Internals/FastResourceLock.cs @@ -0,0 +1,976 @@ +/* + * Process Hacker - + * fast resource lock + * + * Copyright (C) 2009 wj32 + * + * This file is part of Process Hacker. + * + * Process Hacker is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Process Hacker is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Process Hacker. If not, see . + */ + +#define DEFER_EVENT_CREATION +//#define ENABLE_STATISTICS +//#define RIGOROUS_CHECKS + +using System; +using System.Runtime.InteropServices; +using System.Security; +using System.Threading; + +namespace GMap.NET.Internals +{ +#if !MONO && !PocketPC + /// + /// Provides a fast resource (reader-writer) lock. + /// + /// + /// There are three types of acquire methods in this lock: + /// + /// Normal methods (AcquireExclusive, AcquireShared) are preferred + /// for general purpose use. + /// Busy wait methods (SpinAcquireExclusive, SpinAcquireShared) are + /// preferred if very little time is spent while the lock is acquired. + /// However, these do not give exclusive acquires precedence over + /// shared acquires. + /// Try methods (TryAcquireExclusive, TryAcquireShared) can be used to + /// quickly test if the lock is available. + /// + /// Note that all three types of functions can be used concurrently + /// in the same class instance. + /// + internal sealed class FastResourceLock : IDisposable + { + // Details + // + // Resource lock value width: 32 bits. + // Lock owned (either exclusive or shared): L (1 bit). + // Exclusive waking: W (1 bit). + // Shared owners count: SC (10 bits). + // Shared waiters count: SW (10 bits). + // Exclusive waiters count: EW (10 bits). + // + // Acquire exclusive: + // {L=0,W=0,SC=0,SW,EW=0} -> {L=1,W=0,SC=0,SW,EW=0} + // {L=0,W=1,SC=0,SW,EW} or {L=1,W,SC,SW,EW} -> + // {L,W,SC,SW,EW+1}, + // wait on event, + // {L=0,W=1,SC=0,SW,EW} -> {L=1,W=0,SC=0,SW,EW} + // + // Acquire shared: + // {L=0,W=0,SC=0,SW,EW=0} -> {L=1,W=0,SC=1,SW,EW=0} + // {L=1,W=0,SC>0,SW,EW=0} -> {L=1,W=0,SC+1,SW,EW=0} + // {L=1,W=0,SC=0,SW,EW=0} or {L,W=1,SC,SW,EW} or + // {L,W,SC,SW,EW>0} -> {L,W,SC,SW+1,EW}, + // wait on event, + // retry. + // + // Release exclusive: + // {L=1,W=0,SC=0,SW,EW>0} -> + // {L=0,W=1,SC=0,SW,EW-1}, + // release one exclusive waiter. + // {L=1,W=0,SC=0,SW,EW=0} -> + // {L=0,W=0,SC=0,SW=0,EW=0}, + // release all shared waiters. + // + // Note that we never do a direct acquire when W=1 + // (i.e. L=0 if W=1), so here we don't have to check + // the value of W. + // + // Release shared: + // {L=1,W=0,SC>1,SW,EW} -> {L=1,W=0,SC-1,SW,EW} + // {L=1,W=0,SC=1,SW,EW=0} -> {L=0,W=0,SC=0,SW,EW=0} + // {L=1,W=0,SC=1,SW,EW>0} -> + // {L=0,W=1,SC=0,SW,EW-1}, + // release one exclusive waiter. + // + // Again, we don't need to check the value of W. + // + // Convert exclusive to shared: + // {L=1,W=0,SC=0,SW,EW} -> + // {L=1,W=0,SC=1,SW=0,EW}, + // release all shared waiters. + // + // Convert shared to exclusive: + // {L=1,W=0,SC=1,SW,EW} -> + // {L=1,W=0,SC=0,SW,EW} + // + + /* */ + + // Note: I have included many small optimizations in the code + // because of the CLR's dumbass JIT compiler. + + #region Constants + + // Lock owned: 1 bit. + private const int LockOwned = 0x1; + + // Exclusive waking: 1 bit. + private const int LockExclusiveWaking = 0x2; + + // Shared owners count: 10 bits. + private const int LockSharedOwnersShift = 2; + private const int LockSharedOwnersMask = 0x3ff; + private const int LockSharedOwnersIncrement = 0x4; + + // Shared waiters count: 10 bits. + private const int LockSharedWaitersShift = 12; + private const int LockSharedWaitersMask = 0x3ff; + private const int LockSharedWaitersIncrement = 0x1000; + + // Exclusive waiters count: 10 bits. + private const int LockExclusiveWaitersShift = 22; + private const int LockExclusiveWaitersMask = 0x3ff; + private const int LockExclusiveWaitersIncrement = 0x400000; + + private const int ExclusiveMask = LockExclusiveWaking | (LockExclusiveWaitersMask << LockExclusiveWaitersShift); + + #endregion + + public struct Statistics + { + /// + /// The number of times the lock has been acquired in exclusive mode. + /// + public int AcqExcl; + /// + /// The number of times the lock has been acquired in shared mode. + /// + public int AcqShrd; + /// + /// The number of times either the fast path was retried due to the + /// spin count or the exclusive waiter went to sleep. + /// + /// + /// This number is usually much higher than AcqExcl, and indicates + /// a good spin count if AcqExclSlp is very small. + /// + public int AcqExclCont; + /// + /// The number of times either the fast path was retried due to the + /// spin count or the shared waiter went to sleep. + /// + /// + /// This number is usually much higher than AcqShrd, and indicates + /// a good spin count if AcqShrdSlp is very small. + /// + public int AcqShrdCont; + /// + /// The number of times exclusive waiters have gone to sleep. + /// + /// + /// If this number is high and not much time is spent in the + /// lock, consider increasing the spin count. + /// + public int AcqExclSlp; + /// + /// The number of times shared waiters have gone to sleep. + /// + /// + /// If this number is high and not much time is spent in the + /// lock, consider increasing the spin count. + /// + public int AcqShrdSlp; + /// + /// The highest number of exclusive waiters at any one time. + /// + public int PeakExclWtrsCount; + /// + /// The highest number of shared waiters at any one time. + /// + public int PeakShrdWtrsCount; + } + + // The number of times to spin before going to sleep. + private static readonly int SpinCount = NativeMethods.SpinCount; + + private int _value; + private IntPtr _sharedWakeEvent; + private IntPtr _exclusiveWakeEvent; + +#if ENABLE_STATISTICS + private int _acqExclCount = 0; + private int _acqShrdCount = 0; + private int _acqExclContCount = 0; + private int _acqShrdContCount = 0; + private int _acqExclSlpCount = 0; + private int _acqShrdSlpCount = 0; + private int _peakExclWtrsCount = 0; + private int _peakShrdWtrsCount = 0; +#endif + + /// + /// Creates a FastResourceLock. + /// + public FastResourceLock() + { + _value = 0; + +#if !DEFER_EVENT_CREATION + _sharedWakeEvent = NativeMethods.CreateSemaphore(IntPtr.Zero, 0, int.MaxValue, null); + _exclusiveWakeEvent = NativeMethods.CreateSemaphore(IntPtr.Zero, 0, int.MaxValue, null); +#endif + } + + ~FastResourceLock() + { + this.Dispose(false); + } + + private void Dispose(bool disposing) + { + if(_sharedWakeEvent != IntPtr.Zero) + { + NativeMethods.CloseHandle(_sharedWakeEvent); + _sharedWakeEvent = IntPtr.Zero; + } + + if(_exclusiveWakeEvent != IntPtr.Zero) + { + NativeMethods.CloseHandle(_exclusiveWakeEvent); + _exclusiveWakeEvent = IntPtr.Zero; + } + } + + /// + /// Disposes resources associated with the FastResourceLock. + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Gets the number of exclusive waiters. + /// + public int ExclusiveWaiters + { + get + { + return (_value >> LockExclusiveWaitersShift) & LockExclusiveWaitersMask; + } + } + + /// + /// Gets whether the lock is owned in either + /// exclusive or shared mode. + /// + public bool Owned + { + get + { + return (_value & LockOwned) != 0; + } + } + + /// + /// Gets the number of shared owners. + /// + public int SharedOwners + { + get + { + return (_value >> LockSharedOwnersShift) & LockSharedOwnersMask; + } + } + + /// + /// Gets the number of shared waiters. + /// + public int SharedWaiters + { + get + { + return (_value >> LockSharedWaitersShift) & LockSharedWaitersMask; + } + } + + /// + /// Acquires the lock in exclusive mode, blocking + /// if necessary. + /// + /// + /// Exclusive acquires are given precedence over shared + /// acquires. + /// + public void AcquireExclusive() + { + int value; + int i = 0; + +#if ENABLE_STATISTICS + Interlocked.Increment(ref _acqExclCount); + +#endif + while(true) + { + value = _value; + + // Case 1: lock not owned AND an exclusive waiter is not waking up. + // Here we don't have to check if there are exclusive waiters, because + // if there are the lock would be owned, and we are checking that anyway. + if((value & (LockOwned | LockExclusiveWaking)) == 0) + { +#if RIGOROUS_CHECKS + System.Diagnostics.Trace.Assert(((value >> LockSharedOwnersShift) & LockSharedOwnersMask) == 0); + System.Diagnostics.Trace.Assert(((value >> LockExclusiveWaitersShift) & LockExclusiveWaitersMask) == 0); + +#endif + if(Interlocked.CompareExchange( + ref _value, + value + LockOwned, + value + ) == value) + break; + } + // Case 2: lock owned OR lock not owned and an exclusive waiter is waking up. + // The second case means an exclusive waiter has just been woken up and is + // going to acquire the lock. We have to go to sleep to make sure we don't + // steal the lock. + else if(i >= SpinCount) + { +#if DEFER_EVENT_CREATION + // This call must go *before* the next operation. Otherwise, + // we will have a race condition between potential releasers + // and us. + this.EnsureEventCreated(ref _exclusiveWakeEvent); + +#endif + if(Interlocked.CompareExchange( + ref _value, + value + LockExclusiveWaitersIncrement, + value + ) == value) + { +#if ENABLE_STATISTICS + Interlocked.Increment(ref _acqExclSlpCount); + + int exclWtrsCount = (value >> LockExclusiveWaitersShift) & LockExclusiveWaitersMask; + + Interlocked2.Set( + ref _peakExclWtrsCount, + (p) => p < exclWtrsCount, + (p) => exclWtrsCount + ); + +#endif + // Go to sleep. + if(NativeMethods.WaitForSingleObject( + _exclusiveWakeEvent, + Timeout.Infinite + ) != NativeMethods.WaitObject0) + UtilsBreak("Utils.MsgFailedToWaitIndefinitely"); + + // Acquire the lock. + // At this point *no one* should be able to steal the lock from us. + do + { + value = _value; +#if RIGOROUS_CHECKS + + System.Diagnostics.Trace.Assert((value & LockOwned) == 0); + System.Diagnostics.Trace.Assert((value & LockExclusiveWaking) != 0); +#endif + } while(Interlocked.CompareExchange( + ref _value, + value + LockOwned - LockExclusiveWaking, + value + ) != value); + + break; + } + } + +#if ENABLE_STATISTICS + Interlocked.Increment(ref _acqExclContCount); +#endif + i++; + } + } + + /// + /// Acquires the lock in shared mode, blocking + /// if necessary. + /// + /// + /// Exclusive acquires are given precedence over shared + /// acquires. + /// + public void AcquireShared() + { + int value; + int i = 0; + +#if ENABLE_STATISTICS + Interlocked.Increment(ref _acqShrdCount); + +#endif + while(true) + { + value = _value; + + // Case 1: lock not owned AND no exclusive waiter is waking up AND + // there are no shared owners AND there are no exclusive waiters + if((value & ( + LockOwned | + (LockSharedOwnersMask << LockSharedOwnersShift) | + ExclusiveMask + )) == 0) + { + if(Interlocked.CompareExchange( + ref _value, + value + LockOwned + LockSharedOwnersIncrement, + value + ) == value) + break; + } + // Case 2: lock is owned AND no exclusive waiter is waking up AND + // there are shared owners AND there are no exclusive waiters + else if( + (value & LockOwned) != 0 && + ((value >> LockSharedOwnersShift) & LockSharedOwnersMask) != 0 && + (value & ExclusiveMask) == 0 + ) + { + if(Interlocked.CompareExchange( + ref _value, + value + LockSharedOwnersIncrement, + value + ) == value) + break; + } + // Other cases. + else if(i >= SpinCount) + { +#if DEFER_EVENT_CREATION + this.EnsureEventCreated(ref _sharedWakeEvent); + +#endif + if(Interlocked.CompareExchange( + ref _value, + value + LockSharedWaitersIncrement, + value + ) == value) + { +#if ENABLE_STATISTICS + Interlocked.Increment(ref _acqShrdSlpCount); + + int shrdWtrsCount = (value >> LockSharedWaitersShift) & LockSharedWaitersMask; + + Interlocked2.Set( + ref _peakShrdWtrsCount, + (p) => p < shrdWtrsCount, + (p) => shrdWtrsCount + ); + +#endif + // Go to sleep. + if(NativeMethods.WaitForSingleObject( + _sharedWakeEvent, + Timeout.Infinite + ) != NativeMethods.WaitObject0) + UtilsBreak("Utils.MsgFailedToWaitIndefinitely"); + + // Go back and try again. + continue; + } + } + +#if ENABLE_STATISTICS + Interlocked.Increment(ref _acqShrdContCount); +#endif + i++; + } + } + + public static void UtilsBreak(string logMessage) + { + System.Diagnostics.Debugger.Log(0, "Error", logMessage); + System.Diagnostics.Debugger.Break(); + } + + /// + /// Converts the ownership mode from exclusive to shared. + /// + /// + /// Exclusive acquires are not given a chance to acquire + /// the lock before this function does - as a result, + /// this function will never block. + /// + public void ConvertExclusiveToShared() + { + int value; + int sharedWaiters; + + while(true) + { + value = _value; +#if RIGOROUS_CHECKS + + System.Diagnostics.Trace.Assert((value & LockOwned) != 0); + System.Diagnostics.Trace.Assert((value & LockExclusiveWaking) == 0); + System.Diagnostics.Trace.Assert(((value >> LockSharedOwnersShift) & LockSharedOwnersMask) == 0); +#endif + + sharedWaiters = (value >> LockSharedWaitersShift) & LockSharedWaitersMask; + + if(Interlocked.CompareExchange( + ref _value, + (value + LockSharedOwnersIncrement) & ~(LockSharedWaitersMask << LockSharedWaitersShift), + value + ) == value) + { + if(sharedWaiters != 0) + NativeMethods.ReleaseSemaphore(_sharedWakeEvent, sharedWaiters, IntPtr.Zero); + + break; + } + } + } + +#if DEFER_EVENT_CREATION + /// + /// Checks if the specified event has been created, and + /// if not, creates it. + /// + /// A reference to the event handle. + private void EnsureEventCreated(ref IntPtr handle) + { + IntPtr eventHandle; + + if(Thread.VolatileRead(ref handle) != IntPtr.Zero) + return; + + eventHandle = NativeMethods.CreateSemaphore(IntPtr.Zero, 0, int.MaxValue, null); + + if(Interlocked.CompareExchange(ref handle, eventHandle, IntPtr.Zero) != IntPtr.Zero) + NativeMethods.CloseHandle(eventHandle); + } +#endif + + /// + /// Gets statistics information for the lock. + /// + /// A structure containing statistics. + public Statistics GetStatistics() + { +#if ENABLE_STATISTICS + return new Statistics() + { + AcqExcl = _acqExclCount, + AcqShrd = _acqShrdCount, + AcqExclCont = _acqExclContCount, + AcqShrdCont = _acqShrdContCount, + AcqExclSlp = _acqExclSlpCount, + AcqShrdSlp = _acqShrdSlpCount, + PeakExclWtrsCount = _peakExclWtrsCount, + PeakShrdWtrsCount = _peakShrdWtrsCount + }; +#else + return new Statistics(); +#endif + } + + /// + /// Releases the lock in exclusive mode. + /// + public void ReleaseExclusive() + { + int value; + + while(true) + { + value = _value; +#if RIGOROUS_CHECKS + + System.Diagnostics.Trace.Assert((value & LockOwned) != 0); + System.Diagnostics.Trace.Assert((value & LockExclusiveWaking) == 0); + System.Diagnostics.Trace.Assert(((value >> LockSharedOwnersShift) & LockSharedOwnersMask) == 0); +#endif + + // Case 1: if we have exclusive waiters, release one. + if(((value >> LockExclusiveWaitersShift) & LockExclusiveWaitersMask) != 0) + { + if(Interlocked.CompareExchange( + ref _value, + value - LockOwned + LockExclusiveWaking - LockExclusiveWaitersIncrement, + value + ) == value) + { + NativeMethods.ReleaseSemaphore(_exclusiveWakeEvent, 1, IntPtr.Zero); + + break; + } + } + // Case 2: if we have shared waiters, release all of them. + else + { + int sharedWaiters; + + sharedWaiters = (value >> LockSharedWaitersShift) & LockSharedWaitersMask; + + if(Interlocked.CompareExchange( + ref _value, + value & ~(LockOwned | (LockSharedWaitersMask << LockSharedWaitersShift)), + value + ) == value) + { + if(sharedWaiters != 0) + NativeMethods.ReleaseSemaphore(_sharedWakeEvent, sharedWaiters, IntPtr.Zero); + + break; + } + } + } + } + + /// + /// Releases the lock in shared mode. + /// + public void ReleaseShared() + { + int value; + int sharedOwners; + + while(true) + { + value = _value; +#if RIGOROUS_CHECKS + + System.Diagnostics.Trace.Assert((value & LockOwned) != 0); + System.Diagnostics.Trace.Assert((value & LockExclusiveWaking) == 0); + System.Diagnostics.Trace.Assert(((value >> LockSharedOwnersShift) & LockSharedOwnersMask) != 0); +#endif + + sharedOwners = (value >> LockSharedOwnersShift) & LockSharedOwnersMask; + + // Case 1: there are multiple shared owners. + if(sharedOwners > 1) + { + if(Interlocked.CompareExchange( + ref _value, + value - LockSharedOwnersIncrement, + value + ) == value) + break; + } + // Case 2: we are the last shared owner AND there are exclusive waiters. + else if(((value >> LockExclusiveWaitersShift) & LockExclusiveWaitersMask) != 0) + { + if(Interlocked.CompareExchange( + ref _value, + value - LockOwned + LockExclusiveWaking - LockSharedOwnersIncrement - LockExclusiveWaitersIncrement, + value + ) == value) + { + NativeMethods.ReleaseSemaphore(_exclusiveWakeEvent, 1, IntPtr.Zero); + + break; + } + } + // Case 3: we are the last shared owner AND there are no exclusive waiters. + else + { + if(Interlocked.CompareExchange( + ref _value, + value - LockOwned - LockSharedOwnersIncrement, + value + ) == value) + break; + } + } + } + + /// + /// Acquires the lock in exclusive mode, busy waiting + /// if necessary. + /// + /// + /// Exclusive acquires are *not* given precedence over shared + /// acquires for busy wait methods. + /// + public void SpinAcquireExclusive() + { + int value; + + while(true) + { + value = _value; + + if((value & (LockOwned | LockExclusiveWaking)) == 0) + { + if(Interlocked.CompareExchange( + ref _value, + value + LockOwned, + value + ) == value) + break; + } + + if(NativeMethods.SpinEnabled) + Thread.SpinWait(8); + else + Thread.Sleep(0); + } + } + + /// + /// Acquires the lock in shared mode, busy waiting + /// if necessary. + /// + /// + /// Exclusive acquires are *not* given precedence over shared + /// acquires for busy wait methods. + /// + public void SpinAcquireShared() + { + int value; + + while(true) + { + value = _value; + + if((value & ExclusiveMask) == 0) + { + if((value & LockOwned) == 0) + { + if(Interlocked.CompareExchange( + ref _value, + value + LockOwned + LockSharedOwnersIncrement, + value + ) == value) + break; + } + else if(((value >> LockSharedOwnersShift) & LockSharedOwnersMask) != 0) + { + if(Interlocked.CompareExchange( + ref _value, + value + LockSharedOwnersIncrement, + value + ) == value) + break; + } + } + + if(NativeMethods.SpinEnabled) + Thread.SpinWait(8); + else + Thread.Sleep(0); + } + } + + /// + /// Converts the ownership mode from shared to exclusive, + /// busy waiting if necessary. + /// + public void SpinConvertSharedToExclusive() + { + int value; + + while(true) + { + value = _value; + + // Can't convert if there are other shared owners. + if(((value >> LockSharedOwnersShift) & LockSharedOwnersMask) == 1) + { + if(Interlocked.CompareExchange( + ref _value, + value - LockSharedOwnersIncrement, + value + ) == value) + break; + } + + if(NativeMethods.SpinEnabled) + Thread.SpinWait(8); + else + Thread.Sleep(0); + } + } + + /// + /// Attempts to acquire the lock in exclusive mode. + /// + /// Whether the lock was acquired. + public bool TryAcquireExclusive() + { + int value; + + value = _value; + + if((value & (LockOwned | LockExclusiveWaking)) != 0) + return false; + + return Interlocked.CompareExchange( + ref _value, + value + LockOwned, + value + ) == value; + } + + /// + /// Attempts to acquire the lock in shared mode. + /// + /// Whether the lock was acquired. + public bool TryAcquireShared() + { + int value; + + value = _value; + + if((value & ExclusiveMask) != 0) + return false; + + if((value & LockOwned) == 0) + { + return Interlocked.CompareExchange( + ref _value, + value + LockOwned + LockSharedOwnersIncrement, + value + ) == value; + } + else if(((value >> LockSharedOwnersShift) & LockSharedOwnersMask) != 0) + { + return Interlocked.CompareExchange( + ref _value, + value + LockSharedOwnersIncrement, + value + ) == value; + } + else + { + return false; + } + } + + /// + /// Attempts to convert the ownership mode from shared + /// to exclusive. + /// + /// Whether the lock was converted. + public bool TryConvertSharedToExclusive() + { + int value; + + while(true) + { + value = _value; +#if RIGOROUS_CHECKS + + System.Diagnostics.Trace.Assert((value & LockOwned) != 0); + System.Diagnostics.Trace.Assert((value & LockExclusiveWaking) == 0); + System.Diagnostics.Trace.Assert(((value >> LockSharedOwnersShift) & LockSharedOwnersMask) != 0); +#endif + + // Can't convert if there are other shared owners. + if(((value >> LockSharedOwnersShift) & LockSharedOwnersMask) != 1) + return false; + + if(Interlocked.CompareExchange( + ref _value, + value - LockSharedOwnersIncrement, + value + ) == value) + return true; + } + } + } + + [SuppressUnmanagedCodeSecurity] + internal class NativeMethods + { + public const int WaitObject0 = 0x0; + public const int WaitAbandoned = 0x80; + public const int WaitTimeout = 0x102; + public const int WaitFailed = -1; + + public static readonly int SpinCount = Environment.ProcessorCount != 1 ? 4000 : 0; + public static readonly bool SpinEnabled = Environment.ProcessorCount != 1; + + // We need to import some stuff. We can't use + // ProcessHacker.Native because it depends on this library. + + [DllImport("kernel32.dll")] + public static extern bool CloseHandle( + [In] IntPtr Handle + ); + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] + public static extern IntPtr CreateEvent( + [In] [Optional] IntPtr EventAttributes, + [In] bool ManualReset, + [In] bool InitialState, + [In] [Optional] string Name + ); + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] + public static extern IntPtr CreateSemaphore( + [In] [Optional] IntPtr SemaphoreAttributes, + [In] int InitialCount, + [In] int MaximumCount, + [In] [Optional] string Name + ); + + [DllImport("kernel32.dll")] + public static extern bool ReleaseSemaphore( + [In] IntPtr SemaphoreHandle, + [In] int ReleaseCount, + [In] IntPtr PreviousCount // out int + ); + + [DllImport("kernel32.dll")] + public static extern bool ResetEvent( + [In] IntPtr EventHandle + ); + + [DllImport("kernel32.dll")] + public static extern bool SetEvent( + [In] IntPtr EventHandle + ); + + [DllImport("kernel32.dll")] + public static extern int WaitForSingleObject( + [In] IntPtr Handle, + [In] int Milliseconds + ); + + [DllImport("ntdll.dll")] + public static extern int NtCreateKeyedEvent( + [Out] out IntPtr KeyedEventHandle, + [In] int DesiredAccess, + [In] [Optional] IntPtr ObjectAttributes, + [In] int Flags + ); + + [DllImport("ntdll.dll")] + public static extern int NtReleaseKeyedEvent( + [In] IntPtr KeyedEventHandle, + [In] IntPtr KeyValue, + [In] bool Alertable, + [In] [Optional] IntPtr Timeout + ); + + [DllImport("ntdll.dll")] + public static extern int NtWaitForKeyedEvent( + [In] IntPtr KeyedEventHandle, + [In] IntPtr KeyValue, + [In] bool Alertable, + [In] [Optional] IntPtr Timeout + ); + } +#endif +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.Internals/KiberTileCache.cs b/GreatMaps/GMap.NET.Core/GMap.NET.Internals/KiberTileCache.cs new file mode 100644 index 0000000..05759f4 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.Internals/KiberTileCache.cs @@ -0,0 +1,84 @@ + +namespace GMap.NET.Internals +{ + using System.Collections.Generic; + using System.IO; + using System; + + /// + /// kiber speed memory cache for tiles with history support ;} + /// + internal class KiberTileCache : Dictionary + { + readonly Queue Queue = new Queue(); + + /// + /// the amount of tiles in MB to keep in memmory, default: 22MB, if each ~100Kb it's ~222 tiles + /// +#if !PocketPC + public int MemoryCacheCapacity = 22; +#else + public int MemoryCacheCapacity = 3; +#endif + + long memoryCacheSize = 0; + + /// + /// current memmory cache size in MB + /// + public double MemoryCacheSize + { + get + { + return memoryCacheSize / 1048576.0; + } + } + + public new void Add(RawTile key, byte[] value) + { + Queue.Enqueue(key); + base.Add(key, value); + + memoryCacheSize += value.Length; + } + + // do not allow directly removal of elements + private new void Remove(RawTile key) + { + + } + + public new void Clear() + { + Queue.Clear(); + base.Clear(); + } + + internal void RemoveMemoryOverload() + { + while(MemoryCacheSize > MemoryCacheCapacity) + { + if(Keys.Count > 0 && Queue.Count > 0) + { + RawTile first = Queue.Dequeue(); + try + { + var m = base[first]; + { + base.Remove(first); + memoryCacheSize -= m.Length; + } + m = null; + } + catch + { + } + } + else + { + break; + } + } + } + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.Internals/LoadTask.cs b/GreatMaps/GMap.NET.Core/GMap.NET.Internals/LoadTask.cs new file mode 100644 index 0000000..81487bd --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.Internals/LoadTask.cs @@ -0,0 +1,23 @@ + +namespace GMap.NET.Internals +{ + /// + /// tile load task + /// + internal struct LoadTask + { + public GPoint Pos; + public int Zoom; + + public LoadTask(GPoint pos, int zoom) + { + Pos = pos; + Zoom = zoom; + } + + public override string ToString() + { + return Zoom + " - " + Pos.ToString(); + } + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.Internals/PureImage.cs b/GreatMaps/GMap.NET.Core/GMap.NET.Internals/PureImage.cs new file mode 100644 index 0000000..469a815 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.Internals/PureImage.cs @@ -0,0 +1,52 @@ + +namespace GMap.NET +{ + using System; + using System.IO; + + /// + /// image abstraction proxy + /// + public abstract class PureImageProxy + { + abstract public PureImage FromStream(Stream stream); + abstract public bool Save(Stream stream, PureImage image); + + public PureImage FromArray(byte[] data) + { + MemoryStream m = new MemoryStream(data, 0, data.Length, false, true); + var pi = FromStream(m); + if(pi != null) + { + m.Position = 0; + pi.Data = m; + } + else + { + m.Dispose(); + } + m = null; + + return pi; + } + } + + /// + /// image abstraction + /// + public abstract class PureImage : IDisposable + { + public MemoryStream Data; + + internal bool IsParent; + internal long Ix; + internal long Xoff; + internal long Yoff; + + #region IDisposable Members + + public abstract void Dispose(); + + #endregion + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.Internals/RawTile.cs b/GreatMaps/GMap.NET.Core/GMap.NET.Internals/RawTile.cs new file mode 100644 index 0000000..76f832d --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.Internals/RawTile.cs @@ -0,0 +1,27 @@ +namespace GMap.NET.Internals +{ + using System.IO; + using System; + + /// + /// struct for raw tile + /// + internal struct RawTile + { + public int Type; + public GPoint Pos; + public int Zoom; + + public RawTile(int Type, GPoint Pos, int Zoom) + { + this.Type = Type; + this.Pos = Pos; + this.Zoom = Zoom; + } + + public override string ToString() + { + return Type + " at zoom " + Zoom + ", pos: " + Pos; + } + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.Internals/Stuff.cs b/GreatMaps/GMap.NET.Core/GMap.NET.Internals/Stuff.cs new file mode 100644 index 0000000..ee0eb9d --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.Internals/Stuff.cs @@ -0,0 +1,157 @@ + +namespace GMap.NET.Internals +{ + using System; + using System.Collections.Generic; + using System.ComponentModel; + using System.IO; + using System.Reflection; + + /// + /// etc functions... + /// + internal class Stuff + { + public static string EnumToString(Enum value) + { + FieldInfo fi = value.GetType().GetField(value.ToString()); + DescriptionAttribute[] attributes = + (DescriptionAttribute[])fi.GetCustomAttributes( + typeof(DescriptionAttribute), false); + + return (attributes.Length > 0) ? attributes[0].Description : value.ToString(); + } + + [System.Runtime.InteropServices.DllImportAttribute("user32.dll", EntryPoint = "SetCursorPos")] + [return: System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.Bool)] + public static extern bool SetCursorPos(int X, int Y); + + public static readonly Random random = new System.Random(); + + public static void Shuffle(List deck) + { + int N = deck.Count; + + for(int i = 0; i < N; ++i) + { + int r = i + (int)(random.Next(N - i)); + T t = deck[r]; + deck[r] = deck[i]; + deck[i] = t; + } + } + + public static MemoryStream CopyStream(Stream inputStream, bool SeekOriginBegin) + { + const int readSize = 32 * 1024; + byte[] buffer = new byte[readSize]; + MemoryStream ms = new MemoryStream(); + { + int count = 0; + while((count = inputStream.Read(buffer, 0, readSize)) > 0) + { + ms.Write(buffer, 0, count); + } + } + buffer = null; + if(SeekOriginBegin) + { + inputStream.Seek(0, SeekOrigin.Begin); + } + ms.Seek(0, SeekOrigin.Begin); + return ms; + } + + public static bool IsRunningOnVistaOrLater() + { + OperatingSystem os = Environment.OSVersion; + + if(os.Platform == PlatformID.Win32NT) + { + Version vs = os.Version; + + if(vs.Major >= 6 && vs.Minor >= 0) + { + return true; + } + } + + return false; + } + + public static bool IsRunningOnWin7orLater() + { + OperatingSystem os = Environment.OSVersion; + + if(os.Platform == PlatformID.Win32NT) + { + Version vs = os.Version; + + if(vs.Major >= 6 && vs.Minor > 0) + { + return true; + } + } + + return false; + } + + public static void RemoveInvalidPathSymbols(ref string url) + { +#if !PocketPC + char[] ilg = Path.GetInvalidFileNameChars(); +#else + char[] ilg = new char[41]; + for(int i = 0; i < 32; i++) + ilg[i] = (char) i; + + ilg[32] = '"'; + ilg[33] = '<'; + ilg[34] = '>'; + ilg[35] = '|'; + ilg[36] = '?'; + ilg[37] = ':'; + ilg[38] = '/'; + ilg[39] = '\\'; + ilg[39] = '*'; +#endif + foreach(char c in ilg) + { + url = url.Replace(c, '_'); + } + } + } + +#if PocketPC + static class Monitor + { + static readonly OpenNETCF.Threading.Monitor2 wait = new OpenNETCF.Threading.Monitor2(); + + public static void Enter(Stack tileLoadQueue) + { + wait.Enter(); + } + + public static void Exit(Stack tileLoadQueue) + { + wait.Exit(); + } + + public static void Wait(Stack tileLoadQueue) + { + wait.Wait(); + } + + public static bool Wait(Stack tileLoadQueue, int WaitForTileLoadThreadTimeout, bool p) + { + wait.Wait(); + return true; + } + + internal static void PulseAll(Stack tileLoadQueue) + { + wait.PulseAll(); + } + } +#endif +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.Internals/Tile.cs b/GreatMaps/GMap.NET.Core/GMap.NET.Internals/Tile.cs new file mode 100644 index 0000000..a87bc39 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.Internals/Tile.cs @@ -0,0 +1,143 @@ + +namespace GMap.NET.Internals +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Threading; + + /// + /// represent tile + /// + public struct Tile : IDisposable + { + public static readonly Tile Empty = new Tile(); + + GPoint pos; + int zoom; + PureImage[] overlays; + long OverlaysCount; + + public readonly bool NotEmpty; + + public Tile(int zoom, GPoint pos) + { + this.NotEmpty = true; + this.zoom = zoom; + this.pos = pos; + this.overlays = null; + this.OverlaysCount = 0; + } + + public IEnumerable Overlays + { + get + { +#if PocketPC + for (long i = 0, size = OverlaysCount; i < size; i++) +#else + for (long i = 0, size = Interlocked.Read(ref OverlaysCount); i < size; i++) +#endif + { + yield return overlays[i]; + } + } + } + + internal void AddOverlay(PureImage i) + { + if (overlays == null) + { + overlays = new PureImage[4]; + } +#if !PocketPC + overlays[Interlocked.Increment(ref OverlaysCount) - 1] = i; +#else + overlays[++OverlaysCount - 1] = i; +#endif + } + + internal bool HasAnyOverlays + { + get + { +#if PocketPC + return OverlaysCount > 0; +#else + return Interlocked.Read(ref OverlaysCount) > 0; +#endif + } + } + + public int Zoom + { + get + { + return zoom; + } + private set + { + zoom = value; + } + } + + public GPoint Pos + { + get + { + return pos; + } + private set + { + pos = value; + } + } + + #region IDisposable Members + + public void Dispose() + { + if (overlays != null) + { +#if PocketPC + for (long i = OverlaysCount - 1; i >= 0; i--) + +#else + for (long i = Interlocked.Read(ref OverlaysCount) - 1; i >= 0; i--) +#endif + { +#if !PocketPC + Interlocked.Decrement(ref OverlaysCount); +#else + OverlaysCount--; +#endif + overlays[i].Dispose(); + overlays[i] = null; + } + overlays = null; + } + } + + #endregion + + public static bool operator ==(Tile m1, Tile m2) + { + return m1.pos == m2.pos && m1.zoom == m2.zoom; + } + + public static bool operator !=(Tile m1, Tile m2) + { + return !(m1 == m2); + } + + public override bool Equals(object obj) + { + return base.Equals(obj); + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.Internals/TileMatrix.cs b/GreatMaps/GMap.NET.Core/GMap.NET.Internals/TileMatrix.cs new file mode 100644 index 0000000..b355326 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.Internals/TileMatrix.cs @@ -0,0 +1,246 @@ + +namespace GMap.NET.Internals +{ + using System.Collections.Generic; + using System.Threading; + using System.Diagnostics; + using System; + + /// + /// matrix for tiles + /// + internal class TileMatrix : IDisposable + { + List> Levels = new List>(33); + FastReaderWriterLock Lock = new FastReaderWriterLock(); + + public TileMatrix() + { + for(int i = 0; i < Levels.Capacity; i++) + { + Levels.Add(new Dictionary(55)); + } + } + + public void ClearAllLevels() + { + Lock.AcquireWriterLock(); + try + { + foreach(var matrix in Levels) + { + foreach(var t in matrix) + { + t.Value.Dispose(); + } + matrix.Clear(); + } + } + finally + { + Lock.ReleaseWriterLock(); + } + } + + public void ClearLevel(int zoom) + { + Lock.AcquireWriterLock(); + try + { + if(zoom < Levels.Count) + { + var l = Levels[zoom]; + + foreach(var t in l) + { + t.Value.Dispose(); + } + + l.Clear(); + } + } + finally + { + Lock.ReleaseWriterLock(); + } + } + + List> tmp = new List>(44); + + public void ClearLevelAndPointsNotIn(int zoom, List list) + { + Lock.AcquireWriterLock(); + try + { + if(zoom < Levels.Count) + { + var l = Levels[zoom]; + + tmp.Clear(); + + foreach(var t in l) + { + if(!list.Exists(p => p.PosXY == t.Key)) + { + tmp.Add(t); + } + } + + foreach(var r in tmp) + { + l.Remove(r.Key); + r.Value.Dispose(); + } + + tmp.Clear(); + } + } + finally + { + Lock.ReleaseWriterLock(); + } + } + + public void ClearLevelsBelove(int zoom) + { + Lock.AcquireWriterLock(); + try + { + if(zoom - 1 < Levels.Count) + { + for(int i = zoom - 1; i >= 0; i--) + { + var l = Levels[i]; + + foreach(var t in l) + { + t.Value.Dispose(); + } + + l.Clear(); + } + } + } + finally + { + Lock.ReleaseWriterLock(); + } + } + + public void ClearLevelsAbove(int zoom) + { + Lock.AcquireWriterLock(); + try + { + if(zoom + 1 < Levels.Count) + { + for(int i = zoom + 1; i < Levels.Count; i++) + { + var l = Levels[i]; + + foreach(var t in l) + { + t.Value.Dispose(); + } + + l.Clear(); + } + } + } + finally + { + Lock.ReleaseWriterLock(); + } + } + + public void EnterReadLock() + { + Lock.AcquireReaderLock(); + } + + public void LeaveReadLock() + { + Lock.ReleaseReaderLock(); + } + + public Tile GetTileWithNoLock(int zoom, GPoint p) + { + Tile ret = Tile.Empty; + + //if(zoom < Levels.Count) + { + Levels[zoom].TryGetValue(p, out ret); + } + + return ret; + } + + public Tile GetTileWithReadLock(int zoom, GPoint p) + { + Tile ret = Tile.Empty; + + Lock.AcquireReaderLock(); + try + { + ret = GetTileWithNoLock(zoom, p); + } + finally + { + Lock.ReleaseReaderLock(); + } + + return ret; + } + + public void SetTile(Tile t) + { + Lock.AcquireWriterLock(); + try + { + if(t.Zoom < Levels.Count) + { + Levels[t.Zoom][t.Pos] = t; + } + } + finally + { + Lock.ReleaseWriterLock(); + } + } + + #region IDisposable Members + + ~TileMatrix() + { + Dispose(false); + } + + void Dispose(bool disposing) + { + if(Lock != null) + { + if(disposing) + { + ClearAllLevels(); + } + + Levels.Clear(); + Levels = null; + + tmp.Clear(); + tmp = null; + + Lock.Dispose(); + Lock = null; + } + } + + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + #endregion + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/ArcGIS/ArcGIS_DarbAE_Q2_2011_NAVTQ_Eng_V5_MapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/ArcGIS/ArcGIS_DarbAE_Q2_2011_NAVTQ_Eng_V5_MapProvider.cs new file mode 100644 index 0000000..b2b4e28 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/ArcGIS/ArcGIS_DarbAE_Q2_2011_NAVTQ_Eng_V5_MapProvider.cs @@ -0,0 +1,86 @@ + +namespace GMap.NET.MapProviders +{ + using System; + using GMap.NET.Projections; + + /// + /// ArcGIS_DarbAE_Q2_2011_NAVTQ_Eng_V5_Map provider, + /// http://www.darb.ae/ArcGIS/rest/services/BaseMaps/Q2_2011_NAVTQ_Eng_V5/MapServer + /// + public class ArcGIS_DarbAE_Q2_2011_NAVTQ_Eng_V5_MapProvider : GMapProvider + { + public static readonly ArcGIS_DarbAE_Q2_2011_NAVTQ_Eng_V5_MapProvider Instance; + + ArcGIS_DarbAE_Q2_2011_NAVTQ_Eng_V5_MapProvider() + { + MaxZoom = 12; + Area = RectLatLng.FromLTRB(49.8846923723311, 28.0188609585523, 58.2247031977662, 21.154115956732); + Copyright = string.Format("©{0} ESRI - Map data ©{0} ArcGIS", DateTime.Today.Year); + } + + static ArcGIS_DarbAE_Q2_2011_NAVTQ_Eng_V5_MapProvider() + { + Instance = new ArcGIS_DarbAE_Q2_2011_NAVTQ_Eng_V5_MapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("E03CFEDF-9277-49B3-9912-D805347F934B"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "ArcGIS_DarbAE_Q2_2011_NAVTQ_Eng_V5_MapProvider"; + public override string Name + { + get + { + return name; + } + } + + public override PureProjection Projection + { + get + { + return PlateCarreeProjectionDarbAe.Instance; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { this }; + } + return overlays; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom) + { + // http://www.darb.ae/ArcGIS/rest/services/BaseMaps/Q2_2011_NAVTQ_Eng_V5/MapServer/tile/0/121/144 + + return string.Format(UrlFormat, zoom, pos.Y, pos.X); + } + + static readonly string UrlFormat = "http://www.darb.ae/ArcGIS/rest/services/BaseMaps/Q2_2011_NAVTQ_Eng_V5/MapServer/tile/{0}/{1}/{2}"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/ArcGIS/ArcGIS_Imagery_World_2D_MapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/ArcGIS/ArcGIS_Imagery_World_2D_MapProvider.cs new file mode 100644 index 0000000..2de4761 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/ArcGIS/ArcGIS_Imagery_World_2D_MapProvider.cs @@ -0,0 +1,60 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// ArcGIS_Imagery_World_2D_Map provider, http://server.arcgisonline.com + /// + public class ArcGIS_Imagery_World_2D_MapProvider : ArcGISMapPlateCarreeProviderBase + { + public static readonly ArcGIS_Imagery_World_2D_MapProvider Instance; + + ArcGIS_Imagery_World_2D_MapProvider() + { + } + + static ArcGIS_Imagery_World_2D_MapProvider() + { + Instance = new ArcGIS_Imagery_World_2D_MapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("FF7ADDAD-F155-41DB-BC42-CC6FD97C8B9D"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "ArcGIS_Imagery_World_2D_Map"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + // http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_Imagery_World_2D/MapServer/tile/1/0/1.jpg + + return string.Format(UrlFormat, zoom, pos.Y, pos.X); + } + + static readonly string UrlFormat = "http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_Imagery_World_2D/MapServer/tile/{0}/{1}/{2}"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/ArcGIS/ArcGIS_ShadedRelief_World_2D_MapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/ArcGIS/ArcGIS_ShadedRelief_World_2D_MapProvider.cs new file mode 100644 index 0000000..ee0ba3f --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/ArcGIS/ArcGIS_ShadedRelief_World_2D_MapProvider.cs @@ -0,0 +1,60 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// ArcGIS_ShadedRelief_World_2D_Map provider, http://server.arcgisonline.com + /// + public class ArcGIS_ShadedRelief_World_2D_MapProvider : ArcGISMapPlateCarreeProviderBase + { + public static readonly ArcGIS_ShadedRelief_World_2D_MapProvider Instance; + + ArcGIS_ShadedRelief_World_2D_MapProvider() + { + } + + static ArcGIS_ShadedRelief_World_2D_MapProvider() + { + Instance = new ArcGIS_ShadedRelief_World_2D_MapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("A8995FA4-D9D8-415B-87D0-51A7E53A90D4"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "ArcGIS_ShadedRelief_World_2D_Map"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + // http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_ShadedRelief_World_2D/MapServer/tile/1/0/1.jpg + + return string.Format(UrlFormat, zoom, pos.Y, pos.X); + } + + static readonly string UrlFormat = "http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_ShadedRelief_World_2D/MapServer/tile/{0}/{1}/{2}"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/ArcGIS/ArcGIS_StreetMap_World_2D_MapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/ArcGIS/ArcGIS_StreetMap_World_2D_MapProvider.cs new file mode 100644 index 0000000..337d0a5 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/ArcGIS/ArcGIS_StreetMap_World_2D_MapProvider.cs @@ -0,0 +1,166 @@ + +namespace GMap.NET.MapProviders +{ + using System; + using GMap.NET.Projections; + + public abstract class ArcGISMapPlateCarreeProviderBase : GMapProvider + { + public ArcGISMapPlateCarreeProviderBase() + { + Copyright = string.Format("©{0} ESRI - Map data ©{0} ArcGIS", DateTime.Today.Year); + } + + #region GMapProvider Members + public override Guid Id + { + get + { + throw new NotImplementedException(); + } + } + + public override string Name + { + get + { + throw new NotImplementedException(); + } + } + + public override PureProjection Projection + { + get + { + return PlateCarreeProjection.Instance; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { this }; + } + return overlays; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + throw new NotImplementedException(); + } + #endregion + } + + public abstract class ArcGISMapMercatorProviderBase : GMapProvider + { + public ArcGISMapMercatorProviderBase() + { + MaxZoom = null; + Copyright = string.Format("©{0} ESRI - Map data ©{0} ArcGIS", DateTime.Today.Year); + } + + #region GMapProvider Members + public override Guid Id + { + get + { + throw new NotImplementedException(); + } + } + + public override string Name + { + get + { + throw new NotImplementedException(); + } + } + + public override PureProjection Projection + { + get + { + return MercatorProjection.Instance; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { this }; + } + return overlays; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + throw new NotImplementedException(); + } + #endregion + } + + /// + /// ArcGIS_StreetMap_World_2D_Map provider, http://server.arcgisonline.com + /// + public class ArcGIS_StreetMap_World_2D_MapProvider : ArcGISMapPlateCarreeProviderBase + { + public static readonly ArcGIS_StreetMap_World_2D_MapProvider Instance; + + ArcGIS_StreetMap_World_2D_MapProvider() + { + } + + static ArcGIS_StreetMap_World_2D_MapProvider() + { + Instance = new ArcGIS_StreetMap_World_2D_MapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("00BF56D4-4B48-4939-9B11-575BBBE4A718"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "ArcGIS_StreetMap_World_2D_Map"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + // http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_StreetMap_World_2D/MapServer/tile/0/0/0.jpg + + return string.Format(UrlFormat, zoom, pos.Y, pos.X); + } + + static readonly string UrlFormat = "http://server.arcgisonline.com/ArcGIS/rest/services/ESRI_StreetMap_World_2D/MapServer/tile/{0}/{1}/{2}"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/ArcGIS/ArcGIS_Topo_US_2D_MapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/ArcGIS/ArcGIS_Topo_US_2D_MapProvider.cs new file mode 100644 index 0000000..b3be721 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/ArcGIS/ArcGIS_Topo_US_2D_MapProvider.cs @@ -0,0 +1,60 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// ArcGIS_Topo_US_2D_Map provider, http://server.arcgisonline.com + /// + public class ArcGIS_Topo_US_2D_MapProvider : ArcGISMapPlateCarreeProviderBase + { + public static readonly ArcGIS_Topo_US_2D_MapProvider Instance; + + ArcGIS_Topo_US_2D_MapProvider() + { + } + + static ArcGIS_Topo_US_2D_MapProvider() + { + Instance = new ArcGIS_Topo_US_2D_MapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("7652CC72-5C92-40F5-B572-B8FEAA728F6D"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "ArcGIS_Topo_US_2D_Map"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + // http://server.arcgisonline.com/ArcGIS/rest/services/NGS_Topo_US_2D/MapServer/tile/4/3/15 + + return string.Format(UrlFormat, zoom, pos.Y, pos.X); + } + + static readonly string UrlFormat = "http://server.arcgisonline.com/ArcGIS/rest/services/NGS_Topo_US_2D/MapServer/tile/{0}/{1}/{2}"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/ArcGIS/ArcGIS_World_Physical_MapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/ArcGIS/ArcGIS_World_Physical_MapProvider.cs new file mode 100644 index 0000000..e974014 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/ArcGIS/ArcGIS_World_Physical_MapProvider.cs @@ -0,0 +1,60 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// ArcGIS_World_Physical_Map provider, http://server.arcgisonline.com + /// + public class ArcGIS_World_Physical_MapProvider : ArcGISMapMercatorProviderBase + { + public static readonly ArcGIS_World_Physical_MapProvider Instance; + + ArcGIS_World_Physical_MapProvider() + { + } + + static ArcGIS_World_Physical_MapProvider() + { + Instance = new ArcGIS_World_Physical_MapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("0C0E73E3-5EA6-4F08-901C-AE85BCB1BFC8"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "ArcGIS_World_Physical_Map"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + // http://services.arcgisonline.com/ArcGIS/rest/services/World_Physical_Map/MapServer/tile/2/0/2.jpg + + return string.Format(UrlFormat, zoom, pos.Y, pos.X); + } + + static readonly string UrlFormat = "http://server.arcgisonline.com/ArcGIS/rest/services/World_Physical_Map/MapServer/tile/{0}/{1}/{2}"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/ArcGIS/ArcGIS_World_Shaded_Relief_MapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/ArcGIS/ArcGIS_World_Shaded_Relief_MapProvider.cs new file mode 100644 index 0000000..39050a3 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/ArcGIS/ArcGIS_World_Shaded_Relief_MapProvider.cs @@ -0,0 +1,60 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// ArcGIS_World_Shaded_Relief_Map provider, http://server.arcgisonline.com + /// + public class ArcGIS_World_Shaded_Relief_MapProvider : ArcGISMapMercatorProviderBase + { + public static readonly ArcGIS_World_Shaded_Relief_MapProvider Instance; + + ArcGIS_World_Shaded_Relief_MapProvider() + { + } + + static ArcGIS_World_Shaded_Relief_MapProvider() + { + Instance = new ArcGIS_World_Shaded_Relief_MapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("2E821FEF-8EA1-458A-BC82-4F699F4DEE79"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "ArcGIS_World_Shaded_Relief_Map"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + // http://services.arcgisonline.com/ArcGIS/rest/services/World_Shaded_Relief/MapServer/tile/0/0/0jpg + + return string.Format(UrlFormat, zoom, pos.Y, pos.X); + } + + static readonly string UrlFormat = "http://server.arcgisonline.com/ArcGIS/rest/services/World_Shaded_Relief/MapServer/tile/{0}/{1}/{2}"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/ArcGIS/ArcGIS_World_Street_MapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/ArcGIS/ArcGIS_World_Street_MapProvider.cs new file mode 100644 index 0000000..f045ef9 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/ArcGIS/ArcGIS_World_Street_MapProvider.cs @@ -0,0 +1,60 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// ArcGIS_World_Street_Map provider, http://server.arcgisonline.com + /// + public class ArcGIS_World_Street_MapProvider : ArcGISMapMercatorProviderBase + { + public static readonly ArcGIS_World_Street_MapProvider Instance; + + ArcGIS_World_Street_MapProvider() + { + } + + static ArcGIS_World_Street_MapProvider() + { + Instance = new ArcGIS_World_Street_MapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("E1FACDF6-E535-4D69-A49F-12B623A467A9"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "ArcGIS_World_Street_Map"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + // http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/0/0/0jpg + + return string.Format(UrlFormat, zoom, pos.Y, pos.X); + } + + static readonly string UrlFormat = "http://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{0}/{1}/{2}"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/ArcGIS/ArcGIS_World_Terrain_Base_MapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/ArcGIS/ArcGIS_World_Terrain_Base_MapProvider.cs new file mode 100644 index 0000000..efdb1c6 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/ArcGIS/ArcGIS_World_Terrain_Base_MapProvider.cs @@ -0,0 +1,60 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// ArcGIS_World_Terrain_Base_Map provider, http://server.arcgisonline.com + /// + public class ArcGIS_World_Terrain_Base_MapProvider : ArcGISMapMercatorProviderBase + { + public static readonly ArcGIS_World_Terrain_Base_MapProvider Instance; + + ArcGIS_World_Terrain_Base_MapProvider() + { + } + + static ArcGIS_World_Terrain_Base_MapProvider() + { + Instance = new ArcGIS_World_Terrain_Base_MapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("927F175B-5200-4D95-A99B-1C87C93099DA"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "ArcGIS_World_Terrain_Base_Map"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + // http://services.arcgisonline.com/ArcGIS/rest/services/World_Terrain_Base/MapServer/tile/0/0/0jpg + + return string.Format(UrlFormat, zoom, pos.Y, pos.X); + } + + static readonly string UrlFormat = "http://server.arcgisonline.com/ArcGIS/rest/services/World_Terrain_Base/MapServer/tile/{0}/{1}/{2}"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/ArcGIS/ArcGIS_World_Topo_MapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/ArcGIS/ArcGIS_World_Topo_MapProvider.cs new file mode 100644 index 0000000..c93635e --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/ArcGIS/ArcGIS_World_Topo_MapProvider.cs @@ -0,0 +1,60 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// ArcGIS_World_Topo_Map provider, http://server.arcgisonline.com + /// + public class ArcGIS_World_Topo_MapProvider : ArcGISMapMercatorProviderBase + { + public static readonly ArcGIS_World_Topo_MapProvider Instance; + + ArcGIS_World_Topo_MapProvider() + { + } + + static ArcGIS_World_Topo_MapProvider() + { + Instance = new ArcGIS_World_Topo_MapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("E0354A49-7447-4C9A-814F-A68565ED834B"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "ArcGIS_World_Topo_Map"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + // http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/0/0/0jpg + + return string.Format(UrlFormat, zoom, pos.Y, pos.X); + } + + static readonly string UrlFormat = "http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{0}/{1}/{2}"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Bing/BingHybridMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Bing/BingHybridMapProvider.cs new file mode 100644 index 0000000..7f9bc7c --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Bing/BingHybridMapProvider.cs @@ -0,0 +1,61 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// BingHybridMap provider + /// + public class BingHybridMapProvider : BingMapProviderBase + { + public static readonly BingHybridMapProvider Instance; + + BingHybridMapProvider() + { + } + + static BingHybridMapProvider() + { + Instance = new BingHybridMapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("94E2FCB4-CAAC-45EA-A1F9-8147C4B14970"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "BingHybridMap"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + string key = TileXYToQuadKey(pos.X, pos.Y, zoom); + return string.Format(UrlFormat, GetServerNum(pos, 4), key, Version, language, (!string.IsNullOrEmpty(ClientKey) ? "&key=" + ClientKey : string.Empty)); + } + + // http://ecn.dynamic.t3.tiles.virtualearth.net/comp/CompositionHandler/12030012020203?mkt=en-us&it=A,G,L&n=z + + static readonly string UrlFormat = "http://ecn.t{0}.tiles.virtualearth.net/tiles/h{1}.jpeg?g={2}&mkt={3}&n=z{4}"; + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Bing/BingMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Bing/BingMapProvider.cs new file mode 100644 index 0000000..e225d8a --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Bing/BingMapProvider.cs @@ -0,0 +1,243 @@ + +namespace GMap.NET.MapProviders +{ + using System; + using System.Text; + using GMap.NET.Projections; + using System.Diagnostics; + using System.Net; + using System.IO; + using System.Text.RegularExpressions; + using System.Threading; + using GMap.NET.Internals; + + public abstract class BingMapProviderBase : GMapProvider + { + public BingMapProviderBase() + { + MaxZoom = null; + RefererUrl = "http://www.bing.com/maps/"; + Copyright = string.Format("©{0} Microsoft Corporation, ©{0} NAVTEQ, ©{0} Image courtesy of NASA", DateTime.Today.Year); + } + + public string Version = "875"; + + /// + /// Bing Maps Customer Identification, more info here + /// http://msdn.microsoft.com/en-us/library/bb924353.aspx + /// + public string ClientKey = null; + + /// + /// Converts tile XY coordinates into a QuadKey at a specified level of detail. + /// + /// Tile X coordinate. + /// Tile Y coordinate. + /// Level of detail, from 1 (lowest detail) + /// to 23 (highest detail). + /// A string containing the QuadKey. + internal string TileXYToQuadKey(long tileX, long tileY, int levelOfDetail) + { + StringBuilder quadKey = new StringBuilder(); + for(int i = levelOfDetail; i > 0; i--) + { + char digit = '0'; + int mask = 1 << (i - 1); + if((tileX & mask) != 0) + { + digit++; + } + if((tileY & mask) != 0) + { + digit++; + digit++; + } + quadKey.Append(digit); + } + return quadKey.ToString(); + } + + #region GMapProvider Members + public override Guid Id + { + get + { + throw new NotImplementedException(); + } + } + + public override string Name + { + get + { + throw new NotImplementedException(); + } + } + + public override PureProjection Projection + { + get + { + return MercatorProjection.Instance; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { this }; + } + return overlays; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + throw new NotImplementedException(); + } + #endregion + + public bool TryCorrectVersion = true; + static bool init = false; + + public override void OnInitialized() + { + if(!init && TryCorrectVersion) + { + string url = @"http://www.bing.com/maps"; + + try + { + string html = GMaps.Instance.UseUrlCache ? Cache.Instance.GetContent(url, CacheType.UrlCache, TimeSpan.FromHours(8)) : string.Empty; + + if(string.IsNullOrEmpty(html)) + { + html = GetContentUsingHttp(url); + if(!string.IsNullOrEmpty(html)) + { + if(GMaps.Instance.UseUrlCache) + { + Cache.Instance.SaveContent(url, CacheType.UrlCache, html); + } + } + } + + if(!string.IsNullOrEmpty(html)) + { + #region -- match versions -- + Regex reg = new Regex("http://ecn.t(\\d*).tiles.virtualearth.net/tiles/r(\\d*)[?*]g=(\\d*)", RegexOptions.IgnoreCase); + Match mat = reg.Match(html); + if(mat.Success) + { + GroupCollection gc = mat.Groups; + int count = gc.Count; + if(count > 2) + { + string ver = gc[3].Value; + string old = GMapProviders.BingMap.Version; + if(ver != old) + { + GMapProviders.BingMap.Version = ver; + GMapProviders.BingSatelliteMap.Version = ver; + GMapProviders.BingHybridMap.Version = ver; +#if DEBUG + Debug.WriteLine("GMapProviders.BingMap.Version: " + ver + ", old: " + old + ", consider updating source"); + if(Debugger.IsAttached) + { + Thread.Sleep(5555); + } +#endif + } + else + { + Debug.WriteLine("GMapProviders.BingMap.Version: " + ver + ", OK"); + } + } + } + #endregion + } + + init = true; // try it only once + } + catch(Exception ex) + { + Debug.WriteLine("TryCorrectBingVersions failed: " + ex.ToString()); + } + } + } + + protected override bool CheckTileImageHttpResponse(System.Net.HttpWebResponse response) + { + var pass = base.CheckTileImageHttpResponse(response); + if(pass) + { + var tileInfo = response.Headers.Get("X-VE-Tile-Info"); + if(tileInfo != null) + { + return !tileInfo.Equals("no-tile"); + } + } + return pass; + } + } + + /// + /// BingMapProvider provider + /// + public class BingMapProvider : BingMapProviderBase + { + public static readonly BingMapProvider Instance; + + BingMapProvider() + { + } + + static BingMapProvider() + { + Instance = new BingMapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("D0CEB371-F10A-4E12-A2C1-DF617D6674A8"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "BingMap"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + string key = TileXYToQuadKey(pos.X, pos.Y, zoom); + return string.Format(UrlFormat, GetServerNum(pos, 4), key, Version, language, (!string.IsNullOrEmpty(ClientKey) ? "&key=" + ClientKey : string.Empty)); + } + + // http://ecn.t0.tiles.virtualearth.net/tiles/r120030?g=875&mkt=en-us&lbl=l1&stl=h&shading=hill&n=z + + static readonly string UrlFormat = "http://ecn.t{0}.tiles.virtualearth.net/tiles/r{1}?g={2}&mkt={3}&lbl=l1&stl=h&shading=hill&n=z{4}"; + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Bing/BingSatelliteMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Bing/BingSatelliteMapProvider.cs new file mode 100644 index 0000000..d124a10 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Bing/BingSatelliteMapProvider.cs @@ -0,0 +1,61 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// BingSatelliteMapProvider provider + /// + public class BingSatelliteMapProvider : BingMapProviderBase + { + public static readonly BingSatelliteMapProvider Instance; + + BingSatelliteMapProvider() + { + } + + static BingSatelliteMapProvider() + { + Instance = new BingSatelliteMapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("3AC742DD-966B-4CFB-B67D-33E7F82F2D37"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "BingSatelliteMap"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + string key = TileXYToQuadKey(pos.X, pos.Y, zoom); + return string.Format(UrlFormat, GetServerNum(pos, 4), key, Version, language, (!string.IsNullOrEmpty(ClientKey) ? "&key=" + ClientKey : string.Empty)); + } + + // http://ecn.t1.tiles.virtualearth.net/tiles/a12030003131321231.jpeg?g=875&mkt=en-us&n=z + + static readonly string UrlFormat = "http://ecn.t{0}.tiles.virtualearth.net/tiles/a{1}.jpeg?g={2}&mkt={3}&n=z{4}"; + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Czech/CzechHistoryMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Czech/CzechHistoryMapProvider.cs new file mode 100644 index 0000000..1f3abfd --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Czech/CzechHistoryMapProvider.cs @@ -0,0 +1,76 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// CzechHistoryMap provider, http://www.mapy.cz/ + /// + public class CzechHistoryMapProvider : CzechMapProviderBase + { + public static readonly CzechHistoryMapProvider Instance; + + CzechHistoryMapProvider() + { + } + + static CzechHistoryMapProvider() + { + Instance = new CzechHistoryMapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("C666AAF4-9D27-418F-97CB-7F0D8CC44544"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "CzechHistoryMap"; + public override string Name + { + get + { + return name; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { this, CzechHybridMapProvider.Instance }; + } + return overlays; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + // http://m4.mapserver.mapy.cz/army2/9_7d00000_8080000 + + long xx = pos.X << (28 - zoom); + long yy = ((((long)Math.Pow(2.0, (double)zoom)) - 1) - pos.Y) << (28 - zoom); + + return string.Format(UrlFormat, GetServerNum(pos, 3) + 1, zoom, xx, yy); + } + + static readonly string UrlFormat = "http://m{0}.mapserver.mapy.cz/army2/{1}_{2:x7}_{3:x7}"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Czech/CzechHybridMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Czech/CzechHybridMapProvider.cs new file mode 100644 index 0000000..986ddc6 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Czech/CzechHybridMapProvider.cs @@ -0,0 +1,76 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// CzechHybridMap provider, http://www.mapy.cz/ + /// + public class CzechHybridMapProvider : CzechMapProviderBase + { + public static readonly CzechHybridMapProvider Instance; + + CzechHybridMapProvider() + { + } + + static CzechHybridMapProvider() + { + Instance = new CzechHybridMapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("F785D98E-DD1D-46FD-8BC1-1AAB69604980"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "CzechHybridMap"; + public override string Name + { + get + { + return name; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { CzechSatelliteMapProvider.Instance, this }; + } + return overlays; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + // http://m2.mapserver.mapy.cz/hybrid/9_7d00000_7b80000 + + long xx = pos.X << (28 - zoom); + long yy = ((((long)Math.Pow(2.0, (double)zoom)) - 1) - pos.Y) << (28 - zoom); + + return string.Format(UrlFormat, GetServerNum(pos, 3) + 1, zoom, xx, yy); + } + + static readonly string UrlFormat = "http://m{0}.mapserver.mapy.cz/hybrid/{1}_{2:x7}_{3:x7}"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Czech/CzechMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Czech/CzechMapProvider.cs new file mode 100644 index 0000000..e08b765 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Czech/CzechMapProvider.cs @@ -0,0 +1,118 @@ + +namespace GMap.NET.MapProviders +{ + using System; + using GMap.NET.Projections; + + public abstract class CzechMapProviderBase : GMapProvider + { + public CzechMapProviderBase() + { + RefererUrl = "http://www.mapy.cz/"; + Area = new RectLatLng(51.2024819920053, 11.8401353319027, 7.22833716731277, 2.78312271922872); + } + + #region GMapProvider Members + public override Guid Id + { + get + { + throw new NotImplementedException(); + } + } + + public override string Name + { + get + { + throw new NotImplementedException(); + } + } + + public override PureProjection Projection + { + get + { + return MapyCZProjection.Instance; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { this }; + } + return overlays; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + throw new NotImplementedException(); + } + #endregion + } + + /// + /// CzechMap provider, http://www.mapy.cz/ + /// + public class CzechMapProvider : CzechMapProviderBase + { + public static readonly CzechMapProvider Instance; + + CzechMapProvider() + { + } + + static CzechMapProvider() + { + Instance = new CzechMapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("6A1AF99A-84C6-4EF6-91A5-77B9D03257C2"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "CzechMap"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + // ['base','ophoto','turist','army2'] + // http://m1.mapserver.mapy.cz/base-n/3_8000000_8000000 + + long xx = pos.X << (28 - zoom); + long yy = ((((long)Math.Pow(2.0, (double)zoom)) - 1) - pos.Y) << (28 - zoom); + + return string.Format(UrlFormat, GetServerNum(pos, 3) + 1, zoom, xx, yy); + } + + static readonly string UrlFormat = "http://m{0}.mapserver.mapy.cz/base-n/{1}_{2:x7}_{3:x7}"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Czech/CzechSatelliteMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Czech/CzechSatelliteMapProvider.cs new file mode 100644 index 0000000..c584b57 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Czech/CzechSatelliteMapProvider.cs @@ -0,0 +1,63 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// CzechSatelliteMap provider, http://www.mapy.cz/ + /// + public class CzechSatelliteMapProvider : CzechMapProviderBase + { + public static readonly CzechSatelliteMapProvider Instance; + + CzechSatelliteMapProvider() + { + } + + static CzechSatelliteMapProvider() + { + Instance = new CzechSatelliteMapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("7846D655-5F9C-4042-8652-60B6BF629C3C"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "CzechSatelliteMap"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + //http://m3.mapserver.mapy.cz/ophoto/9_7a80000_7a80000 + + long xx = pos.X << (28 - zoom); + long yy = ((((long)Math.Pow(2.0, (double)zoom)) - 1) - pos.Y) << (28 - zoom); + + return string.Format(UrlFormat, GetServerNum(pos, 3) + 1, zoom, xx, yy); + } + + static readonly string UrlFormat = "http://m{0}.mapserver.mapy.cz/ophoto/{1}_{2:x7}_{3:x7}"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Czech/CzechTuristMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Czech/CzechTuristMapProvider.cs new file mode 100644 index 0000000..5167965 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Czech/CzechTuristMapProvider.cs @@ -0,0 +1,63 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// CzechTuristMap provider, http://www.mapy.cz/ + /// + public class CzechTuristMapProvider : CzechMapProviderBase + { + public static readonly CzechTuristMapProvider Instance; + + CzechTuristMapProvider() + { + } + + static CzechTuristMapProvider() + { + Instance = new CzechTuristMapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("B923C81D-880C-42EB-88AB-AF8FE42B564D"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "CzechTuristMap"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + // http://m1.mapserver.mapy.cz/turist/3_8000000_8000000 + + long xx = pos.X << (28 - zoom); + long yy = ((((long)Math.Pow(2.0, (double)zoom)) - 1) - pos.Y) << (28 - zoom); + + return string.Format(UrlFormat, GetServerNum(pos, 3) + 1, zoom, xx, yy); + } + + static readonly string UrlFormat = "http://m{0}.mapserver.mapy.cz/turist/{1}_{2:x7}_{3:x7}"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Etc/CloudMadeMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Etc/CloudMadeMapProvider.cs new file mode 100644 index 0000000..d718553 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Etc/CloudMadeMapProvider.cs @@ -0,0 +1,527 @@ + +namespace GMap.NET.MapProviders +{ + using System; + using GMap.NET.Projections; + using System.Globalization; + using GMap.NET.Internals; + using System.Collections.Generic; + using System.Xml; + using System.Diagnostics; + + public abstract class CloudMadeMapProviderBase : GMapProvider, RoutingProvider, DirectionsProvider + { + public readonly string ServerLetters = "abc"; + public readonly string DoubleResolutionString = "@2x"; + + public bool DoubleResolution = true; + public string Key; + public int StyleID; + + public string Version = "0.3"; + + public CloudMadeMapProviderBase() + { + MaxZoom = null; + } + + #region GMapProvider Members + public override Guid Id + { + get + { + throw new NotImplementedException(); + } + } + + public override string Name + { + get + { + throw new NotImplementedException(); + } + } + + public override PureProjection Projection + { + get + { + return MercatorProjection.Instance; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { this }; + } + return overlays; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + throw new NotImplementedException(); + } + #endregion + + #region RoutingProvider Members + + public MapRoute GetRoute(PointLatLng start, PointLatLng end, bool avoidHighways, bool walkingMode, int Zoom) + { + List points = GetRoutePoints(MakeRoutingUrl(start, end, walkingMode ? TravelTypeFoot : TravelTypeMotorCar, LanguageStr, "km")); + MapRoute route = points != null ? new MapRoute(points, walkingMode ? WalkingStr : DrivingStr) : null; + return route; + } + + /// + /// NotImplemented + /// + /// + /// + /// + /// + /// + /// + public MapRoute GetRoute(string start, string end, bool avoidHighways, bool walkingMode, int Zoom) + { + throw new NotImplementedException(); + } + + #region -- internals -- + + string MakeRoutingUrl(PointLatLng start, PointLatLng end, string travelType, string language, string units) + { + // http://developers.cloudmade.com/projects/routing-http-api/examples/ + // http://routes.cloudmade.com/YOUR-API-KEY-GOES-HERE/api/0.3/start_point,[[transit_point1,...,transit_pointN]],end_point/route_type[/route_type_modifier].output_format[?lang=(en|de)][&units=(km|miles)] + return string.Format(CultureInfo.InvariantCulture, UrlFormat, Key, Version, start.Lat, start.Lng, end.Lat, end.Lng, travelType, language, units); + } + + List GetRoutePoints(string url) + { + List points = null; + try + { + string route = GMaps.Instance.UseRouteCache ? Cache.Instance.GetContent(url, CacheType.RouteCache) : string.Empty; + if(string.IsNullOrEmpty(route)) + { + route = GetContentUsingHttp(url); + if(!string.IsNullOrEmpty(route)) + { + if(GMaps.Instance.UseRouteCache) + { + Cache.Instance.SaveContent(url, CacheType.RouteCache, route); + } + } + } + + #region -- gpx response -- + // + // + // + // 293 + // + // Perckhoevelaan + // Goudenregenlaan + // + // + // + // + // + // + // + // + // + // + // + // Head south on Perckhoevelaan, 0.1 km + // + // 111 + // + // 0 + // 0.1 km + // S + // 160.6 + // + // + // + // Turn left at Laarstraat, 0.1 km + // + // 112 + // + // 3 + // 0.1 km + // NE + // 58.1 + // TL + // 269.0 + // + // + // + // Turn right at Goudenregenlaan, 70 m + // + // 70 + // + // 5 + // 70 m + // SE + // 143.4 + // TR + // 89.8 + // + // + // + // + #endregion + + if(!string.IsNullOrEmpty(route)) + { + XmlDocument xmldoc = new XmlDocument(); + xmldoc.LoadXml(route); + System.Xml.XmlNamespaceManager xmlnsManager = new System.Xml.XmlNamespaceManager(xmldoc.NameTable); + xmlnsManager.AddNamespace("sm", "http://www.topografix.com/GPX/1/1"); + + XmlNodeList wpts = xmldoc.SelectNodes("/sm:gpx/sm:wpt", xmlnsManager); + if(wpts != null && wpts.Count > 0) + { + points = new List(); + foreach(XmlNode w in wpts) + { + double lat = double.Parse(w.Attributes["lat"].InnerText, CultureInfo.InvariantCulture); + double lng = double.Parse(w.Attributes["lon"].InnerText, CultureInfo.InvariantCulture); + points.Add(new PointLatLng(lat, lng)); + } + } + } + } + catch(Exception ex) + { + Debug.WriteLine("GetRoutePoints: " + ex); + } + + return points; + } + + static readonly string UrlFormat = "http://routes.cloudmade.com/{0}/api/{1}/{2},{3},{4},{5}/{6}.gpx?lang={7}&units={8}"; + static readonly string TravelTypeFoot = "foot"; + static readonly string TravelTypeMotorCar = "car"; + static readonly string WalkingStr = "Walking"; + static readonly string DrivingStr = "Driving"; + + #endregion + + #endregion + + #region DirectionsProvider Members + + public DirectionsStatusCode GetDirections(out GDirections direction, PointLatLng start, PointLatLng end, bool avoidHighways, bool avoidTolls, bool walkingMode, bool sensor, bool metric) + { + return GetDirectionsUrl(MakeRoutingUrl(start, end, walkingMode ? TravelTypeFoot : TravelTypeMotorCar, LanguageStr, metric ? "km" : "miles"), out direction); + } + + /// + /// NotImplemented + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public DirectionsStatusCode GetDirections(out GDirections direction, string start, string end, bool avoidHighways, bool avoidTolls, bool walkingMode, bool sensor, bool metric) + { + throw new NotImplementedException(); + } + + /// + /// NotImplemented + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public IEnumerable GetDirections(out DirectionsStatusCode status, string start, string end, bool avoidHighways, bool avoidTolls, bool walkingMode, bool sensor, bool metric) + { + throw new NotImplementedException(); + } + + /// + /// NotImplemented + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public IEnumerable GetDirections(out DirectionsStatusCode status, PointLatLng start, PointLatLng end, bool avoidHighways, bool avoidTolls, bool walkingMode, bool sensor, bool metric) + { + throw new NotImplementedException(); + } + + #region -- internals -- + + DirectionsStatusCode GetDirectionsUrl(string url, out GDirections direction) + { + DirectionsStatusCode ret = DirectionsStatusCode.UNKNOWN_ERROR; + direction = null; + + try + { + string route = GMaps.Instance.UseRouteCache ? Cache.Instance.GetContent(url, CacheType.DirectionsCache) : string.Empty; + if(string.IsNullOrEmpty(route)) + { + route = GetContentUsingHttp(url); + if(!string.IsNullOrEmpty(route)) + { + if(GMaps.Instance.UseRouteCache) + { + Cache.Instance.SaveContent(url, CacheType.DirectionsCache, route); + } + } + } + + #region -- gpx response -- + // + // + // + // 293 + // + // Perckhoevelaan + // Goudenregenlaan + // + // + // + // + // + // + // + // + // + // + // + // Head south on Perckhoevelaan, 0.1 km + // + // 111 + // + // 0 + // 0.1 km + // S + // 160.6 + // + // + // + // Turn left at Laarstraat, 0.1 km + // + // 112 + // + // 3 + // 0.1 km + // NE + // 58.1 + // TL + // 269.0 + // + // + // + // Turn right at Goudenregenlaan, 70 m + // + // 70 + // + // 5 + // 70 m + // SE + // 143.4 + // TR + // 89.8 + // + // + // + // + #endregion + + if(!string.IsNullOrEmpty(route)) + { + XmlDocument xmldoc = new XmlDocument(); + xmldoc.LoadXml(route); + System.Xml.XmlNamespaceManager xmlnsManager = new System.Xml.XmlNamespaceManager(xmldoc.NameTable); + xmlnsManager.AddNamespace("sm", "http://www.topografix.com/GPX/1/1"); + + XmlNodeList wpts = xmldoc.SelectNodes("/sm:gpx/sm:wpt", xmlnsManager); + if(wpts != null && wpts.Count > 0) + { + ret = DirectionsStatusCode.OK; + + direction = new GDirections(); + direction.Route = new List(); + + foreach(XmlNode w in wpts) + { + double lat = double.Parse(w.Attributes["lat"].InnerText, CultureInfo.InvariantCulture); + double lng = double.Parse(w.Attributes["lon"].InnerText, CultureInfo.InvariantCulture); + direction.Route.Add(new PointLatLng(lat, lng)); + } + + if(direction.Route.Count > 0) + { + direction.StartLocation = direction.Route[0]; + direction.EndLocation = direction.Route[direction.Route.Count - 1]; + } + + XmlNode n = xmldoc.SelectSingleNode("/sm:gpx/sm:metadata/sm:copyright/sm:license", xmlnsManager); + if(n != null) + { + direction.Copyrights = n.InnerText; + } + + n = xmldoc.SelectSingleNode("/sm:gpx/sm:extensions/sm:distance", xmlnsManager); + if(n != null) + { + direction.Distance = n.InnerText + "m"; + } + + n = xmldoc.SelectSingleNode("/sm:gpx/sm:extensions/sm:time", xmlnsManager); + if(n != null) + { + direction.Duration = n.InnerText + "s"; + } + + n = xmldoc.SelectSingleNode("/sm:gpx/sm:extensions/sm:start", xmlnsManager); + if(n != null) + { + direction.StartAddress = n.InnerText; + } + + n = xmldoc.SelectSingleNode("/sm:gpx/sm:extensions/sm:end", xmlnsManager); + if(n != null) + { + direction.EndAddress = n.InnerText; + } + + wpts = xmldoc.SelectNodes("/sm:gpx/sm:rte/sm:rtept", xmlnsManager); + if(wpts != null && wpts.Count > 0) + { + direction.Steps = new List(); + + foreach(XmlNode w in wpts) + { + GDirectionStep step = new GDirectionStep(); + + double lat = double.Parse(w.Attributes["lat"].InnerText, CultureInfo.InvariantCulture); + double lng = double.Parse(w.Attributes["lon"].InnerText, CultureInfo.InvariantCulture); + + step.StartLocation = new PointLatLng(lat, lng); + + XmlNode nn = w.SelectSingleNode("sm:desc", xmlnsManager); + if(nn != null) + { + step.HtmlInstructions = nn.InnerText; + } + + nn = w.SelectSingleNode("sm:extensions/sm:distance-text", xmlnsManager); + if(nn != null) + { + step.Distance = nn.InnerText; + } + + nn = w.SelectSingleNode("sm:extensions/sm:time", xmlnsManager); + if(nn != null) + { + step.Duration = nn.InnerText + "s"; + } + + direction.Steps.Add(step); + } + } + } + } + } + catch(Exception ex) + { + ret = DirectionsStatusCode.ExceptionInCode; + direction = null; + Debug.WriteLine("GetDirectionsUrl: " + ex); + } + + return ret; + } + + #endregion + + #endregion + } + + /// + /// CloudMadeMap demo provider, http://maps.cloudmade.com/ + /// + public class CloudMadeMapProvider : CloudMadeMapProviderBase + { + public static readonly CloudMadeMapProvider Instance; + + CloudMadeMapProvider() + { + Key = "5937c2bd907f4f4a92d8980a7c666ac0"; // demo key of CloudMade + StyleID = 45363; // grab your style here http://maps.cloudmade.com/?styleId=45363 + } + + static CloudMadeMapProvider() + { + Instance = new CloudMadeMapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("00403A36-725F-4BC4-934F-BFC1C164D003"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "CloudMade, Demo"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + return string.Format(UrlFormat, ServerLetters[GetServerNum(pos, 3)], Key, StyleID, (DoubleResolution ? DoubleResolutionString : string.Empty), zoom, pos.X, pos.Y); + } + + static readonly string UrlFormat = "http://{0}.tile.cloudmade.com/{1}/{2}{3}/256/{4}/{5}/{6}.png"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Etc/LatviaMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Etc/LatviaMapProvider.cs new file mode 100644 index 0000000..569208d --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Etc/LatviaMapProvider.cs @@ -0,0 +1,117 @@ + +namespace GMap.NET.MapProviders +{ + using System; + using GMap.NET.Projections; + + public abstract class LatviaMapProviderBase : GMapProvider + { + public LatviaMapProviderBase() + { + RefererUrl = "http://www.ikarte.lv/map/default.aspx?lang=en"; + Copyright = string.Format("©{0} Hnit-Baltic - Map data ©{0} LR Valsts zemes dieniests, SIA Envirotech", DateTime.Today.Year); + MaxZoom = 11; + Area = new RectLatLng(58.0794870805093, 20.3286067123543, 7.90883164336887, 2.506129113082); + } + + #region GMapProvider Members + public override Guid Id + { + get + { + throw new NotImplementedException(); + } + } + + public override string Name + { + get + { + throw new NotImplementedException(); + } + } + + public override PureProjection Projection + { + get + { + return LKS92Projection.Instance; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { this }; + } + return overlays; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + throw new NotImplementedException(); + } + #endregion + } + + /// + /// LatviaMap provider, http://www.ikarte.lv/ + /// + public class LatviaMapProvider : LatviaMapProviderBase + { + public static readonly LatviaMapProvider Instance; + + LatviaMapProvider() + { + } + + static LatviaMapProvider() + { + Instance = new LatviaMapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("2A21CBB1-D37C-458D-905E-05F19536EF1F"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "LatviaMap"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + // http://www.maps.lt/cache/ikartelv/map/_alllayers/L03/R00000037/C00000053.png + // http://www.maps.lt/arcgiscache/ikartelv/map/_alllayers/L02/R0000001c/C0000002a.png + + return string.Format(UrlFormat, zoom, pos.Y, pos.X); + } + + static readonly string UrlFormat = "http://www.maps.lt/arcgiscache/ikartelv/map/_alllayers/L{0:00}/R{1:x8}/C{2:x8}.png"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Etc/MapBenderWMSProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Etc/MapBenderWMSProvider.cs new file mode 100644 index 0000000..2e581a2 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Etc/MapBenderWMSProvider.cs @@ -0,0 +1,92 @@ + +namespace GMap.NET.MapProviders +{ + using System; + using GMap.NET.Projections; + using System.Globalization; + + /// + /// MapBender provider, WMS demo http://www.mapbender.org/ + /// + public class MapBenderWMSProvider : GMapProvider + { + public static readonly MapBenderWMSProvider Instance; + + MapBenderWMSProvider() + { + } + + static MapBenderWMSProvider() + { + Instance = new MapBenderWMSProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("45742F8D-B552-4CAF-89AE-F20951BBDB2B"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "MapBender, WMS demo"; + public override string Name + { + get + { + return name; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { this }; + } + return overlays; + } + } + + public override PureProjection Projection + { + get + { + return MercatorProjection.Instance; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + var px1 = Projection.FromTileXYToPixel(pos); + var px2 = px1; + + px1.Offset(0, Projection.TileSize.Height); + PointLatLng p1 = Projection.FromPixelToLatLng(px1, zoom); + + px2.Offset(Projection.TileSize.Width, 0); + PointLatLng p2 = Projection.FromPixelToLatLng(px2, zoom); + + var ret = string.Format(CultureInfo.InvariantCulture, UrlFormat, p1.Lng, p1.Lat, p2.Lng, p2.Lat, Projection.TileSize.Width, Projection.TileSize.Height); + + return ret; + } + + static readonly string UrlFormat = "http://mapbender.wheregroup.com/cgi-bin/mapserv?map=/data/umn/osm/osm_basic.map&VERSION=1.1.1&REQUEST=GetMap&SERVICE=WMS&LAYERS=OSM_Basic&styles=&bbox={0},{1},{2},{3}&width={4}&height={5}&srs=EPSG:4326&format=image/png"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Etc/SpainMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Etc/SpainMapProvider.cs new file mode 100644 index 0000000..1eb1919 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Etc/SpainMapProvider.cs @@ -0,0 +1,93 @@ + +namespace GMap.NET.MapProviders +{ + using System; + using GMap.NET.Projections; + + /// + /// SpainMap provider, http://sigpac.mapa.es/fega/visor/ + /// + public class SpainMapProvider : GMapProvider + { + public static readonly SpainMapProvider Instance; + + SpainMapProvider() + { + Copyright = string.Format("©{0} SIGPAC", DateTime.Today.Year); + MinZoom = 5; + Area = new RectLatLng(43.8741381814747, -9.700927734375, 14.34814453125, 7.8605775962932); + } + + static SpainMapProvider() + { + Instance = new SpainMapProvider(); + } + + readonly string[] levels = + { + "0", "1", "2", "3", "4", + "MTNSIGPAC", + "MTN2000", "MTN2000", "MTN2000", "MTN2000", "MTN2000", + "MTN200", "MTN200", "MTN200", + "MTN25", "MTN25", + "ORTOFOTOS","ORTOFOTOS","ORTOFOTOS","ORTOFOTOS" + }; + + #region GMapProvider Members + + readonly Guid id = new Guid("7B70ABB0-1265-4D34-9442-F0788F4F689F"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "SpainMap"; + public override string Name + { + get + { + return name; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { this }; + } + return overlays; + } + } + + public override PureProjection Projection + { + get + { + return MercatorProjection.Instance; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + return string.Format(UrlFormat, levels[zoom], zoom, pos.X, ((2 << zoom - 1) - pos.Y - 1)); + } + + static readonly string UrlFormat = "http://sigpac.mapa.es/kmlserver/raster/{0}@3785/{1}.{2}.{3}.img"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Etc/TurkeyMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Etc/TurkeyMapProvider.cs new file mode 100644 index 0000000..ae4ffba --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Etc/TurkeyMapProvider.cs @@ -0,0 +1,99 @@ + +namespace GMap.NET.MapProviders +{ + using System; + using GMap.NET.Projections; + + /// + /// TurkeyMap provider, http://maps.pergo.com.tr/ + /// + public class TurkeyMapProvider : GMapProvider + { + public static readonly TurkeyMapProvider Instance; + + TurkeyMapProvider() + { + Copyright = string.Format("©{0} Pergo - Map data ©{0} Fideltus Advanced Technology", DateTime.Today.Year); + Area = new RectLatLng(42.5830078125, 25.48828125, 19.05029296875, 6.83349609375); + InvertedAxisY = true; + } + + static TurkeyMapProvider() + { + Instance = new TurkeyMapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("EDE895BD-756D-4BE4-8D03-D54DD8856F1D"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "TurkeyMap"; + public override string Name + { + get + { + return name; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { this }; + } + return overlays; + } + } + + public override PureProjection Projection + { + get + { + return MercatorProjection.Instance; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + // http://{domain}/{layerName}/{zoomLevel}/{first3LetterOfTileX}/{second3LetterOfTileX}/{third3LetterOfTileX}/{first3LetterOfTileY}/{second3LetterOfTileY}/{third3LetterOfTileXY}.png + + // http://map3.pergo.com.tr/tile/00/000/000/001/000/000/000.png + // That means: Zoom Level: 0 TileX: 1 TileY: 0 + + // http://domain/tile/14/000/019/371/000/011/825.png + // That means: Zoom Level: 14 TileX: 19371 TileY:11825 + + // updated version + // http://map1.pergo.com.tr/publish/tile/tile9913/06/000/000/038/000/000/039.png + + string x = pos.X.ToString(Zeros).Insert(3, Slash).Insert(7, Slash); // - 000/000/001 + string y = pos.Y.ToString(Zeros).Insert(3, Slash).Insert(7, Slash); // - 000/000/000 + + return string.Format(UrlFormat, GetServerNum(pos, 3), zoom, x, y); + } + + static readonly string Zeros = "000000000"; + static readonly string Slash = "/"; + static readonly string UrlFormat = "http://map{0}.pergo.com.tr/publish/tile/tile9913/{1:00}/{2}/{3}.png"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Etc/WikiMapiaMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Etc/WikiMapiaMapProvider.cs new file mode 100644 index 0000000..2a9a5f9 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Etc/WikiMapiaMapProvider.cs @@ -0,0 +1,127 @@ + +namespace GMap.NET.MapProviders +{ + using System; + using GMap.NET.Projections; + + public abstract class WikiMapiaMapProviderBase : GMapProvider + { + public WikiMapiaMapProviderBase() + { + MaxZoom = 22; + RefererUrl = "http://wikimapia.org/"; + Copyright = string.Format("© WikiMapia.org - Map data ©{0} WikiMapia", DateTime.Today.Year); + } + + #region GMapProvider Members + + public override Guid Id + { + get + { + throw new NotImplementedException(); + } + } + + public override string Name + { + get + { + throw new NotImplementedException(); + } + } + + public override PureProjection Projection + { + get + { + return MercatorProjection.Instance; + } + } + + public override GMapProvider[] Overlays + { + get + { + throw new NotImplementedException(); + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + throw new NotImplementedException(); + } + + #endregion + + public static int GetServerNum(GPoint pos) + { + return (int)(pos.X % 4 + (pos.Y % 4) * 4); + } + } + + /// + /// WikiMapiaMap provider, http://wikimapia.org/ + /// + public class WikiMapiaMapProvider : WikiMapiaMapProviderBase + { + public static readonly WikiMapiaMapProvider Instance; + + WikiMapiaMapProvider() + { + } + + static WikiMapiaMapProvider() + { + Instance = new WikiMapiaMapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("7974022B-1AA6-41F1-8D01-F49940E4B48C"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "WikiMapiaMap"; + public override string Name + { + get + { + return name; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { this }; + } + return overlays; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, string.Empty); + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + return string.Format(UrlFormat, GetServerNum(pos), pos.X, pos.Y, zoom); + } + + static readonly string UrlFormat = "http://i{0}.wikimapia.org/?x={1}&y={2}&zoom={3}"; + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/GMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/GMapProvider.cs new file mode 100644 index 0000000..9a10943 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/GMapProvider.cs @@ -0,0 +1,594 @@ + +namespace GMap.NET.MapProviders +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.IO; + using System.Net; + using System.Security.Cryptography; + using GMap.NET.Internals; + using GMap.NET.Projections; + using System.Text; + + /// + /// providers that are already build in + /// + public class GMapProviders + { + static GMapProviders() + { + } + + GMapProviders() + { + } + + public static readonly EmptyProvider EmptyProvider = EmptyProvider.Instance; + + public static readonly OpenStreetMapProvider OpenStreetMap = OpenStreetMapProvider.Instance; + + public static readonly OpenCycleMapProvider OpenCycleMap = OpenCycleMapProvider.Instance; + public static readonly OpenCycleLandscapeMapProvider OpenCycleLandscapeMap = OpenCycleLandscapeMapProvider.Instance; + public static readonly OpenCycleTransportMapProvider OpenCycleTransportMap = OpenCycleTransportMapProvider.Instance; + + public static readonly OpenStreetMapQuestProvider OpenStreetMapQuest = OpenStreetMapQuestProvider.Instance; + public static readonly OpenStreetMapQuestSatteliteProvider OpenStreetMapQuestSattelite = OpenStreetMapQuestSatteliteProvider.Instance; + public static readonly OpenStreetMapQuestHybridProvider OpenStreetMapQuestHybrid = OpenStreetMapQuestHybridProvider.Instance; + + public static readonly OpenSeaMapHybridProvider OpenSeaMapHybrid = OpenSeaMapHybridProvider.Instance; + +#if OpenStreetOsm + public static readonly OpenStreetOsmProvider OpenStreetOsm = OpenStreetOsmProvider.Instance; +#endif + +#if OpenStreetMapSurfer + public static readonly OpenStreetMapSurferProvider OpenStreetMapSurfer = OpenStreetMapSurferProvider.Instance; + public static readonly OpenStreetMapSurferTerrainProvider OpenStreetMapSurferTerrain = OpenStreetMapSurferTerrainProvider.Instance; +#endif + public static readonly WikiMapiaMapProvider WikiMapiaMap = WikiMapiaMapProvider.Instance; + + public static readonly BingMapProvider BingMap = BingMapProvider.Instance; + public static readonly BingSatelliteMapProvider BingSatelliteMap = BingSatelliteMapProvider.Instance; + public static readonly BingHybridMapProvider BingHybridMap = BingHybridMapProvider.Instance; + + public static readonly YahooMapProvider YahooMap = YahooMapProvider.Instance; + public static readonly YahooSatelliteMapProvider YahooSatelliteMap = YahooSatelliteMapProvider.Instance; + public static readonly YahooHybridMapProvider YahooHybridMap = YahooHybridMapProvider.Instance; + + public static readonly GoogleMapProvider GoogleMap = GoogleMapProvider.Instance; + public static readonly GoogleSatelliteMapProvider GoogleSatelliteMap = GoogleSatelliteMapProvider.Instance; + public static readonly GoogleHybridMapProvider GoogleHybridMap = GoogleHybridMapProvider.Instance; + public static readonly GoogleTerrainMapProvider GoogleTerrainMap = GoogleTerrainMapProvider.Instance; + + public static readonly GoogleChinaMapProvider GoogleChinaMap = GoogleChinaMapProvider.Instance; + public static readonly GoogleChinaSatelliteMapProvider GoogleChinaSatelliteMap = GoogleChinaSatelliteMapProvider.Instance; + public static readonly GoogleChinaHybridMapProvider GoogleChinaHybridMap = GoogleChinaHybridMapProvider.Instance; + public static readonly GoogleChinaTerrainMapProvider GoogleChinaTerrainMap = GoogleChinaTerrainMapProvider.Instance; + + public static readonly GoogleKoreaMapProvider GoogleKoreaMap = GoogleKoreaMapProvider.Instance; + public static readonly GoogleKoreaSatelliteMapProvider GoogleKoreaSatelliteMap = GoogleKoreaSatelliteMapProvider.Instance; + public static readonly GoogleKoreaHybridMapProvider GoogleKoreaHybridMap = GoogleKoreaHybridMapProvider.Instance; + + public static readonly NearMapProvider NearMap = NearMapProvider.Instance; + public static readonly NearSatelliteMapProvider NearSatelliteMap = NearSatelliteMapProvider.Instance; + public static readonly NearHybridMapProvider NearHybridMap = NearHybridMapProvider.Instance; + + public static readonly OviMapProvider OviMap = OviMapProvider.Instance; + public static readonly OviSatelliteMapProvider OviSatelliteMap = OviSatelliteMapProvider.Instance; + public static readonly OviHybridMapProvider OviHybridMap = OviHybridMapProvider.Instance; + public static readonly OviTerrainMapProvider OviTerrainMap = OviTerrainMapProvider.Instance; + + public static readonly YandexMapProvider YandexMap = YandexMapProvider.Instance; + public static readonly YandexSatelliteMapProvider YandexSatelliteMap = YandexSatelliteMapProvider.Instance; + public static readonly YandexHybridMapProvider YandexHybridMap = YandexHybridMapProvider.Instance; + + public static readonly LithuaniaMapProvider LithuaniaMap = LithuaniaMapProvider.Instance; + public static readonly Lithuania3dMapProvider Lithuania3dMap = Lithuania3dMapProvider.Instance; + public static readonly LithuaniaOrtoFotoMapProvider LithuaniaOrtoFotoMap = LithuaniaOrtoFotoMapProvider.Instance; + public static readonly LithuaniaOrtoFotoOldMapProvider LithuaniaOrtoFotoOldMap = LithuaniaOrtoFotoOldMapProvider.Instance; + public static readonly LithuaniaHybridMapProvider LithuaniaHybridMap = LithuaniaHybridMapProvider.Instance; + public static readonly LithuaniaHybridOldMapProvider LithuaniaHybridOldMap = LithuaniaHybridOldMapProvider.Instance; + + public static readonly LatviaMapProvider LatviaMap = LatviaMapProvider.Instance; + + public static readonly MapBenderWMSProvider MapBenderWMSdemoMap = MapBenderWMSProvider.Instance; + + public static readonly TurkeyMapProvider TurkeyMap = TurkeyMapProvider.Instance; + + public static readonly CloudMadeMapProvider CloudMadeMap = CloudMadeMapProvider.Instance; + + public static readonly SpainMapProvider SpainMap = SpainMapProvider.Instance; + + public static readonly CzechMapProvider CzechMap = CzechMapProvider.Instance; + public static readonly CzechSatelliteMapProvider CzechSatelliteMap = CzechSatelliteMapProvider.Instance; + public static readonly CzechHybridMapProvider CzechHybridMap = CzechHybridMapProvider.Instance; + public static readonly CzechTuristMapProvider CzechTuristMap = CzechTuristMapProvider.Instance; + public static readonly CzechHistoryMapProvider CzechHistoryMap = CzechHistoryMapProvider.Instance; + + public static readonly ArcGIS_Imagery_World_2D_MapProvider ArcGIS_Imagery_World_2D_Map = ArcGIS_Imagery_World_2D_MapProvider.Instance; + public static readonly ArcGIS_ShadedRelief_World_2D_MapProvider ArcGIS_ShadedRelief_World_2D_Map = ArcGIS_ShadedRelief_World_2D_MapProvider.Instance; + public static readonly ArcGIS_StreetMap_World_2D_MapProvider ArcGIS_StreetMap_World_2D_Map = ArcGIS_StreetMap_World_2D_MapProvider.Instance; + public static readonly ArcGIS_Topo_US_2D_MapProvider ArcGIS_Topo_US_2D_Map = ArcGIS_Topo_US_2D_MapProvider.Instance; + + public static readonly ArcGIS_World_Physical_MapProvider ArcGIS_World_Physical_Map = ArcGIS_World_Physical_MapProvider.Instance; + public static readonly ArcGIS_World_Shaded_Relief_MapProvider ArcGIS_World_Shaded_Relief_Map = ArcGIS_World_Shaded_Relief_MapProvider.Instance; + public static readonly ArcGIS_World_Street_MapProvider ArcGIS_World_Street_Map = ArcGIS_World_Street_MapProvider.Instance; + public static readonly ArcGIS_World_Terrain_Base_MapProvider ArcGIS_World_Terrain_Base_Map = ArcGIS_World_Terrain_Base_MapProvider.Instance; + public static readonly ArcGIS_World_Topo_MapProvider ArcGIS_World_Topo_Map = ArcGIS_World_Topo_MapProvider.Instance; + + public static readonly ArcGIS_DarbAE_Q2_2011_NAVTQ_Eng_V5_MapProvider ArcGIS_DarbAE_Q2_2011_NAVTQ_Eng_V5_Map = ArcGIS_DarbAE_Q2_2011_NAVTQ_Eng_V5_MapProvider.Instance; + + static List list; + + /// + /// get all instances of the supported providers + /// + public static List List + { + get + { + if(list == null) + { + list = new List(); + + Type type = typeof(GMapProviders); + foreach(var p in type.GetFields()) + { + var v = p.GetValue(null) as GMapProvider; // static classes cannot be instanced, so use null... + if(v != null) + { + list.Add(v); + } + } + } + return list; + } + } + + static Dictionary hash; + + /// + /// get hash table of all instances of the supported providers + /// + public static Dictionary Hash + { + get + { + if(hash == null) + { + hash = new Dictionary(); + + foreach(var p in List) + { + hash.Add(p.Id, p); + } + } + return hash; + } + } + + // added to find a GMapProvider by name + // 2013-07-17 DL2ALF + public static GMapProvider Find(string name) + { + foreach (GMapProvider provider in List) + { + if (name == provider.Name) + return provider; + } + return null; + } + } + + /// + /// base class for each map provider + /// + public abstract class GMapProvider + { + /// + /// unique provider id + /// + public abstract Guid Id + { + get; + } + + /// + /// provider name + /// + public abstract string Name + { + get; + } + + /// + /// provider projection + /// + public abstract PureProjection Projection + { + get; + } + + /// + /// provider overlays + /// + public abstract GMapProvider[] Overlays + { + get; + } + + /// + /// gets tile image using implmented provider + /// + /// + /// + /// + public abstract PureImage GetTileImage(GPoint pos, int zoom); + + static readonly List MapProviders = new List(); + static readonly SHA1CryptoServiceProvider HashProvider = new SHA1CryptoServiceProvider(); + + protected GMapProvider() + { + DbId = Math.Abs(BitConverter.ToInt32(HashProvider.ComputeHash(Id.ToByteArray()), 0)); + + if(MapProviders.Exists(p => p.Id == Id || p.DbId == DbId)) + { + throw new Exception("such provider id already exsists, try regenerate your provider guid..."); + } + MapProviders.Add(this); + } + + static GMapProvider() + { + WebProxy = EmptyWebProxy.Instance; + } + + bool isInitialized = false; + + /// + /// was provider initialized + /// + public bool IsInitialized + { + get + { + return isInitialized; + } + internal set + { + isInitialized = value; + } + } + + /// + /// called before first use + /// + public virtual void OnInitialized() + { + // nice place to detect current provider version + } + + /// + /// id for database, a hash of provider guid + /// + public readonly int DbId; + + /// + /// area of map + /// + public RectLatLng? Area; + + /// + /// minimum level of zoom + /// + public int MinZoom; + + /// + /// maximum level of zoom + /// + public int? MaxZoom = 17; + + /// + /// proxy for net access + /// + public static IWebProxy WebProxy; + + /// + /// NetworkCredential for tile http access + /// + public static ICredentials Credential; + + /// + /// Gets or sets the value of the User-agent HTTP header. + /// It's pseudo-randomized to avoid blockages... + /// + public static string UserAgent = string.Format("Mozilla/5.0 (Windows NT 6.1; WOW64; rv:{0}.0) Gecko/{2}{3:00}{4:00} Firefox/{0}.0.{1}", Stuff.random.Next(3, 14), Stuff.random.Next(1, 10), Stuff.random.Next(DateTime.Today.Year - 4, DateTime.Today.Year), Stuff.random.Next(12), Stuff.random.Next(30)); + + /// + /// timeout for provider connections + /// +#if !PocketPC + public static int TimeoutMs = 5 * 1000; +#else + public static int TimeoutMs = 44 * 1000; +#endif + /// + /// Gets or sets the value of the Referer HTTP header. + /// + public string RefererUrl = string.Empty; + + public string Copyright = string.Empty; + + /// + /// true if tile origin at BottomLeft, WMS-C + /// + public bool InvertedAxisY = false; + + static string languageStr = "en"; + public static string LanguageStr + { + get + { + return languageStr; + } + } + static LanguageType language = LanguageType.English; + + /// + /// map language + /// + public static LanguageType Language + { + get + { + return language; + } + set + { + language = value; + languageStr = Stuff.EnumToString(Language); + } + } + + /// + /// internal proxy for image managment + /// + public static PureImageProxy TileImageProxy; + + static readonly string requestAccept = "*/*"; + static readonly string responseContentType = "image"; + + protected virtual bool CheckTileImageHttpResponse(HttpWebResponse response) + { + //Debug.WriteLine(response.StatusCode + "/" + response.StatusDescription + "/" + response.ContentType + " -> " + response.ResponseUri); + return response.ContentType.Contains(responseContentType); + } + + protected PureImage GetTileImageUsingHttp(string url) + { + PureImage ret = null; + + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); + if(WebProxy != null) + { + request.Proxy = WebProxy; + } + + if(Credential != null) + { + request.PreAuthenticate = true; + request.Credentials = Credential; + } + + request.UserAgent = UserAgent; + request.Timeout = TimeoutMs; + request.ReadWriteTimeout = TimeoutMs * 6; + request.Accept = requestAccept; + request.Referer = RefererUrl; + + using(HttpWebResponse response = request.GetResponse() as HttpWebResponse) + { + if(CheckTileImageHttpResponse(response)) + { + MemoryStream responseStream = Stuff.CopyStream(response.GetResponseStream(), false); + { + Debug.WriteLine("Response[" + responseStream.Length + " bytes]: " + url); + + if(responseStream.Length > 0) + { + ret = TileImageProxy.FromStream(responseStream); + } + + if(ret != null) + { + ret.Data = responseStream; + ret.Data.Position = 0; + } + else + { + responseStream.Dispose(); + } + } + responseStream = null; + } + else + { + Debug.WriteLine("CheckTileImageHttpResponse[false]: " + url); + } +#if PocketPC + request.Abort(); +#endif + response.Close(); + } + return ret; + } + + protected string GetContentUsingHttp(string url) + { + string ret = string.Empty; + + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); + if(WebProxy != null) + { + request.Proxy = WebProxy; + } + + if(Credential != null) + { + request.PreAuthenticate = true; + request.Credentials = Credential; + } + + request.UserAgent = UserAgent; + request.Timeout = TimeoutMs; + request.ReadWriteTimeout = TimeoutMs * 6; + request.Accept = requestAccept; + request.Referer = RefererUrl; + + using(HttpWebResponse response = request.GetResponse() as HttpWebResponse) + { + //if(response.StatusCode == HttpStatusCode.OK) + { + using(Stream responseStream = response.GetResponseStream()) + { + using(StreamReader read = new StreamReader(responseStream, Encoding.UTF8)) + { + ret = read.ReadToEnd(); + } + } + } +#if PocketPC + request.Abort(); +#endif + response.Close(); + } + + return ret; + } + + protected static int GetServerNum(GPoint pos, int max) + { + return (int)(pos.X + 2 * pos.Y) % max; + } + + public override int GetHashCode() + { + return (int)DbId; + } + + public override bool Equals(object obj) + { + if(obj is GMapProvider) + { + return Id.Equals((obj as GMapProvider).Id); + } + return false; + } + + public static GMapProvider TryGetProvider(Guid id) + { + GMapProvider ret; + if(GMapProviders.Hash.TryGetValue(id, out ret)) + { + return ret; + } + return null; + } + + public override string ToString() + { + return Name; + } + } + + /// + /// represents empty provider + /// + public class EmptyProvider : GMapProvider + { + public static readonly EmptyProvider Instance; + + EmptyProvider() + { + MaxZoom = null; + } + + static EmptyProvider() + { + Instance = new EmptyProvider(); + } + + #region GMapProvider Members + + public override Guid Id + { + get + { + return Guid.Empty; + } + } + + readonly string name = "None"; + public override string Name + { + get + { + return name; + } + } + + readonly MercatorProjection projection = MercatorProjection.Instance; + public override PureProjection Projection + { + get + { + return projection; + } + } + + public override GMapProvider[] Overlays + { + get + { + return null; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + return null; + } + + #endregion + } + + public sealed class EmptyWebProxy : IWebProxy + { + public static readonly EmptyWebProxy Instance = new EmptyWebProxy(); + + private ICredentials m_credentials; + public ICredentials Credentials + { + get + { + return this.m_credentials; + } + set + { + this.m_credentials = value; + } + } + + public Uri GetProxy(Uri uri) + { + return uri; + } + + public bool IsBypassed(Uri uri) + { + return true; + } + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Google/China/GoogleChinaHybridMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Google/China/GoogleChinaHybridMapProvider.cs new file mode 100644 index 0000000..9c66cd8 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Google/China/GoogleChinaHybridMapProvider.cs @@ -0,0 +1,81 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// GoogleChinaHybridMap provider + /// + public class GoogleChinaHybridMapProvider : GoogleMapProviderBase + { + public static readonly GoogleChinaHybridMapProvider Instance; + + GoogleChinaHybridMapProvider() + { + RefererUrl = string.Format("http://ditu.{0}/", ServerChina); + } + + static GoogleChinaHybridMapProvider() + { + Instance = new GoogleChinaHybridMapProvider(); + } + + public string Version = "h@170"; + + #region GMapProvider Members + + readonly Guid id = new Guid("B8A2A78D-1C49-45D0-8F03-9B95C83116B7"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "GoogleChinaHybridMap"; + public override string Name + { + get + { + return name; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { GoogleChinaSatelliteMapProvider.Instance, this }; + } + return overlays; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + string sec1 = string.Empty; // after &x=... + string sec2 = string.Empty; // after &zoom=... + GetSecureWords(pos, out sec1, out sec2); + + return string.Format(UrlFormat, UrlFormatServer, GetServerNum(pos, 4), UrlFormatRequest, Version, ChinaLanguage, pos.X, sec1, pos.Y, zoom, sec2, ServerChina); + } + + static readonly string ChinaLanguage = "zh-CN"; + static readonly string UrlFormatServer = "mt"; + static readonly string UrlFormatRequest = "vt"; + static readonly string UrlFormat = "http://{0}{1}.{10}/{2}/imgtp=png32&lyrs={3}&hl={4}&gl=cn&x={5}{6}&y={7}&z={8}&s={9}"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Google/China/GoogleChinaMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Google/China/GoogleChinaMapProvider.cs new file mode 100644 index 0000000..4c53ea3 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Google/China/GoogleChinaMapProvider.cs @@ -0,0 +1,68 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// GoogleChinaMap provider + /// + public class GoogleChinaMapProvider : GoogleMapProviderBase + { + public static readonly GoogleChinaMapProvider Instance; + + GoogleChinaMapProvider() + { + RefererUrl = string.Format("http://ditu.{0}/", ServerChina); + } + + static GoogleChinaMapProvider() + { + Instance = new GoogleChinaMapProvider(); + } + + public string Version = "m@170"; + + #region GMapProvider Members + + readonly Guid id = new Guid("1213F763-64EE-4AB6-A14A-D84D6BCC3426"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "GoogleChinaMap"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + string sec1 = string.Empty; // after &x=... + string sec2 = string.Empty; // after &zoom=... + GetSecureWords(pos, out sec1, out sec2); + + return string.Format(UrlFormat, UrlFormatServer, GetServerNum(pos, 4), UrlFormatRequest, Version, ChinaLanguage, pos.X, sec1, pos.Y, zoom, sec2, ServerChina); + } + + static readonly string ChinaLanguage = "zh-CN"; + static readonly string UrlFormatServer = "mt"; + static readonly string UrlFormatRequest = "vt"; + static readonly string UrlFormat = "http://{0}{1}.{10}/{2}/lyrs={3}&hl={4}&gl=cn&x={5}{6}&y={7}&z={8}&s={9}"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Google/China/GoogleChinaSatelliteMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Google/China/GoogleChinaSatelliteMapProvider.cs new file mode 100644 index 0000000..a4eb94c --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Google/China/GoogleChinaSatelliteMapProvider.cs @@ -0,0 +1,67 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// GoogleChinaSatelliteMap provider + /// + public class GoogleChinaSatelliteMapProvider : GoogleMapProviderBase + { + public static readonly GoogleChinaSatelliteMapProvider Instance; + + GoogleChinaSatelliteMapProvider() + { + RefererUrl = string.Format("http://ditu.{0}/", ServerChina); + } + + static GoogleChinaSatelliteMapProvider() + { + Instance = new GoogleChinaSatelliteMapProvider(); + } + + public string Version = "s@104"; + + #region GMapProvider Members + + readonly Guid id = new Guid("543009AC-3379-4893-B580-DBE6372B1753"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "GoogleChinaSatelliteMap"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + string sec1 = string.Empty; // after &x=... + string sec2 = string.Empty; // after &zoom=... + GetSecureWords(pos, out sec1, out sec2); + + return string.Format(UrlFormat, UrlFormatServer, GetServerNum(pos, 4), UrlFormatRequest, Version, pos.X, sec1, pos.Y, zoom, sec2, ServerChina); + } + + static readonly string UrlFormatServer = "mt"; + static readonly string UrlFormatRequest = "vt"; + static readonly string UrlFormat = "http://{0}{1}.{9}/{2}/lyrs={3}&gl=cn&x={4}{5}&y={6}&z={7}&s={8}"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Google/China/GoogleChinaTerrainMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Google/China/GoogleChinaTerrainMapProvider.cs new file mode 100644 index 0000000..e64fce2 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Google/China/GoogleChinaTerrainMapProvider.cs @@ -0,0 +1,68 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// GoogleChinaTerrainMap provider + /// + public class GoogleChinaTerrainMapProvider : GoogleMapProviderBase + { + public static readonly GoogleChinaTerrainMapProvider Instance; + + GoogleChinaTerrainMapProvider() + { + RefererUrl = string.Format("http://ditu.{0}/", ServerChina); + } + + static GoogleChinaTerrainMapProvider() + { + Instance = new GoogleChinaTerrainMapProvider(); + } + + public string Version = "t@128,r@170"; + + #region GMapProvider Members + + readonly Guid id = new Guid("831EC3CC-B044-4097-B4B7-FC9D9F6D2CFC"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "GoogleChinaTerrainMap"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + string sec1 = string.Empty; // after &x=... + string sec2 = string.Empty; // after &zoom=... + GetSecureWords(pos, out sec1, out sec2); + + return string.Format(UrlFormat, UrlFormatServer, GetServerNum(pos, 4), UrlFormatRequest, Version, ChinaLanguage, pos.X, sec1, pos.Y, zoom, sec2, ServerChina); + } + + static readonly string ChinaLanguage = "zh-CN"; + static readonly string UrlFormatServer = "mt"; + static readonly string UrlFormatRequest = "vt"; + static readonly string UrlFormat = "http://{0}{1}.{10}/{2}/lyrs={3}&hl={4}&gl=cn&x={5}{6}&y={7}&z={8}&s={9}"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Google/GoogleHybridMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Google/GoogleHybridMapProvider.cs new file mode 100644 index 0000000..3692725 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Google/GoogleHybridMapProvider.cs @@ -0,0 +1,79 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// GoogleHybridMap provider + /// + public class GoogleHybridMapProvider : GoogleMapProviderBase + { + public static readonly GoogleHybridMapProvider Instance; + + GoogleHybridMapProvider() + { + } + + static GoogleHybridMapProvider() + { + Instance = new GoogleHybridMapProvider(); + } + + public string Version = "h@182000000"; + + #region GMapProvider Members + + readonly Guid id = new Guid("B076C255-6D12-4466-AAE0-4A73D20A7E6A"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "GoogleHybridMap"; + public override string Name + { + get + { + return name; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { GoogleSatelliteMapProvider.Instance, this }; + } + return overlays; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + string sec1 = string.Empty; // after &x=... + string sec2 = string.Empty; // after &zoom=... + GetSecureWords(pos, out sec1, out sec2); + + return string.Format(UrlFormat, UrlFormatServer, GetServerNum(pos, 4), UrlFormatRequest, Version, language, pos.X, sec1, pos.Y, zoom, sec2, Server); + } + + static readonly string UrlFormatServer = "mt"; + static readonly string UrlFormatRequest = "vt"; + static readonly string UrlFormat = "http://{0}{1}.{10}/{2}/lyrs={3}&hl={4}&x={5}{6}&y={7}&z={8}&s={9}"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Google/GoogleMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Google/GoogleMapProvider.cs new file mode 100644 index 0000000..a768f86 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Google/GoogleMapProvider.cs @@ -0,0 +1,1693 @@ + +namespace GMap.NET.MapProviders +{ + using System; + using GMap.NET.Projections; + using System.Security.Cryptography; + using System.Diagnostics; + using System.Net; + using System.IO; + using System.Text.RegularExpressions; + using System.Threading; + using GMap.NET.Internals; + using System.Collections.Generic; + using System.Globalization; + using System.Xml; + + public abstract class GoogleMapProviderBase : GMapProvider, RoutingProvider, GeocodingProvider, DirectionsProvider + { + public GoogleMapProviderBase() + { + MaxZoom = null; + RefererUrl = string.Format("http://maps.{0}/", Server); + Copyright = string.Format("©{0} Google - Map data ©{0} Tele Atlas, Imagery ©{0} TerraMetrics", DateTime.Today.Year); + } + + public readonly string Server = ThisIsLegalString("zOl/KnHzebJUqs6JWROaCQ=="); + public readonly string ServerChina = ThisIsLegalString("zOl/KnHzebLqgdc2FRlQHg=="); + public readonly string ServerKorea = ThisIsLegalString("ecw6OdJzJ/zgnFTB90qgtw=="); + public readonly string ServerKoreaKr = ThisIsLegalString("zOl/KnHzebIhmuu+tK5lbw=="); + + public string SecureWord = "Galileo"; + + /// + /// API generated using http://greatmaps.codeplex.com/ + /// from http://tinyurl.com/3q6zhcw <- http://code.server.com/intl/en-us/apis/maps/signup.html + /// + public string APIKey = @"ABQIAAAAWaQgWiEBF3lW97ifKnAczhRAzBk5Igf8Z5n2W3hNnMT0j2TikxTLtVIGU7hCLLHMAuAMt-BO5UrEWA"; + + #region GMapProvider Members + public override Guid Id + { + get + { + throw new NotImplementedException(); + } + } + + public override string Name + { + get + { + throw new NotImplementedException(); + } + } + + public override PureProjection Projection + { + get + { + return MercatorProjection.Instance; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { this }; + } + return overlays; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + throw new NotImplementedException(); + } + #endregion + + public bool TryCorrectVersion = true; + static bool init = false; + + public override void OnInitialized() + { + if(!init && TryCorrectVersion) + { + string url = string.Format("http://maps.{0}", Server); + try + { + string html = GMaps.Instance.UseUrlCache ? Cache.Instance.GetContent(url, CacheType.UrlCache, TimeSpan.FromHours(8)) : string.Empty; + + if(string.IsNullOrEmpty(html)) + { + html = GetContentUsingHttp(url); + if(!string.IsNullOrEmpty(html)) + { + if(GMaps.Instance.UseUrlCache) + { + Cache.Instance.SaveContent(url, CacheType.UrlCache, html); + } + } + } + + if(!string.IsNullOrEmpty(html)) + { + #region -- match versions -- + Regex reg = new Regex(string.Format("\"*http://mt0.{0}/vt/lyrs=m@(\\d*)", Server), RegexOptions.IgnoreCase); + Match mat = reg.Match(html); + if(mat.Success) + { + GroupCollection gc = mat.Groups; + int count = gc.Count; + if(count > 0) + { + string ver = string.Format("m@{0}", gc[1].Value); + string old = GMapProviders.GoogleMap.Version; + + GMapProviders.GoogleMap.Version = ver; + GMapProviders.GoogleChinaMap.Version = ver; +#if DEBUG + Debug.WriteLine("GMapProviders.GoogleMap.Version: " + ver + ", " + (ver == old ? "OK" : "old: " + old + ", consider updating source")); + if(Debugger.IsAttached && ver != old) + { + Thread.Sleep(5555); + } +#endif + } + } + + reg = new Regex(string.Format("\"*http://mt0.{0}/vt/lyrs=h@(\\d*)", Server), RegexOptions.IgnoreCase); + mat = reg.Match(html); + if(mat.Success) + { + GroupCollection gc = mat.Groups; + int count = gc.Count; + if(count > 0) + { + string ver = string.Format("h@{0}", gc[1].Value); + string old = GMapProviders.GoogleHybridMap.Version; + + GMapProviders.GoogleHybridMap.Version = ver; + GMapProviders.GoogleChinaHybridMap.Version = ver; +#if DEBUG + Debug.WriteLine("GMapProviders.GoogleHybridMap.Version: " + ver + ", " + (ver == old ? "OK" : "old: " + old + ", consider updating source")); + if(Debugger.IsAttached && ver != old) + { + Thread.Sleep(5555); + } +#endif + } + } + + reg = new Regex(string.Format("\"*http://khm0.{0}/kh/v=(\\d*)", Server), RegexOptions.IgnoreCase); + mat = reg.Match(html); + if(mat.Success) + { + GroupCollection gc = mat.Groups; + int count = gc.Count; + if(count > 0) + { + string ver = gc[1].Value; + string old = GMapProviders.GoogleSatelliteMap.Version; + + GMapProviders.GoogleSatelliteMap.Version = ver; + GMapProviders.GoogleKoreaSatelliteMap.Version = ver; + GMapProviders.GoogleChinaSatelliteMap.Version = "s@" + ver; +#if DEBUG + Debug.WriteLine("GMapProviders.GoogleSatelliteMap.Version: " + ver + ", " + (ver == old ? "OK" : "old: " + old + ", consider updating source")); + if(Debugger.IsAttached && ver != old) + { + Thread.Sleep(5555); + } +#endif + } + } + + reg = new Regex(string.Format("\"*http://mt0.{0}/vt/lyrs=t@(\\d*),r@(\\d*)", Server), RegexOptions.IgnoreCase); + mat = reg.Match(html); + if(mat.Success) + { + GroupCollection gc = mat.Groups; + int count = gc.Count; + if(count > 1) + { + string ver = string.Format("t@{0},r@{1}", gc[1].Value, gc[2].Value); + string old = GMapProviders.GoogleTerrainMap.Version; + + GMapProviders.GoogleTerrainMap.Version = ver; + GMapProviders.GoogleChinaTerrainMap.Version = ver; +#if DEBUG + Debug.WriteLine("GMapProviders.GoogleTerrainMap.Version: " + ver + ", " + (ver == old ? "OK" : "old: " + old + ", consider updating source")); + if(Debugger.IsAttached && ver != old) + { + Thread.Sleep(5555); + } +#endif + } + } + #endregion + } + + init = true; // try it only once + } + catch(Exception ex) + { + Debug.WriteLine("TryCorrectGoogleVersions failed: " + ex.ToString()); + } + } + } + + internal void GetSecureWords(GPoint pos, out string sec1, out string sec2) + { + sec1 = string.Empty; // after &x=... + sec2 = string.Empty; // after &zoom=... + int seclen = (int)((pos.X * 3) + pos.Y) % 8; + sec2 = SecureWord.Substring(0, seclen); + if(pos.Y >= 10000 && pos.Y < 100000) + { + sec1 = Sec1; + } + } + + static readonly string Sec1 = "&s="; + + #region -- encryption -- + static string EncryptString(string Message, string Passphrase) + { + byte[] Results; + System.Text.UTF8Encoding UTF8 = new System.Text.UTF8Encoding(); + + // Step 1. We hash the passphrase using MD5 + // We use the MD5 hash generator as the result is a 128 bit byte array + // which is a valid length for the TripleDES encoder we use below + + using(MD5CryptoServiceProvider HashProvider = new MD5CryptoServiceProvider()) + { + byte[] TDESKey = HashProvider.ComputeHash(UTF8.GetBytes(Passphrase)); + + // Step 2. Create a new TripleDESCryptoServiceProvider object + using(TripleDESCryptoServiceProvider TDESAlgorithm = new TripleDESCryptoServiceProvider()) + { + // Step 3. Setup the encoder + TDESAlgorithm.Key = TDESKey; + TDESAlgorithm.Mode = CipherMode.ECB; + TDESAlgorithm.Padding = PaddingMode.PKCS7; + + // Step 4. Convert the input string to a byte[] + byte[] DataToEncrypt = UTF8.GetBytes(Message); + + // Step 5. Attempt to encrypt the string + try + { + using(ICryptoTransform Encryptor = TDESAlgorithm.CreateEncryptor()) + { + Results = Encryptor.TransformFinalBlock(DataToEncrypt, 0, DataToEncrypt.Length); + } + } + finally + { + // Clear the TripleDes and Hashprovider services of any sensitive information + TDESAlgorithm.Clear(); + HashProvider.Clear(); + } + } + } + + // Step 6. Return the encrypted string as a base64 encoded string + return Convert.ToBase64String(Results); + } + + static string DecryptString(string Message, string Passphrase) + { + byte[] Results; + System.Text.UTF8Encoding UTF8 = new System.Text.UTF8Encoding(); + + // Step 1. We hash the passphrase using MD5 + // We use the MD5 hash generator as the result is a 128 bit byte array + // which is a valid length for the TripleDES encoder we use below + + using(MD5CryptoServiceProvider HashProvider = new MD5CryptoServiceProvider()) + { + byte[] TDESKey = HashProvider.ComputeHash(UTF8.GetBytes(Passphrase)); + + // Step 2. Create a new TripleDESCryptoServiceProvider object + using(TripleDESCryptoServiceProvider TDESAlgorithm = new TripleDESCryptoServiceProvider()) + { + // Step 3. Setup the decoder + TDESAlgorithm.Key = TDESKey; + TDESAlgorithm.Mode = CipherMode.ECB; + TDESAlgorithm.Padding = PaddingMode.PKCS7; + + // Step 4. Convert the input string to a byte[] + byte[] DataToDecrypt = Convert.FromBase64String(Message); + + // Step 5. Attempt to decrypt the string + try + { + using(ICryptoTransform Decryptor = TDESAlgorithm.CreateDecryptor()) + { + Results = Decryptor.TransformFinalBlock(DataToDecrypt, 0, DataToDecrypt.Length); + } + } + finally + { + // Clear the TripleDes and Hashprovider services of any sensitive information + TDESAlgorithm.Clear(); + HashProvider.Clear(); + } + } + } + + // Step 6. Return the decrypted string in UTF8 format + return UTF8.GetString(Results, 0, Results.Length); + } + + public static string EncryptString(string Message) + { + return EncryptString(Message, manifesto); + } + + public static string ThisIsLegalString(string Message) + { + return DecryptString(Message, manifesto); + } + + static readonly string manifesto = "GMap.NET is great and Powerful, Free, cross platform, open source .NET control."; + #endregion + + #region RoutingProvider Members + + public MapRoute GetRoute(PointLatLng start, PointLatLng end, bool avoidHighways, bool walkingMode, int Zoom) + { + string tooltip; + int numLevels; + int zoomFactor; + MapRoute ret = null; + List points = GetRoutePoints(MakeRouteUrl(start, end, LanguageStr, avoidHighways, walkingMode), Zoom, out tooltip, out numLevels, out zoomFactor); + if(points != null) + { + ret = new MapRoute(points, tooltip); + } + return ret; + } + + public MapRoute GetRoute(string start, string end, bool avoidHighways, bool walkingMode, int Zoom) + { + string tooltip; + int numLevels; + int zoomFactor; + MapRoute ret = null; + List points = GetRoutePoints(MakeRouteUrl(start, end, LanguageStr, avoidHighways, walkingMode), Zoom, out tooltip, out numLevels, out zoomFactor); + if(points != null) + { + ret = new MapRoute(points, tooltip); + } + return ret; + } + + #region -- internals -- + + string MakeRouteUrl(PointLatLng start, PointLatLng end, string language, bool avoidHighways, bool walkingMode) + { + string opt = walkingMode ? WalkingStr : (avoidHighways ? RouteWithoutHighwaysStr : RouteStr); + return string.Format(CultureInfo.InvariantCulture, RouteUrlFormatPointLatLng, language, opt, start.Lat, start.Lng, end.Lat, end.Lng, Server); + } + + string MakeRouteUrl(string start, string end, string language, bool avoidHighways, bool walkingMode) + { + string opt = walkingMode ? WalkingStr : (avoidHighways ? RouteWithoutHighwaysStr : RouteStr); + return string.Format(RouteUrlFormatStr, language, opt, start.Replace(' ', '+'), end.Replace(' ', '+'), Server); + } + + List GetRoutePoints(string url, int zoom, out string tooltipHtml, out int numLevel, out int zoomFactor) + { + List points = null; + tooltipHtml = string.Empty; + numLevel = -1; + zoomFactor = -1; + try + { + string urlEnd = url.Substring(url.IndexOf("&hl=")); + + string route = GMaps.Instance.UseRouteCache ? Cache.Instance.GetContent(urlEnd, CacheType.RouteCache) : string.Empty; + + if(string.IsNullOrEmpty(route)) + { + route = GetContentUsingHttp(url); + + if(!string.IsNullOrEmpty(route)) + { + if(GMaps.Instance.UseRouteCache) + { + Cache.Instance.SaveContent(urlEnd, CacheType.RouteCache, route); + } + } + } + + // parse values + if(!string.IsNullOrEmpty(route)) + { + //{ + //tooltipHtml:" (300\x26#160;km / 2 valandos 59 min.)", + //polylines: + //[{ + // id:"route0", + // points:"cy~rIcvp`ClJ~v@jHpu@N|BB~A?tA_@`J@nAJrB|AhEf@h@~@^pANh@Mr@a@`@_@x@cBPk@ZiBHeDQ{C]wAc@mAqCeEoA_C{@_Cy@iDoEaW}AsJcJ}t@iWowB{C_Vyw@gvGyTyjBu@gHwDoZ{W_zBsX}~BiA_MmAyOcAwOs@yNy@eTk@mVUmTE}PJ_W`@cVd@cQ`@}KjA_V`AeOn@oItAkOdAaKfBaOhDiVbD}RpBuKtEkTtP}q@fr@ypCfCmK|CmNvEqVvCuQ`BgLnAmJ`CgTpA_N~@sLlBwYh@yLp@cSj@e]zFkzKHaVViSf@wZjFwqBt@{Wr@qS`AaUjAgStBkYrEwe@xIuw@`Gmj@rFok@~BkYtCy_@|KccBvBgZjC}[tD__@pDaYjB_MpBuLhGi[fC}KfFcSnEkObFgOrFkOzEoLt[ys@tJeUlIsSbKqXtFiPfKi]rG_W|CiNhDkPfDuQlDoShEuXrEy[nOgiAxF{`@|DoVzFk[fDwPlXupA~CoPfDuQxGcd@l@yEdH{r@xDam@`AiWz@mYtAq~@p@uqAfAqx@|@kZxA}^lBq\\|Be\\lAaO~Dm`@|Gsj@tS_~AhCyUrCeZrByWv@uLlUiyDpA}NdHkn@pGmb@LkAtAoIjDqR`I{`@`BcH|I_b@zJcd@lKig@\\_CbBaIlJ}g@lIoj@pAuJtFoh@~Eqs@hDmv@h@qOfF{jBn@gSxCio@dAuQn@gIVoBjAiOlCqWbCiT`PekAzKiu@~EgYfIya@fA{ExGwWnDkMdHiU|G}R`HgQhRsa@hW}g@jVsg@|a@cbAbJkUxKoYxLa_@`IiZzHu[`DoOXsBhBuJbCwNdBaL`EkYvAwM`CeVtEwj@nDqj@BkAnB{YpGgeAn@eJ`CmYvEid@tBkQpGkd@rE}UxB}JdJo_@nDcNfSan@nS}j@lCeIvDsMbC{J|CyNbAwFfCgPz@uGvBiSdD}`@rFon@nKaqAxDmc@xBuT|Fqc@nC_PrEcUtC_MpFcT`GqQxJmXfXwq@jQgh@hBeGhG_U|BaK|G}[nRikAzIam@tDsYfE}^v@_MbAwKn@oIr@yLrBub@jAoa@b@sRdDmjBx@aZdA}XnAqVpAgTlAqPn@oGvFye@dCeRzGwb@xT_}A`BcPrAoOvCad@jAmXv@eV`BieA~@a[fBg_@`CiZ~A_OhHqk@hHcn@tEwe@rDub@nBoW~@sN|BeZnAgMvDm\\hFs^hSigArFaY`Gc\\`C}OhD}YfByQdAaNbAkOtOu~Cn@wKz@uLfCeY|CkW~B}OhCmO|AcI~A_IvDoPpEyPdImWrDuKnL_YjI{Ptl@qfAle@u|@xI}PbImQvFwMbGgOxFkOpdAosCdD_KxGsU|E}RxFcXhCwNjDwTvBiPfBqOrAyMfBcTxAaVhAwVrCy_Al@iPt@_OtA}Q`AuJ`AgIzAkK`EoUtBsJhCaKxCaKdDaKhQeg@jGiRfGaSrFyR`HsWvL}f@xp@grC`Sq|@pEsVdAoGjF{XlkAgwHxHgj@|Jex@fg@qlEjQs{AdHwh@zDkVhEkVzI_e@v}AgzHpK_l@tE}YtEy[rC}TpFme@jg@cpEbF{d@~BoXfBqUbAyOx@yN|Ao]bAo[tIazC`@iLb@aJ~AkWbBgRdBgPjA{IdCePlAmHfBmJdCiL~CuM|DoNxhDezKdDkLvBoInFqVbCuMxBqNnAeJ~CwXdBoSb^crElFsl@`Dy[zDu^xBiRzc@aaE|Fsd@vCkShDmTpG}^lD}QzDoR|zAcdHvIob@dKoj@jDmSlKiq@xVacBhEqXnBqL|Ga^zJke@`y@ktD~Mop@tP}_AdOg`AtCiQxCyOlDkPfDoN`GiTfGkRjEwLvEsL|HkQtEkJdE{HrwAkaCrT{a@rpDiuHtE_KvLuV|{AwaDzAqCb@mAf{Ac`D~FqL~y@_fBlNmZbGaNtF}Mpn@s~AlYss@dFgK|DoGhBoCrDuE~AcBtGaGnByAnDwBnCwAfDwAnFaBjGkA~[{E`iEkn@pQaDvIwBnIiCl\\qLn}J{pDhMcGrFcDhGeEvoDehC|AsArCwChBaC`C_EzC_HbBcFd@uB`@qAn@gDdB}Kz@}Hn@iPjByx@jDcvAj@}RDsEn@yTv@a]VcPtEamFBcHT_LNkEdAiShDsi@`GudAbFgx@`@iKdP}yFhBgs@p@yRjCo_AJwCXeEb@uEz@_H|@yEnBqHrCiIpAmE`o@qhBxC_IjIuVdIcXh{AgmG`i@_{BfCuLrhAssGfFeXxbBklInCsN|_AoiGpGs_@pl@w}Czy@_kEvG{]h}@ieFbQehAdHye@lPagA|Eu\\tAmI|CwWjn@mwGj@eH|]azFl@kPjAqd@jJe|DlD}vAxAeh@@eBvVk}JzIkqDfE_aBfA{YbBk[zp@e}LhAaObCeUlAuIzAeJrb@q`CjCcOnAaIpBwOtBkTjDsg@~AiPvBwOlAcH|AkIlCkLlYudApDoN`BgHhBaJvAeIvAqJbAuHrBqQbAsLx@oL`MwrCXkFr@uJh@{FhBsOvXwoB|EqVdBmHxC}KtCcJtDgKjDoIxE}JdHcMdCuDdIoKlmB}|BjJuMfFgIlE{HlEyIdEeJ~FaOvCgInCuI`EmN`J}]rEsP`EuMzCoIxGwPpi@cnAhGgPzCiJvFmRrEwQbDyOtCoPbDwTxDq\\rAsK`BgLhB{KxBoLfCgLjDqKdBqEfEkJtSy^`EcJnDuJjAwDrCeK\\}AjCaNr@qEjAaJtNaqAdCqQ`BsItS}bAbQs{@|Kor@xBmKz}@}uDze@{zAjk@}fBjTsq@r@uCd@aDFyCIwCWcCY}Aq_@w|A{AwF_DyHgHwOgu@m_BSb@nFhL", + // levels:"B?@?????@?@???A???@?@????@??@????????@????@???A????@????@??@???@??@???A???@??@???A??@???@????A??@???@??@????@??@???@????@???@??A@?@???@????A????@??@?@???@???????@??@?@????@????@?A??@???@????@??@?A??????@???????@??A???@??@???@??@????@??@?@?????@?@?A?@????@???@??@??@????@?@??@?@??@??????@???@?@????@???B???@??@??????@??@???A?????@????@???A??@??????@??@??A?@???@???@??A????@???@???@????A????@@??A???@???@??@??A????@??????@??@???@???B????@?@????????@????@????A?????@????@??A???@???@???B???@?????@???@????@????@???A???????@??A@??@?@??@@?????A?@@????????@??@?A????@?????@???@???@???@???@?@?A???@??@?@??@???@?????@???A??@???????@????@???@????@????@@???A????@?@??@?B", + // numLevels:4, + // zoomFactor:16 + //}] + //} + + #region -- title -- + int tooltipEnd = 0; + { + int x = route.IndexOf("tooltipHtml:") + 13; + if(x >= 13) + { + tooltipEnd = route.IndexOf("\"", x + 1); + if(tooltipEnd > 0) + { + int l = tooltipEnd - x; + if(l > 0) + { + tooltipHtml = route.Substring(x, l).Replace(@"\x26#160;", " "); + } + } + } + } + #endregion + + #region -- points -- + int pointsEnd = 0; + { + int x = route.IndexOf("points:", tooltipEnd >= 0 ? tooltipEnd : 0) + 8; + if(x >= 8) + { + pointsEnd = route.IndexOf("\"", x + 1); + if(pointsEnd > 0) + { + int l = pointsEnd - x; + if(l > 0) + { + /* + while(l % 5 != 0) + { + l--; + } + */ + + points = new List(); + DecodePointsInto(points, route.Substring(x, l)); + } + } + } + } + #endregion + + #region -- levels -- + string levels = string.Empty; + int levelsEnd = 0; + { + int x = route.IndexOf("levels:", pointsEnd >= 0 ? pointsEnd : 0) + 8; + if(x >= 8) + { + levelsEnd = route.IndexOf("\"", x + 1); + if(levelsEnd > 0) + { + int l = levelsEnd - x; + if(l > 0) + { + levels = route.Substring(x, l); + } + } + } + } + #endregion + + #region -- numLevel -- + int numLevelsEnd = 0; + { + int x = route.IndexOf("numLevels:", levelsEnd >= 0 ? levelsEnd : 0) + 10; + if(x >= 10) + { + numLevelsEnd = route.IndexOf(",", x); + if(numLevelsEnd > 0) + { + int l = numLevelsEnd - x; + if(l > 0) + { + numLevel = int.Parse(route.Substring(x, l)); + } + } + } + } + #endregion + + #region -- zoomFactor -- + { + int x = route.IndexOf("zoomFactor:", numLevelsEnd >= 0 ? numLevelsEnd : 0) + 11; + if(x >= 11) + { + int end = route.IndexOf("}", x); + if(end > 0) + { + int l = end - x; + if(l > 0) + { + zoomFactor = int.Parse(route.Substring(x, l)); + } + } + } + } + #endregion + + #region -- trim point overload -- + if(points != null && numLevel > 0 && !string.IsNullOrEmpty(levels)) + { + if(points.Count - levels.Length > 0) + { + points.RemoveRange(levels.Length, points.Count - levels.Length); + } + + //http://facstaff.unca.edu/mcmcclur/GoogleMaps/EncodePolyline/description.html + // + string allZlevels = "TSRPONMLKJIHGFEDCBA@?"; + if(numLevel > allZlevels.Length) + { + numLevel = allZlevels.Length; + } + + // used letters in levels string + string pLevels = allZlevels.Substring(allZlevels.Length - numLevel); + + // remove useless points at zoom + { + List removedPoints = new List(); + + for(int i = 0; i < levels.Length; i++) + { + int zi = pLevels.IndexOf(levels[i]); + if(zi > 0) + { + if(zi * numLevel > zoom) + { + removedPoints.Add(points[i]); + } + } + } + + foreach(var v in removedPoints) + { + points.Remove(v); + } + removedPoints.Clear(); + removedPoints = null; + } + } + #endregion + } + } + catch(Exception ex) + { + points = null; + Debug.WriteLine("GetRoutePoints: " + ex); + } + return points; + } + + static readonly string RouteUrlFormatPointLatLng = "http://maps.{6}/maps?f=q&output=dragdir&doflg=p&hl={0}{1}&q=&saddr=@{2},{3}&daddr=@{4},{5}"; + static readonly string RouteUrlFormatStr = "http://maps.{4}/maps?f=q&output=dragdir&doflg=p&hl={0}{1}&q=&saddr=@{2}&daddr=@{3}"; + + static readonly string WalkingStr = "&mra=ls&dirflg=w"; + static readonly string RouteWithoutHighwaysStr = "&mra=ls&dirflg=dh"; + static readonly string RouteStr = "&mra=ls&dirflg=d"; + + #endregion + + #endregion + + #region GeocodingProvider Members + + public GeoCoderStatusCode GetPoints(string keywords, out List pointList) + { + return GetLatLngFromGeocoderUrl(MakeGeocoderUrl(keywords, LanguageStr), out pointList); + } + + public PointLatLng? GetPoint(string keywords, out GeoCoderStatusCode status) + { + List pointList; + status = GetPoints(keywords, out pointList); + return pointList != null && pointList.Count > 0 ? pointList[0] : (PointLatLng?)null; + } + + public GeoCoderStatusCode GetPoints(Placemark placemark, out List pointList) + { + throw new NotImplementedException("use GetPoints(string keywords..."); + } + + public PointLatLng? GetPoint(Placemark placemark, out GeoCoderStatusCode status) + { + throw new NotImplementedException("use GetPoint(string keywords..."); + } + + public GeoCoderStatusCode GetPlacemarks(PointLatLng location, out List placemarkList) + { + return GetPlacemarkFromReverseGeocoderUrl(MakeReverseGeocoderUrl(location, LanguageStr), out placemarkList); + } + + public Placemark? GetPlacemark(PointLatLng location, out GeoCoderStatusCode status) + { + List pointList; + status = GetPlacemarks(location, out pointList); + return pointList != null && pointList.Count > 0 ? pointList[0] : (Placemark?)null; + } + + #region -- internals -- + + // TODO: switch to Geocoding API + // The Google Geocoding API: http://code.google.com/apis/maps/documentation/geocoding/ + + string MakeGeocoderUrl(string keywords, string language) + { + return string.Format(GeocoderUrlFormat, keywords.Replace(' ', '+'), language, APIKey, Server); + } + + string MakeReverseGeocoderUrl(PointLatLng pt, string language) + { + return string.Format(CultureInfo.InvariantCulture, ReverseGeocoderUrlFormat, language, pt.Lat, pt.Lng, APIKey, Server); + } + + GeoCoderStatusCode GetLatLngFromGeocoderUrl(string url, out List pointList) + { + var status = GeoCoderStatusCode.Unknow; + pointList = null; + + try + { + string urlEnd = url.Substring(url.IndexOf("geo?q=")); + + string geo = GMaps.Instance.UseGeocoderCache ? Cache.Instance.GetContent(urlEnd, CacheType.GeocoderCache) : string.Empty; + + bool cache = false; + + if(string.IsNullOrEmpty(geo)) + { + geo = GetContentUsingHttp(url); + + if(!string.IsNullOrEmpty(geo)) + { + cache = true; + } + } + + if(!string.IsNullOrEmpty(geo)) + { + if(geo.StartsWith("200")) + { + // true : 200,4,56.1451640,22.0681787 + // false: 602,0,0,0 + string[] values = geo.Split(','); + if(values.Length == 4) + { + status = (GeoCoderStatusCode)int.Parse(values[0]); + if(status == GeoCoderStatusCode.G_GEO_SUCCESS) + { + if(cache && GMaps.Instance.UseGeocoderCache) + { + Cache.Instance.SaveContent(urlEnd, CacheType.GeocoderCache, geo); + } + + double lat = double.Parse(values[2], CultureInfo.InvariantCulture); + double lng = double.Parse(values[3], CultureInfo.InvariantCulture); + + pointList = new List(); + pointList.Add(new PointLatLng(lat, lng)); + } + } + } + else if(geo.StartsWith(" + // + // + // Lithuania, Vilnius + // + // 200 + // geocode + // + // + //
Vilnius, Lithuania
+ // + // + // LT + // Lithuania + + // + // Vilnius Region + // + // Vilnius + // + // + + // + // + // + // + // + // 25.2800243,54.6893865,0 + //
+ //
+ //
+ #endregion + + XmlDocument doc = new XmlDocument(); + doc.LoadXml(geo); + + XmlNamespaceManager nsMgr = new XmlNamespaceManager(doc.NameTable); + nsMgr.AddNamespace("sm", string.Format("http://earth.{0}/kml/2.0", Server)); + nsMgr.AddNamespace("sn", "urn:oasis:names:tc:ciq:xsdschema:xAL:2.0"); + + XmlNode nn = doc.SelectSingleNode("//sm:Status/sm:code", nsMgr); + if(nn != null) + { + status = (GeoCoderStatusCode)int.Parse(nn.InnerText); + if(status == GeoCoderStatusCode.G_GEO_SUCCESS) + { + if(cache && GMaps.Instance.UseGeocoderCache) + { + Cache.Instance.SaveContent(urlEnd, CacheType.GeocoderCache, geo); + } + + pointList = new List(); + + XmlNodeList l = doc.SelectNodes("/sm:kml/sm:Response/sm:Placemark", nsMgr); + if(l != null) + { + foreach(XmlNode n in l) + { + nn = n.SelectSingleNode("sm:Point/sm:coordinates", nsMgr); + if(nn != null) + { + string[] values = nn.InnerText.Split(','); + if(values.Length >= 2) + { + double lat = double.Parse(values[1], CultureInfo.InvariantCulture); + double lng = double.Parse(values[0], CultureInfo.InvariantCulture); + + pointList.Add(new PointLatLng(lat, lng)); + } + } + } + } + } + } + } + } + } + catch(Exception ex) + { + status = GeoCoderStatusCode.ExceptionInCode; + Debug.WriteLine("GetLatLngFromGeocoderUrl: " + ex); + } + + return status; + } + + GeoCoderStatusCode GetPlacemarkFromReverseGeocoderUrl(string url, out List placemarkList) + { + GeoCoderStatusCode status = GeoCoderStatusCode.Unknow; + placemarkList = null; + + try + { + string urlEnd = url.Substring(url.IndexOf("geo?hl=")); + + string reverse = GMaps.Instance.UsePlacemarkCache ? Cache.Instance.GetContent(urlEnd, CacheType.PlacemarkCache) : string.Empty; + + bool cache = false; + + if(string.IsNullOrEmpty(reverse)) + { + reverse = GetContentUsingHttp(url); + + if(!string.IsNullOrEmpty(reverse)) + { + cache = true; + } + } + + if(!string.IsNullOrEmpty(reverse)) + { + if(reverse.StartsWith("200")) + { + if(cache && GMaps.Instance.UsePlacemarkCache) + { + Cache.Instance.SaveContent(urlEnd, CacheType.PlacemarkCache, reverse); + } + + string acc = reverse.Substring(0, reverse.IndexOf('\"')); + var ret = new Placemark(reverse.Substring(reverse.IndexOf('\"'))); + ret.Accuracy = int.Parse(acc.Split(',').GetValue(1) as string); + placemarkList = new List(); + placemarkList.Add(ret); + status = GeoCoderStatusCode.G_GEO_SUCCESS; + } + else if(reverse.StartsWith(" + // + // + // 55.023322,24.668408 + // + // 200 + // geocode + // + + // + //
4313, Širvintos 19023, Lithuania
+ // LTLithuaniaVilnius RegionŠirvintos431319023 + // + // + // + // 24.6642677,55.0239187,0 + //
+ + // + //
Širvintos 19023, Lithuania
+ // LTLithuaniaVilnius RegionŠirvintos19023 + // + // + // + // 24.6778290,55.0561428,0 + //
+ + // + //
Širvintos, Lithuania
+ // LTLithuaniaVilnius RegionŠirvintos + // + // + // + // 24.9447696,55.0482439,0 + //
+ + // + //
Vilnius Region, Lithuania
+ // LTLithuaniaVilnius Region + // + // + // + // 25.2182138,54.8086502,0 + //
+ + // + //
Lithuania
+ // LTLithuania + // + // + // + // 23.8812750,55.1694380,0 + //
+ //
+ //
+ #endregion + + XmlDocument doc = new XmlDocument(); + doc.LoadXml(reverse); + + XmlNamespaceManager nsMgr = new XmlNamespaceManager(doc.NameTable); + nsMgr.AddNamespace("sm", string.Format("http://earth.{0}/kml/2.0", Server)); + nsMgr.AddNamespace("sn", "urn:oasis:names:tc:ciq:xsdschema:xAL:2.0"); + + var codeNode = doc.SelectSingleNode("//sm:Status/sm:code", nsMgr); + if(codeNode != null) + { + status = (GeoCoderStatusCode)int.Parse(codeNode.InnerText); + if(status == GeoCoderStatusCode.G_GEO_SUCCESS) + { + if(cache && GMaps.Instance.UsePlacemarkCache) + { + Cache.Instance.SaveContent(urlEnd, CacheType.PlacemarkCache, reverse); + } + + placemarkList = new List(); + + #region -- placemarks -- + XmlNodeList l = doc.SelectNodes("/sm:kml/sm:Response/sm:Placemark", nsMgr); + if(l != null) + { + foreach(XmlNode n in l) + { + XmlNode nnd, nnl, nn; + { + nn = n.SelectSingleNode("sm:address", nsMgr); + if(nn != null) + { + var ret = new Placemark(nn.InnerText); + + nnd = n.SelectSingleNode("sn:AddressDetails", nsMgr); + if(nnd != null) + { + nn = nnd.SelectSingleNode("@Accuracy", nsMgr); + if(nn != null) + { + ret.Accuracy = int.Parse(nn.InnerText); + } + + nn = nnd.SelectSingleNode("sn:Country/sn:CountryNameCode", nsMgr); + if(nn != null) + { + ret.CountryNameCode = nn.InnerText; + } + + nn = nnd.SelectSingleNode("sn:Country/sn:CountryName", nsMgr); + if(nn != null) + { + ret.CountryName = nn.InnerText; + } + + nn = nnd.SelectSingleNode("descendant::sn:AdministrativeArea/sn:AdministrativeAreaName", nsMgr); + if(nn != null) + { + ret.AdministrativeAreaName = nn.InnerText; + } + + nn = nnd.SelectSingleNode("descendant::sn:SubAdministrativeArea/sn:SubAdministrativeAreaName", nsMgr); + if(nn != null) + { + ret.SubAdministrativeAreaName = nn.InnerText; + } + + // Locality or DependentLocality tag ? + nnl = nnd.SelectSingleNode("descendant::sn:Locality", nsMgr) ?? nnd.SelectSingleNode("descendant::sn:DependentLocality", nsMgr); + if(nnl != null) + { + nn = nnl.SelectSingleNode(string.Format("sn:{0}Name", nnl.Name), nsMgr); + if(nn != null) + { + ret.LocalityName = nn.InnerText; + } + + nn = nnl.SelectSingleNode("sn:Thoroughfare/sn:ThoroughfareName", nsMgr); + if(nn != null) + { + ret.ThoroughfareName = nn.InnerText; + } + + nn = nnl.SelectSingleNode("sn:PostalCode/sn:PostalCodeNumber", nsMgr); + if(nn != null) + { + ret.PostalCodeNumber = nn.InnerText; + } + } + } + + placemarkList.Add(ret); + } + } + } + } + #endregion + } + } + #endregion + } + } + } + catch(Exception ex) + { + status = GeoCoderStatusCode.ExceptionInCode; + placemarkList = null; + Debug.WriteLine("GetPlacemarkReverseGeocoderUrl: " + ex.ToString()); + } + + return status; + } + + static readonly string ReverseGeocoderUrlFormat = "http://maps.{4}/maps/geo?hl={0}&ll={1},{2}&output=xml&key={3}"; + static readonly string GeocoderUrlFormat = "http://maps.{3}/maps/geo?q={0}&hl={1}&output=kml&key={2}"; + + #endregion + + #endregion + + #region DirectionsProvider Members + + public DirectionsStatusCode GetDirections(out GDirections direction, PointLatLng start, PointLatLng end, bool avoidHighways, bool avoidTolls, bool walkingMode, bool sensor, bool metric) + { + return GetDirectionsUrl(MakeDirectionsUrl(start, end, LanguageStr, avoidHighways, avoidTolls, walkingMode, sensor, metric), out direction); + } + + public DirectionsStatusCode GetDirections(out GDirections direction, string start, string end, bool avoidHighways, bool avoidTolls, bool walkingMode, bool sensor, bool metric) + { + return GetDirectionsUrl(MakeDirectionsUrl(start, end, LanguageStr, avoidHighways, avoidTolls, walkingMode, sensor, metric), out direction); + } + + /// + /// NotImplemented + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public IEnumerable GetDirections(out DirectionsStatusCode status, string start, string end, bool avoidHighways, bool avoidTolls, bool walkingMode, bool sensor, bool metric) + { + // TODO: add alternative directions + + throw new NotImplementedException(); + } + + /// + /// NotImplemented + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public IEnumerable GetDirections(out DirectionsStatusCode status, PointLatLng start, PointLatLng end, bool avoidHighways, bool avoidTolls, bool walkingMode, bool sensor, bool metric) + { + // TODO: add alternative directions + + throw new NotImplementedException(); + } + + #region -- internals -- + + // The Google Directions API: http://code.google.com/apis/maps/documentation/directions/ + + string MakeDirectionsUrl(PointLatLng start, PointLatLng end, string language, bool avoidHighways, bool avoidTolls, bool walkingMode, bool sensor, bool metric) + { + string av = (avoidHighways ? "&avoid=highways" : string.Empty) + (avoidTolls ? "&avoid=tolls" : string.Empty); // 6 + + string mt = "&units=" + (metric ? "metric" : "imperial"); // 7 + string wk = "&mode=" + (walkingMode ? "walking" : "driving"); // 8 + + return string.Format(CultureInfo.InvariantCulture, DirectionUrlFormatPoint, start.Lat, start.Lng, end.Lat, end.Lng, sensor.ToString().ToLower(), language, av, mt, wk); + } + + string MakeDirectionsUrl(string start, string end, string language, bool avoidHighways, bool walkingMode, bool avoidTolls, bool sensor, bool metric) + { + string av = (avoidHighways ? "&avoid=highways" : string.Empty) + (avoidTolls ? "&avoid=tolls" : string.Empty); // 4 + string mt = "&units=" + (metric ? "metric" : "imperial"); // 5 + string wk = "&mode=" + (walkingMode ? "walking" : "driving"); // 6 + + return string.Format(DirectionUrlFormatStr, start.Replace(' ', '+'), end.Replace(' ', '+'), sensor.ToString().ToLower(), language, av, mt, wk); + } + + DirectionsStatusCode GetDirectionsUrl(string url, out GDirections direction) + { + DirectionsStatusCode ret = DirectionsStatusCode.UNKNOWN_ERROR; + direction = null; + + try + { + string urlEnd = url.Substring(url.IndexOf("xml?")); + + string kml = GMaps.Instance.UseDirectionsCache ? Cache.Instance.GetContent(urlEnd, CacheType.DirectionsCache) : string.Empty; + + bool cache = false; + + if(string.IsNullOrEmpty(kml)) + { + kml = GetContentUsingHttp(url); + if(!string.IsNullOrEmpty(kml)) + { + cache = true; + } + } + + if(!string.IsNullOrEmpty(kml)) + { + #region -- kml response -- + // + // + // OK + // + // A1/E85 + // + // + // DRIVING + // + // 54.6893800 + // 25.2800500 + // + // + // 54.6907800 + // 25.2798000 + // + // + // soxlIiohyCYLkCJ}@Vs@? + // + // + // 32 + // 1 min + // + // Head <b>north</b> on <b>Vilniaus gatvė</b> toward <b>Tilto gatvė</b> + // + // 157 + // 0.2 km + // + // + // + // DRIVING + // + // 54.6907800 + // 25.2798000 + // + // + // 54.6942500 + // 25.2621300 + // + // + // kxxlIwmhyCmApUF`@GvAYpD{@dGcCjIoIvOuAhDwAtEa@vBUnDAhB?~AThDRxAh@hBtAdC + // + // + // 133 + // 2 mins + // + // Turn <b>left</b> onto <b>A. Goštauto gatvė</b> + // + // 1326 + // 1.3 km + // + // + // + // DRIVING + // + // 54.6942500 + // 25.2621300 + // + // + // 54.6681200 + // 25.2377500 + // + // + // anylIi_eyC`AwD~@oBLKr@K`U|FdF|@`J^~E[j@Lh@\hB~Bn@tBZhBLrC?zIJ~DzA~OVrELlG^lDdAtDh@hAfApA`EzCvAp@jUpIpAl@bBpAdBpBxA|BdLpV`BxClAbBhBlBbChBpBhAdAXjBHlE_@t@?|@Lt@X + // + // + // 277 + // 5 mins + // + // Turn <b>left</b> to merge onto <b>Geležinio Vilko gatvė</b> + // + // 3806 + // 3.8 km + // + // + // + // DRIVING + // + // 54.6681200 + // 25.2377500 + // + // + // 54.6584100 + // 25.1411300 + // + // + // wjtlI}f`yC~FhBlFr@jD|A~EbC~VjNxBbBdA`BnvA|zCba@l`Bt@tDTbBJpBBfBMvDaAzF}bBjiF{HnXiHxZ + // + // + // 539 + // 9 mins + // + // Continue onto <b>Savanorių prospektas</b> + // + // 8465 + // 8.5 km + // + // + // + // DRIVING + // + // 54.6584100 + // 25.1411300 + // + // + // 54.9358200 + // 23.9260000 + // + // + // anrlIakmxCiq@|qCuBbLcK~n@wUrkAcPnw@gCnPoQt}AoB`MuAdHmAdFoCtJqClImBxE{DrIkQ|ZcEvIkDzIcDhKyBxJ{EdXuCtS_G`g@mF|\eF`WyDhOiE~NiErMaGpOoj@ppAoE|K_EzKeDtKkEnOsLnd@mDzLgI~U{FrNsEvJoEtI_FpI{J`O_EjFooBf_C{GdJ_FjIsH`OoFhMwH`UcDtL{CzMeDlQmAzHuU~bBiArIwApNaBfWaLfiCoBpYsDf\qChR_FlVqEpQ_ZbfA}CfN{A~HwCtRiAfKmBlVwBx[gBfRcBxMaLdp@sXrzAaE~UqCzRyC`[_q@z|LgC|e@m@vNqp@b}WuLraFo@jPaS~bDmJryAeo@v|G}CnWsm@~`EoKvo@kv@lkEkqBrlKwBvLkNj|@cu@`~EgCnNuiBpcJakAx|GyB`KqdC~fKoIfYicAxtCiDrLu@hDyBjQm@xKoGdxBmQhoGuUn|Dc@nJ[`OW|VaEn|Ee@`X + // + // + // 3506 + // 58 mins + // + // Continue onto <b>A1/E85</b> + // + // 85824 + // 85.8 km + // + // + // + // DRIVING + // + // 54.9358200 + // 23.9260000 + // + // + // 54.9376500 + // 23.9195600 + // + // + // {shnIo``qCQ^MnD[lBgA`DqBdEu@xB}@zJCjB + // + // + // 39 + // 1 min + // + // Take the exit toward <b>Senamiestis/Aleksotas</b> + // + // 476 + // 0.5 km + // + // + // + // DRIVING + // + // 54.9376500 + // 23.9195600 + // + // + // 54.9361300 + // 23.9189700 + // + // + // i_inIgx~pCnHtB + // + // + // 28 + // 1 min + // + // Turn <b>left</b> onto <b>Kleboniškio gatvė</b> + // + // 173 + // 0.2 km + // + // + // + // DRIVING + // + // 54.9361300 + // 23.9189700 + // + // + // 54.9018900 + // 23.8937000 + // + // + // yuhnIqt~pCvAb@JLrOvExSdHvDdAv`@pIpHnAdl@hLdB`@nDvAtEjDdCvCjLzOvAzBhC`GpHfRbQd^`JpMPt@ClA + // + // + // 412 + // 7 mins + // + // Continue onto <b>Jonavos gatvė</b> + // + // 4302 + // 4.3 km + // + // + // + // DRIVING + // + // 54.9018900 + // 23.8937000 + // + // + // 54.8985600 + // 23.8933400 + // + // + // y_bnIsvypCMf@FnARlAf@zAl@^v@EZ_@pAe@x@k@xBPpA@pAQNSf@oB + // + // + // 69 + // 1 min + // + // At the roundabout, take the <b>3rd</b> exit and stay on <b>Jonavos gatvė</b> + // + // 478 + // 0.5 km + // + // + // + // DRIVING + // + // 54.8985600 + // 23.8933400 + // + // + // 54.8968500 + // 23.8930000 + // + // + // _kanIktypCbEx@pCH + // + // + // 38 + // 1 min + // + // Turn <b>right</b> onto <b>A. Mapu gatvė</b><div style="font-size:0.9em">Destination will be on the right</div> + // + // 192 + // 0.2 km + // + // + // + // 5073 + // 1 hour 25 mins + // + // + // 105199 + // 105 km + // + // + // 54.6893800 + // 25.2800500 + // + // + // 54.8968500 + // 23.8930000 + // + // Vilnius, Lithuania + // Kaunas, Lithuania + // + // Map data ©2011 Tele Atlas + // + // soxlIiohyCYL}Fb@mApUF`@GvAYpD{@dGcCjIoIvOwBpFy@xC]jBSxCC~E^~Er@lCtAdC`AwD~@oB`AW`U|FdF|@`J^~E[tAj@hB~BjA~ELrCJzOzA~Od@`N^lDdAtDt@xAjAnApDlCbXbKpAl@bBpAdBpBxA|BdLpV`BxCvDpEbChBpBhAdAXjBHbG_@|@LtHbClFr@jK`F~VjNxBbB`@h@rwAt|Cba@l`BjAxGNxEMvDaAzF}bBjiFcFbQ_y@|gD{CxMeBnJcK~n@wh@dkCkAlIoQt}AeEfV}EzQqClImBxE{DrIkQ|ZcEvIkDzIcDhKyBxJ{EdXuCtS_G`g@mF|\eF`WyDhOiE~NiErMaGpOoj@ppAoE|K_EzKeDtKmXzbAgI~U{FrNsEvJoLfT{J`O_EjFooBf_C{GdJkLtSwI`SyClI}CrJcDtL{CzMeDlQcXzlBiArIwApNaBfWaLfiCoBpYsDf\qChR_FlVqEpQ_ZbfAyFfXwCtRiAfKeFfs@gBfRcBxMaLdp@sXrzAaE~UqCzRyC`[_q@z|LuDtu@qp@b}WuLraFo@jPo^r}Faq@pfHaBtMsm@~`EoKvo@kv@lkEcuBjzKkNj|@cu@`~EgCnNuiBpcJakAx|GyB`KqdC~fKoIfYidAbwCoD|MeAbHcA|Im@xK}YnhKyV~gEs@~f@aEn|Ee@`XQ^MnD[lBoF`N}@zJCjBfKxCJLdj@bQv`@pIpHnAdl@hLdB`@nDvAtEjDdCvCbOvSzLhZbQd^`JpMPt@QtBFnAz@hDl@^j@?f@e@pAe@x@k@xBPfCEf@Uj@wBbEx@pCH + // + // + // + // 54.6389500 + // 23.8920900 + // + // + // 54.9376500 + // 25.2800500 + // + // + // + // + #endregion + + XmlDocument doc = new XmlDocument(); + doc.LoadXml(kml); + + XmlNode nn = doc.SelectSingleNode("/DirectionsResponse/status"); + if(nn != null) + { + ret = (DirectionsStatusCode)Enum.Parse(typeof(DirectionsStatusCode), nn.InnerText, false); + if(ret == DirectionsStatusCode.OK) + { + if(cache && GMaps.Instance.UseDirectionsCache) + { + Cache.Instance.SaveContent(urlEnd, CacheType.DirectionsCache, kml); + } + + direction = new GDirections(); + + nn = doc.SelectSingleNode("/DirectionsResponse/route/summary"); + if(nn != null) + { + direction.Summary = nn.InnerText; + Debug.WriteLine("summary: " + direction.Summary); + } + + nn = doc.SelectSingleNode("/DirectionsResponse/route/leg/duration"); + if(nn != null) + { + nn = nn.SelectSingleNode("text"); + if(nn != null) + { + direction.Duration = nn.InnerText; + Debug.WriteLine("duration: " + direction.Duration); + } + } + + nn = doc.SelectSingleNode("/DirectionsResponse/route/leg/distance"); + if(nn != null) + { + nn = nn.SelectSingleNode("text"); + if(nn != null) + { + direction.Distance = nn.InnerText; + Debug.WriteLine("distance: " + direction.Distance); + } + } + + nn = doc.SelectSingleNode("/DirectionsResponse/route/leg/start_location"); + if(nn != null) + { + var pt = nn.SelectSingleNode("lat"); + if(pt != null) + { + direction.StartLocation.Lat = double.Parse(pt.InnerText, CultureInfo.InvariantCulture); + } + + pt = nn.SelectSingleNode("lng"); + if(pt != null) + { + direction.StartLocation.Lng = double.Parse(pt.InnerText, CultureInfo.InvariantCulture); + } + } + + nn = doc.SelectSingleNode("/DirectionsResponse/route/leg/end_location"); + if(nn != null) + { + var pt = nn.SelectSingleNode("lat"); + if(pt != null) + { + direction.EndLocation.Lat = double.Parse(pt.InnerText, CultureInfo.InvariantCulture); + } + + pt = nn.SelectSingleNode("lng"); + if(pt != null) + { + direction.EndLocation.Lng = double.Parse(pt.InnerText, CultureInfo.InvariantCulture); + } + } + + nn = doc.SelectSingleNode("/DirectionsResponse/route/leg/start_address"); + if(nn != null) + { + direction.StartAddress = nn.InnerText; + Debug.WriteLine("start_address: " + direction.StartAddress); + } + + nn = doc.SelectSingleNode("/DirectionsResponse/route/leg/end_address"); + if(nn != null) + { + direction.EndAddress = nn.InnerText; + Debug.WriteLine("end_address: " + direction.EndAddress); + } + + nn = doc.SelectSingleNode("/DirectionsResponse/route/copyrights"); + if(nn != null) + { + direction.Copyrights = nn.InnerText; + Debug.WriteLine("copyrights: " + direction.Copyrights); + } + + nn = doc.SelectSingleNode("/DirectionsResponse/route/overview_polyline/points"); + if(nn != null) + { + direction.Route = new List(); + DecodePointsInto(direction.Route, nn.InnerText); + } + + XmlNodeList steps = doc.SelectNodes("/DirectionsResponse/route/leg/step"); + if(steps != null) + { + if(steps.Count > 0) + { + direction.Steps = new List(); + } + + foreach(XmlNode s in steps) + { + GDirectionStep step = new GDirectionStep(); + + Debug.WriteLine("----------------------"); + nn = s.SelectSingleNode("travel_mode"); + if(nn != null) + { + step.TravelMode = nn.InnerText; + Debug.WriteLine("travel_mode: " + step.TravelMode); + } + + nn = s.SelectSingleNode("start_location"); + if(nn != null) + { + var pt = nn.SelectSingleNode("lat"); + if(pt != null) + { + step.StartLocation.Lat = double.Parse(pt.InnerText, CultureInfo.InvariantCulture); + } + + pt = nn.SelectSingleNode("lng"); + if(pt != null) + { + step.StartLocation.Lng = double.Parse(pt.InnerText, CultureInfo.InvariantCulture); + } + } + + nn = s.SelectSingleNode("end_location"); + if(nn != null) + { + var pt = nn.SelectSingleNode("lat"); + if(pt != null) + { + step.EndLocation.Lat = double.Parse(pt.InnerText, CultureInfo.InvariantCulture); + } + + pt = nn.SelectSingleNode("lng"); + if(pt != null) + { + step.EndLocation.Lng = double.Parse(pt.InnerText, CultureInfo.InvariantCulture); + } + } + + nn = s.SelectSingleNode("duration"); + if(nn != null) + { + nn = nn.SelectSingleNode("text"); + if(nn != null) + { + step.Duration = nn.InnerText; + Debug.WriteLine("duration: " + step.Duration); + } + } + + nn = s.SelectSingleNode("distance"); + if(nn != null) + { + nn = nn.SelectSingleNode("text"); + if(nn != null) + { + step.Distance = nn.InnerText; + Debug.WriteLine("distance: " + step.Distance); + } + } + + nn = s.SelectSingleNode("html_instructions"); + if(nn != null) + { + step.HtmlInstructions = nn.InnerText; + Debug.WriteLine("html_instructions: " + step.HtmlInstructions); + } + + nn = s.SelectSingleNode("polyline"); + if(nn != null) + { + nn = nn.SelectSingleNode("points"); + if(nn != null) + { + step.Points = new List(); + DecodePointsInto(step.Points, nn.InnerText); + } + } + + direction.Steps.Add(step); + } + } + } + } + } + } + catch(Exception ex) + { + direction = null; + ret = DirectionsStatusCode.ExceptionInCode; + Debug.WriteLine("GetDirectionsUrl: " + ex); + } + return ret; + } + + static void DecodePointsInto(List list, string encodedPoints) + { + // http://tinyurl.com/3ds3scr + // http://code.server.com/apis/maps/documentation/polylinealgorithm.html + // + string encoded = encodedPoints.Replace("\\\\", "\\"); + { + int len = encoded.Length; + int index = 0; + double dlat = 0; + double dlng = 0; + + while(index < len) + { + int b; + int shift = 0; + int result = 0; + + do + { + b = encoded[index++] - 63; + result |= (b & 0x1f) << shift; + shift += 5; + + } while(b >= 0x20 && index < len); + + dlat += ((result & 1) == 1 ? ~(result >> 1) : (result >> 1)); + + shift = 0; + result = 0; + + if(index < len) + { + do + { + b = encoded[index++] - 63; + result |= (b & 0x1f) << shift; + shift += 5; + } + while(b >= 0x20 && index < len); + + dlng += ((result & 1) == 1 ? ~(result >> 1) : (result >> 1)); + + list.Add(new PointLatLng(dlat * 1e-5, dlng * 1e-5)); + } + } + } + } + + static readonly string DirectionUrlFormatStr = "http://maps.googleapis.com/maps/api/directions/xml?origin={0}&destination={1}&sensor={2}&language={3}{4}{5}{6}"; + static readonly string DirectionUrlFormatPoint = "http://maps.googleapis.com/maps/api/directions/xml?origin={0},{1}&destination={2},{3}&sensor={4}&language={5}{6}{7}{8}"; + + #endregion + + #endregion + } + + /// + /// GoogleMap provider + /// + public class GoogleMapProvider : GoogleMapProviderBase + { + public static readonly GoogleMapProvider Instance; + + GoogleMapProvider() + { + } + + static GoogleMapProvider() + { + Instance = new GoogleMapProvider(); + } + + public string Version = "m@182000000"; + + #region GMapProvider Members + + readonly Guid id = new Guid("D7287DA0-A7FF-405F-8166-B6BAF26D066C"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "GoogleMap"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + string sec1 = string.Empty; // after &x=... + string sec2 = string.Empty; // after &zoom=... + GetSecureWords(pos, out sec1, out sec2); + + return string.Format(UrlFormat, UrlFormatServer, GetServerNum(pos, 4), UrlFormatRequest, Version, language, pos.X, sec1, pos.Y, zoom, sec2, Server); + } + + static readonly string UrlFormatServer = "mt"; + static readonly string UrlFormatRequest = "vt"; + static readonly string UrlFormat = "http://{0}{1}.{10}/{2}/lyrs={3}&hl={4}&x={5}{6}&y={7}&z={8}&s={9}"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Google/GoogleSatelliteMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Google/GoogleSatelliteMapProvider.cs new file mode 100644 index 0000000..af6ae33 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Google/GoogleSatelliteMapProvider.cs @@ -0,0 +1,66 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// GoogleSatelliteMap provider + /// + public class GoogleSatelliteMapProvider : GoogleMapProviderBase + { + public static readonly GoogleSatelliteMapProvider Instance; + + GoogleSatelliteMapProvider() + { + } + + static GoogleSatelliteMapProvider() + { + Instance = new GoogleSatelliteMapProvider(); + } + + public string Version = "115"; + + #region GMapProvider Members + + readonly Guid id = new Guid("9CB89D76-67E9-47CF-8137-B9EE9FC46388"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "GoogleSatelliteMap"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + string sec1 = string.Empty; // after &x=... + string sec2 = string.Empty; // after &zoom=... + GetSecureWords(pos, out sec1, out sec2); + + return string.Format(UrlFormat, UrlFormatServer, GetServerNum(pos, 4), UrlFormatRequest, Version, language, pos.X, sec1, pos.Y, zoom, sec2, Server); + } + + static readonly string UrlFormatServer = "khm"; + static readonly string UrlFormatRequest = "kh"; + static readonly string UrlFormat = "http://{0}{1}.{10}/{2}/v={3}&hl={4}&x={5}{6}&y={7}&z={8}&s={9}"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Google/GoogleTerrainMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Google/GoogleTerrainMapProvider.cs new file mode 100644 index 0000000..617ddde --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Google/GoogleTerrainMapProvider.cs @@ -0,0 +1,66 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// GoogleTerrainMap provider + /// + public class GoogleTerrainMapProvider : GoogleMapProviderBase + { + public static readonly GoogleTerrainMapProvider Instance; + + GoogleTerrainMapProvider() + { + } + + static GoogleTerrainMapProvider() + { + Instance = new GoogleTerrainMapProvider(); + } + + public string Version = "t@129,r@182000000"; + + #region GMapProvider Members + + readonly Guid id = new Guid("A42EDF2E-63C5-4967-9DBF-4EFB3AF7BC11"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "GoogleTerrainMap"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + string sec1 = string.Empty; // after &x=... + string sec2 = string.Empty; // after &zoom=... + GetSecureWords(pos, out sec1, out sec2); + + return string.Format(UrlFormat, UrlFormatServer, GetServerNum(pos, 4), UrlFormatRequest, Version, language, pos.X, sec1, pos.Y, zoom, sec2, Server); + } + + static readonly string UrlFormatServer = "mt"; + static readonly string UrlFormatRequest = "vt"; + static readonly string UrlFormat = "http://{0}{1}.{10}/{2}/v={3}&hl={4}&x={5}{6}&y={7}&z={8}&s={9}"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Google/Korea/GoogleKoreaHybridMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Google/Korea/GoogleKoreaHybridMapProvider.cs new file mode 100644 index 0000000..3938708 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Google/Korea/GoogleKoreaHybridMapProvider.cs @@ -0,0 +1,79 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// GoogleKoreaHybridMap provider + /// + public class GoogleKoreaHybridMapProvider : GoogleMapProviderBase + { + public static readonly GoogleKoreaHybridMapProvider Instance; + + GoogleKoreaHybridMapProvider() + { + } + + static GoogleKoreaHybridMapProvider() + { + Instance = new GoogleKoreaHybridMapProvider(); + } + + public string Version = "kr1t.12"; + + #region GMapProvider Members + + readonly Guid id = new Guid("41A91842-04BC-442B-9AC8-042156238A5B"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "GoogleKoreaHybridMap"; + public override string Name + { + get + { + return name; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { GoogleKoreaSatelliteMapProvider.Instance, this }; + } + return overlays; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + string sec1 = string.Empty; + string sec2 = string.Empty; + GetSecureWords(pos, out sec1, out sec2); + + return string.Format(UrlFormat, UrlFormatServer, GetServerNum(pos, 4), UrlFormatRequest, Version, language, pos.X, sec1, pos.Y, zoom, sec2, ServerKorea); + } + + static readonly string UrlFormatServer = "mt"; + static readonly string UrlFormatRequest = "mt"; + static readonly string UrlFormat = "http://{0}{1}.{10}/{2}/v={3}&hl={4}&x={5}{6}&y={7}&z={8}&s={9}"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Google/Korea/GoogleKoreaMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Google/Korea/GoogleKoreaMapProvider.cs new file mode 100644 index 0000000..de97122 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Google/Korea/GoogleKoreaMapProvider.cs @@ -0,0 +1,67 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// GoogleKoreaMap provider + /// + public class GoogleKoreaMapProvider : GoogleMapProviderBase + { + public static readonly GoogleKoreaMapProvider Instance; + + GoogleKoreaMapProvider() + { + Area = new RectLatLng(38.6597777307125, 125.738525390625, 4.02099609375, 4.42072406219614); + } + + static GoogleKoreaMapProvider() + { + Instance = new GoogleKoreaMapProvider(); + } + + public string Version = "kr1.12"; + + #region GMapProvider Members + + readonly Guid id = new Guid("0079D360-CB1B-4986-93D5-AD299C8E20E6"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "GoogleKoreaMap"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + string sec1 = string.Empty; + string sec2 = string.Empty; + GetSecureWords(pos, out sec1, out sec2); + + return string.Format(UrlFormat, UrlFormatServer, GetServerNum(pos, 4), UrlFormatRequest, Version, language, pos.X, sec1, pos.Y, zoom, sec2, ServerKorea); + } + + static readonly string UrlFormatServer = "mt"; + static readonly string UrlFormatRequest = "mt"; + static readonly string UrlFormat = "http://{0}{1}.{10}/{2}/v={3}&hl={4}&x={5}{6}&y={7}&z={8}&s={9}"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Google/Korea/GoogleKoreaSatelliteMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Google/Korea/GoogleKoreaSatelliteMapProvider.cs new file mode 100644 index 0000000..eff7700 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Google/Korea/GoogleKoreaSatelliteMapProvider.cs @@ -0,0 +1,66 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// GoogleKoreaSatelliteMap provider + /// + public class GoogleKoreaSatelliteMapProvider : GoogleMapProviderBase + { + public static readonly GoogleKoreaSatelliteMapProvider Instance; + + GoogleKoreaSatelliteMapProvider() + { + } + + static GoogleKoreaSatelliteMapProvider() + { + Instance = new GoogleKoreaSatelliteMapProvider(); + } + + public string Version = "104"; + + #region GMapProvider Members + + readonly Guid id = new Guid("70370941-D70C-4123-BE4A-AEE6754047F5"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "GoogleKoreaSatelliteMap"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + string sec1 = string.Empty; + string sec2 = string.Empty; + GetSecureWords(pos, out sec1, out sec2); + + return string.Format(UrlFormat, UrlFormatServer, GetServerNum(pos, 4), UrlFormatRequest, Version, language, pos.X, sec1, pos.Y, zoom, sec2, ServerKoreaKr); + } + + static readonly string UrlFormatServer = "khm"; + static readonly string UrlFormatRequest = "kh"; + static readonly string UrlFormat = "http://{0}{1}.{10}/{2}/v={3}&hl={4}&x={5}{6}&y={7}&z={8}&s={9}"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Lithuania/Lithuania3dMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Lithuania/Lithuania3dMapProvider.cs new file mode 100644 index 0000000..2b6df4c --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Lithuania/Lithuania3dMapProvider.cs @@ -0,0 +1,65 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// Lithuania3dMap (2.5d) provider + /// + public class Lithuania3dMapProvider : LithuaniaMapProviderBase + { + public static readonly Lithuania3dMapProvider Instance; + + Lithuania3dMapProvider() + { + } + + static Lithuania3dMapProvider() + { + Instance = new Lithuania3dMapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("CCC5B65F-C8BC-47CE-B39D-5E262E6BF083"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "Lithuania 2.5d Map"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + // http://dc1.maps.lt/cache/mapslt_25d_vkkp/map/_alllayers/L01/R00007194/C0000a481.png + int z = zoom; + if(zoom >= 10) + { + z -= 10; + } + + return string.Format(UrlFormat, z, pos.Y, pos.X); + } + + static readonly string UrlFormat = "http://dc1.maps.lt/cache/mapslt_25d_vkkp/map/_alllayers/L{0:00}/R{1:x8}/C{2:x8}.png"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Lithuania/LithuaniaHybridMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Lithuania/LithuaniaHybridMapProvider.cs new file mode 100644 index 0000000..508353a --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Lithuania/LithuaniaHybridMapProvider.cs @@ -0,0 +1,75 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// LithuaniaHybridMap provider + /// + public class LithuaniaHybridMapProvider : LithuaniaMapProviderBase + { + public static readonly LithuaniaHybridMapProvider Instance; + + LithuaniaHybridMapProvider() + { + } + + static LithuaniaHybridMapProvider() + { + Instance = new LithuaniaHybridMapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("279AB0E0-4704-4AA6-86AD-87D13B1F8975"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "LithuaniaHybridMap"; + public override string Name + { + get + { + return name; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { LithuaniaOrtoFotoMapProvider.Instance, this }; + } + return overlays; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + //http://arcgis.maps.lt/ArcGIS/rest/services/mapslt_ortofoto_overlay/MapServer/tile/0/9/13 + //return string.Format("http://arcgis.maps.lt/ArcGIS/rest/services/mapslt_ortofoto_overlay/MapServer/tile/{0}/{1}/{2}", zoom, pos.Y, pos.X); + //http://dc1.maps.lt/cache/mapslt_ortofoto_overlay_512/map/_alllayers/L03/R0000001d/C00000029.png + + return string.Format(UrlFormat, zoom, pos.Y, pos.X); + } + + internal static readonly string UrlFormat = "http://dc1.maps.lt/cache/mapslt_ortofoto_overlay/map/_alllayers/L{0:00}/R{1:x8}/C{2:x8}.png"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Lithuania/LithuaniaHybridOldMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Lithuania/LithuaniaHybridOldMapProvider.cs new file mode 100644 index 0000000..356d3bc --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Lithuania/LithuaniaHybridOldMapProvider.cs @@ -0,0 +1,57 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// LithuaniaHybridNewMap, from 2005 data, provider + /// + public class LithuaniaHybridOldMapProvider : LithuaniaMapProviderBase + { + public static readonly LithuaniaHybridOldMapProvider Instance; + + LithuaniaHybridOldMapProvider() + { + } + + static LithuaniaHybridOldMapProvider() + { + Instance = new LithuaniaHybridOldMapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("35C5C685-E868-4AC7-97BE-10A9A37A81B5"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "LithuaniaHybridMapOld"; + public override string Name + { + get + { + return name; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { LithuaniaOrtoFotoOldMapProvider.Instance, LithuaniaHybridMapProvider.Instance }; + } + return overlays; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Lithuania/LithuaniaMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Lithuania/LithuaniaMapProvider.cs new file mode 100644 index 0000000..09d9f65 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Lithuania/LithuaniaMapProvider.cs @@ -0,0 +1,123 @@ + +namespace GMap.NET.MapProviders +{ + using System; + using GMap.NET.Projections; + + public abstract class LithuaniaMapProviderBase : GMapProvider + { + public LithuaniaMapProviderBase() + { + RefererUrl = "http://www.maps.lt/map/"; + Copyright = string.Format("©{0} Hnit-Baltic - Map data ©{0} ESRI", DateTime.Today.Year); + MaxZoom = 12; + Area = new RectLatLng(56.431489960361, 20.8962105239809, 5.8924169643369, 2.58940626652217); + } + + #region GMapProvider Members + public override Guid Id + { + get + { + throw new NotImplementedException(); + } + } + + public override string Name + { + get + { + throw new NotImplementedException(); + } + } + + public override PureProjection Projection + { + get + { + return LKS94Projection.Instance; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { this }; + } + return overlays; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + throw new NotImplementedException(); + } + #endregion + } + + /// + /// LithuaniaMap provider, http://www.maps.lt/map/ + /// + public class LithuaniaMapProvider : LithuaniaMapProviderBase + { + public static readonly LithuaniaMapProvider Instance; + + LithuaniaMapProvider() + { + } + + static LithuaniaMapProvider() + { + Instance = new LithuaniaMapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("5859079F-1B5E-484B-B05C-41CE664D8A93"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "LithuaniaMap"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + // old stuff + // http://www.maps.lt/ortofoto/mapslt_ortofoto_vector_512/map/_alllayers/L02/R0000001b/C00000028.jpg + // http://arcgis.maps.lt/ArcGIS/rest/services/mapslt_ortofoto/MapServer/tile/0/9/13 + // return string.Format("http://www.maps.lt/ortofoto/mapslt_ortofoto_vector_512/map/_alllayers/L{0:00}/R{1:x8}/C{2:x8}.jpg", zoom, pos.Y, pos.X); + // http://arcgis.maps.lt/ArcGIS/rest/services/mapslt/MapServer/tile/7/1162/1684.png + // http://dc1.maps.lt/cache/mapslt_512/map/_alllayers/L03/R0000001b/C00000029.png + + // http://dc1.maps.lt/cache/mapslt/map/_alllayers/L02/R0000001c/C00000029.png + + return string.Format(UrlFormat, zoom, pos.Y, pos.X); + } + + static readonly string UrlFormat = "http://dc1.maps.lt/cache/mapslt/map/_alllayers/L{0:00}/R{1:x8}/C{2:x8}.png"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Lithuania/LithuaniaOrtoFotoMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Lithuania/LithuaniaOrtoFotoMapProvider.cs new file mode 100644 index 0000000..153f13b --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Lithuania/LithuaniaOrtoFotoMapProvider.cs @@ -0,0 +1,66 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// LithuaniaOrtoFotoMap provider + /// + public class LithuaniaOrtoFotoMapProvider : LithuaniaMapProviderBase + { + public static readonly LithuaniaOrtoFotoMapProvider Instance; + + LithuaniaOrtoFotoMapProvider() + { + } + + static LithuaniaOrtoFotoMapProvider() + { + Instance = new LithuaniaOrtoFotoMapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("043FF9EF-612C-411F-943C-32C787A88D6A"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "LithuaniaOrtoFotoMap"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + // old stuff + // http://www.maps.lt/ortofoto/mapslt_ortofoto_vector_512/map/_alllayers/L02/R0000001b/C00000028.jpg + // http://arcgis.maps.lt/ArcGIS/rest/services/mapslt_ortofoto/MapServer/tile/0/9/13 + // return string.Format("http://www.maps.lt/ortofoto/mapslt_ortofoto_vector_512/map/_alllayers/L{0:00}/R{1:x8}/C{2:x8}.jpg", zoom, pos.Y, pos.X); + // http://dc1.maps.lt/cache/mapslt_ortofoto_512/map/_alllayers/L03/R0000001c/C00000029.jpg + // return string.Format("http://arcgis.maps.lt/ArcGIS/rest/services/mapslt_ortofoto/MapServer/tile/{0}/{1}/{2}", zoom, pos.Y, pos.X); + // http://dc1.maps.lt/cache/mapslt_ortofoto_512/map/_alllayers/L03/R0000001d/C0000002a.jpg + + return string.Format(UrlFormat, zoom, pos.Y, pos.X); + } + + static readonly string UrlFormat = "http://dc1.maps.lt/cache/mapslt_ortofoto/map/_alllayers/L{0:00}/R{1:x8}/C{2:x8}.jpg"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Lithuania/LithuaniaOrtoFotoOldMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Lithuania/LithuaniaOrtoFotoOldMapProvider.cs new file mode 100644 index 0000000..0bbc3ad --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Lithuania/LithuaniaOrtoFotoOldMapProvider.cs @@ -0,0 +1,58 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// LithuaniaOrtoFotoNewMap, from 2005 data, provider + /// + public class LithuaniaOrtoFotoOldMapProvider : LithuaniaMapProviderBase + { + public static readonly LithuaniaOrtoFotoOldMapProvider Instance; + + LithuaniaOrtoFotoOldMapProvider() + { + } + + static LithuaniaOrtoFotoOldMapProvider() + { + Instance = new LithuaniaOrtoFotoOldMapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("C37A148E-0A7D-4123-BE4E-D0D3603BE46B"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "LithuaniaOrtoFotoMapOld"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + return string.Format(UrlFormat, zoom, pos.Y, pos.X); + } + + static readonly string UrlFormat = "http://dc1.maps.lt/cache/mapslt_ortofoto_2005/map/_alllayers/L{0:00}/R{1:x8}/C{2:x8}.jpg"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/NearMap/NearHybridMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/NearMap/NearHybridMapProvider.cs new file mode 100644 index 0000000..7dbfea6 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/NearMap/NearHybridMapProvider.cs @@ -0,0 +1,74 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// NearHybridMap provider - http://www.nearmap.com/ + /// + public class NearHybridMapProvider : NearMapProviderBase + { + public static readonly NearHybridMapProvider Instance; + + NearHybridMapProvider() + { + } + + static NearHybridMapProvider() + { + Instance = new NearHybridMapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("4BF8819A-635D-4A94-8DC7-94C0E0F04BFD"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "NearHybridMap"; + public override string Name + { + get + { + return name; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { NearSatelliteMapProvider.Instance, this }; + } + return overlays; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + // http://web1.nearmap.com/maps/hl=en&x=37&y=19&z=6&nml=MapT&nmg=1&s=2KbhmZZ + // http://web1.nearmap.com/maps/hl=en&x=36&y=19&z=6&nml=MapT&nmg=1&s=2YKWhQi + + return string.Format(UrlFormat, GetServerNum(pos, 3), pos.X, pos.Y, zoom); + } + + static readonly string UrlFormat = "http://web{0}.nearmap.com/maps/hl=en&x={1}&y={2}&z={3}&nml=MapT&nmg=1"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/NearMap/NearMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/NearMap/NearMapProvider.cs new file mode 100644 index 0000000..9b527ea --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/NearMap/NearMapProvider.cs @@ -0,0 +1,170 @@ + +namespace GMap.NET.MapProviders +{ + using System; + using GMap.NET.Projections; + using System.Net; + + public abstract class NearMapProviderBase : GMapProvider + { + public NearMapProviderBase() + { + // credentials doesn't work ;/ + Credential = new NetworkCredential("greatmaps", "greatmaps"); + } + + #region GMapProvider Members + public override Guid Id + { + get + { + throw new NotImplementedException(); + } + } + + public override string Name + { + get + { + throw new NotImplementedException(); + } + } + + public override PureProjection Projection + { + get + { + return MercatorProjection.Instance; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { this }; + } + return overlays; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + throw new NotImplementedException(); + } + #endregion + + public new static int GetServerNum(GPoint pos, int max) + { + // var hostNum=((opts.nodes!==0)?((tileCoords.x&2)%opts.nodes):0)+opts.nodeStart; + return (int)(pos.X & 2) % max; + } + + static readonly string SecureStr = "Vk52edzNRYKbGjF8Ur0WhmQlZs4wgipDETyL1oOMXIAvqtxJBuf7H36acCnS9P"; + + public string GetSafeString(GPoint pos) + { + #region -- source -- + /* + TileLayer.prototype.differenceEngine=function(s,a) + { + var offset=0,result="",alen=a.length,v,p; + for(var i=0; i + /// NearMap provider - http://www.nearmap.com/ + /// + public class NearMapProvider : NearMapProviderBase + { + public static readonly NearMapProvider Instance; + + NearMapProvider() + { + RefererUrl = "http://www.nearmap.com/"; + } + + static NearMapProvider() + { + Instance = new NearMapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("E33803DF-22CB-4FFA-B8E3-15383ED9969D"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "NearMap"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + // http://web1.nearmap.com/maps/hl=en&x=18681&y=10415&z=15&nml=Map_&nmg=1&s=kY8lZssipLIJ7c5 + // http://web1.nearmap.com/kh/v=nm&hl=en&x=20&y=8&z=5&nml=Map_&s=55KUZ + + return string.Format(UrlFormat, GetServerNum(pos, 3), pos.X, pos.Y, zoom, GetSafeString(pos)); + } + + static readonly string UrlFormat = "http://web{0}.nearmap.com/kh/v=nm&hl=en&x={1}&y={2}&z={3}&nml=Map_{4}"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/NearMap/NearSatelliteMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/NearMap/NearSatelliteMapProvider.cs new file mode 100644 index 0000000..e212e30 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/NearMap/NearSatelliteMapProvider.cs @@ -0,0 +1,63 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// NearSatelliteMap provider - http://www.nearmap.com/ + /// + public class NearSatelliteMapProvider : NearMapProviderBase + { + public static readonly NearSatelliteMapProvider Instance; + + NearSatelliteMapProvider() + { + } + + static NearSatelliteMapProvider() + { + Instance = new NearSatelliteMapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("56D00148-05B7-408D-8F7A-8D7250FF8121"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "NearSatelliteMap"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + // http://web2.nearmap.com/maps/hl=en&x=14&y=8&z=5&nml=Vert&s=kdj00 + // http://web2.nearmap.com/maps/hl=en&x=6&y=4&z=4&nml=Vert + // http://web2.nearmap.com/maps/hl=en&x=3&y=1&z=3&nml=Vert&s=2edd + // http://web0.nearmap.com/maps/hl=en&x=69&y=39&z=7&nml=Vert&s=z80wiTM + + return string.Format(UrlFormat, GetServerNum(pos, 4), pos.X, pos.Y, zoom, GetSafeString(pos)); + } + + static readonly string UrlFormat = "http://web{0}.nearmap.com/maps/hl=en&x={1}&y={2}&z={3}&nml=Vert{4}"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/OpenStreetMap/OpenCycleLandscapeMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/OpenStreetMap/OpenCycleLandscapeMapProvider.cs new file mode 100644 index 0000000..536ee6c --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/OpenStreetMap/OpenCycleLandscapeMapProvider.cs @@ -0,0 +1,74 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// OpenCycleMap Landscape provider - http://www.opencyclemap.org + /// + public class OpenCycleLandscapeMapProvider : OpenStreetMapProviderBase + { + public static readonly OpenCycleLandscapeMapProvider Instance; + + OpenCycleLandscapeMapProvider() + { + RefererUrl = "http://www.opencyclemap.org/"; + } + + static OpenCycleLandscapeMapProvider() + { + Instance = new OpenCycleLandscapeMapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("BDBAA939-6597-4D87-8F4F-261C49E35F56"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "OpenCycleLandscapeMap"; + public override string Name + { + get + { + return name; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { this }; + } + return overlays; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, string.Empty); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + char letter = ServerLetters[GMapProvider.GetServerNum(pos, 3)]; + return string.Format(UrlFormat, letter, zoom, pos.X, pos.Y); + } + + + static readonly string UrlFormat = "http://{0}.tile3.opencyclemap.org/landscape/{1}/{2}/{3}.png"; + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/OpenStreetMap/OpenCycleMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/OpenStreetMap/OpenCycleMapProvider.cs new file mode 100644 index 0000000..a6726f1 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/OpenStreetMap/OpenCycleMapProvider.cs @@ -0,0 +1,73 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// OpenCycleMap provider - http://www.opencyclemap.org + /// + public class OpenCycleMapProvider : OpenStreetMapProviderBase + { + public static readonly OpenCycleMapProvider Instance; + + OpenCycleMapProvider() + { + RefererUrl = "http://www.opencyclemap.org/"; + } + + static OpenCycleMapProvider() + { + Instance = new OpenCycleMapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("D7E1826E-EE1E-4441-9F15-7C2DE0FE0B0A"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "OpenCycleMap"; + public override string Name + { + get + { + return name; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { this }; + } + return overlays; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, string.Empty); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + char letter = ServerLetters[GMapProvider.GetServerNum(pos, 3)]; + return string.Format(UrlFormat, letter, zoom, pos.X, pos.Y); + } + + static readonly string UrlFormat = "http://{0}.tile.opencyclemap.org/cycle/{1}/{2}/{3}.png"; + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/OpenStreetMap/OpenCycleTransportMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/OpenStreetMap/OpenCycleTransportMapProvider.cs new file mode 100644 index 0000000..be17cf7 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/OpenStreetMap/OpenCycleTransportMapProvider.cs @@ -0,0 +1,73 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// OpenCycleMap Transport provider - http://www.opencyclemap.org + /// + public class OpenCycleTransportMapProvider : OpenStreetMapProviderBase + { + public static readonly OpenCycleTransportMapProvider Instance; + + OpenCycleTransportMapProvider() + { + RefererUrl = "http://www.opencyclemap.org/"; + } + + static OpenCycleTransportMapProvider() + { + Instance = new OpenCycleTransportMapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("AF66DF88-AD25-43A9-8F82-56FCA49A748A"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "OpenCycleTransportMap"; + public override string Name + { + get + { + return name; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { this }; + } + return overlays; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, string.Empty); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + char letter = ServerLetters[GMapProvider.GetServerNum(pos, 3)]; + return string.Format(UrlFormat, letter, zoom, pos.X, pos.Y); + } + + static readonly string UrlFormat = "http://{0}.tile2.opencyclemap.org/transport/{1}/{2}/{3}.png"; + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/OpenStreetMap/OpenSeaMapHybridProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/OpenStreetMap/OpenSeaMapHybridProvider.cs new file mode 100644 index 0000000..9d154d1 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/OpenStreetMap/OpenSeaMapHybridProvider.cs @@ -0,0 +1,72 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// OpenSeaMapHybrid provider - http://openseamap.org + /// + public class OpenSeaMapHybridProvider : OpenStreetMapProviderBase + { + public static readonly OpenSeaMapHybridProvider Instance; + + OpenSeaMapHybridProvider() + { + RefererUrl = "http://openseamap.org/"; + } + + static OpenSeaMapHybridProvider() + { + Instance = new OpenSeaMapHybridProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("FAACDE73-4B90-4AE6-BB4A-ADE4F3545592"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "OpenSeaMapHybrid"; + public override string Name + { + get + { + return name; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { OpenStreetMapProvider.Instance, this }; + } + return overlays; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, string.Empty); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + return string.Format(UrlFormat, zoom, pos.X, pos.Y); + } + + static readonly string UrlFormat = "http://tiles.openseamap.org/seamark/{0}/{1}/{2}.png"; + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/OpenStreetMap/OpenStreetMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/OpenStreetMap/OpenStreetMapProvider.cs new file mode 100644 index 0000000..291374c --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/OpenStreetMap/OpenStreetMapProvider.cs @@ -0,0 +1,508 @@ + +namespace GMap.NET.MapProviders +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Globalization; + using System.Xml; + using GMap.NET.Internals; + using GMap.NET.Projections; + + public abstract class OpenStreetMapProviderBase : GMapProvider, RoutingProvider, GeocodingProvider + { + public OpenStreetMapProviderBase() + { + MaxZoom = null; + RefererUrl = "http://www.openstreetmap.org/"; + Copyright = string.Format("© OpenStreetMap - Map data ©{0} OpenStreetMap", DateTime.Today.Year); + } + + public readonly string ServerLetters = "abc"; + + #region GMapProvider Members + + public override Guid Id + { + get + { + throw new NotImplementedException(); + } + } + + public override string Name + { + get + { + throw new NotImplementedException(); + } + } + + public override PureProjection Projection + { + get + { + return MercatorProjection.Instance; + } + } + + public override GMapProvider[] Overlays + { + get + { + throw new NotImplementedException(); + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + throw new NotImplementedException(); + } + + #endregion + + #region GMapRoutingProvider Members + + public MapRoute GetRoute(PointLatLng start, PointLatLng end, bool avoidHighways, bool walkingMode, int Zoom) + { + List points = GetRoutePoints(MakeRoutingUrl(start, end, walkingMode ? TravelTypeFoot : TravelTypeMotorCar)); + MapRoute route = points != null ? new MapRoute(points, walkingMode ? WalkingStr : DrivingStr) : null; + return route; + } + + /// + /// NotImplemented + /// + /// + /// + /// + /// + /// + /// + public MapRoute GetRoute(string start, string end, bool avoidHighways, bool walkingMode, int Zoom) + { + throw new NotImplementedException("use GetRoute(PointLatLng start, PointLatLng end..."); + } + + #region -- internals -- + string MakeRoutingUrl(PointLatLng start, PointLatLng end, string travelType) + { + return string.Format(CultureInfo.InvariantCulture, RoutingUrlFormat, start.Lat, start.Lng, end.Lat, end.Lng, travelType); + } + + List GetRoutePoints(string url) + { + List points = null; + try + { + string route = GMaps.Instance.UseRouteCache ? Cache.Instance.GetContent(url, CacheType.RouteCache) : string.Empty; + if(string.IsNullOrEmpty(route)) + { + route = GetContentUsingHttp(url); + if(!string.IsNullOrEmpty(route)) + { + if(GMaps.Instance.UseRouteCache) + { + Cache.Instance.SaveContent(url, CacheType.RouteCache, route); + } + } + } + + if(!string.IsNullOrEmpty(route)) + { + XmlDocument xmldoc = new XmlDocument(); + xmldoc.LoadXml(route); + System.Xml.XmlNamespaceManager xmlnsManager = new System.Xml.XmlNamespaceManager(xmldoc.NameTable); + xmlnsManager.AddNamespace("sm", "http://earth.google.com/kml/2.0"); + + ///Folder/Placemark/LineString/coordinates + var coordNode = xmldoc.SelectSingleNode("/sm:kml/sm:Document/sm:Folder/sm:Placemark/sm:LineString/sm:coordinates", xmlnsManager); + + string[] coordinates = coordNode.InnerText.Split('\n'); + + if(coordinates.Length > 0) + { + points = new List(); + + foreach(string coordinate in coordinates) + { + if(coordinate != string.Empty) + { + string[] XY = coordinate.Split(','); + if(XY.Length == 2) + { + double lat = double.Parse(XY[1], CultureInfo.InvariantCulture); + double lng = double.Parse(XY[0], CultureInfo.InvariantCulture); + points.Add(new PointLatLng(lat, lng)); + } + } + } + } + } + } + catch(Exception ex) + { + Debug.WriteLine("GetRoutePoints: " + ex); + } + + return points; + } + + static readonly string RoutingUrlFormat = "http://www.yournavigation.org/api/1.0/gosmore.php?format=kml&flat={0}&flon={1}&tlat={2}&tlon={3}&v={4}&fast=1&layer=mapnik"; + static readonly string TravelTypeFoot = "foot"; + static readonly string TravelTypeMotorCar = "motorcar"; + + static readonly string WalkingStr = "Walking"; + static readonly string DrivingStr = "Driving"; + #endregion + + #endregion + + #region GeocodingProvider Members + + public GeoCoderStatusCode GetPoints(string keywords, out List pointList) + { + // http://nominatim.openstreetmap.org/search?q=lithuania,vilnius&format=xml + + #region -- response -- + // + // + // + // + // + // + #endregion + + return GetLatLngFromGeocoderUrl(MakeGeocoderUrl(keywords), out pointList); + } + + public PointLatLng? GetPoint(string keywords, out GeoCoderStatusCode status) + { + List pointList; + status = GetPoints(keywords, out pointList); + return pointList != null && pointList.Count > 0 ? pointList[0] : (PointLatLng?)null; + } + + public GeoCoderStatusCode GetPoints(Placemark placemark, out List pointList) + { + throw new NotImplementedException("use GetPoints(string keywords..."); + } + + public PointLatLng? GetPoint(Placemark placemark, out GeoCoderStatusCode status) + { + throw new NotImplementedException("use GetPoint(string keywords..."); + } + + public GeoCoderStatusCode GetPlacemarks(PointLatLng location, out List placemarkList) + { + throw new NotImplementedException("use GetPlacemark"); + } + + public Placemark ? GetPlacemark(PointLatLng location, out GeoCoderStatusCode status) + { + //http://nominatim.openstreetmap.org/reverse?format=xml&lat=52.5487429714954&lon=-1.81602098644987&zoom=18&addressdetails=1 + + #region -- response -- + /* + + + 137, Pilkington Avenue, Castle Vale, City of Birmingham, West Midlands, England, B72 1LH, United Kingdom + + + + 137 + + + Pilkington Avenue + + + Castle Vale + + + City of Birmingham + + + West Midlands + + + West Midlands + + + England + + + B72 1LH + + + United Kingdom + + + gb + + + + */ + + #endregion + + return GetPlacemarkFromReverseGeocoderUrl(MakeReverseGeocoderUrl(location), out status); + } + + #region -- internals -- + + string MakeGeocoderUrl(string keywords) + { + return string.Format(GeocoderUrlFormat, keywords.Replace(' ', '+')); + } + + string MakeReverseGeocoderUrl(PointLatLng pt) + { + return string.Format(CultureInfo.InvariantCulture, ReverseGeocoderUrlFormat, pt.Lat, pt.Lng); + } + + GeoCoderStatusCode GetLatLngFromGeocoderUrl(string url, out List pointList) + { + var status = GeoCoderStatusCode.Unknow; + pointList = null; + + try + { + string geo = GMaps.Instance.UseGeocoderCache ? Cache.Instance.GetContent(url, CacheType.GeocoderCache) : string.Empty; + + bool cache = false; + + if(string.IsNullOrEmpty(geo)) + { + geo = GetContentUsingHttp(url); + + if(!string.IsNullOrEmpty(geo)) + { + cache = true; + } + } + + if(!string.IsNullOrEmpty(geo)) + { + if(geo.StartsWith("(); + + foreach(XmlNode n in l) + { + var nn = n.Attributes["lat"]; + if(nn != null) + { + double lat = double.Parse(nn.Value, CultureInfo.InvariantCulture); + + nn = n.Attributes["lon"]; + if(nn != null) + { + double lng = double.Parse(nn.Value, CultureInfo.InvariantCulture); + pointList.Add(new PointLatLng(lat, lng)); + } + } + } + + status = GeoCoderStatusCode.G_GEO_SUCCESS; + } + } + } + } + } + catch(Exception ex) + { + status = GeoCoderStatusCode.ExceptionInCode; + Debug.WriteLine("GetLatLngFromGeocoderUrl: " + ex); + } + + return status; + } + + Placemark ? GetPlacemarkFromReverseGeocoderUrl(string url, out GeoCoderStatusCode status) + { + status = GeoCoderStatusCode.Unknow; + Placemark ?ret = null; + + try + { + string geo = GMaps.Instance.UsePlacemarkCache ? Cache.Instance.GetContent(url, CacheType.PlacemarkCache) : string.Empty; + + bool cache = false; + + if(string.IsNullOrEmpty(geo)) + { + geo = GetContentUsingHttp(url); + + if(!string.IsNullOrEmpty(geo)) + { + cache = true; + } + } + + if(!string.IsNullOrEmpty(geo)) + { + if(geo.StartsWith(" + /// OpenStreetMap provider - http://www.openstreetmap.org/ + /// + public class OpenStreetMapProvider : OpenStreetMapProviderBase + { + public static readonly OpenStreetMapProvider Instance; + + OpenStreetMapProvider() + { + } + + static OpenStreetMapProvider() + { + Instance = new OpenStreetMapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("0521335C-92EC-47A8-98A5-6FD333DDA9C0"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "OpenStreetMap"; + public override string Name + { + get + { + return name; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { this }; + } + return overlays; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, string.Empty); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + char letter = ServerLetters[GetServerNum(pos, 3)]; + return string.Format(UrlFormat, letter, zoom, pos.X, pos.Y); + } + + static readonly string UrlFormat = "http://{0}.tile.openstreetmap.org/{1}/{2}/{3}.png"; + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/OpenStreetMap/OpenStreetMapQuestHybridProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/OpenStreetMap/OpenStreetMapQuestHybridProvider.cs new file mode 100644 index 0000000..24cc2fe --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/OpenStreetMap/OpenStreetMapQuestHybridProvider.cs @@ -0,0 +1,72 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// OpenStreetMapQuestHybrid provider - http://wiki.openstreetmap.org/wiki/MapQuest + /// + public class OpenStreetMapQuestHybridProvider : OpenStreetMapProviderBase + { + public static readonly OpenStreetMapQuestHybridProvider Instance; + + OpenStreetMapQuestHybridProvider() + { + Copyright = string.Format("© MapQuest - Map data ©{0} MapQuest, OpenStreetMap", DateTime.Today.Year); + } + + static OpenStreetMapQuestHybridProvider() + { + Instance = new OpenStreetMapQuestHybridProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("95E05027-F846-4429-AB7A-9445ABEEFA2A"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "OpenStreetMapQuestHybrid"; + public override string Name + { + get + { + return name; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { OpenStreetMapQuestSatteliteProvider.Instance, this }; + } + return overlays; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, string.Empty); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + return string.Format(UrlFormat, GetServerNum(pos, 3) + 1, zoom, pos.X, pos.Y); + } + + static readonly string UrlFormat = "http://otile{0}.mqcdn.com/tiles/1.0.0/hyb/{1}/{2}/{3}.png"; + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/OpenStreetMap/OpenStreetMapQuestProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/OpenStreetMap/OpenStreetMapQuestProvider.cs new file mode 100644 index 0000000..80aceea --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/OpenStreetMap/OpenStreetMapQuestProvider.cs @@ -0,0 +1,72 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// OpenStreetMapQuest provider - http://wiki.openstreetmap.org/wiki/MapQuest + /// + public class OpenStreetMapQuestProvider : OpenStreetMapProviderBase + { + public static readonly OpenStreetMapQuestProvider Instance; + + OpenStreetMapQuestProvider() + { + Copyright = string.Format("© MapQuest - Map data ©{0} MapQuest, OpenStreetMap", DateTime.Today.Year); + } + + static OpenStreetMapQuestProvider() + { + Instance = new OpenStreetMapQuestProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("D0A12840-973A-448B-B9C2-89B8A07DFF0F"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "OpenStreetMapQuest"; + public override string Name + { + get + { + return name; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { this }; + } + return overlays; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, string.Empty); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + return string.Format(UrlFormat, GetServerNum(pos, 3) + 1, zoom, pos.X, pos.Y); + } + + static readonly string UrlFormat = "http://otile{0}.mqcdn.com/tiles/1.0.0/osm/{1}/{2}/{3}.png"; + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/OpenStreetMap/OpenStreetMapQuestSatteliteProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/OpenStreetMap/OpenStreetMapQuestSatteliteProvider.cs new file mode 100644 index 0000000..5c7ce3a --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/OpenStreetMap/OpenStreetMapQuestSatteliteProvider.cs @@ -0,0 +1,72 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// OpenStreetMapQuestSattelite provider - http://wiki.openstreetmap.org/wiki/MapQuest + /// + public class OpenStreetMapQuestSatteliteProvider : OpenStreetMapProviderBase + { + public static readonly OpenStreetMapQuestSatteliteProvider Instance; + + OpenStreetMapQuestSatteliteProvider() + { + Copyright = string.Format("© MapQuest - Map data ©{0} MapQuest, OpenStreetMap", DateTime.Today.Year); + } + + static OpenStreetMapQuestSatteliteProvider() + { + Instance = new OpenStreetMapQuestSatteliteProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("E590D3B1-37F4-442B-9395-ADB035627F67"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "OpenStreetMapQuestSattelite"; + public override string Name + { + get + { + return name; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { this }; + } + return overlays; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, string.Empty); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + return string.Format(UrlFormat, GetServerNum(pos, 3) + 1, zoom, pos.X, pos.Y); + } + + static readonly string UrlFormat = "http://oatile{0}.mqcdn.com/naip/{1}/{2}/{3}.png"; + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/OpenStreetMap/OpenStreetMapSurferProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/OpenStreetMap/OpenStreetMapSurferProvider.cs new file mode 100644 index 0000000..7c50b97 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/OpenStreetMap/OpenStreetMapSurferProvider.cs @@ -0,0 +1,78 @@ + +namespace GMap.NET.MapProviders +{ + using System; + +#if OpenStreetMapSurfer + /// + /// OpenStreetMapSurfer provider + /// http://wiki.openstreetmap.org/wiki/MapSurfer.Net + /// + /// Since May 2011 the service http://www.mapsurfer.net is unavailable due + /// to hosting problems. + /// + public class OpenStreetMapSurferProvider : OpenStreetMapProviderBase + { + public static readonly OpenStreetMapSurferProvider Instance; + + OpenStreetMapSurferProvider() + { + RefererUrl = "http://www.mapsurfer.net/"; + } + + static OpenStreetMapSurferProvider() + { + Instance = new OpenStreetMapSurferProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("6282888B-2F01-4029-9CD8-0CFFCB043995"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "OpenStreetMapSurfer"; + public override string Name + { + get + { + return name; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { this }; + } + return overlays; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, string.Empty); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + return string.Format(UrlFormat, pos.X, pos.Y, zoom); + } + + static readonly string UrlFormat = "http://tiles1.mapsurfer.net/tms_r.ashx?x={0}&y={1}&z={2}"; + } +#endif +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/OpenStreetMap/OpenStreetMapSurferTerrainProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/OpenStreetMap/OpenStreetMapSurferTerrainProvider.cs new file mode 100644 index 0000000..4e36b35 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/OpenStreetMap/OpenStreetMapSurferTerrainProvider.cs @@ -0,0 +1,74 @@ + +namespace GMap.NET.MapProviders +{ + using System; + +#if OpenStreetMapSurfer + /// + /// OpenStreetMapSurferTerrain provider + /// + public class OpenStreetMapSurferTerrainProvider : OpenStreetMapProviderBase + { + public static readonly OpenStreetMapSurferTerrainProvider Instance; + + OpenStreetMapSurferTerrainProvider() + { + RefererUrl = "http://www.mapsurfer.net/"; + } + + static OpenStreetMapSurferTerrainProvider() + { + Instance = new OpenStreetMapSurferTerrainProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("E87954A4-1852-4B64-95FA-24E512E4B021"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "OpenStreetMapSurferTerrain"; + public override string Name + { + get + { + return name; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { this }; + } + return overlays; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, string.Empty); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + return string.Format(UrlFormat, pos.X, pos.Y, zoom); + } + + static readonly string UrlFormat = "http://tiles2.mapsurfer.net/tms_t.ashx?x={0}&y={1}&z={2}"; + } +#endif +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/OpenStreetMap/OpenStreetOsmProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/OpenStreetMap/OpenStreetOsmProvider.cs new file mode 100644 index 0000000..a8b07a7 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/OpenStreetMap/OpenStreetOsmProvider.cs @@ -0,0 +1,80 @@ + +namespace GMap.NET.MapProviders +{ + using System; + +#if OpenStreetOsm + /// + /// OpenStreetOsm provider + /// http://wiki.openstreetmap.org/wiki/Osmarender + /// + /// Osmarender is a rule-based rendering tool for generating SVG images + /// of OSM data. Note that Osmarender has not been actively maintained + /// since March 2012 and was discontinued as a main Slippy Map layer on + /// openstreetmap.org around that time. + /// + public class OpenStreetOsmProvider : OpenStreetMapProviderBase + { + public static readonly OpenStreetOsmProvider Instance; + + OpenStreetOsmProvider() + { + } + + static OpenStreetOsmProvider() + { + Instance = new OpenStreetOsmProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("07EF8CBC-A91D-4B2F-8B2D-70DBE384EF18"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "OpenStreetOsm"; + public override string Name + { + get + { + return name; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { this }; + } + return overlays; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, string.Empty); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + char letter = ServerLetters[GMapProvider.GetServerNum(pos, 3)]; + return string.Format(UrlFormat, letter, zoom, pos.X, pos.Y); + } + + static readonly string UrlFormat = "http://{0}.tah.openstreetmap.org/Tiles/tile/{1}/{2}/{3}.png"; + } +#endif +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Ovi/OviHybridMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Ovi/OviHybridMapProvider.cs new file mode 100644 index 0000000..5149e2d --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Ovi/OviHybridMapProvider.cs @@ -0,0 +1,60 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// OviHybridMap provider + /// + public class OviHybridMapProvider : OviMapProviderBase + { + public static readonly OviHybridMapProvider Instance; + + OviHybridMapProvider() + { + } + + static OviHybridMapProvider() + { + Instance = new OviHybridMapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("B85A8FD2-40F4-40EE-9B45-491AA45D86C1"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "OviHybridMap"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + // http://c.maptile.maps.svc.ovi.com/maptiler/v2/maptile/newest/hybrid.day/12/2316/1277/256/png8 + + return string.Format(UrlFormat, UrlServerLetters[GetServerNum(pos, 4)], zoom, pos.X, pos.Y); + } + + static readonly string UrlFormat = "http://{0}.maptile.maps.svc.ovi.com/maptiler/v2/maptile/newest/hybrid.day/{1}/{2}/{3}/256/png8"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Ovi/OviMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Ovi/OviMapProvider.cs new file mode 100644 index 0000000..2a253d1 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Ovi/OviMapProvider.cs @@ -0,0 +1,117 @@ + +namespace GMap.NET.MapProviders +{ + using System; + using GMap.NET.Projections; + + public abstract class OviMapProviderBase : GMapProvider + { + public OviMapProviderBase() + { + MaxZoom = null; + RefererUrl = "http://maps.ovi.com/"; + Copyright = string.Format("©{0} OVI Nokia - Map data ©{0} NAVTEQ, Imagery ©{0} DigitalGlobe", DateTime.Today.Year); + } + + #region GMapProvider Members + public override Guid Id + { + get + { + throw new NotImplementedException(); + } + } + + public override string Name + { + get + { + throw new NotImplementedException(); + } + } + + public override PureProjection Projection + { + get + { + return MercatorProjection.Instance; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { this }; + } + return overlays; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + throw new NotImplementedException(); + } + #endregion + + protected static readonly string UrlServerLetters = "bcde"; + } + + /// + /// OviMap provider + /// + public class OviMapProvider : OviMapProviderBase + { + public static readonly OviMapProvider Instance; + + OviMapProvider() + { + } + + static OviMapProvider() + { + Instance = new OviMapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("30DC1083-AC4D-4471-A232-D8A67AC9373A"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "OviMap"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + // http://c.maptile.maps.svc.ovi.com/maptiler/v2/maptile/newest/normal.day/12/2321/1276/256/png8 + + return string.Format(UrlFormat, UrlServerLetters[GetServerNum(pos, 4)], zoom, pos.X, pos.Y); + } + + static readonly string UrlFormat = "http://{0}.maptile.maps.svc.ovi.com/maptiler/v2/maptile/newest/normal.day/{1}/{2}/{3}/256/png8"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Ovi/OviSatelliteMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Ovi/OviSatelliteMapProvider.cs new file mode 100644 index 0000000..31c7cd1 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Ovi/OviSatelliteMapProvider.cs @@ -0,0 +1,60 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// OviSatelliteMap provider + /// + public class OviSatelliteMapProvider : OviMapProviderBase + { + public static readonly OviSatelliteMapProvider Instance; + + OviSatelliteMapProvider() + { + } + + static OviSatelliteMapProvider() + { + Instance = new OviSatelliteMapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("6696CE12-7694-4073-BC48-79EE849F2563"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "OviSatelliteMap"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + // http://b.maptile.maps.svc.ovi.com/maptiler/v2/maptile/newest/satellite.day/12/2313/1275/256/png8 + + return string.Format(UrlFormat, UrlServerLetters[GetServerNum(pos, 4)], zoom, pos.X, pos.Y); + } + + static readonly string UrlFormat = "http://{0}.maptile.maps.svc.ovi.com/maptiler/v2/maptile/newest/satellite.day/{1}/{2}/{3}/256/png8"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Ovi/OviTerrainMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Ovi/OviTerrainMapProvider.cs new file mode 100644 index 0000000..7bbcfea --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Ovi/OviTerrainMapProvider.cs @@ -0,0 +1,60 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// OviTerrainMap provider + /// + public class OviTerrainMapProvider : OviMapProviderBase + { + public static readonly OviTerrainMapProvider Instance; + + OviTerrainMapProvider() + { + } + + static OviTerrainMapProvider() + { + Instance = new OviTerrainMapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("7267339C-445E-4E61-B8B8-82D0B7AAACC5"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "OviTerrainMap"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + // http://d.maptile.maps.svc.ovi.com/maptiler/v2/maptile/newest/terrain.day/12/2317/1277/256/png8 + + return string.Format(UrlFormat, UrlServerLetters[GetServerNum(pos, 4)], zoom, pos.X, pos.Y); + } + + static readonly string UrlFormat = "http://{0}.maptile.maps.svc.ovi.com/maptiler/v2/maptile/newest/terrain.day/{1}/{2}/{3}/256/png8"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Yahoo/YahooHybridMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Yahoo/YahooHybridMapProvider.cs new file mode 100644 index 0000000..e8d93da --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Yahoo/YahooHybridMapProvider.cs @@ -0,0 +1,75 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// YahooHybridMap provider + /// + public class YahooHybridMapProvider : YahooMapProviderBase + { + public static readonly YahooHybridMapProvider Instance; + + YahooHybridMapProvider() + { + } + + static YahooHybridMapProvider() + { + Instance = new YahooHybridMapProvider(); + } + + public string Version = "4.3"; + + #region GMapProvider Members + + readonly Guid id = new Guid("A084E0DB-F9A6-45C1-BC2F-791E1F4E958E"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "YahooHybridMap"; + public override string Name + { + get + { + return name; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { YahooSatelliteMapProvider.Instance, this }; + } + return overlays; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + // http://maps1.yimg.com/hx/tl?b=1&v=4.3&t=h&.intl=en&x=14&y=5&z=7&r=1 + + return string.Format(UrlFormat, ((GetServerNum(pos, 2)) + 1), Version, language, pos.X, (((1 << zoom) >> 1) - 1 - pos.Y), (zoom + 1)); + } + + static readonly string UrlFormat = "http://maps{0}.yimg.com/hx/tl?v={1}&t=h&.intl={2}&x={3}&y={4}&z={5}&r=1"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Yahoo/YahooMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Yahoo/YahooMapProvider.cs new file mode 100644 index 0000000..bfdb24b --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Yahoo/YahooMapProvider.cs @@ -0,0 +1,420 @@ + +namespace GMap.NET.MapProviders +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Globalization; + using System.Xml; + using GMap.NET.Internals; + using GMap.NET.Projections; + + public abstract class YahooMapProviderBase : GMapProvider, GeocodingProvider + { + public YahooMapProviderBase() + { + RefererUrl = "http://maps.yahoo.com/"; + Copyright = string.Format("© Yahoo! Inc. - Map data & Imagery ©{0} NAVTEQ", DateTime.Today.Year); + } + + public string AppId = string.Empty; + public int MinExpectedQuality = 39; + + #region GMapProvider Members + public override Guid Id + { + get + { + throw new NotImplementedException(); + } + } + + public override string Name + { + get + { + throw new NotImplementedException(); + } + } + + public override PureProjection Projection + { + get + { + return MercatorProjection.Instance; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { this }; + } + return overlays; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + throw new NotImplementedException(); + } + #endregion + + #region GeocodingProvider Members + + public GeoCoderStatusCode GetPoints(string keywords, out List pointList) + { + // http://where.yahooapis.com/geocode?q=lithuania,vilnius&appid=1234&flags=CG&gflags=QL&locale=LT-lt + + #region -- response -- + //0No errorLT-lt4014054.68985025.26926054.68985025.26926046100 + #endregion + + return GetLatLngFromGeocoderUrl(MakeGeocoderUrl(keywords), out pointList); + } + + public PointLatLng? GetPoint(string keywords, out GeoCoderStatusCode status) + { + List pointList; + status = GetPoints(keywords, out pointList); + return pointList != null && pointList.Count > 0 ? pointList[0] : (PointLatLng?)null; + } + + public GeoCoderStatusCode GetPoints(Placemark placemark, out List pointList) + { + // http://where.yahooapis.com/geocode?country=LT&state=Vilniaus+Apskritis&county=Vilniaus+Miesto+Savivaldybe&city=Vilnius&neighborhood=Naujamiestis&postal=01108&street=J.+Tumo-Vaizganto+Gatve&house=2&appid=1234&flags=CG&gflags=QL&locale=LT-lt + + #region -- response -- + //0No errorLT-lt1918754.69018125.26948354.69022725.269278500 + #endregion + + return GetLatLngFromGeocoderUrl(MakeGeocoderDetailedUrl(placemark), out pointList); + } + + public PointLatLng? GetPoint(Placemark placemark, out GeoCoderStatusCode status) + { + List pointList; + status = GetPoints(placemark, out pointList); + return pointList != null && pointList.Count > 0 ? pointList[0] : (PointLatLng?)null; + } + + public GeoCoderStatusCode GetPlacemarks(PointLatLng location, out List placemarkList) + { + // http://where.yahooapis.com/geocode?q=54.689850,25.269260&appid=1234&flags=G&gflags=QRL&locale=LT-lt + + #region -- response -- + //0No errorLT-lt9919954.68985025.26926054.68985025.26926050054.689850,25.2692602 J. Tumo-Vaizganto Gatve01108 NaujamiestisLietuvos Respublika2J. Tumo-Vaizganto Gatve01108NaujamiestisVilniusVilniaus Miesto SavivaldybeVilniaus ApskritisLietuvos RespublikaLT127583621101108 + #endregion + + return GetPlacemarksFromReverseGeocoderUrl(MakeReverseGeocoderUrl(location), out placemarkList); + } + + public Placemark ? GetPlacemark(PointLatLng location, out GeoCoderStatusCode status) + { + List placemarkList; + status = GetPlacemarks(location, out placemarkList); + return placemarkList != null && placemarkList.Count > 0 ? placemarkList[0] : (Placemark?)null; + } + + #region -- internals -- + + string MakeGeocoderUrl(string keywords) + { + return string.Format(CultureInfo.InvariantCulture, GeocoderUrlFormat, keywords.Replace(' ', '+'), AppId, !string.IsNullOrEmpty(LanguageStr) ? "&locale=" + LanguageStr : ""); + } + + string MakeGeocoderDetailedUrl(Placemark placemark) + { + return string.Format(GeocoderDetailedUrlFormat, + PrepareUrlString(placemark.CountryName), + PrepareUrlString(placemark.AdministrativeAreaName), + PrepareUrlString(placemark.SubAdministrativeAreaName), + PrepareUrlString(placemark.LocalityName), + PrepareUrlString(placemark.DistrictName), + PrepareUrlString(placemark.PostalCodeNumber), + PrepareUrlString(placemark.ThoroughfareName), + PrepareUrlString(placemark.HouseNo), + AppId, + !string.IsNullOrEmpty(LanguageStr) ? "&locale=" + LanguageStr : string.Empty); + } + + string MakeReverseGeocoderUrl(PointLatLng pt) + { + return string.Format(CultureInfo.InvariantCulture, ReverseGeocoderUrlFormat, pt.Lat, pt.Lng, AppId, !string.IsNullOrEmpty(LanguageStr) ? "&locale=" + LanguageStr : ""); + } + + string PrepareUrlString(string str) + { + if (str == null) return string.Empty; + return str.Replace(' ', '+'); + } + + GeoCoderStatusCode GetLatLngFromGeocoderUrl(string url, out List pointList) + { + var status = GeoCoderStatusCode.Unknow; + pointList = null; + + try + { + string geo = GMaps.Instance.UseGeocoderCache ? Cache.Instance.GetContent(url, CacheType.GeocoderCache) : string.Empty; + + bool cache = false; + + if (string.IsNullOrEmpty(geo)) + { + geo = GetContentUsingHttp(url); + + if (!string.IsNullOrEmpty(geo)) + { + cache = true; + } + } + + if (!string.IsNullOrEmpty(geo)) + { + if (geo.StartsWith("(); + + foreach (XmlNode n in l) + { + var nn = n.SelectSingleNode("quality"); + if (nn != null) + { + var quality = int.Parse(nn.InnerText); + if (quality < MinExpectedQuality) continue; + + nn = n.SelectSingleNode("latitude"); + if (nn != null) + { + double lat = double.Parse(nn.InnerText, CultureInfo.InvariantCulture); + + nn = n.SelectSingleNode("longitude"); + if (nn != null) + { + double lng = double.Parse(nn.InnerText, CultureInfo.InvariantCulture); + pointList.Add(new PointLatLng(lat, lng)); + } + } + } + } + + status = GeoCoderStatusCode.G_GEO_SUCCESS; + } + } + } + } + } + catch (Exception ex) + { + status = GeoCoderStatusCode.ExceptionInCode; + Debug.WriteLine("GetLatLngFromGeocoderUrl: " + ex); + } + + return status; + } + + GeoCoderStatusCode GetPlacemarksFromReverseGeocoderUrl(string url, out List placemarkList) + { + var status = GeoCoderStatusCode.Unknow; + placemarkList = null; + + try + { + string geo = GMaps.Instance.UsePlacemarkCache ? Cache.Instance.GetContent(url, CacheType.PlacemarkCache) : string.Empty; + + bool cache = false; + + if (string.IsNullOrEmpty(geo)) + { + geo = GetContentUsingHttp(url); + + if (!string.IsNullOrEmpty(geo)) + { + cache = true; + } + } + + if (!string.IsNullOrEmpty(geo)) + { + if (geo.StartsWith("(); + + foreach (XmlNode n in l) + { + var vl = n.SelectSingleNode("name"); + if (vl == null) continue; + + Placemark placemark = new Placemark(vl.InnerText); + + vl = n.SelectSingleNode("level0"); + if (vl != null) + { + placemark.CountryName = vl.InnerText; + } + + vl = n.SelectSingleNode("level0code"); + if (vl != null) + { + placemark.CountryNameCode = vl.InnerText; + } + + vl = n.SelectSingleNode("postal"); + if (vl != null) + { + placemark.PostalCodeNumber = vl.InnerText; + } + + vl = n.SelectSingleNode("level1"); + if (vl != null) + { + placemark.AdministrativeAreaName = vl.InnerText; + } + + vl = n.SelectSingleNode("level2"); + if (vl != null) + { + placemark.SubAdministrativeAreaName = vl.InnerText; + } + + vl = n.SelectSingleNode("level3"); + if (vl != null) + { + placemark.LocalityName = vl.InnerText; + } + + vl = n.SelectSingleNode("level4"); + if (vl != null) + { + placemark.DistrictName = vl.InnerText; + } + + vl = n.SelectSingleNode("street"); + if (vl != null) + { + placemark.ThoroughfareName = vl.InnerText; + } + + vl = n.SelectSingleNode("house"); + if (vl != null) + { + placemark.HouseNo = vl.InnerText; + } + + vl = n.SelectSingleNode("radius"); + if (vl != null) + { + placemark.Accuracy = int.Parse(vl.InnerText); + } + + placemarkList.Add(placemark); + } + + status = GeoCoderStatusCode.G_GEO_SUCCESS; + } + } + } + } + } + catch (Exception ex) + { + status = GeoCoderStatusCode.ExceptionInCode; + Debug.WriteLine("GetPlacemarkFromReverseGeocoderUrl: " + ex); + } + + return status; + } + + static readonly string ReverseGeocoderUrlFormat = "http://where.yahooapis.com/geocode?q={0},{1}&appid={2}&flags=G&gflags=QRL{3}"; + static readonly string GeocoderUrlFormat = "http://where.yahooapis.com/geocode?q={0}&appid={1}&flags=CG&gflags=QL{2}"; + static readonly string GeocoderDetailedUrlFormat = "http://where.yahooapis.com/geocode?country={0}&state={1}&county={2}&city={3}&neighborhood={4}&postal={5}&street={6}&house={7}&appid={8}&flags=CG&gflags=QL{9}"; + + #endregion + + #endregion + } + + /// + /// YahooMap provider + /// + public class YahooMapProvider : YahooMapProviderBase + { + public static readonly YahooMapProvider Instance; + + YahooMapProvider() + { + } + + static YahooMapProvider() + { + Instance = new YahooMapProvider(); + } + + public string Version = "4.3"; + + #region GMapProvider Members + + readonly Guid id = new Guid("65DB032C-6869-49B0-A7FC-3AE41A26AF4D"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "YahooMap"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + // http://maps1.yimg.com/hx/tl?b=1&v=4.3&.intl=en&x=12&y=7&z=7&r=1 + + return string.Format(UrlFormat, ((GetServerNum(pos, 2)) + 1), Version, language, pos.X, (((1 << zoom) >> 1) - 1 - pos.Y), (zoom + 1)); + } + + static readonly string UrlFormat = "http://maps{0}.yimg.com/hx/tl?v={1}&.intl={2}&x={3}&y={4}&z={5}&r=1"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Yahoo/YahooSatelliteMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Yahoo/YahooSatelliteMapProvider.cs new file mode 100644 index 0000000..a68e541 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Yahoo/YahooSatelliteMapProvider.cs @@ -0,0 +1,62 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// YahooSatelliteMap provider + /// + public class YahooSatelliteMapProvider : YahooMapProviderBase + { + public static readonly YahooSatelliteMapProvider Instance; + + YahooSatelliteMapProvider() + { + } + + static YahooSatelliteMapProvider() + { + Instance = new YahooSatelliteMapProvider(); + } + + public string Version = "1.9"; + + #region GMapProvider Members + + readonly Guid id = new Guid("55D71878-913F-4320-B5B6-B4167A3F148F"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "YahooSatelliteMap"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + // http://maps3.yimg.com/ae/ximg?v=1.9&t=a&s=256&.intl=en&x=15&y=7&z=7&r=1 + + return string.Format(UrlFormat, ((GetServerNum(pos, 2)) + 1), Version, language, pos.X, (((1 << zoom) >> 1) - 1 - pos.Y), (zoom + 1)); + } + + static readonly string UrlFormat = "http://maps{0}.yimg.com/ae/ximg?v={1}&t=a&s=256&.intl={2}&x={3}&y={4}&z={5}&r=1"; + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Yandex/YandexHybridMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Yandex/YandexHybridMapProvider.cs new file mode 100644 index 0000000..baaec50 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Yandex/YandexHybridMapProvider.cs @@ -0,0 +1,75 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// YandexHybridMap provider + /// + public class YandexHybridMapProvider : YandexMapProviderBase + { + public static readonly YandexHybridMapProvider Instance; + + YandexHybridMapProvider() + { + } + + static YandexHybridMapProvider() + { + Instance = new YandexHybridMapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("78A3830F-5EE3-432C-A32E-91B7AF6BBCB9"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "YandexHybridMap"; + public override string Name + { + get + { + return name; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { YandexSatelliteMapProvider.Instance, this }; + } + return overlays; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + // http://vec01.maps.yandex.ru/tiles?l=map&v=2.10.2&x=1494&y=650&z=11 + // http://vec03.maps.yandex.net/tiles?l=skl&v=2.26.0&x=4663&y=2610&z=13&lang=ru-RU + + return string.Format(UrlFormat, UrlServer, GetServerNum(pos, 4) + 1, Version, pos.X, pos.Y, zoom); + } + + static readonly string UrlServer = "vec"; + static readonly string UrlFormat = "http://{0}0{1}.maps.yandex.ru/tiles?l=skl&v={2}&x={3}&y={4}&z={5}"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Yandex/YandexMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Yandex/YandexMapProvider.cs new file mode 100644 index 0000000..cb52671 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Yandex/YandexMapProvider.cs @@ -0,0 +1,114 @@ + +namespace GMap.NET.MapProviders +{ + using System; + using GMap.NET.Projections; + + public abstract class YandexMapProviderBase : GMapProvider + { + #region GMapProvider Members + public override Guid Id + { + get + { + throw new NotImplementedException(); + } + } + + public override string Name + { + get + { + throw new NotImplementedException(); + } + } + + public override PureProjection Projection + { + get + { + return MercatorProjectionYandex.Instance; + } + } + + GMapProvider[] overlays; + public override GMapProvider[] Overlays + { + get + { + if(overlays == null) + { + overlays = new GMapProvider[] { this }; + } + return overlays; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + throw new NotImplementedException(); + } + #endregion + + protected string Version = "2.26.0"; + } + + /// + /// YandexMap provider + /// + public class YandexMapProvider : YandexMapProviderBase + { + public static readonly YandexMapProvider Instance; + + YandexMapProvider() + { + RefererUrl = "http://maps.yandex.ru/"; + } + + static YandexMapProvider() + { + Instance = new YandexMapProvider(); + } + + #region GMapProvider Members + + readonly Guid id = new Guid("82DC969D-0491-40F3-8C21-4D90B67F47EB"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "YandexMap"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + // http://vec01.maps.yandex.ru/tiles?l=map&v=2.10.2&x=1494&y=650&z=11 + // http://vec03.maps.yandex.net/tiles?l=map&v=2.19.5&x=579&y=326&z=10&g=Gagarin + // http://vec02.maps.yandex.net/tiles?l=map&v=2.26.0&x=586&y=327&z=10&lang=ru-RU + + return string.Format(UrlFormat, UrlServer, GetServerNum(pos, 4) + 1, Version, pos.X, pos.Y, zoom); + } + + static readonly string UrlServer = "vec"; + static readonly string UrlFormat = "http://{0}0{1}.maps.yandex.ru/tiles?l=map&v={2}&x={3}&y={4}&z={5}"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Yandex/YandexSatelliteMapProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Yandex/YandexSatelliteMapProvider.cs new file mode 100644 index 0000000..005af9f --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.MapProviders/Yandex/YandexSatelliteMapProvider.cs @@ -0,0 +1,65 @@ + +namespace GMap.NET.MapProviders +{ + using System; + + /// + /// YandexSatelliteMap provider + /// + public class YandexSatelliteMapProvider : YandexMapProviderBase + { + public static readonly YandexSatelliteMapProvider Instance; + + YandexSatelliteMapProvider() + { + } + + static YandexSatelliteMapProvider() + { + Instance = new YandexSatelliteMapProvider(); + } + + public new string Version = "1.33.0"; + + #region GMapProvider Members + + readonly Guid id = new Guid("2D4CE763-0F91-40B2-A511-13EF428237AD"); + public override Guid Id + { + get + { + return id; + } + } + + readonly string name = "YandexSatelliteMap"; + public override string Name + { + get + { + return name; + } + } + + public override PureImage GetTileImage(GPoint pos, int zoom) + { + string url = MakeTileImageUrl(pos, zoom, LanguageStr); + + return GetTileImageUsingHttp(url); + } + + #endregion + + string MakeTileImageUrl(GPoint pos, int zoom, string language) + { + // http://sat04.maps.yandex.ru/tiles?l=sat&v=1.18.0&x=149511&y=83513&z=18&g=Gagari + // http://sat01.maps.yandex.net/tiles?l=sat&v=1.23.0&x=584&y=324&z=10&g=Gaga + // http://sat03.maps.yandex.net/tiles?l=sat&v=1.33.0&x=583&y=328&z=10&lang=ru-RU + + return string.Format(UrlFormat, UrlServer, GetServerNum(pos, 4) + 1, Version, pos.X, pos.Y, zoom); + } + + static readonly string UrlServer = "sat"; + static readonly string UrlFormat = "http://{0}0{1}.maps.yandex.ru/tiles?l=sat&v={2}&x={3}&y={4}&z={5}"; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.Projections/MapsLTProjection.cs b/GreatMaps/GMap.NET.Core/GMap.NET.Projections/MapsLTProjection.cs new file mode 100644 index 0000000..d230db5 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.Projections/MapsLTProjection.cs @@ -0,0 +1,748 @@ + +namespace GMap.NET.Projections +{ + using System; + + /// + /// GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.01745329251994328,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4326\"]] + /// PROJCS[\"Lietuvos Koordinoei Sistema 1994\",GEOGCS[\"LKS94 (ETRS89)\",DATUM[\"Lithuania_1994_ETRS89\",SPHEROID[\"GRS 1980\",6378137,298.257222101,AUTHORITY[\"EPSG\",\"7019\"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\",\"6126\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9108\"]],AUTHORITY[\"EPSG\",\"4126\"]],PROJECTION[\"Transverse_Mercator\"],PARAMETER[\"latitude_of_origin\",0],PARAMETER[\"central_meridian\",24],PARAMETER[\"scale_factor\",0.9998],PARAMETER[\"false_easting\",500000],PARAMETER[\"false_northing\",0],UNIT[\"metre\",1,AUTHORITY[\"EPSG\",\"9001\"]],AUTHORITY[\"EPSG\",\"2600\"]] + /// + public class LKS94Projection : PureProjection + { + public static readonly LKS94Projection Instance = new LKS94Projection(); + + static readonly double MinLatitude = 53.33; + static readonly double MaxLatitude = 56.55; + static readonly double MinLongitude = 20.22; + static readonly double MaxLongitude = 27.11; + + static readonly double orignX = 5122000; + static readonly double orignY = 10000100; + + static readonly double scaleFactor = 0.9998; // scale factor + static readonly double centralMeridian = 0.41887902047863912;// Center longitude (projection center) + static readonly double latOrigin = 0.0; // center latitude + static readonly double falseNorthing = 0.0; // y offset in meters + static readonly double falseEasting = 500000.0; // x offset in meters + static readonly double semiMajor = 6378137.0; // major axis + static readonly double semiMinor = 6356752.3141403561; // minor axis + static readonly double semiMinor2 = 6356752.3142451793; // minor axis + static readonly double metersPerUnit = 1.0; + static readonly double COS_67P5 = 0.38268343236508977; // cosine of 67.5 degrees + static readonly double AD_C = 1.0026000; // Toms region 1 constant + + public override RectLatLng Bounds + { + get + { + return RectLatLng.FromLTRB(MinLongitude, MaxLatitude, MaxLongitude, MinLatitude); + } + } + + GSize tileSize = new GSize(256, 256); + public override GSize TileSize + { + get + { + return tileSize; + } + } + + public override double Axis + { + get + { + return 6378137; + } + } + + public override double Flattening + { + get + { + return (1.0 / 298.257222101); + } + } + + public override GPoint FromLatLngToPixel(double lat, double lng, int zoom) + { + GPoint ret = GPoint.Empty; + + lat = Clip(lat, MinLatitude, MaxLatitude); + lng = Clip(lng, MinLongitude, MaxLongitude); + + double[] lks = new double[] { lng, lat }; + lks = DTM10(lks); + lks = MTD10(lks); + lks = DTM00(lks); + + double res = GetTileMatrixResolution(zoom); + + ret.X = (long)Math.Floor((lks[0] + orignX) / res); + ret.Y = (long)Math.Floor((orignY - lks[1]) / res); + + return ret; + } + + public override PointLatLng FromPixelToLatLng(long x, long y, int zoom) + { + PointLatLng ret = PointLatLng.Empty; + + double res = GetTileMatrixResolution(zoom); + + double[] lks = new double[] { (x * res) - orignX, -(y * res) + orignY }; + lks = MTD11(lks); + lks = DTM10(lks); + lks = MTD10(lks); + + ret.Lat = Clip(lks[1], MinLatitude, MaxLatitude); + ret.Lng = Clip(lks[0], MinLongitude, MaxLongitude); + + return ret; + } + + double[] DTM10(double[] lonlat) + { + // Eccentricity squared : (a^2 - b^2)/a^2 + double es = 1.0 - (semiMinor2 * semiMinor2) / (semiMajor * semiMajor); // e^2 + + // Second eccentricity squared : (a^2 - b^2)/b^2 + double ses = (Math.Pow(semiMajor, 2) - Math.Pow(semiMinor2, 2)) / Math.Pow(semiMinor2, 2); + + double ba = semiMinor2 / semiMajor; + double ab = semiMajor / semiMinor2; + + double lon = DegreesToRadians(lonlat[0]); + double lat = DegreesToRadians(lonlat[1]); + double h = lonlat.Length < 3 ? 0 : lonlat[2].Equals(Double.NaN) ? 0 : lonlat[2]; + double v = semiMajor / Math.Sqrt(1 - es * Math.Pow(Math.Sin(lat), 2)); + double x = (v + h) * Math.Cos(lat) * Math.Cos(lon); + double y = (v + h) * Math.Cos(lat) * Math.Sin(lon); + double z = ((1 - es) * v + h) * Math.Sin(lat); + return new double[] { x, y, z, }; + } + + double[] MTD10(double[] pnt) + { + // Eccentricity squared : (a^2 - b^2)/a^2 + double es = 1.0 - (semiMinor * semiMinor) / (semiMajor * semiMajor); // e^2 + + // Second eccentricity squared : (a^2 - b^2)/b^2 + double ses = (Math.Pow(semiMajor, 2) - Math.Pow(semiMinor, 2)) / Math.Pow(semiMinor, 2); + + double ba = semiMinor / semiMajor; + double ab = semiMajor / semiMinor; + + bool AtPole = false; // is location in polar region + double Z = pnt.Length < 3 ? 0 : pnt[2].Equals(Double.NaN) ? 0 : pnt[2]; + + double lon = 0; + double lat = 0; + double Height = 0; + if(pnt[0] != 0.0) + { + lon = Math.Atan2(pnt[1], pnt[0]); + } + else + { + if(pnt[1] > 0) + { + lon = Math.PI / 2; + } + else + if(pnt[1] < 0) + { + lon = -Math.PI * 0.5; + } + else + { + AtPole = true; + lon = 0.0; + if(Z > 0.0) // north pole + { + lat = Math.PI * 0.5; + } + else + if(Z < 0.0) // south pole + { + lat = -Math.PI * 0.5; + } + else // center of earth + { + return new double[] { RadiansToDegrees(lon), RadiansToDegrees(Math.PI * 0.5), -semiMinor, }; + } + } + } + double W2 = pnt[0] * pnt[0] + pnt[1] * pnt[1]; // Square of distance from Z axis + double W = Math.Sqrt(W2); // distance from Z axis + double T0 = Z * AD_C; // initial estimate of vertical component + double S0 = Math.Sqrt(T0 * T0 + W2); // initial estimate of horizontal component + double Sin_B0 = T0 / S0; // sin(B0), B0 is estimate of Bowring aux variable + double Cos_B0 = W / S0; // cos(B0) + double Sin3_B0 = Math.Pow(Sin_B0, 3); + double T1 = Z + semiMinor * ses * Sin3_B0; // corrected estimate of vertical component + double Sum = W - semiMajor * es * Cos_B0 * Cos_B0 * Cos_B0; // numerator of cos(phi1) + double S1 = Math.Sqrt(T1 * T1 + Sum * Sum); // corrected estimate of horizontal component + double Sin_p1 = T1 / S1; // sin(phi1), phi1 is estimated latitude + double Cos_p1 = Sum / S1; // cos(phi1) + double Rn = semiMajor / Math.Sqrt(1.0 - es * Sin_p1 * Sin_p1); // Earth radius at location + if(Cos_p1 >= COS_67P5) + { + Height = W / Cos_p1 - Rn; + } + else + if(Cos_p1 <= -COS_67P5) + { + Height = W / -Cos_p1 - Rn; + } + else + { + Height = Z / Sin_p1 + Rn * (es - 1.0); + } + + if(!AtPole) + { + lat = Math.Atan(Sin_p1 / Cos_p1); + } + return new double[] { RadiansToDegrees(lon), RadiansToDegrees(lat), Height, }; + } + + double[] DTM00(double[] lonlat) + { + double e0, e1, e2, e3; // eccentricity constants + double e, es, esp; // eccentricity constants + double ml0; // small value m + + es = 1.0 - Math.Pow(semiMinor / semiMajor, 2); + e = Math.Sqrt(es); + e0 = e0fn(es); + e1 = e1fn(es); + e2 = e2fn(es); + e3 = e3fn(es); + ml0 = semiMajor * mlfn(e0, e1, e2, e3, latOrigin); + esp = es / (1.0 - es); + + // ... + + double lon = DegreesToRadians(lonlat[0]); + double lat = DegreesToRadians(lonlat[1]); + + double delta_lon = 0.0; // Delta longitude (Given longitude - center) + double sin_phi, cos_phi; // sin and cos value + double al, als; // temporary values + double c, t, tq; // temporary values + double con, n, ml; // cone constant, small m + + delta_lon = AdjustLongitude(lon - centralMeridian); + SinCos(lat, out sin_phi, out cos_phi); + + al = cos_phi * delta_lon; + als = Math.Pow(al, 2); + c = esp * Math.Pow(cos_phi, 2); + tq = Math.Tan(lat); + t = Math.Pow(tq, 2); + con = 1.0 - es * Math.Pow(sin_phi, 2); + n = semiMajor / Math.Sqrt(con); + ml = semiMajor * mlfn(e0, e1, e2, e3, lat); + + double x = scaleFactor * n * al * (1.0 + als / 6.0 * (1.0 - t + c + als / 20.0 * + (5.0 - 18.0 * t + Math.Pow(t, 2) + 72.0 * c - 58.0 * esp))) + falseEasting; + + double y = scaleFactor * (ml - ml0 + n * tq * (als * (0.5 + als / 24.0 * + (5.0 - t + 9.0 * c + 4.0 * Math.Pow(c, 2) + als / 30.0 * (61.0 - 58.0 * t + + Math.Pow(t, 2) + 600.0 * c - 330.0 * esp))))) + falseNorthing; + + if(lonlat.Length < 3) + return new double[] { x / metersPerUnit, y / metersPerUnit }; + else + return new double[] { x / metersPerUnit, y / metersPerUnit, lonlat[2] }; + } + + double[] DTM01(double[] lonlat) + { + // Eccentricity squared : (a^2 - b^2)/a^2 + double es = 1.0 - (semiMinor * semiMinor) / (semiMajor * semiMajor); + + // Second eccentricity squared : (a^2 - b^2)/b^2 + double ses = (Math.Pow(semiMajor, 2) - Math.Pow(semiMinor, 2)) / Math.Pow(semiMinor, 2); + + double ba = semiMinor / semiMajor; + double ab = semiMajor / semiMinor; + + double lon = DegreesToRadians(lonlat[0]); + double lat = DegreesToRadians(lonlat[1]); + double h = lonlat.Length < 3 ? 0 : lonlat[2].Equals(Double.NaN) ? 0 : lonlat[2]; + double v = semiMajor / Math.Sqrt(1 - es * Math.Pow(Math.Sin(lat), 2)); + double x = (v + h) * Math.Cos(lat) * Math.Cos(lon); + double y = (v + h) * Math.Cos(lat) * Math.Sin(lon); + double z = ((1 - es) * v + h) * Math.Sin(lat); + return new double[] { x, y, z, }; + } + + double[] MTD01(double[] pnt) + { + // Eccentricity squared : (a^2 - b^2)/a^2 + double es = 1.0 - (semiMinor2 * semiMinor2) / (semiMajor * semiMajor); + + // Second eccentricity squared : (a^2 - b^2)/b^2 + double ses = (Math.Pow(semiMajor, 2) - Math.Pow(semiMinor2, 2)) / Math.Pow(semiMinor2, 2); + + double ba = semiMinor2 / semiMajor; + double ab = semiMajor / semiMinor2; + + bool At_Pole = false; // is location in polar region + double Z = pnt.Length < 3 ? 0 : pnt[2].Equals(Double.NaN) ? 0 : pnt[2]; + + double lon = 0; + double lat = 0; + double Height = 0; + if(pnt[0] != 0.0) + { + lon = Math.Atan2(pnt[1], pnt[0]); + } + else + { + if(pnt[1] > 0) + { + lon = Math.PI / 2; + } + else + if(pnt[1] < 0) + { + lon = -Math.PI * 0.5; + } + else + { + At_Pole = true; + lon = 0.0; + if(Z > 0.0) // north pole + { + lat = Math.PI * 0.5; + } + else + if(Z < 0.0) // south pole + { + lat = -Math.PI * 0.5; + } + else // center of earth + { + return new double[] { RadiansToDegrees(lon), RadiansToDegrees(Math.PI * 0.5), -semiMinor2, }; + } + } + } + + double W2 = pnt[0] * pnt[0] + pnt[1] * pnt[1]; // Square of distance from Z axis + double W = Math.Sqrt(W2); // distance from Z axis + double T0 = Z * AD_C; // initial estimate of vertical component + double S0 = Math.Sqrt(T0 * T0 + W2); //initial estimate of horizontal component + double Sin_B0 = T0 / S0; // sin(B0), B0 is estimate of Bowring aux variable + double Cos_B0 = W / S0; // cos(B0) + double Sin3_B0 = Math.Pow(Sin_B0, 3); + double T1 = Z + semiMinor2 * ses * Sin3_B0; //corrected estimate of vertical component + double Sum = W - semiMajor * es * Cos_B0 * Cos_B0 * Cos_B0; // numerator of cos(phi1) + double S1 = Math.Sqrt(T1 * T1 + Sum * Sum); // corrected estimate of horizontal component + double Sin_p1 = T1 / S1; // sin(phi1), phi1 is estimated latitude + double Cos_p1 = Sum / S1; // cos(phi1) + double Rn = semiMajor / Math.Sqrt(1.0 - es * Sin_p1 * Sin_p1); // Earth radius at location + + if(Cos_p1 >= COS_67P5) + { + Height = W / Cos_p1 - Rn; + } + else + if(Cos_p1 <= -COS_67P5) + { + Height = W / -Cos_p1 - Rn; + } + else + { + Height = Z / Sin_p1 + Rn * (es - 1.0); + } + + if(!At_Pole) + { + lat = Math.Atan(Sin_p1 / Cos_p1); + } + return new double[] { RadiansToDegrees(lon), RadiansToDegrees(lat), Height, }; + } + + double[] MTD11(double[] p) + { + double e0, e1, e2, e3; // eccentricity constants + double e, es, esp; // eccentricity constants + double ml0; // small value m + + es = 1.0 - Math.Pow(semiMinor / semiMajor, 2); + e = Math.Sqrt(es); + e0 = e0fn(es); + e1 = e1fn(es); + e2 = e2fn(es); + e3 = e3fn(es); + ml0 = semiMajor * mlfn(e0, e1, e2, e3, latOrigin); + esp = es / (1.0 - es); + + // ... + + double con, phi; + double delta_phi; + long i; + double sin_phi, cos_phi, tan_phi; + double c, cs, t, ts, n, r, d, ds; + long max_iter = 6; + + double x = p[0] * metersPerUnit - falseEasting; + double y = p[1] * metersPerUnit - falseNorthing; + + con = (ml0 + y / scaleFactor) / semiMajor; + phi = con; + for(i = 0; ; i++) + { + delta_phi = ((con + e1 * Math.Sin(2.0 * phi) - e2 * Math.Sin(4.0 * phi) + e3 * Math.Sin(6.0 * phi)) / e0) - phi; + phi += delta_phi; + + if(Math.Abs(delta_phi) <= EPSLoN) + break; + + if(i >= max_iter) + throw new ArgumentException("Latitude failed to converge"); + } + + if(Math.Abs(phi) < HALF_PI) + { + SinCos(phi, out sin_phi, out cos_phi); + tan_phi = Math.Tan(phi); + c = esp * Math.Pow(cos_phi, 2); + cs = Math.Pow(c, 2); + t = Math.Pow(tan_phi, 2); + ts = Math.Pow(t, 2); + con = 1.0 - es * Math.Pow(sin_phi, 2); + n = semiMajor / Math.Sqrt(con); + r = n * (1.0 - es) / con; + d = x / (n * scaleFactor); + ds = Math.Pow(d, 2); + + double lat = phi - (n * tan_phi * ds / r) * (0.5 - ds / 24.0 * (5.0 + 3.0 * t + + 10.0 * c - 4.0 * cs - 9.0 * esp - ds / 30.0 * (61.0 + 90.0 * t + + 298.0 * c + 45.0 * ts - 252.0 * esp - 3.0 * cs))); + + double lon = AdjustLongitude(centralMeridian + (d * (1.0 - ds / 6.0 * (1.0 + 2.0 * t + + c - ds / 20.0 * (5.0 - 2.0 * c + 28.0 * t - 3.0 * cs + 8.0 * esp + + 24.0 * ts))) / cos_phi)); + + if(p.Length < 3) + return new double[] { RadiansToDegrees(lon), RadiansToDegrees(lat) }; + else + return new double[] { RadiansToDegrees(lon), RadiansToDegrees(lat), p[2] }; + } + else + { + if(p.Length < 3) + return new double[] { RadiansToDegrees(HALF_PI * Sign(y)), RadiansToDegrees(centralMeridian) }; + else + return new double[] { RadiansToDegrees(HALF_PI * Sign(y)), RadiansToDegrees(centralMeridian), p[2] }; + } + } + + #region -- levels info -- + //"tileInfo":{"rows":256,"cols":256,"dpi":96,"format":"PNG32","compressionQuality":0, + //"origin":{"x":-5122000,"y":10000100},"spatialReference":{"wkid":3346}, + + //{"level":0,"resolution":1587.50317500635,"scale":6000000}, + //{"level":1,"resolution":793.751587503175,"scale":3000000}, + //{"level":2,"resolution":529.167725002117,"scale":2000000}, + //{"level":3,"resolution":264.583862501058,"scale":1000000}, + //{"level":4,"resolution":132.291931250529,"scale":500000}, + //{"level":5,"resolution":52.9167725002117,"scale":200000}, + //{"level":6,"resolution":26.4583862501058,"scale":100000}, + //{"level":7,"resolution":13.2291931250529,"scale":50000}, + //{"level":8,"resolution":6.61459656252646,"scale":25000}, + //{"level":9,"resolution":2.64583862501058,"scale":10000}, + //{"level":10,"resolution":1.32291931250529,"scale":5000}, + //{"level":11,"resolution":0.529167725002117,"scale":2000}, + //{"level":12,"resolution":0.264583862501058,"scale":1000}]}, + + //"initialExtent":{"xmin":-412335.466179159,"ymin":5288235.83180987, + // "xmax":1417335.46617916,"ymax":6965767.82449726, + + //"fullExtent":{"xmin":-45000,"ymin":5750000, + // "xmax":1050000,"ymax":6500000, units":"esriMeters" + #endregion + + public static double GetTileMatrixResolution(int zoom) + { + double ret = 0; + + switch(zoom) + { + #region -- sizes -- + case 0: + { + ret = 1587.50317500635; + } + break; + + case 1: + { + ret = 793.751587503175; + } + break; + + case 2: + { + ret = 529.167725002117; + } + break; + + case 3: + { + ret = 264.583862501058; + } + break; + + case 4: + { + ret = 132.291931250529; + } + break; + + case 5: + { + ret = 52.9167725002117; + } + break; + + case 6: + { + ret = 26.4583862501058; + } + break; + + case 7: + { + ret = 13.2291931250529; + } + break; + + case 8: + { + ret = 6.61459656252646; + } + break; + + case 9: + { + ret = 2.64583862501058; + } + break; + + case 10: + { + ret = 1.32291931250529; + } + break; + + case 11: + { + ret = 0.529167725002117; + } + break; + + case 12: + { + ret = 0.264583862501058; + } + break; + #endregion + } + + return ret; + } + + public override double GetGroundResolution(int zoom, double latitude) + { + return GetTileMatrixResolution(zoom); + } + + public override GSize GetTileMatrixMinXY(int zoom) + { + GSize ret = GSize.Empty; + + switch(zoom) + { + #region -- sizes -- + case 0: + { + ret = new GSize(12, 8); + } + break; + + case 1: + { + ret = new GSize(24, 17); + } + break; + + case 2: + { + ret = new GSize(37, 25); + } + break; + + case 3: + { + ret = new GSize(74, 51); + } + break; + + case 4: + { + ret = new GSize(149, 103); + } + break; + + case 5: + { + ret = new GSize(374, 259); + } + break; + + case 6: + { + ret = new GSize(749, 519); + } + break; + + case 7: + { + ret = new GSize(1594, 1100); + } + break; + + case 8: + { + ret = new GSize(3188, 2201); + } + break; + + case 9: + { + ret = new GSize(7971, 5502); + } + break; + + case 10: + { + ret = new GSize(15943, 11005); + } + break; + + case 11: + { + ret = new GSize(39858, 27514); + } + break; + + case 12: + { + ret = new GSize(79716, 27514); + } + break; + + #endregion + } + + return ret; + } + + public override GSize GetTileMatrixMaxXY(int zoom) + { + GSize ret = GSize.Empty; + + switch(zoom) + { + #region -- sizes -- + case 0: + { + ret = new GSize(14, 10); + } + break; + + case 1: + { + ret = new GSize(30, 20); + } + break; + + case 2: + { + ret = new GSize(45, 31); + } + break; + + case 3: + { + ret = new GSize(90, 62); + } + break; + + case 4: + { + ret = new GSize(181, 125); + } + break; + + case 5: + { + ret = new GSize(454, 311); + } + break; + + case 6: + { + ret = new GSize(903, 623); + } + break; + + case 7: + { + ret = new GSize(1718, 1193); + } + break; + + case 8: + { + ret = new GSize(3437, 2386); + } + break; + + case 9: + { + ret = new GSize(8594, 5966); + } + break; + + case 10: + { + ret = new GSize(17189, 11932); + } + break; + + case 11: + { + ret = new GSize(42972, 29831); + } + break; + + case 12: + { + ret = new GSize(85944, 59662); + } + break; + + #endregion + } + + return ret; + } + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.Projections/MapsLVProjection.cs b/GreatMaps/GMap.NET.Core/GMap.NET.Projections/MapsLVProjection.cs new file mode 100644 index 0000000..0591c30 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.Projections/MapsLVProjection.cs @@ -0,0 +1,727 @@ + +namespace GMap.NET.Projections +{ + using System; + + /// + /// GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.01745329251994328,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4326\"]] + /// PROJCS["LKS92 / Latvia TM",GEOGCS["LKS92",DATUM["D_Latvia_1992",SPHEROID["GRS_1980",6378137,298.257222101]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",24],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",500000],PARAMETER["false_northing",-6000000],UNIT["Meter",1]] + /// + public class LKS92Projection : PureProjection + { + public static readonly LKS92Projection Instance = new LKS92Projection(); + + static readonly double MinLatitude = 55.55; + static readonly double MaxLatitude = 58.58; + static readonly double MinLongitude = 20.22; + static readonly double MaxLongitude = 28.28; + + static readonly double orignX = 5120900; + static readonly double orignY = 3998100; + + static readonly double scaleFactor = 0.9996; // scale factor + static readonly double centralMeridian = 0.41887902047863912;// Center longitude (projection center) + static readonly double latOrigin = 0.0; // center latitude + static readonly double falseNorthing = -6000000.0; // y offset in meters + static readonly double falseEasting = 500000.0; // x offset in meters + static readonly double semiMajor = 6378137.0; // major axis + static readonly double semiMinor = 6356752.3141403561; // minor axis + static readonly double semiMinor2 = 6356752.3142451793; // minor axis + static readonly double metersPerUnit = 1.0; + static readonly double COS_67P5 = 0.38268343236508977; // cosine of 67.5 degrees + static readonly double AD_C = 1.0026000; // Toms region 1 constant + + public override RectLatLng Bounds + { + get + { + return RectLatLng.FromLTRB(MinLongitude, MaxLatitude, MaxLongitude, MinLatitude); + } + } + + GSize tileSize = new GSize(256, 256); + public override GSize TileSize + { + get + { + return tileSize; + } + } + + public override double Axis + { + get + { + return 6378137; + } + } + + public override double Flattening + { + get + { + return (1.0 / 298.257222101); + } + } + + public override GPoint FromLatLngToPixel(double lat, double lng, int zoom) + { + GPoint ret = GPoint.Empty; + + lat = Clip(lat, MinLatitude, MaxLatitude); + lng = Clip(lng, MinLongitude, MaxLongitude); + + double[] lks = new double[] { lng, lat }; + lks = DTM10(lks); + lks = MTD10(lks); + lks = DTM00(lks); + + double res = GetTileMatrixResolution(zoom); + + ret.X = (long)Math.Floor((lks[0] + orignX) / res); + ret.Y = (long)Math.Floor((orignY - lks[1]) / res); + + return ret; + } + + public override PointLatLng FromPixelToLatLng(long x, long y, int zoom) + { + PointLatLng ret = PointLatLng.Empty; + + double res = GetTileMatrixResolution(zoom); + + double[] lks = new double[] { (x * res) - orignX, -(y * res) + orignY }; + lks = MTD11(lks); + lks = DTM10(lks); + lks = MTD10(lks); + + ret.Lat = Clip(lks[1], MinLatitude, MaxLatitude); + ret.Lng = Clip(lks[0], MinLongitude, MaxLongitude); + + return ret; + } + + double[] DTM10(double[] lonlat) + { + // Eccentricity squared : (a^2 - b^2)/a^2 + double es = 1.0 - (semiMinor2 * semiMinor2) / (semiMajor * semiMajor); // e^2 + + // Second eccentricity squared : (a^2 - b^2)/b^2 + double ses = (Math.Pow(semiMajor, 2) - Math.Pow(semiMinor2, 2)) / Math.Pow(semiMinor2, 2); + + double ba = semiMinor2 / semiMajor; + double ab = semiMajor / semiMinor2; + + double lon = DegreesToRadians(lonlat[0]); + double lat = DegreesToRadians(lonlat[1]); + double h = lonlat.Length < 3 ? 0 : lonlat[2].Equals(Double.NaN) ? 0 : lonlat[2]; + double v = semiMajor / Math.Sqrt(1 - es * Math.Pow(Math.Sin(lat), 2)); + double x = (v + h) * Math.Cos(lat) * Math.Cos(lon); + double y = (v + h) * Math.Cos(lat) * Math.Sin(lon); + double z = ((1 - es) * v + h) * Math.Sin(lat); + return new double[] { x, y, z, }; + } + + double[] MTD10(double[] pnt) + { + // Eccentricity squared : (a^2 - b^2)/a^2 + double es = 1.0 - (semiMinor * semiMinor) / (semiMajor * semiMajor); // e^2 + + // Second eccentricity squared : (a^2 - b^2)/b^2 + double ses = (Math.Pow(semiMajor, 2) - Math.Pow(semiMinor, 2)) / Math.Pow(semiMinor, 2); + + double ba = semiMinor / semiMajor; + double ab = semiMajor / semiMinor; + + bool AtPole = false; // is location in polar region + double Z = pnt.Length < 3 ? 0 : pnt[2].Equals(Double.NaN) ? 0 : pnt[2]; + + double lon = 0; + double lat = 0; + double Height = 0; + if(pnt[0] != 0.0) + { + lon = Math.Atan2(pnt[1], pnt[0]); + } + else + { + if(pnt[1] > 0) + { + lon = Math.PI / 2; + } + else + if(pnt[1] < 0) + { + lon = -Math.PI * 0.5; + } + else + { + AtPole = true; + lon = 0.0; + if(Z > 0.0) // north pole + { + lat = Math.PI * 0.5; + } + else + if(Z < 0.0) // south pole + { + lat = -Math.PI * 0.5; + } + else // center of earth + { + return new double[] { RadiansToDegrees(lon), RadiansToDegrees(Math.PI * 0.5), -semiMinor, }; + } + } + } + double W2 = pnt[0] * pnt[0] + pnt[1] * pnt[1]; // Square of distance from Z axis + double W = Math.Sqrt(W2); // distance from Z axis + double T0 = Z * AD_C; // initial estimate of vertical component + double S0 = Math.Sqrt(T0 * T0 + W2); // initial estimate of horizontal component + double Sin_B0 = T0 / S0; // sin(B0), B0 is estimate of Bowring aux variable + double Cos_B0 = W / S0; // cos(B0) + double Sin3_B0 = Math.Pow(Sin_B0, 3); + double T1 = Z + semiMinor * ses * Sin3_B0; // corrected estimate of vertical component + double Sum = W - semiMajor * es * Cos_B0 * Cos_B0 * Cos_B0; // numerator of cos(phi1) + double S1 = Math.Sqrt(T1 * T1 + Sum * Sum); // corrected estimate of horizontal component + double Sin_p1 = T1 / S1; // sin(phi1), phi1 is estimated latitude + double Cos_p1 = Sum / S1; // cos(phi1) + double Rn = semiMajor / Math.Sqrt(1.0 - es * Sin_p1 * Sin_p1); // Earth radius at location + if(Cos_p1 >= COS_67P5) + { + Height = W / Cos_p1 - Rn; + } + else + if(Cos_p1 <= -COS_67P5) + { + Height = W / -Cos_p1 - Rn; + } + else + { + Height = Z / Sin_p1 + Rn * (es - 1.0); + } + + if(!AtPole) + { + lat = Math.Atan(Sin_p1 / Cos_p1); + } + return new double[] { RadiansToDegrees(lon), RadiansToDegrees(lat), Height, }; + } + + double[] DTM00(double[] lonlat) + { + double e0, e1, e2, e3; // eccentricity constants + double e, es, esp; // eccentricity constants + double ml0; // small value m + + es = 1.0 - Math.Pow(semiMinor / semiMajor, 2); + e = Math.Sqrt(es); + e0 = e0fn(es); + e1 = e1fn(es); + e2 = e2fn(es); + e3 = e3fn(es); + ml0 = semiMajor * mlfn(e0, e1, e2, e3, latOrigin); + esp = es / (1.0 - es); + + // ... + + double lon = DegreesToRadians(lonlat[0]); + double lat = DegreesToRadians(lonlat[1]); + + double delta_lon = 0.0; // Delta longitude (Given longitude - center) + double sin_phi, cos_phi; // sin and cos value + double al, als; // temporary values + double c, t, tq; // temporary values + double con, n, ml; // cone constant, small m + + delta_lon = AdjustLongitude(lon - centralMeridian); + SinCos(lat, out sin_phi, out cos_phi); + + al = cos_phi * delta_lon; + als = Math.Pow(al, 2); + c = esp * Math.Pow(cos_phi, 2); + tq = Math.Tan(lat); + t = Math.Pow(tq, 2); + con = 1.0 - es * Math.Pow(sin_phi, 2); + n = semiMajor / Math.Sqrt(con); + ml = semiMajor * mlfn(e0, e1, e2, e3, lat); + + double x = scaleFactor * n * al * (1.0 + als / 6.0 * (1.0 - t + c + als / 20.0 * + (5.0 - 18.0 * t + Math.Pow(t, 2) + 72.0 * c - 58.0 * esp))) + falseEasting; + + double y = scaleFactor * (ml - ml0 + n * tq * (als * (0.5 + als / 24.0 * + (5.0 - t + 9.0 * c + 4.0 * Math.Pow(c, 2) + als / 30.0 * (61.0 - 58.0 * t + + Math.Pow(t, 2) + 600.0 * c - 330.0 * esp))))) + falseNorthing; + + if(lonlat.Length < 3) + return new double[] { x / metersPerUnit, y / metersPerUnit }; + else + return new double[] { x / metersPerUnit, y / metersPerUnit, lonlat[2] }; + } + + double[] DTM01(double[] lonlat) + { + // Eccentricity squared : (a^2 - b^2)/a^2 + double es = 1.0 - (semiMinor * semiMinor) / (semiMajor * semiMajor); + + // Second eccentricity squared : (a^2 - b^2)/b^2 + double ses = (Math.Pow(semiMajor, 2) - Math.Pow(semiMinor, 2)) / Math.Pow(semiMinor, 2); + + double ba = semiMinor / semiMajor; + double ab = semiMajor / semiMinor; + + double lon = DegreesToRadians(lonlat[0]); + double lat = DegreesToRadians(lonlat[1]); + double h = lonlat.Length < 3 ? 0 : lonlat[2].Equals(Double.NaN) ? 0 : lonlat[2]; + double v = semiMajor / Math.Sqrt(1 - es * Math.Pow(Math.Sin(lat), 2)); + double x = (v + h) * Math.Cos(lat) * Math.Cos(lon); + double y = (v + h) * Math.Cos(lat) * Math.Sin(lon); + double z = ((1 - es) * v + h) * Math.Sin(lat); + return new double[] { x, y, z, }; + } + + double[] MTD01(double[] pnt) + { + // Eccentricity squared : (a^2 - b^2)/a^2 + double es = 1.0 - (semiMinor2 * semiMinor2) / (semiMajor * semiMajor); + + // Second eccentricity squared : (a^2 - b^2)/b^2 + double ses = (Math.Pow(semiMajor, 2) - Math.Pow(semiMinor2, 2)) / Math.Pow(semiMinor2, 2); + + double ba = semiMinor2 / semiMajor; + double ab = semiMajor / semiMinor2; + + bool At_Pole = false; // is location in polar region + double Z = pnt.Length < 3 ? 0 : pnt[2].Equals(Double.NaN) ? 0 : pnt[2]; + + double lon = 0; + double lat = 0; + double Height = 0; + if(pnt[0] != 0.0) + { + lon = Math.Atan2(pnt[1], pnt[0]); + } + else + { + if(pnt[1] > 0) + { + lon = Math.PI / 2; + } + else + if(pnt[1] < 0) + { + lon = -Math.PI * 0.5; + } + else + { + At_Pole = true; + lon = 0.0; + if(Z > 0.0) // north pole + { + lat = Math.PI * 0.5; + } + else + if(Z < 0.0) // south pole + { + lat = -Math.PI * 0.5; + } + else // center of earth + { + return new double[] { RadiansToDegrees(lon), RadiansToDegrees(Math.PI * 0.5), -semiMinor2, }; + } + } + } + + double W2 = pnt[0] * pnt[0] + pnt[1] * pnt[1]; // Square of distance from Z axis + double W = Math.Sqrt(W2); // distance from Z axis + double T0 = Z * AD_C; // initial estimate of vertical component + double S0 = Math.Sqrt(T0 * T0 + W2); //initial estimate of horizontal component + double Sin_B0 = T0 / S0; // sin(B0), B0 is estimate of Bowring aux variable + double Cos_B0 = W / S0; // cos(B0) + double Sin3_B0 = Math.Pow(Sin_B0, 3); + double T1 = Z + semiMinor2 * ses * Sin3_B0; //corrected estimate of vertical component + double Sum = W - semiMajor * es * Cos_B0 * Cos_B0 * Cos_B0; // numerator of cos(phi1) + double S1 = Math.Sqrt(T1 * T1 + Sum * Sum); // corrected estimate of horizontal component + double Sin_p1 = T1 / S1; // sin(phi1), phi1 is estimated latitude + double Cos_p1 = Sum / S1; // cos(phi1) + double Rn = semiMajor / Math.Sqrt(1.0 - es * Sin_p1 * Sin_p1); // Earth radius at location + + if(Cos_p1 >= COS_67P5) + { + Height = W / Cos_p1 - Rn; + } + else + if(Cos_p1 <= -COS_67P5) + { + Height = W / -Cos_p1 - Rn; + } + else + { + Height = Z / Sin_p1 + Rn * (es - 1.0); + } + + if(!At_Pole) + { + lat = Math.Atan(Sin_p1 / Cos_p1); + } + return new double[] { RadiansToDegrees(lon), RadiansToDegrees(lat), Height, }; + } + + double[] MTD11(double[] p) + { + double e0, e1, e2, e3; // eccentricity constants + double e, es, esp; // eccentricity constants + double ml0; // small value m + + es = 1.0 - Math.Pow(semiMinor / semiMajor, 2); + e = Math.Sqrt(es); + e0 = e0fn(es); + e1 = e1fn(es); + e2 = e2fn(es); + e3 = e3fn(es); + ml0 = semiMajor * mlfn(e0, e1, e2, e3, latOrigin); + esp = es / (1.0 - es); + + // ... + + double con, phi; + double delta_phi; + long i; + double sin_phi, cos_phi, tan_phi; + double c, cs, t, ts, n, r, d, ds; + long max_iter = 6; + + double x = p[0] * metersPerUnit - falseEasting; + double y = p[1] * metersPerUnit - falseNorthing; + + con = (ml0 + y / scaleFactor) / semiMajor; + phi = con; + for(i = 0; ; i++) + { + delta_phi = ((con + e1 * Math.Sin(2.0 * phi) - e2 * Math.Sin(4.0 * phi) + e3 * Math.Sin(6.0 * phi)) / e0) - phi; + phi += delta_phi; + + if(Math.Abs(delta_phi) <= EPSLoN) + break; + + if(i >= max_iter) + throw new ArgumentException("Latitude failed to converge"); + } + + if(Math.Abs(phi) < HALF_PI) + { + SinCos(phi, out sin_phi, out cos_phi); + tan_phi = Math.Tan(phi); + c = esp * Math.Pow(cos_phi, 2); + cs = Math.Pow(c, 2); + t = Math.Pow(tan_phi, 2); + ts = Math.Pow(t, 2); + con = 1.0 - es * Math.Pow(sin_phi, 2); + n = semiMajor / Math.Sqrt(con); + r = n * (1.0 - es) / con; + d = x / (n * scaleFactor); + ds = Math.Pow(d, 2); + + double lat = phi - (n * tan_phi * ds / r) * (0.5 - ds / 24.0 * (5.0 + 3.0 * t + + 10.0 * c - 4.0 * cs - 9.0 * esp - ds / 30.0 * (61.0 + 90.0 * t + + 298.0 * c + 45.0 * ts - 252.0 * esp - 3.0 * cs))); + + double lon = AdjustLongitude(centralMeridian + (d * (1.0 - ds / 6.0 * (1.0 + 2.0 * t + + c - ds / 20.0 * (5.0 - 2.0 * c + 28.0 * t - 3.0 * cs + 8.0 * esp + + 24.0 * ts))) / cos_phi)); + + if(p.Length < 3) + return new double[] { RadiansToDegrees(lon), RadiansToDegrees(lat) }; + else + return new double[] { RadiansToDegrees(lon), RadiansToDegrees(lat), p[2] }; + } + else + { + if(p.Length < 3) + return new double[] { RadiansToDegrees(HALF_PI * Sign(y)), RadiansToDegrees(centralMeridian) }; + else + return new double[] { RadiansToDegrees(HALF_PI * Sign(y)), RadiansToDegrees(centralMeridian), p[2] }; + } + } + + #region -- levels info -- + //"spatialReference":{"wkid":3059},"singleFusedMapCache":true,"tileInfo":{"rows":256,"cols":256,"dpi":96,"format":"PNG8","compressionQuality":0, + //"origin":{"x":-5120900,"y":3998100},"spatialReference":{"wkid":3059}, + + //"lods":[{ + //"level":0,"resolution":1587.50317500635,"scale":6000000}, + //{"level":1,"resolution":793.751587503175,"scale":3000000}, + //{"level":2,"resolution":529.167725002117,"scale":2000000}, + //{"level":3,"resolution":264.583862501058,"scale":1000000}, + //{"level":4,"resolution":132.291931250529,"scale":500000}, + //{"level":5,"resolution":52.9167725002117,"scale":200000}, + //{"level":6,"resolution":26.4583862501058,"scale":100000}, + //{"level":7,"resolution":13.2291931250529,"scale":50000}, + //{"level":8,"resolution":6.61459656252646,"scale":25000}, + //{"level":9,"resolution":2.64583862501058,"scale":10000}, + //{"level":10,"resolution":1.32291931250529,"scale":5000}, + //{"level":11,"resolution":0.529167725002117,"scale":2000}]}, + + //"initialExtent":{"xmin":290284.5745,"ymin":159644.05,"xmax":785045.1155,"ymax":452176.95,"spatialReference":{"wkid":3059}}, + //"fullExtent":{"xmin":290284.5745,"ymin":159644.05,"xmax":785045.1155,"ymax":452176.95,"spatialReference":{"wkid":3059}}, + + //"units":"esriMeters","supportedImageFormatTypes":"PNG24,PNG,JPG,DIB,TIFF,EMF,PS,PDF,GIF,SVG,SVGZ,AI,BMP","documentInfo":{"Title":"ikartelv","Author":"gstanevicius","Comments":"","Subject":"","Category":"","Keywords":"","Credits":""},"capabilities":"Map,Query,Data"}); + #endregion + + public static double GetTileMatrixResolution(int zoom) + { + double ret = 0; + + switch(zoom) + { + #region -- sizes -- + case 0: + { + ret = 1587.50317500635; + } + break; + + case 1: + { + ret = 793.751587503175; + } + break; + + case 2: + { + ret = 529.167725002117; + } + break; + + case 3: + { + ret = 264.583862501058; + } + break; + + case 4: + { + ret = 132.291931250529; + } + break; + + case 5: + { + ret = 52.9167725002117; + } + break; + + case 6: + { + ret = 26.4583862501058; + } + break; + + case 7: + { + ret = 13.2291931250529; + } + break; + + case 8: + { + ret = 6.61459656252646; + } + break; + + case 9: + { + ret = 2.64583862501058; + } + break; + + case 10: + { + ret = 1.32291931250529; + } + break; + + case 11: + { + ret = 0.529167725002117; + } + break; + #endregion + } + + return ret; + } + + public override double GetGroundResolution(int zoom, double latitude) + { + return GetTileMatrixResolution(zoom); + } + + public override GSize GetTileMatrixMinXY(int zoom) + { + GSize ret = GSize.Empty; + + switch(zoom) + { + #region -- sizes -- + case 0: + { + ret = new GSize(13, 8); + } + break; + + case 1: + { + ret = new GSize(26, 17); + } + break; + + case 2: + { + ret = new GSize(39, 26); + } + break; + + case 3: + { + ret = new GSize(79, 52); + } + break; + + case 4: + { + ret = new GSize(159, 105); + } + break; + + case 5: + { + ret = new GSize(399, 262); + } + break; + + case 6: + { + ret = new GSize(798, 525); + } + break; + + case 7: + { + ret = new GSize(1597, 1050); + } + break; + + case 8: + { + ret = new GSize(3195, 2101); + } + break; + + case 9: + { + ret = new GSize(7989, 5254); + } + break; + + case 10: + { + ret = new GSize(15978, 10509); + } + break; + + case 11: + { + ret = new GSize(39945, 26273); + } + break; + #endregion + } + + return ret; + } + + public override GSize GetTileMatrixMaxXY(int zoom) + { + GSize ret = GSize.Empty; + + switch(zoom) + { + #region -- sizes -- + case 0: + { + ret = new GSize(14, 9); + } + break; + + case 1: + { + ret = new GSize(28, 18); + } + break; + + case 2: + { + ret = new GSize(43, 28); + } + break; + + case 3: + { + ret = new GSize(86, 56); + } + break; + + case 4: + { + ret = new GSize(173, 112); + } + break; + + case 5: + { + ret = new GSize(434, 282); + } + break; + + case 6: + { + ret = new GSize(868, 564); + } + break; + + case 7: + { + ret = new GSize(1737, 1129); + } + break; + + case 8: + { + ret = new GSize(3474, 2258); + } + break; + + case 9: + { + ret = new GSize(8686, 5647); + } + break; + + case 10: + { + ret = new GSize(17372, 11294); + } + break; + + case 11: + { + ret = new GSize(43430, 28236); + } + break; + #endregion + } + + return ret; + } + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.Projections/MapyCZProjection.cs b/GreatMaps/GMap.NET.Core/GMap.NET.Projections/MapyCZProjection.cs new file mode 100644 index 0000000..4015105 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.Projections/MapyCZProjection.cs @@ -0,0 +1,259 @@ + +namespace GMap.NET.Projections +{ + using System; + + /// + /// GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.01745329251994328,AUTHORITY[\"EPSG\",\"9122\"]],AUTHORITY[\"EPSG\",\"4326\"]] + /// PROJCS["Mapy.cz",GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]],PROJECTION["Transverse_Mercator"],PARAMETER["latitude_of_origin",0],PARAMETER["central_meridian",15],PARAMETER["scale_factor",0.9996],PARAMETER["false_easting",134400000],PARAMETER["false_northing",-41600000],UNIT["1/32meter",0.03125]] + /// + public class MapyCZProjection : PureProjection + { + public static readonly MapyCZProjection Instance = new MapyCZProjection(); + + static readonly double MinLatitude = 26; + static readonly double MaxLatitude = 76; + static readonly double MinLongitude = -26; + static readonly double MaxLongitude = 38; + + #region -- Common -- + static int getLCM(int zone) + { + if((zone < 1) || (zone > 60)) + { + throw new Exception("MapyCZProjection: UTM Zone number is not between 1 and 60."); + } + else + { + return ((zone * 6) - 183); + } + } + + static double roundoff(double xx, double yy) + { + var x = xx; + var y = yy; + x = Math.Round(x * Math.Pow(10, y)) / Math.Pow(10, y); + return x; + } + + static readonly double UTMSIZE = 2; + static readonly double UNITS = 1; + + #endregion + + #region -- WGSToMapyCZ -- + + public long[] WGSToPP(double la, double lo) + { + var utmEE = wgsToUTM(DegreesToRadians(la), DegreesToRadians(lo), 33); + var pp = utmEEToPP(utmEE[0], utmEE[1]); + return pp; + } + + static long[] utmEEToPP(double east, double north) + { + var x = (Math.Round(east) - (-3700000.0)) * Math.Pow(2, 5); + var y = (Math.Round(north) - (1300000.0)) * Math.Pow(2, 5); + + return new long[] { (long)x, (long)y }; + } + + double[] wgsToUTM(double la, double lo, int zone) + { + var latrad = la; + var lonrad = lo; + var latddd = RadiansToDegrees(la); + var londdd = RadiansToDegrees(lo); + + var k = 0.9996f; + var a = Axis; + var f = Flattening; + var b = a * (1.0 - f); + var e2 = (a * a - b * b) / (a * a); + var e = Math.Sqrt(e2); + var ei2 = (a * a - b * b) / (b * b); + var ei = Math.Sqrt(ei2); + var n = (a - b) / (a + b); + var G = a * (1.0 - n) * (1.0 - n * n) * (1.0 + (9 / 4.0) * n * n + (255.0 / 64.0) * Math.Pow(n, 4)) * (PI / 180.0); + var w = londdd - ((double) (zone * 6 - 183)); + w = DegreesToRadians(w); + var t = Math.Tan(latrad); + var rho = a * (1.0 - e2) / Math.Pow(1.0 - (e2 * Math.Sin(latrad) * Math.Sin(latrad)), (3 / 2.0)); + var nu = a / Math.Sqrt(1.0 - (e2 * Math.Sin(latrad) * Math.Sin(latrad))); + var psi = nu / rho; + var coslat = Math.Cos(latrad); + var sinlat = Math.Sin(latrad); + var A0 = 1 - (e2 / 4.0) - (3 * e2 * e2 / 64.0) - (5 * Math.Pow(e2, 3) / 256.0); + var A2 = (3 / 8.0) * (e2 + (e2 * e2 / 4.0) + (15 * Math.Pow(e2, 3) / 128.0)); + var A4 = (15 / 256.0) * (e2 * e2 + (3 * Math.Pow(e2, 3) / 4.0)); + var A6 = 35 * Math.Pow(e2, 3) / 3072.0; + var m = a * ((A0 * latrad) - (A2 * Math.Sin(2 * latrad)) + (A4 * Math.Sin(4 * latrad)) - (A6 * Math.Sin(6 * latrad))); + var eterm1 = (w * w / 6.0) * coslat * coslat * (psi - t * t); + var eterm2 = (Math.Pow(w, 4) / 120.0) * Math.Pow(coslat, 4) * (4 * Math.Pow(psi, 3) * (1.0 - 6 * t * t) + psi * psi * (1.0 + 8 * t * t) - psi * 2 * t * t + Math.Pow(t, 4)); + var eterm3 = (Math.Pow(w, 6) / 5040.0) * Math.Pow(coslat, 6) * (61.0 - 479 * t * t + 179 * Math.Pow(t, 4) - Math.Pow(t, 6)); + var dE = k * nu * w * coslat * (1.0 + eterm1 + eterm2 + eterm3); + var east = 500000.0 + (dE / UNITS); + east = roundoff(east, UTMSIZE); + var nterm1 = (w * w / 2.0) * nu * sinlat * coslat; + var nterm2 = (Math.Pow(w, 4) / 24.0) * nu * sinlat * Math.Pow(coslat, 3) * (4 * psi * psi + psi - t * t); + var nterm3 = (Math.Pow(w, 6) / 720.0) * nu * sinlat * Math.Pow(coslat, 5) * (8 * Math.Pow(psi, 4) * (11.0 - 24 * t * t) - 28 * Math.Pow(psi, 3) * (1.0 - 6 * t * t) + psi * psi * (1.0 - 32 * t * t) - psi * 2 * t * t + Math.Pow(t, 4)); + var nterm4 = (Math.Pow(w, 8) / 40320.0) * nu * sinlat * Math.Pow(coslat, 7) * (1385.0 - 3111 * t * t + 543 * Math.Pow(t, 4) - Math.Pow(t, 6)); + var dN = k * (m + nterm1 + nterm2 + nterm3 + nterm4); + var north = (0.0 + (dN / UNITS)); + north = roundoff(north, UTMSIZE); + + return new double[] { east, north, zone }; + } + + #endregion + + #region -- MapyCZToWGS -- + + public double[] PPToWGS(double x, double y) + { + var utmEE = ppToUTMEE(x, y); + var ret = utmToWGS(utmEE[0], utmEE[1], 33); + return ret; + } + + double[] ppToUTMEE(double x, double y) + { + var north = y * Math.Pow(2, -5) + 1300000.0; + var east = x * Math.Pow(2, -5) + (-3700000.0); + east = roundoff(east, UTMSIZE); + north = roundoff(north, UTMSIZE); + + return new double[] { east, north }; + } + + double[] utmToWGS(double eastIn, double northIn, int zone) + { + var k = 0.9996f; + var a = Axis; + var f = Flattening; + var b = a * (1.0 - f); + var e2 = (a * a - b * b) / (a * a); + var e = Math.Sqrt(e2); + var ei2 = (a * a - b * b) / (b * b); + var ei = Math.Sqrt(ei2); + var n = (a - b) / (a + b); + var G = a * (1.0 - n) * (1.0 - n * n) * (1.0 + (9 / 4.0) * n * n + (255 / 64.0) * Math.Pow(n, 4)) * (PI / 180.0); + var north = (northIn - 0) * UNITS; + var east = (eastIn - 500000.0) * UNITS; + var m = north / k; + var sigma = (m * PI) / (180.0 * G); + var footlat = sigma + ((3 * n / 2.0) - (27 * Math.Pow(n, 3) / 32.0)) * Math.Sin(2 * sigma) + ((21 * n * n / 16.0) - (55 * Math.Pow(n, 4) / 32.0)) * Math.Sin(4 * sigma) + (151 * Math.Pow(n, 3) / 96.0) * Math.Sin(6 * sigma) + (1097 * Math.Pow(n, 4) / 512.0) * Math.Sin(8 * sigma); + var rho = a * (1.0 - e2) / Math.Pow(1.0 - (e2 * Math.Sin(footlat) * Math.Sin(footlat)), (3 / 2.0)); + var nu = a / Math.Sqrt(1.0 - (e2 * Math.Sin(footlat) * Math.Sin(footlat))); + var psi = nu / rho; + var t = Math.Tan(footlat); + var x = east / (k * nu); + var laterm1 = (t / (k * rho)) * (east * x / 2.0); + var laterm2 = (t / (k * rho)) * (east * Math.Pow(x, 3) / 24.0) * (-4 * psi * psi + 9 * psi * (1 - t * t) + 12 * t * t); + var laterm3 = (t / (k * rho)) * (east * Math.Pow(x, 5) / 720.0) * (8 * Math.Pow(psi, 4) * (11 - 24 * t * t) - 12 * Math.Pow(psi, 3) * (21.0 - 71 * t * t) + 15 * psi * psi * (15.0 - 98 * t * t + 15 * Math.Pow(t, 4)) + 180 * psi * (5 * t * t - 3 * Math.Pow(t, 4)) + 360 * Math.Pow(t, 4)); + var laterm4 = (t / (k * rho)) * (east * Math.Pow(x, 7) / 40320.0) * (1385.0 + 3633 * t * t + 4095 * Math.Pow(t, 4) + 1575 * Math.Pow(t, 6)); + var latrad = footlat - laterm1 + laterm2 - laterm3 + laterm4; + var lat = RadiansToDegrees(latrad); + var seclat = 1 / Math.Cos(footlat); + var loterm1 = x * seclat; + var loterm2 = (Math.Pow(x, 3) / 6.0) * seclat * (psi + 2 * t * t); + var loterm3 = (Math.Pow(x, 5) / 120.0) * seclat * (-4 * Math.Pow(psi, 3) * (1 - 6 * t * t) + psi * psi * (9 - 68 * t * t) + 72 * psi * t * t + 24 * Math.Pow(t, 4)); + var loterm4 = (Math.Pow(x, 7) / 5040.0) * seclat * (61.0 + 662 * t * t + 1320 * Math.Pow(t, 4) + 720 * Math.Pow(t, 6)); + var w = loterm1 - loterm2 + loterm3 - loterm4; + var longrad = DegreesToRadians(getLCM(zone)) + w; + var lon = RadiansToDegrees(longrad); + + return new double[] { lat, lon, latrad, longrad }; + } + + #endregion + + public override RectLatLng Bounds + { + get + { + return RectLatLng.FromLTRB(MinLongitude, MaxLatitude, MaxLongitude, MinLatitude); + } + } + + public override GSize TileSize + { + get + { + return new GSize(256, 256); + } + } + + public override double Axis + { + get + { + return 6378137; + } + } + + public override double Flattening + { + get + { + return (1.0 / 298.257223563); + } + } + + public override GPoint FromLatLngToPixel(double lat, double lng, int zoom) + { + GPoint ret = GPoint.Empty; + + lat = Clip(lat, MinLatitude, MaxLatitude); + lng = Clip(lng, MinLongitude, MaxLongitude); + + var size = GetTileMatrixSizePixel(zoom); + { + var l = WGSToPP(lat, lng); + ret.X = (long)l[0] >> (20 - zoom); + ret.Y = size.Height - ((long)l[1] >> (20 - zoom)); + } + return ret; + } + + public override PointLatLng FromPixelToLatLng(long x, long y, int zoom) + { + PointLatLng ret = PointLatLng.Empty; + + var size = GetTileMatrixSizePixel(zoom); + + var oX = x << (20 - zoom); + var oY = (size.Height - y) << (20 - zoom); + { + var l = PPToWGS(oX, oY); + ret.Lat = Clip(l[0], MinLatitude, MaxLatitude); + ret.Lng = Clip(l[1], MinLongitude, MaxLongitude); + } + return ret; + } + + public override GSize GetTileMatrixSizeXY(int zoom) + { + return new GSize((long)Math.Pow(2, zoom), (long)Math.Pow(2, zoom)); + } + + public override GSize GetTileMatrixSizePixel(int zoom) + { + GSize s = GetTileMatrixSizeXY(zoom); + return new GSize(s.Width << 8, s.Height << 8); + } + + public override GSize GetTileMatrixMinXY(int zoom) + { + long wh = zoom > 3 ? (3 * (long)Math.Pow(2, zoom - 4)) : 1; + return new GSize(wh, wh); + } + + public override GSize GetTileMatrixMaxXY(int zoom) + { + long wh = (long)Math.Pow(2, zoom) - (long)Math.Pow(2, zoom - 2); + return new GSize(wh, wh); + } + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.Projections/MercatorProjection.cs b/GreatMaps/GMap.NET.Core/GMap.NET.Projections/MercatorProjection.cs new file mode 100644 index 0000000..9f10d5c --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.Projections/MercatorProjection.cs @@ -0,0 +1,101 @@ + +namespace GMap.NET.Projections +{ + using System; + + /// + /// The Mercator projection + /// PROJCS["World_Mercator",GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]],PROJECTION["Mercator"],PARAMETER["False_Easting",0],PARAMETER["False_Northing",0],PARAMETER["Central_Meridian",0],PARAMETER["standard_parallel_1",0],UNIT["Meter",1]] + /// + public class MercatorProjection : PureProjection + { + public static readonly MercatorProjection Instance = new MercatorProjection(); + + static readonly double MinLatitude = -85.05112878; + static readonly double MaxLatitude = 85.05112878; + static readonly double MinLongitude = -180; + static readonly double MaxLongitude = 180; + + public override RectLatLng Bounds + { + get + { + return RectLatLng.FromLTRB(MinLongitude, MaxLatitude, MaxLongitude, MinLatitude); + } + } + + readonly GSize tileSize = new GSize(256, 256); + public override GSize TileSize + { + get + { + return tileSize; + } + } + + public override double Axis + { + get + { + return 6378137; + } + } + + public override double Flattening + { + get + { + return (1.0 / 298.257223563); + } + } + + public override GPoint FromLatLngToPixel(double lat, double lng, int zoom) + { + GPoint ret = GPoint.Empty; + + lat = Clip(lat, MinLatitude, MaxLatitude); + lng = Clip(lng, MinLongitude, MaxLongitude); + + double x = (lng + 180) / 360; + double sinLatitude = Math.Sin(lat * Math.PI / 180); + double y = 0.5 - Math.Log((1 + sinLatitude) / (1 - sinLatitude)) / (4 * Math.PI); + + GSize s = GetTileMatrixSizePixel(zoom); + long mapSizeX = s.Width; + long mapSizeY = s.Height; + + ret.X = (long)Clip(x * mapSizeX + 0.5, 0, mapSizeX - 1); + ret.Y = (long)Clip(y * mapSizeY + 0.5, 0, mapSizeY - 1); + + return ret; + } + + public override PointLatLng FromPixelToLatLng(long x, long y, int zoom) + { + PointLatLng ret = PointLatLng.Empty; + + GSize s = GetTileMatrixSizePixel(zoom); + double mapSizeX = s.Width; + double mapSizeY = s.Height; + + double xx = (Clip(x, 0, mapSizeX - 1) / mapSizeX) - 0.5; + double yy = 0.5 - (Clip(y, 0, mapSizeY - 1) / mapSizeY); + + ret.Lat = 90 - 360 * Math.Atan(Math.Exp(-yy * 2 * Math.PI)) / Math.PI; + ret.Lng = 360 * xx; + + return ret; + } + + public override GSize GetTileMatrixMinXY(int zoom) + { + return new GSize(0, 0); + } + + public override GSize GetTileMatrixMaxXY(int zoom) + { + long xy = (1 << zoom); + return new GSize(xy - 1, xy - 1); + } + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.Projections/MercatorProjectionYandex.cs b/GreatMaps/GMap.NET.Core/GMap.NET.Projections/MercatorProjectionYandex.cs new file mode 100644 index 0000000..3d663fa --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.Projections/MercatorProjectionYandex.cs @@ -0,0 +1,113 @@ + +namespace GMap.NET.Projections +{ + using System; + + class MercatorProjectionYandex : PureProjection + { + public static readonly MercatorProjectionYandex Instance = new MercatorProjectionYandex(); + + static readonly double MinLatitude = -85.05112878; + static readonly double MaxLatitude = 85.05112878; + static readonly double MinLongitude = -177; + static readonly double MaxLongitude = 177; + + static readonly double RAD_DEG = 180 / Math.PI; + static readonly double DEG_RAD = Math.PI / 180; + static readonly double MathPiDiv4 = Math.PI / 4; + + public override RectLatLng Bounds + { + get + { + return RectLatLng.FromLTRB(MinLongitude, MaxLatitude, MaxLongitude, MinLatitude); + } + } + + GSize tileSize = new GSize(256, 256); + public override GSize TileSize + { + get + { + return tileSize; + } + } + + public override double Axis + { + get + { + return 6356752.3142; + } + } + + public override double Flattening + { + get + { + return (1.0 / 298.257223563); + } + } + + public override GPoint FromLatLngToPixel(double lat, double lng, int zoom) + { + lat = Clip(lat, MinLatitude, MaxLatitude); + lng = Clip(lng, MinLongitude, MaxLongitude); + + double rLon = lng * DEG_RAD; // Math.PI / 180; + double rLat = lat * DEG_RAD; // Math.PI / 180; + + double a = 6378137; + double k = 0.0818191908426; + + double z = Math.Tan(MathPiDiv4 + rLat / 2) / Math.Pow((Math.Tan(MathPiDiv4 + Math.Asin(k * Math.Sin(rLat)) / 2)), k); + double z1 = Math.Pow(2, 23 - zoom); + + double DX = ((20037508.342789 + a * rLon) * 53.5865938 / z1); + double DY = ((20037508.342789 - a * Math.Log(z)) * 53.5865938 / z1); + + GPoint ret = GPoint.Empty; + ret.X = (long)DX; + ret.Y = (long)DY; + + return ret; + } + + public override PointLatLng FromPixelToLatLng(long x, long y, int zoom) + { + GSize s = GetTileMatrixSizePixel(zoom); + + double mapSizeX = s.Width; + double mapSizeY = s.Height; + + double a = 6378137; + double c1 = 0.00335655146887969; + double c2 = 0.00000657187271079536; + double c3 = 0.00000001764564338702; + double c4 = 0.00000000005328478445; + double z1 = (23 - zoom); + double mercX = (x * Math.Pow(2, z1)) / 53.5865938 - 20037508.342789; + double mercY = 20037508.342789 - (y * Math.Pow(2, z1)) / 53.5865938; + + double g = Math.PI / 2 - 2 * Math.Atan(1 / Math.Exp(mercY / a)); + double z = g + c1 * Math.Sin(2 * g) + c2 * Math.Sin(4 * g) + c3 * Math.Sin(6 * g) + c4 * Math.Sin(8 * g); + + PointLatLng ret = PointLatLng.Empty; + ret.Lat = z * RAD_DEG; + ret.Lng = mercX / a * RAD_DEG; + + return ret; + } + + public override GSize GetTileMatrixMinXY(int zoom) + { + return new GSize(0, 0); + } + + public override GSize GetTileMatrixMaxXY(int zoom) + { + long xy = (1 << zoom); + return new GSize(xy - 1, xy - 1); + } + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.Projections/PlateCarreeProjection.cs b/GreatMaps/GMap.NET.Core/GMap.NET.Projections/PlateCarreeProjection.cs new file mode 100644 index 0000000..c7d4709 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.Projections/PlateCarreeProjection.cs @@ -0,0 +1,98 @@ + +namespace GMap.NET.Projections +{ + using System; + + /// + /// Plate Carrée (literally, “plane square”) projection + /// PROJCS["WGS 84 / World Equidistant Cylindrical",GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]],UNIT["Meter",1]] + /// + public class PlateCarreeProjection : PureProjection + { + public static readonly PlateCarreeProjection Instance = new PlateCarreeProjection(); + + static readonly double MinLatitude = -85.05112878; + static readonly double MaxLatitude = 85.05112878; + static readonly double MinLongitude = -180; + static readonly double MaxLongitude = 180; + + public override RectLatLng Bounds + { + get + { + return RectLatLng.FromLTRB(MinLongitude, MaxLatitude, MaxLongitude, MinLatitude); + } + } + + GSize tileSize = new GSize(512, 512); + public override GSize TileSize + { + get + { + return tileSize; + } + } + + public override double Axis + { + get + { + return 6378137; + } + } + + public override double Flattening + { + get + { + return (1.0 / 298.257223563); + } + } + + public override GPoint FromLatLngToPixel(double lat, double lng, int zoom) + { + GPoint ret = GPoint.Empty; + + lat = Clip(lat, MinLatitude, MaxLatitude); + lng = Clip(lng, MinLongitude, MaxLongitude); + + GSize s = GetTileMatrixSizePixel(zoom); + double mapSizeX = s.Width; + double mapSizeY = s.Height; + + double scale = 360.0 / mapSizeX; + + ret.Y = (long)((90.0 - lat) / scale); + ret.X = (long)((lng + 180.0) / scale); + + return ret; + } + + public override PointLatLng FromPixelToLatLng(long x, long y, int zoom) + { + PointLatLng ret = PointLatLng.Empty; + + GSize s = GetTileMatrixSizePixel(zoom); + double mapSizeX = s.Width; + double mapSizeY = s.Height; + + double scale = 360.0 / mapSizeX; + + ret.Lat = 90 - (y * scale); + ret.Lng = (x * scale) - 180; + + return ret; + } + + public override GSize GetTileMatrixMaxXY(int zoom) + { + long y = (long)Math.Pow(2, zoom); + return new GSize((2 * y) - 1, y - 1); + } + + public override GSize GetTileMatrixMinXY(int zoom) + { + return new GSize(0, 0); + } + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.Projections/PlateCarreeProjectionDarbAe.cs b/GreatMaps/GMap.NET.Core/GMap.NET.Projections/PlateCarreeProjectionDarbAe.cs new file mode 100644 index 0000000..322b2ec --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.Projections/PlateCarreeProjectionDarbAe.cs @@ -0,0 +1,230 @@ + +namespace GMap.NET.Projections +{ + using System; + + /// + /// Plate Carrée (literally, “plane square”) projection + /// PROJCS["WGS 84 / World Equidistant Cylindrical",GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]],UNIT["Meter",1]] + /// + ///"spatialReference": + ///{"wkid":4326},"singleFusedMapCache":true,"tileInfo": + ///{"rows":256,"cols":256,"dpi":96,"format":"PNG8","compressionQuality":0, + ///"origin":{"x":-400,"y":400},"spatialReference":{"wkid":4326},"lods": + /// + ///[{"level":0,"resolution":0.0118973050291514,"scale":5000000}, + ///{"level":1,"resolution":0.0059486525145757,"scale":2500000}, + ///{"level":2,"resolution":0.00297432625728785,"scale":1250000}, + ///{"level":3,"resolution":0.00118973050291514,"scale":500000}, + ///{"level":4,"resolution":0.00059486525145757,"scale":250000}, + ///{"level":5,"resolution":0.000356919150874542,"scale":150000}, + ///{"level":6,"resolution":0.000178459575437271,"scale":75000}, + ///{"level":7,"resolution":0.000118973050291514,"scale":50000}, + ///{"level":8,"resolution":5.9486525145757E-05,"scale":25000}, + ///{"level":9,"resolution":3.56919150874542E-05,"scale":15000}, + ///{"level":10,"resolution":1.90356880466422E-05,"scale":8000}, + ///{"level":11,"resolution":9.51784402332112E-06,"scale":4000}, + ///{"level":12,"resolution":4.75892201166056E-06,"scale":2000}]}, + /// + ///"initialExtent": + ///{"xmin":42.1125196069871,"ymin":18.6650706214551,"xmax":65.698643558112 + ///4,"ymax":29.4472987133981,"spatialReference":{"wkid":4326}}, + /// + ///"fullExtent": + ///{"xmin":41.522866508209,"ymin":18.7071563263201,"xmax":66.2882966568906 + ///,"ymax":29.4052130085331,"spatialReference":{"wkid":4326}}, + /// + ///"units":"esriDecimalDegrees" + /// + public class PlateCarreeProjectionDarbAe : PureProjection + { + public static readonly PlateCarreeProjectionDarbAe Instance = new PlateCarreeProjectionDarbAe(); + + public static readonly double MinLatitude = 18.7071563263201; + public static readonly double MaxLatitude = 29.4052130085331; + public static readonly double MinLongitude = 41.522866508209; + public static readonly double MaxLongitude = 66.2882966568906; + + static readonly double orignX = -400; + static readonly double orignY = 400; + + public override RectLatLng Bounds + { + get + { + return RectLatLng.FromLTRB(MinLongitude, MaxLatitude, MaxLongitude, MinLatitude); + } + } + + GSize tileSize = new GSize(256, 256); + public override GSize TileSize + { + get + { + return tileSize; + } + } + + public override double Axis + { + get + { + return 6378137; + } + } + + public override double Flattening + { + get + { + return (1.0 / 298.257223563); + } + } + + public override GPoint FromLatLngToPixel(double lat, double lng, int zoom) + { + GPoint ret = GPoint.Empty; + + lat = Clip(lat, MinLatitude, MaxLatitude); + lng = Clip(lng, MinLongitude, MaxLongitude); + + /* + getContainingTileCoords:function(ti,_1dd,lod) + { + var to=ti.origin, + res=lod.resolution, + tmw=ti.width*res, + tmh=ti.height*res, + tc=Math.floor((_1dd.x-to.x)/tmw), + tr=Math.floor((to.y-_1dd.y)/tmh); + } + */ + + double res = GetTileMatrixResolution(zoom); + + ret.X = (long)Math.Floor((lng - orignX) / res); + ret.Y = (long)Math.Floor((orignY - lat) / res); + + return ret; + } + + public override PointLatLng FromPixelToLatLng(long x, long y, int zoom) + { + PointLatLng ret = PointLatLng.Empty; + + double res = GetTileMatrixResolution(zoom); + + ret.Lat = orignY - (y * res); + ret.Lng = (x * res) + orignX; + + return ret; + } + + public static double GetTileMatrixResolution(int zoom) + { + double ret = 0; + + switch(zoom) + { + #region -- sizes -- + case 0: + { + ret = 0.0118973050291514; + } + break; + + case 1: + { + ret = 0.0059486525145757; + } + break; + + case 2: + { + ret = 0.00297432625728785; + } + break; + + case 3: + { + ret = 0.00118973050291514; + } + break; + + case 4: + { + ret = 0.00059486525145757; + } + break; + + case 5: + { + ret = 0.000356919150874542; + } + break; + + case 6: + { + ret = 0.000178459575437271; + } + break; + + case 7: + { + ret = 0.000118973050291514; + } + break; + + case 8: + { + ret = 5.9486525145757E-05; + } + break; + + case 9: + { + ret = 3.56919150874542E-05; + } + break; + + case 10: + { + ret = 1.90356880466422E-05; + } + break; + + case 11: + { + ret = 9.51784402332112E-06; + } + break; + + case 12: + { + ret = 4.75892201166056E-06; + } + break; + #endregion + } + + return ret; + } + + public override double GetGroundResolution(int zoom, double latitude) + { + return GetTileMatrixResolution(zoom); + } + + public override GSize GetTileMatrixMaxXY(int zoom) + { + var maxPx = FromLatLngToPixel(MinLatitude, MaxLongitude, zoom); + return new GSize(FromPixelToTileXY(maxPx)); + } + + public override GSize GetTileMatrixMinXY(int zoom) + { + var minPx = FromLatLngToPixel(MaxLatitude, MinLongitude, zoom); + return new GSize(FromPixelToTileXY(minPx)); + } + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET.Projections/PlateCarreeProjectionPergo.cs b/GreatMaps/GMap.NET.Core/GMap.NET.Projections/PlateCarreeProjectionPergo.cs new file mode 100644 index 0000000..4b54de5 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET.Projections/PlateCarreeProjectionPergo.cs @@ -0,0 +1,100 @@ + +namespace GMap.NET.Projections +{ +#if OLD_PERGO + using System; + + /// + /// Plate Carrée (literally, “plane square”) projection + /// PROJCS["WGS 84 / World Equidistant Cylindrical",GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]],UNIT["Meter",1]] + /// + public class PlateCarreeProjectionPergo : PureProjection + { + public static readonly PlateCarreeProjectionPergo Instance = new PlateCarreeProjectionPergo(); + + static readonly double MinLatitude = -85.05112878; + static readonly double MaxLatitude = 85.05112878; + static readonly double MinLongitude = -180; + static readonly double MaxLongitude = 180; + + public override RectLatLng Bounds + { + get + { + return RectLatLng.FromLTRB(MinLongitude, MaxLatitude, MaxLongitude, MinLatitude); + } + } + + GSize tileSize = new GSize(256, 256); + public override GSize TileSize + { + get + { + return tileSize; + } + } + + public override double Axis + { + get + { + return 6378137; + } + } + + public override double Flattening + { + get + { + return (1.0 / 298.257223563); + } + } + + public override GPoint FromLatLngToPixel(double lat, double lng, int zoom) + { + GPoint ret = GPoint.Empty; + + lat = Clip(lat, MinLatitude, MaxLatitude); + lng = Clip(lng, MinLongitude, MaxLongitude); + + GSize s = GetTileMatrixSizePixel(zoom); + double mapSizeX = s.Width; + double mapSizeY = s.Height; + + double scale = 360.0 / mapSizeX; + + ret.Y = (long)((90.0 - lat) / scale); + ret.X = (long)((lng + 180.0) / scale); + + return ret; + } + + public override PointLatLng FromPixelToLatLng(long x, long y, int zoom) + { + PointLatLng ret = PointLatLng.Empty; + + GSize s = GetTileMatrixSizePixel(zoom); + double mapSizeX = s.Width; + double mapSizeY = s.Height; + + double scale = 360.0 / mapSizeX; + + ret.Lat = 90 - (y * scale); + ret.Lng = (x * scale) - 180; + + return ret; + } + + public override GSize GetTileMatrixMaxXY(int zoom) + { + long y = (long)Math.Pow(2, zoom); + return new GSize((2*y) - 1, y - 1); + } + + public override GSize GetTileMatrixMinXY(int zoom) + { + return new GSize(0, 0); + } + } +#endif +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET/AccessMode.cs b/GreatMaps/GMap.NET.Core/GMap.NET/AccessMode.cs new file mode 100644 index 0000000..781770a --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET/AccessMode.cs @@ -0,0 +1,24 @@ + +namespace GMap.NET +{ + /// + /// tile access mode + /// + public enum AccessMode + { + /// + /// access only server + /// + ServerOnly, + + /// + /// access first server and caches localy + /// + ServerAndCache, + + /// + /// access only cache + /// + CacheOnly, + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET/Delegates.cs b/GreatMaps/GMap.NET.Core/GMap.NET/Delegates.cs new file mode 100644 index 0000000..6039566 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET/Delegates.cs @@ -0,0 +1,20 @@ + +namespace GMap.NET +{ + using GMap.NET.MapProviders; + + public delegate void PositionChanged(PointLatLng point); + + public delegate void TileLoadComplete(long ElapsedMilliseconds); + public delegate void TileLoadStart(); + + public delegate void TileCacheComplete(); + public delegate void TileCacheStart(); + public delegate void TileCacheProgress(int tilesLeft); + + public delegate void MapDrag(); + public delegate void MapZoomChanged(); + public delegate void MapTypeChanged(GMapProvider type); + + public delegate void EmptyTileError(int zoom, GPoint pos); +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET/DirectionsProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET/DirectionsProvider.cs new file mode 100644 index 0000000..d4151c1 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET/DirectionsProvider.cs @@ -0,0 +1,43 @@ + +namespace GMap.NET +{ + using System.Collections.Generic; + + /// + /// directions interface + /// + interface DirectionsProvider + { + DirectionsStatusCode GetDirections(out GDirections direction, PointLatLng start, PointLatLng end, bool avoidHighways, bool avoidTolls, bool walkingMode, bool sensor, bool metric); + + DirectionsStatusCode GetDirections(out GDirections direction, string start, string end, bool avoidHighways, bool avoidTolls, bool walkingMode, bool sensor, bool metric); + + /// + /// service may provide more than one route alternative in the response + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + IEnumerable GetDirections(out DirectionsStatusCode status, string start, string end, bool avoidHighways, bool avoidTolls, bool walkingMode, bool sensor, bool metric); + + /// + /// service may provide more than one route alternative in the response + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + IEnumerable GetDirections(out DirectionsStatusCode status, PointLatLng start, PointLatLng end, bool avoidHighways, bool avoidTolls, bool walkingMode, bool sensor, bool metric); + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET/Extensions.cs b/GreatMaps/GMap.NET.Core/GMap.NET/Extensions.cs new file mode 100644 index 0000000..38c5ebe --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET/Extensions.cs @@ -0,0 +1,92 @@ + +namespace GMap.NET +{ + using System; + using System.Runtime.Serialization; + using System.Diagnostics; + + public static class Extensions + { + /// + /// Retrieves a value from the SerializationInfo of the given type. + /// + /// The Type that we are attempting to de-serialize. + /// The SerializationInfo. + /// The key of the value we wish to retrieve. + /// The value if found, otherwise null. + public static T GetValue(SerializationInfo info, string key) where T : class + { + try + { + // Return the value from the SerializationInfo, casting it to type T. + return info.GetValue(key, typeof(T)) as T; + } + catch(Exception ex) + { + Debug.WriteLine("Extensions.GetValue: " + ex.Message); + return null; + } + } + + /// + /// Retrieves a value from the SerializationInfo of the given type. + /// + /// The Type that we are attempting to de-serialize. + /// The SerializationInfo. + /// The key of the value we wish to retrieve. + /// The default value if the de-serialized value was null. + /// The value if found, otherwise the default value. + public static T GetValue(SerializationInfo info, string key, T defaultValue) where T : class + { + T deserializedValue = GetValue(info, key); + if(deserializedValue != null) + { + return deserializedValue; + } + + return defaultValue; + } + + /// + /// Retrieves a value from the SerializationInfo of the given type for structs. + /// + /// The Type that we are attempting to de-serialize. + /// The SerializationInfo. + /// The key of the value we wish to retrieve. + /// The default value if the de-serialized value was null. + /// The value if found, otherwise the default value. + public static T GetStruct(SerializationInfo info, string key, T defaultValue) where T : struct + { + try + { + return (T)info.GetValue(key, typeof(T)); + } + catch(Exception ex) + { + Debug.WriteLine("Extensions.GetStruct: " + ex.Message); + return defaultValue; + } + } + + /// + /// Retrieves a value from the SerializationInfo of the given type for structs. + /// + /// The Type that we are attempting to de-serialize. + /// The SerializationInfo. + /// The key of the value we wish to retrieve. + /// The default value if the de-serialized value was null. + /// The value if found, otherwise the default value. + public static Nullable GetStruct(SerializationInfo info, string key, Nullable defaultValue) where T : struct + { + try + { + return (Nullable)info.GetValue(key, typeof(Nullable)); + } + catch(Exception ex) + { + Debug.WriteLine("Extensions.GetStruct: " + ex.Message); + return defaultValue; + } + } + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET/GDirections.cs b/GreatMaps/GMap.NET.Core/GMap.NET/GDirections.cs new file mode 100644 index 0000000..789f112 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET/GDirections.cs @@ -0,0 +1,111 @@ + +namespace GMap.NET +{ + using System.Collections.Generic; + + public class GDirections + { + /// + /// contains a short textual description for the route, suitable for naming and disambiguating the route from alternatives. + /// + public string Summary; + + /// + /// contains a human-readable representation of the duration. + /// + public string Duration; + + /// + /// contains a human-readable representation of the distance, displayed in units as used at the origin + /// (or as overridden within the units parameter in the request), in the language specified in the request. + /// (For example, miles and feet will be used for any origin within the United States.) + /// + public string Distance; + + /// + /// contains the latitude/longitude coordinates of the origin of this leg. Because the Directions API + /// calculates directions between locations by using the nearest transportation option (usually a road) + /// at the start and end points, start_location may be different than the provided origin of this leg if, + /// for example, a road is not near the origin. + /// + public PointLatLng StartLocation; + + /// + /// contains the latitude/longitude coordinates of the given destination of this leg. Because the Directions + /// API calculates directions between locations by using the nearest transportation option (usually a road) + /// at the start and end points, end_location may be different than the provided destination of this leg if, + /// for example, a road is not near the destination. + /// + public PointLatLng EndLocation; + + /// + /// contains the human-readable address (typically a street address) reflecting the start_location of this leg. + /// + public string StartAddress; + + /// + /// contains the human-readable address (typically a street address) reflecting the end_location of this leg. + /// + public string EndAddress; + + /// + /// contains the copyrights text to be displayed for this route. You must handle and display this information yourself. + /// + public string Copyrights; + + /// + /// contains an array of steps denoting information about each separate step of the leg of the journey. + /// + public List Steps; + + /// + /// contains all points of the route + /// + public List Route; + + public override string ToString() + { + return Summary + " | " + Distance + " | " + Duration; + } + } + + public struct GDirectionStep + { + public string TravelMode; + + /// + /// contains the location of the starting point of this step, as a single set of lat and lng fields. + /// + public PointLatLng StartLocation; + + /// + /// contains the location of the ending point of this step, as a single set of lat and lng fields. + /// + public PointLatLng EndLocation; + + /// + /// contains the typical time required to perform the step, until the next step. This field may be undefined if the duration is unknown. + /// + public string Duration; + + /// + /// contains the distance covered by this step until the next step. This field may be undefined if the distance is unknown. + /// + public string Distance; + + /// + /// contains formatted instructions for this step, presented as an HTML text string. + /// + public string HtmlInstructions; + + /// + /// points of the step + /// + public List Points; + + public override string ToString() + { + return TravelMode + " | " + Distance + " | " + Duration + " | " + HtmlInstructions; + } + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET/GMaps.cs b/GreatMaps/GMap.NET.Core/GMap.NET/GMaps.cs new file mode 100644 index 0000000..81008ee --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET/GMaps.cs @@ -0,0 +1,732 @@ + +namespace GMap.NET +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Globalization; + using System.IO; + using System.Net; + using System.Text; + using System.Threading; + using System.Xml; + using System.Xml.Serialization; + using GMap.NET.CacheProviders; + using GMap.NET.Internals; + using GMap.NET.MapProviders; + +#if PocketPC + using OpenNETCF.ComponentModel; + using OpenNETCF.Threading; + using Thread=OpenNETCF.Threading.Thread2; +#endif + + /// + /// maps manager + /// + public class GMaps : Singleton + { + /// + /// tile access mode + /// + public AccessMode Mode = AccessMode.ServerAndCache; + + /// + /// is map ussing cache for routing + /// + public bool UseRouteCache = true; + + /// + /// is map using cache for geocoder + /// + public bool UseGeocoderCache = true; + + /// + /// is map using cache for directions + /// + public bool UseDirectionsCache = true; + + /// + /// is map using cache for placemarks + /// + public bool UsePlacemarkCache = true; + + /// + /// is map ussing cache for other url + /// + public bool UseUrlCache = true; + + /// + /// is map using memory cache for tiles + /// + public bool UseMemoryCache = true; + + /// + /// primary cache provider, by default: ultra fast SQLite! + /// + public PureImageCache PrimaryCache + { + get + { + return Cache.Instance.ImageCache; + } + set + { + Cache.Instance.ImageCache = value; + } + } + + /// + /// secondary cache provider, by default: none, + /// use it if you have server in your local network + /// + public PureImageCache SecondaryCache + { + get + { + return Cache.Instance.ImageCacheSecond; + } + set + { + Cache.Instance.ImageCacheSecond = value; + } + } + + /// + /// MemoryCache provider + /// + public readonly MemoryCache MemoryCache = new MemoryCache(); + + /// + /// load tiles in random sequence + /// + public bool ShuffleTilesOnLoad = true; + + /// + /// tile queue to cache + /// + readonly Queue tileCacheQueue = new Queue(); + + bool? isRunningOnMono; + + /// + /// return true if running on mono + /// + /// + public bool IsRunningOnMono + { + get + { + if(!isRunningOnMono.HasValue) + { + try + { + isRunningOnMono = (Type.GetType("Mono.Runtime") != null); + return isRunningOnMono.Value; + } + catch + { + } + } + else + { + return isRunningOnMono.Value; + } + return false; + } + } + + /// + /// cache worker + /// + Thread CacheEngine; + + internal readonly AutoResetEvent WaitForCache = new AutoResetEvent(false); + + public GMaps() + { + #region singleton check + if(Instance != null) + { + throw (new Exception("You have tried to create a new singleton class where you should have instanced it. Replace your \"new class()\" with \"class.Instance\"")); + } + #endregion + + ServicePointManager.DefaultConnectionLimit = 5; + } + +#if !PocketPC + /// + /// triggers dynamic sqlite loading, + /// call this before you use sqlite for other reasons than caching maps + /// + public void SQLitePing() + { +#if SQLite +#if !MONO + SQLitePureImageCache.Ping(); +#endif +#endif + } +#endif + + #region -- Stuff -- + +#if !PocketPC + + /// + /// exports current map cache to GMDB file + /// if file exsist only new records will be added + /// otherwise file will be created and all data exported + /// + /// + /// + public bool ExportToGMDB(string file) + { +#if SQLite + if(PrimaryCache is SQLitePureImageCache) + { + StringBuilder db = new StringBuilder((PrimaryCache as SQLitePureImageCache).GtileCache); + db.AppendFormat(CultureInfo.InvariantCulture, "{0}{1}Data.gmdb", GMapProvider.LanguageStr, Path.DirectorySeparatorChar); + + return SQLitePureImageCache.ExportMapDataToDB(db.ToString(), file); + } +#endif + return false; + } + + /// + /// imports GMDB file to current map cache + /// only new records will be added + /// + /// + /// + public bool ImportFromGMDB(string file) + { +#if SQLite + if(PrimaryCache is GMap.NET.CacheProviders.SQLitePureImageCache) + { + StringBuilder db = new StringBuilder((PrimaryCache as SQLitePureImageCache).GtileCache); + db.AppendFormat(CultureInfo.InvariantCulture, "{0}{1}Data.gmdb", GMapProvider.LanguageStr, Path.DirectorySeparatorChar); + + return SQLitePureImageCache.ExportMapDataToDB(file, db.ToString()); + } +#endif + return false; + } + +#if SQLite + + /// + /// optimizes map database, *.gmdb + /// + /// database file name or null to optimize current user db + /// + public bool OptimizeMapDb(string file) + { + if(PrimaryCache is GMap.NET.CacheProviders.SQLitePureImageCache) + { + if(string.IsNullOrEmpty(file)) + { + StringBuilder db = new StringBuilder((PrimaryCache as SQLitePureImageCache).GtileCache); + db.AppendFormat(CultureInfo.InvariantCulture, "{0}{1}Data.gmdb", GMapProvider.LanguageStr, Path.DirectorySeparatorChar); + + return SQLitePureImageCache.VacuumDb(db.ToString()); + } + else + { + return SQLitePureImageCache.VacuumDb(file); + } + } + + return false; + } +#endif + +#endif + + /// + /// enqueueens tile to cache + /// + /// + void EnqueueCacheTask(CacheQueueItem task) + { + lock(tileCacheQueue) + { + if(!tileCacheQueue.Contains(task)) + { + Debug.WriteLine("EnqueueCacheTask: " + task); + + tileCacheQueue.Enqueue(task); + + if(CacheEngine != null && CacheEngine.IsAlive) + { + WaitForCache.Set(); + } +#if PocketPC + else if(CacheEngine == null || CacheEngine.State == ThreadState.Stopped || CacheEngine.State == ThreadState.Unstarted) +#else + else if(CacheEngine == null || CacheEngine.ThreadState == System.Threading.ThreadState.Stopped || CacheEngine.ThreadState == System.Threading.ThreadState.Unstarted) +#endif + { + CacheEngine = null; + CacheEngine = new Thread(new ThreadStart(CacheEngineLoop)); + CacheEngine.Name = "CacheEngine"; + CacheEngine.IsBackground = false; + CacheEngine.Priority = ThreadPriority.Lowest; + + abortCacheLoop = false; + CacheEngine.Start(); + } + } + } + } + + volatile bool abortCacheLoop = false; + internal volatile bool noMapInstances = false; + + public TileCacheComplete OnTileCacheComplete; + public TileCacheStart OnTileCacheStart; + public TileCacheProgress OnTileCacheProgress; + + /// + /// immediately stops background tile caching, call it if you want fast exit the process + /// + public void CancelTileCaching() + { + Debug.WriteLine("CancelTileCaching..."); + + abortCacheLoop = true; + lock(tileCacheQueue) + { + tileCacheQueue.Clear(); + WaitForCache.Set(); + } + } + + int readingCache = 0; + + /// + /// delays writing tiles to cache while performing reads + /// + public volatile bool CacheOnIdleRead = true; + + /// + /// disables delay between saving tiles into database/cache + /// + public volatile bool BoostCacheEngine = false; + + /// + /// live for cache ;} + /// + /// + /// + void CacheEngineLoop() + { + Debug.WriteLine("CacheEngine: start"); + int left = 0; + + if(OnTileCacheStart != null) + { + OnTileCacheStart(); + } + + bool startEvent = false; + + while(!abortCacheLoop) + { + try + { + CacheQueueItem? task = null; + + lock(tileCacheQueue) + { + left = tileCacheQueue.Count; + if(left > 0) + { + task = tileCacheQueue.Dequeue(); + } + } + + if(task.HasValue) + { + if(startEvent) + { + startEvent = false; + + if(OnTileCacheStart != null) + { + OnTileCacheStart(); + } + } + + if(OnTileCacheProgress != null) + { + OnTileCacheProgress(left); + } + + #region -- save -- + // check if stream wasn't disposed somehow + if(task.Value.Img != null) + { + Debug.WriteLine("CacheEngine[" + left + "]: storing tile " + task.Value + ", " + task.Value.Img.Length / 1024 + "kB..."); + + if((task.Value.CacheType & CacheUsage.First) == CacheUsage.First && PrimaryCache != null) + { + if(CacheOnIdleRead) + { + while(Interlocked.Decrement(ref readingCache) > 0) + { + Thread.Sleep(1000); + } + } + PrimaryCache.PutImageToCache(task.Value.Img, task.Value.Tile.Type, task.Value.Tile.Pos, task.Value.Tile.Zoom); + } + + if((task.Value.CacheType & CacheUsage.Second) == CacheUsage.Second && SecondaryCache != null) + { + if(CacheOnIdleRead) + { + while(Interlocked.Decrement(ref readingCache) > 0) + { + Thread.Sleep(1000); + } + } + SecondaryCache.PutImageToCache(task.Value.Img, task.Value.Tile.Type, task.Value.Tile.Pos, task.Value.Tile.Zoom); + } + + task.Value.Clear(); + + if(!BoostCacheEngine) + { +#if PocketPC + Thread.Sleep(3333); +#else + Thread.Sleep(333); +#endif + } + } + else + { + Debug.WriteLine("CacheEngineLoop: skip, tile disposed to early -> " + task.Value); + } + task = null; + #endregion + } + else + { + if(!startEvent) + { + startEvent = true; + + if(OnTileCacheComplete != null) + { + OnTileCacheComplete(); + } + } + + if(abortCacheLoop || noMapInstances || !WaitForCache.WaitOne(33333, false) || noMapInstances) + { + break; + } + } + } +#if !PocketPC + catch(AbandonedMutexException) + { + break; + } +#endif + catch(Exception ex) + { + Debug.WriteLine("CacheEngineLoop: " + ex.ToString()); + } + } + Debug.WriteLine("CacheEngine: stop"); + + if(!startEvent) + { + if(OnTileCacheComplete != null) + { + OnTileCacheComplete(); + } + } + } + + class StringWriterExt : StringWriter + { + public StringWriterExt(IFormatProvider info) + : base(info) + { + + } + + public override Encoding Encoding + { + get + { + return Encoding.UTF8; + } + } + } + + public string SerializeGPX(gpxType targetInstance) + { + string retVal = string.Empty; + using(StringWriterExt writer = new StringWriterExt(CultureInfo.InvariantCulture)) + { + XmlSerializer serializer = new XmlSerializer(targetInstance.GetType()); + serializer.Serialize(writer, targetInstance); + retVal = writer.ToString(); + } + return retVal; + } + + public gpxType DeserializeGPX(string objectXml) + { + gpxType retVal = null; + + using(StringReader stringReader = new StringReader(objectXml)) + { + XmlTextReader xmlReader = new XmlTextReader(stringReader); + + XmlSerializer serializer = new XmlSerializer(typeof(gpxType)); + retVal = serializer.Deserialize(xmlReader) as gpxType; + + xmlReader.Close(); + } + return retVal; + } + + /// + /// exports gps data to gpx file + /// + /// gps data + /// file to export + /// true if success + public bool ExportGPX(IEnumerable> log, string gpxFile) + { + try + { + gpxType gpx = new gpxType(); + { + gpx.creator = "GMap.NET - http://greatmaps.codeplex.com"; + gpx.trk = new trkType[1]; + gpx.trk[0] = new trkType(); + } + + var sessions = new List>(log); + gpx.trk[0].trkseg = new trksegType[sessions.Count]; + + int sesid = 0; + + foreach(var session in sessions) + { + trksegType seg = new trksegType(); + { + seg.trkpt = new wptType[session.Count]; + } + gpx.trk[0].trkseg[sesid++] = seg; + + for(int i = 0; i < session.Count; i++) + { + var point = session[i]; + + wptType t = new wptType(); + { + #region -- set values -- + t.lat = new decimal(point.Position.Lat); + t.lon = new decimal(point.Position.Lng); + + t.time = point.TimeUTC; + t.timeSpecified = true; + + if(point.FixType != FixType.Unknown) + { + t.fix = (point.FixType == FixType.XyD ? fixType.Item2d : fixType.Item3d); + t.fixSpecified = true; + } + + if(point.SeaLevelAltitude.HasValue) + { + t.ele = new decimal(point.SeaLevelAltitude.Value); + t.eleSpecified = true; + } + + if(point.EllipsoidAltitude.HasValue) + { + t.geoidheight = new decimal(point.EllipsoidAltitude.Value); + t.geoidheightSpecified = true; + } + + if(point.VerticalDilutionOfPrecision.HasValue) + { + t.vdopSpecified = true; + t.vdop = new decimal(point.VerticalDilutionOfPrecision.Value); + } + + if(point.HorizontalDilutionOfPrecision.HasValue) + { + t.hdopSpecified = true; + t.hdop = new decimal(point.HorizontalDilutionOfPrecision.Value); + } + + if(point.PositionDilutionOfPrecision.HasValue) + { + t.pdopSpecified = true; + t.pdop = new decimal(point.PositionDilutionOfPrecision.Value); + } + + if(point.SatelliteCount.HasValue) + { + t.sat = point.SatelliteCount.Value.ToString(); + } + #endregion + } + seg.trkpt[i] = t; + } + } + sessions.Clear(); + +#if !PocketPC + File.WriteAllText(gpxFile, SerializeGPX(gpx), Encoding.UTF8); +#else + using(StreamWriter w = File.CreateText(gpxFile)) + { + w.Write(SerializeGPX(gpx)); + w.Close(); + } +#endif + } + catch(Exception ex) + { + Debug.WriteLine("ExportGPX: " + ex.ToString()); + return false; + } + return true; + } + + #endregion + + /// + /// gets image from tile server + /// + /// + /// + /// + /// + public PureImage GetImageFrom(GMapProvider provider, GPoint pos, int zoom, out Exception result) + { + PureImage ret = null; + result = null; + + try + { + var rtile = new RawTile(provider.DbId, pos, zoom); + + // let't check memmory first + if(UseMemoryCache) + { + var m = MemoryCache.GetTileFromMemoryCache(rtile); + if(m != null) + { + if(GMapProvider.TileImageProxy != null) + { + ret = GMapProvider.TileImageProxy.FromArray(m); + if(ret == null) + { +#if DEBUG + Debug.WriteLine("Image disposed in MemoryCache o.O, should never happen ;} " + new RawTile(provider.DbId, pos, zoom)); + if(Debugger.IsAttached) + { + Debugger.Break(); + } +#endif + m = null; + } + } + } + } + + if(ret == null) + { + if(Mode != AccessMode.ServerOnly) + { + if(PrimaryCache != null) + { + // hold writer for 5s + if(CacheOnIdleRead) + { + Interlocked.Exchange(ref readingCache, 5); + } + + ret = PrimaryCache.GetImageFromCache(provider.DbId, pos, zoom); + if(ret != null) + { + if(UseMemoryCache) + { + MemoryCache.AddTileToMemoryCache(rtile, ret.Data.GetBuffer()); + } + return ret; + } + } + + if(SecondaryCache != null) + { + // hold writer for 5s + if(CacheOnIdleRead) + { + Interlocked.Exchange(ref readingCache, 5); + } + + ret = SecondaryCache.GetImageFromCache(provider.DbId, pos, zoom); + if(ret != null) + { + if(UseMemoryCache) + { + MemoryCache.AddTileToMemoryCache(rtile, ret.Data.GetBuffer()); + } + EnqueueCacheTask(new CacheQueueItem(rtile, ret.Data.GetBuffer(), CacheUsage.First)); + return ret; + } + } + } + + if(Mode != AccessMode.CacheOnly) + { + ret = provider.GetTileImage(pos, zoom); + { + // Enqueue Cache + if(ret != null) + { + if(UseMemoryCache) + { + MemoryCache.AddTileToMemoryCache(rtile, ret.Data.GetBuffer()); + } + + if(Mode != AccessMode.ServerOnly) + { + EnqueueCacheTask(new CacheQueueItem(rtile, ret.Data.GetBuffer(), CacheUsage.Both)); + } + } + } + } + else + { + result = noDataException; + } + } + } + catch(Exception ex) + { + result = ex; + ret = null; + Debug.WriteLine("GetImageFrom: " + ex.ToString()); + } + + return ret; + } + + readonly Exception noDataException = new Exception("No data in local tile cache..."); + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET/GPoint.cs b/GreatMaps/GMap.NET.Core/GMap.NET/GPoint.cs new file mode 100644 index 0000000..ee23b0a --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET/GPoint.cs @@ -0,0 +1,146 @@ + +namespace GMap.NET +{ + using System.Globalization; + using System; + + /// + /// the point ;} + /// + [Serializable] + public struct GPoint + { + public static readonly GPoint Empty = new GPoint(); + + private long x; + private long y; + + public GPoint(long x, long y) + { + this.x = x; + this.y = y; + } + + public GPoint(GSize sz) + { + this.x = sz.Width; + this.y = sz.Height; + } + + //public GPoint(int dw) + //{ + // this.x = (short) LOWORD(dw); + // this.y = (short) HIWORD(dw); + //} + + public bool IsEmpty + { + get + { + return x == 0 && y == 0; + } + } + + public long X + { + get + { + return x; + } + set + { + x = value; + } + } + + public long Y + { + get + { + return y; + } + set + { + y = value; + } + } + + public static explicit operator GSize(GPoint p) + { + return new GSize(p.X, p.Y); + } + + public static GPoint operator+(GPoint pt, GSize sz) + { + return Add(pt, sz); + } + + public static GPoint operator-(GPoint pt, GSize sz) + { + return Subtract(pt, sz); + } + + public static bool operator==(GPoint left, GPoint right) + { + return left.X == right.X && left.Y == right.Y; + } + + public static bool operator!=(GPoint left, GPoint right) + { + return !(left == right); + } + + public static GPoint Add(GPoint pt, GSize sz) + { + return new GPoint(pt.X + sz.Width, pt.Y + sz.Height); + } + + public static GPoint Subtract(GPoint pt, GSize sz) + { + return new GPoint(pt.X - sz.Width, pt.Y - sz.Height); + } + + public override bool Equals(object obj) + { + if(!(obj is GPoint)) + return false; + GPoint comp = (GPoint) obj; + return comp.X == this.X && comp.Y == this.Y; + } + + public override int GetHashCode() + { + return (int)(x ^ y); + } + + public void Offset(long dx, long dy) + { + X += dx; + Y += dy; + } + + public void Offset(GPoint p) + { + Offset(p.X, p.Y); + } + public void OffsetNegative(GPoint p) + { + Offset(-p.X, -p.Y); + } + + public override string ToString() + { + return "{X=" + X.ToString(CultureInfo.CurrentCulture) + ",Y=" + Y.ToString(CultureInfo.CurrentCulture) + "}"; + } + + //private static int HIWORD(int n) + //{ + // return (n >> 16) & 0xffff; + //} + + //private static int LOWORD(int n) + //{ + // return n & 0xffff; + //} + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET/GRect.cs b/GreatMaps/GMap.NET.Core/GMap.NET/GRect.cs new file mode 100644 index 0000000..3baeac3 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET/GRect.cs @@ -0,0 +1,324 @@ + +namespace GMap.NET +{ + using System; + using System.Globalization; + + /// + /// the rect + /// + public struct GRect + { + public static readonly GRect Empty = new GRect(); + + private long x; + private long y; + private long width; + private long height; + + public GRect(long x, long y, long width, long height) + { + this.x = x; + this.y = y; + this.width = width; + this.height = height; + } + + public GRect(GPoint location, GSize size) + { + this.x = location.X; + this.y = location.Y; + this.width = size.Width; + this.height = size.Height; + } + + public static GRect FromLTRB(int left, int top, int right, int bottom) + { + return new GRect(left, + top, + right - left, + bottom - top); + } + + public GPoint Location + { + get + { + return new GPoint(X, Y); + } + set + { + X = value.X; + Y = value.Y; + } + } + + public GPoint RightBottom + { + get + { + return new GPoint(Right, Bottom); + } + } + + public GPoint RightTop + { + get + { + return new GPoint(Right, Top); + } + } + + public GPoint LeftBottom + { + get + { + return new GPoint(Left, Bottom); + } + } + + public GSize Size + { + get + { + return new GSize(Width, Height); + } + set + { + this.Width = value.Width; + this.Height = value.Height; + } + } + + public long X + { + get + { + return x; + } + set + { + x = value; + } + } + + public long Y + { + get + { + return y; + } + set + { + y = value; + } + } + + public long Width + { + get + { + return width; + } + set + { + width = value; + } + } + + public long Height + { + get + { + return height; + } + set + { + height = value; + } + } + + public long Left + { + get + { + return X; + } + } + + public long Top + { + get + { + return Y; + } + } + + public long Right + { + get + { + return X + Width; + } + } + + public long Bottom + { + get + { + return Y + Height; + } + } + + public bool IsEmpty + { + get + { + return height == 0 && width == 0 && x == 0 && y == 0; + } + } + + public override bool Equals(object obj) + { + if(!(obj is GRect)) + return false; + + GRect comp = (GRect) obj; + + return (comp.X == this.X) && + (comp.Y == this.Y) && + (comp.Width == this.Width) && + (comp.Height == this.Height); + } + + public static bool operator==(GRect left, GRect right) + { + return (left.X == right.X + && left.Y == right.Y + && left.Width == right.Width + && left.Height == right.Height); + } + + public static bool operator!=(GRect left, GRect right) + { + return !(left == right); + } + + public bool Contains(long x, long y) + { + return this.X <= x && + x < this.X + this.Width && + this.Y <= y && + y < this.Y + this.Height; + } + + public bool Contains(GPoint pt) + { + return Contains(pt.X, pt.Y); + } + + public bool Contains(GRect rect) + { + return (this.X <= rect.X) && + ((rect.X + rect.Width) <= (this.X + this.Width)) && + (this.Y <= rect.Y) && + ((rect.Y + rect.Height) <= (this.Y + this.Height)); + } + + public override int GetHashCode() + { + if(this.IsEmpty) + { + return 0; + } + return (int)(((this.X ^ ((this.Y << 13) | (this.Y >> 0x13))) ^ ((this.Width << 0x1a) | (this.Width >> 6))) ^ ((this.Height << 7) | (this.Height >> 0x19))); + } + + public void Inflate(long width, long height) + { + this.X -= width; + this.Y -= height; + this.Width += 2*width; + this.Height += 2*height; + } + + public void Inflate(GSize size) + { + Inflate(size.Width, size.Height); + } + + public static GRect Inflate(GRect rect, long x, long y) + { + GRect r = rect; + r.Inflate(x, y); + return r; + } + + public void Intersect(GRect rect) + { + GRect result = GRect.Intersect(rect, this); + + this.X = result.X; + this.Y = result.Y; + this.Width = result.Width; + this.Height = result.Height; + } + + public static GRect Intersect(GRect a, GRect b) + { + long x1 = Math.Max(a.X, b.X); + long x2 = Math.Min(a.X + a.Width, b.X + b.Width); + long y1 = Math.Max(a.Y, b.Y); + long y2 = Math.Min(a.Y + a.Height, b.Y + b.Height); + + if(x2 >= x1 + && y2 >= y1) + { + + return new GRect(x1, y1, x2 - x1, y2 - y1); + } + return GRect.Empty; + } + + public bool IntersectsWith(GRect rect) + { + return (rect.X < this.X + this.Width) && + (this.X < (rect.X + rect.Width)) && + (rect.Y < this.Y + this.Height) && + (this.Y < rect.Y + rect.Height); + } + + public static GRect Union(GRect a, GRect b) + { + long x1 = Math.Min(a.X, b.X); + long x2 = Math.Max(a.X + a.Width, b.X + b.Width); + long y1 = Math.Min(a.Y, b.Y); + long y2 = Math.Max(a.Y + a.Height, b.Y + b.Height); + + return new GRect(x1, y1, x2 - x1, y2 - y1); + } + + public void Offset(GPoint pos) + { + Offset(pos.X, pos.Y); + } + + public void OffsetNegative(GPoint pos) + { + Offset(-pos.X, -pos.Y); + } + + public void Offset(long x, long y) + { + this.X += x; + this.Y += y; + } + + public override string ToString() + { + return "{X=" + X.ToString(CultureInfo.CurrentCulture) + ",Y=" + Y.ToString(CultureInfo.CurrentCulture) + + ",Width=" + Width.ToString(CultureInfo.CurrentCulture) + + ",Height=" + Height.ToString(CultureInfo.CurrentCulture) + "}"; + } + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET/GSize.cs b/GreatMaps/GMap.NET.Core/GMap.NET/GSize.cs new file mode 100644 index 0000000..25e5ff8 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET/GSize.cs @@ -0,0 +1,121 @@ + +namespace GMap.NET +{ + using System.Globalization; + + /// + /// the size + /// + public struct GSize + { + public static readonly GSize Empty = new GSize(); + + private long width; + private long height; + + public GSize(GPoint pt) + { + width = pt.X; + height = pt.Y; + } + + public GSize(long width, long height) + { + this.width = width; + this.height = height; + } + + public static GSize operator +(GSize sz1, GSize sz2) + { + return Add(sz1, sz2); + } + + public static GSize operator -(GSize sz1, GSize sz2) + { + return Subtract(sz1, sz2); + } + + public static bool operator ==(GSize sz1, GSize sz2) + { + return sz1.Width == sz2.Width && sz1.Height == sz2.Height; + } + + public static bool operator !=(GSize sz1, GSize sz2) + { + return !(sz1 == sz2); + } + + public static explicit operator GPoint(GSize size) + { + return new GPoint(size.Width, size.Height); + } + + public bool IsEmpty + { + get + { + return width == 0 && height == 0; + } + } + + public long Width + { + get + { + return width; + } + set + { + width = value; + } + } + + public long Height + { + get + { + return height; + } + set + { + height = value; + } + } + + public static GSize Add(GSize sz1, GSize sz2) + { + return new GSize(sz1.Width + sz2.Width, sz1.Height + sz2.Height); + } + + public static GSize Subtract(GSize sz1, GSize sz2) + { + return new GSize(sz1.Width - sz2.Width, sz1.Height - sz2.Height); + } + + public override bool Equals(object obj) + { + if(!(obj is GSize)) + return false; + + GSize comp = (GSize)obj; + // Note value types can't have derived classes, so we don't need to + // + return (comp.width == this.width) && + (comp.height == this.height); + } + + public override int GetHashCode() + { + if(this.IsEmpty) + { + return 0; + } + return (Width.GetHashCode() ^ Height.GetHashCode()); + } + + public override string ToString() + { + return "{Width=" + width.ToString(CultureInfo.CurrentCulture) + ", Height=" + height.ToString(CultureInfo.CurrentCulture) + "}"; + } + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET/GeocodingProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET/GeocodingProvider.cs new file mode 100644 index 0000000..37c985b --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET/GeocodingProvider.cs @@ -0,0 +1,25 @@ + +namespace GMap.NET +{ + using System.Collections.Generic; + + /// + /// geocoding interface + /// + public interface GeocodingProvider + { + GeoCoderStatusCode GetPoints(string keywords, out List pointList); + + PointLatLng? GetPoint(string keywords, out GeoCoderStatusCode status); + + GeoCoderStatusCode GetPoints(Placemark placemark, out List pointList); + + PointLatLng? GetPoint(Placemark placemark, out GeoCoderStatusCode status); + + // ... + + GeoCoderStatusCode GetPlacemarks(PointLatLng location, out List placemarkList); + + Placemark ? GetPlacemark(PointLatLng location, out GeoCoderStatusCode status); + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET/GpsLog.cs b/GreatMaps/GMap.NET.Core/GMap.NET/GpsLog.cs new file mode 100644 index 0000000..4509695 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET/GpsLog.cs @@ -0,0 +1,44 @@ + +namespace GMap.NET +{ + using System; + + public struct GpsLog + { + public DateTime TimeUTC; + public long SessionCounter; + public double? Delta; + public double? Speed; + public double? SeaLevelAltitude; + public double? EllipsoidAltitude; + public short? SatellitesInView; + public short? SatelliteCount; + public PointLatLng Position; + public double? PositionDilutionOfPrecision; + public double? HorizontalDilutionOfPrecision; + public double? VerticalDilutionOfPrecision; + public FixQuality FixQuality; + public FixType FixType; + public FixSelection FixSelection; + } + + public enum FixQuality : int + { + Unknown=0, + Gps, + DGps + } + public enum FixType : int + { + Unknown=0, + XyD, + XyzD + } + + public enum FixSelection : int + { + Unknown=0, + Auto, + Manual + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET/Interface.cs b/GreatMaps/GMap.NET.Core/GMap.NET/Interface.cs new file mode 100644 index 0000000..9306dcb --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET/Interface.cs @@ -0,0 +1,72 @@ + +namespace GMap.NET +{ + using GMap.NET.MapProviders; + + public interface Interface + { + PointLatLng Position + { + get; + set; + } + + GPoint PositionPixel + { + get; + } + + string CacheLocation + { + get; + set; + } + + bool IsDragging + { + get; + } + + RectLatLng ViewArea + { + get; + } + + GMapProvider MapProvider + { + get; + set; + } + + bool CanDragMap + { + get; + set; + } + + RenderMode RenderMode + { + get; + } + + // events + event PositionChanged OnPositionChanged; + event TileLoadComplete OnTileLoadComplete; + event TileLoadStart OnTileLoadStart; + event MapDrag OnMapDrag; + event MapZoomChanged OnMapZoomChanged; + event MapTypeChanged OnMapTypeChanged; + + void ReloadMap(); + + PointLatLng FromLocalToLatLng(int x, int y); + GPoint FromLatLngToLocal(PointLatLng point); + +#if !PocketPC +#if SQLite + bool ShowExportDialog(); + bool ShowImportDialog(); +#endif +#endif + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET/LanguageType.cs b/GreatMaps/GMap.NET.Core/GMap.NET/LanguageType.cs new file mode 100644 index 0000000..af97ed4 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET/LanguageType.cs @@ -0,0 +1,179 @@ + +namespace GMap.NET +{ + using System.ComponentModel; + + /// + /// This enum contains all possible languages for the Google maps. + /// You can find latest information about supported languages in the: + /// http://tinyurl.com/yh4va36 <- http://spreadsheets.server.com/pub?key=p9pdwsai2hDMsLkXsoM05KQ&gid=1 + /// + public enum LanguageType + { + [Description("ar")] + Arabic, + + [Description("bg")] + Bulgarian, + + [Description("bn")] + Bengali, + + [Description("ca")] + Catalan, + + [Description("cs")] + Czech, + + [Description("da")] + Danish, + + [Description("de")] + German, + + [Description("el")] + Greek, + + [Description("en")] + English, + + [Description("en-AU")] + EnglishAustralian, + + [Description("en-GB")] + EnglishGreatBritain, + + [Description("es")] + Spanish, + + [Description("eu")] + Basque, + + [Description("fa")] + FARSI, + + [Description("fi")] + Finnish, + + [Description("fil")] + Filipino, + + [Description("fr")] + French, + + [Description("gl")] + Galician, + + [Description("gu")] + Gujarati, + [Description("hi")] + Hindi, + + [Description("hr")] + Croatian, + + [Description("hu")] + Hungarian, + + [Description("id")] + Indonesian, + + [Description("it")] + Italian, + + [Description("iw")] + Hebrew, + + [Description("ja")] + Japanese, + + [Description("kn")] + Kannada, + + [Description("ko")] + Korean, + + [Description("lt")] + Lithuanian, + + [Description("lv")] + Latvian, + + [Description("ml")] + Malayalam, + + [Description("mr")] + Marathi, + + [Description("nl")] + Dutch, + + [Description("nn")] + NorwegianNynorsk, + + [Description("no")] + Norwegian, + + [Description("or")] + Oriya, + + [Description("pl")] + Polish, + + [Description("pt")] + Portuguese, + + [Description("pt-BR")] + PortugueseBrazil, + + [Description("pt-PT")] + PortuguesePortugal, + + [Description("rm")] + Romansch, + [Description("ro")] + Romanian, + + [Description("ru")] + Russian, + + [Description("sk")] + Slovak, + + [Description("sl")] + Slovenian, + + [Description("sr")] + Serbian, + + [Description("sv")] + Swedish, + + [Description("tl")] + TAGALOG, + + [Description("ta")] + Tamil, + + [Description("te")] + Telugu, + + [Description("th")] + Thai, + + [Description("tr")] + Turkish, + + [Description("uk")] + Ukrainian, + + [Description("vi")] + Vietnamese, + + [Description("zh-CN")] + ChineseSimplified, + + [Description("zh-TW")] + ChineseTraditional, + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET/MapRoute.cs b/GreatMaps/GMap.NET.Core/GMap.NET/MapRoute.cs new file mode 100644 index 0000000..d9e2e21 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET/MapRoute.cs @@ -0,0 +1,160 @@ + +namespace GMap.NET +{ + using System; + using System.Collections.Generic; + using System.Runtime.Serialization; + using GMap.NET.MapProviders; + + /// + /// represents route of map + /// + [Serializable] +#if !PocketPC + public class MapRoute : ISerializable, IDeserializationCallback +#else + public class MapRoute +#endif + { + /// + /// points of route + /// + public readonly List Points = new List(); + + /// + /// route info + /// + public string Name; + + /// + /// custom object + /// + public object Tag; + + /// + /// route start point + /// + public PointLatLng? From + { + get + { + if(Points.Count > 0) + { + return Points[0]; + } + + return null; + } + } + + /// + /// route end point + /// + public PointLatLng? To + { + get + { + if(Points.Count > 1) + { + return Points[Points.Count - 1]; + } + + return null; + } + } + + public MapRoute(string name) + { + Name = name; + } + + public MapRoute(IEnumerable points, string name) + { + Points.AddRange(points); + Name = name; + } + + /// + /// route distance (in km) + /// + public double Distance + { + get + { + double distance = 0.0; + + if(From.HasValue && To.HasValue) + { + for(int i = 1; i < Points.Count; i++) + { + distance += GMapProviders.EmptyProvider.Projection.GetDistance(Points[i - 1], Points[i]); + } + } + + return distance; + } + } + + /// + /// clears points and sets tag and name to null + /// + public void Clear() + { + Points.Clear(); + Tag = null; + Name = null; + } + +#if !PocketPC + #region ISerializable Members + + // Temp store for de-serialization. + private PointLatLng[] deserializedPoints; + + /// + /// Populates a with the data needed to serialize the target object. + /// + /// The to populate with data. + /// The destination (see ) for this serialization. + /// + /// The caller does not have the required permission. + /// + public virtual void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue("Name", this.Name); + info.AddValue("Tag", this.Tag); + info.AddValue("Points", this.Points.ToArray()); + } + + /// + /// Initializes a new instance of the class. + /// + /// The info. + /// The context. + protected MapRoute(SerializationInfo info, StreamingContext context) + { + this.Name = info.GetString("Name"); + this.Tag = Extensions.GetValue(info, "Tag", null); + this.deserializedPoints = Extensions.GetValue(info, "Points"); + this.Points = new List(); + } + + #endregion + + #region IDeserializationCallback Members + + /// + /// Runs when the entire object graph has been de-serialized. + /// + /// The object that initiated the callback. The functionality for this parameter is not currently implemented. + public virtual void OnDeserialization(object sender) + { + // Accounts for the de-serialization being breadth first rather than depth first. + Points.AddRange(deserializedPoints); + Points.TrimExcess(); + } + + #endregion +#endif + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET/MapType.cs b/GreatMaps/GMap.NET.Core/GMap.NET/MapType.cs new file mode 100644 index 0000000..cfb6967 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET/MapType.cs @@ -0,0 +1,127 @@ + +namespace GMap.NET +{ + using System; + + /// + /// types of great maps, legacy, not used anymore, + /// left for old ids + /// + public enum MapType + { + None = 0, // displays no map + + [Obsolete("check http://greatmaps.codeplex.com/discussions/252531", false)] + GoogleMap = 1, + [Obsolete("check http://greatmaps.codeplex.com/discussions/252531", false)] + GoogleSatellite = 4, + [Obsolete("check http://greatmaps.codeplex.com/discussions/252531", false)] + GoogleLabels = 8, + [Obsolete("check http://greatmaps.codeplex.com/discussions/252531", false)] + GoogleTerrain = 16, + [Obsolete("check http://greatmaps.codeplex.com/discussions/252531", false)] + GoogleHybrid = 20, + + [Obsolete("check http://greatmaps.codeplex.com/discussions/252531", false)] + GoogleMapChina = 22, + [Obsolete("check http://greatmaps.codeplex.com/discussions/252531", false)] + GoogleSatelliteChina = 24, + [Obsolete("check http://greatmaps.codeplex.com/discussions/252531", false)] + GoogleLabelsChina = 26, + [Obsolete("check http://greatmaps.codeplex.com/discussions/252531", false)] + GoogleTerrainChina = 28, + [Obsolete("check http://greatmaps.codeplex.com/discussions/252531", false)] + GoogleHybridChina = 29, + + OpenStreetMap = 32, // + OpenStreetOsm = 33, // + OpenStreetMapSurfer = 34, // + OpenStreetMapSurferTerrain = 35, // + OpenSeaMapLabels = 36, // + OpenSeaMapHybrid = 37, // + OpenCycleMap = 38, // + + YahooMap = 64, + YahooSatellite = 128, + YahooLabels = 256, + YahooHybrid = 333, + + BingMap = 444, + BingMap_New = 455, + BingSatellite = 555, + BingHybrid = 666, + + ArcGIS_StreetMap_World_2D = 777, + ArcGIS_Imagery_World_2D = 788, + ArcGIS_ShadedRelief_World_2D = 799, + ArcGIS_Topo_US_2D = 811, + + #region -- use these numbers to clean up old stuff -- + //ArcGIS_MapsLT_Map_Old= 877, + //ArcGIS_MapsLT_OrtoFoto_Old = 888, + //ArcGIS_MapsLT_Map_Labels_Old = 890, + //ArcGIS_MapsLT_Map_Hybrid_Old = 899, + //ArcGIS_MapsLT_Map=977, + //ArcGIS_MapsLT_OrtoFoto=988, + //ArcGIS_MapsLT_Map_Labels=990, + //ArcGIS_MapsLT_Map_Hybrid=999, + //ArcGIS_MapsLT_Map=978, + //ArcGIS_MapsLT_OrtoFoto=989, + //ArcGIS_MapsLT_Map_Labels=991, + //ArcGIS_MapsLT_Map_Hybrid=998, + #endregion + + ArcGIS_World_Physical_Map = 822, + ArcGIS_World_Shaded_Relief = 833, + ArcGIS_World_Street_Map = 844, + ArcGIS_World_Terrain_Base = 855, + ArcGIS_World_Topo_Map = 866, + + MapsLT_Map = 1000, + MapsLT_OrtoFoto = 1001, + MapsLT_Map_Labels = 1002, + MapsLT_Map_Hybrid = 1003, + MapsLT_Map_2_5D = 1004, // 2.5D only for zoom 10 & 11 + MapsLT_OrtoFoto_2010 = 1101, // new but only partial coverage + MapsLT_Map_Hybrid_2010 = 1103, // --..-- + + KarteLV_Map = 1500, + + PergoTurkeyMap = 2001, + SigPacSpainMap = 3001, + + [Obsolete("check http://greatmaps.codeplex.com/discussions/252531", false)] + GoogleMapKorea = 4001, + [Obsolete("check http://greatmaps.codeplex.com/discussions/252531", false)] + GoogleSatelliteKorea = 4002, + [Obsolete("check http://greatmaps.codeplex.com/discussions/252531", false)] + GoogleLabelsKorea = 4003, + [Obsolete("check http://greatmaps.codeplex.com/discussions/252531", false)] + GoogleHybridKorea = 4005, + + YandexMapRu = 5000, + YandexMapRuSatellite = 5001, + YandexMapRuLabels = 5002, + YandexMapRuHybrid = 5003, + + MapBenderWMS = 6000, + + MapyCZ_Map = 7000, + MapyCZ_MapTurist = 7001, + MapyCZ_Satellite = 7002, + MapyCZ_Labels = 7003, + MapyCZ_Hybrid = 7004, + MapyCZ_History = 7005, + MapyCZ_HistoryHybrid = 7006, + + NearMap = 8000, + NearMapSatellite = 8001, + NearMapLabels = 8002, + NearMapHybrid = 8003, + + OviMap = 9000, + OviMapSatellite = 9001, + OviMapHybrid = 9002, + OviMapTerrain = 9003, + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET/MouseWheelZoomType.cs b/GreatMaps/GMap.NET.Core/GMap.NET/MouseWheelZoomType.cs new file mode 100644 index 0000000..4d25a5f --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET/MouseWheelZoomType.cs @@ -0,0 +1,25 @@ + +namespace GMap.NET +{ + /// + /// map zooming type + /// + public enum MouseWheelZoomType + { + /// + /// zooms map to current mouse position and makes it map center + /// + MousePositionAndCenter, + + /// + /// zooms to current mouse position, but doesn't make it map center, + /// google/bing style ;} + /// + MousePositionWithoutCenter, + + /// + /// zooms map to current view center + /// + ViewCenter, + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET/Placemark.cs b/GreatMaps/GMap.NET.Core/GMap.NET/Placemark.cs new file mode 100644 index 0000000..cdb1502 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET/Placemark.cs @@ -0,0 +1,58 @@ + +namespace GMap.NET +{ + /// + /// represents place info + /// + public struct Placemark + { + string address; + + /// + /// the address + /// + public string Address + { + get + { + return address; + } + internal set + { + address = value; + } + } + + /// + /// the accuracy of address + /// + public int Accuracy; + + // parsed values from address + public string HouseNo; + public string ThoroughfareName; + public string DistrictName; + public string LocalityName; + public string PostalCodeNumber; + public string CountryName; + public string CountryNameCode; + public string AdministrativeAreaName; + public string SubAdministrativeAreaName; + + internal Placemark(string address) + { + this.address = address; + + Accuracy = 0; + HouseNo = string.Empty; + ThoroughfareName = string.Empty; + DistrictName = string.Empty; + LocalityName = string.Empty; + PostalCodeNumber = string.Empty; + CountryName = string.Empty; + CountryNameCode = string.Empty; + AdministrativeAreaName = string.Empty; + SubAdministrativeAreaName = string.Empty; + } + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET/PointLatLng.cs b/GreatMaps/GMap.NET.Core/GMap.NET/PointLatLng.cs new file mode 100644 index 0000000..51a807a --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET/PointLatLng.cs @@ -0,0 +1,132 @@ + +namespace GMap.NET +{ + using System; + using System.Globalization; + + /// + /// the point of coordinates + /// + [Serializable] + public struct PointLatLng + { + public static readonly PointLatLng Empty = new PointLatLng(); + private double lat; + private double lng; + + bool NotEmpty; + + // 2016/01/20 by DL2ALF: constructor with decimals added + public PointLatLng(decimal lat, decimal lng) + { + this.lat = (double)lat; + this.lng = (double)lng; + NotEmpty = true; + } + + public PointLatLng(double lat, double lng) + { + this.lat = lat; + this.lng = lng; + NotEmpty = true; + } + + /// + /// returns true if coordinates wasn't assigned + /// + public bool IsEmpty + { + get + { + return !NotEmpty; + } + } + + public double Lat + { + get + { + return this.lat; + } + set + { + this.lat = value; + NotEmpty = true; + } + } + + public double Lng + { + get + { + return this.lng; + } + set + { + this.lng = value; + NotEmpty = true; + } + } + + public static PointLatLng operator +(PointLatLng pt, SizeLatLng sz) + { + return Add(pt, sz); + } + + public static PointLatLng operator -(PointLatLng pt, SizeLatLng sz) + { + return Subtract(pt, sz); + } + + public static bool operator ==(PointLatLng left, PointLatLng right) + { + return ((left.Lng == right.Lng) && (left.Lat == right.Lat)); + } + + public static bool operator !=(PointLatLng left, PointLatLng right) + { + return !(left == right); + } + + public static PointLatLng Add(PointLatLng pt, SizeLatLng sz) + { + return new PointLatLng(pt.Lat - sz.HeightLat, pt.Lng + sz.WidthLng); + } + + public static PointLatLng Subtract(PointLatLng pt, SizeLatLng sz) + { + return new PointLatLng(pt.Lat + sz.HeightLat, pt.Lng - sz.WidthLng); + } + + public override bool Equals(object obj) + { + if(!(obj is PointLatLng)) + { + return false; + } + PointLatLng tf = (PointLatLng)obj; + return (((tf.Lng == this.Lng) && (tf.Lat == this.Lat)) && tf.GetType().Equals(base.GetType())); + } + + public void Offset(PointLatLng pos) + { + this.Offset(pos.Lat, pos.Lng); + } + + public void Offset(double lat, double lng) + { + this.Lng += lng; + this.Lat -= lat; + } + + public override int GetHashCode() + { + return (this.Lng.GetHashCode() ^ this.Lat.GetHashCode()); + } + + public override string ToString() + { + return string.Format(CultureInfo.CurrentCulture, "{{Lat={0}, Lng={1}}}", this.Lat, this.Lng); + } + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET/PureImageCache.cs b/GreatMaps/GMap.NET.Core/GMap.NET/PureImageCache.cs new file mode 100644 index 0000000..540b5b3 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET/PureImageCache.cs @@ -0,0 +1,39 @@ + +namespace GMap.NET +{ + using System.IO; + using System; + + /// + /// pure abstraction for image cache + /// + public interface PureImageCache + { + /// + /// puts image to db + /// + /// + /// + /// + /// + /// + bool PutImageToCache(byte[] tile, int type, GPoint pos, int zoom); + + /// + /// gets image from db + /// + /// + /// + /// + /// + PureImage GetImageFromCache(int type, GPoint pos, int zoom); + + /// + /// delete old tiles beyond a supplied date + /// + /// Tiles older than this will be deleted. + /// provider dbid or null to use all providers + /// The number of deleted tiles. + int DeleteOlderThan(DateTime date, int ? type); + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET/PureProjection.cs b/GreatMaps/GMap.NET.Core/GMap.NET/PureProjection.cs new file mode 100644 index 0000000..5a4ae93 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET/PureProjection.cs @@ -0,0 +1,513 @@ + +namespace GMap.NET +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + + /// + /// defines projection + /// + public abstract class PureProjection + { + readonly List> FromLatLngToPixelCache = new List>(33); + readonly List> FromPixelToLatLngCache = new List>(33); + + public PureProjection() + { + for(int i = 0; i < FromLatLngToPixelCache.Capacity; i++) + { + FromLatLngToPixelCache.Add(new Dictionary()); + FromPixelToLatLngCache.Add(new Dictionary()); + } + } + + /// + /// size of tile + /// + public abstract GSize TileSize + { + get; + } + + /// + /// Semi-major axis of ellipsoid, in meters + /// + public abstract double Axis + { + get; + } + + /// + /// Flattening of ellipsoid + /// + public abstract double Flattening + { + get; + } + + /// + /// get pixel coordinates from lat/lng + /// + /// + /// + /// + /// + public abstract GPoint FromLatLngToPixel(double lat, double lng, int zoom); + + /// + /// gets lat/lng coordinates from pixel coordinates + /// + /// + /// + /// + /// + public abstract PointLatLng FromPixelToLatLng(long x, long y, int zoom); + + public GPoint FromLatLngToPixel(PointLatLng p, int zoom) + { + return FromLatLngToPixel(p, zoom, false); + } + + /// + /// get pixel coordinates from lat/lng + /// + /// + /// + /// + public GPoint FromLatLngToPixel(PointLatLng p, int zoom, bool useCache) + { + if(useCache) + { + GPoint ret = GPoint.Empty; + if(!FromLatLngToPixelCache[zoom].TryGetValue(p, out ret)) + { + ret = FromLatLngToPixel(p.Lat, p.Lng, zoom); + FromLatLngToPixelCache[zoom].Add(p, ret); + + // for reverse cache + if(!FromPixelToLatLngCache[zoom].ContainsKey(ret)) + { + FromPixelToLatLngCache[zoom].Add(ret, p); + } + + Debug.WriteLine("FromLatLngToPixelCache[" + zoom + "] added " + p + " with " + ret); + } + return ret; + } + else + { + return FromLatLngToPixel(p.Lat, p.Lng, zoom); + } + } + + public PointLatLng FromPixelToLatLng(GPoint p, int zoom) + { + return FromPixelToLatLng(p, zoom, false); + } + + /// + /// gets lat/lng coordinates from pixel coordinates + /// + /// + /// + /// + public PointLatLng FromPixelToLatLng(GPoint p, int zoom, bool useCache) + { + if(useCache) + { + PointLatLng ret = PointLatLng.Empty; + if(!FromPixelToLatLngCache[zoom].TryGetValue(p, out ret)) + { + ret = FromPixelToLatLng(p.X, p.Y, zoom); + FromPixelToLatLngCache[zoom].Add(p, ret); + + // for reverse cache + if(!FromLatLngToPixelCache[zoom].ContainsKey(ret)) + { + FromLatLngToPixelCache[zoom].Add(ret, p); + } + + Debug.WriteLine("FromPixelToLatLngCache[" + zoom + "] added " + p + " with " + ret); + } + return ret; + } + else + { + return FromPixelToLatLng(p.X, p.Y, zoom); + } + } + + /// + /// gets tile coorddinate from pixel coordinates + /// + /// + /// + public virtual GPoint FromPixelToTileXY(GPoint p) + { + return new GPoint((long)(p.X / TileSize.Width), (long)(p.Y / TileSize.Height)); + } + + /// + /// gets pixel coordinate from tile coordinate + /// + /// + /// + public virtual GPoint FromTileXYToPixel(GPoint p) + { + return new GPoint((p.X * TileSize.Width), (p.Y * TileSize.Height)); + } + + /// + /// min. tile in tiles at custom zoom level + /// + /// + /// + public abstract GSize GetTileMatrixMinXY(int zoom); + + /// + /// max. tile in tiles at custom zoom level + /// + /// + /// + public abstract GSize GetTileMatrixMaxXY(int zoom); + + /// + /// gets matrix size in tiles + /// + /// + /// + public virtual GSize GetTileMatrixSizeXY(int zoom) + { + GSize sMin = GetTileMatrixMinXY(zoom); + GSize sMax = GetTileMatrixMaxXY(zoom); + + return new GSize(sMax.Width - sMin.Width + 1, sMax.Height - sMin.Height + 1); + } + + /// + /// tile matrix size in pixels at custom zoom level + /// + /// + /// + public long GetTileMatrixItemCount(int zoom) + { + GSize s = GetTileMatrixSizeXY(zoom); + return (s.Width * s.Height); + } + + /// + /// gets matrix size in pixels + /// + /// + /// + public virtual GSize GetTileMatrixSizePixel(int zoom) + { + GSize s = GetTileMatrixSizeXY(zoom); + return new GSize(s.Width * TileSize.Width, s.Height * TileSize.Height); + } + + /// + /// gets all tiles in rect at specific zoom + /// + public List GetAreaTileList(RectLatLng rect, int zoom, int padding) + { + List ret = new List(); + + GPoint topLeft = FromPixelToTileXY(FromLatLngToPixel(rect.LocationTopLeft, zoom)); + GPoint rightBottom = FromPixelToTileXY(FromLatLngToPixel(rect.LocationRightBottom, zoom)); + + for(long x = (topLeft.X - padding); x <= (rightBottom.X + padding); x++) + { + for(long y = (topLeft.Y - padding); y <= (rightBottom.Y + padding); y++) + { + GPoint p = new GPoint(x, y); + if(!ret.Contains(p) && p.X >= 0 && p.Y >= 0) + { + ret.Add(p); + } + } + } + ret.TrimExcess(); + + return ret; + } + + /// + /// The ground resolution indicates the distance (in meters) on the ground that’s represented by a single pixel in the map. + /// For example, at a ground resolution of 10 meters/pixel, each pixel represents a ground distance of 10 meters. + /// + /// + /// + /// + public virtual double GetGroundResolution(int zoom, double latitude) + { + return (Math.Cos(latitude * (Math.PI / 180)) * 2 * Math.PI * Axis) / GetTileMatrixSizePixel(zoom).Width; + } + + /// + /// gets boundaries + /// + public virtual RectLatLng Bounds + { + get + { + return RectLatLng.FromLTRB(-180, 90, 180, -90); + } + } + + #region -- math functions -- + + /// + /// PI + /// + protected static readonly double PI = Math.PI; + + /// + /// Half of PI + /// + protected static readonly double HALF_PI = (PI * 0.5); + + /// + /// PI * 2 + /// + protected static readonly double TWO_PI = (PI * 2.0); + + /// + /// EPSLoN + /// + protected static readonly double EPSLoN = 1.0e-10; + + /// + /// MAX_VAL + /// + protected const double MAX_VAL = 4; + + /// + /// MAXLONG + /// + protected static readonly double MAXLONG = 2147483647; + + /// + /// DBLLONG + /// + protected static readonly double DBLLONG = 4.61168601e18; + + static readonly double R2D = 180 / Math.PI; + static readonly double D2R = Math.PI / 180; + + public static double DegreesToRadians(double deg) + { + return (D2R * deg); + } + + public static double RadiansToDegrees(double rad) + { + return (R2D * rad); + } + + /// + /// return the sign of an argument + /// + protected static double Sign(double x) + { + if(x < 0.0) + return (-1); + else + return (1); + } + + protected static double AdjustLongitude(double x) + { + long count = 0; + while(true) + { + if(Math.Abs(x) <= PI) + break; + else + if(((long)Math.Abs(x / Math.PI)) < 2) + x = x - (Sign(x) * TWO_PI); + else + if(((long)Math.Abs(x / TWO_PI)) < MAXLONG) + { + x = x - (((long)(x / TWO_PI)) * TWO_PI); + } + else + if(((long)Math.Abs(x / (MAXLONG * TWO_PI))) < MAXLONG) + { + x = x - (((long)(x / (MAXLONG * TWO_PI))) * (TWO_PI * MAXLONG)); + } + else + if(((long)Math.Abs(x / (DBLLONG * TWO_PI))) < MAXLONG) + { + x = x - (((long)(x / (DBLLONG * TWO_PI))) * (TWO_PI * DBLLONG)); + } + else + x = x - (Sign(x) * TWO_PI); + count++; + if(count > MAX_VAL) + break; + } + return (x); + } + + /// + /// calculates the sine and cosine + /// + protected static void SinCos(double val, out double sin, out double cos) + { + sin = Math.Sin(val); + cos = Math.Cos(val); + } + + /// + /// computes the constants e0, e1, e2, and e3 which are used + /// in a series for calculating the distance along a meridian. + /// + /// represents the eccentricity squared + /// + protected static double e0fn(double x) + { + return (1.0 - 0.25 * x * (1.0 + x / 16.0 * (3.0 + 1.25 * x))); + } + + protected static double e1fn(double x) + { + return (0.375 * x * (1.0 + 0.25 * x * (1.0 + 0.46875 * x))); + } + + protected static double e2fn(double x) + { + return (0.05859375 * x * x * (1.0 + 0.75 * x)); + } + + protected static double e3fn(double x) + { + return (x * x * x * (35.0 / 3072.0)); + } + + /// + /// computes the value of M which is the distance along a meridian + /// from the Equator to latitude phi. + /// + protected static double mlfn(double e0, double e1, double e2, double e3, double phi) + { + return (e0 * phi - e1 * Math.Sin(2.0 * phi) + e2 * Math.Sin(4.0 * phi) - e3 * Math.Sin(6.0 * phi)); + } + + /// + /// calculates UTM zone number + /// + /// Longitude in degrees + /// + protected static long GetUTMzone(double lon) + { + return ((long)(((lon + 180.0) / 6.0) + 1.0)); + } + + /// + /// Clips a number to the specified minimum and maximum values. + /// + /// The number to clip. + /// Minimum allowable value. + /// Maximum allowable value. + /// The clipped value. + protected static double Clip(double n, double minValue, double maxValue) + { + return Math.Min(Math.Max(n, minValue), maxValue); + } + + /// + /// distance (in km) between two points specified by latitude/longitude + /// The Haversine formula, http://www.movable-type.co.uk/scripts/latlong.html + /// + /// + /// + /// + public double GetDistance(PointLatLng p1, PointLatLng p2) + { + double dLat1InRad = p1.Lat * (Math.PI / 180); + double dLong1InRad = p1.Lng * (Math.PI / 180); + double dLat2InRad = p2.Lat * (Math.PI / 180); + double dLong2InRad = p2.Lng * (Math.PI / 180); + double dLongitude = dLong2InRad - dLong1InRad; + double dLatitude = dLat2InRad - dLat1InRad; + double a = Math.Pow(Math.Sin(dLatitude / 2), 2) + Math.Cos(dLat1InRad) * Math.Cos(dLat2InRad) * Math.Pow(Math.Sin(dLongitude / 2), 2); + double c = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a)); + double dDistance = (Axis / 1000.0) * c; + return dDistance; + } + + public double GetDistanceInPixels(GPoint point1, GPoint point2) + { + double a = (double)(point2.X - point1.X); + double b = (double)(point2.Y - point1.Y); + + return Math.Sqrt(a * a + b * b); + } + + /// + /// Accepts two coordinates in degrees. + /// + /// A double value in degrees. From 0 to 360. + public double GetBearing(PointLatLng p1, PointLatLng p2) + { + var latitude1 = DegreesToRadians(p1.Lat); + var latitude2 = DegreesToRadians(p2.Lat); + var longitudeDifference = DegreesToRadians(p2.Lng - p1.Lng); + + var y = Math.Sin(longitudeDifference) * Math.Cos(latitude2); + var x = Math.Cos(latitude1) * Math.Sin(latitude2) - Math.Sin(latitude1) * Math.Cos(latitude2) * Math.Cos(longitudeDifference); + + return (RadiansToDegrees(Math.Atan2(y, x)) + 360) % 360; + } + + /// + /// Conversion from cartesian earth-sentered coordinates to geodetic coordinates in the given datum + /// + /// + /// + /// Height above ellipsoid [m] + /// + /// + /// + public void FromGeodeticToCartesian(double Lat, double Lng, double Height, out double X, out double Y, out double Z) + { + Lat = (Math.PI / 180) * Lat; + Lng = (Math.PI / 180) * Lng; + + double B = Axis * (1.0 - Flattening); + double ee = 1.0 - (B / Axis) * (B / Axis); + double N = (Axis / Math.Sqrt(1.0 - ee * Math.Sin(Lat) * Math.Sin(Lat))); + + X = (N + Height) * Math.Cos(Lat) * Math.Cos(Lng); + Y = (N + Height) * Math.Cos(Lat) * Math.Sin(Lng); + Z = (N * (B / Axis) * (B / Axis) + Height) * Math.Sin(Lat); + } + + /// + /// Conversion from cartesian earth-sentered coordinates to geodetic coordinates in the given datum + /// + /// + /// + /// + /// + /// + public void FromCartesianTGeodetic(double X, double Y, double Z, out double Lat, out double Lng) + { + double E = Flattening * (2.0 - Flattening); + Lng = Math.Atan2(Y, X); + + double P = Math.Sqrt(X * X + Y * Y); + double Theta = Math.Atan2(Z, (P * (1.0 - Flattening))); + double st = Math.Sin(Theta); + double ct = Math.Cos(Theta); + Lat = Math.Atan2(Z + E / (1.0 - Flattening) * Axis * st * st * st, P - E * Axis * ct * ct * ct); + + Lat /= (Math.PI / 180); + Lng /= (Math.PI / 180); + } + + #endregion + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET/RectLatLng.cs b/GreatMaps/GMap.NET.Core/GMap.NET/RectLatLng.cs new file mode 100644 index 0000000..de71dcd --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET/RectLatLng.cs @@ -0,0 +1,325 @@ + +namespace GMap.NET +{ + using System; + using System.Globalization; + + /// + /// the rect of coordinates + /// + public struct RectLatLng + { + public static readonly RectLatLng Empty; + private double lng; + private double lat; + private double widthLng; + private double heightLat; + + + // 2016/01/20 by DL2ALF: constructor with decimals added + public RectLatLng(decimal lat, decimal lng, decimal widthLng, decimal heightLat) + { + this.lng = (double)lng; + this.lat = (double)lat; + this.widthLng = (double)widthLng; + this.heightLat = (double)heightLat; + } + + public RectLatLng(double lat, double lng, double widthLng, double heightLat) + { + this.lng = lng; + this.lat = lat; + this.widthLng = widthLng; + this.heightLat = heightLat; + } + + public RectLatLng(PointLatLng location, SizeLatLng size) + { + this.lng = location.Lng; + this.lat = location.Lat; + this.widthLng = size.WidthLng; + this.heightLat = size.HeightLat; + } + + public static RectLatLng FromLTRB(double leftLng, double topLat, double rightLng, double bottomLat) + { + return new RectLatLng(topLat, leftLng, rightLng - leftLng, topLat - bottomLat); + } + + public PointLatLng LocationTopLeft + { + get + { + return new PointLatLng(this.Lat, this.Lng); + } + set + { + this.Lng = value.Lng; + this.Lat = value.Lat; + } + } + + public PointLatLng LocationRightBottom + { + get + { + PointLatLng ret = new PointLatLng(this.Lat, this.Lng); + ret.Offset(HeightLat, WidthLng); + return ret; + } + } + + public PointLatLng LocationMiddle + { + get + { + PointLatLng ret = new PointLatLng(this.Lat, this.Lng); + ret.Offset(HeightLat / 2, WidthLng / 2); + return ret; + } + } + + public SizeLatLng Size + { + get + { + return new SizeLatLng(this.HeightLat, this.WidthLng); + } + set + { + this.WidthLng = value.WidthLng; + this.HeightLat = value.HeightLat; + } + } + + public double Lng + { + get + { + return this.lng; + } + set + { + this.lng = value; + } + } + + public double Lat + { + get + { + return this.lat; + } + set + { + this.lat = value; + } + } + + public double WidthLng + { + get + { + return this.widthLng; + } + set + { + this.widthLng = value; + } + } + + public double HeightLat + { + get + { + return this.heightLat; + } + set + { + this.heightLat = value; + } + } + + public double Left + { + get + { + return this.Lng; + } + } + + public double Top + { + get + { + return this.Lat; + } + } + + public double Right + { + get + { + return (this.Lng + this.WidthLng); + } + } + + public double Bottom + { + get + { + return (this.Lat - this.HeightLat); + } + } + + public bool IsEmpty + { + get + { + if(this.WidthLng > 0d) + { + return (this.HeightLat <= 0d); + } + return true; + } + } + + public override bool Equals(object obj) + { + if(!(obj is RectLatLng)) + { + return false; + } + RectLatLng ef = (RectLatLng)obj; + return ((((ef.Lng == this.Lng) && (ef.Lat == this.Lat)) && (ef.WidthLng == this.WidthLng)) && (ef.HeightLat == this.HeightLat)); + } + + public static bool operator ==(RectLatLng left, RectLatLng right) + { + return ((((left.Lng == right.Lng) && (left.Lat == right.Lat)) && (left.WidthLng == right.WidthLng)) && (left.HeightLat == right.HeightLat)); + } + + public static bool operator !=(RectLatLng left, RectLatLng right) + { + return !(left == right); + } + + public bool Contains(double lat, double lng) + { + return ((((this.Lng <= lng) && (lng < (this.Lng + this.WidthLng))) && (this.Lat >= lat)) && (lat > (this.Lat - this.HeightLat))); + } + + public bool Contains(PointLatLng pt) + { + return this.Contains(pt.Lat, pt.Lng); + } + + public bool Contains(RectLatLng rect) + { + return ((((this.Lng <= rect.Lng) && ((rect.Lng + rect.WidthLng) <= (this.Lng + this.WidthLng))) && (this.Lat >= rect.Lat)) && ((rect.Lat - rect.HeightLat) >= (this.Lat - this.HeightLat))); + } + + public override int GetHashCode() + { + if(this.IsEmpty) + { + return 0; + } + return (((this.Lng.GetHashCode() ^ this.Lat.GetHashCode()) ^ this.WidthLng.GetHashCode()) ^ this.HeightLat.GetHashCode()); + } + + // from here down need to test each function to be sure they work good + // | + // . + + #region -- unsure -- + public void Inflate(double lat, double lng) + { + this.Lng -= lng; + this.Lat += lat; + this.WidthLng += 2d * lng; + this.HeightLat += 2d * lat; + } + + public void Inflate(SizeLatLng size) + { + this.Inflate(size.HeightLat, size.WidthLng); + } + + public static RectLatLng Inflate(RectLatLng rect, double lat, double lng) + { + RectLatLng ef = rect; + ef.Inflate(lat, lng); + return ef; + } + + public void Intersect(RectLatLng rect) + { + RectLatLng ef = Intersect(rect, this); + this.Lng = ef.Lng; + this.Lat = ef.Lat; + this.WidthLng = ef.WidthLng; + this.HeightLat = ef.HeightLat; + } + + // ok ??? + public static RectLatLng Intersect(RectLatLng a, RectLatLng b) + { + double lng = Math.Max(a.Lng, b.Lng); + double num2 = Math.Min((double)(a.Lng + a.WidthLng), (double)(b.Lng + b.WidthLng)); + + double lat = Math.Max(a.Lat, b.Lat); + double num4 = Math.Min((double)(a.Lat + a.HeightLat), (double)(b.Lat + b.HeightLat)); + + if((num2 >= lng) && (num4 >= lat)) + { + return new RectLatLng(lat, lng, num2 - lng, num4 - lat); + } + return Empty; + } + + // ok ??? + // http://greatmaps.codeplex.com/workitem/15981 + public bool IntersectsWith(RectLatLng a) + { + return this.Left < a.Right && this.Top > a.Bottom && this.Right > a.Left && this.Bottom < a.Top; + } + + // ok ??? + // http://greatmaps.codeplex.com/workitem/15981 + public static RectLatLng Union(RectLatLng a, RectLatLng b) + { + return RectLatLng.FromLTRB( + Math.Min(a.Left, b.Left), + Math.Max(a.Top, b.Top), + Math.Max(a.Right, b.Right), + Math.Min(a.Bottom, b.Bottom)); + } + #endregion + + // . + // | + // unsure ends here + + public void Offset(PointLatLng pos) + { + this.Offset(pos.Lat, pos.Lng); + } + + public void Offset(double lat, double lng) + { + this.Lng += lng; + this.Lat -= lat; + } + + public override string ToString() + { + return ("{Lat=" + this.Lat.ToString(CultureInfo.CurrentCulture) + ",Lng=" + this.Lng.ToString(CultureInfo.CurrentCulture) + ",WidthLng=" + this.WidthLng.ToString(CultureInfo.CurrentCulture) + ",HeightLat=" + this.HeightLat.ToString(CultureInfo.CurrentCulture) + "}"); + } + + static RectLatLng() + { + Empty = new RectLatLng(); + } + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/GMap.NET/RenderMode.cs b/GreatMaps/GMap.NET.Core/GMap.NET/RenderMode.cs new file mode 100644 index 0000000..f3fe309 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET/RenderMode.cs @@ -0,0 +1,19 @@ + +namespace GMap.NET +{ + /// + /// types of map rendering + /// + public enum RenderMode + { + /// + /// gdi+ should work anywhere on Windows Forms + /// + GDI_PLUS, + + /// + /// only on Windows Presentation Foundation + /// + WPF, + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET/RoutingProvider.cs b/GreatMaps/GMap.NET.Core/GMap.NET/RoutingProvider.cs new file mode 100644 index 0000000..6ec6585 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET/RoutingProvider.cs @@ -0,0 +1,19 @@ + +namespace GMap.NET +{ + /// + /// routing interface + /// + public interface RoutingProvider + { + /// + /// get route between two points + /// + MapRoute GetRoute(PointLatLng start, PointLatLng end, bool avoidHighways, bool walkingMode, int Zoom); + + /// + /// get route between two points + /// + MapRoute GetRoute(string start, string end, bool avoidHighways, bool walkingMode, int Zoom); + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET/Singleton.cs b/GreatMaps/GMap.NET.Core/GMap.NET/Singleton.cs new file mode 100644 index 0000000..8745f33 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET/Singleton.cs @@ -0,0 +1,60 @@ + +namespace GMap.NET +{ + using System; + using System.Diagnostics; + using System.Reflection; + + /// + /// generic for singletons + /// + /// + public class Singleton where T : new() + { + // ctor + protected Singleton() + { + if(Instance != null) + { + throw (new Exception("You have tried to create a new singleton class where you should have instanced it. Replace your \"new class()\" with \"class.Instance\"")); + } + } + + public static T Instance + { + get + { + if(SingletonCreator.exception != null) + { + throw SingletonCreator.exception; + } + return SingletonCreator.instance; + } + } + + class SingletonCreator + { + static SingletonCreator() + { + try + { + instance = new T(); + } + catch(Exception ex) + { + if(ex.InnerException != null) + { + exception = ex.InnerException; + } + else + { + exception = ex; + } + Trace.WriteLine("Singleton: " + exception); + } + } + internal static readonly T instance; + internal static readonly Exception exception; + } + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET/SizeLatLng.cs b/GreatMaps/GMap.NET.Core/GMap.NET/SizeLatLng.cs new file mode 100644 index 0000000..1583dc2 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET/SizeLatLng.cs @@ -0,0 +1,135 @@ + +namespace GMap.NET +{ + using System.Globalization; + + /// + /// the size of coordinates + /// + public struct SizeLatLng + { + public static readonly SizeLatLng Empty; + + private double heightLat; + private double widthLng; + + public SizeLatLng(SizeLatLng size) + { + this.widthLng = size.widthLng; + this.heightLat = size.heightLat; + } + + public SizeLatLng(PointLatLng pt) + { + this.heightLat = pt.Lat; + this.widthLng = pt.Lng; + } + + public SizeLatLng(double heightLat, double widthLng) + { + this.heightLat = heightLat; + this.widthLng = widthLng; + } + + public static SizeLatLng operator+(SizeLatLng sz1, SizeLatLng sz2) + { + return Add(sz1, sz2); + } + + public static SizeLatLng operator-(SizeLatLng sz1, SizeLatLng sz2) + { + return Subtract(sz1, sz2); + } + + public static bool operator==(SizeLatLng sz1, SizeLatLng sz2) + { + return ((sz1.WidthLng == sz2.WidthLng) && (sz1.HeightLat == sz2.HeightLat)); + } + + public static bool operator!=(SizeLatLng sz1, SizeLatLng sz2) + { + return !(sz1 == sz2); + } + + public static explicit operator PointLatLng(SizeLatLng size) + { + return new PointLatLng(size.HeightLat, size.WidthLng); + } + + public bool IsEmpty + { + get + { + return ((this.widthLng == 0d) && (this.heightLat == 0d)); + } + } + + public double WidthLng + { + get + { + return this.widthLng; + } + set + { + this.widthLng = value; + } + } + + public double HeightLat + { + get + { + return this.heightLat; + } + set + { + this.heightLat = value; + } + } + + public static SizeLatLng Add(SizeLatLng sz1, SizeLatLng sz2) + { + return new SizeLatLng(sz1.HeightLat + sz2.HeightLat, sz1.WidthLng + sz2.WidthLng); + } + + public static SizeLatLng Subtract(SizeLatLng sz1, SizeLatLng sz2) + { + return new SizeLatLng(sz1.HeightLat - sz2.HeightLat, sz1.WidthLng - sz2.WidthLng); + } + + public override bool Equals(object obj) + { + if(!(obj is SizeLatLng)) + { + return false; + } + SizeLatLng ef = (SizeLatLng) obj; + return (((ef.WidthLng == this.WidthLng) && (ef.HeightLat == this.HeightLat)) && ef.GetType().Equals(base.GetType())); + } + + public override int GetHashCode() + { + if(this.IsEmpty) + { + return 0; + } + return (this.WidthLng.GetHashCode() ^ this.HeightLat.GetHashCode()); + } + + public PointLatLng ToPointLatLng() + { + return (PointLatLng) this; + } + + public override string ToString() + { + return ("{WidthLng=" + this.widthLng.ToString(CultureInfo.CurrentCulture) + ", HeightLng=" + this.heightLat.ToString(CultureInfo.CurrentCulture) + "}"); + } + + static SizeLatLng() + { + Empty = new SizeLatLng(); + } + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET/StatusCodes.cs b/GreatMaps/GMap.NET.Core/GMap.NET/StatusCodes.cs new file mode 100644 index 0000000..dda3db0 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET/StatusCodes.cs @@ -0,0 +1,125 @@ + +namespace GMap.NET +{ + /// + /// GeoCoder StatusCode + /// + public enum GeoCoderStatusCode : int + { + /// + /// unknow response + /// + Unknow = -1, + + /// + /// No errors occurred; the address was successfully parsed and its geocode has been returned. + /// + G_GEO_SUCCESS = 200, + + /// + /// A directions request could not be successfully parsed. + /// For example, the request may have been rejected if it contained more than the maximum number of waypoints allowed. + /// + G_GEO_BAD_REQUEST = 400, + + /// + /// A geocoding or directions request could not be successfully processed, yet the exact reason for the failure is not known. + /// + G_GEO_SERVER_ERROR = 500, + + /// + /// The HTTP q parameter was either missing or had no value. + /// For geocoding requests, this means that an empty address was specified as input. For directions requests, this means that no query was specified in the input. + /// + G_GEO_MISSING_QUERY = 601, + + /// + /// Synonym for G_GEO_MISSING_QUERY. + /// + G_GEO_MISSING_ADDRESS = 601, + + /// + /// No corresponding geographic location could be found for the specified address. + /// This may be due to the fact that the address is relatively new, or it may be incorrect. + /// + G_GEO_UNKNOWN_ADDRESS = 602, + + /// + /// The geocode for the given address or the route for the given directions query cannot be returned due to legal or contractual reasons. + /// + G_GEO_UNAVAILABLE_ADDRESS = 603, + + /// + /// The GDirections object could not compute directions between the points mentioned in the query. + /// This is usually because there is no route available between the two points, or because we do not have data for routing in that region. + /// + G_GEO_UNKNOWN_DIRECTIONS = 604, + + /// + /// The given key is either invalid or does not match the domain for which it was given. + /// + G_GEO_BAD_KEY = 610, + + /// + /// The given key has gone over the requests limit in the 24 hour period or has submitted too many requests in too short a period of time. + /// If you're sending multiple requests in parallel or in a tight loop, use a timer or pause in your code to make sure you don't send the requests too quickly. + /// + G_GEO_TOO_MANY_QUERIES = 620, + + /// + /// indicates that exception occured during execution + /// + ExceptionInCode, + } + + /// + /// Direction StatusCode + /// + public enum DirectionsStatusCode : int + { + /// + /// indicates the response contains a valid result. + /// + OK = 0, + + /// + /// indicates at least one of the locations specified in the requests's origin, destination, or waypoints could not be geocoded. + /// + NOT_FOUND, + + /// + /// indicates no route could be found between the origin and destination. + /// + ZERO_RESULTS, + + /// + /// indicates that too many waypointss were provided in the request The maximum allowed waypoints is 8, plus the origin, and destination. + /// + MAX_WAYPOINTS_EXCEEDED, + + /// + /// indicates that the provided request was invalid. + /// + INVALID_REQUEST, + + /// + /// indicates the service has received too many requests from your application within the allowed time period. + /// + OVER_QUERY_LIMIT, + + /// + /// indicates that the service denied use of the directions service by your application. + /// + REQUEST_DENIED, + + /// + /// indicates a directions request could not be processed due to a server error. The request may succeed if you try again. + /// + UNKNOWN_ERROR, + + /// + /// indicates that exception occured during execution + /// + ExceptionInCode, + } +} diff --git a/GreatMaps/GMap.NET.Core/GMap.NET/gpx.cs b/GreatMaps/GMap.NET.Core/GMap.NET/gpx.cs new file mode 100644 index 0000000..aa85e26 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/GMap.NET/gpx.cs @@ -0,0 +1,1442 @@ +// +// This source code was auto-generated by xsd, Version=4.0.30319.1. +// +namespace GMap.NET +{ + using System.Xml.Serialization; + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")] + [System.SerializableAttribute()] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.topografix.com/GPX/1/1")] + [System.Xml.Serialization.XmlRootAttribute("gpx", Namespace="http://www.topografix.com/GPX/1/1", IsNullable=false)] + public partial class gpxType + { + + private metadataType metadataField; + + private wptType[] wptField; + + private rteType[] rteField; + + private trkType[] trkField; + + private extensionsType extensionsField; + + private string versionField; + + private string creatorField; + + public gpxType() + { + this.versionField = "1.1"; + } + + /// + public metadataType metadata + { + get + { + return this.metadataField; + } + set + { + this.metadataField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute("wpt")] + public wptType[] wpt + { + get + { + return this.wptField; + } + set + { + this.wptField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute("rte")] + public rteType[] rte + { + get + { + return this.rteField; + } + set + { + this.rteField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute("trk")] + public trkType[] trk + { + get + { + return this.trkField; + } + set + { + this.trkField = value; + } + } + + /// + public extensionsType extensions + { + get + { + return this.extensionsField; + } + set + { + this.extensionsField = value; + } + } + + /// + [System.Xml.Serialization.XmlAttributeAttribute()] + public string version + { + get + { + return this.versionField; + } + set + { + this.versionField = value; + } + } + + /// + [System.Xml.Serialization.XmlAttributeAttribute()] + public string creator + { + get + { + return this.creatorField; + } + set + { + this.creatorField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")] + [System.SerializableAttribute()] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.topografix.com/GPX/1/1")] + public partial class metadataType + { + + private string nameField; + + private string descField; + + private personType authorField; + + private copyrightType copyrightField; + + private linkType[] linkField; + + private System.DateTime timeField; + + private bool timeFieldSpecified; + + private string keywordsField; + + private boundsType boundsField; + + private extensionsType extensionsField; + + /// + public string name + { + get + { + return this.nameField; + } + set + { + this.nameField = value; + } + } + + /// + public string desc + { + get + { + return this.descField; + } + set + { + this.descField = value; + } + } + + /// + public personType author + { + get + { + return this.authorField; + } + set + { + this.authorField = value; + } + } + + /// + public copyrightType copyright + { + get + { + return this.copyrightField; + } + set + { + this.copyrightField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute("link")] + public linkType[] link + { + get + { + return this.linkField; + } + set + { + this.linkField = value; + } + } + + /// + public System.DateTime time + { + get + { + return this.timeField; + } + set + { + this.timeField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool timeSpecified + { + get + { + return this.timeFieldSpecified; + } + set + { + this.timeFieldSpecified = value; + } + } + + /// + public string keywords + { + get + { + return this.keywordsField; + } + set + { + this.keywordsField = value; + } + } + + /// + public boundsType bounds + { + get + { + return this.boundsField; + } + set + { + this.boundsField = value; + } + } + + /// + public extensionsType extensions + { + get + { + return this.extensionsField; + } + set + { + this.extensionsField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")] + [System.SerializableAttribute()] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.topografix.com/GPX/1/1")] + public partial class personType + { + + private string nameField; + + private emailType emailField; + + private linkType linkField; + + /// + public string name + { + get + { + return this.nameField; + } + set + { + this.nameField = value; + } + } + + /// + public emailType email + { + get + { + return this.emailField; + } + set + { + this.emailField = value; + } + } + + /// + public linkType link + { + get + { + return this.linkField; + } + set + { + this.linkField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")] + [System.SerializableAttribute()] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.topografix.com/GPX/1/1")] + public partial class emailType + { + + private string idField; + + private string domainField; + + /// + [System.Xml.Serialization.XmlAttributeAttribute()] + public string id + { + get + { + return this.idField; + } + set + { + this.idField = value; + } + } + + /// + [System.Xml.Serialization.XmlAttributeAttribute()] + public string domain + { + get + { + return this.domainField; + } + set + { + this.domainField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")] + [System.SerializableAttribute()] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.topografix.com/GPX/1/1")] + public partial class trksegType + { + + private wptType[] trkptField; + + private extensionsType extensionsField; + + /// + [System.Xml.Serialization.XmlElementAttribute("trkpt")] + public wptType[] trkpt + { + get + { + return this.trkptField; + } + set + { + this.trkptField = value; + } + } + + /// + public extensionsType extensions + { + get + { + return this.extensionsField; + } + set + { + this.extensionsField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")] + [System.SerializableAttribute()] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.topografix.com/GPX/1/1")] + public partial class wptType + { + + private decimal eleField; + + private bool eleFieldSpecified; + + private System.DateTime timeField; + + private bool timeFieldSpecified; + + private decimal magvarField; + + private bool magvarFieldSpecified; + + private decimal geoidheightField; + + private bool geoidheightFieldSpecified; + + private string nameField; + + private string cmtField; + + private string descField; + + private string srcField; + + private linkType[] linkField; + + private string symField; + + private string typeField; + + private fixType fixField; + + private bool fixFieldSpecified; + + private string satField; + + private decimal hdopField; + + private bool hdopFieldSpecified; + + private decimal vdopField; + + private bool vdopFieldSpecified; + + private decimal pdopField; + + private bool pdopFieldSpecified; + + private decimal ageofdgpsdataField; + + private bool ageofdgpsdataFieldSpecified; + + private string dgpsidField; + + private extensionsType extensionsField; + + private decimal latField; + + private decimal lonField; + + /// + public decimal ele + { + get + { + return this.eleField; + } + set + { + this.eleField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool eleSpecified + { + get + { + return this.eleFieldSpecified; + } + set + { + this.eleFieldSpecified = value; + } + } + + /// + public System.DateTime time + { + get + { + return this.timeField; + } + set + { + this.timeField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool timeSpecified + { + get + { + return this.timeFieldSpecified; + } + set + { + this.timeFieldSpecified = value; + } + } + + /// + public decimal magvar + { + get + { + return this.magvarField; + } + set + { + this.magvarField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool magvarSpecified + { + get + { + return this.magvarFieldSpecified; + } + set + { + this.magvarFieldSpecified = value; + } + } + + /// + public decimal geoidheight + { + get + { + return this.geoidheightField; + } + set + { + this.geoidheightField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool geoidheightSpecified + { + get + { + return this.geoidheightFieldSpecified; + } + set + { + this.geoidheightFieldSpecified = value; + } + } + + /// + public string name + { + get + { + return this.nameField; + } + set + { + this.nameField = value; + } + } + + /// + public string cmt + { + get + { + return this.cmtField; + } + set + { + this.cmtField = value; + } + } + + /// + public string desc + { + get + { + return this.descField; + } + set + { + this.descField = value; + } + } + + /// + public string src + { + get + { + return this.srcField; + } + set + { + this.srcField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute("link")] + public linkType[] link + { + get + { + return this.linkField; + } + set + { + this.linkField = value; + } + } + + /// + public string sym + { + get + { + return this.symField; + } + set + { + this.symField = value; + } + } + + /// + public string type + { + get + { + return this.typeField; + } + set + { + this.typeField = value; + } + } + + /// + public fixType fix + { + get + { + return this.fixField; + } + set + { + this.fixField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool fixSpecified + { + get + { + return this.fixFieldSpecified; + } + set + { + this.fixFieldSpecified = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(DataType="nonNegativeInteger")] + public string sat + { + get + { + return this.satField; + } + set + { + this.satField = value; + } + } + + /// + public decimal hdop + { + get + { + return this.hdopField; + } + set + { + this.hdopField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool hdopSpecified + { + get + { + return this.hdopFieldSpecified; + } + set + { + this.hdopFieldSpecified = value; + } + } + + /// + public decimal vdop + { + get + { + return this.vdopField; + } + set + { + this.vdopField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool vdopSpecified + { + get + { + return this.vdopFieldSpecified; + } + set + { + this.vdopFieldSpecified = value; + } + } + + /// + public decimal pdop + { + get + { + return this.pdopField; + } + set + { + this.pdopField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool pdopSpecified + { + get + { + return this.pdopFieldSpecified; + } + set + { + this.pdopFieldSpecified = value; + } + } + + /// + public decimal ageofdgpsdata + { + get + { + return this.ageofdgpsdataField; + } + set + { + this.ageofdgpsdataField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool ageofdgpsdataSpecified + { + get + { + return this.ageofdgpsdataFieldSpecified; + } + set + { + this.ageofdgpsdataFieldSpecified = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(DataType="integer")] + public string dgpsid + { + get + { + return this.dgpsidField; + } + set + { + this.dgpsidField = value; + } + } + + /// + public extensionsType extensions + { + get + { + return this.extensionsField; + } + set + { + this.extensionsField = value; + } + } + + /// + [System.Xml.Serialization.XmlAttributeAttribute()] + public decimal lat + { + get + { + return this.latField; + } + set + { + this.latField = value; + } + } + + /// + [System.Xml.Serialization.XmlAttributeAttribute()] + public decimal lon + { + get + { + return this.lonField; + } + set + { + this.lonField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")] + [System.SerializableAttribute()] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.topografix.com/GPX/1/1")] + public partial class linkType + { + + private string textField; + + private string typeField; + + private string hrefField; + + /// + public string text + { + get + { + return this.textField; + } + set + { + this.textField = value; + } + } + + /// + public string type + { + get + { + return this.typeField; + } + set + { + this.typeField = value; + } + } + + /// + [System.Xml.Serialization.XmlAttributeAttribute(DataType="anyURI")] + public string href + { + get + { + return this.hrefField; + } + set + { + this.hrefField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")] + [System.SerializableAttribute()] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.topografix.com/GPX/1/1")] + public enum fixType + { + + /// + none, + + /// + [System.Xml.Serialization.XmlEnumAttribute("2d")] + Item2d, + + /// + [System.Xml.Serialization.XmlEnumAttribute("3d")] + Item3d, + + /// + dgps, + + /// + pps, + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")] + [System.SerializableAttribute()] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.topografix.com/GPX/1/1")] + public partial class extensionsType + { + + private System.Xml.XmlElement[] anyField; + + /// + [System.Xml.Serialization.XmlAnyElementAttribute()] + public System.Xml.XmlElement[] Any + { + get + { + return this.anyField; + } + set + { + this.anyField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")] + [System.SerializableAttribute()] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.topografix.com/GPX/1/1")] + public partial class trkType + { + + private string nameField; + + private string cmtField; + + private string descField; + + private string srcField; + + private linkType[] linkField; + + private string numberField; + + private string typeField; + + private extensionsType extensionsField; + + private trksegType[] trksegField; + + /// + public string name + { + get + { + return this.nameField; + } + set + { + this.nameField = value; + } + } + + /// + public string cmt + { + get + { + return this.cmtField; + } + set + { + this.cmtField = value; + } + } + + /// + public string desc + { + get + { + return this.descField; + } + set + { + this.descField = value; + } + } + + /// + public string src + { + get + { + return this.srcField; + } + set + { + this.srcField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute("link")] + public linkType[] link + { + get + { + return this.linkField; + } + set + { + this.linkField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(DataType="nonNegativeInteger")] + public string number + { + get + { + return this.numberField; + } + set + { + this.numberField = value; + } + } + + /// + public string type + { + get + { + return this.typeField; + } + set + { + this.typeField = value; + } + } + + /// + public extensionsType extensions + { + get + { + return this.extensionsField; + } + set + { + this.extensionsField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute("trkseg")] + public trksegType[] trkseg + { + get + { + return this.trksegField; + } + set + { + this.trksegField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")] + [System.SerializableAttribute()] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.topografix.com/GPX/1/1")] + public partial class rteType + { + + private string nameField; + + private string cmtField; + + private string descField; + + private string srcField; + + private linkType[] linkField; + + private string numberField; + + private string typeField; + + private extensionsType extensionsField; + + private wptType[] rteptField; + + /// + public string name + { + get + { + return this.nameField; + } + set + { + this.nameField = value; + } + } + + /// + public string cmt + { + get + { + return this.cmtField; + } + set + { + this.cmtField = value; + } + } + + /// + public string desc + { + get + { + return this.descField; + } + set + { + this.descField = value; + } + } + + /// + public string src + { + get + { + return this.srcField; + } + set + { + this.srcField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute("link")] + public linkType[] link + { + get + { + return this.linkField; + } + set + { + this.linkField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(DataType="nonNegativeInteger")] + public string number + { + get + { + return this.numberField; + } + set + { + this.numberField = value; + } + } + + /// + public string type + { + get + { + return this.typeField; + } + set + { + this.typeField = value; + } + } + + /// + public extensionsType extensions + { + get + { + return this.extensionsField; + } + set + { + this.extensionsField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute("rtept")] + public wptType[] rtept + { + get + { + return this.rteptField; + } + set + { + this.rteptField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")] + [System.SerializableAttribute()] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.topografix.com/GPX/1/1")] + public partial class boundsType + { + + private decimal minlatField; + + private decimal minlonField; + + private decimal maxlatField; + + private decimal maxlonField; + + /// + [System.Xml.Serialization.XmlAttributeAttribute()] + public decimal minlat + { + get + { + return this.minlatField; + } + set + { + this.minlatField = value; + } + } + + /// + [System.Xml.Serialization.XmlAttributeAttribute()] + public decimal minlon + { + get + { + return this.minlonField; + } + set + { + this.minlonField = value; + } + } + + /// + [System.Xml.Serialization.XmlAttributeAttribute()] + public decimal maxlat + { + get + { + return this.maxlatField; + } + set + { + this.maxlatField = value; + } + } + + /// + [System.Xml.Serialization.XmlAttributeAttribute()] + public decimal maxlon + { + get + { + return this.maxlonField; + } + set + { + this.maxlonField = value; + } + } + } + + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")] + [System.SerializableAttribute()] + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.ComponentModel.DesignerCategoryAttribute("code")] + [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.topografix.com/GPX/1/1")] + public partial class copyrightType + { + + private string yearField; + + private string licenseField; + + private string authorField; + + /// + [System.Xml.Serialization.XmlElementAttribute(DataType="gYear")] + public string year + { + get + { + return this.yearField; + } + set + { + this.yearField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(DataType="anyURI")] + public string license + { + get + { + return this.licenseField; + } + set + { + this.licenseField = value; + } + } + + /// + [System.Xml.Serialization.XmlAttributeAttribute()] + public string author + { + get + { + return this.authorField; + } + set + { + this.authorField = value; + } + } + } +} diff --git a/GreatMaps/GMap.NET.Core/Properties/AssemblyInfo.cs b/GreatMaps/GMap.NET.Core/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..ab3864f --- /dev/null +++ b/GreatMaps/GMap.NET.Core/Properties/AssemblyInfo.cs @@ -0,0 +1,41 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +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("GMap.NET.Core")] +[assembly: AssemblyDescription("GMap.NET - Great Maps")] +[assembly: AssemblyProduct("GMap.NET.Core")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("843e1f67-489b-4454-b451-021e5c526e30")] + +// internal visibility +[assembly: +#if DEBUG + InternalsVisibleTo("ConsoleApplication, PublicKey=0024000004800000940000000602000000240000525341310004000001000100cd251b0b8f7079914bbe3e5655d92e5427218f3f0241537a9cb7467b6da2aa5cb20915c31400800e3081d20e6454a35164600fe8bf4f846744f211e040588260cc872c78abd91b422c60071bfda5f11d251eb09f0935944b41de2a28374ad17e8c963d642310df9050e8ae0f1a2b867bcc8f035e4b353dc699cfc7125b9661ce"), +#endif + InternalsVisibleTo("GMap.NET.WindowsForms, PublicKey=0024000004800000940000000602000000240000525341310004000001000100cd251b0b8f7079914bbe3e5655d92e5427218f3f0241537a9cb7467b6da2aa5cb20915c31400800e3081d20e6454a35164600fe8bf4f846744f211e040588260cc872c78abd91b422c60071bfda5f11d251eb09f0935944b41de2a28374ad17e8c963d642310df9050e8ae0f1a2b867bcc8f035e4b353dc699cfc7125b9661ce"), + InternalsVisibleTo("GMap.NET.WindowsMobile, PublicKey=0024000004800000940000000602000000240000525341310004000001000100cd251b0b8f7079914bbe3e5655d92e5427218f3f0241537a9cb7467b6da2aa5cb20915c31400800e3081d20e6454a35164600fe8bf4f846744f211e040588260cc872c78abd91b422c60071bfda5f11d251eb09f0935944b41de2a28374ad17e8c963d642310df9050e8ae0f1a2b867bcc8f035e4b353dc699cfc7125b9661ce"), + InternalsVisibleTo("GMap.NET.WindowsPresentation, PublicKey=0024000004800000940000000602000000240000525341310004000001000100cd251b0b8f7079914bbe3e5655d92e5427218f3f0241537a9cb7467b6da2aa5cb20915c31400800e3081d20e6454a35164600fe8bf4f846744f211e040588260cc872c78abd91b422c60071bfda5f11d251eb09f0935944b41de2a28374ad17e8c963d642310df9050e8ae0f1a2b867bcc8f035e4b353dc699cfc7125b9661ce")] + + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// 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("1.5.3.3")] +//[assembly: AssemblyFileVersion("1.5.3.3")] diff --git a/GreatMaps/GMap.NET.Core/Properties/Resources.Designer.cs b/GreatMaps/GMap.NET.Core/Properties/Resources.Designer.cs new file mode 100644 index 0000000..8ebb9a2 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/Properties/Resources.Designer.cs @@ -0,0 +1,119 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.18052 +// +// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn +// der Code erneut generiert wird. +// +//------------------------------------------------------------------------------ + +namespace GMap.NET.Properties { + using System; + + + /// + /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw. + /// + // Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert + // -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert. + // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen + // mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("GMap.NET.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle + /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die CREATE TABLE IF NOT EXISTS Tiles (id INTEGER NOT NULL PRIMARY KEY, X INTEGER NOT NULL, Y INTEGER NOT NULL, Zoom INTEGER NOT NULL, Type UNSIGNED INTEGER NOT NULL, CacheTime DATETIME); + ///CREATE INDEX IF NOT EXISTS IndexOfTiles ON Tiles (X, Y, Zoom, Type); + /// + ///CREATE TABLE IF NOT EXISTS TilesData (id INTEGER NOT NULL PRIMARY KEY CONSTRAINT fk_Tiles_id REFERENCES Tiles(id) ON DELETE CASCADE, Tile BLOB NULL); + /// + ///-- Foreign Key Preventing insert + ///CREATE TRIGGER fki_TilesData_id_Tiles_id + ///BEFORE INSERT ON [TilesDat [Rest der Zeichenfolge wurde abgeschnitten]"; ähnelt. + /// + internal static string CreateTileDb { + get { + return ResourceManager.GetString("CreateTileDb", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Byte[]. + /// + internal static byte[] System_Data_SQLite_x64_NET2_dll { + get { + object obj = ResourceManager.GetObject("System_Data_SQLite_x64_NET2_dll", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Byte[]. + /// + internal static byte[] System_Data_SQLite_x64_NET4_dll { + get { + object obj = ResourceManager.GetObject("System_Data_SQLite_x64_NET4_dll", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Byte[]. + /// + internal static byte[] System_Data_SQLite_x86_NET2_dll { + get { + object obj = ResourceManager.GetObject("System_Data_SQLite_x86_NET2_dll", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Byte[]. + /// + internal static byte[] System_Data_SQLite_x86_NET4_dll { + get { + object obj = ResourceManager.GetObject("System_Data_SQLite_x86_NET4_dll", resourceCulture); + return ((byte[])(obj)); + } + } + } +} diff --git a/GreatMaps/GMap.NET.Core/Properties/Resources.resx b/GreatMaps/GMap.NET.Core/Properties/Resources.resx new file mode 100644 index 0000000..8a59235 --- /dev/null +++ b/GreatMaps/GMap.NET.Core/Properties/Resources.resx @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + CREATE TABLE IF NOT EXISTS Tiles (id INTEGER NOT NULL PRIMARY KEY, X INTEGER NOT NULL, Y INTEGER NOT NULL, Zoom INTEGER NOT NULL, Type UNSIGNED INTEGER NOT NULL, CacheTime DATETIME); +CREATE INDEX IF NOT EXISTS IndexOfTiles ON Tiles (X, Y, Zoom, Type); + +CREATE TABLE IF NOT EXISTS TilesData (id INTEGER NOT NULL PRIMARY KEY CONSTRAINT fk_Tiles_id REFERENCES Tiles(id) ON DELETE CASCADE, Tile BLOB NULL); + +-- Foreign Key Preventing insert +CREATE TRIGGER fki_TilesData_id_Tiles_id +BEFORE INSERT ON [TilesData] +FOR EACH ROW BEGIN + SELECT RAISE(ROLLBACK, 'insert on table "TilesData" violates foreign key constraint "fki_TilesData_id_Tiles_id"') + WHERE (SELECT id FROM Tiles WHERE id = NEW.id) IS NULL; +END; + +-- Foreign key preventing update +CREATE TRIGGER fku_TilesData_id_Tiles_id +BEFORE UPDATE ON [TilesData] +FOR EACH ROW BEGIN + SELECT RAISE(ROLLBACK, 'update on table "TilesData" violates foreign key constraint "fku_TilesData_id_Tiles_id"') + WHERE (SELECT id FROM Tiles WHERE id = NEW.id) IS NULL; +END; + +-- Cascading Delete +CREATE TRIGGER fkdc_TilesData_id_Tiles_id +BEFORE DELETE ON Tiles +FOR EACH ROW BEGIN + DELETE FROM TilesData WHERE TilesData.id = OLD.id; +END; + + + + ..\Resources\System.Data.SQLite.x64.NET2.dll.gz;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\Resources\System.Data.SQLite.x64.NET4.dll.gz;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\Resources\System.Data.SQLite.x86.NET2.dll.gz;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\Resources\System.Data.SQLite.x86.NET4.dll.gz;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/Properties/VersionInfo.cs b/GreatMaps/GMap.NET.Core/Properties/VersionInfo.cs new file mode 100644 index 0000000..6ded1ca --- /dev/null +++ b/GreatMaps/GMap.NET.Core/Properties/VersionInfo.cs @@ -0,0 +1,25 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +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: AssemblyCulture("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Universe")] +[assembly: AssemblyCopyright("Copyright © Universe 2011")] +[assembly: AssemblyTrademark("email@radioman.lt")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// 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("1.7")] +[assembly: AssemblyFileVersion("1.7")] diff --git a/GreatMaps/GMap.NET.Core/Resources/System.Data.SQLite.x64.NET2.dll.gz b/GreatMaps/GMap.NET.Core/Resources/System.Data.SQLite.x64.NET2.dll.gz new file mode 100644 index 0000000..f84e420 Binary files /dev/null and b/GreatMaps/GMap.NET.Core/Resources/System.Data.SQLite.x64.NET2.dll.gz differ diff --git a/GreatMaps/GMap.NET.Core/Resources/System.Data.SQLite.x64.NET4.dll.gz b/GreatMaps/GMap.NET.Core/Resources/System.Data.SQLite.x64.NET4.dll.gz new file mode 100644 index 0000000..3a9b15c Binary files /dev/null and b/GreatMaps/GMap.NET.Core/Resources/System.Data.SQLite.x64.NET4.dll.gz differ diff --git a/GreatMaps/GMap.NET.Core/Resources/System.Data.SQLite.x86.NET2.dll.gz b/GreatMaps/GMap.NET.Core/Resources/System.Data.SQLite.x86.NET2.dll.gz new file mode 100644 index 0000000..2230d5f Binary files /dev/null and b/GreatMaps/GMap.NET.Core/Resources/System.Data.SQLite.x86.NET2.dll.gz differ diff --git a/GreatMaps/GMap.NET.Core/Resources/System.Data.SQLite.x86.NET4.dll.gz b/GreatMaps/GMap.NET.Core/Resources/System.Data.SQLite.x86.NET4.dll.gz new file mode 100644 index 0000000..45b986b Binary files /dev/null and b/GreatMaps/GMap.NET.Core/Resources/System.Data.SQLite.x86.NET4.dll.gz differ diff --git a/GreatMaps/GMap.NET.Core/Resources/blue-dot.png b/GreatMaps/GMap.NET.Core/Resources/blue-dot.png new file mode 100644 index 0000000..98b280d Binary files /dev/null and b/GreatMaps/GMap.NET.Core/Resources/blue-dot.png differ diff --git a/GreatMaps/GMap.NET.Core/Resources/drag_cross_67_16.png b/GreatMaps/GMap.NET.Core/Resources/drag_cross_67_16.png new file mode 100644 index 0000000..a3094a9 Binary files /dev/null and b/GreatMaps/GMap.NET.Core/Resources/drag_cross_67_16.png differ diff --git a/GreatMaps/GMap.NET.Core/Resources/green-dot.png b/GreatMaps/GMap.NET.Core/Resources/green-dot.png new file mode 100644 index 0000000..c6e6836 Binary files /dev/null and b/GreatMaps/GMap.NET.Core/Resources/green-dot.png differ diff --git a/GreatMaps/GMap.NET.Core/Resources/marker.png b/GreatMaps/GMap.NET.Core/Resources/marker.png new file mode 100644 index 0000000..d3770dd Binary files /dev/null and b/GreatMaps/GMap.NET.Core/Resources/marker.png differ diff --git a/GreatMaps/GMap.NET.Core/Resources/mm_20_blue.png b/GreatMaps/GMap.NET.Core/Resources/mm_20_blue.png new file mode 100644 index 0000000..2ec9ae9 Binary files /dev/null and b/GreatMaps/GMap.NET.Core/Resources/mm_20_blue.png differ diff --git a/GreatMaps/GMap.NET.Core/Resources/mm_20_green.png b/GreatMaps/GMap.NET.Core/Resources/mm_20_green.png new file mode 100644 index 0000000..462894b Binary files /dev/null and b/GreatMaps/GMap.NET.Core/Resources/mm_20_green.png differ diff --git a/GreatMaps/GMap.NET.Core/Resources/mm_20_red.png b/GreatMaps/GMap.NET.Core/Resources/mm_20_red.png new file mode 100644 index 0000000..f960799 Binary files /dev/null and b/GreatMaps/GMap.NET.Core/Resources/mm_20_red.png differ diff --git a/GreatMaps/GMap.NET.Core/Resources/mm_20_shadow.png b/GreatMaps/GMap.NET.Core/Resources/mm_20_shadow.png new file mode 100644 index 0000000..3a89759 Binary files /dev/null and b/GreatMaps/GMap.NET.Core/Resources/mm_20_shadow.png differ diff --git a/GreatMaps/GMap.NET.Core/Resources/mm_20_yellow.png b/GreatMaps/GMap.NET.Core/Resources/mm_20_yellow.png new file mode 100644 index 0000000..26e8a9d Binary files /dev/null and b/GreatMaps/GMap.NET.Core/Resources/mm_20_yellow.png differ diff --git a/GreatMaps/GMap.NET.Core/Resources/red-dot.png b/GreatMaps/GMap.NET.Core/Resources/red-dot.png new file mode 100644 index 0000000..b0f3f0e Binary files /dev/null and b/GreatMaps/GMap.NET.Core/Resources/red-dot.png differ diff --git a/GreatMaps/GMap.NET.Core/Resources/shadow50.png b/GreatMaps/GMap.NET.Core/Resources/shadow50.png new file mode 100644 index 0000000..62af3ea Binary files /dev/null and b/GreatMaps/GMap.NET.Core/Resources/shadow50.png differ diff --git a/GreatMaps/GMap.NET.Core/Resources/yellow-dot.png b/GreatMaps/GMap.NET.Core/Resources/yellow-dot.png new file mode 100644 index 0000000..79974d6 Binary files /dev/null and b/GreatMaps/GMap.NET.Core/Resources/yellow-dot.png differ diff --git a/GreatMaps/GMap.NET.Core/packages.config b/GreatMaps/GMap.NET.Core/packages.config new file mode 100644 index 0000000..0aee17e --- /dev/null +++ b/GreatMaps/GMap.NET.Core/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/GreatMaps/GMap.NET.Core/sn.snk b/GreatMaps/GMap.NET.Core/sn.snk new file mode 100644 index 0000000..5d3f356 Binary files /dev/null and b/GreatMaps/GMap.NET.Core/sn.snk differ diff --git a/GreatMaps/GMap.NET.WindowsForms/GMap.NET.ObjectModel/ObservableCollection.cs b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.ObjectModel/ObservableCollection.cs new file mode 100644 index 0000000..cd7c075 --- /dev/null +++ b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.ObjectModel/ObservableCollection.cs @@ -0,0 +1,541 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; + +namespace GMap.NET.ObjectModel +{ + public delegate void NotifyCollectionChangedEventHandler(object sender, NotifyCollectionChangedEventArgs e); + + public interface INotifyCollectionChanged + { + // Events + event NotifyCollectionChangedEventHandler CollectionChanged; + } + + public interface INotifyPropertyChanged + { + // Events + event PropertyChangedEventHandler PropertyChanged; + } + + public enum NotifyCollectionChangedAction + { + Add, + Remove, + Replace, + Move, + Reset + } + + public class NotifyCollectionChangedEventArgs : EventArgs + { + // Fields + private NotifyCollectionChangedAction _action; + private IList _newItems; + private int _newStartingIndex; + private IList _oldItems; + private int _oldStartingIndex; + + // Methods + public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action) + { + this._newStartingIndex = -1; + this._oldStartingIndex = -1; + if(action != NotifyCollectionChangedAction.Reset) + { + throw new ArgumentException("WrongActionForCtor", "action"); + } + this.InitializeAdd(action, null, -1); + } + + public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, IList changedItems) + { + this._newStartingIndex = -1; + this._oldStartingIndex = -1; + if(((action != NotifyCollectionChangedAction.Add) && (action != NotifyCollectionChangedAction.Remove)) && (action != NotifyCollectionChangedAction.Reset)) + { + throw new ArgumentException("MustBeResetAddOrRemoveActionForCtor", "action"); + } + if(action == NotifyCollectionChangedAction.Reset) + { + if(changedItems != null) + { + throw new ArgumentException("ResetActionRequiresNullItem", "action"); + } + this.InitializeAdd(action, null, -1); + } + else + { + if(changedItems == null) + { + throw new ArgumentNullException("changedItems"); + } + this.InitializeAddOrRemove(action, changedItems, -1); + } + } + + public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, object changedItem) + { + this._newStartingIndex = -1; + this._oldStartingIndex = -1; + if(((action != NotifyCollectionChangedAction.Add) && (action != NotifyCollectionChangedAction.Remove)) && (action != NotifyCollectionChangedAction.Reset)) + { + throw new ArgumentException("MustBeResetAddOrRemoveActionForCtor", "action"); + } + if(action == NotifyCollectionChangedAction.Reset) + { + if(changedItem != null) + { + throw new ArgumentException("ResetActionRequiresNullItem", "action"); + } + this.InitializeAdd(action, null, -1); + } + else + { + this.InitializeAddOrRemove(action, new object[] { changedItem }, -1); + } + } + + public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, IList newItems, IList oldItems) + { + this._newStartingIndex = -1; + this._oldStartingIndex = -1; + if(action != NotifyCollectionChangedAction.Replace) + { + throw new ArgumentException("WrongActionForCtor", "action"); + } + if(newItems == null) + { + throw new ArgumentNullException("newItems"); + } + if(oldItems == null) + { + throw new ArgumentNullException("oldItems"); + } + this.InitializeMoveOrReplace(action, newItems, oldItems, -1, -1); + } + + public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, IList changedItems, int startingIndex) + { + this._newStartingIndex = -1; + this._oldStartingIndex = -1; + if(((action != NotifyCollectionChangedAction.Add) && (action != NotifyCollectionChangedAction.Remove)) && (action != NotifyCollectionChangedAction.Reset)) + { + throw new ArgumentException("MustBeResetAddOrRemoveActionForCtor", "action"); + } + if(action == NotifyCollectionChangedAction.Reset) + { + if(changedItems != null) + { + throw new ArgumentException("ResetActionRequiresNullItem", "action"); + } + if(startingIndex != -1) + { + throw new ArgumentException("ResetActionRequiresIndexMinus1", "action"); + } + this.InitializeAdd(action, null, -1); + } + else + { + if(changedItems == null) + { + throw new ArgumentNullException("changedItems"); + } + if(startingIndex < -1) + { + throw new ArgumentException("IndexCannotBeNegative", "startingIndex"); + } + this.InitializeAddOrRemove(action, changedItems, startingIndex); + } + } + + public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, object changedItem, int index) + { + this._newStartingIndex = -1; + this._oldStartingIndex = -1; + if(((action != NotifyCollectionChangedAction.Add) && (action != NotifyCollectionChangedAction.Remove)) && (action != NotifyCollectionChangedAction.Reset)) + { + throw new ArgumentException("MustBeResetAddOrRemoveActionForCtor", "action"); + } + if(action == NotifyCollectionChangedAction.Reset) + { + if(changedItem != null) + { + throw new ArgumentException("ResetActionRequiresNullItem", "action"); + } + if(index != -1) + { + throw new ArgumentException("ResetActionRequiresIndexMinus1", "action"); + } + this.InitializeAdd(action, null, -1); + } + else + { + this.InitializeAddOrRemove(action, new object[] { changedItem }, index); + } + } + + public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, object newItem, object oldItem) + { + this._newStartingIndex = -1; + this._oldStartingIndex = -1; + if(action != NotifyCollectionChangedAction.Replace) + { + throw new ArgumentException("WrongActionForCtor", "action"); + } + this.InitializeMoveOrReplace(action, new object[] { newItem }, new object[] { oldItem }, -1, -1); + } + + public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, IList newItems, IList oldItems, int startingIndex) + { + this._newStartingIndex = -1; + this._oldStartingIndex = -1; + if(action != NotifyCollectionChangedAction.Replace) + { + throw new ArgumentException("WrongActionForCtor", "action"); + } + if(newItems == null) + { + throw new ArgumentNullException("newItems"); + } + if(oldItems == null) + { + throw new ArgumentNullException("oldItems"); + } + this.InitializeMoveOrReplace(action, newItems, oldItems, startingIndex, startingIndex); + } + + public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, IList changedItems, int index, int oldIndex) + { + this._newStartingIndex = -1; + this._oldStartingIndex = -1; + if(action != NotifyCollectionChangedAction.Move) + { + throw new ArgumentException("WrongActionForCtor", "action"); + } + if(index < 0) + { + throw new ArgumentException("IndexCannotBeNegative", "index"); + } + this.InitializeMoveOrReplace(action, changedItems, changedItems, index, oldIndex); + } + + public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, object changedItem, int index, int oldIndex) + { + this._newStartingIndex = -1; + this._oldStartingIndex = -1; + if(action != NotifyCollectionChangedAction.Move) + { + throw new ArgumentException("WrongActionForCtor", "action"); + } + if(index < 0) + { + throw new ArgumentException("IndexCannotBeNegative", "index"); + } + object[] newItems = new object[] { changedItem }; + this.InitializeMoveOrReplace(action, newItems, newItems, index, oldIndex); + } + + public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, object newItem, object oldItem, int index) + { + this._newStartingIndex = -1; + this._oldStartingIndex = -1; + if(action != NotifyCollectionChangedAction.Replace) + { + throw new ArgumentException("WrongActionForCtor", "action"); + } + this.InitializeMoveOrReplace(action, new object[] { newItem }, new object[] { oldItem }, index, index); + } + + private void InitializeAdd(NotifyCollectionChangedAction action, IList newItems, int newStartingIndex) + { + this._action = action; +#if !PocketPC + this._newItems = (newItems == null) ? null : ArrayList.ReadOnly(newItems); +#else + this._newItems = (newItems == null) ? null : newItems; +#endif + this._newStartingIndex = newStartingIndex; + } + + private void InitializeAddOrRemove(NotifyCollectionChangedAction action, IList changedItems, int startingIndex) + { + if(action == NotifyCollectionChangedAction.Add) + { + this.InitializeAdd(action, changedItems, startingIndex); + } + else if(action == NotifyCollectionChangedAction.Remove) + { + this.InitializeRemove(action, changedItems, startingIndex); + } + else + { + throw new ArgumentException(string.Format("InvariantFailure, Unsupported action: {0}", action.ToString())); + } + } + + private void InitializeMoveOrReplace(NotifyCollectionChangedAction action, IList newItems, IList oldItems, int startingIndex, int oldStartingIndex) + { + this.InitializeAdd(action, newItems, startingIndex); + this.InitializeRemove(action, oldItems, oldStartingIndex); + } + + private void InitializeRemove(NotifyCollectionChangedAction action, IList oldItems, int oldStartingIndex) + { + this._action = action; +#if !PocketPC + this._oldItems = (oldItems == null) ? null : ArrayList.ReadOnly(oldItems); +#else + this._oldItems = (oldItems == null) ? null : oldItems; +#endif + this._oldStartingIndex = oldStartingIndex; + } + + // Properties + public NotifyCollectionChangedAction Action + { + get + { + return this._action; + } + } + + public IList NewItems + { + get + { + return this._newItems; + } + } + + public int NewStartingIndex + { + get + { + return this._newStartingIndex; + } + } + + public IList OldItems + { + get + { + return this._oldItems; + } + } + + public int OldStartingIndex + { + get + { + return this._oldStartingIndex; + } + } + } + + [Serializable] + public class ObservableCollection : Collection, INotifyCollectionChanged, INotifyPropertyChanged + { + // Fields + private SimpleMonitor _monitor; + private const string CountString = "Count"; + private const string IndexerName = "Item[]"; + + // Events + [field: NonSerialized] + public virtual event NotifyCollectionChangedEventHandler CollectionChanged; + + [field: NonSerialized] + protected event PropertyChangedEventHandler PropertyChanged; + + event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged + { + add + { + PropertyChanged += value; + } + remove + { + PropertyChanged -= value; + } + } + + // Methods + public ObservableCollection() + { + this._monitor = new SimpleMonitor(); + } + + public ObservableCollection(IEnumerable collection) + { + this._monitor = new SimpleMonitor(); + if(collection == null) + { + throw new ArgumentNullException("collection"); + } + this.CopyFrom(collection); + } + + public ObservableCollection(List list) + : base((list != null) ? new List(list.Count) : list) + { + this._monitor = new SimpleMonitor(); + this.CopyFrom(list); + } + + protected IDisposable BlockReentrancy() + { + this._monitor.Enter(); + return this._monitor; + } + + protected void CheckReentrancy() + { + if((this._monitor.Busy && (this.CollectionChanged != null)) && (this.CollectionChanged.GetInvocationList().Length > 1)) + { + throw new InvalidOperationException("ObservableCollectionReentrancyNotAllowed"); + } + } + + protected override void ClearItems() + { + this.CheckReentrancy(); + base.ClearItems(); + this.OnPropertyChanged(CountString); + this.OnPropertyChanged(IndexerName); + this.OnCollectionReset(); + } + + private void CopyFrom(IEnumerable collection) + { + IList items = base.Items; + if((collection != null) && (items != null)) + { + using(IEnumerator enumerator = collection.GetEnumerator()) + { + while(enumerator.MoveNext()) + { + items.Add(enumerator.Current); + } + } + } + } + + protected override void InsertItem(int index, T item) + { + this.CheckReentrancy(); + base.InsertItem(index, item); + this.OnPropertyChanged(CountString); + this.OnPropertyChanged(IndexerName); + this.OnCollectionChanged(NotifyCollectionChangedAction.Add, item, index); + } + + public void Move(int oldIndex, int newIndex) + { + this.MoveItem(oldIndex, newIndex); + } + + protected virtual void MoveItem(int oldIndex, int newIndex) + { + this.CheckReentrancy(); + T item = base[oldIndex]; + base.RemoveItem(oldIndex); + base.InsertItem(newIndex, item); + this.OnPropertyChanged(IndexerName); + this.OnCollectionChanged(NotifyCollectionChangedAction.Move, item, newIndex, oldIndex); + } + + protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e) + { + if(this.CollectionChanged != null) + { + using(this.BlockReentrancy()) + { + this.CollectionChanged(this, e); + } + } + } + + private void OnCollectionChanged(NotifyCollectionChangedAction action, object item, int index) + { + this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, item, index)); + } + + private void OnCollectionChanged(NotifyCollectionChangedAction action, object item, int index, int oldIndex) + { + this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, item, index, oldIndex)); + } + + private void OnCollectionChanged(NotifyCollectionChangedAction action, object oldItem, object newItem, int index) + { + this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, newItem, oldItem, index)); + } + + private void OnCollectionReset() + { + this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + } + + protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) + { + if(this.PropertyChanged != null) + { + this.PropertyChanged(this, e); + } + } + + private void OnPropertyChanged(string propertyName) + { + this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName)); + } + + protected override void RemoveItem(int index) + { + this.CheckReentrancy(); + T item = base[index]; + base.RemoveItem(index); + this.OnPropertyChanged(CountString); + this.OnPropertyChanged(IndexerName); + this.OnCollectionChanged(NotifyCollectionChangedAction.Remove, item, index); + } + + protected override void SetItem(int index, T item) + { + this.CheckReentrancy(); + T oldItem = base[index]; + base.SetItem(index, item); + this.OnPropertyChanged(IndexerName); + this.OnCollectionChanged(NotifyCollectionChangedAction.Replace, oldItem, item, index); + } + + // Nested Types + [Serializable] + private class SimpleMonitor : IDisposable + { + // Fields + private int _busyCount; + + // Methods + public void Dispose() + { + this._busyCount--; + } + + public void Enter() + { + this._busyCount++; + } + + // Properties + public bool Busy + { + get + { + return (this._busyCount > 0); + } + } + } + } +} diff --git a/GreatMaps/GMap.NET.WindowsForms/GMap.NET.ObjectModel/ObservableCollectionThreadSafe.cs b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.ObjectModel/ObservableCollectionThreadSafe.cs new file mode 100644 index 0000000..ed1bb2c --- /dev/null +++ b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.ObjectModel/ObservableCollectionThreadSafe.cs @@ -0,0 +1,62 @@ +using System; + +namespace GMap.NET.ObjectModel +{ + public class ObservableCollectionThreadSafe : ObservableCollection + { + NotifyCollectionChangedEventHandler collectionChanged; + public override event NotifyCollectionChangedEventHandler CollectionChanged + { + add + { + collectionChanged += value; + } + remove + { + collectionChanged -= value; + } + } + + protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) + { + // Be nice - use BlockReentrancy like MSDN said + using(BlockReentrancy()) + { + if(collectionChanged != null) + { + Delegate[] delegates = collectionChanged.GetInvocationList(); + + // Walk thru invocation list + foreach(NotifyCollectionChangedEventHandler handler in delegates) + { +#if !PocketPC + System.Windows.Forms.Control dispatcherObject = handler.Target as System.Windows.Forms.Control; + + // If the subscriber is a DispatcherObject and different thread + if(dispatcherObject != null && dispatcherObject.InvokeRequired) + { + // Invoke handler in the target dispatcher's thread + dispatcherObject.Invoke(handler, this, e); + } + else // Execute handler as is + { + collectionChanged(this, e); + } +#else + // If the subscriber is a DispatcherObject and different thread + if(handler != null) + { + // Invoke handler in the target dispatcher's thread + handler.Invoke(handler, e); + } + else // Execute handler as is + { + collectionChanged(this, e); + } +#endif + } + } + } + } + } +} diff --git a/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms.csproj b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms.csproj new file mode 100644 index 0000000..f4c708e --- /dev/null +++ b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms.csproj @@ -0,0 +1,220 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {E06DEF77-F933-42FB-AFD7-DB2D0D8D6A98} + Library + Properties + System.Windows.Forms + GMap.NET.WindowsForms + v4.0 + 512 + true + sn.snk + + + 3.5 + + + + + + + true + bin\Debug\ + TRACE;DEBUG;ContinuesMapNo + true + true + full + AnyCPU + bin\x86\Debug\GMap.NET.WindowsForms.dll.CodeAnalysisLog.xml + true + GlobalSuppressions.cs + prompt + MinimumRecommendedRules.ruleset + ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets + ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules + + + bin\Release\ + TRACE;ContinuesMapNo + true + bin\x86\Release\GMap.NET.WindowsForms.XML + true + AnyCPU + bin\x86\Release\GMap.NET.WindowsForms.dll.CodeAnalysisLog.xml + true + GlobalSuppressions.cs + prompt + MinimumRecommendedRules.ruleset + ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets + ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules + + + true + bin\x86\Debug\ + TRACE;DEBUG;ContinuesMapNo + true + true + full + x86 + bin\x86\Debug\GMap.NET.WindowsForms.dll.CodeAnalysisLog.xml + true + GlobalSuppressions.cs + prompt + MinimumRecommendedRules.ruleset + ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets + true + ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules + true + true + Off + + + bin\x86\Release\ + TRACE;ContinuesMapNo + true + bin\x86\Release\GMap.NET.WindowsForms.XML + true + x86 + bin\x86\Release\GMap.NET.WindowsForms.dll.CodeAnalysisLog.xml + true + GlobalSuppressions.cs + prompt + MinimumRecommendedRules.ruleset + ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets + ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules + + + + ..\..\packages\Newtonsoft.Json.12.0.1\lib\net40\Newtonsoft.Json.dll + True + + + + ..\..\packages\System.Data.SQLite.Core.1.0.110.0\lib\net40\System.Data.SQLite.dll + True + + + + + + + Properties\VersionInfo.cs + + + + + + + + + Form + + + TilePrefetcher.cs + + + + + + + True + True + Resources.resx + + + + Code + + + + UserControl + + + + + + + TilePrefetcher.cs + Designer + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {D0C39D9D-BED0-418B-9A5E-713176CAF40C} + GMap.NET.Core + + + + + + + + + + + Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}". + + + + + \ No newline at end of file diff --git a/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/ColorMatrixs.cs b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/ColorMatrixs.cs new file mode 100644 index 0000000..720422a --- /dev/null +++ b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/ColorMatrixs.cs @@ -0,0 +1,28 @@ + +namespace GMap.NET.WindowsForms +{ + using System.Drawing.Imaging; + + public static class ColorMatrixs + { +#if !PocketPC + public static readonly ColorMatrix GrayScale = new ColorMatrix(new float[][] + { + new float[] {.3f, .3f, .3f, 0, 0}, + new float[] {.59f, .59f, .59f, 0, 0}, + new float[] {.11f, .11f, .11f, 0, 0}, + new float[] {0, 0, 0, 1, 0}, + new float[] {0, 0, 0, 0, 1} + }); + + public static readonly ColorMatrix Negative = new ColorMatrix(new float[][] + { + new float[] {-1, 0, 0, 0, 0}, + new float[] {0, -1, 0, 0, 0}, + new float[] {0, 0, -1, 0, 0}, + new float[] {0, 0, 0, 1, 0}, + new float[] {1, 1, 1, 0, 1} + }); +#endif + } +} diff --git a/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/GMapControl.cs b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/GMapControl.cs new file mode 100644 index 0000000..7a1ff13 --- /dev/null +++ b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/GMapControl.cs @@ -0,0 +1,3147 @@ + +namespace GMap.NET.WindowsForms +{ + using System; + using System.ComponentModel; + using System.Drawing; + using System.Drawing.Drawing2D; + using System.Drawing.Imaging; + using System.IO; + using System.Threading; + using System.Windows.Forms; + using GMap.NET; + using GMap.NET.Internals; + using GMap.NET.ObjectModel; + using System.Diagnostics; + using System.Drawing.Text; + using GMap.NET.MapProviders; + +#if !PocketPC + using System.Runtime.Serialization.Formatters.Binary; + using System.Collections.Generic; + using GMap.NET.Projections; +#else + using OpenNETCF.ComponentModel; +#endif + + /// + /// GMap.NET control for Windows Forms + /// + public partial class GMapControl : UserControl, Interface + { +#if !PocketPC + /// + /// occurs when clicked on marker + /// + public event MarkerClick OnMarkerClick; + + /// + /// occurs when clicked on polygon + /// + public event PolygonClick OnPolygonClick; + + /// + /// occurs when clicked on route + /// + public event RouteClick OnRouteClick; + + /// + /// occurs on mouse enters route area + /// + public event RouteEnter OnRouteEnter; + + /// + /// occurs on mouse leaves route area + /// + public event RouteLeave OnRouteLeave; + + /// + /// occurs when mouse selection is changed + /// + public event SelectionChange OnSelectionChange; +#endif + + /// + /// occurs on mouse enters marker area + /// + public event MarkerEnter OnMarkerEnter; + + /// + /// occurs on mouse leaves marker area + /// + public event MarkerLeave OnMarkerLeave; + + /// + /// occurs on mouse enters Polygon area + /// + public event PolygonEnter OnPolygonEnter; + + /// + /// occurs on mouse leaves Polygon area + /// + public event PolygonLeave OnPolygonLeave; + + /// + /// list of overlays, should be thread safe + /// + public readonly ObservableCollectionThreadSafe Overlays = new ObservableCollectionThreadSafe(); + + /// + /// max zoom + /// + [Category("GMap.NET")] + [Description("maximum zoom level of map")] + public int MaxZoom + { + get + { + return Core.maxZoom; + } + set + { + Core.maxZoom = value; + } + } + + /// + /// min zoom + /// + [Category("GMap.NET")] + [Description("minimum zoom level of map")] + public int MinZoom + { + get + { + return Core.minZoom; + } + set + { + Core.minZoom = value; + } + } + + /// + /// map zooming type for mouse wheel + /// + [Category("GMap.NET")] + [Description("map zooming type for mouse wheel")] + public MouseWheelZoomType MouseWheelZoomType + { + get + { + return Core.MouseWheelZoomType; + } + set + { + Core.MouseWheelZoomType = value; + } + } + + /// + /// text on empty tiles + /// + public string EmptyTileText = "We are sorry, but we don't\nhave imagery at this zoom\nlevel for this region."; + + /// + /// pen for empty tile borders + /// +#if !PocketPC + public Pen EmptyTileBorders = new Pen(Brushes.White, 1); +#else + public Pen EmptyTileBorders = new Pen(Color.White, 1); +#endif + + public bool ShowCenter = true; + + /// + /// pen for scale info + /// +#if !PocketPC + public Pen ScalePen = new Pen(Brushes.Blue, 1); + public Pen CenterPen = new Pen(Brushes.Red, 1); +#else + public Pen ScalePen = new Pen(Color.Blue, 1); + public Pen CenterPen = new Pen(Color.Red, 1); +#endif + +#if !PocketPC + /// + /// area selection pen + /// + public Pen SelectionPen = new Pen(Brushes.Blue, 2); + + Brush SelectedAreaFill = new SolidBrush(Color.FromArgb(33, Color.RoyalBlue)); + Color selectedAreaFillColor = Color.FromArgb(33, Color.RoyalBlue); + + /// + /// background of selected area + /// + [Category("GMap.NET")] + [Description("background color od the selected area")] + public Color SelectedAreaFillColor + { + get + { + return selectedAreaFillColor; + } + set + { + if(selectedAreaFillColor != value) + { + selectedAreaFillColor = value; + + if(SelectedAreaFill != null) + { + SelectedAreaFill.Dispose(); + SelectedAreaFill = null; + } + SelectedAreaFill = new SolidBrush(selectedAreaFillColor); + } + } + } + + HelperLineOptions helperLineOption = HelperLineOptions.DontShow; + + /// + /// draw lines at the mouse pointer position + /// + [Browsable(false)] + public HelperLineOptions HelperLineOption + { + get + { + return helperLineOption; + } + set + { + helperLineOption = value; + renderHelperLine = (helperLineOption == HelperLineOptions.ShowAlways); + if(Core.IsStarted) + { + Invalidate(); + } + } + } + + public Pen HelperLinePen = new Pen(Color.Blue, 1); + bool renderHelperLine = false; + + protected override void OnKeyDown(KeyEventArgs e) + { + if(HelperLineOption == HelperLineOptions.ShowOnModifierKey) + { + renderHelperLine = (e.Modifiers == Keys.Shift || e.Modifiers == Keys.Alt); + if(renderHelperLine) + { + Invalidate(); + } + } + base.OnKeyDown(e); + } + + protected override void OnKeyUp(KeyEventArgs e) + { + if(HelperLineOption == HelperLineOptions.ShowOnModifierKey) + { + renderHelperLine = (e.Modifiers == Keys.Shift || e.Modifiers == Keys.Alt); + if(!renderHelperLine) + { + Invalidate(); + } + } + base.OnKeyUp(e); + } +#endif + + Brush EmptytileBrush = new SolidBrush(Color.Navy); + Color emptyTileColor = Color.Navy; + + /// + /// color of empty tile background + /// + [Category("GMap.NET")] + [Description("background color of the empty tile")] + public Color EmptyTileColor + { + get + { + return emptyTileColor; + } + set + { + if(emptyTileColor != value) + { + emptyTileColor = value; + + if(EmptytileBrush != null) + { + EmptytileBrush.Dispose(); + EmptytileBrush = null; + } + EmptytileBrush = new SolidBrush(emptyTileColor); + } + } + } + +#if PocketPC + readonly Brush TileGridLinesTextBrush = new SolidBrush(Color.Red); + readonly Brush TileGridMissingTextBrush = new SolidBrush(Color.White); + readonly Brush CopyrightBrush = new SolidBrush(Color.Navy); +#endif + + /// + /// show map scale info + /// + public bool MapScaleInfoEnabled = false; + + /// + /// enables filling empty tiles using lower level images + /// + public bool FillEmptyTiles = true; + + /// + /// if true, selects area just by holding mouse and moving + /// + public bool DisableAltForSelection = false; + + /// + /// retry count to get tile + /// + [Browsable(false)] + public int RetryLoadTile + { + get + { + return Core.RetryLoadTile; + } + set + { + Core.RetryLoadTile = value; + } + } + + /// + /// how many levels of tiles are staying decompresed in memory + /// + [Browsable(false)] + public int LevelsKeepInMemmory + { + get + { + return Core.LevelsKeepInMemmory; + } + + set + { + Core.LevelsKeepInMemmory = value; + } + } + + /// + /// map dragg button + /// + [Category("GMap.NET")] + public MouseButtons DragButton = MouseButtons.Right; + + private bool showTileGridLines = false; + + /// + /// shows tile gridlines + /// + [Category("GMap.NET")] + [Description("shows tile gridlines")] + public bool ShowTileGridLines + { + get + { + return showTileGridLines; + } + set + { + showTileGridLines = value; + Invalidate(); + } + } + + /// + /// current selected area in map + /// + private RectLatLng selectedArea; + + [Browsable(false)] + public RectLatLng SelectedArea + { + get + { + return selectedArea; + } + set + { + selectedArea = value; + + if(Core.IsStarted) + { + Invalidate(); + } + } + } + + /// + /// map boundaries + /// + public RectLatLng? BoundsOfMap = null; + + /// + /// enables integrated DoubleBuffer for running on windows mobile + /// +#if !PocketPC + public bool ForceDoubleBuffer = false; + readonly bool MobileMode = false; +#else + readonly bool ForceDoubleBuffer = true; +#endif + + /// + /// stops immediate marker/route/polygon invalidations; + /// call Refresh to perform single refresh and reset invalidation state + /// + public bool HoldInvalidation = false; + + /// + /// call this to stop HoldInvalidation and perform single forced instant refresh + /// + public override void Refresh() + { + HoldInvalidation = false; + + lock(Core.invalidationLock) + { + Core.lastInvalidation = DateTime.Now; + } + + base.Refresh(); + } + +#if !DESIGN + /// + /// enque built-in thread safe invalidation + /// + public new void Invalidate() + { + if(Core.Refresh != null) + { + Core.Refresh.Set(); + } + } +#endif + +#if !PocketPC + private bool _GrayScale = false; + + [Category("GMap.NET")] + public bool GrayScaleMode + { + get + { + return _GrayScale; + } + set + { + _GrayScale = value; + ColorMatrix = (value == true ? ColorMatrixs.GrayScale : null); + } + } + + private bool _Negative = false; + + [Category("GMap.NET")] + public bool NegativeMode + { + get + { + return _Negative; + } + set + { + _Negative = value; + ColorMatrix = (value == true ? ColorMatrixs.Negative : null); + } + } + + ColorMatrix colorMatrix; + + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + [Browsable(false)] + public ColorMatrix ColorMatrix + { + get + { + return colorMatrix; + } + set + { + colorMatrix = value; + if(GMapProvider.TileImageProxy != null && GMapProvider.TileImageProxy is WindowsFormsImageProxy) + { + (GMapProvider.TileImageProxy as WindowsFormsImageProxy).ColorMatrix = value; + if(Core.IsStarted) + { + ReloadMap(); + } + } + } + } +#endif + + // internal stuff + internal readonly Core Core = new Core(); + + internal readonly Font CopyrightFont = new Font(FontFamily.GenericSansSerif, 7, FontStyle.Regular); +#if !PocketPC + internal readonly Font MissingDataFont = new Font(FontFamily.GenericSansSerif, 11, FontStyle.Bold); +#else + internal readonly Font MissingDataFont = new Font(FontFamily.GenericSansSerif, 8, FontStyle.Regular); +#endif + // geändert 29.11.2012 DL2ALF + Font ScaleFont = new Font(FontFamily.GenericSansSerif, 8, FontStyle.Bold); +// Font ScaleFont = new Font(FontFamily.GenericSansSerif, 5, FontStyle.Italic); + internal readonly StringFormat CenterFormat = new StringFormat(); + internal readonly StringFormat BottomFormat = new StringFormat(); +#if !PocketPC + readonly ImageAttributes TileFlipXYAttributes = new ImageAttributes(); +#endif + double zoomReal; + Bitmap backBuffer; + Graphics gxOff; + +#if !DESIGN + /// + /// construct + /// + public GMapControl() + { +#if !PocketPC + if(!DesignModeInConstruct && !IsDesignerHosted) +#endif + { +#if !PocketPC + Manager.SQLitePing(); + + this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true); + this.SetStyle(ControlStyles.AllPaintingInWmPaint, true); + this.SetStyle(ControlStyles.UserPaint, true); + this.SetStyle(ControlStyles.Opaque, true); + ResizeRedraw = true; + + TileFlipXYAttributes.SetWrapMode(WrapMode.TileFlipXY); + + // only one mode will be active, to get mixed mode create new ColorMatrix + GrayScaleMode = GrayScaleMode; + NegativeMode = NegativeMode; +#endif + GMapProvider.TileImageProxy = WindowsFormsImageProxy.Instance; + + Core.SystemType = "WindowsForms"; + + RenderMode = RenderMode.GDI_PLUS; + + CenterFormat.Alignment = StringAlignment.Center; + CenterFormat.LineAlignment = StringAlignment.Center; + + BottomFormat.Alignment = StringAlignment.Center; + +#if !PocketPC + BottomFormat.LineAlignment = StringAlignment.Far; +#endif + + if(GMaps.Instance.IsRunningOnMono) + { + // no imports to move pointer + MouseWheelZoomType = GMap.NET.MouseWheelZoomType.MousePositionWithoutCenter; + } + + Overlays.CollectionChanged += new NotifyCollectionChangedEventHandler(Overlays_CollectionChanged); + } + } + + void Overlays_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + if(e.NewItems != null) + { + foreach(GMapOverlay obj in e.NewItems) + { + if(obj != null) + { + obj.Control = this; + } + } + + if(Core.IsStarted && !HoldInvalidation) + { + Invalidate(); + } + } + } + +#endif + + void invalidatorEngage(object sender, ProgressChangedEventArgs e) + { + base.Invalidate(); + } + + /// + /// update objects when map is draged/zoomed + /// + internal void ForceUpdateOverlays() + { + try + { + HoldInvalidation = true; + + foreach(GMapOverlay o in Overlays) + { + if(o.IsVisibile) + { + o.ForceUpdate(); + } + } + } + finally + { + Refresh(); + } + } + + /// + /// render map in GDI+ + /// + /// + void DrawMap(Graphics g) + { + if(Core.updatingBounds || MapProvider == EmptyProvider.Instance || MapProvider == null) + { + Debug.WriteLine("Core.updatingBounds"); + return; + } + + Core.tileDrawingListLock.AcquireReaderLock(); + Core.Matrix.EnterReadLock(); + try + { + foreach(var tilePoint in Core.tileDrawingList) + { + { + Core.tileRect.Location = tilePoint.PosPixel; + if(ForceDoubleBuffer) + { +#if !PocketPC + if(MobileMode) + { + Core.tileRect.Offset(Core.renderOffset); + } +#else + Core.tileRect.Offset(Core.renderOffset); +#endif + } + Core.tileRect.OffsetNegative(Core.compensationOffset); + + //if(Core.currentRegion.IntersectsWith(Core.tileRect) || IsRotated) + { + bool found = false; + + Tile t = Core.Matrix.GetTileWithNoLock(Core.Zoom, tilePoint.PosXY); + if(t.NotEmpty) + { + // render tile + { + foreach(WindowsFormsImage img in t.Overlays) + { + if(img != null && img.Img != null) + { + if(!found) + found = true; + + if(!img.IsParent) + { +#if !PocketPC + g.DrawImage(img.Img, Core.tileRect.X, Core.tileRect.Y, Core.tileRectBearing.Width, Core.tileRectBearing.Height); +#else + g.DrawImage(img.Img, (int) Core.tileRect.X, (int) Core.tileRect.Y); +#endif + } +#if !PocketPC + else + { + // TODO: move calculations to loader thread + System.Drawing.RectangleF srcRect = new System.Drawing.RectangleF((float)(img.Xoff * (img.Img.Width / img.Ix)), (float)(img.Yoff * (img.Img.Height / img.Ix)), (img.Img.Width / img.Ix), (img.Img.Height / img.Ix)); + System.Drawing.Rectangle dst = new System.Drawing.Rectangle((int)Core.tileRect.X, (int)Core.tileRect.Y, (int)Core.tileRect.Width, (int)Core.tileRect.Height); + + g.DrawImage(img.Img, dst, srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, GraphicsUnit.Pixel, TileFlipXYAttributes); + } +#endif + } + } + } + } +#if !PocketPC + else if(FillEmptyTiles && MapProvider.Projection is MercatorProjection) + { + #region -- fill empty lines -- + int zoomOffset = 1; + Tile parentTile = Tile.Empty; + long Ix = 0; + + while(!parentTile.NotEmpty && zoomOffset < Core.Zoom && zoomOffset <= LevelsKeepInMemmory) + { + Ix = (long)Math.Pow(2, zoomOffset); + parentTile = Core.Matrix.GetTileWithNoLock(Core.Zoom - zoomOffset++, new GPoint((int)(tilePoint.PosXY.X / Ix), (int)(tilePoint.PosXY.Y / Ix))); + } + + if(parentTile.NotEmpty) + { + long Xoff = Math.Abs(tilePoint.PosXY.X - (parentTile.Pos.X * Ix)); + long Yoff = Math.Abs(tilePoint.PosXY.Y - (parentTile.Pos.Y * Ix)); + + // render tile + { + foreach(WindowsFormsImage img in parentTile.Overlays) + { + if(img != null && img.Img != null && !img.IsParent) + { + if(!found) + found = true; + + System.Drawing.RectangleF srcRect = new System.Drawing.RectangleF((float)(Xoff * (img.Img.Width / Ix)), (float)(Yoff * (img.Img.Height / Ix)), (img.Img.Width / Ix), (img.Img.Height / Ix)); + System.Drawing.Rectangle dst = new System.Drawing.Rectangle((int)Core.tileRect.X, (int)Core.tileRect.Y, (int)Core.tileRect.Width, (int)Core.tileRect.Height); + + g.DrawImage(img.Img, dst, srcRect.X, srcRect.Y, srcRect.Width, srcRect.Height, GraphicsUnit.Pixel, TileFlipXYAttributes); + g.FillRectangle(SelectedAreaFill, dst); + } + } + } + } + #endregion + } +#endif + // add text if tile is missing + if(!found) + { + lock(Core.FailedLoads) + { + var lt = new LoadTask(tilePoint.PosXY, Core.Zoom); + if(Core.FailedLoads.ContainsKey(lt)) + { + var ex = Core.FailedLoads[lt]; +#if !PocketPC + g.FillRectangle(EmptytileBrush, new RectangleF(Core.tileRect.X, Core.tileRect.Y, Core.tileRect.Width, Core.tileRect.Height)); + + g.DrawString("Exception: " + ex.Message, MissingDataFont, Brushes.Red, new RectangleF(Core.tileRect.X + 11, Core.tileRect.Y + 11, Core.tileRect.Width - 11, Core.tileRect.Height - 11)); + + g.DrawString(EmptyTileText, MissingDataFont, Brushes.Blue, new RectangleF(Core.tileRect.X, Core.tileRect.Y, Core.tileRect.Width, Core.tileRect.Height), CenterFormat); + +#else + g.FillRectangle(EmptytileBrush, new System.Drawing.Rectangle((int) Core.tileRect.X, (int) Core.tileRect.Y, (int) Core.tileRect.Width, (int) Core.tileRect.Height)); + + g.DrawString("Exception: " + ex.Message, MissingDataFont, TileGridMissingTextBrush, new RectangleF(Core.tileRect.X + 11, Core.tileRect.Y + 11, Core.tileRect.Width - 11, Core.tileRect.Height - 11)); + + g.DrawString(EmptyTileText, MissingDataFont, TileGridMissingTextBrush, new RectangleF(Core.tileRect.X, Core.tileRect.Y + Core.tileRect.Width / 2 + (ShowTileGridLines ? 11 : -22), Core.tileRect.Width, Core.tileRect.Height), BottomFormat); +#endif + + g.DrawRectangle(EmptyTileBorders, (int)Core.tileRect.X, (int)Core.tileRect.Y, (int)Core.tileRect.Width, (int)Core.tileRect.Height); + } + } + } + + if(ShowTileGridLines) + { + g.DrawRectangle(EmptyTileBorders, (int)Core.tileRect.X, (int)Core.tileRect.Y, (int)Core.tileRect.Width, (int)Core.tileRect.Height); + { +#if !PocketPC + g.DrawString((tilePoint.PosXY == Core.centerTileXYLocation ? "CENTER: " : "TILE: ") + tilePoint, MissingDataFont, Brushes.Red, new RectangleF(Core.tileRect.X, Core.tileRect.Y, Core.tileRect.Width, Core.tileRect.Height), CenterFormat); +#else + g.DrawString((tilePoint.PosXY == Core.centerTileXYLocation ? "" : "TILE: ") + tilePoint, MissingDataFont, TileGridLinesTextBrush, new RectangleF(Core.tileRect.X, Core.tileRect.Y, Core.tileRect.Width, Core.tileRect.Height), CenterFormat); +#endif + } + } + } + } + } + } + finally + { + Core.Matrix.LeaveReadLock(); + Core.tileDrawingListLock.ReleaseReaderLock(); + } + } + + /// + /// updates markers local position + /// + /// + public void UpdateMarkerLocalPosition(GMapMarker marker) + { + GPoint p = FromLatLngToLocal(marker.Position); + { +#if !PocketPC + if(!MobileMode) + { + p.OffsetNegative(Core.renderOffset); + } +#endif + + var f = new System.Drawing.Point((int)(p.X + marker.Offset.X), (int)(p.Y + marker.Offset.Y)); + marker.LocalPosition = f; + } + } + + /// + /// updates routes local position + /// + /// + public void UpdateRouteLocalPosition(GMapRoute route) + { + route.LocalPoints.Clear(); + + foreach(GMap.NET.PointLatLng pg in route.Points) + { + GPoint p = FromLatLngToLocal(pg); + +#if !PocketPC + if(!MobileMode) + { + p.OffsetNegative(Core.renderOffset); + } +#endif + + // if(IsRotated) + // { + //#if !PocketPC + // System.Drawing.Point[] tt = new System.Drawing.Point[] { new System.Drawing.Point(p.X, p.Y) }; + // rotationMatrix.TransformPoints(tt); + // var f = tt[0]; + + // p.X = f.X; + // p.Y = f.Y; + //#endif + // } + + route.LocalPoints.Add(p); + } +#if !PocketPC + route.UpdateGraphicsPath(); +#endif + } + + /// + /// updates polygons local position + /// + /// + public void UpdatePolygonLocalPosition(GMapPolygon polygon) + { + polygon.LocalPoints.Clear(); + + foreach(GMap.NET.PointLatLng pg in polygon.Points) + { + GPoint p = FromLatLngToLocal(pg); + +#if !PocketPC + if(!MobileMode) + { + p.OffsetNegative(Core.renderOffset); + } +#endif + + // if(IsRotated) + // { + //#if !PocketPC + // System.Drawing.Point[] tt = new System.Drawing.Point[] { new System.Drawing.Point(p.X, p.Y) }; + // rotationMatrix.TransformPoints(tt); + // var f = tt[0]; + + // p.X = f.X; + // p.Y = f.Y; + //#endif + // } + + polygon.LocalPoints.Add(p); + } + } + + /// + /// sets zoom to max to fit rect + /// + /// + /// + public bool SetZoomToFitRect(RectLatLng rect) + { + if(lazyEvents) + { + lazySetZoomToFitRect = rect; + } + else + { + int maxZoom = Core.GetMaxZoomToFitRect(rect); + if(maxZoom > 0) + { + PointLatLng center = new PointLatLng(rect.Lat - (rect.HeightLat / 2), rect.Lng + (rect.WidthLng / 2)); + Position = center; + + if(maxZoom > MaxZoom) + { + maxZoom = MaxZoom; + } + + if((int)Zoom != maxZoom) + { + Zoom = maxZoom; + } + + return true; + } + } + + return false; + } + + RectLatLng? lazySetZoomToFitRect = null; + bool lazyEvents = true; + + /// + /// sets to max zoom to fit all markers and centers them in map + /// + /// overlay id or null to check all + /// + public bool ZoomAndCenterMarkers(string overlayId) + { + RectLatLng? rect = GetRectOfAllMarkers(overlayId); + if(rect.HasValue) + { + return SetZoomToFitRect(rect.Value); + } + + return false; + } + + /// + /// zooms and centers all route + /// + /// overlay id or null to check all + /// + public bool ZoomAndCenterRoutes(string overlayId) + { + RectLatLng? rect = GetRectOfAllRoutes(overlayId); + if(rect.HasValue) + { + return SetZoomToFitRect(rect.Value); + } + + return false; + } + + /// + /// zooms and centers route + /// + /// + /// + public bool ZoomAndCenterRoute(MapRoute route) + { + RectLatLng? rect = GetRectOfRoute(route); + if(rect.HasValue) + { + return SetZoomToFitRect(rect.Value); + } + + return false; + } + + /// + /// gets rectangle with all objects inside + /// + /// overlay id or null to check all + /// + public RectLatLng? GetRectOfAllMarkers(string overlayId) + { + RectLatLng? ret = null; + + double left = double.MaxValue; + double top = double.MinValue; + double right = double.MinValue; + double bottom = double.MaxValue; + + foreach(GMapOverlay o in Overlays) + { + if(overlayId == null || o.Id == overlayId) + { + if(o.IsVisibile && o.Markers.Count > 0) + { + foreach(GMapMarker m in o.Markers) + { + if(m.IsVisible) + { + // left + if(m.Position.Lng < left) + { + left = m.Position.Lng; + } + + // top + if(m.Position.Lat > top) + { + top = m.Position.Lat; + } + + // right + if(m.Position.Lng > right) + { + right = m.Position.Lng; + } + + // bottom + if(m.Position.Lat < bottom) + { + bottom = m.Position.Lat; + } + } + } + } + } + } + + if(left != double.MaxValue && right != double.MinValue && top != double.MinValue && bottom != double.MaxValue) + { + ret = RectLatLng.FromLTRB(left, top, right, bottom); + } + + return ret; + } + + /// + /// gets rectangle with all objects inside + /// + /// overlay id or null to check all + /// + public RectLatLng? GetRectOfAllRoutes(string overlayId) + { + RectLatLng? ret = null; + + double left = double.MaxValue; + double top = double.MinValue; + double right = double.MinValue; + double bottom = double.MaxValue; + + foreach(GMapOverlay o in Overlays) + { + if(overlayId == null || o.Id == overlayId) + { + if(o.IsVisibile && o.Routes.Count > 0) + { + foreach(GMapRoute route in o.Routes) + { + if(route.IsVisible && route.From.HasValue && route.To.HasValue) + { + foreach(PointLatLng p in route.Points) + { + // left + if(p.Lng < left) + { + left = p.Lng; + } + + // top + if(p.Lat > top) + { + top = p.Lat; + } + + // right + if(p.Lng > right) + { + right = p.Lng; + } + + // bottom + if(p.Lat < bottom) + { + bottom = p.Lat; + } + } + } + } + } + } + } + + if(left != double.MaxValue && right != double.MinValue && top != double.MinValue && bottom != double.MaxValue) + { + ret = RectLatLng.FromLTRB(left, top, right, bottom); + } + + return ret; + } + + /// + /// gets rect of route + /// + /// + /// + public RectLatLng? GetRectOfRoute(MapRoute route) + { + RectLatLng? ret = null; + + double left = double.MaxValue; + double top = double.MinValue; + double right = double.MinValue; + double bottom = double.MaxValue; + + if(route.From.HasValue && route.To.HasValue) + { + foreach(PointLatLng p in route.Points) + { + // left + if(p.Lng < left) + { + left = p.Lng; + } + + // top + if(p.Lat > top) + { + top = p.Lat; + } + + // right + if(p.Lng > right) + { + right = p.Lng; + } + + // bottom + if(p.Lat < bottom) + { + bottom = p.Lat; + } + } + ret = RectLatLng.FromLTRB(left, top, right, bottom); + } + return ret; + } + +#if !PocketPC + /// + /// gets image of the current view + /// + /// + public Image ToImage() + { + Image ret = null; + try + { + using(Bitmap bitmap = new Bitmap(Width, Height)) + { + using(Graphics g = Graphics.FromImage(bitmap)) + { + using(Graphics gg = this.CreateGraphics()) + { +#if !PocketPC + g.CopyFromScreen(PointToScreen(new System.Drawing.Point()).X, PointToScreen(new System.Drawing.Point()).Y, 0, 0, new System.Drawing.Size(Width, Height)); +#else + throw new NotImplementedException("Not implemeted for PocketPC"); +#endif + } + } + + // Convert the Image to a png + using(MemoryStream ms = new MemoryStream()) + { + bitmap.Save(ms, ImageFormat.Png); +#if !PocketPC + ret = Image.FromStream(ms); +#else + throw new NotImplementedException("Not implemeted for PocketPC"); +#endif + } + } + } + catch + { + ret = null; + } + return ret; + } +#endif + + /// + /// offset position in pixels + /// + /// + /// + public void Offset(int x, int y) + { + if(IsHandleCreated) + { + // need to fix in rotated mode usinf rotationMatrix + // ... + Core.DragOffset(new GPoint(x, y)); + + ForceUpdateOverlays(); + } + } + + #region UserControl Events + +#if !PocketPC + protected bool DesignModeInConstruct + { + get + { + return (LicenseManager.UsageMode == LicenseUsageMode.Designtime); + } + } + + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + [Browsable(false)] + public bool IsDesignerHosted + { + get + { + return IsControlDesignerHosted(this); + } + } + + public bool IsControlDesignerHosted(Control ctrl) + { + if(ctrl != null) + { + if(ctrl.Site != null) + { + + if(ctrl.Site.DesignMode == true) + return true; + + else + { + if(IsControlDesignerHosted(ctrl.Parent)) + return true; + + else + return false; + } + } + else + { + if(IsControlDesignerHosted(ctrl.Parent)) + return true; + else + return false; + } + } + else + return false; + } + + protected override void OnLoad(EventArgs e) + { + base.OnLoad(e); + + if(!IsDesignerHosted) + { + //MethodInvoker m = delegate + //{ + // Thread.Sleep(444); + + //OnSizeChanged(null); + + if(lazyEvents) + { + lazyEvents = false; + + if(lazySetZoomToFitRect.HasValue) + { + SetZoomToFitRect(lazySetZoomToFitRect.Value); + lazySetZoomToFitRect = null; + } + } + Core.OnMapOpen().ProgressChanged += new ProgressChangedEventHandler(invalidatorEngage); + ForceUpdateOverlays(); + //}; + //this.BeginInvoke(m); + } + } +#else + //delegate void MethodInvoker(); + bool IsHandleCreated = false; + + protected override void OnPaintBackground(PaintEventArgs e) + { + if(!IsHandleCreated) + { + IsHandleCreated = true; + + if(lazyEvents) + { + lazyEvents = false; + + if(lazySetZoomToFitRect.HasValue) + { + SetZoomToFitRect(lazySetZoomToFitRect.Value); + lazySetZoomToFitRect = null; + } + } + + Core.OnMapOpen().ProgressChanged += new ProgressChangedEventHandler(invalidatorEngage); + ForceUpdateOverlays(); + } + } +#endif + +#if !PocketPC + protected override void OnCreateControl() + { + base.OnCreateControl(); + + var f = ParentForm; + if(f != null) + { + while(f.ParentForm != null) + { + f = f.ParentForm; + } + + if(f != null) + { + f.FormClosing += new FormClosingEventHandler(ParentForm_FormClosing); + } + } + } + + void ParentForm_FormClosing(object sender, FormClosingEventArgs e) + { + if(e.CloseReason == CloseReason.WindowsShutDown || e.CloseReason == CloseReason.TaskManagerClosing) + { + Manager.CancelTileCaching(); + } + } +#endif + + protected override void Dispose(bool disposing) + { + if(disposing) + { + Core.OnMapClose(); + + Overlays.CollectionChanged -= new NotifyCollectionChangedEventHandler(Overlays_CollectionChanged); + + foreach(var o in Overlays) + { + o.Dispose(); + } + Overlays.Clear(); + + if (ScaleFont != null) + ScaleFont.Dispose(); + if (ScalePen != null) + ScalePen.Dispose(); + CenterFormat.Dispose(); + CenterPen.Dispose(); + BottomFormat.Dispose(); + CopyrightFont.Dispose(); + EmptyTileBorders.Dispose(); + EmptytileBrush.Dispose(); + +#if !PocketPC + SelectedAreaFill.Dispose(); + if (SelectionPen != null) + SelectionPen.Dispose(); +#endif + if(backBuffer != null) + { + backBuffer.Dispose(); + backBuffer = null; + } + + if(gxOff != null) + { + gxOff.Dispose(); + gxOff = null; + } + } + base.Dispose(disposing); + } + + PointLatLng selectionStart; + PointLatLng selectionEnd; + +#if !PocketPC + float? MapRenderTransform = null; +#endif + + public Color EmptyMapBackground = Color.WhiteSmoke; + +#if !DESIGN + protected override void OnPaint(PaintEventArgs e) + { + if(ForceDoubleBuffer) + { + #region -- manual buffer -- + if(gxOff != null && backBuffer != null) + { + // render white background + gxOff.Clear(EmptyMapBackground); + +#if !PocketPC + if(MapRenderTransform.HasValue) + { + gxOff.TranslateTransform(Core.renderOffset.X, Core.renderOffset.Y); + gxOff.ScaleTransform(MapRenderTransform.Value, MapRenderTransform.Value); + { + DrawMap(gxOff); + OnPaintOverlays(gxOff); + } + } + else +#endif + { +#if !PocketPC + if(!MobileMode) + { + gxOff.TranslateTransform(Core.renderOffset.X, Core.renderOffset.Y); + } +#endif + DrawMap(gxOff); + } + + OnPaintOverlays(gxOff); + + e.Graphics.DrawImage(backBuffer, 0, 0); + } + #endregion + } + else + { + e.Graphics.Clear(EmptyMapBackground); + +#if !PocketPC + if(MapRenderTransform.HasValue) + { + if(!MobileMode) + { + var pc = new GPoint(Width / 2, Height / 2); + var pc2 = pc; + pc.OffsetNegative(Core.renderOffset); + pc2.OffsetNegative(pc); + + e.Graphics.ScaleTransform(MapRenderTransform.Value, MapRenderTransform.Value, MatrixOrder.Append); + + e.Graphics.TranslateTransform(pc2.X, pc2.Y, MatrixOrder.Append); + } + + { + DrawMap(e.Graphics); + + e.Graphics.ResetTransform(); + if(!MobileMode) + { + var pc = Core.renderOffset; + pc.OffsetNegative(new GPoint(Width / 2, Height / 2)); + e.Graphics.TranslateTransform(Core.renderOffset.X + -pc.X, Core.renderOffset.Y + -pc.Y); + } + + OnPaintOverlays(e.Graphics); + } + } + else +#endif + { +#if !PocketPC + if(IsRotated) + { + #region -- rotation -- + + e.Graphics.TextRenderingHint = TextRenderingHint.AntiAlias; + e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; + + e.Graphics.TranslateTransform((float)(Core.Width / 2.0), (float)(Core.Height / 2.0)); + e.Graphics.RotateTransform(-Bearing); + e.Graphics.TranslateTransform((float)(-Core.Width / 2.0), (float)(-Core.Height / 2.0)); + + e.Graphics.TranslateTransform(Core.renderOffset.X, Core.renderOffset.Y); + + DrawMap(e.Graphics); + OnPaintOverlays(e.Graphics); + + #endregion + } + else +#endif + { +#if !PocketPC + if(!MobileMode) + { + e.Graphics.TranslateTransform(Core.renderOffset.X, Core.renderOffset.Y); + } +#endif + DrawMap(e.Graphics); + OnPaintOverlays(e.Graphics); + } + } + } + + base.OnPaint(e); + } +#endif + +#if !PocketPC + readonly Matrix rotationMatrix = new Matrix(); + readonly Matrix rotationMatrixInvert = new Matrix(); + + /// + /// updates rotation matrix + /// + void UpdateRotationMatrix() + { + PointF center = new PointF(Core.Width / 2, Core.Height / 2); + + rotationMatrix.Reset(); + rotationMatrix.RotateAt(-Bearing, center); + + rotationMatrixInvert.Reset(); + rotationMatrixInvert.RotateAt(-Bearing, center); + rotationMatrixInvert.Invert(); + } + + /// + /// returs true if map bearing is not zero + /// + [Browsable(false)] + public bool IsRotated + { + get + { + return Core.IsRotated; + } + } + + /// + /// bearing for rotation of the map + /// + [Category("GMap.NET")] + public float Bearing + { + get + { + return Core.bearing; + } + set + { + //if(Core.bearing != value) + //{ + // bool resize = Core.bearing == 0; + // Core.bearing = value; + + // //if(VirtualSizeEnabled) + // //{ + // // c.X += (Width - Core.vWidth) / 2; + // // c.Y += (Height - Core.vHeight) / 2; + // //} + + // UpdateRotationMatrix(); + + // if(value != 0 && value % 360 != 0) + // { + // Core.IsRotated = true; + + // if(Core.tileRectBearing.Size == Core.tileRect.Size) + // { + // Core.tileRectBearing = Core.tileRect; + // Core.tileRectBearing.Inflate(1, 1); + // } + // } + // else + // { + // Core.IsRotated = false; + // Core.tileRectBearing = Core.tileRect; + // } + + // if(resize) + // { + // Core.OnMapSizeChanged(Width, Height); + // } + + // if(!HoldInvalidation && Core.IsStarted) + // { + // ForceUpdateOverlays(); + // } + //} + } + } +#endif + + /// + /// override, to render something more + /// + /// + protected virtual void OnPaintOverlays(Graphics g) + { +#if !PocketPC + g.SmoothingMode = SmoothingMode.HighQuality; +#endif + foreach(GMapOverlay o in Overlays) + { + if(o.IsVisibile) + { + o.OnRender(g); + } + } + + // center in virtual spcace... +#if DEBUG + g.DrawLine(ScalePen, -20, 0, 20, 0); + g.DrawLine(ScalePen, 0, -20, 0, 20); + +#if PocketPC + g.DrawString("debug build", CopyrightFont, CopyrightBrush, 2, CopyrightFont.Size); +#else + g.DrawString("debug build", CopyrightFont, Brushes.Blue, 2, CopyrightFont.Height); +#endif + +#endif + +#if !PocketPC + + if(!MobileMode) + { + g.ResetTransform(); + } + + if(!SelectedArea.IsEmpty) + { + GPoint p1 = FromLatLngToLocal(SelectedArea.LocationTopLeft); + GPoint p2 = FromLatLngToLocal(SelectedArea.LocationRightBottom); + + long x1 = p1.X; + long y1 = p1.Y; + long x2 = p2.X; + long y2 = p2.Y; + + g.DrawRectangle(SelectionPen, x1, y1, x2 - x1, y2 - y1); + g.FillRectangle(SelectedAreaFill, x1, y1, x2 - x1, y2 - y1); + } +#endif + if(ShowCenter) + { + g.DrawLine(CenterPen, Width / 2 - 5, Height / 2, Width / 2 + 5, Height / 2); + g.DrawLine(CenterPen, Width / 2, Height / 2 - 5, Width / 2, Height / 2 + 5); + } + + if(renderHelperLine) + { + var p = PointToClient(Form.MousePosition); + + g.DrawLine(HelperLinePen, p.X, 0, p.X, Height); + g.DrawLine(HelperLinePen, 0, p.Y, Width, p.Y); + } + + #region -- copyright -- + + if(!string.IsNullOrEmpty(Core.provider.Copyright)) + { +#if !PocketPC + g.DrawString(Core.provider.Copyright, CopyrightFont, Brushes.Navy, 3, Height - CopyrightFont.Height - 5); +#else + g.DrawString(Core.provider.Copyright, CopyrightFont, CopyrightBrush, 3, Height - CopyrightFont.Size - 15); +#endif + } + + #endregion + + #region -- draw scale -- +#if !PocketPC + if(MapScaleInfoEnabled) + { + /* + if(Width > Core.pxRes5000km) + { + g.DrawRectangle(ScalePen, 10, 10, Core.pxRes5000km, 10); + g.DrawString("5000Km", ScaleFont, Brushes.Blue, Core.pxRes5000km + 10, 11); + } + if(Width > Core.pxRes1000km) + { + g.DrawRectangle(ScalePen, 10, 10, Core.pxRes1000km, 10); + g.DrawString("1000Km", ScaleFont, Brushes.Blue, Core.pxRes1000km + 10, 11); + } + if(Width > Core.pxRes100km && Zoom > 2) + { + g.DrawRectangle(ScalePen, 10, 10, Core.pxRes100km, 10); + g.DrawString("100Km", ScaleFont, Brushes.Blue, Core.pxRes100km + 10, 11); + } + if(Width > Core.pxRes10km && Zoom > 5) + { + g.DrawRectangle(ScalePen, 10, 10, Core.pxRes10km, 10); + g.DrawString("10Km", ScaleFont, Brushes.Blue, Core.pxRes10km + 10, 11); + } + if(Width > Core.pxRes1000m && Zoom >= 10) + { + g.DrawRectangle(ScalePen, 10, 10, Core.pxRes1000m, 10); + g.DrawString("1000m", ScaleFont, Brushes.Blue, Core.pxRes1000m + 10, 11); + } + if(Width > Core.pxRes100m && Zoom > 11) + { + g.DrawRectangle(ScalePen, 10, 10, Core.pxRes100m, 10); + g.DrawString("100m", ScaleFont, Brushes.Blue, Core.pxRes100m + 9, 11); + } + */ + // Darstellung ScaleInfo geändert 21.11.2012 + if((Core.pxRes5000km > 0) && (Width > Core.pxRes5000km)) + { + g.DrawLine(ScalePen, 10, 10, 10, 20); + g.DrawLine(ScalePen, 10, 10, Core.pxRes5000km + 10, 10); + g.DrawLine(ScalePen, Core.pxRes5000km + 10, 10, Core.pxRes5000km + 10, 20); + g.DrawString("5000Km", ScaleFont, Brushes.Black, Core.pxRes5000km - 40, 13); + } + if((Core.pxRes1000km > 0) && (Width > Core.pxRes1000km)) + { + g.DrawLine(ScalePen, 10, 10, 10, 20); + g.DrawLine(ScalePen, 10, 10, Core.pxRes1000km + 10, 10); + g.DrawLine(ScalePen, Core.pxRes1000km + 10, 10, Core.pxRes1000km + 10, 20); + g.DrawString("1000Km", ScaleFont, Brushes.Black, Core.pxRes1000km - 40, 13); + } + if((Core.pxRes100km > 0) && (Width > Core.pxRes100km) && (Zoom > 5)) + { + g.DrawLine(ScalePen, 10, 10, 10, 20); + g.DrawLine(ScalePen, 10, 10, Core.pxRes100km + 10, 10); + g.DrawLine(ScalePen, Core.pxRes100km + 10, 10, Core.pxRes100km + 10, 20); + g.DrawString("100Km", ScaleFont, Brushes.Black, Core.pxRes100km - 35, 13); + } + if((Core.pxRes10km > 0) && (Width > Core.pxRes10km) && (Zoom > 8)) + { + g.DrawLine(ScalePen, 10, 10, 10, 20); + g.DrawLine(ScalePen, 10, 10, Core.pxRes10km + 10, 10); + g.DrawLine(ScalePen, Core.pxRes10km + 10, 10, Core.pxRes10km + 10, 20); + g.DrawString("10Km", ScaleFont, Brushes.Black, Core.pxRes10km - 30, 13); + } + if((Core.pxRes1000m > 0) && (Width > Core.pxRes1000m) && (Zoom >= 12)) + { + g.DrawLine(ScalePen, 10, 10, 10, 20); + g.DrawLine(ScalePen, 10, 10, Core.pxRes1000m + 10, 10); + g.DrawLine(ScalePen, Core.pxRes1000m + 10, 10, Core.pxRes1000m + 10, 20); + g.DrawString("1km", ScaleFont, Brushes.Black, Core.pxRes1000m - 25, 13); + } + if((Core.pxRes100m > 0) && (Width > Core.pxRes100m) && (Zoom > 14) ) + { + g.DrawLine(ScalePen, 10, 10, 10, 20); + g.DrawLine(ScalePen, 10, 10, Core.pxRes100m+10, 10); + g.DrawLine(ScalePen, Core.pxRes100m + 10, 10, Core.pxRes100m + 10, 20); + g.DrawString("100m", ScaleFont, Brushes.Black, Core.pxRes100m -30, 13); + } + } +#endif + #endregion + } + +#if !PocketPC + + /// + /// shrinks map area, useful just for testing + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + [Browsable(false)] + public bool VirtualSizeEnabled + { + get + { + return Core.VirtualSizeEnabled; + } + set + { + Core.VirtualSizeEnabled = value; + } + } + + protected override void OnSizeChanged(EventArgs e) + { + base.OnSizeChanged(e); +#else + protected override void OnResize(EventArgs e) + { + base.OnResize(e); +#endif + if(Width == 0 || Height == 0) + { + Debug.WriteLine("minimized"); + return; + } + + if(Width == Core.Width && Height == Core.Height) + { + Debug.WriteLine("maximized"); + return; + } + +#if !PocketPC + if(!IsDesignerHosted && !DesignModeInConstruct) +#endif + { + if(ForceDoubleBuffer) + { + if(backBuffer != null) + { + backBuffer.Dispose(); + backBuffer = null; + } + if(gxOff != null) + { + gxOff.Dispose(); + gxOff = null; + } + + backBuffer = new Bitmap(Width, Height); + gxOff = Graphics.FromImage(backBuffer); + } + +#if !PocketPC + if(VirtualSizeEnabled) + { + Core.OnMapSizeChanged(Core.vWidth, Core.vHeight); + } + else +#endif + { + Core.OnMapSizeChanged(Width, Height); + } + //Core.currentRegion = new GRect(-50, -50, Core.Width + 50, Core.Height + 50); + + if(Visible && IsHandleCreated && Core.IsStarted) + { +#if !PocketPC + if(IsRotated) + { + UpdateRotationMatrix(); + } +#endif + ForceUpdateOverlays(); + } + } + } + + bool isSelected = false; + protected override void OnMouseDown(MouseEventArgs e) + { + if(!IsMouseOverMarker) + { +#if !PocketPC + if(e.Button == DragButton && CanDragMap) +#else + if(CanDragMap) +#endif + { +#if !PocketPC + Core.mouseDown = ApplyRotationInversion(e.X, e.Y); +#else + Core.mouseDown = new GPoint(e.X, e.Y); +#endif + this.Invalidate(); + } + else if(!isSelected) + { + isSelected = true; + SelectedArea = RectLatLng.Empty; + selectionEnd = PointLatLng.Empty; + selectionStart = FromLocalToLatLng(e.X, e.Y); + } + } + + base.OnMouseDown(e); + } + + protected override void OnMouseUp(MouseEventArgs e) + { + base.OnMouseUp(e); + + if(isSelected) + { + isSelected = false; + } + + if(Core.IsDragging) + { + if(isDragging) + { + isDragging = false; + Debug.WriteLine("IsDragging = " + isDragging); +#if !PocketPC + this.Cursor = cursorBefore; + cursorBefore = null; +#endif + } + Core.EndDrag(); + + if(BoundsOfMap.HasValue && !BoundsOfMap.Value.Contains(Position)) + { + if(Core.LastLocationInBounds.HasValue) + { + Position = Core.LastLocationInBounds.Value; + } + } + } + else + { +#if !PocketPC + if(e.Button == DragButton) + { + Core.mouseDown = GPoint.Empty; + } + + if(!selectionEnd.IsEmpty && !selectionStart.IsEmpty) + { + bool zoomtofit = false; + + if(!SelectedArea.IsEmpty && Form.ModifierKeys == Keys.Shift) + { + zoomtofit = SetZoomToFitRect(SelectedArea); + } + + if(OnSelectionChange != null) + { + OnSelectionChange(SelectedArea, zoomtofit); + } + } + else + { + Invalidate(); + } +#endif + } + } + +#if !PocketPC + protected override void OnMouseClick(MouseEventArgs e) + { + if(!Core.IsDragging) + { + for(int i = Overlays.Count - 1; i >= 0; i--) + { + GMapOverlay o = Overlays[i]; + if(o != null && o.IsVisibile) + { + foreach(GMapMarker m in o.Markers) + { + if(m.IsVisible && m.IsHitTestVisible) + { + #region -- check -- + + if((MobileMode && m.LocalArea.Contains(e.X, e.Y)) || (!MobileMode && m.LocalAreaInControlSpace.Contains(e.X, e.Y))) + { + if(OnMarkerClick != null) + { + OnMarkerClick(m, e); + } + break; + } + + #endregion + } + } + + foreach(GMapRoute m in o.Routes) + { + if(m.IsVisible && m.IsHitTestVisible) + { + #region -- check -- + + GPoint rp = new GPoint(e.X, e.Y); +#if !PocketPC + if(!MobileMode) + { + rp.OffsetNegative(Core.renderOffset); + } +#endif + if(m.IsInside((int)rp.X, (int)rp.Y)) + { + if(OnRouteClick != null) + { + OnRouteClick(m, e); + } + break; + } + #endregion + } + } + + foreach(GMapPolygon m in o.Polygons) + { + if(m.IsVisible && m.IsHitTestVisible) + { + #region -- check -- + if(m.IsInside(FromLocalToLatLng(e.X, e.Y))) + { + if(OnPolygonClick != null) + { + OnPolygonClick(m, e); + } + break; + } + #endregion + } + } + } + } + } + + //m_mousepos = e.Location; + //if(HelperLineOption == HelperLineOptions.ShowAlways) + //{ + // base.Invalidate(); + //} + + base.OnMouseClick(e); + } +#endif +#if !PocketPC + /// + /// apply transformation if in rotation mode + /// + GPoint ApplyRotationInversion(int x, int y) + { + GPoint ret = new GPoint(x, y); + + if(IsRotated) + { + + System.Drawing.Point[] tt = new System.Drawing.Point[] { new System.Drawing.Point(x, y) }; + rotationMatrixInvert.TransformPoints(tt); + var f = tt[0]; + + ret.X = f.X; + ret.Y = f.Y; + } + + return ret; + } + + /// + /// apply transformation if in rotation mode + /// + GPoint ApplyRotation(int x, int y) + { + GPoint ret = new GPoint(x, y); + + if(IsRotated) + { + System.Drawing.Point[] tt = new System.Drawing.Point[] { new System.Drawing.Point(x, y) }; + rotationMatrix.TransformPoints(tt); + var f = tt[0]; + + ret.X = f.X; + ret.Y = f.Y; + } + + return ret; + } + + Cursor cursorBefore = Cursors.Default; +#endif + + /// + /// Gets the width and height of a rectangle centered on the point the mouse + /// button was pressed, within which a drag operation will not begin. + /// +#if !PocketPC + public Size DragSize = SystemInformation.DragSize; +#else + public Size DragSize = new Size(4, 4); +#endif + + protected override void OnMouseMove(MouseEventArgs e) + { + if(!Core.IsDragging && !Core.mouseDown.IsEmpty) + { +#if PocketPC + GPoint p = new GPoint(e.X, e.Y); +#else + GPoint p = ApplyRotationInversion(e.X, e.Y); +#endif + if(Math.Abs(p.X - Core.mouseDown.X) * 2 >= DragSize.Width || Math.Abs(p.Y - Core.mouseDown.Y) * 2 >= DragSize.Height) + { + Core.BeginDrag(Core.mouseDown); + } + } + + if(Core.IsDragging) + { + if(!isDragging) + { + isDragging = true; + Debug.WriteLine("IsDragging = " + isDragging); + +#if !PocketPC + cursorBefore = this.Cursor; + this.Cursor = Cursors.SizeAll; +#endif + } + + if(BoundsOfMap.HasValue && !BoundsOfMap.Value.Contains(Position)) + { + // ... + } + else + { +#if !PocketPC + Core.mouseCurrent = ApplyRotationInversion(e.X, e.Y); +#else + Core.mouseCurrent = new GPoint(e.X, e.Y); +#endif + Core.Drag(Core.mouseCurrent); + +#if !PocketPC + if(MobileMode) + { + ForceUpdateOverlays(); + } +#else + ForceUpdateOverlays(); +#endif + + base.Invalidate(); + } + } + else + { +#if !PocketPC + if(isSelected && !selectionStart.IsEmpty && (Form.ModifierKeys == Keys.Alt || Form.ModifierKeys == Keys.Shift || DisableAltForSelection)) + { + selectionEnd = FromLocalToLatLng(e.X, e.Y); + { + GMap.NET.PointLatLng p1 = selectionStart; + GMap.NET.PointLatLng p2 = selectionEnd; + + double x1 = Math.Min(p1.Lng, p2.Lng); + double y1 = Math.Max(p1.Lat, p2.Lat); + double x2 = Math.Max(p1.Lng, p2.Lng); + double y2 = Math.Min(p1.Lat, p2.Lat); + + SelectedArea = new RectLatLng(y1, x1, x2 - x1, y1 - y2); + } + } + else +#endif + if(Core.mouseDown.IsEmpty) + { + for(int i = Overlays.Count - 1; i >= 0; i--) + { + GMapOverlay o = Overlays[i]; + if(o != null && o.IsVisibile) + { + foreach(GMapMarker m in o.Markers) + { + if(m.IsVisible && m.IsHitTestVisible) + { + #region -- check -- +#if !PocketPC + if((MobileMode && m.LocalArea.Contains(e.X, e.Y)) || (!MobileMode && m.LocalAreaInControlSpace.Contains(e.X, e.Y))) +#else + if(m.LocalArea.Contains(e.X, e.Y)) +#endif + { + if(!m.IsMouseOver) + { +#if !PocketPC + SetCursorHandOnEnter(); +#endif + m.IsMouseOver = true; + IsMouseOverMarker = true; + + if(OnMarkerEnter != null) + { + OnMarkerEnter(m); + } + + Invalidate(); + } + } + else if(m.IsMouseOver) + { + m.IsMouseOver = false; + IsMouseOverMarker = false; +#if !PocketPC + RestoreCursorOnLeave(); +#endif + if(OnMarkerLeave != null) + { + OnMarkerLeave(m); + } + + Invalidate(); + } + #endregion + } + } + +#if !PocketPC + foreach(GMapRoute m in o.Routes) + { + if(m.IsVisible && m.IsHitTestVisible) + { + #region -- check -- + + GPoint rp = new GPoint(e.X, e.Y); +#if !PocketPC + if(!MobileMode) + { + rp.OffsetNegative(Core.renderOffset); + } +#endif + if(m.IsInside((int)rp.X, (int)rp.Y)) + { + if(!m.IsMouseOver) + { +#if !PocketPC + SetCursorHandOnEnter(); +#endif + m.IsMouseOver = true; + IsMouseOverRoute = true; + + if(OnRouteEnter != null) + { + OnRouteEnter(m); + } + + Invalidate(); + } + } + else + { + if(m.IsMouseOver) + { + m.IsMouseOver = false; + IsMouseOverRoute = false; +#if !PocketPC + RestoreCursorOnLeave(); +#endif + if(OnRouteLeave != null) + { + OnRouteLeave(m); + } + + Invalidate(); + } + } + #endregion + } + } +#endif + + foreach(GMapPolygon m in o.Polygons) + { + if(m.IsVisible && m.IsHitTestVisible) + { + #region -- check -- + if(m.IsInside(FromLocalToLatLng(e.X, e.Y))) + { + if(!m.IsMouseOver) + { +#if !PocketPC + SetCursorHandOnEnter(); +#endif + m.IsMouseOver = true; + IsMouseOverPolygon = true; + + if(OnPolygonEnter != null) + { + OnPolygonEnter(m); + } + + Invalidate(); + } + } + else + { + if(m.IsMouseOver) + { + m.IsMouseOver = false; + IsMouseOverPolygon = false; +#if !PocketPC + RestoreCursorOnLeave(); +#endif + if(OnPolygonLeave != null) + { + OnPolygonLeave(m); + } + + Invalidate(); + } + } + #endregion + } + } + } + } + } + + if(renderHelperLine) + { + base.Invalidate(); + } + } + + base.OnMouseMove(e); + } + +#if !PocketPC + + internal void RestoreCursorOnLeave() + { + if(overObjectCount == 0 && cursorBefore != null) + { + this.Cursor = this.cursorBefore; + cursorBefore = null; + } + } + + internal void SetCursorHandOnEnter() + { + if(overObjectCount == 0 && Cursor != Cursors.Hand) + { + cursorBefore = this.Cursor; + this.Cursor = Cursors.Hand; + } + } + + /// + /// prevents focusing map if mouse enters it's area + /// + public bool DisableFocusOnMouseEnter = false; + + protected override void OnMouseEnter(EventArgs e) + { + base.OnMouseEnter(e); + + if(!DisableFocusOnMouseEnter) + { + Focus(); + } + } + + /// + /// reverses MouseWheel zooming direction + /// + public bool InvertedMouseWheelZooming = false; + + /// + /// lets you zoom by MouseWheel even when pointer is in area of marker + /// + public bool IgnoreMarkerOnMouseWheel = false; + + protected override void OnMouseWheel(MouseEventArgs e) + { + base.OnMouseWheel(e); + + if((!IsMouseOverMarker || IgnoreMarkerOnMouseWheel) && !Core.IsDragging) + { + if(Core.mouseLastZoom.X != e.X && Core.mouseLastZoom.Y != e.Y) + { + if(MouseWheelZoomType == MouseWheelZoomType.MousePositionAndCenter) + { + Core.position = FromLocalToLatLng(e.X, e.Y); + } + else if(MouseWheelZoomType == MouseWheelZoomType.ViewCenter) + { + Core.position = FromLocalToLatLng((int)Width / 2, (int)Height / 2); + } + else if(MouseWheelZoomType == MouseWheelZoomType.MousePositionWithoutCenter) + { + Core.position = FromLocalToLatLng(e.X, e.Y); + } + + Core.mouseLastZoom.X = e.X; + Core.mouseLastZoom.Y = e.Y; + } + + // set mouse position to map center + if(MouseWheelZoomType != MouseWheelZoomType.MousePositionWithoutCenter) + { + if(!GMaps.Instance.IsRunningOnMono) + { + System.Drawing.Point p = PointToScreen(new System.Drawing.Point(Width / 2, Height / 2)); + Stuff.SetCursorPos((int)p.X, (int)p.Y); + } + } + + Core.MouseWheelZooming = true; + + if(e.Delta > 0) + { + if(!InvertedMouseWheelZooming) + { + Zoom++; + } + else + { + Zoom--; + } + } + else if(e.Delta < 0) + { + if(!InvertedMouseWheelZooming) + { + Zoom--; + } + else + { + Zoom++; + } + } + + Core.MouseWheelZooming = false; + } + } +#endif + #endregion + + #region IGControl Members + + /// + /// Call it to empty tile cache & reload tiles + /// + public void ReloadMap() + { + Core.ReloadMap(); + } + + /// + /// set current position using keywords + /// + /// + /// true if successfull + public GeoCoderStatusCode SetCurrentPositionByKeywords(string keys) + { + GeoCoderStatusCode status = GeoCoderStatusCode.Unknow; + + GeocodingProvider gp = MapProvider as GeocodingProvider; + if(gp == null) + { + gp = GMapProviders.OpenStreetMap as GeocodingProvider; + } + + if(gp != null) + { + var pt = gp.GetPoint(keys, out status); + if(status == GeoCoderStatusCode.G_GEO_SUCCESS && pt.HasValue) + { + Position = pt.Value; + } + } + + return status; + } + + /// + /// gets world coordinate from local control coordinate + /// + /// + /// + /// + public PointLatLng FromLocalToLatLng(int x, int y) + { +#if !PocketPC + if(MapRenderTransform.HasValue) + { + //var xx = (int)(Core.renderOffset.X + ((x - Core.renderOffset.X) / MapRenderTransform.Value)); + //var yy = (int)(Core.renderOffset.Y + ((y - Core.renderOffset.Y) / MapRenderTransform.Value)); + + //PointF center = new PointF(Core.Width / 2, Core.Height / 2); + + //Matrix m = new Matrix(); + //m.Translate(-Core.renderOffset.X, -Core.renderOffset.Y); + //m.Scale(MapRenderTransform.Value, MapRenderTransform.Value); + + //System.Drawing.Point[] tt = new System.Drawing.Point[] { new System.Drawing.Point(x, y) }; + //m.TransformPoints(tt); + //var z = tt[0]; + + // + + x = (int)(Core.renderOffset.X + ((x - Core.renderOffset.X) / MapRenderTransform.Value)); + y = (int)(Core.renderOffset.Y + ((y - Core.renderOffset.Y) / MapRenderTransform.Value)); + } + + if(IsRotated) + { + System.Drawing.Point[] tt = new System.Drawing.Point[] { new System.Drawing.Point(x, y) }; + rotationMatrixInvert.TransformPoints(tt); + var f = tt[0]; + + if(VirtualSizeEnabled) + { + f.X += (Width - Core.vWidth) / 2; + f.Y += (Height - Core.vHeight) / 2; + } + + x = f.X; + y = f.Y; + } +#endif + return Core.FromLocalToLatLng(x, y); + } + + /// + /// gets local coordinate from world coordinate + /// + /// + /// + public GPoint FromLatLngToLocal(PointLatLng point) + { + GPoint ret = Core.FromLatLngToLocal(point); + +#if !PocketPC + if(MapRenderTransform.HasValue) + { + ret.X = (int)(Core.renderOffset.X + ((Core.renderOffset.X - ret.X) * -MapRenderTransform.Value)); + ret.Y = (int)(Core.renderOffset.Y + ((Core.renderOffset.Y - ret.Y) * -MapRenderTransform.Value)); + } + + if(IsRotated) + { + System.Drawing.Point[] tt = new System.Drawing.Point[] { new System.Drawing.Point((int)ret.X, (int)ret.Y) }; + rotationMatrix.TransformPoints(tt); + var f = tt[0]; + + if(VirtualSizeEnabled) + { + f.X += (Width - Core.vWidth) / 2; + f.Y += (Height - Core.vHeight) / 2; + } + + ret.X = f.X; + ret.Y = f.Y; + } + +#endif + return ret; + } + +#if !PocketPC + + /// + /// shows map db export dialog + /// + /// + public bool ShowExportDialog() + { + using(FileDialog dlg = new SaveFileDialog()) + { + dlg.CheckPathExists = true; + dlg.CheckFileExists = false; + dlg.AddExtension = true; + dlg.DefaultExt = "gmdb"; + dlg.ValidateNames = true; + dlg.Title = "GMap.NET: Export map to db, if file exsist only new data will be added"; + dlg.FileName = "DataExp"; + dlg.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); + dlg.Filter = "GMap.NET DB files (*.gmdb)|*.gmdb"; + dlg.FilterIndex = 1; + dlg.RestoreDirectory = true; + + if(dlg.ShowDialog() == DialogResult.OK) + { + bool ok = GMaps.Instance.ExportToGMDB(dlg.FileName); + if(ok) + { + MessageBox.Show("Complete!", "GMap.NET", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + else + { + MessageBox.Show("Failed!", "GMap.NET", MessageBoxButtons.OK, MessageBoxIcon.Warning); + } + + return ok; + } + } + + return false; + } + + /// + /// shows map dbimport dialog + /// + /// + public bool ShowImportDialog() + { + using(FileDialog dlg = new OpenFileDialog()) + { + dlg.CheckPathExists = true; + dlg.CheckFileExists = false; + dlg.AddExtension = true; + dlg.DefaultExt = "gmdb"; + dlg.ValidateNames = true; + dlg.Title = "GMap.NET: Import to db, only new data will be added"; + dlg.FileName = "DataImport"; + dlg.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); + dlg.Filter = "GMap.NET DB files (*.gmdb)|*.gmdb"; + dlg.FilterIndex = 1; + dlg.RestoreDirectory = true; + + if(dlg.ShowDialog() == DialogResult.OK) + { + bool ok = GMaps.Instance.ImportFromGMDB(dlg.FileName); + if(ok) + { + MessageBox.Show("Complete!", "GMap.NET", MessageBoxButtons.OK, MessageBoxIcon.Information); + ReloadMap(); + } + else + { + MessageBox.Show("Failed!", "GMap.NET", MessageBoxButtons.OK, MessageBoxIcon.Warning); + } + + return ok; + } + } + + return false; + } +#endif + + [Category("GMap.NET"), DefaultValue(0)] + public double Zoom + { + get + { + return zoomReal; + } + set + { + if(zoomReal != value) + { + Debug.WriteLine("ZoomPropertyChanged: " + zoomReal + " -> " + value); + + if(value > MaxZoom) + { + zoomReal = MaxZoom; + } + else if(value < MinZoom) + { + zoomReal = MinZoom; + } + else + { + zoomReal = value; + } + + double remainder = value % 1; + if(remainder != 0) + { + float scaleValue = (float)Math.Pow(2d, remainder); + { +#if !PocketPC + MapRenderTransform = scaleValue; +#endif + } + + ZoomStep = Convert.ToInt32(value - remainder); + } + else + { +#if !PocketPC + MapRenderTransform = null; +#endif + ZoomStep = Convert.ToInt32(value); + zoomReal = ZoomStep; + } + + if(Core.IsStarted && !IsDragging) + { + ForceUpdateOverlays(); + } + } + } + } + + /// + /// map zoom level + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + [Browsable(false)] + internal int ZoomStep + { + get + { + return Core.Zoom; + } + set + { + if(value > MaxZoom) + { + Core.Zoom = MaxZoom; + } + else if(value < MinZoom) + { + Core.Zoom = MinZoom; + } + else + { + Core.Zoom = value; + } + } + } + + /// + /// current map center position + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + [Browsable(false)] + public PointLatLng Position + { + get + { + return Core.Position; + } + set + { + Core.Position = value; + + if(Core.IsStarted) + { + ForceUpdateOverlays(); + } + } + } + + /// + /// current position in pixel coordinates + /// + [Browsable(false)] + public GPoint PositionPixel + { + get + { + return Core.PositionPixel; + } + } + + /// + /// location of cache + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + [Browsable(false)] + public string CacheLocation + { + get + { +#if !DESIGN + return CacheLocator.Location; +#else + return string.Empty; +#endif + } + set + { +#if !DESIGN + CacheLocator.Location = value; +#endif + } + } + + bool isDragging = false; + + /// + /// is user dragging map + /// + [Browsable(false)] + public bool IsDragging + { + get + { + return isDragging; + } + } + + bool isMouseOverMarker; + internal int overObjectCount = 0; + + /// + /// is mouse over marker + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + [Browsable(false)] + public bool IsMouseOverMarker + { + get + { + return isMouseOverMarker; + } + internal set + { + isMouseOverMarker = value; + overObjectCount += value ? 1 : -1; + } + } + + bool isMouseOverRoute; + + /// + /// is mouse over route + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + [Browsable(false)] + public bool IsMouseOverRoute + { + get + { + return isMouseOverRoute; + } + internal set + { + isMouseOverRoute = value; + overObjectCount += value ? 1 : -1; + } + } + + bool isMouseOverPolygon; + + /// + /// is mouse over polygon + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + [Browsable(false)] + public bool IsMouseOverPolygon + { + get + { + return isMouseOverPolygon; + } + internal set + { + isMouseOverPolygon = value; + overObjectCount += value ? 1 : -1; + } + } + + /// + /// gets current map view top/left coordinate, width in Lng, height in Lat + /// + [Browsable(false)] + public RectLatLng ViewArea + { + get + { + return Core.ViewArea; + } + } + + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + [Browsable(false)] + public GMapProvider MapProvider + { + get + { + return Core.Provider; + } + set + { + if(Core.Provider == null || !Core.Provider.Equals(value)) + { + Debug.WriteLine("MapType: " + Core.Provider.Name + " -> " + value.Name); + + RectLatLng viewarea = SelectedArea; + if(viewarea != RectLatLng.Empty) + { + Position = new PointLatLng(viewarea.Lat - viewarea.HeightLat / 2, viewarea.Lng + viewarea.WidthLng / 2); + } + else + { + viewarea = ViewArea; + } + + Core.Provider = value; + + if(Core.IsStarted) + { + if(Core.zoomToArea) + { + // restore zoomrect as close as possible + if(viewarea != RectLatLng.Empty && viewarea != ViewArea) + { + int bestZoom = Core.GetMaxZoomToFitRect(viewarea); + if(bestZoom > 0 && Zoom != bestZoom) + { + Zoom = bestZoom; + } + } + } + else + { + ForceUpdateOverlays(); + } + } + } + } + } + + /// + /// is routes enabled + /// + [Category("GMap.NET")] + public bool RoutesEnabled + { + get + { + return Core.RoutesEnabled; + } + set + { + Core.RoutesEnabled = value; + } + } + + /// + /// is polygons enabled + /// + [Category("GMap.NET")] + public bool PolygonsEnabled + { + get + { + return Core.PolygonsEnabled; + } + set + { + Core.PolygonsEnabled = value; + } + } + + /// + /// is markers enabled + /// + [Category("GMap.NET")] + public bool MarkersEnabled + { + get + { + return Core.MarkersEnabled; + } + set + { + Core.MarkersEnabled = value; + } + } + + /// + /// can user drag map + /// + [Category("GMap.NET")] + public bool CanDragMap + { + get + { + return Core.CanDragMap; + } + set + { + Core.CanDragMap = value; + } + } + + /// + /// map render mode + /// + [Browsable(false)] + public RenderMode RenderMode + { + get + { + return Core.RenderMode; + } + internal set + { + Core.RenderMode = value; + } + } + + /// + /// gets map manager + /// + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + [Browsable(false)] + public GMaps Manager + { + get + { + return GMaps.Instance; + } + } + + #endregion + + #region IGControl event Members + + /// + /// occurs when current position is changed + /// + public event PositionChanged OnPositionChanged + { + add + { + Core.OnCurrentPositionChanged += value; + } + remove + { + Core.OnCurrentPositionChanged -= value; + } + } + + /// + /// occurs when tile set load is complete + /// + public event TileLoadComplete OnTileLoadComplete + { + add + { + Core.OnTileLoadComplete += value; + } + remove + { + Core.OnTileLoadComplete -= value; + } + } + + /// + /// occurs when tile set is starting to load + /// + public event TileLoadStart OnTileLoadStart + { + add + { + Core.OnTileLoadStart += value; + } + remove + { + Core.OnTileLoadStart -= value; + } + } + + /// + /// occurs on map drag + /// + public event MapDrag OnMapDrag + { + add + { + Core.OnMapDrag += value; + } + remove + { + Core.OnMapDrag -= value; + } + } + + /// + /// occurs on map zoom changed + /// + public event MapZoomChanged OnMapZoomChanged + { + add + { + Core.OnMapZoomChanged += value; + } + remove + { + Core.OnMapZoomChanged -= value; + } + } + + /// + /// occures on map type changed + /// + public event MapTypeChanged OnMapTypeChanged + { + add + { + Core.OnMapTypeChanged += value; + } + remove + { + Core.OnMapTypeChanged -= value; + } + } + + /// + /// occurs on empty tile displayed + /// + public event EmptyTileError OnEmptyTileError + { + add + { + Core.OnEmptyTileError += value; + } + remove + { + Core.OnEmptyTileError -= value; + } + } + + #endregion + +#if !PocketPC + #region Serialization + + static readonly BinaryFormatter BinaryFormatter = new BinaryFormatter(); + + /// + /// Serializes the overlays. + /// + /// The stream. + public void SerializeOverlays(Stream stream) + { + if(stream == null) + { + throw new ArgumentNullException("stream"); + } + + // Create an array from the overlays + GMapOverlay[] overlayArray = new GMapOverlay[this.Overlays.Count]; + this.Overlays.CopyTo(overlayArray, 0); + + // Serialize the overlays + BinaryFormatter.Serialize(stream, overlayArray); + } + + /// + /// De-serializes the overlays. + /// + /// The stream. + public void DeserializeOverlays(Stream stream) + { + if(stream == null) + { + throw new ArgumentNullException("stream"); + } + + // De-serialize the overlays + GMapOverlay[] overlayArray = BinaryFormatter.Deserialize(stream) as GMapOverlay[]; + + // Populate the collection of overlays. + foreach(GMapOverlay overlay in overlayArray) + { + overlay.Control = this; + this.Overlays.Add(overlay); + } + + this.ForceUpdateOverlays(); + } + + #endregion +#endif + } + +#if !PocketPC + public enum HelperLineOptions + { + DontShow = 0, + ShowAlways = 1, + ShowOnModifierKey = 2 + } + + public delegate void SelectionChange(RectLatLng Selection, bool ZoomToFit); +#endif +} diff --git a/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/GMapMarker.cs b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/GMapMarker.cs new file mode 100644 index 0000000..7b15d0a --- /dev/null +++ b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/GMapMarker.cs @@ -0,0 +1,366 @@ + +namespace GMap.NET.WindowsForms +{ + using System; + using System.Drawing; + using System.Runtime.Serialization; + using System.Windows.Forms; + using GMap.NET.WindowsForms.ToolTips; + + /// + /// GMap.NET marker + /// + [Serializable] +#if !PocketPC + public abstract class GMapMarker : ISerializable, IDisposable +#else + public class GMapMarker: IDisposable +#endif + { +#if PocketPC + static readonly System.Drawing.Imaging.ImageAttributes attr = new System.Drawing.Imaging.ImageAttributes(); + + static GMapMarker() + { + attr.SetColorKey(Color.White, Color.White); + } +#endif + GMapOverlay overlay; + public GMapOverlay Overlay + { + get + { + return overlay; + } + internal set + { + overlay = value; + } + } + + private PointLatLng position; + public PointLatLng Position + { + get + { + return position; + } + set + { + if(position != value) + { + position = value; + + if(IsVisible) + { + if(Overlay != null && Overlay.Control != null) + { + Overlay.Control.UpdateMarkerLocalPosition(this); + } + } + } + } + } + + public object Tag; + + Point offset; + public Point Offset + { + get + { + return offset; + } + set + { + offset = value; + } + } + + Rectangle area; + + /// + /// marker position in local coordinates, internal only, do not set it manualy + /// + public Point LocalPosition + { + get + { + return area.Location; + } + set + { + if(area.Location != value) + { + area.Location = value; + { + if(Overlay != null && Overlay.Control != null) + { + if(!Overlay.Control.HoldInvalidation) + { + Overlay.Control.Core.Refresh.Set(); + } + } + } + } + } + } + + /// + /// ToolTip position in local coordinates + /// + public Point ToolTipPosition + { + get + { + Point ret = area.Location; + ret.Offset(-Offset.X, -Offset.Y); + return ret; + } + } + + public Size Size + { + get + { + return area.Size; + } + set + { + area.Size = value; + } + } + + public Rectangle LocalArea + { + get + { + return area; + } + } + + internal Rectangle LocalAreaInControlSpace + { + get + { + Rectangle r = area; + if(Overlay != null && Overlay.Control != null) + { + r.Offset((int)Overlay.Control.Core.renderOffset.X, (int)overlay.Control.Core.renderOffset.Y); + } + return r; + } + } + + Font toolTipFont; + public Font ToolTipFont + { + get + { + return toolTipFont; + } + set + { + if (value == null) + { + toolTipFont = new Font(FontFamily.GenericSansSerif, 14, FontStyle.Bold, GraphicsUnit.Pixel); + } + else + toolTipFont = value; + } + } + + public GMapToolTip ToolTip; + + public MarkerTooltipMode ToolTipMode = MarkerTooltipMode.OnMouseOver; + + string toolTipText; + public string ToolTipText + { + get + { + return toolTipText; + } + + set + { + if(ToolTip == null) + { +#if !PocketPC + ToolTip = new GMapRoundedToolTip(this); +#else + ToolTip = new GMapToolTip(this); +#endif + } + toolTipText = value; + } + } + + private bool visible = true; + + /// + /// is marker visible + /// + public bool IsVisible + { + get + { + return visible; + } + set + { + if(value != visible) + { + visible = value; + + if(Overlay != null && Overlay.Control != null) + { + if(visible) + { + Overlay.Control.UpdateMarkerLocalPosition(this); + } + + { + if(!Overlay.Control.HoldInvalidation) + { + Overlay.Control.Core.Refresh.Set(); + } + } + } + } + } + } + + /// + /// if true, marker will be rendered even if it's outside current view + /// + public bool DisableRegionCheck = false; + + /// + /// can maker receive input + /// + public bool IsHitTestVisible = true; + + private bool isMouseOver = false; + + /// + /// is mouse over marker + /// + public bool IsMouseOver + { + get + { + return isMouseOver; + } + internal set + { + isMouseOver = value; + } + } + + public GMapMarker(PointLatLng pos) : + this(pos, null){} + + public GMapMarker(PointLatLng pos, Font font) + { + this.Position = pos; + this.ToolTipFont = font; + } + + public virtual void OnRender(Graphics g) + { + // + } + +#if PocketPC + protected void DrawImageUnscaled(Graphics g, Bitmap inBmp, int x, int y) + { + g.DrawImage(inBmp, new Rectangle(x, y, inBmp.Width, inBmp.Height), 0, 0, inBmp.Width, inBmp.Height, GraphicsUnit.Pixel, attr); + } +#endif + +#if !PocketPC + #region ISerializable Members + + /// + /// Populates a with the data needed to serialize the target object. + /// + /// The to populate with data. + /// The destination (see ) for this serialization. + /// + /// The caller does not have the required permission. + /// + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue("Position", this.Position); + info.AddValue("Tag", this.Tag); + info.AddValue("Offset", this.Offset); + info.AddValue("Area", this.area); + info.AddValue("ToolTip", this.ToolTip); + info.AddValue("ToolTipMode", this.ToolTipMode); + info.AddValue("ToolTipText", this.ToolTipText); + info.AddValue("Visible", this.IsVisible); + info.AddValue("DisableregionCheck", this.DisableRegionCheck); + info.AddValue("IsHitTestVisible", this.IsHitTestVisible); + } + + /// + /// Initializes a new instance of the class. + /// + /// The info. + /// The context. + protected GMapMarker(SerializationInfo info, StreamingContext context) + { + this.Position = Extensions.GetStruct(info, "Position", PointLatLng.Empty); + this.Tag = Extensions.GetValue(info, "Tag", null); + this.Offset = Extensions.GetStruct(info, "Offset", Point.Empty); + this.area = Extensions.GetStruct(info, "Area", Rectangle.Empty); + this.ToolTip = Extensions.GetValue(info, "ToolTip", null); + this.ToolTipMode = Extensions.GetStruct(info, "ToolTipMode", MarkerTooltipMode.OnMouseOver); + this.ToolTipText = info.GetString("ToolTipText"); + this.IsVisible = info.GetBoolean("Visible"); + this.DisableRegionCheck = info.GetBoolean("DisableregionCheck"); + this.IsHitTestVisible = info.GetBoolean("IsHitTestVisible"); + } + + #endregion +#endif + + #region IDisposable Members + + bool disposed = false; + + public virtual void Dispose() + { + if(!disposed) + { + disposed = true; + + Tag = null; + + if(ToolTip != null) + { + toolTipText = null; + ToolTip.Dispose(); + ToolTip = null; + } + } + } + + #endregion + } + + public delegate void MarkerClick(GMapMarker item, MouseEventArgs e); + public delegate void MarkerEnter(GMapMarker item); + public delegate void MarkerLeave(GMapMarker item); + + /// + /// modeof tooltip + /// + public enum MarkerTooltipMode + { + OnMouseOver, + Never, + Always, + } +} diff --git a/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/GMapOverlay.cs b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/GMapOverlay.cs new file mode 100644 index 0000000..94a10da --- /dev/null +++ b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/GMapOverlay.cs @@ -0,0 +1,440 @@ + +namespace GMap.NET.WindowsForms +{ + using System; + using System.Drawing; + using System.Runtime.Serialization; + using System.Windows.Forms; + using GMap.NET.ObjectModel; + + /// + /// GMap.NET overlay + /// + [Serializable] +#if !PocketPC + public class GMapOverlay : ISerializable, IDeserializationCallback, IDisposable +#else + public class GMapOverlay: IDisposable +#endif + { + bool isVisibile = true; + + /// + /// is overlay visible + /// + public bool IsVisibile + { + get + { + return isVisibile; + } + set + { + if(value != isVisibile) + { + isVisibile = value; + + if(Control != null) + { + if(isVisibile) + { + Control.HoldInvalidation = true; + ForceUpdate(); + Control.Refresh(); + } + else + { + if(!Control.HoldInvalidation) + { + Control.Invalidate(); + } + } + } + } + } + } + + /// + /// overlay Id + /// + public string Id; + + /// + /// list of markers, should be thread safe + /// + public readonly ObservableCollectionThreadSafe Markers = new ObservableCollectionThreadSafe(); + + /// + /// list of routes, should be thread safe + /// + public readonly ObservableCollectionThreadSafe Routes = new ObservableCollectionThreadSafe(); + + /// + /// list of polygons, should be thread safe + /// + public readonly ObservableCollectionThreadSafe Polygons = new ObservableCollectionThreadSafe(); + + GMapControl control; + public GMapControl Control + { + get + { + return control; + } + internal set + { + control = value; + } + } + + public GMapOverlay() + { + CreateEvents(); + } + + public GMapOverlay(string id) + { + Id = id; + CreateEvents(); + } + + void CreateEvents() + { + Markers.CollectionChanged += new NotifyCollectionChangedEventHandler(Markers_CollectionChanged); + Routes.CollectionChanged += new NotifyCollectionChangedEventHandler(Routes_CollectionChanged); + Polygons.CollectionChanged += new NotifyCollectionChangedEventHandler(Polygons_CollectionChanged); + } + + void ClearEvents() + { + Markers.CollectionChanged -= new NotifyCollectionChangedEventHandler(Markers_CollectionChanged); + Routes.CollectionChanged -= new NotifyCollectionChangedEventHandler(Routes_CollectionChanged); + Polygons.CollectionChanged -= new NotifyCollectionChangedEventHandler(Polygons_CollectionChanged); + } + + public void Clear() + { + Markers.Clear(); + Routes.Clear(); + Polygons.Clear(); + } + + void Polygons_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + if(e.NewItems != null) + { + foreach(GMapPolygon obj in e.NewItems) + { + if(obj != null) + { + obj.Overlay = this; + if(Control != null) + { + Control.UpdatePolygonLocalPosition(obj); + } + } + } + } + + if(Control != null) + { + if(e.Action == NotifyCollectionChangedAction.Remove || e.Action == NotifyCollectionChangedAction.Reset) + { + if(Control.IsMouseOverPolygon) + { + Control.IsMouseOverPolygon = false; +#if !PocketPC + Control.RestoreCursorOnLeave(); +#endif + } + } + + if(!Control.HoldInvalidation) + { + Control.Invalidate(); + } + } + } + + void Routes_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + if(e.NewItems != null) + { + foreach(GMapRoute obj in e.NewItems) + { + if(obj != null) + { + obj.Overlay = this; + if(Control != null) + { + Control.UpdateRouteLocalPosition(obj); + } + } + } + } + + if(Control != null) + { + if(e.Action == NotifyCollectionChangedAction.Remove || e.Action == NotifyCollectionChangedAction.Reset) + { + if(Control.IsMouseOverRoute) + { + Control.IsMouseOverRoute = false; +#if !PocketPC + Control.RestoreCursorOnLeave(); +#endif + } + } + + if(!Control.HoldInvalidation) + { + Control.Invalidate(); + } + } + } + + void Markers_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + if(e.NewItems != null) + { + foreach(GMapMarker obj in e.NewItems) + { + if(obj != null) + { + obj.Overlay = this; + if(Control != null) + { + Control.UpdateMarkerLocalPosition(obj); + } + } + } + } + + if(Control != null) + { + if(e.Action == NotifyCollectionChangedAction.Remove || e.Action == NotifyCollectionChangedAction.Reset) + { + if(Control.IsMouseOverMarker) + { + Control.IsMouseOverMarker = false; +#if !PocketPC + Control.RestoreCursorOnLeave(); +#endif + } + } + + if(!Control.HoldInvalidation) + { + Control.Invalidate(); + } + } + } + + /// + /// updates local positions of objects + /// + internal void ForceUpdate() + { + if(Control != null) + { + foreach(GMapMarker obj in Markers) + { + if(obj.IsVisible) + { + Control.UpdateMarkerLocalPosition(obj); + } + } + + foreach(GMapPolygon obj in Polygons) + { + if(obj.IsVisible) + { + Control.UpdatePolygonLocalPosition(obj); + } + } + + foreach(GMapRoute obj in Routes) + { + if(obj.IsVisible) + { + Control.UpdateRouteLocalPosition(obj); + } + } + } + } + + /// + /// renders objects/routes/polygons + /// + /// + public virtual void OnRender(Graphics g) + { + if(Control != null) + { + if(Control.RoutesEnabled) + { + foreach(GMapRoute r in Routes) + { + if(r.IsVisible) + { + r.OnRender(g); + } + } + } + + if(Control.PolygonsEnabled) + { + foreach(GMapPolygon r in Polygons) + { + if(r.IsVisible) + { + r.OnRender(g); + } + } + } + + if(Control.MarkersEnabled) + { + // markers + foreach(GMapMarker m in Markers) + { + //if(m.IsVisible && (m.DisableRegionCheck || Control.Core.currentRegion.Contains(m.LocalPosition.X, m.LocalPosition.Y))) + if(m.IsVisible || m.DisableRegionCheck) + { + m.OnRender(g); + } + } + + // tooltips above + foreach(GMapMarker m in Markers) + { + //if(m.ToolTip != null && m.IsVisible && Control.Core.currentRegion.Contains(m.LocalPosition.X, m.LocalPosition.Y)) + if(m.ToolTip != null && m.IsVisible) + { + if(!string.IsNullOrEmpty(m.ToolTipText) && (m.ToolTipMode == MarkerTooltipMode.Always || (m.ToolTipMode == MarkerTooltipMode.OnMouseOver && m.IsMouseOver))) + { + m.ToolTip.OnRender(g); + } + } + } + } + } + } + +#if !PocketPC + #region ISerializable Members + + /// + /// Populates a with the data needed to serialize the target object. + /// + /// The to populate with data. + /// The destination (see ) for this serialization. + /// + /// The caller does not have the required permission. + /// + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue("Id", this.Id); + info.AddValue("IsVisible", this.IsVisibile); + + GMapMarker[] markerArray = new GMapMarker[this.Markers.Count]; + this.Markers.CopyTo(markerArray, 0); + info.AddValue("Markers", markerArray); + + GMapRoute[] routeArray = new GMapRoute[this.Routes.Count]; + this.Routes.CopyTo(routeArray, 0); + info.AddValue("Routes", routeArray); + + GMapPolygon[] polygonArray = new GMapPolygon[this.Polygons.Count]; + this.Polygons.CopyTo(polygonArray, 0); + info.AddValue("Polygons", polygonArray); + } + + private GMapMarker[] deserializedMarkerArray; + private GMapRoute[] deserializedRouteArray; + private GMapPolygon[] deserializedPolygonArray; + + /// + /// Initializes a new instance of the class. + /// + /// The info. + /// The context. + protected GMapOverlay(SerializationInfo info, StreamingContext context) + { + this.Id = info.GetString("Id"); + this.IsVisibile = info.GetBoolean("IsVisible"); + + this.deserializedMarkerArray = Extensions.GetValue(info, "Markers", new GMapMarker[0]); + this.deserializedRouteArray = Extensions.GetValue(info, "Routes", new GMapRoute[0]); + this.deserializedPolygonArray = Extensions.GetValue(info, "Polygons", new GMapPolygon[0]); + } + + #endregion + + #region IDeserializationCallback Members + + /// + /// Runs when the entire object graph has been deserialized. + /// + /// The object that initiated the callback. The functionality for this parameter is not currently implemented. + public void OnDeserialization(object sender) + { + // Populate Markers + foreach(GMapMarker marker in deserializedMarkerArray) + { + marker.Overlay = this; + this.Markers.Add(marker); + } + + // Populate Routes + foreach(GMapRoute route in deserializedRouteArray) + { + route.Overlay = this; + this.Routes.Add(route); + } + + // Populate Polygons + foreach(GMapPolygon polygon in deserializedPolygonArray) + { + polygon.Overlay = this; + this.Polygons.Add(polygon); + } + } + + #endregion +#endif + + #region IDisposable Members + + bool disposed = false; + + public void Dispose() + { + if(!disposed) + { + disposed = true; + + ClearEvents(); + + foreach(var m in Markers) + { + m.Dispose(); + } + + foreach(var r in Routes) + { + r.Dispose(); + } + + foreach(var p in Polygons) + { + p.Dispose(); + } + + Clear(); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/GMapPolygon.cs b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/GMapPolygon.cs new file mode 100644 index 0000000..46adcb9 --- /dev/null +++ b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/GMapPolygon.cs @@ -0,0 +1,302 @@ + +namespace GMap.NET.WindowsForms +{ + using System.Collections.Generic; + using System.Drawing; + using System.Drawing.Drawing2D; + using System.Runtime.Serialization; + using GMap.NET; + using System.Windows.Forms; + using System; + + /// + /// GMap.NET polygon + /// + [System.Serializable] +#if !PocketPC + public class GMapPolygon : MapRoute, ISerializable, IDeserializationCallback, IDisposable +#else + public class GMapPolygon : MapRoute, IDisposable +#endif + { + private bool visible = true; + + /// + /// is polygon visible + /// + public bool IsVisible + { + get + { + return visible; + } + set + { + if(value != visible) + { + visible = value; + + if(Overlay != null && Overlay.Control != null) + { + if(visible) + { + Overlay.Control.UpdatePolygonLocalPosition(this); + } + + { + if(!Overlay.Control.HoldInvalidation) + { + Overlay.Control.Core.Refresh.Set(); + } + } + } + } + } + } + + /// + /// can receive input + /// + public bool IsHitTestVisible = false; + + private bool isMouseOver = false; + + /// + /// is mouse over + /// + public bool IsMouseOver + { + get + { + return isMouseOver; + } + internal set + { + isMouseOver = value; + } + } + + GMapOverlay overlay; + public GMapOverlay Overlay + { + get + { + return overlay; + } + internal set + { + overlay = value; + } + } + + public virtual void OnRender(Graphics g) + { +#if !PocketPC + if(IsVisible) + { + using(GraphicsPath rp = new GraphicsPath()) + { + for(int i = 0; i < LocalPoints.Count; i++) + { + GPoint p2 = LocalPoints[i]; + + if(i == 0) + { + rp.AddLine(p2.X, p2.Y, p2.X, p2.Y); + } + else + { + System.Drawing.PointF p = rp.GetLastPoint(); + rp.AddLine(p.X, p.Y, p2.X, p2.Y); + } + } + + if(rp.PointCount > 0) + { + rp.CloseFigure(); + + g.FillPath(Fill, rp); + + g.DrawPath(Stroke, rp); + } + } + } +#else + { + if(IsVisible) + { + Point[] pnts = new Point[LocalPoints.Count]; + for(int i = 0; i < LocalPoints.Count; i++) + { + Point p2 = new Point((int)LocalPoints[i].X, (int)LocalPoints[i].Y); + pnts[pnts.Length - 1 - i] = p2; + } + + if(pnts.Length > 0) + { + g.FillPolygon(Fill, pnts); + g.DrawPolygon(Stroke, pnts); + } + } + } +#endif + + } + + //public double Area + //{ + // get + // { + // return 0; + // } + //} + + /// + /// specifies how the outline is painted + /// + [NonSerialized] +#if !PocketPC + public Pen Stroke = new Pen(Color.FromArgb(155, Color.MidnightBlue)); +#else + public Pen Stroke = new Pen(Color.MidnightBlue); +#endif + + /// + /// background color + /// + [NonSerialized] +#if !PocketPC + public Brush Fill = new SolidBrush(Color.FromArgb(155, Color.AliceBlue)); +#else + public Brush Fill = new System.Drawing.SolidBrush(Color.AliceBlue); +#endif + + public readonly List LocalPoints = new List(); + + public GMapPolygon(List points, string name) + : base(points, name) + { + LocalPoints.Capacity = Points.Count; + +#if !PocketPC + Stroke.LineJoin = LineJoin.Round; +#endif + Stroke.Width = 5; + } + + /// + /// checks if point is inside the polygon, + /// info.: http://greatmaps.codeplex.com/discussions/279437#post700449 + /// + /// + /// + public bool IsInside(PointLatLng p) + { + int count = Points.Count; + + if(count < 3) + { + return false; + } + + bool result = false; + + for(int i = 0, j = count - 1; i < count; i++) + { + var p1 = Points[i]; + var p2 = Points[j]; + + if(p1.Lat < p.Lat && p2.Lat >= p.Lat || p2.Lat < p.Lat && p1.Lat >= p.Lat) + { + if(p1.Lng + (p.Lat - p1.Lat) / (p2.Lat - p1.Lat) * (p2.Lng - p1.Lng) < p.Lng) + { + result = !result; + } + } + j = i; + } + return result; + } + +#if !PocketPC + #region ISerializable Members + + /// + /// Populates a with the data needed to serialize the target object. + /// + /// The to populate with data. + /// The destination (see ) for this serialization. + /// + /// The caller does not have the required permission. + /// + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + //info.AddValue("Stroke", this.Stroke); + //info.AddValue("Fill", this.Fill); + info.AddValue("LocalPoints", this.LocalPoints.ToArray()); + info.AddValue("Visible", this.IsVisible); + } + + // Temp store for de-serialization. + private GPoint[] deserializedLocalPoints; + + /// + /// Initializes a new instance of the class. + /// + /// The info. + /// The context. + protected GMapPolygon(SerializationInfo info, StreamingContext context) + : base(info, context) + { + //this.Stroke = Extensions.GetValue(info, "Stroke", new Pen(Color.FromArgb(155, Color.MidnightBlue))); + //this.Fill = Extensions.GetValue(info, "Fill", new SolidBrush(Color.FromArgb(155, Color.AliceBlue))); + this.deserializedLocalPoints = Extensions.GetValue(info, "LocalPoints"); + this.IsVisible = Extensions.GetStruct(info, "Visible", true); + } + + #endregion + + #region IDeserializationCallback Members + + /// + /// Runs when the entire object graph has been de-serialized. + /// + /// The object that initiated the callback. The functionality for this parameter is not currently implemented. + public override void OnDeserialization(object sender) + { + base.OnDeserialization(sender); + + // Accounts for the de-serialization being breadth first rather than depth first. + LocalPoints.AddRange(deserializedLocalPoints); + LocalPoints.Capacity = Points.Count; + } + + #endregion +#endif + + #region IDisposable Members + + bool disposed = false; + + public virtual void Dispose() + { + if(!disposed) + { + disposed = true; + + Fill.Dispose(); + Stroke.Dispose(); + LocalPoints.Clear(); + + base.Clear(); + } + } + + #endregion + } + + public delegate void PolygonClick(GMapPolygon item, MouseEventArgs e); + public delegate void PolygonEnter(GMapPolygon item); + public delegate void PolygonLeave(GMapPolygon item); +} diff --git a/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/GMapRoute.cs b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/GMapRoute.cs new file mode 100644 index 0000000..46e08c4 --- /dev/null +++ b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/GMapRoute.cs @@ -0,0 +1,284 @@ + +namespace GMap.NET.WindowsForms +{ + using System; + using System.Collections.Generic; + using System.Drawing; + using System.Drawing.Drawing2D; + using System.Runtime.Serialization; + using System.Windows.Forms; + using GMap.NET; + + /// + /// GMap.NET route + /// + [Serializable] +#if !PocketPC + public class GMapRoute : MapRoute, ISerializable, IDeserializationCallback, IDisposable +#else + public class GMapRoute : MapRoute, IDisposable +#endif + { + GMapOverlay overlay; + public GMapOverlay Overlay + { + get + { + return overlay; + } + internal set + { + overlay = value; + } + } + + private bool visible = true; + + /// + /// is marker visible + /// + public bool IsVisible + { + get + { + return visible; + } + set + { + if (value != visible) + { + visible = value; + + if (Overlay != null && Overlay.Control != null) + { + if (visible) + { + Overlay.Control.UpdateRouteLocalPosition(this); + } + + { + if (!Overlay.Control.HoldInvalidation) + { + Overlay.Control.Core.Refresh.Set(); + } + } + } + } + } + } + + /// + /// can receive input + /// + public bool IsHitTestVisible = false; + + private bool isMouseOver = false; + + /// + /// is mouse over + /// + public bool IsMouseOver + { + get + { + return isMouseOver; + } + internal set + { + isMouseOver = value; + } + } + +#if !PocketPC + /// + /// Indicates whether the specified point is contained within this System.Drawing.Drawing2D.GraphicsPath + /// + /// + /// + /// + internal bool IsInside(int x, int y) + { + if (graphicsPath != null) + { + return graphicsPath.IsOutlineVisible(x, y, Stroke); + } + + return false; + } + + GraphicsPath graphicsPath; + internal void UpdateGraphicsPath() + { + if(graphicsPath == null) + { + graphicsPath = new GraphicsPath(); + } + else + { + graphicsPath.Reset(); + } + + { + for(int i = 0; i < LocalPoints.Count; i++) + { + GPoint p2 = LocalPoints[i]; + + if(i == 0) + { + graphicsPath.AddLine(p2.X, p2.Y, p2.X, p2.Y); + } + else + { + System.Drawing.PointF p = graphicsPath.GetLastPoint(); + graphicsPath.AddLine(p.X, p.Y, p2.X, p2.Y); + } + } + } + } +#endif + + public virtual void OnRender(Graphics g) + { +#if !PocketPC + if(IsVisible) + { + if(graphicsPath != null) + { + g.DrawPath(Stroke, graphicsPath); + } + } +#else + if (IsVisible) + { + Point[] pnts = new Point[LocalPoints.Count]; + for (int i = 0; i < LocalPoints.Count; i++) + { + Point p2 = new Point((int)LocalPoints[i].X, (int)LocalPoints[i].Y); + pnts[pnts.Length - 1 - i] = p2; + } + + if (pnts.Length > 0) + { + g.DrawLines(Stroke, pnts); + } + } +#endif + } + + /// + /// specifies how the outline is painted + /// + [NonSerialized] +#if !PocketPC + public Pen Stroke = new Pen(Color.FromArgb(144, Color.MidnightBlue)); +#else + public Pen Stroke = new Pen(Color.MidnightBlue); +#endif + + public readonly List LocalPoints = new List(); + + public GMapRoute(string name) + : base(name) + { +#if !PocketPC + Stroke.LineJoin = LineJoin.Round; +#endif + Stroke.Width = 5; + } + + public GMapRoute(IEnumerable points, string name) + : base(points, name) + { +#if !PocketPC + Stroke.LineJoin = LineJoin.Round; +#endif + Stroke.Width = 5; + } + +#if !PocketPC + #region ISerializable Members + + // Temp store for de-serialization. + private GPoint[] deserializedLocalPoints; + + /// + /// Populates a with the data needed to serialize the target object. + /// + /// The to populate with data. + /// The destination (see ) for this serialization. + /// + /// The caller does not have the required permission. + /// + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + //info.AddValue("Stroke", this.Stroke); + info.AddValue("Visible", this.IsVisible); + info.AddValue("LocalPoints", this.LocalPoints.ToArray()); + } + + /// + /// Initializes a new instance of the class. + /// + /// The info. + /// The context. + protected GMapRoute(SerializationInfo info, StreamingContext context) + : base(info, context) + { + //this.Stroke = Extensions.GetValue(info, "Stroke", new Pen(Color.FromArgb(144, Color.MidnightBlue))); + this.IsVisible = Extensions.GetStruct(info, "Visible", true); + this.deserializedLocalPoints = Extensions.GetValue(info, "LocalPoints"); + } + + #endregion + + + #region IDeserializationCallback Members + + /// + /// Runs when the entire object graph has been de-serialized. + /// + /// The object that initiated the callback. The functionality for this parameter is not currently implemented. + public override void OnDeserialization(object sender) + { + base.OnDeserialization(sender); + + // Accounts for the de-serialization being breadth first rather than depth first. + LocalPoints.AddRange(deserializedLocalPoints); + LocalPoints.Capacity = Points.Count; + } + + #endregion +#endif + + #region IDisposable Members + + bool disposed = false; + + public virtual void Dispose() + { + if (!disposed) + { + disposed = true; + + Stroke.Dispose(); + LocalPoints.Clear(); + +#if !PocketPC + if (graphicsPath != null) + { + graphicsPath.Dispose(); + graphicsPath = null; + } +#endif + base.Clear(); + } + } + + #endregion + } + + public delegate void RouteClick(GMapRoute item, MouseEventArgs e); + public delegate void RouteEnter(GMapRoute item); + public delegate void RouteLeave(GMapRoute item); +} diff --git a/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/GMapToolTip.cs b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/GMapToolTip.cs new file mode 100644 index 0000000..c32b1ea --- /dev/null +++ b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/GMapToolTip.cs @@ -0,0 +1,186 @@ + +namespace GMap.NET.WindowsForms +{ + using System; + using System.Drawing; + using System.Drawing.Drawing2D; + using System.Runtime.Serialization; + + /// + /// GMap.NET marker + /// + [Serializable] +#if !PocketPC + public class GMapToolTip : ISerializable, IDisposable +#else + public class GMapToolTip: IDisposable +#endif + { + GMapMarker marker; + public GMapMarker Marker + { + get + { + return marker; + } + internal set + { + marker = value; + } + } + + public Point Offset; + + /// + /// string format + /// + [NonSerialized] + public readonly StringFormat Format = new StringFormat(); + + /// + /// font + /// + [NonSerialized] +#if !PocketPC + public Font Font = new Font(FontFamily.GenericSansSerif, 14, FontStyle.Bold, GraphicsUnit.Pixel); +#else + public Font Font = new Font(FontFamily.GenericSansSerif, 6, FontStyle.Bold); +#endif + + /// + /// specifies how the outline is painted + /// + [NonSerialized] +#if !PocketPC + public Pen Stroke = new Pen(Color.FromArgb(140, Color.MidnightBlue)); +#else + public Pen Stroke = new Pen(Color.MidnightBlue); +#endif + + /// + /// background color + /// + [NonSerialized] +#if !PocketPC + public Brush Fill = new SolidBrush(Color.FromArgb(222, Color.AliceBlue)); +#else + public Brush Fill = new System.Drawing.SolidBrush(Color.AliceBlue); +#endif + + /// + /// text foreground + /// + [NonSerialized] + public Brush Foreground = new SolidBrush(Color.Navy); + + /// + /// text padding + /// + public Size TextPadding = new Size(10, 10); + + public GMapToolTip(GMapMarker marker) + { + this.Marker = marker; + this.Offset = new Point(14, -44); + + this.Stroke.Width = 2; + + this.Font = marker.ToolTipFont; + +#if !PocketPC + this.Stroke.LineJoin = LineJoin.Round; + this.Stroke.StartCap = LineCap.RoundAnchor; + this.Format.LineAlignment = StringAlignment.Center; +#endif + + this.Format.Alignment = StringAlignment.Center; + } + + public virtual void OnRender(Graphics g) + { + System.Drawing.Size st = g.MeasureString(Marker.ToolTipText, Font).ToSize(); + System.Drawing.Rectangle rect = new System.Drawing.Rectangle(Marker.ToolTipPosition.X, Marker.ToolTipPosition.Y - st.Height, st.Width + TextPadding.Width, st.Height + TextPadding.Height); + rect.Offset(Offset.X, Offset.Y); + + g.DrawLine(Stroke, Marker.ToolTipPosition.X, Marker.ToolTipPosition.Y, rect.X, rect.Y + rect.Height / 2); + + g.FillRectangle(Fill, rect); + g.DrawRectangle(Stroke, rect); + +#if PocketPC + rect.Offset(0, (rect.Height - st.Height) / 2); +#endif + + g.DrawString(Marker.ToolTipText, Font, Foreground, rect, Format); + } + +#if !PocketPC + #region ISerializable Members + + /// + /// Initializes a new instance of the class. + /// + /// The info. + /// The context. + protected GMapToolTip(SerializationInfo info, StreamingContext context) + { + //this.Foreground = Extensions.GetValue(info, "Foreground", new SolidBrush(Color.Navy)); + //this.Fill = Extensions.GetValue(info, "Fill", new SolidBrush(Color.FromArgb(222, Color.AliceBlue))); + //this.Font = Extensions.GetValue(info, "Font", new Font(FontFamily.GenericSansSerif, 14, FontStyle.Bold, GraphicsUnit.Pixel)); + //this.Format = Extensions.GetValue(info, "Format", new StringFormat()); + this.Offset = Extensions.GetStruct(info, "Offset", Point.Empty); + //this.Stroke = Extensions.GetValue(info, "Stroke", new Pen(Color.FromArgb(140, Color.MidnightBlue))); + this.TextPadding = Extensions.GetStruct(info, "TextPadding", new Size(10, 10)); + } + + /// + /// Populates a with the data needed to serialize the target object. + /// + /// The to populate with data. + /// The destination (see ) for this serialization. + /// + /// The caller does not have the required permission. + /// + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + //info.AddValue("Fill", this.Fill); + //info.AddValue("Foreground", this.Foreground); + //info.AddValue("Font", this.Font); + //info.AddValue("Format", this.Format); + info.AddValue("Offset", this.Offset); + //info.AddValue("Stroke", this.Stroke); + info.AddValue("TextPadding", this.TextPadding); + } + + #endregion +#endif + + #region IDisposable Members + + bool disposed = false; + + public void Dispose() + { + if(!disposed) + { + disposed = true; + + Format.Dispose(); + + Font.Dispose(); + Font = null; + + Stroke.Dispose(); + Stroke = null; + + Fill.Dispose(); + Fill = null; + + Foreground.Dispose(); + Foreground = null; + } + } + + #endregion + } +} diff --git a/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/Markers/GMarkerCross.cs b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/Markers/GMarkerCross.cs new file mode 100644 index 0000000..9d721b6 --- /dev/null +++ b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/Markers/GMarkerCross.cs @@ -0,0 +1,73 @@ + +namespace GMap.NET.WindowsForms.Markers +{ + using System.Drawing; + using System.Runtime.Serialization; + using System; + +#if !PocketPC + [Serializable] + public class GMarkerCross : GMapMarker, ISerializable +#else + public class GMarkerCross : GMapMarker +#endif + { + [NonSerialized] + public Pen Pen; + + public GMarkerCross(PointLatLng p) + : base(p) + { +#if !PocketPC + Pen = new Pen(Brushes.Red, 1); +#else + Pen = new Pen(Color.Red, 1); +#endif + IsHitTestVisible = false; + } + + public override void OnRender(Graphics g) + { + System.Drawing.Point p1 = new System.Drawing.Point(LocalPosition.X, LocalPosition.Y); + p1.Offset(0, -10); + System.Drawing.Point p2 = new System.Drawing.Point(LocalPosition.X, LocalPosition.Y); + p2.Offset(0, 10); + + System.Drawing.Point p3 = new System.Drawing.Point(LocalPosition.X, LocalPosition.Y); + p3.Offset(-10, 0); + System.Drawing.Point p4 = new System.Drawing.Point(LocalPosition.X, LocalPosition.Y); + p4.Offset(10, 0); + + g.DrawLine(Pen, p1.X, p1.Y, p2.X, p2.Y); + g.DrawLine(Pen, p3.X, p3.Y, p4.X, p4.Y); + } + + public override void Dispose() + { + if(Pen != null) + { + Pen.Dispose(); + Pen = null; + } + base.Dispose(); + } + +#if !PocketPC + + #region ISerializable Members + + void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) + { + base.GetObjectData(info, context); + } + + protected GMarkerCross(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + + #endregion + +#endif + } +} diff --git a/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/Markers/GMarkerGoogle.cs b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/Markers/GMarkerGoogle.cs new file mode 100644 index 0000000..a36a62f --- /dev/null +++ b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/Markers/GMarkerGoogle.cs @@ -0,0 +1,299 @@ + +namespace GMap.NET.WindowsForms.Markers +{ + using System.Drawing; + using System.Collections.Generic; + +#if !PocketPC + using System.Windows.Forms.Properties; + using System; + using System.Runtime.Serialization; +#else + using GMap.NET.WindowsMobile.Properties; +#endif + + public enum GMarkerGoogleType + { + none = 0, + arrow, + blue, + blue_small, + blue_dot, + blue_pushpin, + brown_small, + gray_small, + green, + green_small, + green_dot, + green_pushpin, + green_big_go, + yellow, + yellow_small, + yellow_dot, + yellow_big_pause, + yellow_pushpin, + lightblue, + lightblue_dot, + lightblue_pushpin, + orange, + orange_small, + orange_dot, + pink, + pink_dot, + pink_pushpin, + purple, + purple_small, + purple_dot, + purple_pushpin, + red, + red_small, + red_dot, + red_pushpin, + red_big_stop, + black_small, + white_small, + } + +#if !PocketPC + [Serializable] + public class GMarkerGoogle : GMapMarker, ISerializable, IDeserializationCallback +#else + public class GMarkerGoogle : GMapMarker +#endif + { + public float? Bearing; + Bitmap Bitmap; + Bitmap BitmapShadow; + + static Bitmap arrowshadow; + static Bitmap msmarker_shadow; + static Bitmap shadow_small; + static Bitmap pushpin_shadow; + + GMarkerGoogleType type; + + public GMarkerGoogle(PointLatLng p, GMarkerGoogleType type) : + this(p, null, type){} + + public GMarkerGoogle(PointLatLng p, Font font, GMarkerGoogleType type) + : base(p, font) + { + this.type = type; + + if(type != GMarkerGoogleType.none) + { + LoadBitmap(); + } + } + + void LoadBitmap() + { + Bitmap = GetIcon(type.ToString()); + Size = new System.Drawing.Size(Bitmap.Width, Bitmap.Height); + + switch(type) + { + case GMarkerGoogleType.arrow: + { + Offset = new Point(-11, -Size.Height); + + if(arrowshadow == null) + { + arrowshadow = Resources.arrowshadow; + } + BitmapShadow = arrowshadow; + } + break; + + case GMarkerGoogleType.blue: + case GMarkerGoogleType.blue_dot: + case GMarkerGoogleType.green: + case GMarkerGoogleType.green_dot: + case GMarkerGoogleType.yellow: + case GMarkerGoogleType.yellow_dot: + case GMarkerGoogleType.lightblue: + case GMarkerGoogleType.lightblue_dot: + case GMarkerGoogleType.orange: + case GMarkerGoogleType.orange_dot: + case GMarkerGoogleType.pink: + case GMarkerGoogleType.pink_dot: + case GMarkerGoogleType.purple: + case GMarkerGoogleType.purple_dot: + case GMarkerGoogleType.red: + case GMarkerGoogleType.red_dot: + { + Offset = new Point(-Size.Width / 2 + 1, -Size.Height + 1); + + if(msmarker_shadow == null) + { + msmarker_shadow = Resources.msmarker_shadow; + } + BitmapShadow = msmarker_shadow; + } + break; + + case GMarkerGoogleType.black_small: + case GMarkerGoogleType.blue_small: + case GMarkerGoogleType.brown_small: + case GMarkerGoogleType.gray_small: + case GMarkerGoogleType.green_small: + case GMarkerGoogleType.yellow_small: + case GMarkerGoogleType.orange_small: + case GMarkerGoogleType.purple_small: + case GMarkerGoogleType.red_small: + case GMarkerGoogleType.white_small: + { + Offset = new Point(-Size.Width / 2, -Size.Height + 1); + + if(shadow_small == null) + { + shadow_small = Resources.shadow_small; + } + BitmapShadow = shadow_small; + } + break; + + case GMarkerGoogleType.green_big_go: + case GMarkerGoogleType.yellow_big_pause: + case GMarkerGoogleType.red_big_stop: + { + Offset = new Point(-Size.Width / 2, -Size.Height + 1); + if(msmarker_shadow == null) + { + msmarker_shadow = Resources.msmarker_shadow; + } + BitmapShadow = msmarker_shadow; + } + break; + + case GMarkerGoogleType.blue_pushpin: + case GMarkerGoogleType.green_pushpin: + case GMarkerGoogleType.yellow_pushpin: + case GMarkerGoogleType.lightblue_pushpin: + case GMarkerGoogleType.pink_pushpin: + case GMarkerGoogleType.purple_pushpin: + case GMarkerGoogleType.red_pushpin: + { + Offset = new Point(-9, -Size.Height + 1); + + if(pushpin_shadow == null) + { + pushpin_shadow = Resources.pushpin_shadow; + } + BitmapShadow = pushpin_shadow; + } + break; + } + } + + /// + /// marker using manual bitmap, NonSerialized + /// + /// + /// + public GMarkerGoogle(PointLatLng p, Bitmap Bitmap) : + this(p, null, Bitmap) { } + + public GMarkerGoogle(PointLatLng p, Font font, Bitmap Bitmap) + : base(p, font) + { + this.Bitmap = Bitmap; + Size = new System.Drawing.Size(Bitmap.Width, Bitmap.Height); + Offset = new Point(-Size.Width / 2, -Size.Height/2); + } + + static readonly Dictionary iconCache = new Dictionary(); + + internal static Bitmap GetIcon(string name) + { + Bitmap ret; + if(!iconCache.TryGetValue(name, out ret)) + { + ret = Resources.ResourceManager.GetObject(name, Resources.Culture) as Bitmap; + iconCache.Add(name, ret); + } + return ret; + } + + static readonly Point[] Arrow = new Point[] { new Point(-7, 7), new Point(0, -22), new Point(7, 7), new Point(0, 2) }; + + public override void OnRender(Graphics g) + { +#if !PocketPC + //if(!Bearing.HasValue) + { + if(BitmapShadow != null) + { + g.DrawImage(BitmapShadow, LocalPosition.X, LocalPosition.Y, BitmapShadow.Width, BitmapShadow.Height); + } + } + + //if(Bearing.HasValue) + //{ + // g.RotateTransform(Bearing.Value - Overlay.Control.Bearing); + // g.FillPolygon(Brushes.Red, Arrow); + //} + + //if(!Bearing.HasValue) + { + g.DrawImage(Bitmap, LocalPosition.X, LocalPosition.Y, Size.Width, Size.Height); + } +#else + if(BitmapShadow != null) + { + DrawImageUnscaled(g, BitmapShadow, LocalPosition.X, LocalPosition.Y); + } + DrawImageUnscaled(g, Bitmap, LocalPosition.X, LocalPosition.Y); +#endif + } + + public override void Dispose() + { + if(Bitmap != null) + { + if(!iconCache.ContainsValue(Bitmap)) + { + Bitmap.Dispose(); + Bitmap = null; + } + } + + base.Dispose(); + } + +#if !PocketPC + + #region ISerializable Members + + void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue("type", this.type); + info.AddValue("Bearing", this.Bearing); + + base.GetObjectData(info, context); + } + + protected GMarkerGoogle(SerializationInfo info, StreamingContext context) + : base(info, context) + { + this.type = Extensions.GetStruct(info, "type", GMarkerGoogleType.none); + this.Bearing = Extensions.GetStruct(info, "Bearing", null); + } + + #endregion + + #region IDeserializationCallback Members + + public void OnDeserialization(object sender) + { + if(type != GMarkerGoogleType.none) + { + LoadBitmap(); + } + } + + #endregion + +#endif + } +} diff --git a/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/TilePrefetcher.Designer.cs b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/TilePrefetcher.Designer.cs new file mode 100644 index 0000000..7b7ff8d --- /dev/null +++ b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/TilePrefetcher.Designer.cs @@ -0,0 +1,124 @@ +namespace GMap.NET +{ + partial class TilePrefetcher + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if(disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label1 = new System.Windows.Forms.Label(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.progressBarDownload = new System.Windows.Forms.ProgressBar(); + this.label2 = new System.Windows.Forms.Label(); + this.tableLayoutPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Dock = System.Windows.Forms.DockStyle.Fill; + this.label1.Location = new System.Drawing.Point(4, 0); + this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(406, 17); + this.label1.TabIndex = 1; + this.label1.Text = "label1"; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.ColumnCount = 2; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 125F)); + this.tableLayoutPanel1.Controls.Add(this.progressBarDownload, 0, 1); + this.tableLayoutPanel1.Controls.Add(this.label1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.label2, 1, 1); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel1.Location = new System.Drawing.Point(5, 5); + this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(4); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 2; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel1.Size = new System.Drawing.Size(539, 62); + this.tableLayoutPanel1.TabIndex = 2; + // + // progressBarDownload + // + this.progressBarDownload.Dock = System.Windows.Forms.DockStyle.Fill; + this.progressBarDownload.Location = new System.Drawing.Point(4, 21); + this.progressBarDownload.Margin = new System.Windows.Forms.Padding(4); + this.progressBarDownload.Name = "progressBarDownload"; + this.progressBarDownload.Size = new System.Drawing.Size(406, 37); + this.progressBarDownload.Style = System.Windows.Forms.ProgressBarStyle.Continuous; + this.progressBarDownload.TabIndex = 3; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Dock = System.Windows.Forms.DockStyle.Fill; + this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 7.8F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label2.Location = new System.Drawing.Point(418, 17); + this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(117, 45); + this.label2.TabIndex = 2; + this.label2.Text = "please wait..."; + this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // TilePrefetcher + // + this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.BackColor = System.Drawing.Color.AliceBlue; + this.ClientSize = new System.Drawing.Size(549, 72); + this.ControlBox = false; + this.Controls.Add(this.tableLayoutPanel1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.KeyPreview = true; + this.Margin = new System.Windows.Forms.Padding(4); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "TilePrefetcher"; + this.Padding = new System.Windows.Forms.Padding(5); + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "GMap.NET - esc to cancel fetching"; + this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.Prefetch_FormClosed); + this.PreviewKeyDown += new System.Windows.Forms.PreviewKeyDownEventHandler(this.Prefetch_PreviewKeyDown); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.Label label1; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.ProgressBar progressBarDownload; + private System.Windows.Forms.Label label2; + } +} \ No newline at end of file diff --git a/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/TilePrefetcher.cs b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/TilePrefetcher.cs new file mode 100644 index 0000000..0dd8e93 --- /dev/null +++ b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/TilePrefetcher.cs @@ -0,0 +1,252 @@ + +namespace GMap.NET +{ + using System.Collections.Generic; + using System.ComponentModel; + using System.Windows.Forms; + using GMap.NET.Internals; + using System; + using GMap.NET.MapProviders; + using System.Threading; + + /// + /// form helping to prefetch tiles on local db + /// + public partial class TilePrefetcher : Form + { + BackgroundWorker worker = new BackgroundWorker(); + List list; + int zoom; + GMapProvider provider; + int sleep; + int all; + public bool ShowCompleteMessage = false; + RectLatLng area; + GMap.NET.GSize maxOfTiles; + + public TilePrefetcher() + { + InitializeComponent(); + + GMaps.Instance.OnTileCacheComplete += new TileCacheComplete(OnTileCacheComplete); + GMaps.Instance.OnTileCacheStart += new TileCacheStart(OnTileCacheStart); + GMaps.Instance.OnTileCacheProgress += new TileCacheProgress(OnTileCacheProgress); + + worker.WorkerReportsProgress = true; + worker.WorkerSupportsCancellation = true; + worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged); + worker.DoWork += new DoWorkEventHandler(worker_DoWork); + worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted); + } + + readonly AutoResetEvent done = new AutoResetEvent(true); + + void OnTileCacheComplete() + { + if(!IsDisposed) + { + done.Set(); + + MethodInvoker m = delegate + { + label2.Text = "all tiles saved"; + }; + Invoke(m); + } + } + + void OnTileCacheStart() + { + if(!IsDisposed) + { + done.Reset(); + + MethodInvoker m = delegate + { + label2.Text = "saving tiles..."; + }; + Invoke(m); + } + } + + void OnTileCacheProgress(int left) + { + if(!IsDisposed) + { + MethodInvoker m = delegate + { + label2.Text = left + " tile to save..."; + }; + Invoke(m); + } + } + + public void Start(RectLatLng area, int zoom, GMapProvider provider, int sleep) + { + if(!worker.IsBusy) + { + this.label1.Text = "..."; + this.progressBarDownload.Value = 0; + + this.area = area; + this.zoom = zoom; + this.provider = provider; + this.sleep = sleep; + + GMaps.Instance.UseMemoryCache = false; + GMaps.Instance.CacheOnIdleRead = false; + GMaps.Instance.BoostCacheEngine = true; + + worker.RunWorkerAsync(); + + this.ShowDialog(); + } + } + + public void Stop() + { + GMaps.Instance.OnTileCacheComplete -= new TileCacheComplete(OnTileCacheComplete); + GMaps.Instance.OnTileCacheStart -= new TileCacheStart(OnTileCacheStart); + GMaps.Instance.OnTileCacheProgress -= new TileCacheProgress(OnTileCacheProgress); + + done.Set(); + + if(worker.IsBusy) + { + worker.CancelAsync(); + } + + GMaps.Instance.CancelTileCaching(); + + done.Close(); + } + + void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + { + if(ShowCompleteMessage) + { + if(!e.Cancelled) + { + MessageBox.Show("Prefetch Complete! => " + ((int)e.Result).ToString() + " of " + all); + } + else + { + MessageBox.Show("Prefetch Canceled! => " + ((int)e.Result).ToString() + " of " + all); + } + } + + list.Clear(); + + GMaps.Instance.UseMemoryCache = true; + GMaps.Instance.CacheOnIdleRead = true; + GMaps.Instance.BoostCacheEngine = false; + + this.Close(); + } + + bool CacheTiles(int zoom, GPoint p) + { + foreach(var pr in provider.Overlays) + { + Exception ex; + PureImage img; + + // tile number inversion(BottomLeft -> TopLeft) + if(pr.InvertedAxisY) + { + img = GMaps.Instance.GetImageFrom(pr, new GPoint(p.X, maxOfTiles.Height - p.Y), zoom, out ex); + } + else // ok + { + img = GMaps.Instance.GetImageFrom(pr, p, zoom, out ex); + } + + if(img != null) + { + img.Dispose(); + img = null; + } + else + { + return false; + } + } + return true; + } + + void worker_DoWork(object sender, DoWorkEventArgs e) + { + if(list != null) + { + list.Clear(); + list = null; + } + list = provider.Projection.GetAreaTileList(area, zoom, 0); + maxOfTiles = provider.Projection.GetTileMatrixMaxXY(zoom); + all = list.Count; + + int countOk = 0; + int retry = 0; + + Stuff.Shuffle(list); + + for(int i = 0; i < all; i++) + { + if(worker.CancellationPending) + break; + + GPoint p = list[i]; + { + if(CacheTiles(zoom, p)) + { + countOk++; + retry = 0; + } + else + { + if(++retry <= 1) // retry only one + { + i--; + System.Threading.Thread.Sleep(1111); + continue; + } + else + { + retry = 0; + } + } + } + + worker.ReportProgress((int)((i + 1) * 100 / all), i + 1); + + System.Threading.Thread.Sleep(sleep); + } + + e.Result = countOk; + + if(!IsDisposed) + { + done.WaitOne(); + } + } + + void worker_ProgressChanged(object sender, ProgressChangedEventArgs e) + { + this.label1.Text = "Fetching tile at zoom (" + zoom + "): " + ((int)e.UserState).ToString() + " of " + all + ", complete: " + e.ProgressPercentage.ToString() + "%"; + this.progressBarDownload.Value = e.ProgressPercentage; + } + + private void Prefetch_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e) + { + if(e.KeyCode == Keys.Escape) + { + this.Close(); + } + } + + private void Prefetch_FormClosed(object sender, FormClosedEventArgs e) + { + this.Stop(); + } + } +} diff --git a/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/TilePrefetcher.resx b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/TilePrefetcher.resx new file mode 100644 index 0000000..19dc0dd --- /dev/null +++ b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/TilePrefetcher.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/ToolTips/GMapBaloonToolTip.cs b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/ToolTips/GMapBaloonToolTip.cs new file mode 100644 index 0000000..025c62e --- /dev/null +++ b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/ToolTips/GMapBaloonToolTip.cs @@ -0,0 +1,81 @@ + +namespace GMap.NET.WindowsForms.ToolTips +{ + using System.Drawing; + using System.Drawing.Drawing2D; + using System; + using System.Runtime.Serialization; + +#if !PocketPC + /// + /// GMap.NET marker + /// + [Serializable] + public class GMapBaloonToolTip : GMapToolTip, ISerializable + { + public float Radius = 10f; + + public GMapBaloonToolTip(GMapMarker marker) + : base(marker) + { + Stroke = new Pen(Color.FromArgb(140, Color.Navy)); + Stroke.Width = 3; +#if !PocketPC + this.Stroke.LineJoin = LineJoin.Round; + this.Stroke.StartCap = LineCap.RoundAnchor; +#endif + + Fill = Brushes.Yellow; + } + + public override void OnRender(Graphics g) + { + System.Drawing.Size st = g.MeasureString(Marker.ToolTipText, Font).ToSize(); + System.Drawing.Rectangle rect = new System.Drawing.Rectangle(Marker.ToolTipPosition.X, Marker.ToolTipPosition.Y - st.Height, st.Width + TextPadding.Width, st.Height + TextPadding.Height); + rect.Offset(Offset.X, Offset.Y); + + using(GraphicsPath objGP = new GraphicsPath()) + { + objGP.AddLine(rect.X + 2 * Radius, rect.Y + rect.Height, rect.X + Radius, rect.Y + rect.Height + Radius); + objGP.AddLine(rect.X + Radius, rect.Y + rect.Height + Radius, rect.X + Radius, rect.Y + rect.Height); + + objGP.AddArc(rect.X, rect.Y + rect.Height - (Radius * 2), Radius * 2, Radius * 2, 90, 90); + objGP.AddLine(rect.X, rect.Y + rect.Height - (Radius * 2), rect.X, rect.Y + Radius); + objGP.AddArc(rect.X, rect.Y, Radius * 2, Radius * 2, 180, 90); + objGP.AddLine(rect.X + Radius, rect.Y, rect.X + rect.Width - (Radius * 2), rect.Y); + objGP.AddArc(rect.X + rect.Width - (Radius * 2), rect.Y, Radius * 2, Radius * 2, 270, 90); + objGP.AddLine(rect.X + rect.Width, rect.Y + Radius, rect.X + rect.Width, rect.Y + rect.Height - (Radius * 2)); + objGP.AddArc(rect.X + rect.Width - (Radius * 2), rect.Y + rect.Height - (Radius * 2), Radius * 2, Radius * 2, 0, 90); // Corner + + objGP.CloseFigure(); + + g.FillPath(Fill, objGP); + g.DrawPath(Stroke, objGP); + } + +#if !PocketPC + g.DrawString(Marker.ToolTipText, Font, Foreground, rect, Format); +#else + g.DrawString(ToolTipText, ToolTipFont, TooltipForeground, rect, ToolTipFormat); +#endif + } + + #region ISerializable Members + + void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue("Radius", this.Radius); + + base.GetObjectData(info, context); + } + + protected GMapBaloonToolTip(SerializationInfo info, StreamingContext context) + : base(info, context) + { + this.Radius = Extensions.GetStruct(info, "Radius", 10f); + } + + #endregion + } +#endif +} diff --git a/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/ToolTips/GMapRoundedToolTip.cs b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/ToolTips/GMapRoundedToolTip.cs new file mode 100644 index 0000000..460de2b --- /dev/null +++ b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/ToolTips/GMapRoundedToolTip.cs @@ -0,0 +1,85 @@ + +namespace GMap.NET.WindowsForms.ToolTips +{ + using System.Drawing; + using System.Drawing.Drawing2D; + using System; + using System.Runtime.Serialization; + +#if !PocketPC + /// + /// GMap.NET marker + /// + [Serializable] + public class GMapRoundedToolTip : GMapToolTip, ISerializable + { + public float Radius = 10f; + + public GMapRoundedToolTip(GMapMarker marker) + : base(marker) + { + TextPadding = new Size((int)Radius, (int)Radius); + } + + public void DrawRoundRectangle(Graphics g, Pen pen, float h, float v, float width, float height, float radius) + { + using(GraphicsPath gp = new GraphicsPath()) + { + gp.AddLine(h + radius, v, h + width - (radius * 2), v); + gp.AddArc(h + width - (radius * 2), v, radius * 2, radius * 2, 270, 90); + gp.AddLine(h + width, v + radius, h + width, v + height - (radius * 2)); + gp.AddArc(h + width - (radius * 2), v + height - (radius * 2), radius * 2, radius * 2, 0, 90); // Corner + gp.AddLine(h + width - (radius * 2), v + height, h + radius, v + height); + gp.AddArc(h, v + height - (radius * 2), radius * 2, radius * 2, 90, 90); + gp.AddLine(h, v + height - (radius * 2), h, v + radius); + gp.AddArc(h, v, radius * 2, radius * 2, 180, 90); + + gp.CloseFigure(); + + g.FillPath(Fill, gp); + g.DrawPath(pen, gp); + } + } + + public override void OnRender(Graphics g) + { + System.Drawing.Size st = g.MeasureString(Marker.ToolTipText, Font).ToSize(); + + System.Drawing.Rectangle rect = new System.Drawing.Rectangle(Marker.ToolTipPosition.X, Marker.ToolTipPosition.Y - st.Height, st.Width + TextPadding.Width * 2, st.Height + TextPadding.Height); + rect.Offset(Offset.X, Offset.Y); + + g.DrawLine(Stroke, Marker.ToolTipPosition.X, Marker.ToolTipPosition.Y, rect.X + Radius / 2, rect.Y + rect.Height - Radius / 2); + + DrawRoundRectangle(g, Stroke, rect.X, rect.Y, rect.Width, rect.Height, Radius); + +#if !PocketPC + if(Format.Alignment == StringAlignment.Near) + { + rect.Offset(TextPadding.Width, 0); + } + g.DrawString(Marker.ToolTipText, Font, Foreground, rect, Format); +#else + g.DrawString(ToolTipText, ToolTipFont, TooltipForeground, rect, ToolTipFormat); +#endif + } + + + #region ISerializable Members + + void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue("Radius", this.Radius); + + base.GetObjectData(info, context); + } + + protected GMapRoundedToolTip(SerializationInfo info, StreamingContext context) + : base(info, context) + { + this.Radius = Extensions.GetStruct(info, "Radius", 10f); + } + + #endregion + } +#endif +} diff --git a/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/WindowsFormsImage.cs b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/WindowsFormsImage.cs new file mode 100644 index 0000000..6dad04f --- /dev/null +++ b/GreatMaps/GMap.NET.WindowsForms/GMap.NET.WindowsForms/WindowsFormsImage.cs @@ -0,0 +1,140 @@ + +namespace GMap.NET.WindowsForms +{ + using System.Drawing; + using System.IO; + using System.Drawing.Imaging; + using System; + using System.Diagnostics; + using GMap.NET.Internals; + + /// + /// image abstraction + /// + public class WindowsFormsImage : PureImage + { + public System.Drawing.Image Img; + + public override void Dispose() + { + if(Img != null) + { + Img.Dispose(); + Img = null; + } + + if(Data != null) + { + Data.Dispose(); + Data = null; + } + } + } + + /// + /// image abstraction proxy + /// + public class WindowsFormsImageProxy : PureImageProxy + { + WindowsFormsImageProxy() + { + + } + + public static readonly WindowsFormsImageProxy Instance = new WindowsFormsImageProxy(); + +#if !PocketPC + internal ColorMatrix ColorMatrix; +#endif + + static readonly bool Win7OrLater = Stuff.IsRunningOnWin7orLater(); + + public override PureImage FromStream(Stream stream) + { + WindowsFormsImage ret = null; + try + { +#if !PocketPC + Image m = Image.FromStream(stream, true, Win7OrLater ? false : true); +#else + Image m = new Bitmap(stream); +#endif + if(m != null) + { + ret = new WindowsFormsImage(); +#if !PocketPC + ret.Img = ColorMatrix != null ? ApplyColorMatrix(m, ColorMatrix) : m; +#else + ret.Img = m; +#endif + } + + } + catch(Exception ex) + { + ret = null; + Debug.WriteLine("FromStream: " + ex.ToString()); + } + + return ret; + } + + public override bool Save(Stream stream, GMap.NET.PureImage image) + { + WindowsFormsImage ret = image as WindowsFormsImage; + bool ok = true; + + if(ret.Img != null) + { + // try png + try + { + ret.Img.Save(stream, System.Drawing.Imaging.ImageFormat.Png); + } + catch + { + // try jpeg + try + { + stream.Seek(0, SeekOrigin.Begin); + ret.Img.Save(stream, System.Drawing.Imaging.ImageFormat.Jpeg); + } + catch + { + ok = false; + } + } + } + else + { + ok = false; + } + + return ok; + } + +#if !PocketPC + Bitmap ApplyColorMatrix(Image original, ColorMatrix matrix) + { + // create a blank bitmap the same size as original + Bitmap newBitmap = new Bitmap(original.Width, original.Height); + + using(original) // destroy original + { + // get a graphics object from the new image + using(Graphics g = Graphics.FromImage(newBitmap)) + { + // set the color matrix attribute + using(ImageAttributes attributes = new ImageAttributes()) + { + attributes.SetColorMatrix(matrix); + g.DrawImage(original, new Rectangle(0, 0, original.Width, original.Height), 0, 0, original.Width, original.Height, GraphicsUnit.Pixel, attributes); + } + } + } + + return newBitmap; + } +#endif + } +} diff --git a/GreatMaps/GMap.NET.WindowsForms/Properties/AssemblyInfo.cs b/GreatMaps/GMap.NET.WindowsForms/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..2fe1e90 --- /dev/null +++ b/GreatMaps/GMap.NET.WindowsForms/Properties/AssemblyInfo.cs @@ -0,0 +1,31 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +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("GMap.NET.WindowsForms")] +[assembly: AssemblyDescription("GMap.NET - Great Maps for Windows Forms")] +[assembly: AssemblyProduct("GMap.NET.WindowsForms")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("82e283ec-0096-4b55-baaf-89f671fa56d5")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// 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("1.0.0.0")] +//[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/GreatMaps/GMap.NET.WindowsForms/Properties/Resources.Designer.cs b/GreatMaps/GMap.NET.WindowsForms/Properties/Resources.Designer.cs new file mode 100644 index 0000000..9447b73 --- /dev/null +++ b/GreatMaps/GMap.NET.WindowsForms/Properties/Resources.Designer.cs @@ -0,0 +1,483 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.18052 +// +// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn +// der Code erneut generiert wird. +// +//------------------------------------------------------------------------------ + +namespace System.Windows.Forms.Properties { + using System; + + + /// + /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw. + /// + // Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert + // -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert. + // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen + // mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("System.Windows.Forms.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle + /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap arrow { + get { + object obj = ResourceManager.GetObject("arrow", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap arrowshadow { + get { + object obj = ResourceManager.GetObject("arrowshadow", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap black_small { + get { + object obj = ResourceManager.GetObject("black_small", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap blue { + get { + object obj = ResourceManager.GetObject("blue", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap blue_dot { + get { + object obj = ResourceManager.GetObject("blue_dot", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap blue_pushpin { + get { + object obj = ResourceManager.GetObject("blue_pushpin", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap blue_small { + get { + object obj = ResourceManager.GetObject("blue_small", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap brown_small { + get { + object obj = ResourceManager.GetObject("brown_small", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap drag_cross_67_16 { + get { + object obj = ResourceManager.GetObject("drag_cross_67_16", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap gray_small { + get { + object obj = ResourceManager.GetObject("gray_small", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap green { + get { + object obj = ResourceManager.GetObject("green", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap green_big_go { + get { + object obj = ResourceManager.GetObject("green_big_go", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap green_dot { + get { + object obj = ResourceManager.GetObject("green_dot", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap green_pushpin { + get { + object obj = ResourceManager.GetObject("green_pushpin", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap green_small { + get { + object obj = ResourceManager.GetObject("green_small", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap lightblue { + get { + object obj = ResourceManager.GetObject("lightblue", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap lightblue_dot { + get { + object obj = ResourceManager.GetObject("lightblue_dot", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap lightblue_pushpin { + get { + object obj = ResourceManager.GetObject("lightblue_pushpin", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap msmarker_shadow { + get { + object obj = ResourceManager.GetObject("msmarker_shadow", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap orange { + get { + object obj = ResourceManager.GetObject("orange", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap orange_dot { + get { + object obj = ResourceManager.GetObject("orange_dot", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap orange_small { + get { + object obj = ResourceManager.GetObject("orange_small", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap pink { + get { + object obj = ResourceManager.GetObject("pink", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap pink_dot { + get { + object obj = ResourceManager.GetObject("pink_dot", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap pink_pushpin { + get { + object obj = ResourceManager.GetObject("pink_pushpin", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap purple { + get { + object obj = ResourceManager.GetObject("purple", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap purple_dot { + get { + object obj = ResourceManager.GetObject("purple_dot", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap purple_pushpin { + get { + object obj = ResourceManager.GetObject("purple_pushpin", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap purple_small { + get { + object obj = ResourceManager.GetObject("purple_small", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap pushpin_shadow { + get { + object obj = ResourceManager.GetObject("pushpin_shadow", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap red { + get { + object obj = ResourceManager.GetObject("red", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap red_big_stop { + get { + object obj = ResourceManager.GetObject("red_big_stop", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap red_dot { + get { + object obj = ResourceManager.GetObject("red_dot", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap red_pushpin { + get { + object obj = ResourceManager.GetObject("red_pushpin", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap red_small { + get { + object obj = ResourceManager.GetObject("red_small", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap shadow_small { + get { + object obj = ResourceManager.GetObject("shadow_small", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap white_small { + get { + object obj = ResourceManager.GetObject("white_small", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap yellow { + get { + object obj = ResourceManager.GetObject("yellow", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap yellow_big_pause { + get { + object obj = ResourceManager.GetObject("yellow_big_pause", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap yellow_dot { + get { + object obj = ResourceManager.GetObject("yellow_dot", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap yellow_pushpin { + get { + object obj = ResourceManager.GetObject("yellow_pushpin", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap yellow_small { + get { + object obj = ResourceManager.GetObject("yellow_small", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/GreatMaps/GMap.NET.WindowsForms/Properties/Resources.resx b/GreatMaps/GMap.NET.WindowsForms/Properties/Resources.resx new file mode 100644 index 0000000..6754eee --- /dev/null +++ b/GreatMaps/GMap.NET.WindowsForms/Properties/Resources.resx @@ -0,0 +1,248 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\arrow.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\arrowshadow.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + + ..\Resources\black_small.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\blue.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\blue-dot.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\blue-pushpin.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\blue_small.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\brown_small.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\drag_cross_67_16.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\gray_small.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\green.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\go-big-green.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\green-dot.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\grn-pushpin.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\green_small.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\lightblue.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\ltblue-dot.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\ltblu-pushpin.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\msmarker.shadow.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\orange.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\orange-dot.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\orange_small.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\pink.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\pink-dot.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\pink-pushpin.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\purple.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\purple-dot.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\purple-pushpin.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\purple_small.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\pushpin_shadow.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\red.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\stop-big-red.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\red-dot.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\red-pushpin.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\red_small.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\shadow_small.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\white_small.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\yellow.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\pause-big-red.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\yellow-dot.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\ylw-pushpin.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\yellow_small.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/arrow.png b/GreatMaps/GMap.NET.WindowsForms/Resources/arrow.png new file mode 100644 index 0000000..8d34603 Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/arrow.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/arrowshadow.png b/GreatMaps/GMap.NET.WindowsForms/Resources/arrowshadow.png new file mode 100644 index 0000000..c30f691 Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/arrowshadow.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/black_small.png b/GreatMaps/GMap.NET.WindowsForms/Resources/black_small.png new file mode 100644 index 0000000..d9cb30f Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/black_small.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/blue-dot.png b/GreatMaps/GMap.NET.WindowsForms/Resources/blue-dot.png new file mode 100644 index 0000000..98b280d Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/blue-dot.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/blue-pushpin.png b/GreatMaps/GMap.NET.WindowsForms/Resources/blue-pushpin.png new file mode 100644 index 0000000..6e010b0 Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/blue-pushpin.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/blue.png b/GreatMaps/GMap.NET.WindowsForms/Resources/blue.png new file mode 100644 index 0000000..69590b9 Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/blue.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/blue_small.png b/GreatMaps/GMap.NET.WindowsForms/Resources/blue_small.png new file mode 100644 index 0000000..2ec9ae9 Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/blue_small.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/brown_small.png b/GreatMaps/GMap.NET.WindowsForms/Resources/brown_small.png new file mode 100644 index 0000000..322ec49 Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/brown_small.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/drag_cross_67_16.png b/GreatMaps/GMap.NET.WindowsForms/Resources/drag_cross_67_16.png new file mode 100644 index 0000000..a3094a9 Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/drag_cross_67_16.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/go-big-green.png b/GreatMaps/GMap.NET.WindowsForms/Resources/go-big-green.png new file mode 100644 index 0000000..e873154 Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/go-big-green.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/gray_small.png b/GreatMaps/GMap.NET.WindowsForms/Resources/gray_small.png new file mode 100644 index 0000000..0b00733 Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/gray_small.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/green-dot.png b/GreatMaps/GMap.NET.WindowsForms/Resources/green-dot.png new file mode 100644 index 0000000..c6e6836 Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/green-dot.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/green.png b/GreatMaps/GMap.NET.WindowsForms/Resources/green.png new file mode 100644 index 0000000..0f79315 Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/green.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/green_small.png b/GreatMaps/GMap.NET.WindowsForms/Resources/green_small.png new file mode 100644 index 0000000..462894b Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/green_small.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/grn-pushpin.png b/GreatMaps/GMap.NET.WindowsForms/Resources/grn-pushpin.png new file mode 100644 index 0000000..61d2972 Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/grn-pushpin.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/lightblue.png b/GreatMaps/GMap.NET.WindowsForms/Resources/lightblue.png new file mode 100644 index 0000000..03586d7 Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/lightblue.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/ltblu-pushpin.png b/GreatMaps/GMap.NET.WindowsForms/Resources/ltblu-pushpin.png new file mode 100644 index 0000000..64fb973 Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/ltblu-pushpin.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/ltblue-dot.png b/GreatMaps/GMap.NET.WindowsForms/Resources/ltblue-dot.png new file mode 100644 index 0000000..d21f0c5 Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/ltblue-dot.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/msmarker.shadow.png b/GreatMaps/GMap.NET.WindowsForms/Resources/msmarker.shadow.png new file mode 100644 index 0000000..f460f3f Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/msmarker.shadow.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/orange-dot.png b/GreatMaps/GMap.NET.WindowsForms/Resources/orange-dot.png new file mode 100644 index 0000000..db63b26 Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/orange-dot.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/orange.png b/GreatMaps/GMap.NET.WindowsForms/Resources/orange.png new file mode 100644 index 0000000..8bee9da Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/orange.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/orange_small.png b/GreatMaps/GMap.NET.WindowsForms/Resources/orange_small.png new file mode 100644 index 0000000..b875dec Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/orange_small.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/pause-big-red.png b/GreatMaps/GMap.NET.WindowsForms/Resources/pause-big-red.png new file mode 100644 index 0000000..097c957 Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/pause-big-red.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/pink-dot.png b/GreatMaps/GMap.NET.WindowsForms/Resources/pink-dot.png new file mode 100644 index 0000000..e946fe6 Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/pink-dot.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/pink-pushpin.png b/GreatMaps/GMap.NET.WindowsForms/Resources/pink-pushpin.png new file mode 100644 index 0000000..3064998 Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/pink-pushpin.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/pink.png b/GreatMaps/GMap.NET.WindowsForms/Resources/pink.png new file mode 100644 index 0000000..71a7cd6 Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/pink.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/purple-dot.png b/GreatMaps/GMap.NET.WindowsForms/Resources/purple-dot.png new file mode 100644 index 0000000..c0d40cb Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/purple-dot.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/purple-pushpin.png b/GreatMaps/GMap.NET.WindowsForms/Resources/purple-pushpin.png new file mode 100644 index 0000000..9fc54ac Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/purple-pushpin.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/purple.png b/GreatMaps/GMap.NET.WindowsForms/Resources/purple.png new file mode 100644 index 0000000..b578738 Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/purple.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/purple_small.png b/GreatMaps/GMap.NET.WindowsForms/Resources/purple_small.png new file mode 100644 index 0000000..72f6480 Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/purple_small.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/pushpin_shadow.png b/GreatMaps/GMap.NET.WindowsForms/Resources/pushpin_shadow.png new file mode 100644 index 0000000..162aa0f Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/pushpin_shadow.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/red-dot.png b/GreatMaps/GMap.NET.WindowsForms/Resources/red-dot.png new file mode 100644 index 0000000..b0f3f0e Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/red-dot.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/red-pushpin.png b/GreatMaps/GMap.NET.WindowsForms/Resources/red-pushpin.png new file mode 100644 index 0000000..203512d Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/red-pushpin.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/red.png b/GreatMaps/GMap.NET.WindowsForms/Resources/red.png new file mode 100644 index 0000000..e993751 Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/red.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/red_small.png b/GreatMaps/GMap.NET.WindowsForms/Resources/red_small.png new file mode 100644 index 0000000..f960799 Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/red_small.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/shadow_small.png b/GreatMaps/GMap.NET.WindowsForms/Resources/shadow_small.png new file mode 100644 index 0000000..3a89759 Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/shadow_small.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/stop-big-red.png b/GreatMaps/GMap.NET.WindowsForms/Resources/stop-big-red.png new file mode 100644 index 0000000..fe442a1 Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/stop-big-red.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/white_small.png b/GreatMaps/GMap.NET.WindowsForms/Resources/white_small.png new file mode 100644 index 0000000..cf2a8e9 Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/white_small.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/yellow-dot.png b/GreatMaps/GMap.NET.WindowsForms/Resources/yellow-dot.png new file mode 100644 index 0000000..79974d6 Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/yellow-dot.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/yellow.png b/GreatMaps/GMap.NET.WindowsForms/Resources/yellow.png new file mode 100644 index 0000000..a9d65ac Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/yellow.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/yellow_small.png b/GreatMaps/GMap.NET.WindowsForms/Resources/yellow_small.png new file mode 100644 index 0000000..26e8a9d Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/yellow_small.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/Resources/ylw-pushpin.png b/GreatMaps/GMap.NET.WindowsForms/Resources/ylw-pushpin.png new file mode 100644 index 0000000..c33c520 Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/Resources/ylw-pushpin.png differ diff --git a/GreatMaps/GMap.NET.WindowsForms/packages.config b/GreatMaps/GMap.NET.WindowsForms/packages.config new file mode 100644 index 0000000..0aee17e --- /dev/null +++ b/GreatMaps/GMap.NET.WindowsForms/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/GreatMaps/GMap.NET.WindowsForms/sn.snk b/GreatMaps/GMap.NET.WindowsForms/sn.snk new file mode 100644 index 0000000..5d3f356 Binary files /dev/null and b/GreatMaps/GMap.NET.WindowsForms/sn.snk differ diff --git a/GreatMaps/GMap.NET.sln b/GreatMaps/GMap.NET.sln new file mode 100644 index 0000000..b9748e7 --- /dev/null +++ b/GreatMaps/GMap.NET.sln @@ -0,0 +1,108 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual C# Express 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Demo.WindowsForms", "Demo.WindowsForms\Demo.WindowsForms.csproj", "{A2E07A76-8B2C-41A2-B23E-EA31AE94D706}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Demo.WindowsPresentation", "Demo.WindowsPresentation\Demo.WindowsPresentation.csproj", "{83195AEF-0071-471C-9E8B-E67211F5D028}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GMap.NET.Core", "GMap.NET.Core\GMap.NET.Core.csproj", "{D0C39D9D-BED0-418B-9A5E-713176CAF40C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GMap.NET.WindowsForms", "GMap.NET.WindowsForms\GMap.NET.WindowsForms.csproj", "{E06DEF77-F933-42FB-AFD7-DB2D0D8D6A98}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GMap.NET.WindowsPresentation", "GMap.NET.WindowsPresentation\GMap.NET.WindowsPresentation.csproj", "{644FE7D4-0184-400F-B2D7-99CB41360658}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TemplatedBinding", "Testing\TemplatedBinding\TemplatedBinding.csproj", "{C7CD6A76-D941-493F-91F8-6222AB87BECA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WPF-GMapControlNew", "Testing\WPF-GMapControlNew\WPF-GMapControlNew.csproj", "{B5A673B4-6286-4150-A536-1C16F3B8DC8B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Demo.StreetView", "Testing\Demo.StreetView\Demo.StreetView.csproj", "{B6E411A2-DFD8-461E-8207-BDBA405264CA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BigMapMaker", "Testing\BigMapMaker\BigMapMaker.csproj", "{A6E9D42C-935B-44D0-9FB0-E2E0319627D1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Demo.Clouds", "Testing\Demo.Clouds\Demo.Clouds.csproj", "{097FA134-51A5-4801-AADD-A1914EAA32FF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Demo.Docking", "Testing\Demo.Docking\Demo.Docking.csproj", "{F729FF99-2991-4819-9855-7CD7A1199089}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleApplication", "Testing\ConsoleApplication\ConsoleApplication.csproj", "{448B761F-C033-447D-93D5-F4380A7DB0E2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A2E07A76-8B2C-41A2-B23E-EA31AE94D706}.Debug|Any CPU.ActiveCfg = Debug|x86 + {A2E07A76-8B2C-41A2-B23E-EA31AE94D706}.Debug|Any CPU.Build.0 = Debug|x86 + {A2E07A76-8B2C-41A2-B23E-EA31AE94D706}.Debug|x86.ActiveCfg = Debug|x86 + {A2E07A76-8B2C-41A2-B23E-EA31AE94D706}.Debug|x86.Build.0 = Debug|x86 + {A2E07A76-8B2C-41A2-B23E-EA31AE94D706}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A2E07A76-8B2C-41A2-B23E-EA31AE94D706}.Release|Any CPU.Build.0 = Release|Any CPU + {A2E07A76-8B2C-41A2-B23E-EA31AE94D706}.Release|x86.ActiveCfg = Release|x86 + {A2E07A76-8B2C-41A2-B23E-EA31AE94D706}.Release|x86.Build.0 = Release|x86 + {83195AEF-0071-471C-9E8B-E67211F5D028}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {83195AEF-0071-471C-9E8B-E67211F5D028}.Debug|Any CPU.Build.0 = Debug|Any CPU + {83195AEF-0071-471C-9E8B-E67211F5D028}.Debug|x86.ActiveCfg = Debug|x86 + {83195AEF-0071-471C-9E8B-E67211F5D028}.Debug|x86.Build.0 = Debug|x86 + {83195AEF-0071-471C-9E8B-E67211F5D028}.Release|Any CPU.ActiveCfg = Release|Any CPU + {83195AEF-0071-471C-9E8B-E67211F5D028}.Release|Any CPU.Build.0 = Release|Any CPU + {83195AEF-0071-471C-9E8B-E67211F5D028}.Release|x86.ActiveCfg = Release|x86 + {83195AEF-0071-471C-9E8B-E67211F5D028}.Release|x86.Build.0 = Release|x86 + {D0C39D9D-BED0-418B-9A5E-713176CAF40C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D0C39D9D-BED0-418B-9A5E-713176CAF40C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D0C39D9D-BED0-418B-9A5E-713176CAF40C}.Debug|x86.ActiveCfg = Debug|x86 + {D0C39D9D-BED0-418B-9A5E-713176CAF40C}.Debug|x86.Build.0 = Debug|x86 + {D0C39D9D-BED0-418B-9A5E-713176CAF40C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D0C39D9D-BED0-418B-9A5E-713176CAF40C}.Release|Any CPU.Build.0 = Release|Any CPU + {D0C39D9D-BED0-418B-9A5E-713176CAF40C}.Release|x86.ActiveCfg = Release|x86 + {D0C39D9D-BED0-418B-9A5E-713176CAF40C}.Release|x86.Build.0 = Release|x86 + {E06DEF77-F933-42FB-AFD7-DB2D0D8D6A98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E06DEF77-F933-42FB-AFD7-DB2D0D8D6A98}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E06DEF77-F933-42FB-AFD7-DB2D0D8D6A98}.Debug|x86.ActiveCfg = Debug|x86 + {E06DEF77-F933-42FB-AFD7-DB2D0D8D6A98}.Debug|x86.Build.0 = Debug|x86 + {E06DEF77-F933-42FB-AFD7-DB2D0D8D6A98}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E06DEF77-F933-42FB-AFD7-DB2D0D8D6A98}.Release|Any CPU.Build.0 = Release|Any CPU + {E06DEF77-F933-42FB-AFD7-DB2D0D8D6A98}.Release|x86.ActiveCfg = Release|x86 + {E06DEF77-F933-42FB-AFD7-DB2D0D8D6A98}.Release|x86.Build.0 = Release|x86 + {644FE7D4-0184-400F-B2D7-99CB41360658}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {644FE7D4-0184-400F-B2D7-99CB41360658}.Debug|Any CPU.Build.0 = Debug|Any CPU + {644FE7D4-0184-400F-B2D7-99CB41360658}.Debug|x86.ActiveCfg = Debug|x86 + {644FE7D4-0184-400F-B2D7-99CB41360658}.Debug|x86.Build.0 = Debug|x86 + {644FE7D4-0184-400F-B2D7-99CB41360658}.Release|Any CPU.ActiveCfg = Release|Any CPU + {644FE7D4-0184-400F-B2D7-99CB41360658}.Release|Any CPU.Build.0 = Release|Any CPU + {644FE7D4-0184-400F-B2D7-99CB41360658}.Release|x86.ActiveCfg = Release|x86 + {644FE7D4-0184-400F-B2D7-99CB41360658}.Release|x86.Build.0 = Release|x86 + {C7CD6A76-D941-493F-91F8-6222AB87BECA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C7CD6A76-D941-493F-91F8-6222AB87BECA}.Debug|x86.ActiveCfg = Debug|x86 + {C7CD6A76-D941-493F-91F8-6222AB87BECA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C7CD6A76-D941-493F-91F8-6222AB87BECA}.Release|x86.ActiveCfg = Release|x86 + {B5A673B4-6286-4150-A536-1C16F3B8DC8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B5A673B4-6286-4150-A536-1C16F3B8DC8B}.Debug|x86.ActiveCfg = Debug|x86 + {B5A673B4-6286-4150-A536-1C16F3B8DC8B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B5A673B4-6286-4150-A536-1C16F3B8DC8B}.Release|x86.ActiveCfg = Release|x86 + {B6E411A2-DFD8-461E-8207-BDBA405264CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B6E411A2-DFD8-461E-8207-BDBA405264CA}.Debug|x86.ActiveCfg = Debug|x86 + {B6E411A2-DFD8-461E-8207-BDBA405264CA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B6E411A2-DFD8-461E-8207-BDBA405264CA}.Release|x86.ActiveCfg = Release|x86 + {A6E9D42C-935B-44D0-9FB0-E2E0319627D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A6E9D42C-935B-44D0-9FB0-E2E0319627D1}.Debug|x86.ActiveCfg = Debug|x86 + {A6E9D42C-935B-44D0-9FB0-E2E0319627D1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A6E9D42C-935B-44D0-9FB0-E2E0319627D1}.Release|x86.ActiveCfg = Release|x86 + {097FA134-51A5-4801-AADD-A1914EAA32FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {097FA134-51A5-4801-AADD-A1914EAA32FF}.Debug|x86.ActiveCfg = Debug|x86 + {097FA134-51A5-4801-AADD-A1914EAA32FF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {097FA134-51A5-4801-AADD-A1914EAA32FF}.Release|x86.ActiveCfg = Release|x86 + {F729FF99-2991-4819-9855-7CD7A1199089}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F729FF99-2991-4819-9855-7CD7A1199089}.Debug|x86.ActiveCfg = Debug|x86 + {F729FF99-2991-4819-9855-7CD7A1199089}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F729FF99-2991-4819-9855-7CD7A1199089}.Release|x86.ActiveCfg = Release|x86 + {448B761F-C033-447D-93D5-F4380A7DB0E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {448B761F-C033-447D-93D5-F4380A7DB0E2}.Debug|x86.ActiveCfg = Debug|x86 + {448B761F-C033-447D-93D5-F4380A7DB0E2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {448B761F-C033-447D-93D5-F4380A7DB0E2}.Release|x86.ActiveCfg = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/HorizonGenerator/App.config b/HorizonGenerator/App.config new file mode 100644 index 0000000..065f8b7 --- /dev/null +++ b/HorizonGenerator/App.config @@ -0,0 +1,22 @@ + + + + +
+ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/HorizonGenerator/HorizonGenerator.csproj b/HorizonGenerator/HorizonGenerator.csproj new file mode 100644 index 0000000..14575b2 --- /dev/null +++ b/HorizonGenerator/HorizonGenerator.csproj @@ -0,0 +1,164 @@ + + + + + Debug + AnyCPU + {ED1D9A86-30A9-4F0F-8FC8-E36B2EA01D0C} + WinExe + Properties + HorizonGenerator + HorizonGenerator + v4.0 + 512 + true + + + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + AnyCPU + true + full + false + bin\Debug\ + TRACE;DEBUG + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Newtonsoft.Json.12.0.1\lib\net40\Newtonsoft.Json.dll + True + + + + + + ..\packages\System.Data.SQLite.Core.1.0.110.0\lib\net40\System.Data.SQLite.dll + True + + + + + + + + + + + + + Form + + + MainDlg.cs + + + + + MainDlg.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + + {ee86e933-d883-4b18-80eb-0fba55ec67c6} + ScoutBase.Core + + + {009cabfd-726d-481f-972d-0a218e0ad9b9} + ScoutBase.Elevation + + + {610db007-5f74-4b5d-8b71-5e2c163a99b3} + ScoutBase.Propagation + + + {358e87d7-849f-412a-b487-f7b7d585a7f9} + ScoutBase.Stations + + + {6056d3be-7002-4a6a-a9ea-6ff45122a3c7} + SQLiteDatabase + + + + + False + Microsoft .NET Framework 4 %28x86 und x64%29 + true + + + False + .NET Framework 3.5 SP1 + false + + + False + Windows Installer 4.5 + true + + + + + + + Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}". + + + + + \ No newline at end of file diff --git a/HorizonGenerator/MainDlg.Designer.cs b/HorizonGenerator/MainDlg.Designer.cs new file mode 100644 index 0000000..dfd9a12 --- /dev/null +++ b/HorizonGenerator/MainDlg.Designer.cs @@ -0,0 +1,99 @@ +namespace HorizonGenerator +{ + partial class MainDlg + { + /// + /// Erforderliche Designervariable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Verwendete Ressourcen bereinigen. + /// + /// True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Vom Windows Form-Designer generierter Code + + /// + /// Erforderliche Methode für die Designerunterstützung. + /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden. + /// + private void InitializeComponent() + { + this.ss_Main = new System.Windows.Forms.StatusStrip(); + this.tsl_Status = new System.Windows.Forms.ToolStripStatusLabel(); + this.btn_Start = new System.Windows.Forms.Button(); + this.btn_Stop = new System.Windows.Forms.Button(); + this.ss_Main.SuspendLayout(); + this.SuspendLayout(); + // + // ss_Main + // + this.ss_Main.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.tsl_Status}); + this.ss_Main.Location = new System.Drawing.Point(0, 63); + this.ss_Main.Name = "ss_Main"; + this.ss_Main.Size = new System.Drawing.Size(617, 22); + this.ss_Main.TabIndex = 0; + this.ss_Main.Text = "statusStrip1"; + // + // tsl_Status + // + this.tsl_Status.Name = "tsl_Status"; + this.tsl_Status.Size = new System.Drawing.Size(39, 17); + this.tsl_Status.Text = "Status"; + // + // btn_Start + // + this.btn_Start.Location = new System.Drawing.Point(422, 22); + this.btn_Start.Name = "btn_Start"; + this.btn_Start.Size = new System.Drawing.Size(75, 23); + this.btn_Start.TabIndex = 1; + this.btn_Start.Text = "Start"; + this.btn_Start.UseVisualStyleBackColor = true; + this.btn_Start.Click += new System.EventHandler(this.btn_Start_Click); + // + // btn_Stop + // + this.btn_Stop.Location = new System.Drawing.Point(503, 22); + this.btn_Stop.Name = "btn_Stop"; + this.btn_Stop.Size = new System.Drawing.Size(75, 23); + this.btn_Stop.TabIndex = 2; + this.btn_Stop.Text = "Stop"; + this.btn_Stop.UseVisualStyleBackColor = true; + this.btn_Stop.Click += new System.EventHandler(this.btn_Stop_Click); + // + // MainDlg + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(617, 85); + this.Controls.Add(this.btn_Stop); + this.Controls.Add(this.btn_Start); + this.Controls.Add(this.ss_Main); + this.Name = "MainDlg"; + this.Text = "AirScout Horizon Generator"; + this.ss_Main.ResumeLayout(false); + this.ss_Main.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.StatusStrip ss_Main; + private System.Windows.Forms.ToolStripStatusLabel tsl_Status; + private System.Windows.Forms.Button btn_Start; + private System.Windows.Forms.Button btn_Stop; + } +} + diff --git a/HorizonGenerator/MainDlg.cs b/HorizonGenerator/MainDlg.cs new file mode 100644 index 0000000..2982309 --- /dev/null +++ b/HorizonGenerator/MainDlg.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Data.SQLite; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using System.IO; +using ScoutBase.Core; +using ScoutBase.Elevation; +using ScoutBase.Stations; +using ScoutBase.Propagation; +using System.Diagnostics; + +namespace HorizonGenerator +{ + public partial class MainDlg : Form + { + public MainDlg() + { + InitializeComponent(); + } + + private void Say(string text) + { + if (tsl_Status.Text != text) + { + tsl_Status.Text = text; + ss_Main.Refresh(); + } + } + + private void CalculateHorizons() + { + List lds = StationData.Database.LocationGetAll(); + foreach (LocationDesignator ld in lds) + { + Stopwatch st = new Stopwatch(); + try + { + // short max = ElevationModel.Database.ElevationTilesMaxElevation(ELEVATIONMODEL.SRTM1, Properties.Settings.Default.MinLat, Properties.Settings.Default.MinLon, Properties.Settings.Default.MaxLat, Properties.Settings.Default.MaxLon); + short max = 4797; + ELEVATIONMODEL model = ELEVATIONMODEL.SRTM1; + BAND band = BAND.B1_2G; + short antenna_height = 40; + short h1 = (short)(ElevationData.Database[ld.Lat, ld.Lon, model] + antenna_height); + double dist = 500.0; + double stepwidth = ElevationData.Database.GetDefaultStepWidth(model); + int count = (int)(dist / stepwidth / 1000.0); + HorizonPoint hp = new HorizonPoint(); + st.Start(); + List[] l = new List[360]; + for (int j = 0; j < 360; j++) + { + Say("Calculation horizon of " + ld.Call + ": " + j + " of 360..."); + l[j] = new List(); + ElevationPathDesignator path = new ElevationPathDesignator(); + path = ElevationData.Database.ElevationPathCreateFromBearing(this, ld.Lat, ld.Lon, j, dist, stepwidth, model); + short[] elv = path.Path; + // iterate through frequencies + foreach (int f in Enum.GetValues(typeof(BAND))) + { + double f1_clearance = 0.6; + if (f <= 144) + f1_clearance = 0.2; + else if (f <= 432) + f1_clearance = 0.4; + hp = PropagationHorizonDesignator.EpsilonMaxFromPath(h1, ref elv, dist, stepwidth, f / 1000.0, f1_clearance, max, LatLon.Earth.Radius * 1.33); + l[j].Add(hp); + } + hp = l[j].ElementAt(0); + Application.DoEvents(); + } + st.Stop(); + using (StreamWriter sw = new StreamWriter("Horizon.csv")) + { + sw.WriteLine("bearing[deg];eps_50M[deg];eps_70M[deg];eps[144M[deg];eps_432M[deg];eps_1.2G[deg];eps_2.3G[deg];eps_3.4G[deg];eps_5.7G[deg];eps_10G[deg];eps_24G[deg];eps_47G[deg];eps_76G[deg]"); + for (int j = 0; j < l.Length; j++) + { + sw.Write(j + ";"); + for (int k = 0; k < l[0].Count; k++) + { + sw.Write((l[j].ElementAt(k).Epsmin / Math.PI * 180.0).ToString("F8")); + if (k < l[0].Count - 1) + sw.Write(";"); + } + + sw.WriteLine(); + } + } + } + catch (Exception ex) + { + + } + } + } + + private void btn_Start_Click(object sender, EventArgs e) + { + CalculateHorizons(); + } + + private void btn_Stop_Click(object sender, EventArgs e) + { + this.Close(); + } + } +} diff --git a/HorizonGenerator/MainDlg.resx b/HorizonGenerator/MainDlg.resx new file mode 100644 index 0000000..05efec9 --- /dev/null +++ b/HorizonGenerator/MainDlg.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/HorizonGenerator/Program.cs b/HorizonGenerator/Program.cs new file mode 100644 index 0000000..9d7142d --- /dev/null +++ b/HorizonGenerator/Program.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace HorizonGenerator +{ + static class Program + { + /// + /// Der Haupteinstiegspunkt für die Anwendung. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new MainDlg()); + } + } +} diff --git a/HorizonGenerator/Properties/AssemblyInfo.cs b/HorizonGenerator/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..f576351 --- /dev/null +++ b/HorizonGenerator/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Allgemeine Informationen über eine Assembly werden über die folgenden +// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, +// die einer Assembly zugeordnet sind. +[assembly: AssemblyTitle("HorizonGenerator")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("HorizonGenerator")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar +// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von +// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen. +[assembly: ComVisible(false)] + +// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird +[assembly: Guid("ed1d9a86-30a9-4f0f-8fc8-e36b2ea01d0c")] + +// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: +// +// Hauptversion +// Nebenversion +// Buildnummer +// Revision +// +// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern +// übernehmen, indem Sie "*" eingeben: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/HorizonGenerator/Properties/Resources.Designer.cs b/HorizonGenerator/Properties/Resources.Designer.cs new file mode 100644 index 0000000..9baf427 --- /dev/null +++ b/HorizonGenerator/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.42000 +// +// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn +// der Code erneut generiert wird. +// +//------------------------------------------------------------------------------ + +namespace HorizonGenerator.Properties { + using System; + + + /// + /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw. + /// + // Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert + // -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert. + // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen + // mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("HorizonGenerator.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle + /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/HorizonGenerator/Properties/Resources.resx b/HorizonGenerator/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/HorizonGenerator/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/HorizonGenerator/Properties/Settings.Designer.cs b/HorizonGenerator/Properties/Settings.Designer.cs new file mode 100644 index 0000000..aadfc1c --- /dev/null +++ b/HorizonGenerator/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.42000 +// +// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn +// der Code erneut generiert wird. +// +//------------------------------------------------------------------------------ + +namespace HorizonGenerator.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/HorizonGenerator/Properties/Settings.settings b/HorizonGenerator/Properties/Settings.settings new file mode 100644 index 0000000..3964565 --- /dev/null +++ b/HorizonGenerator/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/HorizonGenerator/packages.config b/HorizonGenerator/packages.config new file mode 100644 index 0000000..0aee17e --- /dev/null +++ b/HorizonGenerator/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/LibADSB/ADSBDecoder.cs b/LibADSB/ADSBDecoder.cs new file mode 100644 index 0000000..84323b8 --- /dev/null +++ b/LibADSB/ADSBDecoder.cs @@ -0,0 +1,302 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.ComponentModel; + +namespace LibADSB +{ + // contains all relevant ADSB-Info + public class ADSBInfo + { + public DateTime Timestamp = DateTime.UtcNow; + public string ICAO24 = ""; + public string Call = ""; + public double Lat = double.NaN; + public double Lon = double.NaN; + public int Alt = int.MinValue; + public int Heading = int.MinValue; + public int Speed = int.MinValue; + public AirbornePositionMsg LastEvenAirborne = null; + public AirbornePositionMsg LastOddAirborne = null; + public DateTime LastEvenTimestamp = DateTime.MinValue; + public DateTime LastOddTimestamp = DateTime.MinValue; + public bool NICSupplementA = false; + + } + + public class ADSBDecoder : Object + { + [Browsable(false)] + [DescriptionAttribute("Count of objects currently in list")] + public int Count + { + get + { + return adsbinfos.Count; + } + } + + // time to live --> remove object after TTL is over + private int ttl; + + private DateTime lastsend = DateTime.UtcNow; + + private Dictionary adsbinfos; + + public ADSBDecoder() + : this(5) + { + } + + public ADSBDecoder(int TTL) + { + ttl = TTL; + adsbinfos = new Dictionary(); + } + + private string DecodeIdentificationMsg(ModeSReply msg, DateTime timestamp) + { + IdentificationMsg ident = (IdentificationMsg)msg; + if (msg.ICAO24 == null) + return "IdentifyMsg: No ICAO24 found."; + string icao24 = BitConverter.ToString(msg.ICAO24).Replace("-", String.Empty); + // check if ICAO is already stored in lookup table + ADSBInfo info = null; + if (!adsbinfos.TryGetValue(icao24, out info)) + { + // no --> add new entry + info = new ADSBInfo(); + info.ICAO24 = icao24; + adsbinfos.Add(icao24, info); + } + // add call sign + info.Call = ident.getIdentity(); + return "[" + info.ICAO24 + "] IdentificationMsg: Call=" + info.Call; + } + + private string DecodeAirbornePositionMsg(ModeSReply msg, DateTime timestamp) + { + // Airborne position message --> we need subsequent messages to decode + AirbornePositionMsg pos = (AirbornePositionMsg)msg; + if (msg.ICAO24 == null) + return "AirbornePositionMsg: No ICAO24 found."; + string icao24 = BitConverter.ToString(msg.ICAO24).Replace("-", String.Empty); + // check if ICAO is already stored in lookup table + ADSBInfo info = null; + if (!adsbinfos.TryGetValue(icao24, out info)) + { + // no --> add new entry + info = new ADSBInfo(); + info.ICAO24 = icao24; + adsbinfos.Add(icao24, info); + } + // adsbinfo found --> update information and calculate position + // contains valid position? + if (!pos.HasValidPosition) + { + // no --> return error meesage + return "[" + info.ICAO24 + "] AirbornePositionMsg: No valid position found."; + } + info.ICAO24 = icao24; + info.NICSupplementA = pos.NICSupplementA; + // position calculated before + if (!double.IsNaN(info.Lat) & !double.IsNaN(info.Lon)) + { + try + { + // use local CPR + double[] localpos = pos.getLocalPosition(info.Lat, info.Lon); + // we have a pos --> store in info and update timestamp + info.Lat = localpos[0]; + info.Lon = localpos[1]; + if (pos.HasValidAltitude) + info.Alt = pos.Altitude; + info.Timestamp = timestamp; + return "[" + info.ICAO24 + "] AirbornePositionMsg: Lat= " + info.Lat.ToString("F8") + ", Lon=" + info.Lon.ToString("F8") + ", Alt= " + info.Alt.ToString() + ", Time= " + info.Timestamp.ToString("HH:mm:ss.fff"); + } + catch (Exception ex) + { + } + } + // no position calculated before + if (pos.IsOddFormat) + { + try + { + // odd message + info.LastOddAirborne = pos; + info.LastOddTimestamp = DateTime.UtcNow; + // check if even message was received before and not older than 10secs--> calculate global CPR + if ((info.LastEvenAirborne != null) && ((info.LastOddTimestamp - info.LastOddTimestamp).TotalSeconds <= 10)) + { + try + { + double[] globalpos = pos.getGlobalPosition(info.LastEvenAirborne); + // we have a position --> store in info and update timestamp + info.Lat = globalpos[0]; + info.Lon = globalpos[1]; + if (pos.HasValidAltitude) + info.Alt = pos.Altitude; + // info.Timestamp = timestamp; + return "[" + info.ICAO24 + "] AirbornePositionMsg: Lat= " + info.Lat.ToString("F8") + ", Lon=" + info.Lon.ToString("F8") + ", Alt= " + info.Alt.ToString() + ", Time= " + info.Timestamp.ToString("HH:mm:ss.fff"); + } + catch + { + return "[" + info.ICAO24 + "] AirbornePositionMsg: Error while decoding position"; + } + } + return "[" + info.ICAO24 + "] AirbornePositionMsg: No decoding possible yet"; + } + catch (Exception ex) + { + } + } + // even message + info.LastEvenAirborne = pos; + info.LastEvenTimestamp = DateTime.UtcNow; + // check if odd message was received before and not older than 10secs --> calculate global CPR + if ((info.LastOddAirborne != null) && ((info.LastEvenTimestamp-info.LastOddTimestamp).TotalSeconds <= 10)) + { + try + { + double[] globalpos = pos.getGlobalPosition(info.LastOddAirborne); + // we have a position --> store in info and update timestamp + info.Lat = globalpos[0]; + info.Lon = globalpos[1]; + if (pos.HasValidAltitude) + info.Alt = pos.Altitude; +// info.Timestamp = timestamp; + return "[" + info.ICAO24 + "] AirbornePositionMsg: Lat= " + info.Lat.ToString("F8") + ", Lon=" + info.Lon.ToString("F8") + ", Alt= " + info.Alt.ToString() + ", Time= " +info.Timestamp.ToString("HH:mm:ss.fff"); + } + catch + { + return "[" + info.ICAO24 + "] AirbornePositionMsg: Error while decoding position"; + } + } + else + { + return "[" + info.ICAO24 + "] AirbornePositionMsg:No decoding possible yet"; + } + } + + private string DecodeAirspeedHeadingMsg(ModeSReply msg, DateTime timestamp) + { + AirspeedHeadingMsg heading = (AirspeedHeadingMsg)msg; + if (msg.ICAO24 == null) + return "AirspeedHeadingMsg: No ICAO24 found."; + string icao24 = BitConverter.ToString(msg.ICAO24).Replace("-", String.Empty); + // check if ICAO is already stored in lookup table + ADSBInfo info = null; + if (!adsbinfos.TryGetValue(icao24, out info)) + { + // no --> add new entry + info = new ADSBInfo(); + info.ICAO24 = icao24; + adsbinfos.Add(icao24, info); + } + // add information + if (heading.HasValidAirspeed) + info.Speed = heading.Airspeed; + if (heading.HasValidHeading) + info.Heading = heading.Heading; + return "[" + info.ICAO24 + "] AirspeedHeadingMsg: Speed=" + info.Speed.ToString() + ", Heading= " + info.Heading.ToString(); + } + + private string DecodeVelocityOverGroundMsg(ModeSReply msg, DateTime timestamp) + { + VelocityOverGroundMsg velocity = (VelocityOverGroundMsg)msg; + if (msg.ICAO24 == null) + return "VelocityOverGroundMsg: No ICAO24 found."; + string icao24 = BitConverter.ToString(msg.ICAO24).Replace("-", String.Empty); + // check if ICAO is already stored in lookup table + ADSBInfo info = null; + if (!adsbinfos.TryGetValue(icao24, out info)) + { + // no --> add new entry + info = new ADSBInfo(); + info.ICAO24 = icao24; + adsbinfos.Add(icao24, info); + } + // add information + if (velocity.HasValidVelocity) + info.Speed = velocity.Velocity; + if (velocity.HasValidHeading) + info.Heading = velocity.Heading; + return "[" + info.ICAO24 + "] VelocityOverGroundMsg: Speed=" + info.Speed.ToString() + ", Heading= " + info.Heading.ToString(); + } + + public string DecodeMessage(string raw_msg, DateTime timestamp) + { + // decode an ADS-B message and add information to list + // cut off first and last character + raw_msg = raw_msg.Substring(1, raw_msg.Length - 2); + // do generic decoding first + ModeSReply msg = LibADSB.Decoder.GenericDecoder(raw_msg); + if (!msg.CheckParity) + { + return ("Parity error, no decode."); + } + // parity is OK, let's start to sort the messages and calculate +// Console.WriteLine(msg.ToString()); + try + { + + if (msg.GetType() == typeof(IdentificationMsg)) + { + // Identification message; + return DecodeIdentificationMsg(msg, timestamp); + } + else if (msg.GetType() == typeof(AirbornePositionMsg)) + { + // Airborne position message + return DecodeAirbornePositionMsg(msg, timestamp); + } + else if (msg.GetType() == typeof(VelocityOverGroundMsg)) + { + // Velocity over ground message + return DecodeVelocityOverGroundMsg(msg, timestamp); + } + else if (msg.GetType() == typeof(AirspeedHeadingMsg)) + { + // Airspeed heading message + return DecodeAirspeedHeadingMsg(msg, timestamp); + } + } + catch (Exception ex) + { + string s = msg.GetType().ToString(); + return "Error while decoding " + s + ": " + ex.Message; + } + return ("Unknown message."); + } + + public ArrayList GetPlanes() + { + ArrayList list = new ArrayList(); + // return a list of ADSBInfos + foreach (KeyValuePair info in adsbinfos) + { + // check for old entries + if (info.Value.Timestamp > lastsend) + { + // check if entry is complete + if ((!String.IsNullOrEmpty(info.Value.ICAO24)) && + (!String.IsNullOrEmpty(info.Value.Call)) && + (info.Value.Lat != double.NaN) && + (info.Value.Lon != double.NaN) && + (info.Value.Alt != int.MinValue) && + (info.Value.Speed != int.MinValue) && + (info.Value.Heading != int.MinValue) + ) + { + list.Add(info.Value); + } + } + } + lastsend = DateTime.UtcNow; + return list; + } + } +} diff --git a/LibADSB/AirbornePositionMsg.cs b/LibADSB/AirbornePositionMsg.cs new file mode 100644 index 0000000..d39493b --- /dev/null +++ b/LibADSB/AirbornePositionMsg.cs @@ -0,0 +1,439 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.ComponentModel; + +namespace LibADSB +{ + public class AirbornePositionMsg : ExtendedSquitter + { + + [Browsable(false)] + [DescriptionAttribute("Position is availble in Airborne Position Message")] + public bool HasValidPosition + { + get + { + return horizontal_position_available; + } + } + + [Browsable(false)] + [DescriptionAttribute("Altitude is availble in Airborne Position Message")] + public bool HasValidAltitude + { + get + { + return altitude_available; + } + } + + [Browsable(false)] + [DescriptionAttribute("Altitude in Airborne Position Message [ft]")] + public int Altitude + { + get + { + bool Qbit = (altitude_encoded&0x10)!=0; + int N; + if (Qbit) { // altitude reported in 25ft increments + N = (altitude_encoded&0xF) | ((altitude_encoded&0xFE0)>>1); + return (int)((25*N-1000)+ 0.5); + } + else { // altitude is above 50175ft, so we use 100ft increments + + // it's decoded using the Gillham code + int C1 = (0x800&altitude_encoded)>>11; + int A1 = (0x400&altitude_encoded)>>10; + int C2 = (0x200&altitude_encoded)>>9; + int A2 = (0x100&altitude_encoded)>>8; + int C4 = (0x080&altitude_encoded)>>7; + int A4 = (0x040&altitude_encoded)>>6; + int B1 = (0x020&altitude_encoded)>>5; + int B2 = (0x008&altitude_encoded)>>3; + int D2 = (0x004&altitude_encoded)>>2; + int B4 = (0x002&altitude_encoded)>>1; + int D4 = (0x001&altitude_encoded); + + // this is standard gray code + int N500 = grayToBin(D2<<7|D4<<6|A1<<5|A2<<4|A4<<3|B1<<2|B2<<1|B4, 8); + + // 100-ft steps must be converted + int N100 = grayToBin(C1<<2|C2<<1|C4, 3)-1; + if (N100 == 6) N100=4; + if (N500%2 != 0) N100=4-N100; // invert it + + return (int)((-1200+N500*500+N100*100) + 0.5); + } + } + } + + [Browsable(false)] + [DescriptionAttribute("Surveillance status of Airborne Position Message")] + public byte SurveillanceStatus + { + get + { + return surveillance_status; + } + } + + [Browsable(false)] + [DescriptionAttribute("Synchronization status of Time of Applicability of Airborne Position Message")] + public bool IsUTCTime + { + get + { + return time_flag; + } + } + + [Browsable(false)] + [DescriptionAttribute("Indicates ODD format of Airborne Position Message")] + public bool IsOddFormat + { + get + { + return cpr_format; + } + } + + [Browsable(false)] + [DescriptionAttribute("Indicates barometric measuremnet of altitude of Airborne Position Message")] + public bool IsBarometricAltitude + { + get + { + return this.FormatTypeCode < 20; + } + } + + [Browsable(false)] + [DescriptionAttribute("CPR-encoded Latitude of Airborne Position Message")] + public int CPREncodedLat + { + get + { + return cpr_encoded_lat; + } + } + + [Browsable(false)] + [DescriptionAttribute("CPR-encoded Longitude of Airborne Position Message")] + public int CPREncodedLon + { + get + { + return cpr_encoded_lon; + } + } + + [Browsable(false)] + [DescriptionAttribute("Horizontal containment radius limit of Airborne Position Message")] + public double HorizontalContainmentRadiusLimit + { + get + { + switch (FormatTypeCode) + { + case 0: case 18: case 22: return -1; + case 9: case 20: return 7.5; + case 10: case 21: return 25; + case 11: + if (nic_suppl_a) return 75; + else return 185.2; + case 12: return 370.4; + case 13: + if (!nic_suppl_b) return 926; + else if (nic_suppl_a) return 1111.2; + else return 555.6; + case 14: return 1852; + case 15: return 3704; + case 16: + if (nic_suppl_a) return 7408; + else return 14816; + case 17: return 37040; + default: return 0; + } + } + } + + [Browsable(false)] + [DescriptionAttribute("")] + public byte NavigationIntegrityCategory + { + get + { + switch (FormatTypeCode) + { + case 0: case 18: case 22: return 0; + case 9: case 20: return 11; + case 10: case 21: return 10; + case 11: + if (nic_suppl_a) return 9; + else return 8; + case 12: return 7; + case 13: return 6; + case 14: return 5; + case 15: return 4; + case 16: + if (nic_suppl_a) return 3; + else return 2; + case 17: return 1; + default: return 0; + } + } + } + + [Browsable(false)] + [DescriptionAttribute("NIC supplement A of Airborne Position Message")] + public bool NICSupplementA + { + get + { + return nic_suppl_a; + } + set + { + nic_suppl_a = value; + } + } + + [Browsable(false)] + [DescriptionAttribute("NIC supplement B of Airborne Position Message")] + public bool NICSupplementB + { + get + { + return nic_suppl_b; + } + set + { + nic_suppl_b = value; + } + } + + private bool horizontal_position_available; + private bool altitude_available; + private byte surveillance_status; + private bool nic_suppl_b; + private short altitude_encoded; + private bool time_flag; + private bool cpr_format; + private int cpr_encoded_lat; + private int cpr_encoded_lon; + private bool nic_suppl_a; + + /** + * @param raw_message raw ADS-B airborne position message as hex string + * @throws BadFormatException if message has wrong format + */ + public AirbornePositionMsg(String raw_message) + : base(raw_message) + { + + if (!(FormatTypeCode == 0 || + (FormatTypeCode >= 9 && FormatTypeCode <= 18) || + (FormatTypeCode >= 20 && FormatTypeCode <= 22))) + throw new BadFormatException("This is not a position message! Wrong format type code ("+FormatTypeCode+"): " + raw_message); + + byte[] msg = Message; + + horizontal_position_available = FormatTypeCode != 0; + + surveillance_status = (byte) ((msg[0]>>1)&0x3); + nic_suppl_b = (msg[0]&0x1) == 1; + + altitude_encoded = (short) (((msg[1]<<4)|((msg[2]>>4)&0xF))&0xFFF); + altitude_available = altitude_encoded != 0; + + time_flag = ((msg[2]>>3)&0x1) == 1; + cpr_format = ((msg[2]>>2)&0x1) == 1; + cpr_encoded_lat = (((msg[2]&0x3)<<15) | ((msg[3]&0xFF)<<7) | ((msg[4]>>1)&0x7F)) & 0x1FFFF; + cpr_encoded_lon = (((msg[4]&0x1)<<16) | ((msg[5]&0xFF)<<8) | (msg[6]&0xFF)) & 0x1FFFF; + } + + /** + * This is a function of the surveillance status field in the position + * message. + * + * @return surveillance status description as defines in DO-260B + */ + public string getSurveillanceStatusDescription() + { + string[] desc = + { + "No condition information", + "Permanent alert (emergency condition)", + "Temporary alert (change in Mode A identity code oter than emergency condition)", + "SPI condition" + }; + + return desc[surveillance_status]; + } + + /** + * @param Rlat Even or odd Rlat value (CPR internal) + * @return the number of even longitude zones at a latitude + */ + private double NL(double Rlat) + { + if (Rlat == 0) return 59; + else if (Math.Abs(Rlat) == 87) return 2; + else if (Math.Abs(Rlat) > 87) return 1; + + double tmp = 1-(1-Math.Cos(Math.PI/(2.0*15.0)))/Math.Pow(Math.Cos(Math.PI/180.0*Math.Abs(Rlat)), 2); + return Math.Floor(2*Math.PI/Math.Acos(tmp)); + } + + /** + * Modulo operator in java has stupid behavior + */ + private static double mod(double a, double b) { + return ((a%b)+b)%b; + } + + /** + * This method can only be used if another position report with a different format (even/odd) is available + * and set with msg.setOtherFormatMsg(other). + * @param other airborne position message of the other format (even/odd). Note that the time between + * both messages should be not longer than 10 seconds! + * @return globally unambiguously decoded position tuple (latitude, longitude). The positional + * accuracy maintained by the Airborne CPR encoding will be approximately 5.1 meters. + * A message of the other format is needed for global decoding. + * @throws MissingInformationException if no position information is available in one of the messages + * @throws IllegalArgumentException if input message was emitted from a different transmitter + * @throws PositionStraddleError if position messages straddle latitude transition + * @throws BadFormatException other has the same format (even/odd) + */ + public double[] getGlobalPosition(AirbornePositionMsg other) + { + if (!other.ICAO24.SequenceEqual(ICAO24)) + throw new IllegalArgumentException( + string.Format("Transmitter of other message (%s) not equal to this (%s):", + BitConverter.ToString(other.ICAO24).Replace("-",String.Empty), BitConverter.ToString(ICAO24).Replace("-",String.Empty))); + + if (other.IsOddFormat == IsOddFormat) + throw new BadFormatException("Expected "+ (IsOddFormat? "even":"odd") + " message format:" + other.ToString()); + + if (!horizontal_position_available) + throw new MissingInformationException("No position information available!"); + if (!other.HasValidPosition) + throw new MissingInformationException("Other message has no position information."); + + AirbornePositionMsg even = IsOddFormat?other:this; + AirbornePositionMsg odd = IsOddFormat?this:other; + + // Helper for latitude single(Number of zones NZ is set to 15) + double Dlat0 = 360.0/60.0; + double Dlat1 = 360.0/59.0; + + // latitude index + double j = Math.Floor((59.0*even.CPREncodedLat-60.0*odd.CPREncodedLat)/((double)(1<<17))+0.5); + + // global latitudes + double Rlat0 = Dlat0 * (mod(j,60)+even.CPREncodedLat/((double)(1<<17))); + double Rlat1 = Dlat1 * (mod(j,59)+odd.CPREncodedLat/((double)(1<<17))); + + // Southern hemisphere? + if (Rlat0 >= 270 && Rlat0 <= 360) Rlat0 -= 360; + if (Rlat1 >= 270 && Rlat1 <= 360) Rlat1 -= 360; + + // Northern hemisphere? + if (Rlat0 <= -270 && Rlat0 >= -360) Rlat0 += 360; + if (Rlat1 <= -270 && Rlat1 >= -360) Rlat1 += 360; + + // ensure that the number of even longitude zones are equal + if (NL(Rlat0) != NL(Rlat1)) + throw new PositionStraddleException( + "The two given position straddle a transition latitude "+ + "and cannot be decoded. Wait for positions where they are equal."); + + // Helper for longitude + double Dlon0 = 360.0/Math.Max(1.0, NL(Rlat0)); + double Dlon1 = 360.0/Math.Max(1.0, NL(Rlat1)-1); + + // longitude index + double NL_helper = NL(IsOddFormat?Rlat1:Rlat0); // assuming that this is the newer message + double m = Math.Floor((even.CPREncodedLon*(NL_helper-1)-odd.CPREncodedLon*NL_helper)/((double)(1<<17))+0.5); + + // global longitude + double Rlon0 = Dlon0 * (mod(m,Math.Max(1.0, NL(Rlat0))) + even.CPREncodedLon/((double)(1<<17))); + double Rlon1 = Dlon1 * (mod(m,Math.Max(1.0, NL(Rlat1)-1)) + odd.CPREncodedLon/((double)(1<<17))); + + // correct longitude + if (Rlon0 < -180 && Rlon0 > -360) Rlon0 += 360; + if (Rlon1 < -180 && Rlon1 > -360) Rlon1 += 360; + if (Rlon0 > 180 && Rlon0 < 360) Rlon0 -= 360; + if (Rlon1 > 180 && Rlon1 < 360) Rlon1 -= 360; + + return new double[] {IsOddFormat?Rlat1:Rlat0, IsOddFormat?Rlon1:Rlon0}; + } + + /** + * This method uses a locally unambiguous decoding for airborne position messages. It + * uses a reference position known to be within 180NM (= 333.36km) of the true target + * airborne position. the reference point may be a previously tracked position that has + * been confirmed by global decoding (see getGlobalPosition()). + * @param ref_lat latitude of reference position + * ref_lon longitude of reference position + * @return decoded position as tuple (latitude, longitude). The positional + * accuracy maintained by the Airborne CPR encoding will be approximately 5.1 meters. + * @throws MissingInformationException if no position information is available + */ + public double[] getLocalPosition(double ref_lat, double ref_lon) + { + if (!horizontal_position_available) + throw new MissingInformationException("No position information available!"); + + // latitude zone size + double Dlat = IsOddFormat ? 360.0/59.0 : 360.0/60.0; + + // latitude zone index + double j = Math.Floor(ref_lat/Dlat) + Math.Floor(0.5+(mod(ref_lat, Dlat))/Dlat-CPREncodedLat/((double)(1<<17))); + + // decoded position latitude + double Rlat = Dlat*(j+CPREncodedLat/((double)(1<<17))); + + // longitude zone size + double Dlon = 360.0/Math.Max(1.0, NL(Rlat)-(IsOddFormat?1.0:0.0)); + + // longitude zone coordinate + double m = + Math.Floor(ref_lon/Dlon) + + Math.Floor(0.5+(mod(ref_lon,Dlon))/Dlon-(float)CPREncodedLon/((double)(1<<17))); + + // and finally the longitude + double Rlon = Dlon * (m + CPREncodedLon/((double)(1<<17))); + + // System.out.println("Loc: EncLon: "+CPREncodedLon+ + // " m: "+m+" Dlon: "+Dlon+ " Rlon2: "+Rlon2); + + return new double[] {Rlat,Rlon}; + } + + /** + * This method converts a gray code encoded int to a standard decimal int + * @param gray gray code encoded int of length bitlength + * bitlength bitlength of gray code + * @return radix 2 encoded integer + */ + private static int grayToBin(int gray, int bitlength) { + int result = 0; + for (int i = bitlength-1; i >= 0; --i) + result = result|((((0x1<<(i+1))&result)>>1)^((1<0; + ifr_capability = (msg[1]&0x40)>0; + navigation_accuracy_category = (byte) ((msg[1]>>3)&0x7); + + // check this later + vertical_rate_info_available = true; + geo_minus_baro_available = true; + + heading_available = (msg[1]&0x4)>0; + heading = (int)(((msg[1]&0x3)<<8 | msg[2]&0xFF) * 360.0/1024.0); + + true_airspeed = (msg[3]&0x80)>0; + airspeed = (short) (((msg[3]&0x7F)<<3 | msg[4]>>5&0x07)-1); + if (airspeed == -1) airspeed_available = false; + if (subtype == 2) airspeed<<=2; + + vertical_source = (msg[4]&0x10)>0; + vertical_rate_down = (msg[4]&0x08)>0; + vertical_rate = (short) ((((msg[4]&0x07)<<6 | msg[5]>>2&0x3F)-1)<<6); + if (vertical_rate == -1) vertical_rate_info_available = false; + + geo_minus_baro = (short) (((msg[6]&0x7F)-1)*25); + if (geo_minus_baro == -1) geo_minus_baro_available = false; + if ((msg[6]&0x80)>0) geo_minus_baro *= -1; + } + + /** + * Must be checked before accessing geo minus baro! + * + * @return whether geo-baro difference info is available + */ + public bool hasGeoMinusBaroInfo() { + return geo_minus_baro_available; + } + + /** + * @return If supersonic, velocity has only 4 kts accuracy, otherwise 1 kt + */ + public bool isSupersonic() { + return subtype == 4; + } + + /** + * @return true, if aircraft wants to change altitude for instance + */ + public bool hasChangeIntent() { + return intent_change; + } + + /** + * Note: only in ADS-B version 1 transponders!! + * @return true, iff aircraft has equipage class A1 or higher + */ + public bool hasIFRCapability() { + return ifr_capability; + } + + + /** + * @return NAC according to RTCA DO-260A + */ + public byte getNavigationAccuracyCategory() { + return navigation_accuracy_category; + } + + + /** + * @return difference between barometric and geometric altitude in m + * @throws MissingInformationException if no geo/baro difference info is available + */ + public double getGeoMinusBaro() + { + if (!geo_minus_baro_available) throw new MissingInformationException("No geo/baro difference info available!"); + return geo_minus_baro * 0.3048; + } + + public override string ToString() { + string ret = base.ToString()+"\n"+ + "Airspeed and heading:\n"; + try { ret += "\tAirspeed:\t"+Airspeed+" kts\n"; } + catch (Exception e) { ret += "\tAirspeed:\t\tnot available\n"; } + ret += "\tAirspeed Type:\t\t"+(IsTrueAirspeed ? "true" : "indicated")+"\n"; + try { ret += "\tHeading\t\t\t\t"+Heading+"\n"; } + catch (Exception e) { ret += "\tHeading\t\t\t\tnot available\n"; } + try { ret += "\tVertical rate:\t\t\t"+VerticalRate; } + catch (Exception e) { ret += "\tVertical rate:\t\t\tnot available"; } + + return ret; + } + } +} diff --git a/LibADSB/Decoder.cs b/LibADSB/Decoder.cs new file mode 100644 index 0000000..756d4d7 --- /dev/null +++ b/LibADSB/Decoder.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace LibADSB +{ + // public class for storing subsequent position messages + public class AircraftPositions + { + public AirbornePositionMsg last_even_airborne; + public AirbornePositionMsg last_odd_airborne; + public SurfacePositionMsg last_even_surface; + public SurfacePositionMsg last_odd_surface; + public double[] last_position; // lat lon + public bool supplA; + } + + /** + * General decoder for ADS-B messages + * @author Matthias Schäfer + */ + public class Decoder + { + + /** + * A top-down ADS-B decoder. Use instanceof to check for the message type. + * @param raw_message The Mode S message in hex representation + */ + public static ModeSReply GenericDecoder(String raw_message) + { + ModeSReply modes = new ModeSReply(raw_message); + // check parity; Note: some receivers set parity to 0 + if (modes.IsZeroParity() || modes.CheckParity) + { + // check if it is an ADS-B message + if (modes.DownlinkFormat == 17 || modes.DownlinkFormat == 18) + { + ExtendedSquitter es1090 = new ExtendedSquitter(raw_message); + + // what kind of extended squitter? + byte ftc = es1090.FormatTypeCode; + if (ftc >= 1 && ftc <= 4) // identification message + return new IdentificationMsg(raw_message); + if (ftc >= 5 && ftc <= 8) // surface position message + return new SurfacePositionMsg(raw_message); + if ((ftc >= 9 && ftc <= 18) || (ftc >= 20 && ftc <= 22)) // airborne position message + return new AirbornePositionMsg(raw_message); + if (ftc == 19) { // possible velocity message, check subtype + int subtype = es1090.Message[0]&0x7; + + if (subtype == 1 || subtype == 2) // velocity over ground + return new VelocityOverGroundMsg(raw_message); + else if (subtype == 3 || subtype == 4) // airspeed & heading + return new AirspeedHeadingMsg(raw_message); + } + /* + if (ftc == 28) { // aircraft status message, check subtype + int subtype = es1090.getMessage()[0]&0x7; + + if (subtype == 1) // emergency/priority status + return new EmergencyOrPriorityStatusMsg(raw_message); + if (subtype == 2) // TCAS resolution advisory report + return new TCASResolutionAdvisoryMsg(raw_message); + } + + if (ftc == 31) { // operational status message + int subtype = es1090.getMessage()[0]&0x7; + + if (subtype == 0 || subtype == 1) // airborne or surface? + return new OperationalStatusMsg(raw_message); + } + */ + return es1090; // unknown extended squitter + } + } + return modes; // unknown mode s reply + } + } +} \ No newline at end of file diff --git a/LibADSB/Exceptions.cs b/LibADSB/Exceptions.cs new file mode 100644 index 0000000..4b839b8 --- /dev/null +++ b/LibADSB/Exceptions.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.ComponentModel; + +namespace LibADSB +{ + public class BadFormatException : Exception + { + public BadFormatException(string message) + : base (message) + { + } + } + + public class MissingInformationException : Exception + { + public MissingInformationException(string message) + : base(message) + { + } + } + + public class PositionStraddleException : Exception + { + public PositionStraddleException(string message) + : base(message) + { + } + } + + public class UnspecifiedFormatException : Exception + { + public UnspecifiedFormatException(string message) + : base(message) + { + } + } + + public class IllegalArgumentException : Exception + { + public IllegalArgumentException(string message) + : base(message) + { + } + } + + +} diff --git a/LibADSB/ExtendedSquitter.cs b/LibADSB/ExtendedSquitter.cs new file mode 100644 index 0000000..b963005 --- /dev/null +++ b/LibADSB/ExtendedSquitter.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.ComponentModel; + + +namespace LibADSB +{ + public class ExtendedSquitter : ModeSReply + { + + [Browsable(false)] + [DescriptionAttribute("Capability/Subtype of ES message")] + public override byte Capabilities + { + get + { + return capabilities; + } + } + + [Browsable(false)] + [DescriptionAttribute("Message of ES message")] + public byte[] Message + { + get + { + return message; + } + } + + [Browsable(false)] + [DescriptionAttribute("Format/Typecode of ES message")] + public byte FormatTypeCode + { + get + { + return format_type_code; + } + } + + private byte capabilities; + private byte[] message; + private byte format_type_code; + + /** + * @param raw_message raw extended squitter as hex string + * @throws BadFormatException if message is not extended squitter or + * contains wrong values. + */ + public ExtendedSquitter(string raw_message) : base (raw_message) + { + + if (DownlinkFormat != 17 && DownlinkFormat != 18) + { + throw new BadFormatException("Message is not an extended squitter: " + raw_message); + } + + byte[] payload = Payload; + capabilities = (byte) (payload[0] & 0x7); + + // extract ADS-B message + message = new byte[7]; + for (int i=0; i<7; i++) + message[i] = payload[i+3]; + + format_type_code = (byte) ((message[0] >> 3) & 0x1F); + } + + public override string ToString() + { + return base.ToString() + "\n" + + "Extended Squitter:\n"+ + "\tFormat type code:\t"+FormatTypeCode+"\n"+ + "\tCapabilities:\t\t"+Capabilities+"\n"+ + "\tMessage field:\t\t"+BitConverter.ToString(Message).Replace("-",String.Empty); + } + } +} diff --git a/LibADSB/IdentificationMsg.cs b/LibADSB/IdentificationMsg.cs new file mode 100644 index 0000000..6fa4b95 --- /dev/null +++ b/LibADSB/IdentificationMsg.cs @@ -0,0 +1,161 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.ComponentModel; + +namespace LibADSB +{ + public class IdentificationMsg : ExtendedSquitter + { + [Browsable(false)] + [DescriptionAttribute("Emitter category of identification message")] + public byte EmitterCategory + { + get + { + return emitter_category; + } + } + + [Browsable(false)] + [DescriptionAttribute("Emitter identity of identification message ")] + public byte[] Identity + { + get + { + return identity; + } + } + + private byte emitter_category; + private byte[] identity; + + /** + * Maps ADS-B encoded to readable characters + * @param digit encoded digit + * @return readable character + */ + private static char mapChar (byte digit) + { + if (digit>0 && digit<27) return (char) ('A'+digit-1); + else if (digit>47 && digit<58) return (char) ('0'+digit-48); + else return ' '; + } + + /** + * Maps ADS-B encoded to readable characters + * @param digits array of encoded digits + * @return array of decoded characters + */ + private static char[] mapChar (byte[] digits) { + char[] result = new char[digits.Length]; + + for (int i=0; i 4) + { + throw new BadFormatException("Identification messages must have typecode of 1-4: " + raw_message); + } + + byte[] msg = Message; + emitter_category = (byte) (msg[0] & 0x7); + + // extract identity + identity = new byte[8]; + int byte_off, bit_off; + for (int i=8; i>=1; i--) + { + // calculate offsets + byte_off = (i*6)/8; bit_off = (i*6)%8; + + // char aligned with byte? + if (bit_off == 0) identity[i-1] = (byte) (msg[byte_off]&0x3F); + else { + ++byte_off; + identity[i-1] = (byte) (msg[byte_off]>>(8-bit_off)&(0x3F>>(6-bit_off))); + // should we add bits from the next byte? + if (bit_off < 6) identity[i-1] |= (byte)(msg[byte_off-1]< 300000 lbs)", + "High Performance (> 5g acceleration and 400 kts)", + "Rotorcraft" + }, + new string[] + { + "No ADS-B Emitter Category Information", + "Glider / sailplane", + "Lighter-than-air", + "Parachutist / Skydiver", + "Ultralight / hang-glider / paraglider", + "Reserved", + "Unmanned Aerial Vehicle", + "Space / Trans-atmospheric vehicle", + }, + new string[] + { + "No ADS-B Emitter Category Information", + "Surface Vehicle – Emergency Vehicle", + "Surface Vehicle – Service Vehicle", + "Point Obstacle (includes tethered balloons)", + "Cluster Obstacle", + "Line Obstacle", + "Reserved", + "Reserved" + }, + new string[] + { + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved", + "Reserved" + } + }; + + return categories[4-FormatTypeCode][emitter_category]; + } + + public override string ToString() + { + return base.ToString()+"\n"+ + "Identification:\n"+ + "\tEmitter category:\t"+getCategoryDescription()+" ("+EmitterCategory+")\n"+ + "\tCallsign:\t\t"+getIdentity(); + } + } +} diff --git a/LibADSB/LibADSB.csproj b/LibADSB/LibADSB.csproj new file mode 100644 index 0000000..5f73905 --- /dev/null +++ b/LibADSB/LibADSB.csproj @@ -0,0 +1,83 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {A5775994-404F-4B2E-9FF7-4537BBE17506} + Library + Properties + LibADSB + LibADSB + v4.0 + 512 + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Newtonsoft.Json.12.0.1\lib\net40\Newtonsoft.Json.dll + True + + + + + ..\packages\System.Data.SQLite.Core.1.0.110.0\lib\net40\System.Data.SQLite.dll + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}". + + + + + \ No newline at end of file diff --git a/LibADSB/ModeSReply.cs b/LibADSB/ModeSReply.cs new file mode 100644 index 0000000..0696244 --- /dev/null +++ b/LibADSB/ModeSReply.cs @@ -0,0 +1,256 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.ComponentModel; + +// LibADSB project is based on the java-adsb project (https://github.com/openskynetwork/java-adsb) + +namespace LibADSB +{ + public class ModeSReply + { + + // Public properties + + [Browsable(false)] + [DescriptionAttribute("Downlink format of ADS-B message (0..31)")] + public byte DownlinkFormat + { + get + { + return downlink_format; + } + } + + [Browsable(false)] + [DescriptionAttribute("Capability/Subtype of ADS-B message (0..7)")] + public virtual byte Capabilities + { + get + { + return capabilities; + } + } + + [Browsable(false)] + [DescriptionAttribute("ICAO number of aircraft")] + public byte[] ICAO24 + { + get + { + return icao24; + } + } + + [Browsable(false)] + [DescriptionAttribute("Payload of ADS-B message")] + public byte[] Payload + { + get + { + return payload; + } + } + + [Browsable(false)] + [DescriptionAttribute("Parity of ADS-B message")] + public byte[] Parity + { + get + { + return parity; + } + } + + [Browsable(false)] + [DescriptionAttribute("Recalculated parity of ADS-B message")] + public byte[] RecalculatedParity + { + get + { + byte[] message = new byte[payload.Length + 1]; + + message[0] = (byte)(downlink_format << 3 | capabilities); + for (byte b = 0; b < payload.Length; ++b) + message[b + 1] = payload[b]; + + return calcParity(message); + } + } + + [DescriptionAttribute("Parity check of ADS-B message")] + public bool CheckParity + { + get + { + return RecalculatedParity.SequenceEqual(Parity); + } + } + + // Private properties + private byte downlink_format; // 0-31 + private byte capabilities; // three bits after the downlink format + private byte[] icao24; // 3 bytes + private byte[] payload; // 3 or 10 bytes + private byte[] parity; // 3 bytes + + // Static fields and functions + + // polynomial for the cyclic redundancy check
+ // Note: we assume that the degree of the polynomial + // is divisible by 8 (holds for Mode S) and the msb is left out + + public static byte[] CRC_polynomial = + { + (byte)0xFF, + (byte)0xF4, + (byte)0x09 // according to Annex 10 V4 + }; + + // @return calculated parity field as 3-byte array. We used the implementation from
+ // http://www.eurocontrol.int/eec/gallery/content/public/document/eec/report/1994/022_CRC_calculations_for_Mode_S.pdf + + public static byte[] calcParity (byte[] msg) + { + byte[] pi = new byte[CRC_polynomial.Length]; + Array.Copy(msg, 0, pi, 0, CRC_polynomial.Length); + + bool invert; + int byteidx, bitshift; + for (int i = 0; i < msg.Length*8; ++i) + { + invert = ((pi[0] & 0x80) != 0); + // shift left + pi[0] <<= 1; + for (int b = 1; b < CRC_polynomial.Length; ++b) + { + pi[b-1] |= (byte) ((pi[b] >> 7) & 0x1); + pi[b] <<= 1; + } + + // get next bit from message + byteidx = ((CRC_polynomial.Length*8)+i) / 8; + bitshift = 7-(i%8); + if (byteidx < msg.Length) + pi[pi.Length-1] |= (byte) ((msg[byteidx] >> bitshift) & 0x1); + + // xor + if (invert) + { + for (int b = 0; b < CRC_polynomial.Length; ++b) + pi[b] ^= CRC_polynomial[b]; + } + } + byte[] result = new byte[CRC_polynomial.Length]; + Array.Copy(pi, 0, result, 0, CRC_polynomial.Length); + return result; + } + + // Constructors + + /** + * We assume the following message format: + * | DF | CA | Payload | PI/AP | + * 5 3 3/10 3 + * + * @param raw_message Mode S message in hex representation + * @throws BadFormatException if message has invalid length or payload does + * not match specification or parity has invalid length + */ + public ModeSReply (string raw_message) + { + // check format invariants + int length = raw_message.Length; + if (length != 14 && length != 28) + throw new BadFormatException("Raw message has invalid length: " + raw_message); + + downlink_format = System.Convert.ToByte(raw_message.Substring(0, 2), 16); + capabilities = (byte) (downlink_format & 0x7); + downlink_format = (byte) (downlink_format >> 3 & 0x1F); + + byte[] payload = new byte[(length-8)/2]; + byte[] icao24 = new byte[3]; + byte[] parity = new byte[3]; + + // decode based on format + // TODO + switch (downlink_format) + { + case 0: // Short air-air (ACAS) + case 4: // Short altitude reply + case 5: // Short identity reply + case 16: // Long air-air (ACAS) + case 20: // Long Comm-B, altitude reply + case 21: // Long Comm-B, identity reply + case 24: // Long Comm-D (ELM) + // here we assume that AP is already the icao24 + // i.e. parity is extracted. Therefore we leave + // parity 0 + for (int i=length-6; i= 1) && (movement <= 124); + } + } + + [Browsable(false)] + [DescriptionAttribute("Object ground speed in Surface Position Message [m/s]")] + public double GroundSpeed + { + get + { + double speed; + if (movement == 1) + speed = 0; + else if (movement >= 2 && movement <= 8) + speed = 0.125+(movement-2)*0.125; + else if (movement >= 9 && movement <= 12) + speed = 1+(movement-9)*0.25; + else if (movement >= 13 && movement <= 38) + speed = 2+(movement-13)*0.5; + else if (movement >= 39 && movement <= 93) + speed = 15+(movement-39); + else if (movement >= 94 && movement <= 108) + speed = 70+(movement-94)*2; + else if (movement >= 109 && movement <= 123) + speed = 100+(movement-109)*5; + else if (movement == 124) + speed = 175; + else + throw new MissingInformationException("Ground speed info not available!"); + + return speed*0.514444; + } + } + + [Browsable(false)] + [DescriptionAttribute("Object ground speed resolution in Surface Position Message [m/s]")] + public double GroundSpeedResolution + { + get + { + double resolution; + + if (movement >= 1 && movement <= 8) + resolution = 0.125; + else if (movement >= 9 && movement <= 12) + resolution = 0.25; + else if (movement >= 13 && movement <= 38) + resolution = 0.5; + else if (movement >= 39 && movement <= 93) + resolution = 1; + else if (movement >= 94 && movement <= 108) + resolution = 2; + else if (movement >= 109 && movement <= 123) + resolution = 5; + else if (movement == 124) + resolution = 175; + else + throw new MissingInformationException("Ground speed info not available!"); + + return resolution*0.514444; + } + } + + [Browsable(false)] + [DescriptionAttribute("Object has valid heading in Surface Position Message")] + public bool HasValidHeading + { + get + { + return heading_status; + } + } + + [Browsable(false)] + [DescriptionAttribute("Object heading in Surface Position Message")] + public double Heading + { + get + { + return ground_track * 360.0 / 128.0; + } + } + + [Browsable(false)] + [DescriptionAttribute("Synchronization status of Time of Applicability of Surface Position Message")] + public bool IsUTCTime + { + get + { + return time_flag; + } + } + + [Browsable(false)] + [DescriptionAttribute("Indicates ODD format of Surface Position Message")] + public bool IsOddFormat + { + get + { + return cpr_format; + } + } + + [Browsable(false)] + [DescriptionAttribute("Indicates barometric measuremnet of altitude of Surface Position Message")] + public bool IsBarometricAltitude + { + get + { + return this.FormatTypeCode < 20; + } + } + + [Browsable(false)] + [DescriptionAttribute("CPR-encoded Latitude of Surface Position Message")] + public int CPREncodedLat + { + get + { + return cpr_encoded_lat; + } + } + + [Browsable(false)] + [DescriptionAttribute("CPR-encoded Longitude of Surface Position Message")] + public int CPREncodedLon + { + get + { + return cpr_encoded_lon; + } + } + + [Browsable(false)] + [DescriptionAttribute("NIC supplement of Surface Position Message")] + public byte NICSupplement + { + get + { + return nic_suppl; + } + set + { + nic_suppl = value; + } + } + + + private bool horizontal_position_available; + private byte movement; + private bool heading_status; // is heading valid? + private byte ground_track; + private bool time_flag; + private bool cpr_format; + private int cpr_encoded_lat; + private int cpr_encoded_lon; + private byte nic_suppl; + + + /** + * @param raw_message raw ADS-B surface position message as hex string + * @throws BadFormatException if message has wrong format + */ + public SurfacePositionMsg(String raw_message) + : base(raw_message) + { + if (!(FormatTypeCode == 0 || + (FormatTypeCode >= 5 && FormatTypeCode <= 8))) + throw new BadFormatException("This is not a position message! Wrong format type code ("+FormatTypeCode+"): " +raw_message); + + byte[] msg = Message; + + horizontal_position_available = FormatTypeCode != 0; + + movement = (byte) ((((msg[0]&0x7)<<4) | ((msg[1]&0xF0)>>4))&0x7F); + heading_status = (msg[1]&0x8) != 0; + ground_track = (byte) ((((msg[1]&0x7)<<4) | ((msg[2]&0xF0)>>4))&0x7F); + + time_flag = ((msg[2]>>3)&0x1) == 1; + cpr_format = ((msg[2]>>2)&0x1) == 1; + cpr_encoded_lat = (((msg[2]&0x3)<<15) | ((msg[3]&0xFF)<<7) | ((msg[4]>>1)&0x7F)) & 0x1FFFF; + cpr_encoded_lon = (((msg[4]&0x1)<<16) | ((msg[5]&0xFF)<<8) | (msg[6]&0xFF)) & 0x1FFFF; + } + + /** + * @return horizontal containment radius limit in meters. A return value of 0 means "unkown". + * If NIC supplement is set before, the return value is exactly according to DO-260B. + * Otherwise it can be a little worse than it actually is. 0 means unkown. + */ + public double getHorizontalContainmentRadiusLimit() + { + switch (FormatTypeCode) + { + case 0: return 0; + case 5: return 7.5; + case 6: return 25; + case 7: + if ((nic_suppl&0x5) == 0x4) return 75; + else return 185.2; + case 8: + if ((nic_suppl&0x5) == 0x5) return 370.4; + else if ((nic_suppl&0x5) == 0x4) return 555.6; + else return 1111.2; + default: return 0; + } + } + + /** + * @return Navigation integrity category. A NIC of 0 means "unkown". + * If NIC supplement is set before, the return value is exactly according to DO-260B. + * Otherwise it might be a little worse than it actually is. + */ + public byte getNavigationIntegrityCategory() + { + switch (FormatTypeCode) + { + case 0: return 0; + case 5: return 11; + case 6: return 10; + case 7: + if ((nic_suppl&0x5) == 0x4) return 9; + else return 8; + case 8: + if ((nic_suppl&0x5) == 0x5) return 7; + else if ((nic_suppl&0x5) == 0x4) return 6; + else if ((nic_suppl&0x5) == 0x1) return 6; + else return 0; + default: return 0; + } + } + + /** + * @param Rlat Even or odd Rlat value (CPR internal) + * @return the number of even longitude zones at a latitude + */ + private double NL(double Rlat) { + if (Rlat == 0) return 59; + else if (Math.Abs(Rlat) == 87) return 2; + else if (Math.Abs(Rlat) > 87) return 1; + + double tmp = 1-(1-Math.Cos(Math.PI/(2.0*15.0)))/Math.Pow(Math.Cos(Math.PI/180.0*Math.Abs(Rlat)), 2); + return Math.Floor(2*Math.PI/Math.Acos(tmp)); + } + + /** + * Modulo operator in java has stupid behavior + */ + private static double mod(double a, double b) { + return ((a%b)+b)%b; + } + + /** + * This method can only be used if another position report with a different format (even/odd) is available + * and set with msg.setOtherFormatMsg(other). + * @param other position message of the other format (even/odd). Note that the time between + * both messages should be not longer than 50 seconds! + * @return globally unambiguously decoded position tuple (latitude, longitude). The positional + * accuracy maintained by the CPR encoding will be approximately 1.25 meters. + * A message of the other format is needed for global decoding. + * @throws MissingInformationException if no position information is available in one of the messages + * @throws IllegalArgumentException if input message was emitted from a different transmitter + * @throws PositionStraddleError if position messages straddle latitude transition + * @throws BadFormatException other has the same format (even/odd) + */ + public double[] getGlobalPosition(SurfacePositionMsg other) + { + if (!other.ICAO24.SequenceEqual(ICAO24)) + throw new IllegalArgumentException( + string.Format("Transmitter of other message (%s) not equal to this (%s):", + BitConverter.ToString(other.ICAO24).Replace("-",String.Empty), BitConverter.ToString(ICAO24).Replace("-",String.Empty))); + + if (other.IsOddFormat == IsOddFormat) + throw new BadFormatException("Expected "+ (IsOddFormat? "even":"odd") + " message format:" + other.ToString()); + + if (!horizontal_position_available) + throw new MissingInformationException("No position information available!"); + if (!other.HasValidPosition) + throw new MissingInformationException("Other message has no position information."); + + SurfacePositionMsg even = IsOddFormat?other:this; + SurfacePositionMsg odd = IsOddFormat?this:other; + + // Helper for latitude single(Number of zones NZ is set to 15) + double Dlat0 = 90.0/60.0; + double Dlat1 = 90.0/59.0; + + // latitude index + double j = Math.Floor((59.0*even.CPREncodedLat-60.0*odd.CPREncodedLat)/((double)(1<<17))+0.5); + + // global latitudes + double Rlat0 = Dlat0 * (mod(j,60)+even.CPREncodedLat/((double)(1<<17))); + double Rlat1 = Dlat1 * (mod(j,59)+odd.CPREncodedLat/((double)(1<<17))); + + // Southern hemisphere? + if (Rlat0 >= 270 && Rlat0 <= 360) Rlat0 -= 360; + if (Rlat1 >= 270 && Rlat1 <= 360) Rlat1 -= 360; + + // Northern hemisphere? + if (Rlat0 <= -270 && Rlat0 >= -360) Rlat0 += 360; + if (Rlat1 <= -270 && Rlat1 >= -360) Rlat1 += 360; + + // ensure that the number of even longitude zones are equal + if (NL(Rlat0) != NL(Rlat1)) + throw new PositionStraddleException( + "The two given position straddle a transition latitude "+ + "and cannot be decoded. Wait for positions where they are equal."); + + // Helper for longitude + double Dlon0 = 90.0/Math.Max(1.0, NL(Rlat0)); + double Dlon1 = 90.0/Math.Max(1.0, NL(Rlat1)-1); + + // longitude index + double NL_helper = NL(IsOddFormat?Rlat1:Rlat0); // assuming that this is the newer message + double m = Math.Floor((even.CPREncodedLon*(NL_helper-1)-odd.CPREncodedLon*NL_helper)/((double)(1<<17))+0.5); + + // global longitude + double Rlon0 = Dlon0 * (mod(m,Math.Max(1.0, NL(Rlat0))) + even.CPREncodedLon/((double)(1<<17))); + double Rlon1 = Dlon1 * (mod(m,Math.Max(1.0, NL(Rlat1)-1)) + odd.CPREncodedLon/((double)(1<<17))); + + // correct longitude + if (Rlon0 < -180 && Rlon0 > -360) Rlon0 += 360; + if (Rlon1 < -180 && Rlon1 > -360) Rlon1 += 360; + if (Rlon0 > 180 && Rlon0 < 360) Rlon0 -= 360; + if (Rlon1 > 180 && Rlon1 < 360) Rlon1 -= 360; + + return new double[] {IsOddFormat?Rlat1:Rlat0, IsOddFormat?Rlon1:Rlon0}; + } + + /** + * This method uses a locally unambiguous decoding for position messages. It + * uses a reference position known to be within 45NM (= 83.34km) of the true target + * position. the reference point may be a previously tracked position that has + * been confirmed by global decoding (see getGlobalPosition()). + * @param ref_lat latitude of reference position + * ref_lon longitude of reference position + * @return decoded position as tuple (latitude, longitude). The positional + * accuracy maintained by the CPR encoding will be approximately 5.1 meters. + * @throws MissingInformationException if no position information is available + */ + public double[] getLocalPosition(double ref_lat, double ref_lon) + { + if (!horizontal_position_available) + throw new MissingInformationException("No position information available!"); + + // latitude zone size + double Dlat = IsOddFormat ? 90.0/59.0 : 90.0/60.0; + + // latitude zone index + double j = Math.Floor(ref_lat/Dlat) + Math.Floor(0.5+(mod(ref_lat, Dlat))/Dlat-CPREncodedLat/((double)(1<<17))); + + // decoded position latitude + double Rlat = Dlat*(j+CPREncodedLat/((double)(1<<17))); + + // longitude zone size + double Dlon = 90.0/Math.Max(1.0, NL(Rlat)-(IsOddFormat?1.0:0.0)); + + // longitude zone coordinate + double m = + Math.Floor(ref_lon/Dlon) + + Math.Floor(0.5+(mod(ref_lon,Dlon))/Dlon-(float)CPREncodedLon/((double)(1<<17))); + + // and finally the longitude + double Rlon = Dlon * (m + CPREncodedLon/((double)(1<<17))); + + // System.out.println("Loc: EncLon: "+getCPREncodedLongitude()+ + // " m: "+m+" Dlon: "+Dlon+ " Rlon2: "+Rlon2); + + return new double[] {Rlat,Rlon}; + } + + public override string ToString() + { + try { + return base.ToString()+"\n"+ + "Surface Position:\n"+ + "\tSpeed:\t\t"+(HasGroundSpeed ? GroundSpeed.ToString("F8") : "unkown")+ + "\n\tSpeed Resolution:\t\t"+(HasGroundSpeed ? GroundSpeedResolution.ToString("F8") : "unkown")+ + "\n\tHeading:\t\t"+(HasValidHeading ? Heading.ToString("F8") : "unkown")+ + "\n\tFormat:\t\t"+(IsOddFormat?"odd":"even")+ + "\n\tHas position:\t"+(HasValidPosition?"yes":"no"); + } catch (Exception ex) + { + // should never happen + return "An exception " + ex.Message + " was thrown."; + } + } + + } +} diff --git a/LibADSB/VelocityOverGroundMsg.cs b/LibADSB/VelocityOverGroundMsg.cs new file mode 100644 index 0000000..030b63a --- /dev/null +++ b/LibADSB/VelocityOverGroundMsg.cs @@ -0,0 +1,259 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.ComponentModel; + +namespace LibADSB +{ + public class VelocityOverGroundMsg : ExtendedSquitter + { + [Browsable(false)] + [DescriptionAttribute("IFR capabilities in Velocity Over Ground Message")] + public bool IFRCapability + { + get + { + return ifr_capability; + } + } + + [Browsable(false)] + [DescriptionAttribute(" Velocity is availble in Velocity Over Ground Message")] + public bool HasValidVelocity + { + get + { + return velocity_info_available; + } + } + + [Browsable(false)] + [DescriptionAttribute(" Velocity in Velocity Over Ground Message [kts]")] + public int Velocity + { + get + { + return (int)Math.Sqrt(north_south_velocity*north_south_velocity + east_west_velocity * east_west_velocity); + } + } + + [Browsable(false)] + [DescriptionAttribute(" Direction is WEST in Velocity Over Ground Message")] + public bool DirectionWest + { + get + { + return direction_west; + } + } + + [Browsable(false)] + [DescriptionAttribute(" East to West velocity in Velocity Over Ground Message [kts]")] + public int EastToWestVelocity + { + get + { + return (int)((direction_west ? east_west_velocity : -east_west_velocity) + 0.5); + } + } + + [Browsable(false)] + [DescriptionAttribute(" Direction is SOUTH in Velocity Over Ground Message")] + public bool DirectionSouth + { + get + { + return direction_south; + } + } + + [Browsable(false)] + [DescriptionAttribute(" North to South velocity in Velocity Over Ground Message [kts]")] + public int NorthToSouthVelocity + { + get + { + return (int)((direction_south ? north_south_velocity : -north_south_velocity) + 0.5); + } + } + + [Browsable(false)] + [DescriptionAttribute("Vertical rate is availble in Velocity Over Ground Message")] + public bool HasValidVerticalRate + { + get + { + return vertical_rate_info_available; + } + } + + [Browsable(false)] + [DescriptionAttribute("Vertical rate in Velocity Over Ground Message [ft/s]")] + public int VerticalRate + { + get + { + return (int)((vertical_rate_down ? -vertical_rate : vertical_rate) * 0.00508); + } + } + + [Browsable(false)] + [DescriptionAttribute("Heading is availble in Airspeed Heading Message")] + public bool HasValidHeading + { + get + { + // heading can only calculated when velocity info is available! + return velocity_info_available; + } + } + + [Browsable(false)] + [DescriptionAttribute("Heading in Airspeed Heading Message [deg]")] + public int Heading + { + get + { + int angle = (int) (Math.Atan2( + -this.EastToWestVelocity, + -this.NorthToSouthVelocity) * 180.0 / Math.PI); + + // if negative => clockwise + if (angle < 0) + return 360 + angle; + else + return angle; + } + } + + + private byte subtype; + private bool intent_change; + private bool ifr_capability; + private byte navigation_accuracy_category; + private bool direction_west; // 0 = east, 1 = west + private int east_west_velocity; // in kn + private bool velocity_info_available; + private bool direction_south; // 0 = north, 1 = south + private int north_south_velocity; // in kn + private bool vertical_source; // 0 = geometric, 1 = barometric + private bool vertical_rate_down; // 0 = up, 1 = down + private int vertical_rate; // in ft/s + private bool vertical_rate_info_available; + private int geo_minus_baro; // in ft + private bool geo_minus_baro_available; + + /** + * @param raw_message raw ADS-B velocity-over-ground message as hex string + * @throws BadFormatException if message has wrong format + */ + public VelocityOverGroundMsg(string raw_message) + : base(raw_message) + { + + if (this.FormatTypeCode != 19) + { + throw new BadFormatException("Velocity messages must have typecode 19: " + raw_message); + } + + byte[] msg = Message; + + subtype = (byte) (msg[0]&0x7); + if (subtype != 1 && subtype != 2) + { + throw new BadFormatException("Ground speed messages have subtype 1 or 2: " + raw_message); + } + + intent_change = (msg[1]&0x80)>0; + ifr_capability = (msg[1]&0x40)>0; + navigation_accuracy_category = (byte) ((msg[1]>>3)&0x7); + + // check this later + velocity_info_available = true; + vertical_rate_info_available = true; + geo_minus_baro_available = true; + + direction_west = (msg[1]&0x4)>0; + east_west_velocity = (short) (((msg[1]&0x3)<<8 | msg[2]&0xFF)-1); + if (east_west_velocity == -1) velocity_info_available = false; + if (subtype == 2) east_west_velocity<<=2; + + direction_south = (msg[3]&0x80)>0; + north_south_velocity = (short) (((msg[3]&0x7F)<<3 | msg[4]>>5&0x07)-1); + if (north_south_velocity == -1) velocity_info_available = false; + if (subtype == 2) north_south_velocity<<=2; + + vertical_source = (msg[4]&0x10)>0; + vertical_rate_down = (msg[4]&0x08)>0; + vertical_rate = (short) ((((msg[4]&0x07)<<6 | msg[5]>>2&0x3F)-1)<<6); + if (vertical_rate == -1) vertical_rate_info_available = false; + + geo_minus_baro = (short) (((msg[6]&0x7F)-1)*25); + if (geo_minus_baro == -1) geo_minus_baro_available = false; + if ((msg[6]&0x80)>0) geo_minus_baro *= -1; + } + + public bool hasGeoMinusBaroInfo() { + return geo_minus_baro_available; + } + + /** + * @return If supersonic, velocity has only 4 kts accuracy, otherwise 1 kt + */ + public bool isSupersonic() { + return subtype == 2; + } + + /** + * @return true, if aircraft wants to change altitude for instance + */ + public bool hasChangeIntent() { + return intent_change; + } + + + /** + * @return NAC according to RTCA DO-260A + */ + public byte getNavigationAccuracyCategory() { + return navigation_accuracy_category; + } + + + /** + * @return whether altitude is derived by barometric sensor or GNSS + */ + public bool isBarometricVerticalSpeed() { + return vertical_source; + } + + + /** + * @return difference between barometric and geometric altitude in m + * @throws MissingInformationException if no geo/baro difference info is available + */ + public double getGeoMinusBaro() + { + if (!geo_minus_baro_available) throw new MissingInformationException("No geo/baro difference info available!"); + return geo_minus_baro * 0.3048; + } + public override string ToString() + { + string ret = base.ToString()+"\n"+ + "Velocity over ground:\n"; + try { ret += "\tNorth to south velocity:\t"+NorthToSouthVelocity+"\n"; } + catch (Exception e) { ret += "\tNorth to south velocity:\t\tnot available\n"; } + try { ret += "\tEast to west velocity:\t\t"+EastToWestVelocity+"\n"; } + catch (Exception e) { ret += "\tEast to west velocity:\t\tnot available\n"; } + try { ret += "\tVelocity:\t\t\t"+Velocity+"\n"; } + catch (Exception e) { ret += "\tVelocity:\t\t\tnot available\n"; } + try { ret += "\tHeading\t\t\t\t"+Heading+"\n"; } + catch (Exception e) { ret += "\tHeading\t\t\t\tnot available\n"; } + try { ret += "\tVertical rate:\t\t\t"+VerticalRate; } + catch (Exception e) { ret += "\tVertical rate:\t\t\tnot available"; } + + return ret; + } + } +} diff --git a/LibADSB/packages.config b/LibADSB/packages.config new file mode 100644 index 0000000..0aee17e --- /dev/null +++ b/LibADSB/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/NDDE/NDde/Binary/NDde.dll b/NDDE/NDde/Binary/NDde.dll new file mode 100644 index 0000000..1cd6ddd Binary files /dev/null and b/NDDE/NDde/Binary/NDde.dll differ diff --git a/NDDE/NDde/Binary/NDde.xml b/NDDE/NDde/Binary/NDde.xml new file mode 100644 index 0000000..cb855df --- /dev/null +++ b/NDDE/NDde/Binary/NDde.xml @@ -0,0 +1,3232 @@ + + + + NDde + + + + + This contains information about the Disconnected event. + + + + + + This is the base class for all NDde event argument classes. + + + + + This returns a string containing the current values of all properties. + + + A string containing the current values of all properties. + + + + + This gets a bool indicating whether the client disconnected because of the server. + + + + + This gets a bool indicating whether the client disconnected because Dispose was explicitly called. + + + The value will be true if Dispose was explicitly called on DdeClient. The DdeClient sending this event has + been disposed and can no longer be accessed. Any exception thrown in the currently executing method will be ignored. + + + + + This namespace contains classes for using Dynamic Data Exchange (DDE) in .NET. + + + + + This namespace contains classes for using advanced features of the library. + + + + + This namespace contains classes for creating DDE monitors. + + + + + This namespace contains classes for creating DDE client applications. + + + + + This namespace contains classes for creating DDE server applications. + + + + + This class is needed to dispose of DDEML resources correctly since the DDEML is thread specific. + + + + + This class is needed to dispose of DDEML resources correctly since the DDEML is thread specific. + + + + + This contains information about the CallbackActivity event. + + + + + This contains information about events on DdeMonitor. + + + + + This gets the task handle of the application associated with this event. + + + + + See the MSDN documentation for information about this member. + + + + + See the MSDN documentation for information about this member. + + + + + See the MSDN documentation for information about this member. + + + + + See the MSDN documentation for information about this member. + + + + + See the MSDN documentation for information about this member. + + + + + See the MSDN documentation for information about this member. + + + + + See the MSDN documentation for information about this member. + + + + + See the MSDN documentation for information about this member. + + + + + This gets the return value of the DDEML callback function. See the MSDN documentation for information about this member. + + + + + This represents the client side of a DDE conversation. + + + + + DDE conversations are established by specifying a service name and topic name pair. The service name is usually the name of the application + acting as a DDE server. A DDE server can respond to multiple service names, but most servers usually only respond to one. The topic name + is a logical context for data and is defined by the server application. A server can and usually does support many topic names. + + + After a conversation has been established by calling Connect an application can read and write data using the Request and + Poke methods respectively by specifying an item name supported by the active conversation. An item name identifies a unit of data. + An application can also be notified of changes by initiating an advise loop on an item name using the StartAdvise method. Advise + loops can either be warm or hot. A hot advise loop returns the data associated with an item name when it changes whereas a warm advise loop + only notifies the application without sending any data. Commands can be sent to the server using the Execute method. + + + Callbacks and events are invoked on the thread hosting the DdeContext. All operations must be marshaled onto the thread hosting the + DdeContext associated with this object. Method calls will block until that thread becomes available. An exception will be generated + if the thread does not become available in a timely manner. + + + + The following example demonstrates how to use a DdeClient. + + using System; + using System.Text; + using NDde.Client; + + public sealed class Client + { + public static void Main(string[] args) + { + // Wait for the user to press ENTER before proceding. + Console.WriteLine("The Server sample must be running before the client can connect."); + Console.WriteLine("Press ENTER to continue..."); + Console.ReadLine(); + try + { + // Create a client that connects to 'myapp|mytopic'. + using (DdeClient client = new DdeClient("myapp", "mytopic")) + { + // Subscribe to the Disconnected event. This event will notify the application when a conversation has been terminated. + client.Disconnected += OnDisconnected; + + // Connect to the server. It must be running or an exception will be thrown. + client.Connect(); + + // Synchronous Execute Operation + client.Execute("mycommand", 60000); + + // Synchronous Poke Operation + client.Poke("myitem", DateTime.Now.ToString(), 60000); + + // Syncronous Request Operation + Console.WriteLine("Request: " + client.Request("myitem", 60000)); + + // Asynchronous Execute Operation + client.BeginExecute("mycommand", OnExecuteComplete, client); + + // Asynchronous Poke Operation + client.BeginPoke("myitem", Encoding.ASCII.GetBytes(DateTime.Now.ToString() + "\0"), 1, OnPokeComplete, client); + + // Asynchronous Request Operation + client.BeginRequest("myitem", 1, OnRequestComplete, client); + + // Advise Loop + client.StartAdvise("myitem", 1, true, 60000); + client.Advise += OnAdvise; + + // Wait for the user to press ENTER before proceding. + Console.WriteLine("Press ENTER to quit..."); + Console.ReadLine(); + } + } + catch (Exception e) + { + Console.WriteLine(e.ToString()); + Console.WriteLine("Press ENTER to quit..."); + Console.ReadLine(); + } + } + + private static void OnExecuteComplete(IAsyncResult ar) + { + try + { + DdeClient client = (DdeClient)ar.AsyncState; + client.EndExecute(ar); + Console.WriteLine("OnExecuteComplete"); + } + catch (Exception e) + { + Console.WriteLine("OnExecuteComplete: " + e.Message); + } + } + + private static void OnPokeComplete(IAsyncResult ar) + { + try + { + DdeClient client = (DdeClient)ar.AsyncState; + client.EndPoke(ar); + Console.WriteLine("OnPokeComplete"); + } + catch (Exception e) + { + Console.WriteLine("OnPokeComplete: " + e.Message); + } + } + + private static void OnRequestComplete(IAsyncResult ar) + { + try + { + DdeClient client = (DdeClient)ar.AsyncState; + byte[] data = client.EndRequest(ar); + Console.WriteLine("OnRequestComplete: " + Encoding.ASCII.GetString(data)); + } + catch (Exception e) + { + Console.WriteLine("OnRequestComplete: " + e.Message); + } + } + + private static void OnStartAdviseComplete(IAsyncResult ar) + { + try + { + DdeClient client = (DdeClient)ar.AsyncState; + client.EndStartAdvise(ar); + Console.WriteLine("OnStartAdviseComplete"); + } + catch (Exception e) + { + Console.WriteLine("OnStartAdviseComplete: " + e.Message); + } + } + + private static void OnStopAdviseComplete(IAsyncResult ar) + { + try + { + DdeClient client = (DdeClient)ar.AsyncState; + client.EndStopAdvise(ar); + Console.WriteLine("OnStopAdviseComplete"); + } + catch (Exception e) + { + Console.WriteLine("OnStopAdviseComplete: " + e.Message); + } + } + + private static void OnAdvise(object sender, DdeAdviseEventArgs args) + { + Console.WriteLine("OnAdvise: " + args.Text); + } + + private static void OnDisconnected(object sender, DdeDisconnectedEventArgs args) + { + Console.WriteLine( + "OnDisconnected: " + + "IsServerInitiated=" + args.IsServerInitiated.ToString() + " " + + "IsDisposed=" + args.IsDisposed.ToString()); + } + + } // class + + + + Imports System.Text + Imports NDde.Client + + Module Program + + Sub Main() + + ' Wait for the user to press ENTER before proceding. + Console.WriteLine("The Server sample must be running before the client can connect.") + Console.WriteLine("Press ENTER to continue...") + Console.ReadLine() + + Try + ' Create a client that connects to 'myapp|mytopic'. + Using client As DdeClient = New DdeClient("myapp", "mytopic") + + ' Subscribe to the Disconnected event. This event will notify the application when a conversation has been terminated. + AddHandler client.Disconnected, AddressOf OnDisconnected + + ' Connect to the server. It must be running or an exception will be thrown. + client.Connect() + + ' Synchronous Execute Operation + client.Execute("mycommand", 60000) + + ' Synchronous Poke Operation + client.Poke("myitem", DateTime.Now.ToString(), 60000) + + ' Syncronous Request Operation + Console.WriteLine("Request: " + client.Request("myitem", 60000)) + + ' Asynchronous Execute Operation + client.BeginExecute("mycommand", AddressOf OnExecuteComplete, client) + + ' Asynchronous Poke Operation + client.BeginPoke("myitem", Encoding.ASCII.GetBytes(DateTime.Now.ToString() + Convert.ToChar(0)), 1, AddressOf OnPokeComplete, client) + + ' Asynchronous Request Operation + client.BeginRequest("myitem", 1, AddressOf OnRequestComplete, client) + + ' Advise Loop + client.StartAdvise("myitem", 1, True, 60000) + AddHandler client.Advise, AddressOf OnAdvise + + ' Wait for the user to press ENTER before proceding. + Console.WriteLine("Press ENTER to quit...") + Console.ReadLine() + + End Using + + Catch e As Exception + + Console.WriteLine(e.ToString()) + Console.WriteLine("Press ENTER to quit...") + Console.ReadLine() + + End Try + + End Sub + + Private Sub OnExecuteComplete(ByVal ar As IAsyncResult) + Try + Dim client As DdeClient = DirectCast(ar.AsyncState, DdeClient) + client.EndExecute(ar) + Console.WriteLine("OnExecuteComplete") + Catch e As Exception + Console.WriteLine("OnExecuteComplete: " + e.Message) + End Try + End Sub + + Private Sub OnPokeComplete(ByVal ar As IAsyncResult) + Try + Dim client As DdeClient = DirectCast(ar.AsyncState, DdeClient) + client.EndPoke(ar) + Console.WriteLine("OnPokeComplete") + Catch e As Exception + Console.WriteLine("OnPokeComplete: " + e.Message) + End Try + End Sub + + Private Sub OnRequestComplete(ByVal ar As IAsyncResult) + Try + Dim client As DdeClient = DirectCast(ar.AsyncState, DdeClient) + client.EndRequest(ar) + Console.WriteLine("OnRequestComplete") + Catch e As Exception + Console.WriteLine("OnRequestComplete: " + e.Message) + End Try + End Sub + + Private Sub OnStartAdviseComplete(ByVal ar As IAsyncResult) + Try + Dim client As DdeClient = DirectCast(ar.AsyncState, DdeClient) + client.EndStartAdvise(ar) + Console.WriteLine("OnStartAdviseComplete") + Catch e As Exception + Console.WriteLine("OnStartAdviseComplete: " + e.Message) + End Try + End Sub + + Private Sub OnStopAdviseComplete(ByVal ar As IAsyncResult) + Try + Dim client As DdeClient = DirectCast(ar.AsyncState, DdeClient) + client.EndStopAdvise(ar) + Console.WriteLine("OnStopAdviseComplete") + Catch e As Exception + Console.WriteLine("OnStopAdviseComplete: " + e.Message) + End Try + End Sub + + Private Sub OnAdvise(ByVal sender As Object, ByVal args As DdeAdviseEventArgs) + Console.WriteLine("OnAdvise: " + args.Text) + End Sub + + Private Sub OnDisconnected(ByVal sender As Object, ByVal args As DdeDisconnectedEventArgs) + Console.WriteLine( _ + "OnDisconnected: " + _ + "IsServerInitiated=" + args.IsServerInitiated.ToString() + " " + _ + "IsDisposed=" + args.IsDisposed.ToString()) + End Sub + + End Module + + + + + + + + + + This initializes a new instance of the DdeClient class that can connect to a server that supports the specified service name and + topic name pair. + + + A service name supported by a server application. + + + A topic name support by a server application. + + + This is thown when servic or topic exceeds 255 characters. + + + This is thrown when service or topic is a null reference. + + + + + This initializes a new instance of the DdeClient class that can connect to a server that supports the specified service name and + topic name pair using the specified synchronizing object. + + + A service name supported by a server application. + + + A topic name support by a server application. + + + The synchronizing object to use for this instance. + + + This is thown when service or topic exceeds 255 characters. + + + This is thrown when service or topic is a null reference. + + + + + This initializes a new instance of the DdeClient class that can connect to a server that supports the specified service name and + topic name pair and uses the specified context. + + + A service name supported by a server application. + + + A topic name support by a server application. + + + The context to use for execution. + + + This is thown when servic or topic exceeds 255 characters. + + + This is thrown when service or topic is a null reference. + + + + + This terminates the current conversation and releases all resources held by this instance. + + + + + This contains the implementation to release all resources held by this instance. + + + True if called by Dispose, false otherwise. + + + + + This establishes a conversation with a server that supports the specified service name and topic name pair. + + + This is thrown when the client is already connected. + + + This is thrown when the client could not connect to the server. + + + + + This establishes a conversation with a server that supports the specified service name and topic name pair. + + + Zero if the operation succeed or non-zero if the operation failed. + + + + + This terminates the current conversation. + + + + This is thrown when the client was not previously connected. + + + This is thown when the client could not disconnect from the server. + + + + + This pauses the current conversation. + + + This is thrown when the conversation is already paused. + + + This is thrown when the conversation could not be paused or when the client is not connected. + + + Synchronous operations will timeout if the conversation is paused. Asynchronous operations can begin, but will not complete until the + conversation has resumed. + + + + + This resumes the current conversation. + + + This is thrown when the conversation was not previously paused or when the client is not connected. + + + This is thrown when the conversation could not be resumed. + + + + + This terminates an asychronous operation. + + + The IAsyncResult object returned by a call that begins an asynchronous operation. + + + This method does nothing if the asynchronous operation has already completed. + + + This is thown when asyncResult is an invalid IAsyncResult. + + + This is thrown when asyncResult is a null reference. + + + This is thrown when the client is not connected. + + + This is thrown when the asynchronous operation could not be abandoned. + + + + + This sends a command to the server application. + + + The command to be sent to the server application. + + + The amount of time in milliseconds to wait for a response. + + + This is thown when command exceeds 255 characters or timeout is negative. + + + This is thrown when command is a null reference. + + + This is thrown when the client is not connected. + + + This is thrown when the server does not process the command. + + + This operation will timeout if the conversation is paused. + + + + + This sends a command to the server application. + + + The command to be sent to the server application. + + + The amount of time in milliseconds to wait for a response. + + + Zero if the operation succeed or non-zero if the operation failed. + + + This operation will timeout if the conversation is paused. + + + + + This begins an asynchronous operation to send a command to the server application. + + + The command to be sent to the server application. + + + The delegate to invoke when this operation completes. + + + An application defined data object to associate with this operation. + + + An IAsyncResult object for this operation. + + + This is thown when command exceeds 255 characters. + + + This is thrown when command is a null reference. + + + This is thrown when the client is not connected. + + + This is thrown when the asynchronous operation could not begin. + + + + + This throws any exception that occurred during the asynchronous operation. + + + The IAsyncResult object returned by a call to BeginExecute. + + + This is thown when asyncResult is an invalid IAsyncResult. + + + This is thrown when asyncResult is a null reference. + + + This is thrown when the server does not process the command. + + + + + + + + + This sends data to the server application. + + + An item name supported by the current conversation. + + + The data to send. + + + The amount of time in milliseconds to wait for a response. + + + This is thown when item exceeds 255 characters or timeout is negative. + + + This is thrown when item or data is a null reference. + + + This is thrown when the client is not connected. + + + This is thrown when the server does not process the data. + + + This operation will timeout if the conversation is paused. + + + + + This sends data to the server application. + + + An item name supported by the current conversation. + + + The data to send. + + + The format of the data. + + + The amount of time in milliseconds to wait for a response. + + + This is thown when item exceeds 255 characters or timeout is negative. + + + This is thrown when item or data is a null reference. + + + This is thrown when the client is not connected. + + + This is thrown when the server does not process the data. + + + This operation will timeout if the conversation is paused. + + + + + This sends data to the server application. + + + An item name supported by the current conversation. + + + The data to send. + + + The format of the data. + + + The amount of time in milliseconds to wait for a response. + + + Zero if the operation succeed or non-zero if the operation failed. + + + This operation will timeout if the conversation is paused. + + + + + This begins an asynchronous operation to send data to the server application. + + + An item name supported by the current conversation. + + + The data to send. + + + The format of the data. + + + The delegate to invoke when this operation completes. + + + An application defined data object to associate with this operation. + + + An IAsyncResult object for this operation. + + + This is thown when item exceeds 255 characters. + + + This is thrown when item or data is a null reference. + + + This is thrown when the client is not connected. + + + This is thrown when the asynchronous operation could not begin. + + + + + This throws any exception that occurred during the asynchronous operation. + + + The IAsyncResult object returned by a call to BeginPoke. + + + This is thown when asyncResult is an invalid IAsyncResult. + + + This is thrown when asyncResult is a null reference. + + + This is thrown when the server does not process the data. + + + + + + + + + This requests data using the specified item name. + + + An item name supported by the current conversation. + + + The amount of time in milliseconds to wait for a response. + + + The data returned by the server application in CF_TEXT format. + + + This is thown when item exceeds 255 characters or timeout is negative. + + + This is thrown when item is a null reference. + + + This is thrown when the client is not connected. + + + This is thrown when the server does not process the request. + + + This operation will timeout if the conversation is paused. + + + + + This requests data using the specified item name. + + + An item name supported by the current conversation. + + + The format of the data to return. + + + The amount of time in milliseconds to wait for a response. + + + The data returned by the server application. + + + This is thown when item exceeds 255 characters or timeout is negative. + + + This is thrown when item is a null reference. + + + This is thrown when the client is not connected. + + + This is thrown when the server does not process the request. + + + This operation will timeout if the conversation is paused. + + + + + This requests data using the specified item name. + + + An item name supported by the current conversation. + + + The format of the data to return. + + + The amount of time in milliseconds to wait for a response. + + + The data returned by the server application. + + + Zero if the operation succeeded or non-zero if the operation failed. + + + This operation will timeout if the conversation is paused. + + + + + This begins an asynchronous operation to request data using the specified item name. + + + An item name supported by the current conversation. + + + The format of the data to return. + + + The delegate to invoke when this operation completes. + + + An application defined data object to associate with this operation. + + + An IAsyncResult object for this operation. + + + This is thown when item exceeds 255 characters. + + + This is thrown when item is a null reference. + + + This is thrown when the client is not connected. + + + This is thrown when the asynchronous operation could not begin. + + + + + This gets the data returned by the server application for the operation. + + + The IAsyncResult object returned by a call to BeginRequest. + + + The data returned by the server application. + + + This is thown when asyncResult is an invalid IAsyncResult. + + + This is thrown when asyncResult is a null reference. + + + This is thrown when the server does not process the request. + + + + + + + + + This initiates an advise loop on the specified item name. + + + An item name supported by the current conversation. + + + The format of the data to return. + + + A bool indicating whether data should be included with the notification. + + + The amount of time in milliseconds to wait for a response. + + + + This is thown when item exceeds 255 characters or timeout is negative. + + + This is thrown when item is a null reference. + + + This is thrown when the item is already being advised or when the client is not connected. + + + This is thrown when the server does not initiate the advise loop. + + + This operation will timeout if the conversation is paused. + + + + + This initiates an advise loop on the specified item name. + + + An item name supported by the current conversation. + + + The format of the data to return. + + + A bool indicating whether data should be included with the notification. + + + A bool indicating whether the client should acknowledge each advisory before the server will send send another. + + + The amount of time in milliseconds to wait for a response. + + + An application defined data object to associate with this advise loop. + + + + This is thown when item exceeds 255 characters or timeout is negative. + + + This is thrown when item is a null reference. + + + This is thrown when the item is already being advised or when the client is not connected. + + + This is thrown when the server does not initiate the advise loop. + + + This operation will timeout if the conversation is paused. + + + + + + + + + This begins an asynchronous operation to initiate an advise loop on the specified item name. + + + An item name supported by the current conversation. + + + The format of the data to be returned. + + + A bool indicating whether data should be included with the notification. + + + The delegate to invoke when this operation completes. + + + An application defined data object to associate with this operation. + + + An IAsyncResult object for this operation. + + + + This is thown when item exceeds 255 characters. + + + This is thrown when item is a null reference. + + + This is thrown when the item is already being advised or when the client is not connected. + + + This is thrown when the asynchronous operation could not begin. + + + + + This begins an asynchronous operation to initiate an advise loop on the specified item name. + + + An item name supported by the current conversation. + + + The format of the data to be returned. + + + A bool indicating whether data should be included with the notification. + + + A bool indicating whether the client should acknowledge each advisory before the server will send send another. + + + The delegate to invoke when this operation completes. + + + An application defined data object to associate with this operation. + + + An application defined data object to associate with this advise loop. + + + An IAsyncResult object for this operation. + + + + This is thown when item exceeds 255 characters. + + + This is thrown when item is a null reference. + + + This is thrown when the item is already being advised or when the client is not connected. + + + This is thrown when the asynchronous operation could not begin. + + + + + This throws any exception that occurred during the operation. + + + The IAsyncResult object returned by a call to BeginPoke. + + + This is thown when asyncResult is an invalid IAsyncResult. + + + This is thrown when asyncResult is a null reference. + + + This is thrown when the server does not initiate the advise loop. + + + + + This terminates the advise loop for the specified item name. + + + An item name that has an active advise loop. + + + The amount of time in milliseconds to wait for a response. + + + This operation will timeout if the conversation is paused. + + + This is thown when item exceeds 255 characters or timeout is negative. + + + This is thrown when item is a null reference. + + + This is thrown when the item is not being advised or when the client is not connected. + + + This is thrown when the server does not terminate the advise loop. + + + + + This begins an asynchronous operation to terminate the advise loop for the specified item name. + + + An item name that has an active advise loop. + + + The delegate to invoke when this operation completes. + + + An application defined data object to associate with this operation. + + + An IAsyncResult object for this operation. + + + This is thown when item exceeds 255 characters. + + + This is thrown when item is a null reference. + + + This is thrown when the item is not being advised or when the client is not connected. + + + This is thrown when the asynchronous operation could not begin. + + + + + This throws any exception that occurred during the operation. + + + The IAsyncResult object returned by a call to BeginPoke. + + + This is thown when asyncResult is an invalid IAsyncResult. + + + This is thrown when asyncResult is a null reference. + + + This is thrown when the server does not terminate the advise loop. + + + + + This is raised when the data has changed for an item name that has an advise loop. + + + + + This is raised when the client has been disconnected. + + + + + + + + + + This gets the context associated with this instance. + + + + + This gets the service name associated with this conversation. + + + + + This gets the topic name associated with this conversation. + + + + + This gets the DDEML handle associated with this conversation. + + + + This can be used in any DDEML function requiring a conversation handle. + + + + Incorrect usage of the DDEML can cause this object to function incorrectly and can lead to resource leaks. + + + + + + + This gets a bool indicating whether this conversation is paused. + + + + + This gets a bool indicating whether the conversation is established. + + + + Do not assume that the conversation is still established after checking this property. The conversation can terminate at any time. + + + + + + + + + + + + This specifies the different kinds of DDE activity that can be monitored. + + + + + This indicates activity caused by the execution of a DDEML callback. + + + + + This indicates activity caused by conversation. + + + + + This indicates activity caused by an error. + + + + + This indicates activity caused by an advise loop. + + + + + This indicates activity caused by DDE messages. + + + + + This is used to monitor DDE activity. + + + + + This initializes a new instance of the DdeMonitor class. + + + + + This releases all resources held by this instance. + + + + + This starts monitoring the system for DDE activity. + + + A bitwise combination of DdeMonitorFlags that indicate what DDE activity will be monitored. + + + + + This is raised anytime a DDEML callback is executed. + + + + + This is raised anytime a conversation is established or terminated. + + + + + This is raised anytime there is an error. + + + + + This is raised anytime an advise loop is established or terminated. + + + + + This is raised anytime a DDE message is sent or posted. + + + + + This gets the context associated with this instance. + + + + + This contains the parameters of the DDEML callback function. + + + + + The dwRet property contains the value returned by the DDEML callback function and is the only member that can be modified. See the + MSDN documentation for more information about the members of this class. + + + + Incorrect usage of the DDEML can cause this library to function incorrectly and can lead to resource leaks. + + + + + + + + + + + + + See the MSDN documentation for information about this member. + + + + + See the MSDN documentation for information about this member. + + + + + See the MSDN documentation for information about this member. + + + + + See the MSDN documentation for information about this member. + + + + + See the MSDN documentation for information about this member. + + + + + See the MSDN documentation for information about this member. + + + + + See the MSDN documentation for information about this member. + + + + + See the MSDN documentation for information about this member. + + + + + This gets the return value of the DDEML callback function. See the MSDN documentation for information about this member. + + + This will be ignored if the PreFilterTransaction method returns false. + + + + + This contains information about the Register and Unregister events. + + + + + + This gets the service name associated with this event. + + + + + This class is needed to dispose of DDEML resources correctly since the DDEML is thread specific. + + + + + This represents the kind of message contained in DdeMessageActivityEventArgs. + + + + + The message was posted by a DDE application. + + + + + The message was sent by a DDE application. + + + + + This contains information about the MessageActivity event. + + + + + This gets the kind of message associated with this event. + + + + + This gets the message associated with this event. + + + + + This is a synchronizing object that can run a message loop on any thread. + + + + + + This initializes a new instance of the DdeMessageLoop class. + + + + + This releases all resources held by this instance. + + + + + This begins an asynchronous operation to execute a delegate on the thread hosting this object. + + + The delegate to execute. + + + The arguments to pass to the delegate. + + + An IAsyncResult object for this operation. + + + + + This returns the object that the delegate returned in the operation. + + + The IAsyncResult object returned by a call to BeginInvoke. + + + The object returned by the delegate. + + + + + This executes a delegate on the thread hosting this object. + + + The delegate to execute. + + + The arguments to pass to the delegate. + + + The object returned by the delegate. + + + + + This starts a message loop on the current thread. + + + + + This starts a message loop on the current thread and shows the specified form. + + + The Form to display. + + + + + This gets a bool indicating whether the caller must use Invoke. + + + + + + + + This represents a DDE conversation established on a DdeServer. + + + + + + This returns a string containing the current values of all properties. + + + A string containing the current values of all properties. + + + + + This gets the service name associated with this conversation. + + + + + This gets the topic name associated with this conversation. + + + + + This gets the DDEML handle associated with this conversation. + + + + This can be used in any DDEML function requiring a conversation handle. + + + + Incorrect usage of the DDEML can cause this object to function incorrectly and can lead to resource leaks. + + + + + + + This gets a bool indicating whether this conversation is paused. + + + + + This gets an application defined data object associated with this conversation. + + + Use this property to carry state information with the conversation. + + + + + This defines a transaction filter. + + + + Use a transaction filter to intercept the DDEML callback function. The PreFilterTransaction method will be called every time the + DDEML callback function executes. The Transaction object passed into the method contains the parameters of the DDE callback + function. By using a transaction filter the developer has compelete control over the DDEML. See the MSDN documentation for more + information on using the DDEML. + + + + Incorrect usage of the DDEML can cause this library to function incorrectly and can lead to resource leaks. + + + + + + + This filters a transaction before it is dispatched. + + + The transaction to be dispatched. + + + True to filter the transaction and stop it from being dispatched, false otherwise. + + + + This method is called everytime the DDEML callback function executes. + + + + Incorrect usage of the DDEML can cause this library to function incorrectly and can lead to resource leaks. + + + + + + + This provides an execution context for DdeClient and DdeServer. + + + + + This class provides a context for DDE activity. All DdeClient and DdeServer objects must be associated with an instance of + this class. If one is not specified in their constructors then a default instance of this class is used. This class must be initialized + before it can begin sending and receiving DDE messages. This happens automatically upon its first use by a DdeClient or + DdeServer. An application can call Initialize to make the initialization process occur immediately. This is useful when a + calling application expects this class to raise the Register and Unregister events or invoke the + ITransactionFilter.PreFilterTransaction method before being used by a DdeClient or DdeServer. + + + Since forms and controls implement ISynchronizeInvoke they can be used as the synchronizing object for this class. When an instance + of this class is created to use a form or control as the synchronizing object it will use the UI thread for execution. This is the + preferred way of creating an instance of this class when used in a windows application since it avoids multithreaded synchronization issues + and cross thread marshaling. When an instance of this class is created without specifying a synchronizing object it will create and manage + its own thread for execution. This is convenient if you wish to use this library in a console or service application, but with the added + cost of cross thread marshaling and the potential for deadlocking application threads. + + + Events are invoked on the thread hosting the DdeContext. All operations must be marshaled onto the thread hosting the + DdeContext. Method calls will block until that thread becomes available. An exception will be generated if the thread does not + become available in a timely manner. + + + + The following example demonstrates how to instantiate a DdeContext in a console application. + + using System; + using NDde.Advanced; + + public class Example + { + public static void Main() + { + // Create a context that uses a dedicated thread for DDE message pumping. + DdeContext context = new DdeContext(); + } + } + + + Imports NDde.Advanced + + Public Class Example + Public Shared Sub Main() + ' Create a context that uses a dedicated thread for DDE message pumping. + Dim context As DdeContext = New DdeContext() + End Sub + End Class + + The following example demonstrates how to instantiate a DdeContext in a windows application. + + using System; + using NDde.Advanced; + + public class Example : Form + { + // Standard Form code omitted for brevity. + + private DdeContext context = null; + + private void Form1_Load(object sender, System.EventArgs e) + { + // Create a context that uses the UI thread for DDE message pumping. + context = new DdeContext(this); + } + } + + + Imports NDde.Advanced + + Public Class Example + Inherits Form + + Private context as DdeContext = Nothing + + Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) + ' Create a context that uses the UI thread for DDE message pumping. + context = New DdeContext(Me) + End Sub + End Class + + + + + + + + + + This initializes a new instance of the DdeContext class that uses a dedicated thread for execution. + + + This constructor is used when you want the context to create and manage its own thread for DDE message pumping. + + + + + This initializes a new instance of the DdeContext class that uses the specified synchronizing object for execution. + + + The synchronizing object to use for execution. + + + This is thrown when synchronizer is a null reference. + + + This constructor is used when you want the context to use the specified synchronizing object for DDE message pumping. Since forms and + controls implement ISynchronizeInvoke they can be used as the synchronizing object. In that case the windows application UI + thread that is hosting the form or control is used. + + + + + This releases all resources held by this instance. + + + + + This initializes the context. + + + This is thrown when the context is already initialized. + + + This is thrown when the context could not be initialized. + + + + This class must be initialized before it can begin sending and receiving DDE messages. This happens automatically upon its first use by + a DdeClient or DdeServer. An application can call Initialize to make the initialization process occur immediately. + This is useful when a calling application expects this class to raise the Register and Unregister events or invoke the + ITransactionFilter.PreFilterTransaction method before being used by a DdeClient or DdeServer. + + + If you attempt to use a synchronizer that is not hosted on a thread running a windows message loop an exception will be thrown. + + + Explicitly calling this method will allow added ITransactionFilter objects to begin intercepting the DDEML callback function. + + + + + + This adds a transaction filter to monitor DDE transactions. + + + The implementation of ITransactionFilter that you want to add. + + + This is thrown when filter is a null reference. + + + This is thrown when the filter was already added. + + + + Transaction filters can be used to intercept the DDEML callback. + + + + Incorrect usage of the DDEML can cause this library to function incorrectly and can lead to resource leaks. + + + + + + + This removes a transaction filter and stops it from monitoring DDE transactions. + + + The implementation of ITransactionFilter that you want to remove. + + + This is thrown when filter is a null reference. + + + This is thrown when the filter was not previously added. + + + + Transaction filters can be used to intercept the DDEML callback. + + + + Incorrect usage of the DDEML can cause this library to function incorrectly and can lead to resource leaks. + + + + + + + This executes a ThreadStart delegate on the thread hosting this object. + + + The delegate to execute. + + + + + This executes a delegate on the thread hosting this object. + + + The delegate to execute. + + + The arguments to pass to the delegate. + + + The object returned by the delegate. + + + + + This begins an asynchronous operation to execute a delegate on the thread hosting this object. + + + The delegate to execute. + + + The arguments to pass to the delegate. + + + An IAsyncResult object for this operation. + + + + + This returns the object that the delegate returned in the operation. + + + The IAsyncResult object returned by a call to BeginInvoke. + + + The object returned by the delegate. + + + + + This is raised when a service name has been registered by a server using the DDEML. + + + This event will not be raised by servers that do not use the DDEML. + + + + + This is raised when a service name has been unregistered by a server using the DDEML. + + + This event will not be raised by servers that do not use the DDEML. + + + + + + + + + + This gets the DDEML instance identifier. + + + + This can be used in any DDEML function requiring an instance identifier. + + + + Incorrect usage of the DDEML can cause this library to function incorrectly and can lead to resource leaks. + + + + + + + This gets a bool indicating whether the context is initialized. + + + + + This gets or sets the default encoding that is used. + + + + + This gets a bool indicating whether the caller must use Invoke. + + + + + + + + + + + + + + This contains information about the ErrorActivity event. + + + + + This gets an error code returned by the DDEML. + + + + + This contains information about the ConversationActivity event. + + + + + This gets the service name associated with the conversation. + + + + + This gets the topic name associated with the conversation. + + + + + This gets a bool indicating whether the conversation is being established. + + + The value returned by this property will be true if the conversation is being established. If the conversation + is being terminated then the value will be false. + + + + + This gets the handle to the client application associated with the conversation. + + + + + This gets the handle to the server application associated with the conversation. + + + + + A strongly-typed resource class, for looking up localized strings, etc. + + + + + Returns the cached ResourceManager instance used by this class. + + + + + Overrides the current thread's CurrentUICulture property for all + resource lookups using this strongly typed resource class. + + + + + Looks up a localized string similar to The server failed to advise "${service}|${topic}!${item}".. + + + + + Looks up a localized string similar to An advise loop for "${service}|${topic}!${item}" already exists.. + + + + + Looks up a localized string similar to The client is already connected.. + + + + + Looks up a localized string similar to The context is already intialized.. + + + + + Looks up a localized string similar to The specified conversation is already paused.. + + + + + Looks up a localized string similar to The service is already registered.. + + + + + Looks up a localized string similar to The IAsyncResult must have been returned by a call to ${method}.. + + + + + Looks up a localized string similar to The client failed to pause the conversation.. + + + + + Looks up a localized string similar to The client failed to resume the conversation.. + + + + + Looks up a localized string similar to The client failed to connect to "${service}|${topic}". Make sure the server application is running and that it supports the specified service name and topic name pair.. + + + + + Looks up a localized string similar to The client failed to execute "${command}".. + + + + + Looks up a localized string similar to The transaction filter has already been added.. + + + + + Looks up a localized string similar to The transaction filter has not been added.. + + + + + Looks up a localized string similar to The context failed to initialize.. + + + + + Looks up a localized string similar to The context timed out attempting to marshal the operation.. + + + + + Looks up a localized string similar to The context is not hosted on a thread with a message loop.. + + + + + Looks up a localized string similar to An advise loop for "${service}|${topic}!${item}" does not exist.. + + + + + Looks up a localized string similar to The client is not connected.. + + + + + Looks up a localized string similar to The context is not initialized.. + + + + + Looks up a localized string similar to The specified conversation is not paused.. + + + + + Looks up a localized string similar to The service is not registered.. + + + + + Looks up a localized string similar to The client failed to poke "${service}|${topic}!${item}".. + + + + + Looks up a localized string similar to The server failed to register "${service}".. + + + + + Looks up a localized string similar to The client failed to request "${service}|${topic}!${item}".. + + + + + Looks up a localized string similar to The server failed to pause all conversations.. + + + + + Looks up a localized string similar to The server failed to pause the specified conversation.. + + + + + Looks up a localized string similar to The server failed to resume all conversations.. + + + + + Looks up a localized string similar to The server failed to resume the specified conversation.. + + + + + Looks up a localized string similar to The client failed to initiate an advise loop for "${service}|${topic}!${item}".. + + + + + Looks up a localized string similar to The client failed to terminate the advise loop for "${service}|${topic}!${item}".. + + + + + Looks up a localized string similar to The parameter must be <= 255 characters.. + + + + + Looks up a localized string similar to The parameter must be > 0.. + + + + + Looks up a localized string similar to An unknown error occurred.. + + + + + This is thrown when a DDE exception occurs. + + + + + + + + + + + + + + + + + + This gets an error code returned by the DDEML. + + + + The value is zero if the exception was not thrown because of the DDEML. + + + + 0x0000DMLERR_NO_DMLERROR + 0x4000DMLERR_ADVACKTIMEOUT + 0x4001DMLERR_BUSY + 0x4002DMLERR_DATAACKTIMEOUT + 0x4003DMLERR_DLL_NOT_INITIALIZED + 0x4004DMLERR_DLL_USAGE + 0x4005DMLERR_EXECACKTIMEOUT + 0x4006DMLERR_INVALIDPARAMETER + 0x4007DMLERR_LOW_MEMORY + 0x4008DMLERR_MEMORY_DMLERROR + 0x4009DMLERR_NOTPROCESSED + 0x400ADMLERR_NO_CONV_ESTABLISHED + 0x400BDMLERR_POKEACKTIMEOUT + 0x400CDMLERR_POSTMSG_FAILED + 0x400DDMLERR_REENTRANCY + 0x400EDMLERR_SERVER_DIED + 0x400FDMLERR_SYS_DMLERROR + 0x4010DMLERR_UNADVACKTIMEOUT + 0x4011DMLERR_UNFOUND_QUEUE_ID + + + + + + + This contains information about the LinkActivity event. + + + + + This gets the service name associated with the link. + + + + + This gets the topic name associated with the link. + + + + + This gets the item name associated with the link. + + + + + This gets the format of the data associated with the link. + + + + + This gets a bool indicating whether the link is hot. + + + + + This gets a bool indicating whether the link is being established. + + + The value returned by this property will be true if the conversation is being established. If the conversation + is being terminated then the value will be false. + + + + + This gets a bool indicating whether the link was terminated by the server. + + + + + This gets the handle to the client application associated with the link. + + + + + This gets the handle to the server application associated with the link. + + + + + This represents the server side of DDE conversations. + + + + + DDE conversations are established by specifying a service name and topic name pair. The service name is usually the name of the application + acting as a DDE server. A DDE server can respond to multiple service names, but most servers usually only respond to one. The topic name + is a logical context for data and is defined by the server application. A server can and usually does support many topic names. + + + After this object has registered its service name by calling the Register method clients can connect to it by specifying the service + name the server registered and a topic name that it supports. + + + Event methods are invoked on the thread hosting the DdeContext. All operations must be marshaled onto the thread hosting the + DdeContext associated with this object. Method calls will block until that thread becomes available. An exception will be generated + if the thread does not become available in a timely manner. + + + + The event methods must be overridden in a subclass as needed. + + + + + The following example demostrates how to use a DdeServer. + + using System; + using System.Collections; + using System.Timers; + using NDde.Server; + + public class Server + { + public static void Main() + { + try + { + // Create a server that will register the service name 'myapp'. + using (DdeServer server = new MyServer("myapp")) + { + // Register the service name. + server.Register(); + + // Wait for the user to press ENTER before proceding. + Console.WriteLine("Press ENTER to quit..."); + Console.ReadLine(); + } + } + catch (Exception e) + { + Console.WriteLine(e); + Console.WriteLine("Press ENTER to quit..."); + Console.ReadLine(); + } + } + + private sealed class MyServer : DdeServer + { + private System.Timers.Timer _Timer = new System.Timers.Timer(); + + public MyServer(string service) : base(service) + { + // Create a timer that will be used to advise clients of new data. + _Timer.Elapsed += this.OnTimerElapsed; + _Timer.Interval = 1000; + _Timer.SynchronizingObject = this.Context; + _Timer.Start(); + } + + private void OnTimerElapsed(object sender, ElapsedEventArgs args) + { + // Advise all topic name and item name pairs. + Advise("*", "*"); + } + + protected override bool OnBeforeConnect(string topic) + { + Console.WriteLine("OnBeforeConnect:".PadRight(16) + + " Service='" + base.Service + "'" + + " Topic='" + topic + "'"); + + return true; + } + + protected override void OnAfterConnect(DdeConversation conversation) + { + Console.WriteLine("OnAfterConnect:".PadRight(16) + + " Service='" + conversation.Service + "'" + + " Topic='" + conversation.Topic + "'" + + " Handle=" + conversation.Handle.ToString()); + } + + protected override void OnDisconnect(DdeConversation conversation) + { + Console.WriteLine("OnDisconnect:".PadRight(16) + + " Service='" + conversation.Service + "'" + + " Topic='" + conversation.Topic + "'" + + " Handle=" + conversation.Handle.ToString()); + } + + protected override bool OnStartAdvise(DdeConversation conversation, string item, int format) + { + Console.WriteLine("OnStartAdvise:".PadRight(16) + + " Service='" + conversation.Service + "'" + + " Topic='" + conversation.Topic + "'" + + " Handle=" + conversation.Handle.ToString() + + " Item='" + item + "'" + + " Format=" + format.ToString()); + + // Initiate the advisory loop only if the format is CF_TEXT. + return format == 1; + } + + protected override void OnStopAdvise(DdeConversation conversation, string item) + { + Console.WriteLine("OnStopAdvise:".PadRight(16) + + " Service='" + conversation.Service + "'" + + " Topic='" + conversation.Topic + "'" + + " Handle=" + conversation.Handle.ToString() + + " Item='" + item + "'"); + } + + protected override ExecuteResult OnExecute(DdeConversation conversation, string command) + { + Console.WriteLine("OnExecute:".PadRight(16) + + " Service='" + conversation.Service + "'" + + " Topic='" + conversation.Topic + "'" + + " Handle=" + conversation.Handle.ToString() + + " Command='" + command + "'"); + + // Tell the client that the command was processed. + return ExecuteResult.Processed; + } + + protected override PokeResult OnPoke(DdeConversation conversation, string item, byte[] data, int format) + { + Console.WriteLine("OnPoke:".PadRight(16) + + " Service='" + conversation.Service + "'" + + " Topic='" + conversation.Topic + "'" + + " Handle=" + conversation.Handle.ToString() + + " Item='" + item + "'" + + " Data=" + data.Length.ToString() + + " Format=" + format.ToString()); + + // Tell the client that the data was processed. + return PokeResult.Processed; + } + + protected override RequestResult OnRequest(DdeConversation conversation, string item, int format) + { + Console.WriteLine("OnRequest:".PadRight(16) + + " Service='" + conversation.Service + "'" + + " Topic='" + conversation.Topic + "'" + + " Handle=" + conversation.Handle.ToString() + + " Item='" + item + "'" + + " Format=" + format.ToString()); + + // Return data to the client only if the format is CF_TEXT. + if (format == 1) + { + return new RequestResult(System.Text.Encoding.ASCII.GetBytes("Time=" + DateTime.Now.ToString() + "\0")); + } + return RequestResult.NotProcessed; + } + + protected override byte[] OnAdvise(string topic, string item, int format) + { + Console.WriteLine("OnAdvise:".PadRight(16) + + " Service='" + this.Service + "'" + + " Topic='" + topic + "'" + + " Item='" + item + "'" + + " Format=" + format.ToString()); + + // Send data to the client only if the format is CF_TEXT. + if (format == 1) + { + return System.Text.Encoding.ASCII.GetBytes("Time=" + DateTime.Now.ToString() + "\0"); + } + return null; + } + + } // class + + } // class + + + Imports NDde.Server + + Module Program + + Sub Main() + + Try + + ' Create a server that will register the service name 'myapp'. + Using server As DdeServer = New MyServer("myapp") + + ' Register the service name. + server.Register() + + ' Wait for the user to press ENTER before proceding. + Console.WriteLine("Press ENTER to quit...") + Console.ReadLine() + + End Using + + Catch e As Exception + + Console.WriteLine(e) + Console.WriteLine("Press ENTER to quit...") + Console.ReadLine() + + End Try + + End Sub + + Private Class MyServer + Inherits DdeServer + + Private WithEvents theTimer As System.Timers.Timer = New System.Timers.Timer() + + Public Sub New(ByVal service As String) + MyBase.New(service) + ' Create a timer that will be used to advise clients of new data. + theTimer.Interval = 1000 + theTimer.SynchronizingObject = Me.Context + theTimer.Start() + End Sub + + Private Sub theTimer_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) Handles theTimer.Elapsed + Me.Advise("*", "*") + End Sub + + Protected Overrides Function OnBeforeConnect(ByVal topic As String) As Boolean + Console.WriteLine("OnBeforeConnect:".PadRight(16) _ + + " Service='" + MyBase.Service + "'" _ + + " Topic='" + topic + "'") + + Return True + End Function + + Protected Overrides Sub OnAfterConnect(ByVal conversation As DdeConversation) + Console.WriteLine("OnAfterConnect:".PadRight(16) _ + + " Service='" + conversation.Service + "'" _ + + " Topic='" + conversation.Topic + "'" _ + + " Handle=" + conversation.Handle.ToString()) + End Sub + + Protected Overrides Sub OnDisconnect(ByVal conversation As DdeConversation) + Console.WriteLine("OnDisconnect:".PadRight(16) _ + + " Service='" + conversation.Service + "'" _ + + " Topic='" + conversation.Topic + "'" _ + + " Handle=" + conversation.Handle.ToString()) + End Sub + + Protected Overrides Function OnStartAdvise(ByVal conversation As DdeConversation, ByVal item As String, ByVal format As Integer) As Boolean + Console.WriteLine("OnStartAdvise:".PadRight(16) _ + + " Service='" + conversation.Service + "'" _ + + " Topic='" + conversation.Topic + "'" _ + + " Handle=" + conversation.Handle.ToString() _ + + " Item='" + item + "'" _ + + " Format=" + format.ToString()) + + ' Initiate the advisory loop only if the format is CF_TEXT. + Return format = 1 + End Function + + Protected Overrides Sub OnStopAdvise(ByVal conversation As DdeConversation, ByVal item As String) + Console.WriteLine("OnStopAdvise:".PadRight(16) _ + + " Service='" + conversation.Service + "'" _ + + " Topic='" + conversation.Topic + "'" _ + + " Handle=" + conversation.Handle.ToString() _ + + " Item='" + item + "'") + End Sub + + Protected Overrides Function OnExecute(ByVal conversation As DdeConversation, ByVal command As String) As ExecuteResult + Console.WriteLine("OnExecute:".PadRight(16) _ + + " Service='" + conversation.Service + "'" _ + + " Topic='" + conversation.Topic + "'" _ + + " Handle=" + conversation.Handle.ToString() _ + + " Command='" + command + "'") + + ' Tell the client that the command was processed. + Return ExecuteResult.Processed + End Function + + Protected Overrides Function OnPoke(ByVal conversation As DdeConversation, ByVal item As String, ByVal data As Byte(), ByVal format As Integer) As PokeResult + Console.WriteLine("OnPoke:".PadRight(16) _ + + " Service='" + conversation.Service + "'" _ + + " Topic='" + conversation.Topic + "'" _ + + " Handle=" + conversation.Handle.ToString() _ + + " Item='" + item + "'" _ + + " Data=" + data.Length.ToString() _ + + " Format=" + format.ToString()) + + ' Tell the client that the data was processed. + Return PokeResult.Processed + End Function + + Protected Overrides Function OnRequest(ByVal conversation As DdeConversation, ByVal item As String, ByVal format As Integer) As RequestResult + Console.WriteLine("OnRequest:".PadRight(16) _ + + " Service='" + conversation.Service + "'" _ + + " Topic='" + conversation.Topic + "'" _ + + " Handle=" + conversation.Handle.ToString() _ + + " Item='" + item + "'" _ + + " Format=" + format.ToString()) + + ' Return data to the client only if the format is CF_TEXT. + If format = 1 Then + Return New RequestResult(System.Text.Encoding.ASCII.GetBytes("Time=" + DateTime.Now.ToString() + Convert.ToChar(0))) + End If + Return RequestResult.NotProcessed + End Function + + Protected Overrides Function OnAdvise(ByVal topic As String, ByVal item As String, ByVal format As Integer) As Byte() + Console.WriteLine("OnAdvise:".PadRight(16) _ + + " Service='" + Me.Service + "'" _ + + " Topic='" + topic + "'" _ + + " Item='" + item + "'" _ + + " Format=" + format.ToString()) + + ' Send data to the client only if the format is CF_TEXT. + If format = 1 Then + Return System.Text.Encoding.ASCII.GetBytes("Time=" + DateTime.Now.ToString() + Convert.ToChar(0)) + End If + Return Nothing + End Function + + End Class + + End Module + + + + + + + + + + This initializes a new instance of the DdeServer class that can register the specified service name. + + + The service name that this instance can register. + + + This is thown when service exceeds 255 characters.. + + + This is thrown when service is a null reference. + + + + + This initializes a new instance of the DdeServer class that can register the specified service name and using the specified + synchronizing object. + + + The service name that this instance can register. + + + The synchronizing object to use for this instance. + + + This is thown when service exceeds 255 characters.. + + + This is thrown when service is a null reference. + + + + + This initializes a new instance of the DdeServer class that can register the specified service name and uses the specified + context. + + + The service name that this instance can register. + + + The context to use for execution. + + + This is thown when service exceeds 255 characters.. + + + This is thrown when service is a null reference. + + + + + This unregisters service name and releases all resources held by this instance. + + + + + This contains the implementation to release all resources held by this instance. + + + True if called by Dispose, false otherwise. + + + + + This registers the service name. + + + This is thrown when the server is already registered. + + + This is thrown when the service name could not be registered. + + + + + This unregisters the service name. + + + This is thrown when the server is not registered. + + + + + This notifies all clients that data has changed for the specified topic name and item name pair. + + + A topic name supported by this server. + + + An item name supported by this server. + + + This is thown when topic or item exceeds 255 characters.. + + + This is thrown when topic or item is a null reference. + + + This is thrown when the server is not registered. + + + This is thrown when the notification could not be posted. + + + Use an asterix to indicate that the topic name, item name, or both should be wild. + + + + + + + + Pausing a conversation causes this server to queue events until the conversation resumes. + + + + This pauses the specified conversation. + + + The conversation to pause. + + + This is thrown when conversation is a null reference. + + + This is thrown when the conversation is already paused or when the server is not registered. + + + This is thrown when the conversation could not be paused. + + + + + This pauses all conversations. + + + This is thrown when the server is not registered. + + + This is thrown when the conversations could not be paused. + + + Pausing a conversation causes this object to queue events until the conversation resumes. + + + + + + + + + This resumes the specified conversation. + + + The conversation to resume. + + + This is thrown when conversation is a null reference. + + + This is thrown when the conversation is not paused or when the server is not registered. + + + This is thrown when the conversation could not be resumed. + + + + + This resumes all conversations. + + + This is thrown when the server is not registered. + + + This is thrown when the conversations could not be resumed. + + + + + + + + + This terminates the specified conversation. + + + The conversation to terminate. + + + This is thrown when conversation is a null reference. + + + This is thrown when the server is not registered. + + + This is thrown when the conversation could not be terminated. + + + + + This terminates all conversations. + + + This is thrown when the server is not registered. + + + This is thrown when the conversations could not be terminated. + + + + + This is invoked when a client attempts to initiate an advise loop. + + + The conversation associated with this event. + + + The item name associated with this event. + + + The format of the data. + + + True to allow the advise loop, false otherwise. + + + The default implementation accepts all advise loops. + + + + + This is invoked when a client terminates an advise loop. + + + The conversation associated with this event. + + + The item name associated with this event. + + + + + This is invoked when a client attempts to establish a conversation. + + + The topic name associated with this event. + + + True to allow the connection, false otherwise. + + + The default implementation accepts all connections. + + + + + This is invoked when a client has successfully established a conversation. + + + The conversation associated with this event. + + + + + This is invoked when a client terminates a conversation. + + + The conversation associated with this event. + + + + + This is invoked when a client sends a command. + + + The conversation associated with this event. + + + The command to be executed. + + + An ExecuteResult indicating the result. + + + The default implementation returns ExecuteResult.NotProcessed to the client. + + + + + This is invoked when a client sends data. + + + The conversation associated with this event. + + + The item name associated with this event. + + + The data associated with this event. + + + The format of the data. + + + A PokeResult indicating the result. + + + The default implementation returns PokeResult.NotProcessed to the client. + + + + + + + + + This is invoked when a client attempts to request data. + + + The conversation associated with this event. + + + The item name associated with this event. + + + The format of the data. + + + A RequestResult indicating the result. + + + The default implementation returns RequestResult.NotProcessed to the client. + + + + + This is invoked when the server is performing a hot advise. + + + The topic name associated with this event. + + + The item name associated with this event. + + + The format of the data. + + + The data that will be sent to the clients. + + + The default implementation sends nothing to the clients. + + + + + + + + + + This gets the context associated with his instance. + + + + + This gets the service name associated with this server. + + + + + This gets a bool indicating whether the service name is registered. + + + + + This is the return value of the OnExecute method. + + + + + Return this value if the command was executed successfully. + + + + + Return this value if the command was not executed successfully. + + + + + Return this value if the server is too busy. + + + + + Return this value to pause the conversation and execute the command asynchronously. After the conversation has been resumed the + OnExecute method will run again. + + + + + This determines whether two object instances are equal. + + + The object to compare with the current object. + + + True if the specified object is equal to the current object, false otherwise. + + + + + This returns a hash code for the object. + + + A hash code for the object. + + + + + This determines whether two ExecuteResult objects are equal. + + + The left hand side object. + + + The right hand side object. + + True if the two objects are equal, false otherwise. + + + + + This determines whether two ExecuteResult objects are not equal. + + + The left hand side object. + + + The right hand side object. + + True if the two objects are not equal, false otherwise. + + + + + This is the return value of the OnPoke method. + + + + + Return this value if the poke was successful. + + + + + Return this value if the poke was not successful. + + + + + Return this value if the server is too busy. + + + + + Return this value to pause the conversation and execute the poke asynchronously. After the conversation has been resumed the + OnPoke method will run again. + + + + + This determines whether two object instances are equal. + + + The object to compare with the current object. + + + True if the specified object is equal to the current object, false otherwise. + + + + + This returns a hash code for the object. + + + A hash code for the object. + + + + + This determines whether two PokeResult objects are equal. + + + The left hand side object. + + + The right hand side object. + + True if the two objects are equal, false otherwise. + + + + + This determines whether two ExecuteResult objects are not equal. + + + The left hand side object. + + + The right hand side object. + + True if the two objects are not equal, false otherwise. + + + + + This is the return value of the OnRequest method. + + + + + Return this value if the request was not successful. + + + + + Return this value to pause the conversation and execute the request asynchronously. After the conversation has been resumed the + OnRequest method will run again. + + + + + This initializes the RequestResult struct with the data to return to the client. + + + The data to return to the client. + + + + + This determines whether two object instances are equal. + + + The object to compare with the current object. + + + True if the specified object is equal to the current object, false otherwise. + + + + + This returns a hash code for the object. + + + A hash code for the object. + + + + + This determines whether two RequestResult objects are equal. + + + The left hand side object. + + + The right hand side object. + + True if the two objects are equal, false otherwise. + + + + + This determines whether two ExecuteResult objects are not equal. + + + The left hand side object. + + + The right hand side object. + + True if the two objects are not equal, false otherwise. + + + + + The data to send to the client application. + + + + + This contains information about the Advise event. + + + + + + This gets the item name associated with this notification. + + + + + This gets the format of the data included in this notification. + + + + + This gets an application defined data object associated with this advise loop. + + + + + This gets the data associated with this notification or null if this is not a hot advise loop. + + + + + This gets the text associated with this notification or null if this is not a hot advise loop. + + + + diff --git a/NDDE/NDde/Documentation/Documentation.chm b/NDDE/NDde/Documentation/Documentation.chm new file mode 100644 index 0000000..1fe9b8b Binary files /dev/null and b/NDDE/NDde/Documentation/Documentation.chm differ diff --git a/NDDE/NDde/Documentation/License.doc b/NDDE/NDde/Documentation/License.doc new file mode 100644 index 0000000..90946cd Binary files /dev/null and b/NDDE/NDde/Documentation/License.doc differ diff --git a/NDDE/NDde/Documentation/NDde.ndoc b/NDDE/NDde/Documentation/NDde.ndoc new file mode 100644 index 0000000..a63bb37 --- /dev/null +++ b/NDDE/NDde/Documentation/NDde.ndoc @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NDDE/NDde/Documentation/Readme.doc b/NDDE/NDde/Documentation/Readme.doc new file mode 100644 index 0000000..684437b Binary files /dev/null and b/NDDE/NDde/Documentation/Readme.doc differ diff --git a/NDDE/NDde/Samples/cs/Client/Client.csproj b/NDDE/NDde/Samples/cs/Client/Client.csproj new file mode 100644 index 0000000..241608a --- /dev/null +++ b/NDDE/NDde/Samples/cs/Client/Client.csproj @@ -0,0 +1,58 @@ + + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {04E46B25-B029-488B-BD1B-9E4AB12E72E3} + Exe + Properties + Client + Client + v4.0 + + + + + 2.0 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\..\..\Binary\NDde.dll + + + + + + + + + + + + \ No newline at end of file diff --git a/NDDE/NDde/Samples/cs/Client/Program.cs b/NDDE/NDde/Samples/cs/Client/Program.cs new file mode 100644 index 0000000..4e074cf --- /dev/null +++ b/NDDE/NDde/Samples/cs/Client/Program.cs @@ -0,0 +1,146 @@ +using System; +using System.Text; +using NDde.Client; + +namespace Client +{ + public sealed class Client + { + public static void Main(string[] args) + { + // Wait for the user to press ENTER before proceding. + Console.WriteLine("The Server sample must be running before the client can connect."); + Console.WriteLine("Press ENTER to continue..."); + Console.ReadLine(); + try + { + // Create a client that connects to 'myapp|mytopic'. + using (DdeClient client = new DdeClient("myapp", "mytopic")) + { + // Subscribe to the Disconnected event. This event will notify the application when a conversation has been terminated. + client.Disconnected += OnDisconnected; + + // Connect to the server. It must be running or an exception will be thrown. + client.Connect(); + + // Synchronous Execute Operation + client.Execute("mycommand", 60000); + + // Synchronous Poke Operation + client.Poke("myitem", DateTime.Now.ToString(), 60000); + + // Syncronous Request Operation + Console.WriteLine("Request: " + client.Request("myitem", 60000)); + + // Asynchronous Execute Operation + client.BeginExecute("mycommand", OnExecuteComplete, client); + + // Asynchronous Poke Operation + client.BeginPoke("myitem", Encoding.ASCII.GetBytes(DateTime.Now.ToString() + "\0"), 1, OnPokeComplete, client); + + // Asynchronous Request Operation + client.BeginRequest("myitem", 1, OnRequestComplete, client); + + // Advise Loop + client.StartAdvise("myitem", 1, true, 60000); + client.Advise += OnAdvise; + + // Wait for the user to press ENTER before proceding. + Console.WriteLine("Press ENTER to quit..."); + Console.ReadLine(); + } + } + catch (Exception e) + { + Console.WriteLine(e.ToString()); + Console.WriteLine("Press ENTER to quit..."); + Console.ReadLine(); + } + } + + private static void OnExecuteComplete(IAsyncResult ar) + { + try + { + DdeClient client = (DdeClient)ar.AsyncState; + client.EndExecute(ar); + Console.WriteLine("OnExecuteComplete"); + } + catch (Exception e) + { + Console.WriteLine("OnExecuteComplete: " + e.Message); + } + } + + private static void OnPokeComplete(IAsyncResult ar) + { + try + { + DdeClient client = (DdeClient)ar.AsyncState; + client.EndPoke(ar); + Console.WriteLine("OnPokeComplete"); + } + catch (Exception e) + { + Console.WriteLine("OnPokeComplete: " + e.Message); + } + } + + private static void OnRequestComplete(IAsyncResult ar) + { + try + { + DdeClient client = (DdeClient)ar.AsyncState; + byte[] data = client.EndRequest(ar); + Console.WriteLine("OnRequestComplete: " + Encoding.ASCII.GetString(data)); + } + catch (Exception e) + { + Console.WriteLine("OnRequestComplete: " + e.Message); + } + } + + private static void OnStartAdviseComplete(IAsyncResult ar) + { + try + { + DdeClient client = (DdeClient)ar.AsyncState; + client.EndStartAdvise(ar); + Console.WriteLine("OnStartAdviseComplete"); + } + catch (Exception e) + { + Console.WriteLine("OnStartAdviseComplete: " + e.Message); + } + } + + private static void OnStopAdviseComplete(IAsyncResult ar) + { + try + { + DdeClient client = (DdeClient)ar.AsyncState; + client.EndStopAdvise(ar); + Console.WriteLine("OnStopAdviseComplete"); + } + catch (Exception e) + { + Console.WriteLine("OnStopAdviseComplete: " + e.Message); + } + } + + private static void OnAdvise(object sender, DdeAdviseEventArgs args) + { + Console.WriteLine("OnAdvise: " + args.Text); + } + + private static void OnDisconnected(object sender, DdeDisconnectedEventArgs args) + { + Console.WriteLine( + "OnDisconnected: " + + "IsServerInitiated=" + args.IsServerInitiated.ToString() + " " + + "IsDisposed=" + args.IsDisposed.ToString()); + } + + } // class + +} // namespace diff --git a/NDDE/NDde/Samples/cs/Client/Properties/AssemblyInfo.cs b/NDDE/NDde/Samples/cs/Client/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..dacc6f9 --- /dev/null +++ b/NDDE/NDde/Samples/cs/Client/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +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("Client")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Client")] +[assembly: AssemblyCopyright("Copyright © 2005")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("6641cbf8-926a-481a-a4b3-f7ab1e75efc6")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/NDDE/NDde/Samples/cs/ClientWin/ClientWin.csproj b/NDDE/NDde/Samples/cs/ClientWin/ClientWin.csproj new file mode 100644 index 0000000..452725b --- /dev/null +++ b/NDDE/NDde/Samples/cs/ClientWin/ClientWin.csproj @@ -0,0 +1,90 @@ + + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {8FD4456A-9C49-46ED-9EB4-155C2D738730} + WinExe + Properties + ClientWin + ClientWin + v4.0 + + + + + 2.0 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\..\..\Binary\NDde.dll + + + + + + + + + + + Form + + + MainForm.cs + + + + + Designer + MainForm.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + \ No newline at end of file diff --git a/NDDE/NDde/Samples/cs/ClientWin/MainForm.Designer.cs b/NDDE/NDde/Samples/cs/ClientWin/MainForm.Designer.cs new file mode 100644 index 0000000..6b19b7c --- /dev/null +++ b/NDDE/NDde/Samples/cs/ClientWin/MainForm.Designer.cs @@ -0,0 +1,64 @@ +namespace ClientWin +{ + partial class MainForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.displayTextBox = new System.Windows.Forms.TextBox(); + this.SuspendLayout(); + // + // displayTextBox + // + this.displayTextBox.Location = new System.Drawing.Point(11, 11); + this.displayTextBox.Name = "displayTextBox"; + this.displayTextBox.ReadOnly = true; + this.displayTextBox.Size = new System.Drawing.Size(270, 20); + this.displayTextBox.TabIndex = 0; + // + // MainForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(292, 41); + this.Controls.Add(this.displayTextBox); + this.Name = "MainForm"; + this.Padding = new System.Windows.Forms.Padding(8); + this.Text = "DDE Sample Application"; + this.Resize += new System.EventHandler(this.MainForm_Resize); + this.Load += new System.EventHandler(this.MainForm_Load); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.TextBox displayTextBox; + + } +} + diff --git a/NDDE/NDde/Samples/cs/ClientWin/MainForm.cs b/NDDE/NDde/Samples/cs/ClientWin/MainForm.cs new file mode 100644 index 0000000..8fda4ff --- /dev/null +++ b/NDDE/NDde/Samples/cs/ClientWin/MainForm.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using NDde.Client; + +namespace ClientWin +{ + public partial class MainForm : Form + { + private DdeClient client; + + public MainForm() + { + InitializeComponent(); + + client = new DdeClient("myapp", "myservice", this); + client.Advise += client_Advise; + client.Disconnected += client_Disconnected; + } + + private void MainForm_Load(object sender, EventArgs e) + { + try + { + // Connect to the server. It must be running or an exception will be thrown. + client.Connect(); + + // Advise Loop + client.StartAdvise("myitem", 1, true, 60000); + } + catch (Exception ex) + { + displayTextBox.Text = "MainForm_Load: " + ex.Message; + } + } + + private void MainForm_Resize(object sender, EventArgs e) + { + displayTextBox.Left = this.DisplayRectangle.Left; + displayTextBox.Width = this.DisplayRectangle.Width; + displayTextBox.Top = this.DisplayRectangle.Top; + displayTextBox.Height = this.DisplayRectangle.Height; + } + + private void client_Advise(object sender, DdeAdviseEventArgs args) + { + displayTextBox.Text = "OnAdvise: " + args.Text; + } + + private void client_Disconnected(object sender, DdeDisconnectedEventArgs args) + { + displayTextBox.Text = + "OnDisconnected: " + + "IsServerInitiated=" + args.IsServerInitiated.ToString() + " " + + "IsDisposed=" + args.IsDisposed.ToString(); + } + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Samples/cs/ClientWin/MainForm.resx b/NDDE/NDde/Samples/cs/ClientWin/MainForm.resx new file mode 100644 index 0000000..19dc0dd --- /dev/null +++ b/NDDE/NDde/Samples/cs/ClientWin/MainForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/NDDE/NDde/Samples/cs/ClientWin/Program.cs b/NDDE/NDde/Samples/cs/ClientWin/Program.cs new file mode 100644 index 0000000..5e6f6ea --- /dev/null +++ b/NDDE/NDde/Samples/cs/ClientWin/Program.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Windows.Forms; + +namespace ClientWin +{ + static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new MainForm()); + } + } +} \ No newline at end of file diff --git a/NDDE/NDde/Samples/cs/ClientWin/Properties/AssemblyInfo.cs b/NDDE/NDde/Samples/cs/ClientWin/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..efc09c3 --- /dev/null +++ b/NDDE/NDde/Samples/cs/ClientWin/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +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("ClientWin")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ClientWin")] +[assembly: AssemblyCopyright("Copyright © 2005")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("734ff509-cc61-44b1-baa5-2b4d69a40bf0")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/NDDE/NDde/Samples/cs/ClientWin/Properties/Resources.Designer.cs b/NDDE/NDde/Samples/cs/ClientWin/Properties/Resources.Designer.cs new file mode 100644 index 0000000..295545c --- /dev/null +++ b/NDDE/NDde/Samples/cs/ClientWin/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.18444 +// +// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn +// der Code erneut generiert wird. +// +//------------------------------------------------------------------------------ + +namespace ClientWin.Properties { + using System; + + + /// + /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw. + /// + // Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert + // -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert. + // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen + // mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ClientWin.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle + /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/NDDE/NDde/Samples/cs/ClientWin/Properties/Resources.resx b/NDDE/NDde/Samples/cs/ClientWin/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/NDDE/NDde/Samples/cs/ClientWin/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/NDDE/NDde/Samples/cs/ClientWin/Properties/Settings.Designer.cs b/NDDE/NDde/Samples/cs/ClientWin/Properties/Settings.Designer.cs new file mode 100644 index 0000000..b6456af --- /dev/null +++ b/NDDE/NDde/Samples/cs/ClientWin/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.18444 +// +// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn +// der Code erneut generiert wird. +// +//------------------------------------------------------------------------------ + +namespace ClientWin.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/NDDE/NDde/Samples/cs/ClientWin/Properties/Settings.settings b/NDDE/NDde/Samples/cs/ClientWin/Properties/Settings.settings new file mode 100644 index 0000000..3964565 --- /dev/null +++ b/NDDE/NDde/Samples/cs/ClientWin/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/NDDE/NDde/Samples/cs/Monitor/Monitor.csproj b/NDDE/NDde/Samples/cs/Monitor/Monitor.csproj new file mode 100644 index 0000000..58435b0 --- /dev/null +++ b/NDDE/NDde/Samples/cs/Monitor/Monitor.csproj @@ -0,0 +1,58 @@ + + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {6AEEBA41-2B34-48F8-AE1C-DD8D19E67B6C} + Exe + Properties + Monitor + Monitor + v4.0 + + + + + 2.0 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\..\..\Binary\NDde.dll + + + + + + + + + + + + \ No newline at end of file diff --git a/NDDE/NDde/Samples/cs/Monitor/Program.cs b/NDDE/NDde/Samples/cs/Monitor/Program.cs new file mode 100644 index 0000000..caf9746 --- /dev/null +++ b/NDDE/NDde/Samples/cs/Monitor/Program.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Text; +using NDde; +using NDde.Advanced.Monitor; + +namespace Monitor +{ + class Program + { + static void Main(string[] args) + { + using (DdeMonitor monitor = new DdeMonitor()) + { + monitor.Start(DdeMonitorFlags.Link | DdeMonitorFlags.Callback | DdeMonitorFlags.Conversation | DdeMonitorFlags.Message); + monitor.LinkActivity += DdeMonitor_LinkActivity; + monitor.CallbackActivity += DdeMonitor_CallbackActivity; + monitor.ConversationActivity += DdeMonitor_ConversationActivity; + monitor.MessageActivity += DdeMonitor_MessageActivity; + Console.WriteLine("Press ENTER to quit..."); + Console.ReadLine(); + } + } + + static void DdeMonitor_MessageActivity(object sender, DdeMessageActivityEventArgs e) + { + Console.WriteLine("MessageActivity: " + e.ToString()); + } + + static void DdeMonitor_CallbackActivity(object sender, DdeCallbackActivityEventArgs e) + { + Console.WriteLine("CallbackActivity: " + e.ToString()); + } + + static void DdeMonitor_LinkActivity(object sender, DdeLinkActivityEventArgs e) + { + Console.WriteLine("LinkActivity: " + e.ToString()); + } + + static void DdeMonitor_ConversationActivity(object sender, DdeConversationActivityEventArgs e) + { + Console.WriteLine("ConversationActivity: " + e.ToString()); + } + + } // class + +} // namespace diff --git a/NDDE/NDde/Samples/cs/Monitor/Properties/AssemblyInfo.cs b/NDDE/NDde/Samples/cs/Monitor/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..55f2a74 --- /dev/null +++ b/NDDE/NDde/Samples/cs/Monitor/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +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("Monitor")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Monitor")] +[assembly: AssemblyCopyright("Copyright © 2006")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("03e21b1e-3ad5-42a2-a522-dba86ca04e2b")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/NDDE/NDde/Samples/cs/Samples.sln b/NDDE/NDde/Samples/cs/Samples.sln new file mode 100644 index 0000000..60df5fa --- /dev/null +++ b/NDDE/NDde/Samples/cs/Samples.sln @@ -0,0 +1,38 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual C# Express 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Client", "Client\Client.csproj", "{04E46B25-B029-488B-BD1B-9E4AB12E72E3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClientWin", "ClientWin\ClientWin.csproj", "{8FD4456A-9C49-46ED-9EB4-155C2D738730}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server", "Server\Server.csproj", "{68784D57-3C16-40AC-B483-21202554E114}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Monitor", "Monitor\Monitor.csproj", "{6AEEBA41-2B34-48F8-AE1C-DD8D19E67B6C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {04E46B25-B029-488B-BD1B-9E4AB12E72E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {04E46B25-B029-488B-BD1B-9E4AB12E72E3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {04E46B25-B029-488B-BD1B-9E4AB12E72E3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {04E46B25-B029-488B-BD1B-9E4AB12E72E3}.Release|Any CPU.Build.0 = Release|Any CPU + {8FD4456A-9C49-46ED-9EB4-155C2D738730}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8FD4456A-9C49-46ED-9EB4-155C2D738730}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8FD4456A-9C49-46ED-9EB4-155C2D738730}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8FD4456A-9C49-46ED-9EB4-155C2D738730}.Release|Any CPU.Build.0 = Release|Any CPU + {68784D57-3C16-40AC-B483-21202554E114}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {68784D57-3C16-40AC-B483-21202554E114}.Debug|Any CPU.Build.0 = Debug|Any CPU + {68784D57-3C16-40AC-B483-21202554E114}.Release|Any CPU.ActiveCfg = Release|Any CPU + {68784D57-3C16-40AC-B483-21202554E114}.Release|Any CPU.Build.0 = Release|Any CPU + {6AEEBA41-2B34-48F8-AE1C-DD8D19E67B6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6AEEBA41-2B34-48F8-AE1C-DD8D19E67B6C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6AEEBA41-2B34-48F8-AE1C-DD8D19E67B6C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6AEEBA41-2B34-48F8-AE1C-DD8D19E67B6C}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/NDDE/NDde/Samples/cs/Server/Program.cs b/NDDE/NDde/Samples/cs/Server/Program.cs new file mode 100644 index 0000000..0f42b03 --- /dev/null +++ b/NDDE/NDde/Samples/cs/Server/Program.cs @@ -0,0 +1,173 @@ +using System; +using System.Collections; +using System.Timers; +using NDde.Server; + +namespace Server +{ + public class Server + { + public static void Main() + { + try + { + // Create a server that will register the service name 'myapp'. + using (DdeServer server = new MyServer("myapp")) + { + // Register the service name. + server.Register(); + + // Wait for the user to press ENTER before proceding. + Console.WriteLine("Press ENTER to quit..."); + Console.ReadLine(); + } + } + catch (Exception e) + { + Console.WriteLine(e); + Console.WriteLine("Press ENTER to quit..."); + Console.ReadLine(); + } + } + + private sealed class MyServer : DdeServer + { + private System.Timers.Timer _Timer = new System.Timers.Timer(); + + public MyServer(string service) : base(service) + { + // Create a timer that will be used to advise clients of new data. + _Timer.Elapsed += this.OnTimerElapsed; + _Timer.Interval = 1000; + _Timer.SynchronizingObject = this.Context; + } + + private void OnTimerElapsed(object sender, ElapsedEventArgs args) + { + // Advise all topic name and item name pairs. + Advise("*", "*"); + } + + public override void Register() + { + base.Register(); + _Timer.Start(); + } + + public override void Unregister() + { + _Timer.Stop(); + base.Unregister(); + } + + protected override bool OnBeforeConnect(string topic) + { + Console.WriteLine("OnBeforeConnect:".PadRight(16) + + " Service='" + base.Service + "'" + + " Topic='" + topic + "'"); + + return true; + } + + protected override void OnAfterConnect(DdeConversation conversation) + { + Console.WriteLine("OnAfterConnect:".PadRight(16) + + " Service='" + conversation.Service + "'" + + " Topic='" + conversation.Topic + "'" + + " Handle=" + conversation.Handle.ToString()); + } + + protected override void OnDisconnect(DdeConversation conversation) + { + Console.WriteLine("OnDisconnect:".PadRight(16) + + " Service='" + conversation.Service + "'" + + " Topic='" + conversation.Topic + "'" + + " Handle=" + conversation.Handle.ToString()); + } + + protected override bool OnStartAdvise(DdeConversation conversation, string item, int format) + { + Console.WriteLine("OnStartAdvise:".PadRight(16) + + " Service='" + conversation.Service + "'" + + " Topic='" + conversation.Topic + "'" + + " Handle=" + conversation.Handle.ToString() + + " Item='" + item + "'" + + " Format=" + format.ToString()); + + // Initiate the advisory loop only if the format is CF_TEXT. + return format == 1; + } + + protected override void OnStopAdvise(DdeConversation conversation, string item) + { + Console.WriteLine("OnStopAdvise:".PadRight(16) + + " Service='" + conversation.Service + "'" + + " Topic='" + conversation.Topic + "'" + + " Handle=" + conversation.Handle.ToString() + + " Item='" + item + "'"); + } + + protected override ExecuteResult OnExecute(DdeConversation conversation, string command) + { + Console.WriteLine("OnExecute:".PadRight(16) + + " Service='" + conversation.Service + "'" + + " Topic='" + conversation.Topic + "'" + + " Handle=" + conversation.Handle.ToString() + + " Command='" + command + "'"); + + // Tell the client that the command was processed. + return ExecuteResult.Processed; + } + + protected override PokeResult OnPoke(DdeConversation conversation, string item, byte[] data, int format) + { + Console.WriteLine("OnPoke:".PadRight(16) + + " Service='" + conversation.Service + "'" + + " Topic='" + conversation.Topic + "'" + + " Handle=" + conversation.Handle.ToString() + + " Item='" + item + "'" + + " Data=" + data.Length.ToString() + + " Format=" + format.ToString()); + + // Tell the client that the data was processed. + return PokeResult.Processed; + } + + protected override RequestResult OnRequest(DdeConversation conversation, string item, int format) + { + Console.WriteLine("OnRequest:".PadRight(16) + + " Service='" + conversation.Service + "'" + + " Topic='" + conversation.Topic + "'" + + " Handle=" + conversation.Handle.ToString() + + " Item='" + item + "'" + + " Format=" + format.ToString()); + + // Return data to the client only if the format is CF_TEXT. + if (format == 1) + { + return new RequestResult(System.Text.Encoding.ASCII.GetBytes("Time=" + DateTime.Now.ToString() + "\0")); + } + return RequestResult.NotProcessed; + } + + protected override byte[] OnAdvise(string topic, string item, int format) + { + Console.WriteLine("OnAdvise:".PadRight(16) + + " Service='" + this.Service + "'" + + " Topic='" + topic + "'" + + " Item='" + item + "'" + + " Format=" + format.ToString()); + + // Send data to the client only if the format is CF_TEXT. + if (format == 1) + { + return System.Text.Encoding.ASCII.GetBytes("Time=" + DateTime.Now.ToString() + "\0"); + } + return null; + } + + } // class + + } // class + +} // namespace diff --git a/NDDE/NDde/Samples/cs/Server/Properties/AssemblyInfo.cs b/NDDE/NDde/Samples/cs/Server/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..a96451b --- /dev/null +++ b/NDDE/NDde/Samples/cs/Server/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +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("Server")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Server")] +[assembly: AssemblyCopyright("Copyright © 2005")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("2816e8be-6d14-43e4-85f2-f3a435330164")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/NDDE/NDde/Samples/cs/Server/Server.csproj b/NDDE/NDde/Samples/cs/Server/Server.csproj new file mode 100644 index 0000000..ca4875f --- /dev/null +++ b/NDDE/NDde/Samples/cs/Server/Server.csproj @@ -0,0 +1,58 @@ + + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {68784D57-3C16-40AC-B483-21202554E114} + Exe + Properties + Server + Server + v4.0 + + + + + 2.0 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\..\..\Binary\NDde.dll + + + + + + + + + + + + \ No newline at end of file diff --git a/NDDE/NDde/Samples/vb/Client/Client.vbproj b/NDDE/NDde/Samples/vb/Client/Client.vbproj new file mode 100644 index 0000000..d9c7b32 --- /dev/null +++ b/NDDE/NDde/Samples/vb/Client/Client.vbproj @@ -0,0 +1,96 @@ + + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {2482E71B-A607-4D2B-8CB4-EBD816E07462} + Exe + Client.Program + Client + Client + Console + + + true + full + true + true + bin\Debug\ + Client.xml + 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022 + + + pdbonly + false + true + true + bin\Release\ + Client.xml + 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022 + + + + False + ..\..\..\Binary\NDde.dll + + + + + + + + + + + + + + + + + + + True + Application.myapp + + + True + True + Resources.resx + + + True + Settings.settings + True + + + + + VbMyResourcesResXFileCodeGenerator + Resources.Designer.vb + My.Resources + Designer + + + + + MyApplicationCodeGenerator + Application.Designer.vb + + + SettingsSingleFileGenerator + My + Settings.Designer.vb + + + + + \ No newline at end of file diff --git a/NDDE/NDde/Samples/vb/Client/My Project/Application.Designer.vb b/NDDE/NDde/Samples/vb/Client/My Project/Application.Designer.vb new file mode 100644 index 0000000..8a621ae --- /dev/null +++ b/NDDE/NDde/Samples/vb/Client/My Project/Application.Designer.vb @@ -0,0 +1,13 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:2.0.50727.42 +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict On +Option Explicit On + diff --git a/NDDE/NDde/Samples/vb/Client/My Project/Application.myapp b/NDDE/NDde/Samples/vb/Client/My Project/Application.myapp new file mode 100644 index 0000000..e62f1a5 --- /dev/null +++ b/NDDE/NDde/Samples/vb/Client/My Project/Application.myapp @@ -0,0 +1,10 @@ + + + false + false + 0 + true + 0 + 2 + true + diff --git a/NDDE/NDde/Samples/vb/Client/My Project/AssemblyInfo.vb b/NDDE/NDde/Samples/vb/Client/My Project/AssemblyInfo.vb new file mode 100644 index 0000000..8b28f71 --- /dev/null +++ b/NDDE/NDde/Samples/vb/Client/My Project/AssemblyInfo.vb @@ -0,0 +1,35 @@ +Imports System +Imports System.Reflection +Imports 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. + +' Review the values of the assembly attributes + + + + + + + + + + +'The following GUID is for the ID of the typelib if this project is exposed to COM + + +' Version information for an assembly consists of the following four values: +' +' Major Version +' Minor Version +' Build Number +' Revision +' +' You can specify all the values or you can default the Build and Revision Numbers +' by using the '*' as shown below: +' + + + diff --git a/NDDE/NDde/Samples/vb/Client/My Project/Resources.Designer.vb b/NDDE/NDde/Samples/vb/Client/My Project/Resources.Designer.vb new file mode 100644 index 0000000..5b2f1e1 --- /dev/null +++ b/NDDE/NDde/Samples/vb/Client/My Project/Resources.Designer.vb @@ -0,0 +1,62 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:2.0.50727.42 +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict On +Option Explicit On + + +Namespace My.Resources + + 'This class was auto-generated by the StronglyTypedResourceBuilder + 'class via a tool like ResGen or Visual Studio. + 'To add or remove a member, edit your .ResX file then rerun ResGen + 'with the /str option, or rebuild your VS project. + ' + ' A strongly-typed resource class, for looking up localized strings, etc. + ' + _ + Friend Module Resources + + Private resourceMan As Global.System.Resources.ResourceManager + + Private resourceCulture As Global.System.Globalization.CultureInfo + + ' + ' Returns the cached ResourceManager instance used by this class. + ' + _ + Friend ReadOnly Property ResourceManager() As Global.System.Resources.ResourceManager + Get + If Object.ReferenceEquals(resourceMan, Nothing) Then + Dim temp As Global.System.Resources.ResourceManager = New Global.System.Resources.ResourceManager("Client.Resources", GetType(Resources).Assembly) + resourceMan = temp + End If + Return resourceMan + End Get + End Property + + ' + ' Overrides the current thread's CurrentUICulture property for all + ' resource lookups using this strongly typed resource class. + ' + _ + Friend Property Culture() As Global.System.Globalization.CultureInfo + Get + Return resourceCulture + End Get + Set(ByVal value As Global.System.Globalization.CultureInfo) + resourceCulture = value + End Set + End Property + End Module +End Namespace diff --git a/NDDE/NDde/Samples/vb/Client/My Project/Resources.resx b/NDDE/NDde/Samples/vb/Client/My Project/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/NDDE/NDde/Samples/vb/Client/My Project/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/NDDE/NDde/Samples/vb/Client/My Project/Settings.Designer.vb b/NDDE/NDde/Samples/vb/Client/My Project/Settings.Designer.vb new file mode 100644 index 0000000..fa3ba1d --- /dev/null +++ b/NDDE/NDde/Samples/vb/Client/My Project/Settings.Designer.vb @@ -0,0 +1,73 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:2.0.50727.42 +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict On +Option Explicit On + + +Namespace My + + _ + Partial Friend NotInheritable Class MySettings + Inherits Global.System.Configuration.ApplicationSettingsBase + + Private Shared defaultInstance As MySettings = CType(Global.System.Configuration.ApplicationSettingsBase.Synchronized(New MySettings), MySettings) + +#Region "My.Settings Auto-Save Functionality" +#If _MyType = "WindowsForms" Then + Private Shared addedHandler As Boolean + + Private Shared addedHandlerLockObject As New Object + + _ + Private Shared Sub AutoSaveSettings(ByVal sender As Global.System.Object, ByVal e As Global.System.EventArgs) + If My.Application.SaveMySettingsOnExit Then + My.Settings.Save() + End If + End Sub +#End If +#End Region + + Public Shared ReadOnly Property [Default]() As MySettings + Get + +#If _MyType = "WindowsForms" Then + If Not addedHandler Then + SyncLock addedHandlerLockObject + If Not addedHandler Then + AddHandler My.Application.Shutdown, AddressOf AutoSaveSettings + addedHandler = True + End If + End SyncLock + End If +#End If + Return defaultInstance + End Get + End Property + End Class +End Namespace + +Namespace My + + _ + Friend Module MySettingsProperty + + _ + Friend ReadOnly Property Settings() As Global.Client.My.MySettings + Get + Return Global.Client.My.MySettings.Default + End Get + End Property + End Module +End Namespace diff --git a/NDDE/NDde/Samples/vb/Client/My Project/Settings.settings b/NDDE/NDde/Samples/vb/Client/My Project/Settings.settings new file mode 100644 index 0000000..85b890b --- /dev/null +++ b/NDDE/NDde/Samples/vb/Client/My Project/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/NDDE/NDde/Samples/vb/Client/Program.vb b/NDDE/NDde/Samples/vb/Client/Program.vb new file mode 100644 index 0000000..09c7b81 --- /dev/null +++ b/NDDE/NDde/Samples/vb/Client/Program.vb @@ -0,0 +1,122 @@ +Imports System.Text +Imports NDde.Client + +Module Program + + Sub Main() + + ' Wait for the user to press ENTER before proceding. + Console.WriteLine("The Server sample must be running before the client can connect.") + Console.WriteLine("Press ENTER to continue...") + Console.ReadLine() + + Try + ' Create a client that connects to 'myapp|mytopic'. + Using client As DdeClient = New DdeClient("myapp", "mytopic") + + ' Subscribe to the Disconnected event. This event will notify the application when a conversation has been terminated. + AddHandler client.Disconnected, AddressOf OnDisconnected + + ' Connect to the server. It must be running or an exception will be thrown. + client.Connect() + + ' Synchronous Execute Operation + client.Execute("mycommand", 60000) + + ' Synchronous Poke Operation + client.Poke("myitem", DateTime.Now.ToString(), 60000) + + ' Syncronous Request Operation + Console.WriteLine("Request: " + client.Request("myitem", 60000)) + + ' Asynchronous Execute Operation + client.BeginExecute("mycommand", AddressOf OnExecuteComplete, client) + + ' Asynchronous Poke Operation + client.BeginPoke("myitem", Encoding.ASCII.GetBytes(DateTime.Now.ToString() + Convert.ToChar(0)), 1, AddressOf OnPokeComplete, client) + + ' Asynchronous Request Operation + client.BeginRequest("myitem", 1, AddressOf OnRequestComplete, client) + + ' Advise Loop + client.StartAdvise("myitem", 1, True, 60000) + AddHandler client.Advise, AddressOf OnAdvise + + ' Wait for the user to press ENTER before proceding. + Console.WriteLine("Press ENTER to quit...") + Console.ReadLine() + + End Using + + Catch e As Exception + + Console.WriteLine(e.ToString()) + Console.WriteLine("Press ENTER to quit...") + Console.ReadLine() + + End Try + + End Sub + + Private Sub OnExecuteComplete(ByVal ar As IAsyncResult) + Try + Dim client As DdeClient = DirectCast(ar.AsyncState, DdeClient) + client.EndExecute(ar) + Console.WriteLine("OnExecuteComplete") + Catch e As Exception + Console.WriteLine("OnExecuteComplete: " + e.Message) + End Try + End Sub + + Private Sub OnPokeComplete(ByVal ar As IAsyncResult) + Try + Dim client As DdeClient = DirectCast(ar.AsyncState, DdeClient) + client.EndPoke(ar) + Console.WriteLine("OnPokeComplete") + Catch e As Exception + Console.WriteLine("OnPokeComplete: " + e.Message) + End Try + End Sub + + Private Sub OnRequestComplete(ByVal ar As IAsyncResult) + Try + Dim client As DdeClient = DirectCast(ar.AsyncState, DdeClient) + Dim data() As Byte = client.EndRequest(ar) + Console.WriteLine("OnRequestComplete: " + Encoding.ASCII.GetString(data)) + Catch e As Exception + Console.WriteLine("OnRequestComplete: " + e.Message) + End Try + End Sub + + Private Sub OnStartAdviseComplete(ByVal ar As IAsyncResult) + Try + Dim client As DdeClient = DirectCast(ar.AsyncState, DdeClient) + client.EndStartAdvise(ar) + Console.WriteLine("OnStartAdviseComplete") + Catch e As Exception + Console.WriteLine("OnStartAdviseComplete: " + e.Message) + End Try + End Sub + + Private Sub OnStopAdviseComplete(ByVal ar As IAsyncResult) + Try + Dim client As DdeClient = DirectCast(ar.AsyncState, DdeClient) + client.EndStopAdvise(ar) + Console.WriteLine("OnStopAdviseComplete") + Catch e As Exception + Console.WriteLine("OnStopAdviseComplete: " + e.Message) + End Try + End Sub + + Private Sub OnAdvise(ByVal sender As Object, ByVal args As DdeAdviseEventArgs) + Console.WriteLine("OnAdvise: " + args.Text) + End Sub + + Private Sub OnDisconnected(ByVal sender As Object, ByVal args As DdeDisconnectedEventArgs) + Console.WriteLine( _ + "OnDisconnected: " + _ + "IsServerInitiated=" + args.IsServerInitiated.ToString() + " " + _ + "IsDisposed=" + args.IsDisposed.ToString()) + End Sub + +End Module diff --git a/NDDE/NDde/Samples/vb/ClientWin/ClientWin.vbproj b/NDDE/NDde/Samples/vb/ClientWin/ClientWin.vbproj new file mode 100644 index 0000000..bcc54ef --- /dev/null +++ b/NDDE/NDde/Samples/vb/ClientWin/ClientWin.vbproj @@ -0,0 +1,110 @@ + + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {7E0C3AE2-E2C9-4C6C-913F-0655E9F23A83} + WinExe + ClientWin.My.MyApplication + ClientWin + ClientWin + WindowsForms + + + true + full + true + true + bin\Debug\ + ClientWin.xml + 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022 + + + pdbonly + false + true + true + bin\Release\ + ClientWin.xml + 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022 + + + + False + ..\..\..\Binary\NDde.dll + + + + + + + + + + + + + + + + + + + + + Form + + + MainForm.vb + Form + + + + True + Application.myapp + + + True + True + Resources.resx + + + True + Settings.settings + True + + + + + Designer + MainForm.vb + + + VbMyResourcesResXFileCodeGenerator + Resources.Designer.vb + My.Resources + Designer + + + + + MyApplicationCodeGenerator + Application.Designer.vb + + + SettingsSingleFileGenerator + My + Settings.Designer.vb + + + + + \ No newline at end of file diff --git a/NDDE/NDde/Samples/vb/ClientWin/MainForm.Designer.vb b/NDDE/NDde/Samples/vb/ClientWin/MainForm.Designer.vb new file mode 100644 index 0000000..e4737f9 --- /dev/null +++ b/NDDE/NDde/Samples/vb/ClientWin/MainForm.Designer.vb @@ -0,0 +1,48 @@ + _ +Partial Class MainForm + Inherits System.Windows.Forms.Form + + 'Form overrides dispose to clean up the component list. + _ + Protected Overrides Sub Dispose(ByVal disposing As Boolean) + If disposing AndAlso components IsNot Nothing Then + components.Dispose() + End If + MyBase.Dispose(disposing) + End Sub + + 'Required by the Windows Form Designer + Private components As System.ComponentModel.IContainer + + 'NOTE: The following procedure is required by the Windows Form Designer + 'It can be modified using the Windows Form Designer. + 'Do not modify it using the code editor. + _ + Private Sub InitializeComponent() + Me.displayTextBox = New System.Windows.Forms.TextBox + Me.SuspendLayout() + ' + 'displayTextBox + ' + Me.displayTextBox.Location = New System.Drawing.Point(11, 11) + Me.displayTextBox.Name = "displayTextBox" + Me.displayTextBox.ReadOnly = True + Me.displayTextBox.Size = New System.Drawing.Size(270, 20) + Me.displayTextBox.TabIndex = 0 + ' + 'MainForm + ' + Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!) + Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font + Me.ClientSize = New System.Drawing.Size(292, 41) + Me.Controls.Add(Me.displayTextBox) + Me.Name = "MainForm" + Me.Padding = New System.Windows.Forms.Padding(8) + Me.Text = "DDE Sample Application" + Me.ResumeLayout(False) + Me.PerformLayout() + + End Sub + Friend WithEvents displayTextBox As System.Windows.Forms.TextBox + +End Class diff --git a/NDDE/NDde/Samples/vb/ClientWin/MainForm.resx b/NDDE/NDde/Samples/vb/ClientWin/MainForm.resx new file mode 100644 index 0000000..19dc0dd --- /dev/null +++ b/NDDE/NDde/Samples/vb/ClientWin/MainForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/NDDE/NDde/Samples/vb/ClientWin/MainForm.vb b/NDDE/NDde/Samples/vb/ClientWin/MainForm.vb new file mode 100644 index 0000000..1517d0a --- /dev/null +++ b/NDDE/NDde/Samples/vb/ClientWin/MainForm.vb @@ -0,0 +1,36 @@ +Imports System.Text +Imports NDde.Client + +Public Class MainForm + Private WithEvents client As New DdeClient("myapp", "mytopic", Me) + + Private Sub MainForm_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load + Try + ' Connect to the server. It must be running or an exception will be thrown. + client.Connect() + + ' Advise Loop + client.StartAdvise("myitem", 1, True, 60000) + Catch ex As Exception + displayTextBox.Text = ex.Message + End Try + End Sub + + Private Sub MainForm_Resize(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Resize + displayTextBox.Left = Me.DisplayRectangle.Left + displayTextBox.Width = Me.DisplayRectangle.Width + displayTextBox.Top = Me.DisplayRectangle.Top + displayTextBox.Height = Me.DisplayRectangle.Height + End Sub + + Private Sub client_Advise(ByVal sender As Object, ByVal e As NDde.Client.DdeAdviseEventArgs) Handles client.Advise + displayTextBox.Text = "OnAdvise: " + e.Text + End Sub + + Private Sub client_Disconnected(ByVal sender As Object, ByVal e As NDde.Client.DdeDisconnectedEventArgs) Handles client.Disconnected + displayTextBox.Text = _ + "OnDisconnected: " + _ + "IsServerInitiated=" + e.IsServerInitiated.ToString() + " " + _ + "IsDisposed=" + e.IsDisposed.ToString() + End Sub +End Class diff --git a/NDDE/NDde/Samples/vb/ClientWin/My Project/Application.Designer.vb b/NDDE/NDde/Samples/vb/ClientWin/My Project/Application.Designer.vb new file mode 100644 index 0000000..9e9eaad --- /dev/null +++ b/NDDE/NDde/Samples/vb/ClientWin/My Project/Application.Designer.vb @@ -0,0 +1,38 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:2.0.50727.42 +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict On +Option Explicit On + + +Namespace My + + 'NOTE: This file is auto-generated; do not modify it directly. To make changes, + ' or if you encounter build errors in this file, go to the Project Designer + ' (go to Project Properties or double-click the My Project node in + ' Solution Explorer), and make changes on the Application tab. + ' + Partial Friend Class MyApplication + + _ + Public Sub New() + MyBase.New(Global.Microsoft.VisualBasic.ApplicationServices.AuthenticationMode.Windows) + Me.IsSingleInstance = false + Me.EnableVisualStyles = true + Me.SaveMySettingsOnExit = true + Me.ShutDownStyle = Global.Microsoft.VisualBasic.ApplicationServices.ShutdownMode.AfterMainFormCloses + End Sub + + _ + Protected Overrides Sub OnCreateMainForm() + Me.MainForm = Global.ClientWin.MainForm + End Sub + End Class +End Namespace diff --git a/NDDE/NDde/Samples/vb/ClientWin/My Project/Application.myapp b/NDDE/NDde/Samples/vb/ClientWin/My Project/Application.myapp new file mode 100644 index 0000000..27659f2 --- /dev/null +++ b/NDDE/NDde/Samples/vb/ClientWin/My Project/Application.myapp @@ -0,0 +1,10 @@ + + + true + MainForm + false + 0 + true + 0 + true + \ No newline at end of file diff --git a/NDDE/NDde/Samples/vb/ClientWin/My Project/AssemblyInfo.vb b/NDDE/NDde/Samples/vb/ClientWin/My Project/AssemblyInfo.vb new file mode 100644 index 0000000..61dbf58 --- /dev/null +++ b/NDDE/NDde/Samples/vb/ClientWin/My Project/AssemblyInfo.vb @@ -0,0 +1,35 @@ +Imports System +Imports System.Reflection +Imports 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. + +' Review the values of the assembly attributes + + + + + + + + + + +'The following GUID is for the ID of the typelib if this project is exposed to COM + + +' Version information for an assembly consists of the following four values: +' +' Major Version +' Minor Version +' Build Number +' Revision +' +' You can specify all the values or you can default the Build and Revision Numbers +' by using the '*' as shown below: +' + + + diff --git a/NDDE/NDde/Samples/vb/ClientWin/My Project/Resources.Designer.vb b/NDDE/NDde/Samples/vb/ClientWin/My Project/Resources.Designer.vb new file mode 100644 index 0000000..bd82ac8 --- /dev/null +++ b/NDDE/NDde/Samples/vb/ClientWin/My Project/Resources.Designer.vb @@ -0,0 +1,62 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:2.0.50727.42 +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict On +Option Explicit On + + +Namespace My.Resources + + 'This class was auto-generated by the StronglyTypedResourceBuilder + 'class via a tool like ResGen or Visual Studio. + 'To add or remove a member, edit your .ResX file then rerun ResGen + 'with the /str option, or rebuild your VS project. + ' + ' A strongly-typed resource class, for looking up localized strings, etc. + ' + _ + Friend Module Resources + + Private resourceMan As Global.System.Resources.ResourceManager + + Private resourceCulture As Global.System.Globalization.CultureInfo + + ' + ' Returns the cached ResourceManager instance used by this class. + ' + _ + Friend ReadOnly Property ResourceManager() As Global.System.Resources.ResourceManager + Get + If Object.ReferenceEquals(resourceMan, Nothing) Then + Dim temp As Global.System.Resources.ResourceManager = New Global.System.Resources.ResourceManager("ClientWin.Resources", GetType(Resources).Assembly) + resourceMan = temp + End If + Return resourceMan + End Get + End Property + + ' + ' Overrides the current thread's CurrentUICulture property for all + ' resource lookups using this strongly typed resource class. + ' + _ + Friend Property Culture() As Global.System.Globalization.CultureInfo + Get + Return resourceCulture + End Get + Set(ByVal value As Global.System.Globalization.CultureInfo) + resourceCulture = value + End Set + End Property + End Module +End Namespace diff --git a/NDDE/NDde/Samples/vb/ClientWin/My Project/Resources.resx b/NDDE/NDde/Samples/vb/ClientWin/My Project/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/NDDE/NDde/Samples/vb/ClientWin/My Project/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/NDDE/NDde/Samples/vb/ClientWin/My Project/Settings.Designer.vb b/NDDE/NDde/Samples/vb/ClientWin/My Project/Settings.Designer.vb new file mode 100644 index 0000000..5d44274 --- /dev/null +++ b/NDDE/NDde/Samples/vb/ClientWin/My Project/Settings.Designer.vb @@ -0,0 +1,73 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:2.0.50727.42 +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict On +Option Explicit On + + +Namespace My + + _ + Partial Friend NotInheritable Class MySettings + Inherits Global.System.Configuration.ApplicationSettingsBase + + Private Shared defaultInstance As MySettings = CType(Global.System.Configuration.ApplicationSettingsBase.Synchronized(New MySettings), MySettings) + +#Region "My.Settings Auto-Save Functionality" +#If _MyType = "WindowsForms" Then + Private Shared addedHandler As Boolean + + Private Shared addedHandlerLockObject As New Object + + _ + Private Shared Sub AutoSaveSettings(ByVal sender As Global.System.Object, ByVal e As Global.System.EventArgs) + If My.Application.SaveMySettingsOnExit Then + My.Settings.Save() + End If + End Sub +#End If +#End Region + + Public Shared ReadOnly Property [Default]() As MySettings + Get + +#If _MyType = "WindowsForms" Then + If Not addedHandler Then + SyncLock addedHandlerLockObject + If Not addedHandler Then + AddHandler My.Application.Shutdown, AddressOf AutoSaveSettings + addedHandler = True + End If + End SyncLock + End If +#End If + Return defaultInstance + End Get + End Property + End Class +End Namespace + +Namespace My + + _ + Friend Module MySettingsProperty + + _ + Friend ReadOnly Property Settings() As Global.ClientWin.My.MySettings + Get + Return Global.ClientWin.My.MySettings.Default + End Get + End Property + End Module +End Namespace diff --git a/NDDE/NDde/Samples/vb/ClientWin/My Project/Settings.settings b/NDDE/NDde/Samples/vb/ClientWin/My Project/Settings.settings new file mode 100644 index 0000000..85b890b --- /dev/null +++ b/NDDE/NDde/Samples/vb/ClientWin/My Project/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/NDDE/NDde/Samples/vb/Samples.sln b/NDDE/NDde/Samples/vb/Samples.sln new file mode 100644 index 0000000..d029b54 --- /dev/null +++ b/NDDE/NDde/Samples/vb/Samples.sln @@ -0,0 +1,32 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Client", "Client\Client.vbproj", "{2482E71B-A607-4D2B-8CB4-EBD816E07462}" +EndProject +Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "ClientWin", "ClientWin\ClientWin.vbproj", "{7E0C3AE2-E2C9-4C6C-913F-0655E9F23A83}" +EndProject +Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Server", "Server\Server.vbproj", "{8EAF0758-4C3C-4393-A3C6-8EA3D1C657E5}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2482E71B-A607-4D2B-8CB4-EBD816E07462}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2482E71B-A607-4D2B-8CB4-EBD816E07462}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2482E71B-A607-4D2B-8CB4-EBD816E07462}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2482E71B-A607-4D2B-8CB4-EBD816E07462}.Release|Any CPU.Build.0 = Release|Any CPU + {7E0C3AE2-E2C9-4C6C-913F-0655E9F23A83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7E0C3AE2-E2C9-4C6C-913F-0655E9F23A83}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7E0C3AE2-E2C9-4C6C-913F-0655E9F23A83}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7E0C3AE2-E2C9-4C6C-913F-0655E9F23A83}.Release|Any CPU.Build.0 = Release|Any CPU + {8EAF0758-4C3C-4393-A3C6-8EA3D1C657E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8EAF0758-4C3C-4393-A3C6-8EA3D1C657E5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8EAF0758-4C3C-4393-A3C6-8EA3D1C657E5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8EAF0758-4C3C-4393-A3C6-8EA3D1C657E5}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/NDDE/NDde/Samples/vb/Server/My Project/Application.Designer.vb b/NDDE/NDde/Samples/vb/Server/My Project/Application.Designer.vb new file mode 100644 index 0000000..8a621ae --- /dev/null +++ b/NDDE/NDde/Samples/vb/Server/My Project/Application.Designer.vb @@ -0,0 +1,13 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:2.0.50727.42 +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict On +Option Explicit On + diff --git a/NDDE/NDde/Samples/vb/Server/My Project/Application.myapp b/NDDE/NDde/Samples/vb/Server/My Project/Application.myapp new file mode 100644 index 0000000..e62f1a5 --- /dev/null +++ b/NDDE/NDde/Samples/vb/Server/My Project/Application.myapp @@ -0,0 +1,10 @@ + + + false + false + 0 + true + 0 + 2 + true + diff --git a/NDDE/NDde/Samples/vb/Server/My Project/AssemblyInfo.vb b/NDDE/NDde/Samples/vb/Server/My Project/AssemblyInfo.vb new file mode 100644 index 0000000..9989584 --- /dev/null +++ b/NDDE/NDde/Samples/vb/Server/My Project/AssemblyInfo.vb @@ -0,0 +1,35 @@ +Imports System +Imports System.Reflection +Imports 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. + +' Review the values of the assembly attributes + + + + + + + + + + +'The following GUID is for the ID of the typelib if this project is exposed to COM + + +' Version information for an assembly consists of the following four values: +' +' Major Version +' Minor Version +' Build Number +' Revision +' +' You can specify all the values or you can default the Build and Revision Numbers +' by using the '*' as shown below: +' + + + diff --git a/NDDE/NDde/Samples/vb/Server/My Project/Resources.Designer.vb b/NDDE/NDde/Samples/vb/Server/My Project/Resources.Designer.vb new file mode 100644 index 0000000..a1668c8 --- /dev/null +++ b/NDDE/NDde/Samples/vb/Server/My Project/Resources.Designer.vb @@ -0,0 +1,63 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:2.0.50727.42 +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict On +Option Explicit On + +Imports System + +Namespace My.Resources + + 'This class was auto-generated by the StronglyTypedResourceBuilder + 'class via a tool like ResGen or Visual Studio. + 'To add or remove a member, edit your .ResX file then rerun ResGen + 'with the /str option, or rebuild your VS project. + ''' + ''' A strongly-typed resource class, for looking up localized strings, etc. + ''' + _ + Friend Module Resources + + Private resourceMan As Global.System.Resources.ResourceManager + + Private resourceCulture As Global.System.Globalization.CultureInfo + + ''' + ''' Returns the cached ResourceManager instance used by this class. + ''' + _ + Friend ReadOnly Property ResourceManager() As Global.System.Resources.ResourceManager + Get + If Object.ReferenceEquals(resourceMan, Nothing) Then + Dim temp As Global.System.Resources.ResourceManager = New Global.System.Resources.ResourceManager("Server.Resources", GetType(Resources).Assembly) + resourceMan = temp + End If + Return resourceMan + End Get + End Property + + ''' + ''' Overrides the current thread's CurrentUICulture property for all + ''' resource lookups using this strongly typed resource class. + ''' + _ + Friend Property Culture() As Global.System.Globalization.CultureInfo + Get + Return resourceCulture + End Get + Set + resourceCulture = value + End Set + End Property + End Module +End Namespace diff --git a/NDDE/NDde/Samples/vb/Server/My Project/Resources.resx b/NDDE/NDde/Samples/vb/Server/My Project/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/NDDE/NDde/Samples/vb/Server/My Project/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/NDDE/NDde/Samples/vb/Server/My Project/Settings.Designer.vb b/NDDE/NDde/Samples/vb/Server/My Project/Settings.Designer.vb new file mode 100644 index 0000000..c3f8375 --- /dev/null +++ b/NDDE/NDde/Samples/vb/Server/My Project/Settings.Designer.vb @@ -0,0 +1,73 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:2.0.50727.42 +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict On +Option Explicit On + + +Namespace My + + _ + Partial Friend NotInheritable Class MySettings + Inherits Global.System.Configuration.ApplicationSettingsBase + + Private Shared defaultInstance As MySettings = CType(Global.System.Configuration.ApplicationSettingsBase.Synchronized(New MySettings),MySettings) + +#Region "My.Settings Auto-Save Functionality" +#If _MyType = "WindowsForms" Then + Private Shared addedHandler As Boolean + + Private Shared addedHandlerLockObject As New Object + + _ + Private Shared Sub AutoSaveSettings(ByVal sender As Global.System.Object, ByVal e As Global.System.EventArgs) + If My.Application.SaveMySettingsOnExit Then + My.Settings.Save() + End If + End Sub +#End If +#End Region + + Public Shared ReadOnly Property [Default]() As MySettings + Get + +#If _MyType = "WindowsForms" Then + If Not addedHandler Then + SyncLock addedHandlerLockObject + If Not addedHandler Then + AddHandler My.Application.Shutdown, AddressOf AutoSaveSettings + addedHandler = True + End If + End SyncLock + End If +#End If + Return defaultInstance + End Get + End Property + End Class +End Namespace + +Namespace My + + _ + Friend Module MySettingsProperty + + _ + Friend ReadOnly Property Settings() As Global.Server.My.MySettings + Get + Return Global.Server.My.MySettings.Default + End Get + End Property + End Module +End Namespace diff --git a/NDDE/NDde/Samples/vb/Server/My Project/Settings.settings b/NDDE/NDde/Samples/vb/Server/My Project/Settings.settings new file mode 100644 index 0000000..85b890b --- /dev/null +++ b/NDDE/NDde/Samples/vb/Server/My Project/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/NDDE/NDde/Samples/vb/Server/Program.vb b/NDDE/NDde/Samples/vb/Server/Program.vb new file mode 100644 index 0000000..342cb3d --- /dev/null +++ b/NDDE/NDde/Samples/vb/Server/Program.vb @@ -0,0 +1,154 @@ +Imports NDde.Server + +Module Program + + Sub Main() + + Try + + ' Create a server that will register the service name 'myapp'. + Using server As DdeServer = New MyServer("myapp") + + ' Register the service name. + server.Register() + + ' Wait for the user to press ENTER before proceding. + Console.WriteLine("Press ENTER to quit...") + Console.ReadLine() + + End Using + + Catch e As Exception + + Console.WriteLine(e) + Console.WriteLine("Press ENTER to quit...") + Console.ReadLine() + + End Try + + End Sub + + Private Class MyServer + Inherits DdeServer + + Private WithEvents theTimer As System.Timers.Timer = New System.Timers.Timer() + + Public Sub New(ByVal service As String) + MyBase.New(service) + ' Create a timer that will be used to advise clients of new data. + theTimer.Interval = 1000 + theTimer.SynchronizingObject = Me.Context + End Sub + + Private Sub theTimer_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) Handles theTimer.Elapsed + Me.Advise("*", "*") + End Sub + + Public Overrides Sub Register() + MyBase.Register() + theTimer.Start() + End Sub + + Public Overrides Sub Unregister() + theTimer.Stop() + MyBase.Unregister() + End Sub + + Protected Overrides Function OnBeforeConnect(ByVal topic As String) As Boolean + Console.WriteLine("OnBeforeConnect:".PadRight(16) _ + + " Service='" + MyBase.Service + "'" _ + + " Topic='" + topic + "'") + + Return True + End Function + + Protected Overrides Sub OnAfterConnect(ByVal conversation As DdeConversation) + Console.WriteLine("OnAfterConnect:".PadRight(16) _ + + " Service='" + conversation.Service + "'" _ + + " Topic='" + conversation.Topic + "'" _ + + " Handle=" + conversation.Handle.ToString()) + End Sub + + Protected Overrides Sub OnDisconnect(ByVal conversation As DdeConversation) + Console.WriteLine("OnDisconnect:".PadRight(16) _ + + " Service='" + conversation.Service + "'" _ + + " Topic='" + conversation.Topic + "'" _ + + " Handle=" + conversation.Handle.ToString()) + End Sub + + Protected Overrides Function OnStartAdvise(ByVal conversation As DdeConversation, ByVal item As String, ByVal format As Integer) As Boolean + Console.WriteLine("OnStartAdvise:".PadRight(16) _ + + " Service='" + conversation.Service + "'" _ + + " Topic='" + conversation.Topic + "'" _ + + " Handle=" + conversation.Handle.ToString() _ + + " Item='" + item + "'" _ + + " Format=" + format.ToString()) + + ' Initiate the advisory loop only if the format is CF_TEXT. + Return format = 1 + End Function + + Protected Overrides Sub OnStopAdvise(ByVal conversation As DdeConversation, ByVal item As String) + Console.WriteLine("OnStopAdvise:".PadRight(16) _ + + " Service='" + conversation.Service + "'" _ + + " Topic='" + conversation.Topic + "'" _ + + " Handle=" + conversation.Handle.ToString() _ + + " Item='" + item + "'") + End Sub + + Protected Overrides Function OnExecute(ByVal conversation As DdeConversation, ByVal command As String) As ExecuteResult + Console.WriteLine("OnExecute:".PadRight(16) _ + + " Service='" + conversation.Service + "'" _ + + " Topic='" + conversation.Topic + "'" _ + + " Handle=" + conversation.Handle.ToString() _ + + " Command='" + command + "'") + + ' Tell the client that the command was processed. + Return ExecuteResult.Processed + End Function + + Protected Overrides Function OnPoke(ByVal conversation As DdeConversation, ByVal item As String, ByVal data As Byte(), ByVal format As Integer) As PokeResult + Console.WriteLine("OnPoke:".PadRight(16) _ + + " Service='" + conversation.Service + "'" _ + + " Topic='" + conversation.Topic + "'" _ + + " Handle=" + conversation.Handle.ToString() _ + + " Item='" + item + "'" _ + + " Data=" + data.Length.ToString() _ + + " Format=" + format.ToString()) + + ' Tell the client that the data was processed. + Return PokeResult.Processed + End Function + + Protected Overrides Function OnRequest(ByVal conversation As DdeConversation, ByVal item As String, ByVal format As Integer) As RequestResult + Console.WriteLine("OnRequest:".PadRight(16) _ + + " Service='" + conversation.Service + "'" _ + + " Topic='" + conversation.Topic + "'" _ + + " Handle=" + conversation.Handle.ToString() _ + + " Item='" + item + "'" _ + + " Format=" + format.ToString()) + + ' Return data to the client only if the format is CF_TEXT. + If format = 1 Then + Return New RequestResult(System.Text.Encoding.ASCII.GetBytes("Time=" + DateTime.Now.ToString() + Convert.ToChar(0))) + End If + Return RequestResult.NotProcessed + End Function + + Protected Overrides Function OnAdvise(ByVal topic As String, ByVal item As String, ByVal format As Integer) As Byte() + Console.WriteLine("OnAdvise:".PadRight(16) _ + + " Service='" + Me.Service + "'" _ + + " Topic='" + topic + "'" _ + + " Item='" + item + "'" _ + + " Format=" + format.ToString()) + + ' Send data to the client only if the format is CF_TEXT. + If format = 1 Then + Return System.Text.Encoding.ASCII.GetBytes("Time=" + DateTime.Now.ToString() + Convert.ToChar(0)) + End If + Return Nothing + End Function + + End Class + +End Module diff --git a/NDDE/NDde/Samples/vb/Server/Server.vbproj b/NDDE/NDde/Samples/vb/Server/Server.vbproj new file mode 100644 index 0000000..9ddf237 --- /dev/null +++ b/NDDE/NDde/Samples/vb/Server/Server.vbproj @@ -0,0 +1,96 @@ + + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {8EAF0758-4C3C-4393-A3C6-8EA3D1C657E5} + Exe + Server.Program + Server + Server + Console + + + true + full + true + true + bin\Debug\ + Server.xml + 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022 + + + pdbonly + false + true + true + bin\Release\ + Server.xml + 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022 + + + + False + ..\..\..\Binary\NDde.dll + + + + + + + + + + + + + + + + + + + True + Application.myapp + + + True + True + Resources.resx + + + True + Settings.settings + True + + + + + VbMyResourcesResXFileCodeGenerator + Resources.Designer.vb + My.Resources + Designer + + + + + MyApplicationCodeGenerator + Application.Designer.vb + + + SettingsSingleFileGenerator + My + Settings.Designer.vb + + + + + \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde.Test/Helpers/EventListener.cs b/NDDE/NDde/Source/NDde.Test/Helpers/EventListener.cs new file mode 100644 index 0000000..d098e24 --- /dev/null +++ b/NDDE/NDde/Source/NDde.Test/Helpers/EventListener.cs @@ -0,0 +1,31 @@ +namespace NDde.Test +{ + using System; + using System.Collections.Generic; + using System.Threading; + using NDde; + + internal sealed class EventListener + { + private System.Threading.ManualResetEvent _Received = new System.Threading.ManualResetEvent(false); + private List _Events = new List(); + + public List Events + { + get { return _Events; } + } + + public System.Threading.WaitHandle Received + { + get { return _Received; } + } + + public void OnEvent(object sender, DdeEventArgs args) + { + _Events.Add(args); + _Received.Set(); + } + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde.Test/Helpers/TestServer.cs b/NDDE/NDde/Source/NDde.Test/Helpers/TestServer.cs new file mode 100644 index 0000000..49b1205 --- /dev/null +++ b/NDDE/NDde/Source/NDde.Test/Helpers/TestServer.cs @@ -0,0 +1,205 @@ +namespace NDde.Test +{ + using System; + using System.Collections; + using System.Timers; + using NDde; + using NDde.Advanced; + using NDde.Server; + + internal class TestServer : TracingServer + { + private Timer _Timer = new Timer(); + private string _Command = ""; + private IDictionary _Data = new Hashtable(); + private IDictionary _Conversation = new Hashtable(); + + public TestServer(string service) + : base(service) + { + _Timer.Elapsed += new ElapsedEventHandler(this.OnTimerElapsed); + _Timer.Interval = 1000; + _Timer.SynchronizingObject = base.Context; + } + + public TestServer(string service, DdeContext context) + : base(service, context) + { + _Timer.Elapsed += new ElapsedEventHandler(this.OnTimerElapsed); + _Timer.Interval = 1000; + _Timer.SynchronizingObject = base.Context; + } + + public double Interval + { + get { return _Timer.Interval; } + } + + public string Command + { + get { return _Command; } + } + + public byte[] GetData(string topic, string item, int format) + { + string key = topic + ":" + item + ":" + format.ToString(); + return (byte[])_Data[key]; + } + + public void SetData(string topic, string item, int format, byte[] data) + { + string key = topic + ":" + item + ":" + format.ToString(); + _Data[key] = data; + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _Timer.Dispose(); + } + base.Dispose(true); + } + + private void OnTimerElapsed(object sender, ElapsedEventArgs args) + { + foreach (DdeConversation c in _Conversation.Values) + { + if (c.IsPaused) + { + Resume(c); + } + } + + foreach (DdeConversation c in _Conversation.Values) + { + if (c.IsPaused) + { + return; + } + } + + _Timer.Stop(); + } + + protected override bool OnBeforeConnect(string topic) + { + base.OnBeforeConnect(topic); + return true; + } + + protected override void OnAfterConnect(DdeConversation conversation) + { + base.OnAfterConnect(conversation); + _Conversation.Add(conversation.Handle, conversation); + } + + protected override void OnDisconnect(DdeConversation conversation) + { + base.OnDisconnect(conversation); + _Conversation.Remove(conversation.Handle); + } + + protected override bool OnStartAdvise(DdeConversation conversation, string item, int format) + { + base.OnStartAdvise(conversation, item, format); + return true; + } + + protected override void OnStopAdvise(DdeConversation conversation, string item) + { + base.OnStopAdvise(conversation, item); + } + + protected override ExecuteResult OnExecute(DdeConversation conversation, string command) + { + base.OnExecute(conversation, command); + _Command = command; + switch (command) + { + case "#NotProcessed": + { + return ExecuteResult.NotProcessed; + } + case "#PauseConversation": + { + if ((string)conversation.Tag == command) + { + conversation.Tag = null; + return ExecuteResult.Processed; + } + conversation.Tag = command; + if (!_Timer.Enabled) _Timer.Start(); + return ExecuteResult.PauseConversation; + } + case "#Processed": + { + return ExecuteResult.Processed; + } + case "#TooBusy": + { + return ExecuteResult.TooBusy; + } + } + return ExecuteResult.Processed; + } + + protected override PokeResult OnPoke(DdeConversation conversation, string item, byte[] data, int format) + { + base.OnPoke(conversation, item, data, format); + string key = conversation.Topic + ":" + item + ":" + format.ToString(); + _Data[key] = data; + switch (item) + { + case "#NotProcessed": + { + return PokeResult.NotProcessed; + } + case "#PauseConversation": + { + if ((string)conversation.Tag == item) + { + conversation.Tag = null; + return PokeResult.Processed; + } + conversation.Tag = item; + if (!_Timer.Enabled) _Timer.Start(); + return PokeResult.PauseConversation; + } + case "#Processed": + { + return PokeResult.Processed; + } + case "#TooBusy": + { + return PokeResult.TooBusy; + } + } + return PokeResult.Processed; + } + + protected override RequestResult OnRequest(DdeConversation conversation, string item, int format) + { + base.OnRequest(conversation, item, format); + string key = conversation.Topic + ":" + item + ":" + format.ToString(); + if (_Data.Contains(key)) + { + return new RequestResult((byte[])_Data[key]); + } + return RequestResult.NotProcessed; + } + + protected override byte[] OnAdvise(string topic, string item, int format) + { + base.OnAdvise(topic, item, format); + string key = topic + ":" + item + ":" + format.ToString(); + if (_Data.Contains(key)) + { + return (byte[])_Data[key]; + } + return null; + } + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde.Test/Helpers/TracingServer.cs b/NDDE/NDde/Source/NDde.Test/Helpers/TracingServer.cs new file mode 100644 index 0000000..16f2797 --- /dev/null +++ b/NDDE/NDde/Source/NDde.Test/Helpers/TracingServer.cs @@ -0,0 +1,115 @@ +namespace NDde.Test +{ + using System; + using NDde; + using NDde.Advanced; + using NDde.Server; + + internal class TracingServer : DdeServer + { + public TracingServer(string service) + : base(service) + { + } + + public TracingServer(string service, DdeContext context) + : base(service, context) + { + } + + protected override bool OnBeforeConnect(string topic) + { + Console.WriteLine("OnBeforeConnect:".PadRight(16) + + " Service='" + base.Service + "'" + + " Topic='" + topic + "'"); + + return base.OnBeforeConnect(topic); + } + + protected override void OnAfterConnect(DdeConversation conversation) + { + Console.WriteLine("OnAfterConnect:".PadRight(16) + + " Service='" + conversation.Service + "'" + + " Topic='" + conversation.Topic + "'" + + " Handle=" + conversation.Handle.ToString()); + } + + protected override void OnDisconnect(DdeConversation conversation) + { + Console.WriteLine("OnDisconnect:".PadRight(16) + + " Service='" + conversation.Service + "'" + + " Topic='" + conversation.Topic + "'" + + " Handle=" + conversation.Handle.ToString()); + } + + protected override bool OnStartAdvise(DdeConversation conversation, string item, int format) + { + Console.WriteLine("OnStartAdvise:".PadRight(16) + + " Service='" + conversation.Service + "'" + + " Topic='" + conversation.Topic + "'" + + " Handle=" + conversation.Handle.ToString() + + " Item='" + item + "'" + + " Format=" + format.ToString()); + + return base.OnStartAdvise(conversation, item, format); + } + + protected override void OnStopAdvise(DdeConversation conversation, string item) + { + Console.WriteLine("OnStopAdvise:".PadRight(16) + + " Service='" + conversation.Service + "'" + + " Topic='" + conversation.Topic + "'" + + " Handle=" + conversation.Handle.ToString() + + " Item='" + item + "'"); + } + + protected override ExecuteResult OnExecute(DdeConversation conversation, string command) + { + Console.WriteLine("OnExecute:".PadRight(16) + + " Service='" + conversation.Service + "'" + + " Topic='" + conversation.Topic + "'" + + " Handle=" + conversation.Handle.ToString() + + " Command='" + command + "'"); + + return base.OnExecute(conversation, command); + } + + protected override PokeResult OnPoke(DdeConversation conversation, string item, byte[] data, int format) + { + Console.WriteLine("OnPoke:".PadRight(16) + + " Service='" + conversation.Service + "'" + + " Topic='" + conversation.Topic + "'" + + " Handle=" + conversation.Handle.ToString() + + " Item='" + item + "'" + + " Data=" + data.Length.ToString() + + " Format=" + format.ToString()); + + return base.OnPoke(conversation, item, data, format); + } + + protected override RequestResult OnRequest(DdeConversation conversation, string item, int format) + { + Console.WriteLine("OnRequest:".PadRight(16) + + " Service='" + conversation.Service + "'" + + " Topic='" + conversation.Topic + "'" + + " Handle=" + conversation.Handle.ToString() + + " Item='" + item + "'" + + " Format=" + format.ToString()); + + return base.OnRequest(conversation, item, format); + } + + protected override byte[] OnAdvise(string topic, string item, int format) + { + Console.WriteLine("OnAdvise:".PadRight(16) + + " Service='" + this.Service + "'" + + " Topic='" + topic + "'" + + " Item='" + item + "'" + + " Format=" + format.ToString()); + + return base.OnAdvise(topic, item, format); + } + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde.Test/NDde.Test.csproj b/NDDE/NDde/Source/NDde.Test/NDde.Test.csproj new file mode 100644 index 0000000..0fab3ba --- /dev/null +++ b/NDDE/NDde/Source/NDde.Test/NDde.Test.csproj @@ -0,0 +1,64 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {D6FDDD04-3BB0-4AE4-B69A-CE8C1238FB00} + Exe + Properties + NDde.Test + NDde.Test + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\Reference\nunit.framework.dll + + + + + + + + + + + + + + + + + + + {D77772F9-3D3D-40BA-B95F-05C45878078F} + NDde + + + + + \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde.Test/Program.cs b/NDDE/NDde/Source/NDde.Test/Program.cs new file mode 100644 index 0000000..b770dd9 --- /dev/null +++ b/NDDE/NDde/Source/NDde.Test/Program.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Windows.Forms; +using NDde; +using NDde.Advanced; +using NDde.Advanced.Monitor; +using NDde.Client; +using NDde.Server; + +namespace NDde.Test +{ + class Program + { + static void Main(string[] args) + { + } + + } // class + +} // namespace diff --git a/NDDE/NDde/Source/NDde.Test/Properties/AssemblyInfo.cs b/NDDE/NDde/Source/NDde.Test/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..cf2897b --- /dev/null +++ b/NDDE/NDde/Source/NDde.Test/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +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("NDde.Test")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("NDde.Test")] +[assembly: AssemblyCopyright("Copyright © 2005")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("8e005542-d8d5-43e9-bfcf-f418effca9b4")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/NDDE/NDde/Source/NDde.Test/Test_DdeClient.cs b/NDDE/NDde/Source/NDde.Test/Test_DdeClient.cs new file mode 100644 index 0000000..b290c64 --- /dev/null +++ b/NDDE/NDde/Source/NDde.Test/Test_DdeClient.cs @@ -0,0 +1,1282 @@ +namespace NDde.Test +{ + using System; + using System.Collections; + using System.Text; + using NDde; + using NDde.Advanced; + using NDde.Client; + using NDde.Server; + using NUnit.Framework; + + [TestFixture] + public sealed class Test_DdeClient + { + private const string ServiceName = "myservice"; + private const string TopicName = "mytopic"; + private const string ItemName = "myitem"; + private const string CommandText = "mycommand"; + private const string TestData = "Hello World"; + private const int Timeout = 1000; + + [Test] + public void Test_Ctor_Overload_1() + { + DdeClient client = new DdeClient(ServiceName, TopicName); + } + + [Test] + public void Test_Ctor_Overload_2() + { + using (DdeContext context = new DdeContext()) + { + DdeClient client = new DdeClient(ServiceName, TopicName, context); + } + } + + [Test] + public void Test_Dispose() + { + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + } + } + + [Test] + public void Test_Service() + { + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + Assert.AreEqual(ServiceName, client.Service); + } + } + + [Test] + public void Test_Topic() + { + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + Assert.AreEqual(TopicName, client.Topic); + } + } + + [Test] + public void Test_Connect() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + } + } + } + + [Test] + [ExpectedException(typeof(ObjectDisposedException))] + public void Test_Connect_After_Dispose() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Dispose(); + client.Connect(); + } + } + } + + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void Test_Connect_After_Connect() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + client.Connect(); + } + } + } + + [Test] + public void Test_Disconnect() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + client.Disconnect(); + } + } + } + + [Test] + [ExpectedException(typeof(ObjectDisposedException))] + public void Test_Disconnect_After_Dispose() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + client.Dispose(); + client.Disconnect(); + } + } + } + + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void Test_Disconnect_Before_Connect() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Disconnect(); + } + } + } + + + [Test] + public void Test_Handle_Variation_1() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + Assert.AreEqual(IntPtr.Zero, client.Handle); + } + } + } + + [Test] + public void Test_Handle_Variation_2() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + Assert.AreNotEqual(IntPtr.Zero, client.Handle); + } + } + } + + [Test] + public void Test_Handle_Variation_3() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + client.Disconnect(); + Assert.AreEqual(IntPtr.Zero, client.Handle); + } + } + } + + [Test] + public void Test_IsConnected_Variation_1() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + Assert.IsFalse(client.IsConnected); + } + } + } + + [Test] + public void Test_IsConnected_Variation_2() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + Assert.IsTrue(client.IsConnected); + } + } + } + + [Test] + public void Test_IsConnected_Variation_3() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + client.Disconnect(); + Assert.IsFalse(client.IsConnected); + } + } + } + + [Test] + public void Test_IsConnected_Variation_4() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + EventListener listener = new EventListener(); + client.Disconnected += listener.OnEvent; + client.Connect(); + server.Disconnect(); + Assert.IsTrue(listener.Received.WaitOne(Timeout, false)); + Assert.IsFalse(client.IsConnected); + } + } + } + + [Test] + public void Test_Pause() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + client.Pause(); + IAsyncResult ar = client.BeginExecute(CommandText, null, null); + Assert.IsFalse(ar.AsyncWaitHandle.WaitOne(Timeout, false)); + } + } + } + + [Test] + [ExpectedException(typeof(ObjectDisposedException))] + public void Test_Pause_After_Dispose() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + client.Dispose(); + client.Pause(); + } + } + } + + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void Test_Pause_After_Pause() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + client.Pause(); + client.Pause(); + } + } + } + + [Test] + public void Test_Resume() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + client.Pause(); + IAsyncResult ar = client.BeginExecute(CommandText, null, null); + Assert.IsFalse(ar.AsyncWaitHandle.WaitOne(Timeout, false)); + client.Resume(); + Assert.IsTrue(ar.AsyncWaitHandle.WaitOne(Timeout, false)); + } + } + } + + [Test] + [ExpectedException(typeof(ObjectDisposedException))] + public void Test_Resume_After_Dispose() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + client.Pause(); + client.Dispose(); + client.Resume(); + } + } + } + + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void Test_Resume_Before_Pause() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + client.Resume(); + } + } + } + + [Test] + public void Test_Abandon() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + client.Pause(); + IAsyncResult ar = client.BeginExecute(CommandText, null, null); + Assert.IsFalse(ar.AsyncWaitHandle.WaitOne(Timeout, false)); + client.Abandon(ar); + client.Resume(); + Assert.IsFalse(ar.AsyncWaitHandle.WaitOne(Timeout, false)); + } + } + } + + [Test] + [ExpectedException(typeof(ObjectDisposedException))] + public void Test_Abandon_After_Dispose() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + client.Pause(); + IAsyncResult ar = client.BeginExecute(CommandText, null, null); + client.Dispose(); + client.Abandon(ar); + } + } + } + + [Test] + public void Test_IsPaused_Variation_1() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + Assert.IsFalse(client.IsPaused); + } + } + } + + [Test] + public void Test_IsPaused_Variation_2() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + client.Pause(); + Assert.IsTrue(client.IsPaused); + } + } + } + + [Test] + public void Test_IsPaused_Variation_3() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + client.Pause(); + client.Resume(); + Assert.IsFalse(client.IsPaused); + } + } + } + + [Test] + public void Test_Poke() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + client.Poke(ItemName, Encoding.ASCII.GetBytes(TestData), 1, Timeout); + Assert.AreEqual(TestData, Encoding.ASCII.GetString(server.GetData(TopicName, ItemName, 1))); + } + } + } + + [Test] + public void Test_TryPoke_Variation_1() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + int result = client.TryPoke(ItemName, Encoding.ASCII.GetBytes(TestData), 1, Timeout); + Assert.AreNotEqual(0, result); + } + } + } + + [Test] + public void Test_TryPoke_Variation_2() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + int result = client.TryPoke(ItemName, Encoding.ASCII.GetBytes(TestData), 1, Timeout); + Assert.AreEqual(0, result); + Assert.AreEqual(TestData, Encoding.ASCII.GetString(server.GetData(TopicName, ItemName, 1))); + } + } + } + + [Test] + [ExpectedException(typeof(ObjectDisposedException))] + public void Test_Poke_After_Dispose() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + client.Dispose(); + client.Poke(ItemName, Encoding.ASCII.GetBytes(TestData), 1, Timeout); + } + } + } + + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void Test_Poke_Before_Connect() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Poke(ItemName, Encoding.ASCII.GetBytes(TestData), 1, Timeout); + } + } + } + + [Test] + public void Test_BeginPoke() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + IAsyncResult ar = client.BeginPoke(ItemName, Encoding.ASCII.GetBytes(TestData), 1, null, null); + Assert.IsTrue(ar.AsyncWaitHandle.WaitOne(Timeout, false)); + } + } + } + + [Test] + [ExpectedException(typeof(ObjectDisposedException))] + public void Test_BeginPoke_After_Dispose() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + client.Dispose(); + IAsyncResult ar = client.BeginPoke(ItemName, Encoding.ASCII.GetBytes(TestData), 1, null, null); + } + } + } + + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void Test_BeginPoke_Before_Connect() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + IAsyncResult ar = client.BeginPoke(ItemName, Encoding.ASCII.GetBytes(TestData), 1, null, null); + } + } + } + + [Test] + public void Test_EndPoke() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + IAsyncResult ar = client.BeginPoke(ItemName, Encoding.ASCII.GetBytes(TestData), 1, null, null); + Assert.IsTrue(ar.AsyncWaitHandle.WaitOne(Timeout, false)); + client.EndPoke(ar); + Assert.AreEqual(TestData, Encoding.ASCII.GetString(server.GetData(TopicName, ItemName, 1))); + } + } + } + + [Test] + [ExpectedException(typeof(ObjectDisposedException))] + public void Test_EndPoke_After_Dispose() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + IAsyncResult ar = client.BeginPoke(ItemName, Encoding.ASCII.GetBytes(TestData), 1, null, null); + Assert.IsTrue(ar.AsyncWaitHandle.WaitOne(Timeout, false)); + client.Dispose(); + client.EndPoke(ar); + } + } + } + + [Test] + public void Test_Request_Overload_1() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + server.SetData(TopicName, ItemName, 1, Encoding.ASCII.GetBytes(TestData)); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + byte[] data = client.Request(ItemName, 1, Timeout); + Assert.AreEqual(TestData, Encoding.ASCII.GetString(data)); + } + } + } + + [Test] + public void Test_Request_Overload_2() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + server.SetData(TopicName, ItemName, 1, Encoding.ASCII.GetBytes(TestData)); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + string data = client.Request(ItemName, Timeout); + Assert.AreEqual(TestData, data); + } + } + } + + [Test] + public void Test_TryRequest_Variation_1() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + server.SetData(TopicName, ItemName, 1, Encoding.ASCII.GetBytes(TestData)); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + byte[] data; + int result = client.TryRequest(ItemName, 1, Timeout, out data); + Assert.AreNotEqual(0, result); + } + } + } + + [Test] + public void Test_TryRequest_Variation_2() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + server.SetData(TopicName, ItemName, 1, Encoding.ASCII.GetBytes(TestData)); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + byte[] data; + int result = client.TryRequest(ItemName, 1, Timeout, out data); + Assert.AreEqual(0, result); + Assert.AreEqual(TestData, Encoding.ASCII.GetString(data)); + } + } + } + + [Test] + [ExpectedException(typeof(ObjectDisposedException))] + public void Test_Request_After_Dispose() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + server.SetData(TopicName, ItemName, 1, Encoding.ASCII.GetBytes(TestData)); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + client.Dispose(); + byte[] data = client.Request(ItemName, 1, Timeout); + } + } + } + + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void Test_Request_Before_Connect() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + server.SetData(TopicName, ItemName, 1, Encoding.ASCII.GetBytes(TestData)); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + byte[] data = client.Request(ItemName, 1, Timeout); + } + } + } + + [Test] + public void Test_BeginRequest() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + server.SetData(TopicName, ItemName, 1, Encoding.ASCII.GetBytes(TestData)); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + IAsyncResult ar = client.BeginRequest(ItemName, 1, null, null); + Assert.IsTrue(ar.AsyncWaitHandle.WaitOne(Timeout, false)); + } + } + } + + [Test] + [ExpectedException(typeof(ObjectDisposedException))] + public void Test_BeginRequest_After_Dispose() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + server.SetData(TopicName, ItemName, 1, Encoding.ASCII.GetBytes(TestData)); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + client.Dispose(); + IAsyncResult ar = client.BeginRequest(ItemName, 1, null, null); + } + } + } + + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void Test_BeginRequest_Before_Connect() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + server.SetData(TopicName, ItemName, 1, Encoding.ASCII.GetBytes(TestData)); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + IAsyncResult ar = client.BeginRequest(ItemName, 1, null, null); + } + } + } + + [Test] + public void Test_EndRequest() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + server.SetData(TopicName, ItemName, 1, Encoding.ASCII.GetBytes(TestData)); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + IAsyncResult ar = client.BeginRequest(ItemName, 1, null, null); + Assert.IsTrue(ar.AsyncWaitHandle.WaitOne(Timeout, false)); + byte[] data = client.EndRequest(ar); + Assert.AreEqual(TestData, Encoding.ASCII.GetString(data)); + } + } + } + + [Test] + [ExpectedException(typeof(ObjectDisposedException))] + public void Test_EndRequest_After_Dispose() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + server.SetData(TopicName, ItemName, 1, Encoding.ASCII.GetBytes(TestData)); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + IAsyncResult ar = client.BeginRequest(ItemName, 1, null, null); + Assert.IsTrue(ar.AsyncWaitHandle.WaitOne(Timeout, false)); + client.Dispose(); + byte[] data = client.EndRequest(ar); + } + } + } + + [Test] + public void Test_Execute() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + client.Execute(TestData, Timeout); + Assert.AreEqual(TestData, server.Command); + } + } + } + + [Test] + public void Test_TryExecute_Variation_1() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + int result = client.TryExecute(TestData, Timeout); + Assert.AreNotEqual(0, result); + } + } + } + + [Test] + public void Test_TryExecute_Variation_2() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + int result = client.TryExecute(TestData, Timeout); + Assert.AreEqual(0, result); + Assert.AreEqual(TestData, server.Command); + } + } + } + + [Test] + [ExpectedException(typeof(ObjectDisposedException))] + public void Test_Execute_After_Dispose() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + client.Dispose(); + client.Execute(TestData, Timeout); + } + } + } + + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void Test_Execute_Before_Connect() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Execute(TestData, Timeout); + } + } + } + + [Test] + public void Test_BeginExecute() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + IAsyncResult ar = client.BeginExecute(TestData, null, null); + Assert.IsTrue(ar.AsyncWaitHandle.WaitOne(Timeout, false)); + } + } + } + + [Test] + [ExpectedException(typeof(ObjectDisposedException))] + public void Test_BeginExecute_After_Dispose() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + client.Dispose(); + IAsyncResult ar = client.BeginExecute(TestData, null, null); + } + } + } + + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void Test_BeginExecute_Before_Connect() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + IAsyncResult ar = client.BeginExecute(TestData, null, null); + } + } + } + + [Test] + public void Test_EndExecute() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + IAsyncResult ar = client.BeginExecute(TestData, null, null); + Assert.IsTrue(ar.AsyncWaitHandle.WaitOne(Timeout, false)); + client.EndExecute(ar); + Assert.AreEqual(TestData, server.Command); + } + } + } + + [Test] + [ExpectedException(typeof(ObjectDisposedException))] + public void Test_EndExecute_After_Dispose() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + IAsyncResult ar = client.BeginExecute(TestData, null, null); + Assert.IsTrue(ar.AsyncWaitHandle.WaitOne(Timeout, false)); + client.Dispose(); + client.EndExecute(ar); + } + } + } + + [Test] + public void Test_Disconnected_Variation_1() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + EventListener listener = new EventListener(); + client.Disconnected += listener.OnEvent; + client.Connect(); + client.Disconnect(); + Assert.IsTrue(listener.Received.WaitOne(Timeout, false)); + DdeDisconnectedEventArgs args = (DdeDisconnectedEventArgs)listener.Events[0]; + Assert.IsFalse(args.IsServerInitiated); + Assert.IsFalse(args.IsDisposed); + } + } + } + + [Test] + public void Test_Disconnected_Variation_2() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + EventListener listener = new EventListener(); + client.Disconnected += listener.OnEvent; + client.Connect(); + server.Disconnect(); + Assert.IsTrue(listener.Received.WaitOne(Timeout, false)); + DdeDisconnectedEventArgs args = (DdeDisconnectedEventArgs)listener.Events[0]; + Assert.IsTrue(args.IsServerInitiated); + Assert.IsFalse(args.IsDisposed); + } + } + } + + [Test] + public void Test_Disconnected_Variation_3() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + EventListener listener = new EventListener(); + client.Disconnected += listener.OnEvent; + client.Connect(); + client.Dispose(); + Assert.IsTrue(listener.Received.WaitOne(Timeout, false)); + DdeDisconnectedEventArgs args = (DdeDisconnectedEventArgs)listener.Events[0]; + Assert.IsFalse(args.IsServerInitiated); + Assert.IsTrue(args.IsDisposed); + } + } + } + + [Test] + public void Test_StartAdvise_Variation_1() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + server.SetData(TopicName, ItemName, 1, Encoding.ASCII.GetBytes(TestData)); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + EventListener listener = new EventListener(); + client.Advise += listener.OnEvent; + client.Connect(); + client.StartAdvise(ItemName, 1, true, Timeout); + server.Advise(TopicName, ItemName); + Assert.IsTrue(listener.Received.WaitOne(Timeout, false)); + DdeAdviseEventArgs args = (DdeAdviseEventArgs)listener.Events[0]; + Assert.AreEqual(ItemName, args.Item); + Assert.AreEqual(1, args.Format); + Assert.AreEqual(TestData, Encoding.ASCII.GetString(args.Data)); + Assert.AreEqual(TestData, args.Text); + } + } + } + + [Test] + public void Test_StartAdvise_Variation_2() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + server.SetData(TopicName, ItemName, 1, Encoding.ASCII.GetBytes(TestData)); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + EventListener listener = new EventListener(); + client.Advise += listener.OnEvent; + client.Connect(); + client.StartAdvise(ItemName, 1, false, Timeout); + server.Advise(TopicName, ItemName); + Assert.IsTrue(listener.Received.WaitOne(Timeout, false)); + DdeAdviseEventArgs args = (DdeAdviseEventArgs)listener.Events[0]; + Assert.AreEqual(ItemName, args.Item); + Assert.AreEqual(1, args.Format); + Assert.IsNull(args.Data); + Assert.IsNull(args.Text); + } + } + } + + [Test] + public void Test_StartAdvise_Variation_3() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + server.SetData(TopicName, ItemName, 1, Encoding.ASCII.GetBytes(TestData)); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + EventListener listener = new EventListener(); + client.Advise += listener.OnEvent; + client.Connect(); + client.StartAdvise(ItemName, 1, true, true, Timeout, "MyStateObject"); + server.Advise(TopicName, ItemName); + Assert.IsTrue(listener.Received.WaitOne(Timeout, false)); + DdeAdviseEventArgs args = (DdeAdviseEventArgs)listener.Events[0]; + Assert.AreEqual(ItemName, args.Item); + Assert.AreEqual(1, args.Format); + Assert.AreEqual("MyStateObject", args.State); + Assert.AreEqual(TestData, Encoding.ASCII.GetString(args.Data)); + Assert.AreEqual(TestData, args.Text); + } + } + } + + [Test] + [ExpectedException(typeof(ObjectDisposedException))] + public void Test_StartAdvise_After_Dispose() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + server.SetData(TopicName, ItemName, 1, Encoding.ASCII.GetBytes(TestData)); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + client.Dispose(); + client.StartAdvise(ItemName, 1, false, Timeout); + } + } + } + + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void Test_StartAdvise_Before_Connect() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + server.SetData(TopicName, ItemName, 1, Encoding.ASCII.GetBytes(TestData)); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.StartAdvise(ItemName, 1, false, Timeout); + } + } + } + + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void Test_StartAdvise_After_StartAdvise() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + server.SetData(TopicName, ItemName, 1, Encoding.ASCII.GetBytes(TestData)); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.StartAdvise(ItemName, 1, false, Timeout); + client.StartAdvise(ItemName, 1, false, Timeout); + } + } + } + + [Test] + public void Test_BeginStartAdvise_Variation_1() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + server.SetData(TopicName, ItemName, 1, Encoding.ASCII.GetBytes(TestData)); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + EventListener listener = new EventListener(); + client.Advise += listener.OnEvent; + client.Connect(); + IAsyncResult ar = client.BeginStartAdvise(ItemName, 1, true, null, null); + Assert.IsTrue(ar.AsyncWaitHandle.WaitOne(Timeout, false)); + server.Advise(TopicName, ItemName); + Assert.IsTrue(listener.Received.WaitOne(Timeout, false)); + DdeAdviseEventArgs args = (DdeAdviseEventArgs)listener.Events[0]; + Assert.AreEqual(ItemName, args.Item); + Assert.AreEqual(1, args.Format); + Assert.AreEqual(TestData, Encoding.ASCII.GetString(args.Data)); + } + } + } + + [Test] + public void Test_BeginStartAdvise_Variation_2() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + server.SetData(TopicName, ItemName, 1, Encoding.ASCII.GetBytes(TestData)); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + EventListener listener = new EventListener(); + client.Advise += listener.OnEvent; + client.Connect(); + IAsyncResult ar = client.BeginStartAdvise(ItemName, 1, false, null, null); + Assert.IsTrue(ar.AsyncWaitHandle.WaitOne(Timeout, false)); + server.Advise(TopicName, ItemName); + Assert.IsTrue(listener.Received.WaitOne(Timeout, false)); + DdeAdviseEventArgs args = (DdeAdviseEventArgs)listener.Events[0]; + Assert.AreEqual(ItemName, args.Item); + Assert.AreEqual(1, args.Format); + Assert.IsNull(args.Data); + } + } + } + + [Test] + public void Test_BeginStartAdvise_Variation_3() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + server.SetData(TopicName, ItemName, 1, Encoding.ASCII.GetBytes(TestData)); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + EventListener listener = new EventListener(); + client.Advise += listener.OnEvent; + client.Connect(); + IAsyncResult ar = client.BeginStartAdvise(ItemName, 1, true, true, null, null, "MyStateObject"); + Assert.IsTrue(ar.AsyncWaitHandle.WaitOne(Timeout, false)); + server.Advise(TopicName, ItemName); + Assert.IsTrue(listener.Received.WaitOne(Timeout, false)); + DdeAdviseEventArgs args = (DdeAdviseEventArgs)listener.Events[0]; + Assert.AreEqual(ItemName, args.Item); + Assert.AreEqual(1, args.Format); + Assert.AreEqual("MyStateObject", args.State); + Assert.AreEqual(TestData, Encoding.ASCII.GetString(args.Data)); + } + } + } + + [Test] + [ExpectedException(typeof(ObjectDisposedException))] + public void Test_BeginStartAdvise_After_Dispose() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + server.SetData(TopicName, ItemName, 1, Encoding.ASCII.GetBytes(TestData)); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + client.Dispose(); + IAsyncResult ar = client.BeginStartAdvise(ItemName, 1, false, null, null); + } + } + } + + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void Test_BeginStartAdvise_Before_Connect() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + server.SetData(TopicName, ItemName, 1, Encoding.ASCII.GetBytes(TestData)); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + IAsyncResult ar = client.BeginStartAdvise(ItemName, 1, false, null, null); + } + } + } + + [Test] + public void Test_EndStartAdvise() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + server.SetData(TopicName, ItemName, 1, Encoding.ASCII.GetBytes(TestData)); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + IAsyncResult ar = client.BeginStartAdvise(ItemName, 1, true, null, null); + Assert.IsTrue(ar.AsyncWaitHandle.WaitOne(Timeout, false)); + client.EndStartAdvise(ar); + } + } + } + + [Test] + [ExpectedException(typeof(ObjectDisposedException))] + public void Test_EndStartAdvise_After_Dispose() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + server.SetData(TopicName, ItemName, 1, Encoding.ASCII.GetBytes(TestData)); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + IAsyncResult ar = client.BeginStartAdvise(ItemName, 1, true, null, null); + Assert.IsTrue(ar.AsyncWaitHandle.WaitOne(Timeout, false)); + client.Dispose(); + client.EndStartAdvise(ar); + } + } + } + + [Test] + public void Test_StopAdvise() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + server.SetData(TopicName, ItemName, 1, Encoding.ASCII.GetBytes(TestData)); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + client.StartAdvise(ItemName, 1, true, Timeout); + client.StopAdvise(ItemName, Timeout); + } + } + } + + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void Test_StopAdvise_Before_StartAdvise() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + server.SetData(TopicName, ItemName, 1, Encoding.ASCII.GetBytes(TestData)); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + client.StopAdvise(ItemName, Timeout); + } + } + } + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde.Test/Test_DdeContext.cs b/NDDE/NDde/Source/NDde.Test/Test_DdeContext.cs new file mode 100644 index 0000000..5bb64bc --- /dev/null +++ b/NDDE/NDde/Source/NDde.Test/Test_DdeContext.cs @@ -0,0 +1,209 @@ +namespace NDde.Test +{ + using System; + using System.Collections; + using NDde; + using NDde.Advanced; + using NDde.Client; + using NDde.Server; + using NUnit.Framework; + + [TestFixture] + public sealed class Test_DdeContext + { + private const string ServiceName = "test"; + private const int Timeout = 1000; + + [Test] + public void Test_Ctor_Overload_1() + { + DdeContext context = new DdeContext(); + } + + [Test] + public void Test_Ctor_Overload_2() + { + DdeContext context = new DdeContext(new DdeContext()); + } + + [Test] + public void Test_Dispose() + { + using (DdeContext context = new DdeContext()) + { + } + } + + [Test] + public void Test_Initialize() + { + using (DdeContext context = new DdeContext()) + { + context.Initialize(); + } + } + + [Test] + [ExpectedException(typeof(ObjectDisposedException))] + public void Test_Initialize_After_Dispose() + { + using (DdeContext context = new DdeContext()) + { + context.Dispose(); + context.Initialize(); + } + } + + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void Test_Initialize_After_Initialize() + { + using (DdeContext context = new DdeContext()) + { + context.Initialize(); + context.Initialize(); + } + } + + [Test] + public void Test_IsInitialized_Variation_1() + { + using (DdeContext context = new DdeContext()) + { + Assert.IsFalse(context.IsInitialized); + } + } + + [Test] + public void Test_IsInitialized_Variation_2() + { + using (DdeContext context = new DdeContext()) + { + context.Initialize(); + Assert.IsTrue(context.IsInitialized); + } + } + + [Test] + public void Test_AddTransactionFilter() + { + using (DdeContext context = new DdeContext()) + { + IDdeTransactionFilter filter = new TransactionFilter(); + context.AddTransactionFilter(filter); + } + } + + [Test] + [ExpectedException(typeof(ObjectDisposedException))] + public void Test_AddTransactionFilter_After_Dispose() + { + using (DdeContext context = new DdeContext()) + { + IDdeTransactionFilter filter = new TransactionFilter(); + context.Dispose(); + context.AddTransactionFilter(filter); + } + } + + [Test] + public void Test_RemoveTransactionFilter() + { + using (DdeContext context = new DdeContext()) + { + TransactionFilter filter = new TransactionFilter(); + context.AddTransactionFilter(filter); + context.RemoveTransactionFilter(filter); + } + } + + [Test] + [ExpectedException(typeof(ObjectDisposedException))] + public void Test_RemoveTransactionFilter_After_Dispose() + { + using (DdeContext context = new DdeContext()) + { + TransactionFilter filter = new TransactionFilter(); + context.AddTransactionFilter(filter); + context.Dispose(); + context.RemoveTransactionFilter(filter); + } + } + + [Test] + public void Test_TransactionFilter() + { + using (DdeContext context = new DdeContext()) + { + TransactionFilter filter = new TransactionFilter(); + context.AddTransactionFilter(filter); + context.Initialize(); + using (DdeServer server = new TestServer(ServiceName)) + { + server.Register(); + } + Assert.IsTrue(filter.Received.WaitOne(Timeout, false)); + } + } + + [Test] + public void Test_Register() + { + using (DdeContext context = new DdeContext()) + { + EventListener listener = new EventListener(); + context.Register += listener.OnEvent; + context.Initialize(); + using (DdeServer server = new TestServer(ServiceName)) + { + server.Register(); + } + Assert.IsTrue(listener.Received.WaitOne(Timeout, false)); + } + } + + [Test] + public void Test_Unregister() + { + using (DdeContext context = new DdeContext()) + { + EventListener listener = new EventListener(); + context.Unregister += listener.OnEvent; + context.Initialize(); + using (DdeServer server = new TestServer(ServiceName)) + { + server.Register(); + server.Unregister(); + } + Assert.IsTrue(listener.Received.WaitOne(Timeout, false)); + } + } + + #region TransactionFilter + private sealed class TransactionFilter : IDdeTransactionFilter + { + private System.Threading.ManualResetEvent _Received = new System.Threading.ManualResetEvent(false); + private ArrayList _Transactions = new ArrayList(); + + public IList Transactions + { + get { return ArrayList.ReadOnly(_Transactions); } + } + + public System.Threading.WaitHandle Received + { + get { return _Received; } + } + + public bool PreFilterTransaction(DdeTransaction t) + { + _Transactions.Add(t); + _Received.Set(); + return false; + } + } + #endregion + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde.Test/Test_DdeServer.cs b/NDDE/NDde/Source/NDde.Test/Test_DdeServer.cs new file mode 100644 index 0000000..ca9b235 --- /dev/null +++ b/NDDE/NDde/Source/NDde.Test/Test_DdeServer.cs @@ -0,0 +1,249 @@ +namespace NDde.Test +{ + using System; + using System.Collections; + using System.Text; + using System.Timers; + using NDde; + using NDde.Advanced; + using NDde.Client; + using NDde.Server; + using NUnit.Framework; + + [TestFixture] + public sealed class Test_DdeServer + { + private const string ServiceName = "myservice"; + private const string TopicName = "mytopic"; + private const string ItemName = "myitem"; + private const string CommandText = "mycommand"; + private const string TestData = "Hello World"; + private const int Timeout = 1000; + + [Test] + public void Test_Ctor_Overload_1() + { + DdeServer server = new TestServer(ServiceName); + } + + [Test] + public void Test_Ctor_Overload_2() + { + using (DdeContext context = new DdeContext()) + { + DdeServer server = new TestServer(ServiceName); + } + } + + [Test] + public void Test_Register() + { + using (DdeServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + } + } + } + + [Test] + [ExpectedException(typeof(ObjectDisposedException))] + public void Test_Register_After_Dispose() + { + using (DdeServer server = new TestServer(ServiceName)) + { + server.Dispose(); + server.Register(); + } + } + + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void Test_Register_After_Register() + { + using (DdeServer server = new TestServer(ServiceName)) + { + server.Register(); + server.Register(); + } + } + + [Test] + public void Test_Unregister() + { + using (DdeServer server = new TestServer(ServiceName)) + { + server.Register(); + server.Unregister(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + try + { + client.Connect(); + Assert.Fail(); + } + catch (DdeException e) + { + Assert.AreEqual(0x400A, e.Code); + } + } + } + } + + [Test] + [ExpectedException(typeof(ObjectDisposedException))] + public void Test_Unregister_After_Dispose() + { + using (DdeServer server = new TestServer(ServiceName)) + { + server.Register(); + server.Dispose(); + server.Unregister(); + } + } + + [Test] + [ExpectedException(typeof(InvalidOperationException))] + public void Test_Unregister_Before_Register() + { + using (DdeServer server = new TestServer(ServiceName)) + { + server.Unregister(); + } + } + + [Test] + public void Test_Execute() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + client.Execute(CommandText, Timeout); + Assert.AreEqual(CommandText, server.Command); + } + } + } + + [Test] + public void Test_Execute_NotProcessed() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + try + { + client.Execute("#NotProcessed", Timeout); + } + catch (DdeException e) + { + Assert.AreEqual(0x4009, e.Code); + } + } + } + } + + [Test] + public void Test_Execute_PauseConversation() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + client.Execute("#PauseConversation", (int)server.Interval * 2); + } + } + } + + [Test] + public void Test_Execute_TooBusy() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + try + { + client.Execute("#TooBusy", Timeout); + } + catch (DdeException e) + { + Assert.AreEqual(0x4001, e.Code); + } + } + } + } + + [Test] + public void Test_Poke() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + client.Poke(ItemName, Encoding.ASCII.GetBytes(TestData), 1, Timeout); + Assert.AreEqual(TestData, Encoding.ASCII.GetString(server.GetData(TopicName, ItemName, 1))); + } + } + } + + [Test] + public void Test_Request() + { + using (TestServer server = new TestServer(ServiceName)) + { + server.Register(); + server.SetData(TopicName, ItemName, 1, Encoding.ASCII.GetBytes(TestData)); + using (DdeClient client = new DdeClient(ServiceName, TopicName)) + { + client.Connect(); + byte[] data = client.Request(ItemName, 1, Timeout); + Assert.AreEqual(TestData, Encoding.ASCII.GetString(data)); + } + } + } + + [Test] + public void Test_IsRegistered_Variation_1() + { + using (DdeServer server = new TestServer(ServiceName)) + { + Assert.IsFalse(server.IsRegistered); + } + } + + [Test] + public void Test_IsRegistered_Variation_2() + { + using (DdeServer server = new TestServer(ServiceName)) + { + server.Register(); + Assert.IsTrue(server.IsRegistered); + } + } + + [Test] + public void Test_Service() + { + using (DdeServer server = new TestServer(ServiceName)) + { + Assert.AreEqual(ServiceName, server.Service); + } + } + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde.sln b/NDDE/NDde/Source/NDde.sln new file mode 100644 index 0000000..4fd9ae4 --- /dev/null +++ b/NDDE/NDde/Source/NDde.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NDde", "NDde\NDde.csproj", "{D77772F9-3D3D-40BA-B95F-05C45878078F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NDde.Test", "NDde.Test\NDde.Test.csproj", "{D6FDDD04-3BB0-4AE4-B69A-CE8C1238FB00}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D77772F9-3D3D-40BA-B95F-05C45878078F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D77772F9-3D3D-40BA-B95F-05C45878078F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D77772F9-3D3D-40BA-B95F-05C45878078F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D77772F9-3D3D-40BA-B95F-05C45878078F}.Release|Any CPU.Build.0 = Release|Any CPU + {D6FDDD04-3BB0-4AE4-B69A-CE8C1238FB00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D6FDDD04-3BB0-4AE4-B69A-CE8C1238FB00}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D6FDDD04-3BB0-4AE4-B69A-CE8C1238FB00}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D6FDDD04-3BB0-4AE4-B69A-CE8C1238FB00}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/NDDE/NDde/Source/NDde/Documentation/Examples.xml b/NDDE/NDde/Source/NDde/Documentation/Examples.xml new file mode 100644 index 0000000..fdaee64 --- /dev/null +++ b/NDDE/NDde/Source/NDde/Documentation/Examples.xml @@ -0,0 +1,649 @@ + + + + + The following example demonstrates how to instantiate a DdeContext in a console application. + + using System; + using NDde.Advanced; + + public class Example + { + public static void Main() + { + // Create a context that uses a dedicated thread for DDE message pumping. + DdeContext context = new DdeContext(); + } + } + + + Imports NDde.Advanced + + Public Class Example + Public Shared Sub Main() + ' Create a context that uses a dedicated thread for DDE message pumping. + Dim context As DdeContext = New DdeContext() + End Sub + End Class + + The following example demonstrates how to instantiate a DdeContext in a windows application. + + using System; + using NDde.Advanced; + + public class Example : Form + { + // Standard Form code omitted for brevity. + + private DdeContext context = null; + + private void Form1_Load(object sender, System.EventArgs e) + { + // Create a context that uses the UI thread for DDE message pumping. + context = new DdeContext(this); + } + } + + + Imports NDde.Advanced + + Public Class Example + Inherits Form + + Private context as DdeContext = Nothing + + Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) + ' Create a context that uses the UI thread for DDE message pumping. + context = New DdeContext(Me) + End Sub + End Class + + + + + + The following example demostrates how to use a DdeServer. + + using System; + using System.Collections; + using System.Timers; + using NDde.Server; + + public class Server + { + public static void Main() + { + try + { + // Create a server that will register the service name 'myapp'. + using (DdeServer server = new MyServer("myapp")) + { + // Register the service name. + server.Register(); + + // Wait for the user to press ENTER before proceding. + Console.WriteLine("Press ENTER to quit..."); + Console.ReadLine(); + } + } + catch (Exception e) + { + Console.WriteLine(e); + Console.WriteLine("Press ENTER to quit..."); + Console.ReadLine(); + } + } + + private sealed class MyServer : DdeServer + { + private System.Timers.Timer _Timer = new System.Timers.Timer(); + + public MyServer(string service) : base(service) + { + // Create a timer that will be used to advise clients of new data. + _Timer.Elapsed += this.OnTimerElapsed; + _Timer.Interval = 1000; + _Timer.SynchronizingObject = this.Context; + _Timer.Start(); + } + + private void OnTimerElapsed(object sender, ElapsedEventArgs args) + { + // Advise all topic name and item name pairs. + Advise("*", "*"); + } + + protected override bool OnBeforeConnect(string topic) + { + Console.WriteLine("OnBeforeConnect:".PadRight(16) + + " Service='" + base.Service + "'" + + " Topic='" + topic + "'"); + + return true; + } + + protected override void OnAfterConnect(DdeConversation conversation) + { + Console.WriteLine("OnAfterConnect:".PadRight(16) + + " Service='" + conversation.Service + "'" + + " Topic='" + conversation.Topic + "'" + + " Handle=" + conversation.Handle.ToString()); + } + + protected override void OnDisconnect(DdeConversation conversation) + { + Console.WriteLine("OnDisconnect:".PadRight(16) + + " Service='" + conversation.Service + "'" + + " Topic='" + conversation.Topic + "'" + + " Handle=" + conversation.Handle.ToString()); + } + + protected override bool OnStartAdvise(DdeConversation conversation, string item, int format) + { + Console.WriteLine("OnStartAdvise:".PadRight(16) + + " Service='" + conversation.Service + "'" + + " Topic='" + conversation.Topic + "'" + + " Handle=" + conversation.Handle.ToString() + + " Item='" + item + "'" + + " Format=" + format.ToString()); + + // Initiate the advisory loop only if the format is CF_TEXT. + return format == 1; + } + + protected override void OnStopAdvise(DdeConversation conversation, string item) + { + Console.WriteLine("OnStopAdvise:".PadRight(16) + + " Service='" + conversation.Service + "'" + + " Topic='" + conversation.Topic + "'" + + " Handle=" + conversation.Handle.ToString() + + " Item='" + item + "'"); + } + + protected override ExecuteResult OnExecute(DdeConversation conversation, string command) + { + Console.WriteLine("OnExecute:".PadRight(16) + + " Service='" + conversation.Service + "'" + + " Topic='" + conversation.Topic + "'" + + " Handle=" + conversation.Handle.ToString() + + " Command='" + command + "'"); + + // Tell the client that the command was processed. + return ExecuteResult.Processed; + } + + protected override PokeResult OnPoke(DdeConversation conversation, string item, byte[] data, int format) + { + Console.WriteLine("OnPoke:".PadRight(16) + + " Service='" + conversation.Service + "'" + + " Topic='" + conversation.Topic + "'" + + " Handle=" + conversation.Handle.ToString() + + " Item='" + item + "'" + + " Data=" + data.Length.ToString() + + " Format=" + format.ToString()); + + // Tell the client that the data was processed. + return PokeResult.Processed; + } + + protected override RequestResult OnRequest(DdeConversation conversation, string item, int format) + { + Console.WriteLine("OnRequest:".PadRight(16) + + " Service='" + conversation.Service + "'" + + " Topic='" + conversation.Topic + "'" + + " Handle=" + conversation.Handle.ToString() + + " Item='" + item + "'" + + " Format=" + format.ToString()); + + // Return data to the client only if the format is CF_TEXT. + if (format == 1) + { + return new RequestResult(System.Text.Encoding.ASCII.GetBytes("Time=" + DateTime.Now.ToString() + "\0")); + } + return RequestResult.NotProcessed; + } + + protected override byte[] OnAdvise(string topic, string item, int format) + { + Console.WriteLine("OnAdvise:".PadRight(16) + + " Service='" + this.Service + "'" + + " Topic='" + topic + "'" + + " Item='" + item + "'" + + " Format=" + format.ToString()); + + // Send data to the client only if the format is CF_TEXT. + if (format == 1) + { + return System.Text.Encoding.ASCII.GetBytes("Time=" + DateTime.Now.ToString() + "\0"); + } + return null; + } + + } // class + + } // class + + + Imports NDde.Server + + Module Program + + Sub Main() + + Try + + ' Create a server that will register the service name 'myapp'. + Using server As DdeServer = New MyServer("myapp") + + ' Register the service name. + server.Register() + + ' Wait for the user to press ENTER before proceding. + Console.WriteLine("Press ENTER to quit...") + Console.ReadLine() + + End Using + + Catch e As Exception + + Console.WriteLine(e) + Console.WriteLine("Press ENTER to quit...") + Console.ReadLine() + + End Try + + End Sub + + Private Class MyServer + Inherits DdeServer + + Private WithEvents theTimer As System.Timers.Timer = New System.Timers.Timer() + + Public Sub New(ByVal service As String) + MyBase.New(service) + ' Create a timer that will be used to advise clients of new data. + theTimer.Interval = 1000 + theTimer.SynchronizingObject = Me.Context + theTimer.Start() + End Sub + + Private Sub theTimer_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) Handles theTimer.Elapsed + Me.Advise("*", "*") + End Sub + + Protected Overrides Function OnBeforeConnect(ByVal topic As String) As Boolean + Console.WriteLine("OnBeforeConnect:".PadRight(16) _ + + " Service='" + MyBase.Service + "'" _ + + " Topic='" + topic + "'") + + Return True + End Function + + Protected Overrides Sub OnAfterConnect(ByVal conversation As DdeConversation) + Console.WriteLine("OnAfterConnect:".PadRight(16) _ + + " Service='" + conversation.Service + "'" _ + + " Topic='" + conversation.Topic + "'" _ + + " Handle=" + conversation.Handle.ToString()) + End Sub + + Protected Overrides Sub OnDisconnect(ByVal conversation As DdeConversation) + Console.WriteLine("OnDisconnect:".PadRight(16) _ + + " Service='" + conversation.Service + "'" _ + + " Topic='" + conversation.Topic + "'" _ + + " Handle=" + conversation.Handle.ToString()) + End Sub + + Protected Overrides Function OnStartAdvise(ByVal conversation As DdeConversation, ByVal item As String, ByVal format As Integer) As Boolean + Console.WriteLine("OnStartAdvise:".PadRight(16) _ + + " Service='" + conversation.Service + "'" _ + + " Topic='" + conversation.Topic + "'" _ + + " Handle=" + conversation.Handle.ToString() _ + + " Item='" + item + "'" _ + + " Format=" + format.ToString()) + + ' Initiate the advisory loop only if the format is CF_TEXT. + Return format = 1 + End Function + + Protected Overrides Sub OnStopAdvise(ByVal conversation As DdeConversation, ByVal item As String) + Console.WriteLine("OnStopAdvise:".PadRight(16) _ + + " Service='" + conversation.Service + "'" _ + + " Topic='" + conversation.Topic + "'" _ + + " Handle=" + conversation.Handle.ToString() _ + + " Item='" + item + "'") + End Sub + + Protected Overrides Function OnExecute(ByVal conversation As DdeConversation, ByVal command As String) As ExecuteResult + Console.WriteLine("OnExecute:".PadRight(16) _ + + " Service='" + conversation.Service + "'" _ + + " Topic='" + conversation.Topic + "'" _ + + " Handle=" + conversation.Handle.ToString() _ + + " Command='" + command + "'") + + ' Tell the client that the command was processed. + Return ExecuteResult.Processed + End Function + + Protected Overrides Function OnPoke(ByVal conversation As DdeConversation, ByVal item As String, ByVal data As Byte(), ByVal format As Integer) As PokeResult + Console.WriteLine("OnPoke:".PadRight(16) _ + + " Service='" + conversation.Service + "'" _ + + " Topic='" + conversation.Topic + "'" _ + + " Handle=" + conversation.Handle.ToString() _ + + " Item='" + item + "'" _ + + " Data=" + data.Length.ToString() _ + + " Format=" + format.ToString()) + + ' Tell the client that the data was processed. + Return PokeResult.Processed + End Function + + Protected Overrides Function OnRequest(ByVal conversation As DdeConversation, ByVal item As String, ByVal format As Integer) As RequestResult + Console.WriteLine("OnRequest:".PadRight(16) _ + + " Service='" + conversation.Service + "'" _ + + " Topic='" + conversation.Topic + "'" _ + + " Handle=" + conversation.Handle.ToString() _ + + " Item='" + item + "'" _ + + " Format=" + format.ToString()) + + ' Return data to the client only if the format is CF_TEXT. + If format = 1 Then + Return New RequestResult(System.Text.Encoding.ASCII.GetBytes("Time=" + DateTime.Now.ToString() + Convert.ToChar(0))) + End If + Return RequestResult.NotProcessed + End Function + + Protected Overrides Function OnAdvise(ByVal topic As String, ByVal item As String, ByVal format As Integer) As Byte() + Console.WriteLine("OnAdvise:".PadRight(16) _ + + " Service='" + Me.Service + "'" _ + + " Topic='" + topic + "'" _ + + " Item='" + item + "'" _ + + " Format=" + format.ToString()) + + ' Send data to the client only if the format is CF_TEXT. + If format = 1 Then + Return System.Text.Encoding.ASCII.GetBytes("Time=" + DateTime.Now.ToString() + Convert.ToChar(0)) + End If + Return Nothing + End Function + + End Class + + End Module + + + + + + The following example demonstrates how to use a DdeClient. + + using System; + using System.Text; + using NDde.Client; + + public sealed class Client + { + public static void Main(string[] args) + { + // Wait for the user to press ENTER before proceding. + Console.WriteLine("The Server sample must be running before the client can connect."); + Console.WriteLine("Press ENTER to continue..."); + Console.ReadLine(); + try + { + // Create a client that connects to 'myapp|mytopic'. + using (DdeClient client = new DdeClient("myapp", "mytopic")) + { + // Subscribe to the Disconnected event. This event will notify the application when a conversation has been terminated. + client.Disconnected += OnDisconnected; + + // Connect to the server. It must be running or an exception will be thrown. + client.Connect(); + + // Synchronous Execute Operation + client.Execute("mycommand", 60000); + + // Synchronous Poke Operation + client.Poke("myitem", DateTime.Now.ToString(), 60000); + + // Syncronous Request Operation + Console.WriteLine("Request: " + client.Request("myitem", 60000)); + + // Asynchronous Execute Operation + client.BeginExecute("mycommand", OnExecuteComplete, client); + + // Asynchronous Poke Operation + client.BeginPoke("myitem", Encoding.ASCII.GetBytes(DateTime.Now.ToString() + "\0"), 1, OnPokeComplete, client); + + // Asynchronous Request Operation + client.BeginRequest("myitem", 1, OnRequestComplete, client); + + // Advise Loop + client.StartAdvise("myitem", 1, true, 60000); + client.Advise += OnAdvise; + + // Wait for the user to press ENTER before proceding. + Console.WriteLine("Press ENTER to quit..."); + Console.ReadLine(); + } + } + catch (Exception e) + { + Console.WriteLine(e.ToString()); + Console.WriteLine("Press ENTER to quit..."); + Console.ReadLine(); + } + } + + private static void OnExecuteComplete(IAsyncResult ar) + { + try + { + DdeClient client = (DdeClient)ar.AsyncState; + client.EndExecute(ar); + Console.WriteLine("OnExecuteComplete"); + } + catch (Exception e) + { + Console.WriteLine("OnExecuteComplete: " + e.Message); + } + } + + private static void OnPokeComplete(IAsyncResult ar) + { + try + { + DdeClient client = (DdeClient)ar.AsyncState; + client.EndPoke(ar); + Console.WriteLine("OnPokeComplete"); + } + catch (Exception e) + { + Console.WriteLine("OnPokeComplete: " + e.Message); + } + } + + private static void OnRequestComplete(IAsyncResult ar) + { + try + { + DdeClient client = (DdeClient)ar.AsyncState; + byte[] data = client.EndRequest(ar); + Console.WriteLine("OnRequestComplete: " + Encoding.ASCII.GetString(data)); + } + catch (Exception e) + { + Console.WriteLine("OnRequestComplete: " + e.Message); + } + } + + private static void OnStartAdviseComplete(IAsyncResult ar) + { + try + { + DdeClient client = (DdeClient)ar.AsyncState; + client.EndStartAdvise(ar); + Console.WriteLine("OnStartAdviseComplete"); + } + catch (Exception e) + { + Console.WriteLine("OnStartAdviseComplete: " + e.Message); + } + } + + private static void OnStopAdviseComplete(IAsyncResult ar) + { + try + { + DdeClient client = (DdeClient)ar.AsyncState; + client.EndStopAdvise(ar); + Console.WriteLine("OnStopAdviseComplete"); + } + catch (Exception e) + { + Console.WriteLine("OnStopAdviseComplete: " + e.Message); + } + } + + private static void OnAdvise(object sender, DdeAdviseEventArgs args) + { + Console.WriteLine("OnAdvise: " + args.Text); + } + + private static void OnDisconnected(object sender, DdeDisconnectedEventArgs args) + { + Console.WriteLine( + "OnDisconnected: " + + "IsServerInitiated=" + args.IsServerInitiated.ToString() + " " + + "IsDisposed=" + args.IsDisposed.ToString()); + } + + } // class + + + + Imports System.Text + Imports NDde.Client + + Module Program + + Sub Main() + + ' Wait for the user to press ENTER before proceding. + Console.WriteLine("The Server sample must be running before the client can connect.") + Console.WriteLine("Press ENTER to continue...") + Console.ReadLine() + + Try + ' Create a client that connects to 'myapp|mytopic'. + Using client As DdeClient = New DdeClient("myapp", "mytopic") + + ' Subscribe to the Disconnected event. This event will notify the application when a conversation has been terminated. + AddHandler client.Disconnected, AddressOf OnDisconnected + + ' Connect to the server. It must be running or an exception will be thrown. + client.Connect() + + ' Synchronous Execute Operation + client.Execute("mycommand", 60000) + + ' Synchronous Poke Operation + client.Poke("myitem", DateTime.Now.ToString(), 60000) + + ' Syncronous Request Operation + Console.WriteLine("Request: " + client.Request("myitem", 60000)) + + ' Asynchronous Execute Operation + client.BeginExecute("mycommand", AddressOf OnExecuteComplete, client) + + ' Asynchronous Poke Operation + client.BeginPoke("myitem", Encoding.ASCII.GetBytes(DateTime.Now.ToString() + Convert.ToChar(0)), 1, AddressOf OnPokeComplete, client) + + ' Asynchronous Request Operation + client.BeginRequest("myitem", 1, AddressOf OnRequestComplete, client) + + ' Advise Loop + client.StartAdvise("myitem", 1, True, 60000) + AddHandler client.Advise, AddressOf OnAdvise + + ' Wait for the user to press ENTER before proceding. + Console.WriteLine("Press ENTER to quit...") + Console.ReadLine() + + End Using + + Catch e As Exception + + Console.WriteLine(e.ToString()) + Console.WriteLine("Press ENTER to quit...") + Console.ReadLine() + + End Try + + End Sub + + Private Sub OnExecuteComplete(ByVal ar As IAsyncResult) + Try + Dim client As DdeClient = DirectCast(ar.AsyncState, DdeClient) + client.EndExecute(ar) + Console.WriteLine("OnExecuteComplete") + Catch e As Exception + Console.WriteLine("OnExecuteComplete: " + e.Message) + End Try + End Sub + + Private Sub OnPokeComplete(ByVal ar As IAsyncResult) + Try + Dim client As DdeClient = DirectCast(ar.AsyncState, DdeClient) + client.EndPoke(ar) + Console.WriteLine("OnPokeComplete") + Catch e As Exception + Console.WriteLine("OnPokeComplete: " + e.Message) + End Try + End Sub + + Private Sub OnRequestComplete(ByVal ar As IAsyncResult) + Try + Dim client As DdeClient = DirectCast(ar.AsyncState, DdeClient) + client.EndRequest(ar) + Console.WriteLine("OnRequestComplete") + Catch e As Exception + Console.WriteLine("OnRequestComplete: " + e.Message) + End Try + End Sub + + Private Sub OnStartAdviseComplete(ByVal ar As IAsyncResult) + Try + Dim client As DdeClient = DirectCast(ar.AsyncState, DdeClient) + client.EndStartAdvise(ar) + Console.WriteLine("OnStartAdviseComplete") + Catch e As Exception + Console.WriteLine("OnStartAdviseComplete: " + e.Message) + End Try + End Sub + + Private Sub OnStopAdviseComplete(ByVal ar As IAsyncResult) + Try + Dim client As DdeClient = DirectCast(ar.AsyncState, DdeClient) + client.EndStopAdvise(ar) + Console.WriteLine("OnStopAdviseComplete") + Catch e As Exception + Console.WriteLine("OnStopAdviseComplete: " + e.Message) + End Try + End Sub + + Private Sub OnAdvise(ByVal sender As Object, ByVal args As DdeAdviseEventArgs) + Console.WriteLine("OnAdvise: " + args.Text) + End Sub + + Private Sub OnDisconnected(ByVal sender As Object, ByVal args As DdeDisconnectedEventArgs) + Console.WriteLine( _ + "OnDisconnected: " + _ + "IsServerInitiated=" + args.IsServerInitiated.ToString() + " " + _ + "IsDisposed=" + args.IsDisposed.ToString()) + End Sub + + End Module + + + + diff --git a/NDDE/NDde/Source/NDde/Documentation/NamespaceDoc.cs b/NDDE/NDde/Source/NDde/Documentation/NamespaceDoc.cs new file mode 100644 index 0000000..44aa57f --- /dev/null +++ b/NDDE/NDde/Source/NDde/Documentation/NamespaceDoc.cs @@ -0,0 +1,96 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde +{ + /// + /// This namespace contains classes for using Dynamic Data Exchange (DDE) in .NET. + /// + internal sealed class NamespaceDoc + { + // This class is used by NDoc to build the namespace documentation. + private NamespaceDoc() { } + } + +} // namespace + +namespace NDde.Advanced +{ + /// + /// This namespace contains classes for using advanced features of the library. + /// + internal sealed class NamespaceDoc + { + // This class is used by NDoc to build the namespace documentation. + private NamespaceDoc() { } + } + +} // namespace + +namespace NDde.Advanced.Monitor +{ + /// + /// This namespace contains classes for creating DDE monitors. + /// + internal sealed class NamespaceDoc + { + // This class is used by NDoc to build the namespace documentation. + private NamespaceDoc() { } + } + +} // namespace + +namespace NDde.Client +{ + /// + /// This namespace contains classes for creating DDE client applications. + /// + internal sealed class NamespaceDoc + { + // This class is used by NDoc to build the namespace documentation. + private NamespaceDoc() { } + } + +} // namespace + +namespace NDde.Server +{ + /// + /// This namespace contains classes for creating DDE server applications. + /// + internal sealed class NamespaceDoc + { + // This class is used by NDoc to build the namespace documentation. + private NamespaceDoc() { } + } + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Internal/Advanced/DdemlContext.cs b/NDDE/NDde/Source/NDde/Internal/Advanced/DdemlContext.cs new file mode 100644 index 0000000..b3f2788 --- /dev/null +++ b/NDDE/NDde/Source/NDde/Internal/Advanced/DdemlContext.cs @@ -0,0 +1,548 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Foundation.Advanced +{ + using System; + using System.Collections.Generic; + using System.ComponentModel; + using System.Text; + using System.Threading; + using System.Windows.Forms; + using NDde.Foundation.Client; + using NDde.Foundation.Server; + using NDde.Properties; + + internal sealed class DdemlContext : IDisposable + { + private static WeakReferenceDictionary _Instances = new WeakReferenceDictionary(); + + internal static DdemlContext GetDefault() + { + lock (_Instances) + { + DdemlContext context = _Instances[Ddeml.GetCurrentThreadId()]; + if (context == null) + { + context = new DdemlContext(); + _Instances.Add(Ddeml.GetCurrentThreadId(), context); + } + return context; + } + } + + private int _InstanceId = 0; // DDEML instance identifier + private Ddeml.DdeCallback _Callback = null; // DDEML callback function + private WeakReferenceDictionary _ClientTable = new WeakReferenceDictionary(); // Active clients by conversation + private WeakReferenceDictionary _ServerTable1 = new WeakReferenceDictionary(); // Active servers by conversation + private WeakReferenceDictionary _ServerTable2 = new WeakReferenceDictionary(); // Active servers by service + private List _Filters = new List(); // ITransactionFilter objects + private Encoding _Encoding = Encoding.ASCII; + private bool _Disposed = false; + + public event EventHandler Register; + + public event EventHandler Unregister; + + internal event EventHandler StateChange; + + public DdemlContext() + { + // Create the callback that will be used by the DDEML. + _Callback = this.OnDdeCallback; + } + + ~DdemlContext() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void Dispose(bool disposing) + { + if (!_Disposed) + { + _Disposed = true; + if (disposing) + { + // Dispose all clients. + foreach (DdemlClient client in _ClientTable.Values) + { + client.Dispose(); + } + + // Dispose all servers. + foreach (DdemlServer server in _ServerTable2.Values) + { + server.Dispose(); + } + + // Raise the StateChange event. + foreach (EventHandler handler in StateChange.GetInvocationList()) + { + try + { + handler(this, EventArgs.Empty); + } + catch + { + // Swallow any exception that occurs. + } + } + } + + if (IsInitialized) + { + // Uninitialize this DDEML instance. + InstanceManager.Uninitialize(_InstanceId); + + // Indicate that this object is no longer initialized. + _InstanceId = 0; + } + } + } + + public int InstanceId + { + get { return _InstanceId; } + } + + public bool IsInitialized + { + get { return _InstanceId != 0; } + } + + public Encoding Encoding + { + get { return _Encoding; } + set { _Encoding = value; } + } + + internal bool IsDisposed + { + get { return _Disposed; } + } + + public void Initialize() + { + if (IsDisposed) + { + throw new ObjectDisposedException(this.GetType().ToString()); + } + if (IsInitialized) + { + throw new InvalidOperationException(Resources.AlreadyInitializedMessage); + } + + Initialize(Ddeml.APPCLASS_STANDARD); + + // Raise the StateChange event. + if (StateChange != null) + { + StateChange(this, EventArgs.Empty); + } + } + + public void AddTransactionFilter(IDdemlTransactionFilter filter) + { + if (IsDisposed) + { + throw new ObjectDisposedException(this.GetType().ToString()); + } + if (filter == null) + { + throw new ArgumentNullException("filter"); + } + if (_Filters.Contains(filter)) + { + throw new InvalidOperationException(Resources.FilterAlreadyAddedMessage); + } + + _Filters.Add(filter); + } + + public void RemoveTransactionFilter(IDdemlTransactionFilter filter) + { + if (IsDisposed) + { + throw new ObjectDisposedException(this.GetType().ToString()); + } + if (filter == null) + { + throw new ArgumentNullException("filter"); + } + if (!_Filters.Contains(filter)) + { + throw new InvalidOperationException(Resources.FilterNotAddedMessage); + } + + _Filters.Remove(filter); + } + + internal void Initialize(int afCmd) + { + // Initialize a DDEML instance. + _InstanceId = InstanceManager.Initialize(_Callback, afCmd); + + // If the instance identifier is null then the DDEML could not be initialized. + if (_InstanceId == 0) + { + int error = Ddeml.DdeGetLastError(_InstanceId); + throw new DdemlException(Resources.InitializeFailedMessage, error); + } + } + + internal void RegisterClient(DdemlClient client) + { + _ClientTable[client.Handle] = client; + } + + internal void RegisterServer(DdemlServer server) + { + _ServerTable2[server.Service] = server; + } + + internal void UnregisterClient(DdemlClient client) + { + _ClientTable[client.Handle] = null; + } + + internal void UnregisterServer(DdemlServer server) + { + _ServerTable2[server.Service] = null; + } + + private IntPtr OnDdeCallback(int uType, int uFmt, IntPtr hConv, IntPtr hsz1, IntPtr hsz2, IntPtr hData, IntPtr dwData1, IntPtr dwData2) + { + // Create a new transaction object that will be dispatched to a DdemlClient, DdemlServer, or ITransactionFilter. + DdemlTransaction t = new DdemlTransaction(uType, uFmt, hConv, hsz1, hsz2, hData, dwData1, dwData2); + + // Run each transaction filter. + foreach (IDdemlTransactionFilter filter in _Filters) + { + if (filter.PreFilterTransaction(t)) + { + return t.dwRet; + } + } + + // Dispatch the transaction. + switch (uType) + { + case Ddeml.XTYP_ADVDATA: + { + DdemlClient client = _ClientTable[hConv] as DdemlClient; + if (client != null) + { + if (client.ProcessCallback(t)) + { + return t.dwRet; + } + } + break; + } + case Ddeml.XTYP_ADVREQ: + { + DdemlServer server = _ServerTable1[hConv] as DdemlServer; + if (server != null) + { + if (server.ProcessCallback(t)) + { + return t.dwRet; + } + } + break; + } + case Ddeml.XTYP_ADVSTART: + { + DdemlServer server = _ServerTable1[hConv] as DdemlServer; + if (server != null) + { + if (server.ProcessCallback(t)) + { + return t.dwRet; + } + } + break; + } + case Ddeml.XTYP_ADVSTOP: + { + DdemlServer server = _ServerTable1[hConv] as DdemlServer; + if (server != null) + { + if (server.ProcessCallback(t)) + { + return t.dwRet; + } + } + break; + } + case Ddeml.XTYP_CONNECT: + { + // Get the service name from the hsz2 string handle. + StringBuilder psz = new StringBuilder(Ddeml.MAX_STRING_SIZE); + int length = Ddeml.DdeQueryString(_InstanceId, hsz2, psz, psz.Capacity, Ddeml.CP_WINANSI); + string service = psz.ToString(); + + DdemlServer server = _ServerTable2[service] as DdemlServer; + if (server != null) + { + if (server.ProcessCallback(t)) + { + return t.dwRet; + } + } + break; + } + case Ddeml.XTYP_CONNECT_CONFIRM: + { + // Get the service name from the hsz2 string handle. + StringBuilder psz = new StringBuilder(Ddeml.MAX_STRING_SIZE); + int length = Ddeml.DdeQueryString(_InstanceId, hsz2, psz, psz.Capacity, Ddeml.CP_WINANSI); + string service = psz.ToString(); + + DdemlServer server = _ServerTable2[service] as DdemlServer; + if (server != null) + { + _ServerTable1[hConv] = server; + if (server.ProcessCallback(t)) + { + return t.dwRet; + } + } + break; + } + case Ddeml.XTYP_DISCONNECT: + { + DdemlClient client = _ClientTable[hConv] as DdemlClient; + if (client != null) + { + _ClientTable[hConv] = null; + if (client.ProcessCallback(t)) + { + return t.dwRet; + } + } + DdemlServer server = _ServerTable1[hConv] as DdemlServer; + if (server != null) + { + _ServerTable1[hConv] = null; + if (server.ProcessCallback(t)) + { + return t.dwRet; + } + } + break; + } + case Ddeml.XTYP_EXECUTE: + { + DdemlServer server = _ServerTable1[hConv] as DdemlServer; + if (server != null) + { + if (server.ProcessCallback(t)) + { + return t.dwRet; + } + } + break; + } + case Ddeml.XTYP_POKE: + { + DdemlServer server = _ServerTable1[hConv] as DdemlServer; + if (server != null) + { + if (server.ProcessCallback(t)) + { + return t.dwRet; + } + } + break; + } + case Ddeml.XTYP_REQUEST: + { + DdemlServer server = _ServerTable1[hConv] as DdemlServer; + if (server != null) + { + if (server.ProcessCallback(t)) + { + return t.dwRet; + } + } + break; + } + case Ddeml.XTYP_XACT_COMPLETE: + { + DdemlClient client = _ClientTable[hConv] as DdemlClient; + if (client != null) + { + if (client.ProcessCallback(t)) + { + return t.dwRet; + } + } + break; + } + case Ddeml.XTYP_WILDCONNECT: + { + // This library does not support wild connects. + return IntPtr.Zero; + } + case Ddeml.XTYP_MONITOR: + { + // Monitors are handled separately in DdemlMonitor. + return IntPtr.Zero; + } + case Ddeml.XTYP_ERROR: + { + // Get the error code, but do nothing with it at this time. + int error = dwData1.ToInt32(); + + return IntPtr.Zero; + } + case Ddeml.XTYP_REGISTER: + { + if (Register != null) + { + // Get the service name from the hsz1 string handle. + StringBuilder psz = new StringBuilder(Ddeml.MAX_STRING_SIZE); + int length = Ddeml.DdeQueryString(_InstanceId, hsz1, psz, psz.Capacity, Ddeml.CP_WINANSI); + string service = psz.ToString(); + + Register(this, new DdemlRegistrationEventArgs(service)); + } + return IntPtr.Zero; + } + case Ddeml.XTYP_UNREGISTER: + { + if (Unregister != null) + { + // Get the service name from the hsz1 string handle. + StringBuilder psz = new StringBuilder(Ddeml.MAX_STRING_SIZE); + int length = Ddeml.DdeQueryString(_InstanceId, hsz1, psz, psz.Capacity, Ddeml.CP_WINANSI); + string service = psz.ToString(); + + Unregister(this, new DdemlRegistrationEventArgs(service)); + } + return IntPtr.Zero; + } + } + return IntPtr.Zero; + } + + /// + /// This class is needed to dispose of DDEML resources correctly since the DDEML is thread specific. + /// + private sealed class InstanceManager : IMessageFilter + { + private const int WM_APP = unchecked((int)0x8000); + + private static readonly string DataSlot = typeof(InstanceManager).FullName; + + private static IDictionary _Table = new Dictionary(); + + [System.Runtime.InteropServices.DllImport("user32.dll")] + private static extern void PostThreadMessage(int idThread, int Msg, IntPtr wParam, IntPtr lParam); + + public static int Initialize(Ddeml.DdeCallback pfnCallback, int afCmd) + { + lock (_Table) + { + // Initialize a DDEML instance. + int instanceId = 0; + Ddeml.DdeInitialize(ref instanceId, pfnCallback, afCmd, 0); + + if (instanceId != 0) + { + // Make sure this thread has an IMessageFilter on it. + LocalDataStoreSlot slot = Thread.GetNamedDataSlot(DataSlot); + if (Thread.GetData(slot) == null) + { + InstanceManager filter = new InstanceManager(); + Application.AddMessageFilter(filter); + Thread.SetData(slot, filter); + } + + // Add an entry to the table that maps the instance identifier to the current thread. + _Table.Add(instanceId, Ddeml.GetCurrentThreadId()); + } + + return instanceId; + } + } + + public static void Uninitialize(int instanceId) + { + // This method could be called by the GC finalizer thread. If it is then a direct call to the DDEML will fail since the DDEML is + // thread specific. A message will be posted to the DDEML thread instead. + lock (_Table) + { + if (_Table.ContainsKey(instanceId)) + { + // Determine if the current thread matches what is in the table. + int threadId = (int)_Table[instanceId]; + if (threadId == Ddeml.GetCurrentThreadId()) + { + // Uninitialize the DDEML instance. + Ddeml.DdeUninitialize(instanceId); + } + else + { + // Post a message to the thread that needs to execute Ddeml.DdeUninitialize. + PostThreadMessage(threadId, WM_APP + 1, new IntPtr(instanceId), IntPtr.Zero); + } + + // Remove the instance identifier from the table because it is no longer in use. + _Table.Remove(instanceId); + } + } + } + + bool IMessageFilter.PreFilterMessage(ref Message m) + { + if (m.Msg == WM_APP + 1) + { + // Uninitialize the DDEML instance. + Ddeml.DdeUninitialize(m.WParam.ToInt32()); + } + return false; + } + + } // class + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Internal/Advanced/DdemlRegistrationEventArgs.cs b/NDDE/NDde/Source/NDde/Internal/Advanced/DdemlRegistrationEventArgs.cs new file mode 100644 index 0000000..9ab6462 --- /dev/null +++ b/NDDE/NDde/Source/NDde/Internal/Advanced/DdemlRegistrationEventArgs.cs @@ -0,0 +1,53 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Foundation.Advanced +{ + using System; + + internal sealed class DdemlRegistrationEventArgs : DdemlEventArgs + { + private string _Service = ""; + + public DdemlRegistrationEventArgs(string service) + { + _Service = service; + } + + public string Service + { + get { return _Service; } + } + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Internal/Advanced/DdemlTransaction.cs b/NDDE/NDde/Source/NDde/Internal/Advanced/DdemlTransaction.cs new file mode 100644 index 0000000..5f4a730 --- /dev/null +++ b/NDDE/NDde/Source/NDde/Internal/Advanced/DdemlTransaction.cs @@ -0,0 +1,126 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Foundation.Advanced +{ + using System; + + internal sealed class DdemlTransaction + { + private int _uType = 0; + private int _uFmt = 0; + private IntPtr _hConv = IntPtr.Zero; + private IntPtr _hsz1 = IntPtr.Zero; + private IntPtr _hsz2 = IntPtr.Zero; + private IntPtr _hData = IntPtr.Zero; + private IntPtr _dwData1 = IntPtr.Zero; + private IntPtr _dwData2 = IntPtr.Zero; + private IntPtr _dwRet = IntPtr.Zero; + + public DdemlTransaction(int uType, int uFmt, IntPtr hConv, IntPtr hsz1, IntPtr hsz2, IntPtr hData, IntPtr dwData1, IntPtr dwData2) + { + _uType = uType; + _uFmt = uFmt; + _hConv = hConv; + _hsz1 = hsz1; + _hsz2 = hsz2; + _hData = hData; + _dwData1 = dwData1; + _dwData2 = dwData2; + } + + public int uType + { + get { return _uType; } + } + + public int uFmt + { + get { return _uFmt; } + } + + public IntPtr hConv + { + get { return _hConv; } + } + + public IntPtr hsz1 + { + get { return _hsz1; } + } + + public IntPtr hsz2 + { + get { return _hsz2; } + } + + public IntPtr hData + { + get { return _hData; } + } + + public IntPtr dwData1 + { + get { return _dwData1; } + } + + public IntPtr dwData2 + { + get { return _dwData2; } + } + + public IntPtr dwRet + { + get { return _dwRet; } + set { _dwRet = value; } + } + + public override string ToString() + { + string s = ""; + foreach (System.Reflection.PropertyInfo property in this.GetType().GetProperties()) + { + if (s.Length == 0) + { + s += property.Name + "=" + property.GetValue(this, null).ToString(); + } + else + { + s += " " + property.Name + "=" + property.GetValue(this, null).ToString(); + } + } + return s; + } + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Internal/Advanced/IDdemlTransactionFilter.cs b/NDDE/NDde/Source/NDde/Internal/Advanced/IDdemlTransactionFilter.cs new file mode 100644 index 0000000..5582606 --- /dev/null +++ b/NDDE/NDde/Source/NDde/Internal/Advanced/IDdemlTransactionFilter.cs @@ -0,0 +1,43 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Foundation.Advanced +{ + using System; + + internal interface IDdemlTransactionFilter + { + bool PreFilterTransaction(DdemlTransaction t); + + } // interface + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Internal/Advanced/Monitor/DdemlActivityEventArgs.cs b/NDDE/NDde/Source/NDde/Internal/Advanced/Monitor/DdemlActivityEventArgs.cs new file mode 100644 index 0000000..f8bed3e --- /dev/null +++ b/NDDE/NDde/Source/NDde/Internal/Advanced/Monitor/DdemlActivityEventArgs.cs @@ -0,0 +1,53 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Foundation.Advanced.Monitor +{ + using System; + + internal abstract class DdemlActivityEventArgs : DdemlEventArgs + { + private IntPtr _TaskHandle = IntPtr.Zero; + + public DdemlActivityEventArgs(IntPtr taskHandle) + { + _TaskHandle = taskHandle; + } + + public IntPtr TaskHandle + { + get { return _TaskHandle; } + } + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Internal/Advanced/Monitor/DdemlCallbackActivityEventArgs.cs b/NDDE/NDde/Source/NDde/Internal/Advanced/Monitor/DdemlCallbackActivityEventArgs.cs new file mode 100644 index 0000000..bf75aa6 --- /dev/null +++ b/NDDE/NDde/Source/NDde/Internal/Advanced/Monitor/DdemlCallbackActivityEventArgs.cs @@ -0,0 +1,119 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Foundation.Advanced.Monitor +{ + using System; + + internal sealed class DdemlCallbackActivityEventArgs : DdemlActivityEventArgs + { + private int _uType = 0; + private int _uFmt = 0; + private IntPtr _hConv = IntPtr.Zero; + private IntPtr _hsz1 = IntPtr.Zero; + private IntPtr _hsz2 = IntPtr.Zero; + private IntPtr _hData = IntPtr.Zero; + private IntPtr _dwData1 = IntPtr.Zero; + private IntPtr _dwData2 = IntPtr.Zero; + private IntPtr _dwRet = IntPtr.Zero; + + public DdemlCallbackActivityEventArgs( + int uType, + int uFmt, + IntPtr hConv, + IntPtr hsz1, + IntPtr hsz2, + IntPtr hData, + IntPtr dwData1, + IntPtr dwData2, + IntPtr dwRet, + IntPtr taskHandle) : base(taskHandle) + { + _uType = uType; + _uFmt = uFmt; + _hConv = hConv; + _hsz1 = hsz1; + _hsz2 = hsz2; + _hData = hData; + _dwData1 = dwData1; + _dwData2 = dwData2; + _dwRet = dwRet; + } + + public int uType + { + get { return _uType; } + } + + public int uFmt + { + get { return _uFmt; } + } + + public IntPtr hConv + { + get { return _hConv; } + } + + public IntPtr hsz1 + { + get { return _hsz1; } + } + + public IntPtr hsz2 + { + get { return _hsz2; } + } + + public IntPtr hData + { + get { return _hData; } + } + + public IntPtr dwData1 + { + get { return _dwData1; } + } + + public IntPtr dwData2 + { + get { return _dwData2; } + } + + public IntPtr dwRet + { + get { return _dwRet; } + } + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Internal/Advanced/Monitor/DdemlConversationActivityEventArgs.cs b/NDDE/NDde/Source/NDde/Internal/Advanced/Monitor/DdemlConversationActivityEventArgs.cs new file mode 100644 index 0000000..86f0bfd --- /dev/null +++ b/NDDE/NDde/Source/NDde/Internal/Advanced/Monitor/DdemlConversationActivityEventArgs.cs @@ -0,0 +1,87 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Foundation.Advanced.Monitor +{ + using System; + + internal sealed class DdemlConversationActivityEventArgs : DdemlActivityEventArgs + { + private string _Service = ""; + private string _Topic = ""; + private bool _Established = false; + private IntPtr _ClientHandle = IntPtr.Zero; + private IntPtr _ServerHandle = IntPtr.Zero; + + public DdemlConversationActivityEventArgs( + string service, + string topic, + bool established, + IntPtr clientHandle, + IntPtr serverHandle, + IntPtr taskHandle) : base(taskHandle) + { + _Service = service; + _Topic = topic; + _Established = established; + _ClientHandle = clientHandle; + _ServerHandle = serverHandle; + } + + public string Service + { + get { return _Service; } + } + + public string Topic + { + get { return _Topic; } + } + + public bool IsEstablished + { + get { return _Established; } + } + + public IntPtr ClientHandle + { + get { return _ClientHandle; } + } + + public IntPtr ServerHandle + { + get { return _ServerHandle; } + } + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Internal/Advanced/Monitor/DdemlErrorActivityEventArgs.cs b/NDDE/NDde/Source/NDde/Internal/Advanced/Monitor/DdemlErrorActivityEventArgs.cs new file mode 100644 index 0000000..74b9d3c --- /dev/null +++ b/NDDE/NDde/Source/NDde/Internal/Advanced/Monitor/DdemlErrorActivityEventArgs.cs @@ -0,0 +1,53 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Foundation.Advanced.Monitor +{ + using System; + + internal sealed class DdemlErrorActivityEventArgs : DdemlActivityEventArgs + { + private int _Code = Ddeml.DMLERR_NO_ERROR; + + public DdemlErrorActivityEventArgs(int code, IntPtr taskHandle) : base(taskHandle) + { + _Code = code; + } + + public int Code + { + get { return _Code; } + } + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Internal/Advanced/Monitor/DdemlLinkActivityEventArgs.cs b/NDDE/NDde/Source/NDde/Internal/Advanced/Monitor/DdemlLinkActivityEventArgs.cs new file mode 100644 index 0000000..a6ba802 --- /dev/null +++ b/NDDE/NDde/Source/NDde/Internal/Advanced/Monitor/DdemlLinkActivityEventArgs.cs @@ -0,0 +1,119 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Foundation.Advanced.Monitor +{ + using System; + + internal sealed class DdemlLinkActivityEventArgs : DdemlActivityEventArgs + { + private string _Service = ""; + private string _Topic = ""; + private string _Item = ""; + private int _Format = 0; + private bool _Hot = false; + private bool _Established = false; + private bool _ServerInitiated = false; + private IntPtr _ClientHandle = IntPtr.Zero; + private IntPtr _ServerHandle = IntPtr.Zero; + + public DdemlLinkActivityEventArgs( + string service, + string topic, + string item, + int format, + bool hot, + bool established, + bool serverInitiated, + IntPtr clientHandle, + IntPtr serverHandle, + IntPtr taskHandle) : base(taskHandle) + { + _Service = service; + _Topic = topic; + _Item = item; + _Format = format; + _Hot = hot; + _Established = established; + _ServerInitiated = serverInitiated; + _ClientHandle = clientHandle; + _ServerHandle = serverHandle; + } + + public string Service + { + get { return _Service; } + } + + public string Topic + { + get { return _Topic; } + } + + public string Item + { + get { return _Item; } + } + + public int Format + { + get { return _Format; } + } + + public bool IsHot + { + get { return _Hot; } + } + + public bool IsEstablished + { + get { return _Established; } + } + + public bool IsServerInitiated + { + get { return _ServerInitiated; } + } + + public IntPtr ClientHandle + { + get { return _ClientHandle; } + } + + public IntPtr ServerHandle + { + get { return _ServerHandle; } + } + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Internal/Advanced/Monitor/DdemlMessageActivityEventArgs.cs b/NDDE/NDde/Source/NDde/Internal/Advanced/Monitor/DdemlMessageActivityEventArgs.cs new file mode 100644 index 0000000..0eaa5fb --- /dev/null +++ b/NDDE/NDde/Source/NDde/Internal/Advanced/Monitor/DdemlMessageActivityEventArgs.cs @@ -0,0 +1,72 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Foundation.Advanced.Monitor +{ + using System; + using System.Windows.Forms; + + internal enum DdemlMessageActivityKind + { + Post, + + Send + + } // enum + + internal sealed class DdemlMessageActivityEventArgs : DdemlActivityEventArgs + { + private DdemlMessageActivityKind _Kind = DdemlMessageActivityKind.Post; + private Message _Message = new Message(); + + public DdemlMessageActivityEventArgs( + DdemlMessageActivityKind kind, + Message message, + IntPtr taskHandle) : base(taskHandle) + { + _Kind = kind; + _Message = message; + } + + public DdemlMessageActivityKind Kind + { + get { return _Kind; } + } + + public Message Message + { + get { return _Message; } + } + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Internal/Advanced/Monitor/DdemlMonitor.cs b/NDDE/NDde/Source/NDde/Internal/Advanced/Monitor/DdemlMonitor.cs new file mode 100644 index 0000000..e178b12 --- /dev/null +++ b/NDDE/NDde/Source/NDde/Internal/Advanced/Monitor/DdemlMonitor.cs @@ -0,0 +1,351 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Foundation.Advanced.Monitor +{ + using System; + using System.Runtime.InteropServices; + using System.Text; + using System.Windows.Forms; + using NDde.Properties; + + [Flags] + internal enum DdemlMonitorFlags + { + Callback = Ddeml.MF_CALLBACKS, + + Conversation = Ddeml.MF_CONV, + + Error = Ddeml.MF_ERRORS, + + String = Ddeml.MF_HSZ_INFO, + + Link = Ddeml.MF_LINKS, + + Message = Ddeml.MF_POSTMSGS | Ddeml.MF_SENDMSGS + + } // enum + + internal sealed class DdemlMonitor : IDisposable + { + private DdemlContext _Context = null; + private bool _Disposed = false; + + public event EventHandler CallbackActivity; + public event EventHandler ConversationActivity; + public event EventHandler ErrorActivity; + public event EventHandler LinkActivity; + public event EventHandler MessageActivity; + public event EventHandler StringActivity; + + public DdemlMonitor() + : this(DdemlContext.GetDefault()) + { + } + + public DdemlMonitor(DdemlContext context) + { + _Context = context; + } + + public void Dispose() + { + Dispose(true); + } + + private void Dispose(bool disposing) + { + if (!_Disposed) + { + _Disposed = true; + if (disposing) + { + _Context.Dispose(); + } + } + } + + public void Start(DdemlMonitorFlags flags) + { + _Context.AddTransactionFilter(new TransactionFilter(this)); + _Context.Initialize(Ddeml.APPCLASS_STANDARD | (int)flags); + } + + private void OnCallback(Ddeml.MONCBSTRUCT mon) + { + DdemlCallbackActivityEventArgs args = new DdemlCallbackActivityEventArgs( + mon.wType, + mon.wFmt, + mon.hConv, + mon.hsz1, + mon.hsz2, + mon.hData, + mon.dwData1, + mon.dwData2, + mon.dwRet, + mon.hTask); + + if (CallbackActivity != null) + { + CallbackActivity(this, args); + } + } + + private void OnConversation(Ddeml.MONCONVSTRUCT mon) + { + StringBuilder psz; + int length; + + // Get the service name from the hszSvc string handle. + psz = new StringBuilder(Ddeml.MAX_STRING_SIZE); + length = Ddeml.DdeQueryString(_Context.InstanceId, mon.hszSvc, psz, psz.Capacity, Ddeml.CP_WINANSI); + string service = psz.ToString(); + + // Get the topic name from the hszTopic string handle. + psz = new StringBuilder(Ddeml.MAX_STRING_SIZE); + length = Ddeml.DdeQueryString(_Context.InstanceId, mon.hszTopic, psz, psz.Capacity, Ddeml.CP_WINANSI); + string topic = psz.ToString(); + + DdemlConversationActivityEventArgs args = new DdemlConversationActivityEventArgs( + service, + topic, + mon.fConnect, + mon.hConvClient, + mon.hConvServer, + mon.hTask); + + if (ConversationActivity != null) + { + ConversationActivity(this, args); + } + } + + private void OnError(Ddeml.MONERRSTRUCT mon) + { + DdemlErrorActivityEventArgs args = new DdemlErrorActivityEventArgs(mon.wLastError, mon.hTask); + + if (ErrorActivity != null) + { + ErrorActivity(this, args); + } + } + + private void OnLink(Ddeml.MONLINKSTRUCT mon) + { + StringBuilder psz; + int length; + + // Get the service name from the hszSvc string handle. + psz = new StringBuilder(Ddeml.MAX_STRING_SIZE); + length = Ddeml.DdeQueryString(_Context.InstanceId, mon.hszSvc, psz, psz.Capacity, Ddeml.CP_WINANSI); + string service = psz.ToString(); + + // Get the topic name from the hszTopic string handle. + psz = new StringBuilder(Ddeml.MAX_STRING_SIZE); + length = Ddeml.DdeQueryString(_Context.InstanceId, mon.hszTopic, psz, psz.Capacity, Ddeml.CP_WINANSI); + string topic = psz.ToString(); + + // Get the item name from the hszItem string handle. + psz = new StringBuilder(Ddeml.MAX_STRING_SIZE); + length = Ddeml.DdeQueryString(_Context.InstanceId, mon.hszItem, psz, psz.Capacity, Ddeml.CP_WINANSI); + string item = psz.ToString(); + + DdemlLinkActivityEventArgs args = new DdemlLinkActivityEventArgs( + service, + topic, + item, + mon.wFmt, + !mon.fNoData, + mon.fEstablished, + mon.fServer, + mon.hConvClient, + mon.hConvServer, + mon.hTask); + + if (LinkActivity != null) + { + LinkActivity(this, args); + } + } + + private void OnPost(Ddeml.MONMSGSTRUCT mon) + { + Message m = new Message(); + m.HWnd = mon.hwndTo; + m.Msg = mon.wMsg; + m.LParam = mon.lParam; + m.WParam = mon.wParam; + + DdemlMessageActivityEventArgs args = new DdemlMessageActivityEventArgs(DdemlMessageActivityKind.Post, m, mon.hTask); + + if (MessageActivity != null) + { + MessageActivity(this, args); + } + } + + private void OnSend(Ddeml.MONMSGSTRUCT mon) + { + Message m = new Message(); + m.HWnd = mon.hwndTo; + m.Msg = mon.wMsg; + m.LParam = mon.lParam; + m.WParam = mon.wParam; + + DdemlMessageActivityEventArgs args = new DdemlMessageActivityEventArgs(DdemlMessageActivityKind.Send, m, mon.hTask); + + if (MessageActivity != null) + { + MessageActivity(this, args); + } + } + + private void OnString(Ddeml.MONHSZSTRUCT mon) + { + // Get the string from the hsz string handle. + // TODO: For some reason this does not work correctly. + StringBuilder psz = new StringBuilder(Ddeml.MAX_STRING_SIZE); + int length = Ddeml.DdeQueryString(_Context.InstanceId, mon.hsz, psz, psz.Capacity, Ddeml.CP_WINANSI); + string str = psz.ToString(); + + DdemlStringActivityType action = DdemlStringActivityType.CleanUp; + switch (mon.fsAction) + { + case Ddeml.MH_CLEANUP: action = DdemlStringActivityType.CleanUp; break; + case Ddeml.MH_CREATE: action = DdemlStringActivityType.Create; break; + case Ddeml.MH_DELETE: action = DdemlStringActivityType.Delete; break; + case Ddeml.MH_KEEP: action = DdemlStringActivityType.Keep; break; + } + + DdemlStringActivityEventArgs args = new DdemlStringActivityEventArgs(str, action, mon.hTask); + + if (StringActivity != null) + { + StringActivity(this, args); + } + } + + private sealed class TransactionFilter : IDdemlTransactionFilter + { + private DdemlMonitor _Parent = null; + + public TransactionFilter(DdemlMonitor parent) + { + _Parent = parent; + } + + public bool PreFilterTransaction(DdemlTransaction t) + { + if (t.uType == Ddeml.XTYP_MONITOR) + { + switch (t.dwData2.ToInt32()) + { + case Ddeml.MF_CALLBACKS: + { + // Get the MONCBSTRUCT object. + int length = 0; + IntPtr phData = Ddeml.DdeAccessData(t.hData, ref length); + Ddeml.MONCBSTRUCT mon = (Ddeml.MONCBSTRUCT)Marshal.PtrToStructure(phData, typeof(Ddeml.MONCBSTRUCT)); + Ddeml.DdeUnaccessData(t.hData); + _Parent.OnCallback(mon); + break; + } + case Ddeml.MF_CONV: + { + // Get the MONCONVSTRUCT object. + int length = 0; + IntPtr phData = Ddeml.DdeAccessData(t.hData, ref length); + Ddeml.MONCONVSTRUCT mon = (Ddeml.MONCONVSTRUCT)Marshal.PtrToStructure(phData, typeof(Ddeml.MONCONVSTRUCT)); + Ddeml.DdeUnaccessData(t.hData); + _Parent.OnConversation(mon); + break; + } + case Ddeml.MF_ERRORS: + { + // Get the MONERRSTRUCT object. + int length = 0; + IntPtr phData = Ddeml.DdeAccessData(t.hData, ref length); + Ddeml.MONERRSTRUCT mon = (Ddeml.MONERRSTRUCT)Marshal.PtrToStructure(phData, typeof(Ddeml.MONERRSTRUCT)); + Ddeml.DdeUnaccessData(t.hData); + _Parent.OnError(mon); + break; + } + case Ddeml.MF_HSZ_INFO: + { + // Get the MONHSZSTRUCT object. + int length = 0; + IntPtr phData = Ddeml.DdeAccessData(t.hData, ref length); + Ddeml.MONHSZSTRUCT mon = (Ddeml.MONHSZSTRUCT)Marshal.PtrToStructure(phData, typeof(Ddeml.MONHSZSTRUCT)); + Ddeml.DdeUnaccessData(t.hData); + _Parent.OnString(mon); + break; + } + case Ddeml.MF_LINKS: + { + // Get the MONLINKSTRUCT object. + int length = 0; + IntPtr phData = Ddeml.DdeAccessData(t.hData, ref length); + Ddeml.MONLINKSTRUCT mon = (Ddeml.MONLINKSTRUCT)Marshal.PtrToStructure(phData, typeof(Ddeml.MONLINKSTRUCT)); + Ddeml.DdeUnaccessData(t.hData); + _Parent.OnLink(mon); + break; + } + case Ddeml.MF_POSTMSGS: + { + // Get the MONMSGSTRUCT object. + int length = 0; + IntPtr phData = Ddeml.DdeAccessData(t.hData, ref length); + Ddeml.MONMSGSTRUCT mon = (Ddeml.MONMSGSTRUCT)Marshal.PtrToStructure(phData, typeof(Ddeml.MONMSGSTRUCT)); + Ddeml.DdeUnaccessData(t.hData); + _Parent.OnPost(mon); + break; + } + case Ddeml.MF_SENDMSGS: + { + // Get the MONMSGSTRUCT object. + int length = 0; + IntPtr phData = Ddeml.DdeAccessData(t.hData, ref length); + Ddeml.MONMSGSTRUCT mon = (Ddeml.MONMSGSTRUCT)Marshal.PtrToStructure(phData, typeof(Ddeml.MONMSGSTRUCT)); + Ddeml.DdeUnaccessData(t.hData); + _Parent.OnSend(mon); + break; + } + } + } + return true; + } + + } // class + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Internal/Advanced/Monitor/DdemlStringActivityEventArgs.cs b/NDDE/NDde/Source/NDde/Internal/Advanced/Monitor/DdemlStringActivityEventArgs.cs new file mode 100644 index 0000000..6d84563 --- /dev/null +++ b/NDDE/NDde/Source/NDde/Internal/Advanced/Monitor/DdemlStringActivityEventArgs.cs @@ -0,0 +1,75 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Foundation.Advanced.Monitor +{ + using System; + + internal enum DdemlStringActivityType + { + CleanUp = Ddeml.MH_CLEANUP, + + Create = Ddeml.MH_CREATE, + + Delete = Ddeml.MH_DELETE, + + Keep = Ddeml.MH_KEEP + + } // enum + + internal sealed class DdemlStringActivityEventArgs : DdemlActivityEventArgs + { + private string _Value = ""; + private DdemlStringActivityType _Action = DdemlStringActivityType.Create; + + public DdemlStringActivityEventArgs( + string value, + DdemlStringActivityType action, + IntPtr taskHandle) : base(taskHandle) + { + _Value = value; + _Action = action; + } + + public string Value + { + get { return _Value; } + } + + public DdemlStringActivityType Action + { + get { return _Action; } + } + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Internal/Client/DdemlAdviseEventArgs.cs b/NDDE/NDde/Source/NDde/Internal/Client/DdemlAdviseEventArgs.cs new file mode 100644 index 0000000..2bc5ee6 --- /dev/null +++ b/NDDE/NDde/Source/NDde/Internal/Client/DdemlAdviseEventArgs.cs @@ -0,0 +1,74 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Foundation.Client +{ + using System; + + internal sealed class DdemlAdviseEventArgs : DdemlEventArgs + { + private string _Item = ""; + private int _Format = 0; + private object _State = null; + private byte[] _Data = null; + + public DdemlAdviseEventArgs(string item, int format, object state, byte[] data) + { + _Item = item; + _Format = format; + _State = state; + _Data = data; + } + + public string Item + { + get { return _Item; } + } + + public int Format + { + get { return _Format; } + } + + public object State + { + get { return _State; } + } + + public byte[] Data + { + get { return _Data; } + } + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Internal/Client/DdemlClient.cs b/NDDE/NDde/Source/NDde/Internal/Client/DdemlClient.cs new file mode 100644 index 0000000..a784354 --- /dev/null +++ b/NDDE/NDde/Source/NDde/Internal/Client/DdemlClient.cs @@ -0,0 +1,1865 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Foundation.Client +{ + using System; + using System.Collections.Generic; + using System.Text; + using System.Threading; + using System.Windows.Forms; + using NDde.Foundation.Advanced; + using NDde.Properties; + + internal class DdemlClient : IDisposable + { + private DdemlContext _Context = null; // DDEML instance manager + private int _InstanceId = 0; // DDEML instance identifier + private string _Service = ""; // DDEML service name + private string _Topic = ""; // DDEML topic name + private IntPtr _ConversationHandle = IntPtr.Zero; // DDEML conversation handle + private bool _Paused = false; // DDEML callback enabled? + private IDictionary _AsynchronousTransactionTable = new Dictionary(); // Active DDEML transactions + private IDictionary _AdviseLoopTable = new Dictionary(); // Active DDEML advise loops + private bool _Disposed = false; + + public event EventHandler Advise; + + public event EventHandler Disconnected; + + internal event EventHandler StateChange; + + public DdemlClient(string service, string topic) + : this(service, topic, DdemlContext.GetDefault()) + { + } + + public DdemlClient(string service, string topic, DdemlContext context) + { + if (service == null) + { + throw new ArgumentNullException("service"); + } + if (service.Length > Ddeml.MAX_STRING_SIZE) + { + throw new ArgumentException(Resources.StringParameterInvalidMessage, "service"); + } + if (topic == null) + { + throw new ArgumentNullException("topic"); + } + if (topic.Length > Ddeml.MAX_STRING_SIZE) + { + throw new ArgumentException(Resources.StringParameterInvalidMessage, "topic"); + } + if (context == null) + { + throw new ArgumentNullException("context"); + } + + _Service = service; + _Topic = topic; + _Context = context; + } + + ~DdemlClient() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (!_Disposed) + { + _Disposed = true; + if (disposing) + { + if (IsConnected) + { + // Terminate the conversation. + ConversationManager.Disconnect(_ConversationHandle); + + // Assign each active asynchronous transaction an exception so that the EndXXX methods do not deadlock. + foreach (AsyncResultBase arb in _AsynchronousTransactionTable.Values) + { + arb.Process(new DdemlException(Resources.NotConnectedMessage)); + } + + // Make sure the asynchronous transaction and advise loop tables are empty. + _AsynchronousTransactionTable.Clear(); + _AdviseLoopTable.Clear(); + + // Unregister this client from the context so that it will not receive DDEML callbacks. + _Context.UnregisterClient(this); + + // Indicate that this object is no longer connected or paused. + _Paused = false; + _ConversationHandle = IntPtr.Zero; + _InstanceId = 0; + + // Raise the StateChange event. + foreach (EventHandler handler in StateChange.GetInvocationList()) + { + try + { + handler(this, EventArgs.Empty); + } + catch + { + // Swallow any exception that occurs. + } + } + + // Raise the Disconnected event. + foreach (EventHandler handler in Disconnected.GetInvocationList()) + { + try + { + handler(this, new DdemlDisconnectedEventArgs(false, true)); + } + catch + { + // Swallow any exception that occurs. + } + } + } + } + else + { + if (IsConnected) + { + // Terminate the conversation. + ConversationManager.Disconnect(_ConversationHandle); + } + } + } + } + + public virtual string Service + { + get { return _Service; } + } + + public virtual string Topic + { + get { return _Topic; } + } + + public virtual IntPtr Handle + { + get { return _ConversationHandle; } + } + + public virtual bool IsPaused + { + get { return _Paused; } + } + + public virtual bool IsConnected + { + get { return _ConversationHandle != IntPtr.Zero; } + } + + internal bool IsDisposed + { + get { return _Disposed; } + } + + public virtual void Connect() + { + int error = TryConnect(); + + if (error == -1) + { + throw new ObjectDisposedException(this.GetType().ToString()); + } + if (error == -2) + { + throw new InvalidOperationException(Resources.AlreadyConnectedMessage); + } + if (error > Ddeml.DMLERR_NO_ERROR) + { + string message = Resources.ConnectFailedMessage; + message = message.Replace("${service}", _Service); + message = message.Replace("${topic}", _Topic); + throw new DdemlException(message, error); + } + } + + public virtual int TryConnect() + { + if (IsDisposed) + { + return -1; + } + if (IsConnected) + { + return -2; + } + + // Make sure the context is initialized. + if (!_Context.IsInitialized) + { + _Context.Initialize(); + } + + // Get a local copy of the DDEML instance identifier so that it can be used in the finalizer. + _InstanceId = _Context.InstanceId; + + // Establish a conversation with a server that supports the service name and topic name pair. + _ConversationHandle = ConversationManager.Connect(_InstanceId, _Service, _Topic); + + // If the conversation handle is null then the conversation could not be established. + if (_ConversationHandle == IntPtr.Zero) + { + return Ddeml.DdeGetLastError(_InstanceId); + } + + // Register this client with the context so that it can receive DDEML callbacks. + _Context.RegisterClient(this); + + // Raise the StateChange event. + if (StateChange != null) + { + StateChange(this, EventArgs.Empty); + } + + return Ddeml.DMLERR_NO_ERROR; + } + + public virtual void Disconnect() + { + if (IsDisposed) + { + throw new ObjectDisposedException(this.GetType().ToString()); + } + if (!IsConnected) + { + throw new InvalidOperationException(Resources.NotConnectedMessage); + } + + // Terminate the conversation. + ConversationManager.Disconnect(_ConversationHandle); + + // Assign each active asynchronous transaction an exception so that the EndXXX methods do not deadlock. + foreach (AsyncResultBase arb in _AsynchronousTransactionTable.Values) + { + arb.Process(new DdemlException(Resources.NotConnectedMessage)); + } + + // Make sure the asynchronous transaction and advise loop tables are empty. + _AsynchronousTransactionTable.Clear(); + _AdviseLoopTable.Clear(); + + // Unregister this client from the context so that it will not receive DDEML callbacks. + _Context.UnregisterClient(this); + + // Indicate that this object is no longer connected or paused. + _Paused = false; + _ConversationHandle = IntPtr.Zero; + _InstanceId = 0; + + // Raise the StateChange event. + if (StateChange != null) + { + StateChange(this, EventArgs.Empty); + } + + // Raise the Disconnected event. + if (Disconnected != null) + { + Disconnected(this, new DdemlDisconnectedEventArgs(false, false)); + } + } + + public virtual void Pause() + { + if (IsDisposed) + { + throw new ObjectDisposedException(this.GetType().ToString()); + } + if (!IsConnected) + { + throw new InvalidOperationException(Resources.NotConnectedMessage); + } + if (IsPaused) + { + throw new InvalidOperationException(Resources.AlreadyPausedMessage); + } + + // Disable the DDEML callback. + bool result = Ddeml.DdeEnableCallback(_InstanceId, _ConversationHandle, Ddeml.EC_DISABLE); + + // Check to see if the DDEML callback was disabled. + if (!result) + { + int error = Ddeml.DdeGetLastError(_InstanceId); + throw new DdemlException(Resources.ClientPauseFailedMessage, error); + } + + // The DDEML callback was disabled successfully. + _Paused = true; + + // Raise the StateChange event. + if (StateChange != null) + { + StateChange(this, EventArgs.Empty); + } + } + + public virtual void Resume() + { + if (IsDisposed) + { + throw new ObjectDisposedException(this.GetType().ToString()); + } + if (!IsConnected) + { + throw new InvalidOperationException(Resources.NotConnectedMessage); + } + if (!IsPaused) + { + throw new InvalidOperationException(Resources.NotPausedMessage); + } + + // Enable the DDEML callback. + bool result = Ddeml.DdeEnableCallback(_InstanceId, _ConversationHandle, Ddeml.EC_ENABLEALL); + + // Check to see if the DDEML callback was enabled. + if (!result) + { + int error = Ddeml.DdeGetLastError(_InstanceId); + throw new DdemlException(Resources.ClientResumeFailedMessage, error); + } + + // The DDEML callback was enabled successfully. + _Paused = false; + + // Raise the StateChange event. + if (StateChange != null) + { + StateChange(this, EventArgs.Empty); + } + } + + public virtual void Abandon(IAsyncResult asyncResult) + { + if (IsDisposed) + { + throw new ObjectDisposedException(this.GetType().ToString()); + } + if (!IsConnected) + { + throw new InvalidOperationException(Resources.NotConnectedMessage); + } + if (!(asyncResult is AsyncResultBase)) + { + throw new ArgumentException(Resources.AsyncResultParameterInvalidMessage, "asyncResult"); + } + + AsyncResultBase arb = (AsyncResultBase)asyncResult; + if (!arb.IsCompleted) + { + // Abandon the asynchronous transaction. + bool result = Ddeml.DdeAbandonTransaction(_InstanceId, _ConversationHandle, arb.TransactionId); + + // Remove the IAsyncResult from the transaction table. + if (_AsynchronousTransactionTable.ContainsKey(arb.TransactionId)) + { + _AsynchronousTransactionTable.Remove(arb.TransactionId); + } + } + } + + public virtual void Execute(string command, int timeout) + { + int error = TryExecute(command, timeout); + + if (error == -1) + { + throw new ObjectDisposedException(this.GetType().ToString()); + } + if (error == -2) + { + throw new InvalidOperationException(Resources.NotConnectedMessage); + } + if (error == -3 && command == null) + { + throw new ArgumentNullException("command"); + } + if (error == -3 && command.Length > Ddeml.MAX_STRING_SIZE) + { + throw new ArgumentException(Resources.StringParameterInvalidMessage, "command"); + } + if (error == -3 && timeout <= 0) + { + throw new ArgumentException(Resources.TimeoutParameterInvalidMessage, "timeout"); + } + if (error > Ddeml.DMLERR_NO_ERROR) + { + string message = Resources.ExecuteFailedMessage; + message = message.Replace("${command}", command); + throw new DdemlException(message, error); + } + } + + public virtual int TryExecute(string command, int timeout) + { + if (IsDisposed) + { + return -1; + } + if (!IsConnected) + { + return -2; + } + if (command == null) + { + return -3; + } + if (command.Length > Ddeml.MAX_STRING_SIZE) + { + return -3; + } + if (timeout <= 0) + { + return -3; + } + + // Convert the command to a byte array with a null terminating character. + byte[] data = _Context.Encoding.GetBytes(command + "\0"); + + // Send the command to the server. + int returnFlags = 0; + IntPtr result = Ddeml.DdeClientTransaction( + data, + data.Length, + _ConversationHandle, + IntPtr.Zero, + Ddeml.CF_TEXT, + Ddeml.XTYP_EXECUTE, + timeout, + ref returnFlags); + + // If the result is null then the server did not process the command. + if (result == IntPtr.Zero) + { + return Ddeml.DdeGetLastError(_InstanceId); + } + + return Ddeml.DMLERR_NO_ERROR; + } + + public virtual IAsyncResult BeginExecute(string command, AsyncCallback callback, object state) + { + if (IsDisposed) + { + throw new ObjectDisposedException(this.GetType().ToString()); + } + if (!IsConnected) + { + throw new InvalidOperationException(Resources.NotConnectedMessage); + } + if (command == null) + { + throw new ArgumentNullException("command"); + } + if (command.Length > Ddeml.MAX_STRING_SIZE) + { + throw new ArgumentException(Resources.StringParameterInvalidMessage, "command"); + } + + // Convert the command to a byte array with a null terminating character. + byte[] data = _Context.Encoding.GetBytes(command + "\0"); + + // Send the command to the server. + int transactionId = 0; + IntPtr result = Ddeml.DdeClientTransaction( + data, + data.Length, + _ConversationHandle, + IntPtr.Zero, + Ddeml.CF_TEXT, + Ddeml.XTYP_EXECUTE, + Ddeml.TIMEOUT_ASYNC, + ref transactionId); + + // If the result is null then the asynchronous operation could not begin. + if (result == IntPtr.Zero) + { + int error = Ddeml.DdeGetLastError(_InstanceId); + string message = Resources.ExecuteFailedMessage; + message = message.Replace("${command}", command); + throw new DdemlException(message, error); + } + + // Create an IAsyncResult for this asynchronous operation and add it to the asynchronous transaction table. + ExecuteAsyncResult ar = new ExecuteAsyncResult(this); + ar.Command = command; + ar.Callback = callback; + ar.AsyncState = state; + ar.TransactionId = transactionId; + _AsynchronousTransactionTable.Add(transactionId, ar); + + return ar; + } + + public virtual void EndExecute(IAsyncResult asyncResult) + { + if (IsDisposed) + { + throw new ObjectDisposedException(this.GetType().ToString()); + } + if (!(asyncResult is ExecuteAsyncResult)) + { + string message = Resources.AsyncResultParameterInvalidMessage; + message = message.Replace("${method}", System.Reflection.MethodInfo.GetCurrentMethod().Name); + throw new ArgumentException(message, "asyncResult"); + } + + ExecuteAsyncResult ar = (ExecuteAsyncResult)asyncResult; + if (!ar.IsCompleted) + { + // WaitOne pumps messages so there is no chance of a deadlock. + ar.AsyncWaitHandle.WaitOne(); + } + if (ar.ExceptionObject != null) + { + throw ar.ExceptionObject; + } + } + + public virtual void Poke(string item, byte[] data, int format, int timeout) + { + int error = TryPoke(item, data, format, timeout); + + if (error == -1) + { + throw new ObjectDisposedException(this.GetType().ToString()); + } + if (error == -2) + { + throw new InvalidOperationException(Resources.NotConnectedMessage); + } + if (error == -3 && data == null) + { + throw new ArgumentNullException("data"); + } + if (error == -3 && item == null) + { + throw new ArgumentNullException("item"); + } + if (error == -3 && item.Length > Ddeml.MAX_STRING_SIZE) + { + throw new ArgumentException(Resources.StringParameterInvalidMessage, "item"); + } + if (error == -3 && timeout <= 0) + { + throw new ArgumentException(Resources.TimeoutParameterInvalidMessage, "timeout"); + } + if (error > Ddeml.DMLERR_NO_ERROR) + { + string message = Resources.PokeFailedMessage; + message = message.Replace("${service}", _Service); + message = message.Replace("${topic}", _Topic); + message = message.Replace("${item}", item); + throw new DdemlException(message, error); + } + } + + public virtual int TryPoke(string item, byte[] data, int format, int timeout) + { + if (IsDisposed) + { + return -1; + } + if (!IsConnected) + { + return -2; + } + if (data == null) + { + return -3; + } + if (item == null) + { + return -3; + } + if (item.Length > Ddeml.MAX_STRING_SIZE) + { + return -3; + } + if (timeout <= 0) + { + return -3; + } + + // Create a string handle for the item name. + IntPtr itemHandle = Ddeml.DdeCreateStringHandle(_InstanceId, item, Ddeml.CP_WINANSI); + + try + { + // Create a data handle for the data being poked. + IntPtr dataHandle = Ddeml.DdeCreateDataHandle(_InstanceId, data, data.Length, 0, itemHandle, format, 0); + + // If the data handle is null then it could not be created. + if (dataHandle == IntPtr.Zero) + { + return Ddeml.DdeGetLastError(_InstanceId); + } + + // Send the data to the server. + int returnFlags = 0; + IntPtr result = Ddeml.DdeClientTransaction( + dataHandle, + -1, + _ConversationHandle, + itemHandle, + format, + Ddeml.XTYP_POKE, + timeout, + ref returnFlags); + + // If the result is null then the server did not process the poke. + if (result == IntPtr.Zero) + { + return Ddeml.DdeGetLastError(_InstanceId); + } + } + finally + { + // Free the string handle created earlier. + Ddeml.DdeFreeStringHandle(_InstanceId, itemHandle); + } + + return Ddeml.DMLERR_NO_ERROR; + } + + public virtual IAsyncResult BeginPoke(string item, byte[] data, int format, AsyncCallback callback, object state) + { + if (IsDisposed) + { + throw new ObjectDisposedException(this.GetType().ToString()); + } + if (!IsConnected) + { + throw new InvalidOperationException(Resources.NotConnectedMessage); + } + if (data == null) + { + throw new ArgumentNullException("data"); + } + if (item == null) + { + throw new ArgumentNullException("item"); + } + if (item.Length > Ddeml.MAX_STRING_SIZE) + { + throw new ArgumentException(Resources.StringParameterInvalidMessage, "item"); + } + + // Create a string handle for the item name. + IntPtr itemHandle = Ddeml.DdeCreateStringHandle(_InstanceId, item, Ddeml.CP_WINANSI); + + try + { + // Create a data handle for the data being poked. + IntPtr dataHandle = Ddeml.DdeCreateDataHandle(_InstanceId, data, data.Length, 0, itemHandle, format, 0); + + // If the data handle is null then it could not be created. + if (dataHandle == IntPtr.Zero) + { + int error = Ddeml.DdeGetLastError(_InstanceId); + string message = Resources.PokeFailedMessage; + message = message.Replace("${service}", _Service); + message = message.Replace("${topic}", _Topic); + message = message.Replace("${item}", item); + throw new DdemlException(message, error); + } + + // Send the data to the server. + int transactionId = 0; + IntPtr result = Ddeml.DdeClientTransaction( + dataHandle, + -1, + _ConversationHandle, + itemHandle, + format, + Ddeml.XTYP_POKE, + Ddeml.TIMEOUT_ASYNC, + ref transactionId); + + // If the result is null then the asynchronous operation could not begin. + if (result == IntPtr.Zero) + { + int error = Ddeml.DdeGetLastError(_InstanceId); + string message = Resources.PokeFailedMessage; + message = message.Replace("${service}", _Service); + message = message.Replace("${topic}", _Topic); + message = message.Replace("${item}", item); + throw new DdemlException(message, error); + } + + // Create an IAsyncResult for the asynchronous operation and add it to the asynchronous transaction table. + PokeAsyncResult ar = new PokeAsyncResult(this); + ar.Item = item; + ar.Format = format; + ar.Callback = callback; + ar.AsyncState = state; + ar.TransactionId = transactionId; + _AsynchronousTransactionTable.Add(transactionId, ar); + + return ar; + } + finally + { + // Free the string handle created earlier. + Ddeml.DdeFreeStringHandle(_InstanceId, itemHandle); + } + } + + public virtual void EndPoke(IAsyncResult asyncResult) + { + if (IsDisposed) + { + throw new ObjectDisposedException(this.GetType().ToString()); + } + if (!(asyncResult is PokeAsyncResult)) + { + string message = Resources.AsyncResultParameterInvalidMessage; + message = message.Replace("${method}", System.Reflection.MethodInfo.GetCurrentMethod().Name); + throw new ArgumentException(message, "asyncResult"); + } + + PokeAsyncResult ar = (PokeAsyncResult)asyncResult; + if (!ar.IsCompleted) + { + // WaitOne pumps messages so there is no chance of a deadlock. + ar.AsyncWaitHandle.WaitOne(); + } + if (ar.ExceptionObject != null) + { + throw ar.ExceptionObject; + } + } + + public virtual byte[] Request(string item, int format, int timeout) + { + byte[] data; + + int error = TryRequest(item, format, timeout, out data); + + if (error == -1) + { + throw new ObjectDisposedException(this.GetType().ToString()); + } + if (error == -2) + { + throw new InvalidOperationException(Resources.NotConnectedMessage); + } + if (error == -3 && item == null) + { + throw new ArgumentNullException("item"); + } + if (error == -3 && item.Length > Ddeml.MAX_STRING_SIZE) + { + throw new ArgumentException(Resources.StringParameterInvalidMessage, "item"); + } + if (error == -3 && timeout <= 0) + { + throw new ArgumentException(Resources.TimeoutParameterInvalidMessage, "timeout"); + } + if (error > Ddeml.DMLERR_NO_ERROR) + { + string message = Resources.RequestFailedMessage; + message = message.Replace("${service}", _Service); + message = message.Replace("${topic}", _Topic); + message = message.Replace("${item}", item); + throw new DdemlException(message, error); + } + + return data; + } + + public virtual int TryRequest(string item, int format, int timeout, out byte[] data) + { + data = null; + + if (IsDisposed) + { + return -1; + } + if (!IsConnected) + { + return -2; + } + if (item == null) + { + return -3; + } + if (item.Length > Ddeml.MAX_STRING_SIZE) + { + return -3; + } + if (timeout <= 0) + { + return -3; + } + + // Create a string handle for the item name. + IntPtr itemHandle = Ddeml.DdeCreateStringHandle(_InstanceId, item, Ddeml.CP_WINANSI); + + // Request the data from the server. + int returnFlags = 0; + IntPtr dataHandle = Ddeml.DdeClientTransaction( + IntPtr.Zero, + 0, + _ConversationHandle, + itemHandle, + format, + Ddeml.XTYP_REQUEST, + timeout, + ref returnFlags); + + // Free the string handle created earlier. + Ddeml.DdeFreeStringHandle(_InstanceId, itemHandle); + + // If the data handle is null then the server did not process the request. + if (dataHandle == IntPtr.Zero) + { + return Ddeml.DdeGetLastError(_InstanceId); + } + + // Get the data from the data handle. + int length = Ddeml.DdeGetData(dataHandle, null, 0, 0); + data = new byte[length]; + length = Ddeml.DdeGetData(dataHandle, data, data.Length, 0); + + // Free the data handle created by the server. + Ddeml.DdeFreeDataHandle(dataHandle); + + return Ddeml.DMLERR_NO_ERROR; + } + + public virtual IAsyncResult BeginRequest(string item, int format, AsyncCallback callback, object state) + { + if (IsDisposed) + { + throw new ObjectDisposedException(this.GetType().ToString()); + } + if (!IsConnected) + { + throw new InvalidOperationException(Resources.NotConnectedMessage); + } + if (item == null) + { + throw new ArgumentNullException("item"); + } + if (item.Length > Ddeml.MAX_STRING_SIZE) + { + throw new ArgumentException(Resources.StringParameterInvalidMessage, "item"); + } + + // Create a string handle for the item name. + IntPtr itemHandle = Ddeml.DdeCreateStringHandle(_InstanceId, item, Ddeml.CP_WINANSI); + + // TODO: It might be possible that the request completed synchronously. + // Request the data from the server. + int transactionId = 0; + IntPtr result = Ddeml.DdeClientTransaction( + IntPtr.Zero, + 0, + _ConversationHandle, + itemHandle, + format, + Ddeml.XTYP_REQUEST, + Ddeml.TIMEOUT_ASYNC, + ref transactionId); + + // Free the string handle created earlier. + Ddeml.DdeFreeStringHandle(_InstanceId, itemHandle); + + // If the result is null then the asynchronous operation could not begin. + if (result == IntPtr.Zero) + { + int error = Ddeml.DdeGetLastError(_InstanceId); + string message = Resources.RequestFailedMessage; + message = message.Replace("${service}", _Service); + message = message.Replace("${topic}", _Topic); + message = message.Replace("${item}", item); + throw new DdemlException(message, error); + } + + // Create an IAsyncResult for the asynchronous operation and add it to the asynchronous transaction table. + RequestAsyncResult ar = new RequestAsyncResult(this); + ar.Item = item; + ar.Format = format; + ar.Callback = callback; + ar.AsyncState = state; + ar.TransactionId = transactionId; + _AsynchronousTransactionTable.Add(transactionId, ar); + + return ar; + } + + public virtual byte[] EndRequest(IAsyncResult asyncResult) + { + if (IsDisposed) + { + throw new ObjectDisposedException(this.GetType().ToString()); + } + if (!(asyncResult is RequestAsyncResult)) + { + string message = Resources.AsyncResultParameterInvalidMessage; + message = message.Replace("${method}", System.Reflection.MethodInfo.GetCurrentMethod().Name); + throw new ArgumentException(message, "asyncResult"); + } + + RequestAsyncResult ar = (RequestAsyncResult)asyncResult; + if (!ar.IsCompleted) + { + // WaitOne pumps messages so there is no chance of a deadlock. + ar.AsyncWaitHandle.WaitOne(); + } + if (ar.ExceptionObject != null) + { + throw ar.ExceptionObject; + } + + return ar.Data; + } + + public virtual void StartAdvise(string item, int format, bool hot, bool acknowledge, int timeout, object adviseState) + { + if (IsDisposed) + { + throw new ObjectDisposedException(this.GetType().ToString()); + } + if (!IsConnected) + { + throw new InvalidOperationException(Resources.NotConnectedMessage); + } + if (item == null) + { + throw new ArgumentNullException("item"); + } + if (item.Length > Ddeml.MAX_STRING_SIZE) + { + throw new ArgumentException(Resources.StringParameterInvalidMessage, "item"); + } + if (timeout <= 0) + { + throw new ArgumentException(Resources.TimeoutParameterInvalidMessage, "timeout"); + } + if (_AdviseLoopTable.ContainsKey(item)) + { + string message = Resources.AlreadyBeingAdvisedMessage; + message = message.Replace("${service}", _Service); + message = message.Replace("${topic}", _Topic); + message = message.Replace("${item}", item); + throw new InvalidOperationException(message); + } + + // Create a AdviseLoop object to associate with this advise loop and add it to the advise loop table. + // The object is added to the advise loop table first because an advisory could come in synchronously during the call + // DdeClientTransaction. The assumption is that the advise loop will be initiated successfully. If it is not then the object must + // be removed from the advise loop table prior to leaving this method. + AdviseLoop adviseLoop = new AdviseLoop(this); + adviseLoop.Item = item; + adviseLoop.Format = format; + adviseLoop.State = adviseState; + _AdviseLoopTable.Add(item, adviseLoop); + + // Determine whether the client should acknowledge an advisory before the server posts another. + bool ack = acknowledge; + + // Create a string handle for the item name. + IntPtr itemHandle = Ddeml.DdeCreateStringHandle(_InstanceId, item, Ddeml.CP_WINANSI); + + // Initiate an advise loop. + int type = Ddeml.XTYP_ADVSTART; + type = !hot ? type | Ddeml.XTYPF_NODATA : type; + type = ack ? type | Ddeml.XTYPF_ACKREQ : type; + int returnFlags = 0; + IntPtr result = Ddeml.DdeClientTransaction( + IntPtr.Zero, + 0, + _ConversationHandle, + itemHandle, + format, + type, + timeout, + ref returnFlags); + + // Free the string handle created earlier. + Ddeml.DdeFreeStringHandle(_InstanceId, itemHandle); + + // If the result is null then the server did not initate the advise loop. + if (result == IntPtr.Zero) + { + // Remove the AdviseLoop object created earlier from the advise loop table. It is no longer valid. + _AdviseLoopTable.Remove(item); + + int error = Ddeml.DdeGetLastError(_InstanceId); + string message = Resources.StartAdviseFailedMessage; + message = message.Replace("${service}", _Service); + message = message.Replace("${topic}", _Topic); + message = message.Replace("${item}", item); + throw new DdemlException(message, error); + } + } + + public virtual IAsyncResult BeginStartAdvise(string item, int format, bool hot, bool acknowledge, AsyncCallback callback, object asyncState, object adviseState) + { + if (IsDisposed) + { + throw new ObjectDisposedException(this.GetType().ToString()); + } + if (!IsConnected) + { + throw new InvalidOperationException(Resources.NotConnectedMessage); + } + if (item == null) + { + throw new ArgumentNullException("item"); + } + if (item.Length > Ddeml.MAX_STRING_SIZE) + { + throw new ArgumentException(Resources.StringParameterInvalidMessage, "item"); + } + if (_AdviseLoopTable.ContainsKey(item)) + { + string message = Resources.AlreadyBeingAdvisedMessage; + message = message.Replace("${service}", _Service); + message = message.Replace("${topic}", _Topic); + message = message.Replace("${item}", item); + throw new InvalidOperationException(message); + } + + // Determine whether the client should acknowledge an advisory before the server posts another. + bool ack = acknowledge; + + // Create a string handle for the item name. + IntPtr itemHandle = Ddeml.DdeCreateStringHandle(_InstanceId, item, Ddeml.CP_WINANSI); + + // Initiate an advise loop. + int type = Ddeml.XTYP_ADVSTART; + type = !hot ? type | Ddeml.XTYPF_NODATA : type; + type = ack ? type | Ddeml.XTYPF_ACKREQ : type; + int transactionId = 0; + IntPtr result = Ddeml.DdeClientTransaction( + IntPtr.Zero, + 0, + _ConversationHandle, + itemHandle, + format, + type, + Ddeml.TIMEOUT_ASYNC, + ref transactionId); + + // Free the string handle created earlier. + Ddeml.DdeFreeStringHandle(_InstanceId, itemHandle); + + // If the result is null then the asynchronous operation could not begin. + if (result == IntPtr.Zero) + { + int error = Ddeml.DdeGetLastError(_InstanceId); + string message = Resources.StartAdviseFailedMessage; + message = message.Replace("${service}", _Service); + message = message.Replace("${topic}", _Topic); + message = message.Replace("${item}", item); + throw new DdemlException(message, error); + } + + // Create an IAsyncResult for the asynchronous operation and add it to the asynchronous transaction table. + StartAdviseAsyncResult ar = new StartAdviseAsyncResult(this); + ar.Item = item; + ar.Format = format; + ar.State = adviseState; + ar.Callback = callback; + ar.AsyncState = asyncState; + ar.TransactionId = transactionId; + _AsynchronousTransactionTable.Add(transactionId, ar); + + return ar; + } + + public virtual void EndStartAdvise(IAsyncResult asyncResult) + { + if (IsDisposed) + { + throw new ObjectDisposedException(this.GetType().ToString()); + } + if (!(asyncResult is StartAdviseAsyncResult)) + { + string message = Resources.AsyncResultParameterInvalidMessage; + message = message.Replace("${method}", System.Reflection.MethodInfo.GetCurrentMethod().Name); + throw new ArgumentException(message, "asyncResult"); + } + + StartAdviseAsyncResult ar = (StartAdviseAsyncResult)asyncResult; + if (!ar.IsCompleted) + { + // WaitOne pumps messages so there is no chance of a deadlock. + ar.AsyncWaitHandle.WaitOne(); + } + if (ar.ExceptionObject != null) + { + throw ar.ExceptionObject; + } + } + + public virtual void StopAdvise(string item, int timeout) + { + if (IsDisposed) + { + throw new ObjectDisposedException(this.GetType().ToString()); + } + if (!IsConnected) + { + throw new InvalidOperationException(Resources.NotConnectedMessage); + } + if (item == null) + { + throw new ArgumentNullException("item"); + } + if (item.Length > Ddeml.MAX_STRING_SIZE) + { + throw new ArgumentException(Resources.StringParameterInvalidMessage, "item"); + } + if (timeout <= 0) + { + throw new ArgumentException(Resources.TimeoutParameterInvalidMessage, "timeout"); + } + if (!_AdviseLoopTable.ContainsKey(item)) + { + string message = Resources.NotBeingAdvisedMessage; + message = message.Replace("${service}", _Service); + message = message.Replace("${topic}", _Topic); + message = message.Replace("${item}", item); + throw new InvalidOperationException(message); + } + + // Get the advise loop object from the advise loop table. + AdviseLoop adviseLoop = _AdviseLoopTable[item]; + + // Create a string handle for the item name. + IntPtr itemHandle = Ddeml.DdeCreateStringHandle(_InstanceId, item, Ddeml.CP_WINANSI); + + // Terminate the advise loop. + int returnFlags = 0; + IntPtr result = Ddeml.DdeClientTransaction( + IntPtr.Zero, + 0, + _ConversationHandle, + itemHandle, + adviseLoop.Format, + Ddeml.XTYP_ADVSTOP, + timeout, + ref returnFlags); + + // Free the string handle created earlier. + Ddeml.DdeFreeStringHandle(_InstanceId, itemHandle); + + // If the result is null then the server could not terminate the advise loop. + if (result == IntPtr.Zero) + { + int error = Ddeml.DdeGetLastError(_InstanceId); + string message = Resources.StopAdviseFailedMessage; + message = message.Replace("${service}", _Service); + message = message.Replace("${topic}", _Topic); + message = message.Replace("${item}", item); + throw new DdemlException(message, error); + } + + // Remove the advise loop object from the advise loop table. + _AdviseLoopTable.Remove(item); + } + + public virtual IAsyncResult BeginStopAdvise(string item, AsyncCallback callback, object state) + { + if (IsDisposed) + { + throw new ObjectDisposedException(this.GetType().ToString()); + } + if (!IsConnected) + { + throw new InvalidOperationException(Resources.NotConnectedMessage); + } + if (item == null) + { + throw new ArgumentNullException("item"); + } + if (item.Length > Ddeml.MAX_STRING_SIZE) + { + throw new ArgumentException(Resources.StringParameterInvalidMessage, "item"); + } + if (!_AdviseLoopTable.ContainsKey(item)) + { + string message = Resources.NotBeingAdvisedMessage; + message = message.Replace("${service}", _Service); + message = message.Replace("${topic}", _Topic); + message = message.Replace("${item}", item); + throw new InvalidOperationException(message); + } + + // Get the advise object from the advise loop table. + AdviseLoop adviseLoop = _AdviseLoopTable[item]; + + // Create a string handle for the item name. + IntPtr itemHandle = Ddeml.DdeCreateStringHandle(_InstanceId, item, Ddeml.CP_WINANSI); + + // Terminate the advise loop. + int transactionId = 0; + IntPtr result = Ddeml.DdeClientTransaction( + IntPtr.Zero, + 0, + _ConversationHandle, + itemHandle, + adviseLoop.Format, + Ddeml.XTYP_ADVSTOP, + Ddeml.TIMEOUT_ASYNC, + ref transactionId); + + // Free the string handle created earlier. + Ddeml.DdeFreeStringHandle(_InstanceId, itemHandle); + + // If the result is null then the asynchronous operation could not begin. + if (result == IntPtr.Zero) + { + int error = Ddeml.DdeGetLastError(_InstanceId); + string message = Resources.StopAdviseFailedMessage; + message = message.Replace("${service}", _Service); + message = message.Replace("${topic}", _Topic); + message = message.Replace("${item}", item); + throw new DdemlException(message, error); + } + + // Create an IAsyncResult for the asyncronous operation and add it to the asynchronous transaction table. + StopAdviseAsyncResult ar = new StopAdviseAsyncResult(this); + ar.Item = item; + ar.Format = adviseLoop.Format; + ar.Callback = callback; + ar.AsyncState = state; + ar.TransactionId = transactionId; + _AsynchronousTransactionTable.Add(transactionId, ar); + + return ar; + } + + public virtual void EndStopAdvise(IAsyncResult asyncResult) + { + if (IsDisposed) + { + throw new ObjectDisposedException(this.GetType().ToString()); + } + if (!(asyncResult is StopAdviseAsyncResult)) + { + string message = Resources.AsyncResultParameterInvalidMessage; + message = message.Replace("${method}", System.Reflection.MethodInfo.GetCurrentMethod().Name); + throw new ArgumentException(message, "asyncResult"); + } + + StopAdviseAsyncResult ar = (StopAdviseAsyncResult)asyncResult; + if (!ar.IsCompleted) + { + // WaitOne pumps messages so there is no chance of a deadlock. + ar.AsyncWaitHandle.WaitOne(); + } + if (ar.ExceptionObject != null) + { + throw ar.ExceptionObject; + } + } + + internal bool ProcessCallback(DdemlTransaction transaction) + { + // This is here to alias the transaction object with a shorter variable name. + DdemlTransaction t = transaction; + + switch (t.uType) + { + case Ddeml.XTYP_ADVDATA: + { + // Get the item name from the hsz2 string handle. + StringBuilder psz = new StringBuilder(Ddeml.MAX_STRING_SIZE); + int length = Ddeml.DdeQueryString(_InstanceId, t.hsz2, psz, psz.Capacity, Ddeml.CP_WINANSI); + string item = psz.ToString(); + + // Delegate processing to the advise loop object. + if (_AdviseLoopTable.ContainsKey(item)) + { + t.dwRet = _AdviseLoopTable[item].Process(t.uType, t.uFmt, t.hConv, t.hsz1, t.hsz2, t.hData, t.dwData1, t.dwData2); + return true; + } + + // This transaction could not be processed here. + return false; + } + case Ddeml.XTYP_XACT_COMPLETE: + { + // Get the transaction identifier from dwData1. + int transactionId = t.dwData1.ToInt32(); + + // Get the IAsyncResult from the asynchronous transaction table and delegate processing to it. + if (_AsynchronousTransactionTable.ContainsKey(transactionId)) + { + AsyncResultBase arb = _AsynchronousTransactionTable[transactionId]; + + // Remove the IAsyncResult from the asynchronous transaction table. + _AsynchronousTransactionTable.Remove(arb.TransactionId); + + t.dwRet = arb.Process(t.uType, t.uFmt, t.hConv, t.hsz1, t.hsz2, t.hData, t.dwData1, t.dwData2); + return true; + } + + // This transaction could not be processed here. + return false; + } + case Ddeml.XTYP_DISCONNECT: + { + // Assign each active asynchronous transaction an exception so that the EndXXX methods do not deadlock. + foreach (AsyncResultBase arb in _AsynchronousTransactionTable.Values) + { + arb.Process(new DdemlException(Resources.NotConnectedMessage)); + } + + // Make sure the asynchronous transaction and advise loop tables are empty. + _AsynchronousTransactionTable.Clear(); + _AdviseLoopTable.Clear(); + + // Unregister this client from the context so that it will not receive DDEML callbacks. + _Context.UnregisterClient(this); + + // Indicate that this object is no longer connected or paused. + _Paused = false; + _ConversationHandle = IntPtr.Zero; + _InstanceId = 0; + + // Raise the StateChange event. + if (StateChange != null) + { + StateChange(this, EventArgs.Empty); + } + + // Raise the Disconnected event. + if (Disconnected != null) + { + Disconnected(this, new DdemlDisconnectedEventArgs(true, false)); + } + + // Return zero to indicate that there are no problems. + t.dwRet = IntPtr.Zero; + return true; + } + } + + // This transaction could not be processed here. + return false; + } + + /// + /// This class is needed to dispose of DDEML resources correctly since the DDEML is thread specific. + /// + private sealed class ConversationManager : IMessageFilter + { + private const int WM_APP = unchecked((int)0x8000); + + private static readonly string DataSlot = typeof(ConversationManager).FullName; + + private static IDictionary _Table = new Dictionary(); + + [System.Runtime.InteropServices.DllImport("user32.dll")] + private static extern void PostThreadMessage(int idThread, int Msg, IntPtr wParam, IntPtr lParam); + + public static IntPtr Connect(int instanceId, string service, string topic) + { + lock (_Table) + { + // Create string handles for the service name and topic name. + IntPtr serviceHandle = Ddeml.DdeCreateStringHandle(instanceId, service, Ddeml.CP_WINANSI); + IntPtr topicHandle = Ddeml.DdeCreateStringHandle(instanceId, topic, Ddeml.CP_WINANSI); + + // Establish a conversation with a server that suppoerts the service name and topic name pair. + IntPtr handle = Ddeml.DdeConnect(instanceId, serviceHandle, topicHandle, IntPtr.Zero); + + // Free the string handles that were created earlier. + Ddeml.DdeFreeStringHandle(instanceId, topicHandle); + Ddeml.DdeFreeStringHandle(instanceId, serviceHandle); + + if (handle != IntPtr.Zero) + { + // Make sure this thread has an IMessageFilter on it. + LocalDataStoreSlot slot = Thread.GetNamedDataSlot(DataSlot); + if (Thread.GetData(slot) == null) + { + ConversationManager filter = new ConversationManager(); + Application.AddMessageFilter(filter); + Thread.SetData(slot, filter); + } + + // Add an entry to the table that maps the conversation handle to the current thread. + _Table.Add(handle, Ddeml.GetCurrentThreadId()); + } + return handle; + } + } + + public static void Disconnect(IntPtr conversationHandle) + { + // This method could be called by the GC finalizer thread. If it is then a direct call to the DDEML will fail since the DDEML is + // thread specific. A message will be posted to the DDEML thread instead. + lock (_Table) + { + if (_Table.ContainsKey(conversationHandle)) + { + // Determine if the current thread matches what is in the table. + int threadId = (int)_Table[conversationHandle]; + if (threadId == Ddeml.GetCurrentThreadId()) + { + // Terminate the conversation. + Ddeml.DdeDisconnect(conversationHandle); + } + else + { + // Post a message to the thread that needs to execute Ddeml.DdeDisconnect. + PostThreadMessage(threadId, WM_APP + 2, conversationHandle, IntPtr.Zero); + } + + // Remove the conversation handle from the table because it is no longer in use. + _Table.Remove(conversationHandle); + } + } + } + + bool IMessageFilter.PreFilterMessage(ref Message m) + { + if (m.Msg == WM_APP + 2) + { + // Terminate the conversation. + Ddeml.DdeDisconnect(m.WParam); + } + return false; + } + + } // class + + private sealed class AdviseLoop + { + private string _Item = ""; + private int _Format = 0; + private DdemlClient _Client = null; + private object _State = null; + + public AdviseLoop(DdemlClient client) + { + _Client = client; + } + + public string Item + { + get { return _Item; } + set { _Item = value; } + } + + public int Format + { + get { return _Format; } + set { _Format = value; } + } + + public object State + { + get { return _State; } + set { _State = value; } + } + + public IntPtr Process(int uType, int uFmt, IntPtr hConv, IntPtr hsz1, IntPtr hsz2, IntPtr hData, IntPtr dwData1, IntPtr dwData2) + { + if (_Client.Advise != null) + { + // Assume this is a warm advise (XTYPF_NODATA). + byte[] data = null; + + // If the data handle is not null then it is a hot advise. + if (hData != IntPtr.Zero) + { + // Get the data from the data handle. + int length = Ddeml.DdeGetData(hData, null, 0, 0); + data = new byte[length]; + length = Ddeml.DdeGetData(hData, data, data.Length, 0); + } + + // Raise the Advise event. + _Client.Advise(_Client, new DdemlAdviseEventArgs(_Item, _Format, _State, data)); + } + + // Return DDE_FACK to indicate that are no problems. + return new IntPtr(Ddeml.DDE_FACK); + } + + } // class + + private abstract class AsyncResultBase : IAsyncResult + { + private object _State = null; + private ManualResetEvent _CompletionEvent = new ManualResetEvent(false); + private bool _IsCompleted = false; + private AsyncCallback _Callback = null; + private DdemlClient _Client = null; + private int _TransactionId = 0; + private Exception _Exception = null; + + public AsyncResultBase(DdemlClient client) + { + _Client = client; + } + + public object AsyncState + { + get { return _State; } + set { _State = value; } + } + + public WaitHandle AsyncWaitHandle + { + get { return _CompletionEvent; } + } + + public bool CompletedSynchronously + { + get { return false; } + } + + public bool IsCompleted + { + get { return _IsCompleted; } + } + + public AsyncCallback Callback + { + get { return _Callback; } + set { _Callback = value; } + } + + public DdemlClient Client + { + get { return _Client; } + } + + public int TransactionId + { + get { return _TransactionId; } + set { _TransactionId = value; } + } + + public Exception ExceptionObject + { + get { return _Exception; } + set { _Exception = value; } + } + + public void Process(Exception exception) + { + _Exception = exception; + + // Mark this IAsyncResult as complete and invoke the callback. + _IsCompleted = true; + _CompletionEvent.Set(); + if (_Callback != null) + { + _Callback(this); + } + } + + public IntPtr Process(int uType, int uFmt, IntPtr hConv, IntPtr hsz1, IntPtr hsz2, IntPtr hData, IntPtr dwData1, IntPtr dwData2) + { + // Delegate processing to the concrete class. + IntPtr returnValue = ProcessCallback(uType, uFmt, hConv, hsz1, hsz2, hData, dwData1, dwData2); + + // Mark this IAsyncResult as complete and invoke the callback. + _IsCompleted = true; + _CompletionEvent.Set(); + if (_Callback != null) + { + _Callback(this); + } + + // The return value is sent to the DDEML. + return returnValue; + } + + protected virtual IntPtr ProcessCallback( + int uType, int uFmt, IntPtr hConv, IntPtr hsz1, IntPtr hsz2, IntPtr hData, IntPtr dwData1, IntPtr dwData2) + { + // The default implementation will return zero to the DDEML. + return IntPtr.Zero; + } + + } // class + + private sealed class ExecuteAsyncResult : AsyncResultBase + { + private string _Command = ""; + + public ExecuteAsyncResult(DdemlClient client) : base(client) + { + } + + public string Command + { + get { return _Command; } + set { _Command = value; } + } + + protected override IntPtr ProcessCallback( + int uType, int uFmt, IntPtr hConv, IntPtr hsz1, IntPtr hsz2, IntPtr hData, IntPtr dwData1, IntPtr dwData2) + { + // If the data handle is null then the server did not process the command. + if (hData == IntPtr.Zero) + { + string message = Resources.ExecuteFailedMessage; + message = message.Replace("${command}", _Command); + this.ExceptionObject = new DdemlException(message); + } + + // Return zero to indicate that there are no problems. + return IntPtr.Zero; + } + + } // class + + private sealed class PokeAsyncResult : AsyncResultBase + { + private string _Item = ""; + private int _Format = 0; + + public PokeAsyncResult(DdemlClient client) : base(client) + { + } + + public string Item + { + get { return _Item; } + set { _Item = value; } + } + + public int Format + { + get { return _Format; } + set { _Format = value; } + } + + protected override IntPtr ProcessCallback( + int uType, int uFmt, IntPtr hConv, IntPtr hsz1, IntPtr hsz2, IntPtr hData, IntPtr dwData1, IntPtr dwData2) + { + // If the data handle is null then the server did not process the poke. + if (hData == IntPtr.Zero) + { + string message = Resources.PokeFailedMessage; + message = message.Replace("${service}", this.Client._Service); + message = message.Replace("${topic}", this.Client._Topic); + message = message.Replace("${item}", _Item); + this.ExceptionObject = new DdemlException(message); + } + + // Return zero to indicate that there are no problems. + return IntPtr.Zero; + } + + } // class + + private sealed class RequestAsyncResult : AsyncResultBase + { + private string _Item = ""; + private int _Format = 0; + private byte[] _Data = null; + + public RequestAsyncResult(DdemlClient client) : base(client) + { + } + + public byte[] Data + { + get { return _Data; } + set { _Data = value; } + } + + public string Item + { + get { return _Item; } + set { _Item = value; } + } + + public int Format + { + get { return _Format; } + set { _Format = value; } + } + + protected override IntPtr ProcessCallback( + int uType, int uFmt, IntPtr hConv, IntPtr hsz1, IntPtr hsz2, IntPtr hData, IntPtr dwData1, IntPtr dwData2) + { + // If the data handle is null then the server did not process the request. + // TODO: Some servers may process the request, but return null anyway? + if (hData == IntPtr.Zero) + { + string message = Resources.RequestFailedMessage; + message = message.Replace("${service}", this.Client._Service); + message = message.Replace("${topic}", this.Client._Topic); + message = message.Replace("${item}", _Item); + this.ExceptionObject = new DdemlException(message); + } + else + { + // Get the data from the data handle. + int length = Ddeml.DdeGetData(hData, null, 0, 0); + _Data = new byte[length]; + length = Ddeml.DdeGetData(hData, _Data, _Data.Length, 0); + } + + // Return zero to indicate that there are no problems. + return IntPtr.Zero; + } + + } // class + + private sealed class StartAdviseAsyncResult : AsyncResultBase + { + private string _Item = ""; + private int _Format = 0; + private object _State = null; + + public StartAdviseAsyncResult(DdemlClient client) : base(client) + { + } + + public string Item + { + get { return _Item; } + set { _Item = value; } + } + + public int Format + { + get { return _Format; } + set { _Format = value; } + } + + public object State + { + get { return _State; } + set { _State = value; } + } + + protected override IntPtr ProcessCallback( + int uType, int uFmt, IntPtr hConv, IntPtr hsz1, IntPtr hsz2, IntPtr hData, IntPtr dwData1, IntPtr dwData2) + { + // If the data handle is null then the server did not initiate the advise loop. + if (hData == IntPtr.Zero) + { + string message = Resources.StartAdviseFailedMessage; + message = message.Replace("${service}", this.Client._Service); + message = message.Replace("${topic}", this.Client._Topic); + message = message.Replace("${item}", _Item); + this.ExceptionObject = new DdemlException(message); + } + else + { + // Create a AdviseLoop object to associate with this advise loop and add it to the owner's advise loop table. + AdviseLoop adviseLoop = new AdviseLoop(this.Client); + adviseLoop.Item = _Item; + adviseLoop.Format = _Format; + adviseLoop.State = _State; + this.Client._AdviseLoopTable.Add(_Item, adviseLoop); + } + + // Return zero to indicate that there are no problems. + return IntPtr.Zero; + } + + } // class + + private sealed class StopAdviseAsyncResult : AsyncResultBase + { + private string _Item = ""; + private int _Format = 0; + + public StopAdviseAsyncResult(DdemlClient client) : base(client) + { + } + + public string Item + { + get { return _Item; } + set { _Item = value; } + } + + public int Format + { + get { return _Format; } + set { _Format = value; } + } + + protected override IntPtr ProcessCallback( + int uType, int uFmt, IntPtr hConv, IntPtr hsz1, IntPtr hsz2, IntPtr hData, IntPtr dwData1, IntPtr dwData2) + { + // If the data handle is null then the server could not terminate the advise loop. + if (hData == IntPtr.Zero) + { + string message = Resources.StopAdviseFailedMessage; + message = message.Replace("${service}", this.Client._Service); + message = message.Replace("${topic}", this.Client._Topic); + message = message.Replace("${item}", _Item); + this.ExceptionObject = new DdemlException(message); + } + else + { + // Remove the advise object from the owner's advise loop table. + this.Client._AdviseLoopTable.Remove(_Item); + } + + // Return zero to indicate that there are no problems. + return IntPtr.Zero; + } + + } // class + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Internal/Client/DdemlDisconnectedEventArgs.cs b/NDDE/NDde/Source/NDde/Internal/Client/DdemlDisconnectedEventArgs.cs new file mode 100644 index 0000000..04b7ce2 --- /dev/null +++ b/NDDE/NDde/Source/NDde/Internal/Client/DdemlDisconnectedEventArgs.cs @@ -0,0 +1,60 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Foundation.Client +{ + using System; + + internal sealed class DdemlDisconnectedEventArgs : DdemlEventArgs + { + private bool _ServerInitiated = false; + private bool _Disposed = false; + + public DdemlDisconnectedEventArgs(bool serverInitiated, bool disposed) + { + _ServerInitiated = serverInitiated; + _Disposed = disposed; + } + + public bool IsServerInitiated + { + get { return _ServerInitiated; } + } + + public bool IsDisposed + { + get { return _Disposed; } + } + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Internal/Ddeml.cs b/NDDE/NDde/Source/NDde/Internal/Ddeml.cs new file mode 100644 index 0000000..1b94205 --- /dev/null +++ b/NDDE/NDde/Source/NDde/Internal/Ddeml.cs @@ -0,0 +1,397 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Foundation +{ + using System; + using System.Runtime.InteropServices; + using System.Text; + + internal static class Ddeml + { + public const int MAX_STRING_SIZE = 255; + + public const int APPCMD_CLIENTONLY = unchecked((int)0x00000010); + public const int APPCMD_FILTERINITS = unchecked((int)0x00000020); + public const int APPCMD_MASK = unchecked((int)0x00000FF0); + public const int APPCLASS_STANDARD = unchecked((int)0x00000000); + public const int APPCLASS_MONITOR = unchecked((int)0x00000001); + public const int APPCLASS_MASK = unchecked((int)0x0000000F); + + public const int CBR_BLOCK = unchecked((int)0xFFFFFFFF); + + public const int CBF_FAIL_SELFCONNECTIONS = unchecked((int)0x00001000); + public const int CBF_FAIL_CONNECTIONS = unchecked((int)0x00002000); + public const int CBF_FAIL_ADVISES = unchecked((int)0x00004000); + public const int CBF_FAIL_EXECUTES = unchecked((int)0x00008000); + public const int CBF_FAIL_POKES = unchecked((int)0x00010000); + public const int CBF_FAIL_REQUESTS = unchecked((int)0x00020000); + public const int CBF_FAIL_ALLSVRXACTIONS = unchecked((int)0x0003f000); + public const int CBF_SKIP_CONNECT_CONFIRMS = unchecked((int)0x00040000); + public const int CBF_SKIP_REGISTRATIONS = unchecked((int)0x00080000); + public const int CBF_SKIP_UNREGISTRATIONS = unchecked((int)0x00100000); + public const int CBF_SKIP_DISCONNECTS = unchecked((int)0x00200000); + public const int CBF_SKIP_ALLNOTIFICATIONS = unchecked((int)0x003c0000); + + public const int CF_TEXT = 1; + + public const int CP_WINANSI = 1004; + public const int CP_WINUNICODE = 1200; + + public const int DDE_FACK = unchecked((int)0x8000); + public const int DDE_FBUSY = unchecked((int)0x4000); + public const int DDE_FDEFERUPD = unchecked((int)0x4000); + public const int DDE_FACKREQ = unchecked((int)0x8000); + public const int DDE_FRELEASE = unchecked((int)0x2000); + public const int DDE_FREQUESTED = unchecked((int)0x1000); + public const int DDE_FAPPSTATUS = unchecked((int)0x00ff); + public const int DDE_FNOTPROCESSED = unchecked((int)0x0000); + + public const int DMLERR_NO_ERROR = unchecked((int)0x0000); + public const int DMLERR_FIRST = unchecked((int)0x4000); + public const int DMLERR_ADVACKTIMEOUT = unchecked((int)0x4000); + public const int DMLERR_BUSY = unchecked((int)0x4001); + public const int DMLERR_DATAACKTIMEOUT = unchecked((int)0x4002); + public const int DMLERR_DLL_NOT_INITIALIZED = unchecked((int)0x4003); + public const int DMLERR_DLL_USAGE = unchecked((int)0x4004); + public const int DMLERR_EXECACKTIMEOUT = unchecked((int)0x4005); + public const int DMLERR_INVALIDPARAMETER = unchecked((int)0x4006); + public const int DMLERR_LOW_MEMORY = unchecked((int)0x4007); + public const int DMLERR_MEMORY_ERROR = unchecked((int)0x4008); + public const int DMLERR_NOTPROCESSED = unchecked((int)0x4009); + public const int DMLERR_NO_CONV_ESTABLISHED = unchecked((int)0x400A); + public const int DMLERR_POKEACKTIMEOUT = unchecked((int)0x400B); + public const int DMLERR_POSTMSG_FAILED = unchecked((int)0x400C); + public const int DMLERR_REENTRANCY = unchecked((int)0x400D); + public const int DMLERR_SERVER_DIED = unchecked((int)0x400E); + public const int DMLERR_SYS_ERROR = unchecked((int)0x400F); + public const int DMLERR_UNADVACKTIMEOUT = unchecked((int)0x4010); + public const int DMLERR_UNFOUND_QUEUE_ID = unchecked((int)0x4011); + public const int DMLERR_LAST = unchecked((int)0x4011); + + public const int DNS_REGISTER = unchecked((int)0x0001); + public const int DNS_UNREGISTER = unchecked((int)0x0002); + public const int DNS_FILTERON = unchecked((int)0x0004); + public const int DNS_FILTEROFF = unchecked((int)0x0008); + + public const int EC_ENABLEALL = unchecked((int)0x0000); + public const int EC_ENABLEONE = unchecked((int)0x0080); + public const int EC_DISABLE = unchecked((int)0x0008); + public const int EC_QUERYWAITING = unchecked((int)0x0002); + + public const int HDATA_APPOWNED = unchecked((int)0x0001); + + public const int MF_HSZ_INFO = unchecked((int)0x01000000); + public const int MF_SENDMSGS = unchecked((int)0x02000000); + public const int MF_POSTMSGS = unchecked((int)0x04000000); + public const int MF_CALLBACKS = unchecked((int)0x08000000); + public const int MF_ERRORS = unchecked((int)0x10000000); + public const int MF_LINKS = unchecked((int)0x20000000); + public const int MF_CONV = unchecked((int)0x40000000); + + public const int MH_CREATE = 1; + public const int MH_KEEP = 2; + public const int MH_DELETE = 3; + public const int MH_CLEANUP = 4; + + public const int QID_SYNC = unchecked((int)0xFFFFFFFF); + public const int TIMEOUT_ASYNC = unchecked((int)0xFFFFFFFF); + + public const int XTYPF_NOBLOCK = unchecked((int)0x0002); + public const int XTYPF_NODATA = unchecked((int)0x0004); + public const int XTYPF_ACKREQ = unchecked((int)0x0008); + public const int XCLASS_MASK = unchecked((int)0xFC00); + public const int XCLASS_BOOL = unchecked((int)0x1000); + public const int XCLASS_DATA = unchecked((int)0x2000); + public const int XCLASS_FLAGS = unchecked((int)0x4000); + public const int XCLASS_NOTIFICATION = unchecked((int)0x8000); + public const int XTYP_ERROR = unchecked((int)(0x0000 | XCLASS_NOTIFICATION | XTYPF_NOBLOCK)); + public const int XTYP_ADVDATA = unchecked((int)(0x0010 | XCLASS_FLAGS)); + public const int XTYP_ADVREQ = unchecked((int)(0x0020 | XCLASS_DATA | XTYPF_NOBLOCK)); + public const int XTYP_ADVSTART = unchecked((int)(0x0030 | XCLASS_BOOL)); + public const int XTYP_ADVSTOP = unchecked((int)(0x0040 | XCLASS_NOTIFICATION)); + public const int XTYP_EXECUTE = unchecked((int)(0x0050 | XCLASS_FLAGS)); + public const int XTYP_CONNECT = unchecked((int)(0x0060 | XCLASS_BOOL | XTYPF_NOBLOCK)); + public const int XTYP_CONNECT_CONFIRM = unchecked((int)(0x0070 | XCLASS_NOTIFICATION | XTYPF_NOBLOCK)); + public const int XTYP_XACT_COMPLETE = unchecked((int)(0x0080 | XCLASS_NOTIFICATION)); + public const int XTYP_POKE = unchecked((int)(0x0090 | XCLASS_FLAGS)); + public const int XTYP_REGISTER = unchecked((int)(0x00A0 | XCLASS_NOTIFICATION | XTYPF_NOBLOCK)); + public const int XTYP_REQUEST = unchecked((int)(0x00B0 | XCLASS_DATA)); + public const int XTYP_DISCONNECT = unchecked((int)(0x00C0 | XCLASS_NOTIFICATION | XTYPF_NOBLOCK)); + public const int XTYP_UNREGISTER = unchecked((int)(0x00D0 | XCLASS_NOTIFICATION | XTYPF_NOBLOCK)); + public const int XTYP_WILDCONNECT = unchecked((int)(0x00E0 | XCLASS_DATA | XTYPF_NOBLOCK)); + public const int XTYP_MONITOR = unchecked((int)(0x00F0 | XCLASS_NOTIFICATION | XTYPF_NOBLOCK)); + public const int XTYP_MASK = unchecked((int)0x00F0); + public const int XTYP_SHIFT = unchecked((int)0x0004); + + public delegate IntPtr DdeCallback( + int uType, int uFmt, IntPtr hConv, IntPtr hsz1, IntPtr hsz2, IntPtr hData, IntPtr dwData1, IntPtr dwData2); + + [DllImport("kernel32.dll")] + public static extern int GetCurrentThreadId(); + + [DllImport("user32.dll", EntryPoint="DdeAbandonTransaction", CharSet=CharSet.Ansi)] + public static extern bool DdeAbandonTransaction(int idInst, IntPtr hConv, int idTransaction); + + [DllImport("user32.dll", EntryPoint="DdeAccessData", CharSet=CharSet.Ansi)] + public static extern IntPtr DdeAccessData(IntPtr hData, ref int pcbDataSize); + + [DllImport("user32.dll", EntryPoint="DdeAddData", CharSet=CharSet.Ansi)] + public static extern IntPtr DdeAddData(IntPtr hData, byte[] pSrc, int cb, int cbOff); + + [DllImport("user32.dll", EntryPoint="DdeClientTransaction", CharSet=CharSet.Ansi)] + public static extern IntPtr DdeClientTransaction( + IntPtr pData, int cbData, IntPtr hConv, IntPtr hszItem, int wFmt, int wType, int dwTimeout, ref int pdwResult); + + [DllImport("user32.dll", EntryPoint="DdeClientTransaction", CharSet=CharSet.Ansi)] + public static extern IntPtr DdeClientTransaction( + byte[] pData, int cbData, IntPtr hConv, IntPtr hszItem, int wFmt, int wType, int dwTimeout, ref int pdwResult); + + [DllImport("user32.dll", EntryPoint="DdeCmpStringHandles", CharSet=CharSet.Ansi)] + public static extern int DdeCmpStringHandles(IntPtr hsz1, IntPtr hsz2); + + [DllImport("user32.dll", EntryPoint="DdeConnect", CharSet=CharSet.Ansi)] + public static extern IntPtr DdeConnect(int idInst, IntPtr hszService, IntPtr hszTopic, IntPtr pCC); + + [DllImport("user32.dll", EntryPoint="DdeConnectList", CharSet=CharSet.Ansi)] + public static extern IntPtr DdeConnectList(int idInst, IntPtr hszService, IntPtr hszTopic, IntPtr hConvList, IntPtr pCC); + + [DllImport("user32.dll", EntryPoint="DdeCreateDataHandle", CharSet=CharSet.Ansi)] + public static extern IntPtr DdeCreateDataHandle(int idInst, byte[] pSrc, int cb, int cbOff, IntPtr hszItem, int wFmt, int afCmd); + + [DllImport("user32.dll", EntryPoint="DdeCreateStringHandle", CharSet=CharSet.Ansi)] + public static extern IntPtr DdeCreateStringHandle(int idInst, string psz, int iCodePage); + + [DllImport("user32.dll", EntryPoint="DdeDisconnect", CharSet=CharSet.Ansi)] + public static extern bool DdeDisconnect(IntPtr hConv); + + [DllImport("user32.dll", EntryPoint="DdeDisconnectList", CharSet=CharSet.Ansi)] + public static extern bool DdeDisconnectList(IntPtr hConvList); + + [DllImport("user32.dll", EntryPoint="DdeEnableCallback", CharSet=CharSet.Ansi)] + public static extern bool DdeEnableCallback(int idInst, IntPtr hConv, int wCmd); + + [DllImport("user32.dll", EntryPoint="DdeFreeDataHandle", CharSet=CharSet.Ansi)] + public static extern bool DdeFreeDataHandle(IntPtr hData); + + [DllImport("user32.dll", EntryPoint="DdeFreeStringHandle", CharSet=CharSet.Ansi)] + public static extern bool DdeFreeStringHandle(int idInst, IntPtr hsz); + + [DllImport("user32.dll", EntryPoint="DdeGetData", CharSet=CharSet.Ansi)] + public static extern int DdeGetData(IntPtr hData, [Out] byte[] pDst, int cbMax, int cbOff); + + [DllImport("user32.dll", EntryPoint="DdeGetLastError", CharSet=CharSet.Ansi)] + public static extern int DdeGetLastError(int idInst); + + [DllImport("user32.dll", EntryPoint="DdeImpersonateClient", CharSet=CharSet.Ansi)] + public static extern bool DdeImpersonateClient(IntPtr hConv); + + [DllImport("user32.dll", EntryPoint="DdeInitialize", CharSet=CharSet.Ansi)] + public static extern int DdeInitialize(ref int pidInst, DdeCallback pfnCallback, int afCmd, int ulRes); + + [DllImport("user32.dll", EntryPoint="DdeKeepStringHandle", CharSet=CharSet.Ansi)] + public static extern bool DdeKeepStringHandle(int idInst, IntPtr hsz); + + [DllImport("user32.dll", EntryPoint="DdeNameService", CharSet=CharSet.Ansi)] + public static extern IntPtr DdeNameService(int idInst, IntPtr hsz1, IntPtr hsz2, int afCmd); + + [DllImport("user32.dll", EntryPoint="DdePostAdvise", CharSet=CharSet.Ansi)] + public static extern bool DdePostAdvise(int idInst, IntPtr hszTopic, IntPtr hszItem); + + [DllImport("user32.dll", EntryPoint="DdeQueryConvInfo", CharSet=CharSet.Ansi)] + public static extern int DdeQueryConvInfo(IntPtr hConv, int idTransaction, IntPtr pConvInfo); + + [DllImport("user32.dll", EntryPoint="DdeQueryNextServer", CharSet=CharSet.Ansi)] + public static extern IntPtr DdeQueryNextServer(IntPtr hConvList, IntPtr hConvPrev); + + [DllImport("user32.dll", EntryPoint="DdeQueryString", CharSet=CharSet.Ansi)] + public static extern int DdeQueryString(int idInst, IntPtr hsz, StringBuilder psz, int cchMax, int iCodePage); + + [DllImport("user32.dll", EntryPoint="DdeReconnect", CharSet=CharSet.Ansi)] + public static extern IntPtr DdeReconnect(IntPtr hConv); + + [DllImport("user32.dll", EntryPoint="DdeSetUserHandle", CharSet=CharSet.Ansi)] + public static extern bool DdeSetUserHandle(IntPtr hConv, int id, IntPtr hUser); + + [DllImport("user32.dll", EntryPoint="DdeUnaccessData", CharSet=CharSet.Ansi)] + public static extern bool DdeUnaccessData(IntPtr hData); + + [DllImport("user32.dll", EntryPoint="DdeUninitialize", CharSet=CharSet.Ansi)] + public static extern bool DdeUninitialize(int idInst); + + [StructLayout(LayoutKind.Sequential)] + public struct HSZPAIR + { + public IntPtr hszSvc; + public IntPtr hszTopic; + } + + [StructLayout(LayoutKind.Sequential)] + public struct CONVINFO + { + public int cb; + public IntPtr hUser; + public IntPtr hConvPartner; + public IntPtr hszSvcPartner; + public IntPtr hszServiceReq; + public IntPtr hszTopic; + public IntPtr hszItem; + public int wFmt; + public int wType; + public int wStatus; + public int wConvst; + public int wLastError; + public IntPtr hConvList; + public CONVCONTEXT ConvCtxt; + public IntPtr hwnd; + public IntPtr hwndPartner; + + } // struct + + [StructLayout(LayoutKind.Sequential)] + public struct CONVCONTEXT + { + public int cb; + public int wFlags; + public int wCountryID; + public int iCodePage; + public int dwLangID; + public int dwSecurity; + [MarshalAs(UnmanagedType.ByValArray, SizeConst=12)] + public byte[] filler; + + } // struct + + [StructLayout(LayoutKind.Sequential)] + public struct MONCBSTRUCT + { + public int cb; + public int dwTime; + public IntPtr hTask; + public IntPtr dwRet; + public int wType; + public int wFmt; + public IntPtr hConv; + public IntPtr hsz1; + public IntPtr hsz2; + public IntPtr hData; + public IntPtr dwData1; + public IntPtr dwData2; + public CONVCONTEXT cc; + public int cbData; + [MarshalAs(UnmanagedType.ByValArray, SizeConst=32)] + public byte[] Data; + + } // struct + + [StructLayout(LayoutKind.Sequential)] + public struct MONCONVSTRUCT + { + public int cb; + public bool fConnect; + public int dwTime; + public IntPtr hTask; + public IntPtr hszSvc; + public IntPtr hszTopic; + public IntPtr hConvClient; + public IntPtr hConvServer; + + } // struct + + [StructLayout(LayoutKind.Sequential)] + public struct MONERRSTRUCT + { + public int cb; + public int wLastError; + public int dwTime; + public IntPtr hTask; + + } // struct + + [StructLayout(LayoutKind.Sequential)] + public struct MONHSZSTRUCT + { + public int cb; + public int fsAction; + public int dwTime; + public IntPtr hsz; + public IntPtr hTask; + public IntPtr str; + + } // struct + + [StructLayout(LayoutKind.Sequential)] + public struct MONLINKSTRUCT + { + public int cb; + public int dwTime; + public IntPtr hTask; + public bool fEstablished; + public bool fNoData; + public IntPtr hszSvc; + public IntPtr hszTopic; + public IntPtr hszItem; + public int wFmt; + public bool fServer; + public IntPtr hConvClient; + public IntPtr hConvServer; + + } // struct + + [StructLayout(LayoutKind.Sequential)] + public struct MONMSGSTRUCT + { + public int cb; + public IntPtr hwndTo; + public int dwTime; + public IntPtr hTask; + public int wMsg; + public IntPtr wParam; + public IntPtr lParam; + public DDEML_MSG_HOOK_DATA dmhd; + + } // struct + + [StructLayout(LayoutKind.Sequential)] + public struct DDEML_MSG_HOOK_DATA + { + public IntPtr uiLo; + public IntPtr uiHi; + public int cbData; + [MarshalAs(UnmanagedType.ByValArray, SizeConst=32)] + public byte[] Data; + + } // struct + + } // class + +} // namespace diff --git a/NDDE/NDde/Source/NDde/Internal/DdemlEventArgs.cs b/NDDE/NDde/Source/NDde/Internal/DdemlEventArgs.cs new file mode 100644 index 0000000..b9422c0 --- /dev/null +++ b/NDDE/NDde/Source/NDde/Internal/DdemlEventArgs.cs @@ -0,0 +1,58 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Foundation +{ + using System; + + internal abstract class DdemlEventArgs : EventArgs + { + public override string ToString() + { + string s = ""; + foreach (System.Reflection.PropertyInfo property in this.GetType().GetProperties()) + { + if (s.Length == 0) + { + s += property.Name + "=" + property.GetValue(this, null).ToString(); + } + else + { + s += " " + property.Name + "=" + property.GetValue(this, null).ToString(); + } + } + return s; + } + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Internal/DdemlException.cs b/NDDE/NDde/Source/NDde/Internal/DdemlException.cs new file mode 100644 index 0000000..3ca4653 --- /dev/null +++ b/NDDE/NDde/Source/NDde/Internal/DdemlException.cs @@ -0,0 +1,75 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Foundation +{ + using System; + using System.Runtime.Serialization; + using NDde.Properties; + + [Serializable] + internal class DdemlException : Exception + { + private int _Code = 0; + + public DdemlException() : this(Resources.UnknownErrorMessage) + { + } + + public DdemlException(string message) : base(message) + { + } + + public DdemlException(string message, int code) : base(message) + { + _Code = code; + } + + protected DdemlException(SerializationInfo info, StreamingContext context) : base(info, context) + { + _Code = info.GetInt32("NDde.Ddeml.DdemlException.Code"); + } + + public int Code + { + get { return _Code; } + } + + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue("NDde.Ddeml.DdemlException.Code", _Code); + base.GetObjectData(info, context); + } + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Internal/Server/DdemlConversation.cs b/NDDE/NDde/Source/NDde/Internal/Server/DdemlConversation.cs new file mode 100644 index 0000000..a8a0088 --- /dev/null +++ b/NDDE/NDde/Source/NDde/Internal/Server/DdemlConversation.cs @@ -0,0 +1,117 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Foundation.Server +{ + using System; + + internal sealed class DdemlConversation + { + private IntPtr _Handle = IntPtr.Zero; + private string _Service = ""; + private string _Topic = ""; + private int _Waiting = 0; + private object _Tag = null; + + internal event EventHandler StateChange; + + public DdemlConversation(IntPtr handle, string service, string topic) + { + _Handle = handle; + _Service = service; + _Topic = topic; + } + + public IntPtr Handle + { + get { return _Handle; } + } + + public string Topic + { + get { return _Topic; } + } + + public string Service + { + get { return _Service; } + } + + public bool IsPaused + { + get { return _Waiting > 0; } + } + + public object Tag + { + get { return _Tag; } + set { _Tag = value; } + } + + public override string ToString() + { + string s = ""; + foreach (System.Reflection.PropertyInfo property in this.GetType().GetProperties()) + { + if (s.Length == 0) + { + s += property.Name + "=" + property.GetValue(this, null).ToString(); + } + else + { + s += " " + property.Name + "=" + property.GetValue(this, null).ToString(); + } + } + return s; + } + + internal void IncrementWaiting() + { + _Waiting++; + if (StateChange != null) + { + StateChange(this, EventArgs.Empty); + } + } + + internal void DecrementWaiting() + { + _Waiting--; + if (StateChange != null) + { + StateChange(this, EventArgs.Empty); + } + } + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Internal/Server/DdemlServer.cs b/NDDE/NDde/Source/NDde/Internal/Server/DdemlServer.cs new file mode 100644 index 0000000..9121ed5 --- /dev/null +++ b/NDDE/NDde/Source/NDde/Internal/Server/DdemlServer.cs @@ -0,0 +1,993 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Foundation.Server +{ + using System; + using System.Collections.Generic; + using System.Runtime.InteropServices; + using System.Text; + using System.Threading; + using System.Windows.Forms; + using NDde.Foundation.Advanced; + using NDde.Properties; + + internal abstract class DdemlServer : IDisposable + { + private DdemlContext _Context = null; // DDEML instance manager + private int _InstanceId = 0; // DDEML instance identifier + private string _Service = ""; // DDEML service name + private IntPtr _ServiceHandle = IntPtr.Zero; // DDEML service handle + private IDictionary _ConversationTable = new Dictionary(); // Active DDEML conversations + private IDictionary _AdviseRequestCache = new Dictionary(); // Cached advise data + private bool _Disposed = false; + + internal event EventHandler StateChange; + + public DdemlServer(string service) + : this(service, DdemlContext.GetDefault()) + { + } + + public DdemlServer(string service, DdemlContext context) + { + if (service == null) + { + throw new ArgumentNullException("service"); + } + if (service.Length > Ddeml.MAX_STRING_SIZE) + { + throw new ArgumentException(Resources.StringParameterInvalidMessage, "service"); + } + if (context == null) + { + throw new ArgumentNullException("context"); + } + + _Service = service; + _Context = context; + } + + ~DdemlServer() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (!_Disposed) + { + _Disposed = true; + if (disposing) + { + if (IsRegistered) + { + // Unregister the service name. + RegistrationManager.Unregister(_InstanceId, _ServiceHandle); + + // Unregister this server from the context so that it will not receive DDEML callbacks. + _Context.UnregisterServer(this); + + // Indicate that the service name is no longer registered. + _ServiceHandle = IntPtr.Zero; + _InstanceId = 0; + + // Raise the StateChange event. + foreach (EventHandler handler in StateChange.GetInvocationList()) + { + try + { + handler(this, EventArgs.Empty); + } + catch + { + // Swallow any exception that occurs. + } + } + } + } + else + { + if (IsRegistered) + { + // Unregister the service name. + RegistrationManager.Unregister(_InstanceId, _ServiceHandle); + } + } + } + } + + public virtual string Service + { + get { return _Service; } + } + + public virtual bool IsRegistered + { + get { return _ServiceHandle != IntPtr.Zero; } + } + + internal bool IsDisposed + { + get { return _Disposed; } + } + + public virtual void Register() + { + if (IsDisposed) + { + throw new ObjectDisposedException(this.GetType().ToString()); + } + if (IsRegistered) + { + throw new InvalidOperationException(Resources.AlreadyRegisteredMessage); + } + + // Make sure the context is initialized. + if (!_Context.IsInitialized) + { + _Context.Initialize(); + } + + // Get a local copy of the DDEML instance identifier so that it can be used in the finalizer. + _InstanceId = _Context.InstanceId; + + // Make sure the conversation table is empty. + _ConversationTable.Clear(); + + // Register the service name. + _ServiceHandle = RegistrationManager.Register(_InstanceId, _Service); + + // If the service handle is null then the service name could not be registered. + if (_ServiceHandle == IntPtr.Zero) + { + int error = Ddeml.DdeGetLastError(_InstanceId); + string message = Resources.RegisterFailedMessage; + message = message.Replace("${service}", _Service); + throw new DdemlException(message, error); + } + + // Register this server with the context so that it can receive DDEML callbacks. + _Context.RegisterServer(this); + + // Raise the StateChange event. + if (StateChange != null) + { + StateChange(this, EventArgs.Empty); + } + } + + public virtual void Unregister() + { + if (IsDisposed) + { + throw new ObjectDisposedException(this.GetType().ToString()); + } + if (!IsRegistered) + { + throw new InvalidOperationException(Resources.NotRegisteredMessage); + } + + // Unregister the service name. + RegistrationManager.Unregister(_InstanceId, _ServiceHandle); + + // Unregister this server from the context so that it will not receive DDEML callbacks. + _Context.UnregisterServer(this); + + // Indicate that the service name is no longer registered. + _ServiceHandle = IntPtr.Zero; + _InstanceId = 0; + + // Raise the StateChange event. + if (StateChange != null) + { + StateChange(this, EventArgs.Empty); + } + } + + public virtual void Advise(string topic, string item) + { + if (IsDisposed) + { + throw new ObjectDisposedException(this.GetType().ToString()); + } + if (!IsRegistered) + { + throw new InvalidOperationException(Resources.NotRegisteredMessage); + } + if (topic == null) + { + throw new ArgumentNullException("topic"); + } + if (topic.Length > Ddeml.MAX_STRING_SIZE) + { + throw new ArgumentException(Resources.StringParameterInvalidMessage, "topic"); + } + if (item == null) + { + throw new ArgumentNullException("item"); + } + if (item.Length > Ddeml.MAX_STRING_SIZE) + { + throw new ArgumentException(Resources.StringParameterInvalidMessage, "item"); + } + + // Assume the topic name and item name are wild. + IntPtr topicHandle = IntPtr.Zero; + IntPtr itemHandle = IntPtr.Zero; + + // Create a string handle for the topic name if it is not wild. + if (topic != "*") + { + topicHandle = Ddeml.DdeCreateStringHandle(_InstanceId, topic, Ddeml.CP_WINANSI); + } + + // Create a string handle for the item name if it is not wild. + if (item != "*") + { + itemHandle = Ddeml.DdeCreateStringHandle(_InstanceId, item, Ddeml.CP_WINANSI); + } + + // Post an advise notification. This will cause an XTYP_ADVREQ transaction for each conversation. + bool result = Ddeml.DdePostAdvise(_InstanceId, topicHandle, itemHandle); + + // Free the string handles created earlier. + Ddeml.DdeFreeStringHandle(_InstanceId, itemHandle); + Ddeml.DdeFreeStringHandle(_InstanceId, topicHandle); + + // Check the result to see if the post failed. + if (!result) + { + int error = Ddeml.DdeGetLastError(_InstanceId); + string message = Resources.AdviseFailedMessage; + message = message.Replace("${service}", _Service); + message = message.Replace("${topic}", topic); + message = message.Replace("${item}", item); + throw new DdemlException(message, error); + } + } + + public virtual void Pause(DdemlConversation conversation) + { + if (IsDisposed) + { + throw new ObjectDisposedException(this.GetType().ToString()); + } + if (!IsRegistered) + { + throw new InvalidOperationException(Resources.NotRegisteredMessage); + } + if (conversation == null) + { + throw new ArgumentNullException("conversation"); + } + if (conversation.IsPaused) + { + throw new InvalidOperationException(Resources.AlreadyPausedMessage); + } + + // Disable the DDEML callback for the specified conversation only. + bool result = Ddeml.DdeEnableCallback(_InstanceId, conversation.Handle, Ddeml.EC_DISABLE); + + // Check the result to see if the DDEML callback was disabled. + if (!result) + { + int error = Ddeml.DdeGetLastError(_InstanceId); + throw new DdemlException(Resources.ServerPauseFailedMessage, error); + } + + // Increment the conversation's waiting count. + conversation.IncrementWaiting(); + } + + public virtual void Pause() + { + if (IsDisposed) + { + throw new ObjectDisposedException(this.GetType().ToString()); + } + if (!IsRegistered) + { + throw new InvalidOperationException(Resources.NotRegisteredMessage); + } + + // Disable the DDEML callback for all conversations. + bool result = Ddeml.DdeEnableCallback(_InstanceId, IntPtr.Zero, Ddeml.EC_DISABLE); + + // Check the result to see if the DDEML callback was disabled. + if (!result) + { + int error = Ddeml.DdeGetLastError(_InstanceId); + throw new DdemlException(Resources.ServerPauseAllFailedMessage, error); + } + + // Increment each conversation's waiting count. + foreach (DdemlConversation conversation in _ConversationTable.Values) + { + conversation.IncrementWaiting(); + } + } + + public virtual void Resume(DdemlConversation conversation) + { + if (IsDisposed) + { + throw new ObjectDisposedException(this.GetType().ToString()); + } + if (!IsRegistered) + { + throw new InvalidOperationException(Resources.NotRegisteredMessage); + } + if (conversation == null) + { + throw new ArgumentNullException("conversation"); + } + if (!conversation.IsPaused) + { + throw new InvalidOperationException(Resources.NotPausedMessage); + } + + // Enable the DDEML callback for the specified conversation only. + bool result = Ddeml.DdeEnableCallback(_InstanceId, conversation.Handle, Ddeml.EC_ENABLEALL); + + // Check the result to see if the DDEML callback was enabled. + if (!result) + { + int error = Ddeml.DdeGetLastError(_InstanceId); + throw new DdemlException(Resources.ServerResumeFailedMessage, error); + } + + // Decrement the conversation's waiting count. The conversation will only resume if the count is zero. + conversation.DecrementWaiting(); + } + + public virtual void Resume() + { + if (IsDisposed) + { + throw new ObjectDisposedException(this.GetType().ToString()); + } + if (!IsRegistered) + { + throw new InvalidOperationException(Resources.NotRegisteredMessage); + } + + // Enable the DDEML callback for all conversations. + bool result = Ddeml.DdeEnableCallback(_InstanceId, IntPtr.Zero, Ddeml.EC_ENABLEALL); + + // Check the result to see if the DDEML callback was enabled. + if (!result) + { + int error = Ddeml.DdeGetLastError(_InstanceId); + throw new DdemlException(Resources.ServerResumeAllFailedMessage, error); + } + + // Decrement each conversation's waiting count. The conversation will only resume if the count is zero. + foreach (DdemlConversation conversation in _ConversationTable.Values) + { + conversation.DecrementWaiting(); + } + } + + public virtual void Disconnect(DdemlConversation conversation) + { + if (IsDisposed) + { + throw new ObjectDisposedException(this.GetType().ToString()); + } + if (!IsRegistered) + { + throw new InvalidOperationException(Resources.NotRegisteredMessage); + } + if (conversation == null) + { + throw new ArgumentNullException("conversation"); + } + + if (_ConversationTable.ContainsKey(conversation.Handle)) + { + // Terminate the conversation. + Ddeml.DdeDisconnect(conversation.Handle); + + // Remove the Conversation from the conversation table. + _ConversationTable.Remove(conversation.Handle); + } + } + + public virtual void Disconnect() + { + if (IsDisposed) + { + throw new ObjectDisposedException(this.GetType().ToString()); + } + if (!IsRegistered) + { + throw new InvalidOperationException(Resources.NotRegisteredMessage); + } + + // Terminate all conversations. + foreach (DdemlConversation conversation in _ConversationTable.Values) + { + Ddeml.DdeDisconnect(conversation.Handle); + } + + // clear the conversation table. + _ConversationTable.Clear(); + } + + internal bool ProcessCallback(DdemlTransaction transaction) + { + // This is here to alias the transaction object with a shorter variable name. + DdemlTransaction t = transaction; + + switch (t.uType) + { + case Ddeml.XTYP_ADVREQ: + { + StringBuilder psz; + int length; + + // Get the topic name from the hsz1 string handle. + psz = new StringBuilder(Ddeml.MAX_STRING_SIZE); + length = Ddeml.DdeQueryString(_InstanceId, t.hsz1, psz, psz.Capacity, Ddeml.CP_WINANSI); + string topic = psz.ToString(); + + // Get the item name from the hsz2 string handle. + psz = new StringBuilder(Ddeml.MAX_STRING_SIZE); + length = Ddeml.DdeQueryString(_InstanceId, t.hsz2, psz, psz.Capacity, Ddeml.CP_WINANSI); + string item = psz.ToString(); + + // Create the advise request cache key. + string key = topic + "!" + item + ":" + t.uFmt.ToString(); + + // Get the data being advised if the cache does not contain it already. + if (!_AdviseRequestCache.ContainsKey(key)) + { + // Get the data from the subclass. + byte[] data = OnAdvise(topic, item, t.uFmt); + + // Add the data to the cache because it will be needed later. + _AdviseRequestCache.Add(key, data); + } + + // Get the data from the advise request cache. + byte[] cached = _AdviseRequestCache[key]; + + // Get the number of remaining transactions of this type for the same topic name, item name, and format tuple. + int remaining = t.dwData1.ToInt32(); + + // If this is the last transaction then free the data handle. + if (remaining == 0) + { + // TODO: Does the data handle really need to be freed here? + + // Remove the data from the cache because it is no longer needed. + _AdviseRequestCache.Remove(key); + } + + // Create and return the data handle representing the data being advised. + if (cached != null && cached.Length > 0) + { + t.dwRet = Ddeml.DdeCreateDataHandle(_InstanceId, cached, cached.Length, 0, t.hsz2, t.uFmt, 0); + return true; + } + + // This transaction could not be processed here. + return false; + } + case Ddeml.XTYP_ADVSTART: + { + // Get the item name from the hsz2 string handle. + StringBuilder psz = new StringBuilder(Ddeml.MAX_STRING_SIZE); + int length = Ddeml.DdeQueryString(_InstanceId, t.hsz2, psz, psz.Capacity, Ddeml.CP_WINANSI); + string item = psz.ToString(); + + // Get the Conversation from the conversation table. + DdemlConversation conversation = _ConversationTable[t.hConv]; + + // Get a value indicating whether an advise loop should be initiated from the subclass. + t.dwRet = OnStartAdvise(conversation, item, t.uFmt) ? new IntPtr(1) : IntPtr.Zero; + return true; + } + case Ddeml.XTYP_ADVSTOP: + { + // Get the item name from the hsz2 string handle. + StringBuilder psz = new StringBuilder(Ddeml.MAX_STRING_SIZE); + int length = Ddeml.DdeQueryString(_InstanceId, t.hsz2, psz, psz.Capacity, Ddeml.CP_WINANSI); + string item = psz.ToString(); + + // Get the Conversation from the conversation table. + DdemlConversation conversation = _ConversationTable[t.hConv]; + + // Inform the subclass that the advise loop has been terminated. + OnStopAdvise(conversation, item); + + // Return zero to indicate that there are no problems. + t.dwRet = IntPtr.Zero; + return true; + } + case Ddeml.XTYP_CONNECT: + { + // Get the topic name from the hsz1 string handle. + StringBuilder psz = new StringBuilder(Ddeml.MAX_STRING_SIZE); + int length = Ddeml.DdeQueryString(_InstanceId, t.hsz1, psz, psz.Capacity, Ddeml.CP_WINANSI); + string topic = psz.ToString(); + + // Get a value from the subclass indicating whether the connection should be allowed. + t.dwRet = OnBeforeConnect(topic) ? new IntPtr(1) : IntPtr.Zero; + return true; + } + case Ddeml.XTYP_CONNECT_CONFIRM: + { + // Get the topic name from the hsz1 string handle. + StringBuilder psz = new StringBuilder(Ddeml.MAX_STRING_SIZE); + int length = Ddeml.DdeQueryString(_InstanceId, t.hsz1, psz, psz.Capacity, Ddeml.CP_WINANSI); + string topic = psz.ToString(); + + // Create a Conversation object and add it to the conversation table. + _ConversationTable.Add(t.hConv, new DdemlConversation(t.hConv, _Service, topic)); + + // Inform the subclass that a conversation has been established. + OnAfterConnect(_ConversationTable[t.hConv]); + + // Return zero to indicate that there are no problems. + t.dwRet = IntPtr.Zero; + return true; + } + case Ddeml.XTYP_DISCONNECT: + { + // Get the Conversation from the conversation table. + DdemlConversation conversation = _ConversationTable[t.hConv]; + + // Remove the Conversation from the conversation table. + _ConversationTable.Remove(t.hConv); + + // Inform the subclass that the conversation has been disconnected. + OnDisconnect(conversation); + + // Return zero to indicate that there are no problems. + t.dwRet = IntPtr.Zero; + return true; + } + case Ddeml.XTYP_EXECUTE: + { + // Get the command from the data handle. + int length = Ddeml.DdeGetData(t.hData, null, 0, 0); + byte[] data = new byte[length]; + length = Ddeml.DdeGetData(t.hData, data, data.Length, 0); + string command = _Context.Encoding.GetString(data, 0, data.Length); + if (command[command.Length - 1] == '\0') + { + command = command.Substring(0, command.Length - 1); + } + + // Get the Conversation from the conversation table. + DdemlConversation conversation = _ConversationTable[t.hConv]; + + // Send the command to the subclass and get the result. + ExecuteResult result = OnExecute(conversation, command); + + // Return DDE_FACK if the subclass processed the command successfully. + if (result == ExecuteResult.Processed) + { + t.dwRet = new IntPtr(Ddeml.DDE_FACK); + return true; + } + + // Return CBR_BLOCK if the subclass needs time to process the command. + if (result == ExecuteResult.PauseConversation) + { + // Increment the conversation's waiting count. + conversation.IncrementWaiting(); + t.dwRet = new IntPtr(Ddeml.CBR_BLOCK); + return true; + } + + // Return DDE_FBUSY if the subclass is too busy. + if (result == ExecuteResult.TooBusy) + { + t.dwRet = new IntPtr(Ddeml.DDE_FBUSY); + return true; + } + + // Return DDE_FNOTPROCESSED if the subclass did not process the command. + t.dwRet = new IntPtr(Ddeml.DDE_FNOTPROCESSED); + return true; + } + case Ddeml.XTYP_POKE: + { + // Get the item name from the hsz2 string handle. + StringBuilder psz = new StringBuilder(Ddeml.MAX_STRING_SIZE); + int length = Ddeml.DdeQueryString(_InstanceId, t.hsz2, psz, psz.Capacity, Ddeml.CP_WINANSI); + string item = psz.ToString(); + + // Get the data from the data handle. + length = Ddeml.DdeGetData(t.hData, null, 0, 0); + byte[] data = new byte[length]; + length = Ddeml.DdeGetData(t.hData, data, data.Length, 0); + + // Get the Conversation from the conversation table. + DdemlConversation conversation = _ConversationTable[t.hConv]; + + // Send the data to the subclass and get the result. + PokeResult result = OnPoke(conversation, item, data, t.uFmt); + + // Return DDE_FACK if the subclass processed the data successfully. + if (result == PokeResult.Processed) + { + t.dwRet = new IntPtr(Ddeml.DDE_FACK); + return true; + } + + // Return CBR_BLOCK if the subclass needs time to process the data. + if (result == PokeResult.PauseConversation) + { + // Increment the conversation's waiting count. + conversation.IncrementWaiting(); + t.dwRet = new IntPtr(Ddeml.CBR_BLOCK); + return true; + } + + // Return DDE_FBUSY if the subclass is too busy. + if (result == PokeResult.TooBusy) + { + t.dwRet = new IntPtr(Ddeml.DDE_FBUSY); + return true; + } + + // Return DDE_FNOTPROCESSED if the subclass did not process the data. + t.dwRet = new IntPtr(Ddeml.DDE_FNOTPROCESSED); + return true; + } + case Ddeml.XTYP_REQUEST: + { + // Get the item name from the hsz2 string handle. + StringBuilder psz = new StringBuilder(Ddeml.MAX_STRING_SIZE); + int length = Ddeml.DdeQueryString(_InstanceId, t.hsz2, psz, psz.Capacity, Ddeml.CP_WINANSI); + string item = psz.ToString(); + + // Get the Conversation from the conversation table. + DdemlConversation conversation = _ConversationTable[t.hConv]; + + // Send the request to the subclass and get the result. + RequestResult result = OnRequest(conversation, item, t.uFmt); + + // Return a data handle if the subclass processed the request successfully. + if (result == RequestResult.Processed) + { + // Create and return the data handle for the data being requested. + if (result.Data != null) + { + t.dwRet = Ddeml.DdeCreateDataHandle(_InstanceId, result.Data, result.Data.Length, 0, t.hsz2, t.uFmt, 0); + } + return true; + } + + // Return CBR_BLOCK if the subclass needs time to process the request. + if (result == RequestResult.PauseConversation) + { + conversation.IncrementWaiting(); + t.dwRet = new IntPtr(Ddeml.CBR_BLOCK); + return true; + } + + // Return DDE_FNOTPROCESSED if the subclass did not process the command. + t.dwRet = new IntPtr(Ddeml.DDE_FNOTPROCESSED); + return true; + } + } + + // This transaction could not be processed here. + return false; + } + + protected virtual bool OnStartAdvise(DdemlConversation conversation, string item, int format) + { + return true; + } + + protected virtual void OnStopAdvise(DdemlConversation conversation, string item) + { + } + + protected virtual bool OnBeforeConnect(string topic) + { + return true; + } + + protected virtual void OnAfterConnect(DdemlConversation conversation) + { + } + + protected virtual void OnDisconnect(DdemlConversation conversation) + { + } + + protected virtual ExecuteResult OnExecute(DdemlConversation conversation, string command) + { + return ExecuteResult.NotProcessed; + } + + protected virtual PokeResult OnPoke(DdemlConversation conversation, string item, byte[] data, int format) + { + return PokeResult.NotProcessed; + } + + protected virtual RequestResult OnRequest(DdemlConversation conversation, string item, int format) + { + return RequestResult.NotProcessed; + } + + protected virtual byte[] OnAdvise(string topic, string item, int format) + { + return null; + } + + public struct ExecuteResult + { + public static readonly ExecuteResult Processed = new ExecuteResult(Ddeml.DDE_FACK); + public static readonly ExecuteResult NotProcessed = new ExecuteResult(Ddeml.DDE_FNOTPROCESSED); + public static readonly ExecuteResult TooBusy = new ExecuteResult(Ddeml.DDE_FBUSY); + public static readonly ExecuteResult PauseConversation = new ExecuteResult(Ddeml.CBR_BLOCK); + + private int _Result; + + private ExecuteResult(int result) + { + _Result = result; + } + + public override bool Equals(object o) + { + if (o is ExecuteResult) + { + ExecuteResult r = (ExecuteResult)o; + return _Result == r._Result; + } + return false; + } + + public override int GetHashCode() + { + return _Result.GetHashCode(); + } + + public static bool operator ==(ExecuteResult lhs, ExecuteResult rhs) + { + return lhs._Result == rhs._Result; + } + + public static bool operator !=(ExecuteResult lhs, ExecuteResult rhs) + { + return lhs._Result != rhs._Result; + } + + } // struct + + public struct PokeResult + { + public static readonly PokeResult Processed = new PokeResult(Ddeml.DDE_FACK); + public static readonly PokeResult NotProcessed = new PokeResult(Ddeml.DDE_FNOTPROCESSED); + public static readonly PokeResult TooBusy = new PokeResult(Ddeml.DDE_FBUSY); + public static readonly PokeResult PauseConversation = new PokeResult(Ddeml.CBR_BLOCK); + + private int _Result; + + private PokeResult(int result) + { + _Result = result; + } + + public override bool Equals(object o) + { + if (o is PokeResult) + { + PokeResult r = (PokeResult)o; + return _Result == r._Result; + } + return false; + } + + public override int GetHashCode() + { + return _Result.GetHashCode(); + } + + public static bool operator ==(PokeResult lhs, PokeResult rhs) + { + return lhs._Result == rhs._Result; + } + + public static bool operator !=(PokeResult lhs, PokeResult rhs) + { + return lhs._Result != rhs._Result; + } + + } // struct + + public struct RequestResult + { + internal static readonly RequestResult Processed = new RequestResult(Ddeml.DDE_FACK); + public static readonly RequestResult NotProcessed = new RequestResult(Ddeml.DDE_FNOTPROCESSED); + public static readonly RequestResult PauseConversation = new RequestResult(Ddeml.CBR_BLOCK); + + private int _Result; + private byte[] _Data; + + private RequestResult(int result) + { + _Result = result; + _Data = null; + } + + public RequestResult(byte[] data) + { + _Result = data != null ? Ddeml.DDE_FACK : Ddeml.DDE_FNOTPROCESSED; + _Data = data; + } + + public byte[] Data + { + get { return _Data; } + set { _Data = value; } + } + + public override bool Equals(object o) + { + if (o is RequestResult) + { + RequestResult r = (RequestResult)o; + return _Result == r._Result; + } + return false; + } + + public override int GetHashCode() + { + return _Result.GetHashCode(); + } + + public static bool operator ==(RequestResult lhs, RequestResult rhs) + { + return lhs._Result == rhs._Result; + } + + public static bool operator !=(RequestResult lhs, RequestResult rhs) + { + return lhs._Result != rhs._Result; + } + + } // struct + + /// + /// This class is needed to dispose of DDEML resources correctly since the DDEML is thread specific. + /// + private sealed class RegistrationManager : IMessageFilter + { + private const int WM_APP = unchecked((int)0x8000); + + private static readonly string DataSlot = typeof(RegistrationManager).FullName; + + private static IDictionary _Table = new Dictionary(); + + [System.Runtime.InteropServices.DllImport("user32.dll")] + private static extern void PostThreadMessage(int idThread, int Msg, IntPtr wParam, IntPtr lParam); + + public static IntPtr Register(int instanceId, string service) + { + lock (_Table) + { + // Create a string handle for the service name. + IntPtr serviceHandle = Ddeml.DdeCreateStringHandle(instanceId, service, Ddeml.CP_WINANSI); + + // Register the service name. + IntPtr result = Ddeml.DdeNameService(instanceId, serviceHandle, IntPtr.Zero, Ddeml.DNS_REGISTER); + + if (result != IntPtr.Zero) + { + // Make sure this thread has an IMessageFilter on it. + LocalDataStoreSlot slot = Thread.GetNamedDataSlot(DataSlot); + if (Thread.GetData(slot) == null) + { + RegistrationManager filter = new RegistrationManager(); + Application.AddMessageFilter(filter); + Thread.SetData(slot, filter); + } + + // Add an entry to the table that maps the service handle to the current thread. + _Table.Add(serviceHandle, Ddeml.GetCurrentThreadId()); + } + else + { + // Free the string handle created earlier. + Ddeml.DdeFreeStringHandle(instanceId, serviceHandle); + serviceHandle = IntPtr.Zero; + } + + return serviceHandle; + } + } + + public static void Unregister(int instanceId, IntPtr serviceHandle) + { + // This method could be called by the GC finalizer thread. If it is then a direct call to the DDEML will fail since the DDEML is + // thread specific. A message will be posted to the DDEML thread instead. + lock (_Table) + { + if (_Table.ContainsKey(serviceHandle)) + { + // Determine if the current thread matches what is in the table. + int threadId = (int)_Table[serviceHandle]; + if (threadId == Ddeml.GetCurrentThreadId()) + { + // Unregister the service name. + Ddeml.DdeNameService(instanceId, serviceHandle, IntPtr.Zero, Ddeml.DNS_UNREGISTER); + + // Free the service string handle. + Ddeml.DdeFreeStringHandle(instanceId, serviceHandle); + } + else + { + // Post a message to the thread that needs to execute the Ddeml.DdeXXX methods. + PostThreadMessage(threadId, WM_APP + 3, new IntPtr(instanceId), serviceHandle); + } + + // Remove the service handle from the table because it is no longer in use. + _Table.Remove(serviceHandle); + } + } + } + + bool IMessageFilter.PreFilterMessage(ref Message m) + { + if (m.Msg == WM_APP + 3) + { + // Unregister the service name. + Ddeml.DdeNameService(m.WParam.ToInt32(), m.LParam, IntPtr.Zero, Ddeml.DNS_UNREGISTER); + + // Free the service string handle. + Ddeml.DdeFreeStringHandle(m.WParam.ToInt32(), m.LParam); + } + return false; + } + + } // class + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Internal/Utility/WeakReferenceDictionary.cs b/NDDE/NDde/Source/NDde/Internal/Utility/WeakReferenceDictionary.cs new file mode 100644 index 0000000..715fd7e --- /dev/null +++ b/NDDE/NDde/Source/NDde/Internal/Utility/WeakReferenceDictionary.cs @@ -0,0 +1,273 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde +{ + using System; + using System.Collections.Generic; + + internal sealed class WeakReferenceDictionary : IDictionary where TValue : class + { + private IDictionary _Storage = new Dictionary(); + + public WeakReferenceDictionary() + { + } + + public void Add(TKey key, TValue value) + { + Purge(); + _Storage.Add(key, new WeakReference(value)); + } + + public bool ContainsKey(TKey key) + { + return _Storage.ContainsKey(key); + } + + public ICollection Keys + { + get { return _Storage.Keys; } + } + + public bool Remove(TKey key) + { + return _Storage.Remove(key); + } + + public bool TryGetValue(TKey key, out TValue value) + { + value = null; + if (_Storage.ContainsKey(key)) + { + value = _Storage[key].Target as TValue; + if (value != null) + { + return true; + } + } + return false; + } + + public ICollection Values + { + get { return new MyValueCollection(this); } + } + + public TValue this[TKey key] + { + get + { + if (_Storage.ContainsKey(key)) + { + TValue value = _Storage[key].Target as TValue; + if (value != null) + { + return value; + } + } + return null; + } + set + { + if (value != null) + { + Purge(); + _Storage[key] = new WeakReference(value); + } + else + { + _Storage.Remove(key); + } + } + } + + public void Add(KeyValuePair item) + { + Purge(); + _Storage.Add(item.Key, new WeakReference(item.Value)); + } + + public void Clear() + { + _Storage.Clear(); + } + + public bool Contains(KeyValuePair item) + { + if (_Storage.ContainsKey(item.Key)) + { + TValue value = _Storage[item.Key].Target as TValue; + if (value != null) + { + return true; + } + } + return false; + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + int index = 0; + foreach (KeyValuePair kvp in this) + { + array[arrayIndex + index] = kvp; + index++; + } + } + + public int Count + { + get { return _Storage.Count; } + } + + public bool IsReadOnly + { + get { return _Storage.IsReadOnly; } + } + + public bool Remove(KeyValuePair item) + { + return false; + } + + public IEnumerator> GetEnumerator() + { + foreach (KeyValuePair kvp in _Storage) + { + TValue value = kvp.Value.Target as TValue; + if (value != null) + { + yield return new KeyValuePair(kvp.Key, value); + } + } + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + private void Purge() + { + List dead = new List(); + foreach (KeyValuePair kvp in _Storage) + { + if (!kvp.Value.IsAlive) + { + dead.Add(kvp.Key); + } + } + foreach (TKey key in dead) + { + _Storage.Remove(key); + } + } + + private sealed class MyValueCollection : ICollection + { + private WeakReferenceDictionary _Parent = null; + + public MyValueCollection(WeakReferenceDictionary parent) + { + _Parent = parent; + } + + public void Add(TValue item) + { + throw new Exception("The method or operation is not implemented."); + } + + public void Clear() + { + throw new Exception("The method or operation is not implemented."); + } + + public bool Contains(TValue item) + { + foreach (TValue value in this) + { + if (value == item) + { + return true; + } + } + return false; + } + + public void CopyTo(TValue[] array, int arrayIndex) + { + int index = 0; + foreach (TValue value in this) + { + array[arrayIndex + index] = value; + index++; + } + } + + public int Count + { + get { return _Parent._Storage.Values.Count; } + } + + public bool IsReadOnly + { + get { return true; } + } + + public bool Remove(TValue item) + { + throw new Exception("The method or operation is not implemented."); + } + + public IEnumerator GetEnumerator() + { + foreach (WeakReference wr in _Parent._Storage.Values) + { + TValue value = wr.Target as TValue; + if (value != null) + { + yield return value; + } + } + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + } // class + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/NDde.csproj b/NDDE/NDde/Source/NDde/NDde.csproj new file mode 100644 index 0000000..31abf50 --- /dev/null +++ b/NDDE/NDde/Source/NDde/NDde.csproj @@ -0,0 +1,172 @@ + + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {D77772F9-3D3D-40BA-B95F-05C45878078F} + Library + Properties + NDde + NDde + v4.0 + + + + + 2.0 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + ..\..\..\..\packages\Newtonsoft.Json.12.0.1\lib\net40\Newtonsoft.Json.dll + True + + + + + ..\..\..\..\packages\System.Data.SQLite.Core.1.0.110.0\lib\net40\System.Data.SQLite.dll + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + Resources.resx + + + + + + Designer + ResXFileCodeGenerator + Resources.Designer.cs + + + + + + + + False + Microsoft .NET Framework 4 %28x86 und x64%29 + true + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + false + + + False + Windows Installer 3.1 + true + + + + + + + + + + + + + + Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}". + + + + \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Properties/AssemblyInfo.cs b/NDDE/NDde/Source/NDde/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..a5f0c70 --- /dev/null +++ b/NDDE/NDde/Source/NDde/Properties/AssemblyInfo.cs @@ -0,0 +1,75 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory it’s based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with “Restricted Rights” as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +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("NDde")] +[assembly: AssemblyDescription("Dynamic Data Exchange Library")] +#if DEBUG +[assembly: AssemblyConfiguration("Debug")] +#else +[assembly: AssemblyConfiguration("Release")] +#endif +[assembly: AssemblyCompany("Brian Gideon")] +[assembly: AssemblyProduct("NDde")] +[assembly: AssemblyCopyright("Copyright © 2005-2006 by Brian Gideon (briangideon@yahoo.com)")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("9e2a31da-f0a0-4094-a6cd-85ceb9ac297a")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("2.01.0563.0")] +[assembly: AssemblyFileVersion("2.01.0563.0")] // 1/17/2005 is the original build date + +[assembly: CLSCompliant(true)] + diff --git a/NDDE/NDde/Source/NDde/Properties/Resources.Designer.cs b/NDDE/NDde/Source/NDde/Properties/Resources.Designer.cs new file mode 100644 index 0000000..0d65807 --- /dev/null +++ b/NDDE/NDde/Source/NDde/Properties/Resources.Designer.cs @@ -0,0 +1,360 @@ +//------------------------------------------------------------------------------ +// +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.18444 +// +// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn +// der Code erneut generiert wird. +// +//------------------------------------------------------------------------------ + +namespace NDde.Properties { + using System; + + + /// + /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw. + /// + // Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert + // -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert. + // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen + // mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("NDde.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle + /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die The server failed to advise "${service}|${topic}!${item}". ähnelt. + /// + internal static string AdviseFailedMessage { + get { + return ResourceManager.GetString("AdviseFailedMessage", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die An advise loop for "${service}|${topic}!${item}" already exists. ähnelt. + /// + internal static string AlreadyBeingAdvisedMessage { + get { + return ResourceManager.GetString("AlreadyBeingAdvisedMessage", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die The client is already connected. ähnelt. + /// + internal static string AlreadyConnectedMessage { + get { + return ResourceManager.GetString("AlreadyConnectedMessage", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die The context is already intialized. ähnelt. + /// + internal static string AlreadyInitializedMessage { + get { + return ResourceManager.GetString("AlreadyInitializedMessage", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die The specified conversation is already paused. ähnelt. + /// + internal static string AlreadyPausedMessage { + get { + return ResourceManager.GetString("AlreadyPausedMessage", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die The service is already registered. ähnelt. + /// + internal static string AlreadyRegisteredMessage { + get { + return ResourceManager.GetString("AlreadyRegisteredMessage", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die The IAsyncResult must have been returned by a call to ${method}. ähnelt. + /// + internal static string AsyncResultParameterInvalidMessage { + get { + return ResourceManager.GetString("AsyncResultParameterInvalidMessage", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die The client failed to pause the conversation. ähnelt. + /// + internal static string ClientPauseFailedMessage { + get { + return ResourceManager.GetString("ClientPauseFailedMessage", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die The client failed to resume the conversation. ähnelt. + /// + internal static string ClientResumeFailedMessage { + get { + return ResourceManager.GetString("ClientResumeFailedMessage", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die The client failed to connect to "${service}|${topic}". Make sure the server application is running and that it supports the specified service name and topic name pair. ähnelt. + /// + internal static string ConnectFailedMessage { + get { + return ResourceManager.GetString("ConnectFailedMessage", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die The client failed to execute "${command}". ähnelt. + /// + internal static string ExecuteFailedMessage { + get { + return ResourceManager.GetString("ExecuteFailedMessage", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die The transaction filter has already been added. ähnelt. + /// + internal static string FilterAlreadyAddedMessage { + get { + return ResourceManager.GetString("FilterAlreadyAddedMessage", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die The transaction filter has not been added. ähnelt. + /// + internal static string FilterNotAddedMessage { + get { + return ResourceManager.GetString("FilterNotAddedMessage", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die The context failed to initialize. ähnelt. + /// + internal static string InitializeFailedMessage { + get { + return ResourceManager.GetString("InitializeFailedMessage", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die The context timed out attempting to marshal the operation. ähnelt. + /// + internal static string MarshalTimeoutMessage { + get { + return ResourceManager.GetString("MarshalTimeoutMessage", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die The context is not hosted on a thread with a message loop. ähnelt. + /// + internal static string NoMessageLoopMessage { + get { + return ResourceManager.GetString("NoMessageLoopMessage", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die An advise loop for "${service}|${topic}!${item}" does not exist. ähnelt. + /// + internal static string NotBeingAdvisedMessage { + get { + return ResourceManager.GetString("NotBeingAdvisedMessage", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die The client is not connected. ähnelt. + /// + internal static string NotConnectedMessage { + get { + return ResourceManager.GetString("NotConnectedMessage", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die The context is not initialized. ähnelt. + /// + internal static string NotInitializedMessage { + get { + return ResourceManager.GetString("NotInitializedMessage", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die The specified conversation is not paused. ähnelt. + /// + internal static string NotPausedMessage { + get { + return ResourceManager.GetString("NotPausedMessage", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die The service is not registered. ähnelt. + /// + internal static string NotRegisteredMessage { + get { + return ResourceManager.GetString("NotRegisteredMessage", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die The client failed to poke "${service}|${topic}!${item}". ähnelt. + /// + internal static string PokeFailedMessage { + get { + return ResourceManager.GetString("PokeFailedMessage", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die The server failed to register "${service}". ähnelt. + /// + internal static string RegisterFailedMessage { + get { + return ResourceManager.GetString("RegisterFailedMessage", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die The client failed to request "${service}|${topic}!${item}". ähnelt. + /// + internal static string RequestFailedMessage { + get { + return ResourceManager.GetString("RequestFailedMessage", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die The server failed to pause all conversations. ähnelt. + /// + internal static string ServerPauseAllFailedMessage { + get { + return ResourceManager.GetString("ServerPauseAllFailedMessage", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die The server failed to pause the specified conversation. ähnelt. + /// + internal static string ServerPauseFailedMessage { + get { + return ResourceManager.GetString("ServerPauseFailedMessage", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die The server failed to resume all conversations. ähnelt. + /// + internal static string ServerResumeAllFailedMessage { + get { + return ResourceManager.GetString("ServerResumeAllFailedMessage", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die The server failed to resume the specified conversation. ähnelt. + /// + internal static string ServerResumeFailedMessage { + get { + return ResourceManager.GetString("ServerResumeFailedMessage", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die The client failed to initiate an advise loop for "${service}|${topic}!${item}". ähnelt. + /// + internal static string StartAdviseFailedMessage { + get { + return ResourceManager.GetString("StartAdviseFailedMessage", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die The client failed to terminate the advise loop for "${service}|${topic}!${item}". ähnelt. + /// + internal static string StopAdviseFailedMessage { + get { + return ResourceManager.GetString("StopAdviseFailedMessage", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die The parameter must be <= 255 characters. ähnelt. + /// + internal static string StringParameterInvalidMessage { + get { + return ResourceManager.GetString("StringParameterInvalidMessage", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die The parameter must be > 0. ähnelt. + /// + internal static string TimeoutParameterInvalidMessage { + get { + return ResourceManager.GetString("TimeoutParameterInvalidMessage", resourceCulture); + } + } + + /// + /// Sucht eine lokalisierte Zeichenfolge, die An unknown error occurred. ähnelt. + /// + internal static string UnknownErrorMessage { + get { + return ResourceManager.GetString("UnknownErrorMessage", resourceCulture); + } + } + } +} diff --git a/NDDE/NDde/Source/NDde/Properties/Resources.resx b/NDDE/NDde/Source/NDde/Properties/Resources.resx new file mode 100644 index 0000000..32053a1 --- /dev/null +++ b/NDDE/NDde/Source/NDde/Properties/Resources.resx @@ -0,0 +1,219 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The parameter must be <= 255 characters. + + + The client failed to connect to "${service}|${topic}". Make sure the server application is running and that it supports the specified service name and topic name pair. + + + The client is not connected. + + + The parameter must be > 0. + + + The client failed to execute "${command}". + + + The client failed to poke "${service}|${topic}!${item}". + + + The client failed to request "${service}|${topic}!${item}". + + + The IAsyncResult must have been returned by a call to ${method}. + + + An advise loop for "${service}|${topic}!${item}" already exists. + + + The client failed to initiate an advise loop for "${service}|${topic}!${item}". + + + An advise loop for "${service}|${topic}!${item}" does not exist. + + + The client failed to terminate the advise loop for "${service}|${topic}!${item}". + + + The context failed to initialize. + + + The server failed to register "${service}". + + + The server failed to advise "${service}|${topic}!${item}". + + + The client failed to pause the conversation. + + + The client failed to resume the conversation. + + + The server failed to pause the specified conversation. + + + The server failed to resume the specified conversation. + + + The server failed to pause all conversations. + + + The server failed to resume all conversations. + + + The specified conversation is already paused. + + + The specified conversation is not paused. + + + The client is already connected. + + + The service is not registered. + + + The service is already registered. + + + The transaction filter has not been added. + + + The transaction filter has already been added. + + + The context is not initialized. + + + The context is not hosted on a thread with a message loop. + + + The context is already intialized. + + + The context timed out attempting to marshal the operation. + + + An unknown error occurred. + + \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Public/Advanced/DdeContext.cs b/NDDE/NDde/Source/NDde/Public/Advanced/DdeContext.cs new file mode 100644 index 0000000..ecda0da --- /dev/null +++ b/NDDE/NDde/Source/NDde/Public/Advanced/DdeContext.cs @@ -0,0 +1,813 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Advanced +{ + using System; + using System.Collections.Generic; + using System.ComponentModel; + using System.Reflection; + using System.Runtime.InteropServices; + using System.Text; + using System.Threading; + using System.Windows.Forms; + using NDde.Foundation; + using NDde.Foundation.Advanced; + using NDde.Properties; + + /// + /// This provides an execution context for DdeClient and DdeServer. + /// + /// + /// + /// + /// This class provides a context for DDE activity. All DdeClient and DdeServer objects must be associated with an instance of + /// this class. If one is not specified in their constructors then a default instance of this class is used. This class must be initialized + /// before it can begin sending and receiving DDE messages. This happens automatically upon its first use by a DdeClient or + /// DdeServer. An application can call Initialize to make the initialization process occur immediately. This is useful when a + /// calling application expects this class to raise the Register and Unregister events or invoke the + /// ITransactionFilter.PreFilterTransaction method before being used by a DdeClient or DdeServer. + /// + /// + /// Since forms and controls implement ISynchronizeInvoke they can be used as the synchronizing object for this class. When an instance + /// of this class is created to use a form or control as the synchronizing object it will use the UI thread for execution. This is the + /// preferred way of creating an instance of this class when used in a windows application since it avoids multithreaded synchronization issues + /// and cross thread marshaling. When an instance of this class is created without specifying a synchronizing object it will create and manage + /// its own thread for execution. This is convenient if you wish to use this library in a console or service application, but with the added + /// cost of cross thread marshaling and the potential for deadlocking application threads. + /// + /// + /// Events are invoked on the thread hosting the DdeContext. All operations must be marshaled onto the thread hosting the + /// DdeContext. Method calls will block until that thread becomes available. An exception will be generated if the thread does not + /// become available in a timely manner. + /// + /// + /// + public sealed class DdeContext : IDisposable, ISynchronizeInvoke + { + private static DdeContext _Instance = null; + private static Object _InstanceLock = new Object(); + + private static WeakReferenceDictionary _Instances = new WeakReferenceDictionary(); + + internal static DdeContext GetDefault() + { + lock (_InstanceLock) + { + if (_Instance == null) + { + _Instance = new DdeContext(); + } + return _Instance; + } + } + + internal static DdeContext GetDefault(ISynchronizeInvoke synchronizingObject) + { + if (synchronizingObject != null) + { + lock (_Instances) + { + DdeContext context = _Instances[synchronizingObject]; + if (context == null) + { + if (synchronizingObject is DdeContext) + { + context = synchronizingObject as DdeContext; + } + else + { + context = new DdeContext(synchronizingObject); + } + _Instances.Add(synchronizingObject, context); + } + return context; + } + } + return GetDefault(); + } + + private Object _LockObject = new Object(); + + private EventHandler _RegisterEvent = null; + private EventHandler _UnregisterEvent = null; + + private DdemlContext _DdemlObject = null; // This has lazy initialization through a property. + private ISynchronizeInvoke _Synchronizer = null; + + private int _InstanceId = 0; // This is a cached DdemlContext property. + private bool _IsInitialized = false; // This is a cached DdemlContext property. + private Encoding _Encoding = Encoding.ASCII; // This is a cached DdemlContext property. + + /// + /// This is raised when a service name has been registered by a server using the DDEML. + /// + /// + /// This event will not be raised by servers that do not use the DDEML. + /// + public event EventHandler Register + { + add + { + lock (_LockObject) + { + _RegisterEvent += value; + } + } + remove + { + lock (_LockObject) + { + _RegisterEvent -= value; + } + } + } + + /// + /// This is raised when a service name has been unregistered by a server using the DDEML. + /// + /// + /// This event will not be raised by servers that do not use the DDEML. + /// + public event EventHandler Unregister + { + add + { + lock (_LockObject) + { + _UnregisterEvent += value; + } + } + remove + { + lock (_LockObject) + { + _UnregisterEvent -= value; + } + } + } + + /// + /// + /// + /// + /// + /// This initializes a new instance of the DdeContext class that uses a dedicated thread for execution. + /// + /// + /// This constructor is used when you want the context to create and manage its own thread for DDE message pumping. + /// + public DdeContext() + { + } + + /// + /// This initializes a new instance of the DdeContext class that uses the specified synchronizing object for execution. + /// + /// + /// The synchronizing object to use for execution. + /// + /// + /// This is thrown when synchronizer is a null reference. + /// + /// + /// This constructor is used when you want the context to use the specified synchronizing object for DDE message pumping. Since forms and + /// controls implement ISynchronizeInvoke they can be used as the synchronizing object. In that case the windows application UI + /// thread that is hosting the form or control is used. + /// + public DdeContext(ISynchronizeInvoke synchronizingObject) + { + Synchronizer = synchronizingObject; + } + + /// + /// This releases all resources held by this instance. + /// + public void Dispose() + { + Dispose(true); + } + + private void Dispose(bool disposing) + { + if (disposing) + { + ThreadStart method = delegate() + { + DdemlObject.Dispose(); + }; + + try + { + Invoke(method); + + // Dispose the synchronizer if it was created internally. + DdeThread synchronizer = Synchronizer as DdeThread; + if (synchronizer != null) + { + synchronizer.Dispose(); + } + } + catch + { + // Swallow any exception that occurs. + } + } + } + + private ISynchronizeInvoke Synchronizer + { + get + { + lock (_LockObject) + { + if (_Synchronizer == null) + { + _Synchronizer = new DdeThread(); + } + return _Synchronizer; + } + } + set + { + lock (_LockObject) + { + _Synchronizer = value; + } + } + } + + /// + /// + /// + internal DdemlContext DdemlObject + { + get + { + lock (_LockObject) + { + if (_DdemlObject == null) + { + _DdemlObject = new DdemlContext(); + _DdemlObject.Register += this.OnRegister; + _DdemlObject.Unregister += this.OnUnregister; + _DdemlObject.StateChange += this.OnStateChange; + } + return _DdemlObject; + } + } + } + + /// + /// This gets the DDEML instance identifier. + /// + /// + /// + /// This can be used in any DDEML function requiring an instance identifier. + /// + /// + /// + /// Incorrect usage of the DDEML can cause this library to function incorrectly and can lead to resource leaks. + /// + /// + /// + public int InstanceId + { + get + { + lock (_LockObject) + { + return _InstanceId; + } + } + } + + /// + /// This gets a bool indicating whether the context is initialized. + /// + public bool IsInitialized + { + get + { + lock (_LockObject) + { + return _IsInitialized; + } + } + } + + /// + /// This gets or sets the default encoding that is used. + /// + public Encoding Encoding + { + get + { + lock (_LockObject) + { + return _Encoding; + } + } + set + { + ThreadStart method = delegate() + { + DdemlObject.Encoding = value; + _Encoding = value; + }; + + Invoke(method); + } + } + + /// + /// This gets a bool indicating whether the caller must use Invoke. + /// + public bool InvokeRequired + { + get { return Synchronizer.InvokeRequired; } + } + + /// + /// This initializes the context. + /// + /// + /// This is thrown when the context is already initialized. + /// + /// + /// This is thrown when the context could not be initialized. + /// + /// + /// + /// This class must be initialized before it can begin sending and receiving DDE messages. This happens automatically upon its first use by + /// a DdeClient or DdeServer. An application can call Initialize to make the initialization process occur immediately. + /// This is useful when a calling application expects this class to raise the Register and Unregister events or invoke the + /// ITransactionFilter.PreFilterTransaction method before being used by a DdeClient or DdeServer. + /// + /// + /// If you attempt to use a synchronizer that is not hosted on a thread running a windows message loop an exception will be thrown. + /// + /// + /// Explicitly calling this method will allow added ITransactionFilter objects to begin intercepting the DDEML callback function. + /// + /// + public void Initialize() + { + ThreadStart method = delegate() + { + DdemlObject.Initialize(); + _InstanceId = DdemlObject.InstanceId; + _IsInitialized = DdemlObject.IsInitialized; + }; + + try + { + Invoke(method); + } + catch (DdemlException e) + { + throw new DdeException(e); + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + } + + /// + /// This adds a transaction filter to monitor DDE transactions. + /// + /// + /// The implementation of ITransactionFilter that you want to add. + /// + /// + /// This is thrown when filter is a null reference. + /// + /// + /// This is thrown when the filter was already added. + /// + /// + /// + /// Transaction filters can be used to intercept the DDEML callback. + /// + /// + /// + /// Incorrect usage of the DDEML can cause this library to function incorrectly and can lead to resource leaks. + /// + /// + /// + public void AddTransactionFilter(IDdeTransactionFilter filter) + { + ThreadStart method = delegate() + { + IDdemlTransactionFilter tf = filter == null ? null : new DdemlTransactionFilter(filter); + DdemlObject.AddTransactionFilter(tf); + }; + + try + { + Invoke(method); + } + catch (ArgumentException e) + { + throw e; + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + } + + /// + /// This removes a transaction filter and stops it from monitoring DDE transactions. + /// + /// + /// The implementation of ITransactionFilter that you want to remove. + /// + /// + /// This is thrown when filter is a null reference. + /// + /// + /// This is thrown when the filter was not previously added. + /// + /// + /// + /// Transaction filters can be used to intercept the DDEML callback. + /// + /// + /// + /// Incorrect usage of the DDEML can cause this library to function incorrectly and can lead to resource leaks. + /// + /// + /// + public void RemoveTransactionFilter(IDdeTransactionFilter filter) + { + ThreadStart method = delegate() + { + IDdemlTransactionFilter tf = filter == null ? null : new DdemlTransactionFilter(filter); + DdemlObject.RemoveTransactionFilter(tf); + }; + + try + { + Invoke(method); + } + catch (ArgumentException e) + { + throw e; + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + } + + /// + /// This executes a ThreadStart delegate on the thread hosting this object. + /// + /// + /// The delegate to execute. + /// + internal void Invoke(ThreadStart method) + { + Invoke(method, null); + } + + /// + /// This executes a delegate on the thread hosting this object. + /// + /// + /// The delegate to execute. + /// + /// + /// The arguments to pass to the delegate. + /// + /// + /// The object returned by the delegate. + /// + public object Invoke(Delegate method, object[] args) + { + return Synchronizer.Invoke(method, args); + } + + /// + /// This begins an asynchronous operation to execute a delegate on the thread hosting this object. + /// + /// + /// The delegate to execute. + /// + /// + /// The arguments to pass to the delegate. + /// + /// + /// An IAsyncResult object for this operation. + /// + public IAsyncResult BeginInvoke(Delegate method, object[] args) + { + return Synchronizer.BeginInvoke(method, args); + } + + /// + /// This returns the object that the delegate returned in the operation. + /// + /// + /// The IAsyncResult object returned by a call to BeginInvoke. + /// + /// + /// The object returned by the delegate. + /// + public object EndInvoke(IAsyncResult asyncResult) + { + return Synchronizer.EndInvoke(asyncResult); + } + + private void OnRegister(object sender, DdemlRegistrationEventArgs internalArgs) + { + EventHandler copy; + + // To make this thread-safe we need to hold a local copy of the reference to the invocation list. This works because delegates are + // immutable. + lock (_LockObject) + { + copy = _RegisterEvent; + } + + if (copy != null) + { + copy(this, new DdeRegistrationEventArgs(internalArgs)); + } + } + + private void OnUnregister(object sender, DdemlRegistrationEventArgs internalArgs) + { + EventHandler copy; + + // To make this thread-safe we need to hold a local copy of the reference to the invocation list. This works because delegates are + // immutable. + lock (_LockObject) + { + copy = _UnregisterEvent; + } + + if (copy != null) + { + copy(this, new DdeRegistrationEventArgs(internalArgs)); + } + } + + private void OnStateChange(object sender, EventArgs args) + { + lock (_LockObject) + { + _InstanceId = _DdemlObject.InstanceId; + _IsInitialized = _DdemlObject.IsInitialized; + } + } + + /// + private sealed class DdemlTransactionFilter : IDdemlTransactionFilter + { + private IDdeTransactionFilter _OuterFilter = null; + + public DdemlTransactionFilter(IDdeTransactionFilter filter) + { + _OuterFilter = filter; + } + + public bool PreFilterTransaction(DdemlTransaction t) + { + return _OuterFilter.PreFilterTransaction(new DdeTransaction(t)); + } + + public override bool Equals(object obj) + { + DdemlTransactionFilter target = obj as DdemlTransactionFilter; + if (target != null) + { + return _OuterFilter.Equals(target._OuterFilter); + } + return false; + } + + public override int GetHashCode() + { + return _OuterFilter.GetHashCode(); + } + + } // class + + /// + private sealed class DdeThread : IDisposable, ISynchronizeInvoke + { + [DllImport("user32.dll")] + private static extern void PostThreadMessage(int idThread, int Msg, IntPtr wParam, IntPtr lParam); + + [DllImport("kernel32.dll")] + private static extern int GetCurrentThreadId(); + + private Object _LockObject = new Object(); + + private int _ThreadId = 0; + private Thread _Thread = null; + private Form _Form = null; + + private ManualResetEvent _Initialized = new ManualResetEvent(false); + + public DdeThread() + { + _Form = new HiddenForm(); + _Form.Load += this.Form_Load; + _Thread = new Thread(this.Run); + _Thread.SetApartmentState(ApartmentState.STA); + _Thread.Name = "DdeMessagePump"; + _Thread.IsBackground = true; + } + + public void Dispose() + { + lock (_LockObject) + { + if ((_Thread.ThreadState & ThreadState.Unstarted) != 0) + { + _Thread.Start(); + _Initialized.WaitOne(); + } + } + + if (InvokeRequired) + { + ThreadStart method = delegate() + { + _Form.Dispose(); + }; + Invoke(method, null); + } + else + { + _Form.Dispose(); + } + } + + public bool InvokeRequired + { + get + { + lock (_LockObject) + { + return _ThreadId != GetCurrentThreadId(); + } + } + } + + public object Invoke(Delegate method, object[] args) + { + lock (_LockObject) + { + if ((_Thread.ThreadState & ThreadState.Unstarted) != 0) + { + _Thread.Start(); + _Initialized.WaitOne(); + } + } + + if (InvokeRequired) + { + try + { + return _Form.Invoke(method, args); + } + catch (InvalidOperationException e) + { + if (!_Form.IsHandleCreated) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + throw; + } + } + else + { + return method.DynamicInvoke(args); + } + } + + public IAsyncResult BeginInvoke(Delegate method, object[] args) + { + lock (_LockObject) + { + if ((_Thread.ThreadState & ThreadState.Unstarted) != 0) + { + _Thread.Start(); + _Initialized.WaitOne(); + } + } + + try + { + return _Form.BeginInvoke(method, args); + } + catch (InvalidOperationException e) + { + if (!_Form.IsHandleCreated) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + throw; + } + } + + public object EndInvoke(IAsyncResult asyncResult) + { + return _Form.EndInvoke(asyncResult); + } + + private void Run() + { + Thread.VolatileWrite(ref _ThreadId, GetCurrentThreadId()); + Application.ThreadException += this.Application_ThreadException; + Application.Run(_Form); + } + + private void Form_Load(object source, EventArgs e) + { + _Initialized.Set(); + } + + private void Application_ThreadException(object sender, ThreadExceptionEventArgs e) + { + // This is here to prevent unhandled exceptions from appearing in a message box. + } + + /// + private sealed class HiddenForm : Form + { + [DllImport("user32.dll")] + private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hwndNewParent); + + public HiddenForm() + { + this.Load += this.HiddenForm_Load; + } + + protected override CreateParams CreateParams + { + get + { + const int WS_POPUP = unchecked((int)0x80000000); + const int WS_EX_TOOLWINDOW = 0x80; + + CreateParams cp = base.CreateParams; + cp.ExStyle = WS_EX_TOOLWINDOW; + cp.Style = WS_POPUP; + cp.Height = 0; + cp.Width = 0; + return cp; + } + } + + private void HiddenForm_Load(object source, EventArgs e) + { + if (Environment.OSVersion.Platform == PlatformID.Win32NT && Environment.OSVersion.Version.Major >= 5) + { + // Make this a message only window if the OS is WinXP or higher. + const int HWND_MESSAGE = -1; + SetParent(this.Handle, new IntPtr(HWND_MESSAGE)); + } + } + + } // class + + } // class + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Public/Advanced/DdeMessageLoop.cs b/NDDE/NDde/Source/NDde/Public/Advanced/DdeMessageLoop.cs new file mode 100644 index 0000000..2d2935e --- /dev/null +++ b/NDDE/NDde/Source/NDde/Public/Advanced/DdeMessageLoop.cs @@ -0,0 +1,193 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Advanced +{ + using System; + using System.ComponentModel; + using System.Runtime.InteropServices; + using System.Threading; + using System.Windows.Forms; + + /// + /// This is a synchronizing object that can run a message loop on any thread. + /// + /// + public sealed class DdeMessageLoop : IDisposable, ISynchronizeInvoke + { + [DllImport("kernel32.dll")] + private static extern int GetCurrentThreadId(); + + private int _ThreadId = GetCurrentThreadId(); + private Form _Form = new HiddenForm(); + + /// + /// This initializes a new instance of the DdeMessageLoop class. + /// + public DdeMessageLoop() + { + } + + /// + /// This releases all resources held by this instance. + /// + public void Dispose() + { + _Form.Dispose(); + } + + /// + /// This begins an asynchronous operation to execute a delegate on the thread hosting this object. + /// + /// + /// The delegate to execute. + /// + /// + /// The arguments to pass to the delegate. + /// + /// + /// An IAsyncResult object for this operation. + /// + IAsyncResult ISynchronizeInvoke.BeginInvoke(Delegate method, object[] args) + { + return _Form.BeginInvoke(method, args); + } + + /// + /// This returns the object that the delegate returned in the operation. + /// + /// + /// The IAsyncResult object returned by a call to BeginInvoke. + /// + /// + /// The object returned by the delegate. + /// + object ISynchronizeInvoke.EndInvoke(IAsyncResult asyncResult) + { + return _Form.EndInvoke(asyncResult); + } + + /// + /// This executes a delegate on the thread hosting this object. + /// + /// + /// The delegate to execute. + /// + /// + /// The arguments to pass to the delegate. + /// + /// + /// The object returned by the delegate. + /// + object ISynchronizeInvoke.Invoke(Delegate method, object[] args) + { + if (Thread.VolatileRead(ref _ThreadId) != GetCurrentThreadId()) + { + return _Form.Invoke(method, args); + } + else + { + return method.DynamicInvoke(args); + } + } + + /// + /// This gets a bool indicating whether the caller must use Invoke. + /// + bool ISynchronizeInvoke.InvokeRequired + { + get { return Thread.VolatileRead(ref _ThreadId) != GetCurrentThreadId(); } + } + + /// + /// This starts a message loop on the current thread. + /// + public void Run() + { + _Form.Show(); + Application.Run(); + } + + /// + /// This starts a message loop on the current thread and shows the specified form. + /// + /// + /// The Form to display. + /// + public void Run(Form form) + { + _Form.Show(); + Application.Run(form); + } + + /// + private sealed class HiddenForm : Form + { + [DllImport("user32.dll")] + private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hwndNewParent); + + public HiddenForm() + { + this.Load += this.HiddenForm_Load; + } + + protected override CreateParams CreateParams + { + get + { + const int WS_POPUP = unchecked((int)0x80000000); + const int WS_EX_TOOLWINDOW = 0x80; + + CreateParams cp = base.CreateParams; + cp.ExStyle = WS_EX_TOOLWINDOW; + cp.Style = WS_POPUP; + cp.Height = 0; + cp.Width = 0; + return cp; + } + } + + private void HiddenForm_Load(object source, EventArgs e) + { + if (Environment.OSVersion.Platform == PlatformID.Win32NT && Environment.OSVersion.Version.Major >= 5) + { + // Make this a message only window if the OS is WinXP or higher. + const int HWND_MESSAGE = -1; + SetParent(this.Handle, new IntPtr(HWND_MESSAGE)); + } + } + + } // class + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Public/Advanced/DdeRegistrationEventArgs.cs b/NDDE/NDde/Source/NDde/Public/Advanced/DdeRegistrationEventArgs.cs new file mode 100644 index 0000000..8e3a795 --- /dev/null +++ b/NDDE/NDde/Source/NDde/Public/Advanced/DdeRegistrationEventArgs.cs @@ -0,0 +1,60 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Advanced +{ + using System; + using NDde.Foundation.Advanced; + + /// + /// This contains information about the Register and Unregister events. + /// + /// + public sealed class DdeRegistrationEventArgs : DdeEventArgs + { + private DdemlRegistrationEventArgs _DdemlObject = null; + + internal DdeRegistrationEventArgs(DdemlRegistrationEventArgs args) + { + } + + /// + /// This gets the service name associated with this event. + /// + public string Service + { + get { return _DdemlObject.Service; } + } + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Public/Advanced/DdeTransaction.cs b/NDDE/NDde/Source/NDde/Public/Advanced/DdeTransaction.cs new file mode 100644 index 0000000..b4bc56a --- /dev/null +++ b/NDDE/NDde/Source/NDde/Public/Advanced/DdeTransaction.cs @@ -0,0 +1,161 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Advanced +{ + using System; + using NDde.Foundation.Advanced; + + /// + /// This contains the parameters of the DDEML callback function. + /// + /// + /// + /// + /// The dwRet property contains the value returned by the DDEML callback function and is the only member that can be modified. See the + /// MSDN documentation for more information about the members of this class. + /// + /// + /// + /// Incorrect usage of the DDEML can cause this library to function incorrectly and can lead to resource leaks. + /// + /// + /// + public sealed class DdeTransaction + { + private DdemlTransaction _DdemlObject = null; + + internal DdeTransaction(DdemlTransaction transaction) + { + _DdemlObject = transaction; + } + + /// + /// See the MSDN documentation for information about this member. + /// + public int uType + { + get { return _DdemlObject.uType; } + } + + /// + /// See the MSDN documentation for information about this member. + /// + public int uFmt + { + get { return _DdemlObject.uFmt; } + } + + /// + /// See the MSDN documentation for information about this member. + /// + public IntPtr hConv + { + get { return _DdemlObject.hConv; } + } + + /// + /// See the MSDN documentation for information about this member. + /// + public IntPtr hsz1 + { + get { return _DdemlObject.hsz1; } + } + + /// + /// See the MSDN documentation for information about this member. + /// + public IntPtr hsz2 + { + get { return _DdemlObject.hsz2; } + } + + /// + /// See the MSDN documentation for information about this member. + /// + public IntPtr hData + { + get { return _DdemlObject.hData; } + } + + /// + /// See the MSDN documentation for information about this member. + /// + public IntPtr dwData1 + { + get { return _DdemlObject.dwData1; } + } + + /// + /// See the MSDN documentation for information about this member. + /// + public IntPtr dwData2 + { + get { return _DdemlObject.dwData2; } + } + + /// + /// This gets the return value of the DDEML callback function. See the MSDN documentation for information about this member. + /// + /// + /// This will be ignored if the PreFilterTransaction method returns false. + /// + public IntPtr dwRet + { + get { return _DdemlObject.dwRet; } + set { _DdemlObject.dwRet = value; } + } + + /// + /// + /// + /// + public override string ToString() + { + string s = ""; + foreach (System.Reflection.PropertyInfo property in this.GetType().GetProperties()) + { + if (s.Length == 0) + { + s += property.Name + "=" + property.GetValue(this, null).ToString(); + } + else + { + s += " " + property.Name + "=" + property.GetValue(this, null).ToString(); + } + } + return s; + } + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Public/Advanced/IDdeTransactionFilter.cs b/NDDE/NDde/Source/NDde/Public/Advanced/IDdeTransactionFilter.cs new file mode 100644 index 0000000..db45b64 --- /dev/null +++ b/NDDE/NDde/Source/NDde/Public/Advanced/IDdeTransactionFilter.cs @@ -0,0 +1,80 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Advanced +{ + using System; + using NDde.Foundation.Advanced; + + /// + /// This defines a transaction filter. + /// + /// + /// + /// Use a transaction filter to intercept the DDEML callback function. The PreFilterTransaction method will be called every time the + /// DDEML callback function executes. The Transaction object passed into the method contains the parameters of the DDE callback + /// function. By using a transaction filter the developer has compelete control over the DDEML. See the MSDN documentation for more + /// information on using the DDEML. + /// + /// + /// + /// Incorrect usage of the DDEML can cause this library to function incorrectly and can lead to resource leaks. + /// + /// + /// + public interface IDdeTransactionFilter + { + /// + /// This filters a transaction before it is dispatched. + /// + /// + /// The transaction to be dispatched. + /// + /// + /// True to filter the transaction and stop it from being dispatched, false otherwise. + /// + /// + /// + /// This method is called everytime the DDEML callback function executes. + /// + /// + /// + /// Incorrect usage of the DDEML can cause this library to function incorrectly and can lead to resource leaks. + /// + /// + /// + bool PreFilterTransaction(DdeTransaction t); + + } // interface + + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Public/Advanced/Monitor/DdeActivityEventArgs.cs b/NDDE/NDde/Source/NDde/Public/Advanced/Monitor/DdeActivityEventArgs.cs new file mode 100644 index 0000000..478e56e --- /dev/null +++ b/NDDE/NDde/Source/NDde/Public/Advanced/Monitor/DdeActivityEventArgs.cs @@ -0,0 +1,60 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Advanced.Monitor +{ + using System; + using NDde.Foundation.Advanced.Monitor; + + /// + /// This contains information about events on DdeMonitor. + /// + public abstract class DdeActivityEventArgs : DdeEventArgs + { + private DdemlActivityEventArgs _DdemlObject = null; + + internal DdeActivityEventArgs(DdemlActivityEventArgs args) + { + _DdemlObject = args; + } + + /// + /// This gets the task handle of the application associated with this event. + /// + public IntPtr TaskHandle + { + get { return _DdemlObject.TaskHandle; } + } + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Public/Advanced/Monitor/DdeCallbackActivityEventArgs.cs b/NDDE/NDde/Source/NDde/Public/Advanced/Monitor/DdeCallbackActivityEventArgs.cs new file mode 100644 index 0000000..c327c3a --- /dev/null +++ b/NDDE/NDde/Source/NDde/Public/Advanced/Monitor/DdeCallbackActivityEventArgs.cs @@ -0,0 +1,124 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Advanced.Monitor +{ + using System; + using NDde.Foundation.Advanced.Monitor; + + /// + /// This contains information about the CallbackActivity event. + /// + public sealed class DdeCallbackActivityEventArgs : DdeActivityEventArgs + { + private DdemlCallbackActivityEventArgs _DdemlObject = null; + + internal DdeCallbackActivityEventArgs(DdemlCallbackActivityEventArgs args) : base(args) + { + _DdemlObject = args; + } + + /// + /// See the MSDN documentation for information about this member. + /// + public int uType + { + get { return _DdemlObject.uType; } + } + + /// + /// See the MSDN documentation for information about this member. + /// + public int uFmt + { + get { return _DdemlObject.uFmt; } + } + + /// + /// See the MSDN documentation for information about this member. + /// + public IntPtr hConv + { + get { return _DdemlObject.hConv; } + } + + /// + /// See the MSDN documentation for information about this member. + /// + public IntPtr hsz1 + { + get { return _DdemlObject.hsz1; } + } + + /// + /// See the MSDN documentation for information about this member. + /// + public IntPtr hsz2 + { + get { return _DdemlObject.hsz2; } + } + + /// + /// See the MSDN documentation for information about this member. + /// + public IntPtr hData + { + get { return _DdemlObject.hData; } + } + + /// + /// See the MSDN documentation for information about this member. + /// + public IntPtr dwData1 + { + get { return _DdemlObject.dwData1; } + } + + /// + /// See the MSDN documentation for information about this member. + /// + public IntPtr dwData2 + { + get { return _DdemlObject.dwData2; } + } + + /// + /// This gets the return value of the DDEML callback function. See the MSDN documentation for information about this member. + /// + public IntPtr dwRet + { + get { return _DdemlObject.dwRet; } + } + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Public/Advanced/Monitor/DdeConversationActivityEventArgs.cs b/NDDE/NDde/Source/NDde/Public/Advanced/Monitor/DdeConversationActivityEventArgs.cs new file mode 100644 index 0000000..522c643 --- /dev/null +++ b/NDDE/NDde/Source/NDde/Public/Advanced/Monitor/DdeConversationActivityEventArgs.cs @@ -0,0 +1,96 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Advanced.Monitor +{ + using System; + using NDde.Foundation.Advanced.Monitor; + + /// + /// This contains information about the ConversationActivity event. + /// + public sealed class DdeConversationActivityEventArgs : DdeActivityEventArgs + { + private DdemlConversationActivityEventArgs _DdemlObject = null; + + internal DdeConversationActivityEventArgs(DdemlConversationActivityEventArgs args) : base(args) + { + _DdemlObject = args; + } + + /// + /// This gets the service name associated with the conversation. + /// + public string Service + { + get { return _DdemlObject.Service; } + } + + /// + /// This gets the topic name associated with the conversation. + /// + public string Topic + { + get { return _DdemlObject.Topic; } + } + + /// + /// This gets a bool indicating whether the conversation is being established. + /// + /// + /// The value returned by this property will be true if the conversation is being established. If the conversation + /// is being terminated then the value will be false. + /// + public bool IsEstablished + { + get { return _DdemlObject.IsEstablished; } + } + + /// + /// This gets the handle to the client application associated with the conversation. + /// + public IntPtr ClientHandle + { + get { return _DdemlObject.ClientHandle; } + } + + /// + /// This gets the handle to the server application associated with the conversation. + /// + public IntPtr ServerHandle + { + get { return _DdemlObject.ServerHandle; } + } + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Public/Advanced/Monitor/DdeErrorActivityEventArgs.cs b/NDDE/NDde/Source/NDde/Public/Advanced/Monitor/DdeErrorActivityEventArgs.cs new file mode 100644 index 0000000..e175462 --- /dev/null +++ b/NDDE/NDde/Source/NDde/Public/Advanced/Monitor/DdeErrorActivityEventArgs.cs @@ -0,0 +1,60 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Advanced.Monitor +{ + using System; + using NDde.Foundation.Advanced.Monitor; + + /// + /// This contains information about the ErrorActivity event. + /// + public sealed class DdeErrorActivityEventArgs : DdeActivityEventArgs + { + private DdemlErrorActivityEventArgs _DdemlObject = null; + + internal DdeErrorActivityEventArgs(DdemlErrorActivityEventArgs args) : base(args) + { + _DdemlObject = args; + } + + /// + /// This gets an error code returned by the DDEML. + /// + public int Code + { + get { return _DdemlObject.Code; } + } + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Public/Advanced/Monitor/DdeLinkActivityEventArgs.cs b/NDDE/NDde/Source/NDde/Public/Advanced/Monitor/DdeLinkActivityEventArgs.cs new file mode 100644 index 0000000..5a2903a --- /dev/null +++ b/NDDE/NDde/Source/NDde/Public/Advanced/Monitor/DdeLinkActivityEventArgs.cs @@ -0,0 +1,128 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Advanced.Monitor +{ + using System; + using NDde.Foundation.Advanced.Monitor; + + /// + /// This contains information about the LinkActivity event. + /// + public sealed class DdeLinkActivityEventArgs : DdeActivityEventArgs + { + private DdemlLinkActivityEventArgs _DdemlObject = null; + + internal DdeLinkActivityEventArgs(DdemlLinkActivityEventArgs args) : base(args) + { + _DdemlObject = args; + } + + /// + /// This gets the service name associated with the link. + /// + public string Service + { + get { return _DdemlObject.Service; } + } + + /// + /// This gets the topic name associated with the link. + /// + public string Topic + { + get { return _DdemlObject.Topic; } + } + + /// + /// This gets the item name associated with the link. + /// + public string Item + { + get { return _DdemlObject.Item; } + } + + /// + /// This gets the format of the data associated with the link. + /// + public int Format + { + get { return _DdemlObject.Format; } + } + + /// + /// This gets a bool indicating whether the link is hot. + /// + public bool IsHot + { + get { return _DdemlObject.IsHot; } + } + + /// + /// This gets a bool indicating whether the link is being established. + /// + /// + /// The value returned by this property will be true if the conversation is being established. If the conversation + /// is being terminated then the value will be false. + /// + public bool IsEstablished + { + get { return _DdemlObject.IsEstablished; } + } + + /// + /// This gets a bool indicating whether the link was terminated by the server. + /// + public bool IsServerInitiated + { + get { return _DdemlObject.IsServerInitiated; } + } + + /// + /// This gets the handle to the client application associated with the link. + /// + public IntPtr ClientHandle + { + get { return _DdemlObject.ClientHandle; } + } + + /// + /// This gets the handle to the server application associated with the link. + /// + public IntPtr ServerHandle + { + get { return _DdemlObject.ServerHandle; } + } + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Public/Advanced/Monitor/DdeMessageActivityEventArgs.cs b/NDDE/NDde/Source/NDde/Public/Advanced/Monitor/DdeMessageActivityEventArgs.cs new file mode 100644 index 0000000..526f145 --- /dev/null +++ b/NDDE/NDde/Source/NDde/Public/Advanced/Monitor/DdeMessageActivityEventArgs.cs @@ -0,0 +1,86 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Advanced.Monitor +{ + using System; + using System.Windows.Forms; + using NDde.Foundation.Advanced.Monitor; + + /// + /// This represents the kind of message contained in DdeMessageActivityEventArgs. + /// + public enum DdeMessageActivityKind + { + /// + /// The message was posted by a DDE application. + /// + Post, + + /// + /// The message was sent by a DDE application. + /// + Send + + } // enum + + /// + /// This contains information about the MessageActivity event. + /// + public sealed class DdeMessageActivityEventArgs : DdeActivityEventArgs + { + private DdemlMessageActivityEventArgs _DdemlObject = null; + + internal DdeMessageActivityEventArgs(DdemlMessageActivityEventArgs args) : base(args) + { + _DdemlObject = args; + } + + /// + /// This gets the kind of message associated with this event. + /// + public DdeMessageActivityKind Kind + { + get { return (DdeMessageActivityKind)(int)_DdemlObject.Kind; } + } + + /// + /// This gets the message associated with this event. + /// + public Message Message + { + get { return _DdemlObject.Message; } + } + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Public/Advanced/Monitor/DdeMonitor.cs b/NDDE/NDde/Source/NDde/Public/Advanced/Monitor/DdeMonitor.cs new file mode 100644 index 0000000..b32adaf --- /dev/null +++ b/NDDE/NDde/Source/NDde/Public/Advanced/Monitor/DdeMonitor.cs @@ -0,0 +1,431 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Advanced.Monitor +{ + using System; + using System.Threading; + using NDde.Foundation; + using NDde.Foundation.Advanced.Monitor; + using NDde.Properties; + + /// + /// This specifies the different kinds of DDE activity that can be monitored. + /// + [Flags] + public enum DdeMonitorFlags + { + /// + /// This indicates activity caused by the execution of a DDEML callback. + /// + Callback = DdemlMonitorFlags.Callback, + + /// + /// This indicates activity caused by conversation. + /// + Conversation = DdemlMonitorFlags.Conversation, + + /// + /// This indicates activity caused by an error. + /// + Error = DdemlMonitorFlags.Error, + + ///// + ///// + ///// + //String = DdemlMonitorFlags.String, + + /// + /// This indicates activity caused by an advise loop. + /// + Link = DdemlMonitorFlags.Link, + + /// + /// This indicates activity caused by DDE messages. + /// + Message = DdemlMonitorFlags.Message, + + } // enum + + /// + /// This is used to monitor DDE activity. + /// + public sealed class DdeMonitor : IDisposable + { + private Object _LockObject = new Object(); + + private DdemlMonitor _DdemlObject = null; // This has lazy initialization through a property. + private DdeContext _Context = null; + + private event EventHandler _CallbackActivityEvent = null; + private event EventHandler _ConversationActivityEvent = null; + private event EventHandler _ErrorActivityEvent = null; + private event EventHandler _LinkActivityEvent = null; + private event EventHandler _MessageActivityEvent = null; + //private event EventHandler _StringActivityEvent = null; + + /// + /// This is raised anytime a DDEML callback is executed. + /// + public event EventHandler CallbackActivity + { + add + { + lock (_LockObject) + { + _CallbackActivityEvent += value; + } + } + remove + { + lock (_LockObject) + { + _CallbackActivityEvent -= value; + } + } + } + + /// + /// This is raised anytime a conversation is established or terminated. + /// + public event EventHandler ConversationActivity + { + add + { + lock (_LockObject) + { + _ConversationActivityEvent += value; + } + } + remove + { + lock (_LockObject) + { + _ConversationActivityEvent -= value; + } + } + } + + /// + /// This is raised anytime there is an error. + /// + public event EventHandler ErrorActivity + { + add + { + lock (_LockObject) + { + _ErrorActivityEvent += value; + } + } + remove + { + lock (_LockObject) + { + _ErrorActivityEvent -= value; + } + } + } + + /// + /// This is raised anytime an advise loop is established or terminated. + /// + public event EventHandler LinkActivity + { + add + { + lock (_LockObject) + { + _LinkActivityEvent += value; + } + } + remove + { + lock (_LockObject) + { + _LinkActivityEvent -= value; + } + } + } + + /// + /// This is raised anytime a DDE message is sent or posted. + /// + public event EventHandler MessageActivity + { + add + { + lock (_LockObject) + { + _MessageActivityEvent += value; + } + } + remove + { + lock (_LockObject) + { + _MessageActivityEvent -= value; + } + } + } + + ///// + ///// + ///// + //public event EventHandler StringActivity + //{ + // add + // { + // lock (_LockObject) + // { + // _StringActivityEvent += value; + // } + // } + // remove + // { + // lock (_LockObject) + // { + // _StringActivityEvent -= value; + // } + // } + //} + + /// + /// This initializes a new instance of the DdeMonitor class. + /// + public DdeMonitor() + { + Context = new DdeContext(); + } + + /// + /// This releases all resources held by this instance. + /// + public void Dispose() + { + Dispose(true); + } + + private void Dispose(bool disposing) + { + if (disposing) + { + ThreadStart method = delegate() + { + DdemlObject.Dispose(); + }; + + try + { + Context.Invoke(method); + } + catch + { + // Swallow any exception that occurs. + } + } + } + + /// + /// This gets the context associated with this instance. + /// + public DdeContext Context + { + get + { + lock (_LockObject) + { + return _Context; + } + } + private set + { + lock (_LockObject) + { + _Context = value; + } + } + } + + /// + /// This starts monitoring the system for DDE activity. + /// + /// + /// A bitwise combination of DdeMonitorFlags that indicate what DDE activity will be monitored. + /// + public void Start(DdeMonitorFlags flags) + { + ThreadStart method = delegate() + { + DdemlObject.Start((DdemlMonitorFlags)(int)flags); + }; + + try + { + Context.Invoke(method); + } + catch (DdemlException e) + { + throw new DdeException(e); + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + } + + internal DdemlMonitor DdemlObject + { + get + { + lock (_LockObject) + { + if (_DdemlObject == null) + { + _DdemlObject = new DdemlMonitor(Context.DdemlObject); + _DdemlObject.CallbackActivity += new EventHandler(this.OnCallbackActivity); + _DdemlObject.ConversationActivity += new EventHandler(this.OnConversationActivity); + _DdemlObject.ErrorActivity += new EventHandler(this.OnErrorActivity); + _DdemlObject.LinkActivity += new EventHandler(this.OnLinkActivity); + _DdemlObject.MessageActivity += new EventHandler(this.OnMessageActivity); + //_DdemlObject.StringActivity += new EventHandler(this.OnStringActivity); + } + return _DdemlObject; + } + } + } + + //private void OnStringActivity(object sender, DdemlStringActivityEventArgs e) + //{ + // EventHandler copy; + + // // To make this thread-safe we need to hold a local copy of the reference to the invocation list. This works because delegates are + // //immutable. + // lock (_LockObject) + // { + // copy = _StringActivityEvent; + // } + + // if (copy != null) + // { + // copy(this, new DdeStringActivityEventArgs(e)); + // } + //} + + private void OnMessageActivity(object sender, DdemlMessageActivityEventArgs e) + { + EventHandler copy; + + // To make this thread-safe we need to hold a local copy of the reference to the invocation list. This works because delegates are + //immutable. + lock (_LockObject) + { + copy = _MessageActivityEvent; + } + + if (copy != null) + { + copy(this, new DdeMessageActivityEventArgs(e)); + } + } + + private void OnLinkActivity(object sender, DdemlLinkActivityEventArgs e) + { + EventHandler copy; + + // To make this thread-safe we need to hold a local copy of the reference to the invocation list. This works because delegates are + //immutable. + lock (_LockObject) + { + copy = _LinkActivityEvent; + } + + if (copy != null) + { + copy(this, new DdeLinkActivityEventArgs(e)); + } + } + + private void OnErrorActivity(object sender, DdemlErrorActivityEventArgs e) + { + EventHandler copy; + + // To make this thread-safe we need to hold a local copy of the reference to the invocation list. This works because delegates are + //immutable. + lock (_LockObject) + { + copy = _ErrorActivityEvent; + } + + if (copy != null) + { + copy(this, new DdeErrorActivityEventArgs(e)); + } + } + + private void OnConversationActivity(object sender, DdemlConversationActivityEventArgs e) + { + EventHandler copy; + + // To make this thread-safe we need to hold a local copy of the reference to the invocation list. This works because delegates are + //immutable. + lock (_LockObject) + { + copy = _ConversationActivityEvent; + } + + if (copy != null) + { + copy(this, new DdeConversationActivityEventArgs(e)); + } + } + + private void OnCallbackActivity(object sender, DdemlCallbackActivityEventArgs e) + { + EventHandler copy; + + // To make this thread-safe we need to hold a local copy of the reference to the invocation list. This works because delegates are + //immutable. + lock (_LockObject) + { + copy = _CallbackActivityEvent; + } + + if (copy != null) + { + copy(this, new DdeCallbackActivityEventArgs(e)); + } + } + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Public/Advanced/Monitor/DdeStringActivityEventArgs.cs b/NDDE/NDde/Source/NDde/Public/Advanced/Monitor/DdeStringActivityEventArgs.cs new file mode 100644 index 0000000..b351dcc --- /dev/null +++ b/NDDE/NDde/Source/NDde/Public/Advanced/Monitor/DdeStringActivityEventArgs.cs @@ -0,0 +1,95 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Advanced.Monitor +{ + using System; + using NDde.Foundation.Advanced.Monitor; + + /// + /// This represents the type of string activity. + /// + public enum DdeStringActivityType + { + /// + /// The DDE application called DdeUninitialize. + /// + CleanUp = DdemlStringActivityType.CleanUp, + + /// + /// The DDE application is creating a string handle. + /// + Create = DdemlStringActivityType.Create, + + /// + /// The DDE application is deleting a string handle. + /// + Delete = DdemlStringActivityType.Delete, + + /// + /// The DDE application is incrementing the reference count of an existing string handle. + /// + Keep = DdemlStringActivityType.Keep + + } // enum + + /// + /// This contains information about the StringActivity event. + /// + public sealed class DdeStringActivityEventArgs : DdeActivityEventArgs + { + private DdemlStringActivityEventArgs _DdemlObject = null; + + internal DdeStringActivityEventArgs(DdemlStringActivityEventArgs args) : base(args) + { + _DdemlObject = args; + } + + /// + /// This gets the text associated with the string handle. + /// + public string Value + { + get { return _DdemlObject.Value; } + } + + /// + /// This gets the action being performed. + /// + public DdeStringActivityType Action + { + get { return (DdeStringActivityType)((int)_DdemlObject.Action); } + } + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Public/Client/DdeAdviseEventArgs.cs b/NDDE/NDde/Source/NDde/Public/Client/DdeAdviseEventArgs.cs new file mode 100644 index 0000000..fac9acc --- /dev/null +++ b/NDDE/NDde/Source/NDde/Public/Client/DdeAdviseEventArgs.cs @@ -0,0 +1,103 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Client +{ + using System; + using System.Text; + using NDde.Foundation.Client; + + /// + /// This contains information about the Advise event. + /// + /// + public sealed class DdeAdviseEventArgs : DdeEventArgs + { + private DdemlAdviseEventArgs _DdemlObject = null; + private Encoding _Encoding = null; + + internal DdeAdviseEventArgs(DdemlAdviseEventArgs args, Encoding encoding) + { + _DdemlObject = args; + _Encoding = encoding; + } + + /// + /// This gets the item name associated with this notification. + /// + public string Item + { + get { return _DdemlObject.Item; } + } + + /// + /// This gets the format of the data included in this notification. + /// + public int Format + { + get { return _DdemlObject.Format; } + } + + /// + /// This gets an application defined data object associated with this advise loop. + /// + public object State + { + get { return _DdemlObject.State; } + } + + /// + /// This gets the data associated with this notification or null if this is not a hot advise loop. + /// + public byte[] Data + { + get { return _DdemlObject.Data; } + } + + /// + /// This gets the text associated with this notification or null if this is not a hot advise loop. + /// + public string Text + { + get + { + if (_DdemlObject.Data != null) + { + return _Encoding.GetString(_DdemlObject.Data); + } + return null; + } + } + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Public/Client/DdeClient.cs b/NDDE/NDde/Source/NDde/Public/Client/DdeClient.cs new file mode 100644 index 0000000..22eaf68 --- /dev/null +++ b/NDDE/NDde/Source/NDde/Public/Client/DdeClient.cs @@ -0,0 +1,1891 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Client +{ + using System; + using System.ComponentModel; + using System.Reflection; + using System.Threading; + using System.Text; + using NDde.Advanced; + using NDde.Foundation; + using NDde.Foundation.Client; + using NDde.Properties; + + /// + /// This represents the client side of a DDE conversation. + /// + /// + /// + /// + /// DDE conversations are established by specifying a service name and topic name pair. The service name is usually the name of the application + /// acting as a DDE server. A DDE server can respond to multiple service names, but most servers usually only respond to one. The topic name + /// is a logical context for data and is defined by the server application. A server can and usually does support many topic names. + /// + /// + /// After a conversation has been established by calling Connect an application can read and write data using the Request and + /// Poke methods respectively by specifying an item name supported by the active conversation. An item name identifies a unit of data. + /// An application can also be notified of changes by initiating an advise loop on an item name using the StartAdvise method. Advise + /// loops can either be warm or hot. A hot advise loop returns the data associated with an item name when it changes whereas a warm advise loop + /// only notifies the application without sending any data. Commands can be sent to the server using the Execute method. + /// + /// + /// Callbacks and events are invoked on the thread hosting the DdeContext. All operations must be marshaled onto the thread hosting the + /// DdeContext associated with this object. Method calls will block until that thread becomes available. An exception will be generated + /// if the thread does not become available in a timely manner. + /// + /// + /// + public class DdeClient : IDisposable + { + private Object _LockObject = new Object(); + + private EventHandler _AdviseEvent = null; + private EventHandler _DisconnectedEvent = null; + + private DdemlClient _DdemlObject = null; // This has lazy initialization through a property. + private DdeContext _Context = null; + + private IntPtr _Handle = IntPtr.Zero; // This is a cached DdemlClient property. + private bool _IsConnected = false; // This is a cached DdemlClient property. + private bool _IsPaused = false; // This is a cached DdemlClient property. + private string _Service = ""; // This is a cached DdemlClient property. + private string _Topic = ""; // This is a cached DdemlClient property. + + /// + /// This is raised when the data has changed for an item name that has an advise loop. + /// + public event EventHandler Advise + { + add + { + lock (_LockObject) + { + _AdviseEvent += value; + } + } + remove + { + lock (_LockObject) + { + _AdviseEvent -= value; + } + } + } + + /// + /// This is raised when the client has been disconnected. + /// + public event EventHandler Disconnected + { + add + { + lock (_LockObject) + { + _DisconnectedEvent += value; + } + } + remove + { + lock (_LockObject) + { + _DisconnectedEvent -= value; + } + } + } + + /// + /// + /// + /// + /// + /// This initializes a new instance of the DdeClient class that can connect to a server that supports the specified service name and + /// topic name pair. + /// + /// + /// A service name supported by a server application. + /// + /// + /// A topic name support by a server application. + /// + /// + /// This is thown when servic or topic exceeds 255 characters. + /// + /// + /// This is thrown when service or topic is a null reference. + /// + public DdeClient(string service, string topic) + : this(service, topic, DdeContext.GetDefault()) + { + } + + /// + /// This initializes a new instance of the DdeClient class that can connect to a server that supports the specified service name and + /// topic name pair using the specified synchronizing object. + /// + /// + /// A service name supported by a server application. + /// + /// + /// A topic name support by a server application. + /// + /// + /// The synchronizing object to use for this instance. + /// + /// + /// This is thown when service or topic exceeds 255 characters. + /// + /// + /// This is thrown when service or topic is a null reference. + /// + public DdeClient(string service, string topic, ISynchronizeInvoke synchronizingObject) + : this(service, topic, DdeContext.GetDefault(synchronizingObject)) + { + } + + /// + /// This initializes a new instance of the DdeClient class that can connect to a server that supports the specified service name and + /// topic name pair and uses the specified context. + /// + /// + /// A service name supported by a server application. + /// + /// + /// A topic name support by a server application. + /// + /// + /// The context to use for execution. + /// + /// + /// This is thown when servic or topic exceeds 255 characters. + /// + /// + /// This is thrown when service or topic is a null reference. + /// + public DdeClient(string service, string topic, DdeContext context) + { + Service = service; + Topic = topic; + Context = context; + } + + /// + /// This terminates the current conversation and releases all resources held by this instance. + /// + public void Dispose() + { + Dispose(true); + } + + /// + /// This contains the implementation to release all resources held by this instance. + /// + /// + /// True if called by Dispose, false otherwise. + /// + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + ThreadStart method = delegate() + { + DdemlObject.Dispose(); + }; + + try + { + Context.Invoke(method); + } + catch + { + // Swallow any exception that occurs. + } + } + } + + /// + /// + /// + internal DdemlClient DdemlObject + { + get + { + lock (_LockObject) + { + if (_DdemlObject == null) + { + _DdemlObject = new DdemlClient(Service, Topic, Context.DdemlObject); + _DdemlObject.Advise += this.OnAdviseReceived; + _DdemlObject.Disconnected += this.OnDisconnected; + _DdemlObject.StateChange += this.OnStateChange; + } + return _DdemlObject; + } + } + } + + /// + /// This gets the context associated with this instance. + /// + public virtual DdeContext Context + { + get + { + lock (_LockObject) + { + return _Context; + } + } + private set + { + lock (_LockObject) + { + _Context = value; + } + } + } + + /// + /// This gets the service name associated with this conversation. + /// + public virtual string Service + { + get + { + lock (_LockObject) + { + return _Service; + } + } + private set + { + lock (_LockObject) + { + _Service = value; + } + } + } + + /// + /// This gets the topic name associated with this conversation. + /// + public virtual string Topic + { + get + { + lock (_LockObject) + { + return _Topic; + } + } + private set + { + lock (_LockObject) + { + _Topic = value; + } + } + } + + /// + /// This gets the DDEML handle associated with this conversation. + /// + /// + /// + /// This can be used in any DDEML function requiring a conversation handle. + /// + /// + /// + /// Incorrect usage of the DDEML can cause this object to function incorrectly and can lead to resource leaks. + /// + /// + /// + public virtual IntPtr Handle + { + get + { + lock (_LockObject) + { + return _Handle; + } + } + } + + /// + /// This gets a bool indicating whether this conversation is paused. + /// + public virtual bool IsPaused + { + get + { + lock (_LockObject) + { + return _IsPaused; + } + } + } + + /// + /// This gets a bool indicating whether the conversation is established. + /// + /// + /// + /// Do not assume that the conversation is still established after checking this property. The conversation can terminate at any time. + /// + /// + public virtual bool IsConnected + { + get + { + lock (_LockObject) + { + return _IsConnected; + } + } + } + + /// + /// This establishes a conversation with a server that supports the specified service name and topic name pair. + /// + /// + /// This is thrown when the client is already connected. + /// + /// + /// This is thrown when the client could not connect to the server. + /// + public virtual void Connect() + { + ThreadStart method = delegate() + { + DdemlObject.Connect(); + }; + + try + { + Context.Invoke(method); + } + catch (DdemlException e) + { + throw new DdeException(e); + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + } + + /// + /// This establishes a conversation with a server that supports the specified service name and topic name pair. + /// + /// + /// Zero if the operation succeed or non-zero if the operation failed. + /// + public virtual int TryConnect() + { + int result = 0; + + ThreadStart method = delegate() + { + result = DdemlObject.TryConnect(); + }; + + try + { + Context.Invoke(method); + return result; + } + catch (DdemlException e) + { + throw new DdeException(e); + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + } + + /// + /// This terminates the current conversation. + /// + /// + /// + /// This is thrown when the client was not previously connected. + /// + /// + /// This is thown when the client could not disconnect from the server. + /// + public virtual void Disconnect() + { + ThreadStart method = delegate() + { + DdemlObject.Disconnect(); + }; + + try + { + Context.Invoke(method); + } + catch (DdemlException e) + { + throw new DdeException(e); + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + } + + /// + /// This pauses the current conversation. + /// + /// + /// This is thrown when the conversation is already paused. + /// + /// + /// This is thrown when the conversation could not be paused or when the client is not connected. + /// + /// + /// Synchronous operations will timeout if the conversation is paused. Asynchronous operations can begin, but will not complete until the + /// conversation has resumed. + /// + public virtual void Pause() + { + ThreadStart method = delegate() + { + DdemlObject.Pause(); + }; + + try + { + Context.Invoke(method); + } + catch (DdemlException e) + { + throw new DdeException(e); + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + } + + /// + /// This resumes the current conversation. + /// + /// + /// This is thrown when the conversation was not previously paused or when the client is not connected. + /// + /// + /// This is thrown when the conversation could not be resumed. + /// + public virtual void Resume() + { + ThreadStart method = delegate() + { + DdemlObject.Resume(); + }; + + try + { + Context.Invoke(method); + } + catch (DdemlException e) + { + throw new DdeException(e); + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + } + + /// + /// This terminates an asychronous operation. + /// + /// + /// The IAsyncResult object returned by a call that begins an asynchronous operation. + /// + /// + /// This method does nothing if the asynchronous operation has already completed. + /// + /// + /// This is thown when asyncResult is an invalid IAsyncResult. + /// + /// + /// This is thrown when asyncResult is a null reference. + /// + /// + /// This is thrown when the client is not connected. + /// + /// + /// This is thrown when the asynchronous operation could not be abandoned. + /// + public virtual void Abandon(IAsyncResult asyncResult) + { + ThreadStart method = delegate() + { + if (asyncResult is AsyncResult) + { + DdemlObject.Abandon(((AsyncResult)asyncResult).DdemlAsyncResult); + } + else + { + DdemlObject.Abandon(InvalidAsyncResult.Instance); + } + }; + + try + { + Context.Invoke(method); + } + catch (DdemlException e) + { + throw new DdeException(e); + } + catch (ArgumentException e) + { + throw e; + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + } + + /// + /// This sends a command to the server application. + /// + /// + /// The command to be sent to the server application. + /// + /// + /// The amount of time in milliseconds to wait for a response. + /// + /// + /// This is thown when command exceeds 255 characters or timeout is negative. + /// + /// + /// This is thrown when command is a null reference. + /// + /// + /// This is thrown when the client is not connected. + /// + /// + /// This is thrown when the server does not process the command. + /// + /// + /// This operation will timeout if the conversation is paused. + /// + public virtual void Execute(string command, int timeout) + { + ThreadStart method = delegate() + { + DdemlObject.Execute(command, timeout); + }; + + try + { + Context.Invoke(method); + } + catch (DdemlException e) + { + throw new DdeException(e); + } + catch (ArgumentException e) + { + throw e; + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + } + + /// + /// This sends a command to the server application. + /// + /// + /// The command to be sent to the server application. + /// + /// + /// The amount of time in milliseconds to wait for a response. + /// + /// + /// Zero if the operation succeed or non-zero if the operation failed. + /// + /// + /// This operation will timeout if the conversation is paused. + /// + public virtual int TryExecute(string command, int timeout) + { + int result = 0; + + ThreadStart method = delegate() + { + result = DdemlObject.TryExecute(command, timeout); + }; + + try + { + Context.Invoke(method); + return result; + } + catch (DdemlException e) + { + throw new DdeException(e); + } + catch (ArgumentException e) + { + throw e; + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + } + + /// + /// This begins an asynchronous operation to send a command to the server application. + /// + /// + /// The command to be sent to the server application. + /// + /// + /// The delegate to invoke when this operation completes. + /// + /// + /// An application defined data object to associate with this operation. + /// + /// + /// An IAsyncResult object for this operation. + /// + /// + /// This is thown when command exceeds 255 characters. + /// + /// + /// This is thrown when command is a null reference. + /// + /// + /// This is thrown when the client is not connected. + /// + /// + /// This is thrown when the asynchronous operation could not begin. + /// + public virtual IAsyncResult BeginExecute(string command, AsyncCallback callback, object state) + { + AsyncResult ar = new AsyncResult(Context); + ar.Callback = callback; + ar.State = state; + + ThreadStart method = delegate() + { + ar.DdemlAsyncResult = DdemlObject.BeginExecute(command, this.OnExecuteComplete, ar); + }; + + try + { + Context.Invoke(method); + } + catch (DdemlException e) + { + throw new DdeException(e); + } + catch (ArgumentException e) + { + throw e; + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + + return ar; + } + + /// + /// This throws any exception that occurred during the asynchronous operation. + /// + /// + /// The IAsyncResult object returned by a call to BeginExecute. + /// + /// + /// This is thown when asyncResult is an invalid IAsyncResult. + /// + /// + /// This is thrown when asyncResult is a null reference. + /// + /// + /// This is thrown when the server does not process the command. + /// + public virtual void EndExecute(IAsyncResult asyncResult) + { + ThreadStart method = delegate() + { + if (asyncResult is AsyncResult) + { + DdemlObject.EndExecute(((AsyncResult)asyncResult).DdemlAsyncResult); + } + else + { + DdemlObject.EndExecute(InvalidAsyncResult.Instance); + } + }; + + try + { + Context.Invoke(method); + } + catch (DdemlException e) + { + throw new DdeException(e); + } + catch (ArgumentException e) + { + throw e; + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + } + + /// + /// + /// + /// + /// + /// This sends data to the server application. + /// + /// + /// An item name supported by the current conversation. + /// + /// + /// The data to send. + /// + /// + /// The amount of time in milliseconds to wait for a response. + /// + /// + /// This is thown when item exceeds 255 characters or timeout is negative. + /// + /// + /// This is thrown when item or data is a null reference. + /// + /// + /// This is thrown when the client is not connected. + /// + /// + /// This is thrown when the server does not process the data. + /// + /// + /// This operation will timeout if the conversation is paused. + /// + public virtual void Poke(string item, string data, int timeout) + { + Poke(item, Context.Encoding.GetBytes(data + "\0"), 1, timeout); + } + + /// + /// This sends data to the server application. + /// + /// + /// An item name supported by the current conversation. + /// + /// + /// The data to send. + /// + /// + /// The format of the data. + /// + /// + /// The amount of time in milliseconds to wait for a response. + /// + /// + /// This is thown when item exceeds 255 characters or timeout is negative. + /// + /// + /// This is thrown when item or data is a null reference. + /// + /// + /// This is thrown when the client is not connected. + /// + /// + /// This is thrown when the server does not process the data. + /// + /// + /// This operation will timeout if the conversation is paused. + /// + public virtual void Poke(string item, byte[] data, int format, int timeout) + { + ThreadStart method = delegate() + { + DdemlObject.Poke(item, data, format, timeout); + }; + + try + { + Context.Invoke(method); + } + catch (DdemlException e) + { + throw new DdeException(e); + } + catch (ArgumentException e) + { + throw e; + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + } + + /// + /// This sends data to the server application. + /// + /// + /// An item name supported by the current conversation. + /// + /// + /// The data to send. + /// + /// + /// The format of the data. + /// + /// + /// The amount of time in milliseconds to wait for a response. + /// + /// + /// Zero if the operation succeed or non-zero if the operation failed. + /// + /// + /// This operation will timeout if the conversation is paused. + /// + public virtual int TryPoke(string item, byte[] data, int format, int timeout) + { + int result = 0; + + ThreadStart method = delegate() + { + result = DdemlObject.TryPoke(item, data, format, timeout); + }; + + try + { + Context.Invoke(method); + return result; + } + catch (DdemlException e) + { + throw new DdeException(e); + } + catch (ArgumentException e) + { + throw e; + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + } + + /// + /// This begins an asynchronous operation to send data to the server application. + /// + /// + /// An item name supported by the current conversation. + /// + /// + /// The data to send. + /// + /// + /// The format of the data. + /// + /// + /// The delegate to invoke when this operation completes. + /// + /// + /// An application defined data object to associate with this operation. + /// + /// + /// An IAsyncResult object for this operation. + /// + /// + /// This is thown when item exceeds 255 characters. + /// + /// + /// This is thrown when item or data is a null reference. + /// + /// + /// This is thrown when the client is not connected. + /// + /// + /// This is thrown when the asynchronous operation could not begin. + /// + public virtual IAsyncResult BeginPoke(string item, byte[] data, int format, AsyncCallback callback, object state) + { + AsyncResult ar = new AsyncResult(Context); + ar.Callback = callback; + ar.State = state; + + ThreadStart method = delegate() + { + ar.DdemlAsyncResult = DdemlObject.BeginPoke(item, data, format, this.OnPokeComplete, ar); + }; + + try + { + Context.Invoke(method); + } + catch (DdemlException e) + { + throw new DdeException(e); + } + catch (ArgumentException e) + { + throw e; + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + + return ar; + } + + /// + /// This throws any exception that occurred during the asynchronous operation. + /// + /// + /// The IAsyncResult object returned by a call to BeginPoke. + /// + /// + /// This is thown when asyncResult is an invalid IAsyncResult. + /// + /// + /// This is thrown when asyncResult is a null reference. + /// + /// + /// This is thrown when the server does not process the data. + /// + public virtual void EndPoke(IAsyncResult asyncResult) + { + ThreadStart method = delegate() + { + if (asyncResult is AsyncResult) + { + DdemlObject.EndPoke(((AsyncResult)asyncResult).DdemlAsyncResult); + } + else + { + DdemlObject.EndPoke(InvalidAsyncResult.Instance); + } + }; + + try + { + Context.Invoke(method); + } + catch (DdemlException e) + { + throw new DdeException(e); + } + catch (ArgumentException e) + { + throw e; + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + } + + /// + /// + /// + /// + /// + /// This requests data using the specified item name. + /// + /// + /// An item name supported by the current conversation. + /// + /// + /// The amount of time in milliseconds to wait for a response. + /// + /// + /// The data returned by the server application in CF_TEXT format. + /// + /// + /// This is thown when item exceeds 255 characters or timeout is negative. + /// + /// + /// This is thrown when item is a null reference. + /// + /// + /// This is thrown when the client is not connected. + /// + /// + /// This is thrown when the server does not process the request. + /// + /// + /// This operation will timeout if the conversation is paused. + /// + public virtual string Request(string item, int timeout) + { + return Context.Encoding.GetString(Request(item, 1, timeout)); + } + + /// + /// This requests data using the specified item name. + /// + /// + /// An item name supported by the current conversation. + /// + /// + /// The format of the data to return. + /// + /// + /// The amount of time in milliseconds to wait for a response. + /// + /// + /// The data returned by the server application. + /// + /// + /// This is thown when item exceeds 255 characters or timeout is negative. + /// + /// + /// This is thrown when item is a null reference. + /// + /// + /// This is thrown when the client is not connected. + /// + /// + /// This is thrown when the server does not process the request. + /// + /// + /// This operation will timeout if the conversation is paused. + /// + public virtual byte[] Request(string item, int format, int timeout) + { + byte[] result = null; + + ThreadStart method = delegate() + { + result = DdemlObject.Request(item, format, timeout); + }; + + try + { + Context.Invoke(method); + return result; + } + catch (DdemlException e) + { + throw new DdeException(e); + } + catch (ArgumentException e) + { + throw e; + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + } + + /// + /// This requests data using the specified item name. + /// + /// + /// An item name supported by the current conversation. + /// + /// + /// The format of the data to return. + /// + /// + /// The amount of time in milliseconds to wait for a response. + /// + /// + /// The data returned by the server application. + /// + /// + /// Zero if the operation succeeded or non-zero if the operation failed. + /// + /// + /// This operation will timeout if the conversation is paused. + /// + public virtual int TryRequest(string item, int format, int timeout, out byte[] data) + { + byte[] data2 = null; + int result = 0; + + ThreadStart method = delegate() + { + result = DdemlObject.TryRequest(item, format, timeout, out data2); + }; + + try + { + Context.Invoke(method); + data = data2; + return result; + } + catch (DdemlException e) + { + throw new DdeException(e); + } + catch (ArgumentException e) + { + throw e; + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + } + + /// + /// This begins an asynchronous operation to request data using the specified item name. + /// + /// + /// An item name supported by the current conversation. + /// + /// + /// The format of the data to return. + /// + /// + /// The delegate to invoke when this operation completes. + /// + /// + /// An application defined data object to associate with this operation. + /// + /// + /// An IAsyncResult object for this operation. + /// + /// + /// This is thown when item exceeds 255 characters. + /// + /// + /// This is thrown when item is a null reference. + /// + /// + /// This is thrown when the client is not connected. + /// + /// + /// This is thrown when the asynchronous operation could not begin. + /// + public virtual IAsyncResult BeginRequest(string item, int format, AsyncCallback callback, object state) + { + AsyncResult ar = new AsyncResult(Context); + ar.Callback = callback; + ar.State = state; + + ThreadStart method = delegate() + { + ar.DdemlAsyncResult = DdemlObject.BeginRequest(item, format, this.OnRequestComplete, ar); + }; + + try + { + Context.Invoke(method); + } + catch (DdemlException e) + { + throw new DdeException(e); + } + catch (ArgumentException e) + { + throw e; + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + + return ar; + } + + /// + /// This gets the data returned by the server application for the operation. + /// + /// + /// The IAsyncResult object returned by a call to BeginRequest. + /// + /// + /// The data returned by the server application. + /// + /// + /// This is thown when asyncResult is an invalid IAsyncResult. + /// + /// + /// This is thrown when asyncResult is a null reference. + /// + /// + /// This is thrown when the server does not process the request. + /// + public virtual byte[] EndRequest(IAsyncResult asyncResult) + { + byte[] result = null; + + ThreadStart method = delegate() + { + if (asyncResult is AsyncResult) + { + result = DdemlObject.EndRequest(((AsyncResult)asyncResult).DdemlAsyncResult); + } + else + { + result = DdemlObject.EndRequest(InvalidAsyncResult.Instance); + } + }; + + try + { + Context.Invoke(method); + return result; + } + catch (DdemlException e) + { + throw new DdeException(e); + } + catch (ArgumentException e) + { + throw e; + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + } + + /// + /// + /// + /// + /// + /// This initiates an advise loop on the specified item name. + /// + /// + /// An item name supported by the current conversation. + /// + /// + /// The format of the data to return. + /// + /// + /// A bool indicating whether data should be included with the notification. + /// + /// + /// The amount of time in milliseconds to wait for a response. + /// + /// + /// + /// This is thown when item exceeds 255 characters or timeout is negative. + /// + /// + /// This is thrown when item is a null reference. + /// + /// + /// This is thrown when the item is already being advised or when the client is not connected. + /// + /// + /// This is thrown when the server does not initiate the advise loop. + /// + /// + /// This operation will timeout if the conversation is paused. + /// + public virtual void StartAdvise(string item, int format, bool hot, int timeout) + { + StartAdvise(item, format, hot, true, timeout, null); + } + + /// + /// This initiates an advise loop on the specified item name. + /// + /// + /// An item name supported by the current conversation. + /// + /// + /// The format of the data to return. + /// + /// + /// A bool indicating whether data should be included with the notification. + /// + /// + /// A bool indicating whether the client should acknowledge each advisory before the server will send send another. + /// + /// + /// The amount of time in milliseconds to wait for a response. + /// + /// + /// An application defined data object to associate with this advise loop. + /// + /// + /// + /// This is thown when item exceeds 255 characters or timeout is negative. + /// + /// + /// This is thrown when item is a null reference. + /// + /// + /// This is thrown when the item is already being advised or when the client is not connected. + /// + /// + /// This is thrown when the server does not initiate the advise loop. + /// + /// + /// This operation will timeout if the conversation is paused. + /// + public virtual void StartAdvise(string item, int format, bool hot, bool acknowledge, int timeout, object adviseState) + { + ThreadStart method = delegate() + { + DdemlObject.StartAdvise(item, format, hot, acknowledge, timeout, adviseState); + }; + + try + { + Context.Invoke(method); + } + catch (DdemlException e) + { + throw new DdeException(e); + } + catch (ArgumentException e) + { + throw e; + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + } + + /// + /// + /// + /// + /// + /// This begins an asynchronous operation to initiate an advise loop on the specified item name. + /// + /// + /// An item name supported by the current conversation. + /// + /// + /// The format of the data to be returned. + /// + /// + /// A bool indicating whether data should be included with the notification. + /// + /// + /// The delegate to invoke when this operation completes. + /// + /// + /// An application defined data object to associate with this operation. + /// + /// + /// An IAsyncResult object for this operation. + /// + /// + /// + /// This is thown when item exceeds 255 characters. + /// + /// + /// This is thrown when item is a null reference. + /// + /// + /// This is thrown when the item is already being advised or when the client is not connected. + /// + /// + /// This is thrown when the asynchronous operation could not begin. + /// + public virtual IAsyncResult BeginStartAdvise(string item, int format, bool hot, AsyncCallback callback, object asyncState) + { + return BeginStartAdvise(item, format, hot, true, callback, asyncState, null); + } + + /// + /// This begins an asynchronous operation to initiate an advise loop on the specified item name. + /// + /// + /// An item name supported by the current conversation. + /// + /// + /// The format of the data to be returned. + /// + /// + /// A bool indicating whether data should be included with the notification. + /// + /// + /// A bool indicating whether the client should acknowledge each advisory before the server will send send another. + /// + /// + /// The delegate to invoke when this operation completes. + /// + /// + /// An application defined data object to associate with this operation. + /// + /// + /// An application defined data object to associate with this advise loop. + /// + /// + /// An IAsyncResult object for this operation. + /// + /// + /// + /// This is thown when item exceeds 255 characters. + /// + /// + /// This is thrown when item is a null reference. + /// + /// + /// This is thrown when the item is already being advised or when the client is not connected. + /// + /// + /// This is thrown when the asynchronous operation could not begin. + /// + public virtual IAsyncResult BeginStartAdvise(string item, int format, bool hot, bool acknowledge, AsyncCallback callback, object asyncState, object adviseState) + { + AsyncResult ar = new AsyncResult(Context); + ar.Callback = callback; + ar.State = asyncState; + + ThreadStart method = delegate() + { + ar.DdemlAsyncResult = DdemlObject.BeginStartAdvise(item, format, hot, acknowledge, this.OnStartAdviseComplete, ar, adviseState); + }; + + try + { + Context.Invoke(method); + } + catch (DdemlException e) + { + throw new DdeException(e); + } + catch (ArgumentException e) + { + throw e; + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + + return ar; + } + + /// + /// This throws any exception that occurred during the operation. + /// + /// + /// The IAsyncResult object returned by a call to BeginPoke. + /// + /// + /// This is thown when asyncResult is an invalid IAsyncResult. + /// + /// + /// This is thrown when asyncResult is a null reference. + /// + /// + /// This is thrown when the server does not initiate the advise loop. + /// + public virtual void EndStartAdvise(IAsyncResult asyncResult) + { + ThreadStart method = delegate() + { + if (asyncResult is AsyncResult) + { + DdemlObject.EndStartAdvise(((AsyncResult)asyncResult).DdemlAsyncResult); + } + else + { + DdemlObject.EndStartAdvise(InvalidAsyncResult.Instance); + } + }; + + try + { + Context.Invoke(method); + } + catch (DdemlException e) + { + throw new DdeException(e); + } + catch (ArgumentException e) + { + throw e; + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + } + + /// + /// This terminates the advise loop for the specified item name. + /// + /// + /// An item name that has an active advise loop. + /// + /// + /// The amount of time in milliseconds to wait for a response. + /// + /// + /// This operation will timeout if the conversation is paused. + /// + /// + /// This is thown when item exceeds 255 characters or timeout is negative. + /// + /// + /// This is thrown when item is a null reference. + /// + /// + /// This is thrown when the item is not being advised or when the client is not connected. + /// + /// + /// This is thrown when the server does not terminate the advise loop. + /// + public virtual void StopAdvise(string item, int timeout) + { + ThreadStart method = delegate() + { + DdemlObject.StopAdvise(item, timeout); + }; + + try + { + Context.Invoke(method); + } + catch (DdemlException e) + { + throw new DdeException(e); + } + catch (ArgumentException e) + { + throw e; + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + } + + /// + /// This begins an asynchronous operation to terminate the advise loop for the specified item name. + /// + /// + /// An item name that has an active advise loop. + /// + /// + /// The delegate to invoke when this operation completes. + /// + /// + /// An application defined data object to associate with this operation. + /// + /// + /// An IAsyncResult object for this operation. + /// + /// + /// This is thown when item exceeds 255 characters. + /// + /// + /// This is thrown when item is a null reference. + /// + /// + /// This is thrown when the item is not being advised or when the client is not connected. + /// + /// + /// This is thrown when the asynchronous operation could not begin. + /// + public virtual IAsyncResult BeginStopAdvise(string item, AsyncCallback callback, object state) + { + AsyncResult ar = new AsyncResult(Context); + ar.Callback = callback; + ar.State = state; + + ThreadStart method = delegate() + { + ar.DdemlAsyncResult = DdemlObject.BeginStopAdvise(item, this.OnStopAdviseComplete, ar); + }; + + try + { + Context.Invoke(method); + } + catch (DdemlException e) + { + throw new DdeException(e); + } + catch (ArgumentException e) + { + throw e; + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + + return ar; + } + + /// + /// This throws any exception that occurred during the operation. + /// + /// + /// The IAsyncResult object returned by a call to BeginPoke. + /// + /// + /// This is thown when asyncResult is an invalid IAsyncResult. + /// + /// + /// This is thrown when asyncResult is a null reference. + /// + /// + /// This is thrown when the server does not terminate the advise loop. + /// + public virtual void EndStopAdvise(IAsyncResult asyncResult) + { + ThreadStart method = delegate() + { + if (asyncResult is AsyncResult) + { + DdemlObject.EndStopAdvise(((AsyncResult)asyncResult).DdemlAsyncResult); + } + else + { + DdemlObject.EndStopAdvise(InvalidAsyncResult.Instance); + } + }; + + try + { + Context.Invoke(method); + } + catch (DdemlException e) + { + throw new DdeException(e); + } + catch (ArgumentException e) + { + throw e; + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + } + + private void OnExecuteComplete(IAsyncResult asyncResult) + { + AsyncResult ar = (AsyncResult)asyncResult.AsyncState; + if (ar.Callback != null) + { + ar.Callback(ar); + } + } + + private void OnPokeComplete(IAsyncResult asyncResult) + { + AsyncResult ar = (AsyncResult)asyncResult.AsyncState; + if (ar.Callback != null) + { + ar.Callback(ar); + } + } + + private void OnRequestComplete(IAsyncResult asyncResult) + { + AsyncResult ar = (AsyncResult)asyncResult.AsyncState; + if (ar.Callback != null) + { + ar.Callback(ar); + } + } + + private void OnStartAdviseComplete(IAsyncResult asyncResult) + { + AsyncResult ar = (AsyncResult)asyncResult.AsyncState; + if (ar.Callback != null) + { + ar.Callback(ar); + } + } + + private void OnStopAdviseComplete(IAsyncResult asyncResult) + { + AsyncResult ar = (AsyncResult)asyncResult.AsyncState; + if (ar.Callback != null) + { + ar.Callback(ar); + } + } + + private void OnAdviseReceived(object sender, DdemlAdviseEventArgs internalArgs) + { + EventHandler copy; + + // To make this thread-safe we need to hold a local copy of the reference to the invocation list. This works because delegates are + //immutable. + lock (_LockObject) + { + copy = _AdviseEvent; + } + + if (copy != null) + { + copy(this, new DdeAdviseEventArgs(internalArgs, Context.Encoding)); + } + } + + private void OnDisconnected(object sender, DdemlDisconnectedEventArgs internalArgs) + { + EventHandler copy; + + // To make this thread-safe we need to hold a local copy of the reference to the invocation list. This works because delegates are + //immutable. + lock (_LockObject) + { + copy = _DisconnectedEvent; + } + + if (copy != null) + { + copy(this, new DdeDisconnectedEventArgs(internalArgs)); + } + } + + private void OnStateChange(object sender, EventArgs args) + { + lock (_LockObject) + { + _Handle = _DdemlObject.Handle; + _IsConnected = _DdemlObject.IsConnected; + _IsPaused = _DdemlObject.IsPaused; + _Service = _DdemlObject.Service; + _Topic = _DdemlObject.Topic; + } + } + + /// + private sealed class AsyncResult : IAsyncResult + { + private object _State = null; + private AsyncCallback _Callback = null; + private DdeContext _Context = null; + private IAsyncResult _DdemlObject = null; + + public AsyncResult(DdeContext context) + { + _Context = context; + } + + public object AsyncState + { + get { return _State; } + } + + public WaitHandle AsyncWaitHandle + { + get { return _DdemlObject.AsyncWaitHandle; } + } + + public bool CompletedSynchronously + { + get { return _DdemlObject.CompletedSynchronously; } + } + + public bool IsCompleted + { + get { return _DdemlObject.IsCompleted; } + } + + public AsyncCallback Callback + { + get { return _Callback; } + set { _Callback = value; } + } + + public object State + { + get { return _State; } + set { _State = value; } + } + + public IAsyncResult DdemlAsyncResult + { + get { return _DdemlObject; } + set { _DdemlObject = value; } + } + + } // class + + /// + private sealed class InvalidAsyncResult : IAsyncResult + { + private static readonly InvalidAsyncResult _Instance = new InvalidAsyncResult(); + + public static InvalidAsyncResult Instance + { + get { return _Instance; } + } + + private InvalidAsyncResult() + { + } + + public object AsyncState + { + get { return null; } + } + + public bool CompletedSynchronously + { + get { return false; } + } + + public WaitHandle AsyncWaitHandle + { + get { return null; } + } + + public bool IsCompleted + { + get { return false; } + } + + } // class + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Public/Client/DdeDisconnectedEventArgs.cs b/NDDE/NDde/Source/NDde/Public/Client/DdeDisconnectedEventArgs.cs new file mode 100644 index 0000000..f9c5047 --- /dev/null +++ b/NDDE/NDde/Source/NDde/Public/Client/DdeDisconnectedEventArgs.cs @@ -0,0 +1,73 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Client +{ + using System; + using NDde.Foundation.Client; + + /// + /// This contains information about the Disconnected event. + /// + /// + public sealed class DdeDisconnectedEventArgs : DdeEventArgs + { + private DdemlDisconnectedEventArgs _DdemlObject = null; + + internal DdeDisconnectedEventArgs(DdemlDisconnectedEventArgs args) + { + _DdemlObject = args; + } + + /// + /// This gets a bool indicating whether the client disconnected because of the server. + /// + public bool IsServerInitiated + { + get { return _DdemlObject.IsServerInitiated; } + } + + /// + /// This gets a bool indicating whether the client disconnected because Dispose was explicitly called. + /// + /// + /// The value will be true if Dispose was explicitly called on DdeClient. The DdeClient sending this event has + /// been disposed and can no longer be accessed. Any exception thrown in the currently executing method will be ignored. + /// + public bool IsDisposed + { + get { return _DdemlObject.IsDisposed; } + } + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Public/DdeEventArgs.cs b/NDDE/NDde/Source/NDde/Public/DdeEventArgs.cs new file mode 100644 index 0000000..a009b87 --- /dev/null +++ b/NDDE/NDde/Source/NDde/Public/DdeEventArgs.cs @@ -0,0 +1,67 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde +{ + using System; + + /// + /// This is the base class for all NDde event argument classes. + /// + public abstract class DdeEventArgs : EventArgs + { + /// + /// This returns a string containing the current values of all properties. + /// + /// + /// A string containing the current values of all properties. + /// + public override string ToString() + { + string s = ""; + foreach (System.Reflection.PropertyInfo property in this.GetType().GetProperties()) + { + if (s.Length == 0) + { + s += property.Name + "=" + property.GetValue(this, null).ToString(); + } + else + { + s += " " + property.Name + "=" + property.GetValue(this, null).ToString(); + } + } + return s; + } + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Public/DdeException.cs b/NDDE/NDde/Source/NDde/Public/DdeException.cs new file mode 100644 index 0000000..b39f4c8 --- /dev/null +++ b/NDDE/NDde/Source/NDde/Public/DdeException.cs @@ -0,0 +1,115 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde +{ + using System; + using System.Runtime.Serialization; + using NDde.Foundation; + using NDde.Properties; + + /// + /// This is thrown when a DDE exception occurs. + /// + /// + [Serializable] + public class DdeException : Exception + { + private DdemlException _DdemlObject = null; + + internal DdeException(string message) : this(new DdemlException(message)) + { + } + + internal DdeException(DdemlException exception) : base(exception.Message, exception) + { + _DdemlObject = exception; + } + + /// + /// + /// + /// + protected DdeException(SerializationInfo info, StreamingContext context) : base(info, context) + { + _DdemlObject = (DdemlException)info.GetValue("NDde.DdeException.DdemlObject", typeof(DdemlException)); + } + + /// + /// This gets an error code returned by the DDEML. + /// + /// + /// + /// The value is zero if the exception was not thrown because of the DDEML. + /// + /// + /// + /// 0x0000DMLERR_NO_DMLERROR + /// 0x4000DMLERR_ADVACKTIMEOUT + /// 0x4001DMLERR_BUSY + /// 0x4002DMLERR_DATAACKTIMEOUT + /// 0x4003DMLERR_DLL_NOT_INITIALIZED + /// 0x4004DMLERR_DLL_USAGE + /// 0x4005DMLERR_EXECACKTIMEOUT + /// 0x4006DMLERR_INVALIDPARAMETER + /// 0x4007DMLERR_LOW_MEMORY + /// 0x4008DMLERR_MEMORY_DMLERROR + /// 0x4009DMLERR_NOTPROCESSED + /// 0x400ADMLERR_NO_CONV_ESTABLISHED + /// 0x400BDMLERR_POKEACKTIMEOUT + /// 0x400CDMLERR_POSTMSG_FAILED + /// 0x400DDMLERR_REENTRANCY + /// 0x400EDMLERR_SERVER_DIED + /// 0x400FDMLERR_SYS_DMLERROR + /// 0x4010DMLERR_UNADVACKTIMEOUT + /// 0x4011DMLERR_UNFOUND_QUEUE_ID + /// + /// + /// + public int Code + { + get { return _DdemlObject.Code; } + } + + /// + /// + /// + /// + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue("NDde.DdeException.DdemlObject", _DdemlObject); + base.GetObjectData(info, context); + } + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Public/Server/DdeConversation.cs b/NDDE/NDde/Source/NDde/Public/Server/DdeConversation.cs new file mode 100644 index 0000000..e15dc7d --- /dev/null +++ b/NDDE/NDde/Source/NDde/Public/Server/DdeConversation.cs @@ -0,0 +1,202 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Server +{ + using System; + using NDde.Foundation.Server; + + /// + /// This represents a DDE conversation established on a DdeServer. + /// + /// + public sealed class DdeConversation + { + private Object _LockObject = new Object(); + + private DdemlConversation _DdemlObject = null; + private object _Tag = null; + + // These are used to cache the property values of the DdemlConversation. + private string _Service = ""; + private string _Topic = ""; + private IntPtr _Handle = IntPtr.Zero; + private bool _IsPaused = false; + + internal DdeConversation(DdemlConversation conversation) + { + _DdemlObject = conversation; + _DdemlObject.StateChange += this.OnStateChange; + _Service = _DdemlObject.Service; + _Topic = _DdemlObject.Topic; + _Handle = _DdemlObject.Handle; + _IsPaused = _DdemlObject.IsPaused; + } + + internal DdemlConversation DdemlObject + { + get + { + lock (_LockObject) + { + return _DdemlObject; + } + } + } + + /// + /// This gets the service name associated with this conversation. + /// + public string Service + { + get + { + lock (_LockObject) + { + return _Service; + } + } + } + + /// + /// This gets the topic name associated with this conversation. + /// + public string Topic + { + get + { + lock (_LockObject) + { + return _Topic; + } + } + } + + /// + /// This gets the DDEML handle associated with this conversation. + /// + /// + /// + /// This can be used in any DDEML function requiring a conversation handle. + /// + /// + /// + /// Incorrect usage of the DDEML can cause this object to function incorrectly and can lead to resource leaks. + /// + /// + /// + public IntPtr Handle + { + get + { + lock (_LockObject) + { + return _Handle; + } + } + } + + /// + /// This gets a bool indicating whether this conversation is paused. + /// + public bool IsPaused + { + get + { + lock (_LockObject) + { + return _IsPaused; + } + } + } + + /// + /// This gets an application defined data object associated with this conversation. + /// + /// + /// Use this property to carry state information with the conversation. + /// + public object Tag + { + get + { + lock (_LockObject) + { + return _Tag; + } + } + set + { + lock (_LockObject) + { + _Tag = value; + } + } + } + + /// + /// This returns a string containing the current values of all properties. + /// + /// + /// A string containing the current values of all properties. + /// + public override string ToString() + { + string s = ""; + foreach (System.Reflection.PropertyInfo property in this.GetType().GetProperties()) + { + if (s.Length == 0) + { + s += property.Name + "=" + property.GetValue(this, null).ToString(); + } + else + { + s += " " + property.Name + "=" + property.GetValue(this, null).ToString(); + } + } + return s; + } + + private void OnStateChange(object sender, EventArgs args) + { + lock (_LockObject) + { + _Service = _DdemlObject.Service; + _Topic = _DdemlObject.Topic; + _Handle = _DdemlObject.Handle; + _IsPaused = _DdemlObject.IsPaused; + } + } + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/Public/Server/DdeServer.cs b/NDDE/NDde/Source/NDde/Public/Server/DdeServer.cs new file mode 100644 index 0000000..b22031c --- /dev/null +++ b/NDDE/NDde/Source/NDde/Public/Server/DdeServer.cs @@ -0,0 +1,1178 @@ +#region Copyright (c) 2005 by Brian Gideon (briangideon@yahoo.com) +/* Shared Source License for NDde + * + * This license governs use of the accompanying software ('Software'), and your use of the Software constitutes acceptance of this license. + * + * You may use the Software for any commercial or noncommercial purpose, including distributing derivative works. + * + * In return, we simply require that you agree: + * 1. Not to remove any copyright or other notices from the Software. + * 2. That if you distribute the Software in source code form you do so only under this license (i.e. you must include a complete copy of this + * license with your distribution), and if you distribute the Software solely in object form you only do so under a license that complies with + * this license. + * 3. That the Software comes "as is", with no warranties. None whatsoever. This means no express, implied or statutory warranty, including + * without limitation, warranties of merchantability or fitness for a particular purpose or any warranty of title or non-infringement. Also, + * you must pass this disclaimer on whenever you distribute the Software or derivative works. + * 4. That no contributor to the Software will be liable for any of those types of damages known as indirect, special, consequential, or incidental + * related to the Software or this license, to the maximum extent the law permits, no matter what legal theory its based on. Also, you must + * pass this limitation of liability on whenever you distribute the Software or derivative works. + * 5. That if you sue anyone over patents that you think may apply to the Software for a person's use of the Software, your license to the Software + * ends automatically. + * 6. That the patent rights, if any, granted in this license only apply to the Software, not to any derivative works you make. + * 7. That the Software is subject to U.S. export jurisdiction at the time it is licensed to you, and it may be subject to additional export or + * import laws in other places. You agree to comply with all such laws and regulations that may apply to the Software after delivery of the + * software to you. + * 8. That if you are an agency of the U.S. Government, (i) Software provided pursuant to a solicitation issued on or after December 1, 1995, is + * provided with the commercial license rights set forth in this license, and (ii) Software provided pursuant to a solicitation issued prior to + * December 1, 1995, is provided with Restricted Rights as set forth in FAR, 48 C.F.R. 52.227-14 (June 1987) or DFAR, 48 C.F.R. 252.227-7013 + * (Oct 1988), as applicable. + * 9. That your rights under this License end automatically if you breach it in any way. + * 10. That all rights not expressly granted to you in this license are reserved. + */ +#endregion +namespace NDde.Server +{ + using System; + using System.ComponentModel; + using System.Reflection; + using System.Threading; + using NDde.Advanced; + using NDde.Foundation; + using NDde.Foundation.Server; + using NDde.Foundation.Advanced; + using NDde.Properties; + + /// + /// This represents the server side of DDE conversations. + /// + /// + /// + /// + /// DDE conversations are established by specifying a service name and topic name pair. The service name is usually the name of the application + /// acting as a DDE server. A DDE server can respond to multiple service names, but most servers usually only respond to one. The topic name + /// is a logical context for data and is defined by the server application. A server can and usually does support many topic names. + /// + /// + /// After this object has registered its service name by calling the Register method clients can connect to it by specifying the service + /// name the server registered and a topic name that it supports. + /// + /// + /// Event methods are invoked on the thread hosting the DdeContext. All operations must be marshaled onto the thread hosting the + /// DdeContext associated with this object. Method calls will block until that thread becomes available. An exception will be generated + /// if the thread does not become available in a timely manner. + /// + /// + /// + /// The event methods must be overridden in a subclass as needed. + /// + /// + /// + /// + public abstract class DdeServer : IDisposable + { + private Object _LockObject = new Object(); + + private DdemlServer _DdemlObject = null; // This has lazy initialization through a property. + private DdeContext _Context = null; + + private bool _IsRegistered = false; // This is a cached DdemlServer property. + private string _Service = ""; // This is a cached DdemlServer property. + + + /// + /// + /// + /// + /// + /// This initializes a new instance of the DdeServer class that can register the specified service name. + /// + /// + /// The service name that this instance can register. + /// + /// + /// This is thown when service exceeds 255 characters.. + /// + /// + /// This is thrown when service is a null reference. + /// + public DdeServer(string service) + : this(service, DdeContext.GetDefault()) + { + } + + /// + /// This initializes a new instance of the DdeServer class that can register the specified service name and using the specified + /// synchronizing object. + /// + /// + /// The service name that this instance can register. + /// + /// + /// The synchronizing object to use for this instance. + /// + /// + /// This is thown when service exceeds 255 characters.. + /// + /// + /// This is thrown when service is a null reference. + /// + public DdeServer(string service, ISynchronizeInvoke synchronizingObject) + : this(service, DdeContext.GetDefault(synchronizingObject)) + { + } + + /// + /// This initializes a new instance of the DdeServer class that can register the specified service name and uses the specified + /// context. + /// + /// + /// The service name that this instance can register. + /// + /// + /// The context to use for execution. + /// + /// + /// This is thown when service exceeds 255 characters.. + /// + /// + /// This is thrown when service is a null reference. + /// + public DdeServer(string service, DdeContext context) + { + Service = service; + Context = context; + } + + /// + /// This unregisters service name and releases all resources held by this instance. + /// + public void Dispose() + { + Dispose(true); + } + + /// + /// This contains the implementation to release all resources held by this instance. + /// + /// + /// True if called by Dispose, false otherwise. + /// + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + ThreadStart method = delegate() + { + DdemlObject.Dispose(); + }; + + try + { + Context.Invoke(method); + } + catch + { + // Swallow any exception that occurs. + } + } + } + + /// + /// + /// + internal DdemlServer DdemlObject + { + get + { + lock (_LockObject) + { + if (_DdemlObject == null) + { + _DdemlObject = new MyDdemlServer(Service, Context.DdemlObject, this); + _DdemlObject.StateChange += new EventHandler(this.OnStateChange); + } + return _DdemlObject; + } + } + } + + /// + /// This gets the context associated with his instance. + /// + public DdeContext Context + { + get + { + lock (_LockObject) + { + return _Context; + } + } + private set + { + lock (_LockObject) + { + _Context = value; + } + } + } + + /// + /// This gets the service name associated with this server. + /// + public virtual string Service + { + get + { + lock (_LockObject) + { + return _Service; + } + } + private set + { + lock (_LockObject) + { + _Service = value; + } + } + } + + /// + /// This gets a bool indicating whether the service name is registered. + /// + public virtual bool IsRegistered + { + get + { + lock (_LockObject) + { + return _IsRegistered; + } + } + } + + /// + /// This registers the service name. + /// + /// + /// This is thrown when the server is already registered. + /// + /// + /// This is thrown when the service name could not be registered. + /// + public virtual void Register() + { + ThreadStart method = delegate() + { + DdemlObject.Register(); + }; + + try + { + Context.Invoke(method); + } + catch (DdemlException e) + { + throw new DdeException(e); + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + } + + /// + /// This unregisters the service name. + /// + /// + /// This is thrown when the server is not registered. + /// + public virtual void Unregister() + { + ThreadStart method = delegate() + { + DdemlObject.Unregister(); + }; + + try + { + Context.Invoke(method); + } + catch (DdemlException e) + { + throw new DdeException(e); + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + } + + /// + /// This notifies all clients that data has changed for the specified topic name and item name pair. + /// + /// + /// A topic name supported by this server. + /// + /// + /// An item name supported by this server. + /// + /// + /// This is thown when topic or item exceeds 255 characters.. + /// + /// + /// This is thrown when topic or item is a null reference. + /// + /// + /// This is thrown when the server is not registered. + /// + /// + /// This is thrown when the notification could not be posted. + /// + /// + /// Use an asterix to indicate that the topic name, item name, or both should be wild. + /// + public virtual void Advise(string topic, string item) + { + ThreadStart method = delegate() + { + DdemlObject.Advise(topic, item); + }; + + try + { + Context.Invoke(method); + } + catch (DdemlException e) + { + throw new DdeException(e); + } + catch (ArgumentException e) + { + throw e; + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + } + + /// + /// + /// + /// + /// Pausing a conversation causes this server to queue events until the conversation resumes. + /// + /// + /// + /// This pauses the specified conversation. + /// + /// + /// The conversation to pause. + /// + /// + /// This is thrown when conversation is a null reference. + /// + /// + /// This is thrown when the conversation is already paused or when the server is not registered. + /// + /// + /// This is thrown when the conversation could not be paused. + /// + public virtual void Pause(DdeConversation conversation) + { + ThreadStart method = delegate() + { + DdemlObject.Pause(conversation.DdemlObject); + }; + + try + { + Context.Invoke(method); + } + catch (DdemlException e) + { + throw new DdeException(e); + } + catch (ArgumentException e) + { + throw e; + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + } + + /// + /// This pauses all conversations. + /// + /// + /// This is thrown when the server is not registered. + /// + /// + /// This is thrown when the conversations could not be paused. + /// + /// + /// Pausing a conversation causes this object to queue events until the conversation resumes. + /// + public virtual void Pause() + { + ThreadStart method = delegate() + { + DdemlObject.Pause(); + }; + + try + { + Context.Invoke(method); + } + catch (DdemlException e) + { + throw new DdeException(e); + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + } + + /// + /// + /// + /// + /// + /// This resumes the specified conversation. + /// + /// + /// The conversation to resume. + /// + /// + /// This is thrown when conversation is a null reference. + /// + /// + /// This is thrown when the conversation is not paused or when the server is not registered. + /// + /// + /// This is thrown when the conversation could not be resumed. + /// + public virtual void Resume(DdeConversation conversation) + { + ThreadStart method = delegate() + { + DdemlObject.Resume(conversation.DdemlObject); + }; + + try + { + Context.Invoke(method); + } + catch (DdemlException e) + { + throw new DdeException(e); + } + catch (ArgumentException e) + { + throw e; + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + } + + /// + /// This resumes all conversations. + /// + /// + /// This is thrown when the server is not registered. + /// + /// + /// This is thrown when the conversations could not be resumed. + /// + public virtual void Resume() + { + ThreadStart method = delegate() + { + DdemlObject.Resume(); + }; + + try + { + Context.Invoke(method); + } + catch (DdemlException e) + { + throw new DdeException(e); + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + } + + /// + /// + /// + /// + /// + /// This terminates the specified conversation. + /// + /// + /// The conversation to terminate. + /// + /// + /// This is thrown when conversation is a null reference. + /// + /// + /// This is thrown when the server is not registered. + /// + /// + /// This is thrown when the conversation could not be terminated. + /// + public virtual void Disconnect(DdeConversation conversation) + { + ThreadStart method = delegate() + { + DdemlObject.Disconnect(conversation.DdemlObject); + }; + + try + { + Context.Invoke(method); + } + catch (DdemlException e) + { + throw new DdeException(e); + } + catch (ArgumentException e) + { + throw e; + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + } + + /// + /// This terminates all conversations. + /// + /// + /// This is thrown when the server is not registered. + /// + /// + /// This is thrown when the conversations could not be terminated. + /// + public virtual void Disconnect() + { + ThreadStart method = delegate() + { + DdemlObject.Disconnect(); + }; + + try + { + Context.Invoke(method); + } + catch (DdemlException e) + { + throw new DdeException(e); + } + catch (ObjectDisposedException e) + { + throw new ObjectDisposedException(this.GetType().ToString(), e); + } + } + + private void OnStateChange(object sender, EventArgs args) + { + lock (_LockObject) + { + _IsRegistered = _DdemlObject.IsRegistered; + _Service = _DdemlObject.Service; + } + } + + /// + /// This is invoked when a client attempts to initiate an advise loop. + /// + /// + /// The conversation associated with this event. + /// + /// + /// The item name associated with this event. + /// + /// + /// The format of the data. + /// + /// + /// True to allow the advise loop, false otherwise. + /// + /// + /// The default implementation accepts all advise loops. + /// + protected virtual bool OnStartAdvise(DdeConversation conversation, string item, int format) + { + return true; + } + + /// + /// This is invoked when a client terminates an advise loop. + /// + /// + /// The conversation associated with this event. + /// + /// + /// The item name associated with this event. + /// + protected virtual void OnStopAdvise(DdeConversation conversation, string item) + { + } + + /// + /// This is invoked when a client attempts to establish a conversation. + /// + /// + /// The topic name associated with this event. + /// + /// + /// True to allow the connection, false otherwise. + /// + /// + /// The default implementation accepts all connections. + /// + protected virtual bool OnBeforeConnect(string topic) + { + return true; + } + + /// + /// This is invoked when a client has successfully established a conversation. + /// + /// + /// The conversation associated with this event. + /// + protected virtual void OnAfterConnect(DdeConversation conversation) + { + } + + /// + /// This is invoked when a client terminates a conversation. + /// + /// + /// The conversation associated with this event. + /// + protected virtual void OnDisconnect(DdeConversation conversation) + { + } + + /// + /// This is invoked when a client sends a command. + /// + /// + /// The conversation associated with this event. + /// + /// + /// The command to be executed. + /// + /// + /// An ExecuteResult indicating the result. + /// + /// + /// The default implementation returns ExecuteResult.NotProcessed to the client. + /// + protected virtual ExecuteResult OnExecute(DdeConversation conversation, string command) + { + return ExecuteResult.NotProcessed; + } + + /// + /// This is invoked when a client sends data. + /// + /// + /// The conversation associated with this event. + /// + /// + /// The item name associated with this event. + /// + /// + /// The data associated with this event. + /// + /// + /// The format of the data. + /// + /// + /// A PokeResult indicating the result. + /// + /// + /// The default implementation returns PokeResult.NotProcessed to the client. + /// + protected virtual PokeResult OnPoke(DdeConversation conversation, string item, byte[] data, int format) + { + return PokeResult.NotProcessed; + } + + /// + /// + /// + /// + /// + /// This is invoked when a client attempts to request data. + /// + /// + /// The conversation associated with this event. + /// + /// + /// The item name associated with this event. + /// + /// + /// The format of the data. + /// + /// + /// A RequestResult indicating the result. + /// + /// + /// The default implementation returns RequestResult.NotProcessed to the client. + /// + protected virtual RequestResult OnRequest(DdeConversation conversation, string item, int format) + { + return RequestResult.NotProcessed; + } + + /// + /// This is invoked when the server is performing a hot advise. + /// + /// + /// The topic name associated with this event. + /// + /// + /// The item name associated with this event. + /// + /// + /// The format of the data. + /// + /// + /// The data that will be sent to the clients. + /// + /// + /// The default implementation sends nothing to the clients. + /// + protected virtual byte[] OnAdvise(string topic, string item, int format) + { + return null; + } + + /// + /// This is the return value of the OnExecute method. + /// + public struct ExecuteResult + { + /// + /// Return this value if the command was executed successfully. + /// + public static readonly ExecuteResult Processed = new ExecuteResult(DdemlServer.ExecuteResult.Processed); + + /// + /// Return this value if the command was not executed successfully. + /// + public static readonly ExecuteResult NotProcessed = new ExecuteResult(DdemlServer.ExecuteResult.NotProcessed); + + /// + /// Return this value if the server is too busy. + /// + public static readonly ExecuteResult TooBusy = new ExecuteResult(DdemlServer.ExecuteResult.TooBusy); + + /// + /// Return this value to pause the conversation and execute the command asynchronously. After the conversation has been resumed the + /// OnExecute method will run again. + /// + public static readonly ExecuteResult PauseConversation = new ExecuteResult(DdemlServer.ExecuteResult.PauseConversation); + + private DdemlServer.ExecuteResult _DdemlObject; + + private ExecuteResult(DdemlServer.ExecuteResult result) + { + _DdemlObject = result; + } + + /// + /// This determines whether two object instances are equal. + /// + /// + /// The object to compare with the current object. + /// + /// + /// True if the specified object is equal to the current object, false otherwise. + /// + public override bool Equals(object o) + { + if (o is ExecuteResult) + { + ExecuteResult r = (ExecuteResult)o; + return _DdemlObject == r._DdemlObject; + } + return false; + } + + /// + /// This returns a hash code for the object. + /// + /// + /// A hash code for the object. + /// + public override int GetHashCode() + { + return _DdemlObject.GetHashCode(); + } + + /// + /// This determines whether two ExecuteResult objects are equal. + /// + /// + /// The left hand side object. + /// + /// + /// The right hand side object. + /// + /// True if the two objects are equal, false otherwise. + /// + public static bool operator ==(ExecuteResult lhs, ExecuteResult rhs) + { + return lhs._DdemlObject == rhs._DdemlObject; + } + + /// + /// This determines whether two ExecuteResult objects are not equal. + /// + /// + /// The left hand side object. + /// + /// + /// The right hand side object. + /// + /// True if the two objects are not equal, false otherwise. + /// + public static bool operator !=(ExecuteResult lhs, ExecuteResult rhs) + { + return lhs._DdemlObject != rhs._DdemlObject; + } + + } // struct + + /// + /// This is the return value of the OnPoke method. + /// + public struct PokeResult + { + /// + /// Return this value if the poke was successful. + /// + public static readonly PokeResult Processed = new PokeResult(DdemlServer.PokeResult.Processed); + + /// + /// Return this value if the poke was not successful. + /// + public static readonly PokeResult NotProcessed = new PokeResult(DdemlServer.PokeResult.NotProcessed); + + /// + /// Return this value if the server is too busy. + /// + public static readonly PokeResult TooBusy = new PokeResult(DdemlServer.PokeResult.TooBusy); + + /// + /// Return this value to pause the conversation and execute the poke asynchronously. After the conversation has been resumed the + /// OnPoke method will run again. + /// + public static readonly PokeResult PauseConversation = new PokeResult(DdemlServer.PokeResult.PauseConversation); + + private DdemlServer.PokeResult _DdemlObject; + + private PokeResult(DdemlServer.PokeResult result) + { + _DdemlObject = result; + } + + /// + /// This determines whether two object instances are equal. + /// + /// + /// The object to compare with the current object. + /// + /// + /// True if the specified object is equal to the current object, false otherwise. + /// + public override bool Equals(object o) + { + if (o is PokeResult) + { + PokeResult r = (PokeResult)o; + return _DdemlObject == r._DdemlObject; + } + return false; + } + + /// + /// This returns a hash code for the object. + /// + /// + /// A hash code for the object. + /// + public override int GetHashCode() + { + return _DdemlObject.GetHashCode(); + } + + /// + /// This determines whether two PokeResult objects are equal. + /// + /// + /// The left hand side object. + /// + /// + /// The right hand side object. + /// + /// True if the two objects are equal, false otherwise. + /// + public static bool operator ==(PokeResult lhs, PokeResult rhs) + { + return lhs._DdemlObject == rhs._DdemlObject; + } + + /// + /// This determines whether two ExecuteResult objects are not equal. + /// + /// + /// The left hand side object. + /// + /// + /// The right hand side object. + /// + /// True if the two objects are not equal, false otherwise. + /// + public static bool operator !=(PokeResult lhs, PokeResult rhs) + { + return lhs._DdemlObject != rhs._DdemlObject; + } + + } // struct + + /// + /// This is the return value of the OnRequest method. + /// + public struct RequestResult + { + internal static readonly RequestResult Processed = new RequestResult(DdemlServer.RequestResult.Processed); + + /// + /// Return this value if the request was not successful. + /// + public static readonly RequestResult NotProcessed = new RequestResult(DdemlServer.RequestResult.NotProcessed); + + /// + /// Return this value to pause the conversation and execute the request asynchronously. After the conversation has been resumed the + /// OnRequest method will run again. + /// + public static readonly RequestResult PauseConversation = new RequestResult(DdemlServer.RequestResult.PauseConversation); + + private DdemlServer.RequestResult _DdemlObject; + + private RequestResult(DdemlServer.RequestResult result) + { + _DdemlObject = result; + } + + /// + /// This initializes the RequestResult struct with the data to return to the client. + /// + /// + /// The data to return to the client. + /// + public RequestResult(byte[] data) + { + _DdemlObject = DdemlServer.RequestResult.Processed; + _DdemlObject.Data = data; + } + + /// + /// The data to send to the client application. + /// + public byte[] Data + { + get { return _DdemlObject.Data; } + set { _DdemlObject.Data = value; } + } + + /// + /// This determines whether two object instances are equal. + /// + /// + /// The object to compare with the current object. + /// + /// + /// True if the specified object is equal to the current object, false otherwise. + /// + public override bool Equals(object o) + { + if (o is RequestResult) + { + RequestResult r = (RequestResult)o; + return _DdemlObject == r._DdemlObject; + } + return false; + } + + /// + /// This returns a hash code for the object. + /// + /// + /// A hash code for the object. + /// + public override int GetHashCode() + { + return _DdemlObject.GetHashCode(); + } + + /// + /// This determines whether two RequestResult objects are equal. + /// + /// + /// The left hand side object. + /// + /// + /// The right hand side object. + /// + /// True if the two objects are equal, false otherwise. + /// + public static bool operator ==(RequestResult lhs, RequestResult rhs) + { + return lhs._DdemlObject == rhs._DdemlObject; + } + + /// + /// This determines whether two ExecuteResult objects are not equal. + /// + /// + /// The left hand side object. + /// + /// + /// The right hand side object. + /// + /// True if the two objects are not equal, false otherwise. + /// + public static bool operator !=(RequestResult lhs, RequestResult rhs) + { + return lhs._DdemlObject != rhs._DdemlObject; + } + + } // struct + + private sealed class MyDdemlServer : DdemlServer + { + private DdeServer _Parent = null; + + public MyDdemlServer(string service, DdemlContext context, DdeServer parent) : base(service, context) + { + _Parent = parent; + } + + protected override bool OnStartAdvise(DdemlConversation conversation, string item, int format) + { + return _Parent.OnStartAdvise((DdeConversation)conversation.Tag, item, format); + } + + protected override void OnStopAdvise(DdemlConversation conversation, string item) + { + _Parent.OnStopAdvise((DdeConversation)conversation.Tag, item); + } + + protected override bool OnBeforeConnect(string topic) + { + return _Parent.OnBeforeConnect(topic); + } + + protected override void OnAfterConnect(DdemlConversation conversation) + { + DdeConversation c = new DdeConversation(conversation); + conversation.Tag = c; + _Parent.OnAfterConnect(c); + } + + protected override void OnDisconnect(DdemlConversation conversation) + { + _Parent.OnDisconnect((DdeConversation)conversation.Tag); + } + + protected override ExecuteResult OnExecute(DdemlConversation conversation, string command) + { + DdeServer.ExecuteResult result = _Parent.OnExecute((DdeConversation)conversation.Tag, command); + if (result == DdeServer.ExecuteResult.NotProcessed) + { + return ExecuteResult.NotProcessed; + } + if (result == DdeServer.ExecuteResult.PauseConversation) + { + return ExecuteResult.PauseConversation; + } + if (result == DdeServer.ExecuteResult.Processed) + { + return ExecuteResult.Processed; + } + if (result == DdeServer.ExecuteResult.TooBusy) + { + return ExecuteResult.TooBusy; + } + return ExecuteResult.NotProcessed; + } + + protected override PokeResult OnPoke(DdemlConversation conversation, string item, byte[] data, int format) + { + DdeServer.PokeResult result = _Parent.OnPoke((DdeConversation)conversation.Tag, item, data, format); + if (result == DdeServer.PokeResult.NotProcessed) + { + return PokeResult.NotProcessed; + } + if (result == DdeServer.PokeResult.PauseConversation) + { + return PokeResult.PauseConversation; + } + if (result == DdeServer.PokeResult.Processed) + { + return PokeResult.Processed; + } + if (result == DdeServer.PokeResult.TooBusy) + { + return PokeResult.TooBusy; + } + return PokeResult.NotProcessed; + } + + protected override RequestResult OnRequest(DdemlConversation conversation, string item, int format) + { + DdeServer.RequestResult result = _Parent.OnRequest((DdeConversation)conversation.Tag, item, format); + if (result == DdeServer.RequestResult.NotProcessed) + { + return RequestResult.NotProcessed; + } + if (result == DdeServer.RequestResult.PauseConversation) + { + return RequestResult.PauseConversation; + } + if (result == DdeServer.RequestResult.Processed) + { + return new RequestResult(result.Data); + } + return RequestResult.NotProcessed; + } + + protected override byte[] OnAdvise(string topic, string item, int format) + { + return _Parent.OnAdvise(topic, item, format); + } + + } // class + + } // class + +} // namespace \ No newline at end of file diff --git a/NDDE/NDde/Source/NDde/packages.config b/NDDE/NDde/Source/NDde/packages.config new file mode 100644 index 0000000..0aee17e --- /dev/null +++ b/NDDE/NDde/Source/NDde/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/NDDE/NDde/Source/Reference/nunit.framework.dll b/NDDE/NDde/Source/Reference/nunit.framework.dll new file mode 100644 index 0000000..989ec2a Binary files /dev/null and b/NDDE/NDde/Source/Reference/nunit.framework.dll differ diff --git a/OrbitTools/OrbitTools/Orbit.Core/Coord.cs b/OrbitTools/OrbitTools/Orbit.Core/Coord.cs new file mode 100644 index 0000000..a93e6d3 --- /dev/null +++ b/OrbitTools/OrbitTools/Orbit.Core/Coord.cs @@ -0,0 +1,321 @@ +// +// Coord.cs +// +// Copyright (c) 2003-2012 Michael F. Henry +// Version 10/2012 +// +using System; +using System.Globalization; + +namespace Zeptomoby.OrbitTools +{ + /// + /// Class to encapsulate geocentric coordinates. + /// + public class Geo + { + #region Properties + + /// + /// Latitude, in radians. A negative value indicates latitude south. + /// + public double LatitudeRad { get; set; } + + /// + /// Longitude, in radians. A negative value indicates longitude west. + /// + public double LongitudeRad { get; set; } + + /// + /// Latitude, in degrees. A negative value indicates latitude south. + /// + public double LatitudeDeg { get { return Globals.ToDegrees(LatitudeRad); } } + + /// + /// Longitude, in degrees. A negative value indicates longitude west. + /// + public double LongitudeDeg { get { return Globals.ToDegrees(LongitudeRad); } } + + /// + /// Altitude, in kilometers, above the ellipsoid model. + /// + public double Altitude { get; set; } + + #endregion + + #region Construction + + /// + /// Creates a Geo object from a source Geo object. + /// + /// The source Geo object. + public Geo(Geo geo) + { + LatitudeRad = geo.LatitudeRad; + LongitudeRad = geo.LongitudeRad; + Altitude = geo.Altitude; + } + + /// + /// Creates a new instance of the class with the given components. + /// + /// Latitude, in radians. Negative values indicate + /// latitude south. + /// Longitude, in radians. Negative value indicate longitude + /// west. + /// Altitude above the ellipsoid model, in kilometers. + public Geo(double radLat, double radLon, double kmAlt) + { + LatitudeRad = radLat; + LongitudeRad = radLon; + Altitude = kmAlt; + } + + /// + /// Creates a new instance of the class given ECI coordinates. + /// + /// The ECI coordinates. + /// The Julian date. + public Geo(Eci eci, Julian date) + :this(eci.Position, + (Globals.AcTan(eci.Position.Y, eci.Position.X) - date.ToGmst()) % Globals.TwoPi) + { + } + + /// + /// Creates a new instance of the class given XYZ coordinates. + /// + private Geo(Vector pos, double theta) + { + theta = theta % Globals.TwoPi; + + if (theta < 0.0) + { + // "wrap" negative modulo + theta += Globals.TwoPi; + } + + double r = Math.Sqrt(Globals.Sqr(pos.X) + Globals.Sqr(pos.Y)); + double e2 = Globals.F * (2.0 - Globals.F); + double lat = Globals.AcTan(pos.Z, r); + + const double DELTA = 1.0e-07; + double phi; + double c; + + do + { + phi = lat; + c = 1.0 / Math.Sqrt(1.0 - e2 * Globals.Sqr(Math.Sin(phi))); + lat = Globals.AcTan(pos.Z + Globals.Xkmper * c * e2 * Math.Sin(phi), r); + } + while (Math.Abs(lat - phi) > DELTA); + + LatitudeRad = lat; + LongitudeRad = theta; + Altitude = (r / Math.Cos(lat)) - Globals.Xkmper * c; + } + + #endregion + + #region ToString + + /// + /// Converts to a string representation of the form "38.0N 045.0W 500m". + /// + /// The formatted string. + public override string ToString() + { + bool latNorth = (LatitudeRad >= 0.0); + bool lonEast = (LongitudeRad >= 0.0); + + // latitude in degrees + string str = string.Format(CultureInfo.CurrentCulture, "{0:00.0}{1} ", + Math.Abs(LatitudeDeg), + (latNorth ? 'N' : 'S')); + // longitude in degrees + str += string.Format(CultureInfo.CurrentCulture, "{0:000.0}{1} ", + Math.Abs(LongitudeDeg), + (lonEast ? 'E' : 'W')); + // elevation in meters + str += string.Format(CultureInfo.CurrentCulture, "{0:F0}m", Altitude * 1000.0); + + return str; + } + + #endregion + } + + /// + /// Class to encapsulate a geocentric coordinate and associated time. + /// + public sealed class GeoTime : Geo + { + #region Properties + + /// + /// The time associated with the coordinates. + /// + public Julian Date { get; private set; } + + #endregion + + #region Construction + + /// + /// Standard constructor. + /// + /// Latitude, in radians. Negative values indicate + /// latitude south. + /// Longitude, in radians. Negative value indicate longitude + /// west. + /// Altitude above the ellipsoid model, in kilometers. + /// The time associated with the coordinates. + public GeoTime(double radLat, double radLon, double kmAlt, Julian date) + :base(radLat, radLon, kmAlt) + { + Date = date; + } + + /// + /// Constructor accepting Geo and Julian objects. + /// + /// The Geo object. + /// The Julian date. + public GeoTime(Geo geo, Julian date) + :base(geo) + { + Date = date; + } + + /// + /// Creates a new instance of the class from ECI-time information. + /// + /// The ECI-time coordinate pair. + /// The earth ellipsoid model. + public GeoTime(EciTime eci) + : base(eci, eci.Date) + { + Date = eci.Date; + } + + /// + /// Creates a new instance of the class from ECI coordinates. + /// + /// The ECI coordinates. + /// The Julian date. + public GeoTime(Eci eci, Julian date) + : base(eci, date) + { + Date = date; + } + + #endregion + } + + /// + /// Class to encapsulate topo-centric coordinates. + /// + public class Topo + { + #region Properties + + /// + /// The azimuth, in radians. + /// + public double AzimuthRad { get; set; } + + /// + /// The elevation, in radians. + /// + public double ElevationRad { get; set; } + + /// + /// The azimuth, in degrees. + /// + public double AzimuthDeg { get { return Globals.ToDegrees(AzimuthRad); } } + + /// + /// The elevation, in degrees. + /// + public double ElevationDeg { get { return Globals.ToDegrees(ElevationRad); } } + + /// + /// The range, in kilometers. + /// + public double Range { get; set; } + + /// + /// The range rate, in kilometers per second. + /// A negative value means "towards observer". + /// + public double RangeRate { get; set; } + + #endregion + + #region Construction + + /// + /// Creates a new instance of the class from the given components. + /// + /// Azimuth, in radians. + /// Elevation, in radians. + /// Range, in kilometers. + /// Range rate, in kilometers per second. A negative + /// range rate means "towards the observer". + public Topo(double radAz, double radEl, double range, double rangeRate) + { + AzimuthRad = radAz; + ElevationRad = radEl; + Range = range; + RangeRate = rangeRate; + } + + #endregion + } + + /// + /// Class to encapsulate topo-centric coordinates and a time. + /// + public sealed class TopoTime : Topo + { + #region Properties + + /// + /// The time associated with the coordinates. + /// + public Julian Date { get; private set; } + + #endregion + + #region Construction + + /// + /// Creates an instance of the class from topo and time information. + /// + /// + /// + public TopoTime(Topo topo, Julian date) + :base(topo.AzimuthRad, topo.ElevationRad, topo.Range, topo.RangeRate) + { + Date = date; + } + + /// + /// Creates a new instance of the class from the given components. + /// + /// Azimuth, in radians. + /// Elevation, in radians. + /// Range, in kilometers. + /// Range rate, in kilometers per second. A negative + /// range rate means "towards the observer". + /// The time associated with the coordinates. + public TopoTime(double radAz, double radEl, double range, double rangeRate, Julian date) + :base(radAz, radEl, range, rangeRate) + { + Date = date; + } + + #endregion + } +} \ No newline at end of file diff --git a/OrbitTools/OrbitTools/Orbit.Core/Eci.cs b/OrbitTools/OrbitTools/Orbit.Core/Eci.cs new file mode 100644 index 0000000..14cf9be --- /dev/null +++ b/OrbitTools/OrbitTools/Orbit.Core/Eci.cs @@ -0,0 +1,197 @@ +// +// Eci.cs +// +// Copyright (c) 2003-2012 Michael F. Henry +// Version 09/2012 +// +using System; + +namespace Zeptomoby.OrbitTools +{ + /// + /// Encapsulates an Earth Centered Inertial coordinate position/velocity. + /// + public class Eci + { + #region Properties + + public Vector Position { get; protected set; } + public Vector Velocity { get; protected set; } + + #endregion + + #region Construction + + /// + /// Creates a new instance of the class zero position and zero velocity. + /// + public Eci() + : this(new Vector(), new Vector()) + { + } + + /// + /// Creates a new instance of the class from XYZ coordinates. + /// + /// The XYZ coordinates. + public Eci(Vector pos) + : this(pos, new Vector()) + { + } + + /// + /// Creates a new instance of the class with the given position and + /// velocity components. + /// + /// The position vector. + /// The velocity vector. + public Eci(Vector pos, Vector vel) + { + Position = pos; + Velocity = vel; + } + + /// + /// Creates a new instance of the class from ECI coordinates. + /// + /// The ECI coordinates. + public Eci(Eci eci) + { + Position = new Vector(eci.Position); + Velocity = new Vector(eci.Velocity); + } + + /// + /// Creates a instance of the class from geodetic coordinates. + /// + /// The geocentric coordinates. + /// The Julian date. + /// + /// Assumes the Earth is an oblate spheroid. + /// Reference: The 1992 Astronomical Almanac, page K11 + /// Reference: www.celestrak.com (Dr. T.S. Kelso) + /// + public Eci(Geo geo, Julian date) + { + double lat = geo.LatitudeRad; + double lon = geo.LongitudeRad; + double alt = geo.Altitude; + + // Calculate Local Mean Sidereal Time (theta) + double theta = date.ToLmst(lon); + double c = 1.0 / Math.Sqrt(1.0 + Globals.F * (Globals.F - 2.0) * + Globals.Sqr(Math.Sin(lat))); + double s = Globals.Sqr(1.0 - Globals.F) * c; + double achcp = (Globals.Xkmper * c + alt) * Math.Cos(lat); + + Position = new Vector(); + + Position.X = achcp * Math.Cos(theta); // km + Position.Y = achcp * Math.Sin(theta); // km + Position.Z = (Globals.Xkmper * s + alt) * Math.Sin(lat); // km + Position.W = Math.Sqrt(Globals.Sqr(Position.X) + + Globals.Sqr(Position.Y) + + Globals.Sqr(Position.Z)); // range, km + + Velocity = new Vector(); + double mfactor = Globals.TwoPi * (Globals.OmegaE / Globals.SecPerDay); + + Velocity.X = -mfactor * Position.Y; // km / sec + Velocity.Y = mfactor * Position.X; // km / sec + Velocity.Z = 0.0; // km / sec + Velocity.W = Math.Sqrt(Globals.Sqr(Velocity.X) + // range rate km/sec^2 + Globals.Sqr(Velocity.Y)); + } + #endregion + + /// + /// Scale the position vector by a factor. + /// + public void ScalePosVector(double factor) + { + Position.Mul(factor); + } + + /// + /// Scale the velocity vector by a factor. + /// + public void ScaleVelVector(double factor) + { + Velocity.Mul(factor); + } + + /// + /// Returns a string representation of the coordinate and + /// velocity XYZ values. + /// + /// The formatted string. + public override string ToString() + { + return string.Format("km:({0:F0}, {1:F0}, {2:F0}) km/s:({3:F1}, {4:F1}, {5:F1})", + Position.X, Position.Y, Position.Z, + Velocity.X, Velocity.Y, Velocity.Z); + } + } + + /// + /// Encapsulates an Earth Centered Inertial coordinate and + /// associated time. + /// + public class EciTime : Eci + { + #region Properties + + /// + /// The time associated with the ECI coordinates. + /// + public Julian Date { get; protected set; } + + #endregion + + #region Construction + + /// + /// Creates an instance of the class with the given position, velocity, and time. + /// + /// The position vector. + /// The velocity vector. + /// The time associated with the position. + public EciTime(Vector pos, Vector vel, Julian date) + : base(pos, vel) + { + Date = date; + } + + /// + /// Creates a new instance of the class from ECI-time coordinates. + /// + /// The ECI coordinates. + /// The time associated with the ECI coordinates. + public EciTime(Eci eci, Julian date) + : this(eci.Position, eci.Velocity, date) + { + } + + /// + /// Creates a new instance of the class from geodetic coordinates. + /// + /// The geodetic coordinates. + /// The time associated with the ECI coordinates. + public EciTime(Geo geo, Julian date) + : base(geo, date) + { + Date = date; + } + + /// + /// Creates a new instance of the class from geodetic-time coordinates. + /// + /// The geodetic-time coordinates. + public EciTime(GeoTime geo) + :this(geo, geo.Date) + { + } + + #endregion + } +} diff --git a/OrbitTools/OrbitTools/Orbit.Core/Globals.cs b/OrbitTools/OrbitTools/Orbit.Core/Globals.cs new file mode 100644 index 0000000..6195bc2 --- /dev/null +++ b/OrbitTools/OrbitTools/Orbit.Core/Globals.cs @@ -0,0 +1,167 @@ +// +// Globals.cs +// +// Copyright (c) 2003-2010 Michael F. Henry +// +using System; +using System.Runtime.Serialization; + +namespace Zeptomoby.OrbitTools +{ + /// + /// Numerical constants. + /// + public static class Globals + { + #region Constants + + public const double Pi = 3.141592653589793; + public const double TwoPi = 2.0 * Pi; + public const double RadsPerDegree = Pi / 180.0; + public const double DegreesPerRad = 180.0 / Pi; + + public const double Gm = 398601.2; // Earth gravitational constant, km^3/sec^2 + public const double GeoSyncAlt = 42241.892; // km + public const double EarthDiam = 12800.0; // km + public const double DaySidereal = (23 * 3600) + (56 * 60) + 4.09; // sec + public const double DaySolar = (24 * 3600); // sec + + public const double Ae = 1.0; + public const double Au = 149597870.0; // Astronomical unit (km) (IAU 76) + public const double Sr = 696000.0; // Solar radius (km) (IAU 76) + public const double Xkmper = 6378.135; // Earth equatorial radius - kilometers (WGS '72) + public const double F = 1.0 / 298.26; // Earth flattening (WGS '72) + public const double Ge = 398600.8; // Earth gravitational constant (WGS '72) + public const double J2 = 1.0826158E-3; // J2 harmonic (WGS '72) + public const double J3 = -2.53881E-6; // J3 harmonic (WGS '72) + public const double J4 = -1.65597E-6; // J4 harmonic (WGS '72) + public const double Ck2 = J2 / 2.0; + public const double Ck4 = -3.0 * J4 / 8.0; + public const double Xj3 = J3; + public const double Qo = Globals.Ae + 120.0 / Globals.Xkmper; + public const double S = Globals.Ae + 78.0 / Globals.Xkmper; + public const double MinPerDay = 1440.0; // Minutes per day (solar) + public const double SecPerDay = 86400.0; // Seconds per day (solar) + public const double OmegaE = 1.00273790934; // Earth rotation per sidereal day + public static double Xke = Math.Sqrt(3600.0 * Ge / + (Globals.Xkmper * Globals.Xkmper * Globals.Xkmper)); // sqrt(ge) ER^3/min^2 + public static double Qoms2t = Math.Pow((Qo - Globals.S), 4); //(QO - S)^4 ER^4 + + #endregion + + #region Utility + + // /////////////////////////////////////////////////////////////////////////// + public static double Sqr(double x) + { + return (x * x); + } + + // /////////////////////////////////////////////////////////////////////////// + public static double Fmod2p(double arg) + { + double modu = (arg % TwoPi); + + if (modu < 0.0) + { + modu += TwoPi; + } + + return modu; + } + + // /////////////////////////////////////////////////////////////////////////// + // Globals.AcTan() + // ArcTangent of sin(x) / cos(x). The advantage of this function over arctan() + // is that it returns the correct quadrant of the angle. + public static double AcTan(double sinx, double cosx) + { + double ret; + + if (cosx == 0.0) + { + if (sinx > 0.0) + { + ret = Pi / 2.0; + } + else + { + ret = 3.0 * Pi / 2.0; + } + } + else + { + if (cosx > 0.0) + { + ret = Math.Atan(sinx / cosx); + } + else + { + ret = Pi + Math.Atan(sinx / cosx); + } + } + + return ret; + } + + // /////////////////////////////////////////////////////////////////////////// + public static double ToDegrees(double radians) + { + return radians * DegreesPerRad; + } + + // /////////////////////////////////////////////////////////////////////////// + public static double ToRadians(double degrees) + { + return degrees * RadsPerDegree; + } + + #endregion + } + + #region Exceptions + + [Serializable] + public class PropagationException : Exception + { + public PropagationException() { } + public PropagationException(string message) : base(message) { } + public PropagationException(string message, Exception inner) : base(message, inner) { } + + // Constructor used for deserialization. + protected PropagationException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } + + [Serializable] + public sealed class DecayException : PropagationException + { + /// + /// The GMT when the satellite orbit decays. + /// + public DateTime DecayTime { get; private set; } + + /// + /// The name of the satellite whose orbit decayed. + /// + public string SatelliteName { get; private set; } + + public DecayException() { } + public DecayException(string message) : base(message) { } + public DecayException(string message, Exception inner) : base(message, inner) { } + public DecayException(DateTime decayTime, string satelliteName) + { + DecayTime = decayTime; + SatelliteName = satelliteName; + } + + // Constructor used for deserialization. + private DecayException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } + #endregion +} diff --git a/OrbitTools/OrbitTools/Orbit.Core/Julian.cs b/OrbitTools/OrbitTools/Orbit.Core/Julian.cs new file mode 100644 index 0000000..83734ef --- /dev/null +++ b/OrbitTools/OrbitTools/Orbit.Core/Julian.cs @@ -0,0 +1,204 @@ +// +// Julian.cs +// +// This class encapsulates a Julian date system where the day starts at noon. +// Some Julian dates: +// 01/01/1990 00:00 UTC - 2447892.5 +// 01/01/1990 12:00 UTC - 2447893.0 +// 01/01/2000 00:00 UTC - 2451544.5 +// 01/01/2001 00:00 UTC - 2451910.5 +// +// The Julian day begins at noon, which allows astronomers to have the +// same date in a single observing session. +// +// References: +// "Astronomical Formulae for Calculators", Jean Meeus, 4th Edition +// "Satellite Communications", Dennis Roddy, 2nd Edition, 1995. +// "Spacecraft Attitude Determination and Control", James R. Wertz, 1984 +// +// Copyright (c) 2003-2012 Michael F. Henry +// Version 05/2012 +// +using System; + +namespace Zeptomoby.OrbitTools +{ + /// + /// Encapsulates a Julian date. + /// + public class Julian + { + private const double EPOCH_JAN0_12H_1900 = 2415020.0; // Dec 31.5 1899 = Dec 31 1899 12h UTC + private const double EPOCH_JAN1_00H_1900 = 2415020.5; // Jan 1.0 1900 = Jan 1 1900 00h UTC + private const double EPOCH_JAN1_12H_1900 = 2415021.0; // Jan 1.5 1900 = Jan 1 1900 12h UTC + private const double EPOCH_JAN1_12H_2000 = 2451545.0; // Jan 1.5 2000 = Jan 1 2000 12h UTC + + private double m_Date; // Julian date + private int m_Year; // Year including century + private double m_Day; // Day of year, 1.0 = Jan 1 00h + + #region Construction + + /// + /// Create a Julian date object from a DateTime object. The time + /// contained in the DateTime object is assumed to be UTC. + /// + /// The UTC time to convert. + public Julian(DateTime utc) + { + double day = utc.DayOfYear + + (utc.Hour + + ((utc.Minute + + ((utc.Second + (utc.Millisecond / 1000.0)) / 60.0)) / 60.0)) / 24.0; + + Initialize(utc.Year, day); + } + + /// + /// Create a Julian date object given a year and day-of-year. + /// + /// The year, including the century (i.e., 2012). + /// Day of year (1 means January 1, etc.). + /// + /// The fractional part of the day value is the fractional portion of + /// the day. + /// Examples: + /// day = 1.0 Jan 1 00h + /// day = 1.5 Jan 1 12h + /// day = 2.0 Jan 2 00h + /// + public Julian(int year, double doy) + { + Initialize(year, doy); + } + + #endregion + + #region Properties + + public double Date { get { return m_Date; } } + + public double FromJan0_12h_1900() { return m_Date - EPOCH_JAN0_12H_1900; } + public double FromJan1_00h_1900() { return m_Date - EPOCH_JAN1_00H_1900; } + public double FromJan1_12h_1900() { return m_Date - EPOCH_JAN1_12H_1900; } + public double FromJan1_12h_2000() { return m_Date - EPOCH_JAN1_12H_2000; } + + #endregion + + /// + /// Calculates the time difference between two Julian dates. + /// + /// Julian date. + /// + /// A TimeSpan representing the time difference between the two dates. + /// + public TimeSpan Diff(Julian date) + { + const double TICKS_PER_DAY = 8.64e11; // 1 tick = 100 nanoseconds + return new TimeSpan((long)((m_Date - date.m_Date) * TICKS_PER_DAY)); + } + + /// + /// Initialize the Julian date object. + /// + /// The year, including the century. + /// Day of year (1 means January 1, etc.) + /// + /// The first day of the year, Jan 1, is day 1.0. Noon on Jan 1 is + /// represented by the day value of 1.5, etc. + /// + protected void Initialize(int year, double doy) + { + // Arbitrary years used for error checking + if (year < 1900 || year > 2100) + { + throw new ArgumentOutOfRangeException("year"); + } + + // The last day of a leap year is day 366 + if (doy < 1.0 || doy >= 367.0) + { + throw new ArgumentOutOfRangeException("doy"); + } + + m_Year = year; + m_Day = doy; + + // Now calculate Julian date + // Ref: "Astronomical Formulae for Calculators", Jean Meeus, pages 23-25 + + year--; + + // Centuries are not leap years unless they divide by 400 + int A = (year / 100); + int B = 2 - A + (A / 4); + + double NewYears = (int)(365.25 * year) + + (int)(30.6001 * 14) + + 1720994.5 + B; + + m_Date = NewYears + doy; + } + + /// + /// Calculate Greenwich Mean Sidereal Time for the Julian date. + /// + /// + /// The angle, in radians, measuring eastward from the Vernal Equinox to + /// the prime meridian. This angle is also referred to as "ThetaG" + /// (Theta GMST). + /// + public double ToGmst() + { + // References: + // The 1992 Astronomical Almanac, page B6. + // Explanatory Supplement to the Astronomical Almanac, page 50. + // Orbital Coordinate Systems, Part III, Dr. T.S. Kelso, + // Satellite Times, Nov/Dec 1995 + + double UT = (m_Date + 0.5) % 1.0; + double TU = (FromJan1_12h_2000() - UT) / 36525.0; + + double GMST = 24110.54841 + TU * + (8640184.812866 + TU * (0.093104 - TU * 6.2e-06)); + + GMST = (GMST + Globals.SecPerDay * Globals.OmegaE * UT) % Globals.SecPerDay; + + if (GMST < 0.0) + { + GMST += Globals.SecPerDay; // "wrap" negative modulo value + } + + return (Globals.TwoPi * (GMST / Globals.SecPerDay)); + } + + /// + /// Calculate Local Mean Sidereal Time for this Julian date at the given + /// longitude. + /// + /// The longitude, in radians, measured west from Greenwich. + /// + /// The angle, in radians, measuring eastward from the Vernal Equinox to + /// the given longitude. + /// + public double ToLmst(double lon) + { + return (ToGmst() + lon) % Globals.TwoPi; + } + + /// + /// Returns a UTC DateTime object that corresponds to this Julian date. + /// + /// A DateTime object in UTC. + public DateTime ToTime() + { + // Jan 1 + DateTime dt = new DateTime(m_Year, 1, 1); + + // m_Day = 1 = Jan1 + dt = dt.AddDays(m_Day - 1.0); + + return dt; + } + } +} diff --git a/OrbitTools/OrbitTools/Orbit.Core/Orbit.Core.csproj b/OrbitTools/OrbitTools/Orbit.Core/Orbit.Core.csproj new file mode 100644 index 0000000..7cde51b --- /dev/null +++ b/OrbitTools/OrbitTools/Orbit.Core/Orbit.Core.csproj @@ -0,0 +1,134 @@ + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {99510ED5-99E0-405D-BCAC-9159A7426D61} + Library + Properties + Zeptomoby.OrbitTools + Zeptomoby.OrbitTools.Core + v4.0 + 512 + + + 3.5 + + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + prompt + false + false + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + prompt + false + false + + + + ..\..\..\packages\Newtonsoft.Json.12.0.1\lib\net40\Newtonsoft.Json.dll + True + + + + ..\..\..\packages\System.Data.SQLite.Core.1.0.110.0\lib\net40\System.Data.SQLite.dll + True + + + + + + + + + + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + + + + + + + + + Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}". + + + + + \ No newline at end of file diff --git a/OrbitTools/OrbitTools/Orbit.Core/Properties/AssemblyInfo.cs b/OrbitTools/OrbitTools/Orbit.Core/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..dea46b1 --- /dev/null +++ b/OrbitTools/OrbitTools/Orbit.Core/Properties/AssemblyInfo.cs @@ -0,0 +1,20 @@ +using System; +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("Zeptomoby.OrbitTools.Core")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Zeptomoby.com")] +[assembly: AssemblyProduct("OrbitTools Library - Public (unlicensed) Edition")] +[assembly: AssemblyCopyright("Copyright 2003-2013 Michael F. Henry")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: ComVisible(false)] +[assembly: CLSCompliant(true)] + +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyVersion ("1.1.0.0")] +[assembly: AssemblyFileVersion("1.1.0.0")] + + diff --git a/OrbitTools/OrbitTools/Orbit.Core/Properties/revHistory.txt b/OrbitTools/OrbitTools/Orbit.Core/Properties/revHistory.txt new file mode 100644 index 0000000..ddb2cd4 --- /dev/null +++ b/OrbitTools/OrbitTools/Orbit.Core/Properties/revHistory.txt @@ -0,0 +1,211 @@ + +Revision History for the OrbitTools.Core assembly - Public Edition +Copyright (c) Michael F. Henry +www.zeptomoby.com/satellites + +Version 1.1.0.0 2012-10-27 + + Added classes EciTime, GeoTime, and TopoTime which encapsulate + coordinate information along with a Julian date. This formalizes the + connection between coordinates and an associated time, and allows use + of the base classes Eci, Geo, and Topo when the time component + is not needed. + Added new constructor Geo.Geo(Geo geo). + Added new constructor Site.Site(Geo geo). + Added RotateX(), RotateY(), RotateZ(), and Translate() methods to + class Vector. + + Breaking changes: + + 1. Renamed class CoordGeo to Geo, and removed default constructor. + 2. Renamed class CoordTopo to Topo, and removed default constructor. + 3. Class Eci no longer contains a Date property (use new class EciTime). + 4. Method Eci.ToGeo() removed; use class Geo constructor. + 5. Method Site.GetPosition() returns type EciTime. + 6. IOrbit.GetPosition() returns type EciTime (2 overloads). + 7. Method Site.GetLookAngle() returns type TopoTime. + +Version 1.0.6.0 2012-08-24 + + Added ToString() to class Eci. + Added ToString() and degree-based lat/lon properties to class CoordGeo. + Added degree-based azimuth/elevation properties to class CoordTopo. + + Breaking changes: + + 1. Renamed CoordTopo.Azimuth to AzimuthRad. + 2. Renamed CoordTopo.Elevation to ElevationRad. + +Version 1.0.5.0 2012-06-21 + + Fixed the string-based property Tle.Epoch which was dropping leading + zeros from single-digit year and day values. Thanks to T. Doyle for + reporting this issue. + + Updated the string-based Tle.GetField() to remove all leading/trailing + whitespace from returned strings. + + Added methods Site.LatitudeDeg() and Site.LongitudeDeg(). + + Breaking changes: + + 1. Removed "IsAeUnits" from the constructor for class Eci. + 2. Removed method Eci.AeToKm(). + 3. Removed enumeration VectorUnits. + 4. Renamed Globals.Deg2Rad() to ToRadians(). + 5. Renamed Globals.Rad2Deg() to ToDegrees(). + 6. Renamed Tle.IsValidLine() to IsValidFormat(). + 7. Renamed CoordGeo.Latitude() to LatitudeRad(). + 8. Renamed CoordGeo.Longitude() to LongitudeRad(). + 9. Renamed Site.Latitude() to LatitudeRad(). + 10. Renamed Site.Longitude() to LongitudeRad(). + +Version 1.0.4.0 2012-05-28 + + Some AMSAT TLEs use a non-standard scientific notation format that + excludes the sign of the exponent when the exponent is positive. + Updated Tle.ExpToDecimal() to correctly parse these TLEs. Thanks to + T. Doyle for reporting this issue. + +Version 1.0.3.1 2012-04-26 + + Use generic Dictionary<> instead of HashTable. + Remove references to class CategoryAttribute. + +Version 1.0.3.0 2011-08-21 + + Allow TLE satellite names up to twenty-four characters. + Compile with Visual Studio 2010. + +Version 1.0.2.2 2011-06-26 + + Repaired Site.ToString(), which had formatting errors. + +Version 1.0.2.1 2011-05-17 + + Field Site.m_Geo is now private property "Geo". + Added copy constructor for class Vector. + + Breaking changes: + + 1. Removed Globals.E6a + 2. Removed Globals.TwoThird + +Version 1.0.2.0 2010-12-31 + + Assembly marked as CLSCompliant(true), and ComVisible(false). + Fixed white-space removal code in Tle.IsValidLine(). + Pass CultureInfo objects to all calls to Parse() and ToString(). + Removed unneeded variable initializations. + + Breaking changes from prior version: + + 1. Renamed Tle.getUnits() to GetUnits() and made the method static. + 2. Tle.ConvertUnits() is now a static method. + 3. Tle.Key() is now a static method. + + New behaviors: + + 1. Site.GetLookAngle() throws class ArgumentException (and not class + Exception) when the eci argument does not have kilometer-based + units. + +Version 1.0.1.0 2010-12-22 + + Provide GMT when throwing DecayExceptions. + +Version 1.0.0.0 2010-09-26 + + Split the OrbitTools project into two assemblies: + + Core.dll - Contains core functionality such as Tle, Julian, etc. + Orbit.dll - Contains SGP4/SDP4 and class Orbit functionality. + + Dividing the project into two assemblies allows an easier upgrade path + for users who wish to transition to the Professional version of the + software. + + Method Tle.ExpToDecimal() now uses the invariant culture when parsing + the exponent value. This fixes a problem encountered by users who do + not use Windows English regional settings. + + Added new exception classes PropagatorException and DecayException. + + Compile with Visual Studio 2008. + + Breaking changes from prior (single assembly) release: + + All method names now use Pascal casing: + 1. Renamed Tle.getField() (3 overloads) to GetField(). + 2. Renamed Site.getPosition() to Site.GetPosition(). + 3. Renamed Site.getLookAngle() to Site.GetLookAngle(). This method now + throws System.ArgumentException on error (and not System.Exception). + 4. Renamed Site.toString() to Site.ToString(). + 5. Renamed Julian.toGMST() to Julian.ToGmst(). + 6. Renamed Julian.toLMST() to Julian.ToLmst(). + 7. Renamed Julian.toTime() to Julian.ToTime(). + 8. Renamed Eci.toGeo() to Eci.ToGeo(). + 9. Renamed EciBase.ae2km() to EciBase.Ae2Km(). + 10. All fields in class Globals renamed using Pascal casing. + + ******************** +Revision history below this point was for the single-assembly version of +the library. Only comments relevant to the files that were moved into the +Core.dll assembly have been retained. All other comments were moved into +the revision notes file for the Orbit.dll assembly. + ******************** + +Version 1.9.1.0 10/18/2009 + + Corrected problem in ExpToDecimal() that was dropping the last + significant digit of the mantissa. + +Version 1.9.0.0 01/25/2009 + + Corrected argument range checking in Julian.Initialize(). Also, the + type of exception thrown if an argument is invalid has changed from + "Exception" to "ArgumentOutOfRangeException". + +Version 1.8.0.0 01/02/2009 + + (Incremented due to changes in Orbit.cs) + +Version 1.7.0.0 12/08/2007 + + Added overrides of several functions to accept System.DateTime arguments. + Converted Orbit.Period() to a property and changed return type to TimeSpan. + +Version 1.6.0.0 08/08/2007 + + Added ECF support. + Use invariant culture when parsing strings. + +Version 1.5.0.0 01/28/2006 + + Namespace is now "Zeptomoby.OrbitTools". + +Version 1.4.0.0 11/05/2005 + + More interface rework; many methods are now properties. + +Version 1.3.0.0 07/19/2005 + + Added EpochJulian property to Tle class. Logic was previously located in + class Orbit's constructor. The Tle class has been updated to better work + with the .NET property grid control. + +Version 1.2.0.0 06/01/2005 + + Rework interfaces using C# accessors. Minor code updates. + + Confusion in the Julian day constant values introduced a bug SDP4; the + constants were repaired. Specifically, 2415020.0 is 12:00, on Dec 31, 1899, + or the beginning of Julian "Jan Zero" in the year 1900. + + Simplify Tle.ExpToDecimal() by converting input string into Double-Parsable + form, i.e., " 12345-3" => " 12345e-3", and then simply call Double.Parse(). + +Version 1.1.0.0 12/01/2004 + + Baseline release. + diff --git a/OrbitTools/OrbitTools/Orbit.Core/Site.cs b/OrbitTools/OrbitTools/Orbit.Core/Site.cs new file mode 100644 index 0000000..2a3e9cf --- /dev/null +++ b/OrbitTools/OrbitTools/Orbit.Core/Site.cs @@ -0,0 +1,179 @@ +// +// Site.cs +// +// Copyright (c) 2003-2012 Michael F. Henry +// Version 06/2012 +// +using System; +using System.Globalization; + +namespace Zeptomoby.OrbitTools +{ + /// + /// The Site class encapsulates a location on earth. + /// + public sealed class Site + { + #region Properties + + /// + /// Latitude, in radians. A negative value indicates latitude south. + /// + public double LatitudeRad { get { return Geo.LatitudeRad; } } + + /// + /// Longitude, in radians. A negative value indicates longitude west. + /// + public double LongitudeRad { get { return Geo.LongitudeRad; } } + + /// + /// Latitude, in degrees. A negative value indicates latitude south. + /// + public double LatitudeDeg { get { return Geo.LatitudeDeg; } } + + /// + /// Longitude, in degrees. A negative value indicates longitude west. + /// + public double LongitudeDeg { get { return Geo.LongitudeDeg; } } + + /// + /// The altitude of the site, in kilometers + /// + public double Altitude { get { return Geo.Altitude; } } + + /// + /// The contained geodetic coordinates. + /// + public Geo Geo { get; private set; } + + #endregion + + #region Construction + + /// + /// Standard constructor. + /// + /// Latitude in degrees (negative south). + /// Longitude in degrees (negative west). + /// Altitude in kilometers. + /// The earth ellipsoid model. + public Site(double degLat, double degLon, double kmAlt) + { + Geo = new Geo(Globals.ToRadians(degLat), + Globals.ToRadians(degLon), + kmAlt); + } + + /// + /// Create a Site object from Geo object. + /// + /// The Geo object. + public Site(Geo geo) + { + Geo = new Geo(geo); + } + + #endregion + + /// + /// Returns the ECI coordinates of the site at the given time. + /// + /// Time of position calculation. + /// The site's ECI coordinates. + public EciTime GetPosition(Julian date) + { + return new EciTime(Geo, date); + } + + /// + /// Returns the topo-centric (azimuth, elevation, etc.) coordinates for + /// a target object described by the given ECI coordinates. + /// + /// The ECI coordinates of the target object. + /// The look angle to the target object. + public TopoTime GetLookAngle(EciTime eci) + { + // Calculate the ECI coordinates for this Site object at the time + // of interest. + Julian date = eci.Date; + EciTime eciSite = GetPosition(date); + Vector vecRgRate = new Vector(eci.Velocity.X - eciSite.Velocity.X, + eci.Velocity.Y - eciSite.Velocity.Y, + eci.Velocity.Z - eciSite.Velocity.Z); + + double x = eci.Position.X - eciSite.Position.X; + double y = eci.Position.Y - eciSite.Position.Y; + double z = eci.Position.Z - eciSite.Position.Z; + double w = Math.Sqrt(Globals.Sqr(x) + Globals.Sqr(y) + Globals.Sqr(z)); + + Vector vecRange = new Vector(x, y, z, w); + + // The site's Local Mean Sidereal Time at the time of interest. + double theta = date.ToLmst(LongitudeRad); + + double sin_lat = Math.Sin(LatitudeRad); + double cos_lat = Math.Cos(LatitudeRad); + double sin_theta = Math.Sin(theta); + double cos_theta = Math.Cos(theta); + + double top_s = sin_lat * cos_theta * vecRange.X + + sin_lat * sin_theta * vecRange.Y - + cos_lat * vecRange.Z; + double top_e = -sin_theta * vecRange.X + + cos_theta * vecRange.Y; + double top_z = cos_lat * cos_theta * vecRange.X + + cos_lat * sin_theta * vecRange.Y + + sin_lat * vecRange.Z; + double az = Math.Atan(-top_e / top_s); + + if (top_s > 0.0) + { + az += Globals.Pi; + } + + if (az < 0.0) + { + az += 2.0 * Globals.Pi; + } + + double el = Math.Asin(top_z / vecRange.W); + double rate = (vecRange.X * vecRgRate.X + + vecRange.Y * vecRgRate.Y + + vecRange.Z * vecRgRate.Z) / vecRange.W; + + TopoTime topo = new TopoTime(az, // azimuth, radians + el, // elevation, radians + vecRange.W, // range, km + rate, // rate, km / sec + eci.Date); + +#if WANT_ATMOSPHERIC_CORRECTION + // Elevation correction for atmospheric refraction. + // Reference: Astronomical Algorithms by Jean Meeus, pp. 101-104 + // Note: Correction is meaningless when apparent elevation is below horizon + topo.m_El += Globals.ToRadians((1.02 / + Math.Tan(Globals.ToRadians(Globals.ToDegrees(el) + 10.3 / + (Globals.ToDegrees(el) + 5.11)))) / 60.0); + if (topo.m_El < 0.0) + { + topo.m_El = el; // Reset to true elevation + } + + if (topo.m_El > (Globals.PI / 2)) + { + topo.m_El = (Globals.PI / 2); + } +#endif + return topo; + } + + /// + /// Converts to a string representation of the form "120.00N 090.00W 500m". + /// + /// The formatted string. + public override string ToString() + { + return Geo.ToString(); + } + } +} diff --git a/OrbitTools/OrbitTools/Orbit.Core/Tle.cs b/OrbitTools/OrbitTools/Orbit.Core/Tle.cs new file mode 100644 index 0000000..3099f8a --- /dev/null +++ b/OrbitTools/OrbitTools/Orbit.Core/Tle.cs @@ -0,0 +1,578 @@ +// +// Tle.cs +// +// Copyright (c) 2003-2012 Michael F. Henry +// Version 06/2012 +// +using System; +using System.Collections.Generic; +using System.Globalization; + +// //////////////////////////////////////////////////////////////////////// +// +// NASA Two-Line Element Data format +// +// [Reference: Dr. T.S. Kelso / www.celestrak.com] +// +// Two-line element data consists of three lines in the following format: +// +// AAAAAAAAAAAAAAAAAAAAAAAA +// 1 NNNNNU NNNNNAAA NNNNN.NNNNNNNN +.NNNNNNNN +NNNNN-N +NNNNN-N N NNNNN +// 2 NNNNN NNN.NNNN NNN.NNNN NNNNNNN NNN.NNNN NNN.NNNN NN.NNNNNNNNNNNNNN +// +// Line 0 is a twenty-four-character name. +// +// Lines 1 and 2 are the standard Two-Line Orbital Element Set Format identical +// to that used by NORAD and NASA. The format description is: +// +// Line 1 +// Column Description +// 01-01 Line Number of Element Data +// 03-07 Satellite Number +// 10-11 International Designator (Last two digits of launch year) +// 12-14 International Designator (Launch number of the year) +// 15-17 International Designator (Piece of launch) +// 19-20 Epoch Year (Last two digits of year) +// 21-32 Epoch (Julian Day and fractional portion of the day) +// 34-43 First Time Derivative of the Mean Motion +// or Ballistic Coefficient (Depending on ephemeris type) +// 45-52 Second Time Derivative of Mean Motion (decimal point assumed; +// blank if N/A) +// 54-61 BSTAR drag term if GP4 general perturbation theory was used. +// Otherwise, radiation pressure coefficient. (Decimal point assumed) +// 63-63 Ephemeris type +// 65-68 Element number +// 69-69 Check Sum (Modulo 10) +// (Letters, blanks, periods, plus signs = 0; minus signs = 1) +// Line 2 +// Column Description +// 01-01 Line Number of Element Data +// 03-07 Satellite Number +// 09-16 Inclination [Degrees] +// 18-25 Right Ascension of the Ascending Node [Degrees] +// 27-33 Eccentricity (decimal point assumed) +// 35-42 Argument of Perigee [Degrees] +// 44-51 Mean Anomaly [Degrees] +// 53-63 Mean Motion [Revs per day] +// 64-68 Revolution number at epoch [Revs] +// 69-69 Check Sum (Modulo 10) +// +// All other columns are blank or fixed. +// +// Example: +// +// NOAA 6 +// 1 11416U 86 50.28438588 0.00000140 67960-4 0 5293 +// 2 11416 98.5105 69.3305 0012788 63.2828 296.9658 14.24899292346978 + +namespace Zeptomoby.OrbitTools +{ + /// + /// This class encapsulates a single set of standard NORAD two-line elements. + /// + public class Tle + { + public enum Line + { + Zero, + One, + Two + }; + + public enum Field + { + NoradNum, + IntlDesc, + SetNumber, // TLE set number + EpochYear, // Epoch: Last two digits of year + EpochDay, // Epoch: Fractional Julian Day of year + OrbitAtEpoch, // Orbit at epoch + Inclination, // Inclination + Raan, // R.A. ascending node + Eccentricity, // Eccentricity + ArgPerigee, // Argument of perigee + MeanAnomaly, // Mean anomaly + MeanMotion, // Mean motion + MeanMotionDt, // First time derivative of mean motion + MeanMotionDt2, // Second time derivative of mean motion + BStarDrag // BSTAR Drag + } + + public enum Unit + { + Radians, // radians + Degrees, // degrees + Native // TLE format native units (no conversion) + } + + // Satellite name and two data lines + private string m_Line0; + private string m_Line1; + private string m_Line2; + + // Converted fields, in Double.Parse()-able form + private Dictionary m_Field; + + // Cache of field values in "double" format. + // Key - integer + // Value - cached value + private Dictionary m_Cache; + + #region Properties + + public string Name + { + get { return m_Line0; } + } + + public string Line1 + { + get { return m_Line1; } + } + + public string Line2 + { + get { return m_Line2; } + } + + public string NoradNumber + { + get { return GetField(Field.NoradNum, false); } + } + + public string Eccentricity + { + get { return GetField(Field.Eccentricity, false); } + } + + public string Inclination + { + get { return GetField(Field.Inclination, true); } + } + + public string Epoch + { + get + { + return string.Format(CultureInfo.InvariantCulture, "{0:00}{1:000.00000000}", + GetField(Field.EpochYear), GetField(Field.EpochDay)); + } + } + + public string IntlDescription + { + get { return GetField(Field.IntlDesc, false); } + } + + public string SetNumber + { + get { return GetField(Field.SetNumber, false); } + } + + public string OrbitAtEpoch + { + get { return GetField(Field.OrbitAtEpoch, false); } + } + + public string RAAscendingNode + { + get { return GetField(Field.Raan, true); } + } + + public string ArgPerigee + { + get { return GetField(Field.ArgPerigee, true); } + } + + public string MeanAnomaly + { + get { return GetField(Field.MeanAnomaly, true); } + } + + public string MeanMotion + { + get { return GetField(Field.MeanMotion, true); } + } + + public string MeanMotionDt + { + get { return GetField(Field.MeanMotionDt, false); } + } + + public string MeanMotionDt2 + { + get { return GetField(Field.MeanMotionDt2, false); } + } + + public string BStarDrag + { + get { return GetField(Field.BStarDrag, false); } + } + + public Julian EpochJulian + { + get + { + int epochYear = (int)GetField(Tle.Field.EpochYear); + double epochDay = GetField(Tle.Field.EpochDay); + + if (epochYear < 57) + { + epochYear += 2000; + } + else + { + epochYear += 1900; + } + + return new Julian(epochYear, epochDay); + } + } + + #endregion + + #region Construction + + #region Column Offsets + + // Note: The column offsets are zero-based. + + // Name + private const int TLE_LEN_LINE_DATA = 69; private const int TLE_LEN_LINE_NAME = 24; + + // Line 1 + private const int TLE1_COL_SATNUM = 2; private const int TLE1_LEN_SATNUM = 5; + private const int TLE1_COL_INTLDESC_A = 9; private const int TLE1_LEN_INTLDESC_A = 2; + private const int TLE1_COL_INTLDESC_B = 11; private const int TLE1_LEN_INTLDESC_B = 3; + private const int TLE1_COL_INTLDESC_C = 14; private const int TLE1_LEN_INTLDESC_C = 3; + private const int TLE1_COL_EPOCH_A = 18; private const int TLE1_LEN_EPOCH_A = 2; + private const int TLE1_COL_EPOCH_B = 20; private const int TLE1_LEN_EPOCH_B = 12; + private const int TLE1_COL_MEANMOTIONDT = 33; private const int TLE1_LEN_MEANMOTIONDT = 10; + private const int TLE1_COL_MEANMOTIONDT2 = 44; private const int TLE1_LEN_MEANMOTIONDT2 = 8; + private const int TLE1_COL_BSTAR = 53; private const int TLE1_LEN_BSTAR = 8; + private const int TLE1_COL_EPHEMTYPE = 62; private const int TLE1_LEN_EPHEMTYPE = 1; + private const int TLE1_COL_ELNUM = 64; private const int TLE1_LEN_ELNUM = 4; + + // Line 2 + private const int TLE2_COL_SATNUM = 2; private const int TLE2_LEN_SATNUM = 5; + private const int TLE2_COL_INCLINATION = 8; private const int TLE2_LEN_INCLINATION = 8; + private const int TLE2_COL_RAASCENDNODE = 17; private const int TLE2_LEN_RAASCENDNODE = 8; + private const int TLE2_COL_ECCENTRICITY = 26; private const int TLE2_LEN_ECCENTRICITY = 7; + private const int TLE2_COL_ARGPERIGEE = 34; private const int TLE2_LEN_ARGPERIGEE = 8; + private const int TLE2_COL_MEANANOMALY = 43; private const int TLE2_LEN_MEANANOMALY = 8; + private const int TLE2_COL_MEANMOTION = 52; private const int TLE2_LEN_MEANMOTION = 11; + private const int TLE2_COL_REVATEPOCH = 63; private const int TLE2_LEN_REVATEPOCH = 5; + + #endregion + + // ////////////////////////////////////////////////////////////////////////// + public Tle(string strName, string strLine1, string strLine2) + { + m_Line0 = strName; + m_Line1 = strLine1; + m_Line2 = strLine2; + + Initialize(); + } + + // ////////////////////////////////////////////////////////////////////////// + public Tle(Tle tle) : + this(tle.Name, tle.Line1, tle.Line2) + { + } + + // ////////////////////////////////////////////////////////////////////////// + private void Initialize() + { + m_Field = new Dictionary(); + m_Cache = new Dictionary(); + + m_Field[Field.NoradNum] = m_Line1.Substring(TLE1_COL_SATNUM, TLE1_LEN_SATNUM); + m_Field[Field.IntlDesc] = m_Line1.Substring(TLE1_COL_INTLDESC_A, + TLE1_LEN_INTLDESC_A + + TLE1_LEN_INTLDESC_B + + TLE1_LEN_INTLDESC_C); + m_Field[Field.EpochYear] = m_Line1.Substring(TLE1_COL_EPOCH_A, TLE1_LEN_EPOCH_A); + m_Field[Field.EpochDay] = m_Line1.Substring(TLE1_COL_EPOCH_B, TLE1_LEN_EPOCH_B); + + if (m_Line1[TLE1_COL_MEANMOTIONDT] == '-') + { + // value is negative + m_Field[Field.MeanMotionDt] = "-0"; + } + else + { + m_Field[Field.MeanMotionDt] = "0"; + } + + m_Field[Field.MeanMotionDt] += m_Line1.Substring(TLE1_COL_MEANMOTIONDT + 1, + TLE1_LEN_MEANMOTIONDT); + // decimal point assumed; exponential notation + m_Field[Field.MeanMotionDt2] = + ExpToDecimal(m_Line1.Substring(TLE1_COL_MEANMOTIONDT2, + TLE1_LEN_MEANMOTIONDT2)); + + // decimal point assumed; exponential notation + m_Field[Field.BStarDrag] = + ExpToDecimal(m_Line1.Substring(TLE1_COL_BSTAR, TLE1_LEN_BSTAR)); + //TLE1_COL_EPHEMTYPE + //TLE1_LEN_EPHEMTYPE + + m_Field[Field.SetNumber] = + m_Line1.Substring(TLE1_COL_ELNUM, TLE1_LEN_ELNUM).TrimStart(); + + // TLE2_COL_SATNUM + // TLE2_LEN_SATNUM + + m_Field[Field.Inclination] = + m_Line2.Substring(TLE2_COL_INCLINATION, TLE2_LEN_INCLINATION).TrimStart(); + + m_Field[Field.Raan] = + m_Line2.Substring(TLE2_COL_RAASCENDNODE, TLE2_LEN_RAASCENDNODE).TrimStart(); + + // Eccentricity: decimal point is assumed + m_Field[Field.Eccentricity] = "0." + m_Line2.Substring(TLE2_COL_ECCENTRICITY, + TLE2_LEN_ECCENTRICITY); + + m_Field[Field.ArgPerigee] = + m_Line2.Substring(TLE2_COL_ARGPERIGEE, TLE2_LEN_ARGPERIGEE).TrimStart(); + + m_Field[Field.MeanAnomaly] = + m_Line2.Substring(TLE2_COL_MEANANOMALY, TLE2_LEN_MEANANOMALY).TrimStart(); + + m_Field[Field.MeanMotion] = + m_Line2.Substring(TLE2_COL_MEANMOTION, TLE2_LEN_MEANMOTION).TrimStart(); + + m_Field[Field.OrbitAtEpoch] = + m_Line2.Substring(TLE2_COL_REVATEPOCH, TLE2_LEN_REVATEPOCH).TrimStart(); + } + + #endregion + + #region GetField + + /// + /// Returns the requested TLE data field. + /// + /// The field to return. + /// The requested field, in native form. + public double GetField(Field fld) + { + return GetField(fld, Unit.Native); + } + + /// + /// Returns the requested TLE data field as a type double. + /// + /// + /// The numeric return values are cached; requesting the same field + /// repeatedly incurs minimal overhead. + /// + /// The TLE field to retrieve. + /// Specifies the units desired. + /// + /// The requested field's value, converted to the correct units if necessary. + /// + public double GetField(Field fld, Unit units) + { + // Return cache contents if it exists, else populate cache. + int key = Key(units, fld); + + if (m_Cache.ContainsKey(key)) + { + // return cached value + return (double)m_Cache[key]; + } + else + { + // Value not in cache; add it + double valNative = Double.Parse(m_Field[fld].ToString(), CultureInfo.InvariantCulture); + double valConv = ConvertUnits(valNative, fld, units); + m_Cache[key] = valConv; + + return valConv; + } + } + + /// + /// Returns the requested TLE data field in native form as a text string. + /// + /// The TLE field to retrieve. + /// If true, the native units are appended to + /// the end of the returned string. + /// The requested field as a string. + public string GetField(Field fld, bool appendUnits) + { + string str = m_Field[fld].ToString(); + + if (appendUnits) + { + str += GetUnits(fld); + } + + return str.Trim(); + } + + #endregion + + #region Utility + + // /////////////////////////////////////////////////////////////////////////// + // Generates a key for the TLE field cache + private static int Key(Unit u, Field f) + { + return ((int)u * 100) + (int)f; + } + + /// + /// Converts the given TLE field to the requested units. + /// + /// Value to convert (native units). + /// Field ID of the value being converted. + /// Units to convert to. + /// The converted value. + protected static double ConvertUnits(double valNative, + Field fld, + Unit units) + { + if (fld == Field.Inclination || + fld == Field.Raan || + fld == Field.ArgPerigee || + fld == Field.MeanAnomaly) + { + // The native TLE format is degrees + if (units == Unit.Radians) + { + return Globals.ToRadians(valNative); + } + } + + // unconverted native format + return valNative; + } + + // /////////////////////////////////////////////////////////////////////////// + protected static string GetUnits(Field fld) + { + const string strDegrees = " degrees"; + const string strRevsPerDay = " revs / day"; + + switch (fld) + { + case Field.Inclination: + case Field.Raan: + case Field.ArgPerigee: + case Field.MeanAnomaly: + return strDegrees; + + case Field.MeanMotion: + return strRevsPerDay; + + default: + return string.Empty; + } + } + + // ////////////////////////////////////////////////////////////////////////// + // Converts TLE-style exponential notation of the form [ |+|-]00000[ |+|-]0 + // to decimal notation. Assumes implied decimal point to the left of the first + // number in the string, i.e., + // " 12345-3" = 0.00012345 + // "-23429-5" = -0.0000023429 + // " 40436+1" = 4.0436 + // Also assumes that lack of a sign character implies a positive value, i.e., + // " 00000 0" = 0.00000 + // " 31415 1" = 3.1415 + protected static string ExpToDecimal(string str) + { + const int COL_SIGN = 0; + const int LEN_SIGN = 1; + + const int COL_MANTISSA = 1; + const int LEN_MANTISSA = 5; + + const int COL_EXPONENT = 6; + const int LEN_EXPONENT = 2; + + string sign = str.Substring(COL_SIGN, LEN_SIGN); + string mantissa = str.Substring(COL_MANTISSA, LEN_MANTISSA); + string exponent = str.Substring(COL_EXPONENT, LEN_EXPONENT).TrimStart(); + + double val = Double.Parse(sign +"0." + mantissa + "e" + exponent, CultureInfo.InvariantCulture); + + int sigDigits = mantissa.Length + Math.Abs(int.Parse(exponent, CultureInfo.InvariantCulture)); + + return val.ToString("F" + sigDigits, CultureInfo.InvariantCulture); + } + + /// + /// Determines if a given string has the expected format of a single + /// line of TLE data. + /// + /// The input string. + /// The line ID of the input string. + /// True if the input string has the format of + /// the given line ID. + /// + /// A valid satellite name is less than or equal to TLE_LEN_LINE_NAME + /// characters; + /// A valid data line must: + /// Have as the first character the line number + /// Have as the second character a blank + /// Be TLE_LEN_LINE_DATA characters long + /// + public static bool IsValidFormat(string str, Line line) + { + str = str.Trim(); + + int nLen = str.Length; + + if (line == Line.Zero) + { + // Satellite name + return str.Length <= TLE_LEN_LINE_NAME; + } + else + { + // Data line + return (nLen == TLE_LEN_LINE_DATA) && + ((str[0] - '0') == (int)line) && + (str[1] == ' '); + } + } + + // ////////////////////////////////////////////////////////////////////////// + // Calculate the check sum for a given line of TLE data, the last character + // of which is the current checksum. (Although there is no check here, + // the current checksum should match the one calculated.) + // The checksum algorithm: + // Each number in the data line is summed, modulo 10. + // Non-numeric characters are zero, except minus signs, which are 1. + // + static int CheckSum(string str) + { + // The length is "- 1" because the current (existing) checksum + // character is not included. + int len = str.Length - 1; + int xsum = 0; + + for (int i = 0; i < len; i++) + { + char ch = str[i]; + + if (Char.IsDigit(ch)) + { + xsum += (ch - '0'); + } + else if (ch == '-') + { + xsum++; + } + } + + return (xsum % 10); + } + + #endregion + } +} diff --git a/OrbitTools/OrbitTools/Orbit.Core/Vector.cs b/OrbitTools/OrbitTools/Orbit.Core/Vector.cs new file mode 100644 index 0000000..78feff8 --- /dev/null +++ b/OrbitTools/OrbitTools/Orbit.Core/Vector.cs @@ -0,0 +1,183 @@ +// +// Vector.cs +// +// Copyright (c) 2003-2012 Michael F. Henry +// Version 10/2012 +// +using System; + +namespace Zeptomoby.OrbitTools +{ + /// + /// Encapsulates a simple 4-component vector + /// + public class Vector + { + #region Construction + + /// + /// Standard constructor. + /// + public Vector() + { + } + + /// + /// Creates a new vector from an existing vector. + /// + /// The existing vector to copy. + public Vector(Vector v) + :this(v.X, v.Y, v.Z, v.W) + { + } + + /// + /// Creates a new vector with the given XYZ components. + /// + /// The X component. + /// The Y component. + /// The Z component. + public Vector(double x, double y, double z) + { + X = x; + Y = y; + Z = z; + } + + /// + /// Creates a new vector with the given XYZ-W components. + /// + /// The X component. + /// The Y component. + /// The Z component. + /// The W component. + public Vector(double x, double y, double z, double w) + { + X = x; + Y = y; + Z = z; + W = w; + } + + #endregion + + #region Properties + + public double X { get; set; } + public double Y { get; set; } + public double Z { get; set; } + public double W { get; set; } + + #endregion + + /// + /// Multiply each component in the vector by a given factor. + /// + /// The factor. + public void Mul(double factor) + { + X *= factor; + Y *= factor; + Z *= factor; + W *= Math.Abs(factor); + } + + /// + /// Subtracts a vector from this vector. + /// + /// The vector to subtract. + public void Sub(Vector vec) + { + X -= vec.X; + Y -= vec.Y; + Z -= vec.Z; + W -= vec.W; + } + + /// + /// Calculates the angle, in radians, between this vector and another. + /// + /// The second vector. + /// + /// The angle between the two vectors, in radians. + /// + public double Angle(Vector vec) + { + return Math.Acos(Dot(vec) / (Magnitude() * vec.Magnitude())); + } + + /// + /// Calculates the magnitude of the vector. + /// + /// The vector magnitude. + public double Magnitude() + { + return Math.Sqrt((X * X) + (Y * Y) + (Z * Z)); + } + + /// + /// Calculates the dot product of this vector and another. + /// + /// The second vector. + /// The dot product. + public double Dot(Vector vec) + { + return (X * vec.X) + (Y * vec.Y) + (Z * vec.Z); + } + + /// + /// Calculates the distance between two vectors as points in XYZ space. + /// + /// The second vector. + /// The calculated distance. + public double Distance(Vector vec) + { + return Math.Sqrt(Math.Pow(X - vec.X, 2.0) + + Math.Pow(Y - vec.Y, 2.0) + + Math.Pow(Z - vec.Z, 2.0)); + } + + /// + /// Rotates the XYZ coordinates around the X-axis. + /// + public void RotateX(double radians) + { + double y = Y; + + Y = (Math.Cos(radians) * y) - (Math.Sin(radians) * Z); + Z = (Math.Sin(radians) * y) + (Math.Cos(radians) * Z); + } + + /// + /// Rotates the XYZ coordinates around the Y-axis. + /// + public void RotateY(double radians) + { + double x = X; + + X = ( Math.Cos(radians) * x) + (Math.Sin(radians) * Z); + Z = (-Math.Sin(radians) * x) + (Math.Cos(radians) * Z); + } + + /// + /// Rotates the XYZ coordinates around the Z-axis. + /// + public void RotateZ(double radians) + { + double x = X; + + X = (Math.Cos(radians) * x) - (Math.Sin(radians) * Y); + Y = (Math.Sin(radians) * x) + (Math.Cos(radians) * Y); + } + + /// + /// Offsets the XYZ coordinates. + /// + public void Translate(double x, double y, double z) + { + X += x; + Y += y; + Z += z; + } + } +} diff --git a/OrbitTools/OrbitTools/Orbit.Core/packages.config b/OrbitTools/OrbitTools/Orbit.Core/packages.config new file mode 100644 index 0000000..0aee17e --- /dev/null +++ b/OrbitTools/OrbitTools/Orbit.Core/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/OrbitTools/OrbitTools/Orbit/NoradBase.cs b/OrbitTools/OrbitTools/Orbit/NoradBase.cs new file mode 100644 index 0000000..150ed4b --- /dev/null +++ b/OrbitTools/OrbitTools/Orbit/NoradBase.cs @@ -0,0 +1,275 @@ +// +// This class provides a base class for the NORAD SGP4/SDP4 orbit models. +// +// Copyright (c) 2003-2011 Michael F. Henry +// +using System; + +namespace Zeptomoby.OrbitTools +{ + /// + /// This class provides a base class for the NORAD SGP4/SDP4 orbit models. + /// + abstract internal class NoradBase + { + #region Properties + + // Orbital parameter variables which need only be calculated one + // time for a given orbit (ECI position time-independent). + protected double m_satInc; // inclination + protected double m_satEcc; // eccentricity + + protected double m_cosio; protected double m_theta2; protected double m_x3thm1; protected double m_eosq; + protected double m_betao2; protected double m_betao; protected double m_aodp; protected double m_xnodp; + protected double m_s4; protected double m_qoms24; protected double m_perigee; protected double m_tsi; + protected double m_eta; protected double m_etasq; protected double m_eeta; protected double m_coef; + protected double m_coef1; protected double m_c1; protected double m_c3; protected double m_c4; + protected double m_sinio; protected double m_x1mth2; protected double m_xmdot; protected double m_omgdot; + protected double m_xnodot; protected double m_xnodcf; protected double m_t2cof; protected double m_xlcof; + protected double m_aycof; protected double m_x7thm1; + + protected Orbit Orbit { get; private set; } + + public abstract EciTime GetPosition(double tsince); + + #endregion + + #region Construction + + public NoradBase(Orbit orbit) + { + Orbit = orbit; + Initialize(); + } + + #endregion + + // /////////////////////////////////////////////////////////////////////////// + // Initialize() + // Perform the initialization of member variables, specifically the variables + // used by derived-class objects to calculate ECI coordinates. + private void Initialize() + { + // Initialize any variables which are time-independent when + // calculating the ECI coordinates of the satellite. + m_satInc = Orbit.Inclination; + m_satEcc = Orbit.Eccentricity; + + m_cosio = Math.Cos(m_satInc); + m_theta2 = m_cosio * m_cosio; + m_x3thm1 = 3.0 * m_theta2 - 1.0; + m_eosq = m_satEcc * m_satEcc; + m_betao2 = 1.0 - m_eosq; + m_betao = Math.Sqrt(m_betao2); + + // The "recovered" semimajor axis and mean motion. + m_aodp = Orbit.SemiMajor; + m_xnodp = Orbit.MeanMotion; + + // For perigee below 156 km, the values of Globals.S and Globals.QOMS2T are altered. + m_perigee = Globals.Xkmper * (m_aodp * (1.0 - m_satEcc) - Globals.Ae); + + m_s4 = Globals.S; + m_qoms24 = Globals.Qoms2t; + + if (m_perigee < 156.0) + { + m_s4 = m_perigee - 78.0; + + if (m_perigee <= 98.0) + { + m_s4 = 20.0; + } + + m_qoms24 = Math.Pow((120.0 - m_s4) * Globals.Ae / Globals.Xkmper, 4.0); + m_s4 = m_s4 / Globals.Xkmper + Globals.Ae; + } + + double pinvsq = 1.0 / (m_aodp * m_aodp * m_betao2 * m_betao2); + + m_tsi = 1.0 / (m_aodp - m_s4); + m_eta = m_aodp * m_satEcc * m_tsi; + m_etasq = m_eta * m_eta; + m_eeta = m_satEcc * m_eta; + + double psisq = Math.Abs(1.0 - m_etasq); + + m_coef = m_qoms24 * Math.Pow(m_tsi,4.0); + m_coef1 = m_coef / Math.Pow(psisq,3.5); + + double c2 = m_coef1 * m_xnodp * + (m_aodp * (1.0 + 1.5 * m_etasq + m_eeta * (4.0 + m_etasq)) + + 0.75 * Globals.Ck2 * m_tsi / psisq * m_x3thm1 * + (8.0 + 3.0 * m_etasq * (8.0 + m_etasq))); + + m_c1 = Orbit.BStar * c2; + m_sinio = Math.Sin(m_satInc); + + double a3ovk2 = -Globals.Xj3 / Globals.Ck2 * Math.Pow(Globals.Ae,3.0); + + m_c3 = m_coef * m_tsi * a3ovk2 * m_xnodp * Globals.Ae * m_sinio / m_satEcc; + m_x1mth2 = 1.0 - m_theta2; + m_c4 = 2.0 * m_xnodp * m_coef1 * m_aodp * m_betao2 * + (m_eta * (2.0 + 0.5 * m_etasq) + + m_satEcc * (0.5 + 2.0 * m_etasq) - + 2.0 * Globals.Ck2 * m_tsi / (m_aodp * psisq) * + (-3.0 * m_x3thm1 * (1.0 - 2.0 * m_eeta + m_etasq * (1.5 - 0.5 * m_eeta)) + + 0.75 * m_x1mth2 * + (2.0 * m_etasq - m_eeta * (1.0 + m_etasq)) * + Math.Cos(2.0 * Orbit.ArgPerigee))); + + double theta4 = m_theta2 * m_theta2; + double temp1 = 3.0 * Globals.Ck2 * pinvsq * m_xnodp; + double temp2 = temp1 * Globals.Ck2 * pinvsq; + double temp3 = 1.25 * Globals.Ck4 * pinvsq * pinvsq * m_xnodp; + + m_xmdot = m_xnodp + 0.5 * temp1 * m_betao * m_x3thm1 + + 0.0625 * temp2 * m_betao * + (13.0 - 78.0 * m_theta2 + 137.0 * theta4); + + double x1m5th = 1.0 - 5.0 * m_theta2; + + m_omgdot = -0.5 * temp1 * x1m5th + 0.0625 * temp2 * + (7.0 - 114.0 * m_theta2 + 395.0 * theta4) + + temp3 * (3.0 - 36.0 * m_theta2 + 49.0 * theta4); + + double xhdot1 = -temp1 * m_cosio; + + m_xnodot = xhdot1 + (0.5 * temp2 * (4.0 - 19.0 * m_theta2) + + 2.0 * temp3 * (3.0 - 7.0 * m_theta2)) * m_cosio; + m_xnodcf = 3.5 * m_betao2 * xhdot1 * m_c1; + m_t2cof = 1.5 * m_c1; + m_xlcof = 0.125 * a3ovk2 * m_sinio * + (3.0 + 5.0 * m_cosio) / (1.0 + m_cosio); + m_aycof = 0.25 * a3ovk2 * m_sinio; + m_x7thm1 = 7.0 * m_theta2 - 1.0; + } + + // ///////////////////////////////////////////////////////////////////// + protected EciTime FinalPosition(double incl, double omega, double e, + double a, double xl, double xnode, + double xn, double tsince) + { + if ((e * e) > 1.0) + { + throw new PropagationException("Error in satellite data"); + } + + double beta = Math.Sqrt(1.0 - e * e); + + // Long period periodics + double axn = e * Math.Cos(omega); + double temp = 1.0 / (a * beta * beta); + double xll = temp * m_xlcof * axn; + double aynl = temp * m_aycof; + double xlt = xl + xll; + double ayn = e * Math.Sin(omega) + aynl; + + // Solve Kepler's Equation + double capu = Globals.Fmod2p(xlt - xnode); + double temp2 = capu; + double temp3 = 0.0; + double temp4 = 0.0; + double temp5 = 0.0; + double temp6 = 0.0; + double sinepw = 0.0; + double cosepw = 0.0; + bool fDone = false; + + for (int i = 1; (i <= 10) && !fDone; i++) + { + sinepw = Math.Sin(temp2); + cosepw = Math.Cos(temp2); + temp3 = axn * sinepw; + temp4 = ayn * cosepw; + temp5 = axn * cosepw; + temp6 = ayn * sinepw; + + double epw = (capu - temp4 + temp3 - temp2) / + (1.0 - temp5 - temp6) + temp2; + + if (Math.Abs(epw - temp2) <= 1.0e-06) + { + fDone = true; + } + else + { + temp2 = epw; + } + } + + // Short period preliminary quantities + double ecose = temp5 + temp6; + double esine = temp3 - temp4; + double elsq = axn * axn + ayn * ayn; + temp = 1.0 - elsq; + double pl = a * temp; + double r = a * (1.0 - ecose); + double temp1 = 1.0 / r; + double rdot = Globals.Xke * Math.Sqrt(a) * esine * temp1; + double rfdot = Globals.Xke * Math.Sqrt(pl) * temp1; + temp2 = a * temp1; + double betal = Math.Sqrt(temp); + temp3 = 1.0 / (1.0 + betal); + double cosu = temp2 * (cosepw - axn + ayn * esine * temp3); + double sinu = temp2 * (sinepw - ayn - axn * esine * temp3); + double u = Globals.AcTan(sinu, cosu); + double sin2u = 2.0 * sinu * cosu; + double cos2u = 2.0 * cosu * cosu - 1.0; + + temp = 1.0 / pl; + temp1 = Globals.Ck2 * temp; + temp2 = temp1 * temp; + + // Update for short periodics + double rk = r * (1.0 - 1.5 * temp2 * betal * m_x3thm1) + + 0.5 * temp1 * m_x1mth2 * cos2u; + double uk = u - 0.25 * temp2 * m_x7thm1 * sin2u; + double xnodek = xnode + 1.5 * temp2 * m_cosio * sin2u; + double xinck = incl + 1.5 * temp2 * m_cosio * m_sinio * cos2u; + double rdotk = rdot - xn * temp1 * m_x1mth2 * sin2u; + double rfdotk = rfdot + xn * temp1 * (m_x1mth2 * cos2u + 1.5 * m_x3thm1); + + // Orientation vectors + double sinuk = Math.Sin(uk); + double cosuk = Math.Cos(uk); + double sinik = Math.Sin(xinck); + double cosik = Math.Cos(xinck); + double sinnok = Math.Sin(xnodek); + double cosnok = Math.Cos(xnodek); + double xmx = -sinnok * cosik; + double xmy = cosnok * cosik; + double ux = xmx * sinuk + cosnok * cosuk; + double uy = xmy * sinuk + sinnok * cosuk; + double uz = sinik * sinuk; + double vx = xmx * cosuk - cosnok * sinuk; + double vy = xmy * cosuk - sinnok * sinuk; + double vz = sinik * cosuk; + + // Position + double x = rk * ux; + double y = rk * uy; + double z = rk * uz; + + Vector vecPos = new Vector(x, y, z); + DateTime gmt = Orbit.EpochTime.AddMinutes(tsince); + + // Validate on altitude + double altKm = (vecPos.Magnitude() * (Globals.Xkmper / Globals.Ae)); + + if (altKm < Globals.Xkmper) + { + throw new DecayException(gmt, Orbit.SatNameLong); + } + + // Velocity + double xdot = rdotk * ux + rfdotk * vx; + double ydot = rdotk * uy + rfdotk * vy; + double zdot = rdotk * uz + rfdotk * vz; + + Vector vecVel = new Vector(xdot, ydot, zdot); + + return new EciTime(vecPos, vecVel, new Julian(gmt)); + } + } +} diff --git a/OrbitTools/OrbitTools/Orbit/NoradSDP4.cs b/OrbitTools/OrbitTools/Orbit/NoradSDP4.cs new file mode 100644 index 0000000..957cc10 --- /dev/null +++ b/OrbitTools/OrbitTools/Orbit/NoradSDP4.cs @@ -0,0 +1,705 @@ +// +// NoradSDP4.cs +// +// Copyright (c) 2003-2010 Michael F. Henry +// +using System; + +namespace Zeptomoby.OrbitTools +{ + /// + /// NORAD SDP4 implementation. + /// + internal class NoradSDP4 : NoradBase + { + const double zns = 1.19459E-5; + const double zes = 0.01675; + const double znl = 1.5835218E-4; + const double zel = 0.05490; + const double thdt = 4.3752691E-3; + + double dp_e3; double dp_ee2; double dp_se2; + double dp_se3; double dp_sgh2; double dp_sgh3; double dp_sgh4; + double dp_sh2; double dp_sh3; double dp_si2; + double dp_si3; double dp_sl2; double dp_sl3; double dp_sl4; + double dp_xgh2; double dp_xgh3; double dp_xgh4; double dp_xh2; + double dp_xh3; double dp_xi2; double dp_xi3; double dp_xl2; + double dp_xl3; double dp_xl4; double dp_xqncl; double dp_zmol; + double dp_zmos; + + double dp_atime; double dp_d2201; double dp_d2211; double dp_d3210; + double dp_d3222; double dp_d4410; double dp_d4422; double dp_d5220; + double dp_d5232; double dp_d5421; double dp_d5433; double dp_del1; + double dp_del2; double dp_del3; double dp_omegaq; double dp_sse; + double dp_ssg; double dp_ssh; double dp_ssi; double dp_ssl; + double dp_step2; double dp_stepn; double dp_stepp; double dp_thgr; + double dp_xfact; double dp_xlamo; double dp_xli; double dp_xni; + + bool gp_reso; // geopotential resonant + bool gp_sync; // geopotential synchronous + + // /////////////////////////////////////////////////////////////////////////// + public NoradSDP4(Orbit orbit) : + base(orbit) + { + double sinarg = Math.Sin(Orbit.ArgPerigee); + double cosarg = Math.Cos(Orbit.ArgPerigee); + + // Deep space initialization + Julian jd = Orbit.Epoch; + + dp_thgr = jd.ToGmst(); + + double eq = Orbit.Eccentricity; + double aqnv = 1.0 / Orbit.SemiMajor; + + dp_xqncl = Orbit.Inclination; + + double xmao = Orbit.MeanAnomaly; + double xpidot = m_omgdot + m_xnodot; + double sinq = Math.Sin(Orbit.RAAN); + double cosq = Math.Cos(Orbit.RAAN); + + dp_omegaq = Orbit.ArgPerigee; + + #region Lunar / Solar terms + + // Initialize lunar solar terms + double day = jd.FromJan0_12h_1900(); + + double dpi_xnodce = 4.5236020 - 9.2422029E-4 * day; + double dpi_stem = Math.Sin(dpi_xnodce); + double dpi_ctem = Math.Cos(dpi_xnodce); + double dpi_zcosil = 0.91375164 - 0.03568096 * dpi_ctem; + double dpi_zsinil = Math.Sqrt(1.0 - dpi_zcosil * dpi_zcosil); + double dpi_zsinhl = 0.089683511 * dpi_stem / dpi_zsinil; + double dpi_zcoshl = Math.Sqrt(1.0 - dpi_zsinhl * dpi_zsinhl); + double dpi_c = 4.7199672 + 0.22997150 * day; + double dpi_gam = 5.8351514 + 0.0019443680 * day; + + dp_zmol = Globals.Fmod2p(dpi_c - dpi_gam); + + double dpi_zx = 0.39785416 * dpi_stem / dpi_zsinil; + double dpi_zy = dpi_zcoshl * dpi_ctem + 0.91744867 * dpi_zsinhl * dpi_stem; + + dpi_zx = Globals.AcTan(dpi_zx, dpi_zy) + dpi_gam - dpi_xnodce; + + double dpi_zcosgl = Math.Cos(dpi_zx); + double dpi_zsingl = Math.Sin(dpi_zx); + + dp_zmos = 6.2565837 + 0.017201977 * day; + dp_zmos = Globals.Fmod2p(dp_zmos); + + const double zcosis = 0.91744867; + const double zsinis = 0.39785416; + const double zsings = -0.98088458; + const double zcosgs = 0.1945905; + const double c1ss = 2.9864797E-6; + + double zcosg = zcosgs; + double zsing = zsings; + double zcosi = zcosis; + double zsini = zsinis; + double zcosh = cosq; + double zsinh = sinq; + double cc = c1ss; + double zn = zns; + double ze = zes; + double xnoi = 1.0 / Orbit.MeanMotion; + + double a1; double a3; double a7; double a8; double a9; double a10; + double a2; double a4; double a5; double a6; double x1; double x2; + double x3; double x4; double x5; double x6; double x7; double x8; + double z31; double z32; double z33; double z1; double z2; double z3; + double z11; double z12; double z13; double z21; double z22; double z23; + double s3; double s2; double s4; double s1; double s5; double s6; + double s7; + + double se = 0.0; + double si = 0.0; + double sl = 0.0; + double sgh = 0.0; + double sh = 0.0; + + double eosq = Globals.Sqr(Orbit.Eccentricity); + + // Apply the solar and lunar terms on the first pass, then re-apply the + // solar terms again on the second pass. + + for (int pass = 1; pass <= 2; pass++) + { + // Do solar terms + a1 = zcosg * zcosh + zsing * zcosi * zsinh; + a3 = -zsing * zcosh + zcosg * zcosi * zsinh; + a7 = -zcosg * zsinh + zsing * zcosi * zcosh; + a8 = zsing * zsini; + a9 = zsing * zsinh + zcosg * zcosi * zcosh; + a10 = zcosg * zsini; + a2 = m_cosio * a7 + m_sinio * a8; + a4 = m_cosio * a9 + m_sinio * a10; + a5 = -m_sinio * a7 + m_cosio * a8; + a6 = -m_sinio * a9 + m_cosio * a10; + x1 = a1 * cosarg + a2 * sinarg; + x2 = a3 * cosarg + a4 * sinarg; + x3 = -a1 * sinarg + a2 * cosarg; + x4 = -a3 * sinarg + a4 * cosarg; + x5 = a5 * sinarg; + x6 = a6 * sinarg; + x7 = a5 * cosarg; + x8 = a6 * cosarg; + z31 = 12.0 * x1 * x1 - 3.0 * x3 * x3; + z32 = 24.0 * x1 * x2 - 6.0 * x3 * x4; + z33 = 12.0 * x2 * x2 - 3.0 * x4 * x4; + z1 = 3.0 * (a1 * a1 + a2 * a2) + z31 * eosq; + z2 = 6.0 * (a1 * a3 + a2 * a4) + z32 * eosq; + z3 = 3.0 * (a3 * a3 + a4 * a4) + z33 * eosq; + z11 = -6.0 * a1 * a5 + eosq * (-24.0 * x1 * x7 - 6.0 * x3 * x5); + z12 = -6.0 * (a1 * a6 + a3 * a5) + + eosq * (-24.0 * (x2 * x7 + x1 * x8) - 6.0 * (x3 * x6 + x4 * x5)); + z13 = -6.0 * a3 * a6 + eosq * (-24.0 * x2 * x8 - 6.0 * x4 * x6); + z21 = 6.0 * a2 * a5 + eosq * (24.0 * x1 * x5 - 6.0 * x3 * x7); + z22 = 6.0 * (a4 * a5 + a2 * a6) + + eosq * (24.0 * (x2 * x5 + x1 * x6) - 6.0 * (x4 * x7 + x3 * x8)); + z23 = 6.0 * a4 * a6 + eosq * (24.0 * x2 * x6 - 6.0 * x4 * x8); + z1 = z1 + z1 + m_betao2 * z31; + z2 = z2 + z2 + m_betao2 * z32; + z3 = z3 + z3 + m_betao2 * z33; + s3 = cc * xnoi; + s2 = -0.5 * s3 / m_betao; + s4 = s3 * m_betao; + s1 = -15.0 * eq * s4; + s5 = x1 * x3 + x2 * x4; + s6 = x2 * x3 + x1 * x4; + s7 = x2 * x4 - x1 * x3; + se = s1 * zn * s5; + si = s2 * zn * (z11 + z13); + sl = -zn * s3 * (z1 + z3 - 14.0 - 6.0 * eosq); + sgh = s4 * zn * (z31 + z33 - 6.0); + + if (Orbit.Inclination < 5.2359877E-2) + { + sh = 0.0; + } + else + { + sh = -zn * s2 * (z21 + z23); + } + + dp_ee2 = 2.0 * s1 * s6; + dp_e3 = 2.0 * s1 * s7; + dp_xi2 = 2.0 * s2 * z12; + dp_xi3 = 2.0 * s2 * (z13 - z11); + dp_xl2 = -2.0 * s3 * z2; + dp_xl3 = -2.0 * s3 * (z3 - z1); + dp_xl4 = -2.0 * s3 * (-21.0 - 9.0 * eosq) * ze; + dp_xgh2 = 2.0 * s4 * z32; + dp_xgh3 = 2.0 * s4 * (z33 - z31); + dp_xgh4 = -18.0 * s4 * ze; + dp_xh2 = -2.0 * s2 * z22; + dp_xh3 = -2.0 * s2 * (z23 - z21); + + if (pass == 1) + { + // Do lunar terms + dp_sse = se; + dp_ssi = si; + dp_ssl = sl; + dp_ssh = sh / m_sinio; + dp_ssg = sgh - m_cosio * dp_ssh; + dp_se2 = dp_ee2; + dp_si2 = dp_xi2; + dp_sl2 = dp_xl2; + dp_sgh2 = dp_xgh2; + dp_sh2 = dp_xh2; + dp_se3 = dp_e3; + dp_si3 = dp_xi3; + dp_sl3 = dp_xl3; + dp_sgh3 = dp_xgh3; + dp_sh3 = dp_xh3; + dp_sl4 = dp_xl4; + dp_sgh4 = dp_xgh4; + zcosg = dpi_zcosgl; + zsing = dpi_zsingl; + zcosi = dpi_zcosil; + zsini = dpi_zsinil; + zcosh = dpi_zcoshl * cosq + dpi_zsinhl * sinq; + zsinh = sinq * dpi_zcoshl - cosq * dpi_zsinhl; + zn = znl; + + const double c1l = 4.7968065E-7; + + cc = c1l; + ze = zel; + } + } + + #endregion + + dp_sse = dp_sse + se; + dp_ssi = dp_ssi + si; + dp_ssl = dp_ssl + sl; + dp_ssg = dp_ssg + sgh - m_cosio / m_sinio * sh; + dp_ssh = dp_ssh + sh / m_sinio; + + // Geopotential resonance initialization for 12 hour orbits + gp_reso = false; + gp_sync = false; + + double g310; + double f220; + double bfact = 0.0; + + // Determine if orbit is 12- or 24-hour resonant. + // Mean motion is given in radians per minute. + if ((Orbit.MeanMotion > 0.0034906585) && (Orbit.MeanMotion < 0.0052359877)) + { + // Orbit is within the Clarke Belt (period is 24-hour resonant). + // Synchronous resonance terms initialization + gp_reso = true; + gp_sync = true; + + #region 24-hour resonant + + double g200 = 1.0 + eosq * (-2.5 + 0.8125 * eosq); + + g310 = 1.0 + 2.0 * eosq; + + double g300 = 1.0 + eosq * (-6.0 + 6.60937 * eosq); + + f220 = 0.75 * (1.0 + m_cosio) * (1.0 + m_cosio); + + double f311 = 0.9375 * m_sinio * m_sinio * (1.0 + 3 * m_cosio) - 0.75 * (1.0 + m_cosio); + double f330 = 1.0 + m_cosio; + + f330 = 1.875 * f330 * f330 * f330; + + const double q22 = 1.7891679e-06; + const double q33 = 2.2123015e-07; + const double q31 = 2.1460748e-06; + + dp_del1 = 3.0 * m_xnodp * m_xnodp * aqnv * aqnv; + dp_del2 = 2.0 * dp_del1 * f220 * g200 * q22; + dp_del3 = 3.0 * dp_del1 * f330 * g300 * q33 * aqnv; + + dp_del1 = dp_del1 * f311 * g310 * q31 * aqnv; + dp_xlamo = xmao + Orbit.RAAN + Orbit.ArgPerigee - dp_thgr; + bfact = m_xmdot + xpidot - thdt; + bfact = bfact + dp_ssl + dp_ssg + dp_ssh; + + #endregion + } + else if (((Orbit.MeanMotion >= 8.26E-3) && (Orbit.MeanMotion <= 9.24E-3)) && (eq >= 0.5)) + { + // Period is 12-hour resonant + gp_reso = true; + + #region 12-hour resonant + + double eoc = eq * eosq; + double g201 = -0.306 - (eq - 0.64) * 0.440; + + double g211; double g322; + double g410; double g422; + double g520; + + if (eq <= 0.65) + { + g211 = 3.616 - 13.247 * eq + 16.290 * eosq; + g310 = -19.302 + 117.390 * eq - 228.419 * eosq + 156.591 * eoc; + g322 = -18.9068 + 109.7927 * eq - 214.6334 * eosq + 146.5816 * eoc; + g410 = -41.122 + 242.694 * eq - 471.094 * eosq + 313.953 * eoc; + g422 = -146.407 + 841.880 * eq - 1629.014 * eosq + 1083.435 * eoc; + g520 = -532.114 + 3017.977 * eq - 5740.0 * eosq + 3708.276 * eoc; + } + else + { + g211 = -72.099 + 331.819 * eq - 508.738 * eosq + 266.724 * eoc; + g310 = -346.844 + 1582.851 * eq - 2415.925 * eosq + 1246.113 * eoc; + g322 = -342.585 + 1554.908 * eq - 2366.899 * eosq + 1215.972 * eoc; + g410 = -1052.797 + 4758.686 * eq - 7193.992 * eosq + 3651.957 * eoc; + g422 = -3581.69 + 16178.11 * eq - 24462.77 * eosq + 12422.52 * eoc; + + if (eq <= 0.715) + { + g520 = 1464.74 - 4664.75 * eq + 3763.64 * eosq; + } + else + { + g520 = -5149.66 + 29936.92 * eq - 54087.36 * eosq + 31324.56 * eoc; + } + } + + double g533; + double g521; + double g532; + + if (eq < 0.7) + { + g533 = -919.2277 + 4988.61 * eq - 9064.77 * eosq + 5542.21 * eoc; + g521 = -822.71072 + 4568.6173 * eq - 8491.4146 * eosq + 5337.524 * eoc; + g532 = -853.666 + 4690.25 * eq - 8624.77 * eosq + 5341.4 * eoc; + } + else + { + g533 = -37995.78 + 161616.52 * eq - 229838.2 * eosq + 109377.94 * eoc; + g521 = -51752.104 + 218913.95 * eq - 309468.16 * eosq + 146349.42 * eoc; + g532 = -40023.88 + 170470.89 * eq - 242699.48 * eosq + 115605.82 * eoc; + } + + double sini2 = m_sinio * m_sinio; + double cosi2 = m_cosio * m_cosio; + + f220 = 0.75 * (1.0 + 2.0 * m_cosio + cosi2); + + double f221 = 1.5 * sini2; + double f321 = 1.875 * m_sinio * (1.0 - 2.0 * m_cosio - 3.0 * cosi2); + double f322 = -1.875 * m_sinio * (1.0 + 2.0 * m_cosio - 3.0 * cosi2); + double f441 = 35.0 * sini2 * f220; + double f442 = 39.3750 * sini2 * sini2; + double f522 = 9.84375 * m_sinio * (sini2 * (1.0 - 2.0 * m_cosio - 5.0 * cosi2) + + 0.33333333 * (-2.0 + 4.0 * m_cosio + 6.0 * cosi2)); + double f523 = m_sinio * (4.92187512 * sini2 * (-2.0 - 4.0 * m_cosio + 10.0 * cosi2) + + 6.56250012 * (1.0 + 2.0 * m_cosio - 3.0 * cosi2)); + double f542 = 29.53125 * m_sinio * (2.0 - 8.0 * m_cosio + cosi2 * (-12.0 + 8.0 * m_cosio + 10.0 * cosi2)); + double f543 = 29.53125 * m_sinio * (-2.0 - 8.0 * m_cosio + cosi2 * (12.0 + 8.0 * m_cosio - 10.0 * cosi2)); + double xno2 = m_xnodp * m_xnodp; + double ainv2 = aqnv * aqnv; + double temp1 = 3.0 * xno2 * ainv2; + + const double root22 = 1.7891679E-6; + const double root32 = 3.7393792E-7; + const double root44 = 7.3636953E-9; + const double root52 = 1.1428639E-7; + const double root54 = 2.1765803E-9; + + double temp = temp1 * root22; + + dp_d2201 = temp * f220 * g201; + dp_d2211 = temp * f221 * g211; + temp1 = temp1 * aqnv; + temp = temp1 * root32; + dp_d3210 = temp * f321 * g310; + dp_d3222 = temp * f322 * g322; + temp1 = temp1 * aqnv; + temp = 2.0 * temp1 * root44; + dp_d4410 = temp * f441 * g410; + dp_d4422 = temp * f442 * g422; + temp1 = temp1 * aqnv; + temp = temp1 * root52; + dp_d5220 = temp * f522 * g520; + dp_d5232 = temp * f523 * g532; + temp = 2.0 * temp1 * root54; + dp_d5421 = temp * f542 * g521; + dp_d5433 = temp * f543 * g533; + dp_xlamo = xmao + Orbit.RAAN + Orbit.RAAN - dp_thgr - dp_thgr; + bfact = m_xmdot + m_xnodot + m_xnodot - thdt - thdt; + bfact = bfact + dp_ssl + dp_ssh + dp_ssh; + + #endregion + } + + if (gp_reso || gp_sync) + { + dp_xfact = bfact - m_xnodp; + + // Initialize integrator + dp_xli = dp_xlamo; + dp_xni = m_xnodp; + // dp_atime = 0.0; // performed by runtime + dp_stepp = 720.0; + dp_stepn = -720.0; + dp_step2 = 259200.0; + } + } + + // /////////////////////////////////////////////////////////////////////////// + private bool DeepCalcDotTerms(ref double pxndot, ref double pxnddt, ref double pxldot) + { + // Dot terms calculated + if (gp_sync) + { + const double fasx2 = 0.13130908; + const double fasx4 = 2.8843198; + const double fasx6 = 0.37448087; + + pxndot = dp_del1 * Math.Sin(dp_xli - fasx2) + + dp_del2 * Math.Sin(2.0 * (dp_xli - fasx4)) + + dp_del3 * Math.Sin(3.0 * (dp_xli - fasx6)); + pxnddt = dp_del1 * Math.Cos(dp_xli - fasx2) + + 2.0 * dp_del2 * Math.Cos(2.0 * (dp_xli - fasx4)) + + 3.0 * dp_del3 * Math.Cos(3.0 * (dp_xli - fasx6)); + } + else + { + const double g54 = 4.4108898; + const double g52 = 1.0508330; + const double g44 = 1.8014998; + const double g22 = 5.7686396; + const double g32 = 0.95240898; + + double xomi = dp_omegaq + m_omgdot * dp_atime; + double x2omi = xomi + xomi; + double x2li = dp_xli + dp_xli; + + pxndot = dp_d2201 * Math.Sin(x2omi + dp_xli - g22) + + dp_d2211 * Math.Sin(dp_xli - g22) + + dp_d3210 * Math.Sin( xomi + dp_xli - g32) + + dp_d3222 * Math.Sin(-xomi + dp_xli - g32) + + dp_d4410 * Math.Sin(x2omi + x2li - g44) + + dp_d4422 * Math.Sin(x2li - g44) + + dp_d5220 * Math.Sin( xomi + dp_xli - g52) + + dp_d5232 * Math.Sin(-xomi + dp_xli - g52) + + dp_d5421 * Math.Sin( xomi + x2li - g54) + + dp_d5433 * Math.Sin(-xomi + x2li - g54); + + pxnddt = dp_d2201 * Math.Cos(x2omi + dp_xli - g22) + + dp_d2211 * Math.Cos(dp_xli - g22) + + dp_d3210 * Math.Cos( xomi + dp_xli - g32) + + dp_d3222 * Math.Cos(-xomi + dp_xli - g32) + + dp_d5220 * Math.Cos( xomi + dp_xli - g52) + + dp_d5232 * Math.Cos(-xomi + dp_xli - g52) + + 2.0 * (dp_d4410 * Math.Cos(x2omi + x2li - g44) + + dp_d4422 * Math.Cos(x2li - g44) + + dp_d5421 * Math.Cos( xomi + x2li - g54) + + dp_d5433 * Math.Cos(-xomi + x2li - g54)); + } + + pxldot = dp_xni + dp_xfact; + pxnddt = pxnddt * pxldot; + + return true; + } + + // /////////////////////////////////////////////////////////////////////////// + private void DeepCalcIntegrator(ref double pxndot, ref double pxnddt, + ref double pxldot, double delta) + { + DeepCalcDotTerms(ref pxndot, ref pxnddt, ref pxldot); + + dp_xli = dp_xli + pxldot * delta + pxndot * dp_step2; + dp_xni = dp_xni + pxndot * delta + pxnddt * dp_step2; + dp_atime = dp_atime + delta; + } + + // /////////////////////////////////////////////////////////////////////////// + private bool DeepSecular(ref double xmdf, ref double omgadf, ref double xnode, + ref double emm, ref double xincc, ref double xnn, + ref double tsince) + { + // Deep space secular effects + xmdf = xmdf + dp_ssl * tsince; + omgadf = omgadf + dp_ssg * tsince; + xnode = xnode + dp_ssh * tsince; + emm = Orbit.Eccentricity + dp_sse * tsince; + xincc = Orbit.Inclination + dp_ssi * tsince; + + if (xincc < 0.0) + { + xincc = -xincc; + xnode = xnode + Globals.Pi; + omgadf = omgadf - Globals.Pi; + } + + double xnddt = 0.0; + double xndot = 0.0; + double xldot = 0.0; + double ft = 0.0; + double delt = 0.0; + + bool fDone = false; + + if (gp_reso) + { + while (!fDone) + { + if ((dp_atime == 0.0) || + ((tsince >= 0.0) && (dp_atime < 0.0)) || + ((tsince < 0.0) && (dp_atime >= 0.0))) + { + delt = (tsince < 0) ? dp_stepn : dp_stepp; + + // Epoch restart + dp_atime = 0.0; + dp_xni = m_xnodp; + dp_xli = dp_xlamo; + + fDone = true; + } + else + { + if (Math.Abs(tsince) < Math.Abs(dp_atime)) + { + delt = dp_stepp; + + if (tsince >= 0.0) + { + delt = dp_stepn; + } + + DeepCalcIntegrator(ref xndot, ref xnddt, ref xldot, delt); + } + else + { + delt = dp_stepn; + + if (tsince > 0.0) + { + delt = dp_stepp; + } + + fDone = true; + } + } + } + + while (Math.Abs(tsince - dp_atime) >= dp_stepp) + { + DeepCalcIntegrator(ref xndot, ref xnddt, ref xldot, delt); + } + + ft = tsince - dp_atime; + + DeepCalcDotTerms(ref xndot, ref xnddt, ref xldot); + + xnn = dp_xni + xndot * ft + xnddt * ft * ft * 0.5; + + double xl = dp_xli + xldot * ft + xndot * ft * ft * 0.5; + double temp = -xnode + dp_thgr + tsince * thdt; + + xmdf = xl - omgadf + temp; + + if (!gp_sync) + { + xmdf = xl + temp + temp; + } + } + + return true; + } + + // /////////////////////////////////////////////////////////////////////////// + private bool DeepPeriodics(ref double e, ref double xincc, + ref double omgadf, ref double xnode, + ref double xmam, double tsince) + { + // Lunar-solar periodics + double sinis = Math.Sin(xincc); + double cosis = Math.Cos(xincc); + + double sghs = 0.0; + double shs = 0.0; + double sh1 = 0.0; + double pe = 0.0; + double pinc = 0.0; + double pl = 0.0; + double sghl = 0.0; + + double zm = dp_zmos + zns * tsince; + double zf = zm + 2.0 * zes * Math.Sin(zm); + double sinzf = Math.Sin(zf); + double f2 = 0.5 * sinzf * sinzf - 0.25; + double f3 = -0.5 * sinzf * Math.Cos(zf); + double ses = dp_se2 * f2 + dp_se3 * f3; + double sis = dp_si2 * f2 + dp_si3 * f3; + double sls = dp_sl2 * f2 + dp_sl3 * f3 + dp_sl4 * sinzf; + + sghs = dp_sgh2 * f2 + dp_sgh3 * f3 + dp_sgh4 * sinzf; + shs = dp_sh2 * f2 + dp_sh3 * f3; + zm = dp_zmol + znl * tsince; + zf = zm + 2.0 * zel * Math.Sin(zm); + sinzf = Math.Sin(zf); + f2 = 0.5 * sinzf * sinzf - 0.25; + f3 = -0.5 * sinzf * Math.Cos(zf); + + double sel = dp_ee2 * f2 + dp_e3 * f3; + double sil = dp_xi2 * f2 + dp_xi3 * f3; + double sll = dp_xl2 * f2 + dp_xl3 * f3 + dp_xl4 * sinzf; + + sghl = dp_xgh2 * f2 + dp_xgh3 * f3 + dp_xgh4 * sinzf; + sh1 = dp_xh2 * f2 + dp_xh3 * f3; + pe = ses + sel; + pinc = sis + sil; + pl = sls + sll; + + double pgh = sghs + sghl; + double ph = shs + sh1; + + xincc = xincc + pinc; + e = e + pe; + + if (dp_xqncl >= 0.2) + { + // Apply periodics directly + ph = ph / m_sinio; + pgh = pgh - m_cosio * ph; + omgadf = omgadf + pgh; + xnode = xnode + ph; + xmam = xmam + pl; + } + else + { + // Apply periodics with Lyddane modification + double sinok = Math.Sin(xnode); + double cosok = Math.Cos(xnode); + double alfdp = sinis * sinok; + double betdp = sinis * cosok; + double dalf = ph * cosok + pinc * cosis * sinok; + double dbet = -ph * sinok + pinc * cosis * cosok; + + alfdp = alfdp + dalf; + betdp = betdp + dbet; + + double xls = xmam + omgadf + cosis * xnode; + double dls = pl + pgh - pinc * xnode * sinis; + + xls = xls + dls; + xnode = Globals.AcTan(alfdp, betdp); + xmam = xmam + pl; + omgadf = xls - xmam - Math.Cos(xincc) * xnode; + } + + return true; + } + + /// + /// Calculate satellite ECI position/velocity for a given time. + /// + /// Target time, in minutes-past-epoch format. + /// AU-based position/velocity ECI coordinates. + /// + /// This procedure returns the ECI position and velocity for the satellite + /// in the orbit at the given number of minutes since the TLE epoch time. + /// The algorithm uses NORAD's Simplified General Perturbation 4 deep space + /// orbit model. + /// + public override EciTime GetPosition(double tsince) + { + // Update for secular gravity and atmospheric drag + double xmdf = Orbit.MeanAnomaly + m_xmdot * tsince; + double omgadf = Orbit.ArgPerigee + m_omgdot * tsince; + double xnoddf = Orbit.RAAN + m_xnodot * tsince; + double tsq = tsince * tsince; + double xnode = xnoddf + m_xnodcf * tsq; + double tempa = 1.0 - m_c1 * tsince; + double tempe = Orbit.BStar * m_c4 * tsince; + double templ = m_t2cof * tsq; + double xn = m_xnodp; + double em = 0.0; + double xinc = 0.0; + + DeepSecular(ref xmdf, ref omgadf, ref xnode, ref em, ref xinc, ref xn, ref tsince); + + double a = Math.Pow(Globals.Xke / xn, 2.0 / 3.0) * Globals.Sqr(tempa); + double e = em - tempe; + double xmam = xmdf + m_xnodp * templ; + + DeepPeriodics(ref e, ref xinc, ref omgadf, ref xnode, ref xmam, tsince); + + double xl = xmam + omgadf + xnode; + + xn = Globals.Xke / Math.Pow(a, 1.5); + + return FinalPosition(xinc, omgadf, e, a, xl, xnode, xn, tsince); + } + } +} diff --git a/OrbitTools/OrbitTools/Orbit/NoradSGP4.cs b/OrbitTools/OrbitTools/Orbit/NoradSGP4.cs new file mode 100644 index 0000000..fe25c9d --- /dev/null +++ b/OrbitTools/OrbitTools/Orbit/NoradSGP4.cs @@ -0,0 +1,120 @@ +// +// NoradSGP4.cs +// +// Copyright (c) 2003-2010 Michael F. Henry +// +using System; + +namespace Zeptomoby.OrbitTools +{ + /// + /// NORAD SGP4 implementation. + /// + internal class NoradSGP4 : NoradBase + { + private readonly double m_c5; + private readonly double m_omgcof; + private readonly double m_xmcof; + private readonly double m_delmo; + private readonly double m_sinmo; + + // /////////////////////////////////////////////////////////////////////////// + public NoradSGP4(Orbit orbit) : + base(orbit) + { + m_c5 = 2.0 * m_coef1 * m_aodp * m_betao2 * + (1.0 + 2.75 * (m_etasq + m_eeta) + m_eeta * m_etasq); + m_omgcof = Orbit.BStar * m_c3 * Math.Cos(Orbit.ArgPerigee); + m_xmcof = -(2.0 / 3.0) * m_coef * Orbit.BStar * Globals.Ae / m_eeta; + m_delmo = Math.Pow(1.0 + m_eta * Math.Cos(Orbit.MeanAnomaly), 3.0); + m_sinmo = Math.Sin(Orbit.MeanAnomaly); + } + + /// + /// Calculate satellite ECI position/velocity for a given time. + /// + /// Target time, in minutes-past-epoch format. + /// AU-based position/velocity ECI coordinates. + /// + /// This procedure returns the ECI position and velocity for the satellite + /// in the orbit at the given number of minutes since the TLE epoch time. + /// The algorithm uses NORAD's Simplified General Perturbation 4 near earth + /// orbit model. + /// + public override EciTime GetPosition(double tsince) + { + // For m_perigee less than 220 kilometers, the isimp flag is set and + // the equations are truncated to linear variation in square root of a + // and quadratic variation in mean anomaly. Also, the m_c3 term, the + // delta omega term, and the delta m term are dropped. + bool isimp = false; + + if ((m_aodp * (1.0 - m_satEcc) / Globals.Ae) < (220.0 / Globals.Xkmper + Globals.Ae)) + { + isimp = true; + } + + double d2 = 0.0; + double d3 = 0.0; + double d4 = 0.0; + + double t3cof = 0.0; + double t4cof = 0.0; + double t5cof = 0.0; + + if (!isimp) + { + double c1sq = m_c1 * m_c1; + + d2 = 4.0 * m_aodp * m_tsi * c1sq; + + double temp = d2 * m_tsi * m_c1 / 3.0; + + d3 = (17.0 * m_aodp + m_s4) * temp; + d4 = 0.5 * temp * m_aodp * m_tsi * + (221.0 * m_aodp + 31.0 * m_s4) * m_c1; + t3cof = d2 + 2.0 * c1sq; + t4cof = 0.25 * (3.0 * d3 + m_c1 * (12.0 * d2 + 10.0 * c1sq)); + t5cof = 0.2 * (3.0 * d4 + 12.0 * m_c1 * d3 + 6.0 * + d2 * d2 + 15.0 * c1sq * (2.0 * d2 + c1sq)); + } + + // Update for secular gravity and atmospheric drag. + double xmdf = Orbit.MeanAnomaly + m_xmdot * tsince; + double omgadf = Orbit.ArgPerigee + m_omgdot * tsince; + double xnoddf = Orbit.RAAN + m_xnodot * tsince; + double omega = omgadf; + double xmp = xmdf; + double tsq = tsince * tsince; + double xnode = xnoddf + m_xnodcf * tsq; + double tempa = 1.0 - m_c1 * tsince; + double tempe = Orbit.BStar * m_c4 * tsince; + double templ = m_t2cof * tsq; + + if (!isimp) + { + double delomg = m_omgcof * tsince; + double delm = m_xmcof * (Math.Pow(1.0 + m_eta * Math.Cos(xmdf), 3.0) - m_delmo); + double temp = delomg + delm; + + xmp = xmdf + temp; + omega = omgadf - temp; + + double tcube = tsq * tsince; + double tfour = tsince * tcube; + + tempa = tempa - d2 * tsq - d3 * tcube - d4 * tfour; + tempe = tempe + Orbit.BStar * m_c5 * (Math.Sin(xmp) - m_sinmo); + templ = templ + t3cof * tcube + tfour * (t4cof + tsince * t5cof); + } + + double a = m_aodp * Globals.Sqr(tempa); + double e = m_satEcc - tempe; + + double xl = xmp + omega + xnode + m_xnodp * templ; + double xn = Globals.Xke / Math.Pow(a, 1.5); + + return FinalPosition(m_satInc, omgadf, e, a, xl, xnode, xn, tsince); + } + } +} diff --git a/OrbitTools/OrbitTools/Orbit/Orbit.cs b/OrbitTools/OrbitTools/Orbit/Orbit.cs new file mode 100644 index 0000000..e1a0d7a --- /dev/null +++ b/OrbitTools/OrbitTools/Orbit/Orbit.cs @@ -0,0 +1,215 @@ +// +// Orbit.cs +// +// Copyright (c) 2005-2012 Michael F. Henry +// Version 06/2012 +// +using System; + +namespace Zeptomoby.OrbitTools +{ + /// + /// This class accepts a single satellite's NORAD two-line element + /// set and provides information regarding the satellite's orbit + /// such as period, axis length, ECI coordinates, velocity, etc. + /// + public class Orbit + { + // Caching variables + private TimeSpan m_Period = new TimeSpan(0, 0, 0, -1); + + // TLE caching variables + private double m_Inclination; + private double m_Eccentricity; + private double m_RAAN; + private double m_ArgPerigee; + private double m_BStar; + private double m_Drag; + private double m_MeanAnomaly; + private double m_TleMeanMotion; + + // Caching variables recovered from the input TLE elements + private double m_aeAxisSemiMajorRec; // semimajor axis, in AE units + private double m_aeAxisSemiMinorRec; // semiminor axis, in AE units + private double m_rmMeanMotionRec; // radians per minute + private double m_kmPerigeeRec; // perigee, in km + private double m_kmApogeeRec; // apogee, in km + + #region Properties + + private Tle Tle { get; set; } + private NoradBase NoradModel { get; set; } + public Julian Epoch { get; private set; } + public DateTime EpochTime { get { return Epoch.ToTime(); }} + + // "Recovered" from the input elements + public double SemiMajor { get { return m_aeAxisSemiMajorRec; }} + public double SemiMinor { get { return m_aeAxisSemiMinorRec; }} + public double MeanMotion { get { return m_rmMeanMotionRec; }} + public double Major { get { return 2.0 * SemiMajor; }} + public double Minor { get { return 2.0 * SemiMinor; }} + public double Perigee { get { return m_kmPerigeeRec; }} + public double Apogee { get { return m_kmApogeeRec; }} + + public double Inclination { get { return m_Inclination; }} + public double Eccentricity { get { return m_Eccentricity; }} + public double RAAN { get { return m_RAAN; }} + public double ArgPerigee { get { return m_ArgPerigee; }} + public double BStar { get { return m_BStar; }} + public double Drag { get { return m_Drag; }} + public double MeanAnomaly { get { return m_MeanAnomaly; }} + private double TleMeanMotion { get { return m_TleMeanMotion; }} + + public string SatNoradId { get { return Tle.NoradNumber; }} + public string SatName { get { return Tle.Name; }} + public string SatNameLong { get { return SatName + " #" + SatNoradId; }} + + public TimeSpan Period + { + get + { + if (m_Period.TotalSeconds < 0.0) + { + // Calculate the period using the recovered mean motion. + if (MeanMotion == 0) + { + m_Period = new TimeSpan(0, 0, 0); + } + else + { + double secs = (Globals.TwoPi / MeanMotion) * 60.0; + int msecs = (int)((secs - (int)secs) * 1000); + + m_Period = new TimeSpan(0, 0, 0, (int)secs, msecs); + } + } + + return m_Period; + } + } + + #endregion + + #region Construction + + /// + /// Standard constructor. + /// + /// Two-line element orbital parameters. + public Orbit(Tle tle) + { + Tle = tle; + Epoch = Tle.EpochJulian; + + m_Inclination = GetRad(Tle.Field.Inclination); + m_Eccentricity = Tle.GetField(Tle.Field.Eccentricity); + m_RAAN = GetRad(Tle.Field.Raan); + m_ArgPerigee = GetRad(Tle.Field.ArgPerigee); + m_BStar = Tle.GetField(Tle.Field.BStarDrag); + m_Drag = Tle.GetField(Tle.Field.MeanMotionDt); + m_MeanAnomaly = GetRad(Tle.Field.MeanAnomaly); + m_TleMeanMotion = Tle.GetField(Tle.Field.MeanMotion); + + // Recover the original mean motion and semimajor axis from the + // input elements. + double mm = TleMeanMotion; + double rpmin = mm * Globals.TwoPi / Globals.MinPerDay; // rads per minute + + double a1 = Math.Pow(Globals.Xke / rpmin, 2.0 / 3.0); + double e = Eccentricity; + double i = Inclination; + double temp = (1.5 * Globals.Ck2 * (3.0 * Globals.Sqr(Math.Cos(i)) - 1.0) / + Math.Pow(1.0 - e * e, 1.5)); + double delta1 = temp / (a1 * a1); + double a0 = a1 * + (1.0 - delta1 * + ((1.0 / 3.0) + delta1 * + (1.0 + 134.0 / 81.0 * delta1))); + + double delta0 = temp / (a0 * a0); + + m_rmMeanMotionRec = rpmin / (1.0 + delta0); + m_aeAxisSemiMajorRec = a0 / (1.0 - delta0); + m_aeAxisSemiMinorRec = m_aeAxisSemiMajorRec * Math.Sqrt(1.0 - (e * e)); + m_kmPerigeeRec = Globals.Xkmper * (m_aeAxisSemiMajorRec * (1.0 - e) - Globals.Ae); + m_kmApogeeRec = Globals.Xkmper * (m_aeAxisSemiMajorRec * (1.0 + e) - Globals.Ae); + + if (Period.TotalMinutes >= 225.0) + { + // SDP4 - period >= 225 minutes. + NoradModel = new NoradSDP4(this); + } + else + { + // SGP4 - period < 225 minutes + NoradModel = new NoradSGP4(this); + } + } + + #endregion + + #region Get Position + + /// + /// Calculate satellite ECI position/velocity for a given time. + /// + /// Target time, in minutes past the TLE epoch. + /// Kilometer-based position/velocity ECI coordinates. + public EciTime GetPosition(double minutesPastEpoch) + { + EciTime eci = NoradModel.GetPosition(minutesPastEpoch); + + // Convert ECI vector units from AU to kilometers + double radiusAe = Globals.Xkmper / Globals.Ae; + + eci.ScalePosVector(radiusAe); // km + eci.ScaleVelVector(radiusAe * (Globals.MinPerDay / 86400)); // km/sec + + return eci; + } + + /// + /// Calculate ECI position/velocity for a given time. + /// + /// Target time (UTC). + /// Kilometer-based position/velocity ECI coordinates. + public EciTime GetPosition(DateTime utc) + { + return GetPosition(TPlusEpoch(utc).TotalMinutes); + } + + #endregion + + // /////////////////////////////////////////////////////////////////////////// + // Returns elapsed time from epoch to given time. + // Note: "Predicted" TLEs can have epochs in the future. + public TimeSpan TPlusEpoch(DateTime utc) + { + return (utc - EpochTime); + } + + // /////////////////////////////////////////////////////////////////////////// + // Returns elapsed time from epoch to current time. + // Note: "Predicted" TLEs can have epochs in the future. + public TimeSpan TPlusEpoch() + { + return TPlusEpoch(DateTime.UtcNow); + } + + #region Utility + + // /////////////////////////////////////////////////////////////////// + protected double GetRad(Tle.Field fld) + { + return Tle.GetField(fld, Tle.Unit.Radians); + } + + // /////////////////////////////////////////////////////////////////// + protected double GetDeg(Tle.Field fld) + { + return Tle.GetField(fld, Tle.Unit.Degrees); + } + + #endregion + } +} diff --git a/OrbitTools/OrbitTools/Orbit/Orbit.csproj b/OrbitTools/OrbitTools/Orbit/Orbit.csproj new file mode 100644 index 0000000..22fc221 --- /dev/null +++ b/OrbitTools/OrbitTools/Orbit/Orbit.csproj @@ -0,0 +1,137 @@ + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {BAA5FE10-3E3A-4D5D-AB3D-4B50D6AC0321} + Library + Properties + Zeptomoby.OrbitTools + Zeptomoby.OrbitTools.Orbit + v4.0 + 512 + + + 3.5 + + + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + prompt + true + true + false + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + prompt + + + + ..\..\..\packages\Newtonsoft.Json.12.0.1\lib\net40\Newtonsoft.Json.dll + True + + + + ..\..\..\packages\System.Data.SQLite.Core.1.0.110.0\lib\net40\System.Data.SQLite.dll + True + + + + + + + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + + + + + + + {99510ed5-99e0-405d-bcac-9159a7426d61} + Orbit.Core + + + + + + + Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}". + + + + + \ No newline at end of file diff --git a/OrbitTools/OrbitTools/Orbit/Properties/AssemblyInfo.cs b/OrbitTools/OrbitTools/Orbit/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..60c6b1a --- /dev/null +++ b/OrbitTools/OrbitTools/Orbit/Properties/AssemblyInfo.cs @@ -0,0 +1,18 @@ +using System; +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("Zeptomoby.OrbitTools.Orbit")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Zeptomoby.com")] +[assembly: AssemblyProduct("OrbitTools Library - Public (unlicensed) version")] +[assembly: AssemblyCopyright("Copyright 2003-2012 Michael F. Henry")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: ComVisible(false)] +[assembly: CLSCompliant(true)] + +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyVersion ("1.1.0.0")] +[assembly: AssemblyFileVersion("1.1.0.0")] diff --git a/OrbitTools/OrbitTools/Orbit/Properties/revHistory.txt b/OrbitTools/OrbitTools/Orbit/Properties/revHistory.txt new file mode 100644 index 0000000..18587da --- /dev/null +++ b/OrbitTools/OrbitTools/Orbit/Properties/revHistory.txt @@ -0,0 +1,142 @@ + +Revision History for the OrbitTools.Orbit assembly - Public Edition +Copyright (c) Michael F. Henry +www.zeptomoby.com/satellites + +Version 1.1.0.0 2012-10-27 + + OrbitTools.Core library updates: + Class CoordGeo renamed Geo, added derived class GeoTime. + Class CoordTopo renamed Topo. + Use new classes EciTime, GeoTime, TopoTime. + + Breaking changes: + + 1. Orbit.GetPosition() now returns type EciTime. + +Version 1.0.5.0 2012-06-02 + + Updates for OrbitTools.Core changes: + + Boolean "IsAeUnits" was removed from the constructor for class Eci. + Eci.AeToKm() was removed. + The enumeration VectorUnits was removed. + Global functions to convert between degrees and radians were renamed. + +Version 1.0.4.0 2011-12-19 + + Fix Orbit.TPlusEpoch(); it was not using UTC time. + +Version 1.0.3.0 2011-10-01 + + Performance changes: + + 1. Class Orbit now uses caching variables for common TLE orbital + parameters like inclination, argument of perigee, etc. + + Breaking changes: + + 1. Method Orbit.MeanAnomaly() is now property Orbit.MeanAnomaly. + 2. Method Orbit.MeanAnomaly(DateTime) has been removed. + +Version 1.0.2.0 2011-08-21 + + Compile with Visual Studio 2010. + +Version 1.0.1.1 2011-05-17 + + Renamed Orbit.RadGet() to Orbit.GetRad(). + Renamed Orbit.DegGet() to Orbit.GetDeg(). + + Breaking changes: + + 1. Changed Orbit.SatName() (2 overloads) into two properties: + SatName and SatNameLong (a change that was erroneously dropped + from version 1.0.0.0) + 2. Renamed property Orbit.mnMotion to Orbit.TleMeanMotion. + +Version 1.0.1.0 2010-12-31 + + Assembly marked as CLSCompliant(true), and ComVisible(false). + Removed unused local from class NoradSDP4 c'tor. + +Version 1.0.0.0 2010-09-26 + + Split the OrbitTools project into two assemblies: + + Core.dll - Contains core functionality such as Tle, Julian, etc. + Orbit.dll - Contains SGP4/SDP4 and class Orbit functionality. + + Dividing the project into two assemblies allows an easier upgrade path + for users who wish to transition to the Professional version of the + software. + + Orbit.GetPosition() no longer throws class Exception, but instead + throws class DecayException or class PropagationException. + + Compile with Visual Studio 2008. + + Breaking changes from prior (single assembly) release: + + 1. Assembly name is Zeptomoby.OrbitTools.Orbit.dll, and requires + assembly Zeptomoby.OrbitTools.Core.dll. + 2. Changed Orbit.SatName() (2 overloads) into two properties: + SatName and SatNameLong. + 3. Renamed Orbit.mnAnomaly() (2 overloads) to MeanAnomaly(). + 4. Renamed Orbit.getPosition() (2 overloads) to GetPosition(). + + ******************** +Revision history below this point was for the single-assembly version of +the library. Only comments relevant to the files that were moved into the +Orbit.dll assembly have been retained. All other comments were moved into +the revision notes file for the Core.dll assembly. + ******************** + +Version 1.9.1.0 10/18/2009 + + (Version incremented due to changes in Tle.cs) + +Version 1.9.0.0 01/25/2009 + + (Version incremented due to changes in Julian.cs) + +Version 1.8.0.0 01/02/2009 + + The orbit axis recovered from orbital elements is now correctly + associated with the semimajor axis (and not the semiminor axis). + This change does not effect SGP4/SDP4 output calculations, but + does correct class Orbit's convenience properties SemiMajor, SemiMinor, + Major, Minor, etc. Thanks to C. Wee for discovering this issue. + + Renamed class Orbit's public property "mnMotionRec" to "MeanMotion". + +Version 1.7.0.0 12/08/2007 + + Added overrides of several functions to accept System.DateTime arguments. + Converted Orbit.Period() to a property and changed return type to TimeSpan. + +Version 1.6.0.0 08/08/2007 + + (Version incremented due to changes in Tle.cs) + +Version 1.5.0.0 01/28/2006 + + Namespace is now "Zeptomoby.OrbitTools". + +Version 1.4.0.0 11/05/2005 + + More interface rework; many methods are now properties. + +Version 1.3.0.0 07/19/2005 + + Fixed error in calculating Orbit.m_kmPerigeeRec (which is not used by + the SDP4 or SGP4 algorithms). Accessor is now a property. Added Orbit.Apogee + counterpart property. + +Version 1.2.0.0 06/01/2005 + + Rework interfaces using C# accessors. Minor code updates. + +Version 1.1.0.0 12/01/2004 + + Baseline release. \ No newline at end of file diff --git a/OrbitTools/OrbitTools/Orbit/packages.config b/OrbitTools/OrbitTools/Orbit/packages.config new file mode 100644 index 0000000..0aee17e --- /dev/null +++ b/OrbitTools/OrbitTools/Orbit/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/OrbitTools/OrbitTools/readme.txt b/OrbitTools/OrbitTools/readme.txt new file mode 100644 index 0000000..7e1a60f --- /dev/null +++ b/OrbitTools/OrbitTools/readme.txt @@ -0,0 +1,36 @@ + +C# NORAD SGP4/SDP4 Implementation +Developed by Michael F. Henry + +Copyright 2003-2011 Michael F. Henry. All rights reserved. +Permission to use for non-commercial purposes only. +All other uses contact author at mfh@zeptomoby.com + +The files in this directory are compiled to make the two OrbitTools assemblies: + Zeptomoby.OrbitTools.Core.dll + Zeptomoby.OrbitTools.Orbit.dll + +The "Core" assembly contains several utility classes: + + Tle This class encapsulates a single set of NORAD two-line elements. + Site Describes a location on the earth. Given the ECI coordinates of a + satellite, this class can generate Azimuth/Elevation look angles to + the satellite. + Eci This class encapsulates Earth-Centered Inertial coordinates and + velocity for a given moment in time. + Julian - Encapsulates a julian date/time system. + +The "Orbit" assembly contains the main SGP4/SDP4 implementation: + + Orbit Given a Tle object, this class provides information about the orbit + of the described satellite, including inclination, perigee, + eccentricity, etc. Most importantly, it provides ECI coordinates and + velocity for the satellite. + NoradBase, NoradSGP4, NoradSDP4 These classes implement the NORAD + SGP4/SDP4 algorithms. They are used by class Orbit to calculate the + ECI coordinates and velocity of its associated satellite. + +All classes are contained within the Zeptomoby.OrbitTools namespace. + +Michael F. Henry +September, 2011 \ No newline at end of file diff --git a/OrbitTools/readme.txt b/OrbitTools/readme.txt new file mode 100644 index 0000000..555f8e6 --- /dev/null +++ b/OrbitTools/readme.txt @@ -0,0 +1,38 @@ + +C# NORAD SGP4/SDP4 Implementation +Developed by Michael F. Henry + +Copyright 2003-2011 Michael F. Henry. All rights reserved. +Permission to use for non-commercial purposes only. +All other uses contact author at mfh@zeptomoby.com + +The files in this package provide a C# implementation of the SGP4 and SDP4 +algorithms described in the December, 1980 NORAD document "Space Track +Report No. 3". These two orbital models, one for "near-earth" objects and +one for "deep space" objects, are widely used in satellite tracking +software and can produce very accurate results when used with current +NORAD two-line element data. + +The files are contained in two directories: OrbitTools and OrbitToolsDemo. + +The files in the OrbitTools directory are compiled to make the OrbitTools +assemblies, which contain the NORAD SGP4/SDP4 implementations and +miscellaneous supporting classes. See the "readme.txt" file in the +OrbitTools directory for more information. + +The files in the OrbitToolsDemo directory are compiled to make a +sample executable program. The program demonstrates how to use the +OrbitTools assemblies to calculate the ECI position of a satellite, as +well as determine the look angle from a location on the Earth to the +satellite. See the "readme.txt" file in the OrbitToolsDemo directory +for more information. + +The project files were compiled using Microsoft Visual Studio 2010. + +For excellent information on the underlying physics of orbits, visible +satellite observations, current NORAD TLE data, and other related material, +see http://www.celestrak.com which is maintained by Dr. T.S. Kelso. + +Michael F. Henry +December, 2003 +(Updated September, 2011) \ No newline at end of file diff --git a/OxyPlot/.gitattributes b/OxyPlot/.gitattributes new file mode 100644 index 0000000..412eeda --- /dev/null +++ b/OxyPlot/.gitattributes @@ -0,0 +1,22 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp +*.sln merge=union +*.csproj merge=union +*.vbproj merge=union +*.fsproj merge=union +*.dbproj merge=union + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/OxyPlot/.github/CONTRIBUTING.md b/OxyPlot/.github/CONTRIBUTING.md new file mode 100644 index 0000000..0db3f66 --- /dev/null +++ b/OxyPlot/.github/CONTRIBUTING.md @@ -0,0 +1,159 @@ +Contribute to OxyPlot +===================== + +OxyPlot is driven by the community and contributors like you. We are excited that you're interested to help us move forward and improve OxyPlot. + +The flow when contributing to OxyPlot is as follows: + +1. Add issue (bug or new feature) +2. Wait for issue to be tagged `you-take-it` +3. Commit your changes and rebase +4. Create a pull request + +Note that your contributions must be your own work and licensed under the same terms as OxyPlot. + + +## Reporting bugs + +First, search the issue tracker to see if the bug is already there. +Please use the [discussion forums](http://discussion.oxyplot.org/) if you are unsure whether it is a bug in the library or a problem in your code. +When you are sure, please add defects in the issue tracker. + +It is very helpful to get the generated code (use `CTRL+ALT+C` in the plot control) and report (`CTRL+ALT+R`) of the bug plot. You can also take a screen shot and paste it into the description. + +Please use markdown to format your code blocks with [syntax highlighting](https://help.github.com/articles/github-flavored-markdown/#syntax-highlighting) to make it easier for everyone to read. + +You could also use services like http://snipt.org or https://gist.github.com to share code snippets. + +A bug report should include: + +- used platform and tools version +- used OxyPlot version +- version of dependencies (e.g. Xamarin.Forms and Android SDK) +- description of the issue +- sample code to reproduce the issue + + +## Suggesting new features + +Please use the [discussion forums](http://discussion.oxyplot.org/) or add it directly in the issue tracker in the same way as bug reports. + + +## Create your own fork + +1. Log in to GitHub and open the [oxyplot](https://github.com/oxyplot/oxyplot/) origin repository. Click the "Fork" button to create your own fork of the repository. +2. Create a clone on your local system: `git clone https://github.com/yourusername/oxyplot.git` + + +## Branch + +The repository contains two branches: + +- `master` - the release branch (stable channel) +- `develop` - the main branch with the latest development changes (pre-release channel) + +You should base your work on the `develop` branch. + +See [A successful git branching model](http://nvie.com/posts/a-successful-git-branching-model/) for more information about the branching model in use. + +Create a branch for the bugfix/feature you want to work on: `git branch bugfix-some-error` + +Checkout the branch: `git checkout bugfix-some-error` + + +## Commit your changes + +Before you start committing, please define your [full name](https://help.github.com/articles/setting-your-username-in-git/) and [e-mail](https://help.github.com/articles/setting-your-email-in-git/) address in git. + +Please follow the style of the other messages in the commit history. Explain the intentions for the change and include the issue number. Include the issue number in the commit message, e.g. "#9945". + +Please follow the coding-style: + +- write StyleCop compliant code +- include XML comments for all methods, properties and fields +- do not use #regions +- use linq method syntax (not query syntax) +- keep it simple + +To commit your changes, use the git command: `git commit -m ` + +When you are ready to make a pull request, please + +- [Rebase](https://git-scm.com/docs/git-rebase) to the latest commit of the origin +- [Squash](http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html) each logical change to a single commit + +More info about rebasing + +- https://git-scm.com/book/en/v2/Git-Branching-Rebasing +- https://blog.sourcetreeapp.com/2012/08/21/merge-or-rebase/ +- http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html +- https://ariejan.net/2011/07/05/git-squash-your-latests-commits-into-one/ +- https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History +- https://www.atlassian.com/git/tutorials/rewriting-history/git-rebase-i + +Finally, push your commits: `git push` + + +## Requirements for a pull request + +- Include examples or unit tests for the change / new feature +- Update the CHANGELOG.md file +- Update the CONTRIBUTORS and AUTHORS files if you are not already listed there +- Commits are rebased and squashed + + +## Creating a pull request + +- Open the GitHub page for your fork and create a "Pull Request" (PR) +- Include the issue number in the pull request *comment* (not in the *title* where it will not be linked!) + + +## Merging + +When the PR is submitted, Appveyor will build the code and report if the build was successful. +A team member will then review the PR, and merge the branch if everything looks OK. + +If there are some issues with the PR, you must +- update your code and commit changes +- rebase and squash again +- force push: `git push -f` + +The PR will be updated automatically when you push your changes. + + +## Contributing to the documentation + +The documentation is located in the [docs](https://github.com/oxyplot/docs) repository. +See the README.md file for information about how to build the documentation. +Please contribute by creating pull requests! + + +## Contributing to the website + +The source of the [oxyplot.org](http://oxyplot.org) web site can be found in the [oxyplot.github.io](http://github.com/oxyplot/oxyplot.github.io) repository. +Please contribute by creating pull requests! + +The web-site is built by [GitHub](http://github.com/) with [Jekyll](http://jekyllrb.com/). + + +## Common rules + +To prevent the issue (backlog) list to grow indefinitely, an issue will be closed when you are not responding to requests by a project developer. +We need your help, so we will not close an issue by desire. But when you are not answering to a request by for a month we will close the issue. +If this happens in accident, feel free to re-open the issue... + +The issue reporter can close the issue because: + +- he/she verified the fix/change +- found out that it is not an OxyPlot issue but some problem in his/her code + +In case the reporter does not respond to a request: + +- a project developer is allowed to close the issue (after one month) + +Someone can take over the issue from the reporter, when: + +- he/she can reproduce the issue +- has some sample code +- has more time/information to assist in the resolution +- responds to requests by a project developer diff --git a/OxyPlot/.github/ISSUE_TEMPLATE.md b/OxyPlot/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..d8b1dff --- /dev/null +++ b/OxyPlot/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,29 @@ +*IF BUG, INCLUDE THIS PART:* + +### Steps to reproduce + +1. +2. +3. + +Platform: +.NET version: + +### Expected behaviour + +Tell us what should happen + +### Actual behaviour + +Tell us what happens instead +Can you also include a screen shot? + + + +*IF IT IS A NEW FEATURE REQUEST, INCLUDE THIS PART:* + +### Feature description + +Write a description of the feature. How should it work? How should it look? +Include some graphics if this could help! + diff --git a/OxyPlot/.github/PULL_REQUEST_TEMPLATE.md b/OxyPlot/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..118ffba --- /dev/null +++ b/OxyPlot/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,16 @@ +Fixes # . + +### Checklist + +- [ ] I have included examples or tests +- [ ] I have updated the change log +- [ ] I am listed in the CONTRIBUTORS file +- [ ] I have cleaned up the commit history (use rebase and squash) + +### Changes proposed in this pull request: + +- +- +- + +@oxyplot/admins diff --git a/OxyPlot/.gitignore b/OxyPlot/.gitignore new file mode 100644 index 0000000..419011c --- /dev/null +++ b/OxyPlot/.gitignore @@ -0,0 +1,243 @@ + +# Created by https://www.gitignore.io/api/visualstudio + +### VisualStudio ### +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Microsoft Azure ApplicationInsights config file +ApplicationInsights.config + +# Windows Store app package directory +AppPackages/ +BundleArtifacts/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe + +# FAKE - F# Make +.fake/ + +# OxyPlot specific +Output/ \ No newline at end of file diff --git a/OxyPlot/AUTHORS b/OxyPlot/AUTHORS new file mode 100644 index 0000000..bc7e39c --- /dev/null +++ b/OxyPlot/AUTHORS @@ -0,0 +1,15 @@ +# This is the official list of OxyPlot authors for copyright purposes. +# This file is distinct from the CONTRIBUTORS file. +# See the latter for an explanation. + +# Names should be added to this file as +# Name or Organization +# The email address is not required for organizations. + +# Please keep the list sorted. +# Please notify the first person on the list to be added here. + +Oystein Bjorke +DNV GL AS +LECO® Corporation +TrainerRoad, LLC diff --git a/OxyPlot/CHANGELOG.md b/OxyPlot/CHANGELOG.md new file mode 100644 index 0000000..220d9ae --- /dev/null +++ b/OxyPlot/CHANGELOG.md @@ -0,0 +1,261 @@ +# Change Log +All notable changes to this project will be documented in this file. + +## [Unreleased] +### Added +- Added Avalonia based renderer and control library (based off OxyPlot.Wpf). +- New `InterpolationAlgorithm` property in LineSeries and PolylineAnnotation (#494) +- Catmull-Rom spline interpolation algorithms (#494) +- FontSize, FontWeight and FontFamily on Wpf.TextAnnotation (#1023) +- RectangleSeries (#1060) +- InvalidNumberColor on Wpf.LinearColorAxis (#1087) + +### Changed +- Let Gtk# PlotView show up in Ui editor ToolBox of MonoDevelop and XamarinStudio (#1071) +- OxyPlot.Core changed to target netstandard 1.0 and net45 (#946, #1147) +- OxyPlot.ExampleLibrary changed to target netstandard 1.0 and net45 (#946, #1147) +- OxyPlot.Wpf, OxyPlot.WindowsForms, OxyPlot.Pdf changet to .NET 4.5.2 (#946) +- Place label below negative ColumnSeries (#1119) + +### Deprecated +- OxyPlot.WP8 package. Use OxyPlot.Windows instead (#996) + +### Removed +- The `Smooth` property in LineSeries and PolylineAnnotation (#494) +- Support for Silverlight (#1049) +- Support for WP8 (#1050) +- Support for NET40 (#960) +- Support for Windows8 (#1103) + +### Fixed +- Manipulation when using touch is not working in Windows (#1011) +- Ensure a suitable folder is used when creating a temporary file for PNG export in Oxyplot.GtkSharp (#1034) +- RangeColorAxis is not rendered correctly if the axis is reversed (#1035) +- OxyMouseEvents not caught due to InvalidatePlot() in WPF (#382) +- SharpDX DrawText passed degrees to Matrix3x2.Rotation that requires radians (#1075) +- When Color Property of LineSeries is set Markers are not shown (#937) +- Change from linear to logarithmic axis does not work (#1067) +- OxyPalette.Interpolate() throws exception when paletteSize = 1 (#1068) +- Infinite loop in LineAnnotation (#1029) +- OverflowException when zoomed in on logarithmic axis (#1090) +- ScatterSeries with DateTimeAxis/TimeSpanAxis (#1132) +- Exporting TextAnnotation with TextColor having 255 alpha to SVG produces opaque text (#1160) + +## [1.0.0] - 2016-09-11 +### Added +- Added OxyPlot.SharpDX.Wpf NuGet package +- Added DirectX 9.1-10.1 feature level support for SharpDX renderer +- Added SharpDX based renderer and WPF control with SharpDX render (#124) +- Added MinimumMajorStep and MinimumMinorStep to Axes.Axis (#816) +- Added support for vertical X axis to HeatMapSeries (#535) +- Added fall-back rectangle rendering to HeatMapSeries (#801) +- Added logarithmic HeatMapSeries support (#802) and example +- Axis.MaximumRange to limit the zoom (#401) +- Added OxyPlot.Mobile NuGet package to combine the mobile platforms into a single package (#362) +- Support for XWT (#295) +- TwoColorAreaSeries (#299) +- Delta values in AxisChangedEventArgs (#276) +- Git source server (added GitLink build step) (#267,#266) +- iOS PlotView ZoomThreshold/AllowPinchPastZero for use with KeepAspectRatioWhenPinching=false (#359) +- CandleStickAndVolumeSeries and VolumeSeries (#377) +- Axis.DesiredSize property (#383) +- WPF wrapper for BoxPlotSeries (#434) +- Capability to display mean value to BoxPlotSeries (#440) +- LinearBarSeries for WPF (#506) +- TitleToolTip in PlotModel (#508) +- TextColor property on WPF Axis (#452) +- ThreeColorLineSeries (#378) +- CI of the Xamarin.Android, Xamarin.iOS and Xamarin.Forms packages (#274) +- PlotModel.LegendLineSpacing (#622) +- Legend rendering for LinearBarSeries (#663) +- LegendMaxHeight property in PlotModel and Wpf.Plot (#668) +- Support for a Xamarin Forms UWP project with sample app (#697) +- ListBuilder for building lists by reflection (#705) +- F# example (#699) +- Support for discontinuities in AreaSeries (#215) +- Support for Windows Universal 10.0 apps (#615) +- Support Unicode in OxyPlot.Pdf (#789) +- TouchTrackerManipulator (#787) +- Extracted visible window search code from CandleStickSeries and made a generic version in XYSeries. Used it to optimize AreaSeries performance. (#834) +- Optimized rendering performance of RectangleBarSeries (#834). +- PdfExporter implementing IExporter (#845) +- Color minor and major ticks differently (#417) +- Support for PieSeries in OxyPlot.Wpf (#878) +- Filter in example browser (#118) +- Support for tooltips on WPF annotations +- Support for tracker in OxyPlot.GtkSharp +- Improve tracker style (Windows Forms) (#106) +- Font rendering in OxyPlot.GtkSharp improved by using Pango (#972) +- Improved LineSeries performance (#834) +- Fixed bug causing axes titles to not display in OxyPlot.GtkSharp (#989) + +### Changed +- Fixed closing file stream for PdfReportWriter when PdfReportWriter is closed or disposed of. (#892) +- Renamed OxyPlot.WindowsUniversal to OxyPlot.Windows (#242) +- Changed OxyPlot.Xamarin.Forms to require OxyPlot.Mobile dependency instead of each separate NuGet. (#362) +- Renamed OxyPlot.XamarinIOS to OxyPlot.MonoTouch (#327) +- Renamed OxyPlot.XamarinAndroid to OxyPlot.Xamarin.Android (#327) +- Renamed OxyPlot.XamarinForms to OxyPlot.Xamarin.Forms (#327) +- Renamed OxyPlot.XamarinForms.iOS to OxyPlot.Xamarin.Forms.Platform.iOS (#327) +- Renamed OxyPlot.XamarinFormsIOS to OxyPlot.Xamarin.Forms.Platform.iOS.Classic (#327) +- Renamed OxyPlot.XamarinFormsAndroid to OxyPlot.Xamarin.Forms.Platform.Android (#327) +- Renamed OxyPlot.XamarinFormsWinPhone to OxyPlot.Xamarin.Forms.Platform.WP8 (#327) +- Changed OxyPlot.Xamarin.Android target to Android level 10 (#223) +- Separated WPF Plot and PlotView (#252, #239) +- Current CandleStickSeries renamed to OldCandleStickSeries, replaced by a faster implementation (#369) +- Invalidate plot when ItemsSource contents change (INotifyCollectionChanged) on WPF only (#406) +- Xamarin.Forms references updated to 1.5.0.6447 (#293, #439) +- Change OxyPlot.Xamarin.Forms.Platform.Android target to Android level 15 (#439) +- Changed OxyPlot.Xamarin.Forms to portable Profile259 (#439) +- PlotController should not intercept input per default (#446) +- Changed DefaultTrackerFormatString for BoxPlotSeries (to include Mean) (#440) +- Changed Constructor of BoxPlotItem (to include Mean) (#440) +- Changed Axis, Annotation and Series Render() method (removed model parameter) +- Changed PCL project to profile 259, SL5 is separate now (#115) +- Extracted CreateReport() and CreateTextReport() from PlotModel (#517) +- Renamed GetLastUpdateException to GetLastPlotException and added the ability to see render exceptions(#543) +- Move TileMapAnnotation class to example library (#567) +- Change to semantic versioning (#595) +- Change GTKSharp3 project to x86 (#599) +- Change OxyPlot.Xamarin.Android to API Level 15 (#614) +- Add Xamarin.Forms renderer initialization to PlotViewRenderer (#632) +- Marked OxyPlot.Xamarin.Forms.Platform.*.Forms.Init() obsolete (#632) +- Throw exception if Xamarin.Forms renderer is not 'initialized' (#492) +- Make numeric values of DateTimeAxis compatible with ToOADate (#660) +- Make struct types immutable (#692) +- Implement IEquatable for struct types (#692) +- BoxPlotItem changed to reference type (#692) +- Move Xamarin projects to new repository (#777) +- Remove CandleStickSeries.Append (#826) +- Change MinorInterval calculation, add unit test (#133) +- Rewrite LogarithmicAxis tick calculation (#820) +- Change Axis methods to protected virtual (#837) +- Move CalculateMinorInterval and CreateTickValues to AxisUtilities (#837) +- Change default number format to "g6" in Axis base class (#841) +- Push packages to myget.org (#847) +- Change the default format string to `null` for TimeSpanAxis and DateTimeAxis (#951) + +### Removed +- StyleCop tasks (#556) +- OxyPlot.Metro project (superseded by OxyPlot.WindowsUniversal) (#241) +- PlotModel.ToSvg method. Use the SvgExporter instead. (#347) +- Constructors with parameters, use default constructors instead. (#347) +- Axis.ShowMinorTicks property, use MinorTickSize = 0 instead (#347) +- ManipulatorBase.GetCursorType method (#447) +- Model.GetElements() method +- Remove SL4 support (#115) +- Remove NET35 support (#115) +- PlotElement.Format method, use StringHelper.Format instead +- EnumerableExtensions.Reverse removed (#677) +- ListFiller (#705) + +### Fixed +- Added check to LineAnnotation.GetScreenPoints to check if ActualMaximumX==ActualMinimumX for non-curved lines. (#1029) +- Incorrect placment of axis title of axes with AxisDistance (#1065) +- SharpDX control not being rendered when loaded +- SharpDX out of viewport scrolling. +- Multiple mouse clicks not being reported in OxyPlot.GtkSharp (#854) +- StemSeries Tracking to allow tracking on tiny stems (#809) +- Fixed PDFRenderContext text alignment issues for rotated text (#723) +- HeatMapSeries.GetValue returns NaN instead of calculating a wrong value in proximity to NaN (#256) +- Tracker position is wrong when PlotView is offset from origin (#455) +- CategoryAxis should use StringFormat (#415) +- Fixed the dependency of OxyPlot.Xamarin.Forms NuGet (#370) +- Add default ctor for Xamarin.Forms iOS renderer (#348) +- Windows Phone cursor exception (#345) +- Bar/ColumSeries tracker format string bug (#333) +- Fix exception for default tracker format strings (#265) +- Fix center-aligned legends (#79) +- Fix Markdown links to tag comparison URL with footnote-style links +- WPF dispatcher issue (#311, #309) +- Custom colors for scatters (#307) +- Rotated axis labels (#303,#301) +- Floating point error on axis labels (#289, #227) +- Performance of CandleStickSeries (#290) +- Tracker text for StairStepSeries (#263) +- XamarinForms/iOS view not updating when model is changed (#262) +- Improved WPF rendering performance (#260, #259) +- Null reference with MVVM binding (#255) +- WPF PngExporter background (#234) +- XamlExporter background (#233) +- .NET 3.5 build (#229) +- Support WinPhone 8.1 in core NuGet package (#161) +- Draw legend line with custom pattern (#356) +- iOS pan/zoom stability (#336) +- Xamarin.Forms iOS PlotViewRenderer crash (#458) +- Inaccurate tracker when using LogarithmicAxis (#443) +- Fix reset of transforms in WinForms render context (#489) +- Fix StringFormat for TimeSpanAxis not recognizing f, ff, fff, etc (#330) +- Fix LineSeries SMOOTH=True will crash WinForms on right click (#499) +- Fix PlotView leak on iOS (#503) +- This PlotModel is already in use by some other PlotView control (#497) +- LegendTextColor not synchronized between wpf.Plot and InternalModel (#548) +- Legend in CandleStickSeries does not scale correctly (#554) +- Fix CodeGenerator exception for types without parameterless ctor (#573) +- Migrate automatic package restore (#557) +- Fix rendering of rotated 'math' text (#569, #448) +- WPF export demo (#568) +- Fixing a double comparison issue causing infinite loop (#587) +- Fix null reference exception when ActualPoints was null rendering a StairStepSeries (#582) +- Background color in the Xamarin.Forms views (#546) +- IsVisible change in Xamarin.Forms.Platform.iOS (#546) +- Rendering math text with syntax error gets stuck in an endless loop (#624) +- Fix issue with MinimumRange not taking Minimum and Maximum values into account (#550) +- Do not set default Controller in PlotView ctor (#436) +- Corrected owner type of Wpf.PathAnnotation dependency properties (#645) +- Fixed partial plot rendering on Xamarin.Android (#649) +- Default controller should not be shared in WPF PlotViews (#682) +- PositionAtZeroCrossing adds zero crossing line at wrong position (#635) +- Implement AreaSeries.ConstantY2 (#662) +- Null reference exception in ScatterSeries{T} actual points (#636) +- Code generation for HighLowItem (#634) +- Axis.MinimumRange did not work correctly (#711) +- FillColor in ErrorColumnSeries (#736) +- XAxisKey and YAxisKey added to Wpf.Annotations (#743) +- Fix HeatMapSeries cannot plot on Universal Windows (#745) +- Set Resolution in WinForms PngExporter (#754) +- Axis should never go into infinite loop (#758) +- Exception in BarSeriesBase (#790) +- Vertical Axes Title Font Bug (#474) +- Support string[] as ItemsSource in CategoryAxis (#825) +- Horizontal RangeColorAxis (#767) +- LogarithmicAxis sometimes places major ticks outside of the axis range (#850) +- LineSeries with smoothing raises exception (#72) +- Exception when legend is outside and plot area is small (#880) +- Axis alignment with MinimumRange (#794) +- Fixed strange number formatting when using LogarithmicAxis with very large or very small Series (#589) +- Fixed LogarithmicAxis to no longer freeze when the axis is reversed (#925) +- Prevent endless loop in LogarithmicAxis (#957) +- Fixed WPF series data not refreshed when not visible (included WPF LiveDemo) +- Fixed bug in selection of plot to display in OxyPlot.GtkSharp ExampleBrowser (#979) +- Fixed non-interpolation of HeatMapSeries in OxyPlot.GtkSharp (#980) +- Fixed axis min/max calc and axis assignment for CandleStick + VolumeSeries (#389) +- Fixed drawing of plot backgrounds in OxyPlot.GtkSharp (#990) + +## [0.2014.1.546] - 2014-10-22 +### Added +- Support data binding paths ("Point.X") (#210) +- Support for Xamarin.Forms (#204) +- Support for Windows Universal apps (#190) +- Improve TrackerFormatString consistency (#214) +- Support LineColor.BrokenLineColor +- LabelFormatString for ScatterSeries (#12) + +### Changed +- Changed tracker format strings arguments (#214) +- Rename OxyPenLineJoin to LineJoin +- Rename LineStyle.Undefined to LineStyle.Automatic + +### Fixed +- Improved text rendering for Android and iOS (#209) +- Custom shape outline for PointAnnotation (#174) +- Synchronize Wpf.Axis.MinimumRange (#205) +- TrackerHitResult bug (#198) +- Position of axis when PositionAtZeroCrossing = true (#189) +- Expose ScatterSeries.ActualPoints (#201) +- Add overridable Axis.FormatValueOverride (#181) +- PngExporter text formatting (#170) + +[Unreleased]: https://github.com/oxyplot/oxyplot/compare/v1.0.0...HEAD +[1.0.0]: https://github.com/oxyplot/oxyplot/compare/v0.2014.1.546...v1.0.0 +[0.2014.1.546]: https://github.com/oxyplot/oxyplot/compare/v0.0.1...v0.2014.1.546 diff --git a/OxyPlot/CODE_OF_CONDUCT.md b/OxyPlot/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..21295b4 --- /dev/null +++ b/OxyPlot/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at mail@oxyplot.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/OxyPlot/CONTRIBUTORS b/OxyPlot/CONTRIBUTORS new file mode 100644 index 0000000..a6ab15a --- /dev/null +++ b/OxyPlot/CONTRIBUTORS @@ -0,0 +1,115 @@ +# This is the official list of people who have contributed +# to the OxyPlot repository. +# The AUTHORS file lists the copyright holders; this file +# lists people. + +# People submitting code should be listed in this file (by email address). + +# Names should be added to this file like so: +# Name + +# Please keep the list sorted. + +Auriou +Bartłomiej Szypelow +benjaminrupp +Benoit Blanchon <> +br +brantheman +Brannon King +Bryan Freeman +Brian Lim +Caleb Clarke +Carlos Anderson +Carlos Teixeira +Chase Long +Choden Konigsmark +classicboss302 +csabar +Cyril Martin +Dan Aizenstros +darrelbrown +David Laundav +David Wong +DJDAS +DNV GL AS +Don Syme +DotNetDoctor +efontana2 +elliatab +episage +eric +Federico Coppola +Francois Botha +Garrett +Geert van Horrik +Gimly +Herman Eldering +Iain Nicol +Ilja Nosik +Ilya Skriblovsky +Iurii Gazin +jaykul +Jeremy Koritzinsky +jezza323 +Johan +Johan20D +Jonathan Arweck +Jonathan Shore +julien.bataille +Just Slon +Kaplas80 +kc1212 +kenny_evoleap +Kenny Nygaard +Kevin Crowell +Kyle Pulvermacher +LECO® Corporation +Levi Botelho +Linquize +lsowen +Luka B +Matt Williams +Matthew Leibowitz +Memphisch +Mendel Monteiro-Beckerman +methdotnet +mirolev +Mitch-Connor +moes_leco +moljac +mroth +mrtncls +Oleg Tarasov +Oystein Bjorke +Patrice Marin +Philippe AURIOU +Piotr Warzocha +Poul Erik Venø +Rik Borger +ryang +Sarah Müller +Senen Fernandez +Shankar Mathiah Nanjundan +Shun-ichi Goto +Soarc +Stefan Rado +stefan-schweiger +Steve Hoelzer +Surfin Bird +Sven Dummis +Taldoras +Tandy Carmichael +Thorsten Claff +thepretender +tephyrnex +Thomas Ibel +Tomasz Cielecki +ToplandJ +Udo Liess +VisualMelon +vhoehn +Vsevolod Kukol +Xavier +zur003 +Markus Ebner diff --git a/OxyPlot/GitVersion.yml b/OxyPlot/GitVersion.yml new file mode 100644 index 0000000..ae7bad1 --- /dev/null +++ b/OxyPlot/GitVersion.yml @@ -0,0 +1,2 @@ +next-version: 2.0.0 +branches: {} diff --git a/OxyPlot/Icons/OxyPlot.ico b/OxyPlot/Icons/OxyPlot.ico new file mode 100644 index 0000000..bc94ef8 Binary files /dev/null and b/OxyPlot/Icons/OxyPlot.ico differ diff --git a/OxyPlot/Icons/OxyPlot_100.png b/OxyPlot/Icons/OxyPlot_100.png new file mode 100644 index 0000000..cfef950 Binary files /dev/null and b/OxyPlot/Icons/OxyPlot_100.png differ diff --git a/OxyPlot/Icons/OxyPlot_1024.png b/OxyPlot/Icons/OxyPlot_1024.png new file mode 100644 index 0000000..e1de425 Binary files /dev/null and b/OxyPlot/Icons/OxyPlot_1024.png differ diff --git a/OxyPlot/Icons/OxyPlot_114.png b/OxyPlot/Icons/OxyPlot_114.png new file mode 100644 index 0000000..994bb2e Binary files /dev/null and b/OxyPlot/Icons/OxyPlot_114.png differ diff --git a/OxyPlot/Icons/OxyPlot_120.png b/OxyPlot/Icons/OxyPlot_120.png new file mode 100644 index 0000000..1020395 Binary files /dev/null and b/OxyPlot/Icons/OxyPlot_120.png differ diff --git a/OxyPlot/Icons/OxyPlot_128.png b/OxyPlot/Icons/OxyPlot_128.png new file mode 100644 index 0000000..e0fa4f3 Binary files /dev/null and b/OxyPlot/Icons/OxyPlot_128.png differ diff --git a/OxyPlot/Icons/OxyPlot_144.png b/OxyPlot/Icons/OxyPlot_144.png new file mode 100644 index 0000000..88c764e Binary files /dev/null and b/OxyPlot/Icons/OxyPlot_144.png differ diff --git a/OxyPlot/Icons/OxyPlot_150.png b/OxyPlot/Icons/OxyPlot_150.png new file mode 100644 index 0000000..2bb6e23 Binary files /dev/null and b/OxyPlot/Icons/OxyPlot_150.png differ diff --git a/OxyPlot/Icons/OxyPlot_152.png b/OxyPlot/Icons/OxyPlot_152.png new file mode 100644 index 0000000..3dbd9fa Binary files /dev/null and b/OxyPlot/Icons/OxyPlot_152.png differ diff --git a/OxyPlot/Icons/OxyPlot_16.png b/OxyPlot/Icons/OxyPlot_16.png new file mode 100644 index 0000000..6f083d0 Binary files /dev/null and b/OxyPlot/Icons/OxyPlot_16.png differ diff --git a/OxyPlot/Icons/OxyPlot_20.png b/OxyPlot/Icons/OxyPlot_20.png new file mode 100644 index 0000000..cd63c67 Binary files /dev/null and b/OxyPlot/Icons/OxyPlot_20.png differ diff --git a/OxyPlot/Icons/OxyPlot_210.png b/OxyPlot/Icons/OxyPlot_210.png new file mode 100644 index 0000000..ffa4c9e Binary files /dev/null and b/OxyPlot/Icons/OxyPlot_210.png differ diff --git a/OxyPlot/Icons/OxyPlot_24.png b/OxyPlot/Icons/OxyPlot_24.png new file mode 100644 index 0000000..cc0e878 Binary files /dev/null and b/OxyPlot/Icons/OxyPlot_24.png differ diff --git a/OxyPlot/Icons/OxyPlot_256.png b/OxyPlot/Icons/OxyPlot_256.png new file mode 100644 index 0000000..a3875f7 Binary files /dev/null and b/OxyPlot/Icons/OxyPlot_256.png differ diff --git a/OxyPlot/Icons/OxyPlot_270.png b/OxyPlot/Icons/OxyPlot_270.png new file mode 100644 index 0000000..d62e9c3 Binary files /dev/null and b/OxyPlot/Icons/OxyPlot_270.png differ diff --git a/OxyPlot/Icons/OxyPlot_29.png b/OxyPlot/Icons/OxyPlot_29.png new file mode 100644 index 0000000..9c59de9 Binary files /dev/null and b/OxyPlot/Icons/OxyPlot_29.png differ diff --git a/OxyPlot/Icons/OxyPlot_32.png b/OxyPlot/Icons/OxyPlot_32.png new file mode 100644 index 0000000..e8c88ed Binary files /dev/null and b/OxyPlot/Icons/OxyPlot_32.png differ diff --git a/OxyPlot/Icons/OxyPlot_40.png b/OxyPlot/Icons/OxyPlot_40.png new file mode 100644 index 0000000..db2dd76 Binary files /dev/null and b/OxyPlot/Icons/OxyPlot_40.png differ diff --git a/OxyPlot/Icons/OxyPlot_48.png b/OxyPlot/Icons/OxyPlot_48.png new file mode 100644 index 0000000..ccaa964 Binary files /dev/null and b/OxyPlot/Icons/OxyPlot_48.png differ diff --git a/OxyPlot/Icons/OxyPlot_50.png b/OxyPlot/Icons/OxyPlot_50.png new file mode 100644 index 0000000..83b35d5 Binary files /dev/null and b/OxyPlot/Icons/OxyPlot_50.png differ diff --git a/OxyPlot/Icons/OxyPlot_512.png b/OxyPlot/Icons/OxyPlot_512.png new file mode 100644 index 0000000..51ce631 Binary files /dev/null and b/OxyPlot/Icons/OxyPlot_512.png differ diff --git a/OxyPlot/Icons/OxyPlot_57.png b/OxyPlot/Icons/OxyPlot_57.png new file mode 100644 index 0000000..12031e1 Binary files /dev/null and b/OxyPlot/Icons/OxyPlot_57.png differ diff --git a/OxyPlot/Icons/OxyPlot_58.png b/OxyPlot/Icons/OxyPlot_58.png new file mode 100644 index 0000000..44fe051 Binary files /dev/null and b/OxyPlot/Icons/OxyPlot_58.png differ diff --git a/OxyPlot/Icons/OxyPlot_64.png b/OxyPlot/Icons/OxyPlot_64.png new file mode 100644 index 0000000..136f55e Binary files /dev/null and b/OxyPlot/Icons/OxyPlot_64.png differ diff --git a/OxyPlot/Icons/OxyPlot_72.png b/OxyPlot/Icons/OxyPlot_72.png new file mode 100644 index 0000000..73f1f09 Binary files /dev/null and b/OxyPlot/Icons/OxyPlot_72.png differ diff --git a/OxyPlot/Icons/OxyPlot_76.png b/OxyPlot/Icons/OxyPlot_76.png new file mode 100644 index 0000000..b95f179 Binary files /dev/null and b/OxyPlot/Icons/OxyPlot_76.png differ diff --git a/OxyPlot/Icons/OxyPlot_80.png b/OxyPlot/Icons/OxyPlot_80.png new file mode 100644 index 0000000..8002ee5 Binary files /dev/null and b/OxyPlot/Icons/OxyPlot_80.png differ diff --git a/OxyPlot/Icons/OxyPlot_96.png b/OxyPlot/Icons/OxyPlot_96.png new file mode 100644 index 0000000..1b4f315 Binary files /dev/null and b/OxyPlot/Icons/OxyPlot_96.png differ diff --git a/OxyPlot/Icons/favicon.ico b/OxyPlot/Icons/favicon.ico new file mode 100644 index 0000000..92c3c98 Binary files /dev/null and b/OxyPlot/Icons/favicon.ico differ diff --git a/OxyPlot/LICENSE b/OxyPlot/LICENSE new file mode 100644 index 0000000..bba637a --- /dev/null +++ b/OxyPlot/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2014 OxyPlot contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/OxyPlot/README.md b/OxyPlot/README.md new file mode 100644 index 0000000..eb1fb90 --- /dev/null +++ b/OxyPlot/README.md @@ -0,0 +1,60 @@ +OxyPlot is a cross-platform plotting library for .NET + +- [Web page](http://oxyplot.org) +- [Documentation](http://docs.oxyplot.org/) +- [Announcements](http://oxyplot.org/announcements) / [atom](http://oxyplot.org/atom.xml) +- [Discussion forum](http://discussion.oxyplot.org) +- [Source repository](http://github.com/oxyplot/oxyplot) +- [Issue tracker](http://github.com/oxyplot/oxyplot/issues) +- [NuGet packages](http://www.nuget.org/packages?q=oxyplot) +- [Stack Overflow](http://stackoverflow.com/questions/tagged/oxyplot) +- [Twitter](https://twitter.com/hashtag/oxyplot) +- [Gitter](https://gitter.im/oxyplot/oxyplot) (chat) + +![License](https://img.shields.io/badge/license-MIT-red.svg) +[![Build status](https://img.shields.io/appveyor/ci/objorke/oxyplot/develop.svg)](https://ci.appveyor.com/project/objorke/oxyplot) + +![Plot](http://oxyplot.org/public/images/normal-distributions.png) + +#### Getting started + +1. Use the NuGet package manager to add a reference to OxyPlot (see details below if you want to use pre-release packages) +2. Add a `PlotView` to your user interface +3. Create a `PlotModel` in your code +4. Bind the `PlotModel` to the `Model` property of your `PlotView` + +#### Examples + +You can find examples in the `/Source/Examples` folder in the code repository. + +#### NuGet packages + +The latest pre-release packages are pushed by AppVeyor CI to [myget.org](https://www.myget.org/) +To install these packages, set the myget.org package source `https://www.myget.org/F/oxyplot` and remember the "-pre" flag. + +The stable release packages will be pushed to [nuget.org](https://www.nuget.org/packages?q=oxyplot). +Note that we have currently have a lot of old (v2015.*) and pre-release packages on this feed, this will be cleaned up as soon as we release [v1.0](https://github.com/oxyplot/oxyplot/milestones/v1.0). + +Package | Targets | Dependencies +--------|---------|------------- +OxyPlot.Core | .NET Standard 1.0 | +OxyPlot.Wpf | .NET 4.5.2 | +OxyPlot.WindowsForms | .NET 4.5.2 | +OxyPlot.Windows | Universal Windows 10.0 | +OxyPlot.GtkSharp | .NET 4.5.2 | GTK# 2 +OxyPlot.GtkSharp3 | .NET 4.5.2 | GTK# 3 +OxyPlot.Xamarin.Android | MonoAndroid | +OxyPlot.Xamarin.iOS | MonoTouch and iOS10 | +OxyPlot.Xamarin.Mac | Mac20 | +OxyPlot.Xamarin.Forms | MonoTouch, iOS10, MonoAndroid, WP8 | +OxyPlot.Xwt | .NET 4.5.2 | +OxyPlot.SharpDX.Wpf | .NET 4.5.2 | +OxyPlot.Avalonia | .NET 4.5.2 | +OxyPlot.OpenXML | .NET 4.5.2 | +OxyPlot.Pdf | .NET 4.5.2 | PdfSharp +OxyPlot.Contrib | .NET Standard 1.0 | +OxyPlot.ExampleLibrary | .NET Standard 1.0 | + +#### Contribute + +See [Contributing](.github/CONTRIBUTING.md) for information about how to contribute! diff --git a/OxyPlot/Source/.editorconfig b/OxyPlot/Source/.editorconfig new file mode 100644 index 0000000..58f5348 --- /dev/null +++ b/OxyPlot/Source/.editorconfig @@ -0,0 +1,10 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +end_of_line = crlf +insert_final_newline = true +indent_style = space +indent_size = 4 diff --git a/OxyPlot/Source/AssemblyInfo.cs b/OxyPlot/Source/AssemblyInfo.cs new file mode 100644 index 0000000..263ede5 --- /dev/null +++ b/OxyPlot/Source/AssemblyInfo.cs @@ -0,0 +1,19 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +using System.Reflection; + +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyProduct("OxyPlot")] +[assembly: AssemblyCompany("OxyPlot")] +[assembly: AssemblyCopyright("Copyright (c) 2014 OxyPlot contributors")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The version numbers are updated by the build script. See ~/appveyor.yml +[assembly: AssemblyVersion("2.0.0")] +[assembly: AssemblyInformationalVersion("0.0.1-alpha")] +[assembly: AssemblyFileVersion("2.0.0")] \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Annotations/AnnotationExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Annotations/AnnotationExamples.cs new file mode 100644 index 0000000..e79435b --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Annotations/AnnotationExamples.cs @@ -0,0 +1,31 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using OxyPlot; + using OxyPlot.Annotations; + using OxyPlot.Axes; + + [Examples("Annotations"), Tags("Annotations")] + public static class AnnotationExamples + { + [Example("Tool tips")] + public static PlotModel ToolTips() + { + var model = new PlotModel { Title = "Tool tips" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + model.Annotations.Add(new LineAnnotation { Slope = 0.1, Intercept = 1, Text = "LineAnnotation", ToolTip = "This is a tool tip for the LineAnnotation" }); + model.Annotations.Add(new RectangleAnnotation { MinimumX = 20, MaximumX = 70, MinimumY = 10, MaximumY = 40, TextRotation = 10, Text = "RectangleAnnotation", ToolTip = "This is a tooltip for the RectangleAnnotation", Fill = OxyColor.FromAColor(99, OxyColors.Blue), Stroke = OxyColors.Black, StrokeThickness = 2 }); + model.Annotations.Add(new EllipseAnnotation { X = 20, Y = 60, Width = 20, Height = 15, Text = "EllipseAnnotation", ToolTip = "This is a tool tip for the EllipseAnnotation", TextRotation = 10, Fill = OxyColor.FromAColor(99, OxyColors.Green), Stroke = OxyColors.Black, StrokeThickness = 2 }); + model.Annotations.Add(new PointAnnotation { X = 50, Y = 50, Text = "P1", ToolTip = "This is a tool tip for the PointAnnotation" }); + model.Annotations.Add(new ArrowAnnotation { StartPoint = new DataPoint(8, 4), EndPoint = new DataPoint(0, 0), Color = OxyColors.Green, Text = "ArrowAnnotation", ToolTip = "This is a tool tip for the ArrowAnnotation" }); + model.Annotations.Add(new TextAnnotation { TextPosition = new DataPoint(60, 60), Text = "TextAnnotation", ToolTip = "This is a tool tip for the TextAnnotation" }); + return model; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Annotations/ArrowAnnotationExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Annotations/ArrowAnnotationExamples.cs new file mode 100644 index 0000000..5ee7113 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Annotations/ArrowAnnotationExamples.cs @@ -0,0 +1,28 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using OxyPlot; + using OxyPlot.Annotations; + using OxyPlot.Axes; + + [Examples("ArrowAnnotation"), Tags("Annotations")] + public static class ArrowAnnotationExamples + { + [Example("ArrowAnnotation")] + public static PlotModel ArrowAnnotation() + { + var model = new PlotModel { Title = "ArrowAnnotations" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = -40, Maximum = 60 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = -10, Maximum = 10 }); + model.Annotations.Add(new ArrowAnnotation { StartPoint = new DataPoint(8, 4), EndPoint = new DataPoint(0, 0), Color = OxyColors.Green, Text = "StartPoint and EndPoint" }); + model.Annotations.Add(new ArrowAnnotation { ArrowDirection = new ScreenVector(30, 70), EndPoint = new DataPoint(40, -3), Color = OxyColors.Blue, Text = "ArrowDirection and EndPoint" }); + model.Annotations.Add(new ArrowAnnotation { ArrowDirection = new ScreenVector(30, -70), EndPoint = new DataPoint(10, -3), HeadLength = 14, HeadWidth = 6, Veeness = 4, Color = OxyColors.Red, Text = "HeadLength = 20, HeadWidth = 10, Veeness = 4" }); + return model; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Annotations/EllipseAnnotationExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Annotations/EllipseAnnotationExamples.cs new file mode 100644 index 0000000..ce93b6c --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Annotations/EllipseAnnotationExamples.cs @@ -0,0 +1,30 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using OxyPlot; + using OxyPlot.Annotations; + using OxyPlot.Axes; + + [Examples("EllipseAnnotation"), Tags("Annotations")] + public static class EllipseAnnotationExamples + { + [Example("EllipseAnnotation")] + public static PlotModel EllipseAnnotation() + { + var model = new PlotModel { Title = "EllipseAnnotation" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + model.Annotations.Add(new EllipseAnnotation { X = 20, Y = 60, Width = 20, Height = 15, Text = "EllipseAnnotation", TextRotation = 10, Fill = OxyColor.FromAColor(99, OxyColors.Green), Stroke = OxyColors.Black, StrokeThickness = 2 }); + + model.Annotations.Add(new EllipseAnnotation { X = 20, Y = 20, Width = 20, Height = 20, Fill = OxyColor.FromAColor(99, OxyColors.Green), Stroke = OxyColors.Black, StrokeThickness = 2 }); + model.Annotations.Add(new EllipseAnnotation { X = 30, Y = 20, Width = 20, Height = 20, Fill = OxyColor.FromAColor(99, OxyColors.Red), Stroke = OxyColors.Black, StrokeThickness = 2 }); + model.Annotations.Add(new EllipseAnnotation { X = 25, Y = 30, Width = 20, Height = 20, Fill = OxyColor.FromAColor(99, OxyColors.Blue), Stroke = OxyColors.Black, StrokeThickness = 2 }); + return model; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Annotations/FunctionAnnotationExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Annotations/FunctionAnnotationExamples.cs new file mode 100644 index 0000000..a554af1 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Annotations/FunctionAnnotationExamples.cs @@ -0,0 +1,29 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + + using OxyPlot; + using OxyPlot.Annotations; + using OxyPlot.Axes; + + [Examples("FunctionAnnotation"), Tags("Annotations")] + public static class FunctionAnnotationExamples + { + [Example("FunctionAnnotation")] + public static PlotModel FunctionAnnotation() + { + var model = new PlotModel { Title = "FunctionAnnotation" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = -20, Maximum = 80 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = -10, Maximum = 10 }); + model.Annotations.Add(new FunctionAnnotation { Equation = Math.Sin, StrokeThickness = 2, Color = OxyColor.FromAColor(120, OxyColors.Blue), Text = "f(x)=sin(x)" }); + model.Annotations.Add(new FunctionAnnotation { Equation = y => y * y, StrokeThickness = 2, Color = OxyColor.FromAColor(120, OxyColors.Red), Type = FunctionAnnotationType.EquationY, Text = "f(y)=y^2" }); + return model; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Annotations/ImageAnnotationExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Annotations/ImageAnnotationExamples.cs new file mode 100644 index 0000000..c3b9639 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Annotations/ImageAnnotationExamples.cs @@ -0,0 +1,275 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + using System.Reflection; + + using OxyPlot; + using OxyPlot.Annotations; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("ImageAnnotation"), Tags("Annotations")] + public static class ImageAnnotationExamples + { + [Example("ImageAnnotation")] + public static PlotModel ImageAnnotation() + { + var model = new PlotModel { Title = "ImageAnnotation", PlotMargins = new OxyThickness(60, 4, 4, 60) }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + + OxyImage image; + var assembly = typeof(ImageAnnotationExamples).GetTypeInfo().Assembly; + using (var stream = assembly.GetManifestResourceStream("ExampleLibrary.Resources.OxyPlot.png")) + { + image = new OxyImage(stream); + } + + // Centered in plot area, filling width + model.Annotations.Add(new ImageAnnotation + { + ImageSource = image, + Opacity = 0.2, + Interpolate = false, + X = new PlotLength(0.5, PlotLengthUnit.RelativeToPlotArea), + Y = new PlotLength(0.5, PlotLengthUnit.RelativeToPlotArea), + Width = new PlotLength(1, PlotLengthUnit.RelativeToPlotArea), + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Middle + }); + + // Relative to plot area, inside top/right corner, 120pt wide + model.Annotations.Add(new ImageAnnotation + { + ImageSource = image, + X = new PlotLength(1, PlotLengthUnit.RelativeToPlotArea), + Y = new PlotLength(0, PlotLengthUnit.RelativeToPlotArea), + Width = new PlotLength(120, PlotLengthUnit.ScreenUnits), + HorizontalAlignment = HorizontalAlignment.Right, + VerticalAlignment = VerticalAlignment.Top + }); + + // Relative to plot area, above top/left corner, 20pt high + model.Annotations.Add(new ImageAnnotation + { + ImageSource = image, + X = new PlotLength(0, PlotLengthUnit.RelativeToPlotArea), + Y = new PlotLength(0, PlotLengthUnit.RelativeToPlotArea), + OffsetY = new PlotLength(-5, PlotLengthUnit.ScreenUnits), + Height = new PlotLength(20, PlotLengthUnit.ScreenUnits), + HorizontalAlignment = HorizontalAlignment.Left, + VerticalAlignment = VerticalAlignment.Bottom + }); + + // At the point (50,50), 200pt wide + model.Annotations.Add(new ImageAnnotation + { + ImageSource = image, + X = new PlotLength(50, PlotLengthUnit.Data), + Y = new PlotLength(50, PlotLengthUnit.Data), + Width = new PlotLength(200, PlotLengthUnit.ScreenUnits), + HorizontalAlignment = HorizontalAlignment.Left, + VerticalAlignment = VerticalAlignment.Top + }); + + // At the point (50,20), 50 x units wide + model.Annotations.Add(new ImageAnnotation + { + ImageSource = image, + X = new PlotLength(50, PlotLengthUnit.Data), + Y = new PlotLength(20, PlotLengthUnit.Data), + Width = new PlotLength(50, PlotLengthUnit.Data), + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Top + }); + + // Relative to the viewport, centered at the bottom, with offset (could also use bottom vertical alignment) + model.Annotations.Add(new ImageAnnotation + { + ImageSource = image, + X = new PlotLength(0.5, PlotLengthUnit.RelativeToViewport), + Y = new PlotLength(1, PlotLengthUnit.RelativeToViewport), + OffsetY = new PlotLength(-35, PlotLengthUnit.ScreenUnits), + Height = new PlotLength(30, PlotLengthUnit.ScreenUnits), + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Top + }); + + // Changing opacity + for (int y = 0; y < 10; y++) + { + model.Annotations.Add( + new ImageAnnotation + { + ImageSource = image, + Opacity = (y + 1) / 10.0, + X = new PlotLength(10, PlotLengthUnit.Data), + Y = new PlotLength(y * 2, PlotLengthUnit.Data), + Width = new PlotLength(100, PlotLengthUnit.ScreenUnits), + HorizontalAlignment = HorizontalAlignment.Center, + VerticalAlignment = VerticalAlignment.Bottom + }); + } + + return model; + } + + [Example("ImageAnnotation - gradient backgrounds")] + public static PlotModel ImageAnnotationAsBackgroundGradient() + { + // http://en.wikipedia.org/wiki/Chartjunk + var model = new PlotModel { Title = "Using ImageAnnotations to draw a gradient backgrounds", Subtitle = "But do you really want this? This is called 'chartjunk'!", PlotMargins = new OxyThickness(60, 4, 4, 60) }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + + // create a gradient image of height n + int n = 256; + var imageData1 = new OxyColor[1, n]; + for (int i = 0; i < n; i++) + { + imageData1[0, i] = OxyColor.Interpolate(OxyColors.Blue, OxyColors.Red, i / (n - 1.0)); + } + + var image1 = OxyImage.Create(imageData1, ImageFormat.Png); // png is required for silverlight + + // or create a gradient image of height 2 (requires bitmap interpolation to be supported) + var imageData2 = new OxyColor[1, 2]; + imageData2[0, 0] = OxyColors.Yellow; // top color + imageData2[0, 1] = OxyColors.Gray; // bottom color + + var image2 = OxyImage.Create(imageData2, ImageFormat.Png); // png is required for silverlight + + // gradient filling the viewport + model.Annotations.Add(new ImageAnnotation + { + ImageSource = image2, + Interpolate = true, + Layer = AnnotationLayer.BelowAxes, + X = new PlotLength(0, PlotLengthUnit.RelativeToViewport), + Y = new PlotLength(0, PlotLengthUnit.RelativeToViewport), + Width = new PlotLength(1, PlotLengthUnit.RelativeToViewport), + Height = new PlotLength(1, PlotLengthUnit.RelativeToViewport), + HorizontalAlignment = HorizontalAlignment.Left, + VerticalAlignment = VerticalAlignment.Top + }); + + // gradient filling the plot area + model.Annotations.Add(new ImageAnnotation + { + ImageSource = image1, + Interpolate = true, + Layer = AnnotationLayer.BelowAxes, + X = new PlotLength(0, PlotLengthUnit.RelativeToPlotArea), + Y = new PlotLength(0, PlotLengthUnit.RelativeToPlotArea), + Width = new PlotLength(1, PlotLengthUnit.RelativeToPlotArea), + Height = new PlotLength(1, PlotLengthUnit.RelativeToPlotArea), + HorizontalAlignment = HorizontalAlignment.Left, + VerticalAlignment = VerticalAlignment.Top + }); + + // verify that a series is rendered above the gradients + model.Series.Add(new FunctionSeries(Math.Sin, 0, 7, 0.01)); + + return model; + } + + [Example("ImageAnnotation - normal axes")] + public static PlotModel ImageAnnotation_NormalAxes() + { + var model = new PlotModel { Title = "ImageAnnotation - normal axes" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + + // create an image + var pixels = new OxyColor[2, 2]; + pixels[0, 0] = OxyColors.Blue; + pixels[1, 0] = OxyColors.Yellow; + pixels[0, 1] = OxyColors.Green; + pixels[1, 1] = OxyColors.Red; + + var image = OxyImage.Create(pixels, ImageFormat.Png); + + model.Annotations.Add( + new ImageAnnotation + { + ImageSource = image, + Interpolate = false, + X = new PlotLength(0, PlotLengthUnit.Data), + Y = new PlotLength(0, PlotLengthUnit.Data), + Width = new PlotLength(80, PlotLengthUnit.Data), + Height = new PlotLength(50, PlotLengthUnit.Data), + HorizontalAlignment = HorizontalAlignment.Left, + VerticalAlignment = VerticalAlignment.Bottom + }); + return model; + } + + [Example("ImageAnnotation - reverse horizontal axis")] + public static PlotModel ImageAnnotation_ReverseHorizontalAxis() + { + var model = new PlotModel { Title = "ImageAnnotation - reverse horizontal axis" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, StartPosition = 1, EndPosition = 0 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + + // create an image + var pixels = new OxyColor[2, 2]; + pixels[0, 0] = OxyColors.Blue; + pixels[1, 0] = OxyColors.Yellow; + pixels[0, 1] = OxyColors.Green; + pixels[1, 1] = OxyColors.Red; + + var image = OxyImage.Create(pixels, ImageFormat.Png); + + model.Annotations.Add( + new ImageAnnotation + { + ImageSource = image, + Interpolate = false, + X = new PlotLength(100, PlotLengthUnit.Data), + Y = new PlotLength(0, PlotLengthUnit.Data), + Width = new PlotLength(80, PlotLengthUnit.Data), + Height = new PlotLength(50, PlotLengthUnit.Data), + HorizontalAlignment = HorizontalAlignment.Left, + VerticalAlignment = VerticalAlignment.Bottom + }); + return model; + } + + [Example("ImageAnnotation - reverse vertical axis")] + public static PlotModel ImageAnnotation_ReverseVerticalAxis() + { + var model = new PlotModel { Title = "ImageAnnotation - reverse vertical axis" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, StartPosition = 1, EndPosition = 0 }); + + // create an image + var pixels = new OxyColor[2, 2]; + pixels[0, 0] = OxyColors.Blue; + pixels[1, 0] = OxyColors.Yellow; + pixels[0, 1] = OxyColors.Green; + pixels[1, 1] = OxyColors.Red; + + var image = OxyImage.Create(pixels, ImageFormat.Png); + + model.Annotations.Add( + new ImageAnnotation + { + ImageSource = image, + Interpolate = false, + X = new PlotLength(0, PlotLengthUnit.Data), + Y = new PlotLength(100, PlotLengthUnit.Data), + Width = new PlotLength(80, PlotLengthUnit.Data), + Height = new PlotLength(50, PlotLengthUnit.Data), + HorizontalAlignment = HorizontalAlignment.Left, + VerticalAlignment = VerticalAlignment.Bottom + }); + return model; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Annotations/LineAnnotationExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Annotations/LineAnnotationExamples.cs new file mode 100644 index 0000000..1a847c6 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Annotations/LineAnnotationExamples.cs @@ -0,0 +1,167 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using OxyPlot; + using OxyPlot.Annotations; + using OxyPlot.Axes; + + [Examples("LineAnnotation"), Tags("Annotations")] + public static class LineAnnotationExamples + { + [Example("LineAnnotation on linear axes")] + public static PlotModel LineAnnotationOnLinearAxes() + { + var model = new PlotModel { Title = "LineAnnotation on linear axes" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = -20, Maximum = 80 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = -10, Maximum = 10 }); + model.Annotations.Add(new LineAnnotation { Slope = 0.1, Intercept = 1, Text = "First" }); + model.Annotations.Add( + new LineAnnotation { Slope = 0.3, Intercept = 2, MaximumX = 40, Color = OxyColors.Red, Text = "Second" }); + model.Annotations.Add( + new LineAnnotation + { + Type = LineAnnotationType.Vertical, + X = 4, + MaximumY = 10, + Color = OxyColors.Green, + Text = "Vertical" + }); + model.Annotations.Add( + new LineAnnotation + { + Type = LineAnnotationType.Horizontal, + Y = 2, + MaximumX = 4, + Color = OxyColors.Gold, + Text = "Horizontal" + }); + return model; + } + + [Example("LineAnnotation on logarithmic axes")] + public static PlotModel LineAnnotationOnLogarithmicAxes() + { + var model = new PlotModel { Title = "LineAnnotation on logarithmic axes" }; + model.Axes.Add(new LogarithmicAxis { Position = AxisPosition.Bottom, Minimum = 1, Maximum = 80 }); + model.Axes.Add(new LogarithmicAxis { Position = AxisPosition.Left, Minimum = 1, Maximum = 10 }); + model.Annotations.Add(new LineAnnotation { Slope = 0.1, Intercept = 1, Text = "First", TextMargin = 40 }); + model.Annotations.Add( + new LineAnnotation { Slope = 0.3, Intercept = 2, MaximumX = 40, Color = OxyColors.Red, Text = "Second" }); + model.Annotations.Add( + new LineAnnotation + { + Type = LineAnnotationType.Vertical, + X = 4, + MaximumY = 10, + Color = OxyColors.Green, + Text = "Vertical" + }); + model.Annotations.Add( + new LineAnnotation + { + Type = LineAnnotationType.Horizontal, + Y = 2, + MaximumX = 4, + Color = OxyColors.Gold, + Text = "Horizontal" + }); + return model; + } + + [Example("LineAnnotation with text orientation specified")] + public static PlotModel LineAnnotationOnLinearAxesWithTextOrientation() + { + var model = new PlotModel { Title = "LineAnnotations", Subtitle = "with TextOrientation specified" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = -20, Maximum = 80 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = -10, Maximum = 10 }); + model.Annotations.Add( + new LineAnnotation + { + Slope = 0.1, + Intercept = 1, + Text = "Horizontal", + TextOrientation = AnnotationTextOrientation.Horizontal, + TextVerticalAlignment = VerticalAlignment.Bottom + }); + model.Annotations.Add( + new LineAnnotation + { + Slope = 0.3, + Intercept = 2, + MaximumX = 40, + Color = OxyColors.Red, + Text = "Vertical", + TextOrientation = AnnotationTextOrientation.Vertical + }); + model.Annotations.Add( + new LineAnnotation + { + Type = LineAnnotationType.Vertical, + X = 4, + MaximumY = 10, + Color = OxyColors.Green, + Text = "Horizontal (x=4)", + TextPadding = 8, + TextOrientation = AnnotationTextOrientation.Horizontal + }); + model.Annotations.Add( + new LineAnnotation + { + Type = LineAnnotationType.Vertical, + X = 45, + MaximumY = 10, + Color = OxyColors.Green, + Text = "Horizontal (x=45)", + TextHorizontalAlignment = HorizontalAlignment.Left, + TextPadding = 8, + TextOrientation = AnnotationTextOrientation.Horizontal + }); + model.Annotations.Add( + new LineAnnotation + { + Type = LineAnnotationType.Horizontal, + Y = 2, + MaximumX = 4, + Color = OxyColors.Gold, + Text = "Horizontal", + TextLinePosition = 0.5, + TextOrientation = AnnotationTextOrientation.Horizontal + }); + return model; + } + + [Example("LineAnnotation - ClipByAxis property")] + public static PlotModel LinearAxesMultipleAxes() + { + var model = new PlotModel { Title = "ClipByAxis property", Subtitle = "This property specifies if the annotation should be clipped by the current axes or by the full plot area." }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = -20, Maximum = 80, StartPosition = 0, EndPosition = 0.45, TextColor = OxyColors.Red }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = -10, Maximum = 10, StartPosition = 0, EndPosition = 0.45, TextColor = OxyColors.Green }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = -20, Maximum = 80, StartPosition = 0.55, EndPosition = 1, TextColor = OxyColors.Blue }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = -10, Maximum = 10, StartPosition = 0.55, EndPosition = 1, TextColor = OxyColors.Orange }); + + model.Annotations.Add(new LineAnnotation { ClipByYAxis = true, Type = LineAnnotationType.Vertical, X = 0, Color = OxyColors.Green, Text = "Vertical, ClipByAxis = true" }); + model.Annotations.Add(new LineAnnotation { ClipByYAxis = false, Type = LineAnnotationType.Vertical, X = 20, Color = OxyColors.Green, Text = "Vertical, ClipByAxis = false" }); + model.Annotations.Add(new LineAnnotation { ClipByXAxis = true, Type = LineAnnotationType.Horizontal, Y = 2, Color = OxyColors.Gold, Text = "Horizontal, ClipByAxis = true" }); + model.Annotations.Add(new LineAnnotation { ClipByXAxis = false, Type = LineAnnotationType.Horizontal, Y = 8, Color = OxyColors.Gold, Text = "Horizontal, ClipByAxis = false" }); + return model; + } + + [Example("LineAnnotation on reversed axes")] + public static PlotModel ReversedAxes() + { + var model = new PlotModel { Title = "LineAnnotation on reversed axes" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = -20, Maximum = 80, StartPosition = 1, EndPosition = 0 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = -10, Maximum = 10, StartPosition = 1, EndPosition = 0 }); + model.Annotations.Add(new LineAnnotation { Slope = 0.1, Intercept = 1, Text = "First", TextHorizontalAlignment = HorizontalAlignment.Left }); + model.Annotations.Add(new LineAnnotation { Slope = 0.3, Intercept = 2, MaximumX = 40, Color = OxyColors.Red, Text = "Second", TextHorizontalAlignment = HorizontalAlignment.Left, TextVerticalAlignment = VerticalAlignment.Bottom }); + model.Annotations.Add(new LineAnnotation { Type = LineAnnotationType.Vertical, X = 4, MaximumY = 10, Color = OxyColors.Green, Text = "Vertical", TextHorizontalAlignment = HorizontalAlignment.Right }); + model.Annotations.Add(new LineAnnotation { Type = LineAnnotationType.Horizontal, Y = 2, MaximumX = 4, Color = OxyColors.Gold, Text = "Horizontal", TextHorizontalAlignment = HorizontalAlignment.Left }); + return model; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Annotations/PointAnnotationExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Annotations/PointAnnotationExamples.cs new file mode 100644 index 0000000..bd2471a --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Annotations/PointAnnotationExamples.cs @@ -0,0 +1,161 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using OxyPlot; + using OxyPlot.Annotations; + using OxyPlot.Axes; + + [Examples("PointAnnotation"), Tags("Annotations")] + public static class PointAnnotationExamples + { + [Example("PointAnnotation")] + public static PlotModel PointAnnotation() + { + var model = new PlotModel { Title = "PointAnnotation" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + + model.Annotations.Add(new PointAnnotation { X = 50, Y = 50, Text = "P1" }); + return model; + } + + [Example("PointAnnotation - shapes")] + public static PlotModel PointAnnotationShapes() + { + var model = new PlotModel { Title = "PointAnnotation - shapes" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Maximum = 120 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + + // filled + model.Annotations.Add( + new PointAnnotation + { + X = 20, + Y = 60, + Text = "Circle", + Shape = MarkerType.Circle, + Fill = OxyColors.LightGray, + Stroke = OxyColors.DarkGray, + StrokeThickness = 1 + }); + model.Annotations.Add( + new PointAnnotation + { + X = 40, + Y = 60, + Text = "Square", + Shape = MarkerType.Square, + Fill = OxyColors.LightBlue, + Stroke = OxyColors.DarkBlue, + StrokeThickness = 1 + }); + model.Annotations.Add( + new PointAnnotation + { + X = 60, + Y = 60, + Text = "Triangle", + Shape = MarkerType.Triangle, + Fill = OxyColors.IndianRed, + Stroke = OxyColors.Black, + StrokeThickness = 1 + }); + model.Annotations.Add( + new PointAnnotation + { + X = 80, + Y = 60, + Text = "Diamond", + Shape = MarkerType.Diamond, + Fill = OxyColors.ForestGreen, + Stroke = OxyColors.Black, + StrokeThickness = 1 + }); + model.Annotations.Add( + new PointAnnotation + { + X = 100, + Y = 60, + Text = "Custom", + Shape = MarkerType.Custom, + CustomOutline = + new[] + { + new ScreenPoint(-1, -1), new ScreenPoint(1, 1), new ScreenPoint(-1, 1), + new ScreenPoint(1, -1) + }, + Stroke = OxyColors.Black, + Fill = OxyColors.CadetBlue, + StrokeThickness = 1 + }); + + // not filled + model.Annotations.Add( + new PointAnnotation + { + X = 20, + Y = 40, + Text = "Cross", + Shape = MarkerType.Cross, + Stroke = OxyColors.IndianRed, + StrokeThickness = 1 + }); + model.Annotations.Add( + new PointAnnotation + { + X = 40, + Y = 40, + Text = "Plus", + Shape = MarkerType.Plus, + Stroke = OxyColors.Navy, + StrokeThickness = 1 + }); + model.Annotations.Add( + new PointAnnotation + { + X = 60, + Y = 40, + Text = "Star", + Shape = MarkerType.Star, + Stroke = OxyColors.DarkOliveGreen, + StrokeThickness = 1 + }); + + return model; + } + + [Example("PointAnnotation - text alignments")] + public static PlotModel PointAnnotationTextAlignment() + { + var model = new PlotModel { Title = "PointAnnotation - text alignments" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = -50, Maximum = 50 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = -50, Maximum = 50 }); + + for (var ha = -1; ha <= 1; ha++) + { + var h = (HorizontalAlignment)ha; + for (var va = -1; va <= 1; va++) + { + var v = (VerticalAlignment)va; + model.Annotations.Add( + new PointAnnotation + { + X = ha * 20, + Y = va * 20, + Size = 10, + Text = h + "," + v, + TextHorizontalAlignment = h, + TextVerticalAlignment = v + }); + } + } + + return model; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Annotations/PolygonAnnotationExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Annotations/PolygonAnnotationExamples.cs new file mode 100644 index 0000000..a7dd37e --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Annotations/PolygonAnnotationExamples.cs @@ -0,0 +1,80 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + + using OxyPlot; + using OxyPlot.Annotations; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("PolygonAnnotation"), Tags("Annotations")] + public static class PolygonAnnotationExamples + { + [Example("PolygonAnnotation")] + public static PlotModel PolygonAnnotation() + { + var model = new PlotModel { Title = "PolygonAnnotation" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = -20, Maximum = 20 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = -10, Maximum = 10 }); + var a1 = new PolygonAnnotation { Text = "Polygon 1" }; + a1.Points.AddRange(new[] { new DataPoint(4, -2), new DataPoint(8, -4), new DataPoint(17, 7), new DataPoint(5, 8), new DataPoint(2, 5) }); + model.Annotations.Add(a1); + return model; + } + + [Example("PolygonAnnotation with custom text position and alignment")] + public static PlotModel PolygonAnnotationTextPosition() + { + var model = new PlotModel { Title = "PolygonAnnotation with fixed text position and alignment" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = -20, Maximum = 20 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = -10, Maximum = 10 }); + var a1 = new PolygonAnnotation { Text = "Polygon 1", TextHorizontalAlignment = HorizontalAlignment.Left, TextVerticalAlignment = VerticalAlignment.Bottom, TextPosition = new DataPoint(4.1, -1.9) }; + a1.Points.AddRange(new[] { new DataPoint(4, -2), new DataPoint(8, -2), new DataPoint(17, 7), new DataPoint(5, 8), new DataPoint(4, 5) }); + model.Annotations.Add(a1); + return model; + } + + [Example("AnnotationLayer property")] + public static PlotModel AnnotationLayerProperty() + { + var model = new PlotModel { Title = "Annotation Layers" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = -20, Maximum = 30, MajorGridlineStyle = LineStyle.Solid, MajorGridlineThickness = 1 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = -10, Maximum = 10, MajorGridlineStyle = LineStyle.Solid, MajorGridlineThickness = 1 }); + + var a1 = new PolygonAnnotation + { + Layer = AnnotationLayer.BelowAxes, + Text = "Layer = BelowAxes" + }; + a1.Points.AddRange(new[] + { + new DataPoint(-11, -2), new DataPoint(-7, -4), new DataPoint(-3, 7), new DataPoint(-10, 8), + new DataPoint(-13, 5) + }); + model.Annotations.Add(a1); + var a2 = new PolygonAnnotation + { + Layer = AnnotationLayer.BelowSeries, + Text = "Layer = BelowSeries" + }; + a2.Points.AddRange(new DataPoint[] + { + new DataPoint(4, -2), new DataPoint(8, -4), new DataPoint(12, 7), new DataPoint(5, 8), + new DataPoint(2, 5) + }); + model.Annotations.Add(a2); + var a3 = new PolygonAnnotation { Layer = AnnotationLayer.AboveSeries, Text = "Layer = AboveSeries" }; + a3.Points.AddRange(new[] { new DataPoint(19, -2), new DataPoint(23, -4), new DataPoint(27, 7), new DataPoint(20, 8), new DataPoint(17, 5) }); + model.Annotations.Add(a3); + + model.Series.Add(new FunctionSeries(Math.Sin, -20, 30, 400)); + return model; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Annotations/PolylineAnnotationExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Annotations/PolylineAnnotationExamples.cs new file mode 100644 index 0000000..5376c00 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Annotations/PolylineAnnotationExamples.cs @@ -0,0 +1,35 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using OxyPlot; + using OxyPlot.Annotations; + using OxyPlot.Axes; + + [Examples("PolylineAnnotation"), Tags("Annotations")] + public static class PolylineAnnotationExamples + { + [Example("PolylineAnnotation")] + public static PlotModel PolylineAnnotations() + { + var model = new PlotModel { Title = "PolylineAnnotation" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = 0, Maximum = 30 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = 0, Maximum = 30 }); + var a1 = new PolylineAnnotation { Text = "Polyline" }; + a1.Points.AddRange(new[] { new DataPoint(0, 10), new DataPoint(5, 5), new DataPoint(20, 1), new DataPoint(30, 20) }); + var a2 = new PolylineAnnotation + { + InterpolationAlgorithm = InterpolationAlgorithms.CanonicalSpline, + Text = "Smooth Polyline" + }; + a2.Points.AddRange(new[] { new DataPoint(0, 15), new DataPoint(3, 23), new DataPoint(9, 30), new DataPoint(20, 12), new DataPoint(30, 10) }); + model.Annotations.Add(a1); + model.Annotations.Add(a2); + return model; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Annotations/RectangleAnnotationExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Annotations/RectangleAnnotationExamples.cs new file mode 100644 index 0000000..129aa5d --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Annotations/RectangleAnnotationExamples.cs @@ -0,0 +1,87 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using OxyPlot; + using OxyPlot.Annotations; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("RectangleAnnotation"), Tags("Annotations")] + public static class RectangleAnnotationExamples + { + [Example("RectangleAnnotation")] + public static PlotModel RectangleAnnotation() + { + var model = new PlotModel { Title = "RectangleAnnotation" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + model.Annotations.Add(new RectangleAnnotation { MinimumX = 20, MaximumX = 70, MinimumY = 10, MaximumY = 40, TextRotation = 10, Text = "RectangleAnnotation", Fill = OxyColor.FromAColor(99, OxyColors.Blue), Stroke = OxyColors.Black, StrokeThickness = 2 }); + return model; + } + + [Example("RectangleAnnotations - vertical limit")] + public static PlotModel RectangleAnnotationVerticalLimit() + { + var model = new PlotModel { Title = "RectangleAnnotations - vertical limit" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + model.Annotations.Add(new RectangleAnnotation { MaximumY = 89.5, Text = "Valid area", Fill = OxyColor.FromAColor(99, OxyColors.Black) }); + return model; + } + + [Example("RectangleAnnotation - horizontal bands")] + public static PlotModel RectangleAnnotationHorizontals() + { + var model = new PlotModel { Title = "RectangleAnnotation - horizontal bands" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = 0, Maximum = 10 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = 87, Maximum = 97, MajorStep = 1, MinorStep = 1 }); + model.Annotations.Add(new RectangleAnnotation { MinimumY = 89.5, MaximumY = 90.8, Text = "Invalid", Fill = OxyColor.FromAColor(99, OxyColors.Red) }); + model.Annotations.Add(new RectangleAnnotation { MinimumY = 90.8, MaximumY = 92.1, Fill = OxyColor.FromAColor(99, OxyColors.Orange) }); + model.Annotations.Add(new RectangleAnnotation { MinimumY = 92.1, MaximumY = 94.6, Fill = OxyColor.FromAColor(99, OxyColors.Yellow) }); + model.Annotations.Add(new RectangleAnnotation { MinimumY = 94.6, MaximumY = 96, Text = "Ok", Fill = OxyColor.FromAColor(99, OxyColors.Green) }); + LineSeries series1; + model.Series.Add(series1 = new LineSeries { Color = OxyColors.Black, StrokeThickness = 6.0, LineJoin = LineJoin.Round }); + series1.Points.Add(new DataPoint(0.5, 90.7)); + series1.Points.Add(new DataPoint(1.5, 91.2)); + series1.Points.Add(new DataPoint(2.5, 91)); + series1.Points.Add(new DataPoint(3.5, 89.5)); + series1.Points.Add(new DataPoint(4.5, 92.5)); + series1.Points.Add(new DataPoint(5.5, 93.1)); + series1.Points.Add(new DataPoint(6.5, 94.5)); + series1.Points.Add(new DataPoint(7.5, 95.5)); + series1.Points.Add(new DataPoint(8.5, 95.7)); + series1.Points.Add(new DataPoint(9.5, 96.0)); + return model; + } + + [Example("RectangleAnnotation - vertical bands")] + public static PlotModel RectangleAnnotationVerticals() + { + var model = new PlotModel { Title = "RectangleAnnotation - vertical bands" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = 0, Maximum = 10 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = 87, Maximum = 97, MajorStep = 1, MinorStep = 1 }); + model.Annotations.Add(new RectangleAnnotation { MinimumX = 2.5, MaximumX = 2.8, TextRotation = 90, Text = "Red", Fill = OxyColor.FromAColor(99, OxyColors.Red) }); + model.Annotations.Add(new RectangleAnnotation { MinimumX = 2.8, MaximumX = 6.1, TextRotation = 90, Text = "Orange", Fill = OxyColor.FromAColor(99, OxyColors.Orange) }); + model.Annotations.Add(new RectangleAnnotation { MinimumX = 6.1, MaximumX = 7.6, TextRotation = 90, Text = "Yellow", Fill = OxyColor.FromAColor(99, OxyColors.Yellow) }); + model.Annotations.Add(new RectangleAnnotation { MinimumX = 7.6, MaximumX = 9.7, TextRotation = 270, Text = "Green", Fill = OxyColor.FromAColor(99, OxyColors.Green) }); + LineSeries series1; + model.Series.Add(series1 = new LineSeries { Color = OxyColors.Black, StrokeThickness = 6.0, LineJoin = LineJoin.Round }); + series1.Points.Add(new DataPoint(0.5, 90.7)); + series1.Points.Add(new DataPoint(1.5, 91.2)); + series1.Points.Add(new DataPoint(2.5, 91)); + series1.Points.Add(new DataPoint(3.5, 89.5)); + series1.Points.Add(new DataPoint(4.5, 92.5)); + series1.Points.Add(new DataPoint(5.5, 93.1)); + series1.Points.Add(new DataPoint(6.5, 94.5)); + series1.Points.Add(new DataPoint(7.5, 95.5)); + series1.Points.Add(new DataPoint(8.5, 95.7)); + series1.Points.Add(new DataPoint(9.5, 96.0)); + return model; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Annotations/TextAnnotationExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Annotations/TextAnnotationExamples.cs new file mode 100644 index 0000000..8032322 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Annotations/TextAnnotationExamples.cs @@ -0,0 +1,65 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + using System.Linq; + + using OxyPlot; + using OxyPlot.Annotations; + using OxyPlot.Axes; + + [Examples("TextAnnotation"), Tags("Annotations")] + public static class TextAnnotationExamples + { + [Example("TextAnnotation")] + public static PlotModel TextAnnotations() + { + var model = new PlotModel { Title = "TextAnnotation" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = -15, Maximum = 25 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = -5, Maximum = 18 }); + model.Annotations.Add(new TextAnnotation { TextPosition = new DataPoint(-6, 0), Text = "Text annotation 1" }); + model.Annotations.Add(new TextAnnotation { TextPosition = new DataPoint(-7, 10), TextRotation = 80, Text = "Text annotation 2" }); + model.Annotations.Add(new TextAnnotation { TextPosition = new DataPoint(2, 2), TextRotation = 20, TextHorizontalAlignment = HorizontalAlignment.Right, TextVerticalAlignment = VerticalAlignment.Top, Text = "Right/Top" }); + model.Annotations.Add(new TextAnnotation { TextPosition = new DataPoint(2, 4), TextRotation = 20, TextHorizontalAlignment = HorizontalAlignment.Right, TextVerticalAlignment = VerticalAlignment.Middle, Text = "Right/Middle" }); + model.Annotations.Add(new TextAnnotation { TextPosition = new DataPoint(2, 6), TextRotation = 20, TextHorizontalAlignment = HorizontalAlignment.Right, TextVerticalAlignment = VerticalAlignment.Bottom, Text = "Right/Bottom" }); + model.Annotations.Add(new TextAnnotation { TextPosition = new DataPoint(10, 2), TextRotation = 20, TextHorizontalAlignment = HorizontalAlignment.Center, TextVerticalAlignment = VerticalAlignment.Top, Text = "Center/Top" }); + model.Annotations.Add(new TextAnnotation { TextPosition = new DataPoint(10, 4), TextRotation = 20, TextHorizontalAlignment = HorizontalAlignment.Center, TextVerticalAlignment = VerticalAlignment.Middle, Text = "Center/Middle" }); + model.Annotations.Add(new TextAnnotation { TextPosition = new DataPoint(10, 6), TextRotation = 20, TextHorizontalAlignment = HorizontalAlignment.Center, TextVerticalAlignment = VerticalAlignment.Bottom, Text = "Center/Bottom" }); + model.Annotations.Add(new TextAnnotation { TextPosition = new DataPoint(18, 2), TextRotation = 20, TextHorizontalAlignment = HorizontalAlignment.Left, TextVerticalAlignment = VerticalAlignment.Top, Text = "Left/Top" }); + model.Annotations.Add(new TextAnnotation { TextPosition = new DataPoint(18, 4), TextRotation = 20, TextHorizontalAlignment = HorizontalAlignment.Left, TextVerticalAlignment = VerticalAlignment.Middle, Text = "Left/Middle" }); + model.Annotations.Add(new TextAnnotation { TextPosition = new DataPoint(18, 6), TextRotation = 20, TextHorizontalAlignment = HorizontalAlignment.Left, TextVerticalAlignment = VerticalAlignment.Bottom, Text = "Left/Bottom" }); + + double d = 0.05; + + Action addPoint = (x, y) => + { + var annotation = new PolygonAnnotation + { + Layer = AnnotationLayer.BelowAxes, + }; + annotation.Points.AddRange(new[] + { + new DataPoint(x - d, y - d), new DataPoint(x + d, y - d), new DataPoint(x + d, y + d), + new DataPoint(x - d, y + d), new DataPoint(x - d, y - d) + }); + model.Annotations.Add(annotation); + }; + + foreach (var a in model.Annotations.ToArray()) + { + var ta = a as TextAnnotation; + if (ta != null) + { + addPoint(ta.TextPosition.X, ta.TextPosition.Y); + } + } + + return model; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Annotations/TileMapAnnotation.cs b/OxyPlot/Source/Examples/ExampleLibrary/Annotations/TileMapAnnotation.cs new file mode 100644 index 0000000..be9ac33 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Annotations/TileMapAnnotation.cs @@ -0,0 +1,389 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Provides an annotation that shows a tile based map. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using OxyPlot; + using OxyPlot.Annotations; + using System; + using System.Collections.Generic; + using System.Globalization; + using System.IO; + using System.Net; + using System.Threading; + + /// + /// Provides an annotation that shows a tile based map. + /// + /// The longitude and latitude range of the map is defined by the range of the x and y axis, respectively. + public class TileMapAnnotation : Annotation + { + /// + /// The image cache. + /// + private readonly Dictionary images = new Dictionary(); + + /// + /// The download queue. + /// + private readonly Queue queue = new Queue(); + + /// + /// The current number of downloads + /// + private int numberOfDownloads; + + /// + /// Initializes a new instance of the class. + /// + public TileMapAnnotation() + { + this.TileSize = 256; + this.MinZoomLevel = 0; + this.MaxZoomLevel = 20; + this.Opacity = 1.0; + this.MaxNumberOfDownloads = 8; + } + + /// + /// Gets or sets the max number of simultaneous downloads. + /// + /// The max number of downloads. + public int MaxNumberOfDownloads { get; set; } + + /// + /// Gets or sets the URL. + /// + /// The URL. + public string Url { get; set; } + + /// + /// Gets or sets the copyright notice. + /// + /// The copyright notice. + public string CopyrightNotice { get; set; } + + /// + /// Gets or sets the size of the tiles. + /// + /// The size of the tiles. + public int TileSize { get; set; } + + /// + /// Gets or sets the min zoom level. + /// + /// The min zoom level. + public int MinZoomLevel { get; set; } + + /// + /// Gets or sets the max zoom level. + /// + /// The max zoom level. + public int MaxZoomLevel { get; set; } + + /// + /// Gets or sets the opacity. + /// + /// The opacity. + public double Opacity { get; set; } + + /// + /// Renders the annotation on the specified context. + /// + /// The render context. + public override void Render(IRenderContext rc) + { + base.Render(rc); + + var lon0 = this.XAxis.ActualMinimum; + var lon1 = this.XAxis.ActualMaximum; + var lat0 = this.YAxis.ActualMinimum; + var lat1 = this.YAxis.ActualMaximum; + + // the desired number of tiles horizontally + double tilesx = this.PlotModel.Width / this.TileSize; + + // calculate the desired zoom level + var n = tilesx / (((lon1 + 180) / 360) - ((lon0 + 180) / 360)); + var zoom = (int)Math.Round(Math.Log(n) / Math.Log(2)); + if (zoom < this.MinZoomLevel) + { + zoom = this.MinZoomLevel; + } + + if (zoom > this.MaxZoomLevel) + { + zoom = this.MaxZoomLevel; + } + + // find tile coordinates for the corners + double x0, y0; + LatLonToTile(lat0, lon0, zoom, out x0, out y0); + double x1, y1; + LatLonToTile(lat1, lon1, zoom, out x1, out y1); + + double xmax = Math.Max(x0, x1); + double xmin = Math.Min(x0, x1); + double ymax = Math.Max(y0, y1); + double ymin = Math.Min(y0, y1); + + var clippingRectangle = this.GetClippingRect(); + + // Add the tiles + for (var x = (int)xmin; x < xmax; x++) + { + for (var y = (int)ymin; y < ymax; y++) + { + string uri = this.GetTileUri(x, y, zoom); + var img = this.GetImage(uri, rc.RendersToScreen); + + if (img == null) + { + continue; + } + + // transform from tile coordinates to lat/lon + double latitude0, latitude1, longitude0, longitude1; + TileToLatLon(x, y, zoom, out latitude0, out longitude0); + TileToLatLon(x + 1, y + 1, zoom, out latitude1, out longitude1); + + // transform from lat/lon to screen coordinates + var s00 = this.Transform(longitude0, latitude0); + var s11 = this.Transform(longitude1, latitude1); + + var r = OxyRect.Create(s00.X, s00.Y, s11.X, s11.Y); + + // draw the image + rc.DrawClippedImage(clippingRectangle, img, r.Left, r.Top, r.Width, r.Height, this.Opacity, true); + } + } + + // draw the copyright notice + var p = new ScreenPoint(clippingRectangle.Right - 5, clippingRectangle.Bottom - 5); + var textSize = rc.MeasureText(this.CopyrightNotice, this.ActualFont, this.ActualFontSize, this.ActualFontWeight); + rc.DrawRectangle(new OxyRect(p.X - textSize.Width - 2, p.Y - textSize.Height - 2, textSize.Width + 4, textSize.Height + 4), OxyColor.FromAColor(200, OxyColors.White), OxyColors.Undefined); + + rc.DrawText( + p, + this.CopyrightNotice, + OxyColors.Black, + this.ActualFont, + this.ActualFontSize, + this.ActualFontWeight, + 0, + HorizontalAlignment.Right, + VerticalAlignment.Bottom); + } + + /// + /// Transforms a position to a tile coordinate. + /// + /// The latitude. + /// The longitude. + /// The zoom. + /// The x. + /// The y. + private static void LatLonToTile(double latitude, double longitude, int zoom, out double x, out double y) + { + // http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames + int n = 1 << zoom; + double lat = latitude / 180 * Math.PI; + x = (longitude + 180.0) / 360.0 * n; + y = (1.0 - (Math.Log(Math.Tan(lat) + (1.0 / Math.Cos(lat))) / Math.PI)) / 2.0 * n; + } + + /// + /// Transforms a tile coordinate (x,y) to a position. + /// + /// The x. + /// The y. + /// The zoom. + /// The latitude. + /// The longitude. + private static void TileToLatLon(double x, double y, int zoom, out double latitude, out double longitude) + { + int n = 1 << zoom; + longitude = (x / n * 360.0) - 180.0; + double lat = Math.Atan(Math.Sinh(Math.PI * (1 - (2 * y / n)))); + latitude = lat * 180.0 / Math.PI; + } + + /// + /// Gets the image from the specified uri. + /// + /// The URI. + /// Get the image asynchronously if set to true. The plot model will be invalidated when the image has been downloaded. + /// The image. + /// This method gets the image from cache, or starts an async download. + private OxyImage GetImage(string uri, bool asyncLoading) + { + OxyImage img; + if (this.images.TryGetValue(uri, out img)) + { + return img; + } + + if (!asyncLoading) + { + return this.Download(uri); + } + + lock (this.queue) + { + // 'reserve' an image (otherwise multiple downloads of the same uri may happen) + this.images[uri] = null; + this.queue.Enqueue(uri); + } + + this.BeginDownload(); + return null; + } + + /// + /// Downloads the image from the specified URI. + /// + /// The URI. + /// The image + private OxyImage Download(string uri) + { + OxyImage img = null; + var mre = new ManualResetEvent(false); + var request = (HttpWebRequest)WebRequest.Create(uri); + request.Method = "GET"; + request.BeginGetResponse( + r => + { + try + { + if (request.HaveResponse) + { + var response = request.EndGetResponse(r); + var stream = response.GetResponseStream(); + + var ms = new MemoryStream(); + stream.CopyTo(ms); + var buffer = ms.ToArray(); + + img = new OxyImage(buffer); + this.images[uri] = img; + } + } + catch (Exception e) + { + var ie = e; + while (ie != null) + { + System.Diagnostics.Debug.WriteLine(ie.Message); + ie = ie.InnerException; + } + } + finally + { + mre.Set(); + } + }, + request); + + mre.WaitOne(); + return img; + } + + /// + /// Starts the next download in the queue. + /// + private void BeginDownload() + { + if (this.numberOfDownloads >= this.MaxNumberOfDownloads) + { + return; + } + + string uri = this.queue.Dequeue(); + var request = (HttpWebRequest)WebRequest.Create(uri); + request.Method = "GET"; + Interlocked.Increment(ref this.numberOfDownloads); + request.BeginGetResponse( + r => + { + Interlocked.Decrement(ref this.numberOfDownloads); + try + { + if (request.HaveResponse) + { + var response = request.EndGetResponse(r); + var stream = response.GetResponseStream(); + this.DownloadCompleted(uri, stream); + } + } + catch (Exception e) + { + var ie = e; + while (ie != null) + { + System.Diagnostics.Debug.WriteLine(ie.Message); + ie = ie.InnerException; + } + } + }, + request); + } + + /// + /// The download completed, set the image. + /// + /// The URI. + /// The result. + private void DownloadCompleted(string uri, Stream result) + { + if (result == null) + { + return; + } + + var ms = new MemoryStream(); + result.CopyTo(ms); + var buffer = ms.ToArray(); + + var img = new OxyImage(buffer); + this.images[uri] = img; + + lock (this.queue) + { + // Clear old items in the queue, new ones will be added when the plot is refreshed + foreach (var queuedUri in this.queue) + { + // Remove the 'reserved' image + this.images.Remove(queuedUri); + } + + this.queue.Clear(); + } + + this.PlotModel.InvalidatePlot(false); + if (this.queue.Count > 0) + { + this.BeginDownload(); + } + } + + /// + /// Gets the tile URI. + /// + /// The tile x. + /// The tile y. + /// The zoom. + /// The uri. + private string GetTileUri(int x, int y, int zoom) + { + string url = this.Url.Replace("{X}", x.ToString(CultureInfo.InvariantCulture)); + url = url.Replace("{Y}", y.ToString(CultureInfo.InvariantCulture)); + return url.Replace("{Z}", zoom.ToString(CultureInfo.InvariantCulture)); + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Annotations/TileMapAnnotationExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Annotations/TileMapAnnotationExamples.cs new file mode 100644 index 0000000..4c26d6c --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Annotations/TileMapAnnotationExamples.cs @@ -0,0 +1,67 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using OxyPlot; + using OxyPlot.Annotations; + using OxyPlot.Axes; + + [Examples("TileMapAnnotation"), Tags("Annotations")] + public static class TileMapAnnotationExamples + { + [Example("TileMapAnnotation (openstreetmap.org)")] + public static PlotModel TileMapAnnotation2() + { + var model = new PlotModel { Title = "TileMapAnnotation" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = 10.4, Maximum = 10.6, Title = "Longitude" }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = 59.88, Maximum = 59.96, Title = "Latitude" }); + + // Add the tile map annotation + model.Annotations.Add( + new TileMapAnnotation + { + Url = "http://tile.openstreetmap.org/{Z}/{X}/{Y}.png", + CopyrightNotice = "OpenStreetMap" + }); + + return model; + } + + [Example("TileMapAnnotation (statkart.no)")] + public static PlotModel TileMapAnnotation() + { + var model = new PlotModel { Title = "TileMapAnnotation" }; + + // TODO: scale ratio between the two axes should be fixed (or depending on latitude...) + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = 10.4, Maximum = 10.6, Title = "Longitude" }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = 59.88, Maximum = 59.96, Title = "Latitude" }); + + // Add the tile map annotation + model.Annotations.Add( + new TileMapAnnotation + { + Url = "http://opencache.statkart.no/gatekeeper/gk/gk.open_gmaps?layers=toporaster3&zoom={Z}&x={X}&y={Y}", + CopyrightNotice = "Kartgrunnlag: Statens kartverk, Geovekst og kommuner.", + MinZoomLevel = 5, + MaxZoomLevel = 19 + }); + + model.Annotations.Add(new ArrowAnnotation + { + EndPoint = new DataPoint(10.563, 59.888), + ArrowDirection = new ScreenVector(-40, -60), + StrokeThickness = 3, + FontSize = 20, + FontWeight = FontWeights.Bold, + TextColor = OxyColor.FromAColor(160, OxyColors.Magenta), + Color = OxyColor.FromAColor(100, OxyColors.Magenta) + }); + + return model; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Attributes/ExampleAttribute.cs b/OxyPlot/Source/Examples/ExampleLibrary/Attributes/ExampleAttribute.cs new file mode 100644 index 0000000..0dc84f5 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Attributes/ExampleAttribute.cs @@ -0,0 +1,34 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + + /// + /// Specifies the title for an example. + /// + [AttributeUsage(AttributeTargets.Method)] + public class ExampleAttribute : Attribute + { + /// + /// Initializes a new instance of the class. + /// + /// The title. + public ExampleAttribute(string title = null) + { + this.Title = title; + } + + /// + /// Gets the title. + /// + /// + /// The title. + /// + public string Title { get; private set; } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Attributes/ExamplesAttribute.cs b/OxyPlot/Source/Examples/ExampleLibrary/Attributes/ExamplesAttribute.cs new file mode 100644 index 0000000..78841c5 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Attributes/ExamplesAttribute.cs @@ -0,0 +1,34 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + + /// + /// Specifies the category for a class containing examples. + /// + [AttributeUsage(AttributeTargets.Class)] + public class ExamplesAttribute : Attribute + { + /// + /// Initializes a new instance of the class. + /// + /// The category. + public ExamplesAttribute(string category = null) + { + this.Category = category; + } + + /// + /// Gets the category. + /// + /// + /// The category. + /// + public string Category { get; private set; } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Attributes/TagsAttribute.cs b/OxyPlot/Source/Examples/ExampleLibrary/Attributes/TagsAttribute.cs new file mode 100644 index 0000000..5d0d1a6 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Attributes/TagsAttribute.cs @@ -0,0 +1,34 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + + /// + /// Specifies tags. + /// + [AttributeUsage(AttributeTargets.Class)] + public class TagsAttribute : Attribute + { + /// + /// Initializes a new instance of the class. + /// + /// The tags. + public TagsAttribute(params string[] tags) + { + this.Tags = tags; + } + + /// + /// Gets the tags. + /// + /// + /// The tags. + /// + public string[] Tags { get; private set; } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Axes/AxisExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Axes/AxisExamples.cs new file mode 100644 index 0000000..9678b06 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Axes/AxisExamples.cs @@ -0,0 +1,1204 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Provides examples for general axis properties. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + using System.Collections.Generic; + using System.Globalization; + + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + + /// + /// Provides examples for general axis properties. + /// + [Examples("Axis examples"), Tags("Axes")] + public class AxisExamples + { + /// + /// Creates an example for the property using . + /// + /// A . + [Example("TickStyle: None")] + public static PlotModel TickStyleNone() + { + return CreateTickStyleExample(TickStyle.None); + } + + [Example("TickStyle: Inside")] + public static PlotModel TickStyleInside() + { + return CreateTickStyleExample(TickStyle.Inside); + } + + [Example("TickStyle: Crossing")] + public static PlotModel TickStyleCrossing() + { + return CreateTickStyleExample(TickStyle.Crossing); + } + + [Example("TickStyle: Outside")] + public static PlotModel TickStyleOutside() + { + return CreateTickStyleExample(TickStyle.Outside); + } + + [Example("TickStyle: Color major and minor ticks differently")] + public static PlotModel TickLineColor() + { + var plotModel1 = new PlotModel { Title = "Color major and minor ticks differently" }; + plotModel1.Axes.Add(new LinearAxis + { + Position = AxisPosition.Left, + MajorGridlineThickness = 3, + MinorGridlineThickness = 3, + TicklineColor = OxyColors.Blue, + MinorTicklineColor = OxyColors.Gray, + }); + plotModel1.Axes.Add(new LinearAxis + { + Position = AxisPosition.Bottom, + MajorGridlineThickness = 3, + MinorGridlineThickness = 3, + TicklineColor = OxyColors.Blue, + MinorTicklineColor = OxyColors.Gray, + }); + return plotModel1; + } + + [Example("GridLinestyle: None (default)")] + public static PlotModel GridlineStyleNone() + { + var plotModel1 = new PlotModel { Title = "No gridlines" }; + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + return plotModel1; + } + + [Example("GridLinestyle: Vertical")] + public static PlotModel GridLinestyleVertical() + { + var plotModel1 = new PlotModel { Title = "Vertical gridlines" }; + plotModel1.Axes.Add(new LinearAxis()); + plotModel1.Axes.Add(new LinearAxis + { + MajorGridlineStyle = LineStyle.Solid, + MinorGridlineStyle = LineStyle.Dot, + Position = AxisPosition.Bottom + }); + return plotModel1; + } + + [Example("GridLinestyle: Horizontal")] + public static PlotModel GridLinestyleHorizontal() + { + var plotModel1 = new PlotModel { Title = "Horizontal gridlines" }; + plotModel1.Axes.Add(new LinearAxis + { + MajorGridlineStyle = LineStyle.Solid, + MinorGridlineStyle = LineStyle.Dot + }); + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + return plotModel1; + } + + [Example("GridLinestyle: Horizontal and vertical")] + public static PlotModel GridLinestyleBoth() + { + var plotModel1 = new PlotModel { Title = "Horizontal and vertical gridlines" }; + plotModel1.Axes.Add(new LinearAxis + { + MajorGridlineStyle = LineStyle.Solid, + MinorGridlineStyle = LineStyle.Dot + }); + plotModel1.Axes.Add(new LinearAxis + { + MajorGridlineStyle = LineStyle.Solid, + MinorGridlineStyle = LineStyle.Dot, + Position = AxisPosition.Bottom + }); + return plotModel1; + } + + [Example("Axis position left/bottom")] + public static PlotModel AxisPositionLeftAndBottom() + { + var plotModel1 = new PlotModel(); + plotModel1.Axes.Add(new LinearAxis + { + MajorGridlineStyle = LineStyle.Solid, + MinorGridlineStyle = LineStyle.Dot, + Title = "Left" + }); + plotModel1.Axes.Add(new LinearAxis + { + MajorGridlineStyle = LineStyle.Solid, + MinorGridlineStyle = LineStyle.Dot, + Position = AxisPosition.Bottom, + Title = "Bottom" + }); + return plotModel1; + } + + [Example("Axis position top/right")] + public static PlotModel AxisPositionTopRight() + { + var plotModel1 = new PlotModel(); + plotModel1.Axes.Add(new LinearAxis + { + MajorGridlineStyle = LineStyle.Solid, + MinorGridlineStyle = LineStyle.Dot, + Position = AxisPosition.Right, + Title = "Right" + }); + plotModel1.Axes.Add(new LinearAxis + { + MajorGridlineStyle = LineStyle.Solid, + MinorGridlineStyle = LineStyle.Dot, + Position = AxisPosition.Top, + Title = "Top" + }); + return plotModel1; + } + + [Example("Axis label angle 45deg")] + public static PlotModel AxisAngle45() + { + var plotModel1 = new PlotModel { PlotMargins = new OxyThickness(60, 40, 60, 30) }; + plotModel1.Axes.Add(new LinearAxis + { + Angle = 45, + MajorGridlineStyle = LineStyle.Solid, + MinorGridlineStyle = LineStyle.Dot, + Title = "Left" + }); + plotModel1.Axes.Add(new LinearAxis + { + Angle = 45, + MajorGridlineStyle = LineStyle.Solid, + MinorGridlineStyle = LineStyle.Dot, + Position = AxisPosition.Bottom, + Title = "Bottom" + }); + return plotModel1; + } + + [Example("Zero crossing axis")] + public static PlotModel ZeroCrossing() + { + var plotModel1 = new PlotModel + { + Title = "PositionAtZeroCrossing = true", + PlotAreaBorderThickness = new OxyThickness(0), + PlotMargins = new OxyThickness(10, 10, 10, 10) + }; + plotModel1.Axes.Add(new LinearAxis + { + Maximum = 50, + Minimum = -30, + PositionAtZeroCrossing = true, + AxislineStyle = LineStyle.Solid, + TickStyle = TickStyle.Crossing + }); + plotModel1.Axes.Add(new LinearAxis + { + Maximum = 70, + Minimum = -50, + Position = AxisPosition.Bottom, + PositionAtZeroCrossing = true, + AxislineStyle = LineStyle.Solid, + TickStyle = TickStyle.Crossing + }); + return plotModel1; + } + + [Example("Horizontal zero crossing axis")] + public static PlotModel HorizontalZeroCrossing() + { + var plotModel1 = new PlotModel + { + Title = "Bottom axis: PositionAtZeroCrossing = true" + }; + plotModel1.Axes.Add(new LinearAxis + { + Maximum = 50, + Minimum = -30, + Position = AxisPosition.Left, + PositionAtZeroCrossing = false, + }); + plotModel1.Axes.Add(new LinearAxis + { + Maximum = 70, + Minimum = -50, + Position = AxisPosition.Bottom, + PositionAtZeroCrossing = true, + AxislineStyle = LineStyle.Solid, + }); + return plotModel1; + } + + [Example("Vertical zero crossing axis")] + public static PlotModel VerticalZeroCrossing() + { + var plotModel1 = new PlotModel + { + Title = "Left axis: PositionAtZeroCrossing = true" + }; + plotModel1.Axes.Add(new LinearAxis + { + Maximum = 50, + Minimum = -30, + Position = AxisPosition.Left, + PositionAtZeroCrossing = true, + AxislineStyle = LineStyle.Solid, + }); + plotModel1.Axes.Add(new LinearAxis + { + Maximum = 70, + Minimum = -50, + Position = AxisPosition.Bottom, + PositionAtZeroCrossing = false, + }); + return plotModel1; + } + + [Example("Reversed")] + public static PlotModel Reversed() + { + var plotModel1 = new PlotModel { Title = "EndPosition = 0, StartPosition = 1" }; + plotModel1.Axes.Add(new LinearAxis + { + EndPosition = 0, + StartPosition = 1, + Maximum = 50, + Minimum = -30, + Position = AxisPosition.Left + }); + plotModel1.Axes.Add(new LinearAxis + { + EndPosition = 0, + StartPosition = 1, + Maximum = 70, + Minimum = -50, + Position = AxisPosition.Bottom + }); + return plotModel1; + } + + [Example("Sharing Y axis")] + public static PlotModel SharingY() + { + var plotModel1 = new PlotModel(); + plotModel1.Axes.Add(new LinearAxis + { + EndPosition = 0, + StartPosition = 1, + Maximum = 1.5, + Minimum = -1.5, + Position = AxisPosition.Left + }); + + var x1 = new LinearAxis + { + StartPosition = 0, + EndPosition = 0.45, + Maximum = 7, + Minimum = 0, + Position = AxisPosition.Bottom, + Key = "x1" + }; + plotModel1.Axes.Add(x1); + + var x2 = new LinearAxis + { + StartPosition = 0.55, + EndPosition = 1, + Maximum = 10, + Minimum = 0, + Position = AxisPosition.Bottom, + Key = "x2" + }; + plotModel1.Axes.Add(x2); + + plotModel1.Series.Add(new FunctionSeries(Math.Sin, 0, 10, 1000) { XAxisKey = x1.Key }); + plotModel1.Series.Add(new FunctionSeries(Math.Sin, 0, 10, 1000) { XAxisKey = x2.Key }); + + return plotModel1; + } + + [Example("Four axes")] + public static PlotModel FourAxes() + { + var plotModel1 = new PlotModel(); + plotModel1.Axes.Add(new LinearAxis { Maximum = 36, Minimum = 0, Title = "km/h" }); + plotModel1.Axes.Add(new LinearAxis { Maximum = 10, Minimum = 0, Position = AxisPosition.Right, Title = "m/s" }); + plotModel1.Axes.Add(new LinearAxis + { + Maximum = 10, + Minimum = 0, + Position = AxisPosition.Bottom, + Title = "meter" + }); + plotModel1.Axes.Add(new LinearAxis + { + Maximum = 10000, + Minimum = 0, + Position = AxisPosition.Top, + Title = "millimeter" + }); + return plotModel1; + } + + [Example("Five axes")] + public static PlotModel FiveAxes() + { + var plotModel1 = new PlotModel(); + plotModel1.Axes.Add(new LinearAxis { EndPosition = 0.25, Maximum = 1, Minimum = -1, Title = "C1" }); + plotModel1.Axes.Add(new LinearAxis + { + EndPosition = 0.5, + Maximum = 1, + Minimum = -1, + Position = AxisPosition.Right, + StartPosition = 0.25, + Title = "C2" + }); + plotModel1.Axes.Add(new LinearAxis + { + EndPosition = 0.75, + Maximum = 1, + Minimum = -1, + StartPosition = 0.5, + Title = "C3" + }); + plotModel1.Axes.Add(new LinearAxis + { + Maximum = 1, + Minimum = -1, + Position = AxisPosition.Right, + StartPosition = 0.75, + Title = "C4" + }); + plotModel1.Axes.Add(new LinearAxis { Maximum = 100, Minimum = 0, Position = AxisPosition.Bottom, Title = "s" }); + return plotModel1; + } + + [Example("Logarithmic axes")] + public static PlotModel LogarithmicAxes() + { + var plotModel1 = new PlotModel(); + plotModel1.Axes.Add(new LogarithmicAxis + { + Maximum = 1000000, + Minimum = 1, + Title = "Log axis", + UseSuperExponentialFormat = true + }); + plotModel1.Axes.Add(new LogarithmicAxis + { + Maximum = 10000, + Minimum = 0.001, + Position = AxisPosition.Bottom, + Title = "Log axis", + UseSuperExponentialFormat = true + }); + return plotModel1; + } + + [Example("Big numbers")] + public static PlotModel BigNumbers() + { + var plotModel1 = new PlotModel(); + plotModel1.Axes.Add(new LinearAxis { Maximum = 6E+32, Minimum = -1E+47, Title = "big numbers" }); + plotModel1.Axes.Add(new LinearAxis + { + Maximum = 3E+50, + Minimum = -1E+40, + Position = AxisPosition.Bottom, + Title = "big numbers" + }); + return plotModel1; + } + + [Example("Big numbers with super exponential format")] + public static PlotModel BigNumbersSuperExponentialFormat() + { + var plotModel1 = new PlotModel(); + plotModel1.Axes.Add(new LinearAxis + { + Maximum = 6E+32, + Minimum = -1E+47, + Title = "big numbers", + UseSuperExponentialFormat = true + }); + + plotModel1.Axes.Add(new LinearAxis + { + Maximum = 3E+50, + Minimum = -1E+40, + Position = AxisPosition.Bottom, + Title = "big numbers", + UseSuperExponentialFormat = true + }); + return plotModel1; + } + + [Example("Small numbers")] + public static PlotModel SmallNumbers() + { + var plotModel1 = new PlotModel(); + plotModel1.Axes.Add(new LinearAxis { Maximum = 6E-20, Minimum = -5E-20, Title = "small numbers" }); + plotModel1.Axes.Add(new LinearAxis + { + Maximum = 3E-20, + Minimum = -4E-20, + Position = AxisPosition.Bottom, + Title = "small numbers" + }); + return plotModel1; + } + + [Example("Default padding")] + public static PlotModel Defaultpadding() + { + var plotModel1 = new PlotModel { Title = "Default padding" }; + plotModel1.Axes.Add(new LinearAxis()); + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + var lineSeries1 = new LineSeries + { + Color = OxyColor.FromArgb(255, 78, 154, 6), + MarkerFill = OxyColor.FromArgb(255, 78, 154, 6) + }; + lineSeries1.Points.Add(new DataPoint(10, 4)); + lineSeries1.Points.Add(new DataPoint(12, 7)); + lineSeries1.Points.Add(new DataPoint(16, 3)); + lineSeries1.Points.Add(new DataPoint(20, 9)); + plotModel1.Series.Add(lineSeries1); + return plotModel1; + } + + [Example("No padding")] + public static PlotModel Nopadding() + { + var plotModel1 = new PlotModel { Title = "No padding" }; + plotModel1.Axes.Add(new LinearAxis { MaximumPadding = 0, MinimumPadding = 0 }); + plotModel1.Axes.Add(new LinearAxis { MaximumPadding = 0, MinimumPadding = 0, Position = AxisPosition.Bottom }); + var lineSeries1 = new LineSeries + { + Color = OxyColor.FromArgb(255, 78, 154, 6), + MarkerFill = OxyColor.FromArgb(255, 78, 154, 6) + }; + lineSeries1.Points.Add(new DataPoint(10, 4)); + lineSeries1.Points.Add(new DataPoint(12, 7)); + lineSeries1.Points.Add(new DataPoint(16, 3)); + lineSeries1.Points.Add(new DataPoint(20, 9)); + plotModel1.Series.Add(lineSeries1); + return plotModel1; + } + + [Example("Padding 10%")] + public static PlotModel Padding() + { + var plotModel1 = new PlotModel { Title = "Padding 10%" }; + plotModel1.Axes.Add(new LinearAxis { MaximumPadding = 0.1, MinimumPadding = 0.1 }); + plotModel1.Axes.Add(new LinearAxis + { + MaximumPadding = 0.1, + MinimumPadding = 0.1, + Position = AxisPosition.Bottom + }); + var lineSeries1 = new LineSeries + { + Color = OxyColor.FromArgb(255, 78, 154, 6), + MarkerFill = OxyColor.FromArgb(255, 78, 154, 6) + }; + lineSeries1.Points.Add(new DataPoint(10, 4)); + lineSeries1.Points.Add(new DataPoint(12, 7)); + lineSeries1.Points.Add(new DataPoint(16, 3)); + lineSeries1.Points.Add(new DataPoint(20, 9)); + plotModel1.Series.Add(lineSeries1); + return plotModel1; + } + + [Example("X-axis MinimumPadding=0.1")] + public static PlotModel XaxisMinimumPadding() + { + var plotModel1 = new PlotModel { Title = "X-axis MinimumPadding=0.1" }; + plotModel1.Axes.Add(new LinearAxis()); + plotModel1.Axes.Add(new LinearAxis { MinimumPadding = 0.1, Position = AxisPosition.Bottom }); + var lineSeries1 = new LineSeries + { + Color = OxyColor.FromArgb(255, 78, 154, 6), + MarkerFill = OxyColor.FromArgb(255, 78, 154, 6) + }; + lineSeries1.Points.Add(new DataPoint(10, 4)); + lineSeries1.Points.Add(new DataPoint(12, 7)); + lineSeries1.Points.Add(new DataPoint(16, 3)); + lineSeries1.Points.Add(new DataPoint(20, 9)); + plotModel1.Series.Add(lineSeries1); + return plotModel1; + } + + [Example("X-axis MaximumPadding=0.1")] + public static PlotModel XaxisMaximumPadding() + { + var plotModel1 = new PlotModel { Title = "X-axis MaximumPadding=0.1" }; + plotModel1.Axes.Add(new LinearAxis()); + plotModel1.Axes.Add(new LinearAxis { MaximumPadding = 0.1, Position = AxisPosition.Bottom }); + var lineSeries1 = new LineSeries + { + Color = OxyColor.FromArgb(255, 78, 154, 6), + MarkerFill = OxyColor.FromArgb(255, 78, 154, 6) + }; + lineSeries1.Points.Add(new DataPoint(10, 4)); + lineSeries1.Points.Add(new DataPoint(12, 7)); + lineSeries1.Points.Add(new DataPoint(16, 3)); + lineSeries1.Points.Add(new DataPoint(20, 9)); + plotModel1.Series.Add(lineSeries1); + return plotModel1; + } + + [Example("AbsoluteMinimum and AbsoluteMaximum")] + public static PlotModel AbsoluteMinimumAndMaximum() + { + var model = new PlotModel { Title = "AbsoluteMinimum=-17, AbsoluteMaximum=63", Subtitle = "Zooming and panning is limited to these values." }; + model.Axes.Add( + new LinearAxis + { + Position = AxisPosition.Bottom, + Minimum = 0, + Maximum = 50, + AbsoluteMinimum = -17, + AbsoluteMaximum = 63 + }); + model.Axes.Add( + new LinearAxis + { + Position = AxisPosition.Left, + Minimum = 0, + Maximum = 50, + AbsoluteMinimum = -17, + AbsoluteMaximum = 63 + }); + return model; + } + + [Example("MinimumRange")] + public static PlotModel MinimumRange() + { + var model = new PlotModel { Title = "MinimumRange = 400" }; + model.Axes.Add( + new LinearAxis + { + Position = AxisPosition.Left, + MinimumRange = 400 + }); + + return model; + } + + [Example("MaximumRange")] + public static PlotModel MaximumRange() + { + var model = new PlotModel { Title = "MaximumRange = 40" }; + model.Axes.Add( + new LinearAxis + { + Position = AxisPosition.Left, + MaximumRange = 40 + }); + + return model; + } + + [Example("Title with unit")] + public static PlotModel TitleWithUnit() + { + var model = new PlotModel { Title = "Axis titles with units" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Title = "Speed", Unit = "km/h" }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = "Temperature", Unit = "°C" }); + return model; + } + + [Example("Invisible vertical axis")] + public static PlotModel InvisibleVerticalAxis() + { + var model = new PlotModel { Title = "Invisible vertical axis" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, IsAxisVisible = false }); + model.Series.Add(new FunctionSeries(x => Math.Sin(x) / x, -5, 5, 0.1)); + return model; + } + + [Example("Invisible horizontal axis")] + public static PlotModel InvisibleHorizontalAxis() + { + var model = new PlotModel { Title = "Invisible horizontal axis" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, IsAxisVisible = false }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + model.Series.Add(new FunctionSeries(x => Math.Sin(x) * x * x, -5, 5, 0.1)); + return model; + } + + [Example("Zooming disabled")] + public static PlotModel ZoomingDisabled() + { + var model = new PlotModel { Title = "Zooming disabled" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, IsZoomEnabled = false }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, IsZoomEnabled = false }); + return model; + } + + [Example("Panning disabled")] + public static PlotModel PanningDisabled() + { + var model = new PlotModel { Title = "Panning disabled" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, IsPanEnabled = false }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, IsPanEnabled = false }); + return model; + } + + [Example("Dense intervals")] + public static PlotModel DenseIntervals() + { + var model = new PlotModel { Title = "Dense intervals" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, IntervalLength = 30 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, IntervalLength = 20 }); + return model; + } + + [Example("Graph Paper")] + public static PlotModel GraphPaper() + { + var model = new PlotModel { Title = "Graph Paper" }; + var c = OxyColors.DarkBlue; + model.PlotType = PlotType.Cartesian; + model.Axes.Add( + new LinearAxis + { + Position = AxisPosition.Bottom, + Title = "X", + MajorGridlineStyle = LineStyle.Solid, + MinorGridlineStyle = LineStyle.Solid, + MajorGridlineColor = OxyColor.FromAColor(40, c), + MinorGridlineColor = OxyColor.FromAColor(20, c) + }); + model.Axes.Add( + new LinearAxis + { + Position = AxisPosition.Left, + Title = "Y", + MajorGridlineStyle = LineStyle.Solid, + MinorGridlineStyle = LineStyle.Solid, + MajorGridlineColor = OxyColor.FromAColor(40, c), + MinorGridlineColor = OxyColor.FromAColor(20, c) + }); + return model; + } + + [Example("Log-Log Paper")] + public static PlotModel LogLogPaper() + { + var model = new PlotModel { Title = "Log-Log Paper" }; + var c = OxyColors.DarkBlue; + model.Axes.Add( + new LogarithmicAxis + { + Position = AxisPosition.Bottom, + Title = "X", + Minimum = 0.1, + Maximum = 1000, + MajorGridlineStyle = LineStyle.Solid, + MinorGridlineStyle = LineStyle.Solid, + MajorGridlineColor = OxyColor.FromAColor(40, c), + MinorGridlineColor = OxyColor.FromAColor(20, c) + }); + model.Axes.Add( + new LogarithmicAxis + { + Position = AxisPosition.Left, + Title = "Y", + Minimum = 0.1, + Maximum = 1000, + MajorGridlineStyle = LineStyle.Solid, + MinorGridlineStyle = LineStyle.Solid, + MajorGridlineColor = OxyColor.FromAColor(40, c), + MinorGridlineColor = OxyColor.FromAColor(20, c) + }); + return model; + } + + [Example("Black background")] + public static PlotModel OnBlack() + { + var model = new PlotModel + { + Title = "Black background", + Background = OxyColors.Black, + TextColor = OxyColors.White, + PlotAreaBorderColor = OxyColors.White + }; + var c = OxyColors.White; + model.PlotType = PlotType.Cartesian; + model.Series.Add(new FunctionSeries(Math.Sin, 0, Math.PI * 2, 1000, "f(x)=sin(x)")); + model.Axes.Add( + new LinearAxis + { + Position = AxisPosition.Bottom, + Title = "x", + MajorStep = Math.PI / 2, + FormatAsFractions = true, + FractionUnit = Math.PI, + FractionUnitSymbol = "π", + MajorGridlineStyle = LineStyle.Solid, + MinorGridlineStyle = LineStyle.Solid, + MajorGridlineColor = OxyColor.FromAColor(40, c), + MinorGridlineColor = OxyColor.FromAColor(20, c), + TicklineColor = OxyColors.White + }); + model.Axes.Add( + new LinearAxis + { + Position = AxisPosition.Left, + Title = "f(x)", + MajorGridlineStyle = LineStyle.Solid, + MinorGridlineStyle = LineStyle.Solid, + MajorGridlineColor = OxyColor.FromAColor(40, c), + MinorGridlineColor = OxyColor.FromAColor(20, c), + TicklineColor = OxyColors.White + }); + return model; + } + + [Example("Background and PlotAreaBackground")] + public static PlotModel Backgrounds() + { + var model = new PlotModel + { + Title = "Background and PlotAreaBackground", + Background = OxyColors.Silver, + PlotAreaBackground = OxyColors.Gray, + PlotAreaBorderColor = OxyColors.Black, + PlotAreaBorderThickness = new OxyThickness(3) + }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + return model; + } + + [Example("Auto adjusting plot margins")] + public static PlotModel AutoAdjustingMargins() + { + var model = new PlotModel + { + Title = "Auto adjusting plot margins", + LegendPosition = LegendPosition.RightBottom + }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Title = "X", TickStyle = TickStyle.Outside }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = "Y", TickStyle = TickStyle.Outside }); + model.Series.Add(new LineSeries { Title = "Butterfly curve", ItemsSource = ButterflyCurve(0, Math.PI * 4, 1000) }); + return model; + } + + [Example("Auto adjusting left plot margin")] + public static PlotModel AutoAdjustingLeftMargin() + { + var model = new PlotModel + { + Title = "Auto adjusting left plot margin", + LegendPosition = LegendPosition.RightBottom, + PlotMargins = new OxyThickness(double.NaN, 40, 40, 40) + }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Title = "X", TickStyle = TickStyle.Outside }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = "Y", TickStyle = TickStyle.Outside }); + model.Series.Add(new LineSeries { Title = "Butterfly curve", ItemsSource = ButterflyCurve(0, Math.PI * 4, 1000) }); + return model; + } + + + [Example("Manual plot margins")] + public static PlotModel ManualAdjustingMargins() + { + var model = new PlotModel + { + Title = "Manual plot margins", + LegendPosition = LegendPosition.RightBottom, + PlotMargins = new OxyThickness(60, 4, 4, 40) + }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Title = "X", TickStyle = TickStyle.Outside }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = "Y", TickStyle = TickStyle.Outside }); + model.Series.Add(new LineSeries { Title = "Butterfly curve", ItemsSource = ButterflyCurve(0, Math.PI * 4, 1000) }); + return model; + } + + [Example("Current culture")] + public static PlotModel CurrentCulture() + { + var model = new PlotModel { Title = "Current culture" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = -1, Maximum = 1 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = -1, Maximum = 1 }); + model.Series.Add(new FunctionSeries(Math.Sin, -1, 1, 100)); + return model; + } + + [Example("Invariant culture")] + public static PlotModel InvariantCulture() + { + var model = new PlotModel { Title = "Invariant culture", Culture = CultureInfo.InvariantCulture }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = -1, Maximum = 1 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = -1, MaximumPadding = 1 }); + model.Series.Add(new FunctionSeries(Math.Sin, -1, 1, 100)); + return model; + } + + [Example("Custom culture")] + public static PlotModel CustomCulture() + { + var model = new PlotModel + { + Title = "Custom culture", + Culture = new CultureInfo("en-GB") { NumberFormat = { NumberDecimalSeparator = "·" } } + }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = -1, Maximum = 1 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = -1, Maximum = 1 }); + model.Series.Add(new FunctionSeries(Math.Sin, -1, 1, 100)); + return model; + } + + private static IEnumerable ButterflyCurve(double t0, double t1, int n) + { + // http://en.wikipedia.org/wiki/Butterfly_curve_(transcendental) + double dt = (t1 - t0) / (n - 1); + for (int i = 0; i < n; i++) + { + double t = t0 + dt * i; + double r = (Math.Exp(Math.Cos(t)) - 2 * Math.Cos(4 * t) - Math.Pow(Math.Sin(t / 12), 5)); + double x = Math.Sin(t) * r; + double y = Math.Cos(t) * r; + yield return new DataPoint(x, y); + } + } + + [Example("Long axis titles (clipped at 90%)")] + public static PlotModel LongAxisTitlesClipped90() + { + var longTitle = "Long title 12345678901234567890123456789012345678901234567890123456789012345678901234567890"; + var tooltip = "The tool tip is " + longTitle; + var plotModel1 = new PlotModel { Title = "Long axis titles (clipped at 90%)" }; + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = longTitle, ToolTip = tooltip }); + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Title = longTitle, ToolTip = tooltip }); + return plotModel1; + } + + [Example("Long axis titles (clipped at 100%)")] + public static PlotModel LongAxisTitlesClipped100() + { + var longTitle = "Long title 12345678901234567890123456789012345678901234567890123456789012345678901234567890"; + var tooltip = "The tool tip is " + longTitle; + var plotModel1 = new PlotModel { Title = "Long axis titles (clipped at 100%)" }; + plotModel1.Axes.Add( + new LinearAxis { Position = AxisPosition.Left, Title = longTitle, ToolTip = tooltip, TitleClippingLength = 1.0 }); + plotModel1.Axes.Add( + new LinearAxis { Position = AxisPosition.Bottom, Title = longTitle, ToolTip = tooltip, TitleClippingLength = 1.0 }); + return plotModel1; + } + + [Example("Long axis titles (not clipped)")] + public static PlotModel LongAxisTitlesNotClipped() + { + var longTitle = "Long title 12345678901234567890123456789012345678901234567890123456789012345678901234567890"; + var tooltip = "The tool tip is " + longTitle; + var plotModel1 = new PlotModel { Title = "Long axis titles (not clipped)" }; + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = longTitle, ToolTip = tooltip, ClipTitle = false }); + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Title = longTitle, ToolTip = tooltip, ClipTitle = false }); + return plotModel1; + } + + [Example("PositionTier")] + public static PlotModel PositionTier() + { + var plotModel1 = new PlotModel(); + var linearAxis1 = new LinearAxis { Maximum = 1, Minimum = -1, Title = "PositionTier=0" }; + plotModel1.Axes.Add(linearAxis1); + var linearAxis2 = new LinearAxis + { + AxislineStyle = LineStyle.Solid, + Maximum = 2, + Minimum = -2, + PositionTier = 1, + Title = "PositionTier=1" + }; + plotModel1.Axes.Add(linearAxis2); + var linearAxis3 = new LinearAxis + { + Maximum = 1, + Minimum = -1, + Position = AxisPosition.Right, + Title = "PositionTier=0" + }; + plotModel1.Axes.Add(linearAxis3); + var linearAxis4 = new LinearAxis + { + AxislineStyle = LineStyle.Solid, + Maximum = 2, + Minimum = -2, + Position = AxisPosition.Right, + PositionTier = 1, + Title = "PositionTier=1" + }; + plotModel1.Axes.Add(linearAxis4); + var linearAxis5 = new LinearAxis + { + Maximum = 1, + Minimum = -1, + Position = AxisPosition.Top, + Title = "PositionTier=0" + }; + plotModel1.Axes.Add(linearAxis5); + var linearAxis6 = new LinearAxis + { + AxislineStyle = LineStyle.Solid, + Maximum = 2, + Minimum = -2, + Position = AxisPosition.Top, + PositionTier = 1, + Title = "PositionTier=1" + }; + plotModel1.Axes.Add(linearAxis6); + var linearAxis7 = new LinearAxis + { + AxislineStyle = LineStyle.Solid, + Maximum = 10, + Minimum = -10, + Position = AxisPosition.Top, + PositionTier = 2, + Title = "PositionTier=2" + }; + plotModel1.Axes.Add(linearAxis7); + var linearAxis8 = new LinearAxis + { + Maximum = 1, + Minimum = -1, + Position = AxisPosition.Bottom, + Title = "PositionTier=0" + }; + plotModel1.Axes.Add(linearAxis8); + var linearAxis9 = new LinearAxis + { + AxislineStyle = LineStyle.Solid, + Maximum = 2, + Minimum = -2, + Position = AxisPosition.Bottom, + PositionTier = 1, + Title = "PositionTier=1" + }; + plotModel1.Axes.Add(linearAxis9); + var linearAxis10 = new LinearAxis + { + AxislineStyle = LineStyle.Solid, + Maximum = 10, + Minimum = -10, + Position = AxisPosition.Bottom, + PositionTier = 2, + Title = "PositionTier=2" + }; + plotModel1.Axes.Add(linearAxis10); + return plotModel1; + } + + [Example("Custom axis title color")] + public static PlotModel TitleColor() + { + var model = new PlotModel { Title = "Custom axis title color" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = -1, Maximum = 1, Title = "Bottom axis", TitleColor = OxyColors.Red }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = -1, Maximum = 1, Title = "Left axis", TitleColor = OxyColors.Blue }); + model.Series.Add(new FunctionSeries(Math.Sin, -1, 1, 100)); + return model; + } + + [Example("Custom axis label color")] + public static PlotModel LabelColor() + { + var model = new PlotModel { Title = "Custom axis label color" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = -1, Maximum = 1, Title = "Bottom axis", TextColor = OxyColors.Red }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = -1, Maximum = 1, Title = "Left axis", TextColor = OxyColors.Blue }); + model.Series.Add(new FunctionSeries(Math.Sin, -1, 1, 100)); + return model; + } + + [Example("Angled axis numbers")] + public static PlotModel AngledAxisNumbers() + { + var model = new PlotModel { Title = "Angled axis numbers" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = -1, Maximum = 1, Title = "Bottom axis", Angle = 45 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = -1, Maximum = 1, Title = "Left axis", Angle = 45 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Top, Minimum = -1, Maximum = 1, Title = "Top axis", Angle = 45 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Right, Minimum = -1, Maximum = 1, Title = "Right axis", Angle = 45 }); + return model; + } + + [Example("Axis distance")] + public static PlotModel AxisDistance() + { + var plotModel = new PlotModel { Title = "AxisDistance = 20" }; + plotModel.Axes.Add(new LinearAxis { AxislineStyle = LineStyle.Solid, AxisDistance = 20, Position = AxisPosition.Bottom }); + plotModel.Axes.Add(new LinearAxis { AxislineStyle = LineStyle.Solid, AxisDistance = 20, Position = AxisPosition.Left }); + plotModel.Axes.Add(new LinearAxis { AxislineStyle = LineStyle.Solid, AxisDistance = 20, Position = AxisPosition.Right }); + plotModel.Axes.Add(new LinearAxis { AxislineStyle = LineStyle.Solid, AxisDistance = 20, Position = AxisPosition.Top }); + return plotModel; + } + + [Example("No axes defined")] + public static PlotModel NoAxesDefined() + { + var plotModel = new PlotModel { Title = "No axes defined", Subtitle = "Bottom and left axes are auto-generated." }; + plotModel.Series.Add(new FunctionSeries(Math.Cos, 0, 10, 400)); + return plotModel; + } + + /// + /// Shows usage of the property. + /// + /// The for the example. + [Example("LabelFormatter")] + public static PlotModel LabelFormatter() + { + var plotModel = new PlotModel { Title = "LabelFormatter" }; + plotModel.Axes.Add(new LinearAxis + { + Position = AxisPosition.Bottom, + Minimum = -10, + Maximum = 10, + LabelFormatter = x => Math.Abs(x) < double.Epsilon ? "ZERO" : x.ToString() + }); + plotModel.Axes.Add(new LinearAxis + { + Position = AxisPosition.Left, + Minimum = 0, + Maximum = 25, + MajorStep = 1, + MinorStep = 1, + MaximumPadding = 0, + MinimumPadding = 0, + LabelFormatter = y => ((char)(y + 'A')).ToString() + }); + return plotModel; + } + + [Example("Tool tips")] + public static PlotModel ToolTips() + { + var plotModel1 = new PlotModel { Title = "Tool tips" }; + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = "Left axis", ToolTip = "Tool tip for the left axis" }); + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Title = "Bottom axis", ToolTip = "Tool tip for the bottom axis" }); + return plotModel1; + } + + [Example("Sub- and superscript in axis titles")] + public static PlotModel SubSuperscriptInAxisTitles() + { + var plotModel1 = new PlotModel { Title = "Sub- and superscript in axis titles" }; + plotModel1.Axes.Add(new LinearAxis { Title = "Title with^{super}_{sub}script" }); + plotModel1.Axes.Add(new LinearAxis { Title = "Title with^{super}_{sub}script", Position = AxisPosition.Bottom }); + return plotModel1; + } + + [Example("MinimumMajorStep")] + public static PlotModel MinimumMajorStep() + { + var model = new PlotModel + { + Title = "Axes with MinimumMajorStep" + }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Title = "MinimuMajorStep = 1", Minimum = 0, Maximum = 2, MinimumMajorStep = 1 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = "MinimuMajorStep = 10", Minimum = 0, Maximum = 15, MinimumMajorStep = 10 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Top, Title = "MinimuMajorStep = 0 (default)", Minimum = 0, Maximum = 2 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Right, Title = "MinimuMajorStep = 0 (default)", Minimum = 0, Maximum = 15 }); + return model; + } + + [Example("MinimumMinorStep")] + public static PlotModel MinimumMinorStep() + { + var model = new PlotModel + { + Title = "Axes with MinimumMinorStep" + }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Title = "MinimumMinorStep = 1", Minimum = 0, Maximum = 20, MinimumMinorStep = 1 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = "MinimumMinorStep = 10", Minimum = 0, Maximum = 150, MinimumMinorStep = 10 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Top, Title = "MinimumMinorStep = 0 (default)", Minimum = 0, Maximum = 20 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Right, Title = "MinimumMinorStep = 0 (default)", Minimum = 0, Maximum = 150 }); + return model; + } + + /// + /// Creates an example with the specified . + /// + /// The tick style. + /// A . + private static PlotModel CreateTickStyleExample(TickStyle tickStyle) + { + var plotModel1 = new PlotModel { Title = "TickStyle = " + tickStyle }; + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Left, TickStyle = tickStyle }); + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, TickStyle = tickStyle }); + return plotModel1; + } + + [Example("Gridlines Cropping: Horizontal and vertical")] + public static PlotModel GridlineCroppingBoth() + { + var plotModel1 = new PlotModel { Title = "Gridline cropping" }; + plotModel1.Axes.Add(new LinearAxis + { + MajorGridlineStyle = LineStyle.Solid, + MinorGridlineStyle = LineStyle.Dot, + ExtraGridlines = new double[] { 46d }, + ExtraGridlineColor = OxyColors.Red, + StartPosition = 0.1, + EndPosition = 0.4, + CropGridlines = true + }); + plotModel1.Axes.Add(new LinearAxis + { + MajorGridlineStyle = LineStyle.Solid, + MinorGridlineStyle = LineStyle.Dot, + ExtraGridlines = new double[] { 46d }, + ExtraGridlineColor = OxyColors.Red, + StartPosition = 0.6, + EndPosition = 0.9, + CropGridlines = true + }); + plotModel1.Axes.Add(new LinearAxis + { + MajorGridlineStyle = LineStyle.Solid, + MinorGridlineStyle = LineStyle.Dot, + Position = AxisPosition.Bottom, + ExtraGridlines = new double[] { 46d }, + ExtraGridlineColor = OxyColors.Red, + StartPosition = 0.1, + EndPosition = 0.4, + CropGridlines = true + }); + plotModel1.Axes.Add(new LinearAxis + { + MajorGridlineStyle = LineStyle.Solid, + MinorGridlineStyle = LineStyle.Dot, + Position = AxisPosition.Bottom, + ExtraGridlines = new double[] { 46d }, + ExtraGridlineColor = OxyColors.Red, + StartPosition = 0.6, + EndPosition = 0.9, + CropGridlines = true + }); + return plotModel1; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Axes/CartesianAxesExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Axes/CartesianAxesExamples.cs new file mode 100644 index 0000000..92d592d --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Axes/CartesianAxesExamples.cs @@ -0,0 +1,151 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("Cartesian axes"), Tags("Axes")] + public class CartesianAxesExamples + { + [Example("Trigonometric functions")] + public static PlotModel FunctionSeries() + { + var pm = new PlotModel { Title = "Trigonometric functions", PlotType = PlotType.Cartesian }; + pm.Series.Add(new FunctionSeries(Math.Sin, -10, 10, 0.1, "sin(x)")); + pm.Series.Add(new FunctionSeries(Math.Cos, -10, 10, 0.1, "cos(x)")); + pm.Series.Add(new FunctionSeries(t => 5 * Math.Cos(t), t => 5 * Math.Sin(t), 0, 2 * Math.PI, 1000, "cos(t),sin(t)")); + return pm; + } + + [Example("Clover")] + public static PlotModel Clover() + { + var plot = new PlotModel { Title = "Parametric function", PlotType = PlotType.Cartesian }; + plot.Series.Add(new FunctionSeries(t => 2 * Math.Cos(2 * t) * Math.Cos(t), t => 2 * Math.Cos(2 * t) * Math.Sin(t), + 0, Math.PI * 2, 1000, "2cos(2t)cos(t) , 2cos(2t)sin(t)")); + return plot; + } + + [Example("AbsoluteMinimum Y")] + public static PlotModel AbsoluteYmin() + { + var plot = new PlotModel { Title = "Y: AbsoluteMinimum = 0", PlotType = PlotType.Cartesian }; + var c = OxyColors.DarkBlue; + plot.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Title = "X-axis", MajorGridlineStyle = LineStyle.Solid, MinorGridlineStyle = LineStyle.Solid, MajorGridlineColor = OxyColor.FromAColor(40, c), MinorGridlineColor = OxyColor.FromAColor(20, c) }); + plot.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = "Y-axis", AbsoluteMinimum = 0, Minimum = 0, MajorGridlineStyle = LineStyle.Solid, MinorGridlineStyle = LineStyle.Solid, MajorGridlineColor = OxyColor.FromAColor(40, c), MinorGridlineColor = OxyColor.FromAColor(20, c) }); + plot.Series.Add(CreateTestSeries()); + return plot; + } + + [Example("AbsoluteMinimum Y, manual plotmargins")] + public static PlotModel AbsoluteYmin2() + { + var plot = new PlotModel + { + Title = "Y: AbsoluteMinimum = 0", + Subtitle = "AutoAdjustPlotMargins = false", + PlotType = PlotType.Cartesian, + PlotMargins = new OxyThickness(60, 4, 4, 40) + }; + var c = OxyColors.DarkBlue; + plot.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Title = "X-axis", MajorGridlineStyle = LineStyle.Solid, MinorGridlineStyle = LineStyle.Solid, MajorGridlineColor = OxyColor.FromAColor(40, c), MinorGridlineColor = OxyColor.FromAColor(20, c) }); + plot.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = "Y-axis", AbsoluteMinimum = 0, Minimum = 0, MajorGridlineStyle = LineStyle.Solid, MinorGridlineStyle = LineStyle.Solid, MajorGridlineColor = OxyColor.FromAColor(40, c), MinorGridlineColor = OxyColor.FromAColor(20, c) }); + plot.Series.Add(CreateTestSeries()); + return plot; + } + + [Example("AbsoluteMinimum X/Y")] + public static PlotModel AbsoluteYminXmin() + { + var plot = new PlotModel { Title = "X: AbsoluteMinimum = -10, Y: AbsoluteMinimum = 0", PlotType = PlotType.Cartesian }; + + var c = OxyColors.DarkBlue; + plot.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Title = "X-axis", AbsoluteMinimum = -10, MajorGridlineStyle = LineStyle.Solid, MinorGridlineStyle = LineStyle.Solid, MajorGridlineColor = OxyColor.FromAColor(40, c), MinorGridlineColor = OxyColor.FromAColor(20, c) }); + plot.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = "Y-axis", AbsoluteMinimum = 0, Minimum = 0, MajorGridlineStyle = LineStyle.Solid, MinorGridlineStyle = LineStyle.Solid, MajorGridlineColor = OxyColor.FromAColor(40, c), MinorGridlineColor = OxyColor.FromAColor(20, c) }); + plot.Series.Add(CreateTestSeries()); + return plot; + } + + [Example("AbsoluteMinimum/Maximum Y")] + public static PlotModel AbsoluteYminYmax() + { + var plot = new PlotModel { Title = "Y: AbsoluteMinimum = 0, AbsoluteMaximum = 2", PlotType = PlotType.Cartesian }; + + var c = OxyColors.DarkBlue; + plot.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Title = "X-axis", MajorGridlineStyle = LineStyle.Solid, MinorGridlineStyle = LineStyle.Solid, MajorGridlineColor = OxyColor.FromAColor(40, c), MinorGridlineColor = OxyColor.FromAColor(20, c) }); + plot.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = "Y-axis", AbsoluteMinimum = 0, Minimum = 0, AbsoluteMaximum = 2, MajorGridlineStyle = LineStyle.Solid, MinorGridlineStyle = LineStyle.Solid, MajorGridlineColor = OxyColor.FromAColor(40, c), MinorGridlineColor = OxyColor.FromAColor(20, c) }); + plot.Series.Add(CreateTestSeries()); + return plot; + } + + [Example("AbsoluteMinimum Y, AbsoluteMinimum/Maximum X")] + public static PlotModel AbsoluteYminXminXmax() + { + var plot = new PlotModel { Title = "Y: AbsoluteMinimum = 0, X: AbsoluteMinimum = -10, AbsoluteMaximum = 10", PlotType = PlotType.Cartesian }; + + var c = OxyColors.DarkBlue; + plot.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Title = "X-axis", AbsoluteMinimum = -10, AbsoluteMaximum = 10, MajorGridlineStyle = LineStyle.Solid, MinorGridlineStyle = LineStyle.Solid, MajorGridlineColor = OxyColor.FromAColor(40, c), MinorGridlineColor = OxyColor.FromAColor(20, c) }); + plot.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = "Y-axis", AbsoluteMinimum = 0, Minimum = 0, MajorGridlineStyle = LineStyle.Solid, MinorGridlineStyle = LineStyle.Solid, MajorGridlineColor = OxyColor.FromAColor(40, c), MinorGridlineColor = OxyColor.FromAColor(20, c) }); + plot.Series.Add(CreateTestSeries()); + + return plot; + } + + [Example("AbsoluteMinimum/Maximum X/Y")] + public static PlotModel AbsoluteYminYmaxXminXmax() + { + var plot = new PlotModel { Title = "Y: AbsoluteMinimum = 0, AbsoluteMaximum = 2, X: AbsoluteMinimum = -10, AbsoluteMaximum = 10", PlotType = PlotType.Cartesian }; + + var c = OxyColors.DarkBlue; + plot.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Title = "X-axis", AbsoluteMinimum = -10, AbsoluteMaximum = 10, MajorGridlineStyle = LineStyle.Solid, MinorGridlineStyle = LineStyle.Solid, MajorGridlineColor = OxyColor.FromAColor(40, c), MinorGridlineColor = OxyColor.FromAColor(20, c) }); + plot.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = "Y-axis", AbsoluteMinimum = 0, Minimum = 0, AbsoluteMaximum = 2, MajorGridlineStyle = LineStyle.Solid, MinorGridlineStyle = LineStyle.Solid, MajorGridlineColor = OxyColor.FromAColor(40, c), MinorGridlineColor = OxyColor.FromAColor(20, c) }); + plot.Series.Add(CreateTestSeries()); + + return plot; + } + + private static OxyPlot.Series.Series CreateTestSeries() + { + var absSerie = new LineSeries(); + + absSerie.Points.Add(new DataPoint(-8.0, 0.0)); + absSerie.Points.Add(new DataPoint(-7.5, 0.1)); + absSerie.Points.Add(new DataPoint(-7.0, 0.2)); + absSerie.Points.Add(new DataPoint(-6.0, 0.4)); + absSerie.Points.Add(new DataPoint(-5.0, 0.5)); + absSerie.Points.Add(new DataPoint(-4.0, 0.6)); + absSerie.Points.Add(new DataPoint(-3.0, 0.7)); + absSerie.Points.Add(new DataPoint(-2.0, 0.8)); + absSerie.Points.Add(new DataPoint(-1.0, 0.9)); + absSerie.Points.Add(new DataPoint(0.0, 1.0)); + absSerie.Points.Add(new DataPoint(1.0, 0.9)); + absSerie.Points.Add(new DataPoint(2.0, 0.8)); + absSerie.Points.Add(new DataPoint(3.0, 0.7)); + absSerie.Points.Add(new DataPoint(4.0, 0.6)); + absSerie.Points.Add(new DataPoint(5.0, 0.5)); + absSerie.Points.Add(new DataPoint(6.0, 0.4)); + absSerie.Points.Add(new DataPoint(7.0, 0.2)); + absSerie.Points.Add(new DataPoint(7.5, 0.1)); + absSerie.Points.Add(new DataPoint(8.0, 0.0)); + + absSerie.Points.Add(DataPoint.Undefined); + + // Plot a square + absSerie.Points.Add(new DataPoint(-0.5, 0.5)); + absSerie.Points.Add(new DataPoint(-0.5, 1.5)); + absSerie.Points.Add(new DataPoint(0.5, 1.5)); + absSerie.Points.Add(new DataPoint(0.5, 0.5)); + absSerie.Points.Add(new DataPoint(-0.5, 0.5)); + + return absSerie; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Axes/CategoryAxisExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Axes/CategoryAxisExamples.cs new file mode 100644 index 0000000..852f22d --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Axes/CategoryAxisExamples.cs @@ -0,0 +1,67 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using OxyPlot; + using OxyPlot.Axes; + + [Examples("CategoryAxis"), Tags("Axes")] + public static class CategoryAxisExamples + { + [Example("Standard")] + public static PlotModel StandardCategoryAxis() + { + var plotModel1 = new PlotModel { Title = "Standard" }; + var catAxis = new CategoryAxis(); + catAxis.Labels.AddRange(new[] { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12" }); + plotModel1.Axes.Add(catAxis); + var linearAxis = new LinearAxis { Position = AxisPosition.Left }; + plotModel1.Axes.Add(linearAxis); + return plotModel1; + } + + [Example("ItemsSource - string[]")] + public static PlotModel ItemsSourceStrings() + { + var model = new PlotModel { Title = "CategoryAxis with string[] as ItemsSource" }; + model.Axes.Add(new CategoryAxis + { + StringFormat = "Item {0}", + ItemsSource = new[] { "A", "B", "C" } + }); + var linearAxis = new LinearAxis { Position = AxisPosition.Left }; + model.Axes.Add(linearAxis); + return model; + } + + [Example("ItemsSource - int[]")] + public static PlotModel ItemsSourceValues() + { + var model = new PlotModel { Title = "CategoryAxis with int[] as ItemsSource" }; + model.Axes.Add(new CategoryAxis + { + StringFormat = "Item {0}", + ItemsSource = new[] { 10, 100, 123 } + }); + var linearAxis = new LinearAxis { Position = AxisPosition.Left }; + model.Axes.Add(linearAxis); + return model; + } + + [Example("MajorStep")] + public static PlotModel MajorStepCategoryAxis() + { + var plotModel1 = new PlotModel { Title = "Major Step = 4, IsTickCentered = true" }; + var catAxis = new CategoryAxis { IsTickCentered = true, MajorStep = 4 }; + catAxis.Labels.AddRange(new[] { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12" }); + plotModel1.Axes.Add(catAxis); + var linearAxis = new LinearAxis { Position = AxisPosition.Left }; + plotModel1.Axes.Add(linearAxis); + return plotModel1; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Axes/CategoryColorAxisExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Axes/CategoryColorAxisExamples.cs new file mode 100644 index 0000000..2127362 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Axes/CategoryColorAxisExamples.cs @@ -0,0 +1,49 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("CategoryColorAxis"), Tags("Axes")] + public class CategoryColorAxisExamples + { + [Example("CategoryColorAxis")] + public static PlotModel StandardCategoryColorAxis() + { + var plotModel1 = new PlotModel { Title = "CategoryColorAxis" }; + var catAxis = new CategoryColorAxis { Key = "ccc", Palette = OxyPalettes.BlackWhiteRed(12) }; + catAxis.Labels.AddRange(new[] { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12" }); + plotModel1.Axes.Add(catAxis); + var linearAxis = new LinearAxis { Position = AxisPosition.Left }; + var ss = new ScatterSeries { ColorAxisKey = catAxis.Key }; + ss.Points.Add(new ScatterPoint(0, 0) { Value = 0 }); + ss.Points.Add(new ScatterPoint(3, 0) { Value = 3 }); + plotModel1.Series.Add(ss); + plotModel1.Axes.Add(linearAxis); + return plotModel1; + } + + [Example("Centered ticks, MajorStep = 4")] + public static PlotModel MajorStep4() + { + var plotModel1 = new PlotModel { Title = "Major Step = 4, IsTickCentered = true" }; + var catAxis = new CategoryColorAxis + { + Palette = OxyPalettes.BlackWhiteRed(3), + IsTickCentered = true, + MajorStep = 4 + }; + catAxis.Labels.AddRange(new[] { "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12" }); + plotModel1.Axes.Add(catAxis); + var linearAxis = new LinearAxis { Position = AxisPosition.Left }; + plotModel1.Axes.Add(linearAxis); + return plotModel1; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Axes/CustomAxisExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Axes/CustomAxisExamples.cs new file mode 100644 index 0000000..756d001 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Axes/CustomAxisExamples.cs @@ -0,0 +1,53 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System.Collections.Generic; + + using OxyPlot; + using OxyPlot.Axes; + + [Examples("Custom axes"), Tags("Axes")] + public static class CustomAxisExamples + { + public class ArrowAxis : LinearAxis + { + public override void Render(IRenderContext rc, int pass) + { + base.Render(rc, pass); + var points = new List(); + if (this.IsHorizontal()) + { + var xmax = this.Transform(this.ActualMaximum); + points.Add(new ScreenPoint(xmax + 4, this.PlotModel.PlotArea.Bottom - 4)); + points.Add(new ScreenPoint(xmax + 18, this.PlotModel.PlotArea.Bottom)); + points.Add(new ScreenPoint(xmax + 4, this.PlotModel.PlotArea.Bottom + 4)); + //// etc. + } + else + { + var ymax = this.Transform(this.ActualMaximum); + points.Add(new ScreenPoint(this.PlotModel.PlotArea.Left - 4, ymax - 4)); + points.Add(new ScreenPoint(this.PlotModel.PlotArea.Left, ymax - 18)); + points.Add(new ScreenPoint(this.PlotModel.PlotArea.Left + 4, ymax - 4)); + //// etc. + } + + rc.DrawPolygon(points, OxyColors.Black, OxyColors.Undefined); + } + } + + [Example("ArrowAxis")] + public static PlotModel CustomArrowAxis() + { + var model = new PlotModel { PlotAreaBorderThickness = new OxyThickness(0), PlotMargins = new OxyThickness(60, 60, 60, 60) }; + model.Axes.Add(new ArrowAxis { Position = AxisPosition.Bottom, AxislineStyle = LineStyle.Solid }); + model.Axes.Add(new ArrowAxis { Position = AxisPosition.Left, AxislineStyle = LineStyle.Solid }); + return model; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Axes/DateTimeAxisExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Axes/DateTimeAxisExamples.cs new file mode 100644 index 0000000..650b301 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Axes/DateTimeAxisExamples.cs @@ -0,0 +1,248 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + + +namespace ExampleLibrary +{ + using System; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Globalization; + + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("DateTimeAxis"), Tags("Axes")] + public static class DateTimeAxisExamples + { + public class DateValue + { + public DateTime Date { get; set; } + public double Value { get; set; } + } + + [Example("Default StringFormat")] + public static PlotModel DefaultValues() + { + return CreateExample(7, null); + } + + [Example("StringFormat 'MMM dd\\nyyyy'")] + public static PlotModel StringFormat() + { + return CreateExample(7, "MMM dd\nyyyy"); + } + + private static PlotModel CreateExample(int days, string stringFormat) + { + var m = new PlotModel(); + var startTime = new DateTime(2000, 1, 1); + var min = DateTimeAxis.ToDouble(startTime); + var max = min + days; + m.Axes.Add(new DateTimeAxis { Position = AxisPosition.Bottom, Minimum = min, Maximum = max, StringFormat = stringFormat }); + m.Axes.Add(new DateTimeAxis { Position = AxisPosition.Left, Minimum = min, Maximum = max, StringFormat = stringFormat }); + return m; + } + + // [Example("DateTime Minimum bug")] + public static PlotModel Example1() + { + var tmp = new PlotModel { Title = "Test" }; + tmp.Axes.Add(new LinearAxis { Position = AxisPosition.Left, MajorGridlineStyle = LineStyle.Solid, MinorGridlineStyle = LineStyle.Dot, TickStyle = TickStyle.Outside }); + var dt = new DateTime(2010, 1, 1); + tmp.Axes.Add(new DateTimeAxis + { + Position = AxisPosition.Bottom, + Minimum = DateTimeAxis.ToDouble(dt), + Maximum = DateTimeAxis.ToDouble(dt.AddDays(1)), + IntervalType = DateTimeIntervalType.Hours, + MajorGridlineStyle = LineStyle.Solid, + Angle = 90, + StringFormat = "HH:mm", + MajorStep = 1.0 / 24 / 2, // 1/24 = 1 hour, 1/24/2 = 30 minutes + IsZoomEnabled = true, + MaximumPadding = 0, + MinimumPadding = 0, + TickStyle = TickStyle.None + }); + + var ls = new LineSeries { Title = "Line1", DataFieldX = "X", DataFieldY = "Y" }; + var ii = new List(); + + for (int i = 0; i < 24; i++) + ii.Add(new Item { X = dt.AddHours(i), Y = i * i }); + ls.ItemsSource = ii; + tmp.Series.Add(ls); + return tmp; + } + + [Example("TimeZone adjustments")] + public static PlotModel DaylightSavingsBreak() + { + var m = new PlotModel(); + + var xa = new DateTimeAxis { Position = AxisPosition.Bottom }; + // TimeZone not available in PCL... + + m.Axes.Add(xa); + m.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + var ls = new LineSeries { MarkerType = MarkerType.Circle }; + m.Series.Add(ls); + + // set the origin of the curve to 2013-03-31 00:00:00 (UTC) + var o = new DateTime(2013, 3, 31, 0, 0, 0, DateTimeKind.Utc); + + // add points at 10min intervals + // at 2am the clocks are turned forward 1 hour (W. Europe Standard Time) + for (int i = 0; i < 400; i += 10) + { + var time = o.AddMinutes(i); + ls.Points.Add(DateTimeAxis.CreateDataPoint(time, i)); + } + + return m; + } + + public class Item + { + public DateTime X { get; set; } + public double Y { get; set; } + } + + [Example("DateTime axis")] + public static PlotModel DateTimeaxisPlotModel() + { + var start = new DateTime(2010, 01, 01); + var end = new DateTime(2015, 01, 01); + double increment = 3600 * 24 * 14; + + // Create a random data collection + var r = new Random(13); + var data = new Collection(); + var date = start; + while (date <= end) + { + data.Add(new DateValue { Date = date, Value = r.NextDouble() }); + date = date.AddSeconds(increment); + } + + var plotModel1 = new PlotModel { Title = "DateTime axis" }; + var dateTimeAxis1 = new DateTimeAxis + { + CalendarWeekRule = CalendarWeekRule.FirstFourDayWeek, + FirstDayOfWeek = DayOfWeek.Monday, + Position = AxisPosition.Bottom + }; + plotModel1.Axes.Add(dateTimeAxis1); + var linearAxis1 = new LinearAxis(); + plotModel1.Axes.Add(linearAxis1); + var lineSeries1 = new LineSeries + { + Color = OxyColor.FromArgb(255, 78, 154, 6), + MarkerFill = OxyColor.FromArgb(255, 78, 154, 6), + MarkerStroke = OxyColors.ForestGreen, + MarkerType = MarkerType.Plus, + StrokeThickness = 1, + DataFieldX = "Date", + DataFieldY = "Value", + ItemsSource = data + }; + plotModel1.Series.Add(lineSeries1); + return plotModel1; + } + + public class SunItem + { + public DateTime Day { get; set; } + public TimeSpan Sunrise { get; set; } + public TimeSpan Sunset { get; set; } + } + + private static Collection CreateSunData(int year, double lat, double lon, Func utcToLocalTime) + { + var data = new Collection(); + var day = new DateTime(year, 1, 1); + + while (day.Year == year) + { + var sunrise = Sun.Calculate(day, lat, lon, true, utcToLocalTime); + var sunset = Sun.Calculate(day, lat, lon, false, utcToLocalTime); + data.Add(new SunItem { Day = day, Sunrise = sunrise - day, Sunset = sunset - day }); + day = day.AddDays(1); + } + + return data; + } + + public static bool IsDaylightSaving(DateTime time) + { + // Daylight saving starts last sunday in March and ends last sunday in October + // http://en.wikipedia.org/wiki/Daylight_saving_time + var start = new DateTime(time.Year, 3, 31, 2, 0, 0); + start = start.AddDays(-(int)start.DayOfWeek); + var end = new DateTime(time.Year, 10, 31, 3, 0, 0); + end = end.AddDays(-(int)end.DayOfWeek); + return time >= start && time <= end; + } + + [Example("Sunrise and sunset in Oslo")] + public static PlotModel SunriseandsunsetinOslo() + { + int year = DateTime.Now.Year; + + // Convert UTC time to Western European Time (WET) + Func utcToLocalTime = utc => utc.AddHours(IsDaylightSaving(utc) ? 2 : 1); + + var sunData = CreateSunData(year, 59.91, 10.75, utcToLocalTime); + + var plotModel1 = new PlotModel { Title = "Sunrise and sunset in Oslo", Subtitle = "UTC time" }; + + var dateTimeAxis1 = new DateTimeAxis + { + CalendarWeekRule = CalendarWeekRule.FirstFourDayWeek, + FirstDayOfWeek = DayOfWeek.Monday, + IntervalType = DateTimeIntervalType.Months, + MajorGridlineStyle = LineStyle.Solid, + Position = AxisPosition.Bottom, + StringFormat = "MMM" + }; + plotModel1.Axes.Add(dateTimeAxis1); + var timeSpanAxis1 = new TimeSpanAxis { MajorGridlineStyle = LineStyle.Solid, Maximum = 86400, Minimum = 0, StringFormat = "h:mm" }; + plotModel1.Axes.Add(timeSpanAxis1); + var areaSeries1 = new AreaSeries + { + ItemsSource = sunData, + DataFieldX = "Day", + DataFieldY = "Sunrise", + DataFieldX2 = "Day", + DataFieldY2 = "Sunset", + Fill = OxyColor.FromArgb(128, 255, 255, 0), + Color = OxyColors.Black + }; + plotModel1.Series.Add(areaSeries1); + return plotModel1; + } + + [Example("LabelFormatter")] + public static PlotModel LabelFormatter() + { + var model = new PlotModel { Title = "Using LabelFormatter to format labels by day of week" }; + model.Axes.Add(new DateTimeAxis { LabelFormatter = x => DateTimeAxis.ToDateTime(x).DayOfWeek.ToString().Substring(0, 3) }); + var series = new LineSeries(); + model.Series.Add(series); + for (int i = 0; i < 7; i++) + { + var time = new DateTime(2014, 9, 10).AddDays(i); + double x = DateTimeAxis.ToDouble(time); + double y = Math.Sin(i * i); + series.Points.Add(new DataPoint(x, y)); + } + + return model; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Axes/LinearAxisExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Axes/LinearAxisExamples.cs new file mode 100644 index 0000000..9c45e25 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Axes/LinearAxisExamples.cs @@ -0,0 +1,141 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using OxyPlot; + using OxyPlot.Axes; + + [Examples("LinearAxis"), Tags("Axes")] + public static class LinearAxisExamples + { + [Example("Default StringFormat ('g6')")] + public static PlotModel StringFormat() + { + return CreateExample(1.2345678901234567890e5, 1.2345678901234567890e6, null); + } + + [Example("StringFormat = 'g2'")] + public static PlotModel StringFormatG2() + { + return CreateExample(1.2345678901234567890e5, 1.2345678901234567890e6, "g2"); + } + + [Example("StringFormat = 'g10'")] + public static PlotModel StringFormatG10() + { + return CreateExample(1.2345678901234567890e5, 1.2345678901234567890e6, "g10"); + } + + [Example("StringFormat = 'f2'")] + public static PlotModel StringFormatF2() + { + return CreateExample(1.2345678901234567890e5, 1.2345678901234567890e6, "f2"); + } + + private static PlotModel CreateExample(double min, double max, string stringFormat) + { + var m = new PlotModel(); + m.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = min, Maximum = max, StringFormat = stringFormat }); + m.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = min, Maximum = max, StringFormat = stringFormat }); + return m; + } + + [Example("TickStyle: None")] + public static PlotModel TickStyleNone() + { + return CreateTickStyleModel(TickStyle.None); + } + + [Example("TickStyle: Crossing")] + public static PlotModel TickStyleCrossing() + { + return CreateTickStyleModel(TickStyle.Crossing); + } + + [Example("TickStyle: Inside")] + public static PlotModel TickStyleInside() + { + return CreateTickStyleModel(TickStyle.Inside); + } + + [Example("TickStyle: Outside")] + public static PlotModel TickStyleOutside() + { + return CreateTickStyleModel(TickStyle.Outside); + } + + private static PlotModel CreateTickStyleModel(TickStyle tickStyle) + { + var model = new PlotModel { Title = "TickStyle: " + tickStyle }; + model.Axes.Add(new LinearAxis + { + Position = AxisPosition.Bottom, + TickStyle = tickStyle, + MajorGridlineStyle = LineStyle.None, + MinorGridlineStyle = LineStyle.None, + MaximumPadding = 0, + MinimumPadding = 0 + }); + model.Axes.Add(new LinearAxis + { + Position = AxisPosition.Left, + TickStyle = tickStyle, + MajorGridlineStyle = LineStyle.None, + MinorGridlineStyle = LineStyle.None, + MaximumPadding = 0, + MinimumPadding = 0 + }); + return model; + } + + [Example("Gridlines: None")] + public static PlotModel GridlinesNone() + { + return CreateGridlinesModel("None", LineStyle.None, LineStyle.None); + } + + [Example("Gridlines: Horizontal")] + public static PlotModel GridlinesHorizontal() + { + return CreateGridlinesModel("Horizontal", LineStyle.Solid, LineStyle.None); + } + + [Example("Gridlines: Vertical")] + public static PlotModel GridlinesVertical() + { + return CreateGridlinesModel("Vertical", LineStyle.None, LineStyle.Solid); + } + + [Example("Gridlines: Both")] + public static PlotModel GridlinesBoth() + { + return CreateGridlinesModel("Both", LineStyle.Solid, LineStyle.Solid); + } + + private static PlotModel CreateGridlinesModel(string title, LineStyle horizontal, LineStyle vertical) + { + var model = new PlotModel { Title = "Gridlines: " + title }; + model.Axes.Add(new LinearAxis + { + Position = AxisPosition.Bottom, + MajorGridlineStyle = vertical, + MinorGridlineStyle = vertical == LineStyle.Solid ? LineStyle.Dot : LineStyle.None, + MaximumPadding = 0, + MinimumPadding = 0 + }); + model.Axes.Add(new LinearAxis + { + Position = AxisPosition.Left, + MajorGridlineStyle = horizontal, + MinorGridlineStyle = horizontal == LineStyle.Solid ? LineStyle.Dot : LineStyle.None, + MaximumPadding = 0, + MinimumPadding = 0 + }); + return model; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Axes/LinearColorAxisExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Axes/LinearColorAxisExamples.cs new file mode 100644 index 0000000..fb912e0 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Axes/LinearColorAxisExamples.cs @@ -0,0 +1,203 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using OxyPlot; + using OxyPlot.Axes; + + [Examples("LinearColorAxis"), Tags("Axes")] + public class LinearColorAxisExamples + { + [Example("Default palette")] + public static PlotModel DefaultPalette() + { + var model = HeatMapSeriesExamples.CreatePeaks(null, false); + model.Axes.Clear(); + model.Axes.Add(new LinearColorAxis { Position = AxisPosition.Right }); + return model; + } + + [Example("Jet (200 colors) palette")] + public static PlotModel Jet200() + { + return HeatMapSeriesExamples.CreatePeaks(OxyPalettes.Jet(200), false); + } + + [Example("Jet (20 colors) palette")] + public static PlotModel Jet20() + { + return HeatMapSeriesExamples.CreatePeaks(OxyPalettes.Jet(20), false); + } + + [Example("Hue (400 colors) palette")] + public static PlotModel Hue400() + { + return HeatMapSeriesExamples.CreatePeaks(OxyPalettes.Hue(400), false); + } + + [Example("Hue distinct (200 colors) palette")] + public static PlotModel HueDistinct200() + { + return HeatMapSeriesExamples.CreatePeaks(OxyPalettes.HueDistinct(200), false); + } + + [Example("Hue distinct reversed (200 colors) palette")] + public static PlotModel HueDistinctReverse200() + { + return HeatMapSeriesExamples.CreatePeaks(OxyPalettes.HueDistinct(200).Reverse(), false); + } + + [Example("Hot (200 colors) palette")] + public static PlotModel Hot200() + { + return HeatMapSeriesExamples.CreatePeaks(OxyPalettes.Hot(200), false); + } + + [Example("Hot (64 colors) palette")] + public static PlotModel Hot64() + { + return HeatMapSeriesExamples.CreatePeaks(OxyPalettes.Hot64, false); + } + + [Example("Hot (30 colors) palette")] + public static PlotModel Hot30() + { + return HeatMapSeriesExamples.CreatePeaks(OxyPalettes.Hot(30), false); + } + + [Example("Blue-white-red (200 colors) palette")] + public static PlotModel BlueWhiteRed200() + { + return HeatMapSeriesExamples.CreatePeaks(OxyPalettes.BlueWhiteRed(200), false); + } + + [Example("Blue-white-red (40 colors) palette")] + public static PlotModel BlueWhiteRed40() + { + return HeatMapSeriesExamples.CreatePeaks(OxyPalettes.BlueWhiteRed(40), false); + } + + [Example("Black-white-red (500 colors) palette")] + public static PlotModel BlackWhiteRed500() + { + return HeatMapSeriesExamples.CreatePeaks(OxyPalettes.BlackWhiteRed(500), false); + } + + [Example("Black-white-red (3 colors) palette")] + public static PlotModel BlackWhiteRed3() + { + return HeatMapSeriesExamples.CreatePeaks(OxyPalettes.BlackWhiteRed(3), false); + } + + [Example("Cool (200 colors) palette")] + public static PlotModel Cool200() + { + return HeatMapSeriesExamples.CreatePeaks(OxyPalettes.Cool(200), false); + } + + [Example("Rainbow (200 colors) palette")] + public static PlotModel Rainbow200() + { + return HeatMapSeriesExamples.CreatePeaks(OxyPalettes.Rainbow(200), false); + } + + [Example("Rainbow (7 colors) palette")] + public static PlotModel Rainbow7() + { + return HeatMapSeriesExamples.CreatePeaks(OxyPalettes.Rainbow(7), false); + } + + [Example("Vertical (6 colors)")] + public static PlotModel Vertical_6() + { + return HeatMapSeriesExamples.CreatePeaks(OxyPalettes.Jet(6), false); + } + + [Example("Vertical reverse (6 colors)")] + public static PlotModel Vertical_Reverse_6() + { + var model = HeatMapSeriesExamples.CreatePeaks(OxyPalettes.Jet(6), false); + var colorAxis = (LinearColorAxis)model.Axes[0]; + colorAxis.StartPosition = 1; + colorAxis.EndPosition = 0; + return model; + } + + [Example("Horizontal (6 colors)")] + public static PlotModel Horizontal_6() + { + var model = HeatMapSeriesExamples.CreatePeaks(OxyPalettes.Jet(6), false); + var colorAxis = (LinearColorAxis)model.Axes[0]; + colorAxis.Position = AxisPosition.Top; + return model; + } + + [Example("Horizontal reverse (6 colors)")] + public static PlotModel Horizontal_Reverse_6() + { + var model = HeatMapSeriesExamples.CreatePeaks(OxyPalettes.Jet(6), false); + var colorAxis = (LinearColorAxis)model.Axes[0]; + colorAxis.Position = AxisPosition.Top; + colorAxis.StartPosition = 1; + colorAxis.EndPosition = 0; + return model; + } + + [Example("RenderAsImage (horizontal)")] + public static PlotModel RenderAsImage_Horizontal() + { + var model = HeatMapSeriesExamples.CreatePeaks(OxyPalettes.Jet(1000), false); + var colorAxis = (LinearColorAxis)model.Axes[0]; + colorAxis.RenderAsImage = true; + colorAxis.Position = AxisPosition.Top; + return model; + } + + [Example("RenderAsImage (horizontal reversed)")] + public static PlotModel RenderAsImage_Horizontal_Reversed() + { + var model = HeatMapSeriesExamples.CreatePeaks(OxyPalettes.Jet(1000), false); + var colorAxis = (LinearColorAxis)model.Axes[0]; + colorAxis.RenderAsImage = true; + colorAxis.Position = AxisPosition.Top; + colorAxis.StartPosition = 1; + colorAxis.EndPosition = 0; + return model; + } + + [Example("RenderAsImage (vertical)")] + public static PlotModel RenderAsImage_Vertical() + { + var model = HeatMapSeriesExamples.CreatePeaks(OxyPalettes.Jet(1000), false); + var colorAxis = (LinearColorAxis)model.Axes[0]; + colorAxis.RenderAsImage = true; + return model; + } + + [Example("RenderAsImage (vertical reversed)")] + public static PlotModel RenderAsImage_Vertical_Reversed() + { + var model = HeatMapSeriesExamples.CreatePeaks(OxyPalettes.Jet(1000), false); + var colorAxis = (LinearColorAxis)model.Axes[0]; + colorAxis.RenderAsImage = true; + colorAxis.StartPosition = 1; + colorAxis.EndPosition = 0; + return model; + } + + [Example("Short vertical")] + public static PlotModel Vertical_Short() + { + var model = HeatMapSeriesExamples.CreatePeaks(OxyPalettes.Jet(600), false); + var colorAxis = (LinearColorAxis)model.Axes[0]; + colorAxis.StartPosition = 0.02; + colorAxis.EndPosition = 0.5; + return model; + } + + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Axes/LogarithmicAxisExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Axes/LogarithmicAxisExamples.cs new file mode 100644 index 0000000..817714d --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Axes/LogarithmicAxisExamples.cs @@ -0,0 +1,163 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("LogarithmicAxis"), Tags("Axes")] + public static class LogarithmicAxisExamples + { + [Example("LogarithmicAxis with default values")] + public static PlotModel DefaultValues() + { + var m = new PlotModel(); + m.Axes.Add(new LogarithmicAxis { Position = AxisPosition.Bottom }); + m.Axes.Add(new LogarithmicAxis { Position = AxisPosition.Left}); + return m; + } + + [Example("Amdahl's Law")] + public static PlotModel AmdahlsLaw() + { + var model = new PlotModel { Title = "Amdahl's law", LegendTitle = "Parallel portion" }; + + // http://en.wikipedia.org/wiki/Amdahl's_law + Func maxSpeedup = (p, n) => 1.0 / ((1.0 - p) + (double)p / n); + Func createSpeedupCurve = p => + { + // todo: tracker does not work when smoothing = true (too few points interpolated on the left end of the curve) + var ls = new LineSeries { Title = p.ToString("P0") }; + for (int n = 1; n <= 65536; n *= 2) ls.Points.Add(new DataPoint(n, maxSpeedup(p, n))); + return ls; + }; + model.Axes.Add(new LogarithmicAxis { Position = AxisPosition.Bottom, Title = "Number of processors", Base = 2, MajorGridlineStyle = LineStyle.Solid, TickStyle = TickStyle.None }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = 0, Maximum = 20, MinorStep = 2, MajorStep = 2, Title = "Speedup", StringFormat = "F2", MajorGridlineStyle = LineStyle.Solid, TickStyle = TickStyle.None }); + model.Series.Add(createSpeedupCurve(0.5)); + model.Series.Add(createSpeedupCurve(0.75)); + model.Series.Add(createSpeedupCurve(0.9)); + model.Series.Add(createSpeedupCurve(0.95)); + + return model; + } + + [Example("Richter magnitudes")] + public static PlotModel RichterMagnitudes() + { + // http://en.wikipedia.org/wiki/Richter_magnitude_scale + + var model = new PlotModel + { + Title = "The Richter magnitude scale", + PlotMargins = new OxyThickness(80, 0, 80, 40), + LegendPlacement = LegendPlacement.Inside, + LegendPosition = LegendPosition.TopCenter, + LegendOrientation = LegendOrientation.Horizontal, + LegendSymbolLength = 24 + }; + + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Title = "Richter magnitude scale", MajorGridlineStyle = LineStyle.None, TickStyle = TickStyle.None }); + + var frequencyCurve = new LineSeries + { + Title = "Frequency", + Color = OxyColor.FromUInt32(0xff3c6c9e), + StrokeThickness = 3, + MarkerStroke = OxyColor.FromUInt32(0xff3c6c9e), + MarkerFill = OxyColors.White, + MarkerType = MarkerType.Circle, + MarkerSize = 4, + MarkerStrokeThickness = 3 + }; + + frequencyCurve.Points.Add(new DataPoint(1.5, 8000 * 365 * 100)); + frequencyCurve.Points.Add(new DataPoint(2.5, 1000 * 365 * 100)); + frequencyCurve.Points.Add(new DataPoint(3.5, 49000 * 100)); + frequencyCurve.Points.Add(new DataPoint(4.5, 6200 * 100)); + frequencyCurve.Points.Add(new DataPoint(5.5, 800 * 100)); + frequencyCurve.Points.Add(new DataPoint(6.5, 120 * 100)); + frequencyCurve.Points.Add(new DataPoint(7.5, 18 * 100)); + frequencyCurve.Points.Add(new DataPoint(8.5, 1 * 100)); + frequencyCurve.Points.Add(new DataPoint(9.5, 1.0 / 20 * 100)); + model.Axes.Add(new LogarithmicAxis { Position = AxisPosition.Left, Title = "Frequency / 100 yr", UseSuperExponentialFormat = true, MajorGridlineStyle = LineStyle.None, TickStyle = TickStyle.Outside }); + model.Series.Add(frequencyCurve); + + var energyCurve = new LineSeries + { + Title = "Energy", + Color = OxyColor.FromUInt32(0xff9e6c3c), + StrokeThickness = 3, + MarkerStroke = OxyColor.FromUInt32(0xff9e6c3c), + MarkerFill = OxyColors.White, + MarkerType = MarkerType.Circle, + MarkerSize = 4, + MarkerStrokeThickness = 3 + }; + + energyCurve.Points.Add(new DataPoint(1.5, 11e6)); + energyCurve.Points.Add(new DataPoint(2.5, 360e6)); + energyCurve.Points.Add(new DataPoint(3.5, 11e9)); + energyCurve.Points.Add(new DataPoint(4.5, 360e9)); + energyCurve.Points.Add(new DataPoint(5.5, 11e12)); + energyCurve.Points.Add(new DataPoint(6.5, 360e12)); + energyCurve.Points.Add(new DataPoint(7.5, 11e15)); + energyCurve.Points.Add(new DataPoint(8.5, 360e15)); + energyCurve.Points.Add(new DataPoint(9.5, 11e18)); + energyCurve.YAxisKey = "energyAxis"; + + model.Axes.Add(new LogarithmicAxis { Position = AxisPosition.Right, Title = "Energy / J", Key = "energyAxis", UseSuperExponentialFormat = true, MajorGridlineStyle = LineStyle.None, TickStyle = TickStyle.Outside }); + model.Series.Add(energyCurve); + + return model; + } + + [Example("LogarithmicAxis with AbsoluteMaximum")] + public static PlotModel AbsoluteMaximum() + { + var model = new PlotModel { Title = "AbsoluteMaximum = 1000" }; + model.Axes.Add(new LogarithmicAxis { Position = AxisPosition.Left, Minimum = 0.1, Maximum = 1000, AbsoluteMaximum = 1000 }); + model.Series.Add(new FunctionSeries(Math.Exp, 0, Math.Log(900), 100)); + return model; + } + + [Example("LogarithmicAxis with AxisChanged event handler")] + public static PlotModel AxisChangedEventHAndler() + { + var model = new PlotModel { Title = "AxisChanged event handler" }; + var logAxis = new LogarithmicAxis { Position = AxisPosition.Left, Minimum = 0.1, Maximum = 1000 }; + int n = 0; + logAxis.AxisChanged += (s, e) => { model.Subtitle = "Changed " + (n++) + " times. ActualMaximum=" + logAxis.ActualMaximum; }; + model.Axes.Add(logAxis); + model.Series.Add(new FunctionSeries(Math.Exp, 0, Math.Log(900), 100)); + return model; + } + + [Example("Negative values")] + public static PlotModel NegativeValues() + { + var model = new PlotModel { Title = "LogarithmicAxis", Subtitle = "LineSeries with negative values" }; + model.Axes.Add(new LogarithmicAxis { Position = AxisPosition.Left }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Series.Add(new FunctionSeries(Math.Sin, 0, 40, 1000)); + return model; + } + + [Example("Tick calculation")] + public static PlotModel TickCalculation() + { + var model = new PlotModel { Title = "Tick calculation for different bases" }; + model.Axes.Add(new LogarithmicAxis { Title = "Base 10", Position = AxisPosition.Left, Minimum = 20, Maximum = 20000, MajorGridlineStyle = LineStyle.Solid, MinorGridlineStyle = LineStyle.Solid }); + model.Axes.Add(new LogarithmicAxis { Title = "Base 7", Position = AxisPosition.Bottom, Base = 7, Minimum = 2, Maximum = 10000, MajorGridlineStyle = LineStyle.Solid, MinorGridlineStyle = LineStyle.Solid }); + model.Axes.Add(new LogarithmicAxis { Title = "Base 5.5", Position = AxisPosition.Top, Base = 5.5, Minimum = 1, Maximum = 100 }); + model.Axes.Add(new LogarithmicAxis { Title = "Base 2", Position = AxisPosition.Right, Base = 2, Minimum = 1, Maximum = 1000000 }); + return model; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Axes/PolarPlotExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Axes/PolarPlotExamples.cs new file mode 100644 index 0000000..aabedae --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Axes/PolarPlotExamples.cs @@ -0,0 +1,247 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Shows how to orient 0 degrees at the bottom and add E/W to indicate directions. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("Polar Plots"), Tags("Axes")] + public static class PolarPlotExamples + { + [Example("Spiral")] + public static PlotModel ArchimedeanSpiral() + { + var model = new PlotModel + { + Title = "Polar plot", + Subtitle = "Archimedean spiral with equation r(θ) = θ for 0 < θ < 6π", + PlotType = PlotType.Polar, + PlotAreaBorderThickness = new OxyThickness(0), + PlotMargins = new OxyThickness(60, 20, 4, 40) + }; + model.Axes.Add( + new AngleAxis + { + MajorStep = Math.PI / 4, + MinorStep = Math.PI / 16, + MajorGridlineStyle = LineStyle.Solid, + MinorGridlineStyle = LineStyle.Solid, + FormatAsFractions = true, + FractionUnit = Math.PI, + FractionUnitSymbol = "π", + Minimum = 0, + Maximum = 2 * Math.PI + }); + model.Axes.Add(new MagnitudeAxis + { + MajorGridlineStyle = LineStyle.Solid, + MinorGridlineStyle = LineStyle.Solid + }); + model.Series.Add(new FunctionSeries(t => t, t => t, 0, Math.PI * 6, 0.01)); + return model; + } + + [Example("Spiral2")] + public static PlotModel ArchimedeanSpiral2() + { + var model = ArchimedeanSpiral(); + model.Title += "(reversed angle axis)"; + var angleAxis = (AngleAxis)model.Axes[0]; + angleAxis.StartAngle = 360; + angleAxis.EndAngle = 0; + return model; + } + + [Example("Spiral with magnitude axis min and max")] + public static PlotModel ArchimedeanSpiral3() + { + var model = ArchimedeanSpiral(); + model.Title += " (axis Minimum = 10 and Maximum = 20)"; + var magnitudeAxis = (MagnitudeAxis)model.Axes[1]; + magnitudeAxis.Minimum = 10; + magnitudeAxis.Maximum = 20; + return model; + } + + [Example("Angle axis with offset angle")] + public static PlotModel OffsetAngles() + { + var model = new PlotModel + { + Title = "Offset angle axis", + PlotType = PlotType.Polar, + PlotAreaBorderThickness = new OxyThickness(0), + PlotMargins = new OxyThickness(60, 20, 4, 40) + }; + + var angleAxis = new AngleAxis + { + Minimum = 0, + Maximum = Math.PI * 2, + MajorStep = Math.PI / 4, + MinorStep = Math.PI / 16, + StringFormat = "0.00", + StartAngle = 30, + EndAngle = 390 + }; + model.Axes.Add(angleAxis); + model.Axes.Add(new MagnitudeAxis()); + model.Series.Add(new FunctionSeries(t => t, t => t, 0, Math.PI * 6, 0.01)); + + // Subscribe to the mouse down event on the line series. + model.MouseDown += (s, e) => + { + var increment = 0d; + + // Increment and decrement must be in degrees (corresponds to the StartAngle and EndAngle properties). + if (e.ChangedButton == OxyMouseButton.Left) + { + increment = 15; + } + + if (e.ChangedButton == OxyMouseButton.Right) + { + increment = -15; + } + + if (Math.Abs(increment) > double.Epsilon) + { + angleAxis.StartAngle += increment; + angleAxis.EndAngle += increment; + model.InvalidatePlot(false); + e.Handled = true; + } + }; + + return model; + } + + [Example("Semi-circle")] + public static PlotModel SemiCircle() + { + var model = new PlotModel + { + Title = "Semi-circle polar plot", + PlotType = PlotType.Polar, + PlotAreaBorderThickness = new OxyThickness(0), + PlotMargins = new OxyThickness(60, 20, 4, 40) + }; + model.Axes.Add( + new AngleAxis + { + Minimum = 0, + Maximum = 180, + MajorStep = 45, + MinorStep = 9, + StartAngle = 0, + EndAngle = 180, + MajorGridlineStyle = LineStyle.Solid, + MinorGridlineStyle = LineStyle.Solid + }); + model.Axes.Add(new MagnitudeAxis + { + Minimum = 0, + Maximum = 1, + MajorGridlineStyle = LineStyle.Solid, + MinorGridlineStyle = LineStyle.Solid + }); + model.Series.Add(new FunctionSeries(x => Math.Sin(x / 180 * Math.PI), t => t, 0, 180, 0.01)); + return model; + } + + [Example("Semi-circle offset angle axis range")] + public static PlotModel SemiCircleOffsetAngleAxisRange() + { + var model = new PlotModel + { + Title = "Semi-circle polar plot", + Subtitle = "Angle axis range offset to -180 - 180", + PlotType = PlotType.Polar, + PlotAreaBorderThickness = new OxyThickness(0), + PlotMargins = new OxyThickness(60, 20, 4, 40) + }; + model.Axes.Add( + new AngleAxis + { + Minimum = -180, + Maximum = 180, + MajorStep = 45, + MinorStep = 9, + StartAngle = 0, + EndAngle = 360, + MajorGridlineStyle = LineStyle.Solid, + MinorGridlineStyle = LineStyle.Solid + }); + model.Axes.Add(new MagnitudeAxis + { + Minimum = 0, + Maximum = 1, + MajorGridlineStyle = LineStyle.Solid, + MinorGridlineStyle = LineStyle.Solid + }); + model.Series.Add(new FunctionSeries(x => Math.Sin(x / 180 * Math.PI), t => t, 0, 180, 0.01)); + return model; + } + + /// + /// Shows how to orient 0 degrees at the bottom and add E/W to indicate directions. + /// + /// + [Example("East/west directions")] + public static PlotModel EastWestDirections() + { + var model = new PlotModel + { + Title = "East/west directions", + PlotType = PlotType.Polar, + PlotAreaBorderThickness = new OxyThickness(0), + PlotMargins = new OxyThickness(60, 20, 4, 40) + }; + model.Axes.Add( + new AngleAxis + { + Minimum = 0, + Maximum = 360, + MajorStep = 30, + MinorStep = 30, + StartAngle = -90, + EndAngle = 270, + LabelFormatter = angle => + { + if (angle > 0 && angle < 180) + { + return angle + "E"; + } + + if (angle > 180) + { + return (360 - angle) + "W"; + } + + return angle.ToString(); + }, + MajorGridlineStyle = LineStyle.Dot, + MinorGridlineStyle = LineStyle.None + }); + model.Axes.Add(new MagnitudeAxis + { + Minimum = 0, + Maximum = 1, + MajorGridlineStyle = LineStyle.Solid, + MinorGridlineStyle = LineStyle.Solid + }); + model.Series.Add(new FunctionSeries(x => Math.Sin(x / 180 * Math.PI), t => t, 0, 180, 0.01)); + return model; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Axes/RangeColorAxisExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Axes/RangeColorAxisExamples.cs new file mode 100644 index 0000000..2c714fe --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Axes/RangeColorAxisExamples.cs @@ -0,0 +1,84 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("RangeColorAxis"), Tags("Axes")] + public class RangeColorAxisExamples + { + [Example("ScatterSeries with Reversed RangeColorAxis (Horizontal)")] + public static PlotModel ReversedHorizontalRangeColorAxis() + { + return RangeColorAxis(AxisPosition.Top, true); + } + + [Example("ScatterSeries with Reversed RangeColorAxis (Vertical)")] + public static PlotModel ReversedVerticalRangeColorAxis() + { + return RangeColorAxis(AxisPosition.Right, true); + } + + [Example("ScatterSeries with RangeColorAxis (Horizontal)")] + public static PlotModel HorizontalRangeColorAxis() + { + return RangeColorAxis(AxisPosition.Top, false); + } + + [Example("ScatterSeries with RangeColorAxis (Vertical)")] + public static PlotModel VerticalRangeColorAxis() + { + return RangeColorAxis(AxisPosition.Right, false); + } + + private static PlotModel RangeColorAxis(AxisPosition position, bool reverseAxis) + { + int n = 1000; + + string modelTitle = reverseAxis + ? string.Format("ScatterSeries and Reversed RangeColorAxis (n={0})", n) + : string.Format("ScatterSeries and RangeColorAxis (n={0})", n); + + var model = new PlotModel + { + Title = modelTitle, + Background = OxyColors.LightGray + }; + + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + + var rca = new RangeColorAxis { Position = position, Maximum = 2, Minimum = -2 }; + rca.AddRange(0, 0.5, OxyColors.Blue); + rca.AddRange(-0.2, -0.1, OxyColors.Red); + + if (reverseAxis) + { + rca.StartPosition = 1; + rca.EndPosition = 0; + } + + model.Axes.Add(rca); + + var s1 = new ScatterSeries { MarkerType = MarkerType.Square, MarkerSize = 6, }; + + var random = new Random(13); + for (int i = 0; i < n; i++) + { + double x = (random.NextDouble() * 2.2) - 1.1; + s1.Points.Add(new ScatterPoint(x, random.NextDouble()) { Value = x }); + } + + model.Series.Add(s1); + return model; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Axes/TimeSpanAxisExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Axes/TimeSpanAxisExamples.cs new file mode 100644 index 0000000..3111ca7 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Axes/TimeSpanAxisExamples.cs @@ -0,0 +1,74 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + using System.Collections.ObjectModel; + + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("TimeSpanAxis"), Tags("Axes")] + public static class TimeSpanAxisExamples + { + public class TimeValue + { + public TimeSpan Time { get; set; } + public double Value { get; set; } + } + + [Example("Default StringFormat")] + public static PlotModel TimeSpanaxisPlotModelDefault() + { + return TimeSpanaxisPlotModel(null); + } + + [Example("StringFormat = 'h:mm'")] + public static PlotModel TimeSpanaxisPlotModel1() + { + return TimeSpanaxisPlotModel("h:mm"); + } + + private static PlotModel TimeSpanaxisPlotModel(string stringFormat) + { + var start = new TimeSpan(0, 0, 0, 0); + var end = new TimeSpan(0, 24, 0, 0); + double increment = 3600; + + // Create a random data collection + var r = new Random(7); + var data = new Collection(); + var current = start; + while (current <= end) + { + data.Add(new TimeValue { Time = current, Value = r.NextDouble() }); + current = current.Add(new TimeSpan(0, 0, (int)increment)); + } + + var plotModel1 = new PlotModel { Title = "TimeSpan axis" }; + var timeSpanAxis1 = new TimeSpanAxis { Position = AxisPosition.Bottom, StringFormat = stringFormat }; + plotModel1.Axes.Add(timeSpanAxis1); + var linearAxis1 = new LinearAxis { Position = AxisPosition.Left }; + plotModel1.Axes.Add(linearAxis1); + var lineSeries1 = new LineSeries + { + Color = OxyColor.FromArgb(255, 78, 154, 6), + MarkerFill = OxyColor.FromArgb(255, 78, 154, 6), + MarkerStroke = OxyColors.ForestGreen, + MarkerType = MarkerType.Plus, + StrokeThickness = 1, + DataFieldX = "Time", + DataFieldY = "Value", + ItemsSource = data + }; + plotModel1.Series.Add(lineSeries1); + return plotModel1; + } + + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/CustomSeries/CustomSeriesExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/CustomSeries/CustomSeriesExamples.cs new file mode 100644 index 0000000..b92f2c7 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/CustomSeries/CustomSeriesExamples.cs @@ -0,0 +1,259 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("Custom series"), Tags("Series")] + public static class CustomSeriesExamples + { + [Example("ErrorSeries")] + public static PlotModel ErrorSeries() + { + int n = 20; + + var model = new PlotModel { Title = "ErrorSeries", LegendPosition = LegendPosition.BottomRight }; + + var s1 = new ErrorSeries { Title = "Measurements" }; + var random = new Random(31); + double x = 0; + double y = 0; + for (int i = 0; i < n; i++) + { + x += 2 + (random.NextDouble() * 10); + y += 1 + random.NextDouble(); + double xe = 1 + (random.NextDouble() * 2); + double ye = 1 + (random.NextDouble() * 2); + s1.Points.Add(new ErrorItem(x, y, xe, ye)); + } + + model.Series.Add(s1); + return model; + } + + [Example("LineSegmentSeries")] + public static PlotModel LineSegmentSeries() + { + var model = new PlotModel { Title = "LineSegmentSeries" }; + + var lss1 = new LineSegmentSeries { Title = "The first series" }; + + // First segment + lss1.Points.Add(new DataPoint(0, 3)); + lss1.Points.Add(new DataPoint(2, 3.2)); + + // Second segment + lss1.Points.Add(new DataPoint(2, 2.7)); + lss1.Points.Add(new DataPoint(7, 2.9)); + + model.Series.Add(lss1); + + var lss2 = new LineSegmentSeries { Title = "The second series" }; + + // First segment + lss2.Points.Add(new DataPoint(1, -3)); + lss2.Points.Add(new DataPoint(2, 10)); + + // Second segment + lss2.Points.Add(new DataPoint(0, 4.8)); + lss2.Points.Add(new DataPoint(7, 2.3)); + + // A very short segment + lss2.Points.Add(new DataPoint(6, 4)); + lss2.Points.Add(new DataPoint(6, 4 + 1e-8)); + + model.Series.Add(lss2); + + return model; + } + + [Example("FlagSeries")] + public static PlotModel FlagSeries() + { + var model = new PlotModel { Title = "FlagSeries" }; + + var s1 = new FlagSeries { Title = "Incidents", Color = OxyColors.Red }; + s1.Values.Add(2); + s1.Values.Add(3); + s1.Values.Add(5); + s1.Values.Add(7); + s1.Values.Add(11); + s1.Values.Add(13); + s1.Values.Add(17); + s1.Values.Add(19); + + model.Series.Add(s1); + return model; + } + + [Example("MatrixSeries - diagonal matrix")] + public static PlotModel DiagonalMatrix() + { + var model = new PlotModel(); + + var matrix = new double[3, 3]; + matrix[0, 0] = 1; + matrix[1, 1] = 2; + matrix[2, 2] = 3; + + // Reverse the vertical axis + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, StartPosition = 1, EndPosition = 0 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Series.Add(new MatrixSeries { Matrix = matrix, ShowDiagonal = true }); + + return model; + } + + [Example("PolarHeatMap")] + public static PlotModel PolarHeatMap() + { + var model = new PlotModel { Title = "Polar heat map", PlotMargins = new OxyThickness(40, 80, 40, 40), PlotType = PlotType.Polar, PlotAreaBorderThickness = new OxyThickness(0) }; + + var matrix = new double[2, 2]; + matrix[0, 0] = 0; + matrix[0, 1] = 2; + matrix[1, 0] = 1.5; + matrix[1, 1] = 0.2; + + model.Axes.Add(new AngleAxis { StartAngle = 0, EndAngle = 360, Minimum = 0, Maximum = 360, MajorStep = 30, MinorStep = 15 }); + model.Axes.Add(new MagnitudeAxis { Minimum = 0, Maximum = 100, MajorStep = 25, MinorStep = 5 }); + model.Axes.Add(new LinearColorAxis { Position = AxisPosition.Right, Palette = OxyPalettes.Rainbow(500), HighColor = OxyColors.Gray, LowColor = OxyColors.Black }); + model.Series.Add(new PolarHeatMapSeries { Data = matrix, Angle0 = 30, Angle1 = 150, Magnitude0 = 0, Magnitude1 = 80, Interpolate = false }); + + return model; + } + + [Example("PolarHeatMap (interpolated)")] + public static PlotModel PolarHeatMapInterpolated() + { + var model = PolarHeatMap(); + model.Title = "Polar heat map (interpolated)"; + ((PolarHeatMapSeries)model.Series[0]).Interpolate = true; + return model; + } + + [Example("PolarHeatMap fixed size image")] + public static PlotModel PolarHeatMapFixed() + { + var model = PolarHeatMap(); + model.Title = "Polar heat map with fixed size image"; + ((PolarHeatMapSeries)model.Series[0]).ImageSize = 800; + return model; + } + + [Example("PolarHeatMap on linear axes")] + public static PlotModel PolarHeatMapLinearAxes() + { + var model = new PlotModel { Title = "Polar heat map on linear axes" }; + + var matrix = new double[2, 2]; + matrix[0, 0] = 0; + matrix[0, 1] = 2; + matrix[1, 0] = 1.5; + matrix[1, 1] = 0.2; + + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = -100, Maximum = 100 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = 0, Maximum = 100 }); + model.Axes.Add(new LinearColorAxis { Position = AxisPosition.Right, Palette = OxyPalettes.Rainbow(500), HighColor = OxyColors.Gray, LowColor = OxyColors.Black }); + model.Series.Add(new PolarHeatMapSeries { Data = matrix, Angle0 = 30, Angle1 = 150, Magnitude0 = 0, Magnitude1 = 80, Interpolate = true }); + + return model; + } + + [Example("PolarHeatMap linear axes, fixed size image (256x256)")] + public static PlotModel PolarHeatMapLinearAxesFixed256() + { + var model = PolarHeatMapLinearAxes(); + model.Title = "Polar heat map on linear axes & fixed size image (256x256)"; + ((PolarHeatMapSeries)model.Series[0]).ImageSize = 256; + return model; + } + + [Example("PolarHeatMap linear axes, fixed size image (1000x1000)")] + public static PlotModel PolarHeatMapLinearAxesFixed1000() + { + var model = PolarHeatMapLinearAxes(); + model.Title = "Polar heat map on linear axes & fixed size image (1000x1000)"; + ((PolarHeatMapSeries)model.Series[0]).ImageSize = 1000; + return model; + } + + [Example("Design structure matrix (DSM)")] + public static PlotModel DesignStructureMatrix() + { + // See also http://en.wikipedia.org/wiki/Design_structure_matrix + var data = new double[7, 7]; + + // indexing: data[column,row] + data[1, 0] = 1; + data[5, 0] = 1; + data[3, 1] = 1; + data[0, 2] = 1; + data[6, 2] = 1; + data[4, 3] = 1; + data[1, 4] = 1; + data[5, 4] = 1; + data[2, 5] = 1; + data[0, 6] = 1; + data[4, 6] = 1; + + for (int i = 0; i < 7; i++) + { + data[i, i] = -1; + } + + var model = new PlotModel { Title = "Design structure matrix (DSM)" }; + model.Axes.Add(new LinearColorAxis { Position = AxisPosition.None, Palette = new OxyPalette(OxyColors.White, OxyColors.LightGreen), LowColor = OxyColors.Black, Minimum = 0, IsAxisVisible = false }); + var topAxis = new CategoryAxis + { + Position = AxisPosition.Top + }; + topAxis.Labels.AddRange(new[] { "A", "B", "C", "D", "E", "F", "G" }); + model.Axes.Add(topAxis); + var leftAxis = new CategoryAxis + { + Position = AxisPosition.Left, + StartPosition = 1, + EndPosition = 0 + }; + leftAxis.Labels.AddRange(new[] { "Element A", "Element B", "Element C", "Element D", "Element E", "Element F", "Element G" }); + model.Axes.Add(leftAxis); + + var hms = new DesignStructureMatrixSeries + { + Data = data, + Interpolate = false, + LabelFormatString = "#", + LabelFontSize = 0.25, + X0 = 0, + X1 = data.GetLength(0) - 1, + Y0 = 0, + Y1 = data.GetLength(1) - 1, + }; + + model.Series.Add(hms); + return model; + } + } + + public class DesignStructureMatrixSeries : HeatMapSeries + { + protected override string GetLabel(double v, int i, int j) + { + if (i == j) + { + return ((CategoryAxis)this.XAxis).Labels[i]; + } + + return base.GetLabel(v, i, j); + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/CustomSeries/ErrorItem.cs b/OxyPlot/Source/Examples/ExampleLibrary/CustomSeries/ErrorItem.cs new file mode 100644 index 0000000..2997190 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/CustomSeries/ErrorItem.cs @@ -0,0 +1,70 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Represents an error item. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using OxyPlot; + + /// + /// Represents an error item. + /// + public class ErrorItem + { + /// + /// Initializes a new instance of the class. + /// + public ErrorItem() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The x. + /// The y. + /// The xerror. + /// The yerror. + public ErrorItem(double x, double y, double xerror, double yerror) + { + this.X = x; + this.Y = y; + this.XError = xerror; + this.YError = yerror; + } + + /// + /// Gets or sets the X. + /// + public double X { get; set; } + + /// + /// Gets or sets the Y. + /// + public double Y { get; set; } + + /// + /// Gets or sets the X error. + /// + public double XError { get; set; } + + /// + /// Gets or sets the Y error. + /// + public double YError { get; set; } + + /// + /// Returns c# code that generates this instance. + /// + /// C# code. + public string ToCode() + { + return CodeGenerator.FormatConstructor(this.GetType(), "{0},{1},{2},{3}", this.X, this.Y, this.XError, this.YError); + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/CustomSeries/ErrorSeries.cs b/OxyPlot/Source/Examples/ExampleLibrary/CustomSeries/ErrorSeries.cs new file mode 100644 index 0000000..a56a449 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/CustomSeries/ErrorSeries.cs @@ -0,0 +1,166 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Represents an error series. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + using System.Collections.Generic; + + using OxyPlot; + using OxyPlot.Series; + + /// + /// Represents an error series. + /// + public class ErrorSeries : XYAxisSeries + { + /// + /// The list of error items. + /// + private readonly List points = new List(); + + /// + /// Initializes a new instance of the class. + /// + public ErrorSeries() + { + this.Color = OxyColors.Black; + this.StrokeThickness = 1; + } + + /// + /// Gets or sets the color. + /// + /// The color. + public OxyColor Color { get; set; } + + /// + /// Gets the list of points. + /// + /// A list of . + public List Points + { + get + { + return this.points; + } + } + + /// + /// Gets or sets the stroke thickness. + /// + /// The stroke thickness. + public double StrokeThickness { get; set; } + + /// + /// Renders the series on the specified render context. + /// + /// The rendering context. + public override void Render(IRenderContext rc) + { + var points = this.Points; + if (points.Count == 0) + { + return; + } + + this.VerifyAxes(); + + var clippingRect = GetClippingRect(); + + int n = points.Count; + + // Transform all points to screen coordinates + var segments = new List(n * 6); + for (int i = 0; i < n; i++) + { + var sp = XAxis.Transform(points[i].X, points[i].Y, YAxis); + var ei = points[i]; + double errorx = ei != null ? ei.XError * XAxis.Scale : 0; + double errory = ei != null ? ei.YError * Math.Abs(YAxis.Scale) : 0; + double d = 4; + + if (errorx > 0) + { + var p0 = new ScreenPoint(sp.X - (errorx * 0.5), sp.Y); + var p1 = new ScreenPoint(sp.X + (errorx * 0.5), sp.Y); + segments.Add(p0); + segments.Add(p1); + segments.Add(new ScreenPoint(p0.X, p0.Y - d)); + segments.Add(new ScreenPoint(p0.X, p0.Y + d)); + segments.Add(new ScreenPoint(p1.X, p1.Y - d)); + segments.Add(new ScreenPoint(p1.X, p1.Y + d)); + } + + if (errory > 0) + { + var p0 = new ScreenPoint(sp.X, sp.Y - (errory * 0.5)); + var p1 = new ScreenPoint(sp.X, sp.Y + (errory * 0.5)); + segments.Add(p0); + segments.Add(p1); + segments.Add(new ScreenPoint(p0.X - d, p0.Y)); + segments.Add(new ScreenPoint(p0.X + d, p0.Y)); + segments.Add(new ScreenPoint(p1.X - d, p1.Y)); + segments.Add(new ScreenPoint(p1.X + d, p1.Y)); + } + } + + // clip the line segments with the clipping rectangle + for (int i = 0; i + 1 < segments.Count; i += 2) + { + rc.DrawClippedLine( + clippingRect, + new[] { segments[i], segments[i + 1] }, + 2, + this.GetSelectableColor(this.Color), + this.StrokeThickness, + null, + LineJoin.Bevel, + true); + } + } + + /// + /// Renders the legend symbol on the specified rendering context. + /// + /// The rendering context. + /// The legend rectangle. + public override void RenderLegend(IRenderContext rc, OxyRect legendBox) + { + double xmid = (legendBox.Left + legendBox.Right) * 0.5; + double ymid = (legendBox.Top + legendBox.Bottom) * 0.5; + var pts = new[] + { + new ScreenPoint(legendBox.Left, ymid), + new ScreenPoint(legendBox.Right, ymid), + new ScreenPoint(legendBox.Left, ymid - 2), + new ScreenPoint(legendBox.Left, ymid + 3), + new ScreenPoint(legendBox.Right, ymid - 2), + new ScreenPoint(legendBox.Right, ymid + 3), + + new ScreenPoint(xmid, legendBox.Top), + new ScreenPoint(xmid, legendBox.Bottom), + new ScreenPoint(xmid - 2, legendBox.Top), + new ScreenPoint(xmid + 3, legendBox.Top), + new ScreenPoint(xmid - 2, legendBox.Bottom), + new ScreenPoint(xmid + 3, legendBox.Bottom) + }; + rc.DrawLineSegments(pts, this.GetSelectableColor(this.Color), this.StrokeThickness, null, LineJoin.Miter, true); + } + + /// + /// Updates the maximum and minimum values of the series. + /// + protected override void UpdateMaxMin() + { + base.UpdateMaxMin(); + this.InternalUpdateMaxMin(this.points, p => p.X - p.XError, p => p.X + p.XError, p => p.Y - p.YError, p => p.Y + p.YError); + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/CustomSeries/FlagSeries.cs b/OxyPlot/Source/Examples/ExampleLibrary/CustomSeries/FlagSeries.cs new file mode 100644 index 0000000..06668f0 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/CustomSeries/FlagSeries.cs @@ -0,0 +1,234 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Renders a 'flag' above the x-axis at the specified positions (in the Values list). +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System.Collections.Generic; + using System.Linq; + + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + + /// + /// Renders a 'flag' above the x-axis at the specified positions (in the Values list). + /// + public class FlagSeries : ItemsSeries + { + /// + /// The symbol position (y coordinate). + /// + private double symbolPosition; + + /// + /// The symbol text size. + /// + private OxySize symbolSize; + + /// + /// Initializes a new instance of the class. + /// + public FlagSeries() + { + this.Values = new List(); + this.Color = OxyColors.Black; + this.FontSize = 10; + this.Symbol = ((char)0xEA).ToString(); + this.Font = "Wingdings 2"; + this.TrackerFormatString = "{0}: {1}"; + } + + /// + /// Gets or sets the color of the symbols. + /// + /// The color. + public OxyColor Color { get; set; } + + /// + /// Gets the maximum value. + /// + /// The maximum value. + public double MaximumX { get; private set; } + + /// + /// Gets the minimum value. + /// + /// The minimum value. + public double MinimumX { get; private set; } + + /// + /// Gets or sets the symbol to draw at each value. + /// + /// The symbol. + public string Symbol { get; set; } + + /// + /// Gets the values. + /// + /// The values. + public List Values { get; private set; } + + /// + /// Gets the x-axis. + /// + /// The x-axis. + public Axis XAxis { get; private set; } + + /// + /// Gets or sets the x-axis key. + /// + /// The x-axis key. + public string XAxisKey { get; set; } + + /// + /// Gets the point on the series that is nearest the specified point. + /// + /// The point. + /// Interpolate the series if this flag is set to true. + /// A TrackerHitResult for the current hit. + public override TrackerHitResult GetNearestPoint(ScreenPoint point, bool interpolate) + { + foreach (var v in this.Values) + { + if (double.IsNaN(v) || v < this.XAxis.ActualMinimum || v > this.XAxis.ActualMaximum) + { + continue; + } + + double x = this.XAxis.Transform(v); + var r = new OxyRect(x - (this.symbolSize.Width / 2), this.symbolPosition - this.symbolSize.Height, this.symbolSize.Width, this.symbolSize.Height); + if (r.Contains(point)) + { + return new TrackerHitResult + { + Series = this, + DataPoint = new DataPoint(v, double.NaN), + Position = new ScreenPoint(x, this.symbolPosition - this.symbolSize.Height), + Text = StringHelper.Format(this.ActualCulture, this.TrackerFormatString, null, this.Title, v) + }; + } + } + + return null; + } + + /// + /// Renders the series on the specified render context. + /// + /// The rendering context. + public override void Render(IRenderContext rc) + { + if (this.XAxis == null) + { + return; + } + + this.symbolPosition = this.PlotModel.PlotArea.Bottom; + this.symbolSize = rc.MeasureText(this.Symbol, this.ActualFont, this.ActualFontSize); + foreach (var v in this.Values) + { + if (double.IsNaN(v) || v < this.XAxis.ActualMinimum || v > this.XAxis.ActualMaximum) + { + continue; + } + + double x = this.XAxis.Transform(v); + rc.DrawText( + new ScreenPoint(x, this.symbolPosition), + this.Symbol, + this.Color, + this.ActualFont, + this.ActualFontSize, + this.ActualFontWeight, + 0, + HorizontalAlignment.Center, + VerticalAlignment.Bottom); + } + } + + /// + /// Renders the legend symbol on the specified render context. + /// + /// The rendering context. + /// The legend rectangle. + public override void RenderLegend(IRenderContext rc, OxyRect legendBox) + { + rc.DrawText( + legendBox.Center, + this.Symbol, + this.Color, + this.ActualFont, + this.ActualFontSize, + this.ActualFontWeight, + 0, + HorizontalAlignment.Center, + VerticalAlignment.Middle); + } + + /// + /// Check if this data series requires X/Y axes. (e.g. Pie series do not require axes) + /// + /// True if no axes are required. + protected override bool AreAxesRequired() + { + return true; + } + + /// + /// Ensures that the axes of the series is defined. + /// + protected override void EnsureAxes() + { + this.XAxis = this.PlotModel.GetAxisOrDefault(this.XAxisKey, this.PlotModel.DefaultXAxis); + } + + /// + /// Check if the data series is using the specified axis. + /// + /// An axis which should be checked if used + /// True if the axis is in use. + protected override bool IsUsing(Axis axis) + { + return axis == this.XAxis; + } + + /// + /// Sets default values (colors, line style etc) from the plot model. + /// + protected override void SetDefaultValues() + { + } + + /// + /// Updates the axis maximum and minimum values. + /// + protected override void UpdateAxisMaxMin() + { + this.XAxis.Include(this.MinimumX); + this.XAxis.Include(this.MaximumX); + } + + /// + /// Updates the data from the ItemsSource. + /// + protected override void UpdateData() + { + // todo + } + + /// + /// Updates the maximum and minimum values of the series. + /// + protected override void UpdateMaxMin() + { + this.MinimumX = this.Values.Min(); + this.MaximumX = this.Values.Max(); + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/CustomSeries/LineSegmentSeries.cs b/OxyPlot/Source/Examples/ExampleLibrary/CustomSeries/LineSegmentSeries.cs new file mode 100644 index 0000000..e12856e --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/CustomSeries/LineSegmentSeries.cs @@ -0,0 +1,160 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Represents a line series where the points collection define line segments. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + using System.Collections.Generic; + using System.Linq; + using OxyPlot; + using OxyPlot.Series; + + /// + /// Represents a line series where the points collection define line segments. + /// + public class LineSegmentSeries : LineSeries + { + /// + /// Initializes a new instance of the class. + /// + public LineSegmentSeries() + { + this.ShowVerticals = true; + this.Epsilon = 1e-8; + } + + /// + /// Gets or sets a value indicating whether to show vertical lines where there is no gap in x-coordinate. + /// + /// true if verticals should be shown; otherwise, false. + public bool ShowVerticals { get; set; } + + /// + /// Gets or sets the x-coordinate gap tolerance. + /// + /// The epsilon value. + public double Epsilon { get; set; } + + /// + /// Renders the series on the specified rendering context. + /// + /// The rendering context. + public override void Render(IRenderContext rc) + { + if (Points.Count == 0) + { + return; + } + + if (Points.Count % 2 != 0) + { + throw new InvalidOperationException("The number of points should be even."); + } + + if (this.XAxis == null || this.YAxis == null) + { + throw new InvalidOperationException("Axis has not been defined."); + } + + var clippingRect = GetClippingRect(); + + var screenPoints = Points.Select(this.Transform).ToList(); + var verticalLines = new List(); + + for (int i = 0; i < screenPoints.Count; i += 2) + { + if (screenPoints[i].DistanceToSquared(screenPoints[i + 1]) < this.StrokeThickness) + { + screenPoints[i] = new ScreenPoint(screenPoints[i].X - (this.StrokeThickness * 0.5), screenPoints[i].Y); + screenPoints[i + 1] = new ScreenPoint(screenPoints[i].X + (this.StrokeThickness * 0.5), screenPoints[i].Y); + } + + if (this.ShowVerticals && i > 0 && Math.Abs(screenPoints[i - 1].X - screenPoints[i].X) < this.Epsilon) + { + verticalLines.Add(screenPoints[i - 1]); + verticalLines.Add(screenPoints[i]); + } + } + + rc.DrawClippedLineSegments(clippingRect, screenPoints, this.ActualColor, this.StrokeThickness, this.LineStyle.GetDashArray(), this.LineJoin, false); + rc.DrawClippedLineSegments(clippingRect, verticalLines, this.ActualColor, this.StrokeThickness / 3, LineStyle.Dash.GetDashArray(), this.LineJoin, false); + + rc.DrawMarkers(screenPoints, clippingRect, this.MarkerType, null, this.MarkerSize, this.MarkerFill, this.MarkerStroke, this.MarkerStrokeThickness); + } + + /// + /// Gets the point on the series that is nearest the specified point. + /// + /// The point. + /// Interpolate the series if this flag is set to true. + /// A TrackerHitResult for the current hit. + public override TrackerHitResult GetNearestPoint(ScreenPoint point, bool interpolate) + { + var points = this.Points; + + if (points == null) + { + return null; + } + + var spn = default(ScreenPoint); + var dpn = default(DataPoint); + double index = -1; + + double minimumDistance = double.MaxValue; + + for (int i = 0; i + 1 < points.Count; i += 2) + { + var p1 = points[i]; + var p2 = points[i + 1]; + if (!this.IsValidPoint(p1) || !this.IsValidPoint(p2)) + { + continue; + } + + var sp1 = this.Transform(p1); + var sp2 = this.Transform(p2); + + // Find the nearest point on the line segment. + var spl = ScreenPointHelper.FindPointOnLine(point, sp1, sp2); + + if (ScreenPoint.IsUndefined(spl)) + { + // P1 && P2 coincident + continue; + } + + double l2 = (point - spl).LengthSquared; + + if (l2 < minimumDistance) + { + double u = (spl - sp1).Length / (sp2 - sp1).Length; + dpn = new DataPoint(p1.X + (u * (p2.X - p1.X)), p1.Y + (u * (p2.Y - p1.Y))); + spn = spl; + minimumDistance = l2; + index = i + u; + } + } + + if (minimumDistance < double.MaxValue) + { + return new TrackerHitResult + { + Series = this, + DataPoint = dpn, + Position = spn, + Item = this.GetItem((int)index), + Index = index + }; + } + + return null; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/CustomSeries/MatrixSeries.cs b/OxyPlot/Source/Examples/ExampleLibrary/CustomSeries/MatrixSeries.cs new file mode 100644 index 0000000..8df8f15 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/CustomSeries/MatrixSeries.cs @@ -0,0 +1,237 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Provides a series that visualizes the structure of a matrix. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + using System.Collections.Generic; + + using OxyPlot; + using OxyPlot.Series; + + /// + /// Provides a series that visualizes the structure of a matrix. + /// + public class MatrixSeries : XYAxisSeries + { + /// + /// The image + /// + private OxyImage image; + + /// + /// The matrix + /// + private double[,] matrix; + + /// + /// Initializes a new instance of the class. + /// + public MatrixSeries() + { + this.GridInterval = 1; + this.ShowDiagonal = false; + this.MinimumGridLineDistance = 4; + this.GridColor = OxyColors.LightGray; + this.BorderColor = OxyColors.Gray; + this.NotZeroColor = OxyColors.Black; + this.ZeroTolerance = 0; + this.TrackerFormatString = "{0}\r\n[{1},{2}] = {3}"; + } + + /// + /// Gets or sets the matrix. + /// + public double[,] Matrix + { + get + { + return this.matrix; + } + + set + { + this.image = null; + this.matrix = value; + } + } + + /// + /// Gets or sets the interval between the grid lines (the grid is hidden if value is 0). + /// + public int GridInterval { get; set; } + + /// + /// Gets or sets a value indicating whether to show the diagonal. + /// + /// true if the diagonal should be shown; otherwise, false. + public bool ShowDiagonal { get; set; } + + /// + /// Gets or sets the minimum grid line distance. + /// + public double MinimumGridLineDistance { get; set; } + + /// + /// Gets or sets the color of the grid. + /// + /// The color of the grid. + public OxyColor GridColor { get; set; } + + /// + /// Gets or sets the color of the border around the matrix. + /// + /// The color of the border. + public OxyColor BorderColor { get; set; } + + /// + /// Gets or sets the color of the not zero elements of the matrix. + /// + public OxyColor NotZeroColor { get; set; } + + /// + /// Gets or sets the zero tolerance (inclusive). + /// + /// The zero tolerance. + public double ZeroTolerance { get; set; } + + /// + /// Gets the point on the series that is nearest the specified point. + /// + /// The point. + /// Interpolate the series if this flag is set to true. + /// A TrackerHitResult for the current hit. + public override TrackerHitResult GetNearestPoint(ScreenPoint point, bool interpolate) + { + var dp = this.InverseTransform(point); + int i = (int)dp.Y; + int j = (int)dp.X; + + if (i >= 0 && i < this.matrix.GetLength(0) && j >= 0 && j < this.matrix.GetLength(1)) + { + var value = this.matrix[i, j]; + return new TrackerHitResult + { + Series = this, + DataPoint = dp, + Position = point, + Item = null, + Index = -1, + Text = StringHelper.Format(this.ActualCulture, this.TrackerFormatString, null, this.Title, i, j, value) + }; + } + + return null; + } + + /// + /// Renders the series on the specified render context. + /// + /// The rendering context. + public override void Render(IRenderContext rc) + { + if (this.Matrix == null) + { + return; + } + + int m = this.Matrix.GetLength(0); + int n = this.Matrix.GetLength(1); + var p0 = this.Transform(0, 0); + var p1 = this.Transform(n, m); + + // note matrix index [i,j] maps to image index [j,i] + if (this.image == null) + { + var pixels = new OxyColor[n, m]; + for (int i = 0; i < m; i++) + { + for (int j = 0; j < n; j++) + { + pixels[j, i] = Math.Abs(this.Matrix[i, j]) <= this.ZeroTolerance ? OxyColors.Transparent : this.NotZeroColor; + } + } + + this.image = OxyImage.Create(pixels, ImageFormat.Png); + } + + var clip = this.GetClippingRect(); + var x0 = Math.Min(p0.X, p1.X); + var y0 = Math.Min(p0.Y, p1.Y); + var w = Math.Abs(p0.X - p1.X); + var h = Math.Abs(p0.Y - p1.Y); + rc.DrawClippedImage(clip, this.image, x0, y0, w, h, 1, false); + + var points = new List(); + if (this.GridInterval > 0) + { + var p2 = this.Transform(this.GridInterval, this.GridInterval); + if (Math.Abs(p2.Y - p0.Y) > this.MinimumGridLineDistance) + { + for (int i = 1; i < n; i += this.GridInterval) + { + points.Add(this.Transform(0, i)); + points.Add(this.Transform(n, i)); + } + } + + if (Math.Abs(p2.X - p0.X) > this.MinimumGridLineDistance) + { + for (int j = 1; j < m; j += this.GridInterval) + { + points.Add(this.Transform(j, 0)); + points.Add(this.Transform(j, m)); + } + } + } + + if (this.ShowDiagonal) + { + points.Add(this.Transform(0, 0)); + points.Add(this.Transform(n, m)); + } + + rc.DrawClippedLineSegments(clip, points, this.GridColor, 1, null, LineJoin.Miter, true); + + if (this.BorderColor.IsVisible()) + { + var borderPoints = new[] + { + this.Transform(0, 0), + this.Transform(m, 0), + this.Transform(0, n), + this.Transform(m, n), + this.Transform(0, 0), + this.Transform(0, n), + this.Transform(m, 0), + this.Transform(m, n) + }; + + rc.DrawClippedLineSegments(clip, borderPoints, this.BorderColor, 1, null, LineJoin.Miter, true); + } + } + + /// + /// Updates the maximum and minimum values of the series. + /// + protected override void UpdateMaxMin() + { + base.UpdateMaxMin(); + if (this.Matrix == null) + { + return; + } + + this.MinX = 0; + this.MaxX = this.Matrix.GetLength(1); + this.MinY = 0; + this.MaxY = this.Matrix.GetLength(0); + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/CustomSeries/PolarHeatMapSeries.cs b/OxyPlot/Source/Examples/ExampleLibrary/CustomSeries/PolarHeatMapSeries.cs new file mode 100644 index 0000000..df5b048 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/CustomSeries/PolarHeatMapSeries.cs @@ -0,0 +1,389 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Implements a polar heat map series. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace OxyPlot.Series +{ + using System; + using System.Collections.Generic; + using System.Linq; + + using OxyPlot.Axes; + + /// + /// Implements a polar heat map series. + /// + public class PolarHeatMapSeries : XYAxisSeries + { + /// + /// The image + /// + private OxyImage image; + + /// + /// The pixels + /// + private OxyColor[,] pixels; + + /// + /// Initializes a new instance of the class. + /// + public PolarHeatMapSeries() + { + this.Interpolate = true; + } + + /// + /// Gets or sets the size of the image - if set to 0, the image will be generated at every update. + /// + /// The size of the image. + public int ImageSize { get; set; } + + /// + /// Gets or sets the x-coordinate of the left column mid point. + /// + public double Angle0 { get; set; } + + /// + /// Gets or sets the x-coordinate of the right column mid point. + /// + public double Angle1 { get; set; } + + /// + /// Gets or sets the y-coordinate of the top row mid point. + /// + public double Magnitude0 { get; set; } + + /// + /// Gets or sets the y-coordinate of the bottom row mid point. + /// + public double Magnitude1 { get; set; } + + /// + /// Gets or sets the data array. + /// + /// Note that the indices of the data array refer to [x,y]. + public double[,] Data { get; set; } + + /// + /// Gets or sets a value indicating whether to interpolate when rendering. + /// + /// This property is not supported on all platforms. + public bool Interpolate { get; set; } + + /// + /// Gets or sets the minimum value of the dataset. + /// + public double MinValue { get; protected set; } + + /// + /// Gets or sets the maximum value of the dataset. + /// + public double MaxValue { get; protected set; } + + /// + /// Gets or sets the color axis. + /// + /// The color axis. + public IColorAxis ColorAxis { get; protected set; } + + /// + /// Gets or sets the color axis key. + /// + /// The color axis key. + public string ColorAxisKey { get; set; } + + /// + /// Renders the series on the specified render context. + /// + /// The rendering context. + public override void Render(IRenderContext rc) + { + if (this.Data == null) + { + this.image = null; + return; + } + + if (this.ImageSize > 0) + { + this.RenderFixed(rc, this.PlotModel); + } + else + { + this.RenderDynamic(rc, this.PlotModel); + } + } + + /// + /// Renders by an image sized from the available plot area. + /// + /// The rc. + /// The model. + public void RenderDynamic(IRenderContext rc, PlotModel model) + { + int m = this.Data.GetLength(0); + int n = this.Data.GetLength(1); + + // get the available plot area + var dest = model.PlotArea; + int width = (int)dest.Width; + int height = (int)dest.Height; + if (width == 0 || height == 0) + { + return; + } + + if (this.pixels == null || this.pixels.GetLength(0) != height || this.pixels.GetLength(1) != width) + { + this.pixels = new OxyColor[width, height]; + } + + var p = this.pixels; + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + // transform from screen to magnitude/angle + var sp = new ScreenPoint(dest.Left + x, dest.Top + y); + var xy = this.InverseTransform(sp); + double angle; + double magnitude; + if (this.PlotModel.PlotType != PlotType.Polar) + { + angle = Math.Atan2(xy.Y, xy.X) / Math.PI * 180; + magnitude = Math.Sqrt((xy.X * xy.X) + (xy.Y * xy.Y)); + } + else + { + angle = xy.Y / Math.PI * 180; + magnitude = xy.X; + if (angle < 0) + { + angle += 360; + } + } + + // transform to indices in the Data array + var ii = (angle - this.Angle0) / (this.Angle1 - this.Angle0) * m; + var jj = (magnitude - this.Magnitude0) / (this.Magnitude1 - this.Magnitude0) * n; + if (ii >= 0 && ii < m && jj >= 0 && jj < n) + { + // get the (interpolated) value + var value = this.GetValue(ii, jj); + + // use the color axis to get the color + p[x, y] = OxyColor.FromAColor(160, this.ColorAxis.GetColor(value)); + } + else + { + // outside the range of the Data array + p[x, y] = OxyColors.Transparent; + } + } + } + + // Create the PNG image + this.image = OxyImage.Create(p, ImageFormat.Png); + + // Render the image + var clip = this.GetClippingRect(); + rc.DrawClippedImage(clip, this.image, dest.Left, dest.Top, dest.Width, dest.Height, 1, false); + } + + /// + /// Refreshes the image next time the series is rendered. + /// + public void Refresh() + { + this.image = null; + } + + /// + /// Renders by scaling a fixed image. + /// + /// The render context. + /// The model. + public void RenderFixed(IRenderContext rc, PlotModel model) + { + if (image == null) + { + int m = this.Data.GetLength(0); + int n = this.Data.GetLength(1); + + int width = this.ImageSize; + int height = this.ImageSize; + if (this.pixels == null || this.pixels.GetLength(0) != height || this.pixels.GetLength(1) != width) + { + this.pixels = new OxyColor[width, height]; + } + + var p = this.pixels; + for (int yi = 0; yi < height; yi++) + { + for (int xi = 0; xi < width; xi++) + { + double x = (xi - width * 0.5) / (width * 0.5) * this.Magnitude1; + double y = -(yi - height * 0.5) / (height * 0.5) * this.Magnitude1; + + double angle = Math.Atan2(y, x) / Math.PI * 180; + double magnitude = Math.Sqrt(x * x + y * y); + + if (angle < 0) + { + angle += 360; + } + + // transform to indices in the Data array + var ii = (angle - this.Angle0) / (this.Angle1 - this.Angle0) * m; + var jj = (magnitude - this.Magnitude0) / (this.Magnitude1 - this.Magnitude0) * n; + if (ii >= 0 && ii < m && jj >= 0 && jj < n) + { + // get the (interpolated) value + var value = this.GetValue(ii, jj); + + // use the color axis to get the color + p[xi, yi] = OxyColor.FromAColor(160, this.ColorAxis.GetColor(value)); + } + else + { + // outside the range of the Data array + p[xi, yi] = OxyColors.Transparent; + } + } + } + + // Create the PNG image + this.image = OxyImage.Create(p, ImageFormat.Png); + } + + OxyRect dest; + if (this.PlotModel.PlotType != PlotType.Polar) + { + var topleft = this.Transform(-this.Magnitude1, this.Magnitude1); + var bottomright = this.Transform(this.Magnitude1, -this.Magnitude1); + dest = new OxyRect(topleft.X, topleft.Y, bottomright.X - topleft.X, bottomright.Y - topleft.Y); + } + else + { + var top = this.Transform(this.Magnitude1, 90); + var bottom = this.Transform(this.Magnitude1, 270); + var left = this.Transform(this.Magnitude1, 180); + var right = this.Transform(this.Magnitude1, 0); + dest = new OxyRect(left.X, top.Y, right.X - left.X, bottom.Y - top.Y); + } + + // Render the image + var clip = this.GetClippingRect(); + rc.DrawClippedImage(clip, this.image, dest.Left, dest.Top, dest.Width, dest.Height, 1, false); + } + + /// + /// Gets the value at the specified data indices. + /// + /// The first index in the Data array. + /// The second index in the Data array. + /// The value. + protected virtual double GetValue(double ii, double jj) + { + if (!this.Interpolate) + { + var i = (int)Math.Floor(ii); + var j = (int)Math.Floor(jj); + return this.Data[i, j]; + } + + ii -= 0.5; + jj -= 0.5; + + // bi-linear interpolation http://en.wikipedia.org/wiki/Bilinear_interpolation + var r = (int)Math.Floor(ii); + var c = (int)Math.Floor(jj); + + int r0 = r > 0 ? r : 0; + int r1 = r + 1 < this.Data.GetLength(0) ? r + 1 : r; + int c0 = c > 0 ? c : 0; + int c1 = c + 1 < this.Data.GetLength(1) ? c + 1 : c; + + double v00 = this.Data[r0, c0]; + double v01 = this.Data[r0, c1]; + double v10 = this.Data[r1, c0]; + double v11 = this.Data[r1, c1]; + + double di = ii - r; + double dj = jj - c; + + double v0 = (v00 * (1 - dj)) + (v01 * dj); + double v1 = (v10 * (1 - dj)) + (v11 * dj); + + return (v0 * (1 - di)) + (v1 * di); + } + + /// + /// Gets the point on the series that is nearest the specified point. + /// + /// The point. + /// Interpolate the series if this flag is set to true. + /// A TrackerHitResult for the current hit. + public override TrackerHitResult GetNearestPoint(ScreenPoint point, bool interpolate) + { + return null; + } + + /// + /// Ensures that the axes of the series is defined. + /// + protected override void EnsureAxes() + { + base.EnsureAxes(); + + this.ColorAxis = this.PlotModel.GetAxisOrDefault(this.ColorAxisKey, (Axis)this.PlotModel.DefaultColorAxis) as IColorAxis; + } + + /// + /// Updates the maximum and minimum values of the series. + /// + protected override void UpdateMaxMin() + { + base.UpdateMaxMin(); + + this.MinValue = this.GetData().Min(); + this.MaxValue = this.GetData().Max(); + + //this.XAxis.Include(this.MinX); + //this.XAxis.Include(this.MaxX); + + //this.YAxis.Include(this.MinY); + //this.YAxis.Include(this.MaxY); + + var colorAxis = this.ColorAxis as Axis; + if (colorAxis != null) + { + colorAxis.Include(this.MinValue); + colorAxis.Include(this.MaxValue); + } + } + + /// + /// Gets the data as a sequence (LINQ-friendly). + /// + /// The sequence of data. + protected IEnumerable GetData() + { + int m = this.Data.GetLength(0); + int n = this.Data.GetLength(1); + for (int i = 0; i < m; i++) + { + for (int j = 0; j < n; j++) + { + yield return this.Data[i, j]; + } + } + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Discussions/DiscussionExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Discussions/DiscussionExamples.cs new file mode 100644 index 0000000..b55d831 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Discussions/DiscussionExamples.cs @@ -0,0 +1,366 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + using System.Globalization; + using System.Linq; + + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("Z0 Discussions")] + public class DiscussionExamples + { + [Example("#445576: Invisible contour series")] + public static PlotModel InvisibleContourSeries() + { + var model = new PlotModel { Title = "Invisible contour series" }; + var cs = new ContourSeries + { + IsVisible = false, + ColumnCoordinates = ArrayBuilder.CreateVector(-1, 1, 0.05), + RowCoordinates = ArrayBuilder.CreateVector(-1, 1, 0.05) + }; + cs.Data = ArrayBuilder.Evaluate((x, y) => x + y, cs.ColumnCoordinates, cs.RowCoordinates); + model.Series.Add(cs); + return model; + } + + [Example("#461507: StairStepSeries NullReferenceException")] + public static PlotModel StairStepSeries_NullReferenceException() + { + var plotModel1 = new PlotModel { Title = "StairStepSeries NullReferenceException" }; + plotModel1.Series.Add(new StairStepSeries()); + return plotModel1; + } + + [Example("#501409: Heatmap interpolation color")] + public static PlotModel HeatMapSeriesInterpolationColor() + { + var data = new double[2, 3]; + data[0, 0] = 10; + data[0, 1] = 0; + data[0, 2] = -10; + + var model = new PlotModel { Title = "HeatMapSeries" }; + model.Axes.Add(new LinearColorAxis { Position = AxisPosition.Right, Palette = new OxyPalette(OxyColors.Red, OxyColors.Green, OxyColors.Blue) }); + + var hms = new HeatMapSeries + { + CoordinateDefinition = HeatMapCoordinateDefinition.Center, + X0 = 0, + X1 = 3, + Y0 = 0, + Y1 = 2, + Data = data, + Interpolate = false, + LabelFontSize = 0.2 + }; + model.Series.Add(hms); + return model; + } + + [Example("#522598: Peaks 400x400")] + public static PlotModel Peaks400() + { + return HeatMapSeriesExamples.CreatePeaks(null, true, 400); + } + + [Example("#474875: Updating HeatMapSeries 1")] + public static PlotModel UpdatingHeatMapSeries1() + { + var model = HeatMapSeriesExamples.CreatePeaks(); + model.Title = "Updating HeatMapSeries"; + model.Subtitle = "Click the heat map to change the Maximum of the color axis."; + var lca = (LinearColorAxis)model.Axes[0]; + var hms = (HeatMapSeries)model.Series[0]; + hms.MouseDown += (s, e) => + { + lca.Maximum = Double.IsNaN(lca.Maximum) ? 10 : Double.NaN; + model.InvalidatePlot(true); + }; + return model; + } + + [Example("#474875: Updating HeatMapSeries 2")] + public static PlotModel UpdatingHeatMapSeries() + { + var model = HeatMapSeriesExamples.CreatePeaks(); + model.Title = "Updating HeatMapSeries"; + model.Subtitle = "Click the heat map to change the Maximum of the color axis and invoke the Invalidate method on the HeatMapSeries."; + var lca = (LinearColorAxis)model.Axes[0]; + var hms = (HeatMapSeries)model.Series[0]; + hms.MouseDown += (s, e) => + { + lca.Maximum = Double.IsNaN(lca.Maximum) ? 10 : Double.NaN; + hms.Invalidate(); + model.InvalidatePlot(true); + }; + return model; + } + + [Example("#539104: Reduced color saturation")] + public static PlotModel ReducedColorSaturation() + { + var model = new PlotModel + { + Title = "Reduced color saturation", + }; + + model.Axes.Add(new CategoryAxis { Position = AxisPosition.Bottom }); + + // modify the saturation of the default colors + model.DefaultColors = model.DefaultColors.Select(c => c.ChangeSaturation(0.5)).ToList(); + + var r = new Random(37); + for (var i = 0; i < model.DefaultColors.Count; i++) + { + var columnSeries = new ColumnSeries(); + columnSeries.Items.Add(new ColumnItem(50 + r.Next(50))); + columnSeries.Items.Add(new ColumnItem(40 + r.Next(50))); + model.Series.Add(columnSeries); + } + + return model; + } + + [Example("#539104: Medium intensity colors")] + public static PlotModel MediumIntensityColors() + { + var model = new PlotModel + { + Title = "Medium intensity colors", + }; + + model.Axes.Add(new CategoryAxis { Position = AxisPosition.Bottom }); + + // See http://www.perceptualedge.com/articles/visual_business_intelligence/rules_for_using_color.pdf + model.DefaultColors = new[] + { + OxyColor.FromRgb(114, 114, 114), + OxyColor.FromRgb(241, 89, 95), + OxyColor.FromRgb(121, 195, 106), + OxyColor.FromRgb(89, 154, 211), + OxyColor.FromRgb(249, 166, 90), + OxyColor.FromRgb(158, 102, 171), + OxyColor.FromRgb(205, 112, 88), + OxyColor.FromRgb(215, 127, 179) + }; + + var r = new Random(37); + for (var i = 0; i < model.DefaultColors.Count; i++) + { + var columnSeries = new ColumnSeries(); + columnSeries.Items.Add(new ColumnItem(50 + r.Next(50))); + columnSeries.Items.Add(new ColumnItem(40 + r.Next(50))); + model.Series.Add(columnSeries); + } + + return model; + } + + [Example("#539104: Brewer colors (4)")] + public static PlotModel BrewerColors4() + { + var model = new PlotModel + { + Title = "Brewer colors (Accent scheme)", + }; + + model.Axes.Add(new CategoryAxis { Position = AxisPosition.Bottom }); + + // See http://colorbrewer2.org/?type=qualitative&scheme=Accent&n=4 + model.DefaultColors = new[] + { + OxyColor.FromRgb(127, 201, 127), + OxyColor.FromRgb(190, 174, 212), + OxyColor.FromRgb(253, 192, 134), + OxyColor.FromRgb(255, 255, 153) + }; + + var r = new Random(37); + for (var i = 0; i < model.DefaultColors.Count; i++) + { + var columnSeries = new ColumnSeries(); + columnSeries.Items.Add(new ColumnItem(50 + r.Next(50))); + columnSeries.Items.Add(new ColumnItem(40 + r.Next(50))); + model.Series.Add(columnSeries); + } + + return model; + } + + [Example("#539104: Brewer colors (6)")] + public static PlotModel BrewerColors6() + { + var model = new PlotModel + { + Title = "Brewer colors (Paired scheme)", + }; + + model.Axes.Add(new CategoryAxis { Position = AxisPosition.Bottom }); + + // See http://colorbrewer2.org/?type=qualitative&scheme=Paired&n=6 + model.DefaultColors = new[] + { + OxyColor.FromRgb(166, 206, 227), + OxyColor.FromRgb(31, 120, 180), + OxyColor.FromRgb(178, 223, 138), + OxyColor.FromRgb(51, 160, 44), + OxyColor.FromRgb(251, 154, 153), + OxyColor.FromRgb(227, 26, 28) + }; + + var r = new Random(37); + for (var i = 0; i < model.DefaultColors.Count; i++) + { + var columnSeries = new ColumnSeries(); + columnSeries.Items.Add(new ColumnItem(50 + r.Next(50))); + columnSeries.Items.Add(new ColumnItem(40 + r.Next(50))); + model.Series.Add(columnSeries); + } + + return model; + } + + [Example("#542701: Same color of LineSeries and axis title & labels")] + public static PlotModel SameColorOfLineSeriesAndAxisTitleAndLabels() + { + var model = new PlotModel { Title = "Same color of LineSeries and axis title & labels" }; + var color = OxyColors.IndianRed; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = "Axis 1", TitleColor = color, TextColor = color }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Series.Add(new LineSeries { Title = "LineSeries 1", Color = color, ItemsSource = new[] { new DataPoint(0, 0), new DataPoint(10, 3), new DataPoint(20, 2) } }); + return model; + } + + [Example("#549839: Polar plot with custom arrow series")] + public static PlotModel PolarPlotWithArrows() + { + var model = new PlotModel { Title = "Custom arrow series", PlotType = PlotType.Polar, PlotAreaBorderColor = OxyColors.Undefined }; + model.Axes.Add(new AngleAxis { Minimum = 0, Maximum = 360, MajorStep = 30, MinorStep = 30, MajorGridlineStyle = LineStyle.Dash }); + model.Axes.Add(new MagnitudeAxis { Minimum = 0, Maximum = 5, MajorStep = 1, MinorStep = 1, Angle = 90, MajorGridlineStyle = LineStyle.Dash }); + model.Series.Add(new ArrowSeries549839 { EndPoint = new DataPoint(1, 40) }); + model.Series.Add(new ArrowSeries549839 { EndPoint = new DataPoint(2, 75) }); + model.Series.Add(new ArrowSeries549839 { EndPoint = new DataPoint(3, 110) }); + model.Series.Add(new ArrowSeries549839 { EndPoint = new DataPoint(4, 140) }); + model.Series.Add(new ArrowSeries549839 { EndPoint = new DataPoint(5, 180) }); + return model; + } + + [Example("MarkerType = Circle problem")] + public static PlotModel MarkerTypeCircleProblem() + { + var plotModel = new PlotModel { LegendSymbolLength = 30, PlotType = PlotType.Cartesian, PlotAreaBorderThickness = new OxyThickness(0) }; + + + var xaxis = new DateTimeAxis + { + Position = AxisPosition.Bottom, + TickStyle = TickStyle.None, + AxislineStyle = LineStyle.Solid, + AxislineColor = OxyColor.FromRgb(153, 153, 153), + StringFormat = CultureInfo.CurrentCulture.DateTimeFormat.GetAbbreviatedMonthName(1) + "d HH", + IntervalType = DateTimeIntervalType.Hours + }; + + var yaxis = new LinearAxis + { + Position = AxisPosition.Left, + Minimum = 0.001f, + Maximum = 3, + MajorGridlineStyle = LineStyle.Solid, + TickStyle = TickStyle.None, + IntervalLength = 50 + }; + + plotModel.Axes.Add(xaxis); + plotModel.Axes.Add(yaxis); + + var series1 = new LineSeries + { + Color = OxyColor.FromRgb(44, 169, 173), + StrokeThickness = 1, + MarkerType = MarkerType.Circle, + MarkerStroke = OxyColors.Blue, + MarkerFill = OxyColors.SkyBlue, + // MarkerStrokeThickness = 5, + MarkerSize = 2, + DataFieldX = "Date", + DataFieldY = "Value", + TrackerFormatString = "Date: {2:d HH} Value: {4}" + }; + + series1.Points.Add(new DataPoint(0.1, 0.7)); + series1.Points.Add(new DataPoint(0.6, 0.9)); + series1.Points.Add(new DataPoint(1.0, 0.85)); + series1.Points.Add(new DataPoint(1.4, 0.95)); + series1.Points.Add(new DataPoint(1.8, 1.2)); + series1.Points.Add(new DataPoint(2.2, 1.7)); + series1.Points.Add(new DataPoint(2.6, 1.7)); + series1.Points.Add(new DataPoint(3.0, 0.7)); + + plotModel.Series.Add(series1); + + return plotModel; + } + + private class ArrowSeries549839 : XYAxisSeries + { + private OxyColor defaultColor; + + public ArrowSeries549839() + { + this.Color = OxyColors.Automatic; + this.StrokeThickness = 2; + } + + public DataPoint StartPoint { get; set; } + + public DataPoint EndPoint { get; set; } + + public OxyColor Color { get; set; } + + public double StrokeThickness { get; set; } + + protected override void SetDefaultValues() + { + if (this.Color.IsAutomatic()) + { + this.defaultColor = this.PlotModel.GetDefaultColor(); + } + } + + public OxyColor ActualColor + { + get + { + return this.Color.GetActualColor(this.defaultColor); + } + } + + public override void Render(IRenderContext rc) + { + // transform to screen coordinates + var p0 = this.Transform(this.StartPoint); + var p1 = this.Transform(this.EndPoint); + + var direction = p1 - p0; + var normal = new ScreenVector(direction.Y, -direction.X); + + // the end points of the arrow head, scaled by length of arrow + var p2 = p1 - (direction * 0.2) + (normal * 0.1); + var p3 = p1 - (direction * 0.2) - (normal * 0.1); + + // draw the line segments + rc.DrawLineSegments(new[] { p0, p1, p1, p2, p1, p3 }, this.ActualColor, this.StrokeThickness); + } + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Example.cs b/OxyPlot/Source/Examples/ExampleLibrary/Example.cs new file mode 100644 index 0000000..26c3fdc --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Example.cs @@ -0,0 +1,43 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using OxyPlot; + + /// + /// Represents an example. + /// + public class Example + { + /// + /// Initializes a new instance of the class. + /// + /// The model. + /// The controller. + public Example(PlotModel model, IPlotController controller = null) + { + this.Model = model; + this.Controller = controller; + } + + /// + /// Gets the controller. + /// + /// + /// The controller. + /// + public IPlotController Controller { get; private set; } + + /// + /// Gets the model. + /// + /// + /// The model. + /// + public PlotModel Model { get; private set; } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/ExampleInfo.cs b/OxyPlot/Source/Examples/ExampleLibrary/ExampleInfo.cs new file mode 100644 index 0000000..e0e045e --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/ExampleInfo.cs @@ -0,0 +1,142 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System.Reflection; + + using OxyPlot; + + /// + /// Provides information about an example. + /// + public class ExampleInfo + { + /// + /// The method to invoke. + /// + private readonly MethodInfo method; + + /// + /// The result of the method call. + /// + private object result; + + /// + /// Initializes a new instance of the class. + /// + /// The category. + /// The title. + /// The tags. + /// The method. + public ExampleInfo(string category, string title, string[] tags, MethodInfo method) + { + this.Category = category; + this.Title = title; + this.Tags = tags; + this.method = method; + } + + /// + /// Gets the category. + /// + /// + /// The category. + /// + public string Category { get; private set; } + + /// + /// Gets the title. + /// + /// + /// The title. + /// + public string Title { get; private set; } + + /// + /// Gets the tags. + /// + /// + /// The tags. + /// + public string[] Tags { get; private set; } + + /// + /// Gets the plot model. + /// + /// + /// The plot model. + /// + public PlotModel PlotModel + { + get + { + var plotModel = this.Result as PlotModel; + if (plotModel != null) + { + return plotModel; + } + + var example = this.Result as Example; + return example != null ? example.Model : null; + } + } + + /// + /// Gets the plot controller. + /// + /// + /// The plot controller. + /// + public IPlotController PlotController + { + get + { + var example = this.Result as Example; + return example != null ? example.Controller : null; + } + } + + /// + /// Gets the code. + /// + /// + /// The code. + /// + public string Code + { + get + { + return this.PlotModel != null ? this.PlotModel.ToCode() : null; + } + } + + /// + /// Gets the result. + /// + /// + /// The result. + /// + private object Result + { + get + { + return this.result ?? (this.result = this.method.Invoke(null, null)); + } + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return this.Title; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/ExampleLibrary.csproj b/OxyPlot/Source/Examples/ExampleLibrary/ExampleLibrary.csproj new file mode 100644 index 0000000..0fda4be --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/ExampleLibrary.csproj @@ -0,0 +1,40 @@ + + + + netstandard1.0;net45 + OxyPlot.ExampleLibrary + True + Example models for OxyPlot. + https://raw.githubusercontent.com/oxyplot/oxyplot/master/LICENSE + OxyPlot contributors + http://oxyplot.org/ + https://raw.githubusercontent.com/oxyplot/oxyplot/develop/Icons/OxyPlot_128.png + plotting plot charting chart + git + https://github.com/oxyplot/oxyplot.git + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/ExampleLibrary.snk b/OxyPlot/Source/Examples/ExampleLibrary/ExampleLibrary.snk new file mode 100644 index 0000000..9dadbd1 Binary files /dev/null and b/OxyPlot/Source/Examples/ExampleLibrary/ExampleLibrary.snk differ diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Examples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Examples.cs new file mode 100644 index 0000000..3200fdb --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Examples.cs @@ -0,0 +1,78 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + + /// + /// Enumerates all examples in the assembly. + /// + public static class Examples + { + /// + /// Gets the list of examples. + /// + /// The list of examples. + public static List GetList() + { + var list = new List(); + var assemblyTypes = typeof(Examples).GetTypeInfo().Assembly.DefinedTypes; + + foreach (var type in assemblyTypes) + { + var examplesAttribute = type.GetCustomAttributes().FirstOrDefault(); + if (examplesAttribute == null) + { + continue; + } + + var examplesTags = type.GetCustomAttributes().FirstOrDefault() ?? new TagsAttribute(); + + var types = new List(); + var baseType = type; + while (baseType != null) + { + types.Add(baseType.AsType()); + baseType = baseType.BaseType != null ? baseType.BaseType.GetTypeInfo() : null; + } + + foreach (var t in types) + { + var methods = t.GetRuntimeMethods(); + + foreach (var method in methods) + { + try + { + var exampleAttribute = method.GetCustomAttributes().FirstOrDefault(); + if (exampleAttribute != null) + { + var exampleTags = method.GetCustomAttributes().FirstOrDefault() ?? new TagsAttribute(); + var tags = new List(examplesTags.Tags); + tags.AddRange(exampleTags.Tags); + list.Add( + new ExampleInfo( + examplesAttribute.Category, + exampleAttribute.Title, + tags.ToArray(), + method)); + } + } + catch (Exception) + { + } + } + } + } + + return list; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Examples/FilteringExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Examples/FilteringExamples.cs new file mode 100644 index 0000000..f28a699 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Examples/FilteringExamples.cs @@ -0,0 +1,151 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("Filtering data points")] + public static class FilteringExamples + { + [Example("Filtering NaN points")] + public static PlotModel FilteringInvalidPoints() + { + var plot = new PlotModel { Title = "Filtering NaN points" }; + plot.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + plot.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + + var ls1 = new LineSeries(); + ls1.Points.Add(new DataPoint(double.NaN, double.NaN)); + ls1.Points.Add(new DataPoint(1, 0)); + ls1.Points.Add(new DataPoint(2, 10)); + ls1.Points.Add(new DataPoint(double.NaN, 20)); + ls1.Points.Add(new DataPoint(3, 10)); + ls1.Points.Add(new DataPoint(4, 0)); + ls1.Points.Add(new DataPoint(4.5, double.NaN)); + ls1.Points.Add(new DataPoint(5, 0)); + ls1.Points.Add(new DataPoint(7, 7)); + ls1.Points.Add(new DataPoint(double.NaN, double.NaN)); + ls1.Points.Add(new DataPoint(double.NaN, double.NaN)); + ls1.Points.Add(new DataPoint(7, 0)); + ls1.Points.Add(new DataPoint(double.NaN, double.NaN)); + plot.Series.Add(ls1); + + return plot; + } + + [Example("Filtering NaN points with AreaSeries")] + public static PlotModel FilteringInvalidPointsAreaSeries() + { + var plot = new PlotModel { Title = "Filtering NaN points in an AreaSeries" }; + plot.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + plot.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + + var as1 = new AreaSeries(); + as1.Points.Add(new DataPoint(1, 0)); + as1.Points.Add(new DataPoint(2, 10)); + as1.Points.Add(new DataPoint(3, 10)); + as1.Points.Add(new DataPoint(4, 0)); + as1.Points.Add(new DataPoint(5, 0)); + as1.Points.Add(new DataPoint(6, 7)); + as1.Points.Add(new DataPoint(7, 7)); + as1.Points.Add(new DataPoint(double.NaN, double.NaN)); + as1.Points.Add(new DataPoint(double.NaN, double.NaN)); + as1.Points.Add(new DataPoint(8, 0)); + as1.Points.Add(new DataPoint(9, 0)); + as1.Points.Add(new DataPoint(double.NaN, double.NaN)); + + as1.Points2.Add(new DataPoint(1, 10)); + as1.Points2.Add(new DataPoint(2, 110)); + as1.Points2.Add(new DataPoint(3, 110)); + as1.Points2.Add(new DataPoint(4, 10)); + as1.Points2.Add(new DataPoint(5, 10)); + as1.Points2.Add(new DataPoint(6, 17)); + as1.Points2.Add(new DataPoint(7, 17)); + as1.Points2.Add(new DataPoint(double.NaN, double.NaN)); + as1.Points2.Add(new DataPoint(double.NaN, double.NaN)); + as1.Points2.Add(new DataPoint(8, 10)); + as1.Points2.Add(new DataPoint(9, 10)); + as1.Points2.Add(new DataPoint(double.NaN, double.NaN)); + + plot.Series.Add(as1); + + return plot; + } + + + [Example("Filtering undefined points")] + public static PlotModel FilteringUndefinedPoints() + { + var plot = new PlotModel { Title = "Filtering undefined points" }; + plot.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + plot.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + + var ls1 = new LineSeries(); + ls1.Points.Add(DataPoint.Undefined); + ls1.Points.Add(new DataPoint(1, 0)); + ls1.Points.Add(new DataPoint(2, 10)); + ls1.Points.Add(DataPoint.Undefined); + ls1.Points.Add(new DataPoint(3, 10)); + ls1.Points.Add(new DataPoint(4, 0)); + ls1.Points.Add(DataPoint.Undefined); + ls1.Points.Add(new DataPoint(5, 0)); + ls1.Points.Add(new DataPoint(7, 7)); + ls1.Points.Add(DataPoint.Undefined); + ls1.Points.Add(DataPoint.Undefined); + ls1.Points.Add(new DataPoint(7, 0)); + ls1.Points.Add(DataPoint.Undefined); + plot.Series.Add(ls1); + + return plot; + } + + [Example("Filtering invalid points (log axis)")] + public static PlotModel FilteringInvalidPointsLog() + { + var plot = new PlotModel { Title = "Filtering invalid points on logarithmic axes" }; + plot.Axes.Add(new LogarithmicAxis { Position = AxisPosition.Bottom }); + plot.Axes.Add(new LogarithmicAxis { Position = AxisPosition.Left }); + + var ls = new LineSeries(); + ls.Points.Add(new DataPoint(double.NaN, double.NaN)); + ls.Points.Add(new DataPoint(1, 1)); + ls.Points.Add(new DataPoint(10, 10)); + ls.Points.Add(new DataPoint(0, 20)); + ls.Points.Add(new DataPoint(100, 2)); + ls.Points.Add(new DataPoint(1000, 12)); + ls.Points.Add(new DataPoint(4.5, 0)); + ls.Points.Add(new DataPoint(10000, 4)); + ls.Points.Add(new DataPoint(100000, 14)); + ls.Points.Add(new DataPoint(double.NaN, double.NaN)); + ls.Points.Add(new DataPoint(1000000, 5)); + ls.Points.Add(new DataPoint(double.NaN, double.NaN)); + plot.Series.Add(ls); + return plot; + } + + [Example("Filtering points outside (-1,1)")] + public static PlotModel FilteringPointsOutsideRange() + { + var plot = new PlotModel { Title = "Filtering points outside (-1,1)" }; + plot.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, FilterMinValue = -1, FilterMaxValue = 1 }); + plot.Axes.Add(new LinearAxis { Position = AxisPosition.Left, FilterMinValue = -1, FilterMaxValue = 1 }); + + var ls = new LineSeries(); + for (double i = 0; i < 200; i += 0.01) + { + ls.Points.Add(new DataPoint(0.01 * i * Math.Sin(i), 0.01 * i * Math.Cos(i))); + } + + plot.Series.Add(ls); + return plot; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Examples/ItemsSourceExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Examples/ItemsSourceExamples.cs new file mode 100644 index 0000000..9e4f313 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Examples/ItemsSourceExamples.cs @@ -0,0 +1,159 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Evaluates a chaotic function. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + using System.Collections.Generic; + + using OxyPlot; + using OxyPlot.Series; + + [Examples("ItemsSource")] + public class ItemsSourceExamples + { + private static int n = 100000; + [Example("List")] + public static PlotModel UsingIDataPoint() + { + var points = new List(n); + for (int i = 0; i < n; i++) + { + var x = (double)i / (n - 1); + points.Add(new DataPoint(x, y(x))); + } + + var model = new PlotModel { Title = "Using IDataPoint" }; + model.Series.Add(new LineSeries { ItemsSource = points }); + return model; + } + + [Example("Items implementing IDataPointProvider")] + public static PlotModel UsingIDataPointProvider() + { + var points = new List(n); + for (int i = 0; i < n; i++) + { + var x = (double)i / (n - 1); + points.Add(new PointType1(x, y(x))); + } + + var model = new PlotModel { Title = "Items implementing IDataPointProvider" }; + model.Series.Add(new LineSeries { ItemsSource = points }); + return model; + } + + [Example("Mapping property")] + public static PlotModel UsingMappingProperty() + { + var points = new List(n); + for (int i = 0; i < n; i++) + { + var x = (double)i / (n - 1); + points.Add(new PointType2(x, y(x))); + } + + var model = new PlotModel { Title = "Using Mapping property" }; + model.Series.Add( + new LineSeries + { + ItemsSource = points, + Mapping = item => new DataPoint(((PointType2)item).Abscissa, ((PointType2)item).Ordinate) + }); + return model; + } + + [Example("Using reflection (slow)")] + public static PlotModel UsingReflection() + { + var points = new List(n); + for (int i = 0; i < n; i++) + { + var x = (double)i / (n - 1); + points.Add(new PointType2(x, y(x))); + } + + var model = new PlotModel { Title = "Using reflection (slow)" }; + model.Series.Add(new LineSeries { ItemsSource = points, DataFieldX = "Abscissa", DataFieldY = "Ordinate" }); + return model; + } + + [Example("Using reflection with path (slow)")] + public static PlotModel UsingReflectionPath() + { + var points = new List(n); + for (int i = 0; i < n; i++) + { + var x = (double)i / (n - 1); + points.Add(new ItemType3(x, y(x))); + } + + var model = new PlotModel { Title = "Using reflection with path (slow)" }; + model.Series.Add(new LineSeries { ItemsSource = points, DataFieldX = "Point.X", DataFieldY = "Point.Y" }); + return model; + } + + private class PointType1 : IDataPointProvider, ICodeGenerating + { + public PointType1(double abscissa, double ordinate) + { + this.Abscissa = abscissa; + this.Ordinate = ordinate; + } + + public double Abscissa { get; private set; } + + public double Ordinate { get; private set; } + + public DataPoint GetDataPoint() + { + return new DataPoint(Abscissa, Ordinate); + } + + public string ToCode() + { + return CodeGenerator.FormatConstructor(this.GetType(), "{0},{1}", this.Abscissa, this.Ordinate); + } + } + + private class PointType2 + { + public PointType2(double abscissa, double ordinate) + { + this.Abscissa = abscissa; + this.Ordinate = ordinate; + } + + public double Abscissa { get; private set; } + + public double Ordinate { get; private set; } + } + + private class ItemType3 + { + public ItemType3(double x, double y) + { + this.Point = new ScreenPoint(x, y); + } + + public ScreenPoint Point { get; private set; } + } + + /// + /// Evaluates a chaotic function. + /// + /// The x value. + /// A y value. + private static double y(double x) + { + // http://computing.dcu.ie/~humphrys/Notes/Neural/chaos.html + return Math.Sin(3 / x) * Math.Sin(5 / (1 - x)); + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Examples/LegendExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Examples/LegendExamples.cs new file mode 100644 index 0000000..e1b7fab --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Examples/LegendExamples.cs @@ -0,0 +1,175 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + using System.Collections.Generic; + + using OxyPlot; + using OxyPlot.Series; + + [Examples("Legends")] + public static class LegendExamples + { + [Example("Legend at right top inside")] + public static PlotModel LegendRightTopInside() + { + var model = CreateModel(); + model.LegendPlacement = LegendPlacement.Inside; + model.LegendPosition = LegendPosition.RightTop; + return model; + } + + [Example("Legend at right top outside")] + public static PlotModel LegendRightTopOutside() + { + var model = CreateModel(); + model.LegendPlacement = LegendPlacement.Outside; + model.LegendPosition = LegendPosition.RightTop; + return model; + } + + [Example("Legend at BottomLeft outside horizontal")] + public static PlotModel LegendBottomLeftHorizontal() + { + var model = CreateModel(4); + model.LegendPlacement = LegendPlacement.Outside; + model.LegendPosition = LegendPosition.BottomLeft; + model.LegendOrientation = LegendOrientation.Horizontal; + return model; + } + + [Example("Legend at TopLeft outside vertical")] + public static PlotModel LegendTopLeftVertical() + { + var model = CreateModel(4); + model.LegendPlacement = LegendPlacement.Outside; + model.LegendPosition = LegendPosition.TopLeft; + model.LegendOrientation = LegendOrientation.Vertical; + return model; + } + + [Example("Legend at default position")] + public static PlotModel LegendDefault() + { + var model = CreateModel(); + return model; + } + + [Example("LegendItemSpacing (only for horizontal orientation)")] + public static PlotModel LegendItemSpacing() + { + var model = CreateModel(); + model.LegendOrientation = LegendOrientation.Horizontal; + model.LegendPosition = LegendPosition.BottomLeft; + model.LegendItemSpacing = 100; + return model; + } + + [Example("LegendLineSpacing (vertical legend orientation)")] + public static PlotModel LegendLineSpacingVertical() + { + var model = CreateModel(); + model.LegendOrientation = LegendOrientation.Vertical; + model.LegendPosition = LegendPosition.TopLeft; + model.LegendLineSpacing = 30; + return model; + } + + [Example("LegendLineSpacing (horizontal legend orientation)")] + public static PlotModel LegendLineSpacingHorizontal() + { + var model = CreateModel(); + model.LegendOrientation = LegendOrientation.Horizontal; + model.LegendPosition = LegendPosition.TopLeft; + model.LegendLineSpacing = 30; + return model; + } + + [Example("LegendColumnSpacing (only for vertical orientation)")] + public static PlotModel LegendColumnSpacing() + { + var model = CreateModel(60); + model.LegendOrientation = LegendOrientation.Vertical; + model.LegendPosition = LegendPosition.TopRight; + model.LegendColumnSpacing = 100; + return model; + } + + [Example("Hidden Legend")] + public static PlotModel LegendHidden() + { + var model = CreateModel(); + model.IsLegendVisible = false; + return model; + } + + [Example("Grayscale colors")] + public static PlotModel LegendGrayscale() + { + var model = CreateModel(); + model.DefaultColors = new List { OxyColors.Black, OxyColors.Gray }; + model.LegendSymbolLength = 32; + return model; + } + + [Example("Clipped legends")] + public static PlotModel ClippedLegends() + { + var model = CreateModel(1); + model.Series[0].Title = "1234567890 abcdefghijklmnopqrstuvwxyzæøå ABCDEFGHIJKLMNOPQRSTUVWXYZÆØÅ 1234567890 abcdefghijklmnopqrstuvwxyzæøå ABCDEFGHIJKLMNOPQRSTUVWXYZÆØÅ"; + model.LegendPlacement = LegendPlacement.Inside; + model.LegendPosition = LegendPosition.RightTop; + return model; + } + + [Example("Clipped legends RightTop outside with MaxWidth")] + public static PlotModel ClippedLegendsOutside() + { + var model = ClippedLegends(); + model.LegendPlacement = LegendPlacement.Outside; + model.LegendMaxWidth = 200; + return model; + } + + [Example("Clipped legends TopRight outside")] + public static PlotModel ClippedLegendsRight() + { + var model = ClippedLegends(); + model.LegendPlacement = LegendPlacement.Outside; + model.LegendPosition = LegendPosition.TopRight; + return model; + } + + [Example("LegendMaxHeight (vertical legend orientation)")] + public static PlotModel LegendBottomCenterOutsideWithMaxHeight() + { + var model = CreateModel(); + model.LegendPlacement = LegendPlacement.Outside; + model.LegendPosition = LegendPosition.BottomCenter; + model.LegendOrientation = LegendOrientation.Vertical; + model.LegendMaxHeight = 75.0; + return model; + } + + private static PlotModel CreateModel(int n = 20) + { + var model = new PlotModel { Title = "LineSeries", LegendBackground = OxyColor.FromAColor(200, OxyColors.White), LegendBorder = OxyColors.Black }; + for (int i = 1; i <= n; i++) + { + var s = new LineSeries { Title = "Series " + i }; + model.Series.Add(s); + for (double x = 0; x < 2 * Math.PI; x += 0.1) + { + s.Points.Add(new DataPoint(x, (Math.Sin(x * i) / i) + i)); + } + } + + return model; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Examples/MouseEventExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Examples/MouseEventExamples.cs new file mode 100644 index 0000000..813aa53 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Examples/MouseEventExamples.cs @@ -0,0 +1,648 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + using System.Reflection; + + using OxyPlot; + using OxyPlot.Annotations; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("Mouse Events")] + public class MouseEventExamples + { + [Example("PlotModel mouse events")] + public static PlotModel MouseEvents() + { + var model = new PlotModel { Title = "Mouse events", Subtitle = "Left click and drag" }; + var yaxis = new LinearAxis { Position = AxisPosition.Left, Minimum = -1, Maximum = 1 }; + var xaxis = new LinearAxis { Position = AxisPosition.Bottom, Minimum = -1, Maximum = 1 }; + model.Axes.Add(yaxis); + model.Axes.Add(xaxis); + + LineSeries s1 = null; + + // Subscribe to the mouse down event on the line series + model.MouseDown += (s, e) => + { + // only handle the left mouse button (right button can still be used to pan) + if (e.ChangedButton == OxyMouseButton.Left) + { + // Add a line series + s1 = new LineSeries + { + Title = "LineSeries" + (model.Series.Count + 1), + MarkerType = MarkerType.None, + StrokeThickness = 2 + }; + s1.Points.Add(xaxis.InverseTransform(e.Position.X, e.Position.Y, yaxis)); + model.Series.Add(s1); + model.InvalidatePlot(false); + e.Handled = true; + } + }; + + model.MouseMove += (s, e) => + { + if (s1 != null) + { + s1.Points.Add(xaxis.InverseTransform(e.Position.X, e.Position.Y, yaxis)); + model.InvalidatePlot(false); + e.Handled = true; + } + }; + + model.MouseUp += (s, e) => + { + if (s1 != null) + { + s1 = null; + e.Handled = true; + } + }; + return model; + } + + [Example("MouseDown event and HitTestResult")] + public static PlotModel MouseDownEventHitTestResult() + { + var model = new PlotModel { Title = "MouseDown HitTestResult", Subtitle = "Reports the index of the nearest point." }; + + var s1 = new LineSeries(); + s1.Points.Add(new DataPoint(0, 10)); + s1.Points.Add(new DataPoint(10, 40)); + s1.Points.Add(new DataPoint(40, 20)); + s1.Points.Add(new DataPoint(60, 30)); + model.Series.Add(s1); + s1.MouseDown += (s, e) => + { + model.Subtitle = "Index of nearest point in LineSeries: " + Math.Round(e.HitTestResult.Index); + model.InvalidatePlot(false); + }; + + var s2 = new ScatterSeries(); + s2.Points.Add(new ScatterPoint(0, 15)); + s2.Points.Add(new ScatterPoint(10, 45)); + s2.Points.Add(new ScatterPoint(40, 25)); + s2.Points.Add(new ScatterPoint(60, 35)); + model.Series.Add(s2); + s2.MouseDown += (s, e) => + { + model.Subtitle = "Index of nearest point in ScatterSeries: " + (int)e.HitTestResult.Index; + model.InvalidatePlot(false); + }; + + return model; + } + + [Example("LineSeries and PlotModel MouseDown event")] + public static PlotModel MouseDownEvent() + { + var model = new PlotModel { Title = "MouseDown", Subtitle = "Left click to edit or add points.", LegendSymbolLength = 40 }; + + // Add a line series + var s1 = new LineSeries + { + Title = "LineSeries1", + Color = OxyColors.SkyBlue, + MarkerType = MarkerType.Circle, + MarkerSize = 6, + MarkerStroke = OxyColors.White, + MarkerFill = OxyColors.SkyBlue, + MarkerStrokeThickness = 1.5 + }; + s1.Points.Add(new DataPoint(0, 10)); + s1.Points.Add(new DataPoint(10, 40)); + s1.Points.Add(new DataPoint(40, 20)); + s1.Points.Add(new DataPoint(60, 30)); + model.Series.Add(s1); + + int indexOfPointToMove = -1; + + // Subscribe to the mouse down event on the line series + s1.MouseDown += (s, e) => + { + // only handle the left mouse button (right button can still be used to pan) + if (e.ChangedButton == OxyMouseButton.Left) + { + int indexOfNearestPoint = (int)Math.Round(e.HitTestResult.Index); + var nearestPoint = s1.Transform(s1.Points[indexOfNearestPoint]); + + // Check if we are near a point + if ((nearestPoint - e.Position).Length < 10) + { + // Start editing this point + indexOfPointToMove = indexOfNearestPoint; + } + else + { + // otherwise create a point on the current line segment + int i = (int)e.HitTestResult.Index + 1; + s1.Points.Insert(i, s1.InverseTransform(e.Position)); + indexOfPointToMove = i; + } + + // Change the linestyle while editing + s1.LineStyle = LineStyle.DashDot; + + // Remember to refresh/invalidate of the plot + model.InvalidatePlot(false); + + // Set the event arguments to handled - no other handlers will be called. + e.Handled = true; + } + }; + + s1.MouseMove += (s, e) => + { + if (indexOfPointToMove >= 0) + { + // Move the point being edited. + s1.Points[indexOfPointToMove] = s1.InverseTransform(e.Position); + model.InvalidatePlot(false); + e.Handled = true; + } + }; + + s1.MouseUp += (s, e) => + { + // Stop editing + indexOfPointToMove = -1; + s1.LineStyle = LineStyle.Solid; + model.InvalidatePlot(false); + e.Handled = true; + }; + + model.MouseDown += (s, e) => + { + if (e.ChangedButton == OxyMouseButton.Left) + { + // Add a point to the line series. + s1.Points.Add(s1.InverseTransform(e.Position)); + indexOfPointToMove = s1.Points.Count - 1; + + model.InvalidatePlot(false); + e.Handled = true; + } + }; + return model; + } + + [Example("Add arrow annotations")] + public static PlotModel AddAnnotations() + { + var model = new PlotModel { Title = "Add arrow annotations", Subtitle = "Press and drag the left mouse button" }; + var xaxis = new LinearAxis { Position = AxisPosition.Bottom }; + var yaxis = new LinearAxis { Position = AxisPosition.Left }; + model.Axes.Add(xaxis); + model.Axes.Add(yaxis); + model.Series.Add(new FunctionSeries(x => Math.Sin(x / 4) * Math.Acos(Math.Sin(x)), 0, Math.PI * 8, 2000, "sin(x/4)*acos(sin(x))")); + + ArrowAnnotation tmp = null; + + // Add handlers to the PlotModel's mouse events + model.MouseDown += (s, e) => + { + if (e.ChangedButton == OxyMouseButton.Left) + { + // Create a new arrow annotation + tmp = new ArrowAnnotation(); + tmp.StartPoint = tmp.EndPoint = xaxis.InverseTransform(e.Position.X, e.Position.Y, yaxis); + model.Annotations.Add(tmp); + e.Handled = true; + } + }; + + // Handle mouse movements (note: this is only called when the mousedown event was handled) + model.MouseMove += (s, e) => + { + if (tmp != null) + { + // Modify the end point + tmp.EndPoint = xaxis.InverseTransform(e.Position.X, e.Position.Y, yaxis); + tmp.Text = string.Format("Y = {0:0.###}", tmp.EndPoint.Y); + + // Redraw the plot + model.InvalidatePlot(false); + e.Handled = true; + } + }; + + model.MouseUp += (s, e) => + { + if (tmp != null) + { + tmp = null; + e.Handled = true; + } + }; + + return model; + } + + [Example("LineAnnotation")] + public static PlotModel LineAnnotation() + { + var model = new PlotModel { Title = "LineAnnotation", Subtitle = "Click and drag the annotation line." }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = -20, Maximum = 80 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = -10, Maximum = 10 }); + var la = new LineAnnotation { Type = LineAnnotationType.Vertical, X = 4 }; + la.MouseDown += (s, e) => + { + if (e.ChangedButton != OxyMouseButton.Left) + { + return; + } + + la.StrokeThickness *= 5; + model.InvalidatePlot(false); + e.Handled = true; + }; + + // Handle mouse movements (note: this is only called when the mousedown event was handled) + la.MouseMove += (s, e) => + { + la.X = la.InverseTransform(e.Position).X; + model.InvalidatePlot(false); + e.Handled = true; + }; + la.MouseUp += (s, e) => + { + la.StrokeThickness /= 5; + model.InvalidatePlot(false); + e.Handled = true; + }; + model.Annotations.Add(la); + return model; + } + + [Example("ArrowAnnotation")] + public static PlotModel ArrowAnnotation() + { + var model = new PlotModel { Title = "ArrowAnnotation", Subtitle = "Click and drag the arrow." }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = -40, Maximum = 60 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = -10, Maximum = 10 }); + + var arrow = new ArrowAnnotation { StartPoint = new DataPoint(8, 4), EndPoint = new DataPoint(0, 0), Text = "Move me!" }; + + var lastPoint = DataPoint.Undefined; + bool moveStartPoint = false; + bool moveEndPoint = false; + var originalColor = OxyColors.White; + + // Handle left mouse clicks + arrow.MouseDown += (s, e) => + { + if (e.ChangedButton != OxyMouseButton.Left) + { + return; + } + + lastPoint = arrow.InverseTransform(e.Position); + moveStartPoint = e.HitTestResult.Index != 2; + moveEndPoint = e.HitTestResult.Index != 1; + originalColor = arrow.Color; + arrow.Color = OxyColors.Red; + model.InvalidatePlot(false); + e.Handled = true; + }; + + // Handle mouse movements (note: this is only called when the mousedown event was handled) + arrow.MouseMove += (s, e) => + { + var thisPoint = arrow.InverseTransform(e.Position); + double dx = thisPoint.X - lastPoint.X; + double dy = thisPoint.Y - lastPoint.Y; + if (moveStartPoint) + { + arrow.StartPoint = new DataPoint(arrow.StartPoint.X + dx, arrow.StartPoint.Y + dy); + } + + if (moveEndPoint) + { + arrow.EndPoint = new DataPoint(arrow.EndPoint.X + dx, arrow.EndPoint.Y + dy); + } + + lastPoint = thisPoint; + model.InvalidatePlot(false); + e.Handled = true; + }; + + // Handle mouse up (note: this is only called when the mousedown event was handled) + arrow.MouseUp += (s, e) => + { + arrow.Color = originalColor; + }; + model.Annotations.Add(arrow); + return model; + } + + [Example("PolygonAnnotation")] + public static PlotModel PolygonAnnotation() + { + var model = new PlotModel { Title = "PolygonAnnotation", Subtitle = "Click the polygon" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = -20, Maximum = 20 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = -20, Maximum = 20 }); + var pa = new PolygonAnnotation + { + Text = "Polygon 1" + }; + pa.Points.AddRange(new[] + { + new DataPoint(4, -2), new DataPoint(8, -4), new DataPoint(17, 7), new DataPoint(5, 8), + new DataPoint(2, 5) + }); + + // Handle left mouse clicks + int hitCount = 1; + pa.MouseDown += (s, e) => + { + if (e.ChangedButton != OxyMouseButton.Left) + { + return; + } + + pa.Text = "Hit # " + hitCount++; + model.InvalidatePlot(false); + e.Handled = true; + }; + + model.Annotations.Add(pa); + return model; + } + + [Example("TextAnnotation")] + public static PlotModel TextAnnotation() + { + var model = new PlotModel { Title = "TextAnnotation", Subtitle = "Click the text" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = -20, Maximum = 20 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = -10, Maximum = 10 }); + var ta = new TextAnnotation + { + TextPosition = new DataPoint(4, -2), + Text = "Click here" + }; + + // Handle left mouse clicks + ta.MouseDown += (s, e) => + { + if (e.ChangedButton != OxyMouseButton.Left) + { + return; + } + + ta.Background = ta.Background.IsUndefined() ? OxyColors.LightGreen : OxyColors.Undefined; + model.InvalidatePlot(false); + e.Handled = true; + }; + + model.Annotations.Add(ta); + return model; + } + + [Example("ImageAnnotation")] + public static PlotModel ImageAnnotation() + { + var model = new PlotModel { Title = "ImageAnnotation", Subtitle = "Click the image" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = -20, Maximum = 20 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = -10, Maximum = 10 }); + + OxyImage image; + var assembly = typeof(MouseEventExamples).GetTypeInfo().Assembly; + using (var stream = assembly.GetManifestResourceStream("ExampleLibrary.Resources.OxyPlot.png")) + { + image = new OxyImage(stream); + } + + var ia = new ImageAnnotation { ImageSource = image, X = new PlotLength(4, PlotLengthUnit.Data), Y = new PlotLength(2, PlotLengthUnit.Data), HorizontalAlignment = HorizontalAlignment.Right }; + model.Annotations.Add(ia); + + // Handle left mouse clicks + ia.MouseDown += (s, e) => + { + if (e.ChangedButton != OxyMouseButton.Left) + { + return; + } + + ia.HorizontalAlignment = ia.HorizontalAlignment == HorizontalAlignment.Right ? HorizontalAlignment.Left : HorizontalAlignment.Right; + model.InvalidatePlot(false); + e.Handled = true; + }; + + return model; + } + + [Example("Add Series")] + public static PlotModel AddSeriesByMouseDownEvent() + { + var model = new PlotModel { Title = "MouseDown", Subtitle = "Left click to add series.", LegendSymbolLength = 40 }; + + model.MouseDown += (s, e) => + { + if (e.ChangedButton == OxyMouseButton.Left) + { + double a = model.Series.Count + 1; + model.Series.Add(new FunctionSeries(x => Math.Sin(a * x), 0, 10, 1000)); + model.InvalidatePlot(true); + e.Handled = true; + } + }; + + return model; + } + + [Example("Select range")] + public static PlotModel SelectRange() + { + var model = new PlotModel { Title = "Select range", Subtitle = "Left click and drag to select a range." }; + model.Series.Add(new FunctionSeries(Math.Cos, 0, 40, 0.1)); + + var range = new RectangleAnnotation { Fill = OxyColor.FromAColor(120, OxyColors.SkyBlue), MinimumX = 0, MaximumX = 0 }; + model.Annotations.Add(range); + + double startx = double.NaN; + + model.MouseDown += (s, e) => + { + if (e.ChangedButton == OxyMouseButton.Left) + { + startx = range.InverseTransform(e.Position).X; + range.MinimumX = startx; + range.MaximumX = startx; + model.InvalidatePlot(true); + e.Handled = true; + } + }; + model.MouseMove += (s, e) => + { + if (!double.IsNaN(startx)) + { + var x = range.InverseTransform(e.Position).X; + range.MinimumX = Math.Min(x, startx); + range.MaximumX = Math.Max(x, startx); + range.Text = string.Format("∫ cos(x) dx = {0:0.00}", Math.Sin(range.MaximumX) - Math.Sin(range.MinimumX)); + model.Subtitle = string.Format("Integrating from {0:0.00} to {1:0.00}", range.MinimumX, range.MaximumX); + model.InvalidatePlot(true); + e.Handled = true; + } + }; + + model.MouseUp += (s, e) => + { + startx = double.NaN; + }; + + return model; + } + + [Example("Hover")] + public static PlotModel Hover() + { + var model = new PlotModel { Title = "Hover" }; + LineSeries series = null; + + model.MouseEnter += (s, e) => + { + model.Subtitle = "The mouse entered"; + series = new LineSeries(); + model.Series.Add(series); + model.InvalidatePlot(false); + e.Handled = true; + }; + + model.MouseMove += (s, e) => + { + if (series != null && series.XAxis != null) + { + series.Points.Add(series.InverseTransform(e.Position)); + model.InvalidatePlot(false); + } + }; + + model.MouseLeave += (s, e) => + { + model.Subtitle = "The mouse left"; + model.InvalidatePlot(false); + e.Handled = true; + }; + + return model; + } + + [Example("Touch")] + public static PlotModel Touch() + { + var model = new PlotModel { Title = "Touch" }; + var series = new LineSeries(); + model.Series.Add(series); + + model.TouchStarted += (s, e) => + { + model.Subtitle = "The touch gesture started"; + model.InvalidatePlot(false); + e.Handled = true; + }; + + model.TouchDelta += (s, e) => + { + series.Points.Add(series.InverseTransform(e.Position)); + model.InvalidatePlot(false); + }; + + model.TouchCompleted += (s, e) => + { + model.Subtitle = "The touch gesture completed"; + model.InvalidatePlot(false); + e.Handled = true; + }; + + return model; + } + + [Example("Touch on a LineSeries")] + public static PlotModel TouchSeries() + { + var model = new PlotModel { Title = "Touch on a LineSeries" }; + var series = new LineSeries(); + series.Points.Add(new DataPoint(0, 0)); + series.Points.Add(new DataPoint(10, 10)); + model.Series.Add(series); + + series.TouchStarted += (s, e) => + { + model.Subtitle = "The touch gesture started on the LineSeries"; + model.InvalidatePlot(false); + e.Handled = true; + }; + + series.TouchDelta += (s, e) => + { + series.Points.Add(series.InverseTransform(e.Position)); + model.InvalidatePlot(false); + }; + + series.TouchCompleted += (s, e) => + { + model.Subtitle = "The touch gesture completed"; + model.InvalidatePlot(false); + e.Handled = true; + }; + + return model; + } + + [Example("RectangleAnnotation click")] + public static PlotModel RectangleAnnotationClick() + { + var plotModel = new PlotModel { Title = "RectangleAnnotation click" }; + + plotModel.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + plotModel.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + + var annotation = new RectangleAnnotation() { MinimumX = 10, MaximumX = 60, MinimumY = 10, MaximumY = 20 }; + plotModel.Annotations.Add(annotation); + + int i = 0; + annotation.MouseDown += (s, e) => + { + annotation.Text = "Clicked " + (++i) + " times."; + plotModel.InvalidatePlot(false); + }; + + return plotModel; + } + + [Example("Clicking on an annotation")] + public static PlotModel ClickingOnAnAnnotation() + { + var plotModel = new PlotModel { Title = "Clicking on an annotation", Subtitle = "Click on the rectangles" }; + + plotModel.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + plotModel.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + + var annotation1 = new RectangleAnnotation { Fill = OxyColors.Green, Text = "RectangleAnnotation 1", MinimumX = 25, MaximumX = 75, MinimumY = 20, MaximumY = 40 }; + plotModel.Annotations.Add(annotation1); + + var annotation2 = new RectangleAnnotation { Fill = OxyColors.SkyBlue, Text = "RectangleAnnotation 2", MinimumX = 25, MaximumX = 75, MinimumY = 60, MaximumY = 80 }; + plotModel.Annotations.Add(annotation2); + + EventHandler handleMouseClick = (s, e) => + { + plotModel.Subtitle = "You clicked " + ((RectangleAnnotation)s).Text; + plotModel.InvalidatePlot(false); + }; + + annotation1.MouseDown += handleMouseClick; + annotation2.MouseDown += handleMouseClick; + + return plotModel; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Examples/PerformanceExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Examples/PerformanceExamples.cs new file mode 100644 index 0000000..ac345c1 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Examples/PerformanceExamples.cs @@ -0,0 +1,344 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + using System.Collections.Generic; + using System.Linq; + + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("Performance")] + public class PerformanceExamples + { + [Example("LineSeries, 1M points")] + public static PlotModel LineSeries1M() + { + var model = new PlotModel { Title = "LineSeries, 1M points" }; + var s1 = new LineSeries(); + AddPoints(s1.Points, 1000000); + model.Series.Add(s1); + return model; + } + + [Example("LineSeries, 100k points")] + public static PlotModel LineSeries() + { + var model = new PlotModel { Title = "LineSeries, 100k points" }; + var s1 = new LineSeries(); + AddPoints(s1.Points, 100000); + model.Series.Add(s1); + return model; + } + + [Example("LineSeries, 100k points (dashed line)")] + public static PlotModel LineSeriesDashedLines() + { + var model = new PlotModel { Title = "LineSeries, 100k points", Subtitle = "LineStyle = Dash" }; + var s1 = new LineSeries { LineStyle = LineStyle.Dash }; + AddPoints(s1.Points, 100000); + model.Series.Add(s1); + return model; + } + + [Example("LineSeries, 100k points, markers")] + public static PlotModel LineSeries1WithMarkers() + { + var model = new PlotModel { Title = "LineSeries, 100k points", Subtitle = "MarkerType = Square" }; + var s1 = new LineSeries { MarkerType = MarkerType.Square }; + AddPoints(s1.Points, 100000); + model.Series.Add(s1); + return model; + } + + [Example("LineSeries, 100k points, markers, lower resolution")] + public static PlotModel LineSeries1WithMarkersLowRes() + { + var model = new PlotModel { Title = "LineSeries, 100k points, markers, lower resolution", Subtitle = "MarkerType = Square, MarkerResolution = 3" }; + var s1 = new LineSeries { MarkerType = MarkerType.Square, MarkerResolution = 3 }; + AddPoints(s1.Points, 100000); + model.Series.Add(s1); + return model; + } + + [Example("LineSeries, 100k points, round line joins")] + public static PlotModel LineSeriesRoundLineJoins() + { + var model = new PlotModel { Title = "LineSeries, 100k points", Subtitle = "LineJoin = Round" }; + var s1 = new LineSeries { LineJoin = LineJoin.Round }; + AddPoints(s1.Points, 100000); + model.Series.Add(s1); + return model; + } + + [Example("LineSeries, 100k points by ItemsSource set to a List")] + public static PlotModel LineSeriesItemsSourceList() + { + var model = new PlotModel { Title = "LineSeries, 100k points by ItemsSource set to a List" }; + var s1 = new LineSeries(); + var points = new List(); + AddPoints(points, 100000); + s1.ItemsSource = points; + model.Series.Add(s1); + + return model; + } + + [Example("LineSeries, 100k points by ItemsSource and Mapping")] + public static PlotModel LineSeriesItemsSourceMapping() + { + var model = new PlotModel { Title = "LineSeries, 100k points by ItemsSource and Mapping", Subtitle = "Using the Mapping function" }; + var s1 = new LineSeries(); + var points = new List(); + AddPoints(points, 100000); + var rects = points.Select(pt => new OxyRect(pt.X, pt.Y, 0, 0)).ToList(); + s1.ItemsSource = rects; + s1.Mapping = r => new DataPoint(((OxyRect)r).Left, ((OxyRect)r).Top); + model.Series.Add(s1); + + return model; + } + + [Example("LineSeries, 100k points by ItemsSource and reflection")] + public static PlotModel LineSeriesItemsSourceReflection() + { + var model = new PlotModel { Title = "LineSeries, 100k points, ItemsSource with reflection", Subtitle = "DataFieldX and DataFieldY" }; + var s1 = new LineSeries(); + var points = new List(); + AddPoints(points, 100000); + var rects = points.Select(pt => new OxyRect(pt.X, pt.Y, 0, 0)).ToList(); + s1.ItemsSource = rects; + s1.DataFieldX = "Left"; + s1.DataFieldY = "Top"; + model.Series.Add(s1); + + return model; + } + + [Example("LineSeries, 100k points (thick)")] + public static PlotModel LineSeriesThick() + { + var model = new PlotModel { Title = "LineSeries, 100k points (thick)", Subtitle = "StrokeThickness = 10" }; + var s1 = new LineSeries { StrokeThickness = 10 }; + AddPoints(s1.Points, 100000); + model.Series.Add(s1); + + return model; + } + + [Example("LineSeries, 3k points, miter line joins")] + public static PlotModel LineSeries2MiterLineJoins() + { + var model = new PlotModel { Title = "LineSeries, 3k points, miter line joins", Subtitle = "LineJoin = Miter" }; + var s1 = new LineSeries { LineJoin = LineJoin.Miter, StrokeThickness = 8.0 }; + for (int i = 0; i < 3000; i++) + { + s1.Points.Add(new DataPoint(i, i % 2)); + } + + model.Series.Add(s1); + + return model; + } + + [Example("LineSeries, 3k points, round line joins")] + public static PlotModel LineSeries2RoundLineJoins() + { + var model = new PlotModel { Title = "LineSeries, 3k points, round line joins", Subtitle = "LineJoin = Round" }; + var s1 = new LineSeries { LineJoin = LineJoin.Round, StrokeThickness = 8.0 }; + for (int i = 0; i < 3000; i++) + { + s1.Points.Add(new DataPoint(i, i % 2)); + } + + model.Series.Add(s1); + return model; + } + + [Example("LineSeries, 3k points, bevel line joins")] + public static PlotModel LineSeries2BevelLineJoins() + { + var model = new PlotModel { Title = "LineSeries, 3k points, bevel line joins", Subtitle = "LineJoin = Bevel" }; + var s1 = new LineSeries { LineJoin = LineJoin.Bevel, StrokeThickness = 8.0 }; + for (int i = 0; i < 3000; i++) + { + s1.Points.Add(new DataPoint(i, i % 2)); + } + + model.Series.Add(s1); + return model; + } + + [Example("ScatterSeries (squares)")] + public static PlotModel ScatterSeriesSquares() + { + var model = new PlotModel { Title = "ScatterSeries (squares)" }; + var s1 = new ScatterSeries(); + AddPoints(s1.Points, 2000); + model.Series.Add(s1); + return model; + } + + [Example("ScatterSeries (squares with outline)")] + public static PlotModel ScatterSeriesSquaresOutline() + { + var model = new PlotModel { Title = "ScatterSeries (squares with outline)", Subtitle = "MarkerStroke = Black" }; + var s1 = new ScatterSeries { MarkerStroke = OxyColors.Black }; + AddPoints(s1.Points, 2000); + model.Series.Add(s1); + return model; + } + + [Example("ScatterSeries (squares without fill color)")] + public static PlotModel ScatterSeriesSquaresOutlineOnly() + { + var model = new PlotModel { Title = "ScatterSeries (squares without fill color)", Subtitle = ";arkerFill = Transparent, MarkerStroke = Black" }; + var s1 = new ScatterSeries { MarkerFill = OxyColors.Transparent, MarkerStroke = OxyColors.Black }; + AddPoints(s1.Points, 2000); + model.Series.Add(s1); + return model; + } + + [Example("ScatterSeries by ItemsSource and reflection")] + public static PlotModel ScatterSeriesItemsSourceReflection() + { + var model = new PlotModel { Title = "ScatterSeries (by ItemsSource)", Subtitle = "DataFieldX = 'X', DataFieldY = 'Y'" }; + model.Series.Add(new ScatterSeries { ItemsSource = GetPoints(2000), DataFieldX = "X", DataFieldY = "Y" }); + return model; + } + + [Example("ScatterSeries (circles)")] + public static PlotModel ScatterSeriesCircles() + { + var model = new PlotModel { Title = "ScatterSeries (circles)", Subtitle = "MarkerType = Circle" }; + var s1 = new ScatterSeries { MarkerType = MarkerType.Circle }; + AddPoints(s1.Points, 2000); + model.Series.Add(s1); + return model; + } + + [Example("ScatterSeries (circles with outline)")] + public static PlotModel ScatterSeriesCirclesOutline() + { + var model = new PlotModel { Title = "ScatterSeries (circles with outline)", Subtitle = "MarkerType = Circle, MarkerStroke = Black" }; + var s1 = new ScatterSeries { MarkerType = MarkerType.Circle, MarkerStroke = OxyColors.Black }; + AddPoints(s1.Points, 2000); + model.Series.Add(s1); + return model; + } + + [Example("ScatterSeries (cross)")] + public static PlotModel ScatterSeriesCrosses() + { + var model = new PlotModel { Title = "ScatterSeries (cross)", Subtitle = "MarkerType = Cross" }; + var s1 = new ScatterSeries + { + MarkerType = MarkerType.Cross, + MarkerFill = OxyColors.Undefined, + MarkerStroke = OxyColors.Black + }; + AddPoints(s1.Points, 2000); + model.Series.Add(s1); + return model; + } + + [Example("LinearAxis (no gridlines)")] + public static PlotModel LinearAxisNoGridlines() + { + var model = new PlotModel { Title = "LinearAxis (no gridlines)" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = 0, Maximum = 100, MajorStep = 1, MinorStep = 1 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = 0, Maximum = 100, MajorStep = 1, MinorStep = 1 }); + return model; + } + + [Example("LinearAxis (solid gridlines)")] + public static PlotModel LinearAxisSolidGridlines() + { + var model = new PlotModel { Title = "LinearAxis (solid gridlines)" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = 0, Maximum = 100, MajorStep = 1, MinorStep = 1, MajorGridlineStyle = LineStyle.Solid }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = 0, Maximum = 100, MajorStep = 1, MinorStep = 1, MajorGridlineStyle = LineStyle.Solid }); + return model; + } + + [Example("LinearAxis (dashed gridlines)")] + public static PlotModel LinearAxisDashedGridlines() + { + var model = new PlotModel { Title = "LinearAxis (dashed gridlines)" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = 0, Maximum = 100, MajorStep = 1, MinorStep = 1, MajorGridlineStyle = LineStyle.Dash }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = 0, Maximum = 100, MajorStep = 1, MinorStep = 1, MajorGridlineStyle = LineStyle.Dash }); + return model; + } + + [Example("LinearAxis (dotted gridlines)")] + public static PlotModel LinearAxisDottedGridlines() + { + var model = new PlotModel { Title = "LinearAxis (dotted gridlines)" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = 0, Maximum = 100, MajorStep = 1, MinorStep = 1, MajorGridlineStyle = LineStyle.Dot }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = 0, Maximum = 100, MajorStep = 1, MinorStep = 1, MajorGridlineStyle = LineStyle.Dot }); + return model; + } + + [Example("int overflow (10k)")] + public static PlotModel IntOverflow10k() + { + return IntOverflow(10000); + } + + [Example("int overflow (50k)")] + public static PlotModel IntOverflow50k() + { + return IntOverflow(50000); + } + + [Example("int overflow (100k)")] + public static PlotModel IntOverflow100k() + { + return IntOverflow(100000); + } + + private static PlotModel IntOverflow(int n) + { + var model = new PlotModel { Title = "int overflow", Subtitle = "n = " + n }; + var ls = new LineSeries(); + int k = 0; + for (int i = 0; i < n; i++) + { + ls.Points.Add(new DataPoint(i, k += i * i)); + } + + model.Series.Add(ls); + return model; + } + + private static List GetPoints(int n) + { + var points = new List(); + AddPoints(points, n); + return points; + } + + private static void AddPoints(ICollection points, int n) + { + for (int i = 0; i < n; i++) + { + double x = Math.PI * 10 * i / (n - 1); + points.Add(new DataPoint(x * Math.Cos(x), x * Math.Sin(x))); + } + } + + private static void AddPoints(ICollection points, int n) + { + for (int i = 0; i < n; i++) + { + double x = Math.PI * 10 * i / (n - 1); + points.Add(new ScatterPoint(x * Math.Cos(x), x * Math.Sin(x))); + } + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Examples/PlotControllerExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Examples/PlotControllerExamples.cs new file mode 100644 index 0000000..0f8217a --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Examples/PlotControllerExamples.cs @@ -0,0 +1,113 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + using System.Linq; + + using OxyPlot; + using OxyPlot.Annotations; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("PlotController examples")] + public static class PlotControllerExamples + { + [Example("Basic controller example")] + public static Example BasicExample() + { + var model = new PlotModel { Title = "Basic Controller example", Subtitle = "Panning with left mouse button" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + var controller = new PlotController(); + controller.UnbindAll(); + controller.BindMouseDown(OxyMouseButton.Left, PlotCommands.PanAt); + return new Example(model, controller); + } + + [Example("Show tracker without clicking")] + public static Example HoverTracking() + { + var model = new PlotModel { Title = "Show tracker without clicking" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + model.Series.Add(new FunctionSeries(t => (Math.Cos(t) * 5) + Math.Cos(t * 50), t => (Math.Sin(t) * 5) + Math.Sin(t * 50), 0, Math.PI * 2, 20000)); + + // create a new plot controller with default bindings + var controller = new PlotController(); + + // add a tracker command to the mouse enter event + controller.BindMouseEnter(PlotCommands.HoverPointsOnlyTrack); + + return new Example(model, controller); + } + + [Example("Mouse handling example")] + public static Example MouseHandlingExample() + { + var model = new PlotModel { Title = "Mouse handling example" }; + var series = new ScatterSeries(); + model.Series.Add(series); + + // Create a command that adds points to the scatter series + var command = new DelegatePlotCommand( + (v, c, a) => + { + a.Handled = true; + var point = series.InverseTransform(a.Position); + series.Points.Add(new ScatterPoint(point.X, point.Y)); + model.InvalidatePlot(true); + }); + + var controller = new PlotController(); + controller.BindMouseDown(OxyMouseButton.Left, command); + + return new Example(model, controller); + } + + [Example("Clicking on an annotation")] + public static Example ClickingOnAnAnnotation() + { + var plotModel = new PlotModel { Title = "Clicking on an annotation", Subtitle = "Click on the rectangles" }; + + plotModel.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + plotModel.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + + var annotation1 = new RectangleAnnotation { Fill = OxyColors.Green, Text = "RectangleAnnotation 1", MinimumX = 25, MaximumX = 75, MinimumY = 20, MaximumY = 40 }; + plotModel.Annotations.Add(annotation1); + + var annotation2 = new RectangleAnnotation { Fill = OxyColors.SkyBlue, Text = "RectangleAnnotation 2", MinimumX = 25, MaximumX = 75, MinimumY = 60, MaximumY = 80 }; + plotModel.Annotations.Add(annotation2); + + EventHandler handleMouseClick = (s, e) => + { + plotModel.Subtitle = "You clicked " + ((RectangleAnnotation)s).Text; + plotModel.InvalidatePlot(false); + }; + + annotation1.MouseDown += handleMouseClick; + annotation2.MouseDown += handleMouseClick; + + var controller = new PlotController(); + var handleClick = new DelegatePlotCommand( + (v, c, e) => + { + var args = new HitTestArguments(e.Position, 10); + var firstHit = v.ActualModel.HitTest(args).FirstOrDefault(x => x.Element is RectangleAnnotation); + if (firstHit != null) + { + e.Handled = true; + plotModel.Subtitle = "You clicked " + ((RectangleAnnotation)firstHit.Element).Text; + plotModel.InvalidatePlot(false); + } + }); + controller.Bind(new OxyMouseDownGesture(OxyMouseButton.Left), handleClick); + + return new Example(plotModel, controller); + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Examples/PlotModelExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Examples/PlotModelExamples.cs new file mode 100644 index 0000000..da5d4f8 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Examples/PlotModelExamples.cs @@ -0,0 +1,193 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("PlotModel examples")] + public static class PlotModelExamples + { + [Example("Title")] + public static PlotModel Title() + { + var model = new PlotModel { Title = "Title" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + return model; + } + + [Example("Title and Subtitle")] + public static PlotModel TitleAndSubtitle() + { + var model = new PlotModel { Title = "Title", Subtitle = "Subtitle" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + return model; + } + + [Example("Sub- and superscript in titles")] + public static PlotModel TitleAndSubtitleWithSubSuperscript() + { + var model = new PlotModel { Title = "Title with^{super}_{sub}script", Subtitle = "Subtitle with^{super}_{sub}script" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + return model; + } + + [Example("TitlePadding = 0")] + public static PlotModel TitlePadding0() + { + var model = new PlotModel { Title = "TitlePadding = 0", Subtitle = "This controls the distance between the titles and the plot area. The default value is 6", TitlePadding = 0 }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + return model; + } + + [Example("TitlePadding = 100")] + public static PlotModel TitlePadding100() + { + var model = new PlotModel { Title = "TitlePadding = 100", TitlePadding = 100 }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + return model; + } + + [Example("TitleHorizontalAlignment = CenteredWithinView")] + public static PlotModel TitlesCenteredWithinView() + { + var model = new PlotModel { Title = "Title", Subtitle = "Subtitle", TitleHorizontalAlignment = TitleHorizontalAlignment.CenteredWithinView }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + return model; + } + + [Example("PlotMargins = (100,20,100,50)")] + public static PlotModel PlotMargins() + { + var model = new PlotModel { Title = "PlotMargins = (100,20,100,50)", PlotMargins = new OxyThickness(100, 20, 100, 50) }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + return model; + } + + [Example("No model")] + public static PlotModel NoModel() + { + return null; + } + + [Example("Background = Undefined (default)")] + public static PlotModel BackgroundUndefined() + { + var model = new PlotModel { Title = "Background = Undefined", Background = OxyColors.Undefined }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + return model; + } + + [Example("Background = 50% White")] + public static PlotModel BackgroundWhite50() + { + var model = new PlotModel { Title = "Background = 50% White", Background = OxyColor.FromAColor(128, OxyColors.White) }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + return model; + } + + [Example("Background = Transparent")] + public static PlotModel BackgroundTransparent() + { + var model = new PlotModel { Title = "Background = Transparent", Background = OxyColors.Transparent }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + return model; + } + + [Example("Background = LightSkyBlue")] + public static PlotModel BackgroundLightGray() + { + var model = new PlotModel { Title = "Background = LightSkyBlue", Background = OxyColors.LightSkyBlue }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + return model; + } + + [Example("Background = White")] + public static PlotModel BackgroundWhite() + { + var model = new PlotModel { Title = "Background = White", Background = OxyColors.White }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + return model; + } + + [Example("Background = Black")] + public static PlotModel BackgroundBlack() + { + var model = new PlotModel { Title = "Background = Black", Background = OxyColors.Black, TextColor = OxyColors.White, TitleColor = OxyColors.White, PlotAreaBorderColor = OxyColors.White }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, TicklineColor = OxyColors.White }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, TicklineColor = OxyColors.White }); + return model; + } + + [Example("PlotAreaBorderThickness = 2")] + public static PlotModel PlotAreaBorderThickness2() + { + var model = new PlotModel { Title = "PlotAreaBorderThickness = 2", PlotAreaBorderThickness = new OxyThickness(2) }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + return model; + } + + [Example("PlotAreaBorderThickness = (1,0,0,1)")] + public static PlotModel PlotAreaBorderThickness1001() + { + var model = new PlotModel { Title = "PlotAreaBorderThickness = (1,0,0,1)", PlotAreaBorderThickness = new OxyThickness(1, 0, 0, 1) }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + return model; + } + + [Example("PlotAreaBorderThickness = (4,1,1,4)")] + public static PlotModel PlotAreaBorderThickness4114() + { + var model = new PlotModel { Title = "PlotAreaBorderThickness = (4,1,1,4)", PlotAreaBorderThickness = new OxyThickness(4, 1, 1, 4) }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + return model; + } + + [Example("PlotAreaBorderThickness = 0")] + public static PlotModel PlotAreaBorderThickness0() + { + var model = new PlotModel { Title = "PlotAreaBorderThickness = 0", PlotAreaBorderThickness = new OxyThickness(0) }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + return model; + } + + [Example("PlotAreaBorderThickness / AxisLine")] + public static PlotModel PlotAreaBorderThickness0AxisLineThickness1() + { + var model = new PlotModel { Title = "PlotAreaBorderThickness = 0", Subtitle = "AxislineThickness = 1, AxislineColor = OxyColors.Blue, AxislineStyle = LineStyle.Solid", PlotAreaBorderThickness = new OxyThickness(0) }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, AxislineThickness = 1, AxislineColor = OxyColors.Blue, AxislineStyle = LineStyle.Solid }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, AxislineThickness = 1, AxislineColor = OxyColors.Blue, AxislineStyle = LineStyle.Solid }); + return model; + } + + [Example("Exception handling (invalid XAxisKey)")] + public static PlotModel InvalidAxisKey() + { + var model = new PlotModel(); + model.Axes.Add(new LinearAxis()); + model.Series.Add(new LineSeries { XAxisKey = "invalidKey" }); + return model; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Examples/RenderingCapabilities.cs b/OxyPlot/Source/Examples/ExampleLibrary/Examples/RenderingCapabilities.cs new file mode 100644 index 0000000..d18b2c0 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Examples/RenderingCapabilities.cs @@ -0,0 +1,442 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Provides rendering capability examples. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + using System.Diagnostics; + using System.Globalization; + + using OxyPlot; + using OxyPlot.Annotations; + using System.Linq; + + /// + /// Provides rendering capability examples. + /// + [Examples("9 Rendering capabilities")] + public class RenderingCapabilities + { + /// + /// Shows color capabilities for the DrawText method. + /// + /// A plot model. + [Example("DrawText - Colors")] + public static PlotModel DrawTextColors() + { + var model = new PlotModel(); + model.Annotations.Add(new DelegateAnnotation(rc => + { + const string Font = "Arial"; + const double FontSize = 32d; + const double FontWeight = FontWeights.Bold; + const double D = FontSize * 1.6; + const double X = 20; + double y = 20 - D; + + + rc.DrawText(new ScreenPoint(X, y += D), "Black", OxyColors.Black, Font, FontSize, FontWeight); + rc.DrawText(new ScreenPoint(X, y += D), "Red", OxyColors.Red, Font, FontSize, FontWeight); + rc.DrawText(new ScreenPoint(X, y += D), "Green", OxyColors.Green, Font, FontSize, FontWeight); + rc.DrawText(new ScreenPoint(X, y += D), "Blue", OxyColors.Blue, Font, FontSize, FontWeight); + + rc.FillRectangle(new OxyRect(X, y + D + 15, 200, 10), OxyColors.Black); + rc.DrawText(new ScreenPoint(X, y + D), "Yellow 50%", OxyColor.FromAColor(128, OxyColors.Yellow), Font, FontSize, FontWeight); + })); + return model; + } + + /// + /// Shows font capabilities for the DrawText method. + /// + /// A plot model. + [Example("DrawText - Fonts")] + public static PlotModel DrawTextFonts() + { + var model = new PlotModel(); + model.Annotations.Add(new DelegateAnnotation(rc => + { + const double FontSize = 20d; + const double D = FontSize * 1.6; + const double X = 20; + double y = 20 - D; + + rc.DrawText(new ScreenPoint(X, y += D), "Default font", OxyColors.Black, null, FontSize); + rc.DrawText(new ScreenPoint(X, y += D), "Helvetica", OxyColors.Black, "Helvetica", FontSize); + rc.DrawText(new ScreenPoint(X, y += D), "Arial", OxyColors.Black, "Arial", FontSize); + rc.DrawText(new ScreenPoint(X, y += D), "Courier", OxyColors.Black, "Courier", FontSize); + rc.DrawText(new ScreenPoint(X, y += D), "Courier New", OxyColors.Black, "Courier New", FontSize); + rc.DrawText(new ScreenPoint(X, y += D), "Times", OxyColors.Black, "Times", FontSize); + rc.DrawText(new ScreenPoint(X, y + D), "Times New Roman", OxyColors.Black, "Times New Roman", FontSize); + })); + return model; + } + + /// + /// Shows font size capabilities for the DrawText method. + /// + /// A plot model. + [Example("DrawText - Font sizes")] + public static PlotModel DrawTextFontSizes() + { + var model = new PlotModel(); + model.Annotations.Add(new DelegateAnnotation(rc => + { + const double X = 20; + double y = 20; + + // Font sizes + foreach (var size in new[] { 10, 16, 24, 36, 48 }) + { + rc.DrawText(new ScreenPoint(X, y), size + "pt", OxyColors.Black, "Arial", size); + rc.DrawText(new ScreenPoint(X + 200, y), size + "pt", OxyColors.Black, "Arial", size, FontWeights.Bold); + y += size * 1.6; + } + })); + return model; + } + + /// + /// Shows rotation capabilities for the DrawText method. + /// + /// A plot model. + [Example("DrawText - Rotation")] + public static PlotModel DrawTextRotation() + { + var model = new PlotModel(); + model.Annotations.Add(new DelegateAnnotation(rc => + { + var origin = new ScreenPoint(200, 200); + rc.FillCircle(origin, 3, OxyColors.Blue); + for (int rotation = 0; rotation < 360; rotation += 45) + { + rc.DrawText(origin, string.Format("Rotation {0}", rotation), OxyColors.Black, fontSize: 20d, rotation: rotation); + } + })); + return model; + } + + /// + /// Shows alignment capabilities for the DrawText method. + /// + /// A plot model. + [Example("DrawText - Alignment")] + public static PlotModel DrawTextAlignment() + { + var model = new PlotModel(); + model.Annotations.Add(new DelegateAnnotation(rc => + { + const double FontSize = 20d; + + for (var ha = HorizontalAlignment.Left; ha <= HorizontalAlignment.Right; ha++) + { + for (var va = VerticalAlignment.Top; va <= VerticalAlignment.Bottom; va++) + { + var origin = new ScreenPoint((((int)ha + 1) * 200) + 20, (((int)va + 1) * FontSize * 3) + 20); + rc.FillCircle(origin, 3, OxyColors.Blue); + rc.DrawText(origin, ha + "-" + va, OxyColors.Black, fontSize: FontSize, horizontalAlignment: ha, verticalAlignment: va); + } + } + })); + return model; + } + + /// + /// Shows rotation capabilities for the DrawMathText method. + /// + /// A plot model. + [Example("DrawMathText - Rotation")] + public static PlotModel MathTextRotation() + { + var model = new PlotModel(); + var fontFamily = "Arial"; + var fontSize = 24; + var fontWeight = FontWeights.Normal; + model.Annotations.Add(new DelegateAnnotation(rc => + { + var origin = new ScreenPoint(200, 200); + var origin2 = new ScreenPoint(400, 200); + rc.FillCircle(origin, 3, OxyColors.Blue); + for (int rotation = 0; rotation < 360; rotation += 45) + { + var text = " A_{2}^{3}B"; + rc.DrawMathText(origin, text, OxyColors.Black, fontFamily, fontSize, fontWeight, rotation, HorizontalAlignment.Left, VerticalAlignment.Middle); + var size = rc.MeasureMathText(text, fontFamily, fontSize, fontWeight); + var outline1 = size.GetPolygon(origin, rotation, HorizontalAlignment.Left, VerticalAlignment.Middle).ToArray(); + rc.DrawPolygon(outline1, OxyColors.Undefined, OxyColors.Blue); + + // Compare with normal text + var text2 = " A B"; + rc.DrawText(origin2, text2, OxyColors.Red, fontFamily, fontSize, fontWeight, rotation, HorizontalAlignment.Left, VerticalAlignment.Middle); + var size2 = rc.MeasureText(text2, fontFamily, fontSize, fontWeight); + var outline2 = size2.GetPolygon(origin2, rotation, HorizontalAlignment.Left, VerticalAlignment.Middle).ToArray(); + rc.DrawPolygon(outline2, OxyColors.Undefined, OxyColors.Blue); + } + })); + return model; + } + + /// + /// Shows alignment capabilities for the DrawMathText method. + /// + /// A plot model. + [Example("DrawMathText - Alignment")] + public static PlotModel DrawMathTextAlignment() + { + var text = "A_{2}^{3}B"; + var model = new PlotModel(); + model.Annotations.Add(new DelegateAnnotation(rc => + { + const string FontFamily = "Arial"; + const double FontSize = 20d; + const double FontWeight = FontWeights.Normal; + + for (var ha = HorizontalAlignment.Left; ha <= HorizontalAlignment.Right; ha++) + { + for (var va = VerticalAlignment.Top; va <= VerticalAlignment.Bottom; va++) + { + var origin = new ScreenPoint((((int)ha + 1) * 200) + 20, (((int)va + 1) * FontSize * 3) + 20); + rc.FillCircle(origin, 3, OxyColors.Blue); + rc.DrawMathText(origin, text, OxyColors.Black, FontFamily, FontSize, FontWeight, 0, ha, va); + } + } + })); + return model; + } + + /// + /// Shows alignment capabilities for the DrawText method. + /// + /// A plot model. + [Example("DrawText - Alignment/Rotation")] + public static PlotModel DrawTextAlignmentRotation() + { + var model = new PlotModel(); + model.Annotations.Add(new DelegateAnnotation(rc => + { + for (var ha = HorizontalAlignment.Left; ha <= HorizontalAlignment.Right; ha++) + { + for (var va = VerticalAlignment.Top; va <= VerticalAlignment.Bottom; va++) + { + var origin = new ScreenPoint(((int)ha + 2) * 130, ((int)va + 2) * 130); + rc.FillCircle(origin, 3, OxyColors.Blue); + for (int rotation = 0; rotation < 360; rotation += 90) + { + rc.DrawText(origin, string.Format("R{0:000}", rotation), OxyColors.Black, fontSize: 20d, rotation: rotation, horizontalAlignment: ha, verticalAlignment: va); + } + } + } + })); + return model; + } + + /// + /// Shows color capabilities for the DrawText method. + /// + /// A plot model. + [Example("DrawText - MaxSize")] + public static PlotModel DrawTextMaxSize() + { + var model = new PlotModel(); + model.Annotations.Add(new DelegateAnnotation(rc => + { + const string Font = "Arial"; + const double FontSize = 32d; + const double FontWeight = FontWeights.Bold; + const double D = FontSize * 1.6; + const double X = 20; + const double X2 = 200; + double y = 20; + var testStrings = new[] { "iii", "jjj", "OxyPlot", "Bottom", "100", "KML" }; + foreach (var text in testStrings) + { + var maxSize = rc.MeasureText(text, Font, FontSize, FontWeight); + var p = new ScreenPoint(X, y); + rc.DrawText(p, text, OxyColors.Black, Font, FontSize, FontWeight, maxSize: maxSize); + var rect = new OxyRect(p, maxSize); + rc.DrawRectangle(rect, OxyColors.Undefined, OxyColors.Black); + + var p2 = new ScreenPoint(X2, y); + var maxSize2 = new OxySize(maxSize.Width / 2, maxSize.Height / 2); + rc.DrawText(p2, text, OxyColors.Black, Font, FontSize, FontWeight, maxSize: maxSize2); + var rect2 = new OxyRect(p2, maxSize2); + rc.DrawRectangle(rect2, OxyColors.Undefined, OxyColors.Black); + + y += D; + } + })); + return model; + } + + /// + /// Draws text and shows marks for ascent/descent/baseline/x-height and the expected bounding box. + /// + /// A plot model. + [Example("DrawText - WPF metrics")] + public static PlotModel DrawTextWithWpfMetrics() + { + return DrawTextWithMetrics("OxyPlot", "Arial", 60, 226, 69, 105, 73, 61, 116, 23, 228, "WPF"); + } + + /// + /// Draws text and shows marks for ascent/descent/baseline/x-height and the expected bounding box. + /// + /// A plot model. + [Example("DrawText - WinForms metrics (StringFormat = GenericDefault)")] + public static PlotModel DrawTextWithWinFormsMetricsDefault() + { + return DrawTextWithMetrics("OxyPlot", "Arial", 60, 252.145812988281, 79.4999847412109, 108, 73, 61, 121, 34, 252, "WinForms"); + } + + /// + /// Draws text and shows marks for ascent/descent/baseline/x-height and the expected bounding box. + /// + /// A plot model. + [Example("DrawText - WinForms metrics (StringFormat = GenericTypographic)")] + public static PlotModel DrawTextWithWinFormsMetricsTypographic() + { + return DrawTextWithMetrics("OxyPlot", "Arial", 60, 224.1, 71.5, 108, 73, 61, 121, 23, 242, "WinForms"); + } + + /// + /// Shows capabilities for the MeasureText method. + /// + /// A plot model. + [Example("MeasureText")] + public static PlotModel MeasureText() + { + var model = new PlotModel(); + model.Annotations.Add(new DelegateAnnotation(rc => + { + const string Font = "Arial"; + var strings = new[] { "OxyPlot", "MMM", "III", "jikq", "gh", "123", "!#$&" }; + var fontSizes = new[] { 10d, 20, 40, 60 }; + var x = 5d; + foreach (double fontSize in fontSizes) + { + var y = 5d; + var maxWidth = 0d; + foreach (var s in strings) + { + var size = rc.MeasureText(s, Font, fontSize); + maxWidth = Math.Max(maxWidth, size.Width); + rc.DrawRectangle(new OxyRect(x, y, size.Width, size.Height), OxyColors.LightYellow, OxyColors.Black); + rc.DrawText(new ScreenPoint(x, y), s, OxyColors.Black, Font, fontSize); + y += size.Height + 20; + } + + x += maxWidth + 20; + } + })); + return model; + } + + /// + /// Draws text with metrics. + /// + /// The text. + /// The font. + /// Size of the font. + /// The expected width. + /// The expected height. + /// The baseline position. + /// The x-height position. + /// The ascent position. + /// The descent position. + /// The before position. + /// The after position. + /// The platform. + /// + /// A plot model. + /// + private static PlotModel DrawTextWithMetrics(string text, string font, double fontSize, double expectedWidth, double expectedHeight, double baseline, double xheight, double ascent, double descent, double before, double after, string platform) + { + // http://msdn.microsoft.com/en-us/library/ms742190(v=vs.110).aspx + // http://msdn.microsoft.com/en-us/library/xwf9s90b(v=vs.110).aspx + // http://msdn.microsoft.com/en-us/library/windows/desktop/ms533824(v=vs.85).aspx + // https://developer.apple.com/library/mac/documentation/TextFonts/Conceptual/CocoaTextArchitecture/FontHandling/FontHandling.html + var model = new PlotModel(); + model.Annotations.Add( + new DelegateAnnotation( + rc => + { + var size = rc.MeasureText(text, font, fontSize); + var expectedSize = new OxySize(expectedWidth, expectedHeight); + rc.DrawText(new ScreenPoint(300, 50), "Font size: " + fontSize, OxyColors.Black, font, 12); + rc.DrawText(new ScreenPoint(300, 70), "Actual size: " + size.ToString("0.00", CultureInfo.InvariantCulture), OxyColors.Black, font, 12); + rc.DrawText(new ScreenPoint(300, 90), "Size on " + platform + ": " + expectedSize.ToString("0.00", CultureInfo.InvariantCulture), OxyColors.Green, font, 12); + + var p = new ScreenPoint(20, 50); + rc.DrawText(p, text, OxyColors.Black, font, fontSize); + + rc.FillCircle(p, 3, OxyColors.Black); + + // actual bounds + rc.DrawRectangle(new OxyRect(p, size), OxyColors.Undefined, OxyColors.Black); + + // Expected bounds (WPF) + rc.DrawRectangle(new OxyRect(p, expectedSize), OxyColors.Undefined, OxyColors.Green); + + var color = OxyColor.FromAColor(180, OxyColors.Red); + var pen = new OxyPen(color); + + // Expected vertical positions (WPF) + var x1 = p.X - 10; + var x2 = p.X + expectedSize.Width + 10; + rc.DrawLine(x1, baseline, x2, baseline, pen); + rc.DrawLine(x1, xheight, x2, xheight, pen); + rc.DrawLine(x1, ascent, x2, ascent, pen); + rc.DrawLine(x1, descent, x2, descent, pen); + + // Expected horizonal positions (WPF) + var y1 = p.Y - 10; + var y2 = p.Y + expectedSize.Height + 10; + rc.DrawLine(before, y1, before, y2, pen); + rc.DrawLine(after, y1, after, y2, pen); + })); + + model.MouseDown += (s, e) => Debug.WriteLine(e.Position); + + return model; + } + + /// + /// Represents an annotation that renders by a delegate. + /// + private class DelegateAnnotation : Annotation + { + /// + /// Initializes a new instance of the class. + /// + /// The rendering delegate. + public DelegateAnnotation(Action rendering) + { + this.Rendering = rendering; + } + + /// + /// Gets the rendering delegate. + /// + /// + /// The rendering. + /// + public Action Rendering { get; private set; } + + /// + /// Renders the annotation on the specified context. + /// + /// The render context. + public override void Render(IRenderContext rc) + { + base.Render(rc); + this.Rendering(rc); + } + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Examples/TrackerExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Examples/TrackerExamples.cs new file mode 100644 index 0000000..2b59352 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Examples/TrackerExamples.cs @@ -0,0 +1,55 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + + using OxyPlot; + using OxyPlot.Series; + + [Examples("Tracker")] + public static class TrackerExamples + { + [Example("No interpolation")] + public static PlotModel NoInterpolation() + { + var model = new PlotModel { Title = "No tracker interpolation", Subtitle = "Used for discrete values or scatter plots.", LegendSymbolLength = 30 }; + var s1 = new LineSeries + { + Title = "Series 1", + CanTrackerInterpolatePoints = false, + Color = OxyColors.SkyBlue, + MarkerType = MarkerType.Circle, + MarkerSize = 6, + MarkerStroke = OxyColors.White, + MarkerFill = OxyColors.SkyBlue, + MarkerStrokeThickness = 1.5 + }; + for (int i = 0; i < 63; i++) + { + s1.Points.Add(new DataPoint((int)(Math.Sqrt(i) * Math.Cos(i * 0.1)), (int)(Math.Sqrt(i) * Math.Sin(i * 0.1)))); + } + + model.Series.Add(s1); + + return model; + } + + [Example("TrackerChangedEvent")] + public static PlotModel TrackerChangedEvent() + { + var model = new PlotModel { Title = "Handling the TrackerChanged event", Subtitle = "Press the left mouse button to test the tracker." }; + model.Series.Add(new FunctionSeries(Math.Sin, 0, 10, 100)); + model.TrackerChanged += (s, e) => + { + model.Subtitle = e.HitResult != null ? "Tracker item index = " + e.HitResult.Index : "Not tracking"; + model.InvalidatePlot(false); + }; + return model; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Issues/Issues.cs b/OxyPlot/Source/Examples/ExampleLibrary/Issues/Issues.cs new file mode 100644 index 0000000..4b76f56 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Issues/Issues.cs @@ -0,0 +1,1969 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + using System.Collections.Generic; + using System.Globalization; + using System.Threading; + using System.Threading.Tasks; + + using OxyPlot; + using OxyPlot.Annotations; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("Z1 Issues")] + public class Issues + { + [Example("#977 RectangleAnnotation Axis Clipping not configurable")] + public static PlotModel RectangleAnnotationAxisClipping() + { + var model = new PlotModel + { + Title = "RectangleAnnotation Axis Clipping", + PlotAreaBorderThickness = new OxyThickness(0), + Axes = + { + new LinearAxis + { + Position = AxisPosition.Bottom, + AxislineStyle = LineStyle.Solid, + EndPosition = 0.45 + }, + new LinearAxis + { + Position = AxisPosition.Bottom, + AxislineStyle = LineStyle.Solid, + StartPosition = 0.55, + Key = "X2" + }, + new LinearAxis + { + Position = AxisPosition.Left, + AxislineStyle = LineStyle.Solid, + EndPosition = 0.45, + }, + new LinearAxis + { + Position = AxisPosition.Left, + AxislineStyle = LineStyle.Solid, + StartPosition = 0.55, + Key = "Y2" + } + }, + Annotations = + { + new LineAnnotation + { + Type = LineAnnotationType.Vertical, + Color = OxyColors.DarkCyan, + StrokeThickness = 2, + LineStyle = LineStyle.Solid, + X = 10, + Text = "LineAnnotation (default clipping)" + }, + new LineAnnotation + { + Type = LineAnnotationType.Vertical, + Color = OxyColors.DarkGreen, + StrokeThickness = 2, + LineStyle = LineStyle.Solid, + X = 20, + ClipByYAxis = false, + Text = "LineAnnotation (ClipByYAxis = false)", + TextLinePosition = 0.5 + }, + new RectangleAnnotation + { + Fill = OxyColor.FromArgb(100, 255, 0, 0), + Stroke = OxyColors.Black, + StrokeThickness = 1, + MinimumX = 40, + MaximumX = 60, + Text = "RectangleAnnotation (default clipping)", + TextRotation = -90, + }, + new RectangleAnnotation + { + Fill = OxyColor.FromArgb(100, 0, 0, 255), + Stroke = OxyColors.Black, + StrokeThickness = 1, + MinimumX = 70, + MaximumX = 90, + ClipByYAxis = false, + Text = "RectangleAnnotation (ClipByYAxis = false)", + TextRotation = -90 + }, + new RectangleAnnotation + { + Fill = OxyColor.FromArgb(100, 0, 255, 0), + Stroke = OxyColors.Black, + StrokeThickness = 1, + MinimumY = 80, + MaximumY = 85, + Text = "RectangleAnnotation (default clipping)", + XAxisKey = "X2", + YAxisKey = "Y2" + }, + new RectangleAnnotation + { + Fill = OxyColor.FromArgb(100, 0, 255, 0), + Stroke = OxyColors.Black, + StrokeThickness = 1, + MinimumY = 90, + MaximumY = 95, + ClipByXAxis = false, + Text = "RectangleAnnotation (ClipByXAxis = false)", + XAxisKey = "X2", + YAxisKey = "Y2" + }, + new RectangleAnnotation + { + Fill = OxyColor.FromArgb(50, 100, 100, 100), + Stroke = OxyColors.Black, + StrokeThickness = 1, + MinimumX = 92, MaximumX = 140, + MinimumY = 45, MaximumY = 140, + ClipByXAxis = false, ClipByYAxis = false, + Text = "no clipping at all" + } + } + }; + return model; + } + + + [Example("Support colour coding on scatter plots (Closed)")] + public static PlotModel ColorCodingOnScatterPlots() + { + var model = new PlotModel { Title = "Colour coding on scatter plots" }; + var colorAxis = new LinearColorAxis { Position = AxisPosition.Right, Palette = OxyPalettes.Jet(500), Minimum = 0, Maximum = 5, HighColor = OxyColors.Gray, LowColor = OxyColors.Black }; + model.Axes.Add(colorAxis); + + var s4 = new ScatterSeries { MarkerType = MarkerType.Circle }; + s4.Points.Add(new ScatterPoint(3, 5, 5, 0)); + s4.Points.Add(new ScatterPoint(5, 5, 7, 0)); + s4.Points.Add(new ScatterPoint(2, 4, 5, 0.3)); + s4.Points.Add(new ScatterPoint(3, 3, 8, 0)); + s4.Points.Add(new ScatterPoint(3, 2, 5, 0)); + s4.Points.Add(new ScatterPoint(3, 5, 8, 1)); + s4.Points.Add(new ScatterPoint(2, 2, 3, 5)); + s4.Points.Add(new ScatterPoint(1, 4, 4, 1)); + s4.Points.Add(new ScatterPoint(4, 3, 5, 3)); + s4.Points.Add(new ScatterPoint(0, 0, 1, 1)); + s4.Points.Add(new ScatterPoint(8, 8, 1, 1)); + model.Series.Add(s4); + return model; + } + + [Example("Don't show minor ticks (Closed)")] + public static PlotModel DontShowMinorTicks() + { + var model = new PlotModel { Title = "MinorTickSize = 0" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, MinorTickSize = 0, MajorGridlineStyle = LineStyle.Solid, MinorGridlineStyle = LineStyle.Solid }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, MinorTickSize = 0, MajorGridlineStyle = LineStyle.Solid, MinorGridlineStyle = LineStyle.Solid }); + return model; + } + + /// + /// Grids the lines both different colors. + /// + [Example("Major grid lines in front of minor (Closed)")] + public static PlotModel GridLinesBothDifferentColors() + { + var plotModel1 = new PlotModel + { + Title = "Major grid lines in front of minor", + Subtitle = "Minor grid lines should be below major grid lines" + }; + var leftAxis = new LinearAxis + { + MajorGridlineStyle = LineStyle.Solid, + MajorGridlineColor = OxyColors.Black, + MajorGridlineThickness = 4, + MinorGridlineStyle = LineStyle.Solid, + MinorGridlineColor = OxyColors.LightBlue, + MinorGridlineThickness = 4, + }; + plotModel1.Axes.Add(leftAxis); + var bottomAxis = new LinearAxis + { + Position = AxisPosition.Bottom, + MajorGridlineStyle = LineStyle.Solid, + MajorGridlineColor = OxyColors.Black, + MajorGridlineThickness = 4, + MinorGridlineStyle = LineStyle.Solid, + MinorGridlineColor = OxyColors.LightBlue, + MinorGridlineThickness = 4, + }; + plotModel1.Axes.Add(bottomAxis); + return plotModel1; + } + + [Example("#50: Sub/superscript in vertical axis title")] + public static PlotModel SubSuperScriptInAxisTitles() + { + var plotModel1 = new PlotModel { Title = "x_{i}^{j}", Subtitle = "x_{i}^{j}" }; + var leftAxis = new LinearAxis { Position = AxisPosition.Left, Title = "x_{i}^{j}" }; + plotModel1.Axes.Add(leftAxis); + var bottomAxis = new LinearAxis { Position = AxisPosition.Bottom, Title = "x_{i}^{j}" }; + plotModel1.Axes.Add(bottomAxis); + plotModel1.Series.Add(new FunctionSeries(Math.Sin, 0, 10, 100, "x_{i}^{j}")); + return plotModel1; + } + + [Example("#50: Sub/superscript in rotated annotations")] + public static PlotModel RotatedSubSuperScript() + { + var s = "x_{A}^{B}"; + var plotModel1 = new PlotModel { Title = s, Subtitle = s }; + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = s, Minimum = -1, Maximum = 1 }); + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Title = s, Minimum = -1, Maximum = 11 }); + for (int rotation = 0; rotation < 360; rotation += 45) + { + plotModel1.Annotations.Add(new TextAnnotation { Text = s, TextPosition = new DataPoint(rotation / 360d * 10, 0), TextRotation = rotation }); + } + + return plotModel1; + } + + [Example("#61: DateTimeAxis with IntervalType = Minutes")] + public static PlotModel DateTimeAxisWithIntervalTypeMinutes() + { + var plotModel1 = new PlotModel(); + var linearAxis1 = new LinearAxis(); + plotModel1.Axes.Add(linearAxis1); + + var dateTimeAxis1 = new DateTimeAxis + { + IntervalType = DateTimeIntervalType.Minutes, + EndPosition = 0, + StartPosition = 1, + StringFormat = "hh:mm:ss" + }; + plotModel1.Axes.Add(dateTimeAxis1); + var time0 = new DateTime(2013, 5, 6, 3, 24, 0); + var time1 = new DateTime(2013, 5, 6, 3, 28, 0); + var lineSeries1 = new LineSeries(); + lineSeries1.Points.Add(new DataPoint(DateTimeAxis.ToDouble(time0), 36)); + lineSeries1.Points.Add(new DataPoint(DateTimeAxis.ToDouble(time1), 26)); + plotModel1.Series.Add(lineSeries1); + return plotModel1; + } + + [Example("#67: Hit testing LineSeries with smoothing")] + public static PlotModel MouseDownEvent() + { + var model = new PlotModel { Title = "LineSeries with smoothing", Subtitle = "Tracker uses wrong points" }; + var logarithmicAxis1 = new LogarithmicAxis { Position = AxisPosition.Bottom }; + model.Axes.Add(logarithmicAxis1); + + // Add a line series + var s1 = new LineSeries + { + Color = OxyColors.SkyBlue, + MarkerType = MarkerType.Circle, + MarkerSize = 6, + MarkerStroke = OxyColors.White, + MarkerFill = OxyColors.SkyBlue, + MarkerStrokeThickness = 1.5, + InterpolationAlgorithm = InterpolationAlgorithms.CanonicalSpline + }; + s1.Points.Add(new DataPoint(100, 100)); + s1.Points.Add(new DataPoint(400, 200)); + s1.Points.Add(new DataPoint(600, -300)); + s1.Points.Add(new DataPoint(1000, 400)); + s1.Points.Add(new DataPoint(1500, 500)); + s1.Points.Add(new DataPoint(2500, 600)); + s1.Points.Add(new DataPoint(3000, 700)); + model.Series.Add(s1); + + return model; + } + + [Example("#68: Tracker wrong for logarithmic y-axis")] + public static PlotModel ValueTime() + { + var plotModel1 = new PlotModel + { + LegendBackground = OxyColor.FromArgb(200, 255, 255, 255), + LegendBorder = OxyColors.Black, + LegendPlacement = LegendPlacement.Outside, + PlotAreaBackground = OxyColors.Gray, + PlotAreaBorderColor = OxyColors.Gainsboro, + PlotAreaBorderThickness = new OxyThickness(2), + Title = "Value / Time" + }; + var linearAxis1 = new LinearAxis + { + AbsoluteMaximum = 45, + AbsoluteMinimum = 0, + Key = "X-Axis", + Maximum = 46, + Minimum = -1, + Position = AxisPosition.Bottom, + Title = "Years", + Unit = "yr" + }; + plotModel1.Axes.Add(linearAxis1); + var logarithmicAxis1 = new LogarithmicAxis { Key = "Y-Axis", Title = "Value for section" }; + plotModel1.Axes.Add(logarithmicAxis1); + var lineSeries1 = new LineSeries + { + Color = OxyColors.Red, + LineStyle = LineStyle.Solid, + MarkerFill = OxyColors.Black, + MarkerSize = 2, + MarkerStroke = OxyColors.Black, + MarkerType = MarkerType.Circle, + DataFieldX = "X", + DataFieldY = "Y", + XAxisKey = "X-Axis", + YAxisKey = "Y-Axis", + Background = OxyColors.White, + Title = "Section Value", + TrackerKey = "ValueVersusTimeTracker" + }; + lineSeries1.Points.Add(new DataPoint(0, 0)); + lineSeries1.Points.Add(new DataPoint(5, 0)); + lineSeries1.Points.Add(new DataPoint(10, 0)); + lineSeries1.Points.Add(new DataPoint(15, 0)); + lineSeries1.Points.Add(new DataPoint(20, 1)); + lineSeries1.Points.Add(new DataPoint(25, 1)); + lineSeries1.Points.Add(new DataPoint(30, 1)); + lineSeries1.Points.Add(new DataPoint(35, 1)); + lineSeries1.Points.Add(new DataPoint(40, 1)); + lineSeries1.Points.Add(new DataPoint(45, 1)); + plotModel1.Series.Add(lineSeries1); + return plotModel1; + } + + [Example("AnnotationLayers (Closed)")] + public static PlotModel AnnotationLayers() + { + var model = new PlotModel { Title = "AnnotationLayers" }; + + var a1 = new RectangleAnnotation { MinimumX = 10, MaximumX = 20, MinimumY = -1, MaximumY = 1, Layer = AnnotationLayer.BelowAxes }; + var a2 = new RectangleAnnotation { MinimumX = 30, MaximumX = 40, MinimumY = -1, MaximumY = 1, Layer = AnnotationLayer.BelowSeries }; + var a3 = new RectangleAnnotation { MinimumX = 50, MaximumX = 60, MinimumY = -1, MaximumY = 1, Layer = AnnotationLayer.AboveSeries }; + model.Annotations.Add(a1); + model.Annotations.Add(a2); + model.Annotations.Add(a3); + var s1 = new FunctionSeries(Math.Sin, 0, 100, 0.01); + model.Series.Add(s1); + a1.MouseDown += (s, e) => + { + model.Subtitle = "Clicked annotation below axes"; + model.InvalidatePlot(true); + e.Handled = true; + }; + a2.MouseDown += (s, e) => + { + model.Subtitle = "Clicked annotation below series"; + model.InvalidatePlot(true); + e.Handled = true; + }; + a3.MouseDown += (s, e) => + { + model.Subtitle = "Clicked annotation above series"; + model.InvalidatePlot(true); + e.Handled = true; + }; + s1.MouseDown += (s, e) => + { + model.Subtitle = "Clicked series"; + model.InvalidatePlot(true); + e.Handled = true; + }; + + return model; + } + + [Example("Argument out of range in OxyPlot mouse over (Closed)")] + public static PlotModel ArgumentOutOfRangeInMouseOver() + { + var model = new PlotModel { Title = "Argument out of range in OxyPlot mouse over" }; + var ls = new LineSeries(); + ls.Points.Add(new DataPoint(10, 10)); + ls.Points.Add(new DataPoint(10, 10)); + ls.Points.Add(new DataPoint(12, 10)); + model.Series.Add(ls); + return model; + } + + [Example("Slow redraws with noisy data (Closed)")] + public static PlotModel NoisyData() + { + var model = new PlotModel { Title = "Noisy data" }; + + var points = new List(); + var rng = new Random(7); + for (int i = 0; i < 500; i++) + { + points.Add(new DataPoint(i + 1, rng.NextDouble())); + } + + model.Series.Add(new LineSeries { ItemsSource = points }); + return model; + } + + [Example("Dashed line test (Closed)")] + public static PlotModel DashedLineTest() + { + var model = new PlotModel { Title = "Dashed line test" }; + + for (int y = 1; y <= 24; y++) + { + var line = new LineSeries + { + StrokeThickness = y, + LineStyle = LineStyle.Dash, + Dashes = new double[] { 1, 2, 3 } // has no effect + }; + for (int i = 0; i < 20; i++) + { + line.Points.Add(new DataPoint(i + 1, y)); + } + + model.Series.Add(line); + } + + return model; + } + + [Example("Super exponential format (Closed)")] + public static PlotModel SuperExponentialFormat() + { + var model = new PlotModel { Title = "UseSuperExponentialFormat=true and 0" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = 0, Maximum = 100, MajorStep = 10, MinorStep = 1, UseSuperExponentialFormat = true }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = -100, Maximum = 100, MajorStep = 20, MinorStep = 10, UseSuperExponentialFormat = true }); + return model; + } + + [Example("AreaSeries draws on top of other elements (Closed)")] + public static PlotModel DefaultAnnotationLayer() + { + var plotModel1 = new PlotModel { Title = "Annotations should be drawn on top by default", Subtitle = "The line annotation should be on top!" }; + var areaSeries1 = new AreaSeries(); + areaSeries1.Points.Add(new DataPoint(0, 50)); + areaSeries1.Points.Add(new DataPoint(10, 40)); + areaSeries1.Points.Add(new DataPoint(20, 60)); + areaSeries1.Points2.Add(new DataPoint(0, 60)); + areaSeries1.Points2.Add(new DataPoint(5, 80)); + areaSeries1.Points2.Add(new DataPoint(20, 70)); + areaSeries1.Color = OxyColors.Red; + areaSeries1.Color2 = OxyColors.Blue; + areaSeries1.Fill = OxyColors.Yellow; + + plotModel1.Series.Add(areaSeries1); + var lineAnnotation = new LineAnnotation + { + Type = LineAnnotationType.Vertical, + Layer = AnnotationLayer.AboveSeries, + X = 6 + }; + + plotModel1.Annotations.Add(lineAnnotation); + return plotModel1; + } + + [Example("#79: LegendItemAlignment = Center (closed)")] + public static PlotModel LegendItemAlignmentCenter() + { + var plotModel1 = new PlotModel { Title = "LegendItemAlignment = Center" }; + plotModel1.LegendItemAlignment = HorizontalAlignment.Center; + plotModel1.LegendBorder = OxyColors.Black; + plotModel1.LegendBorderThickness = 1; + plotModel1.Series.Add(new FunctionSeries(x => Math.Sin(x) / x, 0, 10, 100, "sin(x)/x")); + plotModel1.Series.Add(new FunctionSeries(Math.Cos, 0, 10, 100, "cos(x)")); + return plotModel1; + } + + [Example("AreaSeries should respect CanTrackerInterpolatePoints (Closed)")] + public static PlotModel AreaSeries_CanTrackerInterpolatePointsFalse() + { + var plotModel1 = new PlotModel { Title = "AreaSeries with CanTrackerInterpolatePoints=false" }; + var areaSeries1 = new AreaSeries { CanTrackerInterpolatePoints = false }; + areaSeries1.Points.Add(new DataPoint(0, 50)); + areaSeries1.Points.Add(new DataPoint(10, 40)); + areaSeries1.Points.Add(new DataPoint(20, 60)); + areaSeries1.Points2.Add(new DataPoint(0, 60)); + areaSeries1.Points2.Add(new DataPoint(5, 80)); + areaSeries1.Points2.Add(new DataPoint(20, 70)); + plotModel1.Series.Add(areaSeries1); + return plotModel1; + } + + [Example("AreaSeries should respect CanTrackerInterpolatePoints=true (Closed)")] + public static PlotModel AreaSeries_CanTrackerInterpolatePointsTrue() + { + var plotModel1 = new PlotModel { Title = "AreaSeries with CanTrackerInterpolatePoints=true" }; + var areaSeries1 = new AreaSeries { CanTrackerInterpolatePoints = true }; + areaSeries1.Points.Add(new DataPoint(0, 50)); + areaSeries1.Points.Add(new DataPoint(10, 40)); + areaSeries1.Points.Add(new DataPoint(20, 60)); + areaSeries1.Points2.Add(new DataPoint(0, 60)); + areaSeries1.Points2.Add(new DataPoint(5, 80)); + areaSeries1.Points2.Add(new DataPoint(20, 70)); + plotModel1.Series.Add(areaSeries1); + return plotModel1; + } + + [Example("GetNearestPoint return DataPoint even when custom IDataPoint used (closed)")] + public static PlotModel GetNearestPointReturnsDataPoint() + { + var plotModel1 = new PlotModel { Title = "GetNearestPoint" }; + //// TODO: add code to reproduce + return plotModel1; + } + + [Example("#102: Selecting points changes the legend colours")] + public static PlotModel SelectingPointsChangesTheLegendColors() + { + var plotModel1 = new PlotModel { Title = "Selecting points changes the legend colours" }; + //// TODO: add code to reproduce + return plotModel1; + } + + [Example("Empty LineSeries with smoothing (Closed)")] + public static PlotModel EmptyLineSeriesWithSmoothing_ThrowsException() + { + var plotModel1 = new PlotModel { Title = "Empty LineSeries with smoothing" }; + plotModel1.Series.Add(new LineSeries + { + InterpolationAlgorithm = InterpolationAlgorithms.CanonicalSpline + }); + return plotModel1; + } + + [Example("#119: Data points remain visible outside of bounds on panning")] + public static PlotModel DataPointsRemainVisibleOutsideBoundsOnPanning() + { + var plotModel1 = new PlotModel(); + + var masterAxis = new DateTimeAxis { Key = "MasterDateTimeAxis", Position = AxisPosition.Bottom }; + plotModel1.Axes.Add(masterAxis); + + var verticalAxis = new LinearAxis + { + Position = AxisPosition.Left, + Title = "Measurement", + Key = "Measurement", + AbsoluteMinimum = -100, + Minimum = -100, + AbsoluteMaximum = 100, + Maximum = 100, + IsZoomEnabled = false, + IsPanEnabled = false + }; + + plotModel1.Axes.Add(verticalAxis); + + var line = new LineSeries { Title = "Measurement", XAxisKey = masterAxis.Key, YAxisKey = verticalAxis.Key }; + line.Points.Add(new DataPoint(DateTimeAxis.ToDouble(DateTime.Now), 10)); + line.Points.Add(new DataPoint(DateTimeAxis.ToDouble(DateTime.Now.AddSeconds(1)), 10)); + line.Points.Add(new DataPoint(DateTimeAxis.ToDouble(DateTime.Now.AddSeconds(2)), 45)); + line.Points.Add(new DataPoint(DateTimeAxis.ToDouble(DateTime.Now.AddSeconds(3)), 17)); + + line.Points.Add(DataPoint.Undefined); + + // this point should be visible + line.Points.Add(new DataPoint(DateTimeAxis.ToDouble(DateTime.Now.AddSeconds(4)), 10)); + //// line.Points.Add(new DataPoint(DateTimeAxis.ToDouble(DateTime.Now.AddSeconds(4)), 10)); + + line.Points.Add(DataPoint.Undefined); + + line.Points.Add(new DataPoint(DateTimeAxis.ToDouble(DateTime.Now.AddSeconds(5)), 45)); + line.Points.Add(new DataPoint(DateTimeAxis.ToDouble(DateTime.Now.AddSeconds(6)), 17)); + + plotModel1.Series.Add(line); + + return plotModel1; + } + + [Example("Floating-point inaccuracy (Closed)")] + public static PlotModel FloatingPointInaccuracy() + { + var model = new PlotModel { Title = "Floating-point inaccuracy" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = -0.0515724495834661, Maximum = 0.016609368598352, MajorStep = 0.02, MinorStep = 0.002 }); + return model; + } + + [Example("LineSeries.Dashes property (Closed)")] + public static PlotModel DashesTest() + { + var model = new PlotModel { Title = "Dashed line test" }; + + for (int y = 1; y <= 10; y++) + { + var line = new LineSeries + { + StrokeThickness = y, + Dashes = new double[] { 1, 2, 3 } + }; + for (int i = 0; i < 20; i++) + { + line.Points.Add(new DataPoint(i + 1, y)); + } + + model.Series.Add(line); + } + + return model; + } + + [Example("ScatterSeries and LinearColorAxis on the same plot (Closed)")] + public static PlotModel ScatterSeriesAndLinearColorAxis() + { + var plotModel = new PlotModel { Title = "ScatterSeries and LinearColorAxis on the same plot" }; + int npoints = 100; + var random = new Random(); + + var scatterSeries = new ScatterSeries { ColorAxisKey = string.Empty }; + for (var i = 0; i < npoints; i++) + { + scatterSeries.Points.Add(new ScatterPoint((double)i / npoints, random.NextDouble())); + } + + plotModel.Series.Add(scatterSeries); + + var lineSeries = new LineSeries(); + for (var i = 0; i < npoints; i++) + { + lineSeries.Points.Add(new DataPoint((double)i / npoints, random.NextDouble())); + } + + plotModel.Series.Add(lineSeries); + + plotModel.Axes.Add(new LinearColorAxis()); + return plotModel; + } + + [Example("#133: MinorStep should not be MajorStep/5 when MajorStep is 2")] + public static PlotModel MinorTicks() + { + var plotModel1 = new PlotModel { Title = "Issue 10117" }; + plotModel1.Axes.Add(new LinearAxis { Minimum = 0, Maximum = 16 }); + return plotModel1; + } + + [Example("Scatterseries not rendered at specific plot sizes (closed)")] + public static PlotModel ScatterSeries() + { + var plotModel1 = new PlotModel + { + Title = "Scatterseries not rendered at specific plot sizes", + PlotMargins = new OxyThickness(50, 5, 5, 50), + Padding = new OxyThickness(0), + PlotAreaBorderThickness = new OxyThickness(1, 1, 1, 1), + PlotAreaBorderColor = OxyColors.Black, + TextColor = OxyColors.Black, + LegendOrientation = LegendOrientation.Horizontal, + LegendPosition = LegendPosition.TopRight, + LegendMargin = 0 + }; + plotModel1.Axes.Add(new LinearAxis + { + IsAxisVisible = true, + Title = "X", + Position = AxisPosition.Bottom, + TickStyle = TickStyle.Outside, + TicklineColor = OxyColors.Black, + Minimum = 0, + MaximumPadding = 0.05 + }); + plotModel1.Axes.Add(new LogarithmicAxis + { + MinimumPadding = 0.05, + MaximumPadding = 0.1, + Title = "Y", + Position = AxisPosition.Left, + TickStyle = TickStyle.Outside, + TicklineColor = OxyColors.Black, + MajorGridlineColor = OxyColors.Black, + MajorGridlineStyle = LineStyle.Solid + }); + var referenceCurve = new LineSeries + { + Title = "Reference", + InterpolationAlgorithm = InterpolationAlgorithms.CanonicalSpline, + Color = OxyColor.FromArgb(255, 89, 128, 168) + }; + var upperBoundary = new LineSeries + { + LineStyle = LineStyle.Dot, + Color = OxyColors.LightGray, + InterpolationAlgorithm = InterpolationAlgorithms.CanonicalSpline, + Title = string.Empty + }; + + var lowerBoundary = new LineSeries + { + LineStyle = LineStyle.Dot, + Color = OxyColors.LightGray, + InterpolationAlgorithm = InterpolationAlgorithms.CanonicalSpline, + Title = "+/- 15 %" + }; + + // Series that holds and formats points inside of the boundary + var inBoundaryResultLine = new ScatterSeries + { + Title = "actual", + MarkerFill = OxyColors.Black, + MarkerSize = 4, + MarkerStroke = OxyColors.White, + MarkerType = MarkerType.Circle + }; + + // Series that holds and formats points outside of the boundary + var outBoundaryResultLine = new ScatterSeries + { + Title = "not permissible deviation", + MarkerFill = OxyColors.Red, + MarkerSize = 4, + MarkerStroke = OxyColors.White, + MarkerType = MarkerType.Circle + }; + + // Just some random data to fill the series: + var referenceValues = new[] + { + double.NaN, 0.985567558024852, 0.731704530257957, 0.591109071735532, 0.503627816316065, 0.444980686815776, + 0.403576666032678, 0.373234299823915, 0.350375591667333, 0.332795027566349, 0.319063666439909, + 0.30821748743148, 0.299583943726489, 0.292680371378706, 0.287151885046283, 0.282732008216725, + 0.279216923371711, 0.276557880999918 + }; + var actualValues = new[] + { + double.NaN, 0.33378346040897, 1.09868427497967, 0.970771068054048, 0.739778217457323, 0.582112938330166, + 0.456962500853806, 0.37488740614826, 0.330272509496142, 0.334461549522006, 0.30989175806678, + 0.286944862053553, 0.255895385950234, 0.231850970296068, 0.217579897050944, 0.217113227224437, + 0.164759946945322, 0.0459134254747994 + }; + + for (var index = 0; index <= 17; index++) + { + var referenceValue = referenceValues[index]; + var lowerBound = referenceValue - (referenceValue * 0.15); + var upperBound = referenceValue + (referenceValue * 0.15); + referenceCurve.Points.Add(new DataPoint(index, referenceValue)); + lowerBoundary.Points.Add(new DataPoint(index, lowerBound)); + upperBoundary.Points.Add(new DataPoint(index, upperBound)); + + var actualValue = actualValues[index]; + if (actualValue > lowerBound && actualValue < upperBound) + { + inBoundaryResultLine.Points.Add(new ScatterPoint(index, actualValue)); + } + else + { + outBoundaryResultLine.Points.Add(new ScatterPoint(index, actualValue)); + } + } + + plotModel1.Series.Add(referenceCurve); + plotModel1.Series.Add(lowerBoundary); + plotModel1.Series.Add(upperBoundary); + plotModel1.Series.Add(outBoundaryResultLine); + plotModel1.Series.Add(inBoundaryResultLine); + + return plotModel1; + } + + [Example("ScatterSeries with invalid point and marker type circle (closed)")] + public static PlotModel ScatterSeriesWithInvalidPointAndMarkerTypeCircle() + { + var plotModel1 = new PlotModel + { + Title = "ScatterSeries with invalid point and marker type circle", + }; + plotModel1.Series.Add(new ScatterSeries + { + MarkerType = MarkerType.Circle, + ItemsSource = new[] { new ScatterPoint(0, double.NaN), new ScatterPoint(0, 0) } + }); + return plotModel1; + } + + [Example("RectangleBarSeries rendered on top layer (rejected)")] + public static PlotModel RectangleBarSeriesRenderedOnTopLayer() + { + var plotModel1 = new PlotModel + { + Title = "RectangleBarSeries rendered on top layer", + }; + var lineSeries1 = new LineSeries(); + lineSeries1.Points.Add(new DataPoint(0, 1)); + lineSeries1.Points.Add(new DataPoint(1, 0)); + plotModel1.Series.Add(lineSeries1); + var rectangleBarSeries1 = new RectangleBarSeries(); + rectangleBarSeries1.Items.Add(new RectangleBarItem(0.25, 0.25, 0.75, 0.75)); + plotModel1.Series.Add(rectangleBarSeries1); + var lineSeries2 = new LineSeries(); + lineSeries2.Points.Add(new DataPoint(0, 0)); + lineSeries2.Points.Add(new DataPoint(1, 1)); + plotModel1.Series.Add(lineSeries2); + return plotModel1; + } + + [Example("Legend is not visible (closed)")] + public static PlotModel LegendIsNotVisible() + { + var plotModel = new PlotModel + { + Title = "Legend is not visible", + }; + plotModel.Series.Add(new LineSeries { Title = "LineSeries 1" }); + plotModel.Series.Add(new LineSeries { Title = "LineSeries 2" }); + plotModel.Series.Add(new LineSeries { Title = "LineSeries 3" }); + plotModel.IsLegendVisible = true; + plotModel.LegendPlacement = LegendPlacement.Inside; + plotModel.LegendPosition = LegendPosition.RightMiddle; + plotModel.LegendOrientation = LegendOrientation.Vertical; + return plotModel; + } + + [Example("#189: Wrong position of titles when PositionAtZeroCrossing is true")] + public static PlotModel PositionAtZeroCrossing() + { + var plotModel1 = new PlotModel { PlotType = PlotType.Cartesian, Title = "Zero Crossing Diagram", Subtitle = "The titles should be shown next to the axes" }; + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, AxislineStyle = LineStyle.Solid, PositionAtZeroCrossing = true, Title = "horizontal axis" }); + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Left, AxislineStyle = LineStyle.Solid, PositionAtZeroCrossing = true, Title = "vertical axis" }); + plotModel1.Series.Add(new FunctionSeries(x => Math.Cos(x * Math.PI / 180.0) * 2, x => Math.Sin(x * Math.PI / 180.0) * 2, 0.0, 180.0, 1.0) + { + Color = OxyColors.Red + }); + + return plotModel1; + } + + [Example("#189: PositionAtZeroCrossing and no plot area border")] + public static PlotModel PositionAtZeroCrossingNoPlotBorder() + { + var pm = PositionAtZeroCrossing(); + pm.PlotAreaBorderThickness = new OxyThickness(0); + pm.Subtitle = "The axis lines should be drawn when the origin is outside the plot area."; + return pm; + } + + [Example("#185: Wrong plot margins when Angle = 90 (LinearAxis)")] + public static PlotModel PlotMarginsLinearAxisWhenAxisAngleIs90() + { + var plotModel1 = new PlotModel { Title = "Plot margins not adjusted correctly when Angle = 90", Subtitle = "The numbers should not be clipped" }; + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Angle = 90, Minimum = 1e8, Maximum = 1e9 }); + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Angle = 90, Minimum = 1e8, Maximum = 1e9 }); + return plotModel1; + } + + [Example("#185: Wrong plot margins when Angle = 90 (DateTimeAxis)")] + public static PlotModel PlotMarginsDateTimeAxisWhenAxisAngleIs90() + { + var plotModel1 = new PlotModel { Title = "Plot margins not adjusted correctly when Angle = 90", Subtitle = "The numbers should not be clipped" }; + plotModel1.Axes.Add(new DateTimeAxis { Position = AxisPosition.Bottom, Angle = 90 }); + plotModel1.Axes.Add(new DateTimeAxis { Position = AxisPosition.Left, Angle = 90 }); + return plotModel1; + } + + [Example("#301: Wrong label placement for category axis when Angle = 45 (closed)")] + public static PlotModel LabelPlacementCategoryAxisWhenAxisAngleIs45() + { + var plotModel1 = new PlotModel { Title = "Wrong label placement for category axis when Angle = 45", Subtitle = "The labels should not be clipped. Click on text annotation to change the angle." }; + + Action createAxis = (AxisPosition position) => + { + var categoryAxis = new CategoryAxis() { Position = position, Angle = 45 }; + + categoryAxis.Labels.Add("Very looooong and big label"); + categoryAxis.Labels.Add("Very looooong and big label"); + categoryAxis.Labels.Add("Very looooong and big label"); + categoryAxis.Labels.Add("Very looooong and big label"); + plotModel1.Axes.Add(categoryAxis); + }; + + createAxis(AxisPosition.Bottom); + createAxis(AxisPosition.Left); + createAxis(AxisPosition.Right); + createAxis(AxisPosition.Top); + + var textAnnotation = new TextAnnotation() { Text = "Hold mouse button here to increase angle", TextPosition = new DataPoint(0, 6), TextHorizontalAlignment = HorizontalAlignment.Left, TextVerticalAlignment = VerticalAlignment.Top }; + plotModel1.Annotations.Add(textAnnotation); + + var abort = new ManualResetEvent(false); + + Action action = () => + { + do + { + // Angles are the same for all axes. + double angle = 0; + + foreach (var axis in plotModel1.Axes) + { + angle = (axis.Angle + 181) % 360 - 180; + axis.Angle = angle; + } + + plotModel1.Subtitle = string.Format("Current angle is {0}", angle); + plotModel1.InvalidatePlot(false); + } + while (!abort.WaitOne(50)); + }; + + textAnnotation.MouseDown += (o, e) => { abort.Reset(); Task.Factory.StartNew(action); }; + plotModel1.MouseUp += (o, e) => { abort.Set(); }; + + var columnSeries = new ColumnSeries(); + columnSeries.Items.Add(new ColumnItem(5)); + columnSeries.Items.Add(new ColumnItem(3)); + columnSeries.Items.Add(new ColumnItem(7)); + columnSeries.Items.Add(new ColumnItem(2)); + plotModel1.Series.Add(columnSeries); + return plotModel1; + } + + [Example("#180: Two vertical axes on the same position")] + public static PlotModel TwoVerticalAxisOnTheSamePosition() + { + var plotModel1 = new PlotModel { Title = "Two vertical axes on the same position", Subtitle = "The titles should overlap here!" }; + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = "First axis" }); + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = "Second axis" }); + return plotModel1; + } + + [Example("#180: Two vertical axis on the same position (Start/EndPosition)")] + public static PlotModel TwoVerticalAxisOnTheSamePositionStartEndPosition() + { + var plotModel1 = new PlotModel { Title = "Two vertical axes on the same position with different StartPosition/EndPosition", Subtitle = "The titles should be centered on the axes" }; + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Left, StartPosition = 0, EndPosition = 0.4, Title = "First axis" }); + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Left, StartPosition = 0.6, EndPosition = 1, Title = "Second axis" }); + return plotModel1; + } + + [Example("#180: Two vertical axis on the same position (PositionTier)")] + public static PlotModel TwoVerticalAxisOnTheSamePositionStartEndPositionPositionTier() + { + var plotModel1 = new PlotModel { Title = "Two vertical axes on the same position with different PositionTier", Subtitle = "The titles should be centered and not overlapping" }; + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Left, PositionTier = 0, Title = "First axis" }); + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Left, PositionTier = 1, Title = "Second axis", AxislineStyle = LineStyle.Solid }); + return plotModel1; + } + + [Example("#220: Tracker strings not correctly showing date/times (closed)")] + public static PlotModel TrackerStringsNotCorrectlySHowingDateTimes() + { + var plotModel1 = new PlotModel { Title = "Tracker strings not correctly showing date/times" }; + plotModel1.Axes.Add(new DateTimeAxis { Position = AxisPosition.Bottom, Title = "Date" }); + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = "Quantity" }); + var ls = new LineSeries { TrackerFormatString = "{1}: {2:d-M-yyyy}\n{3}: {4:0.00}", MarkerType = MarkerType.Circle }; + var t0 = new DateTime(2014, 10, 16); + for (int i = 0; i < 20; i++) + { + ls.Points.Add(new DataPoint(DateTimeAxis.ToDouble(t0.AddDays(i)), 13 + Math.IEEERemainder(i, 7))); + } + + plotModel1.Series.Add(ls); + return plotModel1; + } + + [Example("#226: LineSeries exception when smoothing")] + public static PlotModel LineSeriesExceptionWhenSmoothing() + { + var plotModel1 = new PlotModel + { + Title = "LineSeries null reference exception when smoothing is enabled and all datapoints have the same y value", + Subtitle = "Click on the plot to reproduce the issue." + }; + var ls = new LineSeries + { + InterpolationAlgorithm = InterpolationAlgorithms.CanonicalSpline, + }; + ls.Points.Add(new DataPoint(0, 0)); + ls.Points.Add(new DataPoint(1, 0)); + ls.Points.Add(new DataPoint(10, 0)); + plotModel1.Series.Add(ls); + return plotModel1; + } + + [Example("#79: Center aligned legends (closed)")] + public static PlotModel CenterAlignedLegends() + { + var plotModel1 = new PlotModel + { + Title = "Center aligned legends", + LegendPosition = LegendPosition.BottomCenter, + LegendItemAlignment = HorizontalAlignment.Center + }; + plotModel1.Series.Add(new LineSeries { Title = "LineSeries 1" }); + plotModel1.Series.Add(new LineSeries { Title = "LS2" }); + return plotModel1; + } + + [Example("#356: Draw legend line with custom pattern")] + public static PlotModel LegendWithCustomPattern() + { + var plotModel1 = new PlotModel + { + Title = "Draw legend line with custom pattern", + }; + var solid = new LineSeries + { + Title = "Solid", + LineStyle = LineStyle.Solid + // without dashes + }; + var custom = new LineSeries + { + Title = "Custom", + LineStyle = LineStyle.Solid, + // dashd-dot pattern + Dashes = new[] { 10.0, 2.0, 4.0, 2.0 }, + }; + solid.Points.Add(new DataPoint(0, 2)); + solid.Points.Add(new DataPoint(100, 1)); + custom.Points.Add(new DataPoint(0, 3)); + custom.Points.Add(new DataPoint(100, 2)); + plotModel1.Series.Add(solid); + plotModel1.Series.Add(custom); + plotModel1.LegendSymbolLength = 100; // wide enough to see pattern + return plotModel1; + } + + [Example("#409: ImageAnnotation width width/height crashes")] + public static PlotModel ImageAnnotationWithWidthHeightCrashes() + { + var myModel = new PlotModel { Title = "Example 1" }; + myModel.Series.Add(new FunctionSeries(Math.Cos, 0, 10, 0.1, "cos(x)")); + + var rng = new Random(); + var buf = new byte[100, 100]; + for (int i = 0; i < 100; i++) + { + for (int j = 0; j < 100; j++) + { + buf[i, j] = (byte)rng.Next(); + } + } + + var palette = new OxyColor[256]; + for (int i = 0; i < palette.Length; i++) + { + palette[i] = OxyColor.FromArgb(128, (byte)i, 0, 0); + } + + var image = OxyImage.Create(buf, palette, ImageFormat.Bmp); + myModel.Annotations.Add(new ImageAnnotation + { + ImageSource = image, + + X = new PlotLength(1, PlotLengthUnit.Data), + Y = new PlotLength(0, PlotLengthUnit.Data), + Width = new PlotLength(1, PlotLengthUnit.Data), + Height = new PlotLength(1, PlotLengthUnit.Data) + }); + + myModel.Annotations.Add(new ImageAnnotation + { + ImageSource = image, + + X = new PlotLength(5, PlotLengthUnit.Data), + Y = new PlotLength(0, PlotLengthUnit.Data), + }); + + return myModel; + } + + [Example("#413: HeatMap tracker format string")] + public static PlotModel HeatMapTrackerFormatString() + { + var plotModel1 = new PlotModel + { + Title = "HeatMap tracker format string", + }; + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Left, StringFormat = "0.000", Minimum = 0, Maximum = 1 }); + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, StringFormat = "0.000", Minimum = 0, Maximum = 1 }); + plotModel1.Axes.Add(new LinearColorAxis { Position = AxisPosition.Right, Minimum = 0, Maximum = 5 }); + var data = new double[,] { { 1, 2 }, { 3, 4 } }; + plotModel1.Series.Add(new HeatMapSeries + { + Data = data, + CoordinateDefinition = HeatMapCoordinateDefinition.Edge, + X0 = 0.1, + X1 = 0.9, + Y0 = 0.1, + Y1 = 0.9, + TrackerFormatString = "{0}\n{1}: {2:0.000}\n{3}: {4:0.000}\n{5}: {6:0.0000}" + }); + return plotModel1; + } + + [Example("#413: Using axis format strings in tracker")] + public static PlotModel AxisFormatStringInTracker() + { + var plotModel1 = new PlotModel + { + Title = "Using axis format strings in tracker", + }; + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Left, StringFormat = "0.000", Minimum = 0, Maximum = 1 }); + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, StringFormat = "0.000", Minimum = 0, Maximum = 1 }); + plotModel1.Axes.Add(new LinearColorAxis { Position = AxisPosition.Right, Minimum = 0, Maximum = 5 }); + var data = new double[,] { { 1, 2 }, { 3, 4 } }; + plotModel1.Series.Add(new HeatMapSeries + { + Data = data, + CoordinateDefinition = HeatMapCoordinateDefinition.Edge, + X0 = 0.1, + X1 = 0.9, + Y0 = 0.1, + Y1 = 0.9, + + // IDEA: add new arguments for axis formatted values + // TODO: this will throw an exception, argument 7 and 8 is not implemented + TrackerFormatString = "{0}\n{1}: {7}\n{3}: {8}\n{5}: {6:0.0000}" + }); + return plotModel1; + } + + [Example("#408: CategoryAxis label clipped on left margin")] + public static PlotModel CategoryAxisLabelClipped() + { + var plotModel1 = new PlotModel + { + Title = "CategoryAxis label clipped on left margin", + }; + var axis = new CategoryAxis { Position = AxisPosition.Left, Angle = -52 }; + axis.Labels.Add("Very very very very long label"); + axis.Labels.Add("Short label"); + axis.Labels.Add("Short label"); + axis.Labels.Add("Short label"); + plotModel1.Axes.Add(axis); + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + return plotModel1; + } + + [Example("#402: ColumnSeries with dates")] + public static PlotModel ColumnSeriesWithDates() + { + var plotModel1 = new PlotModel + { + Title = "ColumnSeries with dates", + Culture = CultureInfo.InvariantCulture + }; + var data = new[] + { + new TimeValue { Time = new DateTime(2015, 1, 1), Value = 700 }, + new TimeValue { Time = new DateTime(2015, 1, 2), Value = 710 }, + new TimeValue { Time = new DateTime(2015, 1, 3), Value = 580}, + new TimeValue { Time = new DateTime(2015, 1, 4), Value = 710 }, + new TimeValue { Time = new DateTime(2015, 1, 5), Value = 715 }, + new TimeValue { Time = new DateTime(2015, 1, 6), Value = 580 }, + }; + + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = 0, Maximum = 1000 }); + plotModel1.Axes.Add(new CategoryAxis { ItemsSource = data, LabelField = "Time", StringFormat = "ddd" }); + plotModel1.Series.Add(new ColumnSeries { ItemsSource = data, ValueField = "Value" }); + return plotModel1; + } + + private class TimeValue + { + public DateTime Time { get; set; } + public double Value { get; set; } + } + + [Example("#474: Vertical Axis Title Font Bug")] + public static PlotModel VerticalAxisTitleFontBug() + { + var plotModel1 = new PlotModel + { + Title = "Vertical Axis Title Font Bug", + }; + + plotModel1.Axes.Add(new LinearAxis + { + Title = "X_Axe", + Position = AxisPosition.Bottom, + MajorGridlineStyle = LineStyle.Solid, + TitleFont = "Times New Roman" + }); + + plotModel1.Axes.Add(new LinearAxis + { + Title = "Y_Axe", + Position = AxisPosition.Left, + MajorGridlineStyle = LineStyle.Solid, + TitleFont = "Times New Roman" + }); + + return plotModel1; + } + + + [Example("#535: Transposed HeatMap")] + public static PlotModel TransposedHeatMap() + { + int n = 100; + + double x0 = -3.1; + double x1 = 3.1; + double y0 = -3; + double y1 = 3; + Func peaks = (x, y) => 3 * (1 - x) * (1 - x) * Math.Exp(-(x * x) - (y + 1) * (y + 1)) - 10 * (x / 5 - x * x * x - y * y * y * y * y) * Math.Exp(-x * x - y * y) - 1.0 / 3 * Math.Exp(-(x + 1) * (x + 1) - y * y); + var xvalues = ArrayBuilder.CreateVector(x0, x1, n); + var yvalues = ArrayBuilder.CreateVector(y0, y1, n); + var peaksData = ArrayBuilder.Evaluate(peaks, xvalues, yvalues); + + var model = new PlotModel { Title = "Normal Heatmap" }; + + model.Axes.Add( + new LinearAxis() { Key = "x_axis", AbsoluteMinimum = x0, AbsoluteMaximum = x1, Position = AxisPosition.Left }); + + model.Axes.Add( + new LinearAxis() { Key = "y_axis", AbsoluteMinimum = y0, AbsoluteMaximum = y1, Position = AxisPosition.Top }); + + model.Axes.Add(new LinearColorAxis { Position = AxisPosition.Right, Palette = OxyPalettes.Jet(500), HighColor = OxyColors.Gray, LowColor = OxyColors.Black }); + + var hms = new HeatMapSeries + { + X0 = x0, + X1 = x1, + Y0 = y0, + Y1 = y1, + Data = peaksData, + XAxisKey = "x_axis", + YAxisKey = "y_axis" + }; + model.Series.Add(hms); + + return model; + } + + [Example("#535: Normal HeatMap")] + public static PlotModel NormalHeatMap() + { + int n = 100; + + double x0 = -3.1; + double x1 = 3.1; + double y0 = -3; + double y1 = 3; + Func peaks = (x, y) => 3 * (1 - x) * (1 - x) * Math.Exp(-(x * x) - (y + 1) * (y + 1)) - 10 * (x / 5 - x * x * x - y * y * y * y * y) * Math.Exp(-x * x - y * y) - 1.0 / 3 * Math.Exp(-(x + 1) * (x + 1) - y * y); + var xvalues = ArrayBuilder.CreateVector(x0, x1, n); + var yvalues = ArrayBuilder.CreateVector(y0, y1, n); + var peaksData = ArrayBuilder.Evaluate(peaks, xvalues, yvalues); + + var model = new PlotModel { Title = "Peaks" }; + + model.Axes.Add( + new LinearAxis() { Key = "x_axis", AbsoluteMinimum = x0, AbsoluteMaximum = x1, Position = AxisPosition.Top }); + + model.Axes.Add( + new LinearAxis() { Key = "y_axis", AbsoluteMinimum = y0, AbsoluteMaximum = y1, Position = AxisPosition.Left }); + + model.Axes.Add(new LinearColorAxis { Position = AxisPosition.Right, Palette = OxyPalettes.Jet(500), HighColor = OxyColors.Gray, LowColor = OxyColors.Black }); + + var hms = new HeatMapSeries + { + X0 = x0, + X1 = x1, + Y0 = y0, + Y1 = y1, + Data = peaksData, + XAxisKey = "x_axis", + YAxisKey = "y_axis" + }; + model.Series.Add(hms); + + return model; + } + + [Example("#1065: LinearColorAxis Title")] + public static PlotModel ColorAxisTitle() + { + int n = 100; + + double x0 = -3.1; + double x1 = 3.1; + double y0 = -3; + double y1 = 3; + Func peaks = (x, y) => 3 * (1 - x) * (1 - x) * Math.Exp(-(x * x) - (y + 1) * (y + 1)) - 10 * (x / 5 - x * x * x - y * y * y * y * y) * Math.Exp(-x * x - y * y) - 1.0 / 3 * Math.Exp(-(x + 1) * (x + 1) - y * y); + var xvalues = ArrayBuilder.CreateVector(x0, x1, n); + var yvalues = ArrayBuilder.CreateVector(y0, y1, n); + var peaksData = ArrayBuilder.Evaluate(peaks, xvalues, yvalues); + + var model = new PlotModel { Title = "Peaks" }; + + model.Axes.Add(new LinearAxis() { Key = "x_axis", AbsoluteMinimum = x0, AbsoluteMaximum = x1, Position = AxisPosition.Top }); + + model.Axes.Add(new LinearAxis() { Key = "y_axis", AbsoluteMinimum = y0, AbsoluteMaximum = y1, Position = AxisPosition.Left }); + + model.Axes.Add(new LinearColorAxis { Title = "wrong Placement", Position = AxisPosition.Right, Palette = OxyPalettes.Jet(500), HighColor = OxyColors.Gray, LowColor = OxyColors.Black }); + + var hms = new HeatMapSeries + { + X0 = x0, + X1 = x1, + Y0 = y0, + Y1 = y1, + Data = peaksData, + XAxisKey = "x_axis", + YAxisKey = "y_axis" + }; + model.Series.Add(hms); + + return model; + } + + /// + /// Contains example code for https://github.com/oxyplot/oxyplot/issues/42 + /// + /// The plot model. + [Example("#42: ContourSeries not working for not square data array")] + public static PlotModel IndexOutOfRangeContour() + { + var model = new PlotModel { Title = "Issue #42" }; + model.Axes.Add(new LinearColorAxis { Position = AxisPosition.Right, Palette = OxyPalettes.Jet(5) }); + + var x = ArrayBuilder.CreateVector(0, 1, 20); + var y = ArrayBuilder.CreateVector(-1, 1, 2); + var data = ArrayBuilder.Evaluate((a, b) => a * b, x, y); + + var contour = new ContourSeries + { + ColumnCoordinates = y, + RowCoordinates = x, + Data = data + }; + model.Series.Add(contour); + + return model; + } + + [Example("#624: Rendering math text with syntax error gets stuck in an endless loop")] + public static PlotModel MathTextWithSyntaxError() + { + var model = new PlotModel { Title = "Math text syntax errors" }; + model.Series.Add(new LineSeries { Title = "x_{1" }); + model.Series.Add(new LineSeries { Title = "x^{2" }); + model.Series.Add(new LineSeries { Title = "x^{2_{1" }); + model.Series.Add(new LineSeries { Title = "x^{ x^" }); + model.Series.Add(new LineSeries { Title = "x_{ x_" }); + model.Series.Add(new LineSeries { Title = "" }); + model.Series.Add(new LineSeries { Title = "x^{ x_{ x^_" }); + return model; + } + + [Example("#19: The minimum value is not mentioned on the axis I")] + public static PlotModel MinimumValueOnAxis() + { + var model = new PlotModel { Title = "Show minimum and maximum values on axis" }; + model.Axes.Add(new LinearAxis + { + Position = AxisPosition.Left, + //ShowMinimumValue = true, + //ShowMaximumValue = true, + //MinimumValueStringFormat = "0.###", + //MaximumValueStringFormat = "0.###", + MaximumPadding = 0, + MinimumPadding = 0 + }); + model.Axes.Add(new LinearAxis + { + Position = AxisPosition.Bottom, + //ShowMinimumValue = true, + //ShowMaximumValue = true, + //MinimumValueStringFormat = "0.###", + //MaximumValueStringFormat = "0.###", + MaximumPadding = 0, + MinimumPadding = 0 + }); + var ls = new LineSeries(); + ls.Points.Add(new DataPoint(0.14645, 0.14645)); + ls.Points.Add(new DataPoint(9.85745, 9.85745)); + model.Series.Add(ls); + return model; + } + + [Example("#19: The minimum value is not mentioned on the axis II")] + public static PlotModel MinimumValueOnAxis2() + { + var model = new PlotModel { Title = "Show minimum and maximum values on axis" }; + model.Axes.Add(new LinearAxis + { + Position = AxisPosition.Left, + //ShowMinimumValue = true, + //ShowMaximumValue = true, + //MinimumValueStringFormat = "0.###", + //MaximumValueStringFormat = "0.###", + MaximumPadding = 0, + MinimumPadding = 0 + }); + model.Axes.Add(new LinearAxis + { + Position = AxisPosition.Bottom, + //ShowMinimumValue = true, + //ShowMaximumValue = true, + //MinimumValueStringFormat = "0.###", + //MaximumValueStringFormat = "0.###", + MaximumPadding = 0, + MinimumPadding = 0 + }); + var ls = new LineSeries(); + ls.Points.Add(new DataPoint(-0.14645, -0.14645)); + ls.Points.Add(new DataPoint(10.15745, 10.15745)); + model.Series.Add(ls); + return model; + } + + [Example("#635: PositionAtZeroCrossing Forces Value Axis Label")] + public static PlotModel PositionAtZeroCrossingForcesValueAxisLabel() + { + var plotModel = new PlotModel + { + Title = "PositionAtZeroCrossing Forces Value Axis Label", + }; + + var categoryAxis = new CategoryAxis + { + Position = AxisPosition.Bottom, + AxislineStyle = LineStyle.Solid, + PositionAtZeroCrossing = true + }; + var valueAxis = new LinearAxis + { + Position = AxisPosition.Left, + MinimumPadding = 0, + Minimum = -14, + Maximum = 14, + IsAxisVisible = false + }; + plotModel.Axes.Add(categoryAxis); + plotModel.Axes.Add(valueAxis); + var series = new ColumnSeries(); + series.Items.Add(new ColumnItem { Value = 3 }); + series.Items.Add(new ColumnItem { Value = 14 }); + series.Items.Add(new ColumnItem { Value = 11 }); + series.Items.Add(new ColumnItem { Value = 12 }); + series.Items.Add(new ColumnItem { Value = 7 }); + plotModel.Series.Add(series); + + return plotModel; + } + + [Example("#550: MinimumRange with Minimum")] + public static PlotModel MinimumRangeWithMinimum() + { + var model = new PlotModel { Title = "MinimumRange of 500 with a Minimum of 50", Subtitle = "Should initially show a range from 50 to 550." }; + model.Axes.Add( + new LinearAxis + { + Position = AxisPosition.Left, + Minimum = 50, + MinimumRange = 500 + }); + + return model; + } + + + [Example("#710: MinimumRange and MaximumRange with Minimum")] + public static PlotModel MinimumRangeAndMaximumRangeWithMinimum() + { + var model = new PlotModel { Title = "MinimumRange of 5 and MaximumRange of 200 with a Minimum of 0", Subtitle = "Should show a range from 0 to 5 minimum and a range of 200 maximum." }; + model.Axes.Add( + new LinearAxis + { + Position = AxisPosition.Left, + Minimum = 0, + MinimumRange = 5, + MaximumRange = 200 + }); + + return model; + } + + [Example("#711: MinimumRange with AbsoluteMinimum")] + public static PlotModel MinimumRangeWithAbsoluteMinimum() + { + var model = new PlotModel { Title = "MinimumRange of 500 with a AbsoluteMinimum of 50", Subtitle = "Should initially show a range from 50 to 550. It should not be possible to pan below 50." }; + model.Axes.Add( + new LinearAxis + { + Position = AxisPosition.Left, + AbsoluteMinimum = 50, + MinimumRange = 500 + }); + + return model; + } + + [Example("#711: MinimumRange with AbsoluteMaximum")] + public static PlotModel MinimumRangeWithAbsoluteMaximum() + { + var model = new PlotModel { Title = "MinimumRange of 500 with a AbsoluteMaximum of 200", Subtitle = "Should initially show a range from -300 to 200. It should not be possible to pan above 200." }; + model.Axes.Add( + new LinearAxis + { + Position = AxisPosition.Left, + AbsoluteMaximum = 200, + MinimumRange = 500 + }); + + return model; + } + + [Example("#711: MaximumRange with AbsoluteMinimum")] + public static PlotModel MaximumRangeWithAbsoluteMinimum() + { + var model = new PlotModel { Title = "MaximumRange of 50 with a AbsoluteMinimum of 20", Subtitle = "Should initially show a range from 20 to 70. It should not be possible to pan below 20." }; + model.Axes.Add( + new LinearAxis + { + Position = AxisPosition.Left, + AbsoluteMinimum = 20, + MaximumRange = 50 + }); + + return model; + } + + [Example("#711: MaximumRange with AbsoluteMaximum")] + public static PlotModel MaximumRangeWithAbsoluteMaximum() + { + var model = new PlotModel { Title = "MaximumRange of 25 with a AbsoluteMaximum of -20", Subtitle = "Should initially show a range from -45 to -20. It should not be possible to pan above -20." }; + model.Axes.Add( + new LinearAxis + { + Position = AxisPosition.Left, + AbsoluteMaximum = -20, + MaximumRange = 25 + }); + + return model; + } + + [Example("#745: HeatMap not working in Windows Universal")] + public static PlotModel PlotHeatMap() + { + var model = new PlotModel { Title = "FOOBAR" }; + model.Axes.Add(new LinearColorAxis + { + Position = AxisPosition.Right, + Palette = OxyPalettes.Jet(500), + HighColor = OxyColors.Gray, + LowColor = OxyColors.Black + }); + + var data = new double[,] { { 1, 2 }, { 1, 1 }, { 2, 1 }, { 2, 2 } }; + + var hs = new HeatMapSeries + { + Background = OxyColors.Red, + X0 = 0, + X1 = 2, + Y0 = 0, + Y1 = 3, + Data = data, + }; + model.Series.Add(hs); + return model; + } + + [Example("#758: IntervalLength = 0")] + public static PlotModel IntervalLength0() + { + var model = new PlotModel + { + Title = "IntervalLength = 0", + Subtitle = "An exception should be thrown. Should not go into infinite loop." + }; + model.Axes.Add(new LinearAxis { IntervalLength = 0 }); + return model; + } + + [Example("#737: Wrong axis line when PositionAtZeroCrossing = true")] + public static PlotModel WrongAxisLineWhenPositionAtZeroCrossingIsSet() + { + var model = new PlotModel { Title = "PositionAtZeroCrossing" }; + model.Axes.Add( + new LinearAxis + { + Position = AxisPosition.Left + }); + model.Axes.Add( + new LinearAxis + { + Position = AxisPosition.Bottom, + PositionAtZeroCrossing = true, + AxislineStyle = LineStyle.Solid, + AxislineThickness = 1 + }); + var lineSeries = new LineSeries(); + lineSeries.Points.Add(new DataPoint(-10, 10)); + lineSeries.Points.Add(new DataPoint(0, -10)); + lineSeries.Points.Add(new DataPoint(10, 10)); + model.Series.Add(lineSeries); + return model; + } + + [Example("#727: Axis Min/Max ignored")] + public static PlotModel AxisMinMaxIgnored() + { + var plotModel1 = new PlotModel + { + Title = "Axes min/max ignored", + PlotType = PlotType.Cartesian, + }; + var ls = new LineSeries(); + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = 0, Maximum = 866, Key = "Horizontal" }); + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = 103, Maximum = 37141, Key = "Vertical" }); + ls.XAxisKey = "Horizontal"; + ls.YAxisKey = "Vertical"; + plotModel1.Series.Add(ls); + + return plotModel1; + } + + [Example("#727: Axis Min/Max")] + public static PlotModel AxisMinMax() + { + var plotModel1 = new PlotModel + { + Title = "Axes min/max", + }; + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = 0, Maximum = 866 }); + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = 103, Maximum = 37141 }); + + return plotModel1; + } + + [Example("#453: Auto plot margin and width of labels")] + public static PlotModel AutoPlotMarginAndAxisLabelWidths() + { + var plotModel1 = new PlotModel { Title = "Auto plot margin not taking width of axis tick labels into account" }; + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = -1e8, Maximum = 1e8 }); + return plotModel1; + } + + /// + /// Creates a demo PlotModel with MinimumRange defined + /// and with series with values which are within this range. + /// + /// The created PlotModel + [Example("#794: Axis alignment when MinimumRange is set")] + public static PlotModel MinimumRangeTest() + { + var model = new PlotModel(); + var yaxis = new LinearAxis() + { + Position = AxisPosition.Left, + MinimumRange = 1, + }; + + model.Axes.Add(yaxis); + + var series = new LineSeries(); + series.Points.Add(new DataPoint(0, 10.1)); + series.Points.Add(new DataPoint(1, 10.15)); + series.Points.Add(new DataPoint(2, 10.3)); + series.Points.Add(new DataPoint(3, 10.25)); + series.Points.Add(new DataPoint(4, 10.1)); + + model.Series.Add(series); + + return model; + } + + [Example("#72: Smooth")] + public static PlotModel Smooth() + { + var model = new PlotModel { Title = "LineSeries with Smooth = true (zoomed in)", LegendSymbolLength = 24 }; + + var s1 = new LineSeries { InterpolationAlgorithm = InterpolationAlgorithms.CanonicalSpline }; + s1.Points.Add(new DataPoint(0, 0)); + s1.Points.Add(new DataPoint(10, 2)); + s1.Points.Add(new DataPoint(40, 1)); + model.Series.Add(s1); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = 10.066564180257437, Maximum = 10.081628088306001 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = 2.0013430243084067, Maximum = 2.00209808854281 }); + return model; + } + + [Example("#880: Too much padding")] + public static PlotModel TooMuchPadding() + { + return new PlotModel { Title = "Too much padding", Padding = new OxyThickness(0, 0, 0, 10000) }; + } + + [Example("#880: Too much padding with legend outside")] + public static PlotModel TooMuchPaddingWithLegend() + { + var model = new PlotModel + { + Title = "Too much padding with legend outside", + LegendPlacement = LegendPlacement.Outside, + Padding = new OxyThickness(500) + }; + model.Series.Add(new LineSeries { Title = "Series 1" }); + model.Series.Add(new LineSeries { Title = "Series 2" }); + return model; + } + + [Example("#880: Too much title padding")] + public static PlotModel TooMuchTitlePadding() + { + var model = new PlotModel { Title = "Too much title padding", TitlePadding = 10000 }; + return model; + } + + /// + /// Creates a demo PlotModel with MinimumRange defined + /// and with series with values which are within this range. + /// + /// The created PlotModel + [Example("#794: Axis alignment when MinimumRange is set with AbsoluteMaximum")] + public static PlotModel MinimumRangeAbsoluteMaximumTest() + { + var model = new PlotModel(); + var yaxis = new LinearAxis() + { + Position = AxisPosition.Left, + MinimumRange = 1, + AbsoluteMaximum = 10.5 + }; + + model.Axes.Add(yaxis); + + var series = new LineSeries(); + series.Points.Add(new DataPoint(0, 10.1)); + series.Points.Add(new DataPoint(1, 10.15)); + series.Points.Add(new DataPoint(2, 10.3)); + series.Points.Add(new DataPoint(3, 10.25)); + series.Points.Add(new DataPoint(4, 10.1)); + + model.Series.Add(series); + + return model; + } + + /// + /// Creates a demo PlotModel with MinimumRange defined + /// and with series with values which are within this range. + /// + /// The created PlotModel + [Example("#794: Axis alignment when MinimumRange is set with AbsoluteMinimum")] + public static PlotModel MinimumRangeAbsoluteMinimumTest() + { + var model = new PlotModel(); + var yaxis = new LinearAxis() + { + Position = AxisPosition.Left, + MinimumRange = 1, + AbsoluteMinimum = 10, + }; + + model.Axes.Add(yaxis); + + var series = new LineSeries(); + series.Points.Add(new DataPoint(0, 10.1)); + series.Points.Add(new DataPoint(1, 10.15)); + series.Points.Add(new DataPoint(2, 10.3)); + series.Points.Add(new DataPoint(3, 10.25)); + series.Points.Add(new DataPoint(4, 10.1)); + + model.Series.Add(series); + + return model; + } + + /// + /// Creates a demo PlotModel with the data from the issue. + /// + /// The created PlotModel + [Example("#589: LogarithmicAxis glitches with multiple series containing small data")] + public static PlotModel LogaritmicAxesSuperExponentialFormatTest() + { + var model = new PlotModel(); + model.Axes.Add(new LogarithmicAxis + { + UseSuperExponentialFormat = true, + Position = AxisPosition.Bottom, + MajorGridlineStyle = LineStyle.Dot, + PowerPadding = true + }); + + model.Axes.Add(new LogarithmicAxis + { + UseSuperExponentialFormat = true, + Position = AxisPosition.Left, + MajorGridlineStyle = LineStyle.Dot, + PowerPadding = true + }); + + var series1 = new LineSeries(); + series1.Points.Add(new DataPoint(1e5, 1e-14)); + series1.Points.Add(new DataPoint(4e7, 1e-12)); + model.Series.Add(series1); + + return model; + } + + /// + /// Attempts to create a logarithmic axis starting at 1 and going to 0. + /// + /// The plot model. + [Example("#925: WPF app freezes when LogarithmicAxis is reversed.")] + public static PlotModel LogarithmicAxisReversed() + { + var model = new PlotModel(); + model.Axes.Add(new LogarithmicAxis { StartPosition = 1, EndPosition = 0 }); + + return model; + } + + [Example("#1029: LineAnnotation (loglin axes)")] + public static PlotModel Issue1029LogLin() + { + var plotModel1 = new PlotModel + { + Title = "Possible Infinite Loop in LineAnnotation.GetPoints() when Minimum=Maximum", + }; + plotModel1.Axes.Add(new LogarithmicAxis { Position = AxisPosition.Left, Minimum = 0, Maximum = 10 }); + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = 0, Maximum = 8 }); + plotModel1.Annotations.Add(new LineAnnotation { Type = LineAnnotationType.Vertical, X = 4, MinimumY = 2, MaximumY = 2 }); + plotModel1.Annotations.Add(new LineAnnotation { Type = LineAnnotationType.Horizontal, Y = 2, MinimumX = 2, MaximumX = 2 }); + return plotModel1; + } + + [Example("#1029: LineAnnotation (linlin axes)")] + public static PlotModel Issue1029LinLin() + { + var plotModel1 = new PlotModel + { + Title = "Possible Infinite Loop in LineAnnotation.GetPoints() when Minimum=Maximum", + }; + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = 0, Maximum = 10 }); + plotModel1.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = 0, Maximum = 8 }); + plotModel1.Annotations.Add(new LineAnnotation { Type = LineAnnotationType.Vertical, X = 4, MinimumY = 2, MaximumY = 2 }); + plotModel1.Annotations.Add(new LineAnnotation { Type = LineAnnotationType.Horizontal, Y = 2, MinimumX = 2, MaximumX = 2 }); + return plotModel1; + } + + /// + /// Creates a plot model as described in issue 1090. + /// + /// The plot model. + [Example("#1090: Overflow when zoomed in on logarithmic scale")] + public static PlotModel Issue1090() + { + var plotModel = new PlotModel(); + plotModel.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, AbsoluteMinimum = 0 }); + plotModel.Axes.Add(new LogarithmicAxis { Position = AxisPosition.Left }); + + return plotModel; + } + + [Example("#1132: ScatterSeries with TimeSpanAxis")] + public static PlotModel ScatterSeriesWithTimeSpanAxis() + { + var plotModel1 = new PlotModel(); + plotModel1.Axes.Add(new TimeSpanAxis { Position = AxisPosition.Bottom }); + plotModel1.Axes.Add(new TimeSpanAxis { Position = AxisPosition.Left }); + + var points = new[] + { + new TimeSpanPoint { X = TimeSpan.FromSeconds(0), Y = TimeSpan.FromHours(1) }, + new TimeSpanPoint { X = TimeSpan.FromSeconds(0), Y = TimeSpan.FromHours(1) } + }; + + plotModel1.Series.Add(new ScatterSeries { ItemsSource = points, DataFieldX = "X", DataFieldY = "Y" }); + + return plotModel1; + } + + [Example("#1132: ScatterSeries with DateTimeAxis")] + public static PlotModel ScatterSeriesWithDateTimeAxis() + { + var plotModel1 = new PlotModel(); + plotModel1.Axes.Add(new DateTimeAxis { Position = AxisPosition.Bottom }); + plotModel1.Axes.Add(new DateTimeAxis { Position = AxisPosition.Left }); + + var points = new[] + { + new DateTimePoint { X = new DateTime(2017,10,10), Y = new DateTime(2017,10,11) }, + new DateTimePoint { X = new DateTime(2017,1,1), Y = new DateTime(2018,1,1) } + }; + + plotModel1.Series.Add(new ScatterSeries { ItemsSource = points, DataFieldX = "X", DataFieldY = "Y" }); + + return plotModel1; + } + + [Example("#1160: Exporting TextAnnotation with transparent TextColor to SVG produces opaque text")] + public static PlotModel ExportTransparentTextAnnotationToSvg() + { + var plot = new PlotModel(); + plot.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + plot.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + plot.Background = OxyColors.Black; + plot.Annotations.Add(new TextAnnotation + { + TextPosition = new DataPoint(25, 0), + Text = "Opaque", + TextColor = OxyColor.FromRgb(255, 0, 0), + FontSize = 10, + }); + plot.Annotations.Add(new TextAnnotation + { + TextPosition = new DataPoint(25, 20), + Text = "Semi transparent", + TextColor = OxyColor.FromArgb(125, 255, 0, 0), + FontSize = 10, + }); + plot.Annotations.Add(new TextAnnotation + { + TextPosition = new DataPoint(25, 40), + Text = "Transparent1", + TextColor = OxyColor.FromArgb(0, 255, 0, 0), + FontSize = 10, + }); + plot.Annotations.Add(new TextAnnotation + { + TextPosition = new DataPoint(25, 60), + Text = "Transparent2", + TextColor = OxyColors.Transparent, + FontSize = 10, + }); + + return plot; + } + + private class TimeSpanPoint + { + public TimeSpan X { get; set; } + public TimeSpan Y { get; set; } + } + + private class DateTimePoint + { + public DateTime X { get; set; } + public DateTime Y { get; set; } + } + /* NEW ISSUE TEMPLATE + [Example("#123: Issue Description")] + public static PlotModel IssueDescription() + { + var plotModel1 = new PlotModel + { + Title = "", + }; + + return plotModel1; + } + */ + } +} diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Misc/MiscExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Misc/MiscExamples.cs new file mode 100644 index 0000000..3445a79 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Misc/MiscExamples.cs @@ -0,0 +1,2393 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Appends the specified target. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + using System.Collections.Generic; + using System.Globalization; + using System.IO; + using System.Reflection; + using System.Threading; + using System.Threading.Tasks; + using System.Xml.Serialization; + + using OxyPlot; + using OxyPlot.Annotations; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("Misc")] + public static class MiscExamples + { + [Example("Numeric ODE solvers (y'=y)")] + public static PlotModel NumericOdeSolvers1() + { + return NumericOdeSolvers("Numeric ODE solvers", "y'=y, y(0)=1", 0, 1, Math.Exp, (t, y) => y); + } + + [Example("Numeric ODE solvers (y'=x)")] + public static PlotModel NumericOdeSolvers2() + { + return NumericOdeSolvers("Numeric ODE solvers", "y'=x, y(0)=0", 0, 0, t => 0.5 * t * t, (t, y) => t); + } + + [Example("Numeric ODE solvers (y'=cos(x))")] + public static PlotModel NumericOdeSolvers3() + { + return NumericOdeSolvers("Numeric ODE solvers", "y'=cos(x), y(0)=0", 0, 0, Math.Sin, (t, y) => Math.Cos(t)); + } + + public static PlotModel NumericOdeSolvers( + string title, + string subtitle, + double t0, + double y0, + Func exact, + Func f) + { + var model = new PlotModel + { + Title = title, + Subtitle = subtitle, + LegendPosition = LegendPosition.BottomCenter, + LegendPlacement = LegendPlacement.Outside, + LegendOrientation = LegendOrientation.Horizontal + }; + model.Series.Add(new FunctionSeries(exact, 0, 4, 100) { Title = "Exact solution", StrokeThickness = 5 }); + var eulerSeries = new LineSeries + { + Title = "Euler, h=0.25", + MarkerType = MarkerType.Circle, + MarkerFill = OxyColors.Black, + }; + eulerSeries.Points.AddRange(Euler(f, t0, y0, 4, 0.25)); + model.Series.Add(eulerSeries); + + //model.Series.Add(new LineSeries("Euler, h=1") + // { + // MarkerType = MarkerType.Circle, + // MarkerFill = OxyColors.Black, + // Points = Euler(f, t0, y0, 4, 1) + // }); + var heunSeries = new LineSeries + { + Title = "Heun, h=0.25", + MarkerType = MarkerType.Circle, + MarkerFill = OxyColors.Black, + }; + heunSeries.Points.AddRange(Heun(f, t0, y0, 4, 0.25)); + model.Series.Add(heunSeries); + + var midpointSeries = new LineSeries + { + Title = "Midpoint, h=0.25", + MarkerType = MarkerType.Circle, + MarkerFill = OxyColors.Black, + }; + midpointSeries.Points.AddRange(Midpoint(f, t0, y0, 4, 0.25)); + model.Series.Add(midpointSeries); + + var rkSeries = new LineSeries + { + Title = "RK4, h=0.25", + MarkerType = MarkerType.Circle, + MarkerFill = OxyColors.Black, + }; + rkSeries.Points.AddRange(RungeKutta4(f, t0, y0, 4, 0.25)); + model.Series.Add(rkSeries); + + //model.Series.Add(new LineSeries("RK4, h=1") + //{ + // MarkerType = MarkerType.Circle, + // MarkerFill = OxyColors.Black, + // Points = RungeKutta4(f, t0, y0, 4, 1) + //}); + + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + return model; + } + + private static List Euler( + Func f, double t0, double y0, double t1, double h) + { + var points = new List(); + double y = y0; + for (double t = t0; t < t1 + h / 2; t += h) + { + points.Add(new DataPoint(t, y)); + y += h * f(t, y); + } + + return points; + } + + private static IList Heun(Func f, double t0, double y0, double t1, double h) + { + var points = new List(); + double y = y0; + for (double t = t0; t < t1 + h / 2; t += h) + { + points.Add(new DataPoint(t, y)); + double ytilde = y + h * f(t, y); + y = y + h / 2 * (f(t, y) + f(t + h, ytilde)); + } + + return points; + } + + private static List Midpoint( + Func f, double t0, double y0, double t1, double h) + { + var points = new List(); + double y = y0; + for (double t = t0; t < t1 + h / 2; t += h) + { + points.Add(new DataPoint(t, y)); + y += h * f(t + h / 2, y + h / 2 * f(t, y)); + } + + return points; + } + + private static List RungeKutta4( + Func f, double t0, double y0, double t1, double h) + { + var points = new List(); + double y = y0; + for (double t = t0; t < t1 + h / 2; t += h) + { + points.Add(new DataPoint(t, y)); + double k1 = h * f(t, y); + double k2 = h * f(t + h / 2, y + k1 / 2); + double k3 = h * f(t + h / 2, y + k2 / 2); + double k4 = h * f(t + h, y + k3); + y += (k1 + 2 * k2 + 2 * k3 + k4) / 6; + } + + return points; + } + + [Example("MatrixSeries (chemical process simulation problem)")] + public static PlotModel MatrixSeriesWest0479() + { + // http://www.cise.ufl.edu/research/sparse/matrices/HB/west0479 + var model = new PlotModel(); + + double[,] matrix = null; + + + + using (var reader = new StreamReader(typeof(MiscExamples).GetTypeInfo().Assembly.GetManifestResourceStream("ExampleLibrary.Resources.west0479.mtx"))) + { + while (!reader.EndOfStream) + { + var line = reader.ReadLine(); + if (line.StartsWith("%")) + { + continue; + } + + var v = line.Split(' '); + if (matrix == null) + { + int m = int.Parse(v[0]); + int n = int.Parse(v[1]); + matrix = new double[m, n]; + continue; + } + + int i = int.Parse(v[0]) - 1; + int j = int.Parse(v[1]) - 1; + matrix[i, j] = double.Parse(v[2], CultureInfo.InvariantCulture); + } + } + + // Reverse the vertical axis + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, StartPosition = 1, EndPosition = 0 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Series.Add(new MatrixSeries { Matrix = matrix, ShowDiagonal = true }); + + return model; + } + + [Example("Train schedule")] + public static PlotModel TrainSchedule() + { + //// http://www.edwardtufte.com/tufte/books_vdqi + //// http://marlenacompton.com/?p=103 + //// http://mbostock.github.com/protovis/ex/caltrain.html + //// http://en.wikipedia.org/wiki/%C3%89tienne-Jules_Marey + //// http://mbostock.github.com/protovis/ex/marey-train-schedule.jpg + //// http://c82.net/posts.php?id=66 + + var model = new PlotModel + { + Title = "Train schedule", + Subtitle = "Bergensbanen (Oslo-Bergen, Norway)", + IsLegendVisible = false, + PlotAreaBorderThickness = new OxyThickness(0), + PlotMargins = new OxyThickness(60, 4, 60, 40) + }; + model.Axes.Add( + new LinearAxis + { + Position = AxisPosition.Left, + Minimum = -20, + Maximum = 540, + Title = "Distance from Oslo S", + IsAxisVisible = true, + StringFormat = "0" + }); + model.Axes.Add( + new TimeSpanAxis + { + Position = AxisPosition.Bottom, + Minimum = 0, + Maximum = TimeSpanAxis.ToDouble(TimeSpan.FromHours(24)), + StringFormat = "hh", + Title = "Time", + MajorStep = TimeSpanAxis.ToDouble(TimeSpan.FromHours(1)), + MinorStep = TimeSpanAxis.ToDouble(TimeSpan.FromMinutes(10)), + TickStyle = TickStyle.None, + MajorGridlineStyle = LineStyle.Solid, + MajorGridlineColor = OxyColors.LightGray, + MinorGridlineStyle = LineStyle.Solid, + MinorGridlineColor = OxyColor.FromArgb(255, 240, 240, 240) + }); + + // Read the train schedule from a .csv resource + using (var reader = new StreamReader(GetResourceStream("Bergensbanen.csv"))) + { + string header = reader.ReadLine(); + var headerFields = header.Split(';'); + int lines = headerFields.Length - 3; + var stations = new LineSeries() + { + StrokeThickness = 0, + MarkerType = MarkerType.Circle, + MarkerFill = OxyColor.FromAColor(200, OxyColors.Black), + MarkerSize = 4, + }; + + // Add the line series for each train line + var series = new LineSeries[lines]; + for (int i = 0; i < series.Length; i++) + { + series[i] = new LineSeries + { + Title = headerFields[3 + i], + Color = + OxyColor.FromAColor( + 180, OxyColors.Black), + StrokeThickness = 2, + TrackerFormatString = + "Train {0}\nTime: {2}\nDistance from Oslo S: {4:0.0} km", + }; + model.Series.Add(series[i]); + } + + // Parse the train schedule + while (!reader.EndOfStream) + { + var line = reader.ReadLine(); + + // skip comments + if (line == null || line.StartsWith("//")) + { + continue; + } + + var fields = line.Split(';'); + double x = double.Parse(fields[1], CultureInfo.InvariantCulture); + if (!string.IsNullOrEmpty(fields[0])) + { + // Add a horizontal annotation line for the station + model.Annotations.Add( + new LineAnnotation + { + Type = LineAnnotationType.Horizontal, + Y = x, + Layer = AnnotationLayer.BelowSeries, + LineStyle = LineStyle.Solid, + Color = OxyColors.LightGray, + Text = fields[0] + " ", + TextVerticalAlignment = VerticalAlignment.Middle, + TextLinePosition = 1, + TextMargin = 0, + TextPadding = 4, + ClipText = false, + TextHorizontalAlignment = HorizontalAlignment.Left + }); + } + + for (int i = 0; i < series.Length; i++) + { + if (string.IsNullOrEmpty(fields[i + 3])) + { + continue; + } + + // Convert time from hhmm to a time span + int hhmm = int.Parse(fields[i + 3]); + var span = new TimeSpan(0, hhmm / 100, (hhmm % 100), 0); + double t = TimeSpanAxis.ToDouble(span); + + // Add the point to the line + series[i].Points.Add(new DataPoint(t, x)); + + // Add the point for the station + stations.Points.Add(new DataPoint(t, x)); + } + } + + // add points and NaN (to make a line break) when passing midnight + double tmidnight = TimeSpanAxis.ToDouble(TimeSpan.FromHours(24)); + foreach (LineSeries s in model.Series) + { + for (int i = 0; i + 1 < s.Points.Count; i++) + { + if (Math.Abs(s.Points[i].X - s.Points[i + 1].X) > tmidnight / 2) + { + double x0 = s.Points[i].X; + if (x0 > tmidnight / 2) + { + x0 -= tmidnight; + } + + double x1 = s.Points[i + 1].X; + if (x1 > tmidnight / 2) + { + x1 -= tmidnight; + } + + double y = s.Points[i].Y + (s.Points[i + 1].Y - s.Points[i].Y) / (x1 - x0) * (0 - x0); + s.Points.Insert(i + 1, new DataPoint(x0 < x1 ? 0 : tmidnight, y)); + s.Points.Insert(i + 1, new DataPoint(double.NaN, y)); + s.Points.Insert(i + 1, new DataPoint(x0 < x1 ? tmidnight : 0, y)); + i += 3; + } + } + } + + model.Series.Add(stations); + } + + return model; + } + + /* [Example("World population")] + public static PlotModel WorldPopulation() + { + WorldPopulationDataSet dataSet; + using (var stream = GetResourceStream("WorldPopulation.xml")) + { + var serializer = new XmlSerializer(typeof(WorldPopulationDataSet)); + dataSet = (WorldPopulationDataSet)serializer.Deserialize(stream); + } + + var model = new PlotModel { Title = "World population" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = "millions" }); + var series1 = new LineSeries { ItemsSource = dataSet.Items, DataFieldX = "Year", DataFieldY = "Population", StrokeThickness = 3, MarkerType = MarkerType.Circle }; + model.Series.Add(series1); + return model; + }*/ + + [Example("La Linea (AreaSeries)")] + public static PlotModel LaLineaAreaSeries() + { + // http://en.wikipedia.org/wiki/La_Linea_(TV_series) + var model = new PlotModel + { + Title = "La Linea", + PlotType = PlotType.Cartesian, + Background = OxyColor.FromRgb(84, 98, 207) + }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = -500, Maximum = 1000 }); + var series1 = new AreaSeries { Fill = OxyColors.White, StrokeThickness = 0 }; + series1.Points.Append(GetLineaPoints()); + model.Series.Add(series1); + return model; + } + + [Example("La Linea (LineSeries)")] + public static PlotModel LaLinea() + { + // http://en.wikipedia.org/wiki/La_Linea_(TV_series) + var model = new PlotModel + { + Title = "La Linea", + PlotType = PlotType.Cartesian, + Background = OxyColor.FromRgb(84, 98, 207) + }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = -500, Maximum = 1000 }); + var series1 = new LineSeries { Color = OxyColors.White, StrokeThickness = 1.5 }; + series1.Points.Append(GetLineaPoints()); + model.Series.Add(series1); + return model; + } + + /// + /// Appends the specified target. + /// + /// + /// The target. + /// The source. + public static void Append(this IList target, IEnumerable source) + { + foreach (var item in source) + { + target.Add(item); + } + } + + private static IEnumerable GetLineaPoints() + { + var points = new List(); + + // The original image was vectorized by http://www.autotracer.org/ + // Then inkscape was used to convert from svg to xaml http://inkscape.org/ + // The xaml geometry was imported by Geometry.Parse and converted to a polyline + // by Geometry.GetFlattenedPathGeometry(); + // The resulting points were output to the following code: + points.Add(new DataPoint(589.3649979, 16.10595703)); + points.Add(new DataPoint(437.9979935, 16.10595703)); + points.Add(new DataPoint(400.4249954, 16.10595703)); + points.Add(new DataPoint(399.1255264, 16.05047607)); + points.Add(new DataPoint(397.463356, 15.92333984)); + points.Add(new DataPoint(393.5852432, 15.69073486)); + points.Add(new DataPoint(389.8589859, 15.88067627)); + points.Add(new DataPoint(388.3866653, 16.28186035)); + points.Add(new DataPoint(387.3529739, 16.96594238)); + points.Add(new DataPoint(386.8373489, 18.53930664)); + points.Add(new DataPoint(387.2163773, 20.51794434)); + points.Add(new DataPoint(387.9814529, 22.51843262)); + points.Add(new DataPoint(388.6240005, 24.15698242)); + points.Add(new DataPoint(395.958992, 45.09094238)); + points.Add(new DataPoint(399.2686234, 54.89562988)); + points.Add(new DataPoint(402.1330338, 64.90338135)); + points.Add(new DataPoint(404.5822525, 75.06884766)); + points.Add(new DataPoint(406.6462479, 85.34680176)); + points.Add(new DataPoint(409.7385635, 106.0593262)); + points.Add(new DataPoint(411.6500015, 126.6789551)); + points.Add(new DataPoint(412.0961685, 137.0930786)); + points.Add(new DataPoint(412.0253677, 147.4713135)); + points.Add(new DataPoint(411.4655228, 157.8103638)); + points.Add(new DataPoint(410.4446182, 168.1069336)); + points.Add(new DataPoint(408.9906387, 178.3577881)); + points.Add(new DataPoint(407.1315689, 188.5595703)); + points.Add(new DataPoint(404.8953629, 198.7091064)); + points.Add(new DataPoint(402.3099747, 208.8029785)); + points.Add(new DataPoint(392.9860001, 237.2509766)); + points.Add(new DataPoint(392.1175613, 240.0527954)); + points.Add(new DataPoint(391.2060013, 243.0959473)); + points.Add(new DataPoint(390.5509415, 246.1691284)); + points.Add(new DataPoint(390.4520035, 249.0609741)); + points.Add(new DataPoint(391.406044, 252.6694336)); + points.Add(new DataPoint(393.1980057, 256.0982056)); + points.Add(new DataPoint(395.3566971, 259.3631287)); + points.Add(new DataPoint(397.4109879, 262.47995)); + points.Add(new DataPoint(411.7649918, 287.7079468)); + points.Add(new DataPoint(426.5997696, 312.9102173)); + points.Add(new DataPoint(441.8913651, 337.8200684)); + points.Add(new DataPoint(457.3402786, 362.6333923)); + points.Add(new DataPoint(472.6469803, 387.5459595)); + points.Add(new DataPoint(478.0007401, 395.7557983)); + points.Add(new DataPoint(483.6958694, 403.8400879)); + points.Add(new DataPoint(489.2894974, 411.9628296)); + points.Add(new DataPoint(494.3389969, 420.2879639)); + points.Add(new DataPoint(494.9800491, 421.8480225)); + points.Add(new DataPoint(495.4455032, 423.6903687)); + points.Add(new DataPoint(496.1577225, 427.7724609)); + points.Add(new DataPoint(497.0915604, 431.6355591)); + points.Add(new DataPoint(497.8341141, 433.2041321)); + points.Add(new DataPoint(498.8629837, 434.3809509)); + points.Add(new DataPoint(500.0935135, 434.9877625)); + points.Add(new DataPoint(501.7391434, 435.3059082)); + points.Add(new DataPoint(505.7623367, 435.3148193)); + points.Add(new DataPoint(509.9061356, 434.8848267)); + points.Add(new DataPoint(511.7024612, 434.6542969)); + points.Add(new DataPoint(513.1439896, 434.4929504)); + points.Add(new DataPoint(520.0768509, 434.1251831)); + points.Add(new DataPoint(527.3961258, 434.1952209)); + points.Add(new DataPoint(534.6892776, 434.728363)); + points.Add(new DataPoint(541.544014, 435.7499695)); + points.Add(new DataPoint(544.2357864, 436.3025513)); + points.Add(new DataPoint(547.021492, 437.0792236)); + points.Add(new DataPoint(549.6099319, 438.2590027)); + points.Add(new DataPoint(551.7099686, 440.0209656)); + points.Add(new DataPoint(552.7028275, 441.4446106)); + points.Add(new DataPoint(553.2691116, 442.791626)); + points.Add(new DataPoint(553.4498978, 444.0619202)); + points.Add(new DataPoint(553.2864456, 445.2554626)); + points.Add(new DataPoint(552.0910721, 447.4119873)); + points.Add(new DataPoint(550.0122147, 449.2607117)); + points.Add(new DataPoint(547.3790359, 450.8010864)); + points.Add(new DataPoint(544.5206985, 452.0325928)); + points.Add(new DataPoint(541.766304, 452.9547119)); + points.Add(new DataPoint(539.445015, 453.5669556)); + points.Add(new DataPoint(539.445015, 454.6409607)); + points.Add(new DataPoint(542.6554031, 455.4246521)); + points.Add(new DataPoint(546.0063553, 455.8735962)); + points.Add(new DataPoint(549.2799149, 456.4869385)); + points.Add(new DataPoint(552.2580032, 457.7639465)); + points.Add(new DataPoint(554.3335648, 459.5966797)); + points.Add(new DataPoint(555.6600418, 461.8208313)); + points.Add(new DataPoint(556.278389, 464.282959)); + points.Add(new DataPoint(556.2294998, 466.8295898)); + points.Add(new DataPoint(555.55439, 469.307251)); + points.Add(new DataPoint(554.2939529, 471.5625)); + points.Add(new DataPoint(552.4892044, 473.4418945)); + points.Add(new DataPoint(550.1809769, 474.7919617)); + points.Add(new DataPoint(547.1414261, 475.8059387)); + points.Add(new DataPoint(543.8482132, 476.5288391)); + points.Add(new DataPoint(537.2979813, 477.0559692)); + points.Add(new DataPoint(535.5239944, 476.8666077)); + points.Add(new DataPoint(533.5114822, 476.5535889)); + points.Add(new DataPoint(531.5334549, 476.4162598)); + points.Add(new DataPoint(529.8629837, 476.7539673)); + points.Add(new DataPoint(529.0471268, 477.3421631)); + points.Add(new DataPoint(528.5394363, 478.1289673)); + points.Add(new DataPoint(528.1448441, 480.0927124)); + points.Add(new DataPoint(528.071846, 482.2338257)); + points.Add(new DataPoint(527.7129593, 484.1409607)); + points.Add(new DataPoint(526.901741, 485.4877014)); + points.Add(new DataPoint(525.8139114, 486.4950867)); + points.Add(new DataPoint(523.0528641, 487.6643372)); + points.Add(new DataPoint(519.9188919, 487.9933777)); + points.Add(new DataPoint(516.9010086, 487.8269653)); + points.Add(new DataPoint(511.7325516, 486.9451599)); + points.Add(new DataPoint(506.4563065, 485.4539185)); + points.Add(new DataPoint(501.155159, 483.4500427)); + points.Add(new DataPoint(495.912117, 481.0302124)); + points.Add(new DataPoint(485.9321365, 475.3295898)); + points.Add(new DataPoint(481.3610916, 472.2423096)); + points.Add(new DataPoint(477.1800003, 469.125946)); + points.Add(new DataPoint(465.3709793, 459.3179626)); + points.Add(new DataPoint(464.3509598, 458.3116455)); + points.Add(new DataPoint(463.1867142, 457.1624451)); + points.Add(new DataPoint(461.9141312, 456.2180176)); + points.Add(new DataPoint(460.5689774, 455.8259583)); + points.Add(new DataPoint(459.6923904, 456.0762939)); + points.Add(new DataPoint(458.8656693, 456.7503662)); + points.Add(new DataPoint(457.3631058, 458.907959)); + points.Add(new DataPoint(456.063179, 461.3753052)); + points.Add(new DataPoint(455.4898758, 462.436554)); + points.Add(new DataPoint(454.9679642, 463.2289734)); + points.Add(new DataPoint(453.0795364, 465.3183289)); + points.Add(new DataPoint(450.8528519, 467.2734985)); + points.Add(new DataPoint(448.3575516, 468.9848328)); + points.Add(new DataPoint(445.6630936, 470.3425903)); + points.Add(new DataPoint(442.83918, 471.2370605)); + points.Add(new DataPoint(439.9552689, 471.5585938)); + points.Add(new DataPoint(437.0810318, 471.1974487)); + points.Add(new DataPoint(434.2859879, 470.0439453)); + points.Add(new DataPoint(432.4744034, 468.6621399)); + points.Add(new DataPoint(431.3244705, 467.0726013)); + points.Add(new DataPoint(430.7551956, 465.3302612)); + points.Add(new DataPoint(430.6856155, 463.4900818)); + points.Add(new DataPoint(431.0347672, 461.6070251)); + points.Add(new DataPoint(431.7216873, 459.7360229)); + points.Add(new DataPoint(433.7849808, 456.2499695)); + points.Add(new DataPoint(438.1093216, 450.118988)); + points.Add(new DataPoint(441.4749832, 444.3893433)); + points.Add(new DataPoint(444.0351639, 438.28302)); + points.Add(new DataPoint(445.0610428, 434.845459)); + points.Add(new DataPoint(445.9430008, 431.0219727)); + points.Add(new DataPoint(446.6270218, 428.7687378)); + points.Add(new DataPoint(447.4476395, 426.4767151)); + points.Add(new DataPoint(447.9032059, 424.1760559)); + points.Add(new DataPoint(447.492012, 421.8969727)); + points.Add(new DataPoint(445.6156082, 418.2295837)); + points.Add(new DataPoint(443.3608475, 414.6139832)); + points.Add(new DataPoint(438.1008682, 407.5364685)); + points.Add(new DataPoint(432.48069, 400.6614685)); + points.Add(new DataPoint(427.2689896, 393.9859619)); + points.Add(new DataPoint(389.1699905, 339.2359619)); + points.Add(new DataPoint(374.5550003, 318.3009644)); + points.Add(new DataPoint(372.5515823, 314.8404541)); + points.Add(new DataPoint(370.2787552, 310.9485779)); + points.Add(new DataPoint(367.7467728, 307.2946777)); + points.Add(new DataPoint(366.3868484, 305.7660828)); + points.Add(new DataPoint(364.9659805, 304.5479736)); + points.Add(new DataPoint(363.9477615, 304.0406799)); + points.Add(new DataPoint(363.082222, 304.0159912)); + points.Add(new DataPoint(361.6236038, 304.9024658)); + points.Add(new DataPoint(360.2191849, 306.1834412)); + points.Add(new DataPoint(359.4213638, 306.651886)); + points.Add(new DataPoint(358.4979935, 306.8349609)); + points.Add(new DataPoint(356.6694107, 306.4464722)); + points.Add(new DataPoint(354.9371109, 305.5308228)); + points.Add(new DataPoint(353.2544937, 304.4515076)); + points.Add(new DataPoint(351.5749893, 303.5719604)); + points.Add(new DataPoint(343.4895706, 301.1234131)); + points.Add(new DataPoint(335.0169449, 299.8048401)); + points.Add(new DataPoint(326.3076553, 299.5128174)); + points.Add(new DataPoint(317.5122147, 300.1439514)); + points.Add(new DataPoint(308.7811966, 301.5948486)); + points.Add(new DataPoint(300.2651443, 303.762085)); + points.Add(new DataPoint(292.1145401, 306.5422668)); + points.Add(new DataPoint(284.4799881, 309.8319702)); + points.Add(new DataPoint(282.3371964, 310.7483521)); + points.Add(new DataPoint(279.925972, 311.644928)); + points.Add(new DataPoint(274.7942581, 313.5763245)); + points.Add(new DataPoint(270.0769119, 316.0212708)); + points.Add(new DataPoint(268.1836319, 317.559845)); + points.Add(new DataPoint(266.7659988, 319.3749695)); + points.Add(new DataPoint(271.6227798, 320.1968384)); + points.Add(new DataPoint(276.5877457, 321.7830811)); + points.Add(new DataPoint(281.472847, 323.7190247)); + points.Add(new DataPoint(286.090004, 325.5899658)); + points.Add(new DataPoint(298.3649979, 330.7419739)); + points.Add(new DataPoint(310.3880997, 336.8226929)); + points.Add(new DataPoint(321.8024063, 343.941803)); + points.Add(new DataPoint(332.2509842, 352.2089539)); + points.Add(new DataPoint(339.2033768, 358.533844)); + points.Add(new DataPoint(342.4841385, 361.901825)); + points.Add(new DataPoint(345.4439774, 365.5359497)); + points.Add(new DataPoint(346.445076, 367.0002136)); + points.Add(new DataPoint(347.3386307, 368.7592163)); + points.Add(new DataPoint(347.6443558, 370.5680847)); + points.Add(new DataPoint(347.4267044, 371.4147034)); + points.Add(new DataPoint(346.8819962, 372.1819458)); + points.Add(new DataPoint(345.387001, 372.7861023)); + points.Add(new DataPoint(343.5981216, 372.4627075)); + points.Add(new DataPoint(341.8064041, 371.7001953)); + points.Add(new DataPoint(340.3029861, 370.986969)); + points.Add(new DataPoint(336.1688919, 369.4472046)); + points.Add(new DataPoint(331.5998611, 368.1002197)); + points.Add(new DataPoint(326.9541702, 367.1023254)); + points.Add(new DataPoint(322.5899734, 366.6099548)); + points.Add(new DataPoint(324.6785049, 369.6851501)); + points.Add(new DataPoint(327.4601212, 372.3129578)); + points.Add(new DataPoint(330.467659, 374.7735291)); + points.Add(new DataPoint(333.2339859, 377.3469543)); + points.Add(new DataPoint(338.3369217, 383.6273193)); + points.Add(new DataPoint(342.70298, 390.7063293)); + points.Add(new DataPoint(344.4534683, 394.4726563)); + points.Add(new DataPoint(345.8323135, 398.3514099)); + points.Add(new DataPoint(346.7769547, 402.3135376)); + points.Add(new DataPoint(347.2249832, 406.3299561)); + points.Add(new DataPoint(346.8078384, 412.0097046)); + points.Add(new DataPoint(345.1297989, 416.5983276)); + points.Add(new DataPoint(342.383522, 420.1838074)); + points.Add(new DataPoint(338.7616043, 422.8540955)); + points.Add(new DataPoint(334.4566727, 424.6971741)); + points.Add(new DataPoint(329.6613541, 425.8010559)); + points.Add(new DataPoint(324.5682449, 426.2536621)); + points.Add(new DataPoint(319.3700027, 426.1429749)); + points.Add(new DataPoint(315.144783, 425.6447144)); + points.Add(new DataPoint(311.0141983, 424.7691345)); + points.Add(new DataPoint(303.0388565, 422.0056763)); + points.Add(new DataPoint(295.4478226, 418.0917969)); + points.Add(new DataPoint(288.2450027, 413.2668457)); + points.Add(new DataPoint(281.4342422, 407.769989)); + points.Add(new DataPoint(275.0194168, 401.8405762)); + points.Add(new DataPoint(269.0043716, 395.717804)); + points.Add(new DataPoint(263.393013, 389.6409607)); + points.Add(new DataPoint(255.0782547, 379.5436707)); + points.Add(new DataPoint(247.6409988, 368.3999634)); + points.Add(new DataPoint(244.4098587, 362.5210571)); + points.Add(new DataPoint(241.5882645, 356.4829712)); + points.Add(new DataPoint(239.2395096, 350.3198853)); + points.Add(new DataPoint(237.4270096, 344.0659485)); + points.Add(new DataPoint(236.2694168, 338.2407532)); + points.Add(new DataPoint(235.5486526, 332.3677368)); + points.Add(new DataPoint(235.0773697, 320.528717)); + points.Add(new DataPoint(235.3326797, 308.6495667)); + points.Add(new DataPoint(235.6340103, 296.8309631)); + points.Add(new DataPoint(233.1889725, 297.9562988)); + points.Add(new DataPoint(230.9140091, 299.4570923)); + points.Add(new DataPoint(226.5090103, 302.6929626)); + points.Add(new DataPoint(206.6489944, 315.875946)); + points.Add(new DataPoint(157.2670059, 346.7819519)); + points.Add(new DataPoint(136.8219986, 360.059967)); + points.Add(new DataPoint(132.2092514, 363.0340881)); + points.Add(new DataPoint(130.0033798, 364.6920166)); + points.Add(new DataPoint(128.1559982, 366.6599731)); + points.Add(new DataPoint(127.0190811, 368.5368958)); + points.Add(new DataPoint(126.298027, 370.4631348)); + points.Add(new DataPoint(125.9532547, 372.4280701)); + points.Add(new DataPoint(125.9451218, 374.4211121)); + points.Add(new DataPoint(126.7804031, 378.4490356)); + points.Add(new DataPoint(128.4870071, 382.4620972)); + points.Add(new DataPoint(130.748024, 386.3754272)); + points.Add(new DataPoint(133.2466202, 390.104248)); + points.Add(new DataPoint(135.6659012, 393.5636902)); + points.Add(new DataPoint(137.689003, 396.6689453)); + points.Add(new DataPoint(139.2043839, 400.197052)); + points.Add(new DataPoint(139.5524673, 402.0329285)); + points.Add(new DataPoint(139.5626297, 403.8374634)); + points.Add(new DataPoint(139.1861954, 405.551239)); + points.Add(new DataPoint(138.3745499, 407.1148682)); + points.Add(new DataPoint(137.0790329, 408.4689026)); + points.Add(new DataPoint(135.2509995, 409.5539551)); + points.Add(new DataPoint(132.8812943, 410.31427)); + points.Add(new DataPoint(130.5507584, 410.5262146)); + points.Add(new DataPoint(128.270546, 410.2424316)); + points.Add(new DataPoint(126.0518265, 409.5155334)); + points.Add(new DataPoint(123.9057388, 408.3982239)); + points.Add(new DataPoint(121.8434372, 406.9431458)); + points.Add(new DataPoint(118.0148697, 403.2301941)); + points.Add(new DataPoint(114.6553879, 398.7979126)); + points.Add(new DataPoint(111.8542252, 394.0675049)); + points.Add(new DataPoint(109.7006912, 389.4601135)); + points.Add(new DataPoint(108.2840042, 385.3969727)); + points.Add(new DataPoint(107.7778549, 382.5092468)); + points.Add(new DataPoint(107.3788681, 379.2887268)); + points.Add(new DataPoint(106.6309433, 376.2470703)); + points.Add(new DataPoint(105.0779953, 373.8959656)); + points.Add(new DataPoint(103.1701126, 372.6677246)); + points.Add(new DataPoint(100.7825394, 371.7599487)); + points.Add(new DataPoint(95.13137054, 370.6252136)); + points.Add(new DataPoint(89.25051117, 369.9303284)); + points.Add(new DataPoint(86.57584381, 369.5724182)); + points.Add(new DataPoint(84.26599884, 369.1139526)); + points.Add(new DataPoint(78.20024872, 367.3027649)); + points.Add(new DataPoint(71.70685577, 364.820343)); + points.Add(new DataPoint(65.1133194, 361.6892395)); + points.Add(new DataPoint(58.74712372, 357.9318237)); + points.Add(new DataPoint(52.93579102, 353.5706482)); + points.Add(new DataPoint(48.00682831, 348.6281433)); + points.Add(new DataPoint(45.97557068, 345.9458923)); + points.Add(new DataPoint(44.28772736, 343.1267395)); + points.Add(new DataPoint(42.98422241, 340.1734924)); + points.Add(new DataPoint(42.10599518, 337.0889587)); + points.Add(new DataPoint(41.90753937, 335.2592163)); + points.Add(new DataPoint(42.08698273, 333.7605286)); + points.Add(new DataPoint(43.18836975, 331.3730774)); + points.Add(new DataPoint(44.62782288, 329.1598206)); + points.Add(new DataPoint(45.6230011, 326.3539734)); + points.Add(new DataPoint(45.62973022, 324.9945984)); + points.Add(new DataPoint(45.33054352, 323.6192627)); + points.Add(new DataPoint(44.36862183, 320.8330994)); + points.Add(new DataPoint(43.98298645, 319.4284058)); + points.Add(new DataPoint(43.84563446, 318.0201111)); + points.Add(new DataPoint(44.09512329, 316.6112671)); + points.Add(new DataPoint(44.86999512, 315.2049561)); + points.Add(new DataPoint(45.80908966, 314.2088623)); + points.Add(new DataPoint(46.79941559, 313.5540161)); + points.Add(new DataPoint(48.9025116, 313.1113892)); + points.Add(new DataPoint(51.11682129, 313.5637512)); + points.Add(new DataPoint(53.37987518, 314.5978394)); + points.Add(new DataPoint(57.8022995, 317.1580811)); + points.Add(new DataPoint(59.83672333, 318.0577087)); + points.Add(new DataPoint(61.6700058, 318.2859497)); + points.Add(new DataPoint(62.82819366, 317.6254883)); + points.Add(new DataPoint(63.23600006, 316.3283386)); + points.Add(new DataPoint(63.24330902, 314.8067627)); + points.Add(new DataPoint(63.20000458, 313.4729614)); + points.Add(new DataPoint(63.68109894, 310.3050232)); + points.Add(new DataPoint(64.93375397, 307.6513367)); + points.Add(new DataPoint(67.09728241, 305.979187)); + points.Add(new DataPoint(68.56415558, 305.6572571)); + points.Add(new DataPoint(70.31099701, 305.7559509)); + points.Add(new DataPoint(73.2078476, 306.4935913)); + points.Add(new DataPoint(76.04866791, 307.6478271)); + points.Add(new DataPoint(81.46486664, 310.9243164)); + points.Add(new DataPoint(86.36489105, 315.0218811)); + points.Add(new DataPoint(90.55399323, 319.3769531)); + points.Add(new DataPoint(92.14154816, 321.4163208)); + points.Add(new DataPoint(93.72263336, 323.6399536)); + points.Add(new DataPoint(95.39614105, 325.7593689)); + points.Add(new DataPoint(97.26099396, 327.4859619)); + points.Add(new DataPoint(98.87421417, 328.2453918)); + points.Add(new DataPoint(100.5960007, 328.4575806)); + points.Add(new DataPoint(104.1259995, 328.1189575)); + points.Add(new DataPoint(107.9671097, 328.0540771)); + points.Add(new DataPoint(112.0256271, 328.345459)); + points.Add(new DataPoint(116.0258255, 328.4305725)); + points.Add(new DataPoint(119.6919937, 327.7469482)); + points.Add(new DataPoint(122.6980515, 326.2321777)); + points.Add(new DataPoint(125.5723801, 324.1764526)); + points.Add(new DataPoint(128.3242722, 321.9129944)); + points.Add(new DataPoint(130.9630051, 319.7749634)); + points.Add(new DataPoint(158.6139908, 297.5969543)); + points.Add(new DataPoint(183.9269943, 278.8919678)); + points.Add(new DataPoint(215.7729874, 251.3609619)); + points.Add(new DataPoint(222.6591263, 245.7957153)); + points.Add(new DataPoint(229.6908646, 240.2703247)); + points.Add(new DataPoint(236.5836563, 234.5932617)); + points.Add(new DataPoint(243.0529861, 228.572937)); + points.Add(new DataPoint(246.137764, 224.8230591)); + points.Add(new DataPoint(248.7661209, 220.4605103)); + points.Add(new DataPoint(251.0138321, 215.6484985)); + points.Add(new DataPoint(252.9567337, 210.5499878)); + points.Add(new DataPoint(256.2312393, 200.1459351)); + points.Add(new DataPoint(257.7144547, 195.1665039)); + points.Add(new DataPoint(259.1959915, 190.5529785)); + points.Add(new DataPoint(263.9845047, 175.0708008)); + points.Add(new DataPoint(267.8167191, 159.2056274)); + points.Add(new DataPoint(270.6394424, 143.0612183)); + points.Add(new DataPoint(272.3993607, 126.7412109)); + points.Add(new DataPoint(273.043251, 110.3493042)); + points.Add(new DataPoint(272.5178299, 93.98913574)); + points.Add(new DataPoint(270.7698441, 77.76446533)); + points.Add(new DataPoint(267.7460098, 61.77893066)); + points.Add(new DataPoint(260.9010086, 28.45196533)); + points.Add(new DataPoint(260.3377457, 25.5088501)); + points.Add(new DataPoint(259.7099991, 22.24182129)); + points.Add(new DataPoint(258.5879898, 19.265625)); + points.Add(new DataPoint(257.7073746, 18.07867432)); + points.Add(new DataPoint(256.5419998, 17.19494629)); + points.Add(new DataPoint(254.5226212, 16.44396973)); + points.Add(new DataPoint(252.1078568, 16.0401001)); + points.Add(new DataPoint(246.5816116, 15.96911621)); + points.Add(new DataPoint(240.9423294, 16.37298584)); + points.Add(new DataPoint(238.3862076, 16.56274414)); + points.Add(new DataPoint(236.1689835, 16.64294434)); + points.Add(new DataPoint(185.1770096, 17.17895508)); + points.Add(new DataPoint(0, 16.64294434)); + points.Add(new DataPoint(0, 0.53894043)); + points.Add(new DataPoint(188.9400101, 0.53894043)); + points.Add(new DataPoint(242.0799942, 0.53894043)); + points.Add(new DataPoint(244.6571732, 0.474975586)); + points.Add(new DataPoint(247.5546951, 0.33001709)); + points.Add(new DataPoint(253.8458633, 0.078552246)); + points.Add(new DataPoint(260.0235977, 0.347839355)); + points.Add(new DataPoint(262.779335, 0.853759766)); + points.Add(new DataPoint(265.1579971, 1.70098877)); + points.Add(new DataPoint(266.7366104, 2.746398926)); + points.Add(new DataPoint(268.0821915, 4.172119141)); + points.Add(new DataPoint(270.1900101, 7.797119141)); + points.Add(new DataPoint(271.7128067, 11.84075928)); + points.Add(new DataPoint(272.8819962, 15.56799316)); + points.Add(new DataPoint(276.034523, 25.44775391)); + points.Add(new DataPoint(279.0441055, 35.50836182)); + points.Add(new DataPoint(281.6616287, 45.66326904)); + points.Add(new DataPoint(283.6380081, 55.82598877)); + points.Add(new DataPoint(285.7761917, 72.32995605)); + points.Add(new DataPoint(287.0616837, 88.9072876)); + points.Add(new DataPoint(287.4597855, 105.5118408)); + points.Add(new DataPoint(286.9358597, 122.0977173)); + points.Add(new DataPoint(285.4552994, 138.6187744)); + points.Add(new DataPoint(282.9833755, 155.0289917)); + points.Add(new DataPoint(279.4855118, 171.2824097)); + points.Add(new DataPoint(274.9270096, 187.3329468)); + points.Add(new DataPoint(271.4097672, 198.2474976)); + points.Add(new DataPoint(267.3623734, 209.4165039)); + points.Add(new DataPoint(262.4050369, 220.1444092)); + points.Add(new DataPoint(259.4664688, 225.1257324)); + points.Add(new DataPoint(256.1579971, 229.7359619)); + points.Add(new DataPoint(249.3205032, 237.2365723)); + points.Add(new DataPoint(241.720253, 244.1188354)); + points.Add(new DataPoint(233.794136, 250.6709595)); + points.Add(new DataPoint(225.9790115, 257.1809692)); + points.Add(new DataPoint(195.5280228, 282.9415894)); + points.Add(new DataPoint(164.2490005, 307.6559448)); + points.Add(new DataPoint(141.1689987, 324.8109741)); + points.Add(new DataPoint(133.2555008, 330.7440796)); + points.Add(new DataPoint(129.0609055, 333.3323975)); + points.Add(new DataPoint(124.5289993, 335.2859497)); + points.Add(new DataPoint(122.1544113, 335.8482361)); + points.Add(new DataPoint(119.7157364, 336.0837402)); + points.Add(new DataPoint(114.7126236, 335.969696)); + points.Add(new DataPoint(109.6527023, 335.7345581)); + points.Add(new DataPoint(104.6689987, 336.1689453)); + points.Add(new DataPoint(102.779686, 336.7739563)); + points.Add(new DataPoint(100.6949997, 337.5380859)); + points.Add(new DataPoint(98.61257172, 337.9761353)); + points.Add(new DataPoint(96.73000336, 337.6029663)); + points.Add(new DataPoint(94.59128571, 335.8127136)); + points.Add(new DataPoint(92.66425323, 333.2740784)); + points.Add(new DataPoint(90.92359161, 330.5338745)); + points.Add(new DataPoint(89.34400177, 328.1389465)); + points.Add(new DataPoint(87.08150482, 325.4346313)); + points.Add(new DataPoint(84.48600006, 322.8813171)); + points.Add(new DataPoint(78.90499115, 318.3849487)); + points.Add(new DataPoint(77.3181076, 317.091217)); + points.Add(new DataPoint(75.22336578, 315.5139465)); + points.Add(new DataPoint(73.02420807, 314.5059509)); + points.Add(new DataPoint(72.01151276, 314.4819031)); + points.Add(new DataPoint(71.12400055, 314.9199524)); + points.Add(new DataPoint(70.59803009, 315.7275085)); + points.Add(new DataPoint(70.51831818, 316.7388916)); + points.Add(new DataPoint(71.29537201, 319.0906982)); + points.Add(new DataPoint(72.65048981, 321.4106445)); + points.Add(new DataPoint(73.77899933, 323.1339722)); + points.Add(new DataPoint(77.82125092, 329.0054626)); + points.Add(new DataPoint(80.16477203, 331.7309265)); + points.Add(new DataPoint(82.71099091, 334.177948)); + points.Add(new DataPoint(84.92420197, 335.9799805)); + points.Add(new DataPoint(87.10187531, 337.9599609)); + points.Add(new DataPoint(88.68236542, 340.2526855)); + points.Add(new DataPoint(89.07314301, 341.5584412)); + points.Add(new DataPoint(89.10399628, 342.9929504)); + points.Add(new DataPoint(89.61742401, 343.8485413)); + points.Add(new DataPoint(89.34513092, 344.4684448)); + points.Add(new DataPoint(88.5472641, 344.7538757)); + points.Add(new DataPoint(87.48400116, 344.605957)); + points.Add(new DataPoint(85.91378021, 343.8370972)); + points.Add(new DataPoint(84.39550018, 342.7818298)); + points.Add(new DataPoint(81.58899689, 340.4889526)); + points.Add(new DataPoint(78.80124664, 338.1679382)); + points.Add(new DataPoint(75.64672089, 335.4441223)); + points.Add(new DataPoint(68.57024384, 329.6803589)); + points.Add(new DataPoint(64.81476593, 327.0864868)); + points.Add(new DataPoint(61.02541351, 324.9821167)); + points.Add(new DataPoint(57.28540802, 323.5902405)); + points.Add(new DataPoint(53.6780014, 323.1339722)); + points.Add(new DataPoint(55.6113739, 326.1587524)); + points.Add(new DataPoint(58.03131866, 328.9006653)); + points.Add(new DataPoint(63.8632431, 333.7370911)); + points.Add(new DataPoint(70.23856354, 338.0457153)); + points.Add(new DataPoint(76.22199249, 342.2289734)); + points.Add(new DataPoint(77.03455353, 342.7995605)); + points.Add(new DataPoint(78.11525726, 343.5061951)); + points.Add(new DataPoint(80.57563019, 345.232605)); + points.Add(new DataPoint(82.59217072, 347.217926)); + points.Add(new DataPoint(83.11811066, 348.24823)); + points.Add(new DataPoint(83.15399933, 349.2719727)); + points.Add(new DataPoint(82.68766022, 350.0397949)); + points.Add(new DataPoint(81.87210846, 350.3486328)); + points.Add(new DataPoint(79.6248703, 349.9825745)); + points.Add(new DataPoint(77.27544403, 348.9604797)); + points.Add(new DataPoint(76.33216095, 348.4492188)); + points.Add(new DataPoint(75.68700409, 348.0689697)); + points.Add(new DataPoint(66.5876236, 342.1790771)); + points.Add(new DataPoint(61.90306854, 339.4937744)); + points.Add(new DataPoint(56.897995, 337.35495)); + points.Add(new DataPoint(55.17505646, 336.726532)); + points.Add(new DataPoint(53.01000214, 336.1444702)); + points.Add(new DataPoint(51.04743958, 336.2538757)); + points.Add(new DataPoint(50.34353638, 336.7695007)); + points.Add(new DataPoint(49.93199921, 337.6999512)); + points.Add(new DataPoint(50.0814743, 339.3566589)); + points.Add(new DataPoint(51.06142426, 341.0038757)); + points.Add(new DataPoint(52.65550232, 342.6039124)); + points.Add(new DataPoint(54.64737701, 344.1190796)); + points.Add(new DataPoint(58.95914459, 346.743988)); + points.Add(new DataPoint(60.84635162, 347.7783203)); + points.Add(new DataPoint(62.26599884, 348.5769653)); + points.Add(new DataPoint(70.1079483, 352.9052734)); + points.Add(new DataPoint(78.1873703, 356.6586914)); + points.Add(new DataPoint(86.4916153, 359.8837585)); + points.Add(new DataPoint(95.00800323, 362.6269531)); + points.Add(new DataPoint(96.84983063, 363.0866394)); + points.Add(new DataPoint(98.97579193, 363.5275574)); + points.Add(new DataPoint(103.6091232, 364.5426941)); + points.Add(new DataPoint(107.965889, 366.0517273)); + points.Add(new DataPoint(109.7461472, 367.1099854)); + points.Add(new DataPoint(111.1039963, 368.43396)); + points.Add(new DataPoint(112.0068741, 370.5414429)); + points.Add(new DataPoint(112.4139938, 373.21521)); + points.Add(new DataPoint(112.6441269, 375.9950867)); + points.Add(new DataPoint(113.0159988, 378.4209595)); + points.Add(new DataPoint(113.902565, 381.2496643)); + points.Add(new DataPoint(115.2284775, 384.6081543)); + points.Add(new DataPoint(116.9393082, 388.2300415)); + points.Add(new DataPoint(118.980629, 391.8488464)); + points.Add(new DataPoint(121.2979813, 395.1981506)); + points.Add(new DataPoint(123.8369522, 398.0115662)); + points.Add(new DataPoint(126.5430984, 400.022644)); + points.Add(new DataPoint(129.3619919, 400.9649658)); + points.Add(new DataPoint(128.5566483, 397.5397949)); + points.Add(new DataPoint(126.8852463, 394.5133362)); + points.Add(new DataPoint(124.8594742, 391.6261902)); + points.Add(new DataPoint(122.9910049, 388.6189575)); + points.Add(new DataPoint(120.504631, 382.5281982)); + points.Add(new DataPoint(119.1972427, 376.1178589)); + points.Add(new DataPoint(119.0547104, 372.875061)); + points.Add(new DataPoint(119.2897568, 369.6510315)); + points.Add(new DataPoint(119.9299698, 366.4787292)); + points.Add(new DataPoint(121.0029984, 363.3909607)); + points.Add(new DataPoint(122.9761124, 359.8041687)); + points.Add(new DataPoint(125.5510788, 356.6000061)); + points.Add(new DataPoint(128.5915298, 353.7098083)); + points.Add(new DataPoint(131.9611282, 351.0648499)); + points.Add(new DataPoint(139.1423569, 346.2359619)); + points.Add(new DataPoint(146.0040054, 341.5639648)); + points.Add(new DataPoint(153.3126602, 336.4172363)); + points.Add(new DataPoint(160.8510056, 331.6148376)); + points.Add(new DataPoint(176.060997, 322.2139587)); + points.Add(new DataPoint(223.2120132, 292.0619507)); + points.Add(new DataPoint(241.0090103, 281.0089722)); + points.Add(new DataPoint(244.4554214, 278.2832642)); + points.Add(new DataPoint(248.0213699, 275.5827026)); + points.Add(new DataPoint(251.8383865, 273.5125122)); + points.Add(new DataPoint(253.8821487, 272.9029541)); + points.Add(new DataPoint(256.038002, 272.677948)); + points.Add(new DataPoint(255.5765762, 275.73526)); + points.Add(new DataPoint(254.4421158, 278.3285828)); + points.Add(new DataPoint(252.9171219, 280.7803345)); + points.Add(new DataPoint(251.2840042, 283.4129639)); + points.Add(new DataPoint(249.1513138, 287.4288635)); + points.Add(new DataPoint(247.2273636, 291.7029724)); + points.Add(new DataPoint(245.725502, 296.1263123)); + points.Add(new DataPoint(244.8589859, 300.5899658)); + points.Add(new DataPoint(244.4298477, 307.2876587)); + points.Add(new DataPoint(244.5635757, 313.9535828)); + points.Add(new DataPoint(245.2291336, 320.570282)); + points.Add(new DataPoint(246.3955154, 327.1204224)); + points.Add(new DataPoint(248.0315933, 333.5866089)); + points.Add(new DataPoint(250.1063919, 339.951416)); + points.Add(new DataPoint(252.5888138, 346.1975098)); + points.Add(new DataPoint(255.4478531, 352.3074646)); + points.Add(new DataPoint(262.1715469, 364.0494385)); + points.Add(new DataPoint(270.0290298, 375.0382385)); + points.Add(new DataPoint(278.7719803, 385.1347656)); + points.Add(new DataPoint(288.1519852, 394.1999512)); + points.Add(new DataPoint(294.92173, 399.6752014)); + points.Add(new DataPoint(302.1428604, 404.5257263)); + points.Add(new DataPoint(309.7718277, 408.7135925)); + points.Add(new DataPoint(317.7649918, 412.2009583)); + points.Add(new DataPoint(322.0982132, 413.5901184)); + points.Add(new DataPoint(326.4935989, 414.2129517)); + points.Add(new DataPoint(328.6687698, 414.1019592)); + points.Add(new DataPoint(330.8044205, 413.6372986)); + points.Add(new DataPoint(332.8823013, 412.7649841)); + points.Add(new DataPoint(334.8839798, 411.4309692)); + points.Add(new DataPoint(336.700325, 409.4726868)); + points.Add(new DataPoint(337.756813, 407.2593689)); + points.Add(new DataPoint(338.1652908, 404.8675537)); + points.Add(new DataPoint(338.0374832, 402.3738403)); + points.Add(new DataPoint(337.4851761, 399.8547668)); + points.Add(new DataPoint(336.6201553, 397.3868713)); + points.Add(new DataPoint(334.3989944, 392.9109497)); + points.Add(new DataPoint(331.2719803, 388.0760193)); + points.Add(new DataPoint(327.7468643, 383.5949097)); + points.Add(new DataPoint(319.7087479, 375.5498352)); + points.Add(new DataPoint(310.6974869, 368.4870605)); + points.Add(new DataPoint(301.1259842, 362.1179504)); + points.Add(new DataPoint(289.341011, 355.3789673)); + points.Add(new DataPoint(288.3749161, 354.5166321)); + points.Add(new DataPoint(287.4871292, 353.3829651)); + points.Add(new DataPoint(287.1665115, 352.1690369)); + points.Add(new DataPoint(287.3716812, 351.5917053)); + points.Add(new DataPoint(287.9019852, 351.0659485)); + points.Add(new DataPoint(290.0786819, 350.5000305)); + points.Add(new DataPoint(292.8593826, 350.8098145)); + points.Add(new DataPoint(295.6623917, 351.5259399)); + points.Add(new DataPoint(297.9060135, 352.1789551)); + points.Add(new DataPoint(300.6676102, 353.0059814)); + points.Add(new DataPoint(303.7700882, 354.0613708)); + points.Add(new DataPoint(310.5398636, 356.3453369)); + points.Add(new DataPoint(313.9782486, 357.3178101)); + points.Add(new DataPoint(317.2997208, 358.0065918)); + points.Add(new DataPoint(320.3897781, 358.2836609)); + points.Add(new DataPoint(323.1339798, 358.0209656)); + points.Add(new DataPoint(311.4509048, 349.4853516)); + points.Add(new DataPoint(299.2507401, 341.6953125)); + points.Add(new DataPoint(286.5124283, 334.8171387)); + points.Add(new DataPoint(273.215004, 329.0169678)); + points.Add(new DataPoint(258.7229996, 323.8179626)); + points.Add(new DataPoint(257.2878494, 323.337738)); + points.Add(new DataPoint(255.7744827, 322.6670837)); + points.Add(new DataPoint(254.4943924, 321.7168884)); + points.Add(new DataPoint(253.7590103, 320.3979492)); + points.Add(new DataPoint(253.827034, 319.0723267)); + points.Add(new DataPoint(254.5322647, 317.7234802)); + points.Add(new DataPoint(255.711647, 316.3885193)); + points.Add(new DataPoint(257.2022476, 315.1045837)); + points.Add(new DataPoint(260.465126, 312.8383789)); + points.Add(new DataPoint(261.9114456, 311.9303589)); + points.Add(new DataPoint(263.0170059, 311.2219543)); + points.Add(new DataPoint(271.8112259, 305.8678284)); + points.Add(new DataPoint(281.198616, 301.1617126)); + points.Add(new DataPoint(290.8884659, 297.0829773)); + points.Add(new DataPoint(300.590004, 293.6109619)); + points.Add(new DataPoint(310.0823746, 291.1308289)); + points.Add(new DataPoint(319.4608536, 289.9150696)); + points.Add(new DataPoint(328.9314041, 289.7652893)); + points.Add(new DataPoint(338.6999893, 290.4829712)); + points.Add(new DataPoint(354.2659988, 293.3499451)); + points.Add(new DataPoint(355.8602982, 293.1119995)); + points.Add(new DataPoint(357.197731, 292.2803345)); + points.Add(new DataPoint(358.3595047, 291.0731506)); + points.Add(new DataPoint(359.4267349, 289.7085876)); + points.Add(new DataPoint(360.4805679, 288.4048157)); + points.Add(new DataPoint(361.6021194, 287.3800049)); + points.Add(new DataPoint(362.8725357, 286.8523254)); + points.Add(new DataPoint(364.3729935, 287.0399475)); + points.Add(new DataPoint(365.9158707, 287.8427734)); + points.Add(new DataPoint(367.3995438, 289.0066833)); + points.Add(new DataPoint(370.1699905, 292.0510864)); + points.Add(new DataPoint(372.6456985, 295.4399109)); + points.Add(new DataPoint(374.788002, 298.4399719)); + points.Add(new DataPoint(394.8579788, 325.8149719)); + points.Add(new DataPoint(397.3873978, 329.0663757)); + points.Add(new DataPoint(399.9987259, 332.6233521)); + points.Add(new DataPoint(402.1026993, 336.3991089)); + points.Add(new DataPoint(402.7802811, 338.3419189)); + points.Add(new DataPoint(403.109993, 340.3069458)); + points.Add(new DataPoint(403.7992325, 340.2913818)); + points.Add(new DataPoint(404.1172256, 340.3743286)); + points.Add(new DataPoint(404.2001114, 340.691864)); + points.Add(new DataPoint(404.1839981, 341.3799744)); + points.Add(new DataPoint(405.9170609, 342.5977478)); + points.Add(new DataPoint(407.5672379, 344.3135681)); + points.Add(new DataPoint(410.6323624, 348.6974487)); + points.Add(new DataPoint(413.4063187, 353.4481201)); + points.Add(new DataPoint(414.6925125, 355.6223755)); + points.Add(new DataPoint(415.9159927, 357.4819641)); + points.Add(new DataPoint(446.1409988, 402.5239563)); + points.Add(new DataPoint(447.8941116, 404.6854248)); + points.Add(new DataPoint(449.9755325, 406.9086609)); + points.Add(new DataPoint(454.483345, 411.5653381)); + points.Add(new DataPoint(456.5896683, 414.0111694)); + points.Add(new DataPoint(458.3842239, 416.5435791)); + points.Add(new DataPoint(459.7070389, 419.1687927)); + points.Add(new DataPoint(460.3980179, 421.8929749)); + points.Add(new DataPoint(460.330513, 423.2209473)); + points.Add(new DataPoint(459.8673782, 424.4043274)); + points.Add(new DataPoint(458.1563797, 426.4813232)); + points.Add(new DataPoint(456.0694046, 428.4118958)); + points.Add(new DataPoint(454.4110184, 430.4839478)); + points.Add(new DataPoint(453.6341629, 432.6287231)); + points.Add(new DataPoint(453.2503738, 434.9552002)); + points.Add(new DataPoint(452.6709671, 439.6079712)); + points.Add(new DataPoint(451.6133499, 443.3995361)); + points.Add(new DataPoint(450.0984573, 447.2178345)); + points.Add(new DataPoint(448.1988602, 450.8544312)); + points.Add(new DataPoint(445.9870071, 454.1009521)); + points.Add(new DataPoint(445.2327957, 454.9499512)); + points.Add(new DataPoint(444.120369, 456.1347046)); + points.Add(new DataPoint(441.6337357, 459.0782166)); + points.Add(new DataPoint(440.6658401, 460.6203003)); + points.Add(new DataPoint(440.1524734, 462.0648193)); + points.Add(new DataPoint(440.2967911, 463.3034973)); + points.Add(new DataPoint(441.3020096, 464.2279663)); + points.Add(new DataPoint(443.0089798, 464.4325867)); + points.Add(new DataPoint(444.7705154, 463.7559509)); + points.Add(new DataPoint(446.3872757, 462.6180725)); + points.Add(new DataPoint(447.6599808, 461.4389648)); + points.Add(new DataPoint(449.96418, 458.7336426)); + points.Add(new DataPoint(451.896492, 455.7789612)); + points.Add(new DataPoint(454.7979813, 449.3179626)); + points.Add(new DataPoint(455.2873001, 447.6248474)); + points.Add(new DataPoint(455.8775101, 445.8508301)); + points.Add(new DataPoint(456.8382034, 444.3796387)); + points.Add(new DataPoint(458.4389725, 443.5949707)); + points.Add(new DataPoint(460.0345535, 443.7620239)); + points.Add(new DataPoint(461.7698441, 444.6128235)); + points.Add(new DataPoint(463.5780106, 445.9676208)); + points.Add(new DataPoint(465.3921585, 447.6465759)); + points.Add(new DataPoint(468.7708817, 451.2580566)); + points.Add(new DataPoint(470.2016678, 452.8309631)); + points.Add(new DataPoint(471.3709793, 454.0089722)); + points.Add(new DataPoint(478.8518143, 460.0104675)); + points.Add(new DataPoint(486.9943924, 465.3340759)); + points.Add(new DataPoint(495.4878006, 470.1171265)); + points.Add(new DataPoint(504.0210037, 474.4969482)); + points.Add(new DataPoint(509.5540848, 477.1457214)); + points.Add(new DataPoint(515.2860184, 479.3189697)); + points.Add(new DataPoint(516.4008255, 479.6032104)); + points.Add(new DataPoint(517.6022415, 479.6870728)); + points.Add(new DataPoint(518.5872879, 479.3061523)); + points.Add(new DataPoint(519.0529861, 478.1959534)); + points.Add(new DataPoint(518.8626175, 476.8864441)); + points.Add(new DataPoint(518.1857986, 475.7019958)); + points.Add(new DataPoint(515.84095, 473.7122192)); + points.Add(new DataPoint(512.9545975, 472.234314)); + points.Add(new DataPoint(510.4629593, 471.2759705)); + points.Add(new DataPoint(507.6074905, 470.256958)); + points.Add(new DataPoint(504.0599442, 468.8930969)); + points.Add(new DataPoint(496.255867, 465.2143555)); + points.Add(new DataPoint(492.682869, 462.9411621)); + points.Add(new DataPoint(489.7850418, 460.4066467)); + points.Add(new DataPoint(487.9041214, 457.6316223)); + points.Add(new DataPoint(487.4518509, 456.1604309)); + points.Add(new DataPoint(487.3819656, 454.6369629)); + points.Add(new DataPoint(491.1286697, 455.6597595)); + points.Add(new DataPoint(494.7612381, 457.2131958)); + points.Add(new DataPoint(498.3274002, 458.9732666)); + points.Add(new DataPoint(501.8750076, 460.6159668)); + points.Add(new DataPoint(508.6896439, 463.2619934)); + points.Add(new DataPoint(515.7986526, 465.5415649)); + points.Add(new DataPoint(523.0577469, 467.2131042)); + points.Add(new DataPoint(530.3230057, 468.0349731)); + points.Add(new DataPoint(536.5177689, 468.0764465)); + points.Add(new DataPoint(542.669014, 467.3979492)); + points.Add(new DataPoint(543.9200516, 466.9833069)); + points.Add(new DataPoint(545.0242386, 466.2348328)); + points.Add(new DataPoint(545.5588455, 465.2004089)); + points.Add(new DataPoint(545.1009598, 463.927948)); + points.Add(new DataPoint(544.175972, 463.0953064)); + points.Add(new DataPoint(542.9511795, 462.4622803)); + points.Add(new DataPoint(539.9430008, 461.6712036)); + points.Add(new DataPoint(536.7585526, 461.3072815)); + points.Add(new DataPoint(534.0799637, 461.1229553)); + points.Add(new DataPoint(520.8706131, 459.9545898)); + points.Add(new DataPoint(514.2815628, 459.0859985)); + points.Add(new DataPoint(507.7789993, 457.8509521)); + points.Add(new DataPoint(506.4525833, 457.5542603)); + points.Add(new DataPoint(504.8121414, 457.1582336)); + points.Add(new DataPoint(501.1944656, 455.9819641)); + points.Add(new DataPoint(499.5198441, 455.1585083)); + points.Add(new DataPoint(498.1362991, 454.1494141)); + points.Add(new DataPoint(497.1952591, 452.9331055)); + points.Add(new DataPoint(496.8479691, 451.4879456)); + points.Add(new DataPoint(497.117012, 450.6549377)); + points.Add(new DataPoint(497.8393021, 450.1732788)); + points.Add(new DataPoint(500.1341019, 449.9840698)); + points.Add(new DataPoint(502.7133255, 450.3605652)); + points.Add(new DataPoint(503.7911453, 450.5859985)); + points.Add(new DataPoint(504.557991, 450.7429504)); + points.Add(new DataPoint(511.3823318, 451.3563843)); + points.Add(new DataPoint(518.3227615, 451.0169373)); + points.Add(new DataPoint(525.2244949, 449.9740295)); + points.Add(new DataPoint(531.932991, 448.4769592)); + points.Add(new DataPoint(532.8128738, 448.2760315)); + points.Add(new DataPoint(534.1250076, 447.9761353)); + points.Add(new DataPoint(537.3393631, 447.0833435)); + points.Add(new DataPoint(538.8882523, 446.4923706)); + points.Add(new DataPoint(540.1627884, 445.8063354)); + points.Add(new DataPoint(540.9862747, 445.0262146)); + points.Add(new DataPoint(541.1820145, 444.1529541)); + points.Add(new DataPoint(540.6489334, 443.4705505)); + points.Add(new DataPoint(539.4853592, 443.0022888)); + points.Add(new DataPoint(537.8792191, 442.7068176)); + points.Add(new DataPoint(536.0183792, 442.5428467)); + points.Add(new DataPoint(532.2842484, 442.4439392)); + points.Add(new DataPoint(530.7866898, 442.4263916)); + points.Add(new DataPoint(529.7860184, 442.3749695)); + points.Add(new DataPoint(522.7177811, 441.8120117)); + points.Add(new DataPoint(515.5401077, 441.6993408)); + points.Add(new DataPoint(501.2470169, 442.2559509)); + points.Add(new DataPoint(498.6232986, 442.5908508)); + points.Add(new DataPoint(495.6024857, 442.9595947)); + points.Add(new DataPoint(492.7076492, 442.7930298)); + points.Add(new DataPoint(491.4709549, 442.3311157)); + points.Add(new DataPoint(490.4619827, 441.5219727)); + points.Add(new DataPoint(489.3800125, 439.9010925)); + points.Add(new DataPoint(488.6179276, 438.0290833)); + points.Add(new DataPoint(487.6418533, 433.8266907)); + points.Add(new DataPoint(486.7113724, 429.5046997)); + points.Add(new DataPoint(486.0061722, 427.4831848)); + points.Add(new DataPoint(485.0039749, 425.6529541)); + points.Add(new DataPoint(464.5570145, 397.51297)); + points.Add(new DataPoint(441.859993, 359.0939636)); + points.Add(new DataPoint(419.5407486, 322.4837036)); + points.Add(new DataPoint(397.7539749, 285.5569458)); + points.Add(new DataPoint(392.6534195, 277.3995361)); + points.Add(new DataPoint(387.2727432, 269.1647034)); + points.Add(new DataPoint(382.4174271, 260.6722412)); + points.Add(new DataPoint(380.4385147, 256.2730713)); + points.Add(new DataPoint(378.8929825, 251.7419434)); + points.Add(new DataPoint(378.4342422, 247.8017578)); + points.Add(new DataPoint(378.8919754, 243.618103)); + points.Add(new DataPoint(379.8762283, 239.4705811)); + points.Add(new DataPoint(380.9969864, 235.6389771)); + points.Add(new DataPoint(388.6119766, 208.7999878)); + points.Add(new DataPoint(391.0815811, 198.9997559)); + points.Add(new DataPoint(393.3593521, 188.8963623)); + points.Add(new DataPoint(395.3594131, 178.5748901)); + points.Add(new DataPoint(396.9958572, 168.1204834)); + points.Add(new DataPoint(398.1828384, 157.6182251)); + points.Add(new DataPoint(398.8344498, 147.1533203)); + points.Add(new DataPoint(398.8647842, 136.8108521)); + points.Add(new DataPoint(398.1879959, 126.6759644)); + points.Add(new DataPoint(395.2633438, 103.3844604)); + points.Add(new DataPoint(393.3301468, 91.60638428)); + points.Add(new DataPoint(390.9952469, 79.85656738)); + points.Add(new DataPoint(388.1928177, 68.22253418)); + points.Add(new DataPoint(384.8570328, 56.79168701)); + points.Add(new DataPoint(380.9220352, 45.65136719)); + points.Add(new DataPoint(376.3219986, 34.88897705)); + points.Add(new DataPoint(371.1558609, 23.22259521)); + points.Add(new DataPoint(366.6760025, 11.27197266)); + points.Add(new DataPoint(365.8827591, 9.214355469)); + points.Add(new DataPoint(365.0445023, 6.805603027)); + points.Add(new DataPoint(364.7049942, 4.406799316)); + points.Add(new DataPoint(364.892189, 3.323974609)); + points.Add(new DataPoint(365.4079971, 2.378967285)); + points.Add(new DataPoint(366.5955276, 1.439331055)); + points.Add(new DataPoint(368.2348099, 0.826660156)); + points.Add(new DataPoint(372.3311234, 0.333557129)); + points.Add(new DataPoint(376.6218643, 0.40246582)); + points.Add(new DataPoint(378.5041885, 0.4921875)); + points.Add(new DataPoint(380.0319901, 0.535949707)); + points.Add(new DataPoint(420.2889786, 0)); + points.Add(new DataPoint(527.1040115, 0)); + points.Add(new DataPoint(558.0751419, 0.09362793)); + points.Add(new DataPoint(573.5140457, 0.204589844)); + points.Add(new DataPoint(588.6629715, 0.352966309)); + points.Add(new DataPoint(588.6629715, 0.352966309)); + return points; + } + + [Example("Conway's Game of Life")] + public static PlotModel ConwayLife() + { + // http://en.wikipedia.org/wiki/Conway's_Game_of_Life + var model = new PlotModel { Title = "Conway's Game of Life", Subtitle = "Click the mouse to step to the next generation." }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, StartPosition = 1, EndPosition = 0 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + int m = 40; + int n = 40; + var matrix = new double[m, n]; + var ms = new MatrixSeries { Matrix = matrix }; + + Action blinker = (i, j) => { matrix[i, j] = matrix[i, j + 1] = matrix[i, j + 2] = 1; }; + Action glider = (i, j) => { matrix[i, j] = matrix[i + 1, j + 1] = matrix[i + 1, j + 2] = matrix[i + 2, j] = matrix[i + 2, j + 1] = 1; }; + Action rpentomino = (i, j) => { matrix[i, j + 1] = matrix[i, j + 2] = matrix[i + 1, j] = matrix[i + 1, j + 1] = matrix[i + 2, j + 1] = 1; }; + + blinker(2, 10); + glider(2, 2); + rpentomino(20, 20); + + model.Series.Add(ms); + int g = 0; + Action stepToNextGeneration = () => + { + var next = new double[m, n]; + for (int i = 1; i < m - 1; i++) + { + for (int j = 1; j < n - 1; j++) + { + int k = (int)(matrix[i - 1, j - 1] + matrix[i - 1, j] + matrix[i - 1, j + 1] + matrix[i, j - 1] + + matrix[i, j + 1] + matrix[i + 1, j - 1] + matrix[i + 1, j] + + matrix[i + 1, j + 1]); + + if (matrix[i, j].Equals(0) && k == 3) + { + // Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction. + next[i, j] = 1; + continue; + } + + if (matrix[i, j].Equals(1) && (k == 2 || k == 3)) + { + // Any live cell with two or three live neighbours lives on to the next generation. + next[i, j] = 1; + } + + // Any live cell with fewer than two live neighbours dies, as if caused by under-population. + // Any live cell with more than three live neighbours dies, as if by overcrowding. + } + } + + g++; + ms.Title = "Generation " + g; + ms.Matrix = matrix = next; + model.InvalidatePlot(true); + }; + + model.MouseDown += (s, e) => + { + if (e.ChangedButton == OxyMouseButton.Left) + { + stepToNextGeneration(); + e.Handled = true; + } + }; + + return model; + } + + [Example("Mandelbrot custom series")] + public static PlotModel Mandelbrot() + { + // http://en.wikipedia.org/wiki/Mandelbrot_set + var model = new PlotModel { Title = "The Mandelbrot set" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = -1.4, Maximum = 1.4 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = -2, Maximum = 1 }); + model.Axes.Add( + new LinearColorAxis + { + Position = AxisPosition.Right, + Minimum = 0, + Maximum = 64, + Palette = OxyPalettes.Jet(64), + HighColor = OxyColors.Black + }); + model.Series.Add(new MandelbrotSetSeries()); + return model; + } + + [Example("Julia set custom series")] + public static PlotModel JuliaSet() + { + // http://en.wikipedia.org/wiki/Julia_set + var model = new PlotModel { Subtitle = "Click and move the mouse" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = -2, Maximum = 2 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = -2.5, Maximum = 2.5 }); + model.Axes.Add( + new LinearColorAxis + { + Position = AxisPosition.Right, + Minimum = 0, + Maximum = 64, + Palette = OxyPalettes.Jet(64), + HighColor = OxyColors.Black + }); + + var jss = new JuliaSetSeries(); + + // Delegate to set the c and title + Action setConstant = (c1, c2) => + { + jss.C1 = c1; + jss.C2 = c2; + model.Title = string.Format("The Julia set, c={0:0.00000}+{1:0.00000}i", jss.C1, jss.C2); + }; + + // Update the c by the position where the mouse was clicked/moved + Action handleMouseEvent = e => + { + var c = jss.InverseTransform(e.Position); + setConstant(c.X, c.Y); + model.InvalidatePlot(true); + e.Handled = true; + }; + jss.MouseDown += (s, e) => handleMouseEvent(e); + jss.MouseMove += (s, e) => handleMouseEvent(e); + + // set the initial c + setConstant(-0.726895347709114071439, 0.188887129043845954792); + + model.Series.Add(jss); + return model; + } + + [Example("Elephant curve")] + public static PlotModel ElephantCurve() + { + // http://www.wolframalpha.com/input/?i=elephant+curve + // See also https://gist.github.com/purem/4687549/raw/8dda1f16cc70469aedecec19af1530534eadbae3/Wolfram+alpha+named+parametric+curves + Func sin = Math.Sin; + Func x = + t => + -27d / 5 * sin(3d / 2 - 30 * t) - 16d / 3 * sin(9d / 8 - 29 * t) - 29d / 5 * sin(5d / 4 - 27 * t) + - 8d / 3 * sin(1d / 4 - 26 * t) - 25d / 7 * sin(1d / 3 - 25 * t) - 31d / 4 * sin(4d / 7 - 22 * t) + - 25d / 4 * sin(4d / 3 - 20 * t) - 33d / 2 * sin(2d / 3 - 19 * t) - 67d / 4 * sin(6d / 5 - 16 * t) + - 100d / 11 * sin(1d / 4 - 10 * t) - 425d / 7 * sin(1 - 4 * t) + 149d / 4 * sin(8 * t) + + 1172d / 3 * sin(t + 21d / 5) + 661d / 11 * sin(2 * t + 3) + 471d / 8 * sin(3 * t + 10d / 7) + + 211d / 7 * sin(5 * t + 13d / 4) + 39d / 4 * sin(6 * t + 10d / 7) + 139d / 10 * sin(7 * t + 7d / 6) + + 77d / 3 * sin(9 * t + 18d / 7) + 135d / 8 * sin(11 * t + 1d / 2) + 23d / 4 * sin(12 * t + 8d / 5) + + 95d / 4 * sin(13 * t + 4) + 31d / 4 * sin(14 * t + 3d / 5) + 67d / 11 * sin(15 * t + 7d / 3) + + 127d / 21 * sin(17 * t + 17d / 4) + 95d / 8 * sin(18 * t + 7d / 8) + + 32d / 11 * sin(21 * t + 8d / 3) + 81d / 10 * sin(23 * t + 45d / 11) + + 13d / 3 * sin(24 * t + 13d / 4) + 7d / 4 * sin(28 * t + 3d / 2) + 11d / 5 * sin(31 * t + 5d / 2) + + 1d / 3 * sin(32 * t + 12d / 5) + 13d / 4 * sin(33 * t + 22d / 5) + 14d / 3 * sin(34 * t + 9d / 4) + + 9d / 5 * sin(35 * t + 8d / 5) + 17d / 9 * sin(36 * t + 22d / 5) + 1d / 3 * sin(37 * t + 15d / 7) + + 3d / 2 * sin(38 * t + 39d / 10) + 4d / 3 * sin(39 * t + 7d / 2) + 5d / 3 * sin(40 * t + 17d / 6); + Func y = + t => + -13d / 7 * sin(1d / 2 - 40 * t) - 31d / 8 * sin(1d / 11 - 34 * t) - 12d / 5 * sin(1d / 4 - 31 * t) + - 9d / 4 * sin(4d / 3 - 29 * t) - 5d / 3 * sin(4d / 3 - 28 * t) - 11d / 2 * sin(6d / 5 - 26 * t) + - 17d / 7 * sin(3d / 2 - 25 * t) - 5d / 2 * sin(1 - 24 * t) - 39d / 7 * sin(1 - 19 * t) + - 59d / 5 * sin(2d / 3 - 18 * t) - 179d / 9 * sin(13d / 12 - 12 * t) + - 103d / 2 * sin(1d / 10 - 9 * t) - 356d / 5 * sin(1 - 5 * t) - 429d / 2 * sin(20d / 19 - t) + + 288d / 5 * sin(2 * t + 10d / 3) + 53d / 6 * sin(3 * t + 5d / 2) + 351d / 7 * sin(4 * t + 5d / 2) + + 201d / 4 * sin(6 * t + 17d / 7) + 167d / 3 * sin(7 * t + 19d / 5) + 323d / 5 * sin(8 * t + 1d / 4) + + 153d / 7 * sin(10 * t + 2d / 3) + 71d / 5 * sin(11 * t + 6d / 5) + + 47d / 12 * sin(13 * t + 11d / 5) + 391d / 26 * sin(14 * t + 2) + 164d / 11 * sin(15 * t + 1d / 7) + + 11d / 2 * sin(16 * t + 2d / 3) + 31d / 3 * sin(17 * t + 1d / 7) + 54d / 11 * sin(20 * t + 1d / 4) + + 43d / 5 * sin(21 * t + 13d / 3) + 13d / 5 * sin(22 * t + 3d / 2) + 17d / 5 * sin(23 * t + 11d / 5) + + 19d / 10 * sin(27 * t + 4) + 15d / 2 * sin(30 * t + 55d / 18) + 4d / 3 * sin(32 * t + 3d / 5) + + 5d / 3 * sin(33 * t + 4) + 27d / 7 * sin(35 * t + 13d / 6) + 1d / 4 * sin(36 * t + 43d / 11) + + 16d / 5 * sin(37 * t + 9d / 2) + 20d / 19 * sin(38 * t + 23d / 6) + 8d / 3 * sin(39 * t + 4d / 7); + + var model = new PlotModel { Title = "Elephant curve", PlotType = PlotType.Cartesian }; + model.Series.Add(new FunctionSeries(x, y, 0, Math.PI * 2, 1000)); + return model; + } + + [Example("PI curve")] + public static PlotModel PiCurve() + { + // http://www.wolframalpha.com/input/?i=pi+curve + Func sin = Math.Sin; + Func x = + t => 17d / 31 * sin(235d / 57 - 32 * t) + 19d / 17 * sin(192d / 55 - 30 * t) + 47d / 32 * sin(69d / 25 - 29 * t) + 35d / 26 * sin(75d / 34 - 27 * t) + 6d / 31 * sin(23d / 10 - 26 * t) + 35d / 43 * sin(10d / 33 - 25 * t) + 126d / 43 * sin(421d / 158 - 24 * t) + 143d / 57 * sin(35d / 22 - 22 * t) + 106d / 27 * sin(84d / 29 - 21 * t) + 88d / 25 * sin(23d / 27 - 20 * t) + 74d / 27 * sin(53d / 22 - 19 * t) + 44d / 53 * sin(117d / 25 - 18 * t) + 126d / 25 * sin(88d / 49 - 17 * t) + 79d / 11 * sin(43d / 26 - 16 * t) + 43d / 12 * sin(41d / 17 - 15 * t) + 47d / 27 * sin(244d / 81 - 14 * t) + 8d / 5 * sin(79d / 19 - 13 * t) + 373d / 46 * sin(109d / 38 - 12 * t) + 1200d / 31 * sin(133d / 74 - 11 * t) + 67d / 24 * sin(157d / 61 - 10 * t) + 583d / 28 * sin(13d / 8 - 8 * t) + 772d / 35 * sin(59d / 16 - 7 * t) + 3705d / 46 * sin(117d / 50 - 6 * t) + 862d / 13 * sin(19d / 8 - 5 * t) + 6555d / 34 * sin(157d / 78 - 3 * t) + 6949d / 13 * sin(83d / 27 - t) - 6805d / 54 * sin(2 * t + 1d / 145) - 5207d / 37 * sin(4 * t + 49d / 74) - 1811d / 58 * sin(9 * t + 55d / 43) - 63d / 20 * sin(23 * t + 2d / 23) - 266d / 177 * sin(28 * t + 13d / 18) - 2d / 21 * sin(31 * t + 7d / 16); + Func y = + t => 70d / 37 * sin(65d / 32 - 32 * t) + 11d / 12 * sin(98d / 41 - 31 * t) + 26d / 29 * sin(35d / 12 - 30 * t) + 54d / 41 * sin(18d / 7 - 29 * t) + 177d / 71 * sin(51d / 19 - 27 * t) + 59d / 34 * sin(125d / 33 - 26 * t) + 49d / 29 * sin(18d / 11 - 25 * t) + 151d / 75 * sin(59d / 22 - 24 * t) + 52d / 9 * sin(118d / 45 - 22 * t) + 52d / 33 * sin(133d / 52 - 21 * t) + 37d / 45 * sin(61d / 14 - 20 * t) + 143d / 46 * sin(144d / 41 - 19 * t) + 254d / 47 * sin(19d / 52 - 18 * t) + 246d / 35 * sin(92d / 25 - 17 * t) + 722d / 111 * sin(176d / 67 - 16 * t) + 136d / 23 * sin(3d / 19 - 15 * t) + 273d / 25 * sin(32d / 21 - 13 * t) + 229d / 33 * sin(117d / 28 - 12 * t) + 19d / 4 * sin(43d / 11 - 11 * t) + 135d / 8 * sin(23d / 10 - 10 * t) + 205d / 6 * sin(33d / 23 - 8 * t) + 679d / 45 * sin(55d / 12 - 7 * t) + 101d / 8 * sin(11d / 12 - 6 * t) + 2760d / 59 * sin(40d / 11 - 5 * t) + 1207d / 18 * sin(21d / 23 - 4 * t) + 8566d / 27 * sin(39d / 28 - 3 * t) + 12334d / 29 * sin(47d / 37 - 2 * t) + 15410d / 39 * sin(185d / 41 - t) - 596d / 17 * sin(9 * t + 3d / 26) - 247d / 28 * sin(14 * t + 25d / 21) - 458d / 131 * sin(23 * t + 21d / 37) - 41d / 36 * sin(28 * t + 7d / 8); + + var model = new PlotModel { Title = "PI curve", PlotType = PlotType.Cartesian }; + model.Series.Add(new FunctionSeries(x, y, 0, Math.PI * 2, 1000)); + return model; + } + + [Example("Angelina Jolie curve")] + public static PlotModel AngelinaJolieCurve() + { + // http://www.wolframalpha.com/input/?i=Angelina+Jolie+curve + + // Heaviside step function + Func theta = x => x < 0 ? 0 : 1; + + Func sin = Math.Sin; + double pi = Math.PI; + Func xt = + t => + ((-23d / 4 * sin(29d / 20 - 41 * t) - 47d / 15 * sin(47d / 34 - 38 * t) + - 979d / 196 * sin(45d / 31 - 36 * t) - 59d / 6 * sin(25d / 17 - 33 * t) + - 259d / 26 * sin(104d / 69 - 32 * t) - 49d / 24 * sin(57d / 37 - 31 * t) + - 37d / 21 * sin(32d / 23 - 30 * t) - 324d / 31 * sin(85d / 57 - 27 * t) + - 47d / 26 * sin(24d / 17 - 25 * t) - 247d / 29 * sin(39d / 25 - 24 * t) + - 834d / 71 * sin(63d / 41 - 23 * t) - 475d / 49 * sin(62d / 41 - 22 * t) + - 20d / 7 * sin(29d / 20 - 17 * t) - 1286d / 61 * sin(98d / 65 - 16 * t) + - 2312d / 25 * sin(20d / 13 - 14 * t) - 2662d / 41 * sin(23d / 15 - 13 * t) + - 2292d / 131 * sin(92d / 61 - 11 * t) - 1690d / 37 * sin(48d / 31 - 8 * t) + + 993d / 23 * sin(t + 52d / 33) + 407d / 31 * sin(2 * t + 25d / 16) + + 1049d / 57 * sin(3 * t + 49d / 31) + 1385d / 42 * sin(4 * t + 80d / 17) + + 2929d / 40 * sin(5 * t + 49d / 31) + 500d / 39 * sin(6 * t + 164d / 35) + + 116d / 25 * sin(7 * t + 155d / 33) + 2593d / 26 * sin(9 * t + 43d / 27) + + 200d / 37 * sin(10 * t + 65d / 42) + 2866d / 39 * sin(12 * t + 133d / 83) + + 703d / 234 * sin(15 * t + 46d / 27) + 133d / 8 * sin(18 * t + 13d / 8) + + 716d / 33 * sin(19 * t + 27d / 17) + 180d / 53 * sin(20 * t + 47d / 30) + + 476d / 31 * sin(21 * t + 57d / 35) + 73d / 22 * sin(26 * t + 77d / 48) + + 549d / 49 * sin(28 * t + 44d / 27) + 657d / 68 * sin(29 * t + 27d / 17) + + 29d / 22 * sin(34 * t + 140d / 31) + 180d / 49 * sin(35 * t + 14d / 9) + + 43d / 4 * sin(37 * t + 77d / 46) + 68d / 23 * sin(39 * t + 39d / 25) + + 80d / 47 * sin(40 * t + 49d / 27) + 16829d / 29) * theta(119 * pi - t) + * theta(t - 115 * pi) + + (-43d / 40 * sin(39d / 29 - 62 * t) - 44d / 17 * sin(56d / 37 - 58 * t) + - 23d / 39 * sin(14d / 11 - 57 * t) - 59d / 10 * sin(49d / 32 - 51 * t) + - 215d / 31 * sin(20d / 13 - 46 * t) - 447d / 22 * sin(110d / 73 - 34 * t) + - 407d / 39 * sin(54d / 35 - 33 * t) - 432d / 35 * sin(14d / 9 - 29 * t) + - 175d / 33 * sin(35d / 24 - 28 * t) - 102d / 5 * sin(64d / 41 - 22 * t) + + 833d / 46 * sin(t + 131d / 28) + 1360d / 9 * sin(2 * t + 52d / 33) + + 139d / 3 * sin(3 * t + 43d / 27) + 108d / 29 * sin(4 * t + 244d / 53) + + 4269d / 41 * sin(5 * t + 113d / 24) + 7407d / 31 * sin(6 * t + 65d / 41) + + 2159d / 32 * sin(7 * t + 103d / 22) + 5476d / 51 * sin(8 * t + 37d / 23) + + 3855d / 37 * sin(9 * t + 60d / 37) + 2247d / 46 * sin(10 * t + 18d / 11) + + 216d / 31 * sin(11 * t + 81d / 40) + 3364d / 35 * sin(12 * t + 69d / 43) + + 1492d / 21 * sin(13 * t + 47d / 10) + 1981d / 29 * sin(14 * t + 21d / 13) + + 1852d / 35 * sin(15 * t + 18d / 11) + 255d / 23 * sin(16 * t + 72d / 41) + + 499d / 25 * sin(17 * t + 134d / 29) + 754d / 17 * sin(18 * t + 57d / 35) + + 203d / 31 * sin(19 * t + 35d / 19) + 1289d / 32 * sin(20 * t + 41d / 25) + + 65d / 21 * sin(21 * t + 55d / 13) + 731d / 31 * sin(23 * t + 34d / 21) + + 816d / 83 * sin(24 * t + 23d / 14) + 467d / 29 * sin(25 * t + 197d / 42) + + 496d / 37 * sin(26 * t + 64d / 39) + 34d / 9 * sin(27 * t + 40d / 27) + + 204d / 23 * sin(30 * t + 76d / 43) + 34d / 3 * sin(31 * t + 50d / 29) + + 1579d / 57 * sin(32 * t + 51d / 31) + 37d / 6 * sin(35 * t + 69d / 44) + + 128d / 21 * sin(36 * t + 21d / 13) + 194d / 83 * sin(37 * t + 52d / 27) + + 35d / 37 * sin(38 * t + 46d / 19) + 39d / 38 * sin(39 * t + 234d / 55) + + 113d / 16 * sin(40 * t + 71d / 43) + 126d / 101 * sin(41 * t + 145d / 83) + + 13d / 6 * sin(42 * t + 184d / 41) + 100d / 31 * sin(43 * t + 117d / 25) + + 355d / 36 * sin(44 * t + 48d / 29) + 148d / 57 * sin(45 * t + 30d / 17) + + 3d / 2 * sin(47 * t + 51d / 28) + 107d / 61 * sin(48 * t + 27d / 16) + + 72d / 13 * sin(49 * t + 93d / 56) + 55d / 37 * sin(50 * t + 144d / 31) + + 53d / 24 * sin(52 * t + 59d / 34) + 182d / 47 * sin(53 * t + 41d / 24) + + 481d / 103 * sin(54 * t + 110d / 61) + 97d / 29 * sin(55 * t + 89d / 19) + + 7d / 4 * sin(56 * t + 49d / 30) + 82d / 37 * sin(59 * t + 55d / 28) + + 20d / 13 * sin(60 * t + 45d / 23) + 147d / 34 * sin(61 * t + 77d / 45) - 27563d / 35) + * theta(115 * pi - t) * theta(t - 111 * pi) + + (-11d / 13 * sin(37d / 33 - 98 * t) - 13d / 32 * sin(69d / 44 - 97 * t) + - 33d / 19 * sin(38d / 29 - 88 * t) - 124d / 45 * sin(44d / 29 - 87 * t) + - 72d / 37 * sin(56d / 43 - 77 * t) - 78d / 77 * sin(197d / 131 - 72 * t) + - 39d / 58 * sin(7d / 33 - 54 * t) - 31d / 20 * sin(26d / 25 - 37 * t) + - 265d / 58 * sin(122d / 81 - 35 * t) - 4901d / 196 * sin(49d / 32 - 12 * t) + - 7950d / 49 * sin(91d / 58 - 8 * t) - 515d / 12 * sin(25d / 16 - 5 * t) + + 21289d / 23 * sin(t + 80d / 51) + 4245d / 8 * sin(2 * t + 36d / 23) + + 394321d / 930 * sin(3 * t + 113d / 24) + 4699d / 17 * sin(4 * t + 113d / 24) + + 1931d / 41 * sin(6 * t + 43d / 28) + 745d / 24 * sin(7 * t + 40d / 27) + + 861d / 8 * sin(9 * t + 113d / 24) + 1348d / 15 * sin(10 * t + 146d / 31) + + 1015d / 39 * sin(11 * t + 43d / 28) + 590d / 39 * sin(13 * t + 53d / 34) + + 271d / 37 * sin(14 * t + 48d / 31) + 268d / 29 * sin(15 * t + 63d / 38) + + 443d / 25 * sin(16 * t + 127d / 27) + 872d / 37 * sin(17 * t + 53d / 35) + + 2329d / 82 * sin(18 * t + 145d / 31) + 11d / 4 * sin(19 * t + 832d / 185) + + 1139d / 36 * sin(20 * t + 49d / 32) + 203d / 13 * sin(21 * t + 114d / 25) + + 2807d / 72 * sin(22 * t + 48d / 31) + 639d / 26 * sin(23 * t + 19d / 12) + + 163d / 23 * sin(24 * t + 43d / 26) + 517d / 36 * sin(25 * t + 75d / 47) + + 359d / 30 * sin(26 * t + 159d / 34) + 603d / 32 * sin(27 * t + 46d / 31) + + 1679d / 34 * sin(28 * t + 387d / 83) + 269d / 22 * sin(29 * t + 41d / 28) + + 94d / 39 * sin(30 * t + 56d / 51) + 1219d / 54 * sin(31 * t + 51d / 11) + + 535d / 29 * sin(32 * t + 61d / 41) + 17d / 52 * sin(33 * t + 54d / 49) + + 133d / 33 * sin(34 * t + 308d / 67) + 73d / 18 * sin(36 * t + 262d / 57) + + 131d / 20 * sin(38 * t + 134d / 29) + 391d / 72 * sin(39 * t + 428d / 93) + + 505d / 26 * sin(40 * t + 3d / 2) + 39d / 10 * sin(41 * t + 256d / 57) + + 76d / 21 * sin(42 * t + 22d / 13) + 341d / 37 * sin(43 * t + 16d / 11) + + 67d / 39 * sin(44 * t + 93d / 22) + 211d / 26 * sin(45 * t + 167d / 36) + + 161d / 20 * sin(46 * t + 19d / 13) + 175d / 23 * sin(47 * t + 124d / 27) + + 229d / 35 * sin(48 * t + 59d / 39) + 23d / 19 * sin(49 * t + 36d / 23) + + 59d / 34 * sin(50 * t + 40d / 9) + 399d / 46 * sin(51 * t + 75d / 52) + + 351d / 49 * sin(52 * t + 119d / 26) + 23d / 17 * sin(53 * t + 49d / 44) + + 179d / 17 * sin(55 * t + 308d / 67) + 74d / 25 * sin(56 * t + 84d / 61) + + 7d / 10 * sin(57 * t + 21d / 17) + 58d / 39 * sin(58 * t + 94d / 21) + + 44d / 23 * sin(59 * t + 97d / 54) + 265d / 36 * sin(60 * t + 47d / 32) + + 44d / 87 * sin(61 * t + 49d / 26) + 899d / 142 * sin(62 * t + 143d / 31) + + 109d / 29 * sin(63 * t + 93d / 70) + 391d / 55 * sin(64 * t + 51d / 11) + + 24d / 37 * sin(65 * t + 29d / 38) + 151d / 30 * sin(66 * t + 78d / 17) + + 291d / 50 * sin(67 * t + 44d / 29) + 124d / 29 * sin(68 * t + 82d / 55) + + 83d / 52 * sin(69 * t + 95d / 47) + 22d / 21 * sin(70 * t + 81d / 38) + + 301d / 47 * sin(71 * t + 13d / 9) + 341d / 28 * sin(73 * t + 239d / 52) + + 133d / 22 * sin(74 * t + 14d / 11) + 245d / 39 * sin(75 * t + 47d / 10) + + 136d / 29 * sin(76 * t + 537d / 115) + 250d / 43 * sin(78 * t + 25d / 17) + + 30d / 41 * sin(79 * t + 83d / 23) + 5d / 2 * sin(80 * t + 198d / 113) + + 129d / 29 * sin(81 * t + 21d / 13) + 169d / 32 * sin(82 * t + 73d / 47) + + 28d / 29 * sin(83 * t + 133d / 33) + 129d / 40 * sin(84 * t + 86d / 19) + + 34d / 11 * sin(85 * t + 56d / 43) + 143d / 32 * sin(86 * t + 163d / 35) + + 77d / 25 * sin(89 * t + 27d / 20) + 418d / 93 * sin(90 * t + 166d / 37) + + 103d / 37 * sin(91 * t + 55d / 39) + 16d / 13 * sin(92 * t + 45d / 26) + + 87d / 40 * sin(93 * t + 55d / 32) + 53d / 25 * sin(94 * t + 67d / 41) + + 58d / 43 * sin(95 * t + 26d / 15) + 67d / 30 * sin(96 * t + 149d / 99) + + 155d / 41 * sin(99 * t + 124d / 27) - 6637d / 21) * theta(111 * pi - t) + * theta(t - 107 * pi) + + (-27d / 23 * sin(5d / 12 - 35 * t) - 94d / 23 * sin(32d / 31 - 34 * t) + - 26d / 9 * sin(17d / 27 - 33 * t) - 226d / 31 * sin(43d / 33 - 32 * t) + - 211d / 50 * sin(38d / 25 - 29 * t) - 11d / 18 * sin(18d / 23 - 28 * t) + - 23d / 18 * sin(32d / 31 - 27 * t) - 255d / 94 * sin(27d / 20 - 25 * t) + - 41d / 31 * sin(65d / 47 - 21 * t) - 59d / 12 * sin(33d / 28 - 19 * t) + - 160d / 31 * sin(31d / 29 - 18 * t) - 859d / 78 * sin(165d / 124 - 17 * t) + - 137d / 13 * sin(37d / 26 - 16 * t) - 25d / 23 * sin(175d / 117 - 9 * t) + + 937d / 6 * sin(t + 41d / 26) + 41d / 9 * sin(2 * t + 163d / 36) + + 563d / 33 * sin(3 * t + 21d / 13) + 19d / 8 * sin(4 * t + 181d / 40) + + 53d / 15 * sin(5 * t + 77d / 46) + 10d / 31 * sin(6 * t + 73d / 23) + + 133d / 39 * sin(7 * t + 67d / 42) + 5d / 9 * sin(8 * t + 30d / 19) + + 22d / 13 * sin(10 * t + 47d / 27) + 108d / 37 * sin(11 * t + 90d / 53) + + 37d / 40 * sin(12 * t + 47d / 23) + 18d / 13 * sin(13 * t + 49d / 11) + + 63d / 40 * sin(14 * t + 139d / 31) + 453d / 34 * sin(15 * t + 65d / 41) + + 27d / 32 * sin(20 * t + 13d / 46) + 8d / 19 * sin(22 * t + 58d / 35) + + 19d / 30 * sin(23 * t + 157d / 34) + 10d / 23 * sin(24 * t + 7d / 27) + + 51d / 31 * sin(26 * t + 29d / 24) + 83d / 26 * sin(30 * t + 52d / 31) + + 179d / 45 * sin(31 * t + 56d / 37) + 14d / 23 * sin(36 * t + 45d / 29) + + 56d / 33 * sin(37 * t + 69d / 37) + 62d / 61 * sin(38 * t + 192d / 47) + + 4d / 5 * sin(39 * t + 102d / 47) + 7d / 43 * sin(40 * t + 67d / 18) + + 3d / 13 * sin(41 * t + 39d / 38) + 15d / 32 * sin(42 * t + 75d / 16) + + 34d / 31 * sin(43 * t + 53d / 29) + 31d / 32 * sin(44 * t + 52d / 25) + + 11d / 29 * sin(45 * t + 23d / 6) + 19d / 11 * sin(46 * t + 134d / 29) + + 55d / 27 * sin(47 * t + 30d / 17) + 8d / 25 * sin(48 * t + 185d / 44) + + 12d / 31 * sin(49 * t + 61d / 41) - 803d / 16) * theta(107 * pi - t) + * theta(t - 103 * pi) + + (-183d / 137 * sin(41d / 31 - 53 * t) - 3d / 2 * sin(45d / 32 - 51 * t) + - 21d / 37 * sin(17d / 24 - 50 * t) - 109d / 73 * sin(21d / 16 - 49 * t) + - 28d / 23 * sin(26d / 19 - 46 * t) - 499d / 95 * sin(49d / 38 - 44 * t) + - 79d / 37 * sin(37d / 28 - 41 * t) - 28d / 25 * sin(35d / 33 - 35 * t) + - 39d / 10 * sin(23d / 17 - 34 * t) - 17d / 6 * sin(27d / 22 - 33 * t) + - 40d / 29 * sin(4d / 3 - 32 * t) - 159d / 65 * sin(23d / 16 - 30 * t) + - 130d / 31 * sin(43d / 34 - 29 * t) - 182d / 27 * sin(37d / 26 - 28 * t) + - 19d / 13 * sin(35d / 23 - 26 * t) - 67d / 39 * sin(58d / 49 - 25 * t) + - 131d / 37 * sin(81d / 59 - 24 * t) - 29d / 18 * sin(53d / 40 - 23 * t) + - 77d / 51 * sin(14d / 11 - 22 * t) - 457d / 61 * sin(68d / 47 - 21 * t) + - 293d / 27 * sin(53d / 36 - 15 * t) - 80d / 39 * sin(23d / 15 - 14 * t) + - 65d / 17 * sin(31d / 21 - 11 * t) - 21d / 17 * sin(26d / 17 - 10 * t) + - 41d / 13 * sin(57d / 37 - 8 * t) + 17496d / 85 * sin(t + 41d / 26) + + 26d / 17 * sin(2 * t + 418d / 93) + 449d / 24 * sin(3 * t + 27d / 17) + + 221d / 63 * sin(4 * t + 51d / 11) + 283d / 43 * sin(5 * t + 69d / 43) + + 16d / 15 * sin(6 * t + 64d / 37) + 29d / 11 * sin(7 * t + 59d / 36) + + 201d / 44 * sin(9 * t + 59d / 37) + 97d / 83 * sin(12 * t + 35d / 22) + + 77d / 10 * sin(13 * t + 173d / 104) + 23d / 22 * sin(16 * t + 61d / 32) + + 349d / 32 * sin(17 * t + 61d / 36) + 60d / 17 * sin(18 * t + 79d / 46) + + 11d / 23 * sin(19 * t + 61d / 29) + 373d / 86 * sin(20 * t + 48d / 29) + + 99d / 16 * sin(27 * t + 67d / 40) + 217d / 38 * sin(31 * t + 46d / 27) + + 25d / 29 * sin(36 * t + 189d / 41) + 423d / 106 * sin(37 * t + 49d / 27) + + 11d / 12 * sin(38 * t + 59d / 29) + 184d / 105 * sin(39 * t + 65d / 34) + + 93d / 50 * sin(40 * t + 93d / 49) + 8d / 25 * sin(42 * t + 45d / 22) + + 13d / 11 * sin(43 * t + 46d / 29) + 19d / 28 * sin(45 * t + 27d / 28) + + 14d / 23 * sin(47 * t + 37d / 31) + 9d / 31 * sin(48 * t + 25d / 16) + + 52d / 31 * sin(52 * t + 56d / 31) - 20749d / 273) * theta(103 * pi - t) + * theta(t - 99 * pi) + + (-2d / 27 * sin(47d / 30 - 3 * t) + 321d / 16 * sin(t + 63d / 40) + + 219d / 31 * sin(2 * t + 41d / 26) + 69d / 47 * sin(4 * t + 30d / 19) + + 36d / 23 * sin(5 * t + 27d / 17) + 888d / 11) * theta(99 * pi - t) + * theta(t - 95 * pi) + + (-93d / 25 * sin(25d / 16 - 8 * t) - 85d / 33 * sin(25d / 16 - 7 * t) + - 217d / 38 * sin(47d / 30 - 6 * t) - 989d / 49 * sin(25d / 16 - 5 * t) + + 6925d / 39 * sin(t + 11d / 7) + 3173d / 138 * sin(2 * t + 52d / 33) + + 289d / 9 * sin(3 * t + 146d / 31) + 187d / 23 * sin(4 * t + 41d / 26) + + 16d / 7 * sin(9 * t + 164d / 35) + 19d / 22 * sin(10 * t + 429d / 92) + + 4d / 27 * sin(11 * t + 47d / 32) + 23d / 40 * sin(12 * t + 29d / 19) - 1249d / 41) + * theta(95 * pi - t) * theta(t - 91 * pi) + + (-14d / 51 * sin(65d / 43 - 4 * t) + 2519d / 13 * sin(t + 74d / 47) + + 14d / 11 * sin(2 * t + 151d / 33) + 601d / 28 * sin(3 * t + 30d / 19) - 5627d / 23) + * theta(91 * pi - t) * theta(t - 87 * pi) + + (-268d / 37 * sin(91d / 58 - t) + 1d / 47 * sin(2 * t + 31d / 22) + + 68d / 41 * sin(3 * t + 113d / 24) + 185d / 19 * sin(4 * t + 52d / 33) + + 133d / 53 * sin(5 * t + 65d / 41) + 3d / 35 * sin(6 * t + 122d / 65) - 14306d / 33) + * theta(87 * pi - t) * theta(t - 83 * pi) + + (-64d / 15 * sin(66d / 43 - 12 * t) - 244d / 63 * sin(38d / 25 - 10 * t) + - 6d / 5 * sin(17d / 12 - 9 * t) - 632d / 39 * sin(20d / 13 - 8 * t) + - 986d / 33 * sin(20d / 13 - 7 * t) - 123d / 23 * sin(31d / 20 - 6 * t) + - 923d / 32 * sin(36d / 23 - 4 * t) + 1270d / 19 * sin(t + 74d / 47) + + 70d / 19 * sin(2 * t + 53d / 32) + 1359d / 43 * sin(3 * t + 41d / 26) + + 854d / 27 * sin(5 * t + 30d / 19) + 32d / 13 * sin(11 * t + 19d / 12) + 15881d / 49) + * theta(83 * pi - t) * theta(t - 79 * pi) + + (-1009d / 28 * sin(36d / 23 - 6 * t) + 1362d / 19 * sin(t + 11d / 7) + + 113d / 11 * sin(2 * t + 174d / 37) + 2128d / 45 * sin(3 * t + 30d / 19) + + 3805d / 58 * sin(4 * t + 30d / 19) + 1316d / 51 * sin(5 * t + 41d / 26) + + 500d / 33 * sin(7 * t + 85d / 54) + 69d / 8 * sin(8 * t + 179d / 38) + + 337d / 37 * sin(9 * t + 19d / 12) + 59d / 18 * sin(10 * t + 53d / 33) + + 5d / 24 * sin(11 * t + 87d / 46) + 335d / 44 * sin(12 * t + 46d / 29) - 9149d / 24) + * theta(79 * pi - t) * theta(t - 75 * pi) + + (-3d / 32 * sin(37d / 27 - 14 * t) - 31d / 25 * sin(54d / 35 - 11 * t) + - 101d / 51 * sin(39d / 25 - 6 * t) + 4553d / 33 * sin(t + 63d / 40) + + 771d / 43 * sin(2 * t + 65d / 41) + 679d / 45 * sin(3 * t + 49d / 31) + + 79d / 19 * sin(4 * t + 65d / 41) + 468d / 67 * sin(5 * t + 68d / 43) + + 127d / 52 * sin(7 * t + 59d / 37) + 30d / 29 * sin(8 * t + 155d / 33) + + 89d / 49 * sin(9 * t + 35d / 22) + 50d / 99 * sin(10 * t + 19d / 12) + + 35d / 52 * sin(12 * t + 49d / 30) + 80d / 39 * sin(13 * t + 21d / 13) + + 25d / 23 * sin(15 * t + 34d / 21) + 13d / 38 * sin(16 * t + 96d / 61) + + 17d / 31 * sin(17 * t + 17d / 10) + 55d / 39 * sin(18 * t + 61d / 38) + + 17d / 32 * sin(19 * t + 179d / 38) + 16d / 13 * sin(20 * t + 55d / 34) + 10772d / 39) + * theta(75 * pi - t) * theta(t - 71 * pi) + + (-sin(54d / 35 - 16 * t) - 17d / 29 * sin(86d / 57 - 12 * t) + - 43d / 19 * sin(47d / 30 - 2 * t) + 4197d / 25 * sin(t + 11d / 7) + + 904d / 39 * sin(3 * t + 36d / 23) + 6d / 11 * sin(4 * t + 127d / 27) + + 209d / 31 * sin(5 * t + 85d / 54) + 12d / 61 * sin(6 * t + 41d / 34) + + 143d / 46 * sin(7 * t + 14d / 9) + 28d / 29 * sin(8 * t + 30d / 19) + + 63d / 32 * sin(9 * t + 36d / 23) + 17d / 43 * sin(10 * t + 149d / 32) + + 29d / 19 * sin(11 * t + 25d / 16) + 5d / 8 * sin(13 * t + 36d / 23) + + 21d / 17 * sin(14 * t + 61d / 39) + 28d / 57 * sin(15 * t + 39d / 25) + + 19d / 31 * sin(17 * t + 49d / 31) + 25d / 42 * sin(18 * t + 127d / 27) + + 17d / 25 * sin(19 * t + 74d / 47) + 5d / 19 * sin(20 * t + 32d / 21) + + 62d / 55 * sin(21 * t + 47d / 30) - 14987d / 44) * theta(71 * pi - t) + * theta(t - 67 * pi) + + (-82d / 31 * sin(35d / 23 - 23 * t) - 165d / 82 * sin(31d / 20 - 22 * t) + - 109d / 20 * sin(36d / 23 - 12 * t) - 74d / 11 * sin(14d / 9 - 11 * t) + - 12d / 29 * sin(89d / 59 - 10 * t) - 8d / 17 * sin(19d / 13 - 7 * t) + - 211d / 35 * sin(14d / 9 - 6 * t) - 59d / 23 * sin(25d / 16 - 5 * t) + - 148d / 13 * sin(80d / 51 - 4 * t) - 465d / 13 * sin(36d / 23 - 2 * t) + + 2488d / 25 * sin(t + 11d / 7) + 95d / 26 * sin(3 * t + 49d / 31) + + 12d / 29 * sin(8 * t + 69d / 44) + 197d / 58 * sin(9 * t + 49d / 31) + + 26d / 33 * sin(13 * t + 79d / 17) + 67d / 9 * sin(14 * t + 62d / 39) + + 400d / 37 * sin(15 * t + 35d / 22) + 355d / 43 * sin(16 * t + 59d / 37) + + 19d / 20 * sin(17 * t + 70d / 43) + 5d / 16 * sin(18 * t + 55d / 32) + + 31d / 18 * sin(19 * t + 34d / 21) + 26d / 19 * sin(20 * t + 76d / 47) + + 6d / 19 * sin(21 * t + 119d / 26) + 6093d / 19) * theta(67 * pi - t) + * theta(t - 63 * pi) + + (-73d / 52 * sin(54d / 35 - 23 * t) - 54d / 25 * sin(58d / 37 - 19 * t) + - 73d / 20 * sin(14d / 9 - 17 * t) - 5d / 33 * sin(43d / 28 - 14 * t) + - 52d / 21 * sin(25d / 16 - 12 * t) + 4675d / 43 * sin(t + 96d / 61) + + 353d / 28 * sin(2 * t + 30d / 19) + 3799d / 200 * sin(3 * t + 41d / 26) + + 28d / 5 * sin(4 * t + 41d / 26) + 200d / 31 * sin(5 * t + 11d / 7) + + 41d / 28 * sin(6 * t + 41d / 26) + 21d / 29 * sin(7 * t + 31d / 20) + + 95d / 46 * sin(8 * t + 99d / 62) + 49d / 99 * sin(9 * t + 74d / 47) + + 17d / 22 * sin(10 * t + 211d / 45) + 77d / 34 * sin(11 * t + 27d / 17) + + 40d / 17 * sin(13 * t + 155d / 97) + 20d / 27 * sin(15 * t + 43d / 26) + + 47d / 16 * sin(16 * t + 59d / 37) + 149d / 37 * sin(18 * t + 43d / 27) + + 30d / 17 * sin(20 * t + 8d / 5) + 3d / 31 * sin(21 * t + 92d / 41) + + 16d / 23 * sin(22 * t + 45d / 29) + 28d / 39 * sin(24 * t + 17d / 11) - 9671d / 24) + * theta(63 * pi - t) * theta(t - 59 * pi) + + (-278d / 19 * sin(47d / 30 - 3 * t) - 181d / 29 * sin(58d / 37 - 2 * t) + - 2421d / 26 * sin(69d / 44 - t) + 9898d / 27) * theta(59 * pi - t) + * theta(t - 55 * pi) + + (7671d / 67 * sin(t + 107d / 68) + 355d / 36 * sin(2 * t + 11d / 7) + + 377d / 24 * sin(3 * t + 49d / 31) - 28766d / 63) * theta(55 * pi - t) + * theta(t - 51 * pi) + + (-1345d / 28 * sin(80d / 51 - 2 * t) - 359d / 25 * sin(113d / 72 - t) + + 259d / 15 * sin(3 * t + 11d / 7) + 5273d / 19) * theta(51 * pi - t) + * theta(t - 47 * pi) + + (-10399d / 100 * sin(91d / 58 - t) + 1514d / 33 * sin(2 * t + 85d / 54) - 24712d / 67) + * theta(47 * pi - t) * theta(t - 43 * pi) + + (-14d / 37 * sin(13d / 11 - 10 * t) + 300d / 41 * sin(t + 77d / 36) + + 102d / 23 * sin(2 * t + 73d / 110) + 259d / 30 * sin(3 * t + 11d / 4) + + 323d / 63 * sin(4 * t + 87d / 28) + 150d / 41 * sin(5 * t + 18d / 25) + + 26d / 37 * sin(6 * t + 69d / 19) + 16d / 35 * sin(7 * t + 19d / 25) + + 18d / 25 * sin(8 * t + 17d / 4) + 11d / 19 * sin(9 * t + 79d / 28) + + 5d / 19 * sin(11 * t + 19d / 10) + 13d / 31 * sin(12 * t + 95d / 21) + 8431d / 36) + * theta(43 * pi - t) * theta(t - 39 * pi) + + (-3d / 13 * sin(5d / 4 - 12 * t) - 21d / 29 * sin(59d / 38 - 6 * t) + - 70d / 11 * sin(26d / 19 - t) + 76d / 11 * sin(2 * t + 19d / 14) + + 162d / 29 * sin(3 * t + 203d / 45) + 439d / 73 * sin(4 * t + 124d / 27) + + 14d / 33 * sin(5 * t + 38d / 29) + 34d / 33 * sin(7 * t + 24d / 11) + + 39d / 46 * sin(8 * t + 94d / 21) + 11d / 40 * sin(9 * t + 13d / 15) + + 9d / 19 * sin(10 * t + 229d / 51) + 4d / 35 * sin(11 * t + 22d / 7) - 2147d / 6) + * theta(39 * pi - t) * theta(t - 35 * pi) + + (769d / 16 * sin(t + 20d / 17) + 49d / 31 * sin(2 * t + 84d / 23) + + 129d / 41 * sin(3 * t + 13d / 37) + 11433d / 47) * theta(35 * pi - t) + * theta(t - 31 * pi) + + (-14d / 17 * sin(22d / 23 - 4 * t) - 21d / 25 * sin(37d / 56 - 2 * t) + + 1493d / 31 * sin(t + 48d / 37) + 49d / 27 * sin(3 * t + 5d / 17) - 8077d / 23) + * theta(31 * pi - t) * theta(t - 27 * pi) + + (2099d / 23 * sin(t + 112d / 67) + 25d / 32 * sin(2 * t + 68d / 23) + + 129d / 16 * sin(3 * t + 50d / 27) + 31d / 34 * sin(4 * t + 373d / 112) + + 38d / 17 * sin(5 * t + 21d / 11) + 38d / 53 * sin(6 * t + 32d / 9) + + 25d / 28 * sin(7 * t + 83d / 46) + 10d / 19 * sin(8 * t + 134d / 35) + 10721d / 42) + * theta(27 * pi - t) * theta(t - 23 * pi) + + (3072d / 29 * sin(t + 29d / 19) + 9d / 8 * sin(2 * t + 256d / 73) + + 254d / 25 * sin(3 * t + 43d / 28) + 19d / 21 * sin(4 * t + 87d / 25) + + 95d / 26 * sin(5 * t + 81d / 52) + 21d / 32 * sin(6 * t + 23d / 6) + + 86d / 53 * sin(7 * t + 45d / 29) + 23d / 38 * sin(8 * t + 243d / 67) - 3827d / 11) + * theta(23 * pi - t) * theta(t - 19 * pi) + + (-25d / 17 * sin(36d / 25 - 6 * t) - 99d / 46 * sin(18d / 29 - 4 * t) + + 3796d / 29 * sin(t + 43d / 31) + 5d / 2 * sin(2 * t + 5d / 28) + + 227d / 35 * sin(3 * t + 26d / 21) + 47d / 20 * sin(5 * t + 43d / 39) + + 23d / 17 * sin(7 * t + 11d / 13) + 4572d / 17) * theta(19 * pi - t) + * theta(t - 15 * pi) + + (6353d / 37 * sin(t + 37d / 21) + 430d / 59 * sin(2 * t + 165d / 37) + + 537d / 43 * sin(3 * t + 78d / 31) + 133d / 25 * sin(4 * t + 143d / 32) + + 128d / 25 * sin(5 * t + 78d / 23) + 218d / 61 * sin(6 * t + 174d / 37) + + 51d / 22 * sin(7 * t + 34d / 9) - 12821d / 38) * theta(15 * pi - t) + * theta(t - 11 * pi) + + (-36d / 23 * sin(13d / 18 - 12 * t) - 77d / 36 * sin(17d / 30 - 10 * t) + - 82d / 31 * sin(17d / 24 - 8 * t) - 255d / 62 * sin(26d / 37 - 6 * t) + - 192d / 37 * sin(9d / 16 - 4 * t) - 199d / 40 * sin(21d / 47 - 2 * t) + + 5791d / 22 * sin(t + 63d / 41) + 1140d / 47 * sin(3 * t + 34d / 23) + + 92d / 13 * sin(5 * t + 29d / 21) + 63d / 22 * sin(7 * t + 19d / 17) + + 31d / 28 * sin(9 * t + 21d / 23) + 13d / 19 * sin(11 * t + 54d / 35) - 3874d / 61) + * theta(11 * pi - t) * theta(t - 7 * pi) + + (-186d / 29 * sin(3d / 2 - 6 * t) - 452d / 43 * sin(81d / 65 - 4 * t) + - 487d / 39 * sin(31d / 34 - 2 * t) + 3854d / 13 * sin(t + 102d / 73) + + 313d / 17 * sin(3 * t + 31d / 30) + 56d / 23 * sin(5 * t + 4d / 13) + + 46d / 33 * sin(7 * t + 33d / 13) + 110d / 27 * sin(8 * t + 57d / 13) + + 67d / 38 * sin(9 * t + 71d / 33) + 138d / 59 * sin(10 * t + 101d / 26) + + 11d / 8 * sin(11 * t + 46d / 25) + 20d / 23 * sin(12 * t + 103d / 29) - 605d / 13) + * theta(7 * pi - t) * theta(t - 3 * pi) + + (-320d / 43 * sin(21d / 34 - 6 * t) - 205d / 29 * sin(299d / 298 - 4 * t) + + 47179d / 69 * sin(t + 6d / 13) + 307d / 32 * sin(2 * t + 46d / 51) + + 1152d / 19 * sin(3 * t + 60d / 37) + 107d / 8 * sin(5 * t + 143d / 37) + + 2626d / 175 * sin(7 * t + 45d / 26) + 131d / 22 * sin(8 * t + 143d / 40) + + 296d / 47 * sin(9 * t + 500d / 499) + 16d / 7 * sin(10 * t + 93d / 25) - 4117d / 31) + * theta(3 * pi - t) * theta(t + pi)) * theta(Math.Sqrt(Math.Sign(sin(t / 2)))); + Func yt = + t => + ((-52d / 5 * sin(54d / 37 - 34 * t) - 179d / 56 * sin(46d / 33 - 31 * t) + - 513d / 28 * sin(59d / 39 - 25 * t) - 316d / 105 * sin(43d / 28 - 22 * t) + - 259d / 16 * sin(102d / 65 - 17 * t) - 26239d / 64 * sin(36d / 23 - 3 * t) + - 18953d / 51 * sin(58d / 37 - t) + 11283d / 29 * sin(2 * t + 30d / 19) + + 29123d / 35 * sin(4 * t + 49d / 31) + 6877d / 14 * sin(5 * t + 65d / 41) + + 117d / 50 * sin(6 * t + 103d / 51) + 587d / 13 * sin(7 * t + 164d / 35) + + 102d / 13 * sin(8 * t + 236d / 51) + 975d / 14 * sin(9 * t + 101d / 63) + + 122d / 41 * sin(10 * t + 79d / 44) + 1900d / 139 * sin(11 * t + 117d / 25) + + 597d / 22 * sin(12 * t + 31d / 19) + 1219d / 39 * sin(13 * t + 8d / 5) + + 552d / 13 * sin(14 * t + 35d / 22) + 621d / 26 * sin(15 * t + 31d / 19) + + 705d / 28 * sin(16 * t + 212d / 45) + 579d / 11 * sin(18 * t + 34d / 21) + + 19d / 27 * sin(19 * t + 190d / 43) + 15d / 29 * sin(20 * t + 89d / 20) + + 497d / 23 * sin(21 * t + 128d / 77) + 205d / 51 * sin(23 * t + 57d / 34) + + 317d / 35 * sin(24 * t + 68d / 39) + 17d / 4 * sin(26 * t + 202d / 43) + + 927d / 34 * sin(27 * t + 33d / 20) + 31d / 23 * sin(28 * t + 154d / 123) + + 37d / 6 * sin(29 * t + 28d / 17) + 159d / 19 * sin(30 * t + 22d / 13) + + 87d / 23 * sin(32 * t + 82d / 49) + 57d / 28 * sin(33 * t + 103d / 52) + + 77d / 29 * sin(35 * t + 116d / 25) + 489d / 41 * sin(36 * t + 49d / 29) + + 61d / 30 * sin(37 * t + 62d / 39) + 313d / 47 * sin(38 * t + 69d / 40) + + 36d / 11 * sin(39 * t + 41d / 23) + 90d / 91 * sin(40 * t + 7d / 4) + + 47d / 19 * sin(41 * t + 101d / 63) - 8810d / 27) * theta(119 * pi - t) + * theta(t - 115 * pi) + + (-91d / 46 * sin(11d / 8 - 62 * t) - 59d / 28 * sin(39d / 34 - 61 * t) + - 11d / 2 * sin(89d / 67 - 60 * t) - 15d / 8 * sin(32d / 35 - 59 * t) + - 53d / 22 * sin(48d / 35 - 58 * t) - 175d / 176 * sin(13d / 12 - 54 * t) + - 71d / 15 * sin(30d / 23 - 53 * t) - 166d / 27 * sin(25d / 18 - 52 * t) + - 48d / 29 * sin(20d / 31 - 51 * t) - 547d / 114 * sin(43d / 31 - 50 * t) + - 271d / 41 * sin(51d / 37 - 46 * t) - 141d / 20 * sin(42d / 31 - 45 * t) + - 129d / 13 * sin(37d / 26 - 44 * t) - 164d / 23 * sin(37d / 26 - 41 * t) + - 451d / 52 * sin(31d / 21 - 40 * t) - 27d / 22 * sin(63d / 44 - 38 * t) + - 83d / 7 * sin(35d / 24 - 36 * t) - 874d / 93 * sin(7d / 5 - 35 * t) + - 117d / 22 * sin(22d / 17 - 34 * t) - 911d / 69 * sin(3d / 2 - 32 * t) + - 109d / 37 * sin(27d / 19 - 31 * t) - 31d / 13 * sin(16d / 19 - 27 * t) + - 383d / 10 * sin(64d / 43 - 26 * t) - 455d / 19 * sin(31d / 21 - 24 * t) + - 764d / 17 * sin(58d / 39 - 18 * t) - 5417d / 48 * sin(74d / 49 - 17 * t) + - 1557d / 35 * sin(80d / 53 - 16 * t) - 3366d / 25 * sin(71d / 46 - 12 * t) + - 2267d / 100 * sin(91d / 61 - 11 * t) - 1523d / 27 * sin(239d / 159 - 10 * t) + - 7929d / 44 * sin(36d / 23 - 7 * t) + 32716d / 55 * sin(t + 146d / 31) + + 26497d / 40 * sin(2 * t + 30d / 19) + 28971d / 35 * sin(3 * t + 19d / 12) + + 12399d / 44 * sin(4 * t + 117d / 73) + 3783d / 29 * sin(5 * t + 145d / 31) + + 21737d / 40 * sin(6 * t + 46d / 29) + 132d / 7 * sin(8 * t + 23d / 14) + + 887d / 32 * sin(9 * t + 25d / 16) + 963d / 14 * sin(13 * t + 67d / 41) + + 972d / 11 * sin(14 * t + 47d / 29) + 5575d / 77 * sin(15 * t + 53d / 33) + + 382d / 25 * sin(19 * t + 47d / 32) + 855d / 44 * sin(20 * t + 28d / 17) + + 701d / 42 * sin(21 * t + 33d / 20) + 109d / 23 * sin(22 * t + 14d / 3) + + 197d / 38 * sin(23 * t + 43d / 29) + 118d / 37 * sin(25 * t + 37d / 38) + + 272d / 31 * sin(28 * t + 44d / 27) + 5d / 23 * sin(29 * t + 25d / 26) + + 195d / 28 * sin(30 * t + 47d / 28) + 185d / 24 * sin(33 * t + 57d / 37) + + 103d / 13 * sin(37 * t + 46d / 29) + 43d / 9 * sin(39 * t + 41d / 26) + + 257d / 32 * sin(42 * t + 51d / 32) + 33d / 13 * sin(43 * t + 41d / 30) + + 67d / 15 * sin(47 * t + 31d / 19) + 39d / 14 * sin(48 * t + 44d / 25) + + 88d / 31 * sin(49 * t + 30d / 19) + 113d / 68 * sin(55 * t + 103d / 69) + + 1d / 54 * sin(56 * t + 197d / 58) + 79d / 24 * sin(57 * t + 167d / 100) + 287d / 29) + * theta(115 * pi - t) * theta(t - 111 * pi) + + (-8d / 25 * sin(17d / 31 - 90 * t) - 144d / 37 * sin(29d / 19 - 89 * t) + - 102d / 47 * sin(37d / 24 - 88 * t) - 74d / 43 * sin(24d / 17 - 87 * t) + - 95d / 17 * sin(25d / 16 - 49 * t) - 137d / 27 * sin(16d / 11 - 27 * t) + + 21445d / 52 * sin(t + 69d / 44) + 24223d / 27 * sin(2 * t + 113d / 24) + + 23911d / 28 * sin(3 * t + 146d / 31) + 2675d / 8 * sin(4 * t + 64d / 41) + + 5772d / 19 * sin(5 * t + 36d / 23) + 3258d / 17 * sin(6 * t + 11d / 7) + + 5245d / 37 * sin(7 * t + 174d / 37) + 355d / 16 * sin(8 * t + 117d / 73) + + 225d / 8 * sin(9 * t + 88d / 19) + 3255d / 26 * sin(10 * t + 11d / 7) + + 1137d / 19 * sin(11 * t + 39d / 25) + 1310d / 21 * sin(12 * t + 8d / 5) + + 943d / 21 * sin(13 * t + 29d / 18) + 9679d / 81 * sin(14 * t + 80d / 51) + + 113d / 20 * sin(15 * t + 47d / 28) + 1342d / 47 * sin(16 * t + 86d / 53) + + 3679d / 84 * sin(17 * t + 55d / 36) + 353d / 20 * sin(18 * t + 50d / 11) + + 939d / 50 * sin(19 * t + 201d / 43) + 4789d / 82 * sin(20 * t + 48d / 31) + + 2649d / 85 * sin(21 * t + 303d / 65) + 1753d / 59 * sin(22 * t + 36d / 23) + + 268d / 29 * sin(23 * t + 58d / 39) + 55d / 16 * sin(24 * t + 82d / 43) + + 228d / 37 * sin(25 * t + 353d / 235) + 8d / 25 * sin(26 * t + 49d / 31) + + 422d / 25 * sin(28 * t + 93d / 20) + 211d / 21 * sin(29 * t + 38d / 27) + + 60d / 41 * sin(30 * t + 131d / 29) + 162d / 11 * sin(31 * t + 65d / 14) + + 2489d / 63 * sin(32 * t + 35d / 23) + 688d / 27 * sin(33 * t + 129d / 28) + + 1447d / 62 * sin(34 * t + 55d / 36) + 233d / 26 * sin(35 * t + 85d / 57) + + 259d / 26 * sin(36 * t + 95d / 21) + 149d / 10 * sin(37 * t + 25d / 17) + + 235d / 57 * sin(38 * t + 50d / 11) + 185d / 38 * sin(39 * t + 183d / 41) + + 125d / 13 * sin(40 * t + 68d / 45) + 223d / 31 * sin(41 * t + 39d / 25) + + 61d / 12 * sin(42 * t + 355d / 79) + 1262d / 99 * sin(43 * t + 160d / 107) + + 309d / 40 * sin(44 * t + 41d / 26) + 307d / 38 * sin(45 * t + 221d / 48) + + 49d / 5 * sin(46 * t + 137d / 91) + 193d / 31 * sin(47 * t + 190d / 41) + + 77d / 54 * sin(48 * t + 95d / 63) + 20d / 33 * sin(50 * t + 148d / 51) + + 256d / 47 * sin(51 * t + 53d / 39) + 133d / 37 * sin(52 * t + 92d / 21) + + 19d / 7 * sin(53 * t + 121d / 81) + 39d / 4 * sin(54 * t + 71d / 47) + + 341d / 85 * sin(55 * t + 113d / 25) + 71d / 31 * sin(56 * t + 115d / 67) + + 31d / 10 * sin(57 * t + 36d / 25) + 11d / 5 * sin(58 * t + 30d / 7) + + 62d / 23 * sin(59 * t + 33d / 23) + 40d / 33 * sin(60 * t + 67d / 36) + + 13d / 40 * sin(61 * t + 32d / 19) + 47d / 6 * sin(62 * t + 80d / 53) + + 48d / 11 * sin(63 * t + 73d / 16) + 301d / 47 * sin(64 * t + 56d / 37) + + 361d / 120 * sin(65 * t + 191d / 41) + 59d / 31 * sin(66 * t + 83d / 52) + + 132d / 25 * sin(67 * t + 117d / 25) + 37d / 13 * sin(68 * t + 4d / 3) + + 167d / 35 * sin(69 * t + 183d / 40) + 67d / 41 * sin(70 * t + 4d / 3) + + 103d / 42 * sin(71 * t + 49d / 34) + 199d / 71 * sin(72 * t + 101d / 23) + + 200d / 31 * sin(73 * t + 77d / 53) + 81d / 58 * sin(74 * t + 169d / 38) + + 17d / 21 * sin(75 * t + 83d / 20) + 27d / 38 * sin(76 * t + 79d / 59) + + 59d / 29 * sin(77 * t + 47d / 33) + 175d / 44 * sin(78 * t + 114d / 25) + + 54d / 19 * sin(79 * t + 63d / 43) + 19d / 27 * sin(80 * t + 61d / 27) + + 149d / 26 * sin(81 * t + 16d / 11) + 68d / 29 * sin(82 * t + 102d / 25) + + 313d / 68 * sin(83 * t + 54d / 35) + 147d / 23 * sin(84 * t + 47d / 32) + + 73d / 12 * sin(85 * t + 107d / 24) + 191d / 51 * sin(86 * t + 38d / 27) + + 37d / 28 * sin(91 * t + 173d / 37) + 70d / 43 * sin(92 * t + 35d / 23) + + 44d / 35 * sin(93 * t + 53d / 13) + 164d / 35 * sin(94 * t + 107d / 67) + + 141d / 34 * sin(95 * t + 37d / 25) + 19d / 12 * sin(96 * t + 83d / 21) + + 22d / 23 * sin(97 * t + 47d / 31) + 152d / 39 * sin(98 * t + 60d / 43) + + 123d / 28 * sin(99 * t + 132d / 29) - 15137d / 22) * theta(111 * pi - t) + * theta(t - 107 * pi) + + (-43d / 40 * sin(102d / 65 - 45 * t) - 74d / 41 * sin(25d / 27 - 35 * t) + - 47d / 19 * sin(15d / 16 - 34 * t) - 67d / 32 * sin(21d / 25 - 33 * t) + - 161d / 39 * sin(27d / 20 - 32 * t) - 12d / 23 * sin(17d / 12 - 29 * t) + - 29d / 57 * sin(45d / 37 - 27 * t) - 19d / 16 * sin(117d / 88 - 25 * t) + - 11d / 36 * sin(15d / 22 - 24 * t) - 11d / 30 * sin(33d / 82 - 23 * t) + - 43d / 57 * sin(14d / 17 - 22 * t) - 87d / 65 * sin(49d / 41 - 21 * t) + - 50d / 21 * sin(64d / 47 - 20 * t) - 17d / 59 * sin(56d / 55 - 6 * t) + - 56d / 61 * sin(77d / 58 - 5 * t) + 127d / 28 * sin(t + 149d / 32) + + 15d / 2 * sin(2 * t + 21d / 13) + 35d / 29 * sin(3 * t + 49d / 29) + + 1d / 9 * sin(4 * t + 51d / 43) + 37d / 38 * sin(7 * t + 19d / 12) + + 18d / 31 * sin(8 * t + 57d / 13) + 38d / 31 * sin(9 * t + 55d / 12) + + 7d / 10 * sin(10 * t + 74d / 45) + 9d / 34 * sin(11 * t + 69d / 34) + + 39d / 19 * sin(12 * t + 41d / 26) + 89d / 26 * sin(13 * t + 67d / 42) + + 63d / 26 * sin(14 * t + 107d / 23) + 119d / 16 * sin(15 * t + 23d / 5) + + 311d / 18 * sin(16 * t + 56d / 33) + 1929d / 386 * sin(17 * t + 99d / 50) + + 79d / 41 * sin(18 * t + 64d / 29) + 34d / 41 * sin(19 * t + 67d / 35) + + 16d / 41 * sin(26 * t + 49d / 33) + 1d / 34 * sin(28 * t + 5d / 14) + + 36d / 43 * sin(30 * t + 193d / 41) + 173d / 42 * sin(31 * t + 30d / 19) + + 15d / 19 * sin(36 * t + 31d / 28) + 2d / 19 * sin(37 * t + 87d / 43) + + 22d / 39 * sin(38 * t + 33d / 14) + 13d / 23 * sin(39 * t + 19d / 6) + + 21d / 19 * sin(40 * t + 1641d / 820) + 29d / 28 * sin(41 * t + 172d / 39) + + 39d / 34 * sin(42 * t + 58d / 31) + 16d / 29 * sin(43 * t + 50d / 23) + + 24d / 25 * sin(44 * t + 41d / 23) + 17d / 23 * sin(46 * t + 289d / 62) + + 37d / 25 * sin(47 * t + 30d / 17) + 17d / 29 * sin(48 * t + 239d / 52) + + 29d / 73 * sin(49 * t + 41d / 26) - 8044d / 23) * theta(107 * pi - t) + * theta(t - 103 * pi) + + (-33d / 29 * sin(7d / 6 - 53 * t) - 67d / 44 * sin(62d / 45 - 51 * t) + - 32d / 65 * sin(1d / 14 - 50 * t) - 151d / 49 * sin(54d / 41 - 49 * t) + - 63d / 38 * sin(29d / 23 - 47 * t) - 59d / 103 * sin(14d / 23 - 46 * t) + - 11d / 30 * sin(8d / 23 - 45 * t) - 151d / 32 * sin(99d / 79 - 44 * t) + - 53d / 26 * sin(45d / 38 - 43 * t) - 67d / 27 * sin(38d / 29 - 36 * t) + - 113d / 32 * sin(39d / 29 - 35 * t) - 182d / 51 * sin(81d / 58 - 33 * t) + - 391d / 65 * sin(33d / 23 - 29 * t) - 48d / 13 * sin(31d / 20 - 25 * t) + - 59d / 32 * sin(24d / 17 - 19 * t) - 90d / 91 * sin(41d / 33 - 18 * t) + - 863d / 54 * sin(35d / 24 - 17 * t) - 384d / 43 * sin(73d / 51 - 16 * t) + - 149d / 26 * sin(19d / 13 - 13 * t) - 90d / 19 * sin(65d / 43 - 12 * t) + + 10d / 29 * sin(t + 47d / 32) + 15d / 17 * sin(2 * t + 77d / 45) + + 300d / 37 * sin(3 * t + 62d / 39) + 4d / 13 * sin(4 * t + 8d / 5) + + 7d / 29 * sin(5 * t + 109d / 25) + 46d / 27 * sin(6 * t + 49d / 30) + + 39d / 23 * sin(7 * t + 30d / 19) + 64d / 47 * sin(8 * t + 72d / 43) + + 19d / 20 * sin(9 * t + 63d / 37) + 14d / 5 * sin(10 * t + 41d / 25) + + 37d / 14 * sin(11 * t + 27d / 16) + 207d / 28 * sin(14 * t + 248d / 149) + + 288d / 25 * sin(15 * t + 58d / 35) + 123d / 52 * sin(20 * t + 85d / 46) + + 194d / 13 * sin(21 * t + 17d / 10) + 64d / 13 * sin(22 * t + 103d / 22) + + 335d / 27 * sin(23 * t + 31d / 18) + 103d / 18 * sin(24 * t + 13d / 7) + + 83d / 20 * sin(26 * t + 117d / 67) + 243d / 52 * sin(27 * t + 36d / 19) + + 1907d / 477 * sin(28 * t + 125d / 73) + 73d / 30 * sin(30 * t + 182d / 109) + + 182d / 109 * sin(31 * t + 43d / 23) + 53d / 23 * sin(32 * t + 147d / 92) + + 21d / 43 * sin(34 * t + 145d / 144) + 20d / 17 * sin(37 * t + 29d / 14) + + 110d / 27 * sin(38 * t + 79d / 44) + 22d / 27 * sin(39 * t + 74d / 17) + + 99d / 37 * sin(40 * t + 52d / 29) + 36d / 25 * sin(41 * t + 255d / 128) + + 11d / 15 * sin(42 * t + 124d / 69) + 31d / 17 * sin(48 * t + 67d / 41) + + 15d / 14 * sin(52 * t + 73d / 47) - 10105d / 36) * theta(103 * pi - t) + * theta(t - 99 * pi) + + (3683d / 48 * sin(t + 74d / 47) + 41d / 26 * sin(2 * t + 103d / 22) + + 149d / 17 * sin(3 * t + 85d / 54) + 6d / 13 * sin(4 * t + 51d / 11) + + 112d / 43 * sin(5 * t + 11d / 7) + 17294d / 53) * theta(99 * pi - t) + * theta(t - 95 * pi) + + (-19d / 40 * sin(29d / 20 - 11 * t) - 289d / 38 * sin(36d / 23 - 9 * t) + + 802d / 33 * sin(t + 179d / 38) + 3002d / 35 * sin(2 * t + 85d / 54) + + 159d / 41 * sin(3 * t + 61d / 39) + 1321d / 44 * sin(4 * t + 52d / 33) + + 131d / 22 * sin(5 * t + 113d / 24) + 52d / 35 * sin(6 * t + 69d / 44) + + 321d / 37 * sin(7 * t + 36d / 23) + 789d / 158 * sin(8 * t + 36d / 23) + + 56d / 113 * sin(10 * t + 50d / 33) + 237d / 118 * sin(12 * t + 39d / 25) - 703d / 42) + * theta(95 * pi - t) * theta(t - 91 * pi) + + (-121d / 68 * sin(25d / 16 - 4 * t) - 55d / 27 * sin(81d / 52 - 3 * t) + - 613d / 68 * sin(25d / 16 - 2 * t) - 505d / 19 * sin(47d / 30 - t) + 16067d / 23) + * theta(91 * pi - t) * theta(t - 87 * pi) + + (-83d / 19 * sin(47d / 30 - 5 * t) + 56d / 25 * sin(t + 193d / 41) + + 35d / 29 * sin(2 * t + 146d / 31) + 127d / 25 * sin(3 * t + 74d / 47) + + 143d / 35 * sin(4 * t + 63d / 40) + 33d / 50 * sin(6 * t + 107d / 68) + 17679d / 28) + * theta(87 * pi - t) * theta(t - 83 * pi) + + (-35d / 32 * sin(31d / 20 - 11 * t) - 55d / 48 * sin(61d / 39 - 9 * t) + - 30d / 17 * sin(50d / 33 - 6 * t) - 247d / 17 * sin(64d / 41 - 5 * t) + - 471d / 35 * sin(58d / 37 - 3 * t) + 405d / 23 * sin(t + 41d / 26) + + 829d / 69 * sin(2 * t + 11d / 7) + 279d / 17 * sin(4 * t + 30d / 19) + + 185d / 27 * sin(7 * t + 93d / 58) + 92d / 25 * sin(8 * t + 43d / 27) + + 1d / 17 * sin(10 * t + 56d / 31) + 64d / 35 * sin(12 * t + 37d / 23) + 3306d / 17) + * theta(83 * pi - t) * theta(t - 79 * pi) + + (-143d / 24 * sin(36d / 23 - t) + 9d / 22 * sin(2 * t + 47d / 10) + + 31d / 42 * sin(3 * t + 28d / 17) + 1287d / 103 * sin(4 * t + 30d / 19) + + 626d / 51 * sin(5 * t + 41d / 26) + 173d / 31 * sin(6 * t + 113d / 24) + + 138d / 19 * sin(7 * t + 19d / 12) + 15d / 4 * sin(8 * t + 27d / 17) + + 39d / 34 * sin(9 * t + 8d / 5) + 11d / 18 * sin(10 * t + 202d / 43) + + 23d / 25 * sin(11 * t + 71d / 44) + 133d / 34 * sin(12 * t + 27d / 17) + 19064d / 89) + * theta(79 * pi - t) * theta(t - 75 * pi) + + (-69d / 38 * sin(44d / 29 - 19 * t) - 49d / 17 * sin(119d / 79 - 16 * t) + - 65d / 23 * sin(83d / 55 - 11 * t) - 149d / 35 * sin(32d / 21 - 10 * t) + - 311d / 56 * sin(14d / 9 - 8 * t) - 83d / 40 * sin(20d / 13 - 7 * t) + - 35d / 6 * sin(57d / 37 - 6 * t) - 55d / 19 * sin(57d / 37 - 5 * t) + - 93d / 19 * sin(39d / 25 - 4 * t) - 87d / 8 * sin(39d / 25 - 3 * t) + - 517d / 30 * sin(25d / 16 - 2 * t) + 387d / 28 * sin(t + 91d / 58) + + 68d / 33 * sin(9 * t + 27d / 17) + 67d / 34 * sin(12 * t + 76d / 47) + + 13d / 20 * sin(13 * t + 45d / 26) + 2d / 25 * sin(14 * t + 127d / 28) + + 131d / 56 * sin(15 * t + 61d / 38) + 62d / 29 * sin(17 * t + 188d / 113) + + 26d / 37 * sin(18 * t + 77d / 46) + 8d / 33 * sin(20 * t + 43d / 38) + 5327d / 11) + * theta(75 * pi - t) * theta(t - 71 * pi) + + (-21d / 23 * sin(98d / 65 - 16 * t) - 22d / 23 * sin(47d / 30 - 9 * t) + - 13d / 19 * sin(25d / 16 - 7 * t) + 28d / 57 * sin(t + 87d / 56) + + 1265d / 47 * sin(2 * t + 179d / 38) + 223d / 20 * sin(3 * t + 11d / 7) + + 227d / 19 * sin(4 * t + 179d / 38) + 45d / 22 * sin(5 * t + 11d / 7) + + 188d / 53 * sin(6 * t + 127d / 27) + 116d / 35 * sin(8 * t + 179d / 38) + + 19d / 12 * sin(10 * t + 27d / 17) + 8d / 11 * sin(11 * t + 19d / 12) + + 29d / 13 * sin(12 * t + 179d / 38) + 69d / 43 * sin(13 * t + 25d / 16) + + 277d / 278 * sin(14 * t + 107d / 23) + 31d / 27 * sin(15 * t + 127d / 27) + + 63d / 22 * sin(17 * t + 155d / 33) + 13d / 19 * sin(18 * t + 43d / 27) + + 107d / 89 * sin(19 * t + 103d / 22) + 13d / 12 * sin(20 * t + 61d / 13) + + 22d / 17 * sin(21 * t + 39d / 25) + 8121d / 16) * theta(71 * pi - t) + * theta(t - 67 * pi) + + (-89d / 18 * sin(68d / 45 - 23 * t) - 27d / 17 * sin(26d / 17 - 20 * t) + - 109d / 39 * sin(73d / 47 - 18 * t) - 142d / 57 * sin(26d / 17 - 17 * t) + - 146d / 27 * sin(45d / 29 - 14 * t) - 43d / 9 * sin(31d / 20 - 13 * t) + - 164d / 33 * sin(95d / 61 - 12 * t) - 91d / 34 * sin(43d / 28 - 11 * t) + - 30d / 19 * sin(29d / 19 - 9 * t) - 55d / 17 * sin(59d / 38 - 8 * t) + - 183d / 29 * sin(64d / 41 - 6 * t) - 137d / 25 * sin(53d / 34 - 5 * t) + - 124d / 19 * sin(36d / 23 - 4 * t) - 655d / 44 * sin(47d / 30 - 2 * t) + - 472d / 13 * sin(58d / 37 - t) + 43d / 57 * sin(3 * t + 70d / 43) + + 41d / 12 * sin(7 * t + 85d / 54) + 29d / 115 * sin(10 * t + 38d / 23) + + 27d / 7 * sin(15 * t + 19d / 12) + 21d / 20 * sin(16 * t + 36d / 23) + + 26d / 51 * sin(19 * t + 36d / 23) + 2d / 31 * sin(21 * t + 67d / 42) + + 59d / 26 * sin(22 * t + 31d / 19) + 8662d / 25) * theta(67 * pi - t) + * theta(t - 63 * pi) + + (-40d / 27 * sin(86d / 57 - 24 * t) - 5d / 33 * sin(4d / 15 - 23 * t) + - 107d / 27 * sin(57d / 37 - 22 * t) - 163d / 12 * sin(37d / 24 - 18 * t) + - 80d / 43 * sin(73d / 47 - 15 * t) - 245d / 57 * sin(31d / 20 - 13 * t) + - 22d / 29 * sin(43d / 28 - 10 * t) - 99d / 20 * sin(59d / 38 - 8 * t) + - 18d / 11 * sin(59d / 38 - 7 * t) - 35d / 16 * sin(58d / 37 - 6 * t) + - 81d / 161 * sin(41d / 28 - 3 * t) - 271d / 13 * sin(58d / 37 - 2 * t) + + 148d / 7 * sin(t + 85d / 54) + 98d / 25 * sin(4 * t + 146d / 31) + + 67d / 24 * sin(5 * t + 30d / 19) + 8d / 25 * sin(9 * t + 179d / 38) + + 5d / 9 * sin(11 * t + 131d / 28) + 46d / 51 * sin(12 * t + 32d / 21) + + 108d / 31 * sin(14 * t + 69d / 43) + 149d / 29 * sin(16 * t + 45d / 28) + + 59d / 15 * sin(17 * t + 46d / 29) + 134d / 21 * sin(19 * t + 125d / 78) + + 158d / 27 * sin(20 * t + 44d / 27) + 35d / 29 * sin(21 * t + 31d / 19) + 9838d / 25) + * theta(63 * pi - t) * theta(t - 59 * pi) + + (-385d / 16 * sin(47d / 30 - 3 * t) - 21082d / 91 * sin(69d / 44 - t) + + 21d / 13 * sin(2 * t + 8d / 5) - 31214d / 91) * theta(59 * pi - t) + * theta(t - 55 * pi) + + (-860d / 41 * sin(39d / 25 - 3 * t) - 5675d / 26 * sin(69d / 44 - t) + + 265d / 43 * sin(2 * t + 46d / 29) - 33516d / 73) * theta(55 * pi - t) + * theta(t - 51 * pi) + + (-621d / 28 * sin(36d / 23 - 3 * t) - 244d / 31 * sin(91d / 58 - 2 * t) + - 4739d / 19 * sin(102d / 65 - t) - 20129d / 76) * theta(51 * pi - t) + * theta(t - 47 * pi) + + (-504d / 31 * sin(25d / 16 - 2 * t) - 6167d / 31 * sin(80d / 51 - t) - 4810d / 29) + * theta(47 * pi - t) * theta(t - 43 * pi) + + (-1d / 14 * sin(9d / 11 - 12 * t) - 21d / 41 * sin(29d / 25 - 11 * t) + - 7d / 10 * sin(48d / 35 - 9 * t) + 157d / 25 * sin(t + 118d / 41) + + 92d / 41 * sin(2 * t + 110d / 39) + 571d / 84 * sin(3 * t + 101d / 23) + + 146d / 37 * sin(4 * t + 211d / 48) + 71d / 34 * sin(5 * t + 45d / 26) + + 7d / 31 * sin(6 * t + 4d / 3) + 7d / 13 * sin(7 * t + 44d / 25) + + 9d / 11 * sin(8 * t + 37d / 53) + 12d / 29 * sin(10 * t + 31d / 18) + 16173d / 50) + * theta(43 * pi - t) * theta(t - 39 * pi) + + (-25d / 29 * sin(1d / 110 - 10 * t) - 81d / 71 * sin(11d / 29 - 6 * t) + - 101d / 19 * sin(16d / 49 - 4 * t) - 81d / 19 * sin(11d / 43 - 3 * t) + + 167d / 43 * sin(t + 175d / 41) + 181d / 37 * sin(2 * t + 58d / 17) + + 6d / 5 * sin(5 * t + 61d / 14) + 25d / 22 * sin(7 * t + 3) + + 37d / 30 * sin(8 * t + 1d / 25) + 31d / 40 * sin(9 * t + 61d / 22) + + 9d / 35 * sin(11 * t + 103d / 30) + 14d / 19 * sin(12 * t + 1d / 10) + 8453d / 25) + * theta(39 * pi - t) * theta(t - 35 * pi) + + (-151d / 36 * sin(17d / 23 - 3 * t) - 331d / 12 * sin(8d / 31 - t) + + 184d / 47 * sin(2 * t + 160d / 39) + 324) * theta(35 * pi - t) * theta(t - 31 * pi) + + (-50d / 19 * sin(6d / 17 - 3 * t) - 1333d / 36 * sin(1d / 5 - t) + + 23d / 19 * sin(2 * t + 23d / 9) + 29d / 36 * sin(4 * t + 67d / 24) + 13271d / 39) + * theta(31 * pi - t) * theta(t - 27 * pi) + + (-119d / 22 * sin(27d / 19 - 2 * t) + 1265d / 36 * sin(t + 4d / 29) + + 98d / 15 * sin(3 * t + 3d / 5) + 89d / 48 * sin(4 * t + 142d / 31) + + 55d / 19 * sin(5 * t + 32d / 37) + 16d / 17 * sin(6 * t + 89d / 25) + + 21d / 16 * sin(7 * t + 23d / 40) + 8d / 13 * sin(8 * t + 37d / 12) + 10562d / 33) + * theta(27 * pi - t) * theta(t - 23 * pi) + + (-71d / 46 * sin(1d / 11 - 7 * t) - 115d / 64 * sin(5d / 19 - 5 * t) + - 11d / 28 * sin(16d / 25 - 4 * t) - 44d / 17 * sin(33d / 43 - 3 * t) + - 1265d / 29 * sin(2d / 13 - t) + 59d / 16 * sin(2 * t + 211d / 53) + + 12d / 25 * sin(6 * t + 57d / 20) + 26d / 51 * sin(8 * t + 65d / 17) + 11467d / 34) + * theta(23 * pi - t) * theta(t - 19 * pi) + + (-23d / 41 * sin(81d / 58 - 5 * t) + 4367d / 48 * sin(t + 111d / 38) + + 49d / 16 * sin(2 * t + 173d / 39) + 134d / 45 * sin(3 * t + 70d / 29) + + 64d / 37 * sin(4 * t + 41d / 17) + 21d / 46 * sin(6 * t + 29d / 13) + + 10d / 19 * sin(7 * t + 53d / 37) + 10855d / 34) * theta(19 * pi - t) + * theta(t - 15 * pi) + + (-127d / 13 * sin(7d / 18 - 3 * t) + 2713d / 29 * sin(t + 2d / 37) + + 101d / 13 * sin(2 * t + 203d / 45) + 89d / 34 * sin(4 * t + 73d / 17) + + 26d / 15 * sin(5 * t + 26d / 41) + 2d / 9 * sin(6 * t + 177d / 50) + + 35d / 71 * sin(7 * t + 66d / 31) + 10805d / 33) * theta(15 * pi - t) + * theta(t - 11 * pi) + + (-20d / 23 * sin(38d / 29 - 11 * t) - 31d / 18 * sin(46d / 37 - 9 * t) + - 497d / 108 * sin(9d / 16 - 5 * t) - 2375d / 26 * sin(3d / 29 - t) + + 970d / 41 * sin(2 * t + 35d / 32) + 1011d / 92 * sin(3 * t + 7d / 38) + + 131d / 27 * sin(4 * t + 91d / 90) + 24d / 71 * sin(6 * t + 34d / 37) + + 90d / 29 * sin(7 * t + 14d / 23) + 21d / 16 * sin(8 * t + 25d / 12) + + 21d / 38 * sin(10 * t + 89d / 19) + 7d / 27 * sin(12 * t + 68d / 15) - 10719d / 37) + * theta(11 * pi - t) * theta(t - 7 * pi) + + (-11d / 37 * sin(4d / 7 - 6 * t) - 299d / 56 * sin(24d / 19 - 5 * t) + - 499d / 38 * sin(5d / 44 - 3 * t) - 6150d / 37 * sin(12d / 59 - t) + + 1019d / 38 * sin(2 * t + 28d / 37) + 183d / 53 * sin(4 * t + 85d / 71) + + 95d / 43 * sin(7 * t + 169d / 38) + 29d / 27 * sin(8 * t + 111d / 40) + + 21d / 22 * sin(9 * t + 16d / 29) + 194d / 83 * sin(10 * t + 164d / 57) + + 31d / 30 * sin(11 * t + 1d / 53) + 17d / 31 * sin(12 * t + 77d / 29) - 24819d / 82) + * theta(7 * pi - t) * theta(t - 3 * pi) + + (-14d / 13 * sin(4d / 45 - 10 * t) - 229d / 26 * sin(16d / 23 - 7 * t) + - 222d / 17 * sin(28d / 23 - 5 * t) - 851d / 29 * sin(10d / 51 - 3 * t) + - 12070d / 13 * sin(12d / 11 - t) + 270d / 31 * sin(2 * t + 62d / 15) + + 53d / 7 * sin(4 * t + 400d / 87) + 59d / 10 * sin(6 * t + 12d / 13) + + 141d / 31 * sin(8 * t + 30d / 17) + 79d / 40 * sin(9 * t + 121d / 26) + 5838d / 29) + * theta(3 * pi - t) * theta(t + pi)) * theta(Math.Sqrt(Math.Sign(sin(t / 2)))); + + var model = new PlotModel { Title = "Angelina Jolie curve", PlotType = PlotType.Cartesian }; + var fs = new FunctionSeries(xt, yt, 0, Math.PI * 120, 10000); + model.Series.Add(fs); + + // Insert breaks at discontinuities + // TODO: this should be improved... + for (int i = 0; i + 1 < fs.Points.Count; i++) + { + var dx = fs.Points[i + 1].X - fs.Points[i].X; + var dy = fs.Points[i + 1].Y - fs.Points[i].Y; + if ((dx * dx) + (dy * dy) > 100000) + { + fs.Points.Insert(i + 1, new DataPoint(double.NaN, double.NaN)); + i++; + } + } + + return model; + } + + /// + /// Gets the stream for the specified embedded resource. + /// + /// The name of the resource. + /// A stream. + private static Stream GetResourceStream(string name) + { + return typeof(MiscExamples).GetTypeInfo().Assembly.GetManifestResourceStream("ExampleLibrary.Resources." + name); + } + + /// + /// Renders the Mandelbrot set as an image inside the current plot area. + /// + public class MandelbrotSetSeries : XYAxisSeries + { + /// + /// Initializes a new instance of the class. + /// + public MandelbrotSetSeries() + { + this.TrackerFormatString = "X: {0:0.000}\r\nY: {1:0.000}\r\nIterations: {2}"; + } + + /// + /// Gets or sets the color axis. + /// + /// The color axis. + /// The Maximum value of the ColorAxis defines the maximum number of iterations. + public LinearColorAxis ColorAxis { get; protected set; } + + /// + /// Gets or sets the color axis key. + /// + /// The color axis key. + public string ColorAxisKey { get; set; } + + /// + /// Gets the point on the series that is nearest the specified point. + /// + /// The point. + /// Interpolate the series if this flag is set to true. + /// A TrackerHitResult for the current hit. + public override TrackerHitResult GetNearestPoint(ScreenPoint point, bool interpolate) + { + var p = this.InverseTransform(point); + var it = this.Solve(p.X, p.Y, (int)this.ColorAxis.ActualMaximum + 1); + return new TrackerHitResult + { + Series = this, + DataPoint = p, + Position = point, + Item = null, + Index = -1, + Text = StringHelper.Format(this.ActualCulture, this.TrackerFormatString, null, p.X, p.Y, it) + }; + } + + /// + /// Renders the series on the specified render context. + /// + /// The rendering context. + public override void Render(IRenderContext rc) + { + var p0 = this.Transform(this.XAxis.ActualMinimum, this.YAxis.ActualMinimum); + var p1 = this.Transform(this.XAxis.ActualMaximum, this.YAxis.ActualMaximum); + var w = (int)(p1.X - p0.X); + var h = (int)(p0.Y - p1.Y); + int maxIterations = (int)this.ColorAxis.ActualMaximum + 1; + var pixels = new OxyColor[w, h]; + + ParallelFor( + 0, + h, + i => + { + double y = this.YAxis.ActualMaximum - ((double)i / (h - 1) * (this.YAxis.ActualMaximum - this.YAxis.ActualMinimum)); + for (int j = 0; j < w; j++) + { + double x = this.XAxis.ActualMinimum + + ((double)j / (w - 1) + * (this.XAxis.ActualMaximum - this.XAxis.ActualMinimum)); + var iterations = Solve(x, y, maxIterations); + pixels[j, i] = this.ColorAxis.GetColor((double)iterations); + } + }); + + var bitmap = OxyImage.Create(pixels, ImageFormat.Png); + rc.DrawImage(bitmap, p0.X, p1.Y, p1.X - p0.X, p0.Y - p1.Y, 1, true); + } + + /// + /// Calculates the escape time for the specified point. + /// + /// The x0. + /// The y0. + /// The max number of iterations. + /// The number of iterations. + protected virtual int Solve(double x0, double y0, int maxIterations) + { + int iteration = 0; + double x = 0; + double y = 0; + while ((x * x) + (y * y) <= 4 && iteration < maxIterations) + { + double xtemp = (x * x) - (y * y) + x0; + y = (2 * x * y) + y0; + x = xtemp; + iteration++; + } + + return iteration; + } + + /// + /// Ensures that the axes of the series is defined. + /// + protected override void EnsureAxes() + { + base.EnsureAxes(); + this.ColorAxis = this.PlotModel.GetAxisOrDefault(this.ColorAxisKey, (Axis)this.PlotModel.DefaultColorAxis) as LinearColorAxis; + } + + /// + /// Executes a serial for loop. + /// + /// The start index (inclusive). + /// The end index (exclusive). + /// The action that is invoked once per iteration. + private static void SerialFor(int i0, int i1, Action action) + { + for (int i = i0; i < i1; i++) + { + action(i); + } + } + + /// + /// Executes a parallel for loop using ThreadPool. + /// + /// The start index (inclusive). + /// The end index (exclusive). + /// The action that is invoked once per iteration. + private static void ParallelFor(int i0, int i1, Action action) + { + // Environment.ProcessorCount is not available here. Use 4 processors. + int p = 4; + + // Initialize wait handles + var doneEvents = new WaitHandle[p]; + for (int i = 0; i < p; i++) + { + doneEvents[i] = new ManualResetEvent(false); + } + + // Invoke the action of a partition of the range + Action invokePartition = (k, j0, j1) => + { + for (int i = j0; i < j1; i++) + { + action(i); + } + + ((ManualResetEvent)doneEvents[k]).Set(); + }; + + // Start p background threads + int n = (i1 - i0 + p - 1) / p; + for (int i = 0; i < p; i++) + { + int k = i; + int j0 = i0 + (i * n); + var j1 = Math.Min(j0 + n, i1); + Task.Factory.StartNew( + () => invokePartition(k, j0, j1), + CancellationToken.None, + TaskCreationOptions.LongRunning, + TaskScheduler.Default); + } + + // Wait for the threads to finish + foreach (var wh in doneEvents) + { + wh.WaitOne(); + } + } + } + + + private class JuliaSetSeries : MandelbrotSetSeries + { + public double C1 { get; set; } + public double C2 { get; set; } + + protected override int Solve(double x0, double y0, int maxIterations) + { + int iteration = 0; + double x = x0; + double y = y0; + double cr = this.C1; + double ci = this.C2; + while ((x * x) + (y * y) <= 4 && iteration < maxIterations) + { + double xtemp = (x * x) - (y * y) + cr; + y = (2 * x * y) + ci; + x = xtemp; + iteration++; + } + + return iteration; + } + } + + /* [XmlRoot("DataSet")] + [XmlInclude(typeof(Data))] + public class WorldPopulationDataSet + { + [XmlElement("Data")] + public List Items { get; set; } + + public class Data + { + [XmlAttribute("Year")] + public int Year { get; set; } + + [XmlAttribute("Population")] + public double Population { get; set; } + } + }*/ + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Misc/XkcdExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Misc/XkcdExamples.cs new file mode 100644 index 0000000..d0aef5d --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Misc/XkcdExamples.cs @@ -0,0 +1,139 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Plot examples in XKCD style. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + + /// + /// Plot examples in XKCD style. + /// + [Examples("XKCD")] + public static class XkcdExamples + { + /// + /// Xkcd style example #1. + /// + /// A . + [Example("Test #1")] + public static PlotModel Test1() + { + var model = new PlotModel + { + Title = "XKCD style plot", + Subtitle = "Install the 'Humor Sans' font for the best experience", + RenderingDecorator = rc => new XkcdRenderingDecorator(rc) + }; + model.Series.Add(new FunctionSeries(Math.Sin, 0, 10, 50, "sin(x)")); + return model; + } + + + /// + /// Xkcd style example #2. + /// + /// A . + [Example("Test #2")] + public static PlotModel Test2() + { + var model = new PlotModel + { + Title = "Test #2", + RenderingDecorator = rc => new XkcdRenderingDecorator(rc) + }; + + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = 0, Maximum = 8, Title = "INTENSITY" }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Title = "TIME" }); + + var s1 = new LineSeries + { + Color = OxyColors.Cyan, + StrokeThickness = 4, + }; + + var s2 = new LineSeries + { + Color = OxyColors.White, + StrokeThickness = 14, + }; + + var s3 = new LineSeries + { + Color = OxyColors.Red, + StrokeThickness = 4, + }; + + int n = 257; + double x0 = 1; + double x1 = 9; + for (int i = 0; i < n; i++) + { + var x = x0 + ((x1 - x0) * i / (n - 1)); + var y1 = 1.5 + (10.0 * (Math.Sin(x) * Math.Sin(x) / Math.Sqrt(x)) * Math.Exp(-0.5 * (x - 5.0) * (x - 5.0))); + var y2 = 3.0 + (10.0 * (Math.Sin(x) * Math.Sin(x) / Math.Sqrt(x)) * Math.Exp(-0.5 * (x - 7.0) * (x - 7.0))); + s1.Points.Add(new DataPoint(x, y1)); + s2.Points.Add(new DataPoint(x, y2)); + s3.Points.Add(new DataPoint(x, y2)); + } + + model.Series.Add(s1); + model.Series.Add(s2); + model.Series.Add(s3); + + return model; + } + + + /// + /// Xkcd style example #3. + /// + /// A . + [Example("Test #3")] + public static PlotModel Test3() + { + var model = new PlotModel + { + Title = "Test #3", + LegendPlacement = LegendPlacement.Outside, + LegendPosition = LegendPosition.BottomCenter, + LegendOrientation = LegendOrientation.Horizontal, + LegendBorderThickness = 0, + RenderingDecorator = rc => new XkcdRenderingDecorator(rc) + }; + + var s1 = new ColumnSeries { Title = "Series 1", IsStacked = false, StrokeColor = OxyColors.Black, StrokeThickness = 1 }; + s1.Items.Add(new ColumnItem { Value = 25 }); + s1.Items.Add(new ColumnItem { Value = 137 }); + s1.Items.Add(new ColumnItem { Value = 18 }); + s1.Items.Add(new ColumnItem { Value = 40 }); + + var s2 = new ColumnSeries { Title = "Series 2", IsStacked = false, StrokeColor = OxyColors.Black, StrokeThickness = 1 }; + s2.Items.Add(new ColumnItem { Value = 12 }); + s2.Items.Add(new ColumnItem { Value = 14 }); + s2.Items.Add(new ColumnItem { Value = 120 }); + s2.Items.Add(new ColumnItem { Value = 26 }); + + var categoryAxis = new CategoryAxis { Position = AxisPosition.Bottom }; + categoryAxis.Labels.Add("Category A"); + categoryAxis.Labels.Add("Category B"); + categoryAxis.Labels.Add("Category C"); + categoryAxis.Labels.Add("Category D"); + var valueAxis = new LinearAxis { Position = AxisPosition.Left, MinimumPadding = 0, MaximumPadding = 0.06, AbsoluteMinimum = 0 }; + model.Series.Add(s1); + model.Series.Add(s2); + model.Axes.Add(categoryAxis); + model.Axes.Add(valueAxis); + return model; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Resources/Bergensbanen.csv b/OxyPlot/Source/Examples/ExampleLibrary/Resources/Bergensbanen.csv new file mode 100644 index 0000000..da13931 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Resources/Bergensbanen.csv @@ -0,0 +1,26 @@ +Stasjon;km;moh;609;61;601;1405;607;603;63;605;62;602;608;604;64;610;1404;606 +Oslo;0.00;4.0;0631;0811;1037;;1227;1437;1609;2311;1432;1736;1956;2210;2236;2348;;0626 +Lysaker;7.00;7.5;0642;0822;1048;;1238;1448;1620;2322;1420;1724;1944;2158;2224;;; +Asker;23.83;104.6;0653;0832;1059;;1249;1458;1630;2334;1409;1713;1932;2144;2214;2324;;0555 +Drammen;52.86;2.2;0708;0846;1115;;1303;1513;1645;2351;1354;1701;1914;2125;2201;2307;;0540 +Hokksund;70.22;8.0;;;1136;;1320;1531;;0007;;1638;1854;2111;2140;;;0525 +Vikersund;95.91;67.1;;;1158;;1349;1552;;0029;;1617;1833;2048;;;;0503 +Hønefoss;124.21;96.8;0804;0937;1222;;1414;1619;1736;0056;1304;1555;1811;2025;2100;2201;;0442 +Flå;186.64;155.0;;;1314;;;1712;;;;1503;1715;1934;;2108;;0349 +Nesbyen;220.06;168.8;;1045;1340;;1532;1744;1845;0223;1152;1436;1643;1909;1949;2041;;0321 +Gol;237.02;207.4;0930;1100;1354;;1545;1800;1900;0239;1141;1423;1630;1858;1938;2028;;0308 +Ål;262.85;436.6;0950;1123;1419;1520;1608;1832;1921;0305;1121;1400;1609;1833;1919;2007;1957;0246 +Geilo;287.38;794.2;1013;1145;1442;1545;1630;1855;1942;0328;1059;1339;1547;1812;1857;1946;1939;0224 +Ustaoset;299.31;990.6;;1156;1454;1556;1643;1905;1953;0341;1048;1326;1536;1800;1845;;1927;0209 +Haugastøl;310.14;988.0;1042;;1505;1606;1655;1916;;0352;;1315;1527;1750;;1922;;0158 +Finse;336.74;1222.2;1108;1224;1526;1626;1717;1937;2021;0417;1019;1254;1508;1730;1818;1900;1854;0138 +Hallingskeid;357.44;1110.1;1123;;1538;1637;1732;1950;;;;1239;1454;1711;;1842;1839; +Myrdal;370.44;866.8;1137;1253;1551;1650;1746;2003;2045;0445;0953;1225;1440;1657;1752;1828;1825;0105 +;370.44;866.8;1220;1258;1556;1652;1751;2004;2047;0445;0950;1220;1439;1652;1750;1823;1822;0101 +// Upsete;376.79;850.2;;;;;;;;;;;;;;;; +// Mjølfjell;388.86;627.2;;;;;;;;;;;;;;;; +Voss;419.96;56.5;1312;1343;1640;1738;1836;2048;2130;0537;0910;1139;1355;1608;1710;1736;1740;0015 +Dale;459;43.4;;;;;1903;;2156;0608;0836;1109;;;;;;2344 +Vaksdal;475.17;16.0;;;;;;;;0626;;;;;;;;2326 +Arna;501.43;8.0;1410;1441;1740;1840;1935;2155;2228;0644;0806;1037;1248;1506;1606;1618;1637;2307 +Bergen;526.64;3.9;1422;1442;1752;1850;1945;2204;2235;0656;0758;1028;1240;1458;1558;1610;1628;2258 diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Resources/OxyPlot.png b/OxyPlot/Source/Examples/ExampleLibrary/Resources/OxyPlot.png new file mode 100644 index 0000000..ffb8774 Binary files /dev/null and b/OxyPlot/Source/Examples/ExampleLibrary/Resources/OxyPlot.png differ diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Resources/WorldPopulation.xml b/OxyPlot/Source/Examples/ExampleLibrary/Resources/WorldPopulation.xml new file mode 100644 index 0000000..ed5cef2 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Resources/WorldPopulation.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Resources/west0479.mtx b/OxyPlot/Source/Examples/ExampleLibrary/Resources/west0479.mtx new file mode 100644 index 0000000..4dc4550 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Resources/west0479.mtx @@ -0,0 +1,1924 @@ +%%MatrixMarket matrix coordinate real general +%------------------------------------------------------------------------------- +% UF Sparse Matrix Collection, Tim Davis +% http://www.cise.ufl.edu/research/sparse/matrices/HB/west0479 +% name: HB/west0479 +% [U 8 STAGE COLUMN SECTION, ALL SECTIONS RIGOROUS ( CHEM. ENG. )] +% id: 267 +% date: 1983 +% author: A. Westerberg +% ed: I. Duff, R. Grimes, J. Lewis +% fields: title A Zeros name id date author ed kind +% kind: chemical process simulation problem +%------------------------------------------------------------------------------- +479 479 1910 +25 1 1 +31 1 -.03764813 +87 1 -.3442396 +26 2 1 +31 2 -.02452262 +88 2 -.3737086 +27 3 1 +31 3 -.03661304 +89 3 -.8369379 +28 4 130 +29 4 -2.433767 +29 5 1 +30 5 -1.614091 +30 6 1.614091 +31 6 -.2187321 +87 6 -1 +88 6 -1 +89 6 -1 +32 7 -1.138352 +43 7 .03669428 +111 7 .09931636 +112 7 .09931636 +113 7 .09931636 +33 8 -.5 +43 8 .01611729 +111 8 .08724576 +34 9 -.3611918 +43 9 .01164286 +112 9 .1050415 +35 10 -.3218876 +43 10 .01037591 +113 10 .1404166 +36 11 -.4362416 +37 11 -.7680425 +38 11 -.1430279 +39 11 -.1593886 +37 12 1 +43 12 -.04856082 +111 12 -.2628684 +38 13 1 +43 13 -.03092595 +112 13 -.2790128 +39 14 1 +43 14 -.04612359 +113 14 -.6241882 +40 15 5.282436 +42 15 -.6123921 +41 16 .2886822 +42 16 -.2163815 +42 17 1.328774 +43 17 -.3694686 +111 17 -1 +112 17 -1 +113 17 -1 +2 18 48.17647 +8 18 -1 +11 18 -3.347484e-5 +3 19 83.5 +9 19 -1 +12 19 -4.136539e-5 +4 20 171.9412 +10 20 -1 +13 20 -8.484345e-5 +5 21 96.65138 +8 21 2.5 +11 21 3.347484e-5 +6 22 168.2706 +9 22 2.5 +12 22 4.136539e-5 +7 23 347.5872 +10 23 2.5 +13 23 8.484345e-5 +8 24 1 +17 24 -.1106967 +27 24 1.605232 +9 25 1 +17 25 -.08980852 +10 26 1 +17 26 -.1517369 +11 27 1.010455 +18 27 -.5 +12 28 1.005978 +18 28 -.3 +13 29 1.002885 +18 29 -.2 +14 30 1 +17 30 -.1811855 +15 31 1 +17 31 -.2400239 +16 32 1 +17 32 -.2265484 +17 33 1 +19 33 -1 +30 33 1.5 +42 33 .5 +18 34 1 +20 34 -316220 +23 34 -12323.69 +24 34 -1 +30 34 -35226.8 +41 34 18449.02 +19 35 5.298339 +21 35 .002452687 +20 36 1080.859 +21 36 -.2050215 +21 37 .144192 +22 37 63.05986 +30 37 -.1159299 +22 38 -18449.02 +24 38 .8453339 +40 38 -18449.02 +41 38 -15595.58 +23 39 1 +30 39 .2020493 +42 39 -.885471 +24 40 .0001000234 +30 40 2.448339 +42 40 .816113 +68 41 1 +74 41 -.0002278669 +99 41 -.2624395 +69 42 1 +74 42 -.0004188763 +100 42 -.3216196 +70 43 1 +74 43 -.001576933 +101 43 -.7264761 +71 44 300 +72 44 -2.851891 +72 45 1 +73 45 -.2870159 +73 46 .2870159 +74 46 -.004341322 +99 46 -1 +100 46 -1 +101 46 -1 +75 47 -1.138352 +86 47 .04200286 +123 47 .9414806 +124 47 .9414806 +125 47 .9414806 +76 48 -.3218876 +86 48 .011877 +123 48 1.331095 +77 49 -.3611918 +86 49 .01332724 +124 49 .9957529 +78 50 -.5 +86 50 .01844898 +125 50 .827056 +79 51 -1 +80 51 -19.24 +81 51 -3.803 +82 51 -9.481 +80 52 1 +86 52 -.0008875784 +123 52 -.09947392 +81 53 1 +86 53 -.001331368 +124 53 -.09947392 +82 54 1 +86 54 -.002218946 +125 54 -.09947392 +83 55 1.177613 +85 55 -3.108963 +84 56 .9919425 +85 56 -2.640056 +85 57 3.192112 +86 57 -.04461363 +123 57 -1 +124 57 -1 +125 57 -1 +45 58 109.8688 +51 58 -1 +54 58 -3.30811e-5 +46 59 191.3846 +52 59 -1 +55 59 -4.108467e-5 +47 60 395.4796 +53 60 -1 +56 60 -8.456383e-5 +48 61 221.7339 +51 61 2.5 +54 61 3.30811e-5 +49 62 387.0092 +52 62 2.5 +55 62 4.108467e-5 +50 63 800.8165 +53 63 2.5 +56 63 8.456383e-5 +51 64 1 +60 64 -.009170229 +52 65 1 +60 65 -.0464411 +53 66 1 +60 66 -.4899919 +54 67 1.00453 +61 67 -.2 +55 68 1.002591 +61 68 -.3 +56 69 1.00125 +61 69 -.5 +57 70 1 +60 70 -.03750053 +58 71 1 +60 71 -.124143 +59 72 1 +60 72 -.2927532 +60 73 1 +62 73 -1 +73 73 .1853733 +85 73 .06179109 +61 74 1 +63 74 -316220 +66 74 -16364.19 +67 74 -1 +73 74 -4696.782 +84 74 131.854 +62 75 22.12913 +64 75 .9537526 +63 76 2494.29 +64 76 -1.021587 +64 77 .8878281 +65 77 1.040042 +73 77 -2.640056 +65 78 -131.854 +67 78 .00805747 +83 78 -131.854 +84 78 -1.062409 +66 79 1 +73 79 .1016426 +85 79 -.06179109 +67 80 .007645257 +73 80 23.09895 +85 80 7.699649 +31 81 1 +244 81 1 +43 82 1 +1 83 1 +17 83 -3.850231 +18 83 -8.459935e-5 +31 83 2.01591 +35 83 1 +43 83 1.596171 +243 83 -.7897512 +74 84 1 +388 84 .8817562 +86 85 1 +44 86 1 +60 86 -2.79376 +61 86 -8.445823e-5 +74 86 2.015665 +78 86 1 +86 86 1.593994 +384 86 0 +387 86 -.961915 +385 87 .002424669 +386 87 .03036278 +387 87 .6730451 +388 87 1.45936 +96 88 .01689661 +97 88 .03484803 +98 88 -.06008018 +120 88 -.5552717 +121 88 -.4770398 +122 88 -.1858293 +141 88 -1 +142 88 -1 +143 88 -1 +185 88 -55.18857 +186 88 -95.67686 +187 88 -215.8377 +389 88 1 +438 88 1 +439 88 1 +440 88 1 +441 88 1 +442 88 1 +443 88 1 +450 88 -.001890756 +451 88 -.00153114 +452 88 -.001013726 +455 88 2.5 +456 88 26.0637 +458 88 1 +459 88 -1.5 +461 88 -9.603181 +462 88 -9.454185 +463 88 -9.437681 +464 88 -48.29572 +472 88 .9075922 +473 88 -4.375967 +474 88 -8.997992 +475 88 -8.881958 +476 88 1 +182 89 1 +183 89 1 +184 89 1 +391 89 1 +455 89 -1 +456 89 -26.0637 +458 89 -1 +468 89 1 +388 90 -1.45936 +467 90 1 +479 91 1 +203 92 -1 +204 92 -1 +205 92 -1 +209 92 1 +210 92 1 +211 92 1 +382 92 56.9531 +385 92 .7058325 +437 92 1 +453 92 -.6491372 +454 92 -3.294841e-5 +467 92 .5380748 +469 92 1 +479 92 .08247112 +203 93 -1 +204 93 -1 +205 93 -1 +209 93 1 +210 93 1 +211 93 1 +383 93 11.58384 +386 93 .7058325 +437 93 1 +453 93 -1.021542 +454 93 -4.099033e-5 +467 93 -.3630248 +470 93 1 +479 93 .06952261 +203 94 -1 +204 94 -1 +205 94 -1 +209 94 1 +210 94 1 +211 94 1 +384 94 .3209631 +387 94 .7058325 +437 94 1 +453 94 -2.049007 +454 94 -8.447003e-5 +467 94 .9135362 +471 94 1 +479 94 .8397638 +241 95 .5508352 +242 95 .4489223 +243 95 .0002425203 +244 95 .4052833 +108 96 -.06727662 +109 96 -.1570061 +110 96 -.0974757 +132 96 -.2506007 +133 96 -.2802617 +134 96 .1008491 +245 96 -1 +395 96 1 +396 96 1 +397 96 1 +398 96 1 +399 96 1 +400 96 1 +407 96 -.002863602 +408 96 -.002316259 +409 96 -.00153089 +412 96 2.5 +413 96 11.5878 +415 96 1 +416 96 -1.101224 +418 96 -1.364432 +419 96 -1.218821 +420 96 -1.210859 +421 96 -34.62105 +429 96 .6031359 +430 96 -.5996384 +431 96 -1.403392 +432 96 -1.370386 +433 96 1 +246 97 1 +412 97 -1 +413 97 -11.5878 +415 97 -1 +425 97 1 +244 98 -.4052833 +424 98 1 +436 99 1 +160 100 -.859384 +161 100 -.773339 +162 100 -.795174 +238 100 -1 +241 100 1 +394 100 1 +410 100 -1.623659 +411 100 -3.303278e-5 +424 100 3.572035 +426 100 1 +436 100 .9243532 +160 101 -1.171821 +161 101 -1.277352 +162 101 -1.250508 +239 101 -1 +242 101 1 +394 101 1 +410 101 -2.460289 +411 101 -4.105081e-5 +424 101 -2.178533 +427 101 1 +436 101 .6509842 +160 102 -2.328047 +161 102 -2.416155 +162 102 -2.512166 +240 102 -1 +243 102 1 +394 102 1 +410 102 -4.75362 +411 102 -8.45305e-5 +424 102 6.16714 +428 102 1 +436 102 3.396691 +241 103 .04373657 +242 103 .5739646 +243 103 .158349 +244 103 .1878125 +253 103 -.04434413 +254 103 -.5819378 +255 103 -.1605487 +256 103 -1 +135 104 -1 +136 104 -1 +137 104 -1 +151 104 -62.6809 +152 104 -123.89 +153 104 -464.3555 +245 104 1 +248 104 -.05164655 +249 104 -.3241762 +148 105 1 +149 105 1 +150 105 1 +247 105 1 +244 106 -.1878125 +248 106 1 +256 106 1 +249 107 1 +147 108 1 +169 108 -1 +170 108 -1 +171 108 -1 +175 108 1 +176 108 1 +177 108 1 +238 108 9.773875 +241 108 .7760502 +248 108 7.252831 +249 108 .78041 +253 108 -.7868306 +147 109 1 +169 109 -1 +170 109 -1 +171 109 -1 +175 109 1 +176 109 1 +177 109 1 +239 109 .6069821 +242 109 .7760502 +248 109 -2.389508 +249 109 .6849398 +254 109 -.7868306 +147 110 1 +169 110 -1 +170 110 -1 +171 110 -1 +175 110 1 +176 110 1 +177 110 1 +240 110 .001188564 +243 110 .7760502 +248 110 11.55883 +249 110 2.202644 +255 110 -.7868306 +363 111 -.1705148 +364 111 -.4342963 +365 111 -.2667421 +366 111 -2.609302 +385 111 .1956447 +386 111 .4983016 +387 111 .3060537 +388 111 .422396 +215 112 1 +216 112 1 +217 112 1 +218 112 1 +219 112 1 +220 112 1 +227 112 -.001890756 +228 112 -.00153114 +229 112 -.001013726 +232 112 2.5 +233 112 16.67241 +235 112 1 +236 112 -1.5 +389 112 -1 +392 112 -.4621213 +393 112 -.1234962 +232 113 -1 +233 113 -16.67241 +235 113 -1 +368 113 -1 +369 113 -1 +390 113 1 +366 114 2.609302 +388 114 -.422396 +392 114 1 +393 115 1 +181 116 1 +194 116 -.5869762 +195 116 -.5065155 +196 116 -.5139918 +230 116 -1.036685 +231 116 -3.294841e-5 +360 116 0 +363 116 -.8715531 +382 116 -1 +385 116 1 +392 116 2.63998 +393 116 .5574207 +181 117 1 +194 117 -.8001637 +195 117 -.836409 +196 117 -.8081026 +230 117 -1.637695 +231 117 -4.099033e-5 +361 117 0 +364 117 -.8715531 +383 117 -1 +386 117 1 +392 117 -1.773678 +393 117 .4075422 +181 118 1 +194 118 -1.589389 +195 118 -1.581811 +196 118 -1.623118 +230 118 -3.205686 +231 118 -8.447003e-5 +362 118 0 +365 118 -.8715531 +384 118 -1 +387 118 1 +392 118 4.467609 +393 118 2.247529 +93 119 -1 +96 119 -1 +248 119 -.4042156 +262 119 -.1818777 +284 119 -.1563455 +306 119 -.1545699 +328 119 -.1521778 +350 119 -.0919058 +372 119 -.007870537 +94 120 -1 +97 120 -1 +248 120 -1.809171 +262 120 -3.189655 +284 120 -3.349019 +306 120 -3.358644 +328 120 -3.308585 +350 120 -1.96787 +372 120 -.1133356 +95 121 -1 +98 121 -1 +248 121 -2.456602 +262 121 -4.101128 +284 121 -4.290892 +306 121 -4.302603 +328 121 -4.254147 +350 121 -2.952389 +372 121 -1.157365 +93 122 -.008121496 +96 122 -.01689661 +248 122 -.00453876 +262 122 -.00217052 +284 122 -.001874069 +306 122 -.001853318 +328 122 -.001824837 +350 122 -.001106561 +372 122 -.0001054494 +94 123 -.01674999 +97 123 -.03484803 +248 123 -.04189692 +262 123 -.07850668 +284 123 -.08279351 +306 123 -.08305532 +328 123 -.08182638 +350 123 -.04886605 +372 123 -.003131732 +95 124 -.02887803 +98 124 -.06008018 +248 124 -.09808224 +262 124 -.1740281 +284 124 -.1828855 +306 124 -.1834374 +328 124 -.1813914 +350 124 -.1263972 +372 124 -.05513677 +105 125 -1 +108 125 -1 +264 125 -.7636232 +286 125 -.5413211 +308 125 -.5290753 +330 125 -.528326 +352 125 -.5296534 +374 125 -.5990106 +392 125 -.5746768 +106 126 -1 +109 126 -1 +264 126 -1.467979 +286 126 -1.289806 +308 126 -1.279992 +330 126 -1.279385 +352 126 -1.280174 +374 126 -1.360143 +392 126 -.714919 +107 127 -1 +110 127 -1 +264 127 -.004370857 +286 127 -.003954101 +308 127 -.003931803 +330 127 -.003947668 +352 127 -.004749023 +374 127 -.07245539 +392 127 -1.602364 +105 128 -.1122933 +108 128 -.06727662 +264 128 -.05460137 +286 128 -.03887722 +308 128 -.03800866 +330 128 -.03795899 +352 128 -.03820886 +374 128 -.04808549 +392 128 -.05817862 +106 129 -.2620633 +109 129 -.1570061 +264 129 -.2449608 +286 129 -.2161806 +308 129 -.2145974 +330 129 -.2145191 +352 129 -.215523 +374 129 -.25481 +392 129 -.1689075 +107 130 -.1626995 +110 130 -.0974757 +264 130 -.0004528176 +286 130 -.0004114529 +308 130 -.0004092503 +330 130 -.0004109467 +352 130 -.0004963737 +374 130 -.008427185 +392 130 -.2350352 +117 131 -1 +120 131 -1 +249 131 -.06970284 +263 131 -.01924332 +285 131 -.01583793 +307 131 -.01561791 +329 131 -.01558321 +351 131 -.01471856 +373 131 -.005759952 +118 132 -1 +121 132 -1 +249 132 -.7417136 +263 132 -.8023494 +285 132 -.8065847 +307 132 -.8068286 +329 132 -.8055031 +351 132 -.7492698 +373 132 -.197197 +119 133 -1 +122 133 -1 +249 133 -.5127598 +263 133 -.5252253 +285 133 -.5261413 +307 133 -.5262245 +329 133 -.5273027 +351 133 -.5723186 +373 133 -1.025242 +117 134 -.4657593 +120 134 -.9690031 +249 134 -.04488488 +263 134 -.01317012 +285 134 -.01088739 +307 134 -.01073924 +329 134 -.01071655 +351 134 -.01016303 +373 134 -.004425719 +118 135 -.5744023 +121 135 -1.195033 +249 135 -.5890342 +263 135 -.6772174 +285 135 -.6838018 +307 135 -.6842053 +329 135 -.6831561 +351 135 -.638044 +373 135 -.1868616 +119 136 -.2292285 +122 136 -.4769055 +249 136 -.1625065 +263 136 -.1769142 +285 136 -.1780062 +307 136 -.1780856 +329 136 -.17847 +351 136 -.1944925 +373 136 -.3877026 +129 137 -1 +132 137 -1 +265 137 -.3427279 +287 137 -.2911376 +309 137 -.2876937 +331 137 -.2874889 +353 137 -.2882318 +375 137 -.3026987 +393 137 -.1750789 +130 138 -1 +133 138 -1 +265 138 -1.062334 +287 138 -1.118506 +309 138 -1.122253 +331 138 -1.122512 +353 138 -1.123285 +375 138 -1.108234 +393 138 -.3511864 +131 139 -1 +134 139 -1 +265 139 -.002399982 +287 139 -.00260173 +309 139 -.002615627 +331 139 -.002628033 +353 139 -.003161735 +375 139 -.04479381 +393 139 -.5972309 +129 140 -2.018253 +132 140 -1.209166 +265 140 -.4404489 +287 140 -.3758029 +309 140 -.3714643 +331 140 -.3712405 +353 140 -.3737109 +375 140 -.4367288 +393 140 -.3185628 +130 141 -2.562688 +133 141 -1.535345 +265 141 -1.733513 +287 141 -1.833245 +309 141 -1.839915 +331 141 -1.840541 +353 141 -1.849286 +375 141 -2.030266 +393 141 -.8113705 +131 142 -.9255429 +134 142 -.5545067 +265 142 -.001414409 +287 142 -.001540086 +309 142 -.001548758 +331 142 -.001556274 +353 142 -.001879925 +375 142 -.0296374 +393 142 -.4983387 +135 143 -1.433892 +141 143 -2.157704 +266 143 -1.523971 +288 143 -1.530708 +310 143 -1.531148 +332 143 -1.531316 +354 143 -1.537533 +376 143 -1.710928 +136 144 -.943206 +142 144 -1.419326 +267 144 -1.00246 +289 144 -1.006891 +311 144 -1.007181 +333 144 -1.007291 +355 144 -1.011381 +377 144 -1.125439 +137 145 -.5964527 +143 145 -.8975353 +268 145 -.6339227 +290 145 -.6367252 +312 145 -.6369083 +334 145 -.6369781 +356 145 -.6395642 +378 145 -.7116909 +135 146 -1 +141 146 -1 +266 146 -1 +288 146 -1 +310 146 -1 +332 146 -1 +354 146 -1 +376 146 -1 +136 147 -1 +142 147 -1 +267 147 -1 +289 147 -1 +311 147 -1 +333 147 -1 +355 147 -1 +377 147 -1 +137 148 -1 +143 148 -1 +268 148 -1 +290 148 -1 +312 148 -1 +334 148 -1 +356 148 -1 +378 148 -1 +138 149 -1 +148 149 1 +238 149 .5508352 +139 150 -1 +149 150 1.647495 +239 150 .7395973 +140 151 -1 +150 151 841.3516 +240 151 .2040448 +144 152 -1 +182 152 1 +382 152 .1956447 +145 153 -1 +183 153 1 +383 153 .4983016 +146 154 -1 +184 154 3.115622 +384 154 .9535478 +395 155 66.22492 +401 155 -1 +404 155 -3.328257e-5 +396 156 115.0623 +402 156 -1 +405 156 -4.122831e-5 +397 157 237.3387 +403 157 -1 +406 157 -8.47069e-5 +398 158 133.245 +401 158 2.5 +404 158 3.328257e-5 +399 159 232.2639 +402 159 2.5 +405 159 4.122831e-5 +400 160 480.1821 +403 160 2.5 +406 160 8.47069e-5 +160 161 -.473379 +401 161 1 +410 161 -.2116876 +161 162 -.5734317 +402 162 1 +410 162 -.3166715 +162 163 -.0006092511 +403 163 1 +410 163 -3.511874e-7 +163 164 -4575.004 +404 164 1.007562 +411 164 -.5508352 +164 165 -3681.416 +405 165 1.004324 +411 165 -.4489223 +165 166 -1787.818 +406 166 1.002087 +411 166 -.0002425203 +160 167 -.5260564 +161 167 -.4259823 +407 167 1 +410 167 -.4704884 +160 168 -.0005645987 +162 168 -.4380098 +408 168 1 +410 168 -.0005049593 +161 169 -.0005859667 +162 169 -.5613809 +409 169 1 +410 169 -.0006471876 +163 170 -1 +164 170 -1 +165 170 -1 +410 170 1 +412 170 -1 +423 170 .06144421 +435 170 .0204814 +157 171 -1 +163 171 4124.06 +164 171 4124.06 +165 171 4124.06 +411 171 1 +413 171 -316220 +416 171 -20034.24 +417 171 -1 +423 171 -2625.657 +434 171 222.129 +412 172 18.73727 +414 172 .9448816 +413 173 1494.367 +414 173 -1.02078 +158 174 -.9918601 +163 174 -17.92234 +164 174 -17.92234 +165 174 -17.92234 +414 174 .862829 +415 174 1.049719 +423 174 -.7341495 +157 175 .00813986 +415 175 -222.129 +417 175 .00813986 +433 175 -222.129 +434 175 -1.808099 +163 176 -1.091328 +164 176 -1.629397 +165 176 -1.444853 +416 176 1 +423 176 .04736406 +435 176 -.02789814 +417 177 .004538534 +423 177 7.57924 +435 177 2.526413 +148 178 -1 +178 178 -1 +149 179 -1 +179 179 -1 +150 180 -1 +180 180 -1 +148 181 1.026646 +166 181 -1 +149 182 1.073724 +167 182 -1 +150 183 1.208147 +168 183 -1 +148 184 -1 +154 184 -1 +149 185 -1 +155 185 -1 +150 186 -1 +156 186 -1 +215 187 99.14973 +221 187 -1 +224 187 -3.311398e-5 +216 188 172.6396 +222 188 -1 +225 188 -4.110812e-5 +217 189 356.6398 +223 189 -1 +226 189 -8.458718e-5 +218 190 200.0008 +221 190 2.5 +224 190 3.311398e-5 +219 191 349.0033 +222 191 2.5 +225 191 4.110812e-5 +220 192 722.0678 +223 192 2.5 +226 192 8.458718e-5 +194 193 -.1148388 +221 193 1 +230 193 -.01164592 +195 194 -.4167839 +222 194 1 +230 194 -.1700616 +196 195 -.4967614 +223 195 1 +230 195 -.2436893 +197 196 -5276.862 +224 196 1.005025 +231 196 -.1956447 +198 197 -4241.591 +225 197 1.002874 +231 197 -.4983016 +199 198 -2058.294 +226 198 1.001387 +231 198 -.3060537 +194 199 -.3987228 +195 199 -.09909709 +227 199 1 +230 199 -.08086976 +194 200 -.4864384 +196 200 -.1005598 +228 200 1 +230 200 -.09866041 +195 201 -.484119 +196 201 -.4026788 +229 201 1 +230 201 -.395073 +197 202 -1 +198 202 -1 +199 202 -1 +230 202 1 +232 202 -1 +191 203 -1 +197 203 3297.623 +198 203 3297.623 +199 203 3297.623 +231 203 1 +233 203 -316220 +236 203 -18966.66 +237 203 -1 +232 204 22.66987 +234 204 .9548602 +233 205 2248.706 +234 205 -1.020655 +192 206 -.9922951 +197 206 -21.89857 +198 206 -21.89857 +199 206 -21.89857 +234 206 .8900096 +235 206 1.039205 +191 207 .007704897 +235 207 -146.1362 +237 207 .007704897 +197 208 -.6589052 +198 208 -1.106497 +199 208 -1.000909 +236 208 1 +237 209 .006895657 +182 210 -1 +212 210 -1 +183 211 -1 +213 211 -1 +184 212 -1 +214 212 -1 +182 213 1 +200 213 -1 +183 214 1.022676 +201 214 -1 +184 215 1.091418 +202 215 -1 +182 216 -1 +188 216 -1 +183 217 -1 +189 217 -1 +184 218 -1 +190 218 -1 +241 219 -.1996961 +242 219 -.7859616 +243 219 -.0006412823 +244 219 .4069041 +253 219 .2024702 +254 219 .7968796 +255 219 .0006501906 +256 219 -2.166544 +257 220 -1 +264 220 -.300015 +265 220 -.4074615 +246 221 -1 +247 221 -1 +258 221 1 +244 222 .4069041 +256 222 -2.166544 +264 222 1 +265 223 1 +238 224 0 +241 224 -.9862989 +250 224 -1 +253 224 1 +261 224 1 +264 224 3.501858 +265 224 1.241884 +239 225 0 +242 225 -.9862989 +251 225 -1 +254 225 1 +261 225 1 +264 225 -2.149559 +265 225 .9360239 +240 226 0 +243 226 -.9862989 +252 226 -1 +255 226 1 +261 226 1 +264 226 6.025986 +265 226 4.086838 +253 227 .0119553 +254 227 .6147503 +255 227 .1605955 +256 227 .599181 +275 227 -.01194968 +276 227 -.6144612 +277 227 -.16052 +278 227 -1 +257 228 1 +262 228 -.09335086 +263 228 -.3468181 +266 228 -1 +267 228 -1 +268 228 -1 +259 229 1 +256 230 -.599181 +262 230 1 +278 230 1 +263 231 1 +250 232 13.33342 +253 232 .7873011 +260 232 1 +262 232 12.12026 +263 232 .7702509 +275 232 -.7869309 +251 233 1.020551 +254 233 .7873011 +260 233 1 +262 233 -3.984399 +263 233 .681342 +276 233 -.7869309 +252 234 .003187485 +255 234 .7873011 +260 234 1 +262 234 19.25216 +263 234 2.236907 +277 234 -.7869309 +253 235 -.1700813 +254 235 -.8296922 +255 235 -.0006970141 +256 235 2.567363 +275 235 .1700014 +276 235 .829302 +277 235 .0006966863 +278 235 -4.284787 +279 236 -1 +286 236 -.2554693 +287 236 -.4122457 +258 237 -1 +259 237 -1 +280 237 1 +256 238 2.567363 +278 238 -4.284787 +286 238 1 +287 239 1 +250 240 0 +253 240 -1.000471 +272 240 -1 +275 240 1 +283 240 1 +286 240 2.955529 +287 240 1.254414 +251 241 0 +254 241 -1.000471 +273 241 -1 +276 241 1 +283 241 1 +286 241 -1.815969 +287 241 .9452119 +252 242 0 +255 242 -1.000471 +274 242 -1 +277 242 1 +283 242 1 +286 242 5.084997 +287 242 4.136479 +250 243 .2024702 +269 243 -1 +251 244 .7968796 +270 244 -1 +252 245 .2039823 +271 245 -1 +275 246 .009818081 +276 246 .6166417 +278 246 .9557944 +297 246 -.009817569 +298 246 -.6166096 +299 246 -.1605148 +300 246 -1 +279 247 1 +284 247 -.0982179 +285 247 -.3485639 +288 247 -1 +289 247 -1 +290 247 -1 +281 248 1 +278 249 -.9557944 +284 249 1 +300 249 1 +285 250 1 +272 251 13.62671 +275 251 .786983 +282 251 1 +284 251 12.68233 +285 251 .7694287 +297 251 -.786942 +273 252 1.058389 +276 252 .786983 +282 252 1 +284 252 -4.168489 +285 252 .6810285 +298 252 -.786942 +274 253 .003415583 +277 253 .786983 +282 253 1 +284 253 20.13996 +285 253 2.239415 +299 253 -.786942 +275 254 -.1678698 +276 254 -.8314825 +277 254 -.0006999047 +278 254 4.328993 +297 254 .167861 +298 254 .8314391 +299 254 .0006998682 +300 254 -4.529209 +301 255 -1 +308 255 -.2530153 +309 255 -.4125625 +280 256 -1 +281 256 -1 +302 256 1 +278 257 4.328993 +300 257 -4.529209 +308 257 1 +309 258 1 +272 259 0 +275 259 -1.000052 +294 259 -1 +297 259 1 +305 259 1 +308 259 2.925436 +309 259 1.255249 +273 260 0 +276 260 -1.000052 +295 260 -1 +298 260 1 +305 260 1 +308 260 -1.797593 +309 260 .9458243 +274 261 0 +277 261 -1.000052 +296 261 -1 +299 261 1 +305 261 1 +308 261 5.033166 +309 261 4.139783 +272 262 .1700014 +291 262 -1 +273 263 .829302 +292 263 -1 +274 264 .2039729 +293 264 -1 +297 265 .00967985 +298 265 .6167109 +299 265 .1605181 +300 265 .9972984 +319 265 -.00968017 +320 265 -.6167313 +321 265 -.1605234 +322 265 -1 +301 266 1 +306 266 -.09852872 +307 266 -.348671 +310 266 -1 +311 266 -1 +312 266 -1 +303 267 1 +300 268 -.9972984 +306 268 1 +322 268 1 +307 269 1 +294 270 13.64601 +297 270 .7869089 +304 270 1 +306 270 12.71619 +307 270 .7693586 +319 270 -.7869349 +295 271 1.060897 +298 271 .7869089 +304 271 1 +306 271 -4.179575 +307 271 .6809936 +320 271 -.7869349 +296 272 .003430968 +299 272 .7869089 +304 272 1 +306 272 20.19341 +307 272 2.239532 +321 272 -.7869349 +297 273 -.1677233 +298 273 -.8315405 +299 273 -.0007031113 +300 273 4.531911 +319 273 .1677288 +320 273 .831568 +321 273 .0007031346 +322 273 -4.544188 +323 274 -1 +330 274 -.2528891 +331 274 -.412629 +302 275 -1 +303 275 -1 +324 275 1 +300 276 4.531911 +322 276 -4.544188 +330 276 1 +331 277 1 +294 278 0 +297 278 -.9999669 +316 278 -1 +319 278 1 +327 278 1 +330 278 2.92357 +331 278 1.255294 +295 279 0 +298 279 -.9999669 +317 279 -1 +320 279 1 +327 279 1 +330 279 -1.79649 +331 279 .9458516 +296 280 0 +299 280 -.9999669 +318 280 -1 +321 280 1 +327 280 1 +330 280 5.029935 +331 280 4.14014 +294 281 .167861 +313 281 -1 +295 282 .8314391 +314 282 -1 +296 283 .2039856 +315 283 -1 +319 284 .00964735 +320 284 .6149968 +321 284 .1606638 +322 284 1.012275 +341 284 -.009663071 +342 284 -.615999 +343 284 -.1609257 +344 284 -1 +323 285 1 +328 285 -.09774015 +329 285 -.348389 +332 285 -1 +333 285 -1 +334 285 -1 +325 286 1 +322 287 -1.012275 +328 287 1 +344 287 1 +329 288 1 +316 289 13.65337 +319 289 .785308 +326 289 1 +328 289 12.53604 +329 289 .7686138 +341 289 -.7865877 +317 290 1.061854 +320 290 .785308 +326 290 1 +328 290 -4.120345 +329 290 .6803446 +342 290 -.7865877 +318 291 .003436848 +321 291 .785308 +326 291 1 +328 291 19.9072 +329 291 2.237486 +343 291 -.7865877 +319 292 -.167696 +320 292 -.8298335 +321 292 -.0008435821 +322 292 4.531913 +341 292 .1679693 +342 292 .8311858 +343 292 .0008449567 +344 292 -4.476957 +345 293 -1 +352 293 -.2542282 +353 293 -.4146785 +324 294 -1 +325 294 -1 +346 294 1 +322 295 4.531912 +344 295 -4.476957 +352 295 1 +353 296 1 +316 297 0 +319 297 -.9983731 +338 297 -1 +341 297 1 +349 297 1 +352 297 2.9258 +353 297 1.254871 +317 298 0 +320 298 -.9983731 +339 298 -1 +342 298 1 +349 298 1 +352 298 -1.799474 +353 298 .9452959 +318 299 0 +321 299 -.9983731 +340 299 -1 +343 299 1 +349 299 1 +352 299 5.032978 +353 299 4.146532 +316 300 .1677288 +335 300 -1 +317 301 .831568 +336 301 -1 +318 302 .204587 +337 302 -1 +341 303 .008958003 +342 303 .5623915 +343 303 .1714316 +344 303 1.534987 +363 303 -.009368401 +364 303 -.5881567 +365 303 -.1792855 +366 303 -1 +345 304 1 +350 304 -.07642457 +351 304 -.336307 +354 304 -1 +355 304 -1 +356 304 -1 +347 305 1 +344 306 -1.534987 +350 306 1 +366 306 1 +351 307 1 +338 308 13.9277 +341 308 .7427812 +348 308 1 +350 308 7.712414 +351 308 .7375405 +363 308 -.7768106 +339 309 1.097792 +342 309 .7427812 +348 309 1 +350 309 -2.534534 +351 309 .653208 +364 309 -.7768106 +340 310 .00366104 +343 310 .7427812 +348 310 1 +350 310 12.24449 +351 310 2.151386 +365 310 -.7768106 +341 311 -.1672642 +342 311 -.7775783 +343 311 -.01135093 +344 311 3.941971 +363 311 .1749272 +364 311 .8132019 +365 311 .01187095 +366 311 -2.568082 +367 312 -1 +374 312 -.3113227 +375 312 -.4557268 +346 313 -1 +347 313 -1 +368 313 1 +344 314 3.941971 +366 314 -2.568082 +374 314 1 +375 315 1 +338 316 0 +341 316 -.9561935 +360 316 -1 +363 316 1 +371 316 1 +374 316 3.149454 +375 316 1.212998 +339 317 0 +342 317 -.9561935 +361 317 -1 +364 317 1 +371 317 1 +374 317 -1.985919 +375 317 .9070684 +340 318 0 +343 318 -.9561935 +362 318 -1 +365 318 1 +371 318 1 +374 318 5.393687 +375 318 4.227463 +338 319 .1679693 +357 319 -1 +339 320 .8311858 +358 320 -1 +340 321 .2307969 +359 321 -1 +363 322 .004956003 +364 322 .2092511 +365 322 .4341566 +366 322 6.177384 +385 322 -.005686404 +386 322 -.2400899 +387 322 -.4981413 +388 322 -1 +367 323 1 +372 323 -.05189959 +373 323 -.2281993 +376 323 -1 +377 323 -1 +378 323 -1 +369 324 1 +366 325 -6.177384 +372 325 1 +388 325 1 +373 326 1 +360 327 22.88466 +363 327 .6483637 +370 327 1 +372 327 1.04345 +373 327 .4217586 +385 327 -.7439176 +361 328 2.519703 +364 328 .6483637 +370 328 1 +372 328 -.3414664 +373 328 .3798898 +386 328 -.7439176 +362 329 .01772792 +365 329 .6483637 +370 329 1 +372 329 1.646052 +373 329 1.305476 +387 329 -.7439176 +360 330 .1749272 +379 330 -1 +361 331 .8132019 +380 331 -1 +362 332 .669619 +381 332 -1 +87 333 6.318058 +93 333 1.008121 +88 334 2.101652 +94 334 .98325 +89 335 10.21634 +95 335 .971122 +90 336 4.771533 +96 336 1.016897 +91 337 1.544553 +97 337 .965152 +92 338 7.403258 +98 338 .9399198 +99 339 276.7648 +105 339 .8877067 +100 340 192.1904 +106 340 1.262063 +101 341 465.2974 +107 341 .8373005 +102 342 402.6353 +108 342 .9327234 +103 343 243.9516 +109 343 1.157006 +104 344 694.4257 +110 344 .9025243 +111 345 2.147169 +117 345 .7331041 +112 346 1.830354 +118 346 .7707069 +113 347 5.419496 +119 347 .9106797 +114 348 1.59346 +120 348 .4447283 +115 349 1.519359 +121 349 .5229601 +116 350 5.927271 +122 350 .8141707 +123 351 8.601323 +129 351 .5817151 +124 352 6.197485 +130 352 .5322072 +125 353 37.67033 +131 353 1.16833 +126 354 10.95535 +132 354 .7493993 +127 355 8.286427 +133 355 .7197383 +128 356 35.09293 +134 356 1.100849 +135 357 .4338919 +138 357 2.279713 +136 358 .1137572 +139 358 .6069821 +137 359 .4035473 +140 359 .008004989 +141 360 1.157704 +144 360 4.042228 +142 361 .4193259 +145 361 2.449611 +143 362 .1024647 +146 362 .3647518 +151 363 172.5745 +154 363 14.91761 +152 364 161.5845 +155 364 12.0938 +153 365 145.3145 +156 365 5.740096 +157 366 .004501889 +158 366 .9526359 +159 366 -1 +158 367 .9448816 +163 367 19.88206 +164 367 15.9987 +165 367 7.769499 +159 368 1.00814 +163 368 98.82898 +164 368 147.5558 +165 368 130.8437 +160 369 1 +163 369 1.801198 +161 370 1 +164 370 2.196221 +162 371 1 +165 371 2.060738 +163 372 19.88206 +166 372 .9740453 +164 373 15.9987 +167 373 .9313378 +165 374 7.769499 +168 374 .827714 +169 375 -1 +172 375 -1 +175 375 .05635791 +176 375 .05635791 +177 375 .05635791 +170 376 -1 +173 376 -1 +175 376 .7395973 +176 376 .7395973 +177 376 .7395973 +171 377 -1 +174 377 -1 +175 377 .2040448 +176 377 .2040448 +177 377 .2040448 +172 378 1 +175 378 -1 +173 379 1 +176 379 -1 +174 380 1 +177 380 -1 +175 381 1 +178 381 1 +176 382 1 +179 382 1 +177 383 1 +180 383 1 +102 384 -19.45389 +418 384 1 +424 384 -.09530421 +103 385 -22.09031 +419 385 1 +424 385 -.08819762 +104 386 -49.56359 +420 386 1 +424 386 -.0001069042 +421 387 179.7345 +422 387 -2.59574 +422 388 1 +423 388 -.09621652 +102 389 -1 +103 389 -1 +104 389 -1 +423 389 .09621652 +424 389 -.008893728 +126 390 .9308279 +127 390 .9308279 +128 390 .9308279 +425 390 -1.138352 +436 390 .09534178 +126 391 .8176979 +426 391 -.5508352 +436 391 .04613478 +127 392 .8176979 +427 392 -.4489223 +436 392 .03759915 +128 393 6.806865 +428 393 -.002018842 +436 393 .0001690866 +429 394 -.6031359 +430 394 -1.191426 +431 394 -.2090101 +432 394 -.2323664 +126 395 -1.588199 +430 395 1 +436 395 -.08960673 +127 396 -1.789478 +431 396 1 +436 396 -.08228325 +128 397 -4.012804 +432 397 1 +436 397 -9.968042e-5 +433 398 1.186874 +435 398 -.8713432 +434 399 .9918601 +435 399 -.7341495 +126 400 -1 +127 400 -1 +128 400 -1 +435 400 .8978249 +436 400 -.1024269 +185 401 263.3025 +188 401 16.71023 +186 402 252.3125 +189 402 15.09138 +187 403 236.0425 +190 403 11.44029 +191 404 .006842933 +192 404 .9622744 +193 404 -1 +192 405 .9548602 +197 405 35.04212 +198 405 28.16719 +199 405 13.66854 +193 406 1.007705 +197 406 85.84676 +198 406 144.1621 +199 406 130.4054 +194 407 1 +197 407 1.658905 +195 408 1 +198 408 2.106497 +196 409 1 +199 409 2.000909 +197 410 35.04212 +200 410 1 +198 411 28.16719 +201 411 .9778269 +199 412 13.66854 +202 412 .9162394 +203 413 -1 +206 413 -1 +209 413 .00343519 +210 413 .00343519 +211 413 .00343519 +204 414 -1 +207 414 -1 +209 414 .04301697 +210 414 .04301697 +211 414 .04301697 +205 415 -1 +208 415 -1 +209 415 .9535478 +210 415 .9535478 +211 415 .9535478 +206 416 1 +209 416 -1 +207 417 1 +210 417 -1 +208 418 1 +211 418 -1 +209 419 1 +212 419 1 +210 420 1 +213 420 1 +211 421 1 +214 421 1 +90 422 -.04761313 +461 422 1 +467 422 -2.33347e-5 +91 423 -.05746435 +462 423 1 +467 423 -.0003526656 +92 424 -.1295604 +463 424 1 +467 424 -.01762542 +464 425 270.4625 +465 425 -2.800067 +465 426 1 +466 426 -1.685693 +90 427 -1 +91 427 -1 +92 427 -1 +466 427 1.685693 +467 427 -.1426674 +114 428 .1214974 +115 428 .1214974 +116 428 .1214974 +468 428 -1.138352 +479 428 .02123052 +114 429 .6055575 +469 429 -.01949018 +479 429 .0003634963 +115 430 .3357927 +470 430 -.1353383 +479 430 .00252409 +116 431 .1067309 +471 431 -.9535478 +479 431 .01778388 +472 432 -.9075922 +473 432 -5.711822 +474 432 -.9356841 +475 432 -1.034655 +114 433 -.04324093 +473 433 1 +479 433 -2.595611e-5 +115 434 -.05217491 +474 434 1 +479 434 -.000392189 +116 435 -.1176314 +475 435 1 +479 435 -.01960016 +476 436 5.314078 +478 436 -1.002184 +477 437 .3448372 +478 437 -.2647862 +114 438 -1 +115 438 -1 +116 438 -1 +478 438 1.766971 +479 438 -.1747406 +438 439 99.14973 +444 439 -1 +447 439 -3.311398e-5 +439 440 172.6396 +445 440 -1 +448 440 -4.110812e-5 +440 441 356.6398 +446 441 -1 +449 441 -8.458718e-5 +441 442 200.0008 +444 442 2.5 +447 442 3.311398e-5 +442 443 349.0033 +445 443 2.5 +448 443 4.110812e-5 +443 444 722.0678 +446 444 2.5 +449 444 8.458718e-5 +444 445 1 +453 445 -1.448565e-6 +445 446 1 +453 446 -.0005113292 +446 447 1 +453 447 -.9543887 +447 448 1.005025 +454 448 -.00343519 +448 449 1.002874 +454 449 -.04301697 +449 450 1.001387 +454 450 -.9535478 +450 451 1 +453 451 -4.94556e-5 +451 452 1 +453 452 -.002177557 +452 453 1 +453 453 -.04287153 +453 454 1 +455 454 -1 +466 454 1.5 +478 454 .5 +454 455 1 +456 455 -316220 +459 455 -12132.58 +460 455 -1 +466 455 -20451.81 +477 455 9152.751 +455 456 9.146357 +457 456 .003773492 +456 457 2248.706 +457 457 -.1250533 +457 458 .06758838 +458 458 65.08712 +466 458 -.1885904 +458 459 -9152.751 +460 459 .7543943 +476 459 -9152.751 +477 459 -6904.783 +459 460 1 +466 460 .1856929 +478 460 -.5 +460 461 .0001916794 +466 461 2.668452 +478 461 .889484 +266 462 .523971 +269 462 2.590273 +267 463 .1209036 +270 463 1 +268 464 .3660773 +271 464 .01832333 +288 465 .5307083 +291 465 2.612032 +289 466 .1214381 +292 466 1 +290 467 .3632748 +293 467 .01939848 +310 468 .5311485 +313 468 2.613447 +311 469 .1214731 +314 469 1 +312 470 .3630917 +315 470 .01947045 +332 471 .5313162 +335 471 2.613986 +333 472 .1214864 +336 472 1 +334 473 .3630219 +337 473 .01949793 +354 474 .5375334 +357 474 2.63388 +355 475 .1219796 +358 475 1 +356 476 .3604358 +359 476 .02053846 +376 477 .7109283 +379 477 3.130467 +377 478 .1357358 +380 478 1 +378 479 .2883091 +381 479 .07148988 diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Series/AreaSeriesExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Series/AreaSeriesExamples.cs new file mode 100644 index 0000000..9fdef0d --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Series/AreaSeriesExamples.cs @@ -0,0 +1,451 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("AreaSeries"), Tags("Series")] + public static class AreaSeriesExamples + { + [Example("Default style")] + public static PlotModel DefaultStyle() + { + var plotModel1 = new PlotModel { Title = "AreaSeries with default style" }; + plotModel1.Series.Add(CreateExampleAreaSeries()); + return plotModel1; + } + + [Example("Different stroke colors")] + public static PlotModel DifferentColors() + { + var plotModel1 = new PlotModel { Title = "AreaSeries with different stroke colors" }; + var areaSeries1 = CreateExampleAreaSeries(); + areaSeries1.Color = OxyColors.Red; + areaSeries1.Color2 = OxyColors.Blue; + plotModel1.Series.Add(areaSeries1); + return plotModel1; + } + + [Example("Crossing lines")] + public static PlotModel CrossingLines() + { + var plotModel1 = new PlotModel { Title = "AreaSeries with crossing lines" }; + var areaSeries1 = new AreaSeries(); + areaSeries1.Points.Add(new DataPoint(0, 50)); + areaSeries1.Points.Add(new DataPoint(10, 140)); + areaSeries1.Points.Add(new DataPoint(20, 60)); + areaSeries1.Points2.Add(new DataPoint(0, 60)); + areaSeries1.Points2.Add(new DataPoint(5, 80)); + areaSeries1.Points2.Add(new DataPoint(20, 70)); + plotModel1.Series.Add(areaSeries1); + return plotModel1; + } + + [Example("Custom TrackerFormatString")] + public static PlotModel TrackerFormatString() + { + var model = new PlotModel { Title = "AreaSeries with custom TrackerFormatString" }; + + // the axis titles will be used in the default tracker format string + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Title = "X-axis" }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = "Y-axis" }); + + var areaSeries1 = CreateExampleAreaSeries(); + areaSeries1.Title = "X={2:0.0} Y={4:0.0}"; + areaSeries1.TrackerFormatString = "X={2:0.0} Y={4:0.0}"; + model.Series.Add(areaSeries1); + return model; + } + + [Example("Constant baseline (empty Points2)")] + public static PlotModel ConstantBaseline() + { + var plotModel1 = new PlotModel { Title = "AreaSeries with constant baseline", Subtitle = "Empty Points2, ConstantY2 = 0 (default)" }; + var areaSeries1 = new AreaSeries(); + areaSeries1.Points.Add(new DataPoint(0, 50)); + areaSeries1.Points.Add(new DataPoint(10, 140)); + areaSeries1.Points.Add(new DataPoint(20, 60)); + plotModel1.Series.Add(areaSeries1); + return plotModel1; + } + + [Example("Constant baseline (empty Points2, ConstantY2=NaN)")] + public static PlotModel ConstantBaselineNaN() + { + var plotModel1 = new PlotModel { Title = "AreaSeries with constant baseline", Subtitle = "Empty Points2, ConstantY2 = NaN" }; + var areaSeries1 = new AreaSeries(); + areaSeries1.Points.Add(new DataPoint(0, 50)); + areaSeries1.Points.Add(new DataPoint(10, 140)); + areaSeries1.Points.Add(new DataPoint(20, 60)); + areaSeries1.ConstantY2 = double.NaN; + plotModel1.Series.Add(areaSeries1); + return plotModel1; + } + + [Example("Constant baseline (ItemsSource and DataField2 not set)")] + public static PlotModel ConstantBaselineItemsSource() + { + var plotModel1 = new PlotModel { Title = "AreaSeries with constant baseline", Subtitle = "ItemsSource and DataField2 not set, ConstantY2 = -20" }; + var areaSeries1 = new AreaSeries(); + var points = new[] { new DataPoint(0, 50), new DataPoint(10, 140), new DataPoint(20, 60) }; + areaSeries1.ItemsSource = points; + areaSeries1.DataFieldX = "X"; + areaSeries1.DataFieldY = "Y"; + areaSeries1.ConstantY2 = -20; + plotModel1.Series.Add(areaSeries1); + return plotModel1; + } + + [Example("LineSeries and AreaSeries")] + public static PlotModel LineSeriesAndAreaSeries() + { + var plotModel1 = new PlotModel { Title = "LineSeries and AreaSeries" }; + var linearAxis1 = new LinearAxis { Position = AxisPosition.Bottom }; + plotModel1.Axes.Add(linearAxis1); + var linearAxis2 = new LinearAxis(); + plotModel1.Axes.Add(linearAxis2); + var areaSeries1 = new AreaSeries + { + Fill = OxyColors.LightBlue, + DataFieldX2 = "Time", + DataFieldY2 = "Minimum", + Color = OxyColors.Red, + StrokeThickness = 0, + MarkerFill = OxyColors.Transparent, + DataFieldX = "Time", + DataFieldY = "Maximum" + }; + areaSeries1.Points2.Add(new DataPoint(0, -5.04135905692417)); + areaSeries1.Points2.Add(new DataPoint(2.5, -4.91731850813018)); + areaSeries1.Points2.Add(new DataPoint(5, -4.45266314658926)); + areaSeries1.Points2.Add(new DataPoint(7.5, -3.87303874542613)); + areaSeries1.Points2.Add(new DataPoint(10, -3.00101110255393)); + areaSeries1.Points2.Add(new DataPoint(12.5, -2.17980725503518)); + areaSeries1.Points2.Add(new DataPoint(15, -1.67332229254456)); + areaSeries1.Points2.Add(new DataPoint(17.5, -1.10537158549082)); + areaSeries1.Points2.Add(new DataPoint(20, -0.6145459544447)); + areaSeries1.Points2.Add(new DataPoint(22.5, 0.120028106039404)); + areaSeries1.Points2.Add(new DataPoint(25, 1.06357270435597)); + areaSeries1.Points2.Add(new DataPoint(27.5, 1.87301405606466)); + areaSeries1.Points2.Add(new DataPoint(30, 2.57569854952195)); + areaSeries1.Points2.Add(new DataPoint(32.5, 3.59165537664278)); + areaSeries1.Points2.Add(new DataPoint(35, 4.87991958133872)); + areaSeries1.Points2.Add(new DataPoint(37.5, 6.36214537958714)); + areaSeries1.Points2.Add(new DataPoint(40, 7.62564585126268)); + areaSeries1.Points2.Add(new DataPoint(42.5, 8.69606320261772)); + areaSeries1.Points2.Add(new DataPoint(45, 10.0118704438265)); + areaSeries1.Points2.Add(new DataPoint(47.5, 11.0434480519236)); + areaSeries1.Points2.Add(new DataPoint(50, 11.9794171576758)); + areaSeries1.Points2.Add(new DataPoint(52.5, 12.9591851832621)); + areaSeries1.Points2.Add(new DataPoint(55, 14.172107889304)); + areaSeries1.Points2.Add(new DataPoint(57.5, 15.5520057698488)); + areaSeries1.Points2.Add(new DataPoint(60, 17.2274942386092)); + areaSeries1.Points2.Add(new DataPoint(62.5, 18.6983982186757)); + areaSeries1.Points2.Add(new DataPoint(65, 20.4560332001448)); + areaSeries1.Points2.Add(new DataPoint(67.5, 22.4867327382261)); + areaSeries1.Points2.Add(new DataPoint(70, 24.5319674302041)); + areaSeries1.Points2.Add(new DataPoint(72.5, 26.600547815813)); + areaSeries1.Points2.Add(new DataPoint(75, 28.5210891459701)); + areaSeries1.Points2.Add(new DataPoint(77.5, 30.6793080755413)); + areaSeries1.Points2.Add(new DataPoint(80, 33.0546651200646)); + areaSeries1.Points2.Add(new DataPoint(82.5, 35.3256065179713)); + areaSeries1.Points2.Add(new DataPoint(85, 37.6336074839968)); + areaSeries1.Points2.Add(new DataPoint(87.5, 40.2012266359763)); + areaSeries1.Points2.Add(new DataPoint(90, 42.8923555399256)); + areaSeries1.Points2.Add(new DataPoint(92.5, 45.8665211907432)); + areaSeries1.Points2.Add(new DataPoint(95, 48.8200195945427)); + areaSeries1.Points2.Add(new DataPoint(97.5, 51.8304284402311)); + areaSeries1.Points2.Add(new DataPoint(100, 54.6969868542147)); + areaSeries1.Points2.Add(new DataPoint(102.5, 57.7047292990632)); + areaSeries1.Points2.Add(new DataPoint(105, 60.4216644602929)); + areaSeries1.Points2.Add(new DataPoint(107.5, 62.926258762519)); + areaSeries1.Points2.Add(new DataPoint(110, 65.1829734629407)); + areaSeries1.Points2.Add(new DataPoint(112.5, 67.2365592083133)); + areaSeries1.Points2.Add(new DataPoint(115, 69.5713628691022)); + areaSeries1.Points2.Add(new DataPoint(117.5, 71.7267046705944)); + areaSeries1.Points2.Add(new DataPoint(120, 73.633463102781)); + areaSeries1.Points2.Add(new DataPoint(122.5, 75.4660150158061)); + areaSeries1.Points2.Add(new DataPoint(125, 77.5669292504745)); + areaSeries1.Points2.Add(new DataPoint(127.5, 79.564218544664)); + areaSeries1.Points2.Add(new DataPoint(130, 81.8631309028078)); + areaSeries1.Points2.Add(new DataPoint(132.5, 83.9698189969034)); + areaSeries1.Points2.Add(new DataPoint(135, 86.3847886532009)); + areaSeries1.Points2.Add(new DataPoint(137.5, 88.5559348267764)); + areaSeries1.Points2.Add(new DataPoint(140, 91.0455050418365)); + areaSeries1.Points2.Add(new DataPoint(142.5, 93.6964157585504)); + areaSeries1.Points2.Add(new DataPoint(145, 96.284336864941)); + areaSeries1.Points2.Add(new DataPoint(147.5, 98.7508602689723)); + areaSeries1.Points2.Add(new DataPoint(150, 100.904510594255)); + areaSeries1.Points2.Add(new DataPoint(152.5, 103.266136681506)); + areaSeries1.Points2.Add(new DataPoint(155, 105.780951269521)); + areaSeries1.Points2.Add(new DataPoint(157.5, 108.032859257065)); + areaSeries1.Points2.Add(new DataPoint(160, 110.035478448093)); + areaSeries1.Points2.Add(new DataPoint(162.5, 112.10655731615)); + areaSeries1.Points2.Add(new DataPoint(165, 114.37480786097)); + areaSeries1.Points2.Add(new DataPoint(167.5, 116.403992550869)); + areaSeries1.Points2.Add(new DataPoint(170, 118.61663988727)); + areaSeries1.Points2.Add(new DataPoint(172.5, 120.538730287384)); + areaSeries1.Points2.Add(new DataPoint(175, 122.515721057177)); + areaSeries1.Points2.Add(new DataPoint(177.5, 124.474386629124)); + areaSeries1.Points2.Add(new DataPoint(180, 126.448283293214)); + areaSeries1.Points2.Add(new DataPoint(182.5, 128.373811322299)); + areaSeries1.Points2.Add(new DataPoint(185, 130.33627914667)); + areaSeries1.Points2.Add(new DataPoint(187.5, 132.487933658477)); + areaSeries1.Points2.Add(new DataPoint(190, 134.716989778456)); + areaSeries1.Points2.Add(new DataPoint(192.5, 136.817287595392)); + areaSeries1.Points2.Add(new DataPoint(195, 139.216488664698)); + areaSeries1.Points2.Add(new DataPoint(197.5, 141.50803227574)); + areaSeries1.Points2.Add(new DataPoint(200, 143.539586683614)); + areaSeries1.Points2.Add(new DataPoint(202.5, 145.535911545221)); + areaSeries1.Points2.Add(new DataPoint(205, 147.516964978686)); + areaSeries1.Points2.Add(new DataPoint(207.5, 149.592416731684)); + areaSeries1.Points2.Add(new DataPoint(210, 151.600983566512)); + areaSeries1.Points2.Add(new DataPoint(212.5, 153.498210993362)); + areaSeries1.Points2.Add(new DataPoint(215, 155.512606828247)); + areaSeries1.Points2.Add(new DataPoint(217.5, 157.426564302774)); + areaSeries1.Points2.Add(new DataPoint(220, 159.364474964172)); + areaSeries1.Points2.Add(new DataPoint(222.5, 161.152806492128)); + areaSeries1.Points2.Add(new DataPoint(225, 162.679069434562)); + areaSeries1.Points2.Add(new DataPoint(227.5, 163.893622036741)); + areaSeries1.Points2.Add(new DataPoint(230, 165.475827621238)); + areaSeries1.Points2.Add(new DataPoint(232.5, 167.303960444734)); + areaSeries1.Points2.Add(new DataPoint(235, 169.259393394952)); + areaSeries1.Points2.Add(new DataPoint(237.5, 171.265193646758)); + areaSeries1.Points2.Add(new DataPoint(240, 173.074304345192)); + areaSeries1.Points2.Add(new DataPoint(242.5, 174.975492766814)); + areaSeries1.Points2.Add(new DataPoint(245, 176.684088218484)); + areaSeries1.Points2.Add(new DataPoint(247.5, 178.406887247603)); + areaSeries1.Points.Add(new DataPoint(0, 5.0184649433561)); + areaSeries1.Points.Add(new DataPoint(2.5, 5.27685959268215)); + areaSeries1.Points.Add(new DataPoint(5, 5.81437064628786)); + areaSeries1.Points.Add(new DataPoint(7.5, 6.51022475040994)); + areaSeries1.Points.Add(new DataPoint(10, 7.49921246878766)); + areaSeries1.Points.Add(new DataPoint(12.5, 8.41941631823751)); + areaSeries1.Points.Add(new DataPoint(15, 9.09826907222079)); + areaSeries1.Points.Add(new DataPoint(17.5, 9.89500750098145)); + areaSeries1.Points.Add(new DataPoint(20, 10.6633345249404)); + areaSeries1.Points.Add(new DataPoint(22.5, 11.6249613445368)); + areaSeries1.Points.Add(new DataPoint(25, 12.8816391467497)); + areaSeries1.Points.Add(new DataPoint(27.5, 13.9665185705603)); + areaSeries1.Points.Add(new DataPoint(30, 14.8501816818724)); + areaSeries1.Points.Add(new DataPoint(32.5, 16.0683128022441)); + areaSeries1.Points.Add(new DataPoint(35, 17.5378799723172)); + areaSeries1.Points.Add(new DataPoint(37.5, 19.1262752954039)); + areaSeries1.Points.Add(new DataPoint(40, 20.4103953650735)); + areaSeries1.Points.Add(new DataPoint(42.5, 21.5430627723891)); + areaSeries1.Points.Add(new DataPoint(45, 22.9105459463366)); + areaSeries1.Points.Add(new DataPoint(47.5, 23.9802361888719)); + areaSeries1.Points.Add(new DataPoint(50, 24.8659461235003)); + areaSeries1.Points.Add(new DataPoint(52.5, 25.7303194442439)); + areaSeries1.Points.Add(new DataPoint(55, 26.7688545912359)); + areaSeries1.Points.Add(new DataPoint(57.5, 28.0545112571933)); + areaSeries1.Points.Add(new DataPoint(60, 29.7036634266394)); + areaSeries1.Points.Add(new DataPoint(62.5, 31.2273634344467)); + areaSeries1.Points.Add(new DataPoint(65, 33.1038196356519)); + areaSeries1.Points.Add(new DataPoint(67.5, 35.2639893610328)); + areaSeries1.Points.Add(new DataPoint(70, 37.434293559489)); + areaSeries1.Points.Add(new DataPoint(72.5, 39.7109359368267)); + areaSeries1.Points.Add(new DataPoint(75, 41.7573881676222)); + areaSeries1.Points.Add(new DataPoint(77.5, 44.0460374479862)); + areaSeries1.Points.Add(new DataPoint(80, 46.5098714746581)); + areaSeries1.Points.Add(new DataPoint(82.5, 48.7754012129155)); + areaSeries1.Points.Add(new DataPoint(85, 51.1619816926597)); + areaSeries1.Points.Add(new DataPoint(87.5, 53.9036778414639)); + areaSeries1.Points.Add(new DataPoint(90, 56.7448825012636)); + areaSeries1.Points.Add(new DataPoint(92.5, 59.9294987878434)); + areaSeries1.Points.Add(new DataPoint(95, 63.0148831289797)); + areaSeries1.Points.Add(new DataPoint(97.5, 66.0721745989622)); + areaSeries1.Points.Add(new DataPoint(100, 68.8980036274521)); + areaSeries1.Points.Add(new DataPoint(102.5, 71.7719322611447)); + areaSeries1.Points.Add(new DataPoint(105, 74.4206055336728)); + areaSeries1.Points.Add(new DataPoint(107.5, 76.816198386632)); + areaSeries1.Points.Add(new DataPoint(110, 79.0040432726983)); + areaSeries1.Points.Add(new DataPoint(112.5, 80.9617606926066)); + areaSeries1.Points.Add(new DataPoint(115, 83.1345574620341)); + areaSeries1.Points.Add(new DataPoint(117.5, 85.0701022046479)); + areaSeries1.Points.Add(new DataPoint(120, 86.8557530286516)); + areaSeries1.Points.Add(new DataPoint(122.5, 88.5673387745243)); + areaSeries1.Points.Add(new DataPoint(125, 90.6003321543338)); + areaSeries1.Points.Add(new DataPoint(127.5, 92.439864576254)); + areaSeries1.Points.Add(new DataPoint(130, 94.5383744861178)); + areaSeries1.Points.Add(new DataPoint(132.5, 96.4600166864507)); + areaSeries1.Points.Add(new DataPoint(135, 98.6091052949006)); + areaSeries1.Points.Add(new DataPoint(137.5, 100.496459351478)); + areaSeries1.Points.Add(new DataPoint(140, 102.705767030085)); + areaSeries1.Points.Add(new DataPoint(142.5, 105.009994476992)); + areaSeries1.Points.Add(new DataPoint(145, 107.31287026052)); + areaSeries1.Points.Add(new DataPoint(147.5, 109.584842542272)); + areaSeries1.Points.Add(new DataPoint(150, 111.641435600837)); + areaSeries1.Points.Add(new DataPoint(152.5, 113.988459973544)); + areaSeries1.Points.Add(new DataPoint(155, 116.50349048027)); + areaSeries1.Points.Add(new DataPoint(157.5, 118.753612704274)); + areaSeries1.Points.Add(new DataPoint(160, 120.801728924085)); + areaSeries1.Points.Add(new DataPoint(162.5, 122.902486914165)); + areaSeries1.Points.Add(new DataPoint(165, 125.104391935796)); + areaSeries1.Points.Add(new DataPoint(167.5, 127.06056966547)); + areaSeries1.Points.Add(new DataPoint(170, 129.217086578495)); + areaSeries1.Points.Add(new DataPoint(172.5, 131.151968896274)); + areaSeries1.Points.Add(new DataPoint(175, 133.159906275133)); + areaSeries1.Points.Add(new DataPoint(177.5, 135.065263957561)); + areaSeries1.Points.Add(new DataPoint(180, 137.041870026822)); + areaSeries1.Points.Add(new DataPoint(182.5, 138.937477489811)); + areaSeries1.Points.Add(new DataPoint(185, 140.776914926282)); + areaSeries1.Points.Add(new DataPoint(187.5, 142.786975776398)); + areaSeries1.Points.Add(new DataPoint(190, 144.862762377347)); + areaSeries1.Points.Add(new DataPoint(192.5, 146.89654967049)); + areaSeries1.Points.Add(new DataPoint(195, 149.204343821204)); + areaSeries1.Points.Add(new DataPoint(197.5, 151.369748673527)); + areaSeries1.Points.Add(new DataPoint(200, 153.324438580137)); + areaSeries1.Points.Add(new DataPoint(202.5, 155.173148715344)); + areaSeries1.Points.Add(new DataPoint(205, 157.0501827528)); + areaSeries1.Points.Add(new DataPoint(207.5, 159.109122278359)); + areaSeries1.Points.Add(new DataPoint(210, 161.044446932778)); + areaSeries1.Points.Add(new DataPoint(212.5, 162.942364031841)); + areaSeries1.Points.Add(new DataPoint(215, 164.966769883021)); + areaSeries1.Points.Add(new DataPoint(217.5, 166.89711806788)); + areaSeries1.Points.Add(new DataPoint(220, 168.906874949069)); + areaSeries1.Points.Add(new DataPoint(222.5, 170.85692034995)); + areaSeries1.Points.Add(new DataPoint(225, 172.602125010408)); + areaSeries1.Points.Add(new DataPoint(227.5, 173.964258466598)); + areaSeries1.Points.Add(new DataPoint(230, 175.629908385654)); + areaSeries1.Points.Add(new DataPoint(232.5, 177.495778359378)); + areaSeries1.Points.Add(new DataPoint(235, 179.432933300749)); + areaSeries1.Points.Add(new DataPoint(237.5, 181.400180771342)); + areaSeries1.Points.Add(new DataPoint(240, 183.232300309899)); + areaSeries1.Points.Add(new DataPoint(242.5, 185.225502661441)); + areaSeries1.Points.Add(new DataPoint(245, 186.979590140413)); + areaSeries1.Points.Add(new DataPoint(247.5, 188.816640077725)); + areaSeries1.Title = "Maximum/Minimum"; + plotModel1.Series.Add(areaSeries1); + + var lineSeries1 = new LineSeries + { + Color = OxyColors.Blue, + MarkerFill = OxyColors.Transparent, + DataFieldX = "Time", + DataFieldY = "Value" + }; + lineSeries1.Points.Add(new DataPoint(0, -0.011447056784037)); + lineSeries1.Points.Add(new DataPoint(2.5, 0.179770542275985)); + lineSeries1.Points.Add(new DataPoint(5, 0.6808537498493)); + lineSeries1.Points.Add(new DataPoint(7.5, 1.31859300249191)); + lineSeries1.Points.Add(new DataPoint(10, 2.24910068311687)); + lineSeries1.Points.Add(new DataPoint(12.5, 3.11980453160117)); + lineSeries1.Points.Add(new DataPoint(15, 3.71247338983811)); + lineSeries1.Points.Add(new DataPoint(17.5, 4.39481795774531)); + lineSeries1.Points.Add(new DataPoint(20, 5.02439428524784)); + lineSeries1.Points.Add(new DataPoint(22.5, 5.87249472528812)); + lineSeries1.Points.Add(new DataPoint(25, 6.97260592555283)); + lineSeries1.Points.Add(new DataPoint(27.5, 7.91976631331247)); + lineSeries1.Points.Add(new DataPoint(30, 8.71294011569719)); + lineSeries1.Points.Add(new DataPoint(32.5, 9.82998408944345)); + lineSeries1.Points.Add(new DataPoint(35, 11.208899776828)); + lineSeries1.Points.Add(new DataPoint(37.5, 12.7442103374955)); + lineSeries1.Points.Add(new DataPoint(40, 14.0180206081681)); + lineSeries1.Points.Add(new DataPoint(42.5, 15.1195629875034)); + lineSeries1.Points.Add(new DataPoint(45, 16.4612081950815)); + lineSeries1.Points.Add(new DataPoint(47.5, 17.5118421203978)); + lineSeries1.Points.Add(new DataPoint(50, 18.4226816405881)); + lineSeries1.Points.Add(new DataPoint(52.5, 19.344752313753)); + lineSeries1.Points.Add(new DataPoint(55, 20.47048124027)); + lineSeries1.Points.Add(new DataPoint(57.5, 21.8032585135211)); + lineSeries1.Points.Add(new DataPoint(60, 23.4655788326243)); + lineSeries1.Points.Add(new DataPoint(62.5, 24.9628808265612)); + lineSeries1.Points.Add(new DataPoint(65, 26.7799264178984)); + lineSeries1.Points.Add(new DataPoint(67.5, 28.8753610496295)); + lineSeries1.Points.Add(new DataPoint(70, 30.9831304948466)); + lineSeries1.Points.Add(new DataPoint(72.5, 33.1557418763199)); + lineSeries1.Points.Add(new DataPoint(75, 35.1392386567962)); + lineSeries1.Points.Add(new DataPoint(77.5, 37.3626727617638)); + lineSeries1.Points.Add(new DataPoint(80, 39.7822682973613)); + lineSeries1.Points.Add(new DataPoint(82.5, 42.0505038654434)); + lineSeries1.Points.Add(new DataPoint(85, 44.3977945883283)); + lineSeries1.Points.Add(new DataPoint(87.5, 47.0524522387201)); + lineSeries1.Points.Add(new DataPoint(90, 49.8186190205946)); + lineSeries1.Points.Add(new DataPoint(92.5, 52.8980099892933)); + lineSeries1.Points.Add(new DataPoint(95, 55.9174513617612)); + lineSeries1.Points.Add(new DataPoint(97.5, 58.9513015195966)); + lineSeries1.Points.Add(new DataPoint(100, 61.7974952408334)); + lineSeries1.Points.Add(new DataPoint(102.5, 64.738330780104)); + lineSeries1.Points.Add(new DataPoint(105, 67.4211349969828)); + lineSeries1.Points.Add(new DataPoint(107.5, 69.8712285745755)); + lineSeries1.Points.Add(new DataPoint(110, 72.0935083678195)); + lineSeries1.Points.Add(new DataPoint(112.5, 74.0991599504599)); + lineSeries1.Points.Add(new DataPoint(115, 76.3529601655682)); + lineSeries1.Points.Add(new DataPoint(117.5, 78.3984034376212)); + lineSeries1.Points.Add(new DataPoint(120, 80.2446080657163)); + lineSeries1.Points.Add(new DataPoint(122.5, 82.0166768951652)); + lineSeries1.Points.Add(new DataPoint(125, 84.0836307024042)); + lineSeries1.Points.Add(new DataPoint(127.5, 86.002041560459)); + lineSeries1.Points.Add(new DataPoint(130, 88.2007526944628)); + lineSeries1.Points.Add(new DataPoint(132.5, 90.2149178416771)); + lineSeries1.Points.Add(new DataPoint(135, 92.4969469740507)); + lineSeries1.Points.Add(new DataPoint(137.5, 94.5261970891274)); + lineSeries1.Points.Add(new DataPoint(140, 96.875636035961)); + lineSeries1.Points.Add(new DataPoint(142.5, 99.3532051177711)); + lineSeries1.Points.Add(new DataPoint(145, 101.798603562731)); + lineSeries1.Points.Add(new DataPoint(147.5, 104.167851405622)); + lineSeries1.Points.Add(new DataPoint(150, 106.272973097546)); + lineSeries1.Points.Add(new DataPoint(152.5, 108.627298327525)); + lineSeries1.Points.Add(new DataPoint(155, 111.142220874895)); + lineSeries1.Points.Add(new DataPoint(157.5, 113.39323598067)); + lineSeries1.Points.Add(new DataPoint(160, 115.418603686089)); + lineSeries1.Points.Add(new DataPoint(162.5, 117.504522115157)); + lineSeries1.Points.Add(new DataPoint(165, 119.739599898383)); + lineSeries1.Points.Add(new DataPoint(167.5, 121.732281108169)); + lineSeries1.Points.Add(new DataPoint(170, 123.916863232882)); + lineSeries1.Points.Add(new DataPoint(172.5, 125.845349591829)); + lineSeries1.Points.Add(new DataPoint(175, 127.837813666155)); + lineSeries1.Points.Add(new DataPoint(177.5, 129.769825293343)); + lineSeries1.Points.Add(new DataPoint(180, 131.745076660018)); + lineSeries1.Points.Add(new DataPoint(182.5, 133.655644406055)); + lineSeries1.Points.Add(new DataPoint(185, 135.556597036476)); + lineSeries1.Points.Add(new DataPoint(187.5, 137.637454717438)); + lineSeries1.Points.Add(new DataPoint(190, 139.789876077902)); + lineSeries1.Points.Add(new DataPoint(192.5, 141.856918632941)); + lineSeries1.Points.Add(new DataPoint(195, 144.210416242951)); + lineSeries1.Points.Add(new DataPoint(197.5, 146.438890474634)); + lineSeries1.Points.Add(new DataPoint(200, 148.432012631876)); + lineSeries1.Points.Add(new DataPoint(202.5, 150.354530130282)); + lineSeries1.Points.Add(new DataPoint(205, 152.283573865743)); + lineSeries1.Points.Add(new DataPoint(207.5, 154.350769505022)); + lineSeries1.Points.Add(new DataPoint(210, 156.322715249645)); + lineSeries1.Points.Add(new DataPoint(212.5, 158.220287512602)); + lineSeries1.Points.Add(new DataPoint(215, 160.239688355634)); + lineSeries1.Points.Add(new DataPoint(217.5, 162.161841185327)); + lineSeries1.Points.Add(new DataPoint(220, 164.135674956621)); + lineSeries1.Points.Add(new DataPoint(222.5, 166.004863421039)); + lineSeries1.Points.Add(new DataPoint(225, 167.640597222485)); + lineSeries1.Points.Add(new DataPoint(227.5, 168.928940251669)); + lineSeries1.Points.Add(new DataPoint(230, 170.552868003446)); + lineSeries1.Points.Add(new DataPoint(232.5, 172.399869402056)); + lineSeries1.Points.Add(new DataPoint(235, 174.346163347851)); + lineSeries1.Points.Add(new DataPoint(237.5, 176.33268720905)); + lineSeries1.Points.Add(new DataPoint(240, 178.153302327545)); + lineSeries1.Points.Add(new DataPoint(242.5, 180.100497714128)); + lineSeries1.Points.Add(new DataPoint(245, 181.831839179449)); + lineSeries1.Points.Add(new DataPoint(247.5, 183.611763662664)); + lineSeries1.Title = "Average"; + plotModel1.Series.Add(lineSeries1); + return plotModel1; + } + + private static AreaSeries CreateExampleAreaSeries() + { + var areaSeries1 = new AreaSeries(); + areaSeries1.Points.Add(new DataPoint(0, 50)); + areaSeries1.Points.Add(new DataPoint(10, 40)); + areaSeries1.Points.Add(new DataPoint(20, 60)); + areaSeries1.Points2.Add(new DataPoint(0, 60)); + areaSeries1.Points2.Add(new DataPoint(5, 80)); + areaSeries1.Points2.Add(new DataPoint(20, 70)); + return areaSeries1; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Series/BarAndColumnSeriesExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Series/BarAndColumnSeriesExamples.cs new file mode 100644 index 0000000..a142c4f --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Series/BarAndColumnSeriesExamples.cs @@ -0,0 +1,946 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Linq; + + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + + public abstract class BarAndColumnSeriesExamples + where TSeries : BarSeriesBase, new() + where TItem : BarItemBase, new() + { + [Example("Simple model")] + public static PlotModel SimpleModel() + { + return CreateSimpleModel(false, "Simple model"); + } + + [Example("With labels")] + public static PlotModel WithLabels() + { + var model = CreateSimpleModel(false, "With labels"); + var s0 = model.Series[0] as BarSeriesBase; + var s1 = model.Series[1] as BarSeriesBase; + s0.LabelFormatString = "{0}"; + s1.LabelFormatString = "{0:0.00}"; + s1.LabelPlacement = LabelPlacement.Middle; + s1.TextColor = OxyColors.White; + return model; + } + + [Example("With labels (negative values)")] + public static PlotModel WithLabelsNegativeValues() + { + var model = new PlotModel + { + Title = "With labels (negative values)", + }; + + var s1 = new TSeries { LabelFormatString = "{0}" }; + s1.Items.Add(new TItem { Value = 25 }); + s1.Items.Add(new TItem { Value = 137 }); + s1.Items.Add(new TItem { Value = -18 }); + s1.Items.Add(new TItem { Value = 40 }); + + var s2 = new TSeries { LabelFormatString = "{0:0.00}" }; + s2.Items.Add(new TItem { Value = 12 }); + s2.Items.Add(new TItem { Value = 14 }); + s2.Items.Add(new TItem { Value = 120 }); + s2.Items.Add(new TItem { Value = -26 }); + + var categoryAxis = new CategoryAxis { Position = CategoryAxisPosition() }; + categoryAxis.Labels.Add("Category A"); + categoryAxis.Labels.Add("Category B"); + categoryAxis.Labels.Add("Category C"); + categoryAxis.Labels.Add("Category D"); + var valueAxis = new LinearAxis { Position = ValueAxisPosition(), MinimumPadding = 0.06, MaximumPadding = 0.06, ExtraGridlines = new[] { 0d } }; + model.Series.Add(s1); + model.Series.Add(s2); + model.Axes.Add(categoryAxis); + model.Axes.Add(valueAxis); + return model; + } + + [Example("Stacked")] + public static PlotModel StackedSeries() + { + return CreateSimpleModel(true, "Simple stacked model"); + } + + [Example("Empty series")] + public static PlotModel EmptySeries() + { + var model = new PlotModel { Title = "Empty series" }; + + var s1 = new TSeries { Title = "Series 1" }; + var s2 = new TSeries { Title = "Series 2" }; + var categoryAxis = new CategoryAxis { Position = CategoryAxisPosition() }; + var valueAxis = new LinearAxis { Position = ValueAxisPosition(), MinimumPadding = 0 }; + model.Series.Add(s1); + model.Series.Add(s2); + model.Axes.Add(categoryAxis); + model.Axes.Add(valueAxis); + return model; + } + + [Example("No category axis defined")] + public static PlotModel NoCategoryAxisDefined() + { + var model = new PlotModel { Title = "No category axis defined" }; + + var s1 = new TSeries { Title = "Series 1", ItemsSource = new[] { new TItem { Value = 25 }, new TItem { Value = 137 } } }; + var s2 = new TSeries { Title = "Series 2", ItemsSource = new[] { new TItem { Value = 52 }, new TItem { Value = 317 } } }; + var valueAxis = new LinearAxis { Position = ValueAxisPosition(), MinimumPadding = 0 }; + model.Series.Add(s1); + model.Series.Add(s2); + model.Axes.Add(valueAxis); + return model; + } + + [Example("Binding to ItemsSource")] + public static PlotModel BindingItemsSource() + { + var items = new Collection + { + new Item { Label = "Apples", Value1 = 37, Value2 = 12, Value3 = 19 }, + new Item { Label = "Pears", Value1 = 7, Value2 = 21, Value3 = 9 }, + new Item { Label = "Bananas", Value1 = 23, Value2 = 2, Value3 = 29 } + }; + + var plotModel1 = new PlotModel { LegendPlacement = LegendPlacement.Outside, Title = "Binding to ItemsSource" }; + var categoryAxis1 = new CategoryAxis { Position = CategoryAxisPosition(), LabelField = "Label", ItemsSource = items, MajorStep = 1, MinorStep = 1 }; + plotModel1.Axes.Add(categoryAxis1); + var linearAxis1 = new LinearAxis { Position = ValueAxisPosition(), AbsoluteMinimum = 0, MinimumPadding = 0 }; + plotModel1.Axes.Add(linearAxis1); + var series1 = new TSeries + { + FillColor = OxyColor.FromArgb(255, 78, 154, 6), + ValueField = "Value1", + Title = "2009", + ItemsSource = items + }; + plotModel1.Series.Add(series1); + var series2 = new TSeries + { + FillColor = OxyColor.FromArgb(255, 200, 141, 0), + ValueField = "Value2", + Title = "2010", + ItemsSource = items + }; + plotModel1.Series.Add(series2); + var series3 = new TSeries + { + FillColor = OxyColor.FromArgb(255, 204, 0, 0), + ValueField = "Value3", + Title = "2011", + ItemsSource = items + }; + plotModel1.Series.Add(series3); + return plotModel1; + } + + [Example("Binding to ItemsSource (array)")] + public static PlotModel BindingToItemsSourceArray() + { + var model = new PlotModel { Title = "Binding to ItemsSource", Subtitle = "The items are defined by an array of BarItem/ColumnItem" }; + model.Series.Add(new TSeries { Title = "Series 1", ItemsSource = new[] { new TItem { Value = 25 }, new TItem { Value = 137 } } }); + model.Series.Add(new TSeries { Title = "Series 2", ItemsSource = new[] { new TItem { Value = 52 }, new TItem { Value = 317 } } }); + model.Axes.Add(new CategoryAxis { Position = CategoryAxisPosition() }); + model.Axes.Add(new LinearAxis { Position = ValueAxisPosition(), MinimumPadding = 0 }); + return model; + } + + [Example("Binding to ItemsSource (list)")] + public static PlotModel BindingToItemsSourceListT() + { + var model = new PlotModel { Title = "Binding to ItemsSource", Subtitle = "The items are defined by a List of BarItem/ColumnItem" }; + model.Series.Add(new TSeries { Title = "Series 1", ItemsSource = new List(new[] { new TItem { Value = 25 }, new TItem { Value = 137 } }) }); + model.Series.Add(new TSeries { Title = "Series 2", ItemsSource = new List(new[] { new TItem { Value = 52 }, new TItem { Value = 317 } }) }); + model.Axes.Add(new CategoryAxis { Position = CategoryAxisPosition() }); + model.Axes.Add(new LinearAxis { Position = ValueAxisPosition(), MinimumPadding = 0 }); + return model; + } + + [Example("Binding to ItemsSource (reflection)")] + public static PlotModel BindingToItemsSourceReflection() + { + var model = new PlotModel { Title = "Binding to ItemsSource", Subtitle = "Reflect by 'ValueField'" }; + model.Series.Add(new TSeries { Title = "Series 1", ValueField = "Value1", ItemsSource = new[] { new Item { Value1 = 25 }, new Item { Value1 = 137 } } }); + model.Series.Add(new TSeries { Title = "Series 2", ValueField = "Value1", ItemsSource = new[] { new Item { Value1 = 52 }, new Item { Value1 = 317 } } }); + model.Axes.Add(new CategoryAxis { Position = CategoryAxisPosition() }); + model.Axes.Add(new LinearAxis { Position = ValueAxisPosition(), MinimumPadding = 0 }); + return model; + } + + [Example("Defined by Items")] + public static PlotModel DefinedByItems() + { + var model = new PlotModel { Title = "Defined by Items", Subtitle = "The items are added to the `Items` property." }; + + var s1 = new TSeries { Title = "Series 1" }; + s1.Items.AddRange(new[] { new TItem { Value = 25 }, new TItem { Value = 137 } }); + var s2 = new TSeries { Title = "Series 2" }; + s2.Items.AddRange(new[] { new TItem { Value = 52 }, new TItem { Value = 317 } }); + model.Series.Add(s1); + model.Series.Add(s2); + + model.Axes.Add(new CategoryAxis { Position = CategoryAxisPosition() }); + model.Axes.Add(new LinearAxis { Position = ValueAxisPosition(), MinimumPadding = 0 }); + return model; + } + + [Example("Empty category axis")] + public static PlotModel EmptyCategoryAxis() + { + var model = new PlotModel { Title = "Empty category axis" }; + + var s1 = new TSeries { Title = "Series 1" }; + s1.Items.Add(new TItem { Value = 25 }); + s1.Items.Add(new TItem { Value = 137 }); + s1.Items.Add(new TItem { Value = 18 }); + s1.Items.Add(new TItem { Value = 40 }); + var s2 = new TSeries { Title = "Series 2" }; + s2.Items.Add(new TItem { Value = -12 }); + s2.Items.Add(new TItem { Value = -14 }); + s2.Items.Add(new TItem { Value = -120 }); + s2.Items.Add(new TItem { Value = -26 }); + var categoryAxis = new CategoryAxis { Position = CategoryAxisPosition() }; + var valueAxis = new LinearAxis + { + Position = ValueAxisPosition(), + MinimumPadding = 0.06, + MaximumPadding = 0.06, + ExtraGridlines = new[] { 0.0 }, + ExtraGridlineStyle = LineStyle.Solid, + ExtraGridlineColor = OxyColors.Black, + ExtraGridlineThickness = 1 + }; + model.Series.Add(s1); + model.Series.Add(s2); + model.Axes.Add(categoryAxis); + model.Axes.Add(valueAxis); + return model; + } + + [Example("With negative values")] + public static PlotModel WithNegativeValue() + { + return CreateModelWithNegativeValues(false, "With negative values"); + } + + [Example("Stacked with negative values")] + public static PlotModel StackedWithNegativeValue() + { + return CreateModelWithNegativeValues(true, "Stacked with negative values"); + } + + [Example("Mixed with LineSeries")] + public static PlotModel MixedWithLineSeries() + { + var model = CreateSimpleModel(false, "Mixed with LineSeries"); + model.Title = "Mixed with LineSeries"; + var s1 = new LineSeries { Title = "LineSeries 1" }; + if (typeof(TSeries) == typeof(ColumnSeries)) + { + s1.Points.Add(new DataPoint(0, 25)); + s1.Points.Add(new DataPoint(1, 137)); + s1.Points.Add(new DataPoint(2, 18)); + s1.Points.Add(new DataPoint(3, 40)); + } + else + { + s1.Points.Add(new DataPoint(25, 0)); + s1.Points.Add(new DataPoint(137, 1)); + s1.Points.Add(new DataPoint(18, 2)); + s1.Points.Add(new DataPoint(40, 3)); + } + + model.Series.Add(s1); + return model; + } + + [Example("No axes defined")] + public static PlotModel NoAxes() + { + var model = CreateSimpleModel(false, "No axes defined"); + model.Axes.Clear(); // default axes will be generated + return model; + } + + [Example("Stacked and no axes defined")] + public static PlotModel StackedNoAxes() + { + var model = CreateSimpleModel(true, "Stacked and no axes defined"); + model.Axes.Clear(); // default axes will be generated + return model; + } + + [Example("Logarithmic axis")] + public static PlotModel LogAxis() + { + var model = new PlotModel + { + Title = "Logarithmic axis", + LegendPlacement = LegendPlacement.Outside, + LegendPosition = LegendPosition.BottomCenter, + LegendOrientation = LegendOrientation.Horizontal, + LegendBorderThickness = 0 + }; + + var s1 = new TSeries { Title = "Series 1", BaseValue = 0.1, StrokeColor = OxyColors.Black, StrokeThickness = 1 }; + s1.Items.Add(new TItem { Value = 25 }); + s1.Items.Add(new TItem { Value = 37 }); + s1.Items.Add(new TItem { Value = 18 }); + s1.Items.Add(new TItem { Value = 40 }); + + var categoryAxis = new CategoryAxis { Position = CategoryAxisPosition() }; + categoryAxis.Labels.Add("Category A"); + categoryAxis.Labels.Add("Category B"); + categoryAxis.Labels.Add("Category C"); + categoryAxis.Labels.Add("Category D"); + model.Series.Add(s1); + model.Axes.Add(categoryAxis); + model.Axes.Add(new LogarithmicAxis { Position = ValueAxisPosition(), Minimum = 0.1, MinimumPadding = 0, AbsoluteMinimum = 0 }); + return model; + } + + [Example("Logarithmic axis (not stacked)")] + public static PlotModel LogAxis2() + { + var model = new PlotModel { Title = "Logarithmic axis", LegendPlacement = LegendPlacement.Outside }; + + var items = new Collection + { + new Item {Label = "Apples", Value1 = 37, Value2 = 12, Value3 = 19}, + new Item {Label = "Pears", Value1 = 7, Value2 = 21, Value3 = 9}, + new Item {Label = "Bananas", Value1 = 23, Value2 = 2, Value3 = 29} + }; + + model.Series.Add(new TSeries { Title = "2009", ItemsSource = items, ValueField = "Value1" }); + model.Series.Add(new TSeries { Title = "2010", ItemsSource = items, ValueField = "Value2" }); + model.Series.Add(new TSeries { Title = "2011", ItemsSource = items, ValueField = "Value3" }); + + model.Axes.Add(new CategoryAxis { Position = CategoryAxisPosition(), ItemsSource = items, LabelField = "Label" }); + model.Axes.Add(new LogarithmicAxis { Position = ValueAxisPosition(), Minimum = 1 }); + return model; + } + + [Example("Logarithmic axis (stacked series)")] + public static PlotModel LogAxis3() + { + var model = LogAxis2(); + foreach (var s in model.Series.OfType()) + { + s.IsStacked = true; + } + + return model; + } + + private static PlotModel CreateSimpleModel(bool stacked, string title) + { + var model = new PlotModel + { + Title = title, + LegendPlacement = LegendPlacement.Outside, + LegendPosition = LegendPosition.BottomCenter, + LegendOrientation = LegendOrientation.Horizontal, + LegendBorderThickness = 0 + }; + + var s1 = new TSeries { Title = "Series 1", IsStacked = stacked, StrokeColor = OxyColors.Black, StrokeThickness = 1 }; + s1.Items.Add(new TItem { Value = 25 }); + s1.Items.Add(new TItem { Value = 137 }); + s1.Items.Add(new TItem { Value = 18 }); + s1.Items.Add(new TItem { Value = 40 }); + + var s2 = new TSeries { Title = "Series 2", IsStacked = stacked, StrokeColor = OxyColors.Black, StrokeThickness = 1 }; + s2.Items.Add(new TItem { Value = 12 }); + s2.Items.Add(new TItem { Value = 14 }); + s2.Items.Add(new TItem { Value = 120 }); + s2.Items.Add(new TItem { Value = 26 }); + + var categoryAxis = new CategoryAxis { Position = CategoryAxisPosition() }; + categoryAxis.Labels.Add("Category A"); + categoryAxis.Labels.Add("Category B"); + categoryAxis.Labels.Add("Category C"); + categoryAxis.Labels.Add("Category D"); + var valueAxis = new LinearAxis { Position = ValueAxisPosition(), MinimumPadding = 0, MaximumPadding = 0.06, AbsoluteMinimum = 0 }; + model.Series.Add(s1); + model.Series.Add(s2); + model.Axes.Add(categoryAxis); + model.Axes.Add(valueAxis); + return model; + } + + private static PlotModel CreateModelWithNegativeValues(bool stacked, string title) + { + var model = new PlotModel + { + Title = title, + LegendPlacement = LegendPlacement.Outside, + LegendPosition = LegendPosition.BottomCenter, + LegendOrientation = LegendOrientation.Horizontal, + LegendBorderThickness = 0 + }; + + var s1 = new TSeries { Title = "Series 1", IsStacked = stacked, StrokeColor = OxyColors.Black, StrokeThickness = 1 }; + s1.Items.Add(new TItem { Value = 25 }); + s1.Items.Add(new TItem { Value = 137 }); + s1.Items.Add(new TItem { Value = 18 }); + s1.Items.Add(new TItem { Value = 40 }); + + var s2 = new TSeries { Title = "Series 2", IsStacked = stacked, StrokeColor = OxyColors.Black, StrokeThickness = 1 }; + s2.Items.Add(new TItem { Value = -12 }); + s2.Items.Add(new TItem { Value = -14 }); + s2.Items.Add(new TItem { Value = -120 }); + s2.Items.Add(new TItem { Value = -26 }); + + var s3 = new TSeries { Title = "Series 3", IsStacked = stacked, StrokeColor = OxyColors.Black, StrokeThickness = 1 }; + s3.Items.Add(new TItem { Value = 21 }); + s3.Items.Add(new TItem { Value = 8 }); + s3.Items.Add(new TItem { Value = 48 }); + s3.Items.Add(new TItem { Value = 3 }); + + var s4 = new TSeries { Title = "Series 4", IsStacked = stacked, StrokeColor = OxyColors.Black, StrokeThickness = 1 }; + s4.Items.Add(new TItem { Value = -8 }); + s4.Items.Add(new TItem { Value = -21 }); + s4.Items.Add(new TItem { Value = -3 }); + s4.Items.Add(new TItem { Value = -48 }); + + var categoryAxis = new CategoryAxis { Position = CategoryAxisPosition() }; + categoryAxis.Labels.Add("Category A"); + categoryAxis.Labels.Add("Category B"); + categoryAxis.Labels.Add("Category C"); + categoryAxis.Labels.Add("Category D"); + + var valueAxis = new LinearAxis + { + Position = ValueAxisPosition(), + MinimumPadding = 0.06, + MaximumPadding = 0.06, + ExtraGridlines = new[] { 0.0 }, + ExtraGridlineStyle = LineStyle.Solid, + ExtraGridlineColor = OxyColors.Black, + ExtraGridlineThickness = 1 + }; + + model.Series.Add(s1); + model.Series.Add(s2); + model.Series.Add(s3); + model.Series.Add(s4); + model.Axes.Add(categoryAxis); + model.Axes.Add(valueAxis); + return model; + } + + public class Item + { + public string Label { get; set; } + public double Value1 { get; set; } + public double Value2 { get; set; } + public double Value3 { get; set; } + } + + public class HistogramBin + { + public string Label { get; set; } + public double Value { get; set; } + } + + [Example("Histogram (bins=5)")] + public static PlotModel Histogram3() + { + return CreateHistogram(100000, 5); + } + + [Example("Histogram (bins=20)")] + public static PlotModel Histogram20() + { + return CreateHistogram(100000, 20); + } + + [Example("Histogram (bins=200)")] + public static PlotModel Histogram200() + { + return CreateHistogram(100000, 200); + } + + public static PlotModel CreateHistogram(int n, int binCount) + { + var bins = new HistogramBin[binCount]; + for (int i = 0; i < bins.Length; i++) + { + bins[i] = new HistogramBin { Label = i.ToString() }; + } + + var r = new Random(31); + for (int i = 0; i < n; i++) + { + int value = r.Next(binCount); + bins[value].Value++; + } + + var temp = new PlotModel { Title = string.Format("Histogram (bins={0}, n={1})", binCount, n), Subtitle = "Pseudorandom numbers" }; + var series1 = new TSeries { ItemsSource = bins, ValueField = "Value" }; + if (binCount < 100) + { + series1.LabelFormatString = "{0}"; + } + + temp.Series.Add(series1); + + temp.Axes.Add(new CategoryAxis { Position = CategoryAxisPosition(), ItemsSource = bins, LabelField = "Label", GapWidth = 0 }); + temp.Axes.Add(new LinearAxis { Position = ValueAxisPosition(), MinimumPadding = 0, MaximumPadding = 0.1, AbsoluteMinimum = 0 }); + + return temp; + } + + [Example("Histogram (standard normal distribution)")] + public static PlotModel HistogramStandardNormalDistribution() + { + return CreateNormalDistributionHistogram(100000, 2000); + } + + public static PlotModel CreateNormalDistributionHistogram(int n, int binCount) + { + var bins = new HistogramBin[binCount]; + double min = -10; + double max = 10; + for (int i = 0; i < bins.Length; i++) + { + var v = min + (max - min) * i / (bins.Length - 1); + bins[i] = new HistogramBin { Label = v.ToString("0.0") }; + } + + var r = new Random(31); + for (int i = 0; i < n; i++) + { + // http://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform + var u1 = r.NextDouble(); + var u2 = r.NextDouble(); + var v = Math.Sqrt(-2 * Math.Log(u1)) * Math.Cos(2 * Math.PI * u2); + + int bin = (int)Math.Round((v - min) / (max - min) * (bins.Length - 1)); + if (bin >= 0 && bin < bins.Length) + { + bins[bin].Value++; + } + } + + var temp = new PlotModel { Title = string.Format("Histogram (bins={0}, n={1})", binCount, n), Subtitle = "Standard normal distribution by Box-Muller transform" }; + var series1 = new TSeries { ItemsSource = bins, ValueField = "Value" }; + temp.Series.Add(series1); + + var categoryAxis = new CategoryAxis + { + Position = CategoryAxisPosition(), + GapWidth = 0 + }; + + categoryAxis.Labels.AddRange(bins.Select(b => b.Label)); + categoryAxis.IsAxisVisible = false; + temp.Axes.Add(categoryAxis); + + // todo: link category and linear axis + temp.Axes.Add(new LinearAxis { Position = CategoryAxisPosition(), Minimum = min, Maximum = max }); + + temp.Axes.Add(new LinearAxis { Position = ValueAxisPosition(), MinimumPadding = 0, AbsoluteMinimum = 0 }); + + return temp; + } + + private static AxisPosition CategoryAxisPosition() + { + if (typeof(TSeries) == typeof(ColumnSeries)) + { + return AxisPosition.Bottom; + } + + return AxisPosition.Left; + } + + private static AxisPosition ValueAxisPosition() + { + if (typeof(TSeries) == typeof(ColumnSeries)) + { + return AxisPosition.Left; + } + + return AxisPosition.Bottom; + } + + [Example("Different colors within the same series")] + public static PlotModel DifferentColors() + { + var model = new PlotModel { Title = "Different colors within the same series" }; + var series = new TSeries { Title = "Series 1" }; + series.Items.Add(new TItem { Value = 1, Color = OxyColors.Red }); + series.Items.Add(new TItem { Value = 2, Color = OxyColors.Green }); + series.Items.Add(new TItem { Value = 1, Color = OxyColors.Blue }); + + var categoryAxis = new CategoryAxis + { + Title = "Category", + Position = CategoryAxisPosition() + }; + categoryAxis.Labels.AddRange(new[] { "A", "B", "C" }); + model.Axes.Add(categoryAxis); + + model.Series.Add(series); + return model; + } + + [Example("Different stacking groups")] + public static PlotModel StackingGroups() + { + var model = new PlotModel { Title = "Stacking groups" }; + var series = new TSeries { Title = "Series 1", StackGroup = "1", IsStacked = true }; + series.Items.Add(new TItem { Value = 1 }); + series.Items.Add(new TItem { Value = 2 }); + model.Series.Add(series); + + var series2 = new TSeries { Title = "Series 2", StackGroup = "2", IsStacked = true }; + series2.Items.Add(new TItem { Value = 2 }); + series2.Items.Add(new TItem { Value = 1 }); + model.Series.Add(series2); + + var series3 = new TSeries { Title = "Series 3", StackGroup = "1", IsStacked = true }; + series3.Items.Add(new TItem { Value = 3 }); + series3.Items.Add(new TItem { Value = 1 }); + model.Series.Add(series3); + + var categoryAxis = new CategoryAxis + { + Title = "Category", + Position = CategoryAxisPosition() + }; + categoryAxis.Labels.AddRange(new[] { "A", "B" }); + model.Axes.Add(categoryAxis); + return model; + } + + [Example("Different widths")] + public static PlotModel DifferentWidths() + { + var model = new PlotModel { Title = "Different widths", Subtitle = "Series1=0.6 and Series2=0.3" }; + var series1 = new TSeries { Title = "Series 1" }; + SetBarWidth(series1, 0.6); + series1.Items.Add(new TItem { Value = 1 }); + series1.Items.Add(new TItem { Value = 2 }); + model.Series.Add(series1); + + var series2 = new TSeries { Title = "Series 2" }; + SetBarWidth(series2, 0.3); + series2.Items.Add(new TItem { Value = 3 }); + series2.Items.Add(new TItem { Value = 1 }); + model.Series.Add(series2); + + var categoryAxis = new CategoryAxis + { + Title = "Category", + Position = CategoryAxisPosition() + }; + categoryAxis.Labels.AddRange(new[] { "A", "B" }); + model.Axes.Add(categoryAxis); + + return model; + } + + private static void SetBarWidth(TSeries series, double width) + { + if (typeof(TSeries) == typeof(ColumnSeries)) + { + (series as ColumnSeries).ColumnWidth = width; + } + else + { + (series as BarSeries).BarWidth = width; + } + } + + [Example("Different widths (stacked)")] + public static PlotModel DifferentWidthsStacked() + { + var model = new PlotModel { Title = "Different widths (stacked)" }; + var series1 = new TSeries { Title = "Series 1", IsStacked = true }; + SetBarWidth(series1, 0.6); + series1.Items.Add(new TItem { Value = 1 }); + series1.Items.Add(new TItem { Value = 2 }); + model.Series.Add(series1); + + var series2 = new TSeries { Title = "Series 2", IsStacked = true }; + SetBarWidth(series2, 0.3); + series2.Items.Add(new TItem { Value = 3 }); + series2.Items.Add(new TItem { Value = 1 }); + model.Series.Add(series2); + + var categoryAxis = new CategoryAxis + { + Title = "Category", + Position = CategoryAxisPosition() + }; + categoryAxis.Labels.AddRange(new[] { "A", "B" }); + model.Axes.Add(categoryAxis); + + return model; + } + + [Example("Invalid values")] + public static PlotModel InvalidValues() + { + var model = new PlotModel { Title = "Invalid values", Subtitle = "Series 1 contains a NaN value for category B." }; + var series1 = new TSeries { Title = "Series 1" }; + series1.Items.Add(new TItem { Value = 1 }); + series1.Items.Add(new TItem { Value = double.NaN }); + model.Series.Add(series1); + + var series2 = new TSeries { Title = "Series 2" }; + series2.Items.Add(new TItem { Value = 3 }); + series2.Items.Add(new TItem { Value = 1 }); + model.Series.Add(series2); + + var categoryAxis = new CategoryAxis + { + Title = "Category", + Position = CategoryAxisPosition() + }; + categoryAxis.Labels.AddRange(new[] { "A", "B" }); + model.Axes.Add(categoryAxis); + + return model; + } + + [Example("Missing values")] + public static PlotModel MissingValues() + { + var model = new PlotModel { Title = "Missing values", Subtitle = "Series 1 contains only one item with CategoryIndex = 1" }; + var series1 = new TSeries { Title = "Series 1" }; + series1.Items.Add(new TItem { Value = 1, CategoryIndex = 1 }); + model.Series.Add(series1); + + var series2 = new TSeries { Title = "Series 2" }; + series2.Items.Add(new TItem { Value = 3 }); + series2.Items.Add(new TItem { Value = 1.2 }); + model.Series.Add(series2); + + var categoryAxis = new CategoryAxis + { + Title = "Category", + Position = CategoryAxisPosition() + }; + categoryAxis.Labels.AddRange(new[] { "A", "B" }); + model.Axes.Add(categoryAxis); + + return model; + } + + [Example("CategoryIndex")] + public static PlotModel CategoryIndex() + { + var model = new PlotModel { Title = "CategoryIndex", Subtitle = "Setting CategoryIndex = 0 for both items in the series." }; + var series = new TSeries { Title = "Series 1", StrokeThickness = 1 }; + series.Items.Add(new TItem { Value = 1, CategoryIndex = 0 }); + series.Items.Add(new TItem { Value = 2, CategoryIndex = 0 }); + model.Series.Add(series); + + var categoryAxis = new CategoryAxis + { + Title = "Category", + Position = CategoryAxisPosition() + }; + categoryAxis.Labels.AddRange(new[] { "A", "B" }); + model.Axes.Add(categoryAxis); + + return model; + } + + [Example("CategoryIndex (stacked)")] + public static PlotModel CategoryIndexStacked() + { + var model = new PlotModel { Title = "CategoryIndex (stacked)", Subtitle = "Setting CategoryIndex = 0 for both items in the series." }; + var series = new TSeries { Title = "Series 1", IsStacked = true, StrokeThickness = 1 }; + series.Items.Add(new TItem { Value = 1, CategoryIndex = 0 }); + series.Items.Add(new TItem { Value = 2, CategoryIndex = 0 }); + model.Series.Add(series); + + var categoryAxis = new CategoryAxis + { + Title = "Category", + Position = CategoryAxisPosition() + }; + categoryAxis.Labels.AddRange(new[] { "A", "B" }); + model.Axes.Add(categoryAxis); + + return model; + } + + [Example("BaseValue")] + public static PlotModel BaseValue() + { + var model = new PlotModel { Title = "BaseValue", Subtitle = "BaseValue = -1" }; + var series1 = new TSeries { Title = "Series 1", BaseValue = -1 }; + series1.Items.Add(new TItem { Value = 1 }); + series1.Items.Add(new TItem { Value = 2 }); + model.Series.Add(series1); + var series2 = new TSeries { Title = "Series 2", BaseValue = -1 }; + series2.Items.Add(new TItem { Value = 4 }); + series2.Items.Add(new TItem { Value = 7 }); + model.Series.Add(series2); + + var categoryAxis = new CategoryAxis + { + Title = "Category", + Position = CategoryAxisPosition() + }; + categoryAxis.Labels.AddRange(new[] { "A", "B" }); + model.Axes.Add(categoryAxis); + + return model; + } + + [Example("BaseValue (stacked)")] + public static PlotModel BaseValueStacked() + { + var model = new PlotModel { Title = "BaseValue (stacked)", Subtitle = "BaseValue = -1" }; + var series1 = new TSeries { Title = "Series 1", IsStacked = true, BaseValue = -1 }; + series1.Items.Add(new TItem { Value = 1 }); + series1.Items.Add(new TItem { Value = 2 }); + model.Series.Add(series1); + var series2 = new TSeries { Title = "Series 2", IsStacked = true, BaseValue = -1 }; + series2.Items.Add(new TItem { Value = 4 }); + series2.Items.Add(new TItem { Value = 7 }); + model.Series.Add(series2); + + var categoryAxis = new CategoryAxis + { + Title = "Category", + Position = CategoryAxisPosition() + }; + categoryAxis.Labels.AddRange(new[] { "A", "B" }); + model.Axes.Add(categoryAxis); + return model; + } + + [Example("GapWidth 0%")] + public static PlotModel GapWidth0() + { + return CreateGapWidthModel(0, "GapWidth 0%"); + } + + [Example("GapWidth 100% (default)")] + public static PlotModel GapWidth100() + { + return CreateGapWidthModel(1, "GapWidth 100% (default)"); + } + + [Example("GapWidth 200%")] + public static PlotModel GapWidth200() + { + return CreateGapWidthModel(2, "GapWidth 200%"); + } + + private static PlotModel CreateGapWidthModel(double gapWidth, string title) + { + var model = CreateSimpleModel(false, title); + ((CategoryAxis)model.Axes[0]).GapWidth = gapWidth; + return model; + } + + // [Example("All in one")] + public static PlotModel AllInOne() + { + var model = new PlotModel + { + Title = "All in one", + LegendPlacement = LegendPlacement.Outside, + LegendPosition = LegendPosition.BottomCenter, + LegendOrientation = LegendOrientation.Horizontal, + LegendBorderThickness = 0 + }; + + var categoryAxis = new CategoryAxis { Position = CategoryAxisPosition(), GapWidth = 0.01 }; + categoryAxis.Labels.Add("Category A"); + categoryAxis.Labels.Add("Category B"); + categoryAxis.Labels.Add("Category C"); + categoryAxis.Labels.Add("Category D"); + var valueAxis = new LinearAxis + { + Position = ValueAxisPosition(), + MinimumPadding = 0.06, + MaximumPadding = 0.06, + ExtraGridlines = new[] { 0.0 }, + ExtraGridlineStyle = LineStyle.Solid, + ExtraGridlineColor = OxyColors.Black, + ExtraGridlineThickness = 1 + }; + + var categoryA = 0; + var categoryB = 1; + var categoryC = 2; + var categoryD = 3; + + var s1 = new TSeries { Title = "Series 1", IsStacked = true, StrokeColor = OxyColors.Black, StrokeThickness = 1, StackGroup = "3" }; + s1.Items.Add(new TItem { Value = 25 }); + s1.Items.Add(new TItem { Value = 137 }); + s1.Items.Add(new TItem { Value = 18 }); + s1.Items.Add(new TItem { Value = 40 }); + var s2 = new TSeries { Title = "Series 2", IsStacked = true, StrokeColor = OxyColors.Black, StrokeThickness = 1, StackGroup = "3" }; + s2.Items.Add(new TItem { Value = -12 }); + s2.Items.Add(new TItem { Value = -14 }); + s2.Items.Add(new TItem { Value = -120 }); + s2.Items.Add(new TItem { Value = -26 }); + + var s3 = new TSeries { Title = "Series 3", IsStacked = true, StrokeColor = OxyColors.Black, StrokeThickness = 1, StackGroup = "5" }; + s3.Items.Add(new TItem { Value = 21 }); + s3.Items.Add(new TItem { Value = 8 }); + s3.Items.Add(new TItem { Value = 48 }); + s3.Items.Add(new TItem { Value = 3 }); + var s4 = new TSeries { Title = "Series 4", IsStacked = true, StrokeColor = OxyColors.Black, StrokeThickness = 1, StackGroup = "5", LabelFormatString = "{0:0}", LabelPlacement = LabelPlacement.Middle }; + s4.Items.Add(new TItem { Value = -8, CategoryIndex = categoryA }); + s4.Items.Add(new TItem { Value = -8, CategoryIndex = categoryA }); + s4.Items.Add(new TItem { Value = -8, CategoryIndex = categoryA }); + s4.Items.Add(new TItem { Value = -21, CategoryIndex = categoryB }); + s4.Items.Add(new TItem { Value = -3, CategoryIndex = categoryC }); + s4.Items.Add(new TItem { Value = -48, CategoryIndex = categoryD }); + s4.Items.Add(new TItem { Value = 8, CategoryIndex = categoryA }); + s4.Items.Add(new TItem { Value = 21, CategoryIndex = categoryB }); + s4.Items.Add(new TItem { Value = 3, CategoryIndex = categoryC }); + s4.Items.Add(new TItem { Value = 48, CategoryIndex = categoryD }); + + var s5 = new TSeries { Title = "Series 5", IsStacked = false, StrokeColor = OxyColors.Black, StrokeThickness = 1 }; + s5.Items.Add(new TItem { Value = 17, CategoryIndex = categoryA }); + s5.Items.Add(new TItem { Value = 179, CategoryIndex = categoryB }); + s5.Items.Add(new TItem { Value = 45, CategoryIndex = categoryC }); + s5.Items.Add(new TItem { Value = 65, CategoryIndex = categoryD }); + s5.Items.Add(new TItem { Value = 97, CategoryIndex = categoryA }); + s5.Items.Add(new TItem { Value = 21, CategoryIndex = categoryD }); + + var s6 = new TSeries { Title = "Series 6", IsStacked = false, StrokeColor = OxyColors.Black, StrokeThickness = 1, LabelFormatString = "{0:0}", LabelPlacement = LabelPlacement.Base }; + s6.Items.Add(new TItem { Value = 7 }); + s6.Items.Add(new TItem { Value = 54 }); + s6.Items.Add(new TItem { Value = 68 }); + s6.Items.Add(new TItem { Value = 12 }); + + model.Series.Add(s1); + model.Series.Add(s2); + model.Series.Add(s3); + model.Series.Add(s4); + model.Series.Add(s5); + model.Series.Add(s6); + model.Axes.Add(categoryAxis); + model.Axes.Add(valueAxis); + return model; + } + } +} diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Series/BarSeriesExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Series/BarSeriesExamples.cs new file mode 100644 index 0000000..69535d4 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Series/BarSeriesExamples.cs @@ -0,0 +1,15 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using OxyPlot.Series; + + [Examples("BarSeries"), Tags("Series")] + public class BarSeriesExamples : BarAndColumnSeriesExamples + { + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Series/BoxPlotSeriesExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Series/BoxPlotSeriesExamples.cs new file mode 100644 index 0000000..c44ee68 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Series/BoxPlotSeriesExamples.cs @@ -0,0 +1,193 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Gets the median. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + using System.Collections.Generic; + using System.Linq; + + using OxyPlot; + using OxyPlot.Annotations; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("BoxPlotSeries"), Tags("Series")] + public class BoxPlotSeriesExamples + { + [Example("BoxPlot")] + public static PlotModel BoxPlot() + { + const int boxes = 10; + + var model = new PlotModel { Title = string.Format("BoxPlot (n={0})", boxes), LegendPlacement = LegendPlacement.Outside }; + + var s1 = new BoxPlotSeries + { + Title = "BoxPlotSeries", + BoxWidth = 0.3 + }; + + var random = new Random(31); + for (var i = 0; i < boxes; i++) + { + double x = i; + var points = 5 + random.Next(15); + var values = new List(); + for (var j = 0; j < points; j++) + { + values.Add(random.Next(0, 20)); + } + + values.Sort(); + var median = GetMedian(values); + var mean = values.Average(); + int r = values.Count % 2; + double firstQuartil = GetMedian(values.Take((values.Count + r) / 2)); + double thirdQuartil = GetMedian(values.Skip((values.Count - r) / 2)); + + var iqr = thirdQuartil - firstQuartil; + var step = iqr * 1.5; + var upperWhisker = thirdQuartil + step; + upperWhisker = values.Where(v => v <= upperWhisker).Max(); + var lowerWhisker = firstQuartil - step; + lowerWhisker = values.Where(v => v >= lowerWhisker).Min(); + + var outliers = new[] { upperWhisker + random.Next(1, 10), lowerWhisker - random.Next(1, 10) }; + + s1.Items.Add(new BoxPlotItem(x, lowerWhisker, firstQuartil, median, thirdQuartil, upperWhisker) { Mean = mean, Outliers = outliers }); + } + + model.Series.Add(s1); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, MinimumPadding = 0.1, MaximumPadding = 0.1 }); + return model; + } + + /// + /// Gets the median. + /// + /// The values. + /// + private static double GetMedian(IEnumerable values) + { + var sortedInterval = new List(values); + sortedInterval.Sort(); + var count = sortedInterval.Count; + if (count % 2 == 1) + { + return sortedInterval[(count - 1) / 2]; + } + + return 0.5 * sortedInterval[count / 2] + 0.5 * sortedInterval[(count / 2) - 1]; + } + + [Example("BoxPlot (minimal data/ink ratio)")] + public static PlotModel BoxPlot2() + { + var model = BoxPlot(); + var boxPlotSeries = (BoxPlotSeries)model.Series[0]; + boxPlotSeries.ShowMedianAsDot = true; + boxPlotSeries.OutlierType = MarkerType.Cross; + boxPlotSeries.Fill = OxyColors.Black; + boxPlotSeries.ShowBox = false; + boxPlotSeries.WhiskerWidth = 0; + return model; + } + + [Example("BoxPlot (dashed line)")] + public static PlotModel BoxPlot3() + { + var model = BoxPlot(); + var boxPlotSeries = (BoxPlotSeries)model.Series[0]; + boxPlotSeries.LineStyle = LineStyle.Dash; + return model; + } + + [Example("Outlier type = Cross")] + public static PlotModel OutlierTypeCross() + { + var model = BoxPlot(); + var boxPlotSeries = (BoxPlotSeries)model.Series[0]; + boxPlotSeries.OutlierType = MarkerType.Cross; + return model; + } + + [Example("Outlier type = Custom")] + public static PlotModel OutlierTypeCustom() + { + var model = BoxPlot(); + var boxPlotSeries = (BoxPlotSeries)model.Series[0]; + boxPlotSeries.OutlierType = MarkerType.Custom; + boxPlotSeries.OutlierOutline = new[] { new ScreenPoint(-1, -1), new ScreenPoint(1, 1), new ScreenPoint(-1, 1), new ScreenPoint(1, -1) }; + return model; + } + + [Example("Michelson-Morley experiment")] + public static PlotModel MichelsonMorleyExperiment() + { + //// http://www.gutenberg.org/files/11753/11753-h/11753-h.htm + //// http://en.wikipedia.org/wiki/Michelson%E2%80%93Morley_experiment + //// http://stat.ethz.ch/R-manual/R-devel/library/datasets/html/morley.html + + var model = new PlotModel(); + + var boxPlotSeries = new BoxPlotSeries + { + Title = "Results", + Stroke = OxyColors.Black, + StrokeThickness = 1, + OutlierSize = 2, + BoxWidth = 0.4 + }; + + // note: approximated data values (not the original values) + boxPlotSeries.Items.Add(new BoxPlotItem(0, 740, 850, 945, 980, 1070) { Outliers = new[] { 650.0 }}); + boxPlotSeries.Items.Add(new BoxPlotItem(1, 750, 805, 845, 890, 970) { Outliers = new double[] { }}); + boxPlotSeries.Items.Add(new BoxPlotItem(2, 845, 847, 855, 880, 910) { Outliers = new[] { 640.0, 950, 970 }}); + boxPlotSeries.Items.Add(new BoxPlotItem(3, 720, 760, 820, 870, 910) { Outliers = new double[] { }}); + boxPlotSeries.Items.Add(new BoxPlotItem(4, 730, 805, 807, 870, 950) { Outliers = new double[] { }}); + model.Series.Add(boxPlotSeries); + model.Annotations.Add(new LineAnnotation { Type = LineAnnotationType.Horizontal, LineStyle = LineStyle.Solid, Y = 792.458, Text = "true speed" }); + var categoryAxis = new CategoryAxis + { + Title = "Experiment No.", + }; + categoryAxis.Labels.AddRange(new[] { "1", "2", "3", "4", "5" }); + model.Axes.Add(categoryAxis); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = "Speed of light (km/s minus 299,000)", MajorStep = 100, MinorStep = 100 }); + return model; + } + + [Example("BoxPlot (DateTime axis)")] + public static PlotModel BoxPlotSeries_DateTimeAxis() + { + var m = new PlotModel(); + var x0 = DateTimeAxis.ToDouble(new DateTime(2013, 05, 04)); + m.Axes.Add(new DateTimeAxis + { + Position = AxisPosition.Bottom, + Minimum = x0 - 0.9, + Maximum = x0 + 1.9, + IntervalType = DateTimeIntervalType.Days, + MajorStep = 1, + MinorStep = 1, + StringFormat = "yyyy-MM-dd" + }); + var boxPlotSeries = new BoxPlotSeries + { + TrackerFormatString = "X: {1:yyyy-MM-dd}\nUpper Whisker: {2:0.00}\nThird Quartil: {3:0.00}\nMedian: {4:0.00}\nFirst Quartil: {5:0.00}\nLower Whisker: {6:0.00}\nMean: {7:0.00}" + }; + boxPlotSeries.Items.Add(new BoxPlotItem(x0, 10, 14, 16, 20, 22) { Mean = 17, Outliers = new[] { 23.5 }}); + boxPlotSeries.Items.Add(new BoxPlotItem(x0 + 1, 11, 13, 14, 15, 18) { Outliers = new[] { 23.4 }}); + m.Series.Add(boxPlotSeries); + return m; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Series/ColumnSeriesExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Series/ColumnSeriesExamples.cs new file mode 100644 index 0000000..9186417 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Series/ColumnSeriesExamples.cs @@ -0,0 +1,15 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using OxyPlot.Series; + + [Examples("ColumnSeries"), Tags("Series")] + public class ColumnSeriesExamples : BarAndColumnSeriesExamples + { + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Series/ContourSeriesExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Series/ContourSeriesExamples.cs new file mode 100644 index 0000000..6d37ea3 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Series/ContourSeriesExamples.cs @@ -0,0 +1,71 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("ContourSeries"), Tags("Series")] + public class ContourSeriesExamples + { + private static Func peaks = (x, y) => + 3 * (1 - x) * (1 - x) * Math.Exp(-(x * x) - (y + 1) * (y + 1)) + - 10 * (x / 5 - x * x * x - y * y * y * y * y) * Math.Exp(-x * x - y * y) + - 1.0 / 3 * Math.Exp(-(x + 1) * (x + 1) - y * y); + + [Example("Peaks")] + public static PlotModel Peaks() + { + var model = new PlotModel { Title = "Peaks" }; + var cs = new ContourSeries + { + ColumnCoordinates = ArrayBuilder.CreateVector(-3, 3, 0.05), + RowCoordinates = ArrayBuilder.CreateVector(-3.1, 3.1, 0.05) + }; + cs.Data = ArrayBuilder.Evaluate(peaks, cs.ColumnCoordinates, cs.RowCoordinates); + model.Subtitle = cs.Data.GetLength(0) + "×" + cs.Data.GetLength(1); + model.Series.Add(cs); + return model; + } + + [Example("Peaks (different contour colors)")] + public static PlotModel PeaksWithColors() + { + var model = new PlotModel { Title = "Peaks" }; + var cs = new ContourSeries + { + ColumnCoordinates = ArrayBuilder.CreateVector(-3, 3, 0.05), + RowCoordinates = ArrayBuilder.CreateVector(-3.1, 3.1, 0.05), + ContourColors = new[] { OxyColors.SeaGreen, OxyColors.RoyalBlue, OxyColors.IndianRed } + }; + cs.Data = ArrayBuilder.Evaluate(peaks, cs.ColumnCoordinates, cs.RowCoordinates); + model.Subtitle = cs.Data.GetLength(0) + "×" + cs.Data.GetLength(1); + model.Series.Add(cs); + return model; + } + + [Example("Peaks (wide array)")] + public static PlotModel WideArrayPeaks() + { + var model = new PlotModel { Title = "Peaks" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = -3.16262, Maximum = 3.162 }); + var cs = new ContourSeries + { + ColumnCoordinates = ArrayBuilder.CreateVector(-3, 3, 0.05), + RowCoordinates = ArrayBuilder.CreateVector(-1, 1, 0.05) + }; + cs.Data = ArrayBuilder.Evaluate(peaks, cs.ColumnCoordinates, cs.RowCoordinates); + model.Subtitle = cs.Data.GetLength(0) + "×" + cs.Data.GetLength(1); + model.Series.Add(cs); + return model; + } + + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Series/ErrorColumnSeriesExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Series/ErrorColumnSeriesExamples.cs new file mode 100644 index 0000000..bad26d7 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Series/ErrorColumnSeriesExamples.cs @@ -0,0 +1,77 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("ErrorColumnSeries"), Tags("Series")] + public class ErrorColumnSeriesExamples + { + [Example("ErrorColumnSeries")] + public static PlotModel GetErrorColumnSeries() + { + var model = new PlotModel + { + Title = "ErrorColumnSeries", + LegendPlacement = LegendPlacement.Outside, + LegendPosition = LegendPosition.BottomCenter, + LegendOrientation = LegendOrientation.Horizontal, + LegendBorderThickness = 0 + }; + + var s1 = new ErrorColumnSeries { Title = "Series 1", IsStacked = false, StrokeColor = OxyColors.Black, StrokeThickness = 1 }; + s1.Items.Add(new ErrorColumnItem { Value = 25, Error = 2 }); + s1.Items.Add(new ErrorColumnItem { Value = 137, Error = 25 }); + s1.Items.Add(new ErrorColumnItem { Value = 18, Error = 4 }); + s1.Items.Add(new ErrorColumnItem { Value = 40, Error = 29 }); + + var s2 = new ErrorColumnSeries { Title = "Series 2", IsStacked = false, StrokeColor = OxyColors.Black, StrokeThickness = 1 }; + s2.Items.Add(new ErrorColumnItem { Value = 35, Error = 20 }); + s2.Items.Add(new ErrorColumnItem { Value = 17, Error = 7 }); + s2.Items.Add(new ErrorColumnItem { Value = 118, Error = 44 }); + s2.Items.Add(new ErrorColumnItem { Value = 49, Error = 29 }); + + var categoryAxis = new CategoryAxis { Position = AxisPosition.Bottom }; + categoryAxis.Labels.Add("Category A"); + categoryAxis.Labels.Add("Category B"); + categoryAxis.Labels.Add("Category C"); + categoryAxis.Labels.Add("Category D"); + + var valueAxis = new LinearAxis { Position = AxisPosition.Left, MinimumPadding = 0, MaximumPadding = 0.06, AbsoluteMinimum = 0 }; + model.Series.Add(s1); + model.Series.Add(s2); + model.Axes.Add(categoryAxis); + model.Axes.Add(valueAxis); + + return model; + } + + [Example("ErrorColumnSeries (thick error lines)")] + public static PlotModel GetErrorColumnSeriesStacked() + { + var model = GetErrorColumnSeries(); + foreach (ErrorColumnSeries s in model.Series) + { + s.ErrorWidth = 0; + s.ErrorStrokeThickness = 4; + } + + return model; + } + + //[Example("ErrorColumnSeries (stacked)")] + //public static PlotModel GetErrorColumnSeriesStacked() + //{ + // var model = GetErrorColumnSeries(); + // foreach (ErrorColumnSeries s in model.Series) + // s.IsStacked = true; + // return model; + //} + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Series/FinancialSeries/CandleStickAndVolumeSeriesExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Series/FinancialSeries/CandleStickAndVolumeSeriesExamples.cs new file mode 100644 index 0000000..74e0a84 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Series/FinancialSeries/CandleStickAndVolumeSeriesExamples.cs @@ -0,0 +1,235 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + using System.Linq; + + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("CandleStickAndVolumeSeries")] + [Tags("Series")] + public static class CandleStickAndVolumeSeriesExamples + { + [Example("Candles + Volume (combined volume), adjusting Y-axis")] + public static Example CombinedVolume_Adjusting() + { + return CreateCandleStickAndVolumeSeriesExample( + "Candles + Volume (combined volume)", + VolumeStyle.Combined, + naturalY: false, + naturalV: false); + } + + [Example("Candles + Volume (combined volume), natural Y-axis")] + public static Example CombinedVolume_Natural() + { + return CreateCandleStickAndVolumeSeriesExample( + "Candles + Volume (combined volume)", + VolumeStyle.Combined, + naturalY: true, + naturalV: true); + } + + [Example("Candles + Volume (stacked volume), adjusting Y-axis")] + public static Example StackedVolume_Adjusting() + { + return CreateCandleStickAndVolumeSeriesExample( + "Candles + Volume (stacked volume)", + VolumeStyle.Stacked, + naturalY: false, + naturalV: false); + } + + [Example("Candles + Volume (stacked volume), natural Y-axis")] + public static Example StackedVolume_Natural() + { + return CreateCandleStickAndVolumeSeriesExample( + "Candles + Volume (stacked volume)", + VolumeStyle.Stacked, + naturalY: true, + naturalV: true); + } + + [Example("Candles + Volume (+/- volume), adjusting Y-axis")] + public static Example PosNegVolume_Adjusting() + { + return CreateCandleStickAndVolumeSeriesExample( + "Candles + Volume (+/- volume)", + VolumeStyle.PositiveNegative, + naturalY: false, + naturalV: false); + } + + [Example("Candles + Volume (+/- volume), natural Y-axis")] + public static Example PosNegVolume_Natural() + { + return CreateCandleStickAndVolumeSeriesExample( + "Candles + Volume (+/- volume)", + VolumeStyle.PositiveNegative, + naturalY: true, + naturalV: true); + } + + [Example("Candles + Volume (volume not shown), adjusting Y-axis")] + public static Example NoVolume_Adjusting() + { + return CreateCandleStickAndVolumeSeriesExample( + "Candles + Volume (volume not shown)", + VolumeStyle.None, + naturalY: false, + naturalV: false); + } + + [Example("Candles + Volume (volume not shown), natural Y-axis")] + public static Example NoVolume_Natural() + { + return CreateCandleStickAndVolumeSeriesExample( + "Candles + Volume (volume not shown)", + VolumeStyle.None, + naturalY: true, + naturalV: true); + } + + /// + /// Creates the candle stick and volume series example. + /// + /// The candle stick and volume series example. + /// Title. + /// Style. + /// N. + /// If set to true natural y. + /// If set to true natural v. + private static Example CreateCandleStickAndVolumeSeriesExample( + string title, + VolumeStyle style, + int n = 10000, + bool naturalY = false, + bool naturalV = false) + { + var pm = new PlotModel { Title = title }; + + var series = new CandleStickAndVolumeSeries + { + PositiveColor = OxyColors.DarkGreen, + NegativeColor = OxyColors.Red, + PositiveHollow = false, + NegativeHollow = false, + SeparatorColor = OxyColors.Gray, + SeparatorLineStyle = LineStyle.Dash, + VolumeStyle = style + }; + + // create bars + foreach (var bar in OhlcvItemGenerator.MRProcess(n)) + { + series.Append(bar); + } + + // create visible window + var Istart = n - 200; + var Iend = n - 120; + var Ymin = series.Items.Skip(Istart).Take(Iend - Istart + 1).Select(x => x.Low).Min(); + var Ymax = series.Items.Skip(Istart).Take(Iend - Istart + 1).Select(x => x.High).Max(); + var Xmin = series.Items[Istart].X; + var Xmax = series.Items[Iend].X; + + // setup axes + var timeAxis = new DateTimeAxis + { + Position = AxisPosition.Bottom, + Minimum = Xmin, + Maximum = Xmax + }; + var barAxis = new LinearAxis + { + Position = AxisPosition.Left, + Key = series.BarAxisKey, + StartPosition = 0.25, + EndPosition = 1.0, + Minimum = naturalY ? double.NaN : Ymin, + Maximum = naturalY ? double.NaN : Ymax + }; + var volAxis = new LinearAxis + { + Position = AxisPosition.Left, + Key = series.VolumeAxisKey, + StartPosition = 0.0, + EndPosition = 0.22, + Minimum = naturalV ? double.NaN : 0, + Maximum = naturalV ? double.NaN : 5000 + }; + + switch (style) + { + case VolumeStyle.None: + barAxis.Key = null; + barAxis.StartPosition = 0.0; + pm.Axes.Add(timeAxis); + pm.Axes.Add(barAxis); + break; + + case VolumeStyle.Combined: + case VolumeStyle.Stacked: + pm.Axes.Add(timeAxis); + pm.Axes.Add(barAxis); + pm.Axes.Add(volAxis); + break; + + case VolumeStyle.PositiveNegative: + volAxis.Minimum = naturalV ? double.NaN : -5000; + pm.Axes.Add(timeAxis); + pm.Axes.Add(barAxis); + pm.Axes.Add(volAxis); + break; + } + + pm.Series.Add(series); + + if (naturalY == false) + { + timeAxis.AxisChanged += (sender, e) => AdjustYExtent(series, timeAxis, barAxis); + } + + var controller = new PlotController(); + controller.UnbindAll(); + controller.BindMouseDown(OxyMouseButton.Left, PlotCommands.PanAt); + return new Example(pm, controller); + } + + /// + /// Adjusts the Y extent. + /// + /// Series. + /// Xaxis. + /// Yaxis. + private static void AdjustYExtent(CandleStickAndVolumeSeries series, DateTimeAxis xaxis, LinearAxis yaxis) + { + var xmin = xaxis.ActualMinimum; + var xmax = xaxis.ActualMaximum; + + var istart = series.FindByX(xmin); + var iend = series.FindByX(xmax, istart); + + var ymin = double.MaxValue; + var ymax = double.MinValue; + for (int i = istart; i <= iend; i++) + { + var bar = series.Items[i]; + ymin = Math.Min(ymin, bar.Low); + ymax = Math.Max(ymax, bar.High); + } + + var extent = ymax - ymin; + var margin = extent * 0.10; + + yaxis.Zoom(ymin - margin, ymax + margin); + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Series/FinancialSeries/CandleStickSeriesExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Series/FinancialSeries/CandleStickSeriesExamples.cs new file mode 100644 index 0000000..1b71af6 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Series/FinancialSeries/CandleStickSeriesExamples.cs @@ -0,0 +1,178 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + using System.Linq; + + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("CandleStickSeries"), Tags("Series")] + public static class CandleStickSeriesExamples + { + [Example("Large Data Set (wide window)")] + public static Example LargeDataSetWide() + { + var pm = new PlotModel { Title = "Large Data Set (wide window)" }; + + var timeSpanAxis1 = new DateTimeAxis { Position = AxisPosition.Bottom }; + pm.Axes.Add(timeSpanAxis1); + var linearAxis1 = new LinearAxis { Position = AxisPosition.Left }; + pm.Axes.Add(linearAxis1); + var n = 1000000; + var items = HighLowItemGenerator.MRProcess(n).ToArray(); + var series = new CandleStickSeries + { + Color = OxyColors.Black, + IncreasingColor = OxyColors.DarkGreen, + DecreasingColor = OxyColors.Red, + DataFieldX = "Time", + DataFieldHigh = "H", + DataFieldLow = "L", + DataFieldOpen = "O", + DataFieldClose = "C", + TrackerFormatString = + "High: {2:0.00}\nLow: {3:0.00}\nOpen: {4:0.00}\nClose: {5:0.00}", + ItemsSource = items + }; + + timeSpanAxis1.Minimum = items[n - 200].X; + timeSpanAxis1.Maximum = items[n - 130].X; + + linearAxis1.Minimum = items.Skip(n - 200).Take(70).Select(x => x.Low).Min(); + linearAxis1.Maximum = items.Skip(n - 200).Take(70).Select(x => x.High).Max(); + + pm.Series.Add(series); + + timeSpanAxis1.AxisChanged += (sender, e) => AdjustYExtent(series, timeSpanAxis1, linearAxis1); + + var controller = new PlotController(); + controller.UnbindAll(); + controller.BindMouseDown(OxyMouseButton.Left, PlotCommands.PanAt); + return new Example(pm, controller); + } + + [Example("Large Data Set (narrow window)")] + public static Example LargeDataSetNarrow() + { + var pm = new PlotModel { Title = "Large Data Set (narrow window)" }; + + var timeSpanAxis1 = new DateTimeAxis { Position = AxisPosition.Bottom }; + pm.Axes.Add(timeSpanAxis1); + var linearAxis1 = new LinearAxis { Position = AxisPosition.Left }; + pm.Axes.Add(linearAxis1); + var n = 1000000; + var items = HighLowItemGenerator.MRProcess(n).ToArray(); + var series = new CandleStickSeries + { + Color = OxyColors.Black, + IncreasingColor = OxyColors.DarkGreen, + DecreasingColor = OxyColors.Red, + TrackerFormatString = + "High: {2:0.00}\nLow: {3:0.00}\nOpen: {4:0.00}\nClose: {5:0.00}", + ItemsSource = items + }; + + + timeSpanAxis1.Minimum = items[0].X; + timeSpanAxis1.Maximum = items[29].X; + + linearAxis1.Minimum = items.Take(30).Select(x => x.Low).Min(); + linearAxis1.Maximum = items.Take(30).Select(x => x.High).Max(); + + pm.Series.Add(series); + + timeSpanAxis1.AxisChanged += (sender, e) => AdjustYExtent(series, timeSpanAxis1, linearAxis1); + + var controller = new PlotController(); + controller.UnbindAll(); + controller.BindMouseDown(OxyMouseButton.Left, PlotCommands.PanAt); + return new Example(pm, controller); + } + + [Example("Small Set")] + public static Example SmallDataSet() + { + var pm = new PlotModel { Title = "Small Data Set" }; + + var timeSpanAxis1 = new DateTimeAxis { Position = AxisPosition.Bottom }; + pm.Axes.Add(timeSpanAxis1); + var linearAxis1 = new LinearAxis { Position = AxisPosition.Left }; + pm.Axes.Add(linearAxis1); + var n = 100; + var items = HighLowItemGenerator.MRProcess(n).ToArray(); + var series = new CandleStickSeries + { + Color = OxyColors.Black, + IncreasingColor = OxyColors.DarkGreen, + DecreasingColor = OxyColors.Red, + DataFieldX = "X", + DataFieldHigh = "High", + DataFieldLow = "Low", + DataFieldOpen = "Open", + DataFieldClose = "Close", + TrackerFormatString = + "High: {2:0.00}\nLow: {3:0.00}\nOpen: {4:0.00}\nClose: {5:0.00}", + ItemsSource = items + }; + + pm.Series.Add(series); + + timeSpanAxis1.AxisChanged += (sender, e) => AdjustYExtent(series, timeSpanAxis1, linearAxis1); + + var controller = new PlotController(); + controller.UnbindAll(); + controller.BindMouseDown(OxyMouseButton.Left, PlotCommands.PanAt); + return new Example(pm, controller); + } + + [Example("Simple CandleStickSeries example")] + public static PlotModel SimpleExample() + { + var startTimeValue = DateTimeAxis.ToDouble(new DateTime(2016, 1, 1)); + var pm = new PlotModel { Title = "Simple CandleStickSeries example" }; + pm.Axes.Add(new DateTimeAxis { Position = AxisPosition.Bottom, Minimum = startTimeValue - 7, Maximum = startTimeValue + 7 }); + pm.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + var series = new CandleStickSeries(); + series.Items.Add(new HighLowItem(startTimeValue, 100, 80, 92, 94)); + series.Items.Add(new HighLowItem(startTimeValue + 1, 102, 77, 94, 93)); + pm.Series.Add(series); + return pm; + } + + /// + /// Adjusts the Y extent. + /// + /// Series. + /// Xaxis. + /// Yaxis. + private static void AdjustYExtent(CandleStickSeries series, DateTimeAxis xaxis, LinearAxis yaxis) + { + var xmin = xaxis.ActualMinimum; + var xmax = xaxis.ActualMaximum; + + var istart = series.FindByX(xmin); + var iend = series.FindByX(xmax, istart); + + var ymin = double.MaxValue; + var ymax = double.MinValue; + for (int i = istart; i <= iend; i++) + { + var bar = series.Items[i]; + ymin = Math.Min(ymin, bar.Low); + ymax = Math.Max(ymax, bar.High); + } + + var extent = ymax - ymin; + var margin = extent * 0.10; + + yaxis.Zoom(ymin - margin, ymax + margin); + } + } +} diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Series/FinancialSeries/HighLowItemGenerator.cs b/OxyPlot/Source/Examples/ExampleLibrary/Series/FinancialSeries/HighLowItemGenerator.cs new file mode 100644 index 0000000..be8dd43 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Series/FinancialSeries/HighLowItemGenerator.cs @@ -0,0 +1,197 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Creates realistic high/low items. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + using System.Collections.Generic; + + using OxyPlot.Axes; + using OxyPlot.Series; + + /// + /// Creates realistic high/low items. + /// + public static class HighLowItemGenerator + { + /// + /// The random number generator. + /// + private static readonly Random Rand = new Random(); + + /// + /// Creates bars governed by a MR process + /// + /// The process. + /// N. + /// X0. + /// Csigma. + /// Esigma. + /// Kappa. + public static IEnumerable MRProcess( + int n, + double x0 = 100.0, + double csigma = 0.50, + double esigma = 0.70, + double kappa = 0.01) + { + double x = x0; + + var baseT = DateTime.UtcNow; + for (int ti = 0; ti < n; ti++) + { + var dx_c = -kappa * (x - x0) + RandomNormal(0, csigma); + var dx_1 = -kappa * (x - x0) + RandomNormal(0, esigma); + var dx_2 = -kappa * (x - x0) + RandomNormal(0, esigma); + + var open = x; + var close = x = x + dx_c; + var low = Min(open, close, open + dx_1, open + dx_2); + var high = Max(open, close, open + dx_1, open + dx_2); + + var nowT = baseT.AddSeconds(ti); + var t = DateTimeAxis.ToDouble(nowT); + yield return new HighLowItem(t, high, low, open, close); + } + } + + /// + /// Finds the minimum of the specified a, b, c and d. + /// + /// A. + /// B. + /// C. + /// D. + /// The minimum. + private static double Min(double a, double b, double c, double d) + { + return Math.Min(a, Math.Min(b, Math.Min(c, d))); + } + + /// + /// Finds the maximum of the specified a, b, c and d. + /// + /// A. + /// B. + /// C. + /// D. + /// The maximum. + private static double Max(double a, double b, double c, double d) + { + return Math.Max(a, Math.Max(b, Math.Max(c, d))); + } + + /// + /// Get random normal + /// + /// Mu. + /// Sigma. + private static double RandomNormal(double mu, double sigma) + { + return InverseCumNormal(Rand.NextDouble(), mu, sigma); + } + + /// + /// Fast approximation for inverse cum normal + /// + /// probability + /// Mean + /// std dev + private static double InverseCumNormal(double p, double mu, double sigma) + { + const double A1 = -3.969683028665376e+01; + const double A2 = 2.209460984245205e+02; + const double A3 = -2.759285104469687e+02; + const double A4 = 1.383577518672690e+02; + const double A5 = -3.066479806614716e+01; + const double A6 = 2.506628277459239e+00; + + const double B1 = -5.447609879822406e+01; + const double B2 = 1.615858368580409e+02; + const double B3 = -1.556989798598866e+02; + const double B4 = 6.680131188771972e+01; + const double B5 = -1.328068155288572e+01; + + const double C1 = -7.784894002430293e-03; + const double C2 = -3.223964580411365e-01; + const double C3 = -2.400758277161838e+00; + const double C4 = -2.549732539343734e+00; + const double C5 = 4.374664141464968e+00; + const double C6 = 2.938163982698783e+00; + + const double D1 = 7.784695709041462e-03; + const double D2 = 3.224671290700398e-01; + const double D3 = 2.445134137142996e+00; + const double D4 = 3.754408661907416e+00; + + const double Xlow = 0.02425; + const double Xhigh = 1.0 - Xlow; + + double z, r; + + if (p < Xlow) + { + // Rational approximation for the lower region 0 + /// Cumulative for a N(0,1) distribution + /// + /// The n0. + /// The x coordinate. + private static double CumN0(double x) + { + const double B1 = 0.319381530; + const double B2 = -0.356563782; + const double B3 = 1.781477937; + const double B4 = -1.821255978; + const double B5 = 1.330274429; + const double P = 0.2316419; + const double C = 0.39894228; + + if (x >= 0.0) + { + double t = 1.0 / (1.0 + (P * x)); + return (1.0 - C * Math.Exp(-x * x / 2.0) * t * (t * (t * (t * (t * B5 + B4) + B3) + B2) + B1)); + } + else + { + double t = 1.0 / (1.0 - P * x); + return (C * Math.Exp(-x * x / 2.0) * t * (t * (t * (t * (t * B5 + B4) + B3) + B2) + B1)); + } + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Series/FinancialSeries/HighLowSeriesExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Series/FinancialSeries/HighLowSeriesExamples.cs new file mode 100644 index 0000000..1d08b1f --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Series/FinancialSeries/HighLowSeriesExamples.cs @@ -0,0 +1,70 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("HighLowSeries"), Tags("Series")] + public static class HighLowSeriesExamples + { + [Example("HighLowSeries")] + public static PlotModel HighLowSeries() + { + var model = new PlotModel { Title = "HighLowSeries", LegendSymbolLength = 24 }; + var s1 = new HighLowSeries { Title = "HighLowSeries 1", Color = OxyColors.Black, }; + var r = new Random(314); + var price = 100.0; + for (int x = 0; x < 24; x++) + { + price = price + r.NextDouble() + 0.1; + var high = price + 10 + r.NextDouble() * 10; + var low = price - (10 + r.NextDouble() * 10); + var open = low + r.NextDouble() * (high - low); + var close = low + r.NextDouble() * (high - low); + s1.Items.Add(new HighLowItem(x, high, low, open, close)); + } + + model.Series.Add(s1); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, MaximumPadding = 0.3, MinimumPadding = 0.3 }); + + return model; + } + + [Example("HighLowSeries (DateTime axis)")] + public static PlotModel HighLowSeriesDateTimeAxis() + { + var m = new PlotModel(); + var x0 = DateTimeAxis.ToDouble(new DateTime(2013, 05, 04)); + var a = new DateTimeAxis + { + Position = AxisPosition.Bottom, + Minimum = x0 - 0.9, + Maximum = x0 + 1.9, + IntervalType = DateTimeIntervalType.Days, + MajorStep = 1, + MinorStep = 1, + StringFormat = "yyyy-MM-dd" + }; + m.Axes.Add(a); + var s = new HighLowSeries + { + TrackerFormatString = + "X: {1:yyyy-MM-dd}\nHigh: {2:0.00}\nLow: {3:0.00}\nOpen: {4:0.00}\nClose: {5:0.00}" + }; + + s.Items.Add(new HighLowItem(x0, 14, 10, 13, 12.4)); + s.Items.Add(new HighLowItem(x0 + 1, 17, 8, 12.4, 16.3)); + m.Series.Add(s); + + return m; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Series/FinancialSeries/OhlcvItemGenerator.cs b/OxyPlot/Source/Examples/ExampleLibrary/Series/FinancialSeries/OhlcvItemGenerator.cs new file mode 100644 index 0000000..307a703 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Series/FinancialSeries/OhlcvItemGenerator.cs @@ -0,0 +1,211 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Creates realistic OHLCV items. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + using System.Collections.Generic; + + using OxyPlot.Axes; + using OxyPlot.Series; + + /// + /// Creates realistic OHLCV items. + /// + public static class OhlcvItemGenerator + { + /// + /// The random number generator. + /// + private static readonly Random Rand = new Random(); + + /// + /// Creates bars governed by a MR process. + /// + /// N. + /// X0. + /// V0. + /// Csigma. + /// Esigma. + /// Kappa. + /// + /// The process. + /// + public static IEnumerable MRProcess( + int n, + double x0 = 100.0, + double v0 = 500, + double csigma = 0.50, + double esigma = 0.75, + double kappa = 0.01) + { + double x = x0; + var baseT = DateTime.UtcNow; + for (int ti = 0; ti < n; ti++) + { + var dx_c = -kappa * (x - x0) + RandomNormal(0, csigma); + var dx_1 = -kappa * (x - x0) + RandomNormal(0, esigma); + var dx_2 = -kappa * (x - x0) + RandomNormal(0, esigma); + + var open = x; + var close = x = x + dx_c; + var low = Min(open, close, open + dx_1, open + dx_2); + var high = Max(open, close, open + dx_1, open + dx_2); + + var dp = close - open; + var v = v0 * Math.Exp(Math.Abs(dp) / csigma); + var dir = (dp < 0) ? + -Math.Min(-dp / esigma, 1.0) : + Math.Min(dp / esigma, 1.0); + + var skew = (dir + 1) / 2.0; + var buyvol = skew * v; + var sellvol = (1 - skew) * v; + + var nowT = baseT.AddSeconds(ti); + var t = DateTimeAxis.ToDouble(nowT); + yield return new OhlcvItem(t, open, high, low, close, buyvol, sellvol); + } + } + + /// + /// Finds the minimum of the specified a, b, c and d. + /// + /// A. + /// B. + /// C. + /// D. + /// The minimum. + private static double Min(double a, double b, double c, double d) + { + return Math.Min(a, Math.Min(b, Math.Min(c, d))); + } + + /// + /// Finds the maximum of the specified a, b, c and d. + /// + /// A. + /// B. + /// C. + /// D. + /// The maximum. + private static double Max(double a, double b, double c, double d) + { + return Math.Max(a, Math.Max(b, Math.Max(c, d))); + } + + /// + /// Gets random normal + /// + /// Mu. + /// Sigma. + /// + private static double RandomNormal(double mu, double sigma) + { + return InverseCumNormal(Rand.NextDouble(), mu, sigma); + } + + /// + /// Fast approximation for inverse cum normal + /// + /// probability + /// Mean + /// std dev + private static double InverseCumNormal(double p, double mu, double sigma) + { + const double A1 = -3.969683028665376e+01; + const double A2 = 2.209460984245205e+02; + const double A3 = -2.759285104469687e+02; + const double A4 = 1.383577518672690e+02; + const double A5 = -3.066479806614716e+01; + const double A6 = 2.506628277459239e+00; + + const double B1 = -5.447609879822406e+01; + const double B2 = 1.615858368580409e+02; + const double B3 = -1.556989798598866e+02; + const double B4 = 6.680131188771972e+01; + const double B5 = -1.328068155288572e+01; + + const double C1 = -7.784894002430293e-03; + const double C2 = -3.223964580411365e-01; + const double C3 = -2.400758277161838e+00; + const double C4 = -2.549732539343734e+00; + const double C5 = 4.374664141464968e+00; + const double C6 = 2.938163982698783e+00; + + const double D1 = 7.784695709041462e-03; + const double D2 = 3.224671290700398e-01; + const double D3 = 2.445134137142996e+00; + const double D4 = 3.754408661907416e+00; + + const double Xlow = 0.02425; + const double Xhigh = 1.0 - Xlow; + + double z, r; + + if (p < Xlow) + { + // Rational approximation for the lower region 0 + /// Cumulative for a N(0,1) distribution + /// + /// The n0. + /// The x coordinate. + private static double CumN0(double x) + { + const double B1 = 0.319381530; + const double B2 = -0.356563782; + const double B3 = 1.781477937; + const double B4 = -1.821255978; + const double B5 = 1.330274429; + const double P = 0.2316419; + const double C = 0.39894228; + + if (x >= 0.0) + { + double t = 1.0 / (1.0 + (P * x)); + return (1.0 - C * Math.Exp(-x * x / 2.0) * t * (t * (t * (t * (t * B5 + B4) + B3) + B2) + B1)); + } + else + { + double t = 1.0 / (1.0 - P * x); + return (C * Math.Exp(-x * x / 2.0) * t * (t * (t * (t * (t * B5 + B4) + B3) + B2) + B1)); + } + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Series/FinancialSeries/OldCandleStickSeriesExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Series/FinancialSeries/OldCandleStickSeriesExamples.cs new file mode 100644 index 0000000..bee71e7 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Series/FinancialSeries/OldCandleStickSeriesExamples.cs @@ -0,0 +1,149 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + using System.Collections.Generic; + + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("Old CandleStickSeries"), Tags("Series")] + [Obsolete] + public static class OldCandleStickSeriesExamples + { + [Example("CandleStickSeries")] + public static PlotModel CandleStickSeries() + { + var model = new PlotModel { Title = "CandleStickSeries", LegendSymbolLength = 24 }; + var s1 = new OldCandleStickSeries + { + Title = "CandleStickSeries 1", + Color = OxyColors.Black, + }; + var r = new Random(314); + var price = 100.0; + for (int x = 0; x < 16; x++) + { + price = price + r.NextDouble() + 0.1; + var high = price + 10 + (r.NextDouble() * 10); + var low = price - (10 + (r.NextDouble() * 10)); + var open = low + (r.NextDouble() * (high - low)); + var close = low + (r.NextDouble() * (high - low)); + s1.Items.Add(new HighLowItem(x, high, low, open, close)); + } + + model.Series.Add(s1); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, MaximumPadding = 0.3, MinimumPadding = 0.3 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, MaximumPadding = 0.03, MinimumPadding = 0.03 }); + + return model; + } + + [Example("CandleStickSeries (red/green)")] + public static PlotModel CandleStickSeriesRedGreen() + { + var model = CandleStickSeries(); + model.Title = "CandleStickSeries (red/green)"; + var s1 = (OldCandleStickSeries)model.Series[0]; + s1.IncreasingFill = OxyColors.DarkGreen; + s1.DecreasingFill = OxyColors.Red; + s1.ShadowEndColor = OxyColors.Gray; + s1.Color = OxyColors.Black; + return model; + } + + [Example("Minute data (DateTimeAxis)")] + public static PlotModel MinuteData_DateTimeAxis() + { + var pm = new PlotModel { Title = "Minute Data (DateTimeAxis)" }; + + var timeSpanAxis1 = new DateTimeAxis { Position = AxisPosition.Bottom, StringFormat = "hh:mm" }; + pm.Axes.Add(timeSpanAxis1); + var linearAxis1 = new LinearAxis { Position = AxisPosition.Left }; + pm.Axes.Add(linearAxis1); + var candleStickSeries = new OldCandleStickSeries + { + CandleWidth = 6, + Color = OxyColors.Black, + IncreasingFill = OxyColors.DarkGreen, + DecreasingFill = OxyColors.Red, + DataFieldX = "Time", + DataFieldHigh = "H", + DataFieldLow = "L", + DataFieldOpen = "O", + DataFieldClose = "C", + TrackerFormatString = "High: {2:0.00}\nLow: {3:0.00}\nOpen: {4:0.00}\nClose: {5:0.00}", + ItemsSource = lst + }; + pm.Series.Add(candleStickSeries); + return pm; + } + + private static List lst = new List + { + new MinuteRec { QTime = TimeSpan.Parse("06:31:00"), O = 1672.5000, H = 1673.5000, L = 1671.7500, C = 1672.7500 }, + new MinuteRec { QTime = TimeSpan.Parse("06:32:00"), O = 1672.5000, H = 1673.5000, L = 1672.5000, C = 1672.5000 }, + new MinuteRec { QTime = TimeSpan.Parse("06:33:00"), O = 1672.5000, H = 1672.7500, L = 1670.7500, C = 1671.2500 }, + new MinuteRec { QTime = TimeSpan.Parse("06:34:00"), O = 1671.2500, H = 1671.2500, L = 1670.2500, C = 1670.5000 }, + new MinuteRec { QTime = TimeSpan.Parse("06:35:00"), O = 1670.7500, H = 1671.7500, L = 1670.5000, C = 1671.2500 }, + new MinuteRec { QTime = TimeSpan.Parse("06:36:00"), O = 1671.0000, H = 1672.5000, L = 1671.0000, C = 1672.5000 }, + new MinuteRec { QTime = TimeSpan.Parse("06:37:00"), O = 1672.5000, H = 1673.0000, L = 1672.0000, C = 1673.0000 }, + new MinuteRec { QTime = TimeSpan.Parse("06:38:00"), O = 1672.7500, H = 1673.2500, L = 1672.5000, C = 1672.5000 }, + new MinuteRec { QTime = TimeSpan.Parse("06:39:00"), O = 1672.5000, H = 1672.7500, L = 1671.2500, C = 1671.2500 }, + new MinuteRec { QTime = TimeSpan.Parse("06:40:00"), O = 1671.2500, H = 1672.5000, L = 1671.0000, C = 1672.0000 }, + new MinuteRec { QTime = TimeSpan.Parse("06:41:00"), O = 1672.2500, H = 1672.5000, L = 1671.2500, C = 1672.5000 }, + new MinuteRec { QTime = TimeSpan.Parse("06:42:00"), O = 1672.2500, H = 1672.5000, L = 1671.5000, C = 1671.5000 }, + new MinuteRec { QTime = TimeSpan.Parse("06:43:00"), O = 1671.5000, H = 1671.7500, L = 1670.5000, C = 1671.0000 }, + new MinuteRec { QTime = TimeSpan.Parse("06:44:00"), O = 1670.7500, H = 1671.7500, L = 1670.7500, C = 1671.7500 }, + new MinuteRec { QTime = TimeSpan.Parse("06:45:00"), O = 1672.0000, H = 1672.2500, L = 1671.5000, C = 1671.5000 }, + new MinuteRec { QTime = TimeSpan.Parse("06:46:00"), O = 1671.7500, H = 1671.7500, L = 1671.0000, C = 1671.5000 }, + new MinuteRec { QTime = TimeSpan.Parse("06:47:00"), O = 1671.7500, H = 1672.2500, L = 1671.5000, C = 1671.7500 }, + new MinuteRec { QTime = TimeSpan.Parse("06:48:00"), O = 1671.7500, H = 1672.7500, L = 1671.7500, C = 1672.5000 }, + new MinuteRec { QTime = TimeSpan.Parse("06:49:00"), O = 1672.2500, H = 1673.7500, L = 1672.2500, C = 1673.7500 }, + new MinuteRec { QTime = TimeSpan.Parse("06:50:00"), O = 1673.7500, H = 1675.0000, L = 1673.5000, C = 1675.0000 } + }; + + [Example("Minute data (TimeSpanAxis)")] + public static PlotModel MinuteData_TimeSpan() + { + var pm = new PlotModel { Title = "Minute Data (TimeSpanAxis)" }; + + var timeSpanAxis1 = new TimeSpanAxis { Position = AxisPosition.Bottom, StringFormat = "hh:mm" }; + pm.Axes.Add(timeSpanAxis1); + var linearAxis1 = new LinearAxis { Position = AxisPosition.Left }; + pm.Axes.Add(linearAxis1); + var candleStickSeries = new OldCandleStickSeries + { + CandleWidth = 5, + Color = OxyColors.DarkGray, + IncreasingFill = OxyColors.DarkGreen, + DecreasingFill = OxyColors.Red, + DataFieldX = "QTime", + DataFieldHigh = "H", + DataFieldLow = "L", + DataFieldOpen = "O", + DataFieldClose = "C", + TrackerFormatString = "High: {2:0.00}\nLow: {3:0.00}\nOpen: {4:0.00}\nClose: {5:0.00}", + ItemsSource = lst + }; + pm.Series.Add(candleStickSeries); + return pm; + } + + public class MinuteRec + { + public DateTime Time { get { return new DateTime(2013, 10, 8) + this.QTime; } } + public TimeSpan QTime { get; set; } + public double H { get; set; } + public double L { get; set; } + public double O { get; set; } + public double C { get; set; } + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Series/FinancialSeries/VolumeSeriesExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Series/FinancialSeries/VolumeSeriesExamples.cs new file mode 100644 index 0000000..0e688cd --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Series/FinancialSeries/VolumeSeriesExamples.cs @@ -0,0 +1,130 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("VolumeSeries")] + [Tags("Series")] + public static class VolumeSeriesExamples + { + [Example("Just Volume (combined), fixed axis")] + public static Example JustVolumeCombined_Fixed() + { + return CreateVolumeSeries("Just Volume (combined)", VolumeStyle.Combined, natural: false); + } + + [Example("Just Volume (combined), natural axis")] + public static Example JustVolumeCombined_Natural() + { + return CreateVolumeSeries("Just Volume (combined)", VolumeStyle.Combined, natural: true); + } + + [Example("Just Volume (stacked), fixed axis")] + public static Example JustVolumeStacked_Fixed() + { + return CreateVolumeSeries("Just Volume (stacked)", VolumeStyle.Stacked, natural: false); + } + + [Example("Just Volume (stacked), natural axis")] + public static Example JustVolumeStacked_Natural() + { + return CreateVolumeSeries("Just Volume (stacked)", VolumeStyle.Stacked, natural: true); + } + + [Example("Just Volume (+/-), fixed axis")] + public static Example JustVolumePositiveNegative_Fixed() + { + return CreateVolumeSeries("Just Volume (+/-)", VolumeStyle.PositiveNegative, natural: false); + } + + [Example("Just Volume (+/-), natural axis")] + public static Example JustVolumePositiveNegative_Natural() + { + return CreateVolumeSeries("Just Volume (+/-)", VolumeStyle.PositiveNegative, natural: true); + } + + /// + /// Creates the volume series. + /// + /// The volume series. + /// Title. + /// Style. + /// N. + /// If set to true natural. + private static Example CreateVolumeSeries( + string title, + VolumeStyle style, + int n = 10000, + bool natural = false) + { + var pm = new PlotModel { Title = title }; + + var series = new VolumeSeries + { + PositiveColor = OxyColors.DarkGreen, + NegativeColor = OxyColors.Red, + PositiveHollow = false, + NegativeHollow = false, + VolumeStyle = style + }; + + // create bars + foreach (var bar in OhlcvItemGenerator.MRProcess(n)) + { + series.Append(bar); + } + + // create visible window + var Istart = n - 200; + var Iend = n - 120; + var Xmin = series.Items[Istart].X; + var Xmax = series.Items[Iend].X; + + // setup axes + var timeAxis = new DateTimeAxis + { + Position = AxisPosition.Bottom, + Minimum = Xmin, + Maximum = Xmax + }; + var volAxis = new LinearAxis + { + Position = AxisPosition.Left, + Key = "Volume", + StartPosition = 0.0, + EndPosition = 1.0, + Minimum = natural ? double.NaN : 0, + Maximum = natural ? double.NaN : 10000 + }; + + switch (style) + { + case VolumeStyle.Combined: + case VolumeStyle.Stacked: + pm.Axes.Add(timeAxis); + pm.Axes.Add(volAxis); + break; + + case VolumeStyle.PositiveNegative: + volAxis.Minimum = natural ? double.NaN : -10000; + pm.Axes.Add(timeAxis); + pm.Axes.Add(volAxis); + break; + } + + pm.Series.Add(series); + + var controller = new PlotController(); + controller.UnbindAll(); + controller.BindMouseDown(OxyMouseButton.Left, PlotCommands.PanAt); + return new Example(pm, controller); + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Series/FunctionSeriesExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Series/FunctionSeriesExamples.cs new file mode 100644 index 0000000..011a7fe --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Series/FunctionSeriesExamples.cs @@ -0,0 +1,321 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + + using OxyPlot; + using OxyPlot.Annotations; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("FunctionSeries"), Tags("Series")] + public class FunctionSeriesExamples + { + [Example("Square wave")] + public static PlotModel CreateSquareWave() + { + return CreateSquareWave(25); + } + + private static PlotModel CreateSquareWave(int n = 25) + { + var plot = new PlotModel { Title = "Square wave (Gibbs phenomenon)" }; + + Func f = (x) => + { + double y = 0; + for (int i = 0; i < n; i++) + { + int j = i * 2 + 1; + y += Math.Sin(j * x) / j; + } + return y; + }; + + var fs = new FunctionSeries(f, -10, 10, 0.0001, "sin(x)+sin(3x)/3+sin(5x)/5+...+sin(" + (2 * n - 1) + ")/" + (2 * n - 1)); + + plot.Series.Add(fs); + plot.Subtitle = "n = " + fs.Points.Count; + + plot.Axes.Add(new LinearAxis + { + Position = AxisPosition.Left, + Minimum = -4, + Maximum = 4 + }); + plot.Axes.Add(new LinearAxis + { + Position = AxisPosition.Bottom + }); + + return plot; + } + + [Example("Parametric function 1")] + public static PlotModel Clover() + { + return CreateParametricPlot( + t => 2 * Math.Cos(2 * t) * Math.Cos(t), + t => 2 * Math.Cos(2 * t) * Math.Sin(t), + // t=>-4*Math.Sin(2*t)*Math.Cos(t)-2*Math.Cos(2*t)*Math.Sin(t), + // t=>-4*Math.Sin(2*t)*Math.Sin(t)+2*Math.Cos(2*t)*Math.Cos(t),)))) + 0, Math.PI * 2, 1000, + "Parametric function", + "Using the CartesianAxes property", + "2cos(2t)cos(t) , 2cos(2t)sin(t)"); + + } + + [Example("Parametric function 2")] + public static PlotModel ParametricFunction2() + { + return CreateParametricPlot( + t => 3 * Math.Sin(5 * t), + t => 3 * Math.Cos(3 * t), + 0, Math.PI * 2, 1000, + "Parametric function", + null, + "3sin(5t) , 3cos(3t)"); + } + + [Example("Parametric function 3")] + public static PlotModel ParametricFunction3() + { + return CreateParametricPlot( + t => 2 * Math.Cos(t) + Math.Cos(8 * t), + t => 2 * Math.Sin(t) + Math.Sin(8 * t), + 0, Math.PI * 2, 1000, + "Parametric function", + null, + "2cos(t)+cos(8t) , 2sin(t)+sin(8t)"); + } + + [Example("Lemniscate of Bernoulli")] + public static PlotModel LemniscateOfBernoulli() + { + // http://en.wikipedia.org/wiki/Lemniscate_of_Bernoulli + double a = 1; + return CreateParametricPlot( + t => a * Math.Sqrt(2) * Math.Cos(t) / (Math.Sin(t) * Math.Sin(t) + 1), + t => a * Math.Sqrt(2) * Math.Cos(t) * Math.Sin(t) / (Math.Sin(t) * Math.Sin(t) + 1), + 0, Math.PI * 2, 1000, "Lemniscate of Bernoulli"); + } + + [Example("Lemniscate of Gerono")] + public static PlotModel LemniscateOfGerono() + { + // http://en.wikipedia.org/wiki/Lemniscate_of_Gerono + return CreateParametricPlot(t => Math.Cos(t), t => Math.Sin(2 * t) / 2, 0, Math.PI * 2, 1000, "Lemniscate of Gerono"); + } + + [Example("Lissajous figure")] + public static PlotModel LissajousFigure() + { + double a = 3; + double b = 2; + double delta = Math.PI / 2; + // http://en.wikipedia.org/wiki/Lissajous_figure + return CreateParametricPlot(t => Math.Sin(a * t + delta), t => Math.Sin(b * t), 0, Math.PI * 2, 1000, "Lissajous figure", null, "a=3, b=2, δ = π/2"); + } + + [Example("Rose curve")] + public static PlotModel RoseCurve() + { + // http://en.wikipedia.org/wiki/Rose_curve + + var m = new PlotModel + { + Title = "Rose curve", + PlotType = PlotType.Polar, + PlotAreaBorderThickness = new OxyThickness(0) + }; + + m.Axes.Add(new AngleAxis + { + Minimum = 0, + Maximum = Math.PI * 2, + MajorStep = Math.PI / 4, + MinorStep = Math.PI / 16, + MajorGridlineStyle = LineStyle.Solid, + FormatAsFractions = true, + FractionUnit = Math.PI, + FractionUnitSymbol = "π" + }); + m.Axes.Add(new MagnitudeAxis() { MajorGridlineStyle = LineStyle.Solid }); + + int d = 4; + int n = 3; + double k = (double)n / d; + m.Series.Add(new FunctionSeries(t => Math.Sin(k * t), t => t, 0, Math.PI * 2 * d, 1000, string.Format("d={0}, n={1}", d, n))); + + return m; + } + + [Example("Limaçon of Pascal")] + public static PlotModel LimaconOfPascal() + { + // http://en.wikipedia.org/wiki/Lima%C3%A7on + + var m = new PlotModel { Title = "Limaçon of Pascal", PlotType = PlotType.Cartesian }; + for (int a = 4; a <= 4; a++) + for (int b = 0; b <= 10; b++) + { + m.Series.Add( + new FunctionSeries( + t => a / 2 + b * Math.Cos(t) + a / 2 * Math.Cos(2 * t), + t => b * Math.Sin(t) + a / 2 * Math.Sin(2 * t), + 0, + Math.PI * 2, + 1000, + string.Format("a={0}, b={1}", a, b))); + } + return m; + } + + [Example("Folium of Descartes")] + public static PlotModel DescartesFolium() + { + // http://www.wolframalpha.com/input/?i=folium+of+Descartes + + var m = new PlotModel { Title = "Folium of Descartes", PlotType = PlotType.Cartesian }; + m.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = -3, Maximum = 3 }); + m.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = -3, Maximum = 3 }); + double a = 1; + m.Series.Add(new FunctionSeries(t => 3 * a * t / (t * t * t + 1), t => 3 * a * t * t / (t * t * t + 1), -30, 30, 1001, string.Format("a={0}", a))); + + return m; + } + + [Example("Trisectrix of Maclaurin")] + public static PlotModel TrisectrixOfMaclaurin() + { + // http://en.wikipedia.org/wiki/Trisectrix_of_Maclaurin + // http://mathworld.wolfram.com/MaclaurinTrisectrix.html + + var m = new PlotModel { Title = "Trisectrix of Maclaurin", PlotType = PlotType.Cartesian }; + double a = 1; + m.Series.Add(new FunctionSeries(t => a * (t * t - 3) / (t * t + 1), t => a * t * (t * t - 3) / (t * t + 1), -5, 5, 1000)); + return m; + } + + [Example("Fermat's spiral")] + public static PlotModel FermatsSpiral() + { + // http://en.wikipedia.org/wiki/Fermat's_spiral + // http://www.wolframalpha.com/input/?i=Fermat%27s+spiral + var m = new PlotModel { Title = "Fermat's spiral", PlotType = PlotType.Cartesian }; + double a = 1; + m.Series.Add(new FunctionSeries(t => a * Math.Sqrt(t) * Math.Cos(t), t => a * Math.Sqrt(t) * Math.Sin(t), 0, 20, 1000)); + m.Series.Add(new FunctionSeries(t => -a * Math.Sqrt(t) * Math.Cos(t), t => -a * Math.Sqrt(t) * Math.Sin(t), 0, 20, 1000)); + return m; + } + + [Example("Fish curve")] + public static PlotModel FishCurve() + { + // http://www.wolframalpha.com/input/?i=fish+curve + var m = new PlotModel { Title = "Fish curve", PlotType = PlotType.Cartesian }; + for (double a = 0.1; a < 1; a += 0.1) + { + m.Series.Add(new FunctionSeries(t => a * (Math.Cos(t) - Math.Sin(t) * Math.Sin(t) / Math.Sqrt(2)), t => a * Math.Cos(t) * Math.Sin(t), 0, 2 * Math.PI, 1000)); + } + + return m; + } + + [Example("Heaviside step function")] + public static PlotModel HeavisideStepFunction() + { + // http://en.wikipedia.org/wiki/Heaviside_step_function + + var m = new PlotModel { Title = "Heaviside step function", PlotType = PlotType.Cartesian }; + m.Series.Add(new FunctionSeries(x => + { + // make a gap in the curve at x=0 + if (Math.Abs(x) < 1e-8) return double.NaN; + return x < 0 ? 0 : 1; + }, -2, 2, 0.001)); + m.Annotations.Add(new LineAnnotation { Type = LineAnnotationType.Vertical, Color = m.DefaultColors[0], X = 0, MinimumY = 0, MaximumY = 1 }); + return m; + } + + [Example("FunctionSeries")] + public static PlotModel FunctionSeries() + { + var pm = new PlotModel + { + Title = "Trigonometric functions", + Subtitle = "Example using the FunctionSeries", + PlotType = PlotType.Cartesian, + PlotAreaBackground = OxyColors.White + }; + pm.Series.Add(new FunctionSeries(Math.Sin, -10, 10, 0.1, "sin(x)")); + pm.Series.Add(new FunctionSeries(Math.Cos, -10, 10, 0.1, "cos(x)")); + pm.Series.Add(new FunctionSeries(t => 5 * Math.Cos(t), t => 5 * Math.Sin(t), 0, 2 * Math.PI, 1000, "cos(t),sin(t)")); + return pm; + } + + [Example("Squirqle")] + public static PlotModel Squirqle() + { + var plot = new PlotModel { Title = "Squirqle", PlotType = PlotType.Cartesian }; + plot.Series.Add(CreateSuperellipseSeries(4, 1, 1)); + + return plot; + } + + [Example("Superellipse n=20")] + public static PlotModel Superellipse20() + { + var plot = new PlotModel { Title = "Superellipse", PlotType = PlotType.Cartesian }; + var s = CreateSuperellipseSeries(20, 1, 1); + s.MarkerType = MarkerType.Circle; + plot.Series.Add(s); + + return plot; + } + + [Example("Lamé curves")] + public static PlotModel LameCurves() + { + var plot = new PlotModel { Title = "Lamé curves", PlotType = PlotType.Cartesian, LegendPlacement = LegendPlacement.Outside }; + for (double n = 0.25; n < 2; n += 0.25) + { + plot.Series.Add(CreateSuperellipseSeries(n, 1, 1)); + } + + for (double n = 2; n <= 8 + 1e-6; n += 1) + { + plot.Series.Add(CreateSuperellipseSeries(n, 1, 1)); + } + + return plot; + } + + public static FunctionSeries CreateSuperellipseSeries(double n, double a, double b) + { + // http://en.wikipedia.org/wiki/Superellipse + return new FunctionSeries( + t => a * Math.Sign(Math.Cos(t)) * Math.Pow(Math.Abs(Math.Cos(t)), 2 / n), + t => b * Math.Sign(Math.Sin(t)) * Math.Pow(Math.Abs(Math.Sin(t)), 2 / n), + 0, + Math.PI * 2, + 101, + string.Format("n={0}, a={1}, b={2}", n, a, b)); + } + + private static PlotModel CreateParametricPlot(Func fx, Func fy, double t0, + double t1, int n, string title, string subtitle = null, + string seriesTitle = null) + { + var plot = new PlotModel { Title = title, Subtitle = subtitle, PlotType = PlotType.Cartesian }; + plot.Series.Add(new FunctionSeries(fx, fy, t0, t1, n, seriesTitle)); + return plot; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Series/HeatMapSeriesExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Series/HeatMapSeriesExamples.cs new file mode 100644 index 0000000..67d5965 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Series/HeatMapSeriesExamples.cs @@ -0,0 +1,473 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Creates a simple example heat map from a 2×3 matrix. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("HeatMapSeries"), Tags("Series")] + public class HeatMapSeriesExamples + { + [Example("Peaks")] + public static PlotModel Peaks() + { + return CreatePeaks(); + } + + public static PlotModel CreatePeaks(OxyPalette palette = null, bool includeContours = true, int n = 100) + { + double x0 = -3.1; + double x1 = 3.1; + double y0 = -3; + double y1 = 3; + Func peaks = (x, y) => 3 * (1 - x) * (1 - x) * Math.Exp(-(x * x) - (y + 1) * (y + 1)) - 10 * (x / 5 - x * x * x - y * y * y * y * y) * Math.Exp(-x * x - y * y) - 1.0 / 3 * Math.Exp(-(x + 1) * (x + 1) - y * y); + var xvalues = ArrayBuilder.CreateVector(x0, x1, n); + var yvalues = ArrayBuilder.CreateVector(y0, y1, n); + var peaksData = ArrayBuilder.Evaluate(peaks, xvalues, yvalues); + + var model = new PlotModel { Title = "Peaks" }; + model.Axes.Add(new LinearColorAxis { Position = AxisPosition.Right, Palette = palette ?? OxyPalettes.Jet(500), HighColor = OxyColors.Gray, LowColor = OxyColors.Black }); + + var hms = new HeatMapSeries { X0 = x0, X1 = x1, Y0 = y0, Y1 = y1, Data = peaksData }; + model.Series.Add(hms); + if (includeContours) + { + var cs = new ContourSeries + { + Color = OxyColors.Black, + FontSize = 0, + ContourLevelStep = 1, + LabelBackground = OxyColors.Undefined, + ColumnCoordinates = yvalues, + RowCoordinates = xvalues, + Data = peaksData + }; + model.Series.Add(cs); + } + + return model; + } + + [Example("2×3, interpolated")] + public static PlotModel Interpolated() + { + return CreateExample("Interpolated", true); + } + + [Example("2×3, interpolated, cartesian axes")] + public static PlotModel InterpolatedCartesian() + { + var model = CreateExample("Interpolated, cartesian axes", true); + model.PlotType = PlotType.Cartesian; + return model; + } + + [Example("2×3, interpolated with two NaN values")] + public static PlotModel InterpolatedWithNanValue() + { + var model = CreateExample("Interpolated including two NaN values", true); + var hms = (HeatMapSeries)model.Series[0]; + hms.Data[0, 1] = double.NaN; + hms.Data[1, 0] = double.NaN; + return model; + } + + [Example("2×3, interpolated with two NaN values, flat data")] + public static PlotModel InterpolatedWithNanValueFlat() + { + var model = CreateExample("Interpolated including two NaN values, otherwise 4.71", true); + var hms = (HeatMapSeries)model.Series[0]; + + double datum = 4.71d; + hms.Data[0, 0] = datum; + hms.Data[0, 1] = datum; + hms.Data[0, 2] = datum; + hms.Data[1, 0] = datum; + hms.Data[1, 1] = datum; + hms.Data[1, 2] = datum; + + hms.Data[0, 1] = double.NaN; + hms.Data[1, 0] = double.NaN; + return model; + } + + [Example("2×3, not interpolated")] + public static PlotModel NotInterpolated() + { + return CreateExample("Not interpolated values", false); + } + + [Example("2×3, not interpolated with two NaN values")] + public static PlotModel NotInterpolatedWithNanValue() + { + var model = CreateExample("Not interpolated values including two NaN values", false); + var ca = (LinearColorAxis)model.Axes[0]; + ca.InvalidNumberColor = OxyColors.Transparent; + var hms = (HeatMapSeries)model.Series[0]; + hms.Data[0, 1] = double.NaN; + hms.Data[1, 0] = double.NaN; + return model; + } + + [Example("2×3, not interpolated with two NaN values, flat data")] + public static PlotModel NotInterpolatedWithNanValueFlat() + { + var model = CreateExample("Not interpolated values including two NaN values, otherwise 4.71", false); + var ca = (LinearColorAxis)model.Axes[0]; + ca.InvalidNumberColor = OxyColors.Transparent; + var hms = (HeatMapSeries)model.Series[0]; + + double datum = 4.71d; + hms.Data[0, 0] = datum; + hms.Data[0, 1] = datum; + hms.Data[0, 2] = datum; + hms.Data[1, 0] = datum; + hms.Data[1, 1] = datum; + hms.Data[1, 2] = datum; + + hms.Data[0, 1] = double.NaN; + hms.Data[1, 0] = double.NaN; + return model; + } + + [Example("2×3, reversed x-axis")] + public static PlotModel NotInterpolatedReversedX() + { + var model = CreateExample("Reversed x-axis", false); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, StartPosition = 1, EndPosition = 0 }); + return model; + } + + [Example("2×3, X0>X1")] + public static PlotModel X0GreaterThanX1() + { + var model = CreateExample("X0>X1", false); + var hms = (HeatMapSeries)model.Series[0]; + var tmp = hms.X0; + hms.X0 = hms.X1; + hms.X1 = tmp; + return model; + } + + [Example("2×3, reversed x-axis, X0>X1")] + public static PlotModel ReversedX_X0GreaterThanX1() + { + var model = CreateExample("Reversed x-axis, X0>X1", false); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, StartPosition = 1, EndPosition = 0 }); + var hms = (HeatMapSeries)model.Series[0]; + var tmp = hms.X0; + hms.X0 = hms.X1; + hms.X1 = tmp; + return model; + } + + [Example("2×3, reversed y-axis")] + public static PlotModel NotInterpolatedReversedY() + { + var model = CreateExample("Reversed y-axis", false); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, StartPosition = 1, EndPosition = 0 }); + return model; + } + + [Example("2×3, Y0>Y1")] + public static PlotModel Y0GreaterThanY1() + { + var model = CreateExample("Y0>Y1", false); + var hms = (HeatMapSeries)model.Series[0]; + var tmp = hms.Y0; + hms.Y0 = hms.Y1; + hms.Y1 = tmp; + return model; + } + + [Example("2×3, reversed y-axis, Y0>Y1")] + public static PlotModel ReversedY_Y0GreaterThanY1() + { + var model = CreateExample("Reversed y-axis, Y0>Y1", false); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, StartPosition = 1, EndPosition = 0 }); + var hms = (HeatMapSeries)model.Series[0]; + var tmp = hms.Y0; + hms.Y0 = hms.Y1; + hms.Y1 = tmp; + return model; + } + + [Example("2x3, reversed x- and y-axis")] + public static PlotModel NotInterpolatedReversedXY() + { + var model = CreateExample("Reversed x- and y-axis", false); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, StartPosition = 1, EndPosition = 0 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, StartPosition = 1, EndPosition = 0 }); + return model; + } + + [Example("3×3, diagonal (center defined)")] + public static PlotModel Diagonal() + { + var data = new double[3, 3]; + data[0, 0] = 1; + data[1, 1] = 1; + data[2, 2] = 1; + + var model = new PlotModel { Title = "Diagonal (center defined)" }; + model.Axes.Add(new LinearColorAxis { Position = AxisPosition.Right, Palette = OxyPalettes.Jet(500), HighColor = OxyColors.Gray, LowColor = OxyColors.Black }); + + // adding half cellwidth/cellheight to bounding box coordinates + var hms = new HeatMapSeries { CoordinateDefinition = HeatMapCoordinateDefinition.Center, X0 = 0.5, X1 = 2.5, Y0 = 2.5, Y1 = 0.5, Data = data, Interpolate = false }; + model.Series.Add(hms); + return model; + } + + [Example("3×3, diagonal (edge defined)")] + public static PlotModel Diagonal2() + { + var data = new double[3, 3]; + data[0, 0] = 1; + data[1, 1] = 1; + data[2, 2] = 1; + + var model = new PlotModel { Title = "Diagonal (edge defined)" }; + model.Axes.Add(new LinearColorAxis { Position = AxisPosition.Right, Palette = OxyPalettes.Jet(500), HighColor = OxyColors.Gray, LowColor = OxyColors.Black }); + + // adding half cellwidth/cellheight to bounding box coordinates + var hms = new HeatMapSeries { CoordinateDefinition = HeatMapCoordinateDefinition.Edge, X0 = 0, X1 = 3, Y0 = 3, Y1 = 0, Data = data, Interpolate = false }; + model.Series.Add(hms); + return model; + } + + [Example("6×6, diagonal")] + public static PlotModel Diagonal_6X6() + { + var data = new double[6, 6]; + data[0, 0] = 1; + data[1, 1] = 1; + data[2, 2] = 1; + data[3, 3] = 1; + data[4, 4] = 1; + data[5, 5] = 1; + + var model = new PlotModel { Title = "Diagonal 6×6" }; + model.Axes.Add(new LinearColorAxis { Position = AxisPosition.Right, Palette = OxyPalettes.Jet(500), HighColor = OxyColors.Gray, LowColor = OxyColors.Black }); + + // note: the coordinates are specifying the centers of the edge cells + var hms = new HeatMapSeries { X0 = 0, X1 = 5, Y0 = 5, Y1 = 0, Data = data, Interpolate = false }; + model.Series.Add(hms); + return model; + } + + [Example("Confusion matrix")] + public static PlotModel ConfusionMatrix() + { + // Example provided by Pau Climent Pérez + // See also http://en.wikipedia.org/wiki/Confusion_matrix + var data = new double[3, 3]; + + data[0, 0] = 1; + data[1, 1] = 0.8; + data[1, 2] = 0.2; + data[2, 2] = 1; + + // I guess this is where the confusion comes from? + data = data.Transpose(); + + string[] cat1 = { "class A", "class B", "class C" }; + + var model = new PlotModel { Title = "Confusion Matrix" }; + + var palette = OxyPalette.Interpolate(50, OxyColors.White, OxyColors.Black); + + var lca = new LinearColorAxis { Position = AxisPosition.Right, Palette = palette, HighColor = OxyColors.White, LowColor = OxyColors.White }; + model.Axes.Add(lca); + + var axis1 = new CategoryAxis { Position = AxisPosition.Top, Title = "Actual class" }; + axis1.Labels.AddRange(cat1); + model.Axes.Add(axis1); + + // We invert this axis, so that they look "symmetrical" + var axis2 = new CategoryAxis { Position = AxisPosition.Left, Title = "Predicted class" }; + axis2.Labels.AddRange(cat1); + axis2.Angle = -90; + axis2.StartPosition = 1; + axis2.EndPosition = 0; + + model.Axes.Add(axis2); + + var hms = new HeatMapSeries + { + Data = data, + Interpolate = false, + LabelFontSize = 0.25, + X0 = 0, + X1 = data.GetLength(1) - 1, + Y0 = 0, + Y1 = data.GetLength(0) - 1, + }; + + model.Series.Add(hms); + return model; + } + + [Example("Logarithmic X, interpolated")] + public static PlotModel LogXInterpolated() + { + var data = new double[11, 21]; + + double k = Math.Pow(2, 0.1); + + for (int i = 0; i < 11; i++) + { + for (int j = 0; j < 21; j++) + { + data[i, j] = Math.Pow(k, (double)i) * (double)j / 40.0; + } + } + + var model = new PlotModel { Title = "Logarithmic X, interpolated" }; + model.Axes.Add(new LogarithmicAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + model.Axes.Add(new LinearColorAxis { Position = AxisPosition.Right, Palette = OxyPalettes.Gray(500), HighColor = OxyColors.White, LowColor = OxyColors.Black }); + + var hms = new HeatMapSeries { X0 = 1.0, X1 = 2.0, Y0 = 0, Y1 = 20, Data = data, Interpolate = true }; + + model.Series.Add(hms); + return model; + } + + [Example("Logarithmic X, discrete rectangles")] + public static PlotModel LogXNotInterpolated() + { + var data = new double[11, 21]; + + double k = Math.Pow(2, 0.1); + + for (int i = 0; i < 11; i++) + { + for (int j = 0; j < 21; j++) + { + data[i, j] = Math.Pow(k, (double)i) * (double)j / 40.0; + } + } + + var model = new PlotModel { Title = "Logarithmic X, discrete rectangles" }; + model.Axes.Add(new LogarithmicAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + model.Axes.Add(new LinearColorAxis { Position = AxisPosition.Right, Palette = OxyPalettes.Gray(500), HighColor = OxyColors.White, LowColor = OxyColors.Black }); + + var hms = new HeatMapSeries { X0 = 1.0, X1 = 2.0, Y0 = 0, Y1 = 20, Data = data, Interpolate = false, RenderMethod = HeatMapRenderMethod.Rectangles, LabelFontSize = 0.4 }; + + model.Series.Add(hms); + return model; + } + + [Example("6×4, not transposed")] + public static PlotModel Normal_6X4() + { + return Create6X4(false, "Normal 6×4 Heatmap"); + } + + [Example("6×4, transposed")] + public static PlotModel Transposed_6X4() + { + return Create6X4(true, "Transposed 6×4 Heatmap"); + } + + private static PlotModel Create6X4(bool transpose, string title) + { + var data = new double[6, 4]; + + for (int i = 1; i <= 6; i++) + { + for (int j = 1; j <= 4; j++) + { + data[i - 1, j - 1] = i * j; + } + } + + var model = new PlotModel { Title = title, Subtitle = "Note the positions of the axes" }; + + var xaxis = new LinearAxis { Key = "x", Title = "X", Position = transpose ? AxisPosition.Left : AxisPosition.Bottom }; + var yaxis = new LinearAxis { Key = "y", Title = "Y", Position = transpose ? AxisPosition.Bottom : AxisPosition.Left }; + + model.Axes.Add(xaxis); + model.Axes.Add(yaxis); + model.Axes.Add(new LinearColorAxis { Position = AxisPosition.Right, Palette = OxyPalettes.Jet(500), HighColor = OxyColors.White, LowColor = OxyColors.Black }); + + var hms = new HeatMapSeries { X0 = 1, X1 = 6, Y0 = 1, Y1 = 4, Data = data, Interpolate = true, LabelFontSize = 0.2 }; + + hms.XAxisKey = "x"; + hms.YAxisKey = "y"; + + model.Series.Add(hms); + return model; + } + + private static void DefaultXAxis_MouseDown(object sender, OxyMouseDownEventArgs e) + { + throw new NotImplementedException(); + } + + /// + /// Creates a simple example heat map from a 2×3 matrix. + /// + /// The title. + /// Interpolate the HeatMapSeries if set to true. + /// A . + private static PlotModel CreateExample(string title, bool interpolate) + { + var data = new double[2, 3]; + data[0, 0] = 0; + data[0, 1] = 0.2; + data[0, 2] = 0.4; + data[1, 0] = 0.1; + data[1, 1] = 0.3; + data[1, 2] = 0.2; + + var model = new PlotModel { Title = "HeatMapSeries", Subtitle = title }; + model.Axes.Add(new LinearColorAxis { Position = AxisPosition.Right, Palette = OxyPalettes.Jet(500), HighColor = OxyColors.Gray, LowColor = OxyColors.Black }); + + // adding half cellwidth/cellheight to bounding box coordinates + var hms = new HeatMapSeries + { + CoordinateDefinition = HeatMapCoordinateDefinition.Center, + X0 = 0.5, + X1 = 1.5, + Y0 = 0.5, + Y1 = 2.5, + Data = data, + Interpolate = interpolate, + LabelFontSize = 0.2 + }; + model.Series.Add(hms); + return model; + } + } + + internal static class ArrayExtensions + { + public static double[,] Transpose(this double[,] input) + { + int m = input.GetLength(0); + int n = input.GetLength(1); + var output = new double[n, m]; + for (int i = 0; i < m; i++) + { + for (int j = 0; j < n; j++) + { + output[j, i] = input[i, j]; + } + } + + return output; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Series/IntervalBarSeriesExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Series/IntervalBarSeriesExamples.cs new file mode 100644 index 0000000..7488c28 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Series/IntervalBarSeriesExamples.cs @@ -0,0 +1,45 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("IntervalBarSeries"), Tags("Series")] + public static class IntervalBarSeriesExamples + { + [Example("IntervalBarSeries")] + public static PlotModel IntervalBarSeries() + { + var model = new PlotModel { Title = "IntervalBarSeries", LegendPlacement = LegendPlacement.Outside }; + + var s1 = new IntervalBarSeries { Title = "IntervalBarSeries 1" }; + s1.Items.Add(new IntervalBarItem { Start = 6, End = 8 }); + s1.Items.Add(new IntervalBarItem { Start = 4, End = 8 }); + s1.Items.Add(new IntervalBarItem { Start = 5, End = 11 }); + s1.Items.Add(new IntervalBarItem { Start = 4, End = 12 }); + model.Series.Add(s1); + var s2 = new IntervalBarSeries { Title = "IntervalBarSeries 2" }; + s2.Items.Add(new IntervalBarItem { Start = 8, End = 9 }); + s2.Items.Add(new IntervalBarItem { Start = 8, End = 10 }); + s2.Items.Add(new IntervalBarItem { Start = 11, End = 12 }); + s2.Items.Add(new IntervalBarItem { Start = 12, End = 12.5 }); + model.Series.Add(s2); + + var categoryAxis = new CategoryAxis { Position = AxisPosition.Left }; + categoryAxis.Labels.Add("Activity A"); + categoryAxis.Labels.Add("Activity B"); + categoryAxis.Labels.Add("Activity C"); + categoryAxis.Labels.Add("Activity D"); + var valueAxis = new LinearAxis { Position = AxisPosition.Bottom, MinimumPadding = 0.1, MaximumPadding = 0.1 }; + model.Axes.Add(categoryAxis); + model.Axes.Add(valueAxis); + return model; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Series/LineSeriesExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Series/LineSeriesExamples.cs new file mode 100644 index 0000000..4a60d0b --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Series/LineSeriesExamples.cs @@ -0,0 +1,462 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +using System; + +using OxyPlot; +using OxyPlot.Series; + +namespace ExampleLibrary +{ + using System; + using System.Collections.Generic; + using System.Linq; + + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("LineSeries"), Tags("Series")] + public class LineSeriesExamples + { + private static readonly Random Randomizer = new Random(13); + + [Example("Default style")] + public static PlotModel DefaultStyle() + { + var model = new PlotModel { Title = "LineSeries with default style" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + var lineSeries1 = CreateExampleLineSeries(); + lineSeries1.Title = "LineSeries 1"; + model.Series.Add(lineSeries1); + + return model; + } + + [Example("Custom style")] + public static PlotModel CustomStyle() + { + var model = new PlotModel { Title = "LineSeries with custom style", LegendSymbolLength = 24 }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + var lineSeries1 = CreateExampleLineSeries(); + lineSeries1.Title = "LineSeries 1"; + lineSeries1.ToolTip = "This is a tooltip for a LineSeries 1"; + lineSeries1.Color = OxyColors.SkyBlue; + lineSeries1.StrokeThickness = 3; + lineSeries1.LineStyle = LineStyle.Dash; + lineSeries1.MarkerType = MarkerType.Circle; + lineSeries1.MarkerSize = 5; + lineSeries1.MarkerStroke = OxyColors.White; + lineSeries1.MarkerFill = OxyColors.SkyBlue; + lineSeries1.MarkerStrokeThickness = 1.5; + model.Series.Add(lineSeries1); + + return model; + } + + [Example("Two LineSeries")] + public static PlotModel TwoLineSeries() + { + var model = new PlotModel { Title = "Two LineSeries" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + var lineSeries1 = CreateExampleLineSeries(); + lineSeries1.Title = "LineSeries 1"; + model.Series.Add(lineSeries1); + + var lineSeries2 = CreateExampleLineSeries(41); + lineSeries2.Title = "LineSeries 2"; + model.Series.Add(lineSeries2); + return model; + } + + [Example("Visibility")] + public static PlotModel IsVisibleFalse() + { + var model = new PlotModel { Title = "LineSeries with IsVisible = false", Subtitle = "Click to change the IsVisible property for LineSeries 2" }; + + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = 0, Maximum = 50 }); + + var s1 = CreateExampleLineSeries(38); + s1.Title = "LineSeries 1"; + model.Series.Add(s1); + + var s2 = CreateExampleLineSeries(39); + s2.Title = "LineSeries 2"; + s2.IsVisible = false; + model.Series.Add(s2); + + // handle mouse clicks to change visibility + model.MouseDown += (s, e) => { s2.IsVisible = !s2.IsVisible; model.InvalidatePlot(true); }; + + return model; + } + + [Example("Custom TrackerFormatString")] + public static PlotModel TrackerFormatString() + { + var model = new PlotModel { Title = "LineSeries with custom TrackerFormatString", Subtitle = "TrackerFormatString = \"X={2:0.0} Y={4:0.0}\"" }; + + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + + var lineSeries1 = CreateExampleLineSeries(); + lineSeries1.TrackerFormatString = "X={2:0.0} Y={4:0.0}"; + model.Series.Add(lineSeries1); + return model; + } + + [Example("Custom markers")] + public static PlotModel CustomMarkers() + { + var model = new PlotModel { Title = "LineSeries with custom markers", LegendSymbolLength = 30 }; + + const int N = 6; + var customMarkerOutline = new ScreenPoint[N]; + for (int i = 0; i < N; i++) + { + double th = Math.PI * ((4.0 * i / (N - 1)) - 0.5); + const double R = 1; + customMarkerOutline[i] = new ScreenPoint(Math.Cos(th) * R, Math.Sin(th) * R); + } + + var s1 = CreateExampleLineSeries(39); + s1.Title = "LineSeries 1"; + s1.MarkerType = MarkerType.Custom; + s1.MarkerSize = 8; + s1.MarkerOutline = customMarkerOutline; + + model.Series.Add(s1); + + return model; + } + + [Example("Marker types")] + public static PlotModel MarkerTypes() + { + var pm = CreateModel("LineSeries with different MarkerType", (int)MarkerType.Custom); + pm.LegendBackground = OxyColor.FromAColor(220, OxyColors.White); + pm.LegendBorder = OxyColors.Black; + pm.LegendBorderThickness = 1.0; + int i = 0; + foreach (var ls in pm.Series.Cast()) + { + ls.Color = OxyColors.Red; + ls.MarkerStroke = OxyColors.Black; + ls.MarkerFill = OxyColors.Green; + ls.MarkerType = (MarkerType)i++; + ls.Title = ls.MarkerType.ToString(); + } + + return pm; + } + + [Example("Labels")] + public static PlotModel Labels() + { + var model = new PlotModel { Title = "LineSeries with labels", Subtitle = "Use the 'LabelFormatString' property", LegendSymbolLength = 24 }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, MaximumPadding = 0.1 }); // increase the top padding to make sure the labels are visible + var s1 = CreateExampleLineSeries(); + s1.LabelFormatString = "{1}"; + s1.MarkerType = MarkerType.Circle; + model.Series.Add(s1); + return model; + } + + [Example("LineStyle")] + public static PlotModel LineStyles() + { + var pm = CreateModel("LineSeries with LineStyle", (int)LineStyle.None); + pm.LegendPlacement = LegendPlacement.Outside; + pm.LegendSymbolLength = 50; + int i = 0; + foreach (var lineSeries in pm.Series.Cast()) + { + lineSeries.Color = OxyColors.Red; + lineSeries.LineStyle = (LineStyle)i++; + lineSeries.Title = lineSeries.LineStyle.ToString(); + } + + return pm; + } + + [Example("Interpolation")] + public static PlotModel Smooth() + { + var model = new PlotModel { Title = "LineSeries with interpolation", Subtitle = "InterpolationAlgorithm = CanonicalSpline", LegendSymbolLength = 24 }; + + var s1 = CreateExampleLineSeries(); + s1.MarkerType = MarkerType.Circle; + s1.InterpolationAlgorithm = InterpolationAlgorithms.CanonicalSpline; + model.Series.Add(s1); + + return model; + } + + [Example("LineLegendPosition")] + public static PlotModel CustomLineLegendPosition() + { + var model = new PlotModel { Title = "LineSeries with LineLegendPosition" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, MinimumPadding = 0.1, MaximumPadding = 0.1 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + var s1 = CreateExampleLineSeries(); + s1.Title = "Start"; + s1.MarkerType = MarkerType.Circle; + s1.LineLegendPosition = LineLegendPosition.Start; + model.Series.Add(s1); + + var s2 = CreateExampleLineSeries(41); + s2.Title = "End"; + s2.MarkerType = MarkerType.Circle; + s2.LineLegendPosition = LineLegendPosition.End; + model.Series.Add(s2); + + return model; + } + + [Example("Broken lines")] + public static PlotModel BrokenLine() + { + var model = new PlotModel { Title = "LineSeries with broken lines" }; + + var s1 = CreateExampleLineSeries(); + s1.Points[3] = DataPoint.Undefined; + s1.Points[7] = DataPoint.Undefined; + s1.BrokenLineColor = OxyColors.Gray; + s1.BrokenLineThickness = 0.5; + s1.BrokenLineStyle = LineStyle.Solid; + model.Series.Add(s1); + + var s2 = CreateExampleLineSeries(49); + s2.Points[3] = DataPoint.Undefined; + s2.Points[7] = DataPoint.Undefined; + s2.BrokenLineColor = OxyColors.Automatic; + s2.BrokenLineThickness = 1; + s2.BrokenLineStyle = LineStyle.Dot; + model.Series.Add(s2); + + return model; + } + + [Example("Without Decimator")] + public static PlotModel WithoutDecimator() + { + var model = new PlotModel { Title = "LineSeries without Decimator" }; + var s1 = CreateSeriesSuitableForDecimation(); + model.Series.Add(s1); + return model; + } + + [Example("With X Decimator")] + public static PlotModel WithXDecimator() + { + var model = new PlotModel { Title = "LineSeries with X Decimator" }; + var s1 = CreateSeriesSuitableForDecimation(); + s1.Decimator = Decimator.Decimate; + model.Series.Add(s1); + return model; + } + + [Example("Canonical spline interpolation")] + public static PlotModel CanonicalSplineInterpolation() + { + var result = CreateRandomPoints(); + + var plotModel = new PlotModel + { + Title = "Canonical spline interpolation", + Series = + { + new LineSeries + { + ItemsSource = result, + Title = "Default (0.5)", + InterpolationAlgorithm = InterpolationAlgorithms.CanonicalSpline, + MarkerType = MarkerType.Circle, + MarkerFill = OxyColors.Black + }, + new LineSeries + { + ItemsSource = result, + Title = "0.1", + InterpolationAlgorithm = new CanonicalSpline(0.1) + }, + new LineSeries + { + ItemsSource = result, + Title = "1.0", + InterpolationAlgorithm = new CanonicalSpline(1) + } + } + }; + + return plotModel; + } + + [Example("Catmull-Rom interpolation")] + public static PlotModel CatmullRomInterpolation() + { + var result = CreateRandomPoints(); + + var plotModel = new PlotModel + { + Title = "Catmull-Rom interpolation", + Series = + { + new LineSeries + { + ItemsSource = result, + Title = "Standard", + InterpolationAlgorithm = InterpolationAlgorithms.CatmullRomSpline, + MarkerType = MarkerType.Circle, + MarkerFill = OxyColors.Black + }, + new LineSeries + { + ItemsSource = result, + Title = "Chordal", + InterpolationAlgorithm = InterpolationAlgorithms.ChordalCatmullRomSpline + }, + new LineSeries + { + ItemsSource = result, + Title = "Uniform", + InterpolationAlgorithm = InterpolationAlgorithms.UniformCatmullRomSpline + } + } + }; + + return plotModel; + } + + [Example("Marker color options")] + public static PlotModel MarkerColorOptions() + { + var result = CreateRandomPoints(); + + var model = new PlotModel { Title = "Marker color options" }; + + // Dont specify line or marker color. Defaults will be used. + var s1 = CreateExampleLineSeries(1); + s1.MarkerType = MarkerType.Circle; + model.Series.Add(s1); + + // Specify line color but not marker color. Marker color should be the same as line color. + var s2 = CreateExampleLineSeries(4); + s2.MarkerType = MarkerType.Square; + s2.Color = OxyColors.LightBlue; + model.Series.Add(s2); + + // Specify marker color but not line color. Default color should be used for line. + var s3 = CreateExampleLineSeries(13); + s3.MarkerType = MarkerType.Square; + s3.MarkerFill = OxyColors.Black; + model.Series.Add(s3); + + // Specify line and marker color. Specified colors should be used. + var s4 = CreateExampleLineSeries(5); + s4.MarkerType = MarkerType.Square; + s4.MarkerFill = OxyColors.OrangeRed; + s4.Color = OxyColors.Orange; + model.Series.Add(s4); + + return model; + } + + private static List CreateRandomPoints(int numberOfPoints = 50) + { + var r = new Random(13); + var result = new List(numberOfPoints); + for (int i = 0; i < numberOfPoints; i++) + { + if (i < 5) + { + result.Add(new DataPoint(i, 0.0)); + } + else if (i < 10) + { + result.Add(new DataPoint(i, 1.0)); + } + else if (i < 12) + { + result.Add(new DataPoint(i, 0.0)); + } + else + { + result.Add(new DataPoint(i, r.NextDouble())); + } + } + return result; + } + + /// + /// Creates an example line series. + /// + /// A line series containing random points. + private static LineSeries CreateExampleLineSeries(int seed = 13) + { + var lineSeries1 = new LineSeries(); + var r = new Random(seed); + var y = r.Next(10, 30); + for (int x = 0; x <= 100; x += 10) + { + lineSeries1.Points.Add(new DataPoint(x, y)); + y += r.Next(-5, 5); + } + + return lineSeries1; + } + + private static PlotModel CreateModel(string title, int n = 20) + { + var model = new PlotModel { Title = title }; + for (int i = 1; i <= n; i++) + { + var s = new LineSeries { Title = "Series " + i }; + model.Series.Add(s); + for (double x = 0; x < 2 * Math.PI; x += 0.1) + { + s.Points.Add(new DataPoint(x, (Math.Sin(x * i) / (i + 1)) + i)); + } + } + + return model; + } + + private static LineSeries CreateSeriesSuitableForDecimation() + { + var s1 = new LineSeries(); + + int n = 20000; + for (int i = 0; i < n; i++) + { + s1.Points.Add(new DataPoint((double)i / n, Math.Sin(i))); + } + + return s1; + } + + private static OxyPlot.Series.Series CreateRandomLineSeries(int n, string title, MarkerType markerType) + { + var s1 = new LineSeries { Title = title, MarkerType = markerType, MarkerStroke = OxyColors.Black, MarkerStrokeThickness = 1.0 }; + double x = 0; + double y = 0; + for (int i = 0; i < n; i++) + { + x += 2 + Randomizer.NextDouble() * 10; + y += 1 + Randomizer.NextDouble(); + var p = new DataPoint(x, y); + s1.Points.Add(p); + } + + return s1; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Series/LinearBarSeriesExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Series/LinearBarSeriesExamples.cs new file mode 100644 index 0000000..5d7d8fe --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Series/LinearBarSeriesExamples.cs @@ -0,0 +1,94 @@ +namespace ExampleLibrary +{ + using System; + + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("LinearBarSeries"), Tags("Series")] + public class LinearBarSeriesExamples + { + //private static readonly Random Randomizer = new Random(13); + + [Example("Default style")] + public static PlotModel DefaultStyle() + { + var model = new PlotModel { Title = "LinearBarSeries with default style" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + var linearBarSeries = CreateExampleLinearBarSeries(); + linearBarSeries.Title = "LinearBarSeries"; + model.Series.Add(linearBarSeries); + + return model; + } + + [Example("With stroke")] + public static PlotModel WithStroke() + { + var model = new PlotModel { Title = "LinearBarSeries with stroke" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + var linearBarSeries = CreateExampleLinearBarSeries(); + linearBarSeries.Title = "LinearBarSeries"; + linearBarSeries.FillColor = OxyColor.Parse("#454CAF50"); + linearBarSeries.StrokeColor = OxyColor.Parse("#4CAF50"); + linearBarSeries.StrokeThickness = 1; + model.Series.Add(linearBarSeries); + + return model; + } + + [Example("With negative colors")] + public static PlotModel WithNegativeColors() + { + var model = new PlotModel { Title = "LinearBarSeries with stroke" }; + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + var linearBarSeries = CreateExampleLinearBarSeriesWithNegativeValues(); + linearBarSeries.Title = "LinearBarSeries"; + linearBarSeries.FillColor = OxyColor.Parse("#454CAF50"); + linearBarSeries.StrokeColor = OxyColor.Parse("#4CAF50"); + linearBarSeries.NegativeFillColor = OxyColor.Parse("#45BF360C"); + linearBarSeries.NegativeStrokeColor = OxyColor.Parse("#BF360C"); + linearBarSeries.StrokeThickness = 1; + model.Series.Add(linearBarSeries); + + return model; + } + + /// + /// Creates an example linear bar series. + /// + /// A linear bar series containing random points. + private static LinearBarSeries CreateExampleLinearBarSeries() + { + var linearBarSeries = new LinearBarSeries(); + var r = new Random(31); + var y = r.Next(10, 30); + for (int x = 0; x <= 50; x++) + { + linearBarSeries.Points.Add(new DataPoint(x, y)); + y += r.Next(-5, 5); + } + return linearBarSeries; + } + + /// + /// Creates an example linear bar series with negative values. + /// + /// A linear bar series containing random points. + private static LinearBarSeries CreateExampleLinearBarSeriesWithNegativeValues() + { + var linearBarSeries = new LinearBarSeries(); + var r = new Random(31); + for (int x = 0; x <= 50; x++) + { + var y = -200 + r.Next(1000); + linearBarSeries.Points.Add(new DataPoint(x, y)); + } + return linearBarSeries; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Series/PieSeriesExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Series/PieSeriesExamples.cs new file mode 100644 index 0000000..9c5810e --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Series/PieSeriesExamples.cs @@ -0,0 +1,54 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using OxyPlot; + using OxyPlot.Series; + + [Examples("PieSeries"), Tags("Series")] + public static class PieSeriesExamples + { + [Example("PieSeries")] + public static PlotModel PieSeries() + { + return CreateExample(); + } + + [Example("PieSeries with inside label color")] + public static PlotModel InsideLabelColor() + { + var model = CreateExample(); + var series = (PieSeries)model.Series[0]; + series.InsideLabelColor = OxyColors.White; + return model; + } + + private static PlotModel CreateExample() + { + var model = new PlotModel { Title = "World population by continent" }; + + var ps = new PieSeries + { + StrokeThickness = 2.0, + InsideLabelPosition = 0.8, + AngleSpan = 360, + StartAngle = 0 + }; + + // http://www.nationsonline.org/oneworld/world_population.htm + // http://en.wikipedia.org/wiki/Continent + ps.Slices.Add(new PieSlice("Africa", 1030) { IsExploded = true }); + ps.Slices.Add(new PieSlice("Americas", 929) { IsExploded = true }); + ps.Slices.Add(new PieSlice("Asia", 4157)); + ps.Slices.Add(new PieSlice("Europe", 739) { IsExploded = true }); + ps.Slices.Add(new PieSlice("Oceania", 35) { IsExploded = true }); + + model.Series.Add(ps); + return model; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Series/RectangleBarSeriesExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Series/RectangleBarSeriesExamples.cs new file mode 100644 index 0000000..911fa4b --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Series/RectangleBarSeriesExamples.cs @@ -0,0 +1,33 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using OxyPlot; + using OxyPlot.Series; + + [Examples("RectangleBarSeries"), Tags("Series")] + public static class RectangleBarSeriesExamples + { + [Example("RectangleBarSeries")] + public static PlotModel RectangleBarSeries() + { + var model = new PlotModel { Title = "RectangleBarSeries", LegendPlacement = LegendPlacement.Outside }; + + var s1 = new RectangleBarSeries { Title = "RectangleBarSeries 1" }; + s1.Items.Add(new RectangleBarItem { X0 = 2, X1 = 8, Y0 = 1, Y1 = 4 }); + s1.Items.Add(new RectangleBarItem { X0 = 6, X1 = 12, Y0 = 6, Y1 = 7 }); + model.Series.Add(s1); + + var s2 = new RectangleBarSeries { Title = "RectangleBarSeries 2" }; + s2.Items.Add(new RectangleBarItem { X0 = 2, X1 = 8, Y0 = -4, Y1 = -1 }); + s2.Items.Add(new RectangleBarItem { X0 = 6, X1 = 12, Y0 = -7, Y1 = -6 }); + model.Series.Add(s2); + + return model; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Series/RectangleSeriesExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Series/RectangleSeriesExamples.cs new file mode 100644 index 0000000..0fd7065 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Series/RectangleSeriesExamples.cs @@ -0,0 +1,76 @@ +namespace ExampleLibrary +{ + using System.Collections.Generic; + + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("RectangleSeries"), Tags("Series")] + public static class RectangleSeriesExamples + { + [Example("RectangleSeries")] + public static PlotModel FromItems() + { + const int NumberOfItems = 10; + var model = new PlotModel { Title = "RectangleSeries" }; + + // the RectangleSeries requires a color axis + model.Axes.Add(new LinearColorAxis + { + Position = AxisPosition.Right, + Palette = OxyPalettes.Jet(100) + }); + + // create the series and add some rectangles with values + var s = new RectangleSeries(); + for (int i = 0; i < NumberOfItems; i++) + { + s.Items.Add(new RectangleItem(i, i * 2, i, i * 2, i)); + } + + model.Series.Add(s); + + return model; + } + + [Example("RectangleSeries from ItemsSource and Mapping")] + public static PlotModel FromItemsSource() + { + const int NumberOfItems = 10; + var model = new PlotModel { Title = "RectangleSeries" }; + + // the RectangleSeries requires a color axis + model.Axes.Add(new LinearColorAxis + { + Position = AxisPosition.Right, + Palette = OxyPalettes.Jet(100) + }); + + // create the data + var items = new List(); + for (int i = 0; i < NumberOfItems; i++) + { + items.Add(new MyItem { X = i, Value = i }); + } + + model.Series.Add(new RectangleSeries + { + ItemsSource = items, + Mapping = x => + { + var r = (MyItem)x; + return new RectangleItem(r.X, r.X * 2, r.X, r.X * 2, r.Value); + } + }); + + return model; + } + + public class MyItem + { + public double X { get; set; } + public double Value { get; set; } + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Series/ScatterErrorSeriesExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Series/ScatterErrorSeriesExamples.cs new file mode 100644 index 0000000..954ec44 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Series/ScatterErrorSeriesExamples.cs @@ -0,0 +1,167 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Creates an example model with the specified number of points. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + using System.Collections.Generic; + using System.Linq; + + using OxyPlot; + using OxyPlot.Series; + + [Examples("ScatterErrorSeries"), Tags("Series")] + public class ScatterErrorSeriesExamples + { + [Example("Random points and errors (n=20)")] + public static PlotModel RandomPointsAndError20() + { + return RandomPointsAndError(20); + } + + [Example("Random points and errors (n=2000)")] + public static PlotModel RandomPointsAndError2000() + { + return RandomPointsAndError(2000); + } + + [Example("Definining points by ItemsSource and Mapping")] + public static PlotModel ItemsSourceMapping() + { + const int N = 20; + var model = new PlotModel { Title = "ScatterErrorSeries, points defined by ItemsSource and Mapping", Subtitle = string.Format("Random data (n={0})", N), LegendPosition = LegendPosition.LeftTop }; + model.Series.Add(new ScatterErrorSeries + { + Title = "Measurements", + ItemsSource = CreateExamplePoints(N).ToArray(), + Mapping = + obj => + { + var p = (ExamplePoint)obj; + return new ScatterErrorPoint(p.V1, p.V2, p.E1, p.E2); + } + }); + return model; + } + + [Example("Defining points by ItemsSource (List)")] + public static PlotModel ItemsSourceList() + { + const int N = 20; + var model = new PlotModel { Title = "ScatterErrorSeries, points defined by ItemsSource (List)", Subtitle = string.Format("Random data (n={0})", N), LegendPosition = LegendPosition.LeftTop }; + model.Series.Add(new ScatterErrorSeries { Title = "Measurements", ItemsSource = CreateScatterErrorPoints(N).ToList() }); + return model; + } + + [Example("Defining points by ItemsSource (IEnumerable)")] + public static PlotModel ItemsSourceEnumerable() + { + const int N = 20; + var model = new PlotModel { Title = "ScatterErrorSeries, points defined by ItemsSource (IEnumerable)", Subtitle = string.Format("Random data (n={0})", N), LegendPosition = LegendPosition.LeftTop }; + model.Series.Add(new ScatterErrorSeries { Title = "Measurements", ItemsSource = CreateScatterErrorPoints(N).ToArray() }); + return model; + } + + [Example("Definining points by ItemsSource and reflection")] + public static PlotModel ItemsSourceReflection() + { + const int N = 20; + var model = new PlotModel { Title = "ScatterErrorSeries, points defined by ItemsSource (reflection)", Subtitle = string.Format("Random data (n={0})", N), LegendPosition = LegendPosition.LeftTop }; + model.Series.Add(new ScatterErrorSeries + { + Title = "Measurements", + ItemsSource = CreateExamplePoints(N).ToArray(), + DataFieldX = "V1", + DataFieldY = "V2", + DataFieldErrorX = "E1", + DataFieldErrorY = "E2" + }); + return model; + } + + /// + /// Creates an example model with the specified number of points. + /// + /// The n. + /// A plot model. + private static PlotModel RandomPointsAndError(int n) + { + var model = new PlotModel { Title = "ScatterErrorSeries", Subtitle = string.Format("Random data (n={0})", n), LegendPosition = LegendPosition.LeftTop }; + + var s1 = new ScatterErrorSeries { Title = "Measurements" }; + s1.Points.AddRange(CreateScatterErrorPoints(n)); + model.Series.Add(s1); + return model; + } + + /// + /// Creates random example data. + /// + /// The number of points to generate. + /// A sequence of points. + private static IEnumerable CreateScatterErrorPoints(int n) + { + var random = new Random(27); + double x = 0; + double y = 0; + for (int i = 0; i < n; i++) + { + x += 2 + random.NextDouble(); + y += 1 + random.NextDouble(); + + yield return new ScatterErrorPoint(x, y, random.NextDouble(), random.NextDouble()); + } + } + + /// + /// Creates random example data. + /// + /// The number of points to generate. + /// A sequence of points. + private static IEnumerable CreateExamplePoints(int n) + { + var random = new Random(27); + double x = 0; + double y = 0; + for (int i = 0; i < n; i++) + { + x += 2 + random.NextDouble(); + y += 1 + random.NextDouble(); + + yield return new ExamplePoint { V1 = x, V2 = y, E1 = random.NextDouble(), E2 = random.NextDouble() }; + } + } + + /// + /// Represents a point with errors. + /// + public class ExamplePoint + { + /// + /// Gets or sets the first value. + /// + public double V1 { get; set; } + + /// + /// Gets or sets the second value. + /// + public double V2 { get; set; } + + /// + /// Gets or sets the first error. + /// + public double E1 { get; set; } + + /// + /// Gets or sets the second error. + /// + public double E2 { get; set; } + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Series/ScatterSeriesExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Series/ScatterSeriesExamples.cs new file mode 100644 index 0000000..e34f822 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Series/ScatterSeriesExamples.cs @@ -0,0 +1,562 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Calculates the Least squares fit of a list of DataPoints. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + using System.Collections.Generic; + using System.Linq; + + using OxyPlot; + using OxyPlot.Annotations; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("ScatterSeries"), Tags("Series")] + public class ScatterSeriesExamples + { + [Example("Random points")] + public static PlotModel RandomScatter() + { + return RandomScatter(32768, 0); + } + + [Example("Random points (BinSize=2)")] + public static PlotModel RandomScatter2() + { + return RandomScatter(32768, 2); + } + + [Example("Random points (BinSize=4)")] + public static PlotModel RandomScatter4() + { + return RandomScatter(32768, 4); + } + + [Example("Random points (BinSize=6)")] + public static PlotModel RandomScatter6() + { + return RandomScatter(32768, 6); + } + + [Example("Random points (BinSize=8)")] + public static PlotModel RandomScatter8() + { + return RandomScatter(32768, 8); + } + + [Example("Random points (BinSize=10)")] + public static PlotModel RandomScatter10() + { + return RandomScatter(32768, 10); + } + + [Example("Two ScatterSeries")] + public static PlotModel TwoScatterSeries() + { + var model = new PlotModel { Title = "Two ScatterSeries (with and without values)", Subtitle = "With values (squares), without values (triangles)" }; + var colorAxis = new LinearColorAxis { Position = AxisPosition.Right, Key = "ColorAxis", Palette = OxyPalettes.Jet(30), Minimum = -1, Maximum = 1 }; + model.Axes.Add(colorAxis); + model.Series.Add(CreateRandomScatterSeries(50, MarkerType.Triangle, false, false, null)); + model.Series.Add(CreateRandomScatterSeries(50, MarkerType.Square, false, true, colorAxis)); + return model; + } + + [Example("LabelFormatString")] + public static PlotModel LabelFormatString() + { + var model = new PlotModel { Title = "ScatterSeries with LabelFormatString" }; + var s = CreateRandomScatterSeries(50, MarkerType.Square, false, false, null); + s.LabelFormatString = "{1:0.###}"; + model.Series.Add(s); + return model; + } + + private static PlotModel CreateRandomScatterSeriesWithColorAxisPlotModel(int n, OxyPalette palette, MarkerType markerType, AxisPosition colorAxisPosition, OxyColor highColor, OxyColor lowColor) + { + var model = new PlotModel { Title = string.Format("ScatterSeries (n={0})", n), Background = OxyColors.LightGray }; + var colorAxis = new LinearColorAxis { Position = colorAxisPosition, Palette = palette, Minimum = -1, Maximum = 1, HighColor = highColor, LowColor = lowColor }; + model.Axes.Add(colorAxis); + model.Series.Add(CreateRandomScatterSeries(n, markerType, false, true, colorAxis)); + return model; + } + + private static ScatterSeries CreateRandomScatterSeries(int n, MarkerType markerType, bool setSize, bool setValue, LinearColorAxis colorAxis) + { + var s1 = new ScatterSeries + { + MarkerType = markerType, + MarkerSize = 6, + ColorAxisKey = colorAxis != null ? colorAxis.Key : null + }; + var random = new Random(13); + for (int i = 0; i < n; i++) + { + var p = new ScatterPoint((random.NextDouble() * 2.2) - 1.1, random.NextDouble()); + if (setSize) + { + p.Size = (random.NextDouble() * 5) + 5; + } + + if (setValue) + { + p.Value = (random.NextDouble() * 2.2) - 1.1; + } + + s1.Points.Add(p); + } + + return s1; + } + + [Example("Random points with random size")] + public static PlotModel RandomSize() + { + return RandomSize(1000, 8); + } + + public static PlotModel RandomSize(int n, int binsize) + { + var model = new PlotModel { Title = string.Format("ScatterSeries with random MarkerSize (n={0})", n), Subtitle = "BinSize = " + binsize }; + + var s1 = new ScatterSeries { Title = "Series 1", MarkerStrokeThickness = 0, BinSize = binsize }; + var random = new Random(13); + for (int i = 0; i < n; i++) + { + s1.Points.Add(new ScatterPoint(random.NextDouble(), random.NextDouble(), 4 + (10 * random.NextDouble()))); + } + + model.Series.Add(s1); + return model; + } + + [Example("Random points with least squares fit")] + public static PlotModel RandomWithFit() + { + const int n = 20; + var model = new PlotModel { Title = string.Format("Random data (n={0})", n), LegendPosition = LegendPosition.LeftTop }; + + var s1 = new ScatterSeries { Title = "Measurements" }; + var random = new Random(7); + double x = 0; + double y = 0; + for (int i = 0; i < n; i++) + { + x += 2 + (random.NextDouble() * 10); + y += 1 + random.NextDouble(); + var p = new ScatterPoint(x, y); + s1.Points.Add(p); + } + + model.Series.Add(s1); + double a, b; + LeastSquaresFit(s1.Points, out a, out b); + model.Annotations.Add(new LineAnnotation { Slope = a, Intercept = b, Text = "Least squares fit" }); + return model; + } + + /// + /// Calculates the Least squares fit of a list of DataPoints. + /// + /// The points. + /// The slope. + /// The intercept. + public static void LeastSquaresFit(IEnumerable points, out double a, out double b) + { + // http://en.wikipedia.org/wiki/Least_squares + // http://mathworld.wolfram.com/LeastSquaresFitting.html + // http://web.cecs.pdx.edu/~gerry/nmm/course/slides/ch09Slides4up.pdf + + double Sx = 0; + double Sy = 0; + double Sxy = 0; + double Sxx = 0; + int m = 0; + foreach (var p in points) + { + Sx += p.X; + Sy += p.Y; + Sxy += p.X * p.Y; + Sxx += p.X * p.X; + m++; + } + + double d = Sx * Sx - m * Sxx; + a = 1 / d * (Sx * Sy - m * Sxy); + b = 1 / d * (Sx * Sxy - Sxx * Sy); + } + + [Example("Marker types")] + public static PlotModel MarkerTypes() + { + var model = new PlotModel { Title = "Marker types" }; + var r = new Random(12345); + model.Series.Add(CreateRandomScatterSeries(r, 10, "Circle", MarkerType.Circle)); + model.Series.Add(CreateRandomScatterSeries(r, 10, "Cross", MarkerType.Cross)); + model.Series.Add(CreateRandomScatterSeries(r, 10, "Diamond", MarkerType.Diamond)); + model.Series.Add(CreateRandomScatterSeries(r, 10, "Plus", MarkerType.Plus)); + model.Series.Add(CreateRandomScatterSeries(r, 10, "Square", MarkerType.Square)); + model.Series.Add(CreateRandomScatterSeries(r, 10, "Star", MarkerType.Star)); + model.Series.Add(CreateRandomScatterSeries(r, 10, "Triangle", MarkerType.Triangle)); + return model; + } + + [Example("ScatterSeries.Points")] + public static PlotModel DataPoints() + { + var model = new PlotModel { Title = "ScatterSeries (n=1000)", Subtitle = "The scatter points are added to the Points collection." }; + var series = new ScatterSeries(); + series.Points.AddRange(CreateRandomScatterPoints(1000)); + model.Series.Add(series); + return model; + } + + [Example("ScatterSeries.ItemsSource")] + public static PlotModel FromItemsSource() + { + var model = new PlotModel { Title = "ScatterSeries (n=1000)", Subtitle = "The scatter points are defined in the ItemsSource property." }; + model.Series.Add(new ScatterSeries + { + ItemsSource = CreateRandomScatterPoints(1000), + }); + return model; + } + + [Example("ScatterSeries.ItemsSource + Mapping")] + public static PlotModel FromMapping() + { + var model = new PlotModel { Title = "ScatterSeries (n=1000)", Subtitle = "The scatter points are defined by a mapping from the ItemsSource." }; + model.Series.Add(new ScatterSeries + { + ItemsSource = CreateRandomDataPoints(1000), + Mapping = item => new ScatterPoint(((DataPoint)item).X, ((DataPoint)item).Y) + }); + return model; + } + + [Example("ScatterSeries.ItemsSource + reflection")] + public static PlotModel FromItemsSourceReflection() + { + var model = new PlotModel { Title = "ScatterSeries (n=1000)", Subtitle = "The scatter points are defined by reflection from the ItemsSource." }; + model.Series.Add(new ScatterSeries + { + ItemsSource = CreateRandomDataPoints(1000), + DataFieldX = "X", + DataFieldY = "Y" + }); + return model; + } + + [Example("ScatterSeries with ColorAxis Rainbow(16)")] + public static PlotModel ColorMapRainbow16() + { + return CreateRandomScatterSeriesWithColorAxisPlotModel(2500, OxyPalettes.Rainbow(16), MarkerType.Square, AxisPosition.Right, OxyColors.Undefined, OxyColors.Undefined); + } + + [Example("ScatterSeries with ColorAxis Hue(30) Star")] + public static PlotModel ColorMapHue30() + { + return CreateRandomScatterSeriesWithColorAxisPlotModel(2500, OxyPalettes.Hue(30), MarkerType.Star, AxisPosition.Right, OxyColors.Undefined, OxyColors.Undefined); + } + + [Example("ScatterSeries with ColorAxis Hot(64)")] + public static PlotModel ColorMapHot64() + { + return CreateRandomScatterSeriesWithColorAxisPlotModel(2500, OxyPalettes.Hot(64), MarkerType.Triangle, AxisPosition.Right, OxyColors.Undefined, OxyColors.Undefined); + } + + [Example("ScatterSeries with ColorAxis Cool(32)")] + public static PlotModel ColorMapCool32() + { + return CreateRandomScatterSeriesWithColorAxisPlotModel(2500, OxyPalettes.Cool(32), MarkerType.Circle, AxisPosition.Right, OxyColors.Undefined, OxyColors.Undefined); + } + + [Example("ScatterSeries with ColorAxis Gray(32)")] + public static PlotModel ColorMapGray32() + { + return CreateRandomScatterSeriesWithColorAxisPlotModel(2500, OxyPalettes.Gray(32), MarkerType.Diamond, AxisPosition.Right, OxyColors.Undefined, OxyColors.Undefined); + } + + [Example("ScatterSeries with ColorAxis Jet(32)")] + public static PlotModel ColorMapJet32() + { + return CreateRandomScatterSeriesWithColorAxisPlotModel(2500, OxyPalettes.Jet(32), MarkerType.Plus, AxisPosition.Right, OxyColors.Undefined, OxyColors.Undefined); + } + + [Example("ScatterSeries with ColorAxis Hot with extreme colors")] + public static PlotModel ColorMapHot64Extreme() + { + return CreateRandomScatterSeriesWithColorAxisPlotModel(2500, OxyPalettes.Hot(64), MarkerType.Square, AxisPosition.Right, OxyColors.Magenta, OxyColors.Green); + } + + [Example("ScatterSeries with ColorAxis Hot (top legend)")] + public static PlotModel ColorMapHot64ExtremeTopLegend() + { + return CreateRandomScatterSeriesWithColorAxisPlotModel(2500, OxyPalettes.Hot(64), MarkerType.Cross, AxisPosition.Top, OxyColors.Magenta, OxyColors.Green); + } + [Example("ScatterSeries with ColorAxis Hot(16) N=31000")] + public static PlotModel ColorMapHot16Big() + { + return CreateRandomScatterSeriesWithColorAxisPlotModel(31000, OxyPalettes.Hot(16), MarkerType.Square, AxisPosition.Right, OxyColors.Undefined, OxyColors.Undefined); + } + + [Example("ScatterSeries with ColorAxis BlueWhiteRed (3)")] + public static PlotModel ColorMapBlueWhiteRed3() + { + return CreateRandomScatterSeriesWithColorAxisPlotModel(2500, OxyPalettes.BlueWhiteRed(3), MarkerType.Square, AxisPosition.Right, OxyColors.Undefined, OxyColors.Undefined); + } + + [Example("ScatterSeries with ColorAxis BlueWhiteRed (9)")] + public static PlotModel ColorMapBlueWhiteRed9() + { + return CreateRandomScatterSeriesWithColorAxisPlotModel(2500, OxyPalettes.BlueWhiteRed(9), MarkerType.Square, AxisPosition.Right, OxyColors.Undefined, OxyColors.Undefined); + } + + [Example("ScatterSeries with ColorAxis BlueWhiteRed (256)")] + public static PlotModel ColorMapBlueWhiteRed256() + { + return CreateRandomScatterSeriesWithColorAxisPlotModel(2500, OxyPalettes.BlueWhiteRed(256), MarkerType.Square, AxisPosition.Right, OxyColors.Undefined, OxyColors.Undefined); + } + + [Example("ScatterSeries with ColorAxis BlackWhiteRed (9)")] + public static PlotModel ColorMapBlackWhiteRed9() + { + return CreateRandomScatterSeriesWithColorAxisPlotModel(2500, OxyPalettes.BlackWhiteRed(9), MarkerType.Square, AxisPosition.Right, OxyColors.Undefined, OxyColors.Undefined); + } + + [Example("ScatterSeries with ColorAxis BlackWhiteRed (9) top legend")] + public static PlotModel ColorMapBlackWhiteRed9TopLegend() + { + return CreateRandomScatterSeriesWithColorAxisPlotModel(2500, OxyPalettes.BlackWhiteRed(9), MarkerType.Square, AxisPosition.Top, OxyColors.Undefined, OxyColors.Undefined); + } + + [Example("ScatterSeries with single-selected items")] + public static PlotModel SingleSelectItems() + { + var model = RandomScatter(10, 8); + model.Subtitle = "Click to select a point"; + + model.SelectionColor = OxyColors.Red; + + var series = model.Series[0]; + + series.SelectionMode = SelectionMode.Single; + + series.SelectItem(3); + series.SelectItem(5); + + series.MouseDown += (s, e) => + { + var index = (int)e.HitTestResult.Index; + series.SelectItem(index); + model.InvalidatePlot(false); + e.Handled = true; + }; + model.MouseDown += (s, e) => + { + series.ClearSelection(); + model.InvalidatePlot(false); + e.Handled = true; + }; + + return model; + } + + [Example("ScatterSeries with multi-selected items")] + public static PlotModel MultiSelectItems() + { + var model = RandomScatter(10, 8); + model.Subtitle = "Click to toggle point selection"; + + model.SelectionColor = OxyColors.Red; + + var series = model.Series[0]; + + series.SelectionMode = SelectionMode.Multiple; + + series.SelectItem(3); + series.SelectItem(5); + + series.MouseDown += (s, e) => + { + var index = (int)e.HitTestResult.Index; + + // Toggle the selection state for this item + if (series.IsItemSelected(index)) + { + series.UnselectItem(index); + } + else + { + series.SelectItem(index); + } + + model.InvalidatePlot(false); + e.Handled = true; + }; + + model.MouseDown += (s, e) => + { + series.ClearSelection(); + model.InvalidatePlot(false); + e.Handled = true; + }; + + return model; + } + + [Example("ScatterSeries with SelectionMode.All (no tracker)")] + public static PlotModel AllSelected() + { + return AllSelected(false); + } + + [Example("ScatterSeries with SelectionMode.All (with tracker)")] + public static PlotModel AllSelectedWithTracker() + { + return AllSelected(true); + } + + private static PlotModel AllSelected(bool showTracker) + { + var model = RandomScatter(10, 8); + model.Subtitle = "Click to select all points"; + + model.SelectionColor = OxyColors.Red; + + var series = model.Series[0]; + + series.SelectionMode = SelectionMode.All; + + series.MouseDown += (s, e) => + { + series.Select(); + model.InvalidatePlot(false); + e.Handled = !showTracker; + }; + + model.MouseDown += (s, e) => + { + if (e.HitTestResult != null && showTracker) + { + return; + } + + series.ClearSelection(); + model.InvalidatePlot(false); + e.Handled = true; + }; + + return model; + } + + [Example("TrackerFormatString")] + public static PlotModel TrackerFormatString() + { + var model = new PlotModel { Title = "TrackerFormatString" }; + + var s1 = new ScatterSeries { TrackerFormatString = "{Sum:0.0}", DataFieldX = "X", DataFieldY = "Y" }; + var myPoints = new List + { + new MyPoint { X = 10, Y = 40 }, + new MyPoint { X = 40, Y = 20 }, + new MyPoint { X = 60, Y = 30 } + }; + s1.ItemsSource = myPoints; + model.Series.Add(s1); + return model; + } + + public struct MyPoint + { + public double X { get; set; } + + public double Y { get; set; } + + public double Sum + { + get + { + // calculated on request + return this.X + this.Y; + } + } + } + + private static PlotModel RandomScatter(int n, int binSize) + { + var model = new PlotModel { Title = string.Format("ScatterSeries (n={0})", n), Subtitle = binSize > 0 ? "BinSize = " + binSize : "No 'binning'" }; + + var s1 = new ScatterSeries() + { + Title = "Series 1", + MarkerType = MarkerType.Diamond, + MarkerStrokeThickness = 0, + BinSize = binSize + }; + + var random = new Random(1); + for (int i = 0; i < n; i++) + { + s1.Points.Add(new ScatterPoint(random.NextDouble(), random.NextDouble())); + } + + model.Series.Add(s1); + return model; + } + + private static ScatterSeries CreateRandomScatterSeries(Random r, int n, string title, MarkerType markerType) + { + var s1 = new ScatterSeries { Title = title, MarkerType = markerType, MarkerStroke = OxyColors.Black, MarkerStrokeThickness = 1.0 }; + for (int i = 0; i < n; i++) + { + double x = r.NextDouble() * 10; + double y = r.NextDouble() * 10; + var p = new ScatterPoint(x, y); + s1.Points.Add(p); + } + + return s1; + } + + private static ScatterSeries CreateRandomScatterSeries2(int n, string title, MarkerType markerType) + { + var series = new ScatterSeries + { + Title = title, + MarkerType = markerType, + MarkerStroke = OxyColors.Black, + MarkerStrokeThickness = 1.0, + }; + series.Points.AddRange(CreateRandomScatterPoints(n)); + return series; + } + + private static List CreateRandomDataPoints(int n) + { + return CreateRandomScatterPoints(n).Select(sp => new DataPoint(sp.X, sp.Y)).ToList(); + } + + private static List CreateRandomScatterPoints(int n) + { + var r = new Random(12345); + + var points = new List(); + for (int i = 0; i < n; i++) + { + double x = r.NextDouble() * 10; + double y = r.NextDouble() * 10; + var p = new ScatterPoint(x, y); + points.Add(p); + } + + return points; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Series/StairStepSeriesExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Series/StairStepSeriesExamples.cs new file mode 100644 index 0000000..fe218e9 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Series/StairStepSeriesExamples.cs @@ -0,0 +1,88 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Provides examples for the . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + + using OxyPlot; + using OxyPlot.Series; + + /// + /// Provides examples for the . + /// + [Examples("StairStepSeries"), Tags("Series")] + public static class StairStepSeriesExamples + { + [Example("StairStepSeries")] + public static PlotModel StairStepSeries() + { + return CreateExampleModel(new StairStepSeries()); + } + + [Example("StairStepSeries with labels")] + public static PlotModel StairStepSeriesWithLabels() + { + return CreateExampleModel(new StairStepSeries { LabelFormatString = "{1:0.00}" }); + } + + [Example("StairStepSeries with markers")] + public static PlotModel StairStepSeriesWithMarkers() + { + return CreateExampleModel(new StairStepSeries + { + Color = OxyColors.SkyBlue, + MarkerType = MarkerType.Circle, + MarkerSize = 6, + MarkerStroke = OxyColors.White, + MarkerFill = OxyColors.SkyBlue, + MarkerStrokeThickness = 1.5 + }); + } + + [Example("StairStepSeries with thin vertical lines")] + public static PlotModel StairStepSeriesThinVertical() + { + return CreateExampleModel(new StairStepSeries + { + StrokeThickness = 3, + VerticalStrokeThickness = 0.4, + MarkerType = MarkerType.None + }); + } + + [Example("StairStepSeries with dashed vertical lines")] + public static PlotModel StairStepSeriesDashedVertical() + { + return CreateExampleModel(new StairStepSeries + { + VerticalLineStyle = LineStyle.Dash, + MarkerType = MarkerType.None + }); + } + + /// + /// Creates an example model and fills the specified series with points. + /// + /// The series. + /// A plot model. + private static PlotModel CreateExampleModel(DataPointSeries series) + { + var model = new PlotModel { Title = "StairStepSeries", LegendSymbolLength = 24 }; + series.Title = "sin(x)"; + for (double x = 0; x < Math.PI * 2; x += 0.5) + { + series.Points.Add(new DataPoint(x, Math.Sin(x))); + } + + model.Series.Add(series); + return model; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Series/StemSeriesExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Series/StemSeriesExamples.cs new file mode 100644 index 0000000..1bebc32 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Series/StemSeriesExamples.cs @@ -0,0 +1,55 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Provides examples for the . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + + using OxyPlot; + using OxyPlot.Series; + + /// + /// Provides examples for the . + /// + [Examples("StemSeries"), Tags("Series")] + public static class StemSeriesExamples + { + [Example("StemSeries")] + public static PlotModel StemSeries() + { + return CreateExampleModel(new StemSeries + { + Color = OxyColors.SkyBlue, + MarkerType = MarkerType.Circle, + MarkerSize = 6, + MarkerStroke = OxyColors.White, + MarkerFill = OxyColors.SkyBlue, + MarkerStrokeThickness = 1.5 + }); + } + + /// + /// Creates an example model and fills the specified series with points. + /// + /// The series. + /// A plot model. + private static PlotModel CreateExampleModel(DataPointSeries series) + { + var model = new PlotModel { Title = "StemSeries", LegendSymbolLength = 24 }; + series.Title = "sin(x)"; + for (double x = 0; x < Math.PI * 2; x += 0.1) + { + series.Points.Add(new DataPoint(x, Math.Sin(x))); + } + + model.Series.Add(series); + return model; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Series/ThreeColorLineSeriesExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Series/ThreeColorLineSeriesExamples.cs new file mode 100644 index 0000000..7522c3a --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Series/ThreeColorLineSeriesExamples.cs @@ -0,0 +1,92 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Provides examples for the ThreeColorLineSeries. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary.Series +{ + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + using System; + + /// + /// Provides examples for the . + /// + [Examples("ThreeColorLineSeries"), Tags("Series")] + public class ThreeColorLineSeriesExamples + { + /// + /// Creates an example showing temperatures. + /// + /// A . + [Example("Temperatures")] + public static PlotModel ThreeColorLineSeries() + { + var plotModel1 = new PlotModel(); + plotModel1.LegendSymbolLength = 24; + plotModel1.Title = "ThreeColorLineSeries"; + var linearAxis1 = new LinearAxis(); + linearAxis1.Title = "Temperature"; + linearAxis1.Unit = "°C"; + linearAxis1.ExtraGridlines = new Double[1]; + linearAxis1.ExtraGridlines[0] = 0; + plotModel1.Axes.Add(linearAxis1); + var linearAxis2 = new LinearAxis(); + linearAxis2.Position = AxisPosition.Bottom; + linearAxis2.Title = "Date"; + plotModel1.Axes.Add(linearAxis2); + var twoColorLineSeries1 = new ThreeColorLineSeries(); + twoColorLineSeries1.MarkerSize = 4; + twoColorLineSeries1.MarkerStroke = OxyColors.Black; + twoColorLineSeries1.MarkerStrokeThickness = 1.5; + twoColorLineSeries1.MarkerType = MarkerType.Circle; + //twoColorLineSeries1.Smooth = true; + twoColorLineSeries1.StrokeThickness = 3; + twoColorLineSeries1.Title = "Temperature Example"; + twoColorLineSeries1.TrackerFormatString = "December {2:0}: {4:0.0} °C"; + twoColorLineSeries1.Points.Add(new DataPoint(1, 5)); + twoColorLineSeries1.Points.Add(new DataPoint(2, 0)); + twoColorLineSeries1.Points.Add(new DataPoint(3, 7)); + twoColorLineSeries1.Points.Add(new DataPoint(4, 7)); + twoColorLineSeries1.Points.Add(new DataPoint(5, 4)); + twoColorLineSeries1.Points.Add(new DataPoint(6, 3)); + twoColorLineSeries1.Points.Add(new DataPoint(7, 5)); + twoColorLineSeries1.Points.Add(new DataPoint(8, 5)); + twoColorLineSeries1.Points.Add(new DataPoint(9, 11)); + twoColorLineSeries1.Points.Add(new DataPoint(10, 4)); + twoColorLineSeries1.Points.Add(new DataPoint(11, 2)); + twoColorLineSeries1.Points.Add(new DataPoint(12, 3)); + twoColorLineSeries1.Points.Add(new DataPoint(13, 2)); + twoColorLineSeries1.Points.Add(new DataPoint(14, 1)); + twoColorLineSeries1.Points.Add(new DataPoint(15, 0)); + twoColorLineSeries1.Points.Add(new DataPoint(16, 2)); + twoColorLineSeries1.Points.Add(new DataPoint(17, -1)); + twoColorLineSeries1.Points.Add(new DataPoint(18, 0)); + twoColorLineSeries1.Points.Add(new DataPoint(19, 0)); + twoColorLineSeries1.Points.Add(new DataPoint(20, -3)); + twoColorLineSeries1.Points.Add(new DataPoint(21, -6)); + twoColorLineSeries1.Points.Add(new DataPoint(22, -13)); + twoColorLineSeries1.Points.Add(new DataPoint(23, -10)); + twoColorLineSeries1.Points.Add(new DataPoint(24, -10)); + twoColorLineSeries1.Points.Add(new DataPoint(25, 0)); + twoColorLineSeries1.Points.Add(new DataPoint(26, -4)); + twoColorLineSeries1.Points.Add(new DataPoint(27, -5)); + twoColorLineSeries1.Points.Add(new DataPoint(28, -4)); + twoColorLineSeries1.Points.Add(new DataPoint(29, 3)); + twoColorLineSeries1.Points.Add(new DataPoint(30, 0)); + twoColorLineSeries1.Points.Add(new DataPoint(31, -5)); + + twoColorLineSeries1.Points.Add(new DataPoint(32, -20)); + twoColorLineSeries1.Points.Add(new DataPoint(33, -20)); + twoColorLineSeries1.Points.Add(new DataPoint(34, 20)); + twoColorLineSeries1.Points.Add(new DataPoint(35, 20)); + plotModel1.Series.Add(twoColorLineSeries1); + return plotModel1; + } + } +} diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Series/TornadoBarSeriesExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Series/TornadoBarSeriesExamples.cs new file mode 100644 index 0000000..63eedaa --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Series/TornadoBarSeriesExamples.cs @@ -0,0 +1,86 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + + [Examples("TornadoBarSeries"), Tags("Series")] + public static class TornadoBarSeriesExamples + { + [Example("Tornado diagram 1")] + public static PlotModel TornadoDiagram1() + { + // http://en.wikipedia.org/wiki/Tornado_diagram + var model = new PlotModel { Title = "Tornado diagram 1", LegendPlacement = LegendPlacement.Outside }; + + var s1 = new BarSeries + { + Title = "High", + IsStacked = true, + FillColor = OxyColor.FromRgb(216, 82, 85), + BaseValue = 7, + StrokeColor = OxyColors.Black, + StrokeThickness = 1 + }; + s1.Items.Add(new BarItem(1)); + s1.Items.Add(new BarItem(1)); + s1.Items.Add(new BarItem(4)); + s1.Items.Add(new BarItem(5)); + + var s2 = new BarSeries + { + Title = "Low", + IsStacked = true, + FillColor = OxyColor.FromRgb(84, 138, 209), + BaseValue = 7, + StrokeColor = OxyColors.Black, + StrokeThickness = 1 + }; + s2.Items.Add(new BarItem(-1)); + s2.Items.Add(new BarItem(-3)); + s2.Items.Add(new BarItem(-2)); + s2.Items.Add(new BarItem(-3)); + + var categoryAxis = new CategoryAxis { Position = AxisPosition.Left }; + categoryAxis.Labels.Add("F/X rate"); + categoryAxis.Labels.Add("Inflation"); + categoryAxis.Labels.Add("Price"); + categoryAxis.Labels.Add("Conversion"); + var valueAxis = new LinearAxis { Position = AxisPosition.Bottom, ExtraGridlines = new[] { 7.0 } }; + model.Series.Add(s1); + model.Series.Add(s2); + model.Axes.Add(categoryAxis); + model.Axes.Add(valueAxis); + return model; + } + + [Example("Tornado diagram 2")] + public static PlotModel TornadoDiagram2() + { + var model = new PlotModel { Title = "Tornado diagram 2", LegendPlacement = LegendPlacement.Outside }; + + var s1 = new TornadoBarSeries { Title = "TornadoBarSeries", BaseValue = 7 }; + s1.Items.Add(new TornadoBarItem { Minimum = 6, Maximum = 8 }); + s1.Items.Add(new TornadoBarItem { Minimum = 4, Maximum = 8 }); + s1.Items.Add(new TornadoBarItem { Minimum = 5, Maximum = 11 }); + s1.Items.Add(new TornadoBarItem { Minimum = 4, Maximum = 12 }); + + var categoryAxis = new CategoryAxis { Position = AxisPosition.Left }; + categoryAxis.Labels.Add("F/X rate"); + categoryAxis.Labels.Add("Inflation"); + categoryAxis.Labels.Add("Price"); + categoryAxis.Labels.Add("Conversion"); + var valueAxis = new LinearAxis { Position = AxisPosition.Bottom, ExtraGridlines = new[] { 7.0 }, MinimumPadding = 0.1, MaximumPadding = 0.1 }; + model.Series.Add(s1); + model.Axes.Add(categoryAxis); + model.Axes.Add(valueAxis); + return model; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Series/TwoColorAreaSeriesExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Series/TwoColorAreaSeriesExamples.cs new file mode 100644 index 0000000..962a503 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Series/TwoColorAreaSeriesExamples.cs @@ -0,0 +1,167 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Provides examples for the . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + + /// + /// Provides examples for the . + /// + [Examples("TwoColorAreaSeries"), Tags("Series")] + public class TwoColorAreaSeriesExamples + { + /// + /// Creates an example showing temperatures by a red/blue area chart. + /// + /// A . + [Example("Temperatures")] + public static PlotModel TwoColorAreaSeries1() + { + var model = new PlotModel { Title = "TwoColorAreaSeries", LegendSymbolLength = 24 }; + var s1 = new TwoColorAreaSeries + { + Title = "Temperature at Eidesmoen, December 1986.", + TrackerFormatString = "December {2:0}: {4:0.0} °C", + Color = OxyColors.Tomato, + Color2 = OxyColors.LightBlue, + MarkerFill = OxyColors.Tomato, + MarkerFill2 = OxyColors.LightBlue, + StrokeThickness = 2, + Limit = -1, + MarkerType = MarkerType.Circle, + MarkerSize = 3, + }; + var temperatures = new[] { 5, 0, 7, 7, 4, 3, 5, 5, 11, 4, 2, 3, 2, 1, 0, 2, -1, 0, 0, -3, -6, -13, -10, -10, 0, -4, -5, -4, 3, 0, -5 }; + + for (int i = 0; i < temperatures.Length; i++) + { + s1.Points.Add(new DataPoint(i + 1, temperatures[i])); + } + + model.Series.Add(s1); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = "Temperature", Unit = "°C", MinorGridlineStyle = LineStyle.Solid, MajorGridlineStyle = LineStyle.Solid, MinorTickSize = 0 }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Title = "Date", MajorGridlineStyle = LineStyle.Solid, MinorGridlineStyle = LineStyle.Solid }); + + return model; + } + + /// + /// Creates an example showing temperatures by a red/blue area chart. + /// + /// A . + [Example("Temperatures ver2")] + public static PlotModel TwoColorAreaSeries2() + { + var model = new PlotModel { Title = "TwoColorAreaSeries", LegendSymbolLength = 24 }; + var s1 = new TwoColorAreaSeries + { + Title = "Temperature at Eidesmoen, December 1986.", + TrackerFormatString = "December {2:0}: {4:0.0} °C", + Color = OxyColors.Black, + Color2 = OxyColors.Brown, + MarkerFill = OxyColors.Red, + Fill = OxyColors.Tomato, + Fill2 = OxyColors.LightBlue, + MarkerFill2 = OxyColors.Blue, + MarkerStroke = OxyColors.Brown, + MarkerStroke2 = OxyColors.Black, + StrokeThickness = 2, + Limit = 0, + MarkerType = MarkerType.Circle, + MarkerSize = 3, + }; + + var temperatures = new[] { 5, 0, 7, 7, 4, 3, 5, 5, 11, 4, 2, 3, 2, 1, 0, 2, -1, 0, 0, -3, -6, -13, -10, -10, 0, -4, -5, -4, 3, 0, -5 }; + + for (int i = 0; i < temperatures.Length; i++) + { + s1.Points.Add(new DataPoint(i + 1, temperatures[i])); + } + + model.Series.Add(s1); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = "Temperature", Unit = "°C", ExtraGridlines = new[] { 0.0 } }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Title = "Date" }); + + return model; + } + + /// + /// Creates an example showing temperatures by a red/blue area chart. + /// + /// A . + [Example("Temperatures ver3")] + public static PlotModel TwoColorAreaSeries3() + { + var model = new PlotModel { Title = "TwoColorAreaSeries", LegendSymbolLength = 24 }; + var s1 = new TwoColorAreaSeries + { + Title = "Temperature at Eidesmoen, December 1986.", + TrackerFormatString = "December {2:0}: {4:0.0} °C", + Color = OxyColors.Black, + Color2 = OxyColors.Brown, + MarkerFill = OxyColors.Red, + Fill = OxyColors.Tomato, + Fill2 = OxyColors.LightBlue, + MarkerFill2 = OxyColors.Blue, + MarkerStroke = OxyColors.Brown, + MarkerStroke2 = OxyColors.Black, + StrokeThickness = 1, + Limit = 0, + InterpolationAlgorithm = InterpolationAlgorithms.CanonicalSpline, + MarkerType = MarkerType.Circle, + MarkerSize = 1, + }; + + var temperatures = new[] { 5, 0, 7, 7, 4, 3, 5, 5, 11, 4, 2, 3, 2, 1, 0, 2, -1, 0, 0, -3, -6, -13, -10, -10, 0, -4, -5, -4, 3, 0, -5 }; + + for (int i = 0; i < temperatures.Length; i++) + { + s1.Points.Add(new DataPoint(i + 1, temperatures[i])); + } + + model.Series.Add(s1); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = "Temperature", Unit = "°C", ExtraGridlines = new[] { 0.0 } }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Title = "Date" }); + + return model; + } + + /// + /// Creates an example showing temperatures by a red/blue area chart. + /// + /// A . + [Example("Two polygons")] + public static PlotModel TwoColorAreaSeriesTwoPolygons() + { + var model = new PlotModel { Title = "Two polygons", LegendSymbolLength = 24 }; + var s1 = new TwoColorAreaSeries + { + Color = OxyColors.Tomato, + Color2 = OxyColors.LightBlue, + MarkerFill = OxyColors.Tomato, + MarkerFill2 = OxyColors.LightBlue, + StrokeThickness = 2, + MarkerType = MarkerType.Circle, + MarkerSize = 3, + }; + + s1.Points.AddRange(new []{new DataPoint(0, 3), new DataPoint(1, 5), new DataPoint(2, 1), new DataPoint(3, 0), new DataPoint(4, 3) }); + s1.Points2.AddRange(new[] { new DataPoint(0, -3), new DataPoint(1, -1), new DataPoint(2, 0), new DataPoint(3, -6), new DataPoint(4, -4) }); + + model.Series.Add(s1); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom}); + + return model; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Series/TwoColorLineSeriesExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Series/TwoColorLineSeriesExamples.cs new file mode 100644 index 0000000..70d62a1 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Series/TwoColorLineSeriesExamples.cs @@ -0,0 +1,58 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Provides examples for the . +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + + /// + /// Provides examples for the . + /// + [Examples("TwoColorLineSeries"), Tags("Series")] + public class TwoColorLineSeriesExamples + { + /// + /// Creates an example showing temperatures by a red/blue line. + /// + /// A . + [Example("Temperatures")] + public static PlotModel TwoColorLineSeries() + { + var model = new PlotModel { Title = "TwoColorLineSeries", LegendSymbolLength = 24 }; + var s1 = new TwoColorLineSeries + { + Title = "Temperature at Eidesmoen, December 1986.", + TrackerFormatString = "December {2:0}: {4:0.0} °C", + Color = OxyColors.Red, + Color2 = OxyColors.LightBlue, + StrokeThickness = 3, + Limit = 0, + InterpolationAlgorithm = InterpolationAlgorithms.CanonicalSpline, + MarkerType = MarkerType.Circle, + MarkerSize = 4, + MarkerStroke = OxyColors.Black, + MarkerStrokeThickness = 1.5 + }; + var temperatures = new[] { 5, 0, 7, 7, 4, 3, 5, 5, 11, 4, 2, 3, 2, 1, 0, 2, -1, 0, 0, -3, -6, -13, -10, -10, 0, -4, -5, -4, 3, 0, -5 }; + + for (int i = 0; i < temperatures.Length; i++) + { + s1.Points.Add(new DataPoint(i + 1, temperatures[i])); + } + + model.Series.Add(s1); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = "Temperature", Unit = "°C", ExtraGridlines = new[] { 0.0 } }); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Title = "Date" }); + + return model; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Showcases/ShowCases.cs b/OxyPlot/Source/Examples/ExampleLibrary/Showcases/ShowCases.cs new file mode 100644 index 0000000..0603c0f --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Showcases/ShowCases.cs @@ -0,0 +1,115 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Showcase models +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + + /// + /// Showcase models + /// + [Examples("1 ShowCases")] + [Tags("Showcase")] + public class ShowCases + { + [Example("Normal distribution")] + public static PlotModel CreateNormalDistributionModel() + { + // http://en.wikipedia.org/wiki/Normal_distribution + + var plot = new PlotModel + { + Title = "Normal distribution", + Subtitle = "Probability density function" + }; + + plot.Axes.Add(new LinearAxis + { + Position = AxisPosition.Left, + Minimum = -0.05, + Maximum = 1.05, + MajorStep = 0.2, + MinorStep = 0.05, + TickStyle = TickStyle.Inside + }); + plot.Axes.Add(new LinearAxis + { + Position = AxisPosition.Bottom, + Minimum = -5.25, + Maximum = 5.25, + MajorStep = 1, + MinorStep = 0.25, + TickStyle = TickStyle.Inside + }); + plot.Series.Add(CreateNormalDistributionSeries(-5, 5, 0, 0.2)); + plot.Series.Add(CreateNormalDistributionSeries(-5, 5, 0, 1)); + plot.Series.Add(CreateNormalDistributionSeries(-5, 5, 0, 5)); + plot.Series.Add(CreateNormalDistributionSeries(-5, 5, -2, 0.5)); + return plot; + } + + [Example("Average (Mean) monthly temperatures in 2003")] + public static PlotModel LineLegendPositionAtEnd() + { + // http://www.perceptualedge.com/example2.php + var model = new PlotModel { Title = "Average (Mean) monthly temperatures in 2003", PlotMargins = new OxyThickness(60, 4, 60, 40), PlotAreaBorderThickness = new OxyThickness(0), IsLegendVisible = false }; + + const string TrackerFormatString = "{0}: {4:0.0}ºF"; + var phoenix = new LineSeries { Title = "Phoenix", LineLegendPosition = LineLegendPosition.End, TrackerFormatString = TrackerFormatString }; + var raleigh = new LineSeries { Title = "Raleigh", LineLegendPosition = LineLegendPosition.End, TrackerFormatString = TrackerFormatString }; + var minneapolis = new LineSeries { Title = "Minneapolis", LineLegendPosition = LineLegendPosition.End, TrackerFormatString = TrackerFormatString }; + + var phoenixTemps = new[] { 52.1, 55.1, 59.7, 67.7, 76.3, 84.6, 91.2, 89.1, 83.8, 72.2, 59.8, 52.5 }; + var raleighTemps = new[] { 40.5, 42.2, 49.2, 59.5, 67.4, 74.4, 77.5, 76.5, 70.6, 60.2, 50.0, 41.2 }; + var minneapolisTemps = new[] { 12.2, 16.5, 28.3, 45.1, 57.1, 66.9, 71.9, 70.2, 60.0, 50.0, 32.4, 18.6 }; + + for (int i = 0; i < 12; i++) + { + phoenix.Points.Add(new DataPoint(i, phoenixTemps[i])); + raleigh.Points.Add(new DataPoint(i, raleighTemps[i])); + minneapolis.Points.Add(new DataPoint(i, minneapolisTemps[i])); + } + + model.Series.Add(phoenix); + model.Series.Add(raleigh); + model.Series.Add(minneapolis); + + var categoryAxis = new CategoryAxis + { + AxislineStyle = LineStyle.Solid + }; + categoryAxis.Labels.AddRange(new[] { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }); + model.Axes.Add(categoryAxis); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = "Fahrenheit", AxislineStyle = LineStyle.Solid }); + + return model; + } + + private static DataPointSeries CreateNormalDistributionSeries(double x0, double x1, double mean, double variance, int n = 1001) + { + var ls = new LineSeries + { + Title = string.Format("μ={0}, σ²={1}", mean, variance) + }; + + for (int i = 0; i < n; i++) + { + double x = x0 + ((x1 - x0) * i / (n - 1)); + double f = 1.0 / Math.Sqrt(2 * Math.PI * variance) * Math.Exp(-(x - mean) * (x - mean) / 2 / variance); + ls.Points.Add(new DataPoint(x, f)); + } + + return ls; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Showcases/ShowMeTheNumbersExamples.cs b/OxyPlot/Source/Examples/ExampleLibrary/Showcases/ShowMeTheNumbersExamples.cs new file mode 100644 index 0000000..0284942 --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Showcases/ShowMeTheNumbersExamples.cs @@ -0,0 +1,350 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Examples from the book "Show Me the Numbers" by Stephen Few +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System.Collections.Generic; + + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + + /// + /// Examples from the book "Show Me the Numbers" by Stephen Few + /// + [Examples("Examples from the book 'Show Me the Numbers'"), Tags("Showcase")] + public class ShowMeTheNumbersExamples + { + /// + /// The graph 1. + /// + /// + [Example("Q1 2003 Calls by Region")] + public static PlotModel Graph1() + { + var pm = new PlotModel { Title = "Q1 2003 Calls by Region", PlotAreaBorderThickness = new OxyThickness(0) }; + var categoryAxis = new CategoryAxis + { + AxislineStyle = LineStyle.Solid, + TickStyle = TickStyle.None + }; + categoryAxis.Labels.AddRange(new[] { "North", "East", "South", "West" }); + pm.Axes.Add(categoryAxis); + pm.Axes.Add( + new LinearAxis + { + Position = AxisPosition.Left, + Minimum = 0, + Maximum = 6000, + MajorStep = 1000, + MinorStep = 1000, + AxislineStyle = LineStyle.Solid, + TickStyle = TickStyle.Outside, + StringFormat = "#,0" + }); + var series = new ColumnSeries { FillColor = OxyColors.Black }; + series.Items.Add(new ColumnItem { Value = 3000 }); + series.Items.Add(new ColumnItem { Value = 4500 }); + series.Items.Add(new ColumnItem { Value = 2100 }); + series.Items.Add(new ColumnItem { Value = 4800 }); + pm.Series.Add(series); + return pm; + } + + /// + /// The graph 2. + /// + /// + [Example("2003 Sales")] + public static PlotModel Graph2() + { + var pm = new PlotModel + { + Title = "2003 Sales", + PlotAreaBorderThickness = new OxyThickness(0), + IsLegendVisible = false + }; + var sales1 = new[] { 1000, 1010, 1020, 1010, 1020, 1030, 1000, 500, 1000, 900, 900, 1000 }; + var sales2 = new[] { 2250, 2500, 2750, 2500, 2750, 3000, 2500, 2750, 3100, 2800, 3100, 3500 }; + var categoryAxis = new CategoryAxis + { + AxislineStyle = LineStyle.Solid, + TickStyle = TickStyle.None + }; + categoryAxis.Labels.AddRange(new[] { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }); + pm.Axes.Add(categoryAxis); + pm.Axes.Add( + new LinearAxis + { + Position = AxisPosition.Left, + Minimum = 0, + Maximum = 4000, + MajorStep = 500, + MinorStep = 500, + AxislineStyle = LineStyle.Solid, + TickStyle = TickStyle.Outside, + StringFormat = "#,0" + }); + var s1 = new LineSeries { Color = OxyColors.Orange }; + for (int i = 0; i < 12; i++) + { + s1.Points.Add(new DataPoint(i, sales1[i])); + } + + var s2 = new LineSeries { Color = OxyColors.Gray }; + for (int i = 0; i < 12; i++) + { + s2.Points.Add(new DataPoint(i, sales2[i])); + } + + pm.Series.Add(s1); + pm.Series.Add(s2); + return pm; + } + + /// + /// The graph 3. + /// + /// + [Example("Headcount")] + public static PlotModel Graph3() + { + var pm = new PlotModel + { + Title = "Headcount", + PlotAreaBorderThickness = new OxyThickness(0), + PlotMargins = new OxyThickness(100, 40, 20, 40) + }; + var values = new Dictionary { + { "Manufacturing", 240 }, + { "Sales", 160 }, + { "Engineering", 50 }, + { "Operations", 45 }, + { "Finance", 40 }, + { "Info Systems", 39 }, + { "Legal", 25 }, + { "Marketing", 10 } + }; + pm.Axes.Add( + new CategoryAxis + { + Position = AxisPosition.Left, + ItemsSource = values, + LabelField = "Key", + TickStyle = TickStyle.None, + AxisTickToLabelDistance = 10, + StartPosition = 1, + EndPosition = 0 + }); + pm.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = 0, Maximum = 250, MajorStep = 50, MinorStep = 50, AxislineStyle = LineStyle.Solid, TickStyle = TickStyle.Outside, MinimumPadding = 0, MaximumPadding = 0 }); + pm.Series.Add(new BarSeries { FillColor = OxyColors.Black, ItemsSource = values, ValueField = "Value" }); + return pm; + } + + /// + /// The graph 4. + /// + /// + [Example("Regional % of Total Expenses")] + public static PlotModel Graph4() + { + var pm = new PlotModel { Title = "Regional % of Total Expenses", PlotAreaBorderThickness = new OxyThickness(0) }; + var categoryAxis = new CategoryAxis + { + TickStyle = TickStyle.None, + GapWidth = 0 + }; + categoryAxis.Labels.AddRange(new[] { "West\n34%", "East\n30%", "North\n20%", "South\n16%" }); + pm.Axes.Add(categoryAxis); + + pm.Axes.Add( + new LinearAxis + { + Position = AxisPosition.Left, + Minimum = 0, + Maximum = 0.35 + double.Epsilon, + MajorStep = 0.05, + MinorStep = 0.05, + AxislineStyle = LineStyle.Solid, + TickStyle = TickStyle.Outside, + StringFormat = "P0" + }); + + var series = new ColumnSeries + { + ColumnWidth = 1.0, + StrokeColor = OxyColors.DarkGray, + StrokeThickness = 1.0, + FillColor = OxyColors.Black, + }; + series.Items.Add(new ColumnItem { Value = 0.34 }); + series.Items.Add(new ColumnItem { Value = 0.3 }); + series.Items.Add(new ColumnItem { Value = 0.2 }); + series.Items.Add(new ColumnItem { Value = 0.16 }); + pm.Series.Add(series); + return pm; + } + + /// + /// The graph 5. + /// + /// + [Example("Actual to Plan Variance")] + public static PlotModel Graph5() + { + var pm = new PlotModel { Title = "Actual to Plan Variance", PlotAreaBorderThickness = new OxyThickness(0) }; + var values = new Dictionary(); + values.Add("Sales", 7); + values.Add("Marketing", -7); + values.Add("Systems", -2); + values.Add("HR", -17); + values.Add("Finance", 5); + pm.Axes.Add(new CategoryAxis { ItemsSource = values, LabelField = "Key", TickStyle = TickStyle.None }); + pm.Axes.Add( + new LinearAxis + { + Position = AxisPosition.Left, + Minimum = -20, + Maximum = 10, + MinorStep = 5, + MajorStep = 5, + Layer = AxisLayer.AboveSeries, + AxislineStyle = LineStyle.Solid, + ExtraGridlines = new double[] { 0 }, + ExtraGridlineColor = OxyColors.Black, + ExtraGridlineThickness = 3, + TickStyle = TickStyle.Outside, + StringFormat = "+0;-0;0" + }); + pm.Series.Add( + new ColumnSeries + { + FillColor = OxyColors.Orange, + NegativeFillColor = OxyColors.Gray, + ItemsSource = values, + ValueField = "Value", + }); + return pm; + } + + /// + /// The graph 6. + /// + /// + [Example("Order Count by Order Size")] + public static PlotModel Graph6() + { + var pm = new PlotModel + { + Title = "Order Count by Order Size", + PlotAreaBorderThickness = new OxyThickness(0), + PlotMargins = new OxyThickness(60, 4, 4, 60) + }; + var values = new Dictionary + { + { " <$10", 5000 }, + { ">=$10\n &\n <$20", 1500 }, + { ">=$20\n &\n <$30", 1000 }, + { ">=$40\n &\n <$40", 500 }, + { ">=$40", 200 } + }; + pm.Axes.Add(new CategoryAxis + { + AxislineStyle = LineStyle.Solid, + ItemsSource = values, + LabelField = "Key", + TickStyle = TickStyle.None + }); + pm.Axes.Add( + new LinearAxis + { + Position = AxisPosition.Left, + Minimum = 0, + Maximum = 6000, + MajorStep = 1000, + MinorStep = 1000, + AxislineStyle = LineStyle.Solid, + TickStyle = TickStyle.Outside, + StringFormat = "+0;-0;0" + }); + pm.Series.Add(new ColumnSeries { FillColor = OxyColors.Orange, ItemsSource = values, ValueField = "Value" }); + return pm; + } + + /// + /// The graph 7. + /// + /// + [Example("Correlation of Employee Heights and Salaries")] + public static PlotModel Graph7() + { + var pm = new PlotModel + { + Title = "Correlation of Employee Heights and Salaries", + PlotAreaBorderThickness = new OxyThickness(0) + }; + var values = new[] + { + new DataPoint(62, 39000), + new DataPoint(66, 44000), + new DataPoint(64, 50000), + new DataPoint(66, 49500), + new DataPoint(67, 52000), + new DataPoint(68, 50000), + new DataPoint(66, 56000), + new DataPoint(67, 56000), + new DataPoint(72, 56000), + new DataPoint(68, 58000), + new DataPoint(69, 62000), + new DataPoint(71, 63000), + new DataPoint(65, 64000), + new DataPoint(68, 71000), + new DataPoint(72, 72000), + new DataPoint(74, 69000), + new DataPoint(74, 79000), + new DataPoint(77, 81000) + }; + pm.Axes.Add( + new LinearAxis + { + Position = AxisPosition.Left, + Minimum = 30000, + Maximum = 90000, + MajorStep = 10000, + MinorStep = 10000, + AxislineStyle = LineStyle.Solid, + TickStyle = TickStyle.Outside, + StringFormat = "0,0" + }); + pm.Axes.Add(new LinearAxis + { + Position = AxisPosition.Bottom, + Minimum = 60, + Maximum = 80, + MajorStep = 5, + MinorStep = 5, + AxislineStyle = LineStyle.Solid, + TickStyle = TickStyle.Outside + }); + pm.Series.Add( + new ScatterSeries + { + ItemsSource = values, + MarkerType = MarkerType.Circle, + MarkerSize = 3.0, + MarkerFill = OxyColors.White, + MarkerStroke = OxyColors.Black, + DataFieldX = "X", + DataFieldY = "Y" + }); + return pm; + } + + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/ExampleLibrary/Utilities/Sun.cs b/OxyPlot/Source/Examples/ExampleLibrary/Utilities/Sun.cs new file mode 100644 index 0000000..1c7241d --- /dev/null +++ b/OxyPlot/Source/Examples/ExampleLibrary/Utilities/Sun.cs @@ -0,0 +1,227 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Calculation of sunrise/sunset +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleLibrary +{ + using System; + + /// + /// Calculation of sunrise/sunset + /// + /// http://williams.best.vwh.net/sunrise_sunset_algorithm.htm + /// based on code by Huysentruit Wouter, Fastload-Media.be + public static class Sun + { + private static double Deg2Rad(double angle) + { + return Math.PI * angle / 180.0; + } + + private static double Rad2Deg(double angle) + { + return 180.0 * angle / Math.PI; + } + + private static double FixValue(double value, double min, double max) + { + while (value < min) + { + value += max - min; + } + + while (value >= max) + { + value -= max - min; + } + + return value; + } + + public static DateTime Calculate(DateTime date, double latitude, double longitude, bool sunrise, Func utcToLocalTime, double zenith = 90.5) + { + // 1. first calculate the day of the year + int n = date.DayOfYear; + + // 2. convert the longitude to hour value and calculate an approximate time + double lngHour = longitude / 15.0; + + double t; + + if (sunrise) + { + t = n + ((6.0 - lngHour) / 24.0); + } + else + { + t = n + ((18.0 - lngHour) / 24.0); + } + + // 3. calculate the Sun's mean anomaly + double m = (0.9856 * t) - 3.289; + + // 4. calculate the Sun's true longitude + double l = m + (1.916 * Math.Sin(Deg2Rad(m))) + (0.020 * Math.Sin(Deg2Rad(2 * m))) + 282.634; + l = FixValue(l, 0, 360); + + // 5a. calculate the Sun's right ascension + double ra = Rad2Deg(Math.Atan(0.91764 * Math.Tan(Deg2Rad(l)))); + ra = FixValue(ra, 0, 360); + + // 5b. right ascension value needs to be in the same quadrant as L + double lquadrant = Math.Floor(l / 90.0) * 90.0; + double raquadrant = Math.Floor(ra / 90.0) * 90.0; + ra = ra + (lquadrant - raquadrant); + + // 5c. right ascension value needs to be converted into hours + ra = ra / 15.0; + + // 6. calculate the Sun's declination + double sinDec = 0.39782 * Math.Sin(Deg2Rad(l)); + double cosDec = Math.Cos(Math.Asin(sinDec)); + + // 7a. calculate the Sun's local hour angle + double cosH = (Math.Cos(Deg2Rad(zenith)) - (sinDec * Math.Sin(Deg2Rad(latitude)))) / + (cosDec * Math.Cos(Deg2Rad(latitude))); + + // 7b. finish calculating H and convert into hours + double h; + + if (sunrise) + { + h = 360.0 - Rad2Deg(Math.Acos(cosH)); + } + else + { + h = Rad2Deg(Math.Acos(cosH)); + } + + h = h / 15.0; + + // 8. calculate local mean time of rising/setting + double localMeanTime = h + ra - (0.06571 * t) - 6.622; + + // 9. adjust back to UTC + double utc = localMeanTime - lngHour; + + // 10. convert UT value to local time zone of latitude/longitude + date = new DateTime(date.Year, date.Month, date.Day, 0, 0, 0, DateTimeKind.Utc); + var utctime = date.AddHours(utc); + var localTime = utcToLocalTime(utctime); + + utc = (localTime - date).TotalHours; + utc = FixValue(utc, 0, 24); + return date.AddHours(utc); + } + } + + /* + Sunrise/Sunset Algorithm + + Source: + Almanac for Computers, 1990 + published by Nautical Almanac Office + United States Naval Observatory + Washington, DC 20392 + + Inputs: + day, month, year: date of sunrise/sunset + latitude, longitude: location for sunrise/sunset + zenith: Sun's zenith for sunrise/sunset + offical = 90 degrees 50' + civil = 96 degrees + nautical = 102 degrees + astronomical = 108 degrees + + NOTE: longitude is positive for East and negative for West + NOTE: the algorithm assumes the use of a calculator with the + trig functions in "degree" (rather than "radian") mode. Most + programming languages assume radian arguments, requiring back + and forth convertions. The factor is 180/pi. So, for instance, + the equation RA = atan(0.91764 * tan(L)) would be coded as RA + = (180/pi)*atan(0.91764 * tan((pi/180)*L)) to give a degree + answer with a degree input for L. + + 1. first calculate the day of the year + + N1 = floor(275 * month / 9) + N2 = floor((month + 9) / 12) + N3 = (1 + floor((year - 4 * floor(year / 4) + 2) / 3)) + N = N1 - (N2 * N3) + day - 30 + + 2. convert the longitude to hour value and calculate an approximate time + + lngHour = longitude / 15 + + if rising time is desired: + t = N + ((6 - lngHour) / 24) + if setting time is desired: + t = N + ((18 - lngHour) / 24) + + 3. calculate the Sun's mean anomaly + + M = (0.9856 * t) - 3.289 + + 4. calculate the Sun's true longitude + + L = M + (1.916 * sin(M)) + (0.020 * sin(2 * M)) + 282.634 + NOTE: L potentially needs to be adjusted into the range [0,360) by adding/subtracting 360 + + 5a. calculate the Sun's right ascension + + RA = atan(0.91764 * tan(L)) + NOTE: RA potentially needs to be adjusted into the range [0,360) by adding/subtracting 360 + + 5b. right ascension value needs to be in the same quadrant as L + + Lquadrant = (floor( L/90)) * 90 + RAquadrant = (floor(RA/90)) * 90 + RA = RA + (Lquadrant - RAquadrant) + + 5c. right ascension value needs to be converted into hours + + RA = RA / 15 + + 6. calculate the Sun's declination + + sinDec = 0.39782 * sin(L) + cosDec = cos(asin(sinDec)) + + 7a. calculate the Sun's local hour angle + + cosH = (cos(zenith) - (sinDec * sin(latitude))) / (cosDec * cos(latitude)) + + if (cosH > 1) + the sun never rises on this location (on the specified date) + if (cosH < -1) + the sun never sets on this location (on the specified date) + + 7b. finish calculating H and convert into hours + + if if rising time is desired: + H = 360 - acos(cosH) + if setting time is desired: + H = acos(cosH) + + H = H / 15 + + 8. calculate local mean time of rising/setting + + T = H + RA - (0.06571 * t) - 6.622 + + 9. adjust back to UTC + + UT = T - lngHour + NOTE: UT potentially needs to be adjusted into the range [0,24) by adding/subtracting 24 + + 10. convert UT value to local time zone of latitude/longitude + + localT = UT + localOffset + + */ +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/LINQPad/MyExtensions.FW40.linq b/OxyPlot/Source/Examples/LINQPad/MyExtensions.FW40.linq new file mode 100644 index 0000000..627ca34 --- /dev/null +++ b/OxyPlot/Source/Examples/LINQPad/MyExtensions.FW40.linq @@ -0,0 +1,33 @@ + + + <RuntimeDirectory>\System.Xaml.dll + <RuntimeDirectory>\WPF\WindowsBase.dll + <RuntimeDirectory>\WPF\PresentationCore.dll + <RuntimeDirectory>\WPF\PresentationFramework.dll + + System.Windows + System.Windows.Controls + System.Windows.Media + OxyPlot + OxyPlot.Wpf + + +void Main() +{ + var pm = new PlotModel("Sine curve"); + pm.Series.Add(new FunctionSeries(Math.Sin,0,20,200)); + pm.Show(); +} + +public static class MyExtensions +{ + public static void Show(this PlotModel model, double width = 800, double height = 500) { + var w = new Window() { Title = "OxyPlot.Wpf.PlotView : " + model.Title, Width = width, Height = height }; + var plot = new PlotView(); + plot.Model = model; + w.Content = plot; + w.Show(); + } +} + +// You can also define non-static classes, enums, etc. \ No newline at end of file diff --git a/OxyPlot/Source/Examples/LINQPad/ReadMe.txt b/OxyPlot/Source/Examples/LINQPad/ReadMe.txt new file mode 100644 index 0000000..d8e4340 --- /dev/null +++ b/OxyPlot/Source/Examples/LINQPad/ReadMe.txt @@ -0,0 +1,8 @@ +Tested with LINQPad 4.38.07 (Beta, free edition) + +1. Build OxyPlot release + Tools/build.cmd +2. Start LINQPad +3. Set the plugins/extensions and queries folders to the "OxyPlot/Source/Examples/LINQPad" folder +4. Test the "My extensions" main example +5. Test the SimpleFunction/SimpleLinePlot queries diff --git a/OxyPlot/Source/Examples/LINQPad/SimpleFunction.linq b/OxyPlot/Source/Examples/LINQPad/SimpleFunction.linq new file mode 100644 index 0000000..9fae7af --- /dev/null +++ b/OxyPlot/Source/Examples/LINQPad/SimpleFunction.linq @@ -0,0 +1,14 @@ + + OxyPlot + + +void Main() +{ + var pm = new PlotModel("Simple Function Plots") { PlotType = PlotType.Cartesian }; + + pm.Series.Add(new FunctionSeries(Math.Sin, -10, 10, 0.1, "sin(x)")); + pm.Series.Add(new FunctionSeries(Math.Cos, -10, 10, 0.1, "cos(x)")); + pm.Series.Add(new FunctionSeries(t => 5 * Math.Cos(t), t => 5 * Math.Sin(t), 0, 2 * Math.PI, 0.1, "5cos(t),5sin(t)")); + + pm.Show(); +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/LINQPad/SimpleLinePlot.linq b/OxyPlot/Source/Examples/LINQPad/SimpleLinePlot.linq new file mode 100644 index 0000000..2c5d832 --- /dev/null +++ b/OxyPlot/Source/Examples/LINQPad/SimpleLinePlot.linq @@ -0,0 +1,18 @@ + + OxyPlot + + +void Main() +{ + var pm = new PlotModel("Simple Line Plot"); + + var lineSeries1 = new LineSeries("LineSeries1"); + + lineSeries1.Points.Add( new DataPoint (1,1)); + lineSeries1.Points.Add( new DataPoint (2,2)); + lineSeries1.Points.Add( new DataPoint (3,1.5)); + + pm.Series.Add(lineSeries1); + + pm.Show(); +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/PerformanceTest/App.config b/OxyPlot/Source/Examples/PerformanceTest/App.config new file mode 100644 index 0000000..8e15646 --- /dev/null +++ b/OxyPlot/Source/Examples/PerformanceTest/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/OxyPlot/Source/Examples/PerformanceTest/EmptyRenderContext.cs b/OxyPlot/Source/Examples/PerformanceTest/EmptyRenderContext.cs new file mode 100644 index 0000000..0f54c87 --- /dev/null +++ b/OxyPlot/Source/Examples/PerformanceTest/EmptyRenderContext.cs @@ -0,0 +1,271 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Provides an empty that does nothing. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace PerformanceTest +{ + using System.Collections.Generic; + + using OxyPlot; + + /// + /// Provides an empty that does nothing. + /// + public class EmptyRenderContext : IRenderContext + { + /// + /// Gets a value indicating whether the context renders to screen. + /// + /// + /// true if the context renders to screen; otherwise, false. + /// + public bool RendersToScreen + { + get + { + return true; + } + } + + /// + /// Draws an ellipse. + /// + /// The rectangle. + /// The fill color. If set to OxyColors.Undefined, the ellipse will not be filled. + /// The stroke color. If set to OxyColors.Undefined, the ellipse will not be stroked. + /// The thickness (in device independent units, 1/96 inch). + public void DrawEllipse(OxyRect rect, OxyColor fill, OxyColor stroke, double thickness = 1) + { + } + + /// + /// Draws a collection of ellipses, where all have the same stroke and fill. + /// This performs better than calling DrawEllipse multiple times. + /// + /// The rectangles. + /// The fill color. If set to OxyColors.Undefined, the ellipses will not be filled. + /// The stroke color. If set to OxyColors.Undefined, the ellipses will not be stroked. + /// The stroke thickness (in device independent units, 1/96 inch). + public void DrawEllipses(IList rectangles, OxyColor fill, OxyColor stroke, double thickness = 1) + { + } + + /// + /// Draws a polyline. + /// + /// The points. + /// The stroke color. + /// The stroke thickness (in device independent units, 1/96 inch). + /// The dash array (in device independent units, 1/96 inch). Use null to get a solid line. + /// The line join type. + /// if set to true the shape will be aliased. + public void DrawLine( + IList points, + OxyColor stroke, + double thickness = 1, + double[] dashArray = null, + LineJoin lineJoin = LineJoin.Miter, + bool aliased = false) + { + } + + /// + /// Draws line segments defined by points (0,1) (2,3) (4,5) etc. + /// This should have better performance than calling DrawLine for each segment. + /// + /// The points. + /// The stroke color. + /// The stroke thickness (in device independent units, 1/96 inch). + /// The dash array (in device independent units, 1/96 inch). + /// The line join type. + /// if set to true the shape will be aliased. + public void DrawLineSegments( + IList points, + OxyColor stroke, + double thickness = 1, + double[] dashArray = null, + LineJoin lineJoin = LineJoin.Miter, + bool aliased = false) + { + } + + /// + /// Draws a polygon. + /// + /// The points. + /// The fill color. If set to OxyColors.Undefined, the polygon will not be filled. + /// The stroke color. If set to OxyColors.Undefined, the polygon will not be stroked. + /// The stroke thickness (in device independent units, 1/96 inch). + /// The dash array (in device independent units, 1/96 inch). + /// The line join type. + /// If set to true the polygon will be aliased. + public void DrawPolygon( + IList points, + OxyColor fill, + OxyColor stroke, + double thickness = 1, + double[] dashArray = null, + LineJoin lineJoin = LineJoin.Miter, + bool aliased = false) + { + } + + /// + /// Draws a collection of polygons, where all polygons have the same stroke and fill. + /// This performs better than calling DrawPolygon multiple times. + /// + /// The polygons. + /// The fill color. If set to OxyColors.Undefined, the polygons will not be filled. + /// The stroke color. If set to OxyColors.Undefined, the polygons will not be stroked. + /// The stroke thickness (in device independent units, 1/96 inch). + /// The dash array (in device independent units, 1/96 inch). + /// The line join type. + /// if set to true the shape will be aliased. + public void DrawPolygons( + IList> polygons, + OxyColor fill, + OxyColor stroke, + double thickness = 1, + double[] dashArray = null, + LineJoin lineJoin = LineJoin.Miter, + bool aliased = false) + { + } + + /// + /// Draws a rectangle. + /// + /// The rectangle. + /// The fill color. If set to OxyColors.Undefined, the rectangle will not be filled. + /// The stroke color. If set to OxyColors.Undefined, the rectangle will not be stroked. + /// The stroke thickness (in device independent units, 1/96 inch). + public void DrawRectangle(OxyRect rect, OxyColor fill, OxyColor stroke, double thickness = 1) + { + } + + /// + /// Draws a collection of rectangles, where all have the same stroke and fill. + /// This performs better than calling DrawRectangle multiple times. + /// + /// The rectangles. + /// The fill color. If set to OxyColors.Undefined, the rectangles will not be filled. + /// The stroke color. If set to OxyColors.Undefined, the rectangles will not be stroked. + /// The stroke thickness (in device independent units, 1/96 inch). + public void DrawRectangles(IList rectangles, OxyColor fill, OxyColor stroke, double thickness = 1) + { + } + + /// + /// Draws text. + /// + /// The position. + /// The text. + /// The text color. + /// The font family. + /// Size of the font (in device independent units, 1/96 inch). + /// The font weight. + /// The rotation angle. + /// The horizontal alignment. + /// The vertical alignment. + /// The maximum size of the text (in device independent units, 1/96 inch). + public void DrawText( + ScreenPoint p, + string text, + OxyColor fill, + string fontFamily = null, + double fontSize = 10, + double fontWeight = 500, + double rotate = 0, + HorizontalAlignment halign = HorizontalAlignment.Left, + VerticalAlignment valign = VerticalAlignment.Top, + OxySize? maxSize = null) + { + } + + /// + /// Measures the size of the specified text. + /// + /// The text. + /// The font family. + /// Size of the font (in device independent units, 1/96 inch). + /// The font weight. + /// + /// The size of the text (in device independent units, 1/96 inch). + /// + public OxySize MeasureText(string text, string fontFamily = null, double fontSize = 10, double fontWeight = 500) + { + return OxySize.Empty; + } + + /// + /// Sets the tool tip for the following items. + /// + /// The text in the tooltip. + public void SetToolTip(string text) + { + } + + /// + /// Cleans up resources not in use. + /// + /// + /// This method is called at the end of each rendering. + /// + public void CleanUp() + { + } + + /// + /// Draws a portion of the specified . + /// + /// The source. + /// The x-coordinate of the upper-left corner of the portion of the source image to draw. + /// The y-coordinate of the upper-left corner of the portion of the source image to draw. + /// Width of the portion of the source image to draw. + /// Height of the portion of the source image to draw. + /// The x-coordinate of the upper-left corner of drawn image. + /// The y-coordinate of the upper-left corner of drawn image. + /// The width of the drawn image. + /// The height of the drawn image. + /// The opacity. + /// interpolate if set to true. + public void DrawImage( + OxyImage source, + double srcX, + double srcY, + double srcWidth, + double srcHeight, + double destX, + double destY, + double destWidth, + double destHeight, + double opacity, + bool interpolate) + { + } + + /// + /// Sets the clip rectangle. + /// + /// The clip rectangle. + /// + /// True if the clip rectangle was set. + /// + public bool SetClip(OxyRect rect) + { + return true; + } + + /// + /// Resets the clip rectangle. + /// + public void ResetClip() + { + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/PerformanceTest/PerformanceTest.csproj b/OxyPlot/Source/Examples/PerformanceTest/PerformanceTest.csproj new file mode 100644 index 0000000..5780d95 --- /dev/null +++ b/OxyPlot/Source/Examples/PerformanceTest/PerformanceTest.csproj @@ -0,0 +1,64 @@ + + + + + Debug + AnyCPU + {EDD52464-AD60-4EA1-B774-87AAFD930B6B} + Exe + Properties + PerformanceTest + PerformanceTest + v4.5 + 512 + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + Properties\AssemblyInfo.cs + + + + + + + + + + + {7A0B35C0-DD17-4964-8E9A-44D6CECDC692} + OxyPlot + + + + + + diff --git a/OxyPlot/Source/Examples/PerformanceTest/Program.cs b/OxyPlot/Source/Examples/PerformanceTest/Program.cs new file mode 100644 index 0000000..0ac69d9 --- /dev/null +++ b/OxyPlot/Source/Examples/PerformanceTest/Program.cs @@ -0,0 +1,150 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// A performance test program. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace PerformanceTest +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + + using OxyPlot; + using OxyPlot.Series; + + /// + /// A performance test program. + /// + /// + /// Build with the Release configuration. + /// To be used with a profiler or as a standalone program (remember to run outside the Visual Studio IDE). + /// + public class Program + { + /// + /// The program entry point. + /// + public static void Main() + { + var testModels = new Dictionary + { + { "LineSeries with 100000 points", CreateModel(100000) }, + { "LineSeries with 100000 points in ItemsSource", CreateModel2(100000) } + }; + + foreach (var kvp in testModels) + { + Console.WriteLine(kvp.Key); + TestModelUpdate(kvp.Value); + TestModelRender(kvp.Value); + Console.WriteLine(); + } + + Console.WriteLine("DrawClippedLine test:"); + var t0 = TestDrawClippedLine(10000, 1000, false); + var t1 = TestDrawClippedLine(10000, 1000, true); + Console.WriteLine("{0:P1}", (t0 - t1) / t0); + Console.ReadKey(); + } + + public static double TestModelUpdate(PlotModel model, int m = 1000) + { + var stopwatch = Stopwatch.StartNew(); + for (int i = 0; i < m; i++) + { + ((IPlotModel)model).Update(true); + } + + stopwatch.Stop(); + Console.WriteLine("Update: {0}", (double)stopwatch.ElapsedMilliseconds); + return stopwatch.ElapsedMilliseconds; + } + + public static double TestModelRender(PlotModel model, int m = 100) + { + var rc = new EmptyRenderContext(); + var stopwatch = Stopwatch.StartNew(); + for (int i = 0; i < m; i++) + { + ((IPlotModel)model).Render(rc, 800, 600); + } + + stopwatch.Stop(); + Console.WriteLine("Render: {0}", (double)stopwatch.ElapsedMilliseconds); + return stopwatch.ElapsedMilliseconds; + } + + private static PlotModel CreateModel(int n) + { + var model = new PlotModel(); + var series = new LineSeries(); + for (int i = 0; i < n; i++) + { + series.Points.Add(new DataPoint(i, Math.Sin(i))); + } + + model.Series.Add(series); + ((IPlotModel)model).Update(true); + return model; + } + + private static PlotModel CreateModel2(int n) + { + var points = new List(); + for (int i = 0; i < n; i++) + { + points.Add(new DataPoint(i, Math.Sin(i))); + } + + var model = new PlotModel(); + var series = new LineSeries(); + series.ItemsSource = points; + + model.Series.Add(series); + ((IPlotModel)model).Update(true); + return model; + } + + /// + /// Tests the method. + /// + /// The number of points. + /// The number of repetitions. + /// true to use an output buffer. + /// The elapsed time in milliseconds. + public static double TestDrawClippedLine(int n, int m, bool useOutputBuffer) + { + var points = new ScreenPoint[n]; + for (int i = 0; i < n; i++) + { + points[i] = new ScreenPoint((double)i / n, Math.Sin(40d * i / n)); + } + + var clippingRectangle = new OxyRect(0.3, -0.5, 0.5, 1); + var rc = new EmptyRenderContext(); + var outputBuffer = useOutputBuffer ? new List(n) : null; + var stopwatch = Stopwatch.StartNew(); + for (int i = 0; i < m; i++) + { + rc.DrawClippedLine( + clippingRectangle, + points, + 1, + OxyColors.Black, + 1, + null, + LineJoin.Miter, + false, + outputBuffer); + } + + stopwatch.Stop(); + Console.WriteLine((double)stopwatch.ElapsedMilliseconds); + return stopwatch.ElapsedMilliseconds; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/PerformanceTest/Properties/AssemblyDescription.cs b/OxyPlot/Source/Examples/PerformanceTest/Properties/AssemblyDescription.cs new file mode 100644 index 0000000..430b6c0 --- /dev/null +++ b/OxyPlot/Source/Examples/PerformanceTest/Properties/AssemblyDescription.cs @@ -0,0 +1,10 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +using System.Reflection; + +[assembly: AssemblyTitle("OxyPlot performance test application")] +[assembly: AssemblyDescription("Test application for profiling")] \ No newline at end of file diff --git a/OxyPlot/Source/Examples/WPF/ExampleBrowser/App.xaml b/OxyPlot/Source/Examples/WPF/ExampleBrowser/App.xaml new file mode 100644 index 0000000..f26d13e --- /dev/null +++ b/OxyPlot/Source/Examples/WPF/ExampleBrowser/App.xaml @@ -0,0 +1,8 @@ + + + + + diff --git a/OxyPlot/Source/Examples/WPF/ExampleBrowser/App.xaml.cs b/OxyPlot/Source/Examples/WPF/ExampleBrowser/App.xaml.cs new file mode 100644 index 0000000..0d8b056 --- /dev/null +++ b/OxyPlot/Source/Examples/WPF/ExampleBrowser/App.xaml.cs @@ -0,0 +1,24 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Interaction logic for App.xaml +// +// -------------------------------------------------------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Linq; +using System.Windows; + +namespace ExampleBrowser +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/WPF/ExampleBrowser/ExampleBrowser.csproj b/OxyPlot/Source/Examples/WPF/ExampleBrowser/ExampleBrowser.csproj new file mode 100644 index 0000000..39d46b2 --- /dev/null +++ b/OxyPlot/Source/Examples/WPF/ExampleBrowser/ExampleBrowser.csproj @@ -0,0 +1,118 @@ + + + + Debug + x86 + {4BB6F831-E215-488E-8ED8-F73C98C4B23F} + WinExe + Properties + ExampleBrowser + ExampleBrowser + v4.5.2 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + + + + true + bin\Debug\NET45\ + obj\Debug\NET45\ + DEBUG;TRACE + full + AnyCPU + prompt + 4 + + + ..\..\bin\Release\Examples\ExampleBrowser\ + obj\Release\NET40\ + TRACE + true + pdbonly + AnyCPU + prompt + 4 + + + ..\..\..\..\Icons\OxyPlot.ico + + + + + + + 4.0 + + + + + + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + Properties\AssemblyInfo.cs + + + App.xaml + Code + + + MainWindow.xaml + Code + + + + + + Code + + + True + True + Resources.resx + + + True + Settings.settings + True + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + + {698CCD8E-ADCC-4565-8517-5EDD36F07155} + OxyPlot.Wpf + + + {7A0B35C0-DD17-4964-8E9A-44D6CECDC692} + OxyPlot + + + {FACB89E5-53A5-4748-9F5B-E0714EBB37B2} + ExampleLibrary + + + + + OxyPlot_64.png + + + + \ No newline at end of file diff --git a/OxyPlot/Source/Examples/WPF/ExampleBrowser/MainWindow.xaml b/OxyPlot/Source/Examples/WPF/ExampleBrowser/MainWindow.xaml new file mode 100644 index 0000000..ea62933 --- /dev/null +++ b/OxyPlot/Source/Examples/WPF/ExampleBrowser/MainWindow.xaml @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OxyPlot/Source/Examples/WPF/ExampleBrowser/MainWindow.xaml.cs b/OxyPlot/Source/Examples/WPF/ExampleBrowser/MainWindow.xaml.cs new file mode 100644 index 0000000..47dce8b --- /dev/null +++ b/OxyPlot/Source/Examples/WPF/ExampleBrowser/MainWindow.xaml.cs @@ -0,0 +1,71 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Interaction logic for MainWindow.xaml +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleBrowser +{ + using System; + using System.Diagnostics; + using System.Windows; + using System.Windows.Media; + + /// + /// Interaction logic for MainWindow.xaml + /// + public partial class MainWindow : Window + { + /// + /// The frame count. + /// + private int frameCount; + + /// + /// The vm. + /// + private MainWindowViewModel vm = new MainWindowViewModel(); + + /// + /// The watch. + /// + private Stopwatch watch = new Stopwatch(); + + /// + /// Initializes a new instance of the class. + /// + public MainWindow() + { + this.InitializeComponent(); + this.DataContext = this.vm; + CompositionTarget.Rendering += this.CompositionTargetRendering; + this.watch.Start(); + } + + /// + /// Handles the Rendering event of the CompositionTarget control. + /// + /// The source of the event. + /// The instance containing the event data. + private void CompositionTargetRendering(object sender, EventArgs e) + { + this.frameCount++; + if (this.watch.ElapsedMilliseconds > 1000 && this.frameCount > 1) + { + this.vm.FrameRate = this.frameCount / (this.watch.ElapsedMilliseconds * 0.001); + this.frameCount = 0; + this.watch.Reset(); + this.watch.Start(); + } + + if (this.vm.MeasureFrameRate) + { + this.Plot1.InvalidatePlot(true); + } + } + + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/WPF/ExampleBrowser/MainWindowViewModel.cs b/OxyPlot/Source/Examples/WPF/ExampleBrowser/MainWindowViewModel.cs new file mode 100644 index 0000000..e5d0e5a --- /dev/null +++ b/OxyPlot/Source/Examples/WPF/ExampleBrowser/MainWindowViewModel.cs @@ -0,0 +1,111 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ExampleBrowser +{ + using System.Collections.Generic; + using System.ComponentModel; + using System.Linq; + using System.Windows.Data; + + using ExampleLibrary; + + using OxyPlot; + + public class MainWindowViewModel : INotifyPropertyChanged + { + private IPlotController defaultController; + + private IEnumerable examples; + + private double frameRate; + + private ExampleInfo selectedExample; + + public MainWindowViewModel() + { + this.defaultController = new PlotController(); + this.Examples = ExampleLibrary.Examples.GetList(); + this.ExamplesView = CollectionViewSource.GetDefaultView(this.Examples.OrderBy(e => e.Category)); + this.ExamplesView.GroupDescriptions.Add(new PropertyGroupDescription("Category")); + } + + public event PropertyChangedEventHandler PropertyChanged; + + public bool MeasureFrameRate { get; set; } + + public double FrameRate + { + get + { + return this.frameRate; + } + + set + { + this.frameRate = value; + this.RaisePropertyChanged("FrameRate"); + } + } + + public IEnumerable Examples + { + get + { + return this.examples; + } + + set + { + this.examples = value; + this.RaisePropertyChanged("Examples"); + } + } + + public ICollectionView ExamplesView { get; set; } + + public ExampleInfo SelectedExample + { + get + { + return this.selectedExample; + } + + set + { + this.selectedExample = value; + this.RaisePropertyChanged("Model"); + this.RaisePropertyChanged("Controller"); + this.RaisePropertyChanged("SelectedExample"); + } + } + + public PlotModel Model + { + get + { + return this.SelectedExample != null ? this.SelectedExample.PlotModel : null; + } + } + + public IPlotController Controller + { + get + { + return this.SelectedExample != null && this.SelectedExample.PlotController != null ? this.SelectedExample.PlotController : this.defaultController; + } + } + + protected void RaisePropertyChanged(string property) + { + var handler = this.PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(property)); + } + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/WPF/ExampleBrowser/Properties/AssemblyDescription.cs b/OxyPlot/Source/Examples/WPF/ExampleBrowser/Properties/AssemblyDescription.cs new file mode 100644 index 0000000..27a2f1f --- /dev/null +++ b/OxyPlot/Source/Examples/WPF/ExampleBrowser/Properties/AssemblyDescription.cs @@ -0,0 +1,12 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +using System.Reflection; +using System.Windows; + +[assembly: AssemblyTitle("OxyPlot WPF demo - Example browser")] +[assembly: AssemblyDescription("")] +[assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)] \ No newline at end of file diff --git a/OxyPlot/Source/Examples/WPF/ExampleBrowser/Properties/Resources.Designer.cs b/OxyPlot/Source/Examples/WPF/ExampleBrowser/Properties/Resources.Designer.cs new file mode 100644 index 0000000..7dc69f6 --- /dev/null +++ b/OxyPlot/Source/Examples/WPF/ExampleBrowser/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ExampleBrowser.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ExampleBrowser.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/OxyPlot/Source/Examples/WPF/ExampleBrowser/Properties/Resources.resx b/OxyPlot/Source/Examples/WPF/ExampleBrowser/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/OxyPlot/Source/Examples/WPF/ExampleBrowser/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/OxyPlot/Source/Examples/WPF/ExampleBrowser/Properties/Settings.Designer.cs b/OxyPlot/Source/Examples/WPF/ExampleBrowser/Properties/Settings.Designer.cs new file mode 100644 index 0000000..5bcdf5c --- /dev/null +++ b/OxyPlot/Source/Examples/WPF/ExampleBrowser/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ExampleBrowser.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.1.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/OxyPlot/Source/Examples/WPF/ExampleBrowser/Properties/Settings.settings b/OxyPlot/Source/Examples/WPF/ExampleBrowser/Properties/Settings.settings new file mode 100644 index 0000000..033d7a5 --- /dev/null +++ b/OxyPlot/Source/Examples/WPF/ExampleBrowser/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/OxyPlot/Source/Examples/WPF/ExampleBrowser/app.config b/OxyPlot/Source/Examples/WPF/ExampleBrowser/app.config new file mode 100644 index 0000000..de82893 --- /dev/null +++ b/OxyPlot/Source/Examples/WPF/ExampleBrowser/app.config @@ -0,0 +1,3 @@ + + + diff --git a/OxyPlot/Source/Examples/WPF/SimpleDemo/App.xaml b/OxyPlot/Source/Examples/WPF/SimpleDemo/App.xaml new file mode 100644 index 0000000..123ebf7 --- /dev/null +++ b/OxyPlot/Source/Examples/WPF/SimpleDemo/App.xaml @@ -0,0 +1,8 @@ + + + + + diff --git a/OxyPlot/Source/Examples/WPF/SimpleDemo/App.xaml.cs b/OxyPlot/Source/Examples/WPF/SimpleDemo/App.xaml.cs new file mode 100644 index 0000000..0626832 --- /dev/null +++ b/OxyPlot/Source/Examples/WPF/SimpleDemo/App.xaml.cs @@ -0,0 +1,25 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Interaction logic for App.xaml +// +// -------------------------------------------------------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Data; +using System.Linq; +using System.Windows; + +namespace SimpleDemo +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/WPF/SimpleDemo/MainViewModel.cs b/OxyPlot/Source/Examples/WPF/SimpleDemo/MainViewModel.cs new file mode 100644 index 0000000..0551a87 --- /dev/null +++ b/OxyPlot/Source/Examples/WPF/SimpleDemo/MainViewModel.cs @@ -0,0 +1,59 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Represents the view-model for the main window. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace SimpleDemo +{ + using OxyPlot; + using OxyPlot.Series; + + /// + /// Represents the view-model for the main window. + /// + public class MainViewModel + { + /// + /// Initializes a new instance of the class. + /// + public MainViewModel() + { + // Create the plot model + var tmp = new PlotModel { Title = "Simple example", Subtitle = "using OxyPlot" }; + + // Create two line series (markers are hidden by default) + var series1 = new LineSeries { Title = "Series 1", MarkerType = MarkerType.Circle }; + series1.Points.Add(new DataPoint(0, 0)); + series1.Points.Add(new DataPoint(10, 18)); + series1.Points.Add(new DataPoint(20, 12)); + series1.Points.Add(new DataPoint(30, 8)); + series1.Points.Add(new DataPoint(40, 15)); + + var series2 = new LineSeries { Title = "Series 2", MarkerType = MarkerType.Square }; + series2.Points.Add(new DataPoint(0, 4)); + series2.Points.Add(new DataPoint(10, 12)); + series2.Points.Add(new DataPoint(20, 16)); + series2.Points.Add(new DataPoint(30, 25)); + series2.Points.Add(new DataPoint(40, 5)); + + + // Add the series to the plot model + tmp.Series.Add(series1); + tmp.Series.Add(series2); + + // Axes are created automatically if they are not defined + + // Set the Model property, the INotifyPropertyChanged event will make the WPF Plot control update its content + this.Model = tmp; + } + + /// + /// Gets the plot model. + /// + public PlotModel Model { get; private set; } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/WPF/SimpleDemo/MainWindow.xaml b/OxyPlot/Source/Examples/WPF/SimpleDemo/MainWindow.xaml new file mode 100644 index 0000000..6323731 --- /dev/null +++ b/OxyPlot/Source/Examples/WPF/SimpleDemo/MainWindow.xaml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/OxyPlot/Source/Examples/WPF/SimpleDemo/MainWindow.xaml.cs b/OxyPlot/Source/Examples/WPF/SimpleDemo/MainWindow.xaml.cs new file mode 100644 index 0000000..146c888 --- /dev/null +++ b/OxyPlot/Source/Examples/WPF/SimpleDemo/MainWindow.xaml.cs @@ -0,0 +1,25 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Interaction logic for MainWindow.xaml +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace SimpleDemo +{ + /// + /// Interaction logic for MainWindow.xaml + /// + public partial class MainWindow + { + /// + /// Initializes a new instance of the class. + /// + public MainWindow() + { + this.InitializeComponent(); + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/WPF/SimpleDemo/Properties/AssemblyDescription.cs b/OxyPlot/Source/Examples/WPF/SimpleDemo/Properties/AssemblyDescription.cs new file mode 100644 index 0000000..4e161b4 --- /dev/null +++ b/OxyPlot/Source/Examples/WPF/SimpleDemo/Properties/AssemblyDescription.cs @@ -0,0 +1,12 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +using System.Reflection; +using System.Windows; + +[assembly: AssemblyTitle("OxyPlot WPF demo")] +[assembly: AssemblyDescription("")] +[assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)] \ No newline at end of file diff --git a/OxyPlot/Source/Examples/WPF/SimpleDemo/Properties/Resources.Designer.cs b/OxyPlot/Source/Examples/WPF/SimpleDemo/Properties/Resources.Designer.cs new file mode 100644 index 0000000..ae17dd6 --- /dev/null +++ b/OxyPlot/Source/Examples/WPF/SimpleDemo/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace SimpleDemo.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SimpleDemo.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/OxyPlot/Source/Examples/WPF/SimpleDemo/Properties/Resources.resx b/OxyPlot/Source/Examples/WPF/SimpleDemo/Properties/Resources.resx new file mode 100644 index 0000000..af7dbeb --- /dev/null +++ b/OxyPlot/Source/Examples/WPF/SimpleDemo/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/OxyPlot/Source/Examples/WPF/SimpleDemo/Properties/Settings.Designer.cs b/OxyPlot/Source/Examples/WPF/SimpleDemo/Properties/Settings.Designer.cs new file mode 100644 index 0000000..d3c2067 --- /dev/null +++ b/OxyPlot/Source/Examples/WPF/SimpleDemo/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace SimpleDemo.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.1.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/OxyPlot/Source/Examples/WPF/SimpleDemo/Properties/Settings.settings b/OxyPlot/Source/Examples/WPF/SimpleDemo/Properties/Settings.settings new file mode 100644 index 0000000..033d7a5 --- /dev/null +++ b/OxyPlot/Source/Examples/WPF/SimpleDemo/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/OxyPlot/Source/Examples/WPF/SimpleDemo/SimpleDemo.csproj b/OxyPlot/Source/Examples/WPF/SimpleDemo/SimpleDemo.csproj new file mode 100644 index 0000000..4580086 --- /dev/null +++ b/OxyPlot/Source/Examples/WPF/SimpleDemo/SimpleDemo.csproj @@ -0,0 +1,116 @@ + + + + Debug + x86 + {F97B3093-EBDD-46C9-890E-0501922B9168} + WinExe + Properties + SimpleDemo + SimpleDemo + v4.5.2 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + + + + ..\..\..\..\Icons\OxyPlot.ico + + + true + bin\Debug\ + DEBUG;TRACE + full + AnyCPU + prompt + MinimumRecommendedRules.ruleset + + + bin\Release\ + TRACE + true + pdbonly + AnyCPU + prompt + MinimumRecommendedRules.ruleset + + + + + + + + + 4.0 + + + + + + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + Properties\AssemblyInfo.cs + + + App.xaml + Code + + + + MainWindow.xaml + Code + + + + + Code + + + True + True + Resources.resx + + + True + Settings.settings + True + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + + {698CCD8E-ADCC-4565-8517-5EDD36F07155} + OxyPlot.Wpf + + + {7A0B35C0-DD17-4964-8E9A-44D6CECDC692} + OxyPlot + + + + + \ No newline at end of file diff --git a/OxyPlot/Source/Examples/WPF/SimpleDemo/app.config b/OxyPlot/Source/Examples/WPF/SimpleDemo/app.config new file mode 100644 index 0000000..de82893 --- /dev/null +++ b/OxyPlot/Source/Examples/WPF/SimpleDemo/app.config @@ -0,0 +1,3 @@ + + + diff --git a/OxyPlot/Source/Examples/WPF/WpfExamples/App.xaml b/OxyPlot/Source/Examples/WPF/WpfExamples/App.xaml new file mode 100644 index 0000000..260b6d5 --- /dev/null +++ b/OxyPlot/Source/Examples/WPF/WpfExamples/App.xaml @@ -0,0 +1,8 @@ + + + + + diff --git a/OxyPlot/Source/Examples/WPF/WpfExamples/App.xaml.cs b/OxyPlot/Source/Examples/WPF/WpfExamples/App.xaml.cs new file mode 100644 index 0000000..b33d063 --- /dev/null +++ b/OxyPlot/Source/Examples/WPF/WpfExamples/App.xaml.cs @@ -0,0 +1,25 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Interaction logic for App.xaml +// +// -------------------------------------------------------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Data; +using System.Linq; +using System.Windows; + +namespace WpfExamples +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/WPF/WpfExamples/BitmapTools.cs b/OxyPlot/Source/Examples/WPF/WpfExamples/BitmapTools.cs new file mode 100644 index 0000000..fcc12ac --- /dev/null +++ b/OxyPlot/Source/Examples/WPF/WpfExamples/BitmapTools.cs @@ -0,0 +1,24 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace WpfExamples +{ + using System.Drawing; + using System.Drawing.Drawing2D; + + public static class BitmapTools + { + public static Bitmap Resize(Bitmap bitmap, int newWidth, int newHeight) + { + var resizedBitmap = new Bitmap(newWidth, newHeight); + var g = Graphics.FromImage(resizedBitmap); + g.InterpolationMode = InterpolationMode.HighQualityBicubic; + g.DrawImage(bitmap, 0, 0, newWidth, newHeight); + g.Dispose(); + return resizedBitmap; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/WPF/WpfExamples/DelegateCommand.cs b/OxyPlot/Source/Examples/WPF/WpfExamples/DelegateCommand.cs new file mode 100644 index 0000000..52d85e3 --- /dev/null +++ b/OxyPlot/Source/Examples/WPF/WpfExamples/DelegateCommand.cs @@ -0,0 +1,104 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Represents a delegate command. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace WpfExamples +{ + using System; + using System.Windows.Input; + + /// + /// Represents a delegate command. + /// + public class DelegateCommand : ICommand + { + /// + /// The can execute. + /// + private readonly Func canExecute; + + /// + /// The execute. + /// + private readonly Action execute; + + /// + /// Initializes a new instance of the class. + /// + /// The execute. + public DelegateCommand(Action execute) + : this(execute, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The execute. + /// The can execute. + public DelegateCommand(Action execute, Func canExecute) + { + if (execute == null) + { + throw new ArgumentNullException("execute"); + } + + this.execute = execute; + this.canExecute = canExecute; + } + + /// + /// Occurs when changes occur that affect whether or not the command should execute. + /// + public event EventHandler CanExecuteChanged + { + add + { + if (this.canExecute != null) + { + CommandManager.RequerySuggested += value; + } + } + + remove + { + if (this.canExecute != null) + { + CommandManager.RequerySuggested -= value; + } + } + } + + /// + /// Defines the method that determines whether the command can execute in its current state. + /// + /// Data used by the command. If the command does not require data to be passed, this object can be set to null. + /// true if this command can be executed; otherwise, false. + public bool CanExecute(object parameter) + { + return this.canExecute == null ? true : this.canExecute(); + } + + /// + /// Defines the method to be called when the command is invoked. + /// + /// Data used by the command. If the command does not require data to be passed, this object can be set to null. + public void Execute(object parameter) + { + this.execute(); + } + + /// + /// Raises the can execute changed. + /// + public void RaiseCanExecuteChanged() + { + CommandManager.InvalidateRequerySuggested(); + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/WPF/WpfExamples/Example.cs b/OxyPlot/Source/Examples/WPF/WpfExamples/Example.cs new file mode 100644 index 0000000..3fef53d --- /dev/null +++ b/OxyPlot/Source/Examples/WPF/WpfExamples/Example.cs @@ -0,0 +1,56 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace WpfExamples +{ + using System; + using System.Diagnostics; + using System.Windows; + using System.Windows.Media; + using System.Windows.Media.Imaging; + + public class Example + { + public Example(Type mainWindowType, string title = null, string description = null) + { + this.MainWindowType = mainWindowType; + this.Title = title ?? mainWindowType.Namespace; + this.Description = description; + try + { + this.Thumbnail = new BitmapImage(new Uri("pack://application:,,,/Images/" + this.ThumbnailFileName)); + } + catch (Exception e) + { + Debug.WriteLine(e); + } + } + + public string Title { get; private set; } + public string Description { get; set; } + private Type MainWindowType { get; set; } + + public ImageSource Thumbnail { get; set; } + + public string ThumbnailFileName + { + get + { + return this.MainWindowType.Namespace + ".png"; + } + } + + public override string ToString() + { + return this.Title; + } + + public Window Create() + { + return Activator.CreateInstance(this.MainWindowType) as Window; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/WPF/WpfExamples/ExampleAttribute.cs b/OxyPlot/Source/Examples/WPF/WpfExamples/ExampleAttribute.cs new file mode 100644 index 0000000..dab18c7 --- /dev/null +++ b/OxyPlot/Source/Examples/WPF/WpfExamples/ExampleAttribute.cs @@ -0,0 +1,28 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace WpfExamples +{ + using System; + + public class ExampleAttribute : Attribute + { + public ExampleAttribute(string description) + : this(null, description) + { + } + + public ExampleAttribute(string title, string description) + { + this.Title = title; + this.Description = description; + } + + public string Title { get; private set; } + + public string Description { get; private set; } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/WPF/WpfExamples/Examples/AlignedAxesDemo/MainWindow.xaml b/OxyPlot/Source/Examples/WPF/WpfExamples/Examples/AlignedAxesDemo/MainWindow.xaml new file mode 100644 index 0000000..8992476 --- /dev/null +++ b/OxyPlot/Source/Examples/WPF/WpfExamples/Examples/AlignedAxesDemo/MainWindow.xaml @@ -0,0 +1,16 @@ + + + + + + + + + + + + diff --git a/OxyPlot/Source/Examples/WPF/WpfExamples/Examples/AlignedAxesDemo/MainWindow.xaml.cs b/OxyPlot/Source/Examples/WPF/WpfExamples/Examples/AlignedAxesDemo/MainWindow.xaml.cs new file mode 100644 index 0000000..fa16f65 --- /dev/null +++ b/OxyPlot/Source/Examples/WPF/WpfExamples/Examples/AlignedAxesDemo/MainWindow.xaml.cs @@ -0,0 +1,55 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 OxyPlot contributors +// +// +// Interaction logic for MainWindow.xaml +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace AlignedAxesDemo +{ + using System; + + using OxyPlot; + using OxyPlot.Axes; + using OxyPlot.Series; + + using WpfExamples; + + /// + /// Interaction logic for MainWindow.xaml + /// + [Example("Aligning plot margins from desired axis widths.")] + public partial class MainWindow + { + /// + /// Initializes a new instance of the class. + /// + public MainWindow() + { + this.InitializeComponent(); + this.Model0 = CreatePlotModel(0, 10); + this.Model1 = CreatePlotModel(0, 1e8); + + // TODO: align the vertical axis size without setting PlotMargins + this.Model0.PlotMargins = this.Model1.PlotMargins = new OxyThickness(70, 40, 20, 20); + + this.DataContext = this; + } + + public PlotModel Model0 { get; private set; } + + public PlotModel Model1 { get; private set; } + + private static PlotModel CreatePlotModel(double min, double max) + { + var model = new PlotModel(); + var verticalAxis = new LinearAxis { Position = AxisPosition.Left, Minimum = min, Maximum = max }; + model.Axes.Add(verticalAxis); + model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom }); + model.Series.Add(new FunctionSeries(x => Math.Sin(x * Math.PI * 4) * Math.Sin(x * Math.PI * 4) * Math.Sqrt(x) * max, 0, 1, 1000)); + return model; + } + } +} \ No newline at end of file diff --git a/OxyPlot/Source/Examples/WPF/WpfExamples/Examples/AnimationsDemo/Controls/AnimationSettingsControl.xaml b/OxyPlot/Source/Examples/WPF/WpfExamples/Examples/AnimationsDemo/Controls/AnimationSettingsControl.xaml new file mode 100644 index 0000000..86b1e3f --- /dev/null +++ b/OxyPlot/Source/Examples/WPF/WpfExamples/Examples/AnimationsDemo/Controls/AnimationSettingsControl.xaml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + +